HandBrake-0.10.2/0000775000175200017520000000000012535641641014046 5ustar handbrakehandbrakeHandBrake-0.10.2/version.txt0000664000175200017520000000053312535641641016275 0ustar handbrakehandbrakePath: 0.10.2 URL: svn://svn.handbrake.fr/HandBrake/tags/0.10.2 Relative URL: ^/tags/0.10.2 Repository Root: svn://svn.handbrake.fr/HandBrake Repository UUID: b64f7644-9d1e-0410-96f1-a4d463321fa5 Revision: 7288 Node Kind: directory Last Changed Author: sr55 Last Changed Rev: 7288 Last Changed Date: 2015-06-09 21:11:42 +0200 (mar., 09 juin 2015) HandBrake-0.10.2/libhb/0000775000175200017520000000000012535641635015131 5ustar handbrakehandbrakeHandBrake-0.10.2/libhb/declpcm.c0000664000175200017520000003217012463330511016673 0ustar handbrakehandbrake/* declpcm.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "hbffmpeg.h" #include "audio_resample.h" struct hb_work_private_s { hb_job_t *job; uint32_t size; /* frame size in bytes */ uint32_t nchunks; /* number of samples pairs if paired */ uint32_t nsamples; /* frame size in samples */ uint32_t pos; /* buffer offset for next input data */ int64_t next_pts; /* pts for next output frame */ int64_t sequence; /* the following is frame info for the frame we're currently accumulating */ uint64_t duration; /* frame duratin (in 90KHz ticks) */ uint32_t offset; /* where in buf frame starts */ uint32_t samplerate; /* sample rate in bits/sec */ uint8_t nchannels; uint8_t sample_size; /* bits per sample */ uint8_t frame[HB_DVD_READ_BUFFER_SIZE*2]; uint8_t * data; uint32_t alloc_size; hb_audio_resample_t *resample; }; static hb_buffer_t * Decode( hb_work_object_t * w ); static int declpcmInit( hb_work_object_t *, hb_job_t * ); static int declpcmWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); static void declpcmClose( hb_work_object_t * ); static int declpcmBSInfo( hb_work_object_t *, const hb_buffer_t *, hb_work_info_t * ); hb_work_object_t hb_declpcm = { WORK_DECLPCM, "LPCM decoder", declpcmInit, declpcmWork, declpcmClose, 0, declpcmBSInfo }; static const int hdr2samplerate[] = { 48000, 96000, 44100, 32000 }; static const int hdr2samplesize[] = { 16, 20, 24, 16 }; static const uint64_t hdr2layout[] = { AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO, AV_CH_LAYOUT_2_1, AV_CH_LAYOUT_QUAD, AV_CH_LAYOUT_5POINT0_BACK, AV_CH_LAYOUT_6POINT0_FRONT, AV_CH_LAYOUT_6POINT1, AV_CH_LAYOUT_7POINT1, }; static void lpcmInfo( hb_work_object_t *w, hb_buffer_t *in ) { hb_work_private_t * pv = w->private_data; /* * LPCM packets have a 7 byte header (the substream id is stripped off * before we get here so it's numbered -1 below):: * byte -1 Substream id * byte 0 Number of frames that begin in this packet * (last frame may finish in next packet) * byte 1,2 offset to first frame that begins in this packet (not including hdr) * byte 3: * bits 0-4 continuity counter (increments modulo 20) * bit 5 reserved * bit 6 audio mute on/off * bit 7 audio emphasis on/off * byte 4: * bits 0-2 #channels - 1 (e.g., stereo = 1) * bit 3 reserved * bits 4-5 sample rate (0=48K,1=96K,2=44.1K,3=32K) * bits 6-7 bits per sample (0=16 bit, 1=20 bit, 2=24 bit) * byte 5 Dynamic range control (0x80 = off) * * The audio is viewed as "frames" of 150 90KHz ticks each (80 samples @ 48KHz). * The frames are laid down continuously without regard to MPEG packet * boundaries. E.g., for 48KHz stereo, the first packet will contain 6 * frames plus the start of the 7th, the second packet will contain the * end of the 7th, 8-13 & the start of 14, etc. The frame structure is * important because the PTS on the packet gives the time of the first * frame that starts in the packet *NOT* the time of the first sample * in the packet. Also samples get split across packet boundaries * so we can't assume that we can consume all the data in one packet * on every call to the work routine. */ pv->offset = ( ( in->data[1] << 8 ) | in->data[2] ) + 2; if ( pv->offset >= HB_DVD_READ_BUFFER_SIZE ) { hb_log( "declpcm: illegal frame offset %d", pv->offset ); pv->offset = 2; /*XXX*/ } pv->nchannels = ( in->data[4] & 7 ) + 1; pv->samplerate = hdr2samplerate[ ( in->data[4] >> 4 ) & 0x3 ]; pv->sample_size = hdr2samplesize[in->data[4] >> 6]; // 20 and 24 bit lpcm is always encoded in sample pairs. So take this // into account when computing sizes. int chunk_size = pv->sample_size / 8; int samples_per_chunk = 1; switch( pv->sample_size ) { case 20: chunk_size = 5; samples_per_chunk = 2; break; case 24: chunk_size = 6; samples_per_chunk = 2; break; } /* * PCM frames have a constant duration (150 90KHz ticks). * We need to convert that to the amount of data expected. It's the * duration divided by the sample rate (to get #samples) times the number * of channels times the bits per sample divided by 8 to get bytes. * (we have to compute in bits because 20 bit samples are not an integral * number of bytes). We do all the multiplies first then the divides to * avoid truncation errors. */ /* * Don't trust the number of frames given in the header. We've seen * streams for which this is incorrect, and it can be computed. * pv->duration = in->data[0] * 150; */ int chunks = ( in->size - pv->offset ) / chunk_size; int samples = chunks * samples_per_chunk; // Calculate number of frames that start in this packet int frames = ( 90000 * samples / ( pv->samplerate * pv->nchannels ) + 149 ) / 150; pv->duration = frames * 150; pv->nchunks = ( pv->duration * pv->nchannels * pv->samplerate + samples_per_chunk - 1 ) / ( 90000 * samples_per_chunk ); pv->nsamples = ( pv->duration * pv->samplerate ) / 90000; pv->size = pv->nchunks * chunk_size; pv->next_pts = in->s.start; } static int declpcmInit( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; pv->job = job; pv->resample = hb_audio_resample_init(AV_SAMPLE_FMT_FLT, w->audio->config.out.mixdown, w->audio->config.out.normalize_mix_level); if (pv->resample == NULL) { hb_error("declpcmInit: hb_audio_resample_init() failed"); return 1; } return 0; } /* * Convert DVD encapsulated LPCM to floating point PCM audio buffers. * The amount of audio in a PCM frame is always <= the amount that will fit * in a DVD block (2048 bytes) but the standard doesn't require that the audio * frames line up with the DVD frames. Since audio frame boundaries are unrelated * to DVD PES boundaries, this routine has to reconstruct then extract the audio * frames. Because of the arbitrary alignment, it can output zero, one or two buf's. */ static int declpcmWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_buffer_t *in = *buf_in; hb_buffer_t *buf = NULL; if ( in->size <= 0 ) { /* EOF on input stream - send it downstream & say that we're done */ *buf_out = in; *buf_in = NULL; return HB_WORK_DONE; } pv->sequence = in->sequence; /* if we have a frame to finish, add enough data from this buf to finish it */ if ( pv->size ) { memcpy( pv->frame + pv->pos, in->data + 6, pv->size - pv->pos ); buf = Decode( w ); } *buf_out = buf; /* save the (rest of) data from this buf in our frame buffer */ lpcmInfo( w, in ); int off = pv->offset; int amt = in->size - off; pv->pos = amt; memcpy( pv->frame, in->data + off, amt ); if ( amt >= pv->size ) { if ( buf ) { buf->next = Decode( w ); } else { *buf_out = Decode( w ); } pv->size = 0; } return HB_WORK_OK; } static hb_buffer_t *Decode( hb_work_object_t *w ) { hb_work_private_t *pv = w->private_data; hb_buffer_t *out; if (pv->nsamples == 0) return NULL; int size = pv->nsamples * pv->nchannels * sizeof( float ); if (pv->alloc_size != size) { pv->data = realloc( pv->data, size ); pv->alloc_size = size; } float *odat = (float *)pv->data; int count = pv->nchunks / pv->nchannels; switch( pv->sample_size ) { case 16: // 2 byte, big endian, signed (the right shift sign extends) { uint8_t *frm = pv->frame; while ( count-- ) { int cc; for( cc = 0; cc < pv->nchannels; cc++ ) { // Shifts below result in sign extension which gives // us proper signed values. The final division adjusts // the range to [-1.0 ... 1.0] *odat++ = (float)( ( (int)( frm[0] << 24 ) >> 16 ) | frm[1] ) / 32768.0; frm += 2; } } } break; case 20: { // There will always be 2 groups of samples. A group is // a collection of samples that spans all channels. // The data for the samples is split. The first 2 msb // bytes for all samples is encoded first, then the remaining // lsb bits are encoded. uint8_t *frm = pv->frame; while ( count-- ) { int gg, cc; int shift = 4; uint8_t *lsb = frm + 4 * pv->nchannels; for( gg = 0; gg < 2; gg++ ) { for( cc = 0; cc < pv->nchannels; cc++ ) { // Shifts below result in sign extension which gives // us proper signed values. The final division adjusts // the range to [-1.0 ... 1.0] *odat = (float)( ( (int)( frm[0] << 24 ) >> 12 ) | ( frm[1] << 4 ) | ( ( ( lsb[0] >> shift ) & 0x0f ) ) ) / (16. * 32768.0); odat++; lsb += !shift; shift ^= 4; frm += 2; } } frm = lsb; } } break; case 24: { // There will always be 2 groups of samples. A group is // a collection of samples that spans all channels. // The data for the samples is split. The first 2 msb // bytes for all samples is encoded first, then the remaining // lsb bits are encoded. uint8_t *frm = pv->frame; while ( count-- ) { int gg, cc; uint8_t *lsb = frm + 4 * pv->nchannels; for( gg = 0; gg < 2; gg++ ) { for( cc = 0; cc < pv->nchannels; cc++ ) { // Shifts below result in sign extension which gives // us proper signed values. The final division adjusts // the range to [-1.0 ... 1.0] *odat++ = (float)( ( (int)( frm[0] << 24 ) >> 8 ) | ( frm[1] << 8 ) | lsb[0] ) / (256. * 32768.0); frm += 2; lsb++; } } frm = lsb; } } break; } hb_audio_resample_set_channel_layout(pv->resample, hdr2layout[pv->nchannels - 1]); if (hb_audio_resample_update(pv->resample)) { hb_log("declpcm: hb_audio_resample_update() failed"); return NULL; } out = hb_audio_resample(pv->resample, &pv->data, pv->nsamples); if (out != NULL) { out->s.start = pv->next_pts; out->s.duration = pv->duration; pv->next_pts += pv->duration; out->s.stop = pv->next_pts; } return out; } static void declpcmClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; if ( pv ) { hb_audio_resample_free(pv->resample); free( pv->data ); free( pv ); w->private_data = 0; } } static int declpcmBSInfo( hb_work_object_t *w, const hb_buffer_t *b, hb_work_info_t *info ) { int nchannels = ( b->data[4] & 7 ) + 1; int sample_size = hdr2samplesize[b->data[4] >> 6]; int rate = hdr2samplerate[ ( b->data[4] >> 4 ) & 0x3 ]; int bitrate = rate * sample_size * nchannels; int64_t duration = b->data[0] * 150; memset( info, 0, sizeof(*info) ); info->name = "LPCM"; info->rate = rate; info->rate_base = 1; info->bitrate = bitrate; info->flags = ( b->data[3] << 16 ) | ( b->data[4] << 8 ) | b->data[5]; info->matrix_encoding = AV_MATRIX_ENCODING_NONE; info->channel_layout = hdr2layout[nchannels - 1]; info->channel_map = &hb_libav_chan_map; info->samples_per_frame = ( duration * rate ) / 90000; return 1; } HandBrake-0.10.2/libhb/denoise.c0000664000175200017520000002775712265031673016740 0ustar handbrakehandbrake/* Copyright (c) 2003 Daniel Moreno Copyright (c) 2012 Loren Merritt This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hb.h" #include "hbffmpeg.h" #define HQDN3D_SPATIAL_LUMA_DEFAULT 4.0f #define HQDN3D_SPATIAL_CHROMA_DEFAULT 3.0f #define HQDN3D_TEMPORAL_LUMA_DEFAULT 6.0f #define ABS(A) ( (A) > 0 ? (A) : -(A) ) #define MIN( a, b ) ( (a) > (b) ? (b) : (a) ) struct hb_filter_private_s { short hqdn3d_coef[6][512*16]; unsigned short * hqdn3d_line; unsigned short * hqdn3d_frame[3]; }; static int hb_denoise_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int hb_denoise_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static void hb_denoise_close( hb_filter_object_t * filter ); hb_filter_object_t hb_filter_denoise = { .id = HB_FILTER_DENOISE, .enforce_order = 1, .name = "Denoise (hqdn3d)", .settings = NULL, .init = hb_denoise_init, .work = hb_denoise_work, .close = hb_denoise_close, }; static void hqdn3d_precalc_coef( short * ct, double dist25 ) { int i; double gamma, simil, c; gamma = log( 0.25 ) / log( 1.0 - MIN(dist25,252.0)/255.0 - 0.00001 ); for( i = -255*16; i <= 255*16; i++ ) { /* hqdn3d_lowpass_mul() truncates (not rounds) the diff, use +15/32 as midpoint */ double f = (i + 15.0/32.0) / 16.0; simil = 1.0 - ABS(f) / 255.0; c = pow(simil, gamma) * 256.0 * f; ct[16*256+i] = (c<0) ? (c-0.5) : (c+0.5); } ct[0] = (dist25 != 0); } static inline unsigned int hqdn3d_lowpass_mul( int prev_mul, int curr_mul, short * coef ) { int d = (prev_mul - curr_mul)>>4; return curr_mul + coef[d]; } static void hqdn3d_denoise_temporal( unsigned char * frame_src, unsigned char * frame_dst, unsigned short * frame_ant, int w, int h, short * temporal) { int x, y; unsigned int tmp; temporal += 0x1000; for( y = 0; y < h; y++ ) { for( x = 0; x < w; x++ ) { frame_ant[x] = tmp = hqdn3d_lowpass_mul( frame_ant[x], frame_src[x]<<8, temporal ); frame_dst[x] = (tmp+0x7F)>>8; } frame_src += w; frame_dst += w; frame_ant += w; } } static void hqdn3d_denoise_spatial( unsigned char * frame_src, unsigned char * frame_dst, unsigned short * line_ant, unsigned short * frame_ant, int w, int h, short * spatial, short * temporal ) { int x, y; unsigned int pixel_ant; unsigned int tmp; spatial += 0x1000; temporal += 0x1000; /* First line has no top neighbor. Only left one for each tmp and last frame */ pixel_ant = frame_src[0]<<8; for ( x = 0; x < w; x++) { line_ant[x] = tmp = pixel_ant = hqdn3d_lowpass_mul( pixel_ant, frame_src[x]<<8, spatial ); frame_ant[x] = tmp = hqdn3d_lowpass_mul( frame_ant[x], tmp, temporal ); frame_dst[x] = (tmp+0x7F)>>8; } for( y = 1; y < h; y++ ) { frame_src += w; frame_dst += w; frame_ant += w; pixel_ant = frame_src[0]<<8; for ( x = 0; x < w-1; x++ ) { line_ant[x] = tmp = hqdn3d_lowpass_mul( line_ant[x], pixel_ant, spatial ); pixel_ant = hqdn3d_lowpass_mul( pixel_ant, frame_src[x+1]<<8, spatial ); frame_ant[x] = tmp = hqdn3d_lowpass_mul( frame_ant[x], tmp, temporal ); frame_dst[x] = (tmp+0x7F)>>8; } line_ant[x] = tmp = hqdn3d_lowpass_mul( line_ant[x], pixel_ant, spatial ); frame_ant[x] = tmp = hqdn3d_lowpass_mul( frame_ant[x], tmp, temporal ); frame_dst[x] = (tmp+0x7F)>>8; } } static void hqdn3d_denoise( unsigned char * frame_src, unsigned char * frame_dst, unsigned short * line_ant, unsigned short ** frame_ant_ptr, int w, int h, short * spatial, short * temporal ) { int x, y; unsigned short* frame_ant = (*frame_ant_ptr); if( !frame_ant) { unsigned char * src = frame_src; (*frame_ant_ptr) = frame_ant = malloc( w*h*sizeof(unsigned short) ); for ( y = 0; y < h; y++, frame_src += w, frame_ant += w ) { for( x = 0; x < w; x++ ) { frame_ant[x] = frame_src[x]<<8; } } frame_src = src; frame_ant = *frame_ant_ptr; } /* If no spatial coefficients, do temporal denoise only */ if( spatial[0] ) { hqdn3d_denoise_spatial( frame_src, frame_dst, line_ant, frame_ant, w, h, spatial, temporal ); } else { hqdn3d_denoise_temporal( frame_src, frame_dst, frame_ant, w, h, temporal); } } static int hb_denoise_init( hb_filter_object_t * filter, hb_filter_init_t * init ) { filter->private_data = calloc( sizeof(struct hb_filter_private_s), 1 ); hb_filter_private_t * pv = filter->private_data; double spatial_luma, spatial_chroma_b, spatial_chroma_r; double temporal_luma, temporal_chroma_b, temporal_chroma_r; if( filter->settings ) { switch( sscanf( filter->settings, "%lf:%lf:%lf:%lf:%lf:%lf", &spatial_luma, &spatial_chroma_b, &spatial_chroma_r, &temporal_luma, &temporal_chroma_b, &temporal_chroma_r ) ) { case 0: spatial_luma = HQDN3D_SPATIAL_LUMA_DEFAULT; spatial_chroma_b = HQDN3D_SPATIAL_CHROMA_DEFAULT; spatial_chroma_r = spatial_chroma_b; temporal_luma = HQDN3D_TEMPORAL_LUMA_DEFAULT; temporal_chroma_b = temporal_luma * spatial_chroma_b / spatial_luma; temporal_chroma_r = temporal_chroma_b; break; case 1: spatial_chroma_b = HQDN3D_SPATIAL_CHROMA_DEFAULT * spatial_luma / HQDN3D_SPATIAL_LUMA_DEFAULT; spatial_chroma_r = spatial_chroma_b; temporal_luma = HQDN3D_TEMPORAL_LUMA_DEFAULT * spatial_luma / HQDN3D_SPATIAL_LUMA_DEFAULT; temporal_chroma_b = temporal_luma * spatial_chroma_b / spatial_luma; temporal_chroma_r = temporal_chroma_b; break; case 2: spatial_chroma_r = spatial_chroma_b; temporal_luma = HQDN3D_TEMPORAL_LUMA_DEFAULT * spatial_luma / HQDN3D_SPATIAL_LUMA_DEFAULT; temporal_chroma_b = temporal_luma * spatial_chroma_b / spatial_luma; temporal_chroma_r = temporal_chroma_b; break; case 3: temporal_luma = HQDN3D_TEMPORAL_LUMA_DEFAULT * spatial_luma / HQDN3D_SPATIAL_LUMA_DEFAULT; temporal_chroma_b = temporal_luma * spatial_chroma_b / spatial_luma; temporal_chroma_r = temporal_chroma_b; break; case 4: temporal_chroma_b = temporal_luma * spatial_chroma_b / spatial_luma; temporal_chroma_r = temporal_chroma_b; break; case 5: temporal_chroma_r = temporal_chroma_b; break; } } hqdn3d_precalc_coef( pv->hqdn3d_coef[0], spatial_luma ); hqdn3d_precalc_coef( pv->hqdn3d_coef[1], temporal_luma ); hqdn3d_precalc_coef( pv->hqdn3d_coef[2], spatial_chroma_b ); hqdn3d_precalc_coef( pv->hqdn3d_coef[3], temporal_chroma_b ); hqdn3d_precalc_coef( pv->hqdn3d_coef[4], spatial_chroma_r ); hqdn3d_precalc_coef( pv->hqdn3d_coef[5], temporal_chroma_r ); return 0; } static void hb_denoise_close( hb_filter_object_t * filter ) { hb_filter_private_t * pv = filter->private_data; if( !pv ) { return; } if( pv->hqdn3d_line ) { free( pv->hqdn3d_line ); pv->hqdn3d_line = NULL; } if( pv->hqdn3d_frame[0] ) { free( pv->hqdn3d_frame[0] ); pv->hqdn3d_frame[0] = NULL; } if( pv->hqdn3d_frame[1] ) { free( pv->hqdn3d_frame[1] ); pv->hqdn3d_frame[1] = NULL; } if( pv->hqdn3d_frame[2] ) { free( pv->hqdn3d_frame[2] ); pv->hqdn3d_frame[2] = NULL; } free( pv ); filter->private_data = NULL; } static int hb_denoise_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in, * out; if ( in->size <= 0 ) { *buf_out = in; *buf_in = NULL; return HB_FILTER_DONE; } out = hb_video_buffer_init( in->f.width, in->f.height ); if( !pv->hqdn3d_line ) { pv->hqdn3d_line = malloc( in->plane[0].stride * sizeof(unsigned short) ); } int c, coef_index; for ( c = 0; c < 3; c++ ) { coef_index = c * 2; hqdn3d_denoise( in->plane[c].data, out->plane[c].data, pv->hqdn3d_line, &pv->hqdn3d_frame[c], in->plane[c].stride, in->plane[c].height, pv->hqdn3d_coef[coef_index], pv->hqdn3d_coef[coef_index+1] ); } out->s = in->s; hb_buffer_move_subs( out, in ); *buf_out = out; return HB_FILTER_OK; } HandBrake-0.10.2/libhb/eedi2.h0000664000175200017520000001202112463330511016252 0ustar handbrakehandbrake/* eedi2.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ // Used to order a sequeunce of metrics for median filtering void eedi2_sort_metrics( int *order, const int length ); // Aping some Windows API funcctions AviSynth seems to like // Taken from here: http://www.gidforums.com/t-8543.html void *eedi2_aligned_malloc(size_t size, size_t align_size); void eedi2_aligned_free(void *ptr); // Copies bitmaps void eedi2_bit_blit( uint8_t * dstp, int dst_pitch, const uint8_t * srcp, int src_pitch, int row_size, int height ); // Sets up the initial field-sized bitmap EEDI2 interpolates from void eedi2_fill_half_height_buffer_plane( uint8_t * src, uint8_t * dst, int pitch, int height ); // Simple line doubler void eedi2_upscale_by_2( uint8_t * srcp, uint8_t * dstp, int height, int pitch ); // Finds places where vertically adjacent pixels abruptly change intensity void eedi2_build_edge_mask( uint8_t * dstp, int dst_pitch, uint8_t *srcp, int src_pitch, int mthresh, int lthresh, int vthresh, int height, int width ); // Expands and smooths out the edge mask by considering a pixel // to be masked if >= dilation threshold adjacent pixels are masked. void eedi2_dilate_edge_mask( uint8_t *mskp, int msk_pitch, uint8_t *dstp, int dst_pitch, int dstr, int height, int width ); // Contracts the edge mask by considering a pixel to be masked // only if > erosion threshold adjacent pixels are masked void eedi2_erode_edge_mask( uint8_t *mskp, int msk_pitch, uint8_t *dstp, int dst_pitch, int estr, int height, int width ); // Smooths out horizontally aligned holes in the mask // If none of the 6 horizontally adjacent pixels are masked, // don't consider the current pixel masked. If there are any // masked on both sides, consider the current pixel masked. void eedi2_remove_small_gaps( uint8_t * mskp, int msk_pitch, uint8_t * dstp, int dst_pitch, int height, int width ); // Spatial vectors. Looks at maximum_search_distance surrounding pixels // to guess which angle edges follow. This is EEDI2's timesink, and can be // thought of as YADIF_CHECK on steroids. Both find edge directions. void eedi2_calc_directions( const int plane, uint8_t * mskp, int msk_pitch, uint8_t * srcp, int src_pitch, uint8_t * dstp, int dst_pitch, int maxd, int nt, int height, int width ); void eedi2_filter_map( uint8_t *mskp, int msk_pitch, uint8_t *dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, int height, int width ); void eedi2_filter_dir_map( uint8_t * mskp, int msk_pitch, uint8_t * dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, int height, int width ); void eedi2_expand_dir_map( uint8_t * mskp, int msk_pitch, uint8_t *dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, int height, int width ); void eedi2_mark_directions_2x( uint8_t * mskp, int msk_pitch, uint8_t * dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, int tff, int height, int width ); void eedi2_filter_dir_map_2x( uint8_t * mskp, int msk_pitch, uint8_t * dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, int field, int height, int width ); void eedi2_expand_dir_map_2x( uint8_t * mskp, int msk_pitch, uint8_t * dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, int field, int height, int width ); void eedi2_fill_gaps_2x( uint8_t *mskp, int msk_pitch, uint8_t * dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, int field, int height, int width ); void eedi2_interpolate_lattice( const int plane, uint8_t * dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, uint8_t * omskp, int omsk_pitch, int field, int nt, int height, int width ); void eedi2_post_process( uint8_t * nmskp, int nmsk_pitch, uint8_t * omskp, int omsk_pitch, uint8_t * dstp, int src_pitch, int field, int height, int width ); void eedi2_gaussian_blur1( uint8_t * src, int src_pitch, uint8_t * tmp, int tmp_pitch, uint8_t * dst, int dst_pitch, int height, int width ); void eedi2_gaussian_blur_sqrt2( int *src, int *tmp, int *dst, const int pitch, const int height, const int width ); void eedi2_calc_derivatives( uint8_t *srcp, int src_pitch, int height, int width, int *x2, int *y2, int *xy); void eedi2_post_process_corner( int *x2, int *y2, int *xy, const int pitch, uint8_t * mskp, int msk_pitch, uint8_t * dstp, int dst_pitch, int height, int width, int field ); HandBrake-0.10.2/libhb/decsrtsub.c0000664000175200017520000005207212463330511017265 0ustar handbrakehandbrake/* decsrtsub.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include #include #include #include #include #include "hb.h" #include "colormap.h" #include "decsrtsub.h" struct start_and_end { unsigned long start, end; }; enum { k_state_inEntry, k_state_inEntry_or_new, k_state_potential_new_entry, k_state_timecode, }; typedef struct srt_entry_s { long offset, duration; long start, stop; char text[1024]; int pos; } srt_entry_t; /* * Store all context in the work private struct, */ struct hb_work_private_s { hb_job_t * job; FILE * file; char buf[1024]; int pos; int end; char utf8_buf[2048]; int utf8_pos; int utf8_end; int utf8_bom_skipped; unsigned long current_time; unsigned long number_of_entries; unsigned long last_entry_number; unsigned long current_state; srt_entry_t current_entry; iconv_t *iconv_context; hb_subtitle_t *subtitle; uint64_t start_time; // In HB time uint64_t stop_time; // In HB time int line; // SSA line number }; static char* srt_markup_to_ssa(char *srt, int *len) { char terminator; char color[40]; uint32_t rgb; *len = 0; if (srt[0] != '<' && srt[0] != '{') return NULL; if (srt[0] == '<') terminator = '>'; else terminator = '}'; if (srt[1] == 'i' && srt[2] == terminator) { *len = 3; return hb_strdup_printf("{\\i1}"); } else if (srt[1] == 'b' && srt[2] == terminator) { *len = 3; return hb_strdup_printf("{\\b1}"); } else if (srt[1] == 'u' && srt[2] == terminator) { *len = 3; return hb_strdup_printf("{\\u1}"); } else if (srt[1] == '/' && srt[2] == 'i' && srt[3] == terminator) { *len = 4; return hb_strdup_printf("{\\i0}"); } else if (srt[1] == '/' && srt[2] == 'b' && srt[3] == terminator) { *len = 4; return hb_strdup_printf("{\\b0}"); } else if (srt[1] == '/' && srt[2] == 'u' && srt[3] == terminator) { *len = 4; return hb_strdup_printf("{\\u0}"); } else if (srt[0] == '<' && !strncmp(srt + 1, "font", 4)) { int match; match = sscanf(srt + 1, "font color=\"%39[^\"]\">", color); if (match != 1) { return NULL; } while (srt[*len] != '>') (*len)++; (*len)++; if (color[0] == '#') rgb = strtol(color + 1, NULL, 16); else rgb = hb_rgb_lookup_by_name(color); return hb_strdup_printf("{\\1c&H%X&}", HB_RGB_TO_BGR(rgb)); } else if (srt[0] == '<' && srt[1] == '/' && !strncmp(srt + 2, "font", 4) && srt[6] == '>') { *len = 7; return hb_strdup_printf("{\\1c&HFFFFFF&}"); } return NULL; } void hb_srt_to_ssa(hb_buffer_t *sub_in, int line) { if (sub_in->size == 0) return; // null terminate input if not already terminated if (sub_in->data[sub_in->size-1] != 0) { hb_buffer_realloc(sub_in, ++sub_in->size); sub_in->data[sub_in->size - 1] = 0; } char * srt = (char*)sub_in->data; // SSA markup expands a little over SRT, so allocate a bit of extra // space. More will be realloc'd if needed. hb_buffer_t * sub = hb_buffer_init(sub_in->size + 80); char * ssa, *ssa_markup; int skip, len, pos, ii; // Exchange data between input sub and new ssa_sub // After this, sub_in contains ssa data hb_buffer_swap_copy(sub_in, sub); ssa = (char*)sub_in->data; sprintf((char*)sub_in->data, "%d,,Default,,0,0,0,,", line); pos = strlen((char*)sub_in->data); ii = 0; while (srt[ii] != '\0') { if ((ssa_markup = srt_markup_to_ssa(srt + ii, &skip)) != NULL) { len = strlen(ssa_markup); hb_buffer_realloc(sub_in, pos + len + 1); // After realloc, sub_in->data may change ssa = (char*)sub_in->data; sprintf(ssa + pos, "%s", ssa_markup); free(ssa_markup); pos += len; ii += skip; } else { hb_buffer_realloc(sub_in, pos + 4); // After realloc, sub_in->data may change ssa = (char*)sub_in->data; if (srt[ii] == '\r') { ssa[pos++] = '\\'; ssa[pos++] = 'N'; ii++; if (srt[ii] == '\n') { ii++; } } else if (srt[ii] == '\n') { ssa[pos++] = '\\'; ssa[pos++] = 'N'; ii++; } else { ssa[pos++] = srt[ii++]; } } } ssa[pos] = '\0'; sub_in->size = pos + 1; hb_buffer_close(&sub); } static int read_time_from_string( const char* timeString, struct start_and_end *result ) { // for ex. 00:00:15,248 --> 00:00:16,545 long houres1, minutes1, seconds1, milliseconds1, houres2, minutes2, seconds2, milliseconds2; int scanned; scanned = sscanf(timeString, "%ld:%ld:%ld,%ld --> %ld:%ld:%ld,%ld\n", &houres1, &minutes1, &seconds1, &milliseconds1, &houres2, &minutes2, &seconds2, &milliseconds2); if (scanned != 8) { return 0; } result->start = milliseconds1 + seconds1*1000 + minutes1*60*1000 + houres1*60*60*1000; result->end = milliseconds2 + seconds2*1000 + minutes2*60*1000 + houres2*60*60*1000; return 1; } static int utf8_fill( hb_work_private_t * pv ) { int bytes, conversion = 0; size_t out_size; /* Align utf8 data to beginning of the buffer so that we can * fill the buffer to its maximum */ memmove( pv->utf8_buf, pv->utf8_buf + pv->utf8_pos, pv->utf8_end - pv->utf8_pos ); pv->utf8_end -= pv->utf8_pos; pv->utf8_pos = 0; out_size = 2048 - pv->utf8_end; while( out_size ) { char *p, *q; size_t in_size, retval; if( pv->end == pv->pos ) { bytes = fread( pv->buf, 1, 1024, pv->file ); pv->pos = 0; pv->end = bytes; if( bytes == 0 ) { if( conversion ) return 1; else return 0; } } p = pv->buf + pv->pos; q = pv->utf8_buf + pv->utf8_end; in_size = pv->end - pv->pos; retval = iconv( pv->iconv_context, &p, &in_size, &q, &out_size); if( q != pv->utf8_buf + pv->utf8_pos ) conversion = 1; pv->utf8_end = q - pv->utf8_buf; pv->pos = p - pv->buf; if ( !pv->utf8_bom_skipped ) { uint8_t *buf = (uint8_t*)pv->utf8_buf; if (buf[0] == 0xef && buf[1] == 0xbb && buf[2] == 0xbf) { pv->utf8_pos = 3; } pv->utf8_bom_skipped = 1; } if( ( retval == -1 ) && ( errno == EINVAL ) ) { /* Incomplete multibyte sequence, read more data */ memmove( pv->buf, p, pv->end - pv->pos ); pv->end -= pv->pos; pv->pos = 0; bytes = fread( pv->buf + pv->end, 1, 1024 - pv->end, pv->file ); if( bytes == 0 ) { if( !conversion ) return 0; else return 1; } pv->end += bytes; } else if ( ( retval == -1 ) && ( errno == EILSEQ ) ) { hb_error( "Invalid byte for codeset in input, discard byte" ); /* Try the next byte of the input */ pv->pos++; } else if ( ( retval == -1 ) && ( errno == E2BIG ) ) { /* buffer full */ return conversion; } } return 1; } static int get_line( hb_work_private_t * pv, char *buf, int size ) { int i; char c; // clear remnants of the previous line before progessing a new one memset(buf, '\0', size); /* Find newline in converted UTF-8 buffer */ for( i = 0; i < size - 1; i++ ) { if( pv->utf8_pos >= pv->utf8_end ) { if( !utf8_fill( pv ) ) { if( i ) return 1; else return 0; } } c = pv->utf8_buf[pv->utf8_pos++]; if( c == '\n' ) { buf[i] = '\n'; buf[i+1] = '\0'; return 1; } buf[i] = c; } buf[0] = '\0'; return 1; } /* * Read the SRT file and put the entries into the subtitle fifo for all to read */ static hb_buffer_t *srt_read( hb_work_private_t *pv ) { char line_buffer[1024]; int reprocess = 0, resync = 0; if( !pv->file ) { return NULL; } while( reprocess || get_line( pv, line_buffer, sizeof( line_buffer ) ) ) { reprocess = 0; switch (pv->current_state) { case k_state_timecode: { struct start_and_end timing; int result; result = read_time_from_string( line_buffer, &timing ); if (!result) { resync = 1; pv->current_state = k_state_potential_new_entry; continue; } pv->current_entry.duration = timing.end - timing.start; pv->current_entry.offset = timing.start - pv->current_time; pv->current_time = timing.end; pv->current_entry.start = timing.start; pv->current_entry.stop = timing.end; pv->current_state = k_state_inEntry; continue; } case k_state_inEntry_or_new: { char *endpoint; /* * Is this really new next entry begin? * Look for entry number. */ strtol(line_buffer, &endpoint, 10); if (endpoint == line_buffer || (endpoint && *endpoint != '\n' && *endpoint != '\r')) { /* * Doesn't resemble an entry number * must still be in an entry */ if (!resync) { reprocess = 1; pv->current_state = k_state_inEntry; } continue; } reprocess = 1; pv->current_state = k_state_potential_new_entry; break; } case k_state_inEntry: { char *q; int size, len; // If the current line is empty, we assume this is the // seperation betwene two entries. In case we are wrong, // the mistake is corrected in the next state. if (strcmp(line_buffer, "\n") == 0 || strcmp(line_buffer, "\r\n") == 0) { pv->current_state = k_state_potential_new_entry; continue; } q = pv->current_entry.text + pv->current_entry.pos; len = strlen( line_buffer ); size = MIN(1024 - pv->current_entry.pos - 1, len ); memcpy(q, line_buffer, size); pv->current_entry.pos += size; pv->current_entry.text[pv->current_entry.pos] = '\0'; break; } case k_state_potential_new_entry: { char *endpoint; long entry_number; hb_buffer_t *buffer = NULL; /* * Is this really new next entry begin? */ entry_number = strtol(line_buffer, &endpoint, 10); if (!resync && (*line_buffer == '\n' || *line_buffer == '\r')) { /* * Well.. looks like we are in the wrong mode.. lets add the * newline we misinterpreted... */ strncat(pv->current_entry.text, " ", 1024); pv->current_state = k_state_inEntry_or_new; continue; } if (endpoint == line_buffer || (endpoint && *endpoint != '\n' && *endpoint != '\r')) { /* * Well.. looks like we are in the wrong mode.. lets add the * line we misinterpreted... */ if (!resync) { reprocess = 1; pv->current_state = k_state_inEntry; } continue; } /* * We found the next entry - or a really rare error condition */ pv->last_entry_number = entry_number; resync = 0; if (*pv->current_entry.text != '\0') { long length; char *p, *q; int line = 1; uint64_t start_time = ( pv->current_entry.start + pv->subtitle->config.offset ) * 90; uint64_t stop_time = ( pv->current_entry.stop + pv->subtitle->config.offset ) * 90; if( !( start_time > pv->start_time && stop_time < pv->stop_time ) ) { hb_deep_log( 3, "Discarding SRT at time start %"PRId64", stop %"PRId64, start_time, stop_time); memset( &pv->current_entry, 0, sizeof( srt_entry_t ) ); ++(pv->number_of_entries); pv->current_state = k_state_timecode; continue; } length = strlen( pv->current_entry.text ); for (q = p = pv->current_entry.text; *p != '\0'; p++) { if (*p == '\n' || *p == '\r') { if (*(p + 1) == '\n' || *(p + 1) == '\r' || *(p + 1) == '\0') { // followed by line break or last character, skip it length--; continue; } else if (line == 1) { // replace '\r' with '\n' *q = '\n'; line = 2; } else { // all subtitles on two lines tops // replace line breaks with spaces *q = ' '; } q++; } else { *q = *p; q++; } } *q = '\0'; buffer = hb_buffer_init( length + 1 ); if( buffer ) { buffer->s.start = start_time - pv->start_time; buffer->s.stop = stop_time - pv->start_time; memcpy( buffer->data, pv->current_entry.text, length + 1 ); } } memset( &pv->current_entry, 0, sizeof( srt_entry_t ) ); ++(pv->number_of_entries); pv->current_state = k_state_timecode; if( buffer ) { return buffer; } continue; } } } hb_buffer_t *buffer = NULL; if (*pv->current_entry.text != '\0') { long length; char *p, *q; int line = 1; uint64_t start_time = ( pv->current_entry.start + pv->subtitle->config.offset ) * 90; uint64_t stop_time = ( pv->current_entry.stop + pv->subtitle->config.offset ) * 90; if( !( start_time > pv->start_time && stop_time < pv->stop_time ) ) { hb_deep_log( 3, "Discarding SRT at time start %"PRId64", stop %"PRId64, start_time, stop_time); memset( &pv->current_entry, 0, sizeof( srt_entry_t ) ); return NULL; } length = strlen( pv->current_entry.text ); for (q = p = pv->current_entry.text; *p != '\0'; p++) { if (*p == '\n' || *p == '\r') { if (*(p + 1) == '\n' || *(p + 1) == '\r' || *(p + 1) == '\0') { // followed by line break or last character, skip it length--; continue; } else if (line == 1) { // replace '\r' with '\n' *q = '\n'; line = 2; } else { // all subtitles on two lines tops // replace line breaks with spaces *q = ' '; } q++; } else { *q = *p; q++; } } *q = '\0'; buffer = hb_buffer_init( length + 1 ); if( buffer ) { buffer->s.start = start_time - pv->start_time; buffer->s.stop = stop_time - pv->start_time; memcpy( buffer->data, pv->current_entry.text, length + 1 ); } } memset( &pv->current_entry, 0, sizeof( srt_entry_t ) ); if( buffer ) { return buffer; } return NULL; } static int decsrtInit( hb_work_object_t * w, hb_job_t * job ) { int retval = 1; hb_work_private_t * pv; hb_buffer_t *buffer; int i; hb_chapter_t * chapter; pv = calloc( 1, sizeof( hb_work_private_t ) ); if( pv ) { w->private_data = pv; pv->job = job; buffer = hb_buffer_init( 0 ); hb_fifo_push( w->fifo_in, buffer); pv->current_state = k_state_potential_new_entry; pv->number_of_entries = 0; pv->last_entry_number = 0; pv->current_time = 0; pv->subtitle = w->subtitle; /* * Figure out the start and stop times from teh chapters being * encoded - drop subtitle not in this range. */ pv->start_time = 0; for( i = 1; i < job->chapter_start; ++i ) { chapter = hb_list_item( job->list_chapter, i - 1 ); if( chapter ) { pv->start_time += chapter->duration; } else { hb_error( "Could not locate chapter %d for SRT start time", i ); retval = 0; } } pv->stop_time = pv->start_time; for( i = job->chapter_start; i <= job->chapter_end; ++i ) { chapter = hb_list_item( job->list_chapter, i - 1 ); if( chapter ) { pv->stop_time += chapter->duration; } else { hb_error( "Could not locate chapter %d for SRT start time", i ); retval = 0; } } hb_deep_log( 3, "SRT Start time %"PRId64", stop time %"PRId64, pv->start_time, pv->stop_time); pv->iconv_context = iconv_open( "utf-8", pv->subtitle->config.src_codeset ); if( pv->iconv_context == (iconv_t) -1 ) { hb_error("Could not open the iconv library with those file formats\n"); } else { memset( &pv->current_entry, 0, sizeof( srt_entry_t ) ); pv->file = hb_fopen(w->subtitle->config.src_filename, "r"); if( !pv->file ) { hb_error("Could not open the SRT subtitle file '%s'\n", w->subtitle->config.src_filename); } else { retval = 0; } } } if (!retval) { // Generate generic SSA Script Info. int height = job->title->height - job->crop[0] - job->crop[1]; int width = job->title->width - job->crop[2] - job->crop[3]; hb_subtitle_add_ssa_header(w->subtitle, width, height); } return retval; } static int decsrtWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_buffer_t * in = *buf_in; hb_buffer_t * out = NULL; out = srt_read( pv ); if( out ) { hb_srt_to_ssa(out, ++pv->line); /* * Keep a buffer in our input fifo so that we get run. */ hb_fifo_push( w->fifo_in, in); *buf_in = NULL; *buf_out = out; } else { *buf_out = NULL; return HB_WORK_OK; } return HB_WORK_OK; } static void decsrtClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; fclose( pv->file ); iconv_close(pv->iconv_context); free( w->private_data ); } hb_work_object_t hb_decsrtsub = { WORK_DECSRTSUB, "SRT Subtitle Decoder", decsrtInit, decsrtWork, decsrtClose }; HandBrake-0.10.2/libhb/qsv_memory.c0000664000175200017520000001112412220306351017455 0ustar handbrakehandbrake/* ********************************************************************* *\ Copyright (C) 2013 Intel Corporation. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \* ********************************************************************* */ #ifdef USE_QSV #include "hb.h" #include "hbffmpeg.h" #include "qsv_memory.h" int qsv_nv12_to_yuv420(struct SwsContext* sws_context,hb_buffer_t* dst, mfxFrameSurface1* src, mfxCoreInterface *core){ int ret = 0; int i,j; int in_pitch = src->Data.Pitch; int w = AV_QSV_ALIGN16(src->Info.Width); int h = (MFX_PICSTRUCT_PROGRESSIVE == src->Info.PicStruct) ? AV_QSV_ALIGN16(src->Info.Height) : AV_QSV_ALIGN32(src->Info.Height); uint8_t *in_luma = 0; uint8_t *in_chroma = 0; static int copyframe_in_use = 1; mfxStatus sts = MFX_ERR_NONE; mfxFrameSurface1 accel_dst; if (copyframe_in_use) { accel_dst.Info.FourCC = src->Info.FourCC; accel_dst.Info.CropH = src->Info.CropH; accel_dst.Info.CropW = src->Info.CropW; accel_dst.Info.CropY = src->Info.CropY; accel_dst.Info.CropX = src->Info.CropX; accel_dst.Info.Width = w; accel_dst.Info.Height = h; accel_dst.Data.Pitch = src->Data.Pitch; accel_dst.Data.Y = calloc( 1, in_pitch*h ); accel_dst.Data.VU = calloc( 1, in_pitch*h/2 ); sts = core->CopyFrame(core->pthis, &accel_dst, src); if (sts < MFX_ERR_NONE) { free(accel_dst.Data.Y); free(accel_dst.Data.VU); copyframe_in_use = 0; } else { in_luma = accel_dst.Data.Y + accel_dst.Info.CropY * in_pitch + accel_dst.Info.CropX; in_chroma = accel_dst.Data.VU + accel_dst.Info.CropY / 2 * in_pitch + accel_dst.Info.CropX; } } if (!copyframe_in_use) { in_luma = src->Data.Y + src->Info.CropY * in_pitch + src->Info.CropX; in_chroma = src->Data.VU + src->Info.CropY / 2 * in_pitch + src->Info.CropX; } hb_video_buffer_realloc( dst, w, h ); uint8_t *srcs[] = { in_luma, in_chroma }; int srcs_stride[] = { in_pitch, in_pitch }; uint8_t *dsts[] = { dst->plane[0].data, dst->plane[1].data, dst->plane[2].data }; int dsts_stride[] = { dst->plane[0].stride, dst->plane[1].stride, dst->plane[2].stride }; ret = sws_scale(sws_context, srcs, srcs_stride, 0, h, dsts, dsts_stride ); if (copyframe_in_use) { free(accel_dst.Data.Y); free(accel_dst.Data.VU); } return ret; } int qsv_yuv420_to_nv12(struct SwsContext* sws_context,mfxFrameSurface1* dst, hb_buffer_t* src){ int ret = 0; int w = src->plane[0].width; int h = src->plane[0].height; int out_pitch = dst->Data.Pitch; uint8_t *out_luma = dst->Data.Y; uint8_t *out_chroma = dst->Data.VU; uint8_t *srcs[] = { src->plane[0].data, src->plane[1].data, src->plane[2].data }; int srcs_stride[] = { src->plane[0].stride, src->plane[1].stride, src->plane[2].stride }; uint8_t *dsts[] = { out_luma, out_chroma }; int dsts_stride[] = { out_pitch, out_pitch }; ret = sws_scale(sws_context, srcs, srcs_stride, 0, h, dsts, dsts_stride ); return ret; } #endif // USE_QSV HandBrake-0.10.2/libhb/vfr.c0000664000175200017520000005451212463330511016065 0ustar handbrakehandbrake/* vfr.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" struct hb_filter_private_s { hb_job_t * job; int cfr; int input_vrate; int input_vrate_base; int vrate; int vrate_base; hb_fifo_t * delay_queue; int dropped_frames; int extended_frames; uint64_t last_start[4]; uint64_t last_stop[4]; uint64_t lost_time[4]; uint64_t total_lost_time; uint64_t total_gained_time; int count_frames; // frames output so far double frame_rate; // 90KHz ticks per frame (for CFR/PFR) uint64_t out_last_stop; // where last frame ended (for CFR/PFR) int drops; // frames dropped (for CFR/PFR) int dups; // frames duped (for CFR/PFR) // Duplicate frame detection members float max_metric; // highest motion metric since // last output frame float frame_metric; // motion metric of last frame float out_metric; // motion metric of last output frame int sync_parity; unsigned gamma_lut[256]; }; static int hb_vfr_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int hb_vfr_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static void hb_vfr_close( hb_filter_object_t * filter ); static int hb_vfr_info( hb_filter_object_t * filter, hb_filter_info_t * info ); hb_filter_object_t hb_filter_vfr = { .id = HB_FILTER_VFR, .enforce_order = 1, .name = "Framerate Shaper", .settings = NULL, .init = hb_vfr_init, .work = hb_vfr_work, .close = hb_vfr_close, .info = hb_vfr_info, }; // Create gamma lookup table. // Note that we are creating a scaled integer lookup table that will // not cause overflows in sse_block16() below. This results in // small values being truncated to 0 which is ok for this usage. static void build_gamma_lut( hb_filter_private_t * pv ) { int i; for( i = 0; i < 256; i++ ) { pv->gamma_lut[i] = 4095 * pow( ( (float)i / (float)255 ), 2.2f ); } } // insert buffer 'succ' after buffer chain element 'pred'. // caller must guarantee that 'pred' and 'succ' are non-null. static hb_buffer_t *insert_buffer_in_chain( hb_buffer_t *pred, hb_buffer_t *succ ) { succ->next = pred->next; pred->next = succ; return succ; } #define DUP_THRESH_SSE 5.0 // Compute ths sum of squared errors for a 16x16 block // Gamma adjusts pixel values so that less visible diffreences // count less. static inline unsigned sse_block16( hb_filter_private_t *pv, uint8_t *a, uint8_t *b, int stride ) { int x, y; unsigned sum = 0; int diff; unsigned *g = pv->gamma_lut; for( y = 0; y < 16; y++ ) { for( x = 0; x < 16; x++ ) { diff = g[a[x]] - g[b[x]]; sum += diff * diff; } a += stride; b += stride; } return sum; } // Sum of squared errors. Computes and sums the SSEs for all // 16x16 blocks in the images. Only checks the Y component. static float motion_metric( hb_filter_private_t * pv, hb_buffer_t * a, hb_buffer_t * b ) { int bw = a->f.width / 16; int bh = a->f.height / 16; int stride = a->plane[0].stride; uint8_t * pa = a->plane[0].data; uint8_t * pb = b->plane[0].data; int x, y; uint64_t sum = 0; for( y = 0; y < bh; y++ ) { for( x = 0; x < bw; x++ ) { sum += sse_block16( pv, pa + y * 16 * stride + x * 16, pb + y * 16 * stride + x * 16, stride ); } } return (float)sum / ( a->f.width * a->f.height );; } // This section of the code implements video frame rate control. // Since filters are allowed to duplicate and drop frames (which // changes the timing), this has to be the last thing done in render. // // There are three options, selected by the value of cfr: // 0 - Variable Frame Rate (VFR) or 'same as source': frame times // are left alone // 1 - Constant Frame Rate (CFR): Frame timings are adjusted so that all // frames are exactly vrate_base ticks apart. Frames are dropped // or duplicated if necessary to maintain this spacing. // 2 - Peak Frame Rate (PFR): vrate_base is treated as the peak // average frame rate. I.e., the average frame rate (current frame // end time divided by number of frames so far) is never allowed to be // greater than vrate_base and frames are dropped if necessary // to keep the average under this value. Other than those drops, frame // times are left alone. // static void adjust_frame_rate( hb_filter_private_t *pv, hb_buffer_t **buf_out ) { hb_buffer_t *out = *buf_out; if ( out && out->size > 0 ) { if ( pv->cfr == 0 ) { ++pv->count_frames; pv->out_last_stop = out->s.stop; return; } // compute where this frame would stop if the frame rate were constant // (this is our target stopping time for CFR and earliest possible // stopping time for PFR). double cfr_stop = pv->frame_rate * ( pv->count_frames + 1 ); hb_buffer_t * next = hb_fifo_see( pv->delay_queue ); float next_metric = 0; if( next ) next_metric = motion_metric( pv, out, next ); if( pv->out_last_stop >= out->s.stop ) { ++pv->drops; hb_buffer_close( buf_out ); pv->frame_metric = next_metric; if( next_metric > pv->max_metric ) pv->max_metric = next_metric; return; } if( out->s.start <= pv->out_last_stop && out->s.stop > pv->out_last_stop && next && next->s.stop < cfr_stop ) { // This frame starts before the end of the last output // frame and ends after the end of the last output // frame (i.e. it straddles it). Also the next frame // ends before the end of the next output frame. If the // next frame is not a duplicate, and we haven't seen // a changed frame since the last output frame, // then drop this frame. // // This causes us to sync to the pattern of progressive // 23.976 fps content that has been upsampled to // progressive 59.94 fps. if( pv->out_metric > pv->max_metric && next_metric > pv->max_metric ) { // Pattern: N R R N // o c n // N == new frame // R == repeat frame // o == last output frame // c == current frame // n == next frame // We haven't seen a frame change since the last output // frame and the next frame changes. Use the next frame, // drop this one. ++pv->drops; pv->frame_metric = next_metric; pv->max_metric = next_metric; pv->sync_parity = 1; hb_buffer_close( buf_out ); return; } else if( pv->sync_parity && pv->out_metric < pv->max_metric && pv->max_metric > pv->frame_metric && pv->frame_metric < next_metric ) { // Pattern: R N R N // o c n // N == new frame // R == repeat frame // o == last output frame // c == current frame // n == next frame // If we see this pattern, we must not use the next // frame when straddling the current frame. pv->sync_parity = 0; } else if( pv->sync_parity ) { // The pattern is indeterminate. Continue dropping // frames on the same schedule ++pv->drops; pv->frame_metric = next_metric; pv->max_metric = next_metric; pv->sync_parity = 1; hb_buffer_close( buf_out ); return; } } // this frame has to start where the last one stopped. out->s.start = pv->out_last_stop; pv->out_metric = pv->frame_metric; pv->frame_metric = next_metric; pv->max_metric = next_metric; // at this point we know that this frame doesn't push the average // rate over the limit so we just pass it on for PFR. For CFR we're // going to return it (with its start & stop times modified) and // we may have to dup it. ++pv->count_frames; if ( pv->cfr > 1 ) { // PFR - we're going to keep the frame but may need to // adjust it's stop time to meet the average rate constraint. if ( out->s.stop <= cfr_stop ) { out->s.stop = cfr_stop; } pv->out_last_stop = out->s.stop; } else { // we're doing CFR so we have to either trim some time from a // buffer that ends too far in the future or, if the buffer is // two or more frame times long, split it into multiple pieces, // each of which is a frame time long. double excess_dur = (double)out->s.stop - cfr_stop; out->s.stop = cfr_stop; pv->out_last_stop = out->s.stop; for ( ; excess_dur >= pv->frame_rate; excess_dur -= pv->frame_rate ) { /* next frame too far ahead - dup current frame */ hb_buffer_t *dup = hb_buffer_dup( out ); dup->s.new_chap = 0; dup->s.start = cfr_stop; cfr_stop += pv->frame_rate; dup->s.stop = cfr_stop; pv->out_last_stop = dup->s.stop; out = insert_buffer_in_chain( out, dup ); ++pv->dups; ++pv->count_frames; } } } } static int hb_vfr_init(hb_filter_object_t *filter, hb_filter_init_t *init) { filter->private_data = calloc(1, sizeof(struct hb_filter_private_s)); hb_filter_private_t *pv = filter->private_data; build_gamma_lut(pv); pv->cfr = init->cfr; pv->input_vrate = pv->vrate = init->vrate; pv->input_vrate_base = pv->vrate_base = init->vrate_base; if (filter->settings != NULL) { sscanf(filter->settings, "%d:%d:%d", &pv->cfr, &pv->vrate, &pv->vrate_base); } pv->job = init->job; /* Setup FIFO queue for subtitle cache */ pv->delay_queue = hb_fifo_init( 8, 1 ); /* VFR IVTC needs a bunch of time-keeping variables to track how many frames are dropped, how many are extended, what the last 4 start and stop times were (so they can be modified), how much time has been lost and gained overall, how much time the latest 4 frames should be extended by */ pv->dropped_frames = 0; pv->extended_frames = 0; pv->last_start[0] = 0; pv->last_stop[0] = 0; pv->total_lost_time = 0; pv->total_gained_time = 0; pv->lost_time[0] = 0; pv->lost_time[1] = 0; pv->lost_time[2] = 0; pv->lost_time[3] = 0; pv->frame_metric = 1000; // Force first frame if (pv->cfr == 2) { // For PFR, we want the framerate based on the source's actual // framerate, unless it's higher than the specified peak framerate. double source_fps = (double)init->vrate / init->vrate_base; double peak_fps = (double)pv->vrate / pv->vrate_base; if (source_fps > peak_fps) { // peak framerate is lower than the source framerate. // so signal that the framerate will be the peak fps. init->vrate = pv->vrate; init->vrate_base = pv->vrate_base; } } else { init->vrate = pv->vrate; init->vrate_base = pv->vrate_base; } pv->frame_rate = (double)pv->vrate_base * 90000. / pv->vrate; init->cfr = pv->cfr; return 0; } static int hb_vfr_info( hb_filter_object_t * filter, hb_filter_info_t * info ) { hb_filter_private_t * pv = filter->private_data; if( !pv ) return 1; memset( info, 0, sizeof( hb_filter_info_t ) ); info->out.vrate_base = pv->input_vrate_base; info->out.vrate = pv->input_vrate; if (pv->cfr == 2) { // For PFR, we want the framerate based on the source's actual // framerate, unless it's higher than the specified peak framerate. double source_fps = (double)pv->input_vrate / pv->input_vrate_base; double peak_fps = (double)pv->vrate / pv->vrate_base; if (source_fps > peak_fps) { // peak framerate is lower than the source framerate. // so signal that the framerate will be the peak fps. info->out.vrate = pv->vrate; info->out.vrate_base = pv->vrate_base; } } else { info->out.vrate = pv->vrate; info->out.vrate_base = pv->vrate_base; } info->out.cfr = pv->cfr; if ( pv->cfr == 0 ) { /* Ensure we're using "Same as source" FPS */ sprintf( info->human_readable_desc, "frame rate: same as source (around %.3f fps)", (float)pv->vrate / pv->vrate_base ); } else if ( pv->cfr == 2 ) { // For PFR, we want the framerate based on the source's actual // framerate, unless it's higher than the specified peak framerate. double source_fps = (double)pv->input_vrate / pv->input_vrate_base; double peak_fps = (double)pv->vrate / pv->vrate_base; sprintf( info->human_readable_desc, "frame rate: %.3f fps -> peak rate limited to %.3f fps", source_fps , peak_fps ); } else { // Constant framerate. Signal the framerate we are using. double source_fps = (double)pv->input_vrate / pv->input_vrate_base; double constant_fps = (double)pv->vrate / pv->vrate_base; sprintf( info->human_readable_desc, "frame rate: %.3f fps -> constant %.3f fps", source_fps , constant_fps ); } return 0; } static void hb_vfr_close( hb_filter_object_t * filter ) { hb_filter_private_t * pv = filter->private_data; if( !pv ) return; if ( pv->cfr ) { hb_log("render: %d frames output, %d dropped and %d duped for CFR/PFR", pv->count_frames, pv->drops, pv->dups ); } if( pv->job ) { hb_interjob_t * interjob = hb_interjob_get( pv->job->h ); /* Preserve dropped frame count for more accurate * framerates in 2nd passes. */ interjob->out_frame_count = pv->count_frames; interjob->total_time = pv->out_last_stop; } hb_log("render: lost time: %"PRId64" (%i frames)", pv->total_lost_time, pv->dropped_frames); hb_log("render: gained time: %"PRId64" (%i frames) (%"PRId64" not accounted for)", pv->total_gained_time, pv->extended_frames, pv->total_lost_time - pv->total_gained_time); if (pv->dropped_frames) { hb_log("render: average dropped frame duration: %"PRId64, (pv->total_lost_time / pv->dropped_frames) ); } if( pv->delay_queue ) { hb_fifo_close( &pv->delay_queue ); } /* Cleanup render work structure */ free( pv ); filter->private_data = NULL; } static int hb_vfr_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in; hb_buffer_t * out = NULL; *buf_in = NULL; *buf_out = NULL; if( in->size <= 0 ) { hb_buffer_t *head = NULL, *tail = NULL, *next; int counter = 2; /* If the input buffer is end of stream, send out an empty one * to the next stage as well. To avoid losing the contents of * the delay queue connect the buffers in the delay queue in * the correct order, and add the end of stream buffer to the * end. */ while( ( next = hb_fifo_get( pv->delay_queue ) ) != NULL ) { /* We can't use the given time stamps. Previous frames might already have been extended, throwing off the raw values fed to render.c. Instead, their stop and start times are stored in arrays. The 4th cached frame will be the to use. If it needed its duration extended to make up lost time, it will have happened above. */ next->s.start = pv->last_start[counter]; next->s.stop = pv->last_stop[counter--]; adjust_frame_rate( pv, &next ); if( next ) { if( !head && !tail ) { head = next; } else { tail->next = next; } // Move tail to the end of the list that // adjust_frame_rate could return while (next) { tail = next; next = next->next; } } } if( tail ) { tail->next = in; *buf_out = head; } else { *buf_out = in; } return HB_FILTER_DONE; } // If there is a gap between the last stop and the current start // then frame(s) were dropped. if ( in->s.start > pv->last_stop[0] ) { /* We need to compensate for the time lost by dropping frame(s). Spread its duration out in quarters, because usually dropped frames maintain a 1-out-of-5 pattern and this spreads it out amongst the remaining ones. Store these in the lost_time array, which has 4 slots in it. Because not every frame duration divides evenly by 4, and we can't lose the remainder, we have to go through an awkward process to preserve it in the 4th array index. */ uint64_t temp_duration = in->s.start - pv->last_stop[0]; pv->lost_time[0] += (temp_duration / 4); pv->lost_time[1] += (temp_duration / 4); pv->lost_time[2] += (temp_duration / 4); pv->lost_time[3] += ( temp_duration - 3 * (temp_duration / 4) ); pv->total_lost_time += temp_duration; } else if ( in->s.stop <= pv->last_stop[0] ) { // This is generally an error somewhere (bad source or hb bug). // But lets do our best to straighten out the mess. ++pv->drops; hb_buffer_close(&in); return HB_FILTER_OK; } /* Cache frame start and stop times, so we can renumber time stamps if dropping frames for VFR. */ int i; for( i = 3; i >= 1; i-- ) { pv->last_start[i] = pv->last_start[i-1]; pv->last_stop[i] = pv->last_stop[i-1]; } /* In order to make sure we have continuous time stamps, store the current frame's duration as starting when the last one stopped. */ pv->last_start[0] = pv->last_stop[1]; pv->last_stop[0] = pv->last_start[0] + (in->s.stop - in->s.start); hb_fifo_push( pv->delay_queue, in ); /* * Keep the last three frames in our queue, this ensures that we have * the last two always in there should we need to rewrite the * durations on them. */ if( hb_fifo_size( pv->delay_queue ) >= 4 ) { out = hb_fifo_get( pv->delay_queue ); } if( out ) { /* The current frame exists. That means it hasn't been dropped by a * filter. We may edit its duration if needed. */ if( pv->lost_time[3] > 0 ) { int time_shift = 0; for( i = 3; i >= 0; i-- ) { /* * A frame's been dropped earlier by VFR detelecine. * Gotta make up the lost time. This will also * slow down the video. * The dropped frame's has to be accounted for, so * divvy it up amongst the 4 frames left behind. * This is what the delay_queue is for; * telecined sequences start 2 frames before * the dropped frame, so to slow down the right * ones you need a 2 frame delay between * reading input and writing output. */ /* We want to extend the outputted frame's duration by the value stored in the 4th slot of the lost_time array. Because we need to adjust all the values in the array so they're contiguous, extend the duration inside the array first, before applying it to the current frame buffer. */ pv->last_start[i] += time_shift; pv->last_stop[i] += pv->lost_time[i] + time_shift; /* Log how much time has been added back in to the video. */ pv->total_gained_time += pv->lost_time[i]; time_shift += pv->lost_time[i]; pv->lost_time[i] = 0; /* Log how many frames have had their durations extended. */ pv->extended_frames++; } } /* We can't use the given time stamps. Previous frames might already have been extended, throwing off the raw values fed to render.c. Instead, their stop and start times are stored in arrays. The 4th cached frame will be the to use. If it needed its duration extended to make up lost time, it will have happened above. */ out->s.start = pv->last_start[3]; out->s.stop = pv->last_stop[3]; adjust_frame_rate( pv, &out ); } *buf_out = out; return HB_FILTER_OK; } HandBrake-0.10.2/libhb/dvdnav.c0000664000175200017520000017433612463330511016561 0ustar handbrakehandbrake/* dvdnav.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "lang.h" #include "dvd.h" #include "dvdnav/dvdnav.h" #include "dvdread/ifo_read.h" #include "dvdread/ifo_print.h" #include "dvdread/nav_read.h" #define DVD_READ_CACHE 1 static char * hb_dvdnav_name( char * path ); static hb_dvd_t * hb_dvdnav_init( char * path ); static int hb_dvdnav_title_count( hb_dvd_t * d ); static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * d, int t, uint64_t min_duration ); static int hb_dvdnav_start( hb_dvd_t * d, hb_title_t *title, int chapter ); static void hb_dvdnav_stop( hb_dvd_t * d ); static int hb_dvdnav_seek( hb_dvd_t * d, float f ); static hb_buffer_t * hb_dvdnav_read( hb_dvd_t * d ); static int hb_dvdnav_chapter( hb_dvd_t * d ); static void hb_dvdnav_close( hb_dvd_t ** _d ); static int hb_dvdnav_angle_count( hb_dvd_t * d ); static void hb_dvdnav_set_angle( hb_dvd_t * d, int angle ); static int hb_dvdnav_main_feature( hb_dvd_t * d, hb_list_t * list_title ); hb_dvd_func_t hb_dvdnav_func = { hb_dvdnav_init, hb_dvdnav_close, hb_dvdnav_name, hb_dvdnav_title_count, hb_dvdnav_title_scan, hb_dvdnav_start, hb_dvdnav_stop, hb_dvdnav_seek, hb_dvdnav_read, hb_dvdnav_chapter, hb_dvdnav_angle_count, hb_dvdnav_set_angle, hb_dvdnav_main_feature }; // there can be at most 999 PGCs per title. round that up to the nearest // power of two. #define MAX_PGCN 1024 /*********************************************************************** * Local prototypes **********************************************************************/ static void PgcWalkInit( uint32_t pgcn_map[MAX_PGCN/32] ); static int FindChapterIndex( hb_list_t * list, int pgcn, int pgn ); static int NextPgcn( ifo_handle_t *ifo, int pgcn, uint32_t pgcn_map[MAX_PGCN/32] ); static int FindNextCell( pgc_t *pgc, int cell_cur ); static int dvdtime2msec( dvd_time_t * ); hb_dvd_func_t * hb_dvdnav_methods( void ) { return &hb_dvdnav_func; } static char * hb_dvdnav_name( char * path ) { static char name[1024]; unsigned char unused[1024]; dvd_reader_t * reader; reader = DVDOpen( path ); if( !reader ) { return NULL; } if( DVDUDFVolumeInfo( reader, name, sizeof( name ), unused, sizeof( unused ) ) ) { DVDClose( reader ); return NULL; } DVDClose( reader ); return name; } /*********************************************************************** * hb_dvdnav_reset *********************************************************************** * Once dvdnav has entered the 'stopped' state, it can not be revived * dvdnav_reset doesn't work because it doesn't remember the path * So this function re-opens dvdnav **********************************************************************/ static int hb_dvdnav_reset( hb_dvdnav_t * d ) { char * path_ccp = hb_utf8_to_cp( d->path ); if ( d->dvdnav ) dvdnav_close( d->dvdnav ); /* Open device */ if( dvdnav_open(&d->dvdnav, path_ccp) != DVDNAV_STATUS_OK ) { /* * Not an error, may be a stream - which we'll try in a moment. */ hb_log( "dvd: not a dvd - trying as a stream/file instead" ); goto fail; } if (dvdnav_set_readahead_flag(d->dvdnav, DVD_READ_CACHE) != DVDNAV_STATUS_OK) { hb_error("Error: dvdnav_set_readahead_flag: %s\n", dvdnav_err_to_string(d->dvdnav)); goto fail; } /* ** set the PGC positioning flag to have position information ** relatively to the whole feature instead of just relatively to the ** current chapter **/ if (dvdnav_set_PGC_positioning_flag(d->dvdnav, 1) != DVDNAV_STATUS_OK) { hb_error("Error: dvdnav_set_PGC_positioning_flag: %s\n", dvdnav_err_to_string(d->dvdnav)); goto fail; } free( path_ccp ); return 1; fail: if( d->dvdnav ) dvdnav_close( d->dvdnav ); free( path_ccp ); return 0; } /*********************************************************************** * hb_dvdnav_init *********************************************************************** * **********************************************************************/ static hb_dvd_t * hb_dvdnav_init( char * path ) { hb_dvd_t * e; hb_dvdnav_t * d; int region_mask; char * path_ccp; e = calloc( sizeof( hb_dvd_t ), 1 ); d = &(e->dvdnav); /* * Convert UTF-8 path to current code page on Windows * hb_utf8_to_cp() is the same as strdup on non-Windows, * so no #ifdef required here */ path_ccp = hb_utf8_to_cp( path ); /* Log DVD drive region code */ if ( hb_dvd_region( path_ccp, ®ion_mask ) == 0 ) { hb_log( "dvd: Region mask 0x%02x", region_mask ); if ( region_mask == 0xFF ) { hb_log( "dvd: Warning, DVD device has no region set" ); } } /* Open device */ if( dvdnav_open(&d->dvdnav, path_ccp) != DVDNAV_STATUS_OK ) { /* * Not an error, may be a stream - which we'll try in a moment. */ hb_log( "dvd: not a dvd - trying as a stream/file instead" ); goto fail; } if (dvdnav_set_readahead_flag(d->dvdnav, DVD_READ_CACHE) != DVDNAV_STATUS_OK) { hb_error("Error: dvdnav_set_readahead_flag: %s\n", dvdnav_err_to_string(d->dvdnav)); goto fail; } /* ** set the PGC positioning flag to have position information ** relatively to the whole feature instead of just relatively to the ** current chapter **/ if (dvdnav_set_PGC_positioning_flag(d->dvdnav, 1) != DVDNAV_STATUS_OK) { hb_error("Error: dvdnav_set_PGC_positioning_flag: %s\n", dvdnav_err_to_string(d->dvdnav)); goto fail; } /* Open device */ if( !( d->reader = DVDOpen( path_ccp ) ) ) { /* * Not an error, may be a stream - which we'll try in a moment. */ hb_log( "dvd: not a dvd - trying as a stream/file instead" ); goto fail; } /* Open main IFO */ if( !( d->vmg = ifoOpen( d->reader, 0 ) ) ) { hb_error( "dvd: ifoOpen failed" ); goto fail; } d->path = strdup( path ); /* hb_dvdnav_title_scan assumes UTF-8 path, so not path_ccp here */ free( path_ccp ); return e; fail: if( d->dvdnav ) dvdnav_close( d->dvdnav ); if( d->vmg ) ifoClose( d->vmg ); if( d->reader ) DVDClose( d->reader ); free( e ); free( path_ccp ); return NULL; } /*********************************************************************** * hb_dvdnav_title_count **********************************************************************/ static int hb_dvdnav_title_count( hb_dvd_t * e ) { int titles = 0; hb_dvdnav_t * d = &(e->dvdnav); dvdnav_get_number_of_titles(d->dvdnav, &titles); return titles; } static uint64_t PttDuration(ifo_handle_t *ifo, int ttn, int pttn, int *blocks, int *last_pgcn) { int pgcn, pgn; pgc_t * pgc; uint64_t duration = 0; int cell_start, cell_end; int i; *blocks = 0; // Initialize map of visited pgc's to prevent loops uint32_t pgcn_map[MAX_PGCN/32]; PgcWalkInit( pgcn_map ); pgcn = ifo->vts_ptt_srpt->title[ttn-1].ptt[pttn-1].pgcn; pgn = ifo->vts_ptt_srpt->title[ttn-1].ptt[pttn-1].pgn; if ( pgcn < 1 || pgcn > ifo->vts_pgcit->nr_of_pgci_srp || pgcn >= MAX_PGCN) { hb_log( "invalid PGC ID %d, skipping", pgcn ); return 0; } if( pgn <= 0 || pgn > 99 ) { hb_log( "scan: pgn %d not valid, skipping", pgn ); return 0; } do { pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc; if (!pgc) { *blocks = 0; duration = 0; hb_log( "scan: pgc not valid, skipping" ); break; } if (pgc->cell_playback == NULL) { *blocks = 0; duration = 0; hb_log("invalid PGC cell_playback table, skipping"); break; } if (pgn > pgc->nr_of_programs) { pgn = 1; continue; } duration += 90LL * dvdtime2msec( &pgc->playback_time ); cell_start = pgc->program_map[pgn-1] - 1; cell_end = pgc->nr_of_cells - 1; for(i = cell_start; i <= cell_end; i = FindNextCell(pgc, i)) { *blocks += pgc->cell_playback[i].last_sector + 1 - pgc->cell_playback[i].first_sector; } *last_pgcn = pgcn; pgn = 1; } while((pgcn = NextPgcn(ifo, pgcn, pgcn_map)) != 0); return duration; } /*********************************************************************** * hb_dvdnav_title_scan **********************************************************************/ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t, uint64_t min_duration ) { hb_dvdnav_t * d = &(e->dvdnav); hb_title_t * title; ifo_handle_t * ifo = NULL; int pgcn, pgn, pgcn_end, i, c; int title_pgcn; pgc_t * pgc; int cell_cur; hb_chapter_t * chapter; int count; uint64_t duration, longest; int longest_pgcn, longest_pgn, longest_pgcn_end; const char * name; const char * codec_name; hb_log( "scan: scanning title %d", t ); title = hb_title_init( d->path, t ); title->type = HB_DVD_TYPE; if (dvdnav_get_title_string(d->dvdnav, &name) == DVDNAV_STATUS_OK) { strncpy( title->name, name, sizeof( title->name ) ); } else { char * p_cur, * p_last = d->path; for( p_cur = d->path; *p_cur; p_cur++ ) { if( IS_DIR_SEP(p_cur[0]) && p_cur[1] ) { p_last = &p_cur[1]; } } snprintf( title->name, sizeof( title->name ), "%s", p_last ); char *dot_term = strrchr(title->name, '.'); if (dot_term) *dot_term = '\0'; } /* VTS which our title is in */ title->vts = d->vmg->tt_srpt->title[t-1].title_set_nr; if ( !title->vts ) { /* A VTS of 0 means the title wasn't found in the title set */ hb_log("Invalid VTS (title set) number: %i", title->vts); goto fail; } hb_log( "scan: opening IFO for VTS %d", title->vts ); if( !( ifo = ifoOpen( d->reader, title->vts ) ) ) { hb_log( "scan: ifoOpen failed" ); goto fail; } /* ignore titles with bogus cell addresses so we don't abort later ** in libdvdread. */ for ( i = 0; i < ifo->vts_c_adt->nr_of_vobs; ++i) { if( (ifo->vts_c_adt->cell_adr_table[i].start_sector & 0xffffff ) == 0xffffff ) { hb_log( "scan: cell_adr_table[%d].start_sector invalid (0x%x) " "- skipping title", i, ifo->vts_c_adt->cell_adr_table[i].start_sector ); goto fail; } if( (ifo->vts_c_adt->cell_adr_table[i].last_sector & 0xffffff ) == 0xffffff ) { hb_log( "scan: cell_adr_table[%d].last_sector invalid (0x%x) " "- skipping title", i, ifo->vts_c_adt->cell_adr_table[i].last_sector ); goto fail; } } if( global_verbosity_level == 3 ) { ifo_print( d->reader, title->vts ); } /* Position of the title in the VTS */ title->ttn = d->vmg->tt_srpt->title[t-1].vts_ttn; if ( title->ttn < 1 || title->ttn > ifo->vts_ptt_srpt->nr_of_srpts ) { hb_log( "invalid VTS PTT offset %d for title %d, skipping", title->ttn, t ); goto fail; } longest = 0LL; longest_pgcn = -1; longest_pgn = 1; longest_pgcn_end = -1; pgcn_end = -1; for( i = 0; i < ifo->vts_ptt_srpt->title[title->ttn-1].nr_of_ptts; i++ ) { int blocks = 0; duration = PttDuration(ifo, title->ttn, i+1, &blocks, &pgcn_end); pgcn = ifo->vts_ptt_srpt->title[title->ttn-1].ptt[i].pgcn; pgn = ifo->vts_ptt_srpt->title[title->ttn-1].ptt[i].pgn; if( duration > longest ) { longest_pgcn = pgcn; longest_pgn = pgn; longest_pgcn_end = pgcn_end; longest = duration; title->block_count = blocks; } else if (pgcn == longest_pgcn && pgn < longest_pgn) { longest_pgn = pgn; title->block_count = blocks; } } /* Get duration */ title->duration = longest; title->hours = title->duration / 90000 / 3600; title->minutes = ( ( title->duration / 90000 ) % 3600 ) / 60; title->seconds = ( title->duration / 90000 ) % 60; hb_log( "scan: duration is %02d:%02d:%02d (%"PRId64" ms)", title->hours, title->minutes, title->seconds, title->duration / 90 ); /* ignore titles under 10 seconds because they're often stills or * clips with no audio & our preview code doesn't currently handle * either of these. */ if( longest < min_duration ) { hb_log( "scan: ignoring title (too short)" ); goto fail; } pgcn = longest_pgcn; pgcn_end = longest_pgcn_end; pgn = longest_pgn;; title_pgcn = pgcn; /* Get pgc */ if ( pgcn < 1 || pgcn > ifo->vts_pgcit->nr_of_pgci_srp || pgcn >= MAX_PGCN) { hb_log( "invalid PGC ID %d for title %d, skipping", pgcn, t ); goto fail; } // Check all pgc's for validity uint32_t pgcn_map[MAX_PGCN/32]; PgcWalkInit( pgcn_map ); do { pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc; if( !pgc || !pgc->program_map ) { hb_log( "scan: pgc not valid, skipping" ); goto fail; } if (pgc->cell_playback == NULL) { hb_log( "invalid PGC cell_playback table for title %d, skipping", t ); goto fail; } } while ((pgcn = NextPgcn(ifo, pgcn, pgcn_map)) != 0); pgcn = longest_pgcn; pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc; hb_log("pgc_id: %d, pgn: %d: pgc: %p", pgcn, pgn, pgc); if (pgn > pgc->nr_of_programs) { hb_log( "invalid PGN %d for title %d, skipping", pgn, t ); goto fail; } /* Title start */ title->cell_start = pgc->program_map[pgn-1] - 1; title->block_start = pgc->cell_playback[title->cell_start].first_sector; pgc = ifo->vts_pgcit->pgci_srp[pgcn_end-1].pgc; /* Title end */ title->cell_end = pgc->nr_of_cells - 1; title->block_end = pgc->cell_playback[title->cell_end].last_sector; hb_log( "scan: vts=%d, ttn=%d, cells=%d->%d, blocks=%"PRIu64"->%"PRIu64", " "%"PRIu64" blocks", title->vts, title->ttn, title->cell_start, title->cell_end, title->block_start, title->block_end, title->block_count ); /* Detect languages */ for( i = 0; i < ifo->vtsi_mat->nr_of_vts_audio_streams; i++ ) { int audio_format, lang_code, lang_extension, audio_control, position, j; hb_audio_t * audio, * audio_tmp; iso639_lang_t * lang; hb_log( "scan: checking audio %d", i + 1 ); audio = calloc( sizeof( hb_audio_t ), 1 ); audio_format = ifo->vtsi_mat->vts_audio_attr[i].audio_format; lang_code = ifo->vtsi_mat->vts_audio_attr[i].lang_code; lang_extension = ifo->vtsi_mat->vts_audio_attr[i].code_extension; audio_control = ifo->vts_pgcit->pgci_srp[title_pgcn-1].pgc->audio_control[i]; if( !( audio_control & 0x8000 ) ) { hb_log( "scan: audio channel is not active" ); free( audio ); continue; } position = ( audio_control & 0x7F00 ) >> 8; switch( audio_format ) { case 0x00: audio->id = ( ( 0x80 + position ) << 8 ) | 0xbd; audio->config.in.codec = HB_ACODEC_AC3; audio->config.in.codec_param = AV_CODEC_ID_AC3; codec_name = "AC3"; break; case 0x02: case 0x03: audio->id = 0xc0 + position; audio->config.in.codec = HB_ACODEC_FFMPEG; audio->config.in.codec_param = AV_CODEC_ID_MP2; codec_name = "MPEG"; break; case 0x04: audio->id = ( ( 0xa0 + position ) << 8 ) | 0xbd; audio->config.in.codec = HB_ACODEC_LPCM; codec_name = "LPCM"; break; case 0x06: audio->id = ( ( 0x88 + position ) << 8 ) | 0xbd; audio->config.in.codec = HB_ACODEC_DCA; audio->config.in.codec_param = AV_CODEC_ID_DTS; codec_name = "DTS"; break; default: audio->id = 0; audio->config.in.codec = 0; codec_name = "Unknown"; hb_log( "scan: unknown audio codec (%x)", audio_format ); break; } if( !audio->id ) { continue; } /* Check for duplicate tracks */ audio_tmp = NULL; for( j = 0; j < hb_list_count( title->list_audio ); j++ ) { audio_tmp = hb_list_item( title->list_audio, j ); if( audio->id == audio_tmp->id ) { break; } audio_tmp = NULL; } if( audio_tmp ) { hb_log( "scan: duplicate audio track" ); free( audio ); continue; } lang = lang_for_code( lang_code ); audio->config.lang.type = lang_extension; snprintf( audio->config.lang.simple, sizeof( audio->config.lang.simple ), "%s", strlen( lang->native_name ) ? lang->native_name : lang->eng_name ); snprintf( audio->config.lang.iso639_2, sizeof( audio->config.lang.iso639_2 ), "%s", lang->iso639_2 ); hb_log("scan: id=0x%x, lang=%s (%s), 3cc=%s ext=%i", audio->id, audio->config.lang.simple, codec_name, audio->config.lang.iso639_2, lang_extension); audio->config.in.track = i; hb_list_add( title->list_audio, audio ); } /* Check for subtitles */ for( i = 0; i < ifo->vtsi_mat->nr_of_vts_subp_streams; i++ ) { hb_subtitle_t * subtitle; int spu_control; int position; iso639_lang_t * lang; int lang_extension = 0; hb_log( "scan: checking subtitle %d", i + 1 ); spu_control = ifo->vts_pgcit->pgci_srp[title_pgcn-1].pgc->subp_control[i]; if( !( spu_control & 0x80000000 ) ) { hb_log( "scan: subtitle channel is not active" ); continue; } if( ifo->vtsi_mat->vts_video_attr.display_aspect_ratio ) { switch( ifo->vtsi_mat->vts_video_attr.permitted_df ) { case 1: position = spu_control & 0xFF; break; case 2: position = ( spu_control >> 8 ) & 0xFF; break; default: position = ( spu_control >> 16 ) & 0xFF; } } else { position = ( spu_control >> 24 ) & 0x7F; } lang_extension = ifo->vtsi_mat->vts_subp_attr[i].code_extension; lang = lang_for_code( ifo->vtsi_mat->vts_subp_attr[i].lang_code ); subtitle = calloc( sizeof( hb_subtitle_t ), 1 ); subtitle->track = i+1; subtitle->id = ( ( 0x20 + position ) << 8 ) | 0xbd; snprintf( subtitle->lang, sizeof( subtitle->lang ), "%s", strlen(lang->native_name) ? lang->native_name : lang->eng_name); snprintf( subtitle->iso639_2, sizeof( subtitle->iso639_2 ), "%s", lang->iso639_2); subtitle->format = PICTURESUB; subtitle->source = VOBSUB; subtitle->config.dest = RENDERSUB; // By default render (burn-in) the VOBSUB. subtitle->stream_type = 0xbd; subtitle->substream_type = 0x20 + position; subtitle->codec = WORK_DECVOBSUB; subtitle->type = lang_extension; memcpy( subtitle->palette, ifo->vts_pgcit->pgci_srp[title_pgcn-1].pgc->palette, 16 * sizeof( uint32_t ) ); subtitle->palette_set = 1; switch( lang_extension ) { case 2: strcat( subtitle->lang, " (Caption with bigger size character)" ); break; case 3: strcat( subtitle->lang, " (Caption for Children)" ); break; case 5: strcat( subtitle->lang, " (Closed Caption)" ); break; case 6: strcat( subtitle->lang, " (Closed Caption with bigger size character)" ); break; case 7: strcat( subtitle->lang, " (Closed Caption for Children)" ); break; case 9: strcat( subtitle->lang, " (Forced Caption)" ); break; case 13: strcat( subtitle->lang, " (Director's Commentary)" ); break; case 14: strcat( subtitle->lang, " (Director's Commentary with bigger size character)" ); break; case 15: strcat( subtitle->lang, " (Director's Commentary for Children)" ); default: break; } hb_log( "scan: id=0x%x, lang=%s, 3cc=%s ext=%i", subtitle->id, subtitle->lang, subtitle->iso639_2, lang_extension ); hb_list_add( title->list_subtitle, subtitle ); } /* Chapters */ PgcWalkInit( pgcn_map ); c = 0; do { pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc; for (i = pgn; i <= pgc->nr_of_programs; i++) { char chapter_title[80]; chapter = calloc( sizeof( hb_chapter_t ), 1 ); chapter->pgcn = pgcn; chapter->pgn = i; chapter->index = c + 1; sprintf( chapter_title, "Chapter %d", chapter->index ); hb_chapter_set_title( chapter, chapter_title ); hb_list_add( title->list_chapter, chapter ); c++; } pgn = 1; } while ((pgcn = NextPgcn(ifo, pgcn, pgcn_map)) != 0); hb_log( "scan: title %d has %d chapters", t, c ); count = hb_list_count( title->list_chapter ); for (i = 0; i < count; i++) { chapter = hb_list_item( title->list_chapter, i ); pgcn = chapter->pgcn; pgn = chapter->pgn; pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc; /* Start cell */ chapter->cell_start = pgc->program_map[pgn-1] - 1; chapter->block_start = pgc->cell_playback[chapter->cell_start].first_sector; // if there are no more programs in this pgc, the end cell is the // last cell. Otherwise it's the cell before the start cell of the // next program. if ( pgn == pgc->nr_of_programs ) { chapter->cell_end = pgc->nr_of_cells - 1; } else { chapter->cell_end = pgc->program_map[pgn] - 2;; } chapter->block_end = pgc->cell_playback[chapter->cell_end].last_sector; /* Block count, duration */ chapter->block_count = 0; chapter->duration = 0; cell_cur = chapter->cell_start; while( cell_cur <= chapter->cell_end ) { #define cp pgc->cell_playback[cell_cur] chapter->block_count += cp.last_sector + 1 - cp.first_sector; chapter->duration += 90LL * dvdtime2msec( &cp.playback_time ); #undef cp cell_cur = FindNextCell( pgc, cell_cur ); } } for( i = 0; i < hb_list_count( title->list_chapter ); i++ ) { chapter = hb_list_item( title->list_chapter, i ); int seconds = ( chapter->duration + 45000 ) / 90000; chapter->hours = ( seconds / 3600 ); chapter->minutes = ( seconds % 3600 ) / 60; chapter->seconds = ( seconds % 60 ); hb_log( "scan: chap %d c=%d->%d, b=%"PRIu64"->%"PRIu64" (%"PRIu64"), %"PRId64" ms", chapter->index, chapter->cell_start, chapter->cell_end, chapter->block_start, chapter->block_end, chapter->block_count, chapter->duration / 90 ); } /* Get aspect. We don't get width/height/rate infos here as they tend to be wrong */ switch( ifo->vtsi_mat->vts_video_attr.display_aspect_ratio ) { case 0: title->container_aspect = 4. / 3.; break; case 3: title->container_aspect = 16. / 9.; break; default: hb_log( "scan: unknown aspect" ); goto fail; } hb_log( "scan: aspect = %g", title->container_aspect ); /* This title is ok so far */ goto cleanup; fail: hb_title_close( &title ); cleanup: if( ifo ) ifoClose( ifo ); return title; } /*********************************************************************** * hb_dvdnav_title_scan **********************************************************************/ static int find_title( hb_list_t * list_title, int title ) { int ii; for ( ii = 0; ii < hb_list_count( list_title ); ii++ ) { hb_title_t * hbtitle = hb_list_item( list_title, ii ); if ( hbtitle->index == title ) return ii; } return -1; } static int skip_to_menu( dvdnav_t * dvdnav, int blocks ) { int ii; int result, event, len; uint8_t buf[HB_DVD_READ_BUFFER_SIZE]; for ( ii = 0; ii < blocks; ii++ ) { result = dvdnav_get_next_block( dvdnav, buf, &event, &len ); if ( result == DVDNAV_STATUS_ERR ) { hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(dvdnav)); return 0; } switch ( event ) { case DVDNAV_BLOCK_OK: break; case DVDNAV_CELL_CHANGE: { } break; case DVDNAV_STILL_FRAME: { dvdnav_still_event_t *event; event = (dvdnav_still_event_t*)buf; dvdnav_still_skip( dvdnav ); if ( event->length == 255 ) { // Infinite still. Can't be the main feature unless // you like watching paint dry. return 0; } } break; case DVDNAV_WAIT: dvdnav_wait_skip( dvdnav ); break; case DVDNAV_STOP: return 0; case DVDNAV_HOP_CHANNEL: break; case DVDNAV_NAV_PACKET: { pci_t *pci = dvdnav_get_current_nav_pci( dvdnav ); if ( pci == NULL ) break; int buttons = pci->hli.hl_gi.btn_ns; int title, part; result = dvdnav_current_title_info( dvdnav, &title, &part ); if (result != DVDNAV_STATUS_OK) { hb_log("dvdnav title info: %s", dvdnav_err_to_string(dvdnav)); } else if ( title == 0 && buttons > 0 ) { // Check button activation duration to see if this // isn't another fake menu. if ( pci->hli.hl_gi.btn_se_e_ptm - pci->hli.hl_gi.hli_s_ptm > 15 * 90000 ) { // Found what appears to be a good menu. return 1; } } } break; case DVDNAV_VTS_CHANGE: { dvdnav_vts_change_event_t *event; event = (dvdnav_vts_change_event_t*)buf; // Some discs initialize the vts with the "first play" item // and some don't seem to. So if we see it is uninitialized, // set it. if ( event->new_vtsN <= 0 ) result = dvdnav_title_play( dvdnav, 1 ); } break; case DVDNAV_HIGHLIGHT: break; case DVDNAV_AUDIO_STREAM_CHANGE: break; case DVDNAV_SPU_STREAM_CHANGE: break; case DVDNAV_SPU_CLUT_CHANGE: break; case DVDNAV_NOP: break; default: break; } } return 0; } static int try_button( dvdnav_t * dvdnav, int button, hb_list_t * list_title ) { int result, event, len; uint8_t buf[HB_DVD_READ_BUFFER_SIZE]; int ii, jj; int32_t cur_title = 0, title, part; uint64_t longest_duration = 0; int longest = -1; pci_t *pci = dvdnav_get_current_nav_pci( dvdnav ); result = dvdnav_button_select_and_activate( dvdnav, pci, button + 1 ); if (result != DVDNAV_STATUS_OK) { hb_log("dvdnav_button_select_and_activate: %s", dvdnav_err_to_string(dvdnav)); } result = dvdnav_current_title_info( dvdnav, &title, &part ); if (result != DVDNAV_STATUS_OK) hb_log("dvdnav cur title info: %s", dvdnav_err_to_string(dvdnav)); cur_title = title; for (jj = 0; jj < 10; jj++) { for (ii = 0; ii < 2000; ii++) { result = dvdnav_get_next_block( dvdnav, buf, &event, &len ); if ( result == DVDNAV_STATUS_ERR ) { hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(dvdnav)); goto done; } switch ( event ) { case DVDNAV_BLOCK_OK: break; case DVDNAV_CELL_CHANGE: { result = dvdnav_current_title_info( dvdnav, &title, &part ); if (result != DVDNAV_STATUS_OK) hb_log("dvdnav title info: %s", dvdnav_err_to_string(dvdnav)); cur_title = title; // Note, some "fake" titles have long advertised durations // but then jump to the real title early in playback. // So keep reading after finding a long title to detect // such cases. } break; case DVDNAV_STILL_FRAME: { dvdnav_still_event_t *event; event = (dvdnav_still_event_t*)buf; dvdnav_still_skip( dvdnav ); if ( event->length == 255 ) { // Infinite still. Can't be the main feature unless // you like watching paint dry. goto done; } } break; case DVDNAV_WAIT: dvdnav_wait_skip( dvdnav ); break; case DVDNAV_STOP: goto done; case DVDNAV_HOP_CHANNEL: break; case DVDNAV_NAV_PACKET: { } break; case DVDNAV_VTS_CHANGE: { result = dvdnav_current_title_info( dvdnav, &title, &part ); if (result != DVDNAV_STATUS_OK) hb_log("dvdnav title info: %s", dvdnav_err_to_string(dvdnav)); cur_title = title; // Note, some "fake" titles have long advertised durations // but then jump to the real title early in playback. // So keep reading after finding a long title to detect // such cases. } break; case DVDNAV_HIGHLIGHT: break; case DVDNAV_AUDIO_STREAM_CHANGE: break; case DVDNAV_SPU_STREAM_CHANGE: break; case DVDNAV_SPU_CLUT_CHANGE: break; case DVDNAV_NOP: break; default: break; } } // Check if the current title is long enough to qualify // as the main feature. if ( cur_title > 0 ) { hb_title_t * hbtitle; int index; index = find_title( list_title, cur_title ); hbtitle = hb_list_item( list_title, index ); if ( hbtitle != NULL ) { if ( hbtitle->duration / 90000 > 10 * 60 ) { hb_deep_log( 3, "dvdnav: Found candidate feature title %d duration %02d:%02d:%02d on button %d", cur_title, hbtitle->hours, hbtitle->minutes, hbtitle->seconds, button+1 ); return cur_title; } if ( hbtitle->duration > longest_duration ) { longest_duration = hbtitle->duration; longest = title; } } // Some titles have long lead-ins. Try skipping it. dvdnav_next_pg_search( dvdnav ); } } done: if ( longest != -1 ) { hb_title_t * hbtitle; int index; index = find_title( list_title, longest ); hbtitle = hb_list_item( list_title, index ); if ( hbtitle != NULL ) { hb_deep_log( 3, "dvdnav: Found candidate feature title %d duration %02d:%02d:%02d on button %d", longest, hbtitle->hours, hbtitle->minutes, hbtitle->seconds, button+1 ); } } return longest; } static int try_menu( hb_dvdnav_t * d, hb_list_t * list_title, DVDMenuID_t menu, uint64_t fallback_duration ) { int result, event, len; uint8_t buf[HB_DVD_READ_BUFFER_SIZE]; int ii, jj; int32_t cur_title, title, part; uint64_t longest_duration = 0; int longest = -1; // A bit of a hack here. Abusing Escape menu to mean use whatever // current menu is already set. if ( menu != DVD_MENU_Escape ) { result = dvdnav_menu_call( d->dvdnav, menu ); if ( result != DVDNAV_STATUS_OK ) { // Sometimes the "first play" item doesn't initialize the // initial VTS. So do it here. result = dvdnav_title_play( d->dvdnav, 1 ); result = dvdnav_menu_call( d->dvdnav, menu ); if ( result != DVDNAV_STATUS_OK ) { hb_error("dvdnav: Can not set dvd menu, %s", dvdnav_err_to_string(d->dvdnav)); goto done; } } } result = dvdnav_current_title_info( d->dvdnav, &title, &part ); if (result != DVDNAV_STATUS_OK) hb_log("dvdnav title info: %s", dvdnav_err_to_string(d->dvdnav)); cur_title = title; for (jj = 0; jj < 4; jj++) { for (ii = 0; ii < 4000; ii++) { result = dvdnav_get_next_block( d->dvdnav, buf, &event, &len ); if ( result == DVDNAV_STATUS_ERR ) { hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(d->dvdnav)); goto done; } switch ( event ) { case DVDNAV_BLOCK_OK: break; case DVDNAV_CELL_CHANGE: { result = dvdnav_current_title_info( d->dvdnav, &title, &part ); if (result != DVDNAV_STATUS_OK) hb_log("dvdnav title info: %s", dvdnav_err_to_string(d->dvdnav)); cur_title = title; } break; case DVDNAV_STILL_FRAME: { dvdnav_still_event_t *event; event = (dvdnav_still_event_t*)buf; dvdnav_still_skip( d->dvdnav ); if ( event->length == 255 ) { // Infinite still. There won't be any menus after this. goto done; } } break; case DVDNAV_WAIT: dvdnav_wait_skip( d->dvdnav ); break; case DVDNAV_STOP: goto done; case DVDNAV_HOP_CHANNEL: break; case DVDNAV_NAV_PACKET: { pci_t *pci = dvdnav_get_current_nav_pci( d->dvdnav ); int kk; int buttons; if ( pci == NULL ) break; buttons = pci->hli.hl_gi.btn_ns; // If we are on a menu that has buttons and // the button activation duration is long enough // that this isn't another fake menu. if ( cur_title == 0 && buttons > 0 && pci->hli.hl_gi.btn_se_e_ptm - pci->hli.hl_gi.hli_s_ptm > 15 * 90000 ) { for (kk = 0; kk < buttons; kk++) { dvdnav_t *dvdnav_copy; result = dvdnav_dup( &dvdnav_copy, d->dvdnav ); if (result != DVDNAV_STATUS_OK) { hb_log("dvdnav dup failed: %s", dvdnav_err_to_string(d->dvdnav)); goto done; } title = try_button( dvdnav_copy, kk, list_title ); dvdnav_free_dup( dvdnav_copy ); if ( title >= 0 ) { hb_title_t * hbtitle; int index; index = find_title( list_title, title ); hbtitle = hb_list_item( list_title, index ); if ( hbtitle != NULL ) { if ( hbtitle->duration > longest_duration ) { longest_duration = hbtitle->duration; longest = title; if ((float)fallback_duration * 0.75 < longest_duration) goto done; } } } } goto done; } } break; case DVDNAV_VTS_CHANGE: { result = dvdnav_current_title_info( d->dvdnav, &title, &part ); if (result != DVDNAV_STATUS_OK) hb_log("dvdnav title info: %s", dvdnav_err_to_string(d->dvdnav)); cur_title = title; } break; case DVDNAV_HIGHLIGHT: break; case DVDNAV_AUDIO_STREAM_CHANGE: break; case DVDNAV_SPU_STREAM_CHANGE: break; case DVDNAV_SPU_CLUT_CHANGE: break; case DVDNAV_NOP: break; default: break; } } // Sometimes the menu is preceeded by a intro that just // gets restarted when hitting the menu button. So // try skipping with the skip forward button. Then // try hitting the menu again. if ( !(jj & 1) ) { dvdnav_next_pg_search( d->dvdnav ); } else { result = dvdnav_menu_call( d->dvdnav, menu ); } } done: return longest; } static int hb_dvdnav_main_feature( hb_dvd_t * e, hb_list_t * list_title ) { hb_dvdnav_t * d = &(e->dvdnav); int longest_root = -1; int longest_title = -1; int longest_fallback = 0; int ii; uint64_t longest_duration_root = 0; uint64_t longest_duration_title = 0; uint64_t longest_duration_fallback = 0; uint64_t avg_duration = 0; int avg_cnt = 0; hb_title_t * title; int index; hb_deep_log( 2, "dvdnav: Searching menus for main feature" ); for ( ii = 0; ii < hb_list_count( list_title ); ii++ ) { title = hb_list_item( list_title, ii ); if ( title->duration > longest_duration_fallback ) { longest_duration_fallback = title->duration; longest_fallback = title->index; } if ( title->duration > 90000L * 60 * 30 ) { avg_duration += title->duration; avg_cnt++; } } if ( avg_cnt ) avg_duration /= avg_cnt; index = find_title( list_title, longest_fallback ); title = hb_list_item( list_title, index ); if ( title ) { hb_deep_log( 2, "dvdnav: Longest title %d duration %02d:%02d:%02d", longest_fallback, title->hours, title->minutes, title->seconds ); } dvdnav_reset( d->dvdnav ); if ( skip_to_menu( d->dvdnav, 2000 ) ) { longest_root = try_menu( d, list_title, DVD_MENU_Escape, longest_duration_fallback ); if ( longest_root >= 0 ) { index = find_title( list_title, longest_root ); title = hb_list_item( list_title, index ); if ( title ) { longest_duration_root = title->duration; hb_deep_log( 2, "dvdnav: Found first-play title %d duration %02d:%02d:%02d", longest_root, title->hours, title->minutes, title->seconds ); } } else { hb_deep_log( 2, "dvdnav: No first-play menu title found" ); } } if ( longest_root < 0 || (float)longest_duration_fallback * 0.7 > longest_duration_root) { longest_root = try_menu( d, list_title, DVD_MENU_Root, longest_duration_fallback ); if ( longest_root >= 0 ) { index = find_title( list_title, longest_root ); title = hb_list_item( list_title, index ); if ( title ) { longest_duration_root = title->duration; hb_deep_log( 2, "dvdnav: Found root title %d duration %02d:%02d:%02d", longest_root, title->hours, title->minutes, title->seconds ); } } else { hb_deep_log( 2, "dvdnav: No root menu title found" ); } } if ( longest_root < 0 || (float)longest_duration_fallback * 0.7 > longest_duration_root) { longest_title = try_menu( d, list_title, DVD_MENU_Title, longest_duration_fallback ); if ( longest_title >= 0 ) { index = find_title( list_title, longest_title ); title = hb_list_item( list_title, index ); if ( title ) { longest_duration_title = title->duration; hb_deep_log( 2, "dvdnav: found title %d duration %02d:%02d:%02d", longest_title, title->hours, title->minutes, title->seconds ); } } else { hb_deep_log( 2, "dvdnav: No title menu title found" ); } } uint64_t longest_duration; int longest; if ( longest_duration_root > longest_duration_title ) { longest_duration = longest_duration_root; longest = longest_root; } else { longest_duration = longest_duration_title; longest = longest_title; } if ((float)longest_duration_fallback * 0.7 > longest_duration && longest_duration < 90000L * 60 * 30 ) { float factor = (float)avg_duration / longest_duration; if ( factor > 1 ) factor = 1 / factor; if ( avg_cnt > 10 && factor < 0.7 ) { longest = longest_fallback; hb_deep_log( 2, "dvdnav: Using longest title %d", longest ); } } return longest; } /*********************************************************************** * hb_dvdnav_start *********************************************************************** * Title and chapter start at 1 **********************************************************************/ static int hb_dvdnav_start( hb_dvd_t * e, hb_title_t *title, int c ) { hb_dvdnav_t * d = &(e->dvdnav); int t = title->index; hb_chapter_t *chapter; dvdnav_status_t result; d->title_block_count = title->block_count; d->list_chapter = title->list_chapter; if ( d->stopped && !hb_dvdnav_reset(d) ) { return 0; } dvdnav_reset( d->dvdnav ); chapter = hb_list_item( title->list_chapter, c - 1); if (chapter != NULL) result = dvdnav_program_play(d->dvdnav, t, chapter->pgcn, chapter->pgn); else result = dvdnav_part_play(d->dvdnav, t, 1); if (result != DVDNAV_STATUS_OK) { hb_error( "dvd: dvdnav_*_play failed - %s", dvdnav_err_to_string(d->dvdnav) ); return 0; } d->title = t; d->stopped = 0; d->chapter = 0; d->cell = 0; return 1; } /*********************************************************************** * hb_dvdnav_stop *********************************************************************** * **********************************************************************/ static void hb_dvdnav_stop( hb_dvd_t * e ) { } /*********************************************************************** * hb_dvdnav_seek *********************************************************************** * **********************************************************************/ static int hb_dvdnav_seek( hb_dvd_t * e, float f ) { hb_dvdnav_t * d = &(e->dvdnav); uint64_t sector = f * d->title_block_count; int result, event, len; uint8_t buf[HB_DVD_READ_BUFFER_SIZE]; int done = 0, ii; if (d->stopped) { return 0; } // XXX the current version of libdvdnav can't seek outside the current // PGC. Check if the place we're seeking to is in a different // PGC. Position there & adjust the offset if so. uint64_t pgc_offset = 0; uint64_t chap_offset = 0; hb_chapter_t *pgc_change = hb_list_item(d->list_chapter, 0 ); for ( ii = 0; ii < hb_list_count( d->list_chapter ); ++ii ) { hb_chapter_t *chapter = hb_list_item( d->list_chapter, ii ); uint64_t chap_len = chapter->block_end - chapter->block_start + 1; if ( chapter->pgcn != pgc_change->pgcn ) { // this chapter's in a different pgc from the previous - note the // change so we can make sector offset's be pgc relative. pgc_offset = chap_offset; pgc_change = chapter; } if ( chap_offset <= sector && sector < chap_offset + chap_len ) { // this chapter contains the sector we want - see if it's in a // different pgc than the one we're currently in. int32_t title, pgcn, pgn; if (dvdnav_current_title_program( d->dvdnav, &title, &pgcn, &pgn ) != DVDNAV_STATUS_OK) hb_log("dvdnav cur pgcn err: %s", dvdnav_err_to_string(d->dvdnav)); // If we find ourselves in a new title, it means a title // transition was made while reading data. Jumping between // titles can cause the vm to get into a bad state. So // reset the vm in this case. if ( d->title != title ) dvdnav_reset( d->dvdnav ); if ( d->title != title || chapter->pgcn != pgcn ) { // this chapter is in a different pgc - switch to it. if (dvdnav_program_play(d->dvdnav, d->title, chapter->pgcn, chapter->pgn) != DVDNAV_STATUS_OK) hb_log("dvdnav prog play err: %s", dvdnav_err_to_string(d->dvdnav)); } // seek sectors are pgc-relative so remove the pgc start sector. sector -= pgc_offset; break; } chap_offset += chap_len; } // dvdnav will not let you seek or poll current position // till it reaches a certain point in parsing. so we // have to get blocks until we reach a cell // Put an arbitrary limit of 100 blocks on how long we search for (ii = 0; ii < 100 && !done; ii++) { result = dvdnav_get_next_block( d->dvdnav, buf, &event, &len ); if ( result == DVDNAV_STATUS_ERR ) { hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(d->dvdnav)); return 0; } switch ( event ) { case DVDNAV_BLOCK_OK: case DVDNAV_CELL_CHANGE: done = 1; break; case DVDNAV_STILL_FRAME: dvdnav_still_skip( d->dvdnav ); break; case DVDNAV_WAIT: dvdnav_wait_skip( d->dvdnav ); break; case DVDNAV_STOP: hb_log("dvdnav: stop encountered during seek"); d->stopped = 1; return 0; case DVDNAV_HOP_CHANNEL: case DVDNAV_NAV_PACKET: case DVDNAV_VTS_CHANGE: case DVDNAV_HIGHLIGHT: case DVDNAV_AUDIO_STREAM_CHANGE: case DVDNAV_SPU_STREAM_CHANGE: case DVDNAV_SPU_CLUT_CHANGE: case DVDNAV_NOP: default: break; } } if (dvdnav_sector_search(d->dvdnav, sector, SEEK_SET) != DVDNAV_STATUS_OK) { hb_error( "dvd: dvdnav_sector_search failed - %s", dvdnav_err_to_string(d->dvdnav) ); return 0; } d->chapter = 0; d->cell = 0; return 1; } /*********************************************************************** * hb_dvdnav_read *********************************************************************** * **********************************************************************/ static hb_buffer_t * hb_dvdnav_read( hb_dvd_t * e ) { hb_dvdnav_t * d = &(e->dvdnav); int result, event, len; int chapter = 0; int error_count = 0; hb_buffer_t *b = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE ); while ( 1 ) { if (d->stopped) { hb_buffer_close( &b ); return NULL; } result = dvdnav_get_next_block( d->dvdnav, b->data, &event, &len ); if ( result == DVDNAV_STATUS_ERR ) { hb_error("dvdnav: Read Error, %s", dvdnav_err_to_string(d->dvdnav)); if (dvdnav_sector_search(d->dvdnav, 1, SEEK_CUR) != DVDNAV_STATUS_OK) { hb_error( "dvd: dvdnav_sector_search failed - %s", dvdnav_err_to_string(d->dvdnav) ); hb_buffer_close( &b ); return NULL; } error_count++; if (error_count > 500) { hb_error("dvdnav: Error, too many consecutive read errors"); hb_buffer_close( &b ); return NULL; } continue; } switch ( event ) { case DVDNAV_BLOCK_OK: // We have received a regular block of the currently playing // MPEG stream. // The muxers expect to only get chapter 2 and above // They write chapter 1 when chapter 2 is detected. if (chapter > 1) b->s.new_chap = chapter; chapter = 0; error_count = 0; return b; case DVDNAV_NOP: /* * Nothing to do here. */ break; case DVDNAV_STILL_FRAME: /* * We have reached a still frame. A real player application * would wait the amount of time specified by the still's * length while still handling user input to make menus and * other interactive stills work. A length of 0xff means an * indefinite still which has to be skipped indirectly by some * user interaction. */ dvdnav_still_skip( d->dvdnav ); break; case DVDNAV_WAIT: /* * We have reached a point in DVD playback, where timing is * critical. Player application with internal fifos can * introduce state inconsistencies, because libdvdnav is * always the fifo's length ahead in the stream compared to * what the application sees. Such applications should wait * until their fifos are empty when they receive this type of * event. */ dvdnav_wait_skip( d->dvdnav ); break; case DVDNAV_SPU_CLUT_CHANGE: /* * Player applications should pass the new colour lookup table * to their SPU decoder */ break; case DVDNAV_SPU_STREAM_CHANGE: /* * Player applications should inform their SPU decoder to * switch channels */ break; case DVDNAV_AUDIO_STREAM_CHANGE: /* * Player applications should inform their audio decoder to * switch channels */ break; case DVDNAV_HIGHLIGHT: /* * Player applications should inform their overlay engine to * highlight the given button */ break; case DVDNAV_VTS_CHANGE: /* * Some status information like video aspect and video scale * permissions do not change inside a VTS. Therefore this * event can be used to query such information only when * necessary and update the decoding/displaying accordingly. */ { int tt = 0, pgcn = 0, pgn = 0; dvdnav_current_title_program(d->dvdnav, &tt, &pgcn, &pgn); if (tt != d->title) { // Transition to another title signals that we are done. hb_buffer_close( &b ); return NULL; } } break; case DVDNAV_CELL_CHANGE: /* * Some status information like the current Title and Part * numbers do not change inside a cell. Therefore this event * can be used to query such information only when necessary * and update the decoding/displaying accordingly. */ { dvdnav_cell_change_event_t * cell_event; int tt = 0, pgcn = 0, pgn = 0, c; cell_event = (dvdnav_cell_change_event_t*)b->data; dvdnav_current_title_program(d->dvdnav, &tt, &pgcn, &pgn); if (tt != d->title) { // Transition to another title signals that we are done. hb_buffer_close( &b ); return NULL; } c = FindChapterIndex(d->list_chapter, pgcn, pgn); if (c != d->chapter) { if (c < d->chapter) { // Some titles end with a 'link' back to the beginning so // a transition to an earlier chapter means we're done. hb_buffer_close( &b ); return NULL; } chapter = d->chapter = c; } else if ( cell_event->cellN <= d->cell ) { hb_buffer_close( &b ); return NULL; } d->cell = cell_event->cellN; } break; case DVDNAV_NAV_PACKET: /* * A NAV packet provides PTS discontinuity information, angle * linking information and button definitions for DVD menus. * Angles are handled completely inside libdvdnav. For the * menus to work, the NAV packet information has to be passed * to the overlay engine of the player so that it knows the * dimensions of the button areas. */ // mpegdemux expects to get these. I don't think it does // anything useful with them however. // The muxers expect to only get chapter 2 and above // They write chapter 1 when chapter 2 is detected. if (chapter > 1) b->s.new_chap = chapter; chapter = 0; return b; break; case DVDNAV_HOP_CHANNEL: /* * This event is issued whenever a non-seamless operation has * been executed. Applications with fifos should drop the * fifos content to speed up responsiveness. */ break; case DVDNAV_STOP: /* * Playback should end here. */ d->stopped = 1; hb_buffer_close( &b ); return NULL; default: break; } } hb_buffer_close( &b ); return NULL; } /*********************************************************************** * hb_dvdnav_chapter *********************************************************************** * Returns in which chapter the next block to be read is. * Chapter numbers start at 1. **********************************************************************/ static int hb_dvdnav_chapter( hb_dvd_t * e ) { hb_dvdnav_t * d = &(e->dvdnav); int32_t t, pgcn, pgn; int32_t c; if (dvdnav_current_title_program(d->dvdnav, &t, &pgcn, &pgn) != DVDNAV_STATUS_OK) { return -1; } c = FindChapterIndex( d->list_chapter, pgcn, pgn ); return c; } /*********************************************************************** * hb_dvdnav_close *********************************************************************** * Closes and frees everything **********************************************************************/ static void hb_dvdnav_close( hb_dvd_t ** _d ) { hb_dvdnav_t * d = &((*_d)->dvdnav); if( d->dvdnav ) dvdnav_close( d->dvdnav ); if( d->vmg ) ifoClose( d->vmg ); if( d->reader ) DVDClose( d->reader ); free(d->path); free( d ); *_d = NULL; } /*********************************************************************** * hb_dvdnav_angle_count *********************************************************************** * Returns the number of angles supported. **********************************************************************/ static int hb_dvdnav_angle_count( hb_dvd_t * e ) { hb_dvdnav_t * d = &(e->dvdnav); int current, angle_count; if (dvdnav_get_angle_info( d->dvdnav, ¤t, &angle_count) != DVDNAV_STATUS_OK) { hb_log("dvdnav_get_angle_info %s", dvdnav_err_to_string(d->dvdnav)); angle_count = 1; } return angle_count; } /*********************************************************************** * hb_dvdnav_set_angle *********************************************************************** * Sets the angle to read **********************************************************************/ static void hb_dvdnav_set_angle( hb_dvd_t * e, int angle ) { hb_dvdnav_t * d = &(e->dvdnav); if (dvdnav_angle_change( d->dvdnav, angle) != DVDNAV_STATUS_OK) { hb_log("dvdnav_angle_change %s", dvdnav_err_to_string(d->dvdnav)); } } /*********************************************************************** * FindChapterIndex *********************************************************************** * Assumes pgc and cell_cur are correctly set, and sets cell_next to the * cell to be read when we will be done with cell_cur. **********************************************************************/ static int FindChapterIndex( hb_list_t * list, int pgcn, int pgn ) { int count, ii; hb_chapter_t *chapter; count = hb_list_count( list ); for (ii = 0; ii < count; ii++) { chapter = hb_list_item( list, ii ); if (chapter->pgcn == pgcn && chapter->pgn == pgn) return chapter->index; } return 0; } /*********************************************************************** * FindNextCell *********************************************************************** * Assumes pgc and cell_cur are correctly set, and sets cell_next to the * cell to be read when we will be done with cell_cur. **********************************************************************/ static int FindNextCell( pgc_t *pgc, int cell_cur ) { int i = 0; int cell_next; if( pgc->cell_playback[cell_cur].block_type == BLOCK_TYPE_ANGLE_BLOCK ) { while( pgc->cell_playback[cell_cur+i].block_mode != BLOCK_MODE_LAST_CELL ) { i++; } cell_next = cell_cur + i + 1; hb_log( "dvd: Skipping multi-angle cells %d-%d", cell_cur, cell_next - 1 ); } else { cell_next = cell_cur + 1; } return cell_next; } /*********************************************************************** * NextPgcn *********************************************************************** * Assumes pgc and cell_cur are correctly set, and sets cell_next to the * cell to be read when we will be done with cell_cur. * Since pg chains can be circularly linked (either from a read error or * deliberately) pgcn_map tracks program chains we've already seen. **********************************************************************/ static int NextPgcn( ifo_handle_t *ifo, int pgcn, uint32_t pgcn_map[MAX_PGCN/32] ) { int next_pgcn; pgc_t *pgc; pgcn_map[pgcn >> 5] |= (1 << (pgcn & 31)); pgc = ifo->vts_pgcit->pgci_srp[pgcn-1].pgc; next_pgcn = pgc->next_pgc_nr; if ( next_pgcn < 1 || next_pgcn >= MAX_PGCN || next_pgcn > ifo->vts_pgcit->nr_of_pgci_srp ) return 0; return pgcn_map[next_pgcn >> 5] & (1 << (next_pgcn & 31))? 0 : next_pgcn; } /*********************************************************************** * PgcWalkInit *********************************************************************** * Pgc links can loop. I track which have been visited in a bit vector * Initialize the bit vector to empty. **********************************************************************/ static void PgcWalkInit( uint32_t pgcn_map[MAX_PGCN/32] ) { memset(pgcn_map, 0, sizeof(uint32_t) * MAX_PGCN/32); } /*********************************************************************** * dvdtime2msec *********************************************************************** * From lsdvd **********************************************************************/ static int dvdtime2msec(dvd_time_t * dt) { double frames_per_s[4] = {-1.0, 25.00, -1.0, 29.97}; double fps = frames_per_s[(dt->frame_u & 0xc0) >> 6]; long ms; ms = (((dt->hour & 0xf0) >> 3) * 5 + (dt->hour & 0x0f)) * 3600000; ms += (((dt->minute & 0xf0) >> 3) * 5 + (dt->minute & 0x0f)) * 60000; ms += (((dt->second & 0xf0) >> 3) * 5 + (dt->second & 0x0f)) * 1000; if( fps > 0 ) { ms += (((dt->frame_u & 0x30) >> 3) * 5 + (dt->frame_u & 0x0f)) * 1000.0 / fps; } return ms; } HandBrake-0.10.2/libhb/rotate.c0000664000175200017520000002341412463330511016563 0ustar handbrakehandbrake/* rorate.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "hbffmpeg.h" #include "taskset.h" #define MODE_DEFAULT 3 // Mode 1: Flip vertically (y0 becomes yN and yN becomes y0) // Mode 2: Flip horizontally (x0 becomes xN and xN becomes x0) // Mode 3: Flip both horizontally and vertically (modes 1 and 2 combined) typedef struct rotate_arguments_s { hb_buffer_t *dst; hb_buffer_t *src; } rotate_arguments_t; struct hb_filter_private_s { int mode; int width; int height; int par_width; int par_height; int cpu_count; taskset_t rotate_taskset; // Threads for Rotate - one per CPU rotate_arguments_t *rotate_arguments; // Arguments to thread for work }; static int hb_rotate_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int hb_rotate_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static void hb_rotate_close( hb_filter_object_t * filter ); static int hb_rotate_info( hb_filter_object_t * filter, hb_filter_info_t * info ); hb_filter_object_t hb_filter_rotate = { .id = HB_FILTER_ROTATE, .enforce_order = 0, .name = "Rotate (rotate & flip image axes)", .settings = NULL, .init = hb_rotate_init, .work = hb_rotate_work, .close = hb_rotate_close, .info = hb_rotate_info }; typedef struct rotate_thread_arg_s { hb_filter_private_t *pv; int segment; } rotate_thread_arg_t; /* * rotate this segment of all three planes in a single thread. */ void rotate_filter_thread( void *thread_args_v ) { rotate_arguments_t *rotate_work = NULL; hb_filter_private_t * pv; int run = 1; int plane; int segment, segment_start, segment_stop; rotate_thread_arg_t *thread_args = thread_args_v; uint8_t *dst; hb_buffer_t *dst_buf; hb_buffer_t *src_buf; int y; pv = thread_args->pv; segment = thread_args->segment; hb_log("Rotate thread started for segment %d", segment); while( run ) { /* * Wait here until there is work to do. */ taskset_thread_wait4start( &pv->rotate_taskset, segment ); if( taskset_thread_stop( &pv->rotate_taskset, segment ) ) { /* * No more work to do, exit this thread. */ run = 0; goto report_completion; } rotate_work = &pv->rotate_arguments[segment]; if( rotate_work->dst == NULL ) { hb_error( "Thread started when no work available" ); hb_snooze(500); goto report_completion; } /* * Process all three planes, but only this segment of it. */ dst_buf = rotate_work->dst; src_buf = rotate_work->src; for( plane = 0; plane < 3; plane++) { int dst_stride, src_stride; dst = dst_buf->plane[plane].data; dst_stride = dst_buf->plane[plane].stride; src_stride = src_buf->plane[plane].stride; int h = src_buf->plane[plane].height; int w = src_buf->plane[plane].width; segment_start = ( h / pv->cpu_count ) * segment; if( segment == pv->cpu_count - 1 ) { /* * Final segment */ segment_stop = h; } else { segment_stop = ( h / pv->cpu_count ) * ( segment + 1 ); } for( y = segment_start; y < segment_stop; y++ ) { uint8_t * cur; int x, xo, yo; cur = &src_buf->plane[plane].data[y * src_stride]; for( x = 0; x < w; x++) { if( pv->mode & 1 ) { yo = h - y - 1; } else { yo = y; } if( pv->mode & 2 ) { xo = w - x - 1; } else { xo = x; } if( pv->mode & 4 ) // Rotate 90 clockwise { int tmp = xo; xo = h - yo - 1; yo = tmp; } dst[yo*dst_stride + xo] = cur[x]; } } } report_completion: /* * Finished this segment, let everyone know. */ taskset_thread_complete( &pv->rotate_taskset, segment ); } } /* * threaded rotate - each thread rotates a single segment of all * three planes. Where a segment is defined as the frame divided by * the number of CPUs. * * This function blocks until the frame is rotated. */ static void rotate_filter( hb_filter_private_t * pv, hb_buffer_t *out, hb_buffer_t *in ) { int segment; for( segment = 0; segment < pv->cpu_count; segment++ ) { /* * Setup the work for this plane. */ pv->rotate_arguments[segment].dst = out; pv->rotate_arguments[segment].src = in; } /* * Allow the taskset threads to make one pass over the data. */ taskset_cycle( &pv->rotate_taskset ); /* * Entire frame is now rotated. */ } static int hb_rotate_init( hb_filter_object_t * filter, hb_filter_init_t * init ) { filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); hb_filter_private_t * pv = filter->private_data; pv->mode = MODE_DEFAULT; if( filter->settings ) { sscanf( filter->settings, "%d", &pv->mode ); } pv->cpu_count = hb_get_cpu_count(); /* * Create rotate taskset. */ pv->rotate_arguments = malloc( sizeof( rotate_arguments_t ) * pv->cpu_count ); if( pv->rotate_arguments == NULL || taskset_init( &pv->rotate_taskset, /*thread_count*/pv->cpu_count, sizeof( rotate_thread_arg_t ) ) == 0 ) { hb_error( "rotate could not initialize taskset" ); } int i; for( i = 0; i < pv->cpu_count; i++ ) { rotate_thread_arg_t *thread_args; thread_args = taskset_thread_args( &pv->rotate_taskset, i ); thread_args->pv = pv; thread_args->segment = i; pv->rotate_arguments[i].dst = NULL; if( taskset_thread_spawn( &pv->rotate_taskset, i, "rotate_filter_segment", rotate_filter_thread, HB_NORMAL_PRIORITY ) == 0 ) { hb_error( "rotate could not spawn thread" ); } } // Set init width/height so the next stage in the pipline // knows what it will be getting if( pv->mode & 4 ) { // 90 degree rotation, exchange width and height int tmp = init->width; init->width = init->height; init->height = tmp; tmp = init->par_width; init->par_width = init->par_height; init->par_height = tmp; } pv->width = init->width; pv->height = init->height; pv->par_width = init->par_width; pv->par_height = init->par_height; return 0; } static int hb_rotate_info( hb_filter_object_t * filter, hb_filter_info_t * info ) { hb_filter_private_t * pv = filter->private_data; if( !pv ) return 1; memset( info, 0, sizeof( hb_filter_info_t ) ); info->out.width = pv->width; info->out.height = pv->height; info->out.par_width = pv->par_width; info->out.par_height = pv->par_height; int pos = 0; if( pv->mode & 1 ) pos += sprintf( &info->human_readable_desc[pos], "flip vertical" ); if( pv->mode & 2 ) { if( pos ) pos += sprintf( &info->human_readable_desc[pos], "/" ); pos += sprintf( &info->human_readable_desc[pos], "flip horizontal" ); } if( pv->mode & 4 ) { if( pos ) pos += sprintf( &info->human_readable_desc[pos], "/" ); pos += sprintf( &info->human_readable_desc[pos], "rotate 90" ); } return 0; } static void hb_rotate_close( hb_filter_object_t * filter ) { hb_filter_private_t * pv = filter->private_data; if( !pv ) { return; } taskset_fini( &pv->rotate_taskset ); /* * free memory for rotate structs */ free( pv->rotate_arguments ); free( pv ); filter->private_data = NULL; } static int hb_rotate_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in, * out; if ( in->size <= 0 ) { *buf_out = in; *buf_in = NULL; return HB_FILTER_DONE; } int width_out, height_out; if ( pv->mode & 4 ) { width_out = in->f.height; height_out = in->f.width; } else { width_out = in->f.width; height_out = in->f.height; } out = hb_video_buffer_init( width_out, height_out ); // Rotate! rotate_filter( pv, out, in ); out->s = in->s; hb_buffer_move_subs( out, in ); *buf_out = out; return HB_FILTER_OK; } HandBrake-0.10.2/libhb/hb.c0000664000175200017520000016202312463330511015656 0ustar handbrakehandbrake/* hb.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "opencl.h" #include "hbffmpeg.h" #include #include #include #ifdef USE_QSV #include "qsv_common.h" #endif #if defined( SYS_MINGW ) #include #if defined( PTW32_STATIC_LIB ) #include #endif #endif struct hb_handle_s { int id; /* The "Check for update" thread */ int build; char version[32]; hb_thread_t * update_thread; /* This thread's only purpose is to check other threads' states */ volatile int die; hb_thread_t * main_thread; int pid; /* DVD/file scan thread */ hb_title_set_t title_set; hb_thread_t * scan_thread; /* The thread which processes the jobs. Others threads are launched from this one (see work.c) */ hb_list_t * jobs; hb_job_t * current_job; int job_count; int job_count_permanent; volatile int work_die; hb_error_code work_error; hb_thread_t * work_thread; hb_lock_t * state_lock; hb_state_t state; int paused; hb_lock_t * pause_lock; /* For MacGui active queue increments each time the scan thread completes*/ int scanCount; volatile int scan_die; /* Stash of persistent data between jobs, for stuff like correcting frame count and framerate estimates on multi-pass encodes where frames get dropped. */ hb_interjob_t * interjob; // power management opaque pointer void *system_sleep_opaque; } ; hb_work_object_t * hb_objects = NULL; int hb_instance_counter = 0; static void thread_func( void * ); static int ff_lockmgr_cb(void **mutex, enum AVLockOp op) { switch ( op ) { case AV_LOCK_CREATE: { *mutex = hb_lock_init(); } break; case AV_LOCK_DESTROY: { hb_lock_close( (hb_lock_t**)mutex ); } break; case AV_LOCK_OBTAIN: { hb_lock( (hb_lock_t*)*mutex ); } break; case AV_LOCK_RELEASE: { hb_unlock( (hb_lock_t*)*mutex ); } break; default: break; } return 0; } void hb_avcodec_init() { av_lockmgr_register(ff_lockmgr_cb); av_register_all(); #ifdef _WIN64 // avresample's assembly optimizations can cause crashes under Win x86_64 // (see http://bugzilla.libav.org/show_bug.cgi?id=496) // disable AVX and FMA4 as a workaround hb_deep_log(2, "hb_avcodec_init: Windows x86_64, disabling AVX and FMA4"); int cpu_flags = av_get_cpu_flags() & ~AV_CPU_FLAG_AVX & ~AV_CPU_FLAG_FMA4; av_set_cpu_flags_mask(cpu_flags); #endif } int hb_avcodec_open(AVCodecContext *avctx, AVCodec *codec, AVDictionary **av_opts, int thread_count) { int ret; if ((thread_count == HB_FFMPEG_THREADS_AUTO || thread_count > 0) && (codec->type == AVMEDIA_TYPE_VIDEO)) { avctx->thread_count = (thread_count == HB_FFMPEG_THREADS_AUTO) ? hb_get_cpu_count() / 2 + 1 : thread_count; avctx->thread_type = FF_THREAD_FRAME|FF_THREAD_SLICE; avctx->thread_safe_callbacks = 1; } else { avctx->thread_count = 1; } if (codec->capabilities & CODEC_CAP_EXPERIMENTAL) { // "experimental" encoders will not open without this avctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; } ret = avcodec_open2(avctx, codec, av_opts); return ret; } int hb_avcodec_close(AVCodecContext *avctx) { int ret; ret = avcodec_close(avctx); return ret; } int hb_avpicture_fill(AVPicture *pic, hb_buffer_t *buf) { int ret, ii; for (ii = 0; ii < 4; ii++) pic->linesize[ii] = buf->plane[ii].stride; ret = av_image_fill_pointers(pic->data, buf->f.fmt, buf->plane[0].height_stride, buf->data, pic->linesize); if (ret != buf->size) { hb_error("Internal error hb_avpicture_fill expected %d, got %d", buf->size, ret); } return ret; } static int handle_jpeg(enum AVPixelFormat *format) { switch (*format) { case AV_PIX_FMT_YUVJ420P: *format = AV_PIX_FMT_YUV420P; return 1; case AV_PIX_FMT_YUVJ422P: *format = AV_PIX_FMT_YUV422P; return 1; case AV_PIX_FMT_YUVJ444P: *format = AV_PIX_FMT_YUV444P; return 1; case AV_PIX_FMT_YUVJ440P: *format = AV_PIX_FMT_YUV440P; return 1; default: return 0; } } struct SwsContext* hb_sws_get_context(int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags) { struct SwsContext * ctx; ctx = sws_alloc_context(); if ( ctx ) { int srcRange, dstRange; srcRange = handle_jpeg(&srcFormat); dstRange = handle_jpeg(&dstFormat); /* enable this when implemented in Libav flags |= SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP; */ av_opt_set_int(ctx, "srcw", srcW, 0); av_opt_set_int(ctx, "srch", srcH, 0); av_opt_set_int(ctx, "src_range", srcRange, 0); av_opt_set_int(ctx, "src_format", srcFormat, 0); av_opt_set_int(ctx, "dstw", dstW, 0); av_opt_set_int(ctx, "dsth", dstH, 0); av_opt_set_int(ctx, "dst_range", dstRange, 0); av_opt_set_int(ctx, "dst_format", dstFormat, 0); av_opt_set_int(ctx, "sws_flags", flags, 0); sws_setColorspaceDetails( ctx, sws_getCoefficients( SWS_CS_DEFAULT ), // src colorspace srcRange, // src range 0 = MPG, 1 = JPG sws_getCoefficients( SWS_CS_DEFAULT ), // dst colorspace dstRange, // dst range 0 = MPG, 1 = JPG 0, // brightness 1 << 16, // contrast 1 << 16 ); // saturation if (sws_init_context(ctx, NULL, NULL) < 0) { fprintf(stderr, "Cannot initialize resampling context\n"); sws_freeContext(ctx); ctx = NULL; } } return ctx; } uint64_t hb_ff_mixdown_xlat(int hb_mixdown, int *downmix_mode) { uint64_t ff_layout = 0; int mode = AV_MATRIX_ENCODING_NONE; switch (hb_mixdown) { // Passthru case HB_AMIXDOWN_NONE: break; case HB_AMIXDOWN_MONO: case HB_AMIXDOWN_LEFT: case HB_AMIXDOWN_RIGHT: ff_layout = AV_CH_LAYOUT_MONO; break; case HB_AMIXDOWN_DOLBY: ff_layout = AV_CH_LAYOUT_STEREO; mode = AV_MATRIX_ENCODING_DOLBY; break; case HB_AMIXDOWN_DOLBYPLII: ff_layout = AV_CH_LAYOUT_STEREO; mode = AV_MATRIX_ENCODING_DPLII; break; case HB_AMIXDOWN_STEREO: ff_layout = AV_CH_LAYOUT_STEREO; break; case HB_AMIXDOWN_5POINT1: ff_layout = AV_CH_LAYOUT_5POINT1; break; case HB_AMIXDOWN_6POINT1: ff_layout = AV_CH_LAYOUT_6POINT1; break; case HB_AMIXDOWN_7POINT1: ff_layout = AV_CH_LAYOUT_7POINT1; break; case HB_AMIXDOWN_5_2_LFE: ff_layout = (AV_CH_LAYOUT_5POINT1_BACK| AV_CH_FRONT_LEFT_OF_CENTER| AV_CH_FRONT_RIGHT_OF_CENTER); break; default: ff_layout = AV_CH_LAYOUT_STEREO; hb_log("hb_ff_mixdown_xlat: unsupported mixdown %d", hb_mixdown); break; } if (downmix_mode != NULL) *downmix_mode = mode; return ff_layout; } /* * Set sample format to the request format if supported by the codec. * The planar/packed variant of the requested format is the next best thing. */ void hb_ff_set_sample_fmt(AVCodecContext *context, AVCodec *codec, enum AVSampleFormat request_sample_fmt) { if (context != NULL && codec != NULL && codec->type == AVMEDIA_TYPE_AUDIO && codec->sample_fmts != NULL) { const enum AVSampleFormat *fmt; enum AVSampleFormat next_best_fmt; next_best_fmt = (av_sample_fmt_is_planar(request_sample_fmt) ? av_get_packed_sample_fmt(request_sample_fmt) : av_get_planar_sample_fmt(request_sample_fmt)); context->request_sample_fmt = AV_SAMPLE_FMT_NONE; for (fmt = codec->sample_fmts; *fmt != AV_SAMPLE_FMT_NONE; fmt++) { if (*fmt == request_sample_fmt) { context->request_sample_fmt = request_sample_fmt; break; } else if (*fmt == next_best_fmt) { context->request_sample_fmt = next_best_fmt; } } /* * When encoding and AVCodec.sample_fmts exists, avcodec_open2() * will error out if AVCodecContext.sample_fmt isn't set. */ if (context->request_sample_fmt == AV_SAMPLE_FMT_NONE) { context->request_sample_fmt = codec->sample_fmts[0]; } context->sample_fmt = context->request_sample_fmt; } } /** * Registers work objects, by adding the work object to a liked list. * @param w Handle to hb_work_object_t to register. */ void hb_register( hb_work_object_t * w ) { w->next = hb_objects; hb_objects = w; } void (*hb_log_callback)(const char* message); static void redirect_thread_func(void *); #if defined( SYS_MINGW ) #define pipe(phandles) _pipe (phandles, 4096, _O_BINARY) #endif /** * Registers the given function as a logger. All logs will be passed to it. * @param log_cb The function to register as a logger. */ void hb_register_logger( void (*log_cb)(const char* message) ) { hb_log_callback = log_cb; hb_thread_init("ioredirect", redirect_thread_func, NULL, HB_NORMAL_PRIORITY); } /** * libhb initialization routine. * @param verbose HB_DEBUG_NONE or HB_DEBUG_ALL. * @param update_check signals libhb to check for updated version from HandBrake website. * @return Handle to hb_handle_t for use on all subsequent calls to libhb. */ hb_handle_t * hb_init( int verbose, int update_check ) { hb_handle_t * h = calloc( sizeof( hb_handle_t ), 1 ); uint64_t date; /* See hb_deep_log() and hb_log() in common.c */ global_verbosity_level = verbose; if( verbose ) putenv( "HB_DEBUG=1" ); h->id = hb_instance_counter++; /* Check for an update on the website if asked to */ h->build = -1; /* Initialize opaque for PowerManagement purposes */ h->system_sleep_opaque = hb_system_sleep_opaque_init(); if( update_check ) { hb_log( "hb_init: checking for updates" ); date = hb_get_date(); h->update_thread = hb_update_init( &h->build, h->version ); for( ;; ) { if( hb_thread_has_exited( h->update_thread ) ) { /* Immediate success or failure */ hb_thread_close( &h->update_thread ); break; } if( hb_get_date() > date + 1000 ) { /* Still nothing after one second. Connection problem, let the thread die */ hb_log( "hb_init: connection problem, not waiting for " "update_thread" ); break; } hb_snooze( 500 ); } } /* * Initialise buffer pool */ hb_buffer_pool_init(); h->title_set.list_title = hb_list_init(); h->jobs = hb_list_init(); h->state_lock = hb_lock_init(); h->state.state = HB_STATE_IDLE; h->pause_lock = hb_lock_init(); h->interjob = calloc( sizeof( hb_interjob_t ), 1 ); /* Start library thread */ hb_log( "hb_init: starting libhb thread" ); h->die = 0; h->main_thread = hb_thread_init( "libhb", thread_func, h, HB_NORMAL_PRIORITY ); return h; } /** * libhb initialization routine. * This version is to use when calling the dylib, the macro hb_init isn't available from a dylib call! * @param verbose HB_DEBUG_NONE or HB_DEBUG_ALL. * @param update_check signals libhb to check for updated version from HandBrake website. * @return Handle to hb_handle_t for use on all subsequent calls to libhb. */ hb_handle_t * hb_init_dl( int verbose, int update_check ) { hb_handle_t * h = calloc( sizeof( hb_handle_t ), 1 ); uint64_t date; /* See hb_log() in common.c */ if( verbose > HB_DEBUG_NONE ) { putenv( "HB_DEBUG=1" ); } h->id = hb_instance_counter++; /* Check for an update on the website if asked to */ h->build = -1; /* Initialize opaque for PowerManagement purposes */ h->system_sleep_opaque = hb_system_sleep_opaque_init(); if( update_check ) { hb_log( "hb_init: checking for updates" ); date = hb_get_date(); h->update_thread = hb_update_init( &h->build, h->version ); for( ;; ) { if( hb_thread_has_exited( h->update_thread ) ) { /* Immediate success or failure */ hb_thread_close( &h->update_thread ); break; } if( hb_get_date() > date + 1000 ) { /* Still nothing after one second. Connection problem, let the thread die */ hb_log( "hb_init: connection problem, not waiting for " "update_thread" ); break; } hb_snooze( 500 ); } } h->title_set.list_title = hb_list_init(); h->jobs = hb_list_init(); h->current_job = NULL; h->state_lock = hb_lock_init(); h->state.state = HB_STATE_IDLE; h->pause_lock = hb_lock_init(); /* Start library thread */ hb_log( "hb_init: starting libhb thread" ); h->die = 0; h->main_thread = hb_thread_init( "libhb", thread_func, h, HB_NORMAL_PRIORITY ); return h; } /** * Returns current version of libhb. * @param h Handle to hb_handle_t. * @return character array of version number. */ char * hb_get_version( hb_handle_t * h ) { return HB_PROJECT_VERSION; } /** * Returns current build of libhb. * @param h Handle to hb_handle_t. * @return character array of build number. */ int hb_get_build( hb_handle_t * h ) { return HB_PROJECT_BUILD; } /** * Checks for needed update. * @param h Handle to hb_handle_t. * @param version Pointer to handle where version will be copied. * @return update indicator. */ int hb_check_update( hb_handle_t * h, char ** version ) { *version = ( h->build < 0 ) ? NULL : h->version; return h->build; } /** * Deletes current previews associated with titles * @param h Handle to hb_handle_t */ void hb_remove_previews( hb_handle_t * h ) { char filename[1024]; char dirname[1024]; hb_title_t * title; int i, count, len; DIR * dir; struct dirent * entry; memset( dirname, 0, 1024 ); hb_get_temporary_directory( dirname ); dir = opendir( dirname ); if (dir == NULL) return; count = hb_list_count( h->title_set.list_title ); while( ( entry = readdir( dir ) ) ) { if( entry->d_name[0] == '.' ) { continue; } for( i = 0; i < count; i++ ) { title = hb_list_item( h->title_set.list_title, i ); len = snprintf( filename, 1024, "%d_%d", h->id, title->index ); if (strncmp(entry->d_name, filename, len) == 0) { snprintf( filename, 1024, "%s/%s", dirname, entry->d_name ); unlink( filename ); break; } } } closedir( dir ); } /** * Initializes a scan of the by calling hb_scan_init * @param h Handle to hb_handle_t * @param path location of VIDEO_TS folder. * @param title_index Desired title to scan. 0 for all titles. * @param preview_count Number of preview images to generate. * @param store_previews Whether or not to write previews to disk. */ void hb_scan( hb_handle_t * h, const char * path, int title_index, int preview_count, int store_previews, uint64_t min_duration ) { hb_title_t * title; h->scan_die = 0; /* Clean up from previous scan */ hb_remove_previews( h ); while( ( title = hb_list_item( h->title_set.list_title, 0 ) ) ) { hb_list_rem( h->title_set.list_title, title ); hb_title_close( &title ); } /* Print CPU info here so that it's in all scan and encode logs */ const char *cpu_name = hb_get_cpu_name(); const char *cpu_type = hb_get_cpu_platform_name(); hb_log("CPU: %s", cpu_name != NULL ? cpu_name : ""); if (cpu_type != NULL) { hb_log(" - %s", cpu_type); } hb_log(" - logical processor count: %d", hb_get_cpu_count()); /* Print OpenCL info here so that it's in all scan and encode logs */ hb_opencl_info_print(); #ifdef USE_QSV /* Print QSV info here so that it's in all scan and encode logs */ hb_qsv_info_print(); #endif hb_log( "hb_scan: path=%s, title_index=%d", path, title_index ); h->scan_thread = hb_scan_init( h, &h->scan_die, path, title_index, &h->title_set, preview_count, store_previews, min_duration ); } /** * Returns the list of titles found. * @param h Handle to hb_handle_t * @return Handle to hb_list_t of the title list. */ hb_list_t * hb_get_titles( hb_handle_t * h ) { return h->title_set.list_title; } hb_title_set_t * hb_get_title_set( hb_handle_t * h ) { return &h->title_set; } int hb_save_preview( hb_handle_t * h, int title, int preview, hb_buffer_t *buf ) { FILE * file; char filename[1024]; hb_get_tempory_filename( h, filename, "%d_%d_%d", hb_get_instance_id(h), title, preview ); file = hb_fopen(filename, "wb"); if( !file ) { hb_error( "hb_save_preview: fopen failed (%s)", filename ); return -1; } int pp, hh; for( pp = 0; pp < 3; pp++ ) { uint8_t *data = buf->plane[pp].data; int stride = buf->plane[pp].stride; int w = buf->plane[pp].width; int h = buf->plane[pp].height; for( hh = 0; hh < h; hh++ ) { fwrite( data, w, 1, file ); data += stride; } } fclose( file ); return 0; } hb_buffer_t * hb_read_preview(hb_handle_t * h, hb_title_t *title, int preview) { FILE * file; char filename[1024]; hb_get_tempory_filename(h, filename, "%d_%d_%d", hb_get_instance_id(h), title->index, preview); file = hb_fopen(filename, "rb"); if (!file) { hb_error( "hb_read_preview: fopen failed (%s)", filename ); return NULL; } hb_buffer_t * buf; buf = hb_frame_buffer_init(AV_PIX_FMT_YUV420P, title->width, title->height); int pp, hh; for (pp = 0; pp < 3; pp++) { uint8_t *data = buf->plane[pp].data; int stride = buf->plane[pp].stride; int w = buf->plane[pp].width; int h = buf->plane[pp].height; for (hh = 0; hh < h; hh++) { fread(data, w, 1, file); data += stride; } } fclose(file); return buf; } hb_image_t* hb_get_preview2(hb_handle_t * h, int title_idx, int picture, hb_ui_geometry_t *ui_geo, int deinterlace) { char filename[1024]; hb_buffer_t * in_buf, * deint_buf = NULL, * preview_buf; uint32_t swsflags; AVPicture pic_in, pic_preview, pic_deint, pic_crop; struct SwsContext * context; int width = ui_geo->width * ui_geo->par.num / ui_geo->par.den; int height = ui_geo->height; swsflags = SWS_LANCZOS | SWS_ACCURATE_RND; preview_buf = hb_frame_buffer_init(AV_PIX_FMT_RGB32, width, height); hb_avpicture_fill( &pic_preview, preview_buf ); // Allocate the AVPicture frames and fill in memset( filename, 0, 1024 ); hb_title_t * title; title = hb_find_title_by_index(h, title_idx); if (title == NULL) { hb_error( "hb_get_preview2: invalid title (%d)", title_idx ); return NULL; } in_buf = hb_read_preview( h, title, picture ); if ( in_buf == NULL ) { return NULL; } hb_avpicture_fill( &pic_in, in_buf ); if (deinterlace) { // Deinterlace and crop deint_buf = hb_frame_buffer_init( AV_PIX_FMT_YUV420P, title->width, title->height ); hb_deinterlace(deint_buf, in_buf); hb_avpicture_fill( &pic_deint, deint_buf ); av_picture_crop(&pic_crop, &pic_deint, AV_PIX_FMT_YUV420P, ui_geo->crop[0], ui_geo->crop[2] ); } else { // Crop av_picture_crop(&pic_crop, &pic_in, AV_PIX_FMT_YUV420P, ui_geo->crop[0], ui_geo->crop[2] ); } // Get scaling context context = hb_sws_get_context( title->width - (ui_geo->crop[2] + ui_geo->crop[3]), title->height - (ui_geo->crop[0] + ui_geo->crop[1]), AV_PIX_FMT_YUV420P, width, height, AV_PIX_FMT_RGB32, swsflags); // Scale sws_scale(context, (const uint8_t* const *)pic_crop.data, pic_crop.linesize, 0, title->height - (ui_geo->crop[0] + ui_geo->crop[1]), pic_preview.data, pic_preview.linesize); // Free context sws_freeContext( context ); hb_image_t *image = hb_buffer_to_image(preview_buf); // Clean up hb_buffer_close( &in_buf ); hb_buffer_close( &deint_buf ); hb_buffer_close( &preview_buf ); return image; } /** * Create preview image of desired title a index of picture. * @param h Handle to hb_handle_t. * @param title Handle to hb_title_t of desired title. * @param picture Index in title. * @param buffer Handle to buffer were image will be drawn. */ void hb_get_preview( hb_handle_t * h, hb_job_t * job, int picture, uint8_t * buffer ) { hb_title_t * title = job->title; char filename[1024]; hb_buffer_t * in_buf, * deint_buf = NULL, * preview_buf; uint8_t * pen; uint32_t swsflags; AVPicture pic_in, pic_preview, pic_deint, pic_crop; struct SwsContext * context; int i; int preview_size; swsflags = SWS_LANCZOS | SWS_ACCURATE_RND; preview_buf = hb_frame_buffer_init( AV_PIX_FMT_RGB32, job->width, job->height ); hb_avpicture_fill( &pic_preview, preview_buf ); // Allocate the AVPicture frames and fill in memset( filename, 0, 1024 ); in_buf = hb_read_preview( h, title, picture ); if ( in_buf == NULL ) { return; } hb_avpicture_fill( &pic_in, in_buf ); if( job->deinterlace ) { // Deinterlace and crop deint_buf = hb_frame_buffer_init( AV_PIX_FMT_YUV420P, title->width, title->height ); hb_deinterlace(deint_buf, in_buf); hb_avpicture_fill( &pic_deint, deint_buf ); av_picture_crop( &pic_crop, &pic_deint, AV_PIX_FMT_YUV420P, job->crop[0], job->crop[2] ); } else { // Crop av_picture_crop( &pic_crop, &pic_in, AV_PIX_FMT_YUV420P, job->crop[0], job->crop[2] ); } // Get scaling context context = hb_sws_get_context(title->width - (job->crop[2] + job->crop[3]), title->height - (job->crop[0] + job->crop[1]), AV_PIX_FMT_YUV420P, job->width, job->height, AV_PIX_FMT_RGB32, swsflags); // Scale sws_scale(context, (const uint8_t* const *)pic_crop.data, pic_crop.linesize, 0, title->height - (job->crop[0] + job->crop[1]), pic_preview.data, pic_preview.linesize); // Free context sws_freeContext( context ); preview_size = pic_preview.linesize[0]; pen = buffer; for( i = 0; i < job->height; i++ ) { memcpy( pen, pic_preview.data[0] + preview_size * i, 4 * job->width ); pen += 4 * job->width; } // Clean up hb_buffer_close( &in_buf ); hb_buffer_close( &deint_buf ); hb_buffer_close( &preview_buf ); } /** * Analyzes a frame to detect interlacing artifacts * and returns true if interlacing (combing) is found. * * Code taken from Thomas Oestreich's 32detect filter * in the Transcode project, with minor formatting changes. * * @param buf An hb_buffer structure holding valid frame data * @param width The frame's width in pixels * @param height The frame's height in pixels * @param color_equal Sensitivity for detecting similar colors * @param color_diff Sensitivity for detecting different colors * @param threshold Sensitivity for flagging planes as combed * @param prog_equal Sensitivity for detecting similar colors on progressive frames * @param prog_diff Sensitivity for detecting different colors on progressive frames * @param prog_threshold Sensitivity for flagging progressive frames as combed */ int hb_detect_comb( hb_buffer_t * buf, int color_equal, int color_diff, int threshold, int prog_equal, int prog_diff, int prog_threshold ) { int j, k, n, off, cc_1, cc_2, cc[3]; // int flag[3] ; // debugging flag uint16_t s1, s2, s3, s4; cc_1 = 0; cc_2 = 0; if ( buf->s.flags & 16 ) { /* Frame is progressive, be more discerning. */ color_diff = prog_diff; color_equal = prog_equal; threshold = prog_threshold; } /* One pas for Y, one pass for Cb, one pass for Cr */ for( k = 0; k < 3; k++ ) { uint8_t * data = buf->plane[k].data; int width = buf->plane[k].width; int stride = buf->plane[k].stride; int height = buf->plane[k].height; for( j = 0; j < width; ++j ) { off = 0; for( n = 0; n < ( height - 4 ); n = n + 2 ) { /* Look at groups of 4 sequential horizontal lines */ s1 = ( ( data )[ off + j ] & 0xff ); s2 = ( ( data )[ off + j + stride ] & 0xff ); s3 = ( ( data )[ off + j + 2 * stride ] & 0xff ); s4 = ( ( data )[ off + j + 3 * stride ] & 0xff ); /* Note if the 1st and 2nd lines are more different in color than the 1st and 3rd lines are similar in color.*/ if ( ( abs( s1 - s3 ) < color_equal ) && ( abs( s1 - s2 ) > color_diff ) ) ++cc_1; /* Note if the 2nd and 3rd lines are more different in color than the 2nd and 4th lines are similar in color.*/ if ( ( abs( s2 - s4 ) < color_equal ) && ( abs( s2 - s3 ) > color_diff) ) ++cc_2; /* Now move down 2 horizontal lines before starting over.*/ off += 2 * stride; } } // compare results /* The final cc score for a plane is the percentage of combed pixels it contains. Because sensitivity goes down to hundreths of a percent, multiply by 1000 so it will be easy to compare against the threhold value which is an integer. */ cc[k] = (int)( ( cc_1 + cc_2 ) * 1000.0 / ( width * height ) ); } /* HandBrake is all yuv420, so weight the average percentage of all 3 planes accordingly.*/ int average_cc = ( 2 * cc[0] + ( cc[1] / 2 ) + ( cc[2] / 2 ) ) / 3; /* Now see if that average percentage of combed pixels surpasses the threshold percentage given by the user.*/ if( average_cc > threshold ) { #if 0 hb_log("Average %i combed (Threshold %i) %i/%i/%i | PTS: %"PRId64" (%fs) %s", average_cc, threshold, cc[0], cc[1], cc[2], buf->start, (float)buf->start / 90000, (buf->flags & 16) ? "Film" : "Video" ); #endif return 1; } #if 0 hb_log("SKIPPED Average %i combed (Threshold %i) %i/%i/%i | PTS: %"PRId64" (%fs) %s", average_cc, threshold, cc[0], cc[1], cc[2], buf->start, (float)buf->start / 90000, (buf->flags & 16) ? "Film" : "Video" ); #endif /* Reaching this point means no combing detected. */ return 0; } /** * Calculates destination width and height for anamorphic content * * Returns calculated geometry * @param source_geometry - Pointer to source geometry info * @param ui_geometry - Pointer to requested destination parameters */ void hb_set_anamorphic_size2(hb_geometry_t *src_geo, hb_ui_geometry_t *ui_geo, hb_geometry_t *result) { hb_rational_t in_par, out_par; int keep_display_aspect = !!(ui_geo->keep & HB_KEEP_DISPLAY_ASPECT); int keep_height = !!(ui_geo->keep & HB_KEEP_HEIGHT); /* Set up some variables to make the math easier to follow. */ int cropped_width = src_geo->width - ui_geo->crop[2] - ui_geo->crop[3]; int cropped_height = src_geo->height - ui_geo->crop[0] - ui_geo->crop[1]; double storage_aspect = (double)cropped_width / cropped_height; int mod = ui_geo->modulus ? EVEN(ui_geo->modulus) : 2; // Use 64 bits to avoid overflow till the final hb_reduce() call hb_reduce(&in_par.num, &in_par.den, ui_geo->par.num, ui_geo->par.den); int64_t dst_par_num = in_par.num; int64_t dst_par_den = in_par.den; hb_rational_t src_par = src_geo->par; /* If a source was really NTSC or PAL and the user specified ITU PAR values, replace the standard PAR values with the ITU broadcast ones. */ if (src_geo->width == 720 && ui_geo->itu_par) { // convert aspect to a scaled integer so we can test for 16:9 & 4:3 // aspect ratios ignoring insignificant differences in the LSBs of // the floating point representation. int iaspect = src_geo->width * src_par.num * 9. / (src_geo->height * src_par.den); /* Handle ITU PARs */ if (src_geo->height == 480) { /* It's NTSC */ if (iaspect == 16) { /* It's widescreen */ dst_par_num = 40; dst_par_den = 33; } else if (iaspect == 12) { /* It's 4:3 */ dst_par_num = 10; dst_par_den = 11; } } else if (src_geo->height == 576) { /* It's PAL */ if (iaspect == 16) { /* It's widescreen */ dst_par_num = 16; dst_par_den = 11; } else if (iaspect == 12) { /* It's 4:3 */ dst_par_num = 12; dst_par_den = 11; } } } /* 3 different ways of deciding output dimensions: - 1: Strict anamorphic, preserve source dimensions - 2: Loose anamorphic, round to mod16 and preserve storage aspect ratio - 3: Power user anamorphic, specify everything */ int width, height; int maxWidth, maxHeight; maxWidth = MULTIPLE_MOD_DOWN(ui_geo->maxWidth, mod); maxHeight = MULTIPLE_MOD_DOWN(ui_geo->maxHeight, mod); if (maxWidth && maxWidth < 32) maxWidth = 32; if (maxHeight && maxHeight < 32) maxHeight = 32; switch (ui_geo->mode) { case HB_ANAMORPHIC_NONE: { double par, cropped_sar, dar; par = (double)src_geo->par.num / src_geo->par.den; cropped_sar = (double)cropped_width / cropped_height; dar = par * cropped_sar; /* "None" anamorphic. a.k.a. non-anamorphic * - Uses mod-compliant dimensions, set by user * - Allows users to set the either width *or* height */ if (keep_display_aspect) { if (!keep_height) { width = MULTIPLE_MOD_UP(ui_geo->width, mod); height = MULTIPLE_MOD(width / dar, mod); } else { height = MULTIPLE_MOD_UP(ui_geo->height, mod); width = MULTIPLE_MOD(height * dar, mod); } } else { width = MULTIPLE_MOD_UP(ui_geo->width, mod); height = MULTIPLE_MOD_UP(ui_geo->height, mod); } if (maxWidth && (width > maxWidth)) { width = maxWidth; height = MULTIPLE_MOD(width / dar, mod); } if (maxHeight && (height > maxHeight)) { height = maxHeight; width = MULTIPLE_MOD(height * dar, mod); } dst_par_num = dst_par_den = 1; } break; default: case HB_ANAMORPHIC_STRICT: { /* "Strict" anamorphic. * - Uses mod2-compliant dimensions, * - Forces title - crop dimensions */ width = MULTIPLE_MOD_UP(cropped_width, 2); height = MULTIPLE_MOD_UP(cropped_height, 2); /* Adjust the output PAR for new width/height * Film AR is the source display width / cropped source height. * Output display width is the output height * film AR. * Output PAR is the output display width / output storage width. * * i.e. * source_display_width = cropped_width * source PAR * AR = source_display_width / cropped_height; * output_display_width = height * AR; * par = output_display_width / width; * * When these terms are reduced, you get the following... */ dst_par_num = (int64_t)height * cropped_width * src_par.num; dst_par_den = (int64_t)width * cropped_height * src_par.den; } break; case HB_ANAMORPHIC_LOOSE: { /* "Loose" anamorphic. * - Uses mod-compliant dimensions, set by user * - Allows users to set the either width *or* height */ if (!keep_height) { width = MULTIPLE_MOD_UP(ui_geo->width, mod); height = MULTIPLE_MOD_UP(width / storage_aspect + 0.5, mod); } else { height = MULTIPLE_MOD_UP(ui_geo->height, mod); width = MULTIPLE_MOD_UP(height * storage_aspect + 0.5, mod); } if (maxWidth && (maxWidth < width)) { width = maxWidth; height = MULTIPLE_MOD(width / storage_aspect + 0.5, mod); } if (maxHeight && (maxHeight < height)) { height = maxHeight; width = MULTIPLE_MOD(height * storage_aspect + 0.5, mod); } /* Adjust the output PAR for new width/height See comment in HB_ANAMORPHIC_STRICT */ dst_par_num = (int64_t)height * cropped_width * src_par.num; dst_par_den = (int64_t)width * cropped_height * src_par.den; } break; case HB_ANAMORPHIC_CUSTOM: { /* Anamorphic 3: Power User Jamboree - Set everything based on specified values */ /* Use specified storage dimensions */ storage_aspect = (double)ui_geo->width / ui_geo->height; /* Time to get picture dimensions that divide cleanly.*/ width = MULTIPLE_MOD_UP(ui_geo->width, mod); height = MULTIPLE_MOD_UP(ui_geo->height, mod); /* Bind to max dimensions */ if (maxWidth && width > maxWidth) { width = maxWidth; // If we are keeping the display aspect, then we are going // to be modifying the PAR anyway. So it's preferred // to let the width/height stray some from the original // requested storage aspect. // // But otherwise, PAR and DAR will change the least // if we stay as close as possible to the requested // storage aspect. if (!keep_display_aspect) { height = width / storage_aspect + 0.5; height = MULTIPLE_MOD(height, mod); } } if (maxHeight && height > maxHeight) { height = maxHeight; // Ditto, see comment above if (!keep_display_aspect) { width = height * storage_aspect + 0.5; width = MULTIPLE_MOD(width, mod); } } /* That finishes the storage dimensions. On to display. */ if (ui_geo->dar.num && ui_geo->dar.den) { /* We need to adjust the PAR to produce this aspect. */ dst_par_num = (int64_t)height * ui_geo->dar.num / ui_geo->dar.den; dst_par_den = width; } else { if (keep_display_aspect) { /* We can ignore the possibility of a PAR change * Adjust the output PAR for new width/height * See comment in HB_ANAMORPHIC_STRICT */ dst_par_num = (int64_t)height * cropped_width * src_par.num; dst_par_den = (int64_t)width * cropped_height * src_par.den; } else { /* If the dimensions were changed by the modulus * or by maxWidth/maxHeight, we also change the * output PAR so that the DAR is unchanged. * * PAR is the requested output display width / storage width * requested output display width is the original * requested width * original requested PAR */ dst_par_num = ui_geo->width * dst_par_num; dst_par_den = width * dst_par_den; } } } break; } /* Pass the results back to the caller */ result->width = width; result->height = height; /* While x264 is smart enough to reduce fractions on its own, libavcodec * needs some help with the math, so lose superfluous factors. */ hb_limit_rational64(&dst_par_num, &dst_par_den, dst_par_num, dst_par_den, 65535); /* If the user is directling updating PAR, don't override his values */ hb_reduce(&out_par.num, &out_par.den, dst_par_num, dst_par_den); if (ui_geo->mode == HB_ANAMORPHIC_CUSTOM && !keep_display_aspect && out_par.num == in_par.num && out_par.den == in_par.den) { result->par.num = ui_geo->par.num; result->par.den = ui_geo->par.den; } else { hb_reduce(&result->par.num, &result->par.den, dst_par_num, dst_par_den); } } /** * Calculates job width and height for anamorphic content, * * @param job Handle to hb_job_t * @param output_width Pointer to returned storage width * @param output_height Pointer to returned storage height * @param output_par_width Pointer to returned pixel width * @param output_par_height Pointer to returned pixel height */ void hb_set_anamorphic_size( hb_job_t * job, int *output_width, int *output_height, int *output_par_width, int *output_par_height ) { hb_geometry_t result; hb_geometry_t src; hb_ui_geometry_t ui_geo; src.width = job->title->width; src.height = job->title->height; src.par.num = job->title->pixel_aspect_width; src.par.den = job->title->pixel_aspect_height; ui_geo.width = job->width; ui_geo.height = job->height; ui_geo.par.num = job->anamorphic.par_width; ui_geo.par.den = job->anamorphic.par_height; ui_geo.modulus = job->modulus; memcpy(ui_geo.crop, job->crop, sizeof(int[4])); ui_geo.maxWidth = job->maxWidth; ui_geo.maxHeight = job->maxHeight; ui_geo.mode = job->anamorphic.mode; ui_geo.keep = 0; if (job->anamorphic.keep_display_aspect) ui_geo.keep = HB_KEEP_DISPLAY_ASPECT; ui_geo.itu_par = job->anamorphic.itu_par; ui_geo.dar.num = job->anamorphic.dar_width; ui_geo.dar.den = job->anamorphic.dar_height; hb_set_anamorphic_size2(&src, &ui_geo, &result); *output_width = result.width; *output_height = result.height; *output_par_width = result.par.num; *output_par_height = result.par.den; } /** * Add a filter to a jobs filter list * * @param job Handle to hb_job_t * @param settings to give the filter */ void hb_add_filter( hb_job_t * job, hb_filter_object_t * filter, const char * settings_in ) { char * settings = NULL; if ( settings_in != NULL ) { settings = strdup( settings_in ); } filter->settings = settings; if( filter->enforce_order ) { // Find the position in the filter chain this filter belongs in int i; for( i = 0; i < hb_list_count( job->list_filter ); i++ ) { hb_filter_object_t * f = hb_list_item( job->list_filter, i ); if( f->id > filter->id ) { hb_list_insert( job->list_filter, i, filter ); return; } else if( f->id == filter->id ) { // Don't allow the same filter to be added twice hb_filter_close( &filter ); return; } } } // No position found or order not enforced for this filter hb_list_add( job->list_filter, filter ); } /** * Validate and adjust dimensions if necessary * * @param job Handle to hb_job_t */ void hb_validate_size( hb_job_t * job ) { int width, height, par_width, par_height; hb_set_anamorphic_size(job, &width, &height, &par_width, &par_height); job->width = width; job->height = height; job->anamorphic.par_width = par_width; job->anamorphic.par_height = par_height; } /** * Returns the number of jobs in the queue. * @param h Handle to hb_handle_t. * @return Number of jobs. */ int hb_count( hb_handle_t * h ) { return hb_list_count( h->jobs ); } /** * Returns handle to job at index i within the job list. * @param h Handle to hb_handle_t. * @param i Index of job. * @returns Handle to hb_job_t of desired job. */ hb_job_t * hb_job( hb_handle_t * h, int i ) { return hb_list_item( h->jobs, i ); } hb_job_t * hb_current_job( hb_handle_t * h ) { return( h->current_job ); } /** * Adds a job to the job list. * @param h Handle to hb_handle_t. * @param job Handle to hb_job_t. */ void hb_add( hb_handle_t * h, hb_job_t * job ) { hb_job_t * job_copy; hb_audio_t * audio; hb_subtitle_t * subtitle; int i; char audio_lang[4]; /* Copy the job */ job_copy = calloc( sizeof( hb_job_t ), 1 ); memcpy( job_copy, job, sizeof( hb_job_t ) ); /* If we're doing Foreign Audio Search, copy all subtitles matching the * first audio track language we find in the audio list. * * Otherwise, copy all subtitles found in the input job (which can be * manually selected by the user, or added after the Foreign Audio * Search pass). */ memset( audio_lang, 0, sizeof( audio_lang ) ); if( job->indepth_scan ) { /* Find the first audio language that is being encoded, then add all the * matching subtitles for that language. */ for( i = 0; i < hb_list_count( job->list_audio ); i++ ) { if( ( audio = hb_list_item( job->list_audio, i ) ) ) { strncpy( audio_lang, audio->config.lang.iso639_2, sizeof( audio_lang ) ); break; } } /* * If doing a subtitle scan then add all the matching subtitles for this * language. */ job_copy->list_subtitle = hb_list_init(); for( i = 0; i < hb_list_count( job->title->list_subtitle ); i++ ) { subtitle = hb_list_item( job->title->list_subtitle, i ); if( strcmp( subtitle->iso639_2, audio_lang ) == 0 && hb_subtitle_can_force( subtitle->source ) ) { /* Matched subtitle language with audio language, so add this to * our list to scan. * * We will update the subtitle list on the next pass later, after * the subtitle scan pass has completed. */ hb_list_add( job_copy->list_subtitle, hb_subtitle_copy( subtitle ) ); } } } else { /* Copy all subtitles from the input job to title_copy/job_copy. */ job_copy->list_subtitle = hb_subtitle_list_copy( job->list_subtitle ); } job_copy->list_chapter = hb_chapter_list_copy( job->list_chapter ); job_copy->list_audio = hb_audio_list_copy( job->list_audio ); job_copy->list_attachment = hb_attachment_list_copy( job->list_attachment ); job_copy->metadata = hb_metadata_copy( job->metadata ); if (job->encoder_preset != NULL) job_copy->encoder_preset = strdup(job->encoder_preset); if (job->encoder_tune != NULL) job_copy->encoder_tune = strdup(job->encoder_tune); if (job->encoder_options != NULL) job_copy->encoder_options = strdup(job->encoder_options); if (job->encoder_profile != NULL) job_copy->encoder_profile = strdup(job->encoder_profile); if (job->encoder_level != NULL) job_copy->encoder_level = strdup(job->encoder_level); if (job->file != NULL) job_copy->file = strdup(job->file); job_copy->h = h; job_copy->pause = h->pause_lock; /* Copy the job filter list */ job_copy->list_filter = hb_filter_list_copy( job->list_filter ); /* Add the job to the list */ hb_list_add( h->jobs, job_copy ); h->job_count = hb_count(h); h->job_count_permanent++; } /** * Removes a job from the job list. * @param h Handle to hb_handle_t. * @param job Handle to hb_job_t. */ void hb_rem( hb_handle_t * h, hb_job_t * job ) { hb_list_rem( h->jobs, job ); h->job_count = hb_count(h); if (h->job_count_permanent) h->job_count_permanent--; /* XXX free everything XXX */ } /** * Starts the conversion process. * Sets state to HB_STATE_WORKING. * calls hb_work_init, to launch work thread. Stores handle to work thread. * @param h Handle to hb_handle_t. */ void hb_start( hb_handle_t * h ) { /* XXX Hack */ h->job_count = hb_list_count( h->jobs ); h->job_count_permanent = h->job_count; hb_lock( h->state_lock ); h->state.state = HB_STATE_WORKING; #define p h->state.param.working p.progress = 0.0; p.job_cur = 1; p.job_count = h->job_count; p.rate_cur = 0.0; p.rate_avg = 0.0; p.hours = -1; p.minutes = -1; p.seconds = -1; p.sequence_id = 0; #undef p hb_unlock( h->state_lock ); h->paused = 0; h->work_die = 0; h->work_error = HB_ERROR_NONE; h->work_thread = hb_work_init( h->jobs, &h->work_die, &h->work_error, &h->current_job ); } /** * Pauses the conversion process. * @param h Handle to hb_handle_t. */ void hb_pause( hb_handle_t * h ) { if( !h->paused ) { hb_lock( h->pause_lock ); h->paused = 1; hb_current_job( h )->st_pause_date = hb_get_date(); hb_lock( h->state_lock ); h->state.state = HB_STATE_PAUSED; hb_unlock( h->state_lock ); } } /** * Resumes the conversion process. * @param h Handle to hb_handle_t. */ void hb_resume( hb_handle_t * h ) { if( h->paused ) { #define job hb_current_job( h ) if( job->st_pause_date != -1 ) { job->st_paused += hb_get_date() - job->st_pause_date; } #undef job hb_unlock( h->pause_lock ); h->paused = 0; } } /** * Stops the conversion process. * @param h Handle to hb_handle_t. */ void hb_stop( hb_handle_t * h ) { h->work_die = 1; h->job_count = hb_count(h); h->job_count_permanent = 0; hb_resume( h ); } /** * Stops the conversion process. * @param h Handle to hb_handle_t. */ void hb_scan_stop( hb_handle_t * h ) { h->scan_die = 1; h->job_count = hb_count(h); h->job_count_permanent = 0; hb_resume( h ); } /** * Returns the state of the conversion process. * @param h Handle to hb_handle_t. * @param s Handle to hb_state_t which to copy the state data. */ void hb_get_state( hb_handle_t * h, hb_state_t * s ) { hb_lock( h->state_lock ); memcpy( s, &h->state, sizeof( hb_state_t ) ); if ( h->state.state == HB_STATE_SCANDONE || h->state.state == HB_STATE_WORKDONE ) h->state.state = HB_STATE_IDLE; hb_unlock( h->state_lock ); } void hb_get_state2( hb_handle_t * h, hb_state_t * s ) { hb_lock( h->state_lock ); memcpy( s, &h->state, sizeof( hb_state_t ) ); hb_unlock( h->state_lock ); } /** * Called in MacGui in UpdateUI to check * for a new scan being completed to set a new source */ int hb_get_scancount( hb_handle_t * h) { return h->scanCount; } /** * Closes access to libhb by freeing the hb_handle_t handle ontained in hb_init. * @param _h Pointer to handle to hb_handle_t. */ void hb_close( hb_handle_t ** _h ) { hb_handle_t * h = *_h; hb_title_t * title; h->die = 1; hb_thread_close( &h->main_thread ); while( ( title = hb_list_item( h->title_set.list_title, 0 ) ) ) { hb_list_rem( h->title_set.list_title, title ); hb_title_close( &title ); } hb_list_close( &h->title_set.list_title ); hb_list_close( &h->jobs ); hb_lock_close( &h->state_lock ); hb_lock_close( &h->pause_lock ); hb_system_sleep_opaque_close(&h->system_sleep_opaque); free( h->interjob ); free( h ); *_h = NULL; } int hb_global_init() { int result = 0; result = hb_platform_init(); if (result < 0) { hb_error("Platform specific initialization failed!"); return -1; } #ifdef USE_QSV result = hb_qsv_info_init(); if (result < 0) { hb_error("hb_qsv_info_init failed!"); return -1; } #endif /* libavcodec */ hb_avcodec_init(); /* HB work objects */ hb_register(&hb_muxer); hb_register(&hb_reader); hb_register(&hb_sync_video); hb_register(&hb_sync_audio); hb_register(&hb_decavcodecv); hb_register(&hb_decavcodeca); hb_register(&hb_declpcm); hb_register(&hb_deccc608); hb_register(&hb_decpgssub); hb_register(&hb_decsrtsub); hb_register(&hb_decssasub); hb_register(&hb_dectx3gsub); hb_register(&hb_decutf8sub); hb_register(&hb_decvobsub); hb_register(&hb_encvobsub); hb_register(&hb_encavcodec); hb_register(&hb_encavcodeca); #ifdef __APPLE__ hb_register(&hb_encca_aac); hb_register(&hb_encca_haac); #endif hb_register(&hb_enclame); hb_register(&hb_enctheora); hb_register(&hb_encvorbis); hb_register(&hb_encx264); #ifdef USE_X265 hb_register(&hb_encx265); #endif #ifdef USE_QSV hb_register(&hb_encqsv); #endif hb_common_global_init(); return result; } /** * Cleans up libhb at a process level. Call before the app closes. Removes preview directory. */ void hb_global_close() { char dirname[1024]; DIR * dir; struct dirent * entry; /* Find and remove temp folder */ memset( dirname, 0, 1024 ); hb_get_temporary_directory( dirname ); dir = opendir( dirname ); if (dir) { while( ( entry = readdir( dir ) ) ) { char filename[1024]; if( entry->d_name[0] == '.' ) { continue; } memset( filename, 0, 1024 ); snprintf( filename, 1023, "%s/%s", dirname, entry->d_name ); unlink( filename ); } closedir( dir ); rmdir( dirname ); } } /** * Monitors the state of the update, scan, and work threads. * Sets scan done state when scan thread exits. * Sets work done state when work thread exits. * @param _h Handle to hb_handle_t */ static void thread_func( void * _h ) { hb_handle_t * h = (hb_handle_t *) _h; char dirname[1024]; h->pid = getpid(); /* Create folder for temporary files */ memset( dirname, 0, 1024 ); hb_get_temporary_directory( dirname ); hb_mkdir( dirname ); while( !h->die ) { /* In case the check_update thread hangs, it'll die sooner or later. Then, we join it here */ if( h->update_thread && hb_thread_has_exited( h->update_thread ) ) { hb_thread_close( &h->update_thread ); } /* Check if the scan thread is done */ if( h->scan_thread && hb_thread_has_exited( h->scan_thread ) ) { hb_thread_close( &h->scan_thread ); if ( h->scan_die ) { hb_title_t * title; hb_remove_previews( h ); while( ( title = hb_list_item( h->title_set.list_title, 0 ) ) ) { hb_list_rem( h->title_set.list_title, title ); hb_title_close( &title ); } hb_log( "hb_scan: canceled" ); } else { hb_log( "libhb: scan thread found %d valid title(s)", hb_list_count( h->title_set.list_title ) ); } hb_lock( h->state_lock ); h->state.state = HB_STATE_SCANDONE; //originally state.state hb_unlock( h->state_lock ); /*we increment this sessions scan count by one for the MacGui to trigger a new source being set */ h->scanCount++; } /* Check if the work thread is done */ if( h->work_thread && hb_thread_has_exited( h->work_thread ) ) { hb_thread_close( &h->work_thread ); hb_log( "libhb: work result = %d", h->work_error ); hb_lock( h->state_lock ); h->state.state = HB_STATE_WORKDONE; h->state.param.workdone.error = h->work_error; h->job_count = hb_count(h); if (h->job_count < 1) h->job_count_permanent = 0; hb_unlock( h->state_lock ); } hb_snooze( 50 ); } if( h->scan_thread ) { hb_scan_stop( h ); hb_thread_close( &h->scan_thread ); } if( h->work_thread ) { hb_stop( h ); hb_thread_close( &h->work_thread ); } hb_remove_previews( h ); } /** * Redirects stderr to the registered callback * function. * @param _data Unused. */ static void redirect_thread_func(void * _data) { int pfd[2]; pipe(pfd); #if defined( SYS_MINGW ) // dup2 doesn't work on windows for some stupid reason stderr->_file = pfd[1]; #else dup2(pfd[1], /*stderr*/ 2); #endif FILE * log_f = fdopen(pfd[0], "rb"); char line_buffer[500]; while(fgets(line_buffer, 500, log_f) != NULL) { hb_log_callback(line_buffer); } } /** * Returns the PID. * @param h Handle to hb_handle_t */ int hb_get_pid( hb_handle_t * h ) { return h->pid; } /** * Returns the id for the given instance. * @param h Handle to hb_handle_t * @returns The ID for the given instance */ int hb_get_instance_id( hb_handle_t * h ) { return h->id; } /** * Sets the current state. * @param h Handle to hb_handle_t * @param s Handle to new hb_state_t */ void hb_set_state( hb_handle_t * h, hb_state_t * s ) { hb_lock( h->pause_lock ); hb_lock( h->state_lock ); memcpy( &h->state, s, sizeof( hb_state_t ) ); if( h->state.state == HB_STATE_WORKING || h->state.state == HB_STATE_SEARCHING ) { /* XXX Hack */ if (h->job_count < 1) h->job_count_permanent = 1; h->state.param.working.job_cur = h->job_count_permanent - hb_list_count( h->jobs ); h->state.param.working.job_count = h->job_count_permanent; // Set which job is being worked on if (h->current_job) h->state.param.working.sequence_id = h->current_job->sequence_id; else h->state.param.working.sequence_id = 0; } hb_unlock( h->state_lock ); hb_unlock( h->pause_lock ); } void hb_system_sleep_allow(hb_handle_t *h) { hb_system_sleep_private_enable(h->system_sleep_opaque); } void hb_system_sleep_prevent(hb_handle_t *h) { hb_system_sleep_private_disable(h->system_sleep_opaque); } /* Passes a pointer to persistent data */ hb_interjob_t * hb_interjob_get( hb_handle_t * h ) { return h->interjob; } HandBrake-0.10.2/libhb/hb_dict.c0000664000175200017520000001444712463330511016667 0ustar handbrakehandbrake/* hb_dict.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "hb_dict.h" hb_dict_t * hb_dict_init( int alloc ) { hb_dict_t * dict = NULL; dict = malloc( sizeof( hb_dict_t ) ); if( !dict ) { hb_log( "ERROR: could not allocate hb_dict_t" ); return NULL; } dict->count = 0; dict->objects = malloc( alloc * sizeof( hb_dict_entry_t ) ); if( !dict->objects ) { hb_log( "ERROR: could not allocate hb_dict_t objects" ); dict->alloc = 0; } else { dict->alloc = alloc; } return dict; } void hb_dict_free( hb_dict_t ** dict_ptr ) { hb_dict_t * dict = *dict_ptr; if( dict ) { if( dict->objects ) { int i; for( i = 0; i < dict->count; i++ ) { if( dict->objects[i].key ) { free( dict->objects[i].key ); } if( dict->objects[i].value ) { free( dict->objects[i].value ); } } free( dict->objects ); } free( *dict_ptr ); *dict_ptr = NULL; } } void hb_dict_set( hb_dict_t ** dict_ptr, const char * key, const char * value ) { hb_dict_t * dict = *dict_ptr; if( !dict ) { hb_log( "hb_dict_set: NULL dictionary" ); return; } if( !key || !strlen( key ) ) return; hb_dict_entry_t * entry = hb_dict_get( dict, key ); if( entry ) { if( entry->value ) { if( value && !strcmp( value, entry->value ) ) return; else { free( entry->value ); entry->value = NULL; } } if( value && strlen( value ) ) entry->value = strdup( value ); } else { if( dict->alloc <= dict->count ) { hb_dict_entry_t * tmp = NULL; tmp = malloc( ( 2 * dict->alloc ) * sizeof( hb_dict_entry_t ) ); if( !tmp ) { hb_log( "ERROR: could not realloc hb_dict_t objects" ); return; } if( dict->objects ) { if( dict->count ) memcpy( tmp, dict->objects, dict->count * sizeof( hb_dict_entry_t ) ); free( dict->objects ); } dict->objects = tmp; dict->alloc *= 2; } dict->objects[dict->count].key = strdup( key ); if( value && strlen( value ) ) dict->objects[dict->count].value = strdup( value ); else dict->objects[dict->count].value = NULL; dict->count++; } } void hb_dict_unset( hb_dict_t ** dict_ptr, const char * key ) { hb_dict_t * dict = *dict_ptr; if( !dict || !dict->objects || !key || !strlen( key ) ) return; int i; for( i = 0; i < dict->count; i++ ) if( !strcmp( key, dict->objects[i].key ) ) { free( dict->objects[i].key ); if( dict->objects[i].value ) free( dict->objects[i].value ); if( i != --dict->count ) memmove( &dict->objects[i], &dict->objects[i+1], sizeof( hb_dict_entry_t ) * ( dict->count - i ) ); } } hb_dict_entry_t * hb_dict_get( hb_dict_t * dict, const char * key ) { if( !dict || !dict->objects || !key || !strlen( key ) ) return NULL; int i; for( i = 0; i < dict->count; i++ ) if( !strcmp( key, dict->objects[i].key ) ) return &dict->objects[i]; return NULL; } hb_dict_entry_t * hb_dict_next( hb_dict_t * dict, hb_dict_entry_t * previous ) { if( dict == NULL || dict->objects == NULL || !dict->count ) return NULL; if( previous == NULL ) return &dict->objects[0]; unsigned int prev_index = previous - dict->objects; if( prev_index + 1 < dict->count ) return &dict->objects[prev_index+1]; return NULL; } hb_dict_t * hb_encopts_to_dict( const char * encopts, int encoder ) { hb_dict_t * dict = NULL; if( encopts && *encopts ) { char *cur_opt, *opts_start, *value; const char *name; dict = hb_dict_init( 10 ); if( !dict ) return NULL; cur_opt = opts_start = strdup( encopts ); if( opts_start ) { while( *cur_opt ) { name = cur_opt; cur_opt += strcspn( cur_opt, ":" ); if( *cur_opt ) { *cur_opt = 0; cur_opt++; } value = strchr( name, '=' ); if( value ) { *value = 0; value++; } // x264 has multiple names for some options if( encoder == HB_VCODEC_X264 ) name = hb_x264_encopt_name( name ); #ifdef USE_X265 // x265 has multiple names for some options if( encoder == HB_VCODEC_X265 ) name = hb_x265_encopt_name( name ); #endif hb_dict_set( &dict, name, value ); } } free( opts_start ); } return dict; } char * hb_dict_to_encopts( hb_dict_t * dict ) { int first_opt = 1; char *tmp, *encopts_tmp, *encopts = NULL; hb_dict_entry_t * entry = NULL; while( ( entry = hb_dict_next( dict, entry ) ) ) { tmp = hb_strdup_printf( "%s%s%s%s", first_opt ? "" : ":", entry->key, entry->value ? "=" : "", entry->value ? entry->value : "" ); if( tmp ) { encopts_tmp = hb_strncat_dup( encopts, tmp, strlen( tmp ) ); if( encopts_tmp ) { if( encopts ) free( encopts ); encopts = encopts_tmp; } first_opt = 0; free( tmp ); } } return encopts; } HandBrake-0.10.2/libhb/bits.h0000664000175200017520000000443412463330511016234 0ustar handbrakehandbrake/* bits.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef HB_BITS_H #define HB_BITS_H static inline int allbits_set(uint32_t *bitmap, int num_words) { unsigned int i; for( i = 0; i < num_words; i++ ) { if( bitmap[i] != 0xFFFFFFFF ) return (0); } return (1); } static inline int bit_is_set( uint32_t *bit_map, int bit_pos ) { return( ( bit_map[bit_pos >> 5] & (0x1 << (bit_pos & 0x1F) ) ) != 0 ); } static inline int bit_is_clear( uint32_t *bit_map, int bit_pos ) { return( ( bit_map[bit_pos >> 5] & ( 0x1 << (bit_pos & 0x1F) ) ) == 0 ); } static inline void bit_set( uint32_t *bit_map, int bit_pos ) { bit_map[bit_pos >> 5] |= 0x1 << (bit_pos & 0x1F); } static inline void bit_clear(uint32_t *bit_map, int bit_pos) { bit_map[bit_pos >> 5] &= ~( 0x1 << ( bit_pos & 0x1F ) ); } static inline void bit_nclear(uint32_t *bit_map, int start_pos, int stop_pos) { int start_word = start_pos >> 5; int stop_word = stop_pos >> 5; if ( start_word == stop_word ) { bit_map[start_word] &= ( ( 0x7FFFFFFF >> ( 31 - (start_pos & 0x1F ) ) ) | ( 0xFFFFFFFE << ( stop_pos & 0x1F ) ) ); } else { bit_map[start_word] &= ( 0x7FFFFFFF >> ( 31 - ( start_pos & 0x1F ) ) ); while (++start_word < stop_word) bit_map[start_word] = 0; bit_map[stop_word] &= 0xFFFFFFFE << ( stop_pos & 0x1F ); } } static inline void bit_nset(uint32_t *bit_map, int start_pos, int stop_pos) { int start_word = start_pos >> 5; int stop_word = stop_pos >> 5; if ( start_word == stop_word ) { bit_map[start_word] |= ( ( 0xFFFFFFFF << ( start_pos & 0x1F ) ) & ( 0xFFFFFFFF >> ( 31 - ( stop_pos & 0x1F ) ) ) ); } else { bit_map[start_word] |= 0xFFFFFFFF << ( start_pos & 0x1F ); while (++start_word < stop_word) bit_map[start_word] = 0xFFFFFFFF; bit_map[stop_word] |= 0xFFFFFFFF >> ( 31 - ( stop_pos & 0x1F ) ); } } #endif /* HB_BITS_H */ HandBrake-0.10.2/libhb/batch.c0000664000175200017520000000774112463330511016353 0ustar handbrakehandbrake/* batch.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "lang.h" struct hb_batch_s { char * path; hb_list_t * list_file; }; static int compare_str(const void *a, const void *b) { return strncmp(*(const char**)a, *(const char**)b, PATH_MAX); } /*********************************************************************** * hb_batch_init *********************************************************************** * **********************************************************************/ hb_batch_t * hb_batch_init( char * path ) { hb_batch_t * d; hb_stat_t sb; HB_DIR * dir; struct dirent * entry; char * filename; int count, ii; char ** files; if ( hb_stat( path, &sb ) ) return NULL; if ( !S_ISDIR( sb.st_mode ) ) return NULL; dir = hb_opendir(path); if ( dir == NULL ) return NULL; // Count the total number of entries count = 0; while ( (entry = hb_readdir( dir ) ) ) { count++; } files = malloc(count * sizeof(char*)); // Find all regular files ii = 0; hb_rewinddir(dir); while ( (entry = hb_readdir( dir ) ) ) { filename = hb_strdup_printf( "%s" DIR_SEP_STR "%s", path, entry->d_name ); if ( hb_stat( filename, &sb ) ) { free( filename ); continue; } if ( !S_ISREG( sb.st_mode ) ) { free( filename ); continue; } files[ii++] = filename; } count = ii; // Sort the files qsort(files, count, sizeof(char*), compare_str); // Create file list d = calloc( sizeof( hb_batch_t ), 1 ); d->list_file = hb_list_init(); for (ii = 0; ii < count; ii++) { hb_list_add( d->list_file, files[ii] ); } hb_closedir( dir ); free(files); if ( hb_list_count( d->list_file ) == 0 ) { hb_list_close( &d->list_file ); free( d ); return NULL; } d->path = strdup( path ); return d; } /*********************************************************************** * hb_batch_title_count **********************************************************************/ int hb_batch_title_count( hb_batch_t * d ) { return hb_list_count( d->list_file ); } /*********************************************************************** * hb_batch_title_scan **********************************************************************/ hb_title_t * hb_batch_title_scan( hb_batch_t * d, int t ) { hb_title_t * title; char * filename; hb_stream_t * stream; if ( t < 0 ) return NULL; filename = hb_list_item( d->list_file, t - 1 ); if ( filename == NULL ) return NULL; hb_log( "batch: scanning %s", filename ); title = hb_title_init( filename, 0 ); stream = hb_stream_open( filename, title, 1 ); if ( stream == NULL ) { hb_title_close( &title ); return NULL; } title = hb_stream_title_scan( stream, title ); hb_stream_close( &stream ); if ( title != NULL ) { title->index = t; } return title; } /*********************************************************************** * hb_batch_close *********************************************************************** * Closes and frees everything **********************************************************************/ void hb_batch_close( hb_batch_t ** _d ) { hb_batch_t * d = *_d; char * filename; while ( ( filename = hb_list_item( d->list_file, 0 ) ) ) { hb_list_rem( d->list_file, filename ); free( filename ); } hb_list_close( &d->list_file ); free( d->path ); free( d ); *_d = NULL; } HandBrake-0.10.2/libhb/vadxva2.h0000664000175200017520000002245512463330511016651 0ustar handbrakehandbrake/* vadxva2.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html Authors: Peng Gao Li Cao */ #ifdef USE_HWD #ifndef HB_VA_DXVA2_H #define HB_VA_DXVA2_H #include "hbffmpeg.h" #include "d3d9.h" #include "libavcodec/dxva2.h" #include "dxva2api.h" #include "common.h" #include "opencl.h" #include "openclwrapper.h" #define HB_FOURCC( a, b, c, d ) ( ((uint32_t)a) | ( ((uint32_t)b) << 8 ) | ( ((uint32_t)c) << 16 ) | ( ((uint32_t)d) << 24 ) ) #define MAKEFOURCC( a, b, c, d ) ((DWORD)(BYTE)(a) | ((DWORD)(BYTE)(b) << 8) | ((DWORD)(BYTE)(c) << 16) | ((DWORD)(BYTE)(d) << 24 )) #define HB_CODEC_YV12 HB_FOURCC( 'Y', 'V', '1', '2' ) #define HB_CODEC_NV12 HB_FOURCC( 'N', 'V', '1', '2' ) #define DXVA2_E_NOT_INITIALIZED MAKE_HRESULT( 1, 4, 4096 ) #define DXVA2_E_NEW_VIDEO_DEVICE MAKE_HRESULT( 1, 4, 4097 ) #define DXVA2_E_VIDEO_DEVICE_LOCKED MAKE_HRESULT( 1, 4, 4098 ) #define DXVA2_E_NOT_AVAILABLE MAKE_HRESULT( 1, 4, 4099 ) #define VA_DXVA2_MAX_SURFACE_COUNT (64) static const GUID DXVA_NoEncrypt = { 0x1b81bed0, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID IID_IDirectXVideoDecoderService = {0xfc51a551, 0xd5e7, 0x11d9, {0xaf, 0x55, 0x00, 0x05, 0x4e, 0x43, 0xff, 0x02}}; static const GUID DXVA2_ModeMPEG2_MoComp = { 0xe6a9f44b, 0x61b0, 0x4563, {0x9e, 0xa4, 0x63, 0xd2, 0xa3, 0xc6, 0xfe, 0x66} }; static const GUID DXVA2_ModeMPEG2_IDCT = { 0xbf22ad00, 0x03ea, 0x4690, {0x80, 0x77, 0x47, 0x33, 0x46, 0x20, 0x9b, 0x7e} }; static const GUID DXVA2_ModeMPEG2_VLD = { 0xee27417f, 0x5e28, 0x4e65, {0xbe, 0xea, 0x1d, 0x26, 0xb5, 0x08, 0xad, 0xc9} }; static const GUID DXVA2_ModeH264_A = { 0x1b81be64, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID DXVA2_ModeH264_B = { 0x1b81be65, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID DXVA2_ModeH264_C = { 0x1b81be66, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID DXVA2_ModeH264_D = { 0x1b81be67, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID DXVA2_ModeH264_E = { 0x1b81be68, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID DXVA2_ModeH264_F = { 0x1b81be69, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID DXVADDI_Intel_ModeH264_A = { 0x604F8E64, 0x4951, 0x4c54, {0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6} }; static const GUID DXVADDI_Intel_ModeH264_C = { 0x604F8E66, 0x4951, 0x4c54, {0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6} }; static const GUID DXVADDI_Intel_ModeH264_E = { 0x604F8E68, 0x4951, 0x4c54, {0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6} }; static const GUID DXVA2_ModeWMV8_A = { 0x1b81be80, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID DXVA2_ModeWMV8_B = { 0x1b81be81, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID DXVA2_ModeWMV9_A = { 0x1b81be90, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID DXVA2_ModeWMV9_B = { 0x1b81be91, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID DXVA2_ModeWMV9_C = { 0x1b81be94, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID DXVA2_ModeVC1_A = { 0x1b81beA0, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID DXVA2_ModeVC1_B = { 0x1b81beA1, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID DXVA2_ModeVC1_C = { 0x1b81beA2, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; static const GUID DXVA2_ModeVC1_D = { 0x1b81beA3, 0xa0c7, 0x11d3, {0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5} }; typedef struct { int width; int height; int rate; int rate_base; }hb_dx_format; typedef struct { LPDIRECT3DSURFACE9 d3d; int refcount; unsigned int order; } hb_va_surface_t; typedef struct { uint8_t *base; uint8_t *buffer; size_t size; } hb_copy_cache_t; typedef struct { const char *name; D3DFORMAT format; uint32_t codec; } hb_d3d_format_t; typedef struct { const char *name; const GUID *guid; int codec; } hb_dx_mode_t; typedef struct { char *description; int codec_id; uint32_t i_chroma; int width; int height; HINSTANCE hd3d9_dll; HINSTANCE hdxva2_dll; D3DPRESENT_PARAMETERS d3dpp; LPDIRECT3D9 d3dobj; D3DADAPTER_IDENTIFIER9 d3dai; LPDIRECT3DDEVICE9 d3ddev; UINT token; IDirect3DDeviceManager9 *devmng; HANDLE device; IDirectXVideoDecoderService *vs; GUID input; D3DFORMAT render; DXVA2_ConfigPictureDecode cfg; IDirectXVideoDecoder *decoder; D3DFORMAT output; struct dxva_context hw; unsigned surface_count; unsigned surface_order; int surface_width; int surface_height; uint32_t surface_chroma; hb_va_surface_t surface[VA_DXVA2_MAX_SURFACE_COUNT]; LPDIRECT3DSURFACE9 hw_surface[VA_DXVA2_MAX_SURFACE_COUNT]; IDirectXVideoProcessorService *ps; IDirectXVideoProcessor *vp; int64_t input_pts[2]; int64_t input_dts; int do_job; // running nv12toyuv kernel. cl_kernel nv12toyuv; cl_mem cl_mem_nv12; cl_mem cl_mem_yuv; uint8_t * nv12toyuv_tmp_in; uint8_t * nv12toyuv_tmp_out; } hb_va_dxva2_t; typedef struct FilterLink_T { cl_mem cl_inbuf; cl_mem cl_outbuf; uint8_t *mem_inbuf; uint8_t *mem_outbuf; int width; int height; int linesizeY; int linesizeUV; int inmemdataflag; int outmemdataflag; int incldataflag; int outcldataflag; int framenum; int outputSize; } T_FilterLink; static const hb_d3d_format_t d3d_formats[] = { { "YV12", MAKEFOURCC( 'Y', 'V', '1', '2' ), HB_CODEC_YV12 }, { "NV12", MAKEFOURCC( 'N', 'V', '1', '2' ), HB_CODEC_NV12 }, { NULL, 0, 0 } }; static const hb_dx_mode_t dxva2_modes[] = { { "DXVA2_ModeMPEG2_VLD", &DXVA2_ModeMPEG2_VLD, AV_CODEC_ID_MPEG2VIDEO }, { "DXVA2_ModeMPEG2_MoComp", &DXVA2_ModeMPEG2_MoComp, 0 }, { "DXVA2_ModeMPEG2_IDCT", &DXVA2_ModeMPEG2_IDCT, 0 }, { "H.264 variable-length decoder (VLD), FGT", &DXVA2_ModeH264_F, AV_CODEC_ID_H264 }, { "H.264 VLD, no FGT", &DXVA2_ModeH264_E, AV_CODEC_ID_H264 }, { "H.264 VLD, no FGT (Intel)", &DXVADDI_Intel_ModeH264_E, AV_CODEC_ID_H264 }, { "H.264 IDCT, FGT", &DXVA2_ModeH264_D, 0 }, { "H.264 inverse discrete cosine transform (IDCT), no FGT", &DXVA2_ModeH264_C, 0 }, { "H.264 inverse discrete cosine transform (IDCT), no FGT (Intel)", &DXVADDI_Intel_ModeH264_C, 0 }, { "H.264 MoComp, FGT", &DXVA2_ModeH264_B, 0 }, { "H.264 motion compensation (MoComp), no FGT", &DXVA2_ModeH264_A, 0 }, { "H.264 motion compensation (MoComp), no FGT (Intel)", &DXVADDI_Intel_ModeH264_A, 0 }, { "Windows Media Video 8 MoComp", &DXVA2_ModeWMV8_B, 0 }, { "Windows Media Video 8 post processing", &DXVA2_ModeWMV8_A, 0 }, { "Windows Media Video 9 IDCT", &DXVA2_ModeWMV9_C, 0 }, { "Windows Media Video 9 MoComp", &DXVA2_ModeWMV9_B, 0 }, { "Windows Media Video 9 post processing", &DXVA2_ModeWMV9_A, 0 }, { "VC-1 VLD", &DXVA2_ModeVC1_D, AV_CODEC_ID_VC1 }, { "VC-1 VLD", &DXVA2_ModeVC1_D, AV_CODEC_ID_WMV3 }, { "VC-1 IDCT", &DXVA2_ModeVC1_C, 0 }, { "VC-1 MoComp", &DXVA2_ModeVC1_B, 0 }, { "VC-1 post processing", &DXVA2_ModeVC1_A, 0 }, { NULL, NULL, 0 } }; int hb_va_get_frame_buf( hb_va_dxva2_t *dxva2, AVCodecContext *p_context, AVFrame *frame ); int hb_va_extract( hb_va_dxva2_t *dxva2, uint8_t *dst, AVFrame *frame, int job_w, int job_h, int *crop, hb_oclscale_t *os, int use_opencl, int use_decomb, int use_detelecine ); enum PixelFormat hb_ffmpeg_get_format( AVCodecContext *, const enum PixelFormat * ); hb_va_dxva2_t *hb_va_create_dxva2( hb_va_dxva2_t *dxva2, int codec_id ); void hb_va_new_dxva2( hb_va_dxva2_t *dxva2, AVCodecContext *p_context ); void hb_va_release( hb_va_dxva2_t *dxva2, AVFrame *frame ); void hb_va_close( hb_va_dxva2_t *dxva2 ); int hb_check_hwd_fmt( int fmt ); #endif // HB_VA_DXVA2_H #endif // USE_HWD HandBrake-0.10.2/libhb/qsv_common.h0000664000175200017520000001340212463330511017447 0ustar handbrakehandbrake/* qsv_common.h * * Copyright (c) 2003-2015 HandBrake Team * This file is part of the HandBrake source code. * Homepage: . * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef HB_QSV_COMMON_H #define HB_QSV_COMMON_H #include "msdk/mfxvideo.h" #include "msdk/mfxplugin.h" #include "libavcodec/avcodec.h" /* Minimum Intel Media SDK version (currently 1.3, for Sandy Bridge support) */ #define HB_QSV_MINVERSION_MAJOR AV_QSV_MSDK_VERSION_MAJOR #define HB_QSV_MINVERSION_MINOR AV_QSV_MSDK_VERSION_MINOR /* * Get & store all available Intel Quick Sync information: * * - general availability * - available implementations (hardware-accelerated, software fallback, etc.) * - available codecs, filters, etc. for direct access (convenience) * - supported API version * - supported resolutions */ typedef struct hb_qsv_info_s { // each info struct only corresponds to one CodecId and implementation combo const mfxU32 codec_id; const mfxIMPL implementation; // whether the encoder is available for this implementation int available; // version-specific or hardware-specific capabilities uint64_t capabilities; // support for API 1.6 or later #define HB_QSV_CAP_MSDK_API_1_6 (1LL << 0) // H.264, H.265: B-frames can be used as references #define HB_QSV_CAP_B_REF_PYRAMID (1LL << 1) // optional rate control methods #define HB_QSV_CAP_RATECONTROL_LA (1LL << 10) #define HB_QSV_CAP_RATECONTROL_LAi (1LL << 11) #define HB_QSV_CAP_RATECONTROL_ICQ (1LL << 12) // mfxExtCodingOption2 fields #define HB_QSV_CAP_OPTION2_MBBRC (1LL << 20) #define HB_QSV_CAP_OPTION2_EXTBRC (1LL << 21) #define HB_QSV_CAP_OPTION2_TRELLIS (1LL << 22) #define HB_QSV_CAP_OPTION2_BREFTYPE (1LL << 23) #define HB_QSV_CAP_OPTION2_IB_ADAPT (1LL << 24) #define HB_QSV_CAP_OPTION2_LA_DOWNS (1LL << 25) #define HB_QSV_CAP_OPTION2_NMBSLICE (1LL << 26) // TODO: add maximum encode resolution, etc. } hb_qsv_info_t; /* Intel Quick Sync Video utilities */ int hb_qsv_available(); int hb_qsv_video_encoder_is_enabled(int encoder); int hb_qsv_audio_encoder_is_enabled(int encoder); int hb_qsv_info_init(); void hb_qsv_info_print(); hb_qsv_info_t* hb_qsv_info_get(int encoder); /* Intel Quick Sync Video DECODE utilities */ const char* hb_qsv_decode_get_codec_name(enum AVCodecID codec_id); int hb_qsv_decode_is_enabled(hb_job_t *job); /* * mfxCoreInterface::CopyFrame had a bug preventing us from using it, but * it was fixed in newer drivers - we can use this to determine usability */ int hb_qsv_copyframe_is_slow(int encoder); /* Media SDK parameters handling */ enum { HB_QSV_PARAM_OK, HB_QSV_PARAM_ERROR, HB_QSV_PARAM_BAD_NAME, HB_QSV_PARAM_BAD_VALUE, HB_QSV_PARAM_UNSUPPORTED, }; typedef struct { /* * Supported mfxExtBuffer.BufferId values: * * MFX_EXTBUFF_AVC_REFLIST_CTRL * MFX_EXTBUFF_AVC_TEMPORAL_LAYERS * MFX_EXTBUFF_CODING_OPTION * MFX_EXTBUFF_CODING_OPTION_SPSPPS * MFX_EXTBUFF_CODING_OPTION2 * MFX_EXTBUFF_ENCODER_CAPABILITY * MFX_EXTBUFF_ENCODER_RESET_OPTION * MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION * MFX_EXTBUFF_PICTURE_TIMING_SEI * MFX_EXTBUFF_VIDEO_SIGNAL_INFO * * This should cover all encode-compatible extended * buffers that can be attached to an mfxVideoParam. */ #define HB_QSV_ENC_NUM_EXT_PARAM_MAX 10 mfxExtBuffer* ExtParamArray[HB_QSV_ENC_NUM_EXT_PARAM_MAX]; mfxExtCodingOption codingOption; mfxExtCodingOption2 codingOption2; mfxExtVideoSignalInfo videoSignalInfo; struct { int b_pyramid; int gop_pic_size; int gop_ref_dist; int int_ref_cycle_size; } gop; struct { int icq; int lookahead; int cqp_offsets[3]; int vbv_max_bitrate; int vbv_buffer_size; float vbv_buffer_init; } rc; // assigned via hb_qsv_param_default, may be shared with another structure mfxVideoParam *videoParam; } hb_qsv_param_t; static const char* const hb_qsv_preset_names1[] = { "speed", "balanced", NULL, }; static const char* const hb_qsv_preset_names2[] = { "speed", "balanced", "quality", NULL, }; const char* const* hb_qsv_preset_get_names(); const char* const* hb_qsv_profile_get_names(int encoder); const char* const* hb_qsv_level_get_names(int encoder); const char* hb_qsv_video_quality_get_name(uint32_t codec); void hb_qsv_video_quality_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction); #define HB_QSV_CLIP3(min, max, val) ((val < min) ? min : (val > max) ? max : val) int hb_qsv_codingoption_xlat (int val); const char* hb_qsv_codingoption_get_name(int val); int hb_qsv_trellisvalue_xlat(int val); int hb_qsv_atoindex(const char* const *arr, const char *str, int *err); int hb_qsv_atobool (const char *str, int *err); int hb_qsv_atoi (const char *str, int *err); float hb_qsv_atof (const char *str, int *err); int hb_qsv_param_default_preset(hb_qsv_param_t *param, mfxVideoParam *videoParam, hb_qsv_info_t *info, const char *preset); int hb_qsv_param_default (hb_qsv_param_t *param, mfxVideoParam *videoParam, hb_qsv_info_t *info); int hb_qsv_param_parse (hb_qsv_param_t *param, hb_qsv_info_t *info, const char *key, const char *value); const char* hb_qsv_frametype_name(uint16_t qsv_frametype); uint8_t hb_qsv_frametype_xlat(uint16_t qsv_frametype, uint16_t *out_flags); int hb_qsv_impl_set_preferred(const char *name); const char* hb_qsv_impl_get_name(int impl); void hb_qsv_force_workarounds(); // for developers only #endif HandBrake-0.10.2/libhb/openclkernels.h0000664000175200017520000007474612463330511020154 0ustar handbrakehandbrake/* openclkernels.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html Authors: Peng Gao Li Cao */ #ifndef USE_EXTERNAL_KERNEL #define KERNEL( ... )# __VA_ARGS__ char *kernel_src_hscale = KERNEL ( typedef unsigned char fixed8; /******************************************************************************************************* dst: Horizontal scale destination; src: YUV content in opencl buf; hf_Y: Horizontal filter coefficients for Y planes; hf_UV: Horizontal filter coefficients for UV planes; hi_Y: Horizontal filter index for Y planes; hi_UV: Horizontal filter index for UV planes; stride: Src width; filter_len: Length of filter; ********************************************************************************************************/ kernel void frame_h_scale ( global fixed8 *src, global float *hf_Y, global float *hf_UV, global int *hi_Y, global int *hi_UV, global fixed8 *dst, int stride, //src_width int filter_len ) { int x = get_global_id( 0 ); int y = get_global_id( 1 ); int width = get_global_size( 0 ); int height = get_global_size( 1 ); float result_Y = 0, result_U = 0, result_V = 0; int i = 0; global fixed8 *src_Y = src; global fixed8 *src_U = src_Y + stride * height; global fixed8 *src_V = src_U + (stride >> 1) * (height >> 1); global fixed8 *dst_Y = dst; global fixed8 *dst_U = dst_Y + width * height; global fixed8 *dst_V = dst_U + (width >> 1) * (height >> 1); int xy = y * width + x; global fixed8 *rowdata_Y = src_Y + (y * stride); for( int i = 0; i < filter_len; i++ ) { result_Y += ( hf_Y[x + i * width] * rowdata_Y[hi_Y[x] + i]); } dst_Y[xy] = result_Y; if( y < (height >> 1) && x < (width >> 1) ) { int xy = y * (width >> 1) + x; global fixed8 *rowdata_U = src_U + (y * (stride >> 1)); global fixed8 *rowdata_V = src_V + (y * (stride >> 1)); for( i = 0; i < filter_len; i++ ) { result_U += ( hf_UV[x + i * (width >> 1)] * rowdata_U[hi_UV[x] + i]); result_V += ( hf_UV[x + i * (width >> 1)] * rowdata_V[hi_UV[x] + i]); } dst_U[xy] = result_U; dst_V[xy] = result_V; } } ); /******************************************************************************************************* dst: Vertical scale destination; src: YUV content in opencl buf; hf_Y: Vertical filter coefficients for Y planes; hf_UV: Vertical filter coefficients for UV planes; hi_Y: Vertical filter index for Y planes; hi_UV: Vertical filter index for UV planes; stride: Src height; filter_len: Length of filter; ********************************************************************************************************/ char *kernel_src_vscale = KERNEL ( kernel void frame_v_scale ( global fixed8 *src, global float *vf_Y, global float *vf_UV, global int *vi_Y, global int *vi_UV, global fixed8 *dst, int src_height, int filter_len ) { int x = get_global_id( 0 ); int y = get_global_id( 1 ); int width = get_global_size( 0 ); int height = get_global_size( 1 ); float result_Y = 0, result_U = 0, result_V = 0; int i = 0; global fixed8 *src_Y = src; global fixed8 *src_U = src_Y + src_height * width; global fixed8 *src_V = src_U + (src_height >> 1) * (width >> 1); global fixed8 *dst_Y = dst; global fixed8 *dst_U = dst_Y + height * width; global fixed8 *dst_V = dst_U + (height >> 1) * (width >> 1); int xy = y * width + x; for( i = 0; i < filter_len; i++ ) { result_Y += vf_Y[y + i * height] * src_Y[(vi_Y[y] + i) * width + x]; } dst_Y[xy] = result_Y; if( y < (height >> 1) && x < (width >> 1) ) { int xy = y * (width >> 1) + x; for( i = 0; i < filter_len; i++ ) { result_U += vf_UV[y + i * (height >> 1)] * src_U[(vi_UV[y] + i) * (width >> 1) + x]; result_V += vf_UV[y + i * (height >> 1)] * src_V[(vi_UV[y] + i) * (width >> 1) + x]; } dst_U[xy] = result_U; dst_V[xy] = result_V; } } ); /******************************************************************************************************* input: Input buffer; output: Output buffer; w: Width of frame; h: Height of frame; ********************************************************************************************************/ char *kernel_src_nvtoyuv = KERNEL ( kernel void nv12toyuv ( global char *input, global char* output, int w, int h ) { int x = get_global_id( 0 ); int y = get_global_id( 1 ); int idx = y * (w >> 1) + x; vstore4((vload4( 0, input + (idx << 2))), 0, output + (idx << 2)); //Y char2 uv = vload2( 0, input + (idx << 1) + w * h ); output[idx + w * h] = uv.s0; output[idx + w * h + ((w * h) >> 2)] = uv.s1; } ); /******************************************************************************************************* dst: Horizontal scale destination; src: YUV content in opencl buf; yfilter: Opencl memory of horizontal filter coefficients for luma/alpha planes; yfilterPos: Opencl memory of horizontal filter starting positions for each dst[i] for luma/alpha planes; yfilterSize: Horizontal filter size for luma/alpha pixels; cfilter: Opencl memory of horizontal filter coefficients for chroma planes; cfilterPos: Opencl memory of horizontal filter starting positions for each dst[i] for chroma planes; cfilterSize: Horizontal filter size for chroma pixels; dstStride: Width of destination luma/alpha planes; dstChrStride: Width of destination chroma planes; ********************************************************************************************************/ char *kernel_src_hscaleall = KERNEL ( kernel void hscale_all_opencl ( global short *dst, const global unsigned char *src, const global short *yfilter, const global int *yfilterPos, int yfilterSize, const global short *cfilter, const global int *cfilterPos, int cfilterSize, int dstWidth, int dstHeight, int srcWidth, int srcHeight, int dstStride, int dstChrStride, int srcStride, int srcChrStride) { int w = get_global_id(0); int h = get_global_id(1); int chrWidth = get_global_size(0); int chrHeight = get_global_size(1); int srcPos1 = h * srcStride + yfilterPos[w]; int srcPos2 = h * srcStride + yfilterPos[w + chrWidth]; int srcPos3 = (h + (srcHeight >> 1)) * srcStride + yfilterPos[w]; int srcPos4 = (h + (srcHeight >> 1)) * srcStride + yfilterPos[w + chrWidth]; int srcc1Pos = srcStride * srcHeight + (h) * (srcChrStride) + cfilterPos[w]; int srcc2Pos = srcc1Pos + ((srcChrStride)*(chrHeight)); int val1 = 0; int val2 = 0; int val3 = 0; int val4 = 0; int val5 = 0; int val6 = 0; int filterPos1 = yfilterSize * w; int filterPos2 = yfilterSize * (w + chrWidth); int cfilterPos1 = cfilterSize * w; int j; for (j = 0; j < yfilterSize; j++) { val1 += src[srcPos1 + j] * yfilter[filterPos1+ j]; val2 += src[srcPos2 + j] * yfilter[filterPos2 + j]; val3 += src[srcPos3 + j] * yfilter[filterPos1 + j]; val4 += src[srcPos4 + j] * yfilter[filterPos2 + j]; val5 += src[srcc1Pos+j] * cfilter[cfilterPos1 + j]; val6 += src[srcc2Pos+j] * cfilter[cfilterPos1 + j]; } int dstPos1 = h *dstStride; int dstPos2 = (h + chrHeight) * dstStride; dst[dstPos1 + w] = ((val1 >> 7) > ((1 << 15) - 1) ? ((1 << 15) - 1) : (val1 >> 7)); dst[dstPos1 + w + chrWidth] = ((val2 >> 7) > ((1 << 15) - 1) ? ((1 << 15) - 1) : (val2 >> 7)); dst[dstPos2 + w] = ((val3 >> 7) > ((1 << 15) - 1) ? ((1 << 15) - 1) : (val3 >> 7)); dst[dstPos2 + w + chrWidth] = ((val4 >> 7) > ((1 << 15) - 1) ? ((1 << 15) - 1) : (val4 >> 7)); int dstPos3 = h * (dstChrStride) + w + dstStride * dstHeight; int dstPos4 = h * (dstChrStride) + w + dstStride * dstHeight + ((dstChrStride) * chrHeight); dst[dstPos3] = ((val5 >> 7) > ((1 << 15) - 1) ? ((1 << 15) - 1) : (val5 >> 7)); dst[dstPos4] = ((val6 >> 7) > ((1 << 15) - 1) ? ((1 << 15) - 1) : (val6 >> 7)); } ); char *kernel_src_hscalefast = KERNEL ( kernel void hscale_fast_opencl ( global short *dst, const global unsigned char *src, int xInc, int chrXInc, int dstWidth, int dstHeight, int srcWidth, int srcHeight, int dstStride, int dstChrStride, int srcStride, int srcChrStride) { int w = get_global_id(0); int h = get_global_id(1); int chrWidth = get_global_size(0); int chrHeight = get_global_size(1); int xpos1 = 0; int xpos2 = 0; int xx = xpos1 >> 16; int xalpha = (xpos1 & 0xFFFF) >> 9; dst[h * dstStride + w] = (src[h * srcStride + xx] << 7) + (src[h * srcStride + xx + 1] -src[h * srcStride + xx]) * xalpha; int lowpart = h + (chrHeight); dst[lowpart * dstStride + w] = (src[lowpart * srcStride + xx] << 7) + (src[lowpart * srcStride + xx + 1] - src[lowpart * srcStride + xx]) * xalpha; int inv_i = w * xInc >> 16; if( inv_i >= srcWidth - 1) { dst[h*dstStride + w] = src[h*srcStride + srcWidth-1]*128; dst[lowpart*dstStride + w] = src[lowpart*srcStride + srcWidth - 1] * 128; } int rightpart = w + (chrWidth); xx = xpos2 >> 16; xalpha = (xpos2 & 0xFFFF) >> 9; dst[h * dstStride + rightpart] = (src[h *srcStride + xx] << 7) + (src[h * srcStride + xx + 1] - src[h * srcStride + xx]) * xalpha; dst[lowpart * dstStride + rightpart] = (src[lowpart * srcStride + xx] << 7) + (src[lowpart * srcStride + xx + 1] - src[lowpart * srcStride + xx]) * xalpha; inv_i = rightpart * xInc >> 16; if( inv_i >= srcWidth - 1) { dst[h * dstStride + rightpart] = src[h * srcStride + srcWidth - 1] * 128; dst[lowpart * dstStride + rightpart] = src[lowpart * srcStride + srcWidth - 1] * 128; } int xpos = 0; xpos = chrXInc * w; xx = xpos >> 16; xalpha = (xpos & 0xFFFF) >> 9; src += srcStride * srcHeight; dst += dstStride * dstHeight; dst[h * (dstChrStride) + w] = (src[h * (srcChrStride) + xx] * (xalpha^127) + src[h * (srcChrStride) + xx + 1] * xalpha); inv_i = w * xInc >> 16; if( inv_i >= (srcWidth >> 1) - 1) { dst[h * (dstChrStride) + w] = src[h * (srcChrStride) + (srcWidth >> 1) -1]*128; } xpos = chrXInc * (w); xx = xpos >> 16; src += srcChrStride * srcHeight >> 1; dst += (dstChrStride * chrHeight); dst[h * (dstChrStride) + w] = (src[h * (srcChrStride) + xx] * (xalpha^127) + src[h * (srcChrStride) + xx + 1 ] * xalpha); if( inv_i >= (srcWidth >> 1) - 1) { //v channel: dst[h * (dstChrStride) + w] = src[h * (srcChrStride) + (srcWidth >> 1) -1] * 128; } } ); char *kernel_src_vscalealldither = KERNEL ( kernel void vscale_all_dither_opencl ( global unsigned char *dst, const global short *src, const global short *yfilter, int yfilterSize, const global short *cfilter, int cfilterSize, const global int *yfilterPos, const global int *cfilterPos, int dstWidth, int dstHeight, int srcWidth, int srcHeight, int dstStride, int dstChrStride, int srcStride, int srcChrStride) { const unsigned char hb_dither_8x8_128[8][8] = { { 36, 68, 60, 92, 34, 66, 58, 90, }, { 100, 4, 124, 28, 98, 2, 122, 26, }, { 52, 84, 44, 76, 50, 82, 42, 74, }, { 116, 20, 108, 12, 114, 18, 106, 10, }, { 32, 64, 56, 88, 38, 70, 62, 94, }, { 96, 0, 120, 24, 102, 6, 126, 30, }, { 48, 80, 40, 72, 54, 86, 46, 78, }, { 112, 16, 104, 8, 118, 22, 110, 14, }, }; int w = get_global_id(0); int h = get_global_id(1); int chrWidth = get_global_size(0); int chrHeight = get_global_size(1); const unsigned char *local_up_dither; const unsigned char *local_down_dither; local_up_dither = hb_dither_8x8_128[h & 7]; local_down_dither = hb_dither_8x8_128[(h + chrHeight) & 7]; //yscale; int srcPos1 = (yfilterPos[h]) * srcStride + w; int srcPos2 = (yfilterPos[h]) * srcStride + w + (chrWidth); int srcPos3 = (yfilterPos[h + chrHeight]) * srcStride + w; int srcPos4 = (yfilterPos[h + chrHeight]) * srcStride + w + chrWidth; int src1Pos = dstStride * srcHeight + (cfilterPos[h]) * dstChrStride + (w); int src2Pos = dstStride * srcHeight + (dstChrStride*(srcHeight>>1)) + (cfilterPos[h]) * dstChrStride + w; int val1 = (local_up_dither[w & 7] << 12); //y offset is 0; int val2 = (local_up_dither[(w + chrWidth) & 7] << 12); int val3 = (local_down_dither[w &7] << 12); int val4 = (local_down_dither[(w + chrWidth) & 7] << 12); int val5 = (local_up_dither[w & 7] << 12); int val6 = (local_up_dither[(w + 3) & 7] << 12); // 3 is offset of the chrome channel. int j; int filterPos1 = h * yfilterSize; int filterPos2 = ( h + chrHeight ) * yfilterSize; for(j = 0; j < yfilterSize; j++) { val1 += src[srcPos1] * yfilter[filterPos1 + j]; srcPos1 += srcStride; val2 += src[srcPos2] * yfilter[filterPos1 + j]; srcPos2 += srcStride; val3 += src[srcPos3] * yfilter[filterPos2 + j]; srcPos3 += srcStride; val4 += src[srcPos4] * yfilter[filterPos2 + j]; srcPos4 += srcStride; val5 += src[src1Pos] * cfilter[filterPos1 + j]; val6 += src[src2Pos] * cfilter[filterPos1 + j]; src1Pos += dstChrStride; src2Pos += dstChrStride; } dst[h * dstStride + w] = (((val1 >> 19)&(~0xFF)) ? ((-(val1 >> 19)) >> 31) : (val1 >> 19)); dst[h * dstStride + w + chrWidth] = (((val2 >> 19)&(~0xFF)) ? ((-(val2 >> 19)) >> 31) : (val2 >> 19)); dst[(h + chrHeight) * dstStride + w] = (((val3 >> 19)&(~0xFF)) ? ((-(val3 >> 19)) >> 31) : (val3 >> 19)); dst[(h + chrHeight) * dstStride + w + chrWidth] = (((val4 >> 19)&(~0xFF)) ? ((-(val4 >> 19)) >> 31) : (val4 >> 19)); int dst1Pos = dstStride * dstHeight + h*(dstChrStride)+(w); int dst2Pos = (dstChrStride * chrHeight) + dst1Pos; dst[dst1Pos] = (((val5 >> 19)&(~0xFF)) ? ((-(val5 >> 19)) >> 31) : (val5 >> 19)); dst[dst2Pos] = (((val6 >> 19)&(~0xFF)) ? ((-(val6 >> 19)) >> 31) : (val6 >> 19)); } ); char *kernel_src_vscaleallnodither = KERNEL ( kernel void vscale_all_nodither_opencl ( global unsigned char *dst, const global short *src, const global short *yfilter, int yfilterSize, const global short *cfilter, int cfilterSize, const global int *yfilterPos, const global int *cfilterPos, int dstWidth, int dstHeight, int srcWidth, int srcHeight, int dstStride, int dstChrStride, int srcStride, int srcChrStride) { const unsigned char hb_sws_pb_64[8] = { 64, 64, 64, 64, 64, 64, 64, 64 }; int w = get_global_id(0); int h = get_global_id(1); int chrWidth = get_global_size(0); int chrHeight = get_global_size(1); const unsigned char *local_up_dither; const unsigned char *local_down_dither; local_up_dither = hb_sws_pb_64; local_down_dither = hb_sws_pb_64; //yscale; int srcPos1 = (yfilterPos[h]) * srcStride + w; int srcPos2 = (yfilterPos[h]) * srcStride + w + (chrWidth); int srcPos3 = (yfilterPos[h + chrHeight]) * srcStride + w; int srcPos4 = (yfilterPos[h + chrHeight]) * srcStride + w + chrWidth; int src1Pos = dstStride * srcHeight + (cfilterPos[h]) * dstChrStride + (w); int src2Pos = dstStride * srcHeight + (dstChrStride*(srcHeight>>1)) + (cfilterPos[h]) * dstChrStride + w; int val1 = (local_up_dither[w & 7] << 12); //y offset is 0; int val2 = (local_up_dither[(w + chrWidth) & 7] << 12); int val3 = (local_down_dither[w &7] << 12); int val4 = (local_down_dither[(w + chrWidth) & 7] << 12); int val5 = (local_up_dither[w & 7] << 12); int val6 = (local_up_dither[(w + 3) & 7] << 12); // 3 is offset of the chrome channel. int j; int filterPos1 = h * yfilterSize; int filterPos2 = ( h + chrHeight ) * yfilterSize; for(j = 0; j < yfilterSize; j++) { val1 += src[srcPos1] * yfilter[filterPos1 + j]; srcPos1 += srcStride; val2 += src[srcPos2] * yfilter[filterPos1 + j]; srcPos2 += srcStride; val3 += src[srcPos3] * yfilter[filterPos2 + j]; srcPos3 += srcStride; val4 += src[srcPos4] * yfilter[filterPos2 + j]; srcPos4 += srcStride; val5 += src[src1Pos] * cfilter[filterPos1 + j]; val6 += src[src2Pos] * cfilter[filterPos1 + j]; src1Pos += dstChrStride; src2Pos += dstChrStride; } dst[h * dstStride + w] = (((val1 >> 19)&(~0xFF)) ? ((-(val1 >> 19)) >> 31) : (val1 >> 19)); dst[h * dstStride + w + chrWidth] = (((val2 >> 19)&(~0xFF)) ? ((-(val2 >> 19)) >> 31) : (val2 >> 19)); dst[(h + chrHeight) * dstStride + w] = (((val3 >> 19)&(~0xFF)) ? ((-(val3 >> 19)) >> 31) : (val3 >> 19)); dst[(h + chrHeight) * dstStride + w + chrWidth] = (((val4 >> 19)&(~0xFF)) ? ((-(val4 >> 19)) >> 31) : (val4 >> 19));; int dst1Pos = dstStride * dstHeight + h * (dstChrStride) + (w); int dst2Pos = (dstChrStride * chrHeight) + dst1Pos; dst[dst1Pos] = (((val5 >> 19)&(~0xFF)) ? ((-(val5 >> 19)) >> 31) : (val5 >> 19)); dst[dst2Pos] = (((val6 >> 19)&(~0xFF)) ? ((-(val6 >> 19)) >> 31) : (val6 >> 19)); } ); char *kernel_src_vscalefast = KERNEL ( kernel void vscale_fast_opencl ( global unsigned char *dst, const global short *src, const global int *yfilterPos, const global int *cfilterPos, int dstWidth, int dstHeight, int srcWidth, int srcHeight, int dstStride, int dstChrStride, int srcStride, int srcChrStride) { const unsigned char hb_sws_pb_64[8] = { 64, 64, 64, 64, 64, 64, 64, 64 }; int w = get_global_id(0); int h = get_global_id(1); int chrWidth = get_global_size(0); int chrHeight = get_global_size(1); const unsigned char *local_up_dither; const unsigned char *local_down_dither; local_up_dither = hb_sws_pb_64; local_down_dither = hb_sws_pb_64; int rightpart = w + chrWidth; int bh = h + chrHeight; // bottom part short val1 = (src[(yfilterPos[h]) * dstStride + w] + local_up_dither[(w + 0) & 7]) >> 7; //lum offset is 0; short val2 = (src[(yfilterPos[h]) * dstStride + rightpart] + local_up_dither[rightpart & 7]) >> 7; short val3 = (src[(yfilterPos[bh]) * dstStride + w] + local_down_dither[w & 7]) >> 7; short val4 = (src[(yfilterPos[bh]) * dstStride + rightpart] + local_down_dither[rightpart & 7]) >> 7; dst[h * dstStride + w] = ((val1&(~0xFF)) ? ((-val1) >> 31) : (val1)); dst[h * dstStride + rightpart] = ((val2&(~0xFF)) ? ((-val2) >> 31) : (val2)); dst[bh * dstStride + w] = ((val3&(~0xFF)) ? ((-val3) >> 31) : (val3)); dst[bh * dstStride + rightpart] = ((val4&(~0xFF)) ? ((-val4) >> 31) : (val4)); src += dstStride * srcHeight; dst += dstStride * dstHeight; val1 = (src[cfilterPos[h] * (dstChrStride) + w] + local_up_dither[ w & 7]) >> 7; dst[h * (dstChrStride) + w] = ((val1&(~0xFF)) ? ((-val1) >> 31) : (val1)); src += dstChrStride * (srcHeight >> 1); dst += dstChrStride * chrHeight; val1 = (src[cfilterPos[h] * dstChrStride + w] + local_up_dither[ (w + 3) & 7] ) >> 7; dst[h * dstChrStride + w] = ((val1&(~0xFF)) ? ((-val1) >> 31) : (val1)); } ); char *kernel_src_scale = KERNEL ( __kernel __attribute__((reqd_work_group_size(64, 1, 1))) void frame_scale(__global uchar *dst, __global const uchar *src, const float xscale, const float yscale, const int srcPlaneOffset0, const int srcPlaneOffset1, const int srcPlaneOffset2, const int dstPlaneOffset0, const int dstPlaneOffset1, const int dstPlaneOffset2, const int srcRowWords0, const int srcRowWords1, const int srcRowWords2, const int dstRowWords0, const int dstRowWords1, const int dstRowWords2, const int srcWidth, const int srcHeight, const int dstWidth, const int dstHeight, __global const float4* restrict xweights, __global const float4* restrict yweights ) { const int x = get_global_id(0); const int y = get_global_id(1); const int z = get_global_id(2); // Abort work items outside the dst image bounds. if ((get_group_id(0) * 64 >= (dstWidth >> ((z == 0) ? 0 : 1))) || (get_group_id(1) * 16 >= (dstHeight >> ((z == 0) ? 0 : 1)))) return; const int srcPlaneOffset = (z == 0) ? srcPlaneOffset0 : ((z == 1) ? srcPlaneOffset1 : srcPlaneOffset2); const int dstPlaneOffset = (z == 0) ? dstPlaneOffset0 : ((z == 1) ? dstPlaneOffset1 : dstPlaneOffset2); const int srcRowWords = (z == 0) ? srcRowWords0: ((z == 1) ? srcRowWords1 : srcRowWords2); const int dstRowWords = (z == 0) ? dstRowWords0: ((z == 1) ? dstRowWords1 : dstRowWords2); __local uchar pixels[64 * 36]; const int localRowPixels = 64; const int groupHeight = 16; // src pixel height output by the workgroup const int ypad = 2; const int localx = get_local_id(0); const int globalStartRow = floor((get_group_id(1) * groupHeight) / yscale); const int globalRowCount = ceil(groupHeight / yscale) + 2 * ypad; float4 weights = xweights[x]; int4 woffs = floor(x / xscale); woffs += (int4)(-1, 0, 1, 2); woffs = clamp(woffs, 0, (srcWidth >> ((z == 0) ? 0 : 1)) - 1); const int maxy = (srcHeight >> ((z == 0) ? 0 : 1)) - 1; // Scale x from global into LDS for (int i = 0; i <= globalRowCount; ++i) { int4 offs = srcPlaneOffset + clamp(globalStartRow - ypad + i, 0, maxy) * srcRowWords; offs += woffs; pixels[localx + i * localRowPixels] = convert_uchar(clamp(round(dot(weights, (float4)(src[offs.x], src[offs.y], src[offs.z], src[offs.w]))), 0.0f, 255.0f)); } barrier(CLK_LOCAL_MEM_FENCE); // Scale y from LDS into global if (x >= dstWidth >> ((z == 0) ? 0 : 1)) return; int off = dstPlaneOffset + x + (get_group_id(1) * groupHeight) * dstRowWords; for (int i = 0; i < groupHeight; ++i) { if (y >= dstHeight >> ((z == 0) ? 0 : 1)) break; int localy = floor((get_group_id(1) * groupHeight + i) / yscale); localy = localy - globalStartRow + ypad; int loff = localx + localy * localRowPixels; dst[off] = convert_uchar(clamp(round(dot(yweights[get_group_id(1) * groupHeight + i], (float4)(pixels[loff - localRowPixels], pixels[loff], pixels[loff + localRowPixels] , pixels[loff + localRowPixels * 2]))), 0.0f, 255.0f)); off += dstRowWords; } } ); char *kernel_src_yadif_filter = KERNEL( void filter_v6( global unsigned char *dst, global unsigned char *prev, global unsigned char *cur, global unsigned char *next, int x, int y, int width, int height, int parity, int inlinesize, int outlinesize, int inmode, int uvflag ) { int flag = uvflag * (y >=height) * height; int prefs = select(-(inlinesize), inlinesize,((y+1) - flag) >1; int e = cur[index + prefs]; int temporal_diff0 = abs((prev2[index]) - (next2[index])); int temporal_diff1 =(abs(prev[index + mrefs] - c) + abs(prev[index + prefs] - e) )>>1; int temporal_diff2 =(abs(next[index + mrefs] - c) + abs(next[index + prefs] - e) )>>1; int diff = max(max(temporal_diff0>>1, temporal_diff1), temporal_diff2); int spatial_pred = (c+e)>>1; int spatial_score = abs(cur[index + mrefs-1] - cur[index + prefs-1]) + abs(c-e) + abs(cur[index + mrefs+1] - cur[index + prefs+1]) - 1; //check -1 score = abs(cur[index + mrefs-2] - cur[index + prefs]) + abs(cur[index + mrefs-1] - cur[index + prefs+1]) + abs(cur[index + mrefs] - cur[index + prefs+2]); if (score < spatial_score) { spatial_score= score; spatial_pred= (cur[index + mrefs-1] + cur[index + prefs+1])>>1; } //check -2 score = abs(cur[index + mrefs-3] - cur[index + prefs+1]) + abs(cur[index + mrefs-2] - cur[index + prefs+2]) + abs(cur[index + mrefs-1] - cur[index + prefs+3]); if (score < spatial_score) { spatial_score= score; spatial_pred= (cur[index + mrefs-2] + cur[index + prefs+2])>>1; } //check 1 score = abs(cur[index + mrefs] - cur[index + prefs-2]) + abs(cur[index + mrefs+1] - cur[index + prefs-1]) + abs(cur[index + mrefs+2] - cur[index + prefs]); if (score < spatial_score) { spatial_score= score; spatial_pred= (cur[index + mrefs+1] + cur[index + prefs-1])>>1; } //check 2 score = abs(cur[index + mrefs+1] - cur[index + prefs-3]) + abs(cur[index + mrefs+2] - cur[index + prefs-2]) + abs(cur[index + mrefs+3] - cur[index + prefs-1]); if (score < spatial_score) { spatial_score= score; spatial_pred= (cur[index + mrefs+2] + cur[index + prefs-2])>>1; } if (mode < 2) { int b = (prev2[index + (mrefs<<1)] + next2[index + (mrefs<<1)])>>1; int f = (prev2[index + (prefs<<1)] + next2[index + (prefs<<1)])>>1; int diffmax = max(max(d-e, d-c), min(b-c, f-e)); int diffmin = min(min(d-e, d-c), max(b-c, f-e)); diff = max(max(diff, diffmin), -diffmax); } if (spatial_pred > d + diff) { spatial_pred = d + diff; } else if (spatial_pred < d - diff) { spatial_pred = d - diff; } dst[outindex] = spatial_pred; } kernel void yadif_filter( global unsigned char *dst, global unsigned char *prev, global unsigned char *cur, global unsigned char *next, int parity, int inlinesizeY, int inlinesizeUV, int outlinesizeY, int outlinesizeUV, int mode) { int x=get_global_id(0); int y=(get_global_id(1)<<1) + (!parity); int width=(get_global_size(0)<<1)/3; int height=get_global_size(1)<<1; global unsigned char *dst_Y=dst; global unsigned char *dst_U=dst_Y+height*outlinesizeY; global unsigned char *prev_Y=prev; global unsigned char *prev_U=prev_Y+height*inlinesizeY; global unsigned char *cur_Y=cur; global unsigned char *cur_U=cur_Y+height*inlinesizeY; global unsigned char *next_Y=next; global unsigned char *next_U=next_Y+height*inlinesizeY; if(x < width) { filter_v6(dst_Y,prev_Y,cur_Y,next_Y,x,y,width,height,parity,inlinesizeY,outlinesizeY,mode,0); } else { x = x - width; filter_v6(dst_U,prev_U,cur_U,next_U,x,y,width>>1,height>>1,parity,inlinesizeUV,outlinesizeUV,mode,1); } } ); #endif HandBrake-0.10.2/libhb/decssasub.h0000664000175200017520000000210012463330511017233 0ustar handbrakehandbrake/* decssasub.h * * Copyright (c) 2003-2015 HandBrake Team * This file is part of the HandBrake source code * Homepage: . * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef __DECSSASUB_H__ #define __DECSSASUB_H__ typedef struct { uint32_t flags; uint32_t fg_rgb; // forground color uint32_t alt_rgb; // secondary color uint32_t ol_rgb; // outline color uint32_t bg_rgb; // background color uint32_t fg_alpha; // forground alpha uint32_t alt_alpha; // secondary alpha uint32_t ol_alpha; // outline alpha uint32_t bg_alpha; // background alpha } hb_subtitle_style_t; #define HB_STYLE_FLAG_ITALIC 0x0001 #define HB_STYLE_FLAG_BOLD 0x0002 #define HB_STYLE_FLAG_UNDERLINE 0x0004 char * hb_ssa_to_text(char *in, int *consumed, hb_subtitle_style_t *style); void hb_ssa_style_init(hb_subtitle_style_t *style); #endif // __DECSSASUB_H__ HandBrake-0.10.2/libhb/ports.h0000664000175200017520000001237412463330511016444 0ustar handbrakehandbrake/* ports.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef HB_PORTS_H #define HB_PORTS_H #if defined(_WIN32) #define DIR_SEP_STR "\\" #define DIR_SEP_CHAR '\\' #define IS_DIR_SEP(c) (c == '\\' || c == '/') #else #define DIR_SEP_STR "/" #define DIR_SEP_CHAR '/' #define IS_DIR_SEP(c) (c == '/') #endif /************************************************************************ * CPU info utilities ***********************************************************************/ enum hb_cpu_platform { // list of microarchitecture codenames HB_CPU_PLATFORM_UNSPECIFIED = 0, HB_CPU_PLATFORM_INTEL_BNL, HB_CPU_PLATFORM_INTEL_SNB, HB_CPU_PLATFORM_INTEL_IVB, HB_CPU_PLATFORM_INTEL_SLM, HB_CPU_PLATFORM_INTEL_HSW, }; int hb_get_cpu_count(); int hb_get_cpu_platform(); const char* hb_get_cpu_name(); const char* hb_get_cpu_platform_name(); /************************************************************************ * Utils ***********************************************************************/ // provide time in ms uint64_t hb_get_date(); // provide time in us uint64_t hb_get_time_us(); void hb_snooze( int delay ); int hb_platform_init(); #ifdef SYS_MINGW char *strtok_r(char *s, const char *delim, char **save_ptr); #endif #ifdef SYS_MINGW typedef struct { _WDIR *wdir; struct dirent entry; } HB_DIR; #else typedef DIR HB_DIR; #endif #ifdef SYS_MINGW typedef struct _stat64 hb_stat_t; #else typedef struct stat hb_stat_t; #endif HB_DIR* hb_opendir(char *path); int hb_closedir(HB_DIR *dir); void hb_rewinddir(HB_DIR *dir); struct dirent * hb_readdir(HB_DIR *dir); int hb_mkdir(char * name); int hb_stat(const char *path, hb_stat_t *sb); FILE * hb_fopen(const char *path, const char *mode); char * hb_strr_dir_sep(const char *path); #ifdef __LIBHB__ // Convert utf8 string to current code page. char * hb_utf8_to_cp(const char *src); /* Everything from now is only used internally and hidden to the UI */ /************************************************************************ * DVD utils ***********************************************************************/ int hb_dvd_region(char *device, int *region_mask); /************************************************************************ * File utils ***********************************************************************/ void hb_get_temporary_directory( char path[512] ); void hb_get_tempory_filename( hb_handle_t *, char name[1024], char * fmt, ... ); /************************************************************************ * Threads ***********************************************************************/ typedef struct hb_thread_s hb_thread_t; #if defined( SYS_BEOS ) # define HB_LOW_PRIORITY 5 # define HB_NORMAL_PRIORITY 10 #elif defined( SYS_DARWIN ) # define HB_LOW_PRIORITY 0 # define HB_NORMAL_PRIORITY 31 #elif defined( SYS_LINUX ) || defined( SYS_FREEBSD ) || defined ( SYS_SunOS ) || defined ( __FreeBSD_kernel__ ) # define HB_LOW_PRIORITY 0 # define HB_NORMAL_PRIORITY 0 #elif defined( SYS_CYGWIN ) # define HB_LOW_PRIORITY 0 # define HB_NORMAL_PRIORITY 1 #elif defined( SYS_MINGW ) # define HB_LOW_PRIORITY 0 # define HB_NORMAL_PRIORITY 0 #endif typedef void (thread_func_t)(void *); hb_thread_t * hb_thread_init( const char * name, thread_func_t *function, void * arg, int priority ); void hb_thread_close( hb_thread_t ** ); int hb_thread_has_exited( hb_thread_t * ); /************************************************************************ * Mutexes ***********************************************************************/ hb_lock_t * hb_lock_init(); void hb_lock_close( hb_lock_t ** ); void hb_lock( hb_lock_t * ); void hb_unlock( hb_lock_t * ); /************************************************************************ * Condition variables ***********************************************************************/ typedef struct hb_cond_s hb_cond_t; hb_cond_t * hb_cond_init(); void hb_cond_wait( hb_cond_t *, hb_lock_t * ); void hb_cond_timedwait( hb_cond_t * c, hb_lock_t * lock, int msec ); void hb_cond_signal( hb_cond_t * ); void hb_cond_broadcast( hb_cond_t * c ); void hb_cond_close( hb_cond_t ** ); /************************************************************************ * Network ***********************************************************************/ typedef struct hb_net_s hb_net_t; hb_net_t * hb_net_open( char * address, int port ); 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_private_enable(void *opaque); void hb_system_sleep_private_disable(void *opaque); #endif /* __LIBHB__ */ #endif HandBrake-0.10.2/libhb/openclwrapper.c0000664000175200017520000010213512463330511020144 0ustar handbrakehandbrake/* openclwrapper.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html Authors: Peng Gao Li Cao */ #include #include #include #include "extras/cl.h" #include "opencl.h" #include "openclwrapper.h" #include "openclkernels.h" //#define USE_EXTERNAL_KERNEL #ifdef SYS_MINGW #include #endif #if defined(_MSC_VER) #define strcasecmp strcmpi #endif #define MAX_KERNEL_STRING_LEN 64 #define MAX_CLFILE_NUM 50 #define MAX_CLKERNEL_NUM 200 #define MAX_CLFILE_PATH 255 #define MAX_KERNEL_NUM 50 #define MAX_KERNEL_NAME_LEN 64 #ifndef INVALID_HANDLE_VALUE #define INVALID_HANDLE_VALUE NULL #endif //#define THREAD_PRIORITY_TIME_CRITICAL 15 enum VENDOR { AMD = 0, Intel, NVIDIA, others }; typedef struct _GPUEnv { //share vb in all modules in hb library cl_platform_id platform; cl_device_type dType; cl_context context; cl_device_id * devices; cl_device_id dev; cl_command_queue command_queue; cl_kernel kernels[MAX_CLFILE_NUM]; cl_program programs[MAX_CLFILE_NUM]; //one program object maps one kernel source file char kernelSrcFile[MAX_CLFILE_NUM][256]; //the max len of kernel file name is 256 int file_count; // only one kernel file char kernel_names[MAX_CLKERNEL_NUM][MAX_KERNEL_STRING_LEN+1]; cl_kernel_function kernel_functions[MAX_CLKERNEL_NUM]; int kernel_count; int isUserCreated; // 1: created , 0:no create and needed to create by opencl wrapper enum VENDOR vendor; }GPUEnv; typedef struct { char kernelName[MAX_KERNEL_NAME_LEN+1]; char * kernelStr; }hb_kernel_node; static GPUEnv gpu_env; static int isInited = 0; static int useBuffers = 0; static hb_kernel_node gKernels[MAX_KERNEL_NUM]; #define HB_OCL_ADD_KERNEL_CFG(idx, s, p) \ { \ strcpy(gKernels[idx].kernelName, s); \ gKernels[idx].kernelStr = p; \ strcpy(gpu_env.kernel_names[idx], s); \ gpu_env.kernel_count++; \ } /** * hb_regist_opencl_kernel */ int hb_regist_opencl_kernel() { //if( !gpu_env.isUserCreated ) // memset( &gpu_env, 0, sizeof(gpu_env) ); //Comment for posterity: When in doubt just zero out a structure full of pointers to allocated resources. gpu_env.file_count = 0; //argc; gpu_env.kernel_count = 0UL; HB_OCL_ADD_KERNEL_CFG(0, "frame_scale", NULL); HB_OCL_ADD_KERNEL_CFG(1, "yadif_filter", NULL); return 0; } /** * hb_regist_opencl_kernel * @param filename - * @param source - * @param gpu_info - * @param int idx - */ int hb_convert_to_string( const char *filename, char **source, GPUEnv *gpu_info, int idx ) { int file_size; size_t result; FILE * file = NULL; file_size = 0; result = 0; file = fopen( filename, "rb+" ); if( file!=NULL ) { fseek( file, 0, SEEK_END ); file_size = ftell( file ); rewind( file ); *source = (char*)malloc( sizeof(char) * file_size + 1 ); if( *source == (char*)NULL ) { return(0); } result = fread( *source, 1, file_size, file ); if( result != file_size ) { free( *source ); return(0); } (*source)[file_size] = '\0'; fclose( file ); return(1); } return(0); } /** * hb_binary_generated * @param context - * @param cl_file_name - * @param fhandle - */ int hb_binary_generated( cl_context context, const char * cl_file_name, FILE ** fhandle ) { int i = 0; cl_int status; cl_uint numDevices; cl_device_id *devices; char * str = NULL; FILE * fd = NULL; if (hb_ocl == NULL) { hb_error("hb_binary_generated: OpenCL support not available"); return 0; } status = hb_ocl->clGetContextInfo(context, CL_CONTEXT_NUM_DEVICES, sizeof(numDevices), &numDevices, NULL); if( status != CL_SUCCESS ) { hb_log( "OpenCL: Get context info failed" ); return 0; } devices = (cl_device_id*)malloc( sizeof(cl_device_id) * numDevices ); if( devices == NULL ) { hb_log( "OpenCL: No device found" ); return 0; } /* grab the handles to all of the devices in the context. */ status = hb_ocl->clGetContextInfo(context, CL_CONTEXT_DEVICES, sizeof(cl_device_id) * numDevices, devices, NULL); status = 0; /* dump out each binary into its own separate file. */ for (i = 0; i < numDevices; i++) { char fileName[256] = { 0 }; char cl_name[128] = { 0 }; if (devices[i]) { char deviceName[1024]; status = hb_ocl->clGetDeviceInfo(devices[i], CL_DEVICE_NAME, sizeof(deviceName), deviceName, NULL); str = (char*)strstr(cl_file_name, ".cl"); memcpy(cl_name, cl_file_name, str - cl_file_name); cl_name[str - cl_file_name] = '\0'; sprintf(fileName, "./%s - %s.bin", cl_name, deviceName); fd = fopen(fileName, "rb"); status = fd != NULL; } } if( devices != NULL ) { free( devices ); devices = NULL; } if( fd != NULL ) *fhandle = fd; return status; } /** * hb_write_binary_to_file * @param fileName - * @param birary - * @param numBytes - */ int hb_write_binary_to_file( const char* fileName, const char* birary, size_t numBytes ) { FILE *output = NULL; output = fopen( fileName, "wb" ); if( output == NULL ) return 0; fwrite( birary, sizeof(char), numBytes, output ); fclose( output ); return 1; } /** * hb_generat_bin_from_kernel_source * @param program - * @param cl_file_name - */ int hb_generat_bin_from_kernel_source( cl_program program, const char * cl_file_name ) { int i = 0; cl_int status; cl_uint numDevices; size_t *binarySizes; cl_device_id *devices; char **binaries; char *str = NULL; if (hb_ocl == NULL) { hb_error("hb_generat_bin_from_kernel_source: OpenCL support not available"); return 0; } status = hb_ocl->clGetProgramInfo(program, CL_PROGRAM_NUM_DEVICES, sizeof(numDevices), &numDevices, NULL); if( status != CL_SUCCESS ) { hb_log("OpenCL: hb_generat_bin_from_kernel_source: clGetProgramInfo for CL_PROGRAM_NUM_DEVICES failed"); return 0; } devices = (cl_device_id*)malloc( sizeof(cl_device_id) * numDevices ); if( devices == NULL ) { hb_log("OpenCL: hb_generat_bin_from_kernel_source: no device found"); return 0; } /* grab the handles to all of the devices in the program. */ status = hb_ocl->clGetProgramInfo(program, CL_PROGRAM_DEVICES, sizeof(cl_device_id) * numDevices, devices, NULL); if( status != CL_SUCCESS ) { hb_log("OpenCL: hb_generat_bin_from_kernel_source: clGetProgramInfo for CL_PROGRAM_DEVICES failed"); return 0; } /* figure out the sizes of each of the binaries. */ binarySizes = (size_t*)malloc( sizeof(size_t) * numDevices ); status = hb_ocl->clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES, sizeof(size_t) * numDevices, binarySizes, NULL); if( status != CL_SUCCESS ) { hb_log("OpenCL: hb_generat_bin_from_kernel_source: clGetProgramInfo for CL_PROGRAM_BINARY_SIZES failed"); return 0; } /* copy over all of the generated binaries. */ binaries = (char**)malloc( sizeof(char *) * numDevices ); if( binaries == NULL ) { hb_log("OpenCL: hb_generat_bin_from_kernel_source: malloc for binaries failed"); return 0; } for( i = 0; i < numDevices; i++ ) { if( binarySizes[i] != 0 ) { binaries[i] = (char*)malloc( sizeof(char) * binarySizes[i] ); if( binaries[i] == NULL ) { hb_log("OpenCL: hb_generat_bin_from_kernel_source: malloc for binaries[%d] failed", i); return 0; } } else { binaries[i] = NULL; } } status = hb_ocl->clGetProgramInfo(program, CL_PROGRAM_BINARIES, sizeof(char *) * numDevices, binaries, NULL); if( status != CL_SUCCESS ) { hb_log("OpenCL: hb_generat_bin_from_kernel_source: clGetProgramInfo for CL_PROGRAM_BINARIES failed"); return 0; } /* dump out each binary into its own separate file. */ for (i = 0; i < numDevices; i++) { char fileName[256] = {0}; char cl_name[128] = {0}; if (binarySizes[i]) { char deviceName[1024]; status = hb_ocl->clGetDeviceInfo(devices[i], CL_DEVICE_NAME, sizeof(deviceName), deviceName, NULL); str = (char*)strstr( cl_file_name, (char*)".cl" ); memcpy(cl_name, cl_file_name, str - cl_file_name); cl_name[str - cl_file_name] = '\0'; sprintf(fileName, "./%s - %s.bin", cl_name, deviceName); if (!hb_write_binary_to_file(fileName, binaries[i], binarySizes[i])) { hb_log("OpenCL: hb_generat_bin_from_kernel_source: unable to write kernel, writing to temporary directory instead."); return 0; } } } // Release all resouces and memory for( i = 0; i < numDevices; i++ ) { if( binaries[i] != NULL ) { free( binaries[i] ); binaries[i] = NULL; } } if( binaries != NULL ) { free( binaries ); binaries = NULL; } if( binarySizes != NULL ) { free( binarySizes ); binarySizes = NULL; } if( devices != NULL ) { free( devices ); devices = NULL; } return 1; } /** * hb_init_opencl_attr * @param env - */ int hb_init_opencl_attr( OpenCLEnv * env ) { if( gpu_env.isUserCreated ) return 1; gpu_env.context = env->context; gpu_env.platform = env->platform; gpu_env.dev = env->devices; gpu_env.command_queue = env->command_queue; gpu_env.isUserCreated = 1; return 0; } /** * hb_create_kernel * @param kernelname - * @param env - */ int hb_create_kernel( char * kernelname, KernelEnv * env ) { int status; if (hb_ocl == NULL) { hb_error("hb_create_kernel: OpenCL support not available"); return 0; } env->kernel = hb_ocl->clCreateKernel(gpu_env.programs[0], kernelname, &status); env->context = gpu_env.context; env->command_queue = gpu_env.command_queue; return status != CL_SUCCESS ? 1 : 0; } /** * hb_release_kernel * @param env - */ int hb_release_kernel( KernelEnv * env ) { if (hb_ocl == NULL) { hb_error("hb_release_kernel: OpenCL support not available"); return 0; } int status = hb_ocl->clReleaseKernel(env->kernel); return status != CL_SUCCESS ? 1 : 0; } /** * hb_init_opencl_env * @param gpu_info - */ static int init_once = 0; int hb_init_opencl_env( GPUEnv *gpu_info ) { size_t length; cl_int status; cl_uint numPlatforms, numDevices; cl_platform_id *platforms; cl_context_properties cps[3]; char platformName[100]; unsigned int i; void *handle = INVALID_HANDLE_VALUE; if (init_once != 0) return 0; else init_once = 1; if (hb_ocl == NULL) { hb_error("hb_init_opencl_env: OpenCL support not available"); return 1; } /* * Have a look at the available platforms. */ if( !gpu_info->isUserCreated ) { status = hb_ocl->clGetPlatformIDs(0, NULL, &numPlatforms); if( status != CL_SUCCESS ) { hb_log( "OpenCL: OpenCL device platform not found." ); return(1); } gpu_info->platform = NULL; if( 0 < numPlatforms ) { platforms = (cl_platform_id*)malloc( numPlatforms * sizeof(cl_platform_id)); if( platforms == (cl_platform_id*)NULL ) { return(1); } status = hb_ocl->clGetPlatformIDs(numPlatforms, platforms, NULL); if( status != CL_SUCCESS ) { hb_log( "OpenCL: Specific opencl platform not found." ); return(1); } for( i = 0; i < numPlatforms; i++ ) { status = hb_ocl->clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR, sizeof(platformName), platformName, NULL); if( status != CL_SUCCESS ) { continue; } gpu_info->platform = platforms[i]; if (!strcmp(platformName, "Advanced Micro Devices, Inc.") || !strcmp(platformName, "AMD")) gpu_info->vendor = AMD; else gpu_info->vendor = others; gpu_info->platform = platforms[i]; status = hb_ocl->clGetDeviceIDs(gpu_info->platform /* platform */, CL_DEVICE_TYPE_GPU /* device_type */, 0 /* num_entries */, NULL /* devices */, &numDevices); if( status != CL_SUCCESS ) { continue; } if( numDevices ) break; } free( platforms ); } if( NULL == gpu_info->platform ) { hb_log( "OpenCL: No OpenCL-compatible GPU found." ); return(1); } if( status != CL_SUCCESS ) { hb_log( "OpenCL: No OpenCL-compatible GPU found." ); return(1); } /* * Use available platform. */ cps[0] = CL_CONTEXT_PLATFORM; cps[1] = (cl_context_properties)gpu_info->platform; cps[2] = 0; /* Check for GPU. */ gpu_info->dType = CL_DEVICE_TYPE_GPU; gpu_info->context = hb_ocl->clCreateContextFromType(cps, gpu_info->dType, NULL, NULL, &status); if( (gpu_info->context == (cl_context)NULL) || (status != CL_SUCCESS) ) { gpu_info->dType = CL_DEVICE_TYPE_CPU; gpu_info->context = hb_ocl->clCreateContextFromType(cps, gpu_info->dType, NULL, NULL, &status); } if( (gpu_info->context == (cl_context)NULL) || (status != CL_SUCCESS) ) { gpu_info->dType = CL_DEVICE_TYPE_DEFAULT; gpu_info->context = hb_ocl->clCreateContextFromType(cps, gpu_info->dType, NULL, NULL, &status); } if( (gpu_info->context == (cl_context)NULL) || (status != CL_SUCCESS) ) { hb_log( "OpenCL: Unable to create opencl context." ); return(1); } /* Detect OpenCL devices. */ /* First, get the size of device list data */ status = hb_ocl->clGetContextInfo(gpu_info->context, CL_CONTEXT_DEVICES, 0, NULL, &length); if((status != CL_SUCCESS) || (length == 0)) { hb_log( "OpenCL: Unable to get the list of devices in context." ); return(1); } /* Now allocate memory for device list based on the size we got earlier */ gpu_info->devices = (cl_device_id*)malloc( length ); if( gpu_info->devices == (cl_device_id*)NULL ) { return(1); } /* Now, get the device list data */ status = hb_ocl->clGetContextInfo(gpu_info->context, CL_CONTEXT_DEVICES, length, gpu_info->devices, NULL); if( status != CL_SUCCESS ) { hb_log( "OpenCL: Unable to get the device list data in context." ); return(1); } /* Create OpenCL command queue. */ gpu_info->command_queue = hb_ocl->clCreateCommandQueue(gpu_info->context, gpu_info->devices[0], 0, &status); if( status != CL_SUCCESS ) { hb_log( "OpenCL: Unable to create opencl command queue." ); return(1); } } if ((CL_SUCCESS == hb_ocl->clGetCommandQueueInfo(gpu_info->command_queue, CL_QUEUE_THREAD_HANDLE_AMD, sizeof(handle), &handle, NULL)) && (INVALID_HANDLE_VALUE != handle)) { #ifdef SYS_MINGW SetThreadPriority( handle, THREAD_PRIORITY_TIME_CRITICAL ); #endif } return 0; } /** * hb_release_opencl_env * @param gpu_info - */ int hb_release_opencl_env( GPUEnv *gpu_info ) { if( !isInited ) return 1; int i; if (hb_ocl == NULL) { hb_error("hb_release_opencl_env: OpenCL support not available"); return 0; } for( i = 0; iclReleaseProgram(gpu_env.programs[i]); gpu_env.programs[i] = NULL; } } if( gpu_env.command_queue ) { hb_ocl->clReleaseCommandQueue(gpu_env.command_queue); gpu_env.command_queue = NULL; } if( gpu_env.context ) { hb_ocl->clReleaseContext(gpu_env.context); gpu_env.context = NULL; } isInited = 0; gpu_info->isUserCreated = 0; return 1; } /** * hb_register_kernel_wrapper * @param kernel_name - * @param function - */ int hb_register_kernel_wrapper( const char *kernel_name, cl_kernel_function function ) { int i; for( i = 0; i < gpu_env.kernel_count; i++ ) { if( strcasecmp( kernel_name, gpu_env.kernel_names[i] ) == 0 ) { gpu_env.kernel_functions[i] = function; return(1); } } return(0); } /** * hb_cached_of_kerner_prg * @param gpu_env - * @param cl_file_name - */ int hb_cached_of_kerner_prg( const GPUEnv *gpu_env, const char * cl_file_name ) { int i; for( i = 0; i < gpu_env->file_count; i++ ) { if( strcasecmp( gpu_env->kernelSrcFile[i], cl_file_name ) == 0 ) { if( gpu_env->programs[i] != NULL ) return(1); } } return(0); } /** * hb_compile_kernel_file * @param filename - * @param gpu_info - * @param indx - * @param build_option - */ int hb_compile_kernel_file( const char *filename, GPUEnv *gpu_info, int indx, const char *build_option ) { cl_int status; size_t length; char *source_str; const char *source; size_t source_size[1]; char *buildLog = NULL; int b_error, binary_status, binaryExisted; char * binary; cl_uint numDevices; cl_device_id *devices; FILE * fd; FILE * fd1; int idx; if( hb_cached_of_kerner_prg( gpu_info, filename ) == 1 ) return (1); idx = gpu_info->file_count; #ifdef USE_EXTERNAL_KERNEL status = hb_convert_to_string( filename, &source_str, gpu_info, idx ); if( status == 0 ) return(0); #else int kernel_src_size = strlen(kernel_src_scale) + strlen(kernel_src_yadif_filter); // char *scale_src; // status = hb_convert_to_string("./scale_kernels.cl", &scale_src, gpu_info, idx); // if (status != 0) // kernel_src_size += strlen(scale_src); source_str = (char*)malloc( kernel_src_size + 2 ); strcpy( source_str, kernel_src_scale ); // strcat( source_str, scale_src ); // strcat( source_str, kernel_src_yadif_filter ); #endif source = source_str; source_size[0] = strlen( source ); if (hb_ocl == NULL) { hb_error("hb_compile_kernel_file: OpenCL support not available"); return 0; } if ((binaryExisted = hb_binary_generated(gpu_info->context, filename, &fd)) == 1) { status = hb_ocl->clGetContextInfo(gpu_info->context, CL_CONTEXT_NUM_DEVICES, sizeof(numDevices), &numDevices, NULL); if (status != CL_SUCCESS) { hb_log("OpenCL: Unable to get the number of devices in context."); return 0; } devices = (cl_device_id*)malloc(sizeof(cl_device_id) * numDevices); if (devices == NULL) return 0; length = 0; b_error = 0; b_error |= fseek(fd, 0, SEEK_END) < 0; b_error |= (length = ftell(fd)) <= 0; b_error |= fseek(fd, 0, SEEK_SET) < 0; if (b_error) return 0; binary = (char*)calloc(length + 2, sizeof(char)); if (binary == NULL) return 0; b_error |= fread(binary, 1, length, fd) != length; #if 0 // this doesn't work under OS X and/or with some non-AMD GPUs if (binary[length-1] != '\n') binary[length++] = '\n'; #endif if (b_error) return 0; /* grab the handles to all of the devices in the context. */ status = hb_ocl->clGetContextInfo(gpu_info->context, CL_CONTEXT_DEVICES, sizeof(cl_device_id) * numDevices, devices, NULL); gpu_info->programs[idx] = hb_ocl->clCreateProgramWithBinary(gpu_info->context, numDevices, devices, &length, (const unsigned char**)&binary, &binary_status, &status); fclose(fd); free(devices); fd = NULL; devices = NULL; } else { /* create a CL program using the kernel source */ gpu_info->programs[idx] = hb_ocl->clCreateProgramWithSource(gpu_info->context, 1, &source, source_size, &status); } if((gpu_info->programs[idx] == (cl_program)NULL) || (status != CL_SUCCESS)){ hb_log( "OpenCL: Unable to get list of devices in context." ); return(0); } /* create a cl program executable for all the devices specified */ if( !gpu_info->isUserCreated ) { status = hb_ocl->clBuildProgram(gpu_info->programs[idx], 1, gpu_info->devices, build_option, NULL, NULL); } else { status = hb_ocl->clBuildProgram(gpu_info->programs[idx], 1, &(gpu_info->dev), build_option, NULL, NULL); } if( status != CL_SUCCESS ) { if( !gpu_info->isUserCreated ) { status = hb_ocl->clGetProgramBuildInfo(gpu_info->programs[idx], gpu_info->devices[0], CL_PROGRAM_BUILD_LOG, 0, NULL, &length); } else { status = hb_ocl->clGetProgramBuildInfo(gpu_info->programs[idx], gpu_info->dev, CL_PROGRAM_BUILD_LOG, 0, NULL, &length); } if( status != CL_SUCCESS ) { hb_log( "OpenCL: Unable to get GPU build information." ); return(0); } buildLog = (char*)malloc( length ); if( buildLog == (char*)NULL ) { return(0); } if( !gpu_info->isUserCreated ) { status = hb_ocl->clGetProgramBuildInfo(gpu_info->programs[idx], gpu_info->devices[0], CL_PROGRAM_BUILD_LOG, length, buildLog, &length); } else { status = hb_ocl->clGetProgramBuildInfo(gpu_info->programs[idx], gpu_info->dev, CL_PROGRAM_BUILD_LOG, length, buildLog, &length); } fd1 = fopen( "kernel-build.log", "w+" ); if( fd1 != NULL ) { fwrite( buildLog, sizeof(char), length, fd1 ); fclose( fd1 ); } free( buildLog ); return(0); } strcpy( gpu_env.kernelSrcFile[idx], filename ); if (binaryExisted != 1) { //hb_generat_bin_from_kernel_source(gpu_env.programs[idx], filename); } gpu_info->file_count += 1; return(1); } /** * hb_get_kernel_env_and_func * @param kernel_name - * @param env - * @param function - */ int hb_get_kernel_env_and_func( const char *kernel_name, KernelEnv *env, cl_kernel_function *function ) { int i; for( i = 0; i < gpu_env.kernel_count; i++ ) { if( strcasecmp( kernel_name, gpu_env.kernel_names[i] ) == 0 ) { env->context = gpu_env.context; env->command_queue = gpu_env.command_queue; env->program = gpu_env.programs[0]; env->kernel = gpu_env.kernels[i]; env->isAMD = ( gpu_env.vendor == AMD ) ? 1 : 0; *function = gpu_env.kernel_functions[i]; return(1); } } return(0); } /** * hb_get_kernel_env_and_func * @param kernel_name - * @param userdata - */ int hb_run_kernel( const char *kernel_name, void **userdata ) { KernelEnv env; cl_kernel_function function; int status; memset( &env, 0, sizeof(KernelEnv)); status = hb_get_kernel_env_and_func( kernel_name, &env, &function ); strcpy( env.kernel_name, kernel_name ); if( status == 1 ) { return(function( userdata, &env )); } return(0); } /** * hb_init_opencl_run_env * @param argc - * @param argv - * @param build_option - */ int hb_init_opencl_run_env( int argc, char **argv, const char *build_option ) { int status = 0; if( MAX_CLKERNEL_NUM <= 0 ) { return 1; } if((argc > MAX_CLFILE_NUM) || (argc<0)) { return 1; } if( !isInited ) { hb_regist_opencl_kernel(); /*initialize devices, context, comand_queue*/ status = hb_init_opencl_env( &gpu_env ); if( status ) return(1); /*initialize program, kernel_name, kernel_count*/ status = hb_compile_kernel_file("hb-opencl-kernels.cl", &gpu_env, 0, build_option); if( status == 0 || gpu_env.kernel_count == 0 ) { return(1); } useBuffers = 1; isInited = 1; } return(0); } /** * hb_release_opencl_run_env */ int hb_release_opencl_run_env() { return hb_release_opencl_env( &gpu_env ); } /** * hb_opencl_stats */ int hb_opencl_stats() { return isInited; } /** * hb_get_opencl_env */ int hb_get_opencl_env() { /* initialize devices, context, command_queue */ return hb_init_opencl_env(&gpu_env); } /** * hb_create_buffer * @param cl_inBuf - * @param flags - * @param size - */ int hb_create_buffer( cl_mem *cl_Buf, int flags, int size ) { int status; if (hb_ocl == NULL) { hb_error("hb_create_buffer: OpenCL support not available"); return 0; } *cl_Buf = hb_ocl->clCreateBuffer(gpu_env.context, flags, size, NULL, &status); if( status != CL_SUCCESS ) { hb_log( "OpenCL: clCreateBuffer error '%d'", status ); return 0; } return 1; } /** * hb_read_opencl_buffer * @param cl_inBuf - * @param outbuf - * @param size - */ int hb_read_opencl_buffer( cl_mem cl_inBuf, unsigned char *outbuf, int size ) { int status; if (hb_ocl == NULL) { hb_error("hb_read_opencl_suffer: OpenCL support not available"); return 0; } status = hb_ocl->clEnqueueReadBuffer(gpu_env.command_queue, cl_inBuf, CL_TRUE, 0, size, outbuf, 0, 0, 0); if( status != CL_SUCCESS ) { hb_log( "OpenCL: av_read_opencl_buffer error '%d'", status ); return 0; } return 1; } int hb_cl_create_mapped_buffer(cl_mem *mem, unsigned char **addr, int size) { int status; int flags = CL_MEM_ALLOC_HOST_PTR; if (hb_ocl == NULL) { hb_error("hb_cl_create_mapped_buffer: OpenCL support not available"); return 0; } //cl_event event; *mem = hb_ocl->clCreateBuffer(gpu_env.context, flags, size, NULL, &status); *addr = hb_ocl->clEnqueueMapBuffer(gpu_env.command_queue, *mem, CL_TRUE, CL_MAP_READ|CL_MAP_WRITE, 0, size, 0, NULL, NULL/*&event*/, &status); //hb_log("\t **** context: %.8x cmdqueue: %.8x cl_mem: %.8x mapaddr: %.8x size: %d status: %d", gpu_env.context, gpu_env.command_queue, mem, addr, size, status); return (status == CL_SUCCESS) ? 1 : 0; } int hb_cl_free_mapped_buffer(cl_mem mem, unsigned char *addr) { cl_event event; if (hb_ocl == NULL) { hb_error("hb_cl_free_mapped_buffer: OpenCL support not available"); return 0; } int status = hb_ocl->clEnqueueUnmapMemObject(gpu_env.command_queue, mem, addr, 0, NULL, &event); if (status == CL_SUCCESS) hb_ocl->clWaitForEvents(1, &event); else hb_log("hb_free_mapped_buffer: error %d", status); return (status == CL_SUCCESS) ? 1 : 0; } void hb_opencl_init() { hb_get_opencl_env(); } int hb_use_buffers() { return useBuffers; } int hb_copy_buffer(cl_mem src_buffer,cl_mem dst_buffer,size_t src_offset,size_t dst_offset,size_t cb) { if (hb_ocl == NULL) { hb_error("hb_copy_buffer: OpenCL support not available"); return 0; } int status = hb_ocl->clEnqueueCopyBuffer(gpu_env.command_queue, src_buffer, dst_buffer, src_offset, dst_offset, cb, 0, 0, 0); if( status != CL_SUCCESS ) { av_log(NULL,AV_LOG_ERROR, "hb_read_opencl_buffer error '%d'\n", status ); return 0; } return 1; } int hb_read_opencl_frame_buffer(cl_mem cl_inBuf,unsigned char *Ybuf,unsigned char *Ubuf,unsigned char *Vbuf,int linesize0,int linesize1,int linesize2,int height) { int chrH = -(-height >> 1); unsigned char *temp = (unsigned char *)av_malloc(sizeof(uint8_t) * (linesize0 * height + linesize1 * chrH * 2)); if(hb_read_opencl_buffer(cl_inBuf,temp,sizeof(uint8_t)*(linesize0 + linesize1)*height)) { memcpy(Ybuf,temp,linesize0 * height); memcpy(Ubuf,temp + linesize0 * height,linesize1 *chrH); memcpy(Vbuf,temp + linesize0 * height + linesize1 * chrH,linesize2 * chrH); } av_free(temp); return 1; } int hb_write_opencl_frame_buffer(cl_mem cl_inBuf,unsigned char *Ybuf,unsigned char *Ubuf,unsigned char *Vbuf,int linesize0,int linesize1,int linesize2,int height,int offset) { if (hb_ocl == NULL) { hb_error("hb_write_opencl_frame_buffer: OpenCL support not available"); return 0; } void *mapped = hb_ocl->clEnqueueMapBuffer(gpu_env.command_queue, cl_inBuf, CL_TRUE,CL_MAP_WRITE, 0, sizeof(uint8_t) * (linesize0 + linesize1) * height + offset, 0, NULL, NULL, NULL); uint8_t *temp = (uint8_t *)mapped; temp += offset; memcpy(temp,Ybuf,sizeof(uint8_t) * linesize0 * height); memcpy(temp + sizeof(uint8_t) * linesize0 * height,Ubuf,sizeof(uint8_t) * linesize1 * height/2); memcpy(temp + sizeof(uint8_t) * (linesize0 * height + linesize1 * height/2),Vbuf,sizeof(uint8_t) * linesize2 * height/2); hb_ocl->clEnqueueUnmapMemObject(gpu_env.command_queue, cl_inBuf, mapped, 0, NULL, NULL); return 1; } cl_command_queue hb_get_command_queue() { return gpu_env.command_queue; } cl_context hb_get_context() { return gpu_env.context; } HandBrake-0.10.2/libhb/colormap.h0000664000175200017520000000121112463330511017075 0ustar handbrakehandbrake/* colormap.h * * Copyright (c) 2003-2015 HandBrake Team * This file is part of the HandBrake source code * Homepage: . * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef HB_COLORMAP_H #define HB_COLORMAP_H #define HB_RGB_TO_BGR(c) (((c & 0xff0000) >> 16) | \ ((c & 0x00ff00) ) | \ ((c & 0x0000ff) << 16)) #define HB_BGR_TO_RGB(c) HB_RGB_TO_BGR(c) uint32_t hb_rgb_lookup_by_name(const char *color); #endif // HB_COLORMAP_H HandBrake-0.10.2/libhb/decomb.c0000664000175200017520000025443412533617362016540 0ustar handbrakehandbrake/* decomb.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html The yadif algorithm was created by Michael Niedermayer. Tritical's work inspired much of the comb detection code: http://web.missouri.edu/~kes25c/ */ /***** Parameters: Mode : Spatial metric : Motion thresh : Spatial thresh : Mask Filter Mode : Block thresh : Block width : Block height Appended for EEDI2: Magnitude thresh : Variance thresh : Laplacian thresh : Dilation thresh : Erosion thresh : Noise thresh : Max search distance : Post-processing Plus: Parity Defaults: 391:2:3:3:2:40:16:16:10:20:20:4:2:50:24:1:-1 Original "Faster" settings: 7:2:6:9:1:80:16:16:10:20:20:4:2:50:24:1:-1 *****/ #define MODE_YADIF 1 // Use yadif #define MODE_BLEND 2 // Use blending interpolation #define MODE_CUBIC 4 // Use cubic interpolation #define MODE_EEDI2 8 // Use EEDI2 interpolation #define MODE_MASK 32 // Output combing masks instead of pictures #define MODE_BOB 64 // Deinterlace each field to a separate frame #define MODE_GAMMA 128 // Scale gamma when decombing #define MODE_FILTER 256 // Filter combing mask #define MODE_COMPOSITE 512 // Overlay combing mask onto picture #define FILTER_CLASSIC 1 #define FILTER_ERODE_DILATE 2 /***** These modes can be layered. For example, Yadif (1) + EEDI2 (8) = 9, which will feed EEDI2 interpolations to yadif. ** Working combos: 1: Just yadif 2: Just blend 3: Switch between yadif and blend 4: Just cubic interpolate 5: Cubic->yadif 6: Switch between cubic and blend 7: Switch between cubic->yadif and blend 8: Just EEDI2 interpolate 9: EEDI2->yadif 10: Switch between EEDI2 and blend 11: Switch between EEDI2->yadif and blend ...okay I'm getting bored now listing all these different modes 32: Passes through the combing mask for every combed frame (white for combed pixels, otherwise black) 33+: Overlay the combing mask for every combed frame on top of the filtered output (white for combed pixels) 12-15: EEDI2 will override cubic interpolation *****/ #include "hb.h" #include "hbffmpeg.h" #include "eedi2.h" #include "taskset.h" #define PARITY_DEFAULT -1 #define ABS(a) ((a) > 0 ? (a) : (-(a))) #define MIN3(a,b,c) MIN(MIN(a,b),c) #define MAX3(a,b,c) MAX(MAX(a,b),c) // Some names to correspond to the pv->eedi_half array's contents #define SRCPF 0 #define MSKPF 1 #define TMPPF 2 #define DSTPF 3 // Some names to correspond to the pv->eedi_full array's contents #define DST2PF 0 #define TMP2PF2 1 #define MSK2PF 2 #define TMP2PF 3 #define DST2MPF 4 struct yadif_arguments_s { hb_buffer_t *dst; int parity; int tff; int is_combed; }; typedef struct yadif_arguments_s yadif_arguments_t; typedef struct eedi2_thread_arg_s { hb_filter_private_t *pv; int plane; } eedi2_thread_arg_t; typedef struct decomb_thread_arg_s { hb_filter_private_t *pv; int segment; int segment_start[3]; int segment_height[3]; } decomb_thread_arg_t; typedef struct yadif_thread_arg_s { hb_filter_private_t *pv; int segment; int segment_start[3]; int segment_height[3]; } yadif_thread_arg_t; struct hb_filter_private_s { // Decomb parameters int mode; int filter_mode; int spatial_metric; int motion_threshold; int spatial_threshold; int block_threshold; int block_width; int block_height; int * block_score; int comb_check_complete; int comb_check_nthreads; int skip_comb_check; int is_combed; float gamma_lut[256]; // EEDI2 parameters int magnitude_threshold; int variance_threshold; int laplacian_threshold; int dilation_threshold; int erosion_threshold; int noise_threshold; int maximum_search_distance; int post_processing; int parity; int tff; int yadif_ready; int deinterlaced_frames; int blended_frames; int unfiltered_frames; hb_buffer_t * ref[3]; /* Make buffers to store a comb masks. */ hb_buffer_t * mask; hb_buffer_t * mask_filtered; hb_buffer_t * mask_temp; int mask_box_x; int mask_box_y; uint8_t mask_box_color; hb_buffer_t * eedi_half[4]; hb_buffer_t * eedi_full[5]; int * cx2; int * cy2; int * cxy; int * tmpc; int cpu_count; int segment_height[3]; taskset_t yadif_taskset; // Threads for Yadif - one per CPU yadif_arguments_t *yadif_arguments; // Arguments to thread for work taskset_t decomb_filter_taskset; // Threads for comb detection taskset_t decomb_check_taskset; // Threads for comb check taskset_t mask_filter_taskset; // Threads for decomb mask filter taskset_t mask_erode_taskset; // Threads for decomb mask erode taskset_t mask_dilate_taskset; // Threads for decomb mask dilate taskset_t eedi2_taskset; // Threads for eedi2 - one per plane }; typedef struct { int tap[5]; int normalize; } filter_param_t; static int hb_decomb_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int hb_decomb_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static void hb_decomb_close( hb_filter_object_t * filter ); hb_filter_object_t hb_filter_decomb = { .id = HB_FILTER_DECOMB, .enforce_order = 1, .name = "Decomb", .settings = NULL, .init = hb_decomb_init, .work = hb_decomb_work, .close = hb_decomb_close, }; // Borrowed from libav #define times4(x) x, x, x, x #define times1024(x) times4(times4(times4(times4(times4(x))))) static const uint8_t hb_crop_table[256 + 2 * 1024] = { times1024(0x00), 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, times1024(0xFF) }; static inline int cubic_interpolate_pixel( int y0, int y1, int y2, int y3 ) { /* From http://www.neuron2.net/library/cubicinterp.html */ int result = ( y0 * -3 ) + ( y1 * 23 ) + ( y2 * 23 ) + ( y3 * -3 ); result = hb_crop_table[(result / 40) + 1024]; return result; } static void cubic_interpolate_line( uint8_t *dst, uint8_t *cur, int width, int height, int stride, int y) { int w = width; int x; for( x = 0; x < w; x++) { int a, b, c, d; a = b = c = d = 0; if( y >= 3 ) { /* Normal top*/ a = cur[-3*stride]; b = cur[-stride]; } else if( y == 2 || y == 1 ) { /* There's only one sample above this pixel, use it twice. */ a = cur[-stride]; b = cur[-stride]; } else if( y == 0 ) { /* No samples above, triple up on the one below. */ a = cur[+stride]; b = cur[+stride]; } if( y <= ( height - 4 ) ) { /* Normal bottom*/ c = cur[+stride]; d = cur[3*stride]; } else if( y == ( height - 3 ) || y == ( height - 2 ) ) { /* There's only one sample below, use it twice. */ c = cur[+stride]; d = cur[+stride]; } else if( y == height - 1) { /* No samples below, triple up on the one above. */ c = cur[-stride]; d = cur[-stride]; } dst[0] = cubic_interpolate_pixel( a, b, c, d ); dst++; cur++; } } static void draw_mask_box( hb_filter_private_t * pv ) { int x = pv->mask_box_x; int y = pv->mask_box_y; int box_width = pv->block_width; int box_height = pv->block_height; int stride; uint8_t * mskp; if (pv->mode & MODE_FILTER) { mskp = pv->mask_filtered->plane[0].data; stride = pv->mask_filtered->plane[0].stride; } else { mskp = pv->mask->plane[0].data; stride = pv->mask->plane[0].stride; } int block_x, block_y; for( block_x = 0; block_x < box_width; block_x++) { mskp[y*stride+x+block_x] = 128; mskp[(y+box_height)*stride+x+block_x] = 128; } for( block_y = 0; block_y < box_height; block_y++) { mskp[stride*(y+block_y)+x] = 128; mskp[stride*(y+block_y) + x + box_width] = 128; } } static void apply_mask_line( uint8_t * srcp, uint8_t * mskp, int width ) { int x; for( x = 0; x < width; x++ ) { if( mskp[x] == 1 ) { srcp[x] = 255; } if( mskp[x] == 128 ) { srcp[x] = 128; } } } static void apply_mask(hb_filter_private_t * pv, hb_buffer_t * b) { /* draw_boxes */ draw_mask_box( pv ); int pp, yy; hb_buffer_t * m; if (pv->mode & MODE_FILTER) { m = pv->mask_filtered; } else { m = pv->mask; } for (pp = 0; pp < 3; pp++) { uint8_t * dstp = b->plane[pp].data; uint8_t * mskp = m->plane[pp].data; for( yy = 0; yy < m->plane[pp].height; yy++ ) { if (!(pv->mode & MODE_COMPOSITE) && pp == 0) { memcpy(dstp, mskp, m->plane[pp].width); } else if (!(pv->mode & MODE_COMPOSITE)) { memset(dstp, 128, m->plane[pp].width); } if (pp == 0) { apply_mask_line(dstp, mskp, m->plane[pp].width); } dstp += b->plane[pp].stride; mskp += m->plane[pp].stride; } } } static void store_ref(hb_filter_private_t * pv, hb_buffer_t * b) { hb_buffer_close(&pv->ref[0]); memmove(&pv->ref[0], &pv->ref[1], sizeof(hb_buffer_t *) * 2 ); pv->ref[2] = b; } static inline int blend_filter_pixel(filter_param_t *filter, int up2, int up1, int current, int down1, int down2) { /* Low-pass 5-tap filter */ int result = 0; result += up2 * filter->tap[0]; result += up1 * filter->tap[1]; result += current * filter->tap[2]; result += down1 * filter->tap[3]; result += down2 * filter->tap[4]; result >>= filter->normalize; result = hb_crop_table[result + 1024]; return result; } static void blend_filter_line(filter_param_t *filter, uint8_t *dst, uint8_t *cur, int width, int height, int stride, int y) { int w = width; int x; int up1, up2, down1, down2; if (y > 1 && y < (height - 2)) { up1 = -1 * stride; up2 = -2 * stride; down1 = 1 * stride; down2 = 2 * stride; } else if (y == 0) { /* First line, so A and B don't exist.*/ up1 = up2 = 0; down1 = 1 * stride; down2 = 2 * stride; } else if (y == 1) { /* Second line, no A. */ up1 = up2 = -1 * stride; down1 = 1 * stride; down2 = 2 * stride; } else if (y == (height - 2)) { /* Second to last line, no E. */ up1 = -1 * stride; up2 = -2 * stride; down1 = down2 = 1 * stride; } else if (y == (height -1)) { /* Last line, no D or E. */ up1 = -1 * stride; up2 = -2 * stride; down1 = down2 = 0; } else { hb_error("Invalid value y %d heigh %d", y, height); return; } for( x = 0; x < w; x++) { /* Low-pass 5-tap filter */ dst[0] = blend_filter_pixel(filter, cur[up2], cur[up1], cur[0], cur[down1], cur[down2] ); dst++; cur++; } } static void reset_combing_results( hb_filter_private_t * pv ) { pv->comb_check_complete = 0; int ii; for (ii = 0; ii < pv->comb_check_nthreads; ii++) { pv->block_score[ii] = 0; } } static int check_combing_results( hb_filter_private_t * pv ) { int threshold = pv->block_threshold; int send_to_blend = 0; int ii; for (ii = 0; ii < pv->comb_check_nthreads; ii++) { if( pv->block_score[ii] >= ( threshold / 2 ) ) { if (pv->block_score[ii] <= threshold) { /* Blend video content that scores between ( threshold / 2 ) and threshold. */ send_to_blend = 1; pv->mask_box_color = 2; } else if( pv->block_score[ii] > threshold ) { /* Yadif deinterlace video content above the threshold. */ pv->mask_box_color = 1; return 1; } } } if( send_to_blend ) { return 2; } else { /* Consider this frame to be uncombed. */ return 0; } } static void check_filtered_combing_mask( hb_filter_private_t * pv, int segment, int start, int stop ) { /* Go through the mask in X*Y blocks. If any of these windows have threshold or more combed pixels, consider the whole frame to be combed and send it on to be deinterlaced. */ /* Block mask threshold -- The number of pixels in a block_width * block_height window of he mask that need to show combing for the whole frame to be seen as such. */ int threshold = pv->block_threshold; int block_width = pv->block_width; int block_height = pv->block_height; int block_x, block_y; int block_score = 0; uint8_t * mask_p; int x, y, pp; for( pp = 0; pp < 1; pp++ ) { int stride = pv->mask_filtered->plane[pp].stride; int width = pv->mask_filtered->plane[pp].width; pv->mask_box_x = -1; pv->mask_box_y = -1; pv->mask_box_color = 0; for( y = start; y < ( stop - block_height + 1 ); y = y + block_height ) { for( x = 0; x < ( width - block_width ); x = x + block_width ) { block_score = 0; for( block_y = 0; block_y < block_height; block_y++ ) { int my = y + block_y; mask_p = &pv->mask_filtered->plane[pp].data[my*stride + x]; for( block_x = 0; block_x < block_width; block_x++ ) { block_score += mask_p[0]; mask_p++; } } if (pv->comb_check_complete) { // Some other thread found coming before this one return; } if( block_score >= ( threshold / 2 ) ) { pv->mask_box_x = x; pv->mask_box_y = y; pv->block_score[segment] = block_score; if( block_score > threshold ) { pv->comb_check_complete = 1; return; } } } } } } static void check_combing_mask( hb_filter_private_t * pv, int segment, int start, int stop ) { /* Go through the mask in X*Y blocks. If any of these windows have threshold or more combed pixels, consider the whole frame to be combed and send it on to be deinterlaced. */ /* Block mask threshold -- The number of pixels in a block_width * block_height window of he mask that need to show combing for the whole frame to be seen as such. */ int threshold = pv->block_threshold; int block_width = pv->block_width; int block_height = pv->block_height; int block_x, block_y; int block_score = 0; uint8_t * mask_p; int x, y, pp; for( pp = 0; pp < 1; pp++ ) { int stride = pv->mask->plane[pp].stride; int width = pv->mask->plane[pp].width; for (y = start; y < (stop - block_height + 1); y = y + block_height) { for (x = 0; x < (width - block_width); x = x + block_width) { block_score = 0; for( block_y = 0; block_y < block_height; block_y++ ) { int mask_y = y + block_y; mask_p = &pv->mask->plane[pp].data[mask_y * stride + x]; for( block_x = 0; block_x < block_width; block_x++ ) { /* We only want to mark a pixel in a block as combed if the adjacent pixels are as well. Got to handle the sides separately. */ if( (x + block_x) == 0 ) { block_score += mask_p[0] & mask_p[1]; } else if( (x + block_x) == (width -1) ) { block_score += mask_p[-1] & mask_p[0]; } else { block_score += mask_p[-1] & mask_p[0] & mask_p[1]; } mask_p++; } } if (pv->comb_check_complete) { // Some other thread found coming before this one return; } if( block_score >= ( threshold / 2 ) ) { pv->mask_box_x = x; pv->mask_box_y = y; pv->block_score[segment] = block_score; if( block_score > threshold ) { pv->comb_check_complete = 1; return; } } } } } } static void build_gamma_lut( hb_filter_private_t * pv ) { int i; for( i = 0; i < 256; i++ ) { pv->gamma_lut[i] = pow( ( (float)i / (float)255 ), 2.2f ); } } static void detect_gamma_combed_segment( hb_filter_private_t * pv, int segment_start, int segment_stop ) { /* A mish-mash of various comb detection tricks picked up from neuron2's Decomb plugin for AviSynth and tritical's IsCombedT and IsCombedTIVTC plugins. */ /* Comb scoring algorithm */ /* Motion threshold */ float mthresh = (float)pv->motion_threshold / (float)255; /* Spatial threshold */ float athresh = (float)pv->spatial_threshold / (float)255; float athresh6 = 6 *athresh; /* One pas for Y, one pass for U, one pass for V */ int pp; for( pp = 0; pp < 1; pp++ ) { int x, y; int stride = pv->ref[0]->plane[pp].stride; int width = pv->ref[0]->plane[pp].width; int height = pv->ref[0]->plane[pp].height; /* Comb detection has to start at y = 2 and end at y = height - 2, because it needs to examine 2 pixels above and 2 below the current pixel. */ if( segment_start < 2 ) segment_start = 2; if( segment_stop > height - 2 ) segment_stop = height - 2; for( y = segment_start; y < segment_stop; y++ ) { /* These are just to make the buffer locations easier to read. */ int up_2 = -2 * stride ; int up_1 = -1 * stride; int down_1 = stride; int down_2 = 2 * stride; /* We need to examine a column of 5 pixels in the prev, cur, and next frames. */ uint8_t * prev = &pv->ref[0]->plane[pp].data[y * stride]; uint8_t * cur = &pv->ref[1]->plane[pp].data[y * stride]; uint8_t * next = &pv->ref[2]->plane[pp].data[y * stride]; uint8_t * mask = &pv->mask->plane[pp].data[y * stride]; memset(mask, 0, stride); for( x = 0; x < width; x++ ) { float up_diff, down_diff; up_diff = pv->gamma_lut[cur[0]] - pv->gamma_lut[cur[up_1]]; down_diff = pv->gamma_lut[cur[0]] - pv->gamma_lut[cur[down_1]]; if( ( up_diff > athresh && down_diff > athresh ) || ( up_diff < -athresh && down_diff < -athresh ) ) { /* The pixel above and below are different, and they change in the same "direction" too.*/ int motion = 0; if( mthresh > 0 ) { /* Make sure there's sufficient motion between frame t-1 to frame t+1. */ if( fabs( pv->gamma_lut[prev[0]] - pv->gamma_lut[cur[0]] ) > mthresh && fabs( pv->gamma_lut[cur[up_1]] - pv->gamma_lut[next[up_1]] ) > mthresh && fabs( pv->gamma_lut[cur[down_1]] - pv->gamma_lut[next[down_1]] ) > mthresh ) motion++; if( fabs( pv->gamma_lut[next[0]] - pv->gamma_lut[cur[0]] ) > mthresh && fabs( pv->gamma_lut[prev[up_1]] - pv->gamma_lut[cur[up_1]] ) > mthresh && fabs( pv->gamma_lut[prev[down_1]] - pv->gamma_lut[cur[down_1]] ) > mthresh ) motion++; } else { /* User doesn't want to check for motion, so move on to the spatial check. */ motion = 1; } if( motion || ( pv->deinterlaced_frames==0 && pv->blended_frames==0 && pv->unfiltered_frames==0) ) { /* Tritical's noise-resistant combing scorer. The check is done on a bob+blur convolution. */ float combing = fabs( pv->gamma_lut[cur[up_2]] + ( 4 * pv->gamma_lut[cur[0]] ) + pv->gamma_lut[cur[down_2]] - ( 3 * ( pv->gamma_lut[cur[up_1]] + pv->gamma_lut[cur[down_1]] ) ) ); /* If the frame is sufficiently combed, then mark it down on the mask as 1. */ if( combing > athresh6 ) { mask[0] = 1; } } } cur++; prev++; next++; mask++; } } } } static void detect_combed_segment( hb_filter_private_t * pv, int segment_start, int segment_stop ) { /* A mish-mash of various comb detection tricks picked up from neuron2's Decomb plugin for AviSynth and tritical's IsCombedT and IsCombedTIVTC plugins. */ /* Comb scoring algorithm */ int spatial_metric = pv->spatial_metric; /* Motion threshold */ int mthresh = pv->motion_threshold; /* Spatial threshold */ int athresh = pv->spatial_threshold; int athresh_squared = athresh * athresh; int athresh6 = 6 * athresh; /* One pas for Y, one pass for U, one pass for V */ int pp; for( pp = 0; pp < 1; pp++ ) { int x, y; int stride = pv->ref[0]->plane[pp].stride; int width = pv->ref[0]->plane[pp].width; int height = pv->ref[0]->plane[pp].height; /* Comb detection has to start at y = 2 and end at y = height - 2, because it needs to examine 2 pixels above and 2 below the current pixel. */ if( segment_start < 2 ) segment_start = 2; if( segment_stop > height - 2 ) segment_stop = height - 2; for( y = segment_start; y < segment_stop; y++ ) { /* These are just to make the buffer locations easier to read. */ int up_2 = -2 * stride ; int up_1 = -1 * stride; int down_1 = stride; int down_2 = 2 * stride; /* We need to examine a column of 5 pixels in the prev, cur, and next frames. */ uint8_t * prev = &pv->ref[0]->plane[pp].data[y * stride]; uint8_t * cur = &pv->ref[1]->plane[pp].data[y * stride]; uint8_t * next = &pv->ref[2]->plane[pp].data[y * stride]; uint8_t * mask = &pv->mask->plane[pp].data[y * stride]; memset(mask, 0, stride); for( x = 0; x < width; x++ ) { int up_diff = cur[0] - cur[up_1]; int down_diff = cur[0] - cur[down_1]; if( ( up_diff > athresh && down_diff > athresh ) || ( up_diff < -athresh && down_diff < -athresh ) ) { /* The pixel above and below are different, and they change in the same "direction" too.*/ int motion = 0; if( mthresh > 0 ) { /* Make sure there's sufficient motion between frame t-1 to frame t+1. */ if( abs( prev[0] - cur[0] ) > mthresh && abs( cur[up_1] - next[up_1] ) > mthresh && abs( cur[down_1] - next[down_1] ) > mthresh ) motion++; if( abs( next[0] - cur[0] ) > mthresh && abs( prev[up_1] - cur[up_1] ) > mthresh && abs( prev[down_1] - cur[down_1] ) > mthresh ) motion++; } else { /* User doesn't want to check for motion, so move on to the spatial check. */ motion = 1; } if( motion || ( pv->deinterlaced_frames==0 && pv->blended_frames==0 && pv->unfiltered_frames==0) ) { /* That means it's time for the spatial check. We've got several options here. */ if( spatial_metric == 0 ) { /* Simple 32detect style comb detection */ if( ( abs( cur[0] - cur[down_2] ) < 10 ) && ( abs( cur[0] - cur[down_1] ) > 15 ) ) { mask[0] = 1; } } else if( spatial_metric == 1 ) { /* This, for comparison, is what IsCombed uses. It's better, but still noise senstive. */ int combing = ( cur[up_1] - cur[0] ) * ( cur[down_1] - cur[0] ); if( combing > athresh_squared ) { mask[0] = 1; } } else if( spatial_metric == 2 ) { /* Tritical's noise-resistant combing scorer. The check is done on a bob+blur convolution. */ int combing = abs( cur[up_2] + ( 4 * cur[0] ) + cur[down_2] - ( 3 * ( cur[up_1] + cur[down_1] ) ) ); /* If the frame is sufficiently combed, then mark it down on the mask as 1. */ if( combing > athresh6 ) { mask[0] = 1; } } } } cur++; prev++; next++; mask++; } } } } // This function calls all the eedi2 filters in sequence for a given plane. // It outputs the final interpolated image to pv->eedi_full[DST2PF]. static void eedi2_interpolate_plane( hb_filter_private_t * pv, int plane ) { /* We need all these pointers. No, seriously. I swear. It's not a joke. They're used. All nine of them. */ uint8_t * mskp = pv->eedi_half[MSKPF]->plane[plane].data; uint8_t * srcp = pv->eedi_half[SRCPF]->plane[plane].data; uint8_t * tmpp = pv->eedi_half[TMPPF]->plane[plane].data; uint8_t * dstp = pv->eedi_half[DSTPF]->plane[plane].data; uint8_t * dst2p = pv->eedi_full[DST2PF]->plane[plane].data; uint8_t * tmp2p2 = pv->eedi_full[TMP2PF2]->plane[plane].data; uint8_t * msk2p = pv->eedi_full[MSK2PF]->plane[plane].data; uint8_t * tmp2p = pv->eedi_full[TMP2PF]->plane[plane].data; uint8_t * dst2mp = pv->eedi_full[DST2MPF]->plane[plane].data; int * cx2 = pv->cx2; int * cy2 = pv->cy2; int * cxy = pv->cxy; int * tmpc = pv->tmpc; int pitch = pv->eedi_full[0]->plane[plane].stride; int height = pv->eedi_full[0]->plane[plane].height; int width = pv->eedi_full[0]->plane[plane].width; int half_height = pv->eedi_half[0]->plane[plane].height; // edge mask eedi2_build_edge_mask( mskp, pitch, srcp, pitch, pv->magnitude_threshold, pv->variance_threshold, pv->laplacian_threshold, half_height, width ); eedi2_erode_edge_mask( mskp, pitch, tmpp, pitch, pv->erosion_threshold, half_height, width ); eedi2_dilate_edge_mask( tmpp, pitch, mskp, pitch, pv->dilation_threshold, half_height, width ); eedi2_erode_edge_mask( mskp, pitch, tmpp, pitch, pv->erosion_threshold, half_height, width ); eedi2_remove_small_gaps( tmpp, pitch, mskp, pitch, half_height, width ); // direction mask eedi2_calc_directions( plane, mskp, pitch, srcp, pitch, tmpp, pitch, pv->maximum_search_distance, pv->noise_threshold, half_height, width ); eedi2_filter_dir_map( mskp, pitch, tmpp, pitch, dstp, pitch, half_height, width ); eedi2_expand_dir_map( mskp, pitch, dstp, pitch, tmpp, pitch, half_height, width ); eedi2_filter_map( mskp, pitch, tmpp, pitch, dstp, pitch, half_height, width ); // upscale 2x vertically eedi2_upscale_by_2( srcp, dst2p, half_height, pitch ); eedi2_upscale_by_2( dstp, tmp2p2, half_height, pitch ); eedi2_upscale_by_2( mskp, msk2p, half_height, pitch ); // upscale the direction mask eedi2_mark_directions_2x( msk2p, pitch, tmp2p2, pitch, tmp2p, pitch, pv->tff, height, width ); eedi2_filter_dir_map_2x( msk2p, pitch, tmp2p, pitch, dst2mp, pitch, pv->tff, height, width ); eedi2_expand_dir_map_2x( msk2p, pitch, dst2mp, pitch, tmp2p, pitch, pv->tff, height, width ); eedi2_fill_gaps_2x( msk2p, pitch, tmp2p, pitch, dst2mp, pitch, pv->tff, height, width ); eedi2_fill_gaps_2x( msk2p, pitch, dst2mp, pitch, tmp2p, pitch, pv->tff, height, width ); // interpolate a full-size plane eedi2_interpolate_lattice( plane, tmp2p, pitch, dst2p, pitch, tmp2p2, pitch, pv->tff, pv->noise_threshold, height, width ); if( pv->post_processing == 1 || pv->post_processing == 3 ) { // make sure the edge directions are consistent eedi2_bit_blit( tmp2p2, pitch, tmp2p, pitch, width, height ); eedi2_filter_dir_map_2x( msk2p, pitch, tmp2p, pitch, dst2mp, pitch, pv->tff, height, width ); eedi2_expand_dir_map_2x( msk2p, pitch, dst2mp, pitch, tmp2p, pitch, pv->tff, height, width ); eedi2_post_process( tmp2p, pitch, tmp2p2, pitch, dst2p, pitch, pv->tff, height, width ); } if( pv->post_processing == 2 || pv->post_processing == 3 ) { // filter junctions and corners eedi2_gaussian_blur1( srcp, pitch, tmpp, pitch, srcp, pitch, half_height, width ); eedi2_calc_derivatives( srcp, pitch, half_height, width, cx2, cy2, cxy ); eedi2_gaussian_blur_sqrt2( cx2, tmpc, cx2, pitch, half_height, width); eedi2_gaussian_blur_sqrt2( cy2, tmpc, cy2, pitch, half_height, width); eedi2_gaussian_blur_sqrt2( cxy, tmpc, cxy, pitch, half_height, width); eedi2_post_process_corner( cx2, cy2, cxy, pitch, tmp2p2, pitch, dst2p, pitch, height, width, pv->tff ); } } /* * eedi2 interpolate this plane in a single thread. */ static void eedi2_filter_thread( void *thread_args_v ) { hb_filter_private_t * pv; int plane; eedi2_thread_arg_t *thread_args = thread_args_v; pv = thread_args->pv; plane = thread_args->plane; hb_log("eedi2 thread started for plane %d", plane); while (1) { /* * Wait here until there is work to do. */ taskset_thread_wait4start( &pv->eedi2_taskset, plane ); if( taskset_thread_stop( &pv->eedi2_taskset, plane ) ) { /* * No more work to do, exit this thread. */ break; } /* * Process plane */ eedi2_interpolate_plane( pv, plane ); /* * Finished this segment, let everyone know. */ taskset_thread_complete( &pv->eedi2_taskset, plane ); } taskset_thread_complete( &pv->eedi2_taskset, plane ); } // Sets up the input field planes for EEDI2 in pv->eedi_half[SRCPF] // and then runs eedi2_filter_thread for each plane. static void eedi2_planer( hb_filter_private_t * pv ) { /* Copy the first field from the source to a half-height frame. */ int pp; for( pp = 0; pp < 3; pp++ ) { int pitch = pv->ref[1]->plane[pp].stride; int height = pv->ref[1]->plane[pp].height; int start_line = !pv->tff; eedi2_fill_half_height_buffer_plane( &pv->ref[1]->plane[pp].data[pitch * start_line], pv->eedi_half[SRCPF]->plane[pp].data, pitch, height ); } /* * Now that all data is ready for our threads, fire them off * and wait for their completion. */ taskset_cycle( &pv->eedi2_taskset ); } static void mask_dilate_thread( void *thread_args_v ) { hb_filter_private_t * pv; int segment, segment_start, segment_stop; decomb_thread_arg_t *thread_args = thread_args_v; pv = thread_args->pv; segment = thread_args->segment; hb_log("mask dilate thread started for segment %d", segment); while (1) { /* * Wait here until there is work to do. */ taskset_thread_wait4start( &pv->mask_dilate_taskset, segment ); if (taskset_thread_stop(&pv->mask_dilate_taskset, segment)) { /* * No more work to do, exit this thread. */ break; } int xx, yy, pp; int count; int dilation_threshold = 4; for( pp = 0; pp < 1; pp++ ) { int width = pv->mask_filtered->plane[pp].width; int height = pv->mask_filtered->plane[pp].height; int stride = pv->mask_filtered->plane[pp].stride; int start, stop, p, c, n; segment_start = thread_args->segment_start[pp]; segment_stop = segment_start + thread_args->segment_height[pp]; if (segment_start == 0) { start = 1; p = 0; c = 1; n = 2; } else { start = segment_start; p = segment_start - 1; c = segment_start; n = segment_start + 1; } if (segment_stop == height) { stop = height -1; } else { stop = segment_stop; } uint8_t *curp = &pv->mask_filtered->plane[pp].data[p * stride + 1]; uint8_t *cur = &pv->mask_filtered->plane[pp].data[c * stride + 1]; uint8_t *curn = &pv->mask_filtered->plane[pp].data[n * stride + 1]; uint8_t *dst = &pv->mask_temp->plane[pp].data[c * stride + 1]; for( yy = start; yy < stop; yy++ ) { for( xx = 1; xx < width - 1; xx++ ) { if (cur[xx]) { dst[xx] = 1; continue; } count = curp[xx-1] + curp[xx] + curp[xx+1] + cur [xx-1] + cur [xx+1] + curn[xx-1] + curn[xx] + curn[xx+1]; dst[xx] = count >= dilation_threshold; } curp += stride; cur += stride; curn += stride; dst += stride; } } taskset_thread_complete( &pv->mask_dilate_taskset, segment ); } /* * Finished this segment, let everyone know. */ taskset_thread_complete( &pv->mask_dilate_taskset, segment ); } static void mask_erode_thread( void *thread_args_v ) { hb_filter_private_t * pv; int segment, segment_start, segment_stop; decomb_thread_arg_t *thread_args = thread_args_v; pv = thread_args->pv; segment = thread_args->segment; hb_log("mask erode thread started for segment %d", segment); while (1) { /* * Wait here until there is work to do. */ taskset_thread_wait4start( &pv->mask_erode_taskset, segment ); if( taskset_thread_stop( &pv->mask_erode_taskset, segment ) ) { /* * No more work to do, exit this thread. */ break; } int xx, yy, pp; int count; int erosion_threshold = 2; for( pp = 0; pp < 1; pp++ ) { int width = pv->mask_filtered->plane[pp].width; int height = pv->mask_filtered->plane[pp].height; int stride = pv->mask_filtered->plane[pp].stride; int start, stop, p, c, n; segment_start = thread_args->segment_start[pp]; segment_stop = segment_start + thread_args->segment_height[pp]; if (segment_start == 0) { start = 1; p = 0; c = 1; n = 2; } else { start = segment_start; p = segment_start - 1; c = segment_start; n = segment_start + 1; } if (segment_stop == height) { stop = height -1; } else { stop = segment_stop; } uint8_t *curp = &pv->mask_temp->plane[pp].data[p * stride + 1]; uint8_t *cur = &pv->mask_temp->plane[pp].data[c * stride + 1]; uint8_t *curn = &pv->mask_temp->plane[pp].data[n * stride + 1]; uint8_t *dst = &pv->mask_filtered->plane[pp].data[c * stride + 1]; for( yy = start; yy < stop; yy++ ) { for( xx = 1; xx < width - 1; xx++ ) { if( cur[xx] == 0 ) { dst[xx] = 0; continue; } count = curp[xx-1] + curp[xx] + curp[xx+1] + cur [xx-1] + cur [xx+1] + curn[xx-1] + curn[xx] + curn[xx+1]; dst[xx] = count >= erosion_threshold; } curp += stride; cur += stride; curn += stride; dst += stride; } } taskset_thread_complete( &pv->mask_erode_taskset, segment ); } /* * Finished this segment, let everyone know. */ taskset_thread_complete( &pv->mask_erode_taskset, segment ); } static void mask_filter_thread( void *thread_args_v ) { hb_filter_private_t * pv; int segment, segment_start, segment_stop; decomb_thread_arg_t *thread_args = thread_args_v; pv = thread_args->pv; segment = thread_args->segment; hb_log("mask filter thread started for segment %d", segment); while (1) { /* * Wait here until there is work to do. */ taskset_thread_wait4start( &pv->mask_filter_taskset, segment ); if( taskset_thread_stop( &pv->mask_filter_taskset, segment ) ) { /* * No more work to do, exit this thread. */ break; } int xx, yy, pp; for( pp = 0; pp < 1; pp++ ) { int width = pv->mask->plane[pp].width; int height = pv->mask->plane[pp].height; int stride = pv->mask->plane[pp].stride; int start, stop, p, c, n; segment_start = thread_args->segment_start[pp]; segment_stop = segment_start + thread_args->segment_height[pp]; if (segment_start == 0) { start = 1; p = 0; c = 1; n = 2; } else { start = segment_start; p = segment_start - 1; c = segment_start; n = segment_start + 1; } if (segment_stop == height) { stop = height - 1; } else { stop = segment_stop; } uint8_t *curp = &pv->mask->plane[pp].data[p * stride + 1]; uint8_t *cur = &pv->mask->plane[pp].data[c * stride + 1]; uint8_t *curn = &pv->mask->plane[pp].data[n * stride + 1]; uint8_t *dst = (pv->filter_mode == FILTER_CLASSIC ) ? &pv->mask_filtered->plane[pp].data[c * stride + 1] : &pv->mask_temp->plane[pp].data[c * stride + 1] ; for( yy = start; yy < stop; yy++ ) { for( xx = 1; xx < width - 1; xx++ ) { int h_count, v_count; h_count = cur[xx-1] & cur[xx] & cur[xx+1]; v_count = curp[xx] & cur[xx] & curn[xx]; if (pv->filter_mode == FILTER_CLASSIC) { dst[xx] = h_count; } else { dst[xx] = h_count & v_count; } } curp += stride; cur += stride; curn += stride; dst += stride; } } taskset_thread_complete( &pv->mask_filter_taskset, segment ); } /* * Finished this segment, let everyone know. */ taskset_thread_complete( &pv->mask_filter_taskset, segment ); } static void decomb_check_thread( void *thread_args_v ) { hb_filter_private_t * pv; int segment, segment_start, segment_stop; decomb_thread_arg_t *thread_args = thread_args_v; pv = thread_args->pv; segment = thread_args->segment; hb_log("decomb check thread started for segment %d", segment); while (1) { /* * Wait here until there is work to do. */ taskset_thread_wait4start( &pv->decomb_check_taskset, segment ); if( taskset_thread_stop( &pv->decomb_check_taskset, segment ) ) { /* * No more work to do, exit this thread. */ break; } segment_start = thread_args->segment_start[0]; segment_stop = segment_start + thread_args->segment_height[0]; if( pv->mode & MODE_FILTER ) { check_filtered_combing_mask(pv, segment, segment_start, segment_stop); } else { check_combing_mask(pv, segment, segment_start, segment_stop); } taskset_thread_complete( &pv->decomb_check_taskset, segment ); } /* * Finished this segment, let everyone know. */ taskset_thread_complete( &pv->decomb_check_taskset, segment ); } /* * comb detect this segment of all three planes in a single thread. */ static void decomb_filter_thread( void *thread_args_v ) { hb_filter_private_t * pv; int segment, segment_start, segment_stop; decomb_thread_arg_t *thread_args = thread_args_v; pv = thread_args->pv; segment = thread_args->segment; hb_log("decomb filter thread started for segment %d", segment); while (1) { /* * Wait here until there is work to do. */ taskset_thread_wait4start( &pv->decomb_filter_taskset, segment ); if( taskset_thread_stop( &pv->decomb_filter_taskset, segment ) ) { /* * No more work to do, exit this thread. */ break; } /* * Process segment (for now just from luma) */ int pp; for( pp = 0; pp < 1; pp++) { segment_start = thread_args->segment_start[pp]; segment_stop = segment_start + thread_args->segment_height[pp]; if( pv->mode & MODE_GAMMA ) { detect_gamma_combed_segment( pv, segment_start, segment_stop ); } else { detect_combed_segment( pv, segment_start, segment_stop ); } } taskset_thread_complete( &pv->decomb_filter_taskset, segment ); } /* * Finished this segment, let everyone know. */ taskset_thread_complete( &pv->decomb_filter_taskset, segment ); } static int comb_segmenter( hb_filter_private_t * pv ) { /* * Now that all data for decomb detection is ready for * our threads, fire them off and wait for their completion. */ taskset_cycle( &pv->decomb_filter_taskset ); if( pv->mode & MODE_FILTER ) { taskset_cycle( &pv->mask_filter_taskset ); if( pv->filter_mode == FILTER_ERODE_DILATE ) { taskset_cycle( &pv->mask_erode_taskset ); taskset_cycle( &pv->mask_dilate_taskset ); taskset_cycle( &pv->mask_erode_taskset ); } //return check_filtered_combing_mask( pv ); } else { //return check_combing_mask( pv ); } reset_combing_results(pv); taskset_cycle( &pv->decomb_check_taskset ); return check_combing_results(pv); } /* EDDI: Edge Directed Deinterlacing Interpolation Checks 4 different slopes to see if there is more similarity along a diagonal than there was vertically. If a diagonal is more similar, then it indicates an edge, so interpolate along that instead of a vertical line, using either linear or cubic interpolation depending on mode. */ #define YADIF_CHECK(j) {\ int score = ABS(cur[-stride-1+j] - cur[+stride-1-j])\ + ABS(cur[-stride +j] - cur[+stride -j])\ + ABS(cur[-stride+1+j] - cur[+stride+1-j]);\ if( score < spatial_score ){\ spatial_score = score;\ if( ( pv->mode & MODE_CUBIC ) && !vertical_edge )\ {\ switch(j)\ {\ case -1:\ spatial_pred = cubic_interpolate_pixel(cur[-3 * stride - 3], cur[-stride -1], cur[+stride + 1], cur[3* stride + 3] );\ break;\ case -2:\ spatial_pred = cubic_interpolate_pixel( ( ( cur[-3*stride - 4] + cur[-stride - 4] ) / 2 ) , cur[-stride -2], cur[+stride + 2], ( ( cur[3*stride + 4] + cur[stride + 4] ) / 2 ) );\ break;\ case 1:\ spatial_pred = cubic_interpolate_pixel(cur[-3 * stride +3], cur[-stride +1], cur[+stride - 1], cur[3* stride -3] );\ break;\ case 2:\ spatial_pred = cubic_interpolate_pixel(( ( cur[-3*stride + 4] + cur[-stride + 4] ) / 2 ), cur[-stride +2], cur[+stride - 2], ( ( cur[3*stride - 4] + cur[stride - 4] ) / 2 ) );\ break;\ }\ }\ else\ {\ spatial_pred = ( cur[-stride +j] + cur[+stride -j] ) >>1;\ }\ static void yadif_filter_line( hb_filter_private_t * pv, uint8_t * dst, uint8_t * prev, uint8_t * cur, uint8_t * next, int plane, int width, int height, int stride, int parity, int y) { /* While prev and next point to the previous and next frames, prev2 and next2 will shift depending on the parity, usually 1. They are the previous and next fields, the fields temporally adjacent to the other field in the current frame--the one not being filtered. */ uint8_t *prev2 = parity ? prev : cur ; uint8_t *next2 = parity ? cur : next; int x; int eedi2_mode = ( pv->mode & MODE_EEDI2 ); /* We can replace spatial_pred with this interpolation*/ uint8_t * eedi2_guess = NULL; if (eedi2_mode) { eedi2_guess = &pv->eedi_full[DST2PF]->plane[plane].data[y*stride]; } /* Decomb's cubic interpolation can only function when there are three samples above and below, so regress to yadif's traditional two-tap interpolation when filtering at the top and bottom edges. */ int vertical_edge = 0; if( ( y < 3 ) || ( y > ( height - 4 ) ) ) vertical_edge = 1; for( x = 0; x < width; x++) { /* Pixel above*/ int c = cur[-stride]; /* Temporal average: the current location in the adjacent fields */ int d = (prev2[0] + next2[0])>>1; /* Pixel below */ int e = cur[+stride]; /* How the current pixel changes between the adjacent fields */ int temporal_diff0 = ABS(prev2[0] - next2[0]); /* The average of how much the pixels above and below change from the frame before to now. */ int temporal_diff1 = ( ABS(prev[-stride] - cur[-stride]) + ABS(prev[+stride] - cur[+stride]) ) >> 1; /* The average of how much the pixels above and below change from now to the next frame. */ int temporal_diff2 = ( ABS(next[-stride] - cur[-stride]) + ABS(next[+stride] - cur[+stride]) ) >> 1; /* For the actual difference, use the largest of the previous average diffs. */ int diff = MAX3(temporal_diff0>>1, temporal_diff1, temporal_diff2); int spatial_pred; if( eedi2_mode ) { /* Who needs yadif's spatial predictions when we can have EEDI2's? */ spatial_pred = eedi2_guess[0]; eedi2_guess++; } else // Yadif spatial interpolation { /* SAD of how the pixel-1, the pixel, and the pixel+1 change from the line above to below. */ int spatial_score = ABS(cur[-stride-1] - cur[+stride-1]) + ABS(cur[-stride]-cur[+stride]) + ABS(cur[-stride+1] - cur[+stride+1]) - 1; /* Spatial pred is either a bilinear or cubic vertical interpolation. */ if( ( pv->mode & MODE_CUBIC ) && !vertical_edge) { spatial_pred = cubic_interpolate_pixel( cur[-3*stride], cur[-stride], cur[+stride], cur[3*stride] ); } else { spatial_pred = (c+e)>>1; } // YADIF_CHECK requires a margin to avoid invalid memory access. // In MODE_CUBIC, margin needed is 2 + ABS(param). // Else, the margin needed is 1 + ABS(param). int margin = 2; if (pv->mode & MODE_CUBIC) margin = 3; if (x >= margin && x <= width - (margin + 1)) { YADIF_CHECK(-1) if (x >= margin + 1 && x <= width - (margin + 2)) YADIF_CHECK(-2) }} }} } if (x >= margin && x <= width - (margin + 1)) { YADIF_CHECK(1) if (x >= margin + 1 && x <= width - (margin + 2)) YADIF_CHECK(2) }} }} } } /* Temporally adjust the spatial prediction by comparing against lines in the adjacent fields. */ int b = (prev2[-2*stride] + next2[-2*stride])>>1; int f = (prev2[+2*stride] + next2[+2*stride])>>1; /* Find the median value */ int max = MAX3(d-e, d-c, MIN(b-c, f-e)); int min = MIN3(d-e, d-c, MAX(b-c, f-e)); diff = MAX3( diff, min, -max ); if( spatial_pred > d + diff ) { spatial_pred = d + diff; } else if( spatial_pred < d - diff ) { spatial_pred = d - diff; } dst[0] = spatial_pred; dst++; cur++; prev++; next++; prev2++; next2++; } } /* * deinterlace this segment of all three planes in a single thread. */ static void yadif_decomb_filter_thread( void *thread_args_v ) { yadif_arguments_t *yadif_work = NULL; hb_filter_private_t * pv; int segment, segment_start, segment_stop; yadif_thread_arg_t *thread_args = thread_args_v; filter_param_t filter; filter.tap[0] = -1; filter.tap[1] = 2; filter.tap[2] = 6; filter.tap[3] = 2; filter.tap[4] = -1; filter.normalize = 3; pv = thread_args->pv; segment = thread_args->segment; hb_log("yadif thread started for segment %d", segment); while (1) { /* * Wait here until there is work to do. */ taskset_thread_wait4start( &pv->yadif_taskset, segment ); if( taskset_thread_stop( &pv->yadif_taskset, segment ) ) { /* * No more work to do, exit this thread. */ break; } yadif_work = &pv->yadif_arguments[segment]; /* * Process all three planes, but only this segment of it. */ hb_buffer_t *dst; int parity, tff, is_combed; is_combed = pv->yadif_arguments[segment].is_combed; dst = yadif_work->dst; tff = yadif_work->tff; parity = yadif_work->parity; int pp; for (pp = 0; pp < 3; pp++) { int yy; int width = dst->plane[pp].width; int stride = dst->plane[pp].stride; int height = dst->plane[pp].height_stride; int penultimate = height - 2; segment_start = thread_args->segment_start[pp]; segment_stop = segment_start + thread_args->segment_height[pp]; // Filter parity lines int start = parity ? (segment_start + 1) & ~1 : segment_start | 1; uint8_t *dst2 = &dst->plane[pp].data[start * stride]; uint8_t *prev = &pv->ref[0]->plane[pp].data[start * stride]; uint8_t *cur = &pv->ref[1]->plane[pp].data[start * stride]; uint8_t *next = &pv->ref[2]->plane[pp].data[start * stride]; if( is_combed == 2 ) { /* These will be useful if we ever do temporal blending. */ for( yy = start; yy < segment_stop; yy += 2 ) { /* This line gets blend filtered, not yadif filtered. */ blend_filter_line(&filter, dst2, cur, width, height, stride, yy); dst2 += stride * 2; cur += stride * 2; } } else if (pv->mode == MODE_CUBIC && is_combed) { for( yy = start; yy < segment_stop; yy += 2 ) { /* Just apply vertical cubic interpolation */ cubic_interpolate_line(dst2, cur, width, height, stride, yy); dst2 += stride * 2; cur += stride * 2; } } else if ((pv->mode & MODE_YADIF) && is_combed == 1) { for( yy = start; yy < segment_stop; yy += 2 ) { if( yy > 1 && yy < penultimate ) { // This isn't the top or bottom, // proceed as normal to yadif yadif_filter_line(pv, dst2, prev, cur, next, pp, width, height, stride, parity ^ tff, yy); } else { // parity == 0 (TFF), y1 = y0 // parity == 1 (BFF), y0 = y1 // parity == 0 (TFF), yu = yp // parity == 1 (BFF), yp = yu int yp = (yy ^ parity) * stride; memcpy(dst2, &pv->ref[1]->plane[pp].data[yp], width); } dst2 += stride * 2; prev += stride * 2; cur += stride * 2; next += stride * 2; } } else { // No combing, copy frame for( yy = start; yy < segment_stop; yy += 2 ) { memcpy(dst2, cur, width); dst2 += stride * 2; cur += stride * 2; } } // Copy unfiltered lines start = !parity ? (segment_start + 1) & ~1 : segment_start | 1; dst2 = &dst->plane[pp].data[start * stride]; prev = &pv->ref[0]->plane[pp].data[start * stride]; cur = &pv->ref[1]->plane[pp].data[start * stride]; next = &pv->ref[2]->plane[pp].data[start * stride]; for( yy = start; yy < segment_stop; yy += 2 ) { memcpy(dst2, cur, width); dst2 += stride * 2; cur += stride * 2; } } taskset_thread_complete( &pv->yadif_taskset, segment ); } /* * Finished this segment, let everyone know. */ taskset_thread_complete( &pv->yadif_taskset, segment ); } static void yadif_filter( hb_filter_private_t * pv, hb_buffer_t * dst, int parity, int tff) { /* If we're running comb detection, do it now, otherwise default to true. */ int is_combed; if (!pv->skip_comb_check) { is_combed = pv->spatial_metric >= 0 ? comb_segmenter( pv ) : 1; } else { is_combed = pv->is_combed; } /* The comb detector suggests three different values: 0: Don't comb this frame. 1: Deinterlace this frame. 2: Blend this frame. Since that might conflict with the filter's mode, it may be necesary to adjust this value. */ if( is_combed == 1 && (pv->mode == MODE_BLEND) ) { /* All combed frames are getting blended */ is_combed = 2; } else if( is_combed == 2 && !( pv->mode & MODE_BLEND ) ) { /* Blending is disabled, so force interpolation of these frames. */ is_combed = 1; } if( is_combed == 1 && ( pv->mode & MODE_BLEND ) && !( pv->mode & ( MODE_YADIF | MODE_EEDI2 | MODE_CUBIC ) ) ) { /* Deinterlacers are disabled, blending isn't, so blend these frames. */ is_combed = 2; } else if( is_combed && !( pv->mode & ( MODE_BLEND | MODE_YADIF | MODE_EEDI2 | MODE_CUBIC | MODE_MASK ) ) ) { /* No deinterlacer or mask chosen, pass the frame through. */ is_combed = 0; } if( is_combed == 1 ) { pv->deinterlaced_frames++; } else if( is_combed == 2 ) { pv->blended_frames++; } else { pv->unfiltered_frames++; } if( is_combed == 1 && ( pv->mode & MODE_EEDI2 ) ) { /* Generate an EEDI2 interpolation */ eedi2_planer( pv ); } pv->is_combed = is_combed; if( is_combed ) { if( ( pv->mode & MODE_EEDI2 ) && !( pv->mode & MODE_YADIF ) && is_combed == 1 ) { // Just pass through the EEDI2 interpolation int pp; for( pp = 0; pp < 3; pp++ ) { uint8_t * ref = pv->eedi_full[DST2PF]->plane[pp].data; int ref_stride = pv->eedi_full[DST2PF]->plane[pp].stride; uint8_t * dest = dst->plane[pp].data; int width = dst->plane[pp].width; int height = dst->plane[pp].height; int stride = dst->plane[pp].stride; int yy; for( yy = 0; yy < height; yy++ ) { memcpy(dest, ref, width); dest += stride; ref += ref_stride; } } } else { int segment; for( segment = 0; segment < pv->cpu_count; segment++ ) { /* * Setup the work for this plane. */ pv->yadif_arguments[segment].parity = parity; pv->yadif_arguments[segment].tff = tff; pv->yadif_arguments[segment].dst = dst; pv->yadif_arguments[segment].is_combed = is_combed; } /* * Allow the taskset threads to make one pass over the data. */ taskset_cycle( &pv->yadif_taskset ); /* * Entire frame is now deinterlaced. */ } } else { /* Just passing through... */ pv->yadif_arguments[0].is_combed = is_combed; // 0 hb_buffer_copy(dst, pv->ref[1]); } } static int hb_decomb_init( hb_filter_object_t * filter, hb_filter_init_t * init ) { filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); hb_filter_private_t * pv = filter->private_data; build_gamma_lut( pv ); pv->deinterlaced_frames = 0; pv->blended_frames = 0; pv->unfiltered_frames = 0; pv->yadif_ready = 0; pv->mode = MODE_YADIF | MODE_BLEND | MODE_CUBIC | MODE_GAMMA | MODE_FILTER; pv->filter_mode = FILTER_ERODE_DILATE; pv->spatial_metric = 2; pv->motion_threshold = 3; pv->spatial_threshold = 3; pv->block_threshold = 40; pv->block_width = 16; pv->block_height = 16; pv->magnitude_threshold = 10; pv->variance_threshold = 20; pv->laplacian_threshold = 20; pv->dilation_threshold = 4; pv->erosion_threshold = 2; pv->noise_threshold = 50; pv->maximum_search_distance = 24; pv->post_processing = 1; pv->parity = PARITY_DEFAULT; if( filter->settings ) { sscanf( filter->settings, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", &pv->mode, &pv->spatial_metric, &pv->motion_threshold, &pv->spatial_threshold, &pv->filter_mode, &pv->block_threshold, &pv->block_width, &pv->block_height, &pv->magnitude_threshold, &pv->variance_threshold, &pv->laplacian_threshold, &pv->dilation_threshold, &pv->erosion_threshold, &pv->noise_threshold, &pv->maximum_search_distance, &pv->post_processing, &pv->parity ); } pv->cpu_count = hb_get_cpu_count(); // Make segment sizes an even number of lines int height = hb_image_height(init->pix_fmt, init->height, 0); // Each segment must begin on the even "parity" row. // I.e. each segment of each plane must begin on an even row. pv->segment_height[0] = (height / pv->cpu_count) & ~3; pv->segment_height[1] = hb_image_height(init->pix_fmt, pv->segment_height[0], 1); pv->segment_height[2] = hb_image_height(init->pix_fmt, pv->segment_height[0], 2); /* Allocate buffers to store comb masks. */ pv->mask = hb_frame_buffer_init(init->pix_fmt, init->width, init->height); pv->mask_filtered = hb_frame_buffer_init(init->pix_fmt, init->width, init->height); pv->mask_temp = hb_frame_buffer_init(init->pix_fmt, init->width, init->height); memset(pv->mask->data, 0, pv->mask->size); memset(pv->mask_filtered->data, 0, pv->mask_filtered->size); memset(pv->mask_temp->data, 0, pv->mask_temp->size); int ii; if( pv->mode & MODE_EEDI2 ) { /* Allocate half-height eedi2 buffers */ for( ii = 0; ii < 4; ii++ ) { pv->eedi_half[ii] = hb_frame_buffer_init( init->pix_fmt, init->width, init->height / 2); } /* Allocate full-height eedi2 buffers */ for( ii = 0; ii < 5; ii++ ) { pv->eedi_full[ii] = hb_frame_buffer_init( init->pix_fmt, init->width, init->height); } } /* * Setup yadif taskset. */ pv->yadif_arguments = malloc( sizeof( yadif_arguments_t ) * pv->cpu_count ); if( pv->yadif_arguments == NULL || taskset_init( &pv->yadif_taskset, pv->cpu_count, sizeof( yadif_thread_arg_t ) ) == 0 ) { hb_error( "yadif could not initialize taskset" ); } yadif_thread_arg_t *yadif_prev_thread_args = NULL; for( ii = 0; ii < pv->cpu_count; ii++ ) { yadif_thread_arg_t *thread_args; thread_args = taskset_thread_args( &pv->yadif_taskset, ii ); thread_args->pv = pv; thread_args->segment = ii; int pp; for (pp = 0; pp < 3; pp++) { if (yadif_prev_thread_args != NULL) { thread_args->segment_start[pp] = yadif_prev_thread_args->segment_start[pp] + yadif_prev_thread_args->segment_height[pp]; } if( ii == pv->cpu_count - 1 ) { /* * Final segment */ thread_args->segment_height[pp] = ((hb_image_height(init->pix_fmt, init->height, pp) + 3) & ~3) - thread_args->segment_start[pp]; } else { thread_args->segment_height[pp] = pv->segment_height[pp]; } } pv->yadif_arguments[ii].dst = NULL; if( taskset_thread_spawn( &pv->yadif_taskset, ii, "yadif_filter_segment", yadif_decomb_filter_thread, HB_NORMAL_PRIORITY ) == 0 ) { hb_error( "yadif could not spawn thread" ); } yadif_prev_thread_args = thread_args; } /* * Create comb detection taskset. */ if( taskset_init( &pv->decomb_filter_taskset, pv->cpu_count, sizeof( decomb_thread_arg_t ) ) == 0 ) { hb_error( "decomb could not initialize taskset" ); } decomb_thread_arg_t *decomb_prev_thread_args = NULL; for( ii = 0; ii < pv->cpu_count; ii++ ) { decomb_thread_arg_t *thread_args; thread_args = taskset_thread_args( &pv->decomb_filter_taskset, ii ); thread_args->pv = pv; thread_args->segment = ii; int pp; for (pp = 0; pp < 3; pp++) { if (decomb_prev_thread_args != NULL) { thread_args->segment_start[pp] = decomb_prev_thread_args->segment_start[pp] + decomb_prev_thread_args->segment_height[pp]; } if( ii == pv->cpu_count - 1 ) { /* * Final segment */ thread_args->segment_height[pp] = hb_image_height(init->pix_fmt, init->height, pp) - thread_args->segment_start[pp]; } else { thread_args->segment_height[pp] = pv->segment_height[pp]; } } if( taskset_thread_spawn( &pv->decomb_filter_taskset, ii, "decomb_filter_segment", decomb_filter_thread, HB_NORMAL_PRIORITY ) == 0 ) { hb_error( "decomb could not spawn thread" ); } decomb_prev_thread_args = thread_args; } pv->comb_check_nthreads = init->height / pv->block_height; if (pv->comb_check_nthreads > pv->cpu_count) pv->comb_check_nthreads = pv->cpu_count; pv->block_score = calloc(pv->comb_check_nthreads, sizeof(int)); /* * Create comb check taskset. */ if( taskset_init( &pv->decomb_check_taskset, pv->comb_check_nthreads, sizeof( decomb_thread_arg_t ) ) == 0 ) { hb_error( "decomb check could not initialize taskset" ); } decomb_prev_thread_args = NULL; for( ii = 0; ii < pv->comb_check_nthreads; ii++ ) { decomb_thread_arg_t *thread_args, *decomb_prev_thread_args = NULL; thread_args = taskset_thread_args( &pv->decomb_check_taskset, ii ); thread_args->pv = pv; thread_args->segment = ii; int pp; for (pp = 0; pp < 3; pp++) { if (decomb_prev_thread_args != NULL) { thread_args->segment_start[pp] = decomb_prev_thread_args->segment_start[pp] + decomb_prev_thread_args->segment_height[pp]; } // Make segment hight a multiple of block_height int h = hb_image_height(init->pix_fmt, init->height, pp) / pv->comb_check_nthreads; h = h / pv->block_height * pv->block_height; if (h == 0) h = pv->block_height; if (ii == pv->comb_check_nthreads - 1) { /* * Final segment */ thread_args->segment_height[pp] = hb_image_height(init->pix_fmt, init->height, pp) - thread_args->segment_start[pp]; } else { thread_args->segment_height[pp] = h; } } if( taskset_thread_spawn( &pv->decomb_check_taskset, ii, "decomb_check_segment", decomb_check_thread, HB_NORMAL_PRIORITY ) == 0 ) { hb_error( "decomb check could not spawn thread" ); } decomb_prev_thread_args = thread_args; } if( pv->mode & MODE_FILTER ) { if( taskset_init( &pv->mask_filter_taskset, pv->cpu_count, sizeof( decomb_thread_arg_t ) ) == 0 ) { hb_error( "maske filter could not initialize taskset" ); } decomb_prev_thread_args = NULL; for( ii = 0; ii < pv->cpu_count; ii++ ) { decomb_thread_arg_t *thread_args; thread_args = taskset_thread_args( &pv->mask_filter_taskset, ii ); thread_args->pv = pv; thread_args->segment = ii; int pp; for (pp = 0; pp < 3; pp++) { if (decomb_prev_thread_args != NULL) { thread_args->segment_start[pp] = decomb_prev_thread_args->segment_start[pp] + decomb_prev_thread_args->segment_height[pp]; } if( ii == pv->cpu_count - 1 ) { /* * Final segment */ thread_args->segment_height[pp] = hb_image_height(init->pix_fmt, init->height, pp) - thread_args->segment_start[pp]; } else { thread_args->segment_height[pp] = pv->segment_height[pp]; } } if( taskset_thread_spawn( &pv->mask_filter_taskset, ii, "mask_filter_segment", mask_filter_thread, HB_NORMAL_PRIORITY ) == 0 ) { hb_error( "mask filter could not spawn thread" ); } decomb_prev_thread_args = thread_args; } if( pv->filter_mode == FILTER_ERODE_DILATE ) { if( taskset_init( &pv->mask_erode_taskset, pv->cpu_count, sizeof( decomb_thread_arg_t ) ) == 0 ) { hb_error( "mask erode could not initialize taskset" ); } decomb_prev_thread_args = NULL; for( ii = 0; ii < pv->cpu_count; ii++ ) { decomb_thread_arg_t *thread_args; thread_args = taskset_thread_args( &pv->mask_erode_taskset, ii ); thread_args->pv = pv; thread_args->segment = ii; int pp; for (pp = 0; pp < 3; pp++) { if (decomb_prev_thread_args != NULL) { thread_args->segment_start[pp] = decomb_prev_thread_args->segment_start[pp] + decomb_prev_thread_args->segment_height[pp]; } if( ii == pv->cpu_count - 1 ) { /* * Final segment */ thread_args->segment_height[pp] = hb_image_height(init->pix_fmt, init->height, pp) - thread_args->segment_start[pp]; } else { thread_args->segment_height[pp] = pv->segment_height[pp]; } } if( taskset_thread_spawn( &pv->mask_erode_taskset, ii, "mask_erode_segment", mask_erode_thread, HB_NORMAL_PRIORITY ) == 0 ) { hb_error( "mask erode could not spawn thread" ); } decomb_prev_thread_args = thread_args; } if( taskset_init( &pv->mask_dilate_taskset, pv->cpu_count, sizeof( decomb_thread_arg_t ) ) == 0 ) { hb_error( "mask dilate could not initialize taskset" ); } decomb_prev_thread_args = NULL; for( ii = 0; ii < pv->cpu_count; ii++ ) { decomb_thread_arg_t *thread_args; thread_args = taskset_thread_args( &pv->mask_dilate_taskset, ii ); thread_args->pv = pv; thread_args->segment = ii; int pp; for (pp = 0; pp < 3; pp++) { if (decomb_prev_thread_args != NULL) { thread_args->segment_start[pp] = decomb_prev_thread_args->segment_start[pp] + decomb_prev_thread_args->segment_height[pp]; } if( ii == pv->cpu_count - 1 ) { /* * Final segment */ thread_args->segment_height[pp] = hb_image_height(init->pix_fmt, init->height, pp) - thread_args->segment_start[pp]; } else { thread_args->segment_height[pp] = pv->segment_height[pp]; } } if( taskset_thread_spawn( &pv->mask_dilate_taskset, ii, "mask_dilate_segment", mask_dilate_thread, HB_NORMAL_PRIORITY ) == 0 ) { hb_error( "mask dilate could not spawn thread" ); } decomb_prev_thread_args = thread_args; } } } if( pv->mode & MODE_EEDI2 ) { /* * Create eedi2 taskset. */ if( taskset_init( &pv->eedi2_taskset, /*thread_count*/3, sizeof( eedi2_thread_arg_t ) ) == 0 ) { hb_error( "eedi2 could not initialize taskset" ); } if( pv->post_processing > 1 ) { int stride = hb_image_stride(init->pix_fmt, init->width, 0); pv->cx2 = (int*)eedi2_aligned_malloc( init->height * stride * sizeof(int), 16); pv->cy2 = (int*)eedi2_aligned_malloc( init->height * stride * sizeof(int), 16); pv->cxy = (int*)eedi2_aligned_malloc( init->height * stride * sizeof(int), 16); pv->tmpc = (int*)eedi2_aligned_malloc( init->height * stride * sizeof(int), 16); if( !pv->cx2 || !pv->cy2 || !pv->cxy || !pv->tmpc ) hb_log("EEDI2: failed to malloc derivative arrays"); else hb_log("EEDI2: successfully mallloced derivative arrays"); } for( ii = 0; ii < 3; ii++ ) { eedi2_thread_arg_t *eedi2_thread_args; eedi2_thread_args = taskset_thread_args( &pv->eedi2_taskset, ii ); eedi2_thread_args->pv = pv; eedi2_thread_args->plane = ii; if( taskset_thread_spawn( &pv->eedi2_taskset, ii, "eedi2_filter_segment", eedi2_filter_thread, HB_NORMAL_PRIORITY ) == 0 ) { hb_error( "eedi2 could not spawn thread" ); } } } return 0; } static void hb_decomb_close( hb_filter_object_t * filter ) { hb_filter_private_t * pv = filter->private_data; if( !pv ) { return; } hb_log("decomb: deinterlaced %i | blended %i | unfiltered %i | total %i", pv->deinterlaced_frames, pv->blended_frames, pv->unfiltered_frames, pv->deinterlaced_frames + pv->blended_frames + pv->unfiltered_frames); taskset_fini( &pv->yadif_taskset ); taskset_fini( &pv->decomb_filter_taskset ); taskset_fini( &pv->decomb_check_taskset ); if( pv->mode & MODE_FILTER ) { taskset_fini( &pv->mask_filter_taskset ); if( pv->filter_mode == FILTER_ERODE_DILATE ) { taskset_fini( &pv->mask_erode_taskset ); taskset_fini( &pv->mask_dilate_taskset ); } } if( pv->mode & MODE_EEDI2 ) { taskset_fini( &pv->eedi2_taskset ); } /* Cleanup reference buffers. */ int ii; for (ii = 0; ii < 3; ii++) { hb_buffer_close(&pv->ref[ii]); } /* Cleanup combing masks. */ hb_buffer_close(&pv->mask); hb_buffer_close(&pv->mask_filtered); hb_buffer_close(&pv->mask_temp); if( pv->mode & MODE_EEDI2 ) { /* Cleanup eedi-half buffers */ int ii; for( ii = 0; ii < 4; ii++ ) { hb_buffer_close(&pv->eedi_half[ii]); } /* Cleanup eedi-full buffers */ for( ii = 0; ii < 5; ii++ ) { hb_buffer_close(&pv->eedi_full[ii]); } } if( pv->post_processing > 1 && ( pv->mode & MODE_EEDI2 ) ) { if (pv->cx2) eedi2_aligned_free(pv->cx2); if (pv->cy2) eedi2_aligned_free(pv->cy2); if (pv->cxy) eedi2_aligned_free(pv->cxy); if (pv->tmpc) eedi2_aligned_free(pv->tmpc); } free(pv->block_score); /* * free memory for yadif structs */ free( pv->yadif_arguments ); free( pv ); filter->private_data = NULL; } // Fill rows above height with copy of last row to prevent color distortion // during blending static void fill_stride(hb_buffer_t * buf) { int pp, ii; for (pp = 0; pp < 3; pp++) { uint8_t * src, * dst; src = buf->plane[pp].data + (buf->plane[pp].height - 1) * buf->plane[pp].stride; dst = buf->plane[pp].data + buf->plane[pp].height * buf->plane[pp].stride; for (ii = 0; ii < 3; ii++) { memcpy(dst, src, buf->plane[pp].stride); dst += buf->plane[pp].stride; } } } static int hb_decomb_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in; hb_buffer_t * last = NULL, * out = NULL; if ( in->size <= 0 ) { *buf_out = in; *buf_in = NULL; return HB_FILTER_DONE; } /* Store current frame in yadif cache */ *buf_in = NULL; fill_stride(in); store_ref(pv, in); // yadif requires 3 buffers, prev, cur, and next. For the first // frame, there can be no prev, so we duplicate the first frame. if (!pv->yadif_ready) { // If yadif is not ready, store another ref and return HB_FILTER_DELAY store_ref(pv, hb_buffer_dup(in)); pv->yadif_ready = 1; // Wait for next return HB_FILTER_DELAY; } /* Determine if top-field first layout */ int tff; if( pv->parity < 0 ) { tff = !!(in->s.flags & PIC_FLAG_TOP_FIELD_FIRST); } else { tff = (pv->parity & 1) ^ 1; } /* deinterlace both fields if bob */ int frame, num_frames = 1; if (pv->mode & MODE_BOB) { num_frames = 2; } // Will need up to 2 buffers simultaneously int idx = 0; hb_buffer_t * o_buf[2] = {NULL,}; /* Perform yadif filtering */ for( frame = 0; frame < num_frames; frame++ ) { int parity = frame ^ tff ^ 1; /* Skip the second run if the frame is uncombed */ if (frame && pv->is_combed == 0) { break; } // tff for eedi2 pv->tff = !parity; if (o_buf[idx] == NULL) { o_buf[idx] = hb_video_buffer_init(in->f.width, in->f.height); } if (frame) pv->skip_comb_check = 1; else pv->skip_comb_check = 0; yadif_filter(pv, o_buf[idx], parity, tff); // If bob, add all frames to output // else, if not combed, add frame to output // else if final iteration, add frame to output if ((pv->mode & MODE_BOB) || pv->is_combed == 0 || frame == num_frames - 1) { if ( out == NULL ) { last = out = o_buf[idx]; } else { last->next = o_buf[idx]; last = last->next; } last->next = NULL; // Indicate that buffer was consumed o_buf[idx] = NULL; /* Copy buffered settings to output buffer settings */ last->s = pv->ref[1]->s; idx ^= 1; if ((pv->mode & MODE_MASK) && pv->spatial_metric >= 0 ) { if (pv->mode == MODE_MASK || ((pv->mode & MODE_MASK) && (pv->mode & MODE_FILTER)) || ((pv->mode & MODE_MASK) && (pv->mode & MODE_GAMMA)) || pv->is_combed) { apply_mask(pv, last); } } } } // Copy subs only to first output buffer hb_buffer_move_subs( out, pv->ref[1] ); hb_buffer_close(&o_buf[0]); hb_buffer_close(&o_buf[1]); /* if this frame was deinterlaced and bob mode is engaged, halve the duration of the saved timestamps. */ if ((pv->mode & MODE_BOB) && pv->is_combed) { out->s.stop -= (out->s.stop - out->s.start) / 2LL; last->s.start = out->s.stop; last->s.new_chap = 0; } *buf_out = out; return HB_FILTER_OK; } void hb_deinterlace(hb_buffer_t *dst, hb_buffer_t *src) { int pp; filter_param_t filter; filter.tap[0] = -1; filter.tap[1] = 4; filter.tap[2] = 2; filter.tap[3] = 4; filter.tap[4] = -1; filter.normalize = 3; fill_stride(src); for (pp = 0; pp < 3; pp++) { int yy; int width = src->plane[pp].width; int stride = src->plane[pp].stride; int height = src->plane[pp].height_stride; // Filter parity lines uint8_t *pdst = &dst->plane[pp].data[0]; uint8_t *psrc = &src->plane[pp].data[0]; /* These will be useful if we ever do temporal blending. */ for( yy = 0; yy < height - 1; yy += 2 ) { /* This line gets blend filtered, not yadif filtered. */ memcpy(pdst, psrc, width); pdst += stride; psrc += stride; blend_filter_line(&filter, pdst, psrc, width, height, stride, yy + 1); pdst += stride; psrc += stride; } } } HandBrake-0.10.2/libhb/sync.c0000664000175200017520000016007312463330511016244 0ustar handbrakehandbrake/* sync.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "hbffmpeg.h" #include #include "samplerate.h" #ifdef INT64_MIN #undef INT64_MIN /* Because it isn't defined correctly in Zeta */ #endif #define INT64_MIN (-9223372036854775807LL-1) typedef struct { hb_lock_t * mutex; int ref; /* Reference count to tell us when it's unused */ int count_frames; int64_t audio_pts_slip; int64_t video_pts_slip; int64_t pts_offset; /* Frame based point-to-point support */ int64_t audio_pts_thresh; int start_found; hb_cond_t * next_frame; int pts_count; int64_t * first_pts; } hb_sync_common_t; typedef struct { int index; double next_start; /* start time of next output frame */ int64_t first_drop; /* PTS of first 'went backwards' frame dropped */ int drop_count; /* count of 'time went backwards' drops */ /* Raw */ SRC_STATE * state; SRC_DATA data; int silence_size; uint8_t * silence_buf; int drop_video_to_sync; double gain_factor; } hb_sync_audio_t; typedef struct { int link; int merge; hb_buffer_t * list_current; hb_buffer_t * last; } subtitle_sanitizer_t; typedef struct { /* Video */ int first_frame; int64_t pts_skip; int64_t next_start; /* start time of next output frame */ int64_t first_drop; /* PTS of first 'went backwards' frame dropped */ int drop_count; /* count of 'time went backwards' drops */ int drops; /* frames dropped to make a cbr video stream */ int dups; /* frames duplicated to make a cbr video stream */ int video_sequence; int count_frames_max; int chap_mark; /* to propagate chapter mark across a drop */ hb_buffer_t * cur; /* The next picture to process */ subtitle_sanitizer_t *subtitle_sanitizer; /* Statistics */ uint64_t st_counts[4]; uint64_t st_dates[4]; uint64_t st_first; } hb_sync_video_t; struct hb_work_private_s { hb_job_t * job; hb_sync_common_t * common; union { hb_sync_video_t video; hb_sync_audio_t audio; } type; }; /*********************************************************************** * Local prototypes **********************************************************************/ static void getPtsOffset( hb_work_object_t * w ); static int checkPtsOffset( hb_work_object_t * w ); static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i ); static void InitSubtitle( hb_job_t * job, hb_sync_video_t * sync, int i ); static void InsertSilence( hb_work_object_t * w, int64_t d ); static void UpdateState( hb_work_object_t * w ); static void UpdateSearchState( hb_work_object_t * w, int64_t start ); static hb_buffer_t * OutputAudioFrame( hb_audio_t *audio, hb_buffer_t *buf, hb_sync_audio_t *sync ); /*********************************************************************** * hb_work_sync_init *********************************************************************** * Initialize the work object **********************************************************************/ hb_work_object_t * hb_sync_init( hb_job_t * job ) { hb_title_t * title = job->title; hb_chapter_t * chapter; int i; uint64_t duration; hb_work_private_t * pv; hb_sync_video_t * sync; hb_work_object_t * w; hb_work_object_t * ret = NULL; pv = calloc( 1, sizeof( hb_work_private_t ) ); sync = &pv->type.video; pv->common = calloc( 1, sizeof( hb_sync_common_t ) ); pv->common->ref++; pv->common->mutex = hb_lock_init(); pv->common->audio_pts_thresh = AV_NOPTS_VALUE; pv->common->next_frame = hb_cond_init(); pv->common->pts_count = 1; if ( job->frame_to_start || job->pts_to_start ) { pv->common->start_found = 0; } else { pv->common->start_found = 1; } ret = w = hb_get_work( WORK_SYNC_VIDEO ); w->private_data = pv; w->fifo_in = job->fifo_raw; // When doing subtitle indepth scan, the pipeline ends at sync if ( !job->indepth_scan ) w->fifo_out = job->fifo_sync; else w->fifo_out = NULL; pv->job = job; pv->common->pts_offset = INT64_MIN; sync->first_frame = 1; if( job->pass == 2 ) { /* We already have an accurate frame count from pass 1 */ hb_interjob_t * interjob = hb_interjob_get( job->h ); sync->count_frames_max = interjob->frame_count; } else { /* Calculate how many video frames we are expecting */ if ( job->pts_to_stop ) { duration = job->pts_to_stop + 90000; } else if( job->frame_to_stop ) { /* Set the duration to a rough estimate */ duration = ( job->frame_to_stop / ( title->rate / title->rate_base ) ) * 90000; } else { duration = 0; for( i = job->chapter_start; i <= job->chapter_end; i++ ) { chapter = hb_list_item( job->list_chapter, i - 1 ); duration += chapter->duration; } } sync->count_frames_max = duration * title->rate / title->rate_base / 90000; } hb_log( "sync: expecting %d video frames", sync->count_frames_max ); /* Initialize libsamplerate for every audio track we have */ if ( ! job->indepth_scan ) { for( i = 0; i < hb_list_count( job->list_audio ); i++ ) { InitAudio( job, pv->common, i ); } } pv->common->first_pts = malloc( sizeof(int64_t) * pv->common->pts_count ); for ( i = 0; i < pv->common->pts_count; i++ ) pv->common->first_pts[i] = INT64_MAX; int count = hb_list_count(job->list_subtitle); sync->subtitle_sanitizer = calloc(count, sizeof(subtitle_sanitizer_t)); for( i = 0; i < count; i++ ) { InitSubtitle(job, sync, i); } return ret; } static void InitSubtitle( hb_job_t * job, hb_sync_video_t * sync, int i ) { hb_subtitle_t * subtitle; subtitle = hb_list_item( job->list_subtitle, i ); if (subtitle->format == TEXTSUB && subtitle->config.dest == PASSTHRUSUB && (job->mux & HB_MUX_MASK_MP4)) { // Merge overlapping subtitles since mpv tx3g does not support them sync->subtitle_sanitizer[i].merge = 1; } // PGS subtitles don't need to be linked because there are explicit // "clear" subtitle packets that indicate the end time of the // previous subtitle if (subtitle->config.dest == PASSTHRUSUB && subtitle->source != PGSSUB) { // Fill in stop time when it is missing sync->subtitle_sanitizer[i].link = 1; } } static void CloseSubtitle(hb_sync_video_t * sync, int ii) { hb_buffer_close(&sync->subtitle_sanitizer[ii].list_current); } /*********************************************************************** * Close Video *********************************************************************** * **********************************************************************/ void syncVideoClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; hb_job_t * job = pv->job; hb_sync_video_t * sync = &pv->type.video; int ii; // Wake up audio sync if it's still waiting on condition. pv->common->pts_offset = 0; pv->common->start_found = 1; hb_cond_broadcast( pv->common->next_frame ); if( sync->cur ) { hb_buffer_close( &sync->cur ); } hb_log( "sync: got %d frames, %d expected", pv->common->count_frames, sync->count_frames_max ); /* save data for second pass */ if( job->pass == 1 ) { /* Preserve frame count for better accuracy in pass 2 */ hb_interjob_t * interjob = hb_interjob_get( job->h ); interjob->frame_count = pv->common->count_frames; interjob->last_job = job->sequence_id; } if (sync->drops || sync->dups ) { hb_log( "sync: %d frames dropped, %d duplicated", sync->drops, sync->dups ); } int count = hb_list_count(job->list_subtitle); for( ii = 0; ii < count; ii++ ) { CloseSubtitle(sync, ii); } free(sync->subtitle_sanitizer); hb_lock( pv->common->mutex ); if ( --pv->common->ref == 0 ) { hb_unlock( pv->common->mutex ); hb_cond_close( &pv->common->next_frame ); hb_lock_close( &pv->common->mutex ); free( pv->common->first_pts ); free( pv->common ); } else { hb_unlock( pv->common->mutex ); } free( pv ); w->private_data = NULL; } #define ABS(a) ((a) < 0 ? -(a) : (a)) static hb_buffer_t * merge_ssa(hb_buffer_t *a, hb_buffer_t *b) { int len, ii; char *text; hb_buffer_t *buf = hb_buffer_init(a->size + b->size); buf->s = a->s; // Find the text in the second SSA sub text = (char*)b->data; for (ii = 0; ii < 8; ii++) { text = strchr(text, ','); if (text == NULL) break; text++; } if (text != NULL) { len = sprintf((char*)buf->data, "%s\n%s", a->data, text); if (len >= 0) buf->size = len + 1; } else { memcpy(buf->data, a->data, a->size); buf->size = a->size; } return buf; } static hb_buffer_t * mergeSubtitles(subtitle_sanitizer_t *sanitizer, int end) { hb_buffer_t *a, *b, *buf, *out = NULL, *last = NULL; do { a = sanitizer->list_current; b = a != NULL ? a->next : NULL; buf = NULL; if (a != NULL && b == NULL && end) { sanitizer->list_current = a->next; if (sanitizer->list_current == NULL) sanitizer->last = NULL; a->next = NULL; buf = a; } else if (a != NULL && a->s.stop != AV_NOPTS_VALUE) { if (!sanitizer->merge) { sanitizer->list_current = a->next; if (sanitizer->list_current == NULL) sanitizer->last = NULL; a->next = NULL; buf = a; } else if (b != NULL && a->s.stop > b->s.start) { // Overlap if (ABS(a->s.start - b->s.start) <= 18000) { // subtitles start within 1/5 second of eachother, merge if (a->s.stop > b->s.stop) { // a continues after b, reorder the list and swap hb_buffer_t *tmp = a; a->next = b->next; b->next = a; if (sanitizer->last == b) { sanitizer->last = a; } a = b; b = tmp; sanitizer->list_current = a; } a->next = NULL; b->s.start = a->s.stop; buf = merge_ssa(a, b); hb_buffer_close(&a); a = buf; buf = NULL; sanitizer->list_current = a; if (b->s.stop != AV_NOPTS_VALUE && ABS(b->s.stop - b->s.start) <= 18000) { // b and a completely overlap, remove b a->next = b->next; b->next = NULL; if (sanitizer->last == b) { sanitizer->last = a; } hb_buffer_close(&b); } else { a->next = b; } } else { // a starts before b, output copy of a and buf = hb_buffer_dup(a); buf->s.stop = b->s.start; a->s.start = b->s.start; } } else if (b != NULL && a->s.stop <= b->s.start) { sanitizer->list_current = a->next; if (sanitizer->list_current == NULL) sanitizer->last = NULL; a->next = NULL; buf = a; } } if (buf != NULL) { if (buf->s.stop != AV_NOPTS_VALUE) buf->s.duration = buf->s.stop - buf->s.start; else buf->s.duration = AV_NOPTS_VALUE; if (last == NULL) { out = last = buf; } else { last->next = buf; last = buf; } } } while (buf != NULL); return out; } static hb_buffer_t * sanitizeSubtitle( hb_work_private_t * pv, int i, hb_buffer_t * sub) { hb_sync_video_t * sync; subtitle_sanitizer_t * sanitizer; sync = &pv->type.video; sanitizer = &sync->subtitle_sanitizer[i]; if (!sanitizer->link && !sanitizer->merge) { if (sub != NULL) { if (sub->s.stop != AV_NOPTS_VALUE) sub->s.duration = sub->s.stop - sub->s.start; else sub->s.duration = 0; sub->s.start -= pv->common->video_pts_slip; if (sub->s.stop != AV_NOPTS_VALUE) sub->s.stop -= pv->common->video_pts_slip; if (sub->s.renderOffset != AV_NOPTS_VALUE) sub->s.renderOffset -= pv->common->video_pts_slip; } return sub; } if (sub == NULL) { return mergeSubtitles(sanitizer, 1); } hb_lock( pv->common->mutex ); sub->s.start -= pv->common->video_pts_slip; if (sub->s.stop != AV_NOPTS_VALUE) sub->s.stop -= pv->common->video_pts_slip; if (sub->s.renderOffset != AV_NOPTS_VALUE) sub->s.renderOffset -= pv->common->video_pts_slip; hb_unlock( pv->common->mutex ); if (sanitizer->last != NULL && sanitizer->last->s.stop == AV_NOPTS_VALUE) { sanitizer->last->s.stop = sub->s.start; } if (sub->s.start == sub->s.stop) { // Used to indicate "clear" subtitles when the duration // of subtitles is not encoded in the stream hb_buffer_close(&sub); } if (sub != NULL) { if (sanitizer->last == NULL) { sanitizer->list_current = sanitizer->last = sub; } else { sanitizer->last->next = sub; sanitizer->last = sub; } } return mergeSubtitles(sanitizer, 0); } /*********************************************************************** * syncVideoWork *********************************************************************** * **********************************************************************/ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_buffer_t * cur, * next, * sub = NULL; hb_work_private_t * pv = w->private_data; hb_job_t * job = pv->job; hb_subtitle_t * subtitle; hb_sync_video_t * sync = &pv->type.video; int i; int64_t next_start; *buf_out = NULL; next = *buf_in; *buf_in = NULL; /* Wait till we can determine the initial pts of all streams */ if( next->size != 0 && pv->common->pts_offset == INT64_MIN ) { pv->common->first_pts[0] = next->s.start; hb_lock( pv->common->mutex ); while( pv->common->pts_offset == INT64_MIN && !*w->done ) { // Full fifos will make us wait forever, so get the // pts offset from the available streams if full if ( hb_fifo_is_full( job->fifo_raw ) ) { getPtsOffset( w ); hb_cond_broadcast( pv->common->next_frame ); } else if ( checkPtsOffset( w ) ) hb_cond_broadcast( pv->common->next_frame ); else hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 200 ); } hb_unlock( pv->common->mutex ); } hb_lock( pv->common->mutex ); next_start = next->s.start - pv->common->video_pts_slip; hb_unlock( pv->common->mutex ); /* Wait for start of point-to-point encoding */ if( !pv->common->start_found ) { hb_sync_video_t * sync = &pv->type.video; if( next->size == 0 ) { *buf_out = next; pv->common->start_found = 1; pv->common->first_pts[0] = INT64_MAX - 1; hb_cond_broadcast( pv->common->next_frame ); /* * Push through any subtitle EOFs in case they * were not synced through. */ for( i = 0; i < hb_list_count( job->list_subtitle ); i++) { subtitle = hb_list_item( job->list_subtitle, i ); if( subtitle->config.dest == PASSTHRUSUB ) { hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) ); } } return HB_WORK_DONE; } if ( pv->common->count_frames < job->frame_to_start || next->s.start < job->pts_to_start ) { // Flush any subtitles that have pts prior to the // current frame for( i = 0; i < hb_list_count( job->list_subtitle ); i++) { subtitle = hb_list_item( job->list_subtitle, i ); while( ( sub = hb_fifo_see( subtitle->fifo_raw ) ) ) { if ( sub->s.start > next->s.start ) break; sub = hb_fifo_get( subtitle->fifo_raw ); hb_buffer_close( &sub ); } } hb_lock( pv->common->mutex ); if (job->frame_to_start > 0) { // When doing frame based p-to-p we must update the audio // start point with each frame skipped. // // Tell the audio threads what must be dropped pv->common->audio_pts_thresh = next->s.start; } hb_cond_broadcast( pv->common->next_frame ); hb_unlock( pv->common->mutex ); UpdateSearchState( w, next_start ); #ifdef USE_QSV // reclaim QSV resources before dropping the buffer // when decoding without QSV, the QSV atom will be NULL if (job != NULL && job->qsv.ctx != NULL && next->qsv_details.qsv_atom != NULL) { av_qsv_stage *stage = av_qsv_get_last_stage(next->qsv_details.qsv_atom); if (stage != NULL) { av_qsv_wait_on_sync(job->qsv.ctx, stage); if (stage->out.sync->in_use > 0) { ff_qsv_atomic_dec(&stage->out.sync->in_use); } if (stage->out.p_surface->Data.Locked > 0) { ff_qsv_atomic_dec(&stage->out.p_surface->Data.Locked); } } av_qsv_flush_stages(job->qsv.ctx->pipes, &next->qsv_details.qsv_atom); } #endif hb_buffer_close( &next ); return HB_WORK_OK; } hb_lock( pv->common->mutex ); pv->common->audio_pts_thresh = 0; pv->common->audio_pts_slip += next_start; pv->common->video_pts_slip += next_start; next_start = 0; pv->common->start_found = 1; pv->common->count_frames = 0; hb_cond_broadcast( pv->common->next_frame ); hb_unlock( pv->common->mutex ); sync->st_first = 0; } if( !sync->cur ) { sync->cur = next; if (next->size == 0) { /* we got an end-of-stream as our first video packet? * Feed it downstream & signal that we're done. */ *buf_out = next; sync->cur = NULL; pv->common->start_found = 1; pv->common->first_pts[0] = INT64_MAX - 1; hb_cond_broadcast( pv->common->next_frame ); /* * Push through any subtitle EOFs in case they * were not synced through. */ for( i = 0; i < hb_list_count( job->list_subtitle ); i++) { subtitle = hb_list_item( job->list_subtitle, i ); if( subtitle->config.dest == PASSTHRUSUB ) { hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) ); } } return HB_WORK_DONE; } return HB_WORK_OK; } cur = sync->cur; /* At this point we have a frame to process. Let's check 1) if we will be able to push into the fifo ahead 2) if the next frame is there already, since we need it to compute the duration of the current frame*/ if( next->size == 0 ) { hb_buffer_close( &next ); pv->common->first_pts[0] = INT64_MAX - 1; cur->s.start = sync->next_start; cur->s.stop = cur->s.start + 90000. / ((double)job->vrate / (double)job->vrate_base); sync->next_start += cur->s.stop - cur->s.start;; /* Make sure last frame is reflected in frame count */ pv->common->count_frames++; /* Push the frame to the renderer */ *buf_out = cur; sync->cur = NULL; /* we got an end-of-stream. Feed it downstream & signal that * we're done. Note that this means we drop the final frame of * video (we don't know its duration). On DVDs the final frame * is often strange and dropping it seems to be a good idea. */ (*buf_out)->next = hb_buffer_init( 0 ); /* * Push through any subtitle EOFs in case they were not synced through. */ for( i = 0; i < hb_list_count( job->list_subtitle ); i++) { subtitle = hb_list_item( job->list_subtitle, i ); // flush out any pending subtitle buffers in the sanitizer hb_buffer_t *out = sanitizeSubtitle(pv, i, NULL); if (out != NULL) hb_fifo_push( subtitle->fifo_out, out ); if( subtitle->config.dest == PASSTHRUSUB ) { hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) ); } } pv->common->start_found = 1; hb_cond_broadcast( pv->common->next_frame ); return HB_WORK_DONE; } /* Check for end of point-to-point frame encoding */ if( job->frame_to_stop && pv->common->count_frames > job->frame_to_stop ) { // Drop an empty buffer into our output to ensure that things // get flushed all the way out. hb_buffer_close( &sync->cur ); hb_buffer_close( &next ); *buf_out = hb_buffer_init( 0 ); hb_log( "sync: reached %d frames, exiting early", pv->common->count_frames ); /* * Push through any subtitle EOFs in case they were not synced through. */ for( i = 0; i < hb_list_count( job->list_subtitle ); i++) { subtitle = hb_list_item( job->list_subtitle, i ); // flush out any pending subtitle buffers in the sanitizer hb_buffer_t *out = sanitizeSubtitle(pv, i, NULL); if (out != NULL) hb_fifo_push( subtitle->fifo_out, out ); if( subtitle->config.dest == PASSTHRUSUB ) { hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) ); } } return HB_WORK_DONE; } /* Check for end of point-to-point pts encoding */ if( job->pts_to_stop && sync->next_start >= job->pts_to_stop ) { // Drop an empty buffer into our output to ensure that things // get flushed all the way out. hb_log( "sync: reached pts %"PRId64", exiting early", cur->s.start ); hb_buffer_close( &sync->cur ); hb_buffer_close( &next ); *buf_out = hb_buffer_init( 0 ); /* * Push through any subtitle EOFs in case they were not synced through. */ for( i = 0; i < hb_list_count( job->list_subtitle ); i++) { subtitle = hb_list_item( job->list_subtitle, i ); // flush out any pending subtitle buffers in the sanitizer hb_buffer_t *out = sanitizeSubtitle(pv, i, NULL); if (out != NULL) hb_fifo_push( subtitle->fifo_out, out ); if( subtitle->config.dest == PASSTHRUSUB ) { hb_fifo_push( subtitle->fifo_out, hb_buffer_init( 0 ) ); } } return HB_WORK_DONE; } if( sync->first_frame ) { /* This is our first frame */ if ( cur->s.start > 0 ) { /* * The first pts from a dvd should always be zero but * can be non-zero with a transport or program stream since * we're not guaranteed to start on an IDR frame. If we get * a non-zero initial PTS extend its duration so it behaves * as if it started at zero so that our audio timing will * be in sync. */ hb_log( "sync: first pts is %"PRId64, cur->s.start ); cur->s.start = 0; } sync->first_frame = 0; } /* * since the first frame is always 0 and the upstream reader code * is taking care of adjusting for pts discontinuities, we just have * to deal with the next frame's start being in the past. This can * happen when the PTS is adjusted after data loss but video frame * reordering causes some frames with the old clock to appear after * the clock change. This creates frames that overlap in time which * looks to us like time going backward. The downstream muxing code * can deal with overlaps of up to a frame time but anything larger * we handle by dropping frames here. */ if ( next_start - cur->s.start <= 0 ) { if ( sync->first_drop == 0 ) { sync->first_drop = next_start; } ++sync->drop_count; if ( next->s.new_chap ) { // don't drop a chapter mark when we drop the buffer sync->chap_mark = next->s.new_chap; } #ifdef USE_QSV // reclaim QSV resources before dropping the buffer // when decoding without QSV, the QSV atom will be NULL if (job != NULL && job->qsv.ctx != NULL && next->qsv_details.qsv_atom != NULL) { av_qsv_stage *stage = av_qsv_get_last_stage(next->qsv_details.qsv_atom); if (stage != NULL) { av_qsv_wait_on_sync(job->qsv.ctx, stage); if (stage->out.sync->in_use > 0) { ff_qsv_atomic_dec(&stage->out.sync->in_use); } if (stage->out.p_surface->Data.Locked > 0) { ff_qsv_atomic_dec(&stage->out.p_surface->Data.Locked); } } av_qsv_flush_stages(job->qsv.ctx->pipes, &next->qsv_details.qsv_atom); } #endif hb_buffer_close( &next ); return HB_WORK_OK; } if ( sync->first_drop ) { hb_log( "sync: video time didn't advance - dropped %d frames " "(delta %d ms, current %"PRId64", next %"PRId64", dur %d)", sync->drop_count, (int)( cur->s.start - sync->first_drop ) / 90, cur->s.start, next_start, (int)( next_start - cur->s.start ) ); sync->first_drop = 0; sync->drop_count = 0; } /* * Track the video sequence number locally so that we can sync the audio * to it using the sequence number as well as the PTS. */ sync->video_sequence = cur->sequence; /* Process subtitles that apply to this video frame */ // NOTE: There is no logic in either subtitle-sync algorithm that waits // for the subtitle-decoder if it is lagging behind the video-decoder. // // Therefore there is the implicit assumption that the subtitle-decoder // is always faster than the video-decoder. This assumption is definitely // incorrect in some cases where the SSA subtitle decoder is used. for( i = 0; i < hb_list_count( job->list_subtitle ); i++) { hb_buffer_t *out; subtitle = hb_list_item( job->list_subtitle, i ); // Sanitize subtitle start and stop times, then pass to // muxer or renderer filter. while ( ( sub = hb_fifo_get( subtitle->fifo_raw ) ) != NULL ) { if (sub->size > 0) { out = sanitizeSubtitle(pv, i, sub); if (out != NULL) hb_fifo_push( subtitle->fifo_out, out ); } else { // Push the end of stream marker hb_fifo_push( subtitle->fifo_out, sub ); } } } /* * Adjust the pts of the current frame so that it's contiguous * with the previous frame. The start time of the current frame * has to be the end time of the previous frame and the stop * time has to be the start of the next frame. We don't * make any adjustments to the source timestamps other than removing * the clock offsets (which also removes pts discontinuities). * This means we automatically encode at the source's frame rate. * MP2 uses an implicit duration (frames end when the next frame * starts) but more advanced containers like MP4 use an explicit * duration. Since we're looking ahead one frame we set the * explicit stop time from the start time of the next frame. */ *buf_out = cur; int64_t duration = next_start - cur->s.start; sync->cur = cur = next; cur->sub = NULL; cur->s.start -= pv->common->video_pts_slip; if (cur->s.renderOffset != AV_NOPTS_VALUE) cur->s.renderOffset -= pv->common->video_pts_slip; cur->s.stop -= pv->common->video_pts_slip; sync->pts_skip = 0; if ( duration <= 0 ) { hb_log( "sync: invalid video duration %"PRId64", start %"PRId64", next %"PRId64"", duration, cur->s.start, next_start ); } (*buf_out)->s.start = sync->next_start; sync->next_start += duration; (*buf_out)->s.stop = sync->next_start; if ( sync->chap_mark ) { // we have a pending chapter mark from a recent drop - put it on this // buffer (this may make it one frame late but we can't do any better). (*buf_out)->s.new_chap = sync->chap_mark; sync->chap_mark = 0; } /* Update UI */ UpdateState( w ); return HB_WORK_OK; } // sync*Init does nothing because sync has a special initializer // that takes care of initializing video and all audio tracks int syncVideoInit( hb_work_object_t * w, hb_job_t * job) { return 0; } hb_work_object_t hb_sync_video = { WORK_SYNC_VIDEO, "Video Synchronization", syncVideoInit, syncVideoWork, syncVideoClose }; /*********************************************************************** * Close Audio *********************************************************************** * **********************************************************************/ void syncAudioClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; hb_sync_audio_t * sync = &pv->type.audio; if( sync->silence_buf ) { free( sync->silence_buf ); } if ( sync->state ) { src_delete( sync->state ); } hb_lock( pv->common->mutex ); if ( --pv->common->ref == 0 ) { hb_unlock( pv->common->mutex ); hb_cond_close( &pv->common->next_frame ); hb_lock_close( &pv->common->mutex ); free( pv->common->first_pts ); free( pv->common ); } else { hb_unlock( pv->common->mutex ); } free( pv ); w->private_data = NULL; } int syncAudioInit( hb_work_object_t * w, hb_job_t * job) { return 0; } /*********************************************************************** * SyncAudio *********************************************************************** * **********************************************************************/ static int syncAudioWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_job_t * job = pv->job; hb_sync_audio_t * sync = &pv->type.audio; hb_buffer_t * buf; int64_t start; *buf_out = NULL; buf = *buf_in; *buf_in = NULL; /* if the next buffer is an eof send it downstream */ if ( buf->size <= 0 ) { hb_buffer_close( &buf ); *buf_out = hb_buffer_init( 0 ); pv->common->first_pts[sync->index+1] = INT64_MAX - 1; return HB_WORK_DONE; } /* Wait till we can determine the initial pts of all streams */ if( pv->common->pts_offset == INT64_MIN ) { pv->common->first_pts[sync->index+1] = buf->s.start; hb_lock( pv->common->mutex ); while( pv->common->pts_offset == INT64_MIN && !*w->done) { // Full fifos will make us wait forever, so get the // pts offset from the available streams if full if (hb_fifo_is_full(w->fifo_in)) { getPtsOffset( w ); hb_cond_broadcast( pv->common->next_frame ); } else if ( checkPtsOffset( w ) ) hb_cond_broadcast( pv->common->next_frame ); else hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 200 ); } hb_unlock( pv->common->mutex ); } // Wait for start frame if doing point-to-point // // When doing p-to-p, video leads the way. The video thead will set // start_found when we have reached the start point. // // When doing frame based p-to-p, as each video frame is processed // it advances audio_pts_thresh which informs us how much audio must // be dropped. hb_lock( pv->common->mutex ); while ( !pv->common->start_found && !*w->done ) { if ( pv->common->audio_pts_thresh < 0 ) { // I would initialize this in hb_sync_init, but // job->pts_to_start can be modified by reader // after hb_sync_init is called. pv->common->audio_pts_thresh = job->pts_to_start; } if ( buf->s.start < pv->common->audio_pts_thresh ) { hb_buffer_close( &buf ); hb_unlock( pv->common->mutex ); return HB_WORK_OK; } // We should only get here when doing frame based p-to-p. // In frame based p-to-p, the video sync thread updates // audio_pts_thresh as it discards frames. So wait here // until the current audio frame needs to be discarded // or start point is found. while ( !pv->common->start_found && buf->s.start >= pv->common->audio_pts_thresh && !*w->done ) { hb_cond_timedwait( pv->common->next_frame, pv->common->mutex, 10 ); // There is an unfortunate unavoidable deadlock that can occur. // Since we need to wait for a specific frame in syncVideoWork, // syncAudioWork can be stalled indefinitely. The video decoder // often drops multiple of the initial frames after starting // because they require references that have not been decoded yet. // This allows a lot of audio to be queued in the fifo and the // audio fifo fills before we get a single video frame. So we // must drop some audio to unplug the pipeline and allow the first // video frame to be decoded. if ( hb_fifo_is_full(w->fifo_in) ) { hb_buffer_t *tmp; tmp = buf = hb_fifo_get( w->fifo_in ); while ( tmp ) { tmp = hb_fifo_get( w->fifo_in ); if ( tmp ) { hb_buffer_close( &buf ); buf = tmp; } } } } } start = buf->s.start - pv->common->audio_pts_slip; hb_unlock( pv->common->mutex ); // When doing p-to-p, video determines when the start point has been // found. Since audio and video are processed asynchronously, there // may yet be audio packets in the pipe that are before the start point. // These packets will have negative start times after applying // audio_pts_slip. if (start < 0) { hb_buffer_close(&buf); return HB_WORK_OK; } if( job->frame_to_stop && pv->common->count_frames >= job->frame_to_stop ) { hb_buffer_close( &buf ); *buf_out = hb_buffer_init( 0 ); return HB_WORK_DONE; } if( job->pts_to_stop && sync->next_start >= job->pts_to_stop ) { hb_buffer_close( &buf ); *buf_out = hb_buffer_init( 0 ); return HB_WORK_DONE; } // audio time went backwards. // If our output clock is more than a half frame ahead of the // input clock drop this frame to move closer to sync. // Otherwise drop frames until the input clock matches the output clock. if ( sync->next_start - start > 90*15 ) { // Discard data that's in the past. if ( sync->first_drop == 0 ) { sync->first_drop = start; } ++sync->drop_count; hb_buffer_close( &buf ); return HB_WORK_OK; } if ( sync->first_drop ) { // we were dropping old data but input buf time is now current hb_log( "sync: audio 0x%x time went backwards %d ms, dropped %d frames " "(start %"PRId64", next %"PRId64")", w->audio->id, (int)( sync->next_start - sync->first_drop ) / 90, sync->drop_count, sync->first_drop, (int64_t)sync->next_start ); sync->first_drop = 0; sync->drop_count = 0; } if ( start - sync->next_start >= (90 * 70) ) { if ( start - sync->next_start > (90000LL * 60) ) { // there's a gap of more than a minute between the last // frame and this. assume we got a corrupted timestamp // and just drop the next buf. hb_log( "sync: %d minute time gap in audio 0x%x - dropping buf" " start %"PRId64", next %"PRId64, (int)((start - sync->next_start) / (90000*60)), w->audio->id, start, (int64_t)sync->next_start ); hb_buffer_close( &buf ); return HB_WORK_OK; } /* * there's a gap of at least 70ms between the last * frame we processed & the next. Fill it with silence. * Or in the case of DCA, skip some frames from the * other streams. */ if ( sync->drop_video_to_sync ) { hb_log( "sync: audio gap %d ms. Skipping frames. Audio 0x%x" " start %"PRId64", next %"PRId64, (int)((start - sync->next_start) / 90), w->audio->id, start, (int64_t)sync->next_start ); hb_lock( pv->common->mutex ); pv->common->audio_pts_slip += (start - sync->next_start); pv->common->video_pts_slip += (start - sync->next_start); hb_unlock( pv->common->mutex ); *buf_out = OutputAudioFrame( w->audio, buf, sync ); return HB_WORK_OK; } hb_log( "sync: adding %d ms of silence to audio 0x%x" " start %"PRId64", next %"PRId64, (int)((start - sync->next_start) / 90), w->audio->id, start, (int64_t)sync->next_start ); InsertSilence( w, start - sync->next_start ); } /* * When we get here we've taken care of all the dups and gaps in the * audio stream and are ready to inject the next input frame into * the output stream. */ *buf_out = OutputAudioFrame( w->audio, buf, sync ); return HB_WORK_OK; } hb_work_object_t hb_sync_audio = { WORK_SYNC_AUDIO, "AudioSynchronization", syncAudioInit, syncAudioWork, syncAudioClose }; static void InitAudio( hb_job_t * job, hb_sync_common_t * common, int i ) { hb_work_object_t * w; hb_work_private_t * pv; hb_sync_audio_t * sync; pv = calloc( 1, sizeof( hb_work_private_t ) ); sync = &pv->type.audio; sync->index = i; pv->job = job; pv->common = common; pv->common->ref++; pv->common->pts_count++; w = hb_get_work( WORK_SYNC_AUDIO ); w->private_data = pv; w->audio = hb_list_item( job->list_audio, i ); w->fifo_in = w->audio->priv.fifo_raw; if ( w->audio->config.out.codec & HB_ACODEC_PASS_FLAG ) { w->fifo_out = w->audio->priv.fifo_out; } else { w->fifo_out = w->audio->priv.fifo_sync; } if( w->audio->config.out.codec == HB_ACODEC_AC3_PASS || w->audio->config.out.codec == HB_ACODEC_AAC_PASS ) { /* Have a silent AC-3/AAC frame ready in case we have to fill a gap */ AVCodec * codec; AVCodecContext * c; switch ( w->audio->config.out.codec ) { case HB_ACODEC_AC3_PASS: { codec = avcodec_find_encoder( AV_CODEC_ID_AC3 ); } break; case HB_ACODEC_AAC_PASS: { codec = avcodec_find_encoder_by_name("aac"); } break; default: { // Never gets here codec = NULL; // Silence compiler warning } break; } c = avcodec_alloc_context3(codec); c->bit_rate = w->audio->config.in.bitrate; c->sample_rate = w->audio->config.in.samplerate; c->channels = av_get_channel_layout_nb_channels(w->audio->config.in.channel_layout); hb_ff_set_sample_fmt(c, codec, AV_SAMPLE_FMT_FLT); if (w->audio->config.in.channel_layout == AV_CH_LAYOUT_STEREO_DOWNMIX) { c->channel_layout = AV_CH_LAYOUT_STEREO; } else { c->channel_layout = w->audio->config.in.channel_layout; } if (hb_avcodec_open(c, codec, NULL, 0) < 0) { hb_log("sync: track %d, hb_avcodec_open() failed, dropping video to sync", w->audio->config.out.track); sync->drop_video_to_sync = 1; } else { // Prepare input frame AVFrame frame = { .nb_samples = c->frame_size, .pts = 0, }; int input_size = av_samples_get_buffer_size(NULL, c->channels, frame.nb_samples, c->sample_fmt, 1); uint8_t *zeros = calloc(1, input_size); avcodec_fill_audio_frame(&frame, c->channels, c->sample_fmt, zeros, input_size, 1); // Allocate enough space for the encoded silence // The output should be < the input sync->silence_buf = malloc( input_size ); // There is some delay in getting output from some audio encoders. // So encode a few packets till we get output. int ii; for ( ii = 0; ii < 10; ii++ ) { // Prepare output packet AVPacket pkt; int got_packet; av_init_packet(&pkt); pkt.data = sync->silence_buf; pkt.size = input_size; int ret = avcodec_encode_audio2( c, &pkt, &frame, &got_packet); if ( ret < 0 ) { hb_log("sync: track %d, avcodec_encode_audio() failed, dropping video to sync", w->audio->config.out.track); sync->drop_video_to_sync = 1; break; } if ( got_packet ) { sync->silence_size = pkt.size; break; } else if (ii + 1 == 10) { hb_log("sync: track %d, failed to get output packet, dropping video to sync", w->audio->config.out.track); sync->drop_video_to_sync = 1; } } free( zeros ); hb_avcodec_close( c ); } av_free( c ); } else { if( w->audio->config.out.codec & HB_ACODEC_PASS_FLAG ) { sync->drop_video_to_sync = 1; } else { /* Not passthru, initialize libsamplerate */ int error; sync->state = src_new( SRC_SINC_MEDIUM_QUALITY, hb_mixdown_get_discrete_channel_count( w->audio->config.out.mixdown ), &error ); sync->data.end_of_input = 0; } } sync->gain_factor = pow(10, w->audio->config.out.gain / 20); hb_list_add( job->list_work, w ); } static hb_buffer_t * OutputAudioFrame( hb_audio_t *audio, hb_buffer_t *buf, hb_sync_audio_t *sync ) { int64_t start = (int64_t)sync->next_start; // Can't count of buf->s.stop - buf->s.start for accurate duration // due to integer rounding, so use buf->s.duration when it is set // (which should be always if I didn't miss anything) double duration; if ( buf->s.duration > 0 ) duration = buf->s.duration; else duration = buf->s.stop - buf->s.start; if ( !( audio->config.out.codec & HB_ACODEC_PASS_FLAG ) ) { // Audio is not passthru. Check if we need to modify the audio // in any way. if( audio->config.in.samplerate != audio->config.out.samplerate ) { /* do sample rate conversion */ int count_in, count_out; hb_buffer_t * buf_raw = buf; int sample_size = hb_mixdown_get_discrete_channel_count( audio->config.out.mixdown ) * sizeof( float ); count_in = buf_raw->size / sample_size; /* * When using stupid rates like 44.1 there will always be some * truncation error. E.g., a 1536 sample AC3 frame will turn into a * 1536*44.1/48.0 = 1411.2 sample frame. If we just truncate the .2 * the error will build up over time and eventually the audio will * substantially lag the video. libsamplerate will keep track of the * fractional sample & give it to us when appropriate if we give it * an extra sample of space in the output buffer. */ count_out = ( duration * audio->config.out.samplerate ) / 90000 + 1; sync->data.input_frames = count_in; sync->data.output_frames = count_out; sync->data.src_ratio = (double)audio->config.out.samplerate / (double)audio->config.in.samplerate; buf = hb_buffer_init( count_out * sample_size ); sync->data.data_in = (float *) buf_raw->data; sync->data.data_out = (float *) buf->data; if( src_process( sync->state, &sync->data ) ) { /* XXX If this happens, we're screwed */ hb_log( "sync: audio 0x%x src_process failed", audio->id ); } hb_buffer_close( &buf_raw ); if (sync->data.output_frames_gen <= 0) { // XXX: don't send empty buffers downstream (EOF) // possibly out-of-sync audio is better than no audio at all hb_buffer_close(&buf); return NULL; } buf->size = sync->data.output_frames_gen * sample_size; duration = (double)( sync->data.output_frames_gen * 90000 ) / audio->config.out.samplerate; } if( audio->config.out.gain > 0.0 ) { int count, ii; count = buf->size / sizeof(float); for ( ii = 0; ii < count; ii++ ) { double sample; sample = (double)*(((float*)buf->data)+ii); sample *= sync->gain_factor; if (sample > 0) sample = MIN(sample, 1.0); else sample = MAX(sample, -1.0); *(((float*)buf->data)+ii) = sample; } } else if( audio->config.out.gain < 0.0 ) { int count, ii; count = buf->size / sizeof(float); for ( ii = 0; ii < count; ii++ ) { double sample; sample = (double)*(((float*)buf->data)+ii); sample *= sync->gain_factor; *(((float*)buf->data)+ii) = sample; } } } buf->s.type = AUDIO_BUF; buf->s.frametype = HB_FRAME_AUDIO; buf->s.start = start; sync->next_start += duration; buf->s.stop = (int64_t)sync->next_start; return buf; } static void InsertSilence( hb_work_object_t * w, int64_t duration ) { hb_work_private_t * pv = w->private_data; hb_sync_audio_t *sync = &pv->type.audio; hb_buffer_t *buf; hb_fifo_t *fifo; int frame_dur; // to keep pass-thru and regular audio in sync we generate silence in // frame-sized units. If the silence duration isn't an integer multiple // of the frame duration we will truncate or round up depending on // which minimizes the timing error. if( w->audio->config.out.codec & HB_ACODEC_PASS_FLAG ) { frame_dur = ( 90000 * w->audio->config.in.samples_per_frame ) / w->audio->config.in.samplerate; } else { frame_dur = ( 90000 * w->audio->config.out.samples_per_frame ) / w->audio->config.in.samplerate; } while (duration >= frame_dur >> 2) { if( w->audio->config.out.codec & HB_ACODEC_PASS_FLAG ) { buf = hb_buffer_init( sync->silence_size ); buf->s.start = sync->next_start; buf->s.stop = buf->s.start + frame_dur; memcpy( buf->data, sync->silence_buf, buf->size ); fifo = w->audio->priv.fifo_out; duration -= frame_dur; } else { int channel_count = hb_mixdown_get_discrete_channel_count( w->audio->config.out.mixdown ); int size = sizeof( float ) * w->audio->config.out.samples_per_frame * channel_count; if (frame_dur > duration) { int samples = duration * w->audio->config.in.samplerate / 90000; if (samples == 0) { break; } size = sizeof(float) * samples * channel_count; frame_dur = (90000 * samples) / w->audio->config.in.samplerate; } buf = hb_buffer_init(size); buf->s.start = sync->next_start; buf->s.duration = frame_dur; buf->s.stop = buf->s.start + frame_dur; memset( buf->data, 0, buf->size ); fifo = w->audio->priv.fifo_sync; duration -= frame_dur; } buf = OutputAudioFrame( w->audio, buf, sync ); hb_fifo_push( fifo, buf ); } } static void UpdateState( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; hb_sync_video_t * sync = &pv->type.video; hb_state_t state; if( !pv->common->count_frames ) { sync->st_first = hb_get_date(); pv->job->st_pause_date = -1; pv->job->st_paused = 0; } pv->common->count_frames++; if (pv->job->indepth_scan) { // Progress for indept scan is handled by reader // pv->common->count_frames is used during indepth_scan // to find start & end points. return; } if( hb_get_date() > sync->st_dates[3] + 1000 ) { memmove( &sync->st_dates[0], &sync->st_dates[1], 3 * sizeof( uint64_t ) ); memmove( &sync->st_counts[0], &sync->st_counts[1], 3 * sizeof( uint64_t ) ); sync->st_dates[3] = hb_get_date(); sync->st_counts[3] = pv->common->count_frames; } #define p state.param.working state.state = HB_STATE_WORKING; p.progress = (float) pv->common->count_frames / (float) sync->count_frames_max; if( p.progress > 1.0 ) { p.progress = 1.0; } p.rate_cur = 1000.0 * (float) ( sync->st_counts[3] - sync->st_counts[0] ) / (float) ( sync->st_dates[3] - sync->st_dates[0] ); if( hb_get_date() > sync->st_first + 4000 ) { int eta; p.rate_avg = 1000.0 * (float) sync->st_counts[3] / (float) ( sync->st_dates[3] - sync->st_first - pv->job->st_paused); eta = (float) ( sync->count_frames_max - sync->st_counts[3] ) / p.rate_avg; p.hours = eta / 3600; p.minutes = ( eta % 3600 ) / 60; p.seconds = eta % 60; } else { p.rate_avg = 0.0; p.hours = -1; p.minutes = -1; p.seconds = -1; } #undef p hb_set_state( pv->job->h, &state ); } static void UpdateSearchState( hb_work_object_t * w, int64_t start ) { hb_work_private_t * pv = w->private_data; hb_sync_video_t * sync = &pv->type.video; hb_state_t state; uint64_t now; double avg; now = hb_get_date(); if( !pv->common->count_frames ) { sync->st_first = now; pv->job->st_pause_date = -1; pv->job->st_paused = 0; } pv->common->count_frames++; if (pv->job->indepth_scan) { // Progress for indept scan is handled by reader // pv->common->count_frames is used during indepth_scan // to find start & end points. return; } #define p state.param.working state.state = HB_STATE_SEARCHING; if ( pv->job->frame_to_start ) p.progress = (float) pv->common->count_frames / (float) pv->job->frame_to_start; else if ( pv->job->pts_to_start ) p.progress = (float) start / (float) pv->job->pts_to_start; else p.progress = 0; if( p.progress > 1.0 ) { p.progress = 1.0; } if (now > sync->st_first) { int eta = 0; if ( pv->job->frame_to_start ) { avg = 1000.0 * (double)pv->common->count_frames / (now - sync->st_first); eta = ( pv->job->frame_to_start - pv->common->count_frames ) / avg; } else if ( pv->job->pts_to_start ) { avg = 1000.0 * (double)start / (now - sync->st_first); eta = ( pv->job->pts_to_start - start ) / avg; } p.hours = eta / 3600; p.minutes = ( eta % 3600 ) / 60; p.seconds = eta % 60; } else { p.rate_avg = 0.0; p.hours = -1; p.minutes = -1; p.seconds = -1; } #undef p hb_set_state( pv->job->h, &state ); } static void getPtsOffset( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; int i ; int64_t first_pts = INT64_MAX; for( i = 0; i < pv->common->pts_count; i++ ) { if ( pv->common->first_pts[i] < first_pts ) first_pts = pv->common->first_pts[i]; } pv->common->video_pts_slip = pv->common->audio_pts_slip = pv->common->pts_offset = first_pts; return; } static int checkPtsOffset( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; int i ; for( i = 0; i < pv->common->pts_count; i++ ) { if ( pv->common->first_pts[i] == INT64_MAX ) return 0; } getPtsOffset( w ); return 1; } HandBrake-0.10.2/libhb/lang.h0000664000175200017520000000252512463330511016213 0ustar handbrakehandbrake/* lang.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef HB_LANG_H #define HB_LANG_H typedef struct iso639_lang_t { char * eng_name; /* Description in English */ char * native_name; /* Description in native language */ char * iso639_1; /* ISO-639-1 (2 characters) code */ char * iso639_2; /* ISO-639-2/t (3 character) code */ char * iso639_2b; /* ISO-639-2/b code (if different from above) */ } iso639_lang_t; #ifdef __cplusplus extern "C" { #endif /* find language associated with ISO-639-1 language code */ iso639_lang_t * lang_for_code( int code ); /* find language associated with ISO-639-2 language code */ iso639_lang_t * lang_for_code2( const char *code2 ); /* ISO-639-1 code for language */ int lang_to_code(const iso639_lang_t *lang); iso639_lang_t * lang_for_english( const char * english ); /* * Get the next language in the list. * Returns NULL if there are no more languages. * Pass NULL to get the first language in the list. */ const iso639_lang_t* lang_get_next(const iso639_lang_t *last); #ifdef __cplusplus } #endif #endif HandBrake-0.10.2/libhb/oclnv12toyuv.h0000664000175200017520000000155412463330511017666 0ustar handbrakehandbrake/* oclnv12toyuv.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html Authors: Peng Gao Li Cao */ #ifndef HB_OCLNV12TOYUV_H #define HB_OCLNV12TOYUV_H #include "common.h" #include "extras/cl.h" #include "openclwrapper.h" /* * nv12 to yuv interface * bufi is input frame of nv12, w is input frame width, h is input frame height */ int hb_ocl_nv12toyuv(uint8_t *bufi[], int p, int w, int h, int *crop, hb_va_dxva2_t *dxva2, int decomb, int detelecine); #endif // HB_OCLNV12TOYUV_H HandBrake-0.10.2/libhb/deblock.c0000664000175200017520000002650212265031673016700 0ustar handbrakehandbrake/* Copyright (C) 2005 Michael Niedermayer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hb.h" #include "hbffmpeg.h" #define PP7_QP_DEFAULT 5 #define PP7_MODE_DEFAULT 2 #define XMIN(a,b) ((a) < (b) ? (a) : (b)) #define XMAX(a,b) ((a) > (b) ? (a) : (b)) typedef short DCTELEM; //===========================================================================// static const uint8_t __attribute__((aligned(8))) pp7_dither[8][8] = { { 0, 48, 12, 60, 3, 51, 15, 63, }, { 32, 16, 44, 28, 35, 19, 47, 31, }, { 8, 56, 4, 52, 11, 59, 7, 55, }, { 40, 24, 36, 20, 43, 27, 39, 23, }, { 2, 50, 14, 62, 1, 49, 13, 61, }, { 34, 18, 46, 30, 33, 17, 45, 29, }, { 10, 58, 6, 54, 9, 57, 5, 53, }, { 42, 26, 38, 22, 41, 25, 37, 21, }, }; struct hb_filter_private_s { int pp7_qp; int pp7_mode; int pp7_mpeg2; int pp7_temp_stride; uint8_t * pp7_src; }; static int hb_deblock_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int hb_deblock_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static void hb_deblock_close( hb_filter_object_t * filter ); hb_filter_object_t hb_filter_deblock = { .id = HB_FILTER_DEBLOCK, .enforce_order = 1, .name = "Deblock (pp7)", .settings = NULL, .init = hb_deblock_init, .work = hb_deblock_work, .close = hb_deblock_close, }; static inline void pp7_dct_a( DCTELEM * dst, uint8_t * src, int stride ) { int i; for( i = 0; i < 4; i++ ) { int s0 = src[0*stride] + src[6*stride]; int s1 = src[1*stride] + src[5*stride]; int s2 = src[2*stride] + src[4*stride]; int s3 = src[3*stride]; int s = s3+s3; s3 = s - s0; s0 = s + s0; s = s2 + s1; s2 = s2 - s1; dst[0] = s0 + s; dst[2] = s0 - s; dst[1] = 2*s3 + s2; dst[3] = s3 - s2*2; src++; dst += 4; } } static void pp7_dct_b( DCTELEM * dst, DCTELEM * src ) { int i; for( i = 0; i < 4; i++ ) { int s0 = src[0*4] + src[6*4]; int s1 = src[1*4] + src[5*4]; int s2 = src[2*4] + src[4*4]; int s3 = src[3*4]; int s = s3+s3; s3 = s - s0; s0 = s + s0; s = s2 + s1; s2 = s2 - s1; dst[0*4] = s0 + s; dst[2*4] = s0 - s; dst[1*4] = 2*s3 + s2; dst[3*4] = s3 - s2*2; src++; dst++; } } #define N (1<<16) #define N0 4 #define N1 5 #define N2 10 #define SN0 2 #define SN1 2.2360679775 #define SN2 3.16227766017 static const int pp7_factor[16] = { N/(N0*N0), N/(N0*N1), N/(N0*N0),N/(N0*N2), N/(N1*N0), N/(N1*N1), N/(N1*N0),N/(N1*N2), N/(N0*N0), N/(N0*N1), N/(N0*N0),N/(N0*N2), N/(N2*N0), N/(N2*N1), N/(N2*N0),N/(N2*N2), }; static int pp7_threshold[99][16]; static void pp7_init_threshold( void ) { int qp, i; int bias = 0; for( qp = 0; qp < 99; qp++ ) { for( i = 0; i < 16; i++ ) { pp7_threshold[qp][i] = ((i&1)?SN2:SN0) * ((i&4)?SN2:SN0) * XMAX(1,qp) * (1<<2) - 1 - bias; } } } static int pp7_hard_threshold( DCTELEM * src, int qp ) { int i; int a; a = src[0] * pp7_factor[0]; for( i = 1; i < 16; i++ ) { unsigned int threshold1 = pp7_threshold[qp][i]; unsigned int threshold2 = (threshold1<<1); int level= src[i]; if( ((unsigned)(level+threshold1)) > threshold2 ) { a += level * pp7_factor[i]; } } return (a + (1<<11)) >> 12; } static int pp7_medium_threshold( DCTELEM * src, int qp ) { int i; int a; a = src[0] * pp7_factor[0]; for( i = 1; i < 16; i++ ) { unsigned int threshold1 = pp7_threshold[qp][i]; unsigned int threshold2 = (threshold1<<1); int level= src[i]; if( ((unsigned)(level+threshold1)) > threshold2 ) { if( ((unsigned)(level+2*threshold1)) > 2*threshold2 ) { a += level * pp7_factor[i]; } else { if( level>0 ) { a += 2*(level - (int)threshold1) * pp7_factor[i]; } else { a += 2*(level + (int)threshold1) * pp7_factor[i]; } } } } return (a + (1<<11)) >> 12; } static int pp7_soft_threshold( DCTELEM * src, int qp ) { int i; int a; a = src[0] * pp7_factor[0]; for( i = 1; i < 16; i++ ) { unsigned int threshold1 = pp7_threshold[qp][i]; unsigned int threshold2 = (threshold1<<1); int level= src[i]; if( ((unsigned)(level+threshold1))>threshold2 ) { if( level>0 ) { a += (level - (int)threshold1) * pp7_factor[i]; } else { a += (level + (int)threshold1) * pp7_factor[i]; } } } return (a + (1<<11)) >> 12; } static int ( * pp7_requantize )( DCTELEM * src, int qp ) = pp7_hard_threshold; static void pp7_filter( hb_filter_private_t * pv, uint8_t * dst, uint8_t * src, int width, int height, uint8_t * qp_store, int qp_stride, int is_luma) { int x, y; const int stride = is_luma ? pv->pp7_temp_stride : ((width+16+15)&(~15)); uint8_t * p_src = pv->pp7_src + 8*stride; DCTELEM * block = (DCTELEM *)(pv->pp7_src); DCTELEM * temp = (DCTELEM *)(pv->pp7_src + 32); if( !src || !dst ) { return; } for( y = 0; y < height; y++ ) { int index = 8 + 8*stride + y*stride; memcpy( p_src + index, src + y*width, width ); for( x = 0; x < 8; x++ ) { p_src[index - x - 1] = p_src[index + x ]; p_src[index + width + x ] = p_src[index + width - x - 1]; } } for( y = 0; y < 8; y++ ) { memcpy( p_src + ( 7-y)*stride, p_src + ( y+8)*stride, stride ); memcpy( p_src + (height+8+y)*stride, p_src + (height-y+7)*stride, stride ); } for( y = 0; y < height; y++ ) { for( x = -8; x < 0; x += 4 ) { const int index = x + y*stride + (8-3)*(1+stride) + 8; uint8_t * src = p_src + index; DCTELEM * tp = temp+4*x; pp7_dct_a( tp+4*8, src, stride ); } for( x = 0; x < width; ) { const int qps = 3 + is_luma; int end = XMIN(x+8, width); int qp; if( pv->pp7_qp ) { qp = pv->pp7_qp; } else { qp = qp_store[ (XMIN(x, width-1)>>qps) + (XMIN(y, height-1)>>qps) * qp_stride ]; if( pv->pp7_mpeg2 ) { qp >>= 1; } } for( ; x < end; x++ ) { const int index = x + y*stride + (8-3)*(1+stride) + 8; uint8_t * src = p_src + index; DCTELEM * tp = temp+4*x; int v; if( (x&3) == 0 ) { pp7_dct_a( tp+4*8, src, stride ); } pp7_dct_b( block, tp ); v = pp7_requantize( block, qp ); v = (v + pp7_dither[y&7][x&7]) >> 6; if( (unsigned)v > 255 ) { v = (-v) >> 31; } dst[x + y*width] = v; } } } } static int hb_deblock_init( hb_filter_object_t * filter, hb_filter_init_t * init ) { filter->private_data = calloc( sizeof(struct hb_filter_private_s), 1 ); hb_filter_private_t * pv = filter->private_data; pv->pp7_qp = PP7_QP_DEFAULT; pv->pp7_mode = PP7_MODE_DEFAULT; pv->pp7_mpeg2 = 1; /*mpi->qscale_type;*/ if( filter->settings ) { sscanf( filter->settings, "%d:%d", &pv->pp7_qp, &pv->pp7_mode ); } if( pv->pp7_qp < 0 ) { pv->pp7_qp = 0; } pp7_init_threshold(); switch( pv->pp7_mode ) { case 0: pp7_requantize = pp7_hard_threshold; break; case 1: pp7_requantize = pp7_soft_threshold; break; case 2: pp7_requantize = pp7_medium_threshold; break; } int h = (init->height+16+15)&(~15); pv->pp7_temp_stride = (init->width+16+15)&(~15); pv->pp7_src = (uint8_t*)malloc( pv->pp7_temp_stride*(h+8)*sizeof(uint8_t) ); return 0; } static void hb_deblock_close( hb_filter_object_t * filter ) { hb_filter_private_t * pv = filter->private_data; if( !pv ) { return; } free( pv ); filter->private_data = NULL; } static int hb_deblock_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in, * out; if ( in->size <= 0 ) { *buf_out = in; *buf_in = NULL; return HB_FILTER_DONE; } if( /*TODO: mpi->qscale ||*/ pv->pp7_qp ) { out = hb_video_buffer_init( in->f.width, in->f.height ); pp7_filter( pv, out->plane[0].data, in->plane[0].data, in->plane[0].stride, in->plane[0].height, NULL, /* TODO: mpi->qscale*/ 0, /* TODO: mpi->qstride*/ 1 ); pp7_filter( pv, out->plane[1].data, in->plane[1].data, in->plane[1].stride, in->plane[1].height, NULL, /* TODO: mpi->qscale*/ 0, /* TODO: mpi->qstride*/ 0 ); pp7_filter( pv, out->plane[2].data, in->plane[2].data, in->plane[2].stride, in->plane[2].height, NULL, /* TODO: mpi->qscale*/ 0, /* TODO: mpi->qstride*/ 0 ); out->s = in->s; hb_buffer_move_subs( out, in ); *buf_out = out; } else { *buf_in = NULL; *buf_out = in; } return HB_FILTER_OK; } HandBrake-0.10.2/libhb/qsv_filter_pp.h0000664000175200017520000001012612205472744020154 0ustar handbrakehandbrake/* ********************************************************************* *\ Copyright (C) 2013 Intel Corporation. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \* ********************************************************************* */ #ifndef QSV_FILTER_PP_H #define QSV_FILTER_PP_H #include "msdk/mfxplugin.h" extern hb_buffer_t *link_buf_list( hb_filter_private_t *pv ); struct qsv_filter_task_s; typedef struct{ mfxFrameAllocator *alloc; mfxStatus (*process)(struct qsv_filter_task_s*,void*); mfxCoreInterface *core; }qsv_filter_processor_t; typedef struct qsv_filter_task_s{ mfxFrameSurface1 *in; mfxFrameSurface1 *out; int busy; hb_filter_private_t *pv; qsv_filter_processor_t processor; } qsv_filter_task_t; typedef struct qsv_filter_private_s{ int is_init_done; mfxCoreInterface *core; mfxVideoParam *videoparam; mfxPluginParam pluginparam; hb_filter_private_t *pv; mfxPlugin plug; hb_list_t *tasks; } qsv_filter_t; typedef struct hb_qsv_sync_s{ int frame_go; int status; hb_cond_t *frame_completed; hb_lock_t *frame_completed_lock; hb_buffer_t *in; hb_buffer_t *out; } hb_qsv_sync_t; typedef struct hb_filter_private_s { hb_job_t *job; hb_list_t *list; hb_qsv_sync_t pre; hb_qsv_sync_t pre_busy; hb_qsv_sync_t post; hb_qsv_sync_t post_busy; av_qsv_space *vpp_space; hb_list_t *qsv_user; struct SwsContext* sws_context_to_nv12; struct SwsContext* sws_context_from_nv12; } hb_filter_private_t_qsv; // methods to be called by Media SDK mfxStatus MFX_CDECL qsv_PluginInit(mfxHDL pthis, mfxCoreInterface *core); mfxStatus MFX_CDECL qsv_PluginClose (mfxHDL pthis); mfxStatus MFX_CDECL qsv_GetPluginParam(mfxHDL pthis, mfxPluginParam *par); mfxStatus MFX_CDECL qsv_Submit(mfxHDL pthis, const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxThreadTask *task); mfxStatus MFX_CDECL qsv_Execute(mfxHDL pthis, mfxThreadTask task, mfxU32 uid_p, mfxU32 uid_a); mfxStatus MFX_CDECL qsv_FreeResources(mfxHDL pthis, mfxThreadTask task, mfxStatus sts); // methods to be called by us mfxStatus plugin_init(qsv_filter_t*,mfxVideoParam*); mfxStatus plugin_close(qsv_filter_t*); //internal functions mfxExtBuffer* get_ext_buffer(mfxExtBuffer**, mfxU32, mfxU32); int get_free_task(hb_list_t*); mfxStatus process_filter(qsv_filter_task_t*,void*); mfxStatus lock_frame(mfxFrameAllocator *,mfxFrameSurface1*); mfxStatus unlock_frame(mfxFrameAllocator *,mfxFrameSurface1*); #endif //QSV_FILTER_PP_H HandBrake-0.10.2/libhb/vadxva2.c0000664000175200017520000005757012463330511016652 0ustar handbrakehandbrake/* vadxva2.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html Authors: Peng Gao Li Cao */ #ifdef USE_HWD #include "vadxva2.h" #include "extras/cl.h" #include "oclnv12toyuv.h" static int hb_va_setup( hb_va_dxva2_t *dxva2, void **hw, int width, int height ); static int hb_va_get( hb_va_dxva2_t *dxva2, AVFrame *frame ); static int hb_d3d_create_device( hb_va_dxva2_t *dxva2 ); static void hb_d3d_destroy_device( hb_va_dxva2_t *dxvva2 ); static int hb_d3d_create_device_manager( hb_va_dxva2_t *dxva2 ); static void hb_d3d_destroy_device_manager( hb_va_dxva2_t *dxva2 ); static int hb_dx_create_video_service( hb_va_dxva2_t *dxva2 ); static void hb_dx_destroy_video_service( hb_va_dxva2_t *dxva2 ); static int hb_dx_find_video_service_conversion( hb_va_dxva2_t *dxva2, GUID *input, D3DFORMAT *output ); static int hb_dx_create_video_decoder( hb_va_dxva2_t *dxva2, int codec_id, const hb_title_t* fmt ); static void hb_dx_create_video_conversion( hb_va_dxva2_t *dxva2 ); static const hb_d3d_format_t *hb_d3d_find_format( D3DFORMAT format ); static const hb_dx_mode_t *hb_dx_find_mode( const GUID *guid ); static void hb_dx_destroy_video_decoder( hb_va_dxva2_t *dxva2 ); /** * It destroys a Direct3D device manager */ static void hb_d3d_destroy_device_manager( hb_va_dxva2_t *dxva2 ) { if( dxva2->devmng ) IDirect3DDeviceManager9_Release( dxva2->devmng ); } /** * It releases a Direct3D device and its resources. */ static void hb_d3d_destroy_device( hb_va_dxva2_t *dxva2 ) { if( dxva2->d3ddev ) IDirect3DDevice9_Release( dxva2->d3ddev ); if( dxva2->d3dobj ) IDirect3D9_Release( dxva2->d3dobj ); } /** * It destroys a DirectX video service */ static void hb_dx_destroy_video_service( hb_va_dxva2_t *dxva2 ) { if( dxva2->device ) IDirect3DDeviceManager9_CloseDeviceHandle( dxva2->devmng, dxva2->device ); if( dxva2->vs ) IDirectXVideoDecoderService_Release( dxva2->vs ); } static const hb_d3d_format_t *hb_d3d_find_format( D3DFORMAT format ) { unsigned i; for( i = 0; d3d_formats[i].name; i++ ) { if( d3d_formats[i].format == format ) return &d3d_formats[i]; } return NULL; } static void hb_dx_create_video_conversion( hb_va_dxva2_t *dxva2 ) { switch( dxva2->render ) { case MAKEFOURCC( 'N', 'V', '1', '2' ): dxva2->output = MAKEFOURCC( 'Y', 'V', '1', '2' ); break; default: dxva2->output = dxva2->render; break; } } void hb_va_release( hb_va_dxva2_t *dxva2, AVFrame *frame ) { LPDIRECT3DSURFACE9 d3d = (LPDIRECT3DSURFACE9)(uintptr_t)frame->data[3]; unsigned i; for( i = 0; i < dxva2->surface_count; i++ ) { hb_va_surface_t *surface = &dxva2->surface[i]; if( surface->d3d == d3d ) surface->refcount--; } } void hb_va_close( hb_va_dxva2_t *dxva2 ) { hb_dx_destroy_video_decoder( dxva2 ); hb_dx_destroy_video_service( dxva2 ); hb_d3d_destroy_device_manager( dxva2 ); hb_d3d_destroy_device( dxva2 ); if( dxva2->hdxva2_dll ) FreeLibrary( dxva2->hdxva2_dll ); if( dxva2->hd3d9_dll ) FreeLibrary( dxva2->hd3d9_dll ); if ( dxva2->nv12toyuv_tmp_in ) free( dxva2->nv12toyuv_tmp_in ); if ( dxva2->nv12toyuv_tmp_out ) free( dxva2->nv12toyuv_tmp_out ); dxva2->description = NULL; free( dxva2 ); } /** * It creates a DXVA2 decoder using the given video format */ static int hb_dx_create_video_decoder( hb_va_dxva2_t *dxva2, int codec_id, const hb_title_t* fmt ) { dxva2->width = fmt->width; dxva2->height = fmt->height; dxva2->surface_width = (fmt->width + 15) & ~15; dxva2->surface_height = (fmt->height + 15) & ~15; switch( codec_id ) { case AV_CODEC_ID_H264: dxva2->surface_count = 16 + 1; break; default: dxva2->surface_count = 2 + 1; break; } LPDIRECT3DSURFACE9 surface_list[VA_DXVA2_MAX_SURFACE_COUNT]; if( FAILED( IDirectXVideoDecoderService_CreateSurface( dxva2->vs, dxva2->surface_width, dxva2->surface_height, dxva2->surface_count - 1, dxva2->render, D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget, surface_list, NULL ))) { hb_log( "dxva2:IDirectXVideoAccelerationService_CreateSurface failed" ); dxva2->surface_count = 0; return HB_WORK_ERROR; } unsigned i; for( i = 0; isurface_count; i++ ) { hb_va_surface_t *surface = &dxva2->surface[i]; surface->d3d = surface_list[i]; surface->refcount = 0; surface->order = 0; } hb_log( "dxva2:CreateSurface succeed with %d, fmt (%dx%d) surfaces (%dx%d)", dxva2->surface_count, fmt->width, fmt->height, dxva2->surface_width, dxva2->surface_height ); DXVA2_VideoDesc dsc; memset( &dsc, 0, sizeof(dsc)); dsc.SampleWidth = fmt->width; dsc.SampleHeight = fmt->height; dsc.Format = dxva2->render; if( fmt->rate> 0 && fmt->rate_base> 0 ) { dsc.InputSampleFreq.Numerator = fmt->rate; dsc.InputSampleFreq.Denominator = fmt->rate_base; } else { dsc.InputSampleFreq.Numerator = 0; dsc.InputSampleFreq.Denominator = 0; } dsc.OutputFrameFreq = dsc.InputSampleFreq; dsc.UABProtectionLevel = FALSE; dsc.Reserved = 0; /* FIXME I am unsure we can let unknown everywhere */ DXVA2_ExtendedFormat *ext = &dsc.SampleFormat; ext->SampleFormat = 0; //DXVA2_SampleUnknown; ext->VideoChromaSubsampling = 0; //DXVA2_VideoChromaSubsampling_Unknown; ext->NominalRange = 0; //DXVA2_NominalRange_Unknown; ext->VideoTransferMatrix = 0; //DXVA2_VideoTransferMatrix_Unknown; ext->VideoLighting = 0; //DXVA2_VideoLighting_Unknown; ext->VideoPrimaries = 0; //DXVA2_VideoPrimaries_Unknown; ext->VideoTransferFunction = 0; //DXVA2_VideoTransFunc_Unknown; /* List all configurations available for the decoder */ UINT cfg_count = 0; DXVA2_ConfigPictureDecode *cfg_list = NULL; if( FAILED( IDirectXVideoDecoderService_GetDecoderConfigurations( dxva2->vs, &dxva2->input, &dsc, NULL, &cfg_count, &cfg_list ))) { hb_log( "dxva2:IDirectXVideoDecoderService_GetDecoderConfigurations failed" ); return HB_WORK_ERROR; } hb_log( "dxva2:we got %d decoder configurations", cfg_count ); /* Select the best decoder configuration */ int cfg_score = 0; for( i = 0; i < cfg_count; i++ ) { const DXVA2_ConfigPictureDecode *cfg = &cfg_list[i]; hb_log( "dxva2:configuration[%d] ConfigBitstreamRaw %d", i, cfg->ConfigBitstreamRaw ); int score; if( cfg->ConfigBitstreamRaw == 1 ) score = 1; else if( codec_id == AV_CODEC_ID_H264 && cfg->ConfigBitstreamRaw == 2 ) score = 2; else continue; if( IsEqualGUID( &cfg->guidConfigBitstreamEncryption, &DXVA_NoEncrypt )) score += 16; if( cfg_score < score ) { dxva2->cfg = *cfg; cfg_score = score; } } //my_release(cfg_list); if( cfg_score <= 0 ) { hb_log( "dxva2:Failed to find a supported decoder configuration" ); return HB_WORK_ERROR; } /* Create the decoder */ IDirectXVideoDecoder *decoder; if( FAILED( IDirectXVideoDecoderService_CreateVideoDecoder( dxva2->vs, &dxva2->input, &dsc, &dxva2->cfg, surface_list, dxva2->surface_count, &decoder ))) { hb_log( "dxva2:IDirectXVideoDecoderService_CreateVideoDecoder failed" ); return HB_WORK_ERROR; } dxva2->decoder = decoder; hb_log( "dxva2:IDirectXVideoDecoderService_CreateVideoDecoder succeed" ); return HB_WORK_OK; } typedef HWND (WINAPI *PROCGETSHELLWND)(); /** * It creates a DirectX video service */ static int hb_d3d_create_device( hb_va_dxva2_t *dxva2 ) { LPDIRECT3D9 (WINAPI *Create9)( UINT SDKVersion ); Create9 = (void*)GetProcAddress( dxva2->hd3d9_dll, TEXT( "Direct3DCreate9" )); if( !Create9 ) { hb_log( "dxva2:Cannot locate reference to Direct3DCreate9 ABI in DLL" ); return HB_WORK_ERROR; } LPDIRECT3D9 d3dobj; d3dobj = Create9( D3D_SDK_VERSION ); if( !d3dobj ) { hb_log( "dxva2:Direct3DCreate9 failed" ); return HB_WORK_ERROR; } dxva2->d3dobj = d3dobj; D3DADAPTER_IDENTIFIER9 *d3dai = &dxva2->d3dai; if( FAILED( IDirect3D9_GetAdapterIdentifier( dxva2->d3dobj, D3DADAPTER_DEFAULT, 0, d3dai ))) { hb_log( "dxva2:IDirect3D9_GetAdapterIdentifier failed" ); memset( d3dai, 0, sizeof(*d3dai)); } PROCGETSHELLWND GetShellWindow; HMODULE hUser32 = GetModuleHandle( "user32" ); GetShellWindow = (PROCGETSHELLWND) GetProcAddress( hUser32, "GetShellWindow" ); D3DPRESENT_PARAMETERS *d3dpp = &dxva2->d3dpp; memset( d3dpp, 0, sizeof(*d3dpp)); d3dpp->Flags = D3DPRESENTFLAG_VIDEO; d3dpp->Windowed = TRUE; d3dpp->hDeviceWindow = NULL; d3dpp->SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp->MultiSampleType = D3DMULTISAMPLE_NONE; d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; d3dpp->BackBufferCount = 0; /* FIXME what to put here */ d3dpp->BackBufferFormat = D3DFMT_X8R8G8B8; /* FIXME what to put here */ d3dpp->BackBufferWidth = 0; d3dpp->BackBufferHeight = 0; d3dpp->EnableAutoDepthStencil = FALSE; LPDIRECT3DDEVICE9 d3ddev; //if (FAILED(IDirect3D9_CreateDevice(d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, GetShellWindow(), D3DCREATE_SOFTWARE_VERTEXPROCESSING|D3DCREATE_MULTITHREADED, d3dpp, &d3ddev))) if( FAILED( IDirect3D9_CreateDevice( d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, GetShellWindow(), D3DCREATE_HARDWARE_VERTEXPROCESSING|D3DCREATE_MULTITHREADED, d3dpp, &d3ddev ))) { hb_log( "dxva2:IDirect3D9_CreateDevice failed" ); return HB_WORK_ERROR; } dxva2->d3ddev = d3ddev; return HB_WORK_OK; } /** * It creates a Direct3D device manager */ static int hb_d3d_create_device_manager( hb_va_dxva2_t *dxva2 ) { HRESULT(WINAPI *CreateDeviceManager9)( UINT *pResetToken, IDirect3DDeviceManager9 ** ); CreateDeviceManager9 = (void*)GetProcAddress( dxva2->hdxva2_dll, TEXT( "DXVA2CreateDirect3DDeviceManager9" )); if( !CreateDeviceManager9 ) { hb_log( "dxva2:cannot load function" ); return HB_WORK_ERROR; } UINT token; IDirect3DDeviceManager9 *devmng; if( FAILED( CreateDeviceManager9( &token, &devmng ))) { hb_log( "dxva2:OurDirect3DCreateDeviceManager9 failed" ); return HB_WORK_ERROR; } dxva2->token = token; dxva2->devmng = devmng; long hr = IDirect3DDeviceManager9_ResetDevice( devmng, dxva2->d3ddev, token ); if( FAILED( hr )) { hb_log( "dxva2:IDirect3DDeviceManager9_ResetDevice failed: %08x", (unsigned)hr ); return HB_WORK_ERROR; } return HB_WORK_OK; } /** * It creates a DirectX video service */ static int hb_dx_create_video_service( hb_va_dxva2_t *dxva2 ) { HRESULT (WINAPI *CreateVideoService)( IDirect3DDevice9 *, REFIID riid, void **ppService ); CreateVideoService = (void*)GetProcAddress( dxva2->hdxva2_dll, TEXT( "DXVA2CreateVideoService" )); if( !CreateVideoService ) { hb_log( "dxva2:cannot load function" ); return HB_WORK_ERROR; } HRESULT hr; HANDLE device; hr = IDirect3DDeviceManager9_OpenDeviceHandle( dxva2->devmng, &device ); if( FAILED( hr )) { hb_log( "dxva2:OpenDeviceHandle failed" ); return HB_WORK_ERROR; } dxva2->device = device; IDirectXVideoDecoderService *vs; hr = IDirect3DDeviceManager9_GetVideoService( dxva2->devmng, device, &IID_IDirectXVideoDecoderService, (void*)&vs ); if( FAILED( hr )) { hb_log( "dxva2:GetVideoService failed" ); return HB_WORK_ERROR; } dxva2->vs = vs; return HB_WORK_OK; } /** * Find the best suited decoder mode GUID and render format. */ static int hb_dx_find_video_service_conversion( hb_va_dxva2_t *dxva2, GUID *input, D3DFORMAT *output ) { unsigned int input_count = 0; GUID *input_list = NULL; if( FAILED( IDirectXVideoDecoderService_GetDecoderDeviceGuids( dxva2->vs, &input_count, &input_list ))) { hb_log( "dxva2:IDirectXVideoDecoderService_GetDecoderDeviceGuids failed" ); return HB_WORK_ERROR; } unsigned i, j; for( i = 0; i < input_count; i++ ) { const GUID *g = &input_list[i]; const hb_dx_mode_t *mode = hb_dx_find_mode( g ); } for( i = 0; dxva2_modes[i].name; i++ ) { const hb_dx_mode_t *mode = &dxva2_modes[i]; if( !mode->codec || mode->codec != dxva2->codec_id ) continue; int is_suported = 0; const GUID *g; for( g = &input_list[0]; !is_suported && g < &input_list[input_count]; g++ ) { is_suported = IsEqualGUID( mode->guid, g ); } if( !is_suported ) continue; unsigned int output_count = 0; D3DFORMAT *output_list = NULL; if( FAILED( IDirectXVideoDecoderService_GetDecoderRenderTargets( dxva2->vs, mode->guid, &output_count, &output_list ))) { hb_log( "dxva2:IDirectXVideoDecoderService_GetDecoderRenderTargets failed" ); continue; } for( j = 0; j < output_count; j++ ) { const D3DFORMAT f = output_list[j]; const hb_d3d_format_t *format = hb_d3d_find_format( f ); if( format ) { //hb_log( "dxva2:%s is supported for output", format->name ); } else { hb_log( "dxvar2:%d is supported for output (%4.4s)", f, (const char*)&f ); } } for( j = 0; d3d_formats[j].name; j++ ) { const hb_d3d_format_t *format = &d3d_formats[j]; int is_suported = 0; unsigned k; for( k = 0; !is_suported && k < output_count; k++ ) { is_suported = format->format == output_list[k]; } if( !is_suported ) continue; *input = *mode->guid; *output = format->format; return HB_WORK_OK; } } return HB_WORK_ERROR; } static const hb_dx_mode_t *hb_dx_find_mode( const GUID *guid ) { unsigned i; for( i = 0; dxva2_modes[i].name; i++ ) { if( IsEqualGUID( dxva2_modes[i].guid, guid )) return &dxva2_modes[i]; } return NULL; } static void hb_dx_destroy_video_decoder( hb_va_dxva2_t *dxva2 ) { if( dxva2->decoder ) IDirectXVideoDecoder_Release( dxva2->decoder ); dxva2->decoder = NULL; unsigned i; for( i = 0; isurface_count; i++ ) IDirect3DSurface9_Release( dxva2->surface[i].d3d ); dxva2->surface_count = 0; } /** * setup dxva2 */ static int hb_va_setup( hb_va_dxva2_t *dxva2, void **hw, int width, int height ) { if( dxva2->width == width && dxva2->height == height && dxva2->decoder ) goto ok; hb_dx_destroy_video_decoder( dxva2 ); *hw = NULL; dxva2->i_chroma = 0; if( width <= 0 || height <= 0 ) return HB_WORK_ERROR; hb_title_t fmt; memset( &fmt, 0, sizeof(fmt)); fmt.width = width; fmt.height = height; if( hb_dx_create_video_decoder( dxva2, dxva2->codec_id, &fmt ) == HB_WORK_ERROR ) return HB_WORK_ERROR; dxva2->hw.decoder = dxva2->decoder; dxva2->hw.cfg = &dxva2->cfg; dxva2->hw.surface_count = dxva2->surface_count; dxva2->hw.surface = dxva2->hw_surface; unsigned i; for( i = 0; i < dxva2->surface_count; i++ ) dxva2->hw.surface[i] = dxva2->surface[i].d3d; hb_dx_create_video_conversion( dxva2 ); ok: *hw = &dxva2->hw; const hb_d3d_format_t *output = hb_d3d_find_format( dxva2->output ); dxva2->i_chroma = output->codec; return HB_WORK_OK; } static int hb_va_get( hb_va_dxva2_t *dxva2, AVFrame *frame ) { unsigned i, old; for( i = 0, old = 0; i < dxva2->surface_count; i++ ) { hb_va_surface_t *surface = &dxva2->surface[i]; if( !surface->refcount ) break; if( surface->order < dxva2->surface[old].order ) old = i; } if( i >= dxva2->surface_count ) i = old; hb_va_surface_t *surface = &dxva2->surface[i]; surface->refcount = 1; surface->order = dxva2->surface_order++; for( i = 0; i < 4; i++ ) { frame->data[i] = NULL; frame->linesize[i] = 0; if( i == 0 || i == 3 ) frame->data[i] = (void*)surface->d3d; } return HB_WORK_OK; } /** * nv12 to yuv of c reference */ static void hb_copy_from_nv12( uint8_t *dst, uint8_t *src[2], size_t src_pitch[2], unsigned width, unsigned height ) { unsigned int i, j; uint8_t *dstU, *dstV; dstU = dst + width*height; dstV = dstU + width*height/4; unsigned int heithtUV, widthUV; heithtUV = height/2; widthUV = width/2; for( i = 0; i < height; i++ ) //Y { memcpy( dst + i * width, src[0] + i * src_pitch[0], width ); } for( i = 0; i < heithtUV; i++ ) { for( j = 0; j < widthUV; j++ ) { dstU[i * widthUV + j] = *(src[1] + i * src_pitch[1] + 2 * j); dstV[i *widthUV + j] = *(src[1] + i * src_pitch[1] + 2 * j + 1); } } } /** * lock frame data form surface. * nv12 to yuv with opencl and with C reference * scale with opencl */ int hb_va_extract( hb_va_dxva2_t *dxva2, uint8_t *dst, AVFrame *frame, int job_w, int job_h, int *crop, hb_oclscale_t *os, int use_opencl, int use_decomb, int use_detelecine ) { LPDIRECT3DSURFACE9 d3d = (LPDIRECT3DSURFACE9)(uintptr_t)frame->data[3]; D3DLOCKED_RECT lock; if( FAILED( IDirect3DSurface9_LockRect( d3d, &lock, NULL, D3DLOCK_READONLY ))) { hb_log( "dxva2:Failed to lock surface" ); return HB_WORK_ERROR; } if( dxva2->render == MAKEFOURCC( 'N', 'V', '1', '2' )) { uint8_t *plane[2] = { lock.pBits, (uint8_t*)lock.pBits + lock.Pitch * dxva2->surface_height }; size_t pitch[2] = { lock.Pitch, lock.Pitch, }; hb_copy_from_nv12( dst, plane, pitch, dxva2->width, dxva2->height ); } IDirect3DSurface9_UnlockRect( d3d ); return HB_WORK_OK; } /** * create dxva2 service * load library D3D9.dll */ hb_va_dxva2_t * hb_va_create_dxva2( hb_va_dxva2_t *dxva2, int codec_id ) { if( dxva2 ) { hb_va_close( dxva2 ); dxva2 = NULL; } hb_va_dxva2_t *dxva = calloc( 1, sizeof(*dxva) ); if( !dxva ) return NULL; dxva->codec_id = codec_id; dxva->hd3d9_dll = LoadLibrary( TEXT( "D3D9.DLL" ) ); if( !dxva->hd3d9_dll ) { hb_log( "dxva2:cannot load d3d9.dll" ); goto error; } dxva->hdxva2_dll = LoadLibrary( TEXT( "DXVA2.DLL" ) ); if( !dxva->hdxva2_dll ) { hb_log( "dxva2:cannot load DXVA2.dll" ); goto error; } if( hb_d3d_create_device( dxva ) == HB_WORK_ERROR ) { hb_log( "dxva2:Failed to create Direct3D device" ); goto error; } if( hb_d3d_create_device_manager( dxva ) == HB_WORK_ERROR ) { hb_log( "dxva2:D3dCreateDeviceManager failed" ); goto error; } if( hb_dx_create_video_service( dxva ) == HB_WORK_ERROR ) { hb_log( "dxva2:DxCreateVideoService failed" ); goto error; } if( hb_dx_find_video_service_conversion( dxva, &dxva->input, &dxva->render ) == HB_WORK_ERROR ) { hb_log( "dxva2:DxFindVideoServiceConversion failed" ); goto error; } dxva->do_job = HB_WORK_OK; dxva->description = "DXVA2"; return dxva; error: hb_va_close( dxva ); return NULL; } void hb_va_new_dxva2( hb_va_dxva2_t *dxva2, AVCodecContext *p_context ) { if( p_context->width > 0 && p_context->height > 0 ) { if( hb_va_setup( dxva2, &p_context->hwaccel_context, p_context->width, p_context->height ) == HB_WORK_ERROR ) { hb_log( "dxva2:hb_va_Setup failed" ); hb_va_close( dxva2 ); dxva2 = NULL; } } if( dxva2 ) { dxva2->input_pts[0] = 0; dxva2->input_pts[1] = 0; if( dxva2->description ) hb_log( "dxva2:Using %s for hardware decoding", dxva2->description ); p_context->draw_horiz_band = NULL; } } char* hb_get_pix_fmt_name( int pix_fmt ) { static const char *ppsz_name[AV_PIX_FMT_NB] = { [AV_PIX_FMT_VDPAU_H264] = "AV_PIX_FMT_VDPAU_H264", [AV_PIX_FMT_VAAPI_IDCT] = "AV_PIX_FMT_VAAPI_IDCT", [AV_PIX_FMT_VAAPI_VLD] = "AV_PIX_FMT_VAAPI_VLD", [AV_PIX_FMT_VAAPI_MOCO] = "AV_PIX_FMT_VAAPI_MOCO", [AV_PIX_FMT_DXVA2_VLD] = "AV_PIX_FMT_DXVA2_VLD", [AV_PIX_FMT_YUYV422] = "AV_PIX_FMT_YUYV422", [AV_PIX_FMT_YUV420P] = "AV_PIX_FMT_YUV420P", }; return ppsz_name[pix_fmt]; } enum PixelFormat hb_ffmpeg_get_format( AVCodecContext *p_context, const enum PixelFormat *pi_fmt ) { int i; for( i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++ ) { hb_log( "dxva2:Available decoder output format %d (%s)", pi_fmt[i], hb_get_pix_fmt_name(pi_fmt[i]) ? : "Unknown" ); if( pi_fmt[i] == AV_PIX_FMT_DXVA2_VLD ) { return pi_fmt[i]; } } return avcodec_default_get_format( p_context, pi_fmt ); } int hb_va_get_frame_buf( hb_va_dxva2_t *dxva2, AVCodecContext *p_context, AVFrame *frame ) { frame->type = FF_BUFFER_TYPE_USER; if( hb_va_get( dxva2, frame ) == HB_WORK_ERROR ) { hb_log( "VaGrabSurface failed" ); return HB_WORK_ERROR; } return HB_WORK_OK; } int hb_check_hwd_fmt( int fmt ) { int result = 1; switch ( fmt ) { case AV_PIX_FMT_YUV420P16LE: case AV_PIX_FMT_YUV420P16BE: case AV_PIX_FMT_YUV422P16LE: case AV_PIX_FMT_YUV422P16BE: case AV_PIX_FMT_YUV444P16LE: case AV_PIX_FMT_YUV444P16BE: case AV_PIX_FMT_YUV420P9BE: case AV_PIX_FMT_YUV420P9LE: case AV_PIX_FMT_YUV420P10BE: case AV_PIX_FMT_YUV420P10LE: case AV_PIX_FMT_YUV422P10BE: case AV_PIX_FMT_YUV422P10LE: case AV_PIX_FMT_YUV444P9BE: case AV_PIX_FMT_YUV444P9LE: case AV_PIX_FMT_YUV444P10BE: case AV_PIX_FMT_YUV444P10LE: case AV_PIX_FMT_YUV422P9BE: case AV_PIX_FMT_YUV422P9LE: case AV_PIX_FMT_GBRP9BE: case AV_PIX_FMT_GBRP9LE: case AV_PIX_FMT_GBRP10BE: case AV_PIX_FMT_GBRP10LE: case AV_PIX_FMT_GBRP16BE: case AV_PIX_FMT_GBRP16LE: case AV_PIX_FMT_YUVA420P9BE: case AV_PIX_FMT_YUVA420P9LE: case AV_PIX_FMT_YUVA422P9BE: case AV_PIX_FMT_YUVA422P9LE: case AV_PIX_FMT_YUVA444P9BE: case AV_PIX_FMT_YUVA444P9LE: case AV_PIX_FMT_YUVA420P10BE: case AV_PIX_FMT_YUVA420P10LE: result = 0; } return result; } #endif // USE_HWD HandBrake-0.10.2/libhb/encavcodec.c0000664000175200017520000004603612463330511017364 0ustar handbrakehandbrake/* encavcodec.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "hb_dict.h" #include "hbffmpeg.h" /* * The frame info struct remembers information about each frame across calls * to avcodec_encode_video. Since frames are uniquely identified by their * frame number, we use this as an index. * * The size of the array is chosen so that two frames can't use the same * slot during the encoder's max frame delay (set by the standard as 16 * frames) and so that, up to some minimum frame rate, frames are guaranteed * to map to * different slots. */ #define FRAME_INFO_SIZE 32 #define FRAME_INFO_MASK (FRAME_INFO_SIZE - 1) struct hb_work_private_s { hb_job_t * job; AVCodecContext * context; FILE * file; int frameno_in; int frameno_out; hb_buffer_t * delay_head; hb_buffer_t * delay_tail; int64_t dts_delay; struct { int64_t start; int64_t duration; } frame_info[FRAME_INFO_SIZE]; }; int encavcodecInit( hb_work_object_t *, hb_job_t * ); int encavcodecWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); void encavcodecClose( hb_work_object_t * ); hb_work_object_t hb_encavcodec = { WORK_ENCAVCODEC, "FFMPEG encoder (libavcodec)", encavcodecInit, encavcodecWork, encavcodecClose }; int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) { AVCodec * codec; AVCodecContext * context; AVRational fps; hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; pv->job = job; switch ( w->codec_param ) { case AV_CODEC_ID_MPEG4: { hb_log("encavcodecInit: MPEG-4 ASP encoder"); } break; case AV_CODEC_ID_MPEG2VIDEO: { hb_log("encavcodecInit: MPEG-2 encoder"); } break; case AV_CODEC_ID_VP8: { hb_log("encavcodecInit: VP8 encoder"); } break; default: { hb_error("encavcodecInit: unsupported encoder!"); return 1; } } codec = avcodec_find_encoder( w->codec_param ); if( !codec ) { hb_log( "encavcodecInit: avcodec_find_encoder " "failed" ); return 1; } context = avcodec_alloc_context3( codec ); // Set things in context that we will allow the user to // override with advanced settings. if( job->pass == 2 ) { hb_interjob_t * interjob = hb_interjob_get( job->h ); fps.den = interjob->vrate_base; fps.num = interjob->vrate; } else { fps.den = job->vrate_base; fps.num = job->vrate; } // If the fps.num is 27000000, there's a good chance this is // a standard rate that we have in our hb_video_rates table. // Because of rounding errors and approximations made while // measuring framerate, the actual value may not be exact. So // we look for rates that are "close" and make an adjustment // to fps.den. if (fps.num == 27000000) { const hb_rate_t *video_framerate = NULL; while ((video_framerate = hb_video_framerate_get_next(video_framerate)) != NULL) { if (abs(fps.den - video_framerate->rate) < 10) { fps.den = video_framerate->rate; break; } } } hb_reduce(&fps.den, &fps.num, fps.den, fps.num); // Check that the framerate is supported. If not, pick the closest. // The mpeg2 codec only supports a specific list of frame rates. if (codec->supported_framerates) { AVRational supported_fps; supported_fps = codec->supported_framerates[av_find_nearest_q_idx(fps, codec->supported_framerates)]; if (supported_fps.num != fps.num || supported_fps.den != fps.den) { hb_log( "encavcodec: framerate %d / %d is not supported. Using %d / %d.", fps.num, fps.den, supported_fps.num, supported_fps.den ); fps = supported_fps; } } else if ((fps.num & ~0xFFFF) || (fps.den & ~0xFFFF)) { // This may only be required for mpeg4 video. But since // our only supported options are mpeg2 and mpeg4, there is // no need to check codec type. hb_log( "encavcodec: truncating framerate %d / %d", fps.num, fps.den ); while ((fps.num & ~0xFFFF) || (fps.den & ~0xFFFF)) { fps.num >>= 1; fps.den >>= 1; } } context->time_base.den = fps.num; context->time_base.num = fps.den; context->gop_size = 10 * (int)( (double)job->vrate / (double)job->vrate_base + 0.5 ); /* place job->encoder_options in an hb_dict_t for convenience */ hb_dict_t * lavc_opts = NULL; if (job->encoder_options != NULL && *job->encoder_options) { lavc_opts = hb_encopts_to_dict(job->encoder_options, job->vcodec); } /* iterate through lavc_opts and have avutil parse the options for us */ AVDictionary * av_opts = NULL; hb_dict_entry_t * entry = NULL; while( ( entry = hb_dict_next( lavc_opts, entry ) ) ) { /* Here's where the strings are passed to avutil for parsing. */ av_dict_set( &av_opts, entry->key, entry->value, 0 ); } hb_dict_free( &lavc_opts ); // Now set the things in context that we don't want to allow // the user to override. if( job->vquality < 0.0 ) { /* Average bitrate */ context->bit_rate = 1000 * job->vbitrate; // ffmpeg's mpeg2 encoder requires that the bit_rate_tolerance be >= // bitrate * fps context->bit_rate_tolerance = context->bit_rate * av_q2d(fps) + 1; } else { /* Constant quantizer */ // These settings produce better image quality than // what was previously used context->flags |= CODEC_FLAG_QSCALE; context->global_quality = FF_QP2LAMBDA * job->vquality + 0.5; //Set constant quality for libvpx if ( w->codec_param == AV_CODEC_ID_VP8 ) { char quality[7]; snprintf(quality, 7, "%.2f", job->vquality); av_dict_set( &av_opts, "crf", quality, 0 ); //Setting the deadline to good and cpu-used to 0 //causes the encoder to balance video quality and //encode time, with a bias to video quality. av_dict_set( &av_opts, "deadline", "good", 0); av_dict_set( &av_opts, "cpu-used", "0", 0); //This value was chosen to make the bitrate high enough //for libvpx to "turn off" the maximum bitrate feature //that is normally applied to constant quality. context->bit_rate = job->width*job->height*( (double)fps.num / (double)fps.den ); hb_log( "encavcodec: encoding at CQ %.2f", job->vquality ); } else { hb_log( "encavcodec: encoding at constant quantizer %d", context->global_quality ); } } context->width = job->width; context->height = job->height; context->pix_fmt = AV_PIX_FMT_YUV420P; if( job->anamorphic.mode ) { context->sample_aspect_ratio.num = job->anamorphic.par_width; context->sample_aspect_ratio.den = job->anamorphic.par_height; hb_log( "encavcodec: encoding with stored aspect %d/%d", job->anamorphic.par_width, job->anamorphic.par_height ); } if( job->mux & HB_MUX_MASK_MP4 ) { context->flags |= CODEC_FLAG_GLOBAL_HEADER; } if( job->grayscale ) { context->flags |= CODEC_FLAG_GRAY; } if( job->pass != 0 && job->pass != -1 ) { char filename[1024]; memset( filename, 0, 1024 ); hb_get_tempory_filename( job->h, filename, "ffmpeg.log" ); if( job->pass == 1 ) { pv->file = hb_fopen(filename, "wb"); context->flags |= CODEC_FLAG_PASS1; } else { int size; char * log; pv->file = hb_fopen(filename, "rb"); fseek( pv->file, 0, SEEK_END ); size = ftell( pv->file ); fseek( pv->file, 0, SEEK_SET ); log = malloc( size + 1 ); log[size] = '\0'; fread( log, size, 1, pv->file ); fclose( pv->file ); pv->file = NULL; context->flags |= CODEC_FLAG_PASS2; context->stats_in = log; } } if (hb_avcodec_open(context, codec, &av_opts, HB_FFMPEG_THREADS_AUTO)) { hb_log( "encavcodecInit: avcodec_open failed" ); } // avcodec_open populates the opts dictionary with the // things it didn't recognize. AVDictionaryEntry *t = NULL; while( ( t = av_dict_get( av_opts, "", t, AV_DICT_IGNORE_SUFFIX ) ) ) { hb_log( "encavcodecInit: Unknown avcodec option %s", t->key ); } av_dict_free( &av_opts ); pv->context = context; job->areBframes = 0; if ( context->has_b_frames ) { job->areBframes = 1; } if( ( job->mux & HB_MUX_MASK_MP4 ) && job->pass != 1 ) { w->config->mpeg4.length = context->extradata_size; memcpy( w->config->mpeg4.bytes, context->extradata, context->extradata_size ); } return 0; } /*********************************************************************** * Close *********************************************************************** * **********************************************************************/ void encavcodecClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; if( pv->context && pv->context->codec ) { hb_deep_log( 2, "encavcodec: closing libavcodec" ); avcodec_flush_buffers( pv->context ); hb_avcodec_close( pv->context ); av_free( pv->context ); } if( pv->file ) { fclose( pv->file ); } free( pv ); w->private_data = NULL; } /* * see comments in definition of 'frame_info' in pv struct for description * of what these routines are doing. */ static void save_frame_info( hb_work_private_t * pv, hb_buffer_t * in ) { int i = pv->frameno_in & FRAME_INFO_MASK; pv->frame_info[i].start = in->s.start; pv->frame_info[i].duration = in->s.stop - in->s.start; } static int64_t get_frame_start( hb_work_private_t * pv, int64_t frameno ) { int i = frameno & FRAME_INFO_MASK; return pv->frame_info[i].start; } static int64_t get_frame_duration( hb_work_private_t * pv, int64_t frameno ) { int i = frameno & FRAME_INFO_MASK; return pv->frame_info[i].duration; } static void compute_dts_offset( hb_work_private_t * pv, hb_buffer_t * buf ) { if ( pv->job->areBframes ) { if ( ( pv->frameno_in ) == pv->job->areBframes ) { pv->dts_delay = buf->s.start; pv->job->config.h264.init_delay = pv->dts_delay; } } } static uint8_t convert_pict_type( int pict_type, char pkt_flag_key, uint16_t* sflags ) { uint8_t retval = 0; switch ( pict_type ) { case AV_PICTURE_TYPE_P: { retval = HB_FRAME_P; } break; case AV_PICTURE_TYPE_B: { retval = HB_FRAME_B; } break; case AV_PICTURE_TYPE_S: { retval = HB_FRAME_P; } break; case AV_PICTURE_TYPE_SP: { retval = HB_FRAME_P; } break; case AV_PICTURE_TYPE_BI: case AV_PICTURE_TYPE_SI: case AV_PICTURE_TYPE_I: { *sflags |= HB_FRAME_REF; if ( pkt_flag_key ) { retval = HB_FRAME_IDR; } else { retval = HB_FRAME_I; } } break; default: { if ( pkt_flag_key ) { //buf->s.flags |= HB_FRAME_REF; *sflags |= HB_FRAME_REF; retval = HB_FRAME_KEY; } else { retval = HB_FRAME_REF; } } break; } return retval; } // Generate DTS by rearranging PTS in this sequence: // pts0 - delay, pts1 - delay, pts2 - delay, pts1, pts2, pts3... // // Where pts0 - ptsN are in decoded monotonically increasing presentation // order and delay == pts1 (1 being the number of frames the decoder must // delay before it has suffecient information to decode). The number of // frames to delay is set by job->areBframes, so it is configurable. // This guarantees that DTS <= PTS for any frame. // // This is similar to how x264 generates DTS static hb_buffer_t * process_delay_list( hb_work_private_t * pv, hb_buffer_t * buf ) { if ( pv->job->areBframes ) { // Has dts_delay been set yet? if ( pv->frameno_in <= pv->job->areBframes ) { // dts_delay not yet set. queue up buffers till it is set. if ( pv->delay_tail == NULL ) { pv->delay_head = pv->delay_tail = buf; } else { pv->delay_tail->next = buf; pv->delay_tail = buf; } return NULL; } // We have dts_delay. Apply it to any queued buffers renderOffset // and return all queued buffers. if ( pv->delay_tail == NULL && buf != NULL ) { // Use the cached frame info to get the start time of Nth frame // Note that start Nth frame != start time this buffer since the // output buffers have rearranged start times. if (pv->frameno_out < pv->job->areBframes) { int64_t start = get_frame_start( pv, pv->frameno_out ); buf->s.renderOffset = start - pv->dts_delay; } else { buf->s.renderOffset = get_frame_start(pv, pv->frameno_out - pv->job->areBframes); } pv->frameno_out++; return buf; } else { pv->delay_tail->next = buf; buf = pv->delay_head; while ( buf ) { // Use the cached frame info to get the start time of Nth frame // Note that start Nth frame != start time this buffer since the // output buffers have rearranged start times. if (pv->frameno_out < pv->job->areBframes) { int64_t start = get_frame_start( pv, pv->frameno_out ); buf->s.renderOffset = start - pv->dts_delay; } else { buf->s.renderOffset = get_frame_start(pv, pv->frameno_out - pv->job->areBframes); } buf = buf->next; pv->frameno_out++; } buf = pv->delay_head; pv->delay_head = pv->delay_tail = NULL; return buf; } } else if ( buf ) { buf->s.renderOffset = buf->s.start; return buf; } return NULL; } /*********************************************************************** * Work *********************************************************************** * **********************************************************************/ int encavcodecWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_job_t * job = pv->job; AVFrame * frame; hb_buffer_t * in = *buf_in, * buf; char final_flushing_call = (in->size <= 0); if ( final_flushing_call ) { //make a flushing call to encode for codecs that can encode out of order /* EOF on input - send it downstream & say we're done */ *buf_in = NULL; frame = NULL; } else { frame = av_frame_alloc(); frame->data[0] = in->plane[0].data; frame->data[1] = in->plane[1].data; frame->data[2] = in->plane[2].data; frame->linesize[0] = in->plane[0].stride; frame->linesize[1] = in->plane[1].stride; frame->linesize[2] = in->plane[2].stride; // For constant quality, setting the quality in AVCodecContext // doesn't do the trick. It must be set in the AVFrame. frame->quality = pv->context->global_quality; // Remember info about this frame that we need to pass across // the avcodec_encode_video call (since it reorders frames). save_frame_info( pv, in ); compute_dts_offset( pv, in ); // Bizarro ffmpeg appears to require the input AVFrame.pts to be // set to a frame number. Setting it to an actual pts causes // jerky video. // frame->pts = in->s.start; frame->pts = pv->frameno_in++; } if ( pv->context->codec ) { int ret; AVPacket pkt; int got_packet; char still_flushing = final_flushing_call; hb_buffer_t* buf_head = NULL; hb_buffer_t* buf_last = NULL; do { av_init_packet(&pkt); /* Should be way too large */ buf = hb_video_buffer_init( job->width, job->height ); pkt.data = buf->data; pkt.size = buf->alloc; ret = avcodec_encode_video2( pv->context, &pkt, frame, &got_packet ); if ( ret < 0 || pkt.size <= 0 || !got_packet ) { hb_buffer_close( &buf ); still_flushing = 0; } else { int64_t frameno = pkt.pts; buf->size = pkt.size; buf->s.start = get_frame_start( pv, frameno ); buf->s.duration = get_frame_duration( pv, frameno ); buf->s.stop = buf->s.stop + buf->s.duration; buf->s.flags &= ~HB_FRAME_REF; buf->s.frametype = convert_pict_type( pv->context->coded_frame->pict_type, pkt.flags & AV_PKT_FLAG_KEY, &buf->s.flags ); buf = process_delay_list( pv, buf ); if (buf_head == NULL) { buf_head = buf; } else { buf_last->next = buf; } buf_last = buf; } /* Write stats */ if (job->pass == 1 && pv->context->stats_out != NULL) { fprintf( pv->file, "%s", pv->context->stats_out ); } } while (still_flushing); if (buf_last != NULL && final_flushing_call) { buf_last->next = in; buf = buf_head; } else if (final_flushing_call) { buf = in; } } else { buf = NULL; hb_error( "encavcodec: codec context has uninitialized codec; skipping frame" ); } av_frame_free( &frame ); *buf_out = buf; return final_flushing_call? HB_WORK_DONE : HB_WORK_OK; } HandBrake-0.10.2/libhb/detelecine.c0000664000175200017520000006315712463330511017376 0ustar handbrakehandbrake/* detelecine.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "hbffmpeg.h" /* * * PULLUP DEFINITIONS * */ #define PULLUP_FMT_Y 1 #define PULLUP_HAVE_BREAKS 1 #define PULLUP_HAVE_AFFINITY 2 #define PULLUP_BREAK_LEFT 1 #define PULLUP_BREAK_RIGHT 2 #define PULLUP_ABS( a ) (((a)^((a)>>31))-((a)>>31)) #ifndef PIC_FLAG_REPEAT_FIRST_FIELD #define PIC_FLAG_REPEAT_FIRST_FIELD 256 #endif struct pullup_buffer { int lock[2]; unsigned char **planes; int *size; }; struct pullup_field { int parity; struct pullup_buffer *buffer; unsigned int flags; int breaks; int affinity; int *diffs; int *comb; int *var; struct pullup_field *prev, *next; }; struct pullup_frame { int lock; int length; int parity; struct pullup_buffer **ifields, *ofields[2]; struct pullup_buffer *buffer; }; struct pullup_context { /* Public interface */ int format; int nplanes; int *bpp, *w, *h, *stride, *background; unsigned int cpu; int junk_left, junk_right, junk_top, junk_bottom; int verbose; int metric_plane; int strict_breaks; int strict_pairs; int parity; /* Internal data */ struct pullup_field *first, *last, *head; struct pullup_buffer *buffers; int nbuffers; int (*diff)(unsigned char *, unsigned char *, int); int (*comb)(unsigned char *, unsigned char *, int); int (*var)(unsigned char *, unsigned char *, int); int metric_w, metric_h, metric_len, metric_offset; struct pullup_frame *frame; }; /* * * DETELECINE FILTER DEFINITIONS * */ struct hb_filter_private_s { struct pullup_context * pullup_ctx; int pullup_fakecount; int pullup_skipflag; }; static int hb_detelecine_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int hb_detelecine_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static void hb_detelecine_close( hb_filter_object_t * filter ); hb_filter_object_t hb_filter_detelecine = { .id = HB_FILTER_DETELECINE, .enforce_order = 1, .name = "Detelecine (pullup)", .settings = NULL, .init = hb_detelecine_init, .work = hb_detelecine_work, .close = hb_detelecine_close, }; /* * * PULLUP STATIC FUNCTIONS * */ static int pullup_diff_y( unsigned char *a, unsigned char * b, int s ) { int i, j, diff = 0; for( i = 4; i; i-- ) { for( j = 0; j < 8; j++ ) { diff += PULLUP_ABS( a[j]-b[j] ); } a+=s; b+=s; } return diff; } static int pullup_licomb_y( unsigned char * a, unsigned char * b, int s ) { int i, j, diff = 0; for( i = 4; i; i-- ) { for( j = 0; j < 8; j++ ) { diff += PULLUP_ABS( (a[j]<<1) - b[j-s] - b[j] ) + PULLUP_ABS( (b[j]<<1) - a[j] - a[j+s] ); } a+=s; b+=s; } return diff; } static int pullup_var_y( unsigned char * a, unsigned char * b, int s ) { int i, j, var = 0; for( i = 3; i; i-- ) { for( j = 0; j < 8; j++ ) { var += PULLUP_ABS( a[j]-a[j+s] ); } a+=s; b+=s; } return 4*var; } static void pullup_alloc_metrics( struct pullup_context * c, struct pullup_field * f ) { f->diffs = calloc( c->metric_len, sizeof(int) ); f->comb = calloc( c->metric_len, sizeof(int) ); f->var = calloc( c->metric_len, sizeof(int) ); } static void pullup_compute_metric( struct pullup_context * c, struct pullup_field * fa, int pa, struct pullup_field * fb, int pb, int (* func)( unsigned char *, unsigned char *, int), int * dest ) { unsigned char *a, *b; int x, y; int mp = c->metric_plane; int xstep = c->bpp[mp]; int ystep = c->stride[mp]<<3; int s = c->stride[mp]<<1; /* field stride */ int w = c->metric_w*xstep; if( !fa->buffer || !fb->buffer ) return; /* Shortcut for duplicate fields (e.g. from RFF flag) */ if( fa->buffer == fb->buffer && pa == pb ) { memset( dest, 0, c->metric_len * sizeof(int) ); return; } a = fa->buffer->planes[mp] + pa * c->stride[mp] + c->metric_offset; b = fb->buffer->planes[mp] + pb * c->stride[mp] + c->metric_offset; for( y = c->metric_h; y; y-- ) { for( x = 0; x < w; x += xstep ) { *dest++ = func( a + x, b + x, s ); } a += ystep; b += ystep; } } static struct pullup_field * pullup_make_field_queue( struct pullup_context * c, int len ) { struct pullup_field * head, * f; f = head = calloc( 1, sizeof(struct pullup_field) ); pullup_alloc_metrics( c, f ); for ( ; len > 0; len-- ) { f->next = calloc( 1, sizeof(struct pullup_field) ); f->next->prev = f; f = f->next; pullup_alloc_metrics( c, f ); } f->next = head; head->prev = f; return head; } static void pullup_check_field_queue( struct pullup_context * c ) { if( c->head->next == c->first ) { struct pullup_field *f = calloc( 1, sizeof(struct pullup_field) ); pullup_alloc_metrics( c, f ); f->prev = c->head; f->next = c->first; c->head->next = f; c->first->prev = f; } } static void pullup_copy_field( struct pullup_context * c, struct pullup_buffer * dest, struct pullup_buffer * src, int parity ) { int i, j; unsigned char *d, *s; for( i = 0; i < c->nplanes; i++ ) { s = src->planes[i] + parity*c->stride[i]; d = dest->planes[i] + parity*c->stride[i]; for( j = c->h[i]>>1; j; j-- ) { memcpy( d, s, c->stride[i] ); s += c->stride[i]<<1; d += c->stride[i]<<1; } } } static int pullup_queue_length( struct pullup_field * begin, struct pullup_field * end ) { int count = 1; struct pullup_field * f; if( !begin || !end ) return 0; for( f = begin; f != end; f = f->next ) count++; return count; } static int pullup_find_first_break( struct pullup_field * f, int max ) { int i; for( i = 0; i < max; i++ ) { if( f->breaks & PULLUP_BREAK_RIGHT || f->next->breaks & PULLUP_BREAK_LEFT ) { return i+1; } f = f->next; } return 0; } static void pullup_compute_breaks( struct pullup_context * c, struct pullup_field * f0 ) { int i; struct pullup_field *f1 = f0->next; struct pullup_field *f2 = f1->next; struct pullup_field *f3 = f2->next; int l, max_l=0, max_r=0; if( f0->flags & PULLUP_HAVE_BREAKS ) return; f0->flags |= PULLUP_HAVE_BREAKS; /* Special case when fields are 100% identical */ if( f0->buffer == f2->buffer && f1->buffer != f3->buffer ) { f2->breaks |= PULLUP_BREAK_RIGHT; return; } if( f0->buffer != f2->buffer && f1->buffer == f3->buffer ) { f1->breaks |= PULLUP_BREAK_LEFT; return; } for( i = 0; i < c->metric_len; i++ ) { l = f2->diffs[i] - f3->diffs[i]; if( l > max_l) max_l = l; if( -l > max_r) max_r = -l; } /* Don't get tripped up when differences are mostly quant error */ if( max_l + max_r < 128 ) return; if( max_l > 4*max_r ) f1->breaks |= PULLUP_BREAK_LEFT; if( max_r > 4*max_l ) f2->breaks |= PULLUP_BREAK_RIGHT; } static void pullup_compute_affinity( struct pullup_context * c, struct pullup_field * f ) { int i; int max_l = 0, max_r = 0, l; if( f->flags & PULLUP_HAVE_AFFINITY ) { return; } f->flags |= PULLUP_HAVE_AFFINITY; if( f->buffer == f->next->next->buffer ) { f->affinity = 1; f->next->affinity = 0; f->next->next->affinity = -1; f->next->flags |= PULLUP_HAVE_AFFINITY; f->next->next->flags |= PULLUP_HAVE_AFFINITY; return; } for( i = 0; i < c->metric_len; i++ ) { int lv = f->prev->var[i]; int rv = f->next->var[i]; int v = f->var[i]; int lc = f->comb[i] - (v+lv) + PULLUP_ABS( v-lv ); int rc = f->next->comb[i] - (v+rv) + PULLUP_ABS( v-rv ); lc = (lc > 0) ? lc : 0; rc = (rc > 0) ? rc : 0; l = lc - rc; if( l > max_l ) max_l = l; if( -l > max_r ) max_r = -l; } if( max_l + max_r < 64 ) { return; } if( max_r > 6*max_l ) { f->affinity = -1; } else if( max_l > 6*max_r ) { f->affinity = 1; } } static void pullup_foo( struct pullup_context * c ) { struct pullup_field * f = c->first; int i, n = pullup_queue_length (f, c->last ); for( i = 0; i < n-1; i++ ) { if( i < n-3 ) pullup_compute_breaks( c, f ); pullup_compute_affinity( c, f ); f = f->next; } } static int pullup_decide_frame_length( struct pullup_context * c ) { struct pullup_field *f0 = c->first; struct pullup_field *f1 = f0->next; struct pullup_field *f2 = f1->next; int l; if( pullup_queue_length( c->first, c->last ) < 4 ) { return 0; } pullup_foo( c ); if( f0->affinity == -1 ) return 1; l = pullup_find_first_break( f0, 3 ); if( l == 1 && c->strict_breaks < 0 ) l = 0; switch (l) { case 1: if ( c->strict_breaks < 1 && f0->affinity == 1 && f1->affinity == -1 ) { return 2; } else { return 1; } case 2: /* FIXME: strictly speaking, f0->prev is no longer valid... :) */ if( c->strict_pairs && (f0->prev->breaks & PULLUP_BREAK_RIGHT) && (f2->breaks & PULLUP_BREAK_LEFT) && (f0->affinity != 1 || f1->affinity != -1) ) { return 1; } if( f1->affinity == 1 ) { return 1; } else { return 2; } case 3: if( f2->affinity == 1 ) { return 2; } else { return 3; } default: /* 9 possibilities covered before switch */ if( f1->affinity == 1 ) { return 1; /* covers 6 */ } else if( f1->affinity == -1 ) { return 2; /* covers 6 */ } else if( f2->affinity == -1 ) { /* covers 2 */ if( f0->affinity == 1 ) { return 3; } else { return 1; } } else { return 2; /* the remaining 6 */ } } } static void pullup_print_aff_and_breaks(struct pullup_context * c, struct pullup_field * f ) { int i; struct pullup_field * f0 = f; const char aff_l[] = "+..", aff_r[] = "..+"; hb_log( "affinity: " ); for( i = 0; i < 4; i++ ) { printf( "%c%d%c", aff_l[1+f->affinity], i, aff_r[1+f->affinity] ); f = f->next; } f = f0; printf("\nbreaks: "); for( i = 0; i < 4; i++ ) { printf( "%c%d%c", f->breaks & PULLUP_BREAK_LEFT ? '|' : '.', i, f->breaks & PULLUP_BREAK_RIGHT ? '|' : '.' ); f = f->next; } printf("\n"); } /* * * PULLUP CONTEXT FUNCTIONS * */ struct pullup_context * pullup_alloc_context( void ) { struct pullup_context * c; c = calloc( 1, sizeof(struct pullup_context)) ; return c; } void pullup_preinit_context( struct pullup_context * c ) { c->bpp = calloc( c->nplanes, sizeof(int) ); c->w = calloc( c->nplanes, sizeof(int) ); c->h = calloc( c->nplanes, sizeof(int) ); c->stride = calloc( c->nplanes, sizeof(int) ); c->background = calloc( c->nplanes, sizeof(int) ); } void pullup_init_context( struct pullup_context * c ) { int mp = c->metric_plane; if ( c->nbuffers < 10 ) { c->nbuffers = 10; } c->buffers = calloc( c->nbuffers, sizeof (struct pullup_buffer) ); c->metric_w = (c->w[mp] - ((c->junk_left + c->junk_right) << 3)) >> 3; c->metric_h = (c->h[mp] - ((c->junk_top + c->junk_bottom) << 1)) >> 3; c->metric_offset = c->junk_left*c->bpp[mp] + (c->junk_top<<1)*c->stride[mp]; c->metric_len = c->metric_w * c->metric_h; c->head = pullup_make_field_queue( c, 8 ); c->frame = calloc( 1, sizeof (struct pullup_frame) ); c->frame->ifields = calloc( 3, sizeof (struct pullup_buffer *) ); if( c->format == PULLUP_FMT_Y ) { c->diff = pullup_diff_y; c->comb = pullup_licomb_y; c->var = pullup_var_y; } } void pullup_free_context( struct pullup_context * c ) { struct pullup_field * f; free( c->buffers ); f = c->head->next; while( f != c->head ) { free( f->diffs ); free( f->comb ); f = f->next; free( f->prev ); } free( f->diffs ); free( f->comb ); free(f); free( c->frame ); free( c ); } /* * * PULLUP BUFFER FUNCTIONS * */ static void pullup_alloc_buffer( struct pullup_context * c, struct pullup_buffer * b ) { int i; if( b->planes ) return; b->planes = calloc( c->nplanes, sizeof(unsigned char *) ); b->size = calloc( c->nplanes, sizeof(int) ); for ( i = 0; i < c->nplanes; i++ ) { b->size[i] = c->h[i] * c->stride[i]; b->planes[i] = malloc(b->size[i]); /* Deal with idiotic 128=0 for chroma: */ memset( b->planes[i], c->background[i], b->size[i] ); } } struct pullup_buffer * pullup_lock_buffer( struct pullup_buffer * b, int parity ) { if( !b ) return 0; if( (parity+1) & 1 ) b->lock[0]++; if( (parity+1) & 2 ) b->lock[1]++; return b; } void pullup_release_buffer( struct pullup_buffer * b, int parity ) { if( !b ) return; if( (parity+1) & 1 ) b->lock[0]--; if( (parity+1) & 2 ) b->lock[1]--; } struct pullup_buffer * pullup_get_buffer( struct pullup_context * c, int parity ) { int i; /* Try first to get the sister buffer for the previous field */ if( parity < 2 && c->last && parity != c->last->parity && !c->last->buffer->lock[parity]) { pullup_alloc_buffer( c, c->last->buffer ); return pullup_lock_buffer( c->last->buffer, parity ); } /* Prefer a buffer with both fields open */ for( i = 0; i < c->nbuffers; i++ ) { if( c->buffers[i].lock[0] ) continue; if( c->buffers[i].lock[1] ) continue; pullup_alloc_buffer( c, &c->buffers[i] ); return pullup_lock_buffer( &c->buffers[i], parity ); } if( parity == 2 ) return 0; /* Search for any half-free buffer */ for( i = 0; i < c->nbuffers; i++ ) { if( ((parity+1) & 1) && c->buffers[i].lock[0] ) continue; if( ((parity+1) & 2) && c->buffers[i].lock[1] ) continue; pullup_alloc_buffer( c, &c->buffers[i] ); return pullup_lock_buffer( &c->buffers[i], parity ); } return 0; } /* * * PULLUP FRAME FUNCTIONS * */ struct pullup_frame * pullup_get_frame( struct pullup_context * c ) { int i; struct pullup_frame * fr = c->frame; int n = pullup_decide_frame_length( c ); int aff = c->first->next->affinity; if ( !n ) return 0; if ( fr->lock ) return 0; if ( c->verbose ) { pullup_print_aff_and_breaks(c, c->first); printf("duration: %d \n", n); } fr->lock++; fr->length = n; fr->parity = c->first->parity; fr->buffer = 0; for( i = 0; i < n; i++ ) { /* We cheat and steal the buffer without release+relock */ fr->ifields[i] = c->first->buffer; c->first->buffer = 0; c->first = c->first->next; } if( n == 1 ) { fr->ofields[fr->parity] = fr->ifields[0]; fr->ofields[fr->parity^1] = 0; } else if( n == 2 ) { fr->ofields[fr->parity] = fr->ifields[0]; fr->ofields[fr->parity^1] = fr->ifields[1]; } else if( n == 3 ) { if( aff == 0 ) { aff = (fr->ifields[0] == fr->ifields[1]) ? -1 : 1; } fr->ofields[fr->parity] = fr->ifields[1+aff]; fr->ofields[fr->parity^1] = fr->ifields[1]; } pullup_lock_buffer( fr->ofields[0], 0 ); pullup_lock_buffer( fr->ofields[1], 1 ); if( fr->ofields[0] == fr->ofields[1] ) { fr->buffer = fr->ofields[0]; pullup_lock_buffer(fr->buffer, 2); return fr; } return fr; } void pullup_pack_frame( struct pullup_context * c, struct pullup_frame * fr) { int i; if (fr->buffer) return; if (fr->length < 2) return; /* FIXME: deal with this */ for( i = 0; i < 2; i++ ) { if( fr->ofields[i]->lock[i^1] ) continue; fr->buffer = fr->ofields[i]; pullup_lock_buffer(fr->buffer, 2); pullup_copy_field( c, fr->buffer, fr->ofields[i^1], i^1 ); return; } fr->buffer = pullup_get_buffer( c, 2 ); pullup_copy_field( c, fr->buffer, fr->ofields[0], 0 ); pullup_copy_field( c, fr->buffer, fr->ofields[1], 1 ); } void pullup_release_frame( struct pullup_frame * fr ) { int i; for( i = 0; i < fr->length; i++ ) { pullup_release_buffer( fr->ifields[i], fr->parity ^ (i&1) ); } pullup_release_buffer( fr->ofields[0], 0 ); pullup_release_buffer( fr->ofields[1], 1 ); if (fr->buffer) pullup_release_buffer( fr->buffer, 2 ); fr->lock--; } /* * * PULLUP FIELD FUNCTIONS * */ void pullup_submit_field( struct pullup_context * c, struct pullup_buffer * b, int parity ) { struct pullup_field * f; /* Grow the circular list if needed */ pullup_check_field_queue( c ); /* Cannot have two fields of same parity in a row; drop the new one */ if( c->last && c->last->parity == parity ) return; f = c->head; f->parity = parity; f->buffer = pullup_lock_buffer( b, parity ); f->flags = 0; f->breaks = 0; f->affinity = 0; pullup_compute_metric( c, f, parity, f->prev->prev, parity, c->diff, f->diffs ); pullup_compute_metric( c, parity?f->prev:f, 0, parity?f:f->prev, 1, c->comb, f->comb ); pullup_compute_metric( c, f, parity, f, -1, c->var, f->var ); /* Advance the circular list */ if( !c->first ) c->first = c->head; c->last = c->head; c->head = c->head->next; } void pullup_flush_fields( struct pullup_context * c ) { struct pullup_field * f; for( f = c->first; f && f != c->head; f = f->next ) { pullup_release_buffer( f->buffer, f->parity ); f->buffer = 0; } c->first = c->last = 0; } /* * * DETELECINE FILTER FUNCTIONS * */ static int hb_detelecine_init( hb_filter_object_t * filter, hb_filter_init_t * init ) { filter->private_data = calloc( sizeof(struct hb_filter_private_s), 1 ); hb_filter_private_t * pv = filter->private_data; struct pullup_context * ctx; pv->pullup_ctx = ctx = pullup_alloc_context(); ctx->junk_left = ctx->junk_right = 1; ctx->junk_top = ctx->junk_bottom = 4; ctx->strict_breaks = -1; ctx->metric_plane = 0; ctx->parity = -1; if( filter->settings ) { sscanf( filter->settings, "%d:%d:%d:%d:%d:%d:%d", &ctx->junk_left, &ctx->junk_right, &ctx->junk_top, &ctx->junk_bottom, &ctx->strict_breaks, &ctx->metric_plane, &ctx->parity ); } ctx->format = PULLUP_FMT_Y; ctx->nplanes = 4; pullup_preinit_context( ctx ); ctx->bpp[0] = ctx->bpp[1] = ctx->bpp[2] = 8; ctx->background[1] = ctx->background[2] = 128; ctx->w[0] = init->width; ctx->h[0] = hb_image_height( init->pix_fmt, init->height, 0 ); ctx->stride[0] = hb_image_stride( init->pix_fmt, init->width, 0 ); ctx->w[1] = init->width >> 1; ctx->h[1] = hb_image_height( init->pix_fmt, init->height, 1 ); ctx->stride[1] = hb_image_stride( init->pix_fmt, init->width, 1 ); ctx->w[1] = init->width >> 1; ctx->h[2] = hb_image_height( init->pix_fmt, init->height, 2 ); ctx->stride[2] = hb_image_stride( init->pix_fmt, init->width, 2 ); ctx->w[3] = ((init->width+15)/16) * ((init->height+15)/16); ctx->h[3] = 2; ctx->stride[3] = ctx->w[3]; #if 0 ctx->verbose = 1; #endif pullup_init_context( ctx ); pv->pullup_fakecount = 1; pv->pullup_skipflag = 0; return 0; } static void hb_detelecine_close( hb_filter_object_t * filter ) { hb_filter_private_t * pv = filter->private_data; if( !pv ) { return; } if( pv->pullup_ctx ) { pullup_free_context( pv->pullup_ctx ); } free( pv ); filter->private_data = NULL; } static int hb_detelecine_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in, * out; if ( in->size <= 0 ) { *buf_out = in; *buf_in = NULL; return HB_FILTER_DONE; } struct pullup_context * ctx = pv->pullup_ctx; struct pullup_buffer * buf; struct pullup_frame * frame; buf = pullup_get_buffer( ctx, 2 ); if( !buf ) { frame = pullup_get_frame( ctx ); pullup_release_frame( frame ); hb_log( "Could not get buffer from pullup!" ); return HB_FILTER_FAILED; } /* Copy input buffer into pullup buffer */ memcpy( buf->planes[0], in->plane[0].data, buf->size[0] ); memcpy( buf->planes[1], in->plane[1].data, buf->size[1] ); memcpy( buf->planes[2], in->plane[2].data, buf->size[2] ); /* Submit buffer fields based on buffer flags. Detelecine assumes BFF when the TFF flag isn't present. */ int parity = 1; if( in->s.flags & PIC_FLAG_TOP_FIELD_FIRST ) { /* Source signals TFF */ parity = 0; } else if( ctx->parity == 0 ) { /* Many non-MPEG-2 sources lack parity flags even though they are TFF, so this allow users to override. */ parity = 0; } if( ctx->parity == 1 ) { /* Override autodetected parity with BFF */ parity = 1; } pullup_submit_field( ctx, buf, parity ); pullup_submit_field( ctx, buf, parity^1 ); if( in->s.flags & PIC_FLAG_REPEAT_FIRST_FIELD ) { pullup_submit_field( ctx, buf, parity ); } pullup_release_buffer( buf, 2 ); /* Get frame and check if pullup is ready */ frame = pullup_get_frame( ctx ); if( !frame ) { if( pv->pullup_fakecount ) { pv->pullup_fakecount--; *buf_in = NULL; *buf_out = in; goto output_frame; } else { goto discard_frame; } } /* Check to see if frame should be dropped */ if( frame->length < 2 ) { pullup_release_frame( frame ); frame = pullup_get_frame( ctx ); if (!frame) { goto discard_frame; } if( frame->length < 2 ) { pullup_release_frame( frame ); if( !(in->s.flags & PIC_FLAG_REPEAT_FIRST_FIELD) ) { goto discard_frame; } frame = pullup_get_frame( ctx ); if( !frame ) { goto discard_frame; } if( frame->length < 2 ) { pullup_release_frame( frame ); goto discard_frame; } } } /* Check to see if frame buffer is ready for export */ if( !frame->buffer ) { pullup_pack_frame( ctx, frame ); } out = hb_video_buffer_init( in->f.width, in->f.height ); /* Copy pullup frame buffer into output buffer */ memcpy( out->plane[0].data, frame->buffer->planes[0], frame->buffer->size[0] ); memcpy( out->plane[1].data, frame->buffer->planes[1], frame->buffer->size[1] ); memcpy( out->plane[2].data, frame->buffer->planes[2], frame->buffer->size[2] ); pullup_release_frame( frame ); out->s = in->s; hb_buffer_move_subs( out, in ); *buf_out = out; output_frame: return HB_FILTER_OK; /* This and all discard_frame calls shown above are the result of me restoring the functionality in pullup that huevos_rancheros disabled because HB couldn't handle it. */ discard_frame: return HB_FILTER_OK; } HandBrake-0.10.2/libhb/deccc608sub.h0000664000175200017520000000632112463330511017301 0ustar handbrakehandbrake/* deccc608sub.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ /* * From ccextractor... */ #ifndef __DECCC608SUB_H__ #define __DECCC608SUB_H__ #include "common.h" struct s_write; #define CC608_SCREEN_WIDTH 32 enum cc_modes { MODE_POPUP = 0, MODE_ROLLUP_2 = 1, MODE_ROLLUP_3 = 2, MODE_ROLLUP_4 = 3, MODE_TEXT = 4 }; enum color_code { COL_WHITE = 0, COL_GREEN = 1, COL_BLUE = 2, COL_CYAN = 3, COL_RED = 4, COL_YELLOW = 5, COL_MAGENTA = 6, COL_USERDEFINED = 7 }; enum font_bits { FONT_REGULAR = 0, FONT_ITALICS = 1, FONT_UNDERLINED = 2, FONT_UNDERLINED_ITALICS = 3 }; #define FONT_STYLE_MASK FONT_UNDERLINED_ITALICS struct eia608_screen // A CC buffer { unsigned char characters[15][33]; unsigned char colors[15][33]; unsigned char fonts[15][33]; // Extra char at the end for a 0 int row_used[15]; // Any data in row? int empty; // Buffer completely empty? int dirty; // Flag indicates buffer has changed since written }; struct eia608 { struct eia608_screen buffer1; struct eia608_screen buffer2; int cursor_row, cursor_column; int visible_buffer; int ssa_counter; // Number of subs currently written int screenfuls_counter; // Number of meaningful screenfuls written int64_t current_visible_start_ms; // At what time did the current visible buffer became so? enum cc_modes mode; unsigned char last_c1, last_c2; int channel; // Currently selected channel unsigned char color; // Color we are currently using to write unsigned char font; // Font we are currently using to write int rollup_base_row; }; struct s_write { struct eia608 *data608; FILE *fh; unsigned char *subline; int new_sentence; int new_channel; int in_xds_mode; hb_buffer_t *hb_buffer; hb_buffer_t *hb_last_buffer; uint64_t last_pts; unsigned char *enc_buffer; // Generic general purpose buffer unsigned enc_buffer_used; unsigned enc_buffer_capacity; int clear_sub_needed; // Indicates that we need to send a null // subtitle to clear the current subtitle int rollup_cr; // Flag indicates if CR command performed by rollup int direct_rollup; int line; // SSA line number int width; int height; int crop[4]; uint8_t prev_font_style; uint8_t prev_font_color; }; enum command_code { COM_UNKNOWN = 0, COM_ERASEDISPLAYEDMEMORY = 1, COM_RESUMECAPTIONLOADING = 2, COM_ENDOFCAPTION = 3, COM_TABOFFSET1 = 4, COM_TABOFFSET2 = 5, COM_TABOFFSET3 = 6, COM_ROLLUP2 = 7, COM_ROLLUP3 = 8, COM_ROLLUP4 = 9, COM_CARRIAGERETURN = 10, COM_ERASENONDISPLAYEDMEMORY = 11, COM_BACKSPACE = 12, COM_RESUMETEXTDISPLAY = 13 }; enum encoding_type { ENC_UNICODE = 0, ENC_LATIN_1 = 1, ENC_UTF_8 = 2 }; enum output_format { OF_RAW = 0, OF_SRT = 1, OF_SAMI = 2, OF_TRANSCRIPT = 3, OF_RCWT = 4 }; #endif // __DECCC608SUB_H__ HandBrake-0.10.2/libhb/reader.c0000664000175200017520000007047112511064504016534 0ustar handbrakehandbrake/* reader.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" static int hb_reader_init( hb_work_object_t * w, hb_job_t * job ); static void hb_reader_close( hb_work_object_t * w ); hb_work_object_t hb_reader = { WORK_READER, "Reader", hb_reader_init, NULL, hb_reader_close, NULL, NULL }; typedef struct { int startup; double average; // average time between packets double filtered_average; // average time between packets int64_t last; // last timestamp seen on this stream int id; // stream id int is_audio; // != 0 if this is an audio stream int valid; // Stream timing is not valid until next scr. } stream_timing_t; struct hb_work_private_s { hb_job_t * job; hb_title_t * title; volatile int * die; hb_bd_t * bd; hb_dvd_t * dvd; hb_stream_t * stream; stream_timing_t *stream_timing; int64_t scr_offset; int sub_scr_set; hb_psdemux_t demux; int scr_changes; uint32_t sequence; uint8_t st_slots; // size (in slots) of stream_timing array uint8_t saw_video; // != 0 if we've seen video uint8_t saw_audio; // != 0 if we've seen audio int start_found; // found pts_to_start point int64_t pts_to_start; uint64_t st_first; uint64_t duration; hb_fifo_t * fifos[100]; }; /*********************************************************************** * Local prototypes **********************************************************************/ static hb_fifo_t ** GetFifoForId( hb_work_private_t * r, int id ); static void UpdateState( hb_work_private_t * r, int64_t start); /*********************************************************************** * hb_reader_init *********************************************************************** * **********************************************************************/ static int hb_reader_open( hb_work_private_t * r ) { if ( r->title->type == HB_BD_TYPE ) { if ( !( r->bd = hb_bd_init( r->title->path ) ) ) return 1; } else if ( r->title->type == HB_DVD_TYPE ) { if ( !( r->dvd = hb_dvd_init( r->title->path ) ) ) return 1; } else if ( r->title->type == HB_STREAM_TYPE || r->title->type == HB_FF_STREAM_TYPE ) { if ( !( r->stream = hb_stream_open( r->title->path, r->title, 0 ) ) ) return 1; } else { // Unknown type, should never happen return 1; } return 0; } static int hb_reader_init( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t * r; r = calloc( sizeof( hb_work_private_t ), 1 ); w->private_data = r; r->job = job; r->title = job->title; r->die = job->die; r->sequence = 0; r->st_slots = 4; r->stream_timing = calloc( sizeof(stream_timing_t), r->st_slots ); r->stream_timing[0].id = r->title->video_id; r->stream_timing[0].average = 90000. * (double)job->vrate_base / (double)job->vrate; r->stream_timing[0].filtered_average = r->stream_timing[0].average; r->stream_timing[0].last = -r->stream_timing[0].average; r->stream_timing[0].valid = 1; r->stream_timing[0].startup = 10; r->stream_timing[1].id = -1; r->demux.last_scr = AV_NOPTS_VALUE; if ( !job->pts_to_start ) r->start_found = 1; else { // The frame at the actual start time may not be an i-frame // so can't be decoded without starting a little early. // sync.c will drop early frames. // Starting a little over 10 seconds early r->pts_to_start = MAX(0, job->pts_to_start - 1000000); } if (job->pts_to_stop) { r->duration = job->pts_to_start + job->pts_to_stop; } else if (job->frame_to_stop) { int frames = job->frame_to_start + job->frame_to_stop; r->duration = (int64_t)frames * job->title->rate_base * 90000 / job->title->rate; } else { hb_chapter_t *chapter; int ii; r->duration = 0; for (ii = job->chapter_start; ii < job->chapter_end; ii++) { chapter = hb_list_item( job->title->list_chapter, ii - 1); r->duration += chapter->duration; } } // The stream needs to be open before starting the reader thead // to prevent a race with decoders that may share information // with the reader. Specifically avcodec needs this. if ( hb_reader_open( r ) ) { free( r->stream_timing ); free( r ); return 1; } return 0; } static void hb_reader_close( hb_work_object_t * w ) { hb_work_private_t * r = w->private_data; if (r->bd) { hb_bd_stop( r->bd ); hb_bd_close( &r->bd ); } else if (r->dvd) { hb_dvd_stop( r->dvd ); hb_dvd_close( &r->dvd ); } else if (r->stream) { hb_stream_close(&r->stream); } if ( r->stream_timing ) { free( r->stream_timing ); } free( r ); } static void push_buf( const hb_work_private_t *r, hb_fifo_t *fifo, hb_buffer_t *buf ) { while ( !*r->die && !r->job->done ) { if ( hb_fifo_full_wait( fifo ) ) { hb_fifo_push( fifo, buf ); buf = NULL; break; } } if ( buf ) { hb_buffer_close( &buf ); } } static int is_audio( hb_work_private_t *r, int id ) { int i; hb_audio_t *audio; for( i = 0; ( audio = hb_list_item( r->title->list_audio, i ) ); ++i ) { if ( audio->id == id ) { return 1; } } return 0; } static int is_subtitle( hb_work_private_t *r, int id ) { int i; hb_subtitle_t *sub; for( i = 0; ( sub = hb_list_item( r->title->list_subtitle, i ) ); ++i ) { if ( sub->id == id ) { return 1; } } return 0; } // The MPEG STD (Standard Target Decoder) essentially requires that we keep // per-stream timing so that when there's a timing discontinuity we can // seemlessly join packets on either side of the discontinuity. This join // requires that we know the timestamp of the previous packet and the // average inter-packet time (since we position the new packet at the end // of the previous packet). The next four routines keep track of this // per-stream timing. // find or create the per-stream timing state for 'buf' static stream_timing_t *id_to_st( hb_work_private_t *r, const hb_buffer_t *buf, int valid ) { stream_timing_t *st = r->stream_timing; while ( st->id != buf->s.id && st->id != -1) { ++st; } // if we haven't seen this stream add it. if ( st->id == -1 ) { // we keep the steam timing info in an array with some power-of-two // number of slots. If we don't have two slots left (one for our new // entry plus one for the "-1" eol) we need to expand the array. int slot = st - r->stream_timing; if ( slot + 1 >= r->st_slots ) { r->st_slots *= 2; r->stream_timing = realloc( r->stream_timing, r->st_slots * sizeof(*r->stream_timing) ); st = r->stream_timing + slot; } st->id = buf->s.id; st->average = 30.*90.; st->filtered_average = st->average; st->startup = 10; st->last = -st->average; if ( ( st->is_audio = is_audio( r, buf->s.id ) ) != 0 ) { r->saw_audio = 1; } st[1].id = -1; st->valid = valid; } return st; } // update the average inter-packet time of the stream associated with 'buf' // using a recursive low-pass filter with a 16 packet time constant. static void update_ipt( hb_work_private_t *r, const hb_buffer_t *buf ) { stream_timing_t *st = id_to_st( r, buf, 1 ); if (buf->s.renderOffset == AV_NOPTS_VALUE) { st->last += st->filtered_average; return; } double dt = buf->s.renderOffset - st->last; // Protect against spurious bad timestamps // timestamps should only move forward and by reasonable increments if ( dt > 0 && dt < 5 * 90000LL ) { if( st->startup ) { st->average += ( dt - st->average ) * (1./4.); st->startup--; } else { st->average += ( dt - st->average ) * (1./32.); } // Ignore outliers if (dt < 1.5 * st->average) { st->filtered_average += ( dt - st->filtered_average ) * (1./32.); } } st->last = buf->s.renderOffset; st->valid = 1; } // use the per-stream state associated with 'buf' to compute a new scr_offset // such that 'buf' will follow the previous packet of this stream separated // by the average packet time of the stream. static void new_scr_offset( hb_work_private_t *r, hb_buffer_t *buf ) { stream_timing_t *st = id_to_st( r, buf, 1 ); int64_t last; if ( !st->valid ) { // !valid means we've not received any previous data // for this stream. There is no 'last' packet time. // So approximate it with video's last time. last = r->stream_timing[0].last; st->valid = 1; } else { last = st->last; } int64_t nxt = last + st->filtered_average; r->scr_offset = buf->s.renderOffset - nxt; // This log is handy when you need to debug timing problems... //hb_log("id %x last %"PRId64" avg %g nxt %"PRId64" renderOffset %"PRId64 // " scr_offset %"PRId64"", // buf->s.id, last, st->filtered_average, nxt, // buf->s.renderOffset, r->scr_offset); r->scr_changes = r->demux.scr_changes; } /*********************************************************************** * ReaderFunc *********************************************************************** * **********************************************************************/ void ReadLoop( void * _w ) { hb_work_object_t * w = _w; hb_work_private_t * r = w->private_data; hb_fifo_t ** fifos; hb_buffer_t * buf = NULL; hb_list_t * list; int n; int chapter = -1; int chapter_end = r->job->chapter_end; uint8_t done = 0; if (r->bd) { if( !hb_bd_start( r->bd, r->title ) ) { hb_bd_close( &r->bd ); return; } if ( r->job->start_at_preview ) { // XXX code from DecodePreviews - should go into its own routine hb_bd_seek( r->bd, (float)r->job->start_at_preview / ( r->job->seek_points ? ( r->job->seek_points + 1.0 ) : 11.0 ) ); } else if ( r->job->pts_to_start ) { // Note, bd seeks always put us to an i-frame. no need // to start decoding early using r->pts_to_start hb_bd_seek_pts( r->bd, r->job->pts_to_start ); r->duration -= r->job->pts_to_start; r->job->pts_to_start = 0; r->start_found = 1; } else { hb_bd_seek_chapter( r->bd, r->job->chapter_start ); } if (r->job->angle > 1) { hb_bd_set_angle( r->bd, r->job->angle - 1 ); } } else if (r->dvd) { /* * XXX this code is a temporary hack that should go away if/when * chapter merging goes away in libhb/dvd.c * map the start and end chapter numbers to on-media chapter * numbers since chapter merging could cause the handbrake numbers * to diverge from the media numbers and, if our chapter_end is after * a media chapter that got merged, we'll stop ripping too early. */ int start = r->job->chapter_start; hb_chapter_t *chap = hb_list_item( r->job->list_chapter, chapter_end - 1 ); chapter_end = chap->index; if (start > 1) { chap = hb_list_item( r->job->list_chapter, start - 1 ); start = chap->index; } /* end chapter mapping XXX */ if( !hb_dvd_start( r->dvd, r->title, start ) ) { hb_dvd_close( &r->dvd ); return; } if (r->job->angle) { hb_dvd_set_angle( r->dvd, r->job->angle ); } if ( r->job->start_at_preview ) { // XXX code from DecodePreviews - should go into its own routine hb_dvd_seek( r->dvd, (float)r->job->start_at_preview / ( r->job->seek_points ? ( r->job->seek_points + 1.0 ) : 11.0 ) ); } } else if ( r->stream && r->job->start_at_preview ) { // XXX code from DecodePreviews - should go into its own routine hb_stream_seek( r->stream, (float)( r->job->start_at_preview - 1 ) / ( r->job->seek_points ? ( r->job->seek_points + 1.0 ) : 11.0 ) ); } else if ( r->stream && r->job->pts_to_start ) { int64_t pts_to_start = r->job->pts_to_start; // Find out what the first timestamp of the stream is // and then seek to the appropriate offset from it if ( ( buf = hb_stream_read( r->stream ) ) ) { if (buf->s.start != AV_NOPTS_VALUE) { pts_to_start += buf->s.start; } } if ( hb_stream_seek_ts( r->stream, pts_to_start ) >= 0 ) { // Seek takes us to the nearest I-frame before the timestamp // that we want. So we will retrieve the start time of the // first packet we get, subtract that from pts_to_start, and // inspect the reset of the frames in sync. r->start_found = 2; r->duration -= r->job->pts_to_start; r->job->pts_to_start = pts_to_start; hb_buffer_close(&buf); } // hb_stream_seek_ts does nothing for TS streams and will return // an error. In this case, the current buf remains valid and // gets processed below. } else if( r->stream ) { /* * Standard stream, seek to the starting chapter, if set, and track the * end chapter so that we end at the right time. */ int start = r->job->chapter_start; hb_chapter_t *chap = hb_list_item( r->job->list_chapter, chapter_end - 1 ); chapter_end = chap->index; if (start > 1) { chap = hb_list_item( r->job->list_chapter, start - 1 ); start = chap->index; } /* * Seek to the start chapter. */ hb_stream_seek_chapter( r->stream, start ); } list = hb_list_init(); while(!*r->die && !r->job->done && !done) { if (r->bd) chapter = hb_bd_chapter( r->bd ); else if (r->dvd) chapter = hb_dvd_chapter( r->dvd ); else if (r->stream) chapter = hb_stream_chapter( r->stream ); if( chapter < 0 ) { hb_log( "reader: end of the title reached" ); break; } if( chapter > chapter_end ) { hb_log( "reader: end of chapter %d (media %d) reached at media chapter %d", r->job->chapter_end, chapter_end, chapter ); break; } if (buf == NULL) { if (r->bd) { if( (buf = hb_bd_read( r->bd )) == NULL ) { break; } } else if (r->dvd) { if( (buf = hb_dvd_read( r->dvd )) == NULL ) { break; } } else if (r->stream) { if ( (buf = hb_stream_read( r->stream )) == NULL ) { break; } } } (hb_demux[r->title->demuxer])( buf, list, &r->demux ); while( ( buf = hb_list_item( list, 0 ) ) ) { hb_list_rem( list, buf ); fifos = GetFifoForId( r, buf->s.id ); if (fifos && r->stream && r->start_found == 2 ) { // We will inspect the timestamps of each frame in sync // to skip from this seek point to the timestamp we // want to start at. if (buf->s.start != AV_NOPTS_VALUE && buf->s.start < r->job->pts_to_start) { r->job->pts_to_start -= buf->s.start; } else if ( buf->s.start >= r->job->pts_to_start ) { r->job->pts_to_start = 0; } r->start_found = 1; } if ( fifos && ! r->saw_video && !r->job->indepth_scan ) { // The first data packet with a PTS from an audio or video stream // that we're decoding defines 'time zero'. Discard packets until // we get one. if (buf->s.start != AV_NOPTS_VALUE && buf->s.renderOffset != AV_NOPTS_VALUE && (buf->s.id == r->title->video_id || is_audio( r, buf->s.id))) { // force a new scr offset computation r->scr_changes = r->demux.scr_changes - 1; // create a stream state if we don't have one so the // offset will get computed correctly. id_to_st( r, buf, 1 ); r->saw_video = 1; hb_log( "reader: first SCR %"PRId64" id 0x%x DTS %"PRId64, r->demux.last_scr, buf->s.id, buf->s.renderOffset ); } else { fifos = NULL; } } if ( r->job->indepth_scan || fifos ) { if ( buf->s.renderOffset != AV_NOPTS_VALUE ) { if ( r->scr_changes != r->demux.scr_changes ) { // This is the first audio or video packet after an SCR // change. Compute a new scr offset that would make this // packet follow the last of this stream with the // correct average spacing. stream_timing_t *st = id_to_st( r, buf, 0 ); // if this is the video stream and we don't have // audio yet or this is an audio stream // generate a new scr if ( st->is_audio || ( st == r->stream_timing && !r->saw_audio ) ) { new_scr_offset( r, buf ); r->sub_scr_set = 0; } else { // defer the scr change until we get some // audio since audio has a timestamp per // frame but video & subtitles don't. Clear // the timestamps so the decoder will generate // them from the frame durations. if (is_subtitle(r, buf->s.id) && buf->s.start != AV_NOPTS_VALUE) { if (!r->sub_scr_set) { // We can't generate timestamps in the // subtitle decoder as we can for // audio & video. So we need to make // the closest guess that we can // for the subtitles start time here. int64_t last = r->stream_timing[0].last; r->scr_offset = buf->s.start - last; r->sub_scr_set = 1; } } else { buf->s.start = AV_NOPTS_VALUE; buf->s.renderOffset = AV_NOPTS_VALUE; } } } } if ( buf->s.start != AV_NOPTS_VALUE ) { int64_t start = buf->s.start - r->scr_offset; if (!r->start_found || r->job->indepth_scan) { UpdateState( r, start ); } if (r->job->indepth_scan && r->job->pts_to_stop && start >= r->pts_to_start + r->job->pts_to_stop) { // sync normally would terminate p-to-p // but sync doesn't run during indepth scan hb_log( "reader: reached pts %"PRId64", exiting early", start ); done = 1; break; } if (!r->start_found && start >= r->pts_to_start) { // pts_to_start point found r->start_found = 1; if (r->stream) { // libav multi-threaded decoders can get into // a bad state if the initial data is not // decodable. So try to improve the chances of // a good start by waiting for an initial iframe hb_stream_set_need_keyframe(r->stream, 1); hb_buffer_close( &buf ); continue; } } // This log is handy when you need to debug timing problems //hb_log("id %x scr_offset %"PRId64 // " start %"PRId64" --> %"PRId64"", // buf->s.id, r->scr_offset, buf->s.start, // buf->s.start - r->scr_offset); buf->s.start -= r->scr_offset; if ( buf->s.stop != AV_NOPTS_VALUE ) { buf->s.stop -= r->scr_offset; } } if ( buf->s.renderOffset != AV_NOPTS_VALUE ) { // This packet is referenced to the same SCR as the last. // Adjust timestamp to remove the System Clock Reference // offset then update the average inter-packet time // for this stream. buf->s.renderOffset -= r->scr_offset; update_ipt( r, buf ); } #if 0 // JAS: This was added to fix a rare "audio time went backward" // sync error I found in one sample. But it has a bad side // effect on DVDs, causing frequent "adding silence" sync // errors. So I am disabling it. else { update_ipt( r, buf ); } #endif } if( fifos ) { if ( !r->start_found ) { hb_buffer_close( &buf ); continue; } buf->sequence = r->sequence++; /* if there are mutiple output fifos, send a copy of the * buffer down all but the first (we have to not ship the * original buffer or we'll race with the thread that's * consuming the buffer & inject garbage into the data stream). */ for( n = 1; fifos[n] != NULL; n++) { hb_buffer_t *buf_copy = hb_buffer_init( buf->size ); buf_copy->s = buf->s; memcpy( buf_copy->data, buf->data, buf->size ); push_buf( r, fifos[n], buf_copy ); } push_buf( r, fifos[0], buf ); buf = NULL; } else { hb_buffer_close( &buf ); } } } // send empty buffers downstream to video & audio decoders to signal we're done. if( !*r->die && !r->job->done ) { push_buf( r, r->job->fifo_mpeg2, hb_buffer_init(0) ); hb_audio_t *audio; for( n = 0; (audio = hb_list_item( r->job->list_audio, n)); ++n ) { if ( audio->priv.fifo_in ) push_buf( r, audio->priv.fifo_in, hb_buffer_init(0) ); } hb_subtitle_t *subtitle; for( n = 0; (subtitle = hb_list_item( r->job->list_subtitle, n)); ++n ) { if ( subtitle->fifo_in && subtitle->source == VOBSUB) push_buf( r, subtitle->fifo_in, hb_buffer_init(0) ); } } hb_list_empty( &list ); hb_log( "reader: done. %d scr changes", r->demux.scr_changes ); if ( r->demux.dts_drops ) { hb_log( "reader: %d drops because DTS out of range", r->demux.dts_drops ); } } static void UpdateState( hb_work_private_t * r, int64_t start) { hb_state_t state; uint64_t now; double avg; now = hb_get_date(); if( !r->st_first ) { r->st_first = now; } #define p state.param.working if ( !r->job->indepth_scan ) { state.state = HB_STATE_SEARCHING; p.progress = (float) start / (float) r->job->pts_to_start; } else { state.state = HB_STATE_WORKING; p.progress = (float) start / (float) r->duration; } if( p.progress > 1.0 ) { p.progress = 1.0; } p.rate_cur = 0.0; p.rate_avg = 0.0; if (now > r->st_first) { int eta; avg = 1000.0 * (double)start / (now - r->st_first); if ( !r->job->indepth_scan ) eta = ( r->job->pts_to_start - start ) / avg; else eta = ( r->duration - start ) / avg; p.hours = eta / 3600; p.minutes = ( eta % 3600 ) / 60; p.seconds = eta % 60; } else { p.hours = -1; p.minutes = -1; p.seconds = -1; } #undef p hb_set_state( r->job->h, &state ); } /*********************************************************************** * GetFifoForId *********************************************************************** * **********************************************************************/ static hb_fifo_t ** GetFifoForId( hb_work_private_t * r, int id ) { hb_job_t * job = r->job; hb_title_t * title = job->title; hb_audio_t * audio; hb_subtitle_t * subtitle; int i, n, count; memset(r->fifos, 0, sizeof(r->fifos)); if( id == title->video_id ) { if (job->indepth_scan && !job->frame_to_stop) { /* * Ditch the video here during the indepth scan until * we can improve the MPEG2 decode performance. * * But if we specify a stop frame, we must decode the * frames in order to count them. */ return NULL; } else { r->fifos[0] = job->fifo_mpeg2; return r->fifos; } } count = hb_list_count( job->list_subtitle ); count = count > 99 ? 99 : count; for( i = n = 0; i < count; i++ ) { subtitle = hb_list_item( job->list_subtitle, i ); if (id == subtitle->id) { /* pass the subtitles to be processed */ r->fifos[n++] = subtitle->fifo_in; } } if ( n != 0 ) { return r->fifos; } if( !job->indepth_scan ) { for( i = n = 0; i < hb_list_count( job->list_audio ); i++ ) { audio = hb_list_item( job->list_audio, i ); if( id == audio->id ) { r->fifos[n++] = audio->priv.fifo_in; } } if( n != 0 ) { return r->fifos; } } return NULL; } HandBrake-0.10.2/libhb/encavcodecaudio.c0000664000175200017520000003566512463330511020414 0ustar handbrakehandbrake/* encavcodecaudio.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "hbffmpeg.h" struct hb_work_private_s { hb_job_t * job; AVCodecContext * context; int out_discrete_channels; int samples_per_frame; unsigned long max_output_bytes; unsigned long input_samples; uint8_t * output_buf; uint8_t * input_buf; hb_list_t * list; AVAudioResampleContext *avresample; }; static int encavcodecaInit( hb_work_object_t *, hb_job_t * ); static int encavcodecaWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); static void encavcodecaClose( hb_work_object_t * ); hb_work_object_t hb_encavcodeca = { WORK_ENCAVCODEC_AUDIO, "AVCodec Audio encoder (libavcodec)", encavcodecaInit, encavcodecaWork, encavcodecaClose }; static int encavcodecaInit(hb_work_object_t *w, hb_job_t *job) { AVCodec *codec; AVCodecContext *context; hb_audio_t *audio = w->audio; hb_work_private_t *pv = calloc(1, sizeof(hb_work_private_t)); w->private_data = pv; pv->job = job; pv->list = hb_list_init(); // channel count, layout and matrix encoding int matrix_encoding; uint64_t channel_layout = hb_ff_mixdown_xlat(audio->config.out.mixdown, &matrix_encoding); pv->out_discrete_channels = hb_mixdown_get_discrete_channel_count(audio->config.out.mixdown); // default settings and options AVDictionary *av_opts = NULL; const char *codec_name = NULL; enum AVCodecID codec_id = AV_CODEC_ID_NONE; enum AVSampleFormat sample_fmt = AV_SAMPLE_FMT_FLTP; int bits_per_raw_sample = 0; int profile = FF_PROFILE_UNKNOWN; // override with encoder-specific values switch (audio->config.out.codec) { case HB_ACODEC_AC3: codec_id = AV_CODEC_ID_AC3; if (matrix_encoding != AV_MATRIX_ENCODING_NONE) av_dict_set(&av_opts, "dsur_mode", "on", 0); break; case HB_ACODEC_FDK_AAC: case HB_ACODEC_FDK_HAAC: codec_name = "libfdk_aac"; sample_fmt = AV_SAMPLE_FMT_S16; bits_per_raw_sample = 16; switch (audio->config.out.codec) { case HB_ACODEC_FDK_HAAC: profile = FF_PROFILE_AAC_HE; break; default: profile = FF_PROFILE_AAC_LOW; break; } // Libav's libfdk-aac wrapper expects back channels for 5.1 // audio, and will error out unless we translate the layout if (channel_layout == AV_CH_LAYOUT_5POINT1) channel_layout = AV_CH_LAYOUT_5POINT1_BACK; break; case HB_ACODEC_FFAAC: codec_name = "aac"; av_dict_set(&av_opts, "stereo_mode", "ms_off", 0); break; case HB_ACODEC_FFFLAC: case HB_ACODEC_FFFLAC24: codec_id = AV_CODEC_ID_FLAC; switch (audio->config.out.codec) { case HB_ACODEC_FFFLAC24: sample_fmt = AV_SAMPLE_FMT_S32; bits_per_raw_sample = 24; break; default: sample_fmt = AV_SAMPLE_FMT_S16; bits_per_raw_sample = 16; break; } break; default: hb_error("encavcodecaInit: unsupported codec (0x%x)", audio->config.out.codec); return 1; } if (codec_name != NULL) { codec = avcodec_find_encoder_by_name(codec_name); if (codec == NULL) { hb_error("encavcodecaInit: avcodec_find_encoder_by_name(%s) failed", codec_name); return 1; } } else { codec = avcodec_find_encoder(codec_id); if (codec == NULL) { hb_error("encavcodecaInit: avcodec_find_encoder(%d) failed", codec_id); return 1; } } // allocate the context and apply the settings context = avcodec_alloc_context3(codec); hb_ff_set_sample_fmt(context, codec, sample_fmt); context->bits_per_raw_sample = bits_per_raw_sample; context->profile = profile; context->channel_layout = channel_layout; context->channels = pv->out_discrete_channels; context->sample_rate = audio->config.out.samplerate; if (audio->config.out.bitrate > 0) { context->bit_rate = audio->config.out.bitrate * 1000; } else if (audio->config.out.quality >= 0) { context->global_quality = audio->config.out.quality * FF_QP2LAMBDA; context->flags |= CODEC_FLAG_QSCALE; } if (audio->config.out.compression_level >= 0) { context->compression_level = audio->config.out.compression_level; } // For some codecs, libav requires the following flag to be set // so that it fills extradata with global header information. // If this flag is not set, it inserts the data into each // packet instead. context->flags |= CODEC_FLAG_GLOBAL_HEADER; if (hb_avcodec_open(context, codec, &av_opts, 0)) { hb_error("encavcodecaInit: hb_avcodec_open() failed"); return 1; } // avcodec_open populates the opts dictionary with the // things it didn't recognize. AVDictionaryEntry *t = NULL; while ((t = av_dict_get(av_opts, "", t, AV_DICT_IGNORE_SUFFIX))) { hb_log("encavcodecaInit: Unknown avcodec option %s", t->key); } av_dict_free(&av_opts); pv->context = context; audio->config.out.samples_per_frame = pv->samples_per_frame = context->frame_size; pv->input_samples = context->frame_size * context->channels; pv->input_buf = malloc(pv->input_samples * sizeof(float)); // Some encoders in libav (e.g. fdk-aac) fail if the output buffer // size is not some minumum value. 8K seems to be enough :( pv->max_output_bytes = MAX(FF_MIN_BUFFER_SIZE, (pv->input_samples * av_get_bytes_per_sample(context->sample_fmt))); // sample_fmt conversion if (context->sample_fmt != AV_SAMPLE_FMT_FLT) { pv->output_buf = malloc(pv->max_output_bytes); pv->avresample = avresample_alloc_context(); if (pv->avresample == NULL) { hb_error("encavcodecaInit: avresample_alloc_context() failed"); return 1; } av_opt_set_int(pv->avresample, "in_sample_fmt", AV_SAMPLE_FMT_FLT, 0); av_opt_set_int(pv->avresample, "out_sample_fmt", context->sample_fmt, 0); av_opt_set_int(pv->avresample, "in_channel_layout", 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"); avresample_free(&pv->avresample); return 1; } } else { pv->avresample = NULL; pv->output_buf = pv->input_buf; } if (context->extradata != NULL) { memcpy(w->config->extradata.bytes, context->extradata, context->extradata_size); w->config->extradata.length = context->extradata_size; } audio->config.out.delay = av_rescale_q(context->delay, context->time_base, (AVRational){1, 90000}); return 0; } /*********************************************************************** * Close *********************************************************************** * **********************************************************************/ // Some encoders (e.g. flac) require a final NULL encode in order to // finalize things. static void Finalize(hb_work_object_t *w) { hb_work_private_t *pv = w->private_data; // Finalize with NULL input needed by FLAC to generate md5sum // in context extradata // Prepare output packet AVPacket pkt; int got_packet; hb_buffer_t *buf = hb_buffer_init(pv->max_output_bytes); av_init_packet(&pkt); pkt.data = buf->data; pkt.size = buf->alloc; avcodec_encode_audio2(pv->context, &pkt, NULL, &got_packet); hb_buffer_close(&buf); // Then we need to recopy the header since it was modified if (pv->context->extradata != NULL) { memcpy(w->config->extradata.bytes, pv->context->extradata, pv->context->extradata_size); w->config->extradata.length = pv->context->extradata_size; } } static void encavcodecaClose(hb_work_object_t * w) { hb_work_private_t * pv = w->private_data; if (pv != NULL) { if (pv->context != NULL) { Finalize(w); hb_deep_log(2, "encavcodeca: closing libavcodec"); if (pv->context->codec != NULL) avcodec_flush_buffers(pv->context); hb_avcodec_close(pv->context); av_free( pv->context ); } if (pv->output_buf != NULL) { free(pv->output_buf); } if (pv->input_buf != NULL && pv->input_buf != pv->output_buf) { free(pv->input_buf); } pv->output_buf = pv->input_buf = NULL; if (pv->list != NULL) { hb_list_empty(&pv->list); } if (pv->avresample != NULL) { avresample_free(&pv->avresample); } free(pv); w->private_data = NULL; } } static hb_buffer_t* Encode(hb_work_object_t *w) { hb_work_private_t *pv = w->private_data; hb_audio_t *audio = w->audio; uint64_t pts, pos; if (hb_list_bytes(pv->list) < pv->input_samples * sizeof(float)) { return NULL; } hb_list_getbytes(pv->list, pv->input_buf, pv->input_samples * sizeof(float), &pts, &pos); // Prepare input frame int out_linesize; int out_size = av_samples_get_buffer_size(&out_linesize, pv->context->channels, pv->samples_per_frame, pv->context->sample_fmt, 1); AVFrame frame = { .nb_samples = pv->samples_per_frame, }; avcodec_fill_audio_frame(&frame, pv->context->channels, pv->context->sample_fmt, pv->output_buf, out_size, 1); if (pv->avresample != NULL) { int in_linesize; av_samples_get_buffer_size(&in_linesize, pv->context->channels, frame.nb_samples, AV_SAMPLE_FMT_FLT, 1); int out_samples = avresample_convert(pv->avresample, frame.extended_data, out_linesize, frame.nb_samples, &pv->input_buf, in_linesize, frame.nb_samples); if (out_samples != pv->samples_per_frame) { // we're not doing sample rate conversion, so this shouldn't happen hb_log("encavcodecaWork: avresample_convert() failed"); return NULL; } } // Libav requires that timebase of audio frames be in sample_rate units frame.pts = pts + (90000 * pos / (sizeof(float) * pv->out_discrete_channels * audio->config.out.samplerate)); frame.pts = av_rescale(frame.pts, pv->context->sample_rate, 90000); // Prepare output packet AVPacket pkt; int got_packet; hb_buffer_t *out = hb_buffer_init(pv->max_output_bytes); av_init_packet(&pkt); pkt.data = out->data; pkt.size = out->alloc; // Encode int ret = avcodec_encode_audio2(pv->context, &pkt, &frame, &got_packet); if (ret < 0) { hb_log("encavcodeca: avcodec_encode_audio failed"); hb_buffer_close(&out); return NULL; } if (got_packet && pkt.size) { out->size = pkt.size; // The output pts from libav is in context->time_base. Convert it back // to our timebase. out->s.start = av_rescale_q(pkt.pts, pv->context->time_base, (AVRational){1, 90000}); out->s.duration = (double)90000 * pv->samples_per_frame / audio->config.out.samplerate; out->s.stop = out->s.start + out->s.duration; out->s.type = AUDIO_BUF; out->s.frametype = HB_FRAME_AUDIO; } else { hb_buffer_close(&out); return Encode(w); } return out; } static hb_buffer_t * Flush( hb_work_object_t * w ) { hb_buffer_t *first, *buf, *last; first = last = buf = Encode( w ); while( buf ) { last = buf; buf->next = Encode( w ); buf = buf->next; } if( last ) { last->next = hb_buffer_init( 0 ); } else { first = hb_buffer_init( 0 ); } return first; } /*********************************************************************** * Work *********************************************************************** * **********************************************************************/ static int encavcodecaWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_buffer_t * in = *buf_in, * buf; if ( in->size <= 0 ) { /* EOF on input - send it downstream & say we're done */ *buf_out = Flush( w ); return HB_WORK_DONE; } if ( pv->context == NULL || pv->context->codec == NULL ) { // No encoder context. Nothing we can do. return HB_WORK_OK; } hb_list_add( pv->list, in ); *buf_in = NULL; *buf_out = buf = Encode( w ); while ( buf ) { buf->next = Encode( w ); buf = buf->next; } return HB_WORK_OK; } HandBrake-0.10.2/libhb/hbffmpeg.h0000664000175200017520000000240612463330511017046 0ustar handbrakehandbrake/* hbffmpeg.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libavutil/channel_layout.h" #include "libavutil/imgutils.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" #include "libavutil/avutil.h" #include "libavutil/downmix_info.h" #include "libswscale/swscale.h" #include "libavresample/avresample.h" #include "common.h" #define HB_FFMPEG_THREADS_AUTO (-1) // let hb_avcodec_open() decide thread_count void hb_avcodec_init(void); int hb_avcodec_open(AVCodecContext *, AVCodec *, AVDictionary **, int); int hb_avcodec_close(AVCodecContext *); uint64_t hb_ff_mixdown_xlat(int hb_mixdown, int *downmix_mode); void hb_ff_set_sample_fmt(AVCodecContext *, AVCodec *, enum AVSampleFormat); struct SwsContext* hb_sws_get_context(int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags); int hb_avpicture_fill(AVPicture *pic, hb_buffer_t *buf); HandBrake-0.10.2/libhb/rendersub.c0000664000175200017520000006541712463330511017267 0ustar handbrakehandbrake/* rendersub.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "hbffmpeg.h" #include struct hb_filter_private_s { // Common int crop[4]; int type; // VOBSUB hb_list_t * sub_list; // List of active subs // SSA ASS_Library * ssa; ASS_Renderer * renderer; ASS_Track * ssaTrack; uint8_t script_initialized; // SRT int line; hb_buffer_t * current_sub; }; // VOBSUB static int vobsub_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int vobsub_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static void vobsub_close( hb_filter_object_t * filter ); // SSA static int ssa_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int ssa_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static void ssa_close( hb_filter_object_t * filter ); // SRT static int textsub_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int textsub_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static void textsub_close( hb_filter_object_t * filter ); // PGS static int pgssub_init ( hb_filter_object_t * filter, hb_filter_init_t * init ); static int pgssub_work ( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static void pgssub_close( hb_filter_object_t * filter ); // Entry points static int hb_rendersub_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int hb_rendersub_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static void hb_rendersub_close( hb_filter_object_t * filter ); hb_filter_object_t hb_filter_render_sub = { .id = HB_FILTER_RENDER_SUB, .enforce_order = 1, .name = "Subtitle renderer", .settings = NULL, .init = hb_rendersub_init, .work = hb_rendersub_work, .close = hb_rendersub_close, }; static void blend( hb_buffer_t *dst, hb_buffer_t *src, int left, int top ) { int xx, yy; int ww, hh; int x0, y0; uint8_t *y_in, *y_out; uint8_t *u_in, *u_out; uint8_t *v_in, *v_out; uint8_t *a_in, alpha; x0 = y0 = 0; if( left < 0 ) { x0 = -left; } if( top < 0 ) { y0 = -top; } ww = src->f.width; if( src->f.width - x0 > dst->f.width - left ) { ww = dst->f.width - left + x0; } hh = src->f.height; if( src->f.height - y0 > dst->f.height - top ) { hh = dst->f.height - top + y0; } // Blend luma for( yy = y0; yy < hh; yy++ ) { y_in = src->plane[0].data + yy * src->plane[0].stride; y_out = dst->plane[0].data + ( yy + top ) * dst->plane[0].stride; a_in = src->plane[3].data + yy * src->plane[3].stride; for( xx = x0; xx < ww; xx++ ) { alpha = a_in[xx]; /* * Merge the luminance and alpha with the picture */ y_out[left + xx] = ( (uint16_t)y_out[left + xx] * ( 255 - alpha ) + (uint16_t)y_in[xx] * alpha ) / 255; } } // Blend U & V // Assumes source and dest are the same PIX_FMT int hshift = 0; int wshift = 0; if( dst->plane[1].height < dst->plane[0].height ) hshift = 1; if( dst->plane[1].width < dst->plane[0].width ) wshift = 1; for( yy = y0 >> hshift; yy < hh >> hshift; yy++ ) { u_in = src->plane[1].data + yy * src->plane[1].stride; u_out = dst->plane[1].data + ( yy + ( top >> hshift ) ) * dst->plane[1].stride; v_in = src->plane[2].data + yy * src->plane[2].stride; v_out = dst->plane[2].data + ( yy + ( top >> hshift ) ) * dst->plane[2].stride; a_in = src->plane[3].data + ( yy << hshift ) * src->plane[3].stride; for( xx = x0 >> wshift; xx < ww >> wshift; xx++ ) { alpha = a_in[xx << wshift]; // Blend averge U and alpha u_out[(left >> wshift) + xx] = ( (uint16_t)u_out[(left >> wshift) + xx] * ( 255 - alpha ) + (uint16_t)u_in[xx] * alpha ) / 255; // Blend V and alpha v_out[(left >> wshift) + xx] = ( (uint16_t)v_out[(left >> wshift) + xx] * ( 255 - alpha ) + (uint16_t)v_in[xx] * alpha ) / 255; } } } // Assumes that the input buffer has the same dimensions // as the original title diminsions static void ApplySub( hb_filter_private_t * pv, hb_buffer_t * buf, hb_buffer_t * sub ) { int top, left, margin_top, margin_percent; if ( !pv->ssa ) { /* * Percent of height of picture that form a margin that subtitles * should not be displayed within. */ margin_percent = 2; /* * If necessary, move the subtitle so it is not in a cropped zone. * When it won't fit, we center it so we lose as much on both ends. * Otherwise we try to leave a 20px or 2% margin around it. */ margin_top = ( ( buf->f.height - pv->crop[0] - pv->crop[1] ) * margin_percent ) / 100; if( margin_top > 20 ) { /* * A maximum margin of 20px regardless of height of the picture. */ margin_top = 20; } if( sub->f.height > buf->f.height - pv->crop[0] - pv->crop[1] - ( margin_top * 2 ) ) { /* * The subtitle won't fit in the cropped zone, so center * it vertically so we fit in as much as we can. */ top = pv->crop[0] + ( buf->f.height - pv->crop[0] - pv->crop[1] - sub->f.height ) / 2; } else if( sub->f.y < pv->crop[0] + margin_top ) { /* * The subtitle fits in the cropped zone, but is currently positioned * within our top margin, so move it outside of our margin. */ top = pv->crop[0] + margin_top; } else if( sub->f.y > buf->f.height - pv->crop[1] - margin_top - sub->f.height ) { /* * The subtitle fits in the cropped zone, and is not within the top * margin but is within the bottom margin, so move it to be above * the margin. */ top = buf->f.height - pv->crop[1] - margin_top - sub->f.height; } else { /* * The subtitle is fine where it is. */ top = sub->f.y; } if( sub->f.width > buf->f.width - pv->crop[2] - pv->crop[3] - 40 ) left = pv->crop[2] + ( buf->f.width - pv->crop[2] - pv->crop[3] - sub->f.width ) / 2; else if( sub->f.x < pv->crop[2] + 20 ) left = pv->crop[2] + 20; else if( sub->f.x > buf->f.width - pv->crop[3] - 20 - sub->f.width ) left = buf->f.width - pv->crop[3] - 20 - sub->f.width; else left = sub->f.x; } else { top = sub->f.y; left = sub->f.x; } blend( buf, sub, left, top ); } // Assumes that the input buffer has the same dimensions // as the original title diminsions static void ApplyVOBSubs( hb_filter_private_t * pv, hb_buffer_t * buf ) { int ii; hb_buffer_t *sub, *next; for( ii = 0; ii < hb_list_count(pv->sub_list); ) { sub = hb_list_item( pv->sub_list, ii ); if (ii + 1 < hb_list_count(pv->sub_list)) next = hb_list_item( pv->sub_list, ii + 1 ); else next = NULL; if ((sub->s.stop != AV_NOPTS_VALUE && sub->s.stop <= buf->s.start) || (next != NULL && sub->s.stop == AV_NOPTS_VALUE && next->s.start <= buf->s.start)) { // Subtitle stop is in the past, delete it hb_list_rem( pv->sub_list, sub ); hb_buffer_close( &sub ); } else if( sub->s.start <= buf->s.start ) { // The subtitle has started before this frame and ends // after it. Render the subtitle into the frame. while ( sub ) { ApplySub( pv, buf, sub ); sub = sub->next; } ii++; } else { // The subtitle starts in the future. No need to continue. break; } } } static int vobsub_init( hb_filter_object_t * filter, hb_filter_init_t * init ) { hb_filter_private_t * pv = filter->private_data; pv->sub_list = hb_list_init(); return 0; } static void vobsub_close( hb_filter_object_t * filter ) { hb_filter_private_t * pv = filter->private_data; if( !pv ) { return; } if( pv->sub_list ) hb_list_empty( &pv->sub_list ); free( pv ); filter->private_data = NULL; } static int vobsub_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in; hb_buffer_t * sub; if ( in->size <= 0 ) { *buf_in = NULL; *buf_out = in; return HB_FILTER_DONE; } // Get any pending subtitles and add them to the active // subtitle list while( ( sub = hb_fifo_get( filter->subtitle->fifo_out ) ) ) { hb_list_add( pv->sub_list, sub ); } ApplyVOBSubs( pv, in ); *buf_in = NULL; *buf_out = in; return HB_FILTER_OK; } static uint8_t ssaAlpha( ASS_Image *frame, int x, int y ) { unsigned frameA = ( frame->color ) & 0xff; unsigned gliphA = frame->bitmap[y*frame->stride + x]; // Alpha for this pixel is the frame opacity (255 - frameA) // multiplied by the gliph alfa (gliphA) for this pixel unsigned alpha = (255 - frameA) * gliphA >> 8; return (uint8_t)alpha; } static hb_buffer_t * RenderSSAFrame( hb_filter_private_t * pv, ASS_Image * frame ) { hb_buffer_t *sub; int xx, yy; unsigned r = ( frame->color >> 24 ) & 0xff; unsigned g = ( frame->color >> 16 ) & 0xff; unsigned b = ( frame->color >> 8 ) & 0xff; int yuv = hb_rgb2yuv((r << 16) | (g << 8) | b ); unsigned frameY = (yuv >> 16) & 0xff; unsigned frameV = (yuv >> 8 ) & 0xff; unsigned frameU = (yuv >> 0 ) & 0xff; sub = hb_frame_buffer_init( AV_PIX_FMT_YUVA420P, frame->w, frame->h ); if( sub == NULL ) return NULL; uint8_t *y_out, *u_out, *v_out, *a_out; y_out = sub->plane[0].data; u_out = sub->plane[1].data; v_out = sub->plane[2].data; a_out = sub->plane[3].data; for( yy = 0; yy < frame->h; yy++ ) { for( xx = 0; xx < frame->w; xx++ ) { y_out[xx] = frameY; if( ( yy & 1 ) == 0 ) { u_out[xx>>1] = frameU; v_out[xx>>1] = frameV; } a_out[xx] = ssaAlpha( frame, xx, yy );; } y_out += sub->plane[0].stride; if( ( yy & 1 ) == 0 ) { u_out += sub->plane[1].stride; v_out += sub->plane[2].stride; } a_out += sub->plane[3].stride; } sub->f.width = frame->w; sub->f.height = frame->h; sub->f.x = frame->dst_x + pv->crop[2]; sub->f.y = frame->dst_y + pv->crop[0]; return sub; } static void ApplySSASubs( hb_filter_private_t * pv, hb_buffer_t * buf ) { ASS_Image *frameList; hb_buffer_t *sub; frameList = ass_render_frame( pv->renderer, pv->ssaTrack, buf->s.start / 90, NULL ); if ( !frameList ) return; ASS_Image *frame; for (frame = frameList; frame; frame = frame->next) { sub = RenderSSAFrame( pv, frame ); if( sub ) { ApplySub( pv, buf, sub ); hb_buffer_close( &sub ); } } } static void ssa_log(int level, const char *fmt, va_list args, void *data) { if ( level < 5 ) // same as default verbosity when no callback is set { hb_valog( 1, "[ass]", fmt, args ); } } static int ssa_init( hb_filter_object_t * filter, hb_filter_init_t * init ) { hb_filter_private_t * pv = filter->private_data; pv->ssa = ass_library_init(); if ( !pv->ssa ) { hb_error( "decssasub: libass initialization failed\n" ); return 1; } // Redirect libass output to hb_log ass_set_message_cb( pv->ssa, ssa_log, NULL ); // Load embedded fonts hb_list_t * list_attachment = init->job->list_attachment; int i; for ( i = 0; i < hb_list_count(list_attachment); i++ ) { hb_attachment_t * attachment = hb_list_item( list_attachment, i ); if ( attachment->type == FONT_TTF_ATTACH ) { ass_add_font( pv->ssa, attachment->name, attachment->data, attachment->size ); } } ass_set_extract_fonts( pv->ssa, 1 ); ass_set_style_overrides( pv->ssa, NULL ); pv->renderer = ass_renderer_init( pv->ssa ); if ( !pv->renderer ) { hb_log( "decssasub: renderer initialization failed\n" ); return 1; } ass_set_use_margins( pv->renderer, 0 ); ass_set_hinting( pv->renderer, ASS_HINTING_LIGHT ); // VLC 1.0.4 uses this ass_set_font_scale( pv->renderer, 1.0 ); ass_set_line_spacing( pv->renderer, 1.0 ); // Setup default font family // // SSA v4.00 requires that "Arial" be the default font const char *font = NULL; const char *family = "Arial"; // NOTE: This can sometimes block for several *seconds*. // It seems that process_fontdata() for some embedded fonts is slow. ass_set_fonts( pv->renderer, font, family, /*haveFontConfig=*/1, NULL, 1 ); // Setup track state pv->ssaTrack = ass_new_track( pv->ssa ); if ( !pv->ssaTrack ) { hb_log( "decssasub: ssa track initialization failed\n" ); return 1; } int width = init->width - ( pv->crop[2] + pv->crop[3] ); int height = init->height - ( pv->crop[0] + pv->crop[1] ); ass_set_frame_size( pv->renderer, width, height); double par = (double)init->par_width / init->par_height; ass_set_aspect_ratio( pv->renderer, 1, par ); return 0; } static void ssa_close( hb_filter_object_t * filter ) { hb_filter_private_t * pv = filter->private_data; if( !pv ) { return; } if ( pv->ssaTrack ) ass_free_track( pv->ssaTrack ); if ( pv->renderer ) ass_renderer_done( pv->renderer ); if ( pv->ssa ) ass_library_done( pv->ssa ); free( pv ); filter->private_data = NULL; } static int ssa_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in; hb_buffer_t * sub; if (!pv->script_initialized) { // NOTE: The codec extradata is expected to be in MKV format // I would like to initialize this in ssa_init, but when we are // transcoding text subtitles to SSA, the extradata does not // get initialized until the decoder is initialized. Since // decoder initialization happens after filter initialization, // we need to postpone this. ass_process_codec_private(pv->ssaTrack, (char*)filter->subtitle->extradata, filter->subtitle->extradata_size); pv->script_initialized = 1; } if ( in->size <= 0 ) { *buf_in = NULL; *buf_out = in; return HB_FILTER_DONE; } // Get any pending subtitles and add them to the active // subtitle list while( ( sub = hb_fifo_get( filter->subtitle->fifo_out ) ) ) { // Parse MKV-SSA packet // SSA subtitles always have an explicit stop time, so we // do not need to do special processing for stop == AV_NOPTS_VALUE ass_process_chunk( pv->ssaTrack, (char*)sub->data, sub->size, sub->s.start / 90, (sub->s.stop - sub->s.start) / 90 ); hb_buffer_close(&sub); } ApplySSASubs( pv, in ); *buf_in = NULL; *buf_out = in; return HB_FILTER_OK; } static int textsub_init( hb_filter_object_t * filter, hb_filter_init_t * init ) { hb_filter_private_t * pv = filter->private_data; int width = init->width - pv->crop[2] - pv->crop[3]; int height = init->height - pv->crop[0] - pv->crop[1]; // Text subtitles for which we create a dummy ASS header need // to have the header rewritten with the correct dimensions. hb_subtitle_add_ssa_header(filter->subtitle, width, height); return ssa_init(filter, init); } static void textsub_close( hb_filter_object_t * filter ) { return ssa_close(filter); } static void process_sub(hb_filter_private_t *pv, hb_buffer_t *sub) { int64_t start, dur; char *ssa, *tmp; // libass expects every chunk to have a unique sequence number // since we are repeating subs in some cases, we need to replace // the sequence number. tmp = strchr((char*)sub->data, ','); if (tmp == NULL) return; ssa = hb_strdup_printf("%d%s", ++pv->line, tmp); // Parse MKV-SSA packet // SSA subtitles always have an explicit stop time, so we // do not need to do special processing for stop == AV_NOPTS_VALUE start = sub->s.start; dur = sub->s.stop - sub->s.start; ass_process_chunk(pv->ssaTrack, ssa, sub->size, start, dur); free(ssa); } static int textsub_work(hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out) { hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in; hb_buffer_t * sub; if (!pv->script_initialized) { ass_process_codec_private(pv->ssaTrack, (char*)filter->subtitle->extradata, filter->subtitle->extradata_size); pv->script_initialized = 1; } if (in->size <= 0) { *buf_in = NULL; *buf_out = in; return HB_FILTER_DONE; } int in_start_ms = in->s.start / 90; // Get any pending subtitles and add them to the active // subtitle list while ((sub = hb_fifo_get(filter->subtitle->fifo_out))) { // libass expects times in ms. So to make the math easy, // convert to ms immediately. sub->s.start /= 90; if (sub->s.stop != AV_NOPTS_VALUE) { sub->s.stop /= 90; } // Subtitle formats such as CC can have stop times // that are not known until an "erase display" command // is encountered in the stream. For these formats // current_sub is the currently active subtitle for which // we do not yet know the stop time. We do not currently // support overlapping subtitles of this type. if (pv->current_sub != NULL) { // Next sub start time tells us the stop time of the // current sub when it is not known in advance. pv->current_sub->s.stop = sub->s.start; process_sub(pv, pv->current_sub); hb_buffer_close(&pv->current_sub); } if (sub->s.start == sub->s.stop) { // Zero duration sub used to "clear" previous sub that had // an unknown duration hb_buffer_close(&sub); } else if (sub->s.stop == AV_NOPTS_VALUE) { // We don't know the duration of this sub. So we will // apply it to every video frame until we see a "clear" sub. pv->current_sub = sub; pv->current_sub->s.stop = pv->current_sub->s.start; } else { // Duration of this subtitle is known, so we can just // process it normally. process_sub(pv, sub); hb_buffer_close(&sub); } } if (pv->current_sub != NULL && pv->current_sub->s.start <= in_start_ms) { // We don't know the duration of this subtitle, but we know // that it started before the current video frame and that // it is still active. So render it on this video frame. pv->current_sub->s.start = pv->current_sub->s.stop; pv->current_sub->s.stop = in_start_ms + 1; process_sub(pv, pv->current_sub); } ApplySSASubs(pv, in); *buf_in = NULL; *buf_out = in; return HB_FILTER_OK; } static void ApplyPGSSubs( hb_filter_private_t * pv, hb_buffer_t * buf ) { int index; hb_buffer_t * old_sub; hb_buffer_t * sub; // Each PGS subtitle supersedes anything that preceded it. // Find the active subtitle (if there is one), and delete // everything before it. for( index = hb_list_count( pv->sub_list ) - 1; index > 0; index-- ) { sub = hb_list_item( pv->sub_list, index); if ( sub->s.start <= buf->s.start ) { while ( index > 0 ) { old_sub = hb_list_item( pv->sub_list, index - 1); hb_list_rem( pv->sub_list, old_sub ); hb_buffer_close( &old_sub ); index--; } } } // Some PGS subtitles have no content and only serve to clear // the screen. If any of these are at the front of our list, // we can now get rid of them. while ( hb_list_count( pv->sub_list ) > 0 ) { sub = hb_list_item( pv->sub_list, 0 ); if (sub->f.width != 0 && sub->f.height != 0) break; hb_list_rem( pv->sub_list, sub ); hb_buffer_close( &sub ); } // Check to see if there's an active subtitle, and apply it. if ( hb_list_count( pv->sub_list ) > 0) { sub = hb_list_item( pv->sub_list, 0 ); if ( sub->s.start <= buf->s.start ) { while ( sub ) { ApplySub( pv, buf, sub ); sub = sub->sub; } } } } static int pgssub_init( hb_filter_object_t * filter, hb_filter_init_t * init ) { hb_filter_private_t * pv = filter->private_data; pv->sub_list = hb_list_init(); return 0; } static void pgssub_close( hb_filter_object_t * filter ) { hb_filter_private_t * pv = filter->private_data; if ( !pv ) { return; } if ( pv->sub_list ) hb_list_empty( &pv->sub_list ); free( pv ); filter->private_data = NULL; } static int pgssub_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out) { hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in; hb_buffer_t * sub; if ( in->size <= 0 ) { *buf_in = NULL; *buf_out = in; return HB_FILTER_DONE; } // Get any pending subtitles and add them to the active // subtitle list while ( ( sub = hb_fifo_get( filter->subtitle->fifo_out ) ) ) { hb_list_add( pv->sub_list, sub ); } ApplyPGSSubs( pv, in ); *buf_in = NULL; *buf_out = in; return HB_FILTER_OK; } static int hb_rendersub_init( hb_filter_object_t * filter, hb_filter_init_t * init ) { filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); hb_filter_private_t * pv = filter->private_data; hb_subtitle_t *subtitle; int ii; if( filter->settings ) { sscanf( filter->settings, "%d:%d:%d:%d", &pv->crop[0], &pv->crop[1], &pv->crop[2], &pv->crop[3]); } // Find the subtitle we need for( ii = 0; ii < hb_list_count(init->job->list_subtitle); ii++ ) { subtitle = hb_list_item( init->job->list_subtitle, ii ); if( subtitle && subtitle->config.dest == RENDERSUB ) { // Found it filter->subtitle = subtitle; pv->type = subtitle->source; break; } } if( filter->subtitle == NULL ) { hb_log("rendersub: no subtitle marked for burn"); return 1; } switch( pv->type ) { case VOBSUB: { return vobsub_init( filter, init ); } break; case SSASUB: { return ssa_init( filter, init ); } break; case SRTSUB: case CC608SUB: case UTF8SUB: case TX3GSUB: { return textsub_init( filter, init ); } break; case PGSSUB: { return pgssub_init( filter, init ); } break; default: { hb_log("rendersub: unsupported subtitle format %d", pv->type ); return 1; } break; } } static int hb_rendersub_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_filter_private_t * pv = filter->private_data; switch( pv->type ) { case VOBSUB: { return vobsub_work( filter, buf_in, buf_out ); } break; case SSASUB: { return ssa_work( filter, buf_in, buf_out ); } break; case SRTSUB: case CC608SUB: case UTF8SUB: case TX3GSUB: { return textsub_work( filter, buf_in, buf_out ); } break; case PGSSUB: { return pgssub_work( filter, buf_in, buf_out ); } break; default: { hb_error("rendersub: unsupported subtitle format %d", pv->type ); return 1; } break; } } static void hb_rendersub_close( hb_filter_object_t * filter ) { hb_filter_private_t * pv = filter->private_data; switch( pv->type ) { case VOBSUB: { vobsub_close( filter ); } break; case SSASUB: { ssa_close( filter ); } break; case SRTSUB: case CC608SUB: case UTF8SUB: case TX3GSUB: { textsub_close( filter ); } break; case PGSSUB: { pgssub_close( filter ); } break; default: { hb_error("rendersub: unsupported subtitle format %d", pv->type ); } break; } } HandBrake-0.10.2/libhb/qsv_memory.h0000664000175200017520000000437512205472744017511 0ustar handbrakehandbrake/* ********************************************************************* *\ Copyright (C) 2013 Intel Corporation. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \* ********************************************************************* */ #ifndef QSV_MEMORY_H #define QSV_MEMORY_H #include "libavcodec/qsv.h" #include "msdk/mfxplugin.h" typedef struct{ struct{ // for "planes" , Y and VU uint8_t *data[2]; int strides[2]; } qsv_data; struct{ // for each plane, Y U V uint8_t *data[3]; int strides[3]; } data; int width; int height; } qsv_memory_copy_t; int qsv_nv12_to_yuv420(struct SwsContext* sws_context,hb_buffer_t* dst, mfxFrameSurface1* src,mfxCoreInterface *core); int qsv_yuv420_to_nv12(struct SwsContext* sws_context,mfxFrameSurface1* dst, hb_buffer_t* src); #endif // QSV_MEMORY_H HandBrake-0.10.2/libhb/decpgssub.c0000664000175200017520000004505612463330511017252 0ustar handbrakehandbrake/* decpgssub.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "hbffmpeg.h" struct hb_work_private_s { AVCodecContext * context; hb_job_t * job; // For PGS subs, when doing passthru, we don't know if we need a // packet until we have processed several packets. So we cache // all the packets we see until libav returns a subtitle with // the information we need. hb_buffer_t * list_pass_buffer; hb_buffer_t * last_pass_buffer; // It is possible for multiple subtitles to be enncapsulated in // one packet. This won't happen for PGS subs, but may for other // types of subtitles. Since I plan to generalize this code to handle // other than PGS, we will need to keep a list of all subtitles seen // while parsing an input packet. hb_buffer_t * list_buffer; hb_buffer_t * last_buffer; // XXX: we may occasionally see subtitles with broken timestamps // while this should really get fixed elsewhere, // dropping subtitles should be avoided as much as possible int64_t last_pts; // for PGS subs, we need to pass 'empty' subtitles through (they clear the // display) - when doing forced-only extraction, only pass empty subtitles // through if we've seen a forced sub and haven't seen any empty sub since uint8_t seen_forced_sub; // if we start encoding partway through the source, we may encounter empty // subtitles before we see any actual subtitle content - discard them uint8_t discard_subtitle; }; static int decsubInit( hb_work_object_t * w, hb_job_t * job ) { AVCodec *codec = avcodec_find_decoder( AV_CODEC_ID_HDMV_PGS_SUBTITLE ); AVCodecContext *context = avcodec_alloc_context3( codec ); context->codec = codec; hb_work_private_t * pv; pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; pv->discard_subtitle = 1; pv->seen_forced_sub = 0; pv->last_pts = 0; pv->context = context; pv->job = job; // Set decoder opts... AVDictionary * av_opts = NULL; // e.g. av_dict_set( &av_opts, "refcounted_frames", "1", 0 ); if (hb_avcodec_open(pv->context, codec, &av_opts, 0)) { av_dict_free( &av_opts ); hb_log("decsubInit: avcodec_open failed"); return 1; } av_dict_free( &av_opts ); return 0; } static void make_empty_pgs( hb_buffer_t * buf ) { hb_buffer_t * b = buf; uint8_t done = 0; // Each buffer is composed of 1 or more segments. // Segment header is: // type - 1 byte // length - 2 bytes // We want to modify the presentation segment which is type 0x16 // // Note that every pgs display set is required to have a presentation // segment, so we will only have to look at one display set. while ( b && !done ) { int ii = 0; while (ii + 3 <= b->size) { uint8_t type; int len; int segment_len_pos; type = b->data[ii++]; segment_len_pos = ii; len = ((int)b->data[ii] << 8) + b->data[ii+1]; ii += 2; if (type == 0x16 && ii + len <= b->size) { int obj_count; int kk, jj = ii; int obj_start; // Skip // video descriptor 5 bytes // composition descriptor 3 bytes // palette update flg 1 byte // palette id ref 1 byte jj += 10; // Set number of composition objects to 0 obj_count = b->data[jj]; b->data[jj] = 0; jj++; obj_start = jj; // And remove all the composition objects for (kk = 0; kk < obj_count; kk++) { uint8_t crop; crop = b->data[jj + 3]; // skip // object id - 2 bytes // window id - 1 byte // object/forced flag - 1 byte // x pos - 2 bytes // y pos - 2 bytes jj += 8; if (crop & 0x80) { // skip // crop x - 2 bytes // crop y - 2 bytes // crop w - 2 bytes // crop h - 2 bytes jj += 8; } } if (jj < b->size) { memmove(b->data + obj_start, b->data + jj, b->size - jj); } b->size = obj_start + ( b->size - jj ); done = 1; len = obj_start - (segment_len_pos + 2); b->data[segment_len_pos] = len >> 8; b->data[segment_len_pos+1] = len & 0xff; break; } ii += len; } b = b->next; } } static int decsubWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_buffer_t * in = *buf_in; if ( in->size <= 0 ) { /* EOF on input stream - send it downstream & say that we're done */ if ( pv->list_buffer == NULL ) { pv->list_buffer = pv->last_buffer = in; } else { pv->last_buffer->next = in; } *buf_in = NULL; *buf_out = pv->list_buffer; pv->list_buffer = NULL; return HB_WORK_DONE; } if ( !pv->job->indepth_scan && w->subtitle->config.dest == PASSTHRUSUB && hb_subtitle_can_pass( PGSSUB, pv->job->mux ) ) { // Append to buffer list. It will be sent to fifo after we determine // if this is a packet we need. if ( pv->list_pass_buffer == NULL ) { pv->list_pass_buffer = pv->last_pass_buffer = in; } else { pv->last_pass_buffer->next = in; pv->last_pass_buffer = in; } // We are keeping the buffer, so prevent the filter loop from // deleting it. *buf_in = NULL; } AVSubtitle subtitle; memset( &subtitle, 0, sizeof(subtitle) ); AVPacket avp; av_init_packet( &avp ); avp.data = in->data; avp.size = in->size; // libav wants pkt pts in AV_TIME_BASE units if (in->s.start != AV_NOPTS_VALUE) { avp.pts = av_rescale(in->s.start, AV_TIME_BASE, 90000); } else { avp.pts = AV_NOPTS_VALUE; } int has_subtitle = 0; do { int usedBytes = avcodec_decode_subtitle2( pv->context, &subtitle, &has_subtitle, &avp ); if (usedBytes < 0) { hb_log("unable to decode subtitle with %d bytes.", avp.size); return HB_WORK_OK; } if (usedBytes <= avp.size) { avp.data += usedBytes; avp.size -= usedBytes; } else { avp.size = 0; } /* Subtitles are "usable" if: * 1. Libav returned a subtitle (has_subtitle) AND * 2. we're not doing Foreign Audio Search (!pv->job->indepth_scan) AND * 3. the sub is non-empty or we've seen one such sub before (!pv->discard_subtitle) * For forced-only extraction, usable subtitles also need to: * a. be forced (subtitle.rects[0]->flags & AV_SUBTITLE_FLAG_FORCED) OR * b. follow a forced sub (pv->seen_forced_sub) */ uint8_t forced_sub = 0; uint8_t useable_sub = 0; uint8_t clear_subtitle = 0; if (has_subtitle) { // subtitle statistics if (subtitle.num_rects) { w->subtitle->hits++; if (subtitle.rects[0]->flags & AV_SUBTITLE_FLAG_FORCED) { forced_sub = 1; w->subtitle->forced_hits++; } } else { clear_subtitle = 1; } // are we doing Foreign Audio Search? if (!pv->job->indepth_scan) { // do we want to discard this subtitle? pv->discard_subtitle = pv->discard_subtitle && clear_subtitle; // do we need this subtitle? useable_sub = (!pv->discard_subtitle && (!w->subtitle->config.force || forced_sub || pv->seen_forced_sub)); // do we need to create an empty subtitle? if (w->subtitle->config.force && useable_sub && !forced_sub && !clear_subtitle) { // We are forced-only and need to output this subtitle, but // it's neither forced nor empty. // // If passthru, create an empty subtitle. // Also, flag an empty subtitle for subtitle RENDER. make_empty_pgs(pv->list_pass_buffer); clear_subtitle = 1; } // is the subtitle forced? pv->seen_forced_sub = forced_sub; } } if (useable_sub) { int64_t pts = AV_NOPTS_VALUE; hb_buffer_t * out = NULL; if (subtitle.pts != AV_NOPTS_VALUE) { pts = av_rescale(subtitle.pts, 90000, AV_TIME_BASE); } else { if (in->s.start >= 0) { pts = in->s.start; } else { // XXX: a broken pts will cause us to drop this subtitle, // which is bad; use a default duration of 3 seconds // // A broken pts is only generated when a pgs packet // occurs after a discontinuity and before the // next audio or video packet which re-establishes // timing (afaik). pts = pv->last_pts + 3 * 90000LL; hb_log("[warning] decpgssub: track %d, invalid PTS", w->subtitle->out_track); } } // work around broken timestamps if (pts < pv->last_pts) { // XXX: this should only happen if the prevous pts // was unknown and our 3 second default duration // overshot the next pgs pts. // // assign a 1 second duration pts = pv->last_pts + 1 * 90000LL; hb_log("[warning] decpgssub: track %d, non-monotically increasing PTS", w->subtitle->out_track); } pv->last_pts = pts; if ( w->subtitle->config.dest == PASSTHRUSUB && hb_subtitle_can_pass( PGSSUB, pv->job->mux ) ) { /* PGS subtitles are spread across multiple packets (1 per segment). * In the MKV container, all segments are found in the same packet * (this is expected by some devices, such as the WD TV Live). * So if there are multiple packets, merge them. */ if (pv->list_pass_buffer->next == NULL) { // packets already merged (e.g. MKV sources) out = pv->list_pass_buffer; pv->list_pass_buffer = NULL; } else { int size = 0; uint8_t * data; hb_buffer_t * b; b = pv->list_pass_buffer; while (b != NULL) { size += b->size; b = b->next; } out = hb_buffer_init( size ); data = out->data; b = pv->list_pass_buffer; while (b != NULL) { memcpy( data, b->data, b->size ); data += b->size; b = b->next; } hb_buffer_close( &pv->list_pass_buffer ); out->s = in->s; out->sequence = in->sequence; } out->s.frametype = HB_FRAME_SUBTITLE; out->s.renderOffset = AV_NOPTS_VALUE; out->s.stop = AV_NOPTS_VALUE; out->s.start = pts; } else { if (!clear_subtitle) { unsigned ii, x0, y0, x1, y1, w, h; x0 = subtitle.rects[0]->x; y0 = subtitle.rects[0]->y; x1 = subtitle.rects[0]->x + subtitle.rects[0]->w; y1 = subtitle.rects[0]->y + subtitle.rects[0]->h; // First, find total bounding rectangle for (ii = 1; ii < subtitle.num_rects; ii++) { if (subtitle.rects[ii]->x < x0) x0 = subtitle.rects[ii]->x; if (subtitle.rects[ii]->y < y0) y0 = subtitle.rects[ii]->y; if (subtitle.rects[ii]->x + subtitle.rects[ii]->w > x1) x1 = subtitle.rects[ii]->x + subtitle.rects[ii]->w; if (subtitle.rects[ii]->y + subtitle.rects[ii]->h > y1) y1 = subtitle.rects[ii]->y + subtitle.rects[ii]->h; } w = x1 - x0; h = y1 - y0; out = hb_frame_buffer_init(AV_PIX_FMT_YUVA420P, w, h); memset(out->data, 0, out->size); out->s.frametype = HB_FRAME_SUBTITLE; out->s.id = in->s.id; out->sequence = in->sequence; out->s.start = pts; out->s.stop = AV_NOPTS_VALUE; out->s.renderOffset = AV_NOPTS_VALUE; out->f.x = x0; out->f.y = y0; for (ii = 0; ii < subtitle.num_rects; ii++) { AVSubtitleRect *rect = subtitle.rects[ii]; int off_x = rect->x - x0; int off_y = rect->y - y0; uint8_t *lum = out->plane[0].data; uint8_t *chromaU = out->plane[1].data; uint8_t *chromaV = out->plane[2].data; uint8_t *alpha = out->plane[3].data; lum += off_y * out->plane[0].stride + off_x; alpha += off_y * out->plane[3].stride + off_x; chromaU += (off_y >> 1) * out->plane[1].stride + (off_x >> 1); chromaV += (off_y >> 1) * out->plane[2].stride + (off_x >> 1); int xx, yy; for (yy = 0; yy < rect->h; yy++) { for (xx = 0; xx < rect->w; xx++) { uint32_t argb, yuv; int pixel; uint8_t color; pixel = yy * rect->w + xx; color = rect->pict.data[0][pixel]; argb = ((uint32_t*)rect->pict.data[1])[color]; yuv = hb_rgb2yuv(argb); lum[xx] = (yuv >> 16) & 0xff; alpha[xx] = (argb >> 24) & 0xff; if ((xx & 1) == 0 && (yy & 1) == 0) { chromaV[xx>>1] = (yuv >> 8) & 0xff; chromaU[xx>>1] = yuv & 0xff; } } lum += out->plane[0].stride; if ((yy & 1) == 0) { chromaU += out->plane[1].stride; chromaV += out->plane[2].stride; } alpha += out->plane[3].stride; } } if ( pv->list_buffer == NULL ) { pv->list_buffer = pv->last_buffer = out; } else { pv->last_buffer->next = out; pv->last_buffer = out; } out = NULL; } else { out = hb_buffer_init( 1 ); out->s.frametype = HB_FRAME_SUBTITLE; out->s.id = in->s.id; out->s.start = pts; out->s.stop = pts; out->f.x = 0; out->f.y = 0; out->f.width = 0; out->f.height = 0; } } if ( pv->list_buffer == NULL ) { pv->list_buffer = pv->last_buffer = out; } else { pv->last_buffer->next = out; } while (pv->last_buffer && pv->last_buffer->next) { pv->last_buffer = pv->last_buffer->next; } } else if ( has_subtitle ) { hb_buffer_close( &pv->list_pass_buffer ); pv->list_pass_buffer = NULL; } if ( has_subtitle ) { avsubtitle_free(&subtitle); } } while (avp.size > 0); *buf_out = pv->list_buffer; pv->list_buffer = NULL; return HB_WORK_OK; } static void decsubClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; avcodec_flush_buffers( pv->context ); avcodec_close( pv->context ); } hb_work_object_t hb_decpgssub = { WORK_DECPGSSUB, "PGS decoder", decsubInit, decsubWork, decsubClose }; HandBrake-0.10.2/libhb/audio_resample.h0000664000175200017520000000722312463330511020263 0ustar handbrakehandbrake/* audio_resample.h * * Copyright (c) 2003-2015 HandBrake Team * This file is part of the HandBrake source code * Homepage: * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ /* Implements a libavresample wrapper for convenience. * * Supports sample_fmt and channel_layout conversion. * * sample_rate conversion will come later (libavresample doesn't support * sample_rate conversion with float samples yet). */ #ifndef AUDIO_RESAMPLE_H #define AUDIO_RESAMPLE_H #include #include #include "libavutil/channel_layout.h" #include "libavresample/avresample.h" /* Default mix level for center and surround channels */ #define HB_MIXLEV_DEFAULT ((double)M_SQRT1_2) /* Default mix level for LFE channel */ #define HB_MIXLEV_ZERO ((double)0.0) typedef struct { int dual_mono_downmix; int dual_mono_right_only; int resample_needed; AVAudioResampleContext *avresample; struct { uint64_t channel_layout; double lfe_mix_level; double center_mix_level; double surround_mix_level; enum AVSampleFormat sample_fmt; } in; struct { int channels; uint64_t channel_layout; double lfe_mix_level; double center_mix_level; double surround_mix_level; enum AVSampleFormat sample_fmt; } resample; struct { int channels; int sample_size; int normalize_mix_level; uint64_t channel_layout; enum AVSampleFormat sample_fmt; enum AVMatrixEncoding matrix_encoding; } out; } hb_audio_resample_t; /* Initialize an hb_audio_resample_t for converting audio to the requested * sample_fmt and mixdown. * * Also sets the default audio input characteristics, so that they are the same * as the output characteristics (no conversion needed). */ hb_audio_resample_t* hb_audio_resample_init(enum AVSampleFormat sample_fmt, int hb_amixdown, int normalize_mix); /* The following functions set the audio input characteristics. * * They should be called whenever the relevant characteristic(s) differ from the * requested output characteristics, or if they may have changed in the source. */ void hb_audio_resample_set_channel_layout(hb_audio_resample_t *resample, uint64_t channel_layout); void hb_audio_resample_set_mix_levels(hb_audio_resample_t *resample, double surround_mix_level, double center_mix_level, double lfe_mix_level); void hb_audio_resample_set_sample_fmt(hb_audio_resample_t *resample, enum AVSampleFormat sample_fmt); /* Update an hb_audio_resample_t. * * Must be called after using any of the above functions. */ int hb_audio_resample_update(hb_audio_resample_t *resample); /* Free an hb_audio_remsample_t. */ void hb_audio_resample_free(hb_audio_resample_t *resample); /* Convert input samples to the requested output characteristics * (sample_fmt and channel_layout + matrix_encoding). * * Returns an hb_buffer_t with the converted output. * * resampling is only done when necessary. */ hb_buffer_t* hb_audio_resample(hb_audio_resample_t *resample, uint8_t **samples, int nsamples); #endif /* AUDIO_RESAMPLE_H */ HandBrake-0.10.2/libhb/decvobsub.c0000664000175200017520000004665512463330511017255 0ustar handbrakehandbrake/* decvobsub.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ /* * Decoder for DVD bitmap subtitles, also known as "VOB subtitles" within the HandBrake source code. * * Input format of the subtitle packets is described here: * http://sam.zoy.org/writings/dvd/subtitles/ * * An auxiliary input is the color palette lookup table, in 'subtitle->palette'. * The demuxer implementation must fill out this table appropriately. * - In the case of a true DVD input, the palette is read from the IFO file. * - In the case of an MKV file input, the palette is read from the codec private data of the subtitle track. * * Output format of this decoder is PICTURESUB, which is: * struct PictureSubPacket { * uint8_t lum[pixelCount]; // Y * uint8_t alpha[pixelCount]; // alpha (max = 16) * uint8_t chromaU[pixelCount]; // Cb * uint8_t chromaV[pixelCount]; // Cr * } */ #include "hb.h" struct hb_work_private_s { hb_job_t * job; hb_buffer_t * buf; int size_sub; int size_got; int size_rle; int64_t pts; int64_t pts_start; int64_t pts_stop; int pts_forced; int x; int y; int width; int height; int stream_id; int offsets[2]; uint8_t lum[4]; uint8_t chromaU[4]; uint8_t chromaV[4]; uint8_t alpha[4]; uint8_t palette_set; }; static hb_buffer_t * Decode( hb_work_object_t * ); int decsubInit( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t * pv; pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; pv->job = job; pv->pts = 0; // Warn if the input color palette is empty pv->palette_set = w->subtitle->palette_set; if ( pv->palette_set ) { // Make sure the entries in the palette are not all 0 pv->palette_set = 0; int i; for (i=0; i<16; i++) { if (w->subtitle->palette[i]) { pv->palette_set = 1; break; } } } if (!pv->palette_set) { hb_log( "decvobsub: input color palette is empty!" ); } return 0; } int decsubWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_buffer_t * in = *buf_in; int size_sub, size_rle; if ( in->size <= 0 ) { /* EOF on input stream - send it downstream & say that we're done */ *buf_out = in; *buf_in = NULL; return HB_WORK_DONE; } pv->stream_id = in->s.id; size_sub = ( in->data[0] << 8 ) | in->data[1]; size_rle = ( in->data[2] << 8 ) | in->data[3]; if( !pv->size_sub ) { /* We are looking for the start of a new subtitle */ if( size_sub && size_rle && size_sub > size_rle && in->size <= size_sub ) { /* Looks all right so far */ pv->size_sub = size_sub; pv->size_rle = size_rle; pv->buf = hb_buffer_init( 0xFFFF ); memcpy( pv->buf->data, in->data, in->size ); pv->buf->s.id = in->s.id; pv->buf->s.frametype = HB_FRAME_SUBTITLE; pv->buf->sequence = in->sequence; pv->size_got = in->size; if( in->s.start >= 0 ) { pv->pts = in->s.start; } } } else { /* We are waiting for the end of the current subtitle */ if( in->size <= pv->size_sub - pv->size_got ) { memcpy( pv->buf->data + pv->size_got, in->data, in->size ); pv->buf->s.id = in->s.id; pv->buf->sequence = in->sequence; pv->size_got += in->size; if( in->s.start >= 0 ) { pv->pts = in->s.start; } } else { // bad size, must have lost sync // force re-sync if ( pv->buf != NULL ) hb_buffer_close( &pv->buf ); pv->size_sub = 0; } } *buf_out = NULL; if( pv->size_sub && pv->size_sub == pv->size_got ) { pv->buf->size = pv->size_sub; /* We got a complete subtitle, decode it */ *buf_out = Decode( w ); if( buf_out && *buf_out ) { (*buf_out)->s.id = in->s.id; (*buf_out)->sequence = in->sequence; } /* Wait for the next one */ pv->size_sub = 0; pv->size_got = 0; pv->size_rle = 0; if ( pv->pts_stop != AV_NOPTS_VALUE ) { // If we don't get a valid next timestamp, use the stop time // of the current sub as the start of the next. // This can happen if reader invalidates timestamps while // waiting for an audio to update the SCR. pv->pts = pv->pts_stop; } } return HB_WORK_OK; } void decsubClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; if ( pv->buf ) hb_buffer_close( &pv->buf ); free( w->private_data ); } hb_work_object_t hb_decvobsub = { WORK_DECVOBSUB, "VOBSUB decoder", decsubInit, decsubWork, decsubClose }; /*********************************************************************** * ParseControls *********************************************************************** * Get the start and end dates (relative to the PTS from the PES * header), the width and height of the subpicture and the colors and * alphas used in it **********************************************************************/ static void ParseControls( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; uint8_t * buf = pv->buf->data; int i; int command; int date, next; pv->pts_start = AV_NOPTS_VALUE; pv->pts_stop = AV_NOPTS_VALUE; pv->pts_forced = 0; pv->alpha[3] = 0; pv->alpha[2] = 0; pv->alpha[1] = 0; pv->alpha[0] = 0; for( i = pv->size_rle; ; ) { date = ( buf[i] << 8 ) | buf[i+1]; i += 2; next = ( buf[i] << 8 ) | buf[i+1]; i += 2; for( ;; ) { command = buf[i++]; /* * There are eight commands available for * Sub-Pictures. The first SP_DCSQ should contain, as a * minimum, SET_COLOR, SET_CONTR, SET_DAREA, and * SET_DSPXA */ if( command == 0xFF ) // 0xFF - CMD_END - ends one SP_DCSQ { break; } switch( command ) { case 0x00: // 0x00 - FSTA_DSP - Forced Start Display, no arguments pv->pts_start = pv->pts + date * 1024; pv->pts_forced = 1; w->subtitle->hits++; w->subtitle->forced_hits++; break; case 0x01: // 0x01 - STA_DSP - Start Display, no arguments pv->pts_start = pv->pts + date * 1024; pv->pts_forced = 0; w->subtitle->hits++; break; case 0x02: // 0x02 - STP_DSP - Stop Display, no arguments if(pv->pts_stop == AV_NOPTS_VALUE) pv->pts_stop = pv->pts + date * 1024; break; case 0x03: // 0x03 - SET_COLOR - Set Colour indices { /* * SET_COLOR - provides four indices into the CLUT * for the current PGC to associate with the four * pixel values */ int colors[4]; int j; colors[0] = (buf[i+0]>>4)&0x0f; colors[1] = (buf[i+0])&0x0f; colors[2] = (buf[i+1]>>4)&0x0f; colors[3] = (buf[i+1])&0x0f; for( j = 0; j < 4; j++ ) { /* * Not sure what is happening here, in theory * the palette is in YCbCr. And we want YUV. * * However it looks more like YCrCb (according * to pgcedit). And the scalers for YCrCb don't * work, but I get the right colours by doing * no conversion. */ uint32_t color = w->subtitle->palette[colors[j]]; uint8_t Cr, Cb, y; y = (color>>16) & 0xff; Cr = (color>>8) & 0xff; Cb = (color) & 0xff; pv->lum[3-j] = y; pv->chromaU[3-j] = Cb; pv->chromaV[3-j] = Cr; /* hb_log("color[%d] y = %d, u = %d, v = %d", 3-j, pv->lum[3-j], pv->chromaU[3-j], pv->chromaV[3-j]); */ } i += 2; break; } case 0x04: // 0x04 - SET_CONTR - Set Contrast { /* * SET_CONTR - directly provides the four contrast * (alpha blend) values to associate with the four * pixel values */ uint8_t alpha[4]; alpha[3] = ((buf[i+0] >> 4) & 0x0f) << 4; alpha[2] = ((buf[i+0] ) & 0x0f) << 4; alpha[1] = ((buf[i+1] >> 4) & 0x0f) << 4; alpha[0] = ((buf[i+1] ) & 0x0f) << 4; int lastAlpha = pv->alpha[3] + pv->alpha[2] + pv->alpha[1] + pv->alpha[0]; int currAlpha = alpha[3] + alpha[2] + alpha[1] + alpha[0]; // fading-in, save the highest alpha value if( currAlpha > lastAlpha ) { pv->alpha[3] = alpha[3]; pv->alpha[2] = alpha[2]; pv->alpha[1] = alpha[1]; pv->alpha[0] = alpha[0]; } // fading-out if (currAlpha < lastAlpha && pv->pts_stop == AV_NOPTS_VALUE) { pv->pts_stop = pv->pts + date * 1024; } i += 2; break; } case 0x05: // 0x05 - SET_DAREA - defines the display area { pv->x = (buf[i+0]<<4) | ((buf[i+1]>>4)&0x0f); pv->width = (((buf[i+1]&0x0f)<<8)| buf[i+2]) - pv->x + 1; pv->y = (buf[i+3]<<4)| ((buf[i+4]>>4)&0x0f); pv->height = (((buf[i+4]&0x0f)<<8)| buf[i+5]) - pv->y + 1; i += 6; break; } case 0x06: // 0x06 - SET_DSPXA - defines the pixel data addresses { pv->offsets[0] = ( buf[i] << 8 ) | buf[i+1]; i += 2; pv->offsets[1] = ( buf[i] << 8 ) | buf[i+1]; i += 2; break; } } } if( i > next ) { break; } i = next; } // Generate timestamps if they are not set if( pv->pts_start == AV_NOPTS_VALUE ) { // Set pts to end of last sub if the start time is unknown. pv->pts_start = pv->pts; } } /*********************************************************************** * CropSubtitle *********************************************************************** * Given a raw decoded subtitle, detects transparent borders and * returns a cropped subtitle in a hb_buffer_t ready to be used by * the renderer, or NULL if the subtitle was completely transparent **********************************************************************/ static int LineIsTransparent( hb_work_object_t * w, uint8_t * p ) { hb_work_private_t * pv = w->private_data; int i; for( i = 0; i < pv->width; i++ ) { if( p[i] ) { return 0; } } return 1; } static int ColumnIsTransparent( hb_work_object_t * w, uint8_t * p ) { hb_work_private_t * pv = w->private_data; int i; for( i = 0; i < pv->height; i++ ) { if( p[i*pv->width] ) { return 0; } } return 1; } // Brain dead resampler. This should really use swscale... // Uses Bresenham algo to pick source samples for averaging static void resample( uint8_t * dst, uint8_t * src, int dst_w, int src_w ) { int dst_x, src_x, err, cnt, sum, val; if( dst_w < src_w ) { // sample down err = 0; sum = 0; val = 0; cnt = 0; err = src_w / 2; dst_x = 0; for( src_x = 0; src_x < src_w; src_x++ ) { sum += src[src_x]; cnt++; err -= dst_w; if( err < 0 ) { val = sum / cnt; dst[dst_x++] = val; sum = cnt = 0; err += src_w; } } for( ; dst_x < dst_w; dst_x++ ) { dst[dst_x] = val; } } else { // sample up err = 0; err = dst_w / 2; src_x = 0; for( dst_x = 0; dst_x < dst_w; dst_x++ ) { dst[dst_x] = src[src_x]; err -= src_w; if( err < 0 ) { src_x++; err += dst_w; } } } } static hb_buffer_t * CropSubtitle( hb_work_object_t * w, uint8_t * raw ) { hb_work_private_t * pv = w->private_data; int i; int crop[4] = { -1,-1,-1,-1 }; uint8_t * alpha; int realwidth, realheight; hb_buffer_t * buf; uint8_t * lum_in, * alpha_in, * u_in, * v_in; alpha = raw + pv->width * pv->height; /* Top */ for( i = 0; i < pv->height; i++ ) { if( !LineIsTransparent( w, &alpha[i*pv->width] ) ) { crop[0] = i; break; } } if( crop[0] < 0 ) { /* Empty subtitle */ return NULL; } /* Bottom */ for( i = pv->height - 1; i >= 0; i-- ) { if( !LineIsTransparent( w, &alpha[i*pv->width] ) ) { crop[1] = i; break; } } /* Left */ for( i = 0; i < pv->width; i++ ) { if( !ColumnIsTransparent( w, &alpha[i] ) ) { crop[2] = i; break; } } /* Right */ for( i = pv->width - 1; i >= 0; i-- ) { if( !ColumnIsTransparent( w, &alpha[i] ) ) { crop[3] = i; break; } } realwidth = crop[3] - crop[2] + 1; realheight = crop[1] - crop[0] + 1; buf = hb_frame_buffer_init( AV_PIX_FMT_YUVA420P, realwidth, realheight ); buf->s.frametype = HB_FRAME_SUBTITLE; buf->s.start = pv->pts_start; buf->s.stop = pv->pts_stop; buf->s.type = SUBTITLE_BUF; buf->f.x = pv->x + crop[2]; buf->f.y = pv->y + crop[0]; lum_in = raw + crop[0] * pv->width + crop[2]; alpha_in = lum_in + pv->width * pv->height; u_in = alpha_in + pv->width * pv->height; v_in = u_in + pv->width * pv->height; uint8_t *dst; for( i = 0; i < realheight; i++ ) { // Luma dst = buf->plane[0].data + buf->plane[0].stride * i; memcpy( dst, lum_in, realwidth ); if( ( i & 1 ) == 0 ) { // chroma U (resample to YUV420) dst = buf->plane[1].data + buf->plane[1].stride * ( i >> 1 ); resample( dst, u_in, buf->plane[1].width, realwidth ); // chroma V (resample to YUV420) dst = buf->plane[2].data + buf->plane[2].stride * ( i >> 1 ); resample( dst, v_in, buf->plane[2].width, realwidth ); } // Alpha dst = buf->plane[3].data + buf->plane[3].stride * i; memcpy( dst, alpha_in, realwidth ); lum_in += pv->width; alpha_in += pv->width; u_in += pv->width; v_in += pv->width; } return buf; } static hb_buffer_t * Decode( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; int code, line, col; int offsets[2]; int * offset; hb_buffer_t * buf; uint8_t * buf_raw = NULL; hb_job_t * job = pv->job; /* Get infos about the subtitle */ ParseControls( w ); if( job->indepth_scan || ( w->subtitle->config.force && pv->pts_forced == 0 ) ) { /* * Don't encode subtitles when doing a scan. * * When forcing subtitles, ignore all those that don't * have the forced flag set. */ hb_buffer_close( &pv->buf ); return NULL; } if (w->subtitle->config.dest == PASSTHRUSUB) { pv->buf->s.start = pv->pts_start; pv->buf->s.stop = pv->pts_stop; buf = pv->buf; pv->buf = NULL; return buf; } /* Do the actual decoding now */ buf_raw = malloc( ( pv->width * pv->height ) * 4 ); #define GET_NEXT_NIBBLE code = ( code << 4 ) | ( ( ( *offset & 1 ) ? \ ( pv->buf->data[((*offset)>>1)] & 0xF ) : ( pv->buf->data[((*offset)>>1)] >> 4 ) ) ); \ (*offset)++ offsets[0] = pv->offsets[0] * 2; offsets[1] = pv->offsets[1] * 2; for( line = 0; line < pv->height; line++ ) { /* Select even or odd field */ offset = ( line & 1 ) ? &offsets[1] : &offsets[0]; for( col = 0; col < pv->width; col += code >> 2 ) { uint8_t * lum, * alpha, * chromaU, * chromaV; code = 0; GET_NEXT_NIBBLE; if( code < 0x4 ) { GET_NEXT_NIBBLE; if( code < 0x10 ) { GET_NEXT_NIBBLE; if( code < 0x40 ) { GET_NEXT_NIBBLE; if( code < 0x100 ) { /* End of line */ code |= ( pv->width - col ) << 2; } } } } lum = buf_raw; alpha = lum + pv->width * pv->height; chromaU = alpha + pv->width * pv->height; chromaV = chromaU + pv->width * pv->height; memset( lum + line * pv->width + col, pv->lum[code & 3], code >> 2 ); memset( alpha + line * pv->width + col, pv->alpha[code & 3], code >> 2 ); memset( chromaU + line * pv->width + col, pv->chromaU[code & 3], code >> 2 ); memset( chromaV + line * pv->width + col, pv->chromaV[code & 3], code >> 2 ); } /* Byte-align */ if( *offset & 1 ) { (*offset)++; } } hb_buffer_close( &pv->buf ); /* Crop subtitle (remove transparent borders) */ buf = CropSubtitle( w, buf_raw ); free( buf_raw ); return buf; } HandBrake-0.10.2/libhb/decutf8sub.c0000664000175200017520000000402012463330511017331 0ustar handbrakehandbrake/* decutf8sub.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ /* * Decoder for UTF-8 subtitles obtained from file input-sources. * * Input and output packet format is UTF-8 encoded text, * with limited HTML-style markup (only , , and ). * * @author David Foster (davidfstr) */ #include #include #include "hb.h" #include "decsrtsub.h" struct hb_work_private_s { int line; // SSA line number }; static int decutf8Init(hb_work_object_t *w, hb_job_t *job) { hb_work_private_t * pv; pv = calloc( 1, sizeof( hb_work_private_t ) ); if (pv == NULL) return 1; w->private_data = pv; // Generate generic SSA Script Info. int height = job->title->height - job->crop[0] - job->crop[1]; int width = job->title->width - job->crop[2] - job->crop[3]; hb_subtitle_add_ssa_header(w->subtitle, width, height); return 0; } static int decutf8Work(hb_work_object_t * w, hb_buffer_t **buf_in, hb_buffer_t **buf_out) { hb_work_private_t * pv = w->private_data; // Pass the packets through without modification hb_buffer_t *out = *buf_in; out->s.frametype = HB_FRAME_SUBTITLE; // Warn if the subtitle's duration has not been passed through by the // demuxer, which will prevent the subtitle from displaying at all if (out->s.stop == 0) { hb_log("decutf8sub: subtitle packet lacks duration"); } hb_srt_to_ssa(out, ++pv->line); *buf_in = NULL; *buf_out = out; if (out->size == 0) return HB_WORK_DONE; return HB_WORK_OK; } static void decutf8Close(hb_work_object_t *w) { free(w->private_data); } hb_work_object_t hb_decutf8sub = { WORK_DECUTF8SUB, "UTF-8 Subtitle Decoder", decutf8Init, decutf8Work, decutf8Close }; HandBrake-0.10.2/libhb/nal_units.c0000664000175200017520000001226412463330511017262 0ustar handbrakehandbrake/* nal_units.c * * Copyright (c) 2003-2015 HandBrake Team * This file is part of the HandBrake source code. * Homepage: . * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include #include #include "common.h" #include "nal_units.h" static const uint8_t hb_annexb_startcode[] = { 0x00, 0x00, 0x00, 0x01, }; size_t hb_nal_unit_write_annexb(uint8_t *buf, const uint8_t *nal_unit, const size_t nal_unit_size) { if (buf != NULL) { memcpy(buf, hb_annexb_startcode, sizeof(hb_annexb_startcode)); memcpy(buf + sizeof(hb_annexb_startcode), nal_unit, nal_unit_size); } return sizeof(hb_annexb_startcode) + nal_unit_size; } size_t hb_nal_unit_write_isomp4(uint8_t *buf, const uint8_t *nal_unit, const size_t nal_unit_size) { int i; uint8_t length[4]; // 4-byte length replaces Annex B start code prefix if (buf != NULL) { for (i = 0; i < sizeof(length); i++) { length[i] = (nal_unit_size >> (8 * (sizeof(length) - 1 - i))) & 0xff; } memcpy(buf, &length[0], sizeof(length)); memcpy(buf + sizeof(length), nal_unit, nal_unit_size); } return sizeof(length) + nal_unit_size; } uint8_t* hb_annexb_find_next_nalu(const uint8_t *start, size_t *size) { uint8_t *nal = NULL; uint8_t *buf = (uint8_t*)start; uint8_t *end = (uint8_t*)start + *size; /* Look for an Annex B start code prefix (3-byte sequence == 1) */ while (end - buf > 3) { if (!buf[0] && !buf[1] && buf[2] == 1) { nal = (buf += 3); // NAL unit begins after start code break; } buf++; } if (nal == NULL) { *size = 0; return NULL; } /* * Start code prefix found, look for the next one to determine the size * * A 4-byte sequence == 1 is also a start code, so check for a 3-byte * sequence == 0 too (start code emulation prevention will prevent such a * sequence from occurring outside of a start code prefix) */ while (end - buf > 3) { if (!buf[0] && !buf[1] && (!buf[2] || buf[2] == 1)) { end = buf; break; } buf++; } *size = end - nal; return nal; } hb_buffer_t* hb_nal_bitstream_annexb_to_mp4(const uint8_t *data, const size_t size) { hb_buffer_t *out; uint8_t *buf, *end; size_t out_size, buf_size; out_size = 0; buf_size = size; buf = (uint8_t*)data; end = (uint8_t*)data + size; while ((buf = hb_annexb_find_next_nalu(buf, &buf_size)) != NULL) { out_size += hb_nal_unit_write_isomp4(NULL, buf, buf_size); buf_size = end - buf; } out = hb_buffer_init(out_size); if (out == NULL) { hb_error("hb_nal_bitstream_annexb_to_mp4: hb_buffer_init failed"); return NULL; } out_size = 0; buf_size = size; buf = (uint8_t*)data; end = (uint8_t*)data + size; while ((buf = hb_annexb_find_next_nalu(buf, &buf_size)) != NULL) { out_size += hb_nal_unit_write_isomp4(out->data + out_size, buf, buf_size); buf_size = end - buf; } return out; } static size_t mp4_nal_unit_length(const uint8_t *data, const size_t nal_length_size, size_t *nal_unit_length) { uint8_t i; /* In MP4, NAL units are preceded by a 2-4 byte length field */ for (i = 0, *nal_unit_length = 0; i < nal_length_size; i++) { *nal_unit_length |= data[i] << (8 * (nal_length_size - 1 - i)); } return nal_length_size; } hb_buffer_t* hb_nal_bitstream_mp4_to_annexb(const uint8_t *data, const size_t size, const uint8_t nal_length_size) { hb_buffer_t *out; uint8_t *buf, *end; size_t out_size, nal_size; out_size = 0; buf = (uint8_t*)data; end = (uint8_t*)data + size; while (end - buf > nal_length_size) { buf += mp4_nal_unit_length(buf, nal_length_size, &nal_size); if (end - buf < nal_size) { hb_log("hb_nal_bitstream_mp4_to_annexb: truncated bitstream" " (remaining: %lu, expected: %lu)", end - buf, nal_size); return NULL; } out_size += hb_nal_unit_write_annexb(NULL, buf, nal_size); buf += nal_size; } out = hb_buffer_init(out_size); if (out == NULL) { hb_error("hb_nal_bitstream_mp4_to_annexb: hb_buffer_init failed"); return NULL; } out_size = 0; buf = (uint8_t*)data; end = (uint8_t*)data + size; while (end - buf > nal_length_size) { buf += mp4_nal_unit_length(buf, nal_length_size, &nal_size); out_size += hb_nal_unit_write_annexb(out->data + out_size, buf, nal_size); buf += nal_size; } return out; } HandBrake-0.10.2/libhb/extras/0000775000175200017520000000000012535641635016437 5ustar handbrakehandbrakeHandBrake-0.10.2/libhb/extras/cl.h0000664000175200017520000016665512230760063017216 0ustar handbrakehandbrake/******************************************************************************* * Copyright (c) 2008 - 2012 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and/or associated documentation files (the * "Materials"), to deal in the Materials without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Materials, and to * permit persons to whom the Materials are furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Materials. * * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. ******************************************************************************/ #ifndef __OPENCL_CL_H #define __OPENCL_CL_H #include "cl_platform.h" #ifdef __cplusplus extern "C" { #endif /******************************************************************************/ typedef struct _cl_platform_id * cl_platform_id; typedef struct _cl_device_id * cl_device_id; typedef struct _cl_context * cl_context; typedef struct _cl_command_queue * cl_command_queue; typedef struct _cl_mem * cl_mem; typedef struct _cl_program * cl_program; typedef struct _cl_kernel * cl_kernel; typedef struct _cl_event * cl_event; typedef struct _cl_sampler * cl_sampler; typedef cl_uint cl_bool; /* WARNING! Unlike cl_ types in cl_platform.h, cl_bool is not guaranteed to be the same size as the bool in kernels. */ typedef cl_ulong cl_bitfield; typedef cl_bitfield cl_device_type; typedef cl_uint cl_platform_info; typedef cl_uint cl_device_info; typedef cl_bitfield cl_device_fp_config; typedef cl_uint cl_device_mem_cache_type; typedef cl_uint cl_device_local_mem_type; typedef cl_bitfield cl_device_exec_capabilities; typedef cl_bitfield cl_command_queue_properties; typedef intptr_t cl_device_partition_property; typedef cl_bitfield cl_device_affinity_domain; typedef intptr_t cl_context_properties; typedef cl_uint cl_context_info; typedef cl_uint cl_command_queue_info; typedef cl_uint cl_channel_order; typedef cl_uint cl_channel_type; typedef cl_bitfield cl_mem_flags; typedef cl_uint cl_mem_object_type; typedef cl_uint cl_mem_info; typedef cl_bitfield cl_mem_migration_flags; typedef cl_uint cl_image_info; typedef cl_uint cl_buffer_create_type; typedef cl_uint cl_addressing_mode; typedef cl_uint cl_filter_mode; typedef cl_uint cl_sampler_info; typedef cl_bitfield cl_map_flags; typedef cl_uint cl_program_info; typedef cl_uint cl_program_build_info; typedef cl_uint cl_program_binary_type; typedef cl_int cl_build_status; typedef cl_uint cl_kernel_info; typedef cl_uint cl_kernel_arg_info; typedef cl_uint cl_kernel_arg_address_qualifier; typedef cl_uint cl_kernel_arg_access_qualifier; typedef cl_bitfield cl_kernel_arg_type_qualifier; typedef cl_uint cl_kernel_work_group_info; typedef cl_uint cl_event_info; typedef cl_uint cl_command_type; typedef cl_uint cl_profiling_info; typedef struct _cl_image_format { cl_channel_order image_channel_order; cl_channel_type image_channel_data_type; } cl_image_format; typedef struct _cl_image_desc { cl_mem_object_type image_type; size_t image_width; size_t image_height; size_t image_depth; size_t image_array_size; size_t image_row_pitch; size_t image_slice_pitch; cl_uint num_mip_levels; cl_uint num_samples; cl_mem buffer; } cl_image_desc; typedef struct _cl_buffer_region { size_t origin; size_t size; } cl_buffer_region; /******************************************************************************/ /* Error Codes */ #define CL_SUCCESS 0 #define CL_DEVICE_NOT_FOUND -1 #define CL_DEVICE_NOT_AVAILABLE -2 #define CL_COMPILER_NOT_AVAILABLE -3 #define CL_MEM_OBJECT_ALLOCATION_FAILURE -4 #define CL_OUT_OF_RESOURCES -5 #define CL_OUT_OF_HOST_MEMORY -6 #define CL_PROFILING_INFO_NOT_AVAILABLE -7 #define CL_MEM_COPY_OVERLAP -8 #define CL_IMAGE_FORMAT_MISMATCH -9 #define CL_IMAGE_FORMAT_NOT_SUPPORTED -10 #define CL_BUILD_PROGRAM_FAILURE -11 #define CL_MAP_FAILURE -12 #define CL_MISALIGNED_SUB_BUFFER_OFFSET -13 #define CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST -14 #define CL_COMPILE_PROGRAM_FAILURE -15 #define CL_LINKER_NOT_AVAILABLE -16 #define CL_LINK_PROGRAM_FAILURE -17 #define CL_DEVICE_PARTITION_FAILED -18 #define CL_KERNEL_ARG_INFO_NOT_AVAILABLE -19 #define CL_INVALID_VALUE -30 #define CL_INVALID_DEVICE_TYPE -31 #define CL_INVALID_PLATFORM -32 #define CL_INVALID_DEVICE -33 #define CL_INVALID_CONTEXT -34 #define CL_INVALID_QUEUE_PROPERTIES -35 #define CL_INVALID_COMMAND_QUEUE -36 #define CL_INVALID_HOST_PTR -37 #define CL_INVALID_MEM_OBJECT -38 #define CL_INVALID_IMAGE_FORMAT_DESCRIPTOR -39 #define CL_INVALID_IMAGE_SIZE -40 #define CL_INVALID_SAMPLER -41 #define CL_INVALID_BINARY -42 #define CL_INVALID_BUILD_OPTIONS -43 #define CL_INVALID_PROGRAM -44 #define CL_INVALID_PROGRAM_EXECUTABLE -45 #define CL_INVALID_KERNEL_NAME -46 #define CL_INVALID_KERNEL_DEFINITION -47 #define CL_INVALID_KERNEL -48 #define CL_INVALID_ARG_INDEX -49 #define CL_INVALID_ARG_VALUE -50 #define CL_INVALID_ARG_SIZE -51 #define CL_INVALID_KERNEL_ARGS -52 #define CL_INVALID_WORK_DIMENSION -53 #define CL_INVALID_WORK_GROUP_SIZE -54 #define CL_INVALID_WORK_ITEM_SIZE -55 #define CL_INVALID_GLOBAL_OFFSET -56 #define CL_INVALID_EVENT_WAIT_LIST -57 #define CL_INVALID_EVENT -58 #define CL_INVALID_OPERATION -59 #define CL_INVALID_GL_OBJECT -60 #define CL_INVALID_BUFFER_SIZE -61 #define CL_INVALID_MIP_LEVEL -62 #define CL_INVALID_GLOBAL_WORK_SIZE -63 #define CL_INVALID_PROPERTY -64 #define CL_INVALID_IMAGE_DESCRIPTOR -65 #define CL_INVALID_COMPILER_OPTIONS -66 #define CL_INVALID_LINKER_OPTIONS -67 #define CL_INVALID_DEVICE_PARTITION_COUNT -68 /* OpenCL Version */ #define CL_VERSION_1_0 1 #define CL_VERSION_1_1 1 #define CL_VERSION_1_2 1 /* cl_bool */ #define CL_FALSE 0 #define CL_TRUE 1 #define CL_BLOCKING CL_TRUE #define CL_NON_BLOCKING CL_FALSE /* cl_platform_info */ #define CL_PLATFORM_PROFILE 0x0900 #define CL_PLATFORM_VERSION 0x0901 #define CL_PLATFORM_NAME 0x0902 #define CL_PLATFORM_VENDOR 0x0903 #define CL_PLATFORM_EXTENSIONS 0x0904 /* cl_device_type - bitfield */ #define CL_DEVICE_TYPE_DEFAULT (1 << 0) #define CL_DEVICE_TYPE_CPU (1 << 1) #define CL_DEVICE_TYPE_GPU (1 << 2) #define CL_DEVICE_TYPE_ACCELERATOR (1 << 3) #define CL_DEVICE_TYPE_CUSTOM (1 << 4) #define CL_DEVICE_TYPE_ALL 0xFFFFFFFF /* cl_device_info */ #define CL_DEVICE_TYPE 0x1000 #define CL_DEVICE_VENDOR_ID 0x1001 #define CL_DEVICE_MAX_COMPUTE_UNITS 0x1002 #define CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS 0x1003 #define CL_DEVICE_MAX_WORK_GROUP_SIZE 0x1004 #define CL_DEVICE_MAX_WORK_ITEM_SIZES 0x1005 #define CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR 0x1006 #define CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT 0x1007 #define CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT 0x1008 #define CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG 0x1009 #define CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT 0x100A #define CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE 0x100B #define CL_DEVICE_MAX_CLOCK_FREQUENCY 0x100C #define CL_DEVICE_ADDRESS_BITS 0x100D #define CL_DEVICE_MAX_READ_IMAGE_ARGS 0x100E #define CL_DEVICE_MAX_WRITE_IMAGE_ARGS 0x100F #define CL_DEVICE_MAX_MEM_ALLOC_SIZE 0x1010 #define CL_DEVICE_IMAGE2D_MAX_WIDTH 0x1011 #define CL_DEVICE_IMAGE2D_MAX_HEIGHT 0x1012 #define CL_DEVICE_IMAGE3D_MAX_WIDTH 0x1013 #define CL_DEVICE_IMAGE3D_MAX_HEIGHT 0x1014 #define CL_DEVICE_IMAGE3D_MAX_DEPTH 0x1015 #define CL_DEVICE_IMAGE_SUPPORT 0x1016 #define CL_DEVICE_MAX_PARAMETER_SIZE 0x1017 #define CL_DEVICE_MAX_SAMPLERS 0x1018 #define CL_DEVICE_MEM_BASE_ADDR_ALIGN 0x1019 #define CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE 0x101A #define CL_DEVICE_SINGLE_FP_CONFIG 0x101B #define CL_DEVICE_GLOBAL_MEM_CACHE_TYPE 0x101C #define CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE 0x101D #define CL_DEVICE_GLOBAL_MEM_CACHE_SIZE 0x101E #define CL_DEVICE_GLOBAL_MEM_SIZE 0x101F #define CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE 0x1020 #define CL_DEVICE_MAX_CONSTANT_ARGS 0x1021 #define CL_DEVICE_LOCAL_MEM_TYPE 0x1022 #define CL_DEVICE_LOCAL_MEM_SIZE 0x1023 #define CL_DEVICE_ERROR_CORRECTION_SUPPORT 0x1024 #define CL_DEVICE_PROFILING_TIMER_RESOLUTION 0x1025 #define CL_DEVICE_ENDIAN_LITTLE 0x1026 #define CL_DEVICE_AVAILABLE 0x1027 #define CL_DEVICE_COMPILER_AVAILABLE 0x1028 #define CL_DEVICE_EXECUTION_CAPABILITIES 0x1029 #define CL_DEVICE_QUEUE_PROPERTIES 0x102A #define CL_DEVICE_NAME 0x102B #define CL_DEVICE_VENDOR 0x102C #define CL_DRIVER_VERSION 0x102D #define CL_DEVICE_PROFILE 0x102E #define CL_DEVICE_VERSION 0x102F #define CL_DEVICE_EXTENSIONS 0x1030 #define CL_DEVICE_PLATFORM 0x1031 #define CL_DEVICE_DOUBLE_FP_CONFIG 0x1032 /* 0x1033 reserved for CL_DEVICE_HALF_FP_CONFIG */ #define CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF 0x1034 #define CL_DEVICE_HOST_UNIFIED_MEMORY 0x1035 #define CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR 0x1036 #define CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT 0x1037 #define CL_DEVICE_NATIVE_VECTOR_WIDTH_INT 0x1038 #define CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG 0x1039 #define CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT 0x103A #define CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE 0x103B #define CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF 0x103C #define CL_DEVICE_OPENCL_C_VERSION 0x103D #define CL_DEVICE_LINKER_AVAILABLE 0x103E #define CL_DEVICE_BUILT_IN_KERNELS 0x103F #define CL_DEVICE_IMAGE_MAX_BUFFER_SIZE 0x1040 #define CL_DEVICE_IMAGE_MAX_ARRAY_SIZE 0x1041 #define CL_DEVICE_PARENT_DEVICE 0x1042 #define CL_DEVICE_PARTITION_MAX_SUB_DEVICES 0x1043 #define CL_DEVICE_PARTITION_PROPERTIES 0x1044 #define CL_DEVICE_PARTITION_AFFINITY_DOMAIN 0x1045 #define CL_DEVICE_PARTITION_TYPE 0x1046 #define CL_DEVICE_REFERENCE_COUNT 0x1047 #define CL_DEVICE_PREFERRED_INTEROP_USER_SYNC 0x1048 #define CL_DEVICE_PRINTF_BUFFER_SIZE 0x1049 #define CL_DEVICE_IMAGE_PITCH_ALIGNMENT 0x104A #define CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT 0x104B /* cl_device_fp_config - bitfield */ #define CL_FP_DENORM (1 << 0) #define CL_FP_INF_NAN (1 << 1) #define CL_FP_ROUND_TO_NEAREST (1 << 2) #define CL_FP_ROUND_TO_ZERO (1 << 3) #define CL_FP_ROUND_TO_INF (1 << 4) #define CL_FP_FMA (1 << 5) #define CL_FP_SOFT_FLOAT (1 << 6) #define CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT (1 << 7) /* cl_device_mem_cache_type */ #define CL_NONE 0x0 #define CL_READ_ONLY_CACHE 0x1 #define CL_READ_WRITE_CACHE 0x2 /* cl_device_local_mem_type */ #define CL_LOCAL 0x1 #define CL_GLOBAL 0x2 /* cl_device_exec_capabilities - bitfield */ #define CL_EXEC_KERNEL (1 << 0) #define CL_EXEC_NATIVE_KERNEL (1 << 1) /* cl_command_queue_properties - bitfield */ #define CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE (1 << 0) #define CL_QUEUE_PROFILING_ENABLE (1 << 1) /* cl_context_info */ #define CL_CONTEXT_REFERENCE_COUNT 0x1080 #define CL_CONTEXT_DEVICES 0x1081 #define CL_CONTEXT_PROPERTIES 0x1082 #define CL_CONTEXT_NUM_DEVICES 0x1083 /* cl_context_properties */ #define CL_CONTEXT_PLATFORM 0x1084 #define CL_CONTEXT_INTEROP_USER_SYNC 0x1085 /* cl_device_partition_property */ #define CL_DEVICE_PARTITION_EQUALLY 0x1086 #define CL_DEVICE_PARTITION_BY_COUNTS 0x1087 #define CL_DEVICE_PARTITION_BY_COUNTS_LIST_END 0x0 #define CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN 0x1088 /* cl_device_affinity_domain */ #define CL_DEVICE_AFFINITY_DOMAIN_NUMA (1 << 0) #define CL_DEVICE_AFFINITY_DOMAIN_L4_CACHE (1 << 1) #define CL_DEVICE_AFFINITY_DOMAIN_L3_CACHE (1 << 2) #define CL_DEVICE_AFFINITY_DOMAIN_L2_CACHE (1 << 3) #define CL_DEVICE_AFFINITY_DOMAIN_L1_CACHE (1 << 4) #define CL_DEVICE_AFFINITY_DOMAIN_NEXT_PARTITIONABLE (1 << 5) /* cl_command_queue_info */ #define CL_QUEUE_CONTEXT 0x1090 #define CL_QUEUE_DEVICE 0x1091 #define CL_QUEUE_REFERENCE_COUNT 0x1092 #define CL_QUEUE_PROPERTIES 0x1093 /* cl_mem_flags - bitfield */ #define CL_MEM_READ_WRITE (1 << 0) #define CL_MEM_WRITE_ONLY (1 << 1) #define CL_MEM_READ_ONLY (1 << 2) #define CL_MEM_USE_HOST_PTR (1 << 3) #define CL_MEM_ALLOC_HOST_PTR (1 << 4) #define CL_MEM_COPY_HOST_PTR (1 << 5) // reserved (1 << 6) #define CL_MEM_HOST_WRITE_ONLY (1 << 7) #define CL_MEM_HOST_READ_ONLY (1 << 8) #define CL_MEM_HOST_NO_ACCESS (1 << 9) /* cl_mem_migration_flags - bitfield */ #define CL_MIGRATE_MEM_OBJECT_HOST (1 << 0) #define CL_MIGRATE_MEM_OBJECT_CONTENT_UNDEFINED (1 << 1) /* cl_channel_order */ #define CL_R 0x10B0 #define CL_A 0x10B1 #define CL_RG 0x10B2 #define CL_RA 0x10B3 #define CL_RGB 0x10B4 #define CL_RGBA 0x10B5 #define CL_BGRA 0x10B6 #define CL_ARGB 0x10B7 #define CL_INTENSITY 0x10B8 #define CL_LUMINANCE 0x10B9 #define CL_Rx 0x10BA #define CL_RGx 0x10BB #define CL_RGBx 0x10BC #define CL_DEPTH 0x10BD #define CL_DEPTH_STENCIL 0x10BE /* cl_channel_type */ #define CL_SNORM_INT8 0x10D0 #define CL_SNORM_INT16 0x10D1 #define CL_UNORM_INT8 0x10D2 #define CL_UNORM_INT16 0x10D3 #define CL_UNORM_SHORT_565 0x10D4 #define CL_UNORM_SHORT_555 0x10D5 #define CL_UNORM_INT_101010 0x10D6 #define CL_SIGNED_INT8 0x10D7 #define CL_SIGNED_INT16 0x10D8 #define CL_SIGNED_INT32 0x10D9 #define CL_UNSIGNED_INT8 0x10DA #define CL_UNSIGNED_INT16 0x10DB #define CL_UNSIGNED_INT32 0x10DC #define CL_HALF_FLOAT 0x10DD #define CL_FLOAT 0x10DE #define CL_UNORM_INT24 0x10DF /* cl_mem_object_type */ #define CL_MEM_OBJECT_BUFFER 0x10F0 #define CL_MEM_OBJECT_IMAGE2D 0x10F1 #define CL_MEM_OBJECT_IMAGE3D 0x10F2 #define CL_MEM_OBJECT_IMAGE2D_ARRAY 0x10F3 #define CL_MEM_OBJECT_IMAGE1D 0x10F4 #define CL_MEM_OBJECT_IMAGE1D_ARRAY 0x10F5 #define CL_MEM_OBJECT_IMAGE1D_BUFFER 0x10F6 /* cl_mem_info */ #define CL_MEM_TYPE 0x1100 #define CL_MEM_FLAGS 0x1101 #define CL_MEM_SIZE 0x1102 #define CL_MEM_HOST_PTR 0x1103 #define CL_MEM_MAP_COUNT 0x1104 #define CL_MEM_REFERENCE_COUNT 0x1105 #define CL_MEM_CONTEXT 0x1106 #define CL_MEM_ASSOCIATED_MEMOBJECT 0x1107 #define CL_MEM_OFFSET 0x1108 /* cl_image_info */ #define CL_IMAGE_FORMAT 0x1110 #define CL_IMAGE_ELEMENT_SIZE 0x1111 #define CL_IMAGE_ROW_PITCH 0x1112 #define CL_IMAGE_SLICE_PITCH 0x1113 #define CL_IMAGE_WIDTH 0x1114 #define CL_IMAGE_HEIGHT 0x1115 #define CL_IMAGE_DEPTH 0x1116 #define CL_IMAGE_ARRAY_SIZE 0x1117 #define CL_IMAGE_BUFFER 0x1118 #define CL_IMAGE_NUM_MIP_LEVELS 0x1119 #define CL_IMAGE_NUM_SAMPLES 0x111A /* cl_addressing_mode */ #define CL_ADDRESS_NONE 0x1130 #define CL_ADDRESS_CLAMP_TO_EDGE 0x1131 #define CL_ADDRESS_CLAMP 0x1132 #define CL_ADDRESS_REPEAT 0x1133 #define CL_ADDRESS_MIRRORED_REPEAT 0x1134 /* cl_filter_mode */ #define CL_FILTER_NEAREST 0x1140 #define CL_FILTER_LINEAR 0x1141 /* cl_sampler_info */ #define CL_SAMPLER_REFERENCE_COUNT 0x1150 #define CL_SAMPLER_CONTEXT 0x1151 #define CL_SAMPLER_NORMALIZED_COORDS 0x1152 #define CL_SAMPLER_ADDRESSING_MODE 0x1153 #define CL_SAMPLER_FILTER_MODE 0x1154 /* cl_map_flags - bitfield */ #define CL_MAP_READ (1 << 0) #define CL_MAP_WRITE (1 << 1) #define CL_MAP_WRITE_INVALIDATE_REGION (1 << 2) /* cl_program_info */ #define CL_PROGRAM_REFERENCE_COUNT 0x1160 #define CL_PROGRAM_CONTEXT 0x1161 #define CL_PROGRAM_NUM_DEVICES 0x1162 #define CL_PROGRAM_DEVICES 0x1163 #define CL_PROGRAM_SOURCE 0x1164 #define CL_PROGRAM_BINARY_SIZES 0x1165 #define CL_PROGRAM_BINARIES 0x1166 #define CL_PROGRAM_NUM_KERNELS 0x1167 #define CL_PROGRAM_KERNEL_NAMES 0x1168 /* cl_program_build_info */ #define CL_PROGRAM_BUILD_STATUS 0x1181 #define CL_PROGRAM_BUILD_OPTIONS 0x1182 #define CL_PROGRAM_BUILD_LOG 0x1183 #define CL_PROGRAM_BINARY_TYPE 0x1184 /* cl_program_binary_type */ #define CL_PROGRAM_BINARY_TYPE_NONE 0x0 #define CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT 0x1 #define CL_PROGRAM_BINARY_TYPE_LIBRARY 0x2 #define CL_PROGRAM_BINARY_TYPE_EXECUTABLE 0x4 /* cl_build_status */ #define CL_BUILD_SUCCESS 0 #define CL_BUILD_NONE -1 #define CL_BUILD_ERROR -2 #define CL_BUILD_IN_PROGRESS -3 /* cl_kernel_info */ #define CL_KERNEL_FUNCTION_NAME 0x1190 #define CL_KERNEL_NUM_ARGS 0x1191 #define CL_KERNEL_REFERENCE_COUNT 0x1192 #define CL_KERNEL_CONTEXT 0x1193 #define CL_KERNEL_PROGRAM 0x1194 #define CL_KERNEL_ATTRIBUTES 0x1195 /* cl_kernel_arg_info */ #define CL_KERNEL_ARG_ADDRESS_QUALIFIER 0x1196 #define CL_KERNEL_ARG_ACCESS_QUALIFIER 0x1197 #define CL_KERNEL_ARG_TYPE_NAME 0x1198 #define CL_KERNEL_ARG_TYPE_QUALIFIER 0x1199 #define CL_KERNEL_ARG_NAME 0x119A /* cl_kernel_arg_address_qualifier */ #define CL_KERNEL_ARG_ADDRESS_GLOBAL 0x119B #define CL_KERNEL_ARG_ADDRESS_LOCAL 0x119C #define CL_KERNEL_ARG_ADDRESS_CONSTANT 0x119D #define CL_KERNEL_ARG_ADDRESS_PRIVATE 0x119E /* cl_kernel_arg_access_qualifier */ #define CL_KERNEL_ARG_ACCESS_READ_ONLY 0x11A0 #define CL_KERNEL_ARG_ACCESS_WRITE_ONLY 0x11A1 #define CL_KERNEL_ARG_ACCESS_READ_WRITE 0x11A2 #define CL_KERNEL_ARG_ACCESS_NONE 0x11A3 /* cl_kernel_arg_type_qualifer */ #define CL_KERNEL_ARG_TYPE_NONE 0 #define CL_KERNEL_ARG_TYPE_CONST (1 << 0) #define CL_KERNEL_ARG_TYPE_RESTRICT (1 << 1) #define CL_KERNEL_ARG_TYPE_VOLATILE (1 << 2) /* cl_kernel_work_group_info */ #define CL_KERNEL_WORK_GROUP_SIZE 0x11B0 #define CL_KERNEL_COMPILE_WORK_GROUP_SIZE 0x11B1 #define CL_KERNEL_LOCAL_MEM_SIZE 0x11B2 #define CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE 0x11B3 #define CL_KERNEL_PRIVATE_MEM_SIZE 0x11B4 #define CL_KERNEL_GLOBAL_WORK_SIZE 0x11B5 /* cl_event_info */ #define CL_EVENT_COMMAND_QUEUE 0x11D0 #define CL_EVENT_COMMAND_TYPE 0x11D1 #define CL_EVENT_REFERENCE_COUNT 0x11D2 #define CL_EVENT_COMMAND_EXECUTION_STATUS 0x11D3 #define CL_EVENT_CONTEXT 0x11D4 /* cl_command_type */ #define CL_COMMAND_NDRANGE_KERNEL 0x11F0 #define CL_COMMAND_TASK 0x11F1 #define CL_COMMAND_NATIVE_KERNEL 0x11F2 #define CL_COMMAND_READ_BUFFER 0x11F3 #define CL_COMMAND_WRITE_BUFFER 0x11F4 #define CL_COMMAND_COPY_BUFFER 0x11F5 #define CL_COMMAND_READ_IMAGE 0x11F6 #define CL_COMMAND_WRITE_IMAGE 0x11F7 #define CL_COMMAND_COPY_IMAGE 0x11F8 #define CL_COMMAND_COPY_IMAGE_TO_BUFFER 0x11F9 #define CL_COMMAND_COPY_BUFFER_TO_IMAGE 0x11FA #define CL_COMMAND_MAP_BUFFER 0x11FB #define CL_COMMAND_MAP_IMAGE 0x11FC #define CL_COMMAND_UNMAP_MEM_OBJECT 0x11FD #define CL_COMMAND_MARKER 0x11FE #define CL_COMMAND_ACQUIRE_GL_OBJECTS 0x11FF #define CL_COMMAND_RELEASE_GL_OBJECTS 0x1200 #define CL_COMMAND_READ_BUFFER_RECT 0x1201 #define CL_COMMAND_WRITE_BUFFER_RECT 0x1202 #define CL_COMMAND_COPY_BUFFER_RECT 0x1203 #define CL_COMMAND_USER 0x1204 #define CL_COMMAND_BARRIER 0x1205 #define CL_COMMAND_MIGRATE_MEM_OBJECTS 0x1206 #define CL_COMMAND_FILL_BUFFER 0x1207 #define CL_COMMAND_FILL_IMAGE 0x1208 /* command execution status */ #define CL_COMPLETE 0x0 #define CL_RUNNING 0x1 #define CL_SUBMITTED 0x2 #define CL_QUEUED 0x3 /* cl_buffer_create_type */ #define CL_BUFFER_CREATE_TYPE_REGION 0x1220 /* cl_profiling_info */ #define CL_PROFILING_COMMAND_QUEUED 0x1280 #define CL_PROFILING_COMMAND_SUBMIT 0x1281 #define CL_PROFILING_COMMAND_START 0x1282 #define CL_PROFILING_COMMAND_END 0x1283 /********************************************************************************************************/ /* Platform API */ extern CL_API_ENTRY cl_int CL_API_CALL clGetPlatformIDs(cl_uint /* num_entries */, cl_platform_id * /* platforms */, cl_uint * /* num_platforms */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetPlatformInfo(cl_platform_id /* platform */, cl_platform_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; /* Device APIs */ extern CL_API_ENTRY cl_int CL_API_CALL clGetDeviceIDs(cl_platform_id /* platform */, cl_device_type /* device_type */, cl_uint /* num_entries */, cl_device_id * /* devices */, cl_uint * /* num_devices */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetDeviceInfo(cl_device_id /* device */, cl_device_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clCreateSubDevices(cl_device_id /* in_device */, const cl_device_partition_property * /* properties */, cl_uint /* num_devices */, cl_device_id * /* out_devices */, cl_uint * /* num_devices_ret */) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_int CL_API_CALL clRetainDevice(cl_device_id /* device */) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseDevice(cl_device_id /* device */) CL_API_SUFFIX__VERSION_1_2; /* Context APIs */ extern CL_API_ENTRY cl_context CL_API_CALL clCreateContext(const cl_context_properties * /* properties */, cl_uint /* num_devices */, const cl_device_id * /* devices */, void (CL_CALLBACK * /* pfn_notify */)(const char *, const void *, size_t, void *), void * /* user_data */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_context CL_API_CALL clCreateContextFromType(const cl_context_properties * /* properties */, cl_device_type /* device_type */, void (CL_CALLBACK * /* pfn_notify*/ )(const char *, const void *, size_t, void *), void * /* user_data */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clRetainContext(cl_context /* context */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseContext(cl_context /* context */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetContextInfo(cl_context /* context */, cl_context_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; /* Command Queue APIs */ extern CL_API_ENTRY cl_command_queue CL_API_CALL clCreateCommandQueue(cl_context /* context */, cl_device_id /* device */, cl_command_queue_properties /* properties */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clRetainCommandQueue(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseCommandQueue(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetCommandQueueInfo(cl_command_queue /* command_queue */, cl_command_queue_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; /* Memory Object APIs */ extern CL_API_ENTRY cl_mem CL_API_CALL clCreateBuffer(cl_context /* context */, cl_mem_flags /* flags */, size_t /* size */, void * /* host_ptr */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_mem CL_API_CALL clCreateSubBuffer(cl_mem /* buffer */, cl_mem_flags /* flags */, cl_buffer_create_type /* buffer_create_type */, const void * /* buffer_create_info */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_1; extern CL_API_ENTRY cl_mem CL_API_CALL clCreateImage(cl_context /* context */, cl_mem_flags /* flags */, const cl_image_format * /* image_format */, const cl_image_desc * /* image_desc */, void * /* host_ptr */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_int CL_API_CALL clRetainMemObject(cl_mem /* memobj */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseMemObject(cl_mem /* memobj */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetSupportedImageFormats(cl_context /* context */, cl_mem_flags /* flags */, cl_mem_object_type /* image_type */, cl_uint /* num_entries */, cl_image_format * /* image_formats */, cl_uint * /* num_image_formats */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetMemObjectInfo(cl_mem /* memobj */, cl_mem_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetImageInfo(cl_mem /* image */, cl_image_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clSetMemObjectDestructorCallback( cl_mem /* memobj */, void (CL_CALLBACK * /*pfn_notify*/)( cl_mem /* memobj */, void* /*user_data*/), void * /*user_data */ ) CL_API_SUFFIX__VERSION_1_1; /* Sampler APIs */ extern CL_API_ENTRY cl_sampler CL_API_CALL clCreateSampler(cl_context /* context */, cl_bool /* normalized_coords */, cl_addressing_mode /* addressing_mode */, cl_filter_mode /* filter_mode */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clRetainSampler(cl_sampler /* sampler */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseSampler(cl_sampler /* sampler */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetSamplerInfo(cl_sampler /* sampler */, cl_sampler_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; /* Program Object APIs */ extern CL_API_ENTRY cl_program CL_API_CALL clCreateProgramWithSource(cl_context /* context */, cl_uint /* count */, const char ** /* strings */, const size_t * /* lengths */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_program CL_API_CALL clCreateProgramWithBinary(cl_context /* context */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const size_t * /* lengths */, const unsigned char ** /* binaries */, cl_int * /* binary_status */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_program CL_API_CALL clCreateProgramWithBuiltInKernels(cl_context /* context */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const char * /* kernel_names */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_int CL_API_CALL clRetainProgram(cl_program /* program */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseProgram(cl_program /* program */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clBuildProgram(cl_program /* program */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const char * /* options */, void (CL_CALLBACK * /* pfn_notify */)(cl_program /* program */, void * /* user_data */), void * /* user_data */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clCompileProgram(cl_program /* program */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const char * /* options */, cl_uint /* num_input_headers */, const cl_program * /* input_headers */, const char ** /* header_include_names */, void (CL_CALLBACK * /* pfn_notify */)(cl_program /* program */, void * /* user_data */), void * /* user_data */) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_program CL_API_CALL clLinkProgram(cl_context /* context */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const char * /* options */, cl_uint /* num_input_programs */, const cl_program * /* input_programs */, void (CL_CALLBACK * /* pfn_notify */)(cl_program /* program */, void * /* user_data */), void * /* user_data */, cl_int * /* errcode_ret */ ) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_int CL_API_CALL clUnloadPlatformCompiler(cl_platform_id /* platform */) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_int CL_API_CALL clGetProgramInfo(cl_program /* program */, cl_program_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetProgramBuildInfo(cl_program /* program */, cl_device_id /* device */, cl_program_build_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; /* Kernel Object APIs */ extern CL_API_ENTRY cl_kernel CL_API_CALL clCreateKernel(cl_program /* program */, const char * /* kernel_name */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clCreateKernelsInProgram(cl_program /* program */, cl_uint /* num_kernels */, cl_kernel * /* kernels */, cl_uint * /* num_kernels_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clRetainKernel(cl_kernel /* kernel */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseKernel(cl_kernel /* kernel */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clSetKernelArg(cl_kernel /* kernel */, cl_uint /* arg_index */, size_t /* arg_size */, const void * /* arg_value */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetKernelInfo(cl_kernel /* kernel */, cl_kernel_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetKernelArgInfo(cl_kernel /* kernel */, cl_uint /* arg_indx */, cl_kernel_arg_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_int CL_API_CALL clGetKernelWorkGroupInfo(cl_kernel /* kernel */, cl_device_id /* device */, cl_kernel_work_group_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; /* Event Object APIs */ extern CL_API_ENTRY cl_int CL_API_CALL clWaitForEvents(cl_uint /* num_events */, const cl_event * /* event_list */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetEventInfo(cl_event /* event */, cl_event_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_event CL_API_CALL clCreateUserEvent(cl_context /* context */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_1; extern CL_API_ENTRY cl_int CL_API_CALL clRetainEvent(cl_event /* event */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseEvent(cl_event /* event */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clSetUserEventStatus(cl_event /* event */, cl_int /* execution_status */) CL_API_SUFFIX__VERSION_1_1; extern CL_API_ENTRY cl_int CL_API_CALL clSetEventCallback( cl_event /* event */, cl_int /* command_exec_callback_type */, void (CL_CALLBACK * /* pfn_notify */)(cl_event, cl_int, void *), void * /* user_data */) CL_API_SUFFIX__VERSION_1_1; /* Profiling APIs */ extern CL_API_ENTRY cl_int CL_API_CALL clGetEventProfilingInfo(cl_event /* event */, cl_profiling_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; /* Flush and Finish APIs */ extern CL_API_ENTRY cl_int CL_API_CALL clFlush(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clFinish(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; /* Enqueued Commands APIs */ extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueReadBuffer(cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_read */, size_t /* offset */, size_t /* size */, void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueReadBufferRect(cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_read */, const size_t * /* buffer_offset */, const size_t * /* host_offset */, const size_t * /* region */, size_t /* buffer_row_pitch */, size_t /* buffer_slice_pitch */, size_t /* host_row_pitch */, size_t /* host_slice_pitch */, void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_1; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueWriteBuffer(cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_write */, size_t /* offset */, size_t /* size */, const void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueWriteBufferRect(cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_write */, const size_t * /* buffer_offset */, const size_t * /* host_offset */, const size_t * /* region */, size_t /* buffer_row_pitch */, size_t /* buffer_slice_pitch */, size_t /* host_row_pitch */, size_t /* host_slice_pitch */, const void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_1; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueFillBuffer(cl_command_queue /* command_queue */, cl_mem /* buffer */, const void * /* pattern */, size_t /* pattern_size */, size_t /* offset */, size_t /* size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueCopyBuffer(cl_command_queue /* command_queue */, cl_mem /* src_buffer */, cl_mem /* dst_buffer */, size_t /* src_offset */, size_t /* dst_offset */, size_t /* size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueCopyBufferRect(cl_command_queue /* command_queue */, cl_mem /* src_buffer */, cl_mem /* dst_buffer */, const size_t * /* src_origin */, const size_t * /* dst_origin */, const size_t * /* region */, size_t /* src_row_pitch */, size_t /* src_slice_pitch */, size_t /* dst_row_pitch */, size_t /* dst_slice_pitch */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_1; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueReadImage(cl_command_queue /* command_queue */, cl_mem /* image */, cl_bool /* blocking_read */, const size_t * /* origin[3] */, const size_t * /* region[3] */, size_t /* row_pitch */, size_t /* slice_pitch */, void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueWriteImage(cl_command_queue /* command_queue */, cl_mem /* image */, cl_bool /* blocking_write */, const size_t * /* origin[3] */, const size_t * /* region[3] */, size_t /* input_row_pitch */, size_t /* input_slice_pitch */, const void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueFillImage(cl_command_queue /* command_queue */, cl_mem /* image */, const void * /* fill_color */, const size_t * /* origin[3] */, const size_t * /* region[3] */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueCopyImage(cl_command_queue /* command_queue */, cl_mem /* src_image */, cl_mem /* dst_image */, const size_t * /* src_origin[3] */, const size_t * /* dst_origin[3] */, const size_t * /* region[3] */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueCopyImageToBuffer(cl_command_queue /* command_queue */, cl_mem /* src_image */, cl_mem /* dst_buffer */, const size_t * /* src_origin[3] */, const size_t * /* region[3] */, size_t /* dst_offset */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueCopyBufferToImage(cl_command_queue /* command_queue */, cl_mem /* src_buffer */, cl_mem /* dst_image */, size_t /* src_offset */, const size_t * /* dst_origin[3] */, const size_t * /* region[3] */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY void * CL_API_CALL clEnqueueMapBuffer(cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_map */, cl_map_flags /* map_flags */, size_t /* offset */, size_t /* size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY void * CL_API_CALL clEnqueueMapImage(cl_command_queue /* command_queue */, cl_mem /* image */, cl_bool /* blocking_map */, cl_map_flags /* map_flags */, const size_t * /* origin[3] */, const size_t * /* region[3] */, size_t * /* image_row_pitch */, size_t * /* image_slice_pitch */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueUnmapMemObject(cl_command_queue /* command_queue */, cl_mem /* memobj */, void * /* mapped_ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueMigrateMemObjects(cl_command_queue /* command_queue */, cl_uint /* num_mem_objects */, const cl_mem * /* mem_objects */, cl_mem_migration_flags /* flags */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueNDRangeKernel(cl_command_queue /* command_queue */, cl_kernel /* kernel */, cl_uint /* work_dim */, const size_t * /* global_work_offset */, const size_t * /* global_work_size */, const size_t * /* local_work_size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueTask(cl_command_queue /* command_queue */, cl_kernel /* kernel */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueNativeKernel(cl_command_queue /* command_queue */, void (CL_CALLBACK * /*user_func*/)(void *), void * /* args */, size_t /* cb_args */, cl_uint /* num_mem_objects */, const cl_mem * /* mem_list */, const void ** /* args_mem_loc */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueMarkerWithWaitList(cl_command_queue /* command_queue */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueBarrierWithWaitList(cl_command_queue /* command_queue */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2; /* Extension function access * * Returns the extension function address for the given function name, * or NULL if a valid function can not be found. The client must * check to make sure the address is not NULL, before using or * calling the returned function address. */ extern CL_API_ENTRY void * CL_API_CALL clGetExtensionFunctionAddressForPlatform(cl_platform_id /* platform */, const char * /* func_name */) CL_API_SUFFIX__VERSION_1_2; // Deprecated OpenCL 1.1 APIs extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL clCreateImage2D(cl_context /* context */, cl_mem_flags /* flags */, const cl_image_format * /* image_format */, size_t /* image_width */, size_t /* image_height */, size_t /* image_row_pitch */, void * /* host_ptr */, cl_int * /* errcode_ret */) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL clCreateImage3D(cl_context /* context */, cl_mem_flags /* flags */, const cl_image_format * /* image_format */, size_t /* image_width */, size_t /* image_height */, size_t /* image_depth */, size_t /* image_row_pitch */, size_t /* image_slice_pitch */, void * /* host_ptr */, cl_int * /* errcode_ret */) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL clEnqueueMarker(cl_command_queue /* command_queue */, cl_event * /* event */) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL clEnqueueWaitForEvents(cl_command_queue /* command_queue */, cl_uint /* num_events */, const cl_event * /* event_list */) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL clEnqueueBarrier(cl_command_queue /* command_queue */) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL clUnloadCompiler(void) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED void * CL_API_CALL clGetExtensionFunctionAddress(const char * /* func_name */) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; #ifdef __cplusplus } #endif #endif /* __OPENCL_CL_H */ HandBrake-0.10.2/libhb/extras/cl_platform.h0000664000175200017520000012107512230760063021105 0ustar handbrakehandbrake/********************************************************************************** * Copyright (c) 2008-2012 The Khronos Group Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and/or associated documentation files (the * "Materials"), to deal in the Materials without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Materials, and to * permit persons to whom the Materials are furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Materials. * * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. **********************************************************************************/ /* $Revision: 11803 $ on $Date: 2010-06-25 10:02:12 -0700 (Fri, 25 Jun 2010) $ */ #ifndef __CL_PLATFORM_H #define __CL_PLATFORM_H #ifdef __APPLE__ /* Contains #defines for AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER below */ #include #endif #ifdef __cplusplus extern "C" { #endif #if defined(_WIN32) #define CL_API_ENTRY #define CL_API_CALL __stdcall #define CL_CALLBACK __stdcall #else #define CL_API_ENTRY #define CL_API_CALL #define CL_CALLBACK #endif #ifdef __APPLE__ #define CL_EXTENSION_WEAK_LINK __attribute__((weak_import)) #ifndef UNAVAILABLE_ATTRIBUTE #define UNAVAILABLE_ATTRIBUTE #endif #ifdef AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER #define CL_API_SUFFIX__VERSION_1_0 AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER #define CL_EXT_SUFFIX__VERSION_1_0 CL_EXTENSION_WEAK_LINK AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER #else #define CL_API_SUFFIX__VERSION_1_0 UNAVAILABLE_ATTRIBUTE #define CL_EXT_SUFFIX__VERSION_1_0 CL_EXTENSION_WEAK_LINK UNAVAILABLE_ATTRIBUTE #endif #ifdef AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER #define CL_API_SUFFIX__VERSION_1_1 AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER #define GCL_API_SUFFIX__VERSION_1_1 AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER #define CL_EXT_SUFFIX__VERSION_1_1 CL_EXTENSION_WEAK_LINK AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED CL_EXTENSION_WEAK_LINK AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_7 #else #define CL_API_SUFFIX__VERSION_1_1 UNAVAILABLE_ATTRIBUTE #define GCL_API_SUFFIX__VERSION_1_1 UNAVAILABLE_ATTRIBUTE #define CL_EXT_SUFFIX__VERSION_1_1 CL_EXTENSION_WEAK_LINK UNAVAILABLE_ATTRIBUTE #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATE CL_EXT_SUFFIX__VERSION_1_0 #endif #ifdef AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER #define CL_API_SUFFIX__VERSION_1_2 AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER #define GCL_API_SUFFIX__VERSION_1_2 AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER #define CL_EXT_SUFFIX__VERSION_1_2 CL_EXTENSION_WEAK_LINK AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED CL_EXTENSION_WEAK_LINK AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_8 #else #define CL_API_SUFFIX__VERSION_1_2 UNAVAILABLE_ATTRIBUTE #define GCL_API_SUFFIX__VERSION_1_2 UNAVAILABLE_ATTRIBUTE #define CL_EXT_SUFFIX__VERSION_1_2 CL_EXTENSION_WEAK_LINK UNAVAILABLE_ATTRIBUTE #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED CL_EXT_SUFFIX__VERSION_1_1 #endif #else #define CL_EXTENSION_WEAK_LINK #define CL_API_SUFFIX__VERSION_1_0 #define CL_EXT_SUFFIX__VERSION_1_0 #define CL_API_SUFFIX__VERSION_1_1 #define CL_EXT_SUFFIX__VERSION_1_1 #define CL_API_SUFFIX__VERSION_1_2 #define CL_EXT_SUFFIX__VERSION_1_2 #ifdef __GNUC__ #ifdef CL_USE_DEPRECATED_OPENCL_1_0_APIS #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED #define CL_EXT_PREFIX__VERSION_1_0_DEPRECATED #else #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED __attribute__((deprecated)) #define CL_EXT_PREFIX__VERSION_1_0_DEPRECATED #endif #ifdef CL_USE_DEPRECATED_OPENCL_1_1_APIS #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED #else #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED __attribute__((deprecated)) #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED #endif #elif _WIN32 #ifdef CL_USE_DEPRECATED_OPENCL_1_0_APIS #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED #define CL_EXT_PREFIX__VERSION_1_0_DEPRECATED #else #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED #define CL_EXT_PREFIX__VERSION_1_0_DEPRECATED __declspec(deprecated) #endif #ifdef CL_USE_DEPRECATED_OPENCL_1_1_APIS #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED #else #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED __declspec(deprecated) #endif #else #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED #define CL_EXT_PREFIX__VERSION_1_0_DEPRECATED #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED #endif #endif #if (defined (_WIN32) && defined(_MSC_VER)) /* scalar types */ typedef signed __int8 cl_char; typedef unsigned __int8 cl_uchar; typedef signed __int16 cl_short; typedef unsigned __int16 cl_ushort; typedef signed __int32 cl_int; typedef unsigned __int32 cl_uint; typedef signed __int64 cl_long; typedef unsigned __int64 cl_ulong; typedef unsigned __int16 cl_half; typedef float cl_float; typedef double cl_double; /* Macro names and corresponding values defined by OpenCL */ #define CL_CHAR_BIT 8 #define CL_SCHAR_MAX 127 #define CL_SCHAR_MIN (-127-1) #define CL_CHAR_MAX CL_SCHAR_MAX #define CL_CHAR_MIN CL_SCHAR_MIN #define CL_UCHAR_MAX 255 #define CL_SHRT_MAX 32767 #define CL_SHRT_MIN (-32767-1) #define CL_USHRT_MAX 65535 #define CL_INT_MAX 2147483647 #define CL_INT_MIN (-2147483647-1) #define CL_UINT_MAX 0xffffffffU #define CL_LONG_MAX ((cl_long) 0x7FFFFFFFFFFFFFFFLL) #define CL_LONG_MIN ((cl_long) -0x7FFFFFFFFFFFFFFFLL - 1LL) #define CL_ULONG_MAX ((cl_ulong) 0xFFFFFFFFFFFFFFFFULL) #define CL_FLT_DIG 6 #define CL_FLT_MANT_DIG 24 #define CL_FLT_MAX_10_EXP +38 #define CL_FLT_MAX_EXP +128 #define CL_FLT_MIN_10_EXP -37 #define CL_FLT_MIN_EXP -125 #define CL_FLT_RADIX 2 #define CL_FLT_MAX 340282346638528859811704183484516925440.0f #define CL_FLT_MIN 1.175494350822287507969e-38f #define CL_FLT_EPSILON 0x1.0p-23f #define CL_DBL_DIG 15 #define CL_DBL_MANT_DIG 53 #define CL_DBL_MAX_10_EXP +308 #define CL_DBL_MAX_EXP +1024 #define CL_DBL_MIN_10_EXP -307 #define CL_DBL_MIN_EXP -1021 #define CL_DBL_RADIX 2 #define CL_DBL_MAX 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0 #define CL_DBL_MIN 2.225073858507201383090e-308 #define CL_DBL_EPSILON 2.220446049250313080847e-16 #define CL_M_E 2.718281828459045090796 #define CL_M_LOG2E 1.442695040888963387005 #define CL_M_LOG10E 0.434294481903251816668 #define CL_M_LN2 0.693147180559945286227 #define CL_M_LN10 2.302585092994045901094 #define CL_M_PI 3.141592653589793115998 #define CL_M_PI_2 1.570796326794896557999 #define CL_M_PI_4 0.785398163397448278999 #define CL_M_1_PI 0.318309886183790691216 #define CL_M_2_PI 0.636619772367581382433 #define CL_M_2_SQRTPI 1.128379167095512558561 #define CL_M_SQRT2 1.414213562373095145475 #define CL_M_SQRT1_2 0.707106781186547572737 #define CL_M_E_F 2.71828174591064f #define CL_M_LOG2E_F 1.44269502162933f #define CL_M_LOG10E_F 0.43429449200630f #define CL_M_LN2_F 0.69314718246460f #define CL_M_LN10_F 2.30258512496948f #define CL_M_PI_F 3.14159274101257f #define CL_M_PI_2_F 1.57079637050629f #define CL_M_PI_4_F 0.78539818525314f #define CL_M_1_PI_F 0.31830987334251f #define CL_M_2_PI_F 0.63661974668503f #define CL_M_2_SQRTPI_F 1.12837922573090f #define CL_M_SQRT2_F 1.41421353816986f #define CL_M_SQRT1_2_F 0.70710676908493f #define CL_NAN (CL_INFINITY - CL_INFINITY) #define CL_HUGE_VALF ((cl_float) 1e50) #define CL_HUGE_VAL ((cl_double) 1e500) #define CL_MAXFLOAT CL_FLT_MAX #define CL_INFINITY CL_HUGE_VALF #else #include /* scalar types */ typedef int8_t cl_char; typedef uint8_t cl_uchar; typedef int16_t cl_short __attribute__((aligned(2))); typedef uint16_t cl_ushort __attribute__((aligned(2))); typedef int32_t cl_int __attribute__((aligned(4))); typedef uint32_t cl_uint __attribute__((aligned(4))); typedef int64_t cl_long __attribute__((aligned(8))); typedef uint64_t cl_ulong __attribute__((aligned(8))); typedef uint16_t cl_half __attribute__((aligned(2))); typedef float cl_float __attribute__((aligned(4))); typedef double cl_double __attribute__((aligned(8))); /* Macro names and corresponding values defined by OpenCL */ #define CL_CHAR_BIT 8 #define CL_SCHAR_MAX 127 #define CL_SCHAR_MIN (-127-1) #define CL_CHAR_MAX CL_SCHAR_MAX #define CL_CHAR_MIN CL_SCHAR_MIN #define CL_UCHAR_MAX 255 #define CL_SHRT_MAX 32767 #define CL_SHRT_MIN (-32767-1) #define CL_USHRT_MAX 65535 #define CL_INT_MAX 2147483647 #define CL_INT_MIN (-2147483647-1) #define CL_UINT_MAX 0xffffffffU #define CL_LONG_MAX ((cl_long) 0x7FFFFFFFFFFFFFFFLL) #define CL_LONG_MIN ((cl_long) -0x7FFFFFFFFFFFFFFFLL - 1LL) #define CL_ULONG_MAX ((cl_ulong) 0xFFFFFFFFFFFFFFFFULL) #define CL_FLT_DIG 6 #define CL_FLT_MANT_DIG 24 #define CL_FLT_MAX_10_EXP +38 #define CL_FLT_MAX_EXP +128 #define CL_FLT_MIN_10_EXP -37 #define CL_FLT_MIN_EXP -125 #define CL_FLT_RADIX 2 #define CL_FLT_MAX 0x1.fffffep127f #define CL_FLT_MIN 0x1.0p-126f #define CL_FLT_EPSILON 0x1.0p-23f #define CL_DBL_DIG 15 #define CL_DBL_MANT_DIG 53 #define CL_DBL_MAX_10_EXP +308 #define CL_DBL_MAX_EXP +1024 #define CL_DBL_MIN_10_EXP -307 #define CL_DBL_MIN_EXP -1021 #define CL_DBL_RADIX 2 #define CL_DBL_MAX 0x1.fffffffffffffp1023 #define CL_DBL_MIN 0x1.0p-1022 #define CL_DBL_EPSILON 0x1.0p-52 #define CL_M_E 2.718281828459045090796 #define CL_M_LOG2E 1.442695040888963387005 #define CL_M_LOG10E 0.434294481903251816668 #define CL_M_LN2 0.693147180559945286227 #define CL_M_LN10 2.302585092994045901094 #define CL_M_PI 3.141592653589793115998 #define CL_M_PI_2 1.570796326794896557999 #define CL_M_PI_4 0.785398163397448278999 #define CL_M_1_PI 0.318309886183790691216 #define CL_M_2_PI 0.636619772367581382433 #define CL_M_2_SQRTPI 1.128379167095512558561 #define CL_M_SQRT2 1.414213562373095145475 #define CL_M_SQRT1_2 0.707106781186547572737 #define CL_M_E_F 2.71828174591064f #define CL_M_LOG2E_F 1.44269502162933f #define CL_M_LOG10E_F 0.43429449200630f #define CL_M_LN2_F 0.69314718246460f #define CL_M_LN10_F 2.30258512496948f #define CL_M_PI_F 3.14159274101257f #define CL_M_PI_2_F 1.57079637050629f #define CL_M_PI_4_F 0.78539818525314f #define CL_M_1_PI_F 0.31830987334251f #define CL_M_2_PI_F 0.63661974668503f #define CL_M_2_SQRTPI_F 1.12837922573090f #define CL_M_SQRT2_F 1.41421353816986f #define CL_M_SQRT1_2_F 0.70710676908493f #if defined( __GNUC__ ) #define CL_HUGE_VALF __builtin_huge_valf() #define CL_HUGE_VAL __builtin_huge_val() #define CL_NAN __builtin_nanf( "" ) #else #define CL_HUGE_VALF ((cl_float) 1e50) #define CL_HUGE_VAL ((cl_double) 1e500) float nanf( const char * ); #define CL_NAN nanf( "" ) #endif #define CL_MAXFLOAT CL_FLT_MAX #define CL_INFINITY CL_HUGE_VALF #endif #include /* Mirror types to GL types. Mirror types allow us to avoid deciding which 87s to load based on whether we are using GL or GLES here. */ typedef unsigned int cl_GLuint; typedef int cl_GLint; typedef unsigned int cl_GLenum; /* * Vector types * * Note: OpenCL requires that all types be naturally aligned. * This means that vector types must be naturally aligned. * For example, a vector of four floats must be aligned to * a 16 byte boundary (calculated as 4 * the natural 4-byte * alignment of the float). The alignment qualifiers here * will only function properly if your compiler supports them * and if you don't actively work to defeat them. For example, * in order for a cl_float4 to be 16 byte aligned in a struct, * the start of the struct must itself be 16-byte aligned. * * Maintaining proper alignment is the user's responsibility. */ /* Define basic vector types */ #if defined( __VEC__ ) #include /* may be omitted depending on compiler. AltiVec spec provides no way to detect whether the header is required. */ typedef vector unsigned char __cl_uchar16; typedef vector signed char __cl_char16; typedef vector unsigned short __cl_ushort8; typedef vector signed short __cl_short8; typedef vector unsigned int __cl_uint4; typedef vector signed int __cl_int4; typedef vector float __cl_float4; #define __CL_UCHAR16__ 1 #define __CL_CHAR16__ 1 #define __CL_USHORT8__ 1 #define __CL_SHORT8__ 1 #define __CL_UINT4__ 1 #define __CL_INT4__ 1 #define __CL_FLOAT4__ 1 #endif #if defined( __SSE__ ) #if defined( __MINGW64__ ) #include #else #include #endif #if defined( __GNUC__ ) typedef float __cl_float4 __attribute__((vector_size(16))); #else typedef __m128 __cl_float4; #endif #define __CL_FLOAT4__ 1 #endif #if defined( __SSE2__ ) #if defined( __MINGW64__ ) #include #else #include #endif #if defined( __GNUC__ ) typedef cl_uchar __cl_uchar16 __attribute__((vector_size(16))); typedef cl_char __cl_char16 __attribute__((vector_size(16))); typedef cl_ushort __cl_ushort8 __attribute__((vector_size(16))); typedef cl_short __cl_short8 __attribute__((vector_size(16))); typedef cl_uint __cl_uint4 __attribute__((vector_size(16))); typedef cl_int __cl_int4 __attribute__((vector_size(16))); typedef cl_ulong __cl_ulong2 __attribute__((vector_size(16))); typedef cl_long __cl_long2 __attribute__((vector_size(16))); typedef cl_double __cl_double2 __attribute__((vector_size(16))); #else typedef __m128i __cl_uchar16; typedef __m128i __cl_char16; typedef __m128i __cl_ushort8; typedef __m128i __cl_short8; typedef __m128i __cl_uint4; typedef __m128i __cl_int4; typedef __m128i __cl_ulong2; typedef __m128i __cl_long2; typedef __m128d __cl_double2; #endif #define __CL_UCHAR16__ 1 #define __CL_CHAR16__ 1 #define __CL_USHORT8__ 1 #define __CL_SHORT8__ 1 #define __CL_INT4__ 1 #define __CL_UINT4__ 1 #define __CL_ULONG2__ 1 #define __CL_LONG2__ 1 #define __CL_DOUBLE2__ 1 #endif #if defined( __MMX__ ) #include #if defined( __GNUC__ ) typedef cl_uchar __cl_uchar8 __attribute__((vector_size(8))); typedef cl_char __cl_char8 __attribute__((vector_size(8))); typedef cl_ushort __cl_ushort4 __attribute__((vector_size(8))); typedef cl_short __cl_short4 __attribute__((vector_size(8))); typedef cl_uint __cl_uint2 __attribute__((vector_size(8))); typedef cl_int __cl_int2 __attribute__((vector_size(8))); typedef cl_ulong __cl_ulong1 __attribute__((vector_size(8))); typedef cl_long __cl_long1 __attribute__((vector_size(8))); typedef cl_float __cl_float2 __attribute__((vector_size(8))); #else typedef __m64 __cl_uchar8; typedef __m64 __cl_char8; typedef __m64 __cl_ushort4; typedef __m64 __cl_short4; typedef __m64 __cl_uint2; typedef __m64 __cl_int2; typedef __m64 __cl_ulong1; typedef __m64 __cl_long1; typedef __m64 __cl_float2; #endif #define __CL_UCHAR8__ 1 #define __CL_CHAR8__ 1 #define __CL_USHORT4__ 1 #define __CL_SHORT4__ 1 #define __CL_INT2__ 1 #define __CL_UINT2__ 1 #define __CL_ULONG1__ 1 #define __CL_LONG1__ 1 #define __CL_FLOAT2__ 1 #endif #if defined( __AVX__ ) #if defined( __MINGW64__ ) #include #else #include #endif #if defined( __GNUC__ ) typedef cl_float __cl_float8 __attribute__((vector_size(32))); typedef cl_double __cl_double4 __attribute__((vector_size(32))); #else typedef __m256 __cl_float8; typedef __m256d __cl_double4; #endif #define __CL_FLOAT8__ 1 #define __CL_DOUBLE4__ 1 #endif /* Define alignment keys */ #if defined( __GNUC__ ) #define CL_ALIGNED(_x) __attribute__ ((aligned(_x))) #elif defined( _WIN32) && (_MSC_VER) /* Alignment keys neutered on windows because MSVC can't swallow function arguments with alignment requirements */ /* http://msdn.microsoft.com/en-us/library/373ak2y1%28VS.71%29.aspx */ /* #include */ /* #define CL_ALIGNED(_x) _CRT_ALIGN(_x) */ #define CL_ALIGNED(_x) #else #warning Need to implement some method to align data here #define CL_ALIGNED(_x) #endif /* Indicate whether .xyzw, .s0123 and .hi.lo are supported */ #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) /* .xyzw and .s0123...{f|F} are supported */ #define CL_HAS_NAMED_VECTOR_FIELDS 1 /* .hi and .lo are supported */ #define CL_HAS_HI_LO_VECTOR_FIELDS 1 #endif /* Define cl_vector types */ /* ---- cl_charn ---- */ typedef union { cl_char CL_ALIGNED(2) s[2]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_char x, y; }; __extension__ struct{ cl_char s0, s1; }; __extension__ struct{ cl_char lo, hi; }; #endif #if defined( __CL_CHAR2__) __cl_char2 v2; #endif }cl_char2; typedef union { cl_char CL_ALIGNED(4) s[4]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_char x, y, z, w; }; __extension__ struct{ cl_char s0, s1, s2, s3; }; __extension__ struct{ cl_char2 lo, hi; }; #endif #if defined( __CL_CHAR2__) __cl_char2 v2[2]; #endif #if defined( __CL_CHAR4__) __cl_char4 v4; #endif }cl_char4; /* cl_char3 is identical in size, alignment and behavior to cl_char4. See section 6.1.5. */ typedef cl_char4 cl_char3; typedef union { cl_char CL_ALIGNED(8) s[8]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_char x, y, z, w; }; __extension__ struct{ cl_char s0, s1, s2, s3, s4, s5, s6, s7; }; __extension__ struct{ cl_char4 lo, hi; }; #endif #if defined( __CL_CHAR2__) __cl_char2 v2[4]; #endif #if defined( __CL_CHAR4__) __cl_char4 v4[2]; #endif #if defined( __CL_CHAR8__ ) __cl_char8 v8; #endif }cl_char8; typedef union { cl_char CL_ALIGNED(16) s[16]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_char x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __extension__ struct{ cl_char s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __extension__ struct{ cl_char8 lo, hi; }; #endif #if defined( __CL_CHAR2__) __cl_char2 v2[8]; #endif #if defined( __CL_CHAR4__) __cl_char4 v4[4]; #endif #if defined( __CL_CHAR8__ ) __cl_char8 v8[2]; #endif #if defined( __CL_CHAR16__ ) __cl_char16 v16; #endif }cl_char16; /* ---- cl_ucharn ---- */ typedef union { cl_uchar CL_ALIGNED(2) s[2]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_uchar x, y; }; __extension__ struct{ cl_uchar s0, s1; }; __extension__ struct{ cl_uchar lo, hi; }; #endif #if defined( __cl_uchar2__) __cl_uchar2 v2; #endif }cl_uchar2; typedef union { cl_uchar CL_ALIGNED(4) s[4]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_uchar x, y, z, w; }; __extension__ struct{ cl_uchar s0, s1, s2, s3; }; __extension__ struct{ cl_uchar2 lo, hi; }; #endif #if defined( __CL_UCHAR2__) __cl_uchar2 v2[2]; #endif #if defined( __CL_UCHAR4__) __cl_uchar4 v4; #endif }cl_uchar4; /* cl_uchar3 is identical in size, alignment and behavior to cl_uchar4. See section 6.1.5. */ typedef cl_uchar4 cl_uchar3; typedef union { cl_uchar CL_ALIGNED(8) s[8]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_uchar x, y, z, w; }; __extension__ struct{ cl_uchar s0, s1, s2, s3, s4, s5, s6, s7; }; __extension__ struct{ cl_uchar4 lo, hi; }; #endif #if defined( __CL_UCHAR2__) __cl_uchar2 v2[4]; #endif #if defined( __CL_UCHAR4__) __cl_uchar4 v4[2]; #endif #if defined( __CL_UCHAR8__ ) __cl_uchar8 v8; #endif }cl_uchar8; typedef union { cl_uchar CL_ALIGNED(16) s[16]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_uchar x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __extension__ struct{ cl_uchar s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __extension__ struct{ cl_uchar8 lo, hi; }; #endif #if defined( __CL_UCHAR2__) __cl_uchar2 v2[8]; #endif #if defined( __CL_UCHAR4__) __cl_uchar4 v4[4]; #endif #if defined( __CL_UCHAR8__ ) __cl_uchar8 v8[2]; #endif #if defined( __CL_UCHAR16__ ) __cl_uchar16 v16; #endif }cl_uchar16; /* ---- cl_shortn ---- */ typedef union { cl_short CL_ALIGNED(4) s[2]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_short x, y; }; __extension__ struct{ cl_short s0, s1; }; __extension__ struct{ cl_short lo, hi; }; #endif #if defined( __CL_SHORT2__) __cl_short2 v2; #endif }cl_short2; typedef union { cl_short CL_ALIGNED(8) s[4]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_short x, y, z, w; }; __extension__ struct{ cl_short s0, s1, s2, s3; }; __extension__ struct{ cl_short2 lo, hi; }; #endif #if defined( __CL_SHORT2__) __cl_short2 v2[2]; #endif #if defined( __CL_SHORT4__) __cl_short4 v4; #endif }cl_short4; /* cl_short3 is identical in size, alignment and behavior to cl_short4. See section 6.1.5. */ typedef cl_short4 cl_short3; typedef union { cl_short CL_ALIGNED(16) s[8]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_short x, y, z, w; }; __extension__ struct{ cl_short s0, s1, s2, s3, s4, s5, s6, s7; }; __extension__ struct{ cl_short4 lo, hi; }; #endif #if defined( __CL_SHORT2__) __cl_short2 v2[4]; #endif #if defined( __CL_SHORT4__) __cl_short4 v4[2]; #endif #if defined( __CL_SHORT8__ ) __cl_short8 v8; #endif }cl_short8; typedef union { cl_short CL_ALIGNED(32) s[16]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_short x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __extension__ struct{ cl_short s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __extension__ struct{ cl_short8 lo, hi; }; #endif #if defined( __CL_SHORT2__) __cl_short2 v2[8]; #endif #if defined( __CL_SHORT4__) __cl_short4 v4[4]; #endif #if defined( __CL_SHORT8__ ) __cl_short8 v8[2]; #endif #if defined( __CL_SHORT16__ ) __cl_short16 v16; #endif }cl_short16; /* ---- cl_ushortn ---- */ typedef union { cl_ushort CL_ALIGNED(4) s[2]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_ushort x, y; }; __extension__ struct{ cl_ushort s0, s1; }; __extension__ struct{ cl_ushort lo, hi; }; #endif #if defined( __CL_USHORT2__) __cl_ushort2 v2; #endif }cl_ushort2; typedef union { cl_ushort CL_ALIGNED(8) s[4]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_ushort x, y, z, w; }; __extension__ struct{ cl_ushort s0, s1, s2, s3; }; __extension__ struct{ cl_ushort2 lo, hi; }; #endif #if defined( __CL_USHORT2__) __cl_ushort2 v2[2]; #endif #if defined( __CL_USHORT4__) __cl_ushort4 v4; #endif }cl_ushort4; /* cl_ushort3 is identical in size, alignment and behavior to cl_ushort4. See section 6.1.5. */ typedef cl_ushort4 cl_ushort3; typedef union { cl_ushort CL_ALIGNED(16) s[8]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_ushort x, y, z, w; }; __extension__ struct{ cl_ushort s0, s1, s2, s3, s4, s5, s6, s7; }; __extension__ struct{ cl_ushort4 lo, hi; }; #endif #if defined( __CL_USHORT2__) __cl_ushort2 v2[4]; #endif #if defined( __CL_USHORT4__) __cl_ushort4 v4[2]; #endif #if defined( __CL_USHORT8__ ) __cl_ushort8 v8; #endif }cl_ushort8; typedef union { cl_ushort CL_ALIGNED(32) s[16]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_ushort x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __extension__ struct{ cl_ushort s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __extension__ struct{ cl_ushort8 lo, hi; }; #endif #if defined( __CL_USHORT2__) __cl_ushort2 v2[8]; #endif #if defined( __CL_USHORT4__) __cl_ushort4 v4[4]; #endif #if defined( __CL_USHORT8__ ) __cl_ushort8 v8[2]; #endif #if defined( __CL_USHORT16__ ) __cl_ushort16 v16; #endif }cl_ushort16; /* ---- cl_intn ---- */ typedef union { cl_int CL_ALIGNED(8) s[2]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_int x, y; }; __extension__ struct{ cl_int s0, s1; }; __extension__ struct{ cl_int lo, hi; }; #endif #if defined( __CL_INT2__) __cl_int2 v2; #endif }cl_int2; typedef union { cl_int CL_ALIGNED(16) s[4]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_int x, y, z, w; }; __extension__ struct{ cl_int s0, s1, s2, s3; }; __extension__ struct{ cl_int2 lo, hi; }; #endif #if defined( __CL_INT2__) __cl_int2 v2[2]; #endif #if defined( __CL_INT4__) __cl_int4 v4; #endif }cl_int4; /* cl_int3 is identical in size, alignment and behavior to cl_int4. See section 6.1.5. */ typedef cl_int4 cl_int3; typedef union { cl_int CL_ALIGNED(32) s[8]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_int x, y, z, w; }; __extension__ struct{ cl_int s0, s1, s2, s3, s4, s5, s6, s7; }; __extension__ struct{ cl_int4 lo, hi; }; #endif #if defined( __CL_INT2__) __cl_int2 v2[4]; #endif #if defined( __CL_INT4__) __cl_int4 v4[2]; #endif #if defined( __CL_INT8__ ) __cl_int8 v8; #endif }cl_int8; typedef union { cl_int CL_ALIGNED(64) s[16]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_int x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __extension__ struct{ cl_int s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __extension__ struct{ cl_int8 lo, hi; }; #endif #if defined( __CL_INT2__) __cl_int2 v2[8]; #endif #if defined( __CL_INT4__) __cl_int4 v4[4]; #endif #if defined( __CL_INT8__ ) __cl_int8 v8[2]; #endif #if defined( __CL_INT16__ ) __cl_int16 v16; #endif }cl_int16; /* ---- cl_uintn ---- */ typedef union { cl_uint CL_ALIGNED(8) s[2]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_uint x, y; }; __extension__ struct{ cl_uint s0, s1; }; __extension__ struct{ cl_uint lo, hi; }; #endif #if defined( __CL_UINT2__) __cl_uint2 v2; #endif }cl_uint2; typedef union { cl_uint CL_ALIGNED(16) s[4]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_uint x, y, z, w; }; __extension__ struct{ cl_uint s0, s1, s2, s3; }; __extension__ struct{ cl_uint2 lo, hi; }; #endif #if defined( __CL_UINT2__) __cl_uint2 v2[2]; #endif #if defined( __CL_UINT4__) __cl_uint4 v4; #endif }cl_uint4; /* cl_uint3 is identical in size, alignment and behavior to cl_uint4. See section 6.1.5. */ typedef cl_uint4 cl_uint3; typedef union { cl_uint CL_ALIGNED(32) s[8]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_uint x, y, z, w; }; __extension__ struct{ cl_uint s0, s1, s2, s3, s4, s5, s6, s7; }; __extension__ struct{ cl_uint4 lo, hi; }; #endif #if defined( __CL_UINT2__) __cl_uint2 v2[4]; #endif #if defined( __CL_UINT4__) __cl_uint4 v4[2]; #endif #if defined( __CL_UINT8__ ) __cl_uint8 v8; #endif }cl_uint8; typedef union { cl_uint CL_ALIGNED(64) s[16]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_uint x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __extension__ struct{ cl_uint s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __extension__ struct{ cl_uint8 lo, hi; }; #endif #if defined( __CL_UINT2__) __cl_uint2 v2[8]; #endif #if defined( __CL_UINT4__) __cl_uint4 v4[4]; #endif #if defined( __CL_UINT8__ ) __cl_uint8 v8[2]; #endif #if defined( __CL_UINT16__ ) __cl_uint16 v16; #endif }cl_uint16; /* ---- cl_longn ---- */ typedef union { cl_long CL_ALIGNED(16) s[2]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_long x, y; }; __extension__ struct{ cl_long s0, s1; }; __extension__ struct{ cl_long lo, hi; }; #endif #if defined( __CL_LONG2__) __cl_long2 v2; #endif }cl_long2; typedef union { cl_long CL_ALIGNED(32) s[4]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_long x, y, z, w; }; __extension__ struct{ cl_long s0, s1, s2, s3; }; __extension__ struct{ cl_long2 lo, hi; }; #endif #if defined( __CL_LONG2__) __cl_long2 v2[2]; #endif #if defined( __CL_LONG4__) __cl_long4 v4; #endif }cl_long4; /* cl_long3 is identical in size, alignment and behavior to cl_long4. See section 6.1.5. */ typedef cl_long4 cl_long3; typedef union { cl_long CL_ALIGNED(64) s[8]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_long x, y, z, w; }; __extension__ struct{ cl_long s0, s1, s2, s3, s4, s5, s6, s7; }; __extension__ struct{ cl_long4 lo, hi; }; #endif #if defined( __CL_LONG2__) __cl_long2 v2[4]; #endif #if defined( __CL_LONG4__) __cl_long4 v4[2]; #endif #if defined( __CL_LONG8__ ) __cl_long8 v8; #endif }cl_long8; typedef union { cl_long CL_ALIGNED(128) s[16]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_long x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __extension__ struct{ cl_long s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __extension__ struct{ cl_long8 lo, hi; }; #endif #if defined( __CL_LONG2__) __cl_long2 v2[8]; #endif #if defined( __CL_LONG4__) __cl_long4 v4[4]; #endif #if defined( __CL_LONG8__ ) __cl_long8 v8[2]; #endif #if defined( __CL_LONG16__ ) __cl_long16 v16; #endif }cl_long16; /* ---- cl_ulongn ---- */ typedef union { cl_ulong CL_ALIGNED(16) s[2]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_ulong x, y; }; __extension__ struct{ cl_ulong s0, s1; }; __extension__ struct{ cl_ulong lo, hi; }; #endif #if defined( __CL_ULONG2__) __cl_ulong2 v2; #endif }cl_ulong2; typedef union { cl_ulong CL_ALIGNED(32) s[4]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_ulong x, y, z, w; }; __extension__ struct{ cl_ulong s0, s1, s2, s3; }; __extension__ struct{ cl_ulong2 lo, hi; }; #endif #if defined( __CL_ULONG2__) __cl_ulong2 v2[2]; #endif #if defined( __CL_ULONG4__) __cl_ulong4 v4; #endif }cl_ulong4; /* cl_ulong3 is identical in size, alignment and behavior to cl_ulong4. See section 6.1.5. */ typedef cl_ulong4 cl_ulong3; typedef union { cl_ulong CL_ALIGNED(64) s[8]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_ulong x, y, z, w; }; __extension__ struct{ cl_ulong s0, s1, s2, s3, s4, s5, s6, s7; }; __extension__ struct{ cl_ulong4 lo, hi; }; #endif #if defined( __CL_ULONG2__) __cl_ulong2 v2[4]; #endif #if defined( __CL_ULONG4__) __cl_ulong4 v4[2]; #endif #if defined( __CL_ULONG8__ ) __cl_ulong8 v8; #endif }cl_ulong8; typedef union { cl_ulong CL_ALIGNED(128) s[16]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_ulong x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __extension__ struct{ cl_ulong s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __extension__ struct{ cl_ulong8 lo, hi; }; #endif #if defined( __CL_ULONG2__) __cl_ulong2 v2[8]; #endif #if defined( __CL_ULONG4__) __cl_ulong4 v4[4]; #endif #if defined( __CL_ULONG8__ ) __cl_ulong8 v8[2]; #endif #if defined( __CL_ULONG16__ ) __cl_ulong16 v16; #endif }cl_ulong16; /* --- cl_floatn ---- */ typedef union { cl_float CL_ALIGNED(8) s[2]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_float x, y; }; __extension__ struct{ cl_float s0, s1; }; __extension__ struct{ cl_float lo, hi; }; #endif #if defined( __CL_FLOAT2__) __cl_float2 v2; #endif }cl_float2; typedef union { cl_float CL_ALIGNED(16) s[4]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_float x, y, z, w; }; __extension__ struct{ cl_float s0, s1, s2, s3; }; __extension__ struct{ cl_float2 lo, hi; }; #endif #if defined( __CL_FLOAT2__) __cl_float2 v2[2]; #endif #if defined( __CL_FLOAT4__) __cl_float4 v4; #endif }cl_float4; /* cl_float3 is identical in size, alignment and behavior to cl_float4. See section 6.1.5. */ typedef cl_float4 cl_float3; typedef union { cl_float CL_ALIGNED(32) s[8]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_float x, y, z, w; }; __extension__ struct{ cl_float s0, s1, s2, s3, s4, s5, s6, s7; }; __extension__ struct{ cl_float4 lo, hi; }; #endif #if defined( __CL_FLOAT2__) __cl_float2 v2[4]; #endif #if defined( __CL_FLOAT4__) __cl_float4 v4[2]; #endif #if defined( __CL_FLOAT8__ ) __cl_float8 v8; #endif }cl_float8; typedef union { cl_float CL_ALIGNED(64) s[16]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_float x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __extension__ struct{ cl_float s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __extension__ struct{ cl_float8 lo, hi; }; #endif #if defined( __CL_FLOAT2__) __cl_float2 v2[8]; #endif #if defined( __CL_FLOAT4__) __cl_float4 v4[4]; #endif #if defined( __CL_FLOAT8__ ) __cl_float8 v8[2]; #endif #if defined( __CL_FLOAT16__ ) __cl_float16 v16; #endif }cl_float16; /* --- cl_doublen ---- */ typedef union { cl_double CL_ALIGNED(16) s[2]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_double x, y; }; __extension__ struct{ cl_double s0, s1; }; __extension__ struct{ cl_double lo, hi; }; #endif #if defined( __CL_DOUBLE2__) __cl_double2 v2; #endif }cl_double2; typedef union { cl_double CL_ALIGNED(32) s[4]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_double x, y, z, w; }; __extension__ struct{ cl_double s0, s1, s2, s3; }; __extension__ struct{ cl_double2 lo, hi; }; #endif #if defined( __CL_DOUBLE2__) __cl_double2 v2[2]; #endif #if defined( __CL_DOUBLE4__) __cl_double4 v4; #endif }cl_double4; /* cl_double3 is identical in size, alignment and behavior to cl_double4. See section 6.1.5. */ typedef cl_double4 cl_double3; typedef union { cl_double CL_ALIGNED(64) s[8]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_double x, y, z, w; }; __extension__ struct{ cl_double s0, s1, s2, s3, s4, s5, s6, s7; }; __extension__ struct{ cl_double4 lo, hi; }; #endif #if defined( __CL_DOUBLE2__) __cl_double2 v2[4]; #endif #if defined( __CL_DOUBLE4__) __cl_double4 v4[2]; #endif #if defined( __CL_DOUBLE8__ ) __cl_double8 v8; #endif }cl_double8; typedef union { cl_double CL_ALIGNED(128) s[16]; #if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) __extension__ struct{ cl_double x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __extension__ struct{ cl_double s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __extension__ struct{ cl_double8 lo, hi; }; #endif #if defined( __CL_DOUBLE2__) __cl_double2 v2[8]; #endif #if defined( __CL_DOUBLE4__) __cl_double4 v4[4]; #endif #if defined( __CL_DOUBLE8__ ) __cl_double8 v8[2]; #endif #if defined( __CL_DOUBLE16__ ) __cl_double16 v16; #endif }cl_double16; /* Macro to facilitate debugging * Usage: * Place CL_PROGRAM_STRING_DEBUG_INFO on the line before the first line of your source. * The first line ends with: CL_PROGRAM_STRING_DEBUG_INFO \" * Each line thereafter of OpenCL C source must end with: \n\ * The last line ends in "; * * Example: * * const char *my_program = CL_PROGRAM_STRING_DEBUG_INFO "\ * kernel void foo( int a, float * b ) \n\ * { \n\ * // my comment \n\ * *b[ get_global_id(0)] = a; \n\ * } \n\ * "; * * This should correctly set up the line, (column) and file information for your source * string so you can do source level debugging. */ #define __CL_STRINGIFY( _x ) # _x #define _CL_STRINGIFY( _x ) __CL_STRINGIFY( _x ) #define CL_PROGRAM_STRING_DEBUG_INFO "#line " _CL_STRINGIFY(__LINE__) " \"" __FILE__ "\" \n\n" #ifdef __cplusplus } #endif #endif /* __CL_PLATFORM_H */ HandBrake-0.10.2/libhb/hb_dict.h0000664000175200017520000000326012463330511016663 0ustar handbrakehandbrake/* hb_dict.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #if !defined(HB_DICT_H) #define HB_DICT_H typedef struct hb_dict_entry_s hb_dict_entry_t; typedef struct hb_dict_s hb_dict_t; /* Basic dictionary implementation. * * an hb_dict_t must be initialized with hb_dict_init() before use. * * "key" must be a string with non-zero length (NULL and "" are invalid keys). * "value" can be NULL (the zero-length string "" is mapped to NULL). * * hb_dict_next( dict, NULL ) returns the first key in the dictionary. * hb_dict_next( dict, previous ) returns key directly following previous, or * NULL if the end of the dictionary was reached. * * hb_encopts_to_dict() converts an op1=val1:opt2=val2:opt3=val3 type string to * an hb_dict_t dictionary. */ hb_dict_t * hb_dict_init( int alloc ); void hb_dict_free( hb_dict_t ** dict_ptr ); void hb_dict_set( hb_dict_t ** dict_ptr, const char * key, const char * value ); void hb_dict_unset( hb_dict_t ** dict_ptr, const char * key ); hb_dict_entry_t * hb_dict_get( hb_dict_t * dict, const char * key ); hb_dict_entry_t * hb_dict_next( hb_dict_t * dict, hb_dict_entry_t * previous ); hb_dict_t * hb_encopts_to_dict( const char * encopts, int encoder ); char * hb_dict_to_encopts( hb_dict_t * dict ); struct hb_dict_entry_s { char * key; char * value; }; struct hb_dict_s { int alloc; int count; hb_dict_entry_t * objects; }; #endif // !defined(HB_DICT_H) HandBrake-0.10.2/libhb/module.defs0000664000175200017520000001134312417602031017245 0ustar handbrakehandbrake__deps__ := A52DEC BZIP2 LIBVPX FFMPEG FONTCONFIG FREETYPE LAME LIBASS LIBDCA \ LIBDVDREAD LIBDVDNAV LIBICONV LIBOGG LIBSAMPLERATE LIBTHEORA LIBVORBIS LIBXML2 \ PTHREADW32 X264 X265 ZLIB LIBBLURAY FDKAAC LIBMFX LIBGNURX $(eval $(call import.MODULE.defs,LIBHB,libhb,$(__deps__))) $(eval $(call import.GCC,LIBHB)) ############################################################################### LIBHB.src/ = $(SRC/)libhb/ LIBHB.build/ = $(BUILD/)libhb/ LIBHB.m4.in = $(wildcard $(LIBHB.src/)*.m4) LIBHB.m4.out = $(patsubst $(LIBHB.src/)%.m4,$(LIBHB.build/)%,$(LIBHB.m4.in)) LIBHB.c = $(wildcard $(LIBHB.src/)*.c) LIBHB.c.o = $(patsubst $(SRC/)%.c,$(BUILD/)%.o,$(LIBHB.c)) LIBHB.d = $(LIBHB.m4.out) $(LIBHB.h.out) \ $(foreach n,$(LIBHB.prerequisites),$($n.INSTALL.target) ) LIBHB.h.in = $(wildcard $(LIBHB.src/)*.h) LIBHB.h.in += $(wildcard $(LIBHB.src/)extras/*.h) LIBHB.h.out = $(patsubst $(SRC/)%,$(BUILD/)%,$(LIBHB.h.in)) ############################################################################### LIBHB.a = $(LIBHB.build/)$(call TARGET.archive,handbrake) ############################################################################### LIBHB.out += $(LIBHB.m4.out) LIBHB.out += $(LIBHB.c.o) LIBHB.out += $(LIBHB.h.out) LIBHB.out += $(LIBHB.a) ############################################################################### ifeq (1,$(FEATURE.hwd)) LIBHB.GCC.D += USE_HWD endif ifeq (1,$(FEATURE.libav_aac)) LIBHB.GCC.D += USE_LIBAV_AAC endif LIBHB.GCC.D += __LIBHB__ USE_PTHREAD LIBHB.GCC.I += $(LIBHB.build/) $(CONTRIB.build/)include ifeq ($(BUILD.system),cygwin) LIBHB.GCC.D += SYS_CYGWIN else ifeq ($(BUILD.system),darwin) LIBHB.GCC.D += SYS_DARWIN LIBHB.c += $(wildcard $(LIBHB.src/)platform/macosx/*.c) else ifeq ($(BUILD.system),linux) LIBHB.GCC.D += SYS_LINUX _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64 else ifeq ($(BUILD.system),mingw) LIBHB.GCC.D += SYS_MINGW PTW32_STATIC_LIB LIBHB.GCC.args.extra.dylib++ += -Wl,--enable-auto-import -static else ifeq ($(BUILD.system),solaris) LIBHB.GCC.D += SYS_SunOS _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64 _POSIX_C_SOURCE=200112L __EXTENSIONS__ else LIBHB.platform.D = SYS_UNKNOWN endif ifeq (1,$(FEATURE.qsv)) LIBHB.GCC.D += USE_QSV HAVE_THREADS=1 endif ifeq (1,$(FEATURE.x265)) LIBHB.GCC.D += USE_X265 endif ## required for ifneq (,$(filter $(BUILD.arch),ppc ppc64)) LIBHB.GCC.D += WORDS_BIGENDIAN endif ############################################################################### ## when defined this gives us the subdir name, or flavor of asm implementation ifneq (disabled,$(FEATURE.asm)) LIBHB.yasm.src/ = $(LIBHB.src/)$(FEATURE.asm)/ LIBHB.yasm.build/ = $(LIBHB.build/)$(FEATURE.asm)/ LIBHB.yasm.asm = $(LIBHB.yasm.src/)deinterlace-a.asm LIBHB.yasm.o = $(LIBHB.yasm.asm:$(LIBHB.yasm.src/)%.asm=$(LIBHB.yasm.build/)%.o) LIBHB.yasm.d = $(wildcard $(LIBHB.yasmsrc/)*.h) LIBHB.YASM.I = $(LIBHB.yasm.src/) LIBHB.YASM.ASM_O = $(strip $(YASM.exe) \ -f $(LIBHB.YASM.f) \ -m $(LIBHB.YASM.m) \ $(LIBHB.YASM.D:%=-D%) \ $(LIBHB.YASM.I:%=-I%) \ -o $(1) $(2)) LIBHB.out += $(LIBHB.yasm.o) endif ############################################################################### ifeq (1-mingw,$(BUILD.cross)-$(BUILD.system)) LIBHB.dll = $(LIBHB.build/)hb.dll LIBHB.lib = $(LIBHB.build/)hb.lib LIBHB.dll.libs = $(foreach n, \ ass avcodec avformat avutil avresample dvdnav dvdread \ fontconfig freetype mp3lame \ ogg samplerate swscale vpx theora vorbis vorbisenc x264 xml2 bluray, \ $(CONTRIB.build/)lib/lib$(n).a ) ifeq (1,$(FEATURE.fdk_aac)) LIBHB.dll.libs += $(CONTRIB.build/)lib/libfdk-aac.a endif ifeq (1,$(FEATURE.qsv)) LIBHB.dll.libs += $(CONTRIB.build/)lib/libmfx.a endif ifeq (1,$(FEATURE.x265)) LIBHB.dll.libs += $(CONTRIB.build/)lib/libx265.a endif ifneq ($(HAS.iconv),1) LIBHB.dll.libs += $(CONTRIB.build/)lib/libiconv.a else LIBHB.GCC.l += iconv endif ifneq ($(HAS.regex),1) LIBHB.dll.libs += $(CONTRIB.build/)lib/libregex.a else LIBHB.GCC.l += regex endif ifeq (1,$(FRIBIDI.enabled)) LIBHB.dll.libs += $(CONTRIB.build/)lib/libfribidi.a else LIBHB.GCC.l += fribidi endif ifneq ($(HAS.pthread),1) LIBHB.dll.libs += $(CONTRIB.build/)lib/libpthreadGC2.a else LIBHB.GCC.l += pthreadGC2 endif ifneq ($(HAS.bz2),1) LIBHB.dll.libs += $(CONTRIB.build/)lib/libbz2.a else LIBHB.GCC.l += bz2 endif ifneq ($(HAS.libz),1) LIBHB.dll.libs += $(CONTRIB.build/)lib/libz.a else LIBHB.GCC.l += z endif LIBHB.GCC.args.extra.dylib++ += -Wl,--out-implib,$(LIBHB.lib) LIBHB.GCC.l += ws2_32 ifeq ($(HAS.dlfcn),1) LIBHB.GCC.l += dl endif LIBHB.out += $(LIBHB.dll) $(LIBHB.lib) endif ############################################################################### BUILD.out += $(LIBHB.out) HandBrake-0.10.2/libhb/h264_common.h0000664000175200017520000000321212463330511017317 0ustar handbrakehandbrake/* h264_common.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef HB_H264_COMMON_H #define HB_H264_COMMON_H static const char * const hb_h264_profile_names[] = { "auto", "high", "main", "baseline", NULL, }; static const char * const hb_h264_level_names[] = { "auto", "1.0", "1b", "1.1", "1.2", "1.3", "2.0", "2.1", "2.2", "3.0", "3.1", "3.2", "4.0", "4.1", "4.2", "5.0", "5.1", "5.2", NULL, }; static const int const hb_h264_level_values[] = { -1, 10, 9, 11, 12, 13, 20, 21, 22, 30, 31, 32, 40, 41, 42, 50, 51, 52, 0, }; // stolen from libx264's x264.h static const char * const hb_h264_fullrange_names[] = { "off", "on", NULL, }; static const char * const hb_h264_vidformat_names[] = { "component", "pal", "ntsc", "secam", "mac", "undef", NULL, }; static const char * const hb_h264_colorprim_names[] = { "", "bt709", "undef", "", "bt470m", "bt470bg", "smpte170m", "smpte240m", "film", "bt2020", NULL, }; static const char * const hb_h264_transfer_names[] = { "", "bt709", "undef", "", "bt470m", "bt470bg", "smpte170m", "smpte240m", "linear", "log100", "log316", "iec61966-2-4", "bt1361e", "iec61966-2-1", "bt2020-10", "bt2020-12", NULL, }; static const char * const hb_h264_colmatrix_names[] = { "GBR", "bt709", "undef", "", "fcc", "bt470bg", "smpte170m", "smpte240m", "YCgCo", "bt2020nc", "bt2020c", NULL, }; #endif //HB_H264_COMMON_H HandBrake-0.10.2/libhb/common.c0000664000175200017520000040404312470166275016572 0ustar handbrakehandbrake/* common.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include #include #include #include #include "hb.h" #include "x264.h" #include "lang.h" #include "common.h" #include "h264_common.h" #include "h265_common.h" #ifdef USE_QSV #include "qsv_common.h" #endif #ifdef USE_X265 #include "x265.h" #endif #ifdef SYS_MINGW #include #endif /********************************************************************** * Global variables *********************************************************************/ static hb_error_handler_t *error_handler = NULL; /* Generic IDs for encoders, containers, etc. */ enum { HB_GID_NONE = -1, // encoders must NEVER use it HB_GID_VCODEC_H264, HB_GID_VCODEC_H265, HB_GID_VCODEC_MPEG2, HB_GID_VCODEC_MPEG4, HB_GID_VCODEC_THEORA, HB_GID_VCODEC_VP8, HB_GID_ACODEC_AAC, HB_GID_ACODEC_AAC_HE, HB_GID_ACODEC_AAC_PASS, HB_GID_ACODEC_AC3, HB_GID_ACODEC_AC3_PASS, HB_GID_ACODEC_AUTO_PASS, HB_GID_ACODEC_DTS_PASS, HB_GID_ACODEC_DTSHD_PASS, HB_GID_ACODEC_FLAC, HB_GID_ACODEC_MP3, HB_GID_ACODEC_MP3_PASS, HB_GID_ACODEC_VORBIS, HB_GID_MUX_MKV, HB_GID_MUX_MP4, }; typedef struct { hb_rate_t item; hb_rate_t *next; int enabled; } hb_rate_internal_t; hb_rate_t *hb_video_rates_first_item = NULL; hb_rate_t *hb_video_rates_last_item = NULL; hb_rate_internal_t hb_video_rates[] = { // legacy framerates (disabled) { { "23.976 (NTSC Film)", 1126125, }, NULL, 0, }, { { "25 (PAL Film/Video)", 1080000, }, NULL, 0, }, { { "29.97 (NTSC Video)", 900900, }, NULL, 0, }, // actual framerates { { "5", 5400000, }, NULL, 1, }, { { "10", 2700000, }, NULL, 1, }, { { "12", 2250000, }, NULL, 1, }, { { "15", 1800000, }, NULL, 1, }, { { "23.976", 1126125, }, NULL, 1, }, { { "24", 1125000, }, NULL, 1, }, { { "25", 1080000, }, NULL, 1, }, { { "29.97", 900900, }, NULL, 1, }, { { "30", 900000, }, NULL, 1, }, { { "50", 540000, }, NULL, 1, }, { { "59.94", 450450, }, NULL, 1, }, { { "60", 450000, }, NULL, 1, }, }; int hb_video_rates_count = sizeof(hb_video_rates) / sizeof(hb_video_rates[0]); hb_rate_t *hb_audio_rates_first_item = NULL; hb_rate_t *hb_audio_rates_last_item = NULL; hb_rate_internal_t hb_audio_rates[] = { { { "8", 8000, }, NULL, 1, }, { { "11.025", 11025, }, NULL, 1, }, { { "12", 12000, }, NULL, 1, }, { { "16", 16000, }, NULL, 1, }, { { "22.05", 22050, }, NULL, 1, }, { { "24", 24000, }, NULL, 1, }, { { "32", 32000, }, NULL, 1, }, { { "44.1", 44100, }, NULL, 1, }, { { "48", 48000, }, NULL, 1, }, }; int hb_audio_rates_count = sizeof(hb_audio_rates) / sizeof(hb_audio_rates[0]); hb_rate_t *hb_audio_bitrates_first_item = NULL; hb_rate_t *hb_audio_bitrates_last_item = NULL; hb_rate_internal_t hb_audio_bitrates[] = { // AC3-compatible bitrates { { "32", 32, }, NULL, 1, }, { { "40", 40, }, NULL, 1, }, { { "48", 48, }, NULL, 1, }, { { "56", 56, }, NULL, 1, }, { { "64", 64, }, NULL, 1, }, { { "80", 80, }, NULL, 1, }, { { "96", 96, }, NULL, 1, }, { { "112", 112, }, NULL, 1, }, { { "128", 128, }, NULL, 1, }, { { "160", 160, }, NULL, 1, }, { { "192", 192, }, NULL, 1, }, { { "224", 224, }, NULL, 1, }, { { "256", 256, }, NULL, 1, }, { { "320", 320, }, NULL, 1, }, { { "384", 384, }, NULL, 1, }, { { "448", 448, }, NULL, 1, }, { { "512", 512, }, NULL, 1, }, { { "576", 576, }, NULL, 1, }, { { "640", 640, }, NULL, 1, }, // additional bitrates { { "768", 768, }, NULL, 1, }, { { "960", 960, }, NULL, 1, }, { { "1152", 1152, }, NULL, 1, }, { { "1344", 1344, }, NULL, 1, }, { { "1536", 1536, }, NULL, 1, }, }; int hb_audio_bitrates_count = sizeof(hb_audio_bitrates) / sizeof(hb_audio_bitrates[0]); typedef struct { hb_dither_t item; hb_dither_t *next; int enabled; } hb_dither_internal_t; hb_dither_t *hb_audio_dithers_first_item = NULL; hb_dither_t *hb_audio_dithers_last_item = NULL; hb_dither_internal_t hb_audio_dithers[] = { { { "default", "auto", AV_RESAMPLE_DITHER_NONE - 1, }, NULL, 1, }, { { "none", "none", AV_RESAMPLE_DITHER_NONE, }, NULL, 1, }, { { "rectangular", "rectangular", AV_RESAMPLE_DITHER_RECTANGULAR, }, NULL, 1, }, { { "triangular", "triangular", AV_RESAMPLE_DITHER_TRIANGULAR, }, NULL, 1, }, { { "triangular with high pass", "triangular_hp", AV_RESAMPLE_DITHER_TRIANGULAR_HP, }, NULL, 1, }, { { "triangular with noise shaping", "triangular_ns", AV_RESAMPLE_DITHER_TRIANGULAR_NS, }, NULL, 1, }, }; int hb_audio_dithers_count = sizeof(hb_audio_dithers) / sizeof(hb_audio_dithers[0]); typedef struct { hb_mixdown_t item; hb_mixdown_t *next; int enabled; } hb_mixdown_internal_t; hb_mixdown_t *hb_audio_mixdowns_first_item = NULL; hb_mixdown_t *hb_audio_mixdowns_last_item = NULL; hb_mixdown_internal_t hb_audio_mixdowns[] = { // legacy mixdowns, back to HB 0.9.4 whenever possible (disabled) { { "AC3 Passthru", "", HB_AMIXDOWN_NONE, }, NULL, 0, }, { { "DTS Passthru", "", HB_AMIXDOWN_NONE, }, NULL, 0, }, { { "DTS-HD Passthru", "", HB_AMIXDOWN_NONE, }, NULL, 0, }, { { "6-channel discrete", "6ch", HB_AMIXDOWN_5POINT1, }, NULL, 0, }, // actual mixdowns { { "None", "none", HB_AMIXDOWN_NONE, }, NULL, 1, }, { { "Mono", "mono", HB_AMIXDOWN_MONO, }, NULL, 1, }, { { "Mono (Left Only)", "left_only", HB_AMIXDOWN_LEFT, }, NULL, 1, }, { { "Mono (Right Only)", "right_only", HB_AMIXDOWN_RIGHT, }, NULL, 1, }, { { "Stereo", "stereo", HB_AMIXDOWN_STEREO, }, NULL, 1, }, { { "Dolby Surround", "dpl1", HB_AMIXDOWN_DOLBY, }, NULL, 1, }, { { "Dolby Pro Logic II", "dpl2", HB_AMIXDOWN_DOLBYPLII, }, NULL, 1, }, { { "5.1 Channels", "5point1", HB_AMIXDOWN_5POINT1, }, NULL, 1, }, { { "6.1 Channels", "6point1", HB_AMIXDOWN_6POINT1, }, NULL, 1, }, { { "7.1 Channels", "7point1", HB_AMIXDOWN_7POINT1, }, NULL, 1, }, { { "7.1 (5F/2R/LFE)", "5_2_lfe", HB_AMIXDOWN_5_2_LFE, }, NULL, 1, }, }; int hb_audio_mixdowns_count = sizeof(hb_audio_mixdowns) / sizeof(hb_audio_mixdowns[0]); typedef struct { hb_encoder_t item; hb_encoder_t *next; int enabled; int gid; } hb_encoder_internal_t; hb_encoder_t *hb_video_encoders_first_item = NULL; hb_encoder_t *hb_video_encoders_last_item = NULL; hb_encoder_internal_t hb_video_encoders[] = { // legacy encoders, back to HB 0.9.4 whenever possible (disabled) { { "FFmpeg", "ffmpeg", NULL, HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_VCODEC_MPEG4, }, { { "MPEG-4 (FFmpeg)", "ffmpeg4", NULL, HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_VCODEC_MPEG4, }, { { "MPEG-2 (FFmpeg)", "ffmpeg2", NULL, HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_VCODEC_MPEG2, }, { { "VP3 (Theora)", "libtheora", NULL, HB_VCODEC_THEORA, HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_VCODEC_THEORA, }, // actual encoders { { "H.264 (x264)", "x264", "H.264 (libx264)", HB_VCODEC_X264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264, }, { { "H.264 (Intel QSV)", "qsv_h264", "H.264 (Intel Media SDK)", HB_VCODEC_QSV_H264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264, }, { { "H.265 (x265)", "x265", "H.265 (libx265)", HB_VCODEC_X265, HB_MUX_AV_MP4|HB_MUX_AV_MKV, }, NULL, 1, HB_GID_VCODEC_H265, }, { { "MPEG-4", "mpeg4", "MPEG-4 (libavcodec)", HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG4, }, { { "MPEG-2", "mpeg2", "MPEG-2 (libavcodec)", HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG2, }, { { "VP8", "VP8", "VP8 (libvpx)", HB_VCODEC_FFMPEG_VP8, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_VP8, }, { { "Theora", "theora", "Theora (libtheora)", HB_VCODEC_THEORA, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_THEORA, }, }; int hb_video_encoders_count = sizeof(hb_video_encoders) / sizeof(hb_video_encoders[0]); static int hb_video_encoder_is_enabled(int encoder) { #ifdef USE_QSV if (encoder & HB_VCODEC_QSV_MASK) { return hb_qsv_video_encoder_is_enabled(encoder); } #endif switch (encoder) { // the following encoders are always enabled case HB_VCODEC_X264: case HB_VCODEC_THEORA: case HB_VCODEC_FFMPEG_MPEG4: case HB_VCODEC_FFMPEG_MPEG2: case HB_VCODEC_FFMPEG_VP8: #ifdef USE_X265 case HB_VCODEC_X265: #endif return 1; default: return 0; } } hb_encoder_t *hb_audio_encoders_first_item = NULL; hb_encoder_t *hb_audio_encoders_last_item = NULL; hb_encoder_internal_t hb_audio_encoders[] = { // legacy encoders, back to HB 0.9.4 whenever possible (disabled) { { "", "dts", NULL, HB_ACODEC_DCA_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_DTS_PASS, }, { { "AAC (faac)", "faac", NULL, 0, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_AAC, }, { { "AAC (ffmpeg)", "ffaac", NULL, HB_ACODEC_FFAAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_AAC, }, { { "AC3 (ffmpeg)", "ffac3", NULL, HB_ACODEC_AC3, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_AC3, }, { { "MP3 (lame)", "lame", NULL, HB_ACODEC_LAME, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_MP3, }, { { "Vorbis (vorbis)", "libvorbis", NULL, HB_ACODEC_VORBIS, HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_VORBIS, }, { { "FLAC (ffmpeg)", "ffflac", NULL, HB_ACODEC_FFFLAC, HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_FLAC, }, { { "FLAC (24-bit)", "ffflac24", NULL, HB_ACODEC_FFFLAC24, HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_ACODEC_FLAC, }, // actual encoders { { "AAC (CoreAudio)", "ca_aac", "AAC (Apple AudioToolbox)", HB_ACODEC_CA_AAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_AAC, }, { { "HE-AAC (CoreAudio)", "ca_haac", "HE-AAC (Apple AudioToolbox)", HB_ACODEC_CA_HAAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_AAC_HE, }, { { "AAC (avcodec)", "av_aac", "AAC (libavcodec)", HB_ACODEC_FFAAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_AAC, }, { { "AAC (FDK)", "fdk_aac", "AAC (libfdk_aac)", HB_ACODEC_FDK_AAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_AAC, }, { { "HE-AAC (FDK)", "fdk_haac", "HE-AAC (libfdk_aac)", HB_ACODEC_FDK_HAAC, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_AAC_HE, }, { { "AAC Passthru", "copy:aac", "AAC Passthru", HB_ACODEC_AAC_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_AAC_PASS, }, { { "AC3", "ac3", "AC3 (libavcodec)", HB_ACODEC_AC3, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_AC3, }, { { "AC3 Passthru", "copy:ac3", "AC3 Passthru", HB_ACODEC_AC3_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_AC3_PASS, }, { { "DTS Passthru", "copy:dts", "DTS Passthru", HB_ACODEC_DCA_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_DTS_PASS, }, { { "DTS-HD Passthru", "copy:dtshd", "DTS-HD Passthru", HB_ACODEC_DCA_HD_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_DTSHD_PASS, }, { { "MP3", "mp3", "MP3 (libmp3lame)", HB_ACODEC_LAME, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_MP3, }, { { "MP3 Passthru", "copy:mp3", "MP3 Passthru", HB_ACODEC_MP3_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_MP3_PASS, }, { { "Vorbis", "vorbis", "Vorbis (libvorbis)", HB_ACODEC_VORBIS, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_VORBIS, }, { { "FLAC 16-bit", "flac16", "FLAC 16-bit (libavcodec)", HB_ACODEC_FFFLAC, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_FLAC, }, { { "FLAC 24-bit", "flac24", "FLAC 24-bit (libavcodec)", HB_ACODEC_FFFLAC24, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_FLAC, }, { { "Auto Passthru", "copy", "Auto Passthru", HB_ACODEC_AUTO_PASS, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_ACODEC_AUTO_PASS, }, }; int hb_audio_encoders_count = sizeof(hb_audio_encoders) / sizeof(hb_audio_encoders[0]); static int hb_audio_encoder_is_enabled(int encoder) { if (encoder & HB_ACODEC_PASS_FLAG) { // Passthru encoders are always enabled return 1; } switch (encoder) { #ifdef __APPLE__ case HB_ACODEC_CA_AAC: case HB_ACODEC_CA_HAAC: return 1; #endif #ifdef USE_LIBAV_AAC case HB_ACODEC_FFAAC: return avcodec_find_encoder_by_name("aac") != NULL; #endif case HB_ACODEC_FDK_AAC: case HB_ACODEC_FDK_HAAC: return avcodec_find_encoder_by_name("libfdk_aac") != NULL; case HB_ACODEC_AC3: return avcodec_find_encoder(AV_CODEC_ID_AC3) != NULL; case HB_ACODEC_FFFLAC: case HB_ACODEC_FFFLAC24: return avcodec_find_encoder(AV_CODEC_ID_FLAC) != NULL; // the following encoders are always enabled case HB_ACODEC_LAME: case HB_ACODEC_VORBIS: return 1; default: return 0; } } typedef struct { hb_container_t item; hb_container_t *next; int enabled; int gid; } hb_container_internal_t; hb_container_t *hb_containers_first_item = NULL; hb_container_t *hb_containers_last_item = NULL; hb_container_internal_t hb_containers[] = { // legacy muxers, back to HB 0.9.4 whenever possible (disabled) { { "M4V file", "m4v", NULL, "m4v", 0, }, NULL, 0, HB_GID_MUX_MP4, }, { { "MP4 file", "mp4", NULL, "mp4", 0, }, NULL, 0, HB_GID_MUX_MP4, }, { { "MKV file", "mkv", NULL, "mkv", 0, }, NULL, 0, HB_GID_MUX_MKV, }, // actual muxers { { "MPEG-4 (avformat)", "av_mp4", "MPEG-4 (libavformat)", "mp4", HB_MUX_AV_MP4, }, NULL, 1, HB_GID_MUX_MP4, }, { { "MPEG-4 (mp4v2)", "mp4v2", "MPEG-4 (libmp4v2)", "mp4", HB_MUX_MP4V2, }, NULL, 1, HB_GID_MUX_MP4, }, { { "Matroska (avformat)", "av_mkv", "Matroska (libavformat)", "mkv", HB_MUX_AV_MKV, }, NULL, 1, HB_GID_MUX_MKV, }, { { "Matroska (libmkv)", "libmkv", "Matroska (libmkv)", "mkv", HB_MUX_LIBMKV, }, NULL, 1, HB_GID_MUX_MKV, }, }; int hb_containers_count = sizeof(hb_containers) / sizeof(hb_containers[0]); static int hb_container_is_enabled(int format) { switch (format) { case HB_MUX_AV_MP4: case HB_MUX_AV_MKV: return 1; default: return 0; } } void hb_common_global_init() { static int common_init_done = 0; if (common_init_done) return; int i, j; // video framerates for (i = 0; i < hb_video_rates_count; i++) { if (hb_video_rates[i].enabled) { if (hb_video_rates_first_item == NULL) { hb_video_rates_first_item = &hb_video_rates[i].item; } else { ((hb_rate_internal_t*)hb_video_rates_last_item)->next = &hb_video_rates[i].item; } hb_video_rates_last_item = &hb_video_rates[i].item; } } // fallbacks are static for now (no setup required) // audio samplerates for (i = 0; i < hb_audio_rates_count; i++) { if (hb_audio_rates[i].enabled) { if (hb_audio_rates_first_item == NULL) { hb_audio_rates_first_item = &hb_audio_rates[i].item; } else { ((hb_rate_internal_t*)hb_audio_rates_last_item)->next = &hb_audio_rates[i].item; } hb_audio_rates_last_item = &hb_audio_rates[i].item; } } // fallbacks are static for now (no setup required) // audio bitrates for (i = 0; i < hb_audio_bitrates_count; i++) { if (hb_audio_bitrates[i].enabled) { if (hb_audio_bitrates_first_item == NULL) { hb_audio_bitrates_first_item = &hb_audio_bitrates[i].item; } else { ((hb_rate_internal_t*)hb_audio_bitrates_last_item)->next = &hb_audio_bitrates[i].item; } hb_audio_bitrates_last_item = &hb_audio_bitrates[i].item; } } // fallbacks are static for now (no setup required) // audio dithers for (i = 0; i < hb_audio_dithers_count; i++) { if (hb_audio_dithers[i].enabled) { if (hb_audio_dithers_first_item == NULL) { hb_audio_dithers_first_item = &hb_audio_dithers[i].item; } else { ((hb_dither_internal_t*)hb_audio_dithers_last_item)->next = &hb_audio_dithers[i].item; } hb_audio_dithers_last_item = &hb_audio_dithers[i].item; } } // fallbacks are static for now (no setup required) // audio mixdowns for (i = 0; i < hb_audio_mixdowns_count; i++) { if (hb_audio_mixdowns[i].enabled) { if (hb_audio_mixdowns_first_item == NULL) { hb_audio_mixdowns_first_item = &hb_audio_mixdowns[i].item; } else { ((hb_mixdown_internal_t*)hb_audio_mixdowns_last_item)->next = &hb_audio_mixdowns[i].item; } hb_audio_mixdowns_last_item = &hb_audio_mixdowns[i].item; } } // fallbacks are static for now (no setup required) // video encoders for (i = 0; i < hb_video_encoders_count; i++) { if (hb_video_encoders[i].enabled) { // we still need to check hb_video_encoders[i].enabled = hb_video_encoder_is_enabled(hb_video_encoders[i].item.codec); } if (hb_video_encoders[i].enabled) { if (hb_video_encoders_first_item == NULL) { hb_video_encoders_first_item = &hb_video_encoders[i].item; } else { ((hb_encoder_internal_t*)hb_video_encoders_last_item)->next = &hb_video_encoders[i].item; } hb_video_encoders_last_item = &hb_video_encoders[i].item; } } // setup fallbacks for (i = 0; i < hb_video_encoders_count; i++) { if (!hb_video_encoders[i].enabled) { if ((hb_video_encoders[i].item.codec & HB_VCODEC_MASK) && (hb_video_encoder_is_enabled(hb_video_encoders[i].item.codec))) { // we have a specific fallback and it's enabled continue; } for (j = 0; j < hb_video_encoders_count; j++) { if (hb_video_encoders[j].enabled && hb_video_encoders[j].gid == hb_video_encoders[i].gid) { hb_video_encoders[i].item.codec = hb_video_encoders[j].item.codec; break; } } } } // audio encoders for (i = 0; i < hb_audio_encoders_count; i++) { if (hb_audio_encoders[i].enabled) { // we still need to check hb_audio_encoders[i].enabled = hb_audio_encoder_is_enabled(hb_audio_encoders[i].item.codec); } if (hb_audio_encoders[i].enabled) { if (hb_audio_encoders_first_item == NULL) { hb_audio_encoders_first_item = &hb_audio_encoders[i].item; } else { ((hb_encoder_internal_t*)hb_audio_encoders_last_item)->next = &hb_audio_encoders[i].item; } hb_audio_encoders_last_item = &hb_audio_encoders[i].item; } } // setup fallbacks for (i = 0; i < hb_audio_encoders_count; i++) { if (!hb_audio_encoders[i].enabled) { if ((hb_audio_encoders[i].item.codec & HB_ACODEC_MASK) && (hb_audio_encoder_is_enabled(hb_audio_encoders[i].item.codec))) { // we have a specific fallback and it's enabled continue; } for (j = 0; j < hb_audio_encoders_count; j++) { if (hb_audio_encoders[j].enabled && hb_audio_encoders[j].gid == hb_audio_encoders[i].gid) { hb_audio_encoders[i].item.codec = hb_audio_encoders[j].item.codec; break; } } if ((hb_audio_encoders[i].item.codec & HB_ACODEC_MASK) == 0 && (hb_audio_encoders[i].gid == HB_GID_ACODEC_AAC_HE)) { // try to find an AAC fallback if no HE-AAC encoder is available for (j = 0; j < hb_audio_encoders_count; j++) { if (hb_audio_encoders[j].enabled && hb_audio_encoders[j].gid == HB_GID_ACODEC_AAC) { hb_audio_encoders[i].item.codec = hb_audio_encoders[j].item.codec; break; } } } } } // video containers for (i = 0; i < hb_containers_count; i++) { if (hb_containers[i].enabled) { // we still need to check hb_containers[i].enabled = hb_container_is_enabled(hb_containers[i].item.format); } if (hb_containers[i].enabled) { if (hb_containers_first_item == NULL) { hb_containers_first_item = &hb_containers[i].item; } else { ((hb_container_internal_t*)hb_containers_last_item)->next = &hb_containers[i].item; } hb_containers_last_item = &hb_containers[i].item; } } // setup fallbacks for (i = 0; i < hb_containers_count; i++) { if (!hb_containers[i].enabled) { if ((hb_containers[i].item.format & HB_MUX_MASK) && (hb_container_is_enabled(hb_containers[i].item.format))) { // we have a specific fallback and it's enabled continue; } for (j = 0; j < hb_containers_count; j++) { if (hb_containers[j].enabled && hb_containers[j].gid == hb_containers[i].gid) { hb_containers[i].item.format = hb_containers[j].item.format; break; } } } } // we're done, yay! common_init_done = 1; } int hb_video_framerate_get_from_name(const char *name) { if (name == NULL || *name == '\0') goto fail; int i; for (i = 0; i < hb_video_rates_count; i++) { if (!strcasecmp(hb_video_rates[i].item.name, name)) { return hb_video_rates[i].item.rate; } } fail: return -1; } const char* hb_video_framerate_get_name(int framerate) { if (framerate > hb_video_rates_first_item->rate || framerate < hb_video_rates_last_item ->rate) goto fail; const hb_rate_t *video_framerate = NULL; while ((video_framerate = hb_video_framerate_get_next(video_framerate)) != NULL) { if (video_framerate->rate == framerate) { return video_framerate->name; } } fail: return NULL; } const char* hb_video_framerate_sanitize_name(const char *name) { return hb_video_framerate_get_name(hb_video_framerate_get_from_name(name)); } const hb_rate_t* hb_video_framerate_get_next(const hb_rate_t *last) { if (last == NULL) { return hb_video_rates_first_item; } return ((hb_rate_internal_t*)last)->next; } int hb_audio_samplerate_get_best(uint32_t codec, int samplerate, int *sr_shift) { int best_samplerate; if (samplerate < 32000 && (codec == HB_ACODEC_AC3 || codec == HB_ACODEC_CA_HAAC)) { // ca_haac can't do samplerates < 32 kHz // AC-3 < 32 kHz suffers from poor hardware compatibility best_samplerate = 32000; } else if (samplerate < 16000 && codec == HB_ACODEC_FDK_HAAC) { // fdk_haac can't do samplerates < 16 kHz best_samplerate = 16000; } else { best_samplerate = hb_audio_rates_first_item->rate; const hb_rate_t *audio_samplerate = NULL; while ((audio_samplerate = hb_audio_samplerate_get_next(audio_samplerate)) != NULL) { if (samplerate == audio_samplerate->rate) { // valid samplerate best_samplerate = audio_samplerate->rate; break; } if (samplerate > audio_samplerate->rate) { // samplerates are sanitized downwards best_samplerate = audio_samplerate->rate; } } } if (sr_shift != NULL) { /* sr_shift: 0 -> 48000, 44100, 32000 Hz * 1 -> 24000, 22050, 16000 Hz * 2 -> 12000, 11025, 8000 Hz * * also, since samplerates are sanitized downwards: * * (samplerate < 32000) implies (samplerate <= 24000) */ *sr_shift = ((best_samplerate < 16000) ? 2 : (best_samplerate < 32000) ? 1 : 0); } return best_samplerate; } int hb_audio_samplerate_get_from_name(const char *name) { if (name == NULL || *name == '\0') goto fail; int i; for (i = 0; i < hb_audio_rates_count; i++) { if (!strcasecmp(hb_audio_rates[i].item.name, name)) { return hb_audio_rates[i].item.rate; } } // maybe the samplerate was specified in Hz i = atoi(name); if (i >= hb_audio_rates_first_item->rate && i <= hb_audio_rates_last_item ->rate) { return hb_audio_samplerate_get_best(0, i, NULL); } fail: return -1; } const char* hb_audio_samplerate_get_name(int samplerate) { if (samplerate < hb_audio_rates_first_item->rate || samplerate > hb_audio_rates_last_item ->rate) goto fail; const hb_rate_t *audio_samplerate = NULL; while ((audio_samplerate = hb_audio_samplerate_get_next(audio_samplerate)) != NULL) { if (audio_samplerate->rate == samplerate) { return audio_samplerate->name; } } fail: return NULL; } const hb_rate_t* hb_audio_samplerate_get_next(const hb_rate_t *last) { if (last == NULL) { return hb_audio_rates_first_item; } return ((hb_rate_internal_t*)last)->next; } // Given an input bitrate, find closest match in the set of allowed bitrates static int hb_audio_bitrate_find_closest(int bitrate) { // Check if bitrate mode was disabled if (bitrate <= 0) return bitrate; int closest_bitrate = hb_audio_bitrates_first_item->rate; const hb_rate_t *audio_bitrate = NULL; while ((audio_bitrate = hb_audio_bitrate_get_next(audio_bitrate)) != NULL) { if (bitrate == audio_bitrate->rate) { // valid bitrate closest_bitrate = audio_bitrate->rate; break; } if (bitrate > audio_bitrate->rate) { // bitrates are sanitized downwards closest_bitrate = audio_bitrate->rate; } } return closest_bitrate; } // Given an input bitrate, sanitize it. // Check low and high limits and make sure it is in the set of allowed bitrates. int hb_audio_bitrate_get_best(uint32_t codec, int bitrate, int samplerate, int mixdown) { int low, high; hb_audio_bitrate_get_limits(codec, samplerate, mixdown, &low, &high); if (bitrate > high) bitrate = high; if (bitrate < low) bitrate = low; return hb_audio_bitrate_find_closest(bitrate); } // Get the default bitrate for a given codec/samplerate/mixdown triplet. int hb_audio_bitrate_get_default(uint32_t codec, int samplerate, int mixdown) { if ((codec & HB_ACODEC_PASS_FLAG) || !(codec & HB_ACODEC_MASK)) goto fail; int bitrate, nchannels, sr_shift; /* full-bandwidth channels, sr_shift */ nchannels = (hb_mixdown_get_discrete_channel_count(mixdown) - hb_mixdown_get_low_freq_channel_count(mixdown)); hb_audio_samplerate_get_best(codec, samplerate, &sr_shift); switch (codec) { case HB_ACODEC_FFFLAC: case HB_ACODEC_FFFLAC24: goto fail; // 96, 224, 640 Kbps case HB_ACODEC_AC3: bitrate = (nchannels * 128) - (32 * (nchannels < 5)); break; case HB_ACODEC_CA_HAAC: case HB_ACODEC_FDK_HAAC: bitrate = nchannels * 32; break; default: bitrate = nchannels * 80; break; } // sample_rate adjustment bitrate >>= sr_shift; return hb_audio_bitrate_get_best(codec, bitrate, samplerate, mixdown); fail: return -1; } /* Get the bitrate low and high limits for a codec/samplerate/mixdown triplet. * * Encoder 1.0 channel 2.0 channels 5.1 channels 6.1 channels 7.1 channels * -------------------------------------------------------------------------------------- * * ffaac * ----- * supported samplerates: 8 - 48 kHz * libavcodec/aacenc.c defines a maximum bitrate: * -> 6144 * samplerate / 1024 bps (per channel, incl. LFE). * But output bitrates don't go as high as the theoretical maximums: * 12 kHz 61 (72) 123 (144) * 24 kHz 121 (144) 242 (288) * 48 kHz 236 (288) 472 (576) * Also, ffaac isn't a great encoder, so you don't want to allow too low a bitrate. * Limits: minimum of 32 Kbps per channel * maximum of 192 Kbps per channel at 32 kHz, adjusted for sr_shift * maximum of 256 Kbps per channel at 44.1-48 kHz, adjusted for sr_shift * * vorbis * ------ * supported samplerates: 8 - 48 kHz * lib/modes/setup_*.h provides a range of allowed bitrates for various configurations. * for each samplerate, the highest minimums and lowest maximums are: * 8 kHz Minimum 8 Kbps, maximum 32 Kbps (per channel, incl. LFE). * 12 kHz Minimum 14 Kbps, maximum 44 Kbps (per channel, incl. LFE). * 16 kHz Minimum 16 Kbps, maximum 86 Kbps (per channel, incl. LFE). * 24 kHz Minimum 22 Kbps, maximum 86 Kbps (per channel, incl. LFE). * 32 kHz Minimum 26 Kbps, maximum 190 Kbps (per channel, incl. LFE). * 48 kHz Minimum 28 Kbps, maximum 240 Kbps (per channel, incl. LFE). * Limits: minimum of 14/22/28 Kbps per channel (8-12, 16-24, 32-48 kHz) * maximum of 32/86/190/240 Kbps per channel (8-12, 16-24, 32, 44.1-48 kHz) * * lame * ---- * supported samplerates: 8 - 48 kHz * lame_init_params() allows the following bitrates: * 12 kHz Minimum 8 Kbps, maximum 64 Kbps * 24 kHz Minimum 8 Kbps, maximum 160 Kbps * 48 kHz Minimum 32 Kbps, maximum 320 Kbps * Limits: minimum of 8/8/32 Kbps (8-12, 16-24, 32-48 kHz) * maximum of 64/160/320 Kbps (8-12, 16-24, 32-48 kHz) * * ffac3 * ----- * supported samplerates: 32 - 48 kHz (< 32 kHz disabled for compatibility reasons) * Dolby's encoder has a min. of 224 Kbps for 5 full-bandwidth channels (5.0, 5.1) * The maximum AC3 bitrate is 640 Kbps * Limits: minimum of 224/5 Kbps per full-bandwidth channel, maximum of 640 Kbps * * ca_aac * ------ * supported samplerates: 8 - 48 kHz * Core Audio API provides a range of allowed bitrates: * 8 kHz 8 - 24 16 - 48 40 - 112 48 - 144 56 - 160 * 12 kHz 12 - 32 24 - 64 64 - 160 72 - 192 96 - 224 * 16 kHz 12 - 48 24 - 96 64 - 224 72 - 288 96 - 320 * 24 kHz 16 - 64 32 - 128 80 - 320 96 - 384 112 - 448 * 32 kHz 24 - 96 48 - 192 128 - 448 144 - 576 192 - 640 * 48 kHz 32 - 256 64 - 320 160 - 768 192 - 960 224 - 960 * Limits: * 8 kHz -> minimum of 8 Kbps and maximum of 24 Kbps per full-bandwidth channel * 12 kHz -> minimum of 12 Kbps and maximum of 32 Kbps per full-bandwidth channel * 16 kHz -> minimum of 12 Kbps and maximum of 48 Kbps per full-bandwidth channel * 24 kHz -> minimum of 16 Kbps and maximum of 64 Kbps per full-bandwidth channel * 32 kHz -> minimum of 24 Kbps and maximum of 96 Kbps per full-bandwidth channel * 48 kHz -> minimum of 32 Kbps and maximum of 160 Kbps per full-bandwidth channel * 48 kHz -> maximum of +96 Kbps for Mono * Note: encCoreAudioInit() will sanitize any mistake made here. * * ca_haac * ------- * supported samplerates: 32 - 48 kHz * Core Audio API provides a range of allowed bitrates: * 32 kHz 12 - 40 24 - 80 64 - 192 N/A 96 - 256 * 48 kHz 16 - 40 32 - 80 80 - 192 N/A 112 - 256 * Limits: minimum of 12 Kbps per full-bandwidth channel (<= 32 kHz) * minimum of 16 Kbps per full-bandwidth channel ( > 32 kHz) * maximum of 40 Kbps per full-bandwidth channel * Note: encCoreAudioInit() will sanitize any mistake made here. * * fdk_aac * ------- * supported samplerates: 8 - 48 kHz * libfdk limits the bitrate to the following values: * 8 kHz 48 96 240 * 12 kHz 72 144 360 * 16 kHz 96 192 480 * 24 kHz 144 288 720 * 32 kHz 192 384 960 * 48 kHz 288 576 1440 * Limits: minimum of samplerate * 2/3 Kbps per full-bandwidth channel (see ca_aac) * maximum of samplerate * 6.0 Kbps per full-bandwidth channel * * fdk_haac * -------- * supported samplerates: 16 - 48 kHz * libfdk limits the bitrate to the following values: * 16 kHz 8 - 48 16 - 96 45 - 199 * 24 kHz 8 - 63 16 - 127 45 - 266 * 32 kHz 8 - 63 16 - 127 45 - 266 * 48 kHz 12 - 63 16 - 127 50 - 266 * Limits: minimum of 12 Kbps per full-bandwidth channel (<= 32 kHz) (see ca_haac) * minimum of 16 Kbps per full-bandwidth channel ( > 32 kHz) (see ca_haac) * maximum of 48, 96 or 192 Kbps (1.0, 2.0, 5.1) (<= 16 kHz) * maximum of 64, 128 or 256 Kbps (1.0, 2.0, 5.1) ( > 16 kHz) */ void hb_audio_bitrate_get_limits(uint32_t codec, int samplerate, int mixdown, int *low, int *high) { /* samplerate, sr_shift */ int sr_shift; samplerate = hb_audio_samplerate_get_best(codec, samplerate, &sr_shift); /* LFE, full-bandwidth channels */ int lfe_count, nchannels; lfe_count = hb_mixdown_get_low_freq_channel_count(mixdown); nchannels = hb_mixdown_get_discrete_channel_count(mixdown) - lfe_count; switch (codec) { // Bitrates don't apply to "lossless" audio case HB_ACODEC_FFFLAC: case HB_ACODEC_FFFLAC24: *low = *high = -1; return; case HB_ACODEC_AC3: *low = 224 * nchannels / 5; *high = 640; break; case HB_ACODEC_CA_AAC: { switch (samplerate) { case 8000: *low = nchannels * 8; *high = nchannels * 24; break; case 11025: case 12000: *low = nchannels * 12; *high = nchannels * 32; break; case 16000: *low = nchannels * 12; *high = nchannels * 48; break; case 22050: case 24000: *low = nchannels * 16; *high = nchannels * 64; break; case 32000: *low = nchannels * 24; *high = nchannels * 96; break; case 44100: case 48000: default: *low = nchannels * 32; *high = nchannels * (160 + (96 * (nchannels == 1))); break; } break; } case HB_ACODEC_CA_HAAC: *low = nchannels * (12 + (4 * (samplerate >= 44100))); *high = nchannels * 40; break; case HB_ACODEC_FDK_AAC: *low = nchannels * samplerate * 2 / 3000; *high = nchannels * samplerate * 6 / 1000; break; case HB_ACODEC_FDK_HAAC: *low = (nchannels * (12 + (4 * (samplerate >= 44100)))); *high = (nchannels - (nchannels > 2)) * (48 + (16 * (samplerate >= 22050))); break; case HB_ACODEC_FFAAC: *low = ((nchannels + lfe_count) * 32); *high = ((nchannels + lfe_count) * ((192 + (64 * ((samplerate << sr_shift) >= 44100))) >> sr_shift)); break; case HB_ACODEC_LAME: *low = 8 + (24 * (sr_shift < 1)); *high = 64 + (96 * (sr_shift < 2)) + (160 * (sr_shift < 1)); break; case HB_ACODEC_VORBIS: *low = (nchannels + lfe_count) * (14 + (8 * (sr_shift < 2)) + (6 * (sr_shift < 1))); *high = (nchannels + lfe_count) * (32 + ( 54 * (sr_shift < 2)) + (104 * (sr_shift < 1)) + ( 50 * (samplerate >= 44100))); break; // Bitrates don't apply to passthrough audio, but may apply if we // fall back to an encoder when the source can't be passed through. default: *low = hb_audio_bitrates_first_item->rate; *high = hb_audio_bitrates_last_item ->rate; break; } // sanitize max. bitrate if (*high < hb_audio_bitrates_first_item->rate) *high = hb_audio_bitrates_first_item->rate; if (*high > hb_audio_bitrates_last_item ->rate) *high = hb_audio_bitrates_last_item ->rate; } const hb_rate_t* hb_audio_bitrate_get_next(const hb_rate_t *last) { if (last == NULL) { return hb_audio_bitrates_first_item; } return ((hb_rate_internal_t*)last)->next; } // Get limits and hints for the UIs. // // granularity sets the minimum step increments that should be used // (it's ok to round up to some nice multiple if you like) // // direction says whether 'low' limit is highest or lowest // quality (direction 0 == lowest value is worst quality) void hb_video_quality_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction) { #ifdef USE_QSV if (codec & HB_VCODEC_QSV_MASK) { return hb_qsv_video_quality_get_limits(codec, low, high, granularity, direction); } #endif switch (codec) { case HB_VCODEC_X264: #ifdef USE_X265 case HB_VCODEC_X265: #endif *direction = 1; *granularity = 0.1; *low = 0.; *high = 51.; break; case HB_VCODEC_THEORA: *direction = 0; *granularity = 1.; *low = 0.; *high = 63.; break; case HB_VCODEC_FFMPEG_VP8: *direction = 1; *granularity = 1.; *low = 0.; *high = 63.; break; case HB_VCODEC_FFMPEG_MPEG2: case HB_VCODEC_FFMPEG_MPEG4: default: *direction = 1; *granularity = 1.; *low = 1.; *high = 31.; break; } } const char* hb_video_quality_get_name(uint32_t codec) { #ifdef USE_QSV if (codec & HB_VCODEC_QSV_MASK) { return hb_qsv_video_quality_get_name(codec); } #endif switch (codec) { case HB_VCODEC_X264: #ifdef USE_X265 case HB_VCODEC_X265: #endif return "RF"; case HB_VCODEC_FFMPEG_VP8: return "CQ"; default: return "QP"; } } const char* const* hb_video_encoder_get_presets(int encoder) { #ifdef USE_QSV if (encoder & HB_VCODEC_QSV_MASK) { return hb_qsv_preset_get_names(); } #endif switch (encoder) { case HB_VCODEC_X264: return x264_preset_names; #ifdef USE_X265 case HB_VCODEC_X265: return x265_preset_names; #endif default: return NULL; } } const char* const* hb_video_encoder_get_tunes(int encoder) { switch (encoder) { case HB_VCODEC_X264: return x264_tune_names; #ifdef USE_X265 case HB_VCODEC_X265: return x265_tune_names; #endif default: return NULL; } } const char* const* hb_video_encoder_get_profiles(int encoder) { #ifdef USE_QSV if (encoder & HB_VCODEC_QSV_MASK) { return hb_qsv_profile_get_names(encoder); } #endif switch (encoder) { case HB_VCODEC_X264: return hb_h264_profile_names; case HB_VCODEC_X265: return hb_h265_profile_names; default: return NULL; } } const char* const* hb_video_encoder_get_levels(int encoder) { #ifdef USE_QSV if (encoder & HB_VCODEC_QSV_MASK) { return hb_qsv_level_get_names(encoder); } #endif switch (encoder) { case HB_VCODEC_X264: return hb_h264_level_names; default: return NULL; } } // Get limits and hints for the UIs. // // granularity sets the minimum step increments that should be used // (it's ok to round up to some nice multiple if you like) // // direction says whether 'low' limit is highest or lowest // quality (direction 0 == lowest value is worst quality) void hb_audio_quality_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction) { switch (codec) { case HB_ACODEC_LAME: *direction = 1; *granularity = 0.5; *low = 0.; *high = 10.; break; case HB_ACODEC_VORBIS: *direction = 0; *granularity = 0.5; *low = -2.; *high = 10.; break; case HB_ACODEC_CA_AAC: *direction = 0; *granularity = 9.; *low = 1.; *high = 127.; break; default: *direction = 0; *granularity = 1.; *low = *high = HB_INVALID_AUDIO_QUALITY; break; } } float hb_audio_quality_get_best(uint32_t codec, float quality) { int direction; float low, high, granularity; hb_audio_quality_get_limits(codec, &low, &high, &granularity, &direction); if (quality > high) quality = high; if (quality < low) quality = low; return quality; } float hb_audio_quality_get_default(uint32_t codec) { switch (codec) { case HB_ACODEC_LAME: return 2.; case HB_ACODEC_VORBIS: return 5.; case HB_ACODEC_CA_AAC: return 91.; default: return HB_INVALID_AUDIO_QUALITY; } } // Get limits and hints for the UIs. // // granularity sets the minimum step increments that should be used // (it's ok to round up to some nice multiple if you like) // // direction says whether 'low' limit is highest or lowest // compression level (direction 0 == lowest value is worst compression level) void hb_audio_compression_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction) { switch (codec) { case HB_ACODEC_FFFLAC: case HB_ACODEC_FFFLAC24: *direction = 0; *granularity = 1.; *high = 12.; *low = 0.; break; case HB_ACODEC_LAME: *direction = 1; *granularity = 1.; *high = 9.; *low = 0.; break; default: *direction = 0; *granularity = 1.; *low = *high = -1.; break; } } float hb_audio_compression_get_best(uint32_t codec, float compression) { int direction; float low, high, granularity; hb_audio_compression_get_limits(codec, &low, &high, &granularity, &direction); if( compression > high ) compression = high; if( compression < low ) compression = low; return compression; } float hb_audio_compression_get_default(uint32_t codec) { switch (codec) { case HB_ACODEC_FFFLAC: case HB_ACODEC_FFFLAC24: return 5.; case HB_ACODEC_LAME: return 2.; default: return -1.; } } int hb_audio_dither_get_default() { // "auto" return hb_audio_dithers_first_item->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: case HB_ACODEC_FDK_AAC: case HB_ACODEC_FDK_HAAC: return 1; default: return 0; } } int hb_audio_dither_get_from_name(const char *name) { if (name == NULL || *name == '\0') goto fail; int i; for ( i = 0; i < hb_audio_dithers_count; i++) { if (!strcasecmp(hb_audio_dithers[i].item.short_name, name) || !strcasecmp(hb_audio_dithers[i].item.description, name)) { return hb_audio_dithers[i].item.method; } } fail: return hb_audio_dither_get_default(); } const char* hb_audio_dither_get_description(int method) { if (method < hb_audio_dithers_first_item->method || method > hb_audio_dithers_last_item ->method) goto fail; const hb_dither_t *audio_dither = NULL; while ((audio_dither = hb_audio_dither_get_next(audio_dither)) != NULL) { if (audio_dither->method == method) { return audio_dither->description; } } fail: return NULL; } const hb_dither_t* hb_audio_dither_get_next(const hb_dither_t *last) { if (last == NULL) { return hb_audio_dithers_first_item; } return ((hb_dither_internal_t*)last)->next; } int hb_mixdown_is_supported(int mixdown, uint32_t codec, uint64_t layout) { return (hb_mixdown_has_codec_support(mixdown, codec) && hb_mixdown_has_remix_support(mixdown, layout)); } int hb_mixdown_has_codec_support(int mixdown, uint32_t codec) { // Passthru, only "None" mixdown is supported if (codec & HB_ACODEC_PASS_FLAG) return (mixdown == HB_AMIXDOWN_NONE); // Not passthru, "None" mixdown never supported if (mixdown == HB_AMIXDOWN_NONE) return 0; switch (codec) { case HB_ACODEC_VORBIS: case HB_ACODEC_FFFLAC: case HB_ACODEC_FFFLAC24: return (mixdown <= HB_AMIXDOWN_7POINT1); case HB_ACODEC_LAME: return (mixdown <= HB_AMIXDOWN_DOLBYPLII); case HB_ACODEC_CA_AAC: case HB_ACODEC_CA_HAAC: return ((mixdown <= HB_AMIXDOWN_5POINT1) || (mixdown == HB_AMIXDOWN_5_2_LFE)); default: return (mixdown <= HB_AMIXDOWN_5POINT1); } } int hb_mixdown_has_remix_support(int mixdown, uint64_t layout) { switch (mixdown) { // stereo + front left/right of center case HB_AMIXDOWN_5_2_LFE: return ((layout & AV_CH_FRONT_LEFT_OF_CENTER) && (layout & AV_CH_FRONT_RIGHT_OF_CENTER) && (layout & AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO); // 7.0 or better case HB_AMIXDOWN_7POINT1: return ((layout & AV_CH_LAYOUT_7POINT0) == AV_CH_LAYOUT_7POINT0); // 6.0 or better case HB_AMIXDOWN_6POINT1: return ((layout & AV_CH_LAYOUT_7POINT0) == AV_CH_LAYOUT_7POINT0 || (layout & AV_CH_LAYOUT_6POINT0) == AV_CH_LAYOUT_6POINT0 || (layout & AV_CH_LAYOUT_HEXAGONAL) == AV_CH_LAYOUT_HEXAGONAL); // stereo + either of front center, side or back left/right, back center case HB_AMIXDOWN_5POINT1: return ((layout & AV_CH_LAYOUT_2_1) == AV_CH_LAYOUT_2_1 || (layout & AV_CH_LAYOUT_2_2) == AV_CH_LAYOUT_2_2 || (layout & AV_CH_LAYOUT_QUAD) == AV_CH_LAYOUT_QUAD || (layout & AV_CH_LAYOUT_SURROUND) == AV_CH_LAYOUT_SURROUND); // stereo + either of side or back left/right, back center // also, allow Dolby Surrounbd output if the input is already Dolby case HB_AMIXDOWN_DOLBY: case HB_AMIXDOWN_DOLBYPLII: return ((layout & AV_CH_LAYOUT_2_1) == AV_CH_LAYOUT_2_1 || (layout & AV_CH_LAYOUT_2_2) == AV_CH_LAYOUT_2_2 || (layout & AV_CH_LAYOUT_QUAD) == AV_CH_LAYOUT_QUAD || (layout == AV_CH_LAYOUT_STEREO_DOWNMIX && mixdown == HB_AMIXDOWN_DOLBY)); // more than 1 channel case HB_AMIXDOWN_STEREO: return (av_get_channel_layout_nb_channels(layout) > 1); // regular stereo (not Dolby) case HB_AMIXDOWN_LEFT: case HB_AMIXDOWN_RIGHT: return (layout == AV_CH_LAYOUT_STEREO); // mono remix always supported // HB_AMIXDOWN_NONE always supported (for Passthru) case HB_AMIXDOWN_MONO: case HB_AMIXDOWN_NONE: return 1; // unknown mixdown, should never happen default: return 0; } } int hb_mixdown_get_discrete_channel_count(int amixdown) { switch (amixdown) { case HB_AMIXDOWN_5_2_LFE: case HB_AMIXDOWN_7POINT1: return 8; case HB_AMIXDOWN_6POINT1: return 7; case HB_AMIXDOWN_5POINT1: return 6; case HB_AMIXDOWN_MONO: case HB_AMIXDOWN_LEFT: case HB_AMIXDOWN_RIGHT: return 1; case HB_AMIXDOWN_NONE: return 0; default: return 2; } } int hb_mixdown_get_low_freq_channel_count(int amixdown) { switch (amixdown) { case HB_AMIXDOWN_5POINT1: case HB_AMIXDOWN_6POINT1: case HB_AMIXDOWN_7POINT1: case HB_AMIXDOWN_5_2_LFE: return 1; default: return 0; } } int hb_mixdown_get_best(uint32_t codec, uint64_t layout, int mixdown) { // Passthru, only "None" mixdown is supported if (codec & HB_ACODEC_PASS_FLAG) return HB_AMIXDOWN_NONE; int best_mixdown = HB_INVALID_AMIXDOWN; const hb_mixdown_t *audio_mixdown = hb_mixdown_get_next(NULL); // test all non-None mixdowns while the value is <= the requested mixdown // HB_INVALID_AMIXDOWN means the highest supported mixdown was requested while ((audio_mixdown = hb_mixdown_get_next(audio_mixdown)) != NULL) { if ((mixdown == HB_INVALID_AMIXDOWN || audio_mixdown->amixdown <= mixdown) && (hb_mixdown_is_supported(audio_mixdown->amixdown, codec, layout))) { best_mixdown = audio_mixdown->amixdown; } } return best_mixdown; } int hb_mixdown_get_default(uint32_t codec, uint64_t layout) { int mixdown; switch (codec) { // the FLAC encoder defaults to the best mixdown up to 7.1 case HB_ACODEC_FFFLAC: case HB_ACODEC_FFFLAC24: mixdown = HB_AMIXDOWN_7POINT1; break; // the AC3 encoder defaults to the best mixdown up to 5.1 case HB_ACODEC_AC3: mixdown = HB_AMIXDOWN_5POINT1; break; // other encoders default to the best mixdown up to DPLII default: mixdown = HB_AMIXDOWN_DOLBYPLII; break; } // return the best available mixdown up to the selected default return hb_mixdown_get_best(codec, layout, mixdown); } int hb_mixdown_get_from_name(const char *name) { if (name == NULL || *name == '\0') goto fail; int i; for (i = 0; i < hb_audio_mixdowns_count; i++) { if (!strcasecmp(hb_audio_mixdowns[i].item.name, name) || !strcasecmp(hb_audio_mixdowns[i].item.short_name, name)) { return hb_audio_mixdowns[i].item.amixdown; } } fail: return HB_INVALID_AMIXDOWN; } const char* hb_mixdown_get_name(int mixdown) { if (mixdown < hb_audio_mixdowns_first_item->amixdown || mixdown > hb_audio_mixdowns_last_item ->amixdown) goto fail; const hb_mixdown_t *audio_mixdown = NULL; while ((audio_mixdown = hb_mixdown_get_next(audio_mixdown)) != NULL) { if (audio_mixdown->amixdown == mixdown) { return audio_mixdown->name; } } fail: return NULL; } const char* hb_mixdown_get_short_name(int mixdown) { if (mixdown < hb_audio_mixdowns_first_item->amixdown || mixdown > hb_audio_mixdowns_last_item ->amixdown) goto fail; const hb_mixdown_t *audio_mixdown = NULL; while ((audio_mixdown = hb_mixdown_get_next(audio_mixdown)) != NULL) { if (audio_mixdown->amixdown == mixdown) { return audio_mixdown->short_name; } } fail: return NULL; } const char* hb_mixdown_sanitize_name(const char *name) { return hb_mixdown_get_name(hb_mixdown_get_from_name(name)); } const hb_mixdown_t* hb_mixdown_get_next(const hb_mixdown_t *last) { if (last == NULL) { return hb_audio_mixdowns_first_item; } return ((hb_mixdown_internal_t*)last)->next; } int hb_video_encoder_get_default(int muxer) { if (!(muxer & HB_MUX_MASK)) goto fail; const hb_encoder_t *video_encoder = NULL; while ((video_encoder = hb_video_encoder_get_next(video_encoder)) != NULL) { if (video_encoder->muxers & muxer) { return video_encoder->codec; } } fail: return 0; } int hb_video_encoder_get_from_name(const char *name) { if (name == NULL || *name == '\0') goto fail; int i; for (i = 0; i < hb_video_encoders_count; i++) { if (!strcasecmp(hb_video_encoders[i].item.name, name) || !strcasecmp(hb_video_encoders[i].item.short_name, name)) { return hb_video_encoders[i].item.codec; } } fail: return 0; } const char* hb_video_encoder_get_name(int encoder) { if (!(encoder & HB_VCODEC_MASK)) goto fail; const hb_encoder_t *video_encoder = NULL; while ((video_encoder = hb_video_encoder_get_next(video_encoder)) != NULL) { if (video_encoder->codec == encoder) { return video_encoder->name; } } fail: return NULL; } const char* hb_video_encoder_get_short_name(int encoder) { if (!(encoder & HB_VCODEC_MASK)) goto fail; const hb_encoder_t *video_encoder = NULL; while ((video_encoder = hb_video_encoder_get_next(video_encoder)) != NULL) { if (video_encoder->codec == encoder) { return video_encoder->short_name; } } fail: return NULL; } const char* hb_video_encoder_get_long_name(int encoder) { if (!(encoder & HB_VCODEC_MASK)) goto fail; const hb_encoder_t *video_encoder = NULL; while ((video_encoder = hb_video_encoder_get_next(video_encoder)) != NULL) { if (video_encoder->codec == encoder) { return video_encoder->long_name; } } fail: return NULL; } const char* hb_video_encoder_sanitize_name(const char *name) { return hb_video_encoder_get_name(hb_video_encoder_get_from_name(name)); } const hb_encoder_t* hb_video_encoder_get_next(const hb_encoder_t *last) { if (last == NULL) { return hb_video_encoders_first_item; } return ((hb_encoder_internal_t*)last)->next; } // for a valid passthru, return the matching encoder for that codec (if any), // else return -1 (i.e. drop the track) int hb_audio_encoder_get_fallback_for_passthru(int passthru) { int gid; const hb_encoder_t *audio_encoder = NULL; switch (passthru) { case HB_ACODEC_AAC_PASS: gid = HB_GID_ACODEC_AAC; break; case HB_ACODEC_AC3_PASS: gid = HB_GID_ACODEC_AC3; break; case HB_ACODEC_MP3_PASS: gid = HB_GID_ACODEC_MP3; break; default: gid = HB_GID_NONE; // will never match an enabled encoder break; } while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) { if (((hb_encoder_internal_t*)audio_encoder)->gid == gid) { return audio_encoder->codec; } } // passthru tracks are often the second audio from the same source track // if we don't have an encoder matching the passthru codec, return 0 // dropping the track, as well as ensuring that there is at least one // audio track in the output is then up to the UIs return 0; } int hb_audio_encoder_get_default(int muxer) { if (!(muxer & HB_MUX_MASK)) goto fail; int codec = 0; const hb_encoder_t *audio_encoder = NULL; while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) { // default encoder should not be passthru if ((audio_encoder->muxers & muxer) && (audio_encoder->codec & HB_ACODEC_PASS_FLAG) == 0) { codec = audio_encoder->codec; break; } } // Lame is better than our low-end AAC encoders // if the container is MKV, use the former // AAC is still used when the container is MP4 (for better compatibility) if (codec == HB_ACODEC_FFAAC && (muxer & HB_MUX_MASK_MKV) == muxer) { return HB_ACODEC_LAME; } else { return codec; } fail: return 0; } int hb_audio_encoder_get_from_name(const char *name) { if (name == NULL || *name == '\0') goto fail; int i; for (i = 0; i < hb_audio_encoders_count; i++) { if (!strcasecmp(hb_audio_encoders[i].item.name, name) || !strcasecmp(hb_audio_encoders[i].item.short_name, name)) { return hb_audio_encoders[i].item.codec; } } fail: return 0; } const char* hb_audio_encoder_get_name(int encoder) { if (!(encoder & HB_ACODEC_ANY)) goto fail; const hb_encoder_t *audio_encoder = NULL; while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) { if (audio_encoder->codec == encoder) { return audio_encoder->name; } } fail: return NULL; } const char* hb_audio_encoder_get_short_name(int encoder) { if (!(encoder & HB_ACODEC_ANY)) goto fail; const hb_encoder_t *audio_encoder = NULL; while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) { if (audio_encoder->codec == encoder) { return audio_encoder->short_name; } } fail: return NULL; } const char* hb_audio_encoder_get_long_name(int encoder) { if (!(encoder & HB_ACODEC_ANY)) goto fail; const hb_encoder_t *audio_encoder = NULL; while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) { if (audio_encoder->codec == encoder) { return audio_encoder->long_name; } } fail: return NULL; } const char* hb_audio_encoder_sanitize_name(const char *name) { return hb_audio_encoder_get_name(hb_audio_encoder_get_from_name(name)); } const hb_encoder_t* hb_audio_encoder_get_next(const hb_encoder_t *last) { if (last == NULL) { return hb_audio_encoders_first_item; } return ((hb_encoder_internal_t*)last)->next; } void hb_autopassthru_apply_settings(hb_job_t *job) { hb_audio_t *audio; int i, already_printed; for (i = already_printed = 0; i < hb_list_count(job->list_audio);) { audio = hb_list_item(job->list_audio, i); if (audio->config.out.codec == HB_ACODEC_AUTO_PASS) { if (!already_printed) hb_autopassthru_print_settings(job); already_printed = 1; audio->config.out.codec = hb_autopassthru_get_encoder(audio->config.in.codec, job->acodec_copy_mask, job->acodec_fallback, job->mux); if (!(audio->config.out.codec & HB_ACODEC_PASS_FLAG) && !(audio->config.out.codec & HB_ACODEC_MASK)) { hb_log("Auto Passthru: passthru not possible and no valid fallback specified, dropping track %d", audio->config.out.track ); hb_list_rem(job->list_audio, audio); hb_audio_close(&audio); continue; } if (!(audio->config.out.codec & HB_ACODEC_PASS_FLAG)) { if (audio->config.out.codec == job->acodec_fallback) { hb_log("Auto Passthru: passthru not possible for track %d, using fallback", audio->config.out.track); } else { hb_log("Auto Passthru: passthru and fallback not possible for track %d, using default encoder", audio->config.out.track); } if (audio->config.out.mixdown <= 0) { audio->config.out.mixdown = hb_mixdown_get_default(audio->config.out.codec, audio->config.in.channel_layout); } else { audio->config.out.mixdown = hb_mixdown_get_best(audio->config.out.codec, audio->config.in.channel_layout, audio->config.out.mixdown); } if (audio->config.out.samplerate <= 0) audio->config.out.samplerate = audio->config.in.samplerate; audio->config.out.samplerate = hb_audio_samplerate_get_best(audio->config.out.codec, audio->config.out.samplerate, NULL); int quality_not_allowed = hb_audio_quality_get_default(audio->config.out.codec) == HB_INVALID_AUDIO_QUALITY; if (audio->config.out.bitrate > 0) { // Use best bitrate audio->config.out.bitrate = hb_audio_bitrate_get_best(audio->config.out.codec, audio->config.out.bitrate, audio->config.out.samplerate, audio->config.out.mixdown); } else if (quality_not_allowed || audio->config.out.quality != HB_INVALID_AUDIO_QUALITY) { // Use default bitrate audio->config.out.bitrate = hb_audio_bitrate_get_default(audio->config.out.codec, audio->config.out.samplerate, audio->config.out.mixdown); } else { // Use best quality audio->config.out.quality = hb_audio_quality_get_best(audio->config.out.codec, audio->config.out.quality); } if (audio->config.out.compression_level < 0) { audio->config.out.compression_level = hb_audio_compression_get_default( audio->config.out.codec); } else { audio->config.out.compression_level = hb_audio_compression_get_best(audio->config.out.codec, audio->config.out.compression_level); } } else { const hb_encoder_t *audio_encoder = NULL; while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) { if (audio_encoder->codec == audio->config.out.codec) { hb_log("Auto Passthru: using %s for track %d", audio_encoder->name, audio->config.out.track); break; } } } } /* Adjust output track number, in case we removed one. * Output tracks sadly still need to be in sequential order. * Note: out.track starts at 1, i starts at 0 */ audio->config.out.track = ++i; } } void hb_autopassthru_print_settings(hb_job_t *job) { char *mask = NULL, *tmp; const char *fallback = NULL; const hb_encoder_t *audio_encoder = NULL; while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) { if ((audio_encoder->codec & HB_ACODEC_PASS_FLAG) && (audio_encoder->codec != HB_ACODEC_AUTO_PASS) && (audio_encoder->codec & job->acodec_copy_mask)) { if (mask != NULL) { tmp = hb_strncat_dup(mask, ", ", 2); if (tmp != NULL) { free(mask); mask = tmp; } } // passthru name without " Passthru" tmp = hb_strncat_dup(mask, audio_encoder->name, strlen(audio_encoder->name) - 9); if (tmp != NULL) { free(mask); mask = tmp; } } else if ((audio_encoder->codec & HB_ACODEC_PASS_FLAG) == 0 && (audio_encoder->codec == job->acodec_fallback)) { fallback = audio_encoder->name; } } if (mask == NULL) hb_log("Auto Passthru: no codecs allowed"); else hb_log("Auto Passthru: allowed codecs are %s", mask); if (fallback == NULL) hb_log("Auto Passthru: no valid fallback specified"); else hb_log("Auto Passthru: fallback is %s", fallback); } int hb_autopassthru_get_encoder(int in_codec, int copy_mask, int fallback, int muxer) { int i = 0; const hb_encoder_t *audio_encoder = NULL; int out_codec = (copy_mask & in_codec) | HB_ACODEC_PASS_FLAG; // sanitize fallback encoder and selected passthru // note: invalid fallbacks are caught in hb_autopassthru_apply_settings while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) { if (audio_encoder->codec == out_codec) { i++; if (!(audio_encoder->muxers & muxer)) out_codec = 0; } else if (audio_encoder->codec == fallback) { i++; if (!(audio_encoder->muxers & muxer)) fallback = hb_audio_encoder_get_default(muxer); } if (i > 1) { break; } } return (out_codec & HB_ACODEC_PASS_MASK) ? out_codec : fallback; } int hb_container_get_from_name(const char *name) { if (name == NULL || *name == '\0') goto fail; int i; for (i = 0; i < hb_containers_count; i++) { if (!strcasecmp(hb_containers[i].item.name, name) || !strcasecmp(hb_containers[i].item.short_name, name)) { return hb_containers[i].item.format; } } fail: return 0; } int hb_container_get_from_extension(const char *extension) { if (extension == NULL || *extension == '\0') goto fail; int i; for (i = 0; i < hb_containers_count; i++) { if (!strcasecmp(hb_containers[i].item.default_extension, extension)) { return hb_containers[i].item.format; } } fail: return 0; } const char* hb_container_get_name(int format) { if (!(format & HB_MUX_MASK)) goto fail; const hb_container_t *container = NULL; while ((container = hb_container_get_next(container)) != NULL) { if (container->format == format) { return container->name; } } fail: return NULL; } const char* hb_container_get_short_name(int format) { if (!(format & HB_MUX_MASK)) goto fail; const hb_container_t *container = NULL; while ((container = hb_container_get_next(container)) != NULL) { if (container->format == format) { return container->short_name; } } fail: return NULL; } const char* hb_container_get_long_name(int format) { if (!(format & HB_MUX_MASK)) goto fail; const hb_container_t *container = NULL; while ((container = hb_container_get_next(container)) != NULL) { if (container->format == format) { return container->long_name; } } fail: return NULL; } const char* hb_container_get_default_extension(int format) { if (!(format & HB_MUX_MASK)) goto fail; const hb_container_t *container = NULL; while ((container = hb_container_get_next(container)) != NULL) { if (container->format == format) { return container->default_extension; } } fail: return NULL; } const char* hb_container_sanitize_name(const char *name) { return hb_container_get_name(hb_container_get_from_name(name)); } const hb_container_t* hb_container_get_next(const hb_container_t *last) { if (last == NULL) { return hb_containers_first_item; } return ((hb_container_internal_t*)last)->next; } /********************************************************************** * hb_reduce ********************************************************************** * Given a numerator (num) and a denominator (den), reduce them to an * equivalent fraction and store the result in x and y. *********************************************************************/ void hb_reduce( int *x, int *y, int num, int den ) { // find the greatest common divisor of num & den by Euclid's algorithm int n = num, d = den; while ( d ) { int t = d; d = n % d; n = t; } // at this point n is the gcd. if it's non-zero remove it from num // and den. Otherwise just return the original values. if ( n ) { *x = num / n; *y = den / n; } else { *x = num; *y = den; } } /********************************************************************** * hb_reduce64 ********************************************************************** * Given a numerator (num) and a denominator (den), reduce them to an * equivalent fraction and store the result in x and y. *********************************************************************/ void hb_reduce64( int64_t *x, int64_t *y, int64_t num, int64_t den ) { // find the greatest common divisor of num & den by Euclid's algorithm int64_t n = num, d = den; while ( d ) { int64_t t = d; d = n % d; n = t; } // at this point n is the gcd. if it's non-zero remove it from num // and den. Otherwise just return the original values. if ( n ) { num /= n; den /= n; } *x = num; *y = den; } void hb_limit_rational64( int64_t *x, int64_t *y, int64_t num, int64_t den, int64_t limit ) { hb_reduce64( &num, &den, num, den ); if ( num < limit && den < limit ) { *x = num; *y = den; return; } if ( num > den ) { double div = (double)limit / num; num = limit; den *= div; } else { double div = (double)limit / den; den = limit; num *= div; } *x = num; *y = den; } /********************************************************************** * hb_list implementation ********************************************************************** * Basic and slow, but enough for what we need *********************************************************************/ #define HB_LIST_DEFAULT_SIZE 20 struct hb_list_s { /* Pointers to items in the list */ void ** items; /* How many (void *) allocated in 'items' */ int items_alloc; /* How many valid pointers in 'items' */ int items_count; }; /********************************************************************** * hb_list_init ********************************************************************** * Allocates an empty list ready for HB_LIST_DEFAULT_SIZE items *********************************************************************/ hb_list_t * hb_list_init() { hb_list_t * l; l = calloc( sizeof( hb_list_t ), 1 ); l->items = calloc( HB_LIST_DEFAULT_SIZE * sizeof( void * ), 1 ); l->items_alloc = HB_LIST_DEFAULT_SIZE; return l; } /********************************************************************** * hb_list_count ********************************************************************** * Returns the number of items currently in the list *********************************************************************/ int hb_list_count( const hb_list_t * l ) { return l->items_count; } /********************************************************************** * hb_list_add ********************************************************************** * Adds an item at the end of the list, making it bigger if necessary. * Can safely be called with a NULL pointer to add, it will be ignored. *********************************************************************/ void hb_list_add( hb_list_t * l, void * p ) { if( !p ) { return; } if( l->items_count == l->items_alloc ) { /* We need a bigger boat */ l->items_alloc += HB_LIST_DEFAULT_SIZE; l->items = realloc( l->items, l->items_alloc * sizeof( void * ) ); } l->items[l->items_count] = p; (l->items_count)++; } /********************************************************************** * hb_list_insert ********************************************************************** * Adds an item at the specifiec position in the list, making it bigger * if necessary. * Can safely be called with a NULL pointer to add, it will be ignored. *********************************************************************/ void hb_list_insert( hb_list_t * l, int pos, void * p ) { if( !p ) { return; } if( l->items_count == l->items_alloc ) { /* We need a bigger boat */ l->items_alloc += HB_LIST_DEFAULT_SIZE; l->items = realloc( l->items, l->items_alloc * sizeof( void * ) ); } if ( l->items_count != pos ) { /* Shift all items after it sizeof( void * ) bytes earlier */ memmove( &l->items[pos+1], &l->items[pos], ( l->items_count - pos ) * sizeof( void * ) ); } l->items[pos] = p; (l->items_count)++; } /********************************************************************** * hb_list_rem ********************************************************************** * Remove an item from the list. Bad things will happen if called * with a NULL pointer or if the item is not in the list. *********************************************************************/ void hb_list_rem( hb_list_t * l, void * p ) { int i; /* Find the item in the list */ for( i = 0; i < l->items_count; i++ ) { if( l->items[i] == p ) { /* Shift all items after it sizeof( void * ) bytes earlier */ memmove( &l->items[i], &l->items[i+1], ( l->items_count - i - 1 ) * sizeof( void * ) ); (l->items_count)--; break; } } } /********************************************************************** * hb_list_item ********************************************************************** * Returns item at position i, or NULL if there are not that many * items in the list *********************************************************************/ void * hb_list_item( const hb_list_t * l, int i ) { if( i < 0 || i >= l->items_count ) { return NULL; } return l->items[i]; } /********************************************************************** * hb_list_bytes ********************************************************************** * Assuming all items are of type hb_buffer_t, returns the total * number of bytes in the list *********************************************************************/ int hb_list_bytes( hb_list_t * l ) { hb_buffer_t * buf; int ret; int i; ret = 0; for( i = 0; i < hb_list_count( l ); i++ ) { buf = hb_list_item( l, i ); ret += buf->size - buf->offset; } return ret; } /********************************************************************** * hb_list_seebytes ********************************************************************** * Assuming all items are of type hb_buffer_t, copy bytes from * the list to , keeping the list unmodified. *********************************************************************/ void hb_list_seebytes( hb_list_t * l, uint8_t * dst, int size ) { hb_buffer_t * buf; int copied; int copying; int i; for( i = 0, copied = 0; copied < size; i++ ) { buf = hb_list_item( l, i ); copying = MIN( buf->size - buf->offset, size - copied ); memcpy( &dst[copied], &buf->data[buf->offset], copying ); copied += copying; } } /********************************************************************** * hb_list_getbytes ********************************************************************** * Assuming all items are of type hb_buffer_t, copy bytes from * the list to . What's copied is removed from the list. * The variable pointed by is set to the PTS of the buffer the * first byte has been got from. * The variable pointed by is set to the position of that byte * in that buffer. *********************************************************************/ void hb_list_getbytes( hb_list_t * l, uint8_t * dst, int size, uint64_t * pts, uint64_t * pos ) { hb_buffer_t * buf; int copied; int copying; uint8_t has_pts; /* So we won't have to deal with NULL pointers */ uint64_t dummy1, dummy2; if( !pts ) pts = &dummy1; if( !pos ) pos = &dummy2; for( copied = 0, has_pts = 0; copied < size; ) { buf = hb_list_item( l, 0 ); copying = MIN( buf->size - buf->offset, size - copied ); memcpy( &dst[copied], &buf->data[buf->offset], copying ); if( !has_pts ) { *pts = buf->s.start; *pos = buf->offset; has_pts = 1; } buf->offset += copying; if( buf->offset >= buf->size ) { hb_list_rem( l, buf ); hb_buffer_close( &buf ); } copied += copying; } } /********************************************************************** * hb_list_empty ********************************************************************** * Assuming all items are of type hb_buffer_t, close them all and * close the list. *********************************************************************/ void hb_list_empty( hb_list_t ** _l ) { hb_list_t * l = *_l; hb_buffer_t * b; while( ( b = hb_list_item( l, 0 ) ) ) { hb_list_rem( l, b ); hb_buffer_close( &b ); } hb_list_close( _l ); } /********************************************************************** * hb_list_close ********************************************************************** * Free memory allocated by hb_list_init. Does NOT free contents of * items still in the list. *********************************************************************/ void hb_list_close( hb_list_t ** _l ) { hb_list_t * l = *_l; free( l->items ); free( l ); *_l = NULL; } int global_verbosity_level; //Necessary for hb_deep_log /********************************************************************** * hb_valog ********************************************************************** * If verbose mode is one, print message with timestamp. Messages * longer than 180 characters are stripped ;p *********************************************************************/ void hb_valog( hb_debug_level_t level, const char * prefix, const char * log, va_list args) { char string[362]; /* 360 chars + \n + \0 */ time_t _now; struct tm * now; if( !getenv( "HB_DEBUG" ) ) { /* We don't want to print it */ return; } if( global_verbosity_level < level ) { /* Hiding message */ return; } /* Get the time */ _now = time( NULL ); now = localtime( &_now ); if ( prefix && *prefix ) { // limit the prefix length snprintf( string, 40, "[%02d:%02d:%02d] %s ", now->tm_hour, now->tm_min, now->tm_sec, prefix ); } else { sprintf( string, "[%02d:%02d:%02d] ", now->tm_hour, now->tm_min, now->tm_sec ); } int end = strlen( string ); /* Convert the message to a string */ vsnprintf( string + end, 361 - end, log, args ); /* Add the end of line */ strcat( string, "\n" ); #ifdef SYS_MINGW wchar_t wstring[2*362]; /* 360 chars + \n + \0 */ // Convert internal utf8 to "console output code page". // // This is just bizarre windows behavior. You would expect that // printf would automatically convert a wide character string to // the current "console output code page" when using the "%ls" format // specifier. But it doesn't... so we must do it. if (!MultiByteToWideChar(CP_UTF8, 0, string, -1, wstring, sizeof(wstring))) return; if (!WideCharToMultiByte(GetConsoleOutputCP(), 0, wstring, -1, string, sizeof(string), NULL, NULL)) return; #endif /* Print it */ fprintf( stderr, "%s", string ); } /********************************************************************** * hb_log ********************************************************************** * If verbose mode is one, print message with timestamp. Messages * longer than 180 characters are stripped ;p *********************************************************************/ void hb_log( char * log, ... ) { va_list args; va_start( args, log ); hb_valog( 0, NULL, log, args ); va_end( args ); } /********************************************************************** * hb_deep_log ********************************************************************** * If verbose mode is >= level, print message with timestamp. Messages * longer than 360 characters are stripped ;p *********************************************************************/ void hb_deep_log( hb_debug_level_t level, char * log, ... ) { va_list args; va_start( args, log ); hb_valog( level, NULL, log, args ); va_end( args ); } /********************************************************************** * hb_error ********************************************************************** * Using whatever output is available display this error. *********************************************************************/ void hb_error( char * log, ... ) { char string[181]; /* 180 chars + \0 */ char rep_string[181]; static char last_string[181]; static int last_error_count = 0; static uint64_t last_series_error_time = 0; static hb_lock_t *mutex = 0; va_list args; uint64_t time_now; /* Convert the message to a string */ va_start( args, log ); vsnprintf( string, 180, log, args ); va_end( args ); if( !mutex ) { mutex = hb_lock_init(); } hb_lock( mutex ); time_now = hb_get_date(); if( strcmp( string, last_string) == 0 ) { /* * The last error and this one are the same, don't log it * just count it instead, unless it was more than one second * ago. */ last_error_count++; if( last_series_error_time + ( 1000 * 1 ) > time_now ) { hb_unlock( mutex ); return; } } /* * A new error, or the same one more than 10sec since the last one * did we have any of the same counted up? */ if( last_error_count > 0 ) { /* * Print out the last error to ensure context for the last * repeated message. */ if( error_handler ) { error_handler( last_string ); } else { hb_log( "%s", last_string ); } if( last_error_count > 1 ) { /* * Only print out the repeat message for more than 2 of the * same, since we just printed out two of them already. */ snprintf( rep_string, 180, "Last error repeated %d times", last_error_count - 1 ); if( error_handler ) { error_handler( rep_string ); } else { hb_log( "%s", rep_string ); } } last_error_count = 0; } last_series_error_time = time_now; strcpy( last_string, string ); /* * Got the error in a single string, send it off to be dispatched. */ if( error_handler ) { error_handler( string ); } else { hb_log( "%s", string ); } hb_unlock( mutex ); } void hb_register_error_handler( hb_error_handler_t * handler ) { error_handler = handler; } static void hb_update_str( char **dst, const char *src ) { if ( dst ) { free( *dst ); *dst = NULL; if ( src ) { *dst = strdup( src ); } } } /********************************************************************** * hb_title_init ********************************************************************** * *********************************************************************/ hb_title_t * hb_title_init( char * path, int index ) { hb_title_t * t; t = calloc( sizeof( hb_title_t ), 1 ); t->index = index; t->playlist = -1; t->list_audio = hb_list_init(); t->list_chapter = hb_list_init(); t->list_subtitle = hb_list_init(); t->list_attachment = hb_list_init(); t->metadata = hb_metadata_init(); strcat( t->path, path ); // default to decoding mpeg2 t->video_id = 0xE0; t->video_codec = WORK_DECAVCODECV; t->video_codec_param = AV_CODEC_ID_MPEG2VIDEO; t->angle_count = 1; t->pixel_aspect_width = 1; t->pixel_aspect_height = 1; return t; } /********************************************************************** * hb_title_close ********************************************************************** * *********************************************************************/ void hb_title_close( hb_title_t ** _t ) { hb_title_t * t = *_t; hb_audio_t * audio; hb_chapter_t * chapter; hb_subtitle_t * subtitle; hb_attachment_t * attachment; while( ( chapter = hb_list_item( t->list_chapter, 0 ) ) ) { hb_list_rem( t->list_chapter, chapter ); hb_chapter_close( &chapter ); } hb_list_close( &t->list_chapter ); while( ( audio = hb_list_item( t->list_audio, 0 ) ) ) { hb_list_rem( t->list_audio, audio ); hb_audio_close( &audio ); } hb_list_close( &t->list_audio ); while( ( subtitle = hb_list_item( t->list_subtitle, 0 ) ) ) { hb_list_rem( t->list_subtitle, subtitle ); hb_subtitle_close( &subtitle ); } hb_list_close( &t->list_subtitle ); while( ( attachment = hb_list_item( t->list_attachment, 0 ) ) ) { hb_list_rem( t->list_attachment, attachment ); hb_attachment_close( &attachment ); } hb_list_close( &t->list_attachment ); hb_metadata_close( &t->metadata ); free( t->video_codec_name ); free(t->container_name); #if defined(HB_TITLE_JOBS) hb_job_close( &t->job ); #endif free( t ); *_t = NULL; } // The mac ui expects certain fields of the job struct to be cleaned up // and others to remain untouched. // e.g. picture settings like cropping, width, height, should remain untouched. // // So only initialize job elements that we know get set up by prepareJob and // prepareJobForPreview. // // This should all get resolved in some future mac ui refactoring. static void job_reset_for_mac_ui( hb_job_t * job, hb_title_t * title ) { if ( job == NULL || title == NULL ) return; job->title = title; /* Set defaults settings */ job->chapter_start = 1; job->chapter_end = hb_list_count( title->list_chapter ); job->list_chapter = hb_chapter_list_copy( title->list_chapter ); job->vcodec = HB_VCODEC_FFMPEG_MPEG4; job->vquality = -1.0; job->vbitrate = 1000; job->pass = 0; job->vrate = title->rate; job->vrate_base = title->rate_base; job->list_audio = hb_list_init(); job->list_subtitle = hb_list_init(); job->list_filter = hb_list_init(); job->list_attachment = hb_attachment_list_copy( title->list_attachment ); job->metadata = hb_metadata_copy( title->metadata ); } static void job_setup( hb_job_t * job, hb_title_t * title ) { if ( job == NULL || title == NULL ) return; job->title = title; /* Set defaults settings */ job->chapter_start = 1; job->chapter_end = hb_list_count( title->list_chapter ); job->list_chapter = hb_chapter_list_copy( title->list_chapter ); /* Autocrop by default. Gnark gnark */ memcpy( job->crop, title->crop, 4 * sizeof( int ) ); /* Preserve a source's pixel aspect, if it's available. */ if( title->pixel_aspect_width && title->pixel_aspect_height ) { job->anamorphic.par_width = title->pixel_aspect_width; job->anamorphic.par_height = title->pixel_aspect_height; } if( title->aspect != 0 && title->aspect != 1. && !job->anamorphic.par_width && !job->anamorphic.par_height) { hb_reduce( &job->anamorphic.par_width, &job->anamorphic.par_height, (int)(title->aspect * title->height + 0.5), title->width ); } job->width = title->width - job->crop[2] - job->crop[3]; job->height = title->height - job->crop[0] - job->crop[1]; job->anamorphic.keep_display_aspect = 1; int width, height, par_width, par_height; hb_set_anamorphic_size(job, &width, &height, &par_width, &par_height); job->width = width; job->height = height; job->anamorphic.par_width = par_width; job->anamorphic.par_height = par_height; job->vcodec = HB_VCODEC_FFMPEG_MPEG4; job->vquality = -1.0; job->vbitrate = 1000; job->pass = 0; job->vrate = title->rate; job->vrate_base = title->rate_base; job->mux = HB_MUX_MP4; job->list_audio = hb_list_init(); job->list_subtitle = hb_list_init(); job->list_filter = hb_list_init(); job->list_attachment = hb_attachment_list_copy( title->list_attachment ); job->metadata = hb_metadata_copy( title->metadata ); #ifdef USE_QSV job->qsv.enc_info.is_init_done = 0; job->qsv.async_depth = AV_QSV_ASYNC_DEPTH_DEFAULT; job->qsv.decode = !!(title->video_decode_support & HB_DECODE_SUPPORT_QSV); #endif } static void job_clean( hb_job_t * job ) { if (job) { hb_chapter_t *chapter; hb_audio_t *audio; hb_subtitle_t *subtitle; hb_filter_object_t *filter; hb_attachment_t *attachment; free(job->encoder_preset); job->encoder_preset = NULL; free(job->encoder_tune); job->encoder_tune = NULL; free(job->encoder_options); job->encoder_options = NULL; free(job->encoder_profile); job->encoder_profile = NULL; free(job->encoder_level); job->encoder_level = NULL; free(job->file); job->file = NULL; // clean up chapter list while( ( chapter = hb_list_item( job->list_chapter, 0 ) ) ) { hb_list_rem( job->list_chapter, chapter ); hb_chapter_close( &chapter ); } hb_list_close( &job->list_chapter ); // clean up audio list while( ( audio = hb_list_item( job->list_audio, 0 ) ) ) { hb_list_rem( job->list_audio, audio ); hb_audio_close( &audio ); } hb_list_close( &job->list_audio ); // clean up subtitle list while( ( subtitle = hb_list_item( job->list_subtitle, 0 ) ) ) { hb_list_rem( job->list_subtitle, subtitle ); hb_subtitle_close( &subtitle ); } hb_list_close( &job->list_subtitle ); // clean up filter list while( ( filter = hb_list_item( job->list_filter, 0 ) ) ) { hb_list_rem( job->list_filter, filter ); hb_filter_close( &filter ); } hb_list_close( &job->list_filter ); // clean up attachment list while( ( attachment = hb_list_item( job->list_attachment, 0 ) ) ) { hb_list_rem( job->list_attachment, attachment ); hb_attachment_close( &attachment ); } hb_list_close( &job->list_attachment ); // clean up metadata hb_metadata_close( &job->metadata ); } } hb_title_t * hb_find_title_by_index( hb_handle_t *h, int title_index ) { hb_title_set_t *title_set = hb_get_title_set( h ); int ii; for (ii = 0; ii < hb_list_count(title_set->list_title); ii++) { hb_title_t *title = hb_list_item(title_set->list_title, ii); if (title_index == title->index) { return title; } } return NULL; } /* * Create a pristine job structure from a title * title_index is 1 based */ hb_job_t * hb_job_init_by_index( hb_handle_t * h, int title_index ) { hb_title_set_t *title_set = hb_get_title_set( h ); hb_title_t * title = hb_list_item( title_set->list_title, title_index - 1 ); return hb_job_init( title ); } hb_job_t * hb_job_init( hb_title_t * title ) { hb_job_t * job; if ( title == NULL ) return NULL; job = calloc( sizeof( hb_job_t ), 1 ); job_setup(job, title); return job; } /** * Clean up the job structure so that is is ready for setting up a new job. * Should be called by front-ends after hb_add(). */ /********************************************************************** * hb_job_reset ********************************************************************** * *********************************************************************/ void hb_job_reset( hb_job_t * job ) { if ( job ) { hb_title_t * title = job->title; job_clean(job); job_reset_for_mac_ui(job, title); } } /********************************************************************** * hb_job_close ********************************************************************** * *********************************************************************/ void hb_job_close( hb_job_t ** _j ) { if (_j && *_j) { job_clean(*_j); free( *_j ); _j = NULL; } } void hb_job_set_encoder_preset(hb_job_t *job, const char *preset) { if (job != NULL) { hb_update_str(&job->encoder_preset, preset); } } void hb_job_set_encoder_tune(hb_job_t *job, const char *tune) { if (job != NULL) { hb_update_str(&job->encoder_tune, tune); } } void hb_job_set_encoder_options(hb_job_t *job, const char *options) { if (job != NULL) { hb_update_str(&job->encoder_options, options); } } void hb_job_set_encoder_profile(hb_job_t *job, const char *profile) { if (job != NULL) { hb_update_str(&job->encoder_profile, profile); } } void hb_job_set_encoder_level(hb_job_t *job, const char *level) { if (job != NULL) { hb_update_str(&job->encoder_level, level); } } void hb_job_set_file(hb_job_t *job, const char *file) { if (job != NULL) { hb_update_str(&job->file, file); } } hb_filter_object_t * hb_filter_copy( hb_filter_object_t * filter ) { if( filter == NULL ) return NULL; hb_filter_object_t * filter_copy = malloc( sizeof( hb_filter_object_t ) ); memcpy( filter_copy, filter, sizeof( hb_filter_object_t ) ); if( filter->settings ) filter_copy->settings = strdup( filter->settings ); return filter_copy; } /********************************************************************** * hb_filter_list_copy ********************************************************************** * *********************************************************************/ hb_list_t *hb_filter_list_copy(const hb_list_t *src) { hb_list_t *list = hb_list_init(); hb_filter_object_t *filter = NULL; int i; if( src ) { for( i = 0; i < hb_list_count(src); i++ ) { if( ( filter = hb_list_item( src, i ) ) ) { hb_list_add( list, hb_filter_copy(filter) ); } } } return list; } /** * Gets a filter object with the given type * @param filter_id The type of filter to get. * @returns The requested filter object. */ hb_filter_object_t * hb_filter_init( int filter_id ) { hb_filter_object_t * filter; switch( filter_id ) { case HB_FILTER_DETELECINE: filter = &hb_filter_detelecine; break; case HB_FILTER_DECOMB: filter = &hb_filter_decomb; break; case HB_FILTER_DEINTERLACE: filter = &hb_filter_deinterlace; break; case HB_FILTER_VFR: filter = &hb_filter_vfr; break; case HB_FILTER_DEBLOCK: filter = &hb_filter_deblock; break; case HB_FILTER_DENOISE: filter = &hb_filter_denoise; break; case HB_FILTER_NLMEANS: filter = &hb_filter_nlmeans; break; case HB_FILTER_RENDER_SUB: filter = &hb_filter_render_sub; break; case HB_FILTER_CROP_SCALE: filter = &hb_filter_crop_scale; break; case HB_FILTER_ROTATE: filter = &hb_filter_rotate; break; #ifdef USE_QSV case HB_FILTER_QSV: filter = &hb_filter_qsv; break; case HB_FILTER_QSV_PRE: filter = &hb_filter_qsv_pre; break; case HB_FILTER_QSV_POST: filter = &hb_filter_qsv_post; break; #endif default: filter = NULL; break; } return hb_filter_copy( filter ); } /********************************************************************** * hb_filter_close ********************************************************************** * *********************************************************************/ void hb_filter_close( hb_filter_object_t ** _f ) { hb_filter_object_t * f = *_f; if( f->settings ) free( f->settings ); free( f ); *_f = NULL; } /********************************************************************** * hb_chapter_copy ********************************************************************** * *********************************************************************/ hb_chapter_t *hb_chapter_copy(const hb_chapter_t *src) { hb_chapter_t *chap = NULL; if ( src ) { chap = calloc( 1, sizeof(*chap) ); memcpy( chap, src, sizeof(*chap) ); if ( src->title ) { chap->title = strdup( src->title ); } } return chap; } /********************************************************************** * hb_chapter_list_copy ********************************************************************** * *********************************************************************/ hb_list_t *hb_chapter_list_copy(const hb_list_t *src) { hb_list_t *list = hb_list_init(); hb_chapter_t *chapter = NULL; int i; if( src ) { for( i = 0; i < hb_list_count(src); i++ ) { if( ( chapter = hb_list_item( src, i ) ) ) { hb_list_add( list, hb_chapter_copy(chapter) ); } } } return list; } /********************************************************************** * hb_chapter_close ********************************************************************** * *********************************************************************/ void hb_chapter_close(hb_chapter_t **chap) { if ( chap && *chap ) { free((*chap)->title); free(*chap); *chap = NULL; } } /********************************************************************** * hb_chapter_set_title ********************************************************************** * *********************************************************************/ void hb_chapter_set_title( hb_chapter_t *chapter, const char *title ) { if ( chapter ) { hb_update_str( &chapter->title, title ); } } /********************************************************************** * hb_chapter_set_title_by_index ********************************************************************** * Applies information from the given job to the official job instance. * @param job Handle to hb_job_t. * @param chapter The chapter to apply the name to (1-based). * @param titel to apply. *********************************************************************/ void hb_chapter_set_title_by_index( hb_job_t * job, int chapter_index, const char * title ) { hb_chapter_t * chapter = hb_list_item( job->list_chapter, chapter_index - 1 ); hb_chapter_set_title( chapter, title ); } /********************************************************************** * hb_audio_copy ********************************************************************** * *********************************************************************/ hb_audio_t *hb_audio_copy(const hb_audio_t *src) { hb_audio_t *audio = NULL; if( src ) { audio = calloc(1, sizeof(*audio)); memcpy(audio, src, sizeof(*audio)); if ( src->config.out.name ) { audio->config.out.name = strdup(src->config.out.name); } } return audio; } /********************************************************************** * hb_audio_list_copy ********************************************************************** * *********************************************************************/ hb_list_t *hb_audio_list_copy(const hb_list_t *src) { hb_list_t *list = hb_list_init(); hb_audio_t *audio = NULL; int i; if( src ) { for( i = 0; i < hb_list_count(src); i++ ) { if( ( audio = hb_list_item( src, i ) ) ) { hb_list_add( list, hb_audio_copy(audio) ); } } } return list; } /********************************************************************** * hb_audio_close ********************************************************************** * *********************************************************************/ void hb_audio_close( hb_audio_t **audio ) { if ( audio && *audio ) { free((*audio)->config.out.name); free(*audio); *audio = NULL; } } /********************************************************************** * hb_audio_new ********************************************************************** * *********************************************************************/ void hb_audio_config_init(hb_audio_config_t * audiocfg) { /* Set read-only paramaters to invalid values */ audiocfg->in.codec = 0; audiocfg->in.codec_param = 0; audiocfg->in.reg_desc = 0; audiocfg->in.stream_type = 0; audiocfg->in.substream_type = 0; audiocfg->in.version = 0; audiocfg->in.flags = 0; audiocfg->in.mode = 0; audiocfg->in.samplerate = -1; audiocfg->in.samples_per_frame = -1; audiocfg->in.bitrate = -1; audiocfg->in.matrix_encoding = AV_MATRIX_ENCODING_NONE; audiocfg->in.channel_layout = 0; audiocfg->in.channel_map = NULL; audiocfg->lang.description[0] = 0; audiocfg->lang.simple[0] = 0; audiocfg->lang.iso639_2[0] = 0; /* Initalize some sensible defaults */ audiocfg->in.track = audiocfg->out.track = 0; audiocfg->out.codec = hb_audio_encoder_get_default(HB_MUX_MP4); // default container audiocfg->out.samplerate = -1; audiocfg->out.samples_per_frame = -1; audiocfg->out.bitrate = -1; audiocfg->out.quality = HB_INVALID_AUDIO_QUALITY; audiocfg->out.compression_level = -1; audiocfg->out.mixdown = HB_INVALID_AMIXDOWN; 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; } /********************************************************************** * hb_audio_add ********************************************************************** * *********************************************************************/ int hb_audio_add(const hb_job_t * job, const hb_audio_config_t * audiocfg) { hb_title_t *title = job->title; hb_audio_t *audio; audio = hb_audio_copy( hb_list_item( title->list_audio, audiocfg->in.track ) ); if( audio == NULL ) { /* We fail! */ return 0; } if( (audiocfg->in.bitrate != -1) && (audiocfg->in.codec != 0xDEADBEEF) ) { /* This most likely means the client didn't call hb_audio_config_init * so bail. */ return 0; } /* Set the job's "in track" to the value passed in audiocfg. * HandBrakeCLI assumes this value is preserved in the jobs * audio list, but in.track in the title's audio list is not * required to be the same. */ audio->config.in.track = audiocfg->in.track; /* Really shouldn't ignore the passed out track, but there is currently no * way to handle duplicates or out-of-order track numbers. */ audio->config.out.track = hb_list_count(job->list_audio) + 1; audio->config.out.codec = audiocfg->out.codec; if((audiocfg->out.codec & HB_ACODEC_PASS_FLAG) && ((audiocfg->out.codec == HB_ACODEC_AUTO_PASS) || (audiocfg->out.codec & audio->config.in.codec & HB_ACODEC_PASS_MASK))) { /* Pass-through, copy from input. */ audio->config.out.samplerate = audio->config.in.samplerate; audio->config.out.bitrate = audio->config.in.bitrate; audio->config.out.mixdown = HB_AMIXDOWN_NONE; audio->config.out.dynamic_range_compression = 0; audio->config.out.gain = 0; 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 = hb_audio_dither_get_default(); } else { /* Non pass-through, use what is given. */ audio->config.out.codec &= ~HB_ACODEC_PASS_FLAG; audio->config.out.samplerate = audiocfg->out.samplerate; audio->config.out.bitrate = audiocfg->out.bitrate; audio->config.out.compression_level = audiocfg->out.compression_level; audio->config.out.quality = audiocfg->out.quality; audio->config.out.dynamic_range_compression = audiocfg->out.dynamic_range_compression; 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) { audio->config.out.name = strdup(audiocfg->out.name); } hb_list_add(job->list_audio, audio); return 1; } hb_audio_config_t * hb_list_audio_config_item(hb_list_t * list, int i) { hb_audio_t *audio = NULL; if( (audio = hb_list_item(list, i)) ) return &(audio->config); return NULL; } /********************************************************************** * hb_subtitle_copy ********************************************************************** * *********************************************************************/ hb_subtitle_t *hb_subtitle_copy(const hb_subtitle_t *src) { hb_subtitle_t *subtitle = NULL; if( src ) { subtitle = calloc(1, sizeof(*subtitle)); memcpy(subtitle, src, sizeof(*subtitle)); if ( src->extradata ) { subtitle->extradata = malloc( src->extradata_size ); memcpy( subtitle->extradata, src->extradata, src->extradata_size ); } } return subtitle; } /********************************************************************** * hb_subtitle_list_copy ********************************************************************** * *********************************************************************/ hb_list_t *hb_subtitle_list_copy(const hb_list_t *src) { hb_list_t *list = hb_list_init(); hb_subtitle_t *subtitle = NULL; int i; if( src ) { for( i = 0; i < hb_list_count(src); i++ ) { if( ( subtitle = hb_list_item( src, i ) ) ) { hb_list_add( list, hb_subtitle_copy(subtitle) ); } } } return list; } /********************************************************************** * hb_subtitle_close ********************************************************************** * *********************************************************************/ void hb_subtitle_close( hb_subtitle_t **sub ) { if ( sub && *sub ) { free ((*sub)->extradata); free(*sub); *sub = NULL; } } /********************************************************************** * hb_subtitle_add ********************************************************************** * *********************************************************************/ int hb_subtitle_add_ssa_header(hb_subtitle_t *subtitle, int w, int h) { // Free any pre-existing extradata free(subtitle->extradata); int fs = h * .066; // SRT subtitles are represented internally as SSA // Create an SSA header const char * ssa_header = "[Script Info]\r\n" "ScriptType: v4.00+\r\n" "Collisions: Normal\r\n" "PlayResX: %d\r\n" "PlayResY: %d\r\n" "Timer: 100.0\r\n" "WrapStyle: 0\r\n" "\r\n" "[V4+ Styles]\r\n" "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\r\n" "Style: Default,Arial,%d,&H00FFFFFF,&H00FFFFFF,&H000F0F0F,&H000F0F0F,0,0,0,0,100,100,0,0.00,1,2,3,2,20,20,20,0\r\n"; subtitle->extradata = (uint8_t*)hb_strdup_printf(ssa_header, w, h, fs); if (subtitle->extradata == NULL) { hb_error("hb_subtitle_add_ssa_header: malloc failed"); return 0; } subtitle->extradata_size = strlen((char*)subtitle->extradata) + 1; return 1; } int hb_subtitle_add(const hb_job_t * job, const hb_subtitle_config_t * subtitlecfg, int track) { hb_title_t *title = job->title; hb_subtitle_t *subtitle; subtitle = hb_subtitle_copy( hb_list_item( title->list_subtitle, track ) ); if( subtitle == NULL ) { /* We fail! */ return 0; } subtitle->config = *subtitlecfg; subtitle->out_track = hb_list_count(job->list_subtitle) + 1; hb_list_add(job->list_subtitle, subtitle); return 1; } int hb_srt_add( const hb_job_t * job, const hb_subtitle_config_t * subtitlecfg, const char *lang ) { hb_subtitle_t *subtitle; iso639_lang_t *language = NULL; subtitle = calloc( 1, sizeof( *subtitle ) ); if (subtitle == NULL) { hb_error("hb_srt_add: malloc failed"); return 0; } subtitle->id = (hb_list_count(job->list_subtitle) << 8) | 0xFF; subtitle->format = TEXTSUB; subtitle->source = SRTSUB; subtitle->codec = WORK_DECSRTSUB; language = lang_for_code2(lang); if (language == NULL) { hb_log("hb_srt_add: unknown language code (%s)", lang); language = lang_for_code2("und"); } strcpy(subtitle->lang, language->eng_name); strcpy(subtitle->iso639_2, language->iso639_2); subtitle->config = *subtitlecfg; hb_list_add(job->list_subtitle, subtitle); return 1; } int hb_subtitle_can_force( int source ) { return source == VOBSUB || source == PGSSUB; } int hb_subtitle_can_burn( int source ) { return source == VOBSUB || source == PGSSUB || source == SSASUB || source == SRTSUB || source == CC608SUB || source == UTF8SUB || source == TX3GSUB; } int hb_subtitle_can_pass( int source, int mux ) { switch (mux) { case HB_MUX_AV_MKV: case HB_MUX_LIBMKV: switch( source ) { case PGSSUB: case VOBSUB: case SSASUB: case SRTSUB: case UTF8SUB: case TX3GSUB: case CC608SUB: case CC708SUB: return 1; default: return 0; } break; case HB_MUX_MP4V2: if (source == VOBSUB) { return 1; } // fall through to next case... case HB_MUX_AV_MP4: switch( source ) { case VOBSUB: case SSASUB: case SRTSUB: case UTF8SUB: case TX3GSUB: case CC608SUB: case CC708SUB: return 1; default: return 0; } break; default: // Internal error. Should never get here. hb_error("internel error. Bad mux %d\n", mux); return 0; } } int hb_audio_can_apply_drc(uint32_t codec, uint32_t codec_param, int encoder) { if (encoder & HB_ACODEC_PASS_FLAG) { // can't apply DRC to passthrough audio return 0; } else if (codec & HB_ACODEC_FF_MASK) { return (codec_param == AV_CODEC_ID_AC3 || codec_param == AV_CODEC_ID_EAC3); } else if (codec == HB_ACODEC_AC3) { return 1; } else { return 0; } } /********************************************************************** * hb_metadata_init ********************************************************************** * *********************************************************************/ hb_metadata_t *hb_metadata_init() { hb_metadata_t *metadata = calloc( 1, sizeof(*metadata) ); return metadata; } /********************************************************************** * hb_metadata_copy ********************************************************************** * *********************************************************************/ hb_metadata_t *hb_metadata_copy( const hb_metadata_t *src ) { hb_metadata_t *metadata = NULL; if ( src ) { metadata = calloc( 1, sizeof(*metadata) ); if ( src->name ) { metadata->name = strdup(src->name); } if ( src->artist ) { metadata->artist = strdup(src->artist); } if ( src->album_artist ) { metadata->album_artist = strdup(src->album_artist); } if ( src->composer ) { metadata->composer = strdup(src->composer); } if ( src->release_date ) { metadata->release_date = strdup(src->release_date); } if ( src->comment ) { metadata->comment = strdup(src->comment); } if ( src->album ) { metadata->album = strdup(src->album); } if ( src->genre ) { metadata->genre = strdup(src->genre); } if ( src->description ) { metadata->description = strdup(src->description); } if ( src->long_description ) { metadata->long_description = strdup(src->long_description); } if ( src->list_coverart ) { int ii; for ( ii = 0; ii < hb_list_count( src->list_coverart ); ii++ ) { hb_coverart_t *art = hb_list_item( src->list_coverart, ii ); hb_metadata_add_coverart( metadata, art->data, art->size, art->type ); } } } return metadata; } /********************************************************************** * hb_metadata_close ********************************************************************** * *********************************************************************/ void hb_metadata_close( hb_metadata_t **_m ) { if ( _m && *_m ) { hb_metadata_t *m = *_m; hb_coverart_t *art; free( m->name ); free( m->artist ); free( m->composer ); free( m->release_date ); free( m->comment ); free( m->album ); free( m->album_artist ); free( m->genre ); free( m->description ); free( m->long_description ); if ( m->list_coverart ) { while( ( art = hb_list_item( m->list_coverart, 0 ) ) ) { hb_list_rem( m->list_coverart, art ); free( art->data ); free( art ); } hb_list_close( &m->list_coverart ); } free( m ); *_m = NULL; } } /********************************************************************** * hb_metadata_set_* ********************************************************************** * *********************************************************************/ void hb_metadata_set_name( hb_metadata_t *metadata, const char *name ) { if ( metadata ) { hb_update_str( &metadata->name, name ); } } void hb_metadata_set_artist( hb_metadata_t *metadata, const char *artist ) { if ( metadata ) { hb_update_str( &metadata->artist, artist ); } } void hb_metadata_set_composer( hb_metadata_t *metadata, const char *composer ) { if ( metadata ) { hb_update_str( &metadata->composer, composer ); } } void hb_metadata_set_release_date( hb_metadata_t *metadata, const char *release_date ) { if ( metadata ) { hb_update_str( &metadata->release_date, release_date ); } } void hb_metadata_set_comment( hb_metadata_t *metadata, const char *comment ) { if ( metadata ) { hb_update_str( &metadata->comment, comment ); } } void hb_metadata_set_genre( hb_metadata_t *metadata, const char *genre ) { if ( metadata ) { hb_update_str( &metadata->genre, genre ); } } void hb_metadata_set_album( hb_metadata_t *metadata, const char *album ) { if ( metadata ) { hb_update_str( &metadata->album, album ); } } void hb_metadata_set_album_artist( hb_metadata_t *metadata, const char *album_artist ) { if ( metadata ) { hb_update_str( &metadata->album_artist, album_artist ); } } void hb_metadata_set_description( hb_metadata_t *metadata, const char *description ) { if ( metadata ) { hb_update_str( &metadata->description, description ); } } void hb_metadata_set_long_description( hb_metadata_t *metadata, const char *long_description ) { if ( metadata ) { hb_update_str( &metadata->long_description, long_description ); } } void hb_metadata_add_coverart( hb_metadata_t *metadata, const uint8_t *data, int size, int type ) { if ( metadata ) { if ( metadata->list_coverart == NULL ) { metadata->list_coverart = hb_list_init(); } hb_coverart_t *art = calloc( 1, sizeof(hb_coverart_t) ); art->data = malloc( size ); memcpy( art->data, data, size ); art->size = size; art->type = type; hb_list_add( metadata->list_coverart, art ); } } void hb_metadata_rem_coverart( hb_metadata_t *metadata, int idx ) { if ( metadata ) { hb_coverart_t *art = hb_list_item( metadata->list_coverart, idx ); if ( art ) { hb_list_rem( metadata->list_coverart, art ); free( art->data ); free( art ); } } } char * hb_strdup_printf( const char * fmt, ... ) { int len; va_list ap; int size = 256; char * str; char * tmp; str = malloc( size ); if ( str == NULL ) return NULL; while (1) { /* Try to print in the allocated space. */ va_start( ap, fmt ); len = vsnprintf( str, size, fmt, ap ); va_end( ap ); /* If that worked, return the string. */ if ( len > -1 && len < size ) { return str; } /* Else try again with more space. */ if ( len > -1 ) /* glibc 2.1 */ size = len + 1; /* precisely what is needed */ else /* glibc 2.0 */ size *= 2; /* twice the old size */ tmp = realloc( str, size ); if ( tmp == NULL ) { free( str ); return NULL; } else str = tmp; } } char * hb_strncat_dup( const char * s1, const char * s2, size_t n ) { size_t len; char * str; len = 0; if( s1 ) len += strlen( s1 ); if( s2 ) len += MAX( strlen( s2 ), n ); if( !len ) return NULL; str = malloc( len + 1 ); if( !str ) return NULL; if( s1 ) strcpy( str, s1 ); else strcpy( str, "" ); strncat( str, s2, n ); return str; } /********************************************************************** * hb_attachment_copy ********************************************************************** * *********************************************************************/ hb_attachment_t *hb_attachment_copy(const hb_attachment_t *src) { hb_attachment_t *attachment = NULL; if( src ) { attachment = calloc(1, sizeof(*attachment)); memcpy(attachment, src, sizeof(*attachment)); if ( src->name ) { attachment->name = strdup( src->name ); } if ( src->data ) { attachment->data = malloc( src->size ); memcpy( attachment->data, src->data, src->size ); } } return attachment; } /********************************************************************** * hb_attachment_list_copy ********************************************************************** * *********************************************************************/ hb_list_t *hb_attachment_list_copy(const hb_list_t *src) { hb_list_t *list = hb_list_init(); hb_attachment_t *attachment = NULL; int i; if( src ) { for( i = 0; i < hb_list_count(src); i++ ) { if( ( attachment = hb_list_item( src, i ) ) ) { hb_list_add( list, hb_attachment_copy(attachment) ); } } } return list; } /********************************************************************** * hb_attachment_close ********************************************************************** * *********************************************************************/ void hb_attachment_close( hb_attachment_t **attachment ) { if ( attachment && *attachment ) { free((*attachment)->data); free((*attachment)->name); free(*attachment); *attachment = NULL; } } /********************************************************************** * hb_yuv2rgb ********************************************************************** * Converts a YCrCb pixel to an RGB pixel. * * This conversion is lossy (due to rounding and clamping). * * Algorithm: * http://en.wikipedia.org/w/index.php?title=YCbCr&oldid=361987695#Technical_details *********************************************************************/ int hb_yuv2rgb(int yuv) { double y, Cr, Cb; int r, g, b; y = (yuv >> 16) & 0xff; Cr = (yuv >> 8) & 0xff; Cb = (yuv ) & 0xff; r = 1.164 * (y - 16) + 1.596 * (Cr - 128); g = 1.164 * (y - 16) - 0.392 * (Cb - 128) - 0.813 * (Cr - 128); b = 1.164 * (y - 16) + 2.017 * (Cb - 128); r = (r < 0) ? 0 : r; g = (g < 0) ? 0 : g; b = (b < 0) ? 0 : b; r = (r > 255) ? 255 : r; g = (g > 255) ? 255 : g; b = (b > 255) ? 255 : b; return (r << 16) | (g << 8) | b; } /********************************************************************** * hb_rgb2yuv ********************************************************************** * Converts an RGB pixel to a YCrCb pixel. * * This conversion is lossy (due to rounding and clamping). * * Algorithm: * http://en.wikipedia.org/w/index.php?title=YCbCr&oldid=361987695#Technical_details *********************************************************************/ int hb_rgb2yuv(int rgb) { double r, g, b; int y, Cr, Cb; r = (rgb >> 16) & 0xff; g = (rgb >> 8) & 0xff; b = (rgb ) & 0xff; y = 16. + ( 0.257 * r) + (0.504 * g) + (0.098 * b); Cb = 128. + (-0.148 * r) - (0.291 * g) + (0.439 * b); Cr = 128. + ( 0.439 * r) - (0.368 * g) - (0.071 * b); y = (y < 0) ? 0 : y; Cb = (Cb < 0) ? 0 : Cb; Cr = (Cr < 0) ? 0 : Cr; y = (y > 255) ? 255 : y; Cb = (Cb > 255) ? 255 : Cb; Cr = (Cr > 255) ? 255 : Cr; return (y << 16) | (Cr << 8) | Cb; } const char * hb_subsource_name( int source ) { switch (source) { case VOBSUB: return "VOBSUB"; case SRTSUB: return "SRT"; case CC608SUB: return "CC"; case CC708SUB: return "CC"; case UTF8SUB: return "UTF-8"; case TX3GSUB: return "TX3G"; case SSASUB: return "SSA"; case PGSSUB: return "PGS"; default: return "Unknown"; } } void hb_hexdump( hb_debug_level_t level, const char * label, const uint8_t * data, int len ) { int ii; char line[80], ascii[19], *p; ascii[18] = 0; ascii[0] = '|'; ascii[17] = '|'; memset(&ascii[1], '.', 16); p = line; if( label ) hb_deep_log(level, "++++ %s ++++", label); else hb_deep_log(level, "++++++++++++"); for( ii = 0; ii < len; ii++ ) { if( ( ii & 0x0f ) == 0x0f ) { p += sprintf( p, "%02x", data[ii] ); hb_deep_log( level, " %-50s%20s", line, ascii ); memset(&ascii[1], '.', 16); p = line; } else if( ( ii & 0x07 ) == 0x07 ) { p += sprintf( p, "%02x ", data[ii] ); } else { p += sprintf( p, "%02x ", data[ii] ); } if( isgraph( data[ii] ) ) ascii[(ii & 0x0f) + 1] = data[ii]; else ascii[(ii & 0x0f) + 1] = '.'; } if( p != line ) { hb_deep_log( level, " %-50s%20s", line, ascii ); } } int hb_gui_use_hwd_flag = 0; int hb_use_dxva( hb_title_t * title ) { return ( (title->video_codec_param == AV_CODEC_ID_MPEG2VIDEO || title->video_codec_param == AV_CODEC_ID_H264 || title->video_codec_param == AV_CODEC_ID_VC1 || title->video_codec_param == AV_CODEC_ID_WMV3 || title->video_codec_param == AV_CODEC_ID_MPEG4 ) && title->opaque_priv ); } HandBrake-0.10.2/libhb/audio_resample.c0000664000175200017520000002356712463330511020267 0ustar handbrakehandbrake/* audio_resample.c * * Copyright (c) 2003-2015 HandBrake Team * This file is part of the HandBrake source code * Homepage: * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "common.h" #include "hbffmpeg.h" #include "audio_resample.h" hb_audio_resample_t* hb_audio_resample_init(enum AVSampleFormat sample_fmt, int hb_amixdown, int normalize_mix) { hb_audio_resample_t *resample = calloc(1, sizeof(hb_audio_resample_t)); if (resample == NULL) { hb_error("hb_audio_resample_init: failed to allocate resample"); goto fail; } // avresample context, initialized in hb_audio_resample_update() resample->avresample = NULL; // we don't support planar output yet if (av_sample_fmt_is_planar(sample_fmt)) { hb_error("hb_audio_resample_init: planar output not supported ('%s')", av_get_sample_fmt_name(sample_fmt)); goto fail; } // convert mixdown to channel_layout/matrix_encoding combo int matrix_encoding; uint64_t channel_layout = hb_ff_mixdown_xlat(hb_amixdown, &matrix_encoding); /* * When downmixing, Dual Mono to Mono is a special case: * the audio must remain 2-channel until all conversions are done. */ if (hb_amixdown == HB_AMIXDOWN_LEFT || hb_amixdown == HB_AMIXDOWN_RIGHT) { channel_layout = AV_CH_LAYOUT_STEREO; resample->dual_mono_downmix = 1; resample->dual_mono_right_only = (hb_amixdown == HB_AMIXDOWN_RIGHT); } else { resample->dual_mono_downmix = 0; } // requested output channel_layout, sample_fmt resample->out.channels = av_get_channel_layout_nb_channels(channel_layout); resample->out.channel_layout = channel_layout; resample->out.matrix_encoding = matrix_encoding; resample->out.normalize_mix_level = normalize_mix; resample->out.sample_fmt = sample_fmt; resample->out.sample_size = av_get_bytes_per_sample(sample_fmt); // set default input characteristics resample->in.sample_fmt = resample->out.sample_fmt; resample->in.channel_layout = resample->out.channel_layout; resample->in.lfe_mix_level = HB_MIXLEV_ZERO; resample->in.center_mix_level = HB_MIXLEV_DEFAULT; resample->in.surround_mix_level = HB_MIXLEV_DEFAULT; // by default, no conversion needed resample->resample_needed = 0; return resample; fail: hb_audio_resample_free(resample); return NULL; } void hb_audio_resample_set_channel_layout(hb_audio_resample_t *resample, uint64_t channel_layout) { if (resample != NULL) { if (channel_layout == AV_CH_LAYOUT_STEREO_DOWNMIX) { // Dolby Surround is Stereo when it comes to remixing channel_layout = AV_CH_LAYOUT_STEREO; } resample->in.channel_layout = channel_layout; } } void hb_audio_resample_set_mix_levels(hb_audio_resample_t *resample, double surround_mix_level, double center_mix_level, double lfe_mix_level) { if (resample != NULL) { resample->in.lfe_mix_level = lfe_mix_level; resample->in.center_mix_level = center_mix_level; resample->in.surround_mix_level = surround_mix_level; } } void hb_audio_resample_set_sample_fmt(hb_audio_resample_t *resample, enum AVSampleFormat sample_fmt) { if (resample != NULL) { resample->in.sample_fmt = sample_fmt; } } int hb_audio_resample_update(hb_audio_resample_t *resample) { if (resample == NULL) { hb_error("hb_audio_resample_update: resample is NULL"); return 1; } int ret, resample_changed; resample->resample_needed = (resample->out.sample_fmt != resample->in.sample_fmt || resample->out.channel_layout != resample->in.channel_layout); resample_changed = (resample->resample_needed && (resample->resample.sample_fmt != resample->in.sample_fmt || resample->resample.channel_layout != resample->in.channel_layout || resample->resample.lfe_mix_level != resample->in.lfe_mix_level || resample->resample.center_mix_level != resample->in.center_mix_level || resample->resample.surround_mix_level != resample->in.surround_mix_level)); if (resample_changed || (resample->resample_needed && resample->avresample == NULL)) { if (resample->avresample == NULL) { resample->avresample = avresample_alloc_context(); if (resample->avresample == NULL) { hb_error("hb_audio_resample_update: avresample_alloc_context() failed"); return 1; } av_opt_set_int(resample->avresample, "out_sample_fmt", resample->out.sample_fmt, 0); av_opt_set_int(resample->avresample, "out_channel_layout", resample->out.channel_layout, 0); av_opt_set_int(resample->avresample, "matrix_encoding", resample->out.matrix_encoding, 0); av_opt_set_int(resample->avresample, "normalize_mix_level", resample->out.normalize_mix_level, 0); } else if (resample_changed) { avresample_close(resample->avresample); } av_opt_set_int(resample->avresample, "in_sample_fmt", resample->in.sample_fmt, 0); av_opt_set_int(resample->avresample, "in_channel_layout", resample->in.channel_layout, 0); av_opt_set_double(resample->avresample, "lfe_mix_level", resample->in.lfe_mix_level, 0); av_opt_set_double(resample->avresample, "center_mix_level", resample->in.center_mix_level, 0); av_opt_set_double(resample->avresample, "surround_mix_level", resample->in.surround_mix_level, 0); if ((ret = avresample_open(resample->avresample))) { char err_desc[64]; av_strerror(ret, err_desc, 63); hb_error("hb_audio_resample_update: avresample_open() failed (%s)", err_desc); // avresample won't open, start over avresample_free(&resample->avresample); return ret; } resample->resample.sample_fmt = resample->in.sample_fmt; resample->resample.channel_layout = resample->in.channel_layout; resample->resample.channels = av_get_channel_layout_nb_channels(resample->in.channel_layout); resample->resample.lfe_mix_level = resample->in.lfe_mix_level; resample->resample.center_mix_level = resample->in.center_mix_level; resample->resample.surround_mix_level = resample->in.surround_mix_level; } return 0; } void hb_audio_resample_free(hb_audio_resample_t *resample) { if (resample != NULL) { if (resample->avresample != NULL) { avresample_free(&resample->avresample); } free(resample); } } hb_buffer_t* hb_audio_resample(hb_audio_resample_t *resample, uint8_t **samples, int nsamples) { if (resample == NULL) { hb_error("hb_audio_resample: resample is NULL"); return NULL; } if (resample->resample_needed && resample->avresample == NULL) { hb_error("hb_audio_resample: resample needed but libavresample context " "is NULL"); return NULL; } hb_buffer_t *out; int out_size, out_samples; if (resample->resample_needed) { int in_linesize, out_linesize; // set in/out linesize and out_size av_samples_get_buffer_size(&in_linesize, resample->resample.channels, nsamples, resample->resample.sample_fmt, 0); out_size = av_samples_get_buffer_size(&out_linesize, resample->out.channels, nsamples, resample->out.sample_fmt, 0); out = hb_buffer_init(out_size); out_samples = avresample_convert(resample->avresample, &out->data, out_linesize, nsamples, samples, in_linesize, nsamples); if (out_samples <= 0) { if (out_samples < 0) hb_log("hb_audio_resample: avresample_convert() failed"); // don't send empty buffers downstream (EOF) hb_buffer_close(&out); return NULL; } out->size = (out_samples * resample->out.sample_size * resample->out.channels); } else { out_samples = nsamples; out_size = (out_samples * resample->out.sample_size * resample->out.channels); out = hb_buffer_init(out_size); memcpy(out->data, samples[0], out_size); } /* * Dual Mono to Mono. * * Copy all left or right samples to the first half of the buffer and halve * the buffer size. */ if (resample->dual_mono_downmix) { int ii, jj = !!resample->dual_mono_right_only; int sample_size = resample->out.sample_size; uint8_t *audio_samples = out->data; for (ii = 0; ii < out_samples; ii++) { memcpy(audio_samples + (ii * sample_size), audio_samples + (jj * sample_size), sample_size); jj += 2; } out->size = out_samples * sample_size; } return out; } HandBrake-0.10.2/libhb/encvorbis.c0000664000175200017520000002036412463330511017260 0ustar handbrakehandbrake/* encvorbis.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "audio_remap.h" #include "vorbis/vorbisenc.h" #define OGGVORBIS_FRAME_SIZE 1024 int encvorbisInit( hb_work_object_t *, hb_job_t * ); int encvorbisWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); void encvorbisClose( hb_work_object_t * ); hb_work_object_t hb_encvorbis = { WORK_ENCVORBIS, "Vorbis encoder (libvorbis)", encvorbisInit, encvorbisWork, encvorbisClose }; struct hb_work_private_s { uint8_t *buf; hb_job_t *job; hb_list_t *list; vorbis_dsp_state vd; vorbis_comment vc; vorbis_block vb; vorbis_info vi; unsigned input_samples; uint64_t pts; int64_t prev_blocksize; int out_discrete_channels; int remap_table[8]; }; int encvorbisInit(hb_work_object_t *w, hb_job_t *job) { hb_work_private_t *pv = calloc(1, sizeof(hb_work_private_t)); hb_audio_t *audio = w->audio; w->private_data = pv; pv->job = job; int i; ogg_packet header[3]; hb_log("encvorbis: opening libvorbis"); /* init */ for (i = 0; i < 3; i++) { // Zero vorbis headers so that we don't crash in mk_laceXiph // when vorbis_encode_setup_managed fails. memset(w->config->vorbis.headers[i], 0, sizeof(ogg_packet)); } vorbis_info_init(&pv->vi); pv->out_discrete_channels = hb_mixdown_get_discrete_channel_count(audio->config.out.mixdown); if (audio->config.out.bitrate > 0) { if (vorbis_encode_setup_managed(&pv->vi, pv->out_discrete_channels, audio->config.out.samplerate, -1, audio->config.out.bitrate * 1000, -1)) { hb_error("encvorbis: vorbis_encode_setup_managed() failed"); *job->done_error = HB_ERROR_INIT; *job->die = 1; return -1; } } else if (audio->config.out.quality != HB_INVALID_AUDIO_QUALITY) { // map VBR quality to Vorbis API (divide by 10) if (vorbis_encode_setup_vbr(&pv->vi, pv->out_discrete_channels, audio->config.out.samplerate, audio->config.out.quality / 10)) { hb_error("encvorbis: vorbis_encode_setup_vbr() failed"); *job->done_error = HB_ERROR_INIT; *job->die = 1; return -1; } } if (vorbis_encode_ctl(&pv->vi, OV_ECTL_RATEMANAGE2_SET, NULL) || vorbis_encode_setup_init(&pv->vi)) { hb_error("encvorbis: vorbis_encode_ctl(ratemanage2_set) OR vorbis_encode_setup_init() failed"); *job->done_error = HB_ERROR_INIT; *job->die = 1; return -1; } /* add a comment */ vorbis_comment_init(&pv->vc); vorbis_comment_add_tag(&pv->vc, "Encoder", "HandBrake"); vorbis_comment_add_tag(&pv->vc, "LANGUAGE", w->config->vorbis.language); /* set up the analysis state and auxiliary encoding storage */ vorbis_analysis_init(&pv->vd, &pv->vi); vorbis_block_init(&pv->vd, &pv->vb); /* get the 3 headers */ vorbis_analysis_headerout(&pv->vd, &pv->vc, &header[0], &header[1], &header[2]); ogg_packet *pheader; for (i = 0; i < 3; i++) { pheader = (ogg_packet*)w->config->theora.headers[i]; memcpy(pheader, &header[i], sizeof(ogg_packet)); pheader->packet = w->config->theora.headers[i] + sizeof(ogg_packet); memcpy(pheader->packet, header[i].packet, header[i].bytes ); } pv->input_samples = pv->out_discrete_channels * OGGVORBIS_FRAME_SIZE; audio->config.out.samples_per_frame = OGGVORBIS_FRAME_SIZE; pv->buf = malloc(pv->input_samples * sizeof(float)); pv->list = hb_list_init(); // channel remapping uint64_t layout = hb_ff_mixdown_xlat(audio->config.out.mixdown, NULL); hb_audio_remap_build_table(&hb_vorbis_chan_map, audio->config.in.channel_map, layout, pv->remap_table); return 0; } /*********************************************************************** * Close *********************************************************************** * **********************************************************************/ void encvorbisClose(hb_work_object_t * w) { hb_work_private_t *pv = w->private_data; vorbis_comment_clear(&pv->vc); vorbis_block_clear(&pv->vb); vorbis_info_clear(&pv->vi); vorbis_dsp_clear(&pv->vd); if (pv->list) { hb_list_empty(&pv->list); } free(pv->buf); free(pv); w->private_data = NULL; } /*********************************************************************** * Flush *********************************************************************** * **********************************************************************/ static hb_buffer_t * Flush( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; hb_buffer_t * buf; int64_t blocksize = 0; if( vorbis_analysis_blockout( &pv->vd, &pv->vb ) == 1 ) { ogg_packet op; vorbis_analysis( &pv->vb, NULL ); vorbis_bitrate_addblock( &pv->vb ); if( vorbis_bitrate_flushpacket( &pv->vd, &op ) ) { buf = hb_buffer_init( op.bytes ); memcpy( buf->data, op.packet, op.bytes ); blocksize = vorbis_packet_blocksize(&pv->vi, &op); buf->s.type = AUDIO_BUF; buf->s.frametype = HB_FRAME_AUDIO; buf->s.start = (int64_t)(vorbis_granule_time(&pv->vd, op.granulepos) * 90000); buf->s.stop = (int64_t)(vorbis_granule_time(&pv->vd, (pv->prev_blocksize + blocksize)/4 + op.granulepos) * 90000); buf->s.duration = buf->s.stop - buf->s.start; /* The stop time isn't accurate for the first ~3 packets, as the actual blocksize depends on the previous _and_ current packets. */ pv->prev_blocksize = blocksize; return buf; } } return NULL; } /*********************************************************************** * Encode *********************************************************************** * **********************************************************************/ static hb_buffer_t* Encode(hb_work_object_t *w) { hb_work_private_t *pv = w->private_data; hb_buffer_t *buf; float **buffer; int i, j; /* Try to extract more data */ if ((buf = Flush(w)) != NULL) { return buf; } /* Check if we need more data */ if (hb_list_bytes(pv->list) < pv->input_samples * sizeof(float)) { return NULL; } /* Process more samples */ hb_list_getbytes(pv->list, pv->buf, pv->input_samples * sizeof(float), &pv->pts, NULL); buffer = vorbis_analysis_buffer(&pv->vd, OGGVORBIS_FRAME_SIZE); for (i = 0; i < OGGVORBIS_FRAME_SIZE; i++) { for (j = 0; j < pv->out_discrete_channels; j++) { buffer[j][i] = ((float*)pv->buf)[(pv->out_discrete_channels * i + pv->remap_table[j])]; } } vorbis_analysis_wrote(&pv->vd, OGGVORBIS_FRAME_SIZE); /* Try to extract again */ return Flush(w); } /*********************************************************************** * Work *********************************************************************** * **********************************************************************/ int encvorbisWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_buffer_t * buf; if ( (*buf_in)->size <= 0 ) { /* EOF on input - send it downstream & say we're done */ *buf_out = *buf_in; *buf_in = NULL; return HB_WORK_DONE; } hb_list_add( pv->list, *buf_in ); *buf_in = NULL; *buf_out = buf = Encode( w ); while( buf ) { buf->next = Encode( w ); buf = buf->next; } return HB_WORK_OK; } HandBrake-0.10.2/libhb/nlmeans.c0000664000175200017520000011622712403636035016733 0ustar handbrakehandbrake/* nlmeans.c Copyright (c) 2013 Dirk Farin Copyright (c) 2003-2014 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ /* Usage * * Parameters: * lumaY_strength : lumaY_origin_tune : lumaY_patch_size : lumaY_range : lumaY_frames : lumaY_prefilter : * chromaB_strength : chromaB_origin_tune : chromaB_patch_size : chromaB_range : chromaB_frames : chromaB_prefilter : * chromaR_strength : chromaR_origin_tune : chromaR_patch_size : chromaR_range : chromaR_frames : chromaR_prefilter * * Defaults: * 8:1:7:3:2:0 for each channel (equivalent to 8:1:7:3:2:0:8:1:7:3:2:0:8:1:7:3:2:0) * * Parameters cascade, e.g. 6:0.8:7:3:3:0:4:1 sets: * strength 6, origin tune 0.8 for luma * patch size 7, range 3, frames 3, prefilter 0 for all channels * strength 4, origin tune 1 for both chroma channels * * Strength is relative and must be adjusted; ALL parameters affect overall strength. * Lower origin tune improves results for noisier input or animation (film 0.5-1, animation 0.15-0.5). * Large patch size (>9) may greatly reduce quality by clobbering detail. * Larger search range increases quality; however, computation time increases exponentially. * Large number of frames (film >3, animation >6) may cause temporal smearing. * Prefiltering can potentially improve weight decisions, yielding better results for difficult sources. * * Prefilter enum combos: * 1: Mean 3x3 * 2: Mean 5x5 * 3: Mean 5x5 (overrides Mean 3x3) * 257: Mean 3x3 reduced by 25% * 258: Mean 5x5 reduced by 25% * 513: Mean 3x3 reduced by 50% * 514: Mean 5x5 reduced by 50% * 769: Mean 3x3 reduced by 75% * 770: Mean 5x5 reduced by 75% * 1025: Mean 3x3 plus edge boost (restores lost edge detail) * 1026: Mean 5x5 plus edge boost * 1281: Mean 3x3 reduced by 25% plus edge boost * etc... * 2049: Mean 3x3 passthru (NL-means off, prefilter is the output) * etc... * 3329: Mean 3x3 reduced by 25% plus edge boost, passthru * etc... */ #include "hb.h" #include "hbffmpeg.h" #include "taskset.h" #define NLMEANS_STRENGTH_LUMA_DEFAULT 8 #define NLMEANS_STRENGTH_CHROMA_DEFAULT 8 #define NLMEANS_ORIGIN_TUNE_LUMA_DEFAULT 1 #define NLMEANS_ORIGIN_TUNE_CHROMA_DEFAULT 1 #define NLMEANS_PATCH_SIZE_LUMA_DEFAULT 7 #define NLMEANS_PATCH_SIZE_CHROMA_DEFAULT 7 #define NLMEANS_RANGE_LUMA_DEFAULT 3 #define NLMEANS_RANGE_CHROMA_DEFAULT 3 #define NLMEANS_FRAMES_LUMA_DEFAULT 2 #define NLMEANS_FRAMES_CHROMA_DEFAULT 2 #define NLMEANS_PREFILTER_LUMA_DEFAULT 0 #define NLMEANS_PREFILTER_CHROMA_DEFAULT 0 #define NLMEANS_PREFILTER_MODE_MEAN3X3 1 #define NLMEANS_PREFILTER_MODE_MEAN5X5 2 #define NLMEANS_PREFILTER_MODE_MEDIAN3X3 4 #define NLMEANS_PREFILTER_MODE_MEDIAN5X5 8 #define NLMEANS_PREFILTER_MODE_RESERVED16 16 // Reserved #define NLMEANS_PREFILTER_MODE_RESERVED32 32 // Reserved #define NLMEANS_PREFILTER_MODE_RESERVED64 64 // Reserved #define NLMEANS_PREFILTER_MODE_RESERVED128 128 // Reserved #define NLMEANS_PREFILTER_MODE_REDUCE25 256 #define NLMEANS_PREFILTER_MODE_REDUCE50 512 #define NLMEANS_PREFILTER_MODE_EDGEBOOST 1024 #define NLMEANS_PREFILTER_MODE_PASSTHRU 2048 #define NLMEANS_SORT(a,b) { if (a > b) NLMEANS_SWAP(a, b); } #define NLMEANS_SWAP(a,b) { a = (a ^ b); b = (a ^ b); a = (b ^ a); } #define NLMEANS_FRAMES_MAX 32 #define NLMEANS_EXPSIZE 128 typedef struct { uint8_t *mem; uint8_t *mem_pre; uint8_t *image; uint8_t *image_pre; int w; int h; int border; hb_lock_t *mutex; int prefiltered; } BorderedPlane; typedef struct { int width; int height; int fmt; BorderedPlane plane[3]; hb_buffer_settings_t s; } Frame; struct PixelSum { float weight_sum; float pixel_sum; }; typedef struct { hb_filter_private_t *pv; int segment; hb_buffer_t *out; } nlmeans_thread_arg_t; struct hb_filter_private_s { double strength[3]; // averaging weight decay, larger produces smoother output double origin_tune[3]; // weight tuning for origin patch, 0.00..1.00 int patch_size[3]; // pixel context region width (must be odd) int range[3]; // spatial search window width (must be odd) int nframes[3]; // temporal search depth in frames int prefilter[3]; // prefilter mode, can improve weight analysis Frame *frame; int next_frame; int max_frames; taskset_t taskset; int thread_count; nlmeans_thread_arg_t **thread_data; }; static int nlmeans_init(hb_filter_object_t *filter, hb_filter_init_t *init); static int nlmeans_work(hb_filter_object_t *filter, hb_buffer_t **buf_in, hb_buffer_t **buf_out); static void nlmeans_close(hb_filter_object_t *filter); static void nlmeans_filter_thread(void *thread_args_v); hb_filter_object_t hb_filter_nlmeans = { .id = HB_FILTER_NLMEANS, .enforce_order = 1, .name = "Denoise (nlmeans)", .settings = NULL, .init = nlmeans_init, .work = nlmeans_work, .close = nlmeans_close, }; static void nlmeans_border(uint8_t *src, int w, int h, int border) { int bw = w + 2 * border; uint8_t *image = src + border + bw * border; // Create faux borders using edge pixels for (int y = 0; y < h; y++) { for (int x = 0; x < border; x++) { *(image + y*bw - x - 1) = *(image + y*bw + x); *(image + y*bw + x + w) = *(image + y*bw - x + (w-1)); } } for (int y = 0; y < border; y++) { memcpy(image - border - (y+1)*bw, image - border + y*bw, bw); memcpy(image - border + (y+h)*bw, image - border + (h-y-1)*bw, bw); } } static void nlmeans_deborder(BorderedPlane *src, uint8_t *dst, int w, int s, int h) { int bw = src->w + 2 * src->border; uint8_t *image = src->mem + src->border + bw * src->border; int width = w; if (src->w < width) width = src->w; // Copy main image for (int y = 0; y < h; y++) { memcpy(dst + y * s, image + y * bw, width); } } static void nlmeans_alloc(uint8_t *src, int src_w, int src_s, int src_h, BorderedPlane *dst, int border) { int bw = src_w + 2 * border; int bh = src_h + 2 * border; uint8_t *mem = malloc(bw * bh * sizeof(uint8_t)); uint8_t *image = mem + border + bw * border; // Copy main image for (int y = 0; y < src_h; y++) { memcpy(image + y * bw, src + y * src_s, src_w); } dst->mem = mem; dst->image = image; dst->w = src_w; dst->h = src_h; dst->border = border; nlmeans_border(dst->mem, dst->w, dst->h, dst->border); dst->mem_pre = dst->mem; dst->image_pre = dst->image; } static void nlmeans_filter_mean(uint8_t *src, uint8_t *dst, int w, int h, int border, int size) { // Mean filter int bw = w + 2 * border; int offset_min = -((size - 1) /2); int offset_max = (size + 1) /2; uint16_t pixel_sum; double pixel_weight = 1.0 / (size * size); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { pixel_sum = 0; for (int k = offset_min; k < offset_max; k++) { for (int j = offset_min; j < offset_max; j++) { pixel_sum = pixel_sum + *(src + bw*(y+j) + (x+k)); } } *(dst + bw*y + x) = (uint8_t)(pixel_sum * pixel_weight); } } } static uint8_t nlmeans_filter_median_opt(uint8_t *pixels, int size) { // Optimized sorting networks if (size == 3) { /* opt_med9() via Nicolas Devillard * http://ndevilla.free.fr/median/median.pdf */ NLMEANS_SORT(pixels[1], pixels[2]); NLMEANS_SORT(pixels[4], pixels[5]); NLMEANS_SORT(pixels[7], pixels[8]); NLMEANS_SORT(pixels[0], pixels[1]); NLMEANS_SORT(pixels[3], pixels[4]); NLMEANS_SORT(pixels[6], pixels[7]); NLMEANS_SORT(pixels[1], pixels[2]); NLMEANS_SORT(pixels[4], pixels[5]); NLMEANS_SORT(pixels[7], pixels[8]); NLMEANS_SORT(pixels[0], pixels[3]); NLMEANS_SORT(pixels[5], pixels[8]); NLMEANS_SORT(pixels[4], pixels[7]); NLMEANS_SORT(pixels[3], pixels[6]); NLMEANS_SORT(pixels[1], pixels[4]); NLMEANS_SORT(pixels[2], pixels[5]); NLMEANS_SORT(pixels[4], pixels[7]); NLMEANS_SORT(pixels[4], pixels[2]); NLMEANS_SORT(pixels[6], pixels[4]); NLMEANS_SORT(pixels[4], pixels[2]); return pixels[4]; } else if (size == 5) { /* opt_med25() via Nicolas Devillard * http://ndevilla.free.fr/median/median.pdf */ NLMEANS_SORT(pixels[0], pixels[1]); NLMEANS_SORT(pixels[3], pixels[4]); NLMEANS_SORT(pixels[2], pixels[4]); NLMEANS_SORT(pixels[2], pixels[3]); NLMEANS_SORT(pixels[6], pixels[7]); NLMEANS_SORT(pixels[5], pixels[7]); NLMEANS_SORT(pixels[5], pixels[6]); NLMEANS_SORT(pixels[9], pixels[10]); NLMEANS_SORT(pixels[8], pixels[10]); NLMEANS_SORT(pixels[8], pixels[9]); NLMEANS_SORT(pixels[12], pixels[13]); NLMEANS_SORT(pixels[11], pixels[13]); NLMEANS_SORT(pixels[11], pixels[12]); NLMEANS_SORT(pixels[15], pixels[16]); NLMEANS_SORT(pixels[14], pixels[16]); NLMEANS_SORT(pixels[14], pixels[15]); NLMEANS_SORT(pixels[18], pixels[19]); NLMEANS_SORT(pixels[17], pixels[19]); NLMEANS_SORT(pixels[17], pixels[18]); NLMEANS_SORT(pixels[21], pixels[22]); NLMEANS_SORT(pixels[20], pixels[22]); NLMEANS_SORT(pixels[20], pixels[21]); NLMEANS_SORT(pixels[23], pixels[24]); NLMEANS_SORT(pixels[2], pixels[5]); NLMEANS_SORT(pixels[3], pixels[6]); NLMEANS_SORT(pixels[0], pixels[6]); NLMEANS_SORT(pixels[0], pixels[3]); NLMEANS_SORT(pixels[4], pixels[7]); NLMEANS_SORT(pixels[1], pixels[7]); NLMEANS_SORT(pixels[1], pixels[4]); NLMEANS_SORT(pixels[11], pixels[14]); NLMEANS_SORT(pixels[8], pixels[14]); NLMEANS_SORT(pixels[8], pixels[11]); NLMEANS_SORT(pixels[12], pixels[15]); NLMEANS_SORT(pixels[9], pixels[15]); NLMEANS_SORT(pixels[9], pixels[12]); NLMEANS_SORT(pixels[13], pixels[16]); NLMEANS_SORT(pixels[10], pixels[16]); NLMEANS_SORT(pixels[10], pixels[13]); NLMEANS_SORT(pixels[20], pixels[23]); NLMEANS_SORT(pixels[17], pixels[23]); NLMEANS_SORT(pixels[17], pixels[20]); NLMEANS_SORT(pixels[21], pixels[24]); NLMEANS_SORT(pixels[18], pixels[24]); NLMEANS_SORT(pixels[18], pixels[21]); NLMEANS_SORT(pixels[19], pixels[22]); NLMEANS_SORT(pixels[8], pixels[17]); NLMEANS_SORT(pixels[9], pixels[18]); NLMEANS_SORT(pixels[0], pixels[18]); NLMEANS_SORT(pixels[0], pixels[9]); NLMEANS_SORT(pixels[10], pixels[19]); NLMEANS_SORT(pixels[1], pixels[19]); NLMEANS_SORT(pixels[1], pixels[10]); NLMEANS_SORT(pixels[11], pixels[20]); NLMEANS_SORT(pixels[2], pixels[20]); NLMEANS_SORT(pixels[2], pixels[11]); NLMEANS_SORT(pixels[12], pixels[21]); NLMEANS_SORT(pixels[3], pixels[21]); NLMEANS_SORT(pixels[3], pixels[12]); NLMEANS_SORT(pixels[13], pixels[22]); NLMEANS_SORT(pixels[4], pixels[22]); NLMEANS_SORT(pixels[4], pixels[13]); NLMEANS_SORT(pixels[14], pixels[23]); NLMEANS_SORT(pixels[5], pixels[23]); NLMEANS_SORT(pixels[5], pixels[14]); NLMEANS_SORT(pixels[15], pixels[24]); NLMEANS_SORT(pixels[6], pixels[24]); NLMEANS_SORT(pixels[6], pixels[15]); NLMEANS_SORT(pixels[7], pixels[16]); NLMEANS_SORT(pixels[7], pixels[19]); NLMEANS_SORT(pixels[13], pixels[21]); NLMEANS_SORT(pixels[15], pixels[23]); NLMEANS_SORT(pixels[7], pixels[13]); NLMEANS_SORT(pixels[7], pixels[15]); NLMEANS_SORT(pixels[1], pixels[9]); NLMEANS_SORT(pixels[3], pixels[11]); NLMEANS_SORT(pixels[5], pixels[17]); NLMEANS_SORT(pixels[11], pixels[17]); NLMEANS_SORT(pixels[9], pixels[17]); NLMEANS_SORT(pixels[4], pixels[10]); NLMEANS_SORT(pixels[6], pixels[12]); NLMEANS_SORT(pixels[7], pixels[14]); NLMEANS_SORT(pixels[4], pixels[6]); NLMEANS_SORT(pixels[4], pixels[7]); NLMEANS_SORT(pixels[12], pixels[14]); NLMEANS_SORT(pixels[10], pixels[14]); NLMEANS_SORT(pixels[6], pixels[7]); NLMEANS_SORT(pixels[10], pixels[12]); NLMEANS_SORT(pixels[6], pixels[10]); NLMEANS_SORT(pixels[6], pixels[17]); NLMEANS_SORT(pixels[12], pixels[17]); NLMEANS_SORT(pixels[7], pixels[17]); NLMEANS_SORT(pixels[7], pixels[10]); NLMEANS_SORT(pixels[12], pixels[18]); NLMEANS_SORT(pixels[7], pixels[12]); NLMEANS_SORT(pixels[10], pixels[18]); NLMEANS_SORT(pixels[12], pixels[20]); NLMEANS_SORT(pixels[10], pixels[20]); NLMEANS_SORT(pixels[10], pixels[12]); return pixels[12]; } // Network for size not implemented return pixels[(int)((size * size)/2)]; } static void nlmeans_filter_median(uint8_t *src, uint8_t *dst, int w, int h, int border, int size) { // Median filter int bw = w + 2 * border; int offset_min = -((size - 1) /2); int offset_max = (size + 1) /2; int index; uint8_t pixels[size * size]; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { index = 0; for (int k = offset_min; k < offset_max; k++) { for (int j = offset_min; j < offset_max; j++) { pixels[index] = *(src + bw*(y+j) + (x+k)); index++; } } *(dst + bw*y + x) = nlmeans_filter_median_opt(pixels, size); } } } static void nlmeans_filter_edgeboost(uint8_t *src, uint8_t *dst, int w, int h, int border) { int bw = w + 2 * border; int bh = h + 2 * border; // Custom kernel int kernel_size = 3; int kernel[3][3] = {{-31, 0, 31}, {-44, 0, 44}, {-31, 0, 31}}; double kernel_coef = 1.0 / 126.42; // Detect edges int offset_min = -((kernel_size - 1) /2); int offset_max = (kernel_size + 1) /2; uint16_t pixel1; uint16_t pixel2; uint8_t *mask_mem = calloc(bw * bh, sizeof(uint8_t)); uint8_t *mask = mask_mem + border + bw * border; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { pixel1 = 0; pixel2 = 0; for (int k = offset_min; k < offset_max; k++) { for (int j = offset_min; j < offset_max; j++) { pixel1 += kernel[j+1][k+1] * *(src + bw*(y+j) + (x+k)); pixel2 += kernel[k+1][j+1] * *(src + bw*(y+j) + (x+k)); } } pixel1 = pixel1 > 0 ? pixel1 : -pixel1; pixel2 = pixel2 > 0 ? pixel2 : -pixel2; pixel1 = (uint16_t)(((double)pixel1 * kernel_coef) + 128); pixel2 = (uint16_t)(((double)pixel2 * kernel_coef) + 128); *(mask + bw*y + x) = (uint8_t)(pixel1 + pixel2); if (*(mask + bw*y + x) > 160) { *(mask + bw*y + x) = 235; } else if (*(mask + bw*y + x) > 16) { *(mask + bw*y + x) = 128; } else { *(mask + bw*y + x) = 16; } } } // Post-process and output int pixels; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { if (*(mask + bw*y + x) > 16) { // Count nearby edge pixels pixels = 0; for (int k = offset_min; k < offset_max; k++) { for (int j = offset_min; j < offset_max; j++) { if (*(mask + bw*(y+j) + (x+k)) > 16) { pixels++; } } } // Remove false positive if (pixels < 3) { *(mask + bw*y + x) = 16; } // Filter output if (*(mask + bw*y + x) > 16) { if (*(mask + bw*y + x) == 235) { *(dst + bw*y + x) = (3 * *(src + bw*y + x) + 1 * *(dst + bw*y + x)) /4; } else { *(dst + bw*y + x) = (2 * *(src + bw*y + x) + 3 * *(dst + bw*y + x)) /5; } //*(dst + bw*y + x) = *(mask + bw*y + x); // Overlay mask } } //*(dst + bw*y + x) = *(mask + bw*y + x); // Full mask } } free(mask_mem); } static void nlmeans_prefilter(BorderedPlane *src, int filter_type) { hb_lock(src->mutex); if (src->prefiltered) { hb_unlock(src->mutex); return; } if (filter_type & NLMEANS_PREFILTER_MODE_MEAN3X3 || filter_type & NLMEANS_PREFILTER_MODE_MEAN5X5 || filter_type & NLMEANS_PREFILTER_MODE_MEDIAN3X3 || filter_type & NLMEANS_PREFILTER_MODE_MEDIAN5X5) { // Source image uint8_t *mem = src->mem; uint8_t *image = src->image; int border = src->border; int w = src->w; int h = src->h; int bw = w + 2 * border; int bh = h + 2 * border; // Duplicate plane uint8_t *mem_pre = malloc(bw * bh * sizeof(uint8_t)); uint8_t *image_pre = mem_pre + border + bw * border; for (int y = 0; y < h; y++) { memcpy(mem_pre + y * bw, mem + y * bw, bw); } // Filter plane; should already have at least 2px extra border on each side if (filter_type & NLMEANS_PREFILTER_MODE_MEDIAN5X5) { // Median 5x5 nlmeans_filter_median(image, image_pre, w, h, border, 5); } else if (filter_type & NLMEANS_PREFILTER_MODE_MEDIAN3X3) { // Median 3x3 nlmeans_filter_median(image, image_pre, w, h, border, 3); } else if (filter_type & NLMEANS_PREFILTER_MODE_MEAN5X5) { // Mean 5x5 nlmeans_filter_mean(image, image_pre, w, h, border, 5); } else if (filter_type & NLMEANS_PREFILTER_MODE_MEAN3X3) { // Mean 3x3 nlmeans_filter_mean(image, image_pre, w, h, border, 3); } // Restore edges if (filter_type & NLMEANS_PREFILTER_MODE_EDGEBOOST) { nlmeans_filter_edgeboost(image, image_pre, w, h, border); } // Blend source and destination for lesser effect int wet = 1; int dry = 0; if (filter_type & NLMEANS_PREFILTER_MODE_REDUCE50 && filter_type & NLMEANS_PREFILTER_MODE_REDUCE25) { wet = 1; dry = 3; } else if (filter_type & NLMEANS_PREFILTER_MODE_REDUCE50) { wet = 1; dry = 1; } else if (filter_type & NLMEANS_PREFILTER_MODE_REDUCE25) { wet = 3; dry = 1; } if (dry > 0) { for (int y = 0; y < bh; y++) { for (int x = 0; x < bw; x++) { *(mem_pre + bw*y + x) = (uint8_t)((wet * *(mem_pre + bw*y + x) + dry * *(mem + bw*y + x)) / (wet + dry)); } } } // Assign result src->mem_pre = mem_pre; src->image_pre = image_pre; // Recreate borders nlmeans_border(mem_pre, w, h, border); } src->prefiltered = 1; hb_unlock(src->mutex); } static void nlmeans_plane(Frame *frame, int prefilter, int plane, int nframes, uint8_t *dst, int w, int s, int h, double h_param, double origin_tune, int n, int r) { int n_half = (n-1) /2; int r_half = (r-1) /2; // Source image uint8_t *src = frame[0].plane[plane].image; uint8_t *src_pre = frame[0].plane[plane].image_pre; int border = frame[0].plane[plane].border; int src_w = frame[0].plane[plane].w + 2 * border; // Allocate temporary pixel sums struct PixelSum *tmp_data = calloc(w * h, sizeof(struct PixelSum)); // Allocate integral image int integral_stride = w + 2 * 16; uint32_t *integral_mem = malloc(integral_stride * (h+1) * sizeof(uint32_t)); uint32_t *integral = integral_mem + integral_stride + 16; // Precompute exponential table float exptable[NLMEANS_EXPSIZE]; const float weight_factor = 1.0/n/n / (h_param * h_param); const float min_weight_in_table = 0.0005; const float stretch = NLMEANS_EXPSIZE / (-log(min_weight_in_table)); const float weight_fact_table = weight_factor * stretch; const int diff_max = NLMEANS_EXPSIZE / weight_fact_table; for (int i = 0; i < NLMEANS_EXPSIZE; i++) { exptable[i] = exp(-i/stretch); } exptable[NLMEANS_EXPSIZE-1] = 0; // Iterate through available frames for (int f = 0; f < nframes; f++) { nlmeans_prefilter(&frame[f].plane[plane], prefilter); // Compare image uint8_t *compare = frame[f].plane[plane].image; uint8_t *compare_pre = frame[f].plane[plane].image_pre; int border = frame[f].plane[plane].border; int compare_w = frame[f].plane[plane].w + 2 * border; // Iterate through all displacements for (int dy = -r_half; dy <= r_half; dy++) { for (int dx = -r_half; dx <= r_half; dx++) { // Apply special weight tuning to origin patch if (dx == 0 && dy == 0 && f == 0) { // TODO: Parallelize this for (int y = n_half; y < h-n + n_half; y++) { for (int x = n_half; x < w-n + n_half; x++) { tmp_data[y*w + x].weight_sum += origin_tune; tmp_data[y*w + x].pixel_sum += origin_tune * src[y*src_w + x]; } } continue; } // Build integral memset(integral-1 - integral_stride, 0, (w+1) * sizeof(uint32_t)); for (int y = 0; y < h; y++) { const uint8_t *p1 = src_pre + y*src_w; const uint8_t *p2 = compare_pre + (y+dy)*compare_w + dx; uint32_t *out = integral + (y*integral_stride) - 1; *out++ = 0; for (int x = 0; x < w; x++) { int diff = *p1++ - *p2++; *out = *(out-1) + diff * diff; out++; } if (y > 0) { out = integral + y*integral_stride; for (int x = 0; x < w; x++) { *out += *(out - integral_stride); out++; } } } // Average displacement // TODO: Parallelize this for (int y = 0; y <= h-n; y++) { const uint32_t *integral_ptr1 = integral + (y -1)*integral_stride - 1; const uint32_t *integral_ptr2 = integral + (y+n-1)*integral_stride - 1; for (int x = 0; x <= w-n; x++) { int xc = x + n_half; int yc = y + n_half; // Difference between patches int diff = (uint32_t)(integral_ptr2[n] - integral_ptr2[0] - integral_ptr1[n] + integral_ptr1[0]); // Sum pixel with weight if (diff < diff_max) { int diffidx = diff * weight_fact_table; //float weight = exp(-diff*weightFact); float weight = exptable[diffidx]; tmp_data[yc*w + xc].weight_sum += weight; tmp_data[yc*w + xc].pixel_sum += weight * compare[(yc+dy)*compare_w + xc + dx]; } integral_ptr1++; integral_ptr2++; } } } } } // Copy edges for (int y = 0; y < h; y++) { for (int x = 0; x < n_half; x++) { *(dst + y * s + x) = *(src + y * src_w - x - 1); *(dst + y * s - x + (w - 1)) = *(src + y * src_w + x + w); } } for (int y = 0; y < n_half; y++) { memcpy(dst + y*s, src - (y+1)*src_w, w); memcpy(dst + (h-y-1)*s, src + (y+h)*src_w, w); } // Copy main image uint8_t result; for (int y = n_half; y < h-n_half; y++) { for (int x = n_half; x < w-n_half; x++) { result = (uint8_t)(tmp_data[y*w + x].pixel_sum / tmp_data[y*w + x].weight_sum); *(dst + y*s + x) = result ? result : *(src + y*src_w + x); } } free(tmp_data); free(integral_mem); } static int nlmeans_init(hb_filter_object_t *filter, hb_filter_init_t *init) { filter->private_data = calloc(sizeof(struct hb_filter_private_s), 1); hb_filter_private_t *pv = filter->private_data; // Mark parameters unset for (int c = 0; c < 3; c++) { pv->strength[c] = -1; pv->origin_tune[c] = -1; pv->patch_size[c] = -1; pv->range[c] = -1; pv->nframes[c] = -1; pv->prefilter[c] = -1; } // Read user parameters if (filter->settings != NULL) { sscanf(filter->settings, "%lf:%lf:%d:%d:%d:%d:%lf:%lf:%d:%d:%d:%d:%lf:%lf:%d:%d:%d:%d", &pv->strength[0], &pv->origin_tune[0], &pv->patch_size[0], &pv->range[0], &pv->nframes[0], &pv->prefilter[0], &pv->strength[1], &pv->origin_tune[1], &pv->patch_size[1], &pv->range[1], &pv->nframes[1], &pv->prefilter[1], &pv->strength[2], &pv->origin_tune[2], &pv->patch_size[2], &pv->range[2], &pv->nframes[2], &pv->prefilter[2]); } // Cascade values // Cr not set; inherit Cb. Cb not set; inherit Y. Y not set; defaults. for (int c = 1; c < 3; c++) { if (pv->strength[c] == -1) { pv->strength[c] = pv->strength[c-1]; } if (pv->origin_tune[c] == -1) { pv->origin_tune[c] = pv->origin_tune[c-1]; } if (pv->patch_size[c] == -1) { pv->patch_size[c] = pv->patch_size[c-1]; } if (pv->range[c] == -1) { pv->range[c] = pv->range[c-1]; } if (pv->nframes[c] == -1) { pv->nframes[c] = pv->nframes[c-1]; } if (pv->prefilter[c] == -1) { pv->prefilter[c] = pv->prefilter[c-1]; } } for (int c = 0; c < 3; c++) { // Replace unset values with defaults if (pv->strength[c] == -1) { pv->strength[c] = c ? NLMEANS_STRENGTH_LUMA_DEFAULT : NLMEANS_STRENGTH_CHROMA_DEFAULT; } if (pv->origin_tune[c] == -1) { pv->origin_tune[c] = c ? NLMEANS_ORIGIN_TUNE_LUMA_DEFAULT : NLMEANS_ORIGIN_TUNE_CHROMA_DEFAULT; } if (pv->patch_size[c] == -1) { pv->patch_size[c] = c ? NLMEANS_PATCH_SIZE_LUMA_DEFAULT : NLMEANS_PATCH_SIZE_CHROMA_DEFAULT; } if (pv->range[c] == -1) { pv->range[c] = c ? NLMEANS_RANGE_LUMA_DEFAULT : NLMEANS_RANGE_CHROMA_DEFAULT; } if (pv->nframes[c] == -1) { pv->nframes[c] = c ? NLMEANS_FRAMES_LUMA_DEFAULT : NLMEANS_FRAMES_CHROMA_DEFAULT; } if (pv->prefilter[c] == -1) { pv->prefilter[c] = c ? NLMEANS_PREFILTER_LUMA_DEFAULT : NLMEANS_PREFILTER_CHROMA_DEFAULT; } // Sanitize if (pv->strength[c] < 0) { pv->strength[c] = 0; } if (pv->origin_tune[c] < 0.01) { pv->origin_tune[c] = 0.01; } // avoid black artifacts if (pv->origin_tune[c] > 1) { pv->origin_tune[c] = 1; } if (pv->patch_size[c] % 2 == 0) { pv->patch_size[c]--; } if (pv->patch_size[c] < 1) { pv->patch_size[c] = 1; } if (pv->range[c] % 2 == 0) { pv->range[c]--; } if (pv->range[c] < 1) { pv->range[c] = 1; } if (pv->nframes[c] < 1) { pv->nframes[c] = 1; } if (pv->nframes[c] > NLMEANS_FRAMES_MAX) { pv->nframes[c] = NLMEANS_FRAMES_MAX; } if (pv->prefilter[c] < 0) { pv->prefilter[c] = 0; } if (pv->max_frames < pv->nframes[c]) pv->max_frames = pv->nframes[c]; } pv->thread_count = hb_get_cpu_count(); pv->frame = calloc(pv->thread_count + pv->max_frames, sizeof(Frame)); for (int ii = 0; ii < pv->thread_count + pv->max_frames; ii++) { for (int c = 0; c < 3; c++) { pv->frame[ii].plane[c].mutex = hb_lock_init(); } } pv->thread_data = malloc(pv->thread_count * sizeof(nlmeans_thread_arg_t*)); if (taskset_init(&pv->taskset, pv->thread_count, sizeof(nlmeans_thread_arg_t)) == 0) { hb_error("nlmeans could not initialize taskset"); goto fail; } for (int ii = 0; ii < pv->thread_count; ii++) { pv->thread_data[ii] = taskset_thread_args(&pv->taskset, ii); if (pv->thread_data[ii] == NULL) { hb_error("nlmeans could not create thread args"); goto fail; } pv->thread_data[ii]->pv = pv; pv->thread_data[ii]->segment = ii; if (taskset_thread_spawn(&pv->taskset, ii, "nlmeans_filter", nlmeans_filter_thread, HB_NORMAL_PRIORITY) == 0) { hb_error("nlmeans could not spawn thread"); goto fail; } } return 0; fail: taskset_fini(&pv->taskset); free(pv->thread_data); free(pv); return -1; } static void nlmeans_close(hb_filter_object_t *filter) { hb_filter_private_t *pv = filter->private_data; if (pv == NULL) { return; } taskset_fini(&pv->taskset); for (int c = 0; c < 3; c++) { for (int f = 0; f < pv->nframes[c]; f++) { if (pv->frame[f].plane[c].mem_pre != NULL && pv->frame[f].plane[c].mem_pre != pv->frame[f].plane[c].mem) { free(pv->frame[f].plane[c].mem_pre); pv->frame[f].plane[c].mem_pre = NULL; } if (pv->frame[f].plane[c].mem != NULL) { free(pv->frame[f].plane[c].mem); pv->frame[f].plane[c].mem = NULL; } } } for (int ii = 0; ii < pv->thread_count + pv->max_frames; ii++) { for (int c = 0; c < 3; c++) { hb_lock_close(&pv->frame[ii].plane[c].mutex); } } free(pv->frame); free(pv->thread_data); free(pv); filter->private_data = NULL; } static void nlmeans_filter_thread(void *thread_args_v) { nlmeans_thread_arg_t *thread_data = thread_args_v; hb_filter_private_t *pv = thread_data->pv; int segment = thread_data->segment; hb_log("NLMeans Denoise thread started for segment %d", segment); while (1) { // Wait until there is work to do. taskset_thread_wait4start(&pv->taskset, segment); if (taskset_thread_stop(&pv->taskset, segment)) { break; } Frame *frame = &pv->frame[segment]; hb_buffer_t *buf; buf = hb_frame_buffer_init(frame->fmt, frame->width, frame->height); for (int c = 0; c < 3; c++) { if (pv->strength[c] == 0) { nlmeans_deborder(&frame->plane[c], buf->plane[c].data, buf->plane[c].width, buf->plane[c].stride, buf->plane[c].height); continue; } if (pv->prefilter[c] & NLMEANS_PREFILTER_MODE_PASSTHRU) { nlmeans_prefilter(&pv->frame->plane[c], pv->prefilter[c]); nlmeans_deborder(&frame->plane[c], buf->plane[c].data, buf->plane[c].width, buf->plane[c].stride, buf->plane[c].height); continue; } // Process current plane nlmeans_plane(frame, pv->prefilter[c], c, pv->nframes[c], buf->plane[c].data, buf->plane[c].width, buf->plane[c].stride, buf->plane[c].height, pv->strength[c], pv->origin_tune[c], pv->patch_size[c], pv->range[c]); } buf->s = pv->frame[segment].s; thread_data->out = buf; // Finished this segment, notify. taskset_thread_complete(&pv->taskset, segment); } taskset_thread_complete(&pv->taskset, segment); } static void nlmeans_add_frame(hb_filter_private_t *pv, hb_buffer_t *buf) { for (int c = 0; c < 3; c++) { // Extend copy of plane with extra border and place in buffer int border = ((pv->range[c] + 2) / 2 + 15) / 16 * 16; nlmeans_alloc(buf->plane[c].data, buf->plane[c].width, buf->plane[c].stride, buf->plane[c].height, &pv->frame[pv->next_frame].plane[c], border); pv->frame[pv->next_frame].s = buf->s; pv->frame[pv->next_frame].width = buf->f.width; pv->frame[pv->next_frame].height = buf->f.height; pv->frame[pv->next_frame].fmt = buf->f.fmt; } pv->next_frame++; } static hb_buffer_t * nlmeans_filter(hb_filter_private_t *pv) { if (pv->next_frame < pv->max_frames + pv->thread_count) return NULL; taskset_cycle(&pv->taskset); // Free buffers that are not needed for next taskset cycle for (int c = 0; c < 3; c++) { for (int t = 0; t < pv->thread_count; t++) { // Release last frame in buffer if (pv->frame[t].plane[c].mem_pre != NULL && pv->frame[t].plane[c].mem_pre != pv->frame[t].plane[c].mem) { free(pv->frame[t].plane[c].mem_pre); pv->frame[t].plane[c].mem_pre = NULL; } if (pv->frame[t].plane[c].mem != NULL) { free(pv->frame[t].plane[c].mem); pv->frame[t].plane[c].mem = NULL; } } } // Shift frames in buffer down for (int f = 0; f < pv->max_frames; f++) { // Don't move the mutex! Frame frame = pv->frame[f]; pv->frame[f] = pv->frame[f+pv->thread_count]; for (int c = 0; c < 3; c++) { pv->frame[f].plane[c].mutex = frame.plane[c].mutex; pv->frame[f+pv->thread_count].plane[c].mem_pre = NULL; pv->frame[f+pv->thread_count].plane[c].mem = NULL; } } pv->next_frame -= pv->thread_count; // Collect results from taskset hb_buffer_t *last = NULL, *out = NULL; for (int t = 0; t < pv->thread_count; t++) { if (out == NULL) { out = last = pv->thread_data[t]->out; } else { last->next = pv->thread_data[t]->out; last = pv->thread_data[t]->out; } } return out; } static hb_buffer_t * nlmeans_filter_flush(hb_filter_private_t *pv) { hb_buffer_t *out = NULL, *last = NULL; for (int f = 0; f < pv->next_frame; f++) { Frame *frame = &pv->frame[f]; hb_buffer_t *buf; buf = hb_frame_buffer_init(frame->fmt, frame->width, frame->height); for (int c = 0; c < 3; c++) { if (pv->strength[c] == 0) { nlmeans_deborder(&frame->plane[c], buf->plane[c].data, buf->plane[c].width, buf->plane[c].stride, buf->plane[c].height); continue; } if (pv->prefilter[c] & NLMEANS_PREFILTER_MODE_PASSTHRU) { nlmeans_prefilter(&pv->frame[f].plane[c], pv->prefilter[c]); nlmeans_deborder(&frame->plane[c], buf->plane[c].data, buf->plane[c].width, buf->plane[c].stride, buf->plane[c].height); continue; } int nframes = pv->next_frame - f; if (pv->nframes[c] < nframes) nframes = pv->nframes[c]; // Process current plane nlmeans_plane(frame, pv->prefilter[c], c, nframes, buf->plane[c].data, buf->plane[c].width, buf->plane[c].stride, buf->plane[c].height, pv->strength[c], pv->origin_tune[c], pv->patch_size[c], pv->range[c]); } buf->s = frame->s; if (out == NULL) { out = last = buf; } else { last->next = buf; last = buf; } } return out; } static int nlmeans_work(hb_filter_object_t *filter, hb_buffer_t **buf_in, hb_buffer_t **buf_out ) { hb_filter_private_t *pv = filter->private_data; hb_buffer_t *in = *buf_in; if (in->size <= 0) { hb_buffer_t *last; // Flush buffered frames last = *buf_out = nlmeans_filter_flush(pv); // And terminate the buffer list with a null buffer if (last != NULL) { while (last->next != NULL) last = last->next; last->next = in; } else { *buf_out = in; } *buf_in = NULL; return HB_FILTER_DONE; } nlmeans_add_frame(pv, in); *buf_out = nlmeans_filter(pv); return HB_FILTER_OK; } HandBrake-0.10.2/libhb/enctheora.c0000664000175200017520000002740412531124076017243 0ustar handbrakehandbrake/* enctheora.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "theora/codec.h" #include "theora/theoraenc.h" int enctheoraInit( hb_work_object_t *, hb_job_t * ); int enctheoraWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); void enctheoraClose( hb_work_object_t * ); hb_work_object_t hb_enctheora = { WORK_ENCTHEORA, "Theora encoder (libtheora)", enctheoraInit, enctheoraWork, enctheoraClose }; struct hb_work_private_s { hb_job_t * job; th_enc_ctx * ctx; FILE * file; unsigned char stat_buf[80]; int stat_read; int stat_fill; }; int enctheoraInit( hb_work_object_t * w, hb_job_t * job ) { int keyframe_frequency, log_keyframe, ret; hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; pv->job = job; if( job->pass != 0 && job->pass != -1 ) { char filename[1024]; memset( filename, 0, 1024 ); hb_get_tempory_filename( job->h, filename, "theroa.log" ); if ( job->pass == 1 ) { pv->file = hb_fopen(filename, "wb"); } else { pv->file = hb_fopen(filename, "rb"); } } th_info ti; th_comment tc; ogg_packet op; th_info_init( &ti ); /* Frame width and height need to be multiples of 16 */ ti.pic_width = job->width; ti.pic_height = job->height; ti.frame_width = (job->width + 0xf) & ~0xf; ti.frame_height = (job->height + 0xf) & ~0xf; ti.pic_x = ti.pic_y = 0; if( job->pass == 2 ) { hb_interjob_t * interjob = hb_interjob_get( job->h ); ti.fps_numerator = interjob->vrate; ti.fps_denominator = interjob->vrate_base; } else { ti.fps_numerator = job->vrate; ti.fps_denominator = job->vrate_base; } if( job->anamorphic.mode ) { ti.aspect_numerator = job->anamorphic.par_width; ti.aspect_denominator = job->anamorphic.par_height; } else { ti.aspect_numerator = ti.aspect_denominator = 1; } ti.colorspace = TH_CS_UNSPECIFIED; ti.pixel_fmt = TH_PF_420; if (job->vquality < 0.0) { ti.target_bitrate = job->vbitrate * 1000; ti.quality = 0; } else { ti.target_bitrate = 0; ti.quality = job->vquality; } keyframe_frequency = 10 * (int)( (double)job->vrate / (double)job->vrate_base + 0.5 ); hb_log("theora: keyint: %i", keyframe_frequency); int tmp = keyframe_frequency - 1; for (log_keyframe = 0; tmp; log_keyframe++) tmp >>= 1; ti.keyframe_granule_shift = log_keyframe; pv->ctx = th_encode_alloc( &ti ); th_info_clear( &ti ); ret = th_encode_ctl(pv->ctx, TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE, &keyframe_frequency, sizeof(keyframe_frequency)); if( ret < 0 ) { hb_log("theora: Could not set keyframe interval to %d", keyframe_frequency); } /* Set "soft target" rate control which improves quality at the * expense of solid bitrate caps */ int arg = TH_RATECTL_CAP_UNDERFLOW; ret = th_encode_ctl(pv->ctx, TH_ENCCTL_SET_RATE_FLAGS, &arg, sizeof(arg)); if( ret < 0 ) { hb_log("theora: Could not set soft ratecontrol"); } if( job->pass != 0 && job->pass != -1 ) { arg = keyframe_frequency * 7 >> 1; ret = th_encode_ctl(pv->ctx, TH_ENCCTL_SET_RATE_BUFFER, &arg, sizeof(arg)); if( ret < 0 ) { hb_log("theora: Could not set rate control buffer"); } } if( job->pass == 1 ) { unsigned char *buffer; int bytes; bytes = th_encode_ctl(pv->ctx, TH_ENCCTL_2PASS_OUT, &buffer, sizeof(buffer)); if( bytes < 0 ) { hb_error("Could not set up the first pass of two-pass mode.\n"); hb_error("Did you remember to specify an estimated bitrate?\n"); return 1; } if( fwrite( buffer, 1, bytes, pv->file ) < bytes ) { hb_error("Unable to write to two-pass data file.\n"); return 1; } fflush( pv->file ); } if( job->pass == 2 ) { /* Enable the second pass here. * We make this call just to set the encoder into 2-pass mode, because * by default enabling two-pass sets the buffer delay to the whole file * (because there's no way to explicitly request that behavior). * If we waited until we were actually encoding, it would overwite our * settings.*/ hb_log("enctheora: init 2nd pass"); if( th_encode_ctl( pv->ctx, TH_ENCCTL_2PASS_IN, NULL, 0) < 0) { hb_log("theora: Could not set up the second pass of two-pass mode."); return 1; } } th_comment_init( &tc ); ogg_packet *header; int ii; for (ii = 0; ii < 3; ii++) { th_encode_flushheader( pv->ctx, &tc, &op ); header = (ogg_packet*)w->config->theora.headers[ii]; memcpy(header, &op, sizeof(op)); header->packet = w->config->theora.headers[ii] + sizeof(ogg_packet); memcpy(header->packet, op.packet, op.bytes ); } th_comment_clear( &tc ); return 0; } /*********************************************************************** * Close *********************************************************************** * **********************************************************************/ void enctheoraClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; th_encode_free( pv->ctx ); if( pv->file ) { fclose( pv->file ); } free( pv ); w->private_data = NULL; } /*********************************************************************** * Work *********************************************************************** * **********************************************************************/ int enctheoraWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_job_t * job = pv->job; hb_buffer_t * in = *buf_in, * buf; th_ycbcr_buffer ycbcr; ogg_packet op; int frame_width, frame_height; if ( in->size <= 0 ) { // EOF on input - send it downstream & say we're done. // XXX may need to flush packets via a call to // th_encode_packetout( pv->ctx, 1, &op ); // but we don't have a timestamp to put on those packets so we // drop them for now. *buf_out = in; *buf_in = NULL; th_encode_packetout( pv->ctx, 1, &op ); if( job->pass == 1 ) { unsigned char *buffer; int bytes; bytes = th_encode_ctl(pv->ctx, TH_ENCCTL_2PASS_OUT, &buffer, sizeof(buffer)); if( bytes < 0 ) { fprintf(stderr,"Could not read two-pass data from encoder.\n"); return HB_WORK_DONE; } fseek( pv->file, 0, SEEK_SET ); if( fwrite( buffer, 1, bytes, pv->file ) < bytes) { fprintf(stderr,"Unable to write to two-pass data file.\n"); return HB_WORK_DONE; } fflush( pv->file ); } return HB_WORK_DONE; } if( job->pass == 2 ) { for(;;) { int bytes, size, ret; /*Ask the encoder how many bytes it would like.*/ bytes = th_encode_ctl( pv->ctx, TH_ENCCTL_2PASS_IN, NULL, 0 ); if( bytes < 0 ) { hb_error("Error requesting stats size in second pass."); *job->done_error = HB_ERROR_UNKNOWN; *job->die = 1; return HB_WORK_DONE; } /*If it's got enough, stop.*/ if( bytes == 0 ) break; /*Read in some more bytes, if necessary.*/ if( bytes > pv->stat_fill - pv->stat_read ) size = bytes - (pv->stat_fill - pv->stat_read); else size = 0; if( size > 80 - pv->stat_fill ) size = 80 - pv->stat_fill; if( size > 0 && fread( pv->stat_buf+pv->stat_fill, 1, size, pv->file ) < size ) { hb_error("Could not read frame data from two-pass data file!"); *job->done_error = HB_ERROR_UNKNOWN; *job->die = 1; return HB_WORK_DONE; } pv->stat_fill += size; /*And pass them off.*/ if( bytes > pv->stat_fill - pv->stat_read ) bytes = pv->stat_fill - pv->stat_read; ret = th_encode_ctl( pv->ctx, TH_ENCCTL_2PASS_IN, pv->stat_buf+pv->stat_read, bytes); if( ret < 0 ) { hb_error("Error submitting pass data in second pass."); *job->done_error = HB_ERROR_UNKNOWN; *job->die = 1; return HB_WORK_DONE; } /*If the encoder consumed the whole buffer, reset it.*/ if( ret >= pv->stat_fill - pv->stat_read ) pv->stat_read = pv->stat_fill = 0; /*Otherwise remember how much it used.*/ else pv->stat_read += ret; } } memset(&op, 0, sizeof(op)); memset(&ycbcr, 0, sizeof(ycbcr)); frame_width = (job->width + 0xf) & ~0xf; frame_height = (job->height + 0xf) & ~0xf; // Y ycbcr[0].width = frame_width; ycbcr[0].height = frame_height; // CbCr decimated by factor of 2 in both width and height ycbcr[1].width = ycbcr[2].width = (frame_width + 1) / 2; ycbcr[1].height = ycbcr[2].height = (frame_height + 1) / 2; ycbcr[0].stride = in->plane[0].stride; ycbcr[1].stride = in->plane[1].stride; ycbcr[2].stride = in->plane[2].stride; ycbcr[0].data = in->plane[0].data; ycbcr[1].data = in->plane[1].data; ycbcr[2].data = in->plane[2].data; th_encode_ycbcr_in( pv->ctx, ycbcr ); if( job->pass == 1 ) { unsigned char *buffer; int bytes; bytes = th_encode_ctl(pv->ctx, TH_ENCCTL_2PASS_OUT, &buffer, sizeof(buffer)); if( bytes < 0 ) { fprintf(stderr,"Could not read two-pass data from encoder.\n"); *job->done_error = HB_ERROR_UNKNOWN; *job->die = 1; return HB_WORK_DONE; } if( fwrite( buffer, 1, bytes, pv->file ) < bytes) { fprintf(stderr,"Unable to write to two-pass data file.\n"); *job->done_error = HB_ERROR_UNKNOWN; *job->die = 1; return HB_WORK_DONE; } fflush( pv->file ); } th_encode_packetout( pv->ctx, 0, &op ); // Theora can generate 0 length output for duplicate frames. // Since we use 0 length buffers to indicate end of stream, we // can't allow 0 lenth buffers. // // As a work-around, always allocate an extra byte for theora buffers. // // This is fixed correctly in svn trunk by using a end of stream flag // instead of 0 length buffer. buf = hb_buffer_init(op.bytes + 1); memcpy(buf->data, op.packet, op.bytes); buf->f.fmt = AV_PIX_FMT_YUV420P; buf->f.width = frame_width; buf->f.height = frame_height; buf->s.frametype = ( th_packet_iskeyframe(&op) ) ? HB_FRAME_KEY : HB_FRAME_REF; buf->s.start = in->s.start; buf->s.stop = in->s.stop; buf->s.duration = in->s.stop - in->s.start; *buf_out = buf; return HB_WORK_OK; } HandBrake-0.10.2/libhb/opencl.h0000664000175200017520000006472312463330511016562 0ustar handbrakehandbrake/* opencl.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef HB_OPENCL_H #define HB_OPENCL_H #include "extras/cl.h" #include "openclwrapper.h" // we only support OpenCL 1.1 or later #define HB_OCL_MINVERSION_MAJOR 1 #define HB_OCL_MINVERSION_MINOR 1 #define HB_OCL_FUNC_TYPE(name) hb_opencl_##name##_func #define HB_OCL_FUNC_DECL(name) HB_OCL_FUNC_TYPE(name) name #define HB_OCL_API(ret, attr, name) typedef ret (attr* HB_OCL_FUNC_TYPE(name)) #ifdef __APPLE__ #pragma mark - #pragma mark OpenCL API #endif // __APPLE__ /* Platform API */ HB_OCL_API(cl_int, CL_API_CALL, clGetPlatformIDs) (cl_uint /* num_entries */, cl_platform_id * /* platforms */, cl_uint * /* num_platforms */); HB_OCL_API(cl_int, CL_API_CALL, clGetPlatformInfo) (cl_platform_id /* platform */, cl_platform_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */); /* Device APIs */ HB_OCL_API(cl_int, CL_API_CALL, clGetDeviceIDs) (cl_platform_id /* platform */, cl_device_type /* device_type */, cl_uint /* num_entries */, cl_device_id * /* devices */, cl_uint * /* num_devices */); HB_OCL_API(cl_int, CL_API_CALL, clGetDeviceInfo) (cl_device_id /* device */, cl_device_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */); HB_OCL_API(cl_int, CL_API_CALL, clCreateSubDevices) (cl_device_id /* in_device */, const cl_device_partition_property * /* properties */, cl_uint /* num_devices */, cl_device_id * /* out_devices */, cl_uint * /* num_devices_ret */); HB_OCL_API(cl_int, CL_API_CALL, clRetainDevice) (cl_device_id /* device */); HB_OCL_API(cl_int, CL_API_CALL, clReleaseDevice) (cl_device_id /* device */); /* Context APIs */ HB_OCL_API(cl_context, CL_API_CALL, clCreateContext) (const cl_context_properties * /* properties */, cl_uint /* num_devices */, const cl_device_id * /* devices */, void (CL_CALLBACK * /* pfn_notify */)(const char *, const void *, size_t, void *), void * /* user_data */, cl_int * /* errcode_ret */); HB_OCL_API(cl_context, CL_API_CALL, clCreateContextFromType) (const cl_context_properties * /* properties */, cl_device_type /* device_type */, void (CL_CALLBACK * /* pfn_notify*/ )(const char *, const void *, size_t, void *), void * /* user_data */, cl_int * /* errcode_ret */); HB_OCL_API(cl_int, CL_API_CALL, clRetainContext) (cl_context /* context */); HB_OCL_API(cl_int, CL_API_CALL, clReleaseContext) (cl_context /* context */); HB_OCL_API(cl_int, CL_API_CALL, clGetContextInfo) (cl_context /* context */, cl_context_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */); /* Command Queue APIs */ HB_OCL_API(cl_command_queue, CL_API_CALL, clCreateCommandQueue) (cl_context /* context */, cl_device_id /* device */, cl_command_queue_properties /* properties */, cl_int * /* errcode_ret */); HB_OCL_API(cl_int, CL_API_CALL, clRetainCommandQueue) (cl_command_queue /* command_queue */); HB_OCL_API(cl_int, CL_API_CALL, clReleaseCommandQueue) (cl_command_queue /* command_queue */); HB_OCL_API(cl_int, CL_API_CALL, clGetCommandQueueInfo) (cl_command_queue /* command_queue */, cl_command_queue_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */); /* Memory Object APIs */ HB_OCL_API(cl_mem, CL_API_CALL, clCreateBuffer) (cl_context /* context */, cl_mem_flags /* flags */, size_t /* size */, void * /* host_ptr */, cl_int * /* errcode_ret */); HB_OCL_API(cl_mem, CL_API_CALL, clCreateSubBuffer) (cl_mem /* buffer */, cl_mem_flags /* flags */, cl_buffer_create_type /* buffer_create_type */, const void * /* buffer_create_info */, cl_int * /* errcode_ret */); HB_OCL_API(cl_mem, CL_API_CALL, clCreateImage) (cl_context /* context */, cl_mem_flags /* flags */, const cl_image_format * /* image_format */, const cl_image_desc * /* image_desc */, void * /* host_ptr */, cl_int * /* errcode_ret */); HB_OCL_API(cl_int, CL_API_CALL, clRetainMemObject) (cl_mem /* memobj */); HB_OCL_API(cl_int, CL_API_CALL, clReleaseMemObject) (cl_mem /* memobj */); HB_OCL_API(cl_int, CL_API_CALL, clGetSupportedImageFormats) (cl_context /* context */, cl_mem_flags /* flags */, cl_mem_object_type /* image_type */, cl_uint /* num_entries */, cl_image_format * /* image_formats */, cl_uint * /* num_image_formats */); HB_OCL_API(cl_int, CL_API_CALL, clGetMemObjectInfo) (cl_mem /* memobj */, cl_mem_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */); HB_OCL_API(cl_int, CL_API_CALL, clGetImageInfo) (cl_mem /* image */, cl_image_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */); HB_OCL_API(cl_int, CL_API_CALL, clSetMemObjectDestructorCallback) (cl_mem /* memobj */, void (CL_CALLBACK * /*pfn_notify*/)( cl_mem /* memobj */, void* /*user_data*/), void * /*user_data */ ); /* Sampler APIs */ HB_OCL_API(cl_sampler, CL_API_CALL, clCreateSampler) (cl_context /* context */, cl_bool /* normalized_coords */, cl_addressing_mode /* addressing_mode */, cl_filter_mode /* filter_mode */, cl_int * /* errcode_ret */); HB_OCL_API(cl_int, CL_API_CALL, clRetainSampler) (cl_sampler /* sampler */); HB_OCL_API(cl_int, CL_API_CALL, clReleaseSampler) (cl_sampler /* sampler */); HB_OCL_API(cl_int, CL_API_CALL, clGetSamplerInfo) (cl_sampler /* sampler */, cl_sampler_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */); /* Program Object APIs */ HB_OCL_API(cl_program, CL_API_CALL, clCreateProgramWithSource) (cl_context /* context */, cl_uint /* count */, const char ** /* strings */, const size_t * /* lengths */, cl_int * /* errcode_ret */); HB_OCL_API(cl_program, CL_API_CALL, clCreateProgramWithBinary) (cl_context /* context */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const size_t * /* lengths */, const unsigned char ** /* binaries */, cl_int * /* binary_status */, cl_int * /* errcode_ret */); HB_OCL_API(cl_program, CL_API_CALL, clCreateProgramWithBuiltInKernels) (cl_context /* context */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const char * /* kernel_names */, cl_int * /* errcode_ret */); HB_OCL_API(cl_int, CL_API_CALL, clRetainProgram) (cl_program /* program */); HB_OCL_API(cl_int, CL_API_CALL, clReleaseProgram) (cl_program /* program */); HB_OCL_API(cl_int, CL_API_CALL, clBuildProgram) (cl_program /* program */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const char * /* options */, void (CL_CALLBACK * /* pfn_notify */)(cl_program /* program */, void * /* user_data */), void * /* user_data */); HB_OCL_API(cl_int, CL_API_CALL, clCompileProgram) (cl_program /* program */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const char * /* options */, cl_uint /* num_input_headers */, const cl_program * /* input_headers */, const char ** /* header_include_names */, void (CL_CALLBACK * /* pfn_notify */)(cl_program /* program */, void * /* user_data */), void * /* user_data */); HB_OCL_API(cl_program, CL_API_CALL, clLinkProgram) (cl_context /* context */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const char * /* options */, cl_uint /* num_input_programs */, const cl_program * /* input_programs */, void (CL_CALLBACK * /* pfn_notify */)(cl_program /* program */, void * /* user_data */), void * /* user_data */, cl_int * /* errcode_ret */ ); HB_OCL_API(cl_int, CL_API_CALL, clUnloadPlatformCompiler) (cl_platform_id /* platform */); HB_OCL_API(cl_int, CL_API_CALL, clGetProgramInfo) (cl_program /* program */, cl_program_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */); HB_OCL_API(cl_int, CL_API_CALL, clGetProgramBuildInfo) (cl_program /* program */, cl_device_id /* device */, cl_program_build_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */); /* Kernel Object APIs */ HB_OCL_API(cl_kernel, CL_API_CALL, clCreateKernel) (cl_program /* program */, const char * /* kernel_name */, cl_int * /* errcode_ret */); HB_OCL_API(cl_int, CL_API_CALL, clCreateKernelsInProgram) (cl_program /* program */, cl_uint /* num_kernels */, cl_kernel * /* kernels */, cl_uint * /* num_kernels_ret */); HB_OCL_API(cl_int, CL_API_CALL, clRetainKernel) (cl_kernel /* kernel */); HB_OCL_API(cl_int, CL_API_CALL, clReleaseKernel) (cl_kernel /* kernel */); HB_OCL_API(cl_int, CL_API_CALL, clSetKernelArg) (cl_kernel /* kernel */, cl_uint /* arg_index */, size_t /* arg_size */, const void * /* arg_value */); HB_OCL_API(cl_int, CL_API_CALL, clGetKernelInfo) (cl_kernel /* kernel */, cl_kernel_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */); HB_OCL_API(cl_int, CL_API_CALL, clGetKernelArgInfo) (cl_kernel /* kernel */, cl_uint /* arg_indx */, cl_kernel_arg_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */); HB_OCL_API(cl_int, CL_API_CALL, clGetKernelWorkGroupInfo) (cl_kernel /* kernel */, cl_device_id /* device */, cl_kernel_work_group_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */); /* Event Object APIs */ HB_OCL_API(cl_int, CL_API_CALL, clWaitForEvents) (cl_uint /* num_events */, const cl_event * /* event_list */); HB_OCL_API(cl_int, CL_API_CALL, clGetEventInfo) (cl_event /* event */, cl_event_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */); HB_OCL_API(cl_event, CL_API_CALL, clCreateUserEvent) (cl_context /* context */, cl_int * /* errcode_ret */); HB_OCL_API(cl_int, CL_API_CALL, clRetainEvent) (cl_event /* event */); HB_OCL_API(cl_int, CL_API_CALL, clReleaseEvent) (cl_event /* event */); HB_OCL_API(cl_int, CL_API_CALL, clSetUserEventStatus) (cl_event /* event */, cl_int /* execution_status */); HB_OCL_API(cl_int, CL_API_CALL, clSetEventCallback) (cl_event /* event */, cl_int /* command_exec_callback_type */, void (CL_CALLBACK * /* pfn_notify */)(cl_event, cl_int, void *), void * /* user_data */); /* Profiling APIs */ HB_OCL_API(cl_int, CL_API_CALL, clGetEventProfilingInfo) (cl_event /* event */, cl_profiling_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */); /* Flush and Finish APIs */ HB_OCL_API(cl_int, CL_API_CALL, clFlush) (cl_command_queue /* command_queue */); HB_OCL_API(cl_int, CL_API_CALL, clFinish) (cl_command_queue /* command_queue */); /* Enqueued Commands APIs */ HB_OCL_API(cl_int, CL_API_CALL, clEnqueueReadBuffer) (cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_read */, size_t /* offset */, size_t /* size */, void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueReadBufferRect) (cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_read */, const size_t * /* buffer_offset */, const size_t * /* host_offset */, const size_t * /* region */, size_t /* buffer_row_pitch */, size_t /* buffer_slice_pitch */, size_t /* host_row_pitch */, size_t /* host_slice_pitch */, void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueWriteBuffer) (cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_write */, size_t /* offset */, size_t /* size */, const void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueWriteBufferRect) (cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_write */, const size_t * /* buffer_offset */, const size_t * /* host_offset */, const size_t * /* region */, size_t /* buffer_row_pitch */, size_t /* buffer_slice_pitch */, size_t /* host_row_pitch */, size_t /* host_slice_pitch */, const void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueFillBuffer) (cl_command_queue /* command_queue */, cl_mem /* buffer */, const void * /* pattern */, size_t /* pattern_size */, size_t /* offset */, size_t /* size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueCopyBuffer) (cl_command_queue /* command_queue */, cl_mem /* src_buffer */, cl_mem /* dst_buffer */, size_t /* src_offset */, size_t /* dst_offset */, size_t /* size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueCopyBufferRect) (cl_command_queue /* command_queue */, cl_mem /* src_buffer */, cl_mem /* dst_buffer */, const size_t * /* src_origin */, const size_t * /* dst_origin */, const size_t * /* region */, size_t /* src_row_pitch */, size_t /* src_slice_pitch */, size_t /* dst_row_pitch */, size_t /* dst_slice_pitch */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueReadImage) (cl_command_queue /* command_queue */, cl_mem /* image */, cl_bool /* blocking_read */, const size_t * /* origin[3] */, const size_t * /* region[3] */, size_t /* row_pitch */, size_t /* slice_pitch */, void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueWriteImage) (cl_command_queue /* command_queue */, cl_mem /* image */, cl_bool /* blocking_write */, const size_t * /* origin[3] */, const size_t * /* region[3] */, size_t /* input_row_pitch */, size_t /* input_slice_pitch */, const void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueFillImage) (cl_command_queue /* command_queue */, cl_mem /* image */, const void * /* fill_color */, const size_t * /* origin[3] */, const size_t * /* region[3] */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueCopyImage) (cl_command_queue /* command_queue */, cl_mem /* src_image */, cl_mem /* dst_image */, const size_t * /* src_origin[3] */, const size_t * /* dst_origin[3] */, const size_t * /* region[3] */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueCopyImageToBuffer) (cl_command_queue /* command_queue */, cl_mem /* src_image */, cl_mem /* dst_buffer */, const size_t * /* src_origin[3] */, const size_t * /* region[3] */, size_t /* dst_offset */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueCopyBufferToImage) (cl_command_queue /* command_queue */, cl_mem /* src_buffer */, cl_mem /* dst_image */, size_t /* src_offset */, const size_t * /* dst_origin[3] */, const size_t * /* region[3] */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(void *, CL_API_CALL, clEnqueueMapBuffer) (cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_map */, cl_map_flags /* map_flags */, size_t /* offset */, size_t /* size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */, cl_int * /* errcode_ret */); HB_OCL_API(void *, CL_API_CALL, clEnqueueMapImage) (cl_command_queue /* command_queue */, cl_mem /* image */, cl_bool /* blocking_map */, cl_map_flags /* map_flags */, const size_t * /* origin[3] */, const size_t * /* region[3] */, size_t * /* image_row_pitch */, size_t * /* image_slice_pitch */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */, cl_int * /* errcode_ret */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueUnmapMemObject) (cl_command_queue /* command_queue */, cl_mem /* memobj */, void * /* mapped_ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueMigrateMemObjects) (cl_command_queue /* command_queue */, cl_uint /* num_mem_objects */, const cl_mem * /* mem_objects */, cl_mem_migration_flags /* flags */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueNDRangeKernel) (cl_command_queue /* command_queue */, cl_kernel /* kernel */, cl_uint /* work_dim */, const size_t * /* global_work_offset */, const size_t * /* global_work_size */, const size_t * /* local_work_size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueTask) (cl_command_queue /* command_queue */, cl_kernel /* kernel */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueNativeKernel) (cl_command_queue /* command_queue */, void (CL_CALLBACK * /*user_func*/)(void *), void * /* args */, size_t /* cb_args */, cl_uint /* num_mem_objects */, const cl_mem * /* mem_list */, const void ** /* args_mem_loc */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueMarkerWithWaitList) (cl_command_queue /* command_queue */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); HB_OCL_API(cl_int, CL_API_CALL, clEnqueueBarrierWithWaitList) (cl_command_queue /* command_queue */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */); /* Extension function access * * Returns the extension function address for the given function name, * or NULL if a valid function can not be found. The client must * check to make sure the address is not NULL, before using or * calling the returned function address. */ HB_OCL_API(void *, CL_API_CALL, clGetExtensionFunctionAddressForPlatform) (cl_platform_id /* platform */, const char * /* func_name */); #ifdef __APPLE__ #pragma mark - #endif // __APPLE__ typedef struct hb_opencl_library_s { void *library; /* Pointers to select OpenCL API functions */ HB_OCL_FUNC_DECL(clBuildProgram); HB_OCL_FUNC_DECL(clCreateBuffer); HB_OCL_FUNC_DECL(clCreateCommandQueue); HB_OCL_FUNC_DECL(clCreateContextFromType); HB_OCL_FUNC_DECL(clCreateKernel); HB_OCL_FUNC_DECL(clCreateProgramWithBinary); HB_OCL_FUNC_DECL(clCreateProgramWithSource); HB_OCL_FUNC_DECL(clEnqueueCopyBuffer); HB_OCL_FUNC_DECL(clEnqueueMapBuffer); HB_OCL_FUNC_DECL(clEnqueueNDRangeKernel); HB_OCL_FUNC_DECL(clEnqueueReadBuffer); HB_OCL_FUNC_DECL(clEnqueueUnmapMemObject); HB_OCL_FUNC_DECL(clEnqueueWriteBuffer); HB_OCL_FUNC_DECL(clFlush); HB_OCL_FUNC_DECL(clGetCommandQueueInfo); HB_OCL_FUNC_DECL(clGetContextInfo); HB_OCL_FUNC_DECL(clGetDeviceIDs); HB_OCL_FUNC_DECL(clGetDeviceInfo); HB_OCL_FUNC_DECL(clGetPlatformIDs); HB_OCL_FUNC_DECL(clGetPlatformInfo); HB_OCL_FUNC_DECL(clGetProgramBuildInfo); HB_OCL_FUNC_DECL(clGetProgramInfo); HB_OCL_FUNC_DECL(clReleaseCommandQueue); HB_OCL_FUNC_DECL(clReleaseContext); HB_OCL_FUNC_DECL(clReleaseEvent); HB_OCL_FUNC_DECL(clReleaseKernel); HB_OCL_FUNC_DECL(clReleaseMemObject); HB_OCL_FUNC_DECL(clReleaseProgram); HB_OCL_FUNC_DECL(clSetKernelArg); HB_OCL_FUNC_DECL(clWaitForEvents); } hb_opencl_library_t; hb_opencl_library_t* hb_opencl_library_init(); void hb_opencl_library_close(hb_opencl_library_t **_opencl); /* * Convenience pointer to a single shared OpenCL library wrapper. * * It can be initialized and closed via hb_ocl_init/close(). */ extern hb_opencl_library_t *hb_ocl; int hb_ocl_init(); void hb_ocl_close(); typedef struct hb_opencl_device_s { cl_platform_id platform; cl_device_type type; cl_device_id id; char version[128]; char driver[128]; char vendor[128]; char name[128]; enum { HB_OCL_VENDOR_AMD, HB_OCL_VENDOR_NVIDIA, HB_OCL_VENDOR_INTEL, HB_OCL_VENDOR_OTHER, } ocl_vendor; } hb_opencl_device_t; int hb_opencl_available(); void hb_opencl_info_print(); /* OpenCL scaling */ typedef struct hb_oclscale_s { int initialized; // bicubic scale weights cl_mem bicubic_x_weights; cl_mem bicubic_y_weights; cl_float xscale; cl_float yscale; int width; int height; // horizontal scaling and vertical scaling kernel handle cl_kernel m_kernel; int use_ocl_mem; // 0 use host memory. 1 use gpu oclmem } hb_oclscale_t; int hb_ocl_scale(hb_buffer_t *in, hb_buffer_t *out, int *crop, hb_oclscale_t *os); /* Utilities */ #define HB_OCL_BUF_CREATE(ocl_lib, out, flags, size) \ { \ out = ocl_lib->clCreateBuffer(kenv->context, flags, size, NULL, &status); \ if (CL_SUCCESS != status) \ { \ return -1; \ } \ } #define HB_OCL_BUF_FREE(ocl_lib, buf) \ { \ if (buf != NULL) \ { \ ocl_lib->clReleaseMemObject(buf); \ buf = NULL; \ } \ } #define HB_OCL_CHECK(method, ...) \ { \ status = method(__VA_ARGS__); \ if (status != CL_SUCCESS) \ { \ hb_error("%s:%d (%s) error: %d\n",__FUNCTION__,__LINE__,#method,status);\ return status; \ } \ } #endif//HB_OPENCL_H HandBrake-0.10.2/libhb/deccc608sub.c0000664000175200017520000016234012463330511017300 0ustar handbrakehandbrake/* deccc608sub.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ /* * From ccextractor, leave this file as intact and close to the original as possible so that * it is easy to patch in fixes - even though this file contains code that we don't need. * * Note that the SRT sub generation from CC could be useful for mkv subs. */ #include "hb.h" #include "deccc608sub.h" #define SSA_PREAMBLE_LEN 24 /* * ccextractor static configuration variables. */ static int debug_608 = 0; static int cc_channel = 1; static int subs_delay = 0; /* * Get the time of the last buffer that we have received. */ static int64_t get_last_pts(struct s_write *wb) { return wb->last_pts; } #define fatal(N, ...) // N int rowdata[] = {11,-1,1,2,3,4,12,13,14,15,5,6,7,8,9,10}; // Relationship between the first PAC byte and the row number // The following enc_buffer is not used at the moment, if it does get used // we need to bring it into the swrite struct. Same for "str". #define INITIAL_ENC_BUFFER_CAPACITY 2048 static const unsigned char pac2_attribs[][3]= // Color, font, ident { {COL_WHITE, FONT_REGULAR, 0}, // 0x40 || 0x60 {COL_WHITE, FONT_UNDERLINED, 0}, // 0x41 || 0x61 {COL_GREEN, FONT_REGULAR, 0}, // 0x42 || 0x62 {COL_GREEN, FONT_UNDERLINED, 0}, // 0x43 || 0x63 {COL_BLUE, FONT_REGULAR, 0}, // 0x44 || 0x64 {COL_BLUE, FONT_UNDERLINED, 0}, // 0x45 || 0x65 {COL_CYAN, FONT_REGULAR, 0}, // 0x46 || 0x66 {COL_CYAN, FONT_UNDERLINED, 0}, // 0x47 || 0x67 {COL_RED, FONT_REGULAR, 0}, // 0x48 || 0x68 {COL_RED, FONT_UNDERLINED, 0}, // 0x49 || 0x69 {COL_YELLOW, FONT_REGULAR, 0}, // 0x4a || 0x6a {COL_YELLOW, FONT_UNDERLINED, 0}, // 0x4b || 0x6b {COL_MAGENTA, FONT_REGULAR, 0}, // 0x4c || 0x6c {COL_MAGENTA, FONT_UNDERLINED, 0}, // 0x4d || 0x6d {COL_WHITE, FONT_ITALICS, 0}, // 0x4e || 0x6e {COL_WHITE, FONT_UNDERLINED_ITALICS, 0}, // 0x4f || 0x6f {COL_WHITE, FONT_REGULAR, 0}, // 0x50 || 0x70 {COL_WHITE, FONT_UNDERLINED, 0}, // 0x51 || 0x71 {COL_WHITE, FONT_REGULAR, 4}, // 0x52 || 0x72 {COL_WHITE, FONT_UNDERLINED, 4}, // 0x53 || 0x73 {COL_WHITE, FONT_REGULAR, 8}, // 0x54 || 0x74 {COL_WHITE, FONT_UNDERLINED, 8}, // 0x55 || 0x75 {COL_WHITE, FONT_REGULAR, 12}, // 0x56 || 0x76 {COL_WHITE, FONT_UNDERLINED, 12}, // 0x57 || 0x77 {COL_WHITE, FONT_REGULAR, 16}, // 0x58 || 0x78 {COL_WHITE, FONT_UNDERLINED, 16}, // 0x59 || 0x79 {COL_WHITE, FONT_REGULAR, 20}, // 0x5a || 0x7a {COL_WHITE, FONT_UNDERLINED, 20}, // 0x5b || 0x7b {COL_WHITE, FONT_REGULAR, 24}, // 0x5c || 0x7c {COL_WHITE, FONT_UNDERLINED, 24}, // 0x5d || 0x7d {COL_WHITE, FONT_REGULAR, 28}, // 0x5e || 0x7e {COL_WHITE, FONT_UNDERLINED, 28} // 0x5f || 0x7f }; // Default color static enum color_code default_color=COL_WHITE; static const char *command_type[] = { "Unknown", "EDM - EraseDisplayedMemory", "RCL - ResumeCaptionLoading", "EOC - End Of Caption", "TO1 - Tab Offset, 1 column", "TO2 - Tab Offset, 2 column", "TO3 - Tab Offset, 3 column", "RU2 - Roll up 2 rows", "RU3 - Roll up 3 rows", "RU4 - Roll up 4 rows", "CR - Carriage Return", "ENM - Erase non-displayed memory", "BS - Backspace", "RTD - Resume Text Display" }; static const char *font_text[]= { "regular", "italics", "underlined", "underlined italics" }; static const char *color_text[][2]= { {"white", "&HFFFFFF&"}, {"green", "&H00FF00&"}, {"blue", "&HFF0000&"}, {"cyan", "&HFFFF00&"}, {"red", "&H0000FF&"}, {"yellow", "&H00FFFF&"}, {"magenta", "&HFF00FF&"}, {"userdefined", "&HFFFFFF&"} }; static int general_608_init (struct s_write *wb) { if( !wb->enc_buffer ) { wb->enc_buffer=(unsigned char *) malloc (INITIAL_ENC_BUFFER_CAPACITY); if (wb->enc_buffer==NULL) return -1; wb->enc_buffer_capacity=INITIAL_ENC_BUFFER_CAPACITY; } if( !wb->subline) { wb->subline = malloc(2048); if (!wb->subline) { return -1; } } wb->new_sentence = 1; wb->new_channel = 1; wb->in_xds_mode = 0; wb->hb_buffer = NULL; wb->hb_last_buffer = NULL; wb->last_pts = 0; return 0; } /* * Free up CC memory - don't call this from HB just yet since it will cause * parallel encodes to fail - to be honest they will be stuffed anyway since * the CC's may be overwriting the buffers. */ static void general_608_close (struct s_write *wb) { if( wb->enc_buffer ) { free(wb->enc_buffer); wb->enc_buffer_capacity = 0; wb->enc_buffer_used = 0; } if( wb->subline ) { free(wb->subline); } if( wb->hb_buffer ) { hb_buffer_close( &wb->hb_buffer ); } } #include // Returns number of bytes used static int get_char_in_utf8(unsigned char *buffer, unsigned char c) { if (c == 0x00) return 0; // Regular line-21 character set, mostly ASCII except these exceptions if (c < 0x80) { switch (c) { case 0x2a: // lowercase a, acute accent *buffer = 0xc3; *(buffer+1) = 0xa1; return 2; case 0x5c: // lowercase e, acute accent *buffer = 0xc3; *(buffer+1) = 0xa9; return 2; case 0x5e: // lowercase i, acute accent *buffer = 0xc3; *(buffer+1) = 0xad; return 2; case 0x5f: // lowercase o, acute accent *buffer = 0xc3; *(buffer+1) = 0xb3; return 2; case 0x60: // lowercase u, acute accent *buffer = 0xc3; *(buffer+1) = 0xba; return 2; case 0x7b: // lowercase c with cedilla *buffer = 0xc3; *(buffer+1) = 0xa7; return 2; case 0x7c: // division symbol *buffer = 0xc3; *(buffer+1) = 0xb7; return 2; case 0x7d: // uppercase N tilde *buffer = 0xc3; *(buffer+1) = 0x91; return 2; case 0x7e: // lowercase n tilde *buffer = 0xc3; *(buffer+1) = 0xb1; return 2; default: *buffer = c; return 1; } } switch (c) { // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS // THAT COME FROM HI BYTE = 0x11 AND LOW BETWEEN 0x30 AND 0x3F case 0x80: // Registered symbol (R) *buffer = 0xc2; *(buffer+1) = 0xae; return 2; case 0x81: // degree sign *buffer = 0xc2; *(buffer+1) = 0xb0; return 2; case 0x82: // 1/2 symbol *buffer = 0xc2; *(buffer+1) = 0xbd; return 2; case 0x83: // Inverted (open) question mark *buffer = 0xc2; *(buffer+1) = 0xbf; return 2; case 0x84: // Trademark symbol (TM) *buffer = 0xe2; *(buffer+1) = 0x84; *(buffer+2) = 0xa2; return 3; case 0x85: // Cents symbol *buffer = 0xc2; *(buffer+1) = 0xa2; return 2; case 0x86: // Pounds sterling *buffer = 0xc2; *(buffer+1) = 0xa3; return 2; case 0x87: // Music note *buffer = 0xe2; *(buffer+1) = 0x99; *(buffer+2) = 0xaa; return 3; case 0x88: // lowercase a, grave accent *buffer = 0xc3; *(buffer+1) = 0xa0; return 2; case 0x89: // transparent space, we make it regular *buffer = 0x20; return 1; case 0x8a: // lowercase e, grave accent *buffer = 0xc3; *(buffer+1) = 0xa8; return 2; case 0x8b: // lowercase a, circumflex accent *buffer = 0xc3; *(buffer+1) = 0xa2; return 2; case 0x8c: // lowercase e, circumflex accent *buffer = 0xc3; *(buffer+1) = 0xaa; return 2; case 0x8d: // lowercase i, circumflex accent *buffer = 0xc3; *(buffer+1) = 0xae; return 2; case 0x8e: // lowercase o, circumflex accent *buffer = 0xc3; *(buffer+1) = 0xb4; return 2; case 0x8f: // lowercase u, circumflex accent *buffer = 0xc3; *(buffer+1) = 0xbb; return 2; // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS // THAT COME FROM HI BYTE = 0x12 AND LOW BETWEEN 0x20 AND 0x3F case 0x90: // capital letter A with acute *buffer = 0xc3; *(buffer+1) = 0x81; return 2; case 0x91: // capital letter E with acute *buffer = 0xc3; *(buffer+1) = 0x89; return 2; case 0x92: // capital letter O with acute *buffer = 0xc3; *(buffer+1) = 0x93; return 2; case 0x93: // capital letter U with acute *buffer = 0xc3; *(buffer+1) = 0x9a; return 2; case 0x94: // capital letter U with diaresis *buffer = 0xc3; *(buffer+1) = 0x9c; return 2; case 0x95: // lowercase letter U with diaeresis *buffer = 0xc3; *(buffer+1) = 0xbc; return 2; case 0x96: // apostrophe *buffer = 0x27; return 1; case 0x97: // inverted exclamation mark *buffer = 0xc2; *(buffer+1) = 0xa1; return 2; case 0x98: // asterisk *buffer = 0x2a; return 1; case 0x99: // apostrophe (yes, duped). See CCADI source code. *buffer = 0x27; return 1; case 0x9a: // hyphen-minus *buffer = 0x2d; return 1; case 0x9b: // copyright sign *buffer = 0xc2; *(buffer+1) = 0xa9; return 2; case 0x9c: // Service mark *buffer = 0xe2; *(buffer+1) = 0x84; *(buffer+2) = 0xa0; return 3; case 0x9d: // Full stop (.) *buffer = 0x2e; return 1; case 0x9e: // Quoatation mark *buffer = 0x22; return 1; case 0x9f: // Quoatation mark *buffer = 0x22; return 1; case 0xa0: // uppercase A, grave accent *buffer = 0xc3; *(buffer+1) = 0x80; return 2; case 0xa1: // uppercase A, circumflex *buffer = 0xc3; *(buffer+1) = 0x82; return 2; case 0xa2: // uppercase C with cedilla *buffer = 0xc3; *(buffer+1) = 0x87; return 2; case 0xa3: // uppercase E, grave accent *buffer = 0xc3; *(buffer+1) = 0x88; return 2; case 0xa4: // uppercase E, circumflex *buffer = 0xc3; *(buffer+1) = 0x8a; return 2; case 0xa5: // capital letter E with diaresis *buffer = 0xc3; *(buffer+1) = 0x8b; return 2; case 0xa6: // lowercase letter e with diaresis *buffer = 0xc3; *(buffer+1) = 0xab; return 2; case 0xa7: // uppercase I, circumflex *buffer = 0xc3; *(buffer+1) = 0x8e; return 2; case 0xa8: // uppercase I, with diaresis *buffer = 0xc3; *(buffer+1) = 0x8f; return 2; case 0xa9: // lowercase i, with diaresis *buffer = 0xc3; *(buffer+1) = 0xaf; return 2; case 0xaa: // uppercase O, circumflex *buffer = 0xc3; *(buffer+1) = 0x94; return 2; case 0xab: // uppercase U, grave accent *buffer = 0xc3; *(buffer+1) = 0x99; return 2; case 0xac: // lowercase u, grave accent *buffer = 0xc3; *(buffer+1) = 0xb9; return 2; case 0xad: // uppercase U, circumflex *buffer = 0xc3; *(buffer+1) = 0x9b; return 2; case 0xae: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK *buffer = 0xc2; *(buffer+1) = 0xab; return 2; case 0xaf: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK *buffer = 0xc2; *(buffer+1) = 0xbb; return 2; // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS // THAT COME FROM HI BYTE = 0x13 AND LOW BETWEEN 0x20 AND 0x3F case 0xb0: // Uppercase A, tilde *buffer = 0xc3; *(buffer+1) = 0x83; return 2; case 0xb1: // Lowercase a, tilde *buffer = 0xc3; *(buffer+1) = 0xa3; return 2; case 0xb2: // Uppercase I, acute accent *buffer = 0xc3; *(buffer+1) = 0x8d; return 2; case 0xb3: // Uppercase I, grave accent *buffer = 0xc3; *(buffer+1) = 0x8c; return 2; case 0xb4: // Lowercase i, grave accent *buffer = 0xc3; *(buffer+1) = 0xac; return 2; case 0xb5: // Uppercase O, grave accent *buffer = 0xc3; *(buffer+1) = 0x92; return 2; case 0xb6: // Lowercase o, grave accent *buffer = 0xc3; *(buffer+1) = 0xb2; return 2; case 0xb7: // Uppercase O, tilde *buffer = 0xc3; *(buffer+1) = 0x95; return 2; case 0xb8: // Lowercase o, tilde *buffer = 0xc3; *(buffer+1) = 0xb5; return 2; case 0xb9: // Open curly brace *buffer = 0x7b; return 1; case 0xba: // Closing curly brace *buffer = 0x7d; return 1; case 0xbb: // Backslash *buffer = 0x5c; return 1; case 0xbc: // Caret *buffer = 0x5e; return 1; case 0xbd: // Underscore *buffer = 0x5f; return 1; case 0xbe: // Pipe (broken bar) *buffer = 0xc2; *(buffer+1) = 0xa6; return 1; case 0xbf: // Tilde *buffer = 0x7e; // Not sure return 1; case 0xc0: // Uppercase A, umlaut *buffer = 0xc3; *(buffer+1) = 0x84; return 2; case 0xc1: // Lowercase A, umlaut *buffer = 0xc3; *(buffer+1) = 0xa4; return 2; case 0xc2: // Uppercase O, umlaut *buffer = 0xc3; *(buffer+1) = 0x96; return 2; case 0xc3: // Lowercase o, umlaut *buffer = 0xc3; *(buffer+1) = 0xb6; return 2; case 0xc4: // Esszett (sharp S) *buffer = 0xc3; *(buffer+1) = 0x9f; return 2; case 0xc5: // Yen symbol *buffer = 0xc2; *(buffer+1) = 0xa5; return 2; case 0xc6: // Currency symbol *buffer = 0xc2; *(buffer+1) = 0xa4; return 2; case 0xc7: // Vertical bar *buffer = 0x7c; return 1; case 0xc8: // Uppercase A, ring *buffer = 0xc3; *(buffer+1) = 0x85; return 2; case 0xc9: // Lowercase A, ring *buffer = 0xc3; *(buffer+1) = 0xa5; return 2; case 0xca: // Uppercase O, slash *buffer = 0xc3; *(buffer+1) = 0x98; return 2; case 0xcb: // Lowercase o, slash *buffer = 0xc3; *(buffer+1) = 0xb8; return 2; case 0xcc: // Upper left corner *buffer = 0xe2; *(buffer+1) = 0x8c; *(buffer+2) = 0x9c; return 3; case 0xcd: // Upper right corner *buffer = 0xe2; *(buffer+1) = 0x8c; *(buffer+2) = 0x9d; return 3; case 0xce: // Lower left corner *buffer = 0xe2; *(buffer+1) = 0x8c; *(buffer+2) = 0x9e; return 3; case 0xcf: // Lower right corner *buffer = 0xe2; *(buffer+1) = 0x8c; *(buffer+2) = 0x9f; return 3; default: // *buffer = '?'; // I'll do it eventually, I promise return 1; // This are weird chars anyway } } // Encodes a generic string. Note that since we use the encoders for closed // caption data, text would have to be encoded as CCs... so using special // characters here it's a bad idea. static unsigned encode_line(unsigned char *buffer, unsigned char *text) { unsigned bytes = 0; while (*text) { *buffer++ = *text++; bytes++; } return bytes; } static void find_limit_characters(unsigned char *line, int *first_non_blank, int *last_non_blank) { int i; *last_non_blank = -1; *first_non_blank = -1; for (i = 0; i < CC608_SCREEN_WIDTH; i++) { unsigned char c = line[i]; if (c != ' ' && c != 0x89) { if (*first_non_blank == -1) *first_non_blank = i; *last_non_blank = i; } } } static unsigned get_decoder_line_encoded(struct s_write *wb, unsigned char *buffer, int line_num, struct eia608_screen *data) { uint8_t font_style; uint8_t font_color; int i; unsigned char *line = data->characters[line_num]; unsigned char *orig = buffer; // Keep for debugging int first = 0, last = 31; find_limit_characters(line, &first, &last); for (i = first; i <= last; i++) { // Handle color font_color = data->colors[line_num][i]; font_style = data->fonts[line_num][i]; // Handle reset to defaults if ((font_style & FONT_STYLE_MASK) == 0 && font_color == COL_WHITE) { if (((font_style ^ wb->prev_font_style) & FONT_STYLE_MASK) || (font_color != wb->prev_font_color)) { buffer += encode_line(buffer, (uint8_t*)"{\\r}"); } } else { // Open markup if (((font_style ^ wb->prev_font_style) & FONT_STYLE_MASK) || (font_color != wb->prev_font_color)) { // style changed buffer += encode_line(buffer, (uint8_t*)"{"); } // Handle underlined if ((font_style ^ wb->prev_font_style) & FONT_UNDERLINED) { int enable = !!(font_style & FONT_UNDERLINED); buffer += encode_line(buffer, (uint8_t*)"\\u"); *buffer++ = enable + 0x30; } // Handle italics if ((font_style ^ wb->prev_font_style) & FONT_ITALICS) { int enable = !!(font_style & FONT_ITALICS); buffer += encode_line(buffer, (uint8_t*)"\\i"); *buffer++ = enable + 0x30; } // Handle color if (font_color != wb->prev_font_color) { buffer += encode_line(buffer, (uint8_t*)"\\1c"); buffer += encode_line(buffer, (uint8_t*)color_text[font_color][1]); } // Close markup if (((font_style ^ wb->prev_font_style) & FONT_STYLE_MASK) || (font_color != wb->prev_font_color)) { // style changed buffer += encode_line(buffer, (uint8_t*)"}"); } } wb->prev_font_style = font_style; wb->prev_font_color = font_color; int bytes = 0; bytes = get_char_in_utf8(buffer, line[i]); buffer += bytes; } *buffer = 0; return (unsigned) (buffer - orig); // Return length } static void clear_eia608_cc_buffer (struct eia608_screen *data) { int i; for (i=0;i<15;i++) { memset(data->characters[i],' ',CC608_SCREEN_WIDTH); data->characters[i][CC608_SCREEN_WIDTH]=0; memset (data->colors[i],default_color,CC608_SCREEN_WIDTH+1); memset (data->fonts[i],FONT_REGULAR,CC608_SCREEN_WIDTH+1); data->row_used[i]=0; } data->empty=1; } static void init_eia608 (struct eia608 *data) { data->cursor_column = 0; data->cursor_row = 0; clear_eia608_cc_buffer (&data->buffer1); clear_eia608_cc_buffer (&data->buffer2); data->visible_buffer = 1; data->last_c1 = 0; data->last_c2 = 0; data->mode = MODE_POPUP; data->current_visible_start_ms = 0; data->ssa_counter = 0; data->screenfuls_counter = 0; data->channel = 1; data->color = default_color; data->font = FONT_REGULAR; data->rollup_base_row = 14; } static struct eia608_screen *get_current_hidden_buffer(struct s_write *wb) { struct eia608_screen *data; if (wb->data608->visible_buffer == 1) data = &wb->data608->buffer2; else data = &wb->data608->buffer1; return data; } static struct eia608_screen *get_current_visible_buffer(struct s_write *wb) { struct eia608_screen *data; if (wb->data608->visible_buffer == 1) data = &wb->data608->buffer1; else data = &wb->data608->buffer2; return data; } static void swap_visible_buffer(struct s_write *wb) { wb->data608->visible_buffer = (wb->data608->visible_buffer == 1) ? 2 : 1; } static struct eia608_screen *get_writing_buffer(struct s_write *wb) { struct eia608_screen *use_buffer=NULL; switch (wb->data608->mode) { case MODE_POPUP: // Write on the non-visible buffer use_buffer = get_current_hidden_buffer(wb); break; case MODE_ROLLUP_2: // Write directly to screen case MODE_ROLLUP_3: case MODE_ROLLUP_4: use_buffer = get_current_visible_buffer(wb); break; default: fatal (EXIT_BUG_BUG, "Caption mode has an illegal value at get_writing_buffer(), this is a bug.\n"); } return use_buffer; } static void write_char(const unsigned char c, struct s_write *wb) { if (wb->data608->mode != MODE_TEXT) { struct eia608_screen * use_buffer = get_writing_buffer(wb); /* hb_log ("\rWriting char [%c] at %s:%d:%d\n",c, use_buffer == &wb->data608->buffer1?"B1":"B2", wb->data608->cursor_row,wb->data608->cursor_column); */ use_buffer->characters[wb->data608->cursor_row][wb->data608->cursor_column] = c; use_buffer->colors[wb->data608->cursor_row][wb->data608->cursor_column] = wb->data608->color; use_buffer->fonts[wb->data608->cursor_row][wb->data608->cursor_column] = wb->data608->font; use_buffer->row_used[wb->data608->cursor_row] = 1; use_buffer->empty = 0; if (wb->data608->cursor_column < 31) wb->data608->cursor_column++; use_buffer->dirty = 1; } } /* Handle MID-ROW CODES. */ static void handle_text_attr(const unsigned char c1, const unsigned char c2, struct s_write *wb) { // Handle channel change wb->data608->channel=wb->new_channel; if (wb->data608->channel!=cc_channel) return; if (debug_608) hb_log ("\r608: text_attr: %02X %02X",c1,c2); if ( ((c1!=0x11 && c1!=0x19) || (c2<0x20 || c2>0x2f)) && debug_608) { hb_log ("\rThis is not a text attribute!\n"); } else { int i = c2-0x20; wb->data608->color=pac2_attribs[i][0]; wb->data608->font=pac2_attribs[i][1]; if (debug_608) hb_log(" -- Color: %s, font: %s\n", color_text[wb->data608->color][0], font_text[wb->data608->font]); if (wb->data608->cursor_column<31) wb->data608->cursor_column++; } } static int write_cc_buffer_as_ssa(struct eia608_screen *data, struct s_write *wb) { int wrote_something = 0; int i; int64_t ms_start = wb->data608->current_visible_start_ms; //int64_t ms_end = get_last_pts(wb) + subs_delay; int row = -1, col = -1; ms_start += subs_delay; if (ms_start<0) // Drop screens that because of subs_delay start too early return 0; if (debug_608) { char timeline[128]; wb->data608->ssa_counter++; sprintf (timeline,"%u\r\n",wb->data608->ssa_counter); hb_log ("\n- - - SSA caption - - -\n"); hb_log ("%s", timeline); } /* * Write all the lines into enc_buffer, and then write that out at the end * ensure that we only have two lines, insert a newline after the first one, * and have a big bottom line (strip spaces from any joined lines). */ int rows = 0, columns = 0; for (i = 0; i < 15; i++) { if (data->row_used[i]) { int first, last; rows++; find_limit_characters(data->characters[i], &first, &last); if (last - first + 1 > columns) columns = last - first + 1; } } wb->prev_font_style = FONT_REGULAR; wb->prev_font_color = COL_WHITE; wb->enc_buffer_used = 0; int line = 1; for (i = 0; i < 15; i++) { if (data->row_used[i]) { // Get position for this CC if (row == -1) { int last, x, y, top, safe_zone, cell_width, cell_height; int cropped_width, cropped_height, font_size; char *pos; row = i; find_limit_characters(data->characters[i], &col, &last); // CC grid is 16 rows by 62 colums // Our SSA resolution is the title resolution // Tranlate CC grid to SSA coordinates // The numbers are tweaked to keep things off the very // edges of the screen and in the "safe" zone cropped_height = wb->height - wb->crop[0] - wb->crop[1]; cropped_width = wb->width - wb->crop[2] - wb->crop[3]; font_size = cropped_height * .066; safe_zone = cropped_height * 0.025; cell_height = (wb->height - 2 * safe_zone) / 16; cell_width = (wb->width - 2 * safe_zone) / 32; // Calculate position assuming the position defines // the baseline of the text which is lower left corner // of bottom row of characters y = cell_height * (row + 1 + rows) + safe_zone - wb->crop[0]; top = y - rows * font_size; x = cell_width * col + safe_zone - wb->crop[2]; if (top < safe_zone) y = (rows * font_size) + safe_zone; if (y > cropped_height - safe_zone) y = cropped_height - safe_zone; if (x + columns * cell_width > cropped_width - safe_zone) x = cropped_width - columns * cell_width - safe_zone; if (x < safe_zone) x = safe_zone; pos = hb_strdup_printf("{\\a1\\pos(%d,%d)}", x, y); wb->enc_buffer_used += encode_line( wb->enc_buffer + wb->enc_buffer_used, (uint8_t*)pos); free(pos); } /* * The intention was to use a newline but QT doesn't like it, * old code still here just in case.. */ if (line == 1) { wb->enc_buffer_used += get_decoder_line_encoded(wb, wb->enc_buffer + wb->enc_buffer_used, i, data); line = 2; } else { wb->enc_buffer_used += encode_line( wb->enc_buffer + wb->enc_buffer_used, (uint8_t*)"\\N"); wb->enc_buffer_used += get_decoder_line_encoded(wb, wb->enc_buffer + wb->enc_buffer_used, i, data); } } } if (wb->enc_buffer_used && wb->enc_buffer[0] != 0 && data->dirty) { hb_buffer_t *buffer; int len; // bump past null terminator wb->enc_buffer_used++; buffer = hb_buffer_init(wb->enc_buffer_used + SSA_PREAMBLE_LEN); buffer->s.frametype = HB_FRAME_SUBTITLE; buffer->s.start = ms_start; buffer->s.stop = AV_NOPTS_VALUE; sprintf((char*)buffer->data, "%d,,Default,,0,0,0,,", ++wb->line); len = strlen((char*)buffer->data); memcpy(buffer->data + len, wb->enc_buffer, wb->enc_buffer_used); if (wb->hb_last_buffer) { wb->hb_last_buffer->next = buffer; } else { wb->hb_buffer = buffer; } wb->hb_last_buffer = buffer; wrote_something=1; wb->clear_sub_needed = 1; } else if (wb->clear_sub_needed) { hb_buffer_t *buffer = hb_buffer_init(1); buffer->s.frametype = HB_FRAME_SUBTITLE; buffer->s.start = ms_start; buffer->s.stop = ms_start; buffer->data[0] = 0; if (wb->hb_last_buffer != NULL) { wb->hb_last_buffer->next = buffer; } else { wb->hb_buffer = buffer; } wb->hb_last_buffer = buffer; wb->clear_sub_needed = 0; } if (debug_608) { hb_log ("- - - - - - - - - - - -\r\n"); } return wrote_something; } static int write_cc_buffer(struct s_write *wb) { struct eia608_screen *data; int wrote_something=0; data = get_current_visible_buffer(wb); if (!data->dirty) return 0; wb->new_sentence=1; wrote_something = write_cc_buffer_as_ssa(data, wb); data->dirty = 0; return wrote_something; } static void move_roll_up(struct s_write *wb, int row) { struct eia608_screen *use_buffer; int ii, src, dst, keep_lines; switch (wb->data608->mode) { case MODE_ROLLUP_2: keep_lines = 2; break; case MODE_ROLLUP_3: keep_lines = 3; break; case MODE_ROLLUP_4: keep_lines = 4; break; default: // Not rollup mode, nothing to do return; } if (row == wb->data608->rollup_base_row) { // base row didn't change, nothing to do return; } use_buffer = get_current_visible_buffer(wb); if (row < wb->data608->rollup_base_row) { src = wb->data608->rollup_base_row - keep_lines + 1; dst = row - keep_lines + 1; for (ii = 0; ii < keep_lines; ii++) { memcpy(use_buffer->characters[dst], use_buffer->characters[src], CC608_SCREEN_WIDTH+1); memcpy(use_buffer->colors[dst], use_buffer->colors[src], CC608_SCREEN_WIDTH+1); memcpy(use_buffer->fonts[dst], use_buffer->fonts[src], CC608_SCREEN_WIDTH+1); use_buffer->row_used[dst] = use_buffer->row_used[src]; memset(use_buffer->characters[src], ' ', CC608_SCREEN_WIDTH); memset(use_buffer->colors[src], COL_WHITE, CC608_SCREEN_WIDTH); memset(use_buffer->fonts[src], FONT_REGULAR, CC608_SCREEN_WIDTH); use_buffer->characters[src][CC608_SCREEN_WIDTH] = 0; use_buffer->row_used[src] = 0; src++; dst++; } } else { src = wb->data608->rollup_base_row; dst = row; for (ii = 0; ii < keep_lines; ii++) { memcpy(use_buffer->characters[dst], use_buffer->characters[src], CC608_SCREEN_WIDTH+1); memcpy(use_buffer->colors[dst], use_buffer->colors[src], CC608_SCREEN_WIDTH+1); memcpy(use_buffer->fonts[dst], use_buffer->fonts[src], CC608_SCREEN_WIDTH+1); use_buffer->row_used[dst] = use_buffer->row_used[src]; memset(use_buffer->characters[src], ' ', CC608_SCREEN_WIDTH); memset(use_buffer->colors[src], COL_WHITE, CC608_SCREEN_WIDTH); memset(use_buffer->fonts[src], FONT_REGULAR, CC608_SCREEN_WIDTH); use_buffer->characters[src][CC608_SCREEN_WIDTH] = 0; use_buffer->row_used[src] = 0; src--; dst--; } } use_buffer->dirty = 1; } static void roll_up(struct s_write *wb) { struct eia608_screen *use_buffer; int i, j; use_buffer = get_current_visible_buffer(wb); int keep_lines; switch (wb->data608->mode) { case MODE_ROLLUP_2: keep_lines = 2; break; case MODE_ROLLUP_3: keep_lines = 3; break; case MODE_ROLLUP_4: keep_lines = 4; break; default: // Shouldn't happen keep_lines = 0; break; } int firstrow = -1, lastrow = -1; // Look for the last line used int rows_now = 0; // Number of rows in use right now for (i = 0; i < 15; i++) { if (use_buffer->row_used[i]) { rows_now++; if (firstrow == -1) firstrow = i; lastrow = i; } } if (debug_608) hb_log ("\rIn roll-up: %d lines used, first: %d, last: %d\n", rows_now, firstrow, lastrow); if (lastrow==-1) // Empty screen, nothing to rollup return; for (j = lastrow - keep_lines + 1; j < lastrow; j++) { if (j >= 0) { memcpy(use_buffer->characters[j], use_buffer->characters[j+1], CC608_SCREEN_WIDTH+1); memcpy(use_buffer->colors[j], use_buffer->colors[j+1], CC608_SCREEN_WIDTH+1); memcpy(use_buffer->fonts[j], use_buffer->fonts[j+1], CC608_SCREEN_WIDTH+1); use_buffer->row_used[j] = use_buffer->row_used[j+1]; } } for (j = 0; j < (1 + wb->data608->cursor_row - keep_lines); j++) { memset(use_buffer->characters[j], ' ', CC608_SCREEN_WIDTH); memset(use_buffer->colors[j], COL_WHITE, CC608_SCREEN_WIDTH); memset(use_buffer->fonts[j], FONT_REGULAR, CC608_SCREEN_WIDTH); use_buffer->characters[j][CC608_SCREEN_WIDTH] = 0; use_buffer->row_used[j] = 0; } memset(use_buffer->characters[lastrow], ' ', CC608_SCREEN_WIDTH); memset(use_buffer->colors[lastrow], COL_WHITE, CC608_SCREEN_WIDTH); memset(use_buffer->fonts[lastrow], FONT_REGULAR, CC608_SCREEN_WIDTH); use_buffer->characters[lastrow][CC608_SCREEN_WIDTH] = 0; use_buffer->row_used[lastrow] = 0; // Sanity check rows_now = 0; for (i = 0; i < 15; i++) if (use_buffer->row_used[i]) rows_now++; if (rows_now > keep_lines) hb_log ("Bug in roll_up, should have %d lines but I have %d.\n", keep_lines, rows_now); use_buffer->dirty = 1; } void erase_memory (struct s_write *wb, int displayed) { struct eia608_screen *buf; if (displayed) { buf = get_current_visible_buffer(wb); } else { buf = get_current_hidden_buffer(wb); } clear_eia608_cc_buffer (buf); } static int is_current_row_empty (struct s_write *wb) { struct eia608_screen *use_buffer; int i; use_buffer = get_current_visible_buffer(wb); for (i=0;icharacters[wb->data608->rollup_base_row][i]!=' ') return 0; } return 1; } /* Process GLOBAL CODES */ static void handle_command(unsigned char c1, const unsigned char c2, struct s_write *wb) { // Handle channel change wb->data608->channel=wb->new_channel; if (wb->data608->channel!=cc_channel) return; enum command_code command = COM_UNKNOWN; if (c1==0x15) c1=0x14; if ((c1==0x14 || c1==0x1C) && c2==0x2C) command = COM_ERASEDISPLAYEDMEMORY; if ((c1==0x14 || c1==0x1C) && c2==0x20) command = COM_RESUMECAPTIONLOADING; if ((c1==0x14 || c1==0x1C) && c2==0x2F) command = COM_ENDOFCAPTION; if ((c1==0x17 || c1==0x1F) && c2==0x21) command = COM_TABOFFSET1; if ((c1==0x17 || c1==0x1F) && c2==0x22) command = COM_TABOFFSET2; if ((c1==0x17 || c1==0x1F) && c2==0x23) command = COM_TABOFFSET3; if ((c1==0x14 || c1==0x1C) && c2==0x25) command = COM_ROLLUP2; if ((c1==0x14 || c1==0x1C) && c2==0x26) command = COM_ROLLUP3; if ((c1==0x14 || c1==0x1C) && c2==0x27) command = COM_ROLLUP4; if ((c1==0x14 || c1==0x1C) && c2==0x2D) command = COM_CARRIAGERETURN; if ((c1==0x14 || c1==0x1C) && c2==0x2E) command = COM_ERASENONDISPLAYEDMEMORY; if ((c1==0x14 || c1==0x1C) && c2==0x21) command = COM_BACKSPACE; if ((c1==0x14 || c1==0x1C) && c2==0x2b) command = COM_RESUMETEXTDISPLAY; if (debug_608) { hb_log ("\rCommand: %02X %02X (%s)\n",c1,c2,command_type[command]); } switch (command) { case COM_BACKSPACE: if (wb->data608->cursor_column>0) { struct eia608_screen *data; data = get_writing_buffer(wb); wb->data608->cursor_column--; data->characters[wb->data608->cursor_row][wb->data608->cursor_column] = ' '; data->dirty = 1; } break; case COM_TABOFFSET1: if (wb->data608->cursor_column<31) wb->data608->cursor_column++; break; case COM_TABOFFSET2: wb->data608->cursor_column+=2; if (wb->data608->cursor_column>31) wb->data608->cursor_column=31; break; case COM_TABOFFSET3: wb->data608->cursor_column+=3; if (wb->data608->cursor_column>31) wb->data608->cursor_column=31; break; case COM_RESUMECAPTIONLOADING: wb->data608->mode=MODE_POPUP; wb->data608->current_visible_start_ms = get_last_pts(wb); break; case COM_RESUMETEXTDISPLAY: wb->data608->mode=MODE_TEXT; wb->data608->current_visible_start_ms = get_last_pts(wb); break; case COM_ROLLUP2: if (wb->data608->rollup_base_row + 1 < 2) { move_roll_up(wb, 1); wb->data608->rollup_base_row = 1; } if (wb->data608->mode==MODE_POPUP) { swap_visible_buffer(wb); if (write_cc_buffer(wb)) wb->data608->screenfuls_counter++; erase_memory (wb, 1); } wb->data608->color=default_color; wb->data608->font=FONT_REGULAR; if (wb->data608->mode==MODE_ROLLUP_2 && !is_current_row_empty(wb)) { if (debug_608) hb_log ("Two RU2, current line not empty. Simulating a CR\n"); handle_command(0x14, 0x2D, wb); wb->rollup_cr = 1; } wb->data608->current_visible_start_ms = get_last_pts(wb); wb->data608->mode=MODE_ROLLUP_2; erase_memory (wb, 0); wb->data608->cursor_column = 0; wb->data608->cursor_row = wb->data608->rollup_base_row; break; case COM_ROLLUP3: if (wb->data608->rollup_base_row + 1 < 3) { move_roll_up(wb, 2); wb->data608->rollup_base_row = 2; } if (wb->data608->mode==MODE_POPUP) { if (write_cc_buffer(wb)) wb->data608->screenfuls_counter++; erase_memory (wb, 1); } wb->data608->color=default_color; wb->data608->font=FONT_REGULAR; if (wb->data608->mode==MODE_ROLLUP_3 && !is_current_row_empty(wb)) { if (debug_608) hb_log ("Two RU3, current line not empty. Simulating a CR\n"); handle_command(0x14, 0x2D, wb); wb->rollup_cr = 1; } wb->data608->current_visible_start_ms = get_last_pts(wb); wb->data608->mode=MODE_ROLLUP_3; erase_memory (wb, 0); wb->data608->cursor_column = 0; wb->data608->cursor_row = wb->data608->rollup_base_row; break; case COM_ROLLUP4: if (wb->data608->rollup_base_row + 1 < 4) { move_roll_up(wb, 3); wb->data608->rollup_base_row = 3; } if (wb->data608->mode==MODE_POPUP) { if (write_cc_buffer(wb)) wb->data608->screenfuls_counter++; erase_memory (wb, 1); } wb->data608->color=default_color; wb->data608->font=FONT_REGULAR; if (wb->data608->mode==MODE_ROLLUP_4 && !is_current_row_empty(wb)) { if (debug_608) hb_log ("Two RU4, current line not empty. Simulating a CR\n"); handle_command(0x14, 0x2D, wb); wb->rollup_cr = 1; } wb->data608->current_visible_start_ms = get_last_pts(wb); wb->data608->mode = MODE_ROLLUP_4; wb->data608->cursor_column = 0; wb->data608->cursor_row = wb->data608->rollup_base_row; erase_memory (wb, 0); break; case COM_CARRIAGERETURN: // In transcript mode, CR doesn't write the whole screen, to avoid // repeated lines. // Skip initial CR if rollup has already done it if (wb->rollup_cr && is_current_row_empty(wb)) { wb->rollup_cr = 0; wb->data608->current_visible_start_ms = get_last_pts(wb); break; } if (write_cc_buffer(wb)) wb->data608->screenfuls_counter++; roll_up(wb); wb->data608->cursor_column = 0; wb->data608->current_visible_start_ms = get_last_pts(wb); break; case COM_ERASENONDISPLAYEDMEMORY: erase_memory (wb,0); break; case COM_ERASEDISPLAYEDMEMORY: // There may be "displayed" rollup data that has not been // written to a buffer yet. if (wb->data608->mode == MODE_ROLLUP_2 || wb->data608->mode == MODE_ROLLUP_3 || wb->data608->mode == MODE_ROLLUP_4) { write_cc_buffer(wb); } erase_memory (wb,1); // the last pts is the time to remove the previously // displayed CC from the display wb->data608->current_visible_start_ms = get_last_pts(wb); // Write "clear" subtitle if necessary struct eia608_screen *data; data = get_current_visible_buffer(wb); data->dirty = 1; write_cc_buffer(wb); break; case COM_ENDOFCAPTION: // Switch buffers // The currently *visible* buffer is leaving, so now we know it's ending // time. Time to actually write it to file. if (wb->data608->mode == MODE_POPUP) { swap_visible_buffer(wb); wb->data608->current_visible_start_ms = get_last_pts(wb); } if (write_cc_buffer(wb)) wb->data608->screenfuls_counter++; if (wb->data608->mode != MODE_POPUP) swap_visible_buffer(wb); wb->data608->cursor_column = 0; wb->data608->cursor_row = 0; wb->data608->color=default_color; wb->data608->font=FONT_REGULAR; break; default: if (debug_608) { hb_log ("\rNot yet implemented.\n"); } break; } } static void handle_end_of_data(struct s_write *wb) { // We issue a EraseDisplayedMemory here so if there's any captions pending // they get written to file. handle_command (0x14, 0x2c, wb); // EDM } static void handle_double(const unsigned char c1, const unsigned char c2, struct s_write *wb) { unsigned char c; if (wb->data608->channel!=cc_channel) return; if (c2>=0x30 && c2<=0x3f) { c=c2 + 0x50; // So if c>=0x80 && c<=0x8f, it comes from here if (debug_608) hb_log ("\rDouble: %02X %02X --> %c\n",c1,c2,c); write_char(c,wb); } } /* Process EXTENDED CHARACTERS */ static unsigned char handle_extended(unsigned char hi, unsigned char lo, struct s_write *wb) { // Handle channel change if (wb->new_channel > 2) { wb->new_channel -= 2; if (debug_608) hb_log ("\nChannel correction, now %d\n", wb->new_channel); } wb->data608->channel=wb->new_channel; if (wb->data608->channel!=cc_channel) return 0; // For lo values between 0x20-0x3f unsigned char c=0; if (debug_608) hb_log ("\rExtended: %02X %02X\n",hi,lo); if (lo>=0x20 && lo<=0x3f && (hi==0x12 || hi==0x13)) { switch (hi) { case 0x12: c=lo+0x70; // So if c>=0x90 && c<=0xaf it comes from here break; case 0x13: c=lo+0x90; // So if c>=0xb0 && c<=0xcf it comes from here break; } // This column change is because extended characters replace // the previous character (which is sent for basic decoders // to show something similar to the real char) if (wb->data608->cursor_column>0) wb->data608->cursor_column--; write_char (c,wb); } return 1; } /* Process PREAMBLE ACCESS CODES (PAC) */ static void handle_pac(unsigned char c1, unsigned char c2, struct s_write *wb) { // Handle channel change if (wb->new_channel > 2) { wb->new_channel -= 2; if (debug_608) hb_log ("\nChannel correction, now %d\n", wb->new_channel); } wb->data608->channel=wb->new_channel; if (wb->data608->channel!=cc_channel) return; int row=rowdata[((c1<<1)&14)|((c2>>5)&1)]; if (debug_608) hb_log ("\rPAC: %02X %02X",c1,c2); if (c2>=0x40 && c2<=0x5f) { c2=c2-0x40; } else { if (c2>=0x60 && c2<=0x7f) { c2=c2-0x60; } else { if (debug_608) hb_log ("\rThis is not a PAC!!!!!\n"); return; } } int color=pac2_attribs[c2][0]; int font=pac2_attribs[c2][1]; int indent=pac2_attribs[c2][2]; if (debug_608) hb_log (" -- Position: %d:%d, color: %s, font: %s\n",row, indent,color_text[color][0],font_text[font]); // CC spec says to the preferred method to handle a roll-up base row // that causes the display to scroll off the top of the screen is to // adjust the base row down. int keep_lines; switch (wb->data608->mode) { case MODE_ROLLUP_2: keep_lines = 2; break; case MODE_ROLLUP_3: keep_lines = 3; break; case MODE_ROLLUP_4: keep_lines = 4; break; default: // Not rollup mode, all rows ok keep_lines = 0; break; } if (row < keep_lines) { row = keep_lines; } if (wb->data608->mode != MODE_TEXT) { // According to Robson, row info is discarded in text mode // but column is accepted // // CC-608 spec says current rollup display must move to the // new position when the cursor row changes move_roll_up(wb, row - 1); wb->data608->cursor_row = row - 1 ; // Since the array is 0 based } wb->data608->rollup_base_row = row - 1; wb->data608->cursor_column = indent; } static void handle_single(const unsigned char c1, struct s_write *wb) { if (c1<0x20 || wb->data608->channel!=cc_channel) return; // We don't allow special stuff here if (debug_608) hb_log ("%c",c1); /*if (debug_608) hb_log ("Character: %02X (%c) -> %02X (%c)\n",c1,c1,c,c); */ write_char (c1,wb); } static int check_channel(unsigned char c1, struct s_write *wb) { if (c1==0x14) { if (debug_608 && wb->data608->channel!=1) hb_log ("\nChannel change, now 1\n"); return 1; } if (c1==0x1c) { if (debug_608 && wb->data608->channel!=2) hb_log ("\nChannel change, now 2\n"); return 2; } if (c1==0x15) { if (debug_608 && wb->data608->channel!=3) hb_log ("\nChannel change, now 3\n"); return 3; } if (c1==0x1d) { if (debug_608 && wb->data608->channel!=4) hb_log ("\nChannel change, now 4\n"); return 4; } // Otherwise keep the current channel return wb->data608->channel; } /* Handle Command, special char or attribute and also check for * channel changes. * Returns 1 if something was written to screen, 0 otherwise */ static int disCommand(unsigned char hi, unsigned char lo, struct s_write *wb) { int wrote_to_screen=0; /* Full channel changes are only allowed for "GLOBAL CODES", * "OTHER POSITIONING CODES", "BACKGROUND COLOR CODES", * "MID-ROW CODES". * "PREAMBLE ACCESS CODES", "BACKGROUND COLOR CODES" and * SPECIAL/SPECIAL CHARACTERS allow only switching * between 1&3 or 2&4. */ wb->new_channel = check_channel (hi,wb); //if (wb->data608->channel!=cc_channel) // continue; if (hi>=0x18 && hi<=0x1f) hi=hi-8; switch (hi) { case 0x10: if (lo>=0x40 && lo<=0x5f) handle_pac (hi,lo,wb); break; case 0x11: if (lo>=0x20 && lo<=0x2f) handle_text_attr (hi,lo,wb); if (lo>=0x30 && lo<=0x3f) { wrote_to_screen=1; handle_double (hi,lo,wb); } if (lo>=0x40 && lo<=0x7f) handle_pac (hi,lo,wb); break; case 0x12: case 0x13: if (lo>=0x20 && lo<=0x3f) { wrote_to_screen=handle_extended (hi,lo,wb); } if (lo>=0x40 && lo<=0x7f) handle_pac (hi,lo,wb); break; case 0x14: case 0x15: if (lo>=0x20 && lo<=0x2f) handle_command (hi,lo,wb); if (lo>=0x40 && lo<=0x7f) handle_pac (hi,lo,wb); break; case 0x16: if (lo>=0x40 && lo<=0x7f) handle_pac (hi,lo,wb); break; case 0x17: if (lo>=0x21 && lo<=0x22) handle_command (hi,lo,wb); if (lo>=0x2e && lo<=0x2f) handle_text_attr (hi,lo,wb); if (lo>=0x40 && lo<=0x7f) handle_pac (hi,lo,wb); break; } return wrote_to_screen; } static void process608(const unsigned char *data, int length, struct s_write *wb) { static int textprinted = 0; int i; if (data!=NULL) { for (i=0;i=0x01 && hi<=0x0E) { // XDS crap - mode. Would be nice to support it eventually // wb->data608->last_c1=0; // wb->data608->last_c2=0; wb->data608->channel=3; // Always channel 3 wb->in_xds_mode=1; } if (hi==0x0F) // End of XDS block { wb->in_xds_mode=0; continue; } if (hi>=0x10 && hi<0x1F) // Non-character code or special/extended char // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CODES.HTML // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CHARS.HTML { // We were writing characters before, start a new line for // diagnostic output from disCommand() if (debug_608 && textprinted == 1 ) { hb_log("\n"); textprinted = 0; } wb->in_xds_mode=0; // Back to normal if (wb->data608->last_c1==hi && wb->data608->last_c2==lo) { // Duplicate dual code, discard continue; } wb->data608->last_c1=hi; wb->data608->last_c2=lo; disCommand (hi,lo,wb); } if (hi>=0x20) // Standard characters (always in pairs) { // Only print if the channel is active if (wb->data608->channel!=cc_channel) continue; if (debug_608) { if( textprinted == 0 ) { hb_log("\n"); textprinted = 1; } } handle_single(hi,wb); handle_single(lo,wb); wb->data608->last_c1=0; wb->data608->last_c2=0; } if ( debug_608 && !textprinted && wb->data608->channel==cc_channel ) { // Current FTS information after the characters are shown //hb_log("Current FTS: %s\n", print_mstime(get_last_pts())); } if ((wb->data608->mode == MODE_ROLLUP_2 || wb->data608->mode == MODE_ROLLUP_3 || wb->data608->mode == MODE_ROLLUP_4) && wb->direct_rollup) { // If we are showing rollup on the fly (direct_rollup) // write a buffer now write_cc_buffer(wb); wb->data608->current_visible_start_ms = get_last_pts(wb); } } } } struct hb_work_private_s { hb_job_t * job; struct s_write * cc608; }; static int decccInit( hb_work_object_t * w, hb_job_t * job ) { int retval = 1; hb_work_private_t * pv; pv = calloc( 1, sizeof( hb_work_private_t ) ); if( pv ) { w->private_data = pv; pv->job = job; pv->cc608 = calloc(1, sizeof(struct s_write)); if( pv->cc608 ) { pv->cc608->width = job->title->width; pv->cc608->height = job->title->height; memcpy(pv->cc608->crop, job->crop, sizeof(int[4])); retval = general_608_init(pv->cc608); if( !retval ) { pv->cc608->data608 = calloc(1, sizeof(struct eia608)); if( !pv->cc608->data608 ) { retval = 1; } init_eia608(pv->cc608->data608); } } } if (!retval) { // Generate generic SSA Script Info. int height = job->title->height - job->crop[0] - job->crop[1]; int width = job->title->width - job->crop[2] - job->crop[3]; hb_subtitle_add_ssa_header(w->subtitle, width, height); } // When rendering subs, we need to push rollup subtitles out // asap (instead of waiting for a completed line) so that we // do not miss the frame that they should be rendered over. pv->cc608->direct_rollup = w->subtitle->config.dest == RENDERSUB; return retval; } static int decccWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_buffer_t * in = *buf_in; if ( in->size <= 0 ) { /* EOF on input stream - send it downstream & say that we're done */ handle_end_of_data(pv->cc608); /* * Grab any pending buffer and output them with the EOF on the end */ if (pv->cc608->hb_last_buffer) { pv->cc608->hb_last_buffer->next = in; *buf_out = pv->cc608->hb_buffer; *buf_in = NULL; pv->cc608->hb_buffer = NULL; pv->cc608->hb_last_buffer = NULL; } else { *buf_out = in; *buf_in = NULL; } return HB_WORK_DONE; } pv->cc608->last_pts = in->s.start; process608(in->data, in->size, pv->cc608); /* * If there is one waiting then pass it on */ *buf_out = pv->cc608->hb_buffer; pv->cc608->hb_buffer = NULL; pv->cc608->hb_last_buffer = NULL; return HB_WORK_OK; } static void decccClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; general_608_close( pv->cc608 ); free( pv->cc608->data608 ); free( pv->cc608 ); free( w->private_data ); } hb_work_object_t hb_deccc608 = { WORK_DECCC608, "Closed Caption (608) decoder", decccInit, decccWork, decccClose }; HandBrake-0.10.2/libhb/colormap.c0000664000175200017520000007417212463330511017110 0ustar handbrakehandbrake/* colormap.c * * Copyright (c) 2003-2015 HandBrake Team * This file is part of the HandBrake source code * Homepage: . * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include #include #include #include "colormap.h" typedef struct { char *name; uint32_t rgb; } hb_colormap_t; static hb_colormap_t colormap[] = { { "none", 0xFFFFFF }, { "black", 0x000000 }, { "white", 0xFFFFFF }, { "red", 0xFF0000 }, { "green", 0x00FF00 }, { "blue", 0x0000FF }, { "aliceblue", 0xF0F8FF }, { "antiquewhite", 0xFAEBD7 }, { "antiquewhite1", 0xFFEFDB }, { "antiquewhite2", 0xEEDFCC }, { "antiquewhite3", 0xCDC0B0 }, { "antiquewhite4", 0x8B8378 }, { "aqua", 0x00FFFF }, { "aquamarine", 0x7FFFD4 }, { "aquamarine1", 0x7FFFD4 }, { "aquamarine2", 0x76EEC6 }, { "aquamarine3", 0x66CDAA }, { "aquamarine4", 0x458B74 }, { "azure", 0xF0FFFF }, { "azure1", 0xF0FFFF }, { "azure2", 0xE0EEEE }, { "azure3", 0xC1CDCD }, { "azure4", 0x838B8B }, { "beige", 0xF5F5DC }, { "bisque", 0xFFE4C4 }, { "bisque1", 0xFFE4C4 }, { "bisque2", 0xEED5B7 }, { "bisque3", 0xCDB79E }, { "bisque4", 0x8B7D6B }, { "black", 0x000000 }, { "blanchedalmond", 0xFFEBCD }, { "blue", 0x0000FF }, { "blue1", 0x0000FF }, { "blue2", 0x0000EE }, { "blue3", 0x0000CD }, { "blue4", 0x00008B }, { "blueviolet", 0x8A2BE2 }, { "brown", 0xA52A2A }, { "brown1", 0xFF4040 }, { "brown2", 0xEE3B3B }, { "brown3", 0xCD3333 }, { "brown4", 0x8B2323 }, { "burlywood", 0xDEB887 }, { "burlywood1", 0xFFD39B }, { "burlywood2", 0xEEC591 }, { "burlywood3", 0xCDAA7D }, { "burlywood4", 0x8B7355 }, { "cadetblue", 0x5F9EA0 }, { "cadetblue", 0x5F9EA0 }, { "cadetblue1", 0x98F5FF }, { "cadetblue2", 0x8EE5EE }, { "cadetblue3", 0x7AC5CD }, { "cadetblue4", 0x53868B }, { "chartreuse", 0x7FFF00 }, { "chartreuse1", 0x7FFF00 }, { "chartreuse2", 0x76EE00 }, { "chartreuse3", 0x66CD00 }, { "chartreuse4", 0x458B00 }, { "chocolate", 0xD2691E }, { "chocolate1", 0xFF7F24 }, { "chocolate2", 0xEE7621 }, { "chocolate3", 0xCD661D }, { "chocolate4", 0x8B4513 }, { "coral", 0xFF7F50 }, { "coral1", 0xFF7256 }, { "coral2", 0xEE6A50 }, { "coral3", 0xCD5B45 }, { "coral4", 0x8B3E2F }, { "cornflowerblue", 0x6495ED }, { "cornsilk", 0xFFF8DC }, { "cornsilk1", 0xFFF8DC }, { "cornsilk2", 0xEEE8CD }, { "cornsilk3", 0xCDC8B1 }, { "cornsilk4", 0x8B8878 }, { "crimson", 0xDC143C }, { "cyan", 0x00FFFF }, { "cyan1", 0x00FFFF }, { "cyan2", 0x00EEEE }, { "cyan3", 0x00CDCD }, { "cyan4", 0x008B8B }, { "darkblue", 0x00008B }, { "darkcyan", 0x008B8B }, { "darkgoldenrod", 0xB8860B }, { "darkgoldenrod1", 0xFFB90F }, { "darkgoldenrod2", 0xEEAD0E }, { "darkgoldenrod3", 0xCD950C }, { "darkgoldenrod4", 0x8B6508 }, { "darkgray", 0xA9A9A9 }, { "darkgreen", 0x006400 }, { "darkgrey", 0xA9A9A9 }, { "darkkhaki", 0xBDB76B }, { "darkmagenta", 0x8B008B }, { "darkolivegreen", 0x556B2F }, { "darkolivegreen1", 0xCAFF70 }, { "darkolivegreen2", 0xBCEE68 }, { "darkolivegreen3", 0xA2CD5A }, { "darkolivegreen4", 0x6E8B3D }, { "darkorange", 0xFF8C00 }, { "darkorange1", 0xFF7F00 }, { "darkorange2", 0xEE7600 }, { "darkorange3", 0xCD6600 }, { "darkorange4", 0x8B4500 }, { "darkorchid", 0x9932CC }, { "darkorchid1", 0xBF3EFF }, { "darkorchid2", 0xB23AEE }, { "darkorchid3", 0x9A32CD }, { "darkorchid4", 0x68228B }, { "darkred", 0x8B0000 }, { "darksalmon", 0xE9967A }, { "darkseagreen", 0x8FBC8F }, { "darkseagreen1", 0xC1FFC1 }, { "darkseagreen2", 0xB4EEB4 }, { "darkseagreen3", 0x9BCD9B }, { "darkseagreen4", 0x698B69 }, { "darkslateblue", 0x483D8B }, { "darkslategray", 0x2F4F4F }, { "darkslategray1", 0x97FFFF }, { "darkslategray2", 0x8DEEEE }, { "darkslategray3", 0x79CDCD }, { "darkslategray4", 0x528B8B }, { "darkslategrey", 0x2F4F4F }, { "darkturquoise", 0x00CED1 }, { "darkviolet", 0x9400D3 }, { "darkviolet", 0x9400D3 }, { "deeppink", 0xFF1493 }, { "deeppink1", 0xFF1493 }, { "deeppink2", 0xEE1289 }, { "deeppink3", 0xCD1076 }, { "deeppink4", 0x8B0A50 }, { "deepskyblue", 0x00BFFF }, { "deepskyblue1", 0x00BFFF }, { "deepskyblue2", 0x00B2EE }, { "deepskyblue3", 0x009ACD }, { "deepskyblue4", 0x00688B }, { "dimgray", 0x696969 }, { "dimgrey", 0x696969 }, { "dodgerblue", 0x1E90FF }, { "dodgerblue1", 0x1E90FF }, { "dodgerblue2", 0x1C86EE }, { "dodgerblue3", 0x1874CD }, { "dodgerblue4", 0x104E8B }, { "firebrick", 0xB22222 }, { "firebrick1", 0xFF3030 }, { "firebrick2", 0xEE2C2C }, { "firebrick3", 0xCD2626 }, { "firebrick4", 0x8B1A1A }, { "floralwhite", 0xFFFAF0 }, { "forestgreen", 0x228B22 }, { "fractal", 0x808080 }, { "fuchsia", 0xFF00FF }, { "gainsboro", 0xDCDCDC }, { "ghostwhite", 0xF8F8FF }, { "gold", 0xFFD700 }, { "gold1", 0xFFD700 }, { "gold2", 0xEEC900 }, { "gold3", 0xCDAD00 }, { "gold4", 0x8B7500 }, { "goldenrod", 0xDAA520 }, { "goldenrod1", 0xFFC125 }, { "goldenrod2", 0xEEB422 }, { "goldenrod3", 0xCD9B1D }, { "goldenrod4", 0x8B6914 }, { "gray", 0x7E7E7E }, { "gray", 0xBEBEBE }, { "gray0", 0x000000 }, { "gray1", 0x030303 }, { "gray10", 0x1A1A1A }, { "gray100", 0xFFFFFF }, { "gray11", 0x1C1C1C }, { "gray12", 0x1F1F1F }, { "gray13", 0x212121 }, { "gray14", 0x242424 }, { "gray15", 0x262626 }, { "gray16", 0x292929 }, { "gray17", 0x2B2B2B }, { "gray18", 0x2E2E2E }, { "gray19", 0x303030 }, { "gray2", 0x050505 }, { "gray20", 0x333333 }, { "gray21", 0x363636 }, { "gray22", 0x383838 }, { "gray23", 0x3B3B3B }, { "gray24", 0x3D3D3D }, { "gray25", 0x404040 }, { "gray26", 0x424242 }, { "gray27", 0x454545 }, { "gray28", 0x474747 }, { "gray29", 0x4A4A4A }, { "gray3", 0x080808 }, { "gray30", 0x4D4D4D }, { "gray31", 0x4F4F4F }, { "gray32", 0x525252 }, { "gray33", 0x545454 }, { "gray34", 0x575757 }, { "gray35", 0x595959 }, { "gray36", 0x5C5C5C }, { "gray37", 0x5E5E5E }, { "gray38", 0x616161 }, { "gray39", 0x636363 }, { "gray4", 0x0A0A0A }, { "gray40", 0x666666 }, { "gray41", 0x696969 }, { "gray42", 0x6B6B6B }, { "gray43", 0x6E6E6E }, { "gray44", 0x707070 }, { "gray45", 0x737373 }, { "gray46", 0x757575 }, { "gray47", 0x787878 }, { "gray48", 0x7A7A7A }, { "gray49", 0x7D7D7D }, { "gray5", 0x0D0D0D }, { "gray50", 0x7F7F7F }, { "gray51", 0x828282 }, { "gray52", 0x858585 }, { "gray53", 0x878787 }, { "gray54", 0x8A8A8A }, { "gray55", 0x8C8C8C }, { "gray56", 0x8F8F8F }, { "gray57", 0x919191 }, { "gray58", 0x949494 }, { "gray59", 0x969696 }, { "gray6", 0x0F0F0F }, { "gray60", 0x999999 }, { "gray61", 0x9C9C9C }, { "gray62", 0x9E9E9E }, { "gray63", 0xA1A1A1 }, { "gray64", 0xA3A3A3 }, { "gray65", 0xA6A6A6 }, { "gray66", 0xA8A8A8 }, { "gray67", 0xABABAB }, { "gray68", 0xADADAD }, { "gray69", 0xB0B0B0 }, { "gray7", 0x121212 }, { "gray70", 0xB3B3B3 }, { "gray71", 0xB5B5B5 }, { "gray72", 0xB8B8B8 }, { "gray73", 0xBABABA }, { "gray74", 0xBDBDBD }, { "gray75", 0xBFBFBF }, { "gray76", 0xC2C2C2 }, { "gray77", 0xC4C4C4 }, { "gray78", 0xC7C7C7 }, { "gray79", 0xC9C9C9 }, { "gray8", 0x141414 }, { "gray80", 0xCCCCCC }, { "gray81", 0xCFCFCF }, { "gray82", 0xD1D1D1 }, { "gray83", 0xD4D4D4 }, { "gray84", 0xD6D6D6 }, { "gray85", 0xD9D9D9 }, { "gray86", 0xDBDBDB }, { "gray87", 0xDEDEDE }, { "gray88", 0xE0E0E0 }, { "gray89", 0xE3E3E3 }, { "gray9", 0x171717 }, { "gray90", 0xE5E5E5 }, { "gray91", 0xE8E8E8 }, { "gray92", 0xEBEBEB }, { "gray93", 0xEDEDED }, { "gray94", 0xF0F0F0 }, { "gray95", 0xF2F2F2 }, { "gray96", 0xF5F5F5 }, { "gray97", 0xF7F7F7 }, { "gray98", 0xFAFAFA }, { "gray99", 0xFCFCFC }, { "green", 0x008000 }, { "green", 0x00FF00 }, { "green1", 0x00FF00 }, { "green2", 0x00EE00 }, { "green3", 0x00CD00 }, { "green4", 0x008B00 }, { "greenyellow", 0xADFF2F }, { "grey", 0xBEBEBE }, { "grey0", 0x000000 }, { "grey1", 0x030303 }, { "grey10", 0x1A1A1A }, { "grey100", 0xFFFFFF }, { "grey11", 0x1C1C1C }, { "grey12", 0x1F1F1F }, { "grey13", 0x212121 }, { "grey14", 0x242424 }, { "grey15", 0x262626 }, { "grey16", 0x292929 }, { "grey17", 0x2B2B2B }, { "grey18", 0x2E2E2E }, { "grey19", 0x303030 }, { "grey2", 0x050505 }, { "grey20", 0x333333 }, { "grey21", 0x363636 }, { "grey22", 0x383838 }, { "grey23", 0x3B3B3B }, { "grey24", 0x3D3D3D }, { "grey25", 0x404040 }, { "grey26", 0x424242 }, { "grey27", 0x454545 }, { "grey28", 0x474747 }, { "grey29", 0x4A4A4A }, { "grey3", 0x080808 }, { "grey30", 0x4D4D4D }, { "grey31", 0x4F4F4F }, { "grey32", 0x525252 }, { "grey33", 0x545454 }, { "grey34", 0x575757 }, { "grey35", 0x595959 }, { "grey36", 0x5C5C5C }, { "grey37", 0x5E5E5E }, { "grey38", 0x616161 }, { "grey39", 0x636363 }, { "grey4", 0x0A0A0A }, { "grey40", 0x666666 }, { "grey41", 0x696969 }, { "grey42", 0x6B6B6B }, { "grey43", 0x6E6E6E }, { "grey44", 0x707070 }, { "grey45", 0x737373 }, { "grey46", 0x757575 }, { "grey47", 0x787878 }, { "grey48", 0x7A7A7A }, { "grey49", 0x7D7D7D }, { "grey5", 0x0D0D0D }, { "grey50", 0x7F7F7F }, { "grey51", 0x828282 }, { "grey52", 0x858585 }, { "grey53", 0x878787 }, { "grey54", 0x8A8A8A }, { "grey55", 0x8C8C8C }, { "grey56", 0x8F8F8F }, { "grey57", 0x919191 }, { "grey58", 0x949494 }, { "grey59", 0x969696 }, { "grey6", 0x0F0F0F }, { "grey60", 0x999999 }, { "grey61", 0x9C9C9C }, { "grey62", 0x9E9E9E }, { "grey63", 0xA1A1A1 }, { "grey64", 0xA3A3A3 }, { "grey65", 0xA6A6A6 }, { "grey66", 0xA8A8A8 }, { "grey67", 0xABABAB }, { "grey68", 0xADADAD }, { "grey69", 0xB0B0B0 }, { "grey7", 0x121212 }, { "grey70", 0xB3B3B3 }, { "grey71", 0xB5B5B5 }, { "grey72", 0xB8B8B8 }, { "grey73", 0xBABABA }, { "grey74", 0xBDBDBD }, { "grey75", 0xBFBFBF }, { "grey76", 0xC2C2C2 }, { "grey77", 0xC4C4C4 }, { "grey78", 0xC7C7C7 }, { "grey79", 0xC9C9C9 }, { "grey8", 0x141414 }, { "grey80", 0xCCCCCC }, { "grey81", 0xCFCFCF }, { "grey82", 0xD1D1D1 }, { "grey83", 0xD4D4D4 }, { "grey84", 0xD6D6D6 }, { "grey85", 0xD9D9D9 }, { "grey86", 0xDBDBDB }, { "grey87", 0xDEDEDE }, { "grey88", 0xE0E0E0 }, { "grey89", 0xE3E3E3 }, { "grey9", 0x171717 }, { "grey90", 0xE5E5E5 }, { "grey91", 0xE8E8E8 }, { "grey92", 0xEBEBEB }, { "grey93", 0xEDEDED }, { "grey94", 0xF0F0F0 }, { "grey95", 0xF2F2F2 }, { "grey96", 0xF5F5F5 }, { "grey97", 0xF7F7F7 }, { "grey98", 0xFAFAFA }, { "grey99", 0xFCFCFC }, { "honeydew", 0xF0FFF0 }, { "honeydew1", 0xF0FFF0 }, { "honeydew2", 0xE0EEE0 }, { "honeydew3", 0xC1CDC1 }, { "honeydew4", 0x838B83 }, { "hotpink", 0xFF69B4 }, { "hotpink1", 0xFF6EB4 }, { "hotpink2", 0xEE6AA7 }, { "hotpink3", 0xCD6090 }, { "hotpink4", 0x8B3A62 }, { "indianred", 0xCD5C5C }, { "indianred1", 0xFF6A6A }, { "indianred2", 0xEE6363 }, { "indianred3", 0xCD5555 }, { "indianred4", 0x8B3A3A }, { "indigo", 0x4B0082 }, { "ivory", 0xFFFFF0 }, { "ivory1", 0xFFFFF0 }, { "ivory2", 0xEEEEE0 }, { "ivory3", 0xCDCDC1 }, { "ivory4", 0x8B8B83 }, { "khaki", 0xF0E68C }, { "khaki1", 0xFFF68F }, { "khaki2", 0xEEE685 }, { "khaki3", 0xCDC673 }, { "khaki4", 0x8B864E }, { "lavender", 0xE6E6FA }, { "lavenderblush", 0xFFF0F5 }, { "lavenderblush1", 0xFFF0F5 }, { "lavenderblush2", 0xEEE0E5 }, { "lavenderblush3", 0xCDC1C5 }, { "lavenderblush4", 0x8B8386 }, { "lawngreen", 0x7CFC00 }, { "lemonchiffon", 0xFFFACD }, { "lemonchiffon1", 0xFFFACD }, { "lemonchiffon2", 0xEEE9BF }, { "lemonchiffon3", 0xCDC9A5 }, { "lemonchiffon4", 0x8B8970 }, { "lightblue", 0xADD8E6 }, { "lightblue1", 0xBFEFFF }, { "lightblue2", 0xB2DFEE }, { "lightblue3", 0x9AC0CD }, { "lightblue4", 0x68838B }, { "lightcoral", 0xF08080 }, { "lightcyan", 0xE0FFFF }, { "lightcyan1", 0xE0FFFF }, { "lightcyan2", 0xD1EEEE }, { "lightcyan3", 0xB4CDCD }, { "lightcyan4", 0x7A8B8B }, { "lightgoldenrod", 0xEEDD82 }, { "lightgoldenrod1", 0xFFEC8B }, { "lightgoldenrod2", 0xEEDC82 }, { "lightgoldenrod3", 0xCDBE70 }, { "lightgoldenrod4", 0x8B814C }, { "lightgoldenrodyellow", 0xFAFAD2 }, { "lightgray", 0xD3D3D3 }, { "lightgreen", 0x90EE90 }, { "lightgrey", 0xD3D3D3 }, { "lightpink", 0xFFB6C1 }, { "lightpink1", 0xFFAEB9 }, { "lightpink2", 0xEEA2AD }, { "lightpink3", 0xCD8C95 }, { "lightpink4", 0x8B5F65 }, { "lightsalmon", 0xFFA07A }, { "lightsalmon1", 0xFFA07A }, { "lightsalmon2", 0xEE9572 }, { "lightsalmon3", 0xCD8162 }, { "lightsalmon4", 0x8B5742 }, { "lightseagreen", 0x20B2AA }, { "lightskyblue", 0x87CEFA }, { "lightskyblue1", 0xB0E2FF }, { "lightskyblue2", 0xA4D3EE }, { "lightskyblue3", 0x8DB6CD }, { "lightskyblue4", 0x607B8B }, { "lightslateblue", 0x8470FF }, { "lightslategray", 0x778899 }, { "lightslategrey", 0x778899 }, { "lightsteelblue", 0xB0C4DE }, { "lightsteelblue1", 0xCAE1FF }, { "lightsteelblue2", 0xBCD2EE }, { "lightsteelblue3", 0xA2B5CD }, { "lightsteelblue4", 0x6E7B8B }, { "lightyellow", 0xFFFFE0 }, { "lightyellow1", 0xFFFFE0 }, { "lightyellow2", 0xEEEED1 }, { "lightyellow3", 0xCDCDB4 }, { "lightyellow4", 0x8B8B7A }, { "lime", 0x00FF00 }, { "limegreen", 0x32CD32 }, { "linen", 0xFAF0E6 }, { "magenta", 0xFF00FF }, { "magenta1", 0xFF00FF }, { "magenta2", 0xEE00EE }, { "magenta3", 0xCD00CD }, { "magenta4", 0x8B008B }, { "maroon", 0x800000 }, { "maroon", 0xB03060 }, { "maroon1", 0xFF34B3 }, { "maroon2", 0xEE30A7 }, { "maroon3", 0xCD2990 }, { "maroon4", 0x8B1C62 }, { "mediumaquamarine", 0x66CDAA }, { "mediumblue", 0x0000CD }, { "mediumforestgreen", 0x32814B }, { "mediumgoldenrod", 0xD1C166 }, { "mediumorchid", 0xBA55D3 }, { "mediumorchid1", 0xE066FF }, { "mediumorchid2", 0xD15FEE }, { "mediumorchid3", 0xB452CD }, { "mediumorchid4", 0x7A378B }, { "mediumpurple", 0x9370DB }, { "mediumpurple1", 0xAB82FF }, { "mediumpurple2", 0x9F79EE }, { "mediumpurple3", 0x8968CD }, { "mediumpurple4", 0x5D478B }, { "mediumseagreen", 0x3CB371 }, { "mediumslateblue", 0x7B68EE }, { "mediumspringgreen", 0x00FA9A }, { "mediumturquoise", 0x48D1CC }, { "mediumvioletred", 0xC71585 }, { "midnightblue", 0x191970 }, { "mintcream", 0xF5FFFA }, { "mistyrose", 0xFFE4E1 }, { "mistyrose1", 0xFFE4E1 }, { "mistyrose2", 0xEED5D2 }, { "mistyrose3", 0xCDB7B5 }, { "mistyrose4", 0x8B7D7B }, { "moccasin", 0xFFE4B5 }, { "navajowhite", 0xFFDEAD }, { "navajowhite1", 0xFFDEAD }, { "navajowhite2", 0xEECFA1 }, { "navajowhite3", 0xCDB38B }, { "navajowhite4", 0x8B795E }, { "navy", 0x000080 }, { "navyblue", 0x000080 }, { "none", 0x0000FF }, { "oldlace", 0xFDF5E6 }, { "olive", 0x808000 }, { "olivedrab", 0x6B8E23 }, { "olivedrab1", 0xC0FF3E }, { "olivedrab2", 0xB3EE3A }, { "olivedrab3", 0x9ACD32 }, { "olivedrab4", 0x698B22 }, { "opaque", 0x000000 }, { "orange", 0xFFA500 }, { "orange1", 0xFFA500 }, { "orange2", 0xEE9A00 }, { "orange3", 0xCD8500 }, { "orange4", 0x8B5A00 }, { "orangered", 0xFF4500 }, { "orangered1", 0xFF4500 }, { "orangered2", 0xEE4000 }, { "orangered3", 0xCD3700 }, { "orangered4", 0x8B2500 }, { "orchid", 0xDA70D6 }, { "orchid1", 0xFF83FA }, { "orchid2", 0xEE7AE9 }, { "orchid3", 0xCD69C9 }, { "orchid4", 0x8B4789 }, { "palegoldenrod", 0xEEE8AA }, { "palegreen", 0x98FB98 }, { "palegreen1", 0x9AFF9A }, { "palegreen2", 0x90EE90 }, { "palegreen3", 0x7CCD7C }, { "palegreen4", 0x548B54 }, { "paleturquoise", 0xAFEEEE }, { "paleturquoise1", 0xBBFFFF }, { "paleturquoise2", 0xAEEEEE }, { "paleturquoise3", 0x96CDCD }, { "paleturquoise4", 0x668B8B }, { "palevioletred", 0xDB7093 }, { "palevioletred1", 0xFF82AB }, { "palevioletred2", 0xEE799F }, { "palevioletred3", 0xCD6889 }, { "palevioletred4", 0x8B475D }, { "papayawhip", 0xFFEFD5 }, { "peachpuff", 0xFFDAB9 }, { "peachpuff1", 0xFFDAB9 }, { "peachpuff2", 0xEECBAD }, { "peachpuff3", 0xCDAF95 }, { "peachpuff4", 0x8B7765 }, { "peru", 0xCD853F }, { "pink", 0xFFC0CB }, { "pink1", 0xFFB5C5 }, { "pink2", 0xEEA9B8 }, { "pink3", 0xCD919E }, { "pink4", 0x8B636C }, { "plum", 0xDDA0DD }, { "plum1", 0xFFBBFF }, { "plum2", 0xEEAEEE }, { "plum3", 0xCD96CD }, { "plum4", 0x8B668B }, { "powderblue", 0xB0E0E6 }, { "purple", 0x800080 }, { "purple", 0xA020F0 }, { "purple1", 0x9B30FF }, { "purple2", 0x912CEE }, { "purple3", 0x7D26CD }, { "purple4", 0x551A8B }, { "red", 0xFF0000 }, { "red1", 0xFF0000 }, { "red2", 0xEE0000 }, { "red3", 0xCD0000 }, { "red4", 0x8B0000 }, { "rosybrown", 0xBC8F8F }, { "rosybrown1", 0xFFC1C1 }, { "rosybrown2", 0xEEB4B4 }, { "rosybrown3", 0xCD9B9B }, { "rosybrown4", 0x8B6969 }, { "royalblue", 0x4169E1 }, { "royalblue1", 0x4876FF }, { "royalblue2", 0x436EEE }, { "royalblue3", 0x3A5FCD }, { "royalblue4", 0x27408B }, { "saddlebrown", 0x8B4513 }, { "salmon", 0xFA8072 }, { "salmon1", 0xFF8C69 }, { "salmon2", 0xEE8262 }, { "salmon3", 0xCD7054 }, { "salmon4", 0x8B4C39 }, { "sandybrown", 0xF4A460 }, { "seagreen", 0x2E8B57 }, { "seagreen1", 0x54FF9F }, { "seagreen2", 0x4EEE94 }, { "seagreen3", 0x43CD80 }, { "seagreen4", 0x2E8B57 }, { "seashell", 0xFFF5EE }, { "seashell1", 0xFFF5EE }, { "seashell2", 0xEEE5DE }, { "seashell3", 0xCDC5BF }, { "seashell4", 0x8B8682 }, { "sienna", 0xA0522D }, { "sienna1", 0xFF8247 }, { "sienna2", 0xEE7942 }, { "sienna3", 0xCD6839 }, { "sienna4", 0x8B4726 }, { "silver", 0xC0C0C0 }, { "skyblue", 0x87CEEB }, { "skyblue1", 0x87CEFF }, { "skyblue2", 0x7EC0EE }, { "skyblue3", 0x6CA6CD }, { "skyblue4", 0x4A708B }, { "slateblue", 0x6A5ACD }, { "slateblue1", 0x836FFF }, { "slateblue2", 0x7A67EE }, { "slateblue3", 0x6959CD }, { "slateblue4", 0x473C8B }, { "slategray", 0x708090 }, { "slategray1", 0xC6E2FF }, { "slategray2", 0xB9D3EE }, { "slategray3", 0x9FB6CD }, { "slategray4", 0x6C7B8B }, { "slategrey", 0x708090 }, { "snow", 0xFFFAFA }, { "snow1", 0xFFFAFA }, { "snow2", 0xEEE9E9 }, { "snow3", 0xCDC9C9 }, { "snow4", 0x8B8989 }, { "springgreen", 0x00FF7F }, { "springgreen1", 0x00FF7F }, { "springgreen2", 0x00EE76 }, { "springgreen3", 0x00CD66 }, { "springgreen4", 0x008B45 }, { "steelblue", 0x4682B4 }, { "steelblue1", 0x63B8FF }, { "steelblue2", 0x5CACEE }, { "steelblue3", 0x4F94CD }, { "steelblue4", 0x36648B }, { "tan", 0xD2B48C }, { "tan1", 0xFFA54F }, { "tan2", 0xEE9A49 }, { "tan3", 0xCD853F }, { "tan4", 0x8B5A2B }, { "teal", 0x008080 }, { "thistle", 0xD8BFD8 }, { "thistle1", 0xFFE1FF }, { "thistle2", 0xEED2EE }, { "thistle3", 0xCDB5CD }, { "thistle4", 0x8B7B8B }, { "tomato", 0xFF6347 }, { "tomato1", 0xFF6347 }, { "tomato2", 0xEE5C42 }, { "tomato3", 0xCD4F39 }, { "tomato4", 0x8B3626 }, { "transparent", 0x0000FF }, { "turquoise", 0x40E0D0 }, { "turquoise1", 0x00F5FF }, { "turquoise2", 0x00E5EE }, { "turquoise3", 0x00C5CD }, { "turquoise4", 0x00868B }, { "violet", 0xEE82EE }, { "violetred", 0xD02090 }, { "violetred1", 0xFF3E96 }, { "violetred2", 0xEE3A8C }, { "violetred3", 0xCD3278 }, { "violetred4", 0x8B2252 }, { "wheat", 0xF5DEB3 }, { "wheat1", 0xFFE7BA }, { "wheat2", 0xEED8AE }, { "wheat3", 0xCDBA96 }, { "wheat4", 0x8B7E66 }, { "white", 0xFFFFFF }, { "whitesmoke", 0xF5F5F5 }, { "yellow", 0xFFFF00 }, { "yellow1", 0xFFFF00 }, { "yellow2", 0xEEEE00 }, { "yellow3", 0xCDCD00 }, { "yellow4", 0x8B8B00 }, { "yellowgreen", 0x9ACD32 }, { NULL, 0x000000 } }; uint32_t hb_rgb_lookup_by_name(const char *color) { int ii = 0; while (colormap[ii].name != NULL) { if (!strcasecmp(color, colormap[ii].name)) return colormap[ii].rgb; ii++; } return 0; } HandBrake-0.10.2/libhb/decssasub.c0000664000175200017520000003333012463330511017237 0ustar handbrakehandbrake/* decssasub.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ /* * Converts SSA subtitles to either: * (1) TEXTSUB format: UTF-8 subtitles with limited HTML-style markup (, , ), or * (2) PICTURESUB format, using libass. * * SSA format references: * http://www.matroska.org/technical/specs/subtitles/ssa.html * http://moodub.free.fr/video/ass-specs.doc * vlc-1.0.4/modules/codec/subtitles/subsass.c:ParseSSAString * * libass references: * libass-0.9.9/ass.h * vlc-1.0.4/modules/codec/libass.c * * @author David Foster (davidfstr) */ #include #include #include #include "hb.h" #include #include "decssasub.h" #include "colormap.h" struct hb_work_private_s { // If decoding to PICTURESUB format: int readOrder; hb_job_t *job; }; #define SSA_2_HB_TIME(hr,min,sec,centi) \ ( 90L * ( hr * 1000L * 60 * 60 +\ min * 1000L * 60 +\ sec * 1000L +\ centi * 10L ) ) #define SSA_VERBOSE_PACKETS 0 static int ssa_update_style(char *ssa, hb_subtitle_style_t *style) { int pos, end, index; if (ssa[0] != '{') return 0; pos = 1; while (ssa[pos] != '}' && ssa[pos] != '\0') { index = -1; // Skip any malformed markup junk while (strchr("\\}", ssa[pos]) == NULL) pos++; pos++; // Check for an index that is in some markup (e.g. font color) if (isdigit(ssa[pos])) { index = ssa[pos++] - 0x30; } // Find the end of this markup clause end = pos; while (strchr("\\}", ssa[end]) == NULL) end++; // Handle simple integer valued attributes if (strchr("ibu", ssa[pos]) != NULL && isdigit(ssa[pos+1])) { int val = strtol(ssa + pos + 1, NULL, 0); switch (ssa[pos]) { case 'i': style->flags = (style->flags & ~HB_STYLE_FLAG_ITALIC) | !!val * HB_STYLE_FLAG_ITALIC; break; case 'b': style->flags = (style->flags & ~HB_STYLE_FLAG_BOLD) | !!val * HB_STYLE_FLAG_BOLD; break; case 'u': style->flags = (style->flags & ~HB_STYLE_FLAG_UNDERLINE) | !!val * HB_STYLE_FLAG_UNDERLINE; break; } } if (ssa[pos] == 'c' && ssa[pos+1] == '&' && ssa[pos+2] == 'H') { // Font color markup char *endptr; uint32_t bgr; bgr = strtol(ssa + pos + 3, &endptr, 16); if (*endptr == '&') { switch (index) { case -1: case 1: style->fg_rgb = HB_BGR_TO_RGB(bgr); break; case 2: style->alt_rgb = HB_BGR_TO_RGB(bgr); break; case 3: style->ol_rgb = HB_BGR_TO_RGB(bgr); break; case 4: style->bg_rgb = HB_BGR_TO_RGB(bgr); break; default: // Unknown color index, ignore break; } } } if ((ssa[pos] == 'a' && ssa[pos+1] == '&' && ssa[pos+2] == 'H') || (!strcmp(ssa+pos, "alpha") && ssa[pos+5] == '&' && ssa[pos+6] == 'H')) { // Font alpha markup char *endptr; uint8_t alpha; int alpha_pos = 3; if (ssa[1] == 'l') alpha_pos = 7; alpha = strtol(ssa + pos + alpha_pos, &endptr, 16); if (*endptr == '&') { // SSA alpha is inverted 0 is opaque alpha = 255 - alpha; switch (index) { case -1: case 1: style->fg_alpha = alpha; break; case 2: style->alt_alpha = alpha; break; case 3: style->ol_alpha = alpha; break; case 4: style->bg_alpha = alpha; break; default: // Unknown alpha index, ignore break; } } } pos = end; } if (ssa[pos] == '}') pos++; return pos; } char * hb_ssa_to_text(char *in, int *consumed, hb_subtitle_style_t *style) { int markup_len = 0; int in_pos = 0; int out_pos = 0; char *out = malloc(strlen(in) + 1); // out will never be longer than in for (in_pos = 0; in[in_pos] != '\0'; in_pos++) { if ((markup_len = ssa_update_style(in + in_pos, style))) { *consumed = in_pos + markup_len; out[out_pos++] = '\0'; return out; } // Check escape codes if (in[in_pos] == '\\') { in_pos++; switch (in[in_pos]) { case '\0': in_pos--; break; case 'N': case 'n': out[out_pos++] = '\n'; break; case 'h': out[out_pos++] = ' '; break; default: out[out_pos++] = in[in_pos]; break; } } else { out[out_pos++] = in[in_pos]; } } *consumed = in_pos; out[out_pos++] = '\0'; return out; } void hb_ssa_style_init(hb_subtitle_style_t *style) { style->flags = 0; style->fg_rgb = 0x00FFFFFF; style->alt_rgb = 0x00FFFFFF; style->ol_rgb = 0x000F0F0F; style->bg_rgb = 0x000F0F0F; style->fg_alpha = 0xFF; style->alt_alpha = 0xFF; style->ol_alpha = 0xFF; style->bg_alpha = 0xFF; } static hb_buffer_t *ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, uint8_t *in_data, int in_size, int in_sequence ); /* * Decodes a single SSA packet to one or more TEXTSUB or PICTURESUB subtitle packets. * * SSA packet format: * ( Dialogue: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text CR LF ) + * 1 2 3 4 5 6 7 8 9 10 */ static hb_buffer_t *ssa_decode_packet( hb_work_object_t * w, hb_buffer_t *in ) { // Store NULL after the end of the buffer to make using string processing safe hb_buffer_realloc(in, ++in->size); in->data[in->size - 1] = '\0'; hb_buffer_t *out_list = NULL; hb_buffer_t **nextPtr = &out_list; const char *EOL = "\r\n"; char *curLine, *curLine_parserData; for ( curLine = strtok_r( (char *) in->data, EOL, &curLine_parserData ); curLine; curLine = strtok_r( NULL, EOL, &curLine_parserData ) ) { // Skip empty lines and spaces between adjacent CR and LF if (curLine[0] == '\0') continue; // Decode an individual SSA line hb_buffer_t *out; out = ssa_decode_line_to_mkv_ssa(w, (uint8_t *)curLine, strlen(curLine), in->sequence); if ( out == NULL ) continue; // Append 'out' to 'out_list' *nextPtr = out; nextPtr = &out->next; } // For point-to-point encoding, when the start time of the stream // may be offset, the timestamps of the subtitles must be offset as well. // // HACK: Here we are making the assumption that, under normal circumstances, // the output display time of the first output packet is equal to the // display time of the input packet. // // During point-to-point encoding, the display time of the input // packet will be offset to compensate. // // Therefore we offset all of the output packets by a slip amount // such that first output packet's display time aligns with the // input packet's display time. This should give the correct time // when point-to-point encoding is in effect. if (out_list && out_list->s.start > in->s.start) { int64_t slip = out_list->s.start - in->s.start; hb_buffer_t *out; out = out_list; while (out) { out->s.start -= slip; out->s.stop -= slip; out = out->next; } } return out_list; } /* * Parses the start and stop time from the specified SSA packet. * * Returns true if parsing failed; false otherwise. */ static int parse_timing_from_ssa_packet( char *in_data, int64_t *in_start, int64_t *in_stop ) { /* * Parse Start and End fields for timing information */ int start_hr, start_min, start_sec, start_centi; int end_hr, end_min, end_sec, end_centi; // SSA subtitles have an empty layer field (bare ','). The scanf // format specifier "%*128[^,]" will not match on a bare ','. There // must be at least one non ',' character in the match. So the format // specifier is placed directly next to the ':' so that the next // expected ' ' after the ':' will be the character it matches on // when there is no layer field. int numPartsRead = sscanf( (char *) in_data, "Dialogue:%*128[^,]," "%d:%d:%d.%d," // Start "%d:%d:%d.%d,", // End &start_hr, &start_min, &start_sec, &start_centi, &end_hr, &end_min, &end_sec, &end_centi ); if ( numPartsRead != 8 ) return 1; *in_start = SSA_2_HB_TIME(start_hr, start_min, start_sec, start_centi); *in_stop = SSA_2_HB_TIME( end_hr, end_min, end_sec, end_centi); return 0; } static uint8_t *find_field( uint8_t *pos, uint8_t *end, int fieldNum ) { int curFieldID = 1; while (pos < end) { if ( *pos++ == ',' ) { curFieldID++; if ( curFieldID == fieldNum ) return pos; } } return NULL; } /* * SSA line format: * Dialogue: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0' * 1 2 3 4 5 6 7 8 9 10 * * MKV-SSA packet format: * ReadOrder,Marked, Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0' * 1 2 3 4 5 6 7 8 9 */ static hb_buffer_t *ssa_decode_line_to_mkv_ssa( hb_work_object_t * w, uint8_t *in_data, int in_size, int in_sequence ) { hb_work_private_t * pv = w->private_data; hb_buffer_t * out; // Parse values for in->s.start and in->s.stop int64_t in_start, in_stop; if ( parse_timing_from_ssa_packet( (char *) in_data, &in_start, &in_stop ) ) goto fail; // Convert the SSA packet to MKV-SSA format, which is what libass expects char *mkvIn; int numPartsRead; char *styleToTextFields; char *layerField = malloc( in_size ); // SSA subtitles have an empty layer field (bare ','). The scanf // format specifier "%*128[^,]" will not match on a bare ','. There // must be at least one non ',' character in the match. So the format // specifier is placed directly next to the ':' so that the next // expected ' ' after the ':' will be the character it matches on // when there is no layer field. numPartsRead = sscanf( (char *)in_data, "Dialogue:%128[^,],", layerField ); if ( numPartsRead != 1 ) goto fail; styleToTextFields = (char *)find_field( in_data, in_data + in_size, 4 ); if ( styleToTextFields == NULL ) { free( layerField ); goto fail; } // The sscanf conversion above will result in an extra space // before the layerField. Strip the space. char *stripLayerField = layerField; for(; *stripLayerField == ' '; stripLayerField++); out = hb_buffer_init( in_size + 1 ); mkvIn = (char*)out->data; mkvIn[0] = '\0'; sprintf(mkvIn, "%d", pv->readOrder++); // ReadOrder: make this up strcat( mkvIn, "," ); strcat( mkvIn, stripLayerField ); strcat( mkvIn, "," ); strcat( mkvIn, (char *)styleToTextFields ); out->size = strlen(mkvIn) + 1; out->s.frametype = HB_FRAME_SUBTITLE; out->s.start = in_start; out->s.stop = in_stop; out->sequence = in_sequence; if( out->size == 0 ) { hb_buffer_close(&out); } free( layerField ); return out; fail: hb_log( "decssasub: malformed SSA subtitle packet: %.*s\n", in_size, in_data ); return NULL; } static int decssaInit( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t * pv; pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; pv->job = job; return 0; } static int decssaWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_buffer_t * in = *buf_in; #if SSA_VERBOSE_PACKETS printf("\nPACKET(%"PRId64",%"PRId64"): %.*s\n", in->s.start/90, in->s.stop/90, in->size, in->data); #endif if ( in->size <= 0 ) { *buf_out = in; *buf_in = NULL; return HB_WORK_DONE; } *buf_out = ssa_decode_packet(w, in); return HB_WORK_OK; } static void decssaClose( hb_work_object_t * w ) { free( w->private_data ); } hb_work_object_t hb_decssasub = { WORK_DECSSASUB, "SSA Subtitle Decoder", decssaInit, decssaWork, decssaClose }; HandBrake-0.10.2/libhb/encx265.c0000664000175200017520000004124012535357536016474 0ustar handbrakehandbrake/* encx265.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifdef USE_X265 #include "hb.h" #include "hb_dict.h" #include "h265_common.h" #include "x265.h" int encx265Init (hb_work_object_t*, hb_job_t*); int encx265Work (hb_work_object_t*, hb_buffer_t**, hb_buffer_t**); void encx265Close(hb_work_object_t*); hb_work_object_t hb_encx265 = { WORK_ENCX265, "H.265/HEVC encoder (libx265)", encx265Init, encx265Work, encx265Close, }; #define FRAME_INFO_MAX2 (8) // 2^8 = 256; 90000/256 = 352 frames/sec #define FRAME_INFO_MIN2 (17) // 2^17 = 128K; 90000/131072 = 1.4 frames/sec #define FRAME_INFO_SIZE (1 << (FRAME_INFO_MIN2 - FRAME_INFO_MAX2 + 1)) #define FRAME_INFO_MASK (FRAME_INFO_SIZE - 1) static const char * const hb_x265_encopt_synonyms[][2] = { { "me", "motion", }, { NULL, NULL, }, }; struct hb_work_private_s { hb_job_t *job; x265_encoder *x265; x265_param *param; int64_t last_stop; uint32_t frames_in; hb_list_t *delayed_chapters; int64_t next_chapter_pts; struct { int64_t duration; } frame_info[FRAME_INFO_SIZE]; char csvfn[1024]; }; // used in delayed_chapters list struct chapter_s { int index; int64_t start; }; static int param_parse(x265_param *param, const char *key, const char *value) { int ret = x265_param_parse(param, key, value); // let x265 sanity check the options for us switch (ret) { case X265_PARAM_BAD_NAME: hb_log("encx265: unknown option '%s'", key); break; case X265_PARAM_BAD_VALUE: hb_log("encx265: bad argument '%s=%s'", key, value ? value : "(null)"); break; default: break; } return ret; } /*********************************************************************** * hb_work_encx265_init *********************************************************************** * **********************************************************************/ int encx265Init(hb_work_object_t *w, hb_job_t *job) { hb_work_private_t *pv = calloc(1, sizeof(hb_work_private_t)); pv->next_chapter_pts = AV_NOPTS_VALUE; pv->delayed_chapters = hb_list_init(); pv->job = job; w->private_data = pv; int ret, vrate, vrate_base; x265_nal *nal; uint32_t nnal; x265_param *param = pv->param = x265_param_alloc(); if (x265_param_default_preset(param, job->encoder_preset, job->encoder_tune) < 0) { goto fail; } /* If the PSNR or SSIM tunes are in use, enable the relevant metric */ param->bEnablePsnr = param->bEnableSsim = 0; if (job->encoder_tune != NULL && *job->encoder_tune) { char *tmp = strdup(job->encoder_tune); char *tok = strtok(tmp, ",./-+"); do { if (!strncasecmp(tok, "psnr", 4)) { param->bEnablePsnr = 1; break; } if (!strncasecmp(tok, "ssim", 4)) { param->bEnableSsim = 1; break; } } while ((tok = strtok(NULL, ",./-+")) != NULL); free(tmp); } /* * Some HandBrake-specific defaults; users can override them * using the encoder_options string. */ hb_reduce(&vrate, &vrate_base, job->vrate, job->vrate_base); param->fpsNum = vrate; param->fpsDenom = vrate_base; param->keyframeMin = (int)((double)vrate / (double)vrate_base + 0.5); param->keyframeMax = param->keyframeMin * 10; /* * Video Signal Type (color description only). * * Use x265_param_parse (let x265 determine which bEnable * flags, if any, should be set in the x265_param struct). */ char colorprim[11], transfer[11], colormatrix[11]; switch (job->color_matrix_code) { case 1: // ITU BT.601 DVD or SD TV content (NTSC) strcpy(colorprim, "smpte170m"); strcpy(transfer, "bt709"); strcpy(colormatrix, "smpte170m"); break; case 2: // ITU BT.601 DVD or SD TV content (PAL) strcpy(colorprim, "bt470bg"); strcpy(transfer, "bt709"); strcpy(colormatrix, "smpte170m"); break; case 3: // ITU BT.709 HD content strcpy(colorprim, "bt709"); strcpy(transfer, "bt709"); strcpy(colormatrix, "bt709"); break; case 4: // custom snprintf(colorprim, sizeof(colorprim), "%d", job->color_prim); snprintf(transfer, sizeof(transfer), "%d", job->color_transfer); snprintf(colormatrix, sizeof(colormatrix), "%d", job->color_matrix); break; default: // detected during scan snprintf(colorprim, sizeof(colorprim), "%d", job->title->color_prim); snprintf(transfer, sizeof(transfer), "%d", job->title->color_transfer); snprintf(colormatrix, sizeof(colormatrix), "%d", job->title->color_matrix); break; } if (param_parse(param, "colorprim", colorprim) || param_parse(param, "transfer", transfer) || param_parse(param, "colormatrix", colormatrix)) { goto fail; } /* iterate through x265_opts and parse the options */ hb_dict_entry_t *entry = NULL; hb_dict_t *x265_opts = hb_encopts_to_dict(job->encoder_options, job->vcodec); while ((entry = hb_dict_next(x265_opts, entry)) != NULL) { // here's where the strings are passed to libx265 for parsing // unknown options or bad values are non-fatal, see encx264.c param_parse(param, entry->key, entry->value); } hb_dict_free(&x265_opts); /* * Reload colorimetry settings in case custom * values were set in the encoder_options string. */ job->color_matrix_code = 4; job->color_prim = param->vui.colorPrimaries; job->color_transfer = param->vui.transferCharacteristics; job->color_matrix = param->vui.matrixCoeffs; /* * Settings which can't be overriden in the encodeer_options string * (muxer-specific settings, resolution, ratecontrol, etc.). */ param->bRepeatHeaders = 0; param->sourceWidth = job->width; param->sourceHeight = job->height; if (job->anamorphic.mode) { /* * Let x265 determnine whether to use an aspect ratio * index vs. the extended SAR index + SAR width/height. */ char sar[22]; snprintf(sar, sizeof(sar), "%d:%d", job->anamorphic.par_width, job->anamorphic.par_height); if (param_parse(param, "sar", sar)) { goto fail; } } if (job->vquality > 0) { param->rc.rateControlMode = X265_RC_CRF; param->rc.rfConstant = job->vquality; } else { param->rc.rateControlMode = X265_RC_ABR; param->rc.bitrate = job->vbitrate; if (job->pass > 0 && job->pass < 3) { char stats_file[1024] = ""; char pass[2]; snprintf(pass, sizeof(pass), "%d", job->pass); hb_get_tempory_filename(job->h, stats_file, "x265.log"); if (param_parse(param, "stats", stats_file) || param_parse(param, "pass", pass)) { goto fail; } if (job->pass == 1 && job->fastfirstpass == 0 && param_parse(param, "slow-firstpass", "1")) { goto fail; } } } /* statsfile (but not 2-pass) */ memset(pv->csvfn, 0, sizeof(pv->csvfn)); if (param->logLevel >= X265_LOG_DEBUG) { if (param->csvfn == NULL) { hb_get_tempory_filename(job->h, pv->csvfn, "x265.csv"); param->csvfn = pv->csvfn; } else { strncpy(pv->csvfn, param->csvfn, sizeof(pv->csvfn)); } } /* Apply profile and level settings last. */ if (job->encoder_profile != NULL && strcasecmp(job->encoder_profile, hb_h265_profile_names[0]) != 0 && x265_param_apply_profile(param, job->encoder_profile) < 0) { goto fail; } /* we should now know whether B-frames are enabled */ job->areBframes = (param->bframes > 0) + (param->bframes > 0 && param->bBPyramid > 0); pv->x265 = x265_encoder_open(param); if (pv->x265 == NULL) { hb_error("encx265: x265_encoder_open failed."); goto fail; } /* * x265's output (headers and bitstream) are in Annex B format. * * Write the header as is, and let the muxer reformat * the extradata and output bitstream properly for us. */ ret = x265_encoder_headers(pv->x265, &nal, &nnal); if (ret < 0) { hb_error("encx265: x265_encoder_headers failed (%d)", ret); goto fail; } if (ret > sizeof(w->config->h265.headers)) { hb_error("encx265: bitstream headers too large (%d)", ret); goto fail; } memcpy(w->config->h265.headers, nal->payload, ret); w->config->h265.headers_length = ret; return 0; fail: w->private_data = NULL; free(pv); return 1; } void encx265Close(hb_work_object_t *w) { hb_work_private_t *pv = w->private_data; if (pv->delayed_chapters != NULL) { struct chapter_s *item; while ((item = hb_list_item(pv->delayed_chapters, 0)) != NULL) { hb_list_rem(pv->delayed_chapters, item); free(item); } hb_list_close(&pv->delayed_chapters); } x265_param_free(pv->param); x265_encoder_close(pv->x265); free(pv); w->private_data = NULL; } /* * see comments in definition of 'frame_info' in pv struct for description * of what these routines are doing. */ static void save_frame_info(hb_work_private_t *pv, hb_buffer_t *in) { int i = (in->s.start >> FRAME_INFO_MAX2) & FRAME_INFO_MASK; pv->frame_info[i].duration = in->s.stop - in->s.start; } static int64_t get_frame_duration(hb_work_private_t * pv, int64_t pts) { int i = (pts >> FRAME_INFO_MAX2) & FRAME_INFO_MASK; return pv->frame_info[i].duration; } static hb_buffer_t* nal_encode(hb_work_object_t *w, x265_picture *pic_out, x265_nal *nal, uint32_t nnal) { hb_work_private_t *pv = w->private_data; hb_job_t *job = pv->job; hb_buffer_t *buf = NULL; int i; if (nnal <= 0) { return NULL; } buf = hb_video_buffer_init(job->width, job->height); if (buf == NULL) { return NULL; } buf->size = 0; // copy the bitstream data for (i = 0; i < nnal; i++) { memcpy(buf->data + buf->size, nal[i].payload, nal[i].sizeBytes); buf->size += nal[i].sizeBytes; } // use the pts to get the original frame's duration. buf->s.duration = get_frame_duration(pv, pic_out->pts); buf->s.stop = pic_out->pts + buf->s.duration; buf->s.start = pic_out->pts; buf->s.renderOffset = pic_out->dts; if (w->config->h264.init_delay == 0 && pic_out->dts < 0) { w->config->h264.init_delay -= pic_out->dts; } switch (pic_out->sliceType) { case X265_TYPE_IDR: buf->s.frametype = HB_FRAME_IDR; break; case X265_TYPE_I: buf->s.frametype = HB_FRAME_I; break; case X265_TYPE_P: buf->s.frametype = HB_FRAME_P; break; case X265_TYPE_B: buf->s.frametype = HB_FRAME_B; break; case X265_TYPE_BREF: buf->s.frametype = HB_FRAME_BREF; break; default: buf->s.frametype = 0; break; } if (pv->next_chapter_pts != AV_NOPTS_VALUE && pv->next_chapter_pts <= pic_out->pts && pic_out->sliceType == X265_TYPE_IDR) { // we're no longer looking for this chapter pv->next_chapter_pts = AV_NOPTS_VALUE; // get the chapter index from the list struct chapter_s *item = hb_list_item(pv->delayed_chapters, 0); if (item != NULL) { // we're done with this chapter hb_list_rem(pv->delayed_chapters, item); buf->s.new_chap = item->index; free(item); // we may still have another pending chapter item = hb_list_item(pv->delayed_chapters, 0); if (item != NULL) { // we're looking for this one now // we still need it, don't remove it pv->next_chapter_pts = item->start; } } } // discard empty buffers (no video) if (buf->size <= 0) { hb_buffer_close(&buf); } return buf; } static hb_buffer_t* x265_encode(hb_work_object_t *w, hb_buffer_t *in) { hb_work_private_t *pv = w->private_data; hb_job_t *job = pv->job; x265_picture pic_in, pic_out; x265_nal *nal; uint32_t nnal; x265_picture_init(pv->param, &pic_in); pic_in.stride[0] = in->plane[0].stride; pic_in.stride[1] = in->plane[1].stride; pic_in.stride[2] = in->plane[2].stride; pic_in.planes[0] = in->plane[0].data; pic_in.planes[1] = in->plane[1].data; pic_in.planes[2] = in->plane[2].data; pic_in.poc = pv->frames_in++; pic_in.pts = in->s.start; pic_in.bitDepth = 8; if (in->s.new_chap && job->chapter_markers) { if (pv->next_chapter_pts == AV_NOPTS_VALUE) { pv->next_chapter_pts = in->s.start; } /* * Chapter markers are sometimes so close we can get a new one before * the previous marker has been through the encoding queue. * * Dropping markers can cause weird side-effects downstream, including * but not limited to missing chapters in the output, so we need to save * it somehow. */ struct chapter_s *item = malloc(sizeof(struct chapter_s)); if (item != NULL) { item->start = in->s.start; item->index = in->s.new_chap; hb_list_add(pv->delayed_chapters, item); } /* don't let 'work_loop' put a chapter mark on the wrong buffer */ in->s.new_chap = 0; /* * Chapters have to start with an IDR frame so request that this frame be * coded as IDR. Since there may be up to 16 frames currently buffered in * the encoder, remember the timestamp so when this frame finally pops out * of the encoder we'll mark its buffer as the start of a chapter. */ pic_in.sliceType = X265_TYPE_IDR; } else { pic_in.sliceType = X265_TYPE_AUTO; } if (pv->last_stop != in->s.start) { hb_log("encx265 input continuity err: last stop %"PRId64" start %"PRId64, pv->last_stop, in->s.start); } pv->last_stop = in->s.stop; save_frame_info(pv, in); if (x265_encoder_encode(pv->x265, &nal, &nnal, &pic_in, &pic_out) > 0) { return nal_encode(w, &pic_out, nal, nnal); } return NULL; } int encx265Work(hb_work_object_t *w, hb_buffer_t **buf_in, hb_buffer_t **buf_out) { hb_work_private_t *pv = w->private_data; hb_buffer_t *in = *buf_in; if (in->size <= 0) { uint32_t nnal; x265_nal *nal; x265_picture pic_out; hb_buffer_t *last_buf = NULL; // flush delayed frames while (x265_encoder_encode(pv->x265, &nal, &nnal, NULL, &pic_out) > 0) { hb_buffer_t *buf = nal_encode(w, &pic_out, nal, nnal); if (buf != NULL) { if (last_buf == NULL) { *buf_out = buf; } else { last_buf->next = buf; } last_buf = buf; } } // add the EOF to the end of the chain if (last_buf == NULL) { *buf_out = in; } else { last_buf->next = in; } *buf_in = NULL; return HB_WORK_DONE; } *buf_out = x265_encode(w, in); return HB_WORK_OK; } const char* hb_x265_encopt_name(const char *name) { int i; for (i = 0; hb_x265_encopt_synonyms[i][0] != NULL; i++) if (!strcmp(name, hb_x265_encopt_synonyms[i][1])) return hb_x265_encopt_synonyms[i][0]; return name; } #endif HandBrake-0.10.2/libhb/work.c0000664000175200017520000020117612463330511016252 0ustar handbrakehandbrake/* work.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "libavformat/avformat.h" #include "openclwrapper.h" #include "opencl.h" #ifdef USE_QSV #include "qsv_common.h" #include "qsv_filter_pp.h" #endif typedef struct { hb_list_t * jobs; hb_job_t ** current_job; hb_error_code * error; volatile int * die; } hb_work_t; static void work_func(); static void do_job( hb_job_t *); static void work_loop( void * ); static void filter_loop( void * ); #define FIFO_UNBOUNDED 65536 #define FIFO_UNBOUNDED_WAKE 65535 #define FIFO_LARGE 32 #define FIFO_LARGE_WAKE 16 #define FIFO_SMALL 16 #define FIFO_SMALL_WAKE 15 #define FIFO_MINI 4 #define FIFO_MINI_WAKE 3 /** * Allocates work object and launches work thread with work_func. * @param jobs Handle to hb_list_t. * @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, hb_error_code * error, hb_job_t ** job ) { hb_work_t * work = calloc( sizeof( hb_work_t ), 1 ); work->jobs = jobs; work->current_job = job; work->die = die; work->error = error; return hb_thread_init( "work", work_func, work, HB_LOW_PRIORITY ); } static void InitWorkState( hb_handle_t * h ) { hb_state_t state; state.state = HB_STATE_WORKING; #define p state.param.working p.progress = 0.0; p.rate_cur = 0.0; p.rate_avg = 0.0; p.hours = -1; p.minutes = -1; p.seconds = -1; #undef p hb_set_state( h, &state ); } /** * Iterates through job list and calls do_job for each job. * @param _work Handle work object. */ static void work_func( void * _work ) { hb_work_t * work = _work; hb_job_t * job; hb_log( "%d job(s) to process", hb_list_count( work->jobs ) ); while( !*work->die && ( job = hb_list_item( work->jobs, 0 ) ) ) { hb_list_rem( work->jobs, job ); job->die = work->die; job->done_error = work->error; *(work->current_job) = job; InitWorkState( job->h ); do_job( job ); *(work->current_job) = NULL; } free( work ); } hb_work_object_t * hb_get_work( int id ) { hb_work_object_t * w; for( w = hb_objects; w; w = w->next ) { if( w->id == id ) { hb_work_object_t *wc = malloc( sizeof(*w) ); *wc = *w; return wc; } } return NULL; } hb_work_object_t* hb_codec_decoder(int codec) { if (codec & HB_ACODEC_FF_MASK) { return hb_get_work(WORK_DECAVCODEC); } switch (codec) { case HB_ACODEC_LPCM: return hb_get_work(WORK_DECLPCM); default: break; } return NULL; } hb_work_object_t* hb_codec_encoder(int codec) { if (codec & HB_ACODEC_FF_MASK) { return hb_get_work(WORK_ENCAVCODEC_AUDIO); } switch (codec) { case HB_ACODEC_AC3: return hb_get_work(WORK_ENCAVCODEC_AUDIO); case HB_ACODEC_LAME: return hb_get_work(WORK_ENCLAME); case HB_ACODEC_VORBIS: return hb_get_work(WORK_ENCVORBIS); case HB_ACODEC_CA_AAC: return hb_get_work(WORK_ENC_CA_AAC); case HB_ACODEC_CA_HAAC: return hb_get_work(WORK_ENC_CA_HAAC); default: break; } return NULL; } /** * Displays job parameters in the debug log. * @param job Handle work hb_job_t. */ void hb_display_job_info(hb_job_t *job) { int i; hb_title_t *title = job->title; hb_audio_t *audio; hb_subtitle_t *subtitle; hb_log("job configuration:"); hb_log( " * source"); hb_log( " + %s", title->path ); if( job->pts_to_start || job->pts_to_stop ) { int64_t stop; int hr_start, min_start, hr_stop, min_stop; float sec_start, sec_stop; stop = job->pts_to_start + job->pts_to_stop; hr_start = job->pts_to_start / (90000 * 60 * 60); min_start = job->pts_to_start / (90000 * 60); sec_start = (float)job->pts_to_start / 90000.0 - min_start * 60; min_start %= 60; hr_stop = stop / (90000 * 60 * 60); min_stop = stop / (90000 * 60); sec_stop = (float)stop / 90000.0 - min_stop * 60; min_stop %= 60; hb_log(" + title %d, start %02d:%02d:%02.2f stop %02d:%02d:%02.2f", title->index, hr_start, min_start, sec_start, hr_stop, min_stop, sec_stop); } else if( job->frame_to_start || job->frame_to_stop ) { hb_log( " + title %d, frames %d to %d", title->index, job->frame_to_start, job->frame_to_start + job->frame_to_stop ); } else { hb_log( " + title %d, chapter(s) %d to %d", title->index, job->chapter_start, job->chapter_end ); } if( title->container_name != NULL ) hb_log( " + container: %s", title->container_name); if( title->data_rate ) { hb_log( " + data rate: %d kbps", title->data_rate / 1000 ); } hb_log( " * destination"); hb_log( " + %s", job->file ); hb_log(" + container: %s", hb_container_get_long_name(job->mux)); switch (job->mux) { case HB_MUX_MP4V2: if (job->largeFileSize) hb_log(" + 64-bit chunk offsets"); case HB_MUX_AV_MP4: if (job->mp4_optimize) hb_log(" + optimized for HTTP streaming (fast start)"); if (job->ipod_atom) hb_log(" + compatibility atom for iPod 5G"); break; default: break; } if( job->chapter_markers ) { hb_log( " + chapter markers" ); } hb_log(" * video track"); #ifdef USE_QSV if (hb_qsv_decode_is_enabled(job)) { hb_log(" + decoder: %s", hb_qsv_decode_get_codec_name(title->video_codec_param)); } else #endif { hb_log(" + decoder: %s", title->video_codec_name); } if( title->video_bitrate ) { hb_log( " + bitrate %d kbps", title->video_bitrate / 1000 ); } // Filters can modify dimensions. So show them first. if( hb_list_count( job->list_filter ) ) { hb_log(" + %s", hb_list_count( job->list_filter) > 1 ? "filters" : "filter" ); for( i = 0; i < hb_list_count( job->list_filter ); i++ ) { hb_filter_object_t * filter = hb_list_item( job->list_filter, i ); if( filter->settings ) hb_log(" + %s (%s)", filter->name, filter->settings); else hb_log(" + %s (default settings)", filter->name); if( filter->info ) { hb_filter_info_t info; filter->info( filter, &info ); if( info.human_readable_desc[0] ) { hb_log(" + %s", info.human_readable_desc); } } } } if( job->anamorphic.mode ) { hb_log( " + %s anamorphic", job->anamorphic.mode == 1 ? "strict" : job->anamorphic.mode == 2? "loose" : "custom" ); if( job->anamorphic.mode == 3 && job->anamorphic.keep_display_aspect ) { hb_log( " + keeping source display aspect ratio"); } hb_log( " + storage dimensions: %d * %d, mod %i", job->width, job->height, job->modulus ); if( job->anamorphic.itu_par ) { hb_log( " + using ITU pixel aspect ratio values"); } hb_log( " + pixel aspect ratio: %i / %i", job->anamorphic.par_width, job->anamorphic.par_height ); hb_log( " + display dimensions: %.0f * %i", (float)( job->width * job->anamorphic.par_width / job->anamorphic.par_height ), job->height ); } else { hb_log( " + dimensions: %d * %d, mod %i", job->width, job->height, job->modulus ); } if ( job->grayscale ) hb_log( " + grayscale mode" ); if( !job->indepth_scan ) { /* Video encoder */ hb_log(" + encoder: %s", hb_video_encoder_get_long_name(job->vcodec)); if (job->encoder_preset && *job->encoder_preset) { switch (job->vcodec) { case HB_VCODEC_X264: case HB_VCODEC_X265: case HB_VCODEC_QSV_H264: hb_log(" + preset: %s", job->encoder_preset); default: break; } } if (job->encoder_tune && *job->encoder_tune) { switch (job->vcodec) { case HB_VCODEC_X264: case HB_VCODEC_X265: hb_log(" + tune: %s", job->encoder_tune); default: break; } } if (job->encoder_options != NULL && *job->encoder_options && job->vcodec != HB_VCODEC_THEORA) { hb_log(" + options: %s", job->encoder_options); } if (job->encoder_profile && *job->encoder_profile) { switch (job->vcodec) { case HB_VCODEC_X264: case HB_VCODEC_X265: case HB_VCODEC_QSV_H264: hb_log(" + profile: %s", job->encoder_profile); default: break; } } if (job->encoder_level && *job->encoder_level) { switch (job->vcodec) { case HB_VCODEC_X264: case HB_VCODEC_QSV_H264: hb_log(" + level: %s", job->encoder_level); default: break; } } if (job->vquality >= 0) { hb_log(" + quality: %.2f (%s)", job->vquality, hb_video_quality_get_name(job->vcodec)); } else { hb_log( " + bitrate: %d kbps, pass: %d", job->vbitrate, job->pass ); if(job->pass == 1 && job->fastfirstpass == 1 && (job->vcodec == HB_VCODEC_X264 || job->vcodec == HB_VCODEC_X265)) { hb_log( " + fast first pass" ); if (job->vcodec == HB_VCODEC_X264) { hb_log( " + options: ref=1:8x8dct=0:me=dia:trellis=0" ); hb_log( " analyse=i4x4 (if originally enabled, else analyse=none)" ); hb_log( " subq=2 (if originally greater than 2, else subq unchanged)" ); } } } if (job->color_matrix_code && (job->vcodec == HB_VCODEC_X264 || job->mux == HB_MUX_MP4V2)) { // color matrix is set: // 1) at the stream level (x264 only), // 2) at the container level (mp4v2 only) hb_log(" + custom color matrix: %s", job->color_matrix_code == 1 ? "ITU Bt.601 (NTSC)" : job->color_matrix_code == 2 ? "ITU Bt.601 (PAL)" : job->color_matrix_code == 3 ? "ITU Bt.709 (HD)" : "Custom"); } } if( job->indepth_scan ) { hb_log( " * Foreign Audio Search: %s%s%s", job->select_subtitle_config.dest == RENDERSUB ? "Render/Burn-in" : "Passthrough", job->select_subtitle_config.force ? ", Forced Only" : "", job->select_subtitle_config.default_track ? ", Default" : "" ); } for( i = 0; i < hb_list_count( job->list_subtitle ); i++ ) { subtitle = hb_list_item( job->list_subtitle, i ); if( subtitle ) { if( job->indepth_scan ) { hb_log( " + subtitle, %s (track %d, id 0x%x) %s [%s]", subtitle->lang, subtitle->track, subtitle->id, subtitle->format == PICTURESUB ? "Picture" : "Text", hb_subsource_name( subtitle->source ) ); } else if( subtitle->source == SRTSUB ) { /* For SRT, print offset and charset too */ hb_log( " * subtitle track %d, %s (track %d, id 0x%x) Text [SRT] -> %s%s, offset: %"PRId64", charset: %s", subtitle->out_track, subtitle->lang, subtitle->track, subtitle->id, subtitle->config.dest == RENDERSUB ? "Render/Burn-in" : "Passthrough", subtitle->config.default_track ? ", Default" : "", subtitle->config.offset, subtitle->config.src_codeset ); } else { hb_log( " * subtitle track %d, %s (track %d, id 0x%x) %s [%s] -> %s%s%s", subtitle->out_track, subtitle->lang, subtitle->track, subtitle->id, subtitle->format == PICTURESUB ? "Picture" : "Text", hb_subsource_name( subtitle->source ), subtitle->config.dest == RENDERSUB ? "Render/Burn-in" : "Passthrough", subtitle->config.force ? ", Forced Only" : "", subtitle->config.default_track ? ", Default" : "" ); } } } if( !job->indepth_scan ) { for( i = 0; i < hb_list_count( job->list_audio ); i++ ) { audio = hb_list_item( job->list_audio, i ); hb_log( " * audio track %d", audio->config.out.track ); if( audio->config.out.name ) hb_log( " + name: %s", audio->config.out.name ); hb_log( " + decoder: %s (track %d, id 0x%x)", audio->config.lang.description, audio->config.in.track + 1, audio->id ); if (audio->config.in.bitrate >= 1000) hb_log(" + bitrate: %d kbps, samplerate: %d Hz", audio->config.in.bitrate / 1000, audio->config.in.samplerate); else hb_log(" + samplerate: %d Hz", audio->config.in.samplerate); if( audio->config.out.codec & HB_ACODEC_PASS_FLAG ) { hb_log(" + %s", hb_audio_encoder_get_name(audio->config.out.codec)); } else { hb_log(" + mixdown: %s", hb_mixdown_get_name(audio->config.out.mixdown)); if( audio->config.out.normalize_mix_level != 0 ) { hb_log( " + normalized mixing levels" ); } if( audio->config.out.gain != 0.0 ) { hb_log( " + gain: %.fdB", audio->config.out.gain ); } if (audio->config.out.dynamic_range_compression > 0.0f && hb_audio_can_apply_drc(audio->config.in.codec, audio->config.in.codec_param, audio->config.out.codec)) { 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)); } hb_log(" + encoder: %s", hb_audio_encoder_get_long_name(audio->config.out.codec)); if (audio->config.out.bitrate > 0) { hb_log(" + bitrate: %d kbps, samplerate: %d Hz", audio->config.out.bitrate, audio->config.out.samplerate); } else if (audio->config.out.quality != HB_INVALID_AUDIO_QUALITY) { hb_log(" + quality: %.2f, samplerate: %d Hz", audio->config.out.quality, audio->config.out.samplerate); } else if (audio->config.out.samplerate > 0) { hb_log(" + samplerate: %d Hz", audio->config.out.samplerate); } if (audio->config.out.compression_level >= 0) { hb_log(" + compression level: %.2f", audio->config.out.compression_level); } } } } } /* Corrects framerates when actual duration and frame count numbers are known. */ void correct_framerate( hb_job_t * job ) { hb_interjob_t * interjob = hb_interjob_get( job->h ); if( ( job->sequence_id & 0xFFFFFF ) != ( interjob->last_job & 0xFFFFFF) ) return; // Interjob information is for a different encode. // compute actual output vrate from first pass interjob->vrate = job->vrate_base * ( (double)interjob->out_frame_count * 90000 / interjob->total_time ); interjob->vrate_base = job->vrate_base; } /** * Job initialization rountine. * Initializes fifos. * Creates work objects for synchronizer, video decoder, video renderer, video decoder, audio decoder, audio encoder, reader, muxer. * Launches thread for each work object with work_loop. * Loops while monitoring status of work threads and fifos. * Exits loop when conversion is done and fifos are empty. * Closes threads and frees fifos. * @param job Handle work hb_job_t. */ static void do_job(hb_job_t *job) { int i; hb_title_t *title; hb_interjob_t *interjob; hb_work_object_t *w; hb_work_object_t *sync; hb_work_object_t *muxer; hb_work_object_t *reader = hb_get_work(WORK_READER); hb_audio_t *audio; hb_subtitle_t *subtitle; unsigned int subtitle_highest = 0; unsigned int subtitle_lowest = 0; unsigned int subtitle_lowest_id = 0; unsigned int subtitle_forced_id = 0; unsigned int subtitle_forced_hits = 0; unsigned int subtitle_hit = 0; title = job->title; interjob = hb_interjob_get( job->h ); if( job->pass == 2 ) { correct_framerate( job ); } job->list_work = hb_list_init(); /* OpenCL */ if (job->use_opencl && (hb_ocl_init() || hb_init_opencl_run_env(0, NULL, "-I."))) { hb_log("work: failed to initialize OpenCL environment, using fallback"); job->use_opencl = 0; hb_ocl_close(); } hb_log( "starting job" ); /* Look for the scanned subtitle in the existing subtitle list * select_subtitle implies that we did a scan. */ if( !job->indepth_scan && interjob->select_subtitle ) { /* Disable forced subtitles if we didn't find any in the scan, so that * we display normal subtitles instead. */ if( interjob->select_subtitle->config.force && interjob->select_subtitle->forced_hits == 0 ) { interjob->select_subtitle->config.force = 0; } for( i = 0; i < hb_list_count( job->list_subtitle ); ) { subtitle = hb_list_item( job->list_subtitle, i ); if( subtitle ) { /* Remove the scanned subtitle from the list if * it would result in: * - an emty track (forced and no forced hits) * - an identical, duplicate subtitle track: * -> both (or neither) are forced * -> subtitle is not forced but all its hits are forced */ if( ( interjob->select_subtitle->id == subtitle->id ) && ( ( subtitle->config.force && interjob->select_subtitle->forced_hits == 0 ) || ( subtitle->config.force == interjob->select_subtitle->config.force ) || ( !subtitle->config.force && interjob->select_subtitle->hits == interjob->select_subtitle->forced_hits ) ) ) { hb_list_rem( job->list_subtitle, subtitle ); free( subtitle ); continue; } /* Adjust output track number, in case we removed one. * Output tracks sadly still need to be in sequential order. * Note: out.track starts at 1, i starts at 0, and track 1 is interjob->select_subtitle */ subtitle->out_track = ++i + 1; } else { // avoid infinite loop is subtitle == NULL i++; } } /* Add the subtitle that we found on the subtitle scan pass. * * Make sure it's the first subtitle in the list so that it becomes the * first burned subtitle (explicitly or after sanitizing) - which should * ensure that it doesn't get dropped. */ interjob->select_subtitle->out_track = 1; if (job->pass == 0 || job->pass == 2) { // final pass, interjob->select_subtitle is no longer needed hb_list_insert(job->list_subtitle, 0, interjob->select_subtitle); interjob->select_subtitle = NULL; } else { // this is not the final pass, so we need to copy it instead hb_list_insert(job->list_subtitle, 0, hb_subtitle_copy(interjob->select_subtitle)); } } if ( !job->indepth_scan ) { // Sanitize subtitles uint8_t one_burned = 0; for( i = 0; i < hb_list_count( job->list_subtitle ); ) { subtitle = hb_list_item( job->list_subtitle, i ); if ( subtitle->config.dest == RENDERSUB ) { if ( one_burned ) { if ( !hb_subtitle_can_pass(subtitle->source, job->mux) ) { hb_log( "More than one subtitle burn-in requested, dropping track %d.", i ); hb_list_rem( job->list_subtitle, subtitle ); free( subtitle ); continue; } else { hb_log( "More than one subtitle burn-in requested. Changing track %d to soft subtitle.", i ); subtitle->config.dest = PASSTHRUSUB; } } else if ( !hb_subtitle_can_burn(subtitle->source) ) { hb_log( "Subtitle burn-in requested and input track can not be rendered. Changing track %d to soft subtitle.", i ); subtitle->config.dest = PASSTHRUSUB; } else { one_burned = 1; } } if ( subtitle->config.dest == PASSTHRUSUB && !hb_subtitle_can_pass(subtitle->source, job->mux) ) { if ( !one_burned ) { hb_log( "Subtitle pass-thru requested and input track is not compatible with container. Changing track %d to burned-in subtitle.", i ); subtitle->config.dest = RENDERSUB; subtitle->config.default_track = 0; one_burned = 1; } else { hb_log( "Subtitle pass-thru requested and input track is not compatible with container. One track already burned, dropping track %d.", i ); hb_list_rem( job->list_subtitle, subtitle ); free( subtitle ); continue; } } /* Adjust output track number, in case we removed one. * Output tracks sadly still need to be in sequential order. * Note: out.track starts at 1, i starts at 0 */ subtitle->out_track = ++i; } if (one_burned) { // Add subtitle rendering filter // Note that if the filter is already in the filter chain, this // has no effect. Note also that this means the front-end is // not required to add the subtitle rendering filter since // we will always try to do it here. hb_filter_object_t *filter = hb_filter_init(HB_FILTER_RENDER_SUB); char *filter_settings = hb_strdup_printf("%d:%d:%d:%d", job->crop[0], job->crop[1], job->crop[2], job->crop[3]); hb_add_filter(job, filter, filter_settings); free(filter_settings); } } #ifdef USE_QSV /* * XXX: mfxCoreInterface's CopyFrame doesn't work in old drivers, and our * workaround is really slow. If we have validated CPU-based filters in * the list and we can't use CopyFrame, disable QSV decoding until a * better solution is implemented. */ if (hb_qsv_copyframe_is_slow(job->vcodec)) { if (job->list_filter != NULL) { int encode_only = 0; for (i = 0; i < hb_list_count(job->list_filter) && !encode_only; i++) { hb_filter_object_t *filter = hb_list_item(job->list_filter, i); switch (filter->id) { // validated, CPU-based filters case HB_FILTER_ROTATE: case HB_FILTER_RENDER_SUB: encode_only = 1; break; // CPU-based deinterlace (validated) case HB_FILTER_DEINTERLACE: if (filter->settings != NULL && strcasecmp(filter->settings, "qsv") != 0) { encode_only = 1; } break; // other filters will be removed default: break; } } if (encode_only) { hb_log("do_job: QSV: possible CopyFrame bug, using encode-only path"); if (hb_get_cpu_platform() >= HB_CPU_PLATFORM_INTEL_IVB) { hb_log("do_job: QSV: please update your Intel graphics driver to version 9.18.10.3257 or later"); } job->qsv.decode = 0; } } } /* * When QSV is used for decoding, not all CPU-based filters are supported, * so we need to do a little extra setup here. */ if (hb_qsv_decode_is_enabled(job)) { int vpp_settings[7]; int num_cpu_filters = 0; hb_filter_object_t *filter; // default values for VPP filter vpp_settings[0] = job->title->width; vpp_settings[1] = job->title->height; vpp_settings[2] = job->title->crop[0]; vpp_settings[3] = job->title->crop[1]; vpp_settings[4] = job->title->crop[2]; vpp_settings[5] = job->title->crop[3]; vpp_settings[6] = 0; // deinterlace: off if (job->list_filter != NULL && hb_list_count(job->list_filter) > 0) { while (hb_list_count(job->list_filter) > num_cpu_filters) { filter = hb_list_item(job->list_filter, num_cpu_filters); switch (filter->id) { // cropping and scaling always done via VPP filter case HB_FILTER_CROP_SCALE: if (filter->settings == NULL || *filter->settings == '\0') { // VPP defaults were set above, so not a problem // however, this should never happen, print an error hb_error("do_job: '%s': no settings!", filter->name); } else { sscanf(filter->settings, "%d:%d:%d:%d:%d:%d", &vpp_settings[0], &vpp_settings[1], &vpp_settings[2], &vpp_settings[3], &vpp_settings[4], &vpp_settings[5]); } hb_list_rem(job->list_filter, filter); hb_filter_close(&filter); break; // pick VPP or CPU deinterlace depending on settings case HB_FILTER_DEINTERLACE: if (filter->settings == NULL || strcasecmp(filter->settings, "qsv") == 0) { // deinterlacing via VPP filter vpp_settings[6] = 1; hb_list_rem(job->list_filter, filter); hb_filter_close(&filter); } else { // validated num_cpu_filters++; } break; // then, validated filters case HB_FILTER_ROTATE: // TODO: use Media SDK for this case HB_FILTER_RENDER_SUB: num_cpu_filters++; break; // finally, drop all unsupported filters default: hb_log("do_job: QSV: full path, removing unsupported filter '%s'", filter->name); hb_list_rem(job->list_filter, filter); hb_filter_close(&filter); break; } } if (num_cpu_filters > 0) { // we need filters to copy to system memory and back filter = hb_filter_init(HB_FILTER_QSV_PRE); hb_add_filter(job, filter, NULL); filter = hb_filter_init(HB_FILTER_QSV_POST); hb_add_filter(job, filter, NULL); } if (vpp_settings[0] != job->title->width || vpp_settings[1] != job->title->height || vpp_settings[2] >= 1 /* crop */ || vpp_settings[3] >= 1 /* crop */ || vpp_settings[4] >= 1 /* crop */ || vpp_settings[5] >= 1 /* crop */ || vpp_settings[6] >= 1 /* deinterlace */) { // we need the VPP filter char *settings = hb_strdup_printf("%d:%d:%d:%d:%d:%d_dei:%d", vpp_settings[0], vpp_settings[1], vpp_settings[2], vpp_settings[3], vpp_settings[4], vpp_settings[5], vpp_settings[6]); filter = hb_filter_init(HB_FILTER_QSV); hb_add_filter(job, filter, settings); free(settings); } } } #endif // Filters have an effect on settings. // So initialize the filters and update the job. if( job->list_filter && hb_list_count( job->list_filter ) ) { hb_filter_init_t init; init.job = job; init.pix_fmt = AV_PIX_FMT_YUV420P; init.width = title->width; init.height = title->height; /* DXVA2 */ init.use_dxva = hb_use_dxva(title); init.par_width = job->anamorphic.par_width; init.par_height = job->anamorphic.par_height; memcpy(init.crop, title->crop, sizeof(int[4])); init.vrate_base = title->rate_base; init.vrate = title->rate; init.cfr = 0; for( i = 0; i < hb_list_count( job->list_filter ); ) { hb_filter_object_t * filter = hb_list_item( job->list_filter, i ); if( filter->init( filter, &init ) ) { hb_log( "Failure to initialise filter '%s', disabling", filter->name ); hb_list_rem( job->list_filter, filter ); hb_filter_close( &filter ); continue; } i++; } job->width = init.width; job->height = init.height; job->anamorphic.par_width = init.par_width; job->anamorphic.par_height = init.par_height; memcpy(job->crop, init.crop, sizeof(int[4])); job->vrate_base = init.vrate_base; job->vrate = init.vrate; job->cfr = init.cfr; } if( job->anamorphic.mode ) { /* While x264 is smart enough to reduce fractions on its own, libavcodec and * the MacGUI need some help with the math, so lose superfluous factors. */ hb_reduce( &job->anamorphic.par_width, &job->anamorphic.par_height, job->anamorphic.par_width, job->anamorphic.par_height ); if( job->vcodec & HB_VCODEC_FFMPEG_MASK ) { /* Just to make working with ffmpeg even more fun, * lavc's MPEG-4 encoder can't handle PAR values >= 255, * even though AVRational does. Adjusting downwards * distorts the display aspect slightly, but such is life. */ while( ( job->anamorphic.par_width & ~0xFF ) || ( job->anamorphic.par_height & ~0xFF ) ) { job->anamorphic.par_width >>= 1; job->anamorphic.par_height >>= 1; hb_reduce( &job->anamorphic.par_width, &job->anamorphic.par_height, job->anamorphic.par_width, job->anamorphic.par_height ); } } } #ifdef USE_QSV if (hb_qsv_decode_is_enabled(job)) { job->fifo_mpeg2 = hb_fifo_init( FIFO_MINI, FIFO_MINI_WAKE ); job->fifo_raw = hb_fifo_init( FIFO_MINI, FIFO_MINI_WAKE ); job->fifo_sync = hb_fifo_init( FIFO_MINI, FIFO_MINI_WAKE ); job->fifo_render = hb_fifo_init( FIFO_MINI, FIFO_MINI_WAKE ); job->fifo_mpeg4 = hb_fifo_init( FIFO_MINI, FIFO_MINI_WAKE ); } else #endif { job->fifo_mpeg2 = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE ); job->fifo_raw = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); job->fifo_sync = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); job->fifo_mpeg4 = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE ); job->fifo_render = NULL; // Attached to filter chain } /* Audio fifos must be initialized before sync */ if (!job->indepth_scan) { // apply Auto Passthru settings hb_autopassthru_apply_settings(job); // sanitize audio settings for (i = 0; i < hb_list_count(job->list_audio);) { audio = hb_list_item(job->list_audio, i); if (audio->config.out.codec == HB_ACODEC_AUTO_PASS) { // Auto Passthru should have been handled above // remove track to avoid a crash hb_log("Auto Passthru error, dropping track %d", audio->config.out.track); hb_list_rem(job->list_audio, audio); free(audio); continue; } if ((audio->config.out.codec & HB_ACODEC_PASS_FLAG) && !(audio->config.in.codec & audio->config.out.codec & HB_ACODEC_PASS_MASK)) { hb_log("Passthru requested and input codec is not the same as output codec for track %d, dropping track", audio->config.out.track); hb_list_rem(job->list_audio, audio); free(audio); continue; } /* Adjust output track number, in case we removed one. * Output tracks sadly still need to be in sequential order. * Note: out.track starts at 1, i starts at 0 */ audio->config.out.track = ++i; } int best_mixdown = 0; int best_bitrate = 0; int best_samplerate = 0; for (i = 0; i < hb_list_count(job->list_audio); i++) { audio = hb_list_item(job->list_audio, i); /* set up the audio work structures */ audio->priv.fifo_raw = hb_fifo_init(FIFO_SMALL, FIFO_SMALL_WAKE); audio->priv.fifo_sync = hb_fifo_init(FIFO_SMALL, FIFO_SMALL_WAKE); audio->priv.fifo_out = hb_fifo_init(FIFO_LARGE, FIFO_LARGE_WAKE); audio->priv.fifo_in = hb_fifo_init(FIFO_LARGE, FIFO_LARGE_WAKE); /* Passthru audio */ if (audio->config.out.codec & HB_ACODEC_PASS_FLAG) { // Muxer needs these to be set correctly in order to // set audio track MP4 time base. audio->config.out.samples_per_frame = audio->config.in.samples_per_frame; audio->config.out.samplerate = audio->config.in.samplerate; continue; } /* Vorbis language information */ if (audio->config.out.codec == HB_ACODEC_VORBIS) audio->priv.config.vorbis.language = audio->config.lang.simple; /* sense-check the requested samplerate */ if (audio->config.out.samplerate <= 0) { // if not specified, set to same as input audio->config.out.samplerate = audio->config.in.samplerate; } best_samplerate = hb_audio_samplerate_get_best(audio->config.out.codec, audio->config.out.samplerate, NULL); if (best_samplerate != audio->config.out.samplerate) { hb_log("work: sanitizing track %d unsupported samplerate %d Hz to %s kHz", audio->config.out.track, audio->config.out.samplerate, hb_audio_samplerate_get_name(best_samplerate)); audio->config.out.samplerate = best_samplerate; } /* sense-check the requested mixdown */ if (audio->config.out.mixdown <= HB_AMIXDOWN_NONE) { /* Mixdown not specified, set the default mixdown */ audio->config.out.mixdown = hb_mixdown_get_default(audio->config.out.codec, audio->config.in.channel_layout); hb_log("work: mixdown not specified, track %d setting mixdown %s", audio->config.out.track, hb_mixdown_get_name(audio->config.out.mixdown)); } else { best_mixdown = hb_mixdown_get_best(audio->config.out.codec, audio->config.in.channel_layout, audio->config.out.mixdown); if (audio->config.out.mixdown != best_mixdown) { /* log the output mixdown */ hb_log("work: sanitizing track %d mixdown %s to %s", audio->config.out.track, hb_mixdown_get_name(audio->config.out.mixdown), hb_mixdown_get_name(best_mixdown)); audio->config.out.mixdown = best_mixdown; } } /* sense-check the requested compression level */ if (audio->config.out.compression_level < 0) { audio->config.out.compression_level = hb_audio_compression_get_default(audio->config.out.codec); if (audio->config.out.compression_level >= 0) { hb_log("work: compression level not specified, track %d setting compression level %.2f", audio->config.out.track, audio->config.out.compression_level); } } else { float best_compression = hb_audio_compression_get_best(audio->config.out.codec, audio->config.out.compression_level); if (best_compression != audio->config.out.compression_level) { if (best_compression == -1) { hb_log("work: track %d, compression level not supported by codec", audio->config.out.track); } else { hb_log("work: sanitizing track %d compression level %.2f to %.2f", audio->config.out.track, audio->config.out.compression_level, best_compression); } audio->config.out.compression_level = best_compression; } } /* sense-check the requested quality */ if (audio->config.out.quality != HB_INVALID_AUDIO_QUALITY) { float best_quality = hb_audio_quality_get_best(audio->config.out.codec, audio->config.out.quality); if (best_quality != audio->config.out.quality) { if (best_quality == HB_INVALID_AUDIO_QUALITY) { hb_log("work: track %d, quality mode not supported by codec", audio->config.out.track); } else { hb_log("work: sanitizing track %d quality %.2f to %.2f", audio->config.out.track, audio->config.out.quality, best_quality); } audio->config.out.quality = best_quality; } } /* sense-check the requested bitrate */ if (audio->config.out.quality == HB_INVALID_AUDIO_QUALITY) { if (audio->config.out.bitrate <= 0) { /* Bitrate not specified, set the default bitrate */ audio->config.out.bitrate = hb_audio_bitrate_get_default(audio->config.out.codec, audio->config.out.samplerate, audio->config.out.mixdown); if (audio->config.out.bitrate > 0) { hb_log("work: bitrate not specified, track %d setting bitrate %d Kbps", audio->config.out.track, audio->config.out.bitrate); } } else { best_bitrate = hb_audio_bitrate_get_best(audio->config.out.codec, audio->config.out.bitrate, audio->config.out.samplerate, audio->config.out.mixdown); if (best_bitrate > 0 && best_bitrate != audio->config.out.bitrate) { /* log the output bitrate */ hb_log("work: sanitizing track %d bitrate %d to %d Kbps", audio->config.out.track, audio->config.out.bitrate, best_bitrate); } 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); } } } /* Synchronization */ sync = hb_sync_init( job ); /* Video decoder */ if (title->video_codec == WORK_NONE) { hb_error("No video decoder set!"); goto cleanup; } hb_list_add(job->list_work, (w = hb_get_work(title->video_codec))); w->codec_param = title->video_codec_param; w->fifo_in = job->fifo_mpeg2; w->fifo_out = job->fifo_raw; for( i = 0; i < hb_list_count( job->list_subtitle ); i++ ) { subtitle = hb_list_item( job->list_subtitle, i ); if( subtitle ) { subtitle->fifo_in = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); // Must set capacity of the raw-FIFO to be set >= the maximum number of subtitle // lines that could be decoded prior to a video frame in order to prevent the following // deadlock condition: // 1. Subtitle decoder blocks trying to generate more subtitle lines than will fit in the FIFO. // 2. Blocks the processing of further subtitle packets read from the input stream. // 3. And that blocks the processing of any further video packets read from the input stream. // 4. And that blocks the sync work-object from running, which is needed to consume the subtitle lines in the raw-FIFO. // Since that number is unbounded, the FIFO must be made (effectively) unbounded in capacity. subtitle->fifo_raw = hb_fifo_init( FIFO_UNBOUNDED, FIFO_UNBOUNDED_WAKE ); subtitle->fifo_sync = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); subtitle->fifo_out = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); w = hb_get_work( subtitle->codec ); w->fifo_in = subtitle->fifo_in; w->fifo_out = subtitle->fifo_raw; w->subtitle = subtitle; hb_list_add( job->list_work, w ); } } /* Set up the video filter fifo pipeline */ if( !job->indepth_scan ) { if( job->list_filter ) { int filter_count = hb_list_count( job->list_filter ); int i; hb_fifo_t * fifo_in = job->fifo_sync; for( i = 0; i < filter_count; i++ ) { hb_filter_object_t * filter = hb_list_item( job->list_filter, i ); filter->fifo_in = fifo_in; filter->fifo_out = hb_fifo_init( FIFO_MINI, FIFO_MINI_WAKE ); fifo_in = filter->fifo_out; } job->fifo_render = fifo_in; } else if ( !job->list_filter ) { hb_log("work: Internal Error: no filters"); job->fifo_render = NULL; } /* Video encoder */ switch( job->vcodec ) { case HB_VCODEC_FFMPEG_MPEG4: w = hb_get_work( WORK_ENCAVCODEC ); w->codec_param = AV_CODEC_ID_MPEG4; break; case HB_VCODEC_FFMPEG_MPEG2: w = hb_get_work( WORK_ENCAVCODEC ); w->codec_param = AV_CODEC_ID_MPEG2VIDEO; break; case HB_VCODEC_FFMPEG_VP8: w = hb_get_work( WORK_ENCAVCODEC ); w->codec_param = AV_CODEC_ID_VP8; break; case HB_VCODEC_X264: w = hb_get_work( WORK_ENCX264 ); break; case HB_VCODEC_QSV_H264: w = hb_get_work( WORK_ENCQSV ); break; case HB_VCODEC_THEORA: w = hb_get_work( WORK_ENCTHEORA ); break; #ifdef USE_X265 case HB_VCODEC_X265: w = hb_get_work( WORK_ENCX265 ); break; #endif } // Handle case where there are no filters. // This really should never happen. if ( job->fifo_render ) w->fifo_in = job->fifo_render; else w->fifo_in = job->fifo_sync; w->fifo_out = job->fifo_mpeg4; w->config = &job->config; hb_list_add( job->list_work, w ); for( i = 0; i < hb_list_count( job->list_audio ); i++ ) { audio = hb_list_item( job->list_audio, i ); /* * Audio Decoder Thread */ if ( audio->priv.fifo_in ) { if ( ( w = hb_codec_decoder( audio->config.in.codec ) ) == NULL ) { hb_error("Invalid input codec: %d", audio->config.in.codec); *job->done_error = HB_ERROR_WRONG_INPUT; *job->die = 1; goto cleanup; } w->fifo_in = audio->priv.fifo_in; w->fifo_out = audio->priv.fifo_raw; w->config = &audio->priv.config; w->audio = audio; w->codec_param = audio->config.in.codec_param; hb_list_add( job->list_work, w ); } /* * Audio Encoder Thread */ if ( !(audio->config.out.codec & HB_ACODEC_PASS_FLAG ) ) { /* * Add the encoder thread if not doing AC-3 pass through */ if ( ( w = hb_codec_encoder( audio->config.out.codec ) ) == NULL ) { hb_error("Invalid audio codec: %#x", audio->config.out.codec); w = NULL; *job->done_error = HB_ERROR_WRONG_INPUT; *job->die = 1; goto cleanup; } w->fifo_in = audio->priv.fifo_sync; w->fifo_out = audio->priv.fifo_out; w->config = &audio->priv.config; w->audio = audio; hb_list_add( job->list_work, w ); } } } if( job->chapter_markers && job->chapter_start == job->chapter_end ) { job->chapter_markers = 0; hb_log("work: only 1 chapter, disabling chapter markers"); } /* Display settings */ hb_display_job_info( job ); /* Init read & write threads */ if ( reader->init( reader, job ) ) { hb_error( "Failure to initialise thread '%s'", reader->name ); *job->done_error = HB_ERROR_INIT; *job->die = 1; goto cleanup; } reader->done = &job->done; reader->thread = hb_thread_init( reader->name, ReadLoop, reader, HB_NORMAL_PRIORITY ); job->done = 0; if( job->list_filter && !job->indepth_scan ) { int filter_count = hb_list_count( job->list_filter ); int i; for( i = 0; i < filter_count; i++ ) { hb_filter_object_t * filter = hb_list_item( job->list_filter, i ); if( !filter ) continue; // Filters were initialized earlier, so we just need // to start the filter's thread filter->done = &job->done; filter->thread = hb_thread_init( filter->name, filter_loop, filter, HB_LOW_PRIORITY ); } } /* Launch processing threads */ for( i = 0; i < hb_list_count( job->list_work ); i++ ) { w = hb_list_item( job->list_work, i ); w->done = &job->done; w->thread_sleep_interval = 10; if( w->init( w, job ) ) { hb_error( "Failure to initialise thread '%s'", w->name ); *job->done_error = HB_ERROR_INIT; *job->die = 1; goto cleanup; } w->thread = hb_thread_init( w->name, work_loop, w, HB_LOW_PRIORITY ); } if ( job->indepth_scan ) { muxer = NULL; w = sync; sync->done = &job->done; } else { sync->done = &job->done; sync->thread_sleep_interval = 10; if( sync->init( w, job ) ) { hb_error( "Failure to initialise thread '%s'", w->name ); *job->done_error = HB_ERROR_INIT; *job->die = 1; goto cleanup; } sync->thread = hb_thread_init( sync->name, work_loop, sync, HB_LOW_PRIORITY ); // The muxer requires track information that's set up by the encoder // init routines so we have to init the muxer last. muxer = hb_muxer_init( job ); w = muxer; } hb_buffer_t * buf_in, * buf_out = NULL; while ( !*job->die && !*w->done && w->status != HB_WORK_DONE ) { buf_in = hb_fifo_get_wait( w->fifo_in ); if ( buf_in == NULL ) continue; if ( *job->die ) { if( buf_in ) { hb_buffer_close( &buf_in ); } break; } buf_out = NULL; w->status = w->work( w, &buf_in, &buf_out ); if( buf_in ) { hb_buffer_close( &buf_in ); } if ( buf_out && w->fifo_out == NULL ) { hb_buffer_close( &buf_out ); } if( buf_out ) { while ( !*job->die ) { if ( hb_fifo_full_wait( w->fifo_out ) ) { hb_fifo_push( w->fifo_out, buf_out ); buf_out = NULL; break; } } } } if ( buf_out ) { hb_buffer_close( &buf_out ); } hb_handle_t * h = job->h; hb_state_t state; hb_get_state( h, &state ); hb_log("work: average encoding speed for job is %f fps", state.param.working.rate_avg); job->done = 1; if( muxer != NULL ) { muxer->close( muxer ); free( muxer ); if( sync->thread != NULL ) { hb_thread_close( &sync->thread ); sync->close( sync ); } free( sync ); } cleanup: /* Stop the write thread (thread_close will block until the muxer finishes) */ job->done = 1; // Close render filter pipeline if( job->list_filter ) { int filter_count = hb_list_count( job->list_filter ); int i; for( i = 0; i < filter_count; i++ ) { hb_filter_object_t * filter = hb_list_item( job->list_filter, i ); if( !filter ) continue; if( filter->thread != NULL ) { hb_thread_close( &filter->thread ); } filter->close( filter ); } } /* Close work objects */ while( ( w = hb_list_item( job->list_work, 0 ) ) ) { hb_list_rem( job->list_work, w ); if( w->thread != NULL ) { hb_thread_close( &w->thread ); w->close( w ); } free( w ); } hb_list_close( &job->list_work ); /* Stop the read thread */ if( reader->thread != NULL ) { hb_thread_close( &reader->thread ); reader->close( reader ); } free( reader ); /* Close fifos */ hb_fifo_close( &job->fifo_mpeg2 ); hb_fifo_close( &job->fifo_raw ); hb_fifo_close( &job->fifo_sync ); hb_fifo_close( &job->fifo_mpeg4 ); for( i = 0; i < hb_list_count( job->list_subtitle ); i++ ) { subtitle = hb_list_item( job->list_subtitle, i ); if( subtitle ) { hb_fifo_close( &subtitle->fifo_in ); hb_fifo_close( &subtitle->fifo_raw ); hb_fifo_close( &subtitle->fifo_sync ); hb_fifo_close( &subtitle->fifo_out ); } } for( i = 0; i < hb_list_count( job->list_audio ); i++ ) { audio = hb_list_item( job->list_audio, i ); if( audio->priv.fifo_in != NULL ) hb_fifo_close( &audio->priv.fifo_in ); if( audio->priv.fifo_raw != NULL ) hb_fifo_close( &audio->priv.fifo_raw ); if( audio->priv.fifo_sync != NULL ) hb_fifo_close( &audio->priv.fifo_sync ); if( audio->priv.fifo_out != NULL ) hb_fifo_close( &audio->priv.fifo_out ); } if( job->list_filter ) { for( i = 0; i < hb_list_count( job->list_filter ); i++ ) { hb_filter_object_t * filter = hb_list_item( job->list_filter, i ); hb_fifo_close( &filter->fifo_out ); } } if( job->indepth_scan ) { /* Before closing the title print out our subtitle stats if we need to * find the highest and lowest. */ for( i = 0; i < hb_list_count( job->list_subtitle ); i++ ) { subtitle = hb_list_item( job->list_subtitle, i ); hb_log( "Subtitle track %d (id 0x%x) '%s': %d hits (%d forced)", subtitle->track, subtitle->id, subtitle->lang, subtitle->hits, subtitle->forced_hits ); if( subtitle->hits == 0 ) continue; if( subtitle_highest < subtitle->hits ) { subtitle_highest = subtitle->hits; } if( subtitle_lowest == 0 || subtitle_lowest > subtitle->hits ) { subtitle_lowest = subtitle->hits; subtitle_lowest_id = subtitle->id; } // pick the track with fewest forced hits if( subtitle->forced_hits > 0 && ( subtitle_forced_hits == 0 || subtitle_forced_hits > subtitle->forced_hits ) ) { subtitle_forced_id = subtitle->id; subtitle_forced_hits = subtitle->forced_hits; } } if( subtitle_forced_id && job->select_subtitle_config.force ) { /* If there is a subtitle stream with forced subtitles and forced-only * is set, then select it in preference to the lowest. */ subtitle_hit = subtitle_forced_id; hb_log( "Found a subtitle candidate with id 0x%x (contains forced subs)", subtitle_hit ); } else if( subtitle_lowest > 0 && subtitle_lowest < ( subtitle_highest * 0.1 ) ) { /* OK we have more than one, and the lowest is lower, * but how much lower to qualify for turning it on by * default? * * Let's say 10% as a default. */ subtitle_hit = subtitle_lowest_id; hb_log( "Found a subtitle candidate with id 0x%x", subtitle_hit ); } else { hb_log( "No candidate detected during subtitle scan" ); } for( i = 0; i < hb_list_count( job->list_subtitle ); i++ ) { subtitle = hb_list_item( job->list_subtitle, i ); if( subtitle->id == subtitle_hit ) { subtitle->config = job->select_subtitle_config; // Remove from list since we are taking ownership // of the subtitle. hb_list_rem( job->list_subtitle, subtitle ); interjob->select_subtitle = subtitle; break; } } } hb_buffer_pool_free(); /* OpenCL: must be closed *after* freeing the buffer pool */ if (job->use_opencl) { hb_ocl_close(); } hb_job_close( &job ); } static inline void copy_chapter( hb_buffer_t * dst, hb_buffer_t * src ) { // Propagate any chapter breaks for the worker if and only if the // output frame has the same time stamp as the input frame (any // worker that delays frames has to propagate the chapter marks itself // and workers that move chapter marks to a different time should set // 'src' to NULL so that this code won't generate spurious duplicates.) if( src && dst && src->s.start == dst->s.start) { // restore log below to debug chapter mark propagation problems dst->s.new_chap = src->s.new_chap; } } /** * Performs the work object's specific work function. * Loops calling work function for associated work object. Sleeps when fifo is full. * Monitors work done indicator. * Exits loop when work indiactor is set. * @param _w Handle to work object. */ static void work_loop( void * _w ) { hb_work_object_t * w = _w; hb_buffer_t * buf_in = NULL, * buf_out = NULL; while( !*w->done && w->status != HB_WORK_DONE ) { buf_in = hb_fifo_get_wait( w->fifo_in ); if ( buf_in == NULL ) continue; if ( *w->done ) { if( buf_in ) { hb_buffer_close( &buf_in ); } break; } // Invalidate buf_out so that if there is no output // we don't try to pass along junk. buf_out = NULL; w->status = w->work( w, &buf_in, &buf_out ); copy_chapter( buf_out, buf_in ); if( buf_in ) { hb_buffer_close( &buf_in ); } if ( buf_out && w->fifo_out == NULL ) { hb_buffer_close( &buf_out ); } if( buf_out ) { while ( !*w->done ) { if ( hb_fifo_full_wait( w->fifo_out ) ) { hb_fifo_push( w->fifo_out, buf_out ); buf_out = NULL; break; } } } } if ( buf_out ) { hb_buffer_close( &buf_out ); } // Consume data in incoming fifo till job complete so that // residual data does not stall the pipeline while( !*w->done ) { buf_in = hb_fifo_get_wait( w->fifo_in ); if ( buf_in != NULL ) hb_buffer_close( &buf_in ); } } /** * Performs the filter object's specific work function. * Loops calling work function for associated filter object. * Sleeps when fifo is full. * Monitors work done indicator. * Exits loop when work indiactor is set. * @param _w Handle to work object. */ static void filter_loop( void * _f ) { hb_filter_object_t * f = _f; hb_buffer_t * buf_in, * buf_out = NULL; while( !*f->done && f->status != HB_FILTER_DONE ) { buf_in = hb_fifo_get_wait( f->fifo_in ); if ( buf_in == NULL ) continue; // Filters can drop buffers. Remember chapter information // so that it can be propagated to the next buffer if ( buf_in->s.new_chap ) { f->chapter_time = buf_in->s.start; f->chapter_val = buf_in->s.new_chap; // don't let 'filter_loop' put a chapter mark on the wrong buffer buf_in->s.new_chap = 0; } if ( *f->done ) { if( buf_in ) { hb_buffer_close( &buf_in ); } break; } buf_out = NULL; #ifdef USE_QSV hb_buffer_t *last_buf_in = buf_in; #endif f->status = f->work( f, &buf_in, &buf_out ); #ifdef USE_QSV if (f->status == HB_FILTER_DELAY && last_buf_in->qsv_details.filter_details != NULL && buf_out == NULL) { hb_filter_private_t_qsv *qsv_user = buf_in ? buf_in->qsv_details.filter_details : last_buf_in->qsv_details.filter_details ; qsv_user->post.status = f->status; hb_lock(qsv_user->post.frame_completed_lock); qsv_user->post.frame_go = 1; hb_cond_broadcast(qsv_user->post.frame_completed); hb_unlock(qsv_user->post.frame_completed_lock); } #endif if ( buf_out && f->chapter_val && f->chapter_time <= buf_out->s.start ) { buf_out->s.new_chap = f->chapter_val; f->chapter_val = 0; } if( buf_in ) { hb_buffer_close( &buf_in ); } if ( buf_out && f->fifo_out == NULL ) { hb_buffer_close( &buf_out ); } if( buf_out ) { while ( !*f->done ) { if ( hb_fifo_full_wait( f->fifo_out ) ) { hb_fifo_push( f->fifo_out, buf_out ); buf_out = NULL; break; } } } } if ( buf_out ) { hb_buffer_close( &buf_out ); } // Consume data in incoming fifo till job complete so that // residual data does not stall the pipeline while( !*f->done ) { buf_in = hb_fifo_get_wait( f->fifo_in ); if ( buf_in != NULL ) hb_buffer_close( &buf_in ); } } HandBrake-0.10.2/libhb/module.rules0000664000175200017520000000245511171166721017471 0ustar handbrakehandbrake$(eval $(call import.MODULE.rules,LIBHB)) libhb.build: $(LIBHB.a) $(LIBHB.a): | $(dir $(LIBHB.a)) $(LIBHB.a): $(LIBHB.c.o) $(LIBHB.yasm.o) $(AR.exe) rsu $@ $^ $(LIBHB.c.o): $(LIBHB.d) $(LIBHB.c.o): | $(dir $(LIBHB.c.o)) $(LIBHB.c.o): $(BUILD/)%.o: $(SRC/)%.c $(call LIBHB.GCC.C_O,$@,$<) $(LIBHB.m4.out): $(BUILD/)project/handbrake.m4 $(LIBHB.m4.out): | $(dir $(LIBHB.m4.out)) $(LIBHB.m4.out): $(LIBHB.build/)%: $(LIBHB.src/)%.m4 $(M4.exe) -Iproject $< > $@ $(LIBHB.h.out): | $(dir $(LIBHB.h.out)) $(LIBHB.h.out): $(BUILD/)%: $(SRC/)% $(CP.exe) $< $@ libhb.clean: $(RM.exe) -f $(LIBHB.out) ############################################################################### ifneq (disabled,$(FEATURE.asm)) $(LIBHB.yasm.o): $(LIBHB.yasm.d) $(LIBHB.yasm.o): | $(dir $(LIBHB.yasm.o)) $(LIBHB.yasm.o): $(LIBHB.yasm.build/)%.o: $(LIBHB.yasm.src/)%.asm $(call LIBHB.YASM.ASM_O,$@,$<) endif ############################################################################### ifeq (1-mingw,$(BUILD.cross)-$(BUILD.system)) libhb.build: $(LIBHB.dll) $(LIBHB.dll): | $(dirname $(LIBHB.dll) $(LIBHB.lib)) $(LIBHB.dll): $(LIBHB.c.o) $(LIBHB.yasm.o) $(call LIBHB.GCC.DYLIB++,$@,$^ $(LIBHB.dll.libs)) endif ############################################################################### clean: libhb.clean build: libhb.build HandBrake-0.10.2/libhb/qsv_filter_pp.c0000664000175200017520000010162512220306351020137 0ustar handbrakehandbrake/* ********************************************************************* *\ Copyright (C) 2013 Intel Corporation. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \* ********************************************************************* */ #ifdef USE_QSV #include "hb.h" #include "hbffmpeg.h" #include "libavcodec/qsv.h" #include "qsv_filter_pp.h" #include "qsv_filter.h" #include "qsv_memory.h" static int hb_qsv_filter_pre_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int hb_qsv_filter_pre_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static int hb_qsv_filter_pre_info( hb_filter_object_t * filter, hb_filter_info_t * info ); static void hb_qsv_filter_pre_close( hb_filter_object_t * filter ); static int hb_qsv_filter_post_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int hb_qsv_filter_post_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static int hb_qsv_filter_post_info( hb_filter_object_t * filter, hb_filter_info_t * info ); static void hb_qsv_filter_post_close( hb_filter_object_t * filter ); hb_filter_object_t hb_filter_qsv_pre = { .id = HB_FILTER_QSV_PRE, .enforce_order = 1, .name = "Quick Sync Video user filter (pre)", .settings = NULL, .init = hb_qsv_filter_pre_init, .work = hb_qsv_filter_pre_work, .close = hb_qsv_filter_pre_close, .info = hb_qsv_filter_pre_info, }; hb_filter_object_t hb_filter_qsv_post = { .id = HB_FILTER_QSV_POST, .enforce_order = 1, .name = "Quick Sync Video user filter (post)", .settings = NULL, .init = hb_qsv_filter_post_init, .work = hb_qsv_filter_post_work, .close = hb_qsv_filter_post_close, .info = hb_qsv_filter_post_info, }; static int filter_pre_init( av_qsv_context* qsv, hb_filter_private_t * pv ){ mfxStatus sts = MFX_ERR_NONE; int i=0; if(!qsv) return 3; av_qsv_space *prev_vpp = 0; if(!qsv->vpp_space){ qsv->vpp_space = av_qsv_list_init(HAVE_THREADS); // note some change as : when no size changes -> no VPP used // impact on : prev_vpp } if(!pv->vpp_space){ for(i=0; ivpp_space);i++){ av_qsv_space *qsv_vpp = av_qsv_list_item( qsv->vpp_space, i ); if(qsv_vpp->type == AV_QSV_VPP_USER){ pv->vpp_space = qsv_vpp; break; } else if(qsv_vpp->type == AV_QSV_VPP_DEFAULT){ prev_vpp = qsv_vpp; } } } if(!pv->vpp_space){ pv->vpp_space = calloc( 1, sizeof( av_qsv_space )); pv->vpp_space->type = AV_QSV_VPP_USER; av_qsv_list_add( qsv->vpp_space, pv->vpp_space ); av_qsv_add_context_usage(qsv,HAVE_THREADS); } else if(pv->vpp_space->is_init_done ) return 1; if(!qsv->dec_space || !qsv->dec_space->is_init_done) return 2; av_qsv_space *qsv_vpp = pv->vpp_space; AV_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam); if (prev_vpp) { memcpy( &qsv_vpp->m_mfxVideoParam.vpp, &prev_vpp->m_mfxVideoParam.vpp, sizeof(prev_vpp->m_mfxVideoParam.vpp)); } else { AV_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam); // FrameRate is important for VPP to start with if( qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN == 0 && qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD == 0 ){ qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN = pv->job->title->rate; qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD = pv->job->title->rate_base; } qsv_vpp->m_mfxVideoParam.vpp.In.FourCC = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC; qsv_vpp->m_mfxVideoParam.vpp.In.ChromaFormat = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat; qsv_vpp->m_mfxVideoParam.vpp.In.CropX = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropX; qsv_vpp->m_mfxVideoParam.vpp.In.CropY = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropY; qsv_vpp->m_mfxVideoParam.vpp.In.CropW = pv->job->title->width; qsv_vpp->m_mfxVideoParam.vpp.In.CropH = pv->job->title->height; qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct; qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtN = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN; qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtD = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD; qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioW = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW; qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioH = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH; qsv_vpp->m_mfxVideoParam.vpp.In.Width = AV_QSV_ALIGN16(pv->job->title->width); qsv_vpp->m_mfxVideoParam.vpp.In.Height = (MFX_PICSTRUCT_PROGRESSIVE == qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct)? AV_QSV_ALIGN16(pv->job->title->height) : AV_QSV_ALIGN32(pv->job->title->height); qsv_vpp->m_mfxVideoParam.vpp.Out.FourCC = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC; qsv_vpp->m_mfxVideoParam.vpp.Out.ChromaFormat = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat; qsv_vpp->m_mfxVideoParam.vpp.Out.CropX = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropX; qsv_vpp->m_mfxVideoParam.vpp.Out.CropY = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropY; qsv_vpp->m_mfxVideoParam.vpp.Out.CropW = pv->job->title->width; qsv_vpp->m_mfxVideoParam.vpp.Out.CropH = pv->job->title->height; qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct; qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN; qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD; qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioW = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW; qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioH = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH; qsv_vpp->m_mfxVideoParam.vpp.Out.Width = AV_QSV_ALIGN16(pv->job->title->width); qsv_vpp->m_mfxVideoParam.vpp.Out.Height = (MFX_PICSTRUCT_PROGRESSIVE == qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct)? AV_QSV_ALIGN16(pv->job->title->height) : AV_QSV_ALIGN32(pv->job->title->height); memset(&qsv_vpp->request, 0, sizeof(mfxFrameAllocRequest)*2); } qsv_vpp->m_mfxVideoParam.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY | MFX_IOPATTERN_OUT_OPAQUE_MEMORY; qsv_vpp->surface_num = FFMIN(prev_vpp ? prev_vpp->surface_num : qsv->dec_space->surface_num/2, AV_QSV_SURFACE_NUM); for(i = 0; i < qsv_vpp->surface_num; i++){ qsv_vpp->p_surfaces[i] = av_mallocz( sizeof(mfxFrameSurface1) ); AV_QSV_CHECK_POINTER(qsv_vpp->p_surfaces[i], MFX_ERR_MEMORY_ALLOC); memcpy(&(qsv_vpp->p_surfaces[i]->Info), &(qsv_vpp->m_mfxVideoParam.vpp.Out), sizeof(mfxFrameInfo)); } qsv_vpp->sync_num = FFMIN(prev_vpp ? prev_vpp->sync_num : qsv->dec_space->sync_num, AV_QSV_SYNC_NUM); for (i = 0; i < qsv_vpp->sync_num; i++){ qsv_vpp->p_syncp[i] = av_mallocz(sizeof(av_qsv_sync)); AV_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i], MFX_ERR_MEMORY_ALLOC); qsv_vpp->p_syncp[i]->p_sync = av_mallocz(sizeof(mfxSyncPoint)); AV_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC); } memset(&qsv_vpp->ext_opaque_alloc, 0, sizeof(mfxExtOpaqueSurfaceAlloc)); qsv_vpp->m_mfxVideoParam.NumExtParam = qsv_vpp->p_ext_param_num = 1; qsv_vpp->p_ext_params = av_mallocz(sizeof(mfxExtBuffer *)*qsv_vpp->p_ext_param_num); AV_QSV_CHECK_POINTER(qsv_vpp->p_ext_params, MFX_ERR_MEMORY_ALLOC); qsv_vpp->m_mfxVideoParam.ExtParam = qsv_vpp->p_ext_params; qsv_vpp->ext_opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION; qsv_vpp->ext_opaque_alloc.Header.BufferSz = sizeof(mfxExtOpaqueSurfaceAlloc); qsv_vpp->p_ext_params[0] = (mfxExtBuffer*)&qsv_vpp->ext_opaque_alloc; if(prev_vpp){ qsv_vpp->ext_opaque_alloc.In.Surfaces = prev_vpp->p_surfaces; qsv_vpp->ext_opaque_alloc.In.NumSurface = prev_vpp->surface_num; } else{ qsv_vpp->ext_opaque_alloc.In.Surfaces = qsv->dec_space->p_surfaces; qsv_vpp->ext_opaque_alloc.In.NumSurface = qsv->dec_space->surface_num; } qsv_vpp->ext_opaque_alloc.In.Type = qsv->dec_space->request[0].Type; qsv_vpp->ext_opaque_alloc.Out.Surfaces = qsv_vpp->p_surfaces; qsv_vpp->ext_opaque_alloc.Out.NumSurface = qsv_vpp->surface_num; qsv_vpp->ext_opaque_alloc.Out.Type = qsv->dec_space->request[0].Type; pv->qsv_user = hb_list_init(); qsv_filter_t *plugin = av_mallocz( sizeof(qsv_filter_t) ); plugin->pv = pv; plugin->plug.pthis = plugin; plugin->plug.PluginInit = qsv_PluginInit; plugin->plug.PluginClose = qsv_PluginClose; plugin->plug.GetPluginParam = qsv_GetPluginParam; plugin->plug.Submit = qsv_Submit; plugin->plug.Execute = qsv_Execute; plugin->plug.FreeResources = qsv_FreeResources; hb_list_add(pv->qsv_user,plugin); sts=MFXVideoUSER_Register(qsv->mfx_session,0,&plugin->plug); AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); plugin_init(plugin,&qsv_vpp->m_mfxVideoParam); qsv_vpp->is_init_done = 1; return 0; } static int hb_qsv_filter_pre_info( hb_filter_object_t * filter, hb_filter_info_t * info ){ hb_filter_private_t * pv = filter->private_data; if( !pv ) return 0; sprintf(info->human_readable_desc, "copy data to system memory"); return 0; } static int hb_qsv_filter_pre_init( hb_filter_object_t * filter, hb_filter_init_t * init ){ filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); hb_filter_private_t * pv = filter->private_data; pv->job = init->job; pv->pre.frame_go = 0; pv->pre.frame_completed = hb_cond_init(); pv->pre.frame_completed_lock = hb_lock_init(); pv->post.frame_go = 0; pv->post.frame_completed = hb_cond_init(); pv->post.frame_completed_lock = hb_lock_init(); pv->pre_busy.frame_go = 0; pv->pre_busy.frame_completed = hb_cond_init(); pv->pre_busy.frame_completed_lock = hb_lock_init(); pv->post_busy.frame_go = 0; pv->post_busy.frame_completed = hb_cond_init(); pv->post_busy.frame_completed_lock = hb_lock_init(); pv->list = hb_list_init(); // just to remind: // PIX_FMT_YUV420P, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) , 3 planes: Y, U, V // PIX_FMT_NV12, ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V) pv->sws_context_from_nv12 = hb_sws_get_context( pv->job->title->width, pv->job->title->height, AV_PIX_FMT_NV12, pv->job->title->width, pv->job->title->height, AV_PIX_FMT_YUV420P, SWS_LANCZOS|SWS_ACCURATE_RND); pv->sws_context_to_nv12 = hb_sws_get_context( pv->job->title->width, pv->job->title->height, AV_PIX_FMT_YUV420P, pv->job->title->width, pv->job->title->height, AV_PIX_FMT_NV12, SWS_LANCZOS|SWS_ACCURATE_RND); return 0; } int pre_process_frame(hb_buffer_t *in, av_qsv_context* qsv, hb_filter_private_t * pv ){ // 1 if have results , 0 otherwise int ret = 1; av_qsv_list* received_item = in->qsv_details.qsv_atom; mfxStatus sts = MFX_ERR_NONE; mfxFrameSurface1 *work_surface = NULL; av_qsv_stage* stage = 0; av_qsv_space *qsv_vpp = pv->vpp_space; if (received_item) { stage = av_qsv_get_last_stage( received_item ); work_surface = stage->out.p_surface; } int sync_idx = av_qsv_get_free_sync(qsv_vpp, qsv); int surface_idx = -1; for (;;) { if (sync_idx == -1) { hb_error("qsv: Not enough resources allocated for the preprocessing filter"); ret = 0; break; } if (sts == MFX_ERR_MORE_SURFACE || sts == MFX_ERR_NONE) surface_idx = av_qsv_get_free_surface(qsv_vpp, qsv, &(qsv_vpp->m_mfxVideoParam.vpp.Out), QSV_PART_ANY); if (surface_idx == -1) { hb_error("qsv: Not enough resources allocated for the preprocessing filter"); ret = 0; break; } sts = MFXVideoUSER_ProcessFrameAsync(qsv->mfx_session, &work_surface, 1, &qsv_vpp->p_surfaces[surface_idx] , 1, qsv_vpp->p_syncp[sync_idx]->p_sync); if (MFX_ERR_MORE_DATA == sts) { if (!qsv_vpp->pending) { qsv_vpp->pending = av_qsv_list_init(0); } // if we have no results, we should not miss resource(s) av_qsv_list_add( qsv_vpp->pending, received_item); ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use); ret = 0; break; } if( MFX_ERR_MORE_SURFACE == sts || MFX_ERR_NONE <= sts){ if( MFX_ERR_MORE_SURFACE == sts ) continue; if (qsv_vpp->p_surfaces[surface_idx] && MFX_WRN_DEVICE_BUSY != sts ) ff_qsv_atomic_inc(&qsv_vpp->p_surfaces[surface_idx]->Data.Locked); } AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); if (MFX_ERR_NONE <= sts ) // repeat the call if warning and no output { if (MFX_WRN_DEVICE_BUSY == sts){ hb_lock(pv->pre_busy.frame_completed_lock); while(!pv->pre_busy.frame_go){ hb_cond_timedwait(pv->pre_busy.frame_completed,pv->pre_busy.frame_completed_lock,1000); if(*pv->job->die) break; } pv->pre_busy.frame_go = 0; hb_unlock(pv->pre_busy.frame_completed_lock); continue; } hb_lock(pv->pre.frame_completed_lock); while(!pv->pre.frame_go){ hb_cond_timedwait(pv->pre.frame_completed,pv->pre.frame_completed_lock,1000); if(*pv->job->die) break; } pv->pre.frame_go = 0; hb_unlock(pv->pre.frame_completed_lock); in = pv->pre.out; if (work_surface){ ff_qsv_atomic_dec(&work_surface->Data.Locked); } // inserting for the future, will be locked until very ready if(stage){ av_qsv_stage* new_stage = av_qsv_stage_init(); new_stage->type = AV_QSV_VPP_USER; new_stage->in.p_surface = work_surface; new_stage->out.p_surface = qsv_vpp->p_surfaces[surface_idx]; new_stage->out.sync = qsv_vpp->p_syncp[sync_idx]; av_qsv_add_stagee( &received_item, new_stage,HAVE_THREADS ); // add pending resources for the proper reclaim later if( qsv_vpp->pending ){ if( av_qsv_list_count(qsv_vpp->pending)>0 ){ new_stage->pending = qsv_vpp->pending; } qsv_vpp->pending = 0; // making free via decrement for all pending int i = 0; for (i = av_qsv_list_count(new_stage->pending); i > 0; i--){ av_qsv_list *atom_list = av_qsv_list_item(new_stage->pending, i-1); av_qsv_stage *stage = av_qsv_get_last_stage( atom_list ); mfxFrameSurface1 *work_surface = stage->out.p_surface; if (work_surface) ff_qsv_atomic_dec(&work_surface->Data.Locked); } } } break; } ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use); if (MFX_ERR_NOT_ENOUGH_BUFFER == sts) HB_DEBUG_ASSERT(1, "The bitstream buffer size is insufficient."); break; } return ret; } static int hb_qsv_filter_pre_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ){ hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in; hb_buffer_t * out = *buf_out; int sts = 0; av_qsv_context* qsv = pv->job->qsv.ctx; if(!in->qsv_details.filter_details) in->qsv_details.filter_details = pv; if ( in->size <= 0 ) { *buf_out = in; *buf_in = NULL; return HB_FILTER_DONE; } while(1){ int ret = filter_pre_init(qsv,pv); if(ret >= 2) av_qsv_sleep(1); else break; } pv->pre.in = in; pv->pre.out = in; sts = pre_process_frame(in, qsv, pv); if(sts){ hb_list_add(pv->list,out); } if( hb_list_count(pv->list) ){ *buf_out = hb_list_item(pv->list,0); hb_list_rem(pv->list,*buf_out); *buf_in = NULL; } else{ *buf_in = NULL; *buf_out = in; } return HB_FILTER_OK; } static void hb_qsv_filter_pre_close( hb_filter_object_t * filter ){ int i = 0; mfxStatus sts = MFX_ERR_NONE; hb_filter_private_t * pv = filter->private_data; if ( !pv ) { return; } sws_freeContext(pv->sws_context_to_nv12); sws_freeContext(pv->sws_context_from_nv12); av_qsv_context* qsv = pv->job->qsv.ctx; if(qsv && qsv->vpp_space && av_qsv_list_count(qsv->vpp_space) > 0 ){ if(pv->qsv_user && qsv->mfx_session){ sts=MFXVideoUSER_Unregister(qsv->mfx_session,0); AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); for(i=hb_list_count(pv->qsv_user);i>0;i--){ qsv_filter_t *plugin = hb_list_item(pv->qsv_user,i-1); hb_list_rem(pv->qsv_user,plugin); plugin_close(plugin); } hb_list_close(&pv->qsv_user); } // closing local stuff qsv_filter_close(qsv,AV_QSV_VPP_USER); // closing the commong stuff av_qsv_context_clean(qsv); } hb_cond_close(&pv->pre.frame_completed); hb_lock_close(&pv->pre.frame_completed_lock); hb_cond_close(&pv->post.frame_completed); hb_lock_close(&pv->post.frame_completed_lock); hb_cond_close(&pv->pre_busy.frame_completed); hb_lock_close(&pv->pre_busy.frame_completed_lock); hb_cond_close(&pv->post_busy.frame_completed); hb_lock_close(&pv->post_busy.frame_completed_lock); hb_list_close( &pv->list ); free( pv ); filter->private_data = NULL; } static int hb_qsv_filter_post_info( hb_filter_object_t * filter, hb_filter_info_t * info ){ hb_filter_private_t * pv = filter->private_data; if( !pv ) return 0; sprintf(info->human_readable_desc, "copy data to opaque memory"); return 0; } static int hb_qsv_filter_post_init( hb_filter_object_t * filter, hb_filter_init_t * init ){ filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); hb_filter_private_t * pv = filter->private_data; pv->job = init->job; return 0; } static int hb_qsv_filter_post_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ){ hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in; hb_buffer_t * out = *buf_out; if ( in->size <= 0 ) { *buf_out = in; *buf_in = NULL; return HB_FILTER_DONE; } av_qsv_context* qsv = pv->job->qsv.ctx; pv = in->qsv_details.filter_details; if (!pv) { *buf_out = NULL; *buf_in = NULL; return HB_FILTER_OK; } while(1){ int ret = filter_pre_init(qsv,pv); if(ret >= 2) av_qsv_sleep(1); else break; } pv->post.in = in; pv->post.out = out; // signal: input is prepared, can start inserting data back into pipeline hb_lock(pv->post.frame_completed_lock); pv->post.frame_go = 1; hb_cond_broadcast(pv->post.frame_completed); hb_unlock(pv->post.frame_completed_lock); // wait: on signal that data is ready hb_lock(pv->post_busy.frame_completed_lock); while(!pv->post_busy.frame_go){ hb_cond_timedwait(pv->post_busy.frame_completed,pv->post_busy.frame_completed_lock,1000); if(*pv->job->die) break; } pv->post_busy.frame_go = 0; hb_unlock(pv->post_busy.frame_completed_lock); if (pv->post.status == HB_FILTER_OK || pv->post.status == HB_FILTER_DONE) { *buf_out = in; } else { *buf_out = NULL; pv->post.status = HB_FILTER_OK; } *buf_in = NULL; return HB_FILTER_OK; } static void hb_qsv_filter_post_close( hb_filter_object_t * filter ){ hb_filter_private_t * pv = filter->private_data; if ( !pv ) { return; } free( pv ); filter->private_data = NULL; } mfxStatus MFX_CDECL qsv_PluginInit(mfxHDL pthis, mfxCoreInterface *core){ mfxStatus sts = MFX_ERR_NONE; if(core && pthis){ qsv_filter_t *plugin = pthis; plugin->core = core; plugin->pluginparam.MaxThreadNum = 1; plugin->pluginparam.ThreadPolicy = MFX_THREADPOLICY_SERIAL; } else sts = MFX_ERR_NULL_PTR; return sts; } mfxStatus MFX_CDECL qsv_PluginClose (mfxHDL pthis){ mfxStatus sts = MFX_ERR_NONE; return sts; } mfxStatus MFX_CDECL qsv_GetPluginParam(mfxHDL pthis, mfxPluginParam *par){ mfxStatus sts = MFX_ERR_NONE; if(pthis){ qsv_filter_t *plugin = pthis; *par = plugin->pluginparam; } else sts = MFX_ERR_NULL_PTR; return sts; } mfxStatus MFX_CDECL qsv_Submit(mfxHDL pthis, const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxThreadTask *task){ mfxStatus sts = MFX_ERR_NONE; qsv_filter_t *plugin = pthis; mfxFrameSurface1 *surface_in = (mfxFrameSurface1 *)in[0]; mfxFrameSurface1 *surface_out = (mfxFrameSurface1 *)out[0]; mfxFrameSurface1 *real_surface_in = surface_in; mfxFrameSurface1 *real_surface_out = surface_out; sts = plugin->core->GetRealSurface(plugin->core->pthis, surface_in, &real_surface_in); AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, MFX_ERR_MEMORY_ALLOC); sts = plugin->core->GetRealSurface(plugin->core->pthis, surface_out, &real_surface_out); AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, MFX_ERR_MEMORY_ALLOC); int task_idx = get_free_task(plugin->tasks); if (task_idx == -1) { return MFX_WRN_DEVICE_BUSY; } plugin->core->IncreaseReference(plugin->core->pthis, &(real_surface_in->Data)); plugin->core->IncreaseReference(plugin->core->pthis, &(real_surface_out->Data)); // to preserve timing if other filters are used in-between surface_out->Data.TimeStamp = surface_in->Data.TimeStamp; surface_out->Data.FrameOrder = surface_in->Data.FrameOrder; qsv_filter_task_t *current_task = hb_list_item(plugin->tasks,task_idx); current_task->in = real_surface_in; current_task->out = real_surface_out; current_task->busy = 1; current_task->pv = plugin->pv; *task = (mfxThreadTask)current_task; return sts; } mfxStatus MFX_CDECL qsv_Execute(mfxHDL pthis, mfxThreadTask task, mfxU32 uid_p, mfxU32 uid_a){ mfxStatus sts = MFX_ERR_NONE; qsv_filter_task_t *current_task = (qsv_filter_task_t *)task; qsv_filter_t *plugin = pthis; sts = (current_task->processor.process)(current_task,0); AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); sts = MFX_TASK_DONE; return sts; } mfxStatus MFX_CDECL qsv_FreeResources(mfxHDL pthis, mfxThreadTask task, mfxStatus sts){ qsv_filter_t *plugin = pthis; qsv_filter_task_t *current_task = (qsv_filter_task_t *)task; plugin->core->DecreaseReference(plugin->core->pthis, &(current_task->in->Data)); plugin->core->DecreaseReference(plugin->core->pthis, &(current_task->out->Data)); current_task->busy = 0; hb_lock(plugin->pv->pre_busy.frame_completed_lock); plugin->pv->pre_busy.frame_go = 1; hb_cond_broadcast(plugin->pv->pre_busy.frame_completed); hb_unlock(plugin->pv->pre_busy.frame_completed_lock); return MFX_ERR_NONE; } mfxStatus plugin_init(qsv_filter_t* plugin, mfxVideoParam *param){ mfxStatus sts = MFX_ERR_NONE; if(plugin->is_init_done) return sts; plugin->videoparam = param; mfxExtOpaqueSurfaceAlloc* plugin_opaque_alloc = NULL; plugin_opaque_alloc = (mfxExtOpaqueSurfaceAlloc*) get_ext_buffer(plugin->videoparam->ExtParam, plugin->videoparam->NumExtParam, MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION); if(!plugin_opaque_alloc || !plugin_opaque_alloc->In.Surfaces || !plugin_opaque_alloc->Out.Surfaces) return MFX_ERR_INVALID_VIDEO_PARAM; sts = plugin->core->MapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->In.NumSurface, plugin_opaque_alloc->In.Type, plugin_opaque_alloc->In.Surfaces); AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); sts = plugin->core->MapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->Out.NumSurface, plugin_opaque_alloc->Out.Type, plugin_opaque_alloc->Out.Surfaces); AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); plugin->tasks = hb_list_init(); qsv_filter_task_t *task = calloc( 1, sizeof( qsv_filter_task_t )); task->processor.process = process_filter; task->processor.alloc = &plugin->core->FrameAllocator; task->processor.core = plugin->core; hb_list_add(plugin->tasks,task); plugin->is_init_done = 1; return sts; } mfxStatus plugin_close(qsv_filter_t* plugin){ int i = 0; mfxStatus sts = MFX_ERR_NONE; if(!plugin->is_init_done) return sts; mfxExtOpaqueSurfaceAlloc* plugin_opaque_alloc = NULL; plugin_opaque_alloc = (mfxExtOpaqueSurfaceAlloc*) get_ext_buffer(plugin->videoparam->ExtParam, plugin->videoparam->NumExtParam, MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION); if(!plugin_opaque_alloc || !plugin_opaque_alloc->In.Surfaces || !plugin_opaque_alloc->Out.Surfaces) return MFX_ERR_INVALID_VIDEO_PARAM; sts = plugin->core->UnmapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->In.NumSurface, plugin_opaque_alloc->In.Type, plugin_opaque_alloc->In.Surfaces); AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); sts = plugin->core->UnmapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->Out.NumSurface, plugin_opaque_alloc->Out.Type, plugin_opaque_alloc->Out.Surfaces); AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); if(plugin->tasks){ for(i=hb_list_count(plugin->tasks);i>0;i--){ qsv_filter_task_t *task = hb_list_item(plugin->tasks,i-1); hb_list_rem(plugin->tasks,task); free(task); } hb_list_close(&plugin->tasks); } plugin->is_init_done = 0; return sts; } mfxExtBuffer* get_ext_buffer(mfxExtBuffer** buffers, mfxU32 buffers_num, mfxU32 buffer_id){ int i = 0; if(!buffers) return 0; for(i=0;iBufferId == buffer_id) return buffers[i]; } return 0; } int get_free_task(hb_list_t* tasks){ int ret = -1; int i = 0; for(i=0;ibusy){ ret = i; break; } } return ret; } mfxStatus lock_frame(mfxFrameAllocator *alloc,mfxFrameSurface1 *surface){ mfxStatus sts = MFX_ERR_NONE; // prevent double lock if (surface->Data.Y != 0 && surface->Data.MemId !=0){ return MFX_ERR_UNSUPPORTED; } // not allocated, therefore no lock if (surface->Data.Y != 0){ return MFX_ERR_NONE; } sts = alloc->Lock(alloc->pthis,surface->Data.MemId,&surface->Data); return sts; } mfxStatus unlock_frame(mfxFrameAllocator *alloc,mfxFrameSurface1 *surface){ mfxStatus sts = MFX_ERR_NONE; // not allocated if (surface->Data.Y != 0 && surface->Data.MemId == 0){ return MFX_ERR_NONE; } // not locked if (surface->Data.Y == 0){ return MFX_ERR_NONE; } sts = alloc->Unlock(alloc->pthis,surface->Data.MemId,&surface->Data); return sts; } int process_filter(qsv_filter_task_t* task, void* params){ mfxStatus sts = MFX_ERR_NONE; if (MFX_ERR_NONE != (sts = lock_frame(task->processor.alloc,task->in)))return sts; if (MFX_ERR_NONE != (sts = lock_frame(task->processor.alloc,task->out))) { unlock_frame(task->processor.alloc,task->in); return sts; } qsv_nv12_to_yuv420(task->pv->sws_context_from_nv12,task->pv->pre.out, task->in, task->processor.core); // signal: input is prepared, converted from pipeline into internal buffer hb_lock(task->pv->pre.frame_completed_lock); task->pv->pre.frame_go = 1; hb_cond_broadcast(task->pv->pre.frame_completed); hb_unlock(task->pv->pre.frame_completed_lock); // wait: input is prepared, converted from pipeline into internal buffer hb_lock(task->pv->post.frame_completed_lock); while(!task->pv->post.frame_go){ hb_cond_timedwait(task->pv->post.frame_completed,task->pv->post.frame_completed_lock,1000); if(*task->pv->job->die) break; } task->pv->post.frame_go = 0; hb_unlock(task->pv->post.frame_completed_lock); // this is just a simple fun/test case #if 0 { int i = 0; char *cur_line; char* luma = task->pv->post.in->plane[0].data; int pitch = task->pv->post.in->plane[0].stride; int h = task->pv->post.in->plane[0].height; int w = task->pv->post.in->plane[0].width; for (i = 0; i < h; i++){ cur_line = luma + i * pitch; if(i>h/4 && i < 3*h/4 && i % 5 == 0 ) memset(cur_line, 0 , w ); } } #endif if(task->pv->post.in) { qsv_yuv420_to_nv12(task->pv->sws_context_to_nv12, task->out, task->pv->post.in); } // signal: output is prepared, converted from internal buffer into pipeline hb_lock(task->pv->post_busy.frame_completed_lock); task->pv->post_busy.frame_go = 1; hb_cond_broadcast(task->pv->post_busy.frame_completed); hb_unlock(task->pv->post_busy.frame_completed_lock); unlock_frame(task->processor.alloc,task->in); unlock_frame(task->processor.alloc,task->out); return sts; } #endif // USE_QSV HandBrake-0.10.2/libhb/platform/0000775000175200017520000000000012535641635016755 5ustar handbrakehandbrakeHandBrake-0.10.2/libhb/platform/macosx/0000775000175200017520000000000012535641635020247 5ustar handbrakehandbrakeHandBrake-0.10.2/libhb/platform/macosx/encca_aac.c0000664000175200017520000003770512463330511022270 0ustar handbrakehandbrake/* encca_aac.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "audio_remap.h" #include #include enum AAC_MODE { AAC_MODE_LC, AAC_MODE_HE }; int encCoreAudioInitLC(hb_work_object_t*, hb_job_t*); int encCoreAudioInitHE(hb_work_object_t*, hb_job_t*); int encCoreAudioInit(hb_work_object_t*, hb_job_t*, enum AAC_MODE mode); int encCoreAudioWork(hb_work_object_t*, hb_buffer_t**, hb_buffer_t**); void encCoreAudioClose(hb_work_object_t*); hb_work_object_t hb_encca_aac = { WORK_ENC_CA_AAC, "AAC encoder (Apple)", encCoreAudioInitLC, encCoreAudioWork, encCoreAudioClose }; hb_work_object_t hb_encca_haac = { WORK_ENC_CA_HAAC, "HE-AAC encoder (Apple)", encCoreAudioInitHE, encCoreAudioWork, encCoreAudioClose }; struct hb_work_private_s { uint8_t *buf; hb_job_t *job; hb_list_t *list; AudioConverterRef converter; unsigned long isamples, isamplesiz, omaxpacket, nchannels; uint64_t samples, ibytes; Float64 osamplerate; hb_audio_remap_t *remap; }; #define MP4ESDescrTag 0x03 #define MP4DecConfigDescrTag 0x04 #define MP4DecSpecificDescrTag 0x05 // based off of mov_mp4_read_descr_len from mov.c in ffmpeg's libavformat static int readDescrLen(UInt8 **buffer) { int len = 0; int count = 4; while (count--) { int c = *(*buffer)++; len = (len << 7) | (c & 0x7f); if (!(c & 0x80)) break; } return len; } // based off of mov_mp4_read_descr from mov.c in ffmpeg's libavformat static int readDescr(UInt8 **buffer, int *tag) { *tag = *(*buffer)++; return readDescrLen(buffer); } // based off of mov_read_esds from mov.c in ffmpeg's libavformat static long ReadESDSDescExt(void* descExt, UInt8 **buffer, UInt32 *size, int versionFlags) { UInt8 *esds = (UInt8*)descExt; int tag, len; *size = 0; if (versionFlags) esds += 4; // version + flags readDescr(&esds, &tag); esds += 2; // ID if (tag == MP4ESDescrTag) esds++; // priority readDescr(&esds, &tag); if (tag == MP4DecConfigDescrTag) { esds++; // object type id esds++; // stream type esds += 3; // buffer size db esds += 4; // max bitrate esds += 4; // average bitrate len = readDescr(&esds, &tag); if (tag == MP4DecSpecificDescrTag) { *buffer = calloc(1, len + 8); if (*buffer) { memcpy(*buffer, esds, len); *size = len; } } } return noErr; } /*********************************************************************** * hb_work_encCoreAudio_init switches *********************************************************************** * **********************************************************************/ int encCoreAudioInitLC(hb_work_object_t *w, hb_job_t *job) { return encCoreAudioInit(w, job, AAC_MODE_LC); } int encCoreAudioInitHE(hb_work_object_t *w, hb_job_t *job) { return encCoreAudioInit(w, job, AAC_MODE_HE); } /*********************************************************************** * hb_work_encCoreAudio_init *********************************************************************** * **********************************************************************/ int encCoreAudioInit(hb_work_object_t *w, hb_job_t *job, enum AAC_MODE mode) { hb_work_private_t *pv = calloc(1, sizeof(hb_work_private_t)); hb_audio_t *audio = w->audio; AudioStreamBasicDescription input, output; UInt32 tmp, tmpsiz = sizeof(tmp); OSStatus err; w->private_data = pv; pv->job = job; // pass the number of channels used into the private work data pv->nchannels = hb_mixdown_get_discrete_channel_count(audio->config.out.mixdown); bzero(&input, sizeof(AudioStreamBasicDescription)); input.mSampleRate = (Float64)audio->config.out.samplerate; input.mFormatID = kAudioFormatLinearPCM; input.mFormatFlags = (kLinearPCMFormatFlagIsFloat|kAudioFormatFlagsNativeEndian); input.mBytesPerPacket = 4 * pv->nchannels; input.mFramesPerPacket = 1; input.mBytesPerFrame = input.mBytesPerPacket * input.mFramesPerPacket; input.mChannelsPerFrame = pv->nchannels; input.mBitsPerChannel = 32; bzero(&output, sizeof(AudioStreamBasicDescription)); switch (mode) { case AAC_MODE_HE: output.mFormatID = kAudioFormatMPEG4AAC_HE; break; case AAC_MODE_LC: default: output.mFormatID = kAudioFormatMPEG4AAC; break; } output.mSampleRate = (Float64)audio->config.out.samplerate; output.mChannelsPerFrame = pv->nchannels; // let CoreAudio decide the rest // initialise encoder err = AudioConverterNew(&input, &output, &pv->converter); if (err != noErr) { // Retry without the samplerate bzero(&output, sizeof(AudioStreamBasicDescription)); switch (mode) { case AAC_MODE_HE: output.mFormatID = kAudioFormatMPEG4AAC_HE; break; case AAC_MODE_LC: default: output.mFormatID = kAudioFormatMPEG4AAC; break; } output.mChannelsPerFrame = pv->nchannels; err = AudioConverterNew(&input, &output, &pv->converter); if (err != noErr) { hb_log("Error creating an AudioConverter err=%"PRId64" output.mBytesPerFrame=%"PRIu64"", (int64_t)err, (uint64_t)output.mBytesPerFrame); *job->done_error = HB_ERROR_UNKNOWN; *job->die = 1; return -1; } } // set encoder quality to maximum tmp = kAudioConverterQuality_Max; AudioConverterSetProperty(pv->converter, kAudioConverterCodecQuality, sizeof(tmp), &tmp); if (audio->config.out.bitrate > 0) { // set encoder bitrate control mode to constrained variable tmp = kAudioCodecBitRateControlMode_VariableConstrained; AudioConverterSetProperty(pv->converter, kAudioCodecPropertyBitRateControlMode, sizeof(tmp), &tmp); // get available bitrates AudioValueRange *bitrates; ssize_t bitrateCounts; err = AudioConverterGetPropertyInfo(pv->converter, kAudioConverterApplicableEncodeBitRates, &tmpsiz, NULL); bitrates = malloc(tmpsiz); err = AudioConverterGetProperty(pv->converter, kAudioConverterApplicableEncodeBitRates, &tmpsiz, bitrates); bitrateCounts = tmpsiz / sizeof(AudioValueRange); // set bitrate tmp = audio->config.out.bitrate * 1000; if (tmp < bitrates[0].mMinimum) tmp = bitrates[0].mMinimum; if (tmp > bitrates[bitrateCounts-1].mMinimum) tmp = bitrates[bitrateCounts-1].mMinimum; free(bitrates); if (tmp != audio->config.out.bitrate * 1000) { hb_log("encCoreAudioInit: sanitizing track %d audio bitrate %d to %"PRIu32"", audio->config.out.track, audio->config.out.bitrate, tmp / 1000); } AudioConverterSetProperty(pv->converter, kAudioConverterEncodeBitRate, sizeof(tmp), &tmp); } else if (audio->config.out.quality >= 0) { if (mode != AAC_MODE_LC) { hb_error("encCoreAudioInit: internal error, VBR set but not applicable"); return 1; } // set encoder bitrate control mode to variable tmp = kAudioCodecBitRateControlMode_Variable; AudioConverterSetProperty(pv->converter, kAudioCodecPropertyBitRateControlMode, sizeof(tmp), &tmp); // set quality tmp = audio->config.out.quality; AudioConverterSetProperty(pv->converter, kAudioCodecPropertySoundQualityForVBR, sizeof(tmp), &tmp); } else { hb_error("encCoreAudioInit: internal error, bitrate/quality not set"); return 1; } // get real input tmpsiz = sizeof(input); AudioConverterGetProperty(pv->converter, kAudioConverterCurrentInputStreamDescription, &tmpsiz, &input); // get real output tmpsiz = sizeof(output); AudioConverterGetProperty(pv->converter, kAudioConverterCurrentOutputStreamDescription, &tmpsiz, &output); // set sizes pv->isamplesiz = input.mBytesPerPacket; pv->isamples = output.mFramesPerPacket; pv->osamplerate = output.mSampleRate; audio->config.out.samples_per_frame = pv->isamples; // channel remapping pv->remap = hb_audio_remap_init(AV_SAMPLE_FMT_FLT, &hb_aac_chan_map, audio->config.in.channel_map); if (pv->remap == NULL) { hb_error("encCoreAudioInit: hb_audio_remap_init() failed"); } uint64_t layout = hb_ff_mixdown_xlat(audio->config.out.mixdown, NULL); hb_audio_remap_set_channel_layout(pv->remap, layout); // get maximum output size AudioConverterGetProperty(pv->converter, kAudioConverterPropertyMaximumOutputPacketSize, &tmpsiz, &tmp); pv->omaxpacket = tmp; // get magic cookie (elementary stream descriptor) tmp = HB_CONFIG_MAX_SIZE; AudioConverterGetProperty(pv->converter, kAudioConverterCompressionMagicCookie, &tmp, w->config->extradata.bytes); // CoreAudio returns a complete ESDS, but we only need // the DecoderSpecific info. UInt8* buffer = NULL; ReadESDSDescExt(w->config->extradata.bytes, &buffer, &tmpsiz, 0); w->config->extradata.length = tmpsiz; memmove(w->config->extradata.bytes, buffer, w->config->extradata.length); free(buffer); pv->list = hb_list_init(); pv->buf = NULL; return 0; } /*********************************************************************** * Close *********************************************************************** * **********************************************************************/ void encCoreAudioClose(hb_work_object_t *w) { hb_work_private_t *pv = w->private_data; if (pv != NULL) { if (pv->converter) { AudioConverterDispose(pv->converter); } if (pv->buf != NULL) { free(pv->buf); } if (pv->remap != NULL) { hb_audio_remap_free(pv->remap); } hb_list_empty(&pv->list); free(pv); w->private_data = NULL; } } /* Called whenever necessary by AudioConverterFillComplexBuffer */ static OSStatus inInputDataProc(AudioConverterRef converter, UInt32 *npackets, AudioBufferList *buffers, AudioStreamPacketDescription **ignored, void *userdata) { hb_work_private_t *pv = userdata; if (!pv->ibytes) { *npackets = 0; return 1; } if (pv->buf != NULL) { free(pv->buf); } buffers->mBuffers[0].mDataByteSize = MIN(pv->ibytes, pv->isamplesiz * *npackets); pv->buf = calloc(1, buffers->mBuffers[0].mDataByteSize); buffers->mBuffers[0].mData = pv->buf; if (hb_list_bytes(pv->list) >= buffers->mBuffers[0].mDataByteSize) { hb_list_getbytes(pv->list, buffers->mBuffers[0].mData, buffers->mBuffers[0].mDataByteSize, NULL, NULL); } else { *npackets = 0; return 1; } *npackets = buffers->mBuffers[0].mDataByteSize / pv->isamplesiz; pv->ibytes -= buffers->mBuffers[0].mDataByteSize; hb_audio_remap(pv->remap, (uint8_t**)(&buffers->mBuffers[0].mData), (int)(*npackets)); return noErr; } /*********************************************************************** * Encode *********************************************************************** * **********************************************************************/ static hb_buffer_t* Encode(hb_work_object_t *w) { hb_work_private_t *pv = w->private_data; UInt32 npackets = 1; /* check if we need more data */ if ((pv->ibytes = hb_list_bytes(pv->list)) < pv->isamples * pv->isamplesiz) { return NULL; } hb_buffer_t *obuf; AudioStreamPacketDescription odesc = { 0 }; AudioBufferList obuflist = { .mNumberBuffers = 1, .mBuffers = { { .mNumberChannels = pv->nchannels } }, }; obuf = hb_buffer_init(pv->omaxpacket); obuflist.mBuffers[0].mDataByteSize = obuf->size; obuflist.mBuffers[0].mData = obuf->data; OSStatus err = AudioConverterFillComplexBuffer(pv->converter, inInputDataProc, pv, &npackets, &obuflist, &odesc); if (err != noErr && err != 1) { hb_log("encCoreAudio: unexpected error in AudioConverterFillComplexBuffer()"); } // only drop the output buffer if it's actually empty if (!npackets || odesc.mDataByteSize <= 0) { hb_log("encCoreAudio: 0 packets returned"); return NULL; } obuf->size = odesc.mDataByteSize; obuf->s.start = 90000LL * pv->samples / pv->osamplerate; pv->samples += pv->isamples; obuf->s.stop = 90000LL * pv->samples / pv->osamplerate; obuf->s.duration = (double)90000 * pv->isamples / pv->osamplerate; obuf->s.type = AUDIO_BUF; obuf->s.frametype = HB_FRAME_AUDIO; return obuf; } static hb_buffer_t* Flush(hb_work_object_t *w, hb_buffer_t *bufin) { hb_work_private_t *pv = w->private_data; // pad whatever data we have out to four input frames. int nbytes = hb_list_bytes(pv->list); int pad = pv->isamples * pv->isamplesiz - nbytes; if (pad > 0) { hb_buffer_t *tmp = hb_buffer_init(pad); memset(tmp->data, 0, pad); hb_list_add(pv->list, tmp); } hb_buffer_t *bufout = NULL, *buf = NULL; while (hb_list_bytes(pv->list) >= pv->isamples * pv->isamplesiz) { hb_buffer_t *b = Encode(w); if (b != NULL) { if (bufout == NULL) { bufout = b; } else { buf->next = b; } buf = b; } } // add the eof marker to the end of our buf chain if (buf != NULL) { buf->next = bufin; } else { bufout = bufin; } return bufout; } /*********************************************************************** * Work *********************************************************************** * **********************************************************************/ int encCoreAudioWork(hb_work_object_t *w, hb_buffer_t **buf_in, hb_buffer_t **buf_out) { hb_work_private_t *pv = w->private_data; hb_buffer_t *buf; if ((*buf_in)->size <= 0) { // EOF on input. Finish encoding what we have buffered then send // it & the eof downstream. *buf_out = Flush(w, *buf_in); *buf_in = NULL; return HB_WORK_DONE; } hb_list_add(pv->list, *buf_in); *buf_in = NULL; *buf_out = buf = Encode(w); while (buf != NULL) { buf->next = Encode(w); buf = buf->next; } return HB_WORK_OK; } HandBrake-0.10.2/libhb/dectx3gsub.c0000664000175200017520000002017212463330511017336 0ustar handbrakehandbrake/* dectx3gsub.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ /* * Converts TX3G subtitles to UTF-8 subtitles with limited HTML-style markup (, , ). * * TX3G == MPEG 4, Part 17 (ISO/IEC 14496-17) == 3GPP Timed Text (26.245) * A full reference to the format can be found here: * http://www.3gpp.org/ftp/Specs/html-info/26245.htm * * @author David Foster (davidfstr) */ #include #include #include "hb.h" #include "colormap.h" struct hb_work_private_s { int line; // SSA line number }; typedef enum { BOLD = 0x1, ITALIC = 0x2, UNDERLINE = 0x4 } FaceStyleFlag; #define MAX_MARKUP_LEN 40 #define SSA_PREAMBLE_LEN 24 typedef struct { uint16_t startChar; // NOTE: indices in terms of *character* (not: byte) positions uint16_t endChar; uint16_t fontID; uint8_t faceStyleFlags; // FaceStyleFlag uint8_t fontSize; uint32_t textColorRGBA; } StyleRecord; // NOTE: None of these macros check for buffer overflow #define READ_U8() *pos; pos += 1; #define READ_U16() (pos[0] << 8) | pos[1]; pos += 2; #define READ_U32() (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | pos[3]; pos += 4; #define READ_ARRAY(n) pos; pos += n; #define SKIP_ARRAY(n) pos += n; #define WRITE_CHAR(c) {dst[0]=c; dst += 1;} #define FOURCC(str) ((((uint32_t) str[0]) << 24) | \ (((uint32_t) str[1]) << 16) | \ (((uint32_t) str[2]) << 8) | \ (((uint32_t) str[3]) << 0)) #define IS_10xxxxxx(c) ((c & 0xC0) == 0x80) static int write_ssa_markup(char *dst, StyleRecord *style) { if (style == NULL) { sprintf(dst, "{\\r}"); return strlen(dst); } sprintf(dst, "{\\i%d\\b%d\\u%d\\1c&H%X&\\1a&H%02X&}", !!(style->faceStyleFlags & ITALIC), !!(style->faceStyleFlags & BOLD), !!(style->faceStyleFlags & UNDERLINE), HB_RGB_TO_BGR(style->textColorRGBA >> 8), 255 - (style->textColorRGBA & 0xFF)); // SSA alpha is inverted 0==opaque return strlen(dst); } static hb_buffer_t *tx3g_decode_to_ssa(hb_work_private_t *pv, hb_buffer_t *in) { uint8_t *pos = in->data; uint8_t *end = in->data + in->size; uint16_t numStyleRecords = 0; StyleRecord *styleRecords = NULL; /* * Parse the packet as a TX3G TextSample. * * Look for a single StyleBox ('styl') and read all contained StyleRecords. * Ignore all other box types. * * NOTE: Buffer overflows on read are not checked. */ uint16_t textLength = READ_U16(); uint8_t *text = READ_ARRAY(textLength); while ( pos < end ) { /* * Read TextSampleModifierBox */ uint32_t size = READ_U32(); if ( size == 0 ) { size = pos - end; // extends to end of packet } if ( size == 1 ) { hb_log( "dectx3gsub: TextSampleModifierBox has unsupported large size" ); break; } uint32_t type = READ_U32(); if (type == FOURCC("uuid")) { hb_log( "dectx3gsub: TextSampleModifierBox has unsupported extended type" ); break; } if (type == FOURCC("styl")) { // Found a StyleBox. Parse the contained StyleRecords if ( numStyleRecords != 0 ) { hb_log( "dectx3gsub: found additional StyleBoxes on subtitle; skipping" ); SKIP_ARRAY(size); continue; } numStyleRecords = READ_U16(); if (numStyleRecords > 0) styleRecords = calloc(numStyleRecords, sizeof(StyleRecord)); int i; for (i = 0; i < numStyleRecords; i++) { styleRecords[i].startChar = READ_U16(); styleRecords[i].endChar = READ_U16(); styleRecords[i].fontID = READ_U16(); styleRecords[i].faceStyleFlags = READ_U8(); styleRecords[i].fontSize = READ_U8(); styleRecords[i].textColorRGBA = READ_U32(); } } else { // Found some other kind of TextSampleModifierBox. Skip it. SKIP_ARRAY(size); } } /* * Copy text to output buffer, and add HTML markup for the style records */ int maxOutputSize = textLength + SSA_PREAMBLE_LEN + (numStyleRecords * MAX_MARKUP_LEN); hb_buffer_t *out = hb_buffer_init( maxOutputSize ); if ( out == NULL ) goto fail; uint8_t *dst = out->data; uint8_t *start; int charIndex = 0; int styleIndex = 0; sprintf((char*)dst, "%d,,Default,,0,0,0,,", pv->line); dst += strlen((char*)dst); start = dst; for (pos = text, end = text + textLength; pos < end; pos++) { if (IS_10xxxxxx(*pos)) { // Is a non-first byte of a multi-byte UTF-8 character WRITE_CHAR(*pos); continue; // ...without incrementing 'charIndex' } if (styleIndex < numStyleRecords) { if (styleRecords[styleIndex].endChar == charIndex) { if (styleIndex + 1 >= numStyleRecords || styleRecords[styleIndex+1].startChar > charIndex) { dst += write_ssa_markup((char*)dst, NULL); } styleIndex++; } if (styleRecords[styleIndex].startChar == charIndex) { dst += write_ssa_markup((char*)dst, &styleRecords[styleIndex]); } } if (*pos == '\n') { WRITE_CHAR('\\'); WRITE_CHAR('N'); } else { WRITE_CHAR(*pos); } charIndex++; } if (start == dst) { // No text in the subtitle. This sub is just filler, drop it. free(styleRecords); hb_buffer_close(&out); return NULL; } *dst = '\0'; dst++; // Trim output buffer to the actual amount of data written out->size = dst - out->data; // Copy metadata from the input packet to the output packet out->s.frametype = HB_FRAME_SUBTITLE; out->s.start = in->s.start; out->s.stop = in->s.stop; fail: free(styleRecords); return out; } #undef READ_U8 #undef READ_U16 #undef READ_U32 #undef READ_ARRAY #undef SKIP_ARRAY #undef WRITE_CHAR #undef WRITE_START_TAG #undef WRITE_END_TAG static int dectx3gInit( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t * pv; pv = calloc( 1, sizeof( hb_work_private_t ) ); if (pv == NULL) return 1; w->private_data = pv; // TODO: // parse w->subtitle->extradata txg3 sample description into // SSA format and replace extradata. // For now we just create a generic SSA Script Info. int height = job->title->height - job->crop[0] - job->crop[1]; int width = job->title->width - job->crop[2] - job->crop[3]; hb_subtitle_add_ssa_header(w->subtitle, width, height); return 0; } static int dectx3gWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_buffer_t * in = *buf_in; if ( in->s.stop == 0 ) { hb_log( "dectx3gsub: subtitle packet lacks duration" ); } if (in->size == 0) { *buf_out = in; *buf_in = NULL; return HB_WORK_DONE; } *buf_out = tx3g_decode_to_ssa(pv, in); return HB_WORK_OK; } static void dectx3gClose( hb_work_object_t * w ) { free(w->private_data); } hb_work_object_t hb_dectx3gsub = { WORK_DECTX3GSUB, "TX3G Subtitle Decoder", dectx3gInit, dectx3gWork, dectx3gClose }; HandBrake-0.10.2/libhb/deinterlace.c0000664000175200017520000004667012265031673017564 0ustar handbrakehandbrake/* Copyright (C) 2006 Michael Niedermayer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "hb.h" #include "hbffmpeg.h" #include "taskset.h" // yadif_mode is a bit vector with the following flags #define MODE_YADIF_ENABLE 1 #define MODE_YADIF_SPATIAL 2 #define MODE_YADIF_2PASS 4 #define MODE_YADIF_BOB 8 #define YADIF_MODE_DEFAULT 0 #define YADIF_PARITY_DEFAULT -1 #define ABS(a) ((a) > 0 ? (a) : (-(a))) #define MIN3(a,b,c) MIN(MIN(a,b),c) #define MAX3(a,b,c) MAX(MAX(a,b),c) typedef struct yadif_arguments_s { hb_buffer_t * dst; int parity; int tff; } yadif_arguments_t; typedef struct deint_arguments_s { hb_buffer_t * src; hb_buffer_t * dst; } deint_arguments_t; typedef struct deint_thread_arg_s { hb_filter_private_t *pv; int segment; } deint_thread_arg_t; struct hb_filter_private_s { int width; int height; int yadif_mode; int yadif_parity; int yadif_ready; hb_buffer_t * yadif_ref[3]; int cpu_count; int segments; int deint_nsegs; taskset_t deint_taskset; // Threads for fast deint taskset_t yadif_taskset; // Threads for Yadif deint_arguments_t *deint_arguments; // Arguments to thread for work yadif_arguments_t *yadif_arguments; // Arguments to thread for work }; static int hb_deinterlace_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int hb_deinterlace_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static void hb_deinterlace_close( hb_filter_object_t * filter ); hb_filter_object_t hb_filter_deinterlace = { .id = HB_FILTER_DEINTERLACE, .enforce_order = 1, .name = "Deinterlace", .settings = NULL, .init = hb_deinterlace_init, .work = hb_deinterlace_work, .close = hb_deinterlace_close, }; static void yadif_store_ref(hb_filter_private_t *pv, hb_buffer_t *b) { hb_buffer_close(&pv->yadif_ref[0]); memmove(&pv->yadif_ref[0], &pv->yadif_ref[1], sizeof(hb_buffer_t *) * 2 ); pv->yadif_ref[2] = b; } static void yadif_filter_line( hb_filter_private_t * pv, uint8_t * dst, uint8_t * prev, uint8_t * cur, uint8_t * next, int width, int stride, int parity) { uint8_t *prev2 = parity ? prev : cur ; uint8_t *next2 = parity ? cur : next; int x; for( x = 0; x < width; x++) { int c = cur[-stride]; int d = (prev2[0] + next2[0])>>1; int e = cur[+stride]; int temporal_diff0 = ABS(prev2[0] - next2[0]); int temporal_diff1 = ( ABS(prev[-stride] - c) + ABS(prev[+stride] - e) ) >> 1; int temporal_diff2 = ( ABS(next[-stride] - c) + ABS(next[+stride] - e) ) >> 1; int diff = MAX3(temporal_diff0>>1, temporal_diff1, temporal_diff2); int spatial_pred = (c+e)>>1; int spatial_score = ABS(cur[-stride-1] - cur[+stride-1]) + ABS(c-e) + ABS(cur[-stride+1] - cur[+stride+1]) - 1; #define YADIF_CHECK(j)\ { int score = ABS(cur[-stride-1+j] - cur[+stride-1-j])\ + ABS(cur[-stride +j] - cur[+stride -j])\ + ABS(cur[-stride+1+j] - cur[+stride+1-j]);\ if( score < spatial_score ){\ spatial_score = score;\ spatial_pred = (cur[-stride +j] + cur[+stride -j])>>1;\ YADIF_CHECK(-1) YADIF_CHECK(-2) }} }} YADIF_CHECK( 1) YADIF_CHECK( 2) }} }} if( pv->yadif_mode & MODE_YADIF_SPATIAL ) { int b = (prev2[-2*stride] + next2[-2*stride])>>1; int f = (prev2[+2*stride] + next2[+2*stride])>>1; int max = MAX3(d-e, d-c, MIN(b-c, f-e)); int min = MIN3(d-e, d-c, MAX(b-c, f-e)); diff = MAX3( diff, min, -max ); } if( spatial_pred > d + diff ) { spatial_pred = d + diff; } else if( spatial_pred < d - diff ) { spatial_pred = d - diff; } dst[0] = spatial_pred; dst++; cur++; prev++; next++; prev2++; next2++; } } typedef struct yadif_thread_arg_s { hb_filter_private_t *pv; int segment; } yadif_thread_arg_t; /* * deinterlace this segment of all three planes in a single thread. */ void yadif_filter_thread( void *thread_args_v ) { yadif_arguments_t *yadif_work = NULL; hb_filter_private_t * pv; int run = 1; int segment, segment_start, segment_stop; yadif_thread_arg_t *thread_args = thread_args_v; pv = thread_args->pv; segment = thread_args->segment; hb_log("Yadif Deinterlace thread started for segment %d", segment); while( run ) { /* * Wait here until there is work to do. */ taskset_thread_wait4start( &pv->yadif_taskset, segment ); if( taskset_thread_stop( &pv->yadif_taskset, segment ) ) { /* * No more work to do, exit this thread. */ run = 0; goto report_completion; } yadif_work = &pv->yadif_arguments[segment]; if( yadif_work->dst == NULL ) { hb_error( "Thread started when no work available" ); goto report_completion; } /* * Process all three planes, but only this segment of it. */ int pp; for(pp = 0; pp < 3; pp++) { hb_buffer_t *dst = yadif_work->dst; int w = dst->plane[pp].width; int s = dst->plane[pp].stride; int h = dst->plane[pp].height; int yy; int parity = yadif_work->parity; int tff = yadif_work->tff; int penultimate = h - 2; int segment_height = (h / pv->segments) & ~1; segment_start = segment_height * segment; if( segment == pv->segments - 1 ) { /* * Final segment */ segment_stop = h; } else { segment_stop = segment_height * ( segment + 1 ); } uint8_t *dst2 = &dst->plane[pp].data[segment_start * s]; uint8_t *prev = &pv->yadif_ref[0]->plane[pp].data[segment_start * s]; uint8_t *cur = &pv->yadif_ref[1]->plane[pp].data[segment_start * s]; uint8_t *next = &pv->yadif_ref[2]->plane[pp].data[segment_start * s]; for( yy = segment_start; yy < segment_stop; yy++ ) { if(((yy ^ parity) & 1)) { /* This is the bottom field when TFF and vice-versa. It's the field that gets filtered. Because yadif needs 2 lines above and below the one being filtered, we need to mirror the edges. When TFF, this means replacing the 2nd line with a copy of the 1st, and the last with the second-to-last. */ if( yy > 1 && yy < penultimate ) { /* This isn't the top or bottom, * proceed as normal to yadif. */ yadif_filter_line(pv, dst2, prev, cur, next, w, s, parity ^ tff); } else { // parity == 0 (TFF), y1 = y0 // parity == 1 (BFF), y0 = y1 // parity == 0 (TFF), yu = yp // parity == 1 (BFF), yp = yu uint8_t *src = &pv->yadif_ref[1]->plane[pp].data[(yy^parity)*s]; memcpy(dst2, src, w); } } else { /* Preserve this field unfiltered */ memcpy(dst2, cur, w); } dst2 += s; prev += s; cur += s; next += s; } } report_completion: /* * Finished this segment, let everyone know. */ taskset_thread_complete( &pv->yadif_taskset, segment ); } } /* * threaded yadif - each thread deinterlaces a single segment of all * three planes. Where a segment is defined as the frame divided by * the number of CPUs. * * This function blocks until the frame is deinterlaced. */ static void yadif_filter( hb_filter_private_t * pv, hb_buffer_t * dst, int parity, int tff) { int segment; for( segment = 0; segment < pv->segments; segment++ ) { /* * Setup the work for this plane. */ pv->yadif_arguments[segment].parity = parity; pv->yadif_arguments[segment].tff = tff; pv->yadif_arguments[segment].dst = dst; } /* Allow the taskset threads to make one pass over the data. */ taskset_cycle( &pv->yadif_taskset ); /* * Entire frame is now deinterlaced. */ } /* * deinterlace a frame in a single thread. */ void deint_filter_thread( void *thread_args_v ) { deint_arguments_t *args = NULL; hb_filter_private_t * pv; int run = 1; int segment; deint_thread_arg_t *thread_args = thread_args_v; pv = thread_args->pv; segment = thread_args->segment; hb_log("Fast Deinterlace thread started for segment %d", segment); while( run ) { /* * Wait here until there is work to do. */ taskset_thread_wait4start( &pv->deint_taskset, segment ); if( taskset_thread_stop( &pv->deint_taskset, segment ) ) { /* * No more work to do, exit this thread. */ run = 0; goto report_completion; } args = &pv->deint_arguments[segment]; if( args->dst == NULL ) { // This can happen when flushing final buffers. goto report_completion; } /* * Process all three planes, but only this segment of it. */ hb_deinterlace(args->dst, args->src); report_completion: /* * Finished this segment, let everyone know. */ taskset_thread_complete( &pv->deint_taskset, segment ); } } /* * threaded fast deint - each thread deinterlaces a single frame. * * This function blocks until all frames are deinterlaced. */ static hb_buffer_t * deint_fast(hb_filter_private_t * pv, hb_buffer_t * in) { int ii; hb_buffer_t *dst, *src; if (in != NULL) { dst = hb_frame_buffer_init(in->f.fmt, in->f.width, in->f.height); pv->deint_arguments[pv->deint_nsegs].src = in; pv->deint_arguments[pv->deint_nsegs].dst = dst; pv->deint_nsegs++; } if (in != NULL && pv->deint_nsegs < pv->segments) { return NULL; } if (pv->deint_nsegs > 0) { /* Allow the taskset threads to make one pass over the data. */ taskset_cycle( &pv->deint_taskset ); } hb_buffer_t *first = NULL, *last = NULL; for (ii = 0; ii < pv->deint_nsegs; ii++) { src = pv->deint_arguments[ii].src; dst = pv->deint_arguments[ii].dst; pv->deint_arguments[ii].src = NULL; pv->deint_arguments[ii].dst = NULL; if (first == NULL) { first = dst; } if (last != NULL) { last->next = dst; } last = dst; dst->s = src->s; hb_buffer_move_subs(dst, src); hb_buffer_close(&src); } if (in == NULL) { // Flushing final buffers. Append EOS marker buffer. dst = hb_buffer_init(0); if (first == NULL) { first = dst; } else { last->next = dst; } } pv->deint_nsegs = 0; return first; } static int hb_deinterlace_init( hb_filter_object_t * filter, hb_filter_init_t * init ) { filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); hb_filter_private_t * pv = filter->private_data; pv->width = init->width; pv->height = init->height; pv->yadif_ready = 0; pv->yadif_mode = YADIF_MODE_DEFAULT; pv->yadif_parity = YADIF_PARITY_DEFAULT; if( filter->settings ) { sscanf( filter->settings, "%d:%d", &pv->yadif_mode, &pv->yadif_parity); } pv->cpu_count = hb_get_cpu_count(); /* Allocate yadif specific buffers */ if( pv->yadif_mode & MODE_YADIF_ENABLE ) { /* * Setup yadif taskset. */ pv->segments = pv->cpu_count; pv->yadif_arguments = malloc( sizeof( yadif_arguments_t ) * pv->segments ); if( pv->yadif_arguments == NULL || taskset_init( &pv->yadif_taskset, /*thread_count*/pv->segments, sizeof( yadif_thread_arg_t ) ) == 0 ) { hb_error( "yadif could not initialize taskset" ); } int ii; for( ii = 0; ii < pv->segments; ii++ ) { yadif_thread_arg_t *thread_args; thread_args = taskset_thread_args( &pv->yadif_taskset, ii ); thread_args->pv = pv; thread_args->segment = ii; pv->yadif_arguments[ii].dst = NULL; if( taskset_thread_spawn( &pv->yadif_taskset, ii, "yadif_filter_segment", yadif_filter_thread, HB_NORMAL_PRIORITY ) == 0 ) { hb_error( "yadif could not spawn thread" ); } } } else { /* * Setup fast deint taskset. */ pv->segments = pv->cpu_count; pv->deint_arguments = malloc( sizeof( deint_arguments_t ) * pv->segments ); if( pv->deint_arguments == NULL || taskset_init( &pv->deint_taskset, pv->segments, sizeof( deint_thread_arg_t ) ) == 0 ) { hb_error( "deint could not initialize taskset" ); } int ii; for( ii = 0; ii < pv->segments; ii++ ) { deint_thread_arg_t *thread_args; thread_args = taskset_thread_args( &pv->deint_taskset, ii ); thread_args->pv = pv; thread_args->segment = ii; pv->deint_arguments[ii].dst = NULL; if( taskset_thread_spawn( &pv->deint_taskset, ii, "deint_filter_segment", deint_filter_thread, HB_NORMAL_PRIORITY ) == 0 ) { hb_error( "deint could not spawn thread" ); } } } return 0; } static void hb_deinterlace_close( hb_filter_object_t * filter ) { hb_filter_private_t * pv = filter->private_data; if( !pv ) { return; } /* Cleanup yadif specific buffers */ if( pv->yadif_mode & MODE_YADIF_ENABLE ) { taskset_fini( &pv->yadif_taskset ); int ii; for(ii = 0; ii < 3; ii++) { hb_buffer_close(&pv->yadif_ref[ii]); } free( pv->yadif_arguments ); } else { taskset_fini( &pv->deint_taskset ); free( pv->deint_arguments ); } free( pv ); filter->private_data = NULL; } static int hb_deinterlace_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in; hb_buffer_t * last = NULL, * out = NULL; if ( in->size <= 0 ) { *buf_out = in; *buf_in = NULL; if( !( pv->yadif_mode & MODE_YADIF_ENABLE ) ) { // Flush final frames *buf_out = deint_fast(pv, NULL); } return HB_FILTER_DONE; } /* Use libavcodec deinterlace if yadif_mode < 0 */ if( !( pv->yadif_mode & MODE_YADIF_ENABLE ) ) { *buf_in = NULL; *buf_out = deint_fast(pv, in); return HB_FILTER_OK; } /* Store current frame in yadif cache */ *buf_in = NULL; yadif_store_ref(pv, in); // yadif requires 3 buffers, prev, cur, and next. For the first // frame, there can be no prev, so we duplicate the first frame. if (!pv->yadif_ready) { // If yadif is not ready, store another ref and return HB_FILTER_DELAY yadif_store_ref(pv, hb_buffer_dup(in)); pv->yadif_ready = 1; // Wait for next return HB_FILTER_DELAY; } /* Determine if top-field first layout */ int tff; if( pv->yadif_parity < 0 ) { tff = !!(in->s.flags & PIC_FLAG_TOP_FIELD_FIRST); } else { tff = (pv->yadif_parity & 1) ^ 1; } /* deinterlace both fields if yadif 2 pass or bob */ int frame, num_frames = 1; if ((pv->yadif_mode & MODE_YADIF_2PASS) || (pv->yadif_mode & MODE_YADIF_BOB)) { num_frames = 2; } // Will need up to 2 buffers simultaneously int idx = 0; hb_buffer_t * o_buf[2] = {NULL,}; /* Perform yadif filtering */ for( frame = 0; frame < num_frames; frame++ ) { int parity = frame ^ tff ^ 1; if (o_buf[idx] == NULL) { o_buf[idx] = hb_frame_buffer_init(in->f.fmt, in->f.width, in->f.height); } yadif_filter(pv, o_buf[idx], parity, tff); // If bob, add both frames // else, add only final frame if (( pv->yadif_mode & MODE_YADIF_BOB ) || frame == num_frames - 1) { if ( out == NULL ) { last = out = o_buf[idx]; } else { last->next = o_buf[idx]; last = last->next; } last->next = NULL; // Indicate that buffer was consumed o_buf[idx] = NULL; /* Copy buffered settings to output buffer settings */ last->s = pv->yadif_ref[1]->s; idx ^= 1; } } // Copy subs only to first output buffer hb_buffer_move_subs( out, pv->yadif_ref[1] ); hb_buffer_close(&o_buf[0]); hb_buffer_close(&o_buf[1]); /* if bob mode is engaged, halve the duration of the * timestamps. */ if (pv->yadif_mode & MODE_YADIF_BOB) { out->s.stop -= (out->s.stop - out->s.start) / 2LL; last->s.start = out->s.stop; last->s.new_chap = 0; } *buf_out = out; return HB_FILTER_OK; } HandBrake-0.10.2/libhb/dvd.c0000664000175200017520000012656512463330511016055 0ustar handbrakehandbrake/* dvd.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "lang.h" #include "dvd.h" #include "dvdread/ifo_read.h" #include "dvdread/ifo_print.h" #include "dvdread/nav_read.h" static hb_dvd_t * hb_dvdread_init( char * path ); static void hb_dvdread_close( hb_dvd_t ** _d ); static char * hb_dvdread_name( char * path ); static int hb_dvdread_title_count( hb_dvd_t * d ); static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * d, int t, uint64_t min_duration ); static int hb_dvdread_start( hb_dvd_t * d, hb_title_t *title, int chapter ); static void hb_dvdread_stop( hb_dvd_t * d ); static int hb_dvdread_seek( hb_dvd_t * d, float f ); static hb_buffer_t * hb_dvdread_read( hb_dvd_t * d ); static int hb_dvdread_chapter( hb_dvd_t * d ); static int hb_dvdread_angle_count( hb_dvd_t * d ); static void hb_dvdread_set_angle( hb_dvd_t * d, int angle ); static int hb_dvdread_main_feature( hb_dvd_t * d, hb_list_t * list_title ); hb_dvd_func_t hb_dvdread_func = { hb_dvdread_init, hb_dvdread_close, hb_dvdread_name, hb_dvdread_title_count, hb_dvdread_title_scan, hb_dvdread_start, hb_dvdread_stop, hb_dvdread_seek, hb_dvdread_read, hb_dvdread_chapter, hb_dvdread_angle_count, hb_dvdread_set_angle, hb_dvdread_main_feature }; static hb_dvd_func_t *dvd_methods = &hb_dvdread_func; /*********************************************************************** * Local prototypes **********************************************************************/ static void FindNextCell( hb_dvdread_t * ); static int dvdtime2msec( dvd_time_t * ); static int hb_dvdread_is_break( hb_dvdread_t * d ); hb_dvd_func_t * hb_dvdread_methods( void ) { return &hb_dvdread_func; } static int hb_dvdread_main_feature( hb_dvd_t * e, hb_list_t * list_title ) { int ii; uint64_t longest_duration = 0; int longest = -1; for ( ii = 0; ii < hb_list_count( list_title ); ii++ ) { hb_title_t * title = hb_list_item( list_title, ii ); if ( title->duration > longest_duration ) { longest_duration = title->duration; longest = title->index; } } return longest; } static char * hb_dvdread_name( char * path ) { static char name[1024]; unsigned char unused[1024]; dvd_reader_t * reader; reader = DVDOpen( path ); if( !reader ) { return NULL; } if( DVDUDFVolumeInfo( reader, name, sizeof( name ), unused, sizeof( unused ) ) ) { DVDClose( reader ); return NULL; } DVDClose( reader ); return name; } /*********************************************************************** * hb_dvdread_init *********************************************************************** * **********************************************************************/ hb_dvd_t * hb_dvdread_init( char * path ) { hb_dvd_t * e; hb_dvdread_t * d; int region_mask; char * path_ccp; e = calloc( sizeof( hb_dvd_t ), 1 ); d = &(e->dvdread); /* * Convert UTF-8 path to current code page on Windows * hb_utf8_to_cp() is the same as strdup on non-Windows, * so no #ifdef required here */ path_ccp = hb_utf8_to_cp( path ); /* Log DVD drive region code */ if ( hb_dvd_region( path_ccp, ®ion_mask ) == 0 ) { hb_log( "dvd: Region mask 0x%02x", region_mask ); if ( region_mask == 0xFF ) { hb_log( "dvd: Warning, DVD device has no region set" ); } } /* Open device */ if( !( d->reader = DVDOpen( path_ccp ) ) ) { /* * Not an error, may be a stream - which we'll try in a moment. */ hb_log( "dvd: not a dvd - trying as a stream/file instead" ); goto fail; } /* Open main IFO */ if( !( d->vmg = ifoOpen( d->reader, 0 ) ) ) { hb_error( "dvd: ifoOpen failed" ); goto fail; } d->path = strdup( path ); /* hb_dvdread_title_scan assumes UTF-8 path, so not path_ccp here */ free( path_ccp ); return e; fail: if( d->vmg ) ifoClose( d->vmg ); if( d->reader ) DVDClose( d->reader ); free( e ); free( path_ccp ); return NULL; } /*********************************************************************** * hb_dvdread_title_count **********************************************************************/ static int hb_dvdread_title_count( hb_dvd_t * e ) { hb_dvdread_t *d = &(e->dvdread); return d->vmg->tt_srpt->nr_of_srpts; } /*********************************************************************** * hb_dvdread_title_scan **********************************************************************/ static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * e, int t, uint64_t min_duration ) { hb_dvdread_t *d = &(e->dvdread); hb_title_t * title; ifo_handle_t * vts = NULL; int pgc_id, pgn, i; hb_chapter_t * chapter; unsigned char unused[1024]; const char * codec_name; hb_log( "scan: scanning title %d", t ); title = hb_title_init( d->path, t ); title->type = HB_DVD_TYPE; if( DVDUDFVolumeInfo( d->reader, title->name, sizeof( title->name ), unused, sizeof( unused ) ) ) { char * p_cur, * p_last = d->path; for( p_cur = d->path; *p_cur; p_cur++ ) { if( IS_DIR_SEP(p_cur[0]) && p_cur[1] ) { p_last = &p_cur[1]; } } snprintf( title->name, sizeof( title->name ), "%s", p_last ); char *dot_term = strrchr(title->name, '.'); if (dot_term) *dot_term = '\0'; } /* VTS which our title is in */ title->vts = d->vmg->tt_srpt->title[t-1].title_set_nr; if ( !title->vts ) { /* A VTS of 0 means the title wasn't found in the title set */ hb_log("Invalid VTS (title set) number: %i", title->vts); goto fail; } hb_log( "scan: opening IFO for VTS %d", title->vts ); if( !( vts = ifoOpen( d->reader, title->vts ) ) ) { hb_log( "scan: ifoOpen failed" ); goto fail; } /* ignore titles with bogus cell addresses so we don't abort later * in libdvdread. */ for ( i = 0; i < vts->vts_c_adt->nr_of_vobs; ++i) { if( (vts->vts_c_adt->cell_adr_table[i].start_sector & 0xffffff ) == 0xffffff ) { hb_log( "scan: cell_adr_table[%d].start_sector invalid (0x%x) " "- skipping title", i, vts->vts_c_adt->cell_adr_table[i].start_sector ); goto fail; } if( (vts->vts_c_adt->cell_adr_table[i].last_sector & 0xffffff ) == 0xffffff ) { hb_log( "scan: cell_adr_table[%d].last_sector invalid (0x%x) " "- skipping title", i, vts->vts_c_adt->cell_adr_table[i].last_sector ); goto fail; } if( vts->vts_c_adt->cell_adr_table[i].start_sector >= vts->vts_c_adt->cell_adr_table[i].last_sector ) { hb_log( "scan: cell_adr_table[%d].start_sector (0x%x) " "is not before last_sector (0x%x) - skipping title", i, vts->vts_c_adt->cell_adr_table[i].start_sector, vts->vts_c_adt->cell_adr_table[i].last_sector ); goto fail; } } if( global_verbosity_level == 3 ) { ifo_print( d->reader, title->vts ); } /* Position of the title in the VTS */ title->ttn = d->vmg->tt_srpt->title[t-1].vts_ttn; if ( title->ttn < 1 || title->ttn > vts->vts_ptt_srpt->nr_of_srpts ) { hb_log( "invalid VTS PTT offset %d for title %d, skipping", title->ttn, t ); goto fail; } /* Get pgc */ pgc_id = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgcn; if ( pgc_id < 1 || pgc_id > vts->vts_pgcit->nr_of_pgci_srp ) { hb_log( "invalid PGC ID %d for title %d, skipping", pgc_id, t ); goto fail; } pgn = vts->vts_ptt_srpt->title[title->ttn-1].ptt[0].pgn; d->pgc = vts->vts_pgcit->pgci_srp[pgc_id-1].pgc; hb_log("pgc_id: %d, pgn: %d: pgc: %p", pgc_id, pgn, d->pgc); if( !d->pgc || !d->pgc->program_map ) { hb_log( "scan: pgc not valid, skipping" ); goto fail; } if (d->pgc->cell_playback == NULL) { hb_log( "invalid PGC cell_playback table for title %d, skipping", t ); goto fail; } if( pgn <= 0 || pgn > 99 ) { hb_log( "scan: pgn %d not valid, skipping", pgn ); goto fail; } /* Start cell */ title->cell_start = d->pgc->program_map[pgn-1] - 1; title->block_start = d->pgc->cell_playback[title->cell_start].first_sector; /* End cell */ title->cell_end = d->pgc->nr_of_cells - 1; title->block_end = d->pgc->cell_playback[title->cell_end].last_sector; /* Block count */ title->block_count = 0; d->cell_cur = title->cell_start; while( d->cell_cur <= title->cell_end ) { #define cp d->pgc->cell_playback[d->cell_cur] title->block_count += cp.last_sector + 1 - cp.first_sector; #undef cp FindNextCell( d ); d->cell_cur = d->cell_next; } hb_log( "scan: vts=%d, ttn=%d, cells=%d->%d, blocks=%"PRIu64"->%"PRIu64", " "%"PRIu64" blocks", title->vts, title->ttn, title->cell_start, title->cell_end, title->block_start, title->block_end, title->block_count ); /* Get duration */ title->duration = 90LL * dvdtime2msec( &d->pgc->playback_time ); title->hours = title->duration / 90000 / 3600; title->minutes = ( ( title->duration / 90000 ) % 3600 ) / 60; title->seconds = ( title->duration / 90000 ) % 60; hb_log( "scan: duration is %02d:%02d:%02d (%"PRId64" ms)", title->hours, title->minutes, title->seconds, title->duration / 90 ); /* ignore titles under 10 seconds because they're often stills or * clips with no audio & our preview code doesn't currently handle * either of these. */ if( title->duration < min_duration ) { hb_log( "scan: ignoring title (too short)" ); goto fail; } /* Detect languages */ for( i = 0; i < vts->vtsi_mat->nr_of_vts_audio_streams; i++ ) { int audio_format, lang_code, lang_extension, audio_control, position, j; hb_audio_t * audio, * audio_tmp; iso639_lang_t * lang; hb_log( "scan: checking audio %d", i + 1 ); audio = calloc( sizeof( hb_audio_t ), 1 ); audio_format = vts->vtsi_mat->vts_audio_attr[i].audio_format; lang_code = vts->vtsi_mat->vts_audio_attr[i].lang_code; lang_extension = vts->vtsi_mat->vts_audio_attr[i].code_extension; audio_control = vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->audio_control[i]; if( !( audio_control & 0x8000 ) ) { hb_log( "scan: audio channel is not active" ); free( audio ); continue; } position = ( audio_control & 0x7F00 ) >> 8; switch( audio_format ) { case 0x00: audio->id = ( ( 0x80 + position ) << 8 ) | 0xbd; audio->config.in.codec = HB_ACODEC_AC3; audio->config.in.codec_param = AV_CODEC_ID_AC3; codec_name = "AC3"; break; case 0x02: case 0x03: audio->id = 0xc0 + position; audio->config.in.codec = HB_ACODEC_FFMPEG; audio->config.in.codec_param = AV_CODEC_ID_MP2; codec_name = "MPEG"; break; case 0x04: audio->id = ( ( 0xa0 + position ) << 8 ) | 0xbd; audio->config.in.codec = HB_ACODEC_LPCM; codec_name = "LPCM"; break; case 0x06: audio->id = ( ( 0x88 + position ) << 8 ) | 0xbd; audio->config.in.codec = HB_ACODEC_DCA; audio->config.in.codec_param = AV_CODEC_ID_DTS; codec_name = "DTS"; break; default: audio->id = 0; audio->config.in.codec = 0; codec_name = "Unknown"; hb_log( "scan: unknown audio codec (%x)", audio_format ); break; } if( !audio->id ) { continue; } /* Check for duplicate tracks */ audio_tmp = NULL; for( j = 0; j < hb_list_count( title->list_audio ); j++ ) { audio_tmp = hb_list_item( title->list_audio, j ); if( audio->id == audio_tmp->id ) { break; } audio_tmp = NULL; } if( audio_tmp ) { hb_log( "scan: duplicate audio track" ); free( audio ); continue; } lang = lang_for_code( lang_code ); audio->config.lang.type = lang_extension; snprintf( audio->config.lang.simple, sizeof( audio->config.lang.simple ), "%s", strlen( lang->native_name ) ? lang->native_name : lang->eng_name ); snprintf( audio->config.lang.iso639_2, sizeof( audio->config.lang.iso639_2 ), "%s", lang->iso639_2 ); hb_log("scan: id=0x%x, lang=%s (%s), 3cc=%s ext=%i", audio->id, audio->config.lang.simple, codec_name, audio->config.lang.iso639_2, lang_extension); audio->config.in.track = i; hb_list_add( title->list_audio, audio ); } /* Check for subtitles */ for( i = 0; i < vts->vtsi_mat->nr_of_vts_subp_streams; i++ ) { hb_subtitle_t * subtitle; int spu_control; int position; iso639_lang_t * lang; int lang_extension = 0; hb_log( "scan: checking subtitle %d", i + 1 ); spu_control = vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->subp_control[i]; if( !( spu_control & 0x80000000 ) ) { hb_log( "scan: subtitle channel is not active" ); continue; } if( vts->vtsi_mat->vts_video_attr.display_aspect_ratio ) { switch( vts->vtsi_mat->vts_video_attr.permitted_df ) { case 1: position = spu_control & 0xFF; break; case 2: position = ( spu_control >> 8 ) & 0xFF; break; default: position = ( spu_control >> 16 ) & 0xFF; } } else { position = ( spu_control >> 24 ) & 0x7F; } lang_extension = vts->vtsi_mat->vts_subp_attr[i].code_extension; lang = lang_for_code( vts->vtsi_mat->vts_subp_attr[i].lang_code ); subtitle = calloc( sizeof( hb_subtitle_t ), 1 ); subtitle->track = i+1; subtitle->id = ( ( 0x20 + position ) << 8 ) | 0xbd; snprintf( subtitle->lang, sizeof( subtitle->lang ), "%s", strlen(lang->native_name) ? lang->native_name : lang->eng_name); snprintf( subtitle->iso639_2, sizeof( subtitle->iso639_2 ), "%s", lang->iso639_2); subtitle->format = PICTURESUB; subtitle->source = VOBSUB; subtitle->config.dest = RENDERSUB; // By default render (burn-in) the VOBSUB. subtitle->stream_type = 0xbd; subtitle->substream_type = 0x20 + position; subtitle->codec = WORK_DECVOBSUB; subtitle->type = lang_extension; memcpy( subtitle->palette, vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->palette, 16 * sizeof( uint32_t ) ); subtitle->palette_set = 1; switch( lang_extension ) { case 2: strcat( subtitle->lang, " (Caption with bigger size character)" ); break; case 3: strcat( subtitle->lang, " (Caption for Children)" ); break; case 5: strcat( subtitle->lang, " (Closed Caption)" ); break; case 6: strcat( subtitle->lang, " (Closed Caption with bigger size character)" ); break; case 7: strcat( subtitle->lang, " (Closed Caption for Children)" ); break; case 9: strcat( subtitle->lang, " (Forced Caption)" ); break; case 13: strcat( subtitle->lang, " (Director's Commentary)" ); break; case 14: strcat( subtitle->lang, " (Director's Commentary with bigger size character)" ); break; case 15: strcat( subtitle->lang, " (Director's Commentary for Children)" ); default: break; } hb_log( "scan: id=0x%x, lang=%s, 3cc=%s ext=%i", subtitle->id, subtitle->lang, subtitle->iso639_2, lang_extension ); hb_list_add( title->list_subtitle, subtitle ); } /* Chapters */ hb_log( "scan: title %d has %d chapters", t, vts->vts_ptt_srpt->title[title->ttn-1].nr_of_ptts ); for( i = 0; i < vts->vts_ptt_srpt->title[title->ttn-1].nr_of_ptts; i++ ) { char chapter_title[80]; chapter = calloc( sizeof( hb_chapter_t ), 1 ); /* remember the on-disc chapter number */ chapter->index = i + 1; sprintf( chapter_title, "Chapter %d", chapter->index ); hb_chapter_set_title( chapter, chapter_title ); pgc_id = vts->vts_ptt_srpt->title[title->ttn-1].ptt[i].pgcn; pgn = vts->vts_ptt_srpt->title[title->ttn-1].ptt[i].pgn; d->pgc = vts->vts_pgcit->pgci_srp[pgc_id-1].pgc; /* Start cell */ chapter->cell_start = d->pgc->program_map[pgn-1] - 1; chapter->block_start = d->pgc->cell_playback[chapter->cell_start].first_sector; // if there are no more programs in this pgc, the end cell is the // last cell. Otherwise it's the cell before the start cell of the // next program. if ( pgn == d->pgc->nr_of_programs ) { chapter->cell_end = d->pgc->nr_of_cells - 1; } else { chapter->cell_end = d->pgc->program_map[pgn] - 2;; } chapter->block_end = d->pgc->cell_playback[chapter->cell_end].last_sector; /* Block count, duration */ chapter->block_count = 0; chapter->duration = 0; d->cell_cur = chapter->cell_start; while( d->cell_cur <= chapter->cell_end ) { #define cp d->pgc->cell_playback[d->cell_cur] chapter->block_count += cp.last_sector + 1 - cp.first_sector; chapter->duration += 90LL * dvdtime2msec( &cp.playback_time ); #undef cp FindNextCell( d ); d->cell_cur = d->cell_next; } hb_list_add( title->list_chapter, chapter ); } for( i = 0; i < hb_list_count( title->list_chapter ); i++ ) { chapter = hb_list_item( title->list_chapter, i ); int seconds = ( chapter->duration + 45000 ) / 90000; chapter->hours = ( seconds / 3600 ); chapter->minutes = ( seconds % 3600 ) / 60; chapter->seconds = ( seconds % 60 ); hb_log( "scan: chap %d c=%d->%d, b=%"PRIu64"->%"PRIu64" (%"PRIu64"), %"PRId64" ms", chapter->index, chapter->cell_start, chapter->cell_end, chapter->block_start, chapter->block_end, chapter->block_count, chapter->duration / 90 ); } /* Get aspect. We don't get width/height/rate infos here as they tend to be wrong */ switch( vts->vtsi_mat->vts_video_attr.display_aspect_ratio ) { case 0: title->container_aspect = 4. / 3.; break; case 3: title->container_aspect = 16. / 9.; break; default: hb_log( "scan: unknown aspect" ); goto fail; } hb_log( "scan: aspect = %g", title->container_aspect ); /* This title is ok so far */ goto cleanup; fail: hb_title_close( &title ); cleanup: if( vts ) ifoClose( vts ); return title; } /*********************************************************************** * hb_dvdread_start *********************************************************************** * Title and chapter start at 1 **********************************************************************/ static int hb_dvdread_start( hb_dvd_t * e, hb_title_t *title, int chapter ) { hb_dvdread_t *d = &(e->dvdread); int pgc_id, pgn; int i; int t = title->index; /* Open the IFO and the VOBs for this title */ d->vts = d->vmg->tt_srpt->title[t-1].title_set_nr; d->ttn = d->vmg->tt_srpt->title[t-1].vts_ttn; if( !( d->ifo = ifoOpen( d->reader, d->vts ) ) ) { hb_error( "dvd: ifoOpen failed for VTS %d", d->vts ); return 0; } if( !( d->file = DVDOpenFile( d->reader, d->vts, DVD_READ_TITLE_VOBS ) ) ) { hb_error( "dvd: DVDOpenFile failed for VTS %d", d->vts ); return 0; } /* Get title first and last blocks */ pgc_id = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[0].pgcn; pgn = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[0].pgn; d->pgc = d->ifo->vts_pgcit->pgci_srp[pgc_id-1].pgc; d->cell_start = d->pgc->program_map[pgn - 1] - 1; d->cell_end = d->pgc->nr_of_cells - 1; d->title_start = d->pgc->cell_playback[d->cell_start].first_sector; d->title_end = d->pgc->cell_playback[d->cell_end].last_sector; /* Block count */ d->title_block_count = 0; for( i = d->cell_start; i <= d->cell_end; i++ ) { d->title_block_count += d->pgc->cell_playback[i].last_sector + 1 - d->pgc->cell_playback[i].first_sector; } /* Get pgc for the current chapter */ pgc_id = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[chapter-1].pgcn; pgn = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[chapter-1].pgn; d->pgc = d->ifo->vts_pgcit->pgci_srp[pgc_id-1].pgc; /* Get the two first cells */ d->cell_cur = d->pgc->program_map[pgn-1] - 1; FindNextCell( d ); d->block = d->pgc->cell_playback[d->cell_cur].first_sector; d->next_vobu = d->block; d->pack_len = 0; d->cell_overlap = 0; d->in_cell = 0; d->in_sync = 2; return 1; } /*********************************************************************** * hb_dvdread_stop *********************************************************************** * **********************************************************************/ static void hb_dvdread_stop( hb_dvd_t * e ) { hb_dvdread_t *d = &(e->dvdread); if( d->ifo ) { ifoClose( d->ifo ); d->ifo = NULL; } if( d->file ) { DVDCloseFile( d->file ); d->file = NULL; } } /*********************************************************************** * hb_dvdread_seek *********************************************************************** * **********************************************************************/ static int hb_dvdread_seek( hb_dvd_t * e, float f ) { hb_dvdread_t *d = &(e->dvdread); int count, sizeCell; int i; count = f * d->title_block_count; for( i = d->cell_start; i <= d->cell_end; i++ ) { sizeCell = d->pgc->cell_playback[i].last_sector + 1 - d->pgc->cell_playback[i].first_sector; if( count < sizeCell ) { d->cell_cur = i; d->cur_cell_id = 0; FindNextCell( d ); /* Now let hb_dvdread_read find the next VOBU */ d->next_vobu = d->pgc->cell_playback[i].first_sector + count; d->pack_len = 0; break; } count -= sizeCell; } if( i > d->cell_end ) { return 0; } /* * Assume that we are in sync, even if we are not given that it is obvious * that we are out of sync if we have just done a seek. */ d->in_sync = 2; return 1; } /*********************************************************************** * is_nav_pack ***********************************************************************/ int is_nav_pack( unsigned char *buf ) { /* * The NAV Pack is comprised of the PCI Packet and DSI Packet, both * of these start at known offsets and start with a special identifier. * * NAV = { * PCI = { 00 00 01 bf # private stream header * ?? ?? # length * 00 # substream * ... * } * DSI = { 00 00 01 bf # private stream header * ?? ?? # length * 01 # substream * ... * } * * The PCI starts at offset 0x26 into the sector, and the DSI starts at 0x400 * * This information from: http://dvd.sourceforge.net/dvdinfo/ */ if( ( buf[0x26] == 0x00 && // PCI buf[0x27] == 0x00 && buf[0x28] == 0x01 && buf[0x29] == 0xbf && buf[0x2c] == 0x00 ) && ( buf[0x400] == 0x00 && // DSI buf[0x401] == 0x00 && buf[0x402] == 0x01 && buf[0x403] == 0xbf && buf[0x406] == 0x01 ) ) { return ( 1 ); } else { return ( 0 ); } } /*********************************************************************** * hb_dvdread_read *********************************************************************** * **********************************************************************/ static hb_buffer_t * hb_dvdread_read( hb_dvd_t * e ) { hb_dvdread_t *d = &(e->dvdread); hb_buffer_t *b = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE ); top: if( !d->pack_len ) { /* New pack */ dsi_t dsi_pack; int error = 0; // if we've just finished the last cell of the title we don't // want to read another block because our next_vobu pointer // is probably invalid. Just return 'no data' & our caller // should check and discover we're at eof. if ( d->cell_cur > d->cell_end ) { hb_buffer_close( &b ); return NULL; } for( ;; ) { int block, pack_len, next_vobu, read_retry; for( read_retry = 1; read_retry < 1024; read_retry++ ) { if( DVDReadBlocks( d->file, d->next_vobu, 1, b->data ) == 1 ) { /* * Successful read. */ if( read_retry > 1 && !is_nav_pack( b->data) ) { // But wasn't a nav pack, so carry on looking read_retry = 1; d->next_vobu++; continue; } break; } else { // First retry the same block, then try the next one, // adjust the skip increment upwards so that we can skip // large sections of bad blocks more efficiently (at the // cost of some missed good blocks at the end). hb_log( "dvd: vobu read error blk %d - skipping to next blk incr %d", d->next_vobu, (read_retry * 10)); d->next_vobu += (read_retry * 10); } } if( read_retry == 1024 ) { // That's too many bad blocks, jump to the start of the // next cell. hb_log( "dvd: vobu read error blk %d - skipping to cell %d", d->next_vobu, d->cell_next ); d->cell_cur = d->cell_next; if ( d->cell_cur > d->cell_end ) { hb_buffer_close( &b ); return NULL; } d->in_cell = 0; d->next_vobu = d->pgc->cell_playback[d->cell_cur].first_sector; FindNextCell( d ); d->cell_overlap = 1; continue; } if ( !is_nav_pack( b->data ) ) { (d->next_vobu)++; if( d->in_sync == 1 ) { hb_log("dvd: Lost sync, searching for NAV pack at blk %d", d->next_vobu); d->in_sync = 0; } continue; } navRead_DSI( &dsi_pack, &b->data[DSI_START_BYTE] ); if ( d->in_sync == 0 && d->cur_cell_id && (d->cur_vob_id != dsi_pack.dsi_gi.vobu_vob_idn || d->cur_cell_id != dsi_pack.dsi_gi.vobu_c_idn ) ) { // We walked out of the cell we're supposed to be in. // If we're not at the start of our next cell go there. hb_log("dvd: left cell %d (%u,%u) for (%u,%u) at block %u", d->cell_cur, d->cur_vob_id, d->cur_cell_id, dsi_pack.dsi_gi.vobu_vob_idn, dsi_pack.dsi_gi.vobu_c_idn, d->next_vobu ); if ( d->next_vobu != d->pgc->cell_playback[d->cell_next].first_sector ) { d->next_vobu = d->pgc->cell_playback[d->cell_next].first_sector; d->cur_cell_id = 0; continue; } } block = dsi_pack.dsi_gi.nv_pck_lbn; pack_len = dsi_pack.dsi_gi.vobu_ea; // There are a total of 21 next vobu offsets (and 21 prev_vobu // offsets) in the navpack SRI structure. The primary one is // 'next_vobu' which is the offset (in dvd blocks) from the current // block to the start of the next vobu. If the block at 'next_vobu' // can't be read, 'next_video' is the offset to the vobu closest to it. // The other 19 offsets are vobus at successively longer distances from // the current block (this is so that a hardware player can do // adaptive error recovery to skip over a bad spot on the disk). In all // these offsets the high bit is set to indicate when it contains a // valid offset. The next bit (2^30) is set to indicate that there's // another valid offset in the SRI that's closer to the current block. // A hardware player uses these bits to chose the closest valid offset // and uses that as its next vobu. (Some mastering schemes appear to // put a bogus offset in next_vobu with the 2^30 bit set & the // correct offset in next_video. This works fine in hardware players // but will mess up software that doesn't implement the full next // vobu decision logic.) In addition to the flag bits there's a // reserved value of the offset that indicates 'no next vobu' (which // indicates the end of a cell). But having no next vobu pointer with a // 'valid' bit set also indicates end of cell. Different mastering // schemes seem to use all possible combinations of the flag bits // and reserved values to indicate end of cell so we have to check // them all or we'll get a disk read error from following an // invalid offset. uint32_t next_ptr = dsi_pack.vobu_sri.next_vobu; if ( ( next_ptr & ( 1 << 31 ) ) == 0 || ( next_ptr & ( 1 << 30 ) ) != 0 || ( next_ptr & 0x3fffffff ) == 0x3fffffff ) { next_ptr = dsi_pack.vobu_sri.next_video; if ( ( next_ptr & ( 1 << 31 ) ) == 0 || ( next_ptr & 0x3fffffff ) == 0x3fffffff ) { // don't have a valid forward pointer - assume end-of-cell d->block = block; d->pack_len = pack_len; break; } } next_vobu = block + ( next_ptr & 0x3fffffff ); if( pack_len > 0 && pack_len < 1024 && block >= d->next_vobu && ( block <= d->title_start + d->title_block_count || block <= d->title_end ) ) { d->block = block; d->pack_len = pack_len; d->next_vobu = next_vobu; break; } /* Wasn't a valid VOBU, try next block */ if( ++error > 1024 ) { hb_log( "dvd: couldn't find a VOBU after 1024 blocks" ); hb_buffer_close( &b ); return NULL; } (d->next_vobu)++; } if( d->in_sync == 0 || d->in_sync == 2 ) { if( d->in_sync == 0 ) { hb_log( "dvd: In sync with DVD at block %d", d->block ); } d->in_sync = 1; } // Revert the cell overlap, and check for a chapter break // If this cell is zero length (prev_vobu & next_vobu both // set to END_OF_CELL) we need to check for beginning of // cell before checking for end or we'll advance to the next // cell too early and fail to generate a chapter mark when this // cell starts a chapter. if( ( dsi_pack.vobu_sri.prev_vobu & (1 << 31 ) ) == 0 || ( dsi_pack.vobu_sri.prev_vobu & 0x3fffffff ) == 0x3fffffff ) { // A vobu that's not at the start of a cell can have an // EOC prev pointer (this seems to be associated with some // sort of drm). The rest of the content in the cell may be // booby-trapped so treat this like an end-of-cell rather // than a beginning of cell. if ( d->pgc->cell_playback[d->cell_cur].first_sector < dsi_pack.dsi_gi.nv_pck_lbn && d->pgc->cell_playback[d->cell_cur].last_sector >= dsi_pack.dsi_gi.nv_pck_lbn ) { hb_log( "dvd: null prev_vobu in cell %d at block %d", d->cell_cur, d->block ); // treat like end-of-cell then go directly to start of next cell. d->cell_cur = d->cell_next; d->in_cell = 0; d->next_vobu = d->pgc->cell_playback[d->cell_cur].first_sector; FindNextCell( d ); d->cell_overlap = 1; goto top; } else { if ( d->block != d->pgc->cell_playback[d->cell_cur].first_sector ) { hb_log( "dvd: beginning of cell %d at block %d", d->cell_cur, d->block ); } if( d->in_cell ) { hb_error( "dvd: assuming missed end of cell %d at block %d", d->cell_cur, d->block ); d->cell_cur = d->cell_next; d->next_vobu = d->pgc->cell_playback[d->cell_cur].first_sector; FindNextCell( d ); d->cell_overlap = 1; d->in_cell = 0; } else { d->in_cell = 1; } d->cur_vob_id = dsi_pack.dsi_gi.vobu_vob_idn; d->cur_cell_id = dsi_pack.dsi_gi.vobu_c_idn; if( d->cell_overlap ) { b->s.new_chap = hb_dvdread_is_break( d ); d->cell_overlap = 0; } } } if( ( dsi_pack.vobu_sri.next_vobu & (1 << 31 ) ) == 0 || ( dsi_pack.vobu_sri.next_vobu & 0x3fffffff ) == 0x3fffffff ) { if ( d->block <= d->pgc->cell_playback[d->cell_cur].first_sector || d->block > d->pgc->cell_playback[d->cell_cur].last_sector ) { hb_log( "dvd: end of cell %d at block %d", d->cell_cur, d->block ); } d->cell_cur = d->cell_next; d->in_cell = 0; d->next_vobu = d->pgc->cell_playback[d->cell_cur].first_sector; FindNextCell( d ); d->cell_overlap = 1; } } else { if( DVDReadBlocks( d->file, d->block, 1, b->data ) != 1 ) { // this may be a real DVD error or may be DRM. Either way // we don't want to quit because of one bad block so set // things up so we'll advance to the next vobu and recurse. hb_error( "dvd: DVDReadBlocks failed (%d), skipping to vobu %u", d->block, d->next_vobu ); d->pack_len = 0; goto top; /* XXX need to restructure this routine & avoid goto */ } d->pack_len--; } d->block++; return b; } /*********************************************************************** * hb_dvdread_chapter *********************************************************************** * Returns in which chapter the next block to be read is. * Chapter numbers start at 1. **********************************************************************/ static int hb_dvdread_chapter( hb_dvd_t * e ) { hb_dvdread_t *d = &(e->dvdread); int i; int pgc_id, pgn; int nr_of_ptts = d->ifo->vts_ptt_srpt->title[d->ttn-1].nr_of_ptts; pgc_t * pgc; for( i = nr_of_ptts - 1; i >= 0; i-- ) { /* Get pgc for chapter (i+1) */ pgc_id = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[i].pgcn; pgn = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[i].pgn; pgc = d->ifo->vts_pgcit->pgci_srp[pgc_id-1].pgc; if( d->cell_cur - d->cell_overlap >= pgc->program_map[pgn-1] - 1 && d->cell_cur - d->cell_overlap <= pgc->nr_of_cells - 1 ) { /* We are in this chapter */ return i + 1; } } /* End of title */ return -1; } /*********************************************************************** * hb_dvdread_is_break *********************************************************************** * Returns chapter number if the current block is a new chapter start **********************************************************************/ static int hb_dvdread_is_break( hb_dvdread_t * d ) { int i; int pgc_id, pgn; int nr_of_ptts = d->ifo->vts_ptt_srpt->title[d->ttn-1].nr_of_ptts; pgc_t * pgc; int cell; for( i = nr_of_ptts - 1; i > 0; i-- ) { /* Get pgc for chapter (i+1) */ pgc_id = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[i].pgcn; pgn = d->ifo->vts_ptt_srpt->title[d->ttn-1].ptt[i].pgn; pgc = d->ifo->vts_pgcit->pgci_srp[pgc_id-1].pgc; cell = pgc->program_map[pgn-1] - 1; if( cell <= d->cell_start ) break; // This must not match against the start cell. if( pgc->cell_playback[cell].first_sector == d->block && cell != d->cell_start ) { return i + 1; } } return 0; } /*********************************************************************** * hb_dvdread_close *********************************************************************** * Closes and frees everything **********************************************************************/ static void hb_dvdread_close( hb_dvd_t ** _d ) { hb_dvdread_t * d = &((*_d)->dvdread); if( d->vmg ) { ifoClose( d->vmg ); } if( d->reader ) { DVDClose( d->reader ); } free( d ); *_d = NULL; } /*********************************************************************** * hb_dvdread_angle_count *********************************************************************** * Returns the number of angles supported. We do not support angles * with dvdread **********************************************************************/ static int hb_dvdread_angle_count( hb_dvd_t * d ) { return 1; } /*********************************************************************** * hb_dvdread_set_angle *********************************************************************** * Sets the angle to read. Not supported with dvdread **********************************************************************/ static void hb_dvdread_set_angle( hb_dvd_t * d, int angle ) { } /*********************************************************************** * FindNextCell *********************************************************************** * Assumes pgc and cell_cur are correctly set, and sets cell_next to the * cell to be read when we will be done with cell_cur. **********************************************************************/ static void FindNextCell( hb_dvdread_t * d ) { int i = 0; if( d->pgc->cell_playback[d->cell_cur].block_type == BLOCK_TYPE_ANGLE_BLOCK ) { while( d->pgc->cell_playback[d->cell_cur+i].block_mode != BLOCK_MODE_LAST_CELL ) { i++; } d->cell_next = d->cell_cur + i + 1; hb_log( "dvd: Skipping multi-angle cells %d-%d", d->cell_cur, d->cell_next - 1 ); } else { d->cell_next = d->cell_cur + 1; } } /*********************************************************************** * dvdtime2msec *********************************************************************** * From lsdvd **********************************************************************/ static int dvdtime2msec(dvd_time_t * dt) { double frames_per_s[4] = {-1.0, 25.00, -1.0, 29.97}; double fps = frames_per_s[(dt->frame_u & 0xc0) >> 6]; long ms; ms = (((dt->hour & 0xf0) >> 3) * 5 + (dt->hour & 0x0f)) * 3600000; ms += (((dt->minute & 0xf0) >> 3) * 5 + (dt->minute & 0x0f)) * 60000; ms += (((dt->second & 0xf0) >> 3) * 5 + (dt->second & 0x0f)) * 1000; if( fps > 0 ) { ms += (((dt->frame_u & 0x30) >> 3) * 5 + (dt->frame_u & 0x0f)) * 1000.0 / fps; } return ms; } char * hb_dvd_name( char * path ) { return dvd_methods->name(path); } hb_dvd_t * hb_dvd_init( char * path ) { return dvd_methods->init(path); } int hb_dvd_title_count( hb_dvd_t * d ) { return dvd_methods->title_count(d); } hb_title_t * hb_dvd_title_scan( hb_dvd_t * d, int t, uint64_t min_duration ) { return dvd_methods->title_scan(d, t, min_duration); } int hb_dvd_start( hb_dvd_t * d, hb_title_t *title, int chapter ) { return dvd_methods->start(d, title, chapter); } void hb_dvd_stop( hb_dvd_t * d ) { dvd_methods->stop(d); } int hb_dvd_seek( hb_dvd_t * d, float f ) { return dvd_methods->seek(d, f); } hb_buffer_t * hb_dvd_read( hb_dvd_t * d ) { return dvd_methods->read(d); } int hb_dvd_chapter( hb_dvd_t * d ) { return dvd_methods->chapter(d); } void hb_dvd_close( hb_dvd_t ** _d ) { dvd_methods->close(_d); } int hb_dvd_angle_count( hb_dvd_t * d ) { return dvd_methods->angle_count(d); } void hb_dvd_set_angle( hb_dvd_t * d, int angle ) { dvd_methods->set_angle(d, angle); } int hb_dvd_main_feature( hb_dvd_t * d, hb_list_t * list_title ) { return dvd_methods->main_feature(d, list_title); } // hb_dvd_set_dvdnav must only be called when no dvd source is open // it rips the rug out from under things so be careful void hb_dvd_set_dvdnav( int enable ) { if (enable) dvd_methods = hb_dvdnav_methods(); else dvd_methods = hb_dvdread_methods(); } HandBrake-0.10.2/libhb/enc_qsv.c0000664000175200017520000020273412321011646016725 0ustar handbrakehandbrake/* ********************************************************************* *\ Copyright (C) 2013 Intel Corporation. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \* ********************************************************************* */ #ifdef USE_QSV #include "hb.h" #include "nal_units.h" #include "qsv_common.h" #include "qsv_memory.h" #include "h264_common.h" /* * The frame info struct remembers information about each frame across calls to * the encoder. Since frames are uniquely identified by their timestamp, we use * some bits of the timestamp as an index. The LSB is chosen so that two * successive frames will have different values in the bits over any plausible * range of frame rates (starting with bit 8 allows any frame rate slower than * 352fps). The MSB determines the size of the array. It is chosen so that two * frames can't use the same slot during the encoder's max frame delay so that, * up to some minimum frame rate, frames are guaranteed to map to different * slots (an MSB of 17 which is 2^(17-8+1) = 1024 slots guarantees no collisions * down to a rate of 0.7 fps). */ #define FRAME_INFO_MAX2 (8) // 2^8 = 256; 90000/256 = 352 frames/sec #define FRAME_INFO_MIN2 (17) // 2^17 = 128K; 90000/131072 = 0.7 frames/sec #define FRAME_INFO_SIZE (1 << (FRAME_INFO_MIN2 - FRAME_INFO_MAX2 + 1)) #define FRAME_INFO_MASK (FRAME_INFO_SIZE - 1) int encqsvInit (hb_work_object_t*, hb_job_t*); int encqsvWork (hb_work_object_t*, hb_buffer_t**, hb_buffer_t**); void encqsvClose(hb_work_object_t*); hb_work_object_t hb_encqsv = { WORK_ENCQSV, "H.264/AVC encoder (Intel QSV)", encqsvInit, encqsvWork, encqsvClose }; struct hb_work_private_s { hb_job_t *job; uint32_t frames_in; uint32_t frames_out; int64_t last_start; hb_qsv_param_t param; av_qsv_space enc_space; hb_qsv_info_t *qsv_info; hb_list_t *delayed_chapters; int64_t next_chapter_pts; #define BFRM_DELAY_MAX 16 uint32_t *init_delay; int bfrm_delay; int64_t init_pts[BFRM_DELAY_MAX + 1]; hb_list_t *list_dts; int64_t frame_duration[FRAME_INFO_SIZE]; int async_depth; int max_async_depth; // if encode-only, system memory used int is_sys_mem; mfxSession mfx_session; struct SwsContext *sws_context_to_nv12; // whether to expect input from VPP or from QSV decode int is_vpp_present; // whether the encoder is initialized int init_done; hb_list_t *delayed_processing; hb_list_t *encoded_frames; }; // used in delayed_chapters list struct chapter_s { int index; int64_t start; }; static void hb_qsv_add_new_dts(hb_list_t *list, int64_t new_dts) { if (list != NULL) { int64_t *item = malloc(sizeof(int64_t)); if (item != NULL) { *item = new_dts; hb_list_add(list, item); } } } static int64_t hb_qsv_pop_next_dts(hb_list_t *list) { int64_t next_dts = INT64_MIN; if (list != NULL && hb_list_count(list) > 0) { int64_t *item = hb_list_item(list, 0); if (item != NULL) { next_dts = *item; hb_list_rem(list, item); free(item); } } return next_dts; } static void save_frame_duration(hb_work_private_t *pv, hb_buffer_t *buf) { int i = (buf->s.start >> FRAME_INFO_MAX2) & FRAME_INFO_MASK; pv->frame_duration[i] = buf->s.stop - buf->s.start; } static int64_t get_frame_duration(hb_work_private_t *pv, hb_buffer_t *buf) { int i = (buf->s.start >> FRAME_INFO_MAX2) & FRAME_INFO_MASK; return pv->frame_duration[i]; } static const char* qsv_h264_profile_xlat(int profile) { switch (profile) { case MFX_PROFILE_AVC_CONSTRAINED_BASELINE: return "Constrained Baseline"; case MFX_PROFILE_AVC_BASELINE: return "Baseline"; case MFX_PROFILE_AVC_EXTENDED: return "Extended"; case MFX_PROFILE_AVC_MAIN: return "Main"; case MFX_PROFILE_AVC_CONSTRAINED_HIGH: return "Constrained High"; case MFX_PROFILE_AVC_PROGRESSIVE_HIGH: return "Progressive High"; case MFX_PROFILE_AVC_HIGH: return "High"; case MFX_PROFILE_UNKNOWN: default: return NULL; } } static const char* qsv_h264_level_xlat(int level) { int i; for (i = 0; hb_h264_level_names[i] != NULL; i++) { if (hb_h264_level_values[i] == level) { return hb_h264_level_names[i]; } } return NULL; } int qsv_enc_init(hb_work_private_t *pv) { av_qsv_context *qsv = pv->job->qsv.ctx; hb_job_t *job = pv->job; mfxStatus sts; int i; if (pv->init_done) { return 0; } if (qsv == NULL) { if (!pv->is_sys_mem) { hb_error("qsv_enc_init: decode enabled but no context!"); return 3; } job->qsv.ctx = qsv = av_mallocz(sizeof(av_qsv_context)); } av_qsv_space *qsv_encode = qsv->enc_space; if (qsv_encode == NULL) { // if only for encode if (pv->is_sys_mem) { // no need to use additional sync as encode only -> single thread av_qsv_add_context_usage(qsv, 0); // re-use the session from encqsvInit qsv->mfx_session = pv->mfx_session; } qsv->enc_space = qsv_encode = &pv->enc_space; } if (!pv->is_sys_mem) { if (!pv->is_vpp_present && job->list_filter != NULL) { for (i = 0; i < hb_list_count(job->list_filter); i++) { hb_filter_object_t *filter = hb_list_item(job->list_filter, i); if (filter->id == HB_FILTER_QSV_PRE || filter->id == HB_FILTER_QSV_POST || filter->id == HB_FILTER_QSV) { pv->is_vpp_present = 1; break; } } } if (pv->is_vpp_present) { if (qsv->vpp_space == NULL) { return 2; } for (i = 0; i < av_qsv_list_count(qsv->vpp_space); i++) { av_qsv_space *vpp = av_qsv_list_item(qsv->vpp_space, i); if (!vpp->is_init_done) { return 2; } } } av_qsv_space *dec_space = qsv->dec_space; if (dec_space == NULL || !dec_space->is_init_done) { return 2; } } else { pv->sws_context_to_nv12 = hb_sws_get_context(job->width, job->height, AV_PIX_FMT_YUV420P, job->width, job->height, AV_PIX_FMT_NV12, SWS_LANCZOS|SWS_ACCURATE_RND); } // allocate tasks qsv_encode->p_buf_max_size = AV_QSV_BUF_SIZE_DEFAULT; qsv_encode->tasks = av_qsv_list_init(HAVE_THREADS); for (i = 0; i < pv->max_async_depth; i++) { av_qsv_task *task = av_mallocz(sizeof(av_qsv_task)); task->bs = av_mallocz(sizeof(mfxBitstream)); task->bs->Data = av_mallocz(sizeof(uint8_t) * qsv_encode->p_buf_max_size); task->bs->MaxLength = qsv_encode->p_buf_max_size; task->bs->DataLength = 0; task->bs->DataOffset = 0; av_qsv_list_add(qsv_encode->tasks, task); } // setup surface allocation pv->param.videoParam->IOPattern = (pv->is_sys_mem ? MFX_IOPATTERN_IN_SYSTEM_MEMORY : MFX_IOPATTERN_IN_OPAQUE_MEMORY); memset(&qsv_encode->request, 0, sizeof(mfxFrameAllocRequest) * 2); sts = MFXVideoENCODE_QueryIOSurf(qsv->mfx_session, pv->param.videoParam, &qsv_encode->request[0]); if (sts < MFX_ERR_NONE) // ignore warnings { hb_error("qsv_enc_init: MFXVideoENCODE_QueryIOSurf failed (%d)", sts); *job->done_error = HB_ERROR_INIT; *job->die = 1; return -1; } // allocate surfaces if (pv->is_sys_mem) { qsv_encode->surface_num = FFMIN(qsv_encode->request[0].NumFrameSuggested + pv->max_async_depth, AV_QSV_SURFACE_NUM); if (qsv_encode->surface_num <= 0) { qsv_encode->surface_num = AV_QSV_SURFACE_NUM; } for (i = 0; i < qsv_encode->surface_num; i++) { mfxFrameSurface1 *surface = av_mallocz(sizeof(mfxFrameSurface1)); mfxFrameInfo info = pv->param.videoParam->mfx.FrameInfo; surface->Info = info; surface->Data.Pitch = info.Width; surface->Data.Y = av_mallocz(info.Width * info.Height); surface->Data.VU = av_mallocz(info.Width * info.Height / 2); qsv_encode->p_surfaces[i] = surface; } } else { av_qsv_space *in_space = qsv->dec_space; if (pv->is_vpp_present) { // we get our input from VPP instead in_space = av_qsv_list_item(qsv->vpp_space, av_qsv_list_count(qsv->vpp_space) - 1); } // introduced in API 1.3 memset(&qsv_encode->ext_opaque_alloc, 0, sizeof(mfxExtOpaqueSurfaceAlloc)); qsv_encode->ext_opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION; qsv_encode->ext_opaque_alloc.Header.BufferSz = sizeof(mfxExtOpaqueSurfaceAlloc); qsv_encode->ext_opaque_alloc.In.Surfaces = in_space->p_surfaces; qsv_encode->ext_opaque_alloc.In.NumSurface = in_space->surface_num; qsv_encode->ext_opaque_alloc.In.Type = qsv_encode->request[0].Type; pv->param.videoParam->ExtParam[pv->param.videoParam->NumExtParam++] = (mfxExtBuffer*)&qsv_encode->ext_opaque_alloc; } // allocate sync points qsv_encode->sync_num = (qsv_encode->surface_num ? FFMIN(qsv_encode->surface_num, AV_QSV_SYNC_NUM) : AV_QSV_SYNC_NUM); for (i = 0; i < qsv_encode->sync_num; i++) { qsv_encode->p_syncp[i] = av_mallocz(sizeof(av_qsv_sync)); AV_QSV_CHECK_POINTER(qsv_encode->p_syncp[i], MFX_ERR_MEMORY_ALLOC); qsv_encode->p_syncp[i]->p_sync = av_mallocz(sizeof(mfxSyncPoint)); AV_QSV_CHECK_POINTER(qsv_encode->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC); } // initialize the encoder sts = MFXVideoENCODE_Init(qsv->mfx_session, pv->param.videoParam); if (sts < MFX_ERR_NONE) // ignore warnings { hb_error("qsv_enc_init: MFXVideoENCODE_Init failed (%d)", sts); *job->done_error = HB_ERROR_INIT; *job->die = 1; return -1; } qsv_encode->is_init_done = 1; mfxIMPL impl; mfxVersion version; // log actual implementation details now that we know them if ((MFXQueryIMPL (qsv->mfx_session, &impl) == MFX_ERR_NONE) && (MFXQueryVersion(qsv->mfx_session, &version) == MFX_ERR_NONE)) { hb_log("qsv_enc_init: using '%s' implementation, API: %"PRIu16".%"PRIu16"", hb_qsv_impl_get_name(impl), version.Major, version.Minor); } else { hb_log("qsv_enc_init: MFXQueryIMPL/MFXQueryVersion failure"); } pv->init_done = 1; return 0; } /*********************************************************************** * encqsvInit *********************************************************************** * **********************************************************************/ int encqsvInit(hb_work_object_t *w, hb_job_t *job) { hb_work_private_t *pv = calloc(1, sizeof(hb_work_private_t)); w->private_data = pv; pv->job = job; pv->is_sys_mem = hb_qsv_decode_is_enabled(job) == 0; pv->qsv_info = hb_qsv_info_get(job->vcodec); pv->delayed_processing = hb_list_init(); pv->encoded_frames = hb_list_init(); pv->last_start = INT64_MIN; pv->next_chapter_pts = AV_NOPTS_VALUE; pv->delayed_chapters = hb_list_init(); // default encoding parameters if (hb_qsv_param_default_preset(&pv->param, &pv->enc_space.m_mfxVideoParam, pv->qsv_info, job->encoder_preset)) { hb_error("encqsvInit: hb_qsv_param_default_preset failed"); return -1; } // set AsyncDepth to match that of decode and VPP pv->param.videoParam->AsyncDepth = job->qsv.async_depth; // enable and set colorimetry (video signal information) pv->param.videoSignalInfo.ColourDescriptionPresent = 1; switch (job->color_matrix_code) { case 4: // custom pv->param.videoSignalInfo.ColourPrimaries = job->color_prim; pv->param.videoSignalInfo.TransferCharacteristics = job->color_transfer; pv->param.videoSignalInfo.MatrixCoefficients = job->color_matrix; break; case 3: // ITU BT.709 HD content pv->param.videoSignalInfo.ColourPrimaries = HB_COLR_PRI_BT709; pv->param.videoSignalInfo.TransferCharacteristics = HB_COLR_TRA_BT709; pv->param.videoSignalInfo.MatrixCoefficients = HB_COLR_MAT_BT709; break; case 2: // ITU BT.601 DVD or SD TV content (PAL) pv->param.videoSignalInfo.ColourPrimaries = HB_COLR_PRI_EBUTECH; pv->param.videoSignalInfo.TransferCharacteristics = HB_COLR_TRA_BT709; pv->param.videoSignalInfo.MatrixCoefficients = HB_COLR_MAT_SMPTE170M; break; case 1: // ITU BT.601 DVD or SD TV content (NTSC) pv->param.videoSignalInfo.ColourPrimaries = HB_COLR_PRI_SMPTEC; pv->param.videoSignalInfo.TransferCharacteristics = HB_COLR_TRA_BT709; pv->param.videoSignalInfo.MatrixCoefficients = HB_COLR_MAT_SMPTE170M; break; default: // detected during scan pv->param.videoSignalInfo.ColourPrimaries = job->title->color_prim; pv->param.videoSignalInfo.TransferCharacteristics = job->title->color_transfer; pv->param.videoSignalInfo.MatrixCoefficients = job->title->color_matrix; break; } // parse user-specified encoder options, if present if (job->encoder_options != NULL && *job->encoder_options) { hb_dict_t *options_list; hb_dict_entry_t *option = NULL; options_list = hb_encopts_to_dict(job->encoder_options, job->vcodec); while ((option = hb_dict_next(options_list, option)) != NULL) { switch (hb_qsv_param_parse(&pv->param, pv->qsv_info, option->key, option->value)) { case HB_QSV_PARAM_OK: break; case HB_QSV_PARAM_BAD_NAME: hb_log("encqsvInit: hb_qsv_param_parse: bad key %s", option->key); break; case HB_QSV_PARAM_BAD_VALUE: hb_log("encqsvInit: hb_qsv_param_parse: bad value %s for key %s", option->value, option->key); break; case HB_QSV_PARAM_UNSUPPORTED: hb_log("encqsvInit: hb_qsv_param_parse: unsupported option %s", option->key); break; case HB_QSV_PARAM_ERROR: default: hb_log("encqsvInit: hb_qsv_param_parse: unknown error"); break; } } hb_dict_free(&options_list); } // reload colorimetry in case values were set in encoder_options if (pv->param.videoSignalInfo.ColourDescriptionPresent) { job->color_matrix_code = 4; job->color_prim = pv->param.videoSignalInfo.ColourPrimaries; job->color_transfer = pv->param.videoSignalInfo.TransferCharacteristics; job->color_matrix = pv->param.videoSignalInfo.MatrixCoefficients; } else { job->color_matrix_code = 0; job->color_prim = HB_COLR_PRI_UNDEF; job->color_transfer = HB_COLR_TRA_UNDEF; job->color_matrix = HB_COLR_MAT_UNDEF; } // sanitize values that may exceed the Media SDK variable size int64_t vrate, vrate_base; int64_t par_width, par_height; hb_limit_rational64(&vrate, &vrate_base, job->vrate, job->vrate_base, UINT32_MAX); hb_limit_rational64(&par_width, &par_height, job->anamorphic.par_width, job->anamorphic.par_height, UINT16_MAX); // some encoding parameters are used by filters to configure their output if (pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE) { job->qsv.enc_info.align_height = AV_QSV_ALIGN32(job->height); } else { job->qsv.enc_info.align_height = AV_QSV_ALIGN16(job->height); } job->qsv.enc_info.align_width = AV_QSV_ALIGN16(job->width); job->qsv.enc_info.pic_struct = pv->param.videoParam->mfx.FrameInfo.PicStruct; job->qsv.enc_info.is_init_done = 1; // encode to H.264 and set FrameInfo pv->param.videoParam->mfx.CodecId = MFX_CODEC_AVC; pv->param.videoParam->mfx.CodecLevel = MFX_LEVEL_UNKNOWN; pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_UNKNOWN; pv->param.videoParam->mfx.FrameInfo.FourCC = MFX_FOURCC_NV12; pv->param.videoParam->mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420; pv->param.videoParam->mfx.FrameInfo.FrameRateExtN = vrate; pv->param.videoParam->mfx.FrameInfo.FrameRateExtD = vrate_base; pv->param.videoParam->mfx.FrameInfo.AspectRatioW = par_width; pv->param.videoParam->mfx.FrameInfo.AspectRatioH = par_height; pv->param.videoParam->mfx.FrameInfo.CropX = 0; pv->param.videoParam->mfx.FrameInfo.CropY = 0; pv->param.videoParam->mfx.FrameInfo.CropW = job->width; pv->param.videoParam->mfx.FrameInfo.CropH = job->height; pv->param.videoParam->mfx.FrameInfo.PicStruct = job->qsv.enc_info.pic_struct; pv->param.videoParam->mfx.FrameInfo.Width = job->qsv.enc_info.align_width; pv->param.videoParam->mfx.FrameInfo.Height = job->qsv.enc_info.align_height; // set H.264 profile and level if (job->encoder_profile != NULL && *job->encoder_profile && strcasecmp(job->encoder_profile, "auto")) { if (!strcasecmp(job->encoder_profile, "baseline")) { pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_AVC_BASELINE; } else if (!strcasecmp(job->encoder_profile, "main")) { pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_AVC_MAIN; } else if (!strcasecmp(job->encoder_profile, "high")) { pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_AVC_HIGH; } else { hb_error("encqsvInit: bad profile %s", job->encoder_profile); return -1; } } if (job->encoder_level != NULL && *job->encoder_level && strcasecmp(job->encoder_level, "auto")) { int err; int i = hb_qsv_atoindex(hb_h264_level_names, job->encoder_level, &err); if (err || i >= (sizeof(hb_h264_level_values) / sizeof(hb_h264_level_values[0]))) { hb_error("encqsvInit: bad level %s", job->encoder_level); return -1; } else if (pv->qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6) { pv->param.videoParam->mfx.CodecLevel = HB_QSV_CLIP3(MFX_LEVEL_AVC_1, MFX_LEVEL_AVC_52, hb_h264_level_values[i]); } else { // Media SDK API < 1.6, MFX_LEVEL_AVC_52 unsupported pv->param.videoParam->mfx.CodecLevel = HB_QSV_CLIP3(MFX_LEVEL_AVC_1, MFX_LEVEL_AVC_51, hb_h264_level_values[i]); } } // interlaced encoding is not always possible if (pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE) { if (pv->param.videoParam->mfx.CodecProfile == MFX_PROFILE_AVC_CONSTRAINED_BASELINE || pv->param.videoParam->mfx.CodecProfile == MFX_PROFILE_AVC_BASELINE || pv->param.videoParam->mfx.CodecProfile == MFX_PROFILE_AVC_PROGRESSIVE_HIGH) { hb_error("encqsvInit: profile %s doesn't support interlaced encoding", qsv_h264_profile_xlat(pv->param.videoParam->mfx.CodecProfile)); return -1; } if ((pv->param.videoParam->mfx.CodecLevel >= MFX_LEVEL_AVC_1b && pv->param.videoParam->mfx.CodecLevel <= MFX_LEVEL_AVC_2) || (pv->param.videoParam->mfx.CodecLevel >= MFX_LEVEL_AVC_42)) { hb_error("encqsvInit: level %s doesn't support interlaced encoding", qsv_h264_level_xlat(pv->param.videoParam->mfx.CodecLevel)); return -1; } } // sanitize ICQ if (!(pv->qsv_info->capabilities & HB_QSV_CAP_RATECONTROL_ICQ)) { // ICQ not supported pv->param.rc.icq = 0; } else { pv->param.rc.icq = !!pv->param.rc.icq; } // sanitize lookahead if (!(pv->qsv_info->capabilities & HB_QSV_CAP_RATECONTROL_LA)) { // lookahead not supported pv->param.rc.lookahead = 0; } else if ((pv->param.rc.lookahead) && (pv->qsv_info->capabilities & HB_QSV_CAP_RATECONTROL_LAi) == 0 && (pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE)) { // lookahead enabled but we can't use it hb_log("encqsvInit: LookAhead not used (LookAhead is progressive-only)"); pv->param.rc.lookahead = 0; } else { pv->param.rc.lookahead = !!pv->param.rc.lookahead; } // set VBV here (this will be overridden for CQP and ignored for LA) // only set BufferSizeInKB, InitialDelayInKB and MaxKbps if we have // them - otheriwse Media SDK will pick values for us automatically if (pv->param.rc.vbv_buffer_size > 0) { if (pv->param.rc.vbv_buffer_init > 1.0) { pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_init / 8); } else if (pv->param.rc.vbv_buffer_init > 0.0) { pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_size * pv->param.rc.vbv_buffer_init / 8); } pv->param.videoParam->mfx.BufferSizeInKB = (pv->param.rc.vbv_buffer_size / 8); } if (pv->param.rc.vbv_max_bitrate > 0) { pv->param.videoParam->mfx.MaxKbps = pv->param.rc.vbv_max_bitrate; } // set rate control paremeters if (job->vquality >= 0) { if (pv->param.rc.icq) { // introduced in API 1.8 if (pv->param.rc.lookahead) { pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_LA_ICQ; } else { pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_ICQ; } pv->param.videoParam->mfx.ICQQuality = HB_QSV_CLIP3(1, 51, job->vquality); } else { // introduced in API 1.1 pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_CQP; pv->param.videoParam->mfx.QPI = HB_QSV_CLIP3(0, 51, job->vquality + pv->param.rc.cqp_offsets[0]); pv->param.videoParam->mfx.QPP = HB_QSV_CLIP3(0, 51, job->vquality + pv->param.rc.cqp_offsets[1]); pv->param.videoParam->mfx.QPB = HB_QSV_CLIP3(0, 51, job->vquality + pv->param.rc.cqp_offsets[2]); // CQP + ExtBRC can cause bad output pv->param.codingOption2.ExtBRC = MFX_CODINGOPTION_OFF; } } else if (job->vbitrate > 0) { if (pv->param.rc.lookahead) { // introduced in API 1.7 pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_LA; pv->param.videoParam->mfx.TargetKbps = job->vbitrate; // ignored, but some drivers will change AsyncDepth because of it pv->param.codingOption2.ExtBRC = MFX_CODINGOPTION_OFF; } else { // introduced in API 1.0 if (job->vbitrate == pv->param.rc.vbv_max_bitrate) { pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_CBR; } else { pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_VBR; } pv->param.videoParam->mfx.TargetKbps = job->vbitrate; } } else { hb_error("encqsvInit: invalid rate control (%d, %d)", job->vquality, job->vbitrate); return -1; } // if VBV is enabled but ignored, log it if (pv->param.rc.vbv_max_bitrate > 0 || pv->param.rc.vbv_buffer_size > 0) { switch (pv->param.videoParam->mfx.RateControlMethod) { case MFX_RATECONTROL_LA: case MFX_RATECONTROL_LA_ICQ: hb_log("encqsvInit: LookAhead enabled, ignoring VBV"); break; case MFX_RATECONTROL_ICQ: hb_log("encqsvInit: ICQ rate control, ignoring VBV"); break; default: break; } } // set B-pyramid if (pv->param.gop.b_pyramid < 0) { if (pv->param.videoParam->mfx.RateControlMethod == MFX_RATECONTROL_CQP) { pv->param.gop.b_pyramid = 1; } else { pv->param.gop.b_pyramid = 0; } } pv->param.gop.b_pyramid = !!pv->param.gop.b_pyramid; // set the GOP structure if (pv->param.gop.gop_ref_dist < 0) { if (pv->param.videoParam->mfx.RateControlMethod == MFX_RATECONTROL_CQP) { pv->param.gop.gop_ref_dist = 4; } else { pv->param.gop.gop_ref_dist = 3; } } pv->param.videoParam->mfx.GopRefDist = pv->param.gop.gop_ref_dist; // set the keyframe interval if (pv->param.gop.gop_pic_size < 0) { int rate = (int)((double)job->vrate / (double)job->vrate_base + 0.5); if (pv->param.videoParam->mfx.RateControlMethod == MFX_RATECONTROL_CQP) { // ensure B-pyramid is enabled for CQP on Haswell pv->param.gop.gop_pic_size = 32; } else { // set the keyframe interval based on the framerate pv->param.gop.gop_pic_size = rate; } } pv->param.videoParam->mfx.GopPicSize = pv->param.gop.gop_pic_size; // sanitize some settings that affect memory consumption if (!pv->is_sys_mem) { // limit these to avoid running out of resources (causes hang) pv->param.videoParam->mfx.GopRefDist = FFMIN(pv->param.videoParam->mfx.GopRefDist, pv->param.rc.lookahead ? 8 : 16); pv->param.codingOption2.LookAheadDepth = FFMIN(pv->param.codingOption2.LookAheadDepth, pv->param.rc.lookahead ? (48 - pv->param.videoParam->mfx.GopRefDist - 3 * !pv->param.videoParam->mfx.GopRefDist) : 0); } else { // encode-only is a bit less sensitive to memory issues pv->param.videoParam->mfx.GopRefDist = FFMIN(pv->param.videoParam->mfx.GopRefDist, 16); pv->param.codingOption2.LookAheadDepth = FFMIN(pv->param.codingOption2.LookAheadDepth, pv->param.rc.lookahead ? 60 : 0); } if ((pv->qsv_info->capabilities & HB_QSV_CAP_B_REF_PYRAMID) && (pv->param.videoParam->mfx.CodecProfile != MFX_PROFILE_AVC_BASELINE && pv->param.videoParam->mfx.CodecProfile != MFX_PROFILE_AVC_CONSTRAINED_HIGH && pv->param.videoParam->mfx.CodecProfile != MFX_PROFILE_AVC_CONSTRAINED_BASELINE)) { int gop_ref_dist = 4; /* * B-pyramid is supported. * * Set gop_ref_dist to a power of two, >= 4 and <= GopRefDist to ensure * Media SDK will not disable B-pyramid if we end up using it below. */ while (pv->param.videoParam->mfx.GopRefDist >= gop_ref_dist * 2) { gop_ref_dist *= 2; } if ((pv->param.gop.b_pyramid) && (pv->param.videoParam->mfx.GopPicSize == 0 || pv->param.videoParam->mfx.GopPicSize > gop_ref_dist)) { /* * B-pyramid enabled and GopPicSize is long enough for gop_ref_dist. * * Use gop_ref_dist. GopPicSize must be a multiple of GopRefDist. * NumRefFrame should be >= (GopRefDist / 2) and >= 3, otherwise * Media SDK may sometimes decide to disable B-pyramid too (whereas * sometimes it will just sanitize NumrefFrame instead). * * Notes: Media SDK handles the NumRefFrame == 0 case for us. * Also, GopPicSize == 0 should always result in a value that * does NOT cause Media SDK to disable B-pyramid, so it's OK. */ pv->param.videoParam->mfx.GopRefDist = gop_ref_dist; pv->param.videoParam->mfx.GopPicSize = (pv->param.videoParam->mfx.GopPicSize / pv->param.videoParam->mfx.GopRefDist * pv->param.videoParam->mfx.GopRefDist); if (pv->param.videoParam->mfx.NumRefFrame) { pv->param.videoParam->mfx.NumRefFrame = FFMAX(pv->param.videoParam->mfx.NumRefFrame, pv->param.videoParam->mfx.GopRefDist / 2); pv->param.videoParam->mfx.NumRefFrame = FFMAX(pv->param.videoParam->mfx.NumRefFrame, 3); } } else { /* * B-pyramid disabled or not possible (GopPicSize too short). * Sanitize gop.b_pyramid to 0 (off/disabled). */ pv->param.gop.b_pyramid = 0; /* Then, adjust settings to actually disable it. */ if (pv->param.videoParam->mfx.GopRefDist == 0) { /* * GopRefDist == 0 means the value will be set by Media SDK. * Since we can't be sure what the actual value would be, we * have to make sure that GopRefDist is set explicitly. */ pv->param.videoParam->mfx.GopRefDist = gop_ref_dist - 1; } else if (pv->param.videoParam->mfx.GopRefDist == gop_ref_dist) { /* GopRefDist is compatible with Media SDK's B-pyramid. */ if (pv->param.videoParam->mfx.GopPicSize == 0) { /* * GopPicSize is unknown and could be a multiple of * GopRefDist. Decrement the latter to disable B-pyramid. */ pv->param.videoParam->mfx.GopRefDist--; } else if (pv->param.videoParam->mfx.GopPicSize % pv->param.videoParam->mfx.GopRefDist == 0) { /* * GopPicSize is a multiple of GopRefDist. * Increment the former to disable B-pyramid. */ pv->param.videoParam->mfx.GopPicSize++; } } } } else { /* B-pyramid not supported. */ pv->param.gop.b_pyramid = 0; } /* * init a dummy encode-only session to get the SPS/PPS * and the final output settings sanitized by Media SDK * this is fine since the actual encode will use the same * values for all parameters relevant to the H.264 bitstream */ mfxStatus err; mfxVersion version; mfxVideoParam videoParam; mfxExtBuffer *extParamArray[3]; mfxSession session = (mfxSession)0; mfxExtCodingOption option1_buf, *option1 = &option1_buf; mfxExtCodingOption2 option2_buf, *option2 = &option2_buf; mfxExtCodingOptionSPSPPS sps_pps_buf, *sps_pps = &sps_pps_buf; version.Major = HB_QSV_MINVERSION_MAJOR; version.Minor = HB_QSV_MINVERSION_MINOR; err = MFXInit(pv->qsv_info->implementation, &version, &session); if (err != MFX_ERR_NONE) { hb_error("encqsvInit: MFXInit failed (%d)", err); return -1; } err = MFXVideoENCODE_Init(session, pv->param.videoParam); // workaround for the early 15.33.x driver, should be removed later #define HB_DRIVER_FIX_33 #ifdef HB_DRIVER_FIX_33 int la_workaround = 0; if (err < MFX_ERR_NONE && pv->param.videoParam->mfx.RateControlMethod == MFX_RATECONTROL_LA) { pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_CBR; err = MFXVideoENCODE_Init(session, pv->param.videoParam); la_workaround = 1; } #endif if (err < MFX_ERR_NONE) // ignore warnings { hb_error("encqsvInit: MFXVideoENCODE_Init failed (%d)", err); MFXClose(session); return -1; } memset(&videoParam, 0, sizeof(mfxVideoParam)); videoParam.ExtParam = extParamArray; videoParam.NumExtParam = 0; // introduced in API 1.3 memset(sps_pps, 0, sizeof(mfxExtCodingOptionSPSPPS)); sps_pps->Header.BufferId = MFX_EXTBUFF_CODING_OPTION_SPSPPS; sps_pps->Header.BufferSz = sizeof(mfxExtCodingOptionSPSPPS); sps_pps->SPSId = 0; sps_pps->SPSBuffer = w->config->h264.sps; sps_pps->SPSBufSize = sizeof(w->config->h264.sps); sps_pps->PPSId = 0; sps_pps->PPSBuffer = w->config->h264.pps; sps_pps->PPSBufSize = sizeof(w->config->h264.pps); videoParam.ExtParam[videoParam.NumExtParam++] = (mfxExtBuffer*)sps_pps; // introduced in API 1.0 memset(option1, 0, sizeof(mfxExtCodingOption)); option1->Header.BufferId = MFX_EXTBUFF_CODING_OPTION; option1->Header.BufferSz = sizeof(mfxExtCodingOption); videoParam.ExtParam[videoParam.NumExtParam++] = (mfxExtBuffer*)option1; // introduced in API 1.6 memset(option2, 0, sizeof(mfxExtCodingOption2)); option2->Header.BufferId = MFX_EXTBUFF_CODING_OPTION2; option2->Header.BufferSz = sizeof(mfxExtCodingOption2); if (pv->qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6) { // attach to get the final output mfxExtCodingOption2 settings videoParam.ExtParam[videoParam.NumExtParam++] = (mfxExtBuffer*)option2; } err = MFXVideoENCODE_GetVideoParam(session, &videoParam); MFXVideoENCODE_Close(session); if (err == MFX_ERR_NONE) { // remove 32-bit NAL prefix (0x00 0x00 0x00 0x01) w->config->h264.sps_length = sps_pps->SPSBufSize - 4; memmove(w->config->h264.sps, w->config->h264.sps + 4, w->config->h264.sps_length); w->config->h264.pps_length = sps_pps->PPSBufSize - 4; memmove(w->config->h264.pps, w->config->h264.pps + 4, w->config->h264.pps_length); } else { hb_error("encqsvInit: MFXVideoENCODE_GetVideoParam failed (%d)", err); MFXClose(session); return -1; } #ifdef HB_DRIVER_FIX_33 if (la_workaround) { videoParam.mfx.RateControlMethod = pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_LA; option2->LookAheadDepth = pv->param.codingOption2.LookAheadDepth; hb_log("encqsvInit: using LookAhead workaround (\"early 33 fix\")"); } #endif // when using system memory, we re-use this same session if (pv->is_sys_mem) { pv->mfx_session = session; } else { MFXClose(session); } /* check whether B-frames are used */ int bframes = videoParam.mfx.GopRefDist > 1 && videoParam.mfx.GopPicSize > 2; if (bframes) { /* the muxer needs to know to the init_delay */ switch (pv->qsv_info->codec_id) { case MFX_CODEC_AVC: pv->init_delay = &w->config->h264.init_delay; break; default: // unreachable break; } /* let the muxer know that it should expect B-frames */ job->areBframes = 1; /* holds the PTS sequence in display order, used to generate DTS */ pv->list_dts = hb_list_init(); } // log code path and main output settings hb_log("encqsvInit: using %s path", pv->is_sys_mem ? "encode-only" : "full QSV"); hb_log("encqsvInit: TargetUsage %"PRIu16" AsyncDepth %"PRIu16"", videoParam.mfx.TargetUsage, videoParam.AsyncDepth); hb_log("encqsvInit: GopRefDist %"PRIu16" GopPicSize %"PRIu16" NumRefFrame %"PRIu16"", videoParam.mfx.GopRefDist, videoParam.mfx.GopPicSize, videoParam.mfx.NumRefFrame); if (pv->qsv_info->capabilities & HB_QSV_CAP_B_REF_PYRAMID) { hb_log("encqsvInit: BFrames %s BPyramid %s", bframes ? "on" : "off", bframes && pv->param.gop.b_pyramid ? "on" : "off"); } else { hb_log("encqsvInit: BFrames %s", bframes ? "on" : "off"); } if (pv->qsv_info->capabilities & HB_QSV_CAP_OPTION2_IB_ADAPT) { if (bframes) { hb_log("encqsvInit: AdaptiveI %s AdaptiveB %s", hb_qsv_codingoption_get_name(option2->AdaptiveI), hb_qsv_codingoption_get_name(option2->AdaptiveB)); } else { hb_log("encqsvInit: AdaptiveI %s", hb_qsv_codingoption_get_name(option2->AdaptiveI)); } } if (videoParam.mfx.RateControlMethod == MFX_RATECONTROL_CQP) { char qpi[7], qpp[9], qpb[9]; snprintf(qpi, sizeof(qpi), "QPI %"PRIu16"", videoParam.mfx.QPI); snprintf(qpp, sizeof(qpp), " QPP %"PRIu16"", videoParam.mfx.QPP); snprintf(qpb, sizeof(qpb), " QPB %"PRIu16"", videoParam.mfx.QPB); hb_log("encqsvInit: RateControlMethod CQP with %s%s%s", qpi, videoParam.mfx.GopPicSize > 1 ? qpp : "", videoParam.mfx.GopRefDist > 1 ? qpb : ""); } else { switch (videoParam.mfx.RateControlMethod) { case MFX_RATECONTROL_LA: hb_log("encqsvInit: RateControlMethod LA TargetKbps %"PRIu16" LookAheadDepth %"PRIu16"", videoParam.mfx.TargetKbps, option2->LookAheadDepth); break; case MFX_RATECONTROL_LA_ICQ: hb_log("encqsvInit: RateControlMethod LA_ICQ ICQQuality %"PRIu16" LookAheadDepth %"PRIu16"", videoParam.mfx.ICQQuality, option2->LookAheadDepth); break; case MFX_RATECONTROL_ICQ: hb_log("encqsvInit: RateControlMethod ICQ ICQQuality %"PRIu16"", videoParam.mfx.ICQQuality); break; case MFX_RATECONTROL_CBR: case MFX_RATECONTROL_VBR: hb_log("encqsvInit: RateControlMethod %s TargetKbps %"PRIu16" MaxKbps %"PRIu16" BufferSizeInKB %"PRIu16" InitialDelayInKB %"PRIu16"", videoParam.mfx.RateControlMethod == MFX_RATECONTROL_CBR ? "CBR" : "VBR", videoParam.mfx.TargetKbps, videoParam.mfx.MaxKbps, videoParam.mfx.BufferSizeInKB, videoParam.mfx.InitialDelayInKB); break; default: hb_log("encqsvInit: invalid rate control method %"PRIu16"", videoParam.mfx.RateControlMethod); return -1; } } if ((pv->qsv_info->capabilities & HB_QSV_CAP_OPTION2_LA_DOWNS) && (videoParam.mfx.RateControlMethod == MFX_RATECONTROL_LA || videoParam.mfx.RateControlMethod == MFX_RATECONTROL_LA_ICQ)) { switch (option2->LookAheadDS) { case MFX_LOOKAHEAD_DS_UNKNOWN: hb_log("encqsvInit: LookAheadDS unknown (auto)"); break; case MFX_LOOKAHEAD_DS_OFF: hb_log("encqsvInit: LookAheadDS off"); break; case MFX_LOOKAHEAD_DS_2x: hb_log("encqsvInit: LookAheadDS 2x"); break; case MFX_LOOKAHEAD_DS_4x: hb_log("encqsvInit: LookAheadDS 4x"); break; default: hb_log("encqsvInit: invalid LookAheadDS value 0x%"PRIx16"", option2->LookAheadDS); break; } } switch (videoParam.mfx.FrameInfo.PicStruct) { // quiet, most people don't care case MFX_PICSTRUCT_PROGRESSIVE: break; // interlaced encoding is intended for advanced users only, who do care case MFX_PICSTRUCT_FIELD_TFF: hb_log("encqsvInit: PicStruct top field first"); break; case MFX_PICSTRUCT_FIELD_BFF: hb_log("encqsvInit: PicStruct bottom field first"); break; default: hb_error("encqsvInit: invalid PicStruct value 0x%"PRIx16"", videoParam.mfx.FrameInfo.PicStruct); return -1; } hb_log("encqsvInit: CAVLC %s", hb_qsv_codingoption_get_name(option1->CAVLC)); if (pv->param.rc.lookahead == 0 && videoParam.mfx.RateControlMethod != MFX_RATECONTROL_CQP) { // LA/CQP and ExtBRC/MBBRC are mutually exclusive if (pv->qsv_info->capabilities & HB_QSV_CAP_OPTION2_EXTBRC) { hb_log("encqsvInit: ExtBRC %s", hb_qsv_codingoption_get_name(option2->ExtBRC)); } if (pv->qsv_info->capabilities & HB_QSV_CAP_OPTION2_MBBRC) { hb_log("encqsvInit: MBBRC %s", hb_qsv_codingoption_get_name(option2->MBBRC)); } } if (pv->qsv_info->capabilities & HB_QSV_CAP_OPTION2_TRELLIS) { switch (option2->Trellis) { case MFX_TRELLIS_OFF: hb_log("encqsvInit: Trellis off"); break; case MFX_TRELLIS_UNKNOWN: hb_log("encqsvInit: Trellis unknown (auto)"); break; default: hb_log("encqsvInit: Trellis on (%s%s%s)", (option2->Trellis & MFX_TRELLIS_I) ? "I" : "", (option2->Trellis & MFX_TRELLIS_P) && (videoParam.mfx.GopPicSize > 1) ? "P" : "", (option2->Trellis & MFX_TRELLIS_B) && (videoParam.mfx.GopRefDist > 1) ? "B" : ""); break; } } hb_log("encqsvInit: H.264 profile %s @ level %s", qsv_h264_profile_xlat(videoParam.mfx.CodecProfile), qsv_h264_level_xlat (videoParam.mfx.CodecLevel)); // AsyncDepth has now been set and/or modified by Media SDK pv->max_async_depth = videoParam.AsyncDepth; pv->async_depth = 0; return 0; } void encqsvClose(hb_work_object_t *w) { hb_work_private_t *pv = w->private_data; int i; if (pv != NULL && pv->job != NULL && pv->job->qsv.ctx != NULL && pv->job->qsv.ctx->is_context_active) { av_qsv_context *qsv_ctx = pv->job->qsv.ctx; av_qsv_space *qsv_enc_space = pv->job->qsv.ctx->enc_space; if (qsv_enc_space != NULL) { if (qsv_enc_space->is_init_done) { for (i = av_qsv_list_count(qsv_enc_space->tasks); i > 1; i--) { av_qsv_task *task = av_qsv_list_item(qsv_enc_space->tasks, i - 1); if (task != NULL) { if (task->bs != NULL) { av_freep(&task->bs->Data); } av_qsv_list_rem(qsv_enc_space->tasks, task); av_freep(&task->bs); av_freep(&task); } } av_qsv_list_close(&qsv_enc_space->tasks); for (i = 0; i < qsv_enc_space->surface_num; i++) { if (pv->is_sys_mem) { av_freep(&qsv_enc_space->p_surfaces[i]->Data.VU); av_freep(&qsv_enc_space->p_surfaces[i]->Data.Y); } av_freep(&qsv_enc_space->p_surfaces[i]); } qsv_enc_space->surface_num = 0; for (i = 0; i < qsv_enc_space->sync_num; i++) { av_freep(&qsv_enc_space->p_syncp[i]->p_sync); av_freep(&qsv_enc_space->p_syncp[i]); } qsv_enc_space->sync_num = 0; } qsv_enc_space->is_init_done = 0; } if (qsv_ctx != NULL) { /* QSV context cleanup and MFXClose */ av_qsv_context_clean(qsv_ctx); if (pv->is_sys_mem) { av_freep(&qsv_ctx); } } } if (pv != NULL) { if (pv->delayed_processing != NULL) { /* the list is already empty */ hb_list_close(&pv->delayed_processing); } if (pv->sws_context_to_nv12 != NULL) { sws_freeContext(pv->sws_context_to_nv12); } if (pv->list_dts != NULL) { int64_t *item; while ((item = hb_list_item(pv->list_dts, 0)) != NULL) { hb_list_rem(pv->list_dts, item); free(item); } hb_list_close(&pv->list_dts); } if (pv->delayed_chapters != NULL) { struct chapter_s *item; while ((item = hb_list_item(pv->delayed_chapters, 0)) != NULL) { hb_list_rem(pv->delayed_chapters, item); free(item); } hb_list_close(&pv->delayed_chapters); } if (pv->encoded_frames != NULL) { hb_buffer_t *item; while ((item = hb_list_item(pv->encoded_frames, 0)) != NULL) { hb_list_rem(pv->encoded_frames, item); hb_buffer_close(&item); } hb_list_close(&pv->encoded_frames); } } free(pv); w->private_data = NULL; } static void save_chapter(hb_work_private_t *pv, hb_buffer_t *buf) { /* * Since there may be several frames buffered in the encoder, remember the * timestamp so when this frame finally pops out of the encoder we'll mark * its buffer as the start of a chapter. */ if (pv->next_chapter_pts == AV_NOPTS_VALUE) { pv->next_chapter_pts = buf->s.start; } /* * Chapter markers are sometimes so close we can get a new * one before the previous goes through the encoding queue. * * Dropping markers can cause weird side-effects downstream, * including but not limited to missing chapters in the * output, so we need to save it somehow. */ struct chapter_s *item = malloc(sizeof(struct chapter_s)); if (item != NULL) { item->start = buf->s.start; item->index = buf->s.new_chap; hb_list_add(pv->delayed_chapters, item); } /* don't let 'work_loop' put a chapter mark on the wrong buffer */ buf->s.new_chap = 0; } static void restore_chapter(hb_work_private_t *pv, hb_buffer_t *buf) { /* we're no longer looking for this chapter */ pv->next_chapter_pts = AV_NOPTS_VALUE; /* get the chapter index from the list */ struct chapter_s *item = hb_list_item(pv->delayed_chapters, 0); if (item != NULL) { /* we're done with this chapter */ hb_list_rem(pv->delayed_chapters, item); buf->s.new_chap = item->index; free(item); /* we may still have another pending chapter */ item = hb_list_item(pv->delayed_chapters, 0); if (item != NULL) { /* * we're looking for this chapter now * we still need it, don't remove it */ pv->next_chapter_pts = item->start; } } } static void compute_init_delay(hb_work_private_t *pv, mfxBitstream *bs) { if (pv->init_delay == NULL) { return; // not needed or already set } /* * In the MP4 container, DT(0) = STTS(0) = 0. * * Which gives us: * CT(0) = CTTS(0) + STTS(0) = CTTS(0) = PTS(0) - DTS(0) * When DTS(0) < PTS(0), we then have: * CT(0) > 0 for video, but not audio (breaks A/V sync). * * This is typically solved by writing an edit list shifting * video samples by the initial delay, PTS(0) - DTS(0). * * See: * ISO/IEC 14496-12:2008(E), ISO base media file format * - 8.6.1.2 Decoding Time to Sample Box */ if (pv->qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6) { /* compute init_delay (in ticks) based on the DTS provided by MSDK. */ int64_t init_delay = bs->TimeStamp - bs->DecodeTimeStamp; /* * we also need to know the delay in frames to generate DTS. * * compute it based on the init_delay and average frame duration, * and account for potential rounding errors due to the timebase. */ double avg_frame_dur = ((double)pv->job->vrate_base / (double)pv->job->vrate * 90000.); pv->bfrm_delay = (init_delay + (avg_frame_dur / 2)) / avg_frame_dur; if (pv->bfrm_delay < 1 || pv->bfrm_delay > BFRM_DELAY_MAX) { hb_log("compute_init_delay: " "invalid delay %d (PTS: %"PRIu64", DTS: %"PRId64")", pv->bfrm_delay, bs->TimeStamp, bs->DecodeTimeStamp); /* we have B-frames, the frame delay should be at least 1 */ if (pv->bfrm_delay < 1) { mfxStatus sts; mfxVideoParam videoParam; mfxSession session = pv->job->qsv.ctx->mfx_session; memset(&videoParam, 0, sizeof(mfxVideoParam)); sts = MFXVideoENCODE_GetVideoParam(session, &videoParam); if (sts != MFX_ERR_NONE) { hb_log("compute_init_delay: " "MFXVideoENCODE_GetVideoParam failed (%d)", sts); pv->bfrm_delay = 1; } else { /* usually too large, but should cover all cases */ pv->bfrm_delay = FFMIN(pv->frames_in - 1, videoParam.mfx.GopRefDist - 1); } } pv->bfrm_delay = FFMIN(BFRM_DELAY_MAX, pv->bfrm_delay); } pv->init_delay[0] = pv->init_pts[pv->bfrm_delay] - pv->init_pts[0]; } else { /* * we can't get the DTS from MSDK, so we need to generate our own. * * B-pyramid not possible here, so the delay in frames is always 1. */ pv->bfrm_delay = 1; pv->init_delay[0] = pv->init_pts[1] - pv->init_pts[0]; } /* The delay only needs to be set once. */ pv->init_delay = NULL; } static int qsv_frame_is_key(mfxU16 FrameType) { return ((FrameType & MFX_FRAMETYPE_IDR) || (FrameType == MFX_FRAMETYPE_UNKNOWN)); } static void qsv_bitstream_slurp(hb_work_private_t *pv, mfxBitstream *bs) { /* * we need to convert the encoder's Annex B output * to an MP4-compatible format (ISO/IEC 14496-15). */ hb_buffer_t *buf = hb_nal_bitstream_annexb_to_mp4(bs->Data + bs->DataOffset, bs->DataLength); if (buf == NULL) { hb_error("encqsv: hb_nal_bitstream_annexb_to_mp4 failed"); goto fail; } bs->DataLength = bs->DataOffset = 0; bs->MaxLength = pv->job->qsv.ctx->enc_space->p_buf_max_size; buf->s.frametype = hb_qsv_frametype_xlat(bs->FrameType, &buf->s.flags); buf->s.start = buf->s.renderOffset = bs->TimeStamp; buf->s.stop = buf->s.start + get_frame_duration(pv, buf); buf->s.duration = buf->s.stop - buf->s.start; /* compute the init_delay before setting the DTS */ compute_init_delay(pv, bs); /* * Generate VFR-compatible output DTS based on input PTS. * * Depends on the B-frame delay: * * 0: ipts0, ipts1, ipts2... * 1: ipts0 - ipts1, ipts1 - ipts1, ipts1, ipts2... * 2: ipts0 - ipts2, ipts1 - ipts2, ipts2 - ipts2, ipts1... * ...and so on. */ if (pv->bfrm_delay) { if (pv->frames_out <= pv->bfrm_delay) { buf->s.renderOffset = (pv->init_pts[pv->frames_out] - pv->init_pts[pv->bfrm_delay]); } else { buf->s.renderOffset = hb_qsv_pop_next_dts(pv->list_dts); } } /* check if B-pyramid is used even though it's disabled */ if ((pv->param.gop.b_pyramid == 0) && (bs->FrameType & MFX_FRAMETYPE_B) && (bs->FrameType & MFX_FRAMETYPE_REF)) { hb_log("encqsv: BPyramid off not respected (delay: %d)", pv->bfrm_delay); /* don't pollute the log unnecessarily */ pv->param.gop.b_pyramid = 1; } /* check for PTS < DTS */ if (buf->s.start < buf->s.renderOffset) { hb_log("encqsv: PTS %"PRId64" < DTS %"PRId64" for frame %d with type '%s'", buf->s.start, buf->s.renderOffset, pv->frames_out + 1, hb_qsv_frametype_name(bs->FrameType)); } /* * If we have a chapter marker pending and this frame's PTS * is at or after the marker's PTS, use it as the chapter start. */ if (pv->next_chapter_pts != AV_NOPTS_VALUE && pv->next_chapter_pts <= buf->s.start && qsv_frame_is_key(bs->FrameType)) { restore_chapter(pv, buf); } hb_list_add(pv->encoded_frames, buf); pv->frames_out++; return; fail: *pv->job->done_error = HB_ERROR_UNKNOWN; *pv->job->die = 1; } static int qsv_enc_work(hb_work_private_t *pv, av_qsv_list *qsv_atom, mfxFrameSurface1 *surface) { mfxStatus sts; av_qsv_context *qsv_ctx = pv->job->qsv.ctx; av_qsv_space *qsv_enc_space = pv->job->qsv.ctx->enc_space; do { int sync_idx = av_qsv_get_free_sync(qsv_enc_space, qsv_ctx); if (sync_idx == -1) { hb_error("encqsv: av_qsv_get_free_sync failed"); return -1; } av_qsv_task *task = av_qsv_list_item(qsv_enc_space->tasks, pv->async_depth); do { sts = MFXVideoENCODE_EncodeFrameAsync(qsv_ctx->mfx_session, NULL, surface, task->bs, qsv_enc_space->p_syncp[sync_idx]->p_sync); if (sts == MFX_ERR_MORE_DATA || (sts >= MFX_ERR_NONE && sts != MFX_WRN_DEVICE_BUSY)) { if (surface != NULL && !pv->is_sys_mem) { ff_qsv_atomic_dec(&surface->Data.Locked); } } if (sts == MFX_ERR_MORE_DATA) { if (qsv_atom != NULL) { hb_list_add(pv->delayed_processing, qsv_atom); } ff_qsv_atomic_dec(&qsv_enc_space->p_syncp[sync_idx]->in_use); break; } else if (sts < MFX_ERR_NONE) { hb_error("encqsv: MFXVideoENCODE_EncodeFrameAsync failed (%d)", sts); return -1; } else if (sts == MFX_WRN_DEVICE_BUSY) { av_qsv_sleep(10); // device is busy, wait then repeat the call continue; } else { av_qsv_stage *new_stage = av_qsv_stage_init(); new_stage->type = AV_QSV_ENCODE; new_stage->in.p_surface = surface; new_stage->out.sync = qsv_enc_space->p_syncp[sync_idx]; new_stage->out.p_bs = task->bs; task->stage = new_stage; pv->async_depth++; if (qsv_atom != NULL) { av_qsv_add_stagee(&qsv_atom, new_stage, HAVE_THREADS); } else { /* encode-only or flushing */ av_qsv_list *new_qsv_atom = av_qsv_list_init(HAVE_THREADS); av_qsv_add_stagee(&new_qsv_atom, new_stage, HAVE_THREADS); av_qsv_list_add (qsv_ctx->pipes, new_qsv_atom); } int i = hb_list_count(pv->delayed_processing); while (--i >= 0) { av_qsv_list *item = hb_list_item(pv->delayed_processing, i); if (item != NULL) { hb_list_rem(pv->delayed_processing, item); av_qsv_flush_stages(qsv_ctx->pipes, &item); } } break; } ff_qsv_atomic_dec(&qsv_enc_space->p_syncp[sync_idx]->in_use); break; } while (sts >= MFX_ERR_NONE); do { if (pv->async_depth == 0) break; /* we've done enough asynchronous operations or we're flushing */ if (pv->async_depth >= pv->max_async_depth || surface == NULL) { av_qsv_task *task = av_qsv_list_item(qsv_enc_space->tasks, 0); pv->async_depth--; /* perform a sync operation to get the output bitstream */ av_qsv_wait_on_sync(qsv_ctx, task->stage); if (task->bs->DataLength > 0) { av_qsv_list *pipe = av_qsv_pipe_by_stage(qsv_ctx->pipes, task->stage); av_qsv_flush_stages(qsv_ctx->pipes, &pipe); /* get the encoded frame from the bitstream */ qsv_bitstream_slurp(pv, task->bs); /* shift for fifo */ if (pv->async_depth) { av_qsv_list_rem(qsv_enc_space->tasks, task); av_qsv_list_add(qsv_enc_space->tasks, task); } task->stage = NULL; } } } while (surface == NULL); } while (surface == NULL && sts != MFX_ERR_MORE_DATA); return 0; } static hb_buffer_t* link_buffer_list(hb_list_t *list) { hb_buffer_t *buf, *prev = NULL, *out = NULL; while ((buf = hb_list_item(list, 0)) != NULL) { hb_list_rem(list, buf); if (prev == NULL) { prev = out = buf; } else { prev->next = buf; prev = buf; } } return out; } int encqsvWork(hb_work_object_t *w, hb_buffer_t **buf_in, hb_buffer_t **buf_out) { hb_work_private_t *pv = w->private_data; hb_buffer_t *in = *buf_in; hb_job_t *job = pv->job; while (qsv_enc_init(pv) >= 2) { av_qsv_sleep(1); // encoding not initialized, wait and repeat the call } if (*job->die) { goto fail; // unrecoverable error, don't attempt to encode } /* * EOF on input. Flush the decoder, then send the * EOF downstream to let the muxer know we're done. */ if (in->size <= 0) { qsv_enc_work(pv, NULL, NULL); hb_list_add(pv->encoded_frames, in); *buf_out = link_buffer_list(pv->encoded_frames); *buf_in = NULL; // don't let 'work_loop' close this buffer return HB_WORK_DONE; } mfxFrameSurface1 *surface = NULL; av_qsv_list *qsv_atom = NULL; av_qsv_context *qsv_ctx = job->qsv.ctx; av_qsv_space *qsv_enc_space = job->qsv.ctx->enc_space; if (pv->is_sys_mem) { mfxFrameInfo *info = &pv->param.videoParam->mfx.FrameInfo; int surface_index = av_qsv_get_free_surface(qsv_enc_space, qsv_ctx, info, QSV_PART_ANY); if (surface_index == -1) { hb_error("encqsv: av_qsv_get_free_surface failed"); goto fail; } surface = qsv_enc_space->p_surfaces[surface_index]; qsv_yuv420_to_nv12(pv->sws_context_to_nv12, surface, in); } else { qsv_atom = in->qsv_details.qsv_atom; surface = av_qsv_get_last_stage(qsv_atom)->out.p_surface; /* * QSV decoding fills the QSV context's dts_seq list, we need to * pop this surface's DTS so dts_seq doesn't grow unnecessarily. */ av_qsv_dts_pop(qsv_ctx); } /* * Debugging code to check that the upstream modules have generated * a continuous, self-consistent frame stream. */ if (pv->last_start > in->s.start) { hb_log("encqsv: input continuity error, " "last start %"PRId64" start %"PRId64"", pv->last_start, in->s.start); } pv->last_start = in->s.start; /* for DTS generation */ if (pv->frames_in <= BFRM_DELAY_MAX) { pv->init_pts[pv->frames_in] = in->s.start; } if (pv->frames_in) { hb_qsv_add_new_dts(pv->list_dts, in->s.start); } pv->frames_in++; /* * Chapters have to start with a keyframe, so request one here. * * Using an mfxEncodeCtrl structure to force key frame generation is not * possible when using a lookahead and frame reordering, so instead do * the following before encoding the frame attached to the chapter: * * - flush the encoder to encode and retrieve any buffered frames * * - do a hard reset (MFXVideoENCODE_Close, then Init) of * the encoder to make sure the next frame is a keyframe * * The hard reset ensures encoding resumes with a clean state, avoiding * miscellaneous hard-to-disagnose issues that may occur when resuming * an encode after flushing the encoder or using MFXVideoENCODE_Reset. */ if (in->s.new_chap > 0 && job->chapter_markers) { mfxStatus sts; if (qsv_enc_work(pv, NULL, NULL) < 0) { goto fail; } sts = MFXVideoENCODE_Close(qsv_ctx->mfx_session); if (sts != MFX_ERR_NONE) { hb_error("encqsv: MFXVideoENCODE_Close failed (%d)", sts); goto fail; } sts = MFXVideoENCODE_Init(qsv_ctx->mfx_session, pv->param.videoParam); if (sts < MFX_ERR_NONE) { hb_error("encqsv: MFXVideoENCODE_Init failed (%d)", sts); goto fail; } save_chapter(pv, in); } /* * If interlaced encoding is requested during encoder initialization, * but the input mfxFrameSurface1 is flagged as progressive here, * the output bitstream will be progressive (according to MediaInfo). * * Assume the user knows what he's doing (say he is e.g. encoding a * progressive-flagged source using interlaced compression - he may * well have a good reason to do so; mis-flagged sources do exist). */ surface->Info.PicStruct = pv->param.videoParam->mfx.FrameInfo.PicStruct; surface->Data.TimeStamp = in->s.start; save_frame_duration(pv, in); /* * Now that the input surface is setup, we can encode it. */ if (qsv_enc_work(pv, qsv_atom, surface) < 0) { goto fail; } *buf_out = link_buffer_list(pv->encoded_frames); return HB_WORK_OK; fail: if (*job->done_error == HB_ERROR_NONE) { *job->done_error = HB_ERROR_UNKNOWN; } *job->die = 1; *buf_out = NULL; return HB_WORK_ERROR; } #endif // USE_QSV HandBrake-0.10.2/libhb/audio_remap.c0000664000175200017520000002302112463330511017544 0ustar handbrakehandbrake/* audio_remap.c * * Copyright (c) 2003-2015 HandBrake Team * This file is part of the HandBrake source code * Homepage: * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "common.h" #include "hbffmpeg.h" #include "audio_remap.h" // source: libavutil/channel_layout.h hb_chan_map_t hb_libav_chan_map = { { AV_CH_FRONT_LEFT, AV_CH_FRONT_RIGHT, AV_CH_FRONT_CENTER, AV_CH_LOW_FREQUENCY, AV_CH_BACK_LEFT, AV_CH_BACK_RIGHT, AV_CH_FRONT_LEFT_OF_CENTER, AV_CH_FRONT_RIGHT_OF_CENTER, AV_CH_BACK_CENTER, AV_CH_SIDE_LEFT, AV_CH_SIDE_RIGHT, 0 } }; // source: liba52 documentation hb_chan_map_t hb_liba52_chan_map = { { AV_CH_LOW_FREQUENCY, AV_CH_FRONT_LEFT, AV_CH_FRONT_CENTER, AV_CH_FRONT_RIGHT, AV_CH_BACK_CENTER, AV_CH_SIDE_LEFT, AV_CH_SIDE_RIGHT, 0 } }; // source: http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9 hb_chan_map_t hb_vorbis_chan_map = { { AV_CH_FRONT_LEFT, AV_CH_FRONT_CENTER, AV_CH_FRONT_RIGHT, AV_CH_SIDE_LEFT, AV_CH_SIDE_RIGHT, AV_CH_BACK_LEFT, AV_CH_BACK_CENTER, AV_CH_BACK_RIGHT, AV_CH_LOW_FREQUENCY, 0 } }; // source: https://developer.apple.com/library/mac/#documentation/musicaudio/reference/CoreAudioDataTypesRef/Reference/reference.html hb_chan_map_t hb_aac_chan_map = { { AV_CH_FRONT_CENTER, AV_CH_FRONT_LEFT_OF_CENTER, AV_CH_FRONT_RIGHT_OF_CENTER, AV_CH_FRONT_LEFT, AV_CH_FRONT_RIGHT, AV_CH_SIDE_LEFT, AV_CH_SIDE_RIGHT, AV_CH_BACK_LEFT, AV_CH_BACK_RIGHT, AV_CH_BACK_CENTER, AV_CH_LOW_FREQUENCY, 0 } }; static void remap_planar(uint8_t **samples, int nsamples, int nchannels, int *remap_table) { int ii; uint8_t *tmp_buf[HB_AUDIO_REMAP_MAX_CHANNELS]; memcpy(tmp_buf, samples, nchannels * sizeof(uint8_t*)); for (ii = 0; ii < nchannels; ii++) { samples[ii] = tmp_buf[remap_table[ii]]; } } static void remap_u8_interleaved(uint8_t **samples, int nsamples, int nchannels, int *remap_table) { int ii, jj; uint8_t *samples_u8 = (*samples); uint8_t tmp_buf[HB_AUDIO_REMAP_MAX_CHANNELS]; for (ii = 0; ii < nsamples; ii++) { memcpy(tmp_buf, samples_u8, nchannels * sizeof(uint8_t)); for (jj = 0; jj < nchannels; jj++) { samples_u8[jj] = tmp_buf[remap_table[jj]]; } samples_u8 += nchannels; } } static void remap_s16_interleaved(uint8_t **samples, int nsamples, int nchannels, int *remap_table) { int ii, jj; int16_t *samples_s16 = (int16_t*)(*samples); int16_t tmp_buf[HB_AUDIO_REMAP_MAX_CHANNELS]; for (ii = 0; ii < nsamples; ii++) { memcpy(tmp_buf, samples_s16, nchannels * sizeof(int16_t)); for (jj = 0; jj < nchannels; jj++) { samples_s16[jj] = tmp_buf[remap_table[jj]]; } samples_s16 += nchannels; } } static void remap_s32_interleaved(uint8_t **samples, int nsamples, int nchannels, int *remap_table) { int ii, jj; int32_t *samples_s32 = (int32_t*)(*samples); int32_t tmp_buf[HB_AUDIO_REMAP_MAX_CHANNELS]; for (ii = 0; ii < nsamples; ii++) { memcpy(tmp_buf, samples_s32, nchannels * sizeof(int32_t)); for (jj = 0; jj < nchannels; jj++) { samples_s32[jj] = tmp_buf[remap_table[jj]]; } samples_s32 += nchannels; } } static void remap_flt_interleaved(uint8_t **samples, int nsamples, int nchannels, int *remap_table) { int ii, jj; float *samples_flt = (float*)(*samples); float tmp_buf[HB_AUDIO_REMAP_MAX_CHANNELS]; for (ii = 0; ii < nsamples; ii++) { memcpy(tmp_buf, samples_flt, nchannels * sizeof(float)); for (jj = 0; jj < nchannels; jj++) { samples_flt[jj] = tmp_buf[remap_table[jj]]; } samples_flt += nchannels; } } static void remap_dbl_interleaved(uint8_t **samples, int nsamples, int nchannels, int *remap_table) { int ii, jj; double *samples_dbl = (double*)(*samples); double tmp_buf[HB_AUDIO_REMAP_MAX_CHANNELS]; for (ii = 0; ii < nsamples; ii++) { memcpy(tmp_buf, samples_dbl, nchannels * sizeof(double)); for (jj = 0; jj < nchannels; jj++) { samples_dbl[jj] = tmp_buf[remap_table[jj]]; } samples_dbl += nchannels; } } hb_audio_remap_t* hb_audio_remap_init(enum AVSampleFormat sample_fmt, hb_chan_map_t *channel_map_out, hb_chan_map_t *channel_map_in) { hb_audio_remap_t *remap = calloc(1, sizeof(hb_audio_remap_t)); if (remap == NULL) { hb_error("hb_audio_remap_init: failed to allocate remap"); goto fail; } // sample format switch (sample_fmt) { case AV_SAMPLE_FMT_U8P: case AV_SAMPLE_FMT_S16P: case AV_SAMPLE_FMT_S32P: case AV_SAMPLE_FMT_FLTP: case AV_SAMPLE_FMT_DBLP: remap->remap = &remap_planar; break; case AV_SAMPLE_FMT_U8: remap->remap = &remap_u8_interleaved; break; case AV_SAMPLE_FMT_S16: remap->remap = &remap_s16_interleaved; break; case AV_SAMPLE_FMT_S32: remap->remap = &remap_s32_interleaved; break; case AV_SAMPLE_FMT_FLT: remap->remap = &remap_flt_interleaved; break; case AV_SAMPLE_FMT_DBL: remap->remap = &remap_dbl_interleaved; break; default: hb_error("hb_audio_remap_init: unsupported sample format '%s'", av_get_sample_fmt_name(sample_fmt)); goto fail; } // input/output channel order if (channel_map_in == NULL || channel_map_out == NULL) { hb_error("hb_audio_remap_init: invalid channel map(s)"); goto fail; } remap->channel_map_in = channel_map_in; remap->channel_map_out = channel_map_out; // remap can't be done until the channel layout has been set remap->remap_needed = 0; return remap; fail: hb_audio_remap_free(remap); return NULL; } void hb_audio_remap_set_channel_layout(hb_audio_remap_t *remap, uint64_t channel_layout) { if (remap != NULL) { int ii; remap->remap_needed = 0; // sanitize the layout if (channel_layout == AV_CH_LAYOUT_STEREO_DOWNMIX) { channel_layout = AV_CH_LAYOUT_STEREO; } remap->nchannels = av_get_channel_layout_nb_channels(channel_layout); // in some cases, remapping is not necessary and/or supported if (remap->nchannels > HB_AUDIO_REMAP_MAX_CHANNELS) { hb_log("hb_audio_remap_set_channel_layout: too many channels (%d)", remap->nchannels); return; } if (remap->channel_map_in == remap->channel_map_out) { return; } // build the table and check whether remapping is necessary hb_audio_remap_build_table(remap->channel_map_out, remap->channel_map_in, channel_layout, remap->table); for (ii = 0; ii < remap->nchannels; ii++) { if (remap->table[ii] != ii) { remap->remap_needed = 1; break; } } } } void hb_audio_remap_free(hb_audio_remap_t *remap) { if (remap != NULL) { free(remap); } } void hb_audio_remap(hb_audio_remap_t *remap, uint8_t **samples, int nsamples) { if (remap != NULL && remap->remap_needed) { remap->remap(samples, nsamples, remap->nchannels, remap->table); } } void hb_audio_remap_build_table(hb_chan_map_t *channel_map_out, hb_chan_map_t *channel_map_in, uint64_t channel_layout, int *remap_table) { int ii, jj, nchannels, out_chan_idx, remap_idx; uint64_t *channels_in, *channels_out; if (channel_layout == AV_CH_LAYOUT_STEREO_DOWNMIX) { // Dolby Surround is Stereo when it comes to remapping channel_layout = AV_CH_LAYOUT_STEREO; } nchannels = av_get_channel_layout_nb_channels(channel_layout); // clear remap table before (re-)building it memset(remap_table, 0, nchannels * sizeof(int)); out_chan_idx = 0; channels_in = channel_map_in ->channel_order_map; channels_out = channel_map_out->channel_order_map; for (ii = 0; channels_out[ii] && out_chan_idx < nchannels; ii++) { if (channel_layout & channels_out[ii]) { remap_idx = 0; for (jj = 0; channels_in[jj] && remap_idx < nchannels; jj++) { if (channels_out[ii] == channels_in[jj]) { remap_table[out_chan_idx++] = remap_idx++; break; } else if (channel_layout & channels_in[jj]) { remap_idx++; } } } } } HandBrake-0.10.2/libhb/param.c0000664000175200017520000002203112515471026016364 0ustar handbrakehandbrake/* param.c * * Copyright (c) 2003-2015 HandBrake Team * This file is part of the HandBrake source code * Homepage: . * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit * http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include /* NL-means presets and tunes * * Presets adjust strength: * ultralight - visually transparent * light * medium * strong * * Tunes adjust settings to the specified content type: * none * film - most content, live action * grain - like film but preserves luma grain * highmotion - like film but avoids color smearing with stronger settings * animation - cel animation such as cartoons, anime */ static char * generate_nlmeans_settings(const char *preset, const char *tune) { char *opt = NULL; if (preset == NULL) return NULL; if (!strcasecmp(preset, "ultralight") || !strcasecmp(preset, "light") || !strcasecmp(preset, "medium") || !strcasecmp(preset, "strong")) { double strength[2], origin_tune[2]; int patch_size[2], range[2], frames[2], prefilter[2]; if (tune == NULL || !strcasecmp(tune, "none")) { strength[0] = strength[1] = 6; origin_tune[0] = origin_tune[1] = 1; patch_size[0] = patch_size[1] = 7; range[0] = range[1] = 3; frames[0] = frames[1] = 2; prefilter[0] = prefilter[1] = 0; if (!strcasecmp(preset, "ultralight")) { strength[0] = strength[1] = 1.5; } else if (!strcasecmp(preset, "light")) { strength[0] = strength[1] = 3; } else if (!strcasecmp(preset, "strong")) { strength[0] = strength[1] = 10; } } else if (!strcasecmp(tune, "film")) { strength[0] = 6; strength[1] = 8; origin_tune[0] = origin_tune[1] = 0.8; patch_size[0] = patch_size[1] = 7; range[0] = range[1] = 3; frames[0] = frames[1] = 2; prefilter[0] = prefilter[1] = 0; if (!strcasecmp(preset, "ultralight")) { strength[0] = 1.5; strength[1] = 2.4; origin_tune[0] = 0.9; origin_tune[1] = 0.9; } else if (!strcasecmp(preset, "light")) { strength[0] = 3; strength[1] = 4; origin_tune[0] = 0.9; origin_tune[1] = 0.9; } else if (!strcasecmp(preset, "strong")) { strength[0] = 8; strength[1] = 10; origin_tune[0] = 0.6; origin_tune[1] = 0.6; } } else if (!strcasecmp(tune, "grain")) { strength[0] = 0; strength[1] = 6; origin_tune[0] = origin_tune[1] = 0.8; patch_size[0] = patch_size[1] = 7; range[0] = range[1] = 3; frames[0] = frames[1] = 2; prefilter[0] = prefilter[1] = 0; if (!strcasecmp(preset, "ultralight")) { strength[0] = 0; strength[1] = 2.4; origin_tune[0] = 0.9; origin_tune[1] = 0.9; } else if (!strcasecmp(preset, "light")) { strength[0] = 0; strength[1] = 3.5; origin_tune[0] = 0.9; origin_tune[1] = 0.9; } else if (!strcasecmp(preset, "strong")) { strength[0] = 0; strength[1] = 8; origin_tune[0] = 0.6; origin_tune[1] = 0.6; } } else if (!strcasecmp(tune, "highmotion")) { strength[0] = 6; strength[1] = 6; origin_tune[0] = 0.8; origin_tune[1] = 0.7; patch_size[0] = 7; patch_size[1] = 7; range[0] = 3; range[1] = 5; frames[0] = 2; frames[1] = 1; prefilter[0] = 0; prefilter[1] = 0; if (!strcasecmp(preset, "ultralight")) { strength[0] = 1.5; strength[1] = 2.4; origin_tune[0] = 0.9; origin_tune[1] = 0.9; } else if (!strcasecmp(preset, "light")) { strength[0] = 3; strength[1] = 3.25; origin_tune[0] = 0.9; origin_tune[1] = 0.8; } else if (!strcasecmp(preset, "strong")) { strength[0] = 8; strength[1] = 6.75; origin_tune[0] = 0.6; origin_tune[1] = 0.5; } } else if (!strcasecmp(tune, "animation")) { strength[0] = 5; strength[1] = 4; origin_tune[0] = origin_tune[1] = 0.15; patch_size[0] = patch_size[1] = 5; range[0] = range[1] = 7; frames[0] = frames[1] = 4; prefilter[0] = prefilter[1] = 0; if (!strcasecmp(preset, "ultralight")) { strength[0] = 2.5; strength[1] = 2; frames[0] = 2; frames[1] = 2; } else if (!strcasecmp(preset, "light")) { strength[0] = 3; strength[1] = 2.25; frames[0] = 3; frames[1] = 3; } else if (!strcasecmp(preset, "strong")) { strength[0] = 10; strength[1] = 8; } } else { fprintf(stderr, "Unrecognized nlmeans tune (%s).\n", tune); return NULL; } opt = hb_strdup_printf("%lf:%lf:%d:%d:%d:%d:%lf:%lf:%d:%d:%d:%d", strength[0], origin_tune[0], patch_size[0], range[0], frames[0], prefilter[0], strength[1], origin_tune[1], patch_size[1], range[1], frames[1], prefilter[1]); } else { opt = strdup(preset); if (tune != NULL) { fprintf(stderr, "Custom nlmeans parameters specified; ignoring nlmeans tune (%s).\n", tune); } } return opt; } /* HQDN3D presets * * Presets adjust strength: * ultralight - visually transparent * light * medium * strong */ static char * generate_hqdn3d_settings(const char *preset, const char *tune) { if (preset == NULL) return NULL; if (!strcasecmp(preset, "strong")) return strdup("7:7:7:5:5:5"); else if (!strcasecmp(preset, "medium")) return strdup("3:2:2:2:3:3"); else if (!strcasecmp(preset, "light") || !strcasecmp(preset, "weak")) return strdup("2:1:1:2:3:3"); else if (!strcasecmp(preset, "ultralight")) return strdup("1:0.7:0.7:1:2:2"); else return strdup(preset); } int hb_validate_param_string(const char *regex_pattern, const char *param_string) { regex_t regex_temp; if (regcomp(®ex_temp, regex_pattern, REG_EXTENDED) == 0) { if (regexec(®ex_temp, param_string, 0, NULL, 0) == 0) { regfree(®ex_temp); return 0; } } else { fprintf(stderr, "hb_validate_param_string: Error compiling regex for pattern (%s).\n", param_string); } regfree(®ex_temp); return 1; } int hb_validate_filter_settings(int filter_id, const char *filter_param) { // Regex matches "number" followed by one or more ":number", where number is uint or ufloat const char *hb_colon_separated_params_regex = "^((([0-9]+([.,][0-9]+)?)|([.,][0-9]+))((:(([0-9]+([.,][0-9]+)?)|([.,][0-9]+)))+)?)$"; const char *regex_pattern = NULL; switch (filter_id) { case HB_FILTER_NLMEANS: case HB_FILTER_HQDN3D: if (filter_param == NULL) { return 0; } regex_pattern = hb_colon_separated_params_regex; break; default: fprintf(stderr, "hb_validate_filter_settings: Unrecognized filter (%d).\n", filter_id); return 1; break; } if (hb_validate_param_string(regex_pattern, filter_param) == 0) { return 0; } return 1; } char * hb_generate_filter_settings(int filter_id, const char *preset, const char *tune) { char *filter_param = NULL; switch (filter_id) { case HB_FILTER_NLMEANS: filter_param = generate_nlmeans_settings(preset, tune); break; case HB_FILTER_HQDN3D: filter_param = generate_hqdn3d_settings(preset, tune); break; default: fprintf(stderr, "hb_generate_filter_settings: Unrecognized filter (%d).\n", filter_id); break; } if (hb_validate_filter_settings(filter_id, filter_param) == 0) { return filter_param; } return NULL; } HandBrake-0.10.2/libhb/project.h.m40000664000175200017520000000335411200722756017264 0ustar handbrakehandbrakechangequote(<<, >>)dnl include(<>)dnl dnl dnl dnl #ifndef HB_PROJECT_H #define HB_PROJECT_H <<#>>define HB_PROJECT_TITLE "__HB_title" <<#>>define HB_PROJECT_NAME "__HB_name" <<#>>define HB_PROJECT_NAME_LOWER "__HB_name_lower" <<#>>define HB_PROJECT_NAME_UPPER "__HB_name_upper" <<#>>define HB_PROJECT_URL_WEBSITE "__HB_url_website" <<#>>define HB_PROJECT_URL_COMMUNITY "__HB_url_community" <<#>>define HB_PROJECT_URL_IRC "__HB_url_irc" <<#>>define HB_PROJECT_URL_APPCAST "__HB_url_appcast" <<#>>define HB_PROJECT_VERSION_MAJOR __HB_version_major <<#>>define HB_PROJECT_VERSION_MINOR __HB_version_minor <<#>>define HB_PROJECT_VERSION_POINT __HB_version_point <<#>>define HB_PROJECT_VERSION "__HB_version" <<#>>define HB_PROJECT_VERSION_HEX 0x<<>>__HB_version_hex<<>>LL <<#>>define HB_PROJECT_BUILD __HB_build <<#>>define HB_PROJECT_REPO_URL "__HB_repo_url" <<#>>define HB_PROJECT_REPO_ROOT "__HB_repo_root" <<#>>define HB_PROJECT_REPO_UUID "__HB_repo_uuid" <<#>>define HB_PROJECT_REPO_REV __HB_repo_rev <<#>>define HB_PROJECT_REPO_DATE "__HB_repo_date" <<#>>define HB_PROJECT_REPO_OFFICIAL __HB_repo_official <<#>>define HB_PROJECT_REPO_TYPE "__HB_repo_type" <<#>>define HB_PROJECT_BUILD_SPEC "__BUILD_spec" <<#>>define HB_PROJECT_BUILD_MACHINE "__BUILD_machine" <<#>>define HB_PROJECT_BUILD_VENDOR "__BUILD_vendor" <<#>>define HB_PROJECT_BUILD_SYSTEM "__BUILD_system" <<#>>define HB_PROJECT_BUILD_SYSTEMF "__BUILD_systemf" <<#>>define HB_PROJECT_BUILD_RELEASE "__BUILD_release" <<#>>define HB_PROJECT_BUILD_TITLE "__BUILD_title" <<#>>define HB_PROJECT_BUILD_ARCH "__BUILD_arch" #endif /* HB_PROJECT_PROJECT_H */ HandBrake-0.10.2/libhb/audio_remap.h0000664000175200017520000000635512463330511017564 0ustar handbrakehandbrake/* audio_remap.h * * Copyright (c) 2003-2015 HandBrake Team * This file is part of the HandBrake source code * Homepage: * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ /* This file handles the following two scenarios: * * 1) remapping audio from decoder order to libav order (for downmixing) * * 2) remapping audio from libav order to encoder order (for encoding) * * We only need to support: * * a) channels found in our non-libavcodec audio decoders' layouts * b) channels found in HB_AMIXDOWN_* layouts * * We consider that: * * Left/Right Surround == Side Left/Right * Left/Right Rear Surround == Back Left/Right */ #ifndef AUDIO_REMAP_H #define AUDIO_REMAP_H #include #include "libavutil/samplefmt.h" /* we only need to support the 11 "most common" channels */ #define HB_AUDIO_REMAP_MAX_CHANNELS 11 typedef struct { uint64_t channel_order_map[HB_AUDIO_REMAP_MAX_CHANNELS + 1]; } hb_chan_map_t; typedef struct { int nchannels; int remap_needed; hb_chan_map_t *channel_map_in; hb_chan_map_t *channel_map_out; int table[HB_AUDIO_REMAP_MAX_CHANNELS]; void (*remap)(uint8_t **samples, int nsamples, int nchannels, int *remap_table); } hb_audio_remap_t; /* * Predefined channel maps for common channel orders. */ extern hb_chan_map_t hb_libav_chan_map; extern hb_chan_map_t hb_liba52_chan_map; extern hb_chan_map_t hb_vorbis_chan_map; extern hb_chan_map_t hb_aac_chan_map; /* * Initialize an hb_audio_remap_t to remap audio with the specified sample * format, from the input to the output channel order (indicated by * channel_map_in and channel_map_out, respectively). */ hb_audio_remap_t* hb_audio_remap_init(enum AVSampleFormat sample_fmt, hb_chan_map_t *channel_map_out, hb_chan_map_t *channel_map_in); /* * Updates an hb_audio_remap_t's number of channels and remap table to work with * the specified channel layout. * * Must be called at least once before remapping. */ void hb_audio_remap_set_channel_layout(hb_audio_remap_t *remap, uint64_t channel_layout); /* * Free an hb_audio_remap_t. */ void hb_audio_remap_free(hb_audio_remap_t *remap); /* * Remap audio between 2 different channel orders, using the settings specified * in the remap paremeter. Remapping is only done when necessary. * * The remap parameter can be NULL (no remapping). */ void hb_audio_remap(hb_audio_remap_t *remap, uint8_t **samples, int nsamples); /* * Generate a table used to remap audio between 2 different channel orders. * * Usage: output_sample[channel_idx] = input_sample[remap_table[channel_idx]] * * remap_table is allocated by the caller. */ void hb_audio_remap_build_table(hb_chan_map_t *channel_map_out, hb_chan_map_t *channel_map_in, uint64_t channel_layout, int *remap_table); #endif /* AUDIO_REMAP_H */ HandBrake-0.10.2/libhb/dxva2api.h0000664000175200017520000010275012463330511017011 0ustar handbrakehandbrake/* dxva2api.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html Authors: Peng Gao Li Cao */ #ifndef _DXVA2API_H #define _DXVA2API_H #ifdef USE_HWD #define MINGW_DXVA2API_H_VERSION (2) #if __GNUC__ >= 3 #pragma GCC system_header #endif #include #include /* Define it to allow using nameless struct/union (non C99 compliant) to match * the official documentation. */ //#define DXVA2API_USE_BITFIELDS /****************STRUCTURES******************/ #pragma pack(push, 1) #define DXVA2API_USE_BITFIELDS typedef struct _DXVA2_ExtendedFormat { #ifdef DXVA2API_USE_BITFIELDS union { struct { UINT SampleFormat : 8; UINT VideoChromaSubsampling : 4; UINT NominalRange : 3; UINT VideoTransferMatrix : 3; UINT VideoLighting : 4; UINT VideoPrimaries : 5; UINT VideoTransferFunction : 5; }; UINT value; }; #else UINT value; #endif } DXVA2_ExtendedFormat; typedef struct _DXVA2_Frequency { UINT Numerator; UINT Denominator; } DXVA2_Frequency; typedef struct _DXVA2_VideoDesc { UINT SampleWidth; UINT SampleHeight; DXVA2_ExtendedFormat SampleFormat; D3DFORMAT Format; DXVA2_Frequency InputSampleFreq; DXVA2_Frequency OutputFrameFreq; UINT UABProtectionLevel; UINT Reserved; } DXVA2_VideoDesc; typedef struct _DXVA2_ConfigPictureDecode { GUID guidConfigBitstreamEncryption; GUID guidConfigMBcontrolEncryption; GUID guidConfigResidDiffEncryption; UINT ConfigBitstreamRaw; UINT ConfigMBcontrolRasterOrder; UINT ConfigResidDiffHost; UINT ConfigSpatialResid8; UINT ConfigResid8Subtraction; UINT ConfigSpatialHost8or9Clipping; UINT ConfigSpatialResidInterleaved; UINT ConfigIntraResidUnsigned; UINT ConfigResidDiffAccelerator; UINT ConfigHostInverseScan; UINT ConfigSpecificIDCT; UINT Config4GroupedCoefs; USHORT ConfigMinRenderTargetBuffCount; USHORT ConfigDecoderSpecific; } DXVA2_ConfigPictureDecode; typedef struct _DXVA2_DecodeBufferDesc { DWORD CompressedBufferType; UINT BufferIndex; UINT DataOffset; UINT DataSize; UINT FirstMBaddress; UINT NumMBsInBuffer; UINT Width; UINT Height; UINT Stride; UINT ReservedBits; PVOID pvPVPState; } DXVA2_DecodeBufferDesc; typedef struct _DXVA2_DecodeExtensionData { UINT Function; PVOID pPrivateInputData; UINT PrivateInputDataSize; PVOID pPrivateOutputData; UINT PrivateOutputDataSize; } DXVA2_DecodeExtensionData; typedef struct _DXVA2_DecodeExecuteParams { UINT NumCompBuffers; DXVA2_DecodeBufferDesc *pCompressedBuffers; DXVA2_DecodeExtensionData *pExtensionData; } DXVA2_DecodeExecuteParams; enum { DXVA2_VideoDecoderRenderTarget = 0, DXVA2_VideoProcessorRenderTarget= 1, DXVA2_VideoSoftwareRenderTarget = 2 }; enum { DXVA2_PictureParametersBufferType = 0, DXVA2_MacroBlockControlBufferType = 1, DXVA2_ResidualDifferenceBufferType = 2, DXVA2_DeblockingControlBufferType = 3, DXVA2_InverseQuantizationMatrixBufferType = 4, DXVA2_SliceControlBufferType = 5, DXVA2_BitStreamDateBufferType = 6, DXVA2_MotionVectorBuffer = 7, DXVA2_FilmGrainBuffer = 8 }; /* DXVA MPEG-I/II and VC-1 */ typedef struct _DXVA_PictureParameters { USHORT wDecodedPictureIndex; USHORT wDeblockedPictureIndex; USHORT wForwardRefPictureIndex; USHORT wBackwardRefPictureIndex; USHORT wPicWidthInMBminus1; USHORT wPicHeightInMBminus1; UCHAR bMacroblockWidthMinus1; UCHAR bMacroblockHeightMinus1; UCHAR bBlockWidthMinus1; UCHAR bBlockHeightMinus1; UCHAR bBPPminus1; UCHAR bPicStructure; UCHAR bSecondField; UCHAR bPicIntra; UCHAR bPicBackwardPrediction; UCHAR bBidirectionalAveragingMode; UCHAR bMVprecisionAndChromaRelation; UCHAR bChromaFormat; UCHAR bPicScanFixed; UCHAR bPicScanMethod; UCHAR bPicReadbackRequests; UCHAR bRcontrol; UCHAR bPicSpatialResid8; UCHAR bPicOverflowBlocks; UCHAR bPicExtrapolation; UCHAR bPicDeblocked; UCHAR bPicDeblockConfined; UCHAR bPic4MVallowed; UCHAR bPicOBMC; UCHAR bPicBinPB; UCHAR bMV_RPS; UCHAR bReservedBits; USHORT wBitstreamFcodes; USHORT wBitstreamPCEelements; UCHAR bBitstreamConcealmentNeed; UCHAR bBitstreamConcealmentMethod; } DXVA_PictureParameters, *LPDXVA_PictureParameters; typedef struct _DXVA_QmatrixData { BYTE bNewQmatrix[4]; WORD Qmatrix[4][8 * 8]; } DXVA_QmatrixData, *LPDXVA_QmatrixData; typedef struct _DXVA_SliceInfo { USHORT wHorizontalPosition; USHORT wVerticalPosition; UINT dwSliceBitsInBuffer; UINT dwSliceDataLocation; UCHAR bStartCodeBitOffset; UCHAR bReservedBits; USHORT wMBbitOffset; USHORT wNumberMBsInSlice; USHORT wQuantizerScaleCode; USHORT wBadSliceChopping; } DXVA_SliceInfo, *LPDXVA_SliceInfo; /* DXVA H264 */ typedef struct { #ifdef DXVA2API_USE_BITFIELDS union { struct { UCHAR Index7Bits : 7; UCHAR AssociatedFlag : 1; }; UCHAR bPicEntry; }; #else UCHAR bPicEntry; #endif } DXVA_PicEntry_H264; typedef struct { USHORT wFrameWidthInMbsMinus1; USHORT wFrameHeightInMbsMinus1; DXVA_PicEntry_H264 CurrPic; UCHAR num_ref_frames; #ifdef DXVA2API_USE_BITFIELDS union { struct { USHORT field_pic_flag : 1; USHORT MbaffFrameFlag : 1; USHORT residual_colour_transform_flag : 1; USHORT sp_for_switch_flag : 1; USHORT chroma_format_idc : 2; USHORT RefPicFlag : 1; USHORT constrained_intra_pred_flag : 1; USHORT weighted_pred_flag : 1; USHORT weighted_bipred_idc : 2; USHORT MbsConsecutiveFlag : 1; USHORT frame_mbs_only_flag : 1; USHORT transform_8x8_mode_flag : 1; USHORT MinLumaBipredSize8x8Flag : 1; USHORT IntraPicFlag : 1; }; USHORT wBitFields; }; #else USHORT wBitFields; #endif UCHAR bit_depth_luma_minus8; UCHAR bit_depth_chroma_minus8; USHORT Reserved16Bits; UINT StatusReportFeedbackNumber; DXVA_PicEntry_H264 RefFrameList[16]; INT CurrFieldOrderCnt[2]; INT FieldOrderCntList[16][2]; CHAR pic_init_qs_minus26; CHAR chroma_qp_index_offset; CHAR second_chroma_qp_index_offset; UCHAR ContinuationFlag; CHAR pic_init_qp_minus26; UCHAR num_ref_idx_l0_active_minus1; UCHAR num_ref_idx_l1_active_minus1; UCHAR Reserved8BitsA; USHORT FrameNumList[16]; UINT UsedForReferenceFlags; USHORT NonExistingFrameFlags; USHORT frame_num; UCHAR log2_max_frame_num_minus4; UCHAR pic_order_cnt_type; UCHAR log2_max_pic_order_cnt_lsb_minus4; UCHAR delta_pic_order_always_zero_flag; UCHAR direct_8x8_inference_flag; UCHAR entropy_coding_mode_flag; UCHAR pic_order_present_flag; UCHAR num_slice_groups_minus1; UCHAR slice_group_map_type; UCHAR deblocking_filter_control_present_flag; UCHAR redundant_pic_cnt_present_flag; UCHAR Reserved8BitsB; USHORT slice_group_change_rate_minus1; UCHAR SliceGroupMap[810]; } DXVA_PicParams_H264; typedef struct { UCHAR bScalingLists4x4[6][16]; UCHAR bScalingLists8x8[2][64]; } DXVA_Qmatrix_H264; typedef struct { UINT BSNALunitDataLocation; UINT SliceBytesInBuffer; USHORT wBadSliceChopping; USHORT first_mb_in_slice; USHORT NumMbsForSlice; USHORT BitOffsetToSliceData; UCHAR slice_type; UCHAR luma_log2_weight_denom; UCHAR chroma_log2_weight_denom; UCHAR num_ref_idx_l0_active_minus1; UCHAR num_ref_idx_l1_active_minus1; CHAR slice_alpha_c0_offset_div2; CHAR slice_beta_offset_div2; UCHAR Reserved8Bits; DXVA_PicEntry_H264 RefPicList[2][32]; SHORT Weights[2][32][3][2]; CHAR slice_qs_delta; CHAR slice_qp_delta; UCHAR redundant_pic_cnt; UCHAR direct_spatial_mv_pred_flag; UCHAR cabac_init_idc; UCHAR disable_deblocking_filter_idc; USHORT slice_id; } DXVA_Slice_H264_Long; typedef struct { UINT BSNALunitDataLocation; UINT SliceBytesInBuffer; USHORT wBadSliceChopping; } DXVA_Slice_H264_Short; typedef struct { USHORT wFrameWidthInMbsMinus1; USHORT wFrameHeightInMbsMinus1; DXVA_PicEntry_H264 InPic; DXVA_PicEntry_H264 OutPic; USHORT PicOrderCnt_offset; INT CurrPicOrderCnt; UINT StatusReportFeedbackNumber; UCHAR model_id; UCHAR separate_colour_description_present_flag; UCHAR film_grain_bit_depth_luma_minus8; UCHAR film_grain_bit_depth_chroma_minus8; UCHAR film_grain_full_range_flag; UCHAR film_grain_colour_primaries; UCHAR film_grain_transfer_characteristics; UCHAR film_grain_matrix_coefficients; UCHAR blending_mode_id; UCHAR log2_scale_factor; UCHAR comp_model_present_flag[4]; UCHAR num_intensity_intervals_minus1[4]; UCHAR num_model_values_minus1[4]; UCHAR intensity_interval_lower_bound[3][16]; UCHAR intensity_interval_upper_bound[3][16]; SHORT comp_model_value[3][16][8]; } DXVA_FilmGrainChar_H264; typedef struct { union { struct { USHORT Fraction; SHORT Value; }; LONG ll; }; }DXVA2_Fixed32; typedef struct { UCHAR Cr; UCHAR Cb; UCHAR Y; UCHAR Alpha; }DXVA2_AYUVSample8; typedef struct { USHORT Cr; USHORT Cb; USHORT Y; USHORT Alpha; }DXVA2_AYUVSample16; typedef struct { DXVA2_Fixed32 MinValue; DXVA2_Fixed32 MaxValue; DXVA2_Fixed32 DefaultValue; DXVA2_Fixed32 StepSize; }DXVA2_ValueRange; typedef struct { DXVA2_Fixed32 Brightness; DXVA2_Fixed32 Contrast; DXVA2_Fixed32 Hue; DXVA2_Fixed32 Saturation; }DXVA2_ProcAmpValues; typedef struct { DXVA2_Fixed32 Level; DXVA2_Fixed32 Threshold; DXVA2_Fixed32 Radius; }DXVA2_FilterValues; typedef struct { UINT DeviceCaps; D3DPOOL InputPool; UINT NumForwardRefSamples; UINT NumBackwardRefSamples; UINT Reserved; UINT DeinterlaceTechnology; UINT ProcAmpControlCaps; UINT VideoProcessorOperations; UINT NoiseFilterTechnology; UINT DetailFilterTechnology; }DXVA2_VideoProcessorCaps; #ifndef _REFERENCE_TIME_ #define _REFERENCE_TIME_ typedef long long int64_t; typedef int64_t REFERENCE_TIME; #endif typedef struct { REFERENCE_TIME Start; REFERENCE_TIME End; DXVA2_ExtendedFormat SampleFormat; IDirect3DSurface9 *SrcSurface; RECT SrcRect; RECT DstRect; DXVA2_AYUVSample8 Pal[16]; DXVA2_Fixed32 PlanarAlpha; DWORD SampleData; }DXVA2_VideoSample; typedef struct { REFERENCE_TIME TargetFrame; RECT TargetRect; SIZE ConstrictionSize; UINT StreamingFlags; DXVA2_AYUVSample16 BackgroundColor; DXVA2_ExtendedFormat DestFormat; DXVA2_ProcAmpValues ProcAmpValues; DXVA2_Fixed32 Alpha; DXVA2_FilterValues NoiseFilterLuma; DXVA2_FilterValues NoiseFilterChroma; DXVA2_FilterValues DetailFilterLuma; DXVA2_FilterValues DetailFilterChroma; DWORD DestData; } DXVA2_VideoProcessBltParams; #pragma pack(pop) /*************INTERFACES************/ #ifdef __cplusplus extern "C" { #endif #define _COM_interface struct typedef _COM_interface IDirectXVideoDecoderService IDirectXVideoDecoderService; typedef _COM_interface IDirectXVideoDecoder IDirectXVideoDecoder; #undef INTERFACE #define INTERFACE IDirectXVideoDecoder DECLARE_INTERFACE_( IDirectXVideoDecoder, IUnknown ) { STDMETHOD( QueryInterface ) ( THIS_ REFIID, PVOID* ) PURE; STDMETHOD_( ULONG, AddRef ) ( THIS ) PURE; STDMETHOD_( ULONG, Release ) ( THIS ) PURE; STDMETHOD( GetVideoDecoderService ) ( THIS_ IDirectXVideoDecoderService** ) PURE; STDMETHOD( GetCreationParameters ) ( THIS_ GUID*, DXVA2_VideoDesc*, DXVA2_ConfigPictureDecode*, IDirect3DSurface9***, UINT* ) PURE; STDMETHOD( GetBuffer ) ( THIS_ UINT, void**, UINT* ) PURE; STDMETHOD( ReleaseBuffer ) ( THIS_ UINT ) PURE; STDMETHOD( BeginFrame ) ( THIS_ IDirect3DSurface9 *, void* ) PURE; STDMETHOD( EndFrame ) ( THIS_ HANDLE * ) PURE; STDMETHOD( Execute ) ( THIS_ const DXVA2_DecodeExecuteParams* ) PURE; }; #if !defined(__cplusplus) || defined(CINTERFACE) #define IDirectXVideoDecoder_QueryInterface( p, a, b ) (p)->lpVtbl->QueryInterface( p, a, b ) #define IDirectXVideoDecoder_AddRef( p ) (p)->lpVtbl->AddRef( p ) #define IDirectXVideoDecoder_Release( p ) (p)->lpVtbl->Release( p ) #define IDirectXVideoDecoder_BeginFrame( p, a, b ) (p)->lpVtbl->BeginFrame( p, a, b ) #define IDirectXVideoDecoder_EndFrame( p, a ) (p)->lpVtbl->EndFrame( p, a ) #define IDirectXVideoDecoder_Execute( p, a ) (p)->lpVtbl->Execute( p, a ) #define IDirectXVideoDecoder_GetBuffer( p, a, b, c ) (p)->lpVtbl->GetBuffer( p, a, b, c ) #define IDirectXVideoDecoder_GetCreationParameters( p, a, b, c, d, e ) (p)->lpVtbl->GetCreationParameters( p, a, b, c, d, e ) #define IDirectXVideoDecoder_GetVideoDecoderService( p, a ) (p)->lpVtbl->GetVideoDecoderService( p, a ) #define IDirectXVideoDecoder_ReleaseBuffer( p, a ) (p)->lpVtbl->ReleaseBuffer( p, a ) #else #define IDirectXVideoDecoder_QueryInterface( p, a, b ) (p)->QueryInterface( a, b ) #define IDirectXVideoDecoder_AddRef( p ) (p)->AddRef() #define IDirectXVideoDecoder_Release( p ) (p)->Release() #define IDirectXVideoDecoder_BeginFrame( p, a, b ) (p)->BeginFrame( a, b ) #define IDirectXVideoDecoder_EndFrame( p, a ) (p)->EndFrame( a ) #define IDirectXVideoDecoder_Execute( p, a ) (p)->Execute( a ) #define IDirectXVideoDecoder_GetBuffer( p, a, b, c ) (p)->GetBuffer( a, b, c ) #define IDirectXVideoDecoder_GetCreationParameters( p, a, b, c, d, e ) (p)->GetCreationParameters( a, b, c, d, e ) #define IDirectXVideoDecoder_GetVideoDecoderService( p, a ) (p)->GetVideoDecoderService( a ) #define IDirectXVideoDecoder_ReleaseBuffer( p, a ) (p)->ReleaseBuffer( a ) #endif #undef INTERFACE #define INTERFACE IDirectXVideoAccelerationService DECLARE_INTERFACE_( IDirectXVideoAccelerationService, IUnknown ) { STDMETHOD( QueryInterface ) ( THIS_ REFIID, PVOID* ) PURE; STDMETHOD_( ULONG, AddRef ) ( THIS ) PURE; STDMETHOD_( ULONG, Release ) ( THIS ) PURE; STDMETHOD( CreateSurface ) ( THIS_ UINT, UINT, UINT, D3DFORMAT, D3DPOOL, DWORD, DWORD, IDirect3DSurface9**, HANDLE* ) PURE; }; #if !defined(__cplusplus) || defined(CINTERFACE) #define IDirectXVideoAccelerationService_QueryInterface( p, a, b ) (p)->lpVtbl->QueryInterface( p, a, b ) #define IDirectXVideoAccelerationService_AddRef( p ) (p)->lpVtbl->AddRef( p ) #define IDirectXVideoAccelerationService_Release( p ) (p)->lpVtbl->Release( p ) #define IDirectXVideoAccelerationService_CreateSurface( p, a, b, c, d, e, f, g, h, i ) (p)->lpVtbl->CreateSurface( p, a, b, c, d, e, f, g, h, i ) #else #define IDirectXVideoAccelerationService_QueryInterface( p, a, b ) (p)->QueryInterface( a, b ) #define IDirectXVideoAccelerationService_AddRef( p ) (p)->AddRef() #define IDirectXVideoAccelerationService_Release( p ) (p)->Release() #define IDirectXVideoAccelerationService_CreateSurface( p, a, b, c, d, e, f, g, h, i ) (p)->CreateSurface( a, b, c, d, e, f, g, h, i ) #endif #undef INTERFACE #define INTERFACE IDirectXVideoDecoderService DECLARE_INTERFACE_( IDirectXVideoDecoderService, IDirectXVideoAccelerationService ) { STDMETHOD( QueryInterface ) ( THIS_ REFIID, PVOID* ) PURE; STDMETHOD_( ULONG, AddRef ) ( THIS ) PURE; STDMETHOD_( ULONG, Release ) ( THIS ) PURE; STDMETHOD( CreateSurface ) ( THIS_ UINT, UINT, UINT, D3DFORMAT, D3DPOOL, DWORD, DWORD, IDirect3DSurface9**, HANDLE* ) PURE; STDMETHOD( GetDecoderDeviceGuids ) ( THIS_ UINT*, GUID ** ) PURE; STDMETHOD( GetDecoderRenderTargets ) ( THIS_ REFGUID, UINT*, D3DFORMAT** ) PURE; STDMETHOD( GetDecoderConfigurations ) ( THIS_ REFGUID, const DXVA2_VideoDesc*, IUnknown*, UINT*, DXVA2_ConfigPictureDecode** ) PURE; STDMETHOD( CreateVideoDecoder ) ( THIS_ REFGUID, const DXVA2_VideoDesc*, DXVA2_ConfigPictureDecode*, IDirect3DSurface9**, UINT, IDirectXVideoDecoder** ) PURE; }; #if !defined(__cplusplus) || defined(CINTERFACE) #define IDirectXVideoDecoderService_QueryInterface( p, a, b ) (p)->lpVtbl->QueryInterface( p, a, b ) #define IDirectXVideoDecoderService_AddRef( p ) (p)->lpVtbl->AddRef( p ) #define IDirectXVideoDecoderService_Release( p ) (p)->lpVtbl->Release( p ) #define IDirectXVideoDecoderService_CreateSurface( p, a, b, c, d, e, f, g, h, i ) (p)->lpVtbl->CreateSurface( p, a, b, c, d, e, f, g, h, i ) #define IDirectXVideoDecoderService_CreateVideoDecoder( p, a, b, c, d, e, f ) (p)->lpVtbl->CreateVideoDecoder( p, a, b, c, d, e, f ) #define IDirectXVideoDecoderService_GetDecoderConfigurations( p, a, b, c, d, e ) (p)->lpVtbl->GetDecoderConfigurations( p, a, b, c, d, e ) #define IDirectXVideoDecoderService_GetDecoderDeviceGuids( p, a, b ) (p)->lpVtbl->GetDecoderDeviceGuids( p, a, b ) #define IDirectXVideoDecoderService_GetDecoderRenderTargets( p, a, b, c ) (p)->lpVtbl->GetDecoderRenderTargets( p, a, b, c ) #else #define IDirectXVideoDecoderService_QueryInterface( p, a, b ) (p)->QueryInterface( a, b ) #define IDirectXVideoDecoderService_AddRef( p ) (p)->AddRef() #define IDirectXVideoDecoderService_Release( p ) (p)->Release() #define IDirectXVideoDecoderService_CreateSurface( p, a, b, c, d, e, f, g, h, i ) (p)->CreateSurface( a, b, c, d, e, f, g, h, i ) #define IDirectXVideoDecoderService_CreateVideoDecoder( p, a, b, c, d, e, f ) (p)->CreateVideoDecoder( a, b, c, d, e, f ) #define IDirectXVideoDecoderService_GetDecoderConfigurations( p, a, b, c, d, e ) (p)->GetDecoderConfigurations( a, b, c, d, e ) #define IDirectXVideoDecoderService_GetDecoderDeviceGuids( p, a, b ) (p)->GetDecoderDeviceGuids( a, b ) #define IDirectXVideoDecoderService_GetDecoderRenderTargets( p, a, b, c ) (p)->GetDecoderRenderTargets( a, b, c ) #endif #undef INTERFACE #define INTERFACE IDirect3DDeviceManager9 DECLARE_INTERFACE_( IDirect3DDeviceManager9, IUnknown ) { STDMETHOD( QueryInterface ) ( THIS_ REFIID, PVOID* ) PURE; STDMETHOD_( ULONG, AddRef ) ( THIS ) PURE; STDMETHOD_( ULONG, Release ) ( THIS ) PURE; STDMETHOD( ResetDevice ) ( THIS_ IDirect3DDevice9*, UINT ) PURE; STDMETHOD( OpenDeviceHandle ) ( THIS_ HANDLE* ) PURE; STDMETHOD( CloseDeviceHandle ) ( THIS_ HANDLE ) PURE; STDMETHOD( TestDevice ) ( THIS_ HANDLE ) PURE; STDMETHOD( LockDevice ) ( THIS_ HANDLE, IDirect3DDevice9**, BOOL ) PURE; STDMETHOD( UnlockDevice ) ( THIS_ HANDLE, BOOL ) PURE; STDMETHOD( GetVideoService ) ( THIS_ HANDLE, REFIID, void** ) PURE; }; #if !defined(__cplusplus) || defined(CINTERFACE) #define IDirect3DDeviceManager9_QueryInterface( p, a, b ) (p)->lpVtbl->QueryInterface( p, a, b ) #define IDirect3DDeviceManager9_AddRef( p ) (p)->lpVtbl->AddRef( p ) #define IDirect3DDeviceManager9_Release( p ) (p)->lpVtbl->Release( p ) #define IDirect3DDeviceManager9_ResetDevice( p, a, b ) (p)->lpVtbl->ResetDevice( p, a, b ) #define IDirect3DDeviceManager9_OpenDeviceHandle( p, a ) (p)->lpVtbl->OpenDeviceHandle( p, a ) #define IDirect3DDeviceManager9_CloseDeviceHandle( p, a ) (p)->lpVtbl->CloseDeviceHandle( p, a ) #define IDirect3DDeviceManager9_TestDevice( p, a ) (p)->lpVtbl->TestDevice( p, a ) #define IDirect3DDeviceManager9_LockDevice( p, a, b, c ) (p)->lpVtbl->LockDevice( p, a, b, c ) #define IDirect3DDeviceManager9_UnlockDevice( p, a, b ) (p)->lpVtbl->UnlockDevice( p, a, b ) #define IDirect3DDeviceManager9_GetVideoService( p, a, b, c ) (p)->lpVtbl->GetVideoService( p, a, b, c ) #else #define IDirect3DDeviceManager9_QueryInterface( p, a, b ) (p)->QueryInterface( a, b ) #define IDirect3DDeviceManager9_AddRef( p ) (p)->AddRef() #define IDirect3DDeviceManager9_Release( p ) (p)->Release() #define IDirect3DDeviceManager9_ResetDevice( p, a, b ) (p)->ResetDevice( a, b ) #define IDirect3DDeviceManager9_OpenDeviceHandle( p, a ) (p)->OpenDeviceHandle( a ) #define IDirect3DDeviceManager9_CloseDeviceHandle( p, a ) (p)->CloseDeviceHandle( a ) #define IDirect3DDeviceManager9_TestDevice( p, a ) (p)->TestDevice( a ) #define IDirect3DDeviceManager9_LockDevice( p, a, b, c ) (p)->LockDevice( a, b, c ) #define IDirect3DDeviceManager9_UnlockDevice( p, a, b ) (p)->UnlockDevice( a, b ) #define IDirect3DDeviceManager9_GetVideoService( p, a, b, c ) (p)->GetVideoService( a, b, c ) #endif typedef _COM_interface IDirectXVideoProcessorService IDirectXVideoProcessorService; typedef _COM_interface IDirectXVideoProcessor IDirectXVideoProcessor; #undef INTERFACE #define INTERFACE IDirectXVideoProcessor DECLARE_INTERFACE_( IDirectXVideoProcessor, IUnknown ) { STDMETHOD( QueryInterface ) ( THIS_ REFIID, PVOID* ) PURE; STDMETHOD_( ULONG, AddRef ) ( THIS ) PURE; STDMETHOD_( ULONG, Release ) ( THIS ) PURE; STDMETHOD( GetVideoProcessorService ) ( THIS_ IDirectXVideoProcessorService** ) PURE; STDMETHOD( GetCreationParameters ) ( THIS_ GUID*, DXVA2_VideoDesc*, D3DFORMAT*, UINT* ) PURE; STDMETHOD( GetVideoProcessorCaps ) ( THIS_ DXVA2_VideoProcessorCaps* ) PURE; STDMETHOD( GetProcAmpRange ) ( THIS_ UINT, DXVA2_ValueRange* ) PURE; STDMETHOD( GetFilterPropertyRange ) ( THIS_ UINT, DXVA2_ValueRange* ) PURE; STDMETHOD( VideoProcessBlt ) ( THIS_ IDirect3DSurface9*, DXVA2_VideoProcessBltParams*, DXVA2_VideoSample*, UINT, HANDLE* ) PURE; }; #if !defined(__cplusplus) || defined(CINTERFACE) #define IDirectXVideoProcessor_QueryInterface( p, a, b ) (p)->lpVtbl->QueryInterface( p, a, b ) #define IDirectXVideoProcessor_AddRef( p ) (p)->lpVtbl->AddRef( p ) #define IDirectXVideoProcessor_Release( p ) (p)->lpVtbl->Release( p ) #define IDirectXVideoProcessor_GetVideoProcessorService( p, a ) (p)->lpVtbl->GetVideoProcessorService( p, a ) #define IDirectXVideoProcessor_GetCreationParameters( p, a, b, c, d ) (p)->lpVtbl->GetCreationParameters( p, a, b, c, d ) #define IDirectXVideoProcessor_GetVideoProcessorCaps( p, a ) (p)->lpVtbl->GetVideoProcessorCaps( p, a ) #define IDirectXVideoProcessor_GetProcAmpRange( p, a, b ) (p)->lpVtbl->GetProcAmpRange( p, a, b ) #define IDirectXVideoProcessor_GetFilterPropertyRange( p, a, b ) (p)->lpVtbl->GetFilterPropertyRange( p, a, b ) #define IDirectXVideoProcessor_VideoProcessBlt( p, a, b, c, d, e ) (p)->lpVtbl->VideoProcessBlt( p, a, b, c, d, e ) #else #define IDirectXVideoProcessor_QueryInterface( p, a, b ) (p)->QueryInterface( a, b ) #define IDirectXVideoProcessor_AddRef( p ) (p)->AddRef() #define IDirectXVideoProcessor_Release( p ) (p)->Release() #define IDirectXVideoProcessor_GetVideoProcessorService( p, a ) (p)->GetVideoProcessorService( a ) #define IDirectXVideoProcessor_GetCreationParameters( p, a, b, c, d ) (p)->GetCreationParameters( a, b, c, d ) #define IDirectXVideoProcessor_GetVideoProcessorCaps( p, a ) (p)->GetVideoProcessorCaps( a ) #define IDirectXVideoProcessor_GetProcAmpRange( p, a, b ) (p)->GetProcAmpRange( a, b ) #define IDirectXVideoProcessor_GetFilterPropertyRange( p, a, b ) (p)->GetFilterPropertyRange( a, b ) #define IDirectXVideoProcessor_VideoProcessBlt( p, a, b, c, d, e ) (p)->VideoProcessBlt( a, b, c, d, e ) #endif #undef INTERFACE #define INTERFACE IDirectXVideoProcessorService DECLARE_INTERFACE_( IDirectXVideoProcessorService, IDirectXVideoAccelerationService ) { STDMETHOD( QueryInterface ) ( THIS_ REFIID, PVOID* ) PURE; STDMETHOD_( ULONG, AddRef ) ( THIS ) PURE; STDMETHOD_( ULONG, Release ) ( THIS ) PURE; STDMETHOD( CreateSurface ) ( THIS_ UINT, UINT, UINT, D3DFORMAT, D3DPOOL, DWORD, DWORD, IDirect3DSurface9**, HANDLE* ) PURE; STDMETHOD( RegisterVideoProcessorSoftwareDevice ) ( THIS_ void* ) PURE; STDMETHOD( GetVideoProcessorDeviceGuids ) ( THIS_ DXVA2_VideoDesc*, UINT, GUID** ) PURE; STDMETHOD( GetVideoProcessorRenderTargets ) ( THIS_ REFGUID, DXVA2_VideoDesc*, UINT*, D3DFORMAT** ) PURE; STDMETHOD( GetVideoProcessorSubStreamFormats ) ( THIS_ REFGUID, DXVA2_VideoDesc*, D3DFORMAT, UINT*, D3DFORMAT** ) PURE; STDMETHOD( GetVideoProcessorCaps ) ( THIS_ REFGUID, DXVA2_VideoDesc*, D3DFORMAT, DXVA2_VideoProcessorCaps* ) PURE; STDMETHOD( GetProcAmpRange ) ( THIS_ REFGUID, DXVA2_VideoDesc*, D3DFORMAT, UINT, DXVA2_ValueRange* ) PURE; STDMETHOD( GetFilterPropertyRange ) ( THIS_ REFGUID, DXVA2_VideoDesc*, D3DFORMAT, UINT, DXVA2_ValueRange* ) PURE; STDMETHOD( CreateVideoProcessor ) ( THIS_ REFGUID, DXVA2_VideoDesc*, D3DFORMAT, UINT, IDirectXVideoProcessor** ) PURE; }; #if !defined(__cplusplus) || defined(CINTERFACE) #define IDirectXVideoProcessorService_QueryInterface( p, a, b ) (p)->lpVtbl->QueryInterface( p, a, b ) #define IDirectXVideoProcessorService_AddRef( p ) (p)->lpVtbl->AddRef( p ) #define IDirectXVideoProcessorService_Release( p ) (p)->lpVtbl->Release( p ) #define IDirectXVideoProcessorService_CreateSurface( p, a, b, c, d, e, f, g, h, i ) (p)->lpVtbl->CreateSurface( p, a, b, c, d, e, f, g, h, i ) #define IDirectXVideoProcessorService_RegisterVideoProcessorSoftwareDevice( p, a ) (p)->lpVtbl->RegisterVideoProcessorSoftwareDevice( p, a ) #define IDirectXVideoProcessorService_GetVideoProcessorDeviceGuids( p, a, b, c ) (p)->lpVtbl->GetVideoProcessorDeviceGuids( p, a, b, c ) #define IDirectXVideoProcessorService_GetVideoProcessorRenderTargets( p, a, b, c, d ) (p)->lpVtbl->GetVideoProcessorRenderTargets( p, a, b, c, d ) #define IDirectXVideoProcessorService_GetVideoProcessorSubStreamFormats( p, a, b, c, d, e ) (p)->lpVtbl->GetVideoProcessorSubStreamFormats( p, a, b, c, d, e ) #define IDirectXVideoProcessorService_GetVideoProcessorCaps( p, a, b, c, d ) (p)->lpVtbl->GetVideoProcessorCaps( p, a, b, c, d ) #define IDirectXVideoProcessorService_GetProcAmpRange( p, a, b, c, d, e ) (p)->lpVtbl->GetProcAmpRange( p, a, b, c, d, e ) #define IDirectXVideoProcessorService_GetFilterPropertyRange( p, a, b, c, d, e ) (p)->lpVtbl->GetFilterPropertyRange( p, a, b, c, d, e ) #define IDirectXVideoProcessorService_CreateVideoProcessor( p, a, b, c, d, e ) (p)->lpVtbl->CreateVideoProcessor( p, a, b, c, d, e ) #else #define IDirectXVideoProcessorService_QueryInterface( p, a, b ) (p)->QueryInterface( a, b ) #define IDirectXVideoProcessorService_AddRef( p ) (p)->AddRef() #define IDirectXVideoProcessorService_Release( p ) (p)->Release() #define IDirectXVideoProcessorService_CreateSurface( p, a, b, c, d, e, f, g, h, i ) (p)->CreateSurface( a, b, c, d, e, f, g, h, i ) #define IDirectXVideoProcessorService_RegisterVideoProcessorSoftwareDevice( p, a ) (p)->RegisterVideoProcessorSoftwareDevice( a ) #define IDirectXVideoProcessorService_GetVideoProcessorDeviceGuids( p, a, b, c ) (p)->GetVideoProcessorDeviceGuids( a, b, c ) #define IDirectXVideoProcessorService_GetVideoProcessorRenderTargets( p, a, b, c, d ) (p)->GetVideoProcessorRenderTargets( a, b, c, d ) #define IDirectXVideoProcessorService_GetVideoProcessorSubStreamFormats( p, a, b, c, d, e ) (p)->GetVideoProcessorSubStreamFormats( a, b, c, d, e ) #define IDirectXVideoProcessorService_GetVideoProcessorCaps( p, a, b, c, d ) (p)->GetVideoProcessorCaps( a, b, c, d ) #define IDirectXVideoProcessorService_GetProcAmpRange( p, a, b, c, d, e ) (p)->GetProcAmpRange( a, b, c, d, e ) #define IDirectXVideoProcessorService_GetFilterPropertyRange( p, a, b, c, d, e ) (p)->GetFilterPropertyRange( a, b, c, d, e ) #define IDirectXVideoProcessorService_CreateVideoProcessor( p, a, b, c, d, e ) (p)->CreateVideoProcessor( a, b, c, d, e ) #endif /***************************************************************************************************** ************************DXVA Video Processor******************************************************** *******************************************************************************************************/ /*#undef INTERFACE #define INTERFACE IDirectXVideoService DECLARE_INTERFACE_(IDirectXVideoService,IUnknown) { STDMETHOD(DXVA2CreateVideoService)(IDirect3DDevice9*, REFIID, void**) PURE; }; #if !defined(__cplusplus) || defined(CINTERFACE) #define IDirectXVideoService_DXVA2CreateVideoService(a,b,c) DXVA2CreateVideoService(a,b,c) #else #define IDirectXVideoService_DXVA2CreateVideoService(a,b,c) DXVA2CreateVideoService(a,b,c) #endif*/ #ifdef __cplusplus }; #endif #ifdef __cplusplus extern "C" HRESULT WINAPI DXVA2CreateVideoService( IDirect3DDevice9 *, REFIID riid, void **ppService ); #else extern HRESULT WINAPI DXVA2CreateVideoService( IDirect3DDevice9 *, REFIID riid, void **ppService ); #endif typedef enum _DXVA2_VideoChromaSubSampling { DXVA2_VideoChromaSubsamplingMask = 0xf, DXVA2_VideoChromaSubsampling_Unknown = 0, DXVA2_VideoChromaSubsampling_ProgressiveChroma = 0x8, DXVA2_VideoChromaSubsampling_Horizontally_Cosited = 0x4, DXVA2_VideoChromaSubsampling_Vertically_Cosited = 0x2, DXVA2_VideoChromaSubsampling_Vertically_AlignedChromaPlanes = 0x1, DXVA2_VideoChromaSubsampling_MPEG2 = ( DXVA2_VideoChromaSubsampling_Horizontally_Cosited | DXVA2_VideoChromaSubsampling_Vertically_AlignedChromaPlanes ), DXVA2_VideoChromaSubsampling_MPEG1 = DXVA2_VideoChromaSubsampling_Vertically_AlignedChromaPlanes, DXVA2_VideoChromaSubsampling_DV_PAL = ( DXVA2_VideoChromaSubsampling_Horizontally_Cosited | DXVA2_VideoChromaSubsampling_Vertically_Cosited ), DXVA2_VideoChromaSubsampling_Cosited = ( ( DXVA2_VideoChromaSubsampling_Horizontally_Cosited | DXVA2_VideoChromaSubsampling_Vertically_Cosited ) | DXVA2_VideoChromaSubsampling_Vertically_AlignedChromaPlanes )} DXVA2_VideoChromaSubSampling; typedef enum _DXVA2_NominalRange { DXVA2_NominalRangeMask = 0x7, DXVA2_NominalRange_Unknown = 0, DXVA2_NominalRange_Normal = 1, DXVA2_NominalRange_Wide = 2, DXVA2_NominalRange_0_255 = 1, DXVA2_NominalRange_16_235 = 2, DXVA2_NominalRange_48_208 = 3} DXVA2_NominalRange; typedef enum _DXVA2_VideoLighting { DXVA2_VideoLightingMask = 0xf, DXVA2_VideoLighting_Unknown = 0, DXVA2_VideoLighting_bright = 1, DXVA2_VideoLighting_office = 2, DXVA2_VideoLighting_dim = 3, DXVA2_VideoLighting_dark = 4} DXVA2_VideoLighting; typedef enum _DXVA2_VideoPrimaries { DXVA2_VideoPrimariesMask = 0x1f, DXVA2_VideoPrimaries_Unknown = 0, DXVA2_VideoPrimaries_reserved = 1, DXVA2_VideoPrimaries_BT709 = 2, DXVA2_VideoPrimaries_BT470_2_SysM = 3, DXVA2_VideoPrimaries_BT470_2_SysBG = 4, DXVA2_VideoPrimaries_SMPTE170M = 5, DXVA2_VideoPrimaries_SMPTE240M = 6, DXVA2_VideoPrimaries_EBU3213 = 7, DXVA2_VideoPrimaries_SMPTE_C = 8} DXVA2_VideoPrimaries; typedef enum _DXVA2_VideoTransferFunction { DXVA2_VideoTransFuncMask = 0x1f, DXVA2_VideoTransFunc_Unknown = 0, DXVA2_VideoTransFunc_10 = 1, DXVA2_VideoTransFunc_18 = 2, DXVA2_VideoTransFunc_20 = 3, DXVA2_VideoTransFunc_22 = 4, DXVA2_VideoTransFunc_709 = 5, DXVA2_VideoTransFunc_240M = 6, DXVA2_VideoTransFunc_sRGB = 7, DXVA2_VideoTransFunc_28 = 8} DXVA2_VideoTransferFunction; typedef enum _DXVA2_SampleFormat { DXVA2_SampleFormatMask = 0xff, DXVA2_SampleUnknown = 0, DXVA2_SampleProgressiveFrame = 2, DXVA2_SampleFieldInterleavedEvenFirst = 3, DXVA2_SampleFieldInterleavedOddFirst = 4, DXVA2_SampleFieldSingleEven = 5, DXVA2_SampleFieldSingleOdd = 6, DXVA2_SampleSubStream = 7} DXVA2_SampleFormat; typedef enum _DXVA2_VideoTransferMatrix { DXVA2_VideoTransferMatrixMask = 0x7, DXVA2_VideoTransferMatrix_Unknown = 0, DXVA2_VideoTransferMatrix_BT709 = 1, DXVA2_VideoTransferMatrix_BT601 = 2, DXVA2_VideoTransferMatrix_SMPTE240M = 3} DXVA2_VideoTransferMatrix; enum __MIDL___MIDL_itf_dxva2api_0000_0000_0004 { DXVA2_NoiseFilterLumaLevel = 1, DXVA2_NoiseFilterLumaThreshold = 2, DXVA2_NoiseFilterLumaRadius = 3, DXVA2_NoiseFilterChromaLevel = 4, DXVA2_NoiseFilterChromaThreshold = 5, DXVA2_NoiseFilterChromaRadius = 6, DXVA2_DetailFilterLumaLevel = 7, DXVA2_DetailFilterLumaThreshold = 8, DXVA2_DetailFilterLumaRadius = 9, DXVA2_DetailFilterChromaLevel = 10, DXVA2_DetailFilterChromaThreshold = 11, DXVA2_DetailFilterChromaRadius = 12}; enum __MIDL___MIDL_itf_dxva2api_0000_0000_0008 { DXVA2_VideoProcess_None = 0, DXVA2_VideoProcess_YUV2RGB = 0x1, DXVA2_VideoProcess_StretchX = 0x2, DXVA2_VideoProcess_StretchY = 0x4, DXVA2_VideoProcess_AlphaBlend = 0x8, DXVA2_VideoProcess_SubRects = 0x10, DXVA2_VideoProcess_SubStreams = 0x20, DXVA2_VideoProcess_SubStreamsExtended = 0x40, DXVA2_VideoProcess_YUV2RGBExtended = 0x80, DXVA2_VideoProcess_AlphaBlendExtended = 0x100, DXVA2_VideoProcess_Constriction = 0x200, DXVA2_VideoProcess_NoiseFilter = 0x400, DXVA2_VideoProcess_DetailFilter = 0x800, DXVA2_VideoProcess_PlanarAlpha = 0x1000, DXVA2_VideoProcess_LinearScaling = 0x2000, DXVA2_VideoProcess_GammaCompensated = 0x4000, DXVA2_VideoProcess_MaintainsOriginalFieldData = 0x8000, DXVA2_VideoProcess_Mask = 0xffff}; __inline float hb_dx_fixedtofloat( const DXVA2_Fixed32 _fixed_ ); __inline const DXVA2_Fixed32 hb_dx_fixed32_opaque_alpha(); __inline DXVA2_Fixed32 hb_dx_floattofixed( const float _float_ ); #endif #endif //_DXVA2API_H HandBrake-0.10.2/libhb/hb.h0000664000175200017520000001312212463330511015656 0ustar handbrakehandbrake/* hb.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef HB_HB_H #define HB_HB_H #ifdef __cplusplus extern "C" { #endif #include "project.h" #include "common.h" #include "hb_dict.h" /* hb_init() Initializes a libhb session (launches his own thread, detects CPUs, etc) */ #define HB_DEBUG_NONE 0 #define HB_DEBUG_ALL 1 void hb_register( hb_work_object_t * ); void hb_register_logger( void (*log_cb)(const char* message) ); hb_handle_t * hb_init( int verbose, int update_check ); hb_handle_t * hb_init_dl ( int verbose, int update_check ); // hb_init for use with dylib /* hb_get_version() */ char * hb_get_version( hb_handle_t * ); int hb_get_build( hb_handle_t * ); /* hb_check_update() Checks for an update on the website. If there is, returns the build number and points 'version' to a version description. Returns a negative value otherwise. */ int hb_check_update( hb_handle_t * h, char ** version ); char * hb_dvd_name( char * path ); void hb_dvd_set_dvdnav( int enable ); /* hb_scan() Scan the specified path. Can be a DVD device, a VIDEO_TS folder or a VOB file. If title_index is 0, scan all titles. */ void hb_scan( hb_handle_t *, const char * path, int title_index, int preview_count, int store_previews, uint64_t min_duration ); void hb_scan_stop( hb_handle_t * ); uint64_t hb_first_duration( hb_handle_t * ); /* hb_get_titles() Returns the list of valid titles detected by the latest scan. */ hb_list_t * hb_get_titles( hb_handle_t * ); /* hb_get_title_set() Returns the title set which contains a list of valid titles detected by the latest scan and title set data. */ hb_title_set_t * hb_get_title_set( hb_handle_t * ); /* hb_detect_comb() Analyze a frame for interlacing artifacts, returns true if they're found. Taken from Thomas Oestreich's 32detect filter in the Transcode project. */ int hb_detect_comb( hb_buffer_t * buf, int color_equal, int color_diff, int threshold, int prog_equal, int prog_diff, int prog_threshold ); // JJJ: title->job? int hb_save_preview( hb_handle_t * h, int title, int preview, hb_buffer_t *buf ); hb_buffer_t * hb_read_preview( hb_handle_t * h, hb_title_t *title, int preview ); void hb_get_preview( hb_handle_t *, hb_job_t *, int, uint8_t * ); hb_image_t * hb_get_preview2(hb_handle_t * h, int title_idx, int picture, hb_ui_geometry_t *ui_geo, int deinterlace); void hb_set_anamorphic_size2(hb_geometry_t *src_geo, hb_ui_geometry_t *ui_geo, hb_geometry_t *result); void hb_set_anamorphic_size( hb_job_t *, int *output_width, int *output_height, int *output_par_width, int *output_par_height ); void hb_validate_size( hb_job_t * job ); void hb_add_filter( hb_job_t * job, hb_filter_object_t * filter, const char * settings ); /* Handling jobs */ int hb_count( hb_handle_t * ); hb_job_t * hb_job( hb_handle_t *, int ); void hb_add( hb_handle_t *, hb_job_t * ); void hb_rem( hb_handle_t *, hb_job_t * ); hb_title_t * hb_find_title_by_index( hb_handle_t *h, int title_index ); hb_job_t * hb_job_init_by_index( hb_handle_t *h, int title_index ); hb_job_t * hb_job_init( hb_title_t * title ); void hb_job_reset( hb_job_t * job ); void hb_job_close( hb_job_t ** job ); void hb_start( hb_handle_t * ); void hb_pause( hb_handle_t * ); void hb_resume( hb_handle_t * ); void hb_stop( hb_handle_t * ); void hb_system_sleep_allow(hb_handle_t*); void hb_system_sleep_prevent(hb_handle_t*); /* Persistent data between jobs. */ typedef struct hb_interjob_s { int last_job; /* job->sequence_id & 0xFFFFFF */ int frame_count; /* number of frames counted by sync */ int out_frame_count; /* number of frames counted by render */ uint64_t total_time; /* real length in 90kHz ticks (i.e. seconds / 90000) */ int vrate; /* actual measured output vrate from 1st pass */ int vrate_base; /* actual measured output vrate_base from 1st pass */ hb_subtitle_t *select_subtitle; /* foreign language scan subtitle */ } hb_interjob_t; hb_interjob_t * hb_interjob_get( hb_handle_t * ); /* hb_get_state() Should be regularly called by the UI (like 5 or 10 times a second). Look at test/test.c to see how to use it. */ void hb_get_state( hb_handle_t *, hb_state_t * ); void hb_get_state2( hb_handle_t *, hb_state_t * ); /* hb_get_scancount() is called by the MacGui in UpdateUI to check for a new scan during HB_STATE_WORKING phase */ int hb_get_scancount( hb_handle_t * ); /* hb_close() Aborts all current jobs if any, frees memory. */ void hb_close( hb_handle_t ** ); /* hb_global_init() Performs process initialization. */ int hb_global_init(); /* hb_global_close() Performs final cleanup for the process. */ void hb_global_close(); /* hb_get_instance_id() Return the unique instance id of an libhb instance created by hb_init. */ int hb_get_instance_id( hb_handle_t * h ); #ifdef __cplusplus } #endif #endif HandBrake-0.10.2/libhb/encx264.c0000664000175200017520000015363312463330511016465 0ustar handbrakehandbrake/* encx264.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include #include "hb.h" #include "hb_dict.h" #include "encx264.h" int encx264Init( hb_work_object_t *, hb_job_t * ); int encx264Work( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); void encx264Close( hb_work_object_t * ); hb_work_object_t hb_encx264 = { WORK_ENCX264, "H.264/AVC encoder (libx264)", encx264Init, encx264Work, encx264Close }; #define DTS_BUFFER_SIZE 32 /* * The frame info struct remembers information about each frame across calls * to x264_encoder_encode. Since frames are uniquely identified by their * timestamp, we use some bits of the timestamp as an index. The LSB is * chosen so that two successive frames will have different values in the * bits over any plausible range of frame rates. (Starting with bit 8 allows * any frame rate slower than 352fps.) The MSB determines the size of the array. * It is chosen so that two frames can't use the same slot during the * encoder's max frame delay (set by the standard as 16 frames) and so * that, up to some minimum frame rate, frames are guaranteed to map to * different slots. (An MSB of 17 which is 2^(17-8+1) = 1024 slots guarantees * no collisions down to a rate of .7 fps). */ #define FRAME_INFO_MAX2 (8) // 2^8 = 256; 90000/256 = 352 frames/sec #define FRAME_INFO_MIN2 (17) // 2^17 = 128K; 90000/131072 = 1.4 frames/sec #define FRAME_INFO_SIZE (1 << (FRAME_INFO_MIN2 - FRAME_INFO_MAX2 + 1)) #define FRAME_INFO_MASK (FRAME_INFO_SIZE - 1) struct hb_work_private_s { hb_job_t * job; x264_t * x264; x264_picture_t pic_in; uint8_t * grey_data; uint32_t frames_in; uint32_t frames_out; int64_t last_stop; // Debugging - stop time of previous input frame hb_list_t *delayed_chapters; int64_t next_chapter_pts; struct { int64_t duration; } frame_info[FRAME_INFO_SIZE]; char filename[1024]; }; // used in delayed_chapters list struct chapter_s { int index; int64_t start; }; /*********************************************************************** * hb_work_encx264_init *********************************************************************** * **********************************************************************/ int encx264Init( hb_work_object_t * w, hb_job_t * job ) { x264_param_t param; x264_nal_t * nal; int nal_count; hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; pv->job = job; pv->next_chapter_pts = AV_NOPTS_VALUE; pv->delayed_chapters = hb_list_init(); if (x264_param_default_preset(¶m, job->encoder_preset, job->encoder_tune) < 0) { free( pv ); pv = NULL; return 1; } /* If the PSNR or SSIM tunes are in use, enable the relevant metric */ if (job->encoder_tune != NULL && *job->encoder_tune) { char *tmp = strdup(job->encoder_tune); char *tok = strtok(tmp, ",./-+"); do { if (!strncasecmp(tok, "psnr", 4)) { param.analyse.b_psnr = 1; break; } if (!strncasecmp(tok, "ssim", 4)) { param.analyse.b_ssim = 1; break; } } while ((tok = strtok(NULL, ",./-+")) != NULL); free(tmp); } /* Some HandBrake-specific defaults; users can override them * using the encoder_options string. */ if( job->pass == 2 && job->cfr != 1 ) { hb_interjob_t * interjob = hb_interjob_get( job->h ); param.i_fps_num = interjob->vrate; param.i_fps_den = interjob->vrate_base; } else { param.i_fps_num = job->vrate; param.i_fps_den = job->vrate_base; } if ( job->cfr == 1 ) { param.i_timebase_num = 0; param.i_timebase_den = 0; param.b_vfr_input = 0; } else { param.i_timebase_num = 1; param.i_timebase_den = 90000; } /* Set min:max keyframe intervals to 1:10 of fps; * adjust +0.5 for when fps has remainder to bump * { 23.976, 29.976, 59.94 } to { 24, 30, 60 }. */ param.i_keyint_min = (int)( (double)job->vrate / (double)job->vrate_base + 0.5 ); param.i_keyint_max = 10 * param.i_keyint_min; param.i_log_level = X264_LOG_INFO; /* set up the VUI color model & gamma to match what the COLR atom * set in muxmp4.c says. See libhb/muxmp4.c for notes. */ if( job->color_matrix_code == 4 ) { // Custom param.vui.i_colorprim = job->color_prim; param.vui.i_transfer = job->color_transfer; param.vui.i_colmatrix = job->color_matrix; } else if( job->color_matrix_code == 3 ) { // ITU BT.709 HD content param.vui.i_colorprim = HB_COLR_PRI_BT709; param.vui.i_transfer = HB_COLR_TRA_BT709; param.vui.i_colmatrix = HB_COLR_MAT_BT709; } else if( job->color_matrix_code == 2 ) { // ITU BT.601 DVD or SD TV content (PAL) param.vui.i_colorprim = HB_COLR_PRI_EBUTECH; param.vui.i_transfer = HB_COLR_TRA_BT709; param.vui.i_colmatrix = HB_COLR_MAT_SMPTE170M; } else if( job->color_matrix_code == 1 ) { // ITU BT.601 DVD or SD TV content (NTSC) param.vui.i_colorprim = HB_COLR_PRI_SMPTEC; param.vui.i_transfer = HB_COLR_TRA_BT709; param.vui.i_colmatrix = HB_COLR_MAT_SMPTE170M; } else { // detected during scan param.vui.i_colorprim = job->title->color_prim; param.vui.i_transfer = job->title->color_transfer; param.vui.i_colmatrix = job->title->color_matrix; } /* place job->encoder_options in an hb_dict_t for convenience */ hb_dict_t * x264_opts = NULL; if (job->encoder_options != NULL && *job->encoder_options) { x264_opts = hb_encopts_to_dict(job->encoder_options, job->vcodec); } /* iterate through x264_opts and have libx264 parse the options for us */ int ret; hb_dict_entry_t * entry = NULL; while( ( entry = hb_dict_next( x264_opts, entry ) ) ) { /* Here's where the strings are passed to libx264 for parsing. */ ret = x264_param_parse( ¶m, entry->key, entry->value ); /* Let x264 sanity check the options for us */ if( ret == X264_PARAM_BAD_NAME ) hb_log( "x264 options: Unknown suboption %s", entry->key ); if( ret == X264_PARAM_BAD_VALUE ) hb_log( "x264 options: Bad argument %s=%s", entry->key, entry->value ? entry->value : "(null)" ); } hb_dict_free( &x264_opts ); /* Reload colorimetry settings in case custom values were set * in the encoder_options string */ job->color_matrix_code = 4; job->color_prim = param.vui.i_colorprim; job->color_transfer = param.vui.i_transfer; job->color_matrix = param.vui.i_colmatrix; /* For 25 fps sources, HandBrake's explicit keyints will match the x264 defaults: * min-keyint 25 (same as auto), keyint 250. */ if( param.i_keyint_min != 25 || param.i_keyint_max != 250 ) { int min_auto; if ( param.i_fps_num / param.i_fps_den < param.i_keyint_max / 10 ) min_auto = param.i_fps_num / param.i_fps_den; else min_auto = param.i_keyint_max / 10; char min[40], max[40]; param.i_keyint_min == X264_KEYINT_MIN_AUTO ? snprintf( min, 40, "auto (%d)", min_auto ) : snprintf( min, 40, "%d", param.i_keyint_min ); param.i_keyint_max == X264_KEYINT_MAX_INFINITE ? snprintf( max, 40, "infinite" ) : snprintf( max, 40, "%d", param.i_keyint_max ); hb_log( "encx264: min-keyint: %s, keyint: %s", min, max ); } /* Settings which can't be overriden in the encoder_options string * (muxer-specific settings, resolution, ratecontrol, etc.). */ /* Disable annexb. Inserts size into nal header instead of start code. */ param.b_annexb = 0; param.i_width = job->width; param.i_height = job->height; if( job->anamorphic.mode ) { param.vui.i_sar_width = job->anamorphic.par_width; param.vui.i_sar_height = job->anamorphic.par_height; } if( job->vquality >= 0 ) { /* Constant RF */ param.rc.i_rc_method = X264_RC_CRF; param.rc.f_rf_constant = job->vquality; hb_log( "encx264: encoding at constant RF %f", param.rc.f_rf_constant ); } else { /* Average bitrate */ param.rc.i_rc_method = X264_RC_ABR; param.rc.i_bitrate = job->vbitrate; hb_log( "encx264: encoding at average bitrate %d", param.rc.i_bitrate ); if( job->pass > 0 && job->pass < 3 ) { memset( pv->filename, 0, 1024 ); hb_get_tempory_filename( job->h, pv->filename, "x264.log" ); } switch( job->pass ) { case 1: param.rc.b_stat_read = 0; param.rc.b_stat_write = 1; param.rc.psz_stat_out = pv->filename; break; case 2: param.rc.b_stat_read = 1; param.rc.b_stat_write = 0; param.rc.psz_stat_in = pv->filename; break; } } /* Apply profile and level settings last, if present. */ if (job->encoder_profile != NULL && *job->encoder_profile) { if (hb_apply_h264_profile(¶m, job->encoder_profile, 1)) { free(pv); pv = NULL; return 1; } } if (job->encoder_level != NULL && *job->encoder_level) { if (hb_apply_h264_level(¶m, job->encoder_level, job->encoder_profile, 1) < 0) { free(pv); pv = NULL; return 1; } } /* Turbo first pass */ if( job->pass == 1 && job->fastfirstpass == 1 ) { x264_param_apply_fastfirstpass( ¶m ); } /* B-pyramid is enabled by default. */ job->areBframes = 2; if( !param.i_bframe ) { job->areBframes = 0; } else if( !param.i_bframe_pyramid ) { job->areBframes = 1; } /* Log the unparsed x264 options string. */ char *x264_opts_unparsed = hb_x264_param_unparse(job->encoder_preset, job->encoder_tune, job->encoder_options, job->encoder_profile, job->encoder_level, job->width, job->height); if( x264_opts_unparsed != NULL ) { hb_log( "encx264: unparsed options: %s", x264_opts_unparsed ); } free( x264_opts_unparsed ); hb_deep_log( 2, "encx264: opening libx264 (pass %d)", job->pass ); pv->x264 = x264_encoder_open( ¶m ); if ( pv->x264 == NULL ) { hb_error("encx264: x264_encoder_open failed."); free( pv ); pv = NULL; return 1; } x264_encoder_headers( pv->x264, &nal, &nal_count ); /* Sequence Parameter Set */ memcpy(w->config->h264.sps, nal[0].p_payload + 4, nal[0].i_payload - 4); w->config->h264.sps_length = nal[0].i_payload - 4; /* Picture Parameter Set */ memcpy(w->config->h264.pps, nal[1].p_payload + 4, nal[1].i_payload - 4); w->config->h264.pps_length = nal[1].i_payload - 4; x264_picture_init( &pv->pic_in ); pv->pic_in.img.i_csp = X264_CSP_I420; pv->pic_in.img.i_plane = 3; if( job->grayscale ) { int uvsize = (hb_image_stride(AV_PIX_FMT_YUV420P, job->width, 1) * hb_image_height(AV_PIX_FMT_YUV420P, job->height, 1)); pv->grey_data = malloc(uvsize); memset(pv->grey_data, 0x80, uvsize); pv->pic_in.img.plane[1] = pv->pic_in.img.plane[2] = pv->grey_data; } return 0; } void encx264Close( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; if (pv->delayed_chapters != NULL) { struct chapter_s *item; while ((item = hb_list_item(pv->delayed_chapters, 0)) != NULL) { hb_list_rem(pv->delayed_chapters, item); free(item); } hb_list_close(&pv->delayed_chapters); } free( pv->grey_data ); x264_encoder_close( pv->x264 ); free( pv ); w->private_data = NULL; /* TODO */ } /* * see comments in definition of 'frame_info' in pv struct for description * of what these routines are doing. */ static void save_frame_info( hb_work_private_t * pv, hb_buffer_t * in ) { int i = (in->s.start >> FRAME_INFO_MAX2) & FRAME_INFO_MASK; pv->frame_info[i].duration = in->s.stop - in->s.start; } static int64_t get_frame_duration( hb_work_private_t * pv, int64_t pts ) { int i = (pts >> FRAME_INFO_MAX2) & FRAME_INFO_MASK; return pv->frame_info[i].duration; } static hb_buffer_t *nal_encode( hb_work_object_t *w, x264_picture_t *pic_out, int i_nal, x264_nal_t *nal ) { hb_buffer_t *buf = NULL; hb_work_private_t *pv = w->private_data; hb_job_t *job = pv->job; /* Should be way too large */ buf = hb_video_buffer_init( job->width, job->height ); buf->size = 0; buf->s.frametype = 0; // use the pts to get the original frame's duration. buf->s.duration = get_frame_duration( pv, pic_out->i_pts ); buf->s.start = pic_out->i_pts; buf->s.stop = buf->s.start + buf->s.duration; buf->s.renderOffset = pic_out->i_dts; if ( !w->config->h264.init_delay && pic_out->i_dts < 0 ) { w->config->h264.init_delay = -pic_out->i_dts; } /* Encode all the NALs we were given into buf. NOTE: This code assumes one video frame per NAL (but there can be other stuff like SPS and/or PPS). If there are multiple frames we only get the duration of the first which will eventually screw up the muxer & decoder. */ int i; for( i = 0; i < i_nal; i++ ) { int size = nal[i].i_payload; memcpy(buf->data + buf->size, nal[i].p_payload, size); if( size < 1 ) { continue; } /* H.264 in .mp4 or .mkv */ switch( nal[i].i_type ) { /* Sequence Parameter Set & Program Parameter Set go in the * mp4 header so skip them here */ case NAL_SPS: case NAL_PPS: continue; case NAL_SLICE: case NAL_SLICE_IDR: case NAL_SEI: default: break; } /* Decide what type of frame we have. */ switch( pic_out->i_type ) { case X264_TYPE_IDR: // Handled in b_keyframe check below. break; case X264_TYPE_I: buf->s.frametype = HB_FRAME_I; break; case X264_TYPE_P: buf->s.frametype = HB_FRAME_P; break; case X264_TYPE_B: buf->s.frametype = HB_FRAME_B; break; /* This is for b-pyramid, which has reference b-frames However, it doesn't seem to ever be used... */ case X264_TYPE_BREF: buf->s.frametype = HB_FRAME_BREF; break; // If it isn't the above, what type of frame is it?? default: buf->s.frametype = 0; break; } /* Since libx264 doesn't tell us when b-frames are themselves reference frames, figure it out on our own. */ if( (buf->s.frametype == HB_FRAME_B) && (nal[i].i_ref_idc != NAL_PRIORITY_DISPOSABLE) ) buf->s.frametype = HB_FRAME_BREF; /* Expose disposable bit to muxer. */ if( nal[i].i_ref_idc == NAL_PRIORITY_DISPOSABLE ) buf->s.flags &= ~HB_FRAME_REF; else buf->s.flags |= HB_FRAME_REF; // PIR has no IDR frames, but x264 marks recovery points // as keyframes. So fake an IDR at these points. This flag // is also set for real IDR frames. if( pic_out->b_keyframe ) { buf->s.frametype = HB_FRAME_IDR; /* if we have a chapter marker pending and this frame's presentation time stamp is at or after the marker's time stamp, use this as the chapter start. */ if (pv->next_chapter_pts != AV_NOPTS_VALUE && pv->next_chapter_pts <= pic_out->i_pts) { // we're no longer looking for this chapter pv->next_chapter_pts = AV_NOPTS_VALUE; // get the chapter index from the list struct chapter_s *item = hb_list_item(pv->delayed_chapters, 0); if (item != NULL) { // we're done with this chapter buf->s.new_chap = item->index; hb_list_rem(pv->delayed_chapters, item); free(item); // we may still have another pending chapter item = hb_list_item(pv->delayed_chapters, 0); if (item != NULL) { // we're looking for this one now // we still need it, don't remove it pv->next_chapter_pts = item->start; } } } } buf->size += size; } // make sure we found at least one video frame if ( buf->size <= 0 ) { // no video - discard the buf hb_buffer_close( &buf ); } return buf; } static hb_buffer_t *x264_encode( hb_work_object_t *w, hb_buffer_t *in ) { hb_work_private_t *pv = w->private_data; hb_job_t *job = pv->job; /* Point x264 at our current buffers Y(UV) data. */ pv->pic_in.img.i_stride[0] = in->plane[0].stride; pv->pic_in.img.i_stride[1] = in->plane[1].stride; pv->pic_in.img.i_stride[2] = in->plane[2].stride; pv->pic_in.img.plane[0] = in->plane[0].data; if( !job->grayscale ) { pv->pic_in.img.plane[1] = in->plane[1].data; pv->pic_in.img.plane[2] = in->plane[2].data; } if( in->s.new_chap && job->chapter_markers ) { /* chapters have to start with an IDR frame so request that this frame be coded as IDR. Since there may be up to 16 frames currently buffered in the encoder remember the timestamp so when this frame finally pops out of the encoder we'll mark its buffer as the start of a chapter. */ pv->pic_in.i_type = X264_TYPE_IDR; if (pv->next_chapter_pts == AV_NOPTS_VALUE) { pv->next_chapter_pts = in->s.start; } /* * Chapter markers are sometimes so close we can get a new one before the * previous marker has been through the encoding queue. * * Dropping markers can cause weird side-effects downstream, including but * not limited to missing chapters in the output, so we need to save it * somehow. */ struct chapter_s *item = malloc(sizeof(struct chapter_s)); if (item != NULL) { item->start = in->s.start; item->index = in->s.new_chap; hb_list_add(pv->delayed_chapters, item); } /* don't let 'work_loop' put a chapter mark on the wrong buffer */ in->s.new_chap = 0; } else { pv->pic_in.i_type = X264_TYPE_AUTO; } /* XXX this is temporary debugging code to check that the upstream * modules (render & sync) have generated a continuous, self-consistent * frame stream with the current frame's start time equal to the * previous frame's stop time. */ if( pv->last_stop != in->s.start ) { hb_log("encx264 input continuity err: last stop %"PRId64" start %"PRId64, pv->last_stop, in->s.start); } pv->last_stop = in->s.stop; // Remember info about this frame that we need to pass across // the x264_encoder_encode call (since it reorders frames). save_frame_info( pv, in ); /* Feed the input PTS to x264 so it can figure out proper output PTS */ pv->pic_in.i_pts = in->s.start; x264_picture_t pic_out; int i_nal; x264_nal_t *nal; x264_encoder_encode( pv->x264, &nal, &i_nal, &pv->pic_in, &pic_out ); if ( i_nal > 0 ) { return nal_encode( w, &pic_out, i_nal, nal ); } return NULL; } int encx264Work( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t *pv = w->private_data; hb_buffer_t *in = *buf_in; *buf_out = NULL; if( in->size <= 0 ) { // EOF on input. Flush any frames still in the decoder then // send the eof downstream to tell the muxer we're done. x264_picture_t pic_out; int i_nal; x264_nal_t *nal; hb_buffer_t *last_buf = NULL; while ( x264_encoder_delayed_frames( pv->x264 ) ) { x264_encoder_encode( pv->x264, &nal, &i_nal, NULL, &pic_out ); if ( i_nal == 0 ) continue; if ( i_nal < 0 ) break; hb_buffer_t *buf = nal_encode( w, &pic_out, i_nal, nal ); if ( buf ) { ++pv->frames_out; if ( last_buf == NULL ) *buf_out = buf; else last_buf->next = buf; last_buf = buf; } } // Flushed everything - add the eof to the end of the chain. if ( last_buf == NULL ) *buf_out = in; else last_buf->next = in; *buf_in = NULL; return HB_WORK_DONE; } // Not EOF - encode the packet & wrap it in a NAL ++pv->frames_in; ++pv->frames_out; *buf_out = x264_encode( w, in ); return HB_WORK_OK; } int hb_apply_h264_profile(x264_param_t *param, const char *h264_profile, int verbose) { if (h264_profile != NULL && strcasecmp(h264_profile, hb_h264_profile_names[0]) != 0) { /* * baseline profile doesn't support interlacing */ if ((param->b_interlaced || param->b_fake_interlaced) && !strcasecmp(h264_profile, "baseline")) { if (verbose) { hb_log("hb_apply_h264_profile [warning]: baseline profile doesn't support interlacing, disabling"); } param->b_interlaced = param->b_fake_interlaced = 0; } /* * lossless requires High 4:4:4 Predictive profile */ if (param->rc.f_rf_constant < 1.0 && param->rc.i_rc_method == X264_RC_CRF && strcasecmp(h264_profile, "high444") != 0) { if (verbose) { hb_log("hb_apply_h264_profile [warning]: lossless requires high444 profile, disabling"); } param->rc.f_rf_constant = 1.0; } if (!strcasecmp(h264_profile, "high10") || !strcasecmp(h264_profile, "high422")) { // arbitrary profile names may be specified via the CLI // map unsupported high10 and high422 profiles to high return x264_param_apply_profile(param, "high"); } return x264_param_apply_profile(param, h264_profile); } else if (!strcasecmp(h264_profile, hb_h264_profile_names[0])) { // "auto", do nothing return 0; } else { // error (profile not a string), abort hb_error("hb_apply_h264_profile: no profile specified"); return -1; } } int hb_check_h264_level(const char *h264_level, int width, int height, int fps_num, int fps_den, int interlaced, int fake_interlaced) { x264_param_t param; x264_param_default(¶m); param.i_width = width; param.i_height = height; param.i_fps_num = fps_num; param.i_fps_den = fps_den; param.b_interlaced = !!interlaced; param.b_fake_interlaced = !!fake_interlaced; return (hb_apply_h264_level(¶m, h264_level, NULL, 0) != 0); } int hb_apply_h264_level(x264_param_t *param, const char *h264_level, const char *h264_profile, int verbose) { float f_framerate; const x264_level_t *x264_level = NULL; int i, i_mb_size, i_mb_rate, i_mb_width, i_mb_height, max_mb_side, ret; /* * find the x264_level_t corresponding to the requested level */ if (h264_level != NULL && strcasecmp(h264_level, hb_h264_level_names[0]) != 0) { for (i = 0; hb_h264_level_values[i]; i++) { if (!strcmp(hb_h264_level_names[i], h264_level)) { int val = hb_h264_level_values[i]; for (i = 0; x264_levels[i].level_idc; i++) { if (x264_levels[i].level_idc == val) { x264_level = &x264_levels[i]; break; } } break; } } if (x264_level == NULL) { // error (invalid or unsupported level), abort hb_error("hb_apply_h264_level: invalid level %s", h264_level); return -1; } } else if(!strcasecmp(h264_level, hb_h264_level_names[0])) { // "auto", do nothing return 0; } else { // error (level not a string), abort hb_error("hb_apply_h264_level: no level specified"); return -1; } /* * the H.264 profile determines VBV constraints */ enum { // Main or Baseline (equivalent) HB_ENCX264_PROFILE_MAIN, // High (no 4:2:2 or 10-bit support, so anything lossy is equivalent) HB_ENCX264_PROFILE_HIGH, // Lossless (4:2:0 8-bit for now) HB_ENCX264_PROFILE_HIGH444, } hb_encx264_profile; /* * H.264 profile * * TODO: we need to guess the profile like x264_sps_init does, otherwise * we'll get an error when setting a Main-incompatible VBV and * x264_sps_init() guesses Main profile. x264_sps_init() may eventually take * VBV into account when guessing profile, at which point this code can be * re-enabled. */ #if 0 if (h264_profile != NULL && *h264_profile) { // if the user explicitly specified a profile, don't guess it if (!strcasecmp(h264_profile, "high444")) { hb_encx264_profile = HB_ENCX264_PROFILE_HIGH444; } else if (!strcasecmp(h264_profile, "main") || !strcasecmp(h264_profile, "baseline")) { hb_encx264_profile = HB_ENCX264_PROFILE_MAIN; } else { hb_encx264_profile = HB_ENCX264_PROFILE_HIGH; } } else #endif { // guess the H.264 profile if the user didn't request one if (param->rc.i_rc_method == X264_RC_CRF && param->rc.f_rf_constant < 1.0) { hb_encx264_profile = HB_ENCX264_PROFILE_HIGH444; } else if (param->analyse.b_transform_8x8 || param->i_cqm_preset != X264_CQM_FLAT) { hb_encx264_profile = HB_ENCX264_PROFILE_HIGH; } else { hb_encx264_profile = HB_ENCX264_PROFILE_MAIN; } } /* * we need at least width and height in order to apply a level correctly */ if (param->i_width <= 0 || param->i_height <= 0) { // error (invalid width or height), abort hb_error("hb_apply_h264_level: invalid resolution (width: %d, height: %d)", param->i_width, param->i_height); return -1; } /* * a return value of 1 means there were warnings */ ret = 0; /* * some levels do not support interlaced encoding */ if (x264_level->frame_only && (param->b_interlaced || param->b_fake_interlaced)) { if (verbose) { hb_log("hb_apply_h264_level [warning]: interlaced flag not supported for level %s, disabling", h264_level); } ret = 1; param->b_interlaced = param->b_fake_interlaced = 0; } /* * frame dimensions and rate (in macroblocks) */ i_mb_width = (param->i_width + 15) / 16; i_mb_height = (param->i_height + 15) / 16; if (param->b_interlaced || param->b_fake_interlaced) { // interlaced: encoded height must divide cleanly by 32 i_mb_height = (i_mb_height + 1) & ~1; } i_mb_size = i_mb_width * i_mb_height; if (param->i_fps_den <= 0 || param->i_fps_num <= 0) { i_mb_rate = 0; f_framerate = 0.0; } else { i_mb_rate = (int64_t)i_mb_size * param->i_fps_num / param->i_fps_den; f_framerate = (float)param->i_fps_num / param->i_fps_den; } /* * sanitize ref/frameref */ if (param->i_keyint_max != 1) { int i_max_dec_frame_buffering = MAX(MIN(x264_level->dpb / i_mb_size, 16), 1); param->i_frame_reference = MIN(i_max_dec_frame_buffering, param->i_frame_reference); /* * some level and resolution combos may require as little as 1 ref; * bframes and b-pyramid are not compatible with this scenario */ if (i_max_dec_frame_buffering < 2) { param->i_bframe = 0; } else if (i_max_dec_frame_buffering < 4) { param->i_bframe_pyramid = X264_B_PYRAMID_NONE; } } /* * set and/or sanitize the VBV (if not lossless) */ if (hb_encx264_profile != HB_ENCX264_PROFILE_HIGH444) { // High profile allows for higher VBV bufsize/maxrate int cbp_factor = hb_encx264_profile == HB_ENCX264_PROFILE_HIGH ? 5 : 4; if (!param->rc.i_vbv_max_bitrate) { param->rc.i_vbv_max_bitrate = (x264_level->bitrate * cbp_factor) / 4; } else { param->rc.i_vbv_max_bitrate = MIN(param->rc.i_vbv_max_bitrate, (x264_level->bitrate * cbp_factor) / 4); } if (!param->rc.i_vbv_buffer_size) { param->rc.i_vbv_buffer_size = (x264_level->cpb * cbp_factor) / 4; } else { param->rc.i_vbv_buffer_size = MIN(param->rc.i_vbv_buffer_size, (x264_level->cpb * cbp_factor) / 4); } } /* * sanitize mvrange/mv-range */ param->analyse.i_mv_range = MIN(param->analyse.i_mv_range, x264_level->mv_range >> !!param->b_interlaced); /* * TODO: check the rest of the limits */ /* * things we can do nothing about (too late to change resolution or fps), * print warnings if we're not being quiet */ if (x264_level->frame_size < i_mb_size) { if (verbose) { hb_log("hb_apply_h264_level [warning]: frame size (%dx%d, %d macroblocks) too high for level %s (max. %d macroblocks)", i_mb_width * 16, i_mb_height * 16, i_mb_size, h264_level, x264_level->frame_size); } ret = 1; } else if (x264_level->mbps < i_mb_rate) { if (verbose) { hb_log("hb_apply_h264_level [warning]: framerate (%.3f) too high for level %s at %dx%d (max. %.3f)", f_framerate, h264_level, param->i_width, param->i_height, (float)x264_level->mbps / i_mb_size); } ret = 1; } /* * width or height squared may not exceed 8 * frame_size (in macroblocks) * thus neither dimension may exceed sqrt(8 * frame_size) */ max_mb_side = sqrt(x264_level->frame_size * 8); if (i_mb_width > max_mb_side) { if (verbose) { hb_log("hb_apply_h264_level [warning]: frame too wide (%d) for level %s (max. %d)", param->i_width, h264_level, max_mb_side * 16); } ret = 1; } if (i_mb_height > max_mb_side) { if (verbose) { hb_log("hb_apply_h264_level [warning]: frame too tall (%d) for level %s (max. %d)", param->i_height, h264_level, max_mb_side * 16); } ret = 1; } /* * level successfully applied, yay! */ param->i_level_idc = x264_level->level_idc; return ret; } char * hb_x264_param_unparse(const char *x264_preset, const char *x264_tune, const char *x264_encopts, const char *h264_profile, const char *h264_level, int width, int height) { int i; char buf[32]; char *unparsed_opts; hb_dict_t *x264_opts; hb_dict_entry_t *entry; x264_param_t defaults, param; /* * get the global x264 defaults (what we compare against) */ x264_param_default(&defaults); /* * apply the defaults, preset and tune */ if (x264_param_default_preset(¶m, x264_preset, x264_tune) < 0) { /* * Note: GUIs should be able to always specifiy valid preset/tunes, so * this code will hopefully never be reached */ return strdup("hb_x264_param_unparse: invalid x264 preset/tune"); } /* * place additional x264 options in a dictionary */ entry = NULL; x264_opts = hb_encopts_to_dict(x264_encopts, HB_VCODEC_X264); /* * some libx264 options are set via dedicated widgets in the video tab or * hardcoded in libhb, and have no effect when present in the advanced x264 * options string. * * clear them from x264_opts so as to not apply then during unparse. */ hb_dict_unset(&x264_opts, "qp"); hb_dict_unset(&x264_opts, "qp_constant"); hb_dict_unset(&x264_opts, "crf"); hb_dict_unset(&x264_opts, "bitrate"); hb_dict_unset(&x264_opts, "fps"); hb_dict_unset(&x264_opts, "force-cfr"); hb_dict_unset(&x264_opts, "sar"); hb_dict_unset(&x264_opts, "annexb"); /* * apply the additional x264 options */ while ((entry = hb_dict_next(x264_opts, entry)) != NULL) { // let's not pollute GUI logs with x264_param_parse return codes x264_param_parse(¶m, entry->key, entry->value); } /* * apply the x264 profile, if specified */ if (h264_profile != NULL && *h264_profile) { // be quiet so at to not pollute GUI logs hb_apply_h264_profile(¶m, h264_profile, 0); } /* * apply the h264 level, if specified */ if (h264_level != NULL && *h264_level) { // set width/height to avoid issues in hb_apply_h264_level param.i_width = width; param.i_height = height; // be quiet so at to not pollute GUI logs hb_apply_h264_level(¶m, h264_level, h264_profile, 0); } /* * if x264_encopts is NULL, x264_opts wasn't initialized */ if (x264_opts == NULL && (x264_opts = hb_dict_init(20)) == NULL) { return strdup("hb_x264_param_unparse: could not initialize hb_dict_t"); } /* * x264 lets you specify some options in multiple ways. For options that we * do unparse, clear the forms that don't match how we unparse said option * from the x264_opts dictionary. * * actual synonyms are already handled by hb_encopts_to_dict(). * * "no-deblock" is a special case as it can't be unparsed to "deblock=0" * * also, don't bother with forms that aren't allowed by the x264 CLI, such * as "no-bframes" - there are too many. */ hb_dict_unset(&x264_opts, "no-sliced-threads"); hb_dict_unset(&x264_opts, "no-scenecut"); hb_dict_unset(&x264_opts, "no-b-adapt"); hb_dict_unset(&x264_opts, "no-weightb"); hb_dict_unset(&x264_opts, "no-cabac"); hb_dict_unset(&x264_opts, "interlaced"); // we unparse to tff/bff hb_dict_unset(&x264_opts, "no-interlaced"); hb_dict_unset(&x264_opts, "no-8x8dct"); hb_dict_unset(&x264_opts, "no-mixed-refs"); hb_dict_unset(&x264_opts, "no-fast-pskip"); hb_dict_unset(&x264_opts, "no-dct-decimate"); hb_dict_unset(&x264_opts, "no-psy"); hb_dict_unset(&x264_opts, "no-mbtree"); /* * compare defaults to param and unparse to the x264_opts dictionary */ if (!param.b_sliced_threads != !defaults.b_sliced_threads) { // can be modified by: tune zerolatency sprintf(buf, "%d", !!param.b_sliced_threads); hb_dict_set(&x264_opts, "sliced-threads", buf); } else { hb_dict_unset(&x264_opts, "sliced-threads"); } if (param.i_sync_lookahead != defaults.i_sync_lookahead) { // can be modified by: tune zerolatency sprintf(buf, "%d", param.i_sync_lookahead); hb_dict_set(&x264_opts, "sync-lookahead", buf); } else { hb_dict_unset(&x264_opts, "sync-lookahead"); } if (param.i_level_idc != defaults.i_level_idc) { // can be modified by: level for (i = 0; hb_h264_level_values[i]; i++) if (param.i_level_idc == hb_h264_level_values[i]) hb_dict_set(&x264_opts, "level", hb_h264_level_names[i]); } else { hb_dict_unset(&x264_opts, "level"); } if (param.i_frame_reference != defaults.i_frame_reference) { // can be modified by: presets, tunes, level sprintf(buf, "%d", param.i_frame_reference); hb_dict_set(&x264_opts, "ref", buf); } else { hb_dict_unset(&x264_opts, "ref"); } if (param.i_scenecut_threshold != defaults.i_scenecut_threshold) { // can be modified by: preset ultrafast sprintf(buf, "%d", param.i_scenecut_threshold); hb_dict_set(&x264_opts, "scenecut", buf); } else { hb_dict_unset(&x264_opts, "scenecut"); } if (param.i_bframe != defaults.i_bframe) { // can be modified by: presets, tunes, profile, level sprintf(buf, "%d", param.i_bframe); hb_dict_set(&x264_opts, "bframes", buf); } else { hb_dict_unset(&x264_opts, "bframes"); } if (param.i_bframe > 0) { if (param.i_bframe_adaptive != defaults.i_bframe_adaptive) { // can be modified by: presets sprintf(buf, "%d", param.i_bframe_adaptive); hb_dict_set(&x264_opts, "b-adapt", buf); } else { hb_dict_unset(&x264_opts, "b-adapt"); } if (param.i_bframe > 1 && param.i_bframe_pyramid != defaults.i_bframe_pyramid) { // can be modified by: level if (param.i_bframe_pyramid < X264_B_PYRAMID_NONE) param.i_bframe_pyramid = X264_B_PYRAMID_NONE; if (param.i_bframe_pyramid > X264_B_PYRAMID_NORMAL) param.i_bframe_pyramid = X264_B_PYRAMID_NORMAL; for (i = 0; x264_b_pyramid_names[i] != NULL; i++) if (param.i_bframe_pyramid == i) hb_dict_set(&x264_opts, "b-pyramid", x264_b_pyramid_names[i]); } else { hb_dict_unset(&x264_opts, "b-pyramid"); } if (param.analyse.i_direct_mv_pred != defaults.analyse.i_direct_mv_pred) { // can be modified by: presets if (param.analyse.i_direct_mv_pred < X264_DIRECT_PRED_NONE) param.analyse.i_direct_mv_pred = X264_DIRECT_PRED_NONE; if (param.analyse.i_direct_mv_pred > X264_DIRECT_PRED_AUTO) param.analyse.i_direct_mv_pred = X264_DIRECT_PRED_AUTO; for (i = 0; x264_direct_pred_names[i] != NULL; i++) if (param.analyse.i_direct_mv_pred == i) hb_dict_set(&x264_opts, "direct", x264_direct_pred_names[i]); } else { hb_dict_unset(&x264_opts, "direct"); } if (!param.analyse.b_weighted_bipred != !defaults.analyse.b_weighted_bipred) { // can be modified by: preset ultrafast, tune fastdecode sprintf(buf, "%d", !!param.analyse.b_weighted_bipred); hb_dict_set(&x264_opts, "weightb", buf); } else { hb_dict_unset(&x264_opts, "weightb"); } } else { // no bframes, these options have no effect hb_dict_unset(&x264_opts, "b-adapt"); hb_dict_unset(&x264_opts, "b-pyramid"); hb_dict_unset(&x264_opts, "direct"); hb_dict_unset(&x264_opts, "weightb"); hb_dict_unset(&x264_opts, "b-bias"); hb_dict_unset(&x264_opts, "open-gop"); } if (!param.b_deblocking_filter != !defaults.b_deblocking_filter) { // can be modified by: preset ultrafast, tune fastdecode sprintf(buf, "%d", !param.b_deblocking_filter); hb_dict_set(&x264_opts, "no-deblock", buf); } else { hb_dict_unset(&x264_opts, "no-deblock"); } if (param.b_deblocking_filter && (param.i_deblocking_filter_alphac0 != defaults.i_deblocking_filter_alphac0 || param.i_deblocking_filter_beta != defaults.i_deblocking_filter_beta)) { // can be modified by: tunes sprintf(buf, "%d,%d", param.i_deblocking_filter_alphac0, param.i_deblocking_filter_beta); hb_dict_set(&x264_opts, "deblock", buf); } else { hb_dict_unset(&x264_opts, "deblock"); } if (!param.b_cabac != !defaults.b_cabac) { // can be modified by: preset ultrafast, tune fastdecode, profile sprintf(buf, "%d", !!param.b_cabac); hb_dict_set(&x264_opts, "cabac", buf); } else { hb_dict_unset(&x264_opts, "cabac"); } if (param.b_interlaced != defaults.b_interlaced) { if (param.b_tff) { hb_dict_set(&x264_opts, "tff", "1"); hb_dict_unset(&x264_opts, "bff"); } else { hb_dict_set(&x264_opts, "bff", "1"); hb_dict_unset(&x264_opts, "tff"); } hb_dict_unset(&x264_opts, "fake-interlaced"); } else if (param.b_fake_interlaced != defaults.b_fake_interlaced) { hb_dict_set(&x264_opts, "fake-interlaced", "1"); hb_dict_unset(&x264_opts, "tff"); hb_dict_unset(&x264_opts, "bff"); } else { hb_dict_unset(&x264_opts, "tff"); hb_dict_unset(&x264_opts, "bff"); hb_dict_unset(&x264_opts, "fake-interlaced"); } if (param.i_cqm_preset == defaults.i_cqm_preset && param.psz_cqm_file == defaults.psz_cqm_file) { // can be reset to default by: profile hb_dict_unset(&x264_opts, "cqm"); hb_dict_unset(&x264_opts, "cqm4"); hb_dict_unset(&x264_opts, "cqm8"); hb_dict_unset(&x264_opts, "cqm4i"); hb_dict_unset(&x264_opts, "cqm4p"); hb_dict_unset(&x264_opts, "cqm8i"); hb_dict_unset(&x264_opts, "cqm8p"); hb_dict_unset(&x264_opts, "cqm4iy"); hb_dict_unset(&x264_opts, "cqm4ic"); hb_dict_unset(&x264_opts, "cqm4py"); hb_dict_unset(&x264_opts, "cqm4pc"); } /* * Note: param.analyse.intra can only be modified directly or by using * x264 --preset ultrafast, but not via the "analyse" option */ if (param.analyse.inter != defaults.analyse.inter) { // can be modified by: presets, tune touhou if (!param.analyse.inter) { hb_dict_set(&x264_opts, "analyse", "none"); } else if ((param.analyse.inter & X264_ANALYSE_I4x4) && (param.analyse.inter & X264_ANALYSE_I8x8) && (param.analyse.inter & X264_ANALYSE_PSUB16x16) && (param.analyse.inter & X264_ANALYSE_PSUB8x8) && (param.analyse.inter & X264_ANALYSE_BSUB16x16)) { hb_dict_set(&x264_opts, "analyse", "all"); } else { sprintf(buf, "%s", ""); if (param.analyse.inter & X264_ANALYSE_I4x4) { strcat(buf, "i4x4"); } if (param.analyse.inter & X264_ANALYSE_I8x8) { if (*buf) strcat(buf, ","); strcat(buf, "i8x8"); } if (param.analyse.inter & X264_ANALYSE_PSUB16x16) { if (*buf) strcat(buf, ","); strcat(buf, "p8x8"); } if (param.analyse.inter & X264_ANALYSE_PSUB8x8) { if (*buf) strcat(buf, ","); strcat(buf, "p4x4"); } if (param.analyse.inter & X264_ANALYSE_BSUB16x16) { if (*buf) strcat(buf, ","); strcat(buf, "b8x8"); } hb_dict_set(&x264_opts, "analyse", buf); } } else { hb_dict_unset(&x264_opts, "analyse"); } if (!param.analyse.b_transform_8x8 != !defaults.analyse.b_transform_8x8) { // can be modified by: preset ultrafast, profile sprintf(buf, "%d", !!param.analyse.b_transform_8x8); hb_dict_set(&x264_opts, "8x8dct", buf); } else { hb_dict_unset(&x264_opts, "8x8dct"); } if (param.analyse.i_weighted_pred != defaults.analyse.i_weighted_pred) { // can be modified by: presets, tune fastdecode, profile sprintf(buf, "%d", param.analyse.i_weighted_pred); hb_dict_set(&x264_opts, "weightp", buf); } else { hb_dict_unset(&x264_opts, "weightp"); } if (param.analyse.i_me_method != defaults.analyse.i_me_method) { // can be modified by: presets if (param.analyse.i_me_method < X264_ME_DIA) param.analyse.i_me_method = X264_ME_DIA; if (param.analyse.i_me_method > X264_ME_TESA) param.analyse.i_me_method = X264_ME_TESA; for (i = 0; x264_motion_est_names[i] != NULL; i++) if (param.analyse.i_me_method == i) hb_dict_set(&x264_opts, "me", x264_motion_est_names[i]); } else { hb_dict_unset(&x264_opts, "me"); } if (param.analyse.i_me_range != defaults.analyse.i_me_range) { // can be modified by: presets sprintf(buf, "%d", param.analyse.i_me_range); hb_dict_set(&x264_opts, "merange", buf); } else { hb_dict_unset(&x264_opts, "merange"); } if (param.analyse.i_mv_range != defaults.analyse.i_mv_range) { // can be modified by: level sprintf(buf, "%d", param.analyse.i_mv_range); hb_dict_set(&x264_opts, "mvrange", buf); } else { hb_dict_unset(&x264_opts, "mvrange"); } if (param.analyse.i_subpel_refine > 9 && (param.rc.i_aq_mode == 0 || param.analyse.i_trellis < 2)) { // subme 10 and higher require AQ and trellis 2 param.analyse.i_subpel_refine = 9; } if (param.analyse.i_subpel_refine != defaults.analyse.i_subpel_refine) { // can be modified by: presets sprintf(buf, "%d", param.analyse.i_subpel_refine); hb_dict_set(&x264_opts, "subme", buf); } else { hb_dict_unset(&x264_opts, "subme"); } if (!param.analyse.b_mixed_references != !defaults.analyse.b_mixed_references) { // can be modified by: presets sprintf(buf, "%d", !!param.analyse.b_mixed_references); hb_dict_set(&x264_opts, "mixed-refs", buf); } else { hb_dict_unset(&x264_opts, "mixed-refs"); } if (param.analyse.i_trellis != defaults.analyse.i_trellis) { // can be modified by: presets sprintf(buf, "%d", param.analyse.i_trellis); hb_dict_set(&x264_opts, "trellis", buf); } else { hb_dict_unset(&x264_opts, "trellis"); } if (!param.analyse.b_fast_pskip != !defaults.analyse.b_fast_pskip) { // can be modified by: preset placebo sprintf(buf, "%d", !!param.analyse.b_fast_pskip); hb_dict_set(&x264_opts, "fast-pskip", buf); } else { hb_dict_unset(&x264_opts, "fast-pskip"); } if (!param.analyse.b_dct_decimate != !defaults.analyse.b_dct_decimate) { // can be modified by: tune grain sprintf(buf, "%d", !!param.analyse.b_dct_decimate); hb_dict_set(&x264_opts, "dct-decimate", buf); } else { hb_dict_unset(&x264_opts, "dct-decimate"); } if (!param.analyse.b_psy != !defaults.analyse.b_psy) { // can be modified by: tunes sprintf(buf, "%d", !!param.analyse.b_psy); hb_dict_set(&x264_opts, "psy", buf); } else { hb_dict_unset(&x264_opts, "psy"); } if (param.analyse.b_psy && (param.analyse.f_psy_rd != defaults.analyse.f_psy_rd || param.analyse.f_psy_trellis != defaults.analyse.f_psy_trellis)) { // can be modified by: tunes sprintf(buf, "%.2f,%.2f", param.analyse.f_psy_rd, param.analyse.f_psy_trellis); hb_dict_set(&x264_opts, "psy-rd", buf); } else { hb_dict_unset(&x264_opts, "psy-rd"); } /* * Note: while deadzone is incompatible with trellis, it still has a slight * effect on the output even when trellis is on, so always unparse it. */ if (param.analyse.i_luma_deadzone[0] != defaults.analyse.i_luma_deadzone[0]) { // can be modified by: tune grain sprintf(buf, "%d", param.analyse.i_luma_deadzone[0]); hb_dict_set(&x264_opts, "deadzone-inter", buf); } else { hb_dict_unset(&x264_opts, "deadzone-inter"); } if (param.analyse.i_luma_deadzone[1] != defaults.analyse.i_luma_deadzone[1]) { // can be modified by: tune grain sprintf(buf, "%d", param.analyse.i_luma_deadzone[1]); hb_dict_set(&x264_opts, "deadzone-intra", buf); } else { hb_dict_unset(&x264_opts, "deadzone-intra"); } if (param.rc.i_vbv_buffer_size != defaults.rc.i_vbv_buffer_size) { // can be modified by: level sprintf(buf, "%d", param.rc.i_vbv_buffer_size); hb_dict_set(&x264_opts, "vbv-bufsize", buf); if (param.rc.i_vbv_max_bitrate != defaults.rc.i_vbv_max_bitrate) { // can be modified by: level sprintf(buf, "%d", param.rc.i_vbv_max_bitrate); hb_dict_set(&x264_opts, "vbv-maxrate", buf); } else { hb_dict_unset(&x264_opts, "vbv-maxrate"); } } else { hb_dict_unset(&x264_opts, "vbv-bufsize"); hb_dict_unset(&x264_opts, "vbv-maxrate"); } if (param.rc.f_ip_factor != defaults.rc.f_ip_factor) { // can be modified by: tune grain sprintf(buf, "%.2f", param.rc.f_ip_factor); hb_dict_set(&x264_opts, "ipratio", buf); } else { hb_dict_unset(&x264_opts, "ipratio"); } if (param.i_bframe > 0 && !param.rc.b_mb_tree && param.rc.f_pb_factor != defaults.rc.f_pb_factor) { // can be modified by: tune grain sprintf(buf, "%.2f", param.rc.f_pb_factor); hb_dict_set(&x264_opts, "pbratio", buf); } else { // pbratio requires bframes and is incomaptible with mbtree hb_dict_unset(&x264_opts, "pbratio"); } if (param.rc.f_qcompress != defaults.rc.f_qcompress) { // can be modified by: tune grain sprintf(buf, "%.2f", param.rc.f_qcompress); hb_dict_set(&x264_opts, "qcomp", buf); } else { hb_dict_unset(&x264_opts, "qcomp"); } if (param.rc.i_aq_mode != defaults.rc.i_aq_mode) { // can be modified by: preset ultrafast, tune psnr sprintf(buf, "%d", param.rc.i_aq_mode); hb_dict_set(&x264_opts, "aq-mode", buf); } else { hb_dict_unset(&x264_opts, "aq-mode"); } if (param.rc.i_aq_mode > 0 && param.rc.f_aq_strength != defaults.rc.f_aq_strength) { // can be modified by: tunes sprintf(buf, "%.2f", param.rc.f_aq_strength); hb_dict_set(&x264_opts, "aq-strength", buf); } else { hb_dict_unset(&x264_opts, "aq-strength"); } if (!param.rc.b_mb_tree != !defaults.rc.b_mb_tree) { // can be modified by: presets, tune zerolatency sprintf(buf, "%d", !!param.rc.b_mb_tree); hb_dict_set(&x264_opts, "mbtree", buf); } else { hb_dict_unset(&x264_opts, "mbtree"); } if (param.rc.i_lookahead != defaults.rc.i_lookahead) { // can be modified by: presets, tune zerolatency sprintf(buf, "%d", param.rc.i_lookahead); hb_dict_set(&x264_opts, "rc-lookahead", buf); } else { hb_dict_unset(&x264_opts, "rc-lookahead"); } if (!param.b_vfr_input != !defaults.b_vfr_input) { // can be modified by: tune zerolatency sprintf(buf, "%d", !param.b_vfr_input); hb_dict_set(&x264_opts, "force-cfr", buf); } else { hb_dict_unset(&x264_opts, "force-cfr"); } /* convert the x264_opts dictionary to an encopts string */ unparsed_opts = hb_dict_to_encopts(x264_opts); hb_dict_free(&x264_opts); /* we're done */ return unparsed_opts; } const char * const * hb_x264_presets() { return x264_preset_names; } const char * const * hb_x264_tunes() { return x264_tune_names; } const char * const * hb_h264_profiles() { return hb_h264_profile_names; } const char * const * hb_h264_levels() { return hb_h264_level_names; } const char * hb_x264_encopt_name(const char *name) { int i; for (i = 0; hb_x264_encopt_synonyms[i][0] != NULL; i++) if (!strcmp(name, hb_x264_encopt_synonyms[i][1])) return hb_x264_encopt_synonyms[i][0]; return name; } HandBrake-0.10.2/libhb/qsv_filter.h0000664000175200017520000000337712205472744017467 0ustar handbrakehandbrake/* ********************************************************************* *\ Copyright (C) 2013 Intel Corporation. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \* ********************************************************************* */ #ifndef QSV_FILTER_H #define QSV_FILTER_H hb_buffer_t *link_buf_list( hb_filter_private_t *pv ); void qsv_filter_close( av_qsv_context* qsv, AV_QSV_STAGE_TYPE vpp_type ); #endif // QSV_FILTER_H HandBrake-0.10.2/libhb/qsv_filter.c0000664000175200017520000006464312317653727017473 0ustar handbrakehandbrake/* ********************************************************************* *\ Copyright (C) 2013 Intel Corporation. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \* ********************************************************************* */ #ifdef USE_QSV #include "hb.h" #include "hbffmpeg.h" #include "libavcodec/qsv.h" #include "qsv_filter.h" struct hb_filter_private_s { hb_job_t *job; hb_list_t *list; int width_in; int height_in; int pix_fmt; int pix_fmt_out; int width_out; int height_out; int crop[4]; int deinterlace; int is_frc_used; // set during init, used to configure input surfaces' "area of interest" mfxU16 CropX; mfxU16 CropY; mfxU16 CropH; mfxU16 CropW; av_qsv_space *vpp_space; // FRC param(s) mfxExtVPPFrameRateConversion frc_config; }; static int hb_qsv_filter_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int hb_qsv_filter_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static int hb_qsv_filter_info( hb_filter_object_t * filter, hb_filter_info_t * info ); static void hb_qsv_filter_close( hb_filter_object_t * filter ); hb_filter_object_t hb_filter_qsv = { .id = HB_FILTER_QSV, .enforce_order = 1, .name = "Quick Sync Video VPP", .settings = NULL, .init = hb_qsv_filter_init, .work = hb_qsv_filter_work, .close = hb_qsv_filter_close, .info = hb_qsv_filter_info, }; static int filter_init( av_qsv_context* qsv, hb_filter_private_t * pv ){ mfxStatus sts; int i=0; if(!qsv) return 3; if(!qsv->vpp_space){ qsv->vpp_space = av_qsv_list_init(HAVE_THREADS); } if(!pv->vpp_space){ for(i=0; ivpp_space);i++){ av_qsv_space *qsv_vpp = av_qsv_list_item( qsv->vpp_space, i ); if(qsv_vpp->type == AV_QSV_VPP_DEFAULT){ pv->vpp_space = qsv_vpp; break; } } } if(!pv->vpp_space){ pv->vpp_space = calloc( 1, sizeof( av_qsv_space )); pv->vpp_space->type = AV_QSV_VPP_DEFAULT; av_qsv_list_add( qsv->vpp_space, pv->vpp_space ); } else if(pv->vpp_space->is_init_done ) return 1; if(!qsv->dec_space || !qsv->dec_space->is_init_done) return 2; // we need to know final output settings before we can properly configure if (!pv->job->qsv.enc_info.is_init_done) { return 2; } av_qsv_add_context_usage(qsv,HAVE_THREADS); // see params needed like at mediasdk-man.pdf:"Appendix A: Configuration Parameter Constraints" // for now - most will take from the decode { av_qsv_space *qsv_vpp = pv->vpp_space; AV_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam); if (pv->deinterlace) { /* * Input may be progressive, interlaced or even mixed, so init with * MFX_PICSTRUCT_UNKNOWN and use per-frame field order information * (mfxFrameSurface1.Info.PicStruct) */ qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct = MFX_PICSTRUCT_UNKNOWN; qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; } else { /* Same PicStruct in/out: no filtering */ qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct; qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct; } // FrameRate is important for VPP to start with if( qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN == 0 && qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD == 0 ){ qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN = pv->job->title->rate; qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD = pv->job->title->rate_base; } /* * In theory, input width/height and decode CropW/CropH should be the * same; however, due to some versions of Libav not applying the H.264 * "crop rect" properly, there can be a mismatch. * * Since we want the same bahevior regardless of whether we're using * software or hardware-accelerated decoding, prefer the Libav values. * * Note that since CropW/CropH may be higher than the decode values, we * need to adjust CropX/CropY to make sure we don't exceed the input's * Width/Height boundaries. */ pv->CropW = pv-> width_in; pv->CropH = pv->height_in; pv->CropX = FFMIN(qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropX, qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.Width - pv->CropW); pv->CropY = FFMIN(qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropY, qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.Height - pv->CropH); /* Then, apply additional cropping requested by the user, if any */ pv->CropX += pv->crop[2]; pv->CropY += pv->crop[0]; pv->CropW -= pv->crop[2] + pv->crop[3]; pv->CropH -= pv->crop[0] + pv->crop[1]; qsv_vpp->m_mfxVideoParam.vpp.In.FourCC = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC; qsv_vpp->m_mfxVideoParam.vpp.In.ChromaFormat = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat; qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtN = pv->job->vrate; qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtD = pv->job->vrate_base; qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioW = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW; qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioH = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH; qsv_vpp->m_mfxVideoParam.vpp.In.Width = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.Width; qsv_vpp->m_mfxVideoParam.vpp.In.Height = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.Height; qsv_vpp->m_mfxVideoParam.vpp.In.CropX = pv->CropX; qsv_vpp->m_mfxVideoParam.vpp.In.CropY = pv->CropY; qsv_vpp->m_mfxVideoParam.vpp.In.CropW = pv->CropW; qsv_vpp->m_mfxVideoParam.vpp.In.CropH = pv->CropH; qsv_vpp->m_mfxVideoParam.vpp.Out.FourCC = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC; qsv_vpp->m_mfxVideoParam.vpp.Out.ChromaFormat = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat; qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN = pv->job->vrate; qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD = pv->job->vrate_base; qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioW = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW; qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioH = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH; qsv_vpp->m_mfxVideoParam.vpp.Out.Width = pv->job->qsv.enc_info.align_width; qsv_vpp->m_mfxVideoParam.vpp.Out.Height = pv->job->qsv.enc_info.align_height; qsv_vpp->m_mfxVideoParam.vpp.Out.CropX = 0; // no letterboxing qsv_vpp->m_mfxVideoParam.vpp.Out.CropY = 0; // no pillarboxing qsv_vpp->m_mfxVideoParam.vpp.Out.CropW = pv-> width_out; qsv_vpp->m_mfxVideoParam.vpp.Out.CropH = pv->height_out; qsv_vpp->m_mfxVideoParam.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY | MFX_IOPATTERN_OUT_OPAQUE_MEMORY; qsv_vpp->m_mfxVideoParam.AsyncDepth = pv->job->qsv.async_depth; memset(&qsv_vpp->request, 0, sizeof(mfxFrameAllocRequest)*2); sts = MFXVideoVPP_QueryIOSurf(qsv->mfx_session, &qsv_vpp->m_mfxVideoParam, qsv_vpp->request ); AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); int num_surfaces_in = qsv_vpp->request[0].NumFrameSuggested; int num_surfaces_out = qsv_vpp->request[1].NumFrameSuggested; av_qsv_config *config = qsv->qsv_config; qsv_vpp->surface_num = FFMIN( num_surfaces_in + num_surfaces_out + qsv_vpp->m_mfxVideoParam.AsyncDepth + config ? config->additional_buffers/2 :0 , AV_QSV_SURFACE_NUM ); if(qsv_vpp->surface_num <= 0 ) qsv_vpp->surface_num = AV_QSV_SURFACE_NUM; int i = 0; for (i = 0; i < qsv_vpp->surface_num; i++){ qsv_vpp->p_surfaces[i] = av_mallocz( sizeof(mfxFrameSurface1) ); AV_QSV_CHECK_POINTER(qsv_vpp->p_surfaces[i], MFX_ERR_MEMORY_ALLOC); memcpy(&(qsv_vpp->p_surfaces[i]->Info), &(qsv_vpp->m_mfxVideoParam.vpp.Out), sizeof(mfxFrameInfo)); } qsv_vpp->sync_num = FFMIN( qsv_vpp->surface_num, AV_QSV_SYNC_NUM ); for (i = 0; i < qsv_vpp->sync_num; i++){ qsv_vpp->p_syncp[i] = av_mallocz(sizeof(av_qsv_sync)); AV_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i], MFX_ERR_MEMORY_ALLOC); qsv_vpp->p_syncp[i]->p_sync = av_mallocz(sizeof(mfxSyncPoint)); AV_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC); } /* about available VPP filters, see "Table 4 Configurable VPP filters", mediasdk-man.pdf Hints (optional feature) IDs: MFX_EXTBUFF_VPP_DENOISE // Remove noise // Value of 0-100 (inclusive) indicates // the level of noise to remove. MFX_EXTBUFF_VPP_DETAIL // Enhance picture details/edges: // 0-100 value (inclusive) to indicate // the level of details to be enhanced. MFX_EXTBUFF_VPP_FRAME_RATE_CONVERSION // Convert input frame rate to match the output, based on frame interpolation: // MFX_FRCALGM_PRESERVE_TIMESTAMP, // MFX_FRCALGM_DISTRIBUTED_TIMESTAMP, // MFX_FRCALGM_FRAME_INTERPOLATION MFX_EXTBUFF_VPP_IMAGE_STABILIZATION // Perform image stabilization // Stabilization modes: // MFX_IMAGESTAB_MODE_UPSCALE // MFX_IMAGESTAB_MODE_BOXING MFX_EXTBUFF_VPP_PICSTRUCT_DETECTION // Perform detection of picture structure: // Detected picture structure - top field first, bottom field first, progressive or unknown // if video processor cannot detect picture structure. MFX_EXTBUFF_VPP_PROCAMP // Adjust the brightness, contrast, saturation, and hue settings // Initialize extended buffer for frame processing // - Process amplifier (ProcAmp) used to control brightness // - mfxExtVPPDoUse: Define the processing algorithm to be used // - mfxExtVPPProcAmp: ProcAmp configuration // - mfxExtBuffer: Add extended buffers to VPP parameter configuration mfxExtVPPDoUse extDoUse; mfxU32 tabDoUseAlg[1]; extDoUse.Header.BufferId = MFX_EXTBUFF_VPP_DOUSE; extDoUse.Header.BufferSz = sizeof(mfxExtVPPDoUse); extDoUse.NumAlg = 1; extDoUse.AlgList = tabDoUseAlg; tabDoUseAlg[0] = MFX_EXTBUFF_VPP_PROCAMP; mfxExtVPPProcAmp procampConfig; procampConfig.Header.BufferId = MFX_EXTBUFF_VPP_PROCAMP; procampConfig.Header.BufferSz = sizeof(mfxExtVPPProcAmp); procampConfig.Hue = 0.0f; // Default procampConfig.Saturation = 1.0f; // Default procampConfig.Contrast = 1.0; // Default procampConfig.Brightness = 40.0; // Adjust brightness mfxExtBuffer* ExtBuffer[2]; ExtBuffer[0] = (mfxExtBuffer*)&extDoUse; ExtBuffer[1] = (mfxExtBuffer*)&procampConfig; VPPParams.NumExtParam = 2; VPPParams.ExtParam = (mfxExtBuffer**)&ExtBuffer[0]; */ memset(&qsv_vpp->ext_opaque_alloc, 0, sizeof(qsv_vpp->ext_opaque_alloc)); if( (qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtN / qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtD ) != (qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN / qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD) ) { pv->is_frc_used = 1; } qsv_vpp->m_mfxVideoParam.NumExtParam = qsv_vpp->p_ext_param_num = 1 + pv->is_frc_used; qsv_vpp->p_ext_params = av_mallocz(sizeof(mfxExtBuffer *)*qsv_vpp->p_ext_param_num); AV_QSV_CHECK_POINTER(qsv_vpp->p_ext_params, MFX_ERR_MEMORY_ALLOC); qsv_vpp->m_mfxVideoParam.ExtParam = qsv_vpp->p_ext_params; qsv_vpp->ext_opaque_alloc.In.Surfaces = qsv->dec_space->p_surfaces; qsv_vpp->ext_opaque_alloc.In.NumSurface = qsv->dec_space->surface_num; qsv_vpp->ext_opaque_alloc.In.Type = qsv->dec_space->request[0].Type; qsv_vpp->ext_opaque_alloc.Out.Surfaces = qsv_vpp->p_surfaces; qsv_vpp->ext_opaque_alloc.Out.NumSurface = qsv_vpp->surface_num; qsv_vpp->ext_opaque_alloc.Out.Type = qsv->dec_space->request[0].Type; qsv_vpp->ext_opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION; qsv_vpp->ext_opaque_alloc.Header.BufferSz = sizeof(mfxExtOpaqueSurfaceAlloc); qsv_vpp->p_ext_params[0] = (mfxExtBuffer*)&qsv_vpp->ext_opaque_alloc; if(pv->is_frc_used) { pv->frc_config.Header.BufferId = MFX_EXTBUFF_VPP_FRAME_RATE_CONVERSION; pv->frc_config.Header.BufferSz = sizeof(mfxExtVPPFrameRateConversion); pv->frc_config.Algorithm = MFX_FRCALGM_PRESERVE_TIMESTAMP; qsv_vpp->p_ext_params[1] = (mfxExtBuffer*)&pv->frc_config; } sts = MFXVideoVPP_Init(qsv->mfx_session, &qsv_vpp->m_mfxVideoParam); AV_QSV_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION); AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); qsv_vpp->is_init_done = 1; } return 0; } static int hb_qsv_filter_init( hb_filter_object_t * filter, hb_filter_init_t * init ) { filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); hb_filter_private_t * pv = filter->private_data; pv->list = hb_list_init(); // list of init params provided at work.c:~700 pv->width_in = init->width; pv->height_in = init->height; pv->width_out = init->width; pv->height_out = init->height; memcpy( pv->crop, init->crop, sizeof( int[4] ) ); if (filter->settings != NULL) { sscanf(filter->settings, "%d:%d:%d:%d:%d:%d_dei:%d", &pv->width_out, &pv->height_out, &pv->crop[0], &pv->crop[1], &pv->crop[2], &pv->crop[3], &pv->deinterlace); } pv->job = init->job; // will be later as more params will be known // filter_init(pv->job->qsv, pv); // just passing init->vrate = init->vrate; init->vrate_base = init->vrate_base; // framerate shaping not yet supported init->cfr = 0; init->pix_fmt = pv->pix_fmt; init->width = pv->width_out; init->height = pv->height_out; memcpy( init->crop, pv->crop, sizeof( int[4] ) ); return 0; } static int hb_qsv_filter_info( hb_filter_object_t * filter, hb_filter_info_t * info ) { hb_filter_private_t *pv = filter->private_data; if (pv == NULL) return -1; sprintf(info->human_readable_desc, "source: %d * %d, crop (%d/%d/%d/%d): %d * %d, scale: %d * %d", pv->width_in, pv->height_in, pv->crop[0], pv->crop[1], pv->crop[2], pv->crop[3], pv->width_in - pv->crop[2] - pv->crop[3], pv->height_in - pv->crop[0] - pv->crop[1], pv->width_out, pv->height_out); if (pv->deinterlace) { sprintf(info->human_readable_desc + strlen(info->human_readable_desc), ", deinterlace"); } return 0; } void qsv_filter_close( av_qsv_context* qsv, AV_QSV_STAGE_TYPE vpp_type ){ int i = 0; av_qsv_space* vpp_space = 0; if(qsv && qsv->is_context_active && qsv->vpp_space) for(i=av_qsv_list_count( qsv->vpp_space);i>0;i--){ vpp_space = av_qsv_list_item( qsv->vpp_space, i-1 ); if( vpp_space->type == vpp_type && vpp_space->is_init_done){ hb_log( "qsv_filter[%s] done: max_surfaces: %u/%u , max_syncs: %u/%u", ((vpp_type == AV_QSV_VPP_DEFAULT)?"Default": "User") ,vpp_space->surface_num_max_used, vpp_space->surface_num, vpp_space->sync_num_max_used, vpp_space->sync_num ); for (i = 0; i < vpp_space->surface_num; i++){ av_freep(&vpp_space->p_surfaces[i]); } vpp_space->surface_num = 0; if( vpp_space->p_ext_param_num || vpp_space->p_ext_params ) av_freep(&vpp_space->p_ext_params); vpp_space->p_ext_param_num = 0; for (i = 0; i < vpp_space->sync_num; i++){ av_freep(&vpp_space->p_syncp[i]->p_sync); av_freep(&vpp_space->p_syncp[i]); } vpp_space->sync_num = 0; av_qsv_list_rem(qsv->vpp_space,vpp_space); if( av_qsv_list_count(qsv->vpp_space) == 0 ) av_qsv_list_close(&qsv->vpp_space); vpp_space->is_init_done = 0; break; } } } static void hb_qsv_filter_close( hb_filter_object_t * filter ) { int i = 0; hb_filter_private_t * pv = filter->private_data; if ( !pv ) { return; } av_qsv_context* qsv = pv->job->qsv.ctx; if(qsv && qsv->vpp_space && av_qsv_list_count(qsv->vpp_space) > 0){ // closing local stuff qsv_filter_close(qsv,AV_QSV_VPP_DEFAULT); // closing the commong stuff av_qsv_context_clean(qsv); } hb_list_close(&pv->list); free( pv ); filter->private_data = NULL; } int process_frame(av_qsv_list* received_item, av_qsv_context* qsv, hb_filter_private_t * pv ){ // 1 if have results , 0 - otherwise int ret = 1; mfxStatus sts = MFX_ERR_NONE; mfxFrameSurface1 *work_surface = NULL; av_qsv_stage* stage = 0; av_qsv_space *qsv_vpp = pv->vpp_space; if(received_item){ stage = av_qsv_get_last_stage( received_item ); work_surface = stage->out.p_surface; } int sync_idx = av_qsv_get_free_sync(qsv_vpp, qsv); int surface_idx = -1; for(;;) { if (sync_idx == -1) { hb_error("qsv: Not enough resources allocated for QSV filter"); ret = 0; break; } if( sts == MFX_ERR_MORE_SURFACE || sts == MFX_ERR_NONE ) surface_idx = av_qsv_get_free_surface(qsv_vpp, qsv, &(qsv_vpp->m_mfxVideoParam.vpp.Out), QSV_PART_ANY); if (surface_idx == -1) { hb_error("qsv: Not enough resources allocated for QSV filter"); ret = 0; break; } if (work_surface != NULL) { work_surface->Info.CropX = pv->CropX; work_surface->Info.CropY = pv->CropY; work_surface->Info.CropW = pv->CropW; work_surface->Info.CropH = pv->CropH; } sts = MFXVideoVPP_RunFrameVPPAsync(qsv->mfx_session, work_surface, qsv_vpp->p_surfaces[surface_idx] , NULL, qsv_vpp->p_syncp[sync_idx]->p_sync); if( MFX_ERR_MORE_DATA == sts ){ if(!qsv_vpp->pending){ qsv_vpp->pending = av_qsv_list_init(0); } // if we have no results, we should not miss resource(s) av_qsv_list_add( qsv_vpp->pending, received_item); ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use); ret = 0; break; } if( MFX_ERR_MORE_DATA == sts || (MFX_ERR_NONE <= sts && MFX_WRN_DEVICE_BUSY != sts)){ if (work_surface){ ff_qsv_atomic_dec(&work_surface->Data.Locked); } } if( MFX_ERR_MORE_SURFACE == sts || MFX_ERR_NONE <= sts){ if( MFX_ERR_MORE_SURFACE == sts ) continue; if (qsv_vpp->p_surfaces[surface_idx] && MFX_WRN_DEVICE_BUSY != sts ) ff_qsv_atomic_inc(&qsv_vpp->p_surfaces[surface_idx]->Data.Locked); } AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); if (MFX_ERR_NONE <= sts ) // repeat the call if warning and no output { if (MFX_WRN_DEVICE_BUSY == sts){ av_qsv_sleep(10); // wait if device is busy continue; } // shouldnt be a case but drain if(stage){ av_qsv_stage* new_stage = av_qsv_stage_init(); new_stage->type = AV_QSV_VPP_DEFAULT; new_stage->in.p_surface = work_surface; new_stage->out.p_surface = qsv_vpp->p_surfaces[surface_idx]; new_stage->out.sync = qsv_vpp->p_syncp[sync_idx]; av_qsv_add_stagee( &received_item, new_stage,HAVE_THREADS ); // add pending resources for the proper reclaim later if( qsv_vpp->pending ){ if( av_qsv_list_count(qsv_vpp->pending)>0 ){ new_stage->pending = qsv_vpp->pending; } qsv_vpp->pending = 0; // making free via decrement for all pending int i = 0; for (i = av_qsv_list_count(new_stage->pending); i > 0; i--){ av_qsv_list *atom_list = av_qsv_list_item(new_stage->pending, i-1); av_qsv_stage *stage = av_qsv_get_last_stage( atom_list ); mfxFrameSurface1 *work_surface = stage->out.p_surface; if (work_surface) ff_qsv_atomic_dec(&work_surface->Data.Locked); } } } break; } ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use); if (MFX_ERR_NOT_ENOUGH_BUFFER == sts) HB_DEBUG_ASSERT(1, "The bitstream buffer size is insufficient."); break; } return ret; } static int hb_qsv_filter_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in; hb_buffer_t * out = *buf_out; int sts = 0; av_qsv_context* qsv = pv->job->qsv.ctx; if ( !pv ) { *buf_out = in; *buf_in = NULL; return HB_FILTER_OK; } while(1){ int ret = filter_init(qsv,pv); if(ret >= 2) av_qsv_sleep(1); else break; } *buf_in = NULL; if ( in->size <= 0 ) { while(1){ sts = process_frame(in->qsv_details.qsv_atom, qsv, pv); if(sts) hb_list_add(pv->list,in); else break; } hb_list_add( pv->list, in ); *buf_out = link_buf_list( pv ); return HB_FILTER_DONE; } sts = process_frame(in->qsv_details.qsv_atom, qsv, pv); if(sts){ hb_list_add(pv->list,in); } if( hb_list_count(pv->list) ){ *buf_out = hb_list_item(pv->list,0); out = *buf_out; if(pv->is_frc_used && out) { mfxStatus sts = MFX_ERR_NONE; if(out->qsv_details.qsv_atom){ av_qsv_stage* stage = av_qsv_get_last_stage( out->qsv_details.qsv_atom ); mfxFrameSurface1 *work_surface = stage->out.p_surface; av_qsv_wait_on_sync( qsv,stage ); av_qsv_space *qsv_vpp = pv->vpp_space; int64_t duration = ((double)qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD/(double)qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN ) * 90000.; out->s.start = work_surface->Data.TimeStamp; out->s.stop = work_surface->Data.TimeStamp + duration; } } hb_list_rem(pv->list,*buf_out); } else *buf_out = NULL; return HB_FILTER_OK; } // see devavcode.c hb_buffer_t *link_buf_list( hb_filter_private_t *pv ) { hb_buffer_t *head = hb_list_item( pv->list, 0 ); if ( head ) { hb_list_rem( pv->list, head ); hb_buffer_t *last = head, *buf; while ( ( buf = hb_list_item( pv->list, 0 ) ) != NULL ) { hb_list_rem( pv->list, buf ); last->next = buf; last = buf; } } return head; } #endif // USE_QSV HandBrake-0.10.2/libhb/opencl.c0000664000175200017520000003120212463330511016537 0ustar handbrakehandbrake/* opencl.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifdef _WIN32 #include #define HB_OCL_DLOPEN LoadLibraryW(L"OpenCL") #define HB_OCL_DLSYM GetProcAddress #define HB_OCL_DLCLOSE FreeLibrary #else #include #ifdef __APPLE__ #define HB_OCL_DLOPEN dlopen("/System/Library/Frameworks/OpenCL.framework/OpenCL", RTLD_NOW) #else #define HB_OCL_DLOPEN dlopen("libOpenCL.so", RTLD_NOW) #endif #define HB_OCL_DLSYM dlsym #define HB_OCL_DLCLOSE dlclose #endif #include "common.h" #include "opencl.h" hb_opencl_library_t *hb_ocl = NULL; int hb_ocl_init() { if (hb_ocl == NULL) { if ((hb_ocl = hb_opencl_library_init()) == NULL) { return -1; } } return 0; } void hb_ocl_close() { hb_opencl_library_close(&hb_ocl); } hb_opencl_library_t* hb_opencl_library_init() { hb_opencl_library_t *opencl; if ((opencl = calloc(1, sizeof(hb_opencl_library_t))) == NULL) { hb_error("hb_opencl_library_init: memory allocation failure"); goto fail; } opencl->library = HB_OCL_DLOPEN; if (opencl->library == NULL) { goto fail; } #define HB_OCL_LOAD(func) \ { \ if ((opencl->func = (void*)HB_OCL_DLSYM(opencl->library, #func)) == NULL) \ { \ hb_log("hb_opencl_library_init: failed to load function '%s'", #func); \ goto fail; \ } \ } HB_OCL_LOAD(clBuildProgram); HB_OCL_LOAD(clCreateBuffer); HB_OCL_LOAD(clCreateCommandQueue); HB_OCL_LOAD(clCreateContextFromType); HB_OCL_LOAD(clCreateKernel); HB_OCL_LOAD(clCreateProgramWithBinary); HB_OCL_LOAD(clCreateProgramWithSource); HB_OCL_LOAD(clEnqueueCopyBuffer); HB_OCL_LOAD(clEnqueueMapBuffer); HB_OCL_LOAD(clEnqueueNDRangeKernel); HB_OCL_LOAD(clEnqueueReadBuffer); HB_OCL_LOAD(clEnqueueUnmapMemObject); HB_OCL_LOAD(clEnqueueWriteBuffer); HB_OCL_LOAD(clFlush); HB_OCL_LOAD(clGetCommandQueueInfo); HB_OCL_LOAD(clGetContextInfo); HB_OCL_LOAD(clGetDeviceIDs); HB_OCL_LOAD(clGetDeviceInfo); HB_OCL_LOAD(clGetPlatformIDs); HB_OCL_LOAD(clGetPlatformInfo); HB_OCL_LOAD(clGetProgramBuildInfo); HB_OCL_LOAD(clGetProgramInfo); HB_OCL_LOAD(clReleaseCommandQueue); HB_OCL_LOAD(clReleaseContext); HB_OCL_LOAD(clReleaseEvent); HB_OCL_LOAD(clReleaseKernel); HB_OCL_LOAD(clReleaseMemObject); HB_OCL_LOAD(clReleaseProgram); HB_OCL_LOAD(clSetKernelArg); HB_OCL_LOAD(clWaitForEvents); //success return opencl; fail: hb_opencl_library_close(&opencl); return NULL; } void hb_opencl_library_close(hb_opencl_library_t **_opencl) { if (_opencl == NULL) { return; } hb_opencl_library_t *opencl = *_opencl; if (opencl != NULL) { if (opencl->library != NULL) { HB_OCL_DLCLOSE(opencl->library); } free(opencl); } *_opencl = NULL; } static int hb_opencl_device_is_supported(hb_opencl_device_t* device) { // we only support OpenCL on GPUs for now // Ivy Bridge supports OpenCL on GPU, but it's too slow to be usable // FIXME: disable on NVIDIA to to a bug if ((device != NULL) && (device->type & CL_DEVICE_TYPE_GPU) && (device->ocl_vendor != HB_OCL_VENDOR_NVIDIA) && (device->ocl_vendor != HB_OCL_VENDOR_INTEL || hb_get_cpu_platform() != HB_CPU_PLATFORM_INTEL_IVB)) { int major, minor; // check OpenCL version: // OpenCL if (sscanf(device->version, "OpenCL %d.%d", &major, &minor) != 2) { return 0; } return (major > HB_OCL_MINVERSION_MAJOR) || (major == HB_OCL_MINVERSION_MAJOR && minor >= HB_OCL_MINVERSION_MINOR); } return 0; } static hb_opencl_device_t* hb_opencl_device_get(hb_opencl_library_t *opencl, cl_device_id device_id) { if (opencl == NULL || opencl->clGetDeviceInfo == NULL) { hb_error("hb_opencl_device_get: OpenCL support not available"); return NULL; } else if (device_id == NULL) { hb_error("hb_opencl_device_get: invalid device ID"); return NULL; } hb_opencl_device_t *device = calloc(1, sizeof(hb_opencl_device_t)); if (device == NULL) { hb_error("hb_opencl_device_get: memory allocation failure"); return NULL; } cl_int status = CL_SUCCESS; device->id = device_id; status |= opencl->clGetDeviceInfo(device->id, CL_DEVICE_VENDOR, sizeof(device->vendor), device->vendor, NULL); status |= opencl->clGetDeviceInfo(device->id, CL_DEVICE_NAME, sizeof(device->name), device->name, NULL); status |= opencl->clGetDeviceInfo(device->id, CL_DEVICE_VERSION, sizeof(device->version), device->version, NULL); status |= opencl->clGetDeviceInfo(device->id, CL_DEVICE_TYPE, sizeof(device->type), &device->type, NULL); status |= opencl->clGetDeviceInfo(device->id, CL_DEVICE_PLATFORM, sizeof(device->platform), &device->platform, NULL); status |= opencl->clGetDeviceInfo(device->id, CL_DRIVER_VERSION, sizeof(device->driver), device->driver, NULL); if (status != CL_SUCCESS) { free(device); return NULL; } if (!strcmp(device->vendor, "Advanced Micro Devices, Inc.") || !strcmp(device->vendor, "AMD")) { device->ocl_vendor = HB_OCL_VENDOR_AMD; } else if (!strncmp(device->vendor, "NVIDIA", 6 /* strlen("NVIDIA") */)) { device->ocl_vendor = HB_OCL_VENDOR_NVIDIA; } else if (!strncmp(device->vendor, "Intel", 5 /* strlen("Intel") */)) { device->ocl_vendor = HB_OCL_VENDOR_INTEL; } else { device->ocl_vendor = HB_OCL_VENDOR_OTHER; } return device; } static void hb_opencl_devices_list_close(hb_list_t **_list) { if (_list != NULL) { hb_list_t *list = *_list; hb_opencl_device_t *device; while (list != NULL && hb_list_count(list) > 0) { if ((device = hb_list_item(list, 0)) != NULL) { hb_list_rem(list, device); free(device); } } } hb_list_close(_list); } static hb_list_t* hb_opencl_devices_list_get(hb_opencl_library_t *opencl, cl_device_type device_type) { if (opencl == NULL || opencl->library == NULL || opencl->clGetDeviceIDs == NULL || opencl->clGetDeviceInfo == NULL || opencl->clGetPlatformIDs == NULL) { hb_error("hb_opencl_devices_list_get: OpenCL support not available"); return NULL; } hb_list_t *list = hb_list_init(); if (list == NULL) { hb_error("hb_opencl_devices_list_get: memory allocation failure"); return NULL; } cl_device_id *device_ids = NULL; hb_opencl_device_t *device = NULL; cl_platform_id *platform_ids = NULL; cl_uint i, j, num_platforms, num_devices; if (opencl->clGetPlatformIDs(0, NULL, &num_platforms) != CL_SUCCESS || !num_platforms) { goto fail; } if ((platform_ids = malloc(sizeof(cl_platform_id) * num_platforms)) == NULL) { hb_error("hb_opencl_devices_list_get: memory allocation failure"); goto fail; } if (opencl->clGetPlatformIDs(num_platforms, platform_ids, NULL) != CL_SUCCESS) { goto fail; } for (i = 0; i < num_platforms; i++) { if (opencl->clGetDeviceIDs(platform_ids[i], device_type, 0, NULL, &num_devices) != CL_SUCCESS || !num_devices) { // non-fatal continue; } if ((device_ids = malloc(sizeof(cl_device_id) * num_devices)) == NULL) { hb_error("hb_opencl_devices_list_get: memory allocation failure"); goto fail; } if (opencl->clGetDeviceIDs(platform_ids[i], device_type, num_devices, device_ids, NULL) != CL_SUCCESS) { // non-fatal continue; } for (j = 0; j < num_devices; j++) { if ((device = hb_opencl_device_get(opencl, device_ids[j])) != NULL) { hb_list_add(list, device); } } } goto end; fail: hb_opencl_devices_list_close(&list); end: free(platform_ids); free(device_ids); return list; } int hb_opencl_available() { static int opencl_available = -1; if (opencl_available >= 0) { return opencl_available; } opencl_available = 0; /* * Check whether we can load the OpenCL library, then check devices and make * sure we support running OpenCL code on at least one of them. */ hb_opencl_library_t *opencl; if ((opencl = hb_opencl_library_init()) != NULL) { int i; hb_list_t *device_list; hb_opencl_device_t *device; if ((device_list = hb_opencl_devices_list_get(opencl, CL_DEVICE_TYPE_ALL)) != NULL) { for (i = 0; i < hb_list_count(device_list); i++) { if ((device = hb_list_item(device_list, i)) != NULL && (hb_opencl_device_is_supported(device))) { opencl_available = 1; break; } } hb_opencl_devices_list_close(&device_list); } hb_opencl_library_close(&opencl); } return opencl_available; } void hb_opencl_info_print() { /* * Note: this function should not log any warnings or errors. * Its only purpose is to list OpenCL-capable devices, so let's initialize * only what we absolutely need here, rather than calling library_open(). */ hb_opencl_library_t ocl, *opencl = &ocl; if ((opencl->library = (void*)HB_OCL_DLOPEN) == NULL || (opencl->clGetDeviceIDs = (void*)HB_OCL_DLSYM(opencl->library, "clGetDeviceIDs" )) == NULL || (opencl->clGetDeviceInfo = (void*)HB_OCL_DLSYM(opencl->library, "clGetDeviceInfo" )) == NULL || (opencl->clGetPlatformIDs = (void*)HB_OCL_DLSYM(opencl->library, "clGetPlatformIDs")) == NULL) { // zero or insufficient OpenCL support hb_log("OpenCL: library not available"); goto end; } int i, idx; hb_list_t *device_list; hb_opencl_device_t *device; if ((device_list = hb_opencl_devices_list_get(opencl, CL_DEVICE_TYPE_ALL)) != NULL) { for (i = 0, idx = 1; i < hb_list_count(device_list); i++) { if ((device = hb_list_item(device_list, i)) != NULL) { // don't list CPU devices (always unsupported) if (!(device->type & CL_DEVICE_TYPE_CPU)) { hb_log("OpenCL device #%d: %s %s", idx++, device->vendor, device->name); hb_log(" - OpenCL version: %s", device->version + 7 /* strlen("OpenCL ") */); hb_log(" - driver version: %s", device->driver); hb_log(" - device type: %s%s", device->type & CL_DEVICE_TYPE_CPU ? "CPU" : device->type & CL_DEVICE_TYPE_GPU ? "GPU" : device->type & CL_DEVICE_TYPE_CUSTOM ? "Custom" : device->type & CL_DEVICE_TYPE_ACCELERATOR ? "Accelerator" : "Unknown", device->type & CL_DEVICE_TYPE_DEFAULT ? " (default)" : ""); hb_log(" - supported: %s", hb_opencl_device_is_supported(device) ? "YES" : "no"); } } } hb_opencl_devices_list_close(&device_list); } end: /* * Close only the initialized part */ if (opencl->library != NULL) { HB_OCL_DLCLOSE(opencl->library); } } HandBrake-0.10.2/libhb/openclwrapper.h0000664000175200017520000000614212463330511020152 0ustar handbrakehandbrake/* openclwrapper.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html Authors: Peng Gao Li Cao */ #ifndef HB_OPENCL_WRAPPER_H #define HB_OPENCL_WRAPPER_H #include "common.h" #include "extras/cl.h" //support AMD opencl #define CL_QUEUE_THREAD_HANDLE_AMD 0x403E #define CL_MAP_WRITE_INVALIDATE_REGION (1 << 2) typedef struct _KernelEnv { cl_context context; cl_command_queue command_queue; cl_program program; cl_kernel kernel; char kernel_name[150]; int isAMD; }KernelEnv; typedef struct _OpenCLEnv { cl_platform_id platform; cl_context context; cl_device_id devices; cl_command_queue command_queue; }OpenCLEnv; //user defined, this is function wrapper which is used to set the input parameters , //luanch kernel and copy data from GPU to CPU or CPU to GPU. typedef int (*cl_kernel_function)( void **userdata, KernelEnv *kenv ); // registe a wapper for running the kernel specified by the kernel name int hb_register_kernel_wrapper( const char *kernel_name, cl_kernel_function function ); // run kernel , user call this function to luanch kernel. // kernel_name: this kernel name is used to find the kernel in opencl runtime environment // userdata: this userdata is the all parameters for running the kernel specified by kernel name int hb_run_kernel( const char *kernel_name, void **userdata ); // init the run time environment , this function must be called befor calling any function related to opencl // the argc must be set zero , argv must be set NULL, build_option is the options for build the kernel. int hb_init_opencl_run_env( int argc, char **argv, const char *build_option ); //relase all resource about the opencl , this function must be called after calling any functions related to opencl int hb_release_opencl_run_env(); // get the opencl status , 0: not init ; 1, inited; this function is used the check whether or not the opencl run time has been created int hb_opencl_stats(); // update opencl run time environments , such as commandqueue , platforme, context. program int hb_init_opencl_attr( OpenCLEnv * env ); // create kernel object by a kernel name on the specified opencl run time indicated by env parameter int hb_create_kernel( char * kernelname, KernelEnv * env ); // release kernel object which is generated by calling the hb_create_kernel api int hb_release_kernel( KernelEnv * env ); void hb_opencl_init(); int hb_get_opencl_env(); int hb_create_buffer(cl_mem *cl_Buf,int flags,int size); int hb_read_opencl_buffer(cl_mem cl_inBuf,unsigned char *outbuf,int size); int hb_cl_create_mapped_buffer(cl_mem *mem, unsigned char **addr, int size); int hb_cl_free_mapped_buffer(cl_mem mem, unsigned char *addr); int hb_use_buffers(); int hb_confirm_gpu_type(); #endif // HB_OPENCL_WRAPPER_H HandBrake-0.10.2/libhb/decsrtsub.h0000664000175200017520000000067612463330511017275 0ustar handbrakehandbrake/* decsrtsub.h * * Copyright (c) 2003-2015 HandBrake Team * This file is part of the HandBrake source code * Homepage: . * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef __DECSRTSUB_H__ #define __DECSRTSUB_H__ void hb_srt_to_ssa(hb_buffer_t *sub_in, int line); #endif // __DECSRTSUB_H__ HandBrake-0.10.2/libhb/ports.c0000664000175200017520000010011412463330511016425 0ustar handbrakehandbrake/* ports.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifdef USE_PTHREAD #ifdef SYS_LINUX #define _GNU_SOURCE #include #endif #include #endif #ifdef SYS_BEOS #include #endif #if defined(SYS_DARWIN) || defined(SYS_FREEBSD) #include #include #endif #ifdef SYS_OPENBSD #include #include #include #endif #ifdef SYS_MINGW #include #include #else #include #include #include #include #endif #ifdef SYS_CYGWIN #include #endif #ifdef SYS_MINGW #include #include #include #include #include #endif #ifdef SYS_SunOS #include #endif #include #include #include #if defined( SYS_LINUX ) #include #include #include #elif defined( SYS_OPENBSD ) #include #include #include #endif #ifdef __APPLE__ #include #endif #include #include #include "hb.h" #include "libavutil/cpu.h" /************************************************************************ * hb_get_date() ************************************************************************ * Returns the current date in milliseconds. * On Win32, we implement a gettimeofday emulation here because * libdvdread and libmp4v2 use it without checking. ************************************************************************/ /* #ifdef SYS_CYGWIN struct timezone { }; int gettimeofday( struct timeval * tv, struct timezone * tz ) { int tick; tick = GetTickCount(); tv->tv_sec = tick / 1000; tv->tv_usec = ( tick % 1000 ) * 1000; return 0; } #endif */ // Convert utf8 string to current code page. // The internal string representation in hb is utf8. But some // libraries (libmkv, and mp4v2) expect filenames in the current // code page. So we must convert. char * hb_utf8_to_cp(const char *src) { char *dst = NULL; #if defined( SYS_MINGW ) int num_chars = MultiByteToWideChar(CP_UTF8, 0, src, -1, NULL, 0); if (num_chars <= 0) return NULL; wchar_t * tmp = calloc(num_chars, sizeof(wchar_t)); MultiByteToWideChar(CP_UTF8, 0, src, -1, tmp, num_chars); int len = WideCharToMultiByte(GetACP(), 0, tmp, num_chars, NULL, 0, NULL, NULL); if (len <= 0) return NULL; dst = calloc(len, sizeof(char)); WideCharToMultiByte(GetACP(), 0, tmp, num_chars, dst, len, NULL, NULL); free(tmp); #else // Other platforms don't have code pages dst = strdup(src); #endif return dst; } int hb_dvd_region(char *device, int *region_mask) { #if defined( DVD_LU_SEND_RPC_STATE ) && defined( DVD_AUTH ) struct stat st; dvd_authinfo ai; int fd, ret; fd = open( device, O_RDONLY ); if ( fd < 0 ) return -1; if ( fstat( fd, &st ) < 0 ) { close( fd ); return -1; } if ( !( S_ISBLK( st.st_mode ) || S_ISCHR( st.st_mode ) ) ) { close( fd ); return -1; } ai.type = DVD_LU_SEND_RPC_STATE; ret = ioctl(fd, DVD_AUTH, &ai); close( fd ); if ( ret < 0 ) return ret; *region_mask = ai.lrpcs.region_mask; return 0; #else return -1; #endif } uint64_t hb_get_date() { struct timeval tv; gettimeofday( &tv, NULL ); return( (uint64_t) tv.tv_sec * 1000 + (uint64_t) tv.tv_usec / 1000 ); } uint64_t hb_get_time_us() { #ifdef SYS_MINGW static LARGE_INTEGER frequency; LARGE_INTEGER cur_time; if (frequency.QuadPart == 0) { QueryPerformanceFrequency(&frequency); } QueryPerformanceCounter(&cur_time); return (uint64_t)(1000000 * cur_time.QuadPart / frequency.QuadPart); #else struct timeval tv; gettimeofday(&tv, NULL); return ((uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec); #endif } /************************************************************************ * hb_snooze() ************************************************************************ * Waits milliseconds. ************************************************************************/ void hb_snooze( int delay ) { if( delay < 1 ) { return; } #if defined( SYS_BEOS ) snooze( 1000 * delay ); #elif defined( SYS_DARWIN ) || defined( SYS_LINUX ) || defined( SYS_FREEBSD) || defined( SYS_SunOS ) usleep( 1000 * delay ); #elif defined( SYS_CYGWIN ) || defined( SYS_MINGW ) Sleep( delay ); #endif } /************************************************************************ * Get information about the CPU (number of cores, name, platform name) ************************************************************************/ static void init_cpu_info(); static int init_cpu_count(); struct { enum hb_cpu_platform platform; const char *name; union { char buf[48]; uint32_t buf4[12]; }; int count; } hb_cpu_info; int hb_get_cpu_count() { return hb_cpu_info.count; } int hb_get_cpu_platform() { return hb_cpu_info.platform; } const char* hb_get_cpu_name() { return hb_cpu_info.name; } const char* hb_get_cpu_platform_name() { switch (hb_cpu_info.platform) { // Intel 64 and IA-32 Architectures Software Developer's Manual, Vol. 3C // Table 35-1: CPUID Signature Values of DisplayFamily_DisplayModel case HB_CPU_PLATFORM_INTEL_BNL: return "Intel microarchitecture Bonnell"; case HB_CPU_PLATFORM_INTEL_SNB: return "Intel microarchitecture Sandy Bridge"; case HB_CPU_PLATFORM_INTEL_IVB: return "Intel microarchitecture Ivy Bridge"; case HB_CPU_PLATFORM_INTEL_SLM: return "Intel microarchitecture Silvermont"; case HB_CPU_PLATFORM_INTEL_HSW: return "Intel microarchitecture Haswell"; default: return NULL; } } #if ARCH_X86_64 # define REG_b "rbx" # define REG_S "rsi" #elif ARCH_X86_32 # define REG_b "ebx" # define REG_S "esi" #endif // ARCH_X86_32 #if ARCH_X86_64 || ARCH_X86_32 #define cpuid(index, eax, ebx, ecx, edx) \ __asm__ volatile ( \ "mov %%"REG_b", %%"REG_S" \n\t" \ "cpuid \n\t" \ "xchg %%"REG_b", %%"REG_S \ : "=a" (*eax), "=S" (*ebx), "=c" (*ecx), "=d" (*edx) \ : "0" (index)) #endif // ARCH_X86_64 || ARCH_X86_32 static void init_cpu_info() { hb_cpu_info.name = NULL; hb_cpu_info.count = init_cpu_count(); hb_cpu_info.platform = HB_CPU_PLATFORM_UNSPECIFIED; if (av_get_cpu_flags() & AV_CPU_FLAG_SSE) { #if ARCH_X86_64 || ARCH_X86_32 int eax, ebx, ecx, edx, family, model; cpuid(1, &eax, &ebx, &ecx, &edx); family = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff); model = ((eax >> 4) & 0xf) + ((eax >> 12) & 0xf0); // Intel 64 and IA-32 Architectures Software Developer's Manual, Vol. 3C // Table 35-1: CPUID Signature Values of DisplayFamily_DisplayModel switch (family) { case 0x06: { switch (model) { case 0x1C: case 0x26: case 0x27: case 0x35: case 0x36: hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_BNL; break; case 0x2A: case 0x2D: hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_SNB; break; case 0x3A: case 0x3E: hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_IVB; break; case 0x37: case 0x4A: case 0x4D: hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_SLM; break; case 0x3C: case 0x45: case 0x46: hb_cpu_info.platform = HB_CPU_PLATFORM_INTEL_HSW; break; default: break; } } break; default: break; } // Intel 64 and IA-32 Architectures Software Developer's Manual, Vol. 2A // Figure 3-8: Determination of Support for the Processor Brand String // Table 3-17: Information Returned by CPUID Instruction cpuid(0x80000000, &eax, &ebx, &ecx, &edx); if ((eax & 0x80000004) < 0x80000004) { cpuid(0x80000002, &hb_cpu_info.buf4[ 0], &hb_cpu_info.buf4[ 1], &hb_cpu_info.buf4[ 2], &hb_cpu_info.buf4[ 3]); cpuid(0x80000003, &hb_cpu_info.buf4[ 4], &hb_cpu_info.buf4[ 5], &hb_cpu_info.buf4[ 6], &hb_cpu_info.buf4[ 7]); cpuid(0x80000004, &hb_cpu_info.buf4[ 8], &hb_cpu_info.buf4[ 9], &hb_cpu_info.buf4[10], &hb_cpu_info.buf4[11]); hb_cpu_info.name = hb_cpu_info.buf; hb_cpu_info.buf[47] = '\0'; // just in case while (isspace(*hb_cpu_info.name)) { // skip leading whitespace to prettify hb_cpu_info.name++; } } #endif // ARCH_X86_64 || ARCH_X86_32 } } /* * Whenever possible, returns the number of CPUs on the current computer. * Returns 1 otherwise. */ static int init_cpu_count() { int cpu_count = 1; #if defined(SYS_CYGWIN) || defined(SYS_MINGW) SYSTEM_INFO cpuinfo; GetSystemInfo( &cpuinfo ); cpu_count = cpuinfo.dwNumberOfProcessors; #elif defined(SYS_LINUX) unsigned int bit; cpu_set_t p_aff; memset( &p_aff, 0, sizeof(p_aff) ); sched_getaffinity( 0, sizeof(p_aff), &p_aff ); for( cpu_count = 0, bit = 0; bit < sizeof(p_aff); bit++ ) cpu_count += (((uint8_t *)&p_aff)[bit / 8] >> (bit % 8)) & 1; #elif defined(SYS_BEOS) system_info info; get_system_info( &info ); cpu_count = info.cpu_count; #elif defined(SYS_DARWIN) || defined(SYS_FREEBSD) || defined(SYS_OPENBSD) size_t length = sizeof( cpu_count ); #ifdef SYS_OPENBSD int mib[2] = { CTL_HW, HW_NCPU }; if( sysctl(mib, 2, &cpu_count, &length, NULL, 0) ) #else if( sysctlbyname("hw.ncpu", &cpu_count, &length, NULL, 0) ) #endif { cpu_count = 1; } #elif defined( SYS_SunOS ) { processorid_t cpumax; int i,j=0; cpumax = sysconf(_SC_CPUID_MAX); for(i = 0; i <= cpumax; i++ ) { if(p_online(i, P_STATUS) != -1) { j++; } } cpu_count=j; } #endif cpu_count = MAX( 1, cpu_count ); cpu_count = MIN( cpu_count, 64 ); return cpu_count; } int hb_platform_init() { int result = 0; #if defined(SYS_MINGW) && defined(PTW32_STATIC_LIB) result = !pthread_win32_process_attach_np(); if (result) { hb_error("pthread_win32_process_attach_np() failed!"); return -1; } #endif #if defined(_WIN32) || defined(__MINGW32__) /* * win32 _IOLBF (line-buffering) is the same as _IOFBF (full-buffering). * force it to unbuffered otherwise informative output is not easily parsed. */ result = setvbuf(stdout, NULL, _IONBF, 0); if (result) { hb_error("setvbuf(stdout, NULL, _IONBF, 0) failed!"); return -1; } result = setvbuf(stderr, NULL, _IONBF, 0); if (result) { hb_error("setvbuf(stderr, NULL, _IONBF, 0) failed!"); return -1; } #endif init_cpu_info(); return result; } /************************************************************************ * Get a temporary directory for HB ***********************************************************************/ void hb_get_temporary_directory( char path[512] ) { char base[512]; char *p; /* Create the base */ #if defined( SYS_CYGWIN ) || defined( SYS_MINGW ) int i_size = GetTempPath( 512, base ); if( i_size <= 0 || i_size >= 512 ) { if( getcwd( base, 512 ) == NULL ) strcpy( base, "c:" ); /* Bad fallback but ... */ } /* c:/path/ works like a charm under cygwin(win32?) so use it */ while( ( p = strchr( base, '\\' ) ) ) *p = '/'; #else if( (p = getenv( "TMPDIR" ) ) != NULL || (p = getenv( "TEMP" ) ) != NULL ) strcpy( base, p ); else strcpy( base, "/tmp" ); #endif /* I prefer to remove evntual last '/' (for cygwin) */ if( base[strlen(base)-1] == '/' ) base[strlen(base)-1] = '\0'; snprintf(path, 512, "%s/hb.%d", base, (int)getpid()); } /************************************************************************ * Get a tempory filename for HB ***********************************************************************/ void hb_get_tempory_filename( hb_handle_t * h, char name[1024], char *fmt, ... ) { va_list args; hb_get_temporary_directory( name ); strcat( name, "/" ); va_start( args, fmt ); vsnprintf( &name[strlen(name)], 1024 - strlen(name), fmt, args ); va_end( args ); } /************************************************************************ * hb_stat ************************************************************************ * Wrapper to the real stat, needed to handle utf8 filenames on * windows. ***********************************************************************/ int hb_stat(const char *path, hb_stat_t *sb) { #ifdef SYS_MINGW wchar_t path_utf16[MAX_PATH]; if (!MultiByteToWideChar(CP_UTF8, 0, path, -1, path_utf16, MAX_PATH)) return -1; return _wstat64( path_utf16, sb ); #else return stat(path, sb); #endif } /************************************************************************ * hb_fopen ************************************************************************ * Wrapper to the real fopen, needed to handle utf8 filenames on * windows. ***********************************************************************/ FILE * hb_fopen(const char *path, const char *mode) { #ifdef SYS_MINGW FILE *f; wchar_t path_utf16[MAX_PATH]; wchar_t mode_utf16[16]; if (!MultiByteToWideChar(CP_UTF8, 0, path, -1, path_utf16, MAX_PATH)) return NULL; if (!MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_utf16, 16)) return NULL; errno_t ret = _wfopen_s(&f, path_utf16, mode_utf16); if (ret) return NULL; return f; #else return fopen(path, mode); #endif } HB_DIR* hb_opendir(char *path) { #ifdef SYS_MINGW HB_DIR *dir; wchar_t path_utf16[MAX_PATH]; if (!MultiByteToWideChar(CP_UTF8, 0, path, -1, path_utf16, MAX_PATH)) return NULL; dir = malloc(sizeof(HB_DIR)); if (dir == NULL) return NULL; dir->wdir = _wopendir(path_utf16); if (dir->wdir == NULL) { free(dir); return NULL; } return dir; #else return opendir(path); #endif } int hb_closedir(HB_DIR *dir) { #ifdef SYS_MINGW int ret; ret = _wclosedir(dir->wdir); free(dir); return ret; #else return closedir(dir); #endif } struct dirent * hb_readdir(HB_DIR *dir) { #ifdef SYS_MINGW struct _wdirent *entry; entry = _wreaddir(dir->wdir); if (entry == NULL) return NULL; int len = WideCharToMultiByte(CP_UTF8, 0, entry->d_name, -1, dir->entry.d_name, sizeof(dir->entry.d_name), NULL, NULL ); dir->entry.d_ino = entry->d_ino; dir->entry.d_reclen = entry->d_reclen; dir->entry.d_namlen = len - 1; return &dir->entry; #else return readdir(dir); #endif } void hb_rewinddir(HB_DIR *dir) { #ifdef SYS_MINGW _wrewinddir(dir->wdir); #else return rewinddir(dir); #endif } char * hb_strr_dir_sep(const char *path) { #ifdef SYS_MINGW char *sep = strrchr(path, '/'); if (sep == NULL) sep = strrchr(path, '\\'); return sep; #else return strrchr(path, '/'); #endif } /************************************************************************ * hb_mkdir ************************************************************************ * Wrapper to the real mkdir, needed only because it doesn't take a * second argument on Win32. Grrr. ***********************************************************************/ int hb_mkdir(char * path) { #ifdef SYS_MINGW wchar_t path_utf16[MAX_PATH]; if (!MultiByteToWideChar(CP_UTF8, 0, path, -1, path_utf16, MAX_PATH)) return -1; return _wmkdir(path_utf16); #else return mkdir(path, 0755); #endif } /************************************************************************ * Portable thread implementation ***********************************************************************/ struct hb_thread_s { char * name; int priority; thread_func_t * function; void * arg; hb_lock_t * lock; int exited; #if defined( SYS_BEOS ) thread_id thread; #elif USE_PTHREAD pthread_t thread; //#elif defined( SYS_CYGWIN ) // HANDLE thread; #endif }; /* Get a unique identifier to thread and represent as 64-bit unsigned. * If unsupported, the value 0 is be returned. * Caller should use result only for display/log purposes. */ static uint64_t hb_thread_to_integer( const hb_thread_t* t ) { #if defined( USE_PTHREAD ) #if defined( SYS_CYGWIN ) return (uint64_t)t->thread; #elif defined( _WIN32 ) || defined( __MINGW32__ ) return (uint64_t)(ptrdiff_t)t->thread.p; #elif defined( SYS_DARWIN ) return (unsigned long)t->thread; #else return (uint64_t)t->thread; #endif #else return 0; #endif } /************************************************************************ * hb_thread_func() ************************************************************************ * We use it as the root routine for any thread, for two reasons: * + To set the thread priority on OS X (pthread_setschedparam() could * be called from hb_thread_init(), but it's nicer to do it as we * are sure it is done before the real routine starts) * + Get informed when the thread exits, so we know whether * hb_thread_close() will block or not. ***********************************************************************/ static void attribute_align_thread hb_thread_func( void * _t ) { hb_thread_t * t = (hb_thread_t *) _t; #if defined( SYS_DARWIN ) || defined( SYS_FREEBSD ) /* Set the thread priority */ struct sched_param param; memset( ¶m, 0, sizeof( struct sched_param ) ); param.sched_priority = t->priority; pthread_setschedparam( pthread_self(), SCHED_OTHER, ¶m ); #endif #if defined( SYS_BEOS ) signal( SIGINT, SIG_IGN ); #endif /* Start the actual routine */ t->function( t->arg ); /* Inform that the thread can be joined now */ hb_deep_log( 2, "thread %"PRIx64" exited (\"%s\")", hb_thread_to_integer( t ), t->name ); hb_lock( t->lock ); t->exited = 1; hb_unlock( t->lock ); } /************************************************************************ * hb_thread_init() ************************************************************************ * name: user-friendly name * function: the thread routine * arg: argument of the routine * priority: HB_LOW_PRIORITY or HB_NORMAL_PRIORITY ***********************************************************************/ hb_thread_t * hb_thread_init( const char * name, void (* function)(void *), void * arg, int priority ) { hb_thread_t * t = calloc( sizeof( hb_thread_t ), 1 ); t->name = strdup( name ); t->function = function; t->arg = arg; t->priority = priority; t->lock = hb_lock_init(); /* Create and start the thread */ #if defined( SYS_BEOS ) t->thread = spawn_thread( (thread_func) hb_thread_func, name, priority, t ); resume_thread( t->thread ); #elif USE_PTHREAD pthread_create( &t->thread, NULL, (void * (*)( void * )) hb_thread_func, t ); //#elif defined( SYS_CYGWIN ) // t->thread = CreateThread( NULL, 0, // (LPTHREAD_START_ROUTINE) hb_thread_func, t, 0, NULL ); // // /* Maybe use THREAD_PRIORITY_LOWEST instead */ // if( priority == HB_LOW_PRIORITY ) // SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL ); #endif hb_deep_log( 2, "thread %"PRIx64" started (\"%s\")", hb_thread_to_integer( t ), t->name ); return t; } /************************************************************************ * hb_thread_close() ************************************************************************ * Joins the thread and frees memory. ***********************************************************************/ void hb_thread_close( hb_thread_t ** _t ) { hb_thread_t * t = *_t; /* Join the thread */ #if defined( SYS_BEOS ) long exit_value; wait_for_thread( t->thread, &exit_value ); #elif USE_PTHREAD pthread_join( t->thread, NULL ); //#elif defined( SYS_CYGWIN ) // WaitForSingleObject( t->thread, INFINITE ); #endif hb_deep_log( 2, "thread %"PRIx64" joined (\"%s\")", hb_thread_to_integer( t ), t->name ); hb_lock_close( &t->lock ); free( t->name ); free( t ); *_t = NULL; } /************************************************************************ * hb_thread_has_exited() ************************************************************************ * Returns 1 if the thread can be joined right away, 0 otherwise. ***********************************************************************/ int hb_thread_has_exited( hb_thread_t * t ) { int exited; hb_lock( t->lock ); exited = t->exited; hb_unlock( t->lock ); return exited; } /************************************************************************ * Portable mutex implementation ***********************************************************************/ struct hb_lock_s { #if defined( SYS_BEOS ) sem_id sem; #elif USE_PTHREAD pthread_mutex_t mutex; //#elif defined( SYS_CYGWIN ) // HANDLE mutex; #endif }; /************************************************************************ * hb_lock_init() * hb_lock_close() * hb_lock() * hb_unlock() ************************************************************************ * Basic wrappers to OS-specific semaphore or mutex functions. ***********************************************************************/ hb_lock_t * hb_lock_init() { hb_lock_t * l = calloc( sizeof( hb_lock_t ), 1 ); #if defined( SYS_BEOS ) l->sem = create_sem( 1, "sem" ); #elif USE_PTHREAD pthread_mutexattr_t mta; pthread_mutexattr_init(&mta); #if defined( SYS_CYGWIN ) || defined( SYS_FREEBSD ) pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_NORMAL); #endif pthread_mutex_init( &l->mutex, &mta ); //#elif defined( SYS_CYGWIN ) // l->mutex = CreateMutex( 0, FALSE, 0 ); #endif return l; } void hb_lock_close( hb_lock_t ** _l ) { hb_lock_t * l = *_l; #if defined( SYS_BEOS ) delete_sem( l->sem ); #elif USE_PTHREAD pthread_mutex_destroy( &l->mutex ); //#elif defined( SYS_CYGWIN ) // CloseHandle( l->mutex ); #endif free( l ); *_l = NULL; } void hb_lock( hb_lock_t * l ) { #if defined( SYS_BEOS ) acquire_sem( l->sem ); #elif USE_PTHREAD pthread_mutex_lock( &l->mutex ); //#elif defined( SYS_CYGWIN ) // WaitForSingleObject( l->mutex, INFINITE ); #endif } void hb_unlock( hb_lock_t * l ) { #if defined( SYS_BEOS ) release_sem( l->sem ); #elif USE_PTHREAD pthread_mutex_unlock( &l->mutex ); //#elif defined( SYS_CYGWIN ) // ReleaseMutex( l->mutex ); #endif } /************************************************************************ * Portable condition variable implementation ***********************************************************************/ struct hb_cond_s { #if defined( SYS_BEOS ) int thread; #elif USE_PTHREAD pthread_cond_t cond; //#elif defined( SYS_CYGWIN ) // HANDLE event; #endif }; /************************************************************************ * hb_cond_init() * hb_cond_close() * hb_cond_wait() * hb_cond_signal() ************************************************************************ * Win9x is not supported by this implementation (SignalObjectAndWait() * only available on Windows 2000/XP). ***********************************************************************/ hb_cond_t * hb_cond_init() { hb_cond_t * c = calloc( sizeof( hb_cond_t ), 1 ); if( c == NULL ) return NULL; #if defined( SYS_BEOS ) c->thread = -1; #elif USE_PTHREAD pthread_cond_init( &c->cond, NULL ); //#elif defined( SYS_CYGWIN ) // c->event = CreateEvent( NULL, FALSE, FALSE, NULL ); #endif return c; } void hb_cond_close( hb_cond_t ** _c ) { hb_cond_t * c = *_c; #if defined( SYS_BEOS ) #elif USE_PTHREAD pthread_cond_destroy( &c->cond ); //#elif defined( SYS_CYGWIN ) // CloseHandle( c->event ); #endif free( c ); *_c = NULL; } void hb_cond_wait( hb_cond_t * c, hb_lock_t * lock ) { #if defined( SYS_BEOS ) c->thread = find_thread( NULL ); release_sem( lock->sem ); suspend_thread( c->thread ); acquire_sem( lock->sem ); c->thread = -1; #elif USE_PTHREAD pthread_cond_wait( &c->cond, &lock->mutex ); //#elif defined( SYS_CYGWIN ) // SignalObjectAndWait( lock->mutex, c->event, INFINITE, FALSE ); // WaitForSingleObject( lock->mutex, INFINITE ); #endif } void hb_clock_gettime( struct timespec *tp ) { struct timeval tv; gettimeofday( &tv, NULL ); tp->tv_sec = tv.tv_sec; tp->tv_nsec = tv.tv_usec * 1000; } void hb_cond_timedwait( hb_cond_t * c, hb_lock_t * lock, int msec ) { #if defined( SYS_BEOS ) c->thread = find_thread( NULL ); release_sem( lock->sem ); suspend_thread( c->thread ); acquire_sem( lock->sem ); c->thread = -1; #elif USE_PTHREAD struct timespec ts; hb_clock_gettime(&ts); ts.tv_nsec += (msec % 1000) * 1000000; ts.tv_sec += msec / 1000 + (ts.tv_nsec / 1000000000); ts.tv_nsec %= 1000000000; pthread_cond_timedwait( &c->cond, &lock->mutex, &ts ); #endif } void hb_cond_signal( hb_cond_t * c ) { #if defined( SYS_BEOS ) while( c->thread != -1 ) { thread_info info; get_thread_info( c->thread, &info ); if( info.state == B_THREAD_SUSPENDED ) { resume_thread( c->thread ); break; } /* Looks like we have been called between hb_cond_wait's release_sem() and suspend_thread() lines. Wait until the thread is actually suspended before we resume it */ snooze( 5000 ); } #elif USE_PTHREAD pthread_cond_signal( &c->cond ); //#elif defined( SYS_CYGWIN ) // PulseEvent( c->event ); #endif } void hb_cond_broadcast( hb_cond_t * c ) { #if USE_PTHREAD pthread_cond_broadcast( &c->cond ); #endif } /************************************************************************ * Network ***********************************************************************/ struct hb_net_s { int socket; }; hb_net_t * hb_net_open( char * address, int port ) { hb_net_t * n = calloc( sizeof( hb_net_t ), 1 ); struct sockaddr_in sock; struct hostent * host; #ifdef SYS_MINGW WSADATA wsaData; int iResult, winsock_init = 0; // Initialize Winsock if (!winsock_init) { iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != 0) { hb_log("WSAStartup failed: %d", iResult); free(n); return NULL; } winsock_init = 1; } #endif /* TODO: find out why this doesn't work on Win32 */ if( !( host = gethostbyname( address ) ) ) { hb_log( "gethostbyname failed (%s)", address ); free( n ); return NULL; } memset( &sock, 0, sizeof( struct sockaddr_in ) ); sock.sin_family = host->h_addrtype; sock.sin_port = htons( port ); memcpy( &sock.sin_addr, host->h_addr, host->h_length ); if( ( n->socket = socket( host->h_addrtype, SOCK_STREAM, 0 ) ) < 0 ) { hb_log( "socket failed" ); free( n ); return NULL; } if( connect( n->socket, (struct sockaddr *) &sock, sizeof( struct sockaddr_in ) ) < 0 ) { hb_log( "connect failed" ); free( n ); return NULL; } return n; } int hb_net_send( hb_net_t * n, char * buffer ) { return send( n->socket, buffer, strlen( buffer ), 0 ); } int hb_net_recv( hb_net_t * n, char * buffer, int size ) { return recv( n->socket, buffer, size - 1, 0 ); } void hb_net_close( hb_net_t ** _n ) { hb_net_t * n = (hb_net_t *) *_n; close( n->socket ); free( n ); *_n = NULL; } #ifdef SYS_MINGW char *strtok_r(char *s, const char *delim, char **save_ptr) { char *token; if (s == NULL) s = *save_ptr; /* Scan leading delimiters. */ s += strspn(s, delim); if (*s == '\0') return NULL; /* Find the end of the token. */ token = s; s = strpbrk(token, delim); if (s == NULL) /* This token finishes the string. */ *save_ptr = strchr(token, '\0'); else { /* Terminate the token and make *SAVE_PTR point past it. */ *s = '\0'; *save_ptr = s + 1; } 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 = NULL; #ifdef __APPLE__ opaque = calloc(sizeof(IOPMAssertionID), 1); if (opaque == NULL) { hb_error("hb_system_sleep: failed to allocate opaque"); return NULL; } IOPMAssertionID *assertionID = (IOPMAssertionID*)opaque; *assertionID = -1; #endif return opaque; } void hb_system_sleep_opaque_close(void **opaque) { if (*opaque != NULL) { hb_system_sleep_private_enable(*opaque); } #ifdef __APPLE__ if (*opaque != NULL) { IOPMAssertionID *assertionID = (IOPMAssertionID*)*opaque; free(assertionID); } #endif *opaque = NULL; } void hb_system_sleep_private_enable(void *opaque) { #ifdef __APPLE__ if (opaque == NULL) { hb_error("hb_system_sleep: opaque is NULL"); } IOPMAssertionID *assertionID = (IOPMAssertionID*)opaque; if (*assertionID == -1) { // nothing to do return; } IOReturn success = IOPMAssertionRelease(*assertionID); if (success == kIOReturnSuccess) { hb_deep_log(3, "hb_system_sleep: assertion %d released, sleep allowed", *assertionID); *assertionID = -1; } else { hb_log("hb_system_sleep: failed to allow system sleep"); } #endif } void hb_system_sleep_private_disable(void *opaque) { #ifdef __APPLE__ if (opaque == NULL) { hb_error("hb_system_sleep: opaque is NULL"); } IOPMAssertionID *assertionID = (IOPMAssertionID*)opaque; if (*assertionID != -1) { // nothing to do return; } IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, reasonForActivity, assertionID); if (success == kIOReturnSuccess) { hb_deep_log(3, "hb_system_sleep: assertion %d created, sleep prevented", *assertionID); } else { hb_log("hb_system_sleep: failed to prevent system sleep"); } #endif } HandBrake-0.10.2/libhb/update.c0000664000175200017520000001274412463330511016553 0ustar handbrakehandbrake/* update.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" static void UpdateFunc( void * ); typedef struct { int * build; char * version; } hb_update_t; hb_thread_t * hb_update_init( int * build, char * version ) { hb_update_t * data = calloc( sizeof( hb_update_t ), 1 ); data->build = build; data->version = version; return hb_thread_init( "update", UpdateFunc, data, HB_NORMAL_PRIORITY ); } static void UpdateFunc( void * _data ) { hb_update_t * data = (hb_update_t *) _data; char* const url = HB_PROJECT_URL_APPCAST; char* const urlz = url + strlen( HB_PROJECT_URL_APPCAST ); /* marks null-term */ char url_host[64]; char url_path[128]; char query[256]; hb_net_t * net; int ret; char buf[4096]; char * cur, * end; int size; int i_vers; char s_vers[32]; /* must be no larger than hb_handle_s.version */ int i; /* Setup hb_query and hb_query_two with the correct appcast file */ hb_log( "Using %s", url ); /* extract host part */ cur = strstr( HB_PROJECT_URL_APPCAST, "//" ); if( !cur || cur+2 > urlz ) goto error; cur += 2; end = strstr( cur, "/" ); if( !end || end > urlz ) goto error; memset( url_host, 0, sizeof(url_host) ); strncpy( url_host, cur, (end-cur) ); /* extract path part */ memset( url_path, 0, sizeof(url_path) ); strncpy( url_path, end, (urlz-end) ); if( !strlen( url_path )) goto error; memset( query, 0, sizeof(query) ); snprintf( query, sizeof(query), "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n", url_path, url_host ); /* Grab the data from the web server */ if( !( net = hb_net_open( url_host, 80 ) ) ) { goto error; } if( hb_net_send( net, query ) < 0 ) { hb_log("Error: Unable to connect to server"); hb_net_close( &net ); goto error; } size = 0; memset( buf, 0, 4096 ); for( ;; ) { ret = hb_net_recv( net, &buf[size], sizeof( buf ) - size ); if( ret < 1 ) { hb_net_close( &net ); break; } size += ret; } cur = buf; end = &buf[sizeof( buf )]; /* Make sure we got it */ cur += 9; if( size < 15 || strncmp( cur, "200 OK", 6 ) ) { hb_log("Error: We did not get a 200 OK from the server. \n"); goto error; } cur += 6; /* Find the end of the headers and the beginning of the content */ for( ; &cur[3] < end; cur++ ) { if( cur[0] == '\r' && cur[1] == '\n' && cur[2] == '\r' && cur[3] == '\n' ) { cur += 4; break; } } if( cur >= end ) { hb_log("Error: Found the end of the buffer before the end of the HTTP header information! \n"); goto error; } /* * Find the tag * Scan though each character of the buffer until we find that the first 4 characters of "cur" are "' ) { cur += 1; break; } /* If the CLI tag has not been found in the first 768 characters, or the end is reached, something bad happened.*/ if (( i > 768) || ( cur >= end )) { hb_log("Error: Did not find the tag in the expected maximum amount of characters into the file. \n"); goto error; } } if( cur >= end ) { goto error; } /* * Ok, The above code didn't position cur, it only found = end ) { hb_log("Error: Unexpected end of buffer! Could not find the build information. \n"); goto error; } /* Stable HB_PROJECT_BUILD */ i_vers = strtol( cur, &cur, 10 ); if( cur >= end ) { hb_log("Error: Unexpected end of buffer! \n"); goto error; } /* * The Version number is 2 places after the build, so shift cur, 2 places. * Get all the characters in cur until the point where " is found. */ cur += 2; if( cur >= end ) { hb_log("Error: Unexpected end of buffer! Could not get version number. \n"); goto error; } memset( s_vers, 0, sizeof( s_vers ) ); for( i = 0; i < sizeof( s_vers ) - 1 && cur < end && *cur != '"'; i++, cur++ ) { s_vers[i] = *cur; /* If the CLI tag has not been found in the first 768 characters, or the end is reached, something bad happened.*/ if (( cur >= end )) { hb_log("Error: Version number too long, or end of buffer reached. \n"); goto error; } } if( cur >= end ) { goto error; } /* Print the version information */ hb_log( "latest: %s, build %d", s_vers, i_vers ); /* Return the build information */ if( i_vers > HB_PROJECT_BUILD ) { memcpy( data->version, s_vers, sizeof(s_vers) ); *(data->build) = i_vers; } error: free( data ); return; } HandBrake-0.10.2/libhb/encvobsub.c0000664000175200017520000000307112463330511017250 0ustar handbrakehandbrake/* encvobsub.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" struct hb_work_private_s { hb_job_t * job; }; int encsubInit( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t * pv; pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; pv->job = job; return 0; } int encsubWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_buffer_t * in = *buf_in; if (w->subtitle->source != VOBSUB) { // Invalid source, send EOF, this shouldn't ever happen hb_log("encvobsub: invalid subtitle source"); hb_buffer_close( buf_in ); *buf_out = hb_buffer_init(0); } if ( in->size <= 0 ) { /* EOF on input stream - send it downstream & say that we're done */ *buf_out = in; *buf_in = NULL; return HB_WORK_DONE; } /* * Not much to do, just pass the buffer on. * Some day, we may re-encode bd subtitles here ;) */ if (buf_out) { *buf_out = in; *buf_in = NULL; } return HB_WORK_OK; } void encsubClose( hb_work_object_t * w ) { free( w->private_data ); } hb_work_object_t hb_encvobsub = { WORK_ENCVOBSUB, "VOBSUB encoder", encsubInit, encsubWork, encsubClose }; HandBrake-0.10.2/libhb/bd.c0000664000175200017520000007010312463330511015647 0ustar handbrakehandbrake/* dvd.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "lang.h" #include "hbffmpeg.h" #include "libbluray/bluray.h" struct hb_bd_s { char * path; BLURAY * bd; int title_count; BLURAY_TITLE_INFO ** title_info; int64_t duration; hb_stream_t * stream; int chapter; int next_chap; }; /*********************************************************************** * Local prototypes **********************************************************************/ static int next_packet( BLURAY *bd, uint8_t *pkt ); static int title_info_compare_mpls(const void *, const void *); /*********************************************************************** * hb_bd_init *********************************************************************** * **********************************************************************/ hb_bd_t * hb_bd_init( char * path ) { hb_bd_t * d; int ii; d = calloc( sizeof( hb_bd_t ), 1 ); /* Open device */ d->bd = bd_open( path, NULL ); if( d->bd == NULL ) { /* * Not an error, may be a stream - which we'll try in a moment. */ hb_log( "bd: not a bd - trying as a stream/file instead" ); goto fail; } d->title_count = bd_get_titles( d->bd, TITLES_RELEVANT, 0 ); if ( d->title_count == 0 ) { hb_log( "bd: not a bd - trying as a stream/file instead" ); goto fail; } d->title_info = calloc( sizeof( BLURAY_TITLE_INFO* ) , d->title_count ); for ( ii = 0; ii < d->title_count; ii++ ) { d->title_info[ii] = bd_get_title_info( d->bd, ii, 0 ); } qsort(d->title_info, d->title_count, sizeof( BLURAY_TITLE_INFO* ), title_info_compare_mpls ); d->path = strdup( path ); return d; fail: if( d->bd ) bd_close( d->bd ); free( d ); return NULL; } /*********************************************************************** * hb_bd_title_count **********************************************************************/ int hb_bd_title_count( hb_bd_t * d ) { return d->title_count; } static void add_subtitle(int track, hb_list_t *list_subtitle, BLURAY_STREAM_INFO *bdsub, uint32_t codec) { hb_subtitle_t * subtitle; iso639_lang_t * lang; subtitle = calloc( sizeof( hb_subtitle_t ), 1 ); subtitle->track = track; subtitle->id = bdsub->pid; lang = lang_for_code2( (char*)bdsub->lang ); snprintf( subtitle->lang, sizeof( subtitle->lang ), "%s", strlen(lang->native_name) ? lang->native_name : lang->eng_name); snprintf( subtitle->iso639_2, sizeof( subtitle->iso639_2 ), "%s", lang->iso639_2); switch ( bdsub->coding_type ) { case BLURAY_STREAM_TYPE_SUB_PG: subtitle->source = PGSSUB; subtitle->format = PICTURESUB; subtitle->config.dest = RENDERSUB; break; default: // Unrecognized, don't add to list free( subtitle ); return; } subtitle->reg_desc = STR4_TO_UINT32("HDMV"); subtitle->stream_type = bdsub->coding_type; subtitle->codec = codec; hb_log( "bd: subtitle id=0x%x, lang=%s, 3cc=%s", subtitle->id, subtitle->lang, subtitle->iso639_2 ); hb_list_add( list_subtitle, subtitle ); return; } static void add_audio(int track, hb_list_t *list_audio, BLURAY_STREAM_INFO *bdaudio, int substream_type, uint32_t codec, uint32_t codec_param) { const char * codec_name; hb_audio_t * audio; iso639_lang_t * lang; audio = calloc( sizeof( hb_audio_t ), 1 ); audio->id = (substream_type << 16) | bdaudio->pid; audio->config.in.reg_desc = STR4_TO_UINT32("HDMV"); audio->config.in.stream_type = bdaudio->coding_type; audio->config.in.substream_type = substream_type; audio->config.in.codec = codec; audio->config.in.codec_param = codec_param; switch( audio->config.in.codec ) { case HB_ACODEC_AC3: codec_name = "AC3"; break; case HB_ACODEC_DCA: codec_name = "DTS"; break; default: { if( audio->config.in.codec & HB_ACODEC_FF_MASK ) { switch( bdaudio->coding_type ) { case BLURAY_STREAM_TYPE_AUDIO_AC3PLUS: codec_name = "E-AC3"; break; case BLURAY_STREAM_TYPE_AUDIO_DTSHD: codec_name = "DTS-HD HRA"; break; case BLURAY_STREAM_TYPE_AUDIO_DTSHD_MASTER: codec_name = "DTS-HD MA"; break; case BLURAY_STREAM_TYPE_AUDIO_LPCM: codec_name = "BD LPCM"; break; case BLURAY_STREAM_TYPE_AUDIO_MPEG1: codec_name = "MPEG1"; break; case BLURAY_STREAM_TYPE_AUDIO_MPEG2: codec_name = "MPEG2"; break; case BLURAY_STREAM_TYPE_AUDIO_TRUHD: codec_name = "TrueHD"; break; default: codec_name = "Unknown FFmpeg"; break; } } else { codec_name = "Unknown"; } } break; } lang = lang_for_code2( (char*)bdaudio->lang ); audio->config.lang.type = 0; snprintf( audio->config.lang.simple, sizeof( audio->config.lang.simple ), "%s", strlen( lang->native_name ) ? lang->native_name : lang->eng_name ); snprintf( audio->config.lang.iso639_2, sizeof( audio->config.lang.iso639_2 ), "%s", lang->iso639_2 ); hb_log("bd: audio id=0x%x, lang=%s (%s), 3cc=%s", audio->id, audio->config.lang.simple, codec_name, audio->config.lang.iso639_2); audio->config.in.track = track; hb_list_add( list_audio, audio ); return; } static int bd_audio_equal( BLURAY_CLIP_INFO *a, BLURAY_CLIP_INFO *b ) { int ii, jj, equal; if ( a->audio_stream_count != b->audio_stream_count ) return 0; if ( a->audio_stream_count == 0 ) return 0; for ( ii = 0; ii < a->audio_stream_count; ii++ ) { BLURAY_STREAM_INFO * s = &a->audio_streams[ii]; equal = 0; for ( jj = 0; jj < b->audio_stream_count; jj++ ) { if ( s->pid == b->audio_streams[jj].pid && s->coding_type == b->audio_streams[jj].coding_type) { equal = 1; break; } } if ( !equal ) return 0; } return 1; } /*********************************************************************** * hb_bd_title_scan **********************************************************************/ hb_title_t * hb_bd_title_scan( hb_bd_t * d, int tt, uint64_t min_duration ) { hb_title_t * title; hb_chapter_t * chapter; int ii, jj; BLURAY_TITLE_INFO * ti = NULL; hb_log( "bd: scanning title %d", tt ); title = hb_title_init( d->path, tt ); title->demuxer = HB_TS_DEMUXER; title->type = HB_BD_TYPE; title->reg_desc = STR4_TO_UINT32("HDMV"); char * p_cur, * p_last = d->path; for( p_cur = d->path; *p_cur; p_cur++ ) { if( IS_DIR_SEP(p_cur[0]) && p_cur[1] ) { p_last = &p_cur[1]; } } snprintf( title->name, sizeof( title->name ), "%s", p_last ); char *dot_term = strrchr(title->name, '.'); if (dot_term) *dot_term = '\0'; title->vts = 0; title->ttn = 0; ti = d->title_info[tt - 1]; if ( ti == NULL ) { hb_log( "bd: invalid title" ); goto fail; } if ( ti->clip_count == 0 ) { hb_log( "bd: stream has no clips" ); goto fail; } if ( ti->clips[0].video_stream_count == 0 ) { hb_log( "bd: stream has no video" ); goto fail; } hb_log( "bd: playlist %05d.MPLS", ti->playlist ); title->playlist = ti->playlist; uint64_t pkt_count = 0; for ( ii = 0; ii < ti->clip_count; ii++ ) { pkt_count += ti->clips[ii].pkt_count; } title->block_start = 0; title->block_end = pkt_count; title->block_count = pkt_count; title->angle_count = ti->angle_count; /* Get duration */ title->duration = ti->duration; title->hours = title->duration / 90000 / 3600; title->minutes = ( ( title->duration / 90000 ) % 3600 ) / 60; title->seconds = ( title->duration / 90000 ) % 60; hb_log( "bd: duration is %02d:%02d:%02d (%"PRId64" ms)", title->hours, title->minutes, title->seconds, title->duration / 90 ); /* ignore short titles because they're often stills */ if( ti->duration < min_duration ) { hb_log( "bd: ignoring title (too short)" ); goto fail; } BLURAY_STREAM_INFO * bdvideo = &ti->clips[0].video_streams[0]; title->video_id = bdvideo->pid; title->video_stream_type = bdvideo->coding_type; hb_log( "bd: video id=0x%x, stream type=%s, format %s", title->video_id, bdvideo->coding_type == BLURAY_STREAM_TYPE_VIDEO_MPEG1 ? "MPEG1" : bdvideo->coding_type == BLURAY_STREAM_TYPE_VIDEO_MPEG2 ? "MPEG2" : bdvideo->coding_type == BLURAY_STREAM_TYPE_VIDEO_VC1 ? "VC-1" : bdvideo->coding_type == BLURAY_STREAM_TYPE_VIDEO_H264 ? "H.264" : "Unknown", bdvideo->format == BLURAY_VIDEO_FORMAT_480I ? "480i" : bdvideo->format == BLURAY_VIDEO_FORMAT_576I ? "576i" : bdvideo->format == BLURAY_VIDEO_FORMAT_480P ? "480p" : bdvideo->format == BLURAY_VIDEO_FORMAT_1080I ? "1080i" : bdvideo->format == BLURAY_VIDEO_FORMAT_720P ? "720p" : bdvideo->format == BLURAY_VIDEO_FORMAT_1080P ? "1080p" : bdvideo->format == BLURAY_VIDEO_FORMAT_576P ? "576p" : "Unknown" ); switch( bdvideo->coding_type ) { case BLURAY_STREAM_TYPE_VIDEO_MPEG1: case BLURAY_STREAM_TYPE_VIDEO_MPEG2: title->video_codec = WORK_DECAVCODECV; title->video_codec_param = AV_CODEC_ID_MPEG2VIDEO; break; case BLURAY_STREAM_TYPE_VIDEO_VC1: title->video_codec = WORK_DECAVCODECV; title->video_codec_param = AV_CODEC_ID_VC1; break; case BLURAY_STREAM_TYPE_VIDEO_H264: title->video_codec = WORK_DECAVCODECV; title->video_codec_param = AV_CODEC_ID_H264; break; default: hb_log( "scan: unknown video codec (0x%x)", bdvideo->coding_type ); goto fail; } switch ( bdvideo->aspect ) { case BLURAY_ASPECT_RATIO_4_3: title->container_aspect = 4. / 3.; break; case BLURAY_ASPECT_RATIO_16_9: title->container_aspect = 16. / 9.; break; default: hb_log( "bd: unknown aspect" ); goto fail; } hb_log( "bd: aspect = %g", title->container_aspect ); /* Detect audio */ // Max primary BD audios is 32 int matches; int most_audio = 0; int audio_clip_index = 0; if (ti->clip_count > 2) { // All BD clips are not all required to have the same audio. // But clips that have seamless transition are required // to have the same audio as the previous clip. // So find the clip that has the most other clips with the // matching audio. for ( ii = 0; ii < ti->clip_count; ii++ ) { matches = 0; for ( jj = 0; jj < ti->clip_count; jj++ ) { if ( bd_audio_equal( &ti->clips[ii], &ti->clips[jj] ) ) { matches++; } } if ( matches > most_audio ) { most_audio = matches; audio_clip_index = ii; } } } else if (ti->clip_count == 2) { // If there are only 2 clips, pick audios from the longer clip if (ti->clips[0].pkt_count < ti->clips[1].pkt_count) audio_clip_index = 1; } // Add all the audios found in the above clip. for (ii = 0; ii < ti->clips[audio_clip_index].audio_stream_count; ii++) { BLURAY_STREAM_INFO * bdaudio; bdaudio = &ti->clips[audio_clip_index].audio_streams[ii]; switch (bdaudio->coding_type) { case BLURAY_STREAM_TYPE_AUDIO_TRUHD: // Add 2 audio tracks. One for TrueHD and one for AC-3 add_audio(ii, title->list_audio, bdaudio, HB_SUBSTREAM_BD_AC3, HB_ACODEC_AC3, AV_CODEC_ID_AC3); add_audio(ii, title->list_audio, bdaudio, HB_SUBSTREAM_BD_TRUEHD, HB_ACODEC_FFMPEG, AV_CODEC_ID_TRUEHD); break; case BLURAY_STREAM_TYPE_AUDIO_DTS: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_DCA, AV_CODEC_ID_DTS); break; case BLURAY_STREAM_TYPE_AUDIO_MPEG2: case BLURAY_STREAM_TYPE_AUDIO_MPEG1: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_FFMPEG, AV_CODEC_ID_MP2); break; case BLURAY_STREAM_TYPE_AUDIO_AC3PLUS: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_FFMPEG, AV_CODEC_ID_EAC3); break; case BLURAY_STREAM_TYPE_AUDIO_LPCM: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_FFMPEG, AV_CODEC_ID_PCM_BLURAY); break; case BLURAY_STREAM_TYPE_AUDIO_AC3: add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_AC3, AV_CODEC_ID_AC3); break; case BLURAY_STREAM_TYPE_AUDIO_DTSHD_MASTER: case BLURAY_STREAM_TYPE_AUDIO_DTSHD: // Add 2 audio tracks. One for DTS-HD and one for DTS add_audio(ii, title->list_audio, bdaudio, HB_SUBSTREAM_BD_DTS, HB_ACODEC_DCA, AV_CODEC_ID_DTS); // DTS-HD is special. The substreams must be concatinated // DTS-core followed by DTS-hd-extensions. Setting // a substream id of 0 says use all substreams. add_audio(ii, title->list_audio, bdaudio, 0, HB_ACODEC_DCA_HD, AV_CODEC_ID_DTS); break; default: hb_log("scan: unknown audio pid 0x%x codec 0x%x", bdaudio->pid, bdaudio->coding_type); break; } } // Add all the subtitles found in the above clip. for ( ii = 0; ii < ti->clips[audio_clip_index].pg_stream_count; ii++ ) { BLURAY_STREAM_INFO * bdpgs; bdpgs = &ti->clips[audio_clip_index].pg_streams[ii]; switch( bdpgs->coding_type ) { case BLURAY_STREAM_TYPE_SUB_PG: add_subtitle(ii, title->list_subtitle, bdpgs, WORK_DECPGSSUB); break; default: hb_log( "scan: unknown subtitle pid 0x%x codec 0x%x", bdpgs->pid, bdpgs->coding_type ); break; } } /* Chapters */ for ( ii = 0, jj = 0; ii < ti->chapter_count; ii++ ) { char chapter_title[80]; // Sanity check start time of this chapter. // If it is beyond the end of the title, drop it. if (ti->chapters[ii].start > ti->duration) { hb_log("bd: chapter %d invalid start %ld, dropping", ii+1, ti->chapters[ii].start); continue; } chapter = calloc( sizeof( hb_chapter_t ), 1 ); chapter->index = ++jj; sprintf( chapter_title, "Chapter %d", chapter->index ); hb_chapter_set_title( chapter, chapter_title ); chapter->duration = ti->chapters[ii].duration; chapter->block_start = ti->chapters[ii].offset; // Sanity check chapter duration and start times // Have seen some invalid durations in the wild if (ii < ti->chapter_count - 1) { // Validate start time if (ti->chapters[ii+1].start < ti->chapters[ii].start) { hb_log("bd: chapter %d invalid start %ld", ii+1, ti->chapters[ii+1].start); ti->chapters[ii+1].start = ti->chapters[ii].start + chapter->duration; } if (ti->chapters[ii+1].start - ti->chapters[ii].start != chapter->duration) { hb_log("bd: chapter %d invalid duration %ld", ii+1, chapter->duration); chapter->duration = ti->chapters[ii+1].start - ti->chapters[ii].start; } } else { if (ti->duration - ti->chapters[ii].start != chapter->duration) { hb_log("bd: chapter %d invalid duration %ld", ii+1, chapter->duration); chapter->duration = ti->duration - ti->chapters[ii].start; } } int seconds = ( chapter->duration + 45000 ) / 90000; chapter->hours = ( seconds / 3600 ); chapter->minutes = ( seconds % 3600 ) / 60; chapter->seconds = ( seconds % 60 ); hb_log( "bd: chap %d packet=%"PRIu64", %"PRId64" ms", chapter->index, chapter->block_start, chapter->duration / 90 ); hb_list_add( title->list_chapter, chapter ); } hb_log( "bd: title %d has %d chapters", tt, ti->chapter_count ); /* This title is ok so far */ goto cleanup; fail: hb_title_close( &title ); cleanup: return title; } /*********************************************************************** * hb_bd_main_feature **********************************************************************/ int hb_bd_main_feature( hb_bd_t * d, hb_list_t * list_title ) { int longest = 0; int ii; uint64_t longest_duration = 0; int highest_rank = 0; int most_chapters = 0; int rank[8] = {0, 1, 3, 2, 6, 5, 7, 4}; BLURAY_TITLE_INFO * ti; for ( ii = 0; ii < hb_list_count( list_title ); ii++ ) { hb_title_t * title = hb_list_item( list_title, ii ); ti = d->title_info[title->index - 1]; if ( ti ) { BLURAY_STREAM_INFO * bdvideo = &ti->clips[0].video_streams[0]; if ( title->duration > longest_duration * 0.7 && bdvideo->format < 8 ) { if (highest_rank < rank[bdvideo->format] || ( title->duration > longest_duration && highest_rank == rank[bdvideo->format])) { longest = title->index; longest_duration = title->duration; highest_rank = rank[bdvideo->format]; most_chapters = ti->chapter_count; } else if (highest_rank == rank[bdvideo->format] && title->duration == longest_duration && ti->chapter_count > most_chapters) { longest = title->index; most_chapters = ti->chapter_count; } } } else if ( title->duration > longest_duration ) { longest_duration = title->duration; longest = title->index; } } return longest; } /*********************************************************************** * hb_bd_start *********************************************************************** * Title and chapter start at 1 **********************************************************************/ int hb_bd_start( hb_bd_t * d, hb_title_t *title ) { BD_EVENT event; d->duration = title->duration; // Calling bd_get_event initializes libbluray event queue. bd_select_title( d->bd, d->title_info[title->index - 1]->idx ); bd_get_event( d->bd, &event ); d->chapter = 1; d->stream = hb_bd_stream_open( title ); if ( d->stream == NULL ) { return 0; } return 1; } /*********************************************************************** * hb_bd_stop *********************************************************************** * **********************************************************************/ void hb_bd_stop( hb_bd_t * d ) { if( d->stream ) hb_stream_close( &d->stream ); } /*********************************************************************** * hb_bd_seek *********************************************************************** * **********************************************************************/ int hb_bd_seek( hb_bd_t * d, float f ) { uint64_t pos = f * d->duration; bd_seek_time(d->bd, pos); d->next_chap = bd_get_current_chapter( d->bd ) + 1; hb_ts_stream_reset(d->stream); return 1; } int hb_bd_seek_pts( hb_bd_t * d, uint64_t pts ) { bd_seek_time(d->bd, pts); d->next_chap = bd_get_current_chapter( d->bd ) + 1; hb_ts_stream_reset(d->stream); return 1; } int hb_bd_seek_chapter( hb_bd_t * d, int c ) { d->next_chap = c; bd_seek_chapter( d->bd, c - 1 ); hb_ts_stream_reset(d->stream); return 1; } /*********************************************************************** * hb_bd_read *********************************************************************** * **********************************************************************/ hb_buffer_t * hb_bd_read( hb_bd_t * d ) { int result; int error_count = 0; uint8_t buf[192]; BD_EVENT event; uint64_t pos; hb_buffer_t * b; uint8_t discontinuity; int new_chap = 0; discontinuity = 0; while ( 1 ) { if ( d->next_chap != d->chapter ) { new_chap = d->chapter = d->next_chap; } result = next_packet( d->bd, buf ); if ( result < 0 ) { hb_error("bd: Read Error"); pos = bd_tell( d->bd ); bd_seek( d->bd, pos + 192 ); error_count++; if (error_count > 10) { hb_error("bd: Error, too many consecutive read errors"); return 0; } continue; } else if ( result == 0 ) { return 0; } error_count = 0; while ( bd_get_event( d->bd, &event ) ) { switch ( event.event ) { case BD_EVENT_CHAPTER: // The muxers expect to only get chapter 2 and above // They write chapter 1 when chapter 2 is detected. d->next_chap = event.param; break; case BD_EVENT_PLAYITEM: discontinuity = 1; hb_deep_log(2, "bd: Playitem %u", event.param); break; case BD_EVENT_STILL: bd_read_skip_still( d->bd ); break; default: break; } } // buf+4 to skip the BD timestamp at start of packet b = hb_ts_decode_pkt( d->stream, buf+4 ); if ( b ) { b->s.discontinuity = discontinuity; b->s.new_chap = new_chap; return b; } } return NULL; } /*********************************************************************** * hb_bd_chapter *********************************************************************** * Returns in which chapter the next block to be read is. * Chapter numbers start at 1. **********************************************************************/ int hb_bd_chapter( hb_bd_t * d ) { return d->next_chap; } /*********************************************************************** * hb_bd_close *********************************************************************** * Closes and frees everything **********************************************************************/ void hb_bd_close( hb_bd_t ** _d ) { hb_bd_t * d = *_d; int ii; if ( d->title_info ) { for ( ii = 0; ii < d->title_count; ii++ ) bd_free_title_info( d->title_info[ii] ); free( d->title_info ); } if( d->stream ) hb_stream_close( &d->stream ); if( d->bd ) bd_close( d->bd ); if( d->path ) free( d->path ); free( d ); *_d = NULL; } /*********************************************************************** * hb_bd_set_angle *********************************************************************** * Sets the angle to read **********************************************************************/ void hb_bd_set_angle( hb_bd_t * d, int angle ) { if ( !bd_select_angle( d->bd, angle) ) { hb_log("bd_select_angle failed"); } } static int check_ts_sync(const uint8_t *buf) { // must have initial sync byte, no scrambling & a legal adaptation ctrl return (buf[0] == 0x47) && ((buf[3] >> 6) == 0) && ((buf[3] >> 4) > 0); } static int have_ts_sync(const uint8_t *buf, int psize) { return check_ts_sync(&buf[0*psize]) && check_ts_sync(&buf[1*psize]) && check_ts_sync(&buf[2*psize]) && check_ts_sync(&buf[3*psize]) && check_ts_sync(&buf[4*psize]) && check_ts_sync(&buf[5*psize]) && check_ts_sync(&buf[6*psize]) && check_ts_sync(&buf[7*psize]); } #define MAX_HOLE 192*80 static uint64_t align_to_next_packet(BLURAY *bd, uint8_t *pkt) { uint8_t buf[MAX_HOLE]; uint64_t pos = 0; uint64_t start = bd_tell(bd); uint64_t orig; uint64_t off = 192; memcpy(buf, pkt, 192); if ( start >= 192 ) { start -= 192; } orig = start; while (1) { if (bd_read(bd, buf+off, sizeof(buf)-off) == sizeof(buf)-off) { const uint8_t *bp = buf; int i; for ( i = sizeof(buf) - 8 * 192; --i >= 0; ++bp ) { if ( have_ts_sync( bp, 192 ) ) { break; } } if ( i >= 0 ) { pos = ( bp - buf ); break; } off = 8 * 192; memcpy(buf, buf + sizeof(buf) - off, off); start += sizeof(buf) - off; } else { return 0; } } off = start + pos - 4; // bd_seek seeks to the nearest access unit *before* the requested position // we don't want to seek backwards, so we need to read until we get // past that position. bd_seek(bd, off); while (off > bd_tell(bd)) { if (bd_read(bd, buf, 192) != 192) { break; } } return start - orig + pos; } static int next_packet( BLURAY *bd, uint8_t *pkt ) { int result; while ( 1 ) { result = bd_read( bd, pkt, 192 ); if ( result < 0 ) { return -1; } if ( result < 192 ) { return 0; } // Sync byte is byte 4. 0-3 are timestamp. if (pkt[4] == 0x47) { return 1; } // lost sync - back up to where we started then try to re-establish. uint64_t pos = bd_tell(bd); uint64_t pos2 = align_to_next_packet(bd, pkt); if ( pos2 == 0 ) { hb_log( "next_packet: eof while re-establishing sync @ %"PRId64, pos ); return 0; } hb_log( "next_packet: sync lost @ %"PRId64", regained after %"PRId64" bytes", pos, pos2 ); } } static int title_info_compare_mpls(const void *va, const void *vb) { BLURAY_TITLE_INFO *a, *b; a = *(BLURAY_TITLE_INFO**)va; b = *(BLURAY_TITLE_INFO**)vb; return a->playlist - b->playlist; } HandBrake-0.10.2/libhb/taskset.c0000664000175200017520000001512412463330511016742 0ustar handbrakehandbrake/* taskset.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "ports.h" #include "taskset.h" int taskset_init( taskset_t *ts, int thread_count, size_t arg_size ) { int init_step; init_step = 0; memset( ts, 0, sizeof( *ts ) ); ts->thread_count = thread_count; ts->arg_size = arg_size; ts->bitmap_elements = ( ts->thread_count + 31 ) / 32; ts->task_threads = malloc( sizeof( hb_thread_t* ) * ts->thread_count ); if( ts->task_threads == NULL ) goto fail; init_step++; if( arg_size != 0 ) { ts->task_threads_args = malloc( arg_size * ts->thread_count ); if( ts->task_threads == NULL ) goto fail; } init_step++; ts->task_begin_bitmap = malloc( sizeof( uint32_t ) * ts->bitmap_elements ); if( ts->task_begin_bitmap == NULL ) goto fail; init_step++; ts->task_complete_bitmap = malloc( sizeof( uint32_t ) * ts->bitmap_elements ); if( ts->task_complete_bitmap == NULL ) goto fail; init_step++; ts->task_stop_bitmap = malloc( sizeof( uint32_t ) * ts->bitmap_elements ); if( ts->task_stop_bitmap == NULL ) goto fail; init_step++; ts->task_cond_lock = hb_lock_init(); if( ts->task_cond_lock == NULL) goto fail; init_step++; ts->task_begin = hb_cond_init(); if( ts->task_begin == NULL) goto fail; init_step++; ts->task_complete = hb_cond_init(); if( ts->task_complete == NULL) goto fail; init_step++; /* * Initialize all arg data to 0. */ memset(ts->task_threads_args, 0, ts->arg_size * ts->thread_count ); /* * Inialize bitmaps to all bits set. This means that any unused bits * in the bitmap are already in the "condition satisfied" state allowing * us to test the bitmap 32bits at a time without having to mask off * the end. */ memset(ts->task_begin_bitmap, 0xFF, sizeof( uint32_t ) * ts->bitmap_elements ); memset(ts->task_complete_bitmap, 0xFF, sizeof( uint32_t ) * ts->bitmap_elements ); memset(ts->task_stop_bitmap, 0, sizeof( uint32_t ) * ts->bitmap_elements ); /* * Important to start off with the threads locked waiting * on input, no work completed, and not asked to stop. */ bit_nclear( ts->task_begin_bitmap, 0, ts->thread_count - 1 ); bit_nclear( ts->task_complete_bitmap, 0, ts->thread_count - 1 ); bit_nclear( ts->task_stop_bitmap, 0, ts->thread_count - 1 ); return (1); fail: switch (init_step) { default: hb_cond_close( &ts->task_complete ); /* FALL THROUGH */ case 7: hb_cond_close( &ts->task_begin ); /* FALL THROUGH */ case 6: hb_lock_close( &ts->task_cond_lock ); /* FALL THROUGH */ case 5: free( ts->task_stop_bitmap ); /* FALL THROUGH */ case 4: free( ts->task_complete_bitmap ); /* FALL THROUGH */ case 3: free( ts->task_begin_bitmap ); /* FALL THROUGH */ case 2: if( ts->task_threads_args == NULL ) free( ts->task_threads_args ); /* FALL THROUGH */ case 1: free( ts->task_threads ); /* FALL THROUGH */ case 0: break; } return (0); } int taskset_thread_spawn( taskset_t *ts, int thr_idx, const char *descr, thread_func_t *func, int priority ) { ts->task_threads[thr_idx] = hb_thread_init( descr, func, taskset_thread_args( ts, thr_idx ), priority); return( ts->task_threads[thr_idx] != NULL ); } void taskset_cycle( taskset_t *ts ) { hb_lock( ts->task_cond_lock ); /* * Signal all threads that their work is available. */ bit_nset( ts->task_begin_bitmap, 0, ts->thread_count - 1 ); hb_cond_broadcast( ts->task_begin ); /* * Wait until all threads have completed. Note that we must * loop here as hb_cond_wait() on some platforms (e.g pthead_cond_wait) * may unblock prematurely. */ do { hb_cond_wait( ts->task_complete, ts->task_cond_lock ); } while ( !allbits_set( ts->task_complete_bitmap, ts->bitmap_elements ) ); /* * Clear completion indications for next time. */ bit_nclear( ts->task_complete_bitmap, 0, ts->thread_count - 1 ); hb_unlock( ts->task_cond_lock ); } /* * Block current thread until work is available for it. */ void taskset_thread_wait4start( taskset_t *ts, int thr_idx ) { hb_lock( ts->task_cond_lock ); while ( bit_is_clear( ts->task_begin_bitmap, thr_idx ) ) hb_cond_wait( ts->task_begin, ts->task_cond_lock ); /* * We've been released for one run. Insure we block the next * time through the loop. */ bit_clear( ts->task_begin_bitmap, thr_idx ); hb_unlock( ts->task_cond_lock ); } /* * Current thread has completed its work. Indicate completion, * and if all threads in this task set have completed, wakeup * anyone waiting for this condition. */ void taskset_thread_complete( taskset_t *ts, int thr_idx ) { hb_lock( ts->task_cond_lock ); bit_set( ts->task_complete_bitmap, thr_idx ); if( allbits_set( ts->task_complete_bitmap, ts->bitmap_elements ) ) { hb_cond_signal( ts->task_complete ); } hb_unlock( ts->task_cond_lock ); } void taskset_fini( taskset_t *ts ) { int i; hb_lock( ts->task_cond_lock ); /* * Tell each thread to stop, and then cleanup. */ bit_nset( ts->task_stop_bitmap, 0, ts->thread_count - 1 ); bit_nset( ts->task_begin_bitmap, 0, ts->thread_count - 1 ); hb_cond_broadcast( ts->task_begin ); /* * Wait for all threads to exit. */ hb_cond_wait( ts->task_complete, ts->task_cond_lock ); hb_unlock( ts->task_cond_lock ); /* * Clean up taskset memory. */ for( i = 0; i < ts->thread_count; i++) { hb_thread_close( &ts->task_threads[i] ); } hb_lock_close( &ts->task_cond_lock ); hb_cond_close( &ts->task_begin ); hb_cond_close( &ts->task_complete ); free( ts->task_threads ); if( ts->task_threads_args != NULL ) free( ts->task_threads_args ); free( ts->task_begin_bitmap ); free( ts->task_complete_bitmap ); free( ts->task_stop_bitmap ); } HandBrake-0.10.2/libhb/cropscale.c0000664000175200017520000002020512463330511017233 0ustar handbrakehandbrake/* cropscale.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "hbffmpeg.h" #include "common.h" #include "opencl.h" struct hb_filter_private_s { hb_job_t *job; int width_in; int height_in; int pix_fmt; int pix_fmt_out; int width_out; int height_out; int crop[4]; /* OpenCL/DXVA2 */ int use_dxva; int use_decomb; int use_detelecine; hb_oclscale_t *os; //ocl scaler handler struct SwsContext * context; }; static int hb_crop_scale_init( hb_filter_object_t * filter, hb_filter_init_t * init ); static int hb_crop_scale_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ); static int hb_crop_scale_info( hb_filter_object_t * filter, hb_filter_info_t * info ); static void hb_crop_scale_close( hb_filter_object_t * filter ); hb_filter_object_t hb_filter_crop_scale = { .id = HB_FILTER_CROP_SCALE, .enforce_order = 1, .name = "Crop and Scale", .settings = NULL, .init = hb_crop_scale_init, .work = hb_crop_scale_work, .close = hb_crop_scale_close, .info = hb_crop_scale_info, }; static int hb_crop_scale_init( hb_filter_object_t * filter, hb_filter_init_t * init ) { filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); hb_filter_private_t * pv = filter->private_data; // TODO: add pix format option to settings pv->job = init->job; pv->pix_fmt_out = init->pix_fmt; pv->width_in = init->width; pv->height_in = init->height; pv->width_out = init->width - (init->crop[2] + init->crop[3]); pv->height_out = init->height - (init->crop[0] + init->crop[1]); /* OpenCL/DXVA2 */ pv->use_dxva = init->use_dxva; pv->use_decomb = init->job->use_decomb; pv->use_detelecine = init->job->use_detelecine; if (pv->job->use_opencl && pv->job->title->opencl_support) { pv->os = ( hb_oclscale_t * )malloc( sizeof( hb_oclscale_t ) ); memset( pv->os, 0, sizeof( hb_oclscale_t ) ); } memcpy( pv->crop, init->crop, sizeof( int[4] ) ); if( filter->settings ) { sscanf( filter->settings, "%d:%d:%d:%d:%d:%d", &pv->width_out, &pv->height_out, &pv->crop[0], &pv->crop[1], &pv->crop[2], &pv->crop[3] ); } // Set init values so the next stage in the pipline // knows what it will be getting init->pix_fmt = pv->pix_fmt; init->width = pv->width_out; init->height = pv->height_out; memcpy( init->crop, pv->crop, sizeof( int[4] ) ); return 0; } static int hb_crop_scale_info( hb_filter_object_t * filter, hb_filter_info_t * info ) { hb_filter_private_t * pv = filter->private_data; if( !pv ) return 0; // Set init values so the next stage in the pipline // knows what it will be getting memset( info, 0, sizeof( hb_filter_info_t ) ); info->out.pix_fmt = pv->pix_fmt; info->out.width = pv->width_out; info->out.height = pv->height_out; memcpy( info->out.crop, pv->crop, sizeof( int[4] ) ); int cropped_width = pv->width_in - ( pv->crop[2] + pv->crop[3] ); int cropped_height = pv->height_in - ( pv->crop[0] + pv->crop[1] ); sprintf( info->human_readable_desc, "source: %d * %d, crop (%d/%d/%d/%d): %d * %d, scale: %d * %d", pv->width_in, pv->height_in, pv->crop[0], pv->crop[1], pv->crop[2], pv->crop[3], cropped_width, cropped_height, pv->width_out, pv->height_out ); return 0; } static void hb_crop_scale_close( hb_filter_object_t * filter ) { hb_filter_private_t * pv = filter->private_data; if ( !pv ) { return; } /* OpenCL */ if (pv->job->use_opencl && pv->job->title->opencl_support && pv->os) { if (hb_ocl != NULL) { HB_OCL_BUF_FREE(hb_ocl, pv->os->bicubic_x_weights); HB_OCL_BUF_FREE(hb_ocl, pv->os->bicubic_y_weights); } free(pv->os); } if( pv->context ) { sws_freeContext( pv->context ); } free( pv ); filter->private_data = NULL; } /* OpenCL */ static hb_buffer_t* crop_scale( hb_filter_private_t * pv, hb_buffer_t * in ) { AVPicture pic_in; AVPicture pic_out; AVPicture pic_crop; hb_buffer_t * out; out = hb_video_buffer_init( pv->width_out, pv->height_out ); hb_avpicture_fill( &pic_in, in ); hb_avpicture_fill( &pic_out, out ); // Crop; this alters the pointer to the data to point to the // correct place for cropped frame av_picture_crop( &pic_crop, &pic_in, in->f.fmt, pv->crop[0], pv->crop[2] ); // Use bicubic OpenCL scaling when selected and when downsampling < 4:1; if ((pv->job->use_opencl && pv->job->title->opencl_support) && (pv->width_out * 4 > pv->width_in) && (in->cl.buffer != NULL) && (out->cl.buffer != NULL)) { /* OpenCL */ hb_ocl_scale(in, out, pv->crop, pv->os); } else { if (pv->context == NULL || pv->width_in != in->f.width || pv->height_in != in->f.height || pv->pix_fmt != in->f.fmt) { // Something changed, need a new scaling context. if (pv->context != NULL) { sws_freeContext(pv->context); } pv->context = hb_sws_get_context(in->f.width - (pv->crop[2] + pv->crop[3]), in->f.height - (pv->crop[0] + pv->crop[1]), in->f.fmt, out->f.width, out->f.height, out->f.fmt, SWS_LANCZOS|SWS_ACCURATE_RND); pv->width_in = in->f.width; pv->height_in = in->f.height; pv->pix_fmt = in->f.fmt; } // Scale pic_crop into pic_render according to the // context set up above sws_scale(pv->context, (const uint8_t* const*)pic_crop.data, pic_crop.linesize, 0, in->f.height - (pv->crop[0] + pv->crop[1]), pic_out.data, pic_out.linesize); } out->s = in->s; hb_buffer_move_subs( out, in ); return out; } static int hb_crop_scale_work( hb_filter_object_t * filter, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_filter_private_t * pv = filter->private_data; hb_buffer_t * in = *buf_in; if ( in->size <= 0 ) { *buf_out = in; *buf_in = NULL; return HB_FILTER_DONE; } if ( !pv ) { *buf_out = in; *buf_in = NULL; return HB_FILTER_OK; } // If width or height were not set, set them now based on the // input width & height if ( pv->width_out <= 0 || pv->height_out <= 0 ) { pv->width_out = in->f.width - (pv->crop[2] + pv->crop[3]); pv->height_out = in->f.height - (pv->crop[0] + pv->crop[1]); } /* OpenCL/DXVA2 */ if ((!pv->use_decomb && !pv->use_detelecine && !pv->crop[0] && !pv->crop[1] && !pv->crop[2] && !pv->crop[3] && in->f.fmt == pv->pix_fmt_out && in->f.width == pv->width_out && in->f.height == pv->height_out) || (pv->use_dxva && in->f.width == pv->width_out && in->f.height == pv->height_out)) { *buf_out = in; *buf_in = NULL; return HB_FILTER_OK; } *buf_out = crop_scale( pv, in ); return HB_FILTER_OK; } HandBrake-0.10.2/libhb/stream.c0000664000175200017520000056062412470727143016602 0ustar handbrakehandbrake/* stream.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include #include #include #include "hb.h" #include "hbffmpeg.h" #include "lang.h" #include "libbluray/bluray.h" #include "vadxva2.h" #define min(a, b) a < b ? a : b #define HB_MAX_PROBE_SIZE (1*1024*1024) /* * This table defines how ISO MPEG stream type codes map to HandBrake * codecs. It is indexed by the 8 bit stream type and contains the codec * worker object id and a parameter for that worker proc (ignored except * for the ffmpeg-based codecs in which case it is the ffmpeg codec id). * * Entries with a worker proc id of 0 or a kind of 'U' indicate that HB * doesn't handle the stream type. * N - Not used * U - Unknown (to be determined by further processing) * A - Audio * V - Video * S - Subtitle * P - PCR */ typedef enum { N, U, A, V, P, S } kind_t; typedef struct { kind_t kind; /* not handled / unknown / audio / video */ int codec; /* HB worker object id of codec */ int codec_param; /* param for codec (usually ffmpeg codec id) */ const char* name; /* description of type */ } stream2codec_t; #define st(id, kind, codec, codec_param, name) \ [id] = { kind, codec, codec_param, name } static const stream2codec_t st2codec[256] = { st(0x00, U, 0, 0, NULL), st(0x01, V, WORK_DECAVCODECV, AV_CODEC_ID_MPEG2VIDEO, "MPEG1"), st(0x02, V, WORK_DECAVCODECV, AV_CODEC_ID_MPEG2VIDEO, "MPEG2"), st(0x03, A, HB_ACODEC_FFMPEG, AV_CODEC_ID_MP2, "MPEG1"), st(0x04, A, HB_ACODEC_FFMPEG, AV_CODEC_ID_MP2, "MPEG2"), st(0x05, N, 0, 0, "ISO 13818-1 private section"), st(0x06, U, 0, 0, "ISO 13818-1 PES private data"), st(0x07, N, 0, 0, "ISO 13522 MHEG"), st(0x08, N, 0, 0, "ISO 13818-1 DSM-CC"), st(0x09, N, 0, 0, "ISO 13818-1 auxiliary"), st(0x0a, N, 0, 0, "ISO 13818-6 encap"), st(0x0b, N, 0, 0, "ISO 13818-6 DSM-CC U-N msgs"), st(0x0c, N, 0, 0, "ISO 13818-6 Stream descriptors"), st(0x0d, N, 0, 0, "ISO 13818-6 Sections"), st(0x0e, N, 0, 0, "ISO 13818-1 auxiliary"), st(0x0f, A, HB_ACODEC_FFAAC, AV_CODEC_ID_AAC, "AAC"), st(0x10, V, WORK_DECAVCODECV, AV_CODEC_ID_MPEG4, "MPEG4"), st(0x11, A, HB_ACODEC_FFMPEG, AV_CODEC_ID_AAC_LATM, "LATM AAC"), st(0x12, U, 0, 0, "MPEG4 generic"), st(0x14, N, 0, 0, "ISO 13818-6 DSM-CC download"), st(0x1b, V, WORK_DECAVCODECV, AV_CODEC_ID_H264, "H.264"), st(0x80, U, HB_ACODEC_FFMPEG, AV_CODEC_ID_PCM_BLURAY, "Digicipher II Video"), st(0x81, A, HB_ACODEC_AC3, AV_CODEC_ID_AC3, "AC3"), st(0x82, A, HB_ACODEC_DCA, AV_CODEC_ID_DTS, "DTS"), // 0x83 can be LPCM or BD TrueHD. Set to 'unknown' till we know more. st(0x83, U, HB_ACODEC_LPCM, 0, "LPCM"), // BD E-AC3 Primary audio st(0x84, U, 0, 0, "SDDS"), st(0x85, U, 0, 0, "ATSC Program ID"), // 0x86 can be BD DTS-HD/DTS. Set to 'unknown' till we know more. st(0x86, U, HB_ACODEC_DCA_HD, AV_CODEC_ID_DTS, "DTS-HD MA"), st(0x87, A, HB_ACODEC_FFMPEG, AV_CODEC_ID_EAC3, "E-AC3"), st(0x8a, A, HB_ACODEC_DCA, AV_CODEC_ID_DTS, "DTS"), st(0x90, S, WORK_DECPGSSUB, 0, "PGS Subtitle"), // 0x91 can be AC3 or BD Interactive Graphics Stream. st(0x91, U, 0, 0, "AC3/IGS"), st(0x92, N, 0, 0, "Subtitle"), st(0x94, U, 0, 0, "SDDS"), st(0xa0, V, 0, 0, "MSCODEC"), // BD E-AC3 Secondary audio st(0xa1, U, 0, 0, "E-AC3"), // BD DTS-HD Secondary audio st(0xa2, U, 0, 0, "DTS-HD LBR"), st(0xea, V, WORK_DECAVCODECV, AV_CODEC_ID_VC1, "VC-1"), }; #undef st typedef enum { hb_stream_type_unknown = 0, transport, program, ffmpeg } hb_stream_type_t; #define MAX_PS_PROBE_SIZE (5*1024*1024) #define kMaxNumberPMTStreams 32 typedef struct { hb_buffer_t *buf; hb_buffer_t *extra_buf; int8_t skipbad; int8_t continuity; uint8_t pkt_summary[8]; int pid; uint8_t is_pcr; int pes_list; } hb_ts_stream_t; typedef struct { int map_idx; int stream_id; uint8_t stream_id_ext; uint8_t stream_type; kind_t stream_kind; int lang_code; uint32_t format_id; #define TS_FORMAT_ID_AC3 (('A' << 24) | ('C' << 16) | ('-' << 8) | '3') int codec; // HB worker object id of codec int codec_param; // param for codec (usually ffmpeg codec id) char codec_name[80]; int next; // next pointer for list // hb_ts_stream_t points to a list of // hb_pes_stream_t hb_buffer_t *probe_buf; int probe_next_size; } hb_pes_stream_t; struct hb_stream_s { int scan; int frames; /* video frames so far */ int errors; /* total errors so far */ int last_error_frame; /* frame # at last error message */ int last_error_count; /* # errors at last error message */ int packetsize; /* Transport Stream packet size */ int need_keyframe; // non-zero if want to start at a keyframe int chapter; /* Chapter that we are currently in */ int64_t chapter_end; /* HB time that the current chapter ends */ struct { uint8_t found_pcr; // non-zero if we've found at least one pcr int pcr_out; // sequence number of most recent output pcr int pcr_in; // sequence number of most recent input pcr int pcr_discontinuity; // sequence number of last discontinuity int pcr_current; // last discontinuity sent to reader int64_t pcr; // most recent input pcr int64_t last_timestamp; // used for discontinuity detection when // there are no PCRs uint8_t *packet; // buffer for one TS packet hb_ts_stream_t *list; int count; int alloc; } ts; struct { uint8_t found_scr; // non-zero if we've found at least one scr int64_t scr; // most recent input scr hb_pes_stream_t *list; int count; int alloc; } pes; /* * Stuff before this point is dynamic state updated as we read the * stream. Stuff after this point is stream description state that * we learn during the initial scan but cache so it can be * reused during the conversion read. */ uint8_t has_IDRs; // # IDRs found during duration scan uint8_t ts_flags; // stream characteristics: #define TS_HAS_PCR (1 << 0) // at least one PCR seen #define TS_HAS_RAP (1 << 1) // Random Access Point bit seen #define TS_HAS_RSEI (1 << 2) // "Restart point" SEI seen char *path; FILE *file_handle; hb_stream_type_t hb_stream_type; hb_title_t *title; AVFormatContext *ffmpeg_ic; AVPacket *ffmpeg_pkt; uint8_t ffmpeg_video_id; uint32_t reg_desc; // 4 byte registration code that identifies // stream semantics struct { unsigned short program_number; unsigned short program_map_PID; } pat_info[kMaxNumberPMTStreams]; int ts_number_pat_entries; struct { int reading; unsigned char *tablebuf; unsigned int tablepos; unsigned char current_continuity_counter; unsigned int PCR_PID; } pmt_info; }; typedef struct { uint8_t *buf; uint32_t val; int pos; int size; } bitbuf_t; typedef struct { uint8_t has_stream_id_ext; uint8_t stream_id; uint8_t stream_id_ext; uint8_t bd_substream_id; int64_t pts; int64_t dts; int64_t scr; int header_len; int packet_len; } hb_pes_info_t; /*********************************************************************** * Local prototypes **********************************************************************/ static void hb_stream_duration(hb_stream_t *stream, hb_title_t *inTitle); static off_t align_to_next_packet(hb_stream_t *stream); static int64_t pes_timestamp( const uint8_t *pes ); static int hb_ts_stream_init(hb_stream_t *stream); static hb_buffer_t * hb_ts_stream_decode(hb_stream_t *stream); static void hb_init_audio_list(hb_stream_t *stream, hb_title_t *title); static void hb_init_subtitle_list(hb_stream_t *stream, hb_title_t *title); static int hb_ts_stream_find_pids(hb_stream_t *stream); static void hb_ps_stream_init(hb_stream_t *stream); static hb_buffer_t * hb_ps_stream_decode(hb_stream_t *stream); static void hb_ps_stream_find_streams(hb_stream_t *stream); static int hb_ps_read_packet( hb_stream_t * stream, hb_buffer_t *b ); static int update_ps_streams( hb_stream_t * stream, int stream_id, int stream_id_ext, int stream_type, int in_kind ); static int update_ts_streams( hb_stream_t * stream, int pid, int stream_id_ext, int stream_type, int in_kind, int *pes_idx ); static void update_pes_kind( hb_stream_t * stream, int idx ); static int ffmpeg_open( hb_stream_t *stream, hb_title_t *title, int scan ); static void ffmpeg_close( hb_stream_t *d ); static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream, hb_title_t *title ); hb_buffer_t *hb_ffmpeg_read( hb_stream_t *stream ); static int ffmpeg_seek( hb_stream_t *stream, float frac ); static int ffmpeg_seek_ts( hb_stream_t *stream, int64_t ts ); static inline unsigned int bits_get(bitbuf_t *bb, int bits); static inline void bits_init(bitbuf_t *bb, uint8_t* buf, int bufsize, int clear); static inline unsigned int bits_peek(bitbuf_t *bb, int bits); static inline int bits_eob(bitbuf_t *bb); static inline int bits_read_ue(bitbuf_t *bb ); static void pes_add_audio_to_title(hb_stream_t *s, int i, hb_title_t *t, int sort); static int hb_parse_ps( hb_stream_t *stream, uint8_t *buf, int len, hb_pes_info_t *pes_info ); static void hb_ts_resolve_pid_types(hb_stream_t *stream); static void hb_ps_resolve_stream_types(hb_stream_t *stream); void hb_ts_stream_reset(hb_stream_t *stream); void hb_ps_stream_reset(hb_stream_t *stream); /* * logging routines. * these frontend hb_log because transport streams can have a lot of errors * so we want to rate limit messages. this routine limits the number of * messages to at most one per minute of video. other errors that occur * during the minute are counted & the count is output with the next * error msg we print. */ static void ts_warn_helper( hb_stream_t *stream, char *log, va_list args ) { // limit error printing to at most one per minute of video (at 30fps) ++stream->errors; if ( stream->frames - stream->last_error_frame >= 30*60 ) { char msg[256]; vsnprintf( msg, sizeof(msg), log, args ); if ( stream->errors - stream->last_error_count < 10 ) { hb_log( "stream: error near frame %d: %s", stream->frames, msg ); } else { int Edelta = stream->errors - stream->last_error_count; double Epcnt = (double)Edelta * 100. / (stream->frames - stream->last_error_frame); hb_log( "stream: %d new errors (%.0f%%) up to frame %d: %s", Edelta, Epcnt, stream->frames, msg ); } stream->last_error_frame = stream->frames; stream->last_error_count = stream->errors; } } static void ts_warn( hb_stream_t*, char*, ... ) HB_WPRINTF(2,3); static void ts_err( hb_stream_t*, int, char*, ... ) HB_WPRINTF(3,4); static void ts_warn( hb_stream_t *stream, char *log, ... ) { va_list args; va_start( args, log ); ts_warn_helper( stream, log, args ); va_end( args ); } static int get_id(hb_pes_stream_t *pes) { return ( pes->stream_id_ext << 16 ) + pes->stream_id; } static int index_of_id(hb_stream_t *stream, int id) { int i; for ( i = 0; i < stream->pes.count; ++i ) { if ( id == get_id( &stream->pes.list[i] ) ) return i; } return -1; } static int index_of_pid(hb_stream_t *stream, int pid) { int i; for ( i = 0; i < stream->ts.count; ++i ) { if ( pid == stream->ts.list[i].pid ) { return i; } } return -1; } static int index_of_ps_stream(hb_stream_t *stream, int id, int sid) { int i; for ( i = 0; i < stream->pes.count; ++i ) { if ( id == stream->pes.list[i].stream_id && sid == stream->pes.list[i].stream_id_ext ) { return i; } } // If there is no match on the stream_id_ext, try matching // on only the stream_id. for ( i = 0; i < stream->pes.count; ++i ) { if ( id == stream->pes.list[i].stream_id && 0 == stream->pes.list[i].stream_id_ext ) { return i; } } return -1; } static kind_t ts_stream_kind( hb_stream_t * stream, int idx ) { if ( stream->ts.list[idx].pes_list != -1 ) { // Retuns kind for the first pes substream in the pes list // All substreams in a TS stream are the same kind. return stream->pes.list[stream->ts.list[idx].pes_list].stream_kind; } else { return U; } } static kind_t ts_stream_type( hb_stream_t * stream, int idx ) { if ( stream->ts.list[idx].pes_list != -1 ) { // Retuns stream type for the first pes substream in the pes list // All substreams in a TS stream are the same stream type. return stream->pes.list[stream->ts.list[idx].pes_list].stream_type; } else { return 0x00; } } static int pes_index_of_video(hb_stream_t *stream) { int i; for ( i = 0; i < stream->pes.count; ++i ) if ( V == stream->pes.list[i].stream_kind ) return i; return -1; } static int ts_index_of_video(hb_stream_t *stream) { int i; for ( i = 0; i < stream->ts.count; ++i ) if ( V == ts_stream_kind( stream, i ) ) return i; return -1; } static void ts_err( hb_stream_t *stream, int curstream, char *log, ... ) { va_list args; va_start( args, log ); ts_warn_helper( stream, log, args ); va_end( args ); stream->ts.list[curstream].skipbad = 1; stream->ts.list[curstream].continuity = -1; } static int check_ps_sync(const uint8_t *buf) { // a legal MPEG program stream must start with a Pack header in the // first four bytes. return (buf[0] == 0x00) && (buf[1] == 0x00) && (buf[2] == 0x01) && (buf[3] == 0xba); } static int check_ps_sc(const uint8_t *buf) { // a legal MPEG program stream must start with a Pack followed by a // some other start code. If we've already verified the pack, this skip // it and checks for a start code prefix. int pos; int mark = buf[4] >> 4; if ( mark == 0x02 ) { // Check other marker bits to make it less likely // that we are being spoofed. if( ( buf[4] & 0xf1 ) != 0x21 || ( buf[6] & 0x01 ) != 0x01 || ( buf[8] & 0x01 ) != 0x01 || ( buf[9] & 0x80 ) != 0x80 || ( buf[11] & 0x01 ) != 0x01 ) { return 0; } // mpeg-1 pack header pos = 12; // skip over the PACK } else { // Check other marker bits to make it less likely // that we are being spoofed. if( ( buf[4] & 0xC4 ) != 0x44 || ( buf[6] & 0x04 ) != 0x04 || ( buf[8] & 0x04 ) != 0x04 || ( buf[9] & 0x01 ) != 0x01 || ( buf[12] & 0x03 ) != 0x03 ) { return 0; } // mpeg-2 pack header pos = 14 + ( buf[13] & 0x7 ); // skip over the PACK } return (buf[pos+0] == 0x00) && (buf[pos+1] == 0x00) && (buf[pos+2] == 0x01); } static int check_ts_sync(const uint8_t *buf) { // must have initial sync byte & a legal adaptation ctrl return (buf[0] == 0x47) && (((buf[3] & 0x30) >> 4) > 0); } static int have_ts_sync(const uint8_t *buf, int psize, int count) { int ii; for ( ii = 0; ii < count; ii++ ) { if ( !check_ts_sync(&buf[ii*psize]) ) return 0; } return 1; } static int hb_stream_check_for_ts(const uint8_t *buf) { // transport streams should have a sync byte every 188 bytes. // search the first 8KB of buf looking for at least 8 consecutive // correctly located sync patterns. int offset = 0; int count = 16; for ( offset = 0; offset < 8*1024-count*188; ++offset ) { if ( have_ts_sync( &buf[offset], 188, count) ) return 188 | (offset << 8); if ( have_ts_sync( &buf[offset], 192, count) ) return 192 | (offset << 8); if ( have_ts_sync( &buf[offset], 204, count) ) return 204 | (offset << 8); if ( have_ts_sync( &buf[offset], 208, count) ) return 208 | (offset << 8); } return 0; } static int hb_stream_check_for_ps(hb_stream_t *stream) { uint8_t buf[2048*4]; uint8_t sc_buf[4]; int pos = 0; fseek(stream->file_handle, 0, SEEK_SET); // program streams should start with a PACK then some other mpeg start // code (usually a SYS but that might be missing if we only have a clip). while (pos < 512 * 1024) { int offset; if ( fread(buf, 1, sizeof(buf), stream->file_handle) != sizeof(buf) ) return 0; for ( offset = 0; offset < 8*1024-27; ++offset ) { if ( check_ps_sync( &buf[offset] ) && check_ps_sc( &buf[offset] ) ) { int pes_offset, prev, data_len; uint8_t sid; uint8_t *b = buf+offset; // Skip the pack header int mark = buf[4] >> 4; if ( mark == 0x02 ) { // mpeg-1 pack header pes_offset = 12; } else { // mpeg-2 pack header pes_offset = 14 + ( buf[13] & 0x7 ); } b += pes_offset; // Get the next stream id sid = b[3]; data_len = (b[4] << 8) + b[5]; if ( data_len && sid > 0xba && sid < 0xf9 ) { prev = ftell( stream->file_handle ); pos = prev - ( sizeof(buf) - offset ); pos += pes_offset + 6 + data_len; fseek( stream->file_handle, pos, SEEK_SET ); if ( fread(sc_buf, 1, 4, stream->file_handle) != 4 ) return 0; if (sc_buf[0] == 0x00 && sc_buf[1] == 0x00 && sc_buf[2] == 0x01) { return 1; } fseek( stream->file_handle, prev, SEEK_SET ); } } } fseek( stream->file_handle, -27, SEEK_CUR ); pos = ftell( stream->file_handle ); } return 0; } static int hb_stream_get_type(hb_stream_t *stream) { uint8_t buf[2048*4]; if ( fread(buf, 1, sizeof(buf), stream->file_handle) == sizeof(buf) ) { #ifdef USE_HWD if ( hb_gui_use_hwd_flag == 1 ) return 0; #endif int psize; if ( ( psize = hb_stream_check_for_ts(buf) ) != 0 ) { int offset = psize >> 8; psize &= 0xff; hb_log("file is MPEG Transport Stream with %d byte packets" " offset %d bytes", psize, offset); stream->packetsize = psize; stream->hb_stream_type = transport; if (hb_ts_stream_init(stream) == 0) return 1; } else if ( hb_stream_check_for_ps(stream) != 0 ) { hb_log("file is MPEG Program Stream"); stream->hb_stream_type = program; hb_ps_stream_init(stream); // We default to mpeg codec for ps streams if no // video found in program stream map return 1; } } return 0; } static void hb_stream_delete_dynamic( hb_stream_t *d ) { if( d->file_handle ) { fclose( d->file_handle ); d->file_handle = NULL; } int i=0; if ( d->ts.packet ) { free( d->ts.packet ); d->ts.packet = NULL; } if ( d->ts.list ) { for (i = 0; i < d->ts.count; i++) { if (d->ts.list[i].buf) { hb_buffer_close(&(d->ts.list[i].buf)); hb_buffer_close(&(d->ts.list[i].extra_buf)); d->ts.list[i].buf = NULL; d->ts.list[i].extra_buf = NULL; } } } } static void hb_stream_delete( hb_stream_t *d ) { hb_stream_delete_dynamic( d ); free( d->ts.list ); free( d->pes.list ); free( d->path ); free( d ); } static int audio_inactive( hb_stream_t *stream, int id, int stream_id_ext ) { if ( id < 0 ) { // PID declared inactive by hb_stream_title_scan return 1; } if ( id == stream->pmt_info.PCR_PID ) { // PCR PID is always active return 0; } int i; for ( i = 0; i < hb_list_count( stream->title->list_audio ); ++i ) { hb_audio_t *audio = hb_list_item( stream->title->list_audio, i ); if ( audio->id == ((stream_id_ext << 16) | id) ) { return 0; } } return 1; } /* when the file was first opened we made entries for all the audio elementary * streams we found in it. Streams that were later found during the preview scan * now have an audio codec, type, rate, etc., associated with them. At the end * of the scan we delete all the audio entries that weren't found by the scan * or don't have a format we support. This routine deletes audio entry 'indx' * by setting its PID to an invalid value so no packet will match it. (We can't * move any of the entries since the index of the entry is used as the id * of the media stream for HB. */ static void hb_stream_delete_ts_entry(hb_stream_t *stream, int indx) { if ( stream->ts.list[indx].pid > 0 ) { stream->ts.list[indx].pid = -stream->ts.list[indx].pid; } } static int hb_stream_try_delete_ts_entry(hb_stream_t *stream, int indx) { int ii; if ( stream->ts.list[indx].pid < 0 ) return 1; for ( ii = stream->ts.list[indx].pes_list; ii != -1; ii = stream->pes.list[ii].next ) { if ( stream->pes.list[ii].stream_id >= 0 ) return 0; } stream->ts.list[indx].pid = -stream->ts.list[indx].pid; return 1; } static void hb_stream_delete_ps_entry(hb_stream_t *stream, int indx) { if ( stream->pes.list[indx].stream_id > 0 ) { stream->pes.list[indx].stream_id = -stream->pes.list[indx].stream_id; } } static void prune_streams(hb_stream_t *d) { if ( d->hb_stream_type == transport ) { int ii, jj; for ( ii = 0; ii < d->ts.count; ii++) { // If probing didn't find audio or video, and the pid // is not the PCR, remove the track if ( ts_stream_kind ( d, ii ) == U && !d->ts.list[ii].is_pcr ) { hb_stream_delete_ts_entry(d, ii); continue; } if ( ts_stream_kind ( d, ii ) == A ) { for ( jj = d->ts.list[ii].pes_list; jj != -1; jj = d->pes.list[jj].next ) { if ( audio_inactive( d, d->pes.list[jj].stream_id, d->pes.list[jj].stream_id_ext ) ) { hb_stream_delete_ps_entry(d, jj); } } if ( !d->ts.list[ii].is_pcr && hb_stream_try_delete_ts_entry(d, ii) ) { continue; } } } // reset to beginning of file and reset some stream // state information hb_stream_seek( d, 0. ); } else if ( d->hb_stream_type == program ) { int ii; for ( ii = 0; ii < d->pes.count; ii++) { // If probing didn't find audio or video, remove the track if ( d->pes.list[ii].stream_kind == U ) { hb_stream_delete_ps_entry(d, ii); } if ( d->pes.list[ii].stream_kind == A && audio_inactive( d, d->pes.list[ii].stream_id, d->pes.list[ii].stream_id_ext ) ) { // this PID isn't wanted (we don't have a codec for it // or scan didn't find audio parameters) hb_stream_delete_ps_entry(d, ii); continue; } } // reset to beginning of file and reset some stream // state information hb_stream_seek( d, 0. ); } } /*********************************************************************** * hb_stream_open *********************************************************************** * **********************************************************************/ hb_stream_t * hb_stream_open( char *path, hb_title_t *title, int scan ) { FILE *f = hb_fopen(path, "rb"); if ( f == NULL ) { hb_log( "hb_stream_open: open %s failed", path ); return NULL; } hb_stream_t *d = calloc( sizeof( hb_stream_t ), 1 ); if ( d == NULL ) { fclose( f ); hb_log( "hb_stream_open: can't allocate space for %s stream state", path ); return NULL; } if( title && !( title->flags & HBTF_NO_IDR ) ) { d->has_IDRs = 1; } /* * If it's something we can deal with (MPEG2 PS or TS) return a stream * reference structure & null otherwise. */ d->file_handle = f; d->title = title; d->scan = scan; d->path = strdup( path ); if (d->path != NULL ) { if ( hb_stream_get_type( d ) != 0 ) { if( !scan ) { prune_streams( d ); } // reset to beginning of file and reset some stream // state information hb_stream_seek( d, 0. ); return d; } fclose( d->file_handle ); d->file_handle = NULL; if ( ffmpeg_open( d, title, scan ) ) { return d; } } if ( d->file_handle ) { fclose( d->file_handle ); } if (d->path) { free( d->path ); } hb_log( "hb_stream_open: open %s failed", path ); free( d ); return NULL; } static int new_pid( hb_stream_t * stream ) { int num = stream->ts.alloc; if ( stream->ts.count == stream->ts.alloc ) { num = stream->ts.alloc ? stream->ts.alloc * 2 : 32; stream->ts.list = realloc( stream->ts.list, sizeof( hb_ts_stream_t ) * num ); } int ii; for ( ii = stream->ts.alloc; ii < num; ii++ ) { memset(&stream->ts.list[ii], 0, sizeof( hb_ts_stream_t )); stream->ts.list[ii].continuity = -1; stream->ts.list[ii].pid = -1; stream->ts.list[ii].pes_list = -1; } stream->ts.alloc = num; num = stream->ts.count; stream->ts.count++; return num; } static int new_pes( hb_stream_t * stream ) { int num = stream->pes.alloc; if ( stream->pes.count == stream->pes.alloc ) { num = stream->pes.alloc ? stream->pes.alloc * 2 : 32; stream->pes.list = realloc( stream->pes.list, sizeof( hb_pes_stream_t ) * num ); } int ii; for ( ii = stream->pes.alloc; ii < num; ii++ ) { memset(&stream->pes.list[ii], 0, sizeof( hb_pes_stream_t )); stream->pes.list[ii].stream_id = -1; stream->pes.list[ii].next = -1; } stream->pes.alloc = num; num = stream->pes.count; stream->pes.count++; return num; } hb_stream_t * hb_bd_stream_open( hb_title_t *title ) { int ii; hb_stream_t *d = calloc( sizeof( hb_stream_t ), 1 ); if ( d == NULL ) { hb_error( "hb_bd_stream_open: can't allocate space for stream state" ); return NULL; } d->file_handle = NULL; d->title = title; d->path = NULL; d->ts.packet = NULL; int pid = title->video_id; int stream_type = title->video_stream_type; update_ts_streams( d, pid, 0, stream_type, V, NULL ); hb_audio_t * audio; for ( ii = 0; ( audio = hb_list_item( title->list_audio, ii ) ); ++ii ) { int stream_id_ext = audio->config.in.substream_type; pid = audio->id & 0xFFFF; stream_type = audio->config.in.stream_type; update_ts_streams( d, pid, stream_id_ext, stream_type, A, NULL ); } hb_subtitle_t * subtitle; for ( ii = 0; ( subtitle = hb_list_item( title->list_subtitle, ii ) ); ++ii ) { // If the subtitle track is CC embedded in the video stream, then // it does not have an independent pid. In this case, we assigned // the subtitle->id to 0. if (subtitle->id != 0) { pid = subtitle->id & 0xFFFF; stream_type = subtitle->stream_type; update_ts_streams( d, pid, 0, stream_type, S, NULL ); } } // We don't need to wait for a PCR when scanning. In fact, it // trips us up on the first preview of every title since we would // have to read quite a lot of data before finding the PCR. if ( title->flags & HBTF_SCAN_COMPLETE ) { /* BD has PCRs, but the BD index always points to a packet * after a PCR packet, so we will not see the initial PCR * after any seek. So don't set the flag that causes us * to drop packets till we see a PCR. */ //d->ts_flags = TS_HAS_RAP | TS_HAS_PCR; // BD PCR PID is specified to always be 0x1001 update_ts_streams( d, 0x1001, 0, -1, P, NULL ); } d->packetsize = 192; d->hb_stream_type = transport; for ( ii = 0; ii < d->ts.count; ii++ ) { d->ts.list[ii].buf = hb_buffer_init(d->packetsize); d->ts.list[ii].extra_buf = hb_buffer_init(d->packetsize); d->ts.list[ii].buf->size = 0; d->ts.list[ii].extra_buf->size = 0; } return d; } /*********************************************************************** * hb_stream_close *********************************************************************** * Closes and frees everything **********************************************************************/ void hb_stream_close( hb_stream_t ** _d ) { hb_stream_t *stream = * _d; if ( stream->hb_stream_type == ffmpeg ) { ffmpeg_close( stream ); hb_stream_delete( stream ); *_d = NULL; return; } if ( stream->frames ) { hb_log( "stream: %d good frames, %d errors (%.0f%%)", stream->frames, stream->errors, (double)stream->errors * 100. / (double)stream->frames ); } hb_stream_delete( stream ); *_d = NULL; } /*********************************************************************** * hb_ps_stream_title_scan *********************************************************************** * **********************************************************************/ hb_title_t * hb_stream_title_scan(hb_stream_t *stream, hb_title_t * title) { if ( stream->hb_stream_type == ffmpeg ) return ffmpeg_title_scan( stream, title ); // 'Barebones Title' title->type = HB_STREAM_TYPE; title->index = 1; // Copy part of the stream path to the title name char *sep = hb_strr_dir_sep(stream->path); if (sep) strcpy(title->name, sep+1); char *dot_term = strrchr(title->name, '.'); if (dot_term) *dot_term = '\0'; // Figure out how many audio streams we really have: // - For transport streams, for each PID listed in the PMT (whether // or not it was an audio stream type) read the bitstream until we // find an packet from that PID containing a PES header and see if // the elementary stream is an audio type. // - For program streams read the first 4MB and take every unique // audio stream we find. hb_init_audio_list(stream, title); hb_init_subtitle_list(stream, title); // set the video id, codec & muxer int idx = pes_index_of_video( stream ); if ( idx < 0 ) { hb_title_close( &title ); return NULL; } title->video_id = get_id( &stream->pes.list[idx] ); title->video_codec = stream->pes.list[idx].codec; title->video_codec_param = stream->pes.list[idx].codec_param; if (stream->hb_stream_type == transport) { title->demuxer = HB_TS_DEMUXER; // make sure we're grabbing the PCR PID update_ts_streams( stream, stream->pmt_info.PCR_PID, 0, -1, P, NULL ); } else { title->demuxer = HB_PS_DEMUXER; } // IDRs will be search for in hb_stream_duration stream->has_IDRs = 0; hb_stream_duration(stream, title); // One Chapter hb_chapter_t * chapter; chapter = calloc( sizeof( hb_chapter_t ), 1 ); hb_chapter_set_title( chapter, "Chapter 1" ); chapter->index = 1; chapter->duration = title->duration; chapter->hours = title->hours; chapter->minutes = title->minutes; chapter->seconds = title->seconds; hb_list_add( title->list_chapter, chapter ); if ( stream->has_IDRs < 1 ) { hb_log( "stream doesn't seem to have video IDR frames" ); title->flags |= HBTF_NO_IDR; } if ( stream->hb_stream_type == transport && ( stream->ts_flags & TS_HAS_PCR ) == 0 ) { hb_log( "transport stream missing PCRs - using video DTS instead" ); } #ifdef USE_HWD hb_va_dxva2_t * dxva2 = NULL; dxva2 = hb_va_create_dxva2( dxva2, title->video_codec_param ); if ( dxva2 ) { title->hwd_support = 1; hb_va_close(dxva2); dxva2 = NULL; } else title->hwd_support = 0; #else title->hwd_support = 0; #endif // Height, width, rate and aspect ratio information is filled in // when the previews are built return title; } /* * read the next transport stream packet from 'stream'. Return NULL if * we hit eof & a pointer to the sync byte otherwise. */ static const uint8_t *next_packet( hb_stream_t *stream ) { uint8_t *buf = stream->ts.packet + stream->packetsize - 188; while ( 1 ) { if ( fread(stream->ts.packet, 1, stream->packetsize, stream->file_handle) != stream->packetsize ) { return NULL; } if (buf[0] == 0x47) { return buf; } // lost sync - back up to where we started then try to re-establish. off_t pos = ftello(stream->file_handle) - stream->packetsize; off_t pos2 = align_to_next_packet(stream); if ( pos2 == 0 ) { hb_log( "next_packet: eof while re-establishing sync @ %"PRId64, pos ); return NULL; } ts_warn( stream, "next_packet: sync lost @ %"PRId64", regained after %"PRId64" bytes", pos, pos2 ); } } /* * skip to the start of the next PACK header in program stream src_stream. */ static void skip_to_next_pack( hb_stream_t *src_stream ) { // scan forward until we find the start of the next pack uint32_t strt_code = -1; int c; flockfile( src_stream->file_handle ); while ( ( c = getc_unlocked( src_stream->file_handle ) ) != EOF ) { strt_code = ( strt_code << 8 ) | c; if ( strt_code == 0x000001ba ) // we found the start of the next pack break; } funlockfile( src_stream->file_handle ); // if we didn't terminate on an eof back up so the next read // starts on the pack boundary. if ( c != EOF ) { fseeko( src_stream->file_handle, -4, SEEK_CUR ); } } static void CreateDecodedNAL( uint8_t **dst, int *dst_len, const uint8_t *src, int src_len ) { const uint8_t *end = &src[src_len]; uint8_t *d = malloc( src_len ); *dst = d; if( d ) { while( src < end ) { if( src < end - 3 && src[0] == 0x00 && src[1] == 0x00 && src[2] == 0x01 ) { // Next start code found break; } if( src < end - 3 && src[0] == 0x00 && src[1] == 0x00 && src[2] == 0x03 ) { *d++ = 0x00; *d++ = 0x00; src += 3; continue; } *d++ = *src++; } } *dst_len = d - *dst; } static int isRecoveryPoint( const uint8_t *buf, int len ) { uint8_t *nal; int nal_len; int ii, type, size; int recovery_frames = 0; CreateDecodedNAL( &nal, &nal_len, buf, len ); for ( ii = 0; ii+1 < nal_len; ) { type = 0; while ( ii+1 < nal_len ) { type += nal[ii++]; if ( nal[ii-1] != 0xff ) break; } size = 0; while ( ii+1 < nal_len ) { size += nal[ii++]; if ( nal[ii-1] != 0xff ) break; } if( type == 6 ) { recovery_frames = 1; break; } ii += size; } free( nal ); return recovery_frames; } static int isIframe( hb_stream_t *stream, const uint8_t *buf, int len ) { // For mpeg2: look for a gop start or i-frame picture start // for h.264: look for idr nal type or a slice header for an i-frame // for vc1: look for a Sequence header int ii; uint32_t strid = 0; int vid = pes_index_of_video( stream ); hb_pes_stream_t *pes = &stream->pes.list[vid]; if ( pes->stream_type <= 2 || pes->codec_param == AV_CODEC_ID_MPEG1VIDEO || pes->codec_param == AV_CODEC_ID_MPEG2VIDEO ) { // This section of the code handles MPEG-1 and MPEG-2 video streams for (ii = 0; ii < len; ii++) { strid = (strid << 8) | buf[ii]; if ( ( strid >> 8 ) == 1 ) { // we found a start code uint8_t id = strid; switch ( id ) { case 0xB8: // group_start_code (GOP header) case 0xB3: // sequence_header code return 1; case 0x00: // picture_start_code // picture_header, let's see if it's an I-frame if (ii < len - 3) { // check if picture_coding_type == 1 if ((buf[ii+2] & (0x7 << 3)) == (1 << 3)) { // found an I-frame picture return 1; } } break; } } } // didn't find an I-frame return 0; } if ( pes->stream_type == 0x1b || pes->codec_param == AV_CODEC_ID_H264 ) { // we have an h.264 stream for (ii = 0; ii < len; ii++) { strid = (strid << 8) | buf[ii]; if ( ( strid >> 8 ) == 1 ) { // we found a start code - remove the ref_idc from the nal type uint8_t nal_type = strid & 0x1f; if ( nal_type == 0x01 ) { // Found slice and no recovery point return 0; } if ( nal_type == 0x05 ) { // h.264 IDR picture start return 1; } else if ( nal_type == 0x06 ) { int off = ii + 1; int recovery_frames = isRecoveryPoint( buf+off, len-off ); if ( recovery_frames ) { return recovery_frames; } } } } // didn't find an I-frame return 0; } if ( pes->stream_type == 0xea || pes->codec_param == AV_CODEC_ID_VC1 ) { // we have an vc1 stream for (ii = 0; ii < len; ii++) { strid = (strid << 8) | buf[ii]; if ( strid == 0x10f ) { // the ffmpeg vc1 decoder requires a seq hdr code in the first // frame. return 1; } } // didn't find an I-frame return 0; } if ( pes->stream_type == 0x10 || pes->codec_param == AV_CODEC_ID_MPEG4 ) { // we have an mpeg4 stream for (ii = 0; ii < len-1; ii++) { strid = (strid << 8) | buf[ii]; if ( strid == 0x1b6 ) { if ((buf[ii+1] & 0xC0) == 0) return 1; } } // didn't find an I-frame return 0; } // we don't understand the stream type so just say "yes" otherwise // we'll discard all the video. return 1; } static int ts_isIframe( hb_stream_t *stream, const uint8_t *buf, int adapt_len ) { return isIframe( stream, buf + 13 + adapt_len, 188 - ( 13 + adapt_len ) ); } /* * scan the next MB of 'stream' to find the next start packet for * the Packetized Elementary Stream associated with TS PID 'pid'. */ static const uint8_t *hb_ts_stream_getPEStype(hb_stream_t *stream, uint32_t pid, int *out_adapt_len) { int npack = 300000; // max packets to read while (--npack >= 0) { const uint8_t *buf = next_packet( stream ); if ( buf == NULL ) { hb_log("hb_ts_stream_getPEStype: EOF while searching for PID 0x%x", pid); return 0; } // while we're reading the stream, check if it has valid PCRs // and/or random access points. uint32_t pack_pid = ( (buf[1] & 0x1f) << 8 ) | buf[2]; if ( pack_pid == stream->pmt_info.PCR_PID ) { if ( ( buf[5] & 0x10 ) && ( ( ( buf[3] & 0x30 ) == 0x20 ) || ( ( buf[3] & 0x30 ) == 0x30 && buf[4] > 6 ) ) ) { stream->ts_flags |= TS_HAS_PCR; } } if ( buf[5] & 0x40 ) { stream->ts_flags |= TS_HAS_RAP; } /* * The PES header is only in TS packets with 'start' set so we check * that first then check for the right PID. */ if ((buf[1] & 0x40) == 0 || pack_pid != pid ) { // not a start packet or not the pid we want continue; } int adapt_len = 0; /* skip over the TS hdr to return a pointer to the PES hdr */ switch (buf[3] & 0x30) { case 0x00: // illegal case 0x20: // fill packet continue; case 0x30: // adaptation adapt_len = buf[4] + 1; if (adapt_len > 184) { hb_log("hb_ts_stream_getPEStype: invalid adaptation field length %d for PID 0x%x", buf[4], pid); continue; } break; } /* PES hdr has to begin with an mpeg start code */ if (buf[adapt_len+4] == 0x00 && buf[adapt_len+5] == 0x00 && buf[adapt_len+6] == 0x01) { *out_adapt_len = adapt_len; return buf; } } /* didn't find it */ return 0; } static hb_buffer_t * hb_ps_stream_getVideo( hb_stream_t *stream, hb_pes_info_t *pi) { hb_buffer_t *buf = hb_buffer_init(HB_DVD_READ_BUFFER_SIZE); hb_pes_info_t pes_info; // how many blocks we read while searching for a video PES header int blksleft = 2048; while (--blksleft >= 0) { buf->size = 0; int len = hb_ps_read_packet( stream, buf ); if ( len == 0 ) { // EOF break; } if ( !hb_parse_ps( stream, buf->data, buf->size, &pes_info ) ) continue; int idx; if ( pes_info.stream_id == 0xbd ) { idx = index_of_ps_stream( stream, pes_info.stream_id, pes_info.bd_substream_id ); } else { idx = index_of_ps_stream( stream, pes_info.stream_id, pes_info.stream_id_ext ); } if ( stream->pes.list[idx].stream_kind == V ) { if ( pes_info.pts != AV_NOPTS_VALUE ) { *pi = pes_info; return buf; } } } hb_buffer_close( &buf ); return NULL; } /*********************************************************************** * hb_stream_duration *********************************************************************** * * Finding stream duration is difficult. One issue is that the video file * may have chunks from several different program fragments (main feature, * commercials, station id, trailers, etc.) all with their own base pts * value. We can't find the piece boundaries without reading the entire * file but if we compute a rate based on time stamps from two different * pieces the result will be meaningless. The second issue is that the * data rate of compressed video normally varies by 5-10x over the length * of the video. This says that we want to compute the rate over relatively * long segments to get a representative average but long segments increase * the likelihood that we'll cross a piece boundary. * * What we do is take time stamp samples at several places in the file * (currently 16) then compute the average rate (i.e., ticks of video per * byte of the file) for all pairs of samples (N^2 rates computed for N * samples). Some of those rates will be absurd because the samples came * from different segments. Some will be way low or high because the * samples came from a low or high motion part of the segment. But given * that we're comparing *all* pairs the majority of the computed rates * should be near the overall average. So we median filter the computed * rates to pick the most representative value. * **********************************************************************/ struct pts_pos { uint64_t pos; /* file position of this PTS sample */ uint64_t pts; /* PTS from video stream */ }; #define NDURSAMPLES 128 // get one (position, timestamp) sampple from a transport or program // stream. static struct pts_pos hb_sample_pts(hb_stream_t *stream, uint64_t fpos) { struct pts_pos pp = { 0, 0 }; if ( stream->hb_stream_type == transport ) { const uint8_t *buf; int adapt_len; fseeko( stream->file_handle, fpos, SEEK_SET ); align_to_next_packet( stream ); int pid = stream->ts.list[ts_index_of_video(stream)].pid; buf = hb_ts_stream_getPEStype( stream, pid, &adapt_len ); if ( buf == NULL ) { hb_log("hb_sample_pts: couldn't find video packet near %"PRIu64, fpos); return pp; } const uint8_t *pes = buf + 4 + adapt_len; if ( ( pes[7] >> 7 ) != 1 ) { hb_log("hb_sample_pts: no PTS in video packet near %"PRIu64, fpos); return pp; } pp.pts = ((((uint64_t)pes[ 9] >> 1 ) & 7) << 30) | ( (uint64_t)pes[10] << 22) | ( ((uint64_t)pes[11] >> 1 ) << 15) | ( (uint64_t)pes[12] << 7 ) | ( (uint64_t)pes[13] >> 1 ); if ( ts_isIframe( stream, buf, adapt_len ) ) { if ( stream->has_IDRs < 255 ) { ++stream->has_IDRs; } } pp.pos = ftello(stream->file_handle); if ( !stream->has_IDRs ) { // Scan a little more to see if we will stumble upon one int ii; for ( ii = 0; ii < 10; ii++ ) { buf = hb_ts_stream_getPEStype( stream, pid, &adapt_len ); if ( buf == NULL ) break; if ( ts_isIframe( stream, buf, adapt_len ) ) { ++stream->has_IDRs; break; } } } } else { hb_buffer_t *buf; hb_pes_info_t pes_info; // round address down to nearest dvd sector start fpos &=~ ( HB_DVD_READ_BUFFER_SIZE - 1 ); fseeko( stream->file_handle, fpos, SEEK_SET ); if ( stream->hb_stream_type == program ) { skip_to_next_pack( stream ); } buf = hb_ps_stream_getVideo( stream, &pes_info ); if ( buf == NULL ) { hb_log("hb_sample_pts: couldn't find video packet near %"PRIu64, fpos); return pp; } if ( pes_info.pts < 0 ) { hb_log("hb_sample_pts: no PTS in video packet near %"PRIu64, fpos); hb_buffer_close( &buf ); return pp; } if ( isIframe( stream, buf->data, buf->size ) ) { if ( stream->has_IDRs < 255 ) { ++stream->has_IDRs; } } hb_buffer_close( &buf ); if ( !stream->has_IDRs ) { // Scan a little more to see if we will stumble upon one int ii; for ( ii = 0; ii < 10; ii++ ) { buf = hb_ps_stream_getVideo( stream, &pes_info ); if ( buf == NULL ) break; if ( isIframe( stream, buf->data, buf->size ) ) { ++stream->has_IDRs; hb_buffer_close( &buf ); break; } hb_buffer_close( &buf ); } } pp.pts = pes_info.pts; pp.pos = ftello(stream->file_handle); } return pp; } static int dur_compare( const void *a, const void *b ) { const double *aval = a, *bval = b; return ( *aval < *bval ? -1 : ( *aval == *bval ? 0 : 1 ) ); } // given an array of (position, time) samples, compute a max-likelihood // estimate of the average rate by computing the rate between all pairs // of samples then taking the median of those rates. static double compute_stream_rate( struct pts_pos *pp, int n ) { int i, j; double rates[NDURSAMPLES * NDURSAMPLES / 8]; double *rp = rates; // the following nested loops compute the rates between all pairs. *rp = 0; for ( i = 0; i < n-1; ++i ) { // Bias the median filter by not including pairs that are "far" // from one another. This is to handle cases where the file is // made of roughly equal size pieces where a symmetric choice of // pairs results in having the same number of intra-piece & // inter-piece rate estimates. This would mean that the median // could easily fall in the inter-piece part of the data which // would give a bogus estimate. The 'ns' index creates an // asymmetry that favors locality. int ns = i + ( n >> 3 ); if ( ns > n ) ns = n; for ( j = i+1; j < ns; ++j ) { if ( (uint64_t)(pp[j].pts - pp[i].pts) > 90000LL*3600*6 ) break; if ( pp[j].pts != pp[i].pts && pp[j].pos > pp[i].pos ) { *rp = ((double)( pp[j].pts - pp[i].pts )) / ((double)( pp[j].pos - pp[i].pos )); ++rp; } } } // now compute and return the median of all the (n*n/2) rates we computed // above. int nrates = rp - rates; qsort( rates, nrates, sizeof (rates[0] ), dur_compare ); return rates[nrates >> 1]; } static void hb_stream_duration(hb_stream_t *stream, hb_title_t *inTitle) { struct pts_pos ptspos[NDURSAMPLES]; struct pts_pos *pp = ptspos; int i; fseeko(stream->file_handle, 0, SEEK_END); uint64_t fsize = ftello(stream->file_handle); uint64_t fincr = fsize / NDURSAMPLES; uint64_t fpos = fincr / 2; for ( i = NDURSAMPLES; --i >= 0; fpos += fincr ) { *pp++ = hb_sample_pts(stream, fpos); } uint64_t dur = compute_stream_rate( ptspos, pp - ptspos ) * (double)fsize; inTitle->duration = dur; dur /= 90000; inTitle->hours = dur / 3600; inTitle->minutes = ( dur % 3600 ) / 60; inTitle->seconds = dur % 60; rewind(stream->file_handle); } /*********************************************************************** * hb_stream_read *********************************************************************** * **********************************************************************/ hb_buffer_t * hb_stream_read( hb_stream_t * src_stream ) { if ( src_stream->hb_stream_type == ffmpeg ) { return hb_ffmpeg_read( src_stream ); } if ( src_stream->hb_stream_type == program ) { return hb_ps_stream_decode( src_stream ); } return hb_ts_stream_decode( src_stream ); } int64_t ffmpeg_initial_timestamp( hb_stream_t * stream ) { AVFormatContext *ic = stream->ffmpeg_ic; if ( ic->start_time != AV_NOPTS_VALUE && ic->start_time > 0 ) return ic->start_time; else return 0; } int hb_stream_seek_chapter( hb_stream_t * stream, int chapter_num ) { if ( stream->hb_stream_type != ffmpeg ) { // currently meaningliess for transport and program streams return 1; } if ( !stream || !stream->title || chapter_num > hb_list_count( stream->title->list_chapter ) ) { return 0; } int64_t sum_dur = 0; hb_chapter_t *chapter = NULL; int i; for ( i = 0; i < chapter_num; ++i) { chapter = hb_list_item( stream->title->list_chapter, i ); sum_dur += chapter->duration; } stream->chapter = chapter_num - 1; stream->chapter_end = sum_dur; int64_t pos = ( ( ( sum_dur - chapter->duration ) * AV_TIME_BASE ) / 90000 ) + ffmpeg_initial_timestamp( stream ); hb_deep_log( 2, "Seeking to chapter %d: starts %"PRId64", ends %"PRId64", AV pos %"PRId64, chapter_num, sum_dur - chapter->duration, sum_dur, pos); if ( chapter_num > 1 && pos > 0 ) { AVStream *st = stream->ffmpeg_ic->streams[stream->ffmpeg_video_id]; // timebase must be adjusted to match timebase of stream we are // using for seeking. pos = av_rescale(pos, st->time_base.den, AV_TIME_BASE * (int64_t)st->time_base.num); avformat_seek_file( stream->ffmpeg_ic, stream->ffmpeg_video_id, 0, pos, pos, AVSEEK_FLAG_BACKWARD); } return 1; } /*********************************************************************** * hb_stream_chapter *********************************************************************** * Return the number of the chapter that we are currently in. We store * the chapter number starting from 0, so + 1 for the real chpater num. **********************************************************************/ int hb_stream_chapter( hb_stream_t * src_stream ) { return( src_stream->chapter + 1 ); } /*********************************************************************** * hb_stream_seek *********************************************************************** * **********************************************************************/ int hb_stream_seek( hb_stream_t * stream, float f ) { if ( stream->hb_stream_type == ffmpeg ) { return ffmpeg_seek( stream, f ); } off_t stream_size, cur_pos, new_pos; double pos_ratio = f; cur_pos = ftello( stream->file_handle ); fseeko( stream->file_handle, 0, SEEK_END ); stream_size = ftello( stream->file_handle ); new_pos = (off_t) ((double) (stream_size) * pos_ratio); new_pos &=~ (HB_DVD_READ_BUFFER_SIZE - 1); int r = fseeko( stream->file_handle, new_pos, SEEK_SET ); if (r == -1) { fseeko( stream->file_handle, cur_pos, SEEK_SET ); return 0; } if ( stream->hb_stream_type == transport ) { // We need to drop the current decoder output and move // forwards to the next transport stream packet. hb_ts_stream_reset(stream); align_to_next_packet(stream); if ( !stream->has_IDRs ) { // the stream has no IDRs so don't look for one. stream->need_keyframe = 0; } } else if ( stream->hb_stream_type == program ) { hb_ps_stream_reset(stream); skip_to_next_pack( stream ); if ( !stream->has_IDRs ) { // the stream has no IDRs so don't look for one. stream->need_keyframe = 0; } } return 1; } int hb_stream_seek_ts( hb_stream_t * stream, int64_t ts ) { if ( stream->hb_stream_type == ffmpeg ) { return ffmpeg_seek_ts( stream, ts ); } return -1; } static char* strncpyupper( char *dst, const char *src, int len ) { int ii; for ( ii = 0; ii < len-1 && src[ii]; ii++ ) { dst[ii] = islower(src[ii]) ? toupper(src[ii]) : src[ii]; } dst[ii] = '\0'; return dst; } static const char *stream_type_name2(hb_stream_t *stream, hb_pes_stream_t *pes) { static char codec_name_caps[80]; if ( stream->reg_desc == STR4_TO_UINT32("HDMV") ) { // Names for streams we know about. switch ( pes->stream_type ) { case 0x80: return "BD LPCM"; case 0x83: return "TrueHD"; case 0x84: return "E-AC3"; case 0x85: return "DTS-HD HRA"; case 0x86: return "DTS-HD MA"; default: break; } } if ( st2codec[pes->stream_type].name ) { return st2codec[pes->stream_type].name; } if ( pes->codec_name[0] != 0 ) { return pes->codec_name; } if ( pes->codec & HB_ACODEC_FF_MASK ) { AVCodec * codec = avcodec_find_decoder( pes->codec_param ); if ( codec && codec->name && codec->name[0] ) { strncpyupper( codec_name_caps, codec->name, 80 ); return codec_name_caps; } } return "Unknown"; } static void set_audio_description(hb_audio_t *audio, iso639_lang_t *lang) { snprintf( audio->config.lang.simple, sizeof( audio->config.lang.simple ), "%s", strlen( lang->native_name ) ? lang->native_name : lang->eng_name ); snprintf( audio->config.lang.iso639_2, sizeof( audio->config.lang.iso639_2 ), "%s", lang->iso639_2 ); audio->config.lang.type = 0; } // Sort specifies the index in the audio list where you would // like sorted items to begin. static void pes_add_subtitle_to_title( hb_stream_t *stream, int idx, hb_title_t *title, int sort) { hb_pes_stream_t *pes = &stream->pes.list[idx]; // Sort by id when adding to the list // This assures that they are always displayed in the same order int id = get_id( pes ); int i; hb_subtitle_t *tmp = NULL; int count = hb_list_count( title->list_subtitle ); // Don't add the same audio twice. Search for audio. for ( i = 0; i < count; i++ ) { tmp = hb_list_item( title->list_subtitle, i ); if ( id == tmp->id ) return; } hb_subtitle_t *subtitle = calloc( sizeof( hb_subtitle_t ), 1 ); iso639_lang_t * lang; subtitle->track = idx; subtitle->id = id; lang = lang_for_code( pes->lang_code ); snprintf( subtitle->lang, sizeof( subtitle->lang ), "%s", strlen(lang->native_name) ? lang->native_name : lang->eng_name); snprintf( subtitle->iso639_2, sizeof( subtitle->iso639_2 ), "%s", lang->iso639_2); switch ( pes->codec ) { case WORK_DECPGSSUB: subtitle->source = PGSSUB; subtitle->format = PICTURESUB; subtitle->config.dest = RENDERSUB; break; case WORK_DECVOBSUB: subtitle->source = VOBSUB; subtitle->format = PICTURESUB; subtitle->config.dest = RENDERSUB; break; default: // Unrecognized, don't add to list hb_log("unregonized subtitle!"); free( subtitle ); return; } subtitle->reg_desc = stream->reg_desc; subtitle->stream_type = pes->stream_type; subtitle->substream_type = pes->stream_id_ext; subtitle->codec = pes->codec; // Create a default palette since vob files do not include the // vobsub palette. if ( subtitle->source == VOBSUB ) { subtitle->palette[0] = 0x108080; subtitle->palette[1] = 0x108080; subtitle->palette[2] = 0x108080; subtitle->palette[3] = 0xbff000; subtitle->palette[4] = 0xbff000; subtitle->palette[5] = 0x108080; subtitle->palette[6] = 0x108080; subtitle->palette[7] = 0x108080; subtitle->palette[8] = 0xbff000; subtitle->palette[9] = 0x108080; subtitle->palette[10] = 0x108080; subtitle->palette[11] = 0x108080; subtitle->palette[12] = 0x108080; subtitle->palette[13] = 0xbff000; subtitle->palette[14] = 0x108080; subtitle->palette[15] = 0x108080; } hb_log("stream id 0x%x (type 0x%x substream 0x%x) subtitle 0x%x", pes->stream_id, pes->stream_type, pes->stream_id_ext, subtitle->id); // Search for the sort position if ( sort >= 0 ) { sort = sort < count ? sort : count; for ( i = sort; i < count; i++ ) { tmp = hb_list_item( title->list_subtitle, i ); int sid = tmp->id & 0xffff; int ssid = tmp->id >> 16; if ( pes->stream_id < sid ) break; else if ( pes->stream_id <= sid && pes->stream_id_ext <= ssid ) { break; } } hb_list_insert( title->list_subtitle, i, subtitle ); } else { hb_list_add( title->list_subtitle, subtitle ); } } // Sort specifies the index in the audio list where you would // like sorted items to begin. static void pes_add_audio_to_title( hb_stream_t *stream, int idx, hb_title_t *title, int sort) { hb_pes_stream_t *pes = &stream->pes.list[idx]; // Sort by id when adding to the list // This assures that they are always displayed in the same order int id = get_id( pes ); int i; hb_audio_t *tmp = NULL; int count = hb_list_count( title->list_audio ); // Don't add the same audio twice. Search for audio. for ( i = 0; i < count; i++ ) { tmp = hb_list_item( title->list_audio, i ); if ( id == tmp->id ) return; } hb_audio_t *audio = calloc( sizeof( hb_audio_t ), 1 ); audio->id = id; audio->config.in.reg_desc = stream->reg_desc; audio->config.in.stream_type = pes->stream_type; audio->config.in.substream_type = pes->stream_id_ext; audio->config.in.codec = pes->codec; audio->config.in.codec_param = pes->codec_param; set_audio_description(audio, lang_for_code(pes->lang_code)); hb_log("stream id 0x%x (type 0x%x substream 0x%x) audio 0x%x", pes->stream_id, pes->stream_type, pes->stream_id_ext, audio->id); audio->config.in.track = idx; // Search for the sort position if ( sort >= 0 ) { sort = sort < count ? sort : count; for ( i = sort; i < count; i++ ) { tmp = hb_list_item( title->list_audio, i ); int sid = tmp->id & 0xffff; int ssid = tmp->id >> 16; if ( pes->stream_id < sid ) break; else if ( pes->stream_id <= sid && pes->stream_id_ext <= ssid ) { break; } } hb_list_insert( title->list_audio, i, audio ); } else { hb_list_add( title->list_audio, audio ); } } static void hb_init_subtitle_list(hb_stream_t *stream, hb_title_t *title) { int ii; int map_idx; int largest = -1; // First add all that were found in a map. for ( map_idx = 0; 1; map_idx++ ) { for ( ii = 0; ii < stream->pes.count; ii++ ) { if ( stream->pes.list[ii].stream_kind == S ) { if ( stream->pes.list[ii].map_idx == map_idx ) { pes_add_subtitle_to_title( stream, ii, title, -1 ); } if ( stream->pes.list[ii].map_idx > largest ) largest = stream->pes.list[ii].map_idx; } } if ( map_idx > largest ) break; } int count = hb_list_count( title->list_audio ); // Now add the reset. Sort them by stream id. for ( ii = 0; ii < stream->pes.count; ii++ ) { if ( stream->pes.list[ii].stream_kind == S ) { pes_add_subtitle_to_title( stream, ii, title, count ); } } } static void hb_init_audio_list(hb_stream_t *stream, hb_title_t *title) { int ii; int map_idx; int largest = -1; // First add all that were found in a map. for ( map_idx = 0; 1; map_idx++ ) { for ( ii = 0; ii < stream->pes.count; ii++ ) { if ( stream->pes.list[ii].stream_kind == A ) { if ( stream->pes.list[ii].map_idx == map_idx ) { pes_add_audio_to_title( stream, ii, title, -1 ); } if ( stream->pes.list[ii].map_idx > largest ) largest = stream->pes.list[ii].map_idx; } } if ( map_idx > largest ) break; } int count = hb_list_count( title->list_audio ); // Now add the reset. Sort them by stream id. for ( ii = 0; ii < stream->pes.count; ii++ ) { if ( stream->pes.list[ii].stream_kind == A ) { pes_add_audio_to_title( stream, ii, title, count ); } } } /*********************************************************************** * hb_ts_stream_init *********************************************************************** * **********************************************************************/ static int hb_ts_stream_init(hb_stream_t *stream) { int i; if ( stream->ts.list ) { for (i=0; i < stream->ts.alloc; i++) { stream->ts.list[i].continuity = -1; stream->ts.list[i].pid = -1; stream->ts.list[i].pes_list = -1; } } stream->ts.count = 0; if ( stream->pes.list ) { for (i=0; i < stream->pes.alloc; i++) { stream->pes.list[i].stream_id = -1; stream->pes.list[i].next = -1; } } stream->pes.count = 0; stream->ts.packet = malloc( stream->packetsize ); // Find the audio and video pids in the stream if (hb_ts_stream_find_pids(stream) < 0) { return -1; } // hb_ts_resolve_pid_types reads some data, so the TS buffers // are needed here. for (i = 0; i < stream->ts.count; i++) { // demuxing buffer for TS to PS conversion stream->ts.list[i].buf = hb_buffer_init(stream->packetsize); stream->ts.list[i].extra_buf = hb_buffer_init(stream->packetsize); stream->ts.list[i].buf->size = 0; stream->ts.list[i].extra_buf->size = 0; } hb_ts_resolve_pid_types(stream); if( stream->scan ) { hb_log("Found the following PIDS"); hb_log(" Video PIDS : "); for (i=0; i < stream->ts.count; i++) { if ( ts_stream_kind( stream, i ) == V ) { hb_log( " 0x%x type %s (0x%x)%s", stream->ts.list[i].pid, stream_type_name2(stream, &stream->pes.list[stream->ts.list[i].pes_list]), ts_stream_type( stream, i ), stream->ts.list[i].is_pcr ? " (PCR)" : ""); } } hb_log(" Audio PIDS : "); for (i = 0; i < stream->ts.count; i++) { if ( ts_stream_kind( stream, i ) == A ) { hb_log( " 0x%x type %s (0x%x)%s", stream->ts.list[i].pid, stream_type_name2(stream, &stream->pes.list[stream->ts.list[i].pes_list]), ts_stream_type( stream, i ), stream->ts.list[i].is_pcr ? " (PCR)" : ""); } } hb_log(" Subtitle PIDS : "); for (i = 0; i < stream->ts.count; i++) { if ( ts_stream_kind( stream, i ) == S ) { hb_log( " 0x%x type %s (0x%x)%s", stream->ts.list[i].pid, stream_type_name2(stream, &stream->pes.list[stream->ts.list[i].pes_list]), ts_stream_type( stream, i ), stream->ts.list[i].is_pcr ? " (PCR)" : ""); } } hb_log(" Other PIDS : "); for (i = 0; i < stream->ts.count; i++) { if ( ts_stream_kind( stream, i ) == N || ts_stream_kind( stream, i ) == P ) { hb_log( " 0x%x type %s (0x%x)%s", stream->ts.list[i].pid, stream_type_name2(stream, &stream->pes.list[stream->ts.list[i].pes_list]), ts_stream_type( stream, i ), stream->ts.list[i].is_pcr ? " (PCR)" : ""); } if ( ts_stream_kind( stream, i ) == N ) hb_stream_delete_ts_entry(stream, i); } } else { for (i = 0; i < stream->ts.count; i++) { if ( ts_stream_kind( stream, i ) == N ) hb_stream_delete_ts_entry(stream, i); } } return 0; } static void hb_ps_stream_init(hb_stream_t *stream) { int i; if ( stream->pes.list ) { for (i=0; i < stream->pes.alloc; i++) { stream->pes.list[i].stream_id = -1; stream->pes.list[i].next = -1; } } stream->pes.count = 0; // Find the audio and video pids in the stream hb_ps_stream_find_streams(stream); hb_ps_resolve_stream_types(stream); if( stream->scan ) { hb_log("Found the following streams"); hb_log(" Video Streams : "); for (i=0; i < stream->pes.count; i++) { if ( stream->pes.list[i].stream_kind == V ) { hb_log( " 0x%x-0x%x type %s (0x%x)", stream->pes.list[i].stream_id, stream->pes.list[i].stream_id_ext, stream_type_name2(stream, &stream->pes.list[i]), stream->pes.list[i].stream_type); } } hb_log(" Audio Streams : "); for (i = 0; i < stream->pes.count; i++) { if ( stream->pes.list[i].stream_kind == A ) { hb_log( " 0x%x-0x%x type %s (0x%x)", stream->pes.list[i].stream_id, stream->pes.list[i].stream_id_ext, stream_type_name2(stream, &stream->pes.list[i]), stream->pes.list[i].stream_type ); } } hb_log(" Subtitle Streams : "); for (i = 0; i < stream->pes.count; i++) { if ( stream->pes.list[i].stream_kind == S ) { hb_log( " 0x%x-0x%x type %s (0x%x)", stream->pes.list[i].stream_id, stream->pes.list[i].stream_id_ext, stream_type_name2(stream, &stream->pes.list[i]), stream->pes.list[i].stream_type ); } } hb_log(" Other Streams : "); for (i = 0; i < stream->pes.count; i++) { if ( stream->pes.list[i].stream_kind == N ) { hb_log( " 0x%x-0x%x type %s (0x%x)", stream->pes.list[i].stream_id, stream->pes.list[i].stream_id_ext, stream_type_name2(stream, &stream->pes.list[i]), stream->pes.list[i].stream_type ); hb_stream_delete_ps_entry(stream, i); } } } else { for (i = 0; i < stream->pes.count; i++) { if ( stream->pes.list[i].stream_kind == N ) hb_stream_delete_ps_entry(stream, i); } } } #define MAX_HOLE 208*80 static off_t align_to_next_packet(hb_stream_t *stream) { uint8_t buf[MAX_HOLE]; off_t pos = 0; off_t start = ftello(stream->file_handle); off_t orig; if ( start >= stream->packetsize ) { start -= stream->packetsize; fseeko(stream->file_handle, start, SEEK_SET); } orig = start; while (1) { if (fread(buf, sizeof(buf), 1, stream->file_handle) == 1) { const uint8_t *bp = buf; int i; for ( i = sizeof(buf) - 8 * stream->packetsize; --i >= 0; ++bp ) { if ( have_ts_sync( bp, stream->packetsize, 8 ) ) { break; } } if ( i >= 0 ) { pos = ( bp - buf ) - stream->packetsize + 188; break; } fseeko(stream->file_handle, -8 * stream->packetsize, SEEK_CUR); start = ftello(stream->file_handle); } else { return 0; } } fseeko(stream->file_handle, start+pos, SEEK_SET); return start - orig + pos; } static const unsigned int bitmask[] = { 0x0,0x1,0x3,0x7,0xf,0x1f,0x3f,0x7f,0xff, 0x1ff,0x3ff,0x7ff,0xfff,0x1fff,0x3fff,0x7fff,0xffff, 0x1ffff,0x3ffff,0x7ffff,0xfffff,0x1fffff,0x3fffff,0x7fffff,0xffffff, 0x1ffffff,0x3ffffff,0x7ffffff,0xfffffff,0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff}; static inline void bits_init(bitbuf_t *bb, uint8_t* buf, int bufsize, int clear) { bb->pos = 0; bb->buf = buf; bb->size = bufsize; bb->val = (bb->buf[0] << 24) | (bb->buf[1] << 16) | (bb->buf[2] << 8) | bb->buf[3]; if (clear) memset(bb->buf, 0, bufsize); bb->size = bufsize; } static inline void bits_clone( bitbuf_t *dst, bitbuf_t *src, int bufsize ) { *dst = *src; dst->size = (dst->pos >> 3) + bufsize; } static inline int bits_bytes_left(bitbuf_t *bb) { return bb->size - (bb->pos >> 3); } static inline int bits_eob(bitbuf_t *bb) { return bb->pos >> 3 == bb->size; } static inline unsigned int bits_peek(bitbuf_t *bb, int bits) { unsigned int val; int left = 32 - (bb->pos & 31); if (bits < left) { val = (bb->val >> (left - bits)) & bitmask[bits]; } else { val = (bb->val & bitmask[left]) << (bits - left); int bpos = bb->pos + left; bits -= left; if (bits > 0) { int pos = bpos >> 3; int bval = (bb->buf[pos] << 24) | (bb->buf[pos + 1] << 16) | (bb->buf[pos + 2] << 8) | bb->buf[pos + 3]; val |= (bval >> (32 - bits)) & bitmask[bits]; } } return val; } static inline unsigned int bits_get(bitbuf_t *bb, int bits) { unsigned int val; int left = 32 - (bb->pos & 31); if (bits < left) { val = (bb->val >> (left - bits)) & bitmask[bits]; bb->pos += bits; } else { val = (bb->val & bitmask[left]) << (bits - left); bb->pos += left; bits -= left; int pos = bb->pos >> 3; bb->val = (bb->buf[pos] << 24) | (bb->buf[pos + 1] << 16) | (bb->buf[pos + 2] << 8) | bb->buf[pos + 3]; if (bits > 0) { val |= (bb->val >> (32 - bits)) & bitmask[bits]; bb->pos += bits; } } return val; } static inline int bits_read_ue(bitbuf_t *bb ) { int ii = 0; while( bits_get( bb, 1 ) == 0 && !bits_eob( bb ) && ii < 32 ) { ii++; } return( ( 1 << ii) - 1 + bits_get( bb, ii ) ); } static inline int bits_skip(bitbuf_t *bb, int bits) { if (bits <= 0) return 0; while (bits > 32) { bits_get(bb, 32); bits -= 32; } bits_get(bb, bits); return 0; } // extract what useful information we can from the elementary stream // descriptor list at 'dp' and add it to the stream at 'esindx'. // Descriptors with info we don't currently use are ignored. // The descriptor list & descriptor item formats are defined in // ISO 13818-1 (2000E) section 2.6 (pg. 62). static void decode_element_descriptors( hb_stream_t *stream, int pes_idx, bitbuf_t *bb) { int ii; while( bits_bytes_left( bb ) > 2 ) { uint8_t tag = bits_get(bb, 8); uint8_t len = bits_get(bb, 8); switch ( tag ) { case 5: // Registration descriptor stream->pes.list[pes_idx].format_id = bits_get(bb, 32); bits_skip(bb, 8 * (len - 4)); break; case 10: // ISO_639_language descriptor { char code[3]; for (ii = 0; ii < 3; ii++) { code[ii] = bits_get(bb, 8); } stream->pes.list[pes_idx].lang_code = lang_to_code(lang_for_code2(code)); bits_skip(bb, 8 * (len - 3)); } break; case 0x56: // DVB Teletext descriptor { // We don't currently process teletext from // TS or PS streams. Set stream 'kind' to N stream->pes.list[pes_idx].stream_type = 0x00; stream->pes.list[pes_idx].stream_kind = N; strncpy(stream->pes.list[pes_idx].codec_name, "DVB Teletext", 80); bits_skip(bb, 8 * len); } break; case 0x59: // DVB Subtitleing descriptor { // We don't currently process subtitles from // TS or PS streams. Set stream 'kind' to N stream->pes.list[pes_idx].stream_type = 0x00; stream->pes.list[pes_idx].stream_kind = N; strncpy(stream->pes.list[pes_idx].codec_name, "DVB Subtitling", 80); bits_skip(bb, 8 * len); } break; case 0x6a: // DVB AC-3 descriptor { stream->pes.list[pes_idx].stream_type = 0x81; update_pes_kind( stream, pes_idx ); bits_skip(bb, 8 * len); } break; case 0x7a: // DVB EAC-3 descriptor { stream->pes.list[pes_idx].stream_type = 0x87; update_pes_kind( stream, pes_idx ); bits_skip(bb, 8 * len); } break; default: bits_skip(bb, 8 * len); break; } } } int decode_program_map(hb_stream_t* stream) { bitbuf_t bb; bits_init(&bb, stream->pmt_info.tablebuf, stream->pmt_info.tablepos, 0); bits_get(&bb, 8); // table_id bits_get(&bb, 4); unsigned int section_length = bits_get(&bb, 12); bits_get(&bb, 16); // program number bits_get(&bb, 2); bits_get(&bb, 5); // version_number bits_get(&bb, 1); bits_get(&bb, 8); // section_number bits_get(&bb, 8); // last_section_number bits_get(&bb, 3); stream->pmt_info.PCR_PID = bits_get(&bb, 13); bits_get(&bb, 4); int program_info_length = bits_get(&bb, 12); int i; for (i = 0; i < program_info_length - 2; ) { uint8_t tag, len; tag = bits_get(&bb, 8); len = bits_get(&bb, 8); i += 2; if ( i + len > program_info_length ) { break; } if (tag == 0x05 && len >= 4) { // registration descriptor stream->reg_desc = bits_get(&bb, 32); i += 4; len -= 4; } int j; for ( j = 0; j < len; j++ ) { bits_get(&bb, 8); } i += len; } for ( ; i < program_info_length; i++ ) { bits_get(&bb, 8); } int cur_pos = 9 /* data after the section length field*/ + program_info_length; int done_reading_stream_types = 0; int ii = 0; while (!done_reading_stream_types) { unsigned char stream_type = bits_get(&bb, 8); bits_get(&bb, 3); unsigned int elementary_PID = bits_get(&bb, 13); bits_get(&bb, 4); unsigned int info_len = bits_get(&bb, 12); // Defined audio stream types are 0x81 for AC-3/A52 audio // and 0x03 for mpeg audio. But content producers seem to // use other values (0x04 and 0x06 have both been observed) // so at this point we say everything that isn't a video // pid is audio then at the end of hb_stream_title_scan // we'll figure out which are really audio by looking at // the PES headers. int pes_idx; update_ts_streams( stream, elementary_PID, 0, stream_type, -1, &pes_idx ); if ( pes_idx >= 0 ) stream->pes.list[pes_idx].map_idx = ii; if (info_len > 0) { bitbuf_t bb_desc; bits_clone( &bb_desc, &bb, info_len ); if ( pes_idx >= 0 ) decode_element_descriptors( stream, pes_idx, &bb_desc ); bits_skip(&bb, 8 * info_len); } cur_pos += 5 /* stream header */ + info_len; if (cur_pos >= section_length - 4 /* stop before the CRC */) done_reading_stream_types = 1; ii++; } return 1; } static int build_program_map(const uint8_t *buf, hb_stream_t *stream) { // Get adaption header info int adapt_len = 0; int adaption = (buf[3] & 0x30) >> 4; if (adaption == 0) return 0; else if (adaption == 0x2) adapt_len = 184; else if (adaption == 0x3) adapt_len = buf[4] + 1; if (adapt_len > 184) return 0; // Get payload start indicator int start; start = (buf[1] & 0x40) != 0; // Get pointer length - only valid in packets with a start flag int pointer_len = 0; if (start) { pointer_len = buf[4 + adapt_len] + 1; stream->pmt_info.tablepos = 0; } // Get Continuity Counter int continuity_counter = buf[3] & 0x0f; if (!start && (stream->pmt_info.current_continuity_counter + 1 != continuity_counter)) { hb_log("build_program_map - Continuity Counter %d out of sequence - expected %d", continuity_counter, stream->pmt_info.current_continuity_counter+1); return 0; } stream->pmt_info.current_continuity_counter = continuity_counter; stream->pmt_info.reading |= start; // Add the payload for this packet to the current buffer int amount_to_copy = 184 - adapt_len - pointer_len; if (stream->pmt_info.reading && (amount_to_copy > 0)) { stream->pmt_info.tablebuf = realloc(stream->pmt_info.tablebuf, stream->pmt_info.tablepos + amount_to_copy); memcpy(stream->pmt_info.tablebuf + stream->pmt_info.tablepos, buf + 4 + adapt_len + pointer_len, amount_to_copy); stream->pmt_info.tablepos += amount_to_copy; } if (stream->pmt_info.tablepos > 3) { // We have enough to check the section length int length; length = ((stream->pmt_info.tablebuf[1] << 8) + stream->pmt_info.tablebuf[2]) & 0xFFF; if (stream->pmt_info.tablepos > length + 1) { // We just finished a bunch of packets - parse the program map details int decode_ok = 0; if (stream->pmt_info.tablebuf[0] == 0x02) decode_ok = decode_program_map(stream); free(stream->pmt_info.tablebuf); stream->pmt_info.tablebuf = NULL; stream->pmt_info.tablepos = 0; stream->pmt_info.reading = 0; if (decode_ok) return decode_ok; } } return 0; } static int decode_PAT(const uint8_t *buf, hb_stream_t *stream) { unsigned char tablebuf[1024]; unsigned int tablepos = 0; int reading = 0; // Get adaption header info int adapt_len = 0; int adaption = (buf[3] & 0x30) >> 4; if (adaption == 0) return 0; else if (adaption == 0x2) adapt_len = 184; else if (adaption == 0x3) adapt_len = buf[4] + 1; if (adapt_len > 184) return 0; // Get pointer length int pointer_len = buf[4 + adapt_len] + 1; // Get payload start indicator int start; start = (buf[1] & 0x40) != 0; if (start) reading = 1; // Add the payload for this packet to the current buffer if (reading && (184 - adapt_len) > 0) { if (tablepos + 184 - adapt_len - pointer_len > 1024) { hb_log("decode_PAT - Bad program section length (> 1024)"); return 0; } memcpy(tablebuf + tablepos, buf + 4 + adapt_len + pointer_len, 184 - adapt_len - pointer_len); tablepos += 184 - adapt_len - pointer_len; } if (start && reading) { memcpy(tablebuf + tablepos, buf + 4 + adapt_len + 1, pointer_len - 1); unsigned int pos = 0; //while (pos < tablepos) { bitbuf_t bb; bits_init(&bb, tablebuf + pos, tablepos - pos, 0); unsigned char section_id = bits_get(&bb, 8); bits_get(&bb, 4); unsigned int section_len = bits_get(&bb, 12); bits_get(&bb, 16); // transport_id bits_get(&bb, 2); bits_get(&bb, 5); // version_num bits_get(&bb, 1); // current_next bits_get(&bb, 8); // section_num bits_get(&bb, 8); // last_section switch (section_id) { case 0x00: { // Program Association Section section_len -= 5; // Already read transport stream ID, version num, section num, and last section num section_len -= 4; // Ignore the CRC int curr_pos = 0; stream->ts_number_pat_entries = 0; while ((curr_pos < section_len) && (stream->ts_number_pat_entries < kMaxNumberPMTStreams)) { unsigned int pkt_program_num = bits_get(&bb, 16); stream->pat_info[stream->ts_number_pat_entries].program_number = pkt_program_num; bits_get(&bb, 3); // Reserved if (pkt_program_num == 0) { bits_get(&bb, 13); // pkt_network_id } else { unsigned int pkt_program_map_PID = bits_get(&bb, 13); stream->pat_info[stream->ts_number_pat_entries].program_map_PID = pkt_program_map_PID; } curr_pos += 4; stream->ts_number_pat_entries++; } } break; case 0xC7: { break; } case 0xC8: { break; } } pos += 3 + section_len; } tablepos = 0; } return 1; } // convert a PES PTS or DTS to an int64 static int64_t parse_pes_timestamp( bitbuf_t *bb ) { int64_t ts; ts = ( (uint64_t) bits_get(bb, 3) << 30 ) + bits_skip(bb, 1) + ( bits_get(bb, 15) << 15 ) + bits_skip(bb, 1) + bits_get(bb, 15); bits_skip(bb, 1); return ts; } static int parse_pes_header( hb_stream_t *stream, bitbuf_t *bb, hb_pes_info_t *pes_info ) { if ( bits_bytes_left(bb) < 6 ) { return 0; } bits_skip(bb, 8 * 4); pes_info->packet_len = bits_get(bb, 16); /* * This would normally be an error. But the decoders can generally * recover well from missing data. So let the packet pass. if ( bits_bytes_left(bb) < pes_info->packet_len ) { return 0; } */ int mark = bits_peek(bb, 2); if ( mark == 0x02 ) { // mpeg2 pes if ( bits_bytes_left(bb) < 3 ) { return 0; } /* bits_skip(bb, 2); bits_get(bb, 2); // scrambling bits_get(bb, 1); // priority bits_get(bb, 1); // alignment bits_get(bb, 1); // copyright bits_get(bb, 1); // original */ bits_get(bb, 8); // skip all of the above int has_pts = bits_get(bb, 2); int has_escr = bits_get(bb, 1); int has_esrate = bits_get(bb, 1); int has_dsm = bits_get(bb, 1); int has_copy_info = bits_get(bb, 1); int has_crc = bits_get(bb, 1); int has_ext = bits_get(bb, 1); int hdr_len = pes_info->header_len = bits_get(bb, 8); pes_info->header_len += bb->pos >> 3; bitbuf_t bb_hdr; bits_clone(&bb_hdr, bb, hdr_len); if ( bits_bytes_left(&bb_hdr) < hdr_len ) { return 0; } int expect = (!!has_pts) * 5 + (has_pts & 0x01) * 5 + has_escr * 6 + has_esrate * 3 + has_dsm + has_copy_info + has_crc * 2 + has_ext; if ( bits_bytes_left(&bb_hdr) < expect ) { return 0; } if( has_pts ) { if ( bits_bytes_left(&bb_hdr) < 5 ) { return 0; } bits_skip(&bb_hdr, 4); pes_info->pts = parse_pes_timestamp( &bb_hdr ); if ( has_pts & 1 ) { if ( bits_bytes_left(&bb_hdr) < 5 ) { return 0; } bits_skip(&bb_hdr, 4); pes_info->dts = parse_pes_timestamp( &bb_hdr ); } else { pes_info->dts = pes_info->pts; } } // A user encountered a stream that has garbage DTS timestamps. // DTS should never be > PTS. Such broken timestamps leads to // HandBrake computing negative buffer start times. if (pes_info->dts > pes_info->pts) { pes_info->dts = pes_info->pts; } if ( has_escr ) bits_skip(&bb_hdr, 8 * 6); if ( has_esrate ) bits_skip(&bb_hdr, 8 * 3); if ( has_dsm ) bits_skip(&bb_hdr, 8); if ( has_copy_info ) bits_skip(&bb_hdr, 8); if ( has_crc ) bits_skip(&bb_hdr, 8 * 2); if ( has_ext ) { int has_private = bits_get(&bb_hdr, 1); int has_pack = bits_get(&bb_hdr, 1); int has_counter = bits_get(&bb_hdr, 1); int has_pstd = bits_get(&bb_hdr, 1); bits_skip(&bb_hdr, 3); // reserved bits int has_ext2 = bits_get(&bb_hdr, 1); expect = (has_private) * 16 + has_pack + has_counter * 2 + has_pstd * 2 + has_ext2 * 2; if ( bits_bytes_left(&bb_hdr) < expect ) { return 0; } if ( has_private ) { bits_skip(&bb_hdr, 8 * 16); expect -= 2; } if ( has_pack ) { int len = bits_get(&bb_hdr, 8); expect -= 1; if ( bits_bytes_left(&bb_hdr) < len + expect ) { return 0; } bits_skip(&bb_hdr, 8 * len); } if ( has_counter ) bits_skip(&bb_hdr, 8 * 2); if ( has_pstd ) bits_skip(&bb_hdr, 8 * 2); if ( has_ext2 ) { bits_skip(&bb_hdr, 1); // marker bits_get(&bb_hdr, 7); // extension length pes_info->has_stream_id_ext = !bits_get(&bb_hdr, 1); if ( pes_info->has_stream_id_ext ) pes_info->stream_id_ext = bits_get(&bb_hdr, 7); } } // eat header stuffing bits_skip(bb, 8 * hdr_len); } else { // mpeg1 pes // Skip stuffing while ( bits_peek(bb, 1) && bits_bytes_left(bb) ) bits_get(bb, 8); if ( !bits_bytes_left(bb) ) return 0; // Skip std buffer info int mark = bits_get(bb, 2); if ( mark == 0x01 ) { if ( bits_bytes_left(bb) < 2 ) return 0; bits_skip(bb, 8 * 2); } int has_pts = bits_get(bb, 2); if( has_pts == 0x02 ) { pes_info->pts = parse_pes_timestamp( bb ); pes_info->dts = pes_info->pts; } else if( has_pts == 0x03 ) { pes_info->pts = parse_pes_timestamp( bb ); bits_skip(bb, 4); pes_info->dts = parse_pes_timestamp( bb ); } else { bits_skip(bb, 8); // 0x0f flag } if ( bits_bytes_left(bb) < 0 ) return 0; pes_info->header_len = bb->pos >> 3; } if ( pes_info->stream_id == 0xbd && stream->hb_stream_type == program ) { if ( bits_bytes_left(bb) < 4 ) { return 0; } int ssid = bits_peek(bb, 8); if( ( ssid >= 0xa0 && ssid <= 0xaf ) || ( ssid >= 0x20 && ssid <= 0x2f ) ) { // DVD LPCM or DVD SPU (subtitles) pes_info->bd_substream_id = bits_get(bb, 8); pes_info->header_len += 1; } else if ( ssid >= 0xb0 && ssid <= 0xbf ) { // HD-DVD TrueHD has a 4 byte header pes_info->bd_substream_id = bits_get(bb, 8); bits_skip(bb, 8 * 4); pes_info->header_len += 5; } else if( ( ssid >= 0x80 && ssid <= 0x9f ) || ( ssid >= 0xc0 && ssid <= 0xcf ) ) { // AC3, E-AC3, DTS, and DTS-HD has 3 byte header pes_info->bd_substream_id = bits_get(bb, 8); bits_skip(bb, 8 * 3); pes_info->header_len += 4; } } return 1; } static int parse_pack_header( hb_stream_t *stream, bitbuf_t *bb, hb_pes_info_t *pes_info ) { if ( bits_bytes_left(bb) < 12) { return 0; } bits_skip(bb, 8 * 4); int mark = bits_get(bb, 2); if ( mark == 0x00 ) { // mpeg1 pack bits_skip(bb, 2); // marker } pes_info->scr = parse_pes_timestamp( bb ); if ( mark == 0x00 ) { bits_skip(bb, 24); pes_info->header_len = (bb->pos >> 3); } else { bits_skip(bb, 39); int stuffing = bits_get(bb, 3); pes_info->header_len = stuffing; pes_info->header_len += (bb->pos >> 3); } return 1; } // Returns the length of the header static int hb_parse_ps( hb_stream_t *stream, uint8_t *buf, int len, hb_pes_info_t *pes_info ) { memset( pes_info, 0, sizeof( hb_pes_info_t ) ); pes_info->pts = AV_NOPTS_VALUE; pes_info->dts = AV_NOPTS_VALUE; bitbuf_t bb, cc; bits_init(&bb, buf, len, 0); bits_clone(&cc, &bb, len); if ( bits_bytes_left(&bb) < 4 ) return 0; // Validate start code if ( bits_get(&bb, 8 * 3) != 0x000001 ) { return 0; } pes_info->stream_id = bits_get(&bb, 8); if ( pes_info->stream_id == 0xb9 ) { // Program stream end code return 1; } else if ( pes_info->stream_id == 0xba ) { return parse_pack_header( stream, &cc, pes_info ); } else if ( pes_info->stream_id >= 0xbd && pes_info->stream_id != 0xbe && pes_info->stream_id != 0xbf && pes_info->stream_id != 0xf0 && pes_info->stream_id != 0xf1 && pes_info->stream_id != 0xf2 && pes_info->stream_id != 0xf8 && pes_info->stream_id != 0xff ) { return parse_pes_header( stream, &cc, pes_info ); } else { if ( bits_bytes_left(&bb) < 2 ) { return 0; } pes_info->packet_len = bits_get(&bb, 16); pes_info->header_len = bb.pos >> 3; return 1; } } static int hb_ps_read_packet( hb_stream_t * stream, hb_buffer_t *b ) { // Appends to buffer if size != 0 int start_code = -1; int pos = b->size; int stream_id = -1; int c; #define cp (b->data) flockfile( stream->file_handle ); while ( ( c = getc_unlocked( stream->file_handle ) ) != EOF ) { start_code = ( start_code << 8 ) | c; if ( ( start_code >> 8 )== 0x000001 ) // we found the start of the next start break; } if ( c == EOF ) goto done; if ( pos + 4 > b->alloc ) { // need to expand the buffer hb_buffer_realloc( b, b->alloc * 2 ); } cp[pos++] = ( start_code >> 24 ) & 0xff; cp[pos++] = ( start_code >> 16 ) & 0xff; cp[pos++] = ( start_code >> 8 ) & 0xff; cp[pos++] = ( start_code ) & 0xff; stream_id = start_code & 0xff; if ( stream_id == 0xba ) { int start = pos - 4; // Read pack header if ( pos + 21 >= b->alloc ) { // need to expand the buffer hb_buffer_realloc( b, b->alloc * 2 ); } // There are at least 8 bytes. More if this is mpeg2 pack. fread( cp+pos, 1, 8, stream->file_handle ); int mark = cp[pos] >> 4; pos += 8; if ( mark != 0x02 ) { // mpeg-2 pack, fread( cp+pos, 1, 2, stream->file_handle ); pos += 2; int len = cp[start+13] & 0x7; fread( cp+pos, 1, len, stream->file_handle ); pos += len; } } // Non-video streams can emulate start codes, so we need // to inspect PES packets and skip over their data // sections to avoid mis-detection of the next pack or pes start code else if ( stream_id >= 0xbb ) { int len = 0; c = getc_unlocked( stream->file_handle ); if ( c == EOF ) goto done; len = c << 8; c = getc_unlocked( stream->file_handle ); if ( c == EOF ) goto done; len |= c; if ( pos + len + 2 > b->alloc ) { if ( b->alloc * 2 > pos + len + 2 ) hb_buffer_realloc( b, b->alloc * 2 ); else hb_buffer_realloc( b, b->alloc * 2 + len + 2 ); } cp[pos++] = len >> 8; cp[pos++] = len & 0xff; if ( len ) { // Length is non-zero, read the packet all at once len = fread( cp+pos, 1, len, stream->file_handle ); pos += len; } else { // Length is zero, read bytes till we find a start code. // Only video PES packets are allowed to have zero length. start_code = -1; while ( ( c = getc_unlocked( stream->file_handle ) ) != EOF ) { start_code = ( start_code << 8 ) | c; if ( pos >= b->alloc ) { // need to expand the buffer hb_buffer_realloc( b, b->alloc * 2 ); } cp[pos++] = c; if ( ( start_code >> 8 ) == 0x000001 && ( start_code & 0xff ) >= 0xb9 ) { // we found the start of the next start break; } } if ( c == EOF ) goto done; pos -= 4; fseeko( stream->file_handle, -4, SEEK_CUR ); } } else { // Unknown, find next start code start_code = -1; while ( ( c = getc_unlocked( stream->file_handle ) ) != EOF ) { start_code = ( start_code << 8 ) | c; if ( pos >= b->alloc ) { // need to expand the buffer hb_buffer_realloc( b, b->alloc * 2 ); } cp[pos++] = c; if ( ( start_code >> 8 ) == 0x000001 && ( start_code & 0xff ) >= 0xb9 ) // we found the start of the next start break; } if ( c == EOF ) goto done; pos -= 4; fseeko( stream->file_handle, -4, SEEK_CUR ); } done: // Parse packet for information we might need funlockfile( stream->file_handle ); int len = pos - b->size; b->size = pos; #undef cp return len; } static hb_buffer_t * hb_ps_stream_decode( hb_stream_t *stream ) { hb_pes_info_t pes_info; hb_buffer_t *buf = hb_buffer_init(HB_DVD_READ_BUFFER_SIZE); while (1) { buf->size = 0; int len = hb_ps_read_packet( stream, buf ); if ( len == 0 ) { // End of file hb_buffer_close( &buf ); return buf; } if ( !hb_parse_ps( stream, buf->data, buf->size, &pes_info ) ) { ++stream->errors; continue; } // pack header if ( pes_info.stream_id == 0xba ) { stream->pes.found_scr = 1; stream->ts_flags |= TS_HAS_PCR; stream->pes.scr = pes_info.scr; continue; } // If we don't have a SCR yet but the stream has SCRs just loop // so we don't process anything until we have a clock reference. if ( !stream->pes.found_scr && ( stream->ts_flags & TS_HAS_PCR ) ) { continue; } // system header if ( pes_info.stream_id == 0xbb ) continue; int idx; if ( pes_info.stream_id == 0xbd ) { idx = index_of_ps_stream( stream, pes_info.stream_id, pes_info.bd_substream_id ); } else { idx = index_of_ps_stream( stream, pes_info.stream_id, pes_info.stream_id_ext ); } // Is this a stream carrying data that we care about? if ( idx < 0 ) continue; switch (stream->pes.list[idx].stream_kind) { case A: buf->s.type = AUDIO_BUF; break; case V: buf->s.type = VIDEO_BUF; break; default: buf->s.type = OTHER_BUF; break; } if ( stream->need_keyframe ) { // we're looking for the first video frame because we're // doing random access during 'scan' if ( buf->s.type != VIDEO_BUF || !isIframe( stream, buf->data, buf->size ) ) { // not the video stream or didn't find an I frame // but we'll only wait 600 video frames for an I frame. if ( buf->s.type != VIDEO_BUF || ++stream->need_keyframe < 600 ) { continue; } } stream->need_keyframe = 0; } if ( buf->s.type == VIDEO_BUF ) ++stream->frames; buf->s.id = get_id( &stream->pes.list[idx] ); buf->s.pcr = stream->pes.scr; buf->s.start = pes_info.pts; buf->s.renderOffset = pes_info.dts; memmove( buf->data, buf->data + pes_info.header_len, buf->size - pes_info.header_len ); buf->size -= pes_info.header_len; if ( buf->size == 0 ) continue; stream->pes.scr = AV_NOPTS_VALUE; return buf; } } static int update_ps_streams( hb_stream_t * stream, int stream_id, int stream_id_ext, int stream_type, int in_kind ) { int ii; int same_stream = -1; kind_t kind = in_kind == -1 ? st2codec[stream_type].kind : in_kind; for ( ii = 0; ii < stream->pes.count; ii++ ) { if ( stream->pes.list[ii].stream_id == stream_id ) same_stream = ii; if ( stream->pes.list[ii].stream_id == stream_id && stream->pes.list[ii].stream_id_ext == 0 && stream->pes.list[ii].stream_kind == U ) { // This is an unknown stream type that hasn't been // given a stream_id_ext. So match only to stream_id // // is the stream_id_ext being updated? if ( stream_id_ext != 0 ) break; // If stream is already in the list and the new 'kind' is // PCR, Unknown, or same as before, just return the index // to the entry found. if ( kind == P || kind == U || kind == stream->pes.list[ii].stream_kind ) return ii; // Update stream_type and kind break; } if ( stream_id == stream->pes.list[ii].stream_id && stream_id_ext == stream->pes.list[ii].stream_id_ext ) { // If stream is already in the list and the new 'kind' is // PCR and the old 'kind' is unknown, set the new 'kind' if ( kind == P && stream->pes.list[ii].stream_kind == U ) break; // If stream is already in the list and the new 'kind' is // PCR, Unknown, or same as before, just return the index // to the entry found. if ( kind == P || kind == U || kind == stream->pes.list[ii].stream_kind ) return ii; // Replace unknown 'kind' with known 'kind' break; } // Resolve multiple videos if ( kind == V && stream->pes.list[ii].stream_kind == V ) { if ( stream_id <= stream->pes.list[ii].stream_id && stream_id_ext <= stream->pes.list[ii].stream_id_ext ) { // Assume primary video stream has the smallest stream id // and only use the primary. move the current item // to the end of the list. we want to keep it for // debug and informational purposes. int jj = new_pes( stream ); memcpy( &stream->pes.list[jj], &stream->pes.list[ii], sizeof( hb_pes_stream_t ) ); break; } } } if ( ii == stream->pes.count ) { ii = new_pes( stream ); if ( same_stream >= 0 ) { memcpy( &stream->pes.list[ii], &stream->pes.list[same_stream], sizeof( hb_pes_stream_t ) ); } else { stream->pes.list[ii].map_idx = -1; } } stream->pes.list[ii].stream_id = stream_id; stream->pes.list[ii].stream_id_ext = stream_id_ext; stream->pes.list[ii].stream_type = stream_type; stream->pes.list[ii].stream_kind = kind; return ii; } static void update_pes_kind( hb_stream_t * stream, int idx ) { kind_t kind = st2codec[stream->pes.list[idx].stream_type].kind; if ( kind != U && kind != N ) { stream->pes.list[idx].stream_kind = kind; } } static void ts_pes_list_add( hb_stream_t *stream, int ts_idx, int pes_idx ) { int ii = stream->ts.list[ts_idx].pes_list; if ( ii == -1 ) { stream->ts.list[ts_idx].pes_list = pes_idx; return; } int idx; while ( ii != -1 ) { if ( ii == pes_idx ) // Already in list return; idx = ii; ii = stream->pes.list[ii].next; } stream->pes.list[idx].next = pes_idx; } static int update_ts_streams( hb_stream_t * stream, int pid, int stream_id_ext, int stream_type, int in_kind, int *out_pes_idx ) { int ii; int pes_idx = update_ps_streams( stream, pid, stream_id_ext, stream_type, in_kind ); if ( out_pes_idx ) *out_pes_idx = pes_idx; if ( pes_idx < 0 ) return -1; kind_t kind = stream->pes.list[pes_idx].stream_kind; for ( ii = 0; ii < stream->ts.count; ii++ ) { if ( pid == stream->ts.list[ii].pid ) { break; } // Resolve multiple videos if ( kind == V && ts_stream_kind( stream, ii ) == V && pes_idx < stream->ts.list[ii].pes_list ) { // We have a new candidate for the primary video. Move // the current video to the end of the list. And put the // new video in this slot int jj = new_pid( stream ); memcpy( &stream->ts.list[jj], &stream->ts.list[ii], sizeof( hb_ts_stream_t ) ); break; } } if ( ii == stream->ts.count ) ii = new_pid( stream ); stream->ts.list[ii].pid = pid; ts_pes_list_add( stream, ii, pes_idx ); if ( in_kind == P ) stream->ts.list[ii].is_pcr = 1; return ii; } static int decode_ps_map( hb_stream_t * stream, uint8_t *buf, int len ) { int retval = 1; bitbuf_t bb; bits_init(&bb, buf, len, 0); if ( bits_bytes_left(&bb) < 10 ) return 0; // Skip stuff not needed bits_skip(&bb, 8 * 8); int info_len = bits_get(&bb, 16); if ( bits_bytes_left(&bb) < info_len ) return 0; if ( info_len ) { bitbuf_t cc; bits_clone( &cc, &bb, info_len ); while ( bits_bytes_left(&cc) >= 2 ) { uint8_t tag, len; tag = bits_get(&cc, 8); len = bits_get(&cc, 8); if ( bits_bytes_left(&cc) < len ) return 0; if (tag == 0x05 && len >= 4) { // registration descriptor stream->reg_desc = bits_get(&cc, 32); bits_skip(&cc, 8 * (len - 4)); } else { bits_skip(&cc, 8 * len); } } bits_skip(&bb, 8 * info_len); } int map_len = bits_get(&bb, 16); if ( bits_bytes_left(&bb) < map_len ) return 0; // Process the map int ii = 0; while ( bits_bytes_left(&bb) >= 8 ) { int pes_idx; int stream_type = bits_get(&bb, 8); int stream_id = bits_get(&bb, 8); info_len = bits_get(&bb, 16); if ( info_len > bits_bytes_left(&bb) ) return 0; int substream_id = 0; switch ( stream_type ) { case 0x81: // ac3 case 0x82: // dts case 0x83: // lpcm case 0x87: // eac3 // If the stream_id isn't one of the standard mpeg // stream ids, assume it is an private stream 1 substream id. // This is how most PS streams specify this type of audio. // // TiVo sets the stream id to 0xbd and does not // give a substream id. This limits them to one audio // stream and differs from how everyone else specifies // this type of audio. if ( stream_id < 0xb9 ) { substream_id = stream_id; stream_id = 0xbd; } break; default: break; } pes_idx = update_ps_streams( stream, stream_id, substream_id, stream_type, -1 ); if ( pes_idx >= 0 ) stream->pes.list[pes_idx].map_idx = ii; if ( info_len > 0 ) { bitbuf_t bb_desc; bits_clone( &bb_desc, &bb, info_len ); if ( pes_idx >= 0 ) decode_element_descriptors( stream, pes_idx, &bb_desc ); bits_skip(&bb, 8 * info_len); } ii++; } // skip CRC 32 return retval; } static void hb_ps_stream_find_streams(hb_stream_t *stream) { int ii, jj; hb_buffer_t *buf = hb_buffer_init(HB_DVD_READ_BUFFER_SIZE); fseeko( stream->file_handle, 0, SEEK_SET ); // Scan beginning of file, then if no program stream map is found // seek to 20% and scan again since there's occasionally no // audio at the beginning (particularly for vobs). for ( ii = 0; ii < 2; ii++ ) { for ( jj = 0; jj < MAX_PS_PROBE_SIZE; jj += buf->size ) { int stream_type; int len; hb_pes_info_t pes_info; buf->size = 0; len = hb_ps_read_packet( stream, buf ); if ( len == 0 ) { // Must have reached EOF break; } if ( !hb_parse_ps( stream, buf->data, buf->size, &pes_info ) ) { hb_deep_log( 2, "hb_ps_stream_find_streams: Error parsing PS packet"); continue; } if ( pes_info.stream_id == 0xba ) { stream->ts_flags |= TS_HAS_PCR; } else if ( pes_info.stream_id == 0xbc ) { // program stream map // Note that if there is a program map, any // extrapolation that is made below based on // stream id may be overridden by entry in the map. if ( decode_ps_map( stream, buf->data, buf->size ) ) { hb_log("Found program stream map"); // Normally, we could quit here since the program // stream map *should* map all streams. But once // again Tivo breaks things by not always creating // complete maps. So continue processing... } else { hb_error("Error parsing program stream map"); } } else if ( ( pes_info.stream_id & 0xe0 ) == 0xc0 ) { // MPeg audio (c0 - df) stream_type = 0x04; update_ps_streams( stream, pes_info.stream_id, pes_info.stream_id_ext, stream_type, -1 ); } else if ( pes_info.stream_id == 0xbd ) { int ssid = pes_info.bd_substream_id; // Add a potentail audio stream // Check dvd substream id if ( ssid >= 0x20 && ssid <= 0x37 ) { int idx = update_ps_streams( stream, pes_info.stream_id, pes_info.bd_substream_id, 0, -1 ); stream->pes.list[idx].stream_kind = S; stream->pes.list[idx].codec = WORK_DECVOBSUB; strncpy(stream->pes.list[idx].codec_name, "DVD Subtitle", 80); continue; } if ( ssid >= 0x80 && ssid <= 0x87 ) { stream_type = 0x81; // ac3 } else if ( ( ssid >= 0x88 && ssid <= 0x8f ) || ( ssid >= 0x98 && ssid <= 0x9f ) ) { // Could be either dts or dts-hd // will have to probe to resolve int idx = update_ps_streams( stream, pes_info.stream_id, pes_info.bd_substream_id, 0, U ); stream->pes.list[idx].codec = HB_ACODEC_DCA_HD; stream->pes.list[idx].codec_param = AV_CODEC_ID_DTS; continue; } else if ( ssid >= 0xa0 && ssid <= 0xaf ) { stream_type = 0x83; // lpcm // This is flagged as an unknown stream type in // st2codec because it can be either LPCM or // BD TrueHD. In this case it is LPCM. update_ps_streams( stream, pes_info.stream_id, pes_info.bd_substream_id, stream_type, A ); continue; } else if ( ssid >= 0xb0 && ssid <= 0xbf ) { // HD-DVD TrueHD int idx = update_ps_streams( stream, pes_info.stream_id, pes_info.bd_substream_id, 0, A ); stream->pes.list[idx].codec = HB_ACODEC_FFMPEG; stream->pes.list[idx].codec_param = AV_CODEC_ID_TRUEHD; continue; } else if ( ssid >= 0xc0 && ssid <= 0xcf ) { // HD-DVD uses this for both ac3 and eac3. // Check ac3 bitstream_id to distinguish between them. bitbuf_t bb; bits_init(&bb, buf->data + pes_info.header_len, buf->size - pes_info.header_len, 0); int sync = bits_get(&bb, 16); if ( sync == 0x0b77 ) { bits_skip(&bb, 24); int bsid = bits_get(&bb, 5); if ( bsid <= 10 ) { // ac3 stream_type = 0x81; // ac3 } else { // eac3 stream_type = 0x87; // eac3 } } else { // Doesn't look like an ac3 stream. Probe it. stream_type = 0x00; } } else { // Unknown. Probe it. stream_type = 0x00; } update_ps_streams( stream, pes_info.stream_id, pes_info.bd_substream_id, stream_type, -1 ); } else if ( ( pes_info.stream_id & 0xf0 ) == 0xe0 ) { // Normally this is MPEG video, but MPEG-1 PS streams // (which do not have a program stream map) may use // this for other types of video. // // Also, the hddvd tards decided to use 0xe2 and 0xe3 for // h.264 video :( and the twits decided not to put a // program stream map in the stream :'( // // So set this to an unknown stream type and probe. stream_type = 0x00; update_ps_streams( stream, pes_info.stream_id, pes_info.stream_id_ext, stream_type, -1 ); } else if ( pes_info.stream_id == 0xfd ) { if ( pes_info.stream_id_ext == 0x55 || pes_info.stream_id_ext == 0x56 ) { // hddvd uses this for vc-1. stream_type = 0xea; } else { // mark as unknown and probe. stream_type = 0x00; } update_ps_streams( stream, pes_info.stream_id, pes_info.stream_id_ext, stream_type, -1 ); } } hb_stream_seek( stream, 0.2 ); } hb_buffer_close( &buf ); } static int probe_dts_profile( hb_pes_stream_t *pes ) { hb_work_info_t info; hb_work_object_t *w = hb_codec_decoder( pes->codec ); w->codec_param = pes->codec_param; int ret = w->bsinfo( w, pes->probe_buf, &info ); if ( ret < 0 ) { hb_log( "probe_dts_profile: no info type %d/0x%x for id 0x%x", pes->codec, pes->codec_param, pes->stream_id ); } switch (info.profile) { case FF_PROFILE_DTS: case FF_PROFILE_DTS_ES: case FF_PROFILE_DTS_96_24: pes->codec = HB_ACODEC_DCA; pes->stream_type = 0x82; pes->stream_kind = A; break; case FF_PROFILE_DTS_HD_HRA: case FF_PROFILE_DTS_HD_MA: pes->stream_type = 0; pes->stream_kind = A; break; default: return 0; } const char *profile_name; AVCodec *codec = avcodec_find_decoder( pes->codec_param ); profile_name = av_get_profile_name( codec, info.profile ); if ( profile_name ) { strncpy(pes->codec_name, profile_name, 80); pes->codec_name[79] = 0; } return 1; } static int do_probe( hb_pes_stream_t *pes, hb_buffer_t *buf ) { // Check upper limit of per stream data to probe if ( pes->probe_buf == NULL ) { pes->probe_buf = hb_buffer_init( 0 ); } if ( pes->probe_buf->size > HB_MAX_PROBE_SIZE ) { pes->stream_kind = N; hb_buffer_close( &pes->probe_buf ); return 1; } // Add this stream buffer to probe buffer and perform probe AVInputFormat *fmt = NULL; int score = 0; AVProbeData pd = {0,}; int size = pes->probe_buf->size + buf->size; hb_buffer_realloc(pes->probe_buf, size + AVPROBE_PADDING_SIZE ); memcpy( pes->probe_buf->data + pes->probe_buf->size, buf->data, buf->size ); pes->probe_buf->size = size; if ( pes->codec == HB_ACODEC_DCA_HD ) { // We need to probe for the profile of DTS audio in this stream. return probe_dts_profile( pes ); } // Probing is slow, so we don't want to re-probe the probe // buffer for every packet we add to it. Grow the buffer // by a factor of 2 before probing again. if ( pes->probe_buf->size < pes->probe_next_size ) return 0; pes->probe_next_size = pes->probe_buf->size * 2; pd.buf = pes->probe_buf->data; pd.buf_size = pes->probe_buf->size; fmt = av_probe_input_format2( &pd, 1, &score ); if ( fmt && score > AVPROBE_SCORE_MAX / 2 ) { AVCodec *codec = avcodec_find_decoder_by_name( fmt->name ); if( !codec ) { int i; static const struct { const char *name; enum AVCodecID id; } fmt_id_type[] = { { "g722" , AV_CODEC_ID_ADPCM_G722 }, { "mlp" , AV_CODEC_ID_MLP }, { "truehd" , AV_CODEC_ID_TRUEHD }, { "shn" , AV_CODEC_ID_SHORTEN }, { "aac" , AV_CODEC_ID_AAC }, { "ac3" , AV_CODEC_ID_AC3 }, { "dts" , AV_CODEC_ID_DTS }, { "eac3" , AV_CODEC_ID_EAC3 }, { "h264" , AV_CODEC_ID_H264 }, { "m4v" , AV_CODEC_ID_MPEG4 }, { "mp3" , AV_CODEC_ID_MP3 }, { "mpegvideo", AV_CODEC_ID_MPEG2VIDEO }, { "cavsvideo", AV_CODEC_ID_CAVS }, { "dnxhd" , AV_CODEC_ID_DNXHD }, { "h261" , AV_CODEC_ID_H261 }, { "h263" , AV_CODEC_ID_H263 }, { "mjpeg" , AV_CODEC_ID_MJPEG }, { "vc1" , AV_CODEC_ID_VC1 }, { 0 }, }; for( i = 0; fmt_id_type[i].name; i++ ) { if( !strcmp(fmt->name, fmt_id_type[i].name ) ) { codec = avcodec_find_decoder( fmt_id_type[i].id ); break; } } } if( codec ) { pes->codec_param = codec->id; if ( codec->type == AVMEDIA_TYPE_VIDEO ) { pes->stream_kind = V; switch ( codec->id ) { case AV_CODEC_ID_MPEG1VIDEO: pes->codec = WORK_DECAVCODECV; pes->stream_type = 0x01; break; case AV_CODEC_ID_MPEG2VIDEO: pes->codec = WORK_DECAVCODECV; pes->stream_type = 0x02; break; case AV_CODEC_ID_H264: pes->codec = WORK_DECAVCODECV; pes->stream_type = 0x1b; break; case AV_CODEC_ID_VC1: pes->codec = WORK_DECAVCODECV; pes->stream_type = 0xea; break; default: pes->codec = WORK_DECAVCODECV; } } else if ( codec->type == AVMEDIA_TYPE_AUDIO ) { pes->stream_kind = A; switch ( codec->id ) { case AV_CODEC_ID_AC3: pes->codec = HB_ACODEC_AC3; break; default: pes->codec = HB_ACODEC_FFMPEG; } } else { pes->stream_kind = N; } strncpy(pes->codec_name, codec->name, 79); pes->codec_name[79] = 0; } else { pes->stream_kind = N; } hb_buffer_close( &pes->probe_buf ); return 1; } return 0; } static void hb_ts_resolve_pid_types(hb_stream_t *stream) { int ii, probe = 0; for ( ii = 0; ii < stream->ts.count; ii++ ) { int pid = stream->ts.list[ii].pid; int stype = ts_stream_type( stream, ii ); int pes_idx; if ( stype == 0x80 && stream->reg_desc == STR4_TO_UINT32("HDMV") ) { // LPCM audio in bluray have an stype of 0x80 // 0x80 is used for other DigiCipher normally // To distinguish, Bluray streams have a reg_desc of HDMV update_ts_streams( stream, pid, 0, stype, A, &pes_idx ); stream->pes.list[pes_idx].codec = HB_ACODEC_FFMPEG; stream->pes.list[pes_idx].codec_param = AV_CODEC_ID_PCM_BLURAY; continue; } // The blu ray consortium apparently forgot to read the portion // of the MPEG spec that says one PID should map to one media // stream and multiplexed multiple types of audio into one PID // using the extended stream identifier of the PES header to // distinguish them. So we have to check if that's happening and // if so tell the runtime what esid we want. if ( stype == 0x83 && stream->reg_desc == STR4_TO_UINT32("HDMV") ) { // This is an interleaved TrueHD/AC-3 stream and the esid of // the AC-3 is 0x76 update_ts_streams( stream, pid, HB_SUBSTREAM_BD_AC3, stype, A, &pes_idx ); stream->pes.list[pes_idx].codec = HB_ACODEC_AC3; stream->pes.list[pes_idx].codec_param = AV_CODEC_ID_AC3; update_ts_streams( stream, pid, HB_SUBSTREAM_BD_TRUEHD, stype, A, &pes_idx ); stream->pes.list[pes_idx].codec = HB_ACODEC_FFMPEG; stream->pes.list[pes_idx].codec_param = AV_CODEC_ID_TRUEHD; continue; } if ( ( stype == 0x84 || stype == 0xa1 ) && stream->reg_desc == STR4_TO_UINT32("HDMV") ) { // EAC3 audio in bluray has an stype of 0x84 // which conflicts with SDDS // To distinguish, Bluray streams have a reg_desc of HDMV update_ts_streams( stream, pid, 0, stype, A, &pes_idx ); stream->pes.list[pes_idx].codec = HB_ACODEC_FFMPEG; stream->pes.list[pes_idx].codec_param = AV_CODEC_ID_EAC3; continue; } // 0xa2 is DTS-HD LBR used in HD-DVD and bluray for // secondary audio streams. Libav can not decode yet. // Having it in the audio list causes delays during scan // while we try to get stream parameters. So skip // this type for now. if ( stype == 0x85 && stream->reg_desc == STR4_TO_UINT32("HDMV") ) { // DTS-HD HRA audio in bluray has an stype of 0x85 // which conflicts with ATSC Program ID // To distinguish, Bluray streams have a reg_desc of HDMV // This is an interleaved DTS-HD HRA/DTS stream and the // esid of the DTS is 0x71 update_ts_streams( stream, pid, HB_SUBSTREAM_BD_DTS, stype, A, &pes_idx ); stream->pes.list[pes_idx].codec = HB_ACODEC_DCA; stream->pes.list[pes_idx].codec_param = AV_CODEC_ID_DTS; update_ts_streams( stream, pid, 0, stype, A, &pes_idx ); stream->pes.list[pes_idx].codec = HB_ACODEC_DCA_HD; stream->pes.list[pes_idx].codec_param = AV_CODEC_ID_DTS; continue; } if ( stype == 0x86 && stream->reg_desc == STR4_TO_UINT32("HDMV") ) { // This is an interleaved DTS-HD MA/DTS stream and the // esid of the DTS is 0x71 update_ts_streams( stream, pid, HB_SUBSTREAM_BD_DTS, stype, A, &pes_idx ); stream->pes.list[pes_idx].codec = HB_ACODEC_DCA; stream->pes.list[pes_idx].codec_param = AV_CODEC_ID_DTS; update_ts_streams( stream, pid, 0, stype, A, &pes_idx ); stream->pes.list[pes_idx].codec = HB_ACODEC_DCA_HD; stream->pes.list[pes_idx].codec_param = AV_CODEC_ID_DTS; continue; } // stype == 0 indicates a type not in st2codec table if ( stype != 0 && ( ts_stream_kind( stream, ii ) == A || ts_stream_kind( stream, ii ) == S || ts_stream_kind( stream, ii ) == V ) ) { // Assuming there are no substreams. // This should be true before probing. // This function is only called before // probing. pes_idx = stream->ts.list[ii].pes_list; stream->pes.list[pes_idx].codec = st2codec[stype].codec; stream->pes.list[pes_idx].codec_param = st2codec[stype].codec_param; continue; } if ( ts_stream_kind( stream, ii ) == U ) { probe++; } } // Probe remaining unknown streams for stream types hb_stream_seek( stream, 0.0 ); stream->need_keyframe = 0; int total_size = 0; hb_buffer_t *buf; if ( probe ) hb_log("Probing %d unknown stream%s", probe, probe > 1 ? "s" : "" ); while ( probe && ( buf = hb_ts_stream_decode( stream ) ) != NULL ) { // Check upper limit of total data to probe total_size += buf->size; if ( total_size > HB_MAX_PROBE_SIZE * 2 ) { hb_buffer_close(&buf); break; } int idx; idx = index_of_id( stream, buf->s.id ); if (idx < 0 || stream->pes.list[idx].stream_kind != U ) { hb_buffer_close(&buf); continue; } hb_pes_stream_t *pes = &stream->pes.list[idx]; if ( do_probe( pes, buf ) ) { probe--; if ( pes->stream_kind != N ) { hb_log(" Probe: Found stream %s. stream id 0x%x-0x%x", pes->codec_name, pes->stream_id, pes->stream_id_ext); } else { hb_log(" Probe: Unsupported stream %s. stream id 0x%x-0x%x", pes->codec_name, pes->stream_id, pes->stream_id_ext); } } hb_buffer_close(&buf); } // Clean up any probe buffers and set all remaining unknown // streams to 'kind' N for ( ii = 0; ii < stream->pes.count; ii++ ) { if ( stream->pes.list[ii].stream_kind == U ) stream->pes.list[ii].stream_kind = N; hb_buffer_close( &stream->pes.list[ii].probe_buf ); stream->pes.list[ii].probe_next_size = 0; } } static void hb_ps_resolve_stream_types(hb_stream_t *stream) { int ii, probe = 0; for ( ii = 0; ii < stream->pes.count; ii++ ) { int stype = stream->pes.list[ii].stream_type; // stype == 0 indicates a type not in st2codec table if ( stype != 0 && ( stream->pes.list[ii].stream_kind == A || stream->pes.list[ii].stream_kind == S || stream->pes.list[ii].stream_kind == V ) ) { stream->pes.list[ii].codec = st2codec[stype].codec; stream->pes.list[ii].codec_param = st2codec[stype].codec_param; continue; } if ( stream->pes.list[ii].stream_kind == U ) { probe++; } } // Probe remaining unknown streams for stream types hb_stream_seek( stream, 0.0 ); stream->need_keyframe = 0; int total_size = 0; hb_buffer_t *buf; if ( probe ) hb_log("Probing %d unknown stream%s", probe, probe > 1 ? "s" : "" ); while ( probe && ( buf = hb_ps_stream_decode( stream ) ) != NULL ) { // Check upper limit of total data to probe total_size += buf->size; if ( total_size > HB_MAX_PROBE_SIZE * 2 ) break; int idx; idx = index_of_id( stream, buf->s.id ); if (idx < 0 || stream->pes.list[idx].stream_kind != U ) continue; hb_pes_stream_t *pes = &stream->pes.list[idx]; if ( do_probe( pes, buf ) ) { probe--; if ( pes->stream_kind != N ) { hb_log(" Probe: Found stream %s. stream id 0x%x-0x%x", pes->codec_name, pes->stream_id, pes->stream_id_ext); } else { hb_log(" Probe: Unsupported stream %s. stream id 0x%x-0x%x", pes->codec_name, pes->stream_id, pes->stream_id_ext); } } } // Clean up any probe buffers and set all remaining unknown // streams to 'kind' N for ( ii = 0; ii < stream->pes.count; ii++ ) { if ( stream->pes.list[ii].stream_kind == U ) stream->pes.list[ii].stream_kind = N; hb_buffer_close( &stream->pes.list[ii].probe_buf ); stream->pes.list[ii].probe_next_size = 0; } } static int hb_ts_stream_find_pids(hb_stream_t *stream) { // To be different from every other broadcaster in the world, New Zealand TV // changes PMTs (and thus video & audio PIDs) when 'programs' change. Since // we may have the tail of the previous program at the beginning of this // file, take our PMT from the middle of the file. fseeko(stream->file_handle, 0, SEEK_END); uint64_t fsize = ftello(stream->file_handle); fseeko(stream->file_handle, fsize >> 1, SEEK_SET); align_to_next_packet(stream); // Read the Transport Stream Packets (188 bytes each) looking at first for PID 0 (the PAT PID), then decode that // to find the program map PID and then decode that to get the list of audio and video PIDs for (;;) { const uint8_t *buf = next_packet( stream ); if ( buf == NULL ) { hb_log("hb_ts_stream_find_pids - end of file"); break; } // Get pid int pid = (((buf[1] & 0x1F) << 8) | buf[2]) & 0x1FFF; if ((pid == 0x0000) && (stream->ts_number_pat_entries == 0)) { decode_PAT(buf, stream); continue; } int pat_index = 0; for (pat_index = 0; pat_index < stream->ts_number_pat_entries; pat_index++) { // There are some streams where the PAT table has multiple // entries as if their are multiple programs in the same // transport stream, and yet there's actually only one // program really in the stream. This seems to be true for // transport streams that originate in the HDHomeRun but have // been output by EyeTV's export utility. What I think is // happening is that the HDHomeRun is sending the entire // transport stream as broadcast, but the EyeTV is only // recording a single (selected) program number and not // rewriting the PAT info on export to match what's actually // on the stream. Until we have a way of handling multiple // programs per transport stream elegantly we'll match on the // first pat entry for which we find a matching program map PID. // The ideal solution would be to build a title choice popup // from the PAT program number details and then select from // their - but right now the API's not capable of that. if (stream->pat_info[pat_index].program_number != 0 && pid == stream->pat_info[pat_index].program_map_PID) { if (build_program_map(buf, stream) > 0) { break; } } } // Keep going until we have a complete set of PIDs if ( ts_index_of_video( stream ) >= 0 ) break; } if ( ts_index_of_video( stream ) < 0 ) return -1; update_ts_streams( stream, stream->pmt_info.PCR_PID, 0, -1, P, NULL ); return 0; } // convert a PES PTS or DTS to an int64 static int64_t pes_timestamp( const uint8_t *buf ) { int64_t ts; ts = ( (uint64_t) ( buf[0] & 0x0e ) << 29 ) + ( buf[1] << 22 ) + ( ( buf[2] >> 1 ) << 15 ) + ( buf[3] << 7 ) + ( buf[4] >> 1 ); return ts; } static hb_buffer_t * generate_output_data(hb_stream_t *stream, int curstream) { hb_buffer_t *buf = NULL, *first = NULL; hb_pes_info_t pes_info; hb_buffer_t * b = stream->ts.list[curstream].buf; if ( !hb_parse_ps( stream, b->data, b->size, &pes_info ) ) { b->size = 0; return NULL; } uint8_t *tdat = b->data + pes_info.header_len; int size = b->size - pes_info.header_len; if ( size <= 0 ) { b->size = 0; return NULL; } int pes_idx; pes_idx = stream->ts.list[curstream].pes_list; if( stream->need_keyframe ) { // we're looking for the first video frame because we're // doing random access during 'scan' int kind = stream->pes.list[pes_idx].stream_kind; if( kind != V || !isIframe( stream, tdat, size ) ) { // not the video stream or didn't find an I frame // but we'll only wait 255 video frames for an I frame. if ( kind != V || ++stream->need_keyframe < 512 ) { b->size = 0; return NULL; } } stream->need_keyframe = 0; } // Check all substreams to see if this packet matches for ( pes_idx = stream->ts.list[curstream].pes_list; pes_idx != -1; pes_idx = stream->pes.list[pes_idx].next ) { if ( stream->pes.list[pes_idx].stream_id_ext != pes_info.stream_id_ext && stream->pes.list[pes_idx].stream_id_ext != 0 ) { continue; } // The substreams match. // Note that when stream->pes.list[pes_idx].stream_id_ext == 0, // we want the whole TS stream including all substreams. // DTS-HD is an example of this. if ( first == NULL ) first = buf = hb_buffer_init( size ); else { hb_buffer_t *tmp = hb_buffer_init( size ); buf->next = tmp; buf = tmp; } buf->s.id = get_id( &stream->pes.list[pes_idx] ); switch (stream->pes.list[pes_idx].stream_kind) { case A: buf->s.type = AUDIO_BUF; break; case V: buf->s.type = VIDEO_BUF; break; default: buf->s.type = OTHER_BUF; break; } if( b->sequence > stream->ts.pcr_out ) { // we have a new pcr stream->ts.pcr_out = b->sequence; buf->s.pcr = b->s.pcr; if( b->sequence >= stream->ts.pcr_discontinuity ) stream->ts.pcr_current = stream->ts.pcr_discontinuity; } else { buf->s.pcr = AV_NOPTS_VALUE; } // check if this packet was referenced to an older pcr and if that // pcr was prior to a discontinuity. if( b->sequence < stream->ts.pcr_current ) { // we've sent up a new pcr but have a packet referenced to an // old pcr and the difference was enough to trigger a discontinuity // correction. smash the timestamps or we'll mess up the correction. buf->s.start = AV_NOPTS_VALUE; buf->s.renderOffset = AV_NOPTS_VALUE; buf->s.stop = AV_NOPTS_VALUE; buf->s.pcr = AV_NOPTS_VALUE; } else { // put the PTS & possible DTS into 'start' & 'renderOffset' // then strip off the PES header. buf->s.start = pes_info.pts; buf->s.renderOffset = pes_info.dts; } memcpy( buf->data, tdat, size ); } b->size = 0; return first; } static void hb_ts_stream_append_pkt(hb_stream_t *stream, int idx, const uint8_t *buf, int len) { if (stream->ts.list[idx].buf->size + len > stream->ts.list[idx].buf->alloc) { int size; size = MAX( stream->ts.list[idx].buf->alloc * 2, stream->ts.list[idx].buf->size + len); hb_buffer_realloc(stream->ts.list[idx].buf, size); } memcpy( stream->ts.list[idx].buf->data + stream->ts.list[idx].buf->size, buf, len); stream->ts.list[idx].buf->size += len; } /*********************************************************************** * hb_ts_stream_decode *********************************************************************** * **********************************************************************/ hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt ) { /* * stash the output buffer pointer in our stream so we don't have to * pass it & its original value to everything we call. */ int video_index = ts_index_of_video(stream); int curstream; hb_buffer_t *buf; /* This next section validates the packet */ // Get pid and use it to find stream state. int pid = ((pkt[1] & 0x1F) << 8) | pkt[2]; if ( ( curstream = index_of_pid( stream, pid ) ) < 0 ) { return NULL; } // Get error int errorbit = (pkt[1] & 0x80) != 0; if (errorbit) { ts_err( stream, curstream, "packet error bit set"); return NULL; } // Get adaption header info int adaption = (pkt[3] & 0x30) >> 4; int adapt_len = 0; if (adaption == 0) { ts_err( stream, curstream, "adaptation code 0"); return NULL; } else if (adaption == 0x2) adapt_len = 184; else if (adaption == 0x3) { adapt_len = pkt[4] + 1; if (adapt_len > 184) { ts_err( stream, curstream, "invalid adapt len %d", adapt_len); return NULL; } } if ( adapt_len > 0 ) { if ( pkt[5] & 0x40 ) { // found a random access point } // if there's an adaptation header & PCR_flag is set // get the PCR (Program Clock Reference) // // JAS: I have a badly mastered BD that does adaptation field // stuffing incorrectly which results in invalid PCRs. Test // for all 0xff to guard against this. if ( adapt_len > 7 && ( pkt[5] & 0x10 ) != 0 && !(pkt[5] == 0xff && pkt[6] == 0xff && pkt[7] == 0xff && pkt[8] == 0xff && pkt[9] == 0xff && pkt[10] == 0xff)) { int64_t pcr; pcr = ( (uint64_t)pkt[6] << (33 - 8) ) | ( (uint64_t)pkt[7] << (33 - 16) ) | ( (uint64_t)pkt[8] << (33 - 24) ) | ( (uint64_t)pkt[9] << (33 - 32) ) | ( pkt[10] >> 7 ); ++stream->ts.pcr_in; stream->ts.found_pcr = 1; stream->ts_flags |= TS_HAS_PCR; // Check for a pcr discontinuity. // The reason for the uint cast on the pcr difference is that the // difference is significant if it advanced by more than 200ms or // if it went backwards by any amount. The negative numbers look // like huge unsigned ints so the cast allows both conditions to // be checked at once. if ( (uint64_t)( pcr - stream->ts.pcr ) > 200*90LL ) { stream->ts.pcr_discontinuity = stream->ts.pcr_in; } stream->ts.pcr = pcr; } } // If we don't have a PCR yet but the stream has PCRs just loop // so we don't process anything until we have a clock reference. // Unfortunately the HD Home Run appears to null out the PCR so if // we didn't detect a PCR during scan keep going and we'll use // the video stream DTS for the PCR. if ( !stream->ts.found_pcr && ( stream->ts_flags & TS_HAS_PCR ) ) { return NULL; } // Get continuity // Continuity only increments for adaption values of 0x3 or 0x01 // and is not checked for start packets. int start = (pkt[1] & 0x40) != 0; if ( (adaption & 0x01) != 0 ) { int continuity = (pkt[3] & 0xF); if ( continuity == stream->ts.list[curstream].continuity ) { // Spliced transport streams can have duplicate // continuity counts at the splice boundary. // Test to see if the packet is really a duplicate // by comparing packet summaries to see if they // match. uint8_t summary[8]; summary[0] = adaption; summary[1] = adapt_len; if (adapt_len + 4 + 6 + 9 <= 188) { memcpy(&summary[2], pkt+4+adapt_len+9, 6); } else { memset(&summary[2], 0, 6); } if ( memcmp( summary, stream->ts.list[curstream].pkt_summary, 8 ) == 0 ) { // we got a duplicate packet (usually used to introduce // a PCR when one is needed). The only thing that can // change in the dup is the PCR which we grabbed above // so ignore the rest. return NULL; } } if ( !start && (stream->ts.list[curstream].continuity != -1) && !stream->ts.list[curstream].skipbad && (continuity != ( (stream->ts.list[curstream].continuity + 1) & 0xf ) ) ) { ts_err( stream, curstream, "continuity error: got %d expected %d", (int)continuity, (stream->ts.list[curstream].continuity + 1) & 0xf ); stream->ts.list[curstream].continuity = continuity; return NULL; } stream->ts.list[curstream].continuity = continuity; // Save a summary of this packet for later duplicate // testing. The summary includes some header information // and payload bytes. Should be enough to detect // non-duplicates. stream->ts.list[curstream].pkt_summary[0] = adaption; stream->ts.list[curstream].pkt_summary[1] = adapt_len; if (adapt_len + 4 + 6 + 9 <= 188) { memcpy(&stream->ts.list[curstream].pkt_summary[2], pkt+4+adapt_len+9, 6); } else { memset(&stream->ts.list[curstream].pkt_summary[2], 0, 6); } } if ( ts_stream_kind( stream, curstream ) == P ) { // This is a stream that only contains PCRs. No need to process // the remainder of the packet. // // I ran across a poorly mastered BD that does not properly pad // the adaptation field and causes parsing errors below if we // do not exit early here. return NULL; } /* If we get here the packet is valid - process its data */ if ( start ) { // Found a random access point or we have finished generating a PES // and must start a new one. // PES must begin with an mpeg start code const uint8_t *pes = pkt + adapt_len + 4; if ( pes[0] != 0x00 || pes[1] != 0x00 || pes[2] != 0x01 ) { ts_err( stream, curstream, "missing start code" ); stream->ts.list[curstream].skipbad = 1; return NULL; } // If we were skipping a bad packet, start fresh on this new PES packet if (stream->ts.list[curstream].skipbad == 1) { stream->ts.list[curstream].skipbad = 0; } if ( curstream == video_index ) { ++stream->frames; // if we don't have a pcr yet use the dts from this frame // to attempt to detect discontinuities if ( !stream->ts.found_pcr ) { // PES must begin with an mpeg start code & contain // a DTS or PTS. const uint8_t *pes = pkt + adapt_len + 4; if ( pes[0] != 0x00 || pes[1] != 0x00 || pes[2] != 0x01 || ( pes[7] >> 6 ) == 0 ) { return NULL; } // if we have a dts use it otherwise use the pts int64_t timestamp; timestamp = pes_timestamp( pes + ( pes[7] & 0x40?14:9 ) ); if( stream->ts.last_timestamp < 0 || timestamp - stream->ts.last_timestamp > 90 * 600 || stream->ts.last_timestamp - timestamp > 90 * 600 ) { stream->ts.pcr = timestamp; ++stream->ts.pcr_in; stream->ts.pcr_discontinuity = stream->ts.pcr_in; } stream->ts.last_timestamp = timestamp; } } // If we have some data already on this stream, turn it into // a program stream packet. Then add the payload for this // packet to the current pid's buffer. if ( stream->ts.list[curstream].buf->size ) { // we have to ship the old packet before updating the pcr // since the packet we've been accumulating is referenced // to the old pcr. buf = generate_output_data(stream, curstream); if ( buf ) { // Output data is ready. // remember the pcr that was in effect when we started // this packet. stream->ts.list[curstream].buf->sequence = stream->ts.pcr_in; stream->ts.list[curstream].buf->s.pcr = stream->ts.pcr; hb_ts_stream_append_pkt(stream, curstream, pkt + 4 + adapt_len, 184 - adapt_len); return buf; } } // remember the pcr that was in effect when we started this packet. stream->ts.list[curstream].buf->sequence = stream->ts.pcr_in; stream->ts.list[curstream].buf->s.pcr = stream->ts.pcr; } // Add the payload for this packet to the current buffer if (!stream->ts.list[curstream].skipbad && (184 - adapt_len) > 0) { hb_ts_stream_append_pkt(stream, curstream, pkt + 4 + adapt_len, 184 - adapt_len); // see if we've hit the end of this PES packet const uint8_t *pes = stream->ts.list[curstream].buf->data; int len = ( pes[4] << 8 ) + pes[5] + 6; if ( len > 6 && stream->ts.list[curstream].buf->size == len && pes[0] == 0x00 && pes[1] == 0x00 && pes[2] == 0x01 ) { buf = generate_output_data(stream, curstream); if ( buf ) return buf; } } return NULL; } static hb_buffer_t * hb_ts_stream_decode( hb_stream_t *stream ) { hb_buffer_t * b; // spin until we get a packet of data from some stream or hit eof while ( 1 ) { const uint8_t *buf = next_packet(stream); if ( buf == NULL ) { // end of file - we didn't finish filling our ps write buffer // so just discard the remainder (the partial buffer is useless) hb_log("hb_ts_stream_decode - eof"); return NULL; } b = hb_ts_decode_pkt( stream, buf ); if ( b ) { return b; } } return NULL; } void hb_stream_set_need_keyframe(hb_stream_t *stream, int need_keyframe) { if ( stream->hb_stream_type == transport || stream->hb_stream_type == program ) { // Only wait for a keyframe if the stream is known to have IDRs stream->need_keyframe = !!need_keyframe & !!stream->has_IDRs; } else { stream->need_keyframe = need_keyframe; } } void hb_ts_stream_reset(hb_stream_t *stream) { int i; for (i=0; i < stream->ts.count; i++) { if ( stream->ts.list[i].buf ) stream->ts.list[i].buf->size = 0; if ( stream->ts.list[i].extra_buf ) stream->ts.list[i].extra_buf->size = 0; stream->ts.list[i].skipbad = 1; stream->ts.list[i].continuity = -1; } stream->need_keyframe = 1; stream->ts.found_pcr = 0; stream->ts.pcr_out = 0; stream->ts.pcr_in = 0; stream->ts.pcr = AV_NOPTS_VALUE; stream->ts.pcr_current = -1; stream->ts.last_timestamp = AV_NOPTS_VALUE; stream->frames = 0; stream->errors = 0; stream->last_error_frame = -10000; stream->last_error_count = 0; } void hb_ps_stream_reset(hb_stream_t *stream) { stream->need_keyframe = 1; stream->pes.found_scr = 0; stream->pes.scr = AV_NOPTS_VALUE; stream->frames = 0; stream->errors = 0; } // ------------------------------------------------------------------ // Support for reading media files via the ffmpeg libraries. static int ffmpeg_open( hb_stream_t *stream, hb_title_t *title, int scan ) { AVFormatContext *info_ic = NULL; av_log_set_level( AV_LOG_ERROR ); // Increase probe buffer size // The default (5MB) is not big enough to successfully scan // some files with large PNGs AVDictionary * av_opts = NULL; av_dict_set( &av_opts, "probesize", "15000000", 0 ); // FFMpeg has issues with seeking. After av_find_stream_info, the // streams are left in an indeterminate position. So a seek is // necessary to force things back to the beginning of the stream. // But then the seek fails for some stream types. So the safest thing // to do seems to be to open 2 AVFormatContext. One for probing info // and the other for reading. if ( avformat_open_input( &info_ic, stream->path, NULL, &av_opts ) < 0 ) { return 0; } // libav populates av_opts with the things it didn't recognize. AVDictionaryEntry *t = NULL; while ((t = av_dict_get(av_opts, "", t, AV_DICT_IGNORE_SUFFIX)) != NULL) { hb_log("ffmpeg_open: unknown option '%s'", t->key); } av_dict_free( &av_opts ); if ( avformat_find_stream_info( info_ic, NULL ) < 0 ) goto fail; title->opaque_priv = (void*)info_ic; stream->ffmpeg_ic = info_ic; stream->hb_stream_type = ffmpeg; stream->ffmpeg_pkt = malloc(sizeof(*stream->ffmpeg_pkt)); av_init_packet( stream->ffmpeg_pkt ); stream->chapter_end = INT64_MAX; if ( !scan ) { // we're opening for read. scan passed out codec params that // indexed its stream so we need to remap them so they point // to this stream. stream->ffmpeg_video_id = title->video_id; av_log_set_level( AV_LOG_ERROR ); } else { // we're opening for scan. let ffmpeg put some info into the // log about what we've got. stream->ffmpeg_video_id = title->video_id; av_log_set_level( AV_LOG_INFO ); av_dump_format( info_ic, 0, stream->path, 0 ); av_log_set_level( AV_LOG_ERROR ); // accept this file if it has at least one video stream we can decode int i; for (i = 0; i < info_ic->nb_streams; ++i ) { if ( info_ic->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) { break; } } if ( i >= info_ic->nb_streams ) goto fail; } return 1; fail: if ( info_ic ) avformat_close_input( &info_ic ); return 0; } static void ffmpeg_close( hb_stream_t *d ) { avformat_close_input( &d->ffmpeg_ic ); if ( d->ffmpeg_pkt != NULL ) { free( d->ffmpeg_pkt ); d->ffmpeg_pkt = NULL; } } static void add_ffmpeg_audio(hb_title_t *title, hb_stream_t *stream, int id) { AVStream *st = stream->ffmpeg_ic->streams[id]; AVCodecContext *codec = st->codec; AVDictionaryEntry *tag = av_dict_get(st->metadata, "language", NULL, 0); hb_audio_t *audio = calloc(1, sizeof(*audio)); audio->id = id; audio->config.in.track = id; audio->config.in.codec = HB_ACODEC_FFMPEG; audio->config.in.codec_param = codec->codec_id; // set the bitrate to 0; decavcodecaBSInfo will be called and fill the rest audio->config.in.bitrate = 0; // set the input codec and extradata for Passthru switch (codec->codec_id) { case AV_CODEC_ID_AAC: { int len = MIN(codec->extradata_size, HB_CONFIG_MAX_SIZE); memcpy(audio->priv.config.extradata.bytes, codec->extradata, len); audio->priv.config.extradata.length = len; audio->config.in.codec = HB_ACODEC_FFAAC; } break; case AV_CODEC_ID_AC3: audio->config.in.codec = HB_ACODEC_AC3; break; case AV_CODEC_ID_DTS: { switch (codec->profile) { case FF_PROFILE_DTS: case FF_PROFILE_DTS_ES: case FF_PROFILE_DTS_96_24: audio->config.in.codec = HB_ACODEC_DCA; break; case FF_PROFILE_DTS_HD_MA: case FF_PROFILE_DTS_HD_HRA: audio->config.in.codec = HB_ACODEC_DCA_HD; break; default: break; } } break; case AV_CODEC_ID_MP3: audio->config.in.codec = HB_ACODEC_MP3; break; default: break; } set_audio_description(audio, lang_for_code2(tag != NULL ? tag->value : "und")); hb_list_add(title->list_audio, audio); } /* * Format: * MkvVobSubtitlePrivateData = ( Line )* * Line = FieldName ':' ' ' FieldValue '\n' * FieldName = [^:]+ * FieldValue = [^\n]+ * * The line of interest is: * PaletteLine = "palette" ':' ' ' RRGGBB ( ',' ' ' RRGGBB )* * * More information on the format at: * http://www.matroska.org/technical/specs/subtitles/images.html */ static int ffmpeg_parse_vobsub_extradata_mkv( AVCodecContext *codec, hb_subtitle_t *subtitle ) { // lines = (string) codec->extradata; char *lines = malloc( codec->extradata_size + 1 ); if ( lines == NULL ) return 1; memcpy( lines, codec->extradata, codec->extradata_size ); lines[codec->extradata_size] = '\0'; uint32_t rgb[16]; int gotPalette = 0; int gotDimensions = 0; char *curLine, *curLine_parserData; for ( curLine = strtok_r( lines, "\n", &curLine_parserData ); curLine; curLine = strtok_r( NULL, "\n", &curLine_parserData ) ) { if (!gotPalette) { int numElementsRead = sscanf(curLine, "palette: " "%06x, %06x, %06x, %06x, " "%06x, %06x, %06x, %06x, " "%06x, %06x, %06x, %06x, " "%06x, %06x, %06x, %06x", &rgb[0], &rgb[1], &rgb[2], &rgb[3], &rgb[4], &rgb[5], &rgb[6], &rgb[7], &rgb[8], &rgb[9], &rgb[10], &rgb[11], &rgb[12], &rgb[13], &rgb[14], &rgb[15]); if (numElementsRead == 16) { gotPalette = 1; } } if (!gotDimensions) { int numElementsRead = sscanf(curLine, "size: %dx%d", &subtitle->width, &subtitle->height); if (numElementsRead == 2) { gotDimensions = 1; } } if (gotPalette && gotDimensions) break; } if (subtitle->width == 0 || subtitle->height == 0) { subtitle->width = 720; subtitle->height = 480; } free( lines ); if ( gotPalette ) { int i; for (i=0; i<16; i++) subtitle->palette[i] = hb_rgb2yuv(rgb[i]); subtitle->palette_set = 1; return 0; } else { return 1; } } /* * Format: 8-bit {0,Y,Cb,Cr} x 16 */ static int ffmpeg_parse_vobsub_extradata_mp4( AVCodecContext *codec, hb_subtitle_t *subtitle ) { if ( codec->extradata_size != 4*16 ) return 1; int i, j; for ( i=0, j=0; i<16; i++, j+=4 ) { subtitle->palette[i] = codec->extradata[j+1] << 16 | // Y codec->extradata[j+2] << 8 | // Cb codec->extradata[j+3] << 0; // Cr subtitle->palette_set = 1; } if (codec->width <= 0 || codec->height <= 0) { subtitle->width = 720; subtitle->height = 480; } else { subtitle->width = codec->width; subtitle->height = codec->height; } return 0; } /* * Parses the 'subtitle->palette' information from the specific VOB subtitle track's private data. * Returns 0 if successful or 1 if parsing failed or was incomplete. */ static int ffmpeg_parse_vobsub_extradata( AVCodecContext *codec, hb_subtitle_t *subtitle ) { // XXX: Better if we actually chose the correct parser based on the input container return ffmpeg_parse_vobsub_extradata_mkv( codec, subtitle ) && ffmpeg_parse_vobsub_extradata_mp4( codec, subtitle ); } static void add_ffmpeg_subtitle( hb_title_t *title, hb_stream_t *stream, int id ) { AVStream *st = stream->ffmpeg_ic->streams[id]; AVCodecContext *codec = st->codec; hb_subtitle_t *subtitle = calloc( 1, sizeof(*subtitle) ); subtitle->id = id; switch ( codec->codec_id ) { case AV_CODEC_ID_DVD_SUBTITLE: subtitle->format = PICTURESUB; subtitle->source = VOBSUB; subtitle->config.dest = RENDERSUB; // By default render (burn-in) the VOBSUB. subtitle->codec = WORK_DECVOBSUB; if ( ffmpeg_parse_vobsub_extradata( codec, subtitle ) ) hb_log( "add_ffmpeg_subtitle: malformed extradata for VOB subtitle track; " "subtitle colors likely to be wrong" ); break; case AV_CODEC_ID_TEXT: subtitle->format = TEXTSUB; subtitle->source = UTF8SUB; subtitle->config.dest = PASSTHRUSUB; subtitle->codec = WORK_DECUTF8SUB; break; case AV_CODEC_ID_MOV_TEXT: // TX3G subtitle->format = TEXTSUB; subtitle->source = TX3GSUB; subtitle->config.dest = PASSTHRUSUB; subtitle->codec = WORK_DECTX3GSUB; break; case AV_CODEC_ID_SSA: subtitle->format = TEXTSUB; subtitle->source = SSASUB; subtitle->config.dest = PASSTHRUSUB; subtitle->codec = WORK_DECSSASUB; break; case AV_CODEC_ID_HDMV_PGS_SUBTITLE: subtitle->format = PICTURESUB; subtitle->source = PGSSUB; subtitle->config.dest = RENDERSUB; subtitle->codec = WORK_DECPGSSUB; break; default: hb_log( "add_ffmpeg_subtitle: unknown subtitle stream type: 0x%x", (int) codec->codec_id ); free(subtitle); return; } AVDictionaryEntry *tag; iso639_lang_t *language; tag = av_dict_get( st->metadata, "language", NULL, 0 ); language = lang_for_code2( tag ? tag->value : "und" ); strcpy( subtitle->lang, language->eng_name ); strncpy( subtitle->iso639_2, language->iso639_2, 4 ); // Copy the extradata for the subtitle track if (codec->extradata != NULL) { subtitle->extradata = malloc( codec->extradata_size ); memcpy( subtitle->extradata, codec->extradata, codec->extradata_size ); subtitle->extradata_size = codec->extradata_size; } subtitle->track = id; hb_list_add(title->list_subtitle, subtitle); } static char *get_ffmpeg_metadata_value( AVDictionary *m, char *key ) { AVDictionaryEntry *tag = NULL; while ( (tag = av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX)) ) { if ( !strcmp( key, tag->key ) ) { return tag->value; } } return NULL; } static void add_ffmpeg_attachment( hb_title_t *title, hb_stream_t *stream, int id ) { AVStream *st = stream->ffmpeg_ic->streams[id]; AVCodecContext *codec = st->codec; enum attachtype type; const char *name = get_ffmpeg_metadata_value( st->metadata, "filename" ); switch ( codec->codec_id ) { case AV_CODEC_ID_TTF: // Libav sets codec ID based on mime type of the attachment type = FONT_TTF_ATTACH; break; default: { int len = name ? strlen( name ) : 0; if( len >= 4 && ( !strcmp( name + len - 4, ".ttc" ) || !strcmp( name + len - 4, ".TTC" ) || !strcmp( name + len - 4, ".ttf" ) || !strcmp( name + len - 4, ".TTF" ) ) ) { // Some attachments don't have the right mime type. // So also trigger on file name extension. type = FONT_TTF_ATTACH; break; } // Ignore unrecognized attachment type return; } } hb_attachment_t *attachment = calloc( 1, sizeof(*attachment) ); // Copy the attachment name and data attachment->type = type; attachment->name = strdup( name ); attachment->data = malloc( codec->extradata_size ); memcpy( attachment->data, codec->extradata, codec->extradata_size ); attachment->size = codec->extradata_size; hb_list_add(title->list_attachment, attachment); } static int ffmpeg_decmetadata( AVDictionary *m, hb_title_t *title ) { int result = 0; AVDictionaryEntry *tag = NULL; while ( (tag = av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX)) ) { if ( !strcasecmp( "TITLE", tag->key ) ) { hb_metadata_set_name(title->metadata, tag->value); result = 1; } else if ( !strcasecmp( "ARTIST", tag->key ) ) { hb_metadata_set_artist(title->metadata, tag->value); result = 1; } else if ( !strcasecmp( "DIRECTOR", tag->key ) || !strcasecmp( "album_artist", tag->key ) ) { hb_metadata_set_album_artist(title->metadata, tag->value); result = 1; } else if ( !strcasecmp( "COMPOSER", tag->key ) ) { hb_metadata_set_composer(title->metadata, tag->value); result = 1; } else if ( !strcasecmp( "DATE_RELEASED", tag->key ) || !strcasecmp( "date", tag->key ) ) { hb_metadata_set_release_date(title->metadata, tag->value); result = 1; } else if ( !strcasecmp( "SUMMARY", tag->key ) || !strcasecmp( "comment", tag->key ) ) { hb_metadata_set_comment(title->metadata, tag->value); result = 1; } else if ( !strcasecmp( "GENRE", tag->key ) ) { hb_metadata_set_genre(title->metadata, tag->value); result = 1; } else if ( !strcasecmp( "DESCRIPTION", tag->key ) ) { hb_metadata_set_description(title->metadata, tag->value); result = 1; } else if ( !strcasecmp( "SYNOPSIS", tag->key ) ) { hb_metadata_set_long_description(title->metadata, tag->value); result = 1; } } return result; } static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream, hb_title_t *title ) { AVFormatContext *ic = stream->ffmpeg_ic; // 'Barebones Title' title->type = HB_FF_STREAM_TYPE; title->index = 1; // Copy part of the stream path to the title name char *sep = hb_strr_dir_sep(stream->path); if (sep) strcpy(title->name, sep+1); char *dot_term = strrchr(title->name, '.'); if (dot_term) *dot_term = '\0'; uint64_t dur = ic->duration * 90000 / AV_TIME_BASE; title->duration = dur; dur /= 90000; title->hours = dur / 3600; title->minutes = ( dur % 3600 ) / 60; title->seconds = dur % 60; // set the title to decode the first video stream in the file title->demuxer = HB_NULL_DEMUXER; title->video_codec = 0; int i; int pix_fmt = -1; for (i = 0; i < ic->nb_streams; ++i ) { if ( ic->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && !(ic->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC) && avcodec_find_decoder( ic->streams[i]->codec->codec_id ) && title->video_codec == 0 ) { AVCodecContext *context = ic->streams[i]->codec; pix_fmt = context->pix_fmt; if ( context->pix_fmt != AV_PIX_FMT_YUV420P && !sws_isSupportedInput( context->pix_fmt ) ) { hb_log( "ffmpeg_title_scan: Unsupported color space" ); continue; } title->video_id = i; stream->ffmpeg_video_id = i; if ( ic->streams[i]->sample_aspect_ratio.num && ic->streams[i]->sample_aspect_ratio.den ) { title->pixel_aspect_width = ic->streams[i]->sample_aspect_ratio.num; title->pixel_aspect_height = ic->streams[i]->sample_aspect_ratio.den; } title->video_codec = WORK_DECAVCODECV; title->video_codec_param = context->codec_id; } else if ( ic->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && avcodec_find_decoder( ic->streams[i]->codec->codec_id ) ) { add_ffmpeg_audio( title, stream, i ); } else if ( ic->streams[i]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE ) { add_ffmpeg_subtitle( title, stream, i ); } else if ( ic->streams[i]->codec->codec_type == AVMEDIA_TYPE_ATTACHMENT ) { add_ffmpeg_attachment( title, stream, i ); } } title->container_name = strdup( ic->iformat->name ); title->data_rate = ic->bit_rate; hb_deep_log( 2, "Found ffmpeg %d chapters, container=%s", ic->nb_chapters, ic->iformat->name ); if( ic->nb_chapters != 0 ) { AVChapter *m; uint64_t duration_sum = 0; for( i = 0; i < ic->nb_chapters; i++ ) if( ( m = ic->chapters[i] ) != NULL ) { AVDictionaryEntry *tag; hb_chapter_t * chapter; chapter = calloc( sizeof( hb_chapter_t ), 1 ); chapter->index = i+1; chapter->duration = ( m->end / ( (double) m->time_base.num * m->time_base.den ) ) * 90000 - duration_sum; duration_sum += chapter->duration; int seconds = ( chapter->duration + 45000 ) / 90000; chapter->hours = ( seconds / 3600 ); chapter->minutes = ( seconds % 3600 ) / 60; chapter->seconds = ( seconds % 60 ); tag = av_dict_get( m->metadata, "title", NULL, 0 ); /* Ignore generic chapter names set by MakeMKV * ("Chapter 00" etc.). * Our default chapter names are better. */ if( tag && tag->value && ( strncmp( "Chapter ", tag->value, 8 ) || strlen( tag->value ) > 11 ) ) { hb_chapter_set_title( chapter, tag->value ); } else { char chapter_title[80]; sprintf( chapter_title, "Chapter %d", chapter->index ); hb_chapter_set_title( chapter, chapter_title ); } hb_deep_log( 2, "Added chapter %i, name='%s', dur=%"PRIu64", (%02i:%02i:%02i)", chapter->index, chapter->title, chapter->duration, chapter->hours, chapter->minutes, chapter->seconds ); hb_list_add( title->list_chapter, chapter ); } } /* * Fill the metadata. */ ffmpeg_decmetadata( ic->metadata, title ); if( hb_list_count( title->list_chapter ) == 0 ) { // Need at least one chapter hb_chapter_t * chapter; chapter = calloc( sizeof( hb_chapter_t ), 1 ); chapter->index = 1; chapter->duration = title->duration; chapter->hours = title->hours; chapter->minutes = title->minutes; chapter->seconds = title->seconds; hb_list_add( title->list_chapter, chapter ); } #ifdef USE_HWD hb_va_dxva2_t * dxva2 = NULL; dxva2 = hb_va_create_dxva2( dxva2, title->video_codec_param ); if (dxva2) { title->hwd_support = 1; hb_va_close(dxva2); dxva2 = NULL; } else title->hwd_support = 0; if ( hb_check_hwd_fmt(pix_fmt) == 0) title->hwd_support = 0; #else // Eliminate compiler warning "pix_fmt set but not used" (void)pix_fmt; title->hwd_support = 0; #endif return title; } static int64_t av_to_hb_pts( int64_t pts, double conv_factor ) { if ( pts == AV_NOPTS_VALUE ) return AV_NOPTS_VALUE; return (int64_t)( (double)pts * conv_factor ); } static int ffmpeg_is_keyframe( hb_stream_t *stream ) { uint8_t *pkt; switch ( stream->ffmpeg_ic->streams[stream->ffmpeg_video_id]->codec->codec_id ) { case AV_CODEC_ID_VC1: // XXX the VC1 codec doesn't mark key frames so to get previews // we do it ourselves here. The decoder gets messed up if it // doesn't get a SEQ header first so we consider that to be a key frame. pkt = stream->ffmpeg_pkt->data; if ( !pkt[0] && !pkt[1] && pkt[2] == 1 && pkt[3] == 0x0f ) return 1; return 0; case AV_CODEC_ID_WMV3: // XXX the ffmpeg WMV3 codec doesn't mark key frames. // Only M$ could make I-frame detection this complicated: there // are two to four bits of unused junk ahead of the frame type // so we have to look at the sequence header to find out how much // to skip. Then there are three different ways of coding the type // depending on whether it's main or advanced profile then whether // there are bframes or not so we have to look at the sequence // header to get that. pkt = stream->ffmpeg_pkt->data; uint8_t *seqhdr = stream->ffmpeg_ic->streams[stream->ffmpeg_video_id]->codec->extradata; int pshift = 2; if ( ( seqhdr[3] & 0x02 ) == 0 ) // no FINTERPFLAG ++pshift; if ( ( seqhdr[3] & 0x80 ) == 0 ) // no RANGEREDUCTION ++pshift; if ( seqhdr[3] & 0x70 ) // stream has b-frames return ( ( pkt[0] >> pshift ) & 0x3 ) == 0x01; return ( ( pkt[0] >> pshift ) & 0x2 ) == 0; default: break; } return ( stream->ffmpeg_pkt->flags & AV_PKT_FLAG_KEY ); } hb_buffer_t * hb_ffmpeg_read( hb_stream_t *stream ) { int err; hb_buffer_t * buf; again: if ( ( err = av_read_frame( stream->ffmpeg_ic, stream->ffmpeg_pkt )) < 0 ) { // av_read_frame can return EAGAIN. In this case, it expects // to be called again to get more data. if ( err == AVERROR(EAGAIN) ) { goto again; } // XXX the following conditional is to handle avi files that // use M$ 'packed b-frames' and occasionally have negative // sizes for the null frames these require. if ( err != AVERROR(ENOMEM) || stream->ffmpeg_pkt->size >= 0 ) // eof return NULL; } if ( stream->ffmpeg_pkt->stream_index == stream->ffmpeg_video_id ) { if ( stream->need_keyframe ) { // we've just done a seek (generally for scan or live preview) and // want to start at a keyframe. Some ffmpeg codecs seek to a key // frame but most don't. So we spin until we either get a keyframe // or we've looked through 50 video frames without finding one. if ( ! ffmpeg_is_keyframe( stream ) && ++stream->need_keyframe < 50 ) { av_free_packet( stream->ffmpeg_pkt ); goto again; } stream->need_keyframe = 0; } ++stream->frames; } if ( stream->ffmpeg_pkt->size <= 0 ) { // M$ "invalid and inefficient" packed b-frames require 'null frames' // following them to preserve the timing (since the packing puts two // or more frames in what looks like one avi frame). The contents and // size of these null frames are ignored by the ff_h263_decode_frame // as long as they're < 20 bytes. We need a positive size so we use // one byte if we're given a zero or negative size. We don't know // if the pkt data points anywhere reasonable so we just stick a // byte of zero in our outbound buf. buf = hb_buffer_init( 1 ); *buf->data = 0; } else { // sometimes we get absurd sizes from ffmpeg if ( stream->ffmpeg_pkt->size >= (1 << 25) ) { hb_log( "ffmpeg_read: pkt too big: %d bytes", stream->ffmpeg_pkt->size ); av_free_packet( stream->ffmpeg_pkt ); return hb_ffmpeg_read( stream ); } buf = hb_buffer_init( stream->ffmpeg_pkt->size ); memcpy( buf->data, stream->ffmpeg_pkt->data, stream->ffmpeg_pkt->size ); const uint8_t *palette; int size; palette = av_packet_get_side_data(stream->ffmpeg_pkt, AV_PKT_DATA_PALETTE, &size); if (palette != NULL) { buf->palette = hb_buffer_init( size ); memcpy( buf->palette->data, palette, size ); } } buf->s.id = stream->ffmpeg_pkt->stream_index; // compute a conversion factor to go from the ffmpeg // timebase for the stream to HB's 90kHz timebase. AVStream *s = stream->ffmpeg_ic->streams[stream->ffmpeg_pkt->stream_index]; double tsconv = 90000. * (double)s->time_base.num / (double)s->time_base.den; buf->s.start = av_to_hb_pts( stream->ffmpeg_pkt->pts, tsconv ); buf->s.renderOffset = av_to_hb_pts( stream->ffmpeg_pkt->dts, tsconv ); if ( buf->s.renderOffset >= 0 && buf->s.start == AV_NOPTS_VALUE ) { buf->s.start = buf->s.renderOffset; } else if ( buf->s.renderOffset == AV_NOPTS_VALUE && buf->s.start >= 0 ) { buf->s.renderOffset = buf->s.start; } /* * Fill out buf->s.stop for subtitle packets * * libavcodec's MKV demuxer stores the duration of UTF-8 subtitles (AV_CODEC_ID_TEXT) * in the 'convergence_duration' field for some reason. * * Other subtitles' durations are stored in the 'duration' field. * * VOB subtitles (AV_CODEC_ID_DVD_SUBTITLE) do not have their duration stored in * either field. This is not a problem because the VOB decoder can extract this * information from the packet payload itself. * * SSA subtitles (AV_CODEC_ID_SSA) do not have their duration stored in * either field. This is not a problem because the SSA decoder can extract this * information from the packet payload itself. */ enum AVCodecID ffmpeg_pkt_codec; enum AVMediaType codec_type; ffmpeg_pkt_codec = stream->ffmpeg_ic->streams[stream->ffmpeg_pkt->stream_index]->codec->codec_id; codec_type = stream->ffmpeg_ic->streams[stream->ffmpeg_pkt->stream_index]->codec->codec_type; switch ( codec_type ) { case AVMEDIA_TYPE_VIDEO: buf->s.type = VIDEO_BUF; /* * libav avcodec_decode_video2() needs AVPacket flagged with AV_PKT_FLAG_KEY * for some codecs. For example, sequence of PNG in a mov container. */ if ( stream->ffmpeg_pkt->flags & AV_PKT_FLAG_KEY ) { buf->s.frametype |= HB_FRAME_KEY; } break; case AVMEDIA_TYPE_AUDIO: buf->s.type = AUDIO_BUF; break; case AVMEDIA_TYPE_SUBTITLE: buf->s.type = SUBTITLE_BUF; break; default: buf->s.type = OTHER_BUF; break; } if ( ffmpeg_pkt_codec == AV_CODEC_ID_TEXT ) { int64_t ffmpeg_pkt_duration = stream->ffmpeg_pkt->convergence_duration; int64_t buf_duration = av_to_hb_pts( ffmpeg_pkt_duration, tsconv ); buf->s.stop = buf->s.start + buf_duration; } if ( ffmpeg_pkt_codec == AV_CODEC_ID_MOV_TEXT ) { int64_t ffmpeg_pkt_duration = stream->ffmpeg_pkt->duration; int64_t buf_duration = av_to_hb_pts( ffmpeg_pkt_duration, tsconv ); buf->s.stop = buf->s.start + buf_duration; } /* * Check to see whether this buffer is on a chapter * boundary, if so mark it as such in the buffer then advance * chapter_end to the end of the next chapter. * If there are no chapters, chapter_end is always initialized to INT64_MAX * (roughly 3 million years at our 90KHz clock rate) so the test * below handles both the chapters & no chapters case. */ if ( stream->ffmpeg_pkt->stream_index == stream->ffmpeg_video_id && buf->s.start >= stream->chapter_end ) { hb_chapter_t *chapter = hb_list_item( stream->title->list_chapter, stream->chapter+1 ); if( chapter ) { stream->chapter++; stream->chapter_end += chapter->duration; buf->s.new_chap = stream->chapter + 1; hb_deep_log( 2, "ffmpeg_read starting chapter %i at %"PRId64, stream->chapter + 1, buf->s.start); } else { // Must have run out of chapters, stop looking. stream->chapter_end = INT64_MAX; buf->s.new_chap = 0; } } else { buf->s.new_chap = 0; } av_free_packet( stream->ffmpeg_pkt ); return buf; } static int ffmpeg_seek( hb_stream_t *stream, float frac ) { AVFormatContext *ic = stream->ffmpeg_ic; int res; if ( frac > 0. ) { int64_t pos = (double)stream->ffmpeg_ic->duration * (double)frac + ffmpeg_initial_timestamp( stream ); res = avformat_seek_file( ic, -1, 0, pos, pos, AVSEEK_FLAG_BACKWARD); if (res < 0) { hb_error("avformat_seek_file failed"); } } else { int64_t pos = ffmpeg_initial_timestamp( stream ); res = avformat_seek_file( ic, -1, 0, pos, pos, AVSEEK_FLAG_BACKWARD); if (res < 0) { hb_error("avformat_seek_file failed"); } } stream->need_keyframe = 1; return 1; } // Assumes that we are always seeking forward static int ffmpeg_seek_ts( hb_stream_t *stream, int64_t ts ) { AVFormatContext *ic = stream->ffmpeg_ic; int64_t pos; int ret; pos = ts * AV_TIME_BASE / 90000 + ffmpeg_initial_timestamp( stream ); AVStream *st = stream->ffmpeg_ic->streams[stream->ffmpeg_video_id]; // timebase must be adjusted to match timebase of stream we are // using for seeking. pos = av_rescale(pos, st->time_base.den, AV_TIME_BASE * (int64_t)st->time_base.num); stream->need_keyframe = 1; // Seek to the nearest timestamp before that requested where // there is an I-frame ret = avformat_seek_file( ic, stream->ffmpeg_video_id, 0, pos, pos, 0); return ret; } HandBrake-0.10.2/libhb/internal.h0000664000175200017520000004106312464460407017117 0ustar handbrakehandbrake/* internal.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hbffmpeg.h" #include "extras/cl.h" /*********************************************************************** * common.c **********************************************************************/ void hb_log( char * log, ... ) HB_WPRINTF(1,2); extern int global_verbosity_level; // Global variable for hb_deep_log typedef enum hb_debug_level_s { HB_SUPPORT_LOG = 1, // helpful in tech support HB_HOUSEKEEPING_LOG = 2, // stuff we hate scrolling through HB_GRANULAR_LOG = 3 // sample-by-sample } hb_debug_level_t; void hb_valog( hb_debug_level_t level, const char * prefix, const char * log, va_list args) HB_WPRINTF(3,0); void hb_deep_log( hb_debug_level_t level, char * log, ... ) HB_WPRINTF(2,3); void hb_error( char * fmt, ...) HB_WPRINTF(1,2); void hb_hexdump( hb_debug_level_t level, const char * label, const uint8_t * data, int len ); int hb_list_bytes( hb_list_t * ); void hb_list_seebytes( hb_list_t * l, uint8_t * dst, int size ); void hb_list_getbytes( hb_list_t * l, uint8_t * dst, int size, uint64_t * pts, uint64_t * pos ); void hb_list_empty( hb_list_t ** ); hb_title_t * hb_title_init( char * dvd, int index ); void hb_title_close( hb_title_t ** ); /*********************************************************************** * hb.c **********************************************************************/ int hb_get_pid( hb_handle_t * ); void hb_set_state( hb_handle_t *, hb_state_t * ); /*********************************************************************** * fifo.c **********************************************************************/ /* * Holds a packet of data that is moving through the transcoding process. * * May have metadata associated with it via extra fields * that are conditionally used depending on the type of packet. */ struct hb_buffer_settings_s { enum { AUDIO_BUF, VIDEO_BUF, SUBTITLE_BUF, FRAME_BUF, OTHER_BUF } type; int id; // ID of the track that the packet comes from int64_t start; // start time of frame double duration; // Actual duration, may be fractional ticks int64_t stop; // stop time of frame int64_t renderOffset; // DTS used by b-frame offsets in muxmp4 int64_t pcr; uint8_t discontinuity; int new_chap; // Video packets: if non-zero, is the index of the chapter whose boundary was crossed #define HB_FRAME_IDR 0x01 #define HB_FRAME_I 0x02 #define HB_FRAME_AUDIO 0x04 #define HB_FRAME_SUBTITLE 0x08 #define HB_FRAME_P 0x10 #define HB_FRAME_B 0x20 #define HB_FRAME_BREF 0x40 #define HB_FRAME_KEY 0x0F #define HB_FRAME_REF 0xF0 uint8_t frametype; uint16_t flags; }; struct hb_image_format_s { int x; int y; int width; int height; int fmt; }; struct hb_buffer_s { int size; // size of this packet int alloc; // used internally by the packet allocator (hb_buffer_init) uint8_t * data; // packet data int offset; // used internally by packet lists (hb_list_t) /* * Corresponds to the order that this packet was read from the demuxer. * * It is important that video decoder work-objects pass this value through * from their input packets to the output packets they generate. Otherwise * RENDERSUB subtitles (especially VOB subtitles) will break. * * Subtitle decoder work-objects that output a renderable subtitle * format (ex: PICTURESUB) must also be careful to pass the sequence number * through for the same reason. */ int64_t sequence; hb_buffer_settings_t s; hb_image_format_t f; struct buffer_plane { uint8_t * data; int stride; int width; int height; int height_stride; int size; } plane[4]; // 3 Color components + alpha struct qsv { void *qsv_atom; void *filter_details; } qsv_details; /* OpenCL */ struct cl_data { cl_mem buffer; cl_event last_event; enum { HOST, DEVICE } buffer_location; } cl; // libav may attach AV_PKT_DATA_PALETTE side data to some AVPackets // Store this data here when read and pass to decoder. hb_buffer_t * palette; // PICTURESUB subtitle packets: // Video packets (after processing by the hb_sync_video work-object): // A (copy of a) PICTURESUB subtitle packet that needs to be burned into // this video packet by the vobsub renderer filter // // Subtitles that are simply passed thru are NOT attached to the // associated video packets. hb_buffer_t * sub; // Packets in a list: // the next packet in the list hb_buffer_t * next; }; void hb_buffer_pool_init( void ); void hb_buffer_pool_free( void ); hb_buffer_t * hb_buffer_init( int size ); hb_buffer_t * hb_frame_buffer_init( int pix_fmt, int w, int h); void hb_buffer_init_planes( hb_buffer_t * b ); void hb_buffer_realloc( hb_buffer_t *, int size ); void hb_video_buffer_realloc( hb_buffer_t * b, int w, int h ); void hb_buffer_reduce( hb_buffer_t * b, int size ); void hb_buffer_close( hb_buffer_t ** ); hb_buffer_t * hb_buffer_dup( const hb_buffer_t * src ); int hb_buffer_copy( hb_buffer_t * dst, const hb_buffer_t * src ); void hb_buffer_swap_copy( hb_buffer_t *src, hb_buffer_t *dst ); void hb_buffer_move_subs( hb_buffer_t * dst, hb_buffer_t * src ); hb_image_t * hb_buffer_to_image(hb_buffer_t *buf); hb_fifo_t * hb_fifo_init( int capacity, int thresh ); int hb_fifo_size( hb_fifo_t * ); int hb_fifo_size_bytes( hb_fifo_t * ); int hb_fifo_is_full( hb_fifo_t * ); float hb_fifo_percent_full( hb_fifo_t * f ); hb_buffer_t * hb_fifo_get( hb_fifo_t * ); hb_buffer_t * hb_fifo_get_wait( hb_fifo_t * ); hb_buffer_t * hb_fifo_see( hb_fifo_t * ); hb_buffer_t * hb_fifo_see_wait( hb_fifo_t * ); hb_buffer_t * hb_fifo_see2( hb_fifo_t * ); void hb_fifo_push( hb_fifo_t *, hb_buffer_t * ); void hb_fifo_push_wait( hb_fifo_t *, hb_buffer_t * ); int hb_fifo_full_wait( hb_fifo_t * f ); void hb_fifo_push_head( hb_fifo_t *, hb_buffer_t * ); void hb_fifo_push_list_element( hb_fifo_t *fifo, hb_buffer_t *buffer_list ); hb_buffer_t * hb_fifo_get_list_element( hb_fifo_t *fifo ); void hb_fifo_close( hb_fifo_t ** ); void hb_fifo_flush( hb_fifo_t * f ); static inline int hb_image_stride( int pix_fmt, int width, int plane ) { int linesize = av_image_get_linesize( pix_fmt, width, plane ); // Make buffer SIMD friendly. // Decomb requires stride aligned to 32 bytes // TODO: eliminate extra buffer copies in decomb linesize = MULTIPLE_MOD_UP( linesize, 32 ); return linesize; } static inline int hb_image_width(int pix_fmt, int width, int plane) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); if (desc != NULL && (plane == 1 || plane == 2)) { // The wacky arithmatic assures rounding up. width = -((-width) >> desc->log2_chroma_w); } return width; } static inline int hb_image_height_stride(int pix_fmt, int height, int plane) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); // Decomb requires 6 extra lines and stride aligned to 32 bytes height = MULTIPLE_MOD_UP(height + 6, 32); if (desc != NULL && (plane == 1 || plane == 2)) { height = height >> desc->log2_chroma_h; } return height; } static inline int hb_image_height(int pix_fmt, int height, int plane) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); if (desc != NULL && (plane == 1 || plane == 2)) { // The wacky arithmatic assures rounding up. height = -((-height) >> desc->log2_chroma_h); } return height; } // this routine gets a buffer for an uncompressed YUV420 video frame // with dimensions width x height. static inline hb_buffer_t * hb_video_buffer_init( int width, int height ) { return hb_frame_buffer_init( AV_PIX_FMT_YUV420P, width, height ); } /*********************************************************************** * Threads: update.c, scan.c, work.c, reader.c, muxcommon.c **********************************************************************/ hb_thread_t * hb_update_init( int * build, char * version ); 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, volatile int * die, hb_error_code * error, hb_job_t ** job ); void ReadLoop( void * _w ); hb_work_object_t * hb_muxer_init( hb_job_t * ); hb_work_object_t * hb_get_work( int ); hb_work_object_t * hb_codec_decoder( int ); hb_work_object_t * hb_codec_encoder( int ); /*********************************************************************** * sync.c **********************************************************************/ hb_work_object_t * hb_sync_init( hb_job_t * job ); /*********************************************************************** * mpegdemux.c **********************************************************************/ typedef struct { int64_t last_scr; /* unadjusted SCR from most recent pack */ int64_t scr_delta; int64_t last_pts; /* last pts we saw */ int scr_changes; /* number of SCR discontinuities */ int dts_drops; /* number of drops because DTS too far from SCR */ int new_chap; } hb_psdemux_t; typedef void (*hb_muxer_t)(hb_buffer_t *, hb_list_t *, hb_psdemux_t*); void hb_demux_ps( hb_buffer_t * ps_buf, hb_list_t * es_list, hb_psdemux_t * ); void hb_demux_ts( hb_buffer_t * ps_buf, hb_list_t * es_list, hb_psdemux_t * ); void hb_demux_null( hb_buffer_t * ps_buf, hb_list_t * es_list, hb_psdemux_t * ); extern const hb_muxer_t hb_demux[]; /*********************************************************************** * batch.c **********************************************************************/ typedef struct hb_batch_s hb_batch_t; hb_batch_t * hb_batch_init( char * path ); void hb_batch_close( hb_batch_t ** _d ); int hb_batch_title_count( hb_batch_t * d ); hb_title_t * hb_batch_title_scan( hb_batch_t * d, int t ); /*********************************************************************** * dvd.c **********************************************************************/ typedef struct hb_bd_s hb_bd_t; typedef union hb_dvd_s hb_dvd_t; typedef struct hb_stream_s hb_stream_t; hb_dvd_t * hb_dvd_init( char * path ); int hb_dvd_title_count( hb_dvd_t * ); hb_title_t * hb_dvd_title_scan( hb_dvd_t *, int title, uint64_t min_duration ); int hb_dvd_start( hb_dvd_t *, hb_title_t *title, int chapter ); void hb_dvd_stop( hb_dvd_t * ); int hb_dvd_seek( hb_dvd_t *, float ); hb_buffer_t * hb_dvd_read( hb_dvd_t * ); int hb_dvd_chapter( hb_dvd_t * ); int hb_dvd_is_break( hb_dvd_t * d ); void hb_dvd_close( hb_dvd_t ** ); int hb_dvd_angle_count( hb_dvd_t * d ); void hb_dvd_set_angle( hb_dvd_t * d, int angle ); int hb_dvd_main_feature( hb_dvd_t * d, hb_list_t * list_title ); hb_bd_t * hb_bd_init( char * path ); int hb_bd_title_count( hb_bd_t * d ); hb_title_t * hb_bd_title_scan( hb_bd_t * d, int t, uint64_t min_duration ); int hb_bd_start( hb_bd_t * d, hb_title_t *title ); void hb_bd_stop( hb_bd_t * d ); int hb_bd_seek( hb_bd_t * d, float f ); int hb_bd_seek_pts( hb_bd_t * d, uint64_t pts ); int hb_bd_seek_chapter( hb_bd_t * d, int chapter ); hb_buffer_t * hb_bd_read( hb_bd_t * d ); int hb_bd_chapter( hb_bd_t * d ); void hb_bd_close( hb_bd_t ** _d ); void hb_bd_set_angle( hb_bd_t * d, int angle ); int hb_bd_main_feature( hb_bd_t * d, hb_list_t * list_title ); hb_stream_t * hb_bd_stream_open( hb_title_t *title ); void hb_ts_stream_reset(hb_stream_t *stream); hb_stream_t * hb_stream_open( char * path, hb_title_t *title, int scan ); void hb_stream_close( hb_stream_t ** ); hb_title_t * hb_stream_title_scan( hb_stream_t *, hb_title_t *); hb_buffer_t * hb_stream_read( hb_stream_t * ); int hb_stream_seek( hb_stream_t *, float ); int hb_stream_seek_ts( hb_stream_t * stream, int64_t ts ); int hb_stream_seek_chapter( hb_stream_t *, int ); int hb_stream_chapter( hb_stream_t * ); hb_buffer_t * hb_ts_decode_pkt( hb_stream_t *stream, const uint8_t * pkt ); void hb_stream_set_need_keyframe( hb_stream_t *stream, int need_keyframe ); #define STR4_TO_UINT32(p) \ ((((const uint8_t*)(p))[0] << 24) | \ (((const uint8_t*)(p))[1] << 16) | \ (((const uint8_t*)(p))[2] << 8) | \ ((const uint8_t*)(p))[3]) /*********************************************************************** * Work objects **********************************************************************/ #define HB_CONFIG_MAX_SIZE (2*8192) union hb_esconfig_u { struct { uint8_t bytes[HB_CONFIG_MAX_SIZE]; int length; } mpeg4; struct { uint8_t sps[HB_CONFIG_MAX_SIZE]; int sps_length; uint8_t pps[HB_CONFIG_MAX_SIZE]; int pps_length; int init_delay; } h264; struct { uint8_t headers[HB_CONFIG_MAX_SIZE]; int headers_length; } h265; struct { uint8_t headers[3][HB_CONFIG_MAX_SIZE]; } theora; struct { uint8_t bytes[HB_CONFIG_MAX_SIZE]; int length; } extradata; struct { uint8_t headers[3][HB_CONFIG_MAX_SIZE]; char *language; } vorbis; }; enum { WORK_NONE = 0, WORK_SYNC_VIDEO, WORK_SYNC_AUDIO, WORK_DECCC608, WORK_DECVOBSUB, WORK_DECSRTSUB, WORK_DECUTF8SUB, WORK_DECTX3GSUB, WORK_DECSSASUB, WORK_ENCVOBSUB, WORK_RENDER, WORK_ENCAVCODEC, WORK_ENCQSV, WORK_ENCX264, WORK_ENCX265, WORK_ENCTHEORA, WORK_DECAVCODEC, WORK_DECAVCODECV, WORK_DECLPCM, WORK_ENCLAME, WORK_ENCVORBIS, WORK_ENC_CA_AAC, WORK_ENC_CA_HAAC, WORK_ENCAVCODEC_AUDIO, WORK_MUX, WORK_READER, WORK_DECPGSSUB }; extern hb_filter_object_t hb_filter_detelecine; extern hb_filter_object_t hb_filter_deinterlace; extern hb_filter_object_t hb_filter_deblock; extern hb_filter_object_t hb_filter_denoise; extern hb_filter_object_t hb_filter_nlmeans; extern hb_filter_object_t hb_filter_decomb; extern hb_filter_object_t hb_filter_rotate; extern hb_filter_object_t hb_filter_crop_scale; extern hb_filter_object_t hb_filter_render_sub; extern hb_filter_object_t hb_filter_vfr; #ifdef USE_QSV extern hb_filter_object_t hb_filter_qsv; extern hb_filter_object_t hb_filter_qsv_pre; extern hb_filter_object_t hb_filter_qsv_post; #endif // Picture flags used by filters #ifndef PIC_FLAG_REPEAT_FIRST_FIELD #define PIC_FLAG_REPEAT_FIRST_FIELD 256 #endif #ifndef PIC_FLAG_TOP_FIELD_FIRST #define PIC_FLAG_TOP_FIELD_FIRST 8 #endif #ifndef PIC_FLAG_PROGRESSIVE_FRAME #define PIC_FLAG_PROGRESSIVE_FRAME 16 #endif #define PIC_FLAG_REPEAT_FRAME 512 extern hb_work_object_t * hb_objects; #define HB_WORK_IDLE 0 #define HB_WORK_OK 1 #define HB_WORK_ERROR 2 #define HB_WORK_DONE 3 /*********************************************************************** * Muxers **********************************************************************/ typedef struct hb_mux_object_s hb_mux_object_t; typedef struct hb_mux_data_s hb_mux_data_t; #define HB_MUX_COMMON \ int (*init) ( hb_mux_object_t * ); \ int (*mux) ( hb_mux_object_t *, hb_mux_data_t *, \ hb_buffer_t * ); \ int (*end) ( hb_mux_object_t * ); #define DECLARE_MUX( a ) \ hb_mux_object_t * hb_mux_##a##_init( hb_job_t * ); DECLARE_MUX( mp4 ); DECLARE_MUX( mkv ); DECLARE_MUX( avformat ); void hb_muxmp4_process_subtitle_style( uint8_t *input, uint8_t *output, uint8_t *style, uint16_t *stylesize ); void hb_deinterlace(hb_buffer_t *dst, hb_buffer_t *src); HandBrake-0.10.2/libhb/oclnv12toyuv.c0000664000175200017520000002406412463330511017662 0ustar handbrakehandbrake/* oclnv12toyuv.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html Authors: Peng Gao Li Cao */ #ifdef USE_HWD #include "opencl.h" #include "vadxva2.h" #include "oclnv12toyuv.h" /** * It creates are opencl bufs w is input frame width, h is input frame height */ static int hb_nv12toyuv_create_cl_buf( KernelEnv *kenv, int w, int h, hb_va_dxva2_t *dxva2 ); /** * It creates are opencl kernel. kernel name is nv12toyuv */ static int hb_nv12toyuv_create_cl_kernel( KernelEnv *kenv, hb_va_dxva2_t *dxva2 ); /** * It set opencl arg, input data,output data, input width, output height */ static int hb_nv12toyuv_setkernelarg( KernelEnv *kenv, int w, int h, hb_va_dxva2_t *dxva2 ); /** * It initialize nv12 to yuv kernel. */ static int hb_init_nv12toyuv_ocl( KernelEnv *kenv, int w, int h, hb_va_dxva2_t *dxva2 ); /** * Run nv12 to yuv kernel. */ static int hb_nv12toyuv( void **userdata, KernelEnv *kenv ); /** * register nv12 to yuv kernel. */ static int hb_nv12toyuv_reg_kernel( void ); /** * It creates are opencl bufs w is input frame width, h is input frame height */ static int hb_nv12toyuv_create_cl_buf( KernelEnv *kenv, int w, int h, hb_va_dxva2_t *dxva2 ) { if (hb_ocl == NULL) { hb_error("hb_nv12toyuv_create_cl_kernel: OpenCL support not available"); return 1; } cl_int status = CL_SUCCESS; int in_bytes = w*h*3/2; HB_OCL_BUF_CREATE(hb_ocl, dxva2->cl_mem_nv12, CL_MEM_READ_ONLY|CL_MEM_ALLOC_HOST_PTR, in_bytes); HB_OCL_BUF_CREATE(hb_ocl, dxva2->cl_mem_yuv, CL_MEM_READ_WRITE|CL_MEM_ALLOC_HOST_PTR, in_bytes); return 0; } /** * It creates are opencl kernel. kernel name is nv12toyuv */ static int hb_nv12toyuv_create_cl_kernel( KernelEnv *kenv, hb_va_dxva2_t *dxva2 ) { if (hb_ocl == NULL) { hb_error("hb_nv12toyuv_create_cl_kernel: OpenCL support not available"); return 1; } int ret; dxva2->nv12toyuv = hb_ocl->clCreateKernel(kenv->program, "nv12toyuv", &ret); return ret; } /** * It set opencl arg, input data,output data, input width, output height */ static int hb_nv12toyuv_setkernelarg( KernelEnv *kenv, int w, int h, hb_va_dxva2_t *dxva2 ) { int arg = 0, status; kenv->kernel = dxva2->nv12toyuv; if (hb_ocl == NULL) { hb_error("hb_nv12toyuv_setkernelarg: OpenCL support not available"); return 1; } HB_OCL_CHECK(hb_ocl->clSetKernelArg, kenv->kernel, arg++, sizeof(cl_mem), &dxva2->cl_mem_nv12); HB_OCL_CHECK(hb_ocl->clSetKernelArg, kenv->kernel, arg++, sizeof(cl_mem), &dxva2->cl_mem_yuv); HB_OCL_CHECK(hb_ocl->clSetKernelArg, kenv->kernel, arg++, sizeof(int), &w); HB_OCL_CHECK(hb_ocl->clSetKernelArg, kenv->kernel, arg++, sizeof(int), &h); return 0; } /** * It initialize nv12 to yuv kernel. */ static int hb_init_nv12toyuv_ocl( KernelEnv *kenv, int w, int h, hb_va_dxva2_t *dxva2 ) { if( !dxva2->nv12toyuv ) { if( hb_nv12toyuv_create_cl_buf( kenv, w, h, dxva2 ) ) { hb_log( "OpenCL: nv12toyuv_create_cl_buf fail" ); return -1; } if (!dxva2->nv12toyuv_tmp_in) { dxva2->nv12toyuv_tmp_in = malloc (w*h*3/2); } if (!dxva2->nv12toyuv_tmp_out) { dxva2->nv12toyuv_tmp_out = malloc (w*h*3/2); } hb_nv12toyuv_create_cl_kernel( kenv, dxva2 ); } return 0; } /** * copy_plane * @param dst - * @param src - * @param dstride - * @param sstride - * @param h - */ static uint8_t *copy_plane( uint8_t *dst, uint8_t* src, int dstride, int sstride, int h ) { if ( dstride == sstride ) { memcpy( dst, src, dstride * h ); return dst + dstride * h; } int lbytes = dstride <= sstride? dstride : sstride; while ( --h >= 0 ) { memcpy( dst, src, lbytes ); src += sstride; dst += dstride; } return dst; } /** * Run nv12 to yuv kernel. */ static int hb_nv12toyuv( void **userdata, KernelEnv *kenv ) { int status; int w = (int)userdata[0]; int h = (int)userdata[1]; uint8_t *bufi1 = userdata[2]; int *crop = userdata[3]; hb_va_dxva2_t *dxva2 = userdata[4]; uint8_t *bufi2 = userdata[5]; int p = (int)userdata[6]; int decomb = (int)userdata[7]; int detelecine = (int)userdata[8]; int i; if( hb_init_nv12toyuv_ocl( kenv, w, h, dxva2 ) ) { return -1; } if( hb_nv12toyuv_setkernelarg( kenv, w, h, dxva2 ) ) { return -1; } if (hb_ocl == NULL) { hb_error("hb_nv12toyuv: OpenCL support not available"); return -1; } int in_bytes = w*h*3/2; if( kenv->isAMD ) { void *data = hb_ocl->clEnqueueMapBuffer(kenv->command_queue, dxva2->cl_mem_nv12, CL_MAP_WRITE_INVALIDATE_REGION, CL_TRUE, 0, in_bytes, 0, NULL, NULL, NULL); for ( i = 0; i < dxva2->height; i++ ) { memcpy( data + i * dxva2->width, bufi1 + i * p, dxva2->width ); if ( i < dxva2->height >> 1 ) { memcpy( data + ( dxva2->width * dxva2->height ) + i * dxva2->width, bufi2 + i * p, dxva2->width ); } } hb_ocl->clEnqueueUnmapMemObject(kenv->command_queue, dxva2->cl_mem_nv12, data, 0, NULL, NULL); } else { uint8_t *tmp = (uint8_t*)malloc( dxva2->width * dxva2->height * 3 / 2 ); for( i = 0; i < dxva2->height; i++ ) { memcpy( tmp + i * dxva2->width, bufi1 + i * p, dxva2->width ); if( i < dxva2->height >> 1 ) { memcpy( tmp + (dxva2->width * dxva2->height) + i * dxva2->width, bufi2 + i * p, dxva2->width ); } } HB_OCL_CHECK(hb_ocl->clEnqueueWriteBuffer, kenv->command_queue, dxva2->cl_mem_nv12, CL_TRUE, 0, in_bytes, tmp, 0, NULL, NULL); free( tmp ); } size_t gdim[2] = {w>>1, h>>1}; HB_OCL_CHECK(hb_ocl->clEnqueueNDRangeKernel, kenv->command_queue, kenv->kernel, 2, NULL, gdim, NULL, 0, NULL, NULL ); if( (crop[0] || crop[1] || crop[2] || crop[3]) && (decomb == 0) && (detelecine == 0) ) { AVPicture pic_in; AVPicture pic_crop; hb_ocl->clEnqueueReadBuffer(kenv->command_queue, dxva2->cl_mem_yuv, CL_TRUE, 0, in_bytes, dxva2->nv12toyuv_tmp_out, 0, NULL, NULL); hb_buffer_t *in = hb_video_buffer_init( w, h ); int wmp = in->plane[0].stride; int hmp = in->plane[0].height; copy_plane( in->plane[0].data, dxva2->nv12toyuv_tmp_out, wmp, w, hmp ); wmp = in->plane[1].stride; hmp = in->plane[1].height; copy_plane( in->plane[1].data, dxva2->nv12toyuv_tmp_out + w * h, wmp, w>>1, hmp ); wmp = in->plane[2].stride; hmp = in->plane[2].height; copy_plane( in->plane[2].data, dxva2->nv12toyuv_tmp_out + w * h +( ( w * h )>>2 ), wmp, w>>1, hmp ); hb_avpicture_fill( &pic_in, in ); av_picture_crop( &pic_crop, &pic_in, in->f.fmt, crop[0], crop[2] ); int i, ww = w - ( crop[2] + crop[3] ), hh = h - ( crop[0] + crop[1] ); for( i = 0; i< hh >> 1; i++ ) { memcpy( dxva2->nv12toyuv_tmp_in + ( ( i << 1 ) + 0 ) * ww, pic_crop.data[0]+ ( ( i << 1 ) + 0 ) * pic_crop.linesize[0], ww ); memcpy( dxva2->nv12toyuv_tmp_in + ( ( i << 1 ) + 1 ) * ww, pic_crop.data[0]+ ( ( i << 1 ) + 1 ) * pic_crop.linesize[0], ww ); memcpy( dxva2->nv12toyuv_tmp_in + ( ww * hh ) + i * ( ww >> 1 ), pic_crop.data[1] + i * pic_crop.linesize[1], ww >> 1 ); memcpy( dxva2->nv12toyuv_tmp_in + ( ww * hh ) + ( ( ww * hh )>>2 ) + i * ( ww >> 1 ), pic_crop.data[2] + i * pic_crop.linesize[2], ww >> 1 ); } if( kenv->isAMD ) { void *data = hb_ocl->clEnqueueMapBuffer(kenv->command_queue, dxva2->cl_mem_yuv, CL_MAP_WRITE_INVALIDATE_REGION, CL_TRUE, 0, ww * hh * 3 / 2, 0, NULL, NULL, NULL); memcpy( data, dxva2->nv12toyuv_tmp_in, ww * hh * 3 / 2 ); hb_ocl->clEnqueueUnmapMemObject(kenv->command_queue, dxva2->cl_mem_yuv, data, 0, NULL, NULL); } else { HB_OCL_CHECK(hb_ocl->clEnqueueWriteBuffer, kenv->command_queue, dxva2->cl_mem_yuv, CL_TRUE, 0, in_bytes, dxva2->nv12toyuv_tmp_in, 0, NULL, NULL); } hb_buffer_close( &in ); } return 0; } /** * register nv12 to yuv kernel. */ static int hb_nv12toyuv_reg_kernel( void ) { int st = hb_register_kernel_wrapper( "nv12toyuv", hb_nv12toyuv ); if( !st ) { hb_log( "OpenCL: register kernel[%s] failed", "nv12toyuv" ); return -1; } return 0; } /** * nv12 to yuv interface * bufi is input frame of nv12, w is input frame width, h is input frame height */ int hb_ocl_nv12toyuv( uint8_t *bufi[], int p, int w, int h, int *crop, hb_va_dxva2_t *dxva2, int decomb, int detelecine ) { void *userdata[9]; userdata[0] = (void*)w; userdata[1] = (void*)h; userdata[2] = bufi[0]; userdata[3] = crop; userdata[4] = dxva2; userdata[5] = bufi[1]; userdata[6] = (void*)p; userdata[7] = decomb; userdata[8] = detelecine; if( hb_nv12toyuv_reg_kernel() ) { return -1; } if( hb_run_kernel( "nv12toyuv", userdata ) ) { hb_log( "OpenCL: run kernel[nv12toyuv] failed" ); return -1; } return 0; } #endif // USE_HWD HandBrake-0.10.2/libhb/lang.c0000664000175200017520000002151112463330511016202 0ustar handbrakehandbrake/* lang.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "lang.h" #include #include static const iso639_lang_t languages[] = { { "Unknown", "", "", "und" }, { "Afar", "", "aa", "aar" }, { "Abkhazian", "", "ab", "abk" }, { "Afrikaans", "", "af", "afr" }, { "Akan", "", "ak", "aka" }, { "Albanian", "", "sq", "sqi", "alb" }, { "Amharic", "", "am", "amh" }, { "Arabic", "", "ar", "ara" }, { "Aragonese", "", "an", "arg" }, { "Armenian", "", "hy", "hye", "arm" }, { "Assamese", "", "as", "asm" }, { "Avaric", "", "av", "ava" }, { "Avestan", "", "ae", "ave" }, { "Aymara", "", "ay", "aym" }, { "Azerbaijani", "", "az", "aze" }, { "Bashkir", "", "ba", "bak" }, { "Bambara", "", "bm", "bam" }, { "Basque", "", "eu", "eus", "baq" }, { "Belarusian", "", "be", "bel" }, { "Bengali", "", "bn", "ben" }, { "Bihari", "", "bh", "bih" }, { "Bislama", "", "bi", "bis" }, { "Bosnian", "", "bs", "bos" }, { "Breton", "", "br", "bre" }, { "Bulgarian", "", "bg", "bul" }, { "Burmese", "", "my", "mya", "bur" }, { "Catalan", "", "ca", "cat" }, { "Chamorro", "", "ch", "cha" }, { "Chechen", "", "ce", "che" }, { "Chinese", "", "zh", "zho", "chi" }, { "Church Slavic", "", "cu", "chu" }, { "Chuvash", "", "cv", "chv" }, { "Cornish", "", "kw", "cor" }, { "Corsican", "", "co", "cos" }, { "Cree", "", "cr", "cre" }, { "Czech", "", "cs", "ces", "cze" }, { "Danish", "Dansk", "da", "dan" }, { "Divehi", "", "dv", "div" }, { "Dutch", "Nederlands", "nl", "nld", "dut" }, { "Dzongkha", "", "dz", "dzo" }, { "English", "English", "en", "eng" }, { "Esperanto", "", "eo", "epo" }, { "Estonian", "", "et", "est" }, { "Ewe", "", "ee", "ewe" }, { "Faroese", "", "fo", "fao" }, { "Fijian", "", "fj", "fij" }, { "Finnish", "Suomi", "fi", "fin" }, { "French", "Francais", "fr", "fra", "fre" }, { "Western Frisian", "", "fy", "fry" }, { "Fulah", "", "ff", "ful" }, { "Georgian", "", "ka", "kat", "geo" }, { "German", "Deutsch", "de", "deu", "ger" }, { "Gaelic (Scots)", "", "gd", "gla" }, { "Irish", "", "ga", "gle" }, { "Galician", "", "gl", "glg" }, { "Manx", "", "gv", "glv" }, { "Greek, Modern", "", "el", "ell", "gre" }, { "Guarani", "", "gn", "grn" }, { "Gujarati", "", "gu", "guj" }, { "Haitian", "", "ht", "hat" }, { "Hausa", "", "ha", "hau" }, { "Hebrew", "", "he", "heb" }, { "Herero", "", "hz", "her" }, { "Hindi", "", "hi", "hin" }, { "Hiri Motu", "", "ho", "hmo" }, { "Hungarian", "Magyar", "hu", "hun" }, { "Igbo", "", "ig", "ibo" }, { "Icelandic", "Islenska", "is", "isl", "ice" }, { "Ido", "", "io", "ido" }, { "Sichuan Yi", "", "ii", "iii" }, { "Inuktitut", "", "iu", "iku" }, { "Interlingue", "", "ie", "ile" }, { "Interlingua", "", "ia", "ina" }, { "Indonesian", "", "id", "ind" }, { "Inupiaq", "", "ik", "ipk" }, { "Italian", "Italiano", "it", "ita" }, { "Javanese", "", "jv", "jav" }, { "Japanese", "", "ja", "jpn" }, { "Kalaallisut (Greenlandic)", "", "kl", "kal" }, { "Kannada", "", "kn", "kan" }, { "Kashmiri", "", "ks", "kas" }, { "Kanuri", "", "kr", "kau" }, { "Kazakh", "", "kk", "kaz" }, { "Central Khmer", "", "km", "khm" }, { "Kikuyu", "", "ki", "kik" }, { "Kinyarwanda", "", "rw", "kin" }, { "Kirghiz", "", "ky", "kir" }, { "Komi", "", "kv", "kom" }, { "Kongo", "", "kg", "kon" }, { "Korean", "", "ko", "kor" }, { "Kuanyama", "", "kj", "kua" }, { "Kurdish", "", "ku", "kur" }, { "Lao", "", "lo", "lao" }, { "Latin", "", "la", "lat" }, { "Latvian", "", "lv", "lav" }, { "Limburgan", "", "li", "lim" }, { "Lingala", "", "ln", "lin" }, { "Lithuanian", "", "lt", "lit" }, { "Luxembourgish", "", "lb", "ltz" }, { "Luba-Katanga", "", "lu", "lub" }, { "Ganda", "", "lg", "lug" }, { "Macedonian", "", "mk", "mkd", "mac" }, { "Marshallese", "", "mh", "mah" }, { "Malayalam", "", "ml", "mal" }, { "Maori", "", "mi", "mri", "mao" }, { "Marathi", "", "mr", "mar" }, { "Malay", "", "ms", "msa", "msa" }, { "Malagasy", "", "mg", "mlg" }, { "Maltese", "", "mt", "mlt" }, { "Moldavian", "", "mo", "mol" }, { "Mongolian", "", "mn", "mon" }, { "Nauru", "", "na", "nau" }, { "Navajo", "", "nv", "nav" }, { "Ndebele, South", "", "nr", "nbl" }, { "Ndebele, North", "", "nd", "nde" }, { "Ndonga", "", "ng", "ndo" }, { "Nepali", "", "ne", "nep" }, { "Norwegian Nynorsk", "", "nn", "nno" }, { "Norwegian Bokmål", "", "nb", "nob" }, { "Norwegian", "Norsk", "no", "nor" }, { "Chichewa; Nyanja", "", "ny", "nya" }, { "Occitan (post 1500); Provençal", "", "oc", "oci" }, { "Ojibwa", "", "oj", "oji" }, { "Oriya", "", "or", "ori" }, { "Oromo", "", "om", "orm" }, { "Ossetian; Ossetic", "", "os", "oss" }, { "Panjabi", "", "pa", "pan" }, { "Persian", "", "fa", "fas", "per" }, { "Pali", "", "pi", "pli" }, { "Polish", "", "pl", "pol" }, { "Portuguese", "Portugues", "pt", "por" }, { "Pushto", "", "ps", "pus" }, { "Quechua", "", "qu", "que" }, { "Romansh", "", "rm", "roh" }, { "Romanian", "", "ro", "ron", "rum" }, { "Rundi", "", "rn", "run" }, { "Russian", "", "ru", "rus" }, { "Sango", "", "sg", "sag" }, { "Sanskrit", "", "sa", "san" }, { "Serbian", "", "sr", "srp", "scc" }, { "Croatian", "Hrvatski", "hr", "hrv", "scr" }, { "Sinhala", "", "si", "sin" }, { "Slovak", "", "sk", "slk", "slo" }, { "Slovenian", "", "sl", "slv" }, { "Northern Sami", "", "se", "sme" }, { "Samoan", "", "sm", "smo" }, { "Shona", "", "sn", "sna" }, { "Sindhi", "", "sd", "snd" }, { "Somali", "", "so", "som" }, { "Sotho, Southern", "", "st", "sot" }, { "Spanish", "Espanol", "es", "spa" }, { "Sardinian", "", "sc", "srd" }, { "Swati", "", "ss", "ssw" }, { "Sundanese", "", "su", "sun" }, { "Swahili", "", "sw", "swa" }, { "Swedish", "Svenska", "sv", "swe" }, { "Tahitian", "", "ty", "tah" }, { "Tamil", "", "ta", "tam" }, { "Tatar", "", "tt", "tat" }, { "Telugu", "", "te", "tel" }, { "Tajik", "", "tg", "tgk" }, { "Tagalog", "", "tl", "tgl" }, { "Thai", "", "th", "tha" }, { "Tibetan", "", "bo", "bod", "tib" }, { "Tigrinya", "", "ti", "tir" }, { "Tonga (Tonga Islands)", "", "to", "ton" }, { "Tswana", "", "tn", "tsn" }, { "Tsonga", "", "ts", "tso" }, { "Turkmen", "", "tk", "tuk" }, { "Turkish", "", "tr", "tur" }, { "Twi", "", "tw", "twi" }, { "Uighur", "", "ug", "uig" }, { "Ukrainian", "", "uk", "ukr" }, { "Urdu", "", "ur", "urd" }, { "Uzbek", "", "uz", "uzb" }, { "Venda", "", "ve", "ven" }, { "Vietnamese", "", "vi", "vie" }, { "Volapük", "", "vo", "vol" }, { "Welsh", "", "cy", "cym", "wel" }, { "Walloon", "", "wa", "wln" }, { "Wolof", "", "wo", "wol" }, { "Xhosa", "", "xh", "xho" }, { "Yiddish", "", "yi", "yid" }, { "Yoruba", "", "yo", "yor" }, { "Zhuang", "", "za", "zha" }, { "Zulu", "", "zu", "zul" }, { NULL, NULL, NULL } }; static const int lang_count = sizeof(languages) / sizeof(languages[0]); iso639_lang_t * lang_for_code( int code ) { char code_string[2]; iso639_lang_t * lang; code_string[0] = tolower( ( code >> 8 ) & 0xFF ); code_string[1] = tolower( code & 0xFF ); for( lang = (iso639_lang_t*) languages; lang->eng_name; lang++ ) { if( !strncmp( lang->iso639_1, code_string, 2 ) ) { return lang; } } return (iso639_lang_t*) languages; } iso639_lang_t * lang_for_code2( const char *code ) { char code_string[4]; iso639_lang_t * lang; code_string[0] = tolower( code[0] ); code_string[1] = tolower( code[1] ); code_string[2] = tolower( code[2] ); code_string[3] = 0; for( lang = (iso639_lang_t*) languages; lang->eng_name; lang++ ) { if( !strcmp( lang->iso639_2, code_string ) ) { return lang; } if( lang->iso639_2b && !strcmp( lang->iso639_2b, code_string ) ) { return lang; } } return (iso639_lang_t*) languages; } int lang_to_code(const iso639_lang_t *lang) { int code = 0; if (lang) code = (lang->iso639_1[0] << 8) | lang->iso639_1[1]; return code; } iso639_lang_t * lang_for_english( const char * english ) { iso639_lang_t * lang; for( lang = (iso639_lang_t*) languages; lang->eng_name; lang++ ) { if( !strcmp( lang->eng_name, english ) ) { return lang; } } return (iso639_lang_t*) languages; } const iso639_lang_t* lang_get_next(const iso639_lang_t *last) { if (last == NULL) { return (const iso639_lang_t*)languages; } if (last < languages || // out of bounds last >= languages + lang_count - 2) // last valid language { return NULL; } return ++last; } HandBrake-0.10.2/libhb/encx264.h0000664000175200017520000000602112463330511016456 0ustar handbrakehandbrake/* encx264.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "x264.h" #include "h264_common.h" /* x264 preferred option names (left) and synonyms (right). * The "preferred" names match names used in x264's param2string function more * closely than their corresponding synonyms, or are just shorter. */ static const char * const hb_x264_encopt_synonyms[][2] = { { "deterministic", "n-deterministic", }, { "level", "level-idc", }, { "ref", "frameref", }, { "keyint-min", "min-keyint", }, { "no-deblock", "nf", }, { "deblock", "filter", }, { "cqm", "cqmfile", }, { "analyse", "partitions", }, { "weightb", "weight-b", }, { "direct", "direct-pred", }, { "merange", "me-range", }, { "mvrange", "mv-range", }, { "mvrange-thread", "mv-range-thread", }, { "subme", "subq", }, { "qp", "qp_constant", }, { "qpmin", "qp-min", }, { "qpmax", "qp-max", }, { "qpstep", "qp-step", }, { "ipratio", "ip-factor", }, { "pbratio", "pb-factor", }, { "cplxblur", "cplx-blur", }, { NULL, NULL, }, }; /* * Check whether a valid h264_level is compatible with the given framerate, * resolution and interlaced compression/flags combination. * * width, height, fps_num and fps_den should be greater than zero. * * interlacing parameters can be set to zero when the information is * unavailable, as hb_apply_h264_level() will disable interlacing if necessary. * * Returns 0 if the level is valid and compatible, 1 otherwise. */ int hb_check_h264_level(const char *h264_level, int width, int height, int fps_num, int fps_den, int interlaced, int fake_interlaced); /* * Applies the restrictions of the requested H.264 level to an x264_param_t. * * Returns -1 if an invalid level (or no level) is specified. GUIs should be * capable of always providing a valid level. * * Does not modify resolution/framerate but warns when they exceed level limits. * * Based on a x264_param_apply_level() draft and other x264 code. */ int hb_apply_h264_level(x264_param_t *param, const char *h264_level, const char *x264_profile, int verbose); /* * Applies the restrictions of the requested H.264 profile to an x264_param_t. * * x264_param_apply_profile wrapper designed to always succeed when a valid * H.264 profile is specified (unlike x264's function). */ int hb_apply_h264_profile(x264_param_t *param, const char *h264_profile, int verbose); HandBrake-0.10.2/libhb/decavcodec.c0000664000175200017520000022657412464263311017365 0ustar handbrakehandbrake/* decavcodec.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ /* This module is Handbrake's interface to the ffmpeg decoder library (libavcodec & small parts of libavformat). It contains four Handbrake "work objects": decavcodeca connects HB to an ffmpeg audio decoder decavcodecv connects HB to an ffmpeg video decoder (Two different routines are needed because the ffmpeg library has different decoder calling conventions for audio & video. These work objects are self-contained & follow all of HB's conventions for a decoder module. They can be used like any other HB decoder These decoders handle 2 kinds of input. Streams that are demuxed by HandBrake and streams that are demuxed by libavformat. In the case of streams that are demuxed by HandBrake, there is an extra parse step required that happens in decodeVideo and decavcodecaWork. In the case of streams that are demuxed by libavformat, there is context information that we need from the libavformat. This information is propagated from hb_stream_open to these decoders through title->opaque_priv. A consequence of the above is that the streams that are demuxed by HandBrake *can't* use information from the AVStream because there isn't one - they get their data from either the dvd reader or the mpeg reader, not the ffmpeg stream reader. That means that they have to make up for deficiencies in the AVCodecContext info by using stuff kept in the HB "title" struct. It also means that ffmpeg codecs that randomly scatter state needed by the decoder across both the AVCodecContext & the AVStream (e.g., the VC1 decoder) can't easily be used by the HB mpeg stream reader. */ #include "hb.h" #include "hbffmpeg.h" #include "audio_resample.h" #ifdef USE_HWD #include "opencl.h" #include "vadxva2.h" #endif #ifdef USE_QSV #include "qsv_common.h" #endif static void compute_frame_duration( hb_work_private_t *pv ); static void flushDelayQueue( hb_work_private_t *pv ); static int decavcodecaInit( hb_work_object_t *, hb_job_t * ); static int decavcodecaWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); static void decavcodecClose( hb_work_object_t * ); static int decavcodecaInfo( hb_work_object_t *, hb_work_info_t * ); static int decavcodecaBSInfo( hb_work_object_t *, const hb_buffer_t *, hb_work_info_t * ); hb_work_object_t hb_decavcodeca = { .id = WORK_DECAVCODEC, .name = "Audio decoder (libavcodec)", .init = decavcodecaInit, .work = decavcodecaWork, .close = decavcodecClose, .info = decavcodecaInfo, .bsinfo = decavcodecaBSInfo }; #define HEAP_SIZE 8 typedef struct { // there are nheap items on the heap indexed 1..nheap (i.e., top of // heap is 1). The 0th slot is unused - a marker is put there to check // for overwrite errs. int64_t h[HEAP_SIZE+1]; int nheap; } pts_heap_t; struct hb_work_private_s { hb_job_t *job; hb_title_t *title; AVCodecContext *context; AVCodecParserContext *parser; AVFrame *frame; hb_buffer_t *palette; int threads; int video_codec_opened; hb_list_t *list; double duration; // frame duration (for video) double field_duration; // field duration (for video) int frame_duration_set; // Indicates valid timing was found in stream double pts_next; // next pts we expect to generate int64_t chap_time; // time of next chap mark (if new_chap != 0) int new_chap; // output chapter mark pending uint32_t nframes; uint32_t ndrops; uint32_t decode_errors; int64_t prev_pts; int brokenTS; // video stream may contain packed b-frames hb_buffer_t* delayq[HEAP_SIZE]; int queue_primed; pts_heap_t pts_heap; void* buffer; struct SwsContext *sws_context; // if we have to rescale or convert color space int sws_width; int sws_height; int sws_pix_fmt; int cadence[12]; int wait_for_keyframe; #ifdef USE_HWD hb_va_dxva2_t *dxva2; uint8_t *dst_frame; hb_oclscale_t *opencl_scale; #endif hb_audio_resample_t *resample; #ifdef USE_QSV // QSV-specific settings struct { int decode; av_qsv_config config; const char *codec_name; #define USE_QSV_PTS_WORKAROUND // work around out-of-order output timestamps #ifdef USE_QSV_PTS_WORKAROUND hb_list_t *pts_list; #endif } qsv; #endif hb_list_t * list_subtitle; }; #ifdef USE_QSV_PTS_WORKAROUND // save/restore PTS if the decoder may not attach the right PTS to the frame static void hb_av_add_new_pts(hb_list_t *list, int64_t new_pts) { int index = 0; int64_t *cur_item, *new_item; if (list != NULL && new_pts != AV_NOPTS_VALUE) { new_item = malloc(sizeof(int64_t)); if (new_item != NULL) { *new_item = new_pts; // sort chronologically for (index = 0; index < hb_list_count(list); index++) { cur_item = hb_list_item(list, index); if (cur_item != NULL) { if (*cur_item == *new_item) { // no duplicates free(new_item); return; } if (*cur_item > *new_item) { // insert here break; } } } hb_list_insert(list, index, new_item); } } } static int64_t hb_av_pop_next_pts(hb_list_t *list) { int64_t *item, next_pts = AV_NOPTS_VALUE; if (list != NULL && hb_list_count(list) > 0) { item = hb_list_item(list, 0); if (item != NULL) { next_pts = *item; hb_list_rem(list, item); free(item); } } return next_pts; } #endif static void decodeAudio( hb_audio_t * audio, hb_work_private_t *pv, uint8_t *data, int size, int64_t pts ); static hb_buffer_t *link_buf_list( hb_work_private_t *pv ); static int64_t heap_pop( pts_heap_t *heap ) { int64_t result; if ( heap->nheap <= 0 ) { return AV_NOPTS_VALUE; } // return the top of the heap then put the bottom element on top, // decrease the heap size by one & rebalence the heap. result = heap->h[1]; int64_t v = heap->h[heap->nheap--]; int parent = 1; int child = parent << 1; while ( child <= heap->nheap ) { // find the smallest of the two children of parent if (child < heap->nheap && heap->h[child] > heap->h[child+1] ) ++child; if (v <= heap->h[child]) // new item is smaller than either child so it's the new parent. break; // smallest child is smaller than new item so move it up then // check its children. int64_t hp = heap->h[child]; heap->h[parent] = hp; parent = child; child = parent << 1; } heap->h[parent] = v; return result; } static void heap_push( pts_heap_t *heap, int64_t v ) { if ( heap->nheap < HEAP_SIZE ) { ++heap->nheap; } // stick the new value on the bottom of the heap then bubble it // up to its correct spot. int child = heap->nheap; while (child > 1) { int parent = child >> 1; if (heap->h[parent] <= v) break; // move parent down int64_t hp = heap->h[parent]; heap->h[child] = hp; child = parent; } heap->h[child] = v; } /*********************************************************************** * hb_work_decavcodec_init *********************************************************************** * **********************************************************************/ static int decavcodecaInit( hb_work_object_t * w, hb_job_t * job ) { AVCodec * codec; hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; pv->job = job; if (job) pv->title = job->title; else 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); // libav's eac3 parser toggles the codec_id in the context as // it reads eac3 data between AV_CODEC_ID_AC3 and AV_CODEC_ID_EAC3. // It detects an AC3 sync pattern sometimes in ac3_sync() which // causes it to eventually set avctx->codec_id to AV_CODEC_ID_AC3 // in ff_aac_ac3_parse(). Since we are parsing some data before // we get here, the codec_id may have flipped. This will cause an // error in hb_avcodec_open(). So flip it back! pv->context->codec_id = w->codec_param; } 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)) { pv->resample = hb_audio_resample_init(AV_SAMPLE_FMT_FLT, w->audio->config.out.mixdown, w->audio->config.out.normalize_mix_level); if (pv->resample == NULL) { hb_error("decavcodecaInit: hb_audio_resample_init() failed"); return 1; } /* * Some audio decoders can downmix using embedded coefficients, * or dedicated audio substreams for a specific channel layout. * * But some will e.g. use normalized mix coefficients unconditionally, * so we need to make sure this matches what the user actually requested. */ int avcodec_downmix = 0; switch (w->codec_param) { case AV_CODEC_ID_AC3: case AV_CODEC_ID_EAC3: avcodec_downmix = w->audio->config.out.normalize_mix_level != 0; break; case AV_CODEC_ID_DTS: avcodec_downmix = w->audio->config.out.normalize_mix_level == 0; break; case AV_CODEC_ID_TRUEHD: avcodec_downmix = (w->audio->config.out.normalize_mix_level == 0 || w->audio->config.out.mixdown == HB_AMIXDOWN_MONO || w->audio->config.out.mixdown == HB_AMIXDOWN_DOLBY || w->audio->config.out.mixdown == HB_AMIXDOWN_DOLBYPLII); break; default: break; } if (avcodec_downmix) { 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_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; } } } // Set decoder opts... AVDictionary * av_opts = NULL; av_dict_set( &av_opts, "refcounted_frames", "1", 0 ); // Dynamic Range Compression if (w->audio->config.out.dynamic_range_compression >= 0.0f && hb_audio_can_apply_drc(w->audio->config.in.codec, w->audio->config.in.codec_param, 0)) { float drc_scale_max = 1.0f; /* * avcodec_open will fail if the value for any of the options is out of * range, so assume a conservative maximum of 1 and try to determine the * option's actual upper limit. */ if (codec != NULL && codec->priv_class != NULL) { const AVOption *opt; opt = av_opt_find2((void*)&codec->priv_class, "drc_scale", NULL, AV_OPT_FLAG_DECODING_PARAM|AV_OPT_FLAG_AUDIO_PARAM, AV_OPT_SEARCH_FAKE_OBJ, NULL); if (opt != NULL) { drc_scale_max = opt->max; } } if (w->audio->config.out.dynamic_range_compression > drc_scale_max) { hb_log("decavcodecaInit: track %d, sanitizing out-of-range DRC %.2f to %.2f", w->audio->config.out.track, w->audio->config.out.dynamic_range_compression, drc_scale_max); w->audio->config.out.dynamic_range_compression = drc_scale_max; } char drc_scale[5]; // "?.??\n" snprintf(drc_scale, sizeof(drc_scale), "%.2f", w->audio->config.out.dynamic_range_compression); av_dict_set(&av_opts, "drc_scale", drc_scale, 0); } if (hb_avcodec_open(pv->context, codec, &av_opts, 0)) { av_dict_free( &av_opts ); hb_log("decavcodecaInit: avcodec_open failed"); return 1; } // avcodec_open populates av_opts with the things it didn't recognize. AVDictionaryEntry *t = NULL; while ((t = av_dict_get(av_opts, "", t, AV_DICT_IGNORE_SUFFIX)) != NULL) { hb_log("decavcodecaInit: unknown option '%s'", t->key); } av_dict_free( &av_opts ); pv->frame = av_frame_alloc(); if (pv->frame == NULL) { hb_log("decavcodecaInit: av_frame_alloc failed"); return 1; } return 0; } /*********************************************************************** * Close *********************************************************************** * **********************************************************************/ static void closePrivData( hb_work_private_t ** ppv ) { hb_work_private_t * pv = *ppv; if ( pv ) { flushDelayQueue( pv ); hb_buffer_t *buf = link_buf_list( pv ); hb_buffer_close( &buf ); if ( pv->job && pv->context && pv->context->codec ) { hb_log( "%s-decoder done: %u frames, %u decoder errors, %u drops", pv->context->codec->name, pv->nframes, pv->decode_errors, pv->ndrops ); } av_frame_free(&pv->frame); if ( pv->sws_context ) { sws_freeContext( pv->sws_context ); } if ( pv->parser ) { av_parser_close(pv->parser); } if ( pv->context && pv->context->codec ) { #ifdef USE_QSV /* * FIXME: knowingly leaked. * * If we're using our Libav QSV wrapper, qsv_decode_end() will call * MFXClose() on the QSV session. Even if decoding is complete, we * still need that session for QSV filtering and/or encoding, so we * we can't close the context here until we implement a proper fix. */ if (!pv->qsv.decode) #endif { hb_avcodec_close(pv->context); } } if ( pv->context ) { av_freep( &pv->context->extradata ); av_freep( &pv->context ); } if ( pv->list ) { hb_list_empty( &pv->list ); } hb_audio_resample_free(pv->resample); #ifdef USE_HWD if (pv->opencl_scale != NULL) { free(pv->opencl_scale); } if (pv->dxva2 != NULL) { if (hb_ocl != NULL) { HB_OCL_BUF_FREE(hb_ocl, pv->dxva2->cl_mem_nv12); } hb_va_close(pv->dxva2); } #endif #ifdef USE_QSV_PTS_WORKAROUND if (pv->qsv.decode && pv->qsv.pts_list != NULL) { while (hb_list_count(pv->qsv.pts_list) > 0) { int64_t *item = hb_list_item(pv->qsv.pts_list, 0); hb_list_rem(pv->qsv.pts_list, item); free(item); } hb_list_close(&pv->qsv.pts_list); } #endif free(pv); } *ppv = NULL; } static void decavcodecClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; #ifdef USE_HWD if( pv->dst_frame ) free( pv->dst_frame ); #endif if ( pv ) { closePrivData( &pv ); w->private_data = NULL; } } /*********************************************************************** * Work *********************************************************************** * **********************************************************************/ static int decavcodecaWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_buffer_t * in = *buf_in; if ( in->size <= 0 ) { /* EOF on input stream - send it downstream & say that we're done */ *buf_out = in; *buf_in = NULL; return HB_WORK_DONE; } *buf_out = NULL; if ( in->s.start < 0 && pv->pts_next <= 0 ) { // discard buffers that start before video time 0 return HB_WORK_OK; } int pos, len; for ( pos = 0; pos < in->size; pos += len ) { uint8_t *pout; int pout_len; int64_t cur; cur = in->s.start; if ( pv->parser != NULL ) { len = av_parser_parse2( pv->parser, pv->context, &pout, &pout_len, in->data + pos, in->size - pos, cur, cur, 0 ); cur = pv->parser->pts; } else { pout = in->data; len = pout_len = in->size; } if (pout != NULL && pout_len > 0) { decodeAudio( w->audio, pv, pout, pout_len, cur ); } } *buf_out = link_buf_list( pv ); return HB_WORK_OK; } static int decavcodecaInfo( hb_work_object_t *w, hb_work_info_t *info ) { hb_work_private_t *pv = w->private_data; memset( info, 0, sizeof(*info) ); if ( pv && pv->context ) { AVCodecContext *context = pv->context; info->bitrate = context->bit_rate; info->rate = context->time_base.num; info->rate_base = context->time_base.den; info->profile = context->profile; info->level = context->level; return 1; } return 0; } static int decavcodecaBSInfo( hb_work_object_t *w, const hb_buffer_t *buf, hb_work_info_t *info ) { hb_work_private_t *pv = w->private_data; int ret = 0; hb_audio_t *audio = w->audio; memset( info, 0, sizeof(*info) ); if ( pv && pv->context ) { return decavcodecaInfo( w, info ); } AVCodec *codec = avcodec_find_decoder( w->codec_param ); if ( ! codec ) { // there's no ffmpeg codec for this audio type - give up return -1; } static char codec_name[64]; info->name = strncpy( codec_name, codec->name, sizeof(codec_name)-1 ); AVCodecContext *context = avcodec_alloc_context3(codec); AVCodecParserContext *parser = NULL; if (w->title && w->title->opaque_priv != NULL) { AVFormatContext *ic = (AVFormatContext*)w->title->opaque_priv; avcodec_copy_context(context, ic->streams[audio->id]->codec); // libav's eac3 parser toggles the codec_id in the context as // it reads eac3 data between AV_CODEC_ID_AC3 and AV_CODEC_ID_EAC3. // It detects an AC3 sync pattern sometimes in ac3_sync() which // causes it to eventually set avctx->codec_id to AV_CODEC_ID_AC3 // in ff_aac_ac3_parse(). Since we are parsing some data before // we get here, the codec_id may have flipped. This will cause an // error in hb_avcodec_open(). So flip it back! context->codec_id = w->codec_param; } else { parser = av_parser_init(codec->id); } hb_ff_set_sample_fmt( context, codec, AV_SAMPLE_FMT_FLT ); AVDictionary * av_opts = NULL; av_dict_set( &av_opts, "err_detect", "crccheck+explode", 0 ); if ( hb_avcodec_open( context, codec, &av_opts, 0 ) ) { av_dict_free( &av_opts ); return -1; } av_dict_free( &av_opts ); unsigned char *parse_buffer; int parse_pos, dec_pos, parse_buffer_size; while (buf != NULL && !ret) { parse_pos = 0; while (parse_pos < buf->size) { int parse_len, truehd_mono = 0; if (parser != NULL) { parse_len = av_parser_parse2(parser, context, &parse_buffer, &parse_buffer_size, buf->data + parse_pos, buf->size - parse_pos, buf->s.start, buf->s.start, 0); } else { parse_buffer = buf->data + parse_pos; parse_len = parse_buffer_size = buf->size - parse_pos; } // libavcodec can't decode TrueHD Mono (bug #356) // work around it by requesting Stereo before decoding if (context->codec_id == AV_CODEC_ID_TRUEHD && context->channel_layout == AV_CH_LAYOUT_MONO) { truehd_mono = 1; context->request_channel_layout = AV_CH_LAYOUT_STEREO; } else { context->request_channel_layout = 0; } dec_pos = 0; while (dec_pos < parse_buffer_size) { int dec_len; int got_frame; AVFrame *frame = av_frame_alloc(); AVPacket avp; av_init_packet(&avp); avp.data = parse_buffer + dec_pos; avp.size = parse_buffer_size - dec_pos; dec_len = avcodec_decode_audio4(context, frame, &got_frame, &avp); if (dec_len < 0) { av_frame_free(&frame); break; } if (dec_len > 0 && got_frame) { info->rate_base = 1; // libavcoded doesn't consistently set frame->sample_rate if (frame->sample_rate != 0) { info->rate = frame->sample_rate; } else { info->rate = context->sample_rate; hb_log("decavcodecaBSInfo: warning: invalid frame sample_rate! Using context sample_rate."); } info->samples_per_frame = frame->nb_samples; int bps = av_get_bits_per_sample(context->codec_id); int channels = av_get_channel_layout_nb_channels(frame->channel_layout); if (bps > 0) { info->bitrate = (bps * channels * info->rate); } else if (context->bit_rate > 0) { info->bitrate = context->bit_rate; } else { info->bitrate = 1; } if (truehd_mono) { info->channel_layout = AV_CH_LAYOUT_MONO; info->matrix_encoding = AV_MATRIX_ENCODING_NONE; } else { AVFrameSideData *side_data; if ((side_data = av_frame_get_side_data(frame, AV_FRAME_DATA_MATRIXENCODING)) != NULL) { info->matrix_encoding = *side_data->data; } else { info->matrix_encoding = AV_MATRIX_ENCODING_NONE; } if (info->matrix_encoding == AV_MATRIX_ENCODING_DOLBY || info->matrix_encoding == AV_MATRIX_ENCODING_DPLII) { info->channel_layout = AV_CH_LAYOUT_STEREO_DOWNMIX; } else { info->channel_layout = frame->channel_layout; } } if (context->codec_id == AV_CODEC_ID_AC3 || context->codec_id == AV_CODEC_ID_EAC3) { if (context->audio_service_type == AV_AUDIO_SERVICE_TYPE_KARAOKE) { info->mode = 7; } else { info->mode = context->audio_service_type; } } else if (context->codec_id == AV_CODEC_ID_AAC && context->extradata_size == 0) { // Parse ADTS AAC streams for AudioSpecificConfig. // This data is required in order to write // proper headers in MP4 and MKV files. AVBitStreamFilterContext* aac_adtstoasc; aac_adtstoasc = av_bitstream_filter_init("aac_adtstoasc"); if (aac_adtstoasc) { int ret, size; uint8_t *data; ret = av_bitstream_filter_filter(aac_adtstoasc, context, NULL, &data, &size, avp.data, avp.size, 0); if (ret >= 0 && context->extradata_size > 0 && audio->priv.config.extradata.length == 0) { int len; len = MIN(context->extradata_size, HB_CONFIG_MAX_SIZE); memcpy(audio->priv.config.extradata.bytes, context->extradata, len); audio->priv.config.extradata.length = len; } av_bitstream_filter_close(aac_adtstoasc); } } ret = 1; av_frame_free(&frame); break; } dec_pos += dec_len; av_frame_free(&frame); } parse_pos += parse_len; } buf = buf->next; } info->profile = context->profile; info->level = context->level; info->channel_map = &hb_libav_chan_map; if ( parser != NULL ) av_parser_close( parser ); hb_avcodec_close( context ); av_freep( &context->extradata ); av_freep( &context ); return ret; } /* ------------------------------------------------------------- * General purpose video decoder using libavcodec */ static uint8_t *copy_plane( uint8_t *dst, uint8_t* src, int dstride, int sstride, int h ) { if ( dstride == sstride ) { memcpy( dst, src, dstride * h ); return dst + dstride * h; } int lbytes = dstride <= sstride? dstride : sstride; while ( --h >= 0 ) { memcpy( dst, src, lbytes ); src += sstride; dst += dstride; } return dst; } // copy one video frame into an HB buf. If the frame isn't in our color space // or at least one of its dimensions is odd, use sws_scale to convert/rescale it. // Otherwise just copy the bits. static hb_buffer_t *copy_frame( hb_work_private_t *pv ) { AVCodecContext *context = pv->context; int w, h; if ( ! pv->job ) { // HandBrake's video pipeline uses yuv420 color. This means all // dimensions must be even. So we must adjust the dimensions // of incoming video if not even. w = context->width & ~1; h = context->height & ~1; } else { w = pv->job->title->width; h = pv->job->title->height; } #ifdef USE_HWD if (pv->dxva2 && pv->job) { hb_buffer_t *buf; int ww, hh; buf = hb_video_buffer_init( w, h ); ww = w; hh = h; if( !pv->dst_frame ) { pv->dst_frame = malloc( ww * hh * 3 / 2 ); } if( hb_va_extract( pv->dxva2, pv->dst_frame, pv->frame, pv->job->width, pv->job->height, pv->job->title->crop, pv->opencl_scale, pv->job->use_opencl, pv->job->use_decomb, pv->job->use_detelecine ) == HB_WORK_ERROR ) { hb_log( "hb_va_Extract failed!!!!!!" ); } w = buf->plane[0].stride; h = buf->plane[0].height; uint8_t *dst = buf->plane[0].data; copy_plane( dst, pv->dst_frame, w, ww, h ); w = buf->plane[1].stride; h = buf->plane[1].height; dst = buf->plane[1].data; copy_plane( dst, pv->dst_frame + ww * hh, w, ww >> 1, h ); w = buf->plane[2].stride; h = buf->plane[2].height; dst = buf->plane[2].data; copy_plane( dst, pv->dst_frame + ww * hh +( ( ww * hh ) >> 2 ), w, ww >> 1, h ); return buf; } else #endif { hb_buffer_t *buf = hb_video_buffer_init( w, h ); #ifdef USE_QSV // no need to copy the frame data when decoding with QSV to opaque memory if (pv->qsv.decode && pv->qsv.config.io_pattern == MFX_IOPATTERN_OUT_OPAQUE_MEMORY) { buf->qsv_details.qsv_atom = pv->frame->data[2]; return buf; } #endif uint8_t *dst = buf->data; if (context->pix_fmt != AV_PIX_FMT_YUV420P || w != context->width || h != context->height) { // have to convert to our internal color space and/or rescale AVPicture dstpic; hb_avpicture_fill(&dstpic, buf); if (pv->sws_context == NULL || pv->sws_width != context->width || pv->sws_height != context->height || pv->sws_pix_fmt != context->pix_fmt) { if (pv->sws_context != NULL) sws_freeContext(pv->sws_context); pv->sws_context = hb_sws_get_context(context->width, context->height, context->pix_fmt, w, h, AV_PIX_FMT_YUV420P, SWS_LANCZOS|SWS_ACCURATE_RND); pv->sws_width = context->width; pv->sws_height = context->height; pv->sws_pix_fmt = context->pix_fmt; } sws_scale(pv->sws_context, (const uint8_t* const *)pv->frame->data, pv->frame->linesize, 0, context->height, dstpic.data, dstpic.linesize); } else { w = buf->plane[0].stride; h = buf->plane[0].height; dst = buf->plane[0].data; copy_plane( dst, pv->frame->data[0], w, pv->frame->linesize[0], h ); w = buf->plane[1].stride; h = buf->plane[1].height; dst = buf->plane[1].data; copy_plane( dst, pv->frame->data[1], w, pv->frame->linesize[1], h ); w = buf->plane[2].stride; h = buf->plane[2].height; dst = buf->plane[2].data; copy_plane( dst, pv->frame->data[2], w, pv->frame->linesize[2], h ); } return buf; } } #ifdef USE_HWD static int get_frame_buf_hwd( AVCodecContext *context, AVFrame *frame ) { hb_work_private_t *pv = (hb_work_private_t*)context->opaque; if ( (pv != NULL) && pv->dxva2 ) { int result = HB_WORK_ERROR; hb_work_private_t *pv = (hb_work_private_t*)context->opaque; result = hb_va_get_frame_buf( pv->dxva2, context, frame ); if( result == HB_WORK_ERROR ) return avcodec_default_get_buffer( context, frame ); return 0; } else return avcodec_default_get_buffer( context, frame ); } static void hb_ffmpeg_release_frame_buf( struct AVCodecContext *p_context, AVFrame *frame ) { hb_work_private_t *p_dec = (hb_work_private_t*)p_context->opaque; int i; if( p_dec->dxva2 ) { hb_va_release( p_dec->dxva2, frame ); } else if( !frame->opaque ) { if( frame->type == FF_BUFFER_TYPE_INTERNAL ) avcodec_default_release_buffer( p_context, frame ); } for( i = 0; i < 4; i++ ) frame->data[i] = NULL; } #endif static void log_chapter( hb_work_private_t *pv, int chap_num, int64_t pts ) { hb_chapter_t *c; if ( !pv->job ) return; c = hb_list_item( pv->job->list_chapter, chap_num - 1 ); if ( c && c->title ) { hb_log( "%s: \"%s\" (%d) at frame %u time %"PRId64, pv->context->codec->name, c->title, chap_num, pv->nframes, pts ); } else { hb_log( "%s: Chapter %d at frame %u time %"PRId64, pv->context->codec->name, chap_num, pv->nframes, pts ); } } static void flushDelayQueue( hb_work_private_t *pv ) { hb_buffer_t *buf; int slot = pv->queue_primed ? pv->nframes & (HEAP_SIZE-1) : 0; // flush all the video packets left on our timestamp-reordering delay q while ( ( buf = pv->delayq[slot] ) != NULL ) { buf->s.start = heap_pop( &pv->pts_heap ); hb_list_add( pv->list, buf ); pv->delayq[slot] = NULL; slot = ( slot + 1 ) & (HEAP_SIZE-1); } } #define TOP_FIRST PIC_FLAG_TOP_FIELD_FIRST #define PROGRESSIVE PIC_FLAG_PROGRESSIVE_FRAME #define REPEAT_FIRST PIC_FLAG_REPEAT_FIRST_FIELD #define TB 8 #define BT 16 #define BT_PROG 32 #define BTB_PROG 64 #define TB_PROG 128 #define TBT_PROG 256 static void checkCadence( int * cadence, uint16_t flags, int64_t start ) { /* Rotate the cadence tracking. */ int i = 0; for(i=11; i > 0; i--) { cadence[i] = cadence[i-1]; } if ( !(flags & PROGRESSIVE) && !(flags & TOP_FIRST) ) { /* Not progressive, not top first... That means it's probably bottom first, 2 fields displayed. */ //hb_log("MPEG2 Flag: Bottom field first, 2 fields displayed."); cadence[0] = BT; } else if ( !(flags & PROGRESSIVE) && (flags & TOP_FIRST) ) { /* Not progressive, top is first, Two fields displayed. */ //hb_log("MPEG2 Flag: Top field first, 2 fields displayed."); cadence[0] = TB; } else if ( (flags & PROGRESSIVE) && !(flags & TOP_FIRST) && !( flags & REPEAT_FIRST ) ) { /* Progressive, but noting else. That means Bottom first, 2 fields displayed. */ //hb_log("MPEG2 Flag: Progressive. Bottom field first, 2 fields displayed."); cadence[0] = BT_PROG; } else if ( (flags & PROGRESSIVE) && !(flags & TOP_FIRST) && ( flags & REPEAT_FIRST ) ) { /* Progressive, and repeat. . That means Bottom first, 3 fields displayed. */ //hb_log("MPEG2 Flag: Progressive repeat. Bottom field first, 3 fields displayed."); cadence[0] = BTB_PROG; } else if ( (flags & PROGRESSIVE) && (flags & TOP_FIRST) && !( flags & REPEAT_FIRST ) ) { /* Progressive, top first. That means top first, 2 fields displayed. */ //hb_log("MPEG2 Flag: Progressive. Top field first, 2 fields displayed."); cadence[0] = TB_PROG; } else if ( (flags & PROGRESSIVE) && (flags & TOP_FIRST) && ( flags & REPEAT_FIRST ) ) { /* Progressive, top, repeat. That means top first, 3 fields displayed. */ //hb_log("MPEG2 Flag: Progressive repeat. Top field first, 3 fields displayed."); cadence[0] = TBT_PROG; } if ( (cadence[2] <= TB) && (cadence[1] <= TB) && (cadence[0] > TB) && (cadence[11]) ) hb_log("%fs: Video -> Film", (float)start / 90000); if ( (cadence[2] > TB) && (cadence[1] <= TB) && (cadence[0] <= TB) && (cadence[11]) ) hb_log("%fs: Film -> Video", (float)start / 90000); } // send cc_buf to the CC decoder(s) static void cc_send_to_decoder(hb_work_private_t *pv, hb_buffer_t *buf) { if (buf == NULL) return; // if there's more than one decoder for the captions send a copy // of the buffer to all. hb_subtitle_t *subtitle; int ii = 0, n = hb_list_count(pv->list_subtitle); while (--n > 0) { // make a copy of the buf then forward it to the decoder hb_buffer_t *cpy = hb_buffer_dup(buf); subtitle = hb_list_item(pv->list_subtitle, ii++); hb_fifo_push(subtitle->fifo_in, cpy); } subtitle = hb_list_item(pv->list_subtitle, ii); hb_fifo_push( subtitle->fifo_in, buf ); } static hb_buffer_t * cc_fill_buffer(hb_work_private_t *pv, uint8_t *cc, int size, int64_t pts) { int cc_count[4] = {0,}; int ii; hb_buffer_t *buf = NULL; for (ii = 0; ii < size; ii += 3) { if ((cc[ii] & 0x04) == 0) // not valid continue; if ((cc[ii+1] & 0x7f) == 0 && (cc[ii+2] & 0x7f) == 0) // stuffing continue; int type = cc[ii] & 0x03; cc_count[type]++; } // Only handles CC1 for now. if (cc_count[0] > 0) { buf = hb_buffer_init(cc_count[0] * 2); buf->s.start = pts; int jj = 0; for (ii = 0; ii < size; ii += 3) { if ((cc[ii] & 0x04) == 0) // not valid continue; if ((cc[ii+1] & 0x7f) == 0 && (cc[ii+2] & 0x7f) == 0) // stuffing continue; int type = cc[ii] & 0x03; if (type == 0) { buf->data[jj++] = cc[ii+1]; buf->data[jj++] = cc[ii+2]; } } } return buf; } static int get_frame_type(int type) { switch(type) { case AV_PICTURE_TYPE_I: return HB_FRAME_I; case AV_PICTURE_TYPE_B: return HB_FRAME_B; case AV_PICTURE_TYPE_P: return HB_FRAME_P; } return 0; } /* * Decodes a video frame from the specified raw packet data * ('data', 'size', 'sequence'). * The output of this function is stored in 'pv->list', which contains a list * of zero or more decoded packets. * * The returned packets are guaranteed to have their timestamps in the correct * order, even if the original packets decoded by libavcodec have misordered * timestamps, due to the use of 'packed B-frames'. * * Internally the set of decoded packets may be buffered in 'pv->delayq' * until enough packets have been decoded so that the timestamps can be * correctly rewritten, if this is necessary. */ static int decodeFrame( hb_work_object_t *w, uint8_t *data, int size, int sequence, int64_t pts, int64_t dts, uint8_t frametype ) { hb_work_private_t *pv = w->private_data; int got_picture, oldlevel = 0; AVPacket avp; if ( global_verbosity_level <= 1 ) { oldlevel = av_log_get_level(); av_log_set_level( AV_LOG_QUIET ); } av_init_packet(&avp); avp.data = data; avp.size = size; avp.pts = pts; avp.dts = dts; if (pv->palette != NULL) { uint8_t * palette; int size; palette = av_packet_new_side_data(&avp, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); size = MIN(pv->palette->size, AVPALETTE_SIZE); memcpy(palette, pv->palette->data, size); hb_buffer_close(&pv->palette); } /* * libav avcodec_decode_video2() needs AVPacket flagged with AV_PKT_FLAG_KEY * for some codecs. For example, sequence of PNG in a mov container. */ if ( frametype & HB_FRAME_KEY ) { avp.flags |= AV_PKT_FLAG_KEY; } #ifdef USE_QSV_PTS_WORKAROUND /* * The MediaSDK decoder will return decoded frames in the correct order, * but *sometimes* with the incorrect timestamp assigned to them. * * We work around it by saving the input timestamps (in chronological order) * and restoring them after decoding. */ if (pv->qsv.decode && avp.data != NULL) { hb_av_add_new_pts(pv->qsv.pts_list, avp.pts); } #endif if ( avcodec_decode_video2( pv->context, pv->frame, &got_picture, &avp ) < 0 ) { ++pv->decode_errors; } #ifdef USE_QSV if (pv->qsv.decode && pv->job->qsv.ctx == NULL && pv->video_codec_opened > 0) { // this is quite late, but we can't be certain that the QSV context is // available until after we call avcodec_decode_video2() at least once pv->job->qsv.ctx = pv->context->priv_data; } #endif #ifdef USE_QSV_PTS_WORKAROUND if (pv->qsv.decode && got_picture) { // we got a decoded frame, restore the lowest available PTS pv->frame->pkt_pts = hb_av_pop_next_pts(pv->qsv.pts_list); } #endif if ( global_verbosity_level <= 1 ) { av_log_set_level( oldlevel ); } if( got_picture ) { uint16_t flags = 0; // ffmpeg makes it hard to attach a pts to a frame. if the MPEG ES // packet had a pts we handed it to av_parser_parse (if the packet had // no pts we set it to AV_NOPTS_VALUE, but before the parse we can't // distinguish between the start of a video frame with no pts & an // intermediate packet of some frame which never has a pts). we hope // that when parse returns the frame to us the pts we originally // handed it will be in parser->pts. we put this pts into avp.pts so // that when avcodec_decode_video finally gets around to allocating an // AVFrame to hold the decoded frame, avcodec_default_get_buffer can // stuff that pts into the it. if all of these relays worked at this // point frame.pts should hold the frame's pts from the original data // stream or AV_NOPTS_VALUE if it didn't have one. in the latter case // we generate the next pts in sequence for it. if ( !pv->frame_duration_set ) compute_frame_duration( pv ); double pts; double frame_dur = pv->duration; if ( pv->frame->repeat_pict ) { frame_dur += pv->frame->repeat_pict * pv->field_duration; } #ifdef USE_HWD if( pv->dxva2 && pv->dxva2->do_job == HB_WORK_OK ) { if( avp.pts>0 ) { if( pv->dxva2->input_pts[0] != 0 && pv->dxva2->input_pts[1] == 0 ) pv->frame->pkt_pts = pv->dxva2->input_pts[0]; else pv->frame->pkt_pts = pv->dxva2->input_pts[0]dxva2->input_pts[1] ? pv->dxva2->input_pts[0] : pv->dxva2->input_pts[1]; } } #endif // If there was no pts for this frame, assume constant frame rate // video & estimate the next frame time from the last & duration. if (pv->frame->pkt_pts == AV_NOPTS_VALUE || hb_gui_use_hwd_flag == 1) { pts = pv->pts_next; } else { pts = pv->frame->pkt_pts; // Detect streams with broken out of order timestamps if (!pv->brokenTS && pv->frame->pkt_pts < pv->prev_pts) { hb_log("Broken timestamps detected. Reordering."); pv->brokenTS = 1; } pv->prev_pts = pv->frame->pkt_pts; } pv->pts_next = pts + frame_dur; if ( pv->frame->top_field_first ) { flags |= PIC_FLAG_TOP_FIELD_FIRST; } if ( !pv->frame->interlaced_frame ) { flags |= PIC_FLAG_PROGRESSIVE_FRAME; } if ( pv->frame->repeat_pict == 1 ) { flags |= PIC_FLAG_REPEAT_FIRST_FIELD; } if ( pv->frame->repeat_pict == 2 ) { flags |= PIC_FLAG_REPEAT_FRAME; } int frametype = get_frame_type(pv->frame->pict_type); // Check for CC data AVFrameSideData *sd; sd = av_frame_get_side_data(pv->frame, AV_FRAME_DATA_A53_CC); if (sd != NULL) { if (!pv->job && pv->title && sd->size > 0) { hb_subtitle_t *subtitle; int i = 0; while ((subtitle = hb_list_item(pv->title->list_subtitle, i++))) { /* * Let's call them 608 subs for now even if they aren't, * since they are the only types we grok. */ if (subtitle->source == CC608SUB) { break; } } if (subtitle == NULL) { subtitle = calloc(sizeof( hb_subtitle_t ), 1); subtitle->track = 0; subtitle->id = 0; subtitle->format = TEXTSUB; subtitle->source = CC608SUB; subtitle->config.dest = PASSTHRUSUB; subtitle->codec = WORK_DECCC608; subtitle->type = 5; snprintf(subtitle->lang, sizeof( subtitle->lang ), "Closed Captions"); /* * The language of the subtitles will be the same as the * first audio track, i.e. the same as the video. */ hb_audio_t *audio = hb_list_item(pv->title->list_audio, 0); if (audio != NULL) { snprintf(subtitle->iso639_2, sizeof(subtitle->iso639_2), "%s", audio->config.lang.iso639_2); } else { snprintf(subtitle->iso639_2, sizeof(subtitle->iso639_2), "und"); } hb_list_add(pv->title->list_subtitle, subtitle); } } if (pv->list_subtitle != NULL && sd->size > 0) { hb_buffer_t *cc_buf; cc_buf = cc_fill_buffer(pv, sd->data, sd->size, pts); cc_send_to_decoder(pv, cc_buf); } } hb_buffer_t *buf; // if we're doing a scan or this content couldn't have been broken // by Microsoft we don't worry about timestamp reordering if ( ! pv->job || ! pv->brokenTS ) { buf = copy_frame( pv ); av_frame_unref(pv->frame); buf->s.start = pts; buf->sequence = sequence; buf->s.flags = flags; buf->s.frametype = frametype; if ( pv->new_chap && buf->s.start >= pv->chap_time ) { buf->s.new_chap = pv->new_chap; log_chapter( pv, pv->new_chap, buf->s.start ); pv->new_chap = 0; pv->chap_time = 0; } else if ( pv->nframes == 0 && pv->job ) { log_chapter( pv, pv->job->chapter_start, buf->s.start ); } checkCadence( pv->cadence, flags, buf->s.start ); hb_list_add( pv->list, buf ); ++pv->nframes; return got_picture; } // XXX This following probably addresses a libavcodec bug but I don't // see an easy fix so we workaround it here. // // The M$ 'packed B-frames' atrocity results in decoded frames with // the wrong timestamp. E.g., if there are 2 b-frames the timestamps // we see here will be "2 3 1 5 6 4 ..." instead of "1 2 3 4 5 6". // The frames are actually delivered in the right order but with // the wrong timestamp. To get the correct timestamp attached to // each frame we have a delay queue (longer than the max number of // b-frames) & a sorting heap for the timestamps. As each frame // comes out of the decoder the oldest frame in the queue is removed // and associated with the smallest timestamp. Then the new frame is // added to the queue & its timestamp is pushed on the heap. // This does nothing if the timestamps are correct (i.e., the video // uses a codec that Micro$oft hasn't broken yet) but the frames // get timestamped correctly even when M$ has munged them. // remove the oldest picture from the frame queue (if any) & // give it the smallest timestamp from our heap. The queue size // is a power of two so we get the slot of the oldest by masking // the frame count & this will become the slot of the newest // once we've removed & processed the oldest. int slot = pv->nframes & (HEAP_SIZE-1); if ( ( buf = pv->delayq[slot] ) != NULL ) { pv->queue_primed = 1; buf->s.start = heap_pop( &pv->pts_heap ); if ( pv->new_chap && buf->s.start >= pv->chap_time ) { buf->s.new_chap = pv->new_chap; log_chapter( pv, pv->new_chap, buf->s.start ); pv->new_chap = 0; pv->chap_time = 0; } else if ( pv->nframes == 0 && pv->job ) { log_chapter( pv, pv->job->chapter_start, buf->s.start ); } checkCadence( pv->cadence, buf->s.flags, buf->s.start ); hb_list_add( pv->list, buf ); } // add the new frame to the delayq & push its timestamp on the heap buf = copy_frame( pv ); av_frame_unref(pv->frame); buf->sequence = sequence; /* Store picture flags for later use by filters */ buf->s.flags = flags; buf->s.frametype = frametype; pv->delayq[slot] = buf; heap_push( &pv->pts_heap, pts ); ++pv->nframes; } return got_picture; } static void decodeVideo( hb_work_object_t *w, uint8_t *data, int size, int sequence, int64_t pts, int64_t dts, uint8_t frametype ) { hb_work_private_t *pv = w->private_data; /* * The following loop is a do..while because we need to handle both * data & the flush at the end (signaled by size=0). At the end there's * generally a frame in the parser & one or more frames in the decoder * (depending on the bframes setting). */ int pos = 0; do { uint8_t *pout; int pout_len, len; int64_t parser_pts, parser_dts; if ( pv->parser ) { len = av_parser_parse2( pv->parser, pv->context, &pout, &pout_len, data + pos, size - pos, pts, dts, 0 ); parser_pts = pv->parser->pts; parser_dts = pv->parser->dts; } else { pout = data; len = pout_len = size; parser_pts = pts; parser_dts = dts; } pos += len; if ( pout_len > 0 ) { decodeFrame( w, pout, pout_len, sequence, parser_pts, parser_dts, frametype ); } } while ( pos < size ); /* the stuff above flushed the parser, now flush the decoder */ if (size <= 0) { while (decodeFrame(w, NULL, 0, sequence, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0)) { continue; } #ifdef USE_QSV if (pv->qsv.decode) { // flush a second time while (decodeFrame(w, NULL, 0, sequence, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0)) { continue; } } #endif flushDelayQueue(pv); if (pv->list_subtitle != NULL) cc_send_to_decoder(pv, hb_buffer_init(0)); } } /* * Removes all packets from 'pv->list', links them together into * a linked-list, and returns the first packet in the list. */ static hb_buffer_t *link_buf_list( hb_work_private_t *pv ) { hb_buffer_t *head = hb_list_item( pv->list, 0 ); if ( head ) { hb_list_rem( pv->list, head ); hb_buffer_t *last = head, *buf; while ( ( buf = hb_list_item( pv->list, 0 ) ) != NULL ) { hb_list_rem( pv->list, buf ); last->next = buf; last = buf; } } return head; } static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t *pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; pv->wait_for_keyframe = 60; pv->job = job; if ( job ) pv->title = job->title; else pv->title = w->title; pv->list = hb_list_init(); #ifdef USE_QSV if (hb_qsv_decode_is_enabled(job)) { // determine which encoder we're using hb_qsv_info_t *info = hb_qsv_info_get(job->vcodec); pv->qsv.decode = info != NULL; if (pv->qsv.decode) { // setup the QSV configuration pv->qsv.config.io_pattern = MFX_IOPATTERN_OUT_OPAQUE_MEMORY; pv->qsv.config.impl_requested = info->implementation; pv->qsv.config.async_depth = job->qsv.async_depth; pv->qsv.config.sync_need = 0; pv->qsv.config.usage_threaded = 1; pv->qsv.config.additional_buffers = 64; // FIFO_LARGE if (info->capabilities & HB_QSV_CAP_RATECONTROL_LA) { // more surfaces may be needed for the lookahead pv->qsv.config.additional_buffers = 160; } pv->qsv.codec_name = hb_qsv_decode_get_codec_name(w->codec_param); } } else { pv->qsv.decode = 0; } #endif if( pv->job && pv->job->title && !pv->job->title->has_resolution_change ) { pv->threads = HB_FFMPEG_THREADS_AUTO; } AVCodec *codec = NULL; #ifdef USE_QSV if (pv->qsv.decode) { codec = avcodec_find_decoder_by_name(pv->qsv.codec_name); } else #endif { codec = avcodec_find_decoder(w->codec_param); } if ( codec == NULL ) { hb_log( "decavcodecvInit: failed to find codec for id (%d)", w->codec_param ); return 1; } 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[pv->title->video_id]->codec); pv->context->workaround_bugs = FF_BUG_AUTODETECT; pv->context->err_recognition = AV_EF_CRCCHECK; pv->context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK; #ifdef USE_HWD // QSV decoding is faster, so prefer it to DXVA2 if (pv->job != NULL && !pv->qsv.decode && pv->job->use_hwd && hb_use_dxva(pv->title)) { pv->dxva2 = hb_va_create_dxva2( pv->dxva2, w->codec_param ); if( pv->dxva2 && pv->dxva2->do_job == HB_WORK_OK ) { hb_va_new_dxva2( pv->dxva2, pv->context ); pv->context->slice_flags |= SLICE_FLAG_ALLOW_FIELD; pv->context->opaque = pv; pv->context->get_buffer = get_frame_buf_hwd; pv->context->release_buffer = hb_ffmpeg_release_frame_buf; pv->context->get_format = hb_ffmpeg_get_format; pv->opencl_scale = ( hb_oclscale_t * )malloc( sizeof( hb_oclscale_t ) ); memset( pv->opencl_scale, 0, sizeof( hb_oclscale_t ) ); pv->threads = 1; } } #endif #ifdef USE_QSV if (pv->qsv.decode) { #ifdef USE_QSV_PTS_WORKAROUND pv->qsv.pts_list = hb_list_init(); #endif // set the QSV configuration before opening the decoder pv->context->hwaccel_context = &pv->qsv.config; } #endif // Set encoder opts... AVDictionary * av_opts = NULL; av_dict_set( &av_opts, "refcounted_frames", "1", 0 ); if (pv->title->flags & HBTF_NO_IDR) { av_dict_set( &av_opts, "flags", "output_corrupt", 0 ); } if ( hb_avcodec_open( pv->context, codec, &av_opts, pv->threads ) ) { av_dict_free( &av_opts ); hb_log( "decavcodecvInit: avcodec_open failed" ); return 1; } av_dict_free( &av_opts ); pv->video_codec_opened = 1; // avi, mkv and possibly mp4 containers can contain the M$ VFW packed // b-frames abortion that messes up frame ordering and timestamps. // XXX ffmpeg knows which streams are broken but doesn't expose the // info externally. We should patch ffmpeg to add a flag to the // codec context for this but until then we mark all ffmpeg streams // as suspicious. pv->brokenTS = 1; } else { pv->parser = av_parser_init( w->codec_param ); } pv->frame = av_frame_alloc(); if (pv->frame == NULL) { hb_log("decavcodecvInit: av_frame_alloc failed"); return 1; } /* * If not scanning, then are we supposed to extract Closed Captions * and send them to the decoder? */ if (job != NULL && hb_list_count(job->list_subtitle) > 0) { hb_subtitle_t *subtitle; int i = 0; while ((subtitle = hb_list_item(job->list_subtitle, i++)) != NULL) { if (subtitle->source == CC608SUB) { if (pv->list_subtitle == NULL) { pv->list_subtitle = hb_list_init(); } hb_list_add(pv->list_subtitle, subtitle); } } } return 0; } static int setup_extradata( hb_work_object_t *w, hb_buffer_t *in ) { hb_work_private_t *pv = w->private_data; // we can't call the avstream funcs but the read_header func in the // AVInputFormat may set up some state in the AVContext. In particular // vc1t_read_header allocates 'extradata' to deal with header issues // related to Microsoft's bizarre engineering notions. We alloc a chunk // of space to make vc1 work then associate the codec with the context. if (pv->context->extradata == NULL) { if (pv->parser == NULL || pv->parser == NULL || pv->parser->parser->split == NULL) { return 0; } else { int size; size = pv->parser->parser->split(pv->context, in->data, in->size); if (size > 0) { pv->context->extradata_size = size; pv->context->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); if (pv->context->extradata == NULL) return 1; memcpy(pv->context->extradata, in->data, size); return 0; } } return 1; } return 0; } static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t *pv = w->private_data; hb_buffer_t *in = *buf_in; int64_t pts = AV_NOPTS_VALUE; int64_t dts = pts; *buf_in = NULL; *buf_out = NULL; /* if we got an empty buffer signaling end-of-stream send it downstream */ if ( in->size == 0 ) { if (pv->context != NULL && pv->context->codec != NULL) { decodeVideo( w, in->data, in->size, in->sequence, pts, dts, in->s.frametype ); } hb_list_add( pv->list, in ); *buf_out = link_buf_list( pv ); return HB_WORK_DONE; } // if this is the first frame open the codec (we have to wait for the // first frame because of M$ VC1 braindamage). if ( !pv->video_codec_opened ) { AVCodec *codec = NULL; #ifdef USE_QSV if (pv->qsv.decode) { codec = avcodec_find_decoder_by_name(pv->qsv.codec_name); } else #endif { codec = avcodec_find_decoder(w->codec_param); } if ( codec == NULL ) { hb_log( "decavcodecvWork: failed to find codec for id (%d)", w->codec_param ); *buf_out = hb_buffer_init( 0 );; return HB_WORK_DONE; } pv->context = avcodec_alloc_context3( codec ); pv->context->workaround_bugs = FF_BUG_AUTODETECT; pv->context->err_recognition = AV_EF_CRCCHECK; pv->context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK; if ( setup_extradata( w, in ) ) { // we didn't find the headers needed to set up extradata. // the codec will abort if we open it so just free the buf // and hope we eventually get the info we need. hb_buffer_close( &in ); return HB_WORK_OK; } #ifdef USE_QSV if (pv->qsv.decode) { #ifdef USE_QSV_PTS_WORKAROUND pv->qsv.pts_list = hb_list_init(); #endif // set the QSV configuration before opening the decoder pv->context->hwaccel_context = &pv->qsv.config; } #endif AVDictionary * av_opts = NULL; av_dict_set( &av_opts, "refcounted_frames", "1", 0 ); if (pv->title->flags & HBTF_NO_IDR) { av_dict_set( &av_opts, "flags", "output_corrupt", 0 ); } // disable threaded decoding for scan, can cause crashes if ( hb_avcodec_open( pv->context, codec, &av_opts, pv->threads ) ) { av_dict_free( &av_opts ); hb_log( "decavcodecvWork: avcodec_open failed" ); *buf_out = hb_buffer_init( 0 );; return HB_WORK_DONE; } av_dict_free( &av_opts ); pv->video_codec_opened = 1; } if( in->s.start >= 0 ) { pts = in->s.start; dts = in->s.renderOffset; } if ( in->s.new_chap ) { pv->new_chap = in->s.new_chap; pv->chap_time = pts >= 0? pts : pv->pts_next; } #ifdef USE_HWD if( pv->dxva2 && pv->dxva2->do_job == HB_WORK_OK ) { if( pv->dxva2->input_pts[0] <= pv->dxva2->input_pts[1] ) pv->dxva2->input_pts[0] = pts; else if( pv->dxva2->input_pts[0] > pv->dxva2->input_pts[1] ) pv->dxva2->input_pts[1] = pts; pv->dxva2->input_dts = dts; } #endif if (in->palette != NULL) { pv->palette = in->palette; in->palette = NULL; } decodeVideo( w, in->data, in->size, in->sequence, pts, dts, in->s.frametype ); hb_buffer_close( &in ); *buf_out = link_buf_list( pv ); return HB_WORK_OK; } static void compute_frame_duration( hb_work_private_t *pv ) { double duration = 0.; int64_t max_fps = 64L; // context->time_base may be in fields, so set the max *fields* per second if ( pv->context->ticks_per_frame > 1 ) max_fps *= pv->context->ticks_per_frame; if ( pv->title->opaque_priv ) { // If ffmpeg is demuxing for us, it collects some additional // information about framerates that is often more accurate // than context->time_base. AVFormatContext *ic = (AVFormatContext*)pv->title->opaque_priv; AVStream *st = ic->streams[pv->title->video_id]; if ( st->nb_frames && st->duration ) { // compute the average frame duration from the total number // of frames & the total duration. duration = ( (double)st->duration * (double)st->time_base.num ) / ( (double)st->nb_frames * (double)st->time_base.den ); } // Raw demuxers set a default fps of 25 and do not parse // a value from the container. So use the codec time_base // for raw demuxers. else if (ic->iformat->raw_codec_id == AV_CODEC_ID_NONE) { // XXX We don't have a frame count or duration so try to use the // far less reliable time base info in the stream. // Because the time bases are so screwed up, we only take values // in the range 8fps - 64fps. AVRational *tb = NULL; if ( st->avg_frame_rate.den * 64L > st->avg_frame_rate.num && st->avg_frame_rate.num > st->avg_frame_rate.den * 8L ) { tb = &(st->avg_frame_rate); duration = (double)tb->den / (double)tb->num; } else if ( st->time_base.num * 64L > st->time_base.den && st->time_base.den > st->time_base.num * 8L ) { tb = &(st->time_base); duration = (double)tb->num / (double)tb->den; } } if ( !duration && pv->context->time_base.num * max_fps > pv->context->time_base.den && pv->context->time_base.den > pv->context->time_base.num * 8L ) { duration = (double)pv->context->time_base.num / (double)pv->context->time_base.den; if ( pv->context->ticks_per_frame > 1 ) { // for ffmpeg 0.5 & later, the H.264 & MPEG-2 time base is // field rate rather than frame rate so convert back to frames. duration *= pv->context->ticks_per_frame; } } } else { if ( pv->context->time_base.num * max_fps > pv->context->time_base.den && pv->context->time_base.den > pv->context->time_base.num * 8L ) { duration = (double)pv->context->time_base.num / (double)pv->context->time_base.den; if ( pv->context->ticks_per_frame > 1 ) { // for ffmpeg 0.5 & later, the H.264 & MPEG-2 time base is // field rate rather than frame rate so convert back to frames. duration *= pv->context->ticks_per_frame; } } } if ( duration == 0 ) { // No valid timing info found in the stream, so pick some value duration = 1001. / 24000.; } else { pv->frame_duration_set = 1; } pv->duration = duration * 90000.; pv->field_duration = pv->duration; if ( pv->context->ticks_per_frame > 1 ) { pv->field_duration /= pv->context->ticks_per_frame; } } static int decavcodecvInfo( hb_work_object_t *w, hb_work_info_t *info ) { hb_work_private_t *pv = w->private_data; memset( info, 0, sizeof(*info) ); if (pv->context == NULL) return 0; info->bitrate = pv->context->bit_rate; // HandBrake's video pipeline uses yuv420 color. This means all // dimensions must be even. So we must adjust the dimensions // of incoming video if not even. info->width = pv->context->width & ~1; info->height = pv->context->height & ~1; info->pixel_aspect_width = pv->context->sample_aspect_ratio.num; info->pixel_aspect_height = pv->context->sample_aspect_ratio.den; compute_frame_duration( pv ); info->rate = 27000000; info->rate_base = pv->duration * 300.; info->profile = pv->context->profile; info->level = pv->context->level; info->name = pv->context->codec->name; switch( pv->context->color_primaries ) { case AVCOL_PRI_BT709: info->color_prim = HB_COLR_PRI_BT709; break; case AVCOL_PRI_BT470BG: info->color_prim = HB_COLR_PRI_EBUTECH; break; case AVCOL_PRI_BT470M: case AVCOL_PRI_SMPTE170M: case AVCOL_PRI_SMPTE240M: info->color_prim = HB_COLR_PRI_SMPTEC; break; default: { if( ( info->width >= 1280 || info->height >= 720 ) || ( info->width > 720 && info->height > 576 ) ) // ITU BT.709 HD content info->color_prim = HB_COLR_PRI_BT709; else if( info->rate_base == 1080000 ) // ITU BT.601 DVD or SD TV content (PAL) info->color_prim = HB_COLR_PRI_EBUTECH; else // ITU BT.601 DVD or SD TV content (NTSC) info->color_prim = HB_COLR_PRI_SMPTEC; break; } } switch( pv->context->color_trc ) { case AVCOL_TRC_SMPTE240M: info->color_transfer = HB_COLR_TRA_SMPTE240M; break; default: // ITU BT.601, BT.709, anything else info->color_transfer = HB_COLR_TRA_BT709; break; } switch( pv->context->colorspace ) { case AVCOL_SPC_BT709: info->color_matrix = HB_COLR_MAT_BT709; break; case AVCOL_SPC_FCC: case AVCOL_SPC_BT470BG: case AVCOL_SPC_SMPTE170M: case AVCOL_SPC_RGB: // libswscale rgb2yuv info->color_matrix = HB_COLR_MAT_SMPTE170M; break; case AVCOL_SPC_SMPTE240M: info->color_matrix = HB_COLR_MAT_SMPTE240M; break; default: { if( ( info->width >= 1280 || info->height >= 720 ) || ( info->width > 720 && info->height > 576 ) ) // ITU BT.709 HD content info->color_matrix = HB_COLR_MAT_BT709; else // ITU BT.601 DVD or SD TV content (PAL) // ITU BT.601 DVD or SD TV content (NTSC) info->color_matrix = HB_COLR_MAT_SMPTE170M; break; } } info->video_decode_support = HB_DECODE_SUPPORT_SW; switch (pv->context->codec_id) { case AV_CODEC_ID_H264: if (pv->context->pix_fmt == AV_PIX_FMT_YUV420P || pv->context->pix_fmt == AV_PIX_FMT_YUVJ420P) { #ifdef USE_QSV info->video_decode_support |= HB_DECODE_SUPPORT_QSV; #endif } break; default: break; } return 1; } static int decavcodecvBSInfo( hb_work_object_t *w, const hb_buffer_t *buf, hb_work_info_t *info ) { return 0; } static void decavcodecvFlush( hb_work_object_t *w ) { hb_work_private_t *pv = w->private_data; if (pv->context != NULL && pv->context->codec != NULL) { flushDelayQueue( pv ); hb_buffer_t *buf = link_buf_list( pv ); hb_buffer_close( &buf ); if ( pv->title->opaque_priv == NULL ) { pv->video_codec_opened = 0; hb_avcodec_close( pv->context ); av_freep( &pv->context->extradata ); av_freep( &pv->context ); if ( pv->parser ) { av_parser_close(pv->parser); } pv->parser = av_parser_init( w->codec_param ); } else { avcodec_flush_buffers( pv->context ); } } pv->wait_for_keyframe = 60; } hb_work_object_t hb_decavcodecv = { .id = WORK_DECAVCODECV, .name = "Video decoder (libavcodec)", .init = decavcodecvInit, .work = decavcodecvWork, .close = decavcodecClose, .flush = decavcodecvFlush, .info = decavcodecvInfo, .bsinfo = decavcodecvBSInfo }; static void decodeAudio(hb_audio_t *audio, hb_work_private_t *pv, uint8_t *data, int size, int64_t pts) { AVCodecContext *context = pv->context; int loop_limit = 256; int pos = 0; // If we are given a pts, use it; but don't lose partial ticks. if (pts != AV_NOPTS_VALUE && (int64_t)pv->pts_next != pts) pv->pts_next = pts; while (pos < size) { int got_frame; AVPacket avp; av_init_packet(&avp); avp.data = data + pos; avp.size = size - pos; avp.pts = pv->pts_next; avp.dts = AV_NOPTS_VALUE; int len = avcodec_decode_audio4(context, pv->frame, &got_frame, &avp); if ((len < 0) || (!got_frame && !(loop_limit--))) { return; } else { loop_limit = 256; } pos += len; if (got_frame) { hb_buffer_t *out; int samplerate; // libavcoded doesn't yet consistently set frame->sample_rate if (pv->frame->sample_rate != 0) { samplerate = pv->frame->sample_rate; } else { samplerate = context->sample_rate; } double duration = (90000. * pv->frame->nb_samples / (double)samplerate); if (audio->config.out.codec & HB_ACODEC_PASS_FLAG) { // Note that even though we are doing passthru, we had to decode // so that we know the stop time and the pts of the next audio // packet. out = hb_buffer_init(avp.size); memcpy(out->data, avp.data, avp.size); } else { AVFrameSideData *side_data; if ((side_data = av_frame_get_side_data(pv->frame, AV_FRAME_DATA_DOWNMIX_INFO)) != NULL) { double surround_mix_level, center_mix_level; AVDownmixInfo *downmix_info = (AVDownmixInfo*)side_data->data; if (audio->config.out.mixdown == HB_AMIXDOWN_DOLBY || audio->config.out.mixdown == HB_AMIXDOWN_DOLBYPLII) { surround_mix_level = downmix_info->surround_mix_level_ltrt; center_mix_level = downmix_info->center_mix_level_ltrt; } else { surround_mix_level = downmix_info->surround_mix_level; center_mix_level = downmix_info->center_mix_level; } hb_audio_resample_set_mix_levels(pv->resample, surround_mix_level, center_mix_level, downmix_info->lfe_mix_level); } hb_audio_resample_set_channel_layout(pv->resample, pv->frame->channel_layout); hb_audio_resample_set_sample_fmt(pv->resample, pv->frame->format); if (hb_audio_resample_update(pv->resample)) { hb_log("decavcodec: hb_audio_resample_update() failed"); av_frame_unref(pv->frame); return; } out = hb_audio_resample(pv->resample, pv->frame->extended_data, pv->frame->nb_samples); } av_frame_unref(pv->frame); if (out != NULL) { out->s.start = pv->pts_next; out->s.duration = duration; out->s.stop = duration + pv->pts_next; pv->pts_next = duration + pv->pts_next; hb_list_add(pv->list, out); } } } } HandBrake-0.10.2/libhb/fifo.c0000664000175200017520000006454112463330511016216 0ustar handbrakehandbrake/* fifo.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "openclwrapper.h" #ifndef SYS_DARWIN #include #endif #define FIFO_TIMEOUT 200 //#define HB_FIFO_DEBUG 1 //#define HB_BUFFER_DEBUG 1 /* Fifo */ struct hb_fifo_s { hb_lock_t * lock; hb_cond_t * cond_full; int wait_full; hb_cond_t * cond_empty; int wait_empty; uint32_t capacity; uint32_t thresh; uint32_t size; uint32_t buffer_size; hb_buffer_t * first; hb_buffer_t * last; #if defined(HB_FIFO_DEBUG) // Fifo list for debugging hb_fifo_t * next; #endif }; #if defined(HB_FIFO_DEBUG) static hb_fifo_t fifo_list = { .next = NULL }; #endif /* we round the requested buffer size up to the next power of 2 so there can * be at most 32 possible pools when the size is a 32 bit int. To avoid a lot * of slow & error-prone run-time checking we allow for all 32. */ #define MAX_BUFFER_POOLS 32 #define BUFFER_POOL_FIRST 10 #define BUFFER_POOL_LAST 25 /* the buffer pool only exists to avoid the two malloc and two free calls that * it would otherwise take to allocate & free a buffer. but we don't want to * tie up a lot of memory in the pool because this allocator isn't as general * as malloc so memory tied up here puts more pressure on the malloc pool. * A pool of 16 elements will avoid 94% of the malloc/free calls without wasting * too much memory. */ #define BUFFER_POOL_MAX_ELEMENTS 32 struct hb_buffer_pools_s { int64_t allocated; hb_lock_t *lock; hb_fifo_t *pool[MAX_BUFFER_POOLS]; #if defined(HB_BUFFER_DEBUG) hb_list_t *alloc_list; #endif } buffers; void hb_buffer_pool_init( void ) { buffers.lock = hb_lock_init(); buffers.allocated = 0; #if defined(HB_BUFFER_DEBUG) buffers.alloc_list = hb_list_init(); #endif /* we allocate pools for sizes 2^10 through 2^25. requests larger than * 2^25 will get passed through to malloc. */ int i; // Create larger queue for 2^10 bucket since all allocations smaller than // 2^10 come from here. buffers.pool[BUFFER_POOL_FIRST] = hb_fifo_init(BUFFER_POOL_MAX_ELEMENTS*10, 1); buffers.pool[BUFFER_POOL_FIRST]->buffer_size = 1 << 10; /* requests smaller than 2^10 are satisfied from the 2^10 pool. */ for ( i = 1; i < BUFFER_POOL_FIRST; ++i ) { buffers.pool[i] = buffers.pool[BUFFER_POOL_FIRST]; } for ( i = BUFFER_POOL_FIRST + 1; i <= BUFFER_POOL_LAST; ++i ) { buffers.pool[i] = hb_fifo_init(BUFFER_POOL_MAX_ELEMENTS, 1); buffers.pool[i]->buffer_size = 1 << i; } } #if defined(HB_FIFO_DEBUG) static void dump_fifo(hb_fifo_t * f) { hb_buffer_t * b = f->first; if (b) { while (b) { fprintf(stderr, "%p:%d:%d\n", b, b->size, b->alloc); b = b->next; } fprintf(stderr, "\n"); } } static void fifo_list_add( hb_fifo_t * f ) { hb_fifo_t *next = fifo_list.next; fifo_list.next = f; f->next = next; } static void fifo_list_rem( hb_fifo_t * f ) { hb_fifo_t *next, *prev; prev = &fifo_list; next = fifo_list.next; while ( next && next != f ) { prev = next; next = next->next; } if ( next == f ) { prev->next = f->next; } } // These routines are useful for finding and debugging problems // with the fifos and buffer pools static void buffer_pool_validate( hb_fifo_t * f ) { hb_buffer_t *b; hb_lock( f->lock ); b = f->first; while (b) { if (b->alloc != f->buffer_size) { fprintf(stderr, "Invalid buffer pool size! buf %p size %d pool size %d\n", b, b->alloc, f->buffer_size); dump_fifo( f ); *(char*)0 = 1; } b = b->next; } hb_unlock( f->lock ); } static void buffer_pools_validate( void ) { int ii; for ( ii = BUFFER_POOL_FIRST; ii <= BUFFER_POOL_LAST; ++ii ) { buffer_pool_validate( buffers.pool[ii] ); } } void fifo_list_validate( void ) { hb_fifo_t *next = fifo_list.next; hb_fifo_t *m; hb_buffer_t *b, *c; int count; buffer_pools_validate(); while ( next ) { count = 0; hb_lock( next->lock ); b = next->first; // Count the number of entries in this fifo while (b) { c = b->next; // check that the current buffer is not duplicated in this fifo while (c) { if (c == b) { fprintf(stderr, "Duplicate buffer in fifo!\n"); dump_fifo(next); *(char*)0 = 1; } c = c->next; } // check that the current buffer is not duplicated in another fifo m = next->next; while (m) { hb_lock( m->lock ); c = m->first; while (c) { if (c == b) { fprintf(stderr, "Duplicate buffer in another fifo!\n"); dump_fifo(next); *(char*)0 = 1; } c = c->next; } hb_unlock( m->lock ); m = m->next; } count++; b = b->next; } if ( count != next->size ) { fprintf(stderr, "Invalid fifo size! count %d size %d\n", count, next->size); dump_fifo(next); *(char*)0 = 1; } hb_unlock( next->lock ); next = next->next; } } #endif void hb_buffer_pool_free( void ) { int i; int count; int64_t freed = 0; hb_buffer_t *b; hb_lock(buffers.lock); #if defined(HB_BUFFER_DEBUG) hb_deep_log(2, "leaked %d buffers", hb_list_count(buffers.alloc_list)); for (i = 0; i < hb_list_count(buffers.alloc_list); i++) { hb_buffer_t *b = hb_list_item(buffers.alloc_list, i); hb_deep_log(2, "leaked buffer %p type %d size %d alloc %d", b, b->s.type, b->size, b->alloc); } #endif for( i = BUFFER_POOL_FIRST; i <= BUFFER_POOL_LAST; ++i) { count = 0; while( ( b = hb_fifo_get(buffers.pool[i]) ) ) { if( b->data ) { freed += b->alloc; if (b->cl.buffer != NULL) { /* OpenCL */ if (hb_cl_free_mapped_buffer(b->cl.buffer, b->data) == 0) { hb_log("hb_buffer_pool_free: bad free: %p -> buffer %p map %p", b, b->cl.buffer, b->data); } } else { free(b->data); } } free( b ); count++; } if ( count ) { hb_deep_log( 2, "Freed %d buffers of size %d", count, buffers.pool[i]->buffer_size); } } hb_deep_log( 2, "Allocated %"PRId64" bytes of buffers on this pass and Freed %"PRId64" bytes, " "%"PRId64" bytes leaked", buffers.allocated, freed, buffers.allocated - freed); buffers.allocated = 0; hb_unlock(buffers.lock); } static hb_fifo_t *size_to_pool( int size ) { int i; for ( i = BUFFER_POOL_FIRST; i <= BUFFER_POOL_LAST; ++i ) { if ( size <= (1 << i) ) { return buffers.pool[i]; } } return NULL; } hb_buffer_t * hb_buffer_init_internal( int size , int needsMapped ) { hb_buffer_t * b; // Certain libraries (hrm ffmpeg) expect buffers passed to them to // end on certain alignments (ffmpeg is 8). So allocate some extra bytes. // Note that we can't simply align the end of our buffer because // sometimes we feed data to these libraries starting from arbitrary // points within the buffer. int alloc = size + 16; hb_fifo_t *buffer_pool = size_to_pool( alloc ); if( buffer_pool ) { b = hb_fifo_get( buffer_pool ); /* OpenCL */ if (b != NULL && needsMapped && b->cl.buffer == NULL) { // We need a mapped OpenCL buffer and that is not // what we got out of the pool. // Ditch it; it will get replaced with what we need. if (b->data != NULL) { free(b->data); } free(b); b = NULL; } if( b ) { /* * Zero the contents of the buffer, would be nice if we * didn't have to do this. */ uint8_t *data = b->data; /* OpenCL */ cl_mem buffer = b->cl.buffer; cl_event last_event = b->cl.last_event; int loc = b->cl.buffer_location; memset( b, 0, sizeof(hb_buffer_t) ); b->alloc = buffer_pool->buffer_size; b->size = size; b->data = data; b->s.start = AV_NOPTS_VALUE; b->s.stop = AV_NOPTS_VALUE; b->s.renderOffset = AV_NOPTS_VALUE; /* OpenCL */ b->cl.buffer = buffer; b->cl.last_event = last_event; b->cl.buffer_location = loc; #if defined(HB_BUFFER_DEBUG) hb_lock(buffers.lock); hb_list_add(buffers.alloc_list, b); hb_unlock(buffers.lock); #endif return( b ); } } /* * No existing buffers, create a new one */ if( !( b = calloc( sizeof( hb_buffer_t ), 1 ) ) ) { hb_log( "out of memory" ); return NULL; } b->size = size; b->alloc = buffer_pool ? buffer_pool->buffer_size : alloc; if (size) { /* OpenCL */ b->cl.last_event = NULL; b->cl.buffer_location = HOST; /* OpenCL */ if (needsMapped) { int status = hb_cl_create_mapped_buffer(&b->cl.buffer, &b->data, b->alloc); if (!status) { hb_error("Failed to map CL buffer"); free(b); return NULL; } } else { b->cl.buffer = NULL; #if defined( SYS_DARWIN ) || defined( SYS_FREEBSD ) || defined( SYS_MINGW ) b->data = malloc( b->alloc ); #elif defined( SYS_CYGWIN ) /* FIXME */ b->data = malloc( b->alloc + 17 ); #else b->data = memalign( 16, b->alloc ); #endif } if( !b->data ) { hb_log( "out of memory" ); free( b ); return NULL; } hb_lock(buffers.lock); buffers.allocated += b->alloc; hb_unlock(buffers.lock); } b->s.start = AV_NOPTS_VALUE; b->s.stop = AV_NOPTS_VALUE; b->s.renderOffset = AV_NOPTS_VALUE; #if defined(HB_BUFFER_DEBUG) hb_lock(buffers.lock); hb_list_add(buffers.alloc_list, b); hb_unlock(buffers.lock); #endif return b; } hb_buffer_t * hb_buffer_init( int size ) { return hb_buffer_init_internal(size, 0); } void hb_buffer_realloc( hb_buffer_t * b, int size ) { if ( size > b->alloc || b->data == NULL ) { uint32_t orig = b->data != NULL ? b->alloc : 0; size = size_to_pool( size )->buffer_size; b->data = realloc( b->data, size ); b->alloc = size; hb_lock(buffers.lock); buffers.allocated += size - orig; hb_unlock(buffers.lock); } } void hb_buffer_reduce( hb_buffer_t * b, int size ) { if ( size < b->alloc / 8 || b->data == NULL ) { hb_buffer_t * tmp = hb_buffer_init( size ); hb_buffer_swap_copy( b, tmp ); memcpy( b->data, tmp->data, size ); tmp->next = NULL; hb_buffer_close( &tmp ); } } hb_buffer_t * hb_buffer_dup( const hb_buffer_t * src ) { hb_buffer_t * buf; if ( src == NULL ) return NULL; buf = hb_buffer_init( src->size ); if ( buf ) { memcpy( buf->data, src->data, src->size ); buf->s = src->s; buf->f = src->f; if ( buf->s.type == FRAME_BUF ) hb_buffer_init_planes( buf ); } #ifdef USE_QSV memcpy(&buf->qsv_details, &src->qsv_details, sizeof(src->qsv_details)); #endif return buf; } int hb_buffer_copy(hb_buffer_t * dst, const hb_buffer_t * src) { if (src == NULL || dst == NULL) return -1; if ( dst->size < src->size ) return -1; memcpy( dst->data, src->data, src->size ); dst->s = src->s; dst->f = src->f; if (dst->s.type == FRAME_BUF) hb_buffer_init_planes(dst); return 0; } static void hb_buffer_init_planes_internal( hb_buffer_t * b, uint8_t * has_plane ) { uint8_t * plane = b->data; int p; for( p = 0; p < 4; p++ ) { if ( has_plane[p] ) { b->plane[p].data = plane; b->plane[p].stride = hb_image_stride( b->f.fmt, b->f.width, p ); b->plane[p].height_stride = hb_image_height_stride( b->f.fmt, b->f.height, p ); b->plane[p].width = hb_image_width( b->f.fmt, b->f.width, p ); b->plane[p].height = hb_image_height( b->f.fmt, b->f.height, p ); b->plane[p].size = b->plane[p].stride * b->plane[p].height_stride; plane += b->plane[p].size; } } } void hb_buffer_init_planes( hb_buffer_t * b ) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(b->f.fmt); int p; uint8_t has_plane[4] = {0,}; for( p = 0; p < 4; p++ ) { has_plane[desc->comp[p].plane] = 1; } hb_buffer_init_planes_internal( b, has_plane ); } // this routine gets a buffer for an uncompressed picture // with pixel format pix_fmt and dimensions width x height. hb_buffer_t * hb_frame_buffer_init( int pix_fmt, int width, int height ) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); hb_buffer_t * buf; int p; uint8_t has_plane[4] = {0,}; for( p = 0; p < 4; p++ ) { has_plane[desc->comp[p].plane] = 1; } int size = 0; for( p = 0; p < 4; p++ ) { if ( has_plane[p] ) { size += hb_image_stride( pix_fmt, width, p ) * hb_image_height_stride( pix_fmt, height, p ); } } /* OpenCL */ buf = hb_buffer_init_internal(size , hb_use_buffers()); if( buf == NULL ) return NULL; buf->s.type = FRAME_BUF; buf->f.width = width; buf->f.height = height; buf->f.fmt = pix_fmt; hb_buffer_init_planes_internal( buf, has_plane ); return buf; } // this routine reallocs a buffer for an uncompressed YUV420 video frame // with dimensions width x height. void hb_video_buffer_realloc( hb_buffer_t * buf, int width, int height ) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(buf->f.fmt); int p; uint8_t has_plane[4] = {0,}; for( p = 0; p < 4; p++ ) { has_plane[desc->comp[p].plane] = 1; } int size = 0; for( p = 0; p < 4; p++ ) { if ( has_plane[p] ) { size += hb_image_stride( buf->f.fmt, width, p ) * hb_image_height_stride( buf->f.fmt, height, p ); } } hb_buffer_realloc(buf, size ); buf->f.width = width; buf->f.height = height; buf->size = size; hb_buffer_init_planes_internal( buf, has_plane ); } // this routine 'moves' data from src to dst by interchanging 'data', // 'size' & 'alloc' between them and copying the rest of the fields // from src to dst. void hb_buffer_swap_copy( hb_buffer_t *src, hb_buffer_t *dst ) { uint8_t *data = dst->data; int size = dst->size; int alloc = dst->alloc; /* OpenCL */ cl_mem buffer = dst->cl.buffer; cl_event last_event = dst->cl.last_event; int loc = dst->cl.buffer_location; *dst = *src; src->data = data; src->size = size; src->alloc = alloc; /* OpenCL */ src->cl.buffer = buffer; src->cl.last_event = last_event; src->cl.buffer_location = loc; } // Frees the specified buffer list. void hb_buffer_close( hb_buffer_t ** _b ) { hb_buffer_t * b = *_b; while( b ) { hb_buffer_t * next = b->next; hb_fifo_t *buffer_pool = size_to_pool( b->alloc ); b->next = NULL; #if defined(HB_BUFFER_DEBUG) hb_lock(buffers.lock); hb_list_rem(buffers.alloc_list, b); hb_unlock(buffers.lock); #endif // Close any attached subtitle buffers hb_buffer_close( &b->sub ); if( buffer_pool && b->data && !hb_fifo_is_full( buffer_pool ) ) { hb_fifo_push_head( buffer_pool, b ); b = next; continue; } // either the pool is full or this size doesn't use a pool // free the buf if( b->data ) { if (b->cl.buffer != NULL) { /* OpenCL */ if (hb_cl_free_mapped_buffer(b->cl.buffer, b->data) == 0) { hb_log("hb_buffer_pool_free: bad free %p -> buffer %p map %p", b, b->cl.buffer, b->data); } } else { free(b->data); } hb_lock(buffers.lock); buffers.allocated -= b->alloc; hb_unlock(buffers.lock); } free( b ); b = next; } *_b = NULL; } void hb_buffer_move_subs( hb_buffer_t * dst, hb_buffer_t * src ) { // Note that dst takes ownership of the subtitles dst->sub = src->sub; src->sub = NULL; #ifdef USE_QSV memcpy(&dst->qsv_details, &src->qsv_details, sizeof(src->qsv_details)); #endif } hb_image_t * hb_buffer_to_image(hb_buffer_t *buf) { hb_image_t *image = calloc(1, sizeof(hb_image_t)); #if defined( SYS_DARWIN ) || defined( SYS_FREEBSD ) || defined( SYS_MINGW ) image->data = malloc( buf->size ); #elif defined( SYS_CYGWIN ) /* FIXME */ image->data = malloc( buf->size + 17 ); #else image->data = memalign( 16, buf->size ); #endif if (image->data == NULL) { free(image); return NULL; } image->format = buf->f.fmt; image->width = buf->f.width; image->height = buf->f.height; memcpy(image->data, buf->data, buf->size); int p; uint8_t *data = image->data; for (p = 0; p < 4; p++) { image->plane[p].data = data; image->plane[p].width = buf->plane[p].width; image->plane[p].height = buf->plane[p].height; image->plane[p].stride = buf->plane[p].stride; image->plane[p].height_stride = buf->plane[p].height_stride; image->plane[p].size = buf->plane[p].size; data += image->plane[p].size; } return image; } void hb_image_close(hb_image_t **_image) { if (_image == NULL) return; hb_image_t * image = *_image; if (image != NULL) { free(image->data); free(image); *_image = NULL; } } hb_fifo_t * hb_fifo_init( int capacity, int thresh ) { hb_fifo_t * f; f = calloc( sizeof( hb_fifo_t ), 1 ); f->lock = hb_lock_init(); f->cond_full = hb_cond_init(); f->cond_empty = hb_cond_init(); f->capacity = capacity; f->thresh = thresh; f->buffer_size = 0; #if defined(HB_FIFO_DEBUG) // Add the fifo to the global fifo list fifo_list_add( f ); #endif return f; } int hb_fifo_size_bytes( hb_fifo_t * f ) { int ret = 0; hb_buffer_t * link; hb_lock( f->lock ); link = f->first; while ( link ) { ret += link->size; link = link->next; } hb_unlock( f->lock ); return ret; } int hb_fifo_size( hb_fifo_t * f ) { int ret; hb_lock( f->lock ); ret = f->size; hb_unlock( f->lock ); return ret; } int hb_fifo_is_full( hb_fifo_t * f ) { int ret; hb_lock( f->lock ); ret = ( f->size >= f->capacity ); hb_unlock( f->lock ); return ret; } float hb_fifo_percent_full( hb_fifo_t * f ) { float ret; hb_lock( f->lock ); ret = f->size / f->capacity; hb_unlock( f->lock ); return ret; } // Pulls the first packet out of this FIFO, blocking until such a packet is available. // Returns NULL if this FIFO has been closed or flushed. hb_buffer_t * hb_fifo_get_wait( hb_fifo_t * f ) { hb_buffer_t * b; hb_lock( f->lock ); if( f->size < 1 ) { f->wait_empty = 1; hb_cond_timedwait( f->cond_empty, f->lock, FIFO_TIMEOUT ); if( f->size < 1 ) { hb_unlock( f->lock ); return NULL; } } b = f->first; f->first = b->next; b->next = NULL; f->size -= 1; if( f->wait_full && f->size == f->capacity - f->thresh ) { f->wait_full = 0; hb_cond_signal( f->cond_full ); } hb_unlock( f->lock ); return b; } // Pulls a packet out of this FIFO, or returns NULL if no packet is available. hb_buffer_t * hb_fifo_get( hb_fifo_t * f ) { hb_buffer_t * b; hb_lock( f->lock ); if( f->size < 1 ) { hb_unlock( f->lock ); return NULL; } b = f->first; f->first = b->next; b->next = NULL; f->size -= 1; if( f->wait_full && f->size == f->capacity - f->thresh ) { f->wait_full = 0; hb_cond_signal( f->cond_full ); } hb_unlock( f->lock ); return b; } hb_buffer_t * hb_fifo_see_wait( hb_fifo_t * f ) { hb_buffer_t * b; hb_lock( f->lock ); if( f->size < 1 ) { f->wait_empty = 1; hb_cond_timedwait( f->cond_empty, f->lock, FIFO_TIMEOUT ); if( f->size < 1 ) { hb_unlock( f->lock ); return NULL; } } b = f->first; hb_unlock( f->lock ); return b; } // Returns the first packet in the specified FIFO. // If the FIFO is empty, returns NULL. hb_buffer_t * hb_fifo_see( hb_fifo_t * f ) { hb_buffer_t * b; hb_lock( f->lock ); if( f->size < 1 ) { hb_unlock( f->lock ); return NULL; } b = f->first; hb_unlock( f->lock ); return b; } hb_buffer_t * hb_fifo_see2( hb_fifo_t * f ) { hb_buffer_t * b; hb_lock( f->lock ); if( f->size < 2 ) { hb_unlock( f->lock ); return NULL; } b = f->first->next; hb_unlock( f->lock ); return b; } // Waits until the specified FIFO is no longer full or until FIFO_TIMEOUT milliseconds have elapsed. // Returns whether the FIFO is non-full upon return. int hb_fifo_full_wait( hb_fifo_t * f ) { int result; hb_lock( f->lock ); if( f->size >= f->capacity ) { f->wait_full = 1; hb_cond_timedwait( f->cond_full, f->lock, FIFO_TIMEOUT ); } result = ( f->size < f->capacity ); hb_unlock( f->lock ); return result; } // Pushes the specified buffer onto the specified FIFO, // blocking until the FIFO has space available. void hb_fifo_push_wait( hb_fifo_t * f, hb_buffer_t * b ) { if( !b ) { return; } hb_lock( f->lock ); if( f->size >= f->capacity ) { f->wait_full = 1; hb_cond_timedwait( f->cond_full, f->lock, FIFO_TIMEOUT ); } if( f->size > 0 ) { f->last->next = b; } else { f->first = b; } f->last = b; f->size += 1; while( f->last->next ) { f->size += 1; f->last = f->last->next; } if( f->wait_empty && f->size >= 1 ) { f->wait_empty = 0; hb_cond_signal( f->cond_empty ); } hb_unlock( f->lock ); } // Appends the specified packet list to the end of the specified FIFO. void hb_fifo_push( hb_fifo_t * f, hb_buffer_t * b ) { if( !b ) { return; } hb_lock( f->lock ); if( f->size > 0 ) { f->last->next = b; } else { f->first = b; } f->last = b; f->size += 1; while( f->last->next ) { f->size += 1; f->last = f->last->next; } if( f->wait_empty && f->size >= 1 ) { f->wait_empty = 0; hb_cond_signal( f->cond_empty ); } hb_unlock( f->lock ); } // Prepends the specified packet list to the start of the specified FIFO. void hb_fifo_push_head( hb_fifo_t * f, hb_buffer_t * b ) { hb_buffer_t * tmp; uint32_t size = 0; if( !b ) { return; } hb_lock( f->lock ); /* * If there are a chain of buffers prepend the lot */ tmp = b; while( tmp->next ) { tmp = tmp->next; size += 1; } if( f->size > 0 ) { tmp->next = f->first; } else { f->last = tmp; } f->first = b; f->size += ( size + 1 ); hb_unlock( f->lock ); } // Pushes a list of packets onto the specified FIFO as a single element. void hb_fifo_push_list_element( hb_fifo_t *fifo, hb_buffer_t *buffer_list ) { hb_buffer_t *container = hb_buffer_init( 0 ); // XXX: Using an arbitrary hb_buffer_t pointer (other than 'next') // to carry the list inside a single "container" buffer container->sub = buffer_list; hb_fifo_push( fifo, container ); } // Removes a list of packets from the specified FIFO that were stored as a single element. hb_buffer_t *hb_fifo_get_list_element( hb_fifo_t *fifo ) { hb_buffer_t *container = hb_fifo_get( fifo ); // XXX: Using an arbitrary hb_buffer_t pointer (other than 'next') // to carry the list inside a single "container" buffer hb_buffer_t *buffer_list = container->sub; hb_buffer_close( &container ); return buffer_list; } void hb_fifo_close( hb_fifo_t ** _f ) { hb_fifo_t * f = *_f; hb_buffer_t * b; if ( f == NULL ) return; hb_deep_log( 2, "fifo_close: trashing %d buffer(s)", hb_fifo_size( f ) ); while( ( b = hb_fifo_get( f ) ) ) { hb_buffer_close( &b ); } hb_lock_close( &f->lock ); hb_cond_close( &f->cond_empty ); hb_cond_close( &f->cond_full ); #if defined(HB_FIFO_DEBUG) // Remove the fifo from the global fifo list fifo_list_rem( f ); #endif free( f ); *_f = NULL; } void hb_fifo_flush( hb_fifo_t * f ) { hb_buffer_t * b; while( ( b = hb_fifo_get( f ) ) ) { hb_buffer_close( &b ); } hb_lock( f->lock ); hb_cond_signal( f->cond_empty ); hb_cond_signal( f->cond_full ); hb_unlock( f->lock ); } HandBrake-0.10.2/libhb/enclame.c0000664000175200017520000001505012463330511016666 0ustar handbrakehandbrake/* enclame.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "lame/lame.h" int enclameInit( hb_work_object_t *, hb_job_t * ); int enclameWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); void enclameClose( hb_work_object_t * ); hb_work_object_t hb_enclame = { WORK_ENCLAME, "MP3 encoder (libmp3lame)", enclameInit, enclameWork, enclameClose }; struct hb_work_private_s { hb_job_t * job; /* LAME handle */ lame_global_flags * lame; int out_discrete_channels; unsigned long input_samples; unsigned long output_bytes; uint8_t * buf; hb_list_t * list; int64_t pts; }; int enclameInit( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); hb_audio_t * audio = w->audio; w->private_data = pv; pv->job = job; hb_log( "enclame: opening libmp3lame" ); pv->lame = lame_init(); // use ABR lame_set_scale( pv->lame, 32768.0 ); if( audio->config.out.compression_level >= 0 ) { lame_set_quality( pv->lame, audio->config.out.compression_level ); } if( audio->config.out.bitrate > 0 ) { lame_set_VBR( pv->lame, vbr_abr ); lame_set_VBR_mean_bitrate_kbps( pv->lame, audio->config.out.bitrate ); } else if( audio->config.out.quality >= 0 ) { lame_set_brate( pv->lame, 0 ); lame_set_VBR( pv->lame, vbr_default ); lame_set_VBR_quality( pv->lame, audio->config.out.quality ); } lame_set_in_samplerate( pv->lame, audio->config.out.samplerate ); lame_set_out_samplerate( pv->lame, audio->config.out.samplerate ); pv->out_discrete_channels = hb_mixdown_get_discrete_channel_count( audio->config.out.mixdown ); // Lame's default encoding mode is JOINT_STEREO. This subtracts signal // that is "common" to left and right (within some threshold) and encodes // it separately. This improves quality at low bitrates, but hurts // imaging (channel separation) at higher bitrates. So if the bitrate // is suffeciently high, use regular STEREO mode. if ( pv->out_discrete_channels == 1 ) { lame_set_mode( pv->lame, MONO ); lame_set_num_channels( pv->lame, 1 ); } else if ( audio->config.out.bitrate >= 128 ) { lame_set_mode( pv->lame, STEREO ); } lame_init_params( pv->lame ); pv->input_samples = 1152 * pv->out_discrete_channels; pv->output_bytes = LAME_MAXMP3BUFFER; pv->buf = malloc( pv->input_samples * sizeof( float ) ); audio->config.out.samples_per_frame = 1152; pv->list = hb_list_init(); pv->pts = AV_NOPTS_VALUE; return 0; } /*********************************************************************** * Close *********************************************************************** * **********************************************************************/ void enclameClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; lame_close( pv->lame ); hb_list_empty( &pv->list ); free( pv->buf ); free( pv ); w->private_data = NULL; } /*********************************************************************** * Encode *********************************************************************** * **********************************************************************/ static hb_buffer_t * Encode( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; hb_audio_t * audio = w->audio; hb_buffer_t * buf; float samples[2][1152]; uint64_t pts, pos; int i, j; if( hb_list_bytes( pv->list ) < pv->input_samples * sizeof( float ) ) { return NULL; } hb_list_getbytes( pv->list, pv->buf, pv->input_samples * sizeof( float ), &pts, &pos); for( i = 0; i < 1152; i++ ) { for( j = 0; j < pv->out_discrete_channels; j++ ) { samples[j][i] = ((float *) pv->buf)[(pv->out_discrete_channels * i + j)]; } } buf = hb_buffer_init( pv->output_bytes ); buf->s.start = pts + 90000 * pos / pv->out_discrete_channels / sizeof( float ) / audio->config.out.samplerate; buf->s.duration = (double)90000 * 1152 / audio->config.out.samplerate; buf->s.stop = buf->s.start + buf->s.duration; pv->pts = buf->s.stop; buf->size = lame_encode_buffer_float( pv->lame, samples[0], samples[1], 1152, buf->data, LAME_MAXMP3BUFFER ); buf->s.type = AUDIO_BUF; buf->s.frametype = HB_FRAME_AUDIO; if( !buf->size ) { /* Encoding was successful but we got no data. Try to encode more */ hb_buffer_close( &buf ); return Encode( w ); } else if( buf->size < 0 ) { hb_log( "enclame: lame_encode_buffer failed" ); hb_buffer_close( &buf ); return NULL; } return buf; } /*********************************************************************** * Work *********************************************************************** * **********************************************************************/ int enclameWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_audio_t * audio = w->audio; hb_buffer_t * in = *buf_in; hb_buffer_t * buf; if ( (*buf_in)->size <= 0 ) { /* EOF on input - send it downstream & say we're done */ buf = hb_buffer_init( pv->output_bytes ); buf->size = lame_encode_flush( pv->lame, buf->data, LAME_MAXMP3BUFFER ); buf->s.start = pv->pts; buf->s.stop = buf->s.start + 90000 * 1152 / audio->config.out.samplerate; buf->s.type = AUDIO_BUF; buf->s.frametype = HB_FRAME_AUDIO; if( buf->size <= 0 ) { hb_buffer_close( &buf ); } // Add the flushed data *buf_out = buf; // Add the eof if ( buf ) { buf->next = in; } else { *buf_out = in; } *buf_in = NULL; return HB_WORK_DONE; } hb_list_add( pv->list, *buf_in ); *buf_in = NULL; *buf_out = buf = Encode( w ); while( buf ) { buf->next = Encode( w ); buf = buf->next; } return HB_WORK_OK; } HandBrake-0.10.2/libhb/muxavformat.c0000664000175200017520000013122412531124076017640 0ustar handbrakehandbrake/* muxavformat.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include #include "libavformat/avformat.h" #include "libavutil/avstring.h" #include "libavutil/intreadwrite.h" #include "hb.h" #include "lang.h" struct hb_mux_data_s { enum { MUX_TYPE_VIDEO, MUX_TYPE_AUDIO, MUX_TYPE_SUBTITLE } type; AVStream *st; int64_t duration; hb_buffer_t * delay_buf; int64_t prev_chapter_tc; int16_t current_chapter; AVBitStreamFilterContext* bitstream_filter; }; struct hb_mux_object_s { HB_MUX_COMMON; hb_job_t * job; AVFormatContext * oc; AVRational time_base; int ntracks; hb_mux_data_t ** tracks; int64_t chapter_delay; }; enum { META_TITLE, META_ARTIST, META_DIRECTOR, META_COMPOSER, META_RELEASE_DATE, META_COMMENT, META_ALBUM, META_GENRE, META_DESCRIPTION, META_SYNOPSIS, META_LAST }; enum { META_MUX_MP4, META_MUX_MKV, META_MUX_LAST }; const char *metadata_keys[META_LAST][META_MUX_LAST] = { {"title", "TITLE"}, {"artist", "ARTIST"}, {"album_artist", "DIRECTOR"}, {"composer", "COMPOSER"}, {"date", "DATE_RELEASED"}, {"comment", "SUMMARY"}, {"album", NULL}, {"genre", "GENRE"}, {"description", "DESCRIPTION"}, {"synopsis", "SYNOPSIS"} }; static char* lookup_lang_code(int mux, char *iso639_2) { iso639_lang_t *lang; char *out = NULL; switch (mux) { case HB_MUX_AV_MP4: out = iso639_2; break; case HB_MUX_AV_MKV: // MKV lang codes should be ISO-639-2B if it exists, // else ISO-639-2 lang = lang_for_code2( iso639_2 ); out = lang->iso639_2b ? lang->iso639_2b : lang->iso639_2; break; default: break; } return out; } /********************************************************************** * avformatInit ********************************************************************** * Allocates hb_mux_data_t structures, create file and write headers *********************************************************************/ static int avformatInit( hb_mux_object_t * m ) { hb_job_t * job = m->job; hb_audio_t * audio; hb_mux_data_t * track; int meta_mux; int max_tracks; int ii, ret; const char *muxer_name = NULL; uint8_t default_track_flag = 1; uint8_t need_fonts = 0; char *lang; max_tracks = 1 + hb_list_count( job->list_audio ) + hb_list_count( job->list_subtitle ); m->tracks = calloc(max_tracks, sizeof(hb_mux_data_t*)); m->oc = avformat_alloc_context(); if (m->oc == NULL) { hb_error( "Could not initialize avformat context." ); goto error; } AVDictionary * av_opts = NULL; switch (job->mux) { case HB_MUX_AV_MP4: m->time_base.num = 1; m->time_base.den = 90000; if( job->ipod_atom ) muxer_name = "ipod"; else muxer_name = "mp4"; meta_mux = META_MUX_MP4; av_dict_set(&av_opts, "brand", "mp42", 0); if (job->mp4_optimize) av_dict_set(&av_opts, "movflags", "faststart+disable_chpl", 0); else av_dict_set(&av_opts, "movflags", "+disable_chpl", 0); break; case HB_MUX_AV_MKV: // libavformat is essentially hard coded such that it only // works with a timebase of 1/1000 m->time_base.num = 1; m->time_base.den = 1000; muxer_name = "matroska"; meta_mux = META_MUX_MKV; break; default: { hb_error("Invalid Mux %x", job->mux); goto error; } } m->oc->oformat = av_guess_format(muxer_name, NULL, NULL); if(m->oc->oformat == NULL) { hb_error("Could not guess output format %s", muxer_name); goto error; } av_strlcpy(m->oc->filename, job->file, sizeof(m->oc->filename)); ret = avio_open2(&m->oc->pb, job->file, AVIO_FLAG_WRITE, &m->oc->interrupt_callback, NULL); if( ret < 0 ) { hb_error( "avio_open2 failed, errno %d", ret); goto error; } /* Video track */ track = m->tracks[m->ntracks++] = calloc(1, sizeof( hb_mux_data_t ) ); job->mux_data = track; track->type = MUX_TYPE_VIDEO; track->prev_chapter_tc = AV_NOPTS_VALUE; track->st = avformat_new_stream(m->oc, NULL); if (track->st == NULL) { hb_error("Could not initialize video stream"); goto error; } track->st->time_base = m->time_base; avcodec_get_context_defaults3(track->st->codec, NULL); track->st->codec->codec_type = AVMEDIA_TYPE_VIDEO; track->st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; uint8_t *priv_data = NULL; int priv_size = 0; switch (job->vcodec) { case HB_VCODEC_X264: case HB_VCODEC_QSV_H264: track->st->codec->codec_id = AV_CODEC_ID_H264; /* Taken from x264 muxers.c */ priv_size = 5 + 1 + 2 + job->config.h264.sps_length + 1 + 2 + job->config.h264.pps_length; priv_data = av_malloc(priv_size); if (priv_data == NULL) { hb_error("H.264 extradata: malloc failure"); goto error; } priv_data[0] = 1; priv_data[1] = job->config.h264.sps[1]; /* AVCProfileIndication */ priv_data[2] = job->config.h264.sps[2]; /* profile_compat */ priv_data[3] = job->config.h264.sps[3]; /* AVCLevelIndication */ priv_data[4] = 0xff; // nalu size length is four bytes priv_data[5] = 0xe1; // one sps priv_data[6] = job->config.h264.sps_length >> 8; priv_data[7] = job->config.h264.sps_length; memcpy(priv_data+8, job->config.h264.sps, job->config.h264.sps_length); priv_data[8+job->config.h264.sps_length] = 1; // one pps priv_data[9+job->config.h264.sps_length] = job->config.h264.pps_length >> 8; priv_data[10+job->config.h264.sps_length] = job->config.h264.pps_length; memcpy(priv_data+11+job->config.h264.sps_length, job->config.h264.pps, job->config.h264.pps_length ); break; case HB_VCODEC_FFMPEG_MPEG4: track->st->codec->codec_id = AV_CODEC_ID_MPEG4; if (job->config.mpeg4.length != 0) { priv_size = job->config.mpeg4.length; priv_data = av_malloc(priv_size); if (priv_data == NULL) { hb_error("MPEG4 extradata: malloc failure"); goto error; } memcpy(priv_data, job->config.mpeg4.bytes, priv_size); } break; case HB_VCODEC_FFMPEG_MPEG2: track->st->codec->codec_id = AV_CODEC_ID_MPEG2VIDEO; if (job->config.mpeg4.length != 0) { priv_size = job->config.mpeg4.length; priv_data = av_malloc(priv_size); if (priv_data == NULL) { hb_error("MPEG2 extradata: malloc failure"); goto error; } memcpy(priv_data, job->config.mpeg4.bytes, priv_size); } break; case HB_VCODEC_FFMPEG_VP8: track->st->codec->codec_id = AV_CODEC_ID_VP8; priv_data = NULL; priv_size = 0; break; case HB_VCODEC_THEORA: { track->st->codec->codec_id = AV_CODEC_ID_THEORA; int size = 0; ogg_packet *ogg_headers[3]; for (ii = 0; ii < 3; ii++) { ogg_headers[ii] = (ogg_packet *)job->config.theora.headers[ii]; size += ogg_headers[ii]->bytes + 2; } priv_size = size; priv_data = av_malloc(priv_size); if (priv_data == NULL) { hb_error("Theora extradata: malloc failure"); goto error; } size = 0; for(ii = 0; ii < 3; ii++) { AV_WB16(priv_data + size, ogg_headers[ii]->bytes); size += 2; memcpy(priv_data+size, ogg_headers[ii]->packet, ogg_headers[ii]->bytes); size += ogg_headers[ii]->bytes; } } break; case HB_VCODEC_X265: track->st->codec->codec_id = AV_CODEC_ID_HEVC; if (job->config.h265.headers_length > 0) { priv_size = job->config.h265.headers_length; priv_data = av_malloc(priv_size); if (priv_data == NULL) { hb_error("H.265 extradata: malloc failure"); goto error; } memcpy(priv_data, job->config.h265.headers, priv_size); } break; default: hb_error("muxavformat: Unknown video codec: %x", job->vcodec); goto error; } track->st->codec->extradata = priv_data; track->st->codec->extradata_size = priv_size; if (job->anamorphic.mode > 0) { track->st->sample_aspect_ratio.num = job->anamorphic.par_width; track->st->sample_aspect_ratio.den = job->anamorphic.par_height; track->st->codec->sample_aspect_ratio.num = job->anamorphic.par_width; track->st->codec->sample_aspect_ratio.den = job->anamorphic.par_height; } else { track->st->sample_aspect_ratio.num = 1; track->st->sample_aspect_ratio.den = 1; track->st->codec->sample_aspect_ratio.num = 1; track->st->codec->sample_aspect_ratio.den = 1; } track->st->codec->width = job->width; track->st->codec->height = job->height; track->st->disposition |= AV_DISPOSITION_DEFAULT; int vrate_base, vrate; if( job->pass == 2 ) { hb_interjob_t * interjob = hb_interjob_get( job->h ); vrate_base = interjob->vrate_base; vrate = interjob->vrate; } else { vrate_base = job->vrate_base; vrate = job->vrate; } // If the vrate is 27000000, there's a good chance this is // a standard rate that we have in our hb_video_rates table. // Because of rounding errors and approximations made while // measuring framerate, the actual value may not be exact. So // we look for rates that are "close" and make an adjustment // to fps.den. if (vrate == 27000000) { const hb_rate_t *video_framerate = NULL; while ((video_framerate = hb_video_framerate_get_next(video_framerate)) != NULL) { if (abs(vrate_base - video_framerate->rate) < 10) { vrate_base = video_framerate->rate; break; } } } hb_reduce(&vrate_base, &vrate, vrate_base, vrate); if (job->mux == HB_MUX_AV_MP4) { // libavformat mp4 muxer requires that the codec time_base have the // same denominator as the stream time_base, it uses it for the // mdhd timescale. double scale = (double)track->st->time_base.den / vrate; track->st->codec->time_base.den = track->st->time_base.den; track->st->codec->time_base.num = vrate_base * scale; } else { track->st->codec->time_base.num = vrate_base; track->st->codec->time_base.den = vrate; } /* add the audio tracks */ for(ii = 0; ii < hb_list_count( job->list_audio ); ii++ ) { audio = hb_list_item( job->list_audio, ii ); track = m->tracks[m->ntracks++] = calloc(1, sizeof( hb_mux_data_t ) ); audio->priv.mux_data = track; track->type = MUX_TYPE_AUDIO; track->st = avformat_new_stream(m->oc, NULL); if (track->st == NULL) { hb_error("Could not initialize audio stream"); goto error; } avcodec_get_context_defaults3(track->st->codec, NULL); track->st->codec->codec_type = AVMEDIA_TYPE_AUDIO; track->st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; if (job->mux == HB_MUX_AV_MP4) { track->st->codec->time_base.num = audio->config.out.samples_per_frame; track->st->codec->time_base.den = audio->config.out.samplerate; track->st->time_base.num = 1; track->st->time_base.den = audio->config.out.samplerate; } else { track->st->codec->time_base = m->time_base; } priv_data = NULL; priv_size = 0; switch (audio->config.out.codec & HB_ACODEC_MASK) { case HB_ACODEC_DCA: case HB_ACODEC_DCA_HD: track->st->codec->codec_id = AV_CODEC_ID_DTS; break; case HB_ACODEC_AC3: track->st->codec->codec_id = AV_CODEC_ID_AC3; break; case HB_ACODEC_LAME: case HB_ACODEC_MP3: track->st->codec->codec_id = AV_CODEC_ID_MP3; break; case HB_ACODEC_VORBIS: { track->st->codec->codec_id = AV_CODEC_ID_VORBIS; int jj, size = 0; ogg_packet *ogg_headers[3]; for (jj = 0; jj < 3; jj++) { ogg_headers[jj] = (ogg_packet *)audio->priv.config.vorbis.headers[jj]; size += ogg_headers[jj]->bytes + 2; } priv_size = size; priv_data = av_malloc(priv_size); if (priv_data == NULL) { hb_error("Vorbis extradata: malloc failure"); goto error; } size = 0; for(jj = 0; jj < 3; jj++) { AV_WB16(priv_data + size, ogg_headers[jj]->bytes); size += 2; memcpy(priv_data+size, ogg_headers[jj]->packet, ogg_headers[jj]->bytes); size += ogg_headers[jj]->bytes; } } break; case HB_ACODEC_FFFLAC: case HB_ACODEC_FFFLAC24: track->st->codec->codec_id = AV_CODEC_ID_FLAC; if (audio->priv.config.extradata.length) { priv_size = audio->priv.config.extradata.length; priv_data = av_malloc(priv_size); if (priv_data == NULL) { hb_error("FLAC extradata: malloc failure"); goto error; } memcpy(priv_data, audio->priv.config.extradata.bytes, audio->priv.config.extradata.length); } break; case HB_ACODEC_FFAAC: case HB_ACODEC_CA_AAC: case HB_ACODEC_CA_HAAC: case HB_ACODEC_FDK_AAC: case HB_ACODEC_FDK_HAAC: track->st->codec->codec_id = AV_CODEC_ID_AAC; // TODO: fix AAC in TS parsing. We need to fill // extradata with AAC config. Some players will play // an AAC stream that is missing extradata and some // will not. // // libav mkv muxer expects there to be extradata for // AAC and will crash if it is NULL. So allocate extra // byte so that av_malloc does not return NULL when length // is 0. priv_size = audio->priv.config.extradata.length; priv_data = av_malloc(priv_size + 1); if (priv_data == NULL) { hb_error("AAC extradata: malloc failure"); goto error; } memcpy(priv_data, audio->priv.config.extradata.bytes, audio->priv.config.extradata.length); // AAC from pass-through source may be ADTS. // Therefore inserting "aac_adtstoasc" bitstream filter is // preferred. // The filter does nothing for non-ADTS bitstream. if (audio->config.out.codec == HB_ACODEC_AAC_PASS) { track->bitstream_filter = av_bitstream_filter_init("aac_adtstoasc"); } break; default: hb_error("muxavformat: Unknown audio codec: %x", audio->config.out.codec); goto error; } track->st->codec->extradata = priv_data; track->st->codec->extradata_size = priv_size; if( default_track_flag ) { track->st->disposition |= AV_DISPOSITION_DEFAULT; default_track_flag = 0; } lang = lookup_lang_code(job->mux, audio->config.lang.iso639_2 ); if (lang != NULL) { av_dict_set(&track->st->metadata, "language", lang, 0); } track->st->codec->sample_rate = audio->config.out.samplerate; if (audio->config.out.codec & HB_ACODEC_PASS_FLAG) { track->st->codec->channels = av_get_channel_layout_nb_channels(audio->config.in.channel_layout); track->st->codec->channel_layout = audio->config.in.channel_layout; } else { track->st->codec->channels = hb_mixdown_get_discrete_channel_count(audio->config.out.mixdown); track->st->codec->channel_layout = hb_ff_mixdown_xlat(audio->config.out.mixdown, NULL); } char *name; if (audio->config.out.name == NULL) { switch (track->st->codec->channels) { case 1: name = "Mono"; break; case 2: name = "Stereo"; break; default: name = "Surround"; break; } } else { name = audio->config.out.name; } // Set audio track title av_dict_set(&track->st->metadata, "title", name, 0); if (job->mux == HB_MUX_AV_MP4) { // Some software (MPC, mediainfo) use hdlr description // for track title av_dict_set(&track->st->metadata, "handler", name, 0); } } char * subidx_fmt = "size: %dx%d\n" "org: %d, %d\n" "scale: 100%%, 100%%\n" "alpha: 100%%\n" "smooth: OFF\n" "fadein/out: 50, 50\n" "align: OFF at LEFT TOP\n" "time offset: 0\n" "forced subs: %s\n" "palette: %06x, %06x, %06x, %06x, %06x, %06x, " "%06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x\n" "custom colors: OFF, tridx: 0000, " "colors: 000000, 000000, 000000, 000000\n"; int subtitle_default = -1; for( ii = 0; ii < hb_list_count( job->list_subtitle ); ii++ ) { hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, ii ); if( subtitle->config.dest == PASSTHRUSUB ) { if ( subtitle->config.default_track ) subtitle_default = ii; } } // Quicktime requires that at least one subtitle is enabled, // else it doesn't show any of the subtitles. // So check to see if any of the subtitles are flagged to be // the defualt. The default will the the enabled track, else // enable the first track. if (job->mux == HB_MUX_AV_MP4 && subtitle_default == -1) { subtitle_default = 0; } for( ii = 0; ii < hb_list_count( job->list_subtitle ); ii++ ) { hb_subtitle_t * subtitle; uint32_t rgb[16]; char subidx[2048]; int len; subtitle = hb_list_item( job->list_subtitle, ii ); if (subtitle->config.dest != PASSTHRUSUB) continue; track = m->tracks[m->ntracks++] = calloc(1, sizeof( hb_mux_data_t ) ); subtitle->mux_data = track; track->type = MUX_TYPE_SUBTITLE; track->st = avformat_new_stream(m->oc, NULL); if (track->st == NULL) { hb_error("Could not initialize subtitle stream"); goto error; } avcodec_get_context_defaults3(track->st->codec, NULL); track->st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; track->st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; track->st->time_base = m->time_base; track->st->codec->time_base = m->time_base; track->st->codec->width = subtitle->width; track->st->codec->height = subtitle->height; priv_data = NULL; priv_size = 0; switch (subtitle->source) { case VOBSUB: { int jj; track->st->codec->codec_id = AV_CODEC_ID_DVD_SUBTITLE; for (jj = 0; jj < 16; jj++) rgb[jj] = hb_yuv2rgb(subtitle->palette[jj]); len = snprintf(subidx, 2048, subidx_fmt, subtitle->width, subtitle->height, 0, 0, "OFF", rgb[0], rgb[1], rgb[2], rgb[3], rgb[4], rgb[5], rgb[6], rgb[7], rgb[8], rgb[9], rgb[10], rgb[11], rgb[12], rgb[13], rgb[14], rgb[15]); priv_size = len + 1; priv_data = av_malloc(priv_size); if (priv_data == NULL) { hb_error("VOBSUB extradata: malloc failure"); goto error; } memcpy(priv_data, subidx, priv_size); } break; case PGSSUB: { track->st->codec->codec_id = AV_CODEC_ID_HDMV_PGS_SUBTITLE; } break; case CC608SUB: case CC708SUB: case TX3GSUB: case SRTSUB: case UTF8SUB: case SSASUB: { if (job->mux == HB_MUX_AV_MP4) { track->st->codec->codec_id = AV_CODEC_ID_MOV_TEXT; } else { track->st->codec->codec_id = AV_CODEC_ID_SSA; need_fonts = 1; if (subtitle->extradata_size) { priv_size = subtitle->extradata_size; priv_data = av_malloc(priv_size); if (priv_data == NULL) { hb_error("SSA extradata: malloc failure"); goto error; } memcpy(priv_data, subtitle->extradata, priv_size); } } } break; default: continue; } if (track->st->codec->codec_id == AV_CODEC_ID_MOV_TEXT) { // Build codec extradata for tx3g. // If we were using a libav codec to generate this data // this would (or should) be done for us. uint8_t properties[] = { 0x00, 0x00, 0x00, 0x00, // Display Flags 0x01, // Horiz. Justification 0xff, // Vert. Justification 0x00, 0x00, 0x00, 0xff, // Bg color 0x00, 0x00, 0x00, 0x00, // Default text box 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved 0x00, 0x01, // Font ID 0x00, // Font face 0x18, // Font size 0xff, 0xff, 0xff, 0xff, // Fg color // Font table: 0x00, 0x00, 0x00, 0x12, // Font table size 'f','t','a','b', // Tag 0x00, 0x01, // Count 0x00, 0x01, // Font ID 0x05, // Font name length 'A','r','i','a','l' // Font name }; int width, height = 60; if (job->anamorphic.mode) width = job->width * ((float)job->anamorphic.par_width / job->anamorphic.par_height); else width = job->width; track->st->codec->width = width; track->st->codec->height = height; properties[14] = height >> 8; properties[15] = height & 0xff; properties[16] = width >> 8; properties[17] = width & 0xff; priv_size = sizeof(properties); priv_data = av_malloc(priv_size); if (priv_data == NULL) { hb_error("TX3G extradata: malloc failure"); goto error; } memcpy(priv_data, properties, priv_size); } track->st->codec->extradata = priv_data; track->st->codec->extradata_size = priv_size; if ( ii == subtitle_default ) { track->st->disposition |= AV_DISPOSITION_DEFAULT; } lang = lookup_lang_code(job->mux, subtitle->iso639_2 ); if (lang != NULL) { av_dict_set(&track->st->metadata, "language", lang, 0); } } if (need_fonts) { hb_list_t * list_attachment = job->list_attachment; int i; for ( i = 0; i < hb_list_count(list_attachment); i++ ) { hb_attachment_t * attachment = hb_list_item( list_attachment, i ); if (attachment->type == FONT_TTF_ATTACH && attachment->size > 0) { AVStream *st = avformat_new_stream(m->oc, NULL); if (st == NULL) { hb_error("Could not initialize attachment stream"); goto error; } avcodec_get_context_defaults3(st->codec, NULL); st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT; st->codec->codec_id = AV_CODEC_ID_TTF; priv_size = attachment->size; priv_data = av_malloc(priv_size); if (priv_data == NULL) { hb_error("Font extradata: malloc failure"); goto error; } memcpy(priv_data, attachment->data, priv_size); st->codec->extradata = priv_data; st->codec->extradata_size = priv_size; av_dict_set(&st->metadata, "filename", attachment->name, 0); } } } if( job->metadata ) { hb_metadata_t *md = job->metadata; hb_deep_log(2, "Writing Metadata to output file..."); if (md->name && metadata_keys[META_TITLE][meta_mux] != NULL) { av_dict_set(&m->oc->metadata, metadata_keys[META_TITLE][meta_mux], md->name, 0); } if (md->artist && metadata_keys[META_ARTIST][meta_mux] != NULL) { av_dict_set(&m->oc->metadata, metadata_keys[META_ARTIST][meta_mux], md->artist, 0); } if (md->album_artist && metadata_keys[META_DIRECTOR][meta_mux] != NULL) { av_dict_set(&m->oc->metadata, metadata_keys[META_DIRECTOR][meta_mux], md->album_artist, 0); } if (md->composer && metadata_keys[META_COMPOSER][meta_mux] != NULL) { av_dict_set(&m->oc->metadata, metadata_keys[META_COMPOSER][meta_mux], md->composer, 0); } if (md->release_date && metadata_keys[META_RELEASE_DATE][meta_mux] != NULL) { av_dict_set(&m->oc->metadata, metadata_keys[META_RELEASE_DATE][meta_mux], md->release_date, 0); } if (md->comment && metadata_keys[META_COMMENT][meta_mux] != NULL) { av_dict_set(&m->oc->metadata, metadata_keys[META_COMMENT][meta_mux], md->comment, 0); } if (!md->name && md->album && metadata_keys[META_ALBUM][meta_mux] != NULL) { av_dict_set(&m->oc->metadata, metadata_keys[META_ALBUM][meta_mux], md->album, 0); } if (md->genre && metadata_keys[META_GENRE][meta_mux] != NULL) { av_dict_set(&m->oc->metadata, metadata_keys[META_GENRE][meta_mux], md->genre, 0); } if (md->description && metadata_keys[META_DESCRIPTION][meta_mux] != NULL) { av_dict_set(&m->oc->metadata, metadata_keys[META_DESCRIPTION][meta_mux], md->description, 0); } if (md->long_description && metadata_keys[META_SYNOPSIS][meta_mux] != NULL) { av_dict_set(&m->oc->metadata, metadata_keys[META_SYNOPSIS][meta_mux], md->long_description, 0); } } char tool_string[80]; snprintf(tool_string, sizeof(tool_string), "HandBrake %s %i", HB_PROJECT_VERSION, HB_PROJECT_BUILD); av_dict_set(&m->oc->metadata, "encoding_tool", tool_string, 0); time_t now = time(NULL); struct tm * now_utc = gmtime(&now); char now_8601[24]; strftime(now_8601, sizeof(now_8601), "%FT%TZ", now_utc); av_dict_set(&m->oc->metadata, "creation_time", now_8601, 0); ret = avformat_write_header(m->oc, &av_opts); if( ret < 0 ) { av_dict_free( &av_opts ); hb_error( "muxavformat: avformat_write_header failed!"); goto error; } AVDictionaryEntry *t = NULL; while( ( t = av_dict_get( av_opts, "", t, AV_DICT_IGNORE_SUFFIX ) ) ) { hb_log( "muxavformat: Unknown option %s", t->key ); } av_dict_free( &av_opts ); return 0; error: free(job->mux_data); job->mux_data = NULL; avformat_free_context(m->oc); *job->done_error = HB_ERROR_INIT; *job->die = 1; return -1; } static int add_chapter(hb_mux_object_t *m, int64_t start, int64_t end, char * title) { AVChapter *chap; AVChapter **chapters; int nchap = m->oc->nb_chapters; nchap++; chapters = av_realloc(m->oc->chapters, nchap * sizeof(AVChapter*)); if (chapters == NULL) { hb_error("chapter array: malloc failure"); return -1; } chap = av_mallocz(sizeof(AVChapter)); if (chap == NULL) { hb_error("chapter: malloc failure"); return -1; } m->oc->chapters = chapters; m->oc->chapters[nchap-1] = chap; m->oc->nb_chapters = nchap; chap->id = nchap; chap->time_base = m->time_base; // libav does not currently have a good way to deal with chapters and // delayed stream timestamps. It makes no corrections to the chapter // track. A patch to libav would touch a lot of things, so for now, // work around the issue here. chap->start = start + m->chapter_delay; chap->end = end; av_dict_set(&chap->metadata, "title", title, 0); return 0; } static int avformatMux(hb_mux_object_t *m, hb_mux_data_t *track, hb_buffer_t *buf) { AVPacket pkt; int64_t dts, pts, duration = AV_NOPTS_VALUE; hb_job_t *job = m->job; uint8_t sub_out[2048]; if (track->type == MUX_TYPE_VIDEO && track->prev_chapter_tc == AV_NOPTS_VALUE) { // Chapter timestamps are biased the same as video timestamps. // This needs to be reflected in the initial chapter timestamp. // // TODO: Don't assume the first chapter is at 0. Pass the first // chapter through the pipeline instead of dropping it as we // currently do. m->chapter_delay = av_rescale_q(m->job->config.h264.init_delay, (AVRational){1,90000}, track->st->time_base); track->prev_chapter_tc = -m->chapter_delay; } // We only compute dts duration for MP4 files if (track->type == MUX_TYPE_VIDEO && (job->mux & HB_MUX_MASK_MP4)) { hb_buffer_t * tmp; // delay by one frame so that we can compute duration properly. tmp = track->delay_buf; track->delay_buf = buf; buf = tmp; } if (buf == NULL) return 0; if (buf->s.renderOffset == AV_NOPTS_VALUE) { dts = av_rescale_q(buf->s.start, (AVRational){1,90000}, track->st->time_base); } else { dts = av_rescale_q(buf->s.renderOffset, (AVRational){1,90000}, track->st->time_base); } pts = av_rescale_q(buf->s.start, (AVRational){1,90000}, track->st->time_base); if (track->type == MUX_TYPE_VIDEO && track->delay_buf != NULL) { int64_t delayed_dts; delayed_dts = av_rescale_q(track->delay_buf->s.renderOffset, (AVRational){1,90000}, track->st->time_base); duration = delayed_dts - dts; } if (duration < 0 && buf->s.duration > 0) { duration = av_rescale_q(buf->s.duration, (AVRational){1,90000}, track->st->time_base); } if (duration < 0) { // There is a possiblility that some subtitles get through the pipeline // without ever discovering their true duration. Make the duration // 10 seconds in this case. Unless they are PGS subs which should // have zero duration. if (track->type == MUX_TYPE_SUBTITLE && track->st->codec->codec_id != AV_CODEC_ID_HDMV_PGS_SUBTITLE) duration = av_rescale_q(10, (AVRational){1,1}, track->st->time_base); else duration = 0; } // Theora can generate 0 length output for duplicate frames. // Since we use 0 length buffers to indicate end of stream, we // can't allow 0 lenth buffers. // // As a work-around, always allocate an extra byte for theora buffers. // Remove this extra byte here. // // This is fixed correctly in svn trunk by using a end of stream flag // instead of 0 length buffer. if (track->type == MUX_TYPE_VIDEO && job->vcodec == HB_VCODEC_THEORA) { buf->size--; } av_init_packet(&pkt); pkt.data = buf->data; pkt.size = buf->size; pkt.dts = dts; pkt.pts = pts; pkt.duration = duration; if (track->type == MUX_TYPE_VIDEO && ((job->vcodec & HB_VCODEC_H264_MASK) || (job->vcodec & HB_VCODEC_FFMPEG_MASK))) { if (buf->s.frametype == HB_FRAME_IDR) pkt.flags |= AV_PKT_FLAG_KEY; } else if (buf->s.frametype & HB_FRAME_KEY) { pkt.flags |= AV_PKT_FLAG_KEY; } switch (track->type) { case MUX_TYPE_VIDEO: { if (job->chapter_markers && buf->s.new_chap) { hb_chapter_t *chapter; // reached chapter N, write marker for chapter N-1 // we don't know the end time of chapter N-1 till we receive // chapter N. So we are always writing the previous chapter // mark. track->current_chapter = buf->s.new_chap - 1; // chapter numbers start at 1, but the list starts at 0 chapter = hb_list_item(job->list_chapter, track->current_chapter - 1); // make sure we're not writing a chapter that has 0 length if (chapter != NULL && track->prev_chapter_tc < pkt.pts) { char title[1024]; if (chapter->title != NULL) { snprintf(title, 1023, "%s", chapter->title); } else { snprintf(title, 1023, "Chapter %d", track->current_chapter); } add_chapter(m, track->prev_chapter_tc, pkt.pts, title); } track->prev_chapter_tc = pkt.pts; } } break; case MUX_TYPE_SUBTITLE: { if (job->mux == HB_MUX_AV_MP4) { /* Write an empty sample */ if ( track->duration < pts ) { AVPacket empty_pkt; uint8_t empty[2] = {0,0}; av_init_packet(&empty_pkt); empty_pkt.data = empty; empty_pkt.size = 2; empty_pkt.dts = track->duration; empty_pkt.pts = track->duration; empty_pkt.duration = pts - duration; empty_pkt.convergence_duration = empty_pkt.duration; empty_pkt.stream_index = track->st->index; int ret = av_interleaved_write_frame(m->oc, &empty_pkt); if (ret < 0) { char errstr[64]; av_strerror(ret, errstr, sizeof(errstr)); hb_error("avformatMux: track %d, av_interleaved_write_frame failed with error '%s' (empty_pkt)", track->st->index, errstr); *job->done_error = HB_ERROR_UNKNOWN; *job->die = 1; return -1; } } if (track->st->codec->codec_id == AV_CODEC_ID_MOV_TEXT) { uint8_t styleatom[2048];; uint16_t stylesize = 0; uint8_t buffer[2048]; uint16_t buffersize = 0; *buffer = '\0'; /* * Copy the subtitle into buffer stripping markup and creating * style atoms for them. */ hb_muxmp4_process_subtitle_style( buf->data, buffer, styleatom, &stylesize ); buffersize = strlen((char*)buffer); /* Write the subtitle sample */ memcpy( sub_out + 2, buffer, buffersize ); memcpy( sub_out + 2 + buffersize, styleatom, stylesize); sub_out[0] = ( buffersize >> 8 ) & 0xff; sub_out[1] = buffersize & 0xff; pkt.data = sub_out; pkt.size = buffersize + stylesize + 2; } } if (track->st->codec->codec_id == AV_CODEC_ID_SSA && job->mux == HB_MUX_AV_MKV) { // avformat requires the this additional information // which it parses and then strips away int start_hh, start_mm, start_ss, start_ms; int stop_hh, stop_mm, stop_ss, stop_ms, layer; char *ssa; start_hh = buf->s.start / (90000 * 60 * 60); start_mm = (buf->s.start / (90000 * 60)) % 60; start_ss = (buf->s.start / 90000) % 60; start_ms = (buf->s.start / 900) % 100; stop_hh = buf->s.stop / (90000 * 60 * 60); stop_mm = (buf->s.stop / (90000 * 60)) % 60; stop_ss = (buf->s.stop / 90000) % 60; stop_ms = (buf->s.stop / 900) % 100; // Skip the read-order field ssa = strchr((char*)buf->data, ','); if (ssa != NULL) ssa++; // Skip the layer field layer = strtol(ssa, NULL, 10); ssa = strchr(ssa, ','); if (ssa != NULL) ssa++; sprintf((char*)sub_out, "Dialogue: %d,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s", layer, start_hh, start_mm, start_ss, start_ms, stop_hh, stop_mm, stop_ss, stop_ms, ssa); pkt.data = sub_out; pkt.size = strlen((char*)sub_out) + 1; } pkt.convergence_duration = pkt.duration; } break; case MUX_TYPE_AUDIO: default: break; } track->duration = pts + pkt.duration; if (track->bitstream_filter) { av_bitstream_filter_filter(track->bitstream_filter, track->st->codec, NULL, &pkt.data, &pkt.size, pkt.data, pkt.size, 0); } pkt.stream_index = track->st->index; int ret = av_interleaved_write_frame(m->oc, &pkt); // Many avformat muxer functions do not check the error status // of the AVIOContext. So we need to check it ourselves to detect // write errors (like disk full condition). if (ret < 0 || m->oc->pb->error != 0) { char errstr[64]; av_strerror(ret < 0 ? ret : m->oc->pb->error, errstr, sizeof(errstr)); hb_error("avformatMux: track %d, av_interleaved_write_frame failed with error '%s'", track->st->index, errstr); *job->done_error = HB_ERROR_UNKNOWN; *job->die = 1; return -1; } hb_buffer_close( &buf ); return 0; } static int avformatEnd(hb_mux_object_t *m) { hb_job_t *job = m->job; hb_mux_data_t *track = job->mux_data; if( !job->mux_data ) { /* * We must have failed to create the file in the first place. */ return 0; } // Flush any delayed frames int ii; for (ii = 0; ii < m->ntracks; ii++) { avformatMux(m, m->tracks[ii], NULL); if (m->tracks[ii]->bitstream_filter) { av_bitstream_filter_close(m->tracks[ii]->bitstream_filter); } } if (job->chapter_markers) { hb_chapter_t *chapter; // get the last chapter chapter = hb_list_item(job->list_chapter, track->current_chapter++); // only write the last chapter marker if it lasts at least 1.5 second if (chapter != NULL && chapter->duration > 135000LL) { char title[1024]; if (chapter->title != NULL) { snprintf(title, 1023, "%s", chapter->title); } else { snprintf(title, 1023, "Chapter %d", track->current_chapter); } add_chapter(m, track->prev_chapter_tc, track->duration, title); } } // Update and track private data that can change during // encode. for(ii = 0; ii < hb_list_count( job->list_audio ); ii++) { AVStream *st; hb_audio_t * audio; audio = hb_list_item(job->list_audio, ii); st = audio->priv.mux_data->st; switch (audio->config.out.codec & HB_ACODEC_MASK) { case HB_ACODEC_FFFLAC: case HB_ACODEC_FFFLAC24: if( audio->priv.config.extradata.length ) { uint8_t *priv_data; int priv_size; priv_size = audio->priv.config.extradata.length; priv_data = av_realloc(st->codec->extradata, priv_size); if (priv_data == NULL) { break; } memcpy(priv_data, audio->priv.config.extradata.bytes, audio->priv.config.extradata.length); st->codec->extradata = priv_data; st->codec->extradata_size = priv_size; } break; default: break; } } av_write_trailer(m->oc); avio_close(m->oc->pb); avformat_free_context(m->oc); free(m->tracks); m->oc = NULL; return 0; } hb_mux_object_t * hb_mux_avformat_init( hb_job_t * job ) { hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 ); m->init = avformatInit; m->mux = avformatMux; m->end = avformatEnd; m->job = job; return m; } HandBrake-0.10.2/libhb/demuxmpeg.c0000664000175200017520000003271212463330511017261 0ustar handbrakehandbrake/* demuxmpeg.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" static inline void check_mpeg_scr( hb_psdemux_t *state, int64_t scr, int tol ) { /* * This section of code implements the timing model of * the "Standard Target Decoder" (STD) of the MPEG2 standard * (specified in ISO 13818-1 sections 2.4.2, 2.5.2 & Annex D). * The STD removes and corrects for clock discontinuities so * that the time stamps on the video, audio & other media * streams can be used for cross-media synchronization. To do * this the STD has its own timestamp value, the System Clock * Reference or SCR, in the PACK header. Clock discontinuities * are detected using the SCR & and the adjustment needed * to correct post-discontinuity timestamps to be contiguous * with pre-discontinuity timestamps is computed from pre- and * post-discontinuity values of the SCR. Then this adjustment * is applied to every media timestamp (PTS). * * ISO 13818-1 says there must be an SCR at least every 700ms * (100ms for Transport Streams) so if the difference between * this SCR & the previous is >700ms it's a discontinuity. * If the difference is negative it's non-physical (time doesn't * go backward) and must also be a discontinuity. When we find a * discontinuity we adjust the scr_offset so that the SCR of the * new packet lines up with that of the previous packet. */ // we declare a discontinuity if there's a gap of more than // 'tol'ms between the last scr & this or if this scr goes back // by more than half a frame time. int64_t scr_delta = scr - state->last_scr; if (state->last_scr == AV_NOPTS_VALUE || scr_delta > 90*tol || scr_delta < -90*10) { ++state->scr_changes; state->last_pts = AV_NOPTS_VALUE; } state->last_scr = scr; } static inline void save_chap( hb_psdemux_t *state, hb_buffer_t *buf ) { if ( state && buf->s.new_chap ) { state->new_chap = buf->s.new_chap; buf->s.new_chap = 0; } } static inline void restore_chap( hb_psdemux_t *state, hb_buffer_t *buf ) { if ( state ) { buf->s.new_chap = state->new_chap; state->new_chap = 0; } } /* Basic MPEG demuxer */ void hb_demux_dvd_ps( hb_buffer_t * buf, hb_list_t * list_es, hb_psdemux_t* state ) { hb_buffer_t * buf_es; int pos = 0; while ( buf ) { save_chap( state, buf ); #define d (buf->data) /* pack_header */ if( d[pos] != 0 || d[pos+1] != 0 || d[pos+2] != 0x1 || d[pos+3] != 0xBA ) { hb_log( "hb_demux_ps: not a PS packet (%02x%02x%02x%02x)", d[pos], d[pos+1], d[pos+2], d[pos+3] ); hb_buffer_t *tmp = buf->next; buf->next = NULL; hb_buffer_close( &buf ); buf = tmp; continue; } pos += 4; /* pack_start_code */ if ( state ) { /* extract the system clock reference (scr) */ int64_t scr = ((uint64_t)(d[pos] & 0x38) << 27) | ((uint64_t)(d[pos] & 0x03) << 28) | ((uint64_t)(d[pos+1]) << 20) | ((uint64_t)(d[pos+2] >> 3) << 15) | ((uint64_t)(d[pos+2] & 3) << 13) | ((uint64_t)(d[pos+3]) << 5) | (d[pos+4] >> 3); check_mpeg_scr( state, scr, 700 ); } pos += 9; /* pack_header */ pos += 1 + ( d[pos] & 0x7 ); /* stuffing bytes */ /* system_header */ if( d[pos] == 0 && d[pos+1] == 0 && d[pos+2] == 0x1 && d[pos+3] == 0xBB ) { int header_length; pos += 4; /* system_header_start_code */ header_length = ( d[pos] << 8 ) + d[pos+1]; pos += 2 + header_length; } /* pes */ while( pos + 6 < buf->size && d[pos] == 0 && d[pos+1] == 0 && d[pos+2] == 0x1 ) { int id; int pes_packet_length; int pes_packet_end; int pes_header_d_length; int pes_header_end; int has_pts; int64_t pts = AV_NOPTS_VALUE, dts = AV_NOPTS_VALUE; pos += 3; /* packet_start_code_prefix */ id = d[pos]; pos += 1; /* pack_header */ if( id == 0xBA) { pos += 10 + (d[pos+9] & 7); continue; } /* system_header */ if( id == 0xBB ) { int header_length; header_length = ( d[pos] << 8 ) + d[pos+1]; pos += 2 + header_length; continue; } pes_packet_length = ( d[pos] << 8 ) + d[pos+1]; pos += 2; /* pes_packet_length */ pes_packet_end = pos + pes_packet_length; if( id != 0xE0 && id != 0xBD && ( id & 0xC0 ) != 0xC0 ) { /* Not interesting */ pos = pes_packet_end; continue; } has_pts = d[pos+1] >> 6; pos += 2; /* Required headers */ pes_header_d_length = d[pos]; pos += 1; pes_header_end = pos + pes_header_d_length; if( has_pts ) { pts = ( (uint64_t)(d[pos] & 0xe ) << 29 ) + ( d[pos+1] << 22 ) + ( ( d[pos+2] >> 1 ) << 15 ) + ( d[pos+3] << 7 ) + ( d[pos+4] >> 1 ); if ( has_pts & 1 ) { dts = ( (uint64_t)(d[pos+5] & 0xe ) << 29 ) + ( d[pos+6] << 22 ) + ( ( d[pos+7] >> 1 ) << 15 ) + ( d[pos+8] << 7 ) + ( d[pos+9] >> 1 ); } else { dts = pts; } } pos = pes_header_end; if( id == 0xBD ) { id |= ( d[pos] << 8 ); if( ( id & 0xF0FF ) == 0x80BD ) /* A52 */ { pos += 4; } else if( ( id & 0xE0FF ) == 0x20BD || /* SPU */ ( id & 0xF0FF ) == 0xA0BD ) /* LPCM */ { pos += 1; } } /* Sanity check */ if( pos >= pes_packet_end ) { pos = pes_packet_end; continue; } /* Here we hit we ES payload */ buf_es = hb_buffer_init( pes_packet_end - pos ); buf_es->s.id = id; buf_es->s.start = pts; buf_es->s.renderOffset = dts; buf_es->s.stop = AV_NOPTS_VALUE; if ( state && id == 0xE0) { // Consume a chapter break, and apply it to the ES. restore_chap( state, buf_es ); } memcpy( buf_es->data, d + pos, pes_packet_end - pos ); hb_list_add( list_es, buf_es ); pos = pes_packet_end; } hb_buffer_t *tmp = buf->next; buf->next = NULL; hb_buffer_close( &buf ); buf = tmp; } #undef d } // mpeg transport stream demuxer. the elementary stream headers have been // stripped off and buf has all the info gleaned from them: id is set, // start contains the pts (if any), renderOffset contains the dts (if any) // and stop contains the pcr (if it changed). void hb_demux_mpeg(hb_buffer_t *buf, hb_list_t *list_es, hb_psdemux_t *state, int pcr_tolerance) { while ( buf ) { save_chap( state, buf ); if ( state ) { if ( buf->s.discontinuity ) { // Buffer has been flagged as a discontinuity. This happens // when a blueray changes clips. ++state->scr_changes; state->last_scr = buf->s.start; state->scr_delta = 0; } // we're keeping track of timing (i.e., not in scan) // check if there's a new pcr in this packet if ( buf->s.pcr >= 0 ) { // we have a new pcr check_mpeg_scr( state, buf->s.pcr, pcr_tolerance ); buf->s.pcr = AV_NOPTS_VALUE; // Some streams have consistantly bad PCRs or SCRs // So filter out the offset if ( buf->s.start >= 0 ) state->scr_delta = buf->s.start - state->last_scr; } if ( buf->s.start >= 0 ) { // Program streams have an SCR in every PACK header so they // can't lose their clock reference. But the PCR in Transport // streams is typically on <.1% of the packets. If a PCR // packet gets lost and it marks a clock discontinuity then // the data following it will be referenced to the wrong // clock & introduce huge gaps or throw our A/V sync off. // We try to protect against that here by sanity checking // timestamps against the current reference clock and discarding // packets where the DTS is "too far" from its clock. int64_t fdelta = buf->s.start - state->last_scr - state->scr_delta; if ( fdelta < -300 * 90000LL || fdelta > 300 * 90000LL ) { // packet too far behind or ahead of its clock reference ++state->dts_drops; hb_buffer_t *tmp = buf->next; buf->next = NULL; hb_buffer_close( &buf ); buf = tmp; continue; } else { // Some streams have no PCRs. In these cases, we // will only get an "PCR" update if a large change // in DTS or PTS is detected. So we need to update // our scr_delta with each valid timestamp so that // fdelta does not continually grow. state->scr_delta = buf->s.start - state->last_scr; } if (buf->s.type == AUDIO_BUF || buf->s.type == VIDEO_BUF) { if ( state->last_pts >= 0 ) { fdelta = buf->s.start - state->last_pts; if ( fdelta < -5 * 90000LL || fdelta > 5 * 90000LL ) { // Packet too far from last. This may be a NZ TV broadcast // as they like to change the PCR without sending a PCR // update. Since it may be a while until they actually tell // us the new PCR use the PTS as the PCR. ++state->scr_changes; state->last_scr = buf->s.start; state->scr_delta = 0; } } state->last_pts = buf->s.start; } } if ( buf->s.type == VIDEO_BUF ) { restore_chap( state, buf ); } } hb_buffer_t *tmp = buf->next; buf->next = NULL; hb_list_add( list_es, buf ); buf = tmp; } } void hb_demux_ts(hb_buffer_t *buf, hb_list_t *list_es, hb_psdemux_t *state) { // Distance between PCRs in TS is up to 100ms, but we have seen // streams that exceed this, so allow up to 300ms. hb_demux_mpeg(buf, list_es, state, 300); } void hb_demux_ps(hb_buffer_t *buf, hb_list_t *list_es, hb_psdemux_t *state) { // Distance between SCRs in PS is up to 700ms hb_demux_mpeg(buf, list_es, state, 700); } // "null" demuxer (makes a copy of input buf & returns it in list) // used when the reader for some format includes its own demuxer. // for example, ffmpeg. void hb_demux_null( hb_buffer_t * buf, hb_list_t * list_es, hb_psdemux_t* state ) { while ( buf ) { save_chap( state, buf ); if ( state ) { // if we don't have a time offset yet, // use this timestamp as the offset. if (state->scr_changes == 0 && (buf->s.start != AV_NOPTS_VALUE || buf->s.renderOffset != AV_NOPTS_VALUE)) { ++state->scr_changes; state->last_scr = buf->s.start >= 0 ? buf->s.start : buf->s.renderOffset; } if ( buf->s.type == VIDEO_BUF ) { restore_chap( state, buf ); } } hb_buffer_t *tmp = buf->next; buf->next = NULL; hb_list_add( list_es, buf ); buf = tmp; } } const hb_muxer_t hb_demux[] = { hb_demux_dvd_ps, hb_demux_ts, hb_demux_ps, hb_demux_null }; HandBrake-0.10.2/libhb/oclscale.c0000664000175200017520000002705412463330511017056 0ustar handbrakehandbrake/* oclscale.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html Authors: Peng Gao Li Cao */ #include #include "common.h" #include "opencl.h" #include "openclwrapper.h" #define FILTER_LEN 4 #define _A -0.5f cl_float cubic(cl_float x) { if (x < 0) x = -x; if (x < 1) return (_A + 2.0f) * (x * x * x) - (_A + 3.0f) * (x * x) + 0 + 1; else if (x < 2) return (_A) * (x * x * x) - (5.0f * _A) * (x * x) + (8.0f * _A) * x - (4.0f * _A); else return 0; } cl_float *hb_bicubic_weights(cl_float scale, int length) { cl_float *weights = (cl_float*) malloc(length * sizeof(cl_float) * 4); int i; // C rocks cl_float *out = weights; for (i = 0; i < length; ++i) { cl_float x = i / scale; cl_float dx = x - (int)x; *out++ = cubic(-dx - 1.0f); *out++ = cubic(-dx); *out++ = cubic(-dx + 1.0f); *out++ = cubic(-dx + 2.0f); } return weights; } int setupScaleWeights(cl_float xscale, cl_float yscale, int width, int height, hb_oclscale_t *os, KernelEnv *kenv); /** * executive scale using opencl * get filter args * create output buffer * create horizontal filter buffer * create vertical filter buffer * create kernels */ int hb_ocl_scale_func( void **data, KernelEnv *kenv ) { cl_int status; cl_mem in_buf = data[0]; cl_mem out_buf = data[1]; int crop_top = (intptr_t)data[2]; int crop_bottom = (intptr_t)data[3]; int crop_left = (intptr_t)data[4]; int crop_right = (intptr_t)data[5]; cl_int in_frame_w = (intptr_t)data[6]; cl_int in_frame_h = (intptr_t)data[7]; cl_int out_frame_w = (intptr_t)data[8]; cl_int out_frame_h = (intptr_t)data[9]; hb_oclscale_t *os = data[10]; hb_buffer_t *in = data[11]; hb_buffer_t *out = data[12]; if (hb_ocl == NULL) { hb_error("hb_ocl_scale_func: OpenCL support not available"); return 0; } if (os->initialized == 0) { hb_log( "Scaling With OpenCL" ); if (kenv->isAMD != 0) hb_log( "Using Zero Copy"); // create the block kernel cl_int status; os->m_kernel = hb_ocl->clCreateKernel(kenv->program, "frame_scale", &status); os->initialized = 1; } { // Use the new kernel cl_event events[5]; int eventCount = 0; if (kenv->isAMD == 0) { status = hb_ocl->clEnqueueUnmapMemObject(kenv->command_queue, in->cl.buffer, in->data, 0, NULL, &events[eventCount++]); status = hb_ocl->clEnqueueUnmapMemObject(kenv->command_queue, out->cl.buffer, out->data, 0, NULL, &events[eventCount++]); } cl_int srcPlaneOffset0 = in->plane[0].data - in->data; cl_int srcPlaneOffset1 = in->plane[1].data - in->data; cl_int srcPlaneOffset2 = in->plane[2].data - in->data; cl_int srcRowWords0 = in->plane[0].stride; cl_int srcRowWords1 = in->plane[1].stride; cl_int srcRowWords2 = in->plane[2].stride; cl_int dstPlaneOffset0 = out->plane[0].data - out->data; cl_int dstPlaneOffset1 = out->plane[1].data - out->data; cl_int dstPlaneOffset2 = out->plane[2].data - out->data; cl_int dstRowWords0 = out->plane[0].stride; cl_int dstRowWords1 = out->plane[1].stride; cl_int dstRowWords2 = out->plane[2].stride; if (crop_top != 0 || crop_bottom != 0 || crop_left != 0 || crop_right != 0) { srcPlaneOffset0 += crop_left + crop_top * srcRowWords0; srcPlaneOffset1 += crop_left / 2 + (crop_top / 2) * srcRowWords1; srcPlaneOffset2 += crop_left / 2 + (crop_top / 2) * srcRowWords2; in_frame_w = in_frame_w - crop_right - crop_left; in_frame_h = in_frame_h - crop_bottom - crop_top; } cl_float xscale = (out_frame_w * 1.0f) / in_frame_w; cl_float yscale = (out_frame_h * 1.0f) / in_frame_h; setupScaleWeights(xscale, yscale, out_frame_w, out_frame_h, os, kenv); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 0, sizeof(cl_mem), &out_buf); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 1, sizeof(cl_mem), &in_buf); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 2, sizeof(cl_float), &xscale); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 3, sizeof(cl_float), &yscale); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 4, sizeof(cl_int), &srcPlaneOffset0); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 5, sizeof(cl_int), &srcPlaneOffset1); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 6, sizeof(cl_int), &srcPlaneOffset2); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 7, sizeof(cl_int), &dstPlaneOffset0); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 8, sizeof(cl_int), &dstPlaneOffset1); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 9, sizeof(cl_int), &dstPlaneOffset2); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 10, sizeof(cl_int), &srcRowWords0); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 11, sizeof(cl_int), &srcRowWords1); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 12, sizeof(cl_int), &srcRowWords2); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 13, sizeof(cl_int), &dstRowWords0); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 14, sizeof(cl_int), &dstRowWords1); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 15, sizeof(cl_int), &dstRowWords2); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 16, sizeof(cl_int), &in_frame_w); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 17, sizeof(cl_int), &in_frame_h); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 18, sizeof(cl_int), &out_frame_w); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 19, sizeof(cl_int), &out_frame_h); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 20, sizeof(cl_mem), &os->bicubic_x_weights); HB_OCL_CHECK(hb_ocl->clSetKernelArg, os->m_kernel, 21, sizeof(cl_mem), &os->bicubic_y_weights); size_t workOffset[] = { 0, 0, 0 }; size_t globalWorkSize[] = { 1, 1, 1 }; size_t localWorkSize[] = { 1, 1, 1 }; int xgroups = (out_frame_w + 63) / 64; int ygroups = (out_frame_h + 15) / 16; localWorkSize[0] = 64; localWorkSize[1] = 1; localWorkSize[2] = 1; globalWorkSize[0] = xgroups * 64; globalWorkSize[1] = ygroups; globalWorkSize[2] = 3; HB_OCL_CHECK(hb_ocl->clEnqueueNDRangeKernel, kenv->command_queue, os->m_kernel, 3, workOffset, globalWorkSize, localWorkSize, eventCount, eventCount == 0 ? NULL : &events[0], &events[eventCount]); ++eventCount; if (kenv->isAMD == 0) { in->data = hb_ocl->clEnqueueMapBuffer(kenv->command_queue, in->cl.buffer, CL_FALSE, CL_MAP_READ|CL_MAP_WRITE, 0, in->alloc, eventCount ? 1 : 0, eventCount ? &events[eventCount - 1] : NULL, &events[eventCount], &status); out->data = hb_ocl->clEnqueueMapBuffer(kenv->command_queue, out->cl.buffer, CL_FALSE, CL_MAP_READ|CL_MAP_WRITE, 0, out->alloc, eventCount ? 1 : 0, eventCount ? &events[eventCount - 1] : NULL, &events[eventCount + 1], &status); eventCount += 2; } hb_ocl->clFlush(kenv->command_queue); hb_ocl->clWaitForEvents(eventCount, &events[0]); int i; for (i = 0; i < eventCount; ++i) { hb_ocl->clReleaseEvent(events[i]); } } return 1; } int setupScaleWeights(cl_float xscale, cl_float yscale, int width, int height, hb_oclscale_t *os, KernelEnv *kenv) { cl_int status; if (hb_ocl == NULL) { hb_error("setupScaleWeights: OpenCL support not available"); return 1; } if (os->xscale != xscale || os->width < width) { cl_float *xweights = hb_bicubic_weights(xscale, width); HB_OCL_BUF_FREE (hb_ocl, os->bicubic_x_weights); HB_OCL_BUF_CREATE(hb_ocl, os->bicubic_x_weights, CL_MEM_READ_ONLY, sizeof(cl_float) * width * 4); HB_OCL_CHECK(hb_ocl->clEnqueueWriteBuffer, kenv->command_queue, os->bicubic_x_weights, CL_TRUE, 0, sizeof(cl_float) * width * 4, xweights, 0, NULL, NULL); os->width = width; os->xscale = xscale; free(xweights); } if ((os->yscale != yscale) || (os->height < height)) { cl_float *yweights = hb_bicubic_weights(yscale, height); HB_OCL_BUF_FREE (hb_ocl, os->bicubic_y_weights); HB_OCL_BUF_CREATE(hb_ocl, os->bicubic_y_weights, CL_MEM_READ_ONLY, sizeof(cl_float) * height * 4); HB_OCL_CHECK(hb_ocl->clEnqueueWriteBuffer, kenv->command_queue, os->bicubic_y_weights, CL_TRUE, 0, sizeof(cl_float) * height * 4, yweights, 0, NULL, NULL); os->height = height; os->yscale = yscale; free(yweights); } return 0; } /** * function describe: this function is used to scaling video frame. it uses the gausi scaling algorithm * parameter: * inputFrameBuffer: the source video frame opencl buffer * outputdata: the destination video frame buffer * inputWidth: the width of the source video frame * inputHeight: the height of the source video frame * outputWidth: the width of destination video frame * outputHeight: the height of destination video frame */ static int s_scale_init_flag = 0; int do_scale_init() { if ( s_scale_init_flag==0 ) { int st = hb_register_kernel_wrapper( "frame_scale", hb_ocl_scale_func ); if( !st ) { hb_log( "register kernel[%s] failed", "frame_scale" ); return 0; } s_scale_init_flag++; } return 1; } int hb_ocl_scale(hb_buffer_t *in, hb_buffer_t *out, int *crop, hb_oclscale_t *os) { void *data[13]; if (do_scale_init() == 0) return 0; data[0] = in->cl.buffer; data[1] = out->cl.buffer; data[2] = (void*)(intptr_t)(crop[0]); data[3] = (void*)(intptr_t)(crop[1]); data[4] = (void*)(intptr_t)(crop[2]); data[5] = (void*)(intptr_t)(crop[3]); data[6] = (void*)(intptr_t)(in->f.width); data[7] = (void*)(intptr_t)(in->f.height); data[8] = (void*)(intptr_t)(out->f.width); data[9] = (void*)(intptr_t)(out->f.height); data[10] = os; data[11] = in; data[12] = out; if( !hb_run_kernel( "frame_scale", data ) ) hb_log( "run kernel[%s] failed", "frame_scale" ); return 0; } HandBrake-0.10.2/libhb/dxva2api.c0000664000175200017520000000204712463330511017002 0ustar handbrakehandbrake/* dxva2api.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html Authors: Peng Gao Li Cao */ #ifdef USE_HWD #include "dxva2api.h" __inline float hb_dx_fixedtofloat( const DXVA2_Fixed32 _fixed_ ) { return (FLOAT)_fixed_.Value + (FLOAT)_fixed_.Fraction / 0x10000; } __inline const DXVA2_Fixed32 hb_dx_fixed32_opaque_alpha() { DXVA2_Fixed32 _fixed_; _fixed_.Fraction = 0; _fixed_.Value = 0; _fixed_.ll = 1; return _fixed_; } __inline DXVA2_Fixed32 hb_dx_floattofixed( const float _float_ ) { DXVA2_Fixed32 _fixed_; _fixed_.Fraction = LOWORD( _float_ * 0x10000 ); _fixed_.Value = HIWORD( _float_ * 0x10000 ); return _fixed_; } #endif HandBrake-0.10.2/libhb/taskset.h0000664000175200017520000000354312463330511016751 0ustar handbrakehandbrake/* taskset.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef HB_TASKSET_H #define HB_TASKSET_H #define TASKSET_POSIX_COMPLIANT 1 #include "bits.h" typedef struct hb_taskset_s { int thread_count; int arg_size; int bitmap_elements; hb_thread_t ** task_threads; uint8_t * task_threads_args; uint32_t * task_begin_bitmap; // Threads can begin uint32_t * task_complete_bitmap; // Threads have completed uint32_t * task_stop_bitmap; // Threads should exit hb_lock_t * task_cond_lock; // Held during condition tests hb_cond_t * task_begin; // Threads can begin work hb_cond_t * task_complete; // Threads have finished work. } taskset_t; int taskset_init( taskset_t *, int /*thread_count*/, size_t /*user_arg_size*/ ); void taskset_cycle( taskset_t * ); void taskset_fini( taskset_t * ); int taskset_thread_spawn( taskset_t *, int /*thr_idx*/, const char * /*descr*/, thread_func_t *, int /*priority*/ ); void taskset_thread_wait4start( taskset_t *, int ); void taskset_thread_complete( taskset_t *, int ); static inline void *taskset_thread_args( taskset_t *, int ); static inline int taskset_thread_stop( taskset_t *, int ); static inline void * taskset_thread_args( taskset_t *ts, int thr_idx ) { return( ts->task_threads_args + ( ts->arg_size * thr_idx ) ); } static inline int taskset_thread_stop( taskset_t *ts, int thr_idx ) { return bit_is_set( ts->task_stop_bitmap, thr_idx ); } #endif /* HB_TASKSET_H */ HandBrake-0.10.2/libhb/muxcommon.c0000664000175200017520000006357712463330511017325 0ustar handbrakehandbrake/* muxcommon.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "decssasub.h" #define MIN_BUFFERING (1024*1024*10) #define MAX_BUFFERING (1024*1024*50) struct hb_mux_object_s { HB_MUX_COMMON; }; typedef struct { int size; // Size in bits uint32_t * vec; } hb_bitvec_t; typedef struct { hb_buffer_t **fifo; uint32_t in; // number of bufs put into fifo uint32_t out; // number of bufs taken out of fifo uint32_t flen; // fifo length (must be power of two) } mux_fifo_t; typedef struct { hb_mux_data_t * mux_data; uint64_t frames; uint64_t bytes; mux_fifo_t mf; int buffered_size; } hb_track_t; typedef struct { hb_lock_t * mutex; int ref; int done; hb_mux_object_t * m; double pts; // end time of next muxing chunk double interleave; // size in 90KHz ticks of media chunks we mux uint32_t max_tracks; // total number of tracks allocated uint32_t ntracks; // total number of tracks we're muxing hb_bitvec_t * eof; // bitmask of track with eof hb_bitvec_t * rdy; // bitmask of tracks ready to output hb_bitvec_t * allEof; // valid bits in eof (all tracks) hb_bitvec_t * allRdy; // valid bits in rdy (audio & video tracks) hb_track_t ** track; // tracks to mux 'max_tracks' elements int buffered_size; } hb_mux_t; struct hb_work_private_s { hb_job_t * job; int track; hb_mux_t * mux; }; static int hb_bitvec_add_bits(hb_bitvec_t *bv, int bits) { int ii; int words_cur = (bv->size + 31) >> 5; int words = (bv->size + bits + 31) >> 5; if (words > words_cur) { uint32_t *tmp = realloc(bv->vec, words * sizeof(uint32_t)); if (tmp == NULL) { return -1; } for (ii = words_cur; ii < words; ii++) tmp[ii] = 0; bv->vec = tmp; } bv->size += bits; return 0; } static hb_bitvec_t* hb_bitvec_new(int size) { hb_bitvec_t *bv = calloc(sizeof(hb_bitvec_t), 1); hb_bitvec_add_bits(bv, size); return bv; } static void hb_bitvec_free(hb_bitvec_t **_bv) { hb_bitvec_t *bv = *_bv; free(bv->vec); free(bv); *_bv = NULL; } static void hb_bitvec_set(hb_bitvec_t *bv, int n) { if (n >= bv->size) return; // Error. Should never happen. int word = n >> 5; uint32_t bit = 1 << (n & 0x1F); bv->vec[word] |= bit; } static void hb_bitvec_clr(hb_bitvec_t *bv, int n) { if (n >= bv->size) return; // Error. Should never happen. int word = n >> 5; uint32_t bit = 1 << (n & 0x1F); bv->vec[word] &= ~bit; } static void hb_bitvec_zero(hb_bitvec_t *bv) { int words = (bv->size + 31) >> 5; memset(bv->vec, 0, words * sizeof(uint32_t)); } static int hb_bitvec_bit(hb_bitvec_t *bv, int n) { if (n >= bv->size) return 0; // Error. Should never happen. int word = n >> 5; uint32_t bit = 1 << (n & 0x1F); return !!(bv->vec[word] & bit); } static int hb_bitvec_any(hb_bitvec_t *bv) { uint32_t result = 0;; int ii; int words = (bv->size + 31) >> 5; for (ii = 0; ii < words; ii++) result |= bv->vec[ii]; return !!result; } static int hb_bitvec_cmp(hb_bitvec_t *bv1, hb_bitvec_t *bv2) { if (bv1->size != bv2->size) return 0; int ii; int words = (bv1->size + 31) >> 5; for (ii = 0; ii < words; ii++) if (bv1->vec[ii] != bv2->vec[ii]) return 0; return 1; } static int hb_bitvec_and_cmp(hb_bitvec_t *bv1, hb_bitvec_t *bv2, hb_bitvec_t *bv3) { if (bv1->size != bv2->size) return 0; int ii; int words = (bv1->size + 31) >> 5; for (ii = 0; ii < words; ii++) if ((bv1->vec[ii] & bv2->vec[ii]) != bv3->vec[ii]) return 0; return 1; } static int hb_bitvec_cpy(hb_bitvec_t *bv1, hb_bitvec_t *bv2) { if (bv1->size < bv2->size) { int result = hb_bitvec_add_bits(bv1, bv2->size - bv1->size); if (result < 0) return result; } int words = (bv1->size + 31) >> 5; memcpy(bv1->vec, bv2->vec, words * sizeof(uint32_t)); return 0; } // The muxer handles two different kinds of media: Video and audio tracks // are continuous: once they start they generate continuous, consecutive // sequence of bufs until they end. The muxer will time align all continuous // media tracks so that their data will be well interleaved in the output file. // (Smooth, low latency playback with minimal player buffering requires that // data that's going to be presented close together in time also be close // together in the output file). Since HB's audio and video encoders run at // different speeds, the time-aligning involves buffering *all* the continuous // media tracks until a frame with a timestamp beyond the current alignment // point arrives on the slowest fifo (usually the video encoder). // // The other kind of media, subtitles, close-captions, vobsubs and // similar tracks, are intermittent. They generate frames sporadically or on // human time scales (seconds) rather than near the video frame rate (milliseconds). // If intermittent sources were treated like continuous sources huge sections of // audio and video would get buffered waiting for the next subtitle to show up. // To keep this from happening the muxer doesn't wait for intermittent tracks // (essentially it assumes that they will always go through the HB processing // pipeline faster than the associated video). They are still time aligned and // interleaved at the appropriate point in the output file. // This routine adds another track for the muxer to process. The media input // stream will be read from HandBrake fifo 'fifo'. Buffers read from that // stream will be time-aligned with all the other media streams then passed // to the container-specific 'mux' routine with argument 'mux_data' (see // routine OutputTrackChunk). 'is_continuous' must be 1 for an audio or video // track and 0 otherwise (see above). static void add_mux_track( hb_mux_t *mux, hb_mux_data_t *mux_data, int is_continuous ) { if ( mux->ntracks + 1 > mux->max_tracks ) { int max_tracks = mux->max_tracks ? mux->max_tracks * 2 : 32; hb_track_t **tmp; tmp = realloc(mux->track, max_tracks * sizeof(hb_track_t*)); if (tmp == NULL) { hb_error("add_mux_track: realloc failed, too many tracks (>%d)", max_tracks); return; } mux->track = tmp; mux->max_tracks = max_tracks; } hb_track_t *track = calloc( sizeof( hb_track_t ), 1 ); track->mux_data = mux_data; track->mf.flen = 8; track->mf.fifo = calloc( sizeof(track->mf.fifo[0]), track->mf.flen ); int t = mux->ntracks++; mux->track[t] = track; hb_bitvec_set(mux->allEof, t); if (is_continuous) hb_bitvec_set(mux->allRdy, t); } static int mf_full( hb_track_t * track ) { if ( track->buffered_size > MAX_BUFFERING ) return 1; return 0; } static void mf_push( hb_mux_t * mux, int tk, hb_buffer_t *buf ) { hb_track_t * track = mux->track[tk]; uint32_t mask = track->mf.flen - 1; uint32_t in = track->mf.in; hb_buffer_reduce( buf, buf->size ); if ( track->buffered_size > MAX_BUFFERING ) { hb_bitvec_cpy(mux->rdy, mux->allRdy); } if ( ( ( in + 1 ) & mask ) == ( track->mf.out & mask ) ) { // fifo is full - expand it to double the current size. // This is a bit tricky because when we change the size // it changes the modulus (mask) used to convert the in // and out counters to fifo indices. Since existing items // will be referenced at a new location after the expand // we can't just realloc the fifo. If there were // hundreds of fifo entries it would be worth it to have code // for each of the four possible before/after configurations // but these fifos are small so we just allocate a new chunk // of memory then do element by element copies using the old & // new masks then free the old fifo's memory.. track->mf.flen *= 2; uint32_t nmask = track->mf.flen - 1; hb_buffer_t **nfifo = malloc( track->mf.flen * sizeof(*nfifo) ); int indx = track->mf.out; while ( indx != track->mf.in ) { nfifo[indx & nmask] = track->mf.fifo[indx & mask]; ++indx; } free( track->mf.fifo ); track->mf.fifo = nfifo; mask = nmask; } track->mf.fifo[in & mask] = buf; track->mf.in = in + 1; track->buffered_size += buf->size; mux->buffered_size += buf->size; } static hb_buffer_t *mf_pull( hb_mux_t * mux, int tk ) { hb_track_t *track =mux->track[tk]; hb_buffer_t *b = NULL; if ( track->mf.out != track->mf.in ) { // the fifo isn't empty b = track->mf.fifo[track->mf.out & (track->mf.flen - 1)]; ++track->mf.out; track->buffered_size -= b->size; mux->buffered_size -= b->size; } return b; } static hb_buffer_t *mf_peek( hb_track_t *track ) { return track->mf.out == track->mf.in ? NULL : track->mf.fifo[track->mf.out & (track->mf.flen - 1)]; } static void MoveToInternalFifos( int tk, hb_mux_t *mux, hb_buffer_t * buf ) { // move all the buffers on the track's fifo to our internal // fifo so that (a) we don't deadlock in the reader and // (b) we can control how data from multiple tracks is // interleaved in the output file. mf_push( mux, tk, buf ); if ( buf->s.start >= mux->pts ) { // buffer is past our next interleave point so // note that this track is ready to be output. hb_bitvec_set(mux->rdy, tk); } } static void OutputTrackChunk( hb_mux_t *mux, int tk, hb_mux_object_t *m ) { hb_track_t *track = mux->track[tk]; hb_buffer_t *buf; while ( ( buf = mf_peek( track ) ) != NULL && buf->s.start < mux->pts ) { buf = mf_pull( mux, tk ); track->frames += 1; track->bytes += buf->size; m->mux( m, track->mux_data, buf ); } } static int muxWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { hb_work_private_t * pv = w->private_data; hb_job_t * job = pv->job; hb_mux_t * mux = pv->mux; hb_track_t * track; int i; hb_buffer_t * buf = *buf_in; hb_lock( mux->mutex ); if ( mux->done ) { hb_unlock( mux->mutex ); return HB_WORK_DONE; } if ( buf->size <= 0 ) { // EOF - mark this track as done hb_buffer_close( &buf ); hb_bitvec_set(mux->eof, pv->track); hb_bitvec_set(mux->rdy, pv->track); } else if ((job->pass != 0 && job->pass != 2) || hb_bitvec_bit(mux->eof, pv->track)) { hb_buffer_close( &buf ); } else { MoveToInternalFifos( pv->track, mux, buf ); } *buf_in = NULL; if (!hb_bitvec_and_cmp(mux->rdy, mux->allRdy, mux->allRdy) && !hb_bitvec_and_cmp(mux->eof, mux->allEof, mux->allEof)) { hb_unlock( mux->mutex ); return HB_WORK_OK; } hb_bitvec_t *more; more = hb_bitvec_new(0); hb_bitvec_cpy(more, mux->rdy); // all tracks have at least 'interleave' ticks of data. Output // all that we can in 'interleave' size chunks. while ((hb_bitvec_and_cmp(mux->rdy, mux->allRdy, mux->allRdy) && hb_bitvec_any(more) && mux->buffered_size > MIN_BUFFERING ) || (hb_bitvec_cmp(mux->eof, mux->allEof))) { hb_bitvec_zero(more); for ( i = 0; i < mux->ntracks; ++i ) { track = mux->track[i]; OutputTrackChunk( mux, i, mux->m ); if ( mf_full( track ) ) { // If the track's fifo is still full, advance // the currint interleave point and try again. hb_bitvec_cpy(mux->rdy, mux->allRdy); break; } // if the track is at eof or still has data that's past // our next interleave point then leave it marked as rdy. // Otherwise clear rdy. if (hb_bitvec_bit(mux->eof, i) && (track->mf.out == track->mf.in || track->mf.fifo[(track->mf.in-1) & (track->mf.flen-1)]->s.start < mux->pts + mux->interleave)) { hb_bitvec_clr(mux->rdy, i); } if ( track->mf.out != track->mf.in ) { hb_bitvec_set(more, i); } } // if all the tracks are at eof we're just purging their // remaining data -- keep going until all internal fifos are empty. if (hb_bitvec_cmp(mux->eof, mux->allEof)) { for ( i = 0; i < mux->ntracks; ++i ) { if ( mux->track[i]->mf.out != mux->track[i]->mf.in ) { break; } } if ( i >= mux->ntracks ) { mux->done = 1; hb_unlock( mux->mutex ); hb_bitvec_free(&more); return HB_WORK_DONE; } } mux->pts += mux->interleave; } hb_bitvec_free(&more); hb_unlock( mux->mutex ); return HB_WORK_OK; } void muxClose( hb_work_object_t * w ) { hb_work_private_t * pv = w->private_data; hb_mux_t * mux = pv->mux; hb_job_t * job = pv->job; hb_track_t * track; int i; hb_lock( mux->mutex ); if ( --mux->ref == 0 ) { // Update state before closing muxer. Closing the muxer // may initiate optimization which can take a while and // we want the muxing state to be visible while this is // happening. if( job->pass == 0 || job->pass == 2 ) { /* Update the UI */ hb_state_t state; state.state = HB_STATE_MUXING; state.param.muxing.progress = 0; hb_set_state( job->h, &state ); } if( mux->m ) { mux->m->end( mux->m ); free( mux->m ); } // we're all done muxing -- print final stats and cleanup. if( job->pass == 0 || job->pass == 2 ) { hb_stat_t sb; uint64_t bytes_total, frames_total; if (!hb_stat(job->file, &sb)) { hb_deep_log( 2, "mux: file size, %"PRId64" bytes", (uint64_t) sb.st_size ); bytes_total = 0; frames_total = 0; for( i = 0; i < mux->ntracks; ++i ) { track = mux->track[i]; hb_log( "mux: track %d, %"PRId64" frames, %"PRId64" bytes, %.2f kbps, fifo %d", i, track->frames, track->bytes, 90000.0 * track->bytes / mux->pts / 125, track->mf.flen ); if( !i && job->vquality < 0 ) { /* Video */ hb_deep_log( 2, "mux: video bitrate error, %+"PRId64" bytes", (int64_t)(track->bytes - mux->pts * job->vbitrate * 125 / 90000) ); } bytes_total += track->bytes; frames_total += track->frames; } if( bytes_total && frames_total ) { hb_deep_log( 2, "mux: overhead, %.2f bytes per frame", (float) ( sb.st_size - bytes_total ) / frames_total ); } } } for( i = 0; i < mux->ntracks; ++i ) { hb_buffer_t * b; track = mux->track[i]; while ( (b = mf_pull( mux, i )) != NULL ) { hb_buffer_close( &b ); } if( track->mux_data ) { free( track->mux_data ); free( track->mf.fifo ); } free( track ); } free(mux->track); hb_unlock( mux->mutex ); hb_lock_close( &mux->mutex ); hb_bitvec_free(&mux->eof); hb_bitvec_free(&mux->rdy); hb_bitvec_free(&mux->allEof); hb_bitvec_free(&mux->allRdy); free( mux ); } else { hb_unlock( mux->mutex ); } free( pv ); w->private_data = NULL; } static void mux_loop( void * _w ) { hb_work_object_t * w = _w; hb_work_private_t * pv = w->private_data; hb_job_t * job = pv->job; hb_buffer_t * buf_in; while ( !*job->die && w->status != HB_WORK_DONE ) { buf_in = hb_fifo_get_wait( w->fifo_in ); if ( pv->mux->done ) break; if ( buf_in == NULL ) continue; if ( *job->die ) { if( buf_in ) { hb_buffer_close( &buf_in ); } break; } w->status = w->work( w, &buf_in, NULL ); if( buf_in ) { hb_buffer_close( &buf_in ); } } } hb_work_object_t * hb_muxer_init( hb_job_t * job ) { int i; hb_mux_t * mux = calloc( sizeof( hb_mux_t ), 1 ); hb_work_object_t * w; hb_work_object_t * muxer; // The bit vectors must be allocated before hb_thread_init for the // audio and subtitle muxer jobs below. int bit_vec_size = 1 + hb_list_count(job->list_audio) + hb_list_count(job->list_subtitle); mux->rdy = hb_bitvec_new(bit_vec_size); mux->eof = hb_bitvec_new(bit_vec_size); mux->allRdy = hb_bitvec_new(bit_vec_size); mux->allEof = hb_bitvec_new(bit_vec_size); mux->mutex = hb_lock_init(); // set up to interleave track data in blocks of 1 video frame time. // (the best case for buffering and playout latency). The container- // specific muxers can reblock this into bigger chunks if necessary. mux->interleave = 90000. * (double)job->vrate_base / (double)job->vrate; mux->pts = mux->interleave; /* Get a real muxer */ if( job->pass == 0 || job->pass == 2) { switch( job->mux ) { case HB_MUX_AV_MP4: case HB_MUX_AV_MKV: mux->m = hb_mux_avformat_init( job ); break; default: hb_error( "No muxer selected, exiting" ); *job->done_error = HB_ERROR_INIT; *job->die = 1; return NULL; } /* Create file, write headers */ if( mux->m ) { mux->m->init( mux->m ); } } /* Initialize the work objects that will receive fifo data */ muxer = hb_get_work( WORK_MUX ); muxer->private_data = calloc( sizeof( hb_work_private_t ), 1 ); muxer->private_data->job = job; muxer->private_data->mux = mux; mux->ref++; muxer->private_data->track = mux->ntracks; muxer->fifo_in = job->fifo_mpeg4; add_mux_track( mux, job->mux_data, 1 ); muxer->done = &muxer->private_data->mux->done; for( i = 0; i < hb_list_count( job->list_audio ); i++ ) { hb_audio_t *audio = hb_list_item( job->list_audio, i ); w = hb_get_work( WORK_MUX ); w->private_data = calloc( sizeof( hb_work_private_t ), 1 ); w->private_data->job = job; w->private_data->mux = mux; mux->ref++; w->private_data->track = mux->ntracks; w->fifo_in = audio->priv.fifo_out; add_mux_track( mux, audio->priv.mux_data, 1 ); w->done = &job->done; hb_list_add( job->list_work, w ); w->thread = hb_thread_init( w->name, mux_loop, w, HB_NORMAL_PRIORITY ); } for( i = 0; i < hb_list_count( job->list_subtitle ); i++ ) { hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i ); if (subtitle->config.dest != PASSTHRUSUB) continue; w = hb_get_work( WORK_MUX ); w->private_data = calloc( sizeof( hb_work_private_t ), 1 ); w->private_data->job = job; w->private_data->mux = mux; mux->ref++; w->private_data->track = mux->ntracks; w->fifo_in = subtitle->fifo_out; add_mux_track( mux, subtitle->mux_data, 0 ); w->done = &job->done; hb_list_add( job->list_work, w ); w->thread = hb_thread_init( w->name, mux_loop, w, HB_NORMAL_PRIORITY ); } return muxer; } // muxInit does nothing because the muxer has a special initializer // that takes care of initializing all muxer work objects static int muxInit( hb_work_object_t * w, hb_job_t * job ) { return 0; } hb_work_object_t hb_muxer = { WORK_MUX, "Muxer", muxInit, muxWork, muxClose }; #define TX3G_STYLES (HB_STYLE_FLAG_BOLD | \ HB_STYLE_FLAG_ITALIC | \ HB_STYLE_FLAG_UNDERLINE) typedef struct style_context_s { uint8_t * style_atoms; int style_atom_count; hb_subtitle_style_t current_style; int style_start; } style_context_t; static void update_style_atoms(style_context_t *ctx, int stop) { uint8_t *style_entry; uint8_t face = 0; style_entry = ctx->style_atoms + 10 + (12 * ctx->style_atom_count); if (ctx->current_style.flags & HB_STYLE_FLAG_BOLD) face |= 1; if (ctx->current_style.flags & HB_STYLE_FLAG_ITALIC) face |= 2; if (ctx->current_style.flags & HB_STYLE_FLAG_UNDERLINE) face |= 4; style_entry[0] = (ctx->style_start >> 8) & 0xff; // startChar style_entry[1] = ctx->style_start & 0xff; style_entry[2] = (stop >> 8) & 0xff; // endChar style_entry[3] = stop & 0xff; style_entry[4] = 0; // font-ID msb style_entry[5] = 1; // font-ID lsb style_entry[6] = face; // face-style-flags style_entry[7] = 24; // font-size style_entry[8] = (ctx->current_style.fg_rgb >> 16) & 0xff; // r style_entry[9] = (ctx->current_style.fg_rgb >> 8) & 0xff; // g style_entry[10] = (ctx->current_style.fg_rgb) & 0xff; // b style_entry[11] = ctx->current_style.fg_alpha; // a ctx->style_atom_count++; } static void update_style(style_context_t *ctx, hb_subtitle_style_t *style, int pos) { if (ctx->style_start < pos) { // do we need to add a style atom? if (((ctx->current_style.flags ^ style->flags) & TX3G_STYLES) || ctx->current_style.fg_rgb != style->fg_rgb || ctx->current_style.fg_alpha != style->fg_alpha) { update_style_atoms(ctx, pos - 1); ctx->current_style = *style; ctx->style_start = pos; } } else { ctx->current_style = *style; ctx->style_start = pos; } } static void style_context_init(style_context_t *ctx, uint8_t *style_atoms) { memset(ctx, 0, sizeof(*ctx)); ctx->style_atoms = style_atoms; ctx->style_start = INT_MAX; } /* * Copy the input to output removing markup and adding markup to the style * atom where appropriate. */ void hb_muxmp4_process_subtitle_style(uint8_t *input, uint8_t *output, uint8_t *style_atoms, uint16_t *stylesize) { uint16_t utf8_count = 0; // utf8 count from start of subtitle int consumed, in_pos = 0, out_pos = 0, len, ii, lines; style_context_t ctx; hb_subtitle_style_t style; char *text, *tmp; *stylesize = 0; style_context_init(&ctx, style_atoms); hb_ssa_style_init(&style); // Skip past the SSA preamble text = (char*)input; for (ii = 0; ii < 8; ii++) { tmp = strchr(text, ','); if (tmp == NULL) break; text = tmp + 1; } in_pos = text - (char*)input; while (input[in_pos] != '\0') { lines = 1; text = hb_ssa_to_text((char*)input + in_pos, &consumed, &style); if (text == NULL) break; // count UTF8 characters, and get length of text len = 0; for (ii = 0; text[ii] != '\0'; ii++) { if ((text[ii] & 0xc0) == 0x80) { utf8_count++; hb_deep_log( 3, "mux: Counted %d UTF-8 chrs within subtitle", utf8_count); } // By default tx3g only supports 2 lines of text // To support more lines, we must enable the virtical placement // flag in the tx3g atom and add tbox atoms to the sample // data to set the vertical placement for each subtitle. // Although tbox defines a rectangle, the QT spec says // that only the vertical placement is honored (bummer). if (text[ii] == '\n') { lines++; if (lines > 2) text[ii] = ' '; } len++; } strcpy((char*)output+out_pos, text); free(text); out_pos += len; in_pos += consumed; update_style(&ctx, &style, out_pos - utf8_count); } // Return to default style at end of line, flushes any pending // style changes hb_ssa_style_init(&style); update_style(&ctx, &style, out_pos - utf8_count); // null terminate output string output[out_pos] = 0; if (ctx.style_atom_count > 0) { *stylesize = 10 + (ctx.style_atom_count * 12); memcpy(style_atoms + 4, "styl", 4); style_atoms[0] = 0; style_atoms[1] = 0; style_atoms[2] = (*stylesize >> 8) & 0xff; style_atoms[3] = *stylesize & 0xff; style_atoms[8] = (ctx.style_atom_count >> 8) & 0xff; style_atoms[9] = ctx.style_atom_count & 0xff; } } HandBrake-0.10.2/libhb/common.h0000664000175200017520000013132512463330511016563 0ustar handbrakehandbrake/* common.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef HB_COMMON_H #define HB_COMMON_H #include #include #include #include #include #include #include #include #include #include /* * It seems WinXP doesn't align the stack of new threads to 16 bytes. * To prevent crashes in SSE functions, we need to force stack alignement * of new threads. */ #if defined( __GNUC__ ) && (defined( _WIN32 ) || defined( __MINGW32__ )) # define attribute_align_thread __attribute__((force_align_arg_pointer)) #else # define attribute_align_thread #endif #if defined( __GNUC__ ) && !(defined( _WIN32 ) || defined( __MINGW32__ )) # define HB_WPRINTF(s,v) __attribute__((format(printf,s,v))) #else # define HB_WPRINTF(s,v) #endif #if defined( SYS_MINGW ) # define fseek fseeko64 # define ftell ftello64 # undef fseeko # define fseeko fseeko64 # undef ftello # define ftello ftello64 # define flockfile(...) # define funlockfile(...) # define getc_unlocked getc # undef off_t # define off_t off64_t #endif #ifndef MIN #define MIN( a, b ) ( (a) > (b) ? (b) : (a) ) #endif #ifndef MAX #define MAX( a, b ) ( (a) > (b) ? (a) : (b) ) #endif #ifndef HB_DEBUG_ASSERT #define HB_DEBUG_ASSERT(x, y) { if ((x)) { hb_error("ASSERT: %s", y); exit(1); } } #endif #define EVEN( a ) ((a) + ((a) & 1)) #define MULTIPLE_MOD(a, b) (((b) * (int)(((a) + ((b) / 2)) / (b)))) #define MULTIPLE_MOD_UP(a, b) (((b) * (int)(((a) + ((b) - 1)) / (b)))) #define MULTIPLE_MOD_DOWN(a, b) (((b) * (int)((a) / (b)))) #define HB_DVD_READ_BUFFER_SIZE 2048 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_container_s hb_container_t; typedef struct hb_rational_s hb_rational_t; typedef struct hb_geometry_s hb_geometry_t; typedef struct hb_ui_geometry_s hb_ui_geometry_t; typedef struct hb_image_s hb_image_t; typedef struct hb_job_s hb_job_t; typedef struct hb_title_set_s hb_title_set_t; typedef struct hb_title_s hb_title_t; typedef struct hb_chapter_s hb_chapter_t; typedef struct hb_audio_s hb_audio_t; typedef struct hb_audio_config_s hb_audio_config_t; typedef struct hb_subtitle_s hb_subtitle_t; typedef struct hb_subtitle_config_s hb_subtitle_config_t; typedef struct hb_attachment_s hb_attachment_t; typedef struct hb_metadata_s hb_metadata_t; typedef struct hb_coverart_s hb_coverart_t; typedef struct hb_state_s hb_state_t; typedef union hb_esconfig_u hb_esconfig_t; typedef struct hb_work_private_s hb_work_private_t; typedef struct hb_work_object_s hb_work_object_t; typedef struct hb_filter_private_s hb_filter_private_t; typedef struct hb_filter_object_s hb_filter_object_t; typedef struct hb_buffer_s hb_buffer_t; typedef struct hb_buffer_settings_s hb_buffer_settings_t; typedef struct hb_image_format_s hb_image_format_t; typedef struct hb_fifo_s hb_fifo_t; typedef struct hb_lock_s hb_lock_t; typedef enum { HB_ERROR_NONE = 0, HB_ERROR_CANCELED , HB_ERROR_WRONG_INPUT, HB_ERROR_INIT , HB_ERROR_UNKNOWN } hb_error_code; #include "ports.h" #ifdef __LIBHB__ #include "internal.h" #define PRIVATE #else #define PRIVATE const #endif #include "audio_remap.h" #include "libavutil/channel_layout.h" #ifdef USE_QSV #include "libavcodec/qsv.h" #endif hb_list_t * hb_list_init(); int hb_list_count( const hb_list_t * ); void hb_list_add( hb_list_t *, void * ); void hb_list_insert( hb_list_t * l, int pos, void * p ); void hb_list_rem( hb_list_t *, void * ); void * hb_list_item( const hb_list_t *, int ); void hb_list_close( hb_list_t ** ); void hb_reduce( int *x, int *y, int num, int den ); void hb_reduce64( int64_t *x, int64_t *y, int64_t num, int64_t den ); void hb_limit_rational64( int64_t *x, int64_t *y, int64_t num, int64_t den, int64_t limit ); #define HB_KEEP_WIDTH 0x01 #define HB_KEEP_HEIGHT 0x02 #define HB_KEEP_DISPLAY_ASPECT 0x04 void hb_job_set_encoder_preset (hb_job_t *job, const char *preset); void hb_job_set_encoder_tune (hb_job_t *job, const char *tune); void hb_job_set_encoder_options(hb_job_t *job, const char *options); void hb_job_set_encoder_profile(hb_job_t *job, const char *profile); void hb_job_set_encoder_level (hb_job_t *job, const char *level); void hb_job_set_file (hb_job_t *job, const char *file); hb_audio_t *hb_audio_copy(const hb_audio_t *src); hb_list_t *hb_audio_list_copy(const hb_list_t *src); void hb_audio_close(hb_audio_t **audio); void hb_audio_config_init(hb_audio_config_t * audiocfg); int hb_audio_add(const hb_job_t * job, const hb_audio_config_t * audiocfg); hb_audio_config_t * hb_list_audio_config_item(hb_list_t * list, int i); int hb_subtitle_add_ssa_header(hb_subtitle_t *subtitle, int width, int height); hb_subtitle_t *hb_subtitle_copy(const hb_subtitle_t *src); hb_list_t *hb_subtitle_list_copy(const hb_list_t *src); void hb_subtitle_close( hb_subtitle_t **sub ); int hb_subtitle_add(const hb_job_t * job, const hb_subtitle_config_t * subtitlecfg, int track); int hb_srt_add(const hb_job_t * job, const hb_subtitle_config_t * subtitlecfg, const char *lang); int hb_subtitle_can_force( int source ); int hb_subtitle_can_burn( int source ); int hb_subtitle_can_pass( int source, int mux ); int hb_audio_can_apply_drc(uint32_t codec, uint32_t codec_param, int encoder); hb_attachment_t *hb_attachment_copy(const hb_attachment_t *src); hb_list_t *hb_attachment_list_copy(const hb_list_t *src); void hb_attachment_close(hb_attachment_t **attachment); hb_metadata_t * hb_metadata_init(); hb_metadata_t * hb_metadata_copy(const hb_metadata_t *src); void hb_metadata_close(hb_metadata_t **metadata); void hb_metadata_set_name( hb_metadata_t *metadata, const char *name ); void hb_metadata_set_artist( hb_metadata_t *metadata, const char *artist ); void hb_metadata_set_composer( hb_metadata_t *metadata, const char *composer ); void hb_metadata_set_release_date( hb_metadata_t *metadata, const char *release_date ); void hb_metadata_set_comment( hb_metadata_t *metadata, const char *comment ); void hb_metadata_set_genre( hb_metadata_t *metadata, const char *genre ); void hb_metadata_set_album( hb_metadata_t *metadata, const char *album ); void hb_metadata_set_album_artist( hb_metadata_t *metadata, const char *album_artist ); void hb_metadata_set_description( hb_metadata_t *metadata, const char *description ); void hb_metadata_set_long_description( hb_metadata_t *metadata, const char *long_description ); void hb_metadata_add_coverart( hb_metadata_t *metadata, const uint8_t *data, int size, int type ); void hb_metadata_rem_coverart( hb_metadata_t *metadata, int ii ); hb_chapter_t *hb_chapter_copy(const hb_chapter_t *src); hb_list_t *hb_chapter_list_copy(const hb_list_t *src); void hb_chapter_close(hb_chapter_t **chapter); void hb_chapter_set_title(hb_chapter_t *chapter, const char *title); // Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_rate_s.cs when changing this struct struct hb_rate_s { const char *name; int rate; }; struct hb_dither_s { const char *description; const char *short_name; int method; }; // Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_mixdown_s.cs when changing this struct struct hb_mixdown_s { const char *name; const char *short_name; int amixdown; }; // Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_encoder_s.cs when changing this struct struct hb_encoder_s { const char *name; // note: used in presets const char *short_name; // note: used in CLI const char *long_name; // used in log int codec; // HB_*CODEC_* define int muxers; // supported muxers }; // Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_container_s.cs when changing this struct struct hb_container_s { const char *name; const char *short_name; const char *long_name; const char *default_extension; int format; }; struct hb_rational_s { int num; int den; }; struct hb_geometry_s { int width; int height; hb_rational_t par; }; struct hb_ui_geometry_s { int mode; // Anamorphic mode, see job struct anamorphic int keep; // Specifies settings that shouldn't be changed int itu_par; // use dvd dimensions to determine PAR int modulus; // pixel alignment for loose anamorphic int crop[4]; // Pixels cropped from source before scaling int width; // destination storage width int height; // destination storage height int maxWidth; // max destination storage width int maxHeight; // max destination storage height hb_rational_t par; // Pixel aspect used in custom anamorphic hb_rational_t dar; // Display aspect used in custom anamorphic }; struct hb_image_s { int format; int width; int height; uint8_t *data; struct image_plane { uint8_t *data; int width; int height; int stride; int height_stride; int size; } plane[4]; }; void hb_image_close(hb_image_t **_image); // Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_subtitle_config_s.cs when changing this struct struct hb_subtitle_config_s { enum subdest { RENDERSUB, PASSTHRUSUB } dest; int force; int default_track; /* SRT subtitle tracks only */ char src_filename[256]; char src_codeset[40]; int64_t offset; }; /******************************************************************************* * Lists of rates, mixdowns, encoders etc. ******************************************************************************* * * Use hb_*_get_next() to get the next list item (use NULL to get the first). * * Use hb_*_get_from_name() to get the value corresponding to a name. * The name can be either the short or full name. * Legacy names are sanitized to currently-supported values whenever possible. * Returns 0 or -1 if no value could be found. * * Use hb_*_get_name() and hb_*_get_short_name() to get the corresponding value. * Returns NULL if the value is invalid. * * Use hb_*_get_long_name() when the name is not descriptive enough for you. * * hb_*_sanitize_name() are convenience functions for use when dealing * with full names (e.g. to translate legacy values while loading a preset). * * Names are case-insensitive; libhb will ensure that the lists do not contain * more than one entry with the same name. * * Use hb_*_get_limits() to get the minimum/maximum for lists with numerically * ordered values. * * Use hb_*_get_best() to sanitize a value based on other relevant parameters. * * Use hb_*_get_default() to get the default based on other relevant parameters. * */ void hb_common_global_init(); int hb_video_framerate_get_from_name(const char *name); const char* hb_video_framerate_get_name(int framerate); const char* hb_video_framerate_sanitize_name(const char *name); const hb_rate_t* hb_video_framerate_get_next(const hb_rate_t *last); int hb_audio_samplerate_get_best(uint32_t codec, int samplerate, int *sr_shift); int hb_audio_samplerate_get_from_name(const char *name); const char* hb_audio_samplerate_get_name(int samplerate); const hb_rate_t* hb_audio_samplerate_get_next(const hb_rate_t *last); int hb_audio_bitrate_get_best(uint32_t codec, int bitrate, int samplerate, int mixdown); int hb_audio_bitrate_get_default(uint32_t codec, int samplerate, int mixdown); void hb_audio_bitrate_get_limits(uint32_t codec, int samplerate, int mixdown, int *low, int *high); const hb_rate_t* hb_audio_bitrate_get_next(const hb_rate_t *last); void hb_video_quality_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction); const char* hb_video_quality_get_name(uint32_t codec); const char* const* hb_video_encoder_get_presets (int encoder); const char* const* hb_video_encoder_get_tunes (int encoder); const char* const* hb_video_encoder_get_profiles(int encoder); const char* const* hb_video_encoder_get_levels (int encoder); void hb_audio_quality_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction); float hb_audio_quality_get_best(uint32_t codec, float quality); float hb_audio_quality_get_default(uint32_t codec); void hb_audio_compression_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction); float hb_audio_compression_get_best(uint32_t codec, float compression); float hb_audio_compression_get_default(uint32_t codec); int hb_audio_dither_get_default(); int hb_audio_dither_get_default_method(); // default method, if enabled && supported int hb_audio_dither_is_supported(uint32_t codec); int hb_audio_dither_get_from_name(const char *name); const char* hb_audio_dither_get_description(int method); const hb_dither_t* hb_audio_dither_get_next(const hb_dither_t *last); 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); int hb_mixdown_get_discrete_channel_count(int mixdown); int hb_mixdown_get_low_freq_channel_count(int mixdown); int hb_mixdown_get_best(uint32_t codec, uint64_t layout, int mixdown); int hb_mixdown_get_default(uint32_t codec, uint64_t layout); int hb_mixdown_get_from_name(const char *name); const char* hb_mixdown_get_name(int mixdown); const char* hb_mixdown_get_short_name(int mixdown); const char* hb_mixdown_sanitize_name(const char *name); const hb_mixdown_t* hb_mixdown_get_next(const hb_mixdown_t *last); int hb_video_encoder_get_default(int muxer); int hb_video_encoder_get_from_name(const char *name); const char* hb_video_encoder_get_name(int encoder); const char* hb_video_encoder_get_short_name(int encoder); const char* hb_video_encoder_get_long_name(int encoder); const char* hb_video_encoder_sanitize_name(const char *name); const hb_encoder_t* hb_video_encoder_get_next(const hb_encoder_t *last); /* * hb_audio_encoder_get_fallback_for_passthru() will sanitize a passthru codec * to the matching audio encoder (if any is available). * * hb_audio_encoder_get_from_name(), hb_audio_encoder_sanitize_name() will * sanitize legacy encoder names, but won't convert passthru to an encoder. */ int hb_audio_encoder_get_fallback_for_passthru(int passthru); int hb_audio_encoder_get_default(int muxer); int hb_audio_encoder_get_from_name(const char *name); const char* hb_audio_encoder_get_name(int encoder); const char* hb_audio_encoder_get_short_name(int encoder); const char* hb_audio_encoder_get_long_name(int encoder); const char* hb_audio_encoder_sanitize_name(const char *name); const hb_encoder_t* hb_audio_encoder_get_next(const hb_encoder_t *last); /* * Not typically used by the UIs * (set hb_job_t.acodec_copy_mask, hb_job_t.acodec_fallback instead). */ void hb_autopassthru_apply_settings(hb_job_t *job); void hb_autopassthru_print_settings(hb_job_t *job); int hb_autopassthru_get_encoder(int in_codec, int copy_mask, int fallback, int muxer); int hb_container_get_from_name(const char *name); int hb_container_get_from_extension(const char *extension); // not really a container name const char* hb_container_get_name(int format); const char* hb_container_get_short_name(int format); const char* hb_container_get_long_name(int format); const char* hb_container_get_default_extension(int format); const char* hb_container_sanitize_name(const char *name); const hb_container_t* hb_container_get_next(const hb_container_t *last); // Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_title_set_s.cs when changing this struct struct hb_title_set_s { hb_list_t * list_title; int feature; // Detected DVD feature title }; extern int hb_gui_use_hwd_flag; typedef enum { HB_ANAMORPHIC_NONE, HB_ANAMORPHIC_STRICT, HB_ANAMORPHIC_LOOSE, HB_ANAMORPHIC_CUSTOM } hb_anamorphic_mode_t; /****************************************************************************** * hb_job_t: settings to be filled by the UI * Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_job_s.cs when changing this struct *****************************************************************************/ struct hb_job_s { /* ID assigned by UI so it can groups job passes together */ int sequence_id; /* Pointer to the title to be ripped */ hb_title_t * title; int feature; // Detected DVD feature title /* Chapter selection */ int chapter_start; int chapter_end; /* Include chapter marker track in mp4? */ int chapter_markers; /* Picture settings: crop: must be multiples of 2 (top/bottom/left/right) deinterlace: 0 or 1 width: must be a multiple of 2 height: must be a multiple of 2 grayscale: black and white encoding pixel_ratio: store pixel aspect ratio in the video pixel_aspect_width: numerator for pixel aspect ratio pixel_aspect_height: denominator for pixel aspect ratio modulus: set a number for dimensions to be multiples of maxWidth: keep width below this maxHeight: keep height below this */ int crop[4]; int deinterlace; hb_list_t * list_filter; int width; int height; int grayscale; struct { hb_anamorphic_mode_t mode; int itu_par; int par_width; int par_height; int dar_width; // 0 if normal int dar_height; // 0 if normal int keep_display_aspect; } anamorphic; int modulus; int maxWidth; int maxHeight; /* Video settings: vcodec: output codec vquality: output quality (if < 0.0, bitrate is used instead) vbitrate: output bitrate (Kbps) vrate, vrate_base: output framerate is vrate / vrate_base cfr: 0 (vfr), 1 (cfr), 2 (pfr) [see render.c] pass: 0, 1 or 2 (or -1 for scan) areBframes: boolean to note if b-frames are used */ #define HB_VCODEC_MASK 0x0000FFF #define HB_VCODEC_X264 0x0000001 #define HB_VCODEC_THEORA 0x0000002 #define HB_VCODEC_X265 0x0000004 #define HB_VCODEC_FFMPEG_MPEG4 0x0000010 #define HB_VCODEC_FFMPEG_MPEG2 0x0000020 #define HB_VCODEC_FFMPEG_VP8 0x0000040 #define HB_VCODEC_FFMPEG_MASK 0x00000F0 #define HB_VCODEC_QSV_H264 0x0000100 #define HB_VCODEC_QSV_MASK 0x0000F00 #define HB_VCODEC_H264_MASK (HB_VCODEC_X264|HB_VCODEC_QSV_H264) int vcodec; float vquality; int vbitrate; int vrate; int vrate_base; int cfr; int pass; int fastfirstpass; char *encoder_preset; char *encoder_tune; char *encoder_options; char *encoder_profile; char *encoder_level; int areBframes; int color_matrix_code; int color_prim; int color_transfer; int color_matrix; // see https://developer.apple.com/quicktime/icefloe/dispatch019.html#colr #define HB_COLR_PRI_BT709 1 #define HB_COLR_PRI_UNDEF 2 #define HB_COLR_PRI_EBUTECH 5 // use for bt470bg #define HB_COLR_PRI_SMPTEC 6 // smpte170m; also use for bt470m and smpte240m // 0, 3-4, 7-65535: reserved #define HB_COLR_TRA_BT709 1 // also use for bt470m, bt470bg and smpte170m #define HB_COLR_TRA_UNDEF 2 #define HB_COLR_TRA_SMPTE240M 7 // 0, 3-6, 8-65535: reserved #define HB_COLR_MAT_BT709 1 #define HB_COLR_MAT_UNDEF 2 #define HB_COLR_MAT_SMPTE170M 6 // also use for fcc and bt470bg #define HB_COLR_MAT_SMPTE240M 7 // 0, 3-5, 8-65535: reserved hb_list_t * list_chapter; /* List of audio settings. */ hb_list_t * list_audio; int acodec_copy_mask; // Auto Passthru allowed codecs int acodec_fallback; // Auto Passthru fallback encoder /* Subtitles */ hb_list_t * list_subtitle; hb_list_t * list_attachment; hb_metadata_t * metadata; /* * Muxer settings * mux: output file format * file: file path */ #define HB_MUX_MASK 0xFF0001 #define HB_MUX_MP4V2 0x010000 #define HB_MUX_AV_MP4 0x020000 #define HB_MUX_MASK_MP4 0x030000 #define HB_MUX_LIBMKV 0x100000 #define HB_MUX_AV_MKV 0x200000 #define HB_MUX_MASK_MKV 0x300000 #define HB_MUX_MASK_AV 0x220000 /* default muxer for each container */ #define HB_MUX_MP4 HB_MUX_AV_MP4 #define HB_MUX_MKV HB_MUX_AV_MKV int mux; char * file; /* Allow MP4 files > 4 gigs */ int largeFileSize; int mp4_optimize; int ipod_atom; int indepth_scan; hb_subtitle_config_t select_subtitle_config; int angle; // dvd angle to encode int frame_to_start; // declare eof when we hit this frame int64_t pts_to_start; // drop frames until we pass this pts // in the time-linearized input stream int frame_to_stop; // declare eof when we hit this frame int64_t pts_to_stop; // declare eof when we pass this pts in // the time-linearized input stream int start_at_preview; // if non-zero, encoding will start // at the position of preview n int seek_points; // out of N previews uint32_t frames_to_skip; // decode but discard this many frames // initially (for frame accurate positioning // to non-I frames). int use_opencl; int use_hwd; int use_decomb; int use_detelecine; #ifdef USE_QSV // QSV-specific settings struct { int decode; int async_depth; av_qsv_context *ctx; // shared encoding parameters // initialized by the QSV encoder, then used upstream (e.g. by filters) // to configure their output so that it matches what the encoder expects struct { int pic_struct; int align_width; int align_height; int is_init_done; } enc_info; } qsv; #endif #ifdef __LIBHB__ /* Internal data */ hb_handle_t * h; hb_lock_t * pause; volatile hb_error_code * done_error; volatile int * die; volatile int done; uint64_t st_pause_date; uint64_t st_paused; hb_fifo_t * fifo_mpeg2; /* MPEG-2 video ES */ hb_fifo_t * fifo_raw; /* Raw pictures */ hb_fifo_t * fifo_sync; /* Raw pictures, framerate corrected */ hb_fifo_t * fifo_render; /* Raw pictures, scaled */ hb_fifo_t * fifo_mpeg4; /* MPEG-4 video ES */ hb_list_t * list_work; hb_esconfig_t config; hb_mux_data_t * mux_data; #endif }; /* Audio starts here */ /* Audio Codecs: Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/NativeConstants.cs when changing these consts */ #define HB_ACODEC_MASK 0x00FFFF00 #define HB_ACODEC_LAME 0x00000200 #define HB_ACODEC_VORBIS 0x00000400 #define HB_ACODEC_AC3 0x00000800 #define HB_ACODEC_LPCM 0x00001000 #define HB_ACODEC_DCA 0x00002000 #define HB_ACODEC_CA_AAC 0x00004000 #define HB_ACODEC_CA_HAAC 0x00008000 #define HB_ACODEC_FFAAC 0x00010000 #define HB_ACODEC_FFMPEG 0x00020000 #define HB_ACODEC_DCA_HD 0x00040000 #define HB_ACODEC_MP3 0x00080000 #define HB_ACODEC_FFFLAC 0x00100000 #define HB_ACODEC_FFFLAC24 0x00200000 #define HB_ACODEC_FDK_AAC 0x00400000 #define HB_ACODEC_FDK_HAAC 0x00800000 #define HB_ACODEC_FF_MASK 0x00FF2800 #define HB_ACODEC_PASS_FLAG 0x40000000 #define HB_ACODEC_PASS_MASK (HB_ACODEC_MP3 | HB_ACODEC_FFAAC | HB_ACODEC_DCA_HD | HB_ACODEC_AC3 | HB_ACODEC_DCA) #define HB_ACODEC_AUTO_PASS (HB_ACODEC_PASS_MASK | HB_ACODEC_PASS_FLAG) #define HB_ACODEC_MP3_PASS (HB_ACODEC_MP3 | HB_ACODEC_PASS_FLAG) #define HB_ACODEC_AAC_PASS (HB_ACODEC_FFAAC | HB_ACODEC_PASS_FLAG) #define HB_ACODEC_AC3_PASS (HB_ACODEC_AC3 | HB_ACODEC_PASS_FLAG) #define HB_ACODEC_DCA_PASS (HB_ACODEC_DCA | HB_ACODEC_PASS_FLAG) #define HB_ACODEC_DCA_HD_PASS (HB_ACODEC_DCA_HD | HB_ACODEC_PASS_FLAG) #define HB_ACODEC_ANY (HB_ACODEC_MASK | HB_ACODEC_PASS_FLAG) #define HB_SUBSTREAM_BD_TRUEHD 0x72 #define HB_SUBSTREAM_BD_AC3 0x76 #define HB_SUBSTREAM_BD_DTSHD 0x72 #define HB_SUBSTREAM_BD_DTS 0x71 /* define an invalid VBR quality compatible with all VBR-capable codecs */ #define HB_INVALID_AUDIO_QUALITY (-3.) // Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_audio_config_s.cs when changing this struct struct hb_audio_config_s { /* Output */ struct { enum { // make sure audio->config.out.mixdown isn't treated as unsigned HB_INVALID_AMIXDOWN = -1, HB_AMIXDOWN_NONE = 0, HB_AMIXDOWN_MONO, HB_AMIXDOWN_LEFT, HB_AMIXDOWN_RIGHT, HB_AMIXDOWN_STEREO, HB_AMIXDOWN_DOLBY, HB_AMIXDOWN_DOLBYPLII, HB_AMIXDOWN_5POINT1, HB_AMIXDOWN_6POINT1, HB_AMIXDOWN_7POINT1, HB_AMIXDOWN_5_2_LFE, } mixdown; /* Audio mixdown */ int track; /* Output track number */ uint32_t codec; /* Output audio codec */ int samplerate; /* Output sample rate (Hz) */ int samples_per_frame; /* Number of samples per frame */ int bitrate; /* Output bitrate (Kbps) */ float quality; /* Output quality (encoder-specific) */ float compression_level; /* Output compression level (encoder-specific) */ 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 */ int delay; } out; /* Input */ struct { int track; /* Input track number */ PRIVATE uint32_t codec; /* Input audio codec */ PRIVATE uint32_t codec_param; /* Per-codec config info */ PRIVATE uint32_t reg_desc; /* Registration descriptor of source */ PRIVATE uint32_t stream_type; /* Stream type from source stream */ PRIVATE uint32_t substream_type; /* Substream type for multiplexed streams */ PRIVATE uint32_t version; /* Bitsream version */ PRIVATE uint32_t flags; /* Bitstream flags, codec-specific */ PRIVATE uint32_t mode; /* Bitstream mode, codec-specific */ PRIVATE int samplerate; /* Input sample rate (Hz) */ PRIVATE int samples_per_frame; /* Number of samples per frame */ PRIVATE int bitrate; /* Input bitrate (bps) */ PRIVATE int matrix_encoding; /* Source matrix encoding mode, set by the audio decoder */ PRIVATE uint64_t channel_layout; /* Source channel layout, set by the audio decoder */ PRIVATE hb_chan_map_t * channel_map; /* Source channel map, set by the audio decoder */ } in; struct { PRIVATE char description[1024]; PRIVATE char simple[1024]; PRIVATE char iso639_2[4]; PRIVATE uint8_t type; /* normal, visually impaired, director's commentary */ } lang; }; #ifdef __LIBHB__ // Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_audio_s.cs when changing this struct struct hb_audio_s { int id; hb_audio_config_t config; struct { hb_fifo_t * fifo_in; /* AC3/MPEG/LPCM ES */ hb_fifo_t * fifo_raw; /* Raw audio */ hb_fifo_t * fifo_sync; /* Resampled, synced raw audio */ hb_fifo_t * fifo_out; /* MP3/AAC/Vorbis ES */ hb_esconfig_t config; hb_mux_data_t * mux_data; hb_fifo_t * scan_cache; } priv; }; #endif // Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_chapter_s.cs when changing this struct struct hb_chapter_s { int index; int pgcn; int pgn; int cell_start; int cell_end; uint64_t block_start; uint64_t block_end; uint64_t block_count; /* Visual-friendly duration */ int hours; int minutes; int seconds; /* Exact duration (in 1/90000s) */ uint64_t duration; /* Optional chapter title */ char *title; }; /* * A subtitle track. * * Required fields when a demuxer creates a subtitle track are: * > id * - ID of this track * - must be unique for all tracks within a single job, * since it is used to look up the appropriate in-FIFO with GetFifoForId() * > format * - format of the packets the subtitle decoder work-object sends to sub->fifo_raw * - for passthru subtitles, is also the format of the final packets sent to sub->fifo_out * - PICTURESUB for banded 8-bit YAUV pixels; see decvobsub.c documentation for more info * - TEXTSUB for UTF-8 text marked up with , , or * - read by the muxers, and by the subtitle burn-in logic in the hb_sync_video work-object * > source * - used to create the appropriate subtitle decoder work-object in do_job() * > config.dest * - whether to render the subtitle on the video track (RENDERSUB) or * to pass it through its own subtitle track in the output container (PASSTHRUSUB) * - all newly created non-VOBSUB tracks should default to PASSTHRUSUB * - all newly created VOBSUB tracks should default to RENDERSUB, for legacy compatibility * > lang * - user-readable description of the subtitle track * - may correspond to the language of the track (see the 'iso639_2' field) * - may correspond to the type of track (see the 'type' field; ex: "Closed Captions") * > iso639_2 * - language code for the subtitle, or "und" if unknown * * Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_subtitle_s.cs when changing this struct */ struct hb_subtitle_s { int id; int track; int out_track; hb_subtitle_config_t config; enum subtype { PICTURESUB, TEXTSUB } format; enum subsource { VOBSUB, SRTSUB, CC608SUB, /*unused*/CC708SUB, UTF8SUB, TX3GSUB, SSASUB, PGSSUB } source; char lang[1024]; char iso639_2[4]; uint8_t type; /* Closed Caption, Childrens, Directors etc */ // Color lookup table for VOB subtitle tracks. Each entry is in YCbCr format. // Must be filled out by the demuxer for VOB subtitle tracks. uint32_t palette[16]; uint8_t palette_set; int width; int height; // Codec private data for subtitles originating from FFMPEG sources uint8_t * extradata; int extradata_size; int hits; /* How many hits/occurrences of this subtitle */ int forced_hits; /* How many forced hits in this subtitle */ #ifdef __LIBHB__ /* Internal data */ PRIVATE uint32_t codec; /* Input "codec" */ PRIVATE uint32_t reg_desc; /* registration descriptor of source */ PRIVATE uint32_t stream_type; /* stream type from source stream */ PRIVATE uint32_t substream_type;/* substream for multiplexed streams */ hb_fifo_t * fifo_in; /* SPU ES */ hb_fifo_t * fifo_raw; /* Decoded SPU */ hb_fifo_t * fifo_sync;/* Synced */ hb_fifo_t * fifo_out; /* Correct Timestamps, ready to be muxed */ hb_mux_data_t * mux_data; #endif }; /* * An attachment. * * These are usually used for attaching embedded fonts to movies containing SSA subtitles. */ struct hb_attachment_s { enum attachtype { FONT_TTF_ATTACH, HB_ART_ATTACH } type; char * name; char * data; int size; }; struct hb_coverart_s { uint8_t *data; uint32_t size; enum arttype { HB_ART_UNDEFINED, HB_ART_BMP, HB_ART_GIF, HB_ART_PNG, HB_ART_JPEG } type; }; struct hb_metadata_s { char *name; char *artist; // Actors char *composer; char *release_date; char *comment; char *album; // DVD char *album_artist; // Director char *genre; char *description; char *long_description; hb_list_t * list_coverart; }; // Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_title_s.cs when changing this struct struct hb_title_s { enum { HB_DVD_TYPE, HB_BD_TYPE, HB_STREAM_TYPE, HB_FF_STREAM_TYPE } type; uint32_t reg_desc; char path[1024]; char name[1024]; int index; int playlist; int vts; int ttn; int cell_start; int cell_end; uint64_t block_start; uint64_t block_end; uint64_t block_count; int angle_count; void *opaque_priv; /* Visual-friendly duration */ int hours; int minutes; int seconds; /* Exact duration (in 1/90000s) */ uint64_t duration; double aspect; // aspect ratio for the title's video double container_aspect; // aspect ratio from container (0 if none) int has_resolution_change; int width; int height; int pixel_aspect_width; int pixel_aspect_height; int color_prim; int color_transfer; int color_matrix; int rate; int rate_base; int crop[4]; enum {HB_DVD_DEMUXER, HB_TS_DEMUXER, HB_PS_DEMUXER, HB_NULL_DEMUXER} demuxer; int detected_interlacing; int pcr_pid; /* PCR PID for TS streams */ int video_id; /* demuxer stream id for video */ int video_codec; /* worker object id of video codec */ uint32_t video_stream_type; /* stream type from source stream */ int video_codec_param; /* codec specific config */ char *video_codec_name; int video_bitrate; char *container_name; int data_rate; // additional supported video decoders (e.g. HW-accelerated implementations) int video_decode_support; #define HB_DECODE_SUPPORT_SW 0x01 // software (libavcodec or mpeg2dec) #define HB_DECODE_SUPPORT_QSV 0x02 // Intel Quick Sync Video hb_metadata_t *metadata; hb_list_t * list_chapter; hb_list_t * list_audio; hb_list_t * list_subtitle; hb_list_t * list_attachment; #define HB_TITLE_JOBS #if defined(HB_TITLE_JOBS) hb_job_t * job; #endif uint32_t flags; // set if video stream doesn't have IDR frames #define HBTF_NO_IDR (1 << 0) #define HBTF_SCAN_COMPLETE (1 << 1) // whether OpenCL scaling is supported for this source int opencl_support; int hwd_support; // TODO: merge to video_decode_support }; // Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_state_s.cs when changing this struct struct hb_state_s { #define HB_STATE_IDLE 1 #define HB_STATE_SCANNING 2 #define HB_STATE_SCANDONE 4 #define HB_STATE_WORKING 8 #define HB_STATE_PAUSED 16 #define HB_STATE_WORKDONE 32 #define HB_STATE_MUXING 64 #define HB_STATE_SEARCHING 128 int state; union { struct { /* HB_STATE_SCANNING */ float progress; int preview_cur; int preview_count; int title_cur; int title_count; } scanning; struct { /* HB_STATE_WORKING */ float progress; int job_cur; int job_count; float rate_cur; float rate_avg; int hours; int minutes; int seconds; int sequence_id; } working; struct { /* HB_STATE_WORKDONE */ hb_error_code error; } workdone; struct { /* HB_STATE_MUXING */ float progress; } muxing; } param; }; typedef struct hb_work_info_s { const char * name; int profile; int level; int bitrate; int rate; int rate_base; uint32_t version; uint32_t flags; uint32_t mode; union { struct { // info only valid for video decoders int width; int height; int pixel_aspect_width; int pixel_aspect_height; int color_prim; int color_transfer; int color_matrix; int video_decode_support; }; struct { // info only valid for audio decoders uint64_t channel_layout; hb_chan_map_t * channel_map; int samples_per_frame; int matrix_encoding; }; }; } hb_work_info_t; struct hb_work_object_s { int id; char * name; #ifdef __LIBHB__ int (* init) ( hb_work_object_t *, hb_job_t * ); int (* work) ( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); void (* close) ( hb_work_object_t * ); /* the info entry point is used by scan to get bitstream information * during a decode (i.e., it should only be called after at least one * call to the 'work' entry point). currently it's only called for * video streams & can be null for other work objects. */ int (* info) ( hb_work_object_t *, hb_work_info_t * ); /* the bitstream info entry point is used by scan to get bitstream * information from a buffer. it doesn't have to be called during a * decode (it can be called even if init & work haven't been). * currently it's only called for audio streams & can be null for * other work objects. */ int (* bsinfo) ( hb_work_object_t *, const hb_buffer_t *, hb_work_info_t * ); void (* flush) ( hb_work_object_t * ); hb_fifo_t * fifo_in; hb_fifo_t * fifo_out; hb_esconfig_t * config; /* Pointer hb_audio_t so we have access to the info in the audio worker threads. */ hb_audio_t * audio; /* Pointer hb_subtitle_t so we have access to the info in the subtitle worker threads. */ hb_subtitle_t * subtitle; hb_work_private_t * private_data; hb_thread_t * thread; volatile int * done; int status; int codec_param; hb_title_t * title; hb_work_object_t * next; int thread_sleep_interval; #endif }; extern hb_work_object_t hb_sync_video; extern hb_work_object_t hb_sync_audio; extern hb_work_object_t hb_decvobsub; extern hb_work_object_t hb_encvobsub; extern hb_work_object_t hb_deccc608; extern hb_work_object_t hb_decsrtsub; extern hb_work_object_t hb_decutf8sub; extern hb_work_object_t hb_dectx3gsub; extern hb_work_object_t hb_decssasub; extern hb_work_object_t hb_decpgssub; extern hb_work_object_t hb_encavcodec; extern hb_work_object_t hb_encqsv; extern hb_work_object_t hb_encx264; extern hb_work_object_t hb_enctheora; extern hb_work_object_t hb_encx265; extern hb_work_object_t hb_decavcodeca; extern hb_work_object_t hb_decavcodecv; extern hb_work_object_t hb_declpcm; extern hb_work_object_t hb_enclame; extern hb_work_object_t hb_encvorbis; extern hb_work_object_t hb_muxer; extern hb_work_object_t hb_encca_aac; extern hb_work_object_t hb_encca_haac; extern hb_work_object_t hb_encavcodeca; extern hb_work_object_t hb_reader; #define HB_FILTER_OK 0 #define HB_FILTER_DELAY 1 #define HB_FILTER_FAILED 2 #define HB_FILTER_DROP 3 #define HB_FILTER_DONE 4 int hb_use_dxva(hb_title_t *title); typedef struct hb_filter_init_s { hb_job_t * job; int pix_fmt; int width; int height; int par_width; int par_height; int crop[4]; int vrate_base; int vrate; int cfr; int use_dxva; } hb_filter_init_t; typedef struct hb_filter_info_s { char human_readable_desc[128]; hb_filter_init_t out; } hb_filter_info_t; struct hb_filter_object_s { int id; int enforce_order; char * name; char * settings; #ifdef __LIBHB__ int (* init) ( hb_filter_object_t *, hb_filter_init_t * ); int (* work) ( hb_filter_object_t *, hb_buffer_t **, hb_buffer_t ** ); void (* close) ( hb_filter_object_t * ); int (* info) ( hb_filter_object_t *, hb_filter_info_t * ); hb_fifo_t * fifo_in; hb_fifo_t * fifo_out; hb_subtitle_t * subtitle; hb_filter_private_t * private_data; hb_thread_t * thread; volatile int * done; int status; // Filters can drop frames and thus chapter marks // These are used to bridge the chapter to the next buffer int chapter_val; int64_t chapter_time; #endif }; // Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_filter_ids.cs when changing this enum enum { // for QSV - important to have before other filters HB_FILTER_QSV_PRE = 1, // First, filters that may change the framerate (drop or dup frames) HB_FILTER_DETELECINE, HB_FILTER_DECOMB, HB_FILTER_DEINTERLACE, HB_FILTER_VFR, // Filters that must operate on the original source image are next HB_FILTER_DEBLOCK, HB_FILTER_DENOISE, HB_FILTER_HQDN3D = HB_FILTER_DENOISE, HB_FILTER_NLMEANS, HB_FILTER_RENDER_SUB, HB_FILTER_CROP_SCALE, // Finally filters that don't care what order they are in, // except that they must be after the above filters HB_FILTER_ROTATE, // for QSV - important to have as a last one HB_FILTER_QSV_POST, // default MSDK VPP filter HB_FILTER_QSV, }; hb_filter_object_t * hb_filter_init( int filter_id ); hb_filter_object_t * hb_filter_copy( hb_filter_object_t * filter ); hb_list_t *hb_filter_list_copy(const hb_list_t *src); void hb_filter_close( hb_filter_object_t ** ); char * hb_generate_filter_settings(int filter_id, const char *preset, const char *tune); int hb_validate_filter_settings(int filter_id, const char *filter_param); int hb_validate_param_string(const char *regex_pattern, const char *param_string); typedef void hb_error_handler_t( const char *errmsg ); extern void hb_register_error_handler( hb_error_handler_t * handler ); char * hb_strdup_printf(const char *fmt, ...) HB_WPRINTF(1, 2); char * hb_strncat_dup( const char * s1, const char * s2, size_t n ); int hb_yuv2rgb(int yuv); int hb_rgb2yuv(int rgb); const char * hb_subsource_name( int source ); // unparse a set of x264 settings to an HB encopts string char * hb_x264_param_unparse(const char *x264_preset, const char *x264_tune, const char *x264_encopts, const char *h264_profile, const char *h264_level, int width, int height); #define HB_API_OLD_PRESET_GETTERS #ifdef HB_API_OLD_PRESET_GETTERS // x264 preset/tune, qsv preset & h264 profile/level helpers const char * const * hb_x264_presets(); const char * const * hb_x264_tunes(); #ifdef USE_QSV const char * const * hb_qsv_presets(); #endif const char * const * hb_h264_profiles(); const char * const * hb_h264_levels(); #endif // x264 option name/synonym helper const char * hb_x264_encopt_name( const char * name ); #ifdef USE_X265 // x265 option name/synonym helper const char * hb_x265_encopt_name( const char * name ); #endif #endif HandBrake-0.10.2/libhb/scan.c0000664000175200017520000013056512524417313016223 0ustar handbrakehandbrake/* scan.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "hb.h" #include "opencl.h" #include "hbffmpeg.h" typedef struct { hb_handle_t * h; volatile int * die; char * path; int title_index; hb_title_set_t * title_set; hb_bd_t * bd; hb_dvd_t * dvd; hb_stream_t * stream; hb_batch_t * batch; int preview_count; int store_previews; uint64_t min_title_duration; } hb_scan_t; #define PREVIEW_READ_THRESH (1024 * 1024 * 300) static void ScanFunc( void * ); static int DecodePreviews( hb_scan_t *, hb_title_t * title, int flush ); static void LookForAudio( hb_title_t * title, hb_buffer_t * b ); static int AllAudioOK( hb_title_t * title ); static void UpdateState1(hb_scan_t *scan, int title); static void UpdateState2(hb_scan_t *scan, int title); static void UpdateState3(hb_scan_t *scan, int preview); static const char *aspect_to_string( double aspect ) { switch ( (int)(aspect * 9.) ) { case 9 * 4 / 3: return "4:3"; case 9 * 16 / 9: return "16:9"; } static char arstr[32]; sprintf( arstr, aspect >= 1.? "%.2f:1" : "1:%.2f", aspect ); return arstr; } hb_thread_t * hb_scan_init( hb_handle_t * handle, 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_scan_t * data = calloc( sizeof( hb_scan_t ), 1 ); data->h = handle; data->die = die; data->path = strdup( path ); data->title_index = title_index; data->title_set = title_set; data->preview_count = preview_count; data->store_previews = store_previews; data->min_title_duration = min_duration; return hb_thread_init( "scan", ScanFunc, data, HB_NORMAL_PRIORITY ); } static void ScanFunc( void * _data ) { hb_scan_t * data = (hb_scan_t *) _data; hb_title_t * title; int i; int feature = 0; data->bd = NULL; data->dvd = NULL; data->stream = NULL; /* Try to open the path as a DVD. If it fails, try as a file */ if( ( data->bd = hb_bd_init( data->path ) ) ) { hb_log( "scan: BD has %d title(s)", hb_bd_title_count( data->bd ) ); if( data->title_index ) { /* Scan this title only */ hb_list_add( data->title_set->list_title, hb_bd_title_scan( data->bd, data->title_index, 0 ) ); } else { /* Scan all titles */ for( i = 0; i < hb_bd_title_count( data->bd ); i++ ) { UpdateState1(data, i + 1); hb_list_add( data->title_set->list_title, hb_bd_title_scan( data->bd, i + 1, data->min_title_duration ) ); } feature = hb_bd_main_feature( data->bd, data->title_set->list_title ); } } else if( ( data->dvd = hb_dvd_init( data->path ) ) ) { hb_log( "scan: DVD has %d title(s)", hb_dvd_title_count( data->dvd ) ); if( data->title_index ) { /* Scan this title only */ hb_list_add( data->title_set->list_title, hb_dvd_title_scan( data->dvd, data->title_index, 0 ) ); } else { /* Scan all titles */ for( i = 0; i < hb_dvd_title_count( data->dvd ); i++ ) { UpdateState1(data, i + 1); hb_list_add( data->title_set->list_title, hb_dvd_title_scan( data->dvd, i + 1, data->min_title_duration ) ); } feature = hb_dvd_main_feature( data->dvd, data->title_set->list_title ); } } else if ( ( data->batch = hb_batch_init( data->path ) ) ) { if( data->title_index ) { /* Scan this title only */ title = hb_batch_title_scan( data->batch, data->title_index ); if ( title ) { hb_list_add( data->title_set->list_title, title ); } } else { /* Scan all titles */ for( i = 0; i < hb_batch_title_count( data->batch ); i++ ) { hb_title_t * title; UpdateState1(data, i + 1); title = hb_batch_title_scan( data->batch, i + 1 ); if ( title != NULL ) { hb_list_add( data->title_set->list_title, title ); } } } } else { data->title_index = 1; hb_title_t * title = hb_title_init( data->path, data->title_index ); if ( (data->stream = hb_stream_open( data->path, title, 1 ) ) != NULL ) { title = hb_stream_title_scan( data->stream, title ); if ( title ) hb_list_add( data->title_set->list_title, title ); } else { hb_title_close( &title ); hb_log( "scan: unrecognized file type" ); return; } } for( i = 0; i < hb_list_count( data->title_set->list_title ); ) { int j, npreviews; hb_audio_t * audio; if ( *data->die ) { goto finish; } title = hb_list_item( data->title_set->list_title, i ); UpdateState2(data, i + 1); /* Decode previews */ /* this will also detect more AC3 / DTS information */ npreviews = DecodePreviews( data, title, 1 ); if (npreviews < 2) { npreviews = DecodePreviews( data, title, 0 ); } if (npreviews == 0) { /* TODO: free things */ hb_list_rem( data->title_set->list_title, title ); for( j = 0; j < hb_list_count( title->list_audio ); j++) { audio = hb_list_item( title->list_audio, j ); if ( audio->priv.scan_cache ) { hb_fifo_flush( audio->priv.scan_cache ); hb_fifo_close( &audio->priv.scan_cache ); } } hb_title_close( &title ); continue; } /* Make sure we found audio rates and bitrates */ for( j = 0; j < hb_list_count( title->list_audio ); ) { audio = hb_list_item( title->list_audio, j ); if ( audio->priv.scan_cache ) { hb_fifo_flush( audio->priv.scan_cache ); hb_fifo_close( &audio->priv.scan_cache ); } if( !audio->config.in.bitrate ) { hb_log( "scan: removing audio 0x%x because no bitrate found", audio->id ); hb_list_rem( title->list_audio, audio ); free( audio ); continue; } j++; } if ( data->dvd || data->bd ) { // The subtitle width and height needs to be set to the // title widht and height for DVDs. title width and // height don't get set until we decode previews, so // we can't set subtitle width/height till we get here. for( j = 0; j < hb_list_count( title->list_subtitle ); j++ ) { hb_subtitle_t *subtitle = hb_list_item( title->list_subtitle, j ); if ( subtitle->source == VOBSUB || subtitle->source == PGSSUB ) { subtitle->width = title->width; subtitle->height = title->height; } } } i++; } data->title_set->feature = feature; /* Mark title scan complete and init jobs */ for( i = 0; i < hb_list_count( data->title_set->list_title ); i++ ) { title = hb_list_item( data->title_set->list_title, i ); title->flags |= HBTF_SCAN_COMPLETE; #if defined(HB_TITLE_JOBS) title->job = hb_job_init( title ); #endif } finish: if( data->bd ) { hb_bd_close( &data->bd ); } if( data->dvd ) { hb_dvd_close( &data->dvd ); } if (data->stream) { hb_stream_close(&data->stream); } if( data->batch ) { hb_batch_close( &data->batch ); } free( data->path ); free( data ); _data = NULL; hb_buffer_pool_free(); } // ----------------------------------------------- // stuff related to cropping #define DARK 32 static inline int absdiff( int x, int y ) { return x < y ? y - x : x - y; } static inline int clampBlack( int x ) { // luma 'black' is 16 and anything less should be clamped at 16 return x < 16 ? 16 : x; } static int row_all_dark( hb_buffer_t* buf, int row ) { int width = buf->plane[0].width; int stride = buf->plane[0].stride; uint8_t *luma = buf->plane[0].data + stride * row; // compute the average luma value of the row int i, avg = 0; for ( i = 0; i < width; ++i ) { avg += clampBlack( luma[i] ); } avg /= width; if ( avg >= DARK ) return 0; // since we're trying to detect smooth borders, only take the row if // all pixels are within +-16 of the average (this range is fairly coarse // but there's a lot of quantization noise for luma values near black // so anything less will fail to crop because of the noise). for ( i = 0; i < width; ++i ) { if ( absdiff( avg, clampBlack( luma[i] ) ) > 16 ) return 0; } return 1; } static int column_all_dark( hb_buffer_t* buf, int top, int bottom, int col ) { int stride = buf->plane[0].stride; int height = buf->plane[0].height - top - bottom; uint8_t *luma = buf->plane[0].data + stride * top + col; // compute the average value of the column int i = height, avg = 0, row = 0; for ( ; --i >= 0; row += stride ) { avg += clampBlack( luma[row] ); } avg /= height; if ( avg >= DARK ) return 0; // since we're trying to detect smooth borders, only take the column if // all pixels are within +-16 of the average. i = height, row = 0; for ( ; --i >= 0; row += stride ) { if ( absdiff( avg, clampBlack( luma[row] ) ) > 16 ) return 0; } return 1; } #undef DARK typedef struct { int n; int *t; int *b; int *l; int *r; } crop_record_t; static crop_record_t * crop_record_init( int max_previews ) { crop_record_t *crops = calloc( 1, sizeof(*crops) ); crops->t = calloc( max_previews, sizeof(int) ); crops->b = calloc( max_previews, sizeof(int) ); crops->l = calloc( max_previews, sizeof(int) ); crops->r = calloc( max_previews, sizeof(int) ); return crops; } static void crop_record_free( crop_record_t *crops ) { free( crops->t ); free( crops->b ); free( crops->l ); free( crops->r ); free( crops ); } static void record_crop( crop_record_t *crops, int t, int b, int l, int r ) { crops->t[crops->n] = t; crops->b[crops->n] = b; crops->l[crops->n] = l; crops->r[crops->n] = r; ++crops->n; } static int compare_int( const void *a, const void *b ) { return *(const int *)a - *(const int *)b; } static void sort_crops( crop_record_t *crops ) { qsort( crops->t, crops->n, sizeof(crops->t[0]), compare_int ); qsort( crops->b, crops->n, sizeof(crops->t[0]), compare_int ); qsort( crops->l, crops->n, sizeof(crops->t[0]), compare_int ); qsort( crops->r, crops->n, sizeof(crops->t[0]), compare_int ); } // ----------------------------------------------- // stuff related to title width/height/aspect info typedef struct { int count; /* number of times we've seen this info entry */ hb_work_info_t info; /* copy of info entry */ } info_list_t; static void remember_info( info_list_t *info_list, hb_work_info_t *info ) { for ( ; info_list->count; ++info_list ) { if ( memcmp( &info_list->info, info, sizeof(*info) ) == 0 ) { // we found a match - bump its count ++info_list->count; return; } } // no match found - add new entry to list (info_list points to // the first free slot). NB - we assume that info_list was allocated // so that it's big enough even if there are no dups. I.e., 10 slots // allocated if there are 10 previews. info_list->count = 1; info_list->info = *info; } static void most_common_info( info_list_t *info_list, hb_work_info_t *info ) { int i, biggest = 0; for ( i = 1; info_list[i].count; ++i ) { if ( info_list[i].count > info_list[biggest].count ) biggest = i; } *info = info_list[biggest].info; } static int has_resolution_change( info_list_t *info_list ) { int w, h, i; if( !info_list[0].count ) return 0; w = info_list[0].info.width; h = info_list[0].info.height; for ( i = 1; info_list[i].count; ++i ) { if ( w != info_list[i].info.width || h != info_list[i].info.height ) return 1; } return 0; } static int is_close_to( int val, int target, int thresh ) { int diff = val - target; diff = diff < 0 ? -diff : diff; return diff < thresh; } /*********************************************************************** * DecodePreviews *********************************************************************** * Decode 10 pictures for the given title. * It assumes that data->reader and data->vts have successfully been * DVDOpen()ed and ifoOpen()ed. **********************************************************************/ static int DecodePreviews( hb_scan_t * data, hb_title_t * title, int flush ) { int i, npreviews = 0, abort = 0; hb_buffer_t * buf, * buf_es; hb_list_t * list_es; int progressive_count = 0; int pulldown_count = 0; int doubled_frame_count = 0; int interlaced_preview_count = 0; int frame_wait = 0; int cc_wait = 10; int frames; hb_stream_t * stream = NULL; info_list_t * info_list = calloc( data->preview_count+1, sizeof(*info_list) ); crop_record_t *crops = crop_record_init( data->preview_count ); list_es = hb_list_init(); if( data->batch ) { hb_log( "scan: decoding previews for title %d (%s)", title->index, title->path ); } else { hb_log( "scan: decoding previews for title %d", title->index ); } if (data->bd) { hb_bd_start( data->bd, title ); hb_log( "scan: title angle(s) %d", title->angle_count ); } else if (data->dvd) { hb_dvd_start( data->dvd, title, 1 ); title->angle_count = hb_dvd_angle_count( data->dvd ); hb_log( "scan: title angle(s) %d", title->angle_count ); } else if (data->batch) { stream = hb_stream_open( title->path, title, 0 ); } else if (data->stream) { stream = hb_stream_open( data->path, title, 0 ); } if (title->video_codec == WORK_NONE) { hb_error("No video decoder set!"); return 0; } hb_work_object_t *vid_decoder = hb_get_work(title->video_codec); vid_decoder->codec_param = title->video_codec_param; vid_decoder->title = title; vid_decoder->init( vid_decoder, NULL ); for( i = 0; i < data->preview_count; i++ ) { int j; UpdateState3(data, i + 1); if ( *data->die ) { free( info_list ); crop_record_free( crops ); return 0; } if (data->bd) { if( !hb_bd_seek( data->bd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) ) { continue; } } if (data->dvd) { if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) ) { continue; } } else if (stream) { /* we start reading streams at zero rather than 1/11 because * short streams may have only one sequence header in the entire * file and we need it to decode any previews. * * Also, seeking to position 0 loses the palette of avi files * so skip initial seek */ if (i != 0) { if (!hb_stream_seek(stream, (float)i / (data->preview_count + 1.0))) { continue; } } else { hb_stream_set_need_keyframe(stream, 1); } } hb_deep_log( 2, "scan: preview %d", i + 1 ); if (flush && vid_decoder->flush) vid_decoder->flush( vid_decoder ); if (title->flags & HBTF_NO_IDR) { if (!flush) { // If we are doing the first previews decode attempt, // set this threshold high so that we get the best // quality frames possible. frame_wait = 100; } else { // If we failed to get enough valid frames in the first // previews decode attempt, lower the threshold to improve // our chances of getting something to work with. frame_wait = 10; } } else { // For certain mpeg-2 streams, libav is delivering a // dummy first frame that is all black. So always skip // one frame frame_wait = 1; } frames = 0; hb_buffer_t * vid_buf = NULL; int total_read = 0, packets = 0; while (total_read < PREVIEW_READ_THRESH || (!AllAudioOK(title) && packets < 10000)) { if (data->bd) { if( (buf = hb_bd_read( data->bd )) == NULL ) { if ( vid_buf ) { break; } hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 ); abort = 1; goto skip_preview; } } else if (data->dvd) { if( (buf = hb_dvd_read( data->dvd )) == NULL ) { if ( vid_buf ) { break; } hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 ); abort = 1; goto skip_preview; } } else if (stream) { if ( (buf = hb_stream_read(stream)) == NULL ) { if ( vid_buf ) { break; } hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 ); abort = 1; goto skip_preview; } } else { // Silence compiler warning buf = NULL; hb_error( "Error: This can't happen!" ); abort = 1; goto skip_preview; } if (buf->size <= 0) { hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 ); abort = 1; goto skip_preview; } total_read += buf->size; packets++; (hb_demux[title->demuxer])(buf, list_es, 0 ); while( ( buf_es = hb_list_item( list_es, 0 ) ) ) { hb_list_rem( list_es, buf_es ); if( buf_es->s.id == title->video_id && vid_buf == NULL ) { vid_decoder->work( vid_decoder, &buf_es, &vid_buf ); // There are 2 conditions we decode additional // video frames for during scan. // 1. We did not detect IDR frames, so the initial video // frames may be corrupt. We docode extra frames to // increase the probability of a complete preview frame // 2. Some frames do not contain CC data, even though // CCs are present in the stream. So we need to decode // additional frames to find the CCs. if (vid_buf != NULL && (frame_wait || cc_wait)) { if (frames > 0 && vid_buf->s.frametype == HB_FRAME_I) frame_wait = 0; if (frame_wait || cc_wait) { hb_buffer_close(&vid_buf); if (frame_wait) frame_wait--; if (cc_wait) cc_wait--; } frames++; } } else if( ! AllAudioOK( title ) ) { LookForAudio( title, buf_es ); buf_es = NULL; } if ( buf_es ) hb_buffer_close( &buf_es ); } if( vid_buf && AllAudioOK( title ) ) break; } if( ! vid_buf ) { hb_log( "scan: could not get a decoded picture" ); continue; } /* Get size and rate infos */ hb_work_info_t vid_info; if( !vid_decoder->info( vid_decoder, &vid_info ) ) { /* * Could not fill vid_info, don't continue and try to use vid_info * in this case. */ if (vid_buf) { hb_buffer_close( &vid_buf ); } hb_log( "scan: could not get a video information" ); continue; } remember_info( info_list, &vid_info ); if( is_close_to( vid_info.rate_base, 900900, 100 ) && ( vid_buf->s.flags & PIC_FLAG_REPEAT_FIRST_FIELD ) ) { /* Potentially soft telecine material */ pulldown_count++; } if( vid_buf->s.flags & PIC_FLAG_REPEAT_FRAME ) { // AVCHD-Lite specifies that all streams are // 50 or 60 fps. To produce 25 or 30 fps, camera // makers are repeating all frames. doubled_frame_count++; } if( is_close_to( vid_info.rate_base, 1126125, 100 ) ) { // Frame FPS is 23.976 (meaning it's progressive), so start keeping // track of how many are reporting at that speed. When enough // show up that way, we want to make that the overall title FPS. progressive_count++; } while( ( buf_es = hb_list_item( list_es, 0 ) ) ) { hb_list_rem( list_es, buf_es ); hb_buffer_close( &buf_es ); } /* Check preview for interlacing artifacts */ if( hb_detect_comb( vid_buf, 10, 30, 9, 10, 30, 9 ) ) { hb_deep_log( 2, "Interlacing detected in preview frame %i", i+1); interlaced_preview_count++; } if( data->store_previews ) { hb_save_preview( data->h, title->index, i, vid_buf ); } /* Detect black borders */ int top, bottom, left, right; int h4 = vid_info.height / 4, w4 = vid_info.width / 4; // When widescreen content is matted to 16:9 or 4:3 there's sometimes // a thin border on the outer edge of the matte. On TV content it can be // "line 21" VBI data that's normally hidden in the overscan. For HD // content it can just be a diagnostic added in post production so that // the frame borders are visible. We try to ignore these borders so // we can crop the matte. The border width depends on the resolution // (12 pixels on 1080i looks visually the same as 4 pixels on 480i) // so we allow the border to be up to 1% of the frame height. const int border = vid_info.height / 100; for ( top = border; top < h4; ++top ) { if ( ! row_all_dark( vid_buf, top ) ) break; } if ( top <= border ) { // we never made it past the border region - see if the rows we // didn't check are dark or if we shouldn't crop at all. for ( top = 0; top < border; ++top ) { if ( ! row_all_dark( vid_buf, top ) ) break; } if ( top >= border ) { top = 0; } } for ( bottom = border; bottom < h4; ++bottom ) { if ( ! row_all_dark( vid_buf, vid_info.height - 1 - bottom ) ) break; } if ( bottom <= border ) { for ( bottom = 0; bottom < border; ++bottom ) { if ( ! row_all_dark( vid_buf, vid_info.height - 1 - bottom ) ) break; } if ( bottom >= border ) { bottom = 0; } } for ( left = 0; left < w4; ++left ) { if ( ! column_all_dark( vid_buf, top, bottom, left ) ) break; } for ( right = 0; right < w4; ++right ) { if ( ! column_all_dark( vid_buf, top, bottom, vid_info.width - 1 - right ) ) break; } // only record the result if all the crops are less than a quarter of // the frame otherwise we can get fooled by frames with a lot of black // like titles, credits & fade-thru-black transitions. if ( top < h4 && bottom < h4 && left < w4 && right < w4 ) { record_crop( crops, top, bottom, left, right ); } ++npreviews; skip_preview: /* Make sure we found audio rates and bitrates */ for( j = 0; j < hb_list_count( title->list_audio ); j++ ) { hb_audio_t * audio = hb_list_item( title->list_audio, j ); if ( audio->priv.scan_cache ) { hb_fifo_flush( audio->priv.scan_cache ); } } if (vid_buf) { hb_buffer_close( &vid_buf ); } if (abort) { break; } } UpdateState3(data, i); vid_decoder->close( vid_decoder ); free( vid_decoder ); if (stream != NULL) { hb_stream_close(&stream); } if ( npreviews ) { // use the most common frame info for our final title dimensions hb_work_info_t vid_info; most_common_info( info_list, &vid_info ); title->has_resolution_change = has_resolution_change( info_list ); if ( title->video_codec_name == NULL ) { title->video_codec_name = strdup( vid_info.name ); } title->width = vid_info.width; title->height = vid_info.height; if ( vid_info.rate && vid_info.rate_base ) { // if the frame rate is very close to one of our "common" framerates, // assume it actually is said frame rate; e.g. some 24000/1001 sources // may have a rate_base of 1126124 (instead of 1126125) const hb_rate_t *video_framerate = NULL; while ((video_framerate = hb_video_framerate_get_next(video_framerate)) != NULL) { if (is_close_to(vid_info.rate_base, video_framerate->rate, 100)) { vid_info.rate_base = video_framerate->rate; break; } } title->rate = vid_info.rate; title->rate_base = vid_info.rate_base; if( vid_info.rate_base == 900900 ) { if( npreviews >= 4 && pulldown_count >= npreviews / 4 ) { title->rate_base = 1126125; hb_deep_log( 2, "Pulldown detected, setting fps to 23.976" ); } if( npreviews >= 2 && progressive_count >= npreviews / 2 ) { // We've already deduced that the frame rate is 23.976, // so set it back again. title->rate_base = 1126125; hb_deep_log( 2, "Title's mostly NTSC Film, setting fps to 23.976" ); } } if( npreviews >= 2 && doubled_frame_count >= 3 * npreviews / 4 ) { // We've detected that a significant number of the frames // have been doubled in duration by repeat flags. title->rate_base = 2 * vid_info.rate_base; hb_deep_log( 2, "Repeat frames detected, setting fps to %.3f", (float)title->rate / title->rate_base ); } } title->video_bitrate = vid_info.bitrate; if( vid_info.pixel_aspect_width && vid_info.pixel_aspect_height ) { title->pixel_aspect_width = vid_info.pixel_aspect_width; title->pixel_aspect_height = vid_info.pixel_aspect_height; } title->color_prim = vid_info.color_prim; title->color_transfer = vid_info.color_transfer; title->color_matrix = vid_info.color_matrix; title->video_decode_support = vid_info.video_decode_support; // TODO: check video dimensions title->opencl_support = !!hb_opencl_available(); // compute the aspect ratio based on the storage dimensions and the // pixel aspect ratio (if supplied) or just storage dimensions if no PAR. title->aspect = (double)title->width / (double)title->height; title->aspect *= (double)title->pixel_aspect_width / (double)title->pixel_aspect_height; // For unknown reasons some French PAL DVDs put the original // content's aspect ratio into the mpeg PAR even though it's // the wrong PAR for the DVD. Apparently they rely on the fact // that DVD players ignore the content PAR and just use the // aspect ratio from the DVD metadata. So, if the aspect computed // from the PAR is different from the container's aspect we use // the container's aspect & recompute the PAR from it. if( title->container_aspect && (int)(title->aspect * 9) != (int)(title->container_aspect * 9) ) { hb_log("scan: content PAR gives wrong aspect %.2f; " "using container aspect %.2f", title->aspect, title->container_aspect ); title->aspect = title->container_aspect; hb_reduce( &title->pixel_aspect_width, &title->pixel_aspect_height, (int)(title->aspect * title->height + 0.5), title->width ); } // don't try to crop unless we got at least 3 previews if ( crops->n > 2 ) { sort_crops( crops ); // The next line selects median cropping - at least // 50% of the frames will have their borders removed. // Other possible choices are loose cropping (i = 0) where // no non-black pixels will be cropped from any frame and a // tight cropping (i = crops->n - (crops->n >> 2)) where at // least 75% of the frames will have their borders removed. i = crops->n >> 1; title->crop[0] = EVEN( crops->t[i] ); title->crop[1] = EVEN( crops->b[i] ); title->crop[2] = EVEN( crops->l[i] ); title->crop[3] = EVEN( crops->r[i] ); } hb_log( "scan: %d previews, %dx%d, %.3f fps, autocrop = %d/%d/%d/%d, " "aspect %s, PAR %d:%d", npreviews, title->width, title->height, (float) title->rate / (float) title->rate_base, title->crop[0], title->crop[1], title->crop[2], title->crop[3], aspect_to_string( title->aspect ), title->pixel_aspect_width, title->pixel_aspect_height ); if( interlaced_preview_count >= ( npreviews / 2 ) ) { hb_log("Title is likely interlaced or telecined (%i out of %i previews). You should do something about that.", interlaced_preview_count, npreviews); title->detected_interlacing = 1; } else { title->detected_interlacing = 0; } } crop_record_free( crops ); free( info_list ); while( ( buf_es = hb_list_item( list_es, 0 ) ) ) { hb_list_rem( list_es, buf_es ); hb_buffer_close( &buf_es ); } hb_list_close( &list_es ); if (data->bd) hb_bd_stop( data->bd ); if (data->dvd) hb_dvd_stop( data->dvd ); return npreviews; } /* * This routine is called for every frame from a non-video elementary stream. * These are a mix of audio & subtitle streams, some of which we want & some * we're ignoring. This routine checks the frame against all our audio streams * to see if it's one we want and haven't identified yet. If yes, it passes the * frame to a codec-specific id routine which is responsible for filling in * the sample rate, bit rate, channels & other audio parameters. * * Since a sample rate is essential for further audio processing, any audio * stream which isn't successfully id'd by is deleted at the end of the scan. * This is necessary to avoid ambiguities where things that might be audio * aren't (e.g., some European DVD Teletext streams use the same IDs as US ATSC * AC-3 audio). */ static void LookForAudio( hb_title_t * title, hb_buffer_t * b ) { int i; hb_audio_t * audio = NULL; for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { audio = hb_list_item( title->list_audio, i ); /* check if this elementary stream is one we want */ if ( audio->id == b->s.id ) { break; } else { audio = NULL; } } if( !audio || audio->config.in.bitrate != 0 ) { /* not found or already done */ hb_buffer_close( &b ); return; } if ( audio->priv.scan_cache == NULL ) audio->priv.scan_cache = hb_fifo_init( 16, 16 ); if ( hb_fifo_size_bytes( audio->priv.scan_cache ) >= 16384 ) { hb_buffer_t * tmp; tmp = hb_fifo_get( audio->priv.scan_cache ); hb_buffer_close( &tmp ); } hb_fifo_push( audio->priv.scan_cache, b ); hb_work_object_t *w = hb_codec_decoder( audio->config.in.codec ); if ( w == NULL || w->bsinfo == NULL ) { hb_log( "Internal error in scan: unhandled audio type %d for id 0x%x", audio->config.in.codec, audio->id ); goto drop_audio; } hb_work_info_t info; w->title = title; w->audio = audio; w->codec_param = audio->config.in.codec_param; b = hb_fifo_see( audio->priv.scan_cache ); int ret = w->bsinfo( w, b, &info ); if ( ret < 0 ) { hb_log( "no info on audio type %d/0x%x for id 0x%x", audio->config.in.codec, audio->config.in.codec_param, audio->id ); goto drop_audio; } if ( !info.bitrate ) { /* didn't find any info */ free( w ); return; } hb_fifo_flush( audio->priv.scan_cache ); hb_fifo_close( &audio->priv.scan_cache ); audio->config.in.samplerate = info.rate; audio->config.in.samples_per_frame = info.samples_per_frame; audio->config.in.bitrate = info.bitrate; audio->config.in.matrix_encoding = info.matrix_encoding; audio->config.in.channel_layout = info.channel_layout; audio->config.in.channel_map = info.channel_map; audio->config.in.version = info.version; audio->config.in.flags = info.flags; audio->config.in.mode = info.mode; // now that we have all the info, set the audio description const char *codec_name = NULL; if (audio->config.in.codec & HB_ACODEC_FF_MASK) { AVCodec *codec = avcodec_find_decoder(audio->config.in.codec_param); if (codec != NULL) { if (info.profile != FF_PROFILE_UNKNOWN) { codec_name = av_get_profile_name(codec, info.profile); } if (codec_name == NULL) { // use our own capitalization for the most common codecs switch (audio->config.in.codec_param) { case AV_CODEC_ID_AAC: codec_name = "AAC"; break; case AV_CODEC_ID_AC3: codec_name = "AC3"; break; case AV_CODEC_ID_EAC3: codec_name = "E-AC3"; break; case AV_CODEC_ID_TRUEHD: codec_name = "TrueHD"; break; case AV_CODEC_ID_DTS: codec_name = audio->config.in.codec == HB_ACODEC_DCA_HD ? "DTS-HD" : "DTS"; break; case AV_CODEC_ID_FLAC: codec_name = "FLAC"; break; case AV_CODEC_ID_MP2: codec_name = "MPEG"; break; case AV_CODEC_ID_MP3: codec_name = "MP3"; break; case AV_CODEC_ID_PCM_BLURAY: codec_name = "BD LPCM"; break; case AV_CODEC_ID_OPUS: codec_name = "Opus"; break; case AV_CODEC_ID_VORBIS: codec_name = "Vorbis"; break; default: codec_name = codec->name; break; } } } else { switch (audio->config.in.codec) { case HB_ACODEC_DCA: codec_name = "DTS"; break; case HB_ACODEC_DCA_HD: codec_name = "DTS-HD"; break; case HB_ACODEC_FFAAC: codec_name = "AAC"; break; case HB_ACODEC_MP3: codec_name = "MP3"; break; default: codec_name = "Unknown (libav)"; break; } } } else { switch (audio->config.in.codec) { case HB_ACODEC_AC3: codec_name = "AC3"; break; case HB_ACODEC_LPCM: codec_name = "LPCM"; break; default: codec_name = "Unknown"; break; } } sprintf(audio->config.lang.description, "%s (%s)", audio->config.lang.simple, codec_name); switch (audio->config.lang.type) { case 2: strcat(audio->config.lang.description, " (Visually Impaired)"); break; case 3: strcat(audio->config.lang.description, " (Director's Commentary 1)"); break; case 4: strcat(audio->config.lang.description, " (Director's Commentary 2)"); break; default: break; } if (audio->config.in.channel_layout) { int lfes = (!!(audio->config.in.channel_layout & AV_CH_LOW_FREQUENCY) + !!(audio->config.in.channel_layout & AV_CH_LOW_FREQUENCY_2)); int channels = av_get_channel_layout_nb_channels(audio->config.in.channel_layout); char *desc = audio->config.lang.description + strlen(audio->config.lang.description); sprintf(desc, " (%d.%d ch)", channels - lfes, lfes); // describe the matrix encoding mode, if any switch (audio->config.in.matrix_encoding) { case AV_MATRIX_ENCODING_DOLBY: if (audio->config.in.codec == HB_ACODEC_AC3 || audio->config.in.codec_param == AV_CODEC_ID_AC3 || audio->config.in.codec_param == AV_CODEC_ID_EAC3 || audio->config.in.codec_param == AV_CODEC_ID_TRUEHD) { strcat(audio->config.lang.description, " (Dolby Surround)"); break; } strcat(audio->config.lang.description, " (Lt/Rt)"); break; case AV_MATRIX_ENCODING_DPLII: strcat(audio->config.lang.description, " (Dolby Pro Logic II)"); break; case AV_MATRIX_ENCODING_DPLIIX: strcat(audio->config.lang.description, " (Dolby Pro Logic IIx)"); break; case AV_MATRIX_ENCODING_DPLIIZ: strcat(audio->config.lang.description, " (Dolby Pro Logic IIz)"); break; case AV_MATRIX_ENCODING_DOLBYEX: strcat(audio->config.lang.description, " (Dolby Digital EX)"); break; case AV_MATRIX_ENCODING_DOLBYHEADPHONE: strcat(audio->config.lang.description, " (Dolby Headphone)"); break; default: break; } } hb_log( "scan: audio 0x%x: %s, rate=%dHz, bitrate=%d %s", audio->id, info.name, audio->config.in.samplerate, audio->config.in.bitrate, audio->config.lang.description ); free( w ); return; // We get here if there's no hope of finding info on an audio bitstream, // either because we don't have a decoder (or a decoder with a bitstream // info proc) or because the decoder's info proc said that the stream // wasn't something it could handle. Delete the item from the title's // audio list so we won't keep reading packets while trying to get its // bitstream info. drop_audio: if ( w ) free( w ); hb_fifo_flush( audio->priv.scan_cache ); hb_fifo_close( &audio->priv.scan_cache ); hb_list_rem( title->list_audio, audio ); return; } /* * This routine checks to see if we've ID'd all the audio streams associated * with a title. It returns 0 if there are more to ID & 1 if all are done. */ static int AllAudioOK( hb_title_t * title ) { int i; hb_audio_t * audio; for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { audio = hb_list_item( title->list_audio, i ); if( audio->config.in.bitrate == 0 ) { return 0; } } return 1; } static void UpdateState1(hb_scan_t *scan, int title) { hb_state_t state; #define p state.param.scanning /* Update the UI */ state.state = HB_STATE_SCANNING; p.title_cur = title; p.title_count = scan->dvd ? hb_dvd_title_count( scan->dvd ) : scan->bd ? hb_bd_title_count( scan->bd ) : scan->batch ? hb_batch_title_count( scan->batch ) : hb_list_count(scan->title_set->list_title); p.preview_cur = 0; p.preview_count = 1; p.progress = 0.5 * ((float)p.title_cur + ((float)p.preview_cur / p.preview_count)) / p.title_count; #undef p hb_set_state(scan->h, &state); } static void UpdateState2(hb_scan_t *scan, int title) { hb_state_t state; #define p state.param.scanning /* Update the UI */ state.state = HB_STATE_SCANNING; p.title_cur = title; p.title_count = hb_list_count( scan->title_set->list_title ); p.preview_cur = 1; p.preview_count = scan->preview_count; if (scan->title_index) p.progress = (float)(p.title_cur - 1) / p.title_count; else p.progress = 0.5 + 0.5 * (float)(p.title_cur - 1) / p.title_count; #undef p hb_set_state(scan->h, &state); } static void UpdateState3(hb_scan_t *scan, int preview) { hb_state_t state; hb_get_state2(scan->h, &state); #define p state.param.scanning p.preview_cur = preview; p.preview_count = scan->preview_count; if (scan->title_index) p.progress = ((float)p.title_cur - 1 + ((float)p.preview_cur / p.preview_count)) / p.title_count; else p.progress = 0.5 + 0.5 * ((float)p.title_cur - 1 + ((float)p.preview_cur / p.preview_count)) / p.title_count; #undef p hb_set_state(scan->h, &state); } HandBrake-0.10.2/libhb/nal_units.h0000664000175200017520000000361512463330511017267 0ustar handbrakehandbrake/* nal_units.h * * Copyright (c) 2003-2015 HandBrake Team * This file is part of the HandBrake source code. * Homepage: . * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef HB_NAL_UNITS_H #define HB_NAL_UNITS_H #include #include "common.h" /* * Write a NAL unit of the specified size to the provided * output buffer, using the requested output format. * Returns the amount (in bytes) of data written to the buffer. * * The provided NAL unit must start with the NAL unit header. * * Note: the buffer is assumed to be large enough to hold the NAL unit * as well as any additonal data the function may prepend/append to it. * * The caller may check the minimum required buffer size by passing a * NULL buffer to the fucntion and checking the returned size value. */ size_t hb_nal_unit_write_annexb(uint8_t *buf, const uint8_t *nal_unit, const size_t nal_unit_size); size_t hb_nal_unit_write_isomp4(uint8_t *buf, const uint8_t *nal_unit, const size_t nal_unit_size); /* * Search the provided data buffer for NAL units in Annex B format. * * Returns a pointer to the start (start code prefix excluded) of the * first NAL unit found, or NULL if no NAL units were found in the buffer. * * On input, size holds the length of the provided data buffer. * On output, size holds the length of the returned NAL unit. */ uint8_t* hb_annexb_find_next_nalu(const uint8_t *start, size_t *size); /* * Returns a newly-allocated buffer holding a copy of the provided * NAL unit bitstream data, converted to the requested format. */ hb_buffer_t* hb_nal_bitstream_annexb_to_mp4(const uint8_t *data, const size_t size); hb_buffer_t* hb_nal_bitstream_mp4_to_annexb(const uint8_t *data, const size_t size, const uint8_t nal_length_size); #endif // HB_NAL_UNITS_H HandBrake-0.10.2/libhb/eedi2.c0000664000175200017520000021215212463330511016254 0ustar handbrakehandbrake/* eedi2.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html The EEDI2 interpolator was created by tritical: http://web.missouri.edu/~kes25c/ */ #include "hb.h" #include "eedi2.h" /** * EEDI2 directional limit lookup table * * These values are used to limit the range of edge direction searches and filtering. */ const int eedi2_limlut[33] __attribute__ ((aligned (16))) = { 6, 6, 7, 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, -1, -1 }; /** * Analog of _aligned_malloc * @param size Size of memory being pointed to * @param align_size Size of memory chunks to align to (must be power of 2) */ void *eedi2_aligned_malloc( size_t size, size_t align_size ) { char * ptr, * ptr2, * aligned_ptr; int align_mask = align_size - 1; ptr = (char *)malloc( size + align_size + sizeof( int ) ); if( ptr==NULL ) return( NULL ); ptr2 = ptr + sizeof( int ); aligned_ptr = ptr2 + ( align_size - ( (size_t)ptr2 & align_mask ) ); ptr2 = aligned_ptr - sizeof( int ); *( (int *)ptr2 ) = (int)( aligned_ptr - ptr ); return( aligned_ptr ); } /** * Analog of _aligned_free * @param ptr The aligned pointer, created with eedi2_aligned_malloc, to be freed */ void eedi2_aligned_free( void *ptr ) { int * ptr2 = (int *)ptr - 1; ptr -= * ptr2; free(ptr); } /** * Sorts metrics for median filtering * @param order Pointer to the table of values to sort * @param length Length of the order array */ void eedi2_sort_metrics( int *order, const int length ) { int i; for( i = 1; i < length; ++i ) { int j = i; const int temp = order[j]; while( j > 0 && order[j-1] > temp ) { order[j] = order[j-1]; --j; } order[j] = temp; } } /** * Bitblits an image plane (overwrites one bitmap with another) * @param dtsp Pointer to destination bitmap * @param dst_pitch Stride of destination bitmap * @param srcp Pointer to source bitmap * @param src_pitch Stride of destination bitmap * @param row_size Width of the bitmap being copied * @param height Height of the source bitmap * * When row_size, dst_pitch, and src_pitch are equal, eedi2_bit_blit can work more quickly by copying the whole plane at once instead of individual lines. */ void eedi2_bit_blit( uint8_t * dstp, int dst_pitch, const uint8_t * srcp, int src_pitch, int row_size, int height ) { if( ( !height ) || ( !row_size ) ) return; if( height == 1 || ( dst_pitch == src_pitch && src_pitch == row_size ) ) { memcpy( dstp, srcp, row_size * height ); } else { int y; for( y = height; y > 0; --y ) { memcpy( dstp, srcp, row_size ); dstp += dst_pitch; srcp += src_pitch; } } } /** * A specialized variant of bit_blit, just for setting up the initial, field-sized bitmap planes that EEDI2 interpolates from. * @param src Pointer to source bitmap plane being copied from * @param dst Pointer to the destination bitmap plane being copied to * @param pitch Stride of both bitmaps * @param height Height of the original, full-size src plane being copied from */ void eedi2_fill_half_height_buffer_plane( uint8_t * src, uint8_t * dst, int pitch, int height ) { /* When TFF, we want to copy alternating lines starting at 0, the top field. When BFF, we want to start at line 1. */ int y; for( y = height; y > 0; y = y - 2 ) { memcpy( dst, src, pitch ); dst += pitch; src += pitch * 2; } } /** * A specialized variant of bit_blit, just for resizing the field-height maps EEDI2 generates to frame-height...a simple line doubler * @param srcp Pointer to source bitmap plane being copied from * @param dstp Pointer to the destination bitmap plane being copied to * @param height Height of the input, half-size src plane being copied from * @param pitch Stride of both bitmaps */ void eedi2_upscale_by_2( uint8_t * srcp, uint8_t * dstp, int height, int pitch ) { int y; for( y = height; y > 0; y-- ) { memcpy( dstp, srcp, pitch ); dstp += pitch; memcpy( dstp, srcp, pitch ); srcp += pitch; dstp += pitch; } } /** * Finds places where verticaly adjacent pixels abruptly change in intensity, i.e., sharp edges. * @param dstp Pointer to the destination bitmap * @param dst_pitch Stride of dstp * @param srcp Pointer to the source bitmap * @param src_pitch Stride of srcp * @param mtresh Magnitude threshold, ensures it doesn't mark edges on pixels that are too similar (10 is a good default value) * @param vthresh Variance threshold, ensures it doesn't look for edges in highly random pixel blocks (20 is a good default value) * @param lthresh Laplacian threshold, ensures edges are still prominent in the 2nd spatial derivative of the srcp plane (20 is a good default value) * @param height Height of half-height single-field frame * @param width Width of srcp bitmap rows, as opposed to the padded stride in src_pitch */ void eedi2_build_edge_mask( uint8_t * dstp, int dst_pitch, uint8_t *srcp, int src_pitch, int mthresh, int lthresh, int vthresh, int height, int width ) { int x, y; mthresh = mthresh * 10; vthresh = vthresh * 81; memset( dstp, 0, ( height / 2 ) * dst_pitch ); srcp += src_pitch; dstp += dst_pitch; unsigned char *srcpp = srcp-src_pitch; unsigned char *srcpn = srcp+src_pitch; for( y = 1; y < height - 1; ++y ) { for( x = 1; x < width-1; ++x ) { if( ( abs( srcpp[x] - srcp[x] ) < 10 && abs( srcp[x] - srcpn[x] ) < 10 && abs( srcpp[x] - srcpn[x] ) < 10 ) || ( abs( srcpp[x-1] - srcp[x-1] ) < 10 && abs( srcp[x-1] - srcpn[x-1] ) < 10 && abs( srcpp[x-1] - srcpn[x-1] ) < 10 && abs( srcpp[x+1] - srcp[x+1] ) < 10 && abs( srcp[x+1] - srcpn[x+1] ) < 10 && abs( srcpp[x+1] - srcpn[x+1] ) < 10) ) continue; const int sum = srcpp[x-1] + srcpp[x] + srcpp[x+1] + srcp[x-1] + srcp[x]+ srcp[x+1] + srcpn[x-1] + srcpn[x] + srcpn[x+1]; const int sumsq = srcpp[x-1] * srcpp[x-1] + srcpp[x] * srcpp[x] + srcpp[x+1] * srcpp[x+1] + srcp[x-1] * srcp[x-1] + srcp[x] * srcp[x] + srcp[x+1] * srcp[x+1] + srcpn[x-1] * srcpn[x-1] + srcpn[x] * srcpn[x] + srcpn[x+1] * srcpn[x+1]; if( 9 * sumsq-sum * sum < vthresh ) continue; const int Ix = srcp[x+1] - srcp[x-1]; const int Iy = MAX( MAX( abs( srcpp[x] - srcpn[x] ), abs( srcpp[x] - srcp[x] ) ), abs( srcp[x] - srcpn[x] ) ); if( Ix * Ix + Iy * Iy >= mthresh ) { dstp[x] = 255; continue; } const int Ixx = srcp[x-1] - 2 * srcp[x] + srcp[x+1]; const int Iyy = srcpp[x] - 2 * srcp[x] + srcpn[x]; if( abs( Ixx ) + abs( Iyy ) >= lthresh ) dstp[x] = 255; } dstp += dst_pitch; srcpp += src_pitch; srcp += src_pitch; srcpn += src_pitch; } } /** * Expands and smooths out the edge mask * @param mskp Pointer to the source edge mask being read from * @param msk_pitch Stride of mskp * @param dstp Pointer to the destination to store the dilated edge mask * @param dst_pitch Stride of dstp * @param dstr Dilation threshold, ensures a pixel is only retained as an edge in dstp if this number of adjacent pixels or greater are also edges in mskp (4 is a good default value) * @param height Height of half-height field-sized frame * @param width Width of mskp bitmap rows, as opposed to the pdded stride in msk_pitch */ void eedi2_dilate_edge_mask( uint8_t *mskp, int msk_pitch, uint8_t *dstp, int dst_pitch, int dstr, int height, int width ) { int x, y; eedi2_bit_blit( dstp, dst_pitch, mskp, msk_pitch, width, height ); mskp += msk_pitch; unsigned char *mskpp = mskp - msk_pitch; unsigned char *mskpn = mskp + msk_pitch; dstp += dst_pitch; for( y = 1; y < height - 1; ++y ) { for( x = 1; x < width - 1; ++x ) { if( mskp[x] != 0 ) continue; int count = 0; if( mskpp[x-1] == 0xFF ) ++count; if( mskpp[x] == 0xFF ) ++count; if( mskpp[x+1] == 0xFF ) ++count; if( mskp[x-1] == 0xFF ) ++count; if( mskp[x+1] == 0xFF ) ++count; if( mskpn[x-1] == 0xFF ) ++count; if( mskpn[x] == 0xFF ) ++count; if( mskpn[x+1] == 0xFF ) ++count; if( count >= dstr ) dstp[x] = 0xFF; } mskpp += msk_pitch; mskp += msk_pitch; mskpn += msk_pitch; dstp += dst_pitch; } } /** * Contracts the edge mask * @param mskp Pointer to the source edge mask being read from * @param msk_pitch Stride of mskp * @param dstp Pointer to the destination to store the eroded edge mask * @param dst_pitch Stride of dstp * @param estr Erosion threshold, ensures a pixel isn't retained as an edge in dstp if fewer than this number of adjacent pixels are also edges in mskp (2 is a good default value) * @param height Height of half-height field-sized frame * @param width Width of mskp bitmap rows, as opposed to the pdded stride in msk_pitch */ void eedi2_erode_edge_mask( uint8_t *mskp, int msk_pitch, uint8_t *dstp, int dst_pitch, int estr, int height, int width ) { int x, y; eedi2_bit_blit( dstp, dst_pitch, mskp, msk_pitch, width, height ); mskp += msk_pitch; unsigned char *mskpp = mskp - msk_pitch; unsigned char *mskpn = mskp + msk_pitch; dstp += dst_pitch; for ( y = 1; y < height - 1; ++y ) { for ( x = 1; x < width - 1; ++x ) { if( mskp[x] != 0xFF ) continue; int count = 0; if ( mskpp[x-1] == 0xFF ) ++count; if ( mskpp[x] == 0xFF ) ++count; if ( mskpp[x+1] == 0xFF ) ++count; if ( mskp[x-1] == 0xFF ) ++count; if ( mskp[x+1] == 0xFF ) ++count; if ( mskpn[x-1] == 0xFF ) ++count; if ( mskpn[x] == 0xFF ) ++count; if ( mskpn[x+1] == 0xFF ) ++count; if ( count < estr) dstp[x] = 0; } mskpp += msk_pitch; mskp += msk_pitch; mskpn += msk_pitch; dstp += dst_pitch; } } /** * Smooths out horizontally aligned holes in the mask * * If none of the 6 horizontally adjacent pixels are edges, mark the current pixel as not edged. * If at least 1 of the 3 on either side are edges, mark the current pixel as an edge. * * @param mskp Pointer to the source edge mask being read from * @param msk_pitch Stride of mskp * @param dstp Pointer to the destination to store the smoothed edge mask * @param dst_pitch Stride of dstp * @param height Height of half-height field-sized frame * @param width Width of mskp bitmap rows, as opposed to the pdded stride in msk_pitch */ void eedi2_remove_small_gaps( uint8_t * mskp, int msk_pitch, uint8_t * dstp, int dst_pitch, int height, int width ) { int x, y; eedi2_bit_blit( dstp, dst_pitch, mskp, msk_pitch, width, height ); mskp += msk_pitch; dstp += dst_pitch; for( y = 1; y < height - 1; ++y ) { for( x = 3; x < width - 3; ++x ) { if( mskp[x] ) { if( mskp[x-3] ) continue; if( mskp[x-2] ) continue; if( mskp[x-1] ) continue; if( mskp[x+1] ) continue; if( mskp[x+2] ) continue; if( mskp[x+3] ) continue; dstp[x] = 0; } else { if ( ( mskp[x+1] && ( mskp[x-1] || mskp[x-2] || mskp[x-3] ) ) || ( mskp[x+2] && ( mskp[x-1] || mskp[x-2] ) ) || ( mskp[x+3] && mskp[x-1] ) ) dstp[x] = 0xFF; } } mskp += msk_pitch; dstp += dst_pitch; } } /** * Calculates spatial direction vectors for the edges. This is EEDI2's timesink, and can be thought of as YADIF_CHECK on steroids, as both try to discern which angle a given edge follows * @param plane The plane of the image being processed, to know to reduce maxd for chroma planes (HandBrake only works with YUV420 video so it is assumed they are half-height) * @param mskp Pointer to the source edge mask being read from * @param msk_pitch Stride of mskp * @param srcp Pointer to the source image being filtered * @param src_pitch Stride of srcp * @param dstp Pointer to the destination to store the dilated edge mask * @param dst_pitch Stride of dstp * @param maxd Maximum pixel distance to search (24 is a good default value) * @param nt Noise threshold (50 is a good default value) * @param height Height of half-height field-sized frame * @param width Width of srcp bitmap rows, as opposed to the pdded stride in src_pitch */ void eedi2_calc_directions( const int plane, uint8_t * mskp, int msk_pitch, uint8_t * srcp, int src_pitch, uint8_t * dstp, int dst_pitch, int maxd, int nt, int height, int width ) { int x, y, u, i; memset( dstp, 255, dst_pitch * height ); mskp += msk_pitch; dstp += dst_pitch; srcp += src_pitch; unsigned char *src2p = srcp - src_pitch * 2; unsigned char *srcpp = srcp - src_pitch; unsigned char *srcpn = srcp + src_pitch; unsigned char *src2n = srcp + src_pitch * 2; unsigned char *mskpp = mskp - msk_pitch; unsigned char *mskpn = mskp + msk_pitch; const int maxdt = plane == 0 ? maxd : ( maxd >> 1 ); for( y = 1; y < height - 1; ++y ) { for( x = 1; x < width - 1; ++x ) { if( mskp[x] != 0xFF || ( mskp[x-1] != 0xFF && mskp[x+1] != 0xFF ) ) continue; const int startu = MAX( -x + 1, -maxdt ); const int stopu = MIN( width - 2 - x, maxdt ); int minb = MIN( 13 * nt, ( abs( srcp[x] - srcpn[x] ) + abs( srcp[x] - srcpp[x] ) ) * 6 ); int mina = MIN( 19 * nt, ( abs( srcp[x] - srcpn[x] ) + abs( srcp[x] - srcpp[x] ) ) * 9 ); int minc = mina; int mind = minb; int mine = minb; int dira = -5000, dirb = -5000, dirc = -5000, dird = -5000, dire = -5000; for( u = startu; u <= stopu; ++u ) { if( y == 1 || mskpp[x-1+u] == 0xFF || mskpp[x+u] == 0xFF || mskpp[x+1+u] == 0xFF ) { if( y == height - 2 || mskpn[x-1-u] == 0xFF || mskpn[x-u] == 0xFF || mskpn[x+1-u] == 0xFF ) { const int diffsn = abs( srcp[x-1] - srcpn[x-1-u] ) + abs( srcp[x] - srcpn[x-u] ) + abs( srcp[x+1] - srcpn[x+1-u] ); const int diffsp = abs( srcp[x-1] - srcpp[x-1+u] ) + abs( srcp[x] - srcpp[x+u] ) + abs( srcp[x+1] - srcpp[x+1+u] ); const int diffps = abs( srcpp[x-1] - srcp[x-1-u] ) + abs( srcpp[x] - srcp[x-u] ) + abs( srcpp[x+1] - srcp[x+1-u] ); const int diffns = abs( srcpn[x-1] - srcp[x-1+u] ) + abs( srcpn[x] - srcp[x+u] ) + abs( srcpn[x+1] - srcp[x+1+u] ); const int diff = diffsn + diffsp + diffps + diffns; int diffd = diffsp + diffns; int diffe = diffsn + diffps; if( diff < minb ) { dirb = u; minb = diff; } if( __builtin_expect( y > 1, 1) ) { const int diff2pp = abs( src2p[x-1] - srcpp[x-1-u] ) + abs( src2p[x] - srcpp[x-u] ) + abs( src2p[x+1] - srcpp[x+1-u] ); const int diffp2p = abs( srcpp[x-1] - src2p[x-1+u] ) + abs( srcpp[x] - src2p[x+u] ) + abs( srcpp[x+1] - src2p[x+1+u] ); const int diffa = diff + diff2pp + diffp2p; diffd += diffp2p; diffe += diff2pp; if( diffa < mina ) { dira = u; mina = diffa; } } if( __builtin_expect( y < height-2, 1) ) { const int diff2nn = abs( src2n[x-1] - srcpn[x-1+u] ) + abs( src2n[x] - srcpn[x+u] ) + abs( src2n[x+1] - srcpn[x+1+u] ); const int diffn2n = abs( srcpn[x-1] - src2n[x-1-u] ) + abs( srcpn[x] - src2n[x-u] ) + abs( srcpn[x+1] - src2n[x+1-u] ); const int diffc = diff + diff2nn + diffn2n; diffd += diff2nn; diffe += diffn2n; if( diffc < minc ) { dirc = u; minc = diffc; } } if( diffd < mind ) { dird = u; mind = diffd; } if( diffe < mine ) { dire = u; mine = diffe; } } } } int order[5], k=0; if( dira != -5000 ) order[k++] = dira; if( dirb != -5000 ) order[k++] = dirb; if( dirc != -5000 ) order[k++] = dirc; if( dird != -5000 ) order[k++] = dird; if( dire != -5000 ) order[k++] = dire; if( k > 1 ) { eedi2_sort_metrics( order, k ); const int mid = ( k & 1 ) ? order[k>>1] : ( order[(k-1)>>1] + order[k>>1] + 1 ) >> 1; const int tlim = MAX( eedi2_limlut[abs(mid)] >> 2, 2 ); int sum = 0, count = 0; for( i = 0; i < k; ++i ) { if( abs( order[i] - mid ) <= tlim ) { ++count; sum += order[i]; } } if( count > 1 ) dstp[x] = 128 + ( (int)( (float)sum / (float)count ) * 4 ); else dstp[x] = 128; } else dstp[x] = 128; } mskpp += msk_pitch; mskp += msk_pitch; mskpn += msk_pitch; src2p += src_pitch; srcpp += src_pitch; srcp += src_pitch; srcpn += src_pitch; src2n += src_pitch; dstp += dst_pitch; } } /** * Filters the edge mask * @param mskp Pointer to the source edge mask being read from * @param msk_pitch Stride of mskp * @param dmskp Pointer to the edge direction mask * @param dmsk_pitch Stride of dmskp * @param dstp Pointer to the destination to store the filtered edge mask * @param dst_pitch Stride of dstp * @param height Height of half-height field-sized frame * @param width Width of mskp bitmap rows, as opposed to the pdded stride in msk_pitch */ void eedi2_filter_map( uint8_t * mskp, int msk_pitch, uint8_t * dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, int height, int width ) { int x, y, j; eedi2_bit_blit( dstp, dst_pitch, dmskp, dmsk_pitch, width, height ); mskp += msk_pitch; dmskp += dmsk_pitch; dstp += dst_pitch; unsigned char *dmskpp = dmskp - dmsk_pitch; unsigned char *dmskpn = dmskp + dmsk_pitch; for( y = 1; y < height - 1; ++y ) { for( x = 1; x < width - 1; ++x ) { if( dmskp[x] == 0xFF || mskp[x] != 0xFF ) continue; const int dir = ( dmskp[x] - 128 ) >> 2; const int lim = MAX( abs( dir ) * 2, 12 ); int ict = 0, icb = 0; if( dir < 0 ) { const int dirt = MAX( -x, dir ); for( j = dirt; j <= 0; ++j ) { if( ( abs( dmskpp[x+j] - dmskp[x] ) > lim && dmskpp[x+j] != 0xFF ) || ( dmskp[x+j] == 0xFF && dmskpp[x+j] == 0xFF ) || ( abs( dmskp[x+j] - dmskp[x] ) > lim && dmskp[x+j] != 0xFF ) ) { ict = 1; break; } } } else { const int dirt = MIN( width - x - 1, dir ); for( j = 0; j <= dirt; ++j ) { if( ( abs( dmskpp[x+j] - dmskp[x] ) > lim && dmskpp[x+j] != 0xFF ) || ( dmskp[x+j] == 0xFF && dmskpp[x+j] == 0xFF ) || ( abs( dmskp[x+j] - dmskp[x] ) > lim && dmskp[x+j] != 0xFF ) ) { ict = 1; break; } } } if( ict ) { if( dir < 0 ) { const int dirt = MIN( width - x - 1, abs( dir ) ); for( j = 0; j <= dirt; ++j ) { if( ( abs( dmskpn[x+j] - dmskp[x] ) > lim && dmskpn[x+j] != 0xFF ) || ( dmskpn[x+j] == 0xFF && dmskp[x+j] == 0xFF ) || ( abs( dmskp[x+j] - dmskp[x] ) > lim && dmskp[x+j] != 0xFF ) ) { icb = 1; break; } } } else { const int dirt = MAX( -x, -dir ); for( j = dirt; j <= 0; ++j ) { if( ( abs( dmskpn[x+j] - dmskp[x] ) > lim && dmskpn[x+j] != 0xFF ) || ( dmskpn[x+j] == 0xFF && dmskp[x+j] == 0xFF ) || ( abs( dmskp[x+j] - dmskp[x] ) > lim && dmskp[x+j] != 0xFF ) ) { icb = 1; break; } } } if( icb ) dstp[x] = 255; } } mskp += msk_pitch; dmskpp += dmsk_pitch; dmskp += dmsk_pitch; dmskpn += dmsk_pitch; dstp += dst_pitch; } } /** * Filters the edge direction mask * @param mskp Pointer to the edge mask * @param msk_pitch Stride of mskp * @param dmskp Pointer to the edge direction mask being read from * @param dmsk_pitch Stride of dmskp * @param dstp Pointer to the destination to store the filtered edge direction mask * @param dst_pitch Stride of dstp * @param height Height of half_height field-sized frame * @param width Width of dmskp bitmap rows, as opposed to the pdded stride in dmsk_pitch */ void eedi2_filter_dir_map( uint8_t * mskp, int msk_pitch, uint8_t * dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, int height, int width ) { int x, y, i; eedi2_bit_blit( dstp, dst_pitch, dmskp, dmsk_pitch, width, height ); dmskp += dmsk_pitch; unsigned char *dmskpp = dmskp - dmsk_pitch; unsigned char *dmskpn = dmskp + dmsk_pitch; dstp += dst_pitch; mskp += msk_pitch; for( y = 1; y < height - 1; ++y ) { for( x = 1; x < width - 1; ++x ) { if( mskp[x] != 0xFF ) continue; int u = 0, order[9]; if( dmskpp[x-1] != 0xFF ) order[u++] = dmskpp[x-1]; if( dmskpp[x] != 0xFF ) order[u++] = dmskpp[x]; if( dmskpp[x+1] != 0xFF ) order[u++] = dmskpp[x+1]; if( dmskp[x-1] != 0xFF ) order[u++] = dmskp[x-1]; if( dmskp[x] != 0xFF ) order[u++] = dmskp[x]; if( dmskp[x+1] != 0xFF ) order[u++] = dmskp[x+1]; if( dmskpn[x-1] != 0xFF ) order[u++] = dmskpn[x-1]; if( dmskpn[x] != 0xFF ) order[u++] = dmskpn[x]; if( dmskpn[x+1] != 0xFF ) order[u++] = dmskpn[x+1]; if( u < 4 ) { dstp[x] = 255; continue; } eedi2_sort_metrics( order, u ); const int mid = ( u & 1 ) ? order[u>>1] : ( order[(u-1)>>1] + order[u>>1] + 1 ) >> 1; int sum = 0, count = 0; const int lim = eedi2_limlut[abs(mid-128)>>2]; for( i = 0; i < u; ++i ) { if( abs( order[i] - mid ) <= lim ) { ++count; sum += order[i]; } } if( count < 4 || ( count < 5 && dmskp[x] == 0xFF ) ) { dstp[x] = 255; continue; } dstp[x] = (int)( ( (float)( sum + mid ) / (float)( count + 1 ) ) + 0.5f ); } dmskpp += dmsk_pitch; dmskp += dmsk_pitch; dmskpn += dmsk_pitch; dstp += dst_pitch; mskp += msk_pitch; } } /** * Smoothes out the edge direction map * @param mskp Pointer to the edge mask * @param msk_pitch Stride of mskp * @param dmskp Pointer to the edge direction mask being read from * @param dmsk_pitch Stride of dmskp * @param dstp Pointer to the destination to store the expanded edge direction mask * @param dst_pitch Stride of dstp * @param height Height of half-height field-sized frame * @param width Width of dmskp bitmap rows, as opposed to the pdded stride in dmsk_pitch */ void eedi2_expand_dir_map( uint8_t * mskp, int msk_pitch, uint8_t * dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, int height, int width ) { int x, y, i; eedi2_bit_blit( dstp, dst_pitch, dmskp, dmsk_pitch, width, height ); dmskp += dmsk_pitch; unsigned char *dmskpp = dmskp - dmsk_pitch; unsigned char *dmskpn = dmskp + dmsk_pitch; dstp += dst_pitch; mskp += msk_pitch; for( y = 1; y < height - 1; ++y ) { for( x = 1; x < width - 1; ++x ) { if( dmskp[x] != 0xFF || mskp[x] != 0xFF ) continue; int u = 0, order[9]; if( dmskpp[x-1] != 0xFF ) order[u++] = dmskpp[x-1]; if( dmskpp[x] != 0xFF ) order[u++] = dmskpp[x]; if( dmskpp[x+1] != 0xFF ) order[u++] = dmskpp[x+1]; if( dmskp[x-1] != 0xFF ) order[u++] = dmskp[x-1]; if( dmskp[x+1] != 0xFF ) order[u++] = dmskp[x+1]; if( dmskpn[x-1] != 0xFF ) order[u++] = dmskpn[x-1]; if( dmskpn[x] != 0xFF ) order[u++] = dmskpn[x]; if( dmskpn[x+1] != 0xFF ) order[u++] = dmskpn[x+1]; if( u < 5 ) continue; eedi2_sort_metrics( order, u ); const int mid = ( u & 1 ) ? order[u>>1] : ( order[(u-1)>>1] + order[u>>1] + 1 ) >> 1; int sum = 0, count = 0; const int lim = eedi2_limlut[abs(mid-128)>>2]; for( i = 0; i < u; ++i ) { if( abs( order[i] - mid ) <= lim ) { ++count; sum += order[i]; } } if( count < 5 ) continue; dstp[x] = (int)( ( (float)( sum + mid ) / (float)( count + 1 ) ) + 0.5f ); } dmskpp += dmsk_pitch; dmskp += dmsk_pitch; dmskpn += dmsk_pitch; dstp += dst_pitch; mskp += msk_pitch; } } /** * Re-draws a clearer, less blocky frame-height edge direction mask * @param mskp Pointer to the edge mask * @param msk_pitch Stride of mskp * @param dmskp Pointer to the edge direction mask being read from * @param dmsk_pitch Stride of dmskp * @param dstp Pointer to the destination to store the redrawn direction mask * @param dst_pitch Stride of dstp * @param tff Whether or not the frame parity is Top Field First * @param height Height of the full-frame output * @param width Width of dmskp bitmap rows, as opposed to the pdded stride in dmsk_pitch */ void eedi2_mark_directions_2x( uint8_t * mskp, int msk_pitch, uint8_t * dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, int tff, int height, int width ) { int x, y, i; memset( dstp, 255, dst_pitch * height ); dstp += dst_pitch * ( 2 - tff ); dmskp += dmsk_pitch * ( 1 - tff ); mskp += msk_pitch * ( 1 - tff ); unsigned char *dmskpn = dmskp + dmsk_pitch * 2; unsigned char *mskpn = mskp + msk_pitch * 2; for( y = 2 - tff; y < height - 1; y += 2 ) { for( x = 1; x < width - 1; ++x ) { if( mskp[x] != 0xFF && mskpn[x] != 0xFF ) continue; int v = 0, order[6]; if( dmskp[x-1] != 0xFF ) order[v++] = dmskp[x-1]; if( dmskp[x] != 0xFF ) order[v++] = dmskp[x]; if( dmskp[x+1] != 0xFF ) order[v++] = dmskp[x+1]; if( dmskpn[x-1] != 0xFF ) order[v++] = dmskpn[x-1]; if( dmskpn[x] != 0xFF ) order[v++] = dmskpn[x]; if( dmskpn[x+1] != 0xFF ) order[v++] = dmskpn[x+1]; if( v < 3 ) continue; else { eedi2_sort_metrics( order, v ); const int mid = ( v & 1 ) ? order[v>>1] : ( order[(v-1)>>1] + order[v>>1]+1) >> 1; const int lim = eedi2_limlut[abs(mid-128)>>2]; int u = 0; if( abs( dmskp[x-1] - dmskpn[x-1] ) <= lim || dmskp[x-1] == 0xFF || dmskpn[x-1] == 0xFF ) ++u; if( abs( dmskp[x] - dmskpn[x] ) <= lim || dmskp[x] == 0xFF || dmskpn[x] == 0xFF ) ++u; if( abs( dmskp[x+1] - dmskpn[x-1] ) <= lim || dmskp[x+1] == 0xFF || dmskpn[x+1] == 0xFF) ++u; if( u < 2 ) continue; int count = 0, sum = 0; for( i = 0; i < v; ++i ) { if( abs( order[i] - mid ) <= lim ) { ++count; sum += order[i]; } } if( count < v - 2 || count < 2 ) continue; dstp[x] = (int)( ( (float)( sum + mid ) / (float)( count + 1 ) ) + 0.5f ); } } mskp += msk_pitch * 2; mskpn += msk_pitch * 2; dstp += dst_pitch * 2; dmskp += dmsk_pitch * 2; dmskpn += dmsk_pitch * 2; } } /** * Filters the frane-height edge direction mask * @param mskp Pointer to the edge mask * @param msk_pitch Stride of mskp * @param dmskp Pointer to the edge direction mask being read from * @param dmsk_pitch Stride of dmskp * @param dstp Pointer to the destination to store the filtered direction mask * @param dst_pitch Stride of dstp * @param field Field to filter * @param height Height of the full-frame output * @param width Width of dmskp bitmap rows, as opposed to the pdded stride in dmsk_pitch */ void eedi2_filter_dir_map_2x( uint8_t * mskp, int msk_pitch, uint8_t * dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, int field, int height, int width ) { int x, y, i; eedi2_bit_blit( dstp, dst_pitch, dmskp, dmsk_pitch, width, height ); dmskp += dmsk_pitch * ( 2 - field ); unsigned char *dmskpp = dmskp - dmsk_pitch * 2; unsigned char *dmskpn = dmskp + dmsk_pitch * 2; mskp += msk_pitch * ( 1 - field ); unsigned char *mskpn = mskp + msk_pitch * 2; dstp += dst_pitch * ( 2 - field ); for( y = 2 - field; y < height - 1; y += 2 ) { for( x = 1; x < width - 1; ++x ) { if( mskp[x] != 0xFF && mskpn[x] != 0xFF ) continue; int u = 0, order[9]; if( y > 1 ) { if( dmskpp[x-1] != 0xFF ) order[u++] = dmskpp[x-1]; if( dmskpp[x] != 0xFF ) order[u++] = dmskpp[x]; if( dmskpp[x+1] != 0xFF ) order[u++] = dmskpp[x+1]; } if( dmskp[x-1] != 0xFF ) order[u++] = dmskp[x-1]; if( dmskp[x] != 0xFF ) order[u++] = dmskp[x]; if( dmskp[x+1] != 0xFF ) order[u++] = dmskp[x+1]; if( y < height - 2 ) { if( dmskpn[x-1] != 0xFF ) order[u++] = dmskpn[x-1]; if( dmskpn[x] != 0xFF ) order[u++] = dmskpn[x]; if( dmskpn[x+1] != 0xFF ) order[u++] = dmskpn[x+1]; } if( u < 4 ) { dstp[x] = 255; continue; } eedi2_sort_metrics( order, u ); const int mid = ( u & 1 ) ? order[u>>1] : (order[(u-1)>>1] + order[u>>1] + 1 ) >> 1; int sum = 0, count = 0; const int lim = eedi2_limlut[abs(mid-128)>>2]; for( i = 0; i < u; ++i ) { if( abs( order[i] - mid ) <= lim ) { ++count; sum += order[i]; } } if( count < 4 || ( count < 5 && dmskp[x] == 0xFF ) ) { dstp[x] = 255; continue; } dstp[x] = (int)( ( (float)( sum + mid ) / (float)( count + 1 ) ) + 0.5f ); } mskp += msk_pitch * 2; mskpn += msk_pitch * 2; dmskpp += dmsk_pitch * 2; dmskp += dmsk_pitch * 2; dmskpn += dmsk_pitch * 2; dstp += dst_pitch * 2; } } /** * Smoothes out the frame-height edge direction mask * @param mskp Pointer to the edge mask * @param msk_pitch Stride of mskp * @param dmskp Pointer to the edge direction mask being read from * @param dmsk_pitch Stride of dmskp * @param dstp Pointer to the destination to store the expanded direction mask * @param dst_pitch Stride of dstp * @param field Field to filter * @param height Height of the full-frame output * @param width Width of dmskp bitmap rows, as opposed to the pdded stride in dmsk_pitch */ void eedi2_expand_dir_map_2x( uint8_t * mskp, int msk_pitch, uint8_t * dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, int field, int height, int width ) { int x, y, i; eedi2_bit_blit( dstp, dst_pitch, dmskp, dmsk_pitch, width, height ); dmskp += dmsk_pitch * ( 2 - field ); unsigned char *dmskpp = dmskp - dmsk_pitch * 2; unsigned char *dmskpn = dmskp + dmsk_pitch * 2; mskp += msk_pitch * ( 1 - field ); unsigned char *mskpn = mskp + msk_pitch * 2; dstp += dst_pitch * ( 2 - field ); for( y = 2 - field; y < height - 1; y += 2) { for( x = 1; x < width - 1; ++x ) { if( dmskp[x] != 0xFF || ( mskp[x] != 0xFF && mskpn[x] != 0xFF ) ) continue; int u = 0, order[9]; if( y > 1 ) { if( dmskpp[x-1] != 0xFF ) order[u++] = dmskpp[x-1]; if( dmskpp[x] != 0xFF ) order[u++] = dmskpp[x]; if( dmskpp[x+1] != 0xFF ) order[u++] = dmskpp[x+1]; } if( dmskp[x-1] != 0xFF ) order[u++] = dmskp[x-1]; if( dmskp[x+1] != 0xFF ) order[u++] = dmskp[x+1]; if( y < height - 2 ) { if( dmskpn[x-1] != 0xFF) order[u++] = dmskpn[x-1]; if( dmskpn[x] != 0xFF) order[u++] = dmskpn[x]; if( dmskpn[x+1] != 0xFF) order[u++] = dmskpn[x+1]; } if( u < 5 ) continue; eedi2_sort_metrics( order, u ); const int mid = ( u & 1 ) ? order[u>>1] : ( order[(u-1)>>1] + order[u>>1] + 1 ) >> 1; int sum = 0, count = 0; const int lim = eedi2_limlut[abs(mid-128)>>2]; for( i = 0; i < u; ++i ) { if( abs( order[i] - mid ) <= lim ) { ++count; sum += order[i]; } } if( count < 5 ) continue; dstp[x] = (int)( ( (float)( sum + mid ) / (float)( count + 1 ) ) + 0.5f ); } mskp += msk_pitch * 2; mskpn += msk_pitch * 2; dmskpp += dmsk_pitch * 2; dmskp += dmsk_pitch * 2; dmskpn += dmsk_pitch * 2; dstp += dst_pitch * 2; } } /** * Like the name suggests, this function fills in gaps in the frame-height edge direction mask * @param mskp Pointer to the edge mask * @param msk_pitch Stride of mskp * @param dmskp Pointer to the edge direction mask being read from * @param dmsk_pitch Stride of dmskp * @param dstp Pointer to the destination to store the filled-in direction mask * @param dst_pitch Stride of dstp * @param field Field to filter * @param height Height of the full-frame output * @param width Width of dmskp bitmap rows, as opposed to the pdded stride in dmsk_pitch */ void eedi2_fill_gaps_2x( uint8_t *mskp, int msk_pitch, uint8_t * dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, int field, int height, int width ) { int x, y, j; eedi2_bit_blit( dstp, dst_pitch, dmskp, dmsk_pitch, width, height ); dmskp += dmsk_pitch * ( 2 - field ); unsigned char *dmskpp = dmskp - dmsk_pitch * 2; unsigned char *dmskpn = dmskp + dmsk_pitch * 2; mskp += msk_pitch * ( 1 - field ); unsigned char *mskpp = mskp - msk_pitch * 2; unsigned char *mskpn = mskp + msk_pitch * 2; unsigned char *mskpnn = mskpn + msk_pitch * 2; dstp += dst_pitch * ( 2 - field ); for( y = 2 - field; y < height - 1; y += 2 ) { for( x = 1; x < width - 1; ++x ) { if( dmskp[x] != 0xFF || ( mskp[x] != 0xFF && mskpn[x] != 0xFF ) ) continue; int u = x - 1, back = 500, forward = -500; while( u ) { if( dmskp[u] != 0xFF ) { back = dmskp[u]; break; } if( mskp[u] != 0xFF && mskpn[u] != 0xFF ) break; --u; } int v = x + 1; while( v < width ) { if( dmskp[v] != 0xFF ) { forward = dmskp[v]; break; } if( mskp[v] != 0xFF && mskpn[v] != 0xFF ) break; ++v; } int tc = 1, bc = 1; int mint = 500, maxt = -20; int minb = 500, maxb = -20; for( j = u; j <= v; ++j ) { if( tc ) { if( y <= 2 || dmskpp[j] == 0xFF || ( mskpp[j] != 0xFF && mskp[j] != 0xFF ) ) { tc = 0; mint = maxt = 20; } else { if( dmskpp[j] < mint ) mint = dmskpp[j]; if( dmskpp[j] > maxt ) maxt = dmskpp[j]; } } if( bc ) { if( y >= height - 3 || dmskpn[j] == 0xFF || ( mskpn[j] != 0xFF && mskpnn[j] != 0xFF ) ) { bc = 0; minb = maxb = 20; } else { if( dmskpn[j] < minb ) minb = dmskpn[j]; if( dmskpn[j] > maxb ) maxb = dmskpn[j]; } } } if( maxt == -20 ) maxt = mint = 20; if( maxb == -20 ) maxb = minb = 20; int thresh = MAX( MAX( MAX( abs( forward - 128 ), abs( back - 128 ) ) >> 2, 8 ), MAX( abs( mint - maxt ), abs( minb - maxb ) ) ); const int flim = MIN( MAX( abs( forward - 128 ), abs( back - 128 ) ) >> 2, 6 ); if( abs( forward - back ) <= thresh && ( v - u - 1 <= flim || tc || bc ) ) { double step = (double)( forward - back ) / (double)( v - u ); for( j = 0; j < v - u - 1; ++j ) dstp[u+j+1] = back + (int)( j * step + 0.5 ); } } mskpp += msk_pitch * 2; mskp += msk_pitch * 2; mskpn += msk_pitch * 2; mskpnn += msk_pitch * 2; dmskpp += dmsk_pitch * 2; dmskp += dmsk_pitch * 2; dmskpn += dmsk_pitch * 2; dstp += dst_pitch * 2; } } /** * Actually renders the output frame, based on the edge and edge direction masks * @param plane The plane of the image being processed, to know to reduce a search distance for chroma planes (HandBrake only works with YUV420 video so it is assumed they are half-height) * @param dmskp Pointer to the edge direction mask being read from * @param dmsk_pitch Stride of dmskp * @param dstp Pointer to the line-doubled source field used being filtered in place * @param dst_pitch Stride of dstp * @param omskp Pointer to the destination to store the output edge mask used for post-processing * @param osmk_pitch Stride of omskp * @param field Field to filter * @nt Noise threshold, (50 is a good default value) * @param height Height of the full-frame output * @param width Width of dstp bitmap rows, as opposed to the pdded stride in dst_pitch */ void eedi2_interpolate_lattice( const int plane, uint8_t * dmskp, int dmsk_pitch, uint8_t * dstp, int dst_pitch, uint8_t * omskp, int omsk_pitch, int field, int nt, int height, int width ) { int x, y, u; if( field == 1 ) { eedi2_bit_blit( dstp + ( height - 1 ) * dst_pitch, dst_pitch, dstp + ( height - 2 ) * dst_pitch, dst_pitch, width, 1 ); } else { eedi2_bit_blit( dstp, dst_pitch, dstp + dst_pitch, dst_pitch, width, 1 ); } dstp += dst_pitch * ( 1 - field ); omskp += omsk_pitch * ( 1 - field ); unsigned char *dstpn = dstp + dst_pitch; unsigned char *dstpnn = dstp + dst_pitch * 2; unsigned char *omskn = omskp + omsk_pitch * 2; dmskp += dmsk_pitch * ( 2 - field ); for( y = 2 - field; y < height - 1; y += 2 ) { for( x = 0; x < width; ++x ) { int dir = dmskp[x]; const int lim = eedi2_limlut[abs(dir-128)>>2]; if( dir == 255 || ( abs( dmskp[x] - dmskp[x-1] ) > lim && abs( dmskp[x] - dmskp[x+1] ) > lim ) ) { dstpn[x] = ( dstp[x] + dstpnn[x] + 1 ) >> 1; if( dir != 255 ) dmskp[x] = 128; continue; } if( lim < 9 ) { const int sum = dstp[x-1] + dstp[x] + dstp[x+1] + dstpnn[x-1] + dstpnn[x] + dstpnn[x+1]; const int sumsq = dstp[x-1] * dstp[x-1] + dstp[x] * dstp[x] + dstp[x+1] * dstp[x+1] + dstpnn[x-1] * dstpnn[x-1] + dstpnn[x] * dstpnn[x] + dstpnn[x+1] * dstpnn[x+1]; if( 6 * sumsq - sum * sum < 576 ) { dstpn[x] = ( dstp[x] + dstpnn[x] + 1 ) >> 1; dmskp[x] = 255; continue; } } if( x > 1 && x < width - 2 && ( ( dstp[x] < MAX( dstp[x-2], dstp[x-1] ) - 3 && dstp[x] < MAX( dstp[x+2], dstp[x+1] ) - 3 && dstpnn[x] < MAX( dstpnn[x-2], dstpnn[x-1] ) - 3 && dstpnn[x] < MAX( dstpnn[x+2], dstpnn[x+1] ) - 3 ) || ( dstp[x] > MIN( dstp[x-2], dstp[x-1] ) + 3 && dstp[x] > MIN( dstp[x+2], dstp[x+1] ) + 3 && dstpnn[x] > MIN( dstpnn[x-2], dstpnn[x-1] ) + 3 && dstpnn[x] > MIN( dstpnn[x+2], dstpnn[x+1] ) + 3 ) ) ) { dstpn[x] = ( dstp[x] + dstpnn[x] + 1 ) >> 1; dmskp[x] = 128; continue; } dir = ( dir - 128 + 2 ) >> 2; int val = ( dstp[x] + dstpnn[x] + 1 ) >> 1; const int startu = ( dir - 2 < 0 ) ? MAX( -x + 1, MAX( dir - 2, -width + 2 + x ) ) : MIN( x - 1, MIN( dir - 2, width - 2 - x ) ); const int stopu = ( dir + 2 < 0 ) ? MAX( -x + 1, MAX( dir + 2, -width + 2 + x ) ) : MIN( x - 1, MIN( dir + 2, width - 2 - x ) ); int min = 8 * nt; for( u = startu; u <= stopu; ++u ) { const int diff = abs( dstp[x-1] - dstpnn[x-u-1] ) + abs( dstp[x] - dstpnn[x-u] ) + abs( dstp[x+1] - dstpnn[x-u+1] ) + abs( dstpnn[x-1] - dstp[x+u-1] ) + abs( dstpnn[x] - dstp[x+u] ) + abs( dstpnn[x+1] - dstp[x+u+1] ); if( diff < min && ( ( omskp[x-1+u] != 0xFF && abs( omskp[x-1+u] - dmskp[x] ) <= lim ) || ( omskp[x+u] != 0xFF && abs( omskp[x+u] - dmskp[x]) <= lim ) || ( omskp[x+1+u] != 0xFF && abs( omskp[x+1+u] - dmskp[x]) <= lim ) ) && ( ( omskn[x-1-u] != 0xFF && abs( omskn[x-1-u] - dmskp[x]) <= lim ) || ( omskn[x-u] != 0xFF && abs( omskn[x-u] - dmskp[x]) <= lim ) || ( omskn[x+1-u] != 0xFF && abs( omskn[x+1-u] - dmskp[x]) <= lim ) ) ) { const int diff2 = abs( dstp[x+(u>>1)-1] - dstpnn[x-(u>>1)-1] ) + abs( dstp[x+(u>>1)] - dstpnn[x-(u>>1)] ) + abs( dstp[x+(u>>1)+1] - dstpnn[x-(u>>1)+1] ); if( diff2 < 4 * nt && ( ( ( abs( omskp[x+(u>>1)] - omskn[x-(u>>1)] ) <= lim || abs( omskp[x+(u>>1)] - omskn[x-((u+1)>>1)] ) <= lim ) && omskp[x+(u>>1)] != 0xFF ) || ( ( abs( omskp[x+((u+1)>>1)] - omskn[x-(u>>1)] ) <= lim || abs( omskp[x+((u+1)>>1)] - omskn[x-((u+1)>>1)] ) <= lim ) && omskp[x+((u+1)>>1)] != 0xFF ) ) ) { if( ( abs( dmskp[x] - omskp[x+(u>>1)] ) <= lim || abs( dmskp[x] - omskp[x+((u+1)>>1)] ) <= lim ) && ( abs( dmskp[x] - omskn[x-(u>>1)] ) <= lim || abs( dmskp[x] - omskn[x-((u+1)>>1)] ) <= lim ) ) { val = ( dstp[x+(u>>1)] + dstp[x+((u+1)>>1)] + dstpnn[x-(u>>1)] + dstpnn[x-((u+1)>>1)] + 2 ) >> 2; min = diff; dir = u; } } } } if( min != 8 * nt ) { dstpn[x] = val; dmskp[x] = 128 + dir * 4; } else { const int minm = MIN( dstp[x], dstpnn[x] ); const int maxm = MAX( dstp[x], dstpnn[x] ); const int d = plane == 0 ? 4 : 2; const int startu = MAX( -x + 1, -d ); const int stopu = MIN( width - 2 - x, d ); min = 7 * nt; for( u = startu; u <= stopu; ++u ) { const int p1 = dstp[x+(u>>1)] + dstp[x+((u+1)>>1)]; const int p2 = dstpnn[x-(u>>1)] + dstpnn[x-((u+1)>>1)]; const int diff = abs( dstp[x-1] - dstpnn[x-u-1] ) + abs( dstp[x] - dstpnn[x-u] ) + abs( dstp[x+1] - dstpnn[x-u+1] ) + abs( dstpnn[x-1] - dstp[x+u-1] ) + abs( dstpnn[x] - dstp[x+u] ) + abs( dstpnn[x+1] - dstp[x+u+1] ) + abs( p1 - p2 ); if( diff < min ) { const int valt = ( p1 + p2 + 2 ) >> 2; if( valt >= minm && valt <= maxm ) { val = valt; min = diff; dir = u; } } } dstpn[x] = val; if( min == 7*nt ) dmskp[x] = 128; else dmskp[x] = 128 + dir * 4; } } dstp += dst_pitch * 2; dstpn += dst_pitch * 2; dstpnn += dst_pitch * 2; dmskp += dmsk_pitch * 2; omskp += omsk_pitch * 2; omskn += omsk_pitch * 2; } } /** * Applies some extra filtering to smooth the edge direction mask * @param nmskp Pointer to the newly-filtered edge direction mask being read from * @param nmsk_pitch Stride of nmskp * @param omskp Pointer to the old unfiltered edge direction mask being read from * @param omsk_pitch Stride of osmkp * @param dstp Pointer to the output image being filtered in place * @param src_pitch Stride of dstp ....not sure why it's named this * @param field Field to filter * @param height Height of the full-frame output * @param width Width of dstp bitmap rows, as opposed to the pdded stride in src_pitch */ void eedi2_post_process( uint8_t * nmskp, int nmsk_pitch, uint8_t * omskp, int omsk_pitch, uint8_t * dstp, int src_pitch, int field, int height, int width ) { int x, y; nmskp += ( 2 - field ) * nmsk_pitch; omskp += ( 2 - field ) * omsk_pitch; dstp += ( 2 - field ) * src_pitch; unsigned char *srcpp = dstp - src_pitch; unsigned char *srcpn = dstp + src_pitch; for( y = 2 - field; y < height - 1; y += 2 ) { for( x = 0; x < width; ++x ) { const int lim = eedi2_limlut[abs(nmskp[x]-128)>>2]; if( abs( nmskp[x] - omskp[x] ) > lim && omskp[x] != 255 && omskp[x] != 128 ) dstp[x] = ( srcpp[x] + srcpn[x] + 1 ) >> 1; } nmskp += nmsk_pitch * 2; omskp += omsk_pitch * 2; srcpp += src_pitch * 2; dstp += src_pitch * 2; srcpn += src_pitch * 2; } } /** * Blurs the source field plane * @param src Pointer to the half-height source field plane * @param src_pitch Stride of src * @param tmp Pointer to a temporary buffer for juggling bitmaps * @param tmp_pitch Stride of tmp * @param dst Pointer to the destination to store the blurred field plane * @param dst_pitch Stride of dst * @param height Height of the hakf-height field-sized frame * @param width Width of dstp bitmap rows, as opposed to the padded stride in dst_pitch */ void eedi2_gaussian_blur1( uint8_t * src, int src_pitch, uint8_t * tmp, int tmp_pitch, uint8_t * dst, int dst_pitch, int height, int width ) { uint8_t * srcp = src; uint8_t * dstp = tmp; int x, y; for( y = 0; y < height; ++y ) { dstp[0] = ( srcp[3] * 582 + srcp[2] * 7078 + srcp[1] * 31724 + srcp[0] * 26152 + 32768 ) >> 16; dstp[1] = ( srcp[4] * 582 + srcp[3] * 7078 + ( srcp[0] + srcp[2] ) * 15862 + srcp[1] * 26152 + 32768 ) >> 16; dstp[2] = ( srcp[5] * 582 + ( srcp[0] + srcp[4] ) * 3539 + ( srcp[1] + srcp[3] ) * 15862 + srcp[2]*26152 + 32768 ) >> 16; for( x = 3; x < width - 3; ++x ) { dstp[x] = ( ( srcp[x-3] + srcp[x+3] ) * 291 + ( srcp[x-2] + srcp[x+2] ) * 3539 + ( srcp[x-1] + srcp[x+1] ) * 15862 + srcp[x] * 26152 + 32768 ) >> 16; } dstp[x] = ( srcp[x-3] * 582 + ( srcp[x-2] + srcp[x+2] ) * 3539 + ( srcp[x-1] + srcp[x+1] ) * 15862 + srcp[x] * 26152 + 32768 ) >> 16; ++x; dstp[x] = ( srcp[x-3] * 582 + srcp[x-2] * 7078 + ( srcp[x-1] + srcp[x+1] ) * 15862 + srcp[x] * 26152 + 32768 ) >> 16; ++x; dstp[x] = ( srcp[x-3] * 582 + srcp[x-2] * 7078 + srcp[x-1] * 31724 + srcp[x] * 26152 + 32768 ) >> 16; srcp += src_pitch; dstp += tmp_pitch; } srcp = tmp; dstp = dst; unsigned char *src3p = srcp - tmp_pitch * 3; unsigned char *src2p = srcp - tmp_pitch * 2; unsigned char *srcpp = srcp - tmp_pitch; unsigned char *srcpn = srcp + tmp_pitch; unsigned char *src2n = srcp + tmp_pitch * 2; unsigned char *src3n = srcp + tmp_pitch * 3; for( x = 0; x < width; ++x ) { dstp[x] = ( src3n[x] * 582 + src2n[x] * 7078 + srcpn[x] * 31724 + srcp[x] * 26152 + 32768 ) >> 16; } src3p += tmp_pitch; src2p += tmp_pitch; srcpp += tmp_pitch; srcp += tmp_pitch; srcpn += tmp_pitch; src2n += tmp_pitch; src3n += tmp_pitch; dstp += dst_pitch; for( x = 0; x < width; ++x ) { dstp[x] = ( src3n[x] * 582 + src2n[x] * 7078 + ( srcpp[x] + srcpn[x] ) * 15862 + srcp[x] * 26152 + 32768 ) >> 16; } src3p += tmp_pitch; src2p += tmp_pitch; srcpp += tmp_pitch; srcp += tmp_pitch; srcpn += tmp_pitch; src2n += tmp_pitch; src3n += tmp_pitch; dstp += dst_pitch; for( x = 0; x < width; ++x ) { dstp[x] = ( src3n[x] * 582 + ( src2p[x] + src2n[x] ) * 3539 + ( srcpp[x] + srcpn[x] ) * 15862 + srcp[x] * 26152 + 32768 ) >> 16; } src3p += src_pitch; src2p += src_pitch; srcpp += src_pitch; srcp += src_pitch; srcpn += src_pitch; src2n += src_pitch; src3n += src_pitch; dstp += dst_pitch; for( y = 3; y < height - 3; ++y ) { for( x = 0; x < width; ++x ) { dstp[x] = ( ( src3p[x] + src3n[x] ) * 291 + ( src2p[x] + src2n[x] ) * 3539 + ( srcpp[x] + srcpn[x] ) * 15862 + srcp[x] * 26152 + 32768 ) >> 16; } src3p += tmp_pitch; src2p += tmp_pitch; srcpp += tmp_pitch; srcp += tmp_pitch; srcpn += tmp_pitch; src2n += tmp_pitch; src3n += tmp_pitch; dstp += dst_pitch; } for( x = 0; x < width; ++x ) { dstp[x] = ( src3p[x] * 582 + ( src2p[x] + src2n[x] ) *3539 + ( srcpp[x] + srcpn[x] ) * 15862 + srcp[x] * 26152 + 32768 ) >> 16; } src3p += tmp_pitch; src2p += tmp_pitch; srcpp += tmp_pitch; srcp += tmp_pitch; srcpn += tmp_pitch; src2n += tmp_pitch; src3n += tmp_pitch; dstp += dst_pitch; for( x = 0; x < width; ++x ) { dstp[x] = ( src3p[x] * 582 + src2p[x] * 7078 + ( srcpp[x] + srcpn[x] ) * 15862 + srcp[x] * 26152 + 32768 ) >> 16; } src3p += tmp_pitch; src2p += tmp_pitch; srcpp += tmp_pitch; srcp += tmp_pitch; srcpn += tmp_pitch; src2n += tmp_pitch; src3n += tmp_pitch; dstp += dst_pitch; for( x = 0; x < width; ++x ) { dstp[x] = ( src3p[x] * 582 + src2p[x] * 7078 + srcpp[x] * 31724 + srcp[x] * 26152 + 32768 ) >> 16; } } /** * Blurs the spatial derivatives of the source field plane * @param src Pointer to the derivative array to filter * @param tmp Pointer to a temporary storage for the derivative array while it's being filtered * @param dst Pointer to the destination to store the filtered output derivative array * @param pitch Stride of the bitmap from which the src array is derived * @param height Height of the half-height field-sized frame from which the src array derivs were taken * @param width Width of the bitmap from which the src array is derived, as opposed to the padded stride in pitch */ void eedi2_gaussian_blur_sqrt2( int *src, int *tmp, int *dst, const int pitch, int height, const int width ) { int * srcp = src; int * dstp = tmp; int x, y; for( y = 0; y < height; ++y ) { x = 0; dstp[x] = ( srcp[x+4] * 678 + srcp[x+3] * 3902 + srcp[x+2] * 13618 + srcp[x+1] * 28830 + srcp[x] * 18508 + 32768 ) >> 16; ++x; dstp[x] = ( srcp[x+4] * 678 + srcp[x+3] * 3902 + srcp[x+2] * 13618 + ( srcp[x-1] + srcp[x+1] ) *14415 + srcp[x] * 18508 + 32768 ) >> 16; ++x; dstp[x] = ( srcp[x+4] * 678 + srcp[x+3] * 3902 + ( srcp[x-2] + srcp[x+2] ) * 6809 + ( srcp[x-1] + srcp[x+1] ) * 14415 + srcp[x] * 18508 + 32768 ) >> 16; ++x; dstp[x] = ( srcp[x+4] * 678 + ( srcp[x-3] + srcp[x+3] ) * 1951 + ( srcp[x-2] + srcp[x+2] ) * 6809 + ( srcp[x-1] + srcp[x+1] ) * 14415 + srcp[x] * 18508 + 32768 ) >> 16; for( x = 4; x < width - 4; ++x ) { dstp[x] = ( ( srcp[x-4] + srcp[x+4] ) * 339 + ( srcp[x-3] + srcp[x+3] ) * 1951 + ( srcp[x-2] + srcp[x+2] ) * 6809 + ( srcp[x-1] + srcp[x+1] ) * 14415 + srcp[x] * 18508 + 32768 ) >> 16; } dstp[x] = ( srcp[x-4] * 678 + ( srcp[x-3] + srcp[x+3] ) * 1951 + ( srcp[x-2] + srcp[x+2] ) * 6809 + ( srcp[x-1] + srcp[x+1] ) * 14415 + srcp[x] * 18508 + 32768 ) >> 16; ++x; dstp[x] = ( srcp[x-4] * 678 + srcp[x-3] * 3902 + ( srcp[x-2] + srcp[x+2] ) * 6809 + ( srcp[x-1] + srcp[x+1] ) * 14415 + srcp[x] * 18508 + 32768 ) >> 16; ++x; dstp[x] = ( srcp[x-4] * 678 + srcp[x+3] * 3902 + srcp[x-2] * 13618 + ( srcp[x-1] + srcp[x+1] ) * 14415 + srcp[x] * 18508 + 32768 ) >> 16; ++x; dstp[x] = ( srcp[x-4] * 678 + srcp[x-3] * 3902 + srcp[x-2] * 13618 + srcp[x-1] * 28830 + srcp[x] * 18508 + 32768 ) >> 16; srcp += pitch; dstp += pitch; } dstp = dst; srcp = tmp; int * src4p = srcp - pitch * 4; int * src3p = srcp - pitch * 3; int * src2p = srcp - pitch * 2; int * srcpp = srcp - pitch; int * srcpn = srcp + pitch; int * src2n = srcp + pitch * 2; int * src3n = srcp + pitch * 3; int * src4n = srcp + pitch * 4; for( x = 0; x < width; ++x ) { dstp[x] = ( src4n[x] * 678 + src3n[x] * 3902 + src2n[x] * 13618 + srcpn[x] * 28830 + srcp[x] * 18508 + 32768 ) >> 18; } src4p += pitch; src3p += pitch; src2p += pitch; srcpp += pitch; srcp += pitch; srcpn += pitch; src2n += pitch; src3n += pitch; src4n += pitch; dstp += pitch; for( x = 0; x < width; ++x ) { dstp[x] = ( src4n[x] * 678 + src3n[x] * 3902 + src2n[x] * 13618 + ( srcpp[x] + srcpn[x] ) * 14415 + srcp[x] * 18508 + 32768 ) >> 18; } src4p += pitch; src3p += pitch; src2p += pitch; srcpp += pitch; srcp += pitch; srcpn += pitch; src2n += pitch; src3n += pitch; src4n += pitch; dstp += pitch; for( x = 0; x < width; ++x ) { dstp[x] = ( src4n[x] * 678 + src3n[x] * 3902 + ( src2p[x] + src2n[x] ) * 6809 + ( srcpp[x] + srcpn[x] ) * 14415 + srcp[x] * 18508 + 32768 ) >> 18; } src4p += pitch; src3p += pitch; src2p += pitch; srcpp += pitch; srcp += pitch; srcpn += pitch; src2n += pitch; src3n += pitch; src4n += pitch; dstp += pitch; for( x = 0; x < width; ++x ) { dstp[x] = ( src4n[x] * 678 + ( src3p[x] + src3n[x] ) * 1951 + ( src2p[x] + src2n[x] ) * 6809 + ( srcpp[x] + srcpn[x] ) * 14415 + srcp[x] * 18508 + 32768 ) >> 18; } src4p += pitch; src3p += pitch; src2p += pitch; srcpp += pitch; srcp += pitch; srcpn += pitch; src2n += pitch; src3n += pitch; src4n += pitch; dstp += pitch; for( y = 4; y < height - 4; ++y ) { for( x = 0; x < width; ++x ) { dstp[x] = ( ( src4p[x] + src4n[x] ) * 339 + ( src3p[x] + src3n[x] ) * 1951 + ( src2p[x] + src2n[x] ) * 6809 + ( srcpp[x] + srcpn[x] ) * 14415 + srcp[x] * 18508 + 32768 ) >> 18; } src4p += pitch; src3p += pitch; src2p += pitch; srcpp += pitch; srcp += pitch; srcpn += pitch; src2n += pitch; src3n += pitch; src4n += pitch; dstp += pitch; } for( x = 0; x < width; ++x ) { dstp[x] = ( src4p[x] * 678 + ( src3p[x] + src3n[x] ) * 1951 + ( src2p[x] + src2n[x] ) * 6809 + ( srcpp[x] + srcpn[x] ) * 14415 + srcp[x] * 18508 + 32768 ) >> 18; } src4p += pitch; src3p += pitch; src2p += pitch; srcpp += pitch; srcp += pitch; srcpn += pitch; src2n += pitch; src3n += pitch; src4n += pitch; dstp += pitch; for( x = 0; x < width; ++x ) { dstp[x] = ( src4p[x] * 678 + src3p[x] * 3902 + ( src2p[x] + src2n[x] ) * 6809 + ( srcpp[x] + srcpn[x] ) * 14415 + srcp[x] * 18508 + 32768 ) >> 18; } src4p += pitch; src3p += pitch; src2p += pitch; srcpp += pitch; srcp += pitch; srcpn += pitch; src2n += pitch; src3n += pitch; src4n += pitch; dstp += pitch; for( x = 0; x < width; ++x ) { dstp[x] = ( src4p[x] * 678 + src3p[x] * 3902 + src2p[x] * 13618 + ( srcpp[x] + srcpn[x] ) * 14415 + srcp[x] * 18508 + 32768 ) >> 18; } src4p += pitch; src3p += pitch; src2p += pitch; srcpp += pitch; srcp += pitch; srcpn += pitch; src2n += pitch; src3n += pitch; src4n += pitch; dstp += pitch; for( x = 0; x < width; ++x ) { dstp[x] = ( src4p[x] * 678 + src3p[x] * 3902 + src2p[x] * 13618 + srcpp[x] * 28830 + srcp[x] * 18508 + 32768 ) >> 18; } } /** * Finds spatial derivatives for a a source field plane * @param srcp Pointer to the plane to derive * @param src_pitch Stride of srcp * @param height Height of the half-height field-sized frame * @param width Width of srcp bitmap rows, as opposed to the padded stride in src_pitch * @param x2 Pointed to the array to store the x/x derivatives * @param y2 Pointer to the array to store the y/y derivatives * @param xy Pointer to the array to store the x/y derivatives */ void eedi2_calc_derivatives( uint8_t *srcp, int src_pitch, int height, int width, int *x2, int *y2, int *xy) { unsigned char * srcpp = srcp - src_pitch; unsigned char * srcpn = srcp + src_pitch; int x, y; { const int Ix = srcp[1] - srcp[0]; const int Iy = srcp[0] - srcpn[0]; x2[0] = ( Ix * Ix ) >> 1; y2[0] = ( Iy * Iy ) >> 1; xy[0] = ( Ix * Iy ) >> 1; } for( x = 1; x < width - 1; ++x ) { const int Ix = srcp[x+1] - srcp[x-1]; const int Iy = srcp[x] - srcpn[x]; x2[x] = ( Ix * Ix ) >> 1; y2[x] = ( Iy * Iy ) >> 1; xy[x] = ( Ix * Iy ) >> 1; } { const int Ix = srcp[x] - srcp[x-1]; const int Iy = srcp[x] - srcpn[x]; x2[x] = ( Ix * Ix ) >> 1; y2[x] = ( Iy * Iy ) >> 1; xy[x] = ( Ix * Iy ) >> 1; } srcpp += src_pitch; srcp += src_pitch; srcpn += src_pitch; x2 += src_pitch; y2 += src_pitch; xy += src_pitch; for( y = 1; y < height - 1; ++y ) { { const int Ix = srcp[1] - srcp[0]; const int Iy = srcpp[0] - srcpn[0]; x2[0] = ( Ix * Ix ) >> 1; y2[0] = ( Iy * Iy ) >> 1; xy[0] = ( Ix * Iy ) >> 1; } for ( x = 1; x < width - 1; ++x ) { const int Ix = srcp[x+1] - srcp[x-1]; const int Iy = srcpp[x] - srcpn[x]; x2[x] = ( Ix * Ix ) >> 1; y2[x] = ( Iy * Iy ) >> 1; xy[x] = ( Ix * Iy ) >> 1; } { const int Ix = srcp[x] - srcp[x-1]; const int Iy = srcpp[x] - srcpn[x]; x2[x] = ( Ix *Ix ) >> 1; y2[x] = ( Iy *Iy ) >> 1; xy[x] = ( Ix *Iy ) >> 1; } srcpp += src_pitch; srcp += src_pitch; srcpn += src_pitch; x2 += src_pitch; y2 += src_pitch; xy += src_pitch; } { const int Ix = srcp[1] - srcp[0]; const int Iy = srcpp[0] - srcp[0]; x2[0] = ( Ix * Ix ) >> 1; y2[0] = ( Iy * Iy ) >> 1; xy[0] = ( Ix * Iy ) >> 1; } for( x = 1; x < width - 1; ++x ) { const int Ix = srcp[x+1] - srcp[x-1]; const int Iy = srcpp[x] - srcp[x]; x2[x] = ( Ix * Ix ) >> 1; y2[x] = ( Iy * Iy ) >> 1; xy[x] = ( Ix * Iy ) >> 1; } { const int Ix = srcp[x] - srcp[x-1]; const int Iy = srcpp[x] - srcp[x]; x2[x] = ( Ix * Ix ) >> 1; y2[x] = ( Iy * Iy ) >> 1; xy[x] = ( Ix * Iy ) >> 1; } } /** * Filters junctions and corners for the output image * @param x2 Pointer to the x/x derivatives * @param y2 Pointer to the y/y derivatives * @param xy Pointer to the x/y derivatives * @param pitch Stride of the source field plane from which the derivatives were calculated * @param mskp Pointer to the edge direction mask * @param msk_pitch Stride of mskp * @param dstp Pointer to the output image being filtered in place * @param dst_pitch Stride of dstp * @param height Height of the full-frame output plane * @param width Width of dstp bitmap rows, as opposed to the padded stride in dst_pitch * @param field Field to filter */ void eedi2_post_process_corner( int *x2, int *y2, int *xy, const int pitch, uint8_t * mskp, int msk_pitch, uint8_t * dstp, int dst_pitch, int height, int width, int field ) { mskp += ( 8 - field ) * msk_pitch; dstp += ( 8 - field ) * dst_pitch; unsigned char * dstpp = dstp - dst_pitch; unsigned char * dstpn = dstp + dst_pitch; x2 += pitch * 3; y2 += pitch * 3; xy += pitch * 3; int *x2n = x2 + pitch; int *y2n = y2 + pitch; int *xyn = xy + pitch; int x, y; for( y = 8 - field; y < height - 7; y += 2 ) { for( x = 4; x < width - 4; ++x ) { if( mskp[x] == 255 || mskp[x] == 128 ) continue; const int c1 = (int)( x2[x] * y2[x] - xy[x] * xy[x] - 0.09 * ( x2[x] + y2[x] ) * ( x2[x] + y2[x] ) ); const int c2 = (int)( x2n[x] * y2n[x] - xyn[x]* xyn[x] - 0.09 * ( x2n[x] + y2n[x] ) * ( x2n[x] + y2n[x] ) ); if (c1 > 775 || c2 > 775) dstp[x] = ( dstpp[x] + dstpn[x] + 1 ) >> 1; } mskp += msk_pitch * 2; dstpp += dst_pitch * 2; dstp += dst_pitch * 2; dstpn += dst_pitch * 2; x2 += pitch; x2n += pitch; y2 += pitch; y2n += pitch; xy += pitch; xyn += pitch; } } HandBrake-0.10.2/libhb/dvd.h0000664000175200017520000000463412463330511016052 0ustar handbrakehandbrake/* dvd.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef HB_DVD_H #define HB_DVD_H #include "dvdnav/dvdnav.h" #include "dvdread/ifo_read.h" #include "dvdread/nav_read.h" struct hb_dvdread_s { char * path; dvd_reader_t * reader; ifo_handle_t * vmg; int vts; int ttn; ifo_handle_t * ifo; dvd_file_t * file; pgc_t * pgc; int cell_start; int cell_end; int title_start; int title_end; int title_block_count; int cell_cur; int cell_next; int cell_overlap; int block; int pack_len; int next_vobu; int in_cell; int in_sync; uint16_t cur_vob_id; uint8_t cur_cell_id; }; struct hb_dvdnav_s { char * path; dvdnav_t * dvdnav; dvd_reader_t * reader; ifo_handle_t * vmg; int title; int title_block_count; int chapter; int cell; hb_list_t * list_chapter; int stopped; }; typedef struct hb_dvdnav_s hb_dvdnav_t; typedef struct hb_dvdread_s hb_dvdread_t; union hb_dvd_s { hb_dvdread_t dvdread; hb_dvdnav_t dvdnav; }; struct hb_dvd_func_s { hb_dvd_t * (* init) ( char * ); void (* close) ( hb_dvd_t ** ); char * (* name) ( char * ); int (* title_count) ( hb_dvd_t * ); hb_title_t * (* title_scan) ( hb_dvd_t *, int, uint64_t ); int (* start) ( hb_dvd_t *, hb_title_t *, int ); void (* stop) ( hb_dvd_t * ); int (* seek) ( hb_dvd_t *, float ); hb_buffer_t * (* read) ( hb_dvd_t * ); int (* chapter) ( hb_dvd_t * ); int (* angle_count) ( hb_dvd_t * ); void (* set_angle) ( hb_dvd_t *, int ); int (* main_feature)( hb_dvd_t *, hb_list_t * ); }; typedef struct hb_dvd_func_s hb_dvd_func_t; hb_dvd_func_t * hb_dvdnav_methods( void ); hb_dvd_func_t * hb_dvdread_methods( void ); #endif // HB_DVD_H HandBrake-0.10.2/libhb/qsv_common.c0000664000175200017520000015336712463330511017461 0ustar handbrakehandbrake/* qsv_common.c * * Copyright (c) 2003-2015 HandBrake Team * This file is part of the HandBrake source code. * Homepage: . * It may be used under the terms of the GNU General Public License v2. * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifdef USE_QSV #include #include "hb.h" #include "ports.h" #include "common.h" #include "hb_dict.h" #include "qsv_common.h" #include "h264_common.h" // QSV info for each codec static hb_qsv_info_t *hb_qsv_info_avc = NULL; static hb_qsv_info_t *hb_qsv_info_hevc = NULL; // API versions static mfxVersion qsv_software_version = { .Version = 0, }; static mfxVersion qsv_hardware_version = { .Version = 0, }; // AVC implementations static hb_qsv_info_t qsv_software_info_avc = { .available = 0, .codec_id = MFX_CODEC_AVC, .implementation = MFX_IMPL_SOFTWARE, }; static hb_qsv_info_t qsv_hardware_info_avc = { .available = 0, .codec_id = MFX_CODEC_AVC, .implementation = MFX_IMPL_HARDWARE_ANY|MFX_IMPL_VIA_ANY, }; // HEVC implementations static mfxPluginUID qsv_encode_plugin_hevc = { .Data = { 0x2F, 0xCA, 0x99, 0x74, 0x9F, 0xDB, 0x49, 0xAE, 0xB1, 0x21, 0xA5, 0xB6, 0x3E, 0xF5, 0x68, 0xF7 } }; static hb_qsv_info_t qsv_software_info_hevc = { .available = 0, .codec_id = MFX_CODEC_HEVC, .implementation = MFX_IMPL_SOFTWARE, }; static hb_qsv_info_t qsv_hardware_info_hevc = { .available = 0, .codec_id = MFX_CODEC_HEVC, .implementation = MFX_IMPL_HARDWARE_ANY|MFX_IMPL_VIA_ANY, }; // check available Intel Media SDK version against a minimum #define HB_CHECK_MFX_VERSION(MFX_VERSION, MAJOR, MINOR) \ (MFX_VERSION.Major == MAJOR && MFX_VERSION.Minor >= MINOR) /* * Determine the "generation" of QSV hardware based on the CPU microarchitecture. * Anything unknown is assumed to be more recent than the latest known generation. * This avoids having to order the hb_cpu_platform enum depending on QSV hardware. */ enum { QSV_G0, // third party hardware QSV_G1, // Sandy Bridge or equivalent QSV_G2, // Ivy Bridge or equivalent QSV_G3, // Haswell or equivalent }; static int qsv_hardware_generation(int cpu_platform) { switch (cpu_platform) { case HB_CPU_PLATFORM_INTEL_BNL: return QSV_G0; case HB_CPU_PLATFORM_INTEL_SNB: return QSV_G1; case HB_CPU_PLATFORM_INTEL_IVB: case HB_CPU_PLATFORM_INTEL_SLM: return QSV_G2; case HB_CPU_PLATFORM_INTEL_HSW: default: return QSV_G3; } } /* * Determine whether a given mfxIMPL is hardware-accelerated. */ static int qsv_implementation_is_hardware(mfxIMPL implementation) { return MFX_IMPL_BASETYPE(implementation) != MFX_IMPL_SOFTWARE; } int hb_qsv_available() { return hb_qsv_video_encoder_is_enabled(HB_VCODEC_QSV_H264); } int hb_qsv_video_encoder_is_enabled(int encoder) { switch (encoder) { case HB_VCODEC_QSV_H264: return hb_qsv_info_avc != NULL && hb_qsv_info_avc->available; default: return 0; } } int hb_qsv_audio_encoder_is_enabled(int encoder) { switch (encoder) { default: return 0; } } static void init_video_param(mfxVideoParam *videoParam) { if (videoParam == NULL) { return; } memset(videoParam, 0, sizeof(mfxVideoParam)); videoParam->mfx.CodecId = MFX_CODEC_AVC; videoParam->mfx.CodecLevel = MFX_LEVEL_UNKNOWN; videoParam->mfx.CodecProfile = MFX_PROFILE_UNKNOWN; videoParam->mfx.RateControlMethod = MFX_RATECONTROL_VBR; videoParam->mfx.TargetUsage = MFX_TARGETUSAGE_BALANCED; videoParam->mfx.TargetKbps = 5000; videoParam->mfx.GopOptFlag = MFX_GOP_CLOSED; videoParam->mfx.FrameInfo.FourCC = MFX_FOURCC_NV12; videoParam->mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420; videoParam->mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; videoParam->mfx.FrameInfo.FrameRateExtN = 25; videoParam->mfx.FrameInfo.FrameRateExtD = 1; videoParam->mfx.FrameInfo.Width = 1920; videoParam->mfx.FrameInfo.CropW = 1920; videoParam->mfx.FrameInfo.AspectRatioW = 1; videoParam->mfx.FrameInfo.Height = 1088; videoParam->mfx.FrameInfo.CropH = 1080; videoParam->mfx.FrameInfo.AspectRatioH = 1; videoParam->AsyncDepth = AV_QSV_ASYNC_DEPTH_DEFAULT; videoParam->IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY; } static void init_ext_coding_option2(mfxExtCodingOption2 *extCodingOption2) { if (extCodingOption2 == NULL) { return; } memset(extCodingOption2, 0, sizeof(mfxExtCodingOption2)); extCodingOption2->Header.BufferId = MFX_EXTBUFF_CODING_OPTION2; extCodingOption2->Header.BufferSz = sizeof(mfxExtCodingOption2); extCodingOption2->MBBRC = MFX_CODINGOPTION_ON; extCodingOption2->ExtBRC = MFX_CODINGOPTION_ON; extCodingOption2->Trellis = MFX_TRELLIS_I|MFX_TRELLIS_P|MFX_TRELLIS_B; extCodingOption2->RepeatPPS = MFX_CODINGOPTION_ON; extCodingOption2->BRefType = MFX_B_REF_PYRAMID; extCodingOption2->AdaptiveI = MFX_CODINGOPTION_ON; extCodingOption2->AdaptiveB = MFX_CODINGOPTION_ON; extCodingOption2->LookAheadDS = MFX_LOOKAHEAD_DS_4x; extCodingOption2->NumMbPerSlice = 2040; // 1920x1088/4 } static int query_capabilities(mfxSession session, mfxVersion version, hb_qsv_info_t *info) { /* * MFXVideoENCODE_Query(mfxSession, mfxVideoParam *in, mfxVideoParam *out); * * Mode 1: * - in is NULL * - out has the parameters we want to query set to 1 * - out->mfx.CodecId field has to be set (mandatory) * - MFXVideoENCODE_Query should zero out all unsupported parameters * * Mode 2: * - the paramaters we want to query are set for in * - in ->mfx.CodecId field has to be set (mandatory) * - out->mfx.CodecId field has to be set (mandatory) * - MFXVideoENCODE_Query should sanitize all unsupported parameters */ mfxStatus status; mfxPluginUID *pluginUID; mfxExtBuffer *videoExtParam[1]; mfxVideoParam videoParam, inputParam; mfxExtCodingOption2 extCodingOption2; /* Reset capabilities before querying */ info->capabilities = 0; /* Load optional codec plug-ins */ switch (info->codec_id) { case MFX_CODEC_HEVC: pluginUID = &qsv_encode_plugin_hevc; break; default: pluginUID = NULL; break; } if (pluginUID != NULL && HB_CHECK_MFX_VERSION(version, 1, 8) && MFXVideoUSER_Load(session, pluginUID, 0) < MFX_ERR_NONE) { // couldn't load plugin successfully return 0; } /* * First of all, check availability of an encoder for * this combination of a codec ID and implementation. * * Note: can error out rather than sanitizing * unsupported codec IDs, so don't log errors. */ if (HB_CHECK_MFX_VERSION(version, HB_QSV_MINVERSION_MAJOR, HB_QSV_MINVERSION_MINOR)) { if (info->implementation & MFX_IMPL_AUDIO) { /* Not yet supported */ return 0; } else { init_video_param(&inputParam); inputParam.mfx.CodecId = info->codec_id; memset(&videoParam, 0, sizeof(mfxVideoParam)); videoParam.mfx.CodecId = inputParam.mfx.CodecId; if (MFXVideoENCODE_Query(session, &inputParam, &videoParam) >= MFX_ERR_NONE && videoParam.mfx.CodecId == info->codec_id) { info->available = 1; } } } if (!info->available) { /* Don't check capabilities for unavailable encoders */ return 0; } if (info->implementation & MFX_IMPL_AUDIO) { /* We don't have any audio capability checks yet */ return 0; } else { /* Implementation-specific features that can't be queried */ if (qsv_implementation_is_hardware(info->implementation)) { if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3) { info->capabilities |= HB_QSV_CAP_B_REF_PYRAMID; } } else { if (HB_CHECK_MFX_VERSION(version, 1, 6)) { info->capabilities |= HB_QSV_CAP_B_REF_PYRAMID; } } /* API-specific features that can't be queried */ if (HB_CHECK_MFX_VERSION(version, 1, 6)) { // API >= 1.6 (mfxBitstream::DecodeTimeStamp, mfxExtCodingOption2) info->capabilities |= HB_QSV_CAP_MSDK_API_1_6; } /* * Check availability of optional rate control methods. * * Mode 2 tends to error out, but mode 1 gives false negatives, which * is worse. So use mode 2 and assume an error means it's unsupported. * * Also assume that LA and ICQ combined imply LA_ICQ is * supported, so we don't need to check the latter too. */ if (HB_CHECK_MFX_VERSION(version, 1, 7)) { init_video_param(&inputParam); inputParam.mfx.CodecId = info->codec_id; inputParam.mfx.RateControlMethod = MFX_RATECONTROL_LA; memset(&videoParam, 0, sizeof(mfxVideoParam)); videoParam.mfx.CodecId = inputParam.mfx.CodecId; if (MFXVideoENCODE_Query(session, &inputParam, &videoParam) >= MFX_ERR_NONE && videoParam.mfx.RateControlMethod == MFX_RATECONTROL_LA) { info->capabilities |= HB_QSV_CAP_RATECONTROL_LA; // also check for LA + interlaced support init_video_param(&inputParam); inputParam.mfx.CodecId = info->codec_id; inputParam.mfx.RateControlMethod = MFX_RATECONTROL_LA; inputParam.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_FIELD_TFF; memset(&videoParam, 0, sizeof(mfxVideoParam)); videoParam.mfx.CodecId = inputParam.mfx.CodecId; if (MFXVideoENCODE_Query(session, &inputParam, &videoParam) >= MFX_ERR_NONE && videoParam.mfx.FrameInfo.PicStruct == MFX_PICSTRUCT_FIELD_TFF && videoParam.mfx.RateControlMethod == MFX_RATECONTROL_LA) { info->capabilities |= HB_QSV_CAP_RATECONTROL_LAi; } } } if (HB_CHECK_MFX_VERSION(version, 1, 8)) { init_video_param(&inputParam); inputParam.mfx.CodecId = info->codec_id; inputParam.mfx.RateControlMethod = MFX_RATECONTROL_ICQ; memset(&videoParam, 0, sizeof(mfxVideoParam)); videoParam.mfx.CodecId = inputParam.mfx.CodecId; if (MFXVideoENCODE_Query(session, &inputParam, &videoParam) >= MFX_ERR_NONE && videoParam.mfx.RateControlMethod == MFX_RATECONTROL_ICQ) { info->capabilities |= HB_QSV_CAP_RATECONTROL_ICQ; } } /* * Check mfxExtCodingOption2 fields. * * Mode 2 suffers from false negatives with some drivers, whereas mode 1 * suffers from false positives instead. The latter is probably easier * and/or safer to sanitize for us, so use mode 1. */ if (HB_CHECK_MFX_VERSION(version, 1, 6) && info->codec_id == MFX_CODEC_AVC) { init_video_param(&videoParam); videoParam.mfx.CodecId = info->codec_id; init_ext_coding_option2(&extCodingOption2); videoParam.ExtParam = videoExtParam; videoParam.ExtParam[0] = (mfxExtBuffer*)&extCodingOption2; videoParam.NumExtParam = 1; status = MFXVideoENCODE_Query(session, NULL, &videoParam); if (status >= MFX_ERR_NONE) { #if 0 // testing code that could come in handy fprintf(stderr, "-------------------\n"); fprintf(stderr, "MBBRC: 0x%02X\n", extCodingOption2.MBBRC); fprintf(stderr, "ExtBRC: 0x%02X\n", extCodingOption2.ExtBRC); fprintf(stderr, "Trellis: 0x%02X\n", extCodingOption2.Trellis); fprintf(stderr, "RepeatPPS: 0x%02X\n", extCodingOption2.RepeatPPS); fprintf(stderr, "BRefType: %4"PRIu16"\n", extCodingOption2.BRefType); fprintf(stderr, "AdaptiveI: 0x%02X\n", extCodingOption2.AdaptiveI); fprintf(stderr, "AdaptiveB: 0x%02X\n", extCodingOption2.AdaptiveB); fprintf(stderr, "LookAheadDS: %4"PRIu16"\n", extCodingOption2.LookAheadDS); fprintf(stderr, "-------------------\n"); #endif /* * Sanitize API 1.6 fields: * * - MBBRC requires G3 hardware (Haswell or equivalent) * - ExtBRC requires G2 hardware (Ivy Bridge or equivalent) */ if (qsv_implementation_is_hardware(info->implementation) && qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3) { if (extCodingOption2.MBBRC) { info->capabilities |= HB_QSV_CAP_OPTION2_MBBRC; } } if (qsv_implementation_is_hardware(info->implementation) && qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G2) { if (extCodingOption2.ExtBRC) { info->capabilities |= HB_QSV_CAP_OPTION2_EXTBRC; } } /* * Sanitize API 1.7 fields: * * - Trellis requires G3 hardware (Haswell or equivalent) */ if (HB_CHECK_MFX_VERSION(version, 1, 7)) { if (qsv_implementation_is_hardware(info->implementation) && qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3) { if (extCodingOption2.Trellis) { info->capabilities |= HB_QSV_CAP_OPTION2_TRELLIS; } } } /* * Sanitize API 1.8 fields: * * - BRefType requires B-pyramid support * - LookAheadDS requires lookahead support * - AdaptiveI, AdaptiveB, NumMbPerSlice unknown (trust Query) */ if (HB_CHECK_MFX_VERSION(version, 1, 8)) { if (info->capabilities & HB_QSV_CAP_B_REF_PYRAMID) { if (extCodingOption2.BRefType) { info->capabilities |= HB_QSV_CAP_OPTION2_BREFTYPE; } } if (info->capabilities & HB_QSV_CAP_RATECONTROL_LA) { if (extCodingOption2.LookAheadDS) { info->capabilities |= HB_QSV_CAP_OPTION2_LA_DOWNS; } } if (extCodingOption2.AdaptiveI && extCodingOption2.AdaptiveB) { info->capabilities |= HB_QSV_CAP_OPTION2_IB_ADAPT; } if (extCodingOption2.NumMbPerSlice) { info->capabilities |= HB_QSV_CAP_OPTION2_NMBSLICE; } } } else { fprintf(stderr, "hb_qsv_info_init: mfxExtCodingOption2 check failed (0x%"PRIX32", 0x%"PRIX32", %d)\n", info->codec_id, info->implementation, status); } } } /* Unload optional codec plug-ins */ if (pluginUID != NULL && HB_CHECK_MFX_VERSION(version, 1, 8)) { MFXVideoUSER_UnLoad(session, pluginUID); } return 0; } int hb_qsv_info_init() { static int init_done = 0; if (init_done) return 0; init_done = 1; /* * First, check for any MSDK version to determine whether one or * more implementations are present; then check if we can use them. * * I've had issues using a NULL version with some combinations of * hardware and driver, so use a low version number (1.0) instead. */ mfxSession session; mfxVersion version = { .Major = 1, .Minor = 0, }; // check for software fallback if (MFXInit(MFX_IMPL_SOFTWARE, &version, &session) == MFX_ERR_NONE) { // Media SDK software found, but check that our minimum is supported MFXQueryVersion(session, &qsv_software_version); if (HB_CHECK_MFX_VERSION(qsv_software_version, HB_QSV_MINVERSION_MAJOR, HB_QSV_MINVERSION_MINOR)) { query_capabilities(session, qsv_software_version, &qsv_software_info_avc); query_capabilities(session, qsv_software_version, &qsv_software_info_hevc); // now that we know which hardware encoders are // available, we can set the preferred implementation hb_qsv_impl_set_preferred("software"); } MFXClose(session); } // check for actual hardware support if (MFXInit(MFX_IMPL_HARDWARE_ANY, &version, &session) == MFX_ERR_NONE) { // Media SDK hardware found, but check that our minimum is supported // // Note: this-party hardware (QSV_G0) is unsupported for the time being MFXQueryVersion(session, &qsv_hardware_version); if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G1 && HB_CHECK_MFX_VERSION(qsv_hardware_version, HB_QSV_MINVERSION_MAJOR, HB_QSV_MINVERSION_MINOR)) { query_capabilities(session, qsv_hardware_version, &qsv_hardware_info_avc); query_capabilities(session, qsv_hardware_version, &qsv_hardware_info_hevc); // now that we know which hardware encoders are // available, we can set the preferred implementation hb_qsv_impl_set_preferred("hardware"); } MFXClose(session); } // success return 0; } static void log_capabilities(int log_level, uint64_t caps, const char *prefix) { if (!caps) { hb_deep_log(log_level, "%s none (standard feature set)", prefix); } else { hb_deep_log(log_level, "%s%s%s%s%s%s%s%s%s%s%s%s%s", prefix, !(caps & HB_QSV_CAP_MSDK_API_1_6) ? "" : " api1.6", !(caps & HB_QSV_CAP_B_REF_PYRAMID) ? "" : " bpyramid", !(caps & HB_QSV_CAP_OPTION2_BREFTYPE) ? "" : " breftype", !(caps & HB_QSV_CAP_RATECONTROL_LA) ? "" : " lookahead", !(caps & HB_QSV_CAP_RATECONTROL_LAi) ? "" : " lookaheadi", !(caps & HB_QSV_CAP_OPTION2_LA_DOWNS) ? "" : " lookaheadds", !(caps & HB_QSV_CAP_RATECONTROL_ICQ) ? "" : " icq", !(caps & HB_QSV_CAP_OPTION2_MBBRC) ? "" : " mbbrc", !(caps & HB_QSV_CAP_OPTION2_EXTBRC) ? "" : " extbrc", !(caps & HB_QSV_CAP_OPTION2_TRELLIS) ? "" : " trellis", !(caps & HB_QSV_CAP_OPTION2_IB_ADAPT) ? "" : " adaptivei adaptiveb", !(caps & HB_QSV_CAP_OPTION2_NMBSLICE) ? "" : " nummbperslice"); } } void hb_qsv_info_print() { // is QSV available and usable? hb_log("Intel Quick Sync Video support: %s", hb_qsv_available() ? "yes": "no"); // also print the details if (qsv_hardware_version.Version) { hb_log(" - Intel Media SDK hardware: API %"PRIu16".%"PRIu16" (minimum: %"PRIu16".%"PRIu16")", qsv_hardware_version.Major, qsv_hardware_version.Minor, HB_QSV_MINVERSION_MAJOR, HB_QSV_MINVERSION_MINOR); } if (qsv_software_version.Version) { hb_log(" - Intel Media SDK software: API %"PRIu16".%"PRIu16" (minimum: %"PRIu16".%"PRIu16")", qsv_software_version.Major, qsv_software_version.Minor, HB_QSV_MINVERSION_MAJOR, HB_QSV_MINVERSION_MINOR); } if (hb_qsv_available()) { if (hb_qsv_info_avc != NULL && hb_qsv_info_avc->available) { hb_log(" - H.264 encoder: yes"); hb_log(" - preferred implementation: %s", hb_qsv_impl_get_name(hb_qsv_info_avc->implementation)); if (qsv_hardware_info_avc.available) { log_capabilities(2, qsv_hardware_info_avc.capabilities, " - capabilities (hardware): "); } if (qsv_software_info_avc.available) { log_capabilities(2, qsv_software_info_avc.capabilities, " - capabilities (software): "); } } else { hb_log(" - H.264 encoder: no"); } if (hb_qsv_info_hevc != NULL && hb_qsv_info_hevc->available) { hb_log(" - H.265 encoder: yes (unsupported)"); hb_log(" - preferred implementation: %s", hb_qsv_impl_get_name(hb_qsv_info_hevc->implementation)); if (qsv_hardware_info_hevc.available) { log_capabilities(2, qsv_hardware_info_hevc.capabilities, " - capabilities (hardware): "); } if (qsv_software_info_hevc.available) { log_capabilities(2, qsv_software_info_hevc.capabilities, " - capabilities (software): "); } } else { hb_log(" - H.265 encoder: no"); } } } hb_qsv_info_t* hb_qsv_info_get(int encoder) { switch (encoder) { case HB_VCODEC_QSV_H264: return hb_qsv_info_avc; default: return NULL; } } const char* hb_qsv_decode_get_codec_name(enum AVCodecID codec_id) { switch (codec_id) { case AV_CODEC_ID_H264: return "h264_qsv"; default: return NULL; } } int hb_qsv_decode_is_enabled(hb_job_t *job) { return ((job != NULL && job->qsv.decode) && (job->vcodec & HB_VCODEC_QSV_MASK) && (job->title->video_decode_support & HB_DECODE_SUPPORT_QSV)); } int hb_qsv_copyframe_is_slow(int encoder) { hb_qsv_info_t *info = hb_qsv_info_get(encoder); if (info != NULL && qsv_implementation_is_hardware(info->implementation)) { // we should really check the driver version, but since it's not // available, checking the API version is the best we can do :-( return !HB_CHECK_MFX_VERSION(qsv_hardware_version, 1, 7); } return 0; } int hb_qsv_codingoption_xlat(int val) { switch (HB_QSV_CLIP3(-1, 2, val)) { case 0: return MFX_CODINGOPTION_OFF; case 1: case 2: // MFX_CODINGOPTION_ADAPTIVE, reserved return MFX_CODINGOPTION_ON; case -1: default: return MFX_CODINGOPTION_UNKNOWN; } } int hb_qsv_trellisvalue_xlat(int val) { switch (HB_QSV_CLIP3(0, 3, val)) { case 0: return MFX_TRELLIS_OFF; case 1: // I-frames only return MFX_TRELLIS_I; case 2: // I- and P-frames return MFX_TRELLIS_I|MFX_TRELLIS_P; case 3: // all frames return MFX_TRELLIS_I|MFX_TRELLIS_P|MFX_TRELLIS_B; default: return MFX_TRELLIS_UNKNOWN; } } const char* hb_qsv_codingoption_get_name(int val) { switch (val) { case MFX_CODINGOPTION_ON: return "on"; case MFX_CODINGOPTION_OFF: return "off"; case MFX_CODINGOPTION_ADAPTIVE: return "adaptive"; case MFX_CODINGOPTION_UNKNOWN: return "unknown (auto)"; default: return NULL; } } int hb_qsv_atoindex(const char* const *arr, const char *str, int *err) { int i; for (i = 0; arr[i] != NULL; i++) { if (!strcasecmp(arr[i], str)) { break; } } *err = (arr[i] == NULL); return i; } // adapted from libx264 int hb_qsv_atobool(const char *str, int *err) { if (!strcasecmp(str, "1") || !strcasecmp(str, "yes") || !strcasecmp(str, "true")) { return 1; } if (!strcasecmp(str, "0") || !strcasecmp(str, "no") || !strcasecmp(str, "false")) { return 0; } *err = 1; return 0; } // adapted from libx264 int hb_qsv_atoi(const char *str, int *err) { char *end; int v = strtol(str, &end, 0); if (end == str || end[0] != '\0') { *err = 1; } return v; } // adapted from libx264 float hb_qsv_atof(const char *str, int *err) { char *end; float v = strtod(str, &end); if (end == str || end[0] != '\0') { *err = 1; } return v; } int hb_qsv_param_parse(hb_qsv_param_t *param, hb_qsv_info_t *info, const char *key, const char *value) { float fvalue; int ivalue, error = 0; if (param == NULL || info == NULL) { return HB_QSV_PARAM_ERROR; } if (value == NULL || value[0] == '\0') { value = "true"; } else if (value[0] == '=') { value++; } if (key == NULL || key[0] == '\0') { return HB_QSV_PARAM_BAD_NAME; } else if (!strncasecmp(key, "no-", 3)) { key += 3; value = hb_qsv_atobool(value, &error) ? "false" : "true"; if (error) { return HB_QSV_PARAM_BAD_VALUE; } } if (!strcasecmp(key, "target-usage") || !strcasecmp(key, "tu")) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->videoParam->mfx.TargetUsage = HB_QSV_CLIP3(MFX_TARGETUSAGE_1, MFX_TARGETUSAGE_7, ivalue); } } else if (!strcasecmp(key, "num-ref-frame") || !strcasecmp(key, "ref")) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->videoParam->mfx.NumRefFrame = HB_QSV_CLIP3(0, 16, ivalue); } } else if (!strcasecmp(key, "gop-ref-dist")) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->gop.gop_ref_dist = HB_QSV_CLIP3(-1, 32, ivalue); } } else if (!strcasecmp(key, "gop-pic-size") || !strcasecmp(key, "keyint")) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->gop.gop_pic_size = HB_QSV_CLIP3(-1, UINT16_MAX, ivalue); } } else if (!strcasecmp(key, "b-pyramid")) { if (info->capabilities & HB_QSV_CAP_B_REF_PYRAMID) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->gop.b_pyramid = HB_QSV_CLIP3(-1, 1, ivalue); } } else { return HB_QSV_PARAM_UNSUPPORTED; } } else if (!strcasecmp(key, "scenecut")) { ivalue = hb_qsv_atobool(value, &error); if (!error) { if (!ivalue) { param->videoParam->mfx.GopOptFlag |= MFX_GOP_STRICT; } else { param->videoParam->mfx.GopOptFlag &= ~MFX_GOP_STRICT; } } } else if (!strcasecmp(key, "adaptive-i") || !strcasecmp(key, "i-adapt")) { if (info->capabilities & HB_QSV_CAP_OPTION2_IB_ADAPT) { ivalue = hb_qsv_atobool(value, &error); if (!error) { param->codingOption2.AdaptiveI = hb_qsv_codingoption_xlat(ivalue); } } else { return HB_QSV_PARAM_UNSUPPORTED; } } else if (!strcasecmp(key, "adaptive-b") || !strcasecmp(key, "b-adapt")) { if (info->capabilities & HB_QSV_CAP_OPTION2_IB_ADAPT) { ivalue = hb_qsv_atobool(value, &error); if (!error) { param->codingOption2.AdaptiveB = hb_qsv_codingoption_xlat(ivalue); } } else { return HB_QSV_PARAM_UNSUPPORTED; } } else if (!strcasecmp(key, "force-cqp")) { ivalue = hb_qsv_atobool(value, &error); if (!error) { param->rc.icq = !ivalue; } } else if (!strcasecmp(key, "cqp-offset-i")) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->rc.cqp_offsets[0] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue); } } else if (!strcasecmp(key, "cqp-offset-p")) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->rc.cqp_offsets[1] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue); } } else if (!strcasecmp(key, "cqp-offset-b")) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->rc.cqp_offsets[2] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue); } } else if (!strcasecmp(key, "vbv-init")) { fvalue = hb_qsv_atof(value, &error); if (!error) { param->rc.vbv_buffer_init = HB_QSV_CLIP3(0, UINT16_MAX, fvalue); } } else if (!strcasecmp(key, "vbv-bufsize")) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->rc.vbv_buffer_size = HB_QSV_CLIP3(0, UINT16_MAX, ivalue); } } else if (!strcasecmp(key, "vbv-maxrate")) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->rc.vbv_max_bitrate = HB_QSV_CLIP3(0, UINT16_MAX, ivalue); } } else if (!strcasecmp(key, "cavlc") || !strcasecmp(key, "cabac")) { switch (info->codec_id) { case MFX_CODEC_AVC: ivalue = hb_qsv_atobool(value, &error); break; default: return HB_QSV_PARAM_UNSUPPORTED; } if (!error) { if (!strcasecmp(key, "cabac")) { ivalue = !ivalue; } param->codingOption.CAVLC = hb_qsv_codingoption_xlat(ivalue); } } else if (!strcasecmp(key, "videoformat")) { switch (info->codec_id) { case MFX_CODEC_AVC: ivalue = hb_qsv_atoindex(hb_h264_vidformat_names, value, &error); break; default: return HB_QSV_PARAM_UNSUPPORTED; } if (!error) { param->videoSignalInfo.VideoFormat = ivalue; } } else if (!strcasecmp(key, "fullrange")) { switch (info->codec_id) { case MFX_CODEC_AVC: ivalue = hb_qsv_atoindex(hb_h264_fullrange_names, value, &error); break; default: return HB_QSV_PARAM_UNSUPPORTED; } if (!error) { param->videoSignalInfo.VideoFullRange = ivalue; } } else if (!strcasecmp(key, "colorprim")) { switch (info->codec_id) { case MFX_CODEC_AVC: ivalue = hb_qsv_atoindex(hb_h264_colorprim_names, value, &error); break; default: return HB_QSV_PARAM_UNSUPPORTED; } if (!error) { param->videoSignalInfo.ColourDescriptionPresent = 1; param->videoSignalInfo.ColourPrimaries = ivalue; } } else if (!strcasecmp(key, "transfer")) { switch (info->codec_id) { case MFX_CODEC_AVC: ivalue = hb_qsv_atoindex(hb_h264_transfer_names, value, &error); break; default: return HB_QSV_PARAM_UNSUPPORTED; } if (!error) { param->videoSignalInfo.ColourDescriptionPresent = 1; param->videoSignalInfo.TransferCharacteristics = ivalue; } } else if (!strcasecmp(key, "colormatrix")) { switch (info->codec_id) { case MFX_CODEC_AVC: ivalue = hb_qsv_atoindex(hb_h264_colmatrix_names, value, &error); break; default: return HB_QSV_PARAM_UNSUPPORTED; } if (!error) { param->videoSignalInfo.ColourDescriptionPresent = 1; param->videoSignalInfo.MatrixCoefficients = ivalue; } } else if (!strcasecmp(key, "tff") || !strcasecmp(key, "interlaced")) { switch (info->codec_id) { case MFX_CODEC_AVC: ivalue = hb_qsv_atobool(value, &error); break; default: return HB_QSV_PARAM_UNSUPPORTED; } if (!error) { param->videoParam->mfx.FrameInfo.PicStruct = (ivalue ? MFX_PICSTRUCT_FIELD_TFF : MFX_PICSTRUCT_PROGRESSIVE); } } else if (!strcasecmp(key, "bff")) { switch (info->codec_id) { case MFX_CODEC_AVC: ivalue = hb_qsv_atobool(value, &error); break; default: return HB_QSV_PARAM_UNSUPPORTED; } if (!error) { param->videoParam->mfx.FrameInfo.PicStruct = (ivalue ? MFX_PICSTRUCT_FIELD_BFF : MFX_PICSTRUCT_PROGRESSIVE); } } else if (!strcasecmp(key, "mbbrc")) { if (info->capabilities & HB_QSV_CAP_OPTION2_MBBRC) { ivalue = hb_qsv_atobool(value, &error); if (!error) { param->codingOption2.MBBRC = hb_qsv_codingoption_xlat(ivalue); } } else { return HB_QSV_PARAM_UNSUPPORTED; } } else if (!strcasecmp(key, "extbrc")) { if (info->capabilities & HB_QSV_CAP_OPTION2_EXTBRC) { ivalue = hb_qsv_atobool(value, &error); if (!error) { param->codingOption2.ExtBRC = hb_qsv_codingoption_xlat(ivalue); } } else { return HB_QSV_PARAM_UNSUPPORTED; } } else if (!strcasecmp(key, "lookahead") || !strcasecmp(key, "la")) { if (info->capabilities & HB_QSV_CAP_RATECONTROL_LA) { ivalue = hb_qsv_atobool(value, &error); if (!error) { param->rc.lookahead = ivalue; } } else { return HB_QSV_PARAM_UNSUPPORTED; } } else if (!strcasecmp(key, "lookahead-depth") || !strcasecmp(key, "la-depth")) { if (info->capabilities & HB_QSV_CAP_RATECONTROL_LA) { ivalue = hb_qsv_atoi(value, &error); if (!error) { // LookAheadDepth 10 will cause a hang with some driver versions param->codingOption2.LookAheadDepth = HB_QSV_CLIP3(11, 100, ivalue); } } else { return HB_QSV_PARAM_UNSUPPORTED; } } else if (!strcasecmp(key, "lookahead-ds") || !strcasecmp(key, "la-ds")) { if (info->capabilities & HB_QSV_CAP_OPTION2_LA_DOWNS) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->codingOption2.LookAheadDS = HB_QSV_CLIP3(MFX_LOOKAHEAD_DS_UNKNOWN, MFX_LOOKAHEAD_DS_4x, ivalue); } } else { return HB_QSV_PARAM_UNSUPPORTED; } } else if (!strcasecmp(key, "trellis")) { if (info->capabilities & HB_QSV_CAP_OPTION2_TRELLIS) { ivalue = hb_qsv_atoi(value, &error); if (!error) { param->codingOption2.Trellis = hb_qsv_trellisvalue_xlat(ivalue); } } else { return HB_QSV_PARAM_UNSUPPORTED; } } else { /* * TODO: * - slice count (num-slice/slices, num-mb-per-slice/slice-max-mbs) * - open-gop * - fake-interlaced (mfxExtCodingOption.FramePicture???) * - intra-refresh */ return HB_QSV_PARAM_BAD_NAME; } return error ? HB_QSV_PARAM_BAD_VALUE : HB_QSV_PARAM_OK; } #ifdef HB_API_OLD_PRESET_GETTERS const char* const* hb_qsv_presets() { return hb_qsv_preset_get_names(); } #endif const char* const* hb_qsv_preset_get_names() { if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3) { return hb_qsv_preset_names2; } else { return hb_qsv_preset_names1; } } const char* const* hb_qsv_profile_get_names(int encoder) { switch (encoder) { case HB_VCODEC_QSV_H264: return hb_h264_profile_names; default: return NULL; } } const char* const* hb_qsv_level_get_names(int encoder) { switch (encoder) { case HB_VCODEC_QSV_H264: return hb_h264_level_names; default: return NULL; } } const char* hb_qsv_video_quality_get_name(uint32_t codec) { uint64_t caps; switch (codec) { case HB_VCODEC_QSV_H264: caps = hb_qsv_info_avc != NULL ? hb_qsv_info_avc->capabilities : 0; return (caps & HB_QSV_CAP_RATECONTROL_ICQ) ? "ICQ" : "QP"; default: return "QP"; } } void hb_qsv_video_quality_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction) { uint64_t caps; switch (codec) { case HB_VCODEC_QSV_H264: caps = hb_qsv_info_avc != NULL ? hb_qsv_info_avc->capabilities : 0; *direction = 1; *granularity = 1.; *low = (caps & HB_QSV_CAP_RATECONTROL_ICQ) ? 1. : 0.; *high = 51.; break; default: *direction = 1; *granularity = 1.; *low = 0.; *high = 51.; break; } } int hb_qsv_param_default_preset(hb_qsv_param_t *param, mfxVideoParam *videoParam, hb_qsv_info_t *info, const char *preset) { if (param != NULL && videoParam != NULL && info != NULL) { int ret = hb_qsv_param_default(param, videoParam, info); if (ret) { return ret; } } else { hb_error("hb_qsv_param_default_preset: invalid pointer(s)"); return -1; } if (preset != NULL && preset[0] != '\0') { if (!strcasecmp(preset, "quality")) { /* * HSW TargetUsage: 2 * NumRefFrame: 0 * GopRefDist: 4 (CQP), 3 (VBR) -> -1 (set by encoder) * GopPicSize: 32 (CQP), 1 second (VBR) -> -1 (set by encoder) * BPyramid: 1 (CQP), 0 (VBR) -> -1 (set by encoder) * LookAhead: 1 (on) * LookAheadDepth: 40 * * * SNB * IVB Preset Not Available * * Note: this preset is the libhb default (like x264's "medium"). */ } else if (!strcasecmp(preset, "balanced")) { /* * HSW TargetUsage: 4 * NumRefFrame: 1 * GopRefDist: 4 (CQP), 3 (VBR) -> -1 (set by encoder) * GopPicSize: 32 (CQP), 1 second (VBR) -> -1 (set by encoder) * BPyramid: 1 (CQP), 0 (VBR) -> -1 (set by encoder) * LookAhead: 0 (off) * LookAheadDepth: Not Applicable */ if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3) { param->rc.lookahead = 0; param->videoParam->mfx.NumRefFrame = 1; param->videoParam->mfx.TargetUsage = MFX_TARGETUSAGE_4; } else { /* * SNB * IVB TargetUsage: 2 * NumRefFrame: 0 * GopRefDist: 4 (CQP), 3 (VBR) -> -1 (set by encoder) * GopPicSize: 32 (CQP), 1 second (VBR) -> -1 (set by encoder) * BPyramid: Not Applicable * LookAhead: Not Applicable * LookAheadDepth: Not Applicable * * Note: this preset is not the libhb default, * but the settings are the same so do nothing. */ } } else if (!strcasecmp(preset, "speed")) { if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3) { /* * HSW TargetUsage: 6 * NumRefFrame: 0 (CQP), 1 (VBR) -> see note * GopRefDist: 4 (CQP), 3 (VBR) -> -1 (set by encoder) * GopPicSize: 32 (CQP), 1 second (VBR) -> -1 (set by encoder) * BPyramid: 1 (CQP), 0 (VBR) -> -1 (set by encoder) * LookAhead: 0 (off) * LookAheadDepth: Not Applicable * * Note: NumRefFrame depends on the RC method, which we don't * know here. Rather than have an additional variable and * having the encoder set it, we set it to 1 and let the * B-pyramid code sanitize it. Since BPyramid is 1 w/CQP, * the result (3) is the same as what MSDK would pick for * NumRefFrame 0 GopRefDist 4 GopPicSize 32. */ param->rc.lookahead = 0; param->videoParam->mfx.NumRefFrame = 1; param->videoParam->mfx.TargetUsage = MFX_TARGETUSAGE_6; } else { /* * SNB * IVB TargetUsage: 4 * NumRefFrame: 0 * GopRefDist: 4 (CQP), 3 (VBR) -> -1 (set by encoder) * GopPicSize: 32 (CQP), 1 second (VBR) -> -1 (set by encoder) * BPyramid: Not Applicable * LookAhead: Not Applicable * LookAheadDepth: Not Applicable */ param->videoParam->mfx.TargetUsage = MFX_TARGETUSAGE_4; } } else { hb_error("hb_qsv_param_default_preset: invalid preset '%s'", preset); return -1; } } return 0; } int hb_qsv_param_default(hb_qsv_param_t *param, mfxVideoParam *videoParam, hb_qsv_info_t *info) { if (param != NULL && videoParam != NULL && info != NULL) { // introduced in API 1.0 memset(¶m->codingOption, 0, sizeof(mfxExtCodingOption)); param->codingOption.Header.BufferId = MFX_EXTBUFF_CODING_OPTION; param->codingOption.Header.BufferSz = sizeof(mfxExtCodingOption); param->codingOption.MECostType = 0; // reserved, must be 0 param->codingOption.MESearchType = 0; // reserved, must be 0 param->codingOption.MVSearchWindow.x = 0; // reserved, must be 0 param->codingOption.MVSearchWindow.y = 0; // reserved, must be 0 param->codingOption.RefPicListReordering = 0; // reserved, must be 0 param->codingOption.IntraPredBlockSize = 0; // reserved, must be 0 param->codingOption.InterPredBlockSize = 0; // reserved, must be 0 param->codingOption.MVPrecision = 0; // reserved, must be 0 param->codingOption.EndOfSequence = MFX_CODINGOPTION_UNKNOWN; param->codingOption.RateDistortionOpt = MFX_CODINGOPTION_UNKNOWN; param->codingOption.ResetRefList = MFX_CODINGOPTION_UNKNOWN; param->codingOption.MaxDecFrameBuffering = 0; // unspecified param->codingOption.AUDelimiter = MFX_CODINGOPTION_OFF; param->codingOption.SingleSeiNalUnit = MFX_CODINGOPTION_UNKNOWN; param->codingOption.PicTimingSEI = MFX_CODINGOPTION_OFF; param->codingOption.VuiNalHrdParameters = MFX_CODINGOPTION_UNKNOWN; param->codingOption.FramePicture = MFX_CODINGOPTION_UNKNOWN; param->codingOption.CAVLC = MFX_CODINGOPTION_OFF; // introduced in API 1.3 param->codingOption.RefPicMarkRep = MFX_CODINGOPTION_UNKNOWN; param->codingOption.FieldOutput = MFX_CODINGOPTION_UNKNOWN; param->codingOption.NalHrdConformance = MFX_CODINGOPTION_UNKNOWN; param->codingOption.SingleSeiNalUnit = MFX_CODINGOPTION_UNKNOWN; param->codingOption.VuiVclHrdParameters = MFX_CODINGOPTION_UNKNOWN; // introduced in API 1.4 param->codingOption.ViewOutput = MFX_CODINGOPTION_UNKNOWN; // introduced in API 1.6 param->codingOption.RecoveryPointSEI = MFX_CODINGOPTION_UNKNOWN; // introduced in API 1.3 memset(¶m->videoSignalInfo, 0, sizeof(mfxExtVideoSignalInfo)); param->videoSignalInfo.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO; param->videoSignalInfo.Header.BufferSz = sizeof(mfxExtVideoSignalInfo); param->videoSignalInfo.VideoFormat = 5; // undefined param->videoSignalInfo.VideoFullRange = 0; // TV range param->videoSignalInfo.ColourDescriptionPresent = 0; // don't write to bitstream param->videoSignalInfo.ColourPrimaries = 2; // undefined param->videoSignalInfo.TransferCharacteristics = 2; // undefined param->videoSignalInfo.MatrixCoefficients = 2; // undefined // introduced in API 1.6 memset(¶m->codingOption2, 0, sizeof(mfxExtCodingOption2)); param->codingOption2.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2; param->codingOption2.Header.BufferSz = sizeof(mfxExtCodingOption2); param->codingOption2.IntRefType = 0; param->codingOption2.IntRefCycleSize = 2; param->codingOption2.IntRefQPDelta = 0; param->codingOption2.MaxFrameSize = 0; param->codingOption2.BitrateLimit = MFX_CODINGOPTION_ON; param->codingOption2.MBBRC = MFX_CODINGOPTION_ON; param->codingOption2.ExtBRC = MFX_CODINGOPTION_OFF; // introduced in API 1.7 param->codingOption2.LookAheadDepth = 40; param->codingOption2.Trellis = MFX_TRELLIS_OFF; // introduced in API 1.8 param->codingOption2.RepeatPPS = MFX_CODINGOPTION_ON; param->codingOption2.BRefType = MFX_B_REF_UNKNOWN; // controlled via gop.b_pyramid param->codingOption2.AdaptiveI = MFX_CODINGOPTION_OFF; param->codingOption2.AdaptiveB = MFX_CODINGOPTION_OFF; param->codingOption2.LookAheadDS = MFX_LOOKAHEAD_DS_OFF; param->codingOption2.NumMbPerSlice = 0; // GOP & rate control param->gop.b_pyramid = -1; // set automatically param->gop.gop_pic_size = -1; // set automatically param->gop.gop_ref_dist = -1; // set automatically param->gop.int_ref_cycle_size = -1; // set automatically param->rc.icq = 1; // enabled by default (if supported) param->rc.lookahead = 1; // enabled by default (if supported) param->rc.cqp_offsets[0] = 0; param->rc.cqp_offsets[1] = 2; param->rc.cqp_offsets[2] = 4; param->rc.vbv_max_bitrate = 0; // set automatically param->rc.vbv_buffer_size = 0; // set automatically param->rc.vbv_buffer_init = .0; // set automatically // introduced in API 1.0 memset(videoParam, 0, sizeof(mfxVideoParam)); param->videoParam = videoParam; param->videoParam->Protected = 0; // reserved, must be 0 param->videoParam->NumExtParam = 0; param->videoParam->IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY; param->videoParam->mfx.TargetUsage = MFX_TARGETUSAGE_2; param->videoParam->mfx.GopOptFlag = MFX_GOP_CLOSED; param->videoParam->mfx.NumThread = 0; // deprecated, must be 0 param->videoParam->mfx.EncodedOrder = 0; // input is in display order param->videoParam->mfx.IdrInterval = 0; // all I-frames are IDR param->videoParam->mfx.NumSlice = 0; // use Media SDK default param->videoParam->mfx.NumRefFrame = 0; // use Media SDK default param->videoParam->mfx.GopPicSize = 0; // use Media SDK default param->videoParam->mfx.GopRefDist = 0; // use Media SDK default // introduced in API 1.1 param->videoParam->AsyncDepth = AV_QSV_ASYNC_DEPTH_DEFAULT; // introduced in API 1.3 param->videoParam->mfx.BRCParamMultiplier = 0; // no multiplier // FrameInfo: set by video encoder, except PicStruct param->videoParam->mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; // attach supported mfxExtBuffer structures to the mfxVideoParam param->videoParam->NumExtParam = 0; param->videoParam->ExtParam = param->ExtParamArray; param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)¶m->codingOption; param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)¶m->videoSignalInfo; if (info->capabilities & HB_QSV_CAP_MSDK_API_1_6) { param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)¶m->codingOption2; } } else { hb_error("hb_qsv_param_default: invalid pointer(s)"); return -1; } return 0; } const char* hb_qsv_frametype_name(uint16_t qsv_frametype) { if (qsv_frametype & MFX_FRAMETYPE_IDR) { return qsv_frametype & MFX_FRAMETYPE_REF ? "IDR (ref)" : "IDR"; } else if (qsv_frametype & MFX_FRAMETYPE_I) { return qsv_frametype & MFX_FRAMETYPE_REF ? "I (ref)" : "I"; } else if (qsv_frametype & MFX_FRAMETYPE_P) { return qsv_frametype & MFX_FRAMETYPE_REF ? "P (ref)" : "P"; } else if (qsv_frametype & MFX_FRAMETYPE_B) { return qsv_frametype & MFX_FRAMETYPE_REF ? "B (ref)" : "B"; } else { return "unknown"; } } uint8_t hb_qsv_frametype_xlat(uint16_t qsv_frametype, uint16_t *out_flags) { uint16_t flags = 0; uint8_t frametype = 0; if (qsv_frametype & MFX_FRAMETYPE_IDR) { frametype = HB_FRAME_IDR; } else if (qsv_frametype & MFX_FRAMETYPE_I) { frametype = HB_FRAME_I; } else if (qsv_frametype & MFX_FRAMETYPE_P) { frametype = HB_FRAME_P; } else if (qsv_frametype & MFX_FRAMETYPE_B) { frametype = HB_FRAME_B; } if (qsv_frametype & MFX_FRAMETYPE_REF) { flags |= HB_FRAME_REF; } if (out_flags != NULL) { *out_flags = flags; } return frametype; } int hb_qsv_impl_set_preferred(const char *name) { if (name == NULL) { return -1; } if (!strcasecmp(name, "software")) { if (qsv_software_info_avc.available) { hb_qsv_info_avc = &qsv_software_info_avc; } if (qsv_software_info_hevc.available) { hb_qsv_info_hevc = &qsv_software_info_hevc; } return 0; } if (!strcasecmp(name, "hardware")) { if (qsv_hardware_info_avc.available) { hb_qsv_info_avc = &qsv_hardware_info_avc; } if (qsv_hardware_info_hevc.available) { hb_qsv_info_hevc = &qsv_hardware_info_hevc; } return 0; } return -1; } const char* hb_qsv_impl_get_name(int impl) { switch (MFX_IMPL_BASETYPE(impl)) { case MFX_IMPL_SOFTWARE: return "software"; case MFX_IMPL_HARDWARE: return "hardware (1)"; case MFX_IMPL_HARDWARE2: return "hardware (2)"; case MFX_IMPL_HARDWARE3: return "hardware (3)"; case MFX_IMPL_HARDWARE4: return "hardware (4)"; case MFX_IMPL_HARDWARE_ANY: return "hardware (any)"; case MFX_IMPL_AUTO: return "automatic"; case MFX_IMPL_AUTO_ANY: return "automatic (any)"; default: return NULL; } } void hb_qsv_force_workarounds() { qsv_software_info_avc.capabilities &= ~HB_QSV_CAP_MSDK_API_1_6; qsv_hardware_info_avc.capabilities &= ~HB_QSV_CAP_MSDK_API_1_6; qsv_software_info_hevc.capabilities &= ~HB_QSV_CAP_MSDK_API_1_6; qsv_hardware_info_hevc.capabilities &= ~HB_QSV_CAP_MSDK_API_1_6; } #endif // USE_QSV HandBrake-0.10.2/libhb/h265_common.h0000664000175200017520000000326312470166275017342 0ustar handbrakehandbrake/* h265_common.h Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #ifndef HB_H265_COMMON_H #define HB_H265_COMMON_H static const char * const hb_h265_tier_names[] = { "auto", "main", "high", NULL, }; static const char * const hb_h265_profile_names[] = { "auto", "main", "mainstillpicture", NULL, }; static const char * const hb_h265_level_names[] = { "auto", "1.0", "2.0", "2.1", "3.0", "3.1", "4.0", "4.1", "5.0", "5.1", "5.2", "6.0", "6.1", "6.2", NULL, }; static const int const hb_h265_level_values[] = { -1, 30, 60, 63, 90, 93, 120, 123, 150, 153, 156, 180, 183, 186, 0, }; // stolen from libx265's x265.h static const char * const hb_h265_fullrange_names[] = { "limited", "full", NULL, }; static const char * const hb_h265_vidformat_names[] = { "component", "pal", "ntsc", "secam", "mac", "undef", NULL, }; static const char * const hb_h265_colorprim_names[] = { "", "bt709", "undef", "", "bt470m", "bt470bg", "smpte170m", "smpte240m", "film", "bt2020", NULL, }; static const char * const hb_h265_transfer_names[] = { "", "bt709", "undef", "", "bt470m", "bt470bg", "smpte170m", "smpte240m", "linear", "log100", "log316", "iec61966-2-4", "bt1361e", "iec61966-2-1", "bt2020-10", "bt2020-12", NULL, }; static const char * const hb_h265_colmatrix_names[] = { "GBR", "bt709", "undef", "", "fcc", "bt470bg", "smpte170m", "smpte240m", "YCgCo", "bt2020nc", "bt2020c", NULL, }; #endif //HB_H265_COMMON_H HandBrake-0.10.2/test/0000775000175200017520000000000012535641635015030 5ustar handbrakehandbrakeHandBrake-0.10.2/test/parsecsv.c0000664000175200017520000001122412463330511017006 0ustar handbrakehandbrake/* parsecsv.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include #include "hb.h" #include "parsecsv.h" /* Internal declarations */ #define is_newline(_x) ( (_x) == 13 || \ (_x) == 11 || \ (_x) == 10 ) #define is_white(_x) ( (_x) == '\t' || \ (_x) == ' ' || \ is_newline(_x) ) #define is_sep(_x) ( (_x) == ',' ) #define is_esc(_x) ( (_x) == '\\' ) #define CSV_CHAR_ERROR 0x8000 #define CSV_CHAR_EOF 0x4000 #define CSV_CHAR_ROWSEP 0x2000 #define CSV_CHAR_COLSEP 0x1000 #define CSV_PARSE_NORMAL 0x0000 #define CSV_PARSE_SEEK 0x0001 #define CSV_PARSE_ESC 0x0002 static uint16_t hb_parse_character( hb_csv_file_t * file ); static void hb_trim_end( char *text ); /* Open a CSV File */ hb_csv_file_t *hb_open_csv_file( const char *filepath ) { hb_csv_file_t *file = NULL; FILE * fileref; if( filepath == NULL ) { return file; } fileref = hb_fopen(filepath, "r"); if( fileref == NULL ) { return file; } file = malloc( sizeof( hb_csv_file_t ) ); if( file == NULL ) { return file; } file->fileref = fileref; file->eof = 0; file->parse_state = CSV_PARSE_SEEK; file->curr_col = 0; file->curr_row = 0; return file; } void hb_close_csv_file( hb_csv_file_t *file ) { if( file == NULL ) { return; } fclose( file->fileref ); free( file ); } /* Parse CSV Cells */ hb_csv_cell_t *hb_read_next_cell( hb_csv_file_t *file ) { hb_csv_cell_t *cell = NULL; uint16_t c; int index; if( file == NULL ) { return cell; } if( file->eof ) { return cell; } cell = malloc( sizeof( hb_csv_cell_t ) ); if( cell == NULL ) { return cell; } cell->cell_row = file->curr_row; cell->cell_col = file->curr_col; index = 0; while( CSV_CHAR_EOF != (c = hb_parse_character( file ) ) ) { if( c == CSV_CHAR_ROWSEP ) { file->curr_row++; file->curr_col = 0; break; } else if( c == CSV_CHAR_COLSEP ) { file->curr_col++; break; } else { if( index < 1023 ) { cell->cell_text[index] = (char)c; index++; } } } if( c == CSV_CHAR_EOF ) { file->eof = 1; } /* Terminate the cell text */ cell->cell_text[index] = '\0'; hb_trim_end( cell->cell_text ); return cell; } void hb_dispose_cell( hb_csv_cell_t *cell ) { if( cell == NULL ) { return; } free( cell ); } /* Raw parsing */ static uint16_t hb_parse_character( hb_csv_file_t * file ) { int byte; uint16_t c = 0; int need_char = 1; if( file == NULL ) { return CSV_CHAR_ERROR; } while( need_char ) { byte = fgetc( file->fileref ); if( feof( file->fileref ) ) { return CSV_CHAR_EOF; } if( ferror( file->fileref ) ) { return CSV_CHAR_ERROR; } if( file->parse_state == CSV_PARSE_SEEK && is_white(byte) ) { continue; } else if( file->parse_state != CSV_PARSE_ESC && is_esc(byte) ) { file->parse_state = CSV_PARSE_ESC; continue; } else if( file->parse_state != CSV_PARSE_ESC && is_sep(byte) ) { file->parse_state = CSV_PARSE_SEEK; need_char = 0; c = CSV_CHAR_COLSEP; } else if( file->parse_state == CSV_PARSE_ESC ) { file->parse_state = CSV_PARSE_NORMAL; need_char = 0; c = (uint16_t)byte; } else if( is_newline(byte) ) { file->parse_state = CSV_PARSE_SEEK; need_char = 0; c = CSV_CHAR_ROWSEP; } else { file->parse_state = CSV_PARSE_NORMAL; need_char = 0; c = (uint16_t)byte; } } return c; } static void hb_trim_end( char *text ) { if( text == NULL ) { return; } int i = strlen(text) - 1; for( i = strlen(text) - 1; i >= 0 && is_white(text[i]) ; i-- ) { text[i] = '\0'; } } HandBrake-0.10.2/test/fakexcode.cpp0000664000175200017520000000004111026035545017447 0ustar handbrakehandbrake/* * Force Xcode to use g++ */ HandBrake-0.10.2/test/module.defs0000664000175200017520000000346312417602031017150 0ustar handbrakehandbrake$(eval $(call import.MODULE.defs,TEST,test,LIBHB)) $(eval $(call import.GCC,TEST)) TEST.src/ = $(SRC/)test/ TEST.build/ = $(BUILD/)test/ TEST.c = $(wildcard $(TEST.src/)*.c) TEST.c.o = $(patsubst $(SRC/)%.c,$(BUILD/)%.o,$(TEST.c)) TEST.exe = $(BUILD/)$(call TARGET.exe,$(HB.name)CLI) TEST.GCC.L = $(CONTRIB.build/)lib TEST.libs = $(LIBHB.a) TEST.GCC.l = \ ass avcodec avformat avutil avresample dvdnav dvdread \ fontconfig fribidi mp3lame ogg \ samplerate swscale vpx theoraenc theoradec vorbis vorbisenc x264 \ bluray freetype xml2 bz2 z ifeq (1,$(FEATURE.qsv)) TEST.GCC.D += USE_QSV HAVE_THREADS=1 endif ifeq (1,$(FEATURE.x265)) TEST.GCC.D += USE_X265 endif TEST.GCC.l += $(foreach m,$(MODULES.NAMES),$($m.OSL.libs)) TEST.install.exe = $(DESTDIR)$(PREFIX/)bin/$(notdir $(TEST.exe)) ############################################################################### TEST.out += $(TEST.c.o) TEST.out += $(TEST.exe) BUILD.out += $(TEST.out) BUILD.out += $(TEST.install.exe) ############################################################################### TEST.GCC.I += $(LIBHB.GCC.I) ifeq ($(BUILD.system),darwin) TEST.GCC.f += IOKit CoreServices AudioToolbox TEST.GCC.l += iconv else ifeq ($(BUILD.system),linux) TEST.GCC.l += pthread dl m else ifeq ($(BUILD.system),kfreebsd) TEST.GCC.l += pthread dl m else ifeq ($(BUILD.system),solaris) TEST.GCC.l += pthread nsl socket iconv TEST.GCC.D += _POSIX_C_SOURCE=200112L __EXTENSIONS__ else ifeq (1-mingw,$(BUILD.cross)-$(BUILD.system)) ifeq ($(HAS.dlfcn),1) TEST.GCC.l += dl endif ifeq (1,$(FEATURE.hwd)) TEST.GCC.D += USE_HWD endif TEST.GCC.l += pthreadGC2 iconv ws2_32 regex TEST.GCC.D += PTW32_STATIC_LIB TEST.GCC.args.extra.exe++ += -static endif # (1-mingw,$(BUILD.cross)-$(BUILD.system)) HandBrake-0.10.2/test/module.rules0000664000175200017520000000167311360657735017403 0ustar handbrakehandbrake$(eval $(call import.MODULE.rules,TEST)) test.build: $(TEST.exe) $(TEST.exe): | $(dir $(TEST.exe)) $(TEST.exe): $(TEST.c.o) $(call TEST.GCC.EXE++,$@,$^ $(TEST.libs)) $(TEST.c.o): $(LIBHB.a) $(TEST.c.o): | $(dir $(TEST.c.o)) $(TEST.c.o): $(BUILD/)%.o: $(SRC/)%.c $(call TEST.GCC.C_O,$@,$<) test.clean: $(RM.exe) -f $(TEST.out) ############################################################################### build: test.build clean: test.clean ############################################################################### ## skip install/uninstall on darwin ifneq ($(BUILD.system),darwin) test.install-strip: | $(dir $(TEST.install.exe)) $(CP.exe) $(TEST.exe) $(TEST.install.exe) $(STRIP.exe) $(TEST.install.exe) test.install: | $(dir $(TEST.install.exe)) $(CP.exe) $(TEST.exe) $(TEST.install.exe) test.uninstall: $(RM.exe) -f $(TEST.install.exe) install-strip: test.install-strip install: test.install uninstall: test.uninstall endif HandBrake-0.10.2/test/test.c0000664000175200017520000053435112463330511016152 0ustar handbrakehandbrake/* test.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include #include #include #include #include #include #include #include #if defined( __MINGW32__ ) #include #include #endif #if defined( PTW32_STATIC_LIB ) #include #endif #include "hb.h" #include "lang.h" #include "parsecsv.h" #include "openclwrapper.h" #ifdef USE_QSV #include "qsv_common.h" #endif #if defined( __APPLE_CC__ ) #import #include #include #include #endif /* Options */ static int debug = HB_DEBUG_ALL; static int update = 0; static int dvdnav = 1; static char * input = NULL; static char * output = NULL; static char * format = NULL; static int titleindex = 1; static int titlescan = 0; static int main_feature = 0; static char * native_language = NULL; static int native_dub = 0; static int twoPass = 0; static int deinterlace = 0; static char * deinterlace_opt = 0; static int deblock = 0; static char * deblock_opt = 0; static int denoise = 0; static char * denoise_opt = 0; static int nlmeans = 0; static char * nlmeans_opt = NULL; static char * nlmeans_tune_opt = NULL; static int detelecine = 0; static char * detelecine_opt = 0; static int decomb = 0; static char * decomb_opt = 0; static int rotate = 0; static char * rotate_opt = 0; static int rotate_val = 0; static int grayscale = 0; static int vcodec = HB_VCODEC_FFMPEG_MPEG4; static hb_list_t * audios = NULL; static hb_audio_config_t * audio = NULL; static int num_audio_tracks = 0; 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; static char ** abitrates = NULL; static char ** aqualities = NULL; static char ** acompressions = NULL; static char * acodec_fallback = NULL; static char * acodecs = NULL; static char ** anames = NULL; static int audio_explicit = 0; static char ** subtracks = NULL; static char ** subforce = NULL; static char * subburn = NULL; static char * subdefault = NULL; static char ** srtfile = NULL; static char ** srtcodeset = NULL; static char ** srtoffset = NULL; static char ** srtlang = NULL; static int srtdefault = -1; static int srtburn = -1; static int subtitle_scan = 0; static int width = 0; static int height = 0; static int crop[4] = { -1,-1,-1,-1 }; static int loose_crop = -1; static int vrate = 0; static float vquality = -1.0; static int vbitrate = 0; static int mux = 0; static int anamorphic_mode = 0; static int modulus = 0; static int par_height = 0; static int par_width = 0; static int display_width = 0; static int keep_display_aspect = 0; static int itu_par = 0; static int angle = 0; static int chapter_start = 0; static int chapter_end = 0; static int chapter_markers = 0; static char * marker_file = NULL; static char * x264_preset = NULL; static char * x264_tune = NULL; static char * advanced_opts = NULL; static char * h264_profile = NULL; static char * h264_level = NULL; static int maxHeight = 0; static int maxWidth = 0; static int turbo_opts_enabled = 0; static int largeFileSize = 0; static int preset = 0; static char * preset_name = 0; static int cfr = 0; static int mp4_optimize = 0; static int ipod_atom = 0; static int color_matrix_code = 0; static int preview_count = 10; static int store_previews = 0; static int start_at_preview = 0; static int64_t start_at_pts = 0; static int start_at_frame = 0; static int64_t stop_at_pts = 0; static int stop_at_frame = 0; static uint64_t min_title_duration = 10; static int use_opencl = 0; static int use_hwd = 0; #ifdef USE_QSV static int qsv_async_depth = -1; static int qsv_decode = 1; #endif /* Exit cleanly on Ctrl-C */ static volatile hb_error_code done_error = HB_ERROR_NONE; static volatile int die = 0; static void SigHandler( int ); /* Utils */ static void ShowHelp(); static void ShowPresets(); static void ShowCommands() { fprintf(stdout, "\nCommands:\n"); fprintf(stdout, " [h]elp Show this message\n"); fprintf(stdout, " [q]uit Exit HandBrakeCLI\n"); fprintf(stdout, " [p]ause Pause encoding\n"); fprintf(stdout, " [r]esume Resume encoding\n"); } static int ParseOptions( int argc, char ** argv ); static int CheckOptions( int argc, char ** argv ); static int HandleEvents( hb_handle_t * h ); static void str_vfree( char **strv ); static char** str_split( char *str, char delem ); static void print_string_list(FILE *out, const char* const *list, const char *prefix); #ifdef __APPLE_CC__ static char* bsd_name_for_path(char *path); static int device_is_dvd(char *device); static io_service_t get_iokit_service( char *device ); static int is_dvd_service( io_service_t service ); static int is_whole_media_service( io_service_t service ); #endif /* Only print the "Muxing..." message once */ static int show_mux_warning = 1; /**************************************************************************** * hb_error_handler * * When using the CLI just display using hb_log as we always did in the past * make sure that we prefix with a nice ERROR message to catch peoples eyes. ****************************************************************************/ static void hb_cli_error_handler ( const char *errmsg ) { fprintf( stderr, "ERROR: %s\n", errmsg ); } static int get_argv_utf8(int *argc_ptr, char ***argv_ptr) { #if defined( __MINGW32__ ) int ret = 0; int argc; char **argv; wchar_t **argv_utf16 = CommandLineToArgvW(GetCommandLineW(), &argc); if (argv_utf16) { int i; int offset = (argc+1) * sizeof(char*); int size = offset; for(i = 0; i < argc; i++) size += WideCharToMultiByte(CP_UTF8, 0, argv_utf16[i], -1, NULL, 0, NULL, NULL ); argv = malloc(size); if (argv) { for (i = 0; i < argc; i++) { argv[i] = (char*)argv + offset; offset += WideCharToMultiByte(CP_UTF8, 0, argv_utf16[i], -1, argv[i], size-offset, NULL, NULL); } argv[argc] = NULL; ret = 1; } LocalFree(argv_utf16); } if (ret) { *argc_ptr = argc; *argv_ptr = argv; } return ret; #else // On other systems, assume command line is already utf8 return 1; #endif } int main( int argc, char ** argv ) { hb_handle_t * h; int build; char * version; hb_global_init(); audios = hb_list_init(); // Get utf8 command line if windows get_argv_utf8(&argc, &argv); /* Parse command line */ if( ParseOptions( argc, argv ) || CheckOptions( argc, argv ) ) { return 1; } /* Register our error handler */ hb_register_error_handler(&hb_cli_error_handler); /* Init libhb */ h = hb_init( debug, update ); hb_dvd_set_dvdnav( dvdnav ); /* Show version */ fprintf( stderr, "%s - %s - %s\n", HB_PROJECT_TITLE, HB_PROJECT_BUILD_TITLE, HB_PROJECT_URL_WEBSITE ); /* Check for update */ if( update ) { if( ( build = hb_check_update( h, &version ) ) > -1 ) { fprintf( stderr, "You are using an old version of " "HandBrake.\nLatest is %s (build %d).\n", version, build ); } else { fprintf( stderr, "Your version of HandBrake is up to " "date.\n" ); } hb_close( &h ); hb_global_close(); return 0; } /* Geeky */ fprintf( stderr, "%d CPU%s detected\n", hb_get_cpu_count(), hb_get_cpu_count( h ) > 1 ? "s" : "" ); /* Exit ASAP on Ctrl-C */ signal( SIGINT, SigHandler ); /* Feed libhb with a DVD to scan */ fprintf( stderr, "Opening %s...\n", input ); if (main_feature) { /* * We need to scan for all the titles in order to find the main feature */ titleindex = 0; } hb_system_sleep_prevent(h); hb_gui_use_hwd_flag = use_hwd; hb_scan( h, input, titleindex, preview_count, store_previews, min_title_duration * 90000LL ); /* Wait... */ while( !die ) { #if defined( __MINGW32__ ) if( _kbhit() ) { switch( _getch() ) { case 0x03: /* ctrl-c */ case 'q': fprintf( stdout, "\nEncoding Quit by user command\n" ); done_error = HB_ERROR_CANCELED; die = 1; break; case 'p': fprintf(stdout, "\nEncoding Paused by user command, 'r' to resume\n"); hb_pause(h); hb_system_sleep_allow(h); break; case 'r': hb_system_sleep_prevent(h); hb_resume(h); break; case 'h': ShowCommands(); break; } } hb_snooze( 200 ); #elif !defined(SYS_BEOS) fd_set fds; struct timeval tv; int ret; char buf[257]; tv.tv_sec = 0; tv.tv_usec = 100000; FD_ZERO( &fds ); FD_SET( STDIN_FILENO, &fds ); ret = select( STDIN_FILENO + 1, &fds, NULL, NULL, &tv ); if( ret > 0 ) { int size = 0; while( size < 256 && read( STDIN_FILENO, &buf[size], 1 ) > 0 ) { if( buf[size] == '\n' ) { break; } size++; } if( size >= 256 || buf[size] == '\n' ) { switch( buf[0] ) { case 'q': fprintf( stdout, "\nEncoding Quit by user command\n" ); done_error = HB_ERROR_CANCELED; die = 1; break; case 'p': fprintf(stdout, "\nEncoding Paused by user command, 'r' to resume\n"); hb_pause(h); hb_system_sleep_allow(h); break; case 'r': hb_system_sleep_prevent(h); hb_resume(h); break; case 'h': ShowCommands(); break; } } } hb_snooze( 200 ); #else hb_snooze( 200 ); #endif HandleEvents( h ); } /* Clean up */ hb_close(&h); hb_global_close(); if (audios != NULL) { while ((audio = hb_list_item(audios, 0)) != NULL) { hb_list_rem(audios, audio); if (audio->out.name != NULL) { free(audio->out.name); } free(audio); } hb_list_close(&audios); } str_vfree(abitrates); str_vfree(acompressions); str_vfree(aqualities); str_vfree(audio_dither); free(acodecs); free(arates); free(atracks); free(audio_gain); free(dynamic_range_compression); free(mixdowns); free(native_language); free(format); free(input); free(output); free(preset_name); free(x264_preset); free(x264_tune); free(advanced_opts); free(h264_profile); free(h264_level); free(nlmeans_opt); free(nlmeans_tune_opt); // write a carriage return to stdout // avoids overlap / line wrapping when stderr is redirected fprintf(stdout, "\n"); fprintf(stderr, "HandBrake has exited.\n"); return done_error; } static void PrintTitleInfo( hb_title_t * title, int feature ) { hb_chapter_t * chapter; hb_subtitle_t * subtitle; int i; fprintf( stderr, "+ title %d:\n", title->index ); if ( title->index == feature ) { fprintf( stderr, " + Main Feature\n" ); } if ( title->type == HB_STREAM_TYPE || title->type == HB_FF_STREAM_TYPE ) { fprintf( stderr, " + stream: %s\n", title->path ); } else if ( title->type == HB_DVD_TYPE ) { fprintf( stderr, " + vts %d, ttn %d, cells %d->%d (%"PRIu64" blocks)\n", title->vts, title->ttn, title->cell_start, title->cell_end, title->block_count ); } else if( title->type == HB_BD_TYPE ) { fprintf( stderr, " + playlist: %05d.MPLS\n", title->playlist ); } if (title->angle_count > 1) fprintf( stderr, " + angle(s) %d\n", title->angle_count ); fprintf( stderr, " + duration: %02d:%02d:%02d\n", title->hours, title->minutes, title->seconds ); fprintf( stderr, " + size: %dx%d, pixel aspect: %d/%d, display aspect: %.2f, %.3f fps\n", title->width, title->height, title->pixel_aspect_width, title->pixel_aspect_height, (float) title->aspect, (float) title->rate / title->rate_base ); fprintf( stderr, " + autocrop: %d/%d/%d/%d\n", title->crop[0], title->crop[1], title->crop[2], title->crop[3] ); fprintf(stderr, " + support opencl: %s\n", title->opencl_support ? "yes" : "no"); #ifdef USE_HWD fprintf(stderr, " + support hwd: %s\n", title->hwd_support ? "yes" : "no"); #else fprintf(stderr, " + support hwd: not built-in\n"); #endif fprintf( stderr, " + chapters:\n" ); for( i = 0; i < hb_list_count( title->list_chapter ); i++ ) { chapter = hb_list_item( title->list_chapter, i ); fprintf( stderr, " + %d: cells %d->%d, %"PRIu64" blocks, duration " "%02d:%02d:%02d\n", chapter->index, chapter->cell_start, chapter->cell_end, chapter->block_count, chapter->hours, chapter->minutes, chapter->seconds ); } fprintf( stderr, " + audio tracks:\n" ); for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { audio = hb_list_audio_config_item( title->list_audio, i ); if( ( audio->in.codec == HB_ACODEC_AC3 ) || ( audio->in.codec == HB_ACODEC_DCA) ) { fprintf( stderr, " + %d, %s (iso639-2: %s), %dHz, %dbps\n", i + 1, audio->lang.description, audio->lang.iso639_2, audio->in.samplerate, audio->in.bitrate ); } else { fprintf( stderr, " + %d, %s (iso639-2: %s)\n", i + 1, audio->lang.description, audio->lang.iso639_2 ); } } fprintf( stderr, " + subtitle tracks:\n" ); for( i = 0; i < hb_list_count( title->list_subtitle ); i++ ) { subtitle = hb_list_item( title->list_subtitle, i ); fprintf( stderr, " + %d, %s (iso639-2: %s) (%s)(%s)\n", i + 1, subtitle->lang, subtitle->iso639_2, (subtitle->format == TEXTSUB) ? "Text" : "Bitmap", hb_subsource_name(subtitle->source)); } if(title->detected_interlacing) { /* Interlacing was found in half or more of the preview frames */ fprintf( stderr, " + combing detected, may be interlaced or telecined\n"); } } static void PrintTitleSetInfo( hb_title_set_t * title_set ) { int i; hb_title_t * title; for( i = 0; i < hb_list_count( title_set->list_title ); i++ ) { title = hb_list_item( title_set->list_title, i ); PrintTitleInfo( title, title_set->feature ); } } static int test_sub_list( char ** list, int pos ) { int i; if ( list == NULL || pos == 0 ) return 0; if ( list[0] == NULL && pos == 1 ) return 1; for ( i = 0; list[i] != NULL; i++ ) { int idx = strtol( list[i], NULL, 0 ); if ( idx == pos ) return 1; } return 0; } static int cmp_lang( char * lang, const char * code ) { iso639_lang_t * iso639; iso639 = lang_for_code2( code ); if ( iso639 == NULL ) return 0; if ( iso639->eng_name && !strcasecmp( lang, iso639->eng_name ) ) return 1; if ( iso639->native_name && !strcasecmp( lang, iso639->native_name ) ) return 1; if ( iso639->iso639_1 && !strcasecmp( lang, iso639->iso639_1 ) ) return 1; if ( iso639->iso639_2 && !strcasecmp( lang, iso639->iso639_2 ) ) return 1; if ( iso639->iso639_2b && !strcasecmp( lang, iso639->iso639_2b ) ) return 1; return 0; } static void apply_loose_crop(int total, int * v1, int * v2, int mod, int max) { /* number of extra pixels which must be cropped to reach next modulus */ int add = (total - *v1 - *v2) % mod; if (add) { /* number of pixels which must be uncropped to reach previous modulus */ int sub = mod - add; /* less than maximum (or can't reduce), increase the crop size */ if (add <= max || sub > (*v1 + *v2)) { int add1 = add / 2; if ((*v1 + add1) & 1) // avoid odd crop if possible ++add1; int add2 = (add - add1); *v1 += add1; *v2 += add2; } /* more than maximum, reduce the crop size instead */ else { int sub1 = sub / 2; if (sub1 > *v1) sub1 = *v1; else if ((*v1 - sub1) & 1) // avoid odd crop if possible ++sub1; int sub2 = sub - sub1; if (sub2 > *v2) { sub1 += (sub2 - *v2); if ((*v1 - sub1) & 1) // avoid odd crop if possible ++sub1; sub2 = sub - sub1; } *v1 -= sub1; *v2 -= sub2; } } } static int HandleEvents( hb_handle_t * h ) { hb_state_t s; const hb_encoder_t *encoder; int tmp_num_audio_tracks; int filter_cfr, filter_vrate, filter_vrate_base; hb_get_state( h, &s ); switch( s.state ) { case HB_STATE_IDLE: /* Nothing to do */ break; #define p s.param.scanning case HB_STATE_SCANNING: /* Show what title is currently being scanned */ if (p.preview_cur) { fprintf(stderr, "\rScanning title %d of %d, preview %d, %.2f %%", p.title_cur, p.title_count, p.preview_cur, 100 * p.progress); } else { fprintf(stderr, "\rScanning title %d of %d, %.2f %%", p.title_cur, p.title_count, 100 * p.progress); } fflush(stderr); break; #undef p case HB_STATE_SCANDONE: { hb_title_set_t * title_set; hb_title_t * title; hb_job_t * job; int i; int sub_burned = 0; /* Audio argument string parsing variables */ int acodec = 0; int abitrate = 0; float aquality = 0; float acompression = 0; int arate = 0; int mixdown = HB_AMIXDOWN_DOLBYPLII; double d_r_c = 0; double gain = 0; /* Audio argument string parsing variables */ title_set = hb_get_title_set( h ); if( !title_set || !hb_list_count( title_set->list_title ) ) { /* No valid title, stop right there */ fprintf( stderr, "No title found.\n" ); done_error = HB_ERROR_WRONG_INPUT; die = 1; break; } if( main_feature ) { int i; int main_feature_idx=0; int main_feature_pos=-1; int main_feature_time=0; int title_time; fprintf( stderr, "Searching for main feature title...\n" ); for( i = 0; i < hb_list_count( title_set->list_title ); i++ ) { title = hb_list_item( title_set->list_title, i ); title_time = (title->hours*60*60 ) + (title->minutes *60) + (title->seconds); fprintf( stderr, " + Title (%d) index %d has length %dsec\n", i, title->index, title_time ); if( main_feature_time < title_time ) { main_feature_time = title_time; main_feature_pos = i; main_feature_idx = title->index; } if( title_set->feature == title->index ) { main_feature_time = title_time; main_feature_pos = i; main_feature_idx = title->index; break; } } if( main_feature_pos == -1 ) { fprintf( stderr, "No main feature title found.\n" ); done_error = HB_ERROR_WRONG_INPUT; die = 1; break; } titleindex = main_feature_idx; fprintf( stderr, "Found main feature title, setting title to %d\n", main_feature_idx); title = hb_list_item( title_set->list_title, main_feature_pos); } else { title = hb_list_item( title_set->list_title, 0 ); } if( !titleindex || titlescan ) { /* Scan-only mode, print infos and exit */ PrintTitleSetInfo( title_set ); die = 1; break; } PrintTitleInfo( title, title_set->feature ); /* Set job settings */ job = hb_job_init(title); filter_cfr = job->cfr; filter_vrate = job->vrate; filter_vrate_base = job->vrate_base; if( chapter_start && chapter_end && !stop_at_pts && !start_at_preview && !stop_at_frame && !start_at_pts && !start_at_frame ) { job->chapter_start = MAX( job->chapter_start, chapter_start ); job->chapter_end = MIN( job->chapter_end, chapter_end ); job->chapter_end = MAX( job->chapter_start, job->chapter_end ); } if ( angle ) { job->angle = angle; } if (preset) { fprintf( stderr, "+ Using preset: %s\n", preset_name); if (!strcasecmp(preset_name, "Universal")) { if( !mux ) { mux = HB_MUX_MP4; } vcodec = HB_VCODEC_X264; job->vquality = 20.0; filter_vrate_base = 900000; filter_cfr = 2; if( !atracks ) { atracks = strdup("1,1"); } if( !acodecs ) { acodecs = strdup("ffaac,copy:ac3"); } if( !abitrates ) { abitrates = str_split("160,160", ','); } if( !mixdowns ) { mixdowns = strdup("dpl2,none"); } if( !arates ) { arates = strdup("Auto,Auto"); } if( !dynamic_range_compression ) { dynamic_range_compression = strdup("0.0,0.0"); } if( allowed_audio_copy == -1 ) { allowed_audio_copy = 0; allowed_audio_copy |= HB_ACODEC_AAC_PASS; allowed_audio_copy |= HB_ACODEC_AC3_PASS; allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; allowed_audio_copy |= HB_ACODEC_DCA_PASS; allowed_audio_copy |= HB_ACODEC_MP3_PASS; allowed_audio_copy &= HB_ACODEC_PASS_MASK; } if( acodec_fallback == NULL ) { acodec_fallback = "ffac3"; } maxWidth = 720; maxHeight = 576; if (x264_preset == NULL) { x264_preset = strdup("fast"); } if (h264_profile == NULL) { h264_profile = strdup("baseline"); } if (h264_level == NULL) { h264_level = strdup("3.0"); } if( !anamorphic_mode ) { anamorphic_mode = 2; } modulus = 2; job->chapter_markers = 1; } if (!strcasecmp(preset_name, "iPod")) { if( !mux ) { mux = HB_MUX_MP4; } job->ipod_atom = 1; vcodec = HB_VCODEC_X264; job->vquality = 22.0; filter_vrate_base = 900000; filter_cfr = 2; if( !atracks ) { atracks = strdup("1"); } if( !acodecs ) { acodecs = strdup("ffaac"); } if( !abitrates ) { abitrates = str_split("160", ','); } if( !mixdowns ) { mixdowns = strdup("dpl2"); } if( !arates ) { arates = strdup("Auto"); } if( !dynamic_range_compression ) { dynamic_range_compression = strdup("0.0"); } if( allowed_audio_copy == -1 ) { allowed_audio_copy = 0; allowed_audio_copy |= HB_ACODEC_AAC_PASS; allowed_audio_copy |= HB_ACODEC_AC3_PASS; allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; allowed_audio_copy |= HB_ACODEC_DCA_PASS; allowed_audio_copy |= HB_ACODEC_MP3_PASS; allowed_audio_copy &= HB_ACODEC_PASS_MASK; } if( acodec_fallback == NULL ) { acodec_fallback = "ffac3"; } maxWidth = 320; maxHeight = 240; if (x264_preset == NULL) { x264_preset = strdup("medium"); } if (h264_profile == NULL) { h264_profile = strdup("baseline"); } if (h264_level == NULL) { h264_level = strdup("1.3"); } modulus = 2; job->chapter_markers = 1; } if (!strcasecmp(preset_name, "iPhone & iPod touch")) { if( !mux ) { mux = HB_MUX_MP4; } job->largeFileSize = 1; vcodec = HB_VCODEC_X264; job->vquality = 22.0; filter_vrate_base = 900000; filter_cfr = 2; if( !atracks ) { atracks = strdup("1"); } if( !acodecs ) { acodecs = strdup("ffaac"); } if( !abitrates ) { abitrates = str_split("160", ','); } if( !mixdowns ) { mixdowns = strdup("dpl2"); } if( !arates ) { arates = strdup("Auto"); } if( !dynamic_range_compression ) { dynamic_range_compression = strdup("0.0"); } if( allowed_audio_copy == -1 ) { allowed_audio_copy = 0; allowed_audio_copy |= HB_ACODEC_AAC_PASS; allowed_audio_copy |= HB_ACODEC_AC3_PASS; allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; allowed_audio_copy |= HB_ACODEC_DCA_PASS; allowed_audio_copy |= HB_ACODEC_MP3_PASS; allowed_audio_copy &= HB_ACODEC_PASS_MASK; } if( acodec_fallback == NULL ) { acodec_fallback = "ffac3"; } maxWidth = 960; maxHeight = 640; if (x264_preset == NULL) { x264_preset = strdup("medium"); } if (h264_profile == NULL) { h264_profile = strdup("high"); } if (h264_level == NULL) { h264_level = strdup("3.1"); } if( !anamorphic_mode ) { anamorphic_mode = 2; } modulus = 2; job->chapter_markers = 1; } if (!strcasecmp(preset_name, "iPad")) { if( !mux ) { mux = HB_MUX_MP4; } job->largeFileSize = 1; vcodec = HB_VCODEC_X264; job->vquality = 20.0; filter_vrate_base = 900000; filter_cfr = 2; if( !atracks ) { atracks = strdup("1"); } if( !acodecs ) { acodecs = strdup("ffaac"); } if( !abitrates ) { abitrates = str_split("160", ','); } if( !mixdowns ) { mixdowns = strdup("dpl2"); } if( !arates ) { arates = strdup("Auto"); } if( !dynamic_range_compression ) { dynamic_range_compression = strdup("0.0"); } if( allowed_audio_copy == -1 ) { allowed_audio_copy = 0; allowed_audio_copy |= HB_ACODEC_AAC_PASS; allowed_audio_copy |= HB_ACODEC_AC3_PASS; allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; allowed_audio_copy |= HB_ACODEC_DCA_PASS; allowed_audio_copy |= HB_ACODEC_MP3_PASS; allowed_audio_copy &= HB_ACODEC_PASS_MASK; } if( acodec_fallback == NULL ) { acodec_fallback = "ffac3"; } maxWidth = 1280; maxHeight = 720; if (x264_preset == NULL) { x264_preset = strdup("medium"); } if (h264_profile == NULL) { h264_profile = strdup("high"); } if (h264_level == NULL) { h264_level = strdup("3.1"); } if( !anamorphic_mode ) { anamorphic_mode = 2; } modulus = 2; job->chapter_markers = 1; } if (!strcasecmp(preset_name, "AppleTV")) { if( !mux ) { mux = HB_MUX_MP4; } job->largeFileSize = 1; vcodec = HB_VCODEC_X264; job->vquality = 20.0; filter_vrate_base = 900000; filter_cfr = 2; if( !atracks ) { atracks = strdup("1,1"); } if( !acodecs ) { acodecs = strdup("ffaac,copy:ac3"); } if( !abitrates ) { abitrates = str_split("160,160", ','); } if( !mixdowns ) { mixdowns = strdup("dpl2,none"); } if( !arates ) { arates = strdup("Auto,Auto"); } if( !dynamic_range_compression ) { dynamic_range_compression = strdup("0.0,0.0"); } if( allowed_audio_copy == -1 ) { allowed_audio_copy = 0; allowed_audio_copy |= HB_ACODEC_AAC_PASS; allowed_audio_copy |= HB_ACODEC_AC3_PASS; allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; allowed_audio_copy |= HB_ACODEC_DCA_PASS; allowed_audio_copy |= HB_ACODEC_MP3_PASS; allowed_audio_copy &= HB_ACODEC_PASS_MASK; } if( acodec_fallback == NULL ) { acodec_fallback = "ffac3"; } maxWidth = 960; maxHeight = 720; if (x264_preset == NULL) { x264_preset = strdup("medium"); } if (h264_profile == NULL) { h264_profile = strdup("high"); } if (h264_level == NULL) { h264_level = strdup("3.1"); } if (advanced_opts == NULL) { advanced_opts = strdup("qpmin=4:cabac=0:ref=2:b-pyramid=none:weightb=0:weightp=0:vbv-maxrate=9500:vbv-bufsize=9500"); } if( !anamorphic_mode ) { anamorphic_mode = 2; } modulus = 2; job->chapter_markers = 1; } if (!strcasecmp(preset_name, "AppleTV 2")) { if( !mux ) { mux = HB_MUX_MP4; } job->largeFileSize = 1; vcodec = HB_VCODEC_X264; job->vquality = 20.0; filter_vrate_base = 900000; filter_cfr = 2; if( !atracks ) { atracks = strdup("1,1"); } if( !acodecs ) { acodecs = strdup("ffaac,copy:ac3"); } if( !abitrates ) { abitrates = str_split("160,160", ','); } if( !mixdowns ) { mixdowns = strdup("dpl2,none"); } if( !arates ) { arates = strdup("Auto,Auto"); } if( !dynamic_range_compression ) { dynamic_range_compression = strdup("0.0,0.0"); } if( allowed_audio_copy == -1 ) { allowed_audio_copy = 0; allowed_audio_copy |= HB_ACODEC_AAC_PASS; allowed_audio_copy |= HB_ACODEC_AC3_PASS; allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; allowed_audio_copy |= HB_ACODEC_DCA_PASS; allowed_audio_copy |= HB_ACODEC_MP3_PASS; allowed_audio_copy &= HB_ACODEC_PASS_MASK; } if( acodec_fallback == NULL ) { acodec_fallback = "ffac3"; } maxWidth = 1280; maxHeight = 720; if (x264_preset == NULL) { x264_preset = strdup("medium"); } if (h264_profile == NULL) { h264_profile = strdup("high"); } if (h264_level == NULL) { h264_level = strdup("3.1"); } if( !anamorphic_mode ) { anamorphic_mode = 2; } modulus = 2; job->chapter_markers = 1; } if (!strcasecmp(preset_name, "AppleTV 3")) { if( !mux ) { mux = HB_MUX_MP4; } job->largeFileSize = 1; vcodec = HB_VCODEC_X264; job->vquality = 20.0; filter_vrate_base = 900000; filter_cfr = 2; if( !atracks ) { atracks = strdup("1,1"); } if( !acodecs ) { acodecs = strdup("ffaac,copy:ac3"); } if( !abitrates ) { abitrates = str_split("160,160", ','); } if( !mixdowns ) { mixdowns = strdup("dpl2,none"); } if( !arates ) { arates = strdup("Auto,Auto"); } if( !dynamic_range_compression ) { dynamic_range_compression = strdup("0.0,0.0"); } if( allowed_audio_copy == -1 ) { allowed_audio_copy = 0; allowed_audio_copy |= HB_ACODEC_AAC_PASS; allowed_audio_copy |= HB_ACODEC_AC3_PASS; allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; allowed_audio_copy |= HB_ACODEC_DCA_PASS; allowed_audio_copy |= HB_ACODEC_MP3_PASS; allowed_audio_copy &= HB_ACODEC_PASS_MASK; } if( acodec_fallback == NULL ) { acodec_fallback = "ffac3"; } maxWidth = 1920; maxHeight = 1080; if (x264_preset == NULL) { x264_preset = strdup("medium"); } if (h264_profile == NULL) { h264_profile = strdup("high"); } if (h264_level == NULL) { h264_level = strdup("4.0"); } decomb = 1; decomb_opt = "7:2:6:9:1:80"; if( !anamorphic_mode ) { anamorphic_mode = 2; } modulus = 2; job->chapter_markers = 1; } if (!strcasecmp(preset_name, "Android")) { if( !mux ) { mux = HB_MUX_MP4; } vcodec = HB_VCODEC_X264; job->vquality = 22.0; filter_vrate_base = 900000; filter_cfr = 2; if( !atracks ) { atracks = strdup("1"); } if( !acodecs ) { acodecs = strdup("ffaac"); } if( !abitrates ) { abitrates = str_split("128", ','); } if( !mixdowns ) { mixdowns = strdup("dpl2"); } if( !arates ) { arates = strdup("Auto"); } if( !dynamic_range_compression ) { dynamic_range_compression = strdup("0.0"); } if( allowed_audio_copy == -1 ) { allowed_audio_copy = 0; allowed_audio_copy |= HB_ACODEC_AAC_PASS; allowed_audio_copy |= HB_ACODEC_AC3_PASS; allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; allowed_audio_copy |= HB_ACODEC_DCA_PASS; allowed_audio_copy |= HB_ACODEC_MP3_PASS; allowed_audio_copy &= HB_ACODEC_PASS_MASK; } if( acodec_fallback == NULL ) { acodec_fallback = "ffac3"; } maxWidth = 720; maxHeight = 576; if (x264_preset == NULL) { x264_preset = strdup("medium"); } if (h264_profile == NULL) { h264_profile = strdup("main"); } if (h264_level == NULL) { h264_level = strdup("3.0"); } if( !anamorphic_mode ) { anamorphic_mode = 2; } modulus = 2; } if (!strcasecmp(preset_name, "Android Tablet")) { if( !mux ) { mux = HB_MUX_MP4; } vcodec = HB_VCODEC_X264; job->vquality = 22.0; filter_vrate_base = 900000; filter_cfr = 2; if( !atracks ) { atracks = strdup("1"); } if( !acodecs ) { acodecs = strdup("ffaac"); } if( !abitrates ) { abitrates = str_split("128", ','); } if( !mixdowns ) { mixdowns = strdup("dpl2"); } if( !arates ) { arates = strdup("Auto"); } if( !dynamic_range_compression ) { dynamic_range_compression = strdup("0.0"); } if( allowed_audio_copy == -1 ) { allowed_audio_copy = 0; allowed_audio_copy |= HB_ACODEC_AAC_PASS; allowed_audio_copy |= HB_ACODEC_AC3_PASS; allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; allowed_audio_copy |= HB_ACODEC_DCA_PASS; allowed_audio_copy |= HB_ACODEC_MP3_PASS; allowed_audio_copy &= HB_ACODEC_PASS_MASK; } if( acodec_fallback == NULL ) { acodec_fallback = "ffac3"; } maxWidth = 1280; maxHeight = 720; if (x264_preset == NULL) { x264_preset = strdup("medium"); } if (h264_profile == NULL) { h264_profile = strdup("main"); } if (h264_level == NULL) { h264_level = strdup("3.1"); } if( !anamorphic_mode ) { anamorphic_mode = 2; } modulus = 2; } if (!strcasecmp(preset_name, "Windows Phone 8")) { if( !mux ) { mux = HB_MUX_MP4; } vcodec = HB_VCODEC_X264; job->vquality = 22.0; filter_vrate_base = 900000; filter_cfr = 2; if( !atracks ) { atracks = strdup("1"); } if( !acodecs ) { acodecs = strdup("ffaac"); } if( !abitrates ) { abitrates = str_split("128", ','); } if( !mixdowns ) { mixdowns = strdup("dpl2"); } if( !arates ) { arates = strdup("Auto"); } if( !dynamic_range_compression ) { dynamic_range_compression = strdup("0.0"); } if( allowed_audio_copy == -1 ) { allowed_audio_copy = 0; allowed_audio_copy |= HB_ACODEC_AAC_PASS; allowed_audio_copy |= HB_ACODEC_AC3_PASS; allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; allowed_audio_copy |= HB_ACODEC_DCA_PASS; allowed_audio_copy |= HB_ACODEC_MP3_PASS; allowed_audio_copy &= HB_ACODEC_PASS_MASK; } if( acodec_fallback == NULL ) { acodec_fallback = "ffac3"; } maxWidth = 1280; maxHeight = 720; if (x264_preset == NULL) { x264_preset = strdup("medium"); } if (h264_profile == NULL) { h264_profile = strdup("main"); } if (h264_level == NULL) { h264_level = strdup("3.1"); } if( !anamorphic_mode ) { anamorphic_mode = 0; } modulus = 2; } if (!strcasecmp(preset_name, "Normal")) { if( !mux ) { mux = HB_MUX_MP4; } vcodec = HB_VCODEC_X264; job->vquality = 20.0; if( !atracks ) { atracks = strdup("1"); } if( !acodecs ) { acodecs = strdup("ffaac"); } if( !abitrates ) { abitrates = str_split("160", ','); } if( !mixdowns ) { mixdowns = strdup("dpl2"); } if( !arates ) { arates = strdup("Auto"); } if( !dynamic_range_compression ) { dynamic_range_compression = strdup("0.0"); } if( allowed_audio_copy == -1 ) { allowed_audio_copy = 0; allowed_audio_copy |= HB_ACODEC_AAC_PASS; allowed_audio_copy |= HB_ACODEC_AC3_PASS; allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; allowed_audio_copy |= HB_ACODEC_DCA_PASS; allowed_audio_copy |= HB_ACODEC_MP3_PASS; allowed_audio_copy &= HB_ACODEC_PASS_MASK; } if( acodec_fallback == NULL ) { acodec_fallback = "ffac3"; } if (x264_preset == NULL) { x264_preset = strdup("veryfast"); } if (h264_profile == NULL) { h264_profile = strdup("main"); } if (h264_level == NULL) { h264_level = strdup("4.0"); } if( !anamorphic_mode ) { anamorphic_mode = 2; } modulus = 2; job->chapter_markers = 1; } if (!strcasecmp(preset_name, "High Profile")) { if( !mux ) { mux = HB_MUX_MP4; } job->largeFileSize = 1; vcodec = HB_VCODEC_X264; job->vquality = 20.0; if( !atracks ) { atracks = strdup("1,1"); } if( !acodecs ) { acodecs = strdup("ffaac,copy:ac3"); } if( !abitrates ) { abitrates = str_split("160,160", ','); } if( !mixdowns ) { mixdowns = strdup("dpl2,none"); } if( !arates ) { arates = strdup("Auto,Auto"); } if( !dynamic_range_compression ) { dynamic_range_compression = strdup("0.0,0.0"); } if( allowed_audio_copy == -1 ) { allowed_audio_copy = 0; allowed_audio_copy |= HB_ACODEC_AAC_PASS; allowed_audio_copy |= HB_ACODEC_AC3_PASS; allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS; allowed_audio_copy |= HB_ACODEC_DCA_PASS; allowed_audio_copy |= HB_ACODEC_MP3_PASS; allowed_audio_copy &= HB_ACODEC_PASS_MASK; } if( acodec_fallback == NULL ) { acodec_fallback = "ffac3"; } if (x264_preset == NULL) { x264_preset = strdup("medium"); } if (h264_profile == NULL) { h264_profile = strdup("high"); } if (h264_level == NULL) { h264_level = strdup("4.1"); } decomb = 1; if( !anamorphic_mode ) { anamorphic_mode = 2; } modulus = 2; job->chapter_markers = 1; } } if ( chapter_markers ) { job->chapter_markers = chapter_markers; if( marker_file != NULL ) { hb_csv_file_t * file = hb_open_csv_file( marker_file ); hb_csv_cell_t * cell; int row = 0; int chapter = 0; fprintf( stderr, "Reading chapter markers from file %s\n", marker_file ); if( file == NULL ) { fprintf( stderr, "Cannot open chapter marker file, using defaults\n" ); } else { /* Parse the cells */ while( NULL != ( cell = hb_read_next_cell( file ) ) ) { /* We have a chapter number */ if( cell->cell_col == 0 ) { row = cell->cell_row; chapter = atoi( cell->cell_text ); } /* We have a chapter name */ if( cell->cell_col == 1 && row == cell->cell_row ) { /* If we have a valid chapter, copy the string an terminate it */ if( chapter >= job->chapter_start && chapter <= job->chapter_end ) { hb_chapter_t * chapter_s; chapter_s = hb_list_item( job->list_chapter, chapter - 1); hb_chapter_set_title(chapter_s, cell->cell_text); } } hb_dispose_cell( cell ); } hb_close_csv_file( file ); } } } if (crop[0] < 0 || crop[1] < 0 || crop[2] < 0 || crop[3] < 0) { memcpy(crop, title->crop, sizeof(int[4])); } if( loose_crop >= 0 ) { int mod = modulus > 0 ? modulus : 2; apply_loose_crop(title->height, &crop[0], &crop[1], mod, loose_crop); apply_loose_crop(title->width, &crop[2], &crop[3], mod, loose_crop); } job->deinterlace = deinterlace; job->grayscale = grayscale; hb_filter_object_t * filter; job->use_detelecine = detelecine; job->use_decomb = decomb; /* Add selected filters */ if( detelecine ) { filter = hb_filter_init( HB_FILTER_DETELECINE ); hb_add_filter( job, filter, detelecine_opt ); } if( decomb ) { filter = hb_filter_init( HB_FILTER_DECOMB ); hb_add_filter( job, filter, decomb_opt ); } if( deinterlace ) { filter = hb_filter_init( HB_FILTER_DEINTERLACE ); hb_add_filter( job, filter, deinterlace_opt ); } if( deblock ) { filter = hb_filter_init( HB_FILTER_DEBLOCK ); hb_add_filter( job, filter, deblock_opt ); } if( denoise ) { filter = hb_filter_init( HB_FILTER_DENOISE ); hb_add_filter( job, filter, denoise_opt ); } if( nlmeans ) { filter = hb_filter_init( HB_FILTER_NLMEANS ); hb_add_filter( job, filter, nlmeans_opt ); } if( rotate ) { filter = hb_filter_init( HB_FILTER_ROTATE ); hb_add_filter( job, filter, rotate_opt); } if (use_hwd) { job->use_hwd = use_hwd; } hb_geometry_t srcGeo, resultGeo; hb_ui_geometry_t uiGeo; srcGeo.width = title->width; srcGeo.height = title->height; srcGeo.par.num = title->pixel_aspect_width; srcGeo.par.den = title->pixel_aspect_height; keep_display_aspect |= anamorphic_mode != HB_ANAMORPHIC_CUSTOM; uiGeo.mode = job->anamorphic.mode = anamorphic_mode; if (width != 0 && height != 0) { if (anamorphic_mode == HB_ANAMORPHIC_NONE) { keep_display_aspect = 0; } else { uiGeo.mode = HB_ANAMORPHIC_CUSTOM; } } job->anamorphic.keep_display_aspect = keep_display_aspect; uiGeo.keep = !!keep_display_aspect * HB_KEEP_DISPLAY_ASPECT; uiGeo.itu_par = job->anamorphic.itu_par = itu_par; uiGeo.modulus = job->modulus = modulus; memcpy(uiGeo.crop, crop, sizeof(int[4])); if (width == 0) { uiGeo.width = title->width - crop[2] - crop[3]; } else { uiGeo.keep |= HB_KEEP_WIDTH; uiGeo.width = width; } if (height == 0) { uiGeo.height = title->height - crop[0] - crop[1]; } else { uiGeo.keep |= HB_KEEP_HEIGHT; uiGeo.height = height; } uiGeo.maxWidth = maxWidth; uiGeo.maxHeight = maxHeight; uiGeo.dar.num = 0; uiGeo.dar.den = 0; if( par_width && par_height ) { uiGeo.par.num = par_width; uiGeo.par.den = par_height; } else if (display_width != 0 && width != 0) { if (height != 0) { fprintf(stderr, "display_width (%d), width (%d), and height (%d) can not all be specified, ignoring height", display_width, width, height); } uiGeo.par.num = display_width; uiGeo.par.den = width; } else if (display_width != 0) { uiGeo.dar.num = display_width; uiGeo.dar.den = uiGeo.height; } else { uiGeo.par = srcGeo.par; } hb_set_anamorphic_size2(&srcGeo, &uiGeo, &resultGeo); job->width = resultGeo.width; job->height = resultGeo.height; job->anamorphic.par_width = resultGeo.par.num; job->anamorphic.par_height = resultGeo.par.den; memcpy(job->crop, crop, sizeof(int[4])); // Add filter that does cropping and scaling char * filter_str; filter_str = hb_strdup_printf("%d:%d:%d:%d:%d:%d", job->width, job->height, crop[0], crop[1], crop[2], crop[3] ); filter = hb_filter_init( HB_FILTER_CROP_SCALE ); hb_add_filter( job, filter, filter_str ); free( filter_str ); // Add framerate shaping filter if (vrate) { filter_cfr = cfr; filter_vrate = 27000000; filter_vrate_base = vrate; } else if (cfr) { // cfr or pfr flag with no rate specified implies // use the title rate. filter_cfr = cfr; filter_vrate = title->rate; filter_vrate_base = title->rate_base; } filter = hb_filter_init(HB_FILTER_VFR); filter_str = hb_strdup_printf("%d:%d:%d", filter_cfr, filter_vrate, filter_vrate_base); hb_add_filter(job, filter, filter_str); free(filter_str); // hb_job_init() will set a default muxer for us // only override it if a specific muxer has been set // note: the muxer must be set after presets, but before encoders if (mux) { job->mux = mux; } // then, muxer options if (largeFileSize) { job->largeFileSize = 1; } if (mp4_optimize) { job->mp4_optimize = 1; } if (ipod_atom) { job->ipod_atom = 1; } if( vquality >= 0.0 ) { job->vquality = vquality; job->vbitrate = 0; } else if( vbitrate ) { job->vquality = -1.0; job->vbitrate = vbitrate; } /* Set video encoder and check muxer compatibility */ if (vcodec) { job->vcodec = vcodec; } encoder = NULL; while ((encoder = hb_video_encoder_get_next(encoder)) != NULL) { if ((encoder->codec == job->vcodec) && (encoder->muxers & job->mux) == 0) { hb_error("incompatible video encoder '%s' for muxer '%s'", hb_video_encoder_get_short_name(job->vcodec), hb_container_get_short_name (job->mux)); done_error = HB_ERROR_INIT; die = 1; return -1; } } #ifdef USE_QSV if (qsv_async_depth >= 0) { job->qsv.async_depth = qsv_async_depth; } job->qsv.decode = qsv_decode; #endif /* Grab audio tracks */ if( atracks ) { char * token = strtok( atracks, "," ); if( token == NULL ) token = optarg; int track_start, track_end; for( ; token != NULL; token = strtok( NULL, "," ) ) { if( strlen( token ) >= 3 ) { if( sscanf( token, "%d-%d", &track_start, &track_end ) == 2 ) { int i; for( i = track_start - 1; i < track_end; i++ ) { if( hb_list_item( title->list_audio, i ) == NULL ) { fprintf( stderr, "Warning: Could not find audio track %d, skipped\n", i + 1 ); continue; } audio = calloc( 1, sizeof( *audio ) ); hb_audio_config_init( audio ); audio->in.track = i; audio->out.track = num_audio_tracks++; hb_list_add( audios, audio ); } } else if( !strcasecmp(token, "none" ) ) { audio = calloc( 1, sizeof( *audio ) ); hb_audio_config_init( audio ); audio->in.track = audio->out.track = -1; audio->out.codec = 0; hb_list_add( audios, audio ); break; } else { fprintf( stderr, "ERROR: unable to parse audio input \"%s\", skipping\n", token); } } else { int i = atoi( token ) - 1; if( hb_list_item( title->list_audio, i ) == NULL ) { fprintf(stderr, "Warning: Could not find audio track '%s', skipped\n", token); continue; } audio = calloc( 1, sizeof( *audio ) ); hb_audio_config_init( audio ); audio->in.track = i; audio->out.track = num_audio_tracks++; hb_list_add( audios, audio ); } } } /* Parse audio tracks */ if( native_language && native_dub ) { if( hb_list_count( audios ) == 0 || !audio_explicit ) { for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { int track = i; audio = hb_list_audio_config_item( title->list_audio, i ); if( cmp_lang( native_language, audio->lang.iso639_2 ) && audio->lang.type != 3 && // Directors 1 audio->lang.type != 4) // Directors 2 { /* * Matched an audio to our native language - use it. * Replace any existing audio tracks that a preset may * have put here. */ if( hb_list_count( audios ) == 0 ) { audio = calloc( 1, sizeof( *audio ) ); hb_audio_config_init( audio ); audio->in.track = track; audio->out.track = num_audio_tracks++; /* Add it to our audios */ hb_list_add( audios, audio ); } else { /* * Update the track numbers on what is already in * there. */ for( i = 0; i < hb_list_count( audios ); i++ ) { audio = hb_list_item( audios, i ); audio->in.track = track; } } break; } } } else { fprintf( stderr, "Warning: Native language (dubbing) selection ignored since an audio track has already been selected\n" ); } } if( hb_list_count(audios) == 0 && hb_list_count(title->list_audio) > 0 ) { /* Create a new audio track with default settings */ audio = calloc( 1, sizeof( *audio ) ); hb_audio_config_init( audio ); /* Add it to our audios */ hb_list_add( audios, audio ); } tmp_num_audio_tracks = num_audio_tracks = hb_list_count( audios ); for( i = 0; i < tmp_num_audio_tracks; i++ ) { audio = hb_list_item( audios, 0 ); if( audio == NULL || audio->in.track == -1 || audio->out.track == -1 || audio->out.codec == 0 || hb_audio_add( job, audio ) == 0 ) { num_audio_tracks--; } if( audio != NULL ) { hb_list_rem( audios, audio ); if( audio->out.name ) { free( audio->out.name ); } free( audio ); } } /* Audio Codecs */ i = 0; if( acodecs ) { char * token = strtok(acodecs, ","); if( token == NULL ) token = acodecs; while ( token != NULL ) { if ((acodec = hb_audio_encoder_get_from_name(token)) <= 0) { fprintf(stderr, "Invalid codec %s, using default for container.\n", token); acodec = hb_audio_encoder_get_default(job->mux); } if( i < num_audio_tracks ) { audio = hb_list_audio_config_item(job->list_audio, i); audio->out.codec = acodec; } else { hb_audio_config_t * last_audio = hb_list_audio_config_item( job->list_audio, i - 1 ); hb_audio_config_t audio; if( last_audio ) { fprintf(stderr, "More audio codecs than audio tracks, copying track %i and using encoder %s\n", i, token); hb_audio_config_init(&audio); audio.in.track = last_audio->in.track; audio.out.track = num_audio_tracks++; audio.out.codec = acodec; hb_audio_add(job, &audio); } else { fprintf(stderr, "Audio codecs and no valid audio tracks, skipping codec %s\n", token); } } token = strtok(NULL, ","); i++; } } if( i < num_audio_tracks ) { /* We have fewer inputs than audio tracks, use the default codec for * this container for the remaining tracks. Unless we only have one input * then use that codec instead. */ if (i != 1) acodec = hb_audio_encoder_get_default(job->mux); for ( ; i < num_audio_tracks; i++) { audio = hb_list_audio_config_item(job->list_audio, i); audio->out.codec = acodec; } } // sanity check muxer compatibility for (i = 0; i < num_audio_tracks; i++) { encoder = NULL; audio = hb_list_audio_config_item(job->list_audio, i); if (audio != NULL) { while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) { if ((encoder->codec == audio->out.codec) && (encoder->muxers & job->mux) == 0) { hb_error("audio track %d: incompatible encoder '%s' for muxer '%s'", i + 1, hb_audio_encoder_get_short_name(audio->out.codec), hb_container_get_short_name (job->mux)); done_error = HB_ERROR_INIT; die = 1; return -1; } } } } /* Audio Codecs */ /* Sample Rate */ int auto_sample_rate = 0; i = 0; if( arates ) { char * token = strtok(arates, ","); if (token == NULL) token = arates; while ( token != NULL ) { audio = hb_list_audio_config_item(job->list_audio, i); if( audio != NULL ) { if ( !strcasecmp( token, "auto" ) ) { arate = audio->in.samplerate; auto_sample_rate = 1; } else { arate = hb_audio_samplerate_get_from_name(token); } if (arate <= 0) { fprintf(stderr, "Invalid sample rate %s, using input rate %d\n", token, audio->in.samplerate); arate = audio->in.samplerate; } audio->out.samplerate = arate; if( (++i) >= num_audio_tracks ) break; /* We have more inputs than audio tracks, oops */ } else { fprintf(stderr, "Ignoring sample rate %d, no audio tracks\n", arate); } token = strtok(NULL, ","); } } if (i < num_audio_tracks) { /* We have fewer inputs than audio tracks, use default sample rate. * Unless we only have one input, then use that for all tracks. */ int use_default = 0; if( i != 1 || auto_sample_rate ) use_default = 1; for ( ; i < num_audio_tracks; i++) { audio = hb_list_audio_config_item(job->list_audio, i); if( use_default ) arate = audio->in.samplerate; audio->out.samplerate = arate; } } /* Sample Rate */ /* Audio Mixdown */ i = 0; if ( mixdowns ) { char * token = strtok(mixdowns, ","); if (token == NULL) token = mixdowns; while ( token != NULL ) { mixdown = hb_mixdown_get_from_name(token); audio = hb_list_audio_config_item(job->list_audio, i); if( audio != NULL ) { audio->out.mixdown = mixdown; if( (++i) >= num_audio_tracks ) break; /* We have more inputs than audio tracks, oops */ } else { fprintf(stderr, "Ignoring mixdown, no audio tracks\n"); } token = strtok(NULL, ","); } } if (i < num_audio_tracks && i == 1) { /* We have fewer inputs than audio tracks * and we only have one input, then use that. */ for (; i < num_audio_tracks; i++) { audio = hb_list_audio_config_item(job->list_audio, i); audio->out.mixdown = mixdown; } } /* Audio Mixdown */ /* Audio Bitrate */ i = 0; if( abitrates ) { for ( i = 0; abitrates[i] != NULL && i < num_audio_tracks; i++ ) { char * token = abitrates[i]; abitrate = atoi(token); audio = hb_list_audio_config_item(job->list_audio, i); if( audio != NULL ) { audio->out.bitrate = abitrate; } else { fprintf(stderr, "Ignoring bitrate %d, no audio tracks\n", abitrate); } } } 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. */ for (; i < num_audio_tracks; i++) { audio = hb_list_audio_config_item(job->list_audio, i); audio->out.bitrate = abitrate; } } /* Audio Bitrate */ /* Audio Quality */ i = 0; if( aqualities ) { for ( i = 0; aqualities[i] != NULL && i < num_audio_tracks; i++ ) { char * token = aqualities[i]; audio = hb_list_audio_config_item(job->list_audio, i); if( audio == NULL ) { fprintf(stderr, "Ignoring quality %.3f, no audio tracks\n", aquality); } else if( *token != 0 ) { aquality = atof(token); audio->out.quality = aquality; audio->out.bitrate = -1; } } } 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. */ for (; i < num_audio_tracks; i++) { audio = hb_list_audio_config_item(job->list_audio, i); if( audio->out.bitrate <= 0 ) audio->out.quality = aquality; } } /* Audio Quality */ /* Audio Compression Level */ i = 0; if( acompressions ) { for ( i = 0; acompressions[i] != NULL && i < num_audio_tracks; i++ ) { char * token = acompressions[i]; audio = hb_list_audio_config_item(job->list_audio, i); if( audio == NULL ) { fprintf(stderr, "Ignoring compression level %.2f, no audio tracks\n", acompression); } else if( *token != 0 ) { acompression = atof(token); audio->out.compression_level = acompression; } } } // Compression levels are codec specific values. So don't // try to apply to other tracks. /* Audio Compression Level */ /* Audio DRC */ i = 0; if ( dynamic_range_compression ) { char * token = strtok(dynamic_range_compression, ","); if (token == NULL) token = dynamic_range_compression; while ( token != NULL ) { d_r_c = atof(token); audio = hb_list_audio_config_item(job->list_audio, i); if( audio != NULL ) { audio->out.dynamic_range_compression = d_r_c; if( (++i) >= num_audio_tracks ) break; /* We have more inputs than audio tracks, oops */ } else { fprintf(stderr, "Ignoring drc, no audio tracks\n"); } token = strtok(NULL, ","); } } if (i < num_audio_tracks) { /* We have fewer inputs than audio tracks, use no DRC for the remaining * tracks. Unless we only have one input, then use the same DRC for all * tracks. */ if (i != 1) d_r_c = 0; for (; i < num_audio_tracks; i++) { audio = hb_list_audio_config_item(job->list_audio, i); audio->out.dynamic_range_compression = d_r_c; } } /* Audio DRC */ /* Audio Gain */ i = 0; if ( audio_gain ) { char * token = strtok(audio_gain, ","); if (token == NULL) token = audio_gain; while ( token != NULL ) { gain = atof(token); audio = hb_list_audio_config_item(job->list_audio, i); if( audio != NULL ) { audio->out.gain = gain; if( (++i) >= num_audio_tracks ) break; /* We have more inputs than audio tracks, oops */ } else { fprintf(stderr, "Ignoring gain, no audio tracks\n"); } token = strtok(NULL, ","); } } if (i < num_audio_tracks) { /* We have fewer inputs than audio tracks, use no gain for the remaining * tracks. Unless we only have one input, then use the same gain for all * tracks. */ if (i != 1) gain = 0; for (; i < num_audio_tracks; i++) { audio = hb_list_audio_config_item(job->list_audio, i); audio->out.gain = gain; } } /* 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 = hb_audio_dither_get_from_name(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; if( normalize_mix_level ) { for ( i = 0; normalize_mix_level[i] != NULL && i < num_audio_tracks; i++ ) { char * token = normalize_mix_level[i]; norm = atoi(token); audio = hb_list_audio_config_item(job->list_audio, i); if( audio != NULL ) { audio->out.normalize_mix_level = norm; } else { fprintf(stderr, "Ignoring normalization %d, no audio tracks\n", norm); } } } 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. */ for (; i < num_audio_tracks; i++) { audio = hb_list_audio_config_item(job->list_audio, i); audio->out.normalize_mix_level = norm; } } /* Audio Mix Normalization */ /* Audio Track Names */ if ( anames ) { char * token; for ( i = 0; anames[i] != NULL && i < num_audio_tracks; i++ ) { token = anames[i]; if ( *token ) { audio = hb_list_audio_config_item(job->list_audio, i); if( audio != NULL ) { audio->out.name = strdup(token); } else { fprintf(stderr, "Ignoring aname '%s', no audio track\n", token); } } } } if( i < num_audio_tracks && i == 1 ) { /* We have exactly one name and more than one audio track. Use the same * name for all tracks. */ for ( ; i < num_audio_tracks; i++) { audio = hb_list_audio_config_item(job->list_audio, i); audio->out.name = strdup(anames[0]); } } /* Audio Track Names */ /* Sanitize passthru (drop/fallback if necessary) */ for( i = 0; i < hb_list_count( job->list_audio ); ) { audio = hb_list_audio_config_item( job->list_audio, i ); if( audio->out.codec == HB_ACODEC_AUTO_PASS ) { // Auto Passthru job->acodec_copy_mask = allowed_audio_copy == -1 ? HB_ACODEC_PASS_MASK : allowed_audio_copy; job->acodec_fallback = hb_audio_encoder_get_from_name(acodec_fallback); } else if( ( audio->out.codec & HB_ACODEC_PASS_FLAG ) && !( audio->out.codec & audio->in.codec & HB_ACODEC_PASS_MASK ) ) { // passthru fallbacks int requested_passthru = audio->out.codec; audio->out.codec = hb_audio_encoder_get_fallback_for_passthru(requested_passthru); if (!(audio->out.codec & HB_ACODEC_MASK)) { // Passthru not possible, drop audio. fprintf(stderr, "Passthru requested and input codec is not the same as output codec for track %d, dropping track\n", audio->out.track); hb_audio_t *item = hb_list_item(job->list_audio, i); hb_list_rem(job->list_audio, item); hb_audio_close(&item); continue; } fprintf(stderr, "%s requested and input codec is not compatible for track %d, using %s encoder\n", hb_audio_encoder_get_name(requested_passthru), audio->out.track, hb_audio_encoder_get_name(audio->out.codec)); } // we didn't drop the track i++; } if( subtracks ) { char * token; int i; int burnpos = 0, defaultpos = 0; if ( subburn ) burnpos = strtol( subburn, NULL, 0 ); if ( subdefault ) defaultpos = strtol( subdefault, NULL, 0 ); for ( i = 0; subtracks[i] != NULL; i++ ) { token = subtracks[i]; if( strcasecmp(token, "scan" ) == 0 ) { int burn = 0, force = 0, def = 0; if ( subburn != NULL ) { burn = ( i == 0 && subburn[0] == 0 ) || ( burnpos == i+1 ); } if ( subdefault != NULL ) { def = ( i == 0 && subdefault[0] == 0 ) || ( defaultpos == i+1 ); } force = test_sub_list( subforce, i+1 ); if ( !burn ) { job->select_subtitle_config.dest = PASSTHRUSUB; } else { if ( sub_burned ) { continue; } sub_burned = 1; } job->select_subtitle_config.force = force; job->select_subtitle_config.default_track = def; subtitle_scan = 1; } else { hb_subtitle_t * subtitle; hb_subtitle_config_t sub_config; int track; int burn = 0, force = 0, def = 0; track = atoi(token) - 1; subtitle = hb_list_item(title->list_subtitle, track); if( subtitle == NULL ) { fprintf(stderr, "Warning: Could not find subtitle track '%s', skipped\n", token); continue; } sub_config = subtitle->config; if ( subburn != NULL ) { burn = ( i == 0 && subburn[0] == 0 ) || ( burnpos == i+1 ); } if ( subdefault != NULL ) { def = ( i == 0 && subdefault[0] == 0 ) || ( defaultpos == i+1 ); } force = test_sub_list(subforce, i+1); int supports_burn = hb_subtitle_can_burn( subtitle->source ); if ( ( burn && supports_burn ) || !hb_subtitle_can_pass( subtitle->source, mux ) ) { // Only allow one subtitle to be burned into video if ( sub_burned ) { fprintf( stderr, "Warning: Skipping subtitle track %d, can't have more than one track burnt in\n", track+1 ); continue; } sub_burned = 1; // Mark as burn-in sub_config.dest = RENDERSUB; } else { sub_config.dest = PASSTHRUSUB; } sub_config.force = force; sub_config.default_track = def; hb_subtitle_add( job, &sub_config, track ); } } } if( srtfile ) { int i; hb_subtitle_config_t sub_config; for( i=0; srtfile[i] != NULL; i++ ) { char *codeset = "L1"; int64_t offset = 0; char *lang = "und"; if (srtburn == i + 1 && hb_subtitle_can_burn(SRTSUB)) { // Only allow one subtitle to be burned into video if ( sub_burned ) { fprintf( stderr, "Warning: Skipping SRT track %d, can't have more than one track burnt in\n", i+1 ); continue; } sub_burned = 1; // Mark as burn-in sub_config.dest = RENDERSUB; } else { sub_config.dest = PASSTHRUSUB; } if( srtcodeset && srtcodeset[i] ) { codeset = srtcodeset[i]; } if( srtoffset && srtoffset[i] ) { offset = strtoll( srtoffset[i], &srtoffset[i], 0 ); } if ( srtlang && srtlang[i] ) { lang = srtlang[i]; } sub_config.default_track = srtdefault == i + 1; sub_config.force = 0; strncpy( sub_config.src_filename, srtfile[i], 255); sub_config.src_filename[255] = 0; strncpy( sub_config.src_codeset, codeset, 39); sub_config.src_codeset[39] = 0; sub_config.offset = offset; hb_srt_add( job, &sub_config, lang); } } if ( sub_burned ) { char * filter_str; filter_str = hb_strdup_printf("%d:%d:%d:%d", crop[0], crop[1], crop[2], crop[3] ); filter = hb_filter_init( HB_FILTER_RENDER_SUB ); hb_add_filter( job, filter, filter_str); free( filter_str ); } if( native_language ) { audio = hb_list_audio_config_item(job->list_audio, 0); if( audio ) { if( !cmp_lang( native_language, audio->lang.iso639_2 ) ) { /* * Audio language is not the same as our native language. * If we have any subtitles in our native language they * should be selected here if they haven't already been. */ hb_subtitle_t *subtitle, *subtitle2 = NULL; int matched_track = 0; for( i = 0; i < hb_list_count( title->list_subtitle ); i++ ) { subtitle = hb_list_item( title->list_subtitle, i ); matched_track = i; if (cmp_lang(native_language, subtitle->iso639_2)) { /* * Found the first matching subtitle in our * native language. Is it already selected? */ for( i = 0; i < hb_list_count( job->list_subtitle ); i++ ) { subtitle2 = hb_list_item( job->list_subtitle, i ); if( subtitle2->track == subtitle->track) { /* * Already selected */ break; } subtitle2 = NULL; } if( subtitle2 == NULL ) { /* * Not already selected, so select it. */ hb_subtitle_config_t sub_config; if( native_dub ) { fprintf( stderr, "Warning: no matching audio for native language - using subtitles instead.\n"); } sub_config = subtitle->config; if ((mux & HB_MUX_MASK_MKV) || subtitle->format == TEXTSUB) { sub_config.dest = PASSTHRUSUB; } sub_config.force = 0; sub_config.default_track = 1; hb_subtitle_add( job, &sub_config, matched_track); } /* * Stop searching. */ break; } } } } } hb_job_set_file( job, output ); if( color_matrix_code ) { job->color_matrix_code = color_matrix_code; } hb_job_set_encoder_preset (job, x264_preset); hb_job_set_encoder_tune (job, x264_tune); hb_job_set_encoder_profile(job, h264_profile); hb_job_set_encoder_level (job, h264_level); if (maxWidth) job->maxWidth = maxWidth; if (maxHeight) job->maxHeight = maxHeight; if( start_at_preview ) { job->start_at_preview = start_at_preview - 1; job->seek_points = preview_count; } if( stop_at_pts ) { job->pts_to_stop = stop_at_pts; subtitle_scan = 0; } if( stop_at_frame ) { job->frame_to_stop = stop_at_frame; subtitle_scan = 0; } if( start_at_pts ) { job->pts_to_start = start_at_pts; subtitle_scan = 0; } if( start_at_frame ) { job->frame_to_start = start_at_frame; subtitle_scan = 0; } /* OpenCL */ job->use_opencl = use_opencl; if( subtitle_scan ) { /* * When subtitle scan is enabled do a fast pre-scan job * which will determine which subtitles to enable, if any. */ job->pass = -1; hb_job_set_encoder_options(job, NULL); job->indepth_scan = subtitle_scan; fprintf( stderr, "Subtitle Scan Enabled - enabling " "subtitles if found for foreign language segments\n"); /* * Add the pre-scan job */ hb_add( h, job ); } hb_job_set_encoder_options(job, advanced_opts); if( twoPass ) { /* * If subtitle_scan is enabled then only turn it on * for the first pass and then off again for the * second. */ job->pass = 1; job->indepth_scan = 0; /* Turbo first pass */ if( turbo_opts_enabled ) { job->fastfirstpass = 1; } else { job->fastfirstpass = 0; } hb_add( h, job ); job->pass = 2; /* * On the second pass we turn off subtitle scan so that we * can actually encode using any subtitles that were auto * selected in the first pass (using the whacky select-subtitle * attribute of the job). */ job->indepth_scan = 0; hb_add( h, job ); } else { /* * Turn on subtitle scan if requested, note that this option * precludes encoding of any actual subtitles. */ job->indepth_scan = 0; job->pass = 0; hb_add( h, job ); } hb_job_close( &job ); hb_start( h ); break; } #define p s.param.working case HB_STATE_SEARCHING: fprintf( stdout, "\rEncoding: task %d of %d, Searching for start time, %.2f %%", p.job_cur, p.job_count, 100.0 * p.progress ); if( p.seconds > -1 ) { fprintf( stdout, " (ETA %02dh%02dm%02ds)", p.hours, p.minutes, p.seconds ); } fflush(stdout); break; case HB_STATE_WORKING: fprintf( stdout, "\rEncoding: task %d of %d, %.2f %%", p.job_cur, p.job_count, 100.0 * p.progress ); if( p.seconds > -1 ) { fprintf( stdout, " (%.2f fps, avg %.2f fps, ETA " "%02dh%02dm%02ds)", p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds ); } fflush(stdout); break; #undef p #define p s.param.muxing case HB_STATE_MUXING: { if (show_mux_warning) { fprintf( stdout, "\rMuxing: this may take awhile..." ); fflush(stdout); show_mux_warning = 0; } break; } #undef p #define p s.param.workdone case HB_STATE_WORKDONE: /* Print error if any, then exit */ switch( p.error ) { case HB_ERROR_NONE: fprintf( stderr, "\nEncode done!\n" ); break; case HB_ERROR_CANCELED: fprintf( stderr, "\nEncode canceled.\n" ); break; default: fprintf( stderr, "\nEncode failed (error %x).\n", p.error ); } done_error = p.error; die = 1; break; #undef p } return 0; } /**************************************************************************** * SigHandler: ****************************************************************************/ static volatile int64_t i_die_date = 0; void SigHandler( int i_signal ) { done_error = HB_ERROR_CANCELED; if( die == 0 ) { die = 1; i_die_date = hb_get_date(); fprintf( stderr, "Signal %d received, terminating - do it " "again in case it gets stuck\n", i_signal ); } else if( i_die_date + 500 < hb_get_date() ) { fprintf( stderr, "Dying badly, files might remain in your /tmp\n" ); exit( done_error ); } } /**************************************************************************** * ShowHelp: ****************************************************************************/ static void ShowHelp() { int i; const char *name; const hb_rate_t *rate; const hb_dither_t *dither; const hb_mixdown_t *mixdown; const hb_encoder_t *encoder; const hb_container_t *container; FILE* const out = stdout; fprintf( out, "Syntax: HandBrakeCLI [options] -i -o \n" "\n" "### General Handbrake Options------------------------------------------------\n\n" " -h, --help Print help\n" " -u, --update Check for updates and exit\n" " -v, --verbose <#> Be verbose (optional argument: logging level)\n" " -Z. --preset Use a built-in preset. Capitalization matters, and\n" " if the preset name has spaces, surround it with\n" " double quotation marks\n" " -z, --preset-list See a list of available built-in presets\n" " --no-dvdnav Do not use dvdnav for reading DVDs\n" " --no-opencl Disable use of OpenCL\n" "\n" "### Source Options-----------------------------------------------------------\n\n" " -i, --input Set input device\n" " -t, --title Select a title to encode (0 to scan all titles only,\n" " default: 1)\n" " --min-duration Set the minimum title duration (in seconds). Shorter\n" " titles will not be scanned (default: 10).\n" " --scan Scan selected title only.\n" " --main-feature Detect and select the main feature title.\n" " -c, --chapters Select chapters (e.g. \"1-3\" for chapters\n" " 1 to 3, or \"3\" for chapter 3 only,\n" " default: all chapters)\n" " --angle Select the video angle (DVD or Blu-ray only)\n" " --previews <#:B> Select how many preview images are generated,\n" " and whether or not they're stored to disk (0 or 1).\n" " (default: 10:0)\n" " --start-at-preview <#> Start encoding at a given preview.\n" " --start-at Start encoding at a given frame, duration (in seconds),\n" " or pts (on a 90kHz clock)\n" " --stop-at Stop encoding at a given frame, duration (in seconds),\n" " or pts (on a 90kHz clock)" "\n" "### Destination Options------------------------------------------------------\n\n" " -o, --output Set output file name\n" " -f, --format Set output container format ("); container = NULL; while ((container = hb_container_get_next(container)) != NULL) { fprintf(out, "%s", container->short_name); if (hb_container_get_next(container) != NULL) { fprintf(out, "/"); } else { fprintf(out, ")\n"); } } fprintf(out, " (default: autodetected from file name)\n" " -m, --markers Add chapter markers\n" " -O, --optimize Optimize mp4 files for HTTP streaming (\"fast start\")\n" " -I, --ipod-atom Mark mp4 files so 5.5G iPods will accept them\n" " -P, --use-opencl Use OpenCL where applicable\n" " -U, --use-hwd Use DXVA2 hardware decoding\n" "\n" "### Video Options------------------------------------------------------------\n\n" " -e, --encoder Set video library encoder\n" " Options: " ); name = NULL; encoder = NULL; while ((encoder = hb_video_encoder_get_next(encoder)) != NULL) { fprintf(out, "%s", encoder->short_name); if (hb_video_encoder_get_next(encoder) != NULL) { fprintf(out, "/"); } else { fprintf(out, "\n"); } if (encoder->codec == vcodec) { name = encoder->short_name; } } fprintf(out, " (default: %s)\n", name); fprintf(out, " --encoder-preset Adjust video encoding settings for a particular\n" " speed/efficiency tradeoff (encoder-specific)\n" " --encoder-preset-list List supported --encoder-preset values for the\n" " specified video encoder\n" " --encoder-tune Adjust video encoding settings for a particular\n" " type of souce or situation (encoder-specific)\n" " --encoder-tune-list List supported --encoder-tune values for the\n" " specified video encoder\n" " -x, --encopts Specify advanced encoding options in the same\n" " style as mencoder (all encoders except theora):\n" " option1=value1:option2=value2\n" " --encoder-profile Ensures compliance with the requested codec\n" " profile (encoder-specific)\n" " --encoder-profile-list List supported --encoder-profile values for the\n" " specified video encoder\n" " --encoder-level Ensures compliance with the requested codec\n" " level (encoder-specific)\n" " --encoder-level-list List supported --encoder-level values for the\n" " specified video encoder\n" " -q, --quality Set video quality\n" " -b, --vb Set video bitrate (default: 1000)\n" " -2, --two-pass Use two-pass mode\n" " -T, --turbo When using 2-pass use \"turbo\" options on the\n" " 1st pass to improve speed (only works with x264)\n" " -r, --rate Set video framerate (" ); rate = NULL; while ((rate = hb_video_framerate_get_next(rate)) != NULL) { fprintf(out, "%s", rate->name); if (hb_video_framerate_get_next(rate) != NULL) { fprintf(out, "/"); } } fprintf( out, ")\n" " Be aware that not specifying a framerate lets\n" " HandBrake preserve a source's time stamps,\n" " potentially creating variable framerate video\n" " --vfr, --cfr, --pfr Select variable, constant or peak-limited\n" " frame rate control. VFR preserves the source\n" " timing. CFR makes the output constant rate at\n" " the rate given by the -r flag (or the source's\n" " average rate if no -r is given). PFR doesn't\n" " allow the rate to go over the rate specified\n" " with the -r flag but won't change the source\n" " timing if it's below that rate.\n" " If none of these flags are given, the default\n" " is --cfr when -r is given and --vfr otherwise\n" "\n" "### Audio Options-----------------------------------------------------------\n\n" " -a, --audio Select audio track(s), separated by commas\n" " (\"none\" for no audio, \"1,2,3\" for multiple\n" " tracks, default: first one).\n" " Multiple output tracks can be used for one input.\n" " -E, --aencoder Audio encoder(s):\n" ); encoder = NULL; while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) { fprintf(out, " %s\n", encoder->short_name); } fprintf(out, " copy:* will passthrough the corresponding\n" " audio unmodified to the muxer if it is a\n" " supported passthrough audio type.\n" " Separated by commas for more than one audio track.\n" " Defaults:\n"); container = NULL; while ((container = hb_container_get_next(container)) != NULL) { int audio_encoder = hb_audio_encoder_get_default(container->format); fprintf(out, " %-8s %s\n", container->short_name, hb_audio_encoder_get_short_name(audio_encoder)); } fprintf(out, " --audio-copy-mask Set audio codecs that are permitted when the\n" " \"copy\" audio encoder option is specified\n" " (" ); i = 0; encoder = NULL; while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) { if ((encoder->codec & HB_ACODEC_PASS_FLAG) && (encoder->codec != HB_ACODEC_AUTO_PASS)) { if (i) { fprintf(out, "/"); } i = 1; // skip "copy:" fprintf(out, "%s", encoder->short_name + 5); } } fprintf(out, ", default: all).\n" " Separated by commas for multiple allowed options.\n" " --audio-fallback Set audio codec to use when it is not possible\n" " to copy an audio track without re-encoding.\n" " -B, --ab Set audio bitrate(s) (default: depends on the\n" " selected codec, mixdown and samplerate)\n" " Separated by commas for more than one audio track.\n" " -Q, --aq Set audio quality metric (default: depends on the\n" " selected codec)\n" " Separated by commas for more than one audio track.\n" " -C, --ac Set audio compression metric (default: depends on the\n" " selected codec)\n" " Separated by commas for more than one audio track.\n" " -6, --mixdown Format(s) for audio downmixing/upmixing:\n"); // skip HB_AMIXDOWN_NONE mixdown = hb_mixdown_get_next(NULL); while((mixdown = hb_mixdown_get_next(mixdown)) != NULL) { fprintf(out, " %s\n", mixdown->short_name); } fprintf(out, " Separated by commas for more than one audio track.\n" " Defaults:\n"); encoder = NULL; while((encoder = hb_audio_encoder_get_next(encoder)) != NULL) { if (!(encoder->codec & HB_ACODEC_PASS_FLAG)) { // layout: UINT64_MAX (all channels) should work with any mixdown int mixdown = hb_mixdown_get_default(encoder->codec, UINT64_MAX); // assumes that the encoder short name is <= 16 characters long fprintf(out, " %-16s up to %s\n", encoder->short_name, hb_mixdown_get_short_name(mixdown)); } } fprintf(out, " --normalize-mix Normalize audio mix levels to prevent clipping.\n" " Separated by commas for more than one audio track.\n" " 0 = Disable Normalization (default)\n" " 1 = Enable Normalization\n" " -R, --arate Set audio samplerate(s) (" ); rate = NULL; while ((rate = hb_audio_samplerate_get_next(rate)) != NULL) { fprintf(out, "%s", rate->name); if (hb_audio_samplerate_get_next(rate) != NULL) { fprintf(out, "/"); } } fprintf( out, " kHz)\n" " Separated by commas for more than one audio track.\n" " -D, --drc Apply extra dynamic range compression to the audio,\n" " making soft sounds louder. Range is 1.0 to 4.0\n" " (too loud), with 1.5 - 2.5 being a useful range.\n" " Separated by commas for more than one audio track.\n" " --gain Amplify or attenuate audio before encoding. Does\n" " 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 ("); i = 0; encoder = NULL; while ((encoder = hb_audio_encoder_get_next(encoder)) != NULL) { if (hb_audio_dither_is_supported(encoder->codec)) { if (i) { fprintf(out, "/"); } i = 1; fprintf(out, "%s", encoder->short_name); } } fprintf(out, ").\n"); fprintf(out, " Options:\n"); dither = NULL; while ((dither = hb_audio_dither_get_next(dither)) != NULL) { if (dither->method == hb_audio_dither_get_default()) { fprintf(out, " %s (default)\n", dither->short_name); } else { fprintf(out, " %s\n", dither->short_name); } } fprintf(out, " -A, --aname Audio track name(s),\n" " Separated by commas for more than one audio track.\n" "\n" "### Picture Settings---------------------------------------------------------\n\n" " -w, --width Set picture width\n" " -l, --height Set picture height\n" " --crop Set cropping values (default: autocrop)\n" " --loose-crop <#> Always crop to a multiple of the modulus\n" " Specifies the maximum number of extra pixels\n" " which may be cropped (default: 15)\n" " -Y, --maxHeight <#> Set maximum height\n" " -X, --maxWidth <#> Set maximum width\n" " --strict-anamorphic Store pixel aspect ratio in video stream\n" " --loose-anamorphic Store pixel aspect ratio with specified width\n" " --custom-anamorphic Store pixel aspect ratio in video stream and\n" " directly control all parameters.\n" " --display-width Set the width to scale the actual pixels to\n" " at playback, for custom anamorphic.\n" " --keep-display-aspect Preserve the source's display aspect ratio\n" " when using custom anamorphic\n" " --pixel-aspect Set a custom pixel aspect for custom anamorphic\n" " \n" " (--display-width and --pixel-aspect are mutually\n" " exclusive and the former will override the latter)\n" " --itu-par Use wider, ITU pixel aspect values for loose and\n" " custom anamorphic, useful with underscanned sources\n" " --modulus Set the number you want the scaled pixel dimensions\n" " to divide cleanly by. Does not affect strict\n" " anamorphic mode, which is always mod 2 (default: 16)\n" " -M, --color-matrix Set the color space signaled by the output\n" " Values: 709, pal, ntsc, 601 (same as ntsc)\n" " (default: detected from source)\n" "\n" "### Filters---------------------------------------------------------\n\n" " -d, --deinterlace Unconditionally deinterlaces all frames\n" " or omitted (default settings)\n" " or\n" " (default 0:-1)\n" " -5, --decomb Selectively deinterlaces when it detects combing\n" " or omitted (default settings)\n" " or\n" " \n" " (default: 7:2:6:9:80:16:16:10:20:20:4:2:50:24:1:-1)\n" " -9, --detelecine Detelecine (ivtc) video with pullup filter\n" " Note: this filter drops duplicate frames to\n" " restore the pre-telecine framerate, unless you\n" " specify a constant framerate (--rate 29.97)\n" " (default 1:1:4:4:0:0:-1)\n" " -8, --denoise Denoise video with hqdn3d filter\n" " or omitted (default settings)\n" " or\n" " \n" " (default: 4:3:3:6:4.5:4.5)\n" " --nlmeans Denoise video with nlmeans filter\n" " or omitted\n" " or\n" " \n" " (default 8:1:7:3:2:0)\n" " --nlmeans-tune Tune nlmeans filter to content type\n" " Note: only works in conjunction with presets\n" " ultralight/light/medium/strong.\n" " or omitted (default none)\n" " -7, --deblock Deblock video with pp7 filter\n" " (default 5:2)\n" " --rotate Rotate image or flip its axes.\n" " Modes: (can be combined)\n" " 1 vertical flip\n" " 2 horizontal flip\n" " 4 rotate clockwise 90 degrees\n" " Default: 3 (vertical and horizontal flip)\n" " -g, --grayscale Grayscale encoding\n" "\n" "### Subtitle Options------------------------------------------------------------\n\n" " -s, --subtitle Select subtitle track(s), separated by commas\n" " More than one output track can be used for one\n" " input.\n" " Example: \"1,2,3\" for multiple tracks.\n" " A special track name \"scan\" adds an extra 1st pass.\n" " This extra pass scans subtitles matching the\n" " language of the first audio or the language \n" " selected by --native-language.\n" " The one that's only used 10 percent of the time\n" " or less is selected. This should locate subtitles\n" " for short foreign language segments. Best used in\n" " conjunction with --subtitle-forced.\n" " -F, --subtitle-forced Only display subtitles from the selected stream if\n" " the subtitle has the forced flag set. The values in\n" " \"string\" are indexes into the subtitle list\n" " specified with '--subtitle'.\n" " Separated by commas for more than one subtitle track.\n" " Example: \"1,2,3\" for multiple tracks.\n" " If \"string\" is omitted, the first track is forced.\n" " --subtitle-burned \"Burn\" the selected subtitle into the video track\n" " If \"number\" is omitted, the first track is burned.\n" " \"number\" is an index into the subtitle list\n" " specified with '--subtitle'.\n" " --subtitle-default Flag the selected subtitle as the default subtitle\n" " to be displayed upon playback. Setting no default\n" " means no subtitle will be automatically displayed\n" " If \"number\" is omitted, the first track is default.\n" " \"number\" is an index into the subtitle list\n" " specified with '--subtitle'.\n" " -N, --native-language Specifiy your language preference. When the first\n" " audio track does not match your native language then\n" " select the first subtitle that does. When used in\n" " conjunction with --native-dub the audio track is\n" " changed in preference to subtitles. Provide the\n" " language's iso639-2 code (fre, eng, spa, dut, et cetera)\n" " --native-dub Used in conjunction with --native-language\n" " requests that if no audio tracks are selected the\n" " default selected audio track will be the first one\n" " that matches the --native-language. If there are no\n" " matching audio tracks then the first matching\n" " subtitle track is used instead.\n" " --srt-file SubRip SRT filename(s), separated by commas.\n" " --srt-codeset Character codeset(s) that the SRT file(s) are\n" " encoded in, separated by commas.\n" " Use 'iconv -l' for a list of valid\n" " codesets. If not specified, 'latin1' is assumed\n" " --srt-offset Offset (in milliseconds) to apply to the SRT file(s),\n" " separated by commas. If not specified, zero is assumed.\n" " Offsets may be negative.\n" " --srt-lang Language as an iso639-2 code fra, eng, spa et cetera)\n" " for the SRT file(s), separated by commas. If not specified,\n" " then 'und' is used.\n" " --srt-default Flag the selected srt as the default subtitle\n" " to be displayed upon playback. Setting no default\n" " means no subtitle will be automatically displayed\n" " If \"number\" is omitted, the first srt is default.\n" " \"number\" is an 1 based index into the srt-file list\n" " --srt-burn \"Burn\" the selected srt subtitle into the video track\n" " If \"number\" is omitted, the first srt is burned.\n" " \"number\" is an 1 based index into the srt-file list\n" "\n" ); #ifdef USE_QSV if (hb_qsv_available()) { fprintf( out, "### Intel Quick Sync Video------------------------------------------------------\n\n" " --disable-qsv-decoding Force software decoding of the video track.\n" " --qsv-async-depth Specifies how many asynchronous operations should be\n" " performed before the result is explicitly synchronized.\n" " Default: 4. If zero, the value is not specified.\n" "\n" ); } #endif } /**************************************************************************** * ShowPresets: ****************************************************************************/ static void ShowPresets() { fprintf( stderr, "%s - %s - %s\n", HB_PROJECT_TITLE, HB_PROJECT_BUILD_TITLE, HB_PROJECT_URL_WEBSITE ); printf("\n< Devices\n"); printf("\n + Universal: -e x264 -q 20.0 -r 30 --pfr -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 720 -Y 576 --loose-anamorphic --modulus 2 -m --x264-preset fast --h264-profile baseline --h264-level 3.0\n"); printf("\n + iPod: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 160 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -I -X 320 -Y 240 --modulus 2 -m --x264-preset medium --h264-profile baseline --h264-level 1.3\n"); printf("\n + iPhone & iPod touch: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 160 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 960 -Y 640 --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 3.1\n"); printf("\n + iPad: -e x264 -q 20.0 -r 30 --pfr -a 1 -E ffaac -B 160 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1280 -Y 720 --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 3.1\n"); printf("\n + AppleTV: -e x264 -q 20.0 -r 30 --pfr -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 960 -Y 720 --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 3.1 -x qpmin=4:cabac=0:ref=2:b-pyramid=none:weightb=0:weightp=0:vbv-maxrate=9500:vbv-bufsize=9500\n"); printf("\n + AppleTV 2: -e x264 -q 20.0 -r 30 --pfr -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1280 -Y 720 --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 3.1\n"); printf("\n + AppleTV 3: -e x264 -q 20.0 -r 30 --pfr -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1920 -Y 1080 --decomb=fast --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 4.0\n"); printf("\n + Android: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 128 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 720 -Y 576 --loose-anamorphic --modulus 2 --x264-preset medium --h264-profile main --h264-level 3.0\n"); printf("\n + Android Tablet: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 128 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1280 -Y 720 --loose-anamorphic --modulus 2 --x264-preset medium --h264-profile main --h264-level 3.1\n"); printf("\n + Windows Phone 8: -e x264 -q 22.0 -r 30 --pfr -a 1 -E ffaac -B 128 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 -X 1280 -Y 720 --modulus 2 --x264-preset medium --h264-profile main --h264-level 3.1\n"); printf("\n>\n"); printf("\n< Regular\n"); printf("\n + Normal: -e x264 -q 20.0 -a 1 -E ffaac -B 160 -6 dpl2 -R Auto -D 0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 --loose-anamorphic --modulus 2 -m --x264-preset veryfast --h264-profile main --h264-level 4.0\n"); printf("\n + High Profile: -e x264 -q 20.0 -a 1,1 -E ffaac,copy:ac3 -B 160,160 -6 dpl2,none -R Auto,Auto -D 0.0,0.0 --audio-copy-mask aac,ac3,dtshd,dts,mp3 --audio-fallback ffac3 -f mp4 --decomb --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 4.1\n"); printf("\n>\n"); } static char* strchr_quote(char *pos, char c, char q) { if (pos == NULL) return NULL; while (*pos != 0 && *pos != c) { if (*pos == q) { pos = strchr_quote(pos+1, q, 0); if (pos == NULL) return NULL; pos++; } else if (*pos == '\\' && *(pos+1) != 0) pos += 2; else pos++; } if (*pos != c) return NULL; return pos; } static char *strndup_quote(char *str, char q, int len) { if (str == NULL) return NULL; char * res; int str_len = strlen( str ); int src = 0, dst = 0; res = malloc( len > str_len ? str_len + 1 : len + 1 ); while (str[src] != 0 && src < len) { if (str[src] == q) src++; else if (str[src] == '\\' && str[src+1] != 0) { res[dst++] = str[src+1]; src += 2; } else res[dst++] = str[src++]; } res[dst] = '\0'; return res; } static char** str_split( char *str, char delem ) { char * pos; char * end; char ** ret; int count, i; char quote = '"'; if (delem == '"') { quote = '\''; } if ( str == NULL || str[0] == 0 ) { ret = malloc( sizeof(char*) ); *ret = NULL; return ret; } // Find number of elements in the string count = 1; pos = str; while ( ( pos = strchr_quote( pos, delem, quote ) ) != NULL ) { count++; pos++; } ret = calloc( ( count + 1 ), sizeof(char*) ); pos = str; for ( i = 0; i < count - 1; i++ ) { end = strchr_quote( pos, delem, quote ); ret[i] = strndup_quote(pos, quote, end - pos); pos = end + 1; } ret[i] = strndup_quote(pos, quote, strlen(pos)); return ret; } static void str_vfree( char **strv ) { int i; if (strv == NULL) return; for ( i = 0; strv[i]; i++ ) { free( strv[i] ); } free( strv ); } static double parse_hhmmss_strtok() { /* Assumes strtok has already been called on a string. Intends to parse * hh:mm:ss.ss or mm:ss.ss or ss.ss or ss into double seconds. Actually * parses a list of doubles separated by colons, multiplying the current * result by 60 then adding in the next value. Malformed input does not * result in a explicit error condition but instead returns an * intermediate result. */ double duration = 0; char* str; while ((str = strtok(NULL, ":")) != NULL) duration = 60*duration + strtod(str, NULL); return duration; } /**************************************************************************** * ParseOptions: ****************************************************************************/ static int ParseOptions( int argc, char ** argv ) { #define PREVIEWS 257 #define START_AT_PREVIEW 258 #define START_AT 259 #define STOP_AT 260 #define ANGLE 261 #define DVDNAV 262 #define DISPLAY_WIDTH 263 #define PIXEL_ASPECT 264 #define MODULUS 265 #define KEEP_DISPLAY_ASPECT 266 #define SUB_BURNED 267 #define SUB_DEFAULT 268 #define NATIVE_DUB 269 #define SRT_FILE 270 #define SRT_CODESET 271 #define SRT_OFFSET 272 #define SRT_LANG 273 #define SRT_DEFAULT 274 #define SRT_BURN 275 #define ROTATE_FILTER 276 #define SCAN_ONLY 277 #define MAIN_FEATURE 278 #define MIN_DURATION 279 #define AUDIO_GAIN 280 #define ALLOWED_AUDIO_COPY 281 #define AUDIO_FALLBACK 282 #define LOOSE_CROP 283 #define ENCODER_PRESET 284 #define ENCODER_PRESET_LIST 285 #define ENCODER_TUNE 286 #define ENCODER_TUNE_LIST 287 #define ENCODER_PROFILE 288 #define ENCODER_PROFILE_LIST 289 #define ENCODER_LEVEL 290 #define ENCODER_LEVEL_LIST 291 #define NO_OPENCL 292 #define NORMALIZE_MIX 293 #define AUDIO_DITHER 294 #define QSV_BASELINE 295 #define QSV_ASYNC_DEPTH 296 #define QSV_IMPLEMENTATION 297 #define FILTER_NLMEANS 298 #define FILTER_NLMEANS_TUNE 299 for( ;; ) { static struct option long_options[] = { { "help", no_argument, NULL, 'h' }, { "update", no_argument, NULL, 'u' }, { "verbose", optional_argument, NULL, 'v' }, { "no-dvdnav", no_argument, NULL, DVDNAV }, { "no-opencl", no_argument, NULL, NO_OPENCL }, #ifdef USE_QSV { "qsv-baseline", no_argument, NULL, QSV_BASELINE, }, { "qsv-async-depth", required_argument, NULL, QSV_ASYNC_DEPTH, }, { "qsv-implementation", required_argument, NULL, QSV_IMPLEMENTATION, }, { "disable-qsv-decoding", no_argument, &qsv_decode, 0, }, #endif { "format", required_argument, NULL, 'f' }, { "input", required_argument, NULL, 'i' }, { "output", required_argument, NULL, 'o' }, { "large-file", no_argument, NULL, '4' }, { "optimize", no_argument, NULL, 'O' }, { "ipod-atom", no_argument, NULL, 'I' }, { "use-opencl", no_argument, NULL, 'P' }, { "use-hwd", no_argument, NULL, 'U' }, { "title", required_argument, NULL, 't' }, { "min-duration",required_argument, NULL, MIN_DURATION }, { "scan", no_argument, NULL, SCAN_ONLY }, { "main-feature",no_argument, NULL, MAIN_FEATURE }, { "chapters", required_argument, NULL, 'c' }, { "angle", required_argument, NULL, ANGLE }, { "markers", optional_argument, NULL, 'm' }, { "audio", required_argument, NULL, 'a' }, { "mixdown", required_argument, NULL, '6' }, { "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 }, { "subtitle-default", optional_argument, NULL, SUB_DEFAULT }, { "srt-file", required_argument, NULL, SRT_FILE }, { "srt-codeset", required_argument, NULL, SRT_CODESET }, { "srt-offset", required_argument, NULL, SRT_OFFSET }, { "srt-lang", required_argument, NULL, SRT_LANG }, { "srt-default", optional_argument, NULL, SRT_DEFAULT }, { "srt-burn", optional_argument, NULL, SRT_BURN }, { "native-language", required_argument, NULL,'N' }, { "native-dub", no_argument, NULL, NATIVE_DUB }, { "encoder", required_argument, NULL, 'e' }, { "aencoder", required_argument, NULL, 'E' }, { "two-pass", no_argument, NULL, '2' }, { "deinterlace", optional_argument, NULL, 'd' }, { "deblock", optional_argument, NULL, '7' }, { "denoise", optional_argument, NULL, '8' }, { "nlmeans", optional_argument, NULL, FILTER_NLMEANS }, { "nlmeans-tune",required_argument, NULL, FILTER_NLMEANS_TUNE }, { "detelecine", optional_argument, NULL, '9' }, { "decomb", optional_argument, NULL, '5' }, { "grayscale", no_argument, NULL, 'g' }, { "rotate", optional_argument, NULL, ROTATE_FILTER }, { "strict-anamorphic", no_argument, &anamorphic_mode, 1 }, { "loose-anamorphic", no_argument, &anamorphic_mode, 2 }, { "custom-anamorphic", no_argument, &anamorphic_mode, 3 }, { "display-width", required_argument, NULL, DISPLAY_WIDTH }, { "keep-display-aspect", no_argument, &keep_display_aspect, 1 }, { "pixel-aspect", required_argument, NULL, PIXEL_ASPECT }, { "modulus", required_argument, NULL, MODULUS }, { "itu-par", no_argument, &itu_par, 1 }, { "width", required_argument, NULL, 'w' }, { "height", required_argument, NULL, 'l' }, { "crop", required_argument, NULL, 'n' }, { "loose-crop", optional_argument, NULL, LOOSE_CROP }, // mapping of legacy option names for backwards compatibility { "qsv-preset", required_argument, NULL, ENCODER_PRESET, }, { "x264-preset", required_argument, NULL, ENCODER_PRESET, }, { "x265-preset", required_argument, NULL, ENCODER_PRESET, }, { "x264-tune", required_argument, NULL, ENCODER_TUNE, }, { "x265-tune", required_argument, NULL, ENCODER_TUNE, }, { "x264-profile", required_argument, NULL, ENCODER_PROFILE, }, { "h264-profile", required_argument, NULL, ENCODER_PROFILE, }, { "h265-profile", required_argument, NULL, ENCODER_PROFILE, }, { "h264-level", required_argument, NULL, ENCODER_LEVEL, }, { "h265-level", required_argument, NULL, ENCODER_LEVEL, }, // encoder preset/tune/options/profile/level { "encoder-preset", required_argument, NULL, ENCODER_PRESET, }, { "encoder-preset-list", required_argument, NULL, ENCODER_PRESET_LIST, }, { "encoder-tune", required_argument, NULL, ENCODER_TUNE, }, { "encoder-tune-list", required_argument, NULL, ENCODER_TUNE_LIST, }, { "encopts", required_argument, NULL, 'x', }, { "encoder-profile", required_argument, NULL, ENCODER_PROFILE, }, { "encoder-profile-list", required_argument, NULL, ENCODER_PROFILE_LIST, }, { "encoder-level", required_argument, NULL, ENCODER_LEVEL, }, { "encoder-level-list", required_argument, NULL, ENCODER_LEVEL_LIST, }, { "vb", required_argument, NULL, 'b' }, { "quality", required_argument, NULL, 'q' }, { "ab", required_argument, NULL, 'B' }, { "aq", required_argument, NULL, 'Q' }, { "ac", required_argument, NULL, 'C' }, { "rate", required_argument, NULL, 'r' }, { "arate", required_argument, NULL, 'R' }, { "turbo", no_argument, NULL, 'T' }, { "maxHeight", required_argument, NULL, 'Y' }, { "maxWidth", required_argument, NULL, 'X' }, { "preset", required_argument, NULL, 'Z' }, { "preset-list", no_argument, NULL, 'z' }, { "aname", required_argument, NULL, 'A' }, { "color-matrix",required_argument, NULL, 'M' }, { "previews", required_argument, NULL, PREVIEWS }, { "start-at-preview", required_argument, NULL, START_AT_PREVIEW }, { "start-at", required_argument, NULL, START_AT }, { "stop-at", required_argument, NULL, STOP_AT }, { "vfr", no_argument, &cfr, 0 }, { "cfr", no_argument, &cfr, 1 }, { "pfr", no_argument, &cfr, 2 }, { "audio-copy-mask", required_argument, NULL, ALLOWED_AUDIO_COPY }, { "audio-fallback", required_argument, NULL, AUDIO_FALLBACK }, { 0, 0, 0, 0 } }; int option_index = 0; int c; int cur_optind; cur_optind = optind; c = getopt_long( argc, argv, "hv::uC:f:4i:Io:PUt:c:m::M:a:A:6:s:F::N:e:E:Q:C:" "2dD:7895gOw:l:n:b:q:S:B:r:R:x:TY:X:Z:z", long_options, &option_index ); if( c < 0 ) { break; } switch( c ) { case 0: /* option was handled entirely in getopt_long */ break; case 'h': ShowHelp(); exit( 0 ); case 'u': update = 1; break; case 'v': if( optarg != NULL ) { debug = atoi( optarg ); } else { debug = 1; } break; case 'Z': preset = 1; preset_name = strdup(optarg); break; case 'z': ShowPresets(); exit ( 0 ); case DVDNAV: dvdnav = 0; break; case 'f': format = strdup( optarg ); break; case 'i': input = strdup( optarg ); #ifdef __APPLE_CC__ char *devName = bsd_name_for_path( input ); // alloc if( devName ) { if( device_is_dvd( devName )) { free( input ); input = malloc( strlen( "/dev/" ) + strlen( devName ) + 1 ); sprintf( input, "/dev/%s", devName ); } free( devName ); } #endif break; case 'o': output = strdup( optarg ); break; case '4': largeFileSize = 1; break; case 'O': mp4_optimize = 1; break; case 'I': ipod_atom = 1; break; case 'P': use_opencl = 1; break; case 'U': use_hwd = 1; break; case 't': titleindex = atoi( optarg ); break; case SCAN_ONLY: titlescan = 1; break; case MAIN_FEATURE: main_feature = 1; break; case 'c': { int start, end; if( sscanf( optarg, "%d-%d", &start, &end ) == 2 ) { chapter_start = start; chapter_end = end; } else if( sscanf( optarg, "%d", &start ) == 1 ) { chapter_start = start; chapter_end = chapter_start; } else { fprintf( stderr, "chapters: invalid syntax (%s)\n", optarg ); return -1; } break; } case NO_OPENCL: use_opencl = 0; break; case ANGLE: angle = atoi( optarg ); break; case 'm': if( optarg != NULL ) { marker_file = strdup( optarg ); } chapter_markers = 1; break; case 'a': if( optarg != NULL ) { atracks = strdup( optarg ); audio_explicit = 1; } else { atracks = "1" ; } break; case '6': if( optarg != NULL ) { mixdowns = strdup( optarg ); } break; case 'D': if( optarg != NULL ) { dynamic_range_compression = strdup( optarg ); } break; case AUDIO_GAIN: if( optarg != NULL ) { audio_gain = strdup( optarg ); } break; case AUDIO_DITHER: if (optarg != NULL) { audio_dither = str_split(optarg, ','); } break; case NORMALIZE_MIX: if( optarg != NULL ) { normalize_mix_level = str_split( optarg, ',' ); } break; case 's': subtracks = str_split( optarg, ',' ); break; case 'F': subforce = str_split( optarg, ',' ); break; case SUB_BURNED: if( optarg != NULL ) { subburn = strdup( optarg ); } else { subburn = "" ; } break; case SUB_DEFAULT: if( optarg != NULL ) { subdefault = strdup( optarg ); } else { subdefault = "" ; } break; case 'N': native_language = strdup( optarg ); break; case NATIVE_DUB: native_dub = 1; break; case SRT_FILE: srtfile = str_split( optarg, ',' ); break; case SRT_CODESET: srtcodeset = str_split( optarg, ',' ); break; case SRT_OFFSET: srtoffset = str_split( optarg, ',' ); break; case SRT_LANG: srtlang = str_split( optarg, ',' ); break; case SRT_DEFAULT: if( optarg != NULL ) { srtdefault = atoi( optarg ); } else { srtdefault = 1 ; } break; case SRT_BURN: if( optarg != NULL ) { srtburn = atoi( optarg ); } else { srtburn = 1 ; } break; case '2': twoPass = 1; break; case 'd': if( optarg != NULL ) { if (!( strcmp( optarg, "fast" ) )) { deinterlace_opt = "0"; } else if (!( strcmp( optarg, "slow" ) )) { deinterlace_opt = "1"; } else if (!( strcmp( optarg, "slower" ) )) { deinterlace_opt = "3"; } else if (!( strcmp( optarg, "bob" ) )) { deinterlace_opt = "15"; } else { deinterlace_opt = strdup( optarg ); } } deinterlace = 1; break; case '7': if( optarg != NULL ) { deblock_opt = strdup( optarg ); } deblock = 1; break; case '8': if( optarg != NULL ) { free(denoise_opt); denoise_opt = strdup( optarg ); } denoise = 1; break; case FILTER_NLMEANS: if (optarg != NULL) { free(nlmeans_opt); nlmeans_opt = strdup(optarg); } nlmeans = 1; break; case FILTER_NLMEANS_TUNE: if (optarg != NULL) { free(nlmeans_tune_opt); nlmeans_tune_opt = strdup(optarg); } break; case '9': if( optarg != NULL ) { detelecine_opt = strdup( optarg ); } detelecine = 1; break; case '5': if( optarg != NULL ) { if (!( strcmp( optarg, "fast" ) )) { decomb_opt = "7:2:6:9:1:80"; } else if (!( strcmp( optarg, "bob" ) )) { decomb_opt = "455"; } else { decomb_opt = strdup( optarg ); } } decomb = 1; break; case 'g': grayscale = 1; break; case ROTATE_FILTER: if( optarg != NULL ) { rotate_opt = strdup( optarg ); rotate_val = atoi( optarg ); } rotate = 1; break; case DISPLAY_WIDTH: if( optarg != NULL ) { sscanf( optarg, "%i", &display_width ); } break; case PIXEL_ASPECT: if( optarg != NULL ) { sscanf( optarg, "%i:%i", &par_width, &par_height ); } break; case MODULUS: if( optarg != NULL ) { sscanf( optarg, "%i", &modulus ); } break; case 'e': { vcodec = hb_video_encoder_get_from_name(optarg); if (vcodec <= 0) { fprintf(stderr, "invalid codec (%s)\n", optarg); return -1; } break; } case 'E': if( optarg != NULL ) { acodecs = strdup( optarg ); } break; case 'w': width = atoi( optarg ); break; case 'l': height = atoi( optarg ); break; case 'n': { int i; char * tmp = optarg; for( i = 0; i < 4; i++ ) { if( !*tmp ) break; crop[i] = strtol( tmp, &tmp, 0 ); tmp++; } break; } case LOOSE_CROP: loose_crop = optarg ? atoi(optarg) : 15; break; case 'r': { vrate = hb_video_framerate_get_from_name(optarg); if (vrate <= 0) { vrate = 0; fprintf(stderr, "invalid framerate %s\n", optarg); } else if (!cfr) { cfr = 1; } break; } case 'R': if( optarg != NULL ) { arates = strdup( optarg ); } break; case 'b': vbitrate = atoi( optarg ); break; case 'q': vquality = atof( optarg ); break; case 'B': abitrates = str_split( optarg, ',' ); break; case 'Q': aqualities = str_split( optarg, ',' ); break; case 'C': acompressions = str_split( optarg, ',' ); break; case ENCODER_PRESET: x264_preset = strdup( optarg ); break; case ENCODER_TUNE: x264_tune = strdup( optarg ); break; case 'x': advanced_opts = strdup( optarg ); break; case ENCODER_PROFILE: h264_profile = strdup( optarg ); break; case ENCODER_LEVEL: h264_level = strdup( optarg ); break; case ENCODER_PRESET_LIST: fprintf(stderr, "Available --encoder-preset values for '%s' encoder:\n", hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg))); print_string_list(stderr, hb_video_encoder_get_presets(hb_video_encoder_get_from_name(optarg)), " "); exit(0); case ENCODER_TUNE_LIST: fprintf(stderr, "Available --encoder-tune values for '%s' encoder:\n", hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg))); print_string_list(stderr, hb_video_encoder_get_tunes(hb_video_encoder_get_from_name(optarg)), " "); exit(0); case ENCODER_PROFILE_LIST: fprintf(stderr, "Available --encoder-profile values for '%s' encoder:\n", hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg))); print_string_list(stderr, hb_video_encoder_get_profiles(hb_video_encoder_get_from_name(optarg)), " "); exit(0); case ENCODER_LEVEL_LIST: fprintf(stderr, "Available --encoder-level values for '%s' encoder:\n", hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(optarg))); print_string_list(stderr, hb_video_encoder_get_levels(hb_video_encoder_get_from_name(optarg)), " "); exit(0); case 'T': turbo_opts_enabled = 1; break; case 'Y': maxHeight = atoi( optarg ); break; case 'X': maxWidth = atoi (optarg ); break; case 'A': if( optarg != NULL ) { anames = str_split( optarg, ',' ); } break; case PREVIEWS: sscanf( optarg, "%i:%i", &preview_count, &store_previews ); break; case START_AT_PREVIEW: start_at_preview = atoi( optarg ); break; case START_AT: { char * start_at_string = NULL; char * start_at_token = NULL; start_at_string = strdup( optarg ); start_at_token = strtok( start_at_string, ":"); if( !strcmp( start_at_token, "frame" ) ) { start_at_token = strtok( NULL, ":"); start_at_frame = atoi(start_at_token); } else if( !strcmp( start_at_token, "pts" ) ) { start_at_token = strtok( NULL, ":"); sscanf( start_at_token, "%"SCNd64, &start_at_pts ); } else if( !strcmp( start_at_token, "duration" ) ) { double duration_seconds = parse_hhmmss_strtok(); start_at_pts = (int64_t)(duration_seconds * 90e3); } free( start_at_string ); break; } case STOP_AT: { char * stop_at_string = NULL; char * stop_at_token = NULL; stop_at_string = strdup( optarg ); stop_at_token = strtok( stop_at_string, ":"); if( !strcmp( stop_at_token, "frame" ) ) { stop_at_token = strtok( NULL, ":"); stop_at_frame = atoi(stop_at_token); } else if( !strcmp( stop_at_token, "pts" ) ) { stop_at_token = strtok( NULL, ":"); sscanf( stop_at_token, "%"SCNd64, &stop_at_pts ); } else if( !strcmp( stop_at_token, "duration" ) ) { double duration_seconds = parse_hhmmss_strtok(); stop_at_pts = (int64_t)(duration_seconds * 90e3); } free( stop_at_string ); break; } case ALLOWED_AUDIO_COPY: { allowed_audio_copy = 0; const hb_encoder_t *audio_encoder = NULL; char **allowed = str_split(optarg, ','); while ((audio_encoder = hb_audio_encoder_get_next(audio_encoder)) != NULL) { if ((audio_encoder->codec & HB_ACODEC_PASS_FLAG) && (audio_encoder->codec != HB_ACODEC_AUTO_PASS)) { int i = -1; while(allowed[++i] != NULL) { // skip "copy:" if (!strcasecmp(allowed[i], audio_encoder->short_name + 5)) { allowed_audio_copy |= audio_encoder->codec; break; } } } } allowed_audio_copy &= HB_ACODEC_PASS_MASK; str_vfree(allowed); break; } case AUDIO_FALLBACK: acodec_fallback = strdup( optarg ); break; case 'M': if( optarg != NULL ) { if( !strcmp( optarg, "601" ) || !strcmp( optarg, "ntsc" ) ) color_matrix_code = 1; else if( !strcmp( optarg, "pal" ) ) color_matrix_code = 2; else if( !strcmp( optarg, "709" ) ) color_matrix_code = 3; } break; case MIN_DURATION: min_title_duration = strtol( optarg, NULL, 0 ); break; #ifdef USE_QSV case QSV_BASELINE: hb_qsv_force_workarounds(); break; case QSV_ASYNC_DEPTH: qsv_async_depth = atoi(optarg); break; case QSV_IMPLEMENTATION: hb_qsv_impl_set_preferred(optarg); break; #endif default: fprintf( stderr, "unknown option (%s)\n", argv[cur_optind] ); return -1; } } if (nlmeans) { char *opt = hb_generate_filter_settings(HB_FILTER_NLMEANS, nlmeans_opt, nlmeans_tune_opt); if (opt != NULL) { free(nlmeans_opt); nlmeans_opt = opt; } else if (nlmeans_opt != NULL) { fprintf(stderr, "Invalid parameters for nlmeans (%s).", nlmeans_opt); return -1; } else if (nlmeans_tune_opt != NULL) { fprintf(stdout, "Default nlmeans parameters specified; ignoring nlmeans tune (%s).\n", nlmeans_tune_opt); } } if (denoise) { char *opt = hb_generate_filter_settings(HB_FILTER_DENOISE, denoise_opt, NULL); if (opt != NULL) { free(denoise_opt); denoise_opt = opt; } else if (denoise_opt != NULL) { fprintf(stderr, "Invalid parameters for hqdn3d (%s).", denoise_opt); return -1; } } return 0; } static int CheckOptions( int argc, char ** argv ) { if( update ) { return 0; } if( input == NULL || *input == '\0' ) { fprintf( stderr, "Missing input device. Run %s --help for " "syntax.\n", argv[0] ); return 1; } /* Parse format */ if( titleindex > 0 && !titlescan ) { if( output == NULL || *output == '\0' ) { fprintf( stderr, "Missing output file name. Run %s --help " "for syntax.\n", argv[0] ); return 1; } if (format == NULL) { /* autodetect */ const char *extension = strrchr(output, '.'); if (extension != NULL) { // skip '.' mux = hb_container_get_from_extension(extension + 1); } if (mux <= 0) { fprintf(stderr, "Output format can't be guessed from file name (%s), " "using default.\n", output); // reset the muxer (use default) mux = 0; return 0; } } else { mux = hb_container_get_from_name(format); if (mux <= 0) { fprintf(stderr, "Invalid output format (%s).", format); fprintf(stderr, "Possible choices are: "); const hb_container_t *container = NULL; while ((container = hb_container_get_next(container)) != NULL) { fprintf(stderr, "%s", container->short_name); if (hb_container_get_next(container) != NULL) { fprintf(stderr, ", "); } else { fprintf(stderr, "\n"); } } return 1; } } } return 0; } static void print_string_list(FILE *out, const char* const *list, const char *prefix) { if (out != NULL && prefix != NULL) { if (list != NULL) { while (*list != NULL) { fprintf(out, "%s%s\n", prefix, *list++); } } else { fprintf(out, "%s" "Option not supported by encoder\n", prefix); } } } #ifdef __APPLE_CC__ /**************************************************************************** * bsd_name_for_path * * Returns the BSD device name for the block device that contains the * passed-in path. Returns NULL on failure. ****************************************************************************/ static char* bsd_name_for_path(char *path) { OSStatus err; FSRef ref; err = FSPathMakeRef( (const UInt8 *) input, &ref, NULL ); if( err != noErr ) { return NULL; } // Get the volume reference number. FSCatalogInfo catalogInfo; err = FSGetCatalogInfo( &ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL); if( err != noErr ) { return NULL; } FSVolumeRefNum volRefNum = catalogInfo.volume; // Now let's get the device name GetVolParmsInfoBuffer volumeParms; err = FSGetVolumeParms( volRefNum, &volumeParms, sizeof( volumeParms ) ); if( err != noErr ) { return NULL; } // A version 4 GetVolParmsInfoBuffer contains the BSD node name in the vMDeviceID field. // It is actually a char * value. This is mentioned in the header CoreServices/CarbonCore/Files.h. if( volumeParms.vMVersion < 4 ) { return NULL; } // vMDeviceID might be zero as is reported with experimental ZFS (zfs-119) support in Leopard. if( !volumeParms.vMDeviceID ) { return NULL; } return strdup( volumeParms.vMDeviceID ); } /**************************************************************************** * device_is_dvd * * Returns whether or not the passed in BSD device represents a DVD, or other * optical media. ****************************************************************************/ static int device_is_dvd(char *device) { io_service_t service = get_iokit_service(device); if( service == IO_OBJECT_NULL ) { return 0; } int result = is_dvd_service(service); IOObjectRelease(service); return result; } /**************************************************************************** * get_iokit_service * * Returns the IOKit service object for the passed in BSD device name. ****************************************************************************/ static io_service_t get_iokit_service( char *device ) { CFMutableDictionaryRef matchingDict; matchingDict = IOBSDNameMatching( kIOMasterPortDefault, 0, device ); if( matchingDict == NULL ) { return IO_OBJECT_NULL; } // Fetch the object with the matching BSD node name. There should only be // one match, so IOServiceGetMatchingService is used instead of // IOServiceGetMatchingServices to simplify the code. return IOServiceGetMatchingService( kIOMasterPortDefault, matchingDict ); } /**************************************************************************** * is_dvd_service * * Returns whether or not the service passed in is a DVD. * * Searches for an IOMedia object that represents the entire (whole) media that * the volume is on. If the volume is on partitioned media, the whole media * object will be a parent of the volume's media object. If the media is not * partitioned, the volume's media object will be the whole media object. ****************************************************************************/ static int is_dvd_service( io_service_t service ) { kern_return_t kernResult; io_iterator_t iter; // Create an iterator across all parents of the service object passed in. kernResult = IORegistryEntryCreateIterator( service, kIOServicePlane, kIORegistryIterateRecursively | kIORegistryIterateParents, &iter ); if( kernResult != KERN_SUCCESS ) { return 0; } if( iter == IO_OBJECT_NULL ) { return 0; } // A reference on the initial service object is released in the do-while // loop below, so add a reference to balance. IOObjectRetain( service ); int result = 0; do { if( is_whole_media_service( service ) && IOObjectConformsTo( service, kIODVDMediaClass) ) { result = 1; } IOObjectRelease( service ); } while( !result && (service = IOIteratorNext( iter )) ); IOObjectRelease( iter ); return result; } /**************************************************************************** * is_whole_media_service * * Returns whether or not the service passed in is an IOMedia service and * represents the "whole" media instead of just a partition. * * The whole media object is indicated in the IORegistry by the presence of a * property with the key "Whole" and value "Yes". ****************************************************************************/ static int is_whole_media_service( io_service_t service ) { int result = 0; if( IOObjectConformsTo( service, kIOMediaClass ) ) { CFTypeRef wholeMedia = IORegistryEntryCreateCFProperty( service, CFSTR( kIOMediaWholeKey ), kCFAllocatorDefault, 0 ); if ( !wholeMedia ) { return 0; } result = CFBooleanGetValue( (CFBooleanRef)wholeMedia ); CFRelease( wholeMedia ); } return result; } #endif // __APPLE_CC__ HandBrake-0.10.2/test/parsecsv.h0000664000175200017520000000164412463330511017020 0ustar handbrakehandbrake/* parsecsv.c Copyright (c) 2003-2015 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ /* A very simple CSV file parser. */ typedef struct hb_csv_file_s hb_csv_file_t; typedef struct hb_csv_cell_s hb_csv_cell_t; struct hb_csv_file_s { FILE * fileref; int eof; int parse_state; int curr_row; int curr_col; }; struct hb_csv_cell_s { char cell_text[1024]; int cell_row; int cell_col; }; /* Open a CSV File */ hb_csv_file_t *hb_open_csv_file( const char *filepath ); void hb_close_csv_file( hb_csv_file_t *file ); /* Parse CSV Cells */ hb_csv_cell_t *hb_read_next_cell( hb_csv_file_t *file ); void hb_dispose_cell( hb_csv_cell_t *cell ); HandBrake-0.10.2/TRANSLATIONS0000664000175200017520000000072212255375101015705 0ustar handbrakehandbrakeTRANSLATIONS file for HandBrake Translating HandBrake ===================== For the UIs that support translation, these can be submitted on our community forum or reviewboard site. Mac GUI: - Translations are not currently supported in this UI. This may be supported at a later point. Windows GUI: - Translations are not currently supported in this UI. This may be supported at a later point. Linux GUI: - Russian - Czech HandBrake-0.10.2/configure0000775000175200017520000000057411155045721015755 0ustar handbrakehandbrake#! /bin/sh # inpath() { IFS=: for d in $PATH do if [ -x $d/$1 ]; then return 0 fi done return 1 } for p in python2.7 python2.6 python2.5 python2.4 python2 python do if ( inpath $p ); then exec $p `dirname $0`/make/configure.py "$@" exit 0 fi done echo "ERROR: no suitable version of python found." exit 1 HandBrake-0.10.2/DoxyfileLibHb0000664000175200017520000021055111433747163016463 0ustar handbrakehandbrake# Doxyfile 1.7.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = HandBrake # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = svn # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = ./documentation/libhb/ # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = "./libhb/" # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.d \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.idl \ *.odl \ *.cs \ *.php \ *.php3 \ *.inc \ *.m \ *.mm \ *.dox \ *.py \ *.f90 \ *.f \ *.vhd \ *.vhdl # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans.ttf # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES HandBrake-0.10.2/DoxyfileMac0000664000175200017520000021055111433750653016201 0ustar handbrakehandbrake# Doxyfile 1.7.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = HandBrake # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = svn # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = ./documentation/objc/ # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = "./macosx/" # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.d \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.idl \ *.odl \ *.cs \ *.php \ *.php3 \ *.inc \ *.m \ *.mm \ *.dox \ *.py \ *.f90 \ *.f \ *.vhd \ *.vhdl # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans.ttf # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES HandBrake-0.10.2/DoxyfileDotNet0000664000175200017520000021057111644117576016705 0ustar handbrakehandbrake# Doxyfile 1.7.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = HandBrake # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = svn # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = ./documentation/dotnet/ # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = "./win/CS/" # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.cs \ *.d \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.idl \ *.odl \ *.cs \ *.php \ *.php3 \ *.inc \ *.m \ *.mm \ *.dox \ *.py \ *.f90 \ *.f \ *.vhd \ *.vhdl # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans.ttf # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES HandBrake-0.10.2/THANKS0000664000175200017520000000147612416263450014765 0ustar handbrakehandbrakeTHANKS file for HandBrake Graphics: Modernised Application Icon by Matt Johnson (mattdog.100 [at] gmail.com) Handbrake Toolbar Icons by Nik Pawlak (holla [at] nikpawlak.com, @nikpawlak, http://nikpawlak.com) Hosting: Github (http://github.com) SourceForge(http://sourceforge.net/projects/handbrake) Eric Petit (Running the servers behind HandBrake.fr) Freenode IRC (http://freenode.net/) Software: ReviewBoard (http://www.reviewboard.org/) PhpBB (http://www.phpbb.com/) Jetbrains ReSharper (http://www.jetbrains.com/resharper/features/index.html?linklogos) VisualSVN (http://www.visualsvn.com) Other: Thanks to all of you who have contributed time, helped moderate our forum, submitted patches, testing, uploaded test files etc. There are too many to name here, but we do appreciate the effort. HandBrake-0.10.2/make/0000775000175200017520000000000012535641641014763 5ustar handbrakehandbrakeHandBrake-0.10.2/make/configure.py0000664000175200017520000020050512532126526017315 0ustar handbrakehandbrake############################################################################### ## ## This script is coded for minimum version of Python 2.4 . ## Pyhthon3 is incompatible. ## ## Authors: konablend ## ############################################################################### import fnmatch import glob import optparse import os import platform import re import subprocess import sys import time from optparse import OptionGroup from optparse import OptionGroup from optparse import OptionParser from sys import stderr from sys import stdout class AbortError( Exception ): def __init__( self, format, *args ): self.value = format % args def __str__( self ): return self.value ############################################################################### ## ## Main configure object. ## ## dir = containing this configure script ## cwd = current working dir at time of script launch ## class Configure( object ): OUT_QUIET = 0 OUT_INFO = 1 OUT_VERBOSE = 2 def __init__( self, verbose ): self._log_info = [] self._log_verbose = [] self._record = False self.verbose = verbose self.dir = os.path.dirname( sys.argv[0] ) self.cwd = os.getcwd() self.build_dir = '.' ## compute src dir which is 2 dirs up from this script self.src_dir = os.path.normpath( sys.argv[0] ) for i in range( 2 ): self.src_dir = os.path.dirname( self.src_dir ) if len( self.src_dir ) == 0: self.src_dir = os.curdir def _final_dir( self, chdir, dir ): dir = os.path.normpath( dir ) if not os.path.isabs( dir ): if os.path.isabs( chdir ): dir = os.path.normpath( os.path.abspath(dir )) else: dir = os.path.normpath( self.relpath( dir, chdir )) return dir ## output functions def errln( self, format, *args ): s = (format % args) if re.match( '^.*[!?:;.]$', s ): stderr.write( 'ERROR: %s configure stop.\n' % (s) ) else: stderr.write( 'ERROR: %s; configure stop.\n' % (s) ) self.record_log() sys.exit( 1 ) def infof( self, format, *args ): line = format % args self._log_verbose.append( line ) if cfg.verbose >= Configure.OUT_INFO: self._log_info.append( line ) stdout.write( line ) def verbosef( self, format, *args ): line = format % args self._log_verbose.append( line ) if cfg.verbose >= Configure.OUT_VERBOSE: stdout.write( line ) ## doc is ready to be populated def doc_ready( self ): ## compute final paths as they are after chdir into build self.build_final = os.curdir self.src_final = self._final_dir( self.build_dir, self.src_dir ) self.prefix_final = self._final_dir( self.build_dir, self.prefix_dir ) cfg.infof( 'compute: makevar SRC/ = %s\n', self.src_final ) cfg.infof( 'compute: makevar BUILD/ = %s\n', self.build_final ) cfg.infof( 'compute: makevar PREFIX/ = %s\n', self.prefix_final ) ## perform chdir and enable log recording def chdir( self ): if os.path.abspath( self.build_dir ) == os.path.abspath( self.src_dir ): cfg.errln( 'build (scratch) directory must not be the same as top-level source root!' ) if self.build_dir != os.curdir: if os.path.exists( self.build_dir ): if not options.force: self.errln( 'build directory already exists: %s (use --force to overwrite)', self.build_dir ) else: self.mkdirs( self.build_dir ) self.infof( 'chdir: %s\n', self.build_dir ) os.chdir( self.build_dir ) ## enable logging self._record = True def mkdirs( self, dir ): if len(dir) and not os.path.exists( dir ): self.infof( 'mkdir: %s\n', dir ) os.makedirs( dir ) def open( self, *args ): dir = os.path.dirname( args[0] ) if len(args) > 1 and args[1].find('w') != -1: self.mkdirs( dir ) m = re.match( '^(.*)\.tmp$', args[0] ) if m: self.infof( 'write: %s\n', m.group(1) ) else: self.infof( 'write: %s\n', args[0] ) try: return open( *args ) except Exception, x: cfg.errln( 'open failure: %s', x ) def record_log( self ): if not self._record: return self._record = False self.verbose = Configure.OUT_QUIET file = cfg.open( 'log/config.info.txt', 'w' ) for line in self._log_info: file.write( line ) file.close() file = cfg.open( 'log/config.verbose.txt', 'w' ) for line in self._log_verbose: file.write( line ) file.close() ## Find executable by searching path. ## On success, returns full pathname of executable. ## On fail, returns None. def findExecutable( self, name ): if len( os.path.split(name)[0] ): if os.access( name, os.X_OK ): return name return None if not os.environ.has_key( 'PATH' ) or os.environ[ 'PATH' ] == '': path = os.defpath else: path = os.environ['PATH'] for dir in path.split( os.pathsep ): f = os.path.join( dir, name ) if os.access( f, os.X_OK ): return f return None ## taken from python2.6 -- we need it def relpath( self, path, start=os.curdir ): """Return a relative version of a path""" if not path: raise ValueError("no path specified") start_list = os.path.abspath(start).split(os.sep) path_list = os.path.abspath(path).split(os.sep) # Work out how much of the filepath is shared by start and path. i = len(os.path.commonprefix([start_list, path_list])) rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:] if not rel_list: return os.curdir return os.path.join(*rel_list) ## update with parsed cli options def update_cli( self, options ): self.src_dir = os.path.normpath( options.src ) self.build_dir = os.path.normpath( options.build ) self.prefix_dir = os.path.normpath( options.prefix ) if options.sysroot != None: self.sysroot_dir = os.path.normpath( options.sysroot ) else: self.sysroot_dir = "" if options.minver != None: self.minver = options.minver else: self.minver = "" ## special case if src == build: add build subdir if os.path.abspath( self.src_dir ) == os.path.abspath( self.build_dir ): self.build_dir = os.path.join( self.build_dir, 'build' ) ############################################################################### ## ## abstract action ## ## pretext = text which immediately follows 'probe:' output prefix ## abort = if true configure will exit on probe fail ## head = if true probe session is stripped of all but first line ## session = output from command, including stderr ## fail = true if probe failed ## class Action( object ): actions = [] def __init__( self, category, pretext='unknown', abort=False, head=False ): if self not in Action.actions: Action.actions.append( self ) self.category = category self.pretext = pretext self.abort = abort self.head = head self.session = None self.run_done = False self.fail = True self.msg_fail = 'fail' self.msg_pass = 'pass' self.msg_end = 'end' def _actionBegin( self ): cfg.infof( '%s: %s...', self.category, self.pretext ) def _actionEnd( self ): if self.fail: cfg.infof( '(%s) %s\n', self.msg_fail, self.msg_end ) if self.abort: self._dumpSession( cfg.infof ) cfg.errln( 'unable to continue' ) self._dumpSession( cfg.verbosef ) self._failSession() else: cfg.infof( '(%s) %s\n', self.msg_pass, self.msg_end ) self._dumpSession( cfg.verbosef ) def _dumpSession( self, printf ): if self.session and len(self.session): for line in self.session: printf( ' : %s\n', line ) else: printf( ' : \n' ) def _parseSession( self ): pass def _failSession( self ): pass def run( self ): if self.run_done: return self.run_done = True self._actionBegin() self._action() if not self.fail: self._parseSession() self._actionEnd() ############################################################################### ## ## base probe: anything which runs in shell. ## ## pretext = text which immediately follows 'probe:' output prefix ## command = full command and arguments to pipe ## abort = if true configure will exit on probe fail ## head = if true probe session is stripped of all but first line ## session = output from command, including stderr ## fail = true if probe failed ## class ShellProbe( Action ): def __init__( self, pretext, command, abort=False, head=False ): super( ShellProbe, self ).__init__( 'probe', pretext, abort, head ) self.command = command def _action( self ): ## pipe and redirect stderr to stdout; effects communicate result pipe = subprocess.Popen( self.command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) ## read data into memory buffers, only first element (stdout) data is used data = pipe.communicate() self.fail = pipe.returncode != 0 if data[0]: self.session = data[0].splitlines() else: self.session = [] if pipe.returncode: self.msg_end = 'code %d' % (pipe.returncode) def _dumpSession( self, printf ): printf( ' + %s\n', self.command ) super( ShellProbe, self )._dumpSession( printf ) ############################################################################### ## ## Compile test probe: determine if compile time feature is supported ## ## returns true if feature successfully compiles ## ## class CCProbe( Action ): def __init__( self, pretext, command, test_file ): super( CCProbe, self ).__init__( 'probe', pretext ) self.command = command self.test_file = test_file def _action( self ): ## write program file file = open( 'conftest.c', 'w' ) file.write( self.test_file ) file.close() ## pipe and redirect stderr to stdout; effects communicate result pipe = subprocess.Popen( '%s -c -o conftest.o conftest.c' % self.command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) ## read data into memory buffers, only first element (stdout) data is used data = pipe.communicate() self.fail = pipe.returncode != 0 if data[0]: self.session = data[0].splitlines() else: self.session = [] if pipe.returncode: self.msg_end = 'code %d' % (pipe.returncode) os.remove( 'conftest.c' ) if not self.fail: os.remove( 'conftest.o' ) def _dumpSession( self, printf ): printf( ' + %s\n', self.command ) super( CCProbe, self )._dumpSession( printf ) ############################################################################### ## ## Compile test probe: determine if compile time feature is supported ## ## returns true if feature successfully compiles ## ## class LDProbe( Action ): def __init__( self, pretext, command, lib, test_file ): super( LDProbe, self ).__init__( 'probe', pretext ) self.command = command self.test_file = test_file self.lib = lib def _action( self ): ## write program file file = open( 'conftest.c', 'w' ) file.write( self.test_file ) file.close() ## pipe and redirect stderr to stdout; effects communicate result pipe = subprocess.Popen( '%s -o conftest conftest.c %s' % (self.command, self.lib), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) ## read data into memory buffers, only first element (stdout) data is used data = pipe.communicate() self.fail = pipe.returncode != 0 if data[0]: self.session = data[0].splitlines() else: self.session = [] if pipe.returncode: self.msg_end = 'code %d' % (pipe.returncode) os.remove( 'conftest.c' ) if not self.fail: os.remove( 'conftest' ) def _dumpSession( self, printf ): printf( ' + %s\n', self.command ) super( LDProbe, self )._dumpSession( printf ) ############################################################################### ## ## GNU host tuple probe: determine canonical platform type ## ## example results from various platforms: ## ## powerpc-apple-darwin9.6.0 (Mac OS X 10.5.6 PPC) ## i386-apple-darwin9.6.0 (Mac OS X 10.5.6 Intel) ## x86_64-apple-darwin10.8.0 (Mac OS X 10.6.8 Intel) ## x86_64-apple-darwin11.2.0 (Mac OS X 10.7.2 Intel) ## i686-pc-cygwin (Cygwin, Microsoft Vista) ## x86_64-unknown-linux-gnu (Linux, Fedora 10 x86_64) ## class HostTupleProbe( ShellProbe, list ): GNU_TUPLE_RE = '([^-]+)-?([^-]*)-([^0-9-]+)([^-]*)-?([^-]*)' def __init__( self ): super( HostTupleProbe, self ).__init__( 'host tuple', '%s/config.guess' % (cfg.dir), abort=True, head=True ) def _parseSession( self ): if len(self.session): self.spec = self.session[0] else: self.spec = '' ## grok GNU host tuples m = re.match( HostTupleProbe.GNU_TUPLE_RE, self.spec ) if not m: self.fail = True self.msg_end = 'invalid host tuple: %s' % (self.spec) return self.msg_end = self.spec ## assign tuple from regex self[:] = m.groups() ## for clarity self.machine = self[0] self.vendor = self[1] self.system = self[2] self.release = self[3] self.extra = self[4] ## nice formal name for 'system' self.systemf = platform.system() if self.match( '*-*-cygwin*' ): self.systemf = self[2][0].upper() + self[2][1:] ## glob-match against spec def match( self, *specs ): for spec in specs: if fnmatch.fnmatch( self.spec, spec ): return True return False ############################################################################### class BuildAction( Action, list ): def __init__( self ): super( BuildAction, self ).__init__( 'compute', 'build tuple', abort=True ) def _action( self ): ## check if --cross spec was used; must maintain 5-tuple compatibility with regex if options.cross: self.spec = os.path.basename( options.cross ).rstrip( '-' ) else: self.spec = arch.mode[arch.mode.mode] ## grok GNU host tuples m = re.match( HostTupleProbe.GNU_TUPLE_RE, self.spec ) if not m: self.msg_end = 'invalid host tuple: %s' % (self.spec) return self.msg_end = self.spec ## assign tuple from regex self[:] = m.groups() ## for clarity self.machine = self[0] self.vendor = self[1] self.system = self[2] self.release = self[3] self.extra = self[4] self.systemf = host.systemf ## when cross we need switch for platforms if options.cross: if self.match( '*mingw*' ): self.systemf = 'MinGW' elif self.systemf: self.systemf[0] = self.systemf[0].upper() self.title = '%s %s' % (build.systemf,self.machine) else: self.title = '%s %s' % (build.systemf,arch.mode.mode) self.fail = False ## glob-match against spec def match( self, *specs ): for spec in specs: if fnmatch.fnmatch( self.spec, spec ): return True return False ############################################################################### ## ## value wrapper; value is accepted only if one of host specs matcheds ## otherwise it is None (or a keyword-supplied val) ## ## result is attribute 'value' ## class IfHost( object ): def __init__( self, value, *specs, **kwargs ): self.value = kwargs.get('none',None) for spec in specs: if host.match( spec ): self.value = value break def __nonzero__( self ): return self.value != None def __str__( self ): return self.value ############################################################################### ## ## platform conditional value; loops through list of tuples comparing ## to first host match and sets value accordingly; the first value is ## always default. ## class ForHost( object ): def __init__( self, default, *tuples ): self.value = default for tuple in tuples: if host.match( tuple[1] ): self.value = tuple[0] break def __str__( self ): return self.value ############################################################################### class ArchAction( Action ): def __init__( self ): super( ArchAction, self ).__init__( 'compute', 'available architectures', abort=True ) self.mode = SelectMode( 'architecture', (host.machine,host.spec) ) def _action( self ): self.fail = False ## some match on system should be made here; otherwise we signal a warning. if host.match( '*-*-cygwin*' ): pass elif host.match( '*-*-darwin11.*' ): self.mode['i386'] = 'i386-apple-darwin%s' % (host.release) self.mode['x86_64'] = 'x86_64-apple-darwin%s' % (host.release) elif host.match( '*-*-darwin*' ): self.mode['i386'] = 'i386-apple-darwin%s' % (host.release) self.mode['x86_64'] = 'x86_64-apple-darwin%s' % (host.release) self.mode['ppc'] = 'powerpc-apple-darwin%s' % (host.release) self.mode['ppc64'] = 'powerpc64-apple-darwin%s' % (host.release) ## special cases in that powerpc does not match gcc -arch value ## which we like to use; so it has to be removed. ## note: we don't know if apple will release Ssnow Leopad/ppc64 yet; just a guess. if 'powerpc' in self.mode: del self.mode['powerpc'] self.mode.mode = 'ppc' elif 'powerpc64' in self.mode: del self.mode['powerpc64'] self.mode.mode = 'ppc64' elif host.match( '*-*-linux*' ): pass elif host.match( '*-*-solaris*' ): pass else: self.msg_pass = 'WARNING' self.msg_end = self.mode.toString() ## glob-match against spec def match( self, spec ): return fnmatch.fnmatch( self.spec, spec ) ############################################################################### class CoreProbe( Action ): def __init__( self ): super( CoreProbe, self ).__init__( 'probe', 'number of CPU cores' ) self.count = 1 def _action( self ): if self.fail: ## good for darwin9.6.0 and linux try: self.count = os.sysconf( 'SC_NPROCESSORS_ONLN' ) if self.count < 1: self.count = 1 self.fail = False except: pass if self.fail: ## windows try: self.count = int( os.environ['NUMBER_OF_PROCESSORS'] ) if self.count < 1: self.count = 1 self.fail = False except: pass ## clamp if self.count < 1: self.count = 1 elif self.count > 32: self.count = 32 if options.launch: if options.launch_jobs == 0: self.jobs = core.count else: self.jobs = options.launch_jobs else: self.jobs = core.count self.msg_end = str(self.count) ############################################################################### class SelectMode( dict ): def __init__( self, descr, *modes, **kwargs ): super( SelectMode, self ).__init__( modes ) self.descr = descr self.modes = modes self.what = kwargs.get('what',' mode') if modes: self.default = kwargs.get('default',modes[0][0]) else: self.default = None self.mode = self.default def cli_add_option( self, parser, option ): parser.add_option( option, default=self.mode, metavar='MODE', help='select %s%s: %s' % (self.descr,self.what,self.toString()), action='callback', callback=self.cli_callback, type='str' ) def cli_callback( self, option, opt_str, value, parser, *args, **kwargs ): if value not in self: raise optparse.OptionValueError( 'invalid %s%s: %s (choose from: %s)' % (self.descr,self.what,value,self.toString( True )) ) self.mode = value def toString( self, nodefault=False ): keys = self.keys() keys.sort() if len(self) == 1: value = self.mode elif nodefault: value = ' '.join( keys ) else: value = '%s [%s]' % (' '.join( keys ), self.mode ) return value ############################################################################### ## ## Repository object. ## Holds information gleaned from subversion working dir. ## ## Builds are classed into one of the following types: ## ## release ## must be built from official svn with '/tags/' in the url ## developer ## must be built from official svn but is not a release ## unofficial ## all other builds ## class RepoProbe( ShellProbe ): def __init__( self ): svn = 'svn' ## Possible the repo was created using an incompatible version than what is ## available in PATH when probe runs. Workaround by checking for file ## .svn/HANDBRAKE_REPO_PROBE which points to a preferred svn executable. try: hrp = os.path.join( cfg.src_dir, '.svn', 'HANDBRAKE_REPO_PROBE' ) if os.path.isfile( hrp ) and os.path.getsize( hrp ) > 0: file = cfg.open( hrp, 'r' ) line = file.readline().strip() file.close() if line: svn = line except: pass super( RepoProbe, self ).__init__( 'svn info', '%s info %s' % (svn,cfg.src_dir) ) self.url = 'svn://nowhere.com/project/unknown' self.root = 'svn://nowhere.com/project' self.branch = 'unknown' self.uuid = '00000000-0000-0000-0000-000000000000'; self.rev = 0 self.date = '0000-00-00 00:00:00 -0000' self.official = 0 self.type = 'unofficial' def _parseSession( self ): for line in self.session: ## grok fields m = re.match( '([^:]+):\\s+(.+)', line ) if not m: continue (name,value) = m.groups() if name == 'URL': self.url = value elif name == 'Repository Root': self.root = value elif name == 'Repository UUID': self.uuid = value elif name == 'Revision': self.rev = int( value ) elif name == 'Last Changed Date': # strip chars in parens if value.find( ' (' ): self.date = value[0:value.find(' (')] else: self.date = value ## grok branch i = self.url.rfind( '/' ) if i != -1 and i < len(self.url)-1: self.branch = self.url[i+1:] # type-classification via repository UUID if self.uuid == 'b64f7644-9d1e-0410-96f1-a4d463321fa5': self.official = 1 m = re.match( '([^:]+)://([^/]+)/(.+)', self.url ) if m and re.match( '.*tags/.*', m.group( 3 )): self.type = 'release' else: self.type = 'developer' self.msg_end = self.url def _failSession( self ): # Look for svn info in version file. # # Version file would be created manually by source packager. # e.g. # $ svn info HandBrake > HandBrake/version.txt # $ tar -czf handbrake-source.tgz --exclude .svn HandBrake cfg.infof( 'probe: version.txt...' ) try: hvp = os.path.join( cfg.src_dir, 'version.txt' ) if os.path.isfile( hvp ) and os.path.getsize( hvp ) > 0: file = open( hvp, 'r' ) self.session = file.readlines() file.close() if self.session: self._parseSession() if self.rev != 0: cfg.infof( '(pass)\n' ) else: cfg.infof( '(fail)\n' ) except: cfg.infof( '(fail)\n' ) ############################################################################### ## ## project object. ## ## Contains manually updated version numbers consistent with HB releases ## and other project metadata. ## class Project( Action ): def __init__( self ): super( Project, self ).__init__( 'compute', 'project data' ) self.name = 'HandBrake' self.acro_lower = 'hb' self.acro_upper = 'HB' self.url_website = 'https://handbrake.fr' self.url_community = 'https://forum.handbrake.fr' self.url_irc = 'irc://irc.freenode.net/handbrake' self.name_lower = self.name.lower() self.name_upper = self.name.upper() self.vmajor = 0 self.vminor = 10 self.vpoint = 2 def _action( self ): ## add architecture to URL only for Mac if fnmatch.fnmatch( build.spec, '*-*-darwin*' ): url_arch = '.%s' % (arch.mode.mode) else: url_arch = '' if repo.type == 'release': self.version = '%d.%d.%d' % (self.vmajor,self.vminor,self.vpoint) url_ctype = '' url_ntype = 'stable' self.build = time.strftime('%Y%m%d') + '00' self.title = '%s %s (%s)' % (self.name,self.version,self.build) elif repo.type == 'developer': self.version = '%dsvn' % (repo.rev) url_ctype = '_unstable' url_ntype = 'unstable' self.build = time.strftime('%Y%m%d') + '01' self.title = '%s svn%d (%s)' % (self.name,repo.rev,self.build) else: self.version = 'rev%d' % (repo.rev) url_ctype = '_unofficial' url_ntype = 'unofficial' self.build = time.strftime('%Y%m%d') + '99' self.title = '%s rev%d (%s)' % (self.name,repo.rev,self.build) self.url_appcast = 'https://handbrake.fr/appcast%s%s.xml' % (url_ctype,url_arch) self.url_appnote = 'https://handbrake.fr/appcast/%s.html' % (url_ntype) self.msg_end = '%s (%s)' % (self.name,repo.type) self.fail = False ############################################################################### class ToolProbe( Action ): tools = [] def __init__( self, var, *names, **kwargs ): super( ToolProbe, self ).__init__( 'find', abort=kwargs.get('abort',True) ) if not self in ToolProbe.tools: ToolProbe.tools.append( self ) self.var = var self.names = [] self.kwargs = kwargs for name in names: if name: self.names.append( str(name) ) self.name = self.names[0] self.pretext = self.name self.pathname = self.names[0] self.minversion = kwargs.get('minversion', None) def _action( self ): self.session = [] for i,name in enumerate(self.names): self.session.append( 'name[%d] = %s' % (i,name) ) for name in self.names: f = cfg.findExecutable( name ) if f: self.pathname = f self.fail = False self.msg_end = f break if self.fail: self.msg_end = 'not found' elif self.minversion: self.version = VersionProbe( [self.pathname, '--version'], minversion=self.minversion ) def cli_add_option( self, parser ): parser.add_option( '--'+self.name, metavar='PROG', help='[%s]' % (self.pathname), action='callback', callback=self.cli_callback, type='str' ) def cli_callback( self, option, opt_str, value, parser, *args, **kwargs ): self.__init__( self.var, value, **self.kwargs ) self.run() def doc_add( self, doc ): doc.add( self.var, self.pathname ) ############################################################################### ############################################################################### ## ## version probe: passes --version to command and only cares about first line ## of output. If probe fails, a default version of '0.0.0' results. ## The default rexpr is useful for some very simple version strings. A Custom ## expression would be required for more complex version strings. ## ## command = full command and arguments to pipe ## rexpr = a regular expression which must return named subgroups: ## name: mandatory. The tool name. ## svers: mandatory. The whole version tuple to be represented as string. ## i0: mandatory. First element of version tuple to be parsed as int. ## i1: optional. Second element of version tuple to be parsed as int. ## i2: optional. Third element of version tuple to be parsed as int. ## All matching is case-insensitive. ## abort = if true configure will exit on probe fail ## session = result. array of lines (stdout/stderr) from command ## fail = result. true if probe failed ## svers = result. string of version tuple ## ivers = result. int[3] of version tuple ## class VersionProbe( Action ): def __init__( self, command, minversion=None, rexpr=None, abort=False ): super( VersionProbe, self ).__init__( 'version probe', os.path.basename(command[0]), abort ) self.command = command self.minversion = minversion if not rexpr: rexpr = '(?P[^.]+)\s+(?P(?P\d+)(\.(?P\d+))?(\.(?P\d+))?)' self.rexpr = rexpr def _action( self ): ## pipe and redirect stderr to stdout; effects communicate result pipe = subprocess.Popen( self.command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) ## read data into memory buffers data = pipe.communicate() self.fail = pipe.returncode != 0 if data[0]: self.session = data[0].splitlines() else: self.session = [] self.svers = '0.0.0' self.ivers = [0,0,0] try: if not self.fail and self.session and len(self.session): self.fail = True self._parse() self.fail = False self.msg_end = self.svers except Exception, x: self.svers = '0.0.0' self.ivers = [0,0,0] self.msg_end = str(x) def _dumpSession( self, printf ): printf( ' + %s\n', ' '.join(self.command) ) super( VersionProbe, self )._dumpSession( printf ) def _parse( self ): mo = re.match( self.rexpr, self.session[0], re.IGNORECASE ) md = mo.groupdict() self.svers = md['svers'] if 'i0' in md and md['i0']: self.ivers[0] = int(md['i0']) if 'i1' in md and md['i1']: self.ivers[1] = int(md['i1']) if 'i2' in md and md['i2']: self.ivers[2] = int(md['i2']) def inadequate( self ): if not self.minversion: return False return self.lesser( self.minversion ) def lesser( self, ivers ): for i in range(0,3): if self.ivers[i] < ivers[i]: return True elif self.ivers[i] > ivers[i]: return False return False ############################################################################### class SelectTool( Action ): selects = [] def __init__( self, var, name, *pool, **kwargs ): super( SelectTool, self ).__init__( 'select', abort=kwargs.get('abort',True) ) self.pretext = name if not self in SelectTool.selects: SelectTool.selects.append( self ) self.var = var self.name = name self.pool = pool self.kwargs = kwargs def _action( self ): self.session = [] for i,(name,tool) in enumerate(self.pool): self.session.append( 'tool[%d] = %s (%s)' % (i,name,tool.pathname) ) for (name,tool) in self.pool: if not tool.fail: self.selected = name self.fail = False self.msg_end = '%s (%s)' % (name,tool.pathname) break if self.fail: self.msg_end = 'not found' def cli_add_option( self, parser ): parser.add_option( '--'+self.name, metavar='MODE', help='select %s mode: %s' % (self.name,self.toString()), action='callback', callback=self.cli_callback, type='str' ) def cli_callback( self, option, opt_str, value, parser, *args, **kwargs ): found = False for (name,tool) in self.pool: if name == value: found = True self.__init__( self.var, self.name, [name,tool], **kwargs ) self.run() break if not found: raise optparse.OptionValueError( 'invalid %s mode: %s (choose from: %s)' % (self.name,value,self.toString( True )) ) def doc_add( self, doc ): doc.add( self.var, self.selected ) def toString( self, nodefault=False ): if len(self.pool) == 1: value = self.pool[0][0] else: s = '' for key,value in self.pool: s += ' ' + key if nodefault: value = s[1:] else: value = '%s [%s]' % (s[1:], self.selected ) return value ############################################################################### ## ## config object used to output gnu-make or gnu-m4 output. ## ## - add() to add NAME/VALUE pairs suitable for both make/m4. ## - addBlank() to add a linefeed for both make/m4. ## - addMake() to add a make-specific line. ## - addM4() to add a m4-specific line. ## class ConfigDocument: def __init__( self ): self._elements = [] def _outputMake( self, file, namelen, name, value, append ): if append: if value == None or len(str(value)) == 0: file.write( '%-*s +=\n' % (namelen, name) ) else: file.write( '%-*s += %s\n' % (namelen, name, value) ) else: if value == None or len(str(value)) == 0: file.write( '%-*s =\n' % (namelen, name) ) else: file.write( '%-*s = %s\n' % (namelen, name, value) ) def _outputM4( self, file, namelen, name, value ): namelen += 7 name = '<<__%s>>,' % name.replace( '.', '_' ) file.write( 'define(%-*s <<%s>>)dnl\n' % (namelen, name, value )) def add( self, name, value, append=False ): self._elements.append( [name,value,append] ) def addBlank( self ): self._elements.append( None ) def addComment( self, format, *args ): self.addMake( '## ' + format % args ) self.addM4( 'dnl ' + format % args ) def addMake( self, line ): self._elements.append( ('?make',line) ) def addM4( self, line ): self._elements.append( ('?m4',line) ) def output( self, file, type ): namelen = 0 for item in self._elements: if item == None or item[0].find( '?' ) == 0: continue if len(item[0]) > namelen: namelen = len(item[0]) for item in self._elements: if item == None: if type == 'm4': file.write( 'dnl\n' ) else: file.write( '\n' ) continue if item[0].find( '?' ) == 0: if item[0].find( type, 1 ) == 1: file.write( '%s\n' % (item[1]) ) continue if type == 'm4': self._outputM4( file, namelen, item[0], item[1] ) else: self._outputMake( file, namelen, item[0], item[1], item[2] ) def update( self, name, value ): for item in self._elements: if item == None: continue if item[0] == name: item[1] = value return raise ValueError( 'element not found: %s' % (name) ) def write( self, type ): if type == 'make': fname = 'GNUmakefile' elif type == 'm4': fname = os.path.join( 'project', project.name_lower + '.m4' ) else: raise ValueError, 'unknown file type: ' + type ftmp = fname + '.tmp' try: try: file = cfg.open( ftmp, 'w' ) self.output( file, type ) finally: try: file.close() except: pass except Exception, x: try: os.remove( ftmp ) except Exception, x: pass cfg.errln( 'failed writing to %s\n%s', ftmp, x ) try: os.rename( ftmp, fname ) except Exception, x: cfg.errln( 'failed writing to %s\n%s', fname, x ) ############################################################################### ## ## create cli parser ## ## class to hook options and create CONF.args list class Option( optparse.Option ): conf_args = [] def _conf_record( self, opt, value ): ## filter out non-applicable options if re.match( '^--(force|launch).*$', opt ): return ## remove duplicates (last duplicate wins) for i,arg in enumerate( Option.conf_args ): if opt == arg[0]: del Option.conf_args[i] break if value: Option.conf_args.append( [opt,'%s=%s' % (opt,value)] ) else: Option.conf_args.append( [opt,'%s' % (opt)] ) def take_action( self, action, dest, opt, value, values, parser ): self._conf_record( opt, value ) return optparse.Option.take_action( self, action, dest, opt, value, values, parser ) def createCLI(): cli = OptionParser( 'usage: %prog [OPTIONS...] [TARGETS...]' ) cli.option_class = Option cli.description = '' cli.description += 'Configure %s build system.' % (project.name) ## add hidden options cli.add_option( '--xcode-driver', default='bootstrap', action='store', help=optparse.SUPPRESS_HELP ) cli.add_option( '--force', default=False, action='store_true', help='overwrite existing build config' ) cli.add_option( '--verbose', default=False, action='store_true', help='increase verbosity' ) ## add install options grp = OptionGroup( cli, 'Directory Locations' ) h = IfHost( 'specify sysroot of SDK for Xcode builds', '*-*-darwin*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--sysroot', default=None, action='store', metavar='DIR', help=h ) grp.add_option( '--src', default=cfg.src_dir, action='store', metavar='DIR', help='specify top-level source dir [%s]' % (cfg.src_dir) ) grp.add_option( '--build', default=cfg.build_dir, action='store', metavar='DIR', help='specify build scratch/output dir [%s]' % (cfg.build_dir) ) grp.add_option( '--prefix', default=cfg.prefix_dir, action='store', metavar='DIR', help='specify install dir for products [%s]' % (cfg.prefix_dir) ) cli.add_option_group( grp ) ## add feature options grp = OptionGroup( cli, 'Feature Options' ) h = IfHost( 'enable assembly code in non-contrib modules', 'NOMATCH*-*-darwin*', 'NOMATCH*-*-linux*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--enable-asm', default=False, action='store_true', help=h ) h = IfHost( 'disable GTK GUI', '*-*-linux*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--disable-gtk', default=False, action='store_true', help=h ) h = IfHost( 'disable GTK GUI update checks', '*-*-linux*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--disable-gtk-update-checks', default=False, action='store_true', help=h ) h = IfHost( 'enable GTK GUI (mingw)', '*-*-mingw*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--enable-gtk-mingw', default=False, action='store_true', help=h ) h = IfHost( 'disable gstreamer (live preview)', '*-*-linux*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--disable-gst', default=False, action='store_true', help=h ) h = IfHost( 'enable use of Intel Quick Sync Video hardware acceleration', '*-*-*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--enable-qsv', default=False, action='store_true', help=h ) h = IfHost( 'enable HWD features', '*-*-*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--enable-hwd', default=False, action='store_true', help=h ) h = IfHost( 'enable use of x265 encoding', '*-*-*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--enable-x265', default=True, action='store_true', help=h ) grp.add_option( '--disable-x265', dest="enable_x265", action='store_false' ) h = IfHost( 'enable use of fdk-aac encoder', '*-*-*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--enable-fdk-aac', dest="enable_fdk_aac", default=not host.match( '*-*-darwin*' ), action='store_true', help=h ) grp.add_option( '--disable-fdk-aac', dest="enable_fdk_aac", action='store_false' ) h = IfHost( 'enable use of libav aac encoder', '*-*-*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--enable-libav-aac', dest="enable_libav_aac", default=not host.match( '*-*-darwin*' ), action='store_true', help=h ) grp.add_option( '--disable-libav-aac', dest="enable_libav_aac", action='store_false' ) cli.add_option_group( grp ) ## add launch options grp = OptionGroup( cli, 'Launch Options' ) grp.add_option( '--launch', default=False, action='store_true', help='launch build, capture log and wait for completion' ) grp.add_option( '--launch-jobs', default=1, action='store', metavar='N', type='int', help='allow N jobs at once; 0 to match CPU count [1]' ) grp.add_option( '--launch-args', default=None, action='store', metavar='ARGS', help='specify additional ARGS for launch command' ) grp.add_option( '--launch-quiet', default=False, action='store_true', help='do not echo build output while waiting' ) cli.add_option_group( grp ) ## add compile options grp = OptionGroup( cli, 'Compiler Options' ) debugMode.cli_add_option( grp, '--debug' ) optimizeMode.cli_add_option( grp, '--optimize' ) arch.mode.cli_add_option( grp, '--arch' ) grp.add_option( '--cross', default=None, action='store', metavar='SPEC', help='specify GCC cross-compilation spec' ) h = IfHost( 'specify Mac OS X deployment target for Xcode builds', '*-*-darwin*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--minver', default=None, action='store', metavar='VER', help=h ) h = IfHost( 'Build and use local yasm', '*-*-*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--enable-local-yasm', default=False, action='store_true', help=h ) h = IfHost( 'Build and use local autotools', '*-*-*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--enable-local-autotools', default=False, action='store_true', help=h ) h = IfHost( 'Build and use local cmake', '*-*-*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--enable-local-cmake', default=False, action='store_true', help=h ) h = IfHost( 'Build and use local pkg-config', '*-*-darwin*', none=optparse.SUPPRESS_HELP ).value grp.add_option( '--enable-local-pkgconfig', default=False, action='store_true', help=h ) cli.add_option_group( grp ) ## add Xcode options if host.match( '*-*-darwin*' ): grp = OptionGroup( cli, 'Xcode Options' ) grp.add_option( '--disable-xcode', default=False, action='store_true', help='disable Xcode' ) grp.add_option( '--xcode-symroot', default='xroot', action='store', metavar='DIR', help='specify root of the directory hierarchy that contains product files and intermediate build files' ) xcconfigMode.cli_add_option( grp, '--xcode-config' ) cli.add_option_group( grp ) ## add tool locations grp = OptionGroup( cli, 'Tool Basenames and Locations' ) for tool in ToolProbe.tools: tool.cli_add_option( grp ) cli.add_option_group( grp ) ## add tool modes grp = OptionGroup( cli, 'Tool Options' ) for select in SelectTool.selects: select.cli_add_option( grp ) cli.add_option_group( grp ) return cli ############################################################################### ## ## launcher - used for QuickStart method; launch; build and capture log. ## class Launcher: def __init__( self, targets ): # open build logfile self._file = cfg.open( 'log/build.txt', 'w' ) cmd = '%s -j%d' % (Tools.gmake.pathname,core.jobs) if options.launch_args: cmd += ' ' + options.launch_args if len(targets): cmd += ' ' + ' '.join(targets) ## record begin timeBegin = time.time() self.infof( 'time begin: %s\n', time.asctime() ) self.infof( 'launch: %s\n', cmd ) if options.launch_quiet: stdout.write( 'building to %s ...\n' % (os.path.abspath( cfg.build_final ))) else: stdout.write( '%s\n' % ('-' * 79) ) ## launch/pipe try: pipe = subprocess.Popen( cmd, shell=True, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) except Exception, x: cfg.errln( 'launch failure: %s', x ) for line in pipe.stdout: self.echof( '%s', line ) pipe.wait() ## record end timeEnd = time.time() elapsed = timeEnd - timeBegin if pipe.returncode: result = 'FAILURE (code %d)' % pipe.returncode else: result = 'SUCCESS' ## present duration in decent format seconds = elapsed hours = int(seconds / 3600) seconds -= hours * 3600 minutes = int(seconds / 60) seconds -= minutes * 60 segs = [] duration = '' if hours == 1: segs.append( '%d hour' % hours ) elif hours > 1: segs.append( '%d hours' % hours ) if len(segs) or minutes == 1: segs.append( '%d minute' % minutes ) elif len(segs) or minutes > 1: segs.append( '%d minutes' % minutes ) if seconds == 1: segs.append( '%d second' % seconds ) else: segs.append( '%d seconds' % seconds ) if not options.launch_quiet: stdout.write( '%s\n' % ('-' * 79) ) self.infof( 'time end: %s\n', time.asctime() ) self.infof( 'duration: %s (%.2fs)\n', ', '.join(segs), elapsed ) self.infof( 'result: %s\n', result ) ## cleanup self._file.close() def echof( self, format, *args ): line = format % args self._file.write( line ) if not options.launch_quiet: stdout.write( ' : %s' % line ) stdout.flush() def infof( self, format, *args ): line = format % args self._file.write( line ) cfg.infof( '%s', line ) ############################################################################### ## ## main program ## try: ## we need to pre-check argv for -h or --help or --verbose to deal with ## initializing Configure correctly. verbose = Configure.OUT_INFO for arg in sys.argv: if arg == '-h' or arg == '--help': verbose = Configure.OUT_QUIET break if arg == '--verbose': verbose = Configure.OUT_VERBOSE ## create main objects; actions/probes run() is delayed. ## if any actions must be run earlier (eg: for configure --help purposes) ## then run() must be invoked earlier. subequent run() invocations ## are ignored. cfg = Configure( verbose ) host = HostTupleProbe(); host.run() cfg.prefix_dir = ForHost( '/usr/local', ['/Applications','*-*-darwin*'] ).value build = BuildAction() arch = ArchAction(); arch.run() ## create remaining main objects core = CoreProbe() repo = RepoProbe() project = Project() ## create tools in a scope class Tools: ar = ToolProbe( 'AR.exe', 'ar' ) cp = ToolProbe( 'CP.exe', 'cp' ) curl = ToolProbe( 'CURL.exe', 'curl', abort=False ) gcc = ToolProbe( 'GCC.gcc', 'gcc', IfHost( 'gcc-4', '*-*-cygwin*' )) if host.match( '*-*-darwin*' ): gmake = ToolProbe( 'GMAKE.exe', 'make', 'gmake' ) else: gmake = ToolProbe( 'GMAKE.exe', 'gmake', 'make' ) m4 = ToolProbe( 'M4.exe', 'm4' ) mkdir = ToolProbe( 'MKDIR.exe', 'mkdir' ) patch = ToolProbe( 'PATCH.exe', 'gpatch', 'patch' ) rm = ToolProbe( 'RM.exe', 'rm' ) ranlib = ToolProbe( 'RANLIB.exe', 'ranlib' ) strip = ToolProbe( 'STRIP.exe', 'strip' ) tar = ToolProbe( 'TAR.exe', 'gtar', 'tar' ) wget = ToolProbe( 'WGET.exe', 'wget', abort=False ) yasm = ToolProbe( 'YASM.exe', 'yasm', abort=False, minversion=[1,2,0] ) autoconf = ToolProbe( 'AUTOCONF.exe', 'autoconf', abort=False ) automake = ToolProbe( 'AUTOMAKE.exe', 'automake', abort=False ) cmake = ToolProbe( 'CMAKE.exe', 'cmake', abort=False ) libtool = ToolProbe( 'LIBTOOL.exe', 'libtool', abort=False ) pkgconfig = ToolProbe( 'PKGCONFIG.exe', 'pkg-config', abort=False ) xcodebuild = ToolProbe( 'XCODEBUILD.exe', 'xcodebuild', abort=False ) lipo = ToolProbe( 'LIPO.exe', 'lipo', abort=False ) fetch = SelectTool( 'FETCH.select', 'fetch', ['wget',wget], ['curl',curl] ) ## run tool probes for tool in ToolProbe.tools: tool.run() for select in SelectTool.selects: select.run() debugMode = SelectMode( 'debug', ('none','none'), ('min','min'), ('std','std'), ('max','max') ) optimizeMode = SelectMode( 'optimize', ('none','none'), ('speed','speed'), ('size','size'), default='speed' ) ## find xcconfig values xcconfigMode = SelectMode( 'xcconfig', ('none',None), what='' ) if host.match( '*-*-darwin*' ): for xc in glob.glob( os.path.join(cfg.dir, '../macosx/xcconfig/*.xcconfig') ): bname = os.path.basename( xc ) xname = os.path.splitext( bname ) if xname and xname[0]: xcconfigMode[xname[0]] = bname if not 'native' in xcconfigMode: raise Exception( 'native xcconfig not found' ) xcconfigMode.default = 'native' xcconfigMode.mode = xcconfigMode.default ## create CLI and parse cli = createCLI() (options,args) = cli.parse_args() ## update cfg with cli directory locations cfg.update_cli( options ) ## prepare list of targets and NAME=VALUE args to pass to make targets = [] exports = [] rx_exports = re.compile( '([^=]+)=(.*)' ) for arg in args: m = rx_exports.match( arg ) if m: exports.append( m.groups() ) else: targets.append( arg ) ## re-run tools with cross-compilation needs if options.cross: for tool in ( Tools.ar, Tools.gcc, Tools.ranlib, Tools.strip ): tool.__init__( tool.var, '%s-%s' % (options.cross,tool.name), **tool.kwargs ) tool.run() ## run delayed actions for action in Action.actions: action.run() ## enable local yasm when yasm probe fails or version is too old ## x264 requires 1.2.0+ if not options.enable_local_yasm: if Tools.yasm.fail: stdout.write( 'note: enabling local yasm: missing system yasm\n' ) options.enable_local_yasm = True elif Tools.yasm.version.inadequate(): stdout.write( 'note: enabling local yasm: minimum required version is %s and %s is %s\n' % ('.'.join([str(i) for i in Tools.yasm.version.minversion]),Tools.yasm.pathname,Tools.yasm.version.svers) ) options.enable_local_yasm = True ## enable local autotools when any of { autoconf, automake, libtool } probe fails if not options.enable_local_autotools and (Tools.autoconf.fail or Tools.automake.fail or Tools.libtool.fail): stdout.write( 'note: enabling local autotools\n' ) options.enable_local_autotools = True ## enable local cmake when cmake probe fails if not options.enable_local_cmake and (Tools.cmake.fail): stdout.write( 'note: enabling local cmake\n' ) options.enable_local_cmake = True ## enable local pkg-config when probe fails if not options.enable_local_pkgconfig and Tools.pkgconfig.fail: stdout.write( 'note: enabling local pkgconfig\n' ) options.enable_local_pkgconfig = True if build.system == 'mingw': dlfcn_test = """ #include #include void fnord() { int i=42;} int main () { void *self = dlopen (0, RTLD_GLOBAL|RTLD_NOW); int status = 1; if (self) { if (dlsym (self,"fnord")) status = 0; else if (dlsym( self,"_fnord")) status = 0; /* dlclose (self); */ } else puts (dlerror ()); return status; } """ dlfcn = LDProbe( 'static dlfcn', '%s -static' % Tools.gcc.pathname, '-ldl', dlfcn_test ) dlfcn.run() pthread_test = """ #include #include int main () { pthread_t thread; pthread_create (&thread, NULL, NULL, NULL); return 0; } """ pthread = LDProbe( 'static pthread', '%s -static' % Tools.gcc.pathname, '-lpthreadGC2', pthread_test ) pthread.run() bz2_test = """ #include #include int main () { BZ2_bzReadOpen(NULL, NULL, 0, 0, NULL, 0); return 0; } """ bz2 = LDProbe( 'static bz2', '%s -static' % Tools.gcc.pathname, '-lbz2', bz2_test ) bz2.run() libz_test = """ #include #include int main () { compress(NULL, NULL, NULL, 0); return 0; } """ libz = LDProbe( 'static zlib', '%s -static' % Tools.gcc.pathname, '-lz', libz_test ) libz.run() iconv_test = """ #include #include int main () { iconv_open(NULL, NULL); return 0; } """ iconv = LDProbe( 'static iconv', '%s -static' % Tools.gcc.pathname, '-liconv', iconv_test ) iconv.run() regex_test = """ #include #include int match(regex_t *x, char *s) { regmatch_t matches[1]; return regexec(x, s, 1, matches, 0); } int main() { int rv; regex_t exp; rv = regcomp(&exp, "^[0-9]+$";", REG_EXTENDED); if (rv != 0) { return 1; } if (match(&exp, "7") != 0) { return 1; } if (match(&exp, "foo") == 0) { return 1; } regfree(&exp); return 0; } """ regex = LDProbe( 'static regex', '%s -static' % Tools.gcc.pathname, '-lregex', regex_test ) regex.run() ## cfg hook before doc prep cfg.doc_ready() ## create document object doc = ConfigDocument() doc.addComment( 'generated by configure on %s', time.strftime( '%c' )) ## add configure line for reconfigure purposes doc.addBlank() args = [] for arg in Option.conf_args: args.append( arg[1] ) doc.add( 'CONF.args', ' '.join( args )) doc.addBlank() doc.add( 'HB.title', project.title ) doc.add( 'HB.name', project.name ) doc.add( 'HB.name.lower', project.name_lower ) doc.add( 'HB.name.upper', project.name_upper ) doc.add( 'HB.acro.lower', project.acro_lower ) doc.add( 'HB.acro.upper', project.acro_upper ) doc.add( 'HB.url.website', project.url_website ) doc.add( 'HB.url.community', project.url_community ) doc.add( 'HB.url.irc', project.url_irc ) doc.add( 'HB.url.appcast', project.url_appcast ) doc.add( 'HB.url.appnote', project.url_appnote ) doc.add( 'HB.version.major', project.vmajor ) doc.add( 'HB.version.minor', project.vminor ) doc.add( 'HB.version.point', project.vpoint ) doc.add( 'HB.version', project.version ) doc.add( 'HB.version.hex', '%04x%02x%02x%08x' % (project.vmajor,project.vminor,project.vpoint,repo.rev) ) doc.add( 'HB.build', project.build ) doc.add( 'HB.repo.url', repo.url ) doc.add( 'HB.repo.root', repo.root ) doc.add( 'HB.repo.branch', repo.branch ) doc.add( 'HB.repo.uuid', repo.uuid ) doc.add( 'HB.repo.rev', repo.rev ) doc.add( 'HB.repo.date', repo.date ) doc.add( 'HB.repo.official', repo.official ) doc.add( 'HB.repo.type', repo.type ) doc.addBlank() doc.add( 'HOST.spec', host.spec ) doc.add( 'HOST.machine', host.machine ) doc.add( 'HOST.vendor', host.vendor ) doc.add( 'HOST.system', host.system ) doc.add( 'HOST.systemf', host.systemf ) doc.add( 'HOST.release', host.release ) doc.add( 'HOST.extra', host.extra ) doc.add( 'HOST.title', '%s %s' % (host.systemf,arch.mode.default) ) doc.add( 'HOST.ncpu', core.count ) doc.addBlank() doc.add( 'BUILD.spec', build.spec ) doc.add( 'BUILD.machine', build.machine ) doc.add( 'BUILD.vendor', build.vendor ) doc.add( 'BUILD.system', build.system ) doc.add( 'BUILD.systemf', build.systemf ) doc.add( 'BUILD.release', build.release ) doc.add( 'BUILD.extra', build.extra ) doc.add( 'BUILD.title', build.title ) doc.add( 'BUILD.ncpu', core.count ) doc.add( 'BUILD.jobs', core.jobs ) doc.add( 'BUILD.cross', int(options.cross != None or arch.mode.mode != arch.mode.default) ) if options.cross: doc.add( 'BUILD.cross.prefix', '%s-' % (options.cross) ) else: doc.add( 'BUILD.cross.prefix', '' ) doc.add( 'BUILD.date', time.strftime('%c') ) doc.add( 'BUILD.arch', arch.mode.mode ) doc.addBlank() doc.add( 'SRC', cfg.src_final ) doc.add( 'SRC/', cfg.src_final + os.sep ) doc.add( 'BUILD', cfg.build_final ) doc.add( 'BUILD/', cfg.build_final + os.sep ) doc.add( 'PREFIX', cfg.prefix_final ) doc.add( 'PREFIX/', cfg.prefix_final + os.sep ) doc.addBlank() doc.add( 'FEATURE.local_yasm', int( options.enable_local_yasm )) doc.add( 'FEATURE.local_autotools', int( options.enable_local_autotools )) doc.add( 'FEATURE.local_cmake', int( options.enable_local_cmake )) doc.add( 'FEATURE.local_pkgconfig', int( options.enable_local_pkgconfig )) doc.add( 'FEATURE.asm', 'disabled' ) doc.add( 'FEATURE.gtk', int( not options.disable_gtk )) doc.add( 'FEATURE.gtk.update.checks', int( not options.disable_gtk_update_checks )) doc.add( 'FEATURE.gtk.mingw', int( options.enable_gtk_mingw )) doc.add( 'FEATURE.gst', int( not options.disable_gst )) doc.add( 'FEATURE.fdk_aac', int( options.enable_fdk_aac )) doc.add( 'FEATURE.libav_aac', int( options.enable_libav_aac )) doc.add( 'FEATURE.qsv', int( options.enable_qsv )) doc.add( 'FEATURE.hwd', int( options.enable_hwd )) doc.add( 'FEATURE.xcode', int( not (Tools.xcodebuild.fail or options.disable_xcode or options.cross) )) doc.add( 'FEATURE.x265', int( options.enable_x265 )) if not Tools.xcodebuild.fail and not options.disable_xcode: doc.addBlank() doc.add( 'XCODE.driver', options.xcode_driver ) if os.path.isabs(options.xcode_symroot): doc.add( 'XCODE.symroot', options.xcode_symroot ) else: doc.add( 'XCODE.symroot', os.path.abspath(os.path.join(cfg.build_dir,options.xcode_symroot)) ) doc.add( 'XCODE.xcconfig', xcconfigMode[xcconfigMode.mode] ) if build.system == 'mingw': doc.addBlank() if not dlfcn.fail: doc.add( 'HAS.dlfcn', 1 ) if not pthread.fail: doc.add( 'HAS.pthread', 1 ) if not bz2.fail: doc.add( 'HAS.bz2', 1 ) if not libz.fail: doc.add( 'HAS.libz', 1 ) if not iconv.fail: doc.add( 'HAS.iconv', 1 ) if not regex.fail: doc.add( 'HAS.regex', 1 ) doc.addMake( '' ) doc.addMake( '## define debug mode and optimize before other includes' ) doc.addMake( '## since it is tested in some module.defs' ) doc.add( 'GCC.g', debugMode.mode ) doc.add( 'GCC.O', optimizeMode.mode ) doc.addBlank() doc.addMake( '## include definitions' ) doc.addMake( 'include $(SRC/)make/include/main.defs' ) doc.addBlank() for tool in ToolProbe.tools: tool.doc_add( doc ) doc.addBlank() for select in SelectTool.selects: select.doc_add( doc ) doc.addBlank() if build.match( '*-*-darwin*' ): doc.add( 'GCC.archs', arch.mode.mode ) doc.add( 'GCC.sysroot', cfg.sysroot_dir ) doc.add( 'GCC.minver', cfg.minver ) else: doc.add( 'GCC.archs', '' ) doc.add( 'GCC.sysroot', '' ) doc.add( 'GCC.minver', '' ) if build.match( 'i?86-*' ): doc.add( 'LIBHB.GCC.D', 'ARCH_X86_32', append=True ) elif build.match( 'x86_64-*' ): doc.add( 'LIBHB.GCC.D', 'ARCH_X86_64', append=True ) if options.enable_asm and ( not Tools.yasm.fail or options.enable_local_yasm ): asm = '' if build.match( 'i?86-*' ): asm = 'x86' doc.add( 'LIBHB.GCC.D', 'HAVE_MMX', append=True ) doc.add( 'LIBHB.YASM.D', 'ARCH_X86', append=True ) if build.match( '*-*-darwin*' ): doc.add( 'LIBHB.YASM.f', 'macho32' ) else: doc.add( 'LIBHB.YASM.f', 'elf32' ) doc.add( 'LIBHB.YASM.m', 'x86' ) elif build.match( 'x86_64-*' ): asm = 'x86' doc.add( 'LIBHB.GCC.D', 'HAVE_MMX ARCH_X86_64', append=True ) if build.match( '*-*-darwin*' ): doc.add( 'LIBHB.YASM.D', 'ARCH_X86_64 PIC', append=True ) doc.add( 'LIBHB.YASM.f', 'macho64' ) else: doc.add( 'LIBHB.YASM.D', 'ARCH_X86_64', append=True ) doc.add( 'LIBHB.YASM.f', 'elf64' ) doc.add( 'LIBHB.YASM.m', 'amd64' ) doc.update( 'FEATURE.asm', asm ) ## add exports to make if len(exports): doc.addBlank() doc.addComment( 'overrides via VARIABLE=VALUE on command-line' ) for nv in exports: doc.add( nv[0], nv[1] ) doc.addMake( '' ) doc.addMake( '## include custom definitions' ) doc.addMake( '-include $(SRC/)custom.defs' ) doc.addMake( '-include $(BUILD/)GNUmakefile.custom.defs' ) doc.addMake( '' ) doc.addMake( '## include rules' ) doc.addMake( 'include $(SRC/)make/include/main.rules' ) doc.addMake( '-include $(SRC/)custom.rules' ) doc.addMake( '-include $(BUILD/)GNUmakefile.custom.rules' ) ## chdir cfg.chdir() ## perform doc.write( 'make' ) doc.write( 'm4' ) if options.launch: Launcher( targets ) cfg.record_log() if os.path.normpath( cfg.build_dir ) == os.curdir: nocd = True else: nocd = False stdout.write( '%s\n' % ('-' * 79) ) if options.launch: stdout.write( 'Build is finished!\n' ) if nocd: stdout.write( 'You may now examine the output.\n' ) else: stdout.write( 'You may now cd into %s and examine the output.\n' % (cfg.build_dir) ) else: stdout.write( 'Build is configured!\n' ) if nocd: stdout.write( 'You may now run make (%s).\n' % (Tools.gmake.pathname) ) else: stdout.write( 'You may now cd into %s and run make (%s).\n' % (cfg.build_dir,Tools.gmake.pathname) ) except AbortError, x: stderr.write( 'ERROR: %s\n' % (x) ) try: cfg.record_log() except: pass sys.exit( 1 ) sys.exit( 0 ) HandBrake-0.10.2/make/test/0000775000175200017520000000000012535641641015742 5ustar handbrakehandbrakeHandBrake-0.10.2/make/test/build.matrix0000775000175200017520000000670111701017267020271 0ustar handbrakehandbrake#!/usr/bin/env ruby1.9 ## This script is used to launch a wide variety of builds for darwin. ## It is unsupported and is meant for use only with build-system testing. require 'pathname' require 'thread' ################################################################################ class Printer < Mutex def p(*args) synchronize { super } end def puts(*args) synchronize { super } end end $out = Printer.new ################################################################################ class Build def initialize(debug, xcconfig, method) @debug = debug @xcconfig = xcconfig @method = method if @xcconfig @dir = '_matrix.%s.%s.%s' % [@debug[0], @xcconfig, @method] else @dir = '_matrix.%s.%s' % [@debug[0], @method] end @configure = [] @make = [] end def doit p = Pathname.new(@dir) return if p.exist? p.mkdir @configure[0..0] += @debug[1].split @configure[0..0] += ["--build=#{@dir}"] @configure << ('--xcode-config=%s' % [@xcconfig]) if @xcconfig if !@make.empty? @make[0..0] += ['-C', @dir] end if !@configure.empty? return if !run(@configure) end if !@make.empty? return if !run(@make, true) end end private def run(args, append=false) s = args.join(' ') $out.puts s return Kernel.system('%s %s %s/matrix.log 2>&1' % [s, append ? '>>' : '>', @dir]) end end ################################################################################ class BuildTerminal < Build def initialize(debug, xcconfig) super(debug, xcconfig, 'term_make') @configure += './configure --force --disable-xcode'.split @make += 'make BUILD.jobs=1'.split end end class BuildLaunch < Build def initialize(debug, xcconfig) super(debug, xcconfig, 'launch_make') @configure += './configure --force --launch --launch-jobs=1 --disable-xcode'.split end end class BuildTerminalXcode < Build def initialize(debug, xcconfig) super(debug, xcconfig, 'term_xcode') @configure += './configure --force'.split @make += 'make BUILD.jobs=1'.split end end class BuildLaunchXcode < Build def initialize(debug, xcconfig) super(debug, xcconfig, 'launch_xcode') @configure += './configure --force --launch --launch-jobs=1'.split end end ################################################################################ ## probe ncpu begin case when RUBY_PLATFORM =~ /darwin/ workload = `sysctl -n hw.activecpu 2>/dev/null`[0].to_i end rescue workload = 1 end ## create work queue queue = Queue.new ## create xcconfig list xclist = [] case when RUBY_PLATFORM =~ /darwin11/ xclist += 'native osx106.i386 osx106.x86_64 osx107.i386 osx107.x86_64'.split when RUBY_PLATFORM =~ /darwin10/ xclist += 'native osx106.i386 osx106.x86_64'.split end ## fill queue [['release',''],['debug','--debug=max --optimize=none']].each do |debug| [BuildTerminal, BuildLaunch].each do |kind| queue << kind.new(debug, nil) end [BuildTerminalXcode, BuildLaunchXcode].each do |kind| xclist.each do |xcconfig| queue << kind.new(debug, xcconfig) end end end ## process queue workers = (1..workload).map do |i| queue << :finish Thread.new() do |worker| loop do item = queue.pop break if item == :finish begin item.doit rescue SystemExit break rescue puts 'whups' end end end end ## wait for all workers to finish workers.each(&:join) HandBrake-0.10.2/make/xcodemake0000775000175200017520000001267112220154570016647 0ustar handbrakehandbrake#!/bin/bash # set -e ## This script is initiated by either xcodebuild or Xcode.app. ## ## We must guarantee no jobserver is passed through since file-descriptors are ## clobbered by Xcode. If this is not done then make allows unlimited jobs. ## MAKEFLAGS= MFLAGS= ## validate environment case "$EXTERNAL_DRIVER" in bootstrap) ;; ## bootstrapping from terminal terminal) ;; ## building from terminal xcode) ;; ## building from Xcode.app *) echo "ERROR: unexpected value for EXTERNAL_DRIVER: $EXTERNAL_DRIVER" exit 1 ;; esac ## validate environment for name in EXTERNAL_BUILD EXTERNAL_JOBS EXTERNAL_SRC; do eval v="\$$name" if [ -z "$v" ]; then echo "ERROR: missing value for $name" exit 1 fi done ## validate environment archcount=`echo $ARCHS | awk '{ print NF }'` if [ "$archcount" -ne 1 ]; then echo "*********************************************************************" echo "***" echo "*** ERROR: multiple architectures: $ARCHS" echo "***" echo "*** This build system does not support more than one (1)" echo "*** simultaneous architecture setting." echo "***" echo "*********************************************************************" exit 1 fi exit_post_log=0 ## compute goals; these correlate with TARGET_NAME and ACTION from Xcode spec="$TARGET_NAME:$ACTION" echo "target specification: $spec" case "$spec" in external:clean) if [ "$EXTERNAL_DRIVER" == "xcode" ]; then ## driving build from Xcode.app do pristine clean if [ -z "$EXTERNAL_BUILD" ]; then echo "ERROR: unsafe rm -fr would result because EXTERNAL_BUILD is not defined" exit 1 fi if [ -d "$EXTERNAL_BUILD" ]; then cmd="/bin/rm -fr $EXTERNAL_BUILD/*" echo "$cmd" $cmd [ $? -ne 0 ] && exit 1 exit_post_log=1 else echo "already clean" fi else ## driving build from xcodebuild do xclean, preserving configuration goals=xclean fi ;; external:*) if [ -z "$EXTERNAL_GOALS" ]; then goals=build else goals="$EXTERNAL_GOALS" fi ;; *) echo "ERROR: unexpected env specification: $spec" exit 1 ;; esac ## compute if re/configure necessary if [ ! -f $EXTERNAL_BUILD/GNUmakefile ]; then reconfigure="no configuration present" elif [ $EXTERNAL_SRC/make/configure.py -nt $EXTERNAL_BUILD/GNUmakefile ]; then reconfigure="configure script was updated" elif [ $EXTERNAL_DRIVER == "bootstrap" ]; then reconfigure="driver bootstrap" else reconfigure= fi ## perform re/configure if [ -n "$reconfigure" ]; then echo "reconfiguring ($reconfigure)" if [ "$EXTERNAL_DRIVER" == "bootstrap" ]; then driver="--xcode-driver=terminal" else driver="--xcode-driver=$EXTERNAL_DRIVER" fi ## determine which compiler to use based on Xcode environment (project). case "$GCC_VERSION" in com.apple.compilers.llvmgcc42) gcc="--gcc=`$DEVELOPER_BIN_DIR/xcodebuild -find-executable llvm-gcc-4.2`" ;; com.apple.compilers.llvm.clang.1_0) gcc="--gcc=`$DEVELOPER_BIN_DIR/xcodebuild -find-executable clang`" ;; *) echo "*********************************************************************" echo "***" echo "*** ERROR: unexpected value for GCC_VERSION: $GCC_VERSION" echo "***" echo "*********************************************************************" exit 1 ;; esac if [ -n "$ARCHS" ]; then arch="--arch=$ARCHS" else arch= fi case "$CONFIGURATION" in debug*) debug="--debug=std --optimize=none" ;; release*|*) debug= ;; esac if [ -n "$SDKROOT" ]; then sysroot="--sysroot=$SDKROOT" else sysroot= fi if [ -n "$MACOSX_DEPLOYMENT_TARGET" ]; then minver="--minver=$MACOSX_DEPLOYMENT_TARGET" else minver= fi ## pickup user setting from Xcode IDE and avoid recursion if [ -n "$EXTERNAL_CONFIGURE" ]; then extconf="$EXTERNAL_CONFIGURE" else extconf= fi EXTERNAL_CONFIGURE= ## invoke configure with (hidden) option which indicates conf performed by xcode (set -ex; $EXTERNAL_SRC/configure --force \ $EXTERNAL_CONF_ARGS \ --build="$EXTERNAL_BUILD" \ $driver \ --xcode-symroot="$SYMROOT" \ --xcode-config="$EXTERNAL_XCCONFIG" \ $gcc $arch $debug $sysroot $minver $extconf) [ $? -ne 0 ] && exit 1 fi ## log environment as provided by Xcode logdir=$EXTERNAL_BUILD/log if [ ! -d $logdir ]; then mkdir -p $logdir fi env | sort > $logdir/xcodemake.env.txt [ $exit_post_log -ne 0 ] && exit 0 ## safeguard against passing blank value which would result in unlimited jobs if [ -z "$EXTERNAL_JOBS" ]; then jobs= elif [ "$EXTERNAL_JOBS" == "auto" ]; then jobs=--jobs=`sysctl -n hw.activecpu` else jobs=--jobs=$EXTERNAL_JOBS fi ## when driving from terminal; ensure $SYMROOT/external/ exists relative to SYMROOT if [ "$EXTERNAL_DRIVER" == "terminal" -a ! -e "$SYMROOT/external" ]; then ln -s "$EXTERNAL_BUILD" "$SYMROOT/external" fi ## pull the trigger ## must set XCODE.driver to prevent inifinite recursion set -x exec make -C $EXTERNAL_BUILD XCODE.driver=xcodemake $jobs $goals $EXTERNAL_VARS HandBrake-0.10.2/make/include/0000775000175200017520000000000012535641641016406 5ustar handbrakehandbrakeHandBrake-0.10.2/make/include/tool.defs0000664000175200017520000000032211460625006020214 0ustar handbrakehandbrakeAR.exe = ar CP.exe = cp CURL.exe = curl M4.exe = m4 MKDIR.exe = mkdir PATCH.exe = patch RM.exe = rm TAR.exe = tar TOUCH.exe = touch WGET.exe = wget MV.exe = mv ZIP.exe = zip LN.exe = ln HandBrake-0.10.2/make/include/main.defs0000664000175200017520000000647412417602031020175 0ustar handbrakehandbrakeinclude $(SRC/)make/include/base.defs include $(SRC/)make/include/contrib.defs include $(SRC/)make/include/function.defs include $(SRC/)make/include/gcc.defs include $(SRC/)make/include/select.defs include $(SRC/)make/include/target.defs include $(SRC/)make/include/tool.defs ############################################################################### ifeq (1,$(FEATURE.local_pkgconfig)) MODULES += contrib/pkgconfig endif HB_TOOLS_PATH = ifeq (1,$(FEATURE.local_cmake)) MODULES += contrib/cmake HB_TOOLS_PATH = $(call fn.ABSOLUTE,$(CONTRIB.build/)bin) endif ifeq (1,$(FEATURE.local_autotools)) MODULES += contrib/autoconf MODULES += contrib/automake MODULES += contrib/libtool MODULES += contrib/m4 AUTOTOOL_MODULES = AUTOCONF AUTOMAKE LIBTOOL M4 HB_TOOLS_PATH = $(call fn.ABSOLUTE,$(CONTRIB.build/)bin) else AUTOTOOL_MODULES = endif ifneq (,$(HB_TOOLS_PATH)) PATH := $(HB_TOOLS_PATH):$(PATH) endif ifneq (,$(filter $(BUILD.system),cygwin mingw)) ifneq ($(HAS.bz2),1) MODULES += contrib/bzip2 endif endif ifneq (,$(filter $(BUILD.system),darwin cygwin mingw)) MODULES += contrib/fontconfig MODULES += contrib/freetype MODULES += contrib/fribidi MODULES += contrib/libxml2 MODULES += contrib/libass MODULES += contrib/libogg MODULES += contrib/libvorbis MODULES += contrib/libtheora MODULES += contrib/libsamplerate MODULES += contrib/lame MODULES += contrib/x264 endif ifeq (1,$(FEATURE.fdk_aac)) MODULES += contrib/fdk-aac endif ifeq (1,$(FEATURE.x265)) MODULES += contrib/x265 endif MODULES += contrib/ffmpeg MODULES += contrib/libvpx MODULES += contrib/libdvdread MODULES += contrib/libdvdnav MODULES += contrib/libbluray ifneq (,$(filter $(BUILD.system),mingw)) ifneq ($(HAS.pthread),1) MODULES += contrib/pthreadw32 endif endif ifeq (1,$(FEATURE.qsv)) MODULES += contrib/libmfx endif ifneq (,$(filter $(BUILD.system),cygwin mingw)) ifneq ($(HAS.iconv),1) MODULES += contrib/libiconv endif ifneq ($(HAS.libz),1) MODULES += contrib/zlib endif ifneq ($(HAS.regex),1) MODULES += contrib/libgnurx endif endif ifneq (,$(filter $(BUILD.system),solaris)) MODULES += contrib/libiconv endif ## these must come after contrib since some contrib modules are optional MODULES += libhb ############################################################################### ifeq (1-darwin,$(FEATURE.xcode)-$(BUILD.system)) ## use macosx module when xcode+darwin MODULES += macosx else ## default is to build CLI MODULES += test endif ifeq (1-mingw,$(FEATURE.gtk.mingw)-$(BUILD.system)) MODULES += gtk endif ifeq (1-linux,$(FEATURE.gtk)-$(BUILD.system)) ## build gtk when gtk+linux MODULES += gtk endif ifeq (1-kfreebsd,$(FEATURE.gtk)-$(BUILD.system)) ## build gtk when gtk+kfreebsd MODULES += gtk endif ifeq (1-gnu,$(FEATURE.gtk)-$(BUILD.system)) ## build gtk when gtk+gnu MODULES += gtk endif ifeq (1,$(FEATURE.local_yasm)) MODULES += contrib/yasm endif ############################################################################### MODULES += doc MODULES += pkg ############################################################################### include $(MODULES:%=$(SRC/)%/module.defs) include $(SRC/)make/variant/$(BUILD.system).defs -include $(SRC/)make/variant/$(BUILD.system).$(BUILD.machine).defs HandBrake-0.10.2/make/include/main.rules0000664000175200017520000000256011701017267020404 0ustar handbrakehandbrake.DELETE_ON_ERROR: .SUFFIXES: ############################################################################### ## shunt make through xcodebuild when FEATURE.xcode=1 and XCODE.driver is applicable ifeq (1:shunt,$(FEATURE.xcode):$(if $(filter bootstrap terminal,$(XCODE.driver)),shunt)) include $(SRC/)macosx/module.xcodebuild else ## only included using special report targets ifneq (,$(REPORT)) include $(SRC/)make/include/report.defs endif ############################################################################### .PHONY: build clean install uninstall xclean doc report build: clean: install: install-strip: uninstall: xclean: contrib.xclean clean doc: report:: report.main report.modules ## legacy mrproper: xclean ############################################################################### include $(SRC/)make/include/base.rules include $(MODULES:%=$(SRC/)%/module.rules) -include $(SRC/)make/variant/$(BUILD.system).rules -include $(SRC/)make/variant/$(BUILD.system).$(BUILD.machine).rules ############################################################################### ## force reconfigure .PHONY: reconfigure reconfigure: $(SRC/)configure --force $(CONF.args) ############################################################################### ## build all dependency dirs $(sort $(dir $(BUILD.out))): $(MKDIR.exe) -p $@ endif ## FEATURE.xcode XCODE.driver HandBrake-0.10.2/make/include/base.defs0000664000175200017520000000142311153115322020146 0ustar handbrakehandbrake## Define module metadata. ## It is mandatory for every module to use this template. ## ## $(1) module name (uppercase) ## $(2) module name (lowercase) ## $(3) list of prerequisite modules (uppercase) ## define import.MODULE.defs ## indicates module is defined; useful for conditionals $(1).enabled = 1 ## module name (lowercase) $(1).name = $(2) ## list of prerequisite modules (uppercase) $(1).prerequisites = $(3) ## add to global list of modules MODULES.NAMES += $(1) MODULES.names += $(2) endef ## ## $(1) module name (uppercase) ## define import.MODULE.rules .PHONY: $($(1).name).build $($(1).name).clean $($(1).name).report: @$(MAKE) report.true REPORT=module REPORT.module=$(1) ## aggregate report.modules:: $($(1).name).report endef HandBrake-0.10.2/make/include/function.defs0000664000175200017520000000163312220154570021070 0ustar handbrakehandbrakefn.ERROR1 = ERROR: $(1) fn.ERROR2 = ERROR: $(1): $(2) fn.HEADER = @echo "$(1): $(2)" fn.DIVIDER = @echo "======================================================================" fn.ABSOLUTE = $(if $(filter /%,$(1)),$(1),$(subst /./,/,$(CURDIR)/$(1))) fn.ARGS = $(strip $(foreach a,$(2), \ $($(1).$(patsubst !%,%,$(filter !%,$(a)))) \ $(foreach x,$(patsubst ?%,%,$(filter ?%,$(a))),$(if $(filter 1,$($(1).$(x))),$($(1).args.$(x)))) \ $(foreach x,$(patsubst .%,%,$(filter .%,$(a))),$($(1).args.$(x).$($(1).$(x)))) \ $(foreach x,$(patsubst @%,%,$(filter @%,$(a))),$(if $($(1).$(x)),$(call $(1).args.$(x),$($(1).$(x))))) \ $(foreach x,$(patsubst *%,%,$(filter *%,$(a))),$(foreach i,$($(1).$(x)),$(call $(1).args.$(x),$(i)))) \ )) fn.VARS = $(foreach v,$($(1).vars),$(v)="$($(1).vars.$(v))") fn.TARGET = $(TARGET.$(2).prefix)$(1)$(TARGET.$(2).suffix)$(TARGET.$(2).ext) define fn.PRINTLN $(1) endef HandBrake-0.10.2/make/include/contrib.defs0000664000175200017520000002401312323536321020702 0ustar handbrakehandbrakeCONTRIB.build/ = $(BUILD/)contrib/ CONTRIB.download/ = $(SRC/)download/ CONTRIB.host = $(if $(filter 1,$(BUILD.cross)),$(BUILD.spec)) CONTRIB.spec = $(if $(filter 1,$(BUILD.cross)),$(HOST.spec)) ############################################################################### ## ## $(1) = module name (uppercase) ## define import.CONTRIB.defs ## ## import gcc/g++ support mainly so we can force contrib choice of ## gcc executable, and debug/optimization flags. ## $$(eval $$(call import.GCC,$(1))) ## ## common values useful across targets ## $(1).src/ = $$(SRC/)contrib/$($(1).name)/ $(1).build/ = $$(CONTRIB.build/)$($(1).name)/ ## add prerequisites and autotool modules for all contribs $(1).deps = $$(foreach n,$($(1).prerequisites) $(if $(filter $1,$(AUTOTOOL_MODULES)),,$(AUTOTOOL_MODULES)),$$($$n.INSTALL.target)) ## ## target: fetch ## $(1).FETCH.tar = $$(CONTRIB.download/)$$(notdir $$($(1).FETCH.url)) $(1).FETCH.url = FETCH_IS_UNDEFINED $(1).FETCH.target = $$($(1).FETCH.tar) define $(1).FETCH $$(call FETCH,$$@,$$($(1).FETCH.url)) endef ## ## target: extract ## $(1).EXTRACT.tarbase = $$(strip $$(foreach x,tar.bz2 tar.gz,$$(patsubst %.$$(x),%,$$(filter %.$$(x),$$(notdir $$($(1).FETCH.url)))))) $(1).EXTRACT.dir/ = $$($(1).build/)$$($(1).EXTRACT.tarbase)/ $(1).EXTRACT.target = $$($(1).build/).stamp.extract define $(1).EXTRACT $$(RM.exe) -fr $$($(1).EXTRACT.dir/) $$(TAR.exe) xfC $$($(1).FETCH.tar) $$($(1).build/) $$(TOUCH.exe) $$@ endef ## ## target: patch ## $(1).PATCH.srcs = $$(sort $$(wildcard \ $$($(1).src/)A??-*.patch \ $$($(1).src/)P??-$$(BUILD.system)*.patch )) # extra line feed is required define $(1).PATCH.item $$(PATCH.exe) -t -N -p1 -d $$(1) < $$(2) endef $(1).PATCH.target = $$($(1).build/).stamp.patch define $(1).PATCH $$(foreach p,$$($(1).PATCH.srcs),$$(call $(1).PATCH.item,$$($(1).EXTRACT.dir/),$$(p))) $$(TOUCH.exe) $$@ endef ## ## target: configure ## $(1).CONFIGURE.sete = set -e; $(1).CONFIGURE.dir = $$($(1).EXTRACT.dir/) $(1).CONFIGURE.bootstrap = $(1).CONFIGURE.exe = ./configure $(1).CONFIGURE.host = $$(CONTRIB.host) $(1).CONFIGURE.build = $$(CONTRIB.spec) $(1).CONFIGURE.prefix = $$(call fn.ABSOLUTE,$$(CONTRIB.build/)) $(1).CONFIGURE.deps = --disable-dependency-tracking $(1).CONFIGURE.shared = --disable-shared $(1).CONFIGURE.static = --enable-static $(1).CONFIGURE.extra = $(1).CONFIGURE.args.dir = cd $$(1); $(1).CONFIGURE.args.host = --host=$$(1) $(1).CONFIGURE.args.build = --build=$$(1) $(1).CONFIGURE.args.prefix = --prefix=$$(1) $(1).CONFIGURE.args = !sete @dir !bootstrap !env !exe @host @build @prefix !deps !shared !static !extra $(1).CONFIGURE.env.LOCAL_PATH = $(1).CONFIGURE.env.CC = CC=$$($(1).GCC.gcc) $(1).CONFIGURE.env.CXX = CXX=$$($(1).GCC.gxx) ## ## Only add debug and optimization flags to contribs when ## debug=max or optimizations=none. Otherwise, use the contribs defaults ifeq (max,$$($(1).GCC.g)) ifeq (none,$$($(1).GCC.O)) $(1).CONFIGURE.env.CFLAGS = CFLAGS="-I$$(call fn.ABSOLUTE,$(CONTRIB.build/))include $$(call fn.ARGS,$(1).GCC,*archs *sysroot *minver ?c_std ?extra .g .O *D)" $(1).CONFIGURE.env.CXXFLAGS = CXXFLAGS="-I$$(call fn.ABSOLUTE,$(CONTRIB.build/))include $$(call fn.ARGS,$(1).GCC,*archs *sysroot *minver ?extra .g .O *D)" else $(1).CONFIGURE.env.CFLAGS = CFLAGS="-I$$(call fn.ABSOLUTE,$(CONTRIB.build/))include $$(call fn.ARGS,$(1).GCC,*archs *sysroot *minver ?c_std ?extra .g *D)" $(1).CONFIGURE.env.CXXFLAGS = CXXFLAGS="-I$$(call fn.ABSOLUTE,$(CONTRIB.build/))include $$(call fn.ARGS,$(1).GCC,*archs *sysroot *minver ?extra .g *D)" endif else $(1).CONFIGURE.env.CFLAGS = CFLAGS="-I$$(call fn.ABSOLUTE,$(CONTRIB.build/))include $$(call fn.ARGS,$(1).GCC,*archs *sysroot *minver ?c_std ?extra *D)" $(1).CONFIGURE.env.CXXFLAGS = CXXFLAGS="-I$$(call fn.ABSOLUTE,$(CONTRIB.build/))include $$(call fn.ARGS,$(1).GCC,*archs *sysroot *minver ?extra *D)" endif $(1).CONFIGURE.env.CPPFLAGS = CPPFLAGS="-I$$(call fn.ABSOLUTE,$(CONTRIB.build/))include $$(call fn.ARGS,$(1).GCC,*archs *sysroot *minver ?extra *D)" $(1).CONFIGURE.env.LDFLAGS = LDFLAGS="-L$$(call fn.ABSOLUTE,$(CONTRIB.build/))lib $$(call fn.ARGS,$(1).GCC,*archs *sysroot *minver ?extra.exe *D)" $(1).CONFIGURE.env.PKG_CONFIG_PATH = PKG_CONFIG_PATH="$$(call fn.ABSOLUTE,$$(CONTRIB.build/))lib/pkgconfig" $(1).CONFIGURE.env.args = !CC !CFLAGS !CXX !CXXFLAGS !CPPFLAGS !LD !LDFLAGS !PKG_CONFIG_PATH !LOCAL_PATH !CROSS $(1).CONFIGURE.env = $$(call fn.ARGS,$(1).CONFIGURE.env,$$($(1).CONFIGURE.env.args)) $(1).CONFIGURE.target = $$($(1).build/).stamp.configure define $(1).CONFIGURE $$(call fn.ARGS,$(1).CONFIGURE,$$($(1).CONFIGURE.args)) $$(TOUCH.exe) $$@ endef ## ## target: build ## $(1).BUILD.env = $(1).BUILD.make = $$(MAKE) $(1).BUILD.dir = $$($(1).EXTRACT.dir/) $(1).BUILD.extra = $(1).BUILD.ntargets = $(1).BUILD.args = !env !make @dir !extra !ntargets $(1).BUILD.args.dir = -C $$(1) $(1).BUILD.target = $$($(1).build/).stamp.build define $(1).BUILD $$(call fn.ARGS,$(1).BUILD,$$($(1).BUILD.args)) $$(TOUCH.exe) $$@ endef ## ## target: install ## $(1).INSTALL.make = $$(MAKE) $(1).INSTALL.dir = $$($(1).EXTRACT.dir/) $(1).INSTALL.extra = $(1).INSTALL.ntargets = $(1).INSTALL.args = !make @dir !extra !ntargets $(1).INSTALL.args.dir = -j 1 -C $$(1) install $(1).INSTALL.mkdirs = $$(CONTRIB.build/)lib/ $$(CONTRIB.build/)include/ $(1).INSTALL.target = $$($(1).build/).stamp.install define $(1).INSTALL $$(call fn.ARGS,$(1).INSTALL,$$($(1).INSTALL.args)) $$(TOUCH.exe) $$@ endef ## ## target: uninstall ## $(1).UNINSTALL.make = $$(MAKE) $(1).UNINSTALL.dir = $$($(1).EXTRACT.dir/) $(1).UNINSTALL.extra = $(1).UNINSTALL.ntargets = uninstall $(1).UNINSTALL.args = !make @dir !extra !ntargets $(1).UNINSTALL.args.dir = -C $$(1) define $(1).UNINSTALL $$(call fn.ARGS,$(1).UNINSTALL,$$($(1).UNINSTALL.args)) $$(RM.exe) -f $$($(1).INSTALL.target) endef ## ## target: clean ## $(1).CLEAN.make = $$(MAKE) $(1).CLEAN.dir = $$($(1).EXTRACT.dir/) $(1).CLEAN.extra = $(1).CLEAN.ntargets = clean $(1).CLEAN.args = !make @dir !extra !ntargets $(1).CLEAN.args.dir = -C $$(1) define $(1).CLEAN $$(call fn.ARGS,$(1).CLEAN,$$($(1).CLEAN.args)) $$(RM.exe) -f $$($(1).BUILD.target) endef ## ## other values used to aid prerequisite dirs and cleanup ## $(1).out += $$($(1).build/) $(1).out += $$($(1).FETCH.target) $(1).out += $$($(1).EXTRACT.target) $(1).out += $$($(1).PATCH.target) $(1).out += $$($(1).CONFIGURE.target) $(1).out += $$($(1).BUILD.target) $(1).out += $$($(1).INSTALL.mkdirs) $(1).out += $$($(1).INSTALL.target) BUILD.out += $$($(1).out) endef ############################################################################### ## ## $(1) = module name ## define import.CONTRIB.rules ## ## target: fetch ## $($(1).name).fetch: $$($(1).FETCH.target) $$($(1).FETCH.target): | $$(dir $$($(1).FETCH.target)) $$($(1).FETCH) ## ## target: extract ## must touch dir after extraction because old timestamp is restored via tar. ## $($(1).name).extract: $$($(1).EXTRACT.target) $$($(1).EXTRACT.target): | $$(dir $$($(1).EXTRACT.target)) $$($(1).EXTRACT.target): $$($(1).FETCH.target) $$($(1).EXTRACT) $($(1).name).extract.touch: $$(TOUCH.exe) $$($(1).EXTRACT.target) $($(1).name).extract.untouch: $$(RM.exe) -f $$($(1).EXTRACT.target) ## ## target: patch ## $($(1).name).patch: $$($(1).PATCH.target) $$($(1).PATCH.target): | $$(dir $$($(1).PATCH.target)) $$($(1).PATCH.target): $$($(1).EXTRACT.target) $$($(1).PATCH) $($(1).name).patch.touch: $$(TOUCH.exe) $$($(1).PATCH.target) $($(1).name).patch.untouch: $$(RM.exe) -f $$($(1).PATCH.target) ## ## target: configure ## $($(1).name).configure: $$($(1).CONFIGURE.target) $$($(1).CONFIGURE.target): | $$(dir $$($(1).CONFIGURE.target)) $$($(1).CONFIGURE.target): $$($(1).deps) $$($(1).CONFIGURE.target): $$($(1).PATCH.target) $$($(1).CONFIGURE) $($(1).name).configure.touch: $$(TOUCH.exe) $$($(1).CONFIGURE.target) $($(1).name).configure.untouch: $$(RM.exe) -f $$($(1).CONFIGURE.target) ## ## target: build ## $($(1).name).build: $$($(1).BUILD.target) $$($(1).BUILD.target): | $$(dir $$($(1).BUILD.target)) $$($(1).BUILD.target): $$($(1).CONFIGURE.target) -$$($(1).CLEAN) +$$($(1).BUILD) $($(1).name).build.touch: $$(TOUCH.exe) $$($(1).BUILD.target) $($(1).name).build.untouch: $$(RM.exe) -f $$($(1).BUILD.target) ## ## target: install ## $($(1).name).install: $$($(1).INSTALL.target) $$($(1).INSTALL.target): | $$(dir $$($(1).INSTALL.target)) $$($(1).INSTALL.mkdirs) $$($(1).INSTALL.target): $$($(1).BUILD.target) $$($(1).INSTALL) $($(1).name).install.touch: $$(TOUCH.exe) $$($(1).INSTALL.target) $($(1).name).install.untouch: $$(RM.exe) -f $$($(1).INSTALL.target) ## ## target: uninstall ## $($(1).name).uninstall: -$$($(1).UNINSTALL) ## ## target: clean ## $($(1).name).clean: -$$($(1).CLEAN) ## ## target: xclean ## $($(1).name).xclean: $($(1).name).uninstall $$(RM.exe) -fr $$($(1).build/) ## ## alias: module name is same as build ## $($(1).name): $($(1).name).build ## ## participate with global convenience targets ## contrib.fetch: $($(1).name).fetch contrib.extract: $($(1).name).extract contrib.patch: $($(1).name).patch contrib.configure: $($(1).name).configure contrib.build: $($(1).name).build contrib.install: $($(1).name).install contrib.uninstall: $($(1).name).uninstall contrib.clean: $($(1).name).clean contrib.xclean: $($(1).name).xclean endef HandBrake-0.10.2/make/include/select.defs0000664000175200017520000000053511152542670020527 0ustar handbrakehandbrake## ## fetch a file from the web via well-known anonymous protocols such as HTTP. ## ## $(1) = output filename ## $(2) = URL ## FETCH = $(FETCH.$(FETCH.select)) FETCH.select = MISSING FETCH.MISSING = $(error one of the following tools is required: wget, curl) FETCH.curl = $(CURL.exe) -q -L -o $(1) $(2) FETCH.wget = $(WGET.exe) -O $(1) $(2) HandBrake-0.10.2/make/include/base.rules0000664000175200017520000000210111154422467020365 0ustar handbrakehandbrake.PHONY: report.main report.gcc report.modules report.var report.true report.help .PHONY: shell.run report.modules:: report.main: @$(MAKE) report.true REPORT=main report.gcc: @$(MAKE) report.true REPORT=gcc report.var: @$(MAKE) report.true REPORT=var ## needed for nested make (which drives each report) report.true: @true ## linefeed is important define REPORT.help.item.global @echo 'report.$(1)' | awk '{ printf(" %-21s $(REPORT.help.$(1))\n", $$0) }' endef define REPORT.help.item.module @echo '$($(1).name).report' | awk '{ printf(" %-21s $(1)-scoped vars\n", $$0) }' endef REPORT.help.main = global general vars REPORT.help.gcc = global gcc vars (inherited by module GCC) REPORT.help.var = usage: make report.var name=VARNAME report.help: @echo " AVAILABLE MAKEFILE VARS REPORTS" @echo " ----------------------------------------------------------------" $(foreach n,main gcc var,$(call REPORT.help.item.global,$n)) $(foreach n,$(MODULES.NAMES),$(call REPORT.help.item.module,$n)) ## diagnostic aid when troubleshooting build issues shell.run: $(command) HandBrake-0.10.2/make/include/target.defs0000664000175200017520000000075011152542670020535 0ustar handbrakehandbrakeTARGET.dylib.prefix = lib TARGET.dylib.suffix = TARGET.dylib.ext = .dylib TARGET.dylib = $(TARGET.dylib.prefix)$(1)$(TARGET.dylib.suffix)$(TARGET.dylib.ext) TARGET.archive.prefix = lib TARGET.archive.suffix = TARGET.archive.ext = .a TARGET.archive = $(TARGET.archive.prefix)$(1)$(TARGET.archive.suffix)$(TARGET.archive.ext) TARGET.exe.prefix = TARGET.exe.suffix = TARGET.exe.ext = TARGET.exe = $(TARGET.exe.prefix)$(1)$(TARGET.exe.suffix)$(TARGET.exe.ext) HandBrake-0.10.2/make/include/report.defs0000664000175200017520000000355611153571217020571 0ustar handbrakehandbrake## function: print a var's name, definition and expanded value ## ## $(1) = name of variable ## define fn.PRINTVAR $(1) ORIGIN = $(origin $(1)) FLAVOR = $(flavor $(1)) DEFINITION = $(value $(1)) EXPANDED = $($(1)) endef ## report: module ## ## REPORT.module = module name (uppercase) ## ifeq (module,$(REPORT)) $(info ###############################################################################) $(info ##) $(info ## MODULE: $(REPORT.module)) $(info ##) $(info ###############################################################################) $(info $(foreach v,$(sort $(filter $(REPORT.module).%,$(.VARIABLES))),$(call fn.PRINTVAR,$v))) $(info ) endif ## report: main ## ifeq (main,$(REPORT)) $(info ###############################################################################) $(info ##) $(info ## MAIN) $(info ##) $(info ###############################################################################) $(info $(foreach v,$(sort $(filter HB.%,$(.VARIABLES))),$(call fn.PRINTVAR,$v))) $(info $(foreach v,$(sort $(filter HOST.%,$(.VARIABLES))),$(call fn.PRINTVAR,$v))) $(info $(foreach v,$(sort $(filter BUILD.%,$(.VARIABLES))),$(call fn.PRINTVAR,$v))) $(info $(foreach v,$(sort $(filter INSTALL.%,$(.VARIABLES))),$(call fn.PRINTVAR,$v))) $(info $(foreach v,$(sort $(filter FEATURE.%,$(.VARIABLES))),$(call fn.PRINTVAR,$v))) $(info $(foreach v,$(sort $(filter CONTRIB.%,$(.VARIABLES))),$(call fn.PRINTVAR,$v))) $(info ) endif ## report: gcc ## ifeq (gcc,$(REPORT)) $(info ###############################################################################) $(info ##) $(info ## GCC) $(info ##) $(info ###############################################################################) $(info $(foreach v,$(sort $(filter GCC.%,$(.VARIABLES))),$(call fn.PRINTVAR,$v))) $(info ) endif ## report: var ## ifeq (var,$(REPORT)) $(info $(call fn.PRINTVAR,$(name))) $(info ) endif HandBrake-0.10.2/make/include/gcc.defs0000664000175200017520000001703212375157147020015 0ustar handbrakehandbrakeGCC.gcc = gcc GCC.gxx = $(dir $(GCC.gcc))$(subst clang,clang++,$(subst gcc,g++,$(notdir $(GCC.gcc)))) GCC.strip = $$(if $$(filter none,$$(GCC.g)),1) GCC.dylib = 1 GCC.pipe = 1 GCC.c_std = 1 GCC.ML = 1 GCC.H = 0 GCC.W = all GCC.archs = GCC.sysroot = GCC.minver = GCC.vis = 0 GCC.pic = 0 ifndef GCC.g GCC.g = none endif ifndef GCC.O GCC.O = none endif GCC.D = GCC.I = GCC.muldefs = 0 GCC.start = 0 GCC.a = GCC.F = GCC.f = GCC.L = GCC.l = GCC.end = 0 GCC.extra = 1 GCC.extra.h_o = 1 GCC.extra.c_o = 1 GCC.extra.dylib = 1 GCC.extra.exe = 1 GCC.extra.hpp_o = 1 GCC.extra.cpp_o = 1 GCC.extra.dylib++ = 1 GCC.extra.exe++ = 1 GCC.args.pipe = -pipe GCC.args.strip = -Wl,-S GCC.args.dylib = -dynamiclib GCC.args.ML = -fmessage-length=0 GCC.args.H = -H GCC.args.W = -W$(1) GCC.args.archs = -arch $(1) GCC.args.sysroot = --sysroot=$(1) GCC.args.minver = -mmacosx-version-min=$(1) GCC.args.vis = -fvisibility=hidden GCC.args.pic = -fPIC GCC.args.c_std = -std=gnu99 GCC.args.g.none = -g0 GCC.args.g.min = -gdwarf-2 -g1 GCC.args.g.std = -gdwarf-2 GCC.args.g.max = -gdwarf-2 -g3 GCC.args.O.none = -O0 GCC.args.O.size = -Os GCC.args.O.speed = -O3 GCC.args.D = -D$(1) GCC.args.I = -I$(1) GCC.args.muldefs = -Wl,--allow-multiple-definition GCC.args.start = -Wl,--start-group GCC.args.F = -F$(1) GCC.args.f = -framework $(1) GCC.args.L = -L$(1) GCC.args.l = -l$(1) GCC.args.end = -Wl,--end-group GCC.args.extra = $(CFLAGS) $(CPPFLAGS) GCC.args.extra.h_o = GCC.args.extra.c_o = GCC.args.extra.dylib = $(LDFLAGS) GCC.args.extra.exe = $(LDFLAGS) GCC.args.extra.hpp_o = GCC.args.extra.cpp_o = GCC.args.extra.dylib++ = $(LDFLAGS) GCC.args.extra.exe++ = $(LDFLAGS) ############################################################################### define import.GCC $(1).GCC.gcc = $$(GCC.gcc) $(1).GCC.gxx = $$(dir $$($(1).GCC.gcc))$$(subst clang,clang++,$$(subst gcc,g++,$$(notdir $$($(1).GCC.gcc)))) $(1).GCC.pipe = $$(GCC.pipe) $(1).GCC.c_std = $$(GCC.c_std) $(1).GCC.strip = $$(if $$(filter none,$$($(1).GCC.g)),1) $(1).GCC.dylib = $$(GCC.dylib) $(1).GCC.ML = $$(GCC.ML) $(1).GCC.H = $$(GCC.H) $(1).GCC.W = $$(GCC.W) $(1).GCC.archs = $$(GCC.archs) $(1).GCC.sysroot = $$(GCC.sysroot) $(1).GCC.minver = $$(GCC.minver) $(1).GCC.vis = $$(GCC.vis) $(1).GCC.pic = $$(GCC.pic) $(1).GCC.g = $$(GCC.g) $(1).GCC.O = $$(GCC.O) $(1).GCC.D = $$(GCC.D) $(1).GCC.I = $$(GCC.I) $(1).GCC.muldefs = $$(GCC.muldefs) $(1).GCC.start = $$(GCC.start) $(1).GCC.a = $$(GCC.a) $(1).GCC.F = $$(GCC.F) $(1).GCC.f = $$(GCC.f) $(1).GCC.L = $$(GCC.L) $(1).GCC.l = $$(GCC.l) $(1).GCC.end = $$(GCC.end) $(1).GCC.extra = $$(GCC.extra) $(1).GCC.extra.h_o = $$(GCC.extra.h_o) $(1).GCC.extra.c_o = $$(GCC.extra.c_o) $(1).GCC.extra.dylib = $$(GCC.extra.dylib) $(1).GCC.extra.exe = $$(GCC.extra.exe) $(1).GCC.extra.hpp_o = $$(GCC.extra.hpp_o) $(1).GCC.extra.cpp_o = $$(GCC.extra.cpp_o) $(1).GCC.extra.dylib++ = $$(GCC.extra.dylib++) $(1).GCC.extra.exe++ = $$(GCC.extra.exe++) $(1).GCC.args.pipe = $$(GCC.args.pipe) $(1).GCC.args.c_std = $$(GCC.args.c_std) $(1).GCC.args.strip = $$(GCC.args.strip) $(1).GCC.args.dylib = $$(GCC.args.dylib) $(1).GCC.args.ML = $$(GCC.args.ML) $(1).GCC.args.H = $$(GCC.args.H) $(1).GCC.args.W = $$(GCC.args.W) $(1).GCC.args.archs = $$(GCC.args.archs) $(1).GCC.args.sysroot = $$(GCC.args.sysroot) $(1).GCC.args.minver = $$(GCC.args.minver) $(1).GCC.args.vis = $$(GCC.args.vis) $(1).GCC.args.pic = $$(GCC.args.pic) $(1).GCC.args.g.none = $$(GCC.args.g.none) $(1).GCC.args.g.min = $$(GCC.args.g.min) $(1).GCC.args.g.std = $$(GCC.args.g.std) $(1).GCC.args.g.max = $$(GCC.args.g.max) $(1).GCC.args.O.none = $$(GCC.args.O.none) $(1).GCC.args.O.size = $$(GCC.args.O.size) $(1).GCC.args.O.speed = $$(GCC.args.O.speed) $(1).GCC.args.D = $$(GCC.args.D) $(1).GCC.args.I = $$(GCC.args.I) $(1).GCC.args.muldefs = $$(GCC.args.muldefs) $(1).GCC.args.start = $$(GCC.args.start) $(1).GCC.args.F = $$(GCC.args.F) $(1).GCC.args.f = $$(GCC.args.f) $(1).GCC.args.L = $$(GCC.args.L) $(1).GCC.args.l = $$(GCC.args.l) $(1).GCC.args.end = $$(GCC.args.end) $(1).GCC.args.extra = $$(GCC.args.extra) $(1).GCC.args.extra.h_o = $$(GCC.args.extra.h_o) $(1).GCC.args.extra.c_o = $$(GCC.args.extra.c_o) $(1).GCC.args.extra.dylib = $$(GCC.args.extra.dylib) $(1).GCC.args.extra.exe = $$(GCC.args.extra.exe) $(1).GCC.args.extra.hpp_o = $$(GCC.args.extra.hpp_o) $(1).GCC.args.extra.cpp_o = $$(GCC.args.extra.cpp_o) $(1).GCC.args.extra.dylib++ = $$(GCC.args.extra.dylib++) $(1).GCC.args.extra.exe++ = $$(GCC.args.extra.exe++) ########################################################################### $(1).GCC.c = -c $$(4) $(1).GCC.o = -o $$(3) # FUNCTION: C precompiled headers $(1).GCC.H_O.args = !gcc ?c_std ?pipe ?ML ?H *W *archs *sysroot *minver ?vis ?pic .g .O ?extra ?extra.h_o *D *I !c !o $(1).GCC.H_O = $$(call fn.ARGS,$(1).GCC,$$($(1).GCC.H_O.args),$$(1),$$(2)) # FUNCTION: C compile source $(1).GCC.C_O.args = !gcc ?c_std ?pipe ?ML ?H *W *archs *sysroot *minver ?vis ?pic .g .O ?extra ?extra.c_o *D *I !c !o $(1).GCC.C_O = $$(call fn.ARGS,$(1).GCC,$$($(1).GCC.C_O.args),$$(1),$$(2)) # FUNCTION: C++ precompile headers $(1).GCC.HPP_O.args = !gxx ?pipe ?ML ?H *W *archs *sysroot *minver ?vis ?pic .g .O ?extra ?extra.hpp_o *D *I !c !o $(1).GCC.HPP_O = $$(call fn.ARGS,$(1).GCC,$$($(1).GCC.HPP_O.args),$$(1),$$(2)) # FUNCTION: C++ compile source $(1).GCC.CPP_O.args = !gxx ?pipe ?ML ?H *W *archs *sysroot *minver ?vis ?pic .g .O ?extra ?extra.cpp_o *D *I !c !o $(1).GCC.CPP_O = $$(call fn.ARGS,$(1).GCC,$$($(1).GCC.CPP_O.args),$$(1),$$(2)) ########################################################################### $(1).GCC.i = $$(4) # FUNCTION: C link dynamic-lib $(1).GCC.DYLIB.args = !gcc ?c_std ?pipe ?strip ?dylib ?extra.dylib ?ML *W *archs *sysroot *minver ?vis ?pic .g .O ?extra *D *I !o ?muldefs ?start !i *F *f *L *l *i !a ?end $(1).GCC.DYLIB = $$(call fn.ARGS,$(1).GCC,$$($(1).GCC.DYLIB.args),$$(1),$$(2)) # FUNCTION: C link executable $(1).GCC.EXE.args = !gcc ?c_std ?pipe ?strip ?extra.exe ?ML *W *archs *sysroot *minver ?vis ?pic .g .O ?extra *D *I !o ?muldefs ?start !i *F *f *L *l *i !a ?end $(1).GCC.EXE = $$(call fn.ARGS,$(1).GCC,$$($(1).GCC.EXE.args),$$(1),$$(2)) # FUNCTION: C++ link dynamic-lib $(1).GCC.DYLIB++.args = !gxx ?pipe ?strip ?dylib ?extra.dylib++ ?ML *W *arch *sysroot *minvers ?vis ?pic .g .O ?extra *D *I !o ?muldefs ?start !i *F *f *L *l *i !a ?end $(1).GCC.DYLIB++ = $$(call fn.ARGS,$(1).GCC,$$($(1).GCC.DYLIB++.args),$$(1),$$(2)) # FUNCTION: C++ link executable $(1).GCC.EXE++.args = !gxx ?pipe ?strip ?extra.exe++ ?ML *W *archs *sysroot *minver ?vis ?pic .g .O ?extra *D *I !o ?muldefs ?start !i *F *f *L *l *i !a ?end $(1).GCC.EXE++ = $$(call fn.ARGS,$(1).GCC,$$($(1).GCC.EXE++.args),$$(1),$$(2)) endef HandBrake-0.10.2/make/config.guess0000775000175200017520000012743212071354043017304 0ustar handbrakehandbrake#! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012 Free Software Foundation, Inc. timestamp='2012-02-10' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Per Bothner. Please send patches (context # diff format) to and include a ChangeLog # entry. # # This script attempts to guess a canonical system name similar to # config.sub. If it succeeds, it prints the system name on stdout, and # exits with 0. Otherwise, it exits with 1. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm:riscos:*:*|arm:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; i*:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-gnu else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-gnueabi else echo ${UNAME_MACHINE}-unknown-linux-gnueabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-gnu exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-gnu exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:Linux:*:*) LIBC=gnu eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __dietlibc__ LIBC=dietlibc #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` echo "${UNAME_MACHINE}-pc-linux-${LIBC}" exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } ;; or32:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; padre:Linux:*:*) echo sparc-unknown-linux-gnu exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-gnu exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-gnu ;; PA8*) echo hppa2.0-unknown-linux-gnu ;; *) echo hppa-unknown-linux-gnu ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-gnu exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-gnu exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-gnu exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown case $UNAME_PROCESSOR in i386) eval $set_cc_for_build if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then UNAME_PROCESSOR="x86_64" fi fi ;; unknown) UNAME_PROCESSOR=powerpc ;; esac echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-?:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; esac #echo '(No uname command or uname output not recognized.)' 1>&2 #echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix\n"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; c34*) echo c34-convex-bsd exit ;; c38*) echo c38-convex-bsd exit ;; c4*) echo c4-convex-bsd exit ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: HandBrake-0.10.2/make/variant/0000775000175200017520000000000012535641641016427 5ustar handbrakehandbrakeHandBrake-0.10.2/make/variant/cygwin.defs0000664000175200017520000000021111177134244020561 0ustar handbrakehandbrakeGCC.muldefs = 1 GCC.start = 1 GCC.end = 1 GCC.args.g.none = -g0 GCC.args.g.min = -g1 GCC.args.g.std = -g2 GCC.args.g.max = -g3 HandBrake-0.10.2/make/variant/darwin.x86_64.defs0000664000175200017520000000027711152542670021515 0ustar handbrakehandbrake## can enable asm if we replace .rept pseudo op with standard pre-processor macros ## since darwin's as doesn't support them. for now just disable. LIBTHEORA.CONFIGURE.extra += --disable-asm HandBrake-0.10.2/make/variant/solaris.defs0000664000175200017520000000032511265116017020737 0ustar handbrakehandbrakeTARGET.dylib.ext = .so GCC.args.dylib = -shared GCC.args.strip = -Wl,-s GCC.args.extra += -Wl,-z,rescan GCC.args.pic = 1 GCC.args.g.none = -g0 GCC.args.g.min = -g1 GCC.args.g.std = -g2 GCC.args.g.max = -g3 HandBrake-0.10.2/make/variant/darwin.defs0000664000175200017520000000253411701017267020555 0ustar handbrakehandbrakeUB.xcconfigs = osx106.i386 osx106.x86_64 UB.builds = $(wildcard $(foreach n,$(UB.xcconfigs),$(SRC/)build.$n)) UB.first = $(word 1,$(UB.xcconfigs)) UB.more = $(wordlist 2,999,$(UB.xcconfigs)) UB.products/ = macosx/release/ UB.BUILD = $(SRC/)configure --force --build=ub.$(1) --xcconfig=$(1) --launch --launch-quiet ## linefeed is important define UB.BUILD.item $(call UB.BUILD,$(1)) --launch-jobs=0 endef define UB.BUILD.SERIAL $(foreach n,$(UB.xcconfigs),$(call UB.BUILD.item,$n)) endef define UB.BUILD.PARALLEL $(call UB.BUILD,$(1)) >/dev/null 2>&1 endef define UB.COMBINE $(RM.exe) -fr ub.combine $(MKDIR.exe) -p ub.combine $(CP.exe) ub.$(UB.first)/$(UB.products/)HandBrakeCLI ub.combine/. $(LIPO.exe) $(foreach n,$(UB.xcconfigs),ub.$n/$(UB.products/)HandBrakeCLI) -create -output ub.combine/HandBrakeCLI $(CP.exe) -R ub.$(UB.first)/$(UB.products/)HandBrake.app ub.combine/. $(LIPO.exe) $(foreach n,$(UB.xcconfigs),ub.$n/$(UB.products/)$(1)) -create -output ub.combine/$(1) @lipo -info ub.combine/$(1) @sync @echo "" @echo "$@: { $(UB.xcconfigs) } combined -> ub.combine/HandBrakeCLI" @echo "$@: UB executable size: `du -sh ub.combine/HandBrakeCLI | awk '{ print $$1 }'`" @echo "" @echo "$@: { $(UB.xcconfigs) } combined -> ub.combine/HandBrake.app" @echo "$@: UB executable size: `du -sh ub.combine/$(1) | awk '{ print $$1 }'`" endef HandBrake-0.10.2/make/variant/kfreebsd.defs0000664000175200017520000000030311775677315021067 0ustar handbrakehandbrakeTARGET.dylib.ext = .so GCC.start = 1 GCC.end = 1 GCC.args.dylib = -shared GCC.args.pic = 1 GCC.args.g.none = -g0 GCC.args.g.min = -g1 GCC.args.g.std = -g2 GCC.args.g.max = -g3 HandBrake-0.10.2/make/variant/mingw.defs0000664000175200017520000000031512111513550020375 0ustar handbrakehandbrakeTARGET.exe.suffix = .exe GCC.start = 1 GCC.end = 1 GCC.args.dylib = -shared 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-bitfieldsHandBrake-0.10.2/make/variant/linux.defs0000664000175200017520000000030311177134244020422 0ustar handbrakehandbrakeTARGET.dylib.ext = .so GCC.start = 1 GCC.end = 1 GCC.args.dylib = -shared GCC.args.pic = 1 GCC.args.g.none = -g0 GCC.args.g.min = -g1 GCC.args.g.std = -g2 GCC.args.g.max = -g3 HandBrake-0.10.2/make/variant/freebsd.defs0000664000175200017520000000023511177134244020701 0ustar handbrakehandbrakeTARGET.dylib.ext = .so GCC.args.dylib = -shared GCC.args.pic = 1 GCC.args.g.none = -g0 GCC.args.g.min = -g1 GCC.args.g.std = -g2 GCC.args.g.max = -g3 HandBrake-0.10.2/make/variant/darwin.rules0000664000175200017520000000102111701017267020754 0ustar handbrakehandbrake.PHONY: ub.build ub.combine ub.clean ub.build: ub.build.serial ub.build.serial: @$(UB.BUILD.SERIAL) ub.build.parallel: @set -e; \ for xcconfig in $(UB.xcconfigs); do \ $(call UB.BUILD.PARALLEL,$$xcconfig) & \ children="$$children $$!"; \ echo "pid $$!: $(call UB.BUILD.PARALLEL,$$xcconfig)"; \ done; \ echo "waiting for background jobs to complete:$$children"; \ wait ub.combine: $(call UB.COMBINE,HandBrake.app/Contents/MacOS/HandBrake) #ub.clean: # $(RM.exe) -fr $(foreach n,$(UB.archs.other),ub.$n) HandBrake-0.10.2/make/variant/gnu.defs0000664000175200017520000000030311775677315020073 0ustar handbrakehandbrakeTARGET.dylib.ext = .so GCC.start = 1 GCC.end = 1 GCC.args.dylib = -shared GCC.args.pic = 1 GCC.args.g.none = -g0 GCC.args.g.min = -g1 GCC.args.g.std = -g2 GCC.args.g.max = -g3 HandBrake-0.10.2/CREDITS0000664000175200017520000000076312416263450015070 0ustar handbrakehandbrakeCREDITS file for HandBrake HandBrake uses many cool libraries from the GNU/Linux world. Thanks to the authors of the following libraries which help power HandBrake: See the local project AUTHORS files for details of the individuals involved. libbzip2 libavcodec libfontconfig libfreetype liblame libass libbluray libdvdnav libdvdread libiconv libogg libsamplerate libtheora libvorbis libxml2 libmpeg2dec libpthreadsw32 libx264 libx265 libzlib fdk-aac libmfx libvpx libgnurxHandBrake-0.10.2/README.pod0000664000175200017520000000234011606136740015503 0ustar handbrakehandbrake=head2 HandBrake Repositories =begin html

Subversion

Subversion Repository: svn://svn.handbrake.fr/HandBrake/trunk

Anyone can check code out; access must be granted by a project administrator to check code in.

GitHub Mirror

We also have a mirror on GitHub. This is currently read-only.

Please note: The Subversion tags have not been copied across. This is just a mirror of the trunk directory.

Trac

Our Trac is located at: https://trac.handbrake.fr
This contains our wiki, tickets and code timeline.

Freenode

IRC: #Handbrake on Freenode Note that accessing IRC through freenode's webchat is disabled for spam concerns.

Submitting a Patch

There are several ways you can contribute code back to the HandBrake repository.

Please see the Contribute page for details.

Note: Submitting git diffs to our ReviewBoard can be tricky, please feel free to post the diff on our pastebin site and provide a link on the Forums or IRC.

=end htmlHandBrake-0.10.2/doc/0000775000175200017520000000000012535641641014613 5ustar handbrakehandbrakeHandBrake-0.10.2/doc/BUILD-Mac0000664000175200017520000005266012323006750016073 0ustar handbrakehandbrakeBuild Guide for HandBrake 6163svn on Mac OS X ********************************************* 1 Introduction 2 Prerequisites 3 QuickStart 4 Overview 5 Building via Terminal 5.1 Checkout Sources 5.2 Configure 5.3 Build 5.4 Make Targets 5.4.1 Global 5.4.2 General Modules 5.4.3 Contrib Modules 5.4.4 Contrib Touch and Untouch 5.4.5 Contrib Aggregates 5.5 Customizing Make 5.6 Universal Binaries 6 Building via Xcode.app 6.1 Checkout Sources 6.2 Build 6.3 Note: Finding Built Products 6.4 Note: Workspace Log Behaviors 6.5 External Target 6.6 User-Defined Settings 7 Troubleshooting Appendix A Project Repository Details 1 Introduction ************** This guide documents the recommended process to build HandBrake on Mac OS X hosts from the official source-code repository. Building from any other source is not supported. 2 Prerequisites *************** Building on Mac OS X is well supported. It is the reference platform for HandBrake. The following are the recommended specifications for this platform; but is not necessarily the only configuration that is possible: * Mac Intel hardware * Mac OS X 10.7.5 or Mac OS X 10.8.3 * Xcode 4.6.1 (4H512) * llvm-gcc-4.2 version 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00) * Xcode command-line tools (installed via Preferences > Downloads > Components) * XQuartz (http://xquartz.macosforge.org/landing/) The following tools are compiled during the build process if necessary, but you can speed up your build by installing them: * yasm 1.2.0 or later * autoconf * automake * libtool Note: It is recommended to use the platform distribution's standard compiler for maximum C++ compatibility. If you build with a custom compiler it will likely introduce non-standard runtime requirements and have new/delete, exception and RTTI incompatibilities. There are of course many valid reasons to build with unbundled compilers, but be aware it is generally unsupported and left as an exercise to the reader. The following general tools are used on various platforms and it is recommended you use these versions or similar: * subversion - 1.6.16 * python - Python 2.7.1 * curl - curl 7.21.4 (or wget) * m4 - GNU M4 1.4.6 * make - GNU Make 3.81 * patch - Patch 2.5.8 * tar - GNU tar 1.26 * wget - GNU Wget 1.13.4 (or curl) 3 QuickStart ************ This chapter is for building from a terminal/shell environment in as few commands as possible. Upon completion of the following commands you should have a fresh build of HandBrake. Further instructions are available beginning with *note overview:: which describes procedures suitable for repeating builds. This chapter should be skipped by those seeking more than a minimalist build. svn checkout svn://svn.handbrake.fr/HandBrake/trunk hb-trunk cd hb-trunk ./configure --launch The special option '--launch' selects launch mode and performs the following steps: * assert scratch directory 'build/' does not exist * create scratch directory 'build/' * change to directory 'build/' * launch 'make' * capture build output to 'build/log/build.txt' * echo build output * print elapsed time * indicate if build ultimately succeeded or failed 4 Overview ********** The two general methods to build on Mac OS X are from terminal or Xcode.app. The preferred method for automated and repeatable builds is to use the terminal. Otherwise the choice is generally up to the individual. To be extra clear, building from the terminal by default actually invokes 'xcodebuild' to build the very same targets contained in the Xcode project. Think of it as building with Xcode but without the GUI. 5 Building via Terminal *********************** 5.1 Checkout Sources ==================== Checkout HandBrake from the official source-code repository. svn checkout svn://svn.handbrake.fr/HandBrake/trunk hb-trunk cd hb-trunk Sources are checked out from the 'trunk' branch. This document was generated from that very branch, and for example purposes, we will use exactly the same branch. If you have write-access to the repository, then you may add the appropriate login/password information as needed. It is recommended to use Subversion 1.6.0 or higher. Lower versions should also work. 5.2 Configure ============= Configure the build system. ./configure Configure will automatically create a scratch build directory 'build' unless you use GNU-style build procedures and first 'cd' to a directory other than top-level source. Additionally you may use '--build' to specify the directory. The name of the directory is arbitrary but it is recommended to use something which indicates transient files which are not checked into the repository. The 'configure' utility accepts many options. It is recommended that you specify '--help' for the complete list of options. The following options are also documented here: '--help' List available options. '--src=DIR' Specify top-level source directory for HandBrake sources. '--build=DIR' Specify destination directory for final product install. The default is to use either 'build' if in the top-level source directory, otherwise '.' '--prefix=DIR' Specify destination directory for final product install. This defaults to a reasonable platform-specific value. '--launch' All-in-one option which launches the build and logs output automatically. Useful for novices and quick-start procedures. '--disable-gtk' Disable building the GTK GUI on applicable platforms such as Linux. '--debug=MODE' Select debug mode. Must be one of 'none', 'min', 'std', 'max'. This generally maps to gcc options '-g0', '-g1', '-g2', '-g3'. '--optimize=MODE' Select optimize mode. Must be one of 'none', 'speed', 'size'. This generally maps to gcc options '-g0', '-O0', '-O3', '-Os'. '--arch=MODE' Select build architecture. The available architectures vary by platform. Most platforms support exactly one architecture except Mac OS X which has support for various universal binary architectures. The available choices are hard-coded per platform and no sanity checks for the required tools are performed. '--disable-xcode' Disable shunting the build through 'xcodebuild'. If this option is applied, 'HandBrakeCLI' will be produced in a similar fashion as it is on other platforms; sans Xcode and the Cocoa application will not be produced. Mac OS X only. '--xcconfig=MODE' Select Xcode project configuration file. The available modes are the basenames of files located in 'macosx/xcconfig/*.xcconfig' which direct Xcode to build using various architecture and Mac OS X deployment options. Mac OS X only. Clean-room procedures dictate that when certain factors change, old builds should be scrapped and new builds configured. This is the main reason for requiring a scratch directory; to promote consistent, reliable and clean software builds. The following is a short list of some of the reasons why someone may choose to scrap an existing build: * configure with different options * subversion working dir is updated and you want configure to re-evaluate working dir metadata. * build corruption is suspected There are generally two methods for scrapping a build. The 'build' directory can be recursively removed which has the effect of loosing your existing configuration but does guarantee no residuals are left behind. The other method is to ask the build system to perform an 'make xclean'. This is known to work well but will leave empty directories behind. However, the configuration is left intact. 5.3 Build ========= Build main product. All necessary dependencies are also built if required. make Parallel builds may optionally be enabled. Be aware that while a parallel build may save time on systems with additional cores, the output is often mixed, overlapped and sometimes even corrupted with binary characters. Thus if you experience a build issue, you should clean and redo the build in default serial mode to produce a readable log. The following command allows for up to 4 concurrent jobs via make: make -j4 5.4 Make Targets ================ The build system supports passing many kinds of targets some of which become very useful in normal development cycles. The targets by convention are lower-case words passed to 'make'. Global targets are one-word targets. Scoped targets are usually two-words separated by a period. 5.4.1 Global ------------ 'make' Alias for 'make build'. 'make build' Build main product. All necessary dependencies are also built if required. 'make clean' Clean all build output excluding contrib modules. Configuration is retained. 'make install' Perform final product(s) install. This will install build products to a standard directory or one specified via 'configure --prefix' option. 'make uninstall' Perform final product(s) uninstall. This will uninstall any products which may have been previously installed. 'make xclean' Clean all build output including contrib modules. Configuration is retained. 'make doc' Build auto-generated project documentation. Various articles are produced and may be found in 'build/doc/articles'. 'make doc.post' Build auto-generated project documentation and post produced articles directly to source tree. 'make report.help' Print list of available makefile vars report targets. These reports detail var definitions and expanded values used by the build system. For experts only. 'make report.all' Convenience target which aggregates all reports. For experts only. 5.4.2 General Modules --------------------- General modules such as 'libhb', 'test' and 'gtk' have the following scoped targets: 'make MODULE.build' Build MODULE. 'make MODULE.clean' Clean build output for MODULE. 5.4.3 Contrib Modules --------------------- Contrib modules such as 'a52dec', 'bzip2', 'faac', 'faad2', 'ffmpeg', 'fontconfig', 'freetype', 'fribidi', 'lame', 'libass', 'libbluray', 'libdca', 'libdvdnav', 'libdvdread', 'libiconv', 'libmkv', 'libogg', 'libsamplerate', 'libtheora', 'libvorbis', 'libxml2', 'mp4v2', 'mpeg2dec', 'x264', 'yasm' and 'zlib' have the following scoped targets: 'make MODULE.fetch' Download source tarball from the Internet and save to 'TOP/downloads' directory. No check-summing is performed. 'make MODULE.extract' Extract source tarball into 'build' tree. 'make MODULE.patch' Apply appropriate patches (if any) to module sources. 'make MODULE.configure' Configure module sources. This usually invokes autotool configure. 'make MODULE.build' Build module. This usually invokes autotool build. 'make MODULE.install' Install module products such as headers and libraries into 'build' tree. This usually invokes autotool install. 'make MODULE.uninstall' Uninstall module products; generally the reverse of install. This usually invokes autotool uninstall. 'make MODULE.clean' Clean module; generally the reverse of build. This usually invokes autotool clean. 'make MODULE.xclean' Extra clean module; first invokes uninstall then recursively removes the module build directory. 5.4.4 Contrib Touch and Untouch ------------------------------- Also available are some very granular targets which help force builds from specific cycle points. The following targets are available to touch and untouch the respective module target; this will force the build system to treat the target as satisfied after a touch or unsatisfied after an untouch: * make MODULE.extract.touch * make MODULE.extract.untouch * make MODULE.patch.touch * make MODULE.patch.untouch * make MODULE.configure.touch * make MODULE.configure.untouch * make MODULE.build.touch * make MODULE.build.untouch * make MODULE.install.touch * make MODULE.install.untouch 5.4.5 Contrib Aggregates ------------------------ For convenience, the following targets aggregate the all contrib modules' respective targets together: * make contrib.fetch * make contrib.extract * make contrib.patch * make contrib.configure * make contrib.build * make contrib.install * make contrib.uninstall * make contrib.clean * make contrib.xclean 5.5 Customizing Make ==================== If the need arises to override settings in the build system (essentially gnu-make variables) the recommended method is to create optional include files which are automatically included if present and follow this naming convention; Do not check these files into the repository: '_SRC_/custom.defs' Custom makevar definitions outside 'build'. Suitable for settings which apply across all builds for a particular checkout; or which survives manual removal of 'build'. '_SRC_/custom.rules' Custom make rules outside 'build'. Suitable for rules which apply across all builds for a particular checkout; or which survives manual removal of 'build'. '_BUILD_/GNUmakefile.custom.defs' Custom makevar definitions specific to a 'build' directory. '_BUILD_/GNUmakefile.custom.rules' Custom makevar rules specific to a 'build' directory. The purpose is to allow a place to store local build settings for testing, tweaking, and experimenting with build configuration without losing your settings if 'configure' is invoked; ie: 'configure' would overwrite 'GNUmakefile' and any customizations contained therein would be lost. Here is a short example of what the contents of '_SRC_/custom.defs' might contain: ## bump to gcc-4.6 in current path GCC.gcc = /usr/bin/gcc-4.6 ## replace optimize for 'speed' with more aggressive settings GCC.args.O.speed = -O3 -fomit-frame-pointer -msse4.2 See also 'make report.help' which displays a set of reports used to dump makefile vars. 5.6 Universal Binaries ====================== This section outlines convenience procedures for creating Universal Binaries for all the architectures. Note: The dummy (container) build configuration uses '--disable-xcode'; but the nested architecture builds will all make full use of Xcode. Create a dummy (container) build configuration and use it to launch a nested-build for each architecture: ./configure --disable-xcode cd build/ make ub.build make ub.combine The list of architectures is hard coded to HandBrake's desired product and currently is composed of combining the binaries produced from two xcconfigs: osx106.i386 and osx106.x86_64. The following example shows how to specify a different list of xcconfigs: ./configure --disable-xcode cd build/ make UB.xcconfigs="osx107.i386 osx107.x86_64" ub.build make UB.xcconfigs="osx107.i386 osx107.x86_64" ub.combine 6 Building via Xcode.app ************************ 6.1 Checkout Sources ==================== Checkout HandBrake from the official source-code repository. svn checkout svn://svn.handbrake.fr/HandBrake/trunk hb-trunk cd hb-trunk Sources are checked out from the 'trunk' branch. This document was generated from that very branch, and for example purposes, we will use exactly the same branch. If you have write-access to the repository, then you may add the appropriate login/password information as needed. It is recommended to use Subversion 1.6.0 or higher. Lower versions should also work. 6.2 Build ========= Perform the following steps to build: * Finder - navigate to 'macosx/' in the HandBrake source tree * Finder - open 'HandBrake.xcodeproj' * Xcode workspace - select scheme HandBrake [RELEASE] * Xcode menu - select Product -> Build * Xcode workspace - Show the Log navigator * Xcode workspace Log navigator - select top Build item 6.3 Note: Finding Built Products ================================ Under default Xcode.app options the products from a build are managed by the Xcode Organizer. Perform the following steps to open Finder at top of build tree and navigate to release products: * Xcode menu - select Window -> Organizer * Xcode organizer - select Projects tab * Xcode organizer Projects - select HandBrake item * HandBrake item - click Derived Data location arrow (immediately right of path) * Finder - navigate to Build -> Products -> release Note: There is a bug with Xcode Organizer. The very first time an Xcode project is opened the Project view Derived Data is greyed-out. Workaround glitch by selecting any other tab and then reselecting Projects tab. 6.4 Note: Workspace Log Behaviors ================================= The default Workspace behavior does not display latest Build log in the navigator and quickly becomes tedious. To automatically switch to Log navigator and show current log: * Xcode menu - select Behaviors -> Edit Behaviors * Xcode behaviors - select Build starts * navigator - enable, select Show, select Log Navigator * nagivate to - select current log Note: The Log navigator supports some possibly confusing options. It is recommended to only show results for the last build by selecting Recent. If All is selected then it will look as though Xcode is performing a build, but in reality it is bringing forward log output from prior builds and it becomes impossible to tell if any single log entry represents actual work performed or if it was brought forward from history. Note: When building external target, many 3rd-party contributed modules have warnings and errors which may safely be ignored and are ignored by the external build system. Ultimately, look to the workspace status indicator for Build Succeeded. 6.5 External Target =================== The external target mechanism is used to launch a full terminal-based build from within Xcode. Unfortunately, we do not have hooks in place to offer finer-grained control over per-module make actions. However, you can still use terminal to accomplish those tasks after launching the build at least once or doing a clean from within Xcode. Be careful to not issue terminal commands simultaneously with Xcode tasks. Invoking a clean from Xcode always destroys the entire external build tree and subsequently configures it. Changing settings in Xcode such as selecting xcconfig files should always be followed by a clean. This allows the external build system configuration to accurately reflect Xcode project changes. The following are some examples of using 'make' from the terminal to effect various components of the external build. But first, you must open a terminal at the top of the external build output tree. Here we navigate to external build configured for release: * Xcode menu - select Window -> Organizer * Xcode organizer - select Projects tab * Xcode organizer Projects - select HandBrake item * HandBrake item - click Derived Data location arrow (immediately right of path) * Finder - navigate to Build -> Products -> release -> external Example; external build failed but error is buried in a parallelized log; redo build sequentially: make xclean make BUILD.jobs=1 Example; build external x264 module: make x264.clean make x264 Example; extract, configure, build and install external x264 module: make x264.xclean make x264.install Example; something in a big module is failing; redo build sequentially: make ffmpeg.clean make BUILD.jobs=1 ffmpeg 6.6 User-Defined Settings ========================= The following user defined settings are visible in Xcode project and are used for the external build system. 'EXTERNAL_BUILD' Do not modify; used to specify the build (scratch) directory. 'EXTERNAL_DRIVER' Do not modify; used for internal/external build coordination and must always be 'xcode'. 'EXTERNAL_JOBS' Specifies the concurrency factor for the external build system when builds are launched from within Xcode. Modify for faster external builds if your system has the horsepower and resources. Specifying a value greater than the number of CPU cores (or virtual cores) in your system is unlikely to produce gains and will needlessly consume extra resources. A special string value of auto sets the factor to the number of active CPUs on the host system. 'EXTERNAL_SRC' Do not modify; specifies the top-level source directory for HandBrake, relative to Xcode project. 'EXTERNAL_XCCONFIG' Do not modify; specifies which xcconfig file is active. Defined inside xcconfig file. 7 Troubleshooting ***************** When troubleshooting build issues, the following files relative to the 'build/' directory may be especially useful: 'GNUmakefile' Top-level makefile which contains build settings generated via configure. 'log/config.info.txt' Record of output from configure. 'log/config.verbose.txt' Record of verbose output from configure. 'log/build.txt' Record of output from 'configure --launch'. Similar output may be recorded using 'make' depending on which shell is in use, eg: 'make >& log/build.txt' or 'make > log/build.txt 2>&1'. 'log/xcodemake.env.txt' Environment (variables) dump as seen when Xcode forks 'make'. Mac OS X only. Appendix A Project Repository Details ************************************* url: svn://svn.handbrake.fr/HandBrake/trunk root: svn://svn.handbrake.fr/HandBrake branch: trunk uuid: b64f7644-9d1e-0410-96f1-a4d463321fa5 rev: 6163 date: 2014-04-13 09:22:16 -0700 type: developer HandBrake-0.10.2/doc/BUILD-MinGW0000664000175200017520000003514112323006750016347 0ustar handbrakehandbrakeGuide to Building HandBrake svn6163 (2014041401) on MinGW ********************************************************* 1 Introduction 2 Prerequisites 3 QuickStart 4 Overview 5 Building via Terminal 5.1 Checkout Sources 5.2 Configure 5.3 Build 5.4 Make Targets 5.4.1 Global 5.4.2 General Modules 5.4.3 Contrib Modules 5.4.4 Contrib Touch and Untouch 5.4.5 Contrib Aggregates 5.5 Customizing Make 6 Troubleshooting Appendix A Project Repository Details 1 Introduction ************** This guide documents the recommended process to build HandBrake on MinGW hosts from the official source-code repository. Building from any other source is not supported. 2 Prerequisites *************** The following are the recommended specifications for building on MinGW; but is not necessarily the only configuration that is possible: * Intel 32-bit or 64-bit hardware * MinGW on Fedora 17+, Ubuntu 12+ or Debian, gcc 4.6+ * yasm 1.2.x (for i386 or x86_64 architectures) Note: It is recommended to use the platform distribution's standard compiler for maximum C++ compatibility. If you build with a custom compiler it will likely introduce non-standard runtime requirements and have new/delete, exception and RTTI incompatibilities. There are of course many valid reasons to build with unbundled compilers, but be aware it is generally unsupported and left as an exercise to the reader. Note: You must set the -cross flag with configure to cross compile with windows. See the Example below. Note that the cross compiler name varies between distributions. As of this writing, MinGW has available to it several versions of gcc; only one of which may be found and used in the path as 'gcc' and 'g++'. Configure will thus find what is probably the older version of gcc in a typical MinGW environment. If you desire to build with the newer gcc, it is found in the path as 'gcc-4' and 'g++-4' respectively and you must indicate to configure the desired versions. The following syntax should do the trick: ../configure --cross=i686-w64-mingw32 --gcc=gcc-4 The following general tools are used on various platforms and it is recommended you use these versions or similar: * subversion - 1.6.16 * python - Python 2.7.1 * curl - curl 7.21.4 (or wget) * m4 - GNU M4 1.4.6 * make - GNU Make 3.81 * patch - Patch 2.5.8 * tar - GNU tar 1.26 * wget - GNU Wget 1.13.4 (or curl) 3 QuickStart ************ This chapter is for building from a terminal/shell environment in as few commands as possible. Upon completion of the following commands you should have a fresh build of HandBrake. Further instructions are available beginning with *note overview:: which describes procedures suitable for repeating builds. This chapter should be skipped by those seeking more than a minimalist build. svn checkout svn://svn.handbrake.fr/HandBrake/trunk hb-trunk cd hb-trunk ./configure --launch The special option '--launch' selects launch mode and performs the following steps: * assert scratch directory 'build/' does not exist * create scratch directory 'build/' * change to directory 'build/' * launch 'make' * capture build output to 'build/log/build.txt' * echo build output * print elapsed time * indicate if build ultimately succeeded or failed 4 Overview ********** MinGW builds are performed from a terminal. There is no support for building from any IDEs. 5 Building via Terminal *********************** 5.1 Checkout Sources ==================== Checkout HandBrake from the official source-code repository. svn checkout svn://svn.handbrake.fr/HandBrake/trunk hb-trunk cd hb-trunk Sources are checked out from the 'trunk' branch. This document was generated from that very branch, and for example purposes, we will use exactly the same branch. If you have write-access to the repository, then you may add the appropriate login/password information as needed. It is recommended to use Subversion 1.6.0 or higher. Lower versions should also work. 5.2 Configure ============= Configure the build system. ./configure Configure will automatically create a scratch build directory 'build' unless you use GNU-style build procedures and first 'cd' to a directory other than top-level source. Additionally you may use '--build' to specify the directory. The name of the directory is arbitrary but it is recommended to use something which indicates transient files which are not checked into the repository. The 'configure' utility accepts many options. It is recommended that you specify '--help' for the complete list of options. The following options are also documented here: '--help' List available options. '--src=DIR' Specify top-level source directory for HandBrake sources. '--build=DIR' Specify destination directory for final product install. The default is to use either 'build' if in the top-level source directory, otherwise '.' '--prefix=DIR' Specify destination directory for final product install. This defaults to a reasonable platform-specific value. '--launch' All-in-one option which launches the build and logs output automatically. Useful for novices and quick-start procedures. '--disable-gtk' Disable building the GTK GUI on applicable platforms such as Linux. '--debug=MODE' Select debug mode. Must be one of 'none', 'min', 'std', 'max'. This generally maps to gcc options '-g0', '-g1', '-g2', '-g3'. '--optimize=MODE' Select optimize mode. Must be one of 'none', 'speed', 'size'. This generally maps to gcc options '-g0', '-O0', '-O3', '-Os'. '--arch=MODE' Select build architecture. The available architectures vary by platform. Most platforms support exactly one architecture except Mac OS X which has support for various universal binary architectures. The available choices are hard-coded per platform and no sanity checks for the required tools are performed. '--disable-xcode' Disable shunting the build through 'xcodebuild'. If this option is applied, 'HandBrakeCLI' will be produced in a similar fashion as it is on other platforms; sans Xcode and the Cocoa application will not be produced. Mac OS X only. '--xcconfig=MODE' Select Xcode project configuration file. The available modes are the basenames of files located in 'macosx/xcconfig/*.xcconfig' which direct Xcode to build using various architecture and Mac OS X deployment options. Mac OS X only. Clean-room procedures dictate that when certain factors change, old builds should be scrapped and new builds configured. This is the main reason for requiring a scratch directory; to promote consistent, reliable and clean software builds. The following is a short list of some of the reasons why someone may choose to scrap an existing build: * configure with different options * subversion working dir is updated and you want configure to re-evaluate working dir metadata. * build corruption is suspected There are generally two methods for scrapping a build. The 'build' directory can be recursively removed which has the effect of loosing your existing configuration but does guarantee no residuals are left behind. The other method is to ask the build system to perform an 'make xclean'. This is known to work well but will leave empty directories behind. However, the configuration is left intact. 5.3 Build ========= Build main product. All necessary dependencies are also built if required. make Parallel builds may optionally be enabled. Be aware that while a parallel build may save time on systems with additional cores, the output is often mixed, overlapped and sometimes even corrupted with binary characters. Thus if you experience a build issue, you should clean and redo the build in default serial mode to produce a readable log. The following command allows for up to 4 concurrent jobs via make: make -j4 5.4 Make Targets ================ The build system supports passing many kinds of targets some of which become very useful in normal development cycles. The targets by convention are lower-case words passed to 'make'. Global targets are one-word targets. Scoped targets are usually two-words separated by a period. 5.4.1 Global ------------ 'make' Alias for 'make build'. 'make build' Build main product. All necessary dependencies are also built if required. 'make clean' Clean all build output excluding contrib modules. Configuration is retained. 'make install' Perform final product(s) install. This will install build products to a standard directory or one specified via 'configure --prefix' option. 'make uninstall' Perform final product(s) uninstall. This will uninstall any products which may have been previously installed. 'make xclean' Clean all build output including contrib modules. Configuration is retained. 'make doc' Build auto-generated project documentation. Various articles are produced and may be found in 'build/doc/articles'. 'make doc.post' Build auto-generated project documentation and post produced articles directly to source tree. 'make report.help' Print list of available makefile vars report targets. These reports detail var definitions and expanded values used by the build system. For experts only. 'make report.all' Convenience target which aggregates all reports. For experts only. 5.4.2 General Modules --------------------- General modules such as 'libhb', 'test' and 'gtk' have the following scoped targets: 'make MODULE.build' Build MODULE. 'make MODULE.clean' Clean build output for MODULE. 5.4.3 Contrib Modules --------------------- Contrib modules such as 'a52dec', 'bzip2', 'faac', 'faad2', 'ffmpeg', 'fontconfig', 'freetype', 'fribidi', 'lame', 'libass', 'libbluray', 'libdca', 'libdvdnav', 'libdvdread', 'libiconv', 'libmkv', 'libogg', 'libsamplerate', 'libtheora', 'libvorbis', 'libxml2', 'mp4v2', 'mpeg2dec', 'x264', 'yasm' and 'zlib' have the following scoped targets: 'make MODULE.fetch' Download source tarball from the Internet and save to 'TOP/downloads' directory. No check-summing is performed. 'make MODULE.extract' Extract source tarball into 'build' tree. 'make MODULE.patch' Apply appropriate patches (if any) to module sources. 'make MODULE.configure' Configure module sources. This usually invokes autotool configure. 'make MODULE.build' Build module. This usually invokes autotool build. 'make MODULE.install' Install module products such as headers and libraries into 'build' tree. This usually invokes autotool install. 'make MODULE.uninstall' Uninstall module products; generally the reverse of install. This usually invokes autotool uninstall. 'make MODULE.clean' Clean module; generally the reverse of build. This usually invokes autotool clean. 'make MODULE.xclean' Extra clean module; first invokes uninstall then recursively removes the module build directory. 5.4.4 Contrib Touch and Untouch ------------------------------- Also available are some very granular targets which help force builds from specific cycle points. The following targets are available to touch and untouch the respective module target; this will force the build system to treat the target as satisfied after a touch or unsatisfied after an untouch: * make MODULE.extract.touch * make MODULE.extract.untouch * make MODULE.patch.touch * make MODULE.patch.untouch * make MODULE.configure.touch * make MODULE.configure.untouch * make MODULE.build.touch * make MODULE.build.untouch * make MODULE.install.touch * make MODULE.install.untouch 5.4.5 Contrib Aggregates ------------------------ For convenience, the following targets aggregate the all contrib modules' respective targets together: * make contrib.fetch * make contrib.extract * make contrib.patch * make contrib.configure * make contrib.build * make contrib.install * make contrib.uninstall * make contrib.clean * make contrib.xclean 5.5 Customizing Make ==================== If the need arises to override settings in the build system (essentially gnu-make variables) the recommended method is to create optional include files which are automatically included if present and follow this naming convention; Do not check these files into the repository: '_SRC_/custom.defs' Custom makevar definitions outside 'build'. Suitable for settings which apply across all builds for a particular checkout; or which survives manual removal of 'build'. '_SRC_/custom.rules' Custom make rules outside 'build'. Suitable for rules which apply across all builds for a particular checkout; or which survives manual removal of 'build'. '_BUILD_/GNUmakefile.custom.defs' Custom makevar definitions specific to a 'build' directory. '_BUILD_/GNUmakefile.custom.rules' Custom makevar rules specific to a 'build' directory. The purpose is to allow a place to store local build settings for testing, tweaking, and experimenting with build configuration without losing your settings if 'configure' is invoked; ie: 'configure' would overwrite 'GNUmakefile' and any customizations contained therein would be lost. Here is a short example of what the contents of '_SRC_/custom.defs' might contain: ## bump to gcc-4.6 in current path GCC.gcc = /usr/bin/gcc-4.6 ## replace optimize for 'speed' with more aggressive settings GCC.args.O.speed = -O3 -fomit-frame-pointer -msse4.2 See also 'make report.help' which displays a set of reports used to dump makefile vars. 6 Troubleshooting ***************** When troubleshooting build issues, the following files relative to the 'build/' directory may be especially useful: 'GNUmakefile' Top-level makefile which contains build settings generated via configure. 'log/config.info.txt' Record of output from configure. 'log/config.verbose.txt' Record of verbose output from configure. 'log/build.txt' Record of output from 'configure --launch'. Similar output may be recorded using 'make' depending on which shell is in use, eg: 'make >& log/build.txt' or 'make > log/build.txt 2>&1'. 'log/xcodemake.env.txt' Environment (variables) dump as seen when Xcode forks 'make'. Mac OS X only. Appendix A Project Repository Details ************************************* url: svn://svn.handbrake.fr/HandBrake/trunk root: svn://svn.handbrake.fr/HandBrake branch: trunk uuid: b64f7644-9d1e-0410-96f1-a4d463321fa5 rev: 6163 date: 2014-04-13 09:22:16 -0700 type: developer HandBrake-0.10.2/doc/module.defs0000664000175200017520000000301611155573060016737 0ustar handbrakehandbrake$(eval $(call import.MODULE.defs,DOC,doc)) DOC.in/ = $(SRC/)doc/ DOC.out/ = $(BUILD/)doc/ DOC.out.api/ = $(DOC.out/)api/ DOC.out.articles/ = $(DOC.out/)articles/ ############################################################################### DOC.m4.srcs = \ texi/base/handbrake.texi.m4 DOC.m4.in = $(foreach n,$(DOC.m4.srcs),$(DOC.in/)$n) DOC.m4.out = $(patsubst $(DOC.in/)%.m4,$(DOC.out/)%,$(DOC.m4.in)) DOC.texi.includes = $(wildcard $(DOC.in/)texi/*/*.texi) DOC.texi.articles = $(wildcard $(DOC.in/)texi/*.texi) DOC.texi2html.out = $(patsubst $(DOC.in/)texi/%.texi,$(DOC.out.articles/)html/%.html,$(DOC.texi.articles)) DOC.texi2txt.out = $(patsubst $(DOC.in/)texi/%.texi,$(DOC.out.articles/)txt/%.txt,$(DOC.texi.articles)) DOC.texi2xml.out = $(patsubst $(DOC.in/)texi/%.texi,$(DOC.out.articles/)xml/%.xml,$(DOC.texi.articles)) BUILD.out += $(DOC.m4.out) BUILD.out += $(DOC.texi2html.out) BUILD.out += $(DOC.texi2txt.out) BUILD.out += $(DOC.texi2xml.out) ############################################################################### DOC.M4.exe = $(M4.exe) DOC.M4.flags = -I$(BUILD/)project DOC.M4.deps = $(BUILD/)project/handbrake.m4 DOC.M4 = $(DOC.M4.exe) $(DOC.M4.flags) $(2) > $(1) MAKEINFO.exe = makeinfo MAKEINFO.flags = -I$(DOC.out/)texi -I$(DOC.in/)texi MAKEINFO.flags.html = --html --no-headers --no-split MAKEINFO.flags.txt = --plaintext --no-headers MAKEINFO.flags.xml = --xml --output-indent=4 MAKEINFO = $(MAKEINFO.exe) $(MAKEINFO.flags) $(MAKEINFO.flags.$(1)) $(2) -o $(3) HandBrake-0.10.2/doc/BUILD-Linux0000664000175200017520000004145212422537240016472 0ustar handbrakehandbrakeGuide to Building HandBrake svn6419 (2014093001) on Linux ********************************************************* 1 Introduction 2 Prerequisites 3 QuickStart 4 Overview 5 Building via Terminal 5.1 Checkout Sources 5.2 Configure 5.3 Build 5.4 Make Targets 5.4.1 Global 5.4.2 General Modules 5.4.3 Contrib Modules 5.4.4 Contrib Touch and Untouch 5.4.5 Contrib Aggregates 5.5 Customizing Make 6 Troubleshooting Appendix A Project Repository Details 1 Introduction ************** This guide documents the recommended process to build HandBrake on Linux hosts from the official source-code repository. Building from any other source is not supported. 2 Prerequisites *************** The following are the recommended specifications for building on Linux; but is not necessarily the only configuration that is possible: * Intel 32-bit or 64-bit kernel * Ubuntu 14.04, gcc 4.8, yasm 1.3.x * Fedora 20, gcc 4.8, yasm 1.3.x * gcc 4.7 or higher is reported to work Note: It is recommended to use the platform distribution's standard compiler for maximum C++ compatibility. If you build with a custom compiler it will likely introduce non-standard runtime requirements and have new/delete, exception and RTTI incompatibilities. There are of course many valid reasons to build with unbundled compilers, but be aware it is generally unsupported and left as an exercise to the reader. The following general tools are used on various platforms and it is recommended you use these versions or similar: * subversion - 1.6.16 * python - Python 2.7.1 * curl - curl 7.21.4 (or wget) * m4 - GNU M4 1.4.6 * make - GNU Make 3.81 * patch - Patch 2.5.8 * tar - GNU tar 1.26 * wget - GNU Wget 1.13.4 (or curl) The GTK UI introduces some significant extra build requirements. GTK version 3.10+ is required to build the GTK UI. If you intend to disable building the GUI with 'configure --disable-gtk' you will not need many of these packages installed: Ubuntu 14.04 packages: * subversion (cli/gui) * cmake (cli/gui) * yasm (cli/gui) * build-essential (cli/gui) * autoconf (cli/gui) * libtool (cli/gui) * zlib1g-dev (cli/gui) * libbz2-dev (cli/gui) * libxml2-dev (cli/gui) * libogg-dev (cli/gui) * libtheora-dev (cli/gui) * libvorbis-dev (cli/gui) * libsamplerate-dev (cli/gui) * libfribidi-dev (cli/gui) * libfreetype6-dev (cli/gui) * libfontconfig1-dev (cli/gui) * libass-dev (cli/gui) * libmp3lame-dev (cli/gui) * libx264-dev (cli/gui) * intltool (gui) * libglib2.0-dev (gui) * libdbus-glib-1-dev (gui) * libgtk-3-dev (gui) * libgudev-1.0-dev (gui) * libwebkitgtk-3.0-dev (gui) * libnotify-dev (gui) * libgstreamer1.0-dev (gui) * libgstreamer-plugins-base1.0-dev (gui) * libappindicator-dev (gui) To install these packages: sudo apt-get install subversion cmake yasm build-essential autoconf libtool \ zlib1g-dev libbz2-dev libogg-dev libtheora-dev libvorbis-dev \ libsamplerate-dev libxml2-dev libfribidi-dev libfreetype6-dev \ libfontconfig1-dev libass-dev libmp3lame-dev libx264-dev intltool \ libglib2.0-dev libdbus-glib-1-dev libgtk-3-dev libgudev-1.0-dev \ libwebkitgtk-3.0-dev libnotify-dev libgstreamer1.0-dev \ libgstreamer-plugins-base1.0-dev libappindicator-dev Fedora 20 package groups: * Development Tools * Development Libraries * X Software Development (gui) * GNOME Software Development (gui) To install these package groups: sudo yum groupinstall "Development Tools" "Development Libraries" \ "X Software Development" "GNOME Software Development" Additional Fedora packages: * patch (cli/gui) * intltool (cli/gui) * libtool (cli/gui) * cmake (cli/gui) * gcc-c++ (cli/gui) * yasm (cli/gui) * zlib-devel (cli/gui) * bzip2-devel (cli/gui) * libogg-devel (cli/gui) * libtheora-devel (cli/gui) * libvorbis-devel (cli/gui) * libsamplerate-devel (cli/gui) * libxml2-devel (cli/gui) * fribidi-devel (cli/gui) * freetype-devel (cli/gui) * fontconfig-devel (cli/gui) * libass-devel (cli/gui) * lame-devel (cli/gui) * x264-devel (cli/gui) * dbus-glib-devel (gui) * libgudev1-devel (gui) * webkitgtk3-devel (gui) * libnotify-devel (gui) * gstreamer1-devel (gui) * gstreamer1-plugins-base-devel (gui) To install these packages: sudo yum install patch intltool libtool cmake gcc-c++ yasm zlib-devel \ bzip2-devel libogg-devel libtheora-devel libvorbis-devel libsamplerate-devel \ libxml2-devel fribidi-devel freetype-devel fontconfig-devel libass-devel \ lame-devel x264-devel dbus-glib-devel libgudev1-devel webkitgtk3-devel \ libnotify-devel gstreamer1-devel gstreamer1-plugins-base-devel 3 QuickStart ************ This chapter is for building from a terminal/shell environment in as few commands as possible. Upon completion of the following commands you should have a fresh build of HandBrake. Further instructions are available beginning with *note overview:: which describes procedures suitable for repeating builds. This chapter should be skipped by those seeking more than a minimalist build. svn checkout svn://svn.handbrake.fr/HandBrake/trunk hb-trunk cd hb-trunk ./configure --launch The special option '--launch' selects launch mode and performs the following steps: * assert scratch directory 'build/' does not exist * create scratch directory 'build/' * change to directory 'build/' * launch 'make' * capture build output to 'build/log/build.txt' * echo build output * print elapsed time * indicate if build ultimately succeeded or failed 4 Overview ********** Linux builds are performed from a terminal. There is no support for building from any IDEs. 5 Building via Terminal *********************** 5.1 Checkout Sources ==================== Checkout HandBrake from the official source-code repository. svn checkout svn://svn.handbrake.fr/HandBrake/trunk hb-trunk cd hb-trunk Sources are checked out from the 'trunk' branch. This document was generated from that very branch, and for example purposes, we will use exactly the same branch. If you have write-access to the repository, then you may add the appropriate login/password information as needed. It is recommended to use Subversion 1.6.0 or higher. Lower versions may also work. 5.2 Configure ============= Configure the build system. ./configure Configure will automatically create a scratch build directory 'build' unless you use GNU-style build procedures and first 'cd' to a directory other than top-level source. Additionally you may use '--build' to specify the directory. The name of the directory is arbitrary but it is recommended to use something which indicates transient files which are not checked into the repository. The 'configure' utility accepts many options. It is recommended that you specify '--help' for the complete list of options. The following options are also documented here: '--help' List available options. '--src=DIR' Specify top-level source directory for HandBrake sources. '--build=DIR' Specify destination directory for final product install. The default is to use either 'build' if in the top-level source directory, otherwise '.' '--prefix=DIR' Specify destination directory for final product install. This defaults to a reasonable platform-specific value. '--launch' All-in-one option which launches the build and logs output automatically. Useful for novices and quick-start procedures. '--disable-gtk' Disable building the GTK GUI on applicable platforms such as Linux. '--debug=MODE' Select debug mode. Must be one of 'none', 'min', 'std', 'max'. This generally maps to gcc options '-g0', '-g1', '-g2', '-g3'. '--optimize=MODE' Select optimize mode. Must be one of 'none', 'speed', 'size'. This generally maps to gcc options '-g0', '-O0', '-O3', '-Os'. '--arch=MODE' Select build architecture. The available architectures vary by platform. Most platforms support exactly one architecture except Mac OS X which has support for various universal binary architectures. The available choices are hard-coded per platform and no sanity checks for the required tools are performed. '--disable-xcode' Disable shunting the build through 'xcodebuild'. If this option is applied, 'HandBrakeCLI' will be produced in a similar fashion as it is on other platforms; sans Xcode and the Cocoa application will not be produced. Mac OS X only. '--xcconfig=MODE' Select Xcode project configuration file. The available modes are the basenames of files located in 'macosx/xcconfig/*.xcconfig' which direct Xcode to build using various architecture and Mac OS X deployment options. Mac OS X only. Clean-room procedures dictate that when certain factors change, old builds should be scrapped and new builds configured. This is the main reason for requiring a scratch directory; to promote consistent, reliable and clean software builds. The following is a short list of some of the reasons why someone may choose to scrap an existing build: * configure with different options * subversion working dir is updated and you want configure to re-evaluate working dir metadata. * build corruption is suspected There are generally two methods for scrapping a build. The 'build' directory can be recursively removed which has the effect of loosing your existing configuration but does guarantee no residuals are left behind. The other method is to ask the build system to perform an 'make xclean'. This is known to work well but will leave empty directories behind. However, the configuration is left intact. 5.3 Build ========= Build main product. All necessary dependencies are also built if required. make Parallel builds may optionally be enabled. Be aware that while a parallel build may save time on systems with additional cores, the output is often mixed, overlapped and sometimes even corrupted with binary characters. Thus if you experience a build issue, you should clean and redo the build in default serial mode to produce a readable log. The following command allows for up to 4 concurrent jobs via make: make -j4 5.4 Make Targets ================ The build system supports passing many kinds of targets some of which become very useful in normal development cycles. The targets by convention are lower-case words passed to 'make'. Global targets are one-word targets. Scoped targets are usually two-words separated by a period. 5.4.1 Global ------------ 'make' Alias for 'make build'. 'make build' Build main product. All necessary dependencies are also built if required. 'make clean' Clean all build output excluding contrib modules. Configuration is retained. 'make install' Perform final product(s) install. This will install build products to a standard directory or one specified via 'configure --prefix' option. 'make uninstall' Perform final product(s) uninstall. This will uninstall any products which may have been previously installed. 'make xclean' Clean all build output including contrib modules. Configuration is retained. 'make doc' Build auto-generated project documentation. Various articles are produced and may be found in 'build/doc/articles'. 'make doc.post' Build auto-generated project documentation and post produced articles directly to source tree. 'make report.help' Print list of available makefile vars report targets. These reports detail var definitions and expanded values used by the build system. For experts only. 'make report.all' Convenience target which aggregates all reports. For experts only. 5.4.2 General Modules --------------------- General modules such as 'libhb', 'test' and 'gtk' have the following scoped targets: 'make MODULE.build' Build MODULE. 'make MODULE.clean' Clean build output for MODULE. 5.4.3 Contrib Modules --------------------- Contrib modules such as 'bzip2', 'ffmpeg', 'fontconfig', 'freetype', 'fribidi', 'lame', 'libass', 'libbluray', 'libdvdnav', 'libdvdread', 'libiconv', 'libogg', 'libsamplerate', 'libtheora', 'libvorbis', 'libxml2', 'x264', 'yasm' and 'zlib' have the following scoped targets: 'make MODULE.fetch' Download source tarball from the Internet and save to 'TOP/downloads' directory. No check-summing is performed. 'make MODULE.extract' Extract source tarball into 'build' tree. 'make MODULE.patch' Apply appropriate patches (if any) to module sources. 'make MODULE.configure' Configure module sources. This usually invokes autotool configure. 'make MODULE.build' Build module. This usually invokes autotool build. 'make MODULE.install' Install module products such as headers and libraries into 'build' tree. This usually invokes autotool install. 'make MODULE.uninstall' Uninstall module products; generally the reverse of install. This usually invokes autotool uninstall. 'make MODULE.clean' Clean module; generally the reverse of build. This usually invokes autotool clean. 'make MODULE.xclean' Extra clean module; first invokes uninstall then recursively removes the module build directory. 5.4.4 Contrib Touch and Untouch ------------------------------- Also available are some very granular targets which help force builds from specific cycle points. The following targets are available to touch and untouch the respective module target; this will force the build system to treat the target as satisfied after a touch or unsatisfied after an untouch: * make MODULE.extract.touch * make MODULE.extract.untouch * make MODULE.patch.touch * make MODULE.patch.untouch * make MODULE.configure.touch * make MODULE.configure.untouch * make MODULE.build.touch * make MODULE.build.untouch * make MODULE.install.touch * make MODULE.install.untouch 5.4.5 Contrib Aggregates ------------------------ For convenience, the following targets aggregate the all contrib modules' respective targets together: * make contrib.fetch * make contrib.extract * make contrib.patch * make contrib.configure * make contrib.build * make contrib.install * make contrib.uninstall * make contrib.clean * make contrib.xclean 5.5 Customizing Make ==================== If the need arises to override settings in the build system (essentially gnu-make variables) the recommended method is to create optional include files which are automatically included if present and follow this naming convention; Do not check these files into the repository: '_SRC_/custom.defs' Custom makevar definitions outside 'build'. Suitable for settings which apply across all builds for a particular checkout; or which survives manual removal of 'build'. '_SRC_/custom.rules' Custom make rules outside 'build'. Suitable for rules which apply across all builds for a particular checkout; or which survives manual removal of 'build'. '_BUILD_/GNUmakefile.custom.defs' Custom makevar definitions specific to a 'build' directory. '_BUILD_/GNUmakefile.custom.rules' Custom makevar rules specific to a 'build' directory. The purpose is to allow a place to store local build settings for testing, tweaking, and experimenting with build configuration without losing your settings if 'configure' is invoked; ie: 'configure' would overwrite 'GNUmakefile' and any customizations contained therein would be lost. Here is a short example of what the contents of '_SRC_/custom.defs' might contain: ## bump to gcc-4.6 in current path GCC.gcc = /usr/bin/gcc-4.6 ## replace optimize for 'speed' with more aggressive settings GCC.args.O.speed = -O3 -fomit-frame-pointer -msse4.2 See also 'make report.help' which displays a set of reports used to dump makefile vars. 6 Troubleshooting ***************** When troubleshooting build issues, the following files relative to the 'build/' directory may be especially useful: 'GNUmakefile' Top-level makefile which contains build settings generated via configure. 'log/config.info.txt' Record of output from configure. 'log/config.verbose.txt' Record of verbose output from configure. 'log/build.txt' Record of output from 'configure --launch'. Similar output may be recorded using 'make' depending on which shell is in use, eg: 'make >& log/build.txt' or 'make > log/build.txt 2>&1'. 'log/xcodemake.env.txt' Environment (variables) dump as seen when Xcode forks 'make'. Mac OS X only. Appendix A Project Repository Details ************************************* url: svn://svn.handbrake.fr/HandBrake/trunk root: svn://svn.handbrake.fr/HandBrake branch: trunk uuid: b64f7644-9d1e-0410-96f1-a4d463321fa5 rev: 6419 date: 2014-09-21 09:44:45 -0700 type: developer HandBrake-0.10.2/doc/module.rules0000664000175200017520000000201012007516767017151 0ustar handbrakehandbrake$(eval $(call import.MODULE.rules,DOC)) doc: doc.txt doc.post: doc.txt $(CP.exe) $(DOC.out.articles/)txt/Building.osx.txt $(DOC.in/)BUILD-Mac $(CP.exe) $(DOC.out.articles/)txt/Building.linux.txt $(DOC.in/)BUILD-Linux $(CP.exe) $(DOC.out.articles/)txt/Building.mingw.txt $(DOC.in/)BUILD-MinGW doc.txt: $(DOC.texi2txt.out) doc.xml: $(DOC.texi2xml.out) doc.clean: $(RM.exe) -fr $(DOC.out/) $(DOC.texi2txt.out): | $(DOC.m4.out) $(DOC.texi2txt.out): | $(dir $(DOC.texi2txt.out)) $(DOC.texi2txt.out): $(DOC.texi.includes) $(DOC.texi2txt.out): $(DOC.out.articles/)txt/%.txt: $(DOC.in/)texi/%.texi $(call MAKEINFO,txt,$<,$@) @echo $^ $(DOC.texi2xml.out): | $(DOC.m4.out) $(DOC.texi2xml.out): | $(dir $(DOC.texi2xml.out)) $(DOC.texi2xml.out): $(DOC.texi.includes) $(DOC.texi2xml.out): $(DOC.out.articles/)xml/%.xml: $(DOC.in/)texi/%.texi $(call MAKEINFO,xml,$<,$@) @echo $^ $(DOC.m4.out): | $(dir $(DOC.m4.out)) $(DOC.m4.out): $(DOC.M4.deps) $(DOC.m4.out): $(DOC.out/)%: $(DOC.in/)%.m4 $(call DOC.M4,$@,$<) clean: doc.clean HandBrake-0.10.2/doc/texi/0000775000175200017520000000000012535641641015564 5ustar handbrakehandbrakeHandBrake-0.10.2/doc/texi/building/0000775000175200017520000000000012535641641017361 5ustar handbrakehandbrakeHandBrake-0.10.2/doc/texi/building/chapter.quickstart.texi0000664000175200017520000000165612271033462024074 0ustar handbrakehandbrake@anchor{quickstart} @chapter QuickStart This chapter is for building from a terminal/shell environment in as few commands as possible. Upon completion of the following commands you should have a fresh build of @value{HB-name}. Further instructions are available beginning with @ref{overview} which describes procedures suitable for repeating builds. This chapter should be skipped by those seeking more than a minimalist build. @example @COMMAND-checkout{} ./configure --launch @end example The special option @command{--launch} selects launch mode and performs the following steps: @itemize @item assert scratch directory @file{build/} does not exist @item create scratch directory @file{build/} @item change to directory @file{build/} @item launch @command{make} @item capture build output to @file{build/log/build.txt} @item echo build output @item print elapsed time @item indicate if build ultimately succeeded or failed @end itemize HandBrake-0.10.2/doc/texi/building/command.texi0000664000175200017520000000024312271033462021662 0ustar handbrakehandbrake@macro COMMAND-checkout{} svn checkout @value{HB-repo-url} @value{HB-acro-lower}-@value{HB-repo-branch} cd @value{HB-acro-lower}-@value{HB-repo-branch} @end macro HandBrake-0.10.2/doc/texi/building/appendix.repo.texi0000664000175200017520000000040612271033462023021 0ustar handbrakehandbrake@appendix Project Repository Details @example url: @value{HB-repo-url} root: @value{HB-repo-root} branch: @value{HB-repo-branch} uuid: @value{HB-repo-uuid} rev: @value{HB-repo-rev} date: @value{HB-repo-date} type: @value{HB-repo-type} @end example HandBrake-0.10.2/doc/texi/building/prerequisites.common.texi0000664000175200017520000000057011701017267024444 0ustar handbrakehandbrakeThe following general tools are used on various platforms and it is recommended you use these versions or similar: @itemize @bullet @item subversion - 1.6.16 @item python - Python 2.7.1 @item curl - curl 7.21.4 (or wget) @item m4 - GNU M4 1.4.6 @item make - GNU Make 3.81 @item patch - Patch 2.5.8 @item tar - GNU tar 1.26 @item wget - GNU Wget 1.13.4 (or curl) @end itemize HandBrake-0.10.2/doc/texi/building/chapter.troubleshooting.texi0000664000175200017520000000142412271033462025122 0ustar handbrakehandbrake@anchor{troubleshooting} @chapter Troubleshooting When troubleshooting build issues, the following files relative to the @file{build/} directory may be especially useful: @table @file @item GNUmakefile Top-level makefile which contains build settings generated via @b{configure}. @item log/config.info.txt Record of output from @b{configure}. @item log/config.verbose.txt Record of verbose output from @b{configure}. @item log/build.txt Record of output from @command{configure --launch}. Similar output may be recorded using @command{make} depending on which shell is in use, eg: @command{make >& log/build.txt} or @command{make > log/build.txt 2>&1}. @item log/xcodemake.env.txt Environment (variables) dump as seen when Xcode forks @command{make}. @value{OS-osx} only. @end table HandBrake-0.10.2/doc/texi/building/method.checkout.texi0000664000175200017520000000075712416262351023344 0ustar handbrakehandbrakeCheckout @value{HB-name} from the official source-code repository. @example @COMMAND-checkout{} @end example Sources are checked out from the @samp{@value{HB-repo-branch}} branch. This document was generated from that very branch, and for example purposes, we will use exactly the same branch. If you have write-access to the repository, then you may add the appropriate login/password information as needed. It is recommended to use Subversion 1.6.0 or higher. Lower versions may also work. HandBrake-0.10.2/doc/texi/building/prerequisites.bundled.texi0000664000175200017520000000067111701017267024573 0ustar handbrakehandbrake@quotation Note It is recommended to use the platform distribution's standard compiler for maximum C++ compatibility. If you build with a custom compiler it will likely introduce non-standard runtime requirements and have new/delete, exception and RTTI incompatibilities. There are of course many valid reasons to build with unbundled compilers, but be aware it is generally unsupported and left as an exercise to the reader. @end quotation HandBrake-0.10.2/doc/texi/building/chapter.introduction.texi0000664000175200017520000000035612271033462024417 0ustar handbrakehandbrake@anchor{introduction} @chapter Introduction This guide documents the recommended process to build @value{HB-name} on @value{BG-platform} hosts from the official source-code repository. @b{Building from any other source is not supported}. HandBrake-0.10.2/doc/texi/building/chapter.via.terminal.texi0000664000175200017520000002527212374457507024311 0ustar handbrakehandbrake@anchor{terminal} @chapter Building via Terminal @c %**------------------------------------------------------------------------- @anchor{terminal.checkout} @section Checkout Sources @include building/method.checkout.texi @c %**------------------------------------------------------------------------- @anchor{terminal.configure} @section Configure Configure the build system. @example ./configure @end example Configure will automatically create a scratch build directory @file{build} unless you use GNU-style build procedures and first @command{cd} to a directory other than top-level source. Additionally you may use @command{--build} to specify the directory. The name of the directory is arbitrary but it is recommended to use something which indicates transient files which are @b{not} checked into the repository. The @command{configure} utility accepts many options. It is recommended that you specify @command{--help} for the complete list of options. The following options are also documented here: @table @samp @item --help List available options. @item --src=DIR Specify top-level source directory for @value{HB-name} sources. @item --build=DIR Specify destination directory for final product install. The default is to use either @file{build} if in the top-level source directory, otherwise @file{.} @item --prefix=DIR Specify destination directory for final product install. This defaults to a reasonable platform-specific value. @item --launch All-in-one option which launches the build and logs output automatically. Useful for novices and quick-start procedures. @item --disable-gtk Disable building the GTK GUI on applicable platforms such as @value{OS-linux}. @item --debug=MODE Select debug mode. Must be one of @samp{none}, @samp{min}, @samp{std}, @samp{max}. This generally maps to gcc options @samp{-g0}, @samp{-g1}, @samp{-g2}, @samp{-g3}. @item --optimize=MODE Select optimize mode. Must be one of @samp{none}, @samp{speed}, @samp{size}. This generally maps to gcc options @samp{-g0}, @samp{-O0}, @samp{-O3}, @samp{-Os}. @item --arch=MODE Select build architecture. The available architectures vary by platform. Most platforms support exactly one architecture except @value{OS-osx} which has support for various universal binary architectures. The available choices are hard-coded per platform and no sanity checks for the required tools are performed. @item --disable-xcode Disable shunting the build through @command{xcodebuild}. If this option is applied, @command{HandBrakeCLI} will be produced in a similar fashion as it is on other platforms; sans Xcode and the Cocoa application will not be produced. @value{OS-osx} only. @item --xcconfig=MODE Select Xcode project configuration file. The available modes are the basenames of files located in @file{macosx/xcconfig/*.xcconfig} which direct Xcode to build using various architecture and @value{OS-osx} deployment options. @value{OS-osx} only. @end table Clean-room procedures dictate that when certain factors change, old builds should be scrapped and new builds configured. This is the main reason for requiring a scratch directory; to promote consistent, reliable and clean software builds. The following is a short list of some of the reasons why someone may choose to scrap an existing build: @itemize @item configure with different options @item subversion working dir is updated and you want configure to re-evaluate working dir metadata. @item build corruption is suspected @end itemize There are generally two methods for scrapping a build. The @file{build} directory can be recursively removed which has the effect of loosing your existing configuration but does guarantee no residuals are left behind. The other method is to ask the build system to perform an @command{make xclean}. This is known to work well but will leave empty directories behind. However, the configuration is left intact. @c %**------------------------------------------------------------------------- @anchor{terminal.build} @section Build Build main product. All necessary dependencies are also built if required. @example make @end example Parallel builds may optionally be enabled. Be aware that while a parallel build may save time on systems with additional cores, the output is often mixed, overlapped and sometimes even corrupted with binary characters. Thus if you experience a build issue, you should clean and redo the build in default serial mode to produce a readable log. The following command allows for up to 4 concurrent jobs via make: @example make -j4 @end example @c %**------------------------------------------------------------------------- @anchor{terminal.targets} @section Make Targets The build system supports passing many kinds of targets some of which become very useful in normal development cycles. The targets by convention are lower-case words passed to @command{make}. Global targets are one-word targets. Scoped targets are usually two-words separated by a period. @anchor{terminal.targets.global} @subsection Global @table @samp @item make Alias for @samp{make build}. @item make build Build main product. All necessary dependencies are also built if required. @item make clean Clean all build output excluding contrib modules. Configuration is retained. @item make install Perform final product(s) install. This will install build products to a standard directory or one specified via @command{configure --prefix} option. @item make uninstall Perform final product(s) uninstall. This will uninstall any products which may have been previously installed. @item make xclean Clean all build output including contrib modules. Configuration is retained. @item make doc Build auto-generated project documentation. Various articles are produced and may be found in @file{build/doc/articles}. @item make doc.post Build auto-generated project documentation and post produced articles directly to source tree. @item make report.help Print list of available makefile vars report targets. These reports detail var definitions and expanded values used by the build system. @b{For experts only}. @item make report.all Convenience target which aggregates all reports. @b{For experts only}. @end table @anchor{terminal.targets.general} @subsection General Modules General modules such as @samp{libhb}, @samp{test} and @samp{gtk} have the following scoped targets: @table @samp @item make @i{MODULE}.build Build @i{MODULE}. @item make @i{MODULE}.clean Clean build output for @i{MODULE}. @end table @anchor{terminal.targets.contrib} @subsection Contrib Modules Contrib modules such as @samp{bzip2}, @samp{ffmpeg}, @samp{fontconfig}, @samp{freetype}, @samp{fribidi}, @samp{lame}, @samp{libass}, @samp{libbluray}, @samp{libdvdnav}, @samp{libdvdread}, @samp{libiconv}, @samp{libogg}, @samp{libsamplerate}, @samp{libtheora}, @samp{libvorbis}, @samp{libxml2}, @samp{x264}, @samp{yasm} and @samp{zlib} have the following scoped targets: @table @samp @item make @i{MODULE}.fetch Download source tarball from the Internet and save to @file{TOP/downloads} directory. No check-summing is performed. @item make @i{MODULE}.extract Extract source tarball into @file{build} tree. @item make @i{MODULE}.patch Apply appropriate patches (if any) to module sources. @item make @i{MODULE}.configure Configure module sources. This usually invokes autotool configure. @item make @i{MODULE}.build Build module. This usually invokes autotool build. @item make @i{MODULE}.install Install module products such as headers and libraries into @file{build} tree. This usually invokes autotool install. @item make @i{MODULE}.uninstall Uninstall module products; generally the reverse of install. This usually invokes autotool uninstall. @item make @i{MODULE}.clean Clean module; generally the reverse of build. This usually invokes autotool clean. @item make @i{MODULE}.xclean Extra clean module; first invokes uninstall then recursively removes the module build directory. @end table @anchor{terminal.targets.contrib.touch} @subsection Contrib Touch and Untouch Also available are some very granular targets which help force builds from specific cycle points. The following targets are available to touch and untouch the respective module target; this will force the build system to treat the target as satisfied after a touch or unsatisfied after an untouch: @itemize @item make @i{MODULE}.extract.touch @item make @i{MODULE}.extract.untouch @item make @i{MODULE}.patch.touch @item make @i{MODULE}.patch.untouch @item make @i{MODULE}.configure.touch @item make @i{MODULE}.configure.untouch @item make @i{MODULE}.build.touch @item make @i{MODULE}.build.untouch @item make @i{MODULE}.install.touch @item make @i{MODULE}.install.untouch @end itemize @anchor{terminal.targets.contrib.aggregate} @subsection Contrib Aggregates For convenience, the following targets aggregate the all contrib modules' respective targets together: @itemize @item make contrib.fetch @item make contrib.extract @item make contrib.patch @item make contrib.configure @item make contrib.build @item make contrib.install @item make contrib.uninstall @item make contrib.clean @item make contrib.xclean @end itemize @c %**------------------------------------------------------------------------- @anchor{terminal.customizing} @section Customizing Make If the need arises to override settings in the build system (essentially gnu-make variables) the recommended method is to create optional include files which are automatically included if present and follow this naming convention; @b{Do not check these files into the repository}: @table @file @item _SRC_/custom.defs Custom makevar definitions @i{outside} @file{build}. Suitable for settings which apply across all builds for a particular checkout; or which survives manual removal of @file{build}. @item _SRC_/custom.rules Custom make rules @i{outside} @file{build}. Suitable for rules which apply across all builds for a particular checkout; or which survives manual removal of @file{build}. @item _BUILD_/GNUmakefile.custom.defs Custom makevar definitions specific to a @file{build} directory. @item _BUILD_/GNUmakefile.custom.rules Custom makevar rules specific to a @file{build} directory. @end table The purpose is to allow a place to store local build settings for testing, tweaking, and experimenting with build configuration without losing your settings if @command{configure} is invoked; ie: @command{configure} would overwrite @file{GNUmakefile} and any customizations contained therein would be lost. Here is a short example of what the contents of @file{_SRC_/custom.defs} might contain: @example ## bump to gcc-4.6 in current path GCC.gcc = /usr/bin/gcc-4.6 ## replace optimize for 'speed' with more aggressive settings GCC.args.O.speed = -O3 -fomit-frame-pointer -msse4.2 @end example See also @command{make report.help} which displays a set of reports used to dump makefile vars. HandBrake-0.10.2/doc/texi/building/chapter.via.xcode.texi0000664000175200017520000001365612271033462023565 0ustar handbrakehandbrake@anchor{xcode} @chapter Building via Xcode.app @c %**------------------------------------------------------------------------- @anchor{xcode.checkout} @section Checkout Sources @include building/method.checkout.texi @c %**------------------------------------------------------------------------- @anchor{xcode.build} @section Build Perform the following steps to build: @itemize @item Finder - navigate to @file{macosx/} in the @value{HB-name} source tree @item Finder - open @file{HandBrake.xcodeproj} @item Xcode workspace - select scheme @b{HandBrake [RELEASE]} @item Xcode menu - select Product -> Build @item Xcode workspace - Show the Log navigator @item Xcode workspace Log navigator - select top Build item @end itemize @c %**------------------------------------------------------------------------- @anchor{xcode.note.products} @section Note: Finding Built Products Under default Xcode.app options the products from a build are managed by the Xcode Organizer. Perform the following steps to open Finder at top of build tree and navigate to release products: @itemize @item Xcode menu - select Window -> Organizer @item Xcode organizer - select Projects tab @item Xcode organizer Projects - select @value{HB-name} item @item @value{HB-name} item - click Derived Data location arrow (immediately right of path) @item Finder - navigate to Build -> Products -> release @end itemize @quotation Note There is a bug with Xcode Organizer. The very first time an Xcode project is opened the Project view Derived Data is greyed-out. Workaround glitch by selecting any other tab and then reselecting Projects tab. @end quotation @c %**------------------------------------------------------------------------- @anchor{xcode.note.behaviors} @section Note: Workspace Log Behaviors The default Workspace behavior does not display latest Build log in the navigator and quickly becomes tedious. To automatically switch to Log navigator and show current log: @itemize @item Xcode menu - select Behaviors -> Edit Behaviors @item Xcode behaviors - select Build starts @item navigator - enable, select Show, select Log Navigator @item nagivate to - select current log @end itemize @quotation Note The Log navigator supports some possibly confusing options. It is recommended to only show results for the last build by selecting @b{Recent}. If @b{All} is selected then it will look as though Xcode is performing a build, but in reality it is bringing forward log output from prior builds and it becomes impossible to tell if any single log entry represents actual work performed or if it was brought forward from history. @end quotation @quotation Note When building external target, many 3rd-party contributed modules have warnings and errors which may safely be ignored and are ignored by the external build system. Ultimately, look to the workspace status indicator for @b{Build Succeeded}. @end quotation @c %**------------------------------------------------------------------------- @anchor{xcode.note.external} @section External Target The external target mechanism is used to launch a full terminal-based build from within Xcode. Unfortunately, we do not have hooks in place to offer finer-grained control over per-module make actions. However, you can still use @b{terminal} to accomplish those tasks after launching the build at least once or doing a clean from within Xcode. @b{Be careful to not issue terminal commands simultaneously with Xcode tasks.} Invoking a clean from Xcode always destroys the entire external build tree and subsequently configures it. Changing settings in Xcode such as selecting xcconfig files should always be followed by a clean. This allows the external build system configuration to accurately reflect Xcode project changes. The following are some examples of using @command{make} from the terminal to effect various components of the external build. But first, you must open a terminal at the top of the external build output tree. Here we navigate to external build configured for @b{release}: @itemize @item Xcode menu - select Window -> Organizer @item Xcode organizer - select Projects tab @item Xcode organizer Projects - select @value{HB-name} item @item @value{HB-name} item - click Derived Data location arrow (immediately right of path) @item Finder - navigate to Build -> Products -> release -> external @end itemize Example; external build failed but error is buried in a parallelized log; redo build sequentially: @example make xclean make BUILD.jobs=1 @end example Example; build external x264 module: @example make x264.clean make x264 @end example Example; extract, configure, build and install external x264 module: @example make x264.xclean make x264.install @end example Example; something in a big module is failing; redo build sequentially: @example make ffmpeg.clean make BUILD.jobs=1 ffmpeg @end example @c %**------------------------------------------------------------------------- @anchor{xcode.userdefined} @section User-Defined Settings The following user defined settings are visible in Xcode project and are used for the external build system. @table @samp @item EXTERNAL_BUILD Do not modify; used to specify the build (scratch) directory. @item EXTERNAL_DRIVER Do not modify; used for internal/external build coordination and must always be @samp{xcode}. @item EXTERNAL_JOBS Specifies the concurrency factor for the external build system when builds are launched from within Xcode. Modify for faster external builds if your system has the horsepower and resources. Specifying a value greater than the number of CPU cores (or virtual cores) in your system is unlikely to produce gains and will needlessly consume extra resources. A special string value of @b{auto} sets the factor to the number of active CPUs on the host system. @item EXTERNAL_SRC Do not modify; specifies the top-level source directory for @value{HB-name}, relative to Xcode project. @item EXTERNAL_XCCONFIG Do not modify; specifies which xcconfig file is active. Defined inside xcconfig file. @end table HandBrake-0.10.2/doc/texi/Building.mingw.texi0000664000175200017520000000327612416262351021337 0ustar handbrakehandbrake\input texinfo @c -*- Texinfo -*- @c %**start of header @setfilename Building.mingw.info @include base/article.texi @include building/command.texi @paragraphindent none @set BG-platform @value{OS-mingw} @c %**end of header @majorheading Guide to Building @value{HB-title} on @value{BG-platform} @contents @sp 1 @include building/chapter.introduction.texi @c %**------------------------------------------------------------------------- @anchor{prerequisites} @chapter Prerequisites The following are the recommended specifications for building on @value{OS-mingw}; but is not necessarily the only configuration that is possible: @itemize @bullet @item Intel 64-bit hardware @item @value{OS-mingw} on Fedora 20+, Ubuntu 14+ or Debian 7+ using gcc 4.7+ @item Official MinGW-w64 builds are recommended over those provided by distributions. @item yasm 1.3.x @end itemize @include building/prerequisites.bundled.texi @quotation Note You must set the --cross flag with configure to cross compile with windows. See the Example below. Note that the cross compiler name varies between distributions. The following is an example: @end quotation @example ../configure --cross=x86_64-w64-mingw32 @end example @include building/prerequisites.common.texi @c %**------------------------------------------------------------------------- @include building/chapter.quickstart.texi @anchor{overview} @chapter Overview @value{OS-mingw} builds are performed from a @b{terminal}. There is no support for building from any IDEs. @c %**------------------------------------------------------------------------- @include building/chapter.via.terminal.texi @include building/chapter.troubleshooting.texi @include building/appendix.repo.texi HandBrake-0.10.2/doc/texi/Building.osx.texi0000664000175200017520000000647312416262351021031 0ustar handbrakehandbrake\input texinfo @c -*- Texinfo -*- @c %**start of header @setfilename Building.osx.info @include base/article.texi @include building/command.texi @paragraphindent none @set BG-platform @value{OS-osx} @c %**end of header @majorheading Build Guide for @value{HB-name} @value{HB-version} on @value{BG-platform} @contents @sp 1 @include building/chapter.introduction.texi @c %**------------------------------------------------------------------------- @anchor{prerequisites} @chapter Prerequisites Building on @value{OS-osx} is well supported. It is the reference platform for @value{HB-name}. The following are the recommended specifications for this platform; but is not necessarily the only configuration that is possible: @itemize @bullet @item Mac Intel hardware @item @value{OS-osx} 10.9.4 or later @item Xcode 6.0.1 @item Xcode command-line tools (installed via Preferences > Downloads > Components) or download via Appla Developer website. @item XQuartz (http://xquartz.macosforge.org/landing/) @end itemize The following tools are compiled during the build process if necessary, but you can speed up your build by installing them: @itemize @bullet @item yasm 1.3.0 or later @item autoconf @item automake @item libtool @end itemize @include building/prerequisites.bundled.texi @include building/prerequisites.common.texi @c %**------------------------------------------------------------------------- @include building/chapter.quickstart.texi @c %**------------------------------------------------------------------------- @anchor{overview} @chapter Overview The two general methods to build on @value{OS-osx} are from @b{terminal} or @b{Xcode.app}. The preferred method for automated and repeatable builds is to use the terminal. Otherwise the choice is generally up to the individual. To be extra clear, building from the terminal by default actually invokes @command{xcodebuild} to build the very same targets contained in the Xcode project. Think of it as building with Xcode but without the GUI. @c %**------------------------------------------------------------------------- @include building/chapter.via.terminal.texi @c %**------------------------------------------------------------------------- @anchor{terminal.ub} @section Universal Binaries This section outlines convenience procedures for creating Universal Binaries for all the architectures. @quotation Note The dummy (container) build configuration uses @command{--disable-xcode}; but the nested architecture builds will all make full use of Xcode. @end quotation Create a dummy (container) build configuration and use it to launch a nested-build for each architecture: @example ./configure --disable-xcode cd build/ make ub.build make ub.combine @end example The list of architectures is hard coded to @value{HB-name}'s desired product and currently is composed of combining the binaries produced from two xcconfigs: osx106.i386 and osx106.x86_64. The following example shows how to specify a different list of xcconfigs: @example ./configure --disable-xcode cd build/ make UB.xcconfigs="osx107.i386 osx107.x86_64" ub.build make UB.xcconfigs="osx107.i386 osx107.x86_64" ub.combine @end example @c %**------------------------------------------------------------------------- @include building/chapter.via.xcode.texi @include building/chapter.troubleshooting.texi @include building/appendix.repo.texi HandBrake-0.10.2/doc/texi/base/0000775000175200017520000000000012535641641016476 5ustar handbrakehandbrakeHandBrake-0.10.2/doc/texi/base/glossary.texi0000664000175200017520000000030512374457507021241 0ustar handbrakehandbrake@set OS-cygwin Cygwin @set OS-freebsd FreeBSD @set OS-linux Linux @set OS-osx Mac OS X @set OS-solaris Solaris @set OS-unix unix @set OS-windows Windows @set OS-mingw MinGW-W64 HandBrake-0.10.2/doc/texi/base/article.texi0000664000175200017520000000013711152542670021011 0ustar handbrakehandbrake@ifhtml @exampleindent 0 @end ifhtml @include base/handbrake.texi @include base/glossary.texi HandBrake-0.10.2/doc/texi/base/handbrake.texi.m40000664000175200017520000000176412271033462021630 0ustar handbrakehandbrakechangequote(<<,>>)dnl include(<>)dnl dnl dnl dnl @set HB-title __HB_title @set HB-name __HB_name @set HB-name-lower __HB_name_lower @set HB-name-upper __HB_name_upper @set HB-acro-lower __HB_acro_lower @set HB-acro-upper __HB_acro_upper @set HB-url-website __HB_url_website @set HB-url-community __HB_url_community @set HB-url-irc __HB_url_irc @set HB-url-appcast __HB_url_appcast @set HB-version-major __HB_version_major @set HB-version-minor __HB_version_minor @set HB-version-point __HB_version_point @set HB-version __HB_version @set HB-version-hex __HB_version_hex @set HB-build __HB_build @set HB-repo-url __HB_repo_url @set HB-repo-root __HB_repo_root @set HB-repo-branch __HB_repo_branch @set HB-repo-uuid __HB_repo_uuid @set HB-repo-rev __HB_repo_rev @set HB-repo-date __HB_repo_date @set HB-repo-official __HB_repo_official @set HB-repo-type __HB_repo_type HandBrake-0.10.2/doc/texi/Building.linux.texi0000664000175200017520000001074212422537240021350 0ustar handbrakehandbrake\input texinfo @c -*- Texinfo -*- @c %**start of header @setfilename Building.linux.info @include base/article.texi @include building/command.texi @paragraphindent none @set BG-platform @value{OS-linux} @c %**end of header @majorheading Guide to Building @value{HB-title} on @value{BG-platform} @contents @sp 1 @include building/chapter.introduction.texi @c %**------------------------------------------------------------------------- @anchor{prerequisites} @chapter Prerequisites The following are the recommended specifications for building on @value{OS-linux}; but is not necessarily the only configuration that is possible: @itemize @bullet @item Intel 32-bit or 64-bit kernel @item Ubuntu 14.04, gcc 4.8, yasm 1.3.x @item Fedora 20, gcc 4.8, yasm 1.3.x @item gcc 4.7 or higher is reported to work @end itemize @include building/prerequisites.bundled.texi @include building/prerequisites.common.texi The @b{GTK UI} introduces some significant extra build requirements. GTK version 3.10+ is required to build the GTK UI. If you intend to disable building the GUI with @command{configure --disable-gtk} you will not need many of these packages installed: Ubuntu 14.04 packages: @itemize @bullet @item subversion (cli/gui) @item cmake (cli/gui) @item yasm (cli/gui) @item build-essential (cli/gui) @item autoconf (cli/gui) @item libtool (cli/gui) @item zlib1g-dev (cli/gui) @item libbz2-dev (cli/gui) @item libxml2-dev (cli/gui) @item libogg-dev (cli/gui) @item libtheora-dev (cli/gui) @item libvorbis-dev (cli/gui) @item libsamplerate-dev (cli/gui) @item libfribidi-dev (cli/gui) @item libfreetype6-dev (cli/gui) @item libfontconfig1-dev (cli/gui) @item libass-dev (cli/gui) @item libmp3lame-dev (cli/gui) @item libx264-dev (cli/gui) @item intltool (gui) @item libglib2.0-dev (gui) @item libdbus-glib-1-dev (gui) @item libgtk-3-dev (gui) @item libgudev-1.0-dev (gui) @item libwebkitgtk-3.0-dev (gui) @item libnotify-dev (gui) @item libgstreamer1.0-dev (gui) @item libgstreamer-plugins-base1.0-dev (gui) @item libappindicator-dev (gui) @end itemize To install these packages: @example sudo apt-get install subversion cmake yasm build-essential autoconf libtool \ zlib1g-dev libbz2-dev libogg-dev libtheora-dev libvorbis-dev \ libsamplerate-dev libxml2-dev libfribidi-dev libfreetype6-dev \ libfontconfig1-dev libass-dev libmp3lame-dev libx264-dev intltool \ libglib2.0-dev libdbus-glib-1-dev libgtk-3-dev libgudev-1.0-dev \ libwebkitgtk-3.0-dev libnotify-dev libgstreamer1.0-dev \ libgstreamer-plugins-base1.0-dev libappindicator-dev @end example Fedora 20 package groups: @itemize @bullet @item Development Tools @item Development Libraries @item X Software Development (gui) @item GNOME Software Development (gui) @end itemize To install these package groups: @example sudo yum groupinstall "Development Tools" "Development Libraries" \ "X Software Development" "GNOME Software Development" @end example Additional Fedora packages: @itemize @bullet @item patch (cli/gui) @item intltool (cli/gui) @item libtool (cli/gui) @item cmake (cli/gui) @item gcc-c++ (cli/gui) @item yasm (cli/gui) @item zlib-devel (cli/gui) @item bzip2-devel (cli/gui) @item libogg-devel (cli/gui) @item libtheora-devel (cli/gui) @item libvorbis-devel (cli/gui) @item libsamplerate-devel (cli/gui) @item libxml2-devel (cli/gui) @item fribidi-devel (cli/gui) @item freetype-devel (cli/gui) @item fontconfig-devel (cli/gui) @item libass-devel (cli/gui) @item lame-devel (cli/gui) @item x264-devel (cli/gui) @item dbus-glib-devel (gui) @item libgudev1-devel (gui) @item webkitgtk3-devel (gui) @item libnotify-devel (gui) @item gstreamer1-devel (gui) @item gstreamer1-plugins-base-devel (gui) @end itemize To install these packages: @example sudo yum install patch intltool libtool cmake gcc-c++ yasm zlib-devel \ bzip2-devel libogg-devel libtheora-devel libvorbis-devel libsamplerate-devel \ libxml2-devel fribidi-devel freetype-devel fontconfig-devel libass-devel \ lame-devel x264-devel dbus-glib-devel libgudev1-devel webkitgtk3-devel \ libnotify-devel gstreamer1-devel gstreamer1-plugins-base-devel @end example @c %**------------------------------------------------------------------------- @include building/chapter.quickstart.texi @anchor{overview} @chapter Overview @value{OS-linux} builds are performed from a @b{terminal}. There is no support for building from any IDEs. @c %**------------------------------------------------------------------------- @include building/chapter.via.terminal.texi @include building/chapter.troubleshooting.texi @include building/appendix.repo.texi HandBrake-0.10.2/scripts/0000775000175200017520000000000012535641637015542 5ustar handbrakehandbrakeHandBrake-0.10.2/scripts/tst.aspect0000775000175200017520000000245111046762612017553 0ustar handbrakehandbrake#!/bin/tcsh # # generate aspect ratio & cropping regression test data # from a set of HandBrake input files # # usage: tst.aspect [file ...] # # if no file names are supplied a default set of inputs is used (see the # variable 'inputs' below). Each file is encoded multiple times using # different options each time. The options to use are the elements of # the 'options' variable below. # # One line is printed for each HB run. It has the input dimensions, # output dimensions, crop, PAR, filename & options for the encode. # Since PAR is only output for anamorphic encodes, an omitted PAR # is indicated by "1:1" (to distinguish it from the explicit PAR "1/1"). set options=('-w 480' '-l 368' '-p' '-P') if ($#argv) then set inputs=($argv:q) else set inputs=(~/Movies/DVD/* ~/tst/*.{ts,mpg,mkv,avi,vob}) endif foreach i ($inputs:q) foreach o ($options:q) (sleep 5; echo q) | ./HandBrakeCLI -v -L -i "$i" -o /dev/null -f mp4 -e x264 $o |& \ awk -v fnm="$i" -v opts="$o" '/ storage dimensions: / { dimen = $5 "*" $7 " -> " $9 "*" $11 " " $13 }\ $3=="dimensions:" { dimen = $4 "*" $6 " -> " $8 "*" $10 " " $12 }\ / pixel aspect ratio: / { par=$6 "/" $8 }\ /encx264: opening libx264/ { if(! par) par="1:1";printf "%s %s %s %s\n", dimen, par, fnm, opts }' end end HandBrake-0.10.2/scripts/manicure.rb0000775000175200017520000015710712352646531017703 0ustar handbrakehandbrake#! /usr/bin/ruby # manincure.rb version 0.66 # This file is part of the HandBrake source code. # Homepage: . # It may be used under the terms of the GNU General Public License. # This script parses HandBrake's Mac presets into hashes, which can # be displayed in various formats for use by the CLI and its wrappers. # For handling command line arguments to the script require 'optparse' require 'ostruct' require 'rubygems' require 'plist' # CLI options: (code based on http://www.ruby-doc.org/stdlib/libdoc/optparse/rdoc/index.html ) def readOptions # --[no-]cli-raw, -r gives raw CLI for wiki # --cli-parse, -p gives CLI strings for wrappers # --api, -a gives preset code for test.c # --api-list, -A gives CLI strings for --preset-list display # --[no-]header, -h turns off banner display options = OpenStruct.new options.cliraw = false options.cliparse = false options.api = false options.apilist = false options.header = false opts = OptionParser.new do |opts| opts.banner = "Usage: manicure.rb [options]" opts.separator "" opts.separator "Options:" opts.on("-r", "--cli-raw", "Gives example strings for the HB wiki") do |raw| options.cliraw = raw option_set = true end opts.on("-p", "--cli-parse", "Gives presets as wrapper-parseable CLI", " option strings") do |par| options.cliparse = par end opts.on("-a", "--api", "Gives preset code for test.c") do |api| options.api = api end opts.on("-A", "--api-list", "Gives code for test.c's --preset-list", " options") do |alist| options.apilist = alist end opts.on("-H", "--Header", "Display a banner before each preset") do |head| options.header = head end opts.on_tail("-h", "--help", "Show this message") do puts opts exit end end.parse! return options end # These arrays contain all the other presets and hashes that are going to be used. # Yeah, they're global variables. In an object-oriented scripting language. # Real smooth, huh? # This class parses the user's presets .plist into an array of hashes class Presets attr_reader :hashMasterList # Running initialization runs everything. # Calling it will also call the parser # and display output. def initialize # Grab the user's home path homeLocation = `echo $HOME`.chomp # Use that to build a path to the presets .plist inputFile = homeLocation+'/Library/Application Support/HandBrake/UserPresets.plist' # Parse the presets into hashes @hashMasterList = Plist::parse_xml( inputFile ) end end # This class displays the presets to stdout in various formats. class Display def initialize(hashMasterList, options) @hashMasterList = hashMasterList @options = options # A width of 40 gives nice, compact output. @columnWidth=40 # Print to screen. displayCommandStrings end def displayCommandStrings # prints everything to screen # Iterate through the hashes. @hashMasterList.each do |hash| # Check to see whether we've got a preset or afolder if !hash["Folder"] # It's a top-level preset displayIndividualPreset(hash, 0) else # It's a folder, yay displayFolder( hash, 0 ) hash["ChildrenArray"].each do |subhash| # Drill down to see its contents if !subhash["Folder"] # It's a preset displayIndividualPreset(subhash, 1) else # It's a folder displayFolder( subhash, 1 ) subhash["ChildrenArray"].each do |subsubhash| # At this point we're far enough down we won't try to drill further if !subsubhash["Folder"] displayIndividualPreset(subsubhash, 2) end end displayFolderCloser( 1 ) end end displayFolderCloser( 0 ) end end end def displayIndividualPreset(hash, depth) if @options.header == true # First throw up a header to make each preset distinct displayHeader(hash) end if @options.cliraw == true # Show the preset's full CLI string equivalent generateCLIString(hash, depth) end if @options.cliparse == true generateCLIParse(hash, depth) end if @options.api == true # Show the preset as code for test/test.c, HandBrakeCLI generateAPIcalls(hash) end if @options.apilist == true # Show the preset as print statements, for CLI wrappers to parse. generateAPIList(hash, depth) end end def displayHeader(hash) # A distinct banner to separate each preset # Print a line of asterisks puts "*" * @columnWidth # Print the name, centered puts '* '+hash["PresetName"].to_s.center(@columnWidth-4)+' *' # Print a line of dashes puts '~' * @columnWidth # Print the description, centered and word-wrapped puts hash["PresetDescription"].to_s.center(@columnWidth).gsub(/\n/," ").scan(/\S.{0,#{@columnWidth-2}}\S(?=\s|$)|\S+/) # Print another line of dashes puts '~' * @columnWidth # Print the formats the preset uses puts "#{hash["FileCodecs"]}".center(@columnWidth) # Note if the preset isn't built-in if hash["Type"] == 1 puts "Custom Preset".center(@columnWidth) end # Note if the preset is marked as default. if hash["Default"] == 1 puts "This is your default preset.".center(@columnWidth) end # End with a line of tildes. puts "~" * @columnWidth end def displayFolder( hash, depth ) if @options.cliraw == true # Show the folder's full in a format that matches the CLI equivalents generateCLIFolderString(hash, depth) end if @options.cliparse == true # Show the folder in a format that matches the CLI wrapper equivalents generateCLIFolderParse(hash, depth) end if @options.apilist == true # Show the folder as print statements, for CLI wrappers to parse. generateAPIFolderList(hash, depth) end end def displayFolderCloser( depth ) if @options.cliraw == true # Show the folder's full in a format that matches the CLI equivalents generateCLIFolderCloserString( depth ) end if @options.cliparse == true # Show the folder in a format that matches the CLI wrapper equivalents generateCLIFolderCloserParse( depth ) end if @options.apilist == true # Show the folder as print statements, for CLI wrappers to parse. generateAPIFolderCloserList( depth ) end end def generateCLIFolderString( hash, depth ) # Shows the folder for the CLI equivalents commandString = "" depth.times do commandString << " " end (depth+1).times do commandString << "<" end commandString << " " << hash["PresetName"] << "\n" puts commandString end def generateCLIFolderCloserString( depth ) commandString = "" depth.times do commandString << " " end (depth+1).times do commandString << ">" end commandString << "\n" puts commandString end def generateCLIString(hash, depth) # Makes a full CLI equivalent of a preset commandString = "" depth.times do commandString << " " end commandString << './HandBrakeCLI -i DVD -o ~/Movies/movie.' #Filename suffix case hash["FileFormat"] when /MPEG-4/, /MP4/ commandString << "mp4 " when /Matroska/, /MKV/ commandString << "mkv " end #Video encoder commandString << " -e " case hash["VideoEncoder"] when /x264/ commandString << "x264" when /Theora/ commandString << "theora" when /MPEG-4/ commandString << "ffmpeg4" when /MPEG-2/ commandString << "ffmpeg2" end #VideoRateControl case hash["VideoQualityType"] when 0 commandString << " -S " << hash["VideoTargetSize"] when 1 commandString << " -b " << hash["VideoAvgBitrate"] when 2 commandString << " -q " << hash["VideoQualitySlider"].to_s end #FPS if hash["VideoFramerate"] != "Same as source" if hash["VideoFramerate"] == "23.976 (NTSC Film)" commandString << " -r " << "23.976" elsif hash["VideoFramerate"] == "29.97 (NTSC Video)" commandString << " -r " << "29.97" elsif hash["VideoFramerate"] == "25 (PAL Film/Video)" commandString << " -r " << "25" else commandString << " -r " << hash["VideoFramerate"] end # not same as source: pfr, else default (cfr) if hash["VideoFramerateMode"] == "pfr" commandString << " --pfr " end # same as source: cfr, else default (vfr) elsif hash["VideoFramerateMode"] == "cfr" commandString << " --cfr " end #Audio tracks audioBitrates = "" audioEncoders = "" audioMixdowns = "" audioSamplerates = "" audioTracks = "" audioTrackDRCs = "" audioCount = hash["AudioList"].size hash["AudioList"].each do |audioTrack| audioCount = audioCount - 1 #Bitrates audioBitrates << audioTrack["AudioBitrate"] #Encoders case audioTrack["AudioEncoder"] when /AC3 Pass/ audioEncoders << "copy:ac3" when /AC3/ audioEncoders << "ac3" when /DTS Pass/ audioEncoders << "copy:dts" when /DTS-HD Pass/ audioEncoders << "copy:dtshd" when /AAC Pass/ audioEncoders << "copy:aac" when "AAC (ffmpeg)", "AAC (avcodec)", "AAC (faac)" audioEncoders << "av_aac" when "AAC (FDK)" audioEncoders << "fdk_aac" when "HE-AAC (FDK)" audioEncoders << "fdk_haac" when "AAC (CoreAudio)" audioEncoders << "ca_aac" when "HE-AAC (CoreAudio)" audioEncoders << "ca_haac" when /Vorbis/ audioEncoders << "vorbis" when /MP3 Pass/ audioEncoders << "copy:mp3" when /MP3/ audioEncoders << "mp3" when "FLAC (ffmpeg)", "FLAC 16-bit" audioEncoders << "flac16" when "FLAC (24-bit)", "FLAC 24-bit" audioEncoders << "flac24" when /Auto Pass/ audioEncoders << "copy" end #Mixdowns case audioTrack["AudioMixdown"] when "Mono (Left Only)" audioMixdowns << "left_only" when "Mono (Right Only)" audioMixdowns << "right_only" when /Mono/ audioMixdowns << "mono" when /Stereo/ audioMixdowns << "stereo" when /Dolby Surround/ audioMixdowns << "dpl1" when /Dolby Pro Logic II/ audioMixdowns << "dpl2" when /5.1/, /discrete/ audioMixdowns << "5point1" when /6.1/ audioMixdowns << "6point1" when "7.1 (5F/2R/LFE)" audioMixdowns << "5_2_lfe" when /7.1/ audioMixdowns << "7point1" when /None/ audioMixdowns << "none" end #Samplerates audioSamplerates << audioTrack["AudioSamplerate"] #Tracks audioTracks << audioTrack["AudioTrack"].to_s #DRC audioTrackDRCs << audioTrack["AudioTrackDRCSlider"].to_s if audioCount > 0 audioBitrates << "," audioEncoders << "," audioMixdowns << "," audioSamplerates << "," audioTracks << "," audioTrackDRCs << "," end end commandString << " -a " << audioTracks commandString << " -E " << audioEncoders commandString << " -B " << audioBitrates commandString << " -6 " << audioMixdowns commandString << " -R " << audioSamplerates commandString << " -D " << audioTrackDRCs #Auto Passthru Mask audioCopyMask = "" if hash["AudioAllowAACPass"].to_i == 1 audioCopyMask << "aac" end if hash["AudioAllowAC3Pass"].to_i == 1 if audioCopyMask.size > 0 audioCopyMask << "," end audioCopyMask << "ac3" end if hash["AudioAllowDTSHDPass"].to_i == 1 if audioCopyMask.size > 0 audioCopyMask << "," end audioCopyMask << "dtshd" end if hash["AudioAllowDTSPass"].to_i == 1 if audioCopyMask.size > 0 audioCopyMask << "," end audioCopyMask << "dts" end if hash["AudioAllowMP3Pass"].to_i == 1 if audioCopyMask.size > 0 audioCopyMask << "," end audioCopyMask << "mp3" end if audioCopyMask.size > 0 commandString << " --audio-copy-mask " << audioCopyMask end #Auto Passthru Fallback audioEncoderFallback = "" case hash["AudioEncoderFallback"] when /AC3/ audioEncoderFallback << "ac3" when "AAC (ffmpeg)", "AAC (avcodec)", "AAC (faac)" audioEncoderFallback << "av_aac" when "AAC (FDK)" audioEncoderFallback << "fdk_aac" when "HE-AAC (FDK)" audioEncoderFallback << "fdk_haac" when "AAC (CoreAudio)" audioEncoderFallback << "ca_aac" when "HE-AAC (CoreAudio)" audioEncoderFallback << "ca_haac" when /Vorbis/ audioEncoderFallback << "vorbis" when /MP3/ audioEncoderFallback << "mp3" when "FLAC (ffmpeg)", "FLAC 16-bit" audioEncoderFallback << "flac16" when "FLAC (24-bit)", "FLAC 24-bit" audioEncoderFallback << "flac24" end if audioEncoderFallback.size > 0 commandString << " --audio-fallback " << audioEncoderFallback end #Container commandString << " -f " case hash["FileFormat"] when "MPEG-4 (mp4v2)" commandString << "mp4v2" when /MP4/ commandString << "mp4" when "Matroska (libmkv)" commandString << "libmkv" when /MKV/ commandString << "mkv" end #iPod MP4 atom if hash["Mp4iPodCompatible"].to_i == 1 commandString << " -I" end # 64-bit files if hash["Mp4LargeFile"] == 1 commandString << " -4" end #MP4 Optimize for HTTP Streaming if hash["Mp4HttpOptimize"].to_i == 1 commandString << " -O" end #Cropping if hash["PictureAutoCrop"] == 0 commandString << " --crop " commandString << hash["PictureTopCrop"].to_s commandString << ":" commandString << hash["PictureBottomCrop"].to_s commandString << ":" commandString << hash["PictureLeftCrop"].to_s commandString << ":" commandString << hash["PictureRightCrop"].to_s end #Dimensions if hash["PictureWidth"] != 0 commandString << " -X " commandString << hash["PictureWidth"].to_s end if hash["PictureHeight"] != 0 commandString << " -Y " commandString << hash["PictureHeight"].to_s end #Subtitles if hash["Subtitles"] && hash["Subtitles"] != "None" if hash["Subtitles"] == "Autoselect" commandString << " --subtitle-scan" else commandString << " -s " commandString << hash["Subtitles"] end end #Video Filters if hash["UsesPictureFilters"] == 1 case hash["PictureDeinterlace"] when 1 commandString << " --deinterlace=" << hash["PictureDeinterlaceCustom"].to_s when 2 commandString << " --deinterlace=fast" when 3 commandString << " --deinterlace=slow" when 4 commandString << " --deinterlace=slower" when 5 commandString << " --deinterlace=bob" end case hash["PictureDenoise"] when 1 commandString << " --denoise=" << hash["PictureDenoiseCustom"].to_s when 2 commandString << " --denoise=weak" when 3 commandString << " --denoise=medium" when 4 commandString << " --denoise=strong" end case hash["PictureDecomb"] when 1 commandString << " --decomb=" << hash["PictureDecombCustom"].to_s when 2 commandString << " --decomb" when 3 commandString << " --decomb=fast" when 4 commandString << " --decomb=bob" end case hash["PictureDetelecine"] when 1 commandString << " --detelecine=" << hash["PictureDetelecineCustom"].to_s when 2 commandString << " --detelecine" end if hash["PictureDeblock"] != 0 commandString << " --deblock=" << hash["PictureDeblock"].to_s end end #Anamorphic if hash["PicturePAR"] == 1 commandString << " --strict-anamorphic" elsif hash["PicturePAR"] == 2 commandString << " --loose-anamorphic" elsif hash["PicturePAR"] == 3 commandString << " --custom-anamorphic" end #Modulus if hash["PictureModulus"] commandString << " --modulus " << hash["PictureModulus"].to_s end #Booleans if hash["ChapterMarkers"] == 1 then commandString << " -m" end if hash["VideoGrayScale"] == 1 then commandString << " -g" end if hash["VideoTwoPass"] == 1 then commandString << " -2" end if hash["VideoTurboTwoPass"] == 1 then commandString << " -T" end #Advanced Options if hash["x264UseAdvancedOptions"] != 1 if hash["x264Preset"] != "" commandString << " --x264-preset " commandString << hash["x264Preset"] end if hash["x264Tune"] != "" && hash["x264Tune"] != "none" commandString << " --x264-tune " commandString << hash["x264Tune"] end if hash["h264Profile"] != "" && hash["h264Profile"] != "auto" commandString << " --h264-profile " commandString << hash["h264Profile"] end if hash["h264Level"] != "" && hash["h264Level"] != "auto" commandString << " --h264-level " commandString << hash["h264Level"] end if hash["x264OptionExtra"] != "" commandString << " -x " commandString << hash["x264OptionExtra"] end elsif hash["x264Option"] != "" commandString << " -x " commandString << hash["x264Option"] end # That's it, print to screen now puts commandString end def generateCLIFolderParse( hash, depth ) # Shows the folder for wrappers to parse commandString = "" depth.times do commandString << " " end (depth+1).times do commandString << "<" end commandString << " " << hash["PresetName"] << "\n" puts commandString end def generateCLIFolderCloserParse( depth ) commandString = "" depth.times do commandString << " " end (depth+1).times do commandString << ">" end commandString << "\n" puts commandString end def generateCLIParse(hash, depth) # Makes a CLI equivalent of all user presets, for wrappers to parse commandString = "" depth.times do commandString << " " end commandString << '+ ' << hash["PresetName"] << ":" #Video encoder commandString << " -e " case hash["VideoEncoder"] when /x264/ commandString << "x264" when /Theora/ commandString << "theora" when /MPEG-4/ commandString << "ffmpeg4" when /MPEG-2/ commandString << "ffmpeg2" end #VideoRateControl case hash["VideoQualityType"] when 0 commandString << " -S " << hash["VideoTargetSize"] when 1 commandString << " -b " << hash["VideoAvgBitrate"] when 2 commandString << " -q " << hash["VideoQualitySlider"].to_s end #FPS if hash["VideoFramerate"] != "Same as source" if hash["VideoFramerate"] == "23.976 (NTSC Film)" commandString << " -r " << "23.976" elsif hash["VideoFramerate"] == "29.97 (NTSC Video)" commandString << " -r " << "29.97" elsif hash["VideoFramerate"] == "25 (PAL Film/Video)" commandString << " -r " << "25" else commandString << " -r " << hash["VideoFramerate"] end # not same as source: pfr, else default (cfr) if hash["VideoFramerateMode"] == "pfr" commandString << " --pfr " end # same as source: cfr, else default (vfr) elsif hash["VideoFramerateMode"] == "cfr" commandString << " --cfr " end #Audio tracks audioBitrates = "" audioEncoders = "" audioMixdowns = "" audioSamplerates = "" audioTracks = "" audioTrackDRCs = "" audioCount = hash["AudioList"].size hash["AudioList"].each do |audioTrack| audioCount = audioCount - 1 #Bitrates audioBitrates << audioTrack["AudioBitrate"] #Encoders case audioTrack["AudioEncoder"] when /AC3 Pass/ audioEncoders << "copy:ac3" when /AC3/ audioEncoders << "ac3" when /DTS Pass/ audioEncoders << "copy:dts" when /DTS-HD Pass/ audioEncoders << "copy:dtshd" when /AAC Pass/ audioEncoders << "copy:aac" when "AAC (ffmpeg)", "AAC (avcodec)", "AAC (faac)" audioEncoders << "av_aac" when "AAC (FDK)" audioEncoders << "fdk_aac" when "HE-AAC (FDK)" audioEncoders << "fdk_haac" when "AAC (CoreAudio)" audioEncoders << "ca_aac" when "HE-AAC (CoreAudio)" audioEncoders << "ca_haac" when /Vorbis/ audioEncoders << "vorbis" when /MP3 Pass/ audioEncoders << "copy:mp3" when /MP3/ audioEncoders << "mp3" when "FLAC (ffmpeg)", "FLAC 16-bit" audioEncoders << "flac16" when "FLAC (24-bit)", "FLAC 24-bit" audioEncoders << "flac24" when /Auto Pass/ audioEncoders << "copy" end #Mixdowns case audioTrack["AudioMixdown"] when "Mono (Left Only)" audioMixdowns << "left_only" when "Mono (Right Only)" audioMixdowns << "right_only" when /Mono/ audioMixdowns << "mono" when /Stereo/ audioMixdowns << "stereo" when /Dolby Surround/ audioMixdowns << "dpl1" when /Dolby Pro Logic II/ audioMixdowns << "dpl2" when /5.1/, /discrete/ audioMixdowns << "5point1" when /6.1/ audioMixdowns << "6point1" when "7.1 (5F/2R/LFE)" audioMixdowns << "5_2_lfe" when /7.1/ audioMixdowns << "7point1" when /None/ audioMixdowns << "none" end #Samplerates audioSamplerates << audioTrack["AudioSamplerate"] #Tracks audioTracks << audioTrack["AudioTrack"].to_s #DRC audioTrackDRCs << audioTrack["AudioTrackDRCSlider"].to_s if audioCount > 0 audioBitrates << "," audioEncoders << "," audioMixdowns << "," audioSamplerates << "," audioTracks << "," audioTrackDRCs << "," end end commandString << " -a " << audioTracks commandString << " -E " << audioEncoders commandString << " -B " << audioBitrates commandString << " -6 " << audioMixdowns commandString << " -R " << audioSamplerates commandString << " -D " << audioTrackDRCs #Auto Passthru Mask audioCopyMask = "" if hash["AudioAllowAACPass"].to_i == 1 audioCopyMask << "aac" end if hash["AudioAllowAC3Pass"].to_i == 1 if audioCopyMask.size > 0 audioCopyMask << "," end audioCopyMask << "ac3" end if hash["AudioAllowDTSHDPass"].to_i == 1 if audioCopyMask.size > 0 audioCopyMask << "," end audioCopyMask << "dtshd" end if hash["AudioAllowDTSPass"].to_i == 1 if audioCopyMask.size > 0 audioCopyMask << "," end audioCopyMask << "dts" end if hash["AudioAllowMP3Pass"].to_i == 1 if audioCopyMask.size > 0 audioCopyMask << "," end audioCopyMask << "mp3" end if audioCopyMask.size > 0 commandString << " --audio-copy-mask " << audioCopyMask end #Auto Passthru Fallback audioEncoderFallback = "" case hash["AudioEncoderFallback"] when /AC3/ audioEncoderFallback << "ac3" when "AAC (ffmpeg)", "AAC (avcodec)", "AAC (faac)" audioEncoderFallback << "av_aac" when "AAC (FDK)" audioEncoderFallback << "fdk_aac" when "HE-AAC (FDK)" audioEncoderFallback << "fdk_haac" when "AAC (CoreAudio)" audioEncoderFallback << "ca_aac" when "HE-AAC (CoreAudio)" audioEncoderFallback << "ca_haac" when /Vorbis/ audioEncoderFallback << "vorbis" when /MP3/ audioEncoderFallback << "mp3" when "FLAC (ffmpeg)", "FLAC 16-bit" audioEncoderFallback << "flac16" when "FLAC (24-bit)", "FLAC 24-bit" audioEncoderFallback << "flac24" end if audioEncoderFallback.size > 0 commandString << " --audio-fallback " << audioEncoderFallback end #Container commandString << " -f " case hash["FileFormat"] when "MPEG-4 (mp4v2)" commandString << "mp4v2" when /MP4/ commandString << "mp4" when "Matroska (libmkv)" commandString << "libmkv" when /MKV/ commandString << "mkv" end #iPod MP4 atom if hash["Mp4iPodCompatible"].to_i == 1 commandString << " -I" end # 64-bit files if hash["Mp4LargeFile"] == 1 commandString << " -4" end #MP4 Optimize for HTTP Streaming if hash["Mp4HttpOptimize"].to_i == 1 commandString << " -O" end #Cropping if hash["PictureAutoCrop"] == 0 commandString << " --crop " commandString << hash["PictureTopCrop"].to_s commandString << ":" commandString << hash["PictureBottomCrop"].to_s commandString << ":" commandString << hash["PictureLeftCrop"].to_s commandString << ":" commandString << hash["PictureRightCrop"].to_s end #Dimensions if hash["PictureWidth"] != 0 commandString << " -X " commandString << hash["PictureWidth"].to_s end if hash["PictureHeight"] != 0 commandString << " -Y " commandString << hash["PictureHeight"].to_s end #Subtitles if hash["Subtitles"] && hash["Subtitles"] != "None" if hash["Subtitles"] == "Autoselect" commandString << " --subtitle-scan" else commandString << " -s " commandString << hash["Subtitles"] end end #Video Filters if hash["UsesPictureFilters"] == 1 case hash["PictureDeinterlace"] when 1 commandString << " --deinterlace=" << hash["PictureDeinterlaceCustom"].to_s when 2 commandString << " --deinterlace=fast" when 3 commandString << " --deinterlace=slow" when 4 commandString << " --deinterlace=slower" when 5 commandString << " --deinterlace=bob" end case hash["PictureDenoise"] when 1 commandString << " --denoise=" << hash["PictureDenoiseCustom"].to_s when 2 commandString << " --denoise=weak" when 3 commandString << " --denoise=medium" when 4 commandString << " --denoise=strong" end case hash["PictureDecomb"] when 1 commandString << " --decomb=" << hash["PictureDecombCustom"].to_s when 2 commandString << " --decomb" when 3 commandString << " --decomb=fast" when 4 commandString << " --decomb=bob" end case hash["PictureDetelecine"] when 1 commandString << " --detelecine=" << hash["PictureDetelecineCustom"].to_s when 2 commandString << " --detelecine" end if hash["PictureDeblock"] != 0 commandString << " --deblock=" << hash["PictureDeblock"].to_s end end #Anamorphic if hash["PicturePAR"] == 1 commandString << " --strict-anamorphic" elsif hash["PicturePAR"] == 2 commandString << " --loose-anamorphic" elsif hash["PicturePAR"] == 3 commandString << " --custom-anamorphic" end #Modulus if hash["PictureModulus"] commandString << " --modulus " << hash["PictureModulus"].to_s end #Booleans if hash["ChapterMarkers"] == 1 then commandString << " -m" end if hash["VideoGrayScale"] == 1 then commandString << " -g" end if hash["VideoTwoPass"] == 1 then commandString << " -2" end if hash["VideoTurboTwoPass"] == 1 then commandString << " -T" end #Advanced Options if hash["x264UseAdvancedOptions"] != 1 if hash["x264Preset"] != "" commandString << " --x264-preset " commandString << hash["x264Preset"] end if hash["x264Tune"] != "" && hash["x264Tune"] != "none" commandString << " --x264-tune " commandString << hash["x264Tune"] end if hash["h264Profile"] != "" && hash["h264Profile"] != "auto" commandString << " --h264-profile " commandString << hash["h264Profile"] end if hash["h264Level"] != "" && hash["h264Level"] != "auto" commandString << " --h264-level " commandString << hash["h264Level"] end if hash["x264OptionExtra"] != "" commandString << " -x " commandString << hash["x264OptionExtra"] end elsif hash["x264Option"] != "" commandString << " -x " commandString << hash["x264Option"] end # That's it, print to screen now puts commandString end def generateAPIcalls(hash) # Makes a C version of the preset ready for coding into the CLI commandString = "if (!strcasecmp(preset_name, \"" << hash["PresetName"] << "\"))\n{\n " #Container commandString << "if( !mux )\n " commandString << "{\n " case hash["FileFormat"] when "MPEG-4 (mp4v2)" commandString << " mux = " << "HB_MUX_MP4V2;\n " when /MP4/ commandString << " mux = " << "HB_MUX_MP4;\n " when "Matroska (libmkv)" commandString << " mux = " << "HB_MUX_LIBMKV;\n " when /MKV/ commandString << " mux = " << "HB_MUX_MKV;\n " end commandString << "}\n " #iPod MP4 atom if hash["Mp4iPodCompatible"].to_i == 1 commandString << "job->ipod_atom = 1;\n " end # 64-bit files if hash["Mp4LargeFile"] == 1 commandString << "job->largeFileSize = 1;\n " end #MP4 Optimize for HTTP Streaming if hash["Mp4HttpOptimize"].to_i == 1 commandString << "job->mp4_optimize = 1;\n " end #Video encoder commandString << "vcodec = " case hash["VideoEncoder"] when /x264/ commandString << "HB_VCODEC_X264;\n " when /Theora/ commandString << "HB_VCODEC_THEORA;\n " when /MPEG-4/ commandString << "HB_VCODEC_FFMPEG_MPEG4;\n " when /MPEG-2/ commandString << "HB_VCODEC_FFMPEG_MPEG2;\n " end #VideoRateControl case hash["VideoQualityType"] when 0 commandString << "size = " << hash["VideoTargetSize"] << ";\n " when 1 commandString << "job->vbitrate = " << hash["VideoAvgBitrate"] << ";\n " when 2 commandString << "job->vquality = " << hash["VideoQualitySlider"].to_s << ";\n " end #FPS if hash["VideoFramerate"] != "Same as source" if hash["VideoFramerate"] == "23.976 (NTSC Film)" commandString << "filter_vrate_base = " << "1126125;\n " elsif hash["VideoFramerate"] == "29.97 (NTSC Video)" commandString << "filter_vrate_base = " << "900900;\n " elsif hash["VideoFramerate"] == "25 (PAL Film/Video)" commandString << "filter_vrate_base = " << "1080000;\n " else commandString << "filter_vrate_base = " << (27000000 / hash["VideoFramerate"].to_i).to_s << ";\n " end # not same as source: pfr, else default (cfr) if hash["VideoFramerateMode"] == "pfr" commandString << "filter_cfr = 2;\n " else commandString << "filter_cfr = 1;\n " end # same as source: cfr, else default (vfr) elsif hash["VideoFramerateMode"] == "cfr" commandString << "filter_cfr = 1;\n " end #Audio tracks audioBitrates = "" audioEncoders = "" audioMixdowns = "" audioSamplerates = "" audioTracks = "" audioTrackDRCs = "" audioCount = hash["AudioList"].size hash["AudioList"].each do |audioTrack| audioCount = audioCount - 1 #Bitrates audioBitrates << audioTrack["AudioBitrate"] #Encoders case audioTrack["AudioEncoder"] when /AC3 Pass/ audioEncoders << "copy:ac3" when /AC3/ audioEncoders << "ac3" when /DTS Pass/ audioEncoders << "copy:dts" when /DTS-HD Pass/ audioEncoders << "copy:dtshd" when /AAC Pass/ audioEncoders << "copy:aac" when "AAC (ffmpeg)", "AAC (avcodec)", "AAC (faac)" audioEncoders << "av_aac" when "AAC (FDK)" audioEncoders << "fdk_aac" when "HE-AAC (FDK)" audioEncoders << "fdk_haac" when "AAC (CoreAudio)" audioEncoders << "ca_aac" when "HE-AAC (CoreAudio)" audioEncoders << "ca_haac" when /Vorbis/ audioEncoders << "vorbis" when /MP3 Pass/ audioEncoders << "copy:mp3" when /MP3/ audioEncoders << "mp3" when "FLAC (ffmpeg)", "FLAC 16-bit" audioEncoders << "flac16" when "FLAC (24-bit)", "FLAC 24-bit" audioEncoders << "flac24" when /Auto Pass/ audioEncoders << "copy" end #Mixdowns case audioTrack["AudioMixdown"] when "Mono (Left Only)" audioMixdowns << "left_only" when "Mono (Right Only)" audioMixdowns << "right_only" when /Mono/ audioMixdowns << "mono" when /Stereo/ audioMixdowns << "stereo" when /Dolby Surround/ audioMixdowns << "dpl1" when /Dolby Pro Logic II/ audioMixdowns << "dpl2" when /5.1/, /discrete/ audioMixdowns << "5point1" when /6.1/ audioMixdowns << "6point1" when "7.1 (5F/2R/LFE)" audioMixdowns << "5_2_lfe" when /7.1/ audioMixdowns << "7point1" when /None/ audioMixdowns << "none" end #Samplerates audioSamplerates << audioTrack["AudioSamplerate"] #Tracks audioTracks << audioTrack["AudioTrack"].to_s #DRC audioTrackDRCs << audioTrack["AudioTrackDRCSlider"].to_s if audioCount > 0 audioBitrates << "," audioEncoders << "," audioMixdowns << "," audioSamplerates << "," audioTracks << "," audioTrackDRCs << "," end end commandString << "if( !atracks )\n " commandString << "{\n " commandString << " atracks = strdup(\"" << audioTracks commandString << "\");\n " commandString << "}\n " commandString << "if( !acodecs )\n " commandString << "{\n " commandString << " acodecs = strdup(\"" << audioEncoders commandString << "\");\n " commandString << "}\n " commandString << "if( !abitrates )\n " commandString << "{\n " commandString << " abitrates = str_split(\"" << audioBitrates commandString << "\", ',');\n " commandString << "}\n " commandString << "if( !mixdowns )\n " commandString << "{\n " commandString << " mixdowns = strdup(\"" << audioMixdowns commandString << "\");\n " commandString << "}\n " commandString << "if( !arates )\n " commandString << "{\n " commandString << " arates = strdup(\"" << audioSamplerates commandString << "\");\n " commandString << "}\n " commandString << "if( !dynamic_range_compression )\n " commandString << "{\n " commandString << " dynamic_range_compression = strdup(\"" << audioTrackDRCs commandString << "\");\n " commandString << "}\n " #Auto Passthru Mask if hash["AudioAllowAACPass"] commandString << "if( allowed_audio_copy == -1 )\n " commandString << "{\n " commandString << " allowed_audio_copy = 0;\n " if hash["AudioAllowAACPass"].to_i == 1 commandString << " allowed_audio_copy |= HB_ACODEC_AAC_PASS;\n " end if hash["AudioAllowAC3Pass"].to_i == 1 commandString << " allowed_audio_copy |= HB_ACODEC_AC3_PASS;\n " end if hash["AudioAllowDTSHDPass"].to_i == 1 commandString << " allowed_audio_copy |= HB_ACODEC_DCA_HD_PASS;\n " end if hash["AudioAllowDTSPass"].to_i == 1 commandString << " allowed_audio_copy |= HB_ACODEC_DCA_PASS;\n " end if hash["AudioAllowMP3Pass"].to_i == 1 commandString << " allowed_audio_copy |= HB_ACODEC_MP3_PASS;\n " end commandString << " allowed_audio_copy &= HB_ACODEC_PASS_MASK;\n " commandString << "}\n " end #Auto Passthru Fallback audioEncoderFallback = "" case hash["AudioEncoderFallback"] when /AC3/ audioEncoderFallback << "ac3" when "AAC (ffmpeg)", "AAC (avcodec)", "AAC (faac)" audioEncoderFallback << "av_aac" when "AAC (FDK)" audioEncoderFallback << "fdk_aac" when "HE-AAC (FDK)" audioEncoderFallback << "fdk_haac" when "AAC (CoreAudio)" audioEncoderFallback << "ca_aac" when "HE-AAC (CoreAudio)" audioEncoderFallback << "ca_haac" when /Vorbis/ audioEncoderFallback << "vorbis" when /MP3/ audioEncoderFallback << "mp3" when "FLAC (ffmpeg)", "FLAC 16-bit" audioEncoderFallback << "flac16" when "FLAC (24-bit)", "FLAC 24-bit" audioEncoderFallback << "flac24" end if audioEncoderFallback.size > 0 commandString << "if( acodec_fallback == NULL )\n " commandString << "{\n " commandString << " acodec_fallback = \"" << audioEncoderFallback << "\";\n " commandString << "}\n " end #Cropping if hash["PictureAutoCrop"] == 0 commandString << "job->crop[0] = " << hash["PictureTopCrop"].to_s << ";\n " commandString << "job->crop[1] = " << hash["PictureBottomCrop"].to_s << ";\n " commandString << "job->crop[2] = " << hash["PictureLeftCrop"].to_s << ";\n " commandString << "job->crop[4] = " << hash["PictureRightCrop"].to_s << ";\n " end #Dimensions if hash["PictureWidth"] != 0 commandString << "maxWidth = " commandString << hash["PictureWidth"].to_s << ";\n " end if hash["PictureHeight"] != 0 commandString << "maxHeight = " commandString << hash["PictureHeight"].to_s << ";\n " end #Subtitles if hash["Subtitles"] != "None" if hash["Subtitles"] == "Autoselect" commandString << "subtitle_scan = 1;\n " else commandString << "job->subtitle = " commandString << ( hash["Subtitles"].to_i - 1).to_s << ";\n " end end #Advanced Options if hash["x264UseAdvancedOptions"] != 1 if hash["x264Preset"] != "" commandString << "if (x264_preset == NULL)\n " commandString << "{\n " commandString << " x264_preset = strdup(\"" commandString << hash["x264Preset"] << "\");\n " commandString << "}\n " end if hash["x264Tune"] != "" && hash["x264Tune"] != "none" commandString << "if (x264_tune == NULL)\n " commandString << "{\n " commandString << " x264_tune = strdup(\"" commandString << hash["x264Tune"] commandString << "\");\n " commandString << "}\n " end if hash["h264Profile"] != "" && hash["h264Profile"] != "auto" commandString << "if (h264_profile == NULL)\n " commandString << "{\n " commandString << " h264_profile = strdup(\"" commandString << hash["h264Profile"] << "\");\n " commandString << "}\n " end if hash["h264Level"] != "" && hash["h264Level"] != "auto" commandString << "if (h264_level == NULL)\n " commandString << "{\n " commandString << " h264_level = strdup(\"" commandString << hash["h264Level"] << "\");\n " commandString << "}\n " end if hash["x264OptionExtra"] != "" commandString << "if (advanced_opts == NULL)\n " commandString << "{\n " commandString << " advanced_opts = strdup(\"" commandString << hash["x264OptionExtra"] << "\");\n " commandString << "}\n " end elsif hash["x264Option"] != "" commandString << "if (advanced_opts == NULL)\n " commandString << "{\n " commandString << " advanced_opts = strdup(\"" commandString << hash["x264Option"] << "\");\n " commandString << "}\n " end #Video Filters if hash["UsesPictureFilters"] == 1 if hash["PictureDeinterlace"].to_i != 0 commandString << "deinterlace = 1;\n " end case hash["PictureDeinterlace"] when 1 commandString << "deinterlace_opt = \"" << hash["PictureDeinterlaceCustom"].to_s << "\";\n " when 2 commandString << "deinterlace_opt = \"0\";\n " when 3 commandString << "deinterlace_opt = \"1\";\n " when 4 commandString << "deinterlace_opt = \"3\";\n " when 5 commandString << "deinterlace_opt = \"15\";\n " end if hash["PictureDenoise"].to_i != 0 commandString << "denoise = 1;\n " end case hash["PictureDenoise"] when 1 commandString << "denoise_opt = \"" << hash["PictureDenoiseCustom"].to_s << "\";\n " when 2 commandString << "denoise_opt = \"2:1:1:2:3:3\";\n " when 3 commandString << "denoise_opt = \"3:2:2:2:3:3\";\n " when 4 commandString << "denoise_opt = \"7:7:7:5:5:5\";\n " end if hash["PictureDecomb"].to_i != 0 commandString << "decomb = 1;\n " end case hash["PictureDecomb"] when 1 commandString << "decomb_opt = \"" << hash["PictureDecombCustom"].to_s << "\";\n " when 3 commandString << "decomb_opt = \"7:2:6:9:1:80\";\n " when 4 commandString << "decomb_opt = \"455\";\n " end if hash["PictureDetelecine"].to_i != 0 commandString << "detelecine = 1;\n " end case hash["PictureDetelecine"] when 1 commandString << "detelecine_opt = \"" << hash["PictureDetelecineCustom"].to_s << "\";\n " end if hash["PictureDeblock"] != 0 commandString << "deblock = 1;\n " commandString << "deblock_opt = \"" << hash["PictureDeblock"].to_s << "\";\n " end end #Anamorphic if hash["PicturePAR"] != 0 commandString << "if( !anamorphic_mode )\n " commandString << "{\n " if hash["PicturePAR"] == 1 commandString << " anamorphic_mode = 1;\n " elsif hash["PicturePAR"] == 2 commandString << " anamorphic_mode = 2;\n " elsif hash["PicturePAR"] == 3 commandString << " anamorphic_mode = 3;\n " end commandString << "}\n " end #Modulus if hash["PictureModulus"] commandString << "modulus = " << hash["PictureModulus"].to_s << ";\n " end #Booleans if hash["ChapterMarkers"] == 1 commandString << "job->chapter_markers = 1;\n " end if hash["VideoGrayScale"] == 1 commandString << "job->grayscale = 1;\n " end if hash["VideoTwoPass"] == 1 commandString << "twoPass = 1;\n " end if hash["VideoTurboTwoPass"] == 1 commandString << "turbo_opts_enabled = 1;\n " end #Finish commandString = commandString.rstrip commandString << "\n}" # That's it, print to screen now puts commandString end def generateAPIFolderList( hash, depth ) commandString = "" commandString << " printf(\"\\n" depth.times do commandString << " " end (depth+1).times do commandString << "<" end commandString << " " << hash["PresetName"] commandString << "\\n\");\n" puts commandString end def generateAPIFolderCloserList( depth ) commandString = "" commandString << " printf(\"\\n" depth.times do commandString << " " end (depth+1).times do commandString << ">" end commandString << "\\n\");\n" puts commandString end def generateAPIList(hash, depth) # Makes a list of the CLI options a built-in CLI preset uses, for wrappers to parse commandString = "" commandString << " printf(\"\\n" depth.times do commandString << " " end commandString << "+ " << hash["PresetName"] << ": " #Video encoder commandString << " -e " case hash["VideoEncoder"] when /x264/ commandString << "x264 " when /Theora/ commandString << "theora " when /MPEG-4/ commandString << "ffmpeg4 " when /MPEG-2/ commandString << "ffmpeg2 " end #VideoRateControl case hash["VideoQualityType"] when 0 commandString << " -S " << hash["VideoTargetSize"] when 1 commandString << " -b " << hash["VideoAvgBitrate"] when 2 commandString << " -q " << hash["VideoQualitySlider"].to_s end #FPS if hash["VideoFramerate"] != "Same as source" if hash["VideoFramerate"] == "23.976 (NTSC Film)" commandString << " -r " << "23.976" elsif hash["VideoFramerate"] == "29.97 (NTSC Video)" commandString << " -r " << "29.97" elsif hash["VideoFramerate"] == "25 (PAL Film/Video)" commandString << " -r " << "25" else commandString << " -r " << hash["VideoFramerate"] end # not same as source: pfr, else default (cfr) if hash["VideoFramerateMode"] == "pfr" commandString << " --pfr " end # same as source: cfr, else default (vfr) elsif hash["VideoFramerateMode"] == "cfr" commandString << " --cfr " end #Audio tracks audioBitrates = "" audioEncoders = "" audioMixdowns = "" audioSamplerates = "" audioTracks = "" audioTrackDRCs = "" audioCount = hash["AudioList"].size hash["AudioList"].each do |audioTrack| audioCount = audioCount - 1 #Bitrates audioBitrates << audioTrack["AudioBitrate"] #Encoders case audioTrack["AudioEncoder"] when /AC3 Pass/ audioEncoders << "copy:ac3" when /AC3/ audioEncoders << "ac3" when /DTS Pass/ audioEncoders << "copy:dts" when /DTS-HD Pass/ audioEncoders << "copy:dtshd" when /AAC Pass/ audioEncoders << "copy:aac" when "AAC (ffmpeg)", "AAC (avcodec)", "AAC (faac)" audioEncoders << "av_aac" when "AAC (FDK)" audioEncoders << "fdk_aac" when "HE-AAC (FDK)" audioEncoders << "fdk_haac" when "AAC (CoreAudio)" audioEncoders << "ca_aac" when "HE-AAC (CoreAudio)" audioEncoders << "ca_haac" when /Vorbis/ audioEncoders << "vorbis" when /MP3 Pass/ audioEncoders << "copy:mp3" when /MP3/ audioEncoders << "mp3" when "FLAC (ffmpeg)", "FLAC 16-bit" audioEncoders << "flac16" when "FLAC (24-bit)", "FLAC 24-bit" audioEncoders << "flac24" when /Auto Pass/ audioEncoders << "copy" end #Mixdowns case audioTrack["AudioMixdown"] when "Mono (Left Only)" audioMixdowns << "left_only" when "Mono (Right Only)" audioMixdowns << "right_only" when /Mono/ audioMixdowns << "mono" when /Stereo/ audioMixdowns << "stereo" when /Dolby Surround/ audioMixdowns << "dpl1" when /Dolby Pro Logic II/ audioMixdowns << "dpl2" when /5.1/, /discrete/ audioMixdowns << "5point1" when /6.1/ audioMixdowns << "6point1" when "7.1 (5F/2R/LFE)" audioMixdowns << "5_2_lfe" when /7.1/ audioMixdowns << "7point1" when /None/ audioMixdowns << "none" end #Samplerates audioSamplerates << audioTrack["AudioSamplerate"] #Tracks audioTracks << audioTrack["AudioTrack"].to_s #DRC audioTrackDRCs << audioTrack["AudioTrackDRCSlider"].to_s if audioCount > 0 audioBitrates << "," audioEncoders << "," audioMixdowns << "," audioSamplerates << "," audioTracks << "," audioTrackDRCs << "," end end commandString << " -a " << audioTracks commandString << " -E " << audioEncoders commandString << " -B " << audioBitrates commandString << " -6 " << audioMixdowns commandString << " -R " << audioSamplerates commandString << " -D " << audioTrackDRCs #Auto Passthru Mask audioCopyMask = "" if hash["AudioAllowAACPass"].to_i == 1 audioCopyMask << "aac" end if hash["AudioAllowAC3Pass"].to_i == 1 if audioCopyMask.size > 0 audioCopyMask << "," end audioCopyMask << "ac3" end if hash["AudioAllowDTSHDPass"].to_i == 1 if audioCopyMask.size > 0 audioCopyMask << "," end audioCopyMask << "dtshd" end if hash["AudioAllowDTSPass"].to_i == 1 if audioCopyMask.size > 0 audioCopyMask << "," end audioCopyMask << "dts" end if hash["AudioAllowMP3Pass"].to_i == 1 if audioCopyMask.size > 0 audioCopyMask << "," end audioCopyMask << "mp3" end if audioCopyMask.size > 0 commandString << " --audio-copy-mask " << audioCopyMask end #Auto Passthru Fallback audioEncoderFallback = "" case hash["AudioEncoderFallback"] when /AC3/ audioEncoderFallback << "ac3" when "AAC (ffmpeg)", "AAC (avcodec)", "AAC (faac)" audioEncoderFallback << "av_aac" when "AAC (FDK)" audioEncoderFallback << "fdk_aac" when "HE-AAC (FDK)" audioEncoderFallback << "fdk_haac" when "AAC (CoreAudio)" audioEncoderFallback << "ca_aac" when "HE-AAC (CoreAudio)" audioEncoderFallback << "ca_haac" when /Vorbis/ audioEncoderFallback << "vorbis" when /MP3/ audioEncoderFallback << "mp3" when "FLAC (ffmpeg)", "FLAC 16-bit" audioEncoderFallback << "flac16" when "FLAC (24-bit)", "FLAC 24-bit" audioEncoderFallback << "flac24" end if audioEncoderFallback.size > 0 commandString << " --audio-fallback " << audioEncoderFallback end #Container commandString << " -f " case hash["FileFormat"] when "MPEG-4 (mp4v2)" commandString << "mp4v2" when /MP4/ commandString << "mp4" when "Matroska (libmkv)" commandString << "libmkv" when /MKV/ commandString << "mkv" end #iPod MP4 atom if hash["Mp4iPodCompatible"].to_i == 1 commandString << " -I" end # 64-bit files if hash["Mp4LargeFile"] == 1 commandString << " -4" end #MP4 Optimize for HTTP Streaming if hash["Mp4HttpOptimize"].to_i == 1 commandString << " -O" end #Cropping if hash["PictureAutoCrop"] == 0 commandString << " --crop " commandString << hash["PictureTopCrop"].to_s commandString << ":" commandString << hash["PictureBottomCrop"].to_s commandString << ":" commandString << hash["PictureLeftCrop"].to_s commandString << ":" commandString << hash["PictureRightCrop"].to_s end #Dimensions if hash["PictureWidth"] != 0 commandString << " -X " commandString << hash["PictureWidth"].to_s end if hash["PictureHeight"] != 0 commandString << " -Y " commandString << hash["PictureHeight"].to_s end #Subtitles if hash["Subtitles"] && hash["Subtitles"] != "None" if hash["Subtitles"] == "Autoselect" commandString << " --subtitle-scan" else commandString << " -s " commandString << hash["Subtitles"] end end #Video Filters if hash["UsesPictureFilters"] == 1 case hash["PictureDeinterlace"] when 1 commandString << " --deinterlace=" << hash["PictureDeinterlaceCustom"].to_s when 2 commandString << " --deinterlace=fast" when 3 commandString << " --deinterlace=slow" when 4 commandString << " --deinterlace=slower" when 5 commandString << " --deinterlace=bob" end case hash["PictureDenoise"] when 1 commandString << " --denoise=" << hash["PictureDenoiseCustom"].to_s when 2 commandString << " --denoise=weak" when 3 commandString << " --denoise=medium" when 4 commandString << " --denoise=strong" end case hash["PictureDecomb"] when 1 commandString << " --decomb=" << hash["PictureDecombCustom"].to_s when 2 commandString << " --decomb" when 3 commandString << " --decomb=fast" when 4 commandString << " --decomb=bob" end case hash["PictureDetelecine"] when 1 commandString << " --detelecine=" << hash["PictureDetelecineCustom"].to_s when 2 commandString << " --detelecine" end if hash["PictureDeblock"] != 0 commandString << " --deblock=" << hash["PictureDeblock"].to_s end end #Anamorphic if hash["PicturePAR"] == 1 commandString << " --strict-anamorphic" elsif hash["PicturePAR"] == 2 commandString << " --loose-anamorphic" elsif hash["PicturePAR"] == 3 commandString << " --custom-anamorphic" end #Modulus if hash["PictureModulus"] commandString << " --modulus " << hash["PictureModulus"].to_s end #Booleans if hash["ChapterMarkers"] == 1 then commandString << " -m" end if hash["VideoGrayScale"] == 1 then commandString << " -g" end if hash["VideoTwoPass"] == 1 then commandString << " -2" end if hash["VideoTurboTwoPass"] == 1 then commandString << " -T" end #Advanced Options if hash["x264UseAdvancedOptions"] != 1 if hash["x264Preset"] != "" commandString << " --x264-preset " commandString << hash["x264Preset"] end if hash["x264Tune"] != "" && hash["x264Tune"] != "none" commandString << " --x264-tune " commandString << hash["x264Tune"] end if hash["h264Profile"] != "" && hash["h264Profile"] != "auto" commandString << " --h264-profile " commandString << hash["h264Profile"] end if hash["h264Level"] != "" && hash["h264Level"] != "auto" commandString << " --h264-level " commandString << hash["h264Level"] end if hash["x264OptionExtra"] != "" commandString << " -x " commandString << hash["x264OptionExtra"] end elsif hash["x264Option"] != "" commandString << " -x " commandString << hash["x264Option"] end commandString << "\\n\");" # That's it, print to screen now puts commandString end end # CLI invocation only if __FILE__ == $0 # First grab the specified CLI options options = readOptions # Only run if one of the useful CLI flags have been passed if options.cliraw == true || options.cliparse == true || options.api == true || options.apilist == true # This line is the ignition -- generates hashes of # presets and then displays them to the screen # with the options the user selects on the CLI. Display.new( Presets.new.hashMasterList, options ) else # Direct the user to the help puts "\n\tUsage: manicure.rb [options]" puts "\tSee help with -h or --help" end end HandBrake-0.10.2/gtk/0000775000175200017520000000000012535641637014640 5ustar handbrakehandbrakeHandBrake-0.10.2/gtk/po/0000775000175200017520000000000012535641637015256 5ustar handbrakehandbrakeHandBrake-0.10.2/gtk/po/ro_RO.po0000664000175200017520000021236312466165306016642 0ustar handbrakehandbrake# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # titus , 2014 msgid "" msgstr "" "Project-Id-Version: HandBrake-0.10\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-03 13:01+0300\n" "PO-Revision-Date: 2014-12-08 21:43+0000\n" "Last-Translator: titus \n" "Language-Team: Romanian (Romania) (http://www.transifex.com/projects/p/handbrake/language/ro_RO/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ro_RO\n" "Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n" msgid "Quality: " msgstr "Calitate:" #, c-format msgid "Bitrate: %dkbps" msgstr "Rată biți: %dkbps" #, c-format msgid "Bitrate: %.4gkbps" msgstr "Rată biți: %.4gkbps" msgid "Passthrough" msgstr "Trecere directă" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s\n" "Track Name: %s" msgstr "%s\nCâștig: %s\nDRC: %s\nNume pistă: %s" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s" msgstr "%s\nCâștig: %s\nDRC: %s" msgid "Add" msgstr "Adaugă" msgid "" "Add an audio encoder.\n" "Each selected source track will be encoded with all selected encoders." msgstr "Adăugați un codor audio.\nFiecare sursă pistă selectată va fi codată cu toate codoarele selectate." msgid "Set the audio codec to encode this track with." msgstr "Configurați codecul audio cu care veți coda această pistă." msgid "Set the bitrate to encode this track with." msgstr "Configurați rata de biți cu care veți coda această pistă." msgid "" "Audio Quality:\n" "For encoders that support it, adjust the quality of the output." msgstr "Calitate audio:\nPentru codoarele care suportă aceasta, ajustați calitatea ieșirii." msgid "Set the mixdown of the output audio track." msgstr "Configurați mixarea pistei de ieșire audio." msgid "Set the sample rate of the output audio track." msgstr "Configurare rată de eșanționare a pistei de ieșire audio." msgid "" "Audio Gain:\n" "Adjust the amplification or attenuation of the output audio track." msgstr "Câștig audio:\nAjustați amplificarea sau atenuarea pistei audio de ieșire." msgid "0dB" msgstr "0dB" msgid "%ddB" msgstr "%ddB" msgid "%.4gkHz" msgstr "%.4gkHz" msgid "%d - %s (%.4gkHz)" msgstr "%d - %s (%.4gkHz)" msgid "" "Dynamic Range Compression:\n" "Adjust the dynamic range of the output audio track.\n" "For source audio that has a wide dynamic range,\n" "very loud and very soft sequences, DRC allows you\n" "to 'compress' the range by making loud sounds\n" "softer and soft sounds louder.\n" msgstr "" msgid "Remove this audio encoder" msgstr "Elimină acest codor audio" msgid "Closing HandBrake will terminate encoding.\n" msgstr "Închizând HandBrake se va termina codificarea.\n" msgid "No Title Found" msgstr "Nici un titlu nu este găsit" msgid "none" msgstr "nimic" msgid "Not Selected" msgstr "Nu este selectat" msgid "zerolatency x264 tune selected, forcing constant framerate" msgstr "" msgid "Scanning ..." msgstr "Se scanează ..." msgid "Stop Scan" msgstr "Oprește scanarea" msgid "On" msgstr "Pornit" msgid "Strict" msgstr "Strict" msgid "Loose" msgstr "Slab" msgid "Custom" msgstr "Personalizat" msgid "Unknown" msgstr "Necunoscut" msgid "auto" msgstr "automat" #, c-format msgid "" "%s\n" "\n" "%s in %d seconds ..." msgstr "%s\n\n%s în %d secunde ..." #, c-format msgid "%sYour movie will be lost if you don't continue encoding." msgstr "%sFilmul va fi pierdut dacă nu continuați codarea." msgid "Cancel Current and Stop" msgstr "Anulează curent și oprește" msgid "Cancel Current, Start Next" msgstr "Anulează curent, pornește următoarea" msgid "Finish Current, then Stop" msgstr "Termină curent, apoi oprește" msgid "Continue Encoding" msgstr "Continuă codarea" msgid "Custom " msgstr "Personalizat" msgid "Modified " msgstr "Modificat" #, c-format msgid "Handbrake Version: %s (%d)\n" msgstr "Versiune Handbrake: %s (%d)\n" #, c-format msgid "%d encode(s) pending" msgstr "%d codare(i) în așteptare" #, c-format msgid "job %d of %d, " msgstr "sarcina %d din %d," #, c-format msgid "pass %d (subtitle scan) of %d, " msgstr "trecut %d (scanare subtitrare) din %d," #, c-format msgid "pass %d of %d, " msgstr "trecut %d din %d" #, c-format msgid "Encoding: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" msgstr "Se codează: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %% (ETA %02dh%02dm%02ds)" msgstr "Se codează: %s%s%.2f %% (ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %%" msgstr "Se codează: %s%s%.2f %%" msgid "Searching for start time, " msgstr "Se caută timpul de pornire," msgid "Scanning..." msgstr "Se scanează..." #, c-format msgid "Scanning title %d of %d..." msgstr "Se scanează titlul %d din %d..." #, c-format msgid "Scanning title %d of %d preview %d..." msgstr "Se scanează titlul %d din %d previzualizarea %d..." msgid "Source" msgstr "Sursă" msgid "Choose Video Source" msgstr "Alegeți sursa video" msgid "Paused" msgstr "Pauzat" msgid "Encode Done!" msgstr "Codare terminată!" msgid "Encode Canceled." msgstr "Codare anulată." msgid "Encode Failed." msgstr "Codare eșuată." msgid "Muxing: This may take a while..." msgstr "Se multiplexează: Aceasta poate dura un timp..." msgid "Scan this DVD source" msgstr "Scanează această sursă DVD" msgid "AutoScan" msgstr "Scanează automat" msgid "Suspend" msgstr "Suspendă" #, c-format msgid "Suspend failed: %s" msgstr "Suspendare eșuată: %s" msgid "Suspend failed" msgstr "Suspendare eșuată" msgid "Shutdown" msgstr "Închide" #, c-format msgid "Shutdown failed: %s" msgstr "Închidere eșuată: %s" msgid "Shutdown failed" msgstr "Închidere eșuată" msgid "Encoding" msgstr "Codare" #, c-format msgid "press %d %d" msgstr "apăsați %d %d" #, c-format msgid "" "Invalid Settings:\n" "%s" msgstr "Configurări nevalide:\n%s" msgid "Cancel" msgstr "Anulare" #, c-format msgid "%s: %.4g (Warning: lossless)" msgstr "%s: %.4g (Atenție: fără pierderi)" #, c-format msgid "HandBrake %s/%s is now available (you have %s/%d)." msgstr "HandBrake %s/%s este acum disponibil (aveți %s/%d)." msgid "Encode Complete" msgstr "Codare terminată" msgid "Put down that cocktail, Your HandBrake queue is done!" msgstr "Lăsați jos acest cocktail, coada HandBrake este gata!" msgid "Your encode is complete." msgstr "Codarea dumneavoastră este terminată." msgid "Shutting down the computer" msgstr "Închide computerul" msgid "Putting computer to sleep" msgstr "Pune computerul în adormire" msgid "Quiting Handbrake" msgstr "Termină Handbrake" msgid "Bottom" msgstr "Dedesubt" #, c-format msgid "open failed: %s\n" msgstr "deschidere eșuată: %s\n" msgid "No key for dictionary item" msgstr "" msgid "Invalid container type. This shouldn't happen" msgstr "Tip container nevalid. Aceasta nu ar trebui să se întâmple" #, c-format msgid "%s:missing a requried attribute" msgstr "%s:lipsește un atribut necesar" #, c-format msgid "" "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" msgstr "" msgid "language" msgstr "limbă" msgid "Language" msgstr "Limbă" msgid "" "The language this text is in, as an ISO code. Pango can use this as a hint " "when rendering the text. If you don't understand this parameter, you " "probably don't need it" msgstr "Limba în care este textul acesta, ca un cod ISO. Pango poate utiliza asta ca un sfat când se randează textul. Dacă nu înțelegeți acest parametru, probabil nu aveți nevoie de el" msgid "" "The preferred place to ellipsize the string, if the cell renderer does not " "have enough room to display the entire string" msgstr "" msgid "" "How to break the string into multiple lines, if the cell renderer does not " "have enough room to display the entire string" msgstr "Cum să rupeți șirul în mai multe linii, dacă randarea celulei nu are suficient spațiu pentru afișat șirul întreg" msgid "" "Render the subtitle over the video.\n" "\n" "The subtitle will be part of the video and can not be disabled." msgstr "Randează subtitrarea peste video.\n\nSubtitrarea va fi parte din video și nu poate fi dezactivată." msgid "Burned In" msgstr "Ars în" msgid "" "Set the default output subtitle track.\n" "\n" "Most players will automatically display this\n" "subtitle track whenever the video is played.\n" "\n" "This is useful for creating a \"forced\" track\n" "in your output." msgstr "Configurează pista subtitrare implicită de ieșire \n\nMulte player-re va afișa automat\naceastă pistă subtitrare oricând video este redat.\n\nAceasta este util pentru crearea unei piste \"forțată\" \nîn ieșire." msgid "Default" msgstr "Implicit" msgid "" "Use only subtitles that have been flagged\n" "as forced in the source subtitle track\n" "\n" "\"Forced\" subtitles are usually used to show\n" "subtitles during scenes where someone is speaking\n" "a foreign language." msgstr "Utilizează numai subtitrările care au fost marcate\nca forțat în pista sursă subtitrare\n\nSubtitrările \"Forțate\" sunt utilizate de obicei să arate\nsubtitrări în timpul scenelor unde cineva vorbește\no limbă străină." msgid "Forced Only" msgstr "Numai forțat" msgid "" "Add (or subtract) an offset (in milliseconds)\n" "to the start of the SRT subtitle track.\n" "\n" "Often, the start of an external SRT file\n" "does not coincide with the start of the video.\n" "This setting allows you to synchronize the files." msgstr "" msgid "SRT Offset" msgstr "Compensare SRT" msgid "Off" msgstr "Închis" msgid "" "The source subtitle track\n" "\n" "You can choose any of the subtitles\n" "recognized in your source file.\n" "\n" "In addition, there is a special track option\n" "\"Foreign Audio Search\". This option will add\n" "an extra pass to the encode that searches for\n" "subtitles that may correspond to a foreign\n" "language scene. This option is best used in\n" "conjunction with the \"Forced\" option." msgstr "" msgid "Track" msgstr "Pistă" msgid "About HandBrake" msgstr "Despre Handbrake" msgid "" "Copyright © 2008 - 2013 John Stebbins\n" "Copyright © 2004 - 2013, HandBrake Devs" msgstr "Copyright © 2008 - 2013 John Stebbins\nCopyright © 2004 - 2013, HandBrake Devs" msgid "" "HandBrake is a GPL-licensed, multiplatform, multithreaded video transcoder." msgstr "HandBrake este licențiat GPL, multiplatformă, transcoder video multiplu." msgid "http://handbrake.fr" msgstr "http://handbrake.fr" msgid "" "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n" "\n" "HandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgstr "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n\nHandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgid "_Minimize/Maximize" msgstr "_Minimizează/Maximizează" msgid "_Pause Queue" msgstr "_Pauzare coadă" msgid "_Quit" msgstr "_Termină" msgid "_About" msgstr "_Despre" msgid "HandBrake" msgstr "HandBrake" msgid "_File" msgstr "_Fișier" msgid "_Source" msgstr "_Sursă" msgid "Single _Title" msgstr "Un singur _titlu" msgid "_Destination" msgstr "_Destinație" msgid "_Preferences" msgstr "_Preferințe" msgid "_Queue" msgstr "_Coadă" msgid "_Add" msgstr "_Adaugă" msgid "Add _Multiple" msgstr "Adaugă _Multiple" msgid "_Start" msgstr "_Pornire" msgid "_Pause" msgstr "_Pauză" msgid "_View" msgstr "_Vizualizare" msgid "HandBrake For _Dumbies" msgstr "HandBrake pentru _novici" msgid "_Show Presets" msgstr "_Arată preconfigurări" msgid "_Preview" msgstr "_Previzualizare" msgid "_Activity Window" msgstr "_Fereastră activitate" msgid "Show _Queue" msgstr "Arată _coada" msgid "_Presets" msgstr "_Preconfigurări" msgid "_Save" msgstr "_Salvare" msgid "_Delete" msgstr "_Șterge" msgid "_Make Default" msgstr "_Fă implicit" msgid "_New Folder" msgstr "_Dosar nou" msgid "_Export" msgstr "_Export" msgid "_Import" msgstr "_Import" msgid "_Update Built-in Presets" msgstr "_Actualizare preconfigurări incluse" msgid "_Help" msgstr "_Ajutor" msgid "_Guide" msgstr "_Ghid" msgid "Start Encoding" msgstr "Pornire codare" msgid "Start" msgstr "Pornire" msgid "Pause Encoding" msgstr "Pauzare codare" msgid "Pause" msgstr "Pauză" msgid "Add to Queue" msgstr "Adaugă la coadă" msgid "Enqueue" msgstr "Adaugă la coadă" msgid "Show Queue" msgstr "Arată coada" msgid "Queue" msgstr "Coadă" msgid "" "Open Picture Settings and Preview window.\n" "Here you can adjust cropping, resolution, aspect ratio, and filters." msgstr "Deschide fereastra configurări poză și previzualizare\nAici puteți ajusta decuparea, rezoluția, proporția și filtrele." msgid "Preview" msgstr "Previzualizare" msgid "Show Activity Window" msgstr "Arată fereastra activitate" msgid "Activity" msgstr "Activitate" msgid "Source:" msgstr "Sursă:" msgid "None" msgstr "Nimic" msgid "Title:" msgstr "Titlu:" msgid "" "Set the title to encode.\n" "By default the longest title is chosen.\n" "This is often the feature title of a DVD." msgstr "" msgid "Angle:" msgstr "Unghi:" msgid "For multi-angle DVD's, select the desired angle to encode." msgstr "Pentru DVD-uri cu unghiuri multiple, selectați unghiul dorit de codificat." msgid "Reset All Titles" msgstr "Resetare toate titlurile" msgid "Apply current settings to all titles" msgstr "Aplică tuturor titlurilor configurările curente" msgid "Range of title to encode. Can be chapters, seconds, or frames." msgstr "Domeniu de titlu pentru codificare. Poate fi capitole, secunde, sau cadre." msgid "Set the first chapter to encode." msgstr "Configurați primul capitol pentru codificat." msgid "Set the last chapter to encode." msgstr "Configurați ultimul capitol pentru codificat." msgid "Duration:" msgstr "Durată:" msgid "Destination" msgstr "Destinație" msgid "File:" msgstr "Fișier:" msgid "Destination filename for your encode." msgstr "Nume fișier destinație pentru rezultatul codificării." msgid "Destination directory for your encode." msgstr "Director destinație pentru rezultatul codificării." msgid "Destination Directory" msgstr "Director destinație" msgid "Format:" msgstr "Format:" msgid "Format to mux encoded tracks to." msgstr "Format pentru mixat pistele codificate." msgid "iPod 5G Support" msgstr "Suport iPod 5G" msgid "Add iPod Atom needed by some older iPods." msgstr "Adaugă iPod Atom necesare unor iPod-uri vechi." msgid "Web optimized" msgstr "Optimizat Web" msgid "" "Optimize the layout of the MP4 file for progressive download.\n" "This allows a player to initiate playback before downloading the entire file." msgstr "Optimizează aspectul unui fișier MP4 pentru descărcare progresivă.\nAceasta permite playerelor să inițieze redarea înaintea descărcării întregului fișier." msgid "Large file (>4GB)" msgstr "Fișier mare (>4GB)" msgid "" "Allow 64 bit MP4 file which can be over 4GB.\n" "\n" "Caution: This option may break device compatibility." msgstr "Permite fișiere MP4 de 64 biți care pot fi mai mari de 4GB.\n\nAtenție: Această opțiune poate deteriora compatibilitatea cu dispozitivul." msgid "Presets List" msgstr "Listă preconfigurări" msgid "Source Codec:" msgstr "Codec sursă:" msgid "Dimensions:" msgstr "Dimensiuni:" msgid "Aspect: " msgstr "Aspect:" msgid "Frame Rate:" msgstr "Rată cadru:" msgid "Source Picture Parameters" msgstr "Parametri sursă poză" msgid "Autocrop:" msgstr "Decupare automată:" msgid "Crop:" msgstr "Decupare:" msgid "Crop Dimensions:" msgstr "Dimensiuni decupare:" msgid "Cropping" msgstr "Decupare" msgid "Scale Dimensions:" msgstr "Dimensiuni scalare:" msgid "Optimal for Source:" msgstr "Optim pentru sursă:" msgid "Anamorphic:" msgstr "Anamorfic:" msgid "Scaling" msgstr "Scalare" msgid "Presentation Dimensions:" msgstr "Dimensiuni prezentare:" msgid "Summary" msgstr "Sumar" msgid "Left Crop" msgstr "Decupează stânga" msgid "Top Crop" msgstr "Decupează sus" msgid "Bottom Crop" msgstr "Decupează dedesubt" msgid "Right Crop" msgstr "Decupează dreapta" msgid "Auto Crop" msgstr "Decupează automat" msgid "Automatically crop black borders around edges of the video." msgstr "Decupează automat benzile negre în jurul marginilor video." msgid "Loose Crop" msgstr "Decupare liberă" msgid "" "When picture settings require that the image\n" "dimensions be rounded to some multiple number\n" "of pixels, this setting will crop a few extra pixels\n" "instead of doing exact cropping and then scaling to\n" "the required multiple." msgstr "" msgid "width:" msgstr "lățime:" msgid "" "This is the width that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "Aceasta este lățimea la care va fi păstrat video.\nDimensiunile afișajului actual va diferi dacă proporția pixelului nu este 1:1." msgid "height:" msgstr "înălțime:" msgid "" "This is the height that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "" msgid "Optimal for source" msgstr "Optim pentru sursă" msgid "" "If enabled, select the 'optimal' storage resolution.\n" "This will be the resolution that most closely matches the source resolution after cropping." msgstr "" msgid "" "Anamorphic Modes:\n" "\n" "None - Force pixel aspect ratio to 1:1.\n" "Loose - Align dimensions to chosen 'Alignment' value\n" " and pick pixel aspect ratio that preserves the\n" " original display aspect ratio\n" "Strict - Keep original source dimensions and pixel\n" " aspect ratio" msgstr "" msgid "Alignment:" msgstr "Aliniament:" msgid "" "Align storage dimensions to multiples of this value.\n" "\n" "This setting is only necessary for compatibility with some devices.\n" "You should use 2 unless you experience compatibility issues." msgstr "" msgid "Storage Geometry" msgstr "Geometrie depozit" msgid "" "This is the display width. It is the result of scaling the storage " "dimensions by the pixel aspect." msgstr "Aceasta este lățimea afișajului. Este rezultatul scalării dimensiunilor păstrate cu aspectul pixelului." msgid "Pixel Aspect:" msgstr "Aspect pixel:" msgid "" "Pixel aspect defines the shape of the pixels.\n" "\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "" msgid ":" msgstr ":" msgid "" "Pixel aspect defines the shape of the pixels.\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "" msgid "Keep Aspect" msgstr "Păstrează aspectul" msgid "" "If enabled, the original display aspect of the source will be maintained." msgstr "Dacă se activează, aspectul afișajului original al sursei va fi menținut." msgid "Display Aspect:" msgstr "Aspect afișaj:" msgid "Display Geometry" msgstr "Geometrie afișaj" msgid "Grayscale" msgstr "Scală de gri" msgid "If enabled, filter colour components out of video." msgstr "Dacă este activat, se filtrează componentele de culoare din videoclip." msgid "Deblock:" msgstr "Deblocare:" msgid "" "The deblocking filter removes a common type of compression artifact.\n" "If your source exhibits 'blockiness', this filter may help clean it up." msgstr "" msgid "Denoise Filter:" msgstr "Filtru reducere zgomot:" msgid "" "Denoise filtering reduces or removes the appearance of noise and grain.\n" "Film grain and other types of high frequency noise are difficult to compress.\n" "Using this filter on such sources can result in smaller file sizes." msgstr "Filtrarea zgomotului reduce sau elimină apariția zgomotului și granulației.\nGranulația filmului și alte tipuri de zgomote de înaltă frecvență sunt dificil de comprimat.\nUtilizând acest filtru pe astfel de surse pot rezulta fișiere de dimensiuni mai mici." msgid "Denoise Preset:" msgstr "Preconfigurare reducere zgomot:" msgid "Denoise Tune:" msgstr "" msgid "" "Custom denoise filter string format\n" "\n" "SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgstr "" msgid "Detelecine:" msgstr "Detelecine:" msgid "" "This filter removes 'combing' artifacts that are the result of telecining.\n" "\n" "Telecining is a process that adjusts film framerates that are 24fps to NTSC video frame rates which are 30fps." msgstr "" msgid "" "Custom detelecine filter string format\n" "\n" "JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgstr "" msgid "Decomb" msgstr "Decomb" msgid "" "Choose decomb or deinterlace filter options.\n" "\n" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced.\n" "\n" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "" msgid "Deinterlace" msgstr "Deântrețesere" msgid "Decomb:" msgstr "Decomb:" msgid "" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced." msgstr "" msgid "" "Custom decomb filter string format\n" "\n" "Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\n" "BlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\n" "ErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgstr "" msgid "Deinterlace:" msgstr "Deântrețesere:" msgid "" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "Filtrul deântrețesere clasic este aplicat tuturor cadrelor.\nCadrele care nu sunt întrețesute vor suferi unele degradări de calitate." msgid "" "Custom deinterlace filter string format\n" "\n" "YadifMode:YadifParity:McdintMode:McdeintQp" msgstr "" msgid "Filters" msgstr "Filtre" msgid "Picture" msgstr "Poză" msgid "Video Encoder:" msgstr "Codor video:" msgid "Available video encoders." msgstr "Codoare video disponibile." msgid "Framerate:" msgstr "Rată de cadre:" msgid "" "Output framerate.\n" "\n" "'Same as source' is recommended. If your source video has\n" "a variable framerate, 'Same as source' will preserve it." msgstr "" msgid "Constant Framerate" msgstr "Rată de cadre constantă" msgid "Same as source" msgstr "La fel ca sursa" msgid "kbps" msgstr "kbps" msgid "(variable)" msgstr "(variabil)" msgid "(constant)" msgstr "(constant)" msgid "Enables constant framerate output." msgstr "Activează rata de cadre constantă la ieșire." msgid "Peak Framerate (VFR)" msgstr "Limitare rată de cadre (VFR)" msgid "" "Enables variable framerate output with a peak\n" "rate determined by the framerate setting.\n" "\n" "VFR is not compatible with some players." msgstr "" msgid "Variable Framerate" msgstr "Rată de cadre variabilă" msgid "" "Enables variable framerate output.\n" "\n" "VFR is not compatible with some players." msgstr "Activează rata de cadre variabilă la ieșire.\n\nVFR nu este compatibil cu unele playere." msgid "" "Set the desired quality factor.\n" "The encoder targets a certain quality.\n" "The scale used by each video encoder is different.\n" "\n" "x264's scale is logarithmic and lower values correspond to higher quality.\n" "So small decreases in value will result in progressively larger increases\n" "in the resulting file size. A value of 0 means lossless and will result\n" "in a file size that is larger than the original source, unless the source\n" "was also lossless.\n" "\n" "FFMpeg's and Theora's scale is more linear.\n" "These encoders do not have a lossless mode." msgstr "" msgid "Constant Quality:" msgstr "Calitate constantă:" msgid "Bitrate (kbps): " msgstr "Rată de biți (kbps):" msgid "" "Set the average bitrate.\n" "\n" "The instantaneous bitrate can be much higher or lower at any point in time.\n" "But the average over a long duration will be the value set here. If you need\n" "to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings." msgstr "" msgid "2-Pass Encoding" msgstr "Codare 2-pași" msgid "" "Perform 2 Pass Encoding.\n" "\n" "The 'Bitrate' option is prerequisite. During the 1st pass, statistics about\n" "the video are collected. Then in the second pass, those statistics are used\n" "to make bitrate allocation decisions." msgstr "" msgid "Turbo First Pass" msgstr "Primul pas turbo" msgid "" "During the 1st pass of a 2 pass encode, use settings that speed things " "along." msgstr "" msgid "Use Advanced Options" msgstr "Utilizează opțiuni avansate" msgid "" "Use advanced options Tab for x264 settings.\n" "\n" "Use at your own risk!" msgstr "Utilizați fila opțiuni avansate pentru configurări x264.\n\nUtilizați pe propriul risc!" msgid "Preset:" msgstr "Preconfigurare:" msgid "" "Adjusts encoder settings to trade off compression efficiency against encoding speed.\n" "\n" "This establishes your default encoder settings.\n" "Tunes, profiles, levels and advanced option string will be applied to this.\n" "You should generally set this option to the slowest you can bear since slower\n" "settings will result in better quality or smaller files." msgstr "" msgid "Tune:" msgstr "Îmbunătățire:" msgid "" "Tune settings to optimize for common scenarios.\n" "\n" "This can improve effeciency for particular source characteristics or set\n" "characteristics of the output file. Changes will be applied after the\n" "preset but before all other parameters." msgstr "" msgid "Fast Decode" msgstr "Decodare rapidă" msgid "" "Reduce decoder CPU usage.\n" "\n" "Set this if your device is struggling to play the output (dropped frames)." msgstr "Reduce utilizarea CPU de către decodor.\n\nConfigurați aceasta dacă dispozitivul se chinuie să redea ieșirea (cadre aruncate)." msgid "Zero Latency" msgstr "Latență zero" msgid "" "Minimize latency between input to encoder and output of decoder.\n" "\n" "This is useful for broadcast of live streams.\n" "\n" "Since HandBrake is not suitable for live stream broadcast purposes,\n" "this setting is of little value here." msgstr "" msgid "Profile:" msgstr "Profil:" msgid "" "Sets and ensures compliance with the specified profile.\n" "\n" "Overrides all other settings." msgstr "Configurează și asigură conformitatea cu profilul selectat.\n\nSuprascrie toate celelalte configurări." msgid "Level:" msgstr "Nivel:" msgid "" "Sets and ensures compliance with the specified level.\n" "\n" "Overrides all other settings." msgstr "Configurează și asigură conformitatea cu nivelul specificat.\n\nSuprascrie toate celelalte configurări." msgid "More Settings:" msgstr "Mai multe configurări:" msgid "" "Additional encoder settings.\n" "\n" "Colon separated list of encoder options." msgstr "Configurări suplimentare codor.\n\nListă separată de două puncte a opțiunilor codorului." msgid "Video" msgstr "Video" msgid "Selection Behavior:" msgstr "Selectare comportament:" msgid "Remove" msgstr "Eliminare" msgid "Available Languages" msgstr "Limbi disponibile" msgid "Selected Languages" msgstr "Limbi selectate" msgid "Use only first encoder for secondary audio" msgstr "Utilizează numai primul codor pentru audio secundar" msgid "" "Only the primary audio track will be encoded with the full encoder list.\n" "All other secondary audio output tracks will be encoded with first encoder only." msgstr "Numai pista audio primară va fi codificată cu întreaga listă de codoare.\nToate celelalte piste audio secundare de ieșire vor fi codificate numai cu primul codor." msgid "Auto Passthru:" msgstr "Trecere directă automat:" msgid "MP3" msgstr "MP3" msgid "" "Enable this if your playback device supports MP3.\n" "This permits MP3 passthru to be selected when automatic passthru selection is enabled." msgstr "Activați aceasta dacă dispozitivul de redare suportă MP3.\nAceasta permite să fie selectată trecerea directă MP3 când este selectată trecerea directă automat." msgid "" "Enable this if your playback device supports AAC.\n" "This permits AAC passthru to be selected when automatic passthru selection is enabled." msgstr "Activați aceasta dacă dispozitivul de redare suportă AAC.\nAceasta permite să fie selectată trecerea directă AAC când este selectată trecerea directă automat." msgid "" "Enable this if your playback device supports AC-3.\n" "This permits AC-3 passthru to be selected when automatic passthru selection is enabled." msgstr "Activați aceasta dacă dispozitivul de redare suportă AC-3.\nAceasta permite să fie selectată trecerea directă AC-3 când este selectată trecerea directă automat." msgid "" "Enable this if your playback device supports DTS.\n" "This permits DTS passthru to be selected when automatic passthru selection is enabled." msgstr "Activați aceasta dacă dispozitivul de redare suportă DTS.\nAceasta permite să fie selectată trecerea directă DTS când este selectată trecerea directă automat." msgid "" "Enable this if your playback device supports DTS-HD.\n" "This permits DTS-HD passthru to be selected when automatic passthru selection is enabled." msgstr "Activați aceasta dacă dispozitivul de redare suportă DTS-HD.\nAceasta permite să fie selectată trecerea directă DTS-HD când este selectată trecerea directă automat." msgid "Passthru Fallback:" msgstr "Trecere directă de rezervă:" msgid "" "Set the audio codec to encode with when a suitable track can not be found " "for audio passthru." msgstr "Configurează codorul audio pentru codificare când o pistă potrivită nu poate fi găsită pentru trecerea directă audio." msgid "Audio Encoder Settings:" msgstr "Configurări codor audio:" msgid "Each selected source track will be encoded with all selected encoders" msgstr "Fiecare pistă sursă selectată va fi codificată cu toate codoarele selectate" msgid "Encoder" msgstr "Codor" msgid "Bitrate/Quality" msgstr "Rată de biți/Calitate" msgid "Mixdown" msgstr "Mixarea" msgid "Samplerate" msgstr "Rată eșanționare" msgid "Gain" msgstr "Câștig" msgid "Audio Defaults" msgstr "Implicite audio" msgid "Add new audio settings to the list" msgstr "Adaugă noi configurări audio la listă" msgid "Add All" msgstr "Adaugă tot" msgid "Add all audio tracks to the list" msgstr "Adaugă toate pistele audio la listă" msgid "Reload Defaults" msgstr "Reâncarcă implicite" msgid "Reload all audio settings from defaults" msgstr "Reâncarcă toate configurările audio din implicite" msgid "Audio List" msgstr "Listă audio" msgid "Preferred Language: None" msgstr "Limbă preferată: Nici una" msgid "Add Foreign Audio Search Pass" msgstr "Adaugă pasul de căutare audio străin" msgid "" "Add \"Foreign Audio Search\" when the default audio track is your preferred language.\n" "This search pass finds short sequences of foreign audio and provides subtitles for them." msgstr "" msgid "Add subtitle track if default audio is foreign" msgstr "Adaugă pistă subtitrare dacă audio implicit este străin" msgid "" "When the default audio track is not your preferred language, add a subtitle " "track." msgstr "Când pista audio implicită nu este în limba preferată, adaugă o pistă subtitrare." msgid "Add Closed Captions when available" msgstr "Adaugă subtitrări în chenar când sunt disponibile" msgid "" "Closed captions are text subtitles that can be added to any container as a " "soft subtitle track (not burned)" msgstr "Subtitrările în chenar sunt subtitrări text care pot fi adăugate la orice container ca o aplicație pistă subtitrare (nu este arsă)" msgid "Subtitle Defaults" msgstr "Subtitrări implicite" msgid "Add new subtitle settings to the list" msgstr "Adaugă noi configurări subtitrare la listă" msgid "Add all subtitle tracks to the list" msgstr "Adaugă toate pistele subtitrare la listă" msgid "Reload all subtitle settings from defaults" msgstr "Reâncarcă toate configurările subtitrării din implicite" msgid "Subtitle List" msgstr "Listă subtitrare" msgid "Reference Frames:" msgstr "Cadre referință:" msgid "" "Sane values are ~1-6. The more you add, the better the compression, but the slower the encode.\n" "Cel animation tends to benefit from more reference frames a lot more than film content.\n" "\n" "Note that many hardware devices have limitations on the number of supported reference\n" "frames, so if you're encoding for a handheld or standalone player, don't touch this unless\n" "you're absolutely sure you know what you're doing!" msgstr "" msgid "Maximum B-Frames:" msgstr "Maxim cadre B:" msgid "" "Sane values are ~2-5. This specifies the maximum number of sequential B-frames that the encoder can use.\n" "\n" "Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.\n" "Cel-animated source material and B-pyramid also significantly increase the usefulness of larger\n" "values.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off)." msgstr "" msgid "Pyramidal B-Frames:" msgstr "Cadre B piramidale:" msgid "" "B-pyramid improves compression by creating a pyramidal structure (hence the name)\n" "of B-frames, allowing B-frames to reference each other to improve compression.\n" "\n" "Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit." msgstr "" msgid "Weighted P-Frames:" msgstr "Cadre-P ponderate:" msgid "" "Performs extra analysis to decide upon weighting parameters for each frame.\n" "\n" "This improves overall compression slightly and improves the quality of fades greatly.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires weighted P-frame\n" "prediction to be disabled. Note that some devices and players, even those that support\n" "Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is\n" "completely incompatible with it, for example." msgstr "" msgid "8x8 Transform" msgstr "Transformare 8x8" msgid "" "The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.\n" "\n" "It improves compression by at least 5% at a very small speed cost and may\n" "provide an unusually high visual quality benefit compared to its compression\n" "gain. However, it requires High Profile, which many devices may not support." msgstr "" msgid "CABAC Entropy Encoding" msgstr "Codare entropică CABAC" msgid "" "After the encoder has done its work, it has a bunch of data that\n" "needs to be compressed losslessly, similar to ZIP or RAR. H.264 provides\n" "two options for this: CAVLC and CABAC. CABAC decodes a lot slower but\n" "compresses significantly better (10-30%), especially at lower bitrates.\n" "\n" "If you're looking to minimize CPU requirements for video playback, disable this option.\n" "Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled." msgstr "" msgid "Encoding Features" msgstr "Funcții codare" msgid "Motion Est. Method:" msgstr "Metodă estimare mișcare:" msgid "" "Controls the motion estimation method.\n" "\n" "Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.\n" "A better motion search method improves compression at the cost of speed.\n" "\n" "Diamond: performs an extremely fast and simple search using a diamond pattern.\n" "Hexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n" "Uneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n" "Exhaustive: performs a \"dumb\" search of every pixel in a wide area. Significantly slower for only a small compression gain.\n" "Transformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement." msgstr "" msgid "Subpel ME & Mode:" msgstr "Subpel ME & Mod:" msgid "" "This setting controls both subpixel-precision motion estimation and mode decision methods.\n" "\n" "Subpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n" "Mode decision is the method used to choose how to encode each block of the frame: a very important decision.\n" "SAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n" "6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n" "11 disables all early terminations in analysis.\n" "10 and 11, the most powerful and slowest options, require adaptive quantization (aq-mode > 0) and trellis 2 (always)." msgstr "" msgid "Motion Est. Range:" msgstr "Gamă estimare mișcare:" msgid "" "This is the distance x264 searches from its initial guess at the\n" "motion of a block in order to try to find its actual motion.\n" "\n" "The default is fine for most content, but extremely high motion video,\n" "especially at HD resolutions, may benefit from higher ranges, albeit at\n" "a high speed cost." msgstr "" msgid "Adaptive Direct Mode:" msgstr "Mod adaptiv direct:" msgid "" "H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n" "\n" "Spatial, the default, is almost always better, but temporal is sometimes useful too.\n" "x264 can, at the cost of a small amount of speed (and accordingly for a small compression gain),\n" "adaptively select which is better for each particular frame." msgstr "H.264 permite două moduri de predicție diferite, spațial și temporar, în cadrele-B.\n\nSpațial, cel implicit, este aproape întotdeauna mai bun, dar cel temporar este de asemenea uneori util.\nx264 poate, cu costul unei mici cantități de viteză (și în consecință, pentru un mic câștig de compresie),\nsă selecteze adaptiv care este mai bun pentru fiecare cadru individual." msgid "Adaptive B-Frames:" msgstr "Cadre B adaptive:" msgid "" "x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n" "\n" "Fast mode takes roughly the same amount of time no matter how many B-frames you specify.\n" "However, while fast, its decisions are often suboptimal.\n" "\n" "Optimal mode gets slower as the maximum number of B-Frames increases,\n" "but makes much more accurate decisions, especially when used with B-pyramid." msgstr "" msgid "Partitions:" msgstr "Partiții:" msgid "" "Mode decision picks from a variety of options to make its decision:\n" "this option chooses what options those are.\n" "\n" "Fewer partitions to check means faster encoding, at the cost of worse\n" "decisions, since the best option might have been one that was turned off." msgstr "" msgid "Trellis:" msgstr "Trellis:" msgid "" "Trellis fine-tunes the rounding of transform coefficients to\n" "squeeze out 3-5% more compression at the cost of some speed.\n" "\n" "\"Always\" uses trellis not only during the main encoding process, but also\n" "during analysis, which improves compression even more, albeit at great speed cost.\n" "\n" "Trellis costs more speed at higher bitrates and requires CABAC." msgstr "" msgid "Analysis" msgstr "Analiză" msgid "Adaptive Quantization Strength:" msgstr "Intensitate eșanționare adaptivă:" msgid "" "Adaptive quantization controls how the encoder distributes bits across the frame.\n" "\n" "Higher values take more bits away from edges and complex areas to improve areas with finer detail." msgstr "" msgid "Psychovisual Rate Distortion:" msgstr "Rată distorsiune psychovisual:" msgid "" "Psychovisual rate-distortion optimization takes advantage of the characteristics of human\n" "vision to dramatically improve apparent detail and sharpness.\n" "The effect can be made weaker or stronger by adjusting the strength.\n" "Being an RD algorithm, it requires mode decision to be at least \"6\"." msgstr "" msgid "Psychovisual Trellis:" msgstr "Psychovisual Trellis:" msgid "" "Psychovisual trellis is an experimental algorithm to further\n" "improve sharpness and detail retention beyond what Psychovisual RD does.\n" "\n" "Recommended values are around 0.2, though higher values may help for very\n" "grainy video or lower bitrate encodes. Not recommended for cel animation\n" "and other sharp-edged graphics." msgstr "" msgid "Deblocking: " msgstr "Deblocare:" msgid "" "H.264 deblocking filter.\n" "\n" "h.264 has a built-in deblocking filter that smooths out blocking artifacts\n" "after decoding each frame. This not only improves visual quality, but also\n" "helps compression significantly. The deblocking filter takes a lot of CPU power,\n" "so if you're looking to minimize CPU requirements for video playback, disable it.\n" "\n" "The deblocking filter has two adjustable parameters, \"strength\" (Alpha) and \"threshold\" (Beta).\n" "The former controls how strong (or weak) the deblocker is, while the latter controls how many\n" "(or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking.\n" "The default is 0 (normal strength) for both parameters." msgstr "" msgid "No DCT Decimate" msgstr "Fără decimare DCT" msgid "" "x264 normally zeroes out nearly-empty data blocks to save bits to\n" "be better used for some other purpose in the video. However, this can\n" "sometimes have slight negative effects on retention of subtle grain and\n" "dither.\n" "\n" "Don't touch this unless you're having banding issues or other such cases\n" "where you are having trouble keeping fine noise." msgstr "" msgid "Psychovisual" msgstr "Psychovisual" msgid "" "Your selected options will appear here.\n" "You can edit these and add additional options.\n" "\n" "Default values will not be shown. The defaults are:\n" "ref=3:bframes=3:b-adapt=fast:direct=spatial:\n" "b-pyramid=normal:weightp=2:me=hex:merange=16:\n" "subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\n" "deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\n" "no-fast-pskip=0:no-dct-decimate=0:cabac=1" msgstr "Opțiunile pe care le-ați selectat vor apare aici.\nLe puteți edita și adăuga opțiuni suplimentare.\n\nValorile implicite nu vor fi afișate. Aceste valori implicite sunt:\nref=3:bframes=3:b-adapt=rapid:direct=spațial:\nb-piramid=normal:lățimep=2:me=hex:merange=16:\nsubme=7:partiții=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\ndeblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\nno-fast-pskip=0:no-dct-decimate=0:cabac=1" msgid "Current x264 Advanced Option String" msgstr "Șir opțiuni avansate x264 curente" msgid "Advanced Video" msgstr "Video avansat" msgid "Chapter Markers" msgstr "Marcaje capitol" msgid "Add chapter markers to output file." msgstr "Adaugă marcajele capitolului la fișierul de ieșire." msgid "Chapters" msgstr "Capitole" msgid "Actors:" msgstr "Actori:" msgid "Director:" msgstr "Director:" msgid "Release Date:" msgstr "Dată lansare:" msgid "Comment:" msgstr "Comentariu:" msgid "Genre:" msgstr "Gen:" msgid "Description:" msgstr "Descriere:" msgid "Plot:" msgstr "Plot:" msgid "Tags" msgstr "Etichete" msgid "Settings" msgstr "Configurări" msgid "Edit" msgstr "Editare" msgid "Reload" msgstr "Reâncarcă" msgid "" "Mark selected queue entry as pending.\n" "Resets the queue job to pending and ready to run again." msgstr "" msgid "Reload All" msgstr "Reâncarcă tot" msgid "" "Mark all queue entries as pending.\n" "Resets all queue jobs to pending and ready to run again." msgstr "" msgid "OK" msgstr "OK" msgid "Select All" msgstr "Selectează tot" msgid "Mark all titles for adding to the queue" msgstr "Marchează toate titlurile pentru adăugare la coadă" msgid "Clear All" msgstr "Curăță tot" msgid "Unmark all titles" msgstr "Demarchează toate titlurile" msgid "Destination files OK. No duplicates detected." msgstr "Destinația fișierelor este în regulă. Nu s-au detectat duplicate." msgid "Select this title for adding to the queue.\n" msgstr "Selectează acest titlu pentru adăugarea la coadă.\n" msgid "Preferences" msgstr "Preferințe" msgid "Automatically check for updates" msgstr "Verifică automat pentru actualizări" msgid "When all encodes are complete" msgstr "Când toate codările sunt terminate" msgid "Use automatic naming (uses modified source name)" msgstr "Utilizează denumirea automată (utilizează numele sursă modificat)" msgid "Auto-Name Template" msgstr "Model denumire-automată" msgid "" "Available Options: {source} {title} {chapters} {date} {time} {quality} " "{bitrate}" msgstr "Opțiuni disponibile: {sursă} {titlu} {capitole} {dată} {timp} {calitate} {rată biți}" msgid "Use iPod/iTunes friendly (.m4v) file extension for MP4" msgstr "Utilizează extensia de fișier prietenoasă iPod/iTunes (.m4v) pentru MP4" msgid "Number of previews" msgstr "Număr de previzualizări" msgid "Filter short titles (seconds)" msgstr "Filtrare titluri scurte (secunde)" msgid "Show system tray icon" msgstr "Arată miniatura în bara de sistem" msgid "General" msgstr "General" msgid "Constant Quality fractional granularity" msgstr "Calitate constantă granularitate fracționată" msgid "Use dvdnav (instead of libdvdread)" msgstr "Utilizează dvdnav (în schimbul a libdvdread)" msgid "Put individual encode logs in same location as movie" msgstr "Pune jurnalele de codare individuale în aceiași locație ca filmul" msgid "Activity Log Verbosity Level" msgstr "Nivel detaliere jurnal activitate" msgid "Activity Log Longevity" msgstr "Longevitate jurnal activitate" msgid "Scale down High Definition previews" msgstr "Reduce previzualizările de înaltă definiție" msgid "Automatically Scan DVD when loaded" msgstr "Scanează automat DVD-urile când sunt încărcate" msgid "Scans the DVD whenever a new disc is loaded" msgstr "Scanează DVD-ul oricând este încărcat un nou disc" msgid "Hide Advanced Video Options Tab" msgstr "Ascunde fila opțiuni video avansate" msgid "" "Use advanced video options at your own risk.\n" "We recommend that you use the controls available\n" "on the Video tab instead." msgstr "Utilizați opțiunile video avansate pe propriul risc.\nVă recomandăm să utilizați în schimb controalele disponibile\nîn fila Video." msgid "Delete completed jobs from queue" msgstr "Șterge sarcinile terminate din coadă" msgid "" "By default, completed jobs remain in the queue and are marked as complete.\n" "Check this if you want the queue to clean itself up by deleting completed jobs." msgstr "Implicit, sarcinile terminate rămân în coadă și sunt marcate ca terminate.\nBifați asta dacă doriți să se curețe coada singură prin ștergerea sarcinilor terminate." msgid "Allow Tweaks" msgstr "Permite îmbunătățiri" msgid "Allow HandBrake For Dummies" msgstr "Permite HandBrake pentru novici" msgid "Advanced" msgstr "Avansat" msgid "Folder Name:" msgstr "Nume dosar:" msgid "Description" msgstr "Descriere" msgid "Preset Name:" msgstr "Nume preconfigurare:" msgid "Custom Picture Dimensions" msgstr "Dimensiuni poză personalizate" msgid "Maximum Width:" msgstr "Lățime maximă:" msgid "Enable maximum width limit." msgstr "Activează limită lățime maximă." msgid "" "This is the maximum width that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source width is greater.\n" "Setting this to 0 means there is no maximum width." msgstr "" msgid "Maximum Height:" msgstr "Înălțime maximă:" msgid "Enable maximum height limit." msgstr "Activează limită înălțime maximă." msgid "" "This is the maximum height that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source height is greater.\n" "Setting this to 0 means there is no maximum height." msgstr "" msgid "Select preview frames." msgstr "Selectați cadre de previzualizat.." msgid "" "Encode and play a short sequence of video starting from the current preview " "position." msgstr "Codificați și redați o scurtă secvență video pornind de la poziția de previzualizare curentă." msgid "Duration:" msgstr "Durată:" msgid "Set the duration of the live preview in seconds." msgstr "Configurați durata previzualizării în direct în secunde." msgid "Show Crop" msgstr "Arată decuparea" msgid "Show Cropped area of the preview" msgstr "Arată zona decupată a previzualizării" msgid "Fullscreen" msgstr "Ecran întreg" msgid "View Fullscreen Preview" msgstr "Vizualizare previzualizare ecran întreg" msgid "Title Number:" msgstr "Număr titlu:" msgid "Detected DVD devices:" msgstr "Dispozitive DVD detectate:" msgid "Setting:" msgstr "Configurare:" msgid "Import SRT" msgstr "Import SRT" msgid "Enable settings to import an SRT subtitle file" msgstr "Activare configurări pentru importul unui fișier subtitrare SRT" msgid "Embedded Subtitle List" msgstr "Listă subtitrare inclusă" msgid "Enable settings to select embedded subtitles" msgstr "Activare configurări pentru selectat subtitrări incluse" msgid "Character Code" msgstr "Cod caracter" msgid "Offset (ms)" msgstr "Compensare (ms)" msgid "" "Set the language of this subtitle.\n" "This value will be used by players in subtitle menus." msgstr "Configurați limba acestei subtitrări.\nAceastă valoare va fi utilizată de playere în meniuri subtitrare." msgid "" "Set the character code used by the SRT file you are importing.\n" "\n" "SRTs come in all flavours of character sets.\n" "We translate the character set to UTF-8.\n" "The source's character code is needed in order to perform this translation." msgstr "" msgid "Select the SRT file to import." msgstr "Selectare fișier SRT de importat." msgid "Srt File" msgstr "Fișier srt" msgid "Adjust the offset in milliseconds between video and SRT timestamps" msgstr "Ajustați compensarea în milisecunde între video și marcajul de timp SRT" msgid "Track" msgstr "Pistă" msgid "List of subtitle tracks available from your source." msgstr "Lista pistelor subtitrare disponibile din sursă." msgid "Forced Subtitles Only" msgstr "Numai subtitrări forțate" msgid "Burn into video" msgstr "Arde în video" msgid "" "Render the subtitle over the video.\n" "The subtitle will be part of the video and can not be disabled." msgstr "Randează subtitrarea peste video.\nSubtitrarea va fi parte din video și nu poate fi dezactivată." msgid "Set Default Track" msgstr "Configurare pistă implicită" msgid "Source Track" msgstr "Pistă sursă" msgid "List of audio tracks available from your source." msgstr "Lista pistelor audio disponibile din sursă." msgid "Track Name:" msgstr "Nume pistă:" msgid "" "Set the audio track name.\n" "\n" "Players may use this in the audio selection list." msgstr "Configurează numele pistei audio.\n\nPlayer-ele pot utiliza aceasta în lista selecției audio." msgid "Mix" msgstr "Mix" msgid "Sample Rate" msgstr "Rată eșanționare" msgid "" "Dynamic Range Compression: Adjust the dynamic range of the output audio track.\n" "\n" "For source audio that has a wide dynamic range (very loud and very soft sequences),\n" "DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder." msgstr "" msgid "Enable bitrate setting" msgstr "Activează configurarea ratei de biți" msgid "Enable quality setting" msgstr "Activare configurarea calității" msgid "" "Quality: For output codec's that support it, adjust the quality of " "the output." msgstr "Calitate: Pentru codecurile de ieșire care suportă asta, ajustează calitatea ieșirii." msgid "00.0" msgstr "00.0" msgid "" "Audio Gain: Adjust the amplification or attenuation of the output " "audio track." msgstr "Câștig audio: Ajustează amplificarea sau atenuarea pistei audio de ieșire." msgid "Skip This Version" msgstr "Omite această versiune" msgid "Remind Me Later" msgstr "Amintește-mi mai târziu" msgid "A new version of HandBrake is available!" msgstr "Este disponibilă o nouă versiune HandBrake!" msgid "HandBrake xxx is now available (you have yyy)." msgstr "HandBrake xxx este acum disponibil (aveți yyy)." msgid "Release Notes" msgstr "Note de lansare" msgid "First Track Matching Selected Languages" msgstr "Prima pistă se potrivește cu limbile selectate" msgid "All Tracks Matching Selected Languages" msgstr "Toate pistele se potrivesc limbilor selectate" msgid "Chapters:" msgstr "Capitole:" msgid "Seconds:" msgstr "Secunde:" msgid "Frames:" msgstr "Cadre:" msgid "Do Nothing" msgstr "Nu fă nimic" msgid "Show Notification" msgstr "Arată notificarea" msgid "Quit Handbrake" msgstr "Termină Handbrake" msgid "Put Computer To Sleep" msgstr "Pune computerul în adormire" msgid "Shutdown Computer" msgstr "Oprește computerul" msgid "Week" msgstr "Săptămână" msgid "Month" msgstr "Lună" msgid "Year" msgstr "An" msgid "Immortal" msgstr "Nemuritor" msgid "Never" msgstr "Niciodată" msgid "Daily" msgstr "Zilnic" msgid "Weekly" msgstr "Săptămânal" msgid "Monthly" msgstr "Lunar" msgid "Default" msgstr "Implicit" msgid "Fast" msgstr "Repede" msgid "Slow" msgstr "Încet" msgid "Slower" msgstr "Mai încet" msgid "Ultralight" msgstr "Foarte ușor" msgid "Light" msgstr "Ușor" msgid "Medium" msgstr "Mediu" msgid "Strong" msgstr "Puternic" msgid "Film" msgstr "Film" msgid "Grain" msgstr "Granulație" msgid "High Motion" msgstr "Mișcare înaltă" msgid "Animation" msgstr "Animație" msgid "Spatial" msgstr "Spațial" msgid "Temporal" msgstr "Temporar" msgid "Automatic" msgstr "Automat" msgid "Optimal" msgstr "Optim" msgid "Normal" msgstr "Normal" msgid "Simple" msgstr "Simplu" msgid "Smart" msgstr "Inteligent" msgid "Diamond" msgstr "Diamant" msgid "Hexagon" msgstr "Hexagon" msgid "Uneven Multi-Hexagon" msgstr "Uneven Multi-Hexagon" msgid "Exhaustive" msgstr "Exhaustiv" msgid "Hadamard Exhaustive" msgstr "Hadamard Exhaustiv" msgid "Most" msgstr "Cel mai mult" msgid "Some" msgstr "Unele" msgid "All" msgstr "Toate" msgid "Encode only" msgstr "Numai codare" msgid "Always" msgstr "Mereu" msgid "(NTSC Film)" msgstr "(NTSC Film)" msgid "(PAL Film/Video)" msgstr "(PAL Film/Video)" msgid "(NTSC Video)" msgstr "(NTSC Video)" msgid "%d - %02dh%02dm%02ds - %s" msgstr "%d - %02dh%02dm%02ds - %s" msgid "%d (%05d.MPLS) - %02dh%02dm%02ds" msgstr "%d (%05d.MPLS) - %02dh%02dm%02ds" msgid "%d (%05d.MPLS) - Unknown Length" msgstr "%d (%05d.MPLS) - Lungime necunoscută" msgid "%d - %02dh%02dm%02ds" msgstr "%d - %02dh%02dm%02ds" msgid "%d - Unknown Length" msgstr "%d - Lungime necunoscută" msgid "No Titles" msgstr "Fără titluri" msgid "No Audio" msgstr "Fără audio" msgid "Foreign Audio Search" msgstr "Căutare audio străin" #, c-format msgid "Chapter %2d" msgstr "Capitol %2d" #, c-format msgid "N/A" msgstr "N/A" #, c-format msgid "" "Invalid Deinterlace Settings:\n" "\n" "%s\n" msgstr "Configurări deântrețesere nevalide:\n\n%s\n" #, c-format msgid "" "Invalid Detelecine Settings:\n" "\n" "%s\n" msgstr "Configurări Detelecine nevalide:\n\n%s\n" #, c-format msgid "" "Invalid Decomb Settings:\n" "\n" "%s\n" msgstr "Configurări Decomb nevalide:\n\n%s\n" msgid "" "Theora is not supported in the MP4 container.\n" "\n" "You should choose a different video codec or container.\n" "If you continue, FFMPEG will be chosen for you." msgstr "Theora nu este suportat în containerul MP4.\n\nAr trebui să alegeți un codor sau container video diferit.\nDacă veți continua, FFMPEG va alege pentru dumneavoastră." msgid "Continue" msgstr "Continuare" msgid "No title found.\n" msgstr "Nu s-a găsit nici un titlu.\n" msgid "" "Only one subtitle may be burned into the video.\n" "\n" "You should change your subtitle selections.\n" "If you continue, some subtitles will be lost." msgstr "Numai o singură subtitrare poate fi arsă în video.\n\nAr trebui să schimbați selecțiile de subtitrări.\nDacă veți continua, unele subtitrări vor fi pierdute." msgid "" "Srt file does not exist or not a regular file.\n" "\n" "You should choose a valid file.\n" "If you continue, this subtitle will be ignored." msgstr "Fișierul srt nu există sau nu este un fișier obișnuit.\n\nAr trebui să alegeți un fișier valid.\nDacă veți continua, această subtitrare va fi ignorată." msgid "" "The source does not support Pass-Thru.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "%s is not supported in the %s container.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "%s nu este suportat în containerul %s .\n\nAr trebui să alegeți un codor audio diferit.\nDacă veți continua, va fi ales unul pentru dumneavoastră." #, c-format msgid "" "The source audio does not support %s mixdown.\n" "\n" "You should choose a different mixdown.\n" "If you continue, one will be chosen for you." msgstr "Sursa audio nu suportă %s mixarea.\n\nAr trebui să alegeți o mixare diferită.\nDacă veți continua, va fi aleasă una pentru dumneavoastră." #, c-format msgid "" "Unable to create %s.\n" "\n" "Internal error. Could not parse UI description.\n" "%s" msgstr "Nu se poate crea %s.\n\nEroare internă. Nu se poate analiza descrierea UI.\n%s" msgid "Index" msgstr "Index" msgid "Duration" msgstr "Durată" msgid "Title" msgstr "Titlu" msgid "Job Information" msgstr "Informație sarcină" msgid "Track Information" msgstr "Informație pistă" msgid "Preset Name" msgstr "Nume preconfigurare" msgid "The device or file to encode" msgstr "Dispozitivul sau fișierul de codat" msgid "The preset values to use for encoding" msgstr "Valorile preconfigurări de utilizat pentru codare" msgid "Spam a lot" msgstr "Mult spam" msgid "- Transcode media formats" msgstr "-Transcodare formate video" msgid "Globals" msgstr "Globale" msgid "Presets" msgstr "Preconfigurări" msgid "Folder" msgstr "Dosar" #, c-format msgid "%s path: (%s)" msgstr "%s cale: (%s)" #, c-format msgid "%s indices: len %d" msgstr "%s indicii: len %d" msgid "Type" msgstr "Tip" msgid "Failed to find parent folder when adding child." msgstr "A eșuat găsirea dosarului părinte când se adaugă copilul." msgid "Failed to find parent folder while adding child." msgstr "A eșuat găsirea dosarului părinte în timpul adăugării copilului." #, c-format msgid "Can't map language value: (%s)" msgstr "Nu se poate mapa valoarea limbii: (%s)" #, c-format msgid "Can't map value: (%s)" msgstr "Nu se poate mapa valoarea: (%s)" msgid "Subtitles" msgstr "Subtitrări" msgid "" "%s: Folder already exists.\n" "You can not replace it with a preset." msgstr "%s: Dosarul deja există.\nNu se poate înlocui cu o preconfigurare." msgid "" "%s: Preset already exists.\n" "You can not replace it with a folder." msgstr "%s: Preconfigurarea deja există.\nNu se poate înlocui cu un dosar." msgid "Import Preset" msgstr "Import preconfigurare" msgid "All (*)" msgstr "Tot (*)" msgid "Presets (*.plist)" msgstr "Preconfigurări (*.plist)" msgid "Export Preset" msgstr "Export preconfigurare" msgid "" "Confirm deletion of %s:\n" "\n" "%s" msgstr "Confirmați ștergerea a %s:\n\n%s" msgid "folder" msgstr "dosar" msgid "preset" msgstr "preconfigurare" msgid "No selection??? Perhaps unselected." msgstr "Nimic selectat??? Poate este neselectat." #, c-format msgid "Gstreamer Error: %s" msgstr "Eroare gstreamer: %s" #, c-format msgid "" "Missing GStreamer plugin\n" "Audio or Video may not play as expected\n" "\n" "%s" msgstr "Lipsește modulul GStreamer\nAudio sau video poate să nu fie redat cum așteptați\n\n%s" msgid "Done" msgstr "Finalizat" msgid "Windowed" msgstr "Mod ferestră" msgid "Seconds" msgstr "Secunde" msgid "Frames" msgstr "Cadre" #, c-format msgid "" "%s (Title %d, %s %d through %d, 2 Video Passes) -->" " %s" msgstr "%s (Titlu %d, %s %d prin %d, 2 Pași video) --> %s" #, c-format msgid "" "%s (Title %d, %s %d through %d) --> %s" msgstr "%s (Titlu %d, %s %d prin %d) --> %s" #, c-format msgid "Modified Preset Based On: %s\n" msgstr "Preconfigurare modificată bazată pe: %s\n" #, c-format msgid "Preset: %s\n" msgstr "Preconfigurare: %s\n" #, c-format msgid "Format: %s Container\n" msgstr "Format: %s Container\n" msgid "Container Options:" msgstr "Opțiuni container:" #, c-format msgid "%sChapter Markers" msgstr "%sMarcaje capitol" #, c-format msgid "%siPod 5G Support" msgstr "%sSuport iPod 5G" #, c-format msgid "%sWeb Optimized" msgstr "%sOptimizat web" #, c-format msgid "%sLarge File Size (>4GB)" msgstr "%sDimensiune fișier mare (>4GB)" #, c-format msgid "Destination: %s\n" msgstr "Destinație: %s\n" msgid "(Aspect Preserved)" msgstr "(Aspect păstrat)" msgid "(Aspect Lost)" msgstr "(Aspect pierdut)" msgid "(Anamorphic)" msgstr "(Anamorfic)" msgid "(Custom Anamorphic)" msgstr "(Anamorfic personalizat)" msgid "Picture: " msgstr "Poză: " #, c-format msgid "Source: %d x %d, Output %d x %d %s, Crop %d:%d:%d:%d" msgstr "Sursă: %d x %d, Ieșire %d x %d %s, Decupare %d:%d:%d:%d" #, c-format msgid ", Display %d x %d" msgstr ", Afișaj %d x %d" msgid "Filters:" msgstr "Filtre:" #, c-format msgid "%sDetelecine" msgstr "%sDetelecine" #, c-format msgid "%sDecomb" msgstr "%sDecomb" #, c-format msgid "%sDeinterlace" msgstr "%sDeântrețesere" #, c-format msgid "%sDenoise Filter %s:" msgstr "%sFiltru reducere zgomot %s:" #, c-format msgid "%sDeblock: %d" msgstr "%sDeblocare: %d" #, c-format msgid "%sGrayscale" msgstr "%sScală de gri" #, c-format msgid "Video: %s" msgstr "Video: %s" #, c-format msgid ", Framerate: %s %s" msgstr ", Rată de cadre: %s %s" #, c-format msgid ", Framerate: Peak %s (may be lower)" msgstr ", Rată de cadre: Vârf %s (poate fi mai mică)" #, c-format msgid ", Framerate: %s (constant frame rate)" msgstr ", Rată de cadre: %s (rată de cadre constantă)" msgid "Error" msgstr "Eroare" msgid "Bitrate:" msgstr "Rată de biți:" msgid "Bitrate" msgstr "Rată de biți" msgid "Quality" msgstr "Calitate" msgid "Turbo 1st Pass: On\n" msgstr "Primul pas turbo: Pornit\n" #, c-format msgid "Video Options: Preset: %s" msgstr "Opțiuni video: Preconfigurare: %s" msgid " - Tune: " msgstr "" #, c-format msgid " - Profile: %s" msgstr " - Profil: %s" #, c-format msgid " - Level: %s" msgstr " - Nivel: %s" #, c-format msgid "Advanced Options: %s\n" msgstr "Opțiuni avansate: %s\n" msgid "Audio: " msgstr "Audio: " #, c-format msgid "Audio Tracks: %d" msgstr "Piste audio: %d" #, c-format msgid "Bitrate: %d" msgstr "Rată de biți: %d" #, c-format msgid "%s --> Encoder: %s" msgstr "%s --> Codor: %s" #, c-format msgid "%s --> Encoder: %s, Mixdown: %s, SampleRate: %s, %s" msgstr "%s --> Codor: %s, Mixarea: %s, Rată eșanționare: %s, %s" msgid "Subtitle: " msgstr "Subtitrare: " #, c-format msgid "Subtitle Tracks: %d\n" msgstr "Piste subtitrare: %d\n" msgid " (Force)" msgstr "(Forțat)" msgid " (Burn)" msgstr "(Arde)" msgid " (Default)" msgstr "(Implicit)" #, c-format msgid " %s (%s), %s, Offset (ms) %d%s\n" msgstr " %s (%s), %s, Compensare (ms) %d%s\n" #, c-format msgid "" "Destination: %s\n" "\n" "Another queued job has specified the same destination.\n" "Do you want to overwrite?" msgstr "Destinație: %s\n\nO altă sarcină din coadă are specificată aceiași destinație.\nDoriți să suprascrieți?" msgid "Overwrite" msgstr "Suprascrie" #, c-format msgid "" "Destination: %s\n" "\n" "This is not a valid directory." msgstr "Destinație: %s\n\nAcesta nu este un director valid." #, c-format msgid "" "Destination: %s\n" "\n" "Can not read or write the directory." msgstr "Destinație: %s\n\nNu se poate citi sau scrie în director." #, c-format msgid "" "Destination filesystem is almost full: %uM free\n" "\n" "Encode may be incomplete if you proceed.\n" msgstr "Sistemul de fișiere destinație este aproape plin: %uM liber\n\nCodificarea poate fi incompletă dacă veți continua.\n" msgid "Proceed" msgstr "Continuare" #, c-format msgid "" "Destination: %s\n" "\n" "File already exists.\n" "Do you want to overwrite?" msgstr "Destinație: %s\n\nFișierul există deja.\nDoriți să îl suprascrieți?" msgid "" "Duplicate destination files detected.\n" "Duplicates will not be added to the queue." msgstr "" msgid "No Title" msgstr "Fără titlu" msgid "" "There is another title with the same destination file name.\n" "This title will not be added to the queue unless you change\n" "the output file name.\n" msgstr "Este un alt titlu cu același nume fișier destinație.\nAcest titlu nu va fi adăugat la coadă până nu schimbați\nnumele fișierului de ieșire.\n" msgid "Stop" msgstr "Oprire" msgid "Stop Encoding" msgstr "Oprire codare" msgid "Resume" msgstr "Reluare" msgid "Resume Encoding" msgstr "Reluare codare" msgid "S_top Queue" msgstr "O_prire coadă" msgid "_Start Queue" msgstr "_Pornire coadă" msgid "_Resume Queue" msgstr "_Reluare coadă" msgid "Resume Queue" msgstr "Reluare coadă" msgid "" "You are currently encoding. What would you like to do?\n" "\n" msgstr "Codificași actualmente. Ce doriți să faceți?\n\n" #, c-format msgid "" "You have %d unfinished job(s) in a saved queue.\n" "\n" "Would you like to reload them?" msgstr "Aveți %d sarcină(i) neterminate în coada salvată.\n\nDoriți să fie reâncărcate?" msgid "No" msgstr "Nu" msgid "Yes" msgstr "Da" #, c-format msgid "Usage: %s infile [outfile]\n" msgstr "Utilizare: %s fișier intrare [fișier ieșire]\n" #, c-format msgid "Offset: %dms" msgstr "Compensare: %dms" msgid "Burned Into Video" msgstr "Ars în video" msgid "Passthrough" msgstr "Trecere directă" msgid "through" msgstr "prin" msgid "(Forced Subtitles Only)" msgstr "(Numai subtitrări forțate)" msgid "(Default)" msgstr "(Implicit)" msgid "Error!" msgstr "Eroare!" #, c-format msgid "Preferred Language: %s" msgstr "Limbă preferată: %s" #, c-format msgid "Add %s subtitle track if default audio is not %s" msgstr "Adaugă %s pistă subtitrare dacă audio implicit nu este %s" #, c-format msgid "Type %s" msgstr "Tip %s" #, c-format msgid "Type %s value %s" msgstr "Tip %s valoare %s" #, c-format msgid "Type %s value %d" msgstr "Tip %s valoare %d" #, c-format msgid "Type %s value %" msgstr "Tip %s valoare %" #, c-format msgid "Type %s value %f" msgstr "Tip %s valoare %f" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"%s\"" msgstr "%s\n\nOpțiuni expandate:\n\"%s\"" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"\"" msgstr "%s\n\nOpțiuni expandate:\n\"\"" msgid "Any" msgstr "Oricare" msgid "0: SAD, no subpel" msgstr "0: SAD, fără subpel" msgid "4: SATD, qpel on all" msgstr "4: SATD, qpel în toate" msgid "5: SATD, multi-qpel on all" msgstr "5: SATD, multi-qpel la toate" msgid "6: RD in I/P-frames" msgstr "6: RD în cadre I/P" msgid "7: RD in all frames" msgstr "7: RD în toate cadrele" msgid "8: RD refine in I/P-frames" msgstr "8: RD redefinit în cadrele I/P" msgid "9: RD refine in all frames" msgstr "9: RD redefinit în toate cadrele" msgid "10: QPRD in all frames" msgstr "10: QPRD în toate cadrele" msgid "11: No early terminations in analysis" msgstr "" msgid "Your names" msgstr "Angelescu Constantin" msgid "Your emails" msgstr "titus0818@yahoo.com" HandBrake-0.10.2/gtk/po/es.po0000664000175200017520000017251212466165306016232 0ustar handbrakehandbrake# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Kevin López Brante , 2014 # Soleil Rojas , 2014 msgid "" msgstr "" "Project-Id-Version: HandBrake-0.10\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-03 13:01+0300\n" "PO-Revision-Date: 2014-11-28 14:44+0000\n" "Last-Translator: VictorR2007 \n" "Language-Team: Spanish (http://www.transifex.com/projects/p/handbrake/language/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Quality: " msgstr "Calidad:" #, c-format msgid "Bitrate: %dkbps" msgstr "Tasa de bits: %dkbps" #, c-format msgid "Bitrate: %.4gkbps" msgstr "Tasa de bits: %.4gkbps" msgid "Passthrough" msgstr "Pasar a través" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s\n" "Track Name: %s" msgstr "%s\nGanancia: %s\nCompresión: %s\nNombre de la pista: %s" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s" msgstr "%s\nGanancia: %s\nCompresión: %s" msgid "Add" msgstr "Agregar" msgid "" "Add an audio encoder.\n" "Each selected source track will be encoded with all selected encoders." msgstr "Agregar un codificador de audio.\nCada pista seleccionada se codificará con todos los codificadores seleccionados." msgid "Set the audio codec to encode this track with." msgstr "Elige el códec de audio con el cual codificar esta pista." msgid "Set the bitrate to encode this track with." msgstr "Elige la tasa de bits en la que se codificará la pista." msgid "" "Audio Quality:\n" "For encoders that support it, adjust the quality of the output." msgstr "Calidad de Audio:\nPara los codificadores que lo soportan, ajusta la calidad de la salida." msgid "Set the mixdown of the output audio track." msgstr "Elige el tipo de mezcla de la pista de salida de audio" msgid "Set the sample rate of the output audio track." msgstr "Elige la tasa de muestreo de la pista de salida de audio." msgid "" "Audio Gain:\n" "Adjust the amplification or attenuation of the output audio track." msgstr "Ganancia de Audio:\nAjustar la amplificación o atenuación de la pista de salida de audio." msgid "0dB" msgstr "0dB" msgid "%ddB" msgstr "%ddB" msgid "%.4gkHz" msgstr "%.4gkHz" msgid "%d - %s (%.4gkHz)" msgstr "%d - %s (%.4gkHz)" msgid "" "Dynamic Range Compression:\n" "Adjust the dynamic range of the output audio track.\n" "For source audio that has a wide dynamic range,\n" "very loud and very soft sequences, DRC allows you\n" "to 'compress' the range by making loud sounds\n" "softer and soft sounds louder.\n" msgstr "Compresión de Rango Dinámico:\nAjusta el rango dinámico de la pista de salida de audio.\nPara un audio de origen que tenga un rango dinámico amplio,\ncon secuencias muy fuertes y muy suaves, la Compresión permite\n'comprimir' el rango de volumen, haciendo los ruidos fuertes\nmás suaves y los más suaves más fuertes.\n" msgid "Remove this audio encoder" msgstr "Eliminar este codificador de audio" msgid "Closing HandBrake will terminate encoding.\n" msgstr "Al cerrar HandBrake se cancelará la codificación.\n" msgid "No Title Found" msgstr "No se encontró el titulo" msgid "none" msgstr "Ninguno" msgid "Not Selected" msgstr "No Seleccionado" msgid "zerolatency x264 tune selected, forcing constant framerate" msgstr "ajuste cero latencia x264 seleccionado, forzando velocidad de fotogramas constante" msgid "Scanning ..." msgstr "Analizando ..." msgid "Stop Scan" msgstr "Detener análisis" msgid "On" msgstr "Sí" msgid "Strict" msgstr "Estricto" msgid "Loose" msgstr "Libre" msgid "Custom" msgstr "Personalizado" msgid "Unknown" msgstr "Desconocido" msgid "auto" msgstr "automático" #, c-format msgid "" "%s\n" "\n" "%s in %d seconds ..." msgstr "%s\n\n%s en %d segundos ..." #, c-format msgid "%sYour movie will be lost if you don't continue encoding." msgstr "%sSu película se perderá si no se continúa la codificación." msgid "Cancel Current and Stop" msgstr "Cancelar actual y detener" msgid "Cancel Current, Start Next" msgstr "Cancelar actual, comenzar siguiente" msgid "Finish Current, then Stop" msgstr "Finalizar actual y despues detener" msgid "Continue Encoding" msgstr "Continuar codificación" msgid "Custom " msgstr "Personalizado" msgid "Modified " msgstr "Modificado" #, c-format msgid "Handbrake Version: %s (%d)\n" msgstr "Handbrake Versión: %s (%d)\n" #, c-format msgid "%d encode(s) pending" msgstr "%d codificación(es) pendiente(s)" #, c-format msgid "job %d of %d, " msgstr "trabajo %d de %d, " #, c-format msgid "pass %d (subtitle scan) of %d, " msgstr "pasada %d (análisis de subtítulos) de %d," #, c-format msgid "pass %d of %d, " msgstr "pasada %d de %d," #, c-format msgid "Encoding: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" msgstr "Codificando: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %% (ETA %02dh%02dm%02ds)" msgstr "Codificando: %s%s%.2f %% (ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %%" msgstr "Codificando: %s%s%.2f %%" msgid "Searching for start time, " msgstr "Buscando la hora de inicio," msgid "Scanning..." msgstr "Analizando..." #, c-format msgid "Scanning title %d of %d..." msgstr "Analizando título %d de %d..." #, c-format msgid "Scanning title %d of %d preview %d..." msgstr "Analizando título %d de %d vista previa %d..." msgid "Source" msgstr "Origen" msgid "Choose Video Source" msgstr "Elegir origen de video" msgid "Paused" msgstr "Pausado" msgid "Encode Done!" msgstr "Codificación finalizada!" msgid "Encode Canceled." msgstr "Codificacón cancelada." msgid "Encode Failed." msgstr "Codificación fallida." msgid "Muxing: This may take a while..." msgstr "Multiplexando: Esto puede tomar un tiempo..." msgid "Scan this DVD source" msgstr "Analizar esta DVD de origen" msgid "AutoScan" msgstr "Autoanálisis" msgid "Suspend" msgstr "Suspender" #, c-format msgid "Suspend failed: %s" msgstr "Suspension fallida: %s" msgid "Suspend failed" msgstr "Suspension fallida" msgid "Shutdown" msgstr "Apagar" #, c-format msgid "Shutdown failed: %s" msgstr "Apagado fallido: %s" msgid "Shutdown failed" msgstr "Apagado fallido" msgid "Encoding" msgstr "Codificando" #, c-format msgid "press %d %d" msgstr "presione %d %d" #, c-format msgid "" "Invalid Settings:\n" "%s" msgstr "Ajustes inválidos:\n%s" msgid "Cancel" msgstr "Cancelar" #, c-format msgid "%s: %.4g (Warning: lossless)" msgstr "%s: %.4g (Advertencia: sin pérdida)" #, c-format msgid "HandBrake %s/%s is now available (you have %s/%d)." msgstr "HandBrake %s/%s ya está disponible (tú tienes %s/%d)." msgid "Encode Complete" msgstr "Codificación completa" msgid "Put down that cocktail, Your HandBrake queue is done!" msgstr "Deja ese coctél, Tu cola de HandBrake finalizó!" msgid "Your encode is complete." msgstr "Su codificación está completa." msgid "Shutting down the computer" msgstr "Apagando la computadora" msgid "Putting computer to sleep" msgstr "Poniendo la computadora a dormir" msgid "Quiting Handbrake" msgstr "Saliendo de HandBrake" msgid "Bottom" msgstr "Inferior" #, c-format msgid "open failed: %s\n" msgstr "apertura fallida: %s\n" msgid "No key for dictionary item" msgstr "No hay clave para elemento de diccionario" msgid "Invalid container type. This shouldn't happen" msgstr "Tipo de contenedor inválido. Esto no debería suceder" #, c-format msgid "%s:missing a requried attribute" msgstr "%s:falta un atributo requerido" #, c-format msgid "" "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" msgstr "Uso: %s [-I ] \nResumen:\n Crea una plist a partir de una lista de recursos \nOptions:\n I - Incluye la ruta para buscar archivos\n Archivo de recursos de entrada\n Archivo de recursos de salida plist\n" msgid "language" msgstr "idioma" msgid "Language" msgstr "Idioma" msgid "" "The language this text is in, as an ISO code. Pango can use this as a hint " "when rendering the text. If you don't understand this parameter, you " "probably don't need it" msgstr "El lenguaje en el que está este texto, es un código ISO. Pango puede usar esto como una ayuda al renderizar el texto. Si no entiendes este parámetro, probablemente no lo necesites" msgid "" "The preferred place to ellipsize the string, if the cell renderer does not " "have enough room to display the entire string" msgstr "El lugar preferido para elipzar la cadena, si el renderizador de celdas no tiene suficiente espacio para mostrar la cadena completa" msgid "" "How to break the string into multiple lines, if the cell renderer does not " "have enough room to display the entire string" msgstr "Cómo romper la cadena en múltiples líneas, si el renderizador de celdas no tiene suficiente espacio para mostrar la cadena completa" msgid "" "Render the subtitle over the video.\n" "\n" "The subtitle will be part of the video and can not be disabled." msgstr "Renderizar el subtítulo sobre el video\n\nEl subtítulo se pegará al video y no se podrá deshabilitar." msgid "Burned In" msgstr "Pegado" msgid "" "Set the default output subtitle track.\n" "\n" "Most players will automatically display this\n" "subtitle track whenever the video is played.\n" "\n" "This is useful for creating a \"forced\" track\n" "in your output." msgstr "Elegir la pista de subtítulos predeterminada.\n\nLa mayoría de los reproductores mostrarán\nesta pista de subtítulos al reproducir el video.\n\nEsto es útil para crear una pista \"forzada\" en la salida." msgid "Default" msgstr "Predeterminada" msgid "" "Use only subtitles that have been flagged\n" "as forced in the source subtitle track\n" "\n" "\"Forced\" subtitles are usually used to show\n" "subtitles during scenes where someone is speaking\n" "a foreign language." msgstr "Usar solo subtítulos que se hayan marcado\ncomo forzados en la pista de origen\n\nLos subtítulos \"forzados\" se utilizan habitualmente \npara mostrarlos en escenas donde alguien hable un\nidioma extranjero." msgid "Forced Only" msgstr "Solo forzado" msgid "" "Add (or subtract) an offset (in milliseconds)\n" "to the start of the SRT subtitle track.\n" "\n" "Often, the start of an external SRT file\n" "does not coincide with the start of the video.\n" "This setting allows you to synchronize the files." msgstr "Añadir (o restar) un desplazamiento (en milisegundos)\nal inicio de la pista de subtítulo SRT \n\nA menudo, el inicio de un archivo SRT externo\nno coincide con el inicio del video\nEsta configuración le permite sincronizar los archivos." msgid "SRT Offset" msgstr "Desplazamiento SRT" msgid "Off" msgstr "Desactivado" msgid "" "The source subtitle track\n" "\n" "You can choose any of the subtitles\n" "recognized in your source file.\n" "\n" "In addition, there is a special track option\n" "\"Foreign Audio Search\". This option will add\n" "an extra pass to the encode that searches for\n" "subtitles that may correspond to a foreign\n" "language scene. This option is best used in\n" "conjunction with the \"Forced\" option." msgstr "La pista de subtítulos de origen\n\nPuedes elegir cualquiera de los subtítulos\nreconocidos en el archivo de origen.\n\nAdemás, hay una opción especial de pista\n\"Búsqueda de audio extranjero\". Esta opción añadirá\nun pase extra a la codificación que busca\nsubtítulos que pueden corresponder a una escena\nen un idioma extranjero. Esta opción se utiliza mejor en\njunto con la opción \"Forzado\"." msgid "Track" msgstr "Pista" msgid "About HandBrake" msgstr "Acerca de HandBrake" msgid "" "Copyright © 2008 - 2013 John Stebbins\n" "Copyright © 2004 - 2013, HandBrake Devs" msgstr "Copyright © 2008 - 2013 John Stebbins\nCopyright © 2004 - 2013, HandBrake Devs" msgid "" "HandBrake is a GPL-licensed, multiplatform, multithreaded video transcoder." msgstr "HandBrake es un transcodificador de vídeo multi-hilo, multi-plataforma, licenciado bajo la GPL." msgid "http://handbrake.fr" msgstr "http://handbrake.fr" msgid "" "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n" "\n" "HandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgstr "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n\nHandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgid "_Minimize/Maximize" msgstr "_Minimizar/Maximizar" msgid "_Pause Queue" msgstr "_Pausar cola" msgid "_Quit" msgstr "Salir (_q)" msgid "_About" msgstr "_Acerca de" msgid "HandBrake" msgstr "HandBrake" msgid "_File" msgstr "_Archivo" msgid "_Source" msgstr "_Origen" msgid "Single _Title" msgstr "Un _título " msgid "_Destination" msgstr "_Destino" msgid "_Preferences" msgstr "_Preferencias" msgid "_Queue" msgstr "_Cola" msgid "_Add" msgstr "_Agregar" msgid "Add _Multiple" msgstr "Agregar _múltiple" msgid "_Start" msgstr "_Empezar" msgid "_Pause" msgstr "_Pausar" msgid "_View" msgstr "_Ver" msgid "HandBrake For _Dumbies" msgstr "HandBrake para _novatos" msgid "_Show Presets" msgstr "Mostrar pre_selecciones" msgid "_Preview" msgstr "Vista _previa" msgid "_Activity Window" msgstr "Ventana de _actividad" msgid "Show _Queue" msgstr "Mostrar _cola" msgid "_Presets" msgstr "_Preselecciones" msgid "_Save" msgstr "_Guardar" msgid "_Delete" msgstr "_Eliminar" msgid "_Make Default" msgstr "Dejar como predeter_minado" msgid "_New Folder" msgstr "Carpeta _nueva" msgid "_Export" msgstr "_Exportar" msgid "_Import" msgstr "_Importar" msgid "_Update Built-in Presets" msgstr "Act_ualizar preselecciones por defecto" msgid "_Help" msgstr "_Ayuda" msgid "_Guide" msgstr "_Guía" msgid "Start Encoding" msgstr "Empezar a codificar" msgid "Start" msgstr "Empezar" msgid "Pause Encoding" msgstr "Pausar codificación" msgid "Pause" msgstr "Pausar" msgid "Add to Queue" msgstr "Agregar a cola" msgid "Enqueue" msgstr "Encolar" msgid "Show Queue" msgstr "Mostrar cola" msgid "Queue" msgstr "Cola" msgid "" "Open Picture Settings and Preview window.\n" "Here you can adjust cropping, resolution, aspect ratio, and filters." msgstr "Abrir ventana de Ajustes de imagen y Vista previa\nAquí puedes ajustar el recorte, la resolución, la razón de aspecto y los filtros." msgid "Preview" msgstr "Vista previa" msgid "Show Activity Window" msgstr "Mostrar Ventana de actividad" msgid "Activity" msgstr "Actividad" msgid "Source:" msgstr "Origen:" msgid "None" msgstr "Ninguno" msgid "Title:" msgstr "Título:" msgid "" "Set the title to encode.\n" "By default the longest title is chosen.\n" "This is often the feature title of a DVD." msgstr "Elige el título a codificar.\nPor defecto, se elige el título más largo.\nA menudo, es la película del DVD." msgid "Angle:" msgstr "Ángulo" msgid "For multi-angle DVD's, select the desired angle to encode." msgstr "Para DVDs multi-ángulos, elige el ángulo deseado." msgid "Reset All Titles" msgstr "Reiniciar todos los títulos" msgid "Apply current settings to all titles" msgstr "Aplicar los ajustes actuales a todos los títulos" msgid "Range of title to encode. Can be chapters, seconds, or frames." msgstr "El rango del título a codificar. Pueden ser capítulos, segundos, o cuadros." msgid "Set the first chapter to encode." msgstr "Elige el primer capítulo a codificar." msgid "Set the last chapter to encode." msgstr "Elige el último capítulo a codificar." msgid "Duration:" msgstr "Duración" msgid "Destination" msgstr "Destino" msgid "File:" msgstr "Archivo:" msgid "Destination filename for your encode." msgstr "Nombre del archivo de destino." msgid "Destination directory for your encode." msgstr "Directorio de destino de tu codificación." msgid "Destination Directory" msgstr "Directorio de destino" msgid "Format:" msgstr "Formato:" msgid "Format to mux encoded tracks to." msgstr "Formato al cual hacer el mux de las pistas codificadas." msgid "iPod 5G Support" msgstr "Soporte iPod 5G" msgid "Add iPod Atom needed by some older iPods." msgstr "Agregar \"átomo\" iPod requerido por algunos iPods antiguos." msgid "Web optimized" msgstr "Optimizado para web" msgid "" "Optimize the layout of the MP4 file for progressive download.\n" "This allows a player to initiate playback before downloading the entire file." msgstr "Optimizar el diseño del archivo MP4 para facilitar la descarga progresiva.\nPermite a un reproductor empezar con la reproducción antes de descargar todo el archivo." msgid "Large file (>4GB)" msgstr "Archivo grande (>4GB)" msgid "" "Allow 64 bit MP4 file which can be over 4GB.\n" "\n" "Caution: This option may break device compatibility." msgstr "Permitir archivos MP4 de 64 bit que pueden ser mas de 4GB.\n\nPrecaución: Esta opción puede romper la compatibilidad de dispositivos." msgid "Presets List" msgstr "Lista de preselecciones" msgid "Source Codec:" msgstr "Códec de origen" msgid "Dimensions:" msgstr "Dimensiones" msgid "Aspect: " msgstr "Aspecto" msgid "Frame Rate:" msgstr "Tasa de cuadros" msgid "Source Picture Parameters" msgstr "Parámetros de la imagen de origen" msgid "Autocrop:" msgstr "Recorte automático:" msgid "Crop:" msgstr "Recorte:" msgid "Crop Dimensions:" msgstr "Dimensiones del recorte:" msgid "Cropping" msgstr "Recorte" msgid "Scale Dimensions:" msgstr "Dimensiones de escala:" msgid "Optimal for Source:" msgstr "Óptimo para origen" msgid "Anamorphic:" msgstr "Anamórfico:" msgid "Scaling" msgstr "Escala" msgid "Presentation Dimensions:" msgstr "Dimensiones de presentación:" msgid "Summary" msgstr "Resumen" msgid "Left Crop" msgstr "Recorte izquierdo" msgid "Top Crop" msgstr "Recorte superior" msgid "Bottom Crop" msgstr "Recorte inferior" msgid "Right Crop" msgstr "Recorte derecho" msgid "Auto Crop" msgstr "Recorte automático" msgid "Automatically crop black borders around edges of the video." msgstr "Recortar automáticamente los bordes negros alrededor del video." msgid "Loose Crop" msgstr "Recorte suelto" msgid "" "When picture settings require that the image\n" "dimensions be rounded to some multiple number\n" "of pixels, this setting will crop a few extra pixels\n" "instead of doing exact cropping and then scaling to\n" "the required multiple." msgstr "Cuando los ajustes de imagen requieren que las dimensiones \nde la imagen sean redondeados a un numero múltiplo\nde píxeles, esta opción recortará unos pocos píxeles extra\nen vez de recortar exactamente y luego escalar al\nmúltiplo requerido." msgid "width:" msgstr "ancho:" msgid "" "This is the width that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "Este es el ancho con el cual se almacenará el video.\nLas dimensiones de presentación serán distintas si la razón de aspecto de los pixeles no es 1:1." msgid "height:" msgstr "alto:" msgid "" "This is the height that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "Esta es la altura con el cual se almacenará el video.\nLas dimensiones de presentación serán distintas si la razón de aspecto de los pixeles no es 1:1." msgid "Optimal for source" msgstr "Óptimo para origen" msgid "" "If enabled, select the 'optimal' storage resolution.\n" "This will be the resolution that most closely matches the source resolution after cropping." msgstr "Si está habilitado, selecciona la resolución \"óptima\" de almacenamiento. \nEsta será la resolución que más se acerque la resolución de la fuente después de recortar." msgid "" "Anamorphic Modes:\n" "\n" "None - Force pixel aspect ratio to 1:1.\n" "Loose - Align dimensions to chosen 'Alignment' value\n" " and pick pixel aspect ratio that preserves the\n" " original display aspect ratio\n" "Strict - Keep original source dimensions and pixel\n" " aspect ratio" msgstr "Modos Anamorficos :\n\nNinguno - Fuerza la razón de aspecto de píxel 1:1.\nLibre - Alinea las dimensiones al valor de 'Alineación' elegido\ny selecciona razón de aspecto de píxel que preserva la\nrazón de aspecto de visualización original\nEstricto - Mantiene las dimensiones y razón de aspecto de píxel\nde origen" msgid "Alignment:" msgstr "Alineación:" msgid "" "Align storage dimensions to multiples of this value.\n" "\n" "This setting is only necessary for compatibility with some devices.\n" "You should use 2 unless you experience compatibility issues." msgstr "Alinear las dimensiones de almacenamiento a múltiplos de este valor. \n\nEste ajuste sólo es necesario para la compatibilidad con algunos dispositivos. \nDebes usar 2 a menos que tengas problemas de compatibilidad." msgid "Storage Geometry" msgstr "Geometría en almacenamiento" msgid "" "This is the display width. It is the result of scaling the storage " "dimensions by the pixel aspect." msgstr "Este es el ancho de presentación. Es el resultado de la ampliación de las dimensiones de almacenamiento por el aspecto de los pixeles." msgid "Pixel Aspect:" msgstr "Aspecto de pixeles:" msgid "" "Pixel aspect defines the shape of the pixels.\n" "\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "El aspecto de los pixeles define la forma de éstos.\n\nUna razón de aspecto de 1:1 significa un pixel cuadrado. Otros valores harán los pixeles rectangulares.\nLos reproductores escalarán la imagen para llegar al aspecto especificado." msgid ":" msgstr ":" msgid "" "Pixel aspect defines the shape of the pixels.\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "El aspecto de los pixeles define la forma de éstos.\nUna razón de aspecto de 1:1 significa un pixel cuadrado. Otros valores harán los pixeles rectangulares.\nLos reproductores escalarán la imagen para llegar al aspecto especificado." msgid "Keep Aspect" msgstr "Mantener aspecto" msgid "" "If enabled, the original display aspect of the source will be maintained." msgstr "Si se activa, se mantendrá el aspecto de presentación original del origen." msgid "Display Aspect:" msgstr "Aspecto de pantalla:" msgid "Display Geometry" msgstr "Geometría de pantalla" msgid "Grayscale" msgstr "En escala de grises" msgid "If enabled, filter colour components out of video." msgstr "Si está activado, se eliminarán los colores del video." msgid "Deblock:" msgstr "Deblock:" msgid "" "The deblocking filter removes a common type of compression artifact.\n" "If your source exhibits 'blockiness', this filter may help clean it up." msgstr "El filtro deblock elimina un tipo común de artefacto de compresión. \nSi el origen se ve 'pixelado', este filtro puede ayudar un poco." msgid "Denoise Filter:" msgstr "Atenuar ruido:" msgid "" "Denoise filtering reduces or removes the appearance of noise and grain.\n" "Film grain and other types of high frequency noise are difficult to compress.\n" "Using this filter on such sources can result in smaller file sizes." msgstr "Este filtro reduce o elimina la aparición del ruido y el grano. \nEl grano de la película y otros tipos de ruido de alta frecuencia son difíciles de comprimir. \nEl uso de este filtro desde dichos orígenes puede resultar en archivos más pequeños." msgid "Denoise Preset:" msgstr "Preselección de Atenuar ruido:" msgid "Denoise Tune:" msgstr "Ajustar Atenuar ruido:" msgid "" "Custom denoise filter string format\n" "\n" "SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgstr "Formato de cadena de filtro Atenuar ruido personalizado\n\nSpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgid "Detelecine:" msgstr "Eliminar telecinado:" msgid "" "This filter removes 'combing' artifacts that are the result of telecining.\n" "\n" "Telecining is a process that adjusts film framerates that are 24fps to NTSC video frame rates which are 30fps." msgstr "Este filtro elimina los artefactos de \"peine\" que pueden aparecer a causa del telecinado.\n\nEl telecinado es un proceso que ajusta la tasa de cuadros de película que son 24fps NTSC a velocidades de fotogramas de vídeo que son 30fps." msgid "" "Custom detelecine filter string format\n" "\n" "JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgstr "Formato de cadena de filtro Eliminar telecinado personalizado\n\nJunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgid "Decomb" msgstr "Eliminar \"peine\"" msgid "" "Choose decomb or deinterlace filter options.\n" "\n" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced.\n" "\n" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "Elige las opciones del filtro Eliminar \"peine\" o Desentrelazado. \n\nEl filtro Eliminar \"peine\" desentrelaza selectivamente cuadros que parecen estar entrelazados. \nEsto preservará la calidad en cuadros que no están entrelazados. \n\nEl filtro Desentrelazado clásico se aplica a todos los cuadros. \nLos cuadros que no están entrelazados sufrirán una degradación de la calidad." msgid "Deinterlace" msgstr "Desentrelazado" msgid "Decomb:" msgstr "Eliminar \"peine\"" msgid "" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced." msgstr "El filtro Eliminar \"peine\" desentrelaza selectivamente cuadros que parecen estar entrelazados. \nEsto preservará la calidad en cuadros que no están entrelazados. " msgid "" "Custom decomb filter string format\n" "\n" "Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\n" "BlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\n" "ErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgstr "Formato de cadena de filtro Eliminar \"peine\" personalizado\n\nMode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\nBlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\nErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgid "Deinterlace:" msgstr "Desentrelazado" msgid "" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "El filtro Desentrelazado clásico se aplica a todos los cuadros. \nLos cuadros que no están entrelazados sufrirán una degradación de la calidad." msgid "" "Custom deinterlace filter string format\n" "\n" "YadifMode:YadifParity:McdintMode:McdeintQp" msgstr "Formato de cadena de filtro Desentrelazado personalizado\n\nYadifMode:YadifParity:McdintMode:McdeintQp" msgid "Filters" msgstr "Filtros" msgid "Picture" msgstr "Imagen" msgid "Video Encoder:" msgstr "Codificador de video" msgid "Available video encoders." msgstr "Codificadores de video disponibles" msgid "Framerate:" msgstr "Tasa de cuadros" msgid "" "Output framerate.\n" "\n" "'Same as source' is recommended. If your source video has\n" "a variable framerate, 'Same as source' will preserve it." msgstr "Velocidad de fotograma de salida.\n\n'Igual que origen' es recomendado. Si tu video de origen tiene \nuna velocidad de fotograma variable, 'Igual que origen' lo preservará." msgid "Constant Framerate" msgstr "Velocidad de fotograma constante" msgid "Same as source" msgstr "Igual que origen" msgid "kbps" msgstr "kbps" msgid "(variable)" msgstr "(variable)" msgid "(constant)" msgstr "(constante)" msgid "Enables constant framerate output." msgstr "Activa velocidad de fotograma de salida constante." msgid "Peak Framerate (VFR)" msgstr "Pico de velocidad de fotograma (VFR)" msgid "" "Enables variable framerate output with a peak\n" "rate determined by the framerate setting.\n" "\n" "VFR is not compatible with some players." msgstr "Activa velocidad de fotograma variable de salida con un rango\npico determinado por el ajuste de velocidad de fotograma.\n\nVFR no es compatible con algunos reproductores." msgid "Variable Framerate" msgstr "Velocidad de fotograma variable" msgid "" "Enables variable framerate output.\n" "\n" "VFR is not compatible with some players." msgstr "Activa velocidad de fotograma variable de salida.\n\nVFR no es compatible con algunos reproductores." msgid "" "Set the desired quality factor.\n" "The encoder targets a certain quality.\n" "The scale used by each video encoder is different.\n" "\n" "x264's scale is logarithmic and lower values correspond to higher quality.\n" "So small decreases in value will result in progressively larger increases\n" "in the resulting file size. A value of 0 means lossless and will result\n" "in a file size that is larger than the original source, unless the source\n" "was also lossless.\n" "\n" "FFMpeg's and Theora's scale is more linear.\n" "These encoders do not have a lossless mode." msgstr "" msgid "Constant Quality:" msgstr "" msgid "Bitrate (kbps): " msgstr "" msgid "" "Set the average bitrate.\n" "\n" "The instantaneous bitrate can be much higher or lower at any point in time.\n" "But the average over a long duration will be the value set here. If you need\n" "to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings." msgstr "" msgid "2-Pass Encoding" msgstr "" msgid "" "Perform 2 Pass Encoding.\n" "\n" "The 'Bitrate' option is prerequisite. During the 1st pass, statistics about\n" "the video are collected. Then in the second pass, those statistics are used\n" "to make bitrate allocation decisions." msgstr "" msgid "Turbo First Pass" msgstr "" msgid "" "During the 1st pass of a 2 pass encode, use settings that speed things " "along." msgstr "" msgid "Use Advanced Options" msgstr "" msgid "" "Use advanced options Tab for x264 settings.\n" "\n" "Use at your own risk!" msgstr "" msgid "Preset:" msgstr "" msgid "" "Adjusts encoder settings to trade off compression efficiency against encoding speed.\n" "\n" "This establishes your default encoder settings.\n" "Tunes, profiles, levels and advanced option string will be applied to this.\n" "You should generally set this option to the slowest you can bear since slower\n" "settings will result in better quality or smaller files." msgstr "" msgid "Tune:" msgstr "" msgid "" "Tune settings to optimize for common scenarios.\n" "\n" "This can improve effeciency for particular source characteristics or set\n" "characteristics of the output file. Changes will be applied after the\n" "preset but before all other parameters." msgstr "" msgid "Fast Decode" msgstr "" msgid "" "Reduce decoder CPU usage.\n" "\n" "Set this if your device is struggling to play the output (dropped frames)." msgstr "" msgid "Zero Latency" msgstr "" msgid "" "Minimize latency between input to encoder and output of decoder.\n" "\n" "This is useful for broadcast of live streams.\n" "\n" "Since HandBrake is not suitable for live stream broadcast purposes,\n" "this setting is of little value here." msgstr "" msgid "Profile:" msgstr "" msgid "" "Sets and ensures compliance with the specified profile.\n" "\n" "Overrides all other settings." msgstr "" msgid "Level:" msgstr "" msgid "" "Sets and ensures compliance with the specified level.\n" "\n" "Overrides all other settings." msgstr "" msgid "More Settings:" msgstr "" msgid "" "Additional encoder settings.\n" "\n" "Colon separated list of encoder options." msgstr "" msgid "Video" msgstr "" msgid "Selection Behavior:" msgstr "" msgid "Remove" msgstr "" msgid "Available Languages" msgstr "" msgid "Selected Languages" msgstr "" msgid "Use only first encoder for secondary audio" msgstr "" msgid "" "Only the primary audio track will be encoded with the full encoder list.\n" "All other secondary audio output tracks will be encoded with first encoder only." msgstr "" msgid "Auto Passthru:" msgstr "" msgid "MP3" msgstr "" msgid "" "Enable this if your playback device supports MP3.\n" "This permits MP3 passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports AAC.\n" "This permits AAC passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports AC-3.\n" "This permits AC-3 passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports DTS.\n" "This permits DTS passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports DTS-HD.\n" "This permits DTS-HD passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "Passthru Fallback:" msgstr "" msgid "" "Set the audio codec to encode with when a suitable track can not be found " "for audio passthru." msgstr "" msgid "Audio Encoder Settings:" msgstr "" msgid "Each selected source track will be encoded with all selected encoders" msgstr "" msgid "Encoder" msgstr "" msgid "Bitrate/Quality" msgstr "" msgid "Mixdown" msgstr "" msgid "Samplerate" msgstr "" msgid "Gain" msgstr "" msgid "Audio Defaults" msgstr "" msgid "Add new audio settings to the list" msgstr "" msgid "Add All" msgstr "" msgid "Add all audio tracks to the list" msgstr "" msgid "Reload Defaults" msgstr "" msgid "Reload all audio settings from defaults" msgstr "" msgid "Audio List" msgstr "" msgid "Preferred Language: None" msgstr "" msgid "Add Foreign Audio Search Pass" msgstr "" msgid "" "Add \"Foreign Audio Search\" when the default audio track is your preferred language.\n" "This search pass finds short sequences of foreign audio and provides subtitles for them." msgstr "" msgid "Add subtitle track if default audio is foreign" msgstr "" msgid "" "When the default audio track is not your preferred language, add a subtitle " "track." msgstr "" msgid "Add Closed Captions when available" msgstr "" msgid "" "Closed captions are text subtitles that can be added to any container as a " "soft subtitle track (not burned)" msgstr "" msgid "Subtitle Defaults" msgstr "" msgid "Add new subtitle settings to the list" msgstr "" msgid "Add all subtitle tracks to the list" msgstr "" msgid "Reload all subtitle settings from defaults" msgstr "" msgid "Subtitle List" msgstr "" msgid "Reference Frames:" msgstr "" msgid "" "Sane values are ~1-6. The more you add, the better the compression, but the slower the encode.\n" "Cel animation tends to benefit from more reference frames a lot more than film content.\n" "\n" "Note that many hardware devices have limitations on the number of supported reference\n" "frames, so if you're encoding for a handheld or standalone player, don't touch this unless\n" "you're absolutely sure you know what you're doing!" msgstr "" msgid "Maximum B-Frames:" msgstr "" msgid "" "Sane values are ~2-5. This specifies the maximum number of sequential B-frames that the encoder can use.\n" "\n" "Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.\n" "Cel-animated source material and B-pyramid also significantly increase the usefulness of larger\n" "values.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off)." msgstr "" msgid "Pyramidal B-Frames:" msgstr "" msgid "" "B-pyramid improves compression by creating a pyramidal structure (hence the name)\n" "of B-frames, allowing B-frames to reference each other to improve compression.\n" "\n" "Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit." msgstr "" msgid "Weighted P-Frames:" msgstr "" msgid "" "Performs extra analysis to decide upon weighting parameters for each frame.\n" "\n" "This improves overall compression slightly and improves the quality of fades greatly.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires weighted P-frame\n" "prediction to be disabled. Note that some devices and players, even those that support\n" "Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is\n" "completely incompatible with it, for example." msgstr "" msgid "8x8 Transform" msgstr "" msgid "" "The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.\n" "\n" "It improves compression by at least 5% at a very small speed cost and may\n" "provide an unusually high visual quality benefit compared to its compression\n" "gain. However, it requires High Profile, which many devices may not support." msgstr "" msgid "CABAC Entropy Encoding" msgstr "" msgid "" "After the encoder has done its work, it has a bunch of data that\n" "needs to be compressed losslessly, similar to ZIP or RAR. H.264 provides\n" "two options for this: CAVLC and CABAC. CABAC decodes a lot slower but\n" "compresses significantly better (10-30%), especially at lower bitrates.\n" "\n" "If you're looking to minimize CPU requirements for video playback, disable this option.\n" "Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled." msgstr "" msgid "Encoding Features" msgstr "" msgid "Motion Est. Method:" msgstr "" msgid "" "Controls the motion estimation method.\n" "\n" "Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.\n" "A better motion search method improves compression at the cost of speed.\n" "\n" "Diamond: performs an extremely fast and simple search using a diamond pattern.\n" "Hexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n" "Uneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n" "Exhaustive: performs a \"dumb\" search of every pixel in a wide area. Significantly slower for only a small compression gain.\n" "Transformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement." msgstr "" msgid "Subpel ME & Mode:" msgstr "" msgid "" "This setting controls both subpixel-precision motion estimation and mode decision methods.\n" "\n" "Subpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n" "Mode decision is the method used to choose how to encode each block of the frame: a very important decision.\n" "SAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n" "6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n" "11 disables all early terminations in analysis.\n" "10 and 11, the most powerful and slowest options, require adaptive quantization (aq-mode > 0) and trellis 2 (always)." msgstr "" msgid "Motion Est. Range:" msgstr "" msgid "" "This is the distance x264 searches from its initial guess at the\n" "motion of a block in order to try to find its actual motion.\n" "\n" "The default is fine for most content, but extremely high motion video,\n" "especially at HD resolutions, may benefit from higher ranges, albeit at\n" "a high speed cost." msgstr "" msgid "Adaptive Direct Mode:" msgstr "" msgid "" "H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n" "\n" "Spatial, the default, is almost always better, but temporal is sometimes useful too.\n" "x264 can, at the cost of a small amount of speed (and accordingly for a small compression gain),\n" "adaptively select which is better for each particular frame." msgstr "" msgid "Adaptive B-Frames:" msgstr "" msgid "" "x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n" "\n" "Fast mode takes roughly the same amount of time no matter how many B-frames you specify.\n" "However, while fast, its decisions are often suboptimal.\n" "\n" "Optimal mode gets slower as the maximum number of B-Frames increases,\n" "but makes much more accurate decisions, especially when used with B-pyramid." msgstr "" msgid "Partitions:" msgstr "" msgid "" "Mode decision picks from a variety of options to make its decision:\n" "this option chooses what options those are.\n" "\n" "Fewer partitions to check means faster encoding, at the cost of worse\n" "decisions, since the best option might have been one that was turned off." msgstr "" msgid "Trellis:" msgstr "" msgid "" "Trellis fine-tunes the rounding of transform coefficients to\n" "squeeze out 3-5% more compression at the cost of some speed.\n" "\n" "\"Always\" uses trellis not only during the main encoding process, but also\n" "during analysis, which improves compression even more, albeit at great speed cost.\n" "\n" "Trellis costs more speed at higher bitrates and requires CABAC." msgstr "" msgid "Analysis" msgstr "" msgid "Adaptive Quantization Strength:" msgstr "" msgid "" "Adaptive quantization controls how the encoder distributes bits across the frame.\n" "\n" "Higher values take more bits away from edges and complex areas to improve areas with finer detail." msgstr "" msgid "Psychovisual Rate Distortion:" msgstr "" msgid "" "Psychovisual rate-distortion optimization takes advantage of the characteristics of human\n" "vision to dramatically improve apparent detail and sharpness.\n" "The effect can be made weaker or stronger by adjusting the strength.\n" "Being an RD algorithm, it requires mode decision to be at least \"6\"." msgstr "" msgid "Psychovisual Trellis:" msgstr "" msgid "" "Psychovisual trellis is an experimental algorithm to further\n" "improve sharpness and detail retention beyond what Psychovisual RD does.\n" "\n" "Recommended values are around 0.2, though higher values may help for very\n" "grainy video or lower bitrate encodes. Not recommended for cel animation\n" "and other sharp-edged graphics." msgstr "" msgid "Deblocking: " msgstr "" msgid "" "H.264 deblocking filter.\n" "\n" "h.264 has a built-in deblocking filter that smooths out blocking artifacts\n" "after decoding each frame. This not only improves visual quality, but also\n" "helps compression significantly. The deblocking filter takes a lot of CPU power,\n" "so if you're looking to minimize CPU requirements for video playback, disable it.\n" "\n" "The deblocking filter has two adjustable parameters, \"strength\" (Alpha) and \"threshold\" (Beta).\n" "The former controls how strong (or weak) the deblocker is, while the latter controls how many\n" "(or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking.\n" "The default is 0 (normal strength) for both parameters." msgstr "" msgid "No DCT Decimate" msgstr "" msgid "" "x264 normally zeroes out nearly-empty data blocks to save bits to\n" "be better used for some other purpose in the video. However, this can\n" "sometimes have slight negative effects on retention of subtle grain and\n" "dither.\n" "\n" "Don't touch this unless you're having banding issues or other such cases\n" "where you are having trouble keeping fine noise." msgstr "" msgid "Psychovisual" msgstr "" msgid "" "Your selected options will appear here.\n" "You can edit these and add additional options.\n" "\n" "Default values will not be shown. The defaults are:\n" "ref=3:bframes=3:b-adapt=fast:direct=spatial:\n" "b-pyramid=normal:weightp=2:me=hex:merange=16:\n" "subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\n" "deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\n" "no-fast-pskip=0:no-dct-decimate=0:cabac=1" msgstr "" msgid "Current x264 Advanced Option String" msgstr "" msgid "Advanced Video" msgstr "" msgid "Chapter Markers" msgstr "" msgid "Add chapter markers to output file." msgstr "" msgid "Chapters" msgstr "" msgid "Actors:" msgstr "" msgid "Director:" msgstr "" msgid "Release Date:" msgstr "" msgid "Comment:" msgstr "" msgid "Genre:" msgstr "" msgid "Description:" msgstr "" msgid "Plot:" msgstr "" msgid "Tags" msgstr "" msgid "Settings" msgstr "" msgid "Edit" msgstr "" msgid "Reload" msgstr "" msgid "" "Mark selected queue entry as pending.\n" "Resets the queue job to pending and ready to run again." msgstr "" msgid "Reload All" msgstr "" msgid "" "Mark all queue entries as pending.\n" "Resets all queue jobs to pending and ready to run again." msgstr "" msgid "OK" msgstr "" msgid "Select All" msgstr "" msgid "Mark all titles for adding to the queue" msgstr "" msgid "Clear All" msgstr "" msgid "Unmark all titles" msgstr "" msgid "Destination files OK. No duplicates detected." msgstr "" msgid "Select this title for adding to the queue.\n" msgstr "" msgid "Preferences" msgstr "" msgid "Automatically check for updates" msgstr "" msgid "When all encodes are complete" msgstr "" msgid "Use automatic naming (uses modified source name)" msgstr "" msgid "Auto-Name Template" msgstr "" msgid "" "Available Options: {source} {title} {chapters} {date} {time} {quality} " "{bitrate}" msgstr "" msgid "Use iPod/iTunes friendly (.m4v) file extension for MP4" msgstr "" msgid "Number of previews" msgstr "" msgid "Filter short titles (seconds)" msgstr "" msgid "Show system tray icon" msgstr "" msgid "General" msgstr "" msgid "Constant Quality fractional granularity" msgstr "" msgid "Use dvdnav (instead of libdvdread)" msgstr "" msgid "Put individual encode logs in same location as movie" msgstr "" msgid "Activity Log Verbosity Level" msgstr "" msgid "Activity Log Longevity" msgstr "" msgid "Scale down High Definition previews" msgstr "" msgid "Automatically Scan DVD when loaded" msgstr "" msgid "Scans the DVD whenever a new disc is loaded" msgstr "" msgid "Hide Advanced Video Options Tab" msgstr "" msgid "" "Use advanced video options at your own risk.\n" "We recommend that you use the controls available\n" "on the Video tab instead." msgstr "" msgid "Delete completed jobs from queue" msgstr "" msgid "" "By default, completed jobs remain in the queue and are marked as complete.\n" "Check this if you want the queue to clean itself up by deleting completed jobs." msgstr "" msgid "Allow Tweaks" msgstr "" msgid "Allow HandBrake For Dummies" msgstr "" msgid "Advanced" msgstr "" msgid "Folder Name:" msgstr "" msgid "Description" msgstr "" msgid "Preset Name:" msgstr "" msgid "Custom Picture Dimensions" msgstr "" msgid "Maximum Width:" msgstr "" msgid "Enable maximum width limit." msgstr "" msgid "" "This is the maximum width that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source width is greater.\n" "Setting this to 0 means there is no maximum width." msgstr "" msgid "Maximum Height:" msgstr "" msgid "Enable maximum height limit." msgstr "" msgid "" "This is the maximum height that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source height is greater.\n" "Setting this to 0 means there is no maximum height." msgstr "" msgid "Select preview frames." msgstr "" msgid "" "Encode and play a short sequence of video starting from the current preview " "position." msgstr "" msgid "Duration:" msgstr "" msgid "Set the duration of the live preview in seconds." msgstr "" msgid "Show Crop" msgstr "" msgid "Show Cropped area of the preview" msgstr "" msgid "Fullscreen" msgstr "" msgid "View Fullscreen Preview" msgstr "" msgid "Title Number:" msgstr "" msgid "Detected DVD devices:" msgstr "" msgid "Setting:" msgstr "" msgid "Import SRT" msgstr "" msgid "Enable settings to import an SRT subtitle file" msgstr "" msgid "Embedded Subtitle List" msgstr "" msgid "Enable settings to select embedded subtitles" msgstr "" msgid "Character Code" msgstr "" msgid "Offset (ms)" msgstr "" msgid "" "Set the language of this subtitle.\n" "This value will be used by players in subtitle menus." msgstr "" msgid "" "Set the character code used by the SRT file you are importing.\n" "\n" "SRTs come in all flavours of character sets.\n" "We translate the character set to UTF-8.\n" "The source's character code is needed in order to perform this translation." msgstr "" msgid "Select the SRT file to import." msgstr "" msgid "Srt File" msgstr "" msgid "Adjust the offset in milliseconds between video and SRT timestamps" msgstr "" msgid "Track" msgstr "" msgid "List of subtitle tracks available from your source." msgstr "" msgid "Forced Subtitles Only" msgstr "" msgid "Burn into video" msgstr "" msgid "" "Render the subtitle over the video.\n" "The subtitle will be part of the video and can not be disabled." msgstr "" msgid "Set Default Track" msgstr "" msgid "Source Track" msgstr "" msgid "List of audio tracks available from your source." msgstr "" msgid "Track Name:" msgstr "" msgid "" "Set the audio track name.\n" "\n" "Players may use this in the audio selection list." msgstr "" msgid "Mix" msgstr "" msgid "Sample Rate" msgstr "" msgid "" "Dynamic Range Compression: Adjust the dynamic range of the output audio track.\n" "\n" "For source audio that has a wide dynamic range (very loud and very soft sequences),\n" "DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder." msgstr "" msgid "Enable bitrate setting" msgstr "" msgid "Enable quality setting" msgstr "" msgid "" "Quality: For output codec's that support it, adjust the quality of " "the output." msgstr "" msgid "00.0" msgstr "" msgid "" "Audio Gain: Adjust the amplification or attenuation of the output " "audio track." msgstr "Ganancia de Audio: Ajusta la amplificación o atenuación de la pista de salida de audio" msgid "Skip This Version" msgstr "" msgid "Remind Me Later" msgstr "" msgid "A new version of HandBrake is available!" msgstr "" msgid "HandBrake xxx is now available (you have yyy)." msgstr "" msgid "Release Notes" msgstr "" msgid "First Track Matching Selected Languages" msgstr "" msgid "All Tracks Matching Selected Languages" msgstr "" msgid "Chapters:" msgstr "" msgid "Seconds:" msgstr "" msgid "Frames:" msgstr "" msgid "Do Nothing" msgstr "" msgid "Show Notification" msgstr "" msgid "Quit Handbrake" msgstr "" msgid "Put Computer To Sleep" msgstr "" msgid "Shutdown Computer" msgstr "" msgid "Week" msgstr "" msgid "Month" msgstr "" msgid "Year" msgstr "" msgid "Immortal" msgstr "" msgid "Never" msgstr "" msgid "Daily" msgstr "" msgid "Weekly" msgstr "" msgid "Monthly" msgstr "" msgid "Default" msgstr "" msgid "Fast" msgstr "" msgid "Slow" msgstr "" msgid "Slower" msgstr "" msgid "Ultralight" msgstr "" msgid "Light" msgstr "" msgid "Medium" msgstr "" msgid "Strong" msgstr "" msgid "Film" msgstr "" msgid "Grain" msgstr "" msgid "High Motion" msgstr "" msgid "Animation" msgstr "" msgid "Spatial" msgstr "" msgid "Temporal" msgstr "" msgid "Automatic" msgstr "" msgid "Optimal" msgstr "" msgid "Normal" msgstr "" msgid "Simple" msgstr "" msgid "Smart" msgstr "" msgid "Diamond" msgstr "" msgid "Hexagon" msgstr "" msgid "Uneven Multi-Hexagon" msgstr "" msgid "Exhaustive" msgstr "" msgid "Hadamard Exhaustive" msgstr "" msgid "Most" msgstr "" msgid "Some" msgstr "" msgid "All" msgstr "" msgid "Encode only" msgstr "" msgid "Always" msgstr "" msgid "(NTSC Film)" msgstr "" msgid "(PAL Film/Video)" msgstr "" msgid "(NTSC Video)" msgstr "" msgid "%d - %02dh%02dm%02ds - %s" msgstr "" msgid "%d (%05d.MPLS) - %02dh%02dm%02ds" msgstr "" msgid "%d (%05d.MPLS) - Unknown Length" msgstr "" msgid "%d - %02dh%02dm%02ds" msgstr "" msgid "%d - Unknown Length" msgstr "" msgid "No Titles" msgstr "" msgid "No Audio" msgstr "" msgid "Foreign Audio Search" msgstr "" #, c-format msgid "Chapter %2d" msgstr "" #, c-format msgid "N/A" msgstr "" #, c-format msgid "" "Invalid Deinterlace Settings:\n" "\n" "%s\n" msgstr "" #, c-format msgid "" "Invalid Detelecine Settings:\n" "\n" "%s\n" msgstr "" #, c-format msgid "" "Invalid Decomb Settings:\n" "\n" "%s\n" msgstr "" msgid "" "Theora is not supported in the MP4 container.\n" "\n" "You should choose a different video codec or container.\n" "If you continue, FFMPEG will be chosen for you." msgstr "" msgid "Continue" msgstr "" msgid "No title found.\n" msgstr "" msgid "" "Only one subtitle may be burned into the video.\n" "\n" "You should change your subtitle selections.\n" "If you continue, some subtitles will be lost." msgstr "" msgid "" "Srt file does not exist or not a regular file.\n" "\n" "You should choose a valid file.\n" "If you continue, this subtitle will be ignored." msgstr "" msgid "" "The source does not support Pass-Thru.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "%s is not supported in the %s container.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "The source audio does not support %s mixdown.\n" "\n" "You should choose a different mixdown.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "Unable to create %s.\n" "\n" "Internal error. Could not parse UI description.\n" "%s" msgstr "" msgid "Index" msgstr "" msgid "Duration" msgstr "" msgid "Title" msgstr "" msgid "Job Information" msgstr "" msgid "Track Information" msgstr "" msgid "Preset Name" msgstr "" msgid "The device or file to encode" msgstr "" msgid "The preset values to use for encoding" msgstr "" msgid "Spam a lot" msgstr "" msgid "- Transcode media formats" msgstr "" msgid "Globals" msgstr "" msgid "Presets" msgstr "" msgid "Folder" msgstr "" #, c-format msgid "%s path: (%s)" msgstr "" #, c-format msgid "%s indices: len %d" msgstr "" msgid "Type" msgstr "" msgid "Failed to find parent folder when adding child." msgstr "" msgid "Failed to find parent folder while adding child." msgstr "" #, c-format msgid "Can't map language value: (%s)" msgstr "" #, c-format msgid "Can't map value: (%s)" msgstr "" msgid "Subtitles" msgstr "" msgid "" "%s: Folder already exists.\n" "You can not replace it with a preset." msgstr "" msgid "" "%s: Preset already exists.\n" "You can not replace it with a folder." msgstr "" msgid "Import Preset" msgstr "" msgid "All (*)" msgstr "" msgid "Presets (*.plist)" msgstr "" msgid "Export Preset" msgstr "" msgid "" "Confirm deletion of %s:\n" "\n" "%s" msgstr "" msgid "folder" msgstr "" msgid "preset" msgstr "" msgid "No selection??? Perhaps unselected." msgstr "" #, c-format msgid "Gstreamer Error: %s" msgstr "" #, c-format msgid "" "Missing GStreamer plugin\n" "Audio or Video may not play as expected\n" "\n" "%s" msgstr "" msgid "Done" msgstr "" msgid "Windowed" msgstr "" msgid "Seconds" msgstr "" msgid "Frames" msgstr "" #, c-format msgid "" "%s (Title %d, %s %d through %d, 2 Video Passes) -->" " %s" msgstr "" #, c-format msgid "" "%s (Title %d, %s %d through %d) --> %s" msgstr "" #, c-format msgid "Modified Preset Based On: %s\n" msgstr "" #, c-format msgid "Preset: %s\n" msgstr "" #, c-format msgid "Format: %s Container\n" msgstr "" msgid "Container Options:" msgstr "" #, c-format msgid "%sChapter Markers" msgstr "" #, c-format msgid "%siPod 5G Support" msgstr "" #, c-format msgid "%sWeb Optimized" msgstr "" #, c-format msgid "%sLarge File Size (>4GB)" msgstr "" #, c-format msgid "Destination: %s\n" msgstr "" msgid "(Aspect Preserved)" msgstr "" msgid "(Aspect Lost)" msgstr "" msgid "(Anamorphic)" msgstr "" msgid "(Custom Anamorphic)" msgstr "" msgid "Picture: " msgstr "" #, c-format msgid "Source: %d x %d, Output %d x %d %s, Crop %d:%d:%d:%d" msgstr "" #, c-format msgid ", Display %d x %d" msgstr "" msgid "Filters:" msgstr "" #, c-format msgid "%sDetelecine" msgstr "" #, c-format msgid "%sDecomb" msgstr "" #, c-format msgid "%sDeinterlace" msgstr "" #, c-format msgid "%sDenoise Filter %s:" msgstr "" #, c-format msgid "%sDeblock: %d" msgstr "" #, c-format msgid "%sGrayscale" msgstr "" #, c-format msgid "Video: %s" msgstr "" #, c-format msgid ", Framerate: %s %s" msgstr "" #, c-format msgid ", Framerate: Peak %s (may be lower)" msgstr "" #, c-format msgid ", Framerate: %s (constant frame rate)" msgstr "" msgid "Error" msgstr "" msgid "Bitrate:" msgstr "" msgid "Bitrate" msgstr "" msgid "Quality" msgstr "" msgid "Turbo 1st Pass: On\n" msgstr "" #, c-format msgid "Video Options: Preset: %s" msgstr "" msgid " - Tune: " msgstr "" #, c-format msgid " - Profile: %s" msgstr "" #, c-format msgid " - Level: %s" msgstr "" #, c-format msgid "Advanced Options: %s\n" msgstr "" msgid "Audio: " msgstr "" #, c-format msgid "Audio Tracks: %d" msgstr "" #, c-format msgid "Bitrate: %d" msgstr "" #, c-format msgid "%s --> Encoder: %s" msgstr "" #, c-format msgid "%s --> Encoder: %s, Mixdown: %s, SampleRate: %s, %s" msgstr "" msgid "Subtitle: " msgstr "" #, c-format msgid "Subtitle Tracks: %d\n" msgstr "" msgid " (Force)" msgstr "" msgid " (Burn)" msgstr "" msgid " (Default)" msgstr "" #, c-format msgid " %s (%s), %s, Offset (ms) %d%s\n" msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "Another queued job has specified the same destination.\n" "Do you want to overwrite?" msgstr "" msgid "Overwrite" msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "This is not a valid directory." msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "Can not read or write the directory." msgstr "" #, c-format msgid "" "Destination filesystem is almost full: %uM free\n" "\n" "Encode may be incomplete if you proceed.\n" msgstr "" msgid "Proceed" msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "File already exists.\n" "Do you want to overwrite?" msgstr "" msgid "" "Duplicate destination files detected.\n" "Duplicates will not be added to the queue." msgstr "" msgid "No Title" msgstr "" msgid "" "There is another title with the same destination file name.\n" "This title will not be added to the queue unless you change\n" "the output file name.\n" msgstr "" msgid "Stop" msgstr "" msgid "Stop Encoding" msgstr "" msgid "Resume" msgstr "" msgid "Resume Encoding" msgstr "" msgid "S_top Queue" msgstr "" msgid "_Start Queue" msgstr "" msgid "_Resume Queue" msgstr "" msgid "Resume Queue" msgstr "" msgid "" "You are currently encoding. What would you like to do?\n" "\n" msgstr "" #, c-format msgid "" "You have %d unfinished job(s) in a saved queue.\n" "\n" "Would you like to reload them?" msgstr "" msgid "No" msgstr "" msgid "Yes" msgstr "" #, c-format msgid "Usage: %s infile [outfile]\n" msgstr "" #, c-format msgid "Offset: %dms" msgstr "" msgid "Burned Into Video" msgstr "" msgid "Passthrough" msgstr "" msgid "through" msgstr "" msgid "(Forced Subtitles Only)" msgstr "" msgid "(Default)" msgstr "" msgid "Error!" msgstr "" #, c-format msgid "Preferred Language: %s" msgstr "" #, c-format msgid "Add %s subtitle track if default audio is not %s" msgstr "" #, c-format msgid "Type %s" msgstr "" #, c-format msgid "Type %s value %s" msgstr "" #, c-format msgid "Type %s value %d" msgstr "" #, c-format msgid "Type %s value %" msgstr "" #, c-format msgid "Type %s value %f" msgstr "" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"%s\"" msgstr "" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"\"" msgstr "" msgid "Any" msgstr "" msgid "0: SAD, no subpel" msgstr "" msgid "4: SATD, qpel on all" msgstr "" msgid "5: SATD, multi-qpel on all" msgstr "" msgid "6: RD in I/P-frames" msgstr "" msgid "7: RD in all frames" msgstr "" msgid "8: RD refine in I/P-frames" msgstr "" msgid "9: RD refine in all frames" msgstr "" msgid "10: QPRD in all frames" msgstr "" msgid "11: No early terminations in analysis" msgstr "" msgid "Your names" msgstr "" msgid "Your emails" msgstr "" HandBrake-0.10.2/gtk/po/ru.po0000664000175200017520000034146112466165306016252 0ustar handbrakehandbrake# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Dimitris Adamakis , 2014 # VictorR2007 , 2014 # VictorR2007 , 2013-2014 msgid "" msgstr "" "Project-Id-Version: HandBrake-0.10\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-03 13:01+0300\n" "PO-Revision-Date: 2014-11-28 14:45+0000\n" "Last-Translator: VictorR2007 \n" "Language-Team: Russian (http://www.transifex.com/projects/p/handbrake/language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ru\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" msgid "Quality: " msgstr "Качество: " #, c-format msgid "Bitrate: %dkbps" msgstr "Битрейт: %dкбит/с" #, c-format msgid "Bitrate: %.4gkbps" msgstr "Битрейт: %.4gкбит/с" msgid "Passthrough" msgstr "Транзитная передача" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s\n" "Track Name: %s" msgstr "%s\nУсиление: %s\nDRC: %s\nНазвание дорожки: %s" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s" msgstr "%s\nУсиление: %s\nDRC: %s" msgid "Add" msgstr "Добавить" msgid "" "Add an audio encoder.\n" "Each selected source track will be encoded with all selected encoders." msgstr "Добавить аудио кодировщик.\nКаждая дорожка выбранного источника будет закодирована со всеми выбранными кодировщиками." msgid "Set the audio codec to encode this track with." msgstr "Установить аудио кодек для кодирования этой дорожки." msgid "Set the bitrate to encode this track with." msgstr "Установить битрейт для кодирования этой дорожки." msgid "" "Audio Quality:\n" "For encoders that support it, adjust the quality of the output." msgstr "Качество звука:\nДля выходного кодека, который поддерживают это, установить качество вывода." msgid "Set the mixdown of the output audio track." msgstr "Установить микширование каналов выходной звуковой дорожки." msgid "Set the sample rate of the output audio track." msgstr "Установить частоту дискретизации дорожки вывода звука." msgid "" "Audio Gain:\n" "Adjust the amplification or attenuation of the output audio track." msgstr "Усиление звука: \nРегулирует усиление или ослабление звуковой дорожки вывода." msgid "0dB" msgstr "0дБ" msgid "%ddB" msgstr "%dдБ" msgid "%.4gkHz" msgstr "%.4gкГц" msgid "%d - %s (%.4gkHz)" msgstr "%d - %s (%.4gкГц)" msgid "" "Dynamic Range Compression:\n" "Adjust the dynamic range of the output audio track.\n" "For source audio that has a wide dynamic range,\n" "very loud and very soft sequences, DRC allows you\n" "to 'compress' the range by making loud sounds\n" "softer and soft sounds louder.\n" msgstr "Динамическое сжатие диапазона:\nУстанавливает динамический диапазон для звуковой дорожки вывода.\nДля источника звука, который имеет широкий динамический диапазон.\nОчень громкие и очень мягкие последовательности, DRC позволяет сжимать\nдиапазон, делая громкие разделы мягче и мягкие разделы громче.\n" msgid "Remove this audio encoder" msgstr "Удалить этот кодировщик звука" msgid "Closing HandBrake will terminate encoding.\n" msgstr "Закрытие HandBrake прекратит кодирование.\n" msgid "No Title Found" msgstr "Название не найдено" msgid "none" msgstr "нет" msgid "Not Selected" msgstr "Не выбрано" msgid "zerolatency x264 tune selected, forcing constant framerate" msgstr "Выбраны настройки x264 без задержки, принудительная частота кадров" msgid "Scanning ..." msgstr "Сканирование…" msgid "Stop Scan" msgstr "Остановить сканирование" msgid "On" msgstr "Вкл" msgid "Strict" msgstr "Строгое" msgid "Loose" msgstr "Произвольно" msgid "Custom" msgstr "Другое" msgid "Unknown" msgstr "Неизвестно" msgid "auto" msgstr "авто" #, c-format msgid "" "%s\n" "\n" "%s in %d seconds ..." msgstr "%s\n\n%s в %d секунды ..." #, c-format msgid "%sYour movie will be lost if you don't continue encoding." msgstr "%sВаш фильм будет потерян, если вы не продолжать кодирование." msgid "Cancel Current and Stop" msgstr "Отменить текущий и остановить" msgid "Cancel Current, Start Next" msgstr "Отменить текущий, начать следующий" msgid "Finish Current, then Stop" msgstr "Закончить текущий, затем остановить" msgid "Continue Encoding" msgstr "Продолжить кодирование" msgid "Custom " msgstr "Другое " msgid "Modified " msgstr "Изменён " #, c-format msgid "Handbrake Version: %s (%d)\n" msgstr "Версия Handbrake: %s (%d)\n" #, c-format msgid "%d encode(s) pending" msgstr "В ожидании кодирования %d" #, c-format msgid "job %d of %d, " msgstr "задание %d из %d, " #, c-format msgid "pass %d (subtitle scan) of %d, " msgstr "проход %d (сканирование субтитров) из %d, " #, c-format msgid "pass %d of %d, " msgstr "проход %d из %d, " #, c-format msgid "Encoding: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" msgstr "Кодирование: %s%s%.2f %% (%.2f fps, средний %.2f fps, время до окончания %02d час %02d мин %02d сек)." #, c-format msgid "Encoding: %s%s%.2f %% (ETA %02dh%02dm%02ds)" msgstr "Кодирование: %s%s%.2f %% (время до окончания %02d час %02d мин %02d сек)." #, c-format msgid "Encoding: %s%s%.2f %%" msgstr "Кодирование: %s%s%.2f %%." msgid "Searching for start time, " msgstr "Поиск времени начала, " msgid "Scanning..." msgstr "Сканирование…" #, c-format msgid "Scanning title %d of %d..." msgstr "Сканирование фильма %d из %d..." #, c-format msgid "Scanning title %d of %d preview %d..." msgstr "Сканирование фильма %d из %d просмотр %d..." msgid "Source" msgstr "Источник" msgid "Choose Video Source" msgstr "Выбрать исходное видео" msgid "Paused" msgstr "Приостановлено" msgid "Encode Done!" msgstr "Кодирование завершено!" msgid "Encode Canceled." msgstr "Кодирование отменено." msgid "Encode Failed." msgstr "Ошибка кодирования." msgid "Muxing: This may take a while..." msgstr "Мультиплексирование: Это может занять некоторое время..." msgid "Scan this DVD source" msgstr "Сканировать этот DVD" msgid "AutoScan" msgstr "Автосканирование" msgid "Suspend" msgstr "Ждущий режим" #, c-format msgid "Suspend failed: %s" msgstr "Не удалось перейти в ждущий режим: %s" msgid "Suspend failed" msgstr "Не удалось перейти в ждущий режим" msgid "Shutdown" msgstr "Выключить компьютер" #, c-format msgid "Shutdown failed: %s" msgstr "Ошибка выключения компьютера: %s" msgid "Shutdown failed" msgstr "Ошибка выключения компьютера" msgid "Encoding" msgstr "Кодирование" #, c-format msgid "press %d %d" msgstr "проход %d %d, " #, c-format msgid "" "Invalid Settings:\n" "%s" msgstr "Недопустимые настройки:\n%s" msgid "Cancel" msgstr "Отмена" #, c-format msgid "%s: %.4g (Warning: lossless)" msgstr "%s: %.4g (Предупреждение: без потерь)" #, c-format msgid "HandBrake %s/%s is now available (you have %s/%d)." msgstr "Доступна новая версия HandBrake %s/%s (у вас версия %s/%d)." msgid "Encode Complete" msgstr "Кодирование завершено" msgid "Put down that cocktail, Your HandBrake queue is done!" msgstr "HandBrake завершил кодировать этот фильм!" msgid "Your encode is complete." msgstr "Кодирование завершено." msgid "Shutting down the computer" msgstr "Выключение компьютера" msgid "Putting computer to sleep" msgstr "Переход в спящий режим" msgid "Quiting Handbrake" msgstr "Очередь" msgid "Bottom" msgstr "Снизу" #, c-format msgid "open failed: %s\n" msgstr "не удалось открыть: %s\n" msgid "No key for dictionary item" msgstr "Нет ключа для элемента словаря" msgid "Invalid container type. This shouldn't happen" msgstr "Неправильный тип контейнера. Этого не должно быть" #, c-format msgid "%s:missing a requried attribute" msgstr "%s:отсутствует нужный атрибут" #, c-format msgid "" "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" msgstr "Используется: %s [-I ] \nСводка:\n Создает плейлист ресурса из списка ресурсов\nOptions:\n I - Включить путь для поиска файлов\n Входной файл ресурсов\n Выходной плейлист файлов ресурсов\n" msgid "language" msgstr "Язык" msgid "Language" msgstr "Язык" msgid "" "The language this text is in, as an ISO code. Pango can use this as a hint " "when rendering the text. If you don't understand this parameter, you " "probably don't need it" msgstr "Язык, на котором написан текст, в виде ISO-кода. Pango может использовать это как хинт при отрисовке текста. Если вы не понимаете, что это за параметр, то он вам, скорее всего, не нужен." msgid "" "The preferred place to ellipsize the string, if the cell renderer does not " "have enough room to display the entire string" msgstr "Желаемое место эллиптизации строки, если механизму отображения строки в ячейке не хватает места для отображения всей строки." msgid "" "How to break the string into multiple lines, if the cell renderer does not " "have enough room to display the entire string" msgstr "Как разделить строку на несколько строк, если механизму отображения строки не хватает места для отображения всей строки" msgid "" "Render the subtitle over the video.\n" "\n" "The subtitle will be part of the video and can not be disabled." msgstr "Отображение субтитров на видео.\n\nСубтитры будут на части видео и не могут быть отключены." msgid "Burned In" msgstr "Встроенные" msgid "" "Set the default output subtitle track.\n" "\n" "Most players will automatically display this\n" "subtitle track whenever the video is played.\n" "\n" "This is useful for creating a \"forced\" track\n" "in your output." msgstr "Устанавливает по умолчанию дорожку субтитров вывода.\n\nБольшинство проигрывателей будет автоматически отображать эту\nдорожку субтитров при воспроизводится видео.\n\nЭто эффективно для создания \"принудительной\" дорожки\nв выводе." msgid "Default" msgstr "По умолчанию" msgid "" "Use only subtitles that have been flagged\n" "as forced in the source subtitle track\n" "\n" "\"Forced\" subtitles are usually used to show\n" "subtitles during scenes where someone is speaking\n" "a foreign language." msgstr "Использует только те субтитры, которые были помечены\nкак принудительные, в треке субтитров источника\n\n\"Принудительные\" субтитры, как правило, используется, чтобы показать\nсубтитры во время сцен, где кто-то говорит\nна иностранном языке." msgid "Forced Only" msgstr "Только принудительные" msgid "" "Add (or subtract) an offset (in milliseconds)\n" "to the start of the SRT subtitle track.\n" "\n" "Often, the start of an external SRT file\n" "does not coincide with the start of the video.\n" "This setting allows you to synchronize the files." msgstr "Добавить (или вычесть) смещение (в миллисекундах)\nс начала дорожки SRT субтитров.\n\nЧасто, начало внешнего SRT файла\nне совпадает с началом видео.\nЭтот параметр позволяет синхронизировать файлы." msgid "SRT Offset" msgstr "Смещение SRT" msgid "Off" msgstr "Выкл" msgid "" "The source subtitle track\n" "\n" "You can choose any of the subtitles\n" "recognized in your source file.\n" "\n" "In addition, there is a special track option\n" "\"Foreign Audio Search\". This option will add\n" "an extra pass to the encode that searches for\n" "subtitles that may correspond to a foreign\n" "language scene. This option is best used in\n" "conjunction with the \"Forced\" option." msgstr "Дорожка субтитров источника\n\nВы можете выбрать любые из субтитров,\nраспознанных в исходном файле.\n\nКроме того, есть специальный вариант дорожки\n\"Поиск иностранного языка\". Эта опция добавит\nдополнительный проход в кодирование, который ищет\nсубтитры, которые могут соответствовать сцене с\nиностранным языком.Эту опцию лучше всего использовать в\nсочетании с опцией «Принудительные»." msgid "Track" msgstr "Дорожка" msgid "About HandBrake" msgstr "О программе" msgid "" "Copyright © 2008 - 2013 John Stebbins\n" "Copyright © 2004 - 2013, HandBrake Devs" msgstr "Copyright © 2008 - 2013 John Stebbins\nCopyright © 2004 - 2013, HandBrake Devs" msgid "" "HandBrake is a GPL-licensed, multiplatform, multithreaded video transcoder." msgstr "HandBrake, это GPL-лицензированный, мультиплатформенный, многопоточный видео транскодер." msgid "http://handbrake.fr" msgstr "http://handbrake.fr" msgid "" "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n" "\n" "HandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgstr "HandBrake― свободное программное обеспечение. Вы можете распространять или изменять его при условиях соответствия GNU General Public License опубликованной Free Software Foundation; либо версии 2 лицензии, либо (на ваше усмотрение) любой более поздней версии.\n\nHandBrake распространяется в надежде на то, что приложение будет полезно, но БЕЗ ВСЯКИХ ГАРАНТИЙ; не гарантируется даже ПРИГОДНОСТЬ или СООТВЕТСТВИЕ КАКИМ-ЛИБО ТРЕБОВАНИЯМ. Для получения дополнительной информации ознакомьтесь с GNU General Public License.\n\nВы должны получить копию GNU General Public License вместе с этой программой. Если этого не произошло, напишите to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA." msgid "_Minimize/Maximize" msgstr "_Свернуть/распахнуть" msgid "_Pause Queue" msgstr "_Приостановить очередь" msgid "_Quit" msgstr "_Выход" msgid "_About" msgstr "_О программе" msgid "HandBrake" msgstr "HandBrake" msgid "_File" msgstr "_Файл" msgid "_Source" msgstr "_Источник" msgid "Single _Title" msgstr "Один _файл" msgid "_Destination" msgstr "_Файл выхода" msgid "_Preferences" msgstr "_Параметры" msgid "_Queue" msgstr "_Очередь" msgid "_Add" msgstr "_Добавить" msgid "Add _Multiple" msgstr "Добавить _несколько" msgid "_Start" msgstr "_Старт" msgid "_Pause" msgstr "_Пауза" msgid "_View" msgstr "_Вид" msgid "HandBrake For _Dumbies" msgstr "HandBrake для чайников" msgid "_Show Presets" msgstr "_Показать предустановки" msgid "_Preview" msgstr "_Просмотр" msgid "_Activity Window" msgstr "_Окно процесса обработки" msgid "Show _Queue" msgstr "Показать _очередь" msgid "_Presets" msgstr "_Предустановки" msgid "_Save" msgstr "_Сохранить" msgid "_Delete" msgstr "_Удалить" msgid "_Make Default" msgstr "_Сделать по умолчанию" msgid "_New Folder" msgstr "_Новая папка" msgid "_Export" msgstr "_Экспорт" msgid "_Import" msgstr "_Импорт" msgid "_Update Built-in Presets" msgstr "_Обновить предустановки" msgid "_Help" msgstr "_Помощь" msgid "_Guide" msgstr "_Руководство" msgid "Start Encoding" msgstr "Начать кодирование" msgid "Start" msgstr "Старт" msgid "Pause Encoding" msgstr "Приостановить кодирование" msgid "Pause" msgstr "Пауза" msgid "Add to Queue" msgstr "Добавить в очередь" msgid "Enqueue" msgstr "Поставить в очередь" msgid "Show Queue" msgstr "Показать очередь" msgid "Queue" msgstr "Очередь" msgid "" "Open Picture Settings and Preview window.\n" "Here you can adjust cropping, resolution, aspect ratio, and filters." msgstr "Открыть настройки изображения и окно просмотра.\nЗдесь вы можете отрегулировать обрезку, разрешение, соотношение сторон и фильтры." msgid "Preview" msgstr "Предварительный просмотр" msgid "Show Activity Window" msgstr "Показать окно процесса обработки" msgid "Activity" msgstr "Процесс" msgid "Source:" msgstr "Источник:" msgid "None" msgstr "Нет" msgid "Title:" msgstr "Название:" msgid "" "Set the title to encode.\n" "By default the longest title is chosen.\n" "This is often the feature title of a DVD." msgstr "Установить название для кодирования.\nПо умолчанию выбрано длинное название.\nЧасто это название функций DVD." msgid "Angle:" msgstr "Угол:" msgid "For multi-angle DVD's, select the desired angle to encode." msgstr "Для нескольких ракурсов DVD, выберите нужный ракурс для кодирования." msgid "Reset All Titles" msgstr "Сбросить все" msgid "Apply current settings to all titles" msgstr "Применить текущие настройки ко всем названиям." msgid "Range of title to encode. Can be chapters, seconds, or frames." msgstr "Диапазон заголовков для кодирования. Могут быть главы, секунды или кадры." msgid "Set the first chapter to encode." msgstr "Установить первую главу для кодирования." msgid "Set the last chapter to encode." msgstr "Установить последнюю главу для кодирования." msgid "Duration:" msgstr "Продолжительность:" msgid "Destination" msgstr "Назначение" msgid "File:" msgstr "Файл:" msgid "Destination filename for your encode." msgstr "Название папки для перекодированных файлов." msgid "Destination directory for your encode." msgstr "Папка назначения для перекодированных файлов." msgid "Destination Directory" msgstr "Каталог назначения" msgid "Format:" msgstr "Формат:" msgid "Format to mux encoded tracks to." msgstr "Формат мультиплексора кодирования треков." msgid "iPod 5G Support" msgstr "Поддержка iPod 5G" msgid "Add iPod Atom needed by some older iPods." msgstr "Добавить iPod Atom, необходимый некоторым старым плеерам." msgid "Web optimized" msgstr "Оптимизация для интернет-трансляции" msgid "" "Optimize the layout of the MP4 file for progressive download.\n" "This allows a player to initiate playback before downloading the entire file." msgstr "Оптимизирует структуру файлов mp4 для последовательной загрузки.\nЭто позволяет проигрывателю начать воспроизведение до загрузки всего файла." msgid "Large file (>4GB)" msgstr "Большой файл (>4GB)" msgid "" "Allow 64 bit MP4 file which can be over 4GB.\n" "\n" "Caution: This option may break device compatibility." msgstr "Разрешить 64 битный MP4 файл, который может быть более 4 ГБ.\n\nВнимание: Эта опция может сломать совместимость устройств." msgid "Presets List" msgstr "Список предустановок" msgid "Source Codec:" msgstr "Кодек источника:" msgid "Dimensions:" msgstr "Размер изображения:" msgid "Aspect: " msgstr "Соотношение сторон: " msgid "Frame Rate:" msgstr "Частота кадров:" msgid "Source Picture Parameters" msgstr "Параметры изображения источника" msgid "Autocrop:" msgstr "Автокадрирование:" msgid "Crop:" msgstr "Кадрирование:" msgid "Crop Dimensions:" msgstr "Размер после обрезки:" msgid "Cropping" msgstr "Кадрирование" msgid "Scale Dimensions:" msgstr "Масштаб:" msgid "Optimal for Source:" msgstr "Оптимально для источника:" msgid "Anamorphic:" msgstr "Анаморфотный:" msgid "Scaling" msgstr "Масштабирование" msgid "Presentation Dimensions:" msgstr "Размеры презентации:" msgid "Summary" msgstr "Описание" msgid "Left Crop" msgstr "Обрезка слева" msgid "Top Crop" msgstr "Обрезка сверху" msgid "Bottom Crop" msgstr "Обрезка снизу" msgid "Right Crop" msgstr "Обрезка справа" msgid "Auto Crop" msgstr "Автообрезка черных полос" msgid "Automatically crop black borders around edges of the video." msgstr "Автоматически обрезает черные полосы по краям видео." msgid "Loose Crop" msgstr "Произвольное кадрирование" msgid "" "When picture settings require that the image\n" "dimensions be rounded to some multiple number\n" "of pixels, this setting will crop a few extra pixels\n" "instead of doing exact cropping and then scaling to\n" "the required multiple." msgstr "Когда настройки изображения требуют, чтобы размеры\nизображения округлялись до некоторого многократного числа\nпикселей, эта установка будет обрезать несколько дополнительных\nпикселей, а не делать точную обрезку и затем масштабирование\nдо требуемого числа пикселей." msgid "width:" msgstr "Ширина:" msgid "" "This is the width that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "Это ширина, в которой будет храниться видео.\nФактические размеры дисплей будут отличаться, если соотношение сторон пиксела не равно 1:1." msgid "height:" msgstr "Высота:" msgid "" "This is the height that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "Это высота, в которой будет храниться видео.\nФактические размеры дисплей будут отличаться, если соотношение сторон пиксела не равно 1:1." msgid "Optimal for source" msgstr "Оптимально для источника" msgid "" "If enabled, select the 'optimal' storage resolution.\n" "This will be the resolution that most closely matches the source resolution after cropping." msgstr "Если включено, выберет оптимальное разрешение для сохранения.\nЭто будет разрешение, наиболее точно соответствующее исходному разрешению после обрезки." msgid "" "Anamorphic Modes:\n" "\n" "None - Force pixel aspect ratio to 1:1.\n" "Loose - Align dimensions to chosen 'Alignment' value\n" " and pick pixel aspect ratio that preserves the\n" " original display aspect ratio\n" "Strict - Keep original source dimensions and pixel\n" " aspect ratio" msgstr "Анаморфотные режимы:\n\nНет - Принудительное соотношение сторон 1:1.\nПроизвольно - Выровнять размеры выбрав 'Выравнивание' \n чтобы значение и соотношение сторон соответствовало \n соотношению сторон оригинала\nСтрого - Держать исходные размеры и исходное \n соотношение сторон" msgid "Alignment:" msgstr "Выравнивание:" msgid "" "Align storage dimensions to multiples of this value.\n" "\n" "This setting is only necessary for compatibility with some devices.\n" "You should use 2 unless you experience compatibility issues." msgstr "Выравнивание размеров сохранения в несколько раз этого значения.\n\nЭтот параметр необходим только для совместимости с некоторыми устройствами.\nСледует использовать 2, если вы не испытываете проблем с совместимостью." msgid "Storage Geometry" msgstr "Геометрия сохранения" msgid "" "This is the display width. It is the result of scaling the storage " "dimensions by the pixel aspect." msgstr "Это ширина дисплея. Это является результатом изменения размеров сохранения пропорций пикселей." msgid "Pixel Aspect:" msgstr "Пропорции в пикселах:" msgid "" "Pixel aspect defines the shape of the pixels.\n" "\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "Соотношение сторон пикселей определяет форму пикселей.\n\nСоотношение 1:1 определяет квадратный пиксель. Другие значения определяют прямоугольные формы.\nПроигрыватели будут масштабировать изображение, чтобы достичь указанного соотношения." msgid ":" msgstr ":" msgid "" "Pixel aspect defines the shape of the pixels.\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "Соотношение сторон пикселей определяет форму пикселей.\nСоотношение 1:1 определяет квадратный пиксель. Другие значения определяют прямоугольные формы.\nПроигрыватели будут масштабировать изображение, чтобы достичь указанного соотношения." msgid "Keep Aspect" msgstr "Сохранять соотношение сторон" msgid "" "If enabled, the original display aspect of the source will be maintained." msgstr "Если включено, оригинальное соотношение сторон источника будут сохранено." msgid "Display Aspect:" msgstr "Соотношение дисплея:" msgid "Display Geometry" msgstr "Геометрия экрана" msgid "Grayscale" msgstr "Чёрно-белое" msgid "If enabled, filter colour components out of video." msgstr "Если включено, фильтр вывода оттенков цвета в видео." msgid "Deblock:" msgstr "Подавление блоков:" msgid "" "The deblocking filter removes a common type of compression artifact.\n" "If your source exhibits 'blockiness', this filter may help clean it up." msgstr "Фильтр для устранения \"блочности\" устраняет распространенный тип артефактов сжатия.\nЕсли в источнике существует \"блочность\", этот фильтр может помочь в ее очистке." msgid "Denoise Filter:" msgstr "Фильтр шумоподавления:" msgid "" "Denoise filtering reduces or removes the appearance of noise and grain.\n" "Film grain and other types of high frequency noise are difficult to compress.\n" "Using this filter on such sources can result in smaller file sizes." msgstr "Фильтр шумоподавления уменьшает или устраняет появление шума и зернистости.\nЗернистость фильма и другие виды высокочастотного шума сложно сжимать.\nИспользование этого фильтра на таких источниках может привести к уменьшению размеров файлов." msgid "Denoise Preset:" msgstr "Предустановки шумоподавления:" msgid "Denoise Tune:" msgstr "Настройка шумоподавления:" msgid "" "Custom denoise filter string format\n" "\n" "SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgstr "Пользовательский фильтр удаления шумов\n\nSpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgid "Detelecine:" msgstr "Детелесин:" msgid "" "This filter removes 'combing' artifacts that are the result of telecining.\n" "\n" "Telecining is a process that adjusts film framerates that are 24fps to NTSC video frame rates which are 30fps." msgstr "Этот фильтр устраняет артефакты 'гребенки', которые являются результатом телесина.\n\nТелесин, это процесс, который регулирует частоту кадров фильма 24fps в NTSC, в видео с частотой кадров 30fps." msgid "" "Custom detelecine filter string format\n" "\n" "JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgstr "Пользовательский фильтр детелесина\n\nJunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgid "Decomb" msgstr "Устранение гребенки" msgid "" "Choose decomb or deinterlace filter options.\n" "\n" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced.\n" "\n" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "Выберите фильтр \"Устранение гребенки\" или \"Деинтерлейсинг\".\n\nФильтр устранения гребенки избирательно убирает чересстрочность в кадрах, которые могут оказаться чересстрочными.\nЭто позволит сохранить качество кадров с нечересстрочной разверткой.\n\nКлассический фильтр деинтерлейсинга применяется ко всем кадрам.\nКадры, которые не чересстрочные, пострадают от некоторого ухудшение качества." msgid "Deinterlace" msgstr "Деинтерлейсинг" msgid "Decomb:" msgstr "Устранение гребенки:" msgid "" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced." msgstr "Фильтр устранения гребенки избирательно убирает чересстрочность кадров, которые могут оказаться чересстрочными.\nЭто позволит сохранить качество кадров, которые с нечересстрочной разверткой." msgid "" "Custom decomb filter string format\n" "\n" "Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\n" "BlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\n" "ErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgstr "Пользовательский фильтр устранения гребенки\n\nMode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\nBlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\nErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgid "Deinterlace:" msgstr "Деинтерлейсинг:" msgid "" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "Классический фильтр деинтерлейсинга применяется ко всем кадрам.\nКадры, которые не чересстрочные, пострадают от некоторого ухудшение качества." msgid "" "Custom deinterlace filter string format\n" "\n" "YadifMode:YadifParity:McdintMode:McdeintQp" msgstr "Пользовательский фильтр деинтерлейсинга\n\nYadifMode:YadifParity:McdintMode:McdeintQp" msgid "Filters" msgstr "Фильтры" msgid "Picture" msgstr "Изображение" msgid "Video Encoder:" msgstr "Кодировщик видео:" msgid "Available video encoders." msgstr "Доступные кодировщики видео." msgid "Framerate:" msgstr "Частота кадров:" msgid "" "Output framerate.\n" "\n" "'Same as source' is recommended. If your source video has\n" "a variable framerate, 'Same as source' will preserve it." msgstr "Частота кадров на выходе.\n\nРекомендуется 'Как в источнике'. Если исходное видео имеет\nпеременную частоту кадров, выбор 'Как в источнике' сохранит ее." msgid "Constant Framerate" msgstr "Постоянная частота кадров" msgid "Same as source" msgstr "Как в источнике" msgid "kbps" msgstr "кбит/с" msgid "(variable)" msgstr "(переменная)" msgid "(constant)" msgstr "(постоянная)" msgid "Enables constant framerate output." msgstr "Включает постоянную частоту кадров вывода." msgid "Peak Framerate (VFR)" msgstr "Пик частоты кадров (VFR)" msgid "" "Enables variable framerate output with a peak\n" "rate determined by the framerate setting.\n" "\n" "VFR is not compatible with some players." msgstr "Включает переменную частоту кадров на выходе с пиковой\nскоростью, определенной настройкой частоты кадров.\n\nVFR не совместим с некоторыми проигрывателями." msgid "Variable Framerate" msgstr "Переменная частота кадров" msgid "" "Enables variable framerate output.\n" "\n" "VFR is not compatible with some players." msgstr "Включает переменную частоту кадров.\n\nVFR не совместим с некоторыми проигрывателями." msgid "" "Set the desired quality factor.\n" "The encoder targets a certain quality.\n" "The scale used by each video encoder is different.\n" "\n" "x264's scale is logarithmic and lower values correspond to higher quality.\n" "So small decreases in value will result in progressively larger increases\n" "in the resulting file size. A value of 0 means lossless and will result\n" "in a file size that is larger than the original source, unless the source\n" "was also lossless.\n" "\n" "FFMpeg's and Theora's scale is more linear.\n" "These encoders do not have a lossless mode." msgstr "Установите требуемый коэффициент качества.\nКонкретное качество для кодировщика целевого файла.\nШкала, используемая каждым видео кодировщиком, различается.\n\nШкала X264 является логарифмической и более низкие значения соответствуют более\nвысокому качеству. Так, небольшое снижение значения приведет к прогрессивно более значительному\nувеличению размера файла. Значение 0 означает без потерь и приведет к размеру файла не больше, чем\nу источника, если только источник также без потерь.\n\nУ FFMpeg и Theora шкала является более линейной.\nЭти кодировщики не имеет режима без потерь." msgid "Constant Quality:" msgstr "Постоянное качество:" msgid "Bitrate (kbps): " msgstr "Битрейт (кбит/с): " msgid "" "Set the average bitrate.\n" "\n" "The instantaneous bitrate can be much higher or lower at any point in time.\n" "But the average over a long duration will be the value set here. If you need\n" "to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings." msgstr "Установите средний битрейт.\n\nКратковременный битрейт может быть гораздо выше или ниже в любой момент времени.\nНо средний, на протяжении длительного срока, будет на установленном здесь значении.\nЕсли нужно ограничить кратковременный битрейт, смотрите настройки x264 vbv-bufsize и vbv-maxrate." msgid "2-Pass Encoding" msgstr "Кодирование в 2 прохода" msgid "" "Perform 2 Pass Encoding.\n" "\n" "The 'Bitrate' option is prerequisite. During the 1st pass, statistics about\n" "the video are collected. Then in the second pass, those statistics are used\n" "to make bitrate allocation decisions." msgstr "Производит кодирование в 2 прохода.\n\nОпция 'Битрейт' является обязательным условием. В 1-ый проход собирается\nстатистика о видео. Тогда на втором проходе используются эти статистические\nданные в решении распределения битрейта." msgid "Turbo First Pass" msgstr "Первый проход быстро" msgid "" "During the 1st pass of a 2 pass encode, use settings that speed things " "along." msgstr "Во время 1-го проход при 2-х проходном кодировании используйте настройки скоростного прохода." msgid "Use Advanced Options" msgstr "Использовать дополнительные параметры" msgid "" "Use advanced options Tab for x264 settings.\n" "\n" "Use at your own risk!" msgstr "Используйте вкладку дополнительных настроек для настройки x264. \n\n Используйте на свой страх и риск!" msgid "Preset:" msgstr "Предустановка:" msgid "" "Adjusts encoder settings to trade off compression efficiency against encoding speed.\n" "\n" "This establishes your default encoder settings.\n" "Tunes, profiles, levels and advanced option string will be applied to this.\n" "You should generally set this option to the slowest you can bear since slower\n" "settings will result in better quality or smaller files." msgstr "Регулирует настройки кодировщика, балансируя между эффективностью сжатия и скоростью кодирования.\n\nЭто определяют настройки кодировщика по умолчанию.\nРегулировка, профили, уровни и дополнительные параметры строки, будут применять это.\nКак правило, нужно установить этот параметр на самый медленный, Медленная настройка\nприведёт к улучшению качества или несколько меньшему размеру файла." msgid "Tune:" msgstr "Настройки:" msgid "" "Tune settings to optimize for common scenarios.\n" "\n" "This can improve effeciency for particular source characteristics or set\n" "characteristics of the output file. Changes will be applied after the\n" "preset but before all other parameters." msgstr "Оптимизированные настройки параметров для выполнения типовых сценариев.\n\nЭто может улучшить эффективность для конкретных характеристик исходного или заданных\nхарактеристик выходного файла. Изменения будут применены после предустановки, но\nдо всех остальных параметров." msgid "Fast Decode" msgstr "Быстрое кодирование" msgid "" "Reduce decoder CPU usage.\n" "\n" "Set this if your device is struggling to play the output (dropped frames)." msgstr "Уменьшает использование CPU при декодировании.\n\nУстановите ее, если ваше устройство имеет затруднение с воспроизведением на выходе (пропускает кадры)." msgid "Zero Latency" msgstr "Нулевая задержка" msgid "" "Minimize latency between input to encoder and output of decoder.\n" "\n" "This is useful for broadcast of live streams.\n" "\n" "Since HandBrake is not suitable for live stream broadcast purposes,\n" "this setting is of little value here." msgstr "Минимизация задержки между входом в кодировщик и выходом декодера.\n\nЭто полезно для трансляции потокового вещания.\n\nПоскольку HandBrake не подходит трансляции потокового вещания,\nэтот параметр не имеет здесь большого значения." msgid "Profile:" msgstr "Профиль:" msgid "" "Sets and ensures compliance with the specified profile.\n" "\n" "Overrides all other settings." msgstr "Устанавливает и обеспечивает соблюдение указанного профиля.\n\nПодавляет все другие настройки." msgid "Level:" msgstr "Уровень:" msgid "" "Sets and ensures compliance with the specified level.\n" "\n" "Overrides all other settings." msgstr "Устанавливает и обеспечивает соблюдение указанного уровня.\n\nПодавляет все другие настройки." msgid "More Settings:" msgstr "Дополнительно:" msgid "" "Additional encoder settings.\n" "\n" "Colon separated list of encoder options." msgstr "Дополнительные настройки кодирования.\n\nСписок вариантов опций разделять двоеточиями." msgid "Video" msgstr "Видео" msgid "Selection Behavior:" msgstr "Выбор поведения:" msgid "Remove" msgstr "Удалить" msgid "Available Languages" msgstr "Доступные языки" msgid "Selected Languages" msgstr "Выбранные языки" msgid "Use only first encoder for secondary audio" msgstr "Использовать только первый кодировщик для вторичного аудиосигнала" msgid "" "Only the primary audio track will be encoded with the full encoder list.\n" "All other secondary audio output tracks will be encoded with first encoder only." msgstr "Только основная звуковая дорожка будет закодирован полным списком кодировщиков.\nВсе другие, вторичные аудиодорожки, будут закодированы только первым кодировщиком." msgid "Auto Passthru:" msgstr "Автопроходные без перекодирования:" msgid "MP3" msgstr "MP3" msgid "" "Enable this if your playback device supports MP3.\n" "This permits MP3 passthru to be selected when automatic passthru selection is enabled." msgstr "Включите это, если ваше устройство воспроизведения поддерживает MP3.\nЭто позволяет при необходимости вызвать MP3, если включен автоматический выбор." msgid "" "Enable this if your playback device supports AAC.\n" "This permits AAC passthru to be selected when automatic passthru selection is enabled." msgstr "Включите это, если ваше устройство воспроизведения поддерживает AAC.\nЭто позволяет при необходимости вызвать AAC, если включен автоматический выбор." msgid "" "Enable this if your playback device supports AC-3.\n" "This permits AC-3 passthru to be selected when automatic passthru selection is enabled." msgstr "Включите это, если ваше устройство воспроизведения поддерживает AC-3.\nЭто позволяет при необходимости вызвать AC-3, если включен автоматический выбор." msgid "" "Enable this if your playback device supports DTS.\n" "This permits DTS passthru to be selected when automatic passthru selection is enabled." msgstr "Включите это, если ваше устройство воспроизведения поддерживает DTS.\nЭто позволяет при необходимости вызвать DTS, если включен автоматический выбор." msgid "" "Enable this if your playback device supports DTS-HD.\n" "This permits DTS-HD passthru to be selected when automatic passthru selection is enabled." msgstr "Включите это, если ваше устройство воспроизведения поддерживает DTS-HD.\nЭто позволяет при необходимости вызвать DTS-HD, если включен автоматический выбор." msgid "Passthru Fallback:" msgstr "Резервный без перекодирования:" msgid "" "Set the audio codec to encode with when a suitable track can not be found " "for audio passthru." msgstr "Установить кодировщика аудио для кодирования с тем, что бы использовать когда подходящий декодер не найден." msgid "Audio Encoder Settings:" msgstr "Параметры кодирования звука:" msgid "Each selected source track will be encoded with all selected encoders" msgstr "Каждая дорожка выбранного источника будет закодирована со всеми выбранными кодировщиками." msgid "Encoder" msgstr "Кодировщик" msgid "Bitrate/Quality" msgstr "Битрейт/Качество" msgid "Mixdown" msgstr "Микширование" msgid "Samplerate" msgstr "Частота дискретизации" msgid "Gain" msgstr "Усиление" msgid "Audio Defaults" msgstr "Аудио по умолчанию" msgid "Add new audio settings to the list" msgstr "Добавить новые настройки аудио в список" msgid "Add All" msgstr "Добавить все" msgid "Add all audio tracks to the list" msgstr "Добавить все аудиодорожки в список" msgid "Reload Defaults" msgstr "Перезагрузить по умолчанию" msgid "Reload all audio settings from defaults" msgstr "Перезагрузить все настройки звука по умолчанию" msgid "Audio List" msgstr "Список аудио" msgid "Preferred Language: None" msgstr "Предпочитаемый язык: нет" msgid "Add Foreign Audio Search Pass" msgstr "Добавить поиск иностранного языка в проход" msgid "" "Add \"Foreign Audio Search\" when the default audio track is your preferred language.\n" "This search pass finds short sequences of foreign audio and provides subtitles for them." msgstr "Добавить \"Поиск иностранного языка\", когда для звуковой дорожки у вас выбран предпочитаемый язык по умолчанию.\nЭтот проход поиска находит короткие последовательности иностранного звука и предоставляет для них субтитры." msgid "Add subtitle track if default audio is foreign" msgstr "Добавить дорожку субтитров, если по умолчанию будет иностранный звук" msgid "" "When the default audio track is not your preferred language, add a subtitle " "track." msgstr "Когда для звуковой дорожки по умолчанию нет нужного языка, добавить дорожку субтитров." msgid "Add Closed Captions when available" msgstr "Добавлять скрытые субтитры, когда возможно" msgid "" "Closed captions are text subtitles that can be added to any container as a " "soft subtitle track (not burned)" msgstr "Текстовые субтитры, которые могут быть добавлены в любой контейнер, как дорожка мягких субтитров (не записывая)" msgid "Subtitle Defaults" msgstr "Субтитры по умолчанию" msgid "Add new subtitle settings to the list" msgstr "Добавить новые настройки субтитров в список" msgid "Add all subtitle tracks to the list" msgstr "Добавить все дорожки субтитров в список" msgid "Reload all subtitle settings from defaults" msgstr "Перезагрузить все настройки субтитров по умолчанию" msgid "Subtitle List" msgstr "Список субтитров" msgid "Reference Frames:" msgstr "Рефернсные кадры:" msgid "" "Sane values are ~1-6. The more you add, the better the compression, but the slower the encode.\n" "Cel animation tends to benefit from more reference frames a lot more than film content.\n" "\n" "Note that many hardware devices have limitations on the number of supported reference\n" "frames, so if you're encoding for a handheld or standalone player, don't touch this unless\n" "you're absolutely sure you know what you're doing!" msgstr "Нормальные значения ~ 1-6.Чем больше вы добавите, тем лучше сжатие, но медленнее кодирование.\nCel анимация, как правило, дает выгоду от дополнительных опорных кадров гораздо больше, чем от содержимого фильма.\n\nСледует отметить, что многие аппаратные устройства имеют ограничения по количеству поддерживаемых опорных\nкадров, так что если вы кодируете для карманного компьютера, не трогайте это, если\nне уверенны, что знаете, что вы делаете!" msgid "Maximum B-Frames:" msgstr "Максимум B-кадров:" msgid "" "Sane values are ~2-5. This specifies the maximum number of sequential B-frames that the encoder can use.\n" "\n" "Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.\n" "Cel-animated source material and B-pyramid also significantly increase the usefulness of larger\n" "values.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off)." msgstr "Нормальные значения ~ 2-5. Это определяет максимальное число последовательных B-кадров, которые может использовать кодировщик.\nБольшое количество обычно не помогает, если адаптивные B-кадры не установлены в \"Оптимально\".\n\nБазовый профиль, как это требуется для iPod и аналогичных устройств, требует чтобы для B-кадров было установлено на 0 (Выкл.)." msgid "Pyramidal B-Frames:" msgstr "Пирамидальные B-кадры:" msgid "" "B-pyramid improves compression by creating a pyramidal structure (hence the name)\n" "of B-frames, allowing B-frames to reference each other to improve compression.\n" "\n" "Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit." msgstr "Пирамидальные B-кадры улучшают сжатие, создавая пирамидальную структуру (отсюда\nназвание)\nВ-кадров, что позволяет B-кадрам ссылаться друг на друга, чтобы улучшить сжатие.\n\nТребования для Максимум B-кадров, больше 1; для адаптивных B-кадров рекомендуются \"Оптимально\" для выгоды полного сжатия." msgid "Weighted P-Frames:" msgstr "Взвешенные P-кадры:" msgid "" "Performs extra analysis to decide upon weighting parameters for each frame.\n" "\n" "This improves overall compression slightly and improves the quality of fades greatly.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires weighted P-frame\n" "prediction to be disabled. Note that some devices and players, even those that support\n" "Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is\n" "completely incompatible with it, for example." msgstr "Выполняет дополнительный анализ, чтобы принять решение о весовых параметрах для каждого кадра.\n\nЭто немного улучшает общее сжатие и значительно улучшает качество изображения.\n\nБазовый профиль, как это требуется для iPod и подобных устройств, требует чтобы прогнозирование\nвзвешенных P-кадров было отключено. Обратите внимание, что у некоторых устройств и проигрывателей, даже тех, которые поддерживают\nосновной профиль, могут возникнуть проблемы с прогнозированием взвешенных P-кадров: например Apple TV, является\nсовершенно несовместим с этим." msgid "8x8 Transform" msgstr "Преобразование 8x8 " msgid "" "The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.\n" "\n" "It improves compression by at least 5% at a very small speed cost and may\n" "provide an unusually high visual quality benefit compared to its compression\n" "gain. However, it requires High Profile, which many devices may not support." msgstr "Умное использование преобразований 8x8 является наиболее полезной особенностью x264 с точки зрения сжатие-скорость.\n\nЭто улучшает сжатие по меньшей мере на 5% при очень малых издержках скорости и может\nобеспечить необычайно высокое качество изображения при получении выгоды в его\nсжатии. Тем не менее, она требует высокого профиля, которые многие устройства могут не поддерживать." msgid "CABAC Entropy Encoding" msgstr "Энтропийное кодирование CABAC" msgid "" "After the encoder has done its work, it has a bunch of data that\n" "needs to be compressed losslessly, similar to ZIP or RAR. H.264 provides\n" "two options for this: CAVLC and CABAC. CABAC decodes a lot slower but\n" "compresses significantly better (10-30%), especially at lower bitrates.\n" "\n" "If you're looking to minimize CPU requirements for video playback, disable this option.\n" "Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled." msgstr "После того как кодировщик завершит работу, у него много данных, которые\nдолжна быть сжаты без потерь, подобно ZIP или RAR. H.264 обеспечивает\nдля этого два варианта: CAVLC и CABAC. CABAC декодирует намного медленнее,\nно сжимает значительно лучше (10-30%), особенно при более низком битрейте.\n\nЕсли хотите минимизировать требования к процессору при воспроизведении видео, отключите эту опцию.\nБазовый профиль, требуемый для iPod и аналогичных устройств, требует чтобы CABAC быть отключен." msgid "Encoding Features" msgstr "Особенности кодирования" msgid "Motion Est. Method:" msgstr "Метод оценки движения" msgid "" "Controls the motion estimation method.\n" "\n" "Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.\n" "A better motion search method improves compression at the cost of speed.\n" "\n" "Diamond: performs an extremely fast and simple search using a diamond pattern.\n" "Hexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n" "Uneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n" "Exhaustive: performs a \"dumb\" search of every pixel in a wide area. Significantly slower for only a small compression gain.\n" "Transformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement." msgstr "Управляет методом оценки движения.\n\nОценкой движения кодировщик определяет, как каждый блок пикселей переместился в кадре.\nПоиск метода движения улучшает сжатие за счет скорости.\n\nРомб: выполняет чрезвычайно быстрый и простой поиск с помощью ромбовидной формы.\nШестиугольник: работает несколько более эффективно, но поиск немного медленнее при использовании формы шестиугольника.\nНеравномерный мульти-шестиугольник: выполняет очень широкий поиск, используя различные модели более точного захвата сложного движения.\nПолный: выполняет \"немой\" поиск каждого пиксела в широкой области. Существенно медленнее, лишь с небольшим приростом сжатия.\nПолный Адамара: как всесторонний, делает еще более точные решения. Соответственно, работает несколько медленнее, и только для небольшого улучшения." msgid "Subpel ME & Mode:" msgstr "Режим оценки субпикселов:" msgid "" "This setting controls both subpixel-precision motion estimation and mode decision methods.\n" "\n" "Subpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n" "Mode decision is the method used to choose how to encode each block of the frame: a very important decision.\n" "SAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n" "6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n" "11 disables all early terminations in analysis.\n" "10 and 11, the most powerful and slowest options, require adaptive quantization (aq-mode > 0) and trellis 2 (always)." msgstr "Этот параметр управляет субпиксельной точностью оценки движения и режимами методов решений.\n\nСубпиксельная оценка движения используется для улучшения оценки движения с точностью до пикселя, улучшая сжатие.\nРежим выбора метода используется, чтобы выбирать, как кодировать каждый блок кадра:: очень важное решение.\nБыстрый CAD метод, последующий SATD, RD, RD повышенного качества, и медленный QPRD.\nНастоятельно рекомендуется 6 или выше: Psy-RD, это очень мощная пси оптимизация, которая помогает сохранить детали, требуется RD.\n11 отключает все ранее сделанные анализы.\n10 и 11, самые мощные и самые медленные параметры, требуют адаптивное квантование (AQ-режим> 0) и треллис (Всегда)." msgid "Motion Est. Range:" msgstr "Диапазон поиска движения:" msgid "" "This is the distance x264 searches from its initial guess at the\n" "motion of a block in order to try to find its actual motion.\n" "\n" "The default is fine for most content, but extremely high motion video,\n" "especially at HD resolutions, may benefit from higher ranges, albeit at\n" "a high speed cost." msgstr "Это расстояние поиска x264 от предположительного начала блока\nдвижения, чтобы попытаться найти его фактическое движение.\n\nПо умолчанию подходит для большинства содержимого, но чрезвычайно высокое движение видео,\nособенно на разрешениях HD, можно извлечь выгоду из более высоких диапазонов, хотя\nбольшие издержки в скорости." msgid "Adaptive Direct Mode:" msgstr "Режим адаптивного управления:" msgid "" "H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n" "\n" "Spatial, the default, is almost always better, but temporal is sometimes useful too.\n" "x264 can, at the cost of a small amount of speed (and accordingly for a small compression gain),\n" "adaptively select which is better for each particular frame." msgstr "H.264 позволяет прогнозировать два различных режима, пространственные и временные B-кадры.\n\nПространственный по умолчанию, он почти всегда лучше, но временной иногда бывает тоже полезен.\nx264 может, ценой небольшого уменьшения скорости (и соответственно, небольшого коэффициента усиления сжатия),\nадаптивно выбирать, какой лучше для каждого конкретного кадра." msgid "Adaptive B-Frames:" msgstr "Адаптивные B-кадры:" msgid "" "x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n" "\n" "Fast mode takes roughly the same amount of time no matter how many B-frames you specify.\n" "However, while fast, its decisions are often suboptimal.\n" "\n" "Optimal mode gets slower as the maximum number of B-Frames increases,\n" "but makes much more accurate decisions, especially when used with B-pyramid." msgstr "x264 имеет множество алгоритмов, чтобы решить, когда и сколько B-кадров следует использовать.\n\nБыстрый режим не занимает много времени, независимо от того, сколько B-кадров вы укажете.\nТем не менее, хоть и быстро, его решения часто недостаточны.\n\nОптимальный режим медленнее, а максимальное число B-кадров увеличенное,\nи он делает намного более точные решения, особенно при использовании B-пирамиды." msgid "Partitions:" msgstr "Разделение на макроблоки:" msgid "" "Mode decision picks from a variety of options to make its decision:\n" "this option chooses what options those are.\n" "\n" "Fewer partitions to check means faster encoding, at the cost of worse\n" "decisions, since the best option might have been one that was turned off." msgstr "Режим выбирает решение из множества вариантов, чтобы сделать свое решение:\nэта опция выбирает, какие варианты использовать.\n\nМеньшее количество разделов для проверки означает более быстрое кодирование, за счет худшего\nрешения, поскольку лучший вариант возможно был тот, который был выключен." msgid "Trellis:" msgstr "Треллис квантование:" msgid "" "Trellis fine-tunes the rounding of transform coefficients to\n" "squeeze out 3-5% more compression at the cost of some speed.\n" "\n" "\"Always\" uses trellis not only during the main encoding process, but also\n" "during analysis, which improves compression even more, albeit at great speed cost.\n" "\n" "Trellis costs more speed at higher bitrates and requires CABAC." msgstr "Треллис настраивает коэффициент округления преобразования, чтобы\nвыжать 3-5% большего сжатия, ценой некоторой скорости.\n\nИспользовать \"Всегда\" не только во время основного процесса кодирования, но и\nпри анализе, который улучшает сжатие, хотя и с большими издержками скорости.\n\nТреллис придает больше скорости с более высоким битрейтом и требует CABAC." msgid "Analysis" msgstr "Анализ" msgid "Adaptive Quantization Strength:" msgstr "Адаптивная сила квантования:" msgid "" "Adaptive quantization controls how the encoder distributes bits across the frame.\n" "\n" "Higher values take more bits away from edges and complex areas to improve areas with finer detail." msgstr "Адаптивное квантование контролирует, как кодировщик распределяет биты по всем кадрам.\n\nВысокие значения считают больше битов от краев и сложных областей для улучшения участков с четкими деталями." msgid "Psychovisual Rate Distortion:" msgstr "Психовизуальная скорость искажения:" msgid "" "Psychovisual rate-distortion optimization takes advantage of the characteristics of human\n" "vision to dramatically improve apparent detail and sharpness.\n" "The effect can be made weaker or stronger by adjusting the strength.\n" "Being an RD algorithm, it requires mode decision to be at least \"6\"." msgstr "Психовизуальная скорость искажения использует преимущества характеристик человеческого зрения,\nчтобы значительно улучшить детали и резкость.\nЭффект можно ослабить или усилить, регулируя силу. Алгоритм скорости искажения\nтребует по меньшей мере установки режима \"6\"." msgid "Psychovisual Trellis:" msgstr "Психовизуальные треллисы:" msgid "" "Psychovisual trellis is an experimental algorithm to further\n" "improve sharpness and detail retention beyond what Psychovisual RD does.\n" "\n" "Recommended values are around 0.2, though higher values may help for very\n" "grainy video or lower bitrate encodes. Not recommended for cel animation\n" "and other sharp-edged graphics." msgstr "Психовизуальные треллисы являются экспериментальным алгоритмом, способствующим\nулучшению резкости и сохранения деталей выше, чем это делает психовизуальная скорость искажения.\n\nРекомендуемые значения около 0,2. Более высокие значения могут помочь при очень\nзернистом видео или низким битрейте кодирования. Не рекомендуется для кель анимации кель\nи другой графики с острыми краями." msgid "Deblocking: " msgstr "Подавление блочности: " msgid "" "H.264 deblocking filter.\n" "\n" "h.264 has a built-in deblocking filter that smooths out blocking artifacts\n" "after decoding each frame. This not only improves visual quality, but also\n" "helps compression significantly. The deblocking filter takes a lot of CPU power,\n" "so if you're looking to minimize CPU requirements for video playback, disable it.\n" "\n" "The deblocking filter has two adjustable parameters, \"strength\" (Alpha) and \"threshold\" (Beta).\n" "The former controls how strong (or weak) the deblocker is, while the latter controls how many\n" "(or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking.\n" "The default is 0 (normal strength) for both parameters." msgstr "Фильтр устранения блочности H.264.\n\nh.264 имеет встроенный фильтр устранения блочности для сглаживания артефактов блочности\nпосле декодирования каждого кадра. Это не только улучшает визуальное качество, но и\nзначительно помогает сжатию. Фильтр деблокирования занимает много ресурсов процессора.\nЕсли хотите минимизировать требования к процессору при воспроизведении видео, отключите эту опцию.\n\nФильтр деблокирования имеет два регулируемых параметра, \"сила\" (Alpha) и\n\"порог\" (бета-версия).\nПервый контролирует, насколько сильное (или слабое) деблокирование, в то время как последний определяет, сколько\n(или намного) она применяется. Более низкие значения означают меньше блочности, высокие значения означают больше блочности.\nПо умолчанию 0 (нормальная сила) для обоих параметров." msgid "No DCT Decimate" msgstr "Не опустошать DCT" msgid "" "x264 normally zeroes out nearly-empty data blocks to save bits to\n" "be better used for some other purpose in the video. However, this can\n" "sometimes have slight negative effects on retention of subtle grain and\n" "dither.\n" "\n" "Don't touch this unless you're having banding issues or other such cases\n" "where you are having trouble keeping fine noise." msgstr "x264 обычно обнуляет почти пустые блоки данных для сохранения битов, которые\nлучше будет использовать для других целей в видео. Тем не менее, иногда это\nможет иметь некоторое отрицательное воздействие при сохранении тонких мелких\nчастиц и размытия.\nНе касайтесь этого, если не испытываете проблем от сохранения мелких шумов." msgid "Psychovisual" msgstr "Восприятие" msgid "" "Your selected options will appear here.\n" "You can edit these and add additional options.\n" "\n" "Default values will not be shown. The defaults are:\n" "ref=3:bframes=3:b-adapt=fast:direct=spatial:\n" "b-pyramid=normal:weightp=2:me=hex:merange=16:\n" "subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\n" "deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\n" "no-fast-pskip=0:no-dct-decimate=0:cabac=1" msgstr "Выбранные опции будут показаны здесь.\nВы можете их отредактировать и добавить в дополнительные опции.\n\nЗначение по умолчанию не будет показано. По умолчанию это:\nref=3:bframes=3:b-adapt=fast:direct=spatial:\nb-pyramid=normal:weightp=2:me=hex:merange=16:\nsubme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\ndeblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\nno-fast-pskip=0:no-dct-decimate=0:cabac=1" msgid "Current x264 Advanced Option String" msgstr "Дополнительная строка текущей опции x264" msgid "Advanced Video" msgstr "Дополнительно" msgid "Chapter Markers" msgstr "Маркеры глав" msgid "Add chapter markers to output file." msgstr "Добавить маркеры глав в выходной файл." msgid "Chapters" msgstr "Главы" msgid "Actors:" msgstr "Актеры:" msgid "Director:" msgstr "Режиссёр:" msgid "Release Date:" msgstr "Дата выпуска:" msgid "Comment:" msgstr "Комментарий:" msgid "Genre:" msgstr "Жанр:" msgid "Description:" msgstr "Описание:" msgid "Plot:" msgstr "Сюжет:" msgid "Tags" msgstr "Теги" msgid "Settings" msgstr "Настройки" msgid "Edit" msgstr "Изменить" msgid "Reload" msgstr "Перезагрузить" msgid "" "Mark selected queue entry as pending.\n" "Resets the queue job to pending and ready to run again." msgstr "Отметить выбранные записи очереди, как в ожидании.\nСбросить ожидание очереди заданий и продолжить выполнять задание снова." msgid "Reload All" msgstr "Обновить все" msgid "" "Mark all queue entries as pending.\n" "Resets all queue jobs to pending and ready to run again." msgstr "Отметить все записи очереди, как в ожидании.\nСбросить ожидание все заданий очереди и продолжить выполнять задание снова." msgid "OK" msgstr "OK" msgid "Select All" msgstr "Выбрать все" msgid "Mark all titles for adding to the queue" msgstr "Отметить все названия для добавления в очередь" msgid "Clear All" msgstr "Очистить все" msgid "Unmark all titles" msgstr "Снять все названия" msgid "Destination files OK. No duplicates detected." msgstr "С файлами назначения все хорошо. Дубликаты не обнаружены." msgid "Select this title for adding to the queue.\n" msgstr "Отметить все названия для добавления в очередь.\n" msgid "Preferences" msgstr "Параметры" msgid "Automatically check for updates" msgstr "Автоматически проверять наличие обновлений" msgid "When all encodes are complete" msgstr "Когда все кодирование завершено" msgid "Use automatic naming (uses modified source name)" msgstr "Использовать автоматический идентификатор (использует измененное исходное имя)" msgid "Auto-Name Template" msgstr "Шаблон автонаименования" msgid "" "Available Options: {source} {title} {chapters} {date} {time} {quality} " "{bitrate}" msgstr "Доступные параметры: {source} {title} {chapters} {date} {time} {quality} {bitrate}" msgid "Use iPod/iTunes friendly (.m4v) file extension for MP4" msgstr "Использовать дружественное iPod/iTunes расширение файла (.m4v) для MP4" msgid "Number of previews" msgstr "Количество миниатюр" msgid "Filter short titles (seconds)" msgstr "Фильтр коротких наименований (в секундах)" msgid "Show system tray icon" msgstr "Показывать значек в системном лотке" msgid "General" msgstr "Общие" msgid "Constant Quality fractional granularity" msgstr "Незначительная зернистость на постоянном качестве" msgid "Use dvdnav (instead of libdvdread)" msgstr "Использовать dvdnav (вместо libdvdread)" msgid "Put individual encode logs in same location as movie" msgstr "Помещать индивидуальные логи кодирования в то же расположение, что и фильм" msgid "Activity Log Verbosity Level" msgstr "Подробный журнал уровня деятельности" msgid "Activity Log Longevity" msgstr "Долговечный журнала операций" msgid "Scale down High Definition previews" msgstr "Уменьшать высокую четкость просмотра" msgid "Automatically Scan DVD when loaded" msgstr "Автоматически сканировать DVD при загрузке" msgid "Scans the DVD whenever a new disc is loaded" msgstr "Сканировать DVD после загрузки нового диска" msgid "Hide Advanced Video Options Tab" msgstr "Скрывать вкладку дополнительной настройки видео" msgid "" "Use advanced video options at your own risk.\n" "We recommend that you use the controls available\n" "on the Video tab instead." msgstr "Используйте дополнительные параметры видео на свой страх и риск.\nМы рекомендуем вам использовать доступные элементы управления на\nна вкладке \"Видео\"." msgid "Delete completed jobs from queue" msgstr "Удалять завершённые задачи из очереди" msgid "" "By default, completed jobs remain in the queue and are marked as complete.\n" "Check this if you want the queue to clean itself up by deleting completed jobs." msgstr "По умолчанию, выполненные задания остаются в очереди и помечаются как завершенные.\nОтметьте это, если хотите очищать очередь до окончания выполнения задания." msgid "Allow Tweaks" msgstr "Разрешить тонкие настройки" msgid "Allow HandBrake For Dummies" msgstr "Сделать HandBrake для чайников" msgid "Advanced" msgstr "Дополнительно" msgid "Folder Name:" msgstr "Название папки:" msgid "Description" msgstr "Описание" msgid "Preset Name:" msgstr "Название предустановки:" msgid "Custom Picture Dimensions" msgstr "Пользовательский размер изображения" msgid "Maximum Width:" msgstr "Максимальная ширина:" msgid "Enable maximum width limit." msgstr "Включить лимит максимальной ширины." msgid "" "This is the maximum width that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source width is greater.\n" "Setting this to 0 means there is no maximum width." msgstr "Это максимальная ширина, в которой будет сохраняться видео.\n\nВсякий раз, когда загружается новый источник, это значение будет применяться, если ширина источника больше.\nЗначение 0 означает, что максимальной ширины нет." msgid "Maximum Height:" msgstr "Максимальная высота:" msgid "Enable maximum height limit." msgstr "Включить лимит максимальной высоты." msgid "" "This is the maximum height that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source height is greater.\n" "Setting this to 0 means there is no maximum height." msgstr "Это максимальная высота, в которой будет сохраняться видео.\n\nВсякий раз, когда загружается новый источник, это значение будет применяться, если высота источника больше.\nЗначение 0 означает, что максимальной высоты нет." msgid "Select preview frames." msgstr "Выбор кадра просмотра." msgid "" "Encode and play a short sequence of video starting from the current preview " "position." msgstr "Кодировать и воспроизводить короткие последовательности видео, начиная с текущего положения просмотра." msgid "Duration:" msgstr "Продолжительность:" msgid "Set the duration of the live preview in seconds." msgstr "Установить продолжительность предварительного просмотра в секундах." msgid "Show Crop" msgstr "Показать границы обрезки" msgid "Show Cropped area of the preview" msgstr "Показать просмотр обрезанных областей" msgid "Fullscreen" msgstr "Полноэкранный" msgid "View Fullscreen Preview" msgstr "Просмотр в полный экран" msgid "Title Number:" msgstr "Количество названий:" msgid "Detected DVD devices:" msgstr "Обнаружено устройство DVD:" msgid "Setting:" msgstr "Настройка:" msgid "Import SRT" msgstr "Импорт SRT" msgid "Enable settings to import an SRT subtitle file" msgstr "Включить настройки импорта файла субтитров SRT" msgid "Embedded Subtitle List" msgstr "Список встроенных субтитров" msgid "Enable settings to select embedded subtitles" msgstr "Включить настройки выбранных встроенных субтитров" msgid "Character Code" msgstr "Кодировка" msgid "Offset (ms)" msgstr "Смещение (мс)" msgid "" "Set the language of this subtitle.\n" "This value will be used by players in subtitle menus." msgstr "Установить язык этих субтитров.\nЭто значение будет использоваться проигрывателями в меню субтитров." msgid "" "Set the character code used by the SRT file you are importing.\n" "\n" "SRTs come in all flavours of character sets.\n" "We translate the character set to UTF-8.\n" "The source's character code is needed in order to perform this translation." msgstr "Установить кодирокау символов, используемую SRT для импортируемого файла.\n\nSRT бывают для всех разновидностей кодировок.\nМы переводим набор символов в UTF-8.\nКодировка источника необходима для того, чтобы выполнить этот перевод." msgid "Select the SRT file to import." msgstr "Выберите файл srt для импорта." msgid "Srt File" msgstr "Файл srt" msgid "Adjust the offset in milliseconds between video and SRT timestamps" msgstr "Установить смещение в миллисекундах между видео и метками времени SRT" msgid "Track" msgstr "Дорожка" msgid "List of subtitle tracks available from your source." msgstr "Список дорожек субтитров доступных в источнике." msgid "Forced Subtitles Only" msgstr "Только принудительные субтитры" msgid "Burn into video" msgstr "Записать в видео" msgid "" "Render the subtitle over the video.\n" "The subtitle will be part of the video and can not be disabled." msgstr "Отображение субтитров на видео.\nСубтитры будут на части видео и не могут быть отключены." msgid "Set Default Track" msgstr "Установить дорожку по умолчанию" msgid "Source Track" msgstr "Дорожка источника" msgid "List of audio tracks available from your source." msgstr "Список доступных в источнике звуковых дорожек." msgid "Track Name:" msgstr "Название дорожки:" msgid "" "Set the audio track name.\n" "\n" "Players may use this in the audio selection list." msgstr "Установить название звуковой дорожки.\n\nПроигрыватели могут использовать это списке выбора звука. " msgid "Mix" msgstr "Микшер" msgid "Sample Rate" msgstr "Частота дискретизации" msgid "" "Dynamic Range Compression: Adjust the dynamic range of the output audio track.\n" "\n" "For source audio that has a wide dynamic range (very loud and very soft sequences),\n" "DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder." msgstr "Динамическое сжатие диапазона:\nДля источника звука, который имеет широкий динамический диапазон. Очень громкие и очень мягкие последовательности, DRC позволяет сжимать диапазон, делая громкие разделы мягче и мягкие разделы громче." msgid "Enable bitrate setting" msgstr "Включить настройку битрейта" msgid "Enable quality setting" msgstr "Включить настройку качества" msgid "" "Quality: For output codec's that support it, adjust the quality of " "the output." msgstr "Качество: Для выходного кодека, который поддерживают это, установить качество вывода." msgid "00.0" msgstr "00.0" msgid "" "Audio Gain: Adjust the amplification or attenuation of the output " "audio track." msgstr "Усиление звука: Регулирует усиление или ослабление звуковой дорожки вывода." msgid "Skip This Version" msgstr "Пропустить эту версию" msgid "Remind Me Later" msgstr "Напомнить позже" msgid "A new version of HandBrake is available!" msgstr "Доступна новая версия HandBrake!" msgid "HandBrake xxx is now available (you have yyy)." msgstr "Доступна новая версия HandBrake xxx (у вас версия yyy)." msgid "Release Notes" msgstr "Заметки о релизе" msgid "First Track Matching Selected Languages" msgstr "Первая дорожка соответствующая выбранным языкам" msgid "All Tracks Matching Selected Languages" msgstr "Все дорожки соответствующие выбранным языкам" msgid "Chapters:" msgstr "Главы:" msgid "Seconds:" msgstr "Секунды:" msgid "Frames:" msgstr "Кадры:" msgid "Do Nothing" msgstr "Ничего не делать" msgid "Show Notification" msgstr "Показать уведомление" msgid "Quit Handbrake" msgstr "Выйти из Handbrake" msgid "Put Computer To Sleep" msgstr "Перейти в спящий режим" msgid "Shutdown Computer" msgstr "Выключить компьютер" msgid "Week" msgstr "Неделя" msgid "Month" msgstr "Месяц" msgid "Year" msgstr "Год" msgid "Immortal" msgstr "Вечно" msgid "Never" msgstr "Никогда" msgid "Daily" msgstr "Ежедневно" msgid "Weekly" msgstr "Еженедельно" msgid "Monthly" msgstr "Ежемесячно" msgid "Default" msgstr "По умолчанию" msgid "Fast" msgstr "Быстро" msgid "Slow" msgstr "Медленно" msgid "Slower" msgstr "Медленно" msgid "Ultralight" msgstr "Очень легкое" msgid "Light" msgstr "Легкое" msgid "Medium" msgstr "Среднее" msgid "Strong" msgstr "Сильное" msgid "Film" msgstr "Фильм" msgid "Grain" msgstr "Зернистость" msgid "High Motion" msgstr "Высокое движение" msgid "Animation" msgstr "Анимация" msgid "Spatial" msgstr "Пространственный" msgid "Temporal" msgstr "Временной" msgid "Automatic" msgstr "Автоматически" msgid "Optimal" msgstr "Оптимально" msgid "Normal" msgstr "Обычные" msgid "Simple" msgstr "Простые" msgid "Smart" msgstr "Интеллектуальные" msgid "Diamond" msgstr "Ромб" msgid "Hexagon" msgstr "Шестиугольник" msgid "Uneven Multi-Hexagon" msgstr "Неравномерный мульти-шестиугольник" msgid "Exhaustive" msgstr "Полный" msgid "Hadamard Exhaustive" msgstr "Исчерпывающая Адамара" msgid "Most" msgstr "Большинство" msgid "Some" msgstr "Некоторые" msgid "All" msgstr "Все" msgid "Encode only" msgstr "Только кодирование" msgid "Always" msgstr "Всегда" msgid "(NTSC Film)" msgstr "(Фильм NTSC)" msgid "(PAL Film/Video)" msgstr "(Фильм/видео PAL)" msgid "(NTSC Video)" msgstr "(Видео NTSC)" msgid "%d - %02dh%02dm%02ds - %s" msgstr "%d - %02d час %02d мин %02d сек - %s" msgid "%d (%05d.MPLS) - %02dh%02dm%02ds" msgstr "%d (%05d.MPLS) - %02d час %02d мин %02d сек" msgid "%d (%05d.MPLS) - Unknown Length" msgstr "%d (%05d.MPLS) - Продолжительность неизвестна" msgid "%d - %02dh%02dm%02ds" msgstr "%d - %02d час %02d мин %02d сек" msgid "%d - Unknown Length" msgstr "%d - Продолжительность неизвестна" msgid "No Titles" msgstr "Без названия" msgid "No Audio" msgstr "Нет звука" msgid "Foreign Audio Search" msgstr "Поиск иностранного языка" #, c-format msgid "Chapter %2d" msgstr "Глава %2d" #, c-format msgid "N/A" msgstr "Н/Д" #, c-format msgid "" "Invalid Deinterlace Settings:\n" "\n" "%s\n" msgstr "Недопустимые настройки деинтерлейсинга:\n\n%s\n" #, c-format msgid "" "Invalid Detelecine Settings:\n" "\n" "%s\n" msgstr "Недопустимые настройки детелесина:\n\n%s\n" #, c-format msgid "" "Invalid Decomb Settings:\n" "\n" "%s\n" msgstr "Недопустимые настройки устранения гребенки:\n\n%s\n" msgid "" "Theora is not supported in the MP4 container.\n" "\n" "You should choose a different video codec or container.\n" "If you continue, FFMPEG will be chosen for you." msgstr "Theora не поддерживается в контейнере MP4.\n\nНеобходимо выбрать другой видеокодек или контейнер.\nЕсли продолжите, для вас будет выбран ffmpeg." msgid "Continue" msgstr "Продолжить" msgid "No title found.\n" msgstr "Название не найдено.\n" msgid "" "Only one subtitle may be burned into the video.\n" "\n" "You should change your subtitle selections.\n" "If you continue, some subtitles will be lost." msgstr "Только одни субтитры могут быть записаны в видео.\n\nВы должны изменить настройки субтитров.\nЕсли продолжите, некоторые субтитры будут потеряны." msgid "" "Srt file does not exist or not a regular file.\n" "\n" "You should choose a valid file.\n" "If you continue, this subtitle will be ignored." msgstr "Srt файл не существует или неправильный файл.\n\nВам следует выбрать правильный файл.\nЕсли продолжите, эти субтитры будут игнорироваться." msgid "" "The source does not support Pass-Thru.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "Источника не поддерживает %s автовыбор кодека.\n\nВы должны выбрать другой кодек.\nЕсли вы продолжите, один из них будет выбран для вас." #, c-format msgid "" "%s is not supported in the %s container.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "%s не поддерживается в контейнере %s.\n\nВам следует выбрать другой кодировщик звука.\nЕсли продолжите, один будет выбран для вас." #, c-format msgid "" "The source audio does not support %s mixdown.\n" "\n" "You should choose a different mixdown.\n" "If you continue, one will be chosen for you." msgstr "Аудио источника не поддерживает %s микширование.\n\nВы должны выбрать другое микширование.\nЕсли вы продолжите, одно из них будет выбрано для вас." #, c-format msgid "" "Unable to create %s.\n" "\n" "Internal error. Could not parse UI description.\n" "%s" msgstr "Не удалось создать %s.\n\nВнутренняя ошибка. Не удалось разобрать описание пользовательского интерфейса.\n%s" msgid "Index" msgstr "Индекс" msgid "Duration" msgstr "Продолжительность" msgid "Title" msgstr "Название" msgid "Job Information" msgstr "Сведения о задании" msgid "Track Information" msgstr "Информация о дорожке" msgid "Preset Name" msgstr "Название предустановки" msgid "The device or file to encode" msgstr "Устройство или файл для кодирования" msgid "The preset values to use for encoding" msgstr "Использовать для кодирования заданные значения" msgid "Spam a lot" msgstr "Много спама" msgid "- Transcode media formats" msgstr "- Форматы перекодирования" msgid "Globals" msgstr "Глобальные" msgid "Presets" msgstr "Предустановки" msgid "Folder" msgstr "Папка" #, c-format msgid "%s path: (%s)" msgstr "%s путь: (%s)" #, c-format msgid "%s indices: len %d" msgstr "%s индексов: len %d" msgid "Type" msgstr "Тип" msgid "Failed to find parent folder when adding child." msgstr "Не удалось найти родительскую папку." msgid "Failed to find parent folder while adding child." msgstr "Не удалось найти родительскую папку." #, c-format msgid "Can't map language value: (%s)" msgstr "Не удается отобразить значение языка: (%s)" #, c-format msgid "Can't map value: (%s)" msgstr "Не удается отобразить значение: (%s)" msgid "Subtitles" msgstr "Субтитры" msgid "" "%s: Folder already exists.\n" "You can not replace it with a preset." msgstr "%s: Папка уже существует.\nВы не можете изменить эту папку." msgid "" "%s: Preset already exists.\n" "You can not replace it with a folder." msgstr "%s: Предустановка уже существует.\nВы не можете изменить это с папки." msgid "Import Preset" msgstr "Импорт предустановки" msgid "All (*)" msgstr "Все (*)" msgid "Presets (*.plist)" msgstr "Предустановки (*.plist)" msgid "Export Preset" msgstr "Экспорт предустановки" msgid "" "Confirm deletion of %s:\n" "\n" "%s" msgstr "Хотите удалить %s?\n\n%s" msgid "folder" msgstr "папку" msgid "preset" msgstr "предустановку" msgid "No selection??? Perhaps unselected." msgstr "Нет выбора ??? Возможно, не выбрано." #, c-format msgid "Gstreamer Error: %s" msgstr "Ошибка Gstreamer: %s" #, c-format msgid "" "Missing GStreamer plugin\n" "Audio or Video may not play as expected\n" "\n" "%s" msgstr "Отсутствует плагин GStreamer\nАудио или видео не сможет проигрываться, как ожидалось\n\n%s" msgid "Done" msgstr "Готово" msgid "Windowed" msgstr "Оконный" msgid "Seconds" msgstr "Секунды" msgid "Frames" msgstr "Кадры" #, c-format msgid "" "%s (Title %d, %s %d through %d, 2 Video Passes) -->" " %s" msgstr "%s (Название %d, %s %d транзитная %d, 2 проход видео) --> %s" #, c-format msgid "" "%s (Title %d, %s %d through %d) --> %s" msgstr "%s (Название %d, %s %d транзитная %d) --> %s" #, c-format msgid "Modified Preset Based On: %s\n" msgstr "Изменен шаблон на основе: %s\n" #, c-format msgid "Preset: %s\n" msgstr "Предустановка: %s\n" #, c-format msgid "Format: %s Container\n" msgstr "Формат: Контейнер %s\n" msgid "Container Options:" msgstr "Параметры контейнера:" #, c-format msgid "%sChapter Markers" msgstr "%sМаркеры глав" #, c-format msgid "%siPod 5G Support" msgstr "%sПоддержка iPod 5G" #, c-format msgid "%sWeb Optimized" msgstr "%sОптимизация для интернет-трансляции" #, c-format msgid "%sLarge File Size (>4GB)" msgstr "%sБольшой размер файла (>4GB)" #, c-format msgid "Destination: %s\n" msgstr "Назначение: %s\n" msgid "(Aspect Preserved)" msgstr "(Соотношение сохранилось)" msgid "(Aspect Lost)" msgstr "(Соотношение потерялось)" msgid "(Anamorphic)" msgstr "(Анаморфотный)" msgid "(Custom Anamorphic)" msgstr "(Пользовательский анаморфотный)" msgid "Picture: " msgstr "Изображение:" #, c-format msgid "Source: %d x %d, Output %d x %d %s, Crop %d:%d:%d:%d" msgstr "Источник: %d x %d, Вывод %d x %d %s, Обрезка %d:%d:%d:%d" #, c-format msgid ", Display %d x %d" msgstr ", Дисплей %d x %d" msgid "Filters:" msgstr "Фильтры:" #, c-format msgid "%sDetelecine" msgstr "%sДетелесин" #, c-format msgid "%sDecomb" msgstr "%sУстранение гребенки" #, c-format msgid "%sDeinterlace" msgstr "%sДеинтерлейсинг" #, c-format msgid "%sDenoise Filter %s:" msgstr "%sФильтр шумоподавления %s:" #, c-format msgid "%sDeblock: %d" msgstr "%sУровень блочности: %d" #, c-format msgid "%sGrayscale" msgstr "%sЧёрно-белое" #, c-format msgid "Video: %s" msgstr "Видео: %s" #, c-format msgid ", Framerate: %s %s" msgstr ", Частота кадров: %s %s" #, c-format msgid ", Framerate: Peak %s (may be lower)" msgstr ", Частота кадров: Пик %s (может быть ниже)" #, c-format msgid ", Framerate: %s (constant frame rate)" msgstr ", Частота кадров: %s (включает постоянную частоту кадров)" msgid "Error" msgstr "Ошибка" msgid "Bitrate:" msgstr "Битрейт:" msgid "Bitrate" msgstr "Битрейт" msgid "Quality" msgstr "Качество" msgid "Turbo 1st Pass: On\n" msgstr "Turbo 1 проход: На\n" #, c-format msgid "Video Options: Preset: %s" msgstr "Параметры видео: Предустановка: %s" msgid " - Tune: " msgstr " - Настройки: " #, c-format msgid " - Profile: %s" msgstr " - Профиль: %s" #, c-format msgid " - Level: %s" msgstr " - Уровень: %s" #, c-format msgid "Advanced Options: %s\n" msgstr "Дополнительные опции: %s\n" msgid "Audio: " msgstr "Аудио: " #, c-format msgid "Audio Tracks: %d" msgstr "Звуковые дорожки: %d" #, c-format msgid "Bitrate: %d" msgstr "Битрейт: %d" #, c-format msgid "%s --> Encoder: %s" msgstr "%s --> Кодировщик: %s" #, c-format msgid "%s --> Encoder: %s, Mixdown: %s, SampleRate: %s, %s" msgstr "%s --> Кодировщик: %s, Микширование: %s, Частота дискредитации: %s, %s" msgid "Subtitle: " msgstr "Субтитры:" #, c-format msgid "Subtitle Tracks: %d\n" msgstr "Дорожки субтитров: %d\n" msgid " (Force)" msgstr " (Принудительно)" msgid " (Burn)" msgstr " (Записать)" msgid " (Default)" msgstr " (По умолчанию)" #, c-format msgid " %s (%s), %s, Offset (ms) %d%s\n" msgstr " %s (%s), %s, Смещение (мс) %d%s\n" #, c-format msgid "" "Destination: %s\n" "\n" "Another queued job has specified the same destination.\n" "Do you want to overwrite?" msgstr "Назначение: %s\n\nДругое задание в очереди имеет это же назначение.\nХотите его перезаписать?" msgid "Overwrite" msgstr "Перезаписать" #, c-format msgid "" "Destination: %s\n" "\n" "This is not a valid directory." msgstr "Назначение: %s\n\nЭто не допустимый каталог." #, c-format msgid "" "Destination: %s\n" "\n" "Can not read or write the directory." msgstr "Назначение: %s\n\nНе удается чтение или запись в папку." #, c-format msgid "" "Destination filesystem is almost full: %uM free\n" "\n" "Encode may be incomplete if you proceed.\n" msgstr "Файловая система почти заполнена: %uM свободно\n\nКодирование может быть неполным, если продолжите.\n" msgid "Proceed" msgstr "Продолжить" #, c-format msgid "" "Destination: %s\n" "\n" "File already exists.\n" "Do you want to overwrite?" msgstr "Назначение: %s\n\nФайл уже существует.\nХотите его переписать?" msgid "" "Duplicate destination files detected.\n" "Duplicates will not be added to the queue." msgstr "Обнаружены повторяющиеся целевые файлы.\nДубликаты не будут добавлены в очередь." msgid "No Title" msgstr "Без названия" msgid "" "There is another title with the same destination file name.\n" "This title will not be added to the queue unless you change\n" "the output file name.\n" msgstr "Существует еще одно название с тем же именем файла назначения.\nЭто название не будет добавлено в очередь, если не изменить\nимя выходного файла.\n" msgid "Stop" msgstr "Остановить" msgid "Stop Encoding" msgstr "Остановить кодирование" msgid "Resume" msgstr "Продолжить" msgid "Resume Encoding" msgstr "Продолжить кодирование" msgid "S_top Queue" msgstr "О_становить очередь" msgid "_Start Queue" msgstr "_Запустить очередь" msgid "_Resume Queue" msgstr "_Продолжить очередь" msgid "Resume Queue" msgstr "Продолжить очередь" msgid "" "You are currently encoding. What would you like to do?\n" "\n" msgstr "Вы сейчас кодируете. Что бы вы хотели сделать?\n\n" #, c-format msgid "" "You have %d unfinished job(s) in a saved queue.\n" "\n" "Would you like to reload them?" msgstr "У вас есть %d незаконченное задание(я) в сохраненной очереди.\n\nХотите их перезапустить?" msgid "No" msgstr "Нет" msgid "Yes" msgstr "Да" #, c-format msgid "Usage: %s infile [outfile]\n" msgstr "Используется: %s infile [outfile]\n" #, c-format msgid "Offset: %dms" msgstr "Смещение: %dms" msgid "Burned Into Video" msgstr "Сведения о записываемом видео" msgid "Passthrough" msgstr "Передача" msgid "through" msgstr "по" msgid "(Forced Subtitles Only)" msgstr "(Только принудительные субтитры)" msgid "(Default)" msgstr "(По умолчанию)" msgid "Error!" msgstr "Ошибка!" #, c-format msgid "Preferred Language: %s" msgstr "Предпочитаемый язык: %s" #, c-format msgid "Add %s subtitle track if default audio is not %s" msgstr "Добавить %s дорожку субтитров, если не звука по умолчанию %s" #, c-format msgid "Type %s" msgstr "Тип %s" #, c-format msgid "Type %s value %s" msgstr "Тип %s значение %s" #, c-format msgid "Type %s value %d" msgstr "Тип %s значение %d" #, c-format msgid "Type %s value %" msgstr "Тип %s значение %" #, c-format msgid "Type %s value %f" msgstr "Тип %s значение %f" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"%s\"" msgstr "%s\n\nРасширенные параметры:\n\"%s\"" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"\"" msgstr "%s\n\nРасширенные параметры:\n\"\"" msgid "Any" msgstr "Любой" msgid "0: SAD, no subpel" msgstr "0: SAD, без субпиксельной" msgid "4: SATD, qpel on all" msgstr "4: SATD, qpel во всех" msgid "5: SATD, multi-qpel on all" msgstr "5: SATD, multi-qpel во всех" msgid "6: RD in I/P-frames" msgstr "6: RD в I/P-кадрах" msgid "7: RD in all frames" msgstr "7: RD во всех кадрах" msgid "8: RD refine in I/P-frames" msgstr "8: RD улучшать в I/P-кадрах" msgid "9: RD refine in all frames" msgstr "9: RD улучшать во всех кадрах" msgid "10: QPRD in all frames" msgstr "10: QPRD во всех кадрах" msgid "11: No early terminations in analysis" msgstr "11: Без ранние сделанных анализов" msgid "Your names" msgstr "Виктор Рыжих" msgid "Your emails" msgstr "victorr2007@yandex.ru" HandBrake-0.10.2/gtk/po/LINGUAS0000664000175200017520000000014412466165306016277 0ustar handbrakehandbrake# please keep this list sorted alphabetically # cs da de es fr it_IT ja_JP ko no pt_BR ro_RO ru th HandBrake-0.10.2/gtk/po/th.po0000664000175200017520000031744312466165306016242 0ustar handbrakehandbrake# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # M. Somsak, 2014 # M. Somsak, 2014 msgid "" msgstr "" "Project-Id-Version: HandBrake-0.10\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-03 13:01+0300\n" "PO-Revision-Date: 2014-12-17 14:01+0000\n" "Last-Translator: M. Somsak\n" "Language-Team: Thai (http://www.transifex.com/projects/p/handbrake/language/th/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: th\n" "Plural-Forms: nplurals=1; plural=0;\n" msgid "Quality: " msgstr "คุณภาพ:" #, c-format msgid "Bitrate: %dkbps" msgstr "อัตราบิต: %dkbps" #, c-format msgid "Bitrate: %.4gkbps" msgstr "อัตราบิต: %.4gkbps" msgid "Passthrough" msgstr "ส่งผ่าน" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s\n" "Track Name: %s" msgstr "%s\nฟื้นค่า: %s\nDRC: %s\nชื่อแทร็ค: %s" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s" msgstr "%s\nฟื้นค่า: %s\nDRC: %s" msgid "Add" msgstr "เพิ่ม" msgid "" "Add an audio encoder.\n" "Each selected source track will be encoded with all selected encoders." msgstr "เพิ่มตัวเข้ารหัสเสียง\nแต่ละแทร็คแหล่งที่ถูกเลือก จะถูกเข้ารหัสด้วยตัวเข้ารหัสที่เลือกทั้งหมด" msgid "Set the audio codec to encode this track with." msgstr "กำหนด codec เสียงเพื่อเข้ารหัสกับแทร็คนี้" msgid "Set the bitrate to encode this track with." msgstr "กำหนดอัตราบิตเพื่อเข้ารหัสกับแทร็คนี้" msgid "" "Audio Quality:\n" "For encoders that support it, adjust the quality of the output." msgstr "คุณภาพเสียง:\nสำหรับตัวเข้ารหัสที่รองรับมัน, ปรับคุณภาพของผลลัพธ์" msgid "Set the mixdown of the output audio track." msgstr "กำหนดการผสมเสียงของแทร็คเสียงผลลัพธ์" msgid "Set the sample rate of the output audio track." msgstr "กำหนดอัตราสุ่มฯของแทร็คเสียงผลลัพธ์" msgid "" "Audio Gain:\n" "Adjust the amplification or attenuation of the output audio track." msgstr "การฟื้นค่าเสียง:\nปรับการขยายเสียงหรือลดเสียงของแทร็คเสียงผลลัพธ์" msgid "0dB" msgstr "0dB" msgid "%ddB" msgstr "%ddB" msgid "%.4gkHz" msgstr "%.4gkHz" msgid "%d - %s (%.4gkHz)" msgstr "%d - %s (%.4gkHz)" msgid "" "Dynamic Range Compression:\n" "Adjust the dynamic range of the output audio track.\n" "For source audio that has a wide dynamic range,\n" "very loud and very soft sequences, DRC allows you\n" "to 'compress' the range by making loud sounds\n" "softer and soft sounds louder.\n" msgstr "การบีบอัดช่วงไดนามิก:\nปรับช่วงไดนามิกของแทร็คเสียงผลลัพธ์\nสำหรับเสียงแหล่งที่มีช่วงไดนามิกกว้าง,\nมีลำดับเสียงดังมากและเบามาก, DRC ช่วยให้คุณ\n'บีบอัด' ช่วงโดยทำให้เสียงที่ดังเบาลง\nและเสียงที่เบาดังขึ้น\n" msgid "Remove this audio encoder" msgstr "เอาตัวเข้ารหัสเสียงนี้ออก" msgid "Closing HandBrake will terminate encoding.\n" msgstr "การปิด HandBrake จะยุติการเข้ารหัส\n" msgid "No Title Found" msgstr "ไม่พบหัวเรื่อง" msgid "none" msgstr "ไม่มี" msgid "Not Selected" msgstr "ไม่ถูกเลือก" msgid "zerolatency x264 tune selected, forcing constant framerate" msgstr "zerolatency x264 tune ถูกเลือก, บังคับอัตราเฟรมคงที่" msgid "Scanning ..." msgstr "กำลังอ่าน ..." msgid "Stop Scan" msgstr "หยุดอ่าน" msgid "On" msgstr "เปิด" msgid "Strict" msgstr "เข้มงวด" msgid "Loose" msgstr "หละหลวม" msgid "Custom" msgstr "กำหนดเอง" msgid "Unknown" msgstr "ไม่ทราบ" msgid "auto" msgstr "อัตโนมัติ" #, c-format msgid "" "%s\n" "\n" "%s in %d seconds ..." msgstr "%s\n\n%s ใน %d วินาที ..." #, c-format msgid "%sYour movie will be lost if you don't continue encoding." msgstr "%sภาพยนตร์ของคุณจะสูญหาย ถ้าคุณไม่ทำการเข้ารหัสต่อ" msgid "Cancel Current and Stop" msgstr "ยกเลิกรายการปัจจุบัน และหยุด" msgid "Cancel Current, Start Next" msgstr "ยกเลิกรายการปัจจุบัน, เริ่มรายการถัดไป" msgid "Finish Current, then Stop" msgstr "เสร็จรายการปัจจุบัน, แล้วจึงหยุด" msgid "Continue Encoding" msgstr "เข้ารหัสต่อไป" msgid "Custom " msgstr "กำหนดเอง" msgid "Modified " msgstr "ถูกปรับเปลี่ยน" #, c-format msgid "Handbrake Version: %s (%d)\n" msgstr "Handbrake เวอร์ชั่น: %s (%d)\n" #, c-format msgid "%d encode(s) pending" msgstr "%d การเข้ารหัสที่คงค้างอยู่" #, c-format msgid "job %d of %d, " msgstr "งาน %d จาก %d, " #, c-format msgid "pass %d (subtitle scan) of %d, " msgstr "ผ่าน %d (อ่านศัพท์บรรยาย) จาก %d, " #, c-format msgid "pass %d of %d, " msgstr "ผ่าน %d จาก %d, " #, c-format msgid "Encoding: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" msgstr "การเข้ารหัส: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %% (ETA %02dh%02dm%02ds)" msgstr "การเข้ารหัส: %s%s%.2f %% (ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %%" msgstr "การเข้ารหัส: %s%s%.2f %%" msgid "Searching for start time, " msgstr "กำลังค้นหาเวลาเริ่มต้น," msgid "Scanning..." msgstr "กำลังอ่าน..." #, c-format msgid "Scanning title %d of %d..." msgstr "กำลังอ่านหัวเรื่อง %d จาก %d..." #, c-format msgid "Scanning title %d of %d preview %d..." msgstr "กำลังอ่านหัวเรื่อง %d จาก %d ตัวอย่าง %d..." msgid "Source" msgstr "แหล่ง" msgid "Choose Video Source" msgstr "เลือกแหล่งวิดีโอ" msgid "Paused" msgstr "ถูกพัก" msgid "Encode Done!" msgstr "เข้ารหัสเสร็จแล้ว!" msgid "Encode Canceled." msgstr "การเข้ารหัสถูกยกเลิก" msgid "Encode Failed." msgstr "การเข้ารหัสล้มเหลว" msgid "Muxing: This may take a while..." msgstr "กำลังผสาน: สิ่งนี้อาจใช้เวลาสักพัก..." msgid "Scan this DVD source" msgstr "อ่านแหล่ง DVD นี้" msgid "AutoScan" msgstr "อ่านอัตโนมัติ" msgid "Suspend" msgstr "ระงับ" #, c-format msgid "Suspend failed: %s" msgstr "การระงับล้มเหลว: %s" msgid "Suspend failed" msgstr "การระงับล้มเหลว" msgid "Shutdown" msgstr "ปิดเครื่อง" #, c-format msgid "Shutdown failed: %s" msgstr "ปิดเครื่องล้มเหลว: %s" msgid "Shutdown failed" msgstr "การปิดเครื่องล้มเหลว" msgid "Encoding" msgstr "การเข้ารหัส" #, c-format msgid "press %d %d" msgstr "กด %d %d" #, c-format msgid "" "Invalid Settings:\n" "%s" msgstr "การตั้งค่าไม่ถูกต้อง:\n%s" msgid "Cancel" msgstr "ยกเลิก" #, c-format msgid "%s: %.4g (Warning: lossless)" msgstr "%s: %.4g (คำเตือน: ไร้การสูญเสีย)" #, c-format msgid "HandBrake %s/%s is now available (you have %s/%d)." msgstr "HandBrake %s/%s มีในตอนนี้แล้ว (คุณมี %s/%d)" msgid "Encode Complete" msgstr "เข้ารหัสเสร็จสมบูรณ์" msgid "Put down that cocktail, Your HandBrake queue is done!" msgstr "วางเครื่องดื่มนั่นลง, คิว HandBrake ของคุณเสร็จแล้ว!" msgid "Your encode is complete." msgstr "การเข้ารหัสเสร็จสมบูรณ์" msgid "Shutting down the computer" msgstr "กำลังปิดเครื่องคอมพิวเตอร์" msgid "Putting computer to sleep" msgstr "กำลังทำให้คอมพิวเตอร์สลีป" msgid "Quiting Handbrake" msgstr "กำลังออกจาก Handbrake" msgid "Bottom" msgstr "ด้านล่าง" #, c-format msgid "open failed: %s\n" msgstr "เปิดล้มเหลว: %s\n" msgid "No key for dictionary item" msgstr "ไม่มีคีย์สำหรับรายการพจนานุกรม" msgid "Invalid container type. This shouldn't happen" msgstr "ชนิดตัวบรรจุไม่ถูกต้อง สิ่งนี้ไม่ควรเกิดขึ้น" #, c-format msgid "%s:missing a requried attribute" msgstr "%s:ขาดคุณลักษณะที่ต้องการ" #, c-format msgid "" "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" msgstr "" msgid "language" msgstr "ภาษา" msgid "Language" msgstr "ภาษา" msgid "" "The language this text is in, as an ISO code. Pango can use this as a hint " "when rendering the text. If you don't understand this parameter, you " "probably don't need it" msgstr "ข้อความนี้อยู่ในภาษาที่เป็นรหัส ISO, Pango สามารถใช้สิ่งนี้เป็นตัวช่วยแนะเมื่อแปลผลข้อความ ถ้าคุณไม่เข้าใจพารามิเตอร์นี้ คุณอาจไม่จำเป็นต้องใช้มัน" msgid "" "The preferred place to ellipsize the string, if the cell renderer does not " "have enough room to display the entire string" msgstr "ตำแหน่งที่ชอบเพื่อย่อสตริงให้สั้นลง เมื่อตัวแปลผลเซลล์มีห้องไม่เพียงพอต่อการแสดงทั้งสตริง" msgid "" "How to break the string into multiple lines, if the cell renderer does not " "have enough room to display the entire string" msgstr "วิธีแบ่งสตริงเป็นหลายบรรทัด ถ้าตัวแปลผลเซลล์มีห้องไม่เพียงพอต่อการแสดงผลทั้งสตริง" msgid "" "Render the subtitle over the video.\n" "\n" "The subtitle will be part of the video and can not be disabled." msgstr "แสดงผลศัพท์บรรยายบนวิดีโอ\n\nศัพท์บรรยายจะเป็นส่วนหนึ่งของวิดีโอ และไม่สามารถปิดการใช้งานได้" msgid "Burned In" msgstr "ประทับลงใน" msgid "" "Set the default output subtitle track.\n" "\n" "Most players will automatically display this\n" "subtitle track whenever the video is played.\n" "\n" "This is useful for creating a \"forced\" track\n" "in your output." msgstr "กำหนดแทร็คศัพท์บรรยายผลลัพธ์ตั้งต้น\n\nเครื่องเล่นส่วนใหญ่จะแสดงแทร็คศัพท์บรรยายนี้\nอัตโนมัติเมื่อวิดีโอถูกเล่น\n\nสิ่งนี้มีประโยชน์สำหรับสร้างแทร็คแบบ \"บังคับ\" ในผลลัพธ์ของคุณ" msgid "Default" msgstr "ค่าตั้งต้น" msgid "" "Use only subtitles that have been flagged\n" "as forced in the source subtitle track\n" "\n" "\"Forced\" subtitles are usually used to show\n" "subtitles during scenes where someone is speaking\n" "a foreign language." msgstr "ใช้เฉพาะศัพท์บรรยายที่ถูกหมายเหตุเป็น\nแบบบังคับในแทร็คศัพท์บรรยายของแหล่ง\n\nศัพท์บรรยายแบบ \"บังคับ\" ปกติจะใช้เพื่อแสดง\nศัพท์บรรยายระหว่างฉากที่บางคนกำลังพูด\nภาษาต่างประเทศ" msgid "Forced Only" msgstr "บังคับเท่านั้น" msgid "" "Add (or subtract) an offset (in milliseconds)\n" "to the start of the SRT subtitle track.\n" "\n" "Often, the start of an external SRT file\n" "does not coincide with the start of the video.\n" "This setting allows you to synchronize the files." msgstr "บวก (หรือลบ) ค่าชดเชย (เป็นมิลลิวินาที)\nเพื่อเริ่มแทร็คศัพท์บรรยาย SRT\n\nบ่อยครั้งที่ไฟล์ SRT ภายนอก\nแสดงไม่ตรงกับการเริ่มของวิดีโอ\nการตั้งค่านี้ช่วยให้คุณซิงค์ไฟล์ให้ตรงกัน" msgid "SRT Offset" msgstr "ค่าชดเชย SRT" msgid "Off" msgstr "ปิด" msgid "" "The source subtitle track\n" "\n" "You can choose any of the subtitles\n" "recognized in your source file.\n" "\n" "In addition, there is a special track option\n" "\"Foreign Audio Search\". This option will add\n" "an extra pass to the encode that searches for\n" "subtitles that may correspond to a foreign\n" "language scene. This option is best used in\n" "conjunction with the \"Forced\" option." msgstr "แทร็คศัพท์บรรยายของแหล่ง\n\nคุณสามารถใช้สิ่งใดๆของศัพท์บรรยายที่\nจดจำไว้ในไฟล์แหล่งของคุณ\n\nนอกจากนี้ มีตัวเลือกแทร็คพิเศษ\n\"ค้นหาเสียงต่างประเทศ\" ตัวเลือกนี้จะเพิ่ม\nการผ่านพิเศษสู่การเข้ารหัสซึ่งค้นหาศัพท์บรรยาย\nที่อาจสอดคล้องกับฉากภาษาต่างประเทศ\nตัวเลือกนี้จะดีที่สุดเทื่อใช้ร่วมกับ\nตัวเลือก \"บังคับ\"" msgid "Track" msgstr "แทร็ค" msgid "About HandBrake" msgstr "เกี่ยวกับ HandBrake" msgid "" "Copyright © 2008 - 2013 John Stebbins\n" "Copyright © 2004 - 2013, HandBrake Devs" msgstr "สงวนลิขสิทธิ์ © 2008 - 2013 John Stebbins\nสงวนลิขสิทธิ์ © 2004 - 2013, HandBrake Devs" msgid "" "HandBrake is a GPL-licensed, multiplatform, multithreaded video transcoder." msgstr "" msgid "http://handbrake.fr" msgstr "http://handbrake.fr" msgid "" "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n" "\n" "HandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgstr "" msgid "_Minimize/Maximize" msgstr "_ย่อเล็กสุด/ขยายใหญ่สุด" msgid "_Pause Queue" msgstr "_พักคิว" msgid "_Quit" msgstr "_ออก" msgid "_About" msgstr "เ_กี่ยวกับ" msgid "HandBrake" msgstr "HandBrake" msgid "_File" msgstr "ไ_ฟล์" msgid "_Source" msgstr "แห_ล่ง" msgid "Single _Title" msgstr " _หัวเรื่องเดี่ยว" msgid "_Destination" msgstr "_ปลายทาง" msgid "_Preferences" msgstr "การ_ตั้งค่า" msgid "_Queue" msgstr "_คิว" msgid "_Add" msgstr "เ_พิ่ม" msgid "Add _Multiple" msgstr "_เพิ่มหลายสิ่ง" msgid "_Start" msgstr "เ_ริ่ม" msgid "_Pause" msgstr "_พัก" msgid "_View" msgstr "_มุมมอง" msgid "HandBrake For _Dumbies" msgstr "HandBrake สำหรับ _Dumbies" msgid "_Show Presets" msgstr "แสดงค่า_สำเร็จรูป" msgid "_Preview" msgstr "_ตัวอย่าง" msgid "_Activity Window" msgstr "หน้าต่าง_กิจกรรม" msgid "Show _Queue" msgstr "แสดง_คิว" msgid "_Presets" msgstr "ค่า_สำเร็จรูป" msgid "_Save" msgstr "_บันทึก" msgid "_Delete" msgstr "_ลบ" msgid "_Make Default" msgstr "ทำเป็นค่า_ตั้งต้น" msgid "_New Folder" msgstr "โฟลเ_ดอร์ใหม่" msgid "_Export" msgstr "_ส่งออก" msgid "_Import" msgstr "_นำเข้า" msgid "_Update Built-in Presets" msgstr "_อัปเดตค่าสำเร็จรูปที่มีมาให้" msgid "_Help" msgstr "_ช่วยเหลือ" msgid "_Guide" msgstr "แ_นวทาง" msgid "Start Encoding" msgstr "เริ่มการเข้ารหัส" msgid "Start" msgstr "เริ่ม" msgid "Pause Encoding" msgstr "พักการเข้ารหัส" msgid "Pause" msgstr "พัก" msgid "Add to Queue" msgstr "เพิ่มสู่คิว" msgid "Enqueue" msgstr "เข้าคิว" msgid "Show Queue" msgstr "แสดงคิว" msgid "Queue" msgstr "คิว" msgid "" "Open Picture Settings and Preview window.\n" "Here you can adjust cropping, resolution, aspect ratio, and filters." msgstr "เปิดการตั้งค่าภาพและหน้าต่างของตัวอย่าง\nที่นี่คุณสามารถปรับการขลิบ, ความละเอียด, อัตราส่วนภาพ, และตัวกรอง" msgid "Preview" msgstr "ตัวอย่าง" msgid "Show Activity Window" msgstr "แสดงหน้าต่างกิจกรรม" msgid "Activity" msgstr "กิจกรรม" msgid "Source:" msgstr "แหล่ง:" msgid "None" msgstr "ไม่มี" msgid "Title:" msgstr "หัวเรื่อง:" msgid "" "Set the title to encode.\n" "By default the longest title is chosen.\n" "This is often the feature title of a DVD." msgstr "กำหนดหัวเรื่องเพื่อเข้ารหัส\nหัวเรื่องที่ยาวที่สุดจะถูกเลือกเป็นค่าตั้งต้น\nสิ่งนี้มักเป็นคุณลักษณะหัวเรื่องของ DVD" msgid "Angle:" msgstr "มุมกล้อง:" msgid "For multi-angle DVD's, select the desired angle to encode." msgstr "สำหรับ DVD แบบหลายมุมกล้อง, เลือกมุมกล้องที่ต้องการจะเข้ารหัส" msgid "Reset All Titles" msgstr "คืนค่าหัวเรื่องทั้งหมด" msgid "Apply current settings to all titles" msgstr "ใช้การตั้งค่าปัจจุบันกับหัวเรื่องทั้งหมด" msgid "Range of title to encode. Can be chapters, seconds, or frames." msgstr "ช่วงหัวเรื่องเพื่อเข้ารหัส โดยสามารถเป็นได้ทั้ง ตอน, วินาที, หรือเฟรม" msgid "Set the first chapter to encode." msgstr "กำหนดตอนแรก เพื่อเข้ารหัส" msgid "Set the last chapter to encode." msgstr "กำหนดตอนสุดท้าย เพื่อเข้ารหัส" msgid "Duration:" msgstr "ระยะเวลา:" msgid "Destination" msgstr "ปลายทาง" msgid "File:" msgstr "ไฟล์:" msgid "Destination filename for your encode." msgstr "ชื่อไฟล์ปลายทางสำหรับการเข้ารหัสของคุณ" msgid "Destination directory for your encode." msgstr "เส้นทางปลายทางสำหรับการเข้ารหัสของคุณ" msgid "Destination Directory" msgstr "เส้นทางปลายทาง" msgid "Format:" msgstr "รูปแบบ:" msgid "Format to mux encoded tracks to." msgstr "รูปแบบที่จะทำการผสานแทร็คที่เข้ารหัสลงไป" msgid "iPod 5G Support" msgstr "รองรับ iPod 5G" msgid "Add iPod Atom needed by some older iPods." msgstr "เพิ่ม iPod Atom ที่ต้องการโดย iPods เก่าบางรุ่น" msgid "Web optimized" msgstr "ให้เหมาะกับเว็บ" msgid "" "Optimize the layout of the MP4 file for progressive download.\n" "This allows a player to initiate playback before downloading the entire file." msgstr "ปรับเค้าโครงของไฟล์ MP4 ให้เหมาะสมสำหรับการดาวน์โหลดไว้ล่วงหน้า\nสิ่งนี้อนุญาตให้เครื่องเล่นสามารถเริ่มเล่นก่อนการดาวน์โหลดจะครบทั้งไฟล์" msgid "Large file (>4GB)" msgstr "ไฟล์ใหญ่ (>4GB)" msgid "" "Allow 64 bit MP4 file which can be over 4GB.\n" "\n" "Caution: This option may break device compatibility." msgstr "อนุญาตไฟล์ MP4 แบบ 64 บิต ซึ่งขนาดสามารถใหญ่เกิน 4GB\nระวัง: ตัวเลือกนี้อาจละเมิดความเข้ากันกับอุปกรณ์" msgid "Presets List" msgstr "รายการค่าสำเร็จรูป" msgid "Source Codec:" msgstr "Codec แหล่ง:" msgid "Dimensions:" msgstr "มิติ:" msgid "Aspect: " msgstr "สัดส่วน:" msgid "Frame Rate:" msgstr "อัตราเฟรม:" msgid "Source Picture Parameters" msgstr "พารามิเตอร์ภาพแหล่ง" msgid "Autocrop:" msgstr "ขลิบอัตโนมัติ:" msgid "Crop:" msgstr "ขลิบ:" msgid "Crop Dimensions:" msgstr "ขลิบมิติ:" msgid "Cropping" msgstr "การขลิบ" msgid "Scale Dimensions:" msgstr "สเกลมิติ:" msgid "Optimal for Source:" msgstr "เหมาะสำหรับแหล่ง:" msgid "Anamorphic:" msgstr "อนามอร์ฟิก:" msgid "Scaling" msgstr "การสเกล" msgid "Presentation Dimensions:" msgstr "มิติการนำเสนอ:" msgid "Summary" msgstr "สรุป" msgid "Left Crop" msgstr "ขลิบซ้าย" msgid "Top Crop" msgstr "ขลิบบน" msgid "Bottom Crop" msgstr "ขลิบล่าง" msgid "Right Crop" msgstr "ขลิบขวา" msgid "Auto Crop" msgstr "ขลิบอัตโนมัติ" msgid "Automatically crop black borders around edges of the video." msgstr "ขลิบขอบดำรอบวิดีโอโดยอัตโนมัติ" msgid "Loose Crop" msgstr "ขลิบคร่าวๆ" msgid "" "When picture settings require that the image\n" "dimensions be rounded to some multiple number\n" "of pixels, this setting will crop a few extra pixels\n" "instead of doing exact cropping and then scaling to\n" "the required multiple." msgstr "เมื่อการตั้งค่าภาพต้องการให้มิติภาพ\nถูกปัดค่าสู่บางเลขคูณของพิกเซล\nการตั้งค่านี้จะขลิบพิกเซลพิเศษเล็กน้อย\nแทนที่จะทำการขลิบอย่างแม่นยำ\nแล้วจึงสเกลสู่ผลคูณที่ต้องการ" msgid "width:" msgstr "กว้าง:" msgid "" "This is the width that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "นี่คือความกว้างที่วิดีโอจะถูกจัดเก็บไว้\nมิติการแสดงผลที่แท้จริงจะแตกต่างถ้าอัตราส่วนภาพพิกเซลไม่ใช่ 1:1" msgid "height:" msgstr "สูง:" msgid "" "This is the height that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "นี่คือความสูงที่วิดีโอจะถูกจัดเก็บไว้\nมิติการแสดงผลที่แท้จริงจะแตกต่างถ้าอัตราส่วนภาพพิกเซลไม่ใช่ 1:1" msgid "Optimal for source" msgstr "เหมาะสำหรับแหล่ง" msgid "" "If enabled, select the 'optimal' storage resolution.\n" "This will be the resolution that most closely matches the source resolution after cropping." msgstr "ถ้าถูกใช้งาน, เลือกความละเอียดการจัดเก็บที่ 'เหมาะสม'\nนี่จะเป็นความละเอียดที่ใกล้เคียงความละเอียดของแหล่งมากที่สุดหลังจากการขลิบ" msgid "" "Anamorphic Modes:\n" "\n" "None - Force pixel aspect ratio to 1:1.\n" "Loose - Align dimensions to chosen 'Alignment' value\n" " and pick pixel aspect ratio that preserves the\n" " original display aspect ratio\n" "Strict - Keep original source dimensions and pixel\n" " aspect ratio" msgstr "โหมด อนามอร์ฟิก:\n\nไม่มี - บังคับอัตราส่วนภาพเป็น 1:1\nหละหลวม - จัดวางมิติสู่ค่า 'แนวจัดวาง' ที่เลือก\n และหยิบอัตราส่วนภาพพิกเซลที่จะสงวน\n อัตราส่วนภาพเพื่อการแสดงผลของต้นฉบับ\nเข้มงวด - รักษามิติและอัตราส่วนภาพพิกเซล\n ของแหล่งต้นฉบับ" msgid "Alignment:" msgstr "แนวจัดวาง:" msgid "" "Align storage dimensions to multiples of this value.\n" "\n" "This setting is only necessary for compatibility with some devices.\n" "You should use 2 unless you experience compatibility issues." msgstr "จัดวางมิติการจัดเก็บสู่ผลคูณของค่านี้\n\nการตั้งค่านี้จำเป็นเฉพาะความเข้ากันกับบางอุปกรณ์\nคุณควรใช้ 2 เว้นแต่คุณประสบปัญหาความเข้ากัน" msgid "Storage Geometry" msgstr "เรขาคณิตการจัดเก็บ" msgid "" "This is the display width. It is the result of scaling the storage " "dimensions by the pixel aspect." msgstr "นี่คือความกว้างการแสดงผล มันคือผลของการสเกลมิติการจัดเก็บโดยสัดส่วนพิกเซล" msgid "Pixel Aspect:" msgstr "สัดส่วนพิกเซล:" msgid "" "Pixel aspect defines the shape of the pixels.\n" "\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "สัดส่วนพิกเซลจะกำหนดรูปทรงของพิกเซล\n\nอัตราส่วน 1:1 จะกำหนดพิกเซลสี่เหลี่ยมจัตุรัส ค่าอื่นๆจะกำหนดรูปทรงสี่เหลี่ยมผืนผ้า\nเครื่องเล่นจะสเกลภาพเพื่อที่จะให้ได้ตามสัดส่วนที่ระบุ" msgid ":" msgstr ":" msgid "" "Pixel aspect defines the shape of the pixels.\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "สัดส่วนพิกเซลจะกำหนดรูปทรงของพิกเซล\nอัตราส่วน 1:1 จะกำหนดพิกเซลสี่เหลี่ยมจัตุรัส ค่าอื่นๆจะกำหนดรูปทรงสี่เหลี่ยมผืนผ้า\nเครื่องเล่นจะสเกลภาพเพื่อที่จะให้ได้ตามสัดส่วนที่ระบุ" msgid "Keep Aspect" msgstr "คงสัดส่วน" msgid "" "If enabled, the original display aspect of the source will be maintained." msgstr "ถ้าใช้งาน สัดส่วนการแสดงผลต้นฉบับของแหล่งจะถูกคงไว้" msgid "Display Aspect:" msgstr "สัดส่วนแสดงผล:" msgid "Display Geometry" msgstr "เรขาคณิตการแสดงผล" msgid "Grayscale" msgstr "สเกลสีเทา" msgid "If enabled, filter colour components out of video." msgstr "ถ้าถูกใช้งาน จะกรององค์ประกอบสีออกจากวิดีโอ" msgid "Deblock:" msgstr "Deblock:" msgid "" "The deblocking filter removes a common type of compression artifact.\n" "If your source exhibits 'blockiness', this filter may help clean it up." msgstr "ตัวกรองการ Deblock จะลบชนิดธรรมดาของวัตถุการบีบอัด\nถ้าแหล่งคุณแสดงออกว่า 'blockiness', ตัวกรองนี้จะช่วยล้างมันออกไป" msgid "Denoise Filter:" msgstr "ตัวกรอง Denoise:" msgid "" "Denoise filtering reduces or removes the appearance of noise and grain.\n" "Film grain and other types of high frequency noise are difficult to compress.\n" "Using this filter on such sources can result in smaller file sizes." msgstr "การกรอง Denoise จะลดหรือลบการปรากฏของนอยส์และเกรน\nFilm grain และนอยส์ความถี่สูงชนิดอื่นจะยากต่อการบีบอัด\nการใช้ตัวกรองนี้กับบางแหล่งจะสามารถทำให้ขนาดไฟล์มีขนาดเล็กลงได้" msgid "Denoise Preset:" msgstr "ค่าสำเร็จรูป Denoise:" msgid "Denoise Tune:" msgstr "การจูน Denoise:" msgid "" "Custom denoise filter string format\n" "\n" "SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgstr "รูปแบบสตริงตัวกรอง denoise ที่กำหนดเอง\n\nSpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgid "Detelecine:" msgstr "Detelecine:" msgid "" "This filter removes 'combing' artifacts that are the result of telecining.\n" "\n" "Telecining is a process that adjusts film framerates that are 24fps to NTSC video frame rates which are 30fps." msgstr "ตัวกรองนี้จะลบวัตถุ 'หวี' ที่เป็นผลของการ telecining\n\nTelecining คือกระบวนการที่ปรับอัตราเฟรมหนัง 24fps เป็นอัตราเฟรมวิดีโอ NTSC ซึ่งเป็น 30fps" msgid "" "Custom detelecine filter string format\n" "\n" "JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgstr "รูปแบบสตริงตัวกรอง detelecine ที่กำหนดเอง\n\nJunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgid "Decomb" msgstr "Decomb" msgid "" "Choose decomb or deinterlace filter options.\n" "\n" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced.\n" "\n" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "เลือกตัวเลือกตัวกรอง decomb หรือ deinterlace\n\nตัวกรอง decomb จะ deinterlace เฟรมที่ปรากฏว่ามีการอินเตอร์เลซอย่างจำเพาะเจาะจง\nสิ่งนี้จะช่วยสงวนคุณภาพในเฟรมที่ไม่มีการอินเตอร์เลซ\n\nตัวกรอง deinterlace แบบคลาสสิคจะถูกใช้สำหรับเฟรมทั้งหมด\nเฟรมที่ไม่มีการอินเตอร์เลซจะได้รับผลกระทบทำให้คุณภาพลดลง" msgid "Deinterlace" msgstr "Deinterlace" msgid "Decomb:" msgstr "Decomb:" msgid "" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced." msgstr "ตัวกรอง decomb จะ deinterlace เฟรมที่ปรากฏว่าอินเตอร์เลซอย่างจำเพาะเจาะจง\nสิ่งนี้จะสงวนคุณภาพในเฟรมที่ไม่ถูกอินเตอร์เลซ" msgid "" "Custom decomb filter string format\n" "\n" "Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\n" "BlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\n" "ErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgstr "รูปแบบสตริงตัวกรอง decomb ที่กำหนดเอง\n\nMode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\nBlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\nErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgid "Deinterlace:" msgstr "Deinterlace:" msgid "" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "ตัวกรอง deinterlace คลาสสิค ถูกใช้งานกับเฟรมทั้งหมด\nเฟรมที่ไม่ถูกอินเตอร์เลซคุณภาพจะสูญเสียบางส่วน" msgid "" "Custom deinterlace filter string format\n" "\n" "YadifMode:YadifParity:McdintMode:McdeintQp" msgstr "รูปแบบสตริงตัวกรอง deinterlace ที่กำหนดเอง\n\nYadifMode:YadifParity:McdintMode:McdeintQp" msgid "Filters" msgstr "ตัวกรอง" msgid "Picture" msgstr "ภาพ" msgid "Video Encoder:" msgstr "ตัวเข้ารหัสวิดีโอ:" msgid "Available video encoders." msgstr "ตัวเข้ารหัสวิดีโอที่มี" msgid "Framerate:" msgstr "อัตราเฟรม:" msgid "" "Output framerate.\n" "\n" "'Same as source' is recommended. If your source video has\n" "a variable framerate, 'Same as source' will preserve it." msgstr "อัตราเฟรมผลลัพธ์\n\n'เหมือนกับแหล่ง' จะถูกแนะนำ ถ้าวิดีโอแหล่งของคุณมีอัตราเฟรมที่แปรผัน, 'เหมือนกับแหล่ง' จะคงค่ามันเอาไว้" msgid "Constant Framerate" msgstr "อัตราเฟรมคงที่" msgid "Same as source" msgstr "เหมือนกับแหล่ง" msgid "kbps" msgstr "kbps" msgid "(variable)" msgstr "(แปรผัน)" msgid "(constant)" msgstr "(คงที่)" msgid "Enables constant framerate output." msgstr "เปิดใช้งานผลลัพท์อัตราเฟรมที่คงที่" msgid "Peak Framerate (VFR)" msgstr "อัตราเฟรมสูงสุด (VFR)" msgid "" "Enables variable framerate output with a peak\n" "rate determined by the framerate setting.\n" "\n" "VFR is not compatible with some players." msgstr "ใช้งานผลลัพธ์อัตราเฟรมแปรผันกับอัตราสูงสุด\nที่บ่งโดยการตั้งค่าอัตราเฟรม\n\nอัตราเฟรมแปรผันหรือ VFR จะไม่เข้ากับบางเครื่องเล่น" msgid "Variable Framerate" msgstr "อัตราเฟรมแปรผัน" msgid "" "Enables variable framerate output.\n" "\n" "VFR is not compatible with some players." msgstr "ใช้งานผลลัพธ์อัตราเฟรมแปรผัน\n\nอัตราเฟรมแปรผันหรือ VFR จะไม่เข้ากับบางเครื่องเล่น" msgid "" "Set the desired quality factor.\n" "The encoder targets a certain quality.\n" "The scale used by each video encoder is different.\n" "\n" "x264's scale is logarithmic and lower values correspond to higher quality.\n" "So small decreases in value will result in progressively larger increases\n" "in the resulting file size. A value of 0 means lossless and will result\n" "in a file size that is larger than the original source, unless the source\n" "was also lossless.\n" "\n" "FFMpeg's and Theora's scale is more linear.\n" "These encoders do not have a lossless mode." msgstr "กำหนดแฟกเตอร์คุณภาพที่ต้องการ\nตัวเข้ารหัสจะมุ่งเป้าหมายคุณภาพที่แน่นอน\nสเกลที่ใช้โดยแต่ละตัวเข้ารหัสวิดีโอจะต่างกัน\n\nสเกลของ x264 เป็นอัลกอริทึมซึ่งค่าที่ต่ำลงจะสอดคล้องกับคุณภาพที่สูงขึ้น\nดังนั้นค่าที่ลดลงเล็กน้อยจะส่งผลต่อขนาดไฟล์ผลลัพธ์ที่ใหญ่ขึ้นมาก\nค่า 0 หมายถึงไม่สูญเสียและขนาดไฟล์ผลลัพธ์จะใหญ่กว่า\nไฟล์แหล่งต้นฉบับ เว้นแต่ไฟล์แหล่งก็เป็นแบบไม่สูญเสีย\n\nสเกลของ FFMpeg และ Theora จะเป็นเส้นตรงมากกว่า\nตัวเข้ารหัสเหล่านี้ไม่มีโหมดไม่สูญเสีย" msgid "Constant Quality:" msgstr "คุณภาพคงที่:" msgid "Bitrate (kbps): " msgstr "อัตราบิต (kbps): " msgid "" "Set the average bitrate.\n" "\n" "The instantaneous bitrate can be much higher or lower at any point in time.\n" "But the average over a long duration will be the value set here. If you need\n" "to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings." msgstr "กำหนดอัตราบิตเฉลี่ย\n\nอัตราบิตฉับพลันสามารถสูงกว่าหรือต่ำกว่ามาก ณ ในเวลาใดๆ\nแต่ค่าเฉลี่ยของระยะเวลาที่ยาวนานจะเป็นค่าที่กำหนดที่นี่\nถ้าคุณต้องการจำกัดอัตราบิตฉับพลัน ดูที่ x264's vbv-bufsize และการตั้งค่า vbv-maxrate" msgid "2-Pass Encoding" msgstr "การเข้ารหัส 2-Pass" msgid "" "Perform 2 Pass Encoding.\n" "\n" "The 'Bitrate' option is prerequisite. During the 1st pass, statistics about\n" "the video are collected. Then in the second pass, those statistics are used\n" "to make bitrate allocation decisions." msgstr "กระทำการเข้ารหัส 2 Pass\n\nต้องทำตัวเลือก 'อัตราบิต' ก่อน ในระหว่างการผ่านรอบที่ 1 สถิติเกี่ยวกับวิดีโอ\nจะถูกรวบรวม จากนั้นทำการผ่านรอบที่สอง สถิติเหล่านั้นจะถูกใช้เพื่อ\nการตัดสินจัดสรรอัตราบิต" msgid "Turbo First Pass" msgstr "ผ่านแรกเทอร์โบ" msgid "" "During the 1st pass of a 2 pass encode, use settings that speed things " "along." msgstr "ในระหว่างเข้ารหัส 1st pass ของ 2 pass, ใช้การตั้งค่าที่เร่งสิ่งต่างๆไปด้วยกัน" msgid "Use Advanced Options" msgstr "ใช้ตัวเลือกขั้นสูง" msgid "" "Use advanced options Tab for x264 settings.\n" "\n" "Use at your own risk!" msgstr "ใช้แท็บตัวเลือกขั้นสูงสำหรับการตั้งค่า x264\n\nใช้ตามที่คุณอยากเสี่ยง!" msgid "Preset:" msgstr "ค่าสำเร็จรูป:" msgid "" "Adjusts encoder settings to trade off compression efficiency against encoding speed.\n" "\n" "This establishes your default encoder settings.\n" "Tunes, profiles, levels and advanced option string will be applied to this.\n" "You should generally set this option to the slowest you can bear since slower\n" "settings will result in better quality or smaller files." msgstr "" msgid "Tune:" msgstr "จูน:" msgid "" "Tune settings to optimize for common scenarios.\n" "\n" "This can improve effeciency for particular source characteristics or set\n" "characteristics of the output file. Changes will be applied after the\n" "preset but before all other parameters." msgstr "การตั้งค่าจูนเพื่อปรับบทหนังธรรมดาให้เหมาะสม\n\nสิ่งนี้สามารถเพิ่มประสิทธิภาพลักษณะเฉพาะของแหล่งที่จำเพาะ หรือ\nกำหนดลักษณะเฉพาะของไฟล์ผลลัพธ์ การเปลี่ยนแปลงจะถูกใช้\nหลังค่าสำเร็จรูปแต่จะใช้ก่อนพารามิเตอร์อื่นๆ" msgid "Fast Decode" msgstr "ถอดรหัสรวดเร็ว" msgid "" "Reduce decoder CPU usage.\n" "\n" "Set this if your device is struggling to play the output (dropped frames)." msgstr "ลดการใช้ CPU ของตัวถอดรหัส\n\nกำหนดสิ่งนี้ถ้าอุปกรณ์ดิ้นรนเพื่อเล่นไฟล์ผลลัพธ์ (เฟรมที่ตก)" msgid "Zero Latency" msgstr "การแฝงเป็นศูนย์" msgid "" "Minimize latency between input to encoder and output of decoder.\n" "\n" "This is useful for broadcast of live streams.\n" "\n" "Since HandBrake is not suitable for live stream broadcast purposes,\n" "this setting is of little value here." msgstr "" msgid "Profile:" msgstr "โปรไฟล์:" msgid "" "Sets and ensures compliance with the specified profile.\n" "\n" "Overrides all other settings." msgstr "กำหนดและทำให้มั่นใจว่าทำตามโปรไฟล์ที่ระบุ\n\nมีผลเหนือการตั้งค่าอื่นทั้งหมด" msgid "Level:" msgstr "ระดับ:" msgid "" "Sets and ensures compliance with the specified level.\n" "\n" "Overrides all other settings." msgstr "กำหนดและทำให้มั่นใจว่าทำตามระดับที่ระบุ\n\nมีผลเหนือการตั้งค่าอื่นทั้งหมด" msgid "More Settings:" msgstr "การตั้งค่าเพิ่มเติม:" msgid "" "Additional encoder settings.\n" "\n" "Colon separated list of encoder options." msgstr "การตั้งค่าตัวเข้ารหัสเพิ่มเติม\n\nรายการที่คั่นด้วยเครื่องหมาย Colon ของตัวเลือกตัวเข้ารหัส" msgid "Video" msgstr "วิดีโอ" msgid "Selection Behavior:" msgstr "พฤติกรรมการเลือก:" msgid "Remove" msgstr "เอาออก" msgid "Available Languages" msgstr "ภาษาที่มี" msgid "Selected Languages" msgstr "ภาษาที่เลือก" msgid "Use only first encoder for secondary audio" msgstr "ใช้เฉพาะตัวเข้ารหัสแรกสำหรับเสียงที่สอง" msgid "" "Only the primary audio track will be encoded with the full encoder list.\n" "All other secondary audio output tracks will be encoded with first encoder only." msgstr "เฉพาะแทร็คเสียงหลักที่จะถูกเข้ารหัสด้วยรายการตัวเข้ารหัสทั้งหมด\nแทร็คผลลัพธ์ของเสียงสำรองทั้งหมดจะถูกเข้ารหัสด้วยตัวเข้ารหัสแรกเท่านั้น" msgid "Auto Passthru:" msgstr "ส่งผ่านอัตโนมัติ:" msgid "MP3" msgstr "MP3" msgid "" "Enable this if your playback device supports MP3.\n" "This permits MP3 passthru to be selected when automatic passthru selection is enabled." msgstr "ใช้งานสิ่งนี้ ถ้าอุปกรณ์การเล่นของคุณรองรับ MP3\nสิ่งนี้ยินยอมให้เลือกการส่งผ่าน MP3 เมื่อการเลือกการส่งผ่านอัตโนมัติถูกเปิดใช้งาน" msgid "" "Enable this if your playback device supports AAC.\n" "This permits AAC passthru to be selected when automatic passthru selection is enabled." msgstr "ใช้งานสิ่งนี้ ถ้าอุปกรณ์การเล่นของคุณรองรับ AAC\nสิ่งนี้ยินยอมให้เลือกการส่งผ่าน AAC เมื่อการเลือกการส่งผ่านอัตโนมัติถูกเปิดใช้งาน" msgid "" "Enable this if your playback device supports AC-3.\n" "This permits AC-3 passthru to be selected when automatic passthru selection is enabled." msgstr "ใช้งานสิ่งนี้ ถ้าอุปกรณ์การเล่นของคุณรองรับ AC-3\nสิ่งนี้ยินยอมให้เลือกการส่งผ่าน AC-3 เมื่อการเลือกการส่งผ่านอัตโนมัติถูกเปิดใช้งาน" msgid "" "Enable this if your playback device supports DTS.\n" "This permits DTS passthru to be selected when automatic passthru selection is enabled." msgstr "ใช้งานสิ่งนี้ ถ้าอุปกรณ์การเล่นของคุณรองรับ DTS\nสิ่งนี้ยินยอมให้เลือกการส่งผ่าน DTS เมื่อการเลือกการส่งผ่านอัตโนมัติถูกเปิดใช้งาน" msgid "" "Enable this if your playback device supports DTS-HD.\n" "This permits DTS-HD passthru to be selected when automatic passthru selection is enabled." msgstr "ใช้งานสิ่งนี้ ถ้าอุปกรณ์การเล่นของคุณรองรับ DTS-HD\nสิ่งนี้ยินยอมให้เลือกการส่งผ่าน DTS-HD เมื่อการเลือกการส่งผ่านอัตโนมัติถูกเปิดใช้งาน" msgid "Passthru Fallback:" msgstr "การส่งผ่านยืนพื้น:" msgid "" "Set the audio codec to encode with when a suitable track can not be found " "for audio passthru." msgstr "กำหนด codec เสียงเพื่อเข้ารหัส เมื่อไม่พบแทร็คที่เหมาะสมสำหรับการส่งผ่านเสียง" msgid "Audio Encoder Settings:" msgstr "การตั้งค่าตัวเข้ารหัสเสียง:" msgid "Each selected source track will be encoded with all selected encoders" msgstr "แต่ละแทร็คแหล่งที่เลือกจะถูกเข้ารหัสด้วยตัวเข้ารหัสที่เลือกทั้งหมด" msgid "Encoder" msgstr "ตัวเข้ารหัส" msgid "Bitrate/Quality" msgstr "อัตราบิต/คุณภาพ" msgid "Mixdown" msgstr "ปรับลง" msgid "Samplerate" msgstr "อัตราสุ่มฯ" msgid "Gain" msgstr "ฟื้นค่า" msgid "Audio Defaults" msgstr "ค่าตั้งต้นเสียง" msgid "Add new audio settings to the list" msgstr "เพิ่มการตั้งค่าเสียงใหม่สู่รายการ" msgid "Add All" msgstr "เพิ่มทั้งหมด" msgid "Add all audio tracks to the list" msgstr "เพิ่มแทร็คเสียงทั้งหมดสู่รายการ" msgid "Reload Defaults" msgstr "โหลดค่าตั้งต้นใหม่" msgid "Reload all audio settings from defaults" msgstr "โหลดการตั้งค่าเสียงทั้งหมดใหม่จากค่าตั้งต้น" msgid "Audio List" msgstr "รายการเสียง" msgid "Preferred Language: None" msgstr "ภาษาที่ชอบ: ไม่มี" msgid "Add Foreign Audio Search Pass" msgstr "เพิ่มการส่งผ่านการค้นหาเสียงต่างประเทศ" msgid "" "Add \"Foreign Audio Search\" when the default audio track is your preferred language.\n" "This search pass finds short sequences of foreign audio and provides subtitles for them." msgstr "เพิ่ม \"การค้นหาเสียงต่างประเทศ\" เมื่อแทร็คเสียงตั้งต้นเป็นเสียงภาษาที่คุณชอบ\nการส่งผ่านการค้นหานี้จะค้นลำดับของเสียงต่างประเทศและเตรียมศัพท์บรรยายสำหรับพวกมัน" msgid "Add subtitle track if default audio is foreign" msgstr "เพิ่มแทร็คศัพท์บรรยาย ถ้าเสียงตั้งต้นเป็นเสียงต่างประเทศ" msgid "" "When the default audio track is not your preferred language, add a subtitle " "track." msgstr "เพิ่มแทร็คศัพท์บรรยายเมื่อแทร็คเสียงตั้งต้นไม่ใช่ภาษาที่คุณชอบ" msgid "Add Closed Captions when available" msgstr "เพิ่มศัพท์บรรยายเหตุการณ์ถ้ามี" msgid "" "Closed captions are text subtitles that can be added to any container as a " "soft subtitle track (not burned)" msgstr "ศัพท์บรรยายเหตุการณ์เป็นศัพท์ฯตัวอักษร ซึ่งสามารถเพิ่มลงในตัวบรรจุใดก็ได้โดยการเป็นแทร็คศัพท์บรรยายชนิดอ่อน (ไม่ถูกประทับ)" msgid "Subtitle Defaults" msgstr "ค่าตั้งต้นศัพท์บรรยาย" msgid "Add new subtitle settings to the list" msgstr "เพิ่มการตั้งค่าศัพท์บรรยายใหม่สู่รายการ" msgid "Add all subtitle tracks to the list" msgstr "เพิ่มแทร็คศัพท์บรรยายทั้งหมดสู่รายการ" msgid "Reload all subtitle settings from defaults" msgstr "โหลดการตั้งค่าศัพท์บรรยายทั้งหมดใหม่จากค่าตั้งต้น" msgid "Subtitle List" msgstr "รายการศัพท์บรรยาย" msgid "Reference Frames:" msgstr "เฟรมอ้างอิง:" msgid "" "Sane values are ~1-6. The more you add, the better the compression, but the slower the encode.\n" "Cel animation tends to benefit from more reference frames a lot more than film content.\n" "\n" "Note that many hardware devices have limitations on the number of supported reference\n" "frames, so if you're encoding for a handheld or standalone player, don't touch this unless\n" "you're absolutely sure you know what you're doing!" msgstr "" msgid "Maximum B-Frames:" msgstr "เฟรม-B สูงสุด:" msgid "" "Sane values are ~2-5. This specifies the maximum number of sequential B-frames that the encoder can use.\n" "\n" "Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.\n" "Cel-animated source material and B-pyramid also significantly increase the usefulness of larger\n" "values.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off)." msgstr "" msgid "Pyramidal B-Frames:" msgstr "เฟรม-B พีรามิด:" msgid "" "B-pyramid improves compression by creating a pyramidal structure (hence the name)\n" "of B-frames, allowing B-frames to reference each other to improve compression.\n" "\n" "Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit." msgstr "พีรามิด-B ปรับปรุงการบีบอัดโดยสร้างโครงสร้างพีรามิด (เช่นดังชื่อ)\nของเฟรม-B, ช่วยให้เฟรม-B อิงอ้างสู่แต่ละอันอื่นเพื่อปรับปรุงการบีบอัด\n\nต้องการเฟรม-B สูงสุดมากกว่า 1; เฟรม-B ที่ดัดแปลงเหมาะสม ถูกแนะนำสำหรับการบีบอัดให้ได้ผลสูงสุด" msgid "Weighted P-Frames:" msgstr "" msgid "" "Performs extra analysis to decide upon weighting parameters for each frame.\n" "\n" "This improves overall compression slightly and improves the quality of fades greatly.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires weighted P-frame\n" "prediction to be disabled. Note that some devices and players, even those that support\n" "Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is\n" "completely incompatible with it, for example." msgstr "" msgid "8x8 Transform" msgstr "แปลง 8x8" msgid "" "The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.\n" "\n" "It improves compression by at least 5% at a very small speed cost and may\n" "provide an unusually high visual quality benefit compared to its compression\n" "gain. However, it requires High Profile, which many devices may not support." msgstr "การแปลง 8x8 คือคุณลักษณะเดียวที่มีประโยชน์สูงสุดของ x264 ในเงื่อนไขของการบีบอัดต่อความเร็ว\n\nมันปรับปรุงการบีบอัดอย่างน้อย 5% ที่ต้นทุนความเร็วน้อยมากและอาจ\nให้คุณภาพการมองเห็นสูงอย่างไม่ธรรมดาเปรียบเทียบกับการบีบอัดของมันที่ได้ อย่างไรก็ตาม มันต้องการโปรไฟล์รูปแบบสูง ซึ่งบางอุปกรณ์อาจไม่รองรับ" msgid "CABAC Entropy Encoding" msgstr "การเข้ารหัส CABAC Entropy" msgid "" "After the encoder has done its work, it has a bunch of data that\n" "needs to be compressed losslessly, similar to ZIP or RAR. H.264 provides\n" "two options for this: CAVLC and CABAC. CABAC decodes a lot slower but\n" "compresses significantly better (10-30%), especially at lower bitrates.\n" "\n" "If you're looking to minimize CPU requirements for video playback, disable this option.\n" "Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled." msgstr "" msgid "Encoding Features" msgstr "คุณลักษณะการเข้ารหัส" msgid "Motion Est. Method:" msgstr "วิธีการประมาณการเคลื่อนไหว:" msgid "" "Controls the motion estimation method.\n" "\n" "Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.\n" "A better motion search method improves compression at the cost of speed.\n" "\n" "Diamond: performs an extremely fast and simple search using a diamond pattern.\n" "Hexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n" "Uneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n" "Exhaustive: performs a \"dumb\" search of every pixel in a wide area. Significantly slower for only a small compression gain.\n" "Transformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement." msgstr "" msgid "Subpel ME & Mode:" msgstr "" msgid "" "This setting controls both subpixel-precision motion estimation and mode decision methods.\n" "\n" "Subpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n" "Mode decision is the method used to choose how to encode each block of the frame: a very important decision.\n" "SAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n" "6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n" "11 disables all early terminations in analysis.\n" "10 and 11, the most powerful and slowest options, require adaptive quantization (aq-mode > 0) and trellis 2 (always)." msgstr "" msgid "Motion Est. Range:" msgstr "ช่วงการประมาณการเคลื่อนไหว:" msgid "" "This is the distance x264 searches from its initial guess at the\n" "motion of a block in order to try to find its actual motion.\n" "\n" "The default is fine for most content, but extremely high motion video,\n" "especially at HD resolutions, may benefit from higher ranges, albeit at\n" "a high speed cost." msgstr "" msgid "Adaptive Direct Mode:" msgstr "" msgid "" "H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n" "\n" "Spatial, the default, is almost always better, but temporal is sometimes useful too.\n" "x264 can, at the cost of a small amount of speed (and accordingly for a small compression gain),\n" "adaptively select which is better for each particular frame." msgstr "" msgid "Adaptive B-Frames:" msgstr "" msgid "" "x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n" "\n" "Fast mode takes roughly the same amount of time no matter how many B-frames you specify.\n" "However, while fast, its decisions are often suboptimal.\n" "\n" "Optimal mode gets slower as the maximum number of B-Frames increases,\n" "but makes much more accurate decisions, especially when used with B-pyramid." msgstr "" msgid "Partitions:" msgstr "" msgid "" "Mode decision picks from a variety of options to make its decision:\n" "this option chooses what options those are.\n" "\n" "Fewer partitions to check means faster encoding, at the cost of worse\n" "decisions, since the best option might have been one that was turned off." msgstr "" msgid "Trellis:" msgstr "" msgid "" "Trellis fine-tunes the rounding of transform coefficients to\n" "squeeze out 3-5% more compression at the cost of some speed.\n" "\n" "\"Always\" uses trellis not only during the main encoding process, but also\n" "during analysis, which improves compression even more, albeit at great speed cost.\n" "\n" "Trellis costs more speed at higher bitrates and requires CABAC." msgstr "" msgid "Analysis" msgstr "การวิเคราะห์" msgid "Adaptive Quantization Strength:" msgstr "" msgid "" "Adaptive quantization controls how the encoder distributes bits across the frame.\n" "\n" "Higher values take more bits away from edges and complex areas to improve areas with finer detail." msgstr "" msgid "Psychovisual Rate Distortion:" msgstr "" msgid "" "Psychovisual rate-distortion optimization takes advantage of the characteristics of human\n" "vision to dramatically improve apparent detail and sharpness.\n" "The effect can be made weaker or stronger by adjusting the strength.\n" "Being an RD algorithm, it requires mode decision to be at least \"6\"." msgstr "" msgid "Psychovisual Trellis:" msgstr "" msgid "" "Psychovisual trellis is an experimental algorithm to further\n" "improve sharpness and detail retention beyond what Psychovisual RD does.\n" "\n" "Recommended values are around 0.2, though higher values may help for very\n" "grainy video or lower bitrate encodes. Not recommended for cel animation\n" "and other sharp-edged graphics." msgstr "" msgid "Deblocking: " msgstr "Deblocking: " msgid "" "H.264 deblocking filter.\n" "\n" "h.264 has a built-in deblocking filter that smooths out blocking artifacts\n" "after decoding each frame. This not only improves visual quality, but also\n" "helps compression significantly. The deblocking filter takes a lot of CPU power,\n" "so if you're looking to minimize CPU requirements for video playback, disable it.\n" "\n" "The deblocking filter has two adjustable parameters, \"strength\" (Alpha) and \"threshold\" (Beta).\n" "The former controls how strong (or weak) the deblocker is, while the latter controls how many\n" "(or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking.\n" "The default is 0 (normal strength) for both parameters." msgstr "" msgid "No DCT Decimate" msgstr "" msgid "" "x264 normally zeroes out nearly-empty data blocks to save bits to\n" "be better used for some other purpose in the video. However, this can\n" "sometimes have slight negative effects on retention of subtle grain and\n" "dither.\n" "\n" "Don't touch this unless you're having banding issues or other such cases\n" "where you are having trouble keeping fine noise." msgstr "" msgid "Psychovisual" msgstr "" msgid "" "Your selected options will appear here.\n" "You can edit these and add additional options.\n" "\n" "Default values will not be shown. The defaults are:\n" "ref=3:bframes=3:b-adapt=fast:direct=spatial:\n" "b-pyramid=normal:weightp=2:me=hex:merange=16:\n" "subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\n" "deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\n" "no-fast-pskip=0:no-dct-decimate=0:cabac=1" msgstr "" msgid "Current x264 Advanced Option String" msgstr "สตริงตัวเลือกขั้นสูง x264 ปัจจุบัน" msgid "Advanced Video" msgstr "วิดีโอขั้นสูง" msgid "Chapter Markers" msgstr "ตัวหมายตอน" msgid "Add chapter markers to output file." msgstr "เพิ่มตัวหมายตอนสู่ไฟล์ผลลัพธ์" msgid "Chapters" msgstr "ตอน" msgid "Actors:" msgstr "นักแสดง:" msgid "Director:" msgstr "ผู้กำกับ:" msgid "Release Date:" msgstr "วันเผยแพร่:" msgid "Comment:" msgstr "ข้อคิดเห็น:" msgid "Genre:" msgstr "ประเภท:" msgid "Description:" msgstr "คำบรรยาย:" msgid "Plot:" msgstr "เค้าเรื่อง:" msgid "Tags" msgstr "แท็ก" msgid "Settings" msgstr "การตั้งค่า" msgid "Edit" msgstr "แก้ไข" msgid "Reload" msgstr "โหลดใหม่" msgid "" "Mark selected queue entry as pending.\n" "Resets the queue job to pending and ready to run again." msgstr "หมายเหตุรายการคิวที่เลือกเป็นกำลังรอคอย\nคืนค่างานคิวเป็นกำลังรอคอย และพร้อมจะปฏิบัติการอีกครั้ง" msgid "Reload All" msgstr "โหลดใหม่ทั้งหมด" msgid "" "Mark all queue entries as pending.\n" "Resets all queue jobs to pending and ready to run again." msgstr "หมายเหตุรายการคิวทั้งหมดเป็นกำลังรอคอย\nคืนค่างานคิวทั้งหมดเป็นกำลังรอคอย และพร้อมจะปฏิบัติการอีกครั้ง" msgid "OK" msgstr "ตกลง" msgid "Select All" msgstr "เลือกทั้งหมด" msgid "Mark all titles for adding to the queue" msgstr "หมายหัวเรื่องทั้งหมดสำหรับเพิ่มสู่คิว" msgid "Clear All" msgstr "ล้างทั้งหมด" msgid "Unmark all titles" msgstr "เลิกหมายหัวเรื่องทั้งหมด" msgid "Destination files OK. No duplicates detected." msgstr "ไฟล์ปลายทางโอเค ไม่พบการซ้ำซ้อน" msgid "Select this title for adding to the queue.\n" msgstr "เลือกหัวเรื่องนี้เพื่อเพิ่มสู่คิว\n" msgid "Preferences" msgstr "การตั้งค่า" msgid "Automatically check for updates" msgstr "ตรวจสอบการอัปเดตอัตโนมัติ" msgid "When all encodes are complete" msgstr "เมื่อการเข้ารหัสทั้งหมดเสร็จสมบูรณ์" msgid "Use automatic naming (uses modified source name)" msgstr "ใช้การตั้งชื่ออัตโนมัติ (ใช้ชื่อแหล่งที่ปรับเปลี่ยน)" msgid "Auto-Name Template" msgstr "แม่แบบชื่ออัตโนมัติ" msgid "" "Available Options: {source} {title} {chapters} {date} {time} {quality} " "{bitrate}" msgstr "ตัวเลือกที่มี: {แหล่ง} {หัวเรื่อง} {ตอน} {วันที่} {เวลา} {คุณภาพ} {อัตราบิต}" msgid "Use iPod/iTunes friendly (.m4v) file extension for MP4" msgstr "ใช้สกุลไฟล์ (.m4v) ซึ่งเหมาะกับ iPod/iTunes สำหรับรูปแบบ MP4" msgid "Number of previews" msgstr "จำนวนตัวอย่าง" msgid "Filter short titles (seconds)" msgstr "กรองหัวเรื่องสั้น (วินาที)" msgid "Show system tray icon" msgstr "แสดงไอคอนที่ถาดระบบ" msgid "General" msgstr "ทั่วไป" msgid "Constant Quality fractional granularity" msgstr "" msgid "Use dvdnav (instead of libdvdread)" msgstr "ใช้ dvdnav (แทน libdvdread)" msgid "Put individual encode logs in same location as movie" msgstr "วางบันทึกกิจกรรมการเข้ารหัสรายตัว ในที่ตั้งเดียวกับภาพยนตร์" msgid "Activity Log Verbosity Level" msgstr "ระดับความมากของการจดบันทึกกิจกรรม" msgid "Activity Log Longevity" msgstr "ความยืนนานของการจดบัทึกกิจกรรม" msgid "Scale down High Definition previews" msgstr "สเกลตัวอย่างที่มีความละเอียดสูงให้ลดลง" msgid "Automatically Scan DVD when loaded" msgstr "อ่าน DVD อัตโนมัติเมื่อถูกโหลด" msgid "Scans the DVD whenever a new disc is loaded" msgstr "อ่าน DVD เมื่อใดก็ตามที่ดิสก์ใหม่ถูกโหลด" msgid "Hide Advanced Video Options Tab" msgstr "ซ่อนแท็บตัวเลือกวิดีโอขั้นสูง" msgid "" "Use advanced video options at your own risk.\n" "We recommend that you use the controls available\n" "on the Video tab instead." msgstr "" msgid "Delete completed jobs from queue" msgstr "ลบงานที่เสร็จแล้วจากคิว" msgid "" "By default, completed jobs remain in the queue and are marked as complete.\n" "Check this if you want the queue to clean itself up by deleting completed jobs." msgstr "โดยตั้งต้น, งานที่เสร็จแล้วจะยังคงอยู่ในคิว และถูกหมายเหตุว่าเสร็จสมบูรณ์\nกาเลือกสิ่งนี้ถ้าคุณต้องการให้คิวทำการลบงานที่เสร็จแล้วออกไปเลย" msgid "Allow Tweaks" msgstr "อนุญาตการปรับแต่ง" msgid "Allow HandBrake For Dummies" msgstr "อนุญาต HandBrake For Dummies" msgid "Advanced" msgstr "ขั้นสูง" msgid "Folder Name:" msgstr "ชื่อโฟลเดอร์:" msgid "Description" msgstr "คำบรรยาย" msgid "Preset Name:" msgstr "ชื่อค่าสำเร็จรูป:" msgid "Custom Picture Dimensions" msgstr "มิติภาพที่กำหนดเอง" msgid "Maximum Width:" msgstr "ความกว้างสูงสุด:" msgid "Enable maximum width limit." msgstr "ใช้การจำกัดความกว้างสูงสุด" msgid "" "This is the maximum width that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source width is greater.\n" "Setting this to 0 means there is no maximum width." msgstr "นี่คือความกว้างสูงสุดที่วิดีโอจะถูกจัดเก็บ\n\nเมื่อใดก็ตามที่แหล่งใหม่ถูกโหลด ค่านี้จะถูกใช้ถ้าความกว้างของแหล่งมากกว่า\nการตั้งค่าสิ่งนี้เป็น 0 หมายถึงไม่มีความกว้างสูงสุด" msgid "Maximum Height:" msgstr "ความสูงสูงสุด:" msgid "Enable maximum height limit." msgstr "ใช้การจำกัดความสูงสูงสุด" msgid "" "This is the maximum height that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source height is greater.\n" "Setting this to 0 means there is no maximum height." msgstr "นี่คือความสูงสูงสุดที่วิดีโอจะถูกจัดเก็บ\n\nเมื่อใดก็ตามที่แหล่งใหม่ถูกโหลด ค่านี้จะถูกใช้ถ้าความสูงของแหล่งมากกว่า\nการตั้งค่าสิ่งนี้เป็น 0 หมายถึงไม่มีความสูงสูงสุด" msgid "Select preview frames." msgstr "เลือกเฟรมดูตัวอย่าง" msgid "" "Encode and play a short sequence of video starting from the current preview " "position." msgstr "เข้ารหัสและเล่นช่วงสั้นๆของวิดีโอเริ่มจากตำแหน่งตัวอย่างปัจจุบัน" msgid "Duration:" msgstr "ระยะเวลา:" msgid "Set the duration of the live preview in seconds." msgstr "กำหนดระยะเวลาของตัวอย่างสดเป็นวินาที" msgid "Show Crop" msgstr "แสดงการขลิบ" msgid "Show Cropped area of the preview" msgstr "แสดงพื้นที่ที่ถูกขลิบของตัวอย่าง" msgid "Fullscreen" msgstr "เต็มจอ" msgid "View Fullscreen Preview" msgstr "ดูตัวอย่างเต็มจอ" msgid "Title Number:" msgstr "หมายเลขหัวเรื่อง:" msgid "Detected DVD devices:" msgstr "อุปกรณ์ DVD ที่ตรวจพบ:" msgid "Setting:" msgstr "การตั้งค่า:" msgid "Import SRT" msgstr "นำเข้า SRT" msgid "Enable settings to import an SRT subtitle file" msgstr "ใช้งานการตั้งค่าเพื่อนำเข้าไฟล์ศัพท์บรรยายชนิด SRT" msgid "Embedded Subtitle List" msgstr "รายการศัพท์บรรยายที่ถูกฝัง" msgid "Enable settings to select embedded subtitles" msgstr "ใช้งานการตั้งค่าเพื่อเลือกศัพท์บรรยายที่ถูกฝัง" msgid "Character Code" msgstr "รหัสอักขระ" msgid "Offset (ms)" msgstr "ค่าชดเชย (ms)" msgid "" "Set the language of this subtitle.\n" "This value will be used by players in subtitle menus." msgstr "กำหนดภาษาของศัพท์บรรยายนี้\nค่านี้จะถูกใช้โดยเครื่องเล่นในเมนูศัพท์บรรยาย" msgid "" "Set the character code used by the SRT file you are importing.\n" "\n" "SRTs come in all flavours of character sets.\n" "We translate the character set to UTF-8.\n" "The source's character code is needed in order to perform this translation." msgstr "กำหนดรหัสอักขระที่ใช้โดยไฟล์ SRT ที่คุณกำลังนำเข้า\n\nSRTs มาในรูปแบบทั้งหมดของชุดอักขระ\nเราแปลชุดอักขระเป็น UTF-8\nรหัสอักขระของแหล่งจึงจำเป็นเพื่อที่จะกระทำการแปลนี้" msgid "Select the SRT file to import." msgstr "เลือกไฟล์ SRT เพื่อนำเข้า" msgid "Srt File" msgstr "ไฟล์ Srt" msgid "Adjust the offset in milliseconds between video and SRT timestamps" msgstr "ปรับค่าชดเชยเป็นมิลลิวินาทีระหว่างค่าเวลาของวิดีโอและ SRT" msgid "Track" msgstr "แทร็ค" msgid "List of subtitle tracks available from your source." msgstr "รายการแทร็คศัพท์บรรยายที่มีจากแหล่งของคุณ" msgid "Forced Subtitles Only" msgstr "เฉพาะศัพท์บรรยายที่ถูกบังคับ" msgid "Burn into video" msgstr "ประทับลงในวิดีโอ" msgid "" "Render the subtitle over the video.\n" "The subtitle will be part of the video and can not be disabled." msgstr "แสดงผลศัพท์บรรยายบนวิดีโอ\nศัพท์บรรยายจะเป็นส่วนหนึ่งของวิดีโอและไม่สามารถปิดการใช้งานได้" msgid "Set Default Track" msgstr "กำหนดแทร็คตั้งต้น" msgid "Source Track" msgstr "แทร็คแหล่ง" msgid "List of audio tracks available from your source." msgstr "รายการแทร็คเสียงที่มีจากแหล่งของคุณ" msgid "Track Name:" msgstr "ชื่อแทร็ค:" msgid "" "Set the audio track name.\n" "\n" "Players may use this in the audio selection list." msgstr "กำหนดชื่อแทร็คเสียง\n\nเครื่องเล่นอาจใช้สิ่งนี้ในรายการเพื่อเลือกเสียง" msgid "Mix" msgstr "ผสม" msgid "Sample Rate" msgstr "อัตราสุ่มฯ" msgid "" "Dynamic Range Compression: Adjust the dynamic range of the output audio track.\n" "\n" "For source audio that has a wide dynamic range (very loud and very soft sequences),\n" "DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder." msgstr "การบีบอัดช่วงแบบจลน์: ปรับช่วงจลน์ของแทร็คเสียงผลลัพธ์\n\nสำหรับเสียงแหล่งที่มีช่วงจลน์กว้าง (มีการดังมากและเบามาก),\nDRC อนุญาตให้คุณ 'บีบอัด' ช่วง โดยการทำให้เสียงที่ดังเบาลง และทำให้เสียงที่เบาดังขึ้น" msgid "Enable bitrate setting" msgstr "ใช้งานการตั้งค่าอัตราบิต" msgid "Enable quality setting" msgstr "ใช้งานการตั้งค่าคุณภาพ" msgid "" "Quality: For output codec's that support it, adjust the quality of " "the output." msgstr "คุณภาพ: สำหรับ codec ผลลัพธ์ที่รองรับมัน, ปรับคุณภาพของผลลัพธ์" msgid "00.0" msgstr "00.0" msgid "" "Audio Gain: Adjust the amplification or attenuation of the output " "audio track." msgstr "ฟื้นค่าเสียง: ปรับการขยายเสียงหรือลดเสียงของแทร็คเสียงผลลัพธ์" msgid "Skip This Version" msgstr "ข้ามเวอร์ชั่นนี้" msgid "Remind Me Later" msgstr "แจ้งฉันทีหลัง" msgid "A new version of HandBrake is available!" msgstr "HandBrake มีเวอร์ชั่นใหม่แล้ว!" msgid "HandBrake xxx is now available (you have yyy)." msgstr "HandBrake xxx มีในตอนนี้แล้ว (คุณมี yyy)." msgid "Release Notes" msgstr "หมายเหตุการเผยแพร่" msgid "First Track Matching Selected Languages" msgstr "แทร็คแรกที่ตรงกับภาษาที่เลือก" msgid "All Tracks Matching Selected Languages" msgstr "แทร็คทั้งหมดที่ตรงกับภาษาที่เลือก" msgid "Chapters:" msgstr "ตอน:" msgid "Seconds:" msgstr "วินาที:" msgid "Frames:" msgstr "เฟรม:" msgid "Do Nothing" msgstr "ไม่ต้องทำอะไร" msgid "Show Notification" msgstr "แสดงการแจ้ง" msgid "Quit Handbrake" msgstr "ออกจาก Handbrake" msgid "Put Computer To Sleep" msgstr "ทำให้คอมพิวเตอร์สลีป" msgid "Shutdown Computer" msgstr "ปิดเครื่องคอมพิวเตอร์" msgid "Week" msgstr "สัปดาห์" msgid "Month" msgstr "เดือน" msgid "Year" msgstr "ปี" msgid "Immortal" msgstr "อมตะ" msgid "Never" msgstr "ไม่ต้อง" msgid "Daily" msgstr "รายวัน" msgid "Weekly" msgstr "รายสัปดาห์" msgid "Monthly" msgstr "รายเดือน" msgid "Default" msgstr "ค่าตั้งต้น" msgid "Fast" msgstr "เร็ว" msgid "Slow" msgstr "ช้า" msgid "Slower" msgstr "ช้ากว่า" msgid "Ultralight" msgstr "" msgid "Light" msgstr "" msgid "Medium" msgstr "ปานกลาง" msgid "Strong" msgstr "" msgid "Film" msgstr "" msgid "Grain" msgstr "" msgid "High Motion" msgstr "" msgid "Animation" msgstr "แอนิเมชั่น" msgid "Spatial" msgstr "" msgid "Temporal" msgstr "" msgid "Automatic" msgstr "อัตโนมัติ" msgid "Optimal" msgstr "เหมาะสม" msgid "Normal" msgstr "ปกติ" msgid "Simple" msgstr "อย่างง่าย" msgid "Smart" msgstr "" msgid "Diamond" msgstr "" msgid "Hexagon" msgstr "" msgid "Uneven Multi-Hexagon" msgstr "" msgid "Exhaustive" msgstr "" msgid "Hadamard Exhaustive" msgstr "" msgid "Most" msgstr "ส่วนใหญ่" msgid "Some" msgstr "บางส่วน" msgid "All" msgstr "ทั้งหมด" msgid "Encode only" msgstr "เข้ารหัสเท่านั้น" msgid "Always" msgstr "เสมอ" msgid "(NTSC Film)" msgstr "(หนัง NTSC)" msgid "(PAL Film/Video)" msgstr "(หนัง/วิดีโอ PAL)" msgid "(NTSC Video)" msgstr "(วิดีโอ NTSC)" msgid "%d - %02dh%02dm%02ds - %s" msgstr "%d - %02dh%02dm%02ds - %s" msgid "%d (%05d.MPLS) - %02dh%02dm%02ds" msgstr "%d (%05d.MPLS) - %02dh%02dm%02ds" msgid "%d (%05d.MPLS) - Unknown Length" msgstr "%d (%05d.MPLS) - ไม่ทราบความยาว" msgid "%d - %02dh%02dm%02ds" msgstr "%d - %02dh%02dm%02ds" msgid "%d - Unknown Length" msgstr "%d - ไม่ทราบความยาว" msgid "No Titles" msgstr "ไม่มีหัวเรื่อง" msgid "No Audio" msgstr "ไม่มีเสียง" msgid "Foreign Audio Search" msgstr "การค้นหาเสียงต่างประเทศ" #, c-format msgid "Chapter %2d" msgstr "ตอน %2d" #, c-format msgid "N/A" msgstr "ไม่มีข้อมูล" #, c-format msgid "" "Invalid Deinterlace Settings:\n" "\n" "%s\n" msgstr "การตั้งค่า Deinterlace ไม่ถูกต้อง:\n\n%s\n" #, c-format msgid "" "Invalid Detelecine Settings:\n" "\n" "%s\n" msgstr "การตั้งค่า Detelecine ไม่ถูกต้อง:\n\n%s\n" #, c-format msgid "" "Invalid Decomb Settings:\n" "\n" "%s\n" msgstr "การตั้งค่า Decomb ไม่ถูกต้อง:\n\n%s\n" msgid "" "Theora is not supported in the MP4 container.\n" "\n" "You should choose a different video codec or container.\n" "If you continue, FFMPEG will be chosen for you." msgstr "Theora ไม่ถูกรองรับในตัวบรรจุชนิด MP4\n\nคุณสามารถเลือก codec วิดีโอหรือตัวบรรจุอื่น\nถ้าคุณทำต่อ, FFMPEG จะถูกเลือกให้กับคุณ" msgid "Continue" msgstr "ทำต่อไป" msgid "No title found.\n" msgstr "ไม่พบหัวเรื่อง\n" msgid "" "Only one subtitle may be burned into the video.\n" "\n" "You should change your subtitle selections.\n" "If you continue, some subtitles will be lost." msgstr "เพียงศัพท์บรรยายเดียวที่สามารถถูกประทับลงในวิดีโอ\n\nคุณควรเปลี่ยนการเลือกศัพท์บรรยายของคุณ\nถ้าคุณทำต่อ, บางศัพท์ฯจะสูญหาย" msgid "" "Srt file does not exist or not a regular file.\n" "\n" "You should choose a valid file.\n" "If you continue, this subtitle will be ignored." msgstr "ไฟล์ Srt ไม่มีอยู่หรือไม่ใช่ไฟล์ปกติ\n\nคุณควรเลือกำฟล์ที่ถูกต้อง\nถ้าคุณทำต่อ, ศัพท์บรรยายนี้จะถูกละเลย" msgid "" "The source does not support Pass-Thru.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "แหล่งไม่รองรับการส่งผ่าน\n\nคุณควรเลือก codec เสียงอื่น\nถ้าคุณทำต่อ, หนึ่งอย่างจะถูกเลือกให้คุณ" #, c-format msgid "" "%s is not supported in the %s container.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "%s ไม่ถูกรองรับในตัวบรรจุแบบ %s\n\nคุณควรเลือก codec เสียงที่แตกต่าง\nถ้าคุณทำต่อ, หนึ่งอย่างจะถูกเลือกให้คุณ" #, c-format msgid "" "The source audio does not support %s mixdown.\n" "\n" "You should choose a different mixdown.\n" "If you continue, one will be chosen for you." msgstr "แหล่งเสียงไม่รองรับการผสมเสียง %s\n\nคุณควรเลือกการผสมเสียงแบบอื่น\nถ้าคุณทำต่อ, หนึ่งอย่างจะถูกเลือกให้คุณ" #, c-format msgid "" "Unable to create %s.\n" "\n" "Internal error. Could not parse UI description.\n" "%s" msgstr "ไม่สามารถสร้าง %s\n\nข้อผิดพลาดภายใน ไม่สามารถกระจายคำบรรยาย UI\n%s" msgid "Index" msgstr "ดัชนี" msgid "Duration" msgstr "ระยะเวลา" msgid "Title" msgstr "หัวเรื่อง" msgid "Job Information" msgstr "ข้อมูลงาน" msgid "Track Information" msgstr "ข้อมูลแทร็ค" msgid "Preset Name" msgstr "ชื่อค่าสำเร็จรูป" msgid "The device or file to encode" msgstr "อุปกรณ์หรือไฟล์เพื่อเข้ารหัส" msgid "The preset values to use for encoding" msgstr "ค่าของค่าสำเร็จรูปเพื่อใช้สำหรับการเข้ารหัส" msgid "Spam a lot" msgstr "ขยะเยอะมาก" msgid "- Transcode media formats" msgstr "- แปลงรหัสรูปแบบสื่อ" msgid "Globals" msgstr "ทั่วทั้งหมด" msgid "Presets" msgstr "ค่าสำเร็จรูป" msgid "Folder" msgstr "โฟลเดอร์" #, c-format msgid "%s path: (%s)" msgstr "%s เส้นทาง: (%s)" #, c-format msgid "%s indices: len %d" msgstr "%s ดัชนี: len %d" msgid "Type" msgstr "ชนิด" msgid "Failed to find parent folder when adding child." msgstr "ล้มเหลวในการค้นหาโฟลเดอร์แม่เมื่อกำลังเพิ่มส่วนลูก" msgid "Failed to find parent folder while adding child." msgstr "ล้มเหลวในการค้นหาโฟลเดอร์แม่เมื่อกำลังเพิ่มส่วนลูก" #, c-format msgid "Can't map language value: (%s)" msgstr "ไม่สามารถวางตำแหน่งค่าภาษา: (%s)" #, c-format msgid "Can't map value: (%s)" msgstr "ไม่สามารถวางตำแหน่งค่า: (%s)" msgid "Subtitles" msgstr "ศัพท์บรรยาย" msgid "" "%s: Folder already exists.\n" "You can not replace it with a preset." msgstr "%s: โฟลเดอร์มีอยู่แล้ว\nคุณไม่สามารถแทนที่มันด้วยค่าสำเร็จรูป" msgid "" "%s: Preset already exists.\n" "You can not replace it with a folder." msgstr "%s: ค่าสำเร็จรูปมีอยู่แล้ว\nคุณไม่สามารถแทนที่มันด้วยโฟลเดอร์" msgid "Import Preset" msgstr "นำเข้าค่าสำเร็จรูป" msgid "All (*)" msgstr "ทั้งหมด (*)" msgid "Presets (*.plist)" msgstr "ค่าสำเร็จรูป (*.plist)" msgid "Export Preset" msgstr "ส่งออกค่าสำเร็จรูป" msgid "" "Confirm deletion of %s:\n" "\n" "%s" msgstr "ยืนยันการลบของ %s:\n\n%s" msgid "folder" msgstr "โฟลเดอร์" msgid "preset" msgstr "ค่าสำเร็จรูป" msgid "No selection??? Perhaps unselected." msgstr "ไม่มีการเลือก??? บางทีไม่ได้เลือก" #, c-format msgid "Gstreamer Error: %s" msgstr "Gstreamer ผิดพลาด: %s" #, c-format msgid "" "Missing GStreamer plugin\n" "Audio or Video may not play as expected\n" "\n" "%s" msgstr "ขาดปลั๊กอิน GStreamer\nเสียงหรือวิดีโออาจไม่เล่นดังที่หวัง\n\n%s" msgid "Done" msgstr "เสร็จ" msgid "Windowed" msgstr "เป็นหน้าต่าง" msgid "Seconds" msgstr "วินาที" msgid "Frames" msgstr "เฟรม" #, c-format msgid "" "%s (Title %d, %s %d through %d, 2 Video Passes) -->" " %s" msgstr "%s (หัวเรื่อง %d, %s %d ถึง %d, 2 Video Passes) --> %s" #, c-format msgid "" "%s (Title %d, %s %d through %d) --> %s" msgstr "" #, c-format msgid "Modified Preset Based On: %s\n" msgstr "ปรับเปลี่ยนค่าสำเร็จรูปบนพื้นฐาน: %s\n" #, c-format msgid "Preset: %s\n" msgstr "ค่าสำเร็จรูป: %s\n" #, c-format msgid "Format: %s Container\n" msgstr "รูปแบบ: %s ตัวบรรจุ\n" msgid "Container Options:" msgstr "ตัวเลือกของตัวบรรจุ:" #, c-format msgid "%sChapter Markers" msgstr "%sตัวหมายตอน" #, c-format msgid "%siPod 5G Support" msgstr "%sรองรับ iPod 5G" #, c-format msgid "%sWeb Optimized" msgstr "%sเหมาะกับเว็บ" #, c-format msgid "%sLarge File Size (>4GB)" msgstr "%sขนาดไฟล์ใหญ่ (>4GB)" #, c-format msgid "Destination: %s\n" msgstr "ปลายทาง: %s\n" msgid "(Aspect Preserved)" msgstr "(สัดส่วนถูกสงวน)" msgid "(Aspect Lost)" msgstr "(สัดส่วนสูญเสีย)" msgid "(Anamorphic)" msgstr "(อนามอร์ฟิก)" msgid "(Custom Anamorphic)" msgstr "(อนามอร์ฟิกที่กำหนดเอง)" msgid "Picture: " msgstr "ภาพ: " #, c-format msgid "Source: %d x %d, Output %d x %d %s, Crop %d:%d:%d:%d" msgstr "แหล่ง: %d x %d, ผลลัพธ์ %d x %d %s, ขลิบ %d:%d:%d:%d" #, c-format msgid ", Display %d x %d" msgstr ", แสดงผล %d x %d" msgid "Filters:" msgstr "ตัวกรอง:" #, c-format msgid "%sDetelecine" msgstr "%sDetelecine" #, c-format msgid "%sDecomb" msgstr "%sDecomb" #, c-format msgid "%sDeinterlace" msgstr "%sDeinterlace" #, c-format msgid "%sDenoise Filter %s:" msgstr "%s ตัวกรอง Denoise %s:" #, c-format msgid "%sDeblock: %d" msgstr "%sDeblock: %d" #, c-format msgid "%sGrayscale" msgstr "%sGrayscale" #, c-format msgid "Video: %s" msgstr "วิดีโอ: %s" #, c-format msgid ", Framerate: %s %s" msgstr ", อัตราเฟรม: %s %s" #, c-format msgid ", Framerate: Peak %s (may be lower)" msgstr ", อัตราเฟรม: สูงสุด %s (อาจต่ำกว่า)" #, c-format msgid ", Framerate: %s (constant frame rate)" msgstr ", อัตราเฟรม: %s (อัตราเฟรมคงที่)" msgid "Error" msgstr "ผิดพลาด" msgid "Bitrate:" msgstr "อัตราบิต:" msgid "Bitrate" msgstr "อัตราบิต" msgid "Quality" msgstr "คุณภาพ" msgid "Turbo 1st Pass: On\n" msgstr "Turbo 1st Pass: เปิด\n" #, c-format msgid "Video Options: Preset: %s" msgstr "ตัวเลือกวิดีโอ: ค่าสำเร็จรูป: %s" msgid " - Tune: " msgstr " - จูน: " #, c-format msgid " - Profile: %s" msgstr " - โปรไฟล์: %s" #, c-format msgid " - Level: %s" msgstr " - ระดับ: %s" #, c-format msgid "Advanced Options: %s\n" msgstr "ตัวเลือกขั้นสูง: %s\n" msgid "Audio: " msgstr "เสียง: " #, c-format msgid "Audio Tracks: %d" msgstr "แทร็คเสียง: %d" #, c-format msgid "Bitrate: %d" msgstr "อัตราบิต: %d" #, c-format msgid "%s --> Encoder: %s" msgstr "%s --> ตัวเข้ารหัส: %s" #, c-format msgid "%s --> Encoder: %s, Mixdown: %s, SampleRate: %s, %s" msgstr "%s --> ตัวเข้ารหัส: %s, การผสมเสียง: %s, อัตราสุ่มฯ: %s, %s" msgid "Subtitle: " msgstr "ศัพท์บรรยาย: " #, c-format msgid "Subtitle Tracks: %d\n" msgstr "แทร็คศัพท์บรรยาย: %d\n" msgid " (Force)" msgstr " (บังคับ)" msgid " (Burn)" msgstr " (ประทับ)" msgid " (Default)" msgstr " (ค่าตั้งต้น)" #, c-format msgid " %s (%s), %s, Offset (ms) %d%s\n" msgstr " %s (%s), %s, ชดเชย (ms) %d%s\n" #, c-format msgid "" "Destination: %s\n" "\n" "Another queued job has specified the same destination.\n" "Do you want to overwrite?" msgstr "ปลายทาง: %s\n\nงานคิวอื่นได้ถูกระบุปลายทางเดียวกัน\nคุณต้องการเขียนทับหรือไม่?" msgid "Overwrite" msgstr "เขียนทับ" #, c-format msgid "" "Destination: %s\n" "\n" "This is not a valid directory." msgstr "ปลายทาง: %s\n\nนี่ไม่ใช่เส้นทางที่ถูกต้อง" #, c-format msgid "" "Destination: %s\n" "\n" "Can not read or write the directory." msgstr "ปลายทาง: %s\n\nไม่สามารถอ่านหรือเขียนสู่เส้นทางได้" #, c-format msgid "" "Destination filesystem is almost full: %uM free\n" "\n" "Encode may be incomplete if you proceed.\n" msgstr "ระบบไฟล์ปลายทางเกือบเต็ม: ว่าง %uM\n\nการเข้ารหัสอาจไม่สมบูรณ์ถ้าคุณดำเนินการ\n" msgid "Proceed" msgstr "ดำเนินการ" #, c-format msgid "" "Destination: %s\n" "\n" "File already exists.\n" "Do you want to overwrite?" msgstr "ปลายทาง: %s\n\nไฟล์มีอยู่แล้ว\nคุณต้องการเขียนทับหรือไม่?" msgid "" "Duplicate destination files detected.\n" "Duplicates will not be added to the queue." msgstr "พบไฟล์ปลายทางที่ซ้ำซ้อน\nรายการที่ซ้ำซ้อนจะไม่ถูกเพิ่มสู่คิว" msgid "No Title" msgstr "ไม่มีหัวเรื่อง" msgid "" "There is another title with the same destination file name.\n" "This title will not be added to the queue unless you change\n" "the output file name.\n" msgstr "มีหัวเรื่องอื่นที่มีชื่อไฟล์ปลายทางเหมือนกัน\nหัวเรื่องนี้จะไม่ถูกเพิ่มสู่คิว เว้นแต่\nคุณจะเปลี่ยนชื่อไฟล์ผลลัพธ์\n" msgid "Stop" msgstr "หยุด" msgid "Stop Encoding" msgstr "หยุดการเข้ารหัส" msgid "Resume" msgstr "ทำต่อ" msgid "Resume Encoding" msgstr "เข้ารหัสต่อ" msgid "S_top Queue" msgstr "_หยุดคิว" msgid "_Start Queue" msgstr "เ_ริ่มคิว" msgid "_Resume Queue" msgstr "ทำคิว_ต่อ" msgid "Resume Queue" msgstr "ทำคิวต่อ" msgid "" "You are currently encoding. What would you like to do?\n" "\n" msgstr "ขณะนี้คุณกำลังเข้ารหัสอยู่ คุณอยากจะทำอะไร?\n\n" #, c-format msgid "" "You have %d unfinished job(s) in a saved queue.\n" "\n" "Would you like to reload them?" msgstr "คุณมี %d งานที่ยังไม่เสร็จซึ่งอยู่ในคิวที่บันทึกไว้\n\nคุณต้องการโหลดพวกมันใหม่หรือไม่?" msgid "No" msgstr "ไม่" msgid "Yes" msgstr "ใช่" #, c-format msgid "Usage: %s infile [outfile]\n" msgstr "การใช้: %s ไฟล์ใน [ไฟล์นอก]\n" #, c-format msgid "Offset: %dms" msgstr "ชดเชย: %dms" msgid "Burned Into Video" msgstr "ถูกประทับลงในวิดีโอแล้ว" msgid "Passthrough" msgstr "ส่งผ่าน" msgid "through" msgstr "ถึง" msgid "(Forced Subtitles Only)" msgstr "(เฉพาะศัพท์บรรยายที่ถูกบังคับ)" msgid "(Default)" msgstr "(ค่าตั้งต้น)" msgid "Error!" msgstr "ผิดพลาด!" #, c-format msgid "Preferred Language: %s" msgstr "ภาษาที่ชอบ: %s" #, c-format msgid "Add %s subtitle track if default audio is not %s" msgstr "เพิ่มแทร็คศัพท์บรรยาย %s ถ้าเสียงตั้งต้นไม่ใช่ %s" #, c-format msgid "Type %s" msgstr "ชนิด %s" #, c-format msgid "Type %s value %s" msgstr "ชนิด %s ค่า %s" #, c-format msgid "Type %s value %d" msgstr "ชนิด %s ค่า %d" #, c-format msgid "Type %s value %" msgstr "ชนิด %s ค่า %" #, c-format msgid "Type %s value %f" msgstr "ชนิด %s ค่า %f" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"%s\"" msgstr "" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"\"" msgstr "" msgid "Any" msgstr "ใดๆ" msgid "0: SAD, no subpel" msgstr "" msgid "4: SATD, qpel on all" msgstr "" msgid "5: SATD, multi-qpel on all" msgstr "" msgid "6: RD in I/P-frames" msgstr "" msgid "7: RD in all frames" msgstr "7: RD ในเฟรมทั้งหมด" msgid "8: RD refine in I/P-frames" msgstr "" msgid "9: RD refine in all frames" msgstr "" msgid "10: QPRD in all frames" msgstr "10: QPRD ในเฟรมทั้งหมด" msgid "11: No early terminations in analysis" msgstr "11: ไม่มีการยุติก่อนนี้ในการวิเคราะห์" msgid "Your names" msgstr "ชื่อของคุณ" msgid "Your emails" msgstr "อีเมลของคุณ" HandBrake-0.10.2/gtk/po/ChangeLog0000664000175200017520000000000011026035545017003 0ustar handbrakehandbrakeHandBrake-0.10.2/gtk/po/POTFILES.in0000664000175200017520000000011612174530362017020 0ustar handbrakehandbrake# List of source files containing translatable strings. src/main.c src/ghb.ui HandBrake-0.10.2/gtk/po/da.po0000664000175200017520000014451112466165306016205 0ustar handbrakehandbrake# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Ulrik Johansen, 2014 msgid "" msgstr "" "Project-Id-Version: HandBrake-0.10\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-03 13:01+0300\n" "PO-Revision-Date: 2014-11-28 14:44+0000\n" "Last-Translator: VictorR2007 \n" "Language-Team: Danish (http://www.transifex.com/projects/p/handbrake/language/da/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: da\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Quality: " msgstr "" #, c-format msgid "Bitrate: %dkbps" msgstr "" #, c-format msgid "Bitrate: %.4gkbps" msgstr "" msgid "Passthrough" msgstr "" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s\n" "Track Name: %s" msgstr "" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s" msgstr "" msgid "Add" msgstr "" msgid "" "Add an audio encoder.\n" "Each selected source track will be encoded with all selected encoders." msgstr "" msgid "Set the audio codec to encode this track with." msgstr "" msgid "Set the bitrate to encode this track with." msgstr "" msgid "" "Audio Quality:\n" "For encoders that support it, adjust the quality of the output." msgstr "" msgid "Set the mixdown of the output audio track." msgstr "" msgid "Set the sample rate of the output audio track." msgstr "" msgid "" "Audio Gain:\n" "Adjust the amplification or attenuation of the output audio track." msgstr "" msgid "0dB" msgstr "" msgid "%ddB" msgstr "" msgid "%.4gkHz" msgstr "" msgid "%d - %s (%.4gkHz)" msgstr "" msgid "" "Dynamic Range Compression:\n" "Adjust the dynamic range of the output audio track.\n" "For source audio that has a wide dynamic range,\n" "very loud and very soft sequences, DRC allows you\n" "to 'compress' the range by making loud sounds\n" "softer and soft sounds louder.\n" msgstr "" msgid "Remove this audio encoder" msgstr "" msgid "Closing HandBrake will terminate encoding.\n" msgstr "" msgid "No Title Found" msgstr "" msgid "none" msgstr "" msgid "Not Selected" msgstr "" msgid "zerolatency x264 tune selected, forcing constant framerate" msgstr "" msgid "Scanning ..." msgstr "" msgid "Stop Scan" msgstr "" msgid "On" msgstr "Til" msgid "Strict" msgstr "" msgid "Loose" msgstr "" msgid "Custom" msgstr "" msgid "Unknown" msgstr "" msgid "auto" msgstr "" #, c-format msgid "" "%s\n" "\n" "%s in %d seconds ..." msgstr "" #, c-format msgid "%sYour movie will be lost if you don't continue encoding." msgstr "" msgid "Cancel Current and Stop" msgstr "" msgid "Cancel Current, Start Next" msgstr "" msgid "Finish Current, then Stop" msgstr "" msgid "Continue Encoding" msgstr "" msgid "Custom " msgstr "" msgid "Modified " msgstr "" #, c-format msgid "Handbrake Version: %s (%d)\n" msgstr "" #, c-format msgid "%d encode(s) pending" msgstr "" #, c-format msgid "job %d of %d, " msgstr "" #, c-format msgid "pass %d (subtitle scan) of %d, " msgstr "" #, c-format msgid "pass %d of %d, " msgstr "" #, c-format msgid "Encoding: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" msgstr "" #, c-format msgid "Encoding: %s%s%.2f %% (ETA %02dh%02dm%02ds)" msgstr "" #, c-format msgid "Encoding: %s%s%.2f %%" msgstr "" msgid "Searching for start time, " msgstr "" msgid "Scanning..." msgstr "" #, c-format msgid "Scanning title %d of %d..." msgstr "" #, c-format msgid "Scanning title %d of %d preview %d..." msgstr "" msgid "Source" msgstr "" msgid "Choose Video Source" msgstr "Vælg videokilde" msgid "Paused" msgstr "" msgid "Encode Done!" msgstr "" msgid "Encode Canceled." msgstr "" msgid "Encode Failed." msgstr "" msgid "Muxing: This may take a while..." msgstr "" msgid "Scan this DVD source" msgstr "" msgid "AutoScan" msgstr "" msgid "Suspend" msgstr "" #, c-format msgid "Suspend failed: %s" msgstr "" msgid "Suspend failed" msgstr "" msgid "Shutdown" msgstr "" #, c-format msgid "Shutdown failed: %s" msgstr "" msgid "Shutdown failed" msgstr "" msgid "Encoding" msgstr "" #, c-format msgid "press %d %d" msgstr "" #, c-format msgid "" "Invalid Settings:\n" "%s" msgstr "" msgid "Cancel" msgstr "" #, c-format msgid "%s: %.4g (Warning: lossless)" msgstr "" #, c-format msgid "HandBrake %s/%s is now available (you have %s/%d)." msgstr "" msgid "Encode Complete" msgstr "" msgid "Put down that cocktail, Your HandBrake queue is done!" msgstr "" msgid "Your encode is complete." msgstr "" msgid "Shutting down the computer" msgstr "" msgid "Putting computer to sleep" msgstr "" msgid "Quiting Handbrake" msgstr "" msgid "Bottom" msgstr "" #, c-format msgid "open failed: %s\n" msgstr "" msgid "No key for dictionary item" msgstr "" msgid "Invalid container type. This shouldn't happen" msgstr "" #, c-format msgid "%s:missing a requried attribute" msgstr "" #, c-format msgid "" "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" msgstr "" msgid "language" msgstr "" msgid "Language" msgstr "" msgid "" "The language this text is in, as an ISO code. Pango can use this as a hint " "when rendering the text. If you don't understand this parameter, you " "probably don't need it" msgstr "" msgid "" "The preferred place to ellipsize the string, if the cell renderer does not " "have enough room to display the entire string" msgstr "" msgid "" "How to break the string into multiple lines, if the cell renderer does not " "have enough room to display the entire string" msgstr "" msgid "" "Render the subtitle over the video.\n" "\n" "The subtitle will be part of the video and can not be disabled." msgstr "Læg underteksterne over på videoen.\n\nUnderteksterne vil være en del af videoen og kan ikke fjernes." msgid "Burned In" msgstr "Indbrændt" msgid "" "Set the default output subtitle track.\n" "\n" "Most players will automatically display this\n" "subtitle track whenever the video is played.\n" "\n" "This is useful for creating a \"forced\" track\n" "in your output." msgstr "" msgid "Default" msgstr "Standard" msgid "" "Use only subtitles that have been flagged\n" "as forced in the source subtitle track\n" "\n" "\"Forced\" subtitles are usually used to show\n" "subtitles during scenes where someone is speaking\n" "a foreign language." msgstr "" msgid "Forced Only" msgstr "Kun tvungen" msgid "" "Add (or subtract) an offset (in milliseconds)\n" "to the start of the SRT subtitle track.\n" "\n" "Often, the start of an external SRT file\n" "does not coincide with the start of the video.\n" "This setting allows you to synchronize the files." msgstr "" msgid "SRT Offset" msgstr "SRT-forskydning" msgid "Off" msgstr "Fra" msgid "" "The source subtitle track\n" "\n" "You can choose any of the subtitles\n" "recognized in your source file.\n" "\n" "In addition, there is a special track option\n" "\"Foreign Audio Search\". This option will add\n" "an extra pass to the encode that searches for\n" "subtitles that may correspond to a foreign\n" "language scene. This option is best used in\n" "conjunction with the \"Forced\" option." msgstr "Kilde-undertekstspor \n\nDu kan vælge en af ​​de undertekster,\nder genkendes i kildefilen. \n\nDesuden er der en særligt sporvalgmulighed\n\"Søgning efter udenlandsk lyd\". Denne\nmulighed vil tilføje en ekstra gennemkørsel\naf konverteringen, der søger efter\nundertekster, der kan svare til en\nfremmedsprogsscene. Denne mulighed\nfungerer bedst i forbindelse med\n\"Tvungen\"-valgmuligheden." msgid "Track" msgstr "Spor" msgid "About HandBrake" msgstr "" msgid "" "Copyright © 2008 - 2013 John Stebbins\n" "Copyright © 2004 - 2013, HandBrake Devs" msgstr "" msgid "" "HandBrake is a GPL-licensed, multiplatform, multithreaded video transcoder." msgstr "" msgid "http://handbrake.fr" msgstr "" msgid "" "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n" "\n" "HandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgstr "" msgid "_Minimize/Maximize" msgstr "_Minimér/Maximér" msgid "_Pause Queue" msgstr "_Paus kø" msgid "_Quit" msgstr "" msgid "_About" msgstr "" msgid "HandBrake" msgstr "" msgid "_File" msgstr "_Fil" msgid "_Source" msgstr "K_ilde" msgid "Single _Title" msgstr "_Enkel titel" msgid "_Destination" msgstr "_Destination" msgid "_Preferences" msgstr "" msgid "_Queue" msgstr "_Kø" msgid "_Add" msgstr "" msgid "Add _Multiple" msgstr "" msgid "_Start" msgstr "" msgid "_Pause" msgstr "" msgid "_View" msgstr "_Vis" msgid "HandBrake For _Dumbies" msgstr "HandBrake kort forklaret" msgid "_Show Presets" msgstr "" msgid "_Preview" msgstr "" msgid "_Activity Window" msgstr "_Aktivitetsvindue" msgid "Show _Queue" msgstr "Vis _kø" msgid "_Presets" msgstr "" msgid "_Save" msgstr "" msgid "_Delete" msgstr "" msgid "_Make Default" msgstr "_Gør standard" msgid "_New Folder" msgstr "" msgid "_Export" msgstr "_Eksportér" msgid "_Import" msgstr "_Importér" msgid "_Update Built-in Presets" msgstr "_Opdatér indbyggede forudindstillinger" msgid "_Help" msgstr "_Hjælp" msgid "_Guide" msgstr "_Guide" msgid "Start Encoding" msgstr "Start konvertering" msgid "Start" msgstr "" msgid "Pause Encoding" msgstr "Paus konvertering" msgid "Pause" msgstr "" msgid "Add to Queue" msgstr "Tilføj til kø" msgid "Enqueue" msgstr "Sæt i kø" msgid "Show Queue" msgstr "Vis kø" msgid "Queue" msgstr "Kø" msgid "" "Open Picture Settings and Preview window.\n" "Here you can adjust cropping, resolution, aspect ratio, and filters." msgstr "" msgid "Preview" msgstr "" msgid "Show Activity Window" msgstr "Vis aktivitetsvindue" msgid "Activity" msgstr "Aktivitet" msgid "Source:" msgstr "Kilde:" msgid "None" msgstr "Ingen" msgid "Title:" msgstr "" msgid "" "Set the title to encode.\n" "By default the longest title is chosen.\n" "This is often the feature title of a DVD." msgstr "" msgid "Angle:" msgstr "Vinkel:" msgid "For multi-angle DVD's, select the desired angle to encode." msgstr "Ved multivinkel-DVDer, vælg den ønskede vinkel der skal konverteres." msgid "Reset All Titles" msgstr "" msgid "Apply current settings to all titles" msgstr "" msgid "Range of title to encode. Can be chapters, seconds, or frames." msgstr "Område af titlen der skal konverteres. Kan være kapitler, sekunder, eller frames." msgid "Set the first chapter to encode." msgstr "Vælg det første kapitel der skal konverteres." msgid "Set the last chapter to encode." msgstr "Vælg det sidste kapitel der skal konverteres." msgid "Duration:" msgstr "Varighed:" msgid "Destination" msgstr "" msgid "File:" msgstr "Fil:" msgid "Destination filename for your encode." msgstr "Destinationsfilnavn til din konverterede fil." msgid "Destination directory for your encode." msgstr "Destinationsmappe til din konverterede fil." msgid "Destination Directory" msgstr "Destinationsmappe" msgid "Format:" msgstr "Format:" msgid "Format to mux encoded tracks to." msgstr "Format til de konverterede spor." msgid "iPod 5G Support" msgstr "iPod 5G-understøttelse" msgid "Add iPod Atom needed by some older iPods." msgstr "Tilføj iPod-Atom der kræves af nogle ældre iPods." msgid "Web optimized" msgstr "Web-optimeret" msgid "" "Optimize the layout of the MP4 file for progressive download.\n" "This allows a player to initiate playback before downloading the entire file." msgstr "" msgid "Large file (>4GB)" msgstr "Stor fil (>4GB)" msgid "" "Allow 64 bit MP4 file which can be over 4GB.\n" "\n" "Caution: This option may break device compatibility." msgstr "" msgid "Presets List" msgstr "" msgid "Source Codec:" msgstr "Kilde-codec:" msgid "Dimensions:" msgstr "Dimensioner:" msgid "Aspect: " msgstr "Billedforhold:" msgid "Frame Rate:" msgstr "Frame Rate:" msgid "Source Picture Parameters" msgstr "Kilde-billedparametre" msgid "Autocrop:" msgstr "Auto-beskær:" msgid "Crop:" msgstr "Beskær:" msgid "Crop Dimensions:" msgstr "" msgid "Cropping" msgstr "" msgid "Scale Dimensions:" msgstr "Skaleringsdimensioner:" msgid "Optimal for Source:" msgstr "Optimal til kilde:" msgid "Anamorphic:" msgstr "Anamorft:" msgid "Scaling" msgstr "Skalering" msgid "Presentation Dimensions:" msgstr "" msgid "Summary" msgstr "Opsummering" msgid "Left Crop" msgstr "" msgid "Top Crop" msgstr "" msgid "Bottom Crop" msgstr "" msgid "Right Crop" msgstr "" msgid "Auto Crop" msgstr "" msgid "Automatically crop black borders around edges of the video." msgstr "" msgid "Loose Crop" msgstr "" msgid "" "When picture settings require that the image\n" "dimensions be rounded to some multiple number\n" "of pixels, this setting will crop a few extra pixels\n" "instead of doing exact cropping and then scaling to\n" "the required multiple." msgstr "" msgid "width:" msgstr "" msgid "" "This is the width that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "" msgid "height:" msgstr "" msgid "" "This is the height that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "" msgid "Optimal for source" msgstr "" msgid "" "If enabled, select the 'optimal' storage resolution.\n" "This will be the resolution that most closely matches the source resolution after cropping." msgstr "" msgid "" "Anamorphic Modes:\n" "\n" "None - Force pixel aspect ratio to 1:1.\n" "Loose - Align dimensions to chosen 'Alignment' value\n" " and pick pixel aspect ratio that preserves the\n" " original display aspect ratio\n" "Strict - Keep original source dimensions and pixel\n" " aspect ratio" msgstr "" msgid "Alignment:" msgstr "" msgid "" "Align storage dimensions to multiples of this value.\n" "\n" "This setting is only necessary for compatibility with some devices.\n" "You should use 2 unless you experience compatibility issues." msgstr "" msgid "Storage Geometry" msgstr "" msgid "" "This is the display width. It is the result of scaling the storage " "dimensions by the pixel aspect." msgstr "" msgid "Pixel Aspect:" msgstr "" msgid "" "Pixel aspect defines the shape of the pixels.\n" "\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "" msgid ":" msgstr "" msgid "" "Pixel aspect defines the shape of the pixels.\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "" msgid "Keep Aspect" msgstr "" msgid "" "If enabled, the original display aspect of the source will be maintained." msgstr "" msgid "Display Aspect:" msgstr "" msgid "Display Geometry" msgstr "" msgid "Grayscale" msgstr "" msgid "If enabled, filter colour components out of video." msgstr "" msgid "Deblock:" msgstr "" msgid "" "The deblocking filter removes a common type of compression artifact.\n" "If your source exhibits 'blockiness', this filter may help clean it up." msgstr "" msgid "Denoise Filter:" msgstr "" msgid "" "Denoise filtering reduces or removes the appearance of noise and grain.\n" "Film grain and other types of high frequency noise are difficult to compress.\n" "Using this filter on such sources can result in smaller file sizes." msgstr "" msgid "Denoise Preset:" msgstr "" msgid "Denoise Tune:" msgstr "" msgid "" "Custom denoise filter string format\n" "\n" "SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgstr "" msgid "Detelecine:" msgstr "" msgid "" "This filter removes 'combing' artifacts that are the result of telecining.\n" "\n" "Telecining is a process that adjusts film framerates that are 24fps to NTSC video frame rates which are 30fps." msgstr "" msgid "" "Custom detelecine filter string format\n" "\n" "JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgstr "" msgid "Decomb" msgstr "" msgid "" "Choose decomb or deinterlace filter options.\n" "\n" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced.\n" "\n" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "" msgid "Deinterlace" msgstr "" msgid "Decomb:" msgstr "" msgid "" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced." msgstr "" msgid "" "Custom decomb filter string format\n" "\n" "Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\n" "BlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\n" "ErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgstr "" msgid "Deinterlace:" msgstr "" msgid "" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "" msgid "" "Custom deinterlace filter string format\n" "\n" "YadifMode:YadifParity:McdintMode:McdeintQp" msgstr "" msgid "Filters" msgstr "" msgid "Picture" msgstr "" msgid "Video Encoder:" msgstr "Video-encoder:" msgid "Available video encoders." msgstr "Tilgængelige video-encodere." msgid "Framerate:" msgstr "Framerate:" msgid "" "Output framerate.\n" "\n" "'Same as source' is recommended. If your source video has\n" "a variable framerate, 'Same as source' will preserve it." msgstr "" msgid "Constant Framerate" msgstr "Konstant framerate" msgid "Same as source" msgstr "" msgid "kbps" msgstr "" msgid "(variable)" msgstr "" msgid "(constant)" msgstr "" msgid "Enables constant framerate output." msgstr "" msgid "Peak Framerate (VFR)" msgstr "Top-framerate (VFR)" msgid "" "Enables variable framerate output with a peak\n" "rate determined by the framerate setting.\n" "\n" "VFR is not compatible with some players." msgstr "" msgid "Variable Framerate" msgstr "Variabel framerate" msgid "" "Enables variable framerate output.\n" "\n" "VFR is not compatible with some players." msgstr "" msgid "" "Set the desired quality factor.\n" "The encoder targets a certain quality.\n" "The scale used by each video encoder is different.\n" "\n" "x264's scale is logarithmic and lower values correspond to higher quality.\n" "So small decreases in value will result in progressively larger increases\n" "in the resulting file size. A value of 0 means lossless and will result\n" "in a file size that is larger than the original source, unless the source\n" "was also lossless.\n" "\n" "FFMpeg's and Theora's scale is more linear.\n" "These encoders do not have a lossless mode." msgstr "" msgid "Constant Quality:" msgstr "" msgid "Bitrate (kbps): " msgstr "" msgid "" "Set the average bitrate.\n" "\n" "The instantaneous bitrate can be much higher or lower at any point in time.\n" "But the average over a long duration will be the value set here. If you need\n" "to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings." msgstr "" msgid "2-Pass Encoding" msgstr "" msgid "" "Perform 2 Pass Encoding.\n" "\n" "The 'Bitrate' option is prerequisite. During the 1st pass, statistics about\n" "the video are collected. Then in the second pass, those statistics are used\n" "to make bitrate allocation decisions." msgstr "" msgid "Turbo First Pass" msgstr "" msgid "" "During the 1st pass of a 2 pass encode, use settings that speed things " "along." msgstr "" msgid "Use Advanced Options" msgstr "" msgid "" "Use advanced options Tab for x264 settings.\n" "\n" "Use at your own risk!" msgstr "" msgid "Preset:" msgstr "" msgid "" "Adjusts encoder settings to trade off compression efficiency against encoding speed.\n" "\n" "This establishes your default encoder settings.\n" "Tunes, profiles, levels and advanced option string will be applied to this.\n" "You should generally set this option to the slowest you can bear since slower\n" "settings will result in better quality or smaller files." msgstr "" msgid "Tune:" msgstr "" msgid "" "Tune settings to optimize for common scenarios.\n" "\n" "This can improve effeciency for particular source characteristics or set\n" "characteristics of the output file. Changes will be applied after the\n" "preset but before all other parameters." msgstr "" msgid "Fast Decode" msgstr "" msgid "" "Reduce decoder CPU usage.\n" "\n" "Set this if your device is struggling to play the output (dropped frames)." msgstr "" msgid "Zero Latency" msgstr "" msgid "" "Minimize latency between input to encoder and output of decoder.\n" "\n" "This is useful for broadcast of live streams.\n" "\n" "Since HandBrake is not suitable for live stream broadcast purposes,\n" "this setting is of little value here." msgstr "" msgid "Profile:" msgstr "" msgid "" "Sets and ensures compliance with the specified profile.\n" "\n" "Overrides all other settings." msgstr "" msgid "Level:" msgstr "" msgid "" "Sets and ensures compliance with the specified level.\n" "\n" "Overrides all other settings." msgstr "" msgid "More Settings:" msgstr "" msgid "" "Additional encoder settings.\n" "\n" "Colon separated list of encoder options." msgstr "" msgid "Video" msgstr "" msgid "Selection Behavior:" msgstr "" msgid "Remove" msgstr "" msgid "Available Languages" msgstr "" msgid "Selected Languages" msgstr "" msgid "Use only first encoder for secondary audio" msgstr "" msgid "" "Only the primary audio track will be encoded with the full encoder list.\n" "All other secondary audio output tracks will be encoded with first encoder only." msgstr "" msgid "Auto Passthru:" msgstr "" msgid "MP3" msgstr "" msgid "" "Enable this if your playback device supports MP3.\n" "This permits MP3 passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports AAC.\n" "This permits AAC passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports AC-3.\n" "This permits AC-3 passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports DTS.\n" "This permits DTS passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports DTS-HD.\n" "This permits DTS-HD passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "Passthru Fallback:" msgstr "" msgid "" "Set the audio codec to encode with when a suitable track can not be found " "for audio passthru." msgstr "" msgid "Audio Encoder Settings:" msgstr "" msgid "Each selected source track will be encoded with all selected encoders" msgstr "" msgid "Encoder" msgstr "" msgid "Bitrate/Quality" msgstr "" msgid "Mixdown" msgstr "" msgid "Samplerate" msgstr "" msgid "Gain" msgstr "" msgid "Audio Defaults" msgstr "" msgid "Add new audio settings to the list" msgstr "" msgid "Add All" msgstr "" msgid "Add all audio tracks to the list" msgstr "" msgid "Reload Defaults" msgstr "" msgid "Reload all audio settings from defaults" msgstr "" msgid "Audio List" msgstr "" msgid "Preferred Language: None" msgstr "" msgid "Add Foreign Audio Search Pass" msgstr "" msgid "" "Add \"Foreign Audio Search\" when the default audio track is your preferred language.\n" "This search pass finds short sequences of foreign audio and provides subtitles for them." msgstr "" msgid "Add subtitle track if default audio is foreign" msgstr "" msgid "" "When the default audio track is not your preferred language, add a subtitle " "track." msgstr "" msgid "Add Closed Captions when available" msgstr "" msgid "" "Closed captions are text subtitles that can be added to any container as a " "soft subtitle track (not burned)" msgstr "" msgid "Subtitle Defaults" msgstr "" msgid "Add new subtitle settings to the list" msgstr "" msgid "Add all subtitle tracks to the list" msgstr "" msgid "Reload all subtitle settings from defaults" msgstr "" msgid "Subtitle List" msgstr "" msgid "Reference Frames:" msgstr "" msgid "" "Sane values are ~1-6. The more you add, the better the compression, but the slower the encode.\n" "Cel animation tends to benefit from more reference frames a lot more than film content.\n" "\n" "Note that many hardware devices have limitations on the number of supported reference\n" "frames, so if you're encoding for a handheld or standalone player, don't touch this unless\n" "you're absolutely sure you know what you're doing!" msgstr "" msgid "Maximum B-Frames:" msgstr "" msgid "" "Sane values are ~2-5. This specifies the maximum number of sequential B-frames that the encoder can use.\n" "\n" "Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.\n" "Cel-animated source material and B-pyramid also significantly increase the usefulness of larger\n" "values.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off)." msgstr "" msgid "Pyramidal B-Frames:" msgstr "" msgid "" "B-pyramid improves compression by creating a pyramidal structure (hence the name)\n" "of B-frames, allowing B-frames to reference each other to improve compression.\n" "\n" "Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit." msgstr "" msgid "Weighted P-Frames:" msgstr "" msgid "" "Performs extra analysis to decide upon weighting parameters for each frame.\n" "\n" "This improves overall compression slightly and improves the quality of fades greatly.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires weighted P-frame\n" "prediction to be disabled. Note that some devices and players, even those that support\n" "Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is\n" "completely incompatible with it, for example." msgstr "" msgid "8x8 Transform" msgstr "" msgid "" "The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.\n" "\n" "It improves compression by at least 5% at a very small speed cost and may\n" "provide an unusually high visual quality benefit compared to its compression\n" "gain. However, it requires High Profile, which many devices may not support." msgstr "" msgid "CABAC Entropy Encoding" msgstr "" msgid "" "After the encoder has done its work, it has a bunch of data that\n" "needs to be compressed losslessly, similar to ZIP or RAR. H.264 provides\n" "two options for this: CAVLC and CABAC. CABAC decodes a lot slower but\n" "compresses significantly better (10-30%), especially at lower bitrates.\n" "\n" "If you're looking to minimize CPU requirements for video playback, disable this option.\n" "Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled." msgstr "" msgid "Encoding Features" msgstr "" msgid "Motion Est. Method:" msgstr "" msgid "" "Controls the motion estimation method.\n" "\n" "Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.\n" "A better motion search method improves compression at the cost of speed.\n" "\n" "Diamond: performs an extremely fast and simple search using a diamond pattern.\n" "Hexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n" "Uneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n" "Exhaustive: performs a \"dumb\" search of every pixel in a wide area. Significantly slower for only a small compression gain.\n" "Transformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement." msgstr "" msgid "Subpel ME & Mode:" msgstr "" msgid "" "This setting controls both subpixel-precision motion estimation and mode decision methods.\n" "\n" "Subpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n" "Mode decision is the method used to choose how to encode each block of the frame: a very important decision.\n" "SAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n" "6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n" "11 disables all early terminations in analysis.\n" "10 and 11, the most powerful and slowest options, require adaptive quantization (aq-mode > 0) and trellis 2 (always)." msgstr "" msgid "Motion Est. Range:" msgstr "" msgid "" "This is the distance x264 searches from its initial guess at the\n" "motion of a block in order to try to find its actual motion.\n" "\n" "The default is fine for most content, but extremely high motion video,\n" "especially at HD resolutions, may benefit from higher ranges, albeit at\n" "a high speed cost." msgstr "" msgid "Adaptive Direct Mode:" msgstr "" msgid "" "H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n" "\n" "Spatial, the default, is almost always better, but temporal is sometimes useful too.\n" "x264 can, at the cost of a small amount of speed (and accordingly for a small compression gain),\n" "adaptively select which is better for each particular frame." msgstr "" msgid "Adaptive B-Frames:" msgstr "" msgid "" "x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n" "\n" "Fast mode takes roughly the same amount of time no matter how many B-frames you specify.\n" "However, while fast, its decisions are often suboptimal.\n" "\n" "Optimal mode gets slower as the maximum number of B-Frames increases,\n" "but makes much more accurate decisions, especially when used with B-pyramid." msgstr "" msgid "Partitions:" msgstr "" msgid "" "Mode decision picks from a variety of options to make its decision:\n" "this option chooses what options those are.\n" "\n" "Fewer partitions to check means faster encoding, at the cost of worse\n" "decisions, since the best option might have been one that was turned off." msgstr "" msgid "Trellis:" msgstr "" msgid "" "Trellis fine-tunes the rounding of transform coefficients to\n" "squeeze out 3-5% more compression at the cost of some speed.\n" "\n" "\"Always\" uses trellis not only during the main encoding process, but also\n" "during analysis, which improves compression even more, albeit at great speed cost.\n" "\n" "Trellis costs more speed at higher bitrates and requires CABAC." msgstr "" msgid "Analysis" msgstr "" msgid "Adaptive Quantization Strength:" msgstr "" msgid "" "Adaptive quantization controls how the encoder distributes bits across the frame.\n" "\n" "Higher values take more bits away from edges and complex areas to improve areas with finer detail." msgstr "" msgid "Psychovisual Rate Distortion:" msgstr "" msgid "" "Psychovisual rate-distortion optimization takes advantage of the characteristics of human\n" "vision to dramatically improve apparent detail and sharpness.\n" "The effect can be made weaker or stronger by adjusting the strength.\n" "Being an RD algorithm, it requires mode decision to be at least \"6\"." msgstr "" msgid "Psychovisual Trellis:" msgstr "" msgid "" "Psychovisual trellis is an experimental algorithm to further\n" "improve sharpness and detail retention beyond what Psychovisual RD does.\n" "\n" "Recommended values are around 0.2, though higher values may help for very\n" "grainy video or lower bitrate encodes. Not recommended for cel animation\n" "and other sharp-edged graphics." msgstr "" msgid "Deblocking: " msgstr "" msgid "" "H.264 deblocking filter.\n" "\n" "h.264 has a built-in deblocking filter that smooths out blocking artifacts\n" "after decoding each frame. This not only improves visual quality, but also\n" "helps compression significantly. The deblocking filter takes a lot of CPU power,\n" "so if you're looking to minimize CPU requirements for video playback, disable it.\n" "\n" "The deblocking filter has two adjustable parameters, \"strength\" (Alpha) and \"threshold\" (Beta).\n" "The former controls how strong (or weak) the deblocker is, while the latter controls how many\n" "(or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking.\n" "The default is 0 (normal strength) for both parameters." msgstr "" msgid "No DCT Decimate" msgstr "" msgid "" "x264 normally zeroes out nearly-empty data blocks to save bits to\n" "be better used for some other purpose in the video. However, this can\n" "sometimes have slight negative effects on retention of subtle grain and\n" "dither.\n" "\n" "Don't touch this unless you're having banding issues or other such cases\n" "where you are having trouble keeping fine noise." msgstr "" msgid "Psychovisual" msgstr "" msgid "" "Your selected options will appear here.\n" "You can edit these and add additional options.\n" "\n" "Default values will not be shown. The defaults are:\n" "ref=3:bframes=3:b-adapt=fast:direct=spatial:\n" "b-pyramid=normal:weightp=2:me=hex:merange=16:\n" "subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\n" "deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\n" "no-fast-pskip=0:no-dct-decimate=0:cabac=1" msgstr "" msgid "Current x264 Advanced Option String" msgstr "" msgid "Advanced Video" msgstr "" msgid "Chapter Markers" msgstr "" msgid "Add chapter markers to output file." msgstr "" msgid "Chapters" msgstr "" msgid "Actors:" msgstr "" msgid "Director:" msgstr "" msgid "Release Date:" msgstr "" msgid "Comment:" msgstr "" msgid "Genre:" msgstr "" msgid "Description:" msgstr "" msgid "Plot:" msgstr "" msgid "Tags" msgstr "" msgid "Settings" msgstr "" msgid "Edit" msgstr "" msgid "Reload" msgstr "" msgid "" "Mark selected queue entry as pending.\n" "Resets the queue job to pending and ready to run again." msgstr "" msgid "Reload All" msgstr "" msgid "" "Mark all queue entries as pending.\n" "Resets all queue jobs to pending and ready to run again." msgstr "" msgid "OK" msgstr "" msgid "Select All" msgstr "" msgid "Mark all titles for adding to the queue" msgstr "" msgid "Clear All" msgstr "" msgid "Unmark all titles" msgstr "" msgid "Destination files OK. No duplicates detected." msgstr "" msgid "Select this title for adding to the queue.\n" msgstr "" msgid "Preferences" msgstr "" msgid "Automatically check for updates" msgstr "" msgid "When all encodes are complete" msgstr "" msgid "Use automatic naming (uses modified source name)" msgstr "" msgid "Auto-Name Template" msgstr "" msgid "" "Available Options: {source} {title} {chapters} {date} {time} {quality} " "{bitrate}" msgstr "" msgid "Use iPod/iTunes friendly (.m4v) file extension for MP4" msgstr "" msgid "Number of previews" msgstr "" msgid "Filter short titles (seconds)" msgstr "" msgid "Show system tray icon" msgstr "" msgid "General" msgstr "" msgid "Constant Quality fractional granularity" msgstr "" msgid "Use dvdnav (instead of libdvdread)" msgstr "" msgid "Put individual encode logs in same location as movie" msgstr "" msgid "Activity Log Verbosity Level" msgstr "" msgid "Activity Log Longevity" msgstr "" msgid "Scale down High Definition previews" msgstr "" msgid "Automatically Scan DVD when loaded" msgstr "" msgid "Scans the DVD whenever a new disc is loaded" msgstr "" msgid "Hide Advanced Video Options Tab" msgstr "" msgid "" "Use advanced video options at your own risk.\n" "We recommend that you use the controls available\n" "on the Video tab instead." msgstr "" msgid "Delete completed jobs from queue" msgstr "" msgid "" "By default, completed jobs remain in the queue and are marked as complete.\n" "Check this if you want the queue to clean itself up by deleting completed jobs." msgstr "" msgid "Allow Tweaks" msgstr "" msgid "Allow HandBrake For Dummies" msgstr "" msgid "Advanced" msgstr "" msgid "Folder Name:" msgstr "" msgid "Description" msgstr "" msgid "Preset Name:" msgstr "" msgid "Custom Picture Dimensions" msgstr "" msgid "Maximum Width:" msgstr "" msgid "Enable maximum width limit." msgstr "" msgid "" "This is the maximum width that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source width is greater.\n" "Setting this to 0 means there is no maximum width." msgstr "" msgid "Maximum Height:" msgstr "" msgid "Enable maximum height limit." msgstr "" msgid "" "This is the maximum height that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source height is greater.\n" "Setting this to 0 means there is no maximum height." msgstr "" msgid "Select preview frames." msgstr "" msgid "" "Encode and play a short sequence of video starting from the current preview " "position." msgstr "" msgid "Duration:" msgstr "" msgid "Set the duration of the live preview in seconds." msgstr "" msgid "Show Crop" msgstr "" msgid "Show Cropped area of the preview" msgstr "" msgid "Fullscreen" msgstr "" msgid "View Fullscreen Preview" msgstr "" msgid "Title Number:" msgstr "" msgid "Detected DVD devices:" msgstr "" msgid "Setting:" msgstr "" msgid "Import SRT" msgstr "" msgid "Enable settings to import an SRT subtitle file" msgstr "" msgid "Embedded Subtitle List" msgstr "" msgid "Enable settings to select embedded subtitles" msgstr "" msgid "Character Code" msgstr "" msgid "Offset (ms)" msgstr "" msgid "" "Set the language of this subtitle.\n" "This value will be used by players in subtitle menus." msgstr "" msgid "" "Set the character code used by the SRT file you are importing.\n" "\n" "SRTs come in all flavours of character sets.\n" "We translate the character set to UTF-8.\n" "The source's character code is needed in order to perform this translation." msgstr "" msgid "Select the SRT file to import." msgstr "" msgid "Srt File" msgstr "" msgid "Adjust the offset in milliseconds between video and SRT timestamps" msgstr "" msgid "Track" msgstr "" msgid "List of subtitle tracks available from your source." msgstr "" msgid "Forced Subtitles Only" msgstr "" msgid "Burn into video" msgstr "" msgid "" "Render the subtitle over the video.\n" "The subtitle will be part of the video and can not be disabled." msgstr "" msgid "Set Default Track" msgstr "" msgid "Source Track" msgstr "" msgid "List of audio tracks available from your source." msgstr "" msgid "Track Name:" msgstr "" msgid "" "Set the audio track name.\n" "\n" "Players may use this in the audio selection list." msgstr "" msgid "Mix" msgstr "" msgid "Sample Rate" msgstr "" msgid "" "Dynamic Range Compression: Adjust the dynamic range of the output audio track.\n" "\n" "For source audio that has a wide dynamic range (very loud and very soft sequences),\n" "DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder." msgstr "" msgid "Enable bitrate setting" msgstr "" msgid "Enable quality setting" msgstr "" msgid "" "Quality: For output codec's that support it, adjust the quality of " "the output." msgstr "" msgid "00.0" msgstr "" msgid "" "Audio Gain: Adjust the amplification or attenuation of the output " "audio track." msgstr "" msgid "Skip This Version" msgstr "" msgid "Remind Me Later" msgstr "" msgid "A new version of HandBrake is available!" msgstr "" msgid "HandBrake xxx is now available (you have yyy)." msgstr "" msgid "Release Notes" msgstr "" msgid "First Track Matching Selected Languages" msgstr "" msgid "All Tracks Matching Selected Languages" msgstr "" msgid "Chapters:" msgstr "" msgid "Seconds:" msgstr "" msgid "Frames:" msgstr "" msgid "Do Nothing" msgstr "" msgid "Show Notification" msgstr "" msgid "Quit Handbrake" msgstr "" msgid "Put Computer To Sleep" msgstr "" msgid "Shutdown Computer" msgstr "" msgid "Week" msgstr "" msgid "Month" msgstr "" msgid "Year" msgstr "" msgid "Immortal" msgstr "" msgid "Never" msgstr "" msgid "Daily" msgstr "" msgid "Weekly" msgstr "" msgid "Monthly" msgstr "" msgid "Default" msgstr "" msgid "Fast" msgstr "" msgid "Slow" msgstr "" msgid "Slower" msgstr "" msgid "Ultralight" msgstr "" msgid "Light" msgstr "" msgid "Medium" msgstr "" msgid "Strong" msgstr "" msgid "Film" msgstr "" msgid "Grain" msgstr "" msgid "High Motion" msgstr "" msgid "Animation" msgstr "" msgid "Spatial" msgstr "" msgid "Temporal" msgstr "" msgid "Automatic" msgstr "" msgid "Optimal" msgstr "" msgid "Normal" msgstr "" msgid "Simple" msgstr "" msgid "Smart" msgstr "" msgid "Diamond" msgstr "" msgid "Hexagon" msgstr "" msgid "Uneven Multi-Hexagon" msgstr "" msgid "Exhaustive" msgstr "" msgid "Hadamard Exhaustive" msgstr "" msgid "Most" msgstr "" msgid "Some" msgstr "" msgid "All" msgstr "" msgid "Encode only" msgstr "" msgid "Always" msgstr "" msgid "(NTSC Film)" msgstr "" msgid "(PAL Film/Video)" msgstr "" msgid "(NTSC Video)" msgstr "" msgid "%d - %02dh%02dm%02ds - %s" msgstr "" msgid "%d (%05d.MPLS) - %02dh%02dm%02ds" msgstr "" msgid "%d (%05d.MPLS) - Unknown Length" msgstr "" msgid "%d - %02dh%02dm%02ds" msgstr "" msgid "%d - Unknown Length" msgstr "" msgid "No Titles" msgstr "Ingen titler" msgid "No Audio" msgstr "" msgid "Foreign Audio Search" msgstr "" #, c-format msgid "Chapter %2d" msgstr "" #, c-format msgid "N/A" msgstr "" #, c-format msgid "" "Invalid Deinterlace Settings:\n" "\n" "%s\n" msgstr "" #, c-format msgid "" "Invalid Detelecine Settings:\n" "\n" "%s\n" msgstr "" #, c-format msgid "" "Invalid Decomb Settings:\n" "\n" "%s\n" msgstr "" msgid "" "Theora is not supported in the MP4 container.\n" "\n" "You should choose a different video codec or container.\n" "If you continue, FFMPEG will be chosen for you." msgstr "" msgid "Continue" msgstr "" msgid "No title found.\n" msgstr "" msgid "" "Only one subtitle may be burned into the video.\n" "\n" "You should change your subtitle selections.\n" "If you continue, some subtitles will be lost." msgstr "" msgid "" "Srt file does not exist or not a regular file.\n" "\n" "You should choose a valid file.\n" "If you continue, this subtitle will be ignored." msgstr "" msgid "" "The source does not support Pass-Thru.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "%s is not supported in the %s container.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "The source audio does not support %s mixdown.\n" "\n" "You should choose a different mixdown.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "Unable to create %s.\n" "\n" "Internal error. Could not parse UI description.\n" "%s" msgstr "" msgid "Index" msgstr "" msgid "Duration" msgstr "" msgid "Title" msgstr "" msgid "Job Information" msgstr "" msgid "Track Information" msgstr "" msgid "Preset Name" msgstr "" msgid "The device or file to encode" msgstr "" msgid "The preset values to use for encoding" msgstr "" msgid "Spam a lot" msgstr "" msgid "- Transcode media formats" msgstr "" msgid "Globals" msgstr "" msgid "Presets" msgstr "" msgid "Folder" msgstr "" #, c-format msgid "%s path: (%s)" msgstr "" #, c-format msgid "%s indices: len %d" msgstr "" msgid "Type" msgstr "" msgid "Failed to find parent folder when adding child." msgstr "" msgid "Failed to find parent folder while adding child." msgstr "" #, c-format msgid "Can't map language value: (%s)" msgstr "" #, c-format msgid "Can't map value: (%s)" msgstr "" msgid "Subtitles" msgstr "" msgid "" "%s: Folder already exists.\n" "You can not replace it with a preset." msgstr "" msgid "" "%s: Preset already exists.\n" "You can not replace it with a folder." msgstr "" msgid "Import Preset" msgstr "" msgid "All (*)" msgstr "" msgid "Presets (*.plist)" msgstr "" msgid "Export Preset" msgstr "" msgid "" "Confirm deletion of %s:\n" "\n" "%s" msgstr "" msgid "folder" msgstr "" msgid "preset" msgstr "" msgid "No selection??? Perhaps unselected." msgstr "" #, c-format msgid "Gstreamer Error: %s" msgstr "" #, c-format msgid "" "Missing GStreamer plugin\n" "Audio or Video may not play as expected\n" "\n" "%s" msgstr "" msgid "Done" msgstr "" msgid "Windowed" msgstr "" msgid "Seconds" msgstr "" msgid "Frames" msgstr "" #, c-format msgid "" "%s (Title %d, %s %d through %d, 2 Video Passes) -->" " %s" msgstr "" #, c-format msgid "" "%s (Title %d, %s %d through %d) --> %s" msgstr "" #, c-format msgid "Modified Preset Based On: %s\n" msgstr "" #, c-format msgid "Preset: %s\n" msgstr "" #, c-format msgid "Format: %s Container\n" msgstr "" msgid "Container Options:" msgstr "" #, c-format msgid "%sChapter Markers" msgstr "" #, c-format msgid "%siPod 5G Support" msgstr "" #, c-format msgid "%sWeb Optimized" msgstr "" #, c-format msgid "%sLarge File Size (>4GB)" msgstr "" #, c-format msgid "Destination: %s\n" msgstr "" msgid "(Aspect Preserved)" msgstr "" msgid "(Aspect Lost)" msgstr "" msgid "(Anamorphic)" msgstr "" msgid "(Custom Anamorphic)" msgstr "" msgid "Picture: " msgstr "" #, c-format msgid "Source: %d x %d, Output %d x %d %s, Crop %d:%d:%d:%d" msgstr "" #, c-format msgid ", Display %d x %d" msgstr "" msgid "Filters:" msgstr "" #, c-format msgid "%sDetelecine" msgstr "" #, c-format msgid "%sDecomb" msgstr "" #, c-format msgid "%sDeinterlace" msgstr "" #, c-format msgid "%sDenoise Filter %s:" msgstr "" #, c-format msgid "%sDeblock: %d" msgstr "" #, c-format msgid "%sGrayscale" msgstr "" #, c-format msgid "Video: %s" msgstr "" #, c-format msgid ", Framerate: %s %s" msgstr "" #, c-format msgid ", Framerate: Peak %s (may be lower)" msgstr "" #, c-format msgid ", Framerate: %s (constant frame rate)" msgstr "" msgid "Error" msgstr "" msgid "Bitrate:" msgstr "" msgid "Bitrate" msgstr "" msgid "Quality" msgstr "" msgid "Turbo 1st Pass: On\n" msgstr "" #, c-format msgid "Video Options: Preset: %s" msgstr "" msgid " - Tune: " msgstr "" #, c-format msgid " - Profile: %s" msgstr "" #, c-format msgid " - Level: %s" msgstr "" #, c-format msgid "Advanced Options: %s\n" msgstr "" msgid "Audio: " msgstr "" #, c-format msgid "Audio Tracks: %d" msgstr "" #, c-format msgid "Bitrate: %d" msgstr "" #, c-format msgid "%s --> Encoder: %s" msgstr "" #, c-format msgid "%s --> Encoder: %s, Mixdown: %s, SampleRate: %s, %s" msgstr "" msgid "Subtitle: " msgstr "" #, c-format msgid "Subtitle Tracks: %d\n" msgstr "" msgid " (Force)" msgstr "" msgid " (Burn)" msgstr "" msgid " (Default)" msgstr "" #, c-format msgid " %s (%s), %s, Offset (ms) %d%s\n" msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "Another queued job has specified the same destination.\n" "Do you want to overwrite?" msgstr "" msgid "Overwrite" msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "This is not a valid directory." msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "Can not read or write the directory." msgstr "" #, c-format msgid "" "Destination filesystem is almost full: %uM free\n" "\n" "Encode may be incomplete if you proceed.\n" msgstr "" msgid "Proceed" msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "File already exists.\n" "Do you want to overwrite?" msgstr "" msgid "" "Duplicate destination files detected.\n" "Duplicates will not be added to the queue." msgstr "" msgid "No Title" msgstr "" msgid "" "There is another title with the same destination file name.\n" "This title will not be added to the queue unless you change\n" "the output file name.\n" msgstr "" msgid "Stop" msgstr "" msgid "Stop Encoding" msgstr "Stop konvertering" msgid "Resume" msgstr "" msgid "Resume Encoding" msgstr "Genoptag konvertering" msgid "S_top Queue" msgstr "S_top kø" msgid "_Start Queue" msgstr "_Start kø" msgid "_Resume Queue" msgstr "_Genoptag kø" msgid "Resume Queue" msgstr "" msgid "" "You are currently encoding. What would you like to do?\n" "\n" msgstr "" #, c-format msgid "" "You have %d unfinished job(s) in a saved queue.\n" "\n" "Would you like to reload them?" msgstr "" msgid "No" msgstr "" msgid "Yes" msgstr "" #, c-format msgid "Usage: %s infile [outfile]\n" msgstr "" #, c-format msgid "Offset: %dms" msgstr "" msgid "Burned Into Video" msgstr "" msgid "Passthrough" msgstr "" msgid "through" msgstr "" msgid "(Forced Subtitles Only)" msgstr "" msgid "(Default)" msgstr "" msgid "Error!" msgstr "" #, c-format msgid "Preferred Language: %s" msgstr "" #, c-format msgid "Add %s subtitle track if default audio is not %s" msgstr "" #, c-format msgid "Type %s" msgstr "" #, c-format msgid "Type %s value %s" msgstr "" #, c-format msgid "Type %s value %d" msgstr "" #, c-format msgid "Type %s value %" msgstr "" #, c-format msgid "Type %s value %f" msgstr "" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"%s\"" msgstr "" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"\"" msgstr "" msgid "Any" msgstr "" msgid "0: SAD, no subpel" msgstr "" msgid "4: SATD, qpel on all" msgstr "" msgid "5: SATD, multi-qpel on all" msgstr "" msgid "6: RD in I/P-frames" msgstr "" msgid "7: RD in all frames" msgstr "" msgid "8: RD refine in I/P-frames" msgstr "" msgid "9: RD refine in all frames" msgstr "" msgid "10: QPRD in all frames" msgstr "" msgid "11: No early terminations in analysis" msgstr "" msgid "Your names" msgstr "" msgid "Your emails" msgstr "" HandBrake-0.10.2/gtk/po/no.po0000664000175200017520000015677412466165306016253 0ustar handbrakehandbrake# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Alf Andreas Stray Rambo , 2014 msgid "" msgstr "" "Project-Id-Version: HandBrake-0.10\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-03 13:01+0300\n" "PO-Revision-Date: 2014-12-05 21:33+0000\n" "Last-Translator: Alf Andreas Stray Rambo \n" "Language-Team: Norwegian (http://www.transifex.com/projects/p/handbrake/language/no/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: no\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Quality: " msgstr "Kvalitet:" #, c-format msgid "Bitrate: %dkbps" msgstr "Bithastighet: %dkbps" #, c-format msgid "Bitrate: %.4gkbps" msgstr "Bithastighet: %.4gkbps" msgid "Passthrough" msgstr "Gjennomgang" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s\n" "Track Name: %s" msgstr "" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s" msgstr "" msgid "Add" msgstr "Legg til" msgid "" "Add an audio encoder.\n" "Each selected source track will be encoded with all selected encoders." msgstr "Legg til en lyd omkoder.\nHver valgte kilde spor vil bli kodet med alle valgte omkodere." msgid "Set the audio codec to encode this track with." msgstr "Sett en lyd kodek til å omkode dette sporet med." msgid "Set the bitrate to encode this track with." msgstr "Sett bithastigheten til å omkode dette sporet med." msgid "" "Audio Quality:\n" "For encoders that support it, adjust the quality of the output." msgstr "Lyd Kvalitet:\nFor omkoderene som støtter det, juster kvaliteten på utdataen." msgid "Set the mixdown of the output audio track." msgstr "Sett nedmiksen til utdataens lyd spor." msgid "Set the sample rate of the output audio track." msgstr "Sett samplingsfrekvensen til utdataens lyd spor." msgid "" "Audio Gain:\n" "Adjust the amplification or attenuation of the output audio track." msgstr "" msgid "0dB" msgstr "0dB" msgid "%ddB" msgstr "%ddB" msgid "%.4gkHz" msgstr "%.4gkHz" msgid "%d - %s (%.4gkHz)" msgstr "%d - %s (%.4gkHz)" msgid "" "Dynamic Range Compression:\n" "Adjust the dynamic range of the output audio track.\n" "For source audio that has a wide dynamic range,\n" "very loud and very soft sequences, DRC allows you\n" "to 'compress' the range by making loud sounds\n" "softer and soft sounds louder.\n" msgstr "" msgid "Remove this audio encoder" msgstr "Fjern denne lyd omkoderen" msgid "Closing HandBrake will terminate encoding.\n" msgstr "Lukking av HandBrake vil avslutte omkodingen.\n" msgid "No Title Found" msgstr "Ingen Tittel Funnet" msgid "none" msgstr "Ingen" msgid "Not Selected" msgstr "Ikke Valgt" msgid "zerolatency x264 tune selected, forcing constant framerate" msgstr "" msgid "Scanning ..." msgstr "Skanning ..." msgid "Stop Scan" msgstr "Stopp Skanning" msgid "On" msgstr "På" msgid "Strict" msgstr "" msgid "Loose" msgstr "" msgid "Custom" msgstr "" msgid "Unknown" msgstr "Ukjent" msgid "auto" msgstr "Automatisk" #, c-format msgid "" "%s\n" "\n" "%s in %d seconds ..." msgstr "%s\n\n%s i %d sekunder ..." #, c-format msgid "%sYour movie will be lost if you don't continue encoding." msgstr "%sDin video vil bli mistet hvis du ikke fortsetter med omkodingen." msgid "Cancel Current and Stop" msgstr "Avbryt Nåværende og Stopp" msgid "Cancel Current, Start Next" msgstr "Avbryt Nåværende, Start Neste" msgid "Finish Current, then Stop" msgstr "Fullføre Nåværende, Så Stopp" msgid "Continue Encoding" msgstr "Fortsett Omkodingen" msgid "Custom " msgstr "" msgid "Modified " msgstr "Modifisert" #, c-format msgid "Handbrake Version: %s (%d)\n" msgstr "Handbrake Versjon: %s (%d)\n" #, c-format msgid "%d encode(s) pending" msgstr "%d omkoder(e) ventende" #, c-format msgid "job %d of %d, " msgstr "jobb %d av %d," #, c-format msgid "pass %d (subtitle scan) of %d, " msgstr "" #, c-format msgid "pass %d of %d, " msgstr "" #, c-format msgid "Encoding: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" msgstr "Omkoder: %s%s%.2f %% (%.2f fps, snitt %.2f fps, ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %% (ETA %02dh%02dm%02ds)" msgstr "Omkoder: %s%s%.2f %% (ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %%" msgstr "Omkoder: %s%s%.2f %%" msgid "Searching for start time, " msgstr "Søker etter start tid," msgid "Scanning..." msgstr "Skanning..." #, c-format msgid "Scanning title %d of %d..." msgstr "Skanning tittel %d av %d..." #, c-format msgid "Scanning title %d of %d preview %d..." msgstr "Skanning tittel %d av %d forhåndsvisning %d..." msgid "Source" msgstr "Kilde" msgid "Choose Video Source" msgstr "Velg Video Kilde" msgid "Paused" msgstr "Pauset" msgid "Encode Done!" msgstr "Omkoding Ferdig!" msgid "Encode Canceled." msgstr "Omkoding Kansellert." msgid "Encode Failed." msgstr "Omkoding Feilet." msgid "Muxing: This may take a while..." msgstr "Multipleksing: Dette kan ta litt tid..." msgid "Scan this DVD source" msgstr "Skann denne DVD kilden" msgid "AutoScan" msgstr "Automatisk Skanning" msgid "Suspend" msgstr "Suspendere" #, c-format msgid "Suspend failed: %s" msgstr "Suspendering feilet: %s" msgid "Suspend failed" msgstr "Suspendering feilet" msgid "Shutdown" msgstr "Slå av" #, c-format msgid "Shutdown failed: %s" msgstr "Avslåing feilet: %s" msgid "Shutdown failed" msgstr "Avslåing feilet" msgid "Encoding" msgstr "Omkoder" #, c-format msgid "press %d %d" msgstr "" #, c-format msgid "" "Invalid Settings:\n" "%s" msgstr "Ugyldig Innstillinger:\n%s" msgid "Cancel" msgstr "Avbryt" #, c-format msgid "%s: %.4g (Warning: lossless)" msgstr "%s: %.4g (Advarsel: Lossless)" #, c-format msgid "HandBrake %s/%s is now available (you have %s/%d)." msgstr "HandBrake %s/%s er nå tilgjengelig (Du har %s/%d)." msgid "Encode Complete" msgstr "Omkoding Fullført" msgid "Put down that cocktail, Your HandBrake queue is done!" msgstr "Sett ned den drinken, Din Handbrake kø er ferdig!" msgid "Your encode is complete." msgstr "Din omkoding er fullført." msgid "Shutting down the computer" msgstr "Slår av datamaskinen" msgid "Putting computer to sleep" msgstr "Setter datamaskinen i dvale" msgid "Quiting Handbrake" msgstr "Avslutter Handbrake" msgid "Bottom" msgstr "Bunn" #, c-format msgid "open failed: %s\n" msgstr "åpning feilet: %s\n" msgid "No key for dictionary item" msgstr "" msgid "Invalid container type. This shouldn't happen" msgstr "" #, c-format msgid "%s:missing a requried attribute" msgstr "" #, c-format msgid "" "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" msgstr "" msgid "language" msgstr "språk" msgid "Language" msgstr "Språk" msgid "" "The language this text is in, as an ISO code. Pango can use this as a hint " "when rendering the text. If you don't understand this parameter, you " "probably don't need it" msgstr "" msgid "" "The preferred place to ellipsize the string, if the cell renderer does not " "have enough room to display the entire string" msgstr "" msgid "" "How to break the string into multiple lines, if the cell renderer does not " "have enough room to display the entire string" msgstr "" msgid "" "Render the subtitle over the video.\n" "\n" "The subtitle will be part of the video and can not be disabled." msgstr "" msgid "Burned In" msgstr "Brennet inn" msgid "" "Set the default output subtitle track.\n" "\n" "Most players will automatically display this\n" "subtitle track whenever the video is played.\n" "\n" "This is useful for creating a \"forced\" track\n" "in your output." msgstr "" msgid "Default" msgstr "Standard" msgid "" "Use only subtitles that have been flagged\n" "as forced in the source subtitle track\n" "\n" "\"Forced\" subtitles are usually used to show\n" "subtitles during scenes where someone is speaking\n" "a foreign language." msgstr "" msgid "Forced Only" msgstr "Bare Tvunget" msgid "" "Add (or subtract) an offset (in milliseconds)\n" "to the start of the SRT subtitle track.\n" "\n" "Often, the start of an external SRT file\n" "does not coincide with the start of the video.\n" "This setting allows you to synchronize the files." msgstr "" msgid "SRT Offset" msgstr "" msgid "Off" msgstr "Av" msgid "" "The source subtitle track\n" "\n" "You can choose any of the subtitles\n" "recognized in your source file.\n" "\n" "In addition, there is a special track option\n" "\"Foreign Audio Search\". This option will add\n" "an extra pass to the encode that searches for\n" "subtitles that may correspond to a foreign\n" "language scene. This option is best used in\n" "conjunction with the \"Forced\" option." msgstr "" msgid "Track" msgstr "" msgid "About HandBrake" msgstr "Om HandBrake" msgid "" "Copyright © 2008 - 2013 John Stebbins\n" "Copyright © 2004 - 2013, HandBrake Devs" msgstr "Kopibeskyttet © 2008 - 2013 John Stebbins\nKopibeskyttet © 2004 - 2013 HandBrake Devs" msgid "" "HandBrake is a GPL-licensed, multiplatform, multithreaded video transcoder." msgstr "HandBrake er en GPL-lisensiert, multiplatform, multitrådet video transkoder." msgid "http://handbrake.fr" msgstr "http://handbrake.fr" msgid "" "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n" "\n" "HandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgstr "HandBrake er gratis programvare; du kan redistribuere det og/eller modifisere det under vilkårene til GNU General Public License som er publisert av Free Software Foundation; enten versjon 2 av lisensen, eller (hvis du vil) en nyere versjon.\n\nHandBrake er distribuert i håp om at det skal være nyttig, men UTEN NOEN FORM FOR GARANTI; også uten implisitt garanti av SALGBARHET eller EGENHET FOR ET SPESIELT FORMÅL. Se GNU General Public License for flere detaljer.\n\nDu skal motta en kopi av GNU General Public License sammen med Glade; hvis ikke, skriv til Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgid "_Minimize/Maximize" msgstr "_Minimere/Maximere" msgid "_Pause Queue" msgstr "_Pause køen" msgid "_Quit" msgstr "_Avslutt" msgid "_About" msgstr "_Om" msgid "HandBrake" msgstr "HandBrake" msgid "_File" msgstr "_Fil" msgid "_Source" msgstr "_Kilde" msgid "Single _Title" msgstr "Enkel_Tittel" msgid "_Destination" msgstr "_Destinasjon" msgid "_Preferences" msgstr "_Alternativer" msgid "_Queue" msgstr "_Køen" msgid "_Add" msgstr "_Legg til" msgid "Add _Multiple" msgstr "Legg til _Flere" msgid "_Start" msgstr "_Start" msgid "_Pause" msgstr "_Pause" msgid "_View" msgstr "_Visning" msgid "HandBrake For _Dumbies" msgstr "HandBrake For _Idioter" msgid "_Show Presets" msgstr "_Vis Forhåndsinnstillinger" msgid "_Preview" msgstr "_Forhåndsvisning" msgid "_Activity Window" msgstr "_Aktivitets Vindu" msgid "Show _Queue" msgstr "Vis _Køen" msgid "_Presets" msgstr "_Forhåndsinnstillinger" msgid "_Save" msgstr "_Lagre" msgid "_Delete" msgstr "_Slett" msgid "_Make Default" msgstr "_Sett som Standard" msgid "_New Folder" msgstr "_Ny Mappe" msgid "_Export" msgstr "_Exportere" msgid "_Import" msgstr "_Importere" msgid "_Update Built-in Presets" msgstr "_Oppdater Innebygde Forhåndsinnstillinger" msgid "_Help" msgstr "_Hjelp" msgid "_Guide" msgstr "_Veiledning" msgid "Start Encoding" msgstr "Start Omkoding" msgid "Start" msgstr "Start" msgid "Pause Encoding" msgstr "Pause Omkodingen" msgid "Pause" msgstr "Pause" msgid "Add to Queue" msgstr "Legg til Køen" msgid "Enqueue" msgstr "Legge i køen" msgid "Show Queue" msgstr "Vis Køen" msgid "Queue" msgstr "Køen" msgid "" "Open Picture Settings and Preview window.\n" "Here you can adjust cropping, resolution, aspect ratio, and filters." msgstr "Åpne Bilde Instillinger og Forhåndsvis vindu.\nHer kan du justere beskjæring, oppløsning, størrelsesforholdet, og filtre." msgid "Preview" msgstr "Forhåndsvisning" msgid "Show Activity Window" msgstr "Vis Aktivt Vindu" msgid "Activity" msgstr "Aktivitet" msgid "Source:" msgstr "Kilde:" msgid "None" msgstr "Ingen" msgid "Title:" msgstr "Tittel:" msgid "" "Set the title to encode.\n" "By default the longest title is chosen.\n" "This is often the feature title of a DVD." msgstr "Sett tittelen som skal omkodes.\nSom standard er den lengste tittelen valgt.\nDet er ofte funksjons tittelen på en DVD." msgid "Angle:" msgstr "Vinkel:" msgid "For multi-angle DVD's, select the desired angle to encode." msgstr "For flere vinklet DVD'er, velg den ønskede vinkelen til omkoding." msgid "Reset All Titles" msgstr "Tilbakestill Alle Tittelene" msgid "Apply current settings to all titles" msgstr "Bruk nåværende innstillinger på alle tittelene" msgid "Range of title to encode. Can be chapters, seconds, or frames." msgstr "Utvalg av titler til omkoding. Kan være Kapitler, sekunder, eller bilder." msgid "Set the first chapter to encode." msgstr "Sett det første kapittelet til omkoding." msgid "Set the last chapter to encode." msgstr "Sett det siste kapittelet til omkoding." msgid "Duration:" msgstr "Varighet:" msgid "Destination" msgstr "Destinasjon" msgid "File:" msgstr "Fil:" msgid "Destination filename for your encode." msgstr "Destinasjons filnavn for din omkoding." msgid "Destination directory for your encode." msgstr "Destinasjons mappe for din omkoding." msgid "Destination Directory" msgstr "Destinasjons mappe" msgid "Format:" msgstr "Format:" msgid "Format to mux encoded tracks to." msgstr "" msgid "iPod 5G Support" msgstr "IPod 4G Støtte" msgid "Add iPod Atom needed by some older iPods." msgstr "Legg til iPod Atom som brukes av noen eldre versjoner av iPod." msgid "Web optimized" msgstr "Internett optimalisert" msgid "" "Optimize the layout of the MP4 file for progressive download.\n" "This allows a player to initiate playback before downloading the entire file." msgstr "Optimaliser oppsettet av MP4 filen for fremskrittsvennlig nedlasting.\nDette gjør det mulig å starte avspilling før hele filen er lastet ned." msgid "Large file (>4GB)" msgstr "Stor fil (>4GB)" msgid "" "Allow 64 bit MP4 file which can be over 4GB.\n" "\n" "Caution: This option may break device compatibility." msgstr "Tillat 64 bit MP4 filer som kan være over 4GB.\n\nAdvarsel: Denne innstillingen kan ødelegge enhetens kompatibilitet." msgid "Presets List" msgstr "Forhåndsinnstillings liste" msgid "Source Codec:" msgstr "Kilde Kodek:" msgid "Dimensions:" msgstr "Dimensjon:" msgid "Aspect: " msgstr "Aspekt:" msgid "Frame Rate:" msgstr "Bildefrekvens:" msgid "Source Picture Parameters" msgstr "Kilde Bilde Parametere" msgid "Autocrop:" msgstr "Auto-beskjæring:" msgid "Crop:" msgstr "Beskjæring:" msgid "Crop Dimensions:" msgstr "Beskjær Dimensjonen:" msgid "Cropping" msgstr "Beskjæring" msgid "Scale Dimensions:" msgstr "Skalere Dimensjonen:" msgid "Optimal for Source:" msgstr "Optimal som Kilde:" msgid "Anamorphic:" msgstr "Anamorfisk:" msgid "Scaling" msgstr "Skalere" msgid "Presentation Dimensions:" msgstr "" msgid "Summary" msgstr "Sammendrag" msgid "Left Crop" msgstr "Venstre Beskjæring" msgid "Top Crop" msgstr "Topp Beskjæring" msgid "Bottom Crop" msgstr "Bunn Beskjæring" msgid "Right Crop" msgstr "Høyre Beskjæring" msgid "Auto Crop" msgstr "Automatisk Beskjæring" msgid "Automatically crop black borders around edges of the video." msgstr "Automatisk beskjær svarte grenser rundt kanten på videoen." msgid "Loose Crop" msgstr "" msgid "" "When picture settings require that the image\n" "dimensions be rounded to some multiple number\n" "of pixels, this setting will crop a few extra pixels\n" "instead of doing exact cropping and then scaling to\n" "the required multiple." msgstr "" msgid "width:" msgstr "Bredde:" msgid "" "This is the width that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "Dette er bredden videoen vil bli lagret på.\nDe faktiske visnings dimensjonene vil være forskjellig hvis piksel-formatet ikke er 1:1." msgid "height:" msgstr "Høyde:" msgid "" "This is the height that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "Dette er høyden videoen vil bli lagret på.\nDe faktiske visnings dimensjonene vil være forskjellig hvis piksel-formatet ikke er 1:1." msgid "Optimal for source" msgstr "Optimal for kilde" msgid "" "If enabled, select the 'optimal' storage resolution.\n" "This will be the resolution that most closely matches the source resolution after cropping." msgstr "" msgid "" "Anamorphic Modes:\n" "\n" "None - Force pixel aspect ratio to 1:1.\n" "Loose - Align dimensions to chosen 'Alignment' value\n" " and pick pixel aspect ratio that preserves the\n" " original display aspect ratio\n" "Strict - Keep original source dimensions and pixel\n" " aspect ratio" msgstr "" msgid "Alignment:" msgstr "" msgid "" "Align storage dimensions to multiples of this value.\n" "\n" "This setting is only necessary for compatibility with some devices.\n" "You should use 2 unless you experience compatibility issues." msgstr "" msgid "Storage Geometry" msgstr "" msgid "" "This is the display width. It is the result of scaling the storage " "dimensions by the pixel aspect." msgstr "" msgid "Pixel Aspect:" msgstr "" msgid "" "Pixel aspect defines the shape of the pixels.\n" "\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "" msgid ":" msgstr ":" msgid "" "Pixel aspect defines the shape of the pixels.\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "" msgid "Keep Aspect" msgstr "" msgid "" "If enabled, the original display aspect of the source will be maintained." msgstr "" msgid "Display Aspect:" msgstr "" msgid "Display Geometry" msgstr "" msgid "Grayscale" msgstr "Gråskala" msgid "If enabled, filter colour components out of video." msgstr "" msgid "Deblock:" msgstr "" msgid "" "The deblocking filter removes a common type of compression artifact.\n" "If your source exhibits 'blockiness', this filter may help clean it up." msgstr "" msgid "Denoise Filter:" msgstr "" msgid "" "Denoise filtering reduces or removes the appearance of noise and grain.\n" "Film grain and other types of high frequency noise are difficult to compress.\n" "Using this filter on such sources can result in smaller file sizes." msgstr "" msgid "Denoise Preset:" msgstr "" msgid "Denoise Tune:" msgstr "" msgid "" "Custom denoise filter string format\n" "\n" "SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgstr "" msgid "Detelecine:" msgstr "" msgid "" "This filter removes 'combing' artifacts that are the result of telecining.\n" "\n" "Telecining is a process that adjusts film framerates that are 24fps to NTSC video frame rates which are 30fps." msgstr "" msgid "" "Custom detelecine filter string format\n" "\n" "JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgstr "" msgid "Decomb" msgstr "" msgid "" "Choose decomb or deinterlace filter options.\n" "\n" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced.\n" "\n" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "" msgid "Deinterlace" msgstr "" msgid "Decomb:" msgstr "" msgid "" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced." msgstr "" msgid "" "Custom decomb filter string format\n" "\n" "Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\n" "BlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\n" "ErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgstr "" msgid "Deinterlace:" msgstr "" msgid "" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "" msgid "" "Custom deinterlace filter string format\n" "\n" "YadifMode:YadifParity:McdintMode:McdeintQp" msgstr "" msgid "Filters" msgstr " Filtre" msgid "Picture" msgstr "Bilde" msgid "Video Encoder:" msgstr "Video Omkoder:" msgid "Available video encoders." msgstr "Tilgjengelig video omkodere." msgid "Framerate:" msgstr "Bildefrekvens:" msgid "" "Output framerate.\n" "\n" "'Same as source' is recommended. If your source video has\n" "a variable framerate, 'Same as source' will preserve it." msgstr "Utdataens bildefrekvens.\n\n'Samme som kilde' er anbefalt. Hvis kilde videoen har\nen variabel bildefrekvens, 'Samme som kilde' vil bevare det." msgid "Constant Framerate" msgstr "Konstant Bildefrekvens" msgid "Same as source" msgstr "Samme som kilde" msgid "kbps" msgstr "Kbps" msgid "(variable)" msgstr "(variabel)" msgid "(constant)" msgstr "(konstant)" msgid "Enables constant framerate output." msgstr "Aktiverer konstant bildefrekvens utdata." msgid "Peak Framerate (VFR)" msgstr "Toppunkt Bildefrekvens (VFR)" msgid "" "Enables variable framerate output with a peak\n" "rate determined by the framerate setting.\n" "\n" "VFR is not compatible with some players." msgstr "" msgid "Variable Framerate" msgstr "Variabel Bildefrekvens" msgid "" "Enables variable framerate output.\n" "\n" "VFR is not compatible with some players." msgstr "Aktiverer variabel bildefrekvens utdata.\n\nVFR er ikke kompatibel med noen avspillere." msgid "" "Set the desired quality factor.\n" "The encoder targets a certain quality.\n" "The scale used by each video encoder is different.\n" "\n" "x264's scale is logarithmic and lower values correspond to higher quality.\n" "So small decreases in value will result in progressively larger increases\n" "in the resulting file size. A value of 0 means lossless and will result\n" "in a file size that is larger than the original source, unless the source\n" "was also lossless.\n" "\n" "FFMpeg's and Theora's scale is more linear.\n" "These encoders do not have a lossless mode." msgstr "" msgid "Constant Quality:" msgstr "Konstant Kvalitet:" msgid "Bitrate (kbps): " msgstr "Bithastighet (kbps)" msgid "" "Set the average bitrate.\n" "\n" "The instantaneous bitrate can be much higher or lower at any point in time.\n" "But the average over a long duration will be the value set here. If you need\n" "to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings." msgstr "" msgid "2-Pass Encoding" msgstr "" msgid "" "Perform 2 Pass Encoding.\n" "\n" "The 'Bitrate' option is prerequisite. During the 1st pass, statistics about\n" "the video are collected. Then in the second pass, those statistics are used\n" "to make bitrate allocation decisions." msgstr "" msgid "Turbo First Pass" msgstr "" msgid "" "During the 1st pass of a 2 pass encode, use settings that speed things " "along." msgstr "" msgid "Use Advanced Options" msgstr "Bruk Avanserte Innstillinger" msgid "" "Use advanced options Tab for x264 settings.\n" "\n" "Use at your own risk!" msgstr "Bruk avanserte alternativer fanen for x264 innstillinger.\n\nBruk på ditt eget risiko!" msgid "Preset:" msgstr "Forhåndsinnstilt:" msgid "" "Adjusts encoder settings to trade off compression efficiency against encoding speed.\n" "\n" "This establishes your default encoder settings.\n" "Tunes, profiles, levels and advanced option string will be applied to this.\n" "You should generally set this option to the slowest you can bear since slower\n" "settings will result in better quality or smaller files." msgstr "" msgid "Tune:" msgstr "" msgid "" "Tune settings to optimize for common scenarios.\n" "\n" "This can improve effeciency for particular source characteristics or set\n" "characteristics of the output file. Changes will be applied after the\n" "preset but before all other parameters." msgstr "" msgid "Fast Decode" msgstr "Rask Dekoding" msgid "" "Reduce decoder CPU usage.\n" "\n" "Set this if your device is struggling to play the output (dropped frames)." msgstr "Reduser dekoders CPU bruk.\n\nSett denne hvis din enhet sliter med avspilling av utdataen (mistede bilder)." msgid "Zero Latency" msgstr "" msgid "" "Minimize latency between input to encoder and output of decoder.\n" "\n" "This is useful for broadcast of live streams.\n" "\n" "Since HandBrake is not suitable for live stream broadcast purposes,\n" "this setting is of little value here." msgstr "" msgid "Profile:" msgstr "Profil:" msgid "" "Sets and ensures compliance with the specified profile.\n" "\n" "Overrides all other settings." msgstr "" msgid "Level:" msgstr "Nivå:" msgid "" "Sets and ensures compliance with the specified level.\n" "\n" "Overrides all other settings." msgstr "" msgid "More Settings:" msgstr "Flere Innstillinger:" msgid "" "Additional encoder settings.\n" "\n" "Colon separated list of encoder options." msgstr "" msgid "Video" msgstr "Video" msgid "Selection Behavior:" msgstr "" msgid "Remove" msgstr "Fjern" msgid "Available Languages" msgstr "Tilgjengelige Språk" msgid "Selected Languages" msgstr "Valgt Språk" msgid "Use only first encoder for secondary audio" msgstr "Bruk bare første omkoder for sekundær lyd" msgid "" "Only the primary audio track will be encoded with the full encoder list.\n" "All other secondary audio output tracks will be encoded with first encoder only." msgstr "" msgid "Auto Passthru:" msgstr "" msgid "MP3" msgstr "MP3" msgid "" "Enable this if your playback device supports MP3.\n" "This permits MP3 passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports AAC.\n" "This permits AAC passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports AC-3.\n" "This permits AC-3 passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports DTS.\n" "This permits DTS passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports DTS-HD.\n" "This permits DTS-HD passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "Passthru Fallback:" msgstr "" msgid "" "Set the audio codec to encode with when a suitable track can not be found " "for audio passthru." msgstr "" msgid "Audio Encoder Settings:" msgstr " Lyd Omkoder Innstillinger:" msgid "Each selected source track will be encoded with all selected encoders" msgstr "Hver valgt kilde spor vil bli omkodet med alle valgte omkodere" msgid "Encoder" msgstr "Omkoder" msgid "Bitrate/Quality" msgstr "Bithastighet/Kvalitet" msgid "Mixdown" msgstr "" msgid "Samplerate" msgstr "Samplingsfrekvens" msgid "Gain" msgstr "" msgid "Audio Defaults" msgstr "Lyd Standard" msgid "Add new audio settings to the list" msgstr "Legg til nye lyd innstillinger til listen" msgid "Add All" msgstr "Legg til alle" msgid "Add all audio tracks to the list" msgstr "Legg til alle lyd spor til listen" msgid "Reload Defaults" msgstr "Last inn på nytt standard" msgid "Reload all audio settings from defaults" msgstr "Last inn på nytt alle lyd innstillinger fra standard" msgid "Audio List" msgstr "Lyd liste" msgid "Preferred Language: None" msgstr "Foretrukket Språk: Ingen " msgid "Add Foreign Audio Search Pass" msgstr "" msgid "" "Add \"Foreign Audio Search\" when the default audio track is your preferred language.\n" "This search pass finds short sequences of foreign audio and provides subtitles for them." msgstr "" msgid "Add subtitle track if default audio is foreign" msgstr "" msgid "" "When the default audio track is not your preferred language, add a subtitle " "track." msgstr "" msgid "Add Closed Captions when available" msgstr "" msgid "" "Closed captions are text subtitles that can be added to any container as a " "soft subtitle track (not burned)" msgstr "" msgid "Subtitle Defaults" msgstr "" msgid "Add new subtitle settings to the list" msgstr "" msgid "Add all subtitle tracks to the list" msgstr "" msgid "Reload all subtitle settings from defaults" msgstr "" msgid "Subtitle List" msgstr "" msgid "Reference Frames:" msgstr "" msgid "" "Sane values are ~1-6. The more you add, the better the compression, but the slower the encode.\n" "Cel animation tends to benefit from more reference frames a lot more than film content.\n" "\n" "Note that many hardware devices have limitations on the number of supported reference\n" "frames, so if you're encoding for a handheld or standalone player, don't touch this unless\n" "you're absolutely sure you know what you're doing!" msgstr "" msgid "Maximum B-Frames:" msgstr "" msgid "" "Sane values are ~2-5. This specifies the maximum number of sequential B-frames that the encoder can use.\n" "\n" "Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.\n" "Cel-animated source material and B-pyramid also significantly increase the usefulness of larger\n" "values.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off)." msgstr "" msgid "Pyramidal B-Frames:" msgstr "" msgid "" "B-pyramid improves compression by creating a pyramidal structure (hence the name)\n" "of B-frames, allowing B-frames to reference each other to improve compression.\n" "\n" "Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit." msgstr "" msgid "Weighted P-Frames:" msgstr "" msgid "" "Performs extra analysis to decide upon weighting parameters for each frame.\n" "\n" "This improves overall compression slightly and improves the quality of fades greatly.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires weighted P-frame\n" "prediction to be disabled. Note that some devices and players, even those that support\n" "Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is\n" "completely incompatible with it, for example." msgstr "" msgid "8x8 Transform" msgstr "" msgid "" "The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.\n" "\n" "It improves compression by at least 5% at a very small speed cost and may\n" "provide an unusually high visual quality benefit compared to its compression\n" "gain. However, it requires High Profile, which many devices may not support." msgstr "" msgid "CABAC Entropy Encoding" msgstr "" msgid "" "After the encoder has done its work, it has a bunch of data that\n" "needs to be compressed losslessly, similar to ZIP or RAR. H.264 provides\n" "two options for this: CAVLC and CABAC. CABAC decodes a lot slower but\n" "compresses significantly better (10-30%), especially at lower bitrates.\n" "\n" "If you're looking to minimize CPU requirements for video playback, disable this option.\n" "Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled." msgstr "" msgid "Encoding Features" msgstr "" msgid "Motion Est. Method:" msgstr "" msgid "" "Controls the motion estimation method.\n" "\n" "Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.\n" "A better motion search method improves compression at the cost of speed.\n" "\n" "Diamond: performs an extremely fast and simple search using a diamond pattern.\n" "Hexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n" "Uneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n" "Exhaustive: performs a \"dumb\" search of every pixel in a wide area. Significantly slower for only a small compression gain.\n" "Transformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement." msgstr "" msgid "Subpel ME & Mode:" msgstr "" msgid "" "This setting controls both subpixel-precision motion estimation and mode decision methods.\n" "\n" "Subpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n" "Mode decision is the method used to choose how to encode each block of the frame: a very important decision.\n" "SAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n" "6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n" "11 disables all early terminations in analysis.\n" "10 and 11, the most powerful and slowest options, require adaptive quantization (aq-mode > 0) and trellis 2 (always)." msgstr "" msgid "Motion Est. Range:" msgstr "" msgid "" "This is the distance x264 searches from its initial guess at the\n" "motion of a block in order to try to find its actual motion.\n" "\n" "The default is fine for most content, but extremely high motion video,\n" "especially at HD resolutions, may benefit from higher ranges, albeit at\n" "a high speed cost." msgstr "" msgid "Adaptive Direct Mode:" msgstr "" msgid "" "H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n" "\n" "Spatial, the default, is almost always better, but temporal is sometimes useful too.\n" "x264 can, at the cost of a small amount of speed (and accordingly for a small compression gain),\n" "adaptively select which is better for each particular frame." msgstr "" msgid "Adaptive B-Frames:" msgstr "" msgid "" "x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n" "\n" "Fast mode takes roughly the same amount of time no matter how many B-frames you specify.\n" "However, while fast, its decisions are often suboptimal.\n" "\n" "Optimal mode gets slower as the maximum number of B-Frames increases,\n" "but makes much more accurate decisions, especially when used with B-pyramid." msgstr "" msgid "Partitions:" msgstr "" msgid "" "Mode decision picks from a variety of options to make its decision:\n" "this option chooses what options those are.\n" "\n" "Fewer partitions to check means faster encoding, at the cost of worse\n" "decisions, since the best option might have been one that was turned off." msgstr "" msgid "Trellis:" msgstr "" msgid "" "Trellis fine-tunes the rounding of transform coefficients to\n" "squeeze out 3-5% more compression at the cost of some speed.\n" "\n" "\"Always\" uses trellis not only during the main encoding process, but also\n" "during analysis, which improves compression even more, albeit at great speed cost.\n" "\n" "Trellis costs more speed at higher bitrates and requires CABAC." msgstr "" msgid "Analysis" msgstr "" msgid "Adaptive Quantization Strength:" msgstr "" msgid "" "Adaptive quantization controls how the encoder distributes bits across the frame.\n" "\n" "Higher values take more bits away from edges and complex areas to improve areas with finer detail." msgstr "" msgid "Psychovisual Rate Distortion:" msgstr "" msgid "" "Psychovisual rate-distortion optimization takes advantage of the characteristics of human\n" "vision to dramatically improve apparent detail and sharpness.\n" "The effect can be made weaker or stronger by adjusting the strength.\n" "Being an RD algorithm, it requires mode decision to be at least \"6\"." msgstr "" msgid "Psychovisual Trellis:" msgstr "" msgid "" "Psychovisual trellis is an experimental algorithm to further\n" "improve sharpness and detail retention beyond what Psychovisual RD does.\n" "\n" "Recommended values are around 0.2, though higher values may help for very\n" "grainy video or lower bitrate encodes. Not recommended for cel animation\n" "and other sharp-edged graphics." msgstr "" msgid "Deblocking: " msgstr "" msgid "" "H.264 deblocking filter.\n" "\n" "h.264 has a built-in deblocking filter that smooths out blocking artifacts\n" "after decoding each frame. This not only improves visual quality, but also\n" "helps compression significantly. The deblocking filter takes a lot of CPU power,\n" "so if you're looking to minimize CPU requirements for video playback, disable it.\n" "\n" "The deblocking filter has two adjustable parameters, \"strength\" (Alpha) and \"threshold\" (Beta).\n" "The former controls how strong (or weak) the deblocker is, while the latter controls how many\n" "(or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking.\n" "The default is 0 (normal strength) for both parameters." msgstr "" msgid "No DCT Decimate" msgstr "" msgid "" "x264 normally zeroes out nearly-empty data blocks to save bits to\n" "be better used for some other purpose in the video. However, this can\n" "sometimes have slight negative effects on retention of subtle grain and\n" "dither.\n" "\n" "Don't touch this unless you're having banding issues or other such cases\n" "where you are having trouble keeping fine noise." msgstr "" msgid "Psychovisual" msgstr "" msgid "" "Your selected options will appear here.\n" "You can edit these and add additional options.\n" "\n" "Default values will not be shown. The defaults are:\n" "ref=3:bframes=3:b-adapt=fast:direct=spatial:\n" "b-pyramid=normal:weightp=2:me=hex:merange=16:\n" "subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\n" "deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\n" "no-fast-pskip=0:no-dct-decimate=0:cabac=1" msgstr "" msgid "Current x264 Advanced Option String" msgstr "" msgid "Advanced Video" msgstr "Avansert Video" msgid "Chapter Markers" msgstr "Kapittel Merker" msgid "Add chapter markers to output file." msgstr "Legg til kapittel merker til utdata filen" msgid "Chapters" msgstr "Kapitler " msgid "Actors:" msgstr "Skuespillere:" msgid "Director:" msgstr "Regissør:" msgid "Release Date:" msgstr "Utgivelsedato:" msgid "Comment:" msgstr "Kommentar:" msgid "Genre:" msgstr "Sjanger:" msgid "Description:" msgstr "Beskrivelse:" msgid "Plot:" msgstr "Handling:" msgid "Tags" msgstr "Merkelapp" msgid "Settings" msgstr "Innstillinger" msgid "Edit" msgstr "Redigere" msgid "Reload" msgstr "Last inn på nytt" msgid "" "Mark selected queue entry as pending.\n" "Resets the queue job to pending and ready to run again." msgstr "" msgid "Reload All" msgstr "Last inn på nytt alle" msgid "" "Mark all queue entries as pending.\n" "Resets all queue jobs to pending and ready to run again." msgstr "" msgid "OK" msgstr "OK" msgid "Select All" msgstr "Velg Alle" msgid "Mark all titles for adding to the queue" msgstr "Merk alle titlene for å legge dem til køen" msgid "Clear All" msgstr "Fjern Alle" msgid "Unmark all titles" msgstr "Avmerk alle titlene" msgid "Destination files OK. No duplicates detected." msgstr "Destinasjons fil OK. Ingen duplikat oppdaget." msgid "Select this title for adding to the queue.\n" msgstr "" msgid "Preferences" msgstr "" msgid "Automatically check for updates" msgstr "Automatisk se etter oppdateringer" msgid "When all encodes are complete" msgstr "Når alle omkoderene er ferdig" msgid "Use automatic naming (uses modified source name)" msgstr "Bruk automatisk navn givning (bruker modifiserte kilde navn)" msgid "Auto-Name Template" msgstr "" msgid "" "Available Options: {source} {title} {chapters} {date} {time} {quality} " "{bitrate}" msgstr "" msgid "Use iPod/iTunes friendly (.m4v) file extension for MP4" msgstr "Bruk iPod/iTunes vennelig (.m4v) fil utvidelse for MP4" msgid "Number of previews" msgstr "Antall av forhåndsvisninger" msgid "Filter short titles (seconds)" msgstr "" msgid "Show system tray icon" msgstr "" msgid "General" msgstr "" msgid "Constant Quality fractional granularity" msgstr "" msgid "Use dvdnav (instead of libdvdread)" msgstr "" msgid "Put individual encode logs in same location as movie" msgstr "" msgid "Activity Log Verbosity Level" msgstr "" msgid "Activity Log Longevity" msgstr "" msgid "Scale down High Definition previews" msgstr "" msgid "Automatically Scan DVD when loaded" msgstr "" msgid "Scans the DVD whenever a new disc is loaded" msgstr "" msgid "Hide Advanced Video Options Tab" msgstr "" msgid "" "Use advanced video options at your own risk.\n" "We recommend that you use the controls available\n" "on the Video tab instead." msgstr "" msgid "Delete completed jobs from queue" msgstr "" msgid "" "By default, completed jobs remain in the queue and are marked as complete.\n" "Check this if you want the queue to clean itself up by deleting completed jobs." msgstr "" msgid "Allow Tweaks" msgstr "" msgid "Allow HandBrake For Dummies" msgstr "Tillat HandBrake for Idioter" msgid "Advanced" msgstr "Avansert" msgid "Folder Name:" msgstr "Mappe Navn:" msgid "Description" msgstr "Beskrivelse" msgid "Preset Name:" msgstr "" msgid "Custom Picture Dimensions" msgstr "" msgid "Maximum Width:" msgstr "Maksimum Bredde:" msgid "Enable maximum width limit." msgstr "" msgid "" "This is the maximum width that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source width is greater.\n" "Setting this to 0 means there is no maximum width." msgstr "" msgid "Maximum Height:" msgstr "Maksimum Høyde:" msgid "Enable maximum height limit." msgstr "" msgid "" "This is the maximum height that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source height is greater.\n" "Setting this to 0 means there is no maximum height." msgstr "" msgid "Select preview frames." msgstr "" msgid "" "Encode and play a short sequence of video starting from the current preview " "position." msgstr "" msgid "Duration:" msgstr "" msgid "Set the duration of the live preview in seconds." msgstr "" msgid "Show Crop" msgstr "" msgid "Show Cropped area of the preview" msgstr "" msgid "Fullscreen" msgstr "Fullskjerm" msgid "View Fullscreen Preview" msgstr "" msgid "Title Number:" msgstr "" msgid "Detected DVD devices:" msgstr "" msgid "Setting:" msgstr "" msgid "Import SRT" msgstr "" msgid "Enable settings to import an SRT subtitle file" msgstr "" msgid "Embedded Subtitle List" msgstr "" msgid "Enable settings to select embedded subtitles" msgstr "" msgid "Character Code" msgstr "" msgid "Offset (ms)" msgstr "" msgid "" "Set the language of this subtitle.\n" "This value will be used by players in subtitle menus." msgstr "" msgid "" "Set the character code used by the SRT file you are importing.\n" "\n" "SRTs come in all flavours of character sets.\n" "We translate the character set to UTF-8.\n" "The source's character code is needed in order to perform this translation." msgstr "" msgid "Select the SRT file to import." msgstr "" msgid "Srt File" msgstr "" msgid "Adjust the offset in milliseconds between video and SRT timestamps" msgstr "" msgid "Track" msgstr "" msgid "List of subtitle tracks available from your source." msgstr "" msgid "Forced Subtitles Only" msgstr "" msgid "Burn into video" msgstr "Brenn inn i videoen" msgid "" "Render the subtitle over the video.\n" "The subtitle will be part of the video and can not be disabled." msgstr "" msgid "Set Default Track" msgstr "Sett Standard Spor" msgid "Source Track" msgstr "Kilde Spor" msgid "List of audio tracks available from your source." msgstr "Liste av lyd spor tilgjengelig fra din kilde" msgid "Track Name:" msgstr "Spor Navn:" msgid "" "Set the audio track name.\n" "\n" "Players may use this in the audio selection list." msgstr "" msgid "Mix" msgstr "" msgid "Sample Rate" msgstr "" msgid "" "Dynamic Range Compression: Adjust the dynamic range of the output audio track.\n" "\n" "For source audio that has a wide dynamic range (very loud and very soft sequences),\n" "DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder." msgstr "" msgid "Enable bitrate setting" msgstr "" msgid "Enable quality setting" msgstr "" msgid "" "Quality: For output codec's that support it, adjust the quality of " "the output." msgstr "" msgid "00.0" msgstr "" msgid "" "Audio Gain: Adjust the amplification or attenuation of the output " "audio track." msgstr "" msgid "Skip This Version" msgstr "" msgid "Remind Me Later" msgstr "Minn meg på seinere" msgid "A new version of HandBrake is available!" msgstr " En ny versjon av HandBrake er tilgjengelig!" msgid "HandBrake xxx is now available (you have yyy)." msgstr "HandBrake xxx er nå tilgjengelig (du har yyy)" msgid "Release Notes" msgstr "" msgid "First Track Matching Selected Languages" msgstr "" msgid "All Tracks Matching Selected Languages" msgstr "" msgid "Chapters:" msgstr "" msgid "Seconds:" msgstr "" msgid "Frames:" msgstr "" msgid "Do Nothing" msgstr "" msgid "Show Notification" msgstr "" msgid "Quit Handbrake" msgstr "Avslutt HandBrake" msgid "Put Computer To Sleep" msgstr "" msgid "Shutdown Computer" msgstr "" msgid "Week" msgstr "" msgid "Month" msgstr "" msgid "Year" msgstr "" msgid "Immortal" msgstr "" msgid "Never" msgstr "" msgid "Daily" msgstr "" msgid "Weekly" msgstr "" msgid "Monthly" msgstr "" msgid "Default" msgstr "" msgid "Fast" msgstr "" msgid "Slow" msgstr "" msgid "Slower" msgstr "" msgid "Ultralight" msgstr "" msgid "Light" msgstr "" msgid "Medium" msgstr "" msgid "Strong" msgstr "" msgid "Film" msgstr "" msgid "Grain" msgstr "" msgid "High Motion" msgstr "" msgid "Animation" msgstr "" msgid "Spatial" msgstr "" msgid "Temporal" msgstr "" msgid "Automatic" msgstr "" msgid "Optimal" msgstr "" msgid "Normal" msgstr "" msgid "Simple" msgstr "" msgid "Smart" msgstr "" msgid "Diamond" msgstr "" msgid "Hexagon" msgstr "" msgid "Uneven Multi-Hexagon" msgstr "" msgid "Exhaustive" msgstr "" msgid "Hadamard Exhaustive" msgstr "" msgid "Most" msgstr "" msgid "Some" msgstr "" msgid "All" msgstr "" msgid "Encode only" msgstr "" msgid "Always" msgstr "" msgid "(NTSC Film)" msgstr "" msgid "(PAL Film/Video)" msgstr "" msgid "(NTSC Video)" msgstr "" msgid "%d - %02dh%02dm%02ds - %s" msgstr "" msgid "%d (%05d.MPLS) - %02dh%02dm%02ds" msgstr "" msgid "%d (%05d.MPLS) - Unknown Length" msgstr "" msgid "%d - %02dh%02dm%02ds" msgstr "" msgid "%d - Unknown Length" msgstr "" msgid "No Titles" msgstr "" msgid "No Audio" msgstr "" msgid "Foreign Audio Search" msgstr "" #, c-format msgid "Chapter %2d" msgstr "" #, c-format msgid "N/A" msgstr "" #, c-format msgid "" "Invalid Deinterlace Settings:\n" "\n" "%s\n" msgstr "" #, c-format msgid "" "Invalid Detelecine Settings:\n" "\n" "%s\n" msgstr "" #, c-format msgid "" "Invalid Decomb Settings:\n" "\n" "%s\n" msgstr "" msgid "" "Theora is not supported in the MP4 container.\n" "\n" "You should choose a different video codec or container.\n" "If you continue, FFMPEG will be chosen for you." msgstr "" msgid "Continue" msgstr "" msgid "No title found.\n" msgstr "" msgid "" "Only one subtitle may be burned into the video.\n" "\n" "You should change your subtitle selections.\n" "If you continue, some subtitles will be lost." msgstr "" msgid "" "Srt file does not exist or not a regular file.\n" "\n" "You should choose a valid file.\n" "If you continue, this subtitle will be ignored." msgstr "" msgid "" "The source does not support Pass-Thru.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "%s is not supported in the %s container.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "The source audio does not support %s mixdown.\n" "\n" "You should choose a different mixdown.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "Unable to create %s.\n" "\n" "Internal error. Could not parse UI description.\n" "%s" msgstr "" msgid "Index" msgstr "" msgid "Duration" msgstr "" msgid "Title" msgstr "" msgid "Job Information" msgstr "" msgid "Track Information" msgstr "" msgid "Preset Name" msgstr "" msgid "The device or file to encode" msgstr "" msgid "The preset values to use for encoding" msgstr "" msgid "Spam a lot" msgstr "" msgid "- Transcode media formats" msgstr "" msgid "Globals" msgstr "" msgid "Presets" msgstr "" msgid "Folder" msgstr "" #, c-format msgid "%s path: (%s)" msgstr "" #, c-format msgid "%s indices: len %d" msgstr "" msgid "Type" msgstr "" msgid "Failed to find parent folder when adding child." msgstr "" msgid "Failed to find parent folder while adding child." msgstr "" #, c-format msgid "Can't map language value: (%s)" msgstr "" #, c-format msgid "Can't map value: (%s)" msgstr "" msgid "Subtitles" msgstr "" msgid "" "%s: Folder already exists.\n" "You can not replace it with a preset." msgstr "" msgid "" "%s: Preset already exists.\n" "You can not replace it with a folder." msgstr "" msgid "Import Preset" msgstr "" msgid "All (*)" msgstr "" msgid "Presets (*.plist)" msgstr "" msgid "Export Preset" msgstr "" msgid "" "Confirm deletion of %s:\n" "\n" "%s" msgstr "" msgid "folder" msgstr "" msgid "preset" msgstr "" msgid "No selection??? Perhaps unselected." msgstr "" #, c-format msgid "Gstreamer Error: %s" msgstr "" #, c-format msgid "" "Missing GStreamer plugin\n" "Audio or Video may not play as expected\n" "\n" "%s" msgstr "" msgid "Done" msgstr "" msgid "Windowed" msgstr "" msgid "Seconds" msgstr "" msgid "Frames" msgstr "" #, c-format msgid "" "%s (Title %d, %s %d through %d, 2 Video Passes) -->" " %s" msgstr "" #, c-format msgid "" "%s (Title %d, %s %d through %d) --> %s" msgstr "" #, c-format msgid "Modified Preset Based On: %s\n" msgstr "" #, c-format msgid "Preset: %s\n" msgstr "" #, c-format msgid "Format: %s Container\n" msgstr "" msgid "Container Options:" msgstr "" #, c-format msgid "%sChapter Markers" msgstr "" #, c-format msgid "%siPod 5G Support" msgstr "" #, c-format msgid "%sWeb Optimized" msgstr "" #, c-format msgid "%sLarge File Size (>4GB)" msgstr "" #, c-format msgid "Destination: %s\n" msgstr "" msgid "(Aspect Preserved)" msgstr "" msgid "(Aspect Lost)" msgstr "" msgid "(Anamorphic)" msgstr "" msgid "(Custom Anamorphic)" msgstr "" msgid "Picture: " msgstr "" #, c-format msgid "Source: %d x %d, Output %d x %d %s, Crop %d:%d:%d:%d" msgstr "" #, c-format msgid ", Display %d x %d" msgstr "" msgid "Filters:" msgstr "" #, c-format msgid "%sDetelecine" msgstr "" #, c-format msgid "%sDecomb" msgstr "" #, c-format msgid "%sDeinterlace" msgstr "" #, c-format msgid "%sDenoise Filter %s:" msgstr "" #, c-format msgid "%sDeblock: %d" msgstr "" #, c-format msgid "%sGrayscale" msgstr "" #, c-format msgid "Video: %s" msgstr "" #, c-format msgid ", Framerate: %s %s" msgstr "" #, c-format msgid ", Framerate: Peak %s (may be lower)" msgstr "" #, c-format msgid ", Framerate: %s (constant frame rate)" msgstr "" msgid "Error" msgstr "" msgid "Bitrate:" msgstr "" msgid "Bitrate" msgstr "" msgid "Quality" msgstr "" msgid "Turbo 1st Pass: On\n" msgstr "" #, c-format msgid "Video Options: Preset: %s" msgstr "" msgid " - Tune: " msgstr "" #, c-format msgid " - Profile: %s" msgstr "" #, c-format msgid " - Level: %s" msgstr "" #, c-format msgid "Advanced Options: %s\n" msgstr "" msgid "Audio: " msgstr "" #, c-format msgid "Audio Tracks: %d" msgstr "" #, c-format msgid "Bitrate: %d" msgstr "" #, c-format msgid "%s --> Encoder: %s" msgstr "" #, c-format msgid "%s --> Encoder: %s, Mixdown: %s, SampleRate: %s, %s" msgstr "" msgid "Subtitle: " msgstr "" #, c-format msgid "Subtitle Tracks: %d\n" msgstr "" msgid " (Force)" msgstr "" msgid " (Burn)" msgstr "" msgid " (Default)" msgstr "" #, c-format msgid " %s (%s), %s, Offset (ms) %d%s\n" msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "Another queued job has specified the same destination.\n" "Do you want to overwrite?" msgstr "" msgid "Overwrite" msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "This is not a valid directory." msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "Can not read or write the directory." msgstr "" #, c-format msgid "" "Destination filesystem is almost full: %uM free\n" "\n" "Encode may be incomplete if you proceed.\n" msgstr "" msgid "Proceed" msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "File already exists.\n" "Do you want to overwrite?" msgstr "" msgid "" "Duplicate destination files detected.\n" "Duplicates will not be added to the queue." msgstr "" msgid "No Title" msgstr "" msgid "" "There is another title with the same destination file name.\n" "This title will not be added to the queue unless you change\n" "the output file name.\n" msgstr "" msgid "Stop" msgstr "" msgid "Stop Encoding" msgstr "" msgid "Resume" msgstr "" msgid "Resume Encoding" msgstr "" msgid "S_top Queue" msgstr "" msgid "_Start Queue" msgstr "" msgid "_Resume Queue" msgstr "" msgid "Resume Queue" msgstr "" msgid "" "You are currently encoding. What would you like to do?\n" "\n" msgstr "" #, c-format msgid "" "You have %d unfinished job(s) in a saved queue.\n" "\n" "Would you like to reload them?" msgstr "" msgid "No" msgstr "" msgid "Yes" msgstr "" #, c-format msgid "Usage: %s infile [outfile]\n" msgstr "" #, c-format msgid "Offset: %dms" msgstr "" msgid "Burned Into Video" msgstr "" msgid "Passthrough" msgstr "" msgid "through" msgstr "" msgid "(Forced Subtitles Only)" msgstr "" msgid "(Default)" msgstr "" msgid "Error!" msgstr "" #, c-format msgid "Preferred Language: %s" msgstr "" #, c-format msgid "Add %s subtitle track if default audio is not %s" msgstr "" #, c-format msgid "Type %s" msgstr "" #, c-format msgid "Type %s value %s" msgstr "" #, c-format msgid "Type %s value %d" msgstr "" #, c-format msgid "Type %s value %" msgstr "" #, c-format msgid "Type %s value %f" msgstr "" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"%s\"" msgstr "" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"\"" msgstr "" msgid "Any" msgstr "" msgid "0: SAD, no subpel" msgstr "" msgid "4: SATD, qpel on all" msgstr "" msgid "5: SATD, multi-qpel on all" msgstr "" msgid "6: RD in I/P-frames" msgstr "" msgid "7: RD in all frames" msgstr "" msgid "8: RD refine in I/P-frames" msgstr "" msgid "9: RD refine in all frames" msgstr "" msgid "10: QPRD in all frames" msgstr "" msgid "11: No early terminations in analysis" msgstr "" msgid "Your names" msgstr "" msgid "Your emails" msgstr "" HandBrake-0.10.2/gtk/po/ko.po0000664000175200017520000023166012466165306016234 0ustar handbrakehandbrake# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Changkyoon Kim , 2014 msgid "" msgstr "" "Project-Id-Version: HandBrake-0.10\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-03 13:01+0300\n" "PO-Revision-Date: 2014-12-16 04:57+0000\n" "Last-Translator: Changkyoon Kim \n" "Language-Team: Korean (http://www.transifex.com/projects/p/handbrake/language/ko/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ko\n" "Plural-Forms: nplurals=1; plural=0;\n" msgid "Quality: " msgstr "품질:" #, c-format msgid "Bitrate: %dkbps" msgstr "비트레이트: %dkpbs" #, c-format msgid "Bitrate: %.4gkbps" msgstr "비트레이트: %.4gkbps" msgid "Passthrough" msgstr "패스쓰루" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s\n" "Track Name: %s" msgstr "%s\n게인: %s\nDRC: %s\n트랙명: %s" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s" msgstr "%s\n게인: %s\nDRC: %s" msgid "Add" msgstr "추가" msgid "" "Add an audio encoder.\n" "Each selected source track will be encoded with all selected encoders." msgstr "오디오 인코더 추가하기.\n선택된 각 원본 트랙은 선택된 모든 인코더로 인코딩됩니다." msgid "Set the audio codec to encode this track with." msgstr "이 트랙을 인코딩할 오디오 코덱 설정" msgid "Set the bitrate to encode this track with." msgstr "이 트랙을 인코딩할 비트레이트 설정" msgid "" "Audio Quality:\n" "For encoders that support it, adjust the quality of the output." msgstr "오디오 품질:\n지원되는 인코더라면 출력 품질을 조정합니다." msgid "Set the mixdown of the output audio track." msgstr "오디오 트랙 출력의 믹스다운 설정" msgid "Set the sample rate of the output audio track." msgstr "오디오 트랙 출력의 샘플레이트 설정" msgid "" "Audio Gain:\n" "Adjust the amplification or attenuation of the output audio track." msgstr "오디오 게인:\n출력된 오디오 트랙의 증폭 또는 감쇄를 조정합니다." msgid "0dB" msgstr "0dB" msgid "%ddB" msgstr "%ddB" msgid "%.4gkHz" msgstr "%.4gkHz" msgid "%d - %s (%.4gkHz)" msgstr "%d - %s (%.4gkHz)" msgid "" "Dynamic Range Compression:\n" "Adjust the dynamic range of the output audio track.\n" "For source audio that has a wide dynamic range,\n" "very loud and very soft sequences, DRC allows you\n" "to 'compress' the range by making loud sounds\n" "softer and soft sounds louder.\n" msgstr "다이나믹 레인지 압축:\n오디오 트랙 출력의 다이나믹 레인지를 조정합니다.\n넓은 다이나믹 레인지를 가진 원본 오디오\n(매우 크고 부드러운 시퀀스)에 대해서 다이나믹\n레인지 압축은 큰 소리를 부드럽게 만들고 부드러운\n소리를 크게 만들어서 레인지를 '압축'합니다.\n" msgid "Remove this audio encoder" msgstr "오디오 인코더 제거" msgid "Closing HandBrake will terminate encoding.\n" msgstr "HandBrake를 종료하면 인코딩이 중지됩니다.\n" msgid "No Title Found" msgstr "발견된 타이틀 없음" msgid "none" msgstr "없음" msgid "Not Selected" msgstr "선택되지 않음" msgid "zerolatency x264 tune selected, forcing constant framerate" msgstr "zerolatency x264 tune이 선택되어, 고정 프레임레이트 사용됨" msgid "Scanning ..." msgstr "검색 중 ..." msgid "Stop Scan" msgstr "검색 중지" msgid "On" msgstr "켜기" msgid "Strict" msgstr "엄격하게" msgid "Loose" msgstr "느슨하게" msgid "Custom" msgstr "사용자화" msgid "Unknown" msgstr "알 수 없음" msgid "auto" msgstr "자동" #, c-format msgid "" "%s\n" "\n" "%s in %d seconds ..." msgstr "%s\n\n%s (%d 초) ..." #, c-format msgid "%sYour movie will be lost if you don't continue encoding." msgstr "인코딩을 계속하지 않으면 %s영화가 사라집니다." msgid "Cancel Current and Stop" msgstr "현재 작업 취소 및 전체 중지" msgid "Cancel Current, Start Next" msgstr "현재 작업 취소 및 다음 작업 시작" msgid "Finish Current, then Stop" msgstr "현재 작업 마친 후 전체 중지" msgid "Continue Encoding" msgstr "인코딩 계속하기" msgid "Custom " msgstr "사용자화" msgid "Modified " msgstr "수정됨" #, c-format msgid "Handbrake Version: %s (%d)\n" msgstr "Handbrake 버전: %s (%d)\n" #, c-format msgid "%d encode(s) pending" msgstr "%d 인코딩 대기 중" #, c-format msgid "job %d of %d, " msgstr "작업 %d / %d," #, c-format msgid "pass %d (subtitle scan) of %d, " msgstr "단계 %d / %d (자막 검색)," #, c-format msgid "pass %d of %d, " msgstr "단계 %d / %d," #, c-format msgid "Encoding: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" msgstr "인코딩: %s%s%.2f %% (%.2f fps, 평균 %.2f fps, ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %% (ETA %02dh%02dm%02ds)" msgstr "인코딩: %s%s%.2f %% (ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %%" msgstr "인코딩: %s%s%.2f %%" msgid "Searching for start time, " msgstr "시작 시간 찾는 중," msgid "Scanning..." msgstr "검색 중..." #, c-format msgid "Scanning title %d of %d..." msgstr "타이틀 %d / %d 검색 중..." #, c-format msgid "Scanning title %d of %d preview %d..." msgstr "타이틀 %d / %d 검색 중, 미리보기 %d..." msgid "Source" msgstr "원본" msgid "Choose Video Source" msgstr "비디오 원본 선택" msgid "Paused" msgstr "정지됨" msgid "Encode Done!" msgstr "인코딩 완료!" msgid "Encode Canceled." msgstr "인코딩 취소됨." msgid "Encode Failed." msgstr "인코딩 실패함." msgid "Muxing: This may take a while..." msgstr "먹싱: 시간이 걸립니다..." msgid "Scan this DVD source" msgstr "이 DVD 원본 검색" msgid "AutoScan" msgstr "자동 검색" msgid "Suspend" msgstr "대기" #, c-format msgid "Suspend failed: %s" msgstr "대기 실패: %s" msgid "Suspend failed" msgstr "대기 실패" msgid "Shutdown" msgstr "종료" #, c-format msgid "Shutdown failed: %s" msgstr "종료 실패: %s" msgid "Shutdown failed" msgstr "종료 실패" msgid "Encoding" msgstr "인코딩" #, c-format msgid "press %d %d" msgstr "" #, c-format msgid "" "Invalid Settings:\n" "%s" msgstr "잘못된 설정:\n%s" msgid "Cancel" msgstr "취소" #, c-format msgid "%s: %.4g (Warning: lossless)" msgstr "%s: %.4g (주의: 무손실)" #, c-format msgid "HandBrake %s/%s is now available (you have %s/%d)." msgstr "HandBrake %s/%s 버전이 사용 가능합니다. (현재 버전 %s/%d)" msgid "Encode Complete" msgstr "인코딩 완료됨" msgid "Put down that cocktail, Your HandBrake queue is done!" msgstr "칵테일을 내려놓으세요, HandBrake 큐에 있는 작업이 완료됐습니다!" msgid "Your encode is complete." msgstr "인코딩이 완료됐습니다." msgid "Shutting down the computer" msgstr "컴퓨터 종료 중" msgid "Putting computer to sleep" msgstr "컴퓨터 잠가지 진입 중" msgid "Quiting Handbrake" msgstr "Handbrake 종료 중" msgid "Bottom" msgstr "하단" #, c-format msgid "open failed: %s\n" msgstr "불러오기 실패: %s\n" msgid "No key for dictionary item" msgstr "사전의 항목에 대한 키 없음" msgid "Invalid container type. This shouldn't happen" msgstr "잘못된 컨테이너 유형. 발생하면 안되는 상황입니다" #, c-format msgid "%s:missing a requried attribute" msgstr "%s:필수 속성이 누락됨" #, c-format msgid "" "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" msgstr "사용법: %s [-I ] \n요약:\n자원 목록으로부터 자원 plist 생성\n옵션:\nI - 파일을 검색할 경로\n 입력 자원 파일 이름\n 출력 자원 plist 파일 이름\n" msgid "language" msgstr "언어" msgid "Language" msgstr "언어" msgid "" "The language this text is in, as an ISO code. Pango can use this as a hint " "when rendering the text. If you don't understand this parameter, you " "probably don't need it" msgstr "ISO 코드 형태의 언어. Pango는 텍스트를 렌더링할 때 이 설정을 사용합니다. 이 파라미터를 이해하지 못한다면, 아마 필요 없는 설정일 것입니다." msgid "" "The preferred place to ellipsize the string, if the cell renderer does not " "have enough room to display the entire string" msgstr "화면에 전체 문자열을 표시할 공간이 충분하지 않은 경우, 문자열을 생략할 선호 위치" msgid "" "How to break the string into multiple lines, if the cell renderer does not " "have enough room to display the entire string" msgstr "화면에 전체 문자열을 표시할 공간이 충분하지 않은 경우, 문자열을 여러 줄로 나누는 방법" msgid "" "Render the subtitle over the video.\n" "\n" "The subtitle will be part of the video and can not be disabled." msgstr "비디오에 자막을 입힙니다.\n\n자막은 비디오의 일부가 되고, 비활성화할 수 없습니다." msgid "Burned In" msgstr "영상에 추가" msgid "" "Set the default output subtitle track.\n" "\n" "Most players will automatically display this\n" "subtitle track whenever the video is played.\n" "\n" "This is useful for creating a \"forced\" track\n" "in your output." msgstr "기본 출력 자막 트랙 설정\n\n대부분의 재생기는 비디오를 재생할 때\n이 자막 트랙을 자동적으로 표시합니다.\n\n이 설정은 출력에서 \"강제 표시\" 트랙으로 만들 때\n유용합니다." msgid "Default" msgstr "기본" msgid "" "Use only subtitles that have been flagged\n" "as forced in the source subtitle track\n" "\n" "\"Forced\" subtitles are usually used to show\n" "subtitles during scenes where someone is speaking\n" "a foreign language." msgstr "원본 자막 트랙에서 강제 표시로 지정된\n자막만 사용\n\n보통 \"강제 표시\" 자막은 누군가 외국어로\n얘기하는 장면에서 자막을 보여줄 때\n사용됩니다." msgid "Forced Only" msgstr "강제 표시만" msgid "" "Add (or subtract) an offset (in milliseconds)\n" "to the start of the SRT subtitle track.\n" "\n" "Often, the start of an external SRT file\n" "does not coincide with the start of the video.\n" "This setting allows you to synchronize the files." msgstr "SRT 자막 트랙의 시작 시간에 대한\n오프셋 (밀리초 단위) 추가 (또는 빼기)\n\n종종 외부 SRT 파일의 시작 시간은\n비디오의 시작 시간과 일치하지 않기도 합니다.\n이 설정은 파일의 동기화를 제공합니다." msgid "SRT Offset" msgstr "SRT 오프셋" msgid "Off" msgstr "끄기" msgid "" "The source subtitle track\n" "\n" "You can choose any of the subtitles\n" "recognized in your source file.\n" "\n" "In addition, there is a special track option\n" "\"Foreign Audio Search\". This option will add\n" "an extra pass to the encode that searches for\n" "subtitles that may correspond to a foreign\n" "language scene. This option is best used in\n" "conjunction with the \"Forced\" option." msgstr "원본 자막 트랙\n\n원본 파일에 포함된 어떤 자막이라도\n선택할 수 있습니다.\n\n추가로, \"외국어 오디오 검색\"이라는 특별한\n트랙 옵션이 있습니다. 이 옵션은 외국어가\n사용된 장면에서 표시할 자막을 찾아서\n인코딩하는 단계를 추가합니다. 이 옵션은\n\"강제 표시\" 옵션과 함께 사용할 때 가장\n유용합니다." msgid "Track" msgstr "트랙" msgid "About HandBrake" msgstr "HandBrake 정보" msgid "" "Copyright © 2008 - 2013 John Stebbins\n" "Copyright © 2004 - 2013, HandBrake Devs" msgstr "Copyright © 2008 - 2013 John Stebbins\nCopyright © 2004 - 2013, HandBrake Devs" msgid "" "HandBrake is a GPL-licensed, multiplatform, multithreaded video transcoder." msgstr "HandBrake는 GPL 라이센스를 따르며, 다중플랫폼, 다중쓰레드 기반의 비디오 변환기입니다." msgid "http://handbrake.fr" msgstr "http://handbrake.fr" msgid "" "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n" "\n" "HandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgstr "HandBrake는 자유 소프트웨어입니다; 자유 소프트웨어 재단에서 공표한 GNU 일반 공중 사용 허가서(라이센스 버전 2 또는 그 이후)에 따라 재배포하거나 수정할 수 있습니다.\n\nHandBrake는 유용하길 희망하면서 배포되지만, 어떠한 보증도 하지 않습니다; 특정 목적을 위한 상업성 또는 적합성에 대한 묵시적인 보증도 하지 않습니다. 자세한 사항은 GNU 일반 공중 사용 허가서를 참고하세요.\n\nGlade와 함께 GNU 일반 공중 사용 허가서의 사본이 포함되어 있습니다; 그렇지 않다면, 자유 소프트웨어 재단으로 연락하세요, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgid "_Minimize/Maximize" msgstr "최소화/최대화(_M)" msgid "_Pause Queue" msgstr "큐 정지(_P)" msgid "_Quit" msgstr "종료(_Q)" msgid "_About" msgstr "정보(_A)" msgid "HandBrake" msgstr "HandBrake" msgid "_File" msgstr "파일(_F)" msgid "_Source" msgstr "원본(_S)" msgid "Single _Title" msgstr "단일 타이틀(_T)" msgid "_Destination" msgstr "대상(_D)" msgid "_Preferences" msgstr "설정(_P)" msgid "_Queue" msgstr "큐(_Q)" msgid "_Add" msgstr "추가(_A)" msgid "Add _Multiple" msgstr "다중 추가(_M)" msgid "_Start" msgstr "시작(_S)" msgid "_Pause" msgstr "정지(_P)" msgid "_View" msgstr "보기(_V)" msgid "HandBrake For _Dumbies" msgstr "더미에 대한 HandBrake(_D)" msgid "_Show Presets" msgstr "사전설정 보기(_S)" msgid "_Preview" msgstr "미리보기(_P)" msgid "_Activity Window" msgstr "작업 창(_A)" msgid "Show _Queue" msgstr "큐 보기(_Q)" msgid "_Presets" msgstr "사전설정(_P)" msgid "_Save" msgstr "저장(_S)" msgid "_Delete" msgstr "삭제(_D)" msgid "_Make Default" msgstr "기본으로 지정(_M)" msgid "_New Folder" msgstr "새 폴더(_N)" msgid "_Export" msgstr "내보내기(_E)" msgid "_Import" msgstr "가져오기(_I)" msgid "_Update Built-in Presets" msgstr "내장된 사전설정 갱신(_U)" msgid "_Help" msgstr "도움말(_H)" msgid "_Guide" msgstr "가이드(_G)" msgid "Start Encoding" msgstr "인코딩 시작" msgid "Start" msgstr "시작" msgid "Pause Encoding" msgstr "인코딩 정지" msgid "Pause" msgstr "정지" msgid "Add to Queue" msgstr "큐에 추가하기" msgid "Enqueue" msgstr "큐에 삽입" msgid "Show Queue" msgstr "큐 보기" msgid "Queue" msgstr "큐" msgid "" "Open Picture Settings and Preview window.\n" "Here you can adjust cropping, resolution, aspect ratio, and filters." msgstr "영상 설정 및 미리보기 창 열기.\n잘라내기, 해상도, 종횡비, 필터 등을 조정할 수 있습니다." msgid "Preview" msgstr "미리보기" msgid "Show Activity Window" msgstr "작업 창 보기" msgid "Activity" msgstr "작업" msgid "Source:" msgstr "원본:" msgid "None" msgstr "없음" msgid "Title:" msgstr "타이틀:" msgid "" "Set the title to encode.\n" "By default the longest title is chosen.\n" "This is often the feature title of a DVD." msgstr "인코딩할 타이틀 지정.\n기본적으로 가장 킨 타이틀이 선택됩니다.\n이는 보통 DVD의 주요 타이틀입니다." msgid "Angle:" msgstr "앵글:" msgid "For multi-angle DVD's, select the desired angle to encode." msgstr "멀티 앵글 DVD의 경우, 인코딩할 앵글을 선택합니다." msgid "Reset All Titles" msgstr "모든 타이틀 초기화" msgid "Apply current settings to all titles" msgstr "모든 타이틀에 현재 설정 적용" msgid "Range of title to encode. Can be chapters, seconds, or frames." msgstr "인코딩할 타이틀의 범위. 챕터, 초, 프레임 등" msgid "Set the first chapter to encode." msgstr "인코딩할 첫 챕터 설정" msgid "Set the last chapter to encode." msgstr "인코딩할 마지막 챕터 설정" msgid "Duration:" msgstr "재생시간:" msgid "Destination" msgstr "대상" msgid "File:" msgstr "파일:" msgid "Destination filename for your encode." msgstr "인코딩에 대한 대상 파일 이름" msgid "Destination directory for your encode." msgstr "인코딩에 대한 대상 디렉토리" msgid "Destination Directory" msgstr "대상 디렉토리" msgid "Format:" msgstr "형식:" msgid "Format to mux encoded tracks to." msgstr "인코딩된 트랙에 합쳐질 형식" msgid "iPod 5G Support" msgstr "iPod 5G 지원" msgid "Add iPod Atom needed by some older iPods." msgstr "일부 구형 iPods에 필요한 iPod Atom 추가" msgid "Web optimized" msgstr "웹에 최적화" msgid "" "Optimize the layout of the MP4 file for progressive download.\n" "This allows a player to initiate playback before downloading the entire file." msgstr "증분 다운로드 동안 MP4 파일의 레이아웃 최적화.\n이 설정은 전체 파일을 다운로드하는 동안 재생기가 재생을 시작할 수 있게 합니다." msgid "Large file (>4GB)" msgstr "큰 파일 (>4GB)" msgid "" "Allow 64 bit MP4 file which can be over 4GB.\n" "\n" "Caution: This option may break device compatibility." msgstr "4GB를 넘는 64 비트 MP4 파일 허용.\n\n주의: 이 옵션은 장치 호환성을 저해할 수도 있습니다." msgid "Presets List" msgstr "사전설정 목록" msgid "Source Codec:" msgstr "원본 코덱:" msgid "Dimensions:" msgstr "" msgid "Aspect: " msgstr "비율:" msgid "Frame Rate:" msgstr "프레임레이트:" msgid "Source Picture Parameters" msgstr "원본 영상 파라미터" msgid "Autocrop:" msgstr "자동 잘라내기:" msgid "Crop:" msgstr "잘라내기:" msgid "Crop Dimensions:" msgstr "" msgid "Cropping" msgstr "잘라내기" msgid "Scale Dimensions:" msgstr "" msgid "Optimal for Source:" msgstr "원본에 최적화:" msgid "Anamorphic:" msgstr "아나모픽:" msgid "Scaling" msgstr "크기 조절" msgid "Presentation Dimensions:" msgstr "" msgid "Summary" msgstr "요약" msgid "Left Crop" msgstr "좌측 잘라내기" msgid "Top Crop" msgstr "상단 잘라내기" msgid "Bottom Crop" msgstr "하단 잘라내기" msgid "Right Crop" msgstr "우측 잘라내기" msgid "Auto Crop" msgstr "자동 잘라내기" msgid "Automatically crop black borders around edges of the video." msgstr "비디오 외곽의 검은 배경을 자동으로 잘라냅니다." msgid "Loose Crop" msgstr "느슨하게 잘라내기" msgid "" "When picture settings require that the image\n" "dimensions be rounded to some multiple number\n" "of pixels, this setting will crop a few extra pixels\n" "instead of doing exact cropping and then scaling to\n" "the required multiple." msgstr "영상 설정에서 픽셀 개수의 배수 크기로 근사화된\n영상 크기가 필요할 때, 이 설정은 추가적인\n잘라내기와 요구된 배수로 크기를 조절하는 것\n대신에 일부 추가 픽셀을 잘라냅니다." msgid "width:" msgstr "폭:" msgid "" "This is the width that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "이 값은 비디오가 저장될 폭의 길이입니다.\n픽셀 종횡비가 1:1이 아니라면 실제 화면 크기는 달라질 수 있습니다." msgid "height:" msgstr "높이:" msgid "" "This is the height that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "이 값은 비디오가 저장될 높이의 길이입니다.\n픽셀 종횡비가 1:1이 아니라면 실제 화면 크기는 달라질 수 있습니다." msgid "Optimal for source" msgstr "원본에 최적화" msgid "" "If enabled, select the 'optimal' storage resolution.\n" "This will be the resolution that most closely matches the source resolution after cropping." msgstr "" msgid "" "Anamorphic Modes:\n" "\n" "None - Force pixel aspect ratio to 1:1.\n" "Loose - Align dimensions to chosen 'Alignment' value\n" " and pick pixel aspect ratio that preserves the\n" " original display aspect ratio\n" "Strict - Keep original source dimensions and pixel\n" " aspect ratio" msgstr "아나모픽 모드:\n\n안함 - 픽셀 종횡비를 1:1로 고정합니다.\n느슨하게 - 선택된 \"정렬\" 값으로 크기를\n정렬하고 원본의 화면 종횡비를 보존하기\n위해 픽셀 종횡비를 정합니다\n엄격하게 - 원본의 크기와 픽셀 종횡비를\n유지합니다" msgid "Alignment:" msgstr "정렬:" msgid "" "Align storage dimensions to multiples of this value.\n" "\n" "This setting is only necessary for compatibility with some devices.\n" "You should use 2 unless you experience compatibility issues." msgstr "" msgid "Storage Geometry" msgstr "" msgid "" "This is the display width. It is the result of scaling the storage " "dimensions by the pixel aspect." msgstr "" msgid "Pixel Aspect:" msgstr "픽셀 종횡비:" msgid "" "Pixel aspect defines the shape of the pixels.\n" "\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "픽셀 종횡비는 픽셀의 모양을 결정합니다.\n\n1:1 비율은 정사각 픽셀로 결정됩니다. 다른 값은 사각형 모양을 결정합니다.\n재생기는 지정된 비율에 따라서 이미지의 크기를 조절합니다." msgid ":" msgstr ":" msgid "" "Pixel aspect defines the shape of the pixels.\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "픽셀 종횡비는 픽셀의 모양을 결정합니다.\n1:1 비율은 정사각 픽셀로 결정됩니다. 다른 값은 사각형 모양을 결정합니다.\n재생기는 지정된 비율에 따라서 이미지의 크기를 조절합니다." msgid "Keep Aspect" msgstr "비율 유지" msgid "" "If enabled, the original display aspect of the source will be maintained." msgstr "활성화하면, 원본의 화면 종횡비가 유지됩니다." msgid "Display Aspect:" msgstr "화면 비율:" msgid "Display Geometry" msgstr "" msgid "Grayscale" msgstr "회색조" msgid "If enabled, filter colour components out of video." msgstr "" msgid "Deblock:" msgstr "Deblock:" msgid "" "The deblocking filter removes a common type of compression artifact.\n" "If your source exhibits 'blockiness', this filter may help clean it up." msgstr "Deblock 필터는 압축 과정에서 생긴 영향을 제거합니다.\n원본 영상에 블럭이 많다면, 이 필터가 도움이 됩니다." msgid "Denoise Filter:" msgstr "노이즈 제거 필터:" msgid "" "Denoise filtering reduces or removes the appearance of noise and grain.\n" "Film grain and other types of high frequency noise are difficult to compress.\n" "Using this filter on such sources can result in smaller file sizes." msgstr "노이즈 제거 필터는 노이즈와 그레인을 줄이거나 제거합니다.\n필름 그레인과 다른 종류의 고주파 노이즈는 압축을 어렵게 만듭니다.\n그런 원본에 이 필터를 사용하면 파일을 더 작게 만들 수 있습니다." msgid "Denoise Preset:" msgstr "노이즈 제거 사전설정:" msgid "Denoise Tune:" msgstr "노이즈 제거 조정:" msgid "" "Custom denoise filter string format\n" "\n" "SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgstr "사용자 노이즈 제거 필터 문자열 형식\n\nSpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgid "Detelecine:" msgstr "Detelecine:" msgid "" "This filter removes 'combing' artifacts that are the result of telecining.\n" "\n" "Telecining is a process that adjusts film framerates that are 24fps to NTSC video frame rates which are 30fps." msgstr "" msgid "" "Custom detelecine filter string format\n" "\n" "JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgstr "사용자 detelecine 필터 문자열 형식\n\nJunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgid "Decomb" msgstr "Decomb" msgid "" "Choose decomb or deinterlace filter options.\n" "\n" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced.\n" "\n" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "Decomb 또는 디인터레이스 필터 옵션 선택.\n\nDecomb 필터는 인터레이스로 나타나는 프레임을 선택적으로 디인터레이스합니다.\n이 설정은 인터레이스되지 않는 프레임의 품질을 보존합니다.\n\n전통적인 디인터레이스 필터는 모든 프레임에 적용됩니다.\n인터레이스되지 않는 프레임은 일부 품질 저하가 나타날 수 있습니다." msgid "Deinterlace" msgstr "디인터레이스" msgid "Decomb:" msgstr "Decomb:" msgid "" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced." msgstr "Decomb 필터는 인터레이스로 나타나는 프레임을 선택적으로 디인터레이스합니다.\n이 설정은 인터레이스되지 않는 프레임의 품질을 보존합니다." msgid "" "Custom decomb filter string format\n" "\n" "Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\n" "BlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\n" "ErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgstr "사용자 decomb 필터 문자열 형식\n\nMode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\nBlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\nErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgid "Deinterlace:" msgstr "디인터레이스:" msgid "" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "전통적인 디인터레이스 필터는 모든 프레임에 적용됩니다.\n인터레이스되지 않는 프레임은 일부 품질 저하가 나타날 수 있습니다." msgid "" "Custom deinterlace filter string format\n" "\n" "YadifMode:YadifParity:McdintMode:McdeintQp" msgstr "사용자 디인터레이스 필터 문자열 형식\n\nYadifMode:YadifParity:McdintMode:McdeintQp" msgid "Filters" msgstr "필터" msgid "Picture" msgstr "영상" msgid "Video Encoder:" msgstr "비디오 인코더:" msgid "Available video encoders." msgstr "사용 가능한 비디오 인코더." msgid "Framerate:" msgstr "프레임레이트:" msgid "" "Output framerate.\n" "\n" "'Same as source' is recommended. If your source video has\n" "a variable framerate, 'Same as source' will preserve it." msgstr "출력 프레임레이트.\n\n'원본과 동일'이 권장됩니다. 원본 비디오가 가변\n프레임레이트라면, '원본과 동일'은 그 설정을 보존합니다." msgid "Constant Framerate" msgstr "고정 프레임레이트" msgid "Same as source" msgstr "원본과 동일하게" msgid "kbps" msgstr "kbps" msgid "(variable)" msgstr "(가변)" msgid "(constant)" msgstr "(고정)" msgid "Enables constant framerate output." msgstr "고정 프레임레이트 출력 활성화" msgid "Peak Framerate (VFR)" msgstr "피크 프레임레이트 (VFR)" msgid "" "Enables variable framerate output with a peak\n" "rate determined by the framerate setting.\n" "\n" "VFR is not compatible with some players." msgstr "프레임레이트 설정에 의해 결정된 최고 레이트를\n가진 가변 프레임레이트 출력 활성화.\n\n가변 프레임레이트는 일부 재생기와 호환되지 않을 수 있습니다." msgid "Variable Framerate" msgstr "가변 프레임레이트" msgid "" "Enables variable framerate output.\n" "\n" "VFR is not compatible with some players." msgstr "가변 프레임레이트 출력 활성화.\n\nVFR은 일부 재생기와 호환되지 않습니다." msgid "" "Set the desired quality factor.\n" "The encoder targets a certain quality.\n" "The scale used by each video encoder is different.\n" "\n" "x264's scale is logarithmic and lower values correspond to higher quality.\n" "So small decreases in value will result in progressively larger increases\n" "in the resulting file size. A value of 0 means lossless and will result\n" "in a file size that is larger than the original source, unless the source\n" "was also lossless.\n" "\n" "FFMpeg's and Theora's scale is more linear.\n" "These encoders do not have a lossless mode." msgstr "원하는 품질 지정.\n인코더는 지정된 품질을 목표로 동작합니다.\n각 비디오 인코더에 사용되는 스케일은 다릅니다.\n\nx264의 스케일은 로그형태로 동작하며 작은 값일수록 높은 품질을\n의미합니다. 작은 값은 점점 더 큰 파일을 만들어 냅니다. 0 값은\n무손실을 의미하고, 원본 역시 무손실이라고해도 원본보다 더 큰\n파일이 만들어질 수도 있습니다.\n\nFFMpeg과 Theora의 스케일은 좀더 선형적입니다.\n이런 인코더들은 무손실 모드를 지원하지 않습니다." msgid "Constant Quality:" msgstr "고정 품질:" msgid "Bitrate (kbps): " msgstr "비트레이트 (kbps):" msgid "" "Set the average bitrate.\n" "\n" "The instantaneous bitrate can be much higher or lower at any point in time.\n" "But the average over a long duration will be the value set here. If you need\n" "to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings." msgstr "평균 비트레이트 설정.\n\n특정 시간에서 순간 비트레이트는 높거나 낮을 수도 있습니다.\n그러나 긴 시간 동안의 평균치는 이 값에 수렴합니다. 순간 비트레이트를\n제한하려면 x264의 vbv-bufsize와 vbv-maxrate 설정을 살펴보세요." msgid "2-Pass Encoding" msgstr "2단계 인코딩" msgid "" "Perform 2 Pass Encoding.\n" "\n" "The 'Bitrate' option is prerequisite. During the 1st pass, statistics about\n" "the video are collected. Then in the second pass, those statistics are used\n" "to make bitrate allocation decisions." msgstr "2단계 인코딩 수행.\n\n'비트레이트' 옵션은 필요조건입니다. 1단계 동안, 비디오에 대한 통계치가\n수집됩니다. 두번째 단계에서, 그 통계치는 비트레이트 할당을 결정하는데\n사용됩니다." msgid "Turbo First Pass" msgstr "1단계 가속" msgid "" "During the 1st pass of a 2 pass encode, use settings that speed things " "along." msgstr "2단계 인코딩 중 1단계에서 속도를 높이려면 설정을 사용하세요." msgid "Use Advanced Options" msgstr "고급 옵션 사용" msgid "" "Use advanced options Tab for x264 settings.\n" "\n" "Use at your own risk!" msgstr "x264 설정에 고급 옵션 탭 사용.\n\n위험 부담을 갖고 사용하세요!" msgid "Preset:" msgstr "사전설정:" msgid "" "Adjusts encoder settings to trade off compression efficiency against encoding speed.\n" "\n" "This establishes your default encoder settings.\n" "Tunes, profiles, levels and advanced option string will be applied to this.\n" "You should generally set this option to the slowest you can bear since slower\n" "settings will result in better quality or smaller files." msgstr "압축 효율과 인코딩 속도 사이의 인코더 설정 사용.\n\n이 설정은 기본 인코더 설정을 만듭니다.\n조정, 프로필, 수준, 고급 옵션 문자열이 이 설정에 적용됩니다.\n낮은 속도의 설정은 더 좋은 품질 또는 더 작은 파일을 만들기 때문에\n참을 수 있는 정도의 속도를 지정하는데 이 옵션을 사용합니다." msgid "Tune:" msgstr "조정:" msgid "" "Tune settings to optimize for common scenarios.\n" "\n" "This can improve effeciency for particular source characteristics or set\n" "characteristics of the output file. Changes will be applied after the\n" "preset but before all other parameters." msgstr "일반적인 상황에 대한 최적화 조정 설정.\n\n이 설정은 세세한 원본 특성에 대한 효율을 증가시키거나 출력 파일의\n특성을 지정할 수 있습니다. 사전 설정 적용 후 다른 모든 파라미터\n적용 직전에 변경 사항이 적용됩니다." msgid "Fast Decode" msgstr "고속 디코딩" msgid "" "Reduce decoder CPU usage.\n" "\n" "Set this if your device is struggling to play the output (dropped frames)." msgstr "디코더의 CPU 사용율 감소.\n\n(끊김 등으로) 출력물 재생이 어려우면 이 설정을 사용하세요." msgid "Zero Latency" msgstr "" msgid "" "Minimize latency between input to encoder and output of decoder.\n" "\n" "This is useful for broadcast of live streams.\n" "\n" "Since HandBrake is not suitable for live stream broadcast purposes,\n" "this setting is of little value here." msgstr "" msgid "Profile:" msgstr "프로필:" msgid "" "Sets and ensures compliance with the specified profile.\n" "\n" "Overrides all other settings." msgstr "지정된 프로필과의 호환성 지정 및 보장.\n\n다른 모든 설정을 덮어씁니다." msgid "Level:" msgstr "수준:" msgid "" "Sets and ensures compliance with the specified level.\n" "\n" "Overrides all other settings." msgstr "지정된 수준과의 호환성 지정 및 보장.\n\n다른 모든 설정을 덮어씁니다." msgid "More Settings:" msgstr "상세 설정:" msgid "" "Additional encoder settings.\n" "\n" "Colon separated list of encoder options." msgstr "추가적인 인코더 설정.\n\n콜론으로 분리된 인코더 옵션 목록." msgid "Video" msgstr "비디오" msgid "Selection Behavior:" msgstr "선택 동작:" msgid "Remove" msgstr "제거" msgid "Available Languages" msgstr "사용 가능한 언어" msgid "Selected Languages" msgstr "선택된 언어" msgid "Use only first encoder for secondary audio" msgstr "부 오디오에 대해서 첫 인코더만 사용" msgid "" "Only the primary audio track will be encoded with the full encoder list.\n" "All other secondary audio output tracks will be encoded with first encoder only." msgstr "주 오디오 트랙만 목록의 모든 인코더로 인코딩됩니다.\n다른 모든 부 오디오 출력 트랙은 첫 인코더로만 인코딩됩니다." msgid "Auto Passthru:" msgstr "자동 패스쓰루:" msgid "MP3" msgstr "MP3" msgid "" "Enable this if your playback device supports MP3.\n" "This permits MP3 passthru to be selected when automatic passthru selection is enabled." msgstr "재생 장치가 MP3를 지원하면 활성화하세요.\n이 옵션은 자동 패스쓰루 선택이 활성화될 때, MP3 패스쓰루 선택을 허용합니다." msgid "" "Enable this if your playback device supports AAC.\n" "This permits AAC passthru to be selected when automatic passthru selection is enabled." msgstr "재생 장치가 AAC를 지원하면 활성화하세요.\n이 옵션은 자동 패스쓰루 선택이 활성화될 때, AAC 패스쓰루 선택을 허용합니다." msgid "" "Enable this if your playback device supports AC-3.\n" "This permits AC-3 passthru to be selected when automatic passthru selection is enabled." msgstr "재생 장치가 AC-3를 지원하면 활성화하세요.\n이 옵션은 자동 패스쓰루 선택이 활성화될 때, AC-3 패스쓰루 선택을 허용합니다." msgid "" "Enable this if your playback device supports DTS.\n" "This permits DTS passthru to be selected when automatic passthru selection is enabled." msgstr "재생 장치가 DTS를 지원하면 활성화하세요.\n이 옵션은 자동 패스쓰루 선택이 활성화될 때, DTS 패스쓰루 선택을 허용합니다." msgid "" "Enable this if your playback device supports DTS-HD.\n" "This permits DTS-HD passthru to be selected when automatic passthru selection is enabled." msgstr "재생 장치가 DTS-HD를 지원하면 활성화하세요.\n이 옵션은 자동 패스쓰루 선택이 활성화될 때, DTS-HD 패스쓰루 선택을 허용합니다." msgid "Passthru Fallback:" msgstr "패스쓰루 대체:" msgid "" "Set the audio codec to encode with when a suitable track can not be found " "for audio passthru." msgstr "오디오 패스쓰루에 적합한 트랙이 없는 경우 인코딩할 오디오 코덱을 설정합니다." msgid "Audio Encoder Settings:" msgstr "오디오 인코딩 설정:" msgid "Each selected source track will be encoded with all selected encoders" msgstr "선택된 각각의 원본 트랙은 선택된 모든 인코더로 인코딩됩니다." msgid "Encoder" msgstr "인코더" msgid "Bitrate/Quality" msgstr "비프레이트/품질" msgid "Mixdown" msgstr "믹스다운" msgid "Samplerate" msgstr "샘플레이트" msgid "Gain" msgstr "게인" msgid "Audio Defaults" msgstr "오디오 기본 설정" msgid "Add new audio settings to the list" msgstr "새로운 오디오 설정을 목록에 추가" msgid "Add All" msgstr "모두 추가" msgid "Add all audio tracks to the list" msgstr "모든 오디오 트랙을 목록에 추가" msgid "Reload Defaults" msgstr "기본 설정 다시 불러오기" msgid "Reload all audio settings from defaults" msgstr "기본 설정에서 모든 오디오 설정 다시 불러오기" msgid "Audio List" msgstr "오디오 목록" msgid "Preferred Language: None" msgstr "선호하는 언어: 없음" msgid "Add Foreign Audio Search Pass" msgstr "외국어 오디오 검색 단계 추가" msgid "" "Add \"Foreign Audio Search\" when the default audio track is your preferred language.\n" "This search pass finds short sequences of foreign audio and provides subtitles for them." msgstr "기본 오디오 트랙이 선호하는 언어라면 \"외국어 오디오 검색\"를 추가하세요.\n이 검색 단계는 외국어 오디오의 일부를 찾고 그 자막을 제공합니다." msgid "Add subtitle track if default audio is foreign" msgstr "기본 오디오가 외국어인 경우 자막 트랙 추가" msgid "" "When the default audio track is not your preferred language, add a subtitle " "track." msgstr "기본 오디오 트랙이 선호하는 언어가 아니라면, 자막 트랙을 추가하세요." msgid "Add Closed Captions when available" msgstr "표시 여부 선택이 가능한 캡션이 존재하는 경우 추가" msgid "" "Closed captions are text subtitles that can be added to any container as a " "soft subtitle track (not burned)" msgstr "표시 여부 선택이 가능한 캡션은 어떤 컨테이너에도 자막 트랙으로 추가될 수 있는 텍스트 자막입니다. (영상에 추가되지는 않음)" msgid "Subtitle Defaults" msgstr "자막 기본 설정" msgid "Add new subtitle settings to the list" msgstr "새로운 자막 설정을 목록에 추가" msgid "Add all subtitle tracks to the list" msgstr "모든 자막 트랙을 목록에 추가" msgid "Reload all subtitle settings from defaults" msgstr "기본 설정에서 모든 자막 설정 다시 불러오기" msgid "Subtitle List" msgstr "자막 목록" msgid "Reference Frames:" msgstr "참조 프레임:" msgid "" "Sane values are ~1-6. The more you add, the better the compression, but the slower the encode.\n" "Cel animation tends to benefit from more reference frames a lot more than film content.\n" "\n" "Note that many hardware devices have limitations on the number of supported reference\n" "frames, so if you're encoding for a handheld or standalone player, don't touch this unless\n" "you're absolutely sure you know what you're doing!" msgstr "" msgid "Maximum B-Frames:" msgstr "" msgid "" "Sane values are ~2-5. This specifies the maximum number of sequential B-frames that the encoder can use.\n" "\n" "Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.\n" "Cel-animated source material and B-pyramid also significantly increase the usefulness of larger\n" "values.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off)." msgstr "" msgid "Pyramidal B-Frames:" msgstr "" msgid "" "B-pyramid improves compression by creating a pyramidal structure (hence the name)\n" "of B-frames, allowing B-frames to reference each other to improve compression.\n" "\n" "Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit." msgstr "" msgid "Weighted P-Frames:" msgstr "" msgid "" "Performs extra analysis to decide upon weighting parameters for each frame.\n" "\n" "This improves overall compression slightly and improves the quality of fades greatly.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires weighted P-frame\n" "prediction to be disabled. Note that some devices and players, even those that support\n" "Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is\n" "completely incompatible with it, for example." msgstr "" msgid "8x8 Transform" msgstr "8x8 변환" msgid "" "The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.\n" "\n" "It improves compression by at least 5% at a very small speed cost and may\n" "provide an unusually high visual quality benefit compared to its compression\n" "gain. However, it requires High Profile, which many devices may not support." msgstr "" msgid "CABAC Entropy Encoding" msgstr "CABAC 엔트로피 인코딩" msgid "" "After the encoder has done its work, it has a bunch of data that\n" "needs to be compressed losslessly, similar to ZIP or RAR. H.264 provides\n" "two options for this: CAVLC and CABAC. CABAC decodes a lot slower but\n" "compresses significantly better (10-30%), especially at lower bitrates.\n" "\n" "If you're looking to minimize CPU requirements for video playback, disable this option.\n" "Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled." msgstr "" msgid "Encoding Features" msgstr "인코딩 항목" msgid "Motion Est. Method:" msgstr "움직임 추정 방법:" msgid "" "Controls the motion estimation method.\n" "\n" "Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.\n" "A better motion search method improves compression at the cost of speed.\n" "\n" "Diamond: performs an extremely fast and simple search using a diamond pattern.\n" "Hexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n" "Uneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n" "Exhaustive: performs a \"dumb\" search of every pixel in a wide area. Significantly slower for only a small compression gain.\n" "Transformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement." msgstr "움직임 추정 방법 제어.\n\n움직임 추정은 프레임에서 각 픽셀 블럭이 어떻게 움직일지를 인코더가 추정하는 것입니다.\n더 좋은 움직임 검색 방법은 속도를 희생해서 압축율을 향상시킵니다.\n\nDiamond: 다이아몬드 패턴을 사용해 매우 빠르고 간단한 검색 수행.\nHexagon: 육각 패턴을 이용해 다소 느리지만 효율적인 검색 수행.\nUneven Multi-Hex: 복잡한 움직임을 좀 더 정확하게 포착하기 위해, 다양한 패턴을 이용해 광범위한 검색 수행.\nExhaustive: 넓은 영역에서 모든 픽셀에 대해 \"무식한\" 검색 수행. 압축률도 낮고 매우 느림.\nTransformed Exhaustive: Exhaustive와 유사하지만 더 정확함. 압축률은 낮고 다소 느림." msgid "Subpel ME & Mode:" msgstr "" msgid "" "This setting controls both subpixel-precision motion estimation and mode decision methods.\n" "\n" "Subpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n" "Mode decision is the method used to choose how to encode each block of the frame: a very important decision.\n" "SAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n" "6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n" "11 disables all early terminations in analysis.\n" "10 and 11, the most powerful and slowest options, require adaptive quantization (aq-mode > 0) and trellis 2 (always)." msgstr "" msgid "Motion Est. Range:" msgstr "움직임 추정 범위:" msgid "" "This is the distance x264 searches from its initial guess at the\n" "motion of a block in order to try to find its actual motion.\n" "\n" "The default is fine for most content, but extremely high motion video,\n" "especially at HD resolutions, may benefit from higher ranges, albeit at\n" "a high speed cost." msgstr "" msgid "Adaptive Direct Mode:" msgstr "" msgid "" "H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n" "\n" "Spatial, the default, is almost always better, but temporal is sometimes useful too.\n" "x264 can, at the cost of a small amount of speed (and accordingly for a small compression gain),\n" "adaptively select which is better for each particular frame." msgstr "" msgid "Adaptive B-Frames:" msgstr "" msgid "" "x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n" "\n" "Fast mode takes roughly the same amount of time no matter how many B-frames you specify.\n" "However, while fast, its decisions are often suboptimal.\n" "\n" "Optimal mode gets slower as the maximum number of B-Frames increases,\n" "but makes much more accurate decisions, especially when used with B-pyramid." msgstr "" msgid "Partitions:" msgstr "" msgid "" "Mode decision picks from a variety of options to make its decision:\n" "this option chooses what options those are.\n" "\n" "Fewer partitions to check means faster encoding, at the cost of worse\n" "decisions, since the best option might have been one that was turned off." msgstr "" msgid "Trellis:" msgstr "" msgid "" "Trellis fine-tunes the rounding of transform coefficients to\n" "squeeze out 3-5% more compression at the cost of some speed.\n" "\n" "\"Always\" uses trellis not only during the main encoding process, but also\n" "during analysis, which improves compression even more, albeit at great speed cost.\n" "\n" "Trellis costs more speed at higher bitrates and requires CABAC." msgstr "" msgid "Analysis" msgstr "분석" msgid "Adaptive Quantization Strength:" msgstr "" msgid "" "Adaptive quantization controls how the encoder distributes bits across the frame.\n" "\n" "Higher values take more bits away from edges and complex areas to improve areas with finer detail." msgstr "" msgid "Psychovisual Rate Distortion:" msgstr "" msgid "" "Psychovisual rate-distortion optimization takes advantage of the characteristics of human\n" "vision to dramatically improve apparent detail and sharpness.\n" "The effect can be made weaker or stronger by adjusting the strength.\n" "Being an RD algorithm, it requires mode decision to be at least \"6\"." msgstr "" msgid "Psychovisual Trellis:" msgstr "Psychovisual 격자:" msgid "" "Psychovisual trellis is an experimental algorithm to further\n" "improve sharpness and detail retention beyond what Psychovisual RD does.\n" "\n" "Recommended values are around 0.2, though higher values may help for very\n" "grainy video or lower bitrate encodes. Not recommended for cel animation\n" "and other sharp-edged graphics." msgstr "" msgid "Deblocking: " msgstr "Deblocking:" msgid "" "H.264 deblocking filter.\n" "\n" "h.264 has a built-in deblocking filter that smooths out blocking artifacts\n" "after decoding each frame. This not only improves visual quality, but also\n" "helps compression significantly. The deblocking filter takes a lot of CPU power,\n" "so if you're looking to minimize CPU requirements for video playback, disable it.\n" "\n" "The deblocking filter has two adjustable parameters, \"strength\" (Alpha) and \"threshold\" (Beta).\n" "The former controls how strong (or weak) the deblocker is, while the latter controls how many\n" "(or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking.\n" "The default is 0 (normal strength) for both parameters." msgstr "" msgid "No DCT Decimate" msgstr "" msgid "" "x264 normally zeroes out nearly-empty data blocks to save bits to\n" "be better used for some other purpose in the video. However, this can\n" "sometimes have slight negative effects on retention of subtle grain and\n" "dither.\n" "\n" "Don't touch this unless you're having banding issues or other such cases\n" "where you are having trouble keeping fine noise." msgstr "" msgid "Psychovisual" msgstr "Psychovisual" msgid "" "Your selected options will appear here.\n" "You can edit these and add additional options.\n" "\n" "Default values will not be shown. The defaults are:\n" "ref=3:bframes=3:b-adapt=fast:direct=spatial:\n" "b-pyramid=normal:weightp=2:me=hex:merange=16:\n" "subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\n" "deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\n" "no-fast-pskip=0:no-dct-decimate=0:cabac=1" msgstr "선택된 옵션이 여기에 표시됩니다.\n이 옵션들을 편집하거나 다른 옵션을 추가할 수 있습니다.\n\n기본 값은 나타나지 않습니다. 기본 값은 다음과 같습니다:\nref=3:bframes=3:b-adapt=fast:direct=spatial:\nb-pyramid=normal:weightp=2:me=hex:merange=16:\nsubme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\ndeblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\nno-fast-pskip=0:no-dct-decimate=0:cabac=1" msgid "Current x264 Advanced Option String" msgstr "현재의 x264 고급 옵션 문자열" msgid "Advanced Video" msgstr "고급 비디오" msgid "Chapter Markers" msgstr "챕터 마커" msgid "Add chapter markers to output file." msgstr "출력 파일에 챕터 마커 추가" msgid "Chapters" msgstr "챕터" msgid "Actors:" msgstr "배우:" msgid "Director:" msgstr "감독:" msgid "Release Date:" msgstr "배포일:" msgid "Comment:" msgstr "주석:" msgid "Genre:" msgstr "장르:" msgid "Description:" msgstr "설명:" msgid "Plot:" msgstr "플롯:" msgid "Tags" msgstr "태그" msgid "Settings" msgstr "설정" msgid "Edit" msgstr "편집" msgid "Reload" msgstr "다시 불러오기" msgid "" "Mark selected queue entry as pending.\n" "Resets the queue job to pending and ready to run again." msgstr "선택된 큐의 작업을 보류로 표시.\n큐 작업을 보류로 초기화하고 재 시작을 대기." msgid "Reload All" msgstr "모두 다시 불러오기" msgid "" "Mark all queue entries as pending.\n" "Resets all queue jobs to pending and ready to run again." msgstr "모든 큐의 작업을 보류로 표시.\n모든 큐 작업을 보류로 초기화하고 재 시작을 대기." msgid "OK" msgstr "승인" msgid "Select All" msgstr "전체 선택" msgid "Mark all titles for adding to the queue" msgstr "모든 타이틀을 추에 추가하기로 표시" msgid "Clear All" msgstr "모두 지우기" msgid "Unmark all titles" msgstr "모든 타이틀에서 마커 제거" msgid "Destination files OK. No duplicates detected." msgstr "대상 파일 승인. 중복 발견되지 않음." msgid "Select this title for adding to the queue.\n" msgstr "이 타이틀을 큐에 추가하기 위해 선택.\n" msgid "Preferences" msgstr "설정" msgid "Automatically check for updates" msgstr "자동으로 업데이트 확인" msgid "When all encodes are complete" msgstr "모든 인코딩이 완료됐을 때" msgid "Use automatic naming (uses modified source name)" msgstr "자동 이름 (수정된 원본 이름) 사용" msgid "Auto-Name Template" msgstr "자동 이름 부여 템플릿" msgid "" "Available Options: {source} {title} {chapters} {date} {time} {quality} " "{bitrate}" msgstr "가능한 옵션: {source} {title} {chapters} {date} {time} {quality} {bitrate}" msgid "Use iPod/iTunes friendly (.m4v) file extension for MP4" msgstr "MP4에 대해 iPod/iTunes 친화적인 확장자 (.m4v) 사용" msgid "Number of previews" msgstr "미리보기 수" msgid "Filter short titles (seconds)" msgstr "짧은 타이틀 필터링 (초)" msgid "Show system tray icon" msgstr "시스템 트레이 아이콘 보기" msgid "General" msgstr "일반" msgid "Constant Quality fractional granularity" msgstr "분수 형태의 고정 품질 단위" msgid "Use dvdnav (instead of libdvdread)" msgstr "(libdvdread 대신) dvdnav 사용" msgid "Put individual encode logs in same location as movie" msgstr "개별 인코딩 로그를 영화와 동일한 위치에 놓기" msgid "Activity Log Verbosity Level" msgstr "작업 로그 상세 수준" msgid "Activity Log Longevity" msgstr "작업 로그 길이" msgid "Scale down High Definition previews" msgstr "HD 미리보기 축소" msgid "Automatically Scan DVD when loaded" msgstr "DVD가 삽입되면 자동으로 검색" msgid "Scans the DVD whenever a new disc is loaded" msgstr "새로운 디스크가 삽입되면 DVD 검색" msgid "Hide Advanced Video Options Tab" msgstr "고급 비디오 출력 탭 숨기기" msgid "" "Use advanced video options at your own risk.\n" "We recommend that you use the controls available\n" "on the Video tab instead." msgstr "위험 부다을 갖고 고급 비디오 옵션 사용.\n비디오 탭에 위치한 제어 명령을 사용하시는 편이\n더 좋습니다." msgid "Delete completed jobs from queue" msgstr "큐에서 완료된 작업 삭제" msgid "" "By default, completed jobs remain in the queue and are marked as complete.\n" "Check this if you want the queue to clean itself up by deleting completed jobs." msgstr "기본적으로 완료된 작업은 큐에 남아있고 완료된 것으로 표시됩니다.\n완료된 작업을 큐에서 지우려면 이 것을 선택하세요." msgid "Allow Tweaks" msgstr "설정 변경 허용" msgid "Allow HandBrake For Dummies" msgstr "" msgid "Advanced" msgstr "고급" msgid "Folder Name:" msgstr "폴더 이름:" msgid "Description" msgstr "설명" msgid "Preset Name:" msgstr "사전설정 이름:" msgid "Custom Picture Dimensions" msgstr "" msgid "Maximum Width:" msgstr "최대 폭:" msgid "Enable maximum width limit." msgstr "최대 폭 제한을 활성화합니다." msgid "" "This is the maximum width that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source width is greater.\n" "Setting this to 0 means there is no maximum width." msgstr "이 값은 저장될 비디오의 최대 폭입니다.\n\n새로운 원본이 적재됐을 때, 원본의 폭이 이 값보다 크면 이 값이 적용됩니다.\n0은 최대 폭을 지정하지 않는다는 의미입니다." msgid "Maximum Height:" msgstr "최대 높이:" msgid "Enable maximum height limit." msgstr "최대 높이 제한 활성화." msgid "" "This is the maximum height that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source height is greater.\n" "Setting this to 0 means there is no maximum height." msgstr "이 값은 저장될 비디오의 최대 높이입니다.\n\n새로운 원본이 적재됐을 때, 원본의 높이가 이 값보다 크면 이 값이 적용됩니다.\n0은 최대 높이를 지정하지 않는다는 의미입니다." msgid "Select preview frames." msgstr "미리보기 프레임 선택" msgid "" "Encode and play a short sequence of video starting from the current preview " "position." msgstr "현재 미리보기 위치부터 시작하는 짧은 비디오를 인코딩하고 재생" msgid "Duration:" msgstr "재생시간:" msgid "Set the duration of the live preview in seconds." msgstr "실시간 미리보기의 재생시간을 초단위로 설정." msgid "Show Crop" msgstr "잘라내기 보기" msgid "Show Cropped area of the preview" msgstr "미리보기에서 잘라낸 영역 보기" msgid "Fullscreen" msgstr "전체화면" msgid "View Fullscreen Preview" msgstr "전체 화면 미리보기" msgid "Title Number:" msgstr "타이틀 번호:" msgid "Detected DVD devices:" msgstr "발견된 DVD 장치:" msgid "Setting:" msgstr "설정:" msgid "Import SRT" msgstr "SRT 가져오기" msgid "Enable settings to import an SRT subtitle file" msgstr "SRT 자막 파일 가져오기 활성화" msgid "Embedded Subtitle List" msgstr "내장된 자막 목록" msgid "Enable settings to select embedded subtitles" msgstr "내장 자막 선택 설정 활성화" msgid "Character Code" msgstr "문자 인코딩" msgid "Offset (ms)" msgstr "오프셋 (ms)" msgid "" "Set the language of this subtitle.\n" "This value will be used by players in subtitle menus." msgstr "이 자막의 언어 설정.\n이 값은 자막 메뉴에서 재생기가 사용합니다." msgid "" "Set the character code used by the SRT file you are importing.\n" "\n" "SRTs come in all flavours of character sets.\n" "We translate the character set to UTF-8.\n" "The source's character code is needed in order to perform this translation." msgstr "가져올 SRT 파일의 문자 인코딩 설정.\n\nSRT는 문자들의 집합 형태로 들어옵니다.\n이 문자 집합을 UTF-8로 변환합니다.\n이 변환을 수행하기 위해서 원본 문자 인코딩 방식이 필요합니다." msgid "Select the SRT file to import." msgstr "가져올 SRT 파일 선택" msgid "Srt File" msgstr "Srt 파일" msgid "Adjust the offset in milliseconds between video and SRT timestamps" msgstr "비디오와 SRT 타임스탬프 사이에 밀리초 단위로 오프셋을 조정합니다" msgid "Track" msgstr "트랙" msgid "List of subtitle tracks available from your source." msgstr "원본에서 사용 가능한 자막 목록" msgid "Forced Subtitles Only" msgstr "강제 표시 자막만" msgid "Burn into video" msgstr "영상에 추가" msgid "" "Render the subtitle over the video.\n" "The subtitle will be part of the video and can not be disabled." msgstr "비디오에 자막을 입힙니다.\n자막은 비디오의 일부가 되고, 비활성화할 수 없습니다." msgid "Set Default Track" msgstr "기본 트랙 지정" msgid "Source Track" msgstr "원본 트랙" msgid "List of audio tracks available from your source." msgstr "원본에서 사용가능한 오디오 트랙의 목록" msgid "Track Name:" msgstr "트랙 이름:" msgid "" "Set the audio track name.\n" "\n" "Players may use this in the audio selection list." msgstr "오디오 트랙 이름 설정.\n\n재생기에서 오디오 선택 목록에 이 이름을 사용합니다." msgid "Mix" msgstr "믹스" msgid "Sample Rate" msgstr "샘플레이트" msgid "" "Dynamic Range Compression: Adjust the dynamic range of the output audio track.\n" "\n" "For source audio that has a wide dynamic range (very loud and very soft sequences),\n" "DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder." msgstr "다이나믹 레인지 압축: 오디오 트랙 출력의 다이나믹 레인지를 조정합니다.\n넓은 다이나믹 레인지를 가진 원본 오디오 (매우 크고 부드러운 시퀀스)에 대해서\n다이나믹 레인지 압축은 큰 소리를 부드럽게 만들고 부드러운 소리를 크게 만들어서 레인지를 '압축'합니다." msgid "Enable bitrate setting" msgstr "비트레이트 설정 활성화" msgid "Enable quality setting" msgstr "품질 설정 활성화" msgid "" "Quality: For output codec's that support it, adjust the quality of " "the output." msgstr "품질: 출력 코덱이 지원하는 경우, 출력물의 품질을 조정." msgid "00.0" msgstr "00.0" msgid "" "Audio Gain: Adjust the amplification or attenuation of the output " "audio track." msgstr "오디오 게인: 출력된 오디오 트랙의 증폭 또는 감쇄를 조정합니다." msgid "Skip This Version" msgstr "이 버전 무시" msgid "Remind Me Later" msgstr "나중에 알려주기" msgid "A new version of HandBrake is available!" msgstr " 새 버전의 HandBrake가 있습니다!" msgid "HandBrake xxx is now available (you have yyy)." msgstr "HandBrake xxx 버전이 있습니다. (현재 버전은 yyy 입니다)" msgid "Release Notes" msgstr "배포 기록" msgid "First Track Matching Selected Languages" msgstr "선택된 언어에 부합하는 첫 트랙" msgid "All Tracks Matching Selected Languages" msgstr "선택된 언어에 부합하는 모든 트랙" msgid "Chapters:" msgstr "챕터:" msgid "Seconds:" msgstr "초:" msgid "Frames:" msgstr "프레임:" msgid "Do Nothing" msgstr "아무 것도 하지 않음" msgid "Show Notification" msgstr "알림 보기" msgid "Quit Handbrake" msgstr "Handbrake 종료" msgid "Put Computer To Sleep" msgstr "잠자기 모르도 전환" msgid "Shutdown Computer" msgstr "컴퓨터 종료" msgid "Week" msgstr "주" msgid "Month" msgstr "월" msgid "Year" msgstr "연도" msgid "Immortal" msgstr "" msgid "Never" msgstr "하지 않음" msgid "Daily" msgstr "매일" msgid "Weekly" msgstr "매주" msgid "Monthly" msgstr "매달" msgid "Default" msgstr "기본" msgid "Fast" msgstr "빠름" msgid "Slow" msgstr "느림" msgid "Slower" msgstr "약간 느림" msgid "Ultralight" msgstr "매우 가벼움" msgid "Light" msgstr "가벼움" msgid "Medium" msgstr "보통" msgid "Strong" msgstr "강인함" msgid "Film" msgstr "필름" msgid "Grain" msgstr "게인" msgid "High Motion" msgstr "많은 움직임" msgid "Animation" msgstr "애니메이션" msgid "Spatial" msgstr "공간" msgid "Temporal" msgstr "임시" msgid "Automatic" msgstr "자동" msgid "Optimal" msgstr "최적" msgid "Normal" msgstr "보통" msgid "Simple" msgstr "단순" msgid "Smart" msgstr "" msgid "Diamond" msgstr "Diamond" msgid "Hexagon" msgstr "Hexagon" msgid "Uneven Multi-Hexagon" msgstr "Uneven Multi-Hexagon" msgid "Exhaustive" msgstr "Exhaustive" msgid "Hadamard Exhaustive" msgstr "Hadamard Exhaustive" msgid "Most" msgstr "대부분" msgid "Some" msgstr "일부" msgid "All" msgstr "모두" msgid "Encode only" msgstr "인코딩만" msgid "Always" msgstr "항상" msgid "(NTSC Film)" msgstr "(NTSC 필름)" msgid "(PAL Film/Video)" msgstr "(PAL 필름/비디오)" msgid "(NTSC Video)" msgstr "(NTSC 비디오)" msgid "%d - %02dh%02dm%02ds - %s" msgstr "%d - %02d시%02d분%02d초 - %s" msgid "%d (%05d.MPLS) - %02dh%02dm%02ds" msgstr "%d (%05d.MPLS) - %02d시%02d분%02d초" msgid "%d (%05d.MPLS) - Unknown Length" msgstr "%d (%05d.MPLS) - 알 수 없는 길이" msgid "%d - %02dh%02dm%02ds" msgstr "%d - %02d시%02d분%02d초" msgid "%d - Unknown Length" msgstr "%d - 알 수 없는 길이" msgid "No Titles" msgstr "타이틀 없음" msgid "No Audio" msgstr "오디오 없음" msgid "Foreign Audio Search" msgstr "외국어 오디오 검색" #, c-format msgid "Chapter %2d" msgstr "챕터 %2d" #, c-format msgid "N/A" msgstr "N/A" #, c-format msgid "" "Invalid Deinterlace Settings:\n" "\n" "%s\n" msgstr "잘못된 디인터레이스 설정:\n\n%s\n" #, c-format msgid "" "Invalid Detelecine Settings:\n" "\n" "%s\n" msgstr "잘못된 Detelecine 설정:\n\n%s\n" #, c-format msgid "" "Invalid Decomb Settings:\n" "\n" "%s\n" msgstr "잘못된 Decomb 설정:\n\n%s\n" msgid "" "Theora is not supported in the MP4 container.\n" "\n" "You should choose a different video codec or container.\n" "If you continue, FFMPEG will be chosen for you." msgstr "Theora는 MP4 컨테이너에서 지원하지 않습니다.\n\n다른 비디오 코덱이나 컨테이너를 선택하세요.\n계속하면, FFMPEG이 선택됩니다." msgid "Continue" msgstr "계속" msgid "No title found.\n" msgstr "발견된 타이틀 없음.\n" msgid "" "Only one subtitle may be burned into the video.\n" "\n" "You should change your subtitle selections.\n" "If you continue, some subtitles will be lost." msgstr "단 하나의 자막만 영상에 추가됩니다.\n\n자막 선택을 변경해야 합니다.\n계속하면, 일부 자막은 무시됩니다." msgid "" "Srt file does not exist or not a regular file.\n" "\n" "You should choose a valid file.\n" "If you continue, this subtitle will be ignored." msgstr "Srt 파일이 존재하지 않거나 올바른 파일이 아닙니다.\n\n유효한 파일을 선택하세요.\n계속하면, 이 자막은 무시됩니다." msgid "" "The source does not support Pass-Thru.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "원본이 패스쓰루를 지원하지 않습니다.\n\n다른 오디오 코덱을 선택해야 합니다.\n계속하면, 임의의 코덱이 선택됩니다." #, c-format msgid "" "%s is not supported in the %s container.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "%s는 %s 컨테이너에서 지원하지 않습니다.\n\n다른 오디오 코덱을 선택하세요.\n계속하면, 임의의 코덱이 선택됩니다." #, c-format msgid "" "The source audio does not support %s mixdown.\n" "\n" "You should choose a different mixdown.\n" "If you continue, one will be chosen for you." msgstr "원본 오디오는 %s 믹스다운을 지원하지 않습니다.\n\n다른 믹스다운을 선택하세요.\n계속하면, 임의의 믹스다운이 선택됩니다." #, c-format msgid "" "Unable to create %s.\n" "\n" "Internal error. Could not parse UI description.\n" "%s" msgstr "%s 생성할 수 없음.\n\n내부 에러. UI 설명을 분석할 수 없음.\n%s" msgid "Index" msgstr "색인" msgid "Duration" msgstr "재생시간" msgid "Title" msgstr "타이틀:" msgid "Job Information" msgstr "작업 정보" msgid "Track Information" msgstr "트랙 정보" msgid "Preset Name" msgstr "사전설정 이름" msgid "The device or file to encode" msgstr "인코딩할 장치 또는 파일" msgid "The preset values to use for encoding" msgstr "인코딩에 사용할 사전설정 값" msgid "Spam a lot" msgstr "" msgid "- Transcode media formats" msgstr "- 변환 미디어 형식" msgid "Globals" msgstr "전역" msgid "Presets" msgstr "사전설정" msgid "Folder" msgstr "폴더" #, c-format msgid "%s path: (%s)" msgstr "%s 경로: (%s)" #, c-format msgid "%s indices: len %d" msgstr "%s 색인: 길이 %d" msgid "Type" msgstr "유형" msgid "Failed to find parent folder when adding child." msgstr "" msgid "Failed to find parent folder while adding child." msgstr "" #, c-format msgid "Can't map language value: (%s)" msgstr "업어 값을 대응할 수 없음: (%s)" #, c-format msgid "Can't map value: (%s)" msgstr "값을 대응할 수 없음: (%s)" msgid "Subtitles" msgstr "자막" msgid "" "%s: Folder already exists.\n" "You can not replace it with a preset." msgstr "%s: 폴더가 이미 존재합니다.\n사전설정으로 대치할 수 없습니다." msgid "" "%s: Preset already exists.\n" "You can not replace it with a folder." msgstr "%s: 사전설정이 이미 존재합니다.\n폴더로 대치할 수 없습니다." msgid "Import Preset" msgstr "사전설정 가져오기" msgid "All (*)" msgstr "모두 (*)" msgid "Presets (*.plist)" msgstr "사전설정 (*.plist)" msgid "Export Preset" msgstr "사전설정 내보내기" msgid "" "Confirm deletion of %s:\n" "\n" "%s" msgstr "%s 삭제 승인:\n\n%s" msgid "folder" msgstr "폴더" msgid "preset" msgstr "사전설정" msgid "No selection??? Perhaps unselected." msgstr "선택한 것 없음??? 아마도 선택되지 않음." #, c-format msgid "Gstreamer Error: %s" msgstr "Gstreamer 에러: %s" #, c-format msgid "" "Missing GStreamer plugin\n" "Audio or Video may not play as expected\n" "\n" "%s" msgstr "GStreamer 플러그인 누락\n오디오나 비디오가 제대로 재생되지 않을 수 있습니다\n\n%s" msgid "Done" msgstr "완료" msgid "Windowed" msgstr "" msgid "Seconds" msgstr "초" msgid "Frames" msgstr "프레임" #, c-format msgid "" "%s (Title %d, %s %d through %d, 2 Video Passes) -->" " %s" msgstr "%s (타이틀 %d, %s %d ~ %d, 2 비디오 수행) --> %s" #, c-format msgid "" "%s (Title %d, %s %d through %d) --> %s" msgstr "%s (타이틀 %d, %s %d ~ %d) --> %s" #, c-format msgid "Modified Preset Based On: %s\n" msgstr "수정된 사전설정의 기반: %s\n" #, c-format msgid "Preset: %s\n" msgstr "사전설정: %s\n" #, c-format msgid "Format: %s Container\n" msgstr "형식: %s 컨테이너\n" msgid "Container Options:" msgstr "컨테이너 옵션:" #, c-format msgid "%sChapter Markers" msgstr "%s챕터 마커" #, c-format msgid "%siPod 5G Support" msgstr "%siPod 5G 지원" #, c-format msgid "%sWeb Optimized" msgstr "%s웹에 최적화" #, c-format msgid "%sLarge File Size (>4GB)" msgstr "%s큰 파일 크기 (>4GB)" #, c-format msgid "Destination: %s\n" msgstr "대상: %s\n" msgid "(Aspect Preserved)" msgstr "(비율 보존)" msgid "(Aspect Lost)" msgstr "(비율 손실)" msgid "(Anamorphic)" msgstr "(아나모픽)" msgid "(Custom Anamorphic)" msgstr "(사용자 아나모픽)" msgid "Picture: " msgstr "영상: " #, c-format msgid "Source: %d x %d, Output %d x %d %s, Crop %d:%d:%d:%d" msgstr "원본: %d x %d, 출력%d x %d %s, 잘라내기 %d:%d:%d:%d" #, c-format msgid ", Display %d x %d" msgstr ", 화면 %d x %d" msgid "Filters:" msgstr "필터:" #, c-format msgid "%sDetelecine" msgstr "%sDetelecine" #, c-format msgid "%sDecomb" msgstr "%sDecomb" #, c-format msgid "%sDeinterlace" msgstr "%s디인터레이스" #, c-format msgid "%sDenoise Filter %s:" msgstr "%s노이즈 제거 필터 %s:" #, c-format msgid "%sDeblock: %d" msgstr "%sDeblock: %d" #, c-format msgid "%sGrayscale" msgstr "%s회색조" #, c-format msgid "Video: %s" msgstr "비디오: %s" #, c-format msgid ", Framerate: %s %s" msgstr ", 프레임레이트: %s %s" #, c-format msgid ", Framerate: Peak %s (may be lower)" msgstr ", 프레임레이트: 피크 %s (낮을 수도 있음)" #, c-format msgid ", Framerate: %s (constant frame rate)" msgstr ", 프레임레이트: %s (고정 프레임레이트)" msgid "Error" msgstr "에러" msgid "Bitrate:" msgstr "비트레이트:" msgid "Bitrate" msgstr "비트레이트" msgid "Quality" msgstr "품질" msgid "Turbo 1st Pass: On\n" msgstr "1 단계 가속: 켜기\n" #, c-format msgid "Video Options: Preset: %s" msgstr "비디오 출력: 사전설정: %s" msgid " - Tune: " msgstr " - 조정:" #, c-format msgid " - Profile: %s" msgstr " - 프로필: %s" #, c-format msgid " - Level: %s" msgstr " - 수준: %s" #, c-format msgid "Advanced Options: %s\n" msgstr "고급 옵션: %s\n" msgid "Audio: " msgstr "오디오: " #, c-format msgid "Audio Tracks: %d" msgstr "오디오 트랙: %d" #, c-format msgid "Bitrate: %d" msgstr "비트레이트: %d" #, c-format msgid "%s --> Encoder: %s" msgstr "%s --> 인코더: %s" #, c-format msgid "%s --> Encoder: %s, Mixdown: %s, SampleRate: %s, %s" msgstr "%s --> 인코더: %s, 믹스다운: %s, 샘플레이트: %s, %s" msgid "Subtitle: " msgstr "자막:" #, c-format msgid "Subtitle Tracks: %d\n" msgstr "자막 트랙: %d\n" msgid " (Force)" msgstr "(강제 표시)" msgid " (Burn)" msgstr "(굽기)" msgid " (Default)" msgstr "(기본)" #, c-format msgid " %s (%s), %s, Offset (ms) %d%s\n" msgstr " %s (%s), %s, 오프셋 (ms) %d%s\n" #, c-format msgid "" "Destination: %s\n" "\n" "Another queued job has specified the same destination.\n" "Do you want to overwrite?" msgstr "대상: %s\n\n큐의 다른 작업이 동일한 대상을 지정하고 있습니다.\n덮어쓰시겠습니까?" msgid "Overwrite" msgstr "덮어쓰기" #, c-format msgid "" "Destination: %s\n" "\n" "This is not a valid directory." msgstr "대상: %s\n\n유효한 디렉토리가 아닙니다." #, c-format msgid "" "Destination: %s\n" "\n" "Can not read or write the directory." msgstr "대상: %s\n\n읽거나 쓸 수 없는 디렉토리입니다." #, c-format msgid "" "Destination filesystem is almost full: %uM free\n" "\n" "Encode may be incomplete if you proceed.\n" msgstr "파일시스템의 저장 공간이 거의 다 찼습니다: %uM 비었음\n\n계속하면 인코딩이 완료되지 않을 수 있습니다.\n" msgid "Proceed" msgstr "진행" #, c-format msgid "" "Destination: %s\n" "\n" "File already exists.\n" "Do you want to overwrite?" msgstr "대상: %s\n\n파일이 이미 존재합니다.\n덮어쓰시겠습니까?" msgid "" "Duplicate destination files detected.\n" "Duplicates will not be added to the queue." msgstr "중복된 대상 파일이 반경되었습니다.\n중복된 파일은 큐에 추가되지 않습니다." msgid "No Title" msgstr "타이틀 없음" msgid "" "There is another title with the same destination file name.\n" "This title will not be added to the queue unless you change\n" "the output file name.\n" msgstr "같은 대상 파일 이름을 가진 다른 타이틀이 있습니다.\n출력 파일을 변경하기 전에는 이 타이틀이 큐에\n추가되지 않습니다.\n" msgid "Stop" msgstr "중지" msgid "Stop Encoding" msgstr "인코딩 중지" msgid "Resume" msgstr "다시 시작" msgid "Resume Encoding" msgstr "인코딩 다시 시작" msgid "S_top Queue" msgstr "큐 정지(_T)" msgid "_Start Queue" msgstr "큐 시작(_S)" msgid "_Resume Queue" msgstr "큐 다시 시작(_R)" msgid "Resume Queue" msgstr "큐 다시 시작" msgid "" "You are currently encoding. What would you like to do?\n" "\n" msgstr "인코딩 중입니다. 무엇을 하시겠습니까?\n\n" #, c-format msgid "" "You have %d unfinished job(s) in a saved queue.\n" "\n" "Would you like to reload them?" msgstr "저장된 큐에 종료되지 않은 %d 개의 작업이 있습니다.\n\n다시 부르겠습니까?" msgid "No" msgstr "아니오" msgid "Yes" msgstr "네" #, c-format msgid "Usage: %s infile [outfile]\n" msgstr "사용법: %s 입력파일 [출력파일]\n" #, c-format msgid "Offset: %dms" msgstr "오프셋: %dms" msgid "Burned Into Video" msgstr "영상에 추가" msgid "Passthrough" msgstr "패스쓰루" msgid "through" msgstr "~" msgid "(Forced Subtitles Only)" msgstr "(강제 표시 자막만)" msgid "(Default)" msgstr "(기본)" msgid "Error!" msgstr "에러!" #, c-format msgid "Preferred Language: %s" msgstr "선호하는 언어: %s" #, c-format msgid "Add %s subtitle track if default audio is not %s" msgstr "%s 자막 트랙을 기본 오디오 트랙이 %s 아닌 경우 추가" #, c-format msgid "Type %s" msgstr "유형 %s" #, c-format msgid "Type %s value %s" msgstr "유형 %s 값 %s" #, c-format msgid "Type %s value %d" msgstr "유형 %s 값 %d" #, c-format msgid "Type %s value %" msgstr "유형 %s 값 %" #, c-format msgid "Type %s value %f" msgstr "유형 %s 값 %f" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"%s\"" msgstr "%s\n\n확장 옵션:\n\"%s\"" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"\"" msgstr "%s\n\n확장 옵션:\n\"\"" msgid "Any" msgstr "아무거나" msgid "0: SAD, no subpel" msgstr "0: SAD, 부화소 없음" msgid "4: SATD, qpel on all" msgstr "4: SATD, 1/4 화소 적용" msgid "5: SATD, multi-qpel on all" msgstr "5: SATD, 복수의 1/4 화소 적용" msgid "6: RD in I/P-frames" msgstr "6: I/P-프레임의 RD" msgid "7: RD in all frames" msgstr "7: 모든 프레임의 RD" msgid "8: RD refine in I/P-frames" msgstr "8: I/P-프레임의 RD 수정" msgid "9: RD refine in all frames" msgstr "9: 모든 프레임의 RD 수정" msgid "10: QPRD in all frames" msgstr "10: 모든 프레임의 QPRD" msgid "11: No early terminations in analysis" msgstr "11: 분석 중 조기 종료하지 않음" msgid "Your names" msgstr "이름" msgid "Your emails" msgstr "이메일" HandBrake-0.10.2/gtk/po/pt_BR.po0000664000175200017520000014405712466165306016634 0ustar handbrakehandbrake# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Gabriel Luiz Bastos de Oliveira , 2014 msgid "" msgstr "" "Project-Id-Version: HandBrake-0.10\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-03 13:01+0300\n" "PO-Revision-Date: 2014-12-15 11:17+0000\n" "Last-Translator: Gabriel Luiz Bastos de Oliveira \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/handbrake/language/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: pt_BR\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" msgid "Quality: " msgstr "Qualidade:" #, c-format msgid "Bitrate: %dkbps" msgstr "" #, c-format msgid "Bitrate: %.4gkbps" msgstr "" msgid "Passthrough" msgstr "" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s\n" "Track Name: %s" msgstr "%s\nGanho: %s\nDRC: %s\nNome da Faixa: %s" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s" msgstr "%s\nGanho: %s\nDRC: %s" msgid "Add" msgstr "Adicionar" msgid "" "Add an audio encoder.\n" "Each selected source track will be encoded with all selected encoders." msgstr "Adicionar um encoder de áudio.\nCada faixa selecionada será codificada com todos os encoders selecionados." msgid "Set the audio codec to encode this track with." msgstr "Selecione o codec de áudio para codificar essa faixa." msgid "Set the bitrate to encode this track with." msgstr "Selecione o bitrate para codificar essa faixa." msgid "" "Audio Quality:\n" "For encoders that support it, adjust the quality of the output." msgstr "" msgid "Set the mixdown of the output audio track." msgstr "" msgid "Set the sample rate of the output audio track." msgstr "" msgid "" "Audio Gain:\n" "Adjust the amplification or attenuation of the output audio track." msgstr "" msgid "0dB" msgstr "" msgid "%ddB" msgstr "" msgid "%.4gkHz" msgstr "" msgid "%d - %s (%.4gkHz)" msgstr "" msgid "" "Dynamic Range Compression:\n" "Adjust the dynamic range of the output audio track.\n" "For source audio that has a wide dynamic range,\n" "very loud and very soft sequences, DRC allows you\n" "to 'compress' the range by making loud sounds\n" "softer and soft sounds louder.\n" msgstr "" msgid "Remove this audio encoder" msgstr "" msgid "Closing HandBrake will terminate encoding.\n" msgstr "" msgid "No Title Found" msgstr "" msgid "none" msgstr "nenhum" msgid "Not Selected" msgstr "Não Selecionado" msgid "zerolatency x264 tune selected, forcing constant framerate" msgstr "" msgid "Scanning ..." msgstr "Escaneando ..." msgid "Stop Scan" msgstr "" msgid "On" msgstr "Ligado" msgid "Strict" msgstr "Estrito" msgid "Loose" msgstr "" msgid "Custom" msgstr "Customizado" msgid "Unknown" msgstr "Desconhecido" msgid "auto" msgstr "automático" #, c-format msgid "" "%s\n" "\n" "%s in %d seconds ..." msgstr "%s\n\n%s em %d segundos ..." #, c-format msgid "%sYour movie will be lost if you don't continue encoding." msgstr "%sSeu vídeo será perdido se não continuar a codificação." msgid "Cancel Current and Stop" msgstr "Cancelar Atual e Parar" msgid "Cancel Current, Start Next" msgstr "Cancelar Atual, Começar o Próximo" msgid "Finish Current, then Stop" msgstr "Terminar o Atual, em seguida Parar." msgid "Continue Encoding" msgstr "Continuar Codificação" msgid "Custom " msgstr "" msgid "Modified " msgstr "Modificado" #, c-format msgid "Handbrake Version: %s (%d)\n" msgstr "Versão do Handbrake: %s (%d)\n" #, c-format msgid "%d encode(s) pending" msgstr "" #, c-format msgid "job %d of %d, " msgstr "trabalho %d de %d," #, c-format msgid "pass %d (subtitle scan) of %d, " msgstr "" #, c-format msgid "pass %d of %d, " msgstr "" #, c-format msgid "Encoding: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" msgstr "" #, c-format msgid "Encoding: %s%s%.2f %% (ETA %02dh%02dm%02ds)" msgstr "" #, c-format msgid "Encoding: %s%s%.2f %%" msgstr "" msgid "Searching for start time, " msgstr "" msgid "Scanning..." msgstr "Escaneando..." #, c-format msgid "Scanning title %d of %d..." msgstr "" #, c-format msgid "Scanning title %d of %d preview %d..." msgstr "" msgid "Source" msgstr "Fonte" msgid "Choose Video Source" msgstr "Escolher Fonte de Vídeo" msgid "Paused" msgstr "Pausado" msgid "Encode Done!" msgstr "Codificação Terminada!" msgid "Encode Canceled." msgstr "Codificação Cancelada." msgid "Encode Failed." msgstr "Falha na Codificação." msgid "Muxing: This may take a while..." msgstr "" msgid "Scan this DVD source" msgstr "Escanear essa fonte de DVD." msgid "AutoScan" msgstr "" msgid "Suspend" msgstr "Suspender" #, c-format msgid "Suspend failed: %s" msgstr "" msgid "Suspend failed" msgstr "" msgid "Shutdown" msgstr "Desligar" #, c-format msgid "Shutdown failed: %s" msgstr "Falha ao desligar: %s" msgid "Shutdown failed" msgstr "Falha ao desligar" msgid "Encoding" msgstr "Codificando" #, c-format msgid "press %d %d" msgstr "pressione %d %d" #, c-format msgid "" "Invalid Settings:\n" "%s" msgstr "Configurações inválidas:\n%s" msgid "Cancel" msgstr "Cancelar" #, c-format msgid "%s: %.4g (Warning: lossless)" msgstr "%s: %.4g (Aviso: sem perda)" #, c-format msgid "HandBrake %s/%s is now available (you have %s/%d)." msgstr "HandBrake %s/%s está disponível (você tem %s/%d)." msgid "Encode Complete" msgstr "Codificação Completa" msgid "Put down that cocktail, Your HandBrake queue is done!" msgstr "" msgid "Your encode is complete." msgstr "Sua codificação está completa." msgid "Shutting down the computer" msgstr "Desligando o computador" msgid "Putting computer to sleep" msgstr "Colocando o computador em suspensão" msgid "Quiting Handbrake" msgstr "Saindo Handbrake" msgid "Bottom" msgstr "" #, c-format msgid "open failed: %s\n" msgstr "falha ao abrir: %s\n" msgid "No key for dictionary item" msgstr "" msgid "Invalid container type. This shouldn't happen" msgstr "" #, c-format msgid "%s:missing a requried attribute" msgstr "" #, c-format msgid "" "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" msgstr "" msgid "language" msgstr "linguagem" msgid "Language" msgstr "Linguagem" msgid "" "The language this text is in, as an ISO code. Pango can use this as a hint " "when rendering the text. If you don't understand this parameter, you " "probably don't need it" msgstr "" msgid "" "The preferred place to ellipsize the string, if the cell renderer does not " "have enough room to display the entire string" msgstr "" msgid "" "How to break the string into multiple lines, if the cell renderer does not " "have enough room to display the entire string" msgstr "" msgid "" "Render the subtitle over the video.\n" "\n" "The subtitle will be part of the video and can not be disabled." msgstr "" msgid "Burned In" msgstr "" msgid "" "Set the default output subtitle track.\n" "\n" "Most players will automatically display this\n" "subtitle track whenever the video is played.\n" "\n" "This is useful for creating a \"forced\" track\n" "in your output." msgstr "" msgid "Default" msgstr "Padrão" msgid "" "Use only subtitles that have been flagged\n" "as forced in the source subtitle track\n" "\n" "\"Forced\" subtitles are usually used to show\n" "subtitles during scenes where someone is speaking\n" "a foreign language." msgstr "" msgid "Forced Only" msgstr "Somente Forçado" msgid "" "Add (or subtract) an offset (in milliseconds)\n" "to the start of the SRT subtitle track.\n" "\n" "Often, the start of an external SRT file\n" "does not coincide with the start of the video.\n" "This setting allows you to synchronize the files." msgstr "" msgid "SRT Offset" msgstr "" msgid "Off" msgstr "" msgid "" "The source subtitle track\n" "\n" "You can choose any of the subtitles\n" "recognized in your source file.\n" "\n" "In addition, there is a special track option\n" "\"Foreign Audio Search\". This option will add\n" "an extra pass to the encode that searches for\n" "subtitles that may correspond to a foreign\n" "language scene. This option is best used in\n" "conjunction with the \"Forced\" option." msgstr "" msgid "Track" msgstr "Faixa" msgid "About HandBrake" msgstr "Sobre HandBrake" msgid "" "Copyright © 2008 - 2013 John Stebbins\n" "Copyright © 2004 - 2013, HandBrake Devs" msgstr "Copyright © 2008-2013 John Stebbins\nCopyright © 2004 - 2013, HandBrake Devs" msgid "" "HandBrake is a GPL-licensed, multiplatform, multithreaded video transcoder." msgstr "" msgid "http://handbrake.fr" msgstr "http://handbrake.fr" msgid "" "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n" "\n" "HandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgstr "" msgid "_Minimize/Maximize" msgstr "_Minimizar/Maximizar" msgid "_Pause Queue" msgstr "_Pausar Fila" msgid "_Quit" msgstr "_Sair" msgid "_About" msgstr "S_obre" msgid "HandBrake" msgstr "HandBrake" msgid "_File" msgstr "_Arquivo" msgid "_Source" msgstr "_Fonte" msgid "Single _Title" msgstr "Título _Único" msgid "_Destination" msgstr "_Destino" msgid "_Preferences" msgstr "_Preferências" msgid "_Queue" msgstr "F_ila" msgid "_Add" msgstr "_Adicionar" msgid "Add _Multiple" msgstr "" msgid "_Start" msgstr "" msgid "_Pause" msgstr "" msgid "_View" msgstr "" msgid "HandBrake For _Dumbies" msgstr "" msgid "_Show Presets" msgstr "" msgid "_Preview" msgstr "" msgid "_Activity Window" msgstr "" msgid "Show _Queue" msgstr "" msgid "_Presets" msgstr "" msgid "_Save" msgstr "" msgid "_Delete" msgstr "" msgid "_Make Default" msgstr "" msgid "_New Folder" msgstr "" msgid "_Export" msgstr "" msgid "_Import" msgstr "" msgid "_Update Built-in Presets" msgstr "" msgid "_Help" msgstr "" msgid "_Guide" msgstr "" msgid "Start Encoding" msgstr "" msgid "Start" msgstr "" msgid "Pause Encoding" msgstr "" msgid "Pause" msgstr "" msgid "Add to Queue" msgstr "" msgid "Enqueue" msgstr "" msgid "Show Queue" msgstr "" msgid "Queue" msgstr "" msgid "" "Open Picture Settings and Preview window.\n" "Here you can adjust cropping, resolution, aspect ratio, and filters." msgstr "" msgid "Preview" msgstr "" msgid "Show Activity Window" msgstr "" msgid "Activity" msgstr "" msgid "Source:" msgstr "" msgid "None" msgstr "" msgid "Title:" msgstr "" msgid "" "Set the title to encode.\n" "By default the longest title is chosen.\n" "This is often the feature title of a DVD." msgstr "" msgid "Angle:" msgstr "" msgid "For multi-angle DVD's, select the desired angle to encode." msgstr "" msgid "Reset All Titles" msgstr "" msgid "Apply current settings to all titles" msgstr "" msgid "Range of title to encode. Can be chapters, seconds, or frames." msgstr "" msgid "Set the first chapter to encode." msgstr "" msgid "Set the last chapter to encode." msgstr "" msgid "Duration:" msgstr "" msgid "Destination" msgstr "" msgid "File:" msgstr "" msgid "Destination filename for your encode." msgstr "" msgid "Destination directory for your encode." msgstr "" msgid "Destination Directory" msgstr "" msgid "Format:" msgstr "" msgid "Format to mux encoded tracks to." msgstr "" msgid "iPod 5G Support" msgstr "" msgid "Add iPod Atom needed by some older iPods." msgstr "" msgid "Web optimized" msgstr "" msgid "" "Optimize the layout of the MP4 file for progressive download.\n" "This allows a player to initiate playback before downloading the entire file." msgstr "" msgid "Large file (>4GB)" msgstr "" msgid "" "Allow 64 bit MP4 file which can be over 4GB.\n" "\n" "Caution: This option may break device compatibility." msgstr "" msgid "Presets List" msgstr "" msgid "Source Codec:" msgstr "" msgid "Dimensions:" msgstr "" msgid "Aspect: " msgstr "" msgid "Frame Rate:" msgstr "" msgid "Source Picture Parameters" msgstr "" msgid "Autocrop:" msgstr "" msgid "Crop:" msgstr "" msgid "Crop Dimensions:" msgstr "" msgid "Cropping" msgstr "" msgid "Scale Dimensions:" msgstr "" msgid "Optimal for Source:" msgstr "" msgid "Anamorphic:" msgstr "" msgid "Scaling" msgstr "" msgid "Presentation Dimensions:" msgstr "" msgid "Summary" msgstr "" msgid "Left Crop" msgstr "" msgid "Top Crop" msgstr "" msgid "Bottom Crop" msgstr "" msgid "Right Crop" msgstr "" msgid "Auto Crop" msgstr "" msgid "Automatically crop black borders around edges of the video." msgstr "" msgid "Loose Crop" msgstr "" msgid "" "When picture settings require that the image\n" "dimensions be rounded to some multiple number\n" "of pixels, this setting will crop a few extra pixels\n" "instead of doing exact cropping and then scaling to\n" "the required multiple." msgstr "" msgid "width:" msgstr "" msgid "" "This is the width that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "" msgid "height:" msgstr "" msgid "" "This is the height that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "" msgid "Optimal for source" msgstr "" msgid "" "If enabled, select the 'optimal' storage resolution.\n" "This will be the resolution that most closely matches the source resolution after cropping." msgstr "" msgid "" "Anamorphic Modes:\n" "\n" "None - Force pixel aspect ratio to 1:1.\n" "Loose - Align dimensions to chosen 'Alignment' value\n" " and pick pixel aspect ratio that preserves the\n" " original display aspect ratio\n" "Strict - Keep original source dimensions and pixel\n" " aspect ratio" msgstr "" msgid "Alignment:" msgstr "" msgid "" "Align storage dimensions to multiples of this value.\n" "\n" "This setting is only necessary for compatibility with some devices.\n" "You should use 2 unless you experience compatibility issues." msgstr "" msgid "Storage Geometry" msgstr "" msgid "" "This is the display width. It is the result of scaling the storage " "dimensions by the pixel aspect." msgstr "" msgid "Pixel Aspect:" msgstr "" msgid "" "Pixel aspect defines the shape of the pixels.\n" "\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "" msgid ":" msgstr "" msgid "" "Pixel aspect defines the shape of the pixels.\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "" msgid "Keep Aspect" msgstr "" msgid "" "If enabled, the original display aspect of the source will be maintained." msgstr "" msgid "Display Aspect:" msgstr "" msgid "Display Geometry" msgstr "" msgid "Grayscale" msgstr "" msgid "If enabled, filter colour components out of video." msgstr "" msgid "Deblock:" msgstr "" msgid "" "The deblocking filter removes a common type of compression artifact.\n" "If your source exhibits 'blockiness', this filter may help clean it up." msgstr "" msgid "Denoise Filter:" msgstr "" msgid "" "Denoise filtering reduces or removes the appearance of noise and grain.\n" "Film grain and other types of high frequency noise are difficult to compress.\n" "Using this filter on such sources can result in smaller file sizes." msgstr "" msgid "Denoise Preset:" msgstr "" msgid "Denoise Tune:" msgstr "" msgid "" "Custom denoise filter string format\n" "\n" "SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgstr "" msgid "Detelecine:" msgstr "" msgid "" "This filter removes 'combing' artifacts that are the result of telecining.\n" "\n" "Telecining is a process that adjusts film framerates that are 24fps to NTSC video frame rates which are 30fps." msgstr "" msgid "" "Custom detelecine filter string format\n" "\n" "JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgstr "" msgid "Decomb" msgstr "" msgid "" "Choose decomb or deinterlace filter options.\n" "\n" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced.\n" "\n" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "" msgid "Deinterlace" msgstr "" msgid "Decomb:" msgstr "" msgid "" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced." msgstr "" msgid "" "Custom decomb filter string format\n" "\n" "Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\n" "BlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\n" "ErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgstr "" msgid "Deinterlace:" msgstr "" msgid "" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "" msgid "" "Custom deinterlace filter string format\n" "\n" "YadifMode:YadifParity:McdintMode:McdeintQp" msgstr "" msgid "Filters" msgstr "" msgid "Picture" msgstr "" msgid "Video Encoder:" msgstr "" msgid "Available video encoders." msgstr "" msgid "Framerate:" msgstr "" msgid "" "Output framerate.\n" "\n" "'Same as source' is recommended. If your source video has\n" "a variable framerate, 'Same as source' will preserve it." msgstr "" msgid "Constant Framerate" msgstr "" msgid "Same as source" msgstr "" msgid "kbps" msgstr "" msgid "(variable)" msgstr "" msgid "(constant)" msgstr "" msgid "Enables constant framerate output." msgstr "" msgid "Peak Framerate (VFR)" msgstr "" msgid "" "Enables variable framerate output with a peak\n" "rate determined by the framerate setting.\n" "\n" "VFR is not compatible with some players." msgstr "" msgid "Variable Framerate" msgstr "" msgid "" "Enables variable framerate output.\n" "\n" "VFR is not compatible with some players." msgstr "" msgid "" "Set the desired quality factor.\n" "The encoder targets a certain quality.\n" "The scale used by each video encoder is different.\n" "\n" "x264's scale is logarithmic and lower values correspond to higher quality.\n" "So small decreases in value will result in progressively larger increases\n" "in the resulting file size. A value of 0 means lossless and will result\n" "in a file size that is larger than the original source, unless the source\n" "was also lossless.\n" "\n" "FFMpeg's and Theora's scale is more linear.\n" "These encoders do not have a lossless mode." msgstr "" msgid "Constant Quality:" msgstr "" msgid "Bitrate (kbps): " msgstr "" msgid "" "Set the average bitrate.\n" "\n" "The instantaneous bitrate can be much higher or lower at any point in time.\n" "But the average over a long duration will be the value set here. If you need\n" "to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings." msgstr "" msgid "2-Pass Encoding" msgstr "" msgid "" "Perform 2 Pass Encoding.\n" "\n" "The 'Bitrate' option is prerequisite. During the 1st pass, statistics about\n" "the video are collected. Then in the second pass, those statistics are used\n" "to make bitrate allocation decisions." msgstr "" msgid "Turbo First Pass" msgstr "" msgid "" "During the 1st pass of a 2 pass encode, use settings that speed things " "along." msgstr "" msgid "Use Advanced Options" msgstr "" msgid "" "Use advanced options Tab for x264 settings.\n" "\n" "Use at your own risk!" msgstr "" msgid "Preset:" msgstr "" msgid "" "Adjusts encoder settings to trade off compression efficiency against encoding speed.\n" "\n" "This establishes your default encoder settings.\n" "Tunes, profiles, levels and advanced option string will be applied to this.\n" "You should generally set this option to the slowest you can bear since slower\n" "settings will result in better quality or smaller files." msgstr "" msgid "Tune:" msgstr "" msgid "" "Tune settings to optimize for common scenarios.\n" "\n" "This can improve effeciency for particular source characteristics or set\n" "characteristics of the output file. Changes will be applied after the\n" "preset but before all other parameters." msgstr "" msgid "Fast Decode" msgstr "" msgid "" "Reduce decoder CPU usage.\n" "\n" "Set this if your device is struggling to play the output (dropped frames)." msgstr "" msgid "Zero Latency" msgstr "" msgid "" "Minimize latency between input to encoder and output of decoder.\n" "\n" "This is useful for broadcast of live streams.\n" "\n" "Since HandBrake is not suitable for live stream broadcast purposes,\n" "this setting is of little value here." msgstr "" msgid "Profile:" msgstr "" msgid "" "Sets and ensures compliance with the specified profile.\n" "\n" "Overrides all other settings." msgstr "" msgid "Level:" msgstr "" msgid "" "Sets and ensures compliance with the specified level.\n" "\n" "Overrides all other settings." msgstr "" msgid "More Settings:" msgstr "" msgid "" "Additional encoder settings.\n" "\n" "Colon separated list of encoder options." msgstr "" msgid "Video" msgstr "" msgid "Selection Behavior:" msgstr "" msgid "Remove" msgstr "" msgid "Available Languages" msgstr "" msgid "Selected Languages" msgstr "" msgid "Use only first encoder for secondary audio" msgstr "" msgid "" "Only the primary audio track will be encoded with the full encoder list.\n" "All other secondary audio output tracks will be encoded with first encoder only." msgstr "" msgid "Auto Passthru:" msgstr "" msgid "MP3" msgstr "" msgid "" "Enable this if your playback device supports MP3.\n" "This permits MP3 passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports AAC.\n" "This permits AAC passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports AC-3.\n" "This permits AC-3 passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports DTS.\n" "This permits DTS passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports DTS-HD.\n" "This permits DTS-HD passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "Passthru Fallback:" msgstr "" msgid "" "Set the audio codec to encode with when a suitable track can not be found " "for audio passthru." msgstr "" msgid "Audio Encoder Settings:" msgstr "" msgid "Each selected source track will be encoded with all selected encoders" msgstr "" msgid "Encoder" msgstr "" msgid "Bitrate/Quality" msgstr "" msgid "Mixdown" msgstr "" msgid "Samplerate" msgstr "" msgid "Gain" msgstr "" msgid "Audio Defaults" msgstr "" msgid "Add new audio settings to the list" msgstr "" msgid "Add All" msgstr "" msgid "Add all audio tracks to the list" msgstr "" msgid "Reload Defaults" msgstr "" msgid "Reload all audio settings from defaults" msgstr "" msgid "Audio List" msgstr "" msgid "Preferred Language: None" msgstr "" msgid "Add Foreign Audio Search Pass" msgstr "" msgid "" "Add \"Foreign Audio Search\" when the default audio track is your preferred language.\n" "This search pass finds short sequences of foreign audio and provides subtitles for them." msgstr "" msgid "Add subtitle track if default audio is foreign" msgstr "" msgid "" "When the default audio track is not your preferred language, add a subtitle " "track." msgstr "" msgid "Add Closed Captions when available" msgstr "" msgid "" "Closed captions are text subtitles that can be added to any container as a " "soft subtitle track (not burned)" msgstr "" msgid "Subtitle Defaults" msgstr "" msgid "Add new subtitle settings to the list" msgstr "" msgid "Add all subtitle tracks to the list" msgstr "" msgid "Reload all subtitle settings from defaults" msgstr "" msgid "Subtitle List" msgstr "" msgid "Reference Frames:" msgstr "" msgid "" "Sane values are ~1-6. The more you add, the better the compression, but the slower the encode.\n" "Cel animation tends to benefit from more reference frames a lot more than film content.\n" "\n" "Note that many hardware devices have limitations on the number of supported reference\n" "frames, so if you're encoding for a handheld or standalone player, don't touch this unless\n" "you're absolutely sure you know what you're doing!" msgstr "" msgid "Maximum B-Frames:" msgstr "" msgid "" "Sane values are ~2-5. This specifies the maximum number of sequential B-frames that the encoder can use.\n" "\n" "Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.\n" "Cel-animated source material and B-pyramid also significantly increase the usefulness of larger\n" "values.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off)." msgstr "" msgid "Pyramidal B-Frames:" msgstr "" msgid "" "B-pyramid improves compression by creating a pyramidal structure (hence the name)\n" "of B-frames, allowing B-frames to reference each other to improve compression.\n" "\n" "Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit." msgstr "" msgid "Weighted P-Frames:" msgstr "" msgid "" "Performs extra analysis to decide upon weighting parameters for each frame.\n" "\n" "This improves overall compression slightly and improves the quality of fades greatly.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires weighted P-frame\n" "prediction to be disabled. Note that some devices and players, even those that support\n" "Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is\n" "completely incompatible with it, for example." msgstr "" msgid "8x8 Transform" msgstr "" msgid "" "The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.\n" "\n" "It improves compression by at least 5% at a very small speed cost and may\n" "provide an unusually high visual quality benefit compared to its compression\n" "gain. However, it requires High Profile, which many devices may not support." msgstr "" msgid "CABAC Entropy Encoding" msgstr "" msgid "" "After the encoder has done its work, it has a bunch of data that\n" "needs to be compressed losslessly, similar to ZIP or RAR. H.264 provides\n" "two options for this: CAVLC and CABAC. CABAC decodes a lot slower but\n" "compresses significantly better (10-30%), especially at lower bitrates.\n" "\n" "If you're looking to minimize CPU requirements for video playback, disable this option.\n" "Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled." msgstr "" msgid "Encoding Features" msgstr "" msgid "Motion Est. Method:" msgstr "" msgid "" "Controls the motion estimation method.\n" "\n" "Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.\n" "A better motion search method improves compression at the cost of speed.\n" "\n" "Diamond: performs an extremely fast and simple search using a diamond pattern.\n" "Hexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n" "Uneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n" "Exhaustive: performs a \"dumb\" search of every pixel in a wide area. Significantly slower for only a small compression gain.\n" "Transformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement." msgstr "" msgid "Subpel ME & Mode:" msgstr "" msgid "" "This setting controls both subpixel-precision motion estimation and mode decision methods.\n" "\n" "Subpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n" "Mode decision is the method used to choose how to encode each block of the frame: a very important decision.\n" "SAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n" "6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n" "11 disables all early terminations in analysis.\n" "10 and 11, the most powerful and slowest options, require adaptive quantization (aq-mode > 0) and trellis 2 (always)." msgstr "" msgid "Motion Est. Range:" msgstr "" msgid "" "This is the distance x264 searches from its initial guess at the\n" "motion of a block in order to try to find its actual motion.\n" "\n" "The default is fine for most content, but extremely high motion video,\n" "especially at HD resolutions, may benefit from higher ranges, albeit at\n" "a high speed cost." msgstr "" msgid "Adaptive Direct Mode:" msgstr "" msgid "" "H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n" "\n" "Spatial, the default, is almost always better, but temporal is sometimes useful too.\n" "x264 can, at the cost of a small amount of speed (and accordingly for a small compression gain),\n" "adaptively select which is better for each particular frame." msgstr "" msgid "Adaptive B-Frames:" msgstr "" msgid "" "x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n" "\n" "Fast mode takes roughly the same amount of time no matter how many B-frames you specify.\n" "However, while fast, its decisions are often suboptimal.\n" "\n" "Optimal mode gets slower as the maximum number of B-Frames increases,\n" "but makes much more accurate decisions, especially when used with B-pyramid." msgstr "" msgid "Partitions:" msgstr "" msgid "" "Mode decision picks from a variety of options to make its decision:\n" "this option chooses what options those are.\n" "\n" "Fewer partitions to check means faster encoding, at the cost of worse\n" "decisions, since the best option might have been one that was turned off." msgstr "" msgid "Trellis:" msgstr "" msgid "" "Trellis fine-tunes the rounding of transform coefficients to\n" "squeeze out 3-5% more compression at the cost of some speed.\n" "\n" "\"Always\" uses trellis not only during the main encoding process, but also\n" "during analysis, which improves compression even more, albeit at great speed cost.\n" "\n" "Trellis costs more speed at higher bitrates and requires CABAC." msgstr "" msgid "Analysis" msgstr "" msgid "Adaptive Quantization Strength:" msgstr "" msgid "" "Adaptive quantization controls how the encoder distributes bits across the frame.\n" "\n" "Higher values take more bits away from edges and complex areas to improve areas with finer detail." msgstr "" msgid "Psychovisual Rate Distortion:" msgstr "" msgid "" "Psychovisual rate-distortion optimization takes advantage of the characteristics of human\n" "vision to dramatically improve apparent detail and sharpness.\n" "The effect can be made weaker or stronger by adjusting the strength.\n" "Being an RD algorithm, it requires mode decision to be at least \"6\"." msgstr "" msgid "Psychovisual Trellis:" msgstr "" msgid "" "Psychovisual trellis is an experimental algorithm to further\n" "improve sharpness and detail retention beyond what Psychovisual RD does.\n" "\n" "Recommended values are around 0.2, though higher values may help for very\n" "grainy video or lower bitrate encodes. Not recommended for cel animation\n" "and other sharp-edged graphics." msgstr "" msgid "Deblocking: " msgstr "" msgid "" "H.264 deblocking filter.\n" "\n" "h.264 has a built-in deblocking filter that smooths out blocking artifacts\n" "after decoding each frame. This not only improves visual quality, but also\n" "helps compression significantly. The deblocking filter takes a lot of CPU power,\n" "so if you're looking to minimize CPU requirements for video playback, disable it.\n" "\n" "The deblocking filter has two adjustable parameters, \"strength\" (Alpha) and \"threshold\" (Beta).\n" "The former controls how strong (or weak) the deblocker is, while the latter controls how many\n" "(or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking.\n" "The default is 0 (normal strength) for both parameters." msgstr "" msgid "No DCT Decimate" msgstr "" msgid "" "x264 normally zeroes out nearly-empty data blocks to save bits to\n" "be better used for some other purpose in the video. However, this can\n" "sometimes have slight negative effects on retention of subtle grain and\n" "dither.\n" "\n" "Don't touch this unless you're having banding issues or other such cases\n" "where you are having trouble keeping fine noise." msgstr "" msgid "Psychovisual" msgstr "" msgid "" "Your selected options will appear here.\n" "You can edit these and add additional options.\n" "\n" "Default values will not be shown. The defaults are:\n" "ref=3:bframes=3:b-adapt=fast:direct=spatial:\n" "b-pyramid=normal:weightp=2:me=hex:merange=16:\n" "subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\n" "deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\n" "no-fast-pskip=0:no-dct-decimate=0:cabac=1" msgstr "" msgid "Current x264 Advanced Option String" msgstr "" msgid "Advanced Video" msgstr "" msgid "Chapter Markers" msgstr "" msgid "Add chapter markers to output file." msgstr "" msgid "Chapters" msgstr "" msgid "Actors:" msgstr "" msgid "Director:" msgstr "" msgid "Release Date:" msgstr "" msgid "Comment:" msgstr "" msgid "Genre:" msgstr "" msgid "Description:" msgstr "" msgid "Plot:" msgstr "" msgid "Tags" msgstr "" msgid "Settings" msgstr "" msgid "Edit" msgstr "" msgid "Reload" msgstr "" msgid "" "Mark selected queue entry as pending.\n" "Resets the queue job to pending and ready to run again." msgstr "" msgid "Reload All" msgstr "" msgid "" "Mark all queue entries as pending.\n" "Resets all queue jobs to pending and ready to run again." msgstr "" msgid "OK" msgstr "" msgid "Select All" msgstr "" msgid "Mark all titles for adding to the queue" msgstr "" msgid "Clear All" msgstr "" msgid "Unmark all titles" msgstr "" msgid "Destination files OK. No duplicates detected." msgstr "" msgid "Select this title for adding to the queue.\n" msgstr "" msgid "Preferences" msgstr "" msgid "Automatically check for updates" msgstr "" msgid "When all encodes are complete" msgstr "" msgid "Use automatic naming (uses modified source name)" msgstr "" msgid "Auto-Name Template" msgstr "" msgid "" "Available Options: {source} {title} {chapters} {date} {time} {quality} " "{bitrate}" msgstr "" msgid "Use iPod/iTunes friendly (.m4v) file extension for MP4" msgstr "" msgid "Number of previews" msgstr "" msgid "Filter short titles (seconds)" msgstr "" msgid "Show system tray icon" msgstr "" msgid "General" msgstr "" msgid "Constant Quality fractional granularity" msgstr "" msgid "Use dvdnav (instead of libdvdread)" msgstr "" msgid "Put individual encode logs in same location as movie" msgstr "" msgid "Activity Log Verbosity Level" msgstr "" msgid "Activity Log Longevity" msgstr "" msgid "Scale down High Definition previews" msgstr "" msgid "Automatically Scan DVD when loaded" msgstr "" msgid "Scans the DVD whenever a new disc is loaded" msgstr "" msgid "Hide Advanced Video Options Tab" msgstr "" msgid "" "Use advanced video options at your own risk.\n" "We recommend that you use the controls available\n" "on the Video tab instead." msgstr "" msgid "Delete completed jobs from queue" msgstr "" msgid "" "By default, completed jobs remain in the queue and are marked as complete.\n" "Check this if you want the queue to clean itself up by deleting completed jobs." msgstr "" msgid "Allow Tweaks" msgstr "" msgid "Allow HandBrake For Dummies" msgstr "" msgid "Advanced" msgstr "" msgid "Folder Name:" msgstr "" msgid "Description" msgstr "" msgid "Preset Name:" msgstr "" msgid "Custom Picture Dimensions" msgstr "" msgid "Maximum Width:" msgstr "" msgid "Enable maximum width limit." msgstr "" msgid "" "This is the maximum width that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source width is greater.\n" "Setting this to 0 means there is no maximum width." msgstr "" msgid "Maximum Height:" msgstr "" msgid "Enable maximum height limit." msgstr "" msgid "" "This is the maximum height that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source height is greater.\n" "Setting this to 0 means there is no maximum height." msgstr "" msgid "Select preview frames." msgstr "" msgid "" "Encode and play a short sequence of video starting from the current preview " "position." msgstr "" msgid "Duration:" msgstr "" msgid "Set the duration of the live preview in seconds." msgstr "" msgid "Show Crop" msgstr "" msgid "Show Cropped area of the preview" msgstr "" msgid "Fullscreen" msgstr "" msgid "View Fullscreen Preview" msgstr "" msgid "Title Number:" msgstr "" msgid "Detected DVD devices:" msgstr "" msgid "Setting:" msgstr "" msgid "Import SRT" msgstr "" msgid "Enable settings to import an SRT subtitle file" msgstr "" msgid "Embedded Subtitle List" msgstr "" msgid "Enable settings to select embedded subtitles" msgstr "" msgid "Character Code" msgstr "" msgid "Offset (ms)" msgstr "" msgid "" "Set the language of this subtitle.\n" "This value will be used by players in subtitle menus." msgstr "" msgid "" "Set the character code used by the SRT file you are importing.\n" "\n" "SRTs come in all flavours of character sets.\n" "We translate the character set to UTF-8.\n" "The source's character code is needed in order to perform this translation." msgstr "" msgid "Select the SRT file to import." msgstr "" msgid "Srt File" msgstr "" msgid "Adjust the offset in milliseconds between video and SRT timestamps" msgstr "" msgid "Track" msgstr "" msgid "List of subtitle tracks available from your source." msgstr "" msgid "Forced Subtitles Only" msgstr "" msgid "Burn into video" msgstr "" msgid "" "Render the subtitle over the video.\n" "The subtitle will be part of the video and can not be disabled." msgstr "" msgid "Set Default Track" msgstr "" msgid "Source Track" msgstr "" msgid "List of audio tracks available from your source." msgstr "" msgid "Track Name:" msgstr "" msgid "" "Set the audio track name.\n" "\n" "Players may use this in the audio selection list." msgstr "" msgid "Mix" msgstr "" msgid "Sample Rate" msgstr "" msgid "" "Dynamic Range Compression: Adjust the dynamic range of the output audio track.\n" "\n" "For source audio that has a wide dynamic range (very loud and very soft sequences),\n" "DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder." msgstr "" msgid "Enable bitrate setting" msgstr "" msgid "Enable quality setting" msgstr "" msgid "" "Quality: For output codec's that support it, adjust the quality of " "the output." msgstr "" msgid "00.0" msgstr "" msgid "" "Audio Gain: Adjust the amplification or attenuation of the output " "audio track." msgstr "" msgid "Skip This Version" msgstr "" msgid "Remind Me Later" msgstr "" msgid "A new version of HandBrake is available!" msgstr "" msgid "HandBrake xxx is now available (you have yyy)." msgstr "" msgid "Release Notes" msgstr "" msgid "First Track Matching Selected Languages" msgstr "" msgid "All Tracks Matching Selected Languages" msgstr "" msgid "Chapters:" msgstr "" msgid "Seconds:" msgstr "" msgid "Frames:" msgstr "" msgid "Do Nothing" msgstr "" msgid "Show Notification" msgstr "" msgid "Quit Handbrake" msgstr "" msgid "Put Computer To Sleep" msgstr "" msgid "Shutdown Computer" msgstr "" msgid "Week" msgstr "" msgid "Month" msgstr "" msgid "Year" msgstr "" msgid "Immortal" msgstr "" msgid "Never" msgstr "" msgid "Daily" msgstr "" msgid "Weekly" msgstr "" msgid "Monthly" msgstr "" msgid "Default" msgstr "" msgid "Fast" msgstr "" msgid "Slow" msgstr "" msgid "Slower" msgstr "" msgid "Ultralight" msgstr "" msgid "Light" msgstr "" msgid "Medium" msgstr "" msgid "Strong" msgstr "" msgid "Film" msgstr "" msgid "Grain" msgstr "" msgid "High Motion" msgstr "" msgid "Animation" msgstr "" msgid "Spatial" msgstr "" msgid "Temporal" msgstr "" msgid "Automatic" msgstr "" msgid "Optimal" msgstr "" msgid "Normal" msgstr "" msgid "Simple" msgstr "" msgid "Smart" msgstr "" msgid "Diamond" msgstr "" msgid "Hexagon" msgstr "" msgid "Uneven Multi-Hexagon" msgstr "" msgid "Exhaustive" msgstr "" msgid "Hadamard Exhaustive" msgstr "" msgid "Most" msgstr "" msgid "Some" msgstr "" msgid "All" msgstr "" msgid "Encode only" msgstr "" msgid "Always" msgstr "" msgid "(NTSC Film)" msgstr "" msgid "(PAL Film/Video)" msgstr "" msgid "(NTSC Video)" msgstr "" msgid "%d - %02dh%02dm%02ds - %s" msgstr "" msgid "%d (%05d.MPLS) - %02dh%02dm%02ds" msgstr "" msgid "%d (%05d.MPLS) - Unknown Length" msgstr "" msgid "%d - %02dh%02dm%02ds" msgstr "" msgid "%d - Unknown Length" msgstr "" msgid "No Titles" msgstr "" msgid "No Audio" msgstr "" msgid "Foreign Audio Search" msgstr "" #, c-format msgid "Chapter %2d" msgstr "" #, c-format msgid "N/A" msgstr "" #, c-format msgid "" "Invalid Deinterlace Settings:\n" "\n" "%s\n" msgstr "" #, c-format msgid "" "Invalid Detelecine Settings:\n" "\n" "%s\n" msgstr "" #, c-format msgid "" "Invalid Decomb Settings:\n" "\n" "%s\n" msgstr "" msgid "" "Theora is not supported in the MP4 container.\n" "\n" "You should choose a different video codec or container.\n" "If you continue, FFMPEG will be chosen for you." msgstr "" msgid "Continue" msgstr "" msgid "No title found.\n" msgstr "" msgid "" "Only one subtitle may be burned into the video.\n" "\n" "You should change your subtitle selections.\n" "If you continue, some subtitles will be lost." msgstr "" msgid "" "Srt file does not exist or not a regular file.\n" "\n" "You should choose a valid file.\n" "If you continue, this subtitle will be ignored." msgstr "" msgid "" "The source does not support Pass-Thru.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "%s is not supported in the %s container.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "The source audio does not support %s mixdown.\n" "\n" "You should choose a different mixdown.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "Unable to create %s.\n" "\n" "Internal error. Could not parse UI description.\n" "%s" msgstr "" msgid "Index" msgstr "" msgid "Duration" msgstr "" msgid "Title" msgstr "" msgid "Job Information" msgstr "" msgid "Track Information" msgstr "" msgid "Preset Name" msgstr "" msgid "The device or file to encode" msgstr "" msgid "The preset values to use for encoding" msgstr "" msgid "Spam a lot" msgstr "" msgid "- Transcode media formats" msgstr "" msgid "Globals" msgstr "" msgid "Presets" msgstr "" msgid "Folder" msgstr "" #, c-format msgid "%s path: (%s)" msgstr "" #, c-format msgid "%s indices: len %d" msgstr "" msgid "Type" msgstr "" msgid "Failed to find parent folder when adding child." msgstr "" msgid "Failed to find parent folder while adding child." msgstr "" #, c-format msgid "Can't map language value: (%s)" msgstr "" #, c-format msgid "Can't map value: (%s)" msgstr "" msgid "Subtitles" msgstr "" msgid "" "%s: Folder already exists.\n" "You can not replace it with a preset." msgstr "" msgid "" "%s: Preset already exists.\n" "You can not replace it with a folder." msgstr "" msgid "Import Preset" msgstr "" msgid "All (*)" msgstr "" msgid "Presets (*.plist)" msgstr "" msgid "Export Preset" msgstr "" msgid "" "Confirm deletion of %s:\n" "\n" "%s" msgstr "" msgid "folder" msgstr "" msgid "preset" msgstr "" msgid "No selection??? Perhaps unselected." msgstr "" #, c-format msgid "Gstreamer Error: %s" msgstr "" #, c-format msgid "" "Missing GStreamer plugin\n" "Audio or Video may not play as expected\n" "\n" "%s" msgstr "" msgid "Done" msgstr "" msgid "Windowed" msgstr "" msgid "Seconds" msgstr "" msgid "Frames" msgstr "" #, c-format msgid "" "%s (Title %d, %s %d through %d, 2 Video Passes) -->" " %s" msgstr "" #, c-format msgid "" "%s (Title %d, %s %d through %d) --> %s" msgstr "" #, c-format msgid "Modified Preset Based On: %s\n" msgstr "" #, c-format msgid "Preset: %s\n" msgstr "" #, c-format msgid "Format: %s Container\n" msgstr "" msgid "Container Options:" msgstr "" #, c-format msgid "%sChapter Markers" msgstr "" #, c-format msgid "%siPod 5G Support" msgstr "" #, c-format msgid "%sWeb Optimized" msgstr "" #, c-format msgid "%sLarge File Size (>4GB)" msgstr "" #, c-format msgid "Destination: %s\n" msgstr "" msgid "(Aspect Preserved)" msgstr "" msgid "(Aspect Lost)" msgstr "" msgid "(Anamorphic)" msgstr "" msgid "(Custom Anamorphic)" msgstr "" msgid "Picture: " msgstr "" #, c-format msgid "Source: %d x %d, Output %d x %d %s, Crop %d:%d:%d:%d" msgstr "" #, c-format msgid ", Display %d x %d" msgstr "" msgid "Filters:" msgstr "" #, c-format msgid "%sDetelecine" msgstr "" #, c-format msgid "%sDecomb" msgstr "" #, c-format msgid "%sDeinterlace" msgstr "" #, c-format msgid "%sDenoise Filter %s:" msgstr "" #, c-format msgid "%sDeblock: %d" msgstr "" #, c-format msgid "%sGrayscale" msgstr "" #, c-format msgid "Video: %s" msgstr "" #, c-format msgid ", Framerate: %s %s" msgstr "" #, c-format msgid ", Framerate: Peak %s (may be lower)" msgstr "" #, c-format msgid ", Framerate: %s (constant frame rate)" msgstr "" msgid "Error" msgstr "" msgid "Bitrate:" msgstr "" msgid "Bitrate" msgstr "" msgid "Quality" msgstr "" msgid "Turbo 1st Pass: On\n" msgstr "" #, c-format msgid "Video Options: Preset: %s" msgstr "" msgid " - Tune: " msgstr "" #, c-format msgid " - Profile: %s" msgstr "" #, c-format msgid " - Level: %s" msgstr "" #, c-format msgid "Advanced Options: %s\n" msgstr "" msgid "Audio: " msgstr "" #, c-format msgid "Audio Tracks: %d" msgstr "" #, c-format msgid "Bitrate: %d" msgstr "" #, c-format msgid "%s --> Encoder: %s" msgstr "" #, c-format msgid "%s --> Encoder: %s, Mixdown: %s, SampleRate: %s, %s" msgstr "" msgid "Subtitle: " msgstr "" #, c-format msgid "Subtitle Tracks: %d\n" msgstr "" msgid " (Force)" msgstr "" msgid " (Burn)" msgstr "" msgid " (Default)" msgstr "" #, c-format msgid " %s (%s), %s, Offset (ms) %d%s\n" msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "Another queued job has specified the same destination.\n" "Do you want to overwrite?" msgstr "" msgid "Overwrite" msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "This is not a valid directory." msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "Can not read or write the directory." msgstr "" #, c-format msgid "" "Destination filesystem is almost full: %uM free\n" "\n" "Encode may be incomplete if you proceed.\n" msgstr "" msgid "Proceed" msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "File already exists.\n" "Do you want to overwrite?" msgstr "" msgid "" "Duplicate destination files detected.\n" "Duplicates will not be added to the queue." msgstr "" msgid "No Title" msgstr "" msgid "" "There is another title with the same destination file name.\n" "This title will not be added to the queue unless you change\n" "the output file name.\n" msgstr "" msgid "Stop" msgstr "" msgid "Stop Encoding" msgstr "" msgid "Resume" msgstr "" msgid "Resume Encoding" msgstr "" msgid "S_top Queue" msgstr "" msgid "_Start Queue" msgstr "" msgid "_Resume Queue" msgstr "" msgid "Resume Queue" msgstr "" msgid "" "You are currently encoding. What would you like to do?\n" "\n" msgstr "" #, c-format msgid "" "You have %d unfinished job(s) in a saved queue.\n" "\n" "Would you like to reload them?" msgstr "" msgid "No" msgstr "" msgid "Yes" msgstr "" #, c-format msgid "Usage: %s infile [outfile]\n" msgstr "" #, c-format msgid "Offset: %dms" msgstr "" msgid "Burned Into Video" msgstr "" msgid "Passthrough" msgstr "" msgid "through" msgstr "" msgid "(Forced Subtitles Only)" msgstr "" msgid "(Default)" msgstr "" msgid "Error!" msgstr "" #, c-format msgid "Preferred Language: %s" msgstr "" #, c-format msgid "Add %s subtitle track if default audio is not %s" msgstr "" #, c-format msgid "Type %s" msgstr "" #, c-format msgid "Type %s value %s" msgstr "" #, c-format msgid "Type %s value %d" msgstr "" #, c-format msgid "Type %s value %" msgstr "" #, c-format msgid "Type %s value %f" msgstr "" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"%s\"" msgstr "" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"\"" msgstr "" msgid "Any" msgstr "" msgid "0: SAD, no subpel" msgstr "" msgid "4: SATD, qpel on all" msgstr "" msgid "5: SATD, multi-qpel on all" msgstr "" msgid "6: RD in I/P-frames" msgstr "" msgid "7: RD in all frames" msgstr "" msgid "8: RD refine in I/P-frames" msgstr "" msgid "9: RD refine in all frames" msgstr "" msgid "10: QPRD in all frames" msgstr "" msgid "11: No early terminations in analysis" msgstr "" msgid "Your names" msgstr "" msgid "Your emails" msgstr "" HandBrake-0.10.2/gtk/po/de.po0000664000175200017520000017646212466165306016223 0ustar handbrakehandbrake# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Ettore Atalan , 2014 # mod16 , 2014 msgid "" msgstr "" "Project-Id-Version: HandBrake-0.10\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-03 13:01+0300\n" "PO-Revision-Date: 2014-12-02 21:50+0000\n" "Last-Translator: Ettore Atalan \n" "Language-Team: German (http://www.transifex.com/projects/p/handbrake/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Quality: " msgstr "Qualität: " #, c-format msgid "Bitrate: %dkbps" msgstr "Bitrate: %dkbps" #, c-format msgid "Bitrate: %.4gkbps" msgstr "Bitrate: %.4gkbps" msgid "Passthrough" msgstr "Durchleiten" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s\n" "Track Name: %s" msgstr "%s\nVerstärkung: %s\nDRC: %s\nSpurname: %s" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s" msgstr "%s\nVerstärkung: %s\nDRC: %s" msgid "Add" msgstr "Hinzufügen" msgid "" "Add an audio encoder.\n" "Each selected source track will be encoded with all selected encoders." msgstr "Fügen Sie einen Audiokodierer hinzu.\nJede ausgewählte Quellspur wird mit allen ausgewählten Kodierern kodiert." msgid "Set the audio codec to encode this track with." msgstr "Legen Sie den Audiocodec fest, mit dem diese Spur kodiert wird." msgid "Set the bitrate to encode this track with." msgstr "Legen Sie die Bitrate fest, mit der diese Spur kodiert wird." msgid "" "Audio Quality:\n" "For encoders that support it, adjust the quality of the output." msgstr "" msgid "Set the mixdown of the output audio track." msgstr "Legen Sie die Abmischung der Ausgabeaudiospur fest." msgid "Set the sample rate of the output audio track." msgstr "Legen Sie die Abtastrate der Ausgabeaudiospur fest." msgid "" "Audio Gain:\n" "Adjust the amplification or attenuation of the output audio track." msgstr "" msgid "0dB" msgstr "0dB" msgid "%ddB" msgstr "%ddB" msgid "%.4gkHz" msgstr "%.4gkHz" msgid "%d - %s (%.4gkHz)" msgstr "%d - %s (%.4gkHz)" msgid "" "Dynamic Range Compression:\n" "Adjust the dynamic range of the output audio track.\n" "For source audio that has a wide dynamic range,\n" "very loud and very soft sequences, DRC allows you\n" "to 'compress' the range by making loud sounds\n" "softer and soft sounds louder.\n" msgstr "" msgid "Remove this audio encoder" msgstr "Diesen Audiokodierer entfernen" msgid "Closing HandBrake will terminate encoding.\n" msgstr "Das Schließen von HandBrake wird die Kodierung abbrechen.\n" msgid "No Title Found" msgstr "Kein Titel gefunden" msgid "none" msgstr "kein" msgid "Not Selected" msgstr "Nicht ausgewählt" msgid "zerolatency x264 tune selected, forcing constant framerate" msgstr "" msgid "Scanning ..." msgstr "Absuchen..." msgid "Stop Scan" msgstr "Absuchen stoppen" msgid "On" msgstr "Ein" msgid "Strict" msgstr "Genau" msgid "Loose" msgstr "Frei" msgid "Custom" msgstr "Benutzerdefiniert" msgid "Unknown" msgstr "Unbekannt" msgid "auto" msgstr "automatisch" #, c-format msgid "" "%s\n" "\n" "%s in %d seconds ..." msgstr "%s\n\n%s in %d Sekunden ..." #, c-format msgid "%sYour movie will be lost if you don't continue encoding." msgstr "%sIhr Film wird verloren gehen, wenn Sie die Kodierung nicht fortsetzen." msgid "Cancel Current and Stop" msgstr "Aktuelles abbrechen und anhalten" msgid "Cancel Current, Start Next" msgstr "Aktuelles abbrechen, nächstes starten" msgid "Finish Current, then Stop" msgstr "Aktuelles fertigstellen, anschließend anhalten" msgid "Continue Encoding" msgstr "Kodierung fortsetzen" msgid "Custom " msgstr "Benutzerdefiniert" msgid "Modified " msgstr "Geändert" #, c-format msgid "Handbrake Version: %s (%d)\n" msgstr "Handbrake-Version: %s (%d)\n" #, c-format msgid "%d encode(s) pending" msgstr "%d Kodierung(en) ausstehend" #, c-format msgid "job %d of %d, " msgstr "Aufgabe %d von %d, " #, c-format msgid "pass %d (subtitle scan) of %d, " msgstr "Durchgang %d (Untertitelabtastung) von %d, " #, c-format msgid "pass %d of %d, " msgstr "Durchgang %d von %d, " #, c-format msgid "Encoding: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" msgstr "Kodieren: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %% (ETA %02dh%02dm%02ds)" msgstr "Kodieren: %s%s%.2f %% (ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %%" msgstr "Kodieren: %s%s%.2f %%" msgid "Searching for start time, " msgstr "Suche nach der Startzeit, " msgid "Scanning..." msgstr "Absuchen..." #, c-format msgid "Scanning title %d of %d..." msgstr "Titel %d von %d wird abgetastet..." #, c-format msgid "Scanning title %d of %d preview %d..." msgstr "Titel %d von %d Vorschau %d wird abgetastet..." msgid "Source" msgstr "Quelle" msgid "Choose Video Source" msgstr "Quellvideo auswählen" msgid "Paused" msgstr "Pausiert" msgid "Encode Done!" msgstr "Kodieren fertig!" msgid "Encode Canceled." msgstr "Kodieren abgebrochen." msgid "Encode Failed." msgstr "Kodieren fehlgeschlagen." msgid "Muxing: This may take a while..." msgstr "Multiplexen: Dies kann eine Weile dauern..." msgid "Scan this DVD source" msgstr "Diese DVD-Quelle absuchen" msgid "AutoScan" msgstr "Automatisches Absuchen" msgid "Suspend" msgstr "Unterbrechen" #, c-format msgid "Suspend failed: %s" msgstr "Unterbrechen fehlgeschlagen: %s" msgid "Suspend failed" msgstr "Unterbrechen fehlgeschlagen" msgid "Shutdown" msgstr "Herunterfahren" #, c-format msgid "Shutdown failed: %s" msgstr "Herunterfahren fehlgeschlagen: %s" msgid "Shutdown failed" msgstr "Herunterfahren fehlgeschlagen" msgid "Encoding" msgstr "Kodieren" #, c-format msgid "press %d %d" msgstr "drücken Sie %d %d" #, c-format msgid "" "Invalid Settings:\n" "%s" msgstr "Ungültige Einstellungen:\n%s" msgid "Cancel" msgstr "Abbrechen" #, c-format msgid "%s: %.4g (Warning: lossless)" msgstr "%s: %.4g (Warnung: verlustfrei)" #, c-format msgid "HandBrake %s/%s is now available (you have %s/%d)." msgstr "HandBrake %s/%s ist jetzt verfügbar (Sie haben %s/%d)." msgid "Encode Complete" msgstr "Kodieren abgeschlossen" msgid "Put down that cocktail, Your HandBrake queue is done!" msgstr "Stellen Sie Ihren Cocktail ab, Ihre HandBrake-Warteschlange ist fertig!" msgid "Your encode is complete." msgstr "Ihre Kodierung ist abgeschlossen." msgid "Shutting down the computer" msgstr "Der Computer wird heruntergefahren" msgid "Putting computer to sleep" msgstr "Computer wird in den Ruhezustand versetzt" msgid "Quiting Handbrake" msgstr "Handbrake wird beendet" msgid "Bottom" msgstr "Unterseite" #, c-format msgid "open failed: %s\n" msgstr "Öffnen fehlgeschlagen: %s\n" msgid "No key for dictionary item" msgstr "Kein Schlüssel für Wörterbuchelement" msgid "Invalid container type. This shouldn't happen" msgstr "Ungültiger Containertyp. Das sollte nicht passieren" #, c-format msgid "%s:missing a requried attribute" msgstr "%s:ein erforderliches Attribut fehlt" #, c-format msgid "" "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" msgstr "Aufruf: %s [-I ] \nZusammenfassung:\n Erstellt eine Ressource plist aus einer Ressourcenliste\nOptionen:\n I - Include-Pfad, um nach Dateien zu suchen\n Eingaberessourcendatei\n Ausgaberessourcen-plist-datei\n" msgid "language" msgstr "Sprache" msgid "Language" msgstr "Sprache" msgid "" "The language this text is in, as an ISO code. Pango can use this as a hint " "when rendering the text. If you don't understand this parameter, you " "probably don't need it" msgstr "" msgid "" "The preferred place to ellipsize the string, if the cell renderer does not " "have enough room to display the entire string" msgstr "" msgid "" "How to break the string into multiple lines, if the cell renderer does not " "have enough room to display the entire string" msgstr "" msgid "" "Render the subtitle over the video.\n" "\n" "The subtitle will be part of the video and can not be disabled." msgstr "Untertitel über das Video legen.\n\nDie Untertitel werden dadurch Teil des Videobilds und können nicht abgeschaltet werden." msgid "Burned In" msgstr "Eingebrannt" msgid "" "Set the default output subtitle track.\n" "\n" "Most players will automatically display this\n" "subtitle track whenever the video is played.\n" "\n" "This is useful for creating a \"forced\" track\n" "in your output." msgstr "" msgid "Default" msgstr "Standard" msgid "" "Use only subtitles that have been flagged\n" "as forced in the source subtitle track\n" "\n" "\"Forced\" subtitles are usually used to show\n" "subtitles during scenes where someone is speaking\n" "a foreign language." msgstr "" msgid "Forced Only" msgstr "Nur Erzwungene>" msgid "" "Add (or subtract) an offset (in milliseconds)\n" "to the start of the SRT subtitle track.\n" "\n" "Often, the start of an external SRT file\n" "does not coincide with the start of the video.\n" "This setting allows you to synchronize the files." msgstr "" msgid "SRT Offset" msgstr "SRT-Versatz" msgid "Off" msgstr "Aus" msgid "" "The source subtitle track\n" "\n" "You can choose any of the subtitles\n" "recognized in your source file.\n" "\n" "In addition, there is a special track option\n" "\"Foreign Audio Search\". This option will add\n" "an extra pass to the encode that searches for\n" "subtitles that may correspond to a foreign\n" "language scene. This option is best used in\n" "conjunction with the \"Forced\" option." msgstr "" msgid "Track" msgstr "Spur" msgid "About HandBrake" msgstr "Über HandBrake" msgid "" "Copyright © 2008 - 2013 John Stebbins\n" "Copyright © 2004 - 2013, HandBrake Devs" msgstr "Copyright © 2008 - 2013 John Stebbins\nCopyright © 2004 - 2013, HandBrake-Entwickler" msgid "" "HandBrake is a GPL-licensed, multiplatform, multithreaded video transcoder." msgstr "HandBrake ist ein GPL-lizenzierter, plattformübergreifender, mehrprozessgestützter Videotranskodierer." msgid "http://handbrake.fr" msgstr "http://handbrake.fr" msgid "" "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n" "\n" "HandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgstr "" msgid "_Minimize/Maximize" msgstr "_Minimieren/Maximieren" msgid "_Pause Queue" msgstr "Warteschlange _pausieren" msgid "_Quit" msgstr "_Beenden" msgid "_About" msgstr "_Über" msgid "HandBrake" msgstr "HandBrake" msgid "_File" msgstr "_Datei" msgid "_Source" msgstr "_Quelle" msgid "Single _Title" msgstr "Einzelner _Titel" msgid "_Destination" msgstr "_Ziel" msgid "_Preferences" msgstr "_Einstellungen" msgid "_Queue" msgstr "_Warteschlange" msgid "_Add" msgstr "_HInzufügen" msgid "Add _Multiple" msgstr "_Mehrere hinzufügen" msgid "_Start" msgstr "_Start" msgid "_Pause" msgstr "_Pause" msgid "_View" msgstr "_Ansicht" msgid "HandBrake For _Dumbies" msgstr "HandBrake für _Dumbies" msgid "_Show Presets" msgstr "Voreinstellungen an_zeigen" msgid "_Preview" msgstr "_Vorschau" msgid "_Activity Window" msgstr "_Aktivitätsfenster" msgid "Show _Queue" msgstr "_Warteschlange anzeigen" msgid "_Presets" msgstr "_Voreinstellungen" msgid "_Save" msgstr "_Speichern" msgid "_Delete" msgstr "_Löschen" msgid "_Make Default" msgstr "Als _Standard festlegen" msgid "_New Folder" msgstr "_Neuer Ordner" msgid "_Export" msgstr "_Exportieren" msgid "_Import" msgstr "_Importieren" msgid "_Update Built-in Presets" msgstr "Integrierte Voreinstellungen akt_ualisieren" msgid "_Help" msgstr "_Hilfe" msgid "_Guide" msgstr "An_leitung" msgid "Start Encoding" msgstr "Kodierung starten" msgid "Start" msgstr "Start" msgid "Pause Encoding" msgstr "Kodierung pausieren" msgid "Pause" msgstr "Pause" msgid "Add to Queue" msgstr "In Warteschlange einreihen" msgid "Enqueue" msgstr "Hinzufügen" msgid "Show Queue" msgstr "Warteschlange anzeigen" msgid "Queue" msgstr "Warteschlange" msgid "" "Open Picture Settings and Preview window.\n" "Here you can adjust cropping, resolution, aspect ratio, and filters." msgstr "" msgid "Preview" msgstr "Vorschau" msgid "Show Activity Window" msgstr "Aktivitätslog in eigenem Fenster anzeigen" msgid "Activity" msgstr "Aktivität" msgid "Source:" msgstr "Quelle:" msgid "None" msgstr "Kein" msgid "Title:" msgstr "Titel:" msgid "" "Set the title to encode.\n" "By default the longest title is chosen.\n" "This is often the feature title of a DVD." msgstr "" msgid "Angle:" msgstr "Perspektive:" msgid "For multi-angle DVD's, select the desired angle to encode." msgstr "" msgid "Reset All Titles" msgstr "Alle Titel zurücksetzen" msgid "Apply current settings to all titles" msgstr "Aktuelle Einstellungen auf alle Titel anwenden" msgid "Range of title to encode. Can be chapters, seconds, or frames." msgstr "" msgid "Set the first chapter to encode." msgstr "Legen Sie das erste Kapitel zum Kodieren fest." msgid "Set the last chapter to encode." msgstr "Legen Sie das letzte Kapitel zum Kodieren fest." msgid "Duration:" msgstr "Laufzeit:" msgid "Destination" msgstr "Ziel" msgid "File:" msgstr "Datei:" msgid "Destination filename for your encode." msgstr "Zieldateiname für Ihre kodieren Dateien." msgid "Destination directory for your encode." msgstr "Zielverzeichnis für Ihre kodieren Dateien." msgid "Destination Directory" msgstr "Zielverzeichnis" msgid "Format:" msgstr "Format:" msgid "Format to mux encoded tracks to." msgstr "Format, in das kodierte Spuren gemultiplext werden." msgid "iPod 5G Support" msgstr "iPod 5G-Unterstützung" msgid "Add iPod Atom needed by some older iPods." msgstr "Das von einigen älteren iPods benötigte iPod Atom hinzufügen." msgid "Web optimized" msgstr "weboptimiert" msgid "" "Optimize the layout of the MP4 file for progressive download.\n" "This allows a player to initiate playback before downloading the entire file." msgstr "" msgid "Large file (>4GB)" msgstr "Große Datei (>4GB)" msgid "" "Allow 64 bit MP4 file which can be over 4GB.\n" "\n" "Caution: This option may break device compatibility." msgstr "64-Bit-MP4-Datei, welche größer als 4 GB sein kann, erlauben.\n\nVorsicht: Diese Option kann die Gerätekompatibilität ruinieren." msgid "Presets List" msgstr "Voreinstellungsliste" msgid "Source Codec:" msgstr "Quellcodec:" msgid "Dimensions:" msgstr "Format:" msgid "Aspect: " msgstr "Seitenverhältnis:" msgid "Frame Rate:" msgstr "Bildwechselfrequenz:" msgid "Source Picture Parameters" msgstr "Quellbildparameter" msgid "Autocrop:" msgstr "Automatisch zuschneiden:" msgid "Crop:" msgstr "Zuschneiden" msgid "Crop Dimensions:" msgstr "Format zuschneiden:" msgid "Cropping" msgstr "Zuschneiden" msgid "Scale Dimensions:" msgstr "Format skalieren:" msgid "Optimal for Source:" msgstr "Optimal für Quelle:" msgid "Anamorphic:" msgstr "Anamorph:" msgid "Scaling" msgstr "Skalierung" msgid "Presentation Dimensions:" msgstr "Darstellungsformat:" msgid "Summary" msgstr "Zusammenfassung" msgid "Left Crop" msgstr "Links abschneiden" msgid "Top Crop" msgstr "Oben abschneiden" msgid "Bottom Crop" msgstr "Unten abschneiden" msgid "Right Crop" msgstr "Rechts abschneiden" msgid "Auto Crop" msgstr "Automatisch abschneiden" msgid "Automatically crop black borders around edges of the video." msgstr "Schwarze Ränder an den Kanten des Videos automatisch abschneiden." msgid "Loose Crop" msgstr "Freies Abschneiden" msgid "" "When picture settings require that the image\n" "dimensions be rounded to some multiple number\n" "of pixels, this setting will crop a few extra pixels\n" "instead of doing exact cropping and then scaling to\n" "the required multiple." msgstr "" msgid "width:" msgstr "Breite:" msgid "" "This is the width that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "" msgid "height:" msgstr "Höhe:" msgid "" "This is the height that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "" msgid "Optimal for source" msgstr "Optimal für Quelle" msgid "" "If enabled, select the 'optimal' storage resolution.\n" "This will be the resolution that most closely matches the source resolution after cropping." msgstr "" msgid "" "Anamorphic Modes:\n" "\n" "None - Force pixel aspect ratio to 1:1.\n" "Loose - Align dimensions to chosen 'Alignment' value\n" " and pick pixel aspect ratio that preserves the\n" " original display aspect ratio\n" "Strict - Keep original source dimensions and pixel\n" " aspect ratio" msgstr "" msgid "Alignment:" msgstr "Ausrichtung:" msgid "" "Align storage dimensions to multiples of this value.\n" "\n" "This setting is only necessary for compatibility with some devices.\n" "You should use 2 unless you experience compatibility issues." msgstr "" msgid "Storage Geometry" msgstr "Speichergeometrie" msgid "" "This is the display width. It is the result of scaling the storage " "dimensions by the pixel aspect." msgstr "Dies ist die Anzeigenbreite. Sie ist das Ergebnis der Skalierung der Speichermaße durch das Pixelseitenverhältnis." msgid "Pixel Aspect:" msgstr "Pixelseitenverhältnis:" msgid "" "Pixel aspect defines the shape of the pixels.\n" "\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "" msgid ":" msgstr ":" msgid "" "Pixel aspect defines the shape of the pixels.\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "" msgid "Keep Aspect" msgstr "Seitenverhältnis bewahren" msgid "" "If enabled, the original display aspect of the source will be maintained." msgstr "" msgid "Display Aspect:" msgstr "Bildschirmseitenverhältnis:" msgid "Display Geometry" msgstr "Bildschirmgeometrie" msgid "Grayscale" msgstr "Graustufen" msgid "If enabled, filter colour components out of video." msgstr "" msgid "Deblock:" msgstr "Entblocken:" msgid "" "The deblocking filter removes a common type of compression artifact.\n" "If your source exhibits 'blockiness', this filter may help clean it up." msgstr "" msgid "Denoise Filter:" msgstr "Entrauschen-Filter:" msgid "" "Denoise filtering reduces or removes the appearance of noise and grain.\n" "Film grain and other types of high frequency noise are difficult to compress.\n" "Using this filter on such sources can result in smaller file sizes." msgstr "" msgid "Denoise Preset:" msgstr "Entrauschen-Voreinstellung:" msgid "Denoise Tune:" msgstr "Entrauschen-Abstimmung:" msgid "" "Custom denoise filter string format\n" "\n" "SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgstr "Benutzerdefiniertes Entrauschen-Filter-Zeichenkettenformat\n\nSpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgid "Detelecine:" msgstr "" msgid "" "This filter removes 'combing' artifacts that are the result of telecining.\n" "\n" "Telecining is a process that adjusts film framerates that are 24fps to NTSC video frame rates which are 30fps." msgstr "" msgid "" "Custom detelecine filter string format\n" "\n" "JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgstr "" msgid "Decomb" msgstr "" msgid "" "Choose decomb or deinterlace filter options.\n" "\n" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced.\n" "\n" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "" msgid "Deinterlace" msgstr "Zeilenentflechtung" msgid "Decomb:" msgstr "" msgid "" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced." msgstr "" msgid "" "Custom decomb filter string format\n" "\n" "Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\n" "BlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\n" "ErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgstr "" msgid "Deinterlace:" msgstr "Zeilenentflechtung:" msgid "" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "Der klassische Zeilenentflechtungsfilter wird auf alle Bilder angewandt.\nNicht verflochtene Bilder werden einen Qualitätsverlust erleiden." msgid "" "Custom deinterlace filter string format\n" "\n" "YadifMode:YadifParity:McdintMode:McdeintQp" msgstr "Benutzerdefiniertes Zeilenentflechtungsfilter-Zeichenkettenformat\n\nYadifMode:YadifParity:McdintMode:McdeintQp" msgid "Filters" msgstr "Filter" msgid "Picture" msgstr "Bild" msgid "Video Encoder:" msgstr "Videokodierer:" msgid "Available video encoders." msgstr "Verfügbare Videokodierer." msgid "Framerate:" msgstr "Bildwechselfrequenz::" msgid "" "Output framerate.\n" "\n" "'Same as source' is recommended. If your source video has\n" "a variable framerate, 'Same as source' will preserve it." msgstr "Ausgabebildwechselfrequenz.\n\n\"Gleiche wie Quelle\" wird hier empfohlen. Eine variable Framerate im Quellvideo bleibt bei \"Gleiche wie Quelle\" erhalten." msgid "Constant Framerate" msgstr "Konstante Bildwechselfrequenz" msgid "Same as source" msgstr "Wie Quelle" msgid "kbps" msgstr "kbit/s" msgid "(variable)" msgstr "(variabel)" msgid "(constant)" msgstr "(konstant)" msgid "Enables constant framerate output." msgstr "Aktiviert eine konstante Bildwechselfrequenzausgabe." msgid "Peak Framerate (VFR)" msgstr "Bildwechselfrequenz-Spitzenwert (VFR)" msgid "" "Enables variable framerate output with a peak\n" "rate determined by the framerate setting.\n" "\n" "VFR is not compatible with some players." msgstr "" msgid "Variable Framerate" msgstr "Variable Bildwechselfrequenz" msgid "" "Enables variable framerate output.\n" "\n" "VFR is not compatible with some players." msgstr "Aktiviert eine variable Bildwechselfrequenzausgabe.\n\nVFR ist mit manchen Abspielern nicht kompatibel." msgid "" "Set the desired quality factor.\n" "The encoder targets a certain quality.\n" "The scale used by each video encoder is different.\n" "\n" "x264's scale is logarithmic and lower values correspond to higher quality.\n" "So small decreases in value will result in progressively larger increases\n" "in the resulting file size. A value of 0 means lossless and will result\n" "in a file size that is larger than the original source, unless the source\n" "was also lossless.\n" "\n" "FFMpeg's and Theora's scale is more linear.\n" "These encoders do not have a lossless mode." msgstr "" msgid "Constant Quality:" msgstr "Konstante Qualität:" msgid "Bitrate (kbps): " msgstr "Bitrate (kbps): " msgid "" "Set the average bitrate.\n" "\n" "The instantaneous bitrate can be much higher or lower at any point in time.\n" "But the average over a long duration will be the value set here. If you need\n" "to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings." msgstr "" msgid "2-Pass Encoding" msgstr "Kodieren in 2 Durchgängen" msgid "" "Perform 2 Pass Encoding.\n" "\n" "The 'Bitrate' option is prerequisite. During the 1st pass, statistics about\n" "the video are collected. Then in the second pass, those statistics are used\n" "to make bitrate allocation decisions." msgstr "" msgid "Turbo First Pass" msgstr "" msgid "" "During the 1st pass of a 2 pass encode, use settings that speed things " "along." msgstr "" msgid "Use Advanced Options" msgstr "Erweiterte Optionen verwenden" msgid "" "Use advanced options Tab for x264 settings.\n" "\n" "Use at your own risk!" msgstr "" msgid "Preset:" msgstr "Voreinstellung:" msgid "" "Adjusts encoder settings to trade off compression efficiency against encoding speed.\n" "\n" "This establishes your default encoder settings.\n" "Tunes, profiles, levels and advanced option string will be applied to this.\n" "You should generally set this option to the slowest you can bear since slower\n" "settings will result in better quality or smaller files." msgstr "" msgid "Tune:" msgstr "Abstimmung:" msgid "" "Tune settings to optimize for common scenarios.\n" "\n" "This can improve effeciency for particular source characteristics or set\n" "characteristics of the output file. Changes will be applied after the\n" "preset but before all other parameters." msgstr "" msgid "Fast Decode" msgstr "Schnelles Dekodieren" msgid "" "Reduce decoder CPU usage.\n" "\n" "Set this if your device is struggling to play the output (dropped frames)." msgstr "" msgid "Zero Latency" msgstr "Null Latenz" msgid "" "Minimize latency between input to encoder and output of decoder.\n" "\n" "This is useful for broadcast of live streams.\n" "\n" "Since HandBrake is not suitable for live stream broadcast purposes,\n" "this setting is of little value here." msgstr "" msgid "Profile:" msgstr "Profil:" msgid "" "Sets and ensures compliance with the specified profile.\n" "\n" "Overrides all other settings." msgstr "" msgid "Level:" msgstr "Stufe:" msgid "" "Sets and ensures compliance with the specified level.\n" "\n" "Overrides all other settings." msgstr "" msgid "More Settings:" msgstr "Weitere Einstellungen:" msgid "" "Additional encoder settings.\n" "\n" "Colon separated list of encoder options." msgstr "Zusätzliche Kodierereinstellungen.\n\nDurch Doppelpunkte getrennte Liste von Kodiereroptionen." msgid "Video" msgstr "Video" msgid "Selection Behavior:" msgstr "Auswahlverhalten:" msgid "Remove" msgstr "Entfernen" msgid "Available Languages" msgstr "Verfügbare Sprachen" msgid "Selected Languages" msgstr "Ausgewählte Sprachen" msgid "Use only first encoder for secondary audio" msgstr "Nur ersten Kodierer für sekundäres Audio verwenden" msgid "" "Only the primary audio track will be encoded with the full encoder list.\n" "All other secondary audio output tracks will be encoded with first encoder only." msgstr "" msgid "Auto Passthru:" msgstr "Automatisches Durchleiten:" msgid "MP3" msgstr "MP3" msgid "" "Enable this if your playback device supports MP3.\n" "This permits MP3 passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports AAC.\n" "This permits AAC passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports AC-3.\n" "This permits AC-3 passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports DTS.\n" "This permits DTS passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports DTS-HD.\n" "This permits DTS-HD passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "Passthru Fallback:" msgstr "Durchleiten-Reservesystem:" msgid "" "Set the audio codec to encode with when a suitable track can not be found " "for audio passthru." msgstr "" msgid "Audio Encoder Settings:" msgstr "Audiokodierereinstellungen:" msgid "Each selected source track will be encoded with all selected encoders" msgstr "" msgid "Encoder" msgstr "Kodierer" msgid "Bitrate/Quality" msgstr "Bitrate/Qualität" msgid "Mixdown" msgstr "Abmischung" msgid "Samplerate" msgstr "Abtastrate" msgid "Gain" msgstr "Verstärkung" msgid "Audio Defaults" msgstr "Audiostandardeinstellungen" msgid "Add new audio settings to the list" msgstr "Neue Audioeinstellungen zur Liste hinzufügen" msgid "Add All" msgstr "Alle hinzufügen" msgid "Add all audio tracks to the list" msgstr "Alle Audiostücke zur Liste hinzufügen" msgid "Reload Defaults" msgstr "Standardeinstellungen erneut laden" msgid "Reload all audio settings from defaults" msgstr "Alle Audioeinstellungen aus den Standardeinstellungen neu laden" msgid "Audio List" msgstr "Audioliste" msgid "Preferred Language: None" msgstr "Bevorzugte Sprache: Keine" msgid "Add Foreign Audio Search Pass" msgstr "" msgid "" "Add \"Foreign Audio Search\" when the default audio track is your preferred language.\n" "This search pass finds short sequences of foreign audio and provides subtitles for them." msgstr "" msgid "Add subtitle track if default audio is foreign" msgstr "Untertitelspur hinzufügen, wenn das voreingestellte Audio in einer fremden Sprache ist" msgid "" "When the default audio track is not your preferred language, add a subtitle " "track." msgstr "Eine Untertitelspur hinzufügen, wenn die Standardaudiospur nicht in Ihrer bevorzugten Sprache ist." msgid "Add Closed Captions when available" msgstr "Wenn verfügbar, geschlossene Untertitel hinzufügen" msgid "" "Closed captions are text subtitles that can be added to any container as a " "soft subtitle track (not burned)" msgstr "" msgid "Subtitle Defaults" msgstr "Untertitelstandardeinstellungen" msgid "Add new subtitle settings to the list" msgstr "Neue Untertiteleinstellungen zur Liste hinzufügen" msgid "Add all subtitle tracks to the list" msgstr "Alle Untertitelspuren zur Liste hinzufügen" msgid "Reload all subtitle settings from defaults" msgstr "Alle Untertiteleinstellungen aus den Standardeinstellungen neu laden" msgid "Subtitle List" msgstr "Untertitelliste" msgid "Reference Frames:" msgstr "Referenzbilder:" msgid "" "Sane values are ~1-6. The more you add, the better the compression, but the slower the encode.\n" "Cel animation tends to benefit from more reference frames a lot more than film content.\n" "\n" "Note that many hardware devices have limitations on the number of supported reference\n" "frames, so if you're encoding for a handheld or standalone player, don't touch this unless\n" "you're absolutely sure you know what you're doing!" msgstr "" msgid "Maximum B-Frames:" msgstr "Größtmögliche B-Bilder:" msgid "" "Sane values are ~2-5. This specifies the maximum number of sequential B-frames that the encoder can use.\n" "\n" "Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.\n" "Cel-animated source material and B-pyramid also significantly increase the usefulness of larger\n" "values.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off)." msgstr "" msgid "Pyramidal B-Frames:" msgstr "Pyramidenförmige B-Bilder:" msgid "" "B-pyramid improves compression by creating a pyramidal structure (hence the name)\n" "of B-frames, allowing B-frames to reference each other to improve compression.\n" "\n" "Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit." msgstr "" msgid "Weighted P-Frames:" msgstr "Gewichtete P-Bilder:" msgid "" "Performs extra analysis to decide upon weighting parameters for each frame.\n" "\n" "This improves overall compression slightly and improves the quality of fades greatly.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires weighted P-frame\n" "prediction to be disabled. Note that some devices and players, even those that support\n" "Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is\n" "completely incompatible with it, for example." msgstr "" msgid "8x8 Transform" msgstr "8x8 Transformierung" msgid "" "The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.\n" "\n" "It improves compression by at least 5% at a very small speed cost and may\n" "provide an unusually high visual quality benefit compared to its compression\n" "gain. However, it requires High Profile, which many devices may not support." msgstr "" msgid "CABAC Entropy Encoding" msgstr "CABAC-Entropiekodierung" msgid "" "After the encoder has done its work, it has a bunch of data that\n" "needs to be compressed losslessly, similar to ZIP or RAR. H.264 provides\n" "two options for this: CAVLC and CABAC. CABAC decodes a lot slower but\n" "compresses significantly better (10-30%), especially at lower bitrates.\n" "\n" "If you're looking to minimize CPU requirements for video playback, disable this option.\n" "Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled." msgstr "" msgid "Encoding Features" msgstr "Kodierungsmerkmale" msgid "Motion Est. Method:" msgstr "Bewegungsabschätzungsverfahren:" msgid "" "Controls the motion estimation method.\n" "\n" "Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.\n" "A better motion search method improves compression at the cost of speed.\n" "\n" "Diamond: performs an extremely fast and simple search using a diamond pattern.\n" "Hexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n" "Uneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n" "Exhaustive: performs a \"dumb\" search of every pixel in a wide area. Significantly slower for only a small compression gain.\n" "Transformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement." msgstr "" msgid "Subpel ME & Mode:" msgstr "Teilbildpunkt-Bewegungsvorhersage & Modus:" msgid "" "This setting controls both subpixel-precision motion estimation and mode decision methods.\n" "\n" "Subpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n" "Mode decision is the method used to choose how to encode each block of the frame: a very important decision.\n" "SAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n" "6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n" "11 disables all early terminations in analysis.\n" "10 and 11, the most powerful and slowest options, require adaptive quantization (aq-mode > 0) and trellis 2 (always)." msgstr "" msgid "Motion Est. Range:" msgstr "Bewegungsabschätzungsreichweite:" msgid "" "This is the distance x264 searches from its initial guess at the\n" "motion of a block in order to try to find its actual motion.\n" "\n" "The default is fine for most content, but extremely high motion video,\n" "especially at HD resolutions, may benefit from higher ranges, albeit at\n" "a high speed cost." msgstr "" msgid "Adaptive Direct Mode:" msgstr "Adaptiver Direktmodus:" msgid "" "H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n" "\n" "Spatial, the default, is almost always better, but temporal is sometimes useful too.\n" "x264 can, at the cost of a small amount of speed (and accordingly for a small compression gain),\n" "adaptively select which is better for each particular frame." msgstr "" msgid "Adaptive B-Frames:" msgstr "Adaptive B-Bilder:" msgid "" "x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n" "\n" "Fast mode takes roughly the same amount of time no matter how many B-frames you specify.\n" "However, while fast, its decisions are often suboptimal.\n" "\n" "Optimal mode gets slower as the maximum number of B-Frames increases,\n" "but makes much more accurate decisions, especially when used with B-pyramid." msgstr "" msgid "Partitions:" msgstr "Teile:" msgid "" "Mode decision picks from a variety of options to make its decision:\n" "this option chooses what options those are.\n" "\n" "Fewer partitions to check means faster encoding, at the cost of worse\n" "decisions, since the best option might have been one that was turned off." msgstr "" msgid "Trellis:" msgstr "Trellis:" msgid "" "Trellis fine-tunes the rounding of transform coefficients to\n" "squeeze out 3-5% more compression at the cost of some speed.\n" "\n" "\"Always\" uses trellis not only during the main encoding process, but also\n" "during analysis, which improves compression even more, albeit at great speed cost.\n" "\n" "Trellis costs more speed at higher bitrates and requires CABAC." msgstr "" msgid "Analysis" msgstr "Analyse" msgid "Adaptive Quantization Strength:" msgstr "Adaptive Quantisierungsstärke:" msgid "" "Adaptive quantization controls how the encoder distributes bits across the frame.\n" "\n" "Higher values take more bits away from edges and complex areas to improve areas with finer detail." msgstr "" msgid "Psychovisual Rate Distortion:" msgstr "Psychovisuelle Ratenverzerrung:" msgid "" "Psychovisual rate-distortion optimization takes advantage of the characteristics of human\n" "vision to dramatically improve apparent detail and sharpness.\n" "The effect can be made weaker or stronger by adjusting the strength.\n" "Being an RD algorithm, it requires mode decision to be at least \"6\"." msgstr "" msgid "Psychovisual Trellis:" msgstr "Psychovisuelles Trellis:" msgid "" "Psychovisual trellis is an experimental algorithm to further\n" "improve sharpness and detail retention beyond what Psychovisual RD does.\n" "\n" "Recommended values are around 0.2, though higher values may help for very\n" "grainy video or lower bitrate encodes. Not recommended for cel animation\n" "and other sharp-edged graphics." msgstr "" msgid "Deblocking: " msgstr "Entblocken:" msgid "" "H.264 deblocking filter.\n" "\n" "h.264 has a built-in deblocking filter that smooths out blocking artifacts\n" "after decoding each frame. This not only improves visual quality, but also\n" "helps compression significantly. The deblocking filter takes a lot of CPU power,\n" "so if you're looking to minimize CPU requirements for video playback, disable it.\n" "\n" "The deblocking filter has two adjustable parameters, \"strength\" (Alpha) and \"threshold\" (Beta).\n" "The former controls how strong (or weak) the deblocker is, while the latter controls how many\n" "(or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking.\n" "The default is 0 (normal strength) for both parameters." msgstr "" msgid "No DCT Decimate" msgstr "Keine DCT-Dezimierung" msgid "" "x264 normally zeroes out nearly-empty data blocks to save bits to\n" "be better used for some other purpose in the video. However, this can\n" "sometimes have slight negative effects on retention of subtle grain and\n" "dither.\n" "\n" "Don't touch this unless you're having banding issues or other such cases\n" "where you are having trouble keeping fine noise." msgstr "" msgid "Psychovisual" msgstr "Psychovisuell" msgid "" "Your selected options will appear here.\n" "You can edit these and add additional options.\n" "\n" "Default values will not be shown. The defaults are:\n" "ref=3:bframes=3:b-adapt=fast:direct=spatial:\n" "b-pyramid=normal:weightp=2:me=hex:merange=16:\n" "subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\n" "deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\n" "no-fast-pskip=0:no-dct-decimate=0:cabac=1" msgstr "" msgid "Current x264 Advanced Option String" msgstr "Aktuelle x264 Erweiterte Option-Zeichenkette" msgid "Advanced Video" msgstr "Erweitertes Video" msgid "Chapter Markers" msgstr "Kapitelmarkierungen" msgid "Add chapter markers to output file." msgstr "Der Ausgabedatei Kapitelmarkierungen hinzufügen" msgid "Chapters" msgstr "Kapitel" msgid "Actors:" msgstr "Schauspieler:" msgid "Director:" msgstr "Regisseur:" msgid "Release Date:" msgstr "Veröffentlichungsdatum:" msgid "Comment:" msgstr "Anmerkung:" msgid "Genre:" msgstr "Genre:" msgid "Description:" msgstr "Beschreibung:" msgid "Plot:" msgstr "Handlung:" msgid "Tags" msgstr "Schlagwörter" msgid "Settings" msgstr "Einstellungen" msgid "Edit" msgstr "Bearbeiten" msgid "Reload" msgstr "Erneut laden" msgid "" "Mark selected queue entry as pending.\n" "Resets the queue job to pending and ready to run again." msgstr "" msgid "Reload All" msgstr "Alle erneut laden" msgid "" "Mark all queue entries as pending.\n" "Resets all queue jobs to pending and ready to run again." msgstr "" msgid "OK" msgstr "OK" msgid "Select All" msgstr "Alle auswählen" msgid "Mark all titles for adding to the queue" msgstr "Markieren Sie alle Titel für die Aufnahme in die Warteschlange" msgid "Clear All" msgstr "Alle löschen" msgid "Unmark all titles" msgstr "Markierung aller Titel löschen" msgid "Destination files OK. No duplicates detected." msgstr "Zieldateien OK. Keine Duplikate erkannt." msgid "Select this title for adding to the queue.\n" msgstr "Diesen Titel für die Aufnahme in die Warteschlange auswählen.\n" msgid "Preferences" msgstr "Einstellungen" msgid "Automatically check for updates" msgstr "Automatisch nach Updates suchen" msgid "When all encodes are complete" msgstr "Wenn alle Kodierungsvorgänge abgeschlossen sind" msgid "Use automatic naming (uses modified source name)" msgstr "Automatische Benennung verwenden (auf Basis der Quelle)" msgid "Auto-Name Template" msgstr "Vorlage automatisch benennen" msgid "" "Available Options: {source} {title} {chapters} {date} {time} {quality} " "{bitrate}" msgstr "Verfügbare Optionen: {source} {title} {chapters} {date} {time} {quality} {bitrate}" msgid "Use iPod/iTunes friendly (.m4v) file extension for MP4" msgstr "iPod/iTunes-freundliche Dateierweiterung (.m4v) für MP4 verwenden" msgid "Number of previews" msgstr "Anzahl der Vorschaubilder" msgid "Filter short titles (seconds)" msgstr "Kurztitel (Sekunden) filtern" msgid "Show system tray icon" msgstr "Taskleistensymbol anzeigen" msgid "General" msgstr "Allgemein" msgid "Constant Quality fractional granularity" msgstr "Schrittweite für den CQ-Faktor" msgid "Use dvdnav (instead of libdvdread)" msgstr "dvdnav verwenden (anstatt libdvdread)" msgid "Put individual encode logs in same location as movie" msgstr "Zugehörige Kodierungsprotokolle im gleichen Verzeichnis des Films ablegen" msgid "Activity Log Verbosity Level" msgstr "Aktivitätsprotokoll-Ausführlichkeitsstufe" msgid "Activity Log Longevity" msgstr "Aktivitätsprotokoll-Lebensdauer" msgid "Scale down High Definition previews" msgstr "Hochauflösende Vorschauen herunterskalieren" msgid "Automatically Scan DVD when loaded" msgstr "DVD beim Laden automatisch durchsuchen" msgid "Scans the DVD whenever a new disc is loaded" msgstr "Durchsucht die DVD, immer wenn eine neue Disc geladen ist" msgid "Hide Advanced Video Options Tab" msgstr "" msgid "" "Use advanced video options at your own risk.\n" "We recommend that you use the controls available\n" "on the Video tab instead." msgstr "" msgid "Delete completed jobs from queue" msgstr "Abgeschlossene Aufgaben aus der Warteschlange löschen" msgid "" "By default, completed jobs remain in the queue and are marked as complete.\n" "Check this if you want the queue to clean itself up by deleting completed jobs." msgstr "" msgid "Allow Tweaks" msgstr "Optimierungen erlauben" msgid "Allow HandBrake For Dummies" msgstr "HandBrake für Dummköpfe erlauben" msgid "Advanced" msgstr "Erweitert" msgid "Folder Name:" msgstr "Ordnername:" msgid "Description" msgstr "Beschreibung" msgid "Preset Name:" msgstr "Voreinstellungsname:" msgid "Custom Picture Dimensions" msgstr "Benutzerdefiniertes Bildformat" msgid "Maximum Width:" msgstr "Maximale Breite:" msgid "Enable maximum width limit." msgstr "Maximale Breitenbegrenzung aktivieren" msgid "" "This is the maximum width that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source width is greater.\n" "Setting this to 0 means there is no maximum width." msgstr "" msgid "Maximum Height:" msgstr "Maximale Höhe:" msgid "Enable maximum height limit." msgstr "Maximale Höhenbegrenzung aktivieren" msgid "" "This is the maximum height that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source height is greater.\n" "Setting this to 0 means there is no maximum height." msgstr "" msgid "Select preview frames." msgstr "Vorschaubilder auswählen." msgid "" "Encode and play a short sequence of video starting from the current preview " "position." msgstr "" msgid "Duration:" msgstr "Laufzeit:" msgid "Set the duration of the live preview in seconds." msgstr "Legen Sie die Dauer der Live-Vorschau in Sekunden fest." msgid "Show Crop" msgstr "Zuschneiden anzeigen" msgid "Show Cropped area of the preview" msgstr "Zugeschnittenen Bereich in der Vorschau anzeigen" msgid "Fullscreen" msgstr "Vollbild" msgid "View Fullscreen Preview" msgstr "Vollbildvorschau ansehen" msgid "Title Number:" msgstr "Titelnummer:" msgid "Detected DVD devices:" msgstr "Erkannte DVD-Geräte:" msgid "Setting:" msgstr "Einstellung:" msgid "Import SRT" msgstr "SRT importieren" msgid "Enable settings to import an SRT subtitle file" msgstr "Einstellungen zum Importieren einer SRT-Untertiteldatei aktivieren" msgid "Embedded Subtitle List" msgstr "Eingebettete Untertitelliste" msgid "Enable settings to select embedded subtitles" msgstr "Einstellungen zum Auswählen von eingebetteten Untertiteln aktivieren" msgid "Character Code" msgstr "Zeichencode" msgid "Offset (ms)" msgstr "Versatz (ms)" msgid "" "Set the language of this subtitle.\n" "This value will be used by players in subtitle menus." msgstr "Legen Sie die Sprache dieses Untertitels fest.\nDieser Wert wird von Abspielern in den Untertitelmenüs verwendet werden." msgid "" "Set the character code used by the SRT file you are importing.\n" "\n" "SRTs come in all flavours of character sets.\n" "We translate the character set to UTF-8.\n" "The source's character code is needed in order to perform this translation." msgstr "" msgid "Select the SRT file to import." msgstr "Wählen Sie die SRT-Datei zum Importieren aus." msgid "Srt File" msgstr "SRT-Datei" msgid "Adjust the offset in milliseconds between video and SRT timestamps" msgstr "Stellen Sie den Versatz zwischen Video- und SRT-Zeitstempel in Millisekunden ein" msgid "Track" msgstr "Spur" msgid "List of subtitle tracks available from your source." msgstr "Liste der von Ihrer Quelle verfügbaren Untertitelspuren." msgid "Forced Subtitles Only" msgstr "Nur erzwungene Untertitel" msgid "Burn into video" msgstr "Ins Video einbrennen" msgid "" "Render the subtitle over the video.\n" "The subtitle will be part of the video and can not be disabled." msgstr "Untertitel über das Video rendern.\nDie Untertitel werden dadurch Teil des Videobilds und können nicht abgeschaltet werden." msgid "Set Default Track" msgstr "Standardspur festlegen" msgid "Source Track" msgstr "Quellspur" msgid "List of audio tracks available from your source." msgstr "Liste der von Ihrer Quelle verfügbaren Audiospuren." msgid "Track Name:" msgstr "Spurname:" msgid "" "Set the audio track name.\n" "\n" "Players may use this in the audio selection list." msgstr "Legen Sie den Audiospurnamen fest.\n\nAbspieler können diese in der Audio-Auswahlliste verwenden." msgid "Mix" msgstr "Mischung" msgid "Sample Rate" msgstr "Abtastrate" msgid "" "Dynamic Range Compression: Adjust the dynamic range of the output audio track.\n" "\n" "For source audio that has a wide dynamic range (very loud and very soft sequences),\n" "DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder." msgstr "" msgid "Enable bitrate setting" msgstr "Bitrateneinstellung aktivieren" msgid "Enable quality setting" msgstr "Qualitätseinstellung aktivieren" msgid "" "Quality: For output codec's that support it, adjust the quality of " "the output." msgstr "" msgid "00.0" msgstr "00.0" msgid "" "Audio Gain: Adjust the amplification or attenuation of the output " "audio track." msgstr "" msgid "Skip This Version" msgstr "Diese Version überspringen" msgid "Remind Me Later" msgstr "Später nochmal erinnern" msgid "A new version of HandBrake is available!" msgstr "Eine neue Version von HandBrake ist verfügbar!" msgid "HandBrake xxx is now available (you have yyy)." msgstr "HandBrake xxx ist jetzt verfügbar (aktuell: yyy)" msgid "Release Notes" msgstr "Versionshinweise" msgid "First Track Matching Selected Languages" msgstr "Erste Spur mit den ausgewählten Sprachen" msgid "All Tracks Matching Selected Languages" msgstr "Alle Spuren mit den ausgewählten Sprachen" msgid "Chapters:" msgstr "Kapitel:" msgid "Seconds:" msgstr "Sekunden:" msgid "Frames:" msgstr "Einzelbilder:" msgid "Do Nothing" msgstr "Nichts tun" msgid "Show Notification" msgstr "Benachrichtigung anzeigen" msgid "Quit Handbrake" msgstr "Handbrake beenden" msgid "Put Computer To Sleep" msgstr "Computer in den Ruhezustand versetzen" msgid "Shutdown Computer" msgstr "Computer herunterfahren" msgid "Week" msgstr "Woche" msgid "Month" msgstr "Monat" msgid "Year" msgstr "Jahr" msgid "Immortal" msgstr "Unvergänglich" msgid "Never" msgstr "Niemals" msgid "Daily" msgstr "Täglich" msgid "Weekly" msgstr "Wöchentlich" msgid "Monthly" msgstr "Monatlich" msgid "Default" msgstr "Standard" msgid "Fast" msgstr "Schnell" msgid "Slow" msgstr "Langsam" msgid "Slower" msgstr "Langsamer" msgid "Ultralight" msgstr "" msgid "Light" msgstr "" msgid "Medium" msgstr "Mittel" msgid "Strong" msgstr "Stark" msgid "Film" msgstr "Film" msgid "Grain" msgstr "Körnigkeit" msgid "High Motion" msgstr "" msgid "Animation" msgstr "Animation" msgid "Spatial" msgstr "Räumlich" msgid "Temporal" msgstr "Zeitlich" msgid "Automatic" msgstr "Automatisch" msgid "Optimal" msgstr "Optimal" msgid "Normal" msgstr "Normal" msgid "Simple" msgstr "Einfach" msgid "Smart" msgstr "" msgid "Diamond" msgstr "Diamant" msgid "Hexagon" msgstr "Sechseck" msgid "Uneven Multi-Hexagon" msgstr "" msgid "Exhaustive" msgstr "Erschöpfend" msgid "Hadamard Exhaustive" msgstr "" msgid "Most" msgstr "Meiste" msgid "Some" msgstr "Manche" msgid "All" msgstr "Alle" msgid "Encode only" msgstr "Nur Kodieren" msgid "Always" msgstr "Immer" msgid "(NTSC Film)" msgstr "(NTSC-Film)" msgid "(PAL Film/Video)" msgstr "(PAL-Film/Video)" msgid "(NTSC Video)" msgstr "(NTSC-Video)" msgid "%d - %02dh%02dm%02ds - %s" msgstr "%d - %02dh%02dm%02ds - %s" msgid "%d (%05d.MPLS) - %02dh%02dm%02ds" msgstr "%d (%05d.MPLS) - %02dh%02dm%02ds" msgid "%d (%05d.MPLS) - Unknown Length" msgstr "%d (%05d.MPLS) - Unbekannte Länge" msgid "%d - %02dh%02dm%02ds" msgstr "%d - %02dh%02dm%02ds" msgid "%d - Unknown Length" msgstr "%d - Unbekannte Länge" msgid "No Titles" msgstr "Keine Titel" msgid "No Audio" msgstr "Kein Audio" msgid "Foreign Audio Search" msgstr "" #, c-format msgid "Chapter %2d" msgstr "Kapitel %2d" #, c-format msgid "N/A" msgstr "N/V" #, c-format msgid "" "Invalid Deinterlace Settings:\n" "\n" "%s\n" msgstr "Ungültige Zeilenentflechtungseinstellungen:\n\n%s\n" #, c-format msgid "" "Invalid Detelecine Settings:\n" "\n" "%s\n" msgstr "" #, c-format msgid "" "Invalid Decomb Settings:\n" "\n" "%s\n" msgstr "" msgid "" "Theora is not supported in the MP4 container.\n" "\n" "You should choose a different video codec or container.\n" "If you continue, FFMPEG will be chosen for you." msgstr "" msgid "Continue" msgstr "Fortsetzen" msgid "No title found.\n" msgstr "Kein Titel gefunden.\n" msgid "" "Only one subtitle may be burned into the video.\n" "\n" "You should change your subtitle selections.\n" "If you continue, some subtitles will be lost." msgstr "" msgid "" "Srt file does not exist or not a regular file.\n" "\n" "You should choose a valid file.\n" "If you continue, this subtitle will be ignored." msgstr "" msgid "" "The source does not support Pass-Thru.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "%s is not supported in the %s container.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "The source audio does not support %s mixdown.\n" "\n" "You should choose a different mixdown.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "Unable to create %s.\n" "\n" "Internal error. Could not parse UI description.\n" "%s" msgstr "" msgid "Index" msgstr "Index" msgid "Duration" msgstr "Laufzeit" msgid "Title" msgstr "Titel" msgid "Job Information" msgstr "Aufgabeninformation" msgid "Track Information" msgstr "Spurinformation" msgid "Preset Name" msgstr "Voreinstellungsname" msgid "The device or file to encode" msgstr "Das Gerät oder die Datei zum Kodieren" msgid "The preset values to use for encoding" msgstr "Die voreingestellten Werte für die Kodierung" msgid "Spam a lot" msgstr "" msgid "- Transcode media formats" msgstr "- Medienformate transkodieren" msgid "Globals" msgstr "" msgid "Presets" msgstr "Voreinstellungen" msgid "Folder" msgstr "Ordner" #, c-format msgid "%s path: (%s)" msgstr "%s Pfad: (%s)" #, c-format msgid "%s indices: len %d" msgstr "" msgid "Type" msgstr "Typ" msgid "Failed to find parent folder when adding child." msgstr "" msgid "Failed to find parent folder while adding child." msgstr "" #, c-format msgid "Can't map language value: (%s)" msgstr "" #, c-format msgid "Can't map value: (%s)" msgstr "" msgid "Subtitles" msgstr "Untertitel" msgid "" "%s: Folder already exists.\n" "You can not replace it with a preset." msgstr "%s: Ordner bereits vorhanden.\nSie können ihn nicht mit einer Voreinstellung ersetzen." msgid "" "%s: Preset already exists.\n" "You can not replace it with a folder." msgstr "%s: Voreinstellung bereits vorhanden.\nSie können sie nicht mit einem Ordner ersetzen." msgid "Import Preset" msgstr "Voreinstellung importieren" msgid "All (*)" msgstr "Alle (*)" msgid "Presets (*.plist)" msgstr "Voreinstellungen (*.plist)" msgid "Export Preset" msgstr "Voreinstellung exportieren" msgid "" "Confirm deletion of %s:\n" "\n" "%s" msgstr "Löschung von %s bestätigen:\n\n%s" msgid "folder" msgstr "Ordner" msgid "preset" msgstr "Voreinstellung" msgid "No selection??? Perhaps unselected." msgstr "Keine Auswahl??? Vielleicht abgewählt." #, c-format msgid "Gstreamer Error: %s" msgstr "Gstreamer-Fehler: %s" #, c-format msgid "" "Missing GStreamer plugin\n" "Audio or Video may not play as expected\n" "\n" "%s" msgstr "" msgid "Done" msgstr "Fertig" msgid "Windowed" msgstr "Fenstermodus" msgid "Seconds" msgstr "Sekunden" msgid "Frames" msgstr "Einzelbilder" #, c-format msgid "" "%s (Title %d, %s %d through %d, 2 Video Passes) -->" " %s" msgstr "%s (Titel %d, %s %d durch %d, 2 Videodurchgänge) --> %s" #, c-format msgid "" "%s (Title %d, %s %d through %d) --> %s" msgstr "%s (Titel %d, %s %d durch %d) --> %s" #, c-format msgid "Modified Preset Based On: %s\n" msgstr "Geänderte Voreinstellung basierend auf: %s\n" #, c-format msgid "Preset: %s\n" msgstr "Voreinstellung: %s\n" #, c-format msgid "Format: %s Container\n" msgstr "Format: %s Container\n" msgid "Container Options:" msgstr "Containeroptionen:" #, c-format msgid "%sChapter Markers" msgstr "%sKapitelmarkierungen" #, c-format msgid "%siPod 5G Support" msgstr "%siPod 5G-Unterstützung" #, c-format msgid "%sWeb Optimized" msgstr "%sweboptimiert" #, c-format msgid "%sLarge File Size (>4GB)" msgstr "%sGroße Dateigröße (>4GB)" #, c-format msgid "Destination: %s\n" msgstr "Ziel: %s\n" msgid "(Aspect Preserved)" msgstr "(Seitenverhältnis erhalten)" msgid "(Aspect Lost)" msgstr "(Seitenverhältnis verloren)" msgid "(Anamorphic)" msgstr "(Anamorph)" msgid "(Custom Anamorphic)" msgstr "(Benutzerdefiniert anamorph)" msgid "Picture: " msgstr "Bild: " #, c-format msgid "Source: %d x %d, Output %d x %d %s, Crop %d:%d:%d:%d" msgstr "Quelle: %d x %d, Ausgabe %d x %d %s, Zuschneiden %d:%d:%d:%d" #, c-format msgid ", Display %d x %d" msgstr ", Anzeige %d x %d" msgid "Filters:" msgstr "Filter:" #, c-format msgid "%sDetelecine" msgstr "" #, c-format msgid "%sDecomb" msgstr "" #, c-format msgid "%sDeinterlace" msgstr "%sZeilenentflechtung" #, c-format msgid "%sDenoise Filter %s:" msgstr "%sEntrauschen-Filter %s:" #, c-format msgid "%sDeblock: %d" msgstr "%sEntblocken: %d" #, c-format msgid "%sGrayscale" msgstr "%sGraustufen" #, c-format msgid "Video: %s" msgstr "Video: %s" #, c-format msgid ", Framerate: %s %s" msgstr ", Bildwechselfrequenz: %s %s" #, c-format msgid ", Framerate: Peak %s (may be lower)" msgstr ", Bildwechselfrequenz: Spitzenwert %s (kann niedriger sein)" #, c-format msgid ", Framerate: %s (constant frame rate)" msgstr ", Bildwechselfrequenz: %s (konstante Bildwechselfrequenz)" msgid "Error" msgstr "Fehler" msgid "Bitrate:" msgstr "Bitrate:" msgid "Bitrate" msgstr "Bitrate" msgid "Quality" msgstr "Qualität" msgid "Turbo 1st Pass: On\n" msgstr "" #, c-format msgid "Video Options: Preset: %s" msgstr "Videooptionen: Voreinstellung: %s" msgid " - Tune: " msgstr " - Abstimmen: " #, c-format msgid " - Profile: %s" msgstr " - Profil: %s" #, c-format msgid " - Level: %s" msgstr " - Stufe: %s" #, c-format msgid "Advanced Options: %s\n" msgstr "Erweiterte Optionen: %s\n" msgid "Audio: " msgstr "Audio: " #, c-format msgid "Audio Tracks: %d" msgstr "Audiospuren: %d" #, c-format msgid "Bitrate: %d" msgstr "Bitrate: %d" #, c-format msgid "%s --> Encoder: %s" msgstr "%s --> Kodierer: %s" #, c-format msgid "%s --> Encoder: %s, Mixdown: %s, SampleRate: %s, %s" msgstr "%s --> Kodierer: %s, Abmischung: %s, Abtastrate: %s, %s" msgid "Subtitle: " msgstr "Untertitel: " #, c-format msgid "Subtitle Tracks: %d\n" msgstr "Untertitelspuren: %d\n" msgid " (Force)" msgstr " (Erzwingen)" msgid " (Burn)" msgstr " (Brennen)" msgid " (Default)" msgstr " (Standard)" #, c-format msgid " %s (%s), %s, Offset (ms) %d%s\n" msgstr " %s (%s), %s, Versatz (ms) %d%s\n" #, c-format msgid "" "Destination: %s\n" "\n" "Another queued job has specified the same destination.\n" "Do you want to overwrite?" msgstr "Ziel: %s\n\nEine weitere Aufgabe in der Warteschlange hat das gleiche Ziel angegeben.\nMöchten Sie es überschreiben?" msgid "Overwrite" msgstr "Überschreiben" #, c-format msgid "" "Destination: %s\n" "\n" "This is not a valid directory." msgstr "Ziel: %s\n\nDies ist kein gültiges Verzeichnis." #, c-format msgid "" "Destination: %s\n" "\n" "Can not read or write the directory." msgstr "Ziel: %s\n\nVerzeichnis nicht lesbar oder beschreibbar." #, c-format msgid "" "Destination filesystem is almost full: %uM free\n" "\n" "Encode may be incomplete if you proceed.\n" msgstr "Zieldateisystem ist fast voll: %uM frei\n\nKodieren könnte unvollständig sein, wenn Sie fortfahren.\n" msgid "Proceed" msgstr "Fortfahren" #, c-format msgid "" "Destination: %s\n" "\n" "File already exists.\n" "Do you want to overwrite?" msgstr "Ziel: %s\n\nDie Datei existiert bereits.\nSoll sie überschrieben werden?" msgid "" "Duplicate destination files detected.\n" "Duplicates will not be added to the queue." msgstr "Doppelte Zieldateien erkannt.\nDuplikate werden nicht in die Warteschlange aufgenommen." msgid "No Title" msgstr "Kein Titel" msgid "" "There is another title with the same destination file name.\n" "This title will not be added to the queue unless you change\n" "the output file name.\n" msgstr "" msgid "Stop" msgstr "Stopp" msgid "Stop Encoding" msgstr "Kodierung stoppen" msgid "Resume" msgstr "Fortsetzen" msgid "Resume Encoding" msgstr "Kodierung fortsetzen" msgid "S_top Queue" msgstr "Warteschlange s_toppen" msgid "_Start Queue" msgstr "Warteschlange _abarbeiten" msgid "_Resume Queue" msgstr "Warteschlange fo_rtsetzen" msgid "Resume Queue" msgstr "Warteschlange fortsetzen" msgid "" "You are currently encoding. What would you like to do?\n" "\n" msgstr "" #, c-format msgid "" "You have %d unfinished job(s) in a saved queue.\n" "\n" "Would you like to reload them?" msgstr "Sie haben noch %d nicht abgearbeitete(n) Auftrag/Aufträge in der gespeicherten Warteschlange.\n\nSollen diese erneut geladen werden?" msgid "No" msgstr "Nein" msgid "Yes" msgstr "Ja" #, c-format msgid "Usage: %s infile [outfile]\n" msgstr "Aufruf: %s Eingabedatei [Ausgabedatei]\n" #, c-format msgid "Offset: %dms" msgstr "Versatz: %dms" msgid "Burned Into Video" msgstr "Ins Video gebrannt" msgid "Passthrough" msgstr "Durchleiten" msgid "through" msgstr "durch" msgid "(Forced Subtitles Only)" msgstr "(Nur erzwungene Untertitel)" msgid "(Default)" msgstr "(Standard)" msgid "Error!" msgstr "Fehler!" #, c-format msgid "Preferred Language: %s" msgstr "Bevorzugte Sprache: %s" #, c-format msgid "Add %s subtitle track if default audio is not %s" msgstr "%s Untertitelspur hinzufügen, wenn das voreingestellte Audio nicht %s ist" #, c-format msgid "Type %s" msgstr "Typ %s" #, c-format msgid "Type %s value %s" msgstr "Typ %s Wert %s" #, c-format msgid "Type %s value %d" msgstr "Typ %s Wert %d" #, c-format msgid "Type %s value %" msgstr "Typ %s Wert %" #, c-format msgid "Type %s value %f" msgstr "Typ %s Wert %f" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"%s\"" msgstr "%s\n\nErweiterte Optionen:\n\"%s\"" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"\"" msgstr "%s\n\nErweiterte Optionen:\n\"\"" msgid "Any" msgstr "Beliebig" msgid "0: SAD, no subpel" msgstr "" msgid "4: SATD, qpel on all" msgstr "" msgid "5: SATD, multi-qpel on all" msgstr "" msgid "6: RD in I/P-frames" msgstr "" msgid "7: RD in all frames" msgstr "" msgid "8: RD refine in I/P-frames" msgstr "" msgid "9: RD refine in all frames" msgstr "" msgid "10: QPRD in all frames" msgstr "" msgid "11: No early terminations in analysis" msgstr "" msgid "Your names" msgstr "Ihr Name" msgid "Your emails" msgstr "Ihre E-Mail" HandBrake-0.10.2/gtk/po/ja_JP.po0000664000175200017520000027301412466165306016605 0ustar handbrakehandbrake# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: msgid "" msgstr "" "Project-Id-Version: HandBrake-0.10\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-03 13:01+0300\n" "PO-Revision-Date: 2015-02-09 12:01+0000\n" "Last-Translator: Masato HASHIMOTO \n" "Language-Team: Japanese (Japan) (http://www.transifex.com/projects/p/handbrake/language/ja_JP/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ja_JP\n" "Plural-Forms: nplurals=1; plural=0;\n" msgid "Quality: " msgstr "音質: " #, c-format msgid "Bitrate: %dkbps" msgstr "ビットレート: %dkbps" #, c-format msgid "Bitrate: %.4gkbps" msgstr "ビットレート: %.4gkbps" msgid "Passthrough" msgstr "パススルー" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s\n" "Track Name: %s" msgstr "%s\nゲイン: %s\nDRC: %s\nトラック名: %s" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s" msgstr "%s\nゲイン: %s\nDRC: %s" msgid "Add" msgstr "追加" msgid "" "Add an audio encoder.\n" "Each selected source track will be encoded with all selected encoders." msgstr "オーディオエンコーダーを追加します。\n選択した各ソーストラックは選択したすべてのエンコーダーでエンコードされます。" msgid "Set the audio codec to encode this track with." msgstr "このトラックをエンコードするオーディオコーデックを指定します。" msgid "Set the bitrate to encode this track with." msgstr "このトラックをエンコードするビットレートを指定します。" msgid "" "Audio Quality:\n" "For encoders that support it, adjust the quality of the output." msgstr "音質:\nこれをサポートするエンコーダーでは、出力の音質を調整します。" msgid "Set the mixdown of the output audio track." msgstr "出力オーディオトラックのミックスダウンを指定します。" msgid "Set the sample rate of the output audio track." msgstr "出力オーディオトラックのサンプルレートを指定します。" msgid "" "Audio Gain:\n" "Adjust the amplification or attenuation of the output audio track." msgstr "オーディオゲイン:\n出力オーディオトラックの増幅または減衰量を指定します。" msgid "0dB" msgstr "0dB" msgid "%ddB" msgstr "%ddB" msgid "%.4gkHz" msgstr "%.4gkHz" msgid "%d - %s (%.4gkHz)" msgstr "%d - %s (%.4gkHz)" msgid "" "Dynamic Range Compression:\n" "Adjust the dynamic range of the output audio track.\n" "For source audio that has a wide dynamic range,\n" "very loud and very soft sequences, DRC allows you\n" "to 'compress' the range by making loud sounds\n" "softer and soft sounds louder.\n" msgstr "ダイナミックレンジ圧縮:\n出力オーディオトラックのダイナミックレンジを調節します。ソースオーディオのダイナミックレンジが広く、とても大きい/小さいシーケンスの場合、DRC は音量を下げて/上げてレンジを '圧縮' できます。\n" msgid "Remove this audio encoder" msgstr "このオーディオエンコーダーを削除します" msgid "Closing HandBrake will terminate encoding.\n" msgstr "HandBrake を閉じるとエンコードを中止します。\n" msgid "No Title Found" msgstr "タイトルが見つかりません" msgid "none" msgstr "なし" msgid "Not Selected" msgstr "選択されていません" msgid "zerolatency x264 tune selected, forcing constant framerate" msgstr "zerolatency x264 tune selected, forcing constant framerate" msgid "Scanning ..." msgstr "スキャン中..." msgid "Stop Scan" msgstr "スキャン中止" msgid "On" msgstr "オン" msgid "Strict" msgstr "厳密" msgid "Loose" msgstr "ルーズ" msgid "Custom" msgstr "カスタム" msgid "Unknown" msgstr "不明" msgid "auto" msgstr "自動" #, c-format msgid "" "%s\n" "\n" "%s in %d seconds ..." msgstr "%1$s\n\n%3$d 秒後に %2$s します ..." #, c-format msgid "%sYour movie will be lost if you don't continue encoding." msgstr "%sエンコードを中止するとエンコードファイルは正しく再生できません。" msgid "Cancel Current and Stop" msgstr "現在のエンコードをキャンセルして停止" msgid "Cancel Current, Start Next" msgstr "現在のエンコードをキャンセルして次を開始" msgid "Finish Current, then Stop" msgstr "現在のエンコードの完了後に停止" msgid "Continue Encoding" msgstr "エンコード続行" msgid "Custom " msgstr "Custom " msgid "Modified " msgstr "Modified " #, c-format msgid "Handbrake Version: %s (%d)\n" msgstr "Handbrake Version: %s (%d)\n" #, c-format msgid "%d encode(s) pending" msgstr "%d 個のエンコードが待機中です" #, c-format msgid "job %d of %d, " msgstr "ジョブ %d / %d, " #, c-format msgid "pass %d (subtitle scan) of %d, " msgstr "パス %d (字幕スキャン) / %d, " #, c-format msgid "pass %d of %d, " msgstr "パス %d / %d, " #, c-format msgid "Encoding: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" msgstr "エンコード中: %s%s%.2f %% (%.2f fps, 平均 %.2f fps, 完了予定 %02d時間%02d分%02d秒後)" #, c-format msgid "Encoding: %s%s%.2f %% (ETA %02dh%02dm%02ds)" msgstr "エンコード中: %s%s%.2f %% (完了予定 %02d時間%02d分%02d秒後)" #, c-format msgid "Encoding: %s%s%.2f %%" msgstr "エンコード中: %s%s%.2f %%" msgid "Searching for start time, " msgstr "開始時間を検索中 " msgid "Scanning..." msgstr "スキャン中..." #, c-format msgid "Scanning title %d of %d..." msgstr "タイトル %d / %d をスキャン中..." #, c-format msgid "Scanning title %d of %d preview %d..." msgstr "タイトル %d / %d のプレビュー %d をスキャン中..." msgid "Source" msgstr "ソース" msgid "Choose Video Source" msgstr "ビデオソースを選択します" msgid "Paused" msgstr "一時停止" msgid "Encode Done!" msgstr "エンコード完了!" msgid "Encode Canceled." msgstr "エンコードをキャンセルしました。" msgid "Encode Failed." msgstr "エンコードに失敗しました。" msgid "Muxing: This may take a while..." msgstr "MUX中: これは多少時間がかかるかもしれません..." msgid "Scan this DVD source" msgstr "この DVD ソースをスキャン" msgid "AutoScan" msgstr "自動スキャン" msgid "Suspend" msgstr "サスペンド" #, c-format msgid "Suspend failed: %s" msgstr "サスペンド失敗: %s" msgid "Suspend failed" msgstr "サスペンドに失敗しました" msgid "Shutdown" msgstr "シャットダウン" #, c-format msgid "Shutdown failed: %s" msgstr "シャットダウン失敗: %s" msgid "Shutdown failed" msgstr "シャットダウンに失敗しました" msgid "Encoding" msgstr "エンコード中" #, c-format msgid "press %d %d" msgstr "press %d %d" #, c-format msgid "" "Invalid Settings:\n" "%s" msgstr "不正な設定:\n%s" msgid "Cancel" msgstr "キャンセル" #, c-format msgid "%s: %.4g (Warning: lossless)" msgstr "%s: %.4g (警告: ロスレスです)" #, c-format msgid "HandBrake %s/%s is now available (you have %s/%d)." msgstr "HandBrake %s/%s が利用可能です (あなたのバージョン: %s/%d)。" msgid "Encode Complete" msgstr "エンコード完了" msgid "Put down that cocktail, Your HandBrake queue is done!" msgstr "カクテルを戻してください。HandBrake キューが完了しました!" msgid "Your encode is complete." msgstr "エンコードが完了しました。" msgid "Shutting down the computer" msgstr "コンピューターをシャットダウンしています" msgid "Putting computer to sleep" msgstr "コンピューターをスリープしています" msgid "Quiting Handbrake" msgstr "HandBrake を終了しています" msgid "Bottom" msgstr "" #, c-format msgid "open failed: %s\n" msgstr "" msgid "No key for dictionary item" msgstr "辞書アイテムのキーがありません" msgid "Invalid container type. This shouldn't happen" msgstr "不正なコンテナータイプです。これは起こってはならないことです。" #, c-format msgid "%s:missing a requried attribute" msgstr "" #, c-format msgid "" "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" msgstr "Usage: %s [-I ] \nSummary:\n Creates a resource plist from a resource list\nOptions:\n I - Include path to search for files\n Input resources file\n Output resources plist file\n" msgid "language" msgstr "言語" msgid "Language" msgstr "言語" msgid "" "The language this text is in, as an ISO code. Pango can use this as a hint " "when rendering the text. If you don't understand this parameter, you " "probably don't need it" msgstr "このテキストの言語を ISO コードで指定します。Pango はテキストのレンダリング時にこれをヒント情報として使用できます。このパラメーターの意味がわからない人は指定する必要はありません。" msgid "" "The preferred place to ellipsize the string, if the cell renderer does not " "have enough room to display the entire string" msgstr "セルレンダラーが文字列全体を表示するだけの広さがないときの文字列を省略する位置を指定します。" msgid "" "How to break the string into multiple lines, if the cell renderer does not " "have enough room to display the entire string" msgstr "セルレンダラーが文字列全体を表示するだけの広さがないときの文字列の改行方法を指定します。" msgid "" "Render the subtitle over the video.\n" "\n" "The subtitle will be part of the video and can not be disabled." msgstr "映像上に字幕を書き込みます。\n\n字幕は映像の一部になり、消すことはできません。" msgid "Burned In" msgstr "焼き込み" msgid "" "Set the default output subtitle track.\n" "\n" "Most players will automatically display this\n" "subtitle track whenever the video is played.\n" "\n" "This is useful for creating a \"forced\" track\n" "in your output." msgstr "デフォルトの出力字幕トラックを指定します。\n\nほとんどのプレーヤーは再生時にこの字幕を自動的に表示します。\n\nこれは出力に \"強制\" トラックを作成するのに便利です。" msgid "Default" msgstr "デフォルト" msgid "" "Use only subtitles that have been flagged\n" "as forced in the source subtitle track\n" "\n" "\"Forced\" subtitles are usually used to show\n" "subtitles during scenes where someone is speaking\n" "a foreign language." msgstr "ソース字幕トラックで強制指定されている字幕のみ使用します。\n\n\"強制\" 字幕は通常外国語で喋っているシーンで表示されます。" msgid "Forced Only" msgstr "強制のみ" msgid "" "Add (or subtract) an offset (in milliseconds)\n" "to the start of the SRT subtitle track.\n" "\n" "Often, the start of an external SRT file\n" "does not coincide with the start of the video.\n" "This setting allows you to synchronize the files." msgstr "SRT 字幕トラックの開始にミリ秒単位でオフセットを指定します。\n\n外部 SRT ファイルでは映像と字幕にズレが出るときが良くあります。\nこの設定でファイル位の同期を調整できます。" msgid "SRT Offset" msgstr "SRT オフセット" msgid "Off" msgstr "オフ" msgid "" "The source subtitle track\n" "\n" "You can choose any of the subtitles\n" "recognized in your source file.\n" "\n" "In addition, there is a special track option\n" "\"Foreign Audio Search\". This option will add\n" "an extra pass to the encode that searches for\n" "subtitles that may correspond to a foreign\n" "language scene. This option is best used in\n" "conjunction with the \"Forced\" option." msgstr "ソース字幕トラックです。\n\nソースファイル内にある任意の字幕を選択できます。\n\n加えて、特殊トラックオプション \"外国語音声検索\" があります。\nこのオプションは外国語シーンに応じた字幕を検索する拡張パスをエンコードに追加します。このオプションは \"強制\" オプションとともに使用するのにもっとも適しています。" msgid "Track" msgstr "トラック" msgid "About HandBrake" msgstr "HandBrake について" msgid "" "Copyright © 2008 - 2013 John Stebbins\n" "Copyright © 2004 - 2013, HandBrake Devs" msgstr "" msgid "" "HandBrake is a GPL-licensed, multiplatform, multithreaded video transcoder." msgstr "HandBrake は GPL で公開されているマルチプラットフォームでマルチスレッドのビデオエンコーダーです。" msgid "http://handbrake.fr" msgstr "http://handbrake.fr" msgid "" "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n" "\n" "HandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgstr "HandBrake はフリーソフトウェアです。あなたはこれを、フリーソフトウェア財団 (Free Software Foundation) が発行する GNU General Public License 第 2 版あるいは (あなたが選ぶ) それ以降の版のいずれかの条項の下で再配布および/または変更できます。\n\nHandBrake は有用であることを期待して配布されていますが、特定の目的に対する適合性、市場性の暗黙の保証などを含む一切の責任を負いません。詳細は GNU General Public License を参照してください。\n\nあなたは Glade とともに GNU General Public License のコピーを受け取っているはずですが、もしないのであればフリーソフトウェア財団に連絡してください: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA" msgid "_Minimize/Maximize" msgstr "最小化/最大化(_M)" msgid "_Pause Queue" msgstr "キューの一時停止(_P)" msgid "_Quit" msgstr "終了(_Q)" msgid "_About" msgstr "HandBrake について(_A)" msgid "HandBrake" msgstr "HandBrake" msgid "_File" msgstr "ファイル(_F)" msgid "_Source" msgstr "ソース(_S)" msgid "Single _Title" msgstr "単一タイトル(_T)" msgid "_Destination" msgstr "保存先(_D)" msgid "_Preferences" msgstr "設定(_P)" msgid "_Queue" msgstr "キュー(_Q)" msgid "_Add" msgstr "追加(_A)" msgid "Add _Multiple" msgstr "複数追加(_M)" msgid "_Start" msgstr "開始(_S)" msgid "_Pause" msgstr "一時停止(_P)" msgid "_View" msgstr "表示(_V)" msgid "HandBrake For _Dumbies" msgstr "バカチン用 HandBrake(_D)" msgid "_Show Presets" msgstr "プリセットを表示(_S)" msgid "_Preview" msgstr "プレビュー(_P)" msgid "_Activity Window" msgstr "アクティビティウィンドウ(_A)" msgid "Show _Queue" msgstr "キューを表示(_Q)" msgid "_Presets" msgstr "プリセット(_P)" msgid "_Save" msgstr "保存(_S)" msgid "_Delete" msgstr "削除(_D)" msgid "_Make Default" msgstr "デフォルトに設定(_M)" msgid "_New Folder" msgstr "新規フォルダー(_N)" msgid "_Export" msgstr "エクスポート(_E)" msgid "_Import" msgstr "インポート(_I)" msgid "_Update Built-in Presets" msgstr "組み込みプリセットの更新(_U)" msgid "_Help" msgstr "ヘルプ(_H)" msgid "_Guide" msgstr "ガイド(_G)" msgid "Start Encoding" msgstr "エンコードを開始します" msgid "Start" msgstr "開始" msgid "Pause Encoding" msgstr "エンコードを一時停止します" msgid "Pause" msgstr "一時停止" msgid "Add to Queue" msgstr "キューに追加します" msgid "Enqueue" msgstr "追加" msgid "Show Queue" msgstr "キューを表示します" msgid "Queue" msgstr "キュー" msgid "" "Open Picture Settings and Preview window.\n" "Here you can adjust cropping, resolution, aspect ratio, and filters." msgstr "映像設定とプレビューウィンドウを開きます。\nここではクロップ、解像度、アスペクト比およびフィルターを調整できます。" msgid "Preview" msgstr "プレビュー" msgid "Show Activity Window" msgstr "アクティビティウィンドウを表示します" msgid "Activity" msgstr "アクティビティ" msgid "Source:" msgstr "ソース:" msgid "None" msgstr "なし" msgid "Title:" msgstr "タイトル:" msgid "" "Set the title to encode.\n" "By default the longest title is chosen.\n" "This is often the feature title of a DVD." msgstr "エンコードするタイトルを指定します。\nデフォルトではもっとも長いタイトルが選択されます。\nDVD によっては DVD のフィーチャータイトルになるときがあります。" msgid "Angle:" msgstr "アングル:" msgid "For multi-angle DVD's, select the desired angle to encode." msgstr "マルチアングルの DVD のため、エンコードしたいアングルを選択してください。" msgid "Reset All Titles" msgstr "全タイトルのリセット" msgid "Apply current settings to all titles" msgstr "現在の設定を全タイトルに適用します" msgid "Range of title to encode. Can be chapters, seconds, or frames." msgstr "エンコードするタイトルの範囲を指定します。チャプター、秒、フレームで指定できます。" msgid "Set the first chapter to encode." msgstr "エンコードする最初のチャプターを指定します。" msgid "Set the last chapter to encode." msgstr "エンコードする最後のチャプターを指定します。" msgid "Duration:" msgstr "時間:" msgid "Destination" msgstr "保存先" msgid "File:" msgstr "ファイル:" msgid "Destination filename for your encode." msgstr "エンコードファイルのファイル名です。" msgid "Destination directory for your encode." msgstr "エンコードファイルを保存するディレクトリです。" msgid "Destination Directory" msgstr "保存先ディレクトリ" msgid "Format:" msgstr "フォーマット:" msgid "Format to mux encoded tracks to." msgstr "エンコードしたトラックを MUX するフォーマットです。" msgid "iPod 5G Support" msgstr "iPod 5G サポート" msgid "Add iPod Atom needed by some older iPods." msgstr "一部の古い iPod のための iPod Atom を追加します。" msgid "Web optimized" msgstr "ウェブに最適化" msgid "" "Optimize the layout of the MP4 file for progressive download.\n" "This allows a player to initiate playback before downloading the entire file." msgstr "MP4 ファイルのレイアウトをプログレッシブダウンロード用に最適化します。\nこれによりファイル全体をダウンロードする前にプレーヤーで再生を開始できます。" msgid "Large file (>4GB)" msgstr "大きいファイル (>4GB)" msgid "" "Allow 64 bit MP4 file which can be over 4GB.\n" "\n" "Caution: This option may break device compatibility." msgstr "4GB 以上のサイズをサポートする 64bit MP4 ファイルにします。\n\n注意: このオプションはデバイス互換性を破壊するかもしれません。" msgid "Presets List" msgstr "プリセットリスト" msgid "Source Codec:" msgstr "ソースコーデック:" msgid "Dimensions:" msgstr "映像サイズ:" msgid "Aspect: " msgstr "アスペクト比: " msgid "Frame Rate:" msgstr "フレームレート:" msgid "Source Picture Parameters" msgstr "ソース映像パラメーター" msgid "Autocrop:" msgstr "自動クロップ:" msgid "Crop:" msgstr "クロップ:" msgid "Crop Dimensions:" msgstr "クロップサイズ:" msgid "Cropping" msgstr "クロッピング" msgid "Scale Dimensions:" msgstr "スケールサイズ:" msgid "Optimal for Source:" msgstr "ソースに最適化:" msgid "Anamorphic:" msgstr "アナモフィック:" msgid "Scaling" msgstr "スケーリング" msgid "Presentation Dimensions:" msgstr "出力映像サイズ:" msgid "Summary" msgstr "概要" msgid "Left Crop" msgstr "左クロップ" msgid "Top Crop" msgstr "上クロップ" msgid "Bottom Crop" msgstr "下クロップ" msgid "Right Crop" msgstr "右クロップ" msgid "Auto Crop" msgstr "自動クロップ" msgid "Automatically crop black borders around edges of the video." msgstr "映像周辺の黒帯を自動的に切り落とします。" msgid "Loose Crop" msgstr "ルーズクロップ" msgid "" "When picture settings require that the image\n" "dimensions be rounded to some multiple number\n" "of pixels, this setting will crop a few extra pixels\n" "instead of doing exact cropping and then scaling to\n" "the required multiple." msgstr "映像設定で画像周辺の複数のピクセルを切り落とす必要があったとき、正確に切り落とすのではなく、わずかな拡張ピクセルを切り落とします。" msgid "width:" msgstr "幅:" msgid "" "This is the width that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "保存される動画の幅になります。\nピクセルアスペクト比が 1:1 ではないとき、実際の表示サイズと異なる場合があります。" msgid "height:" msgstr "高さ:" msgid "" "This is the height that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "保存される動画の高さになります。\nピクセルアスペクト比が 1:1 ではないとき、実際の表示サイズと異なる場合があります。" msgid "Optimal for source" msgstr "ソースに最適化" msgid "" "If enabled, select the 'optimal' storage resolution.\n" "This will be the resolution that most closely matches the source resolution after cropping." msgstr "このオプションを有効にすると、'最適な' 解像度で保存されます。\nこれはクロップ後にソースの解像度にもっとも近い解像度にします。" msgid "" "Anamorphic Modes:\n" "\n" "None - Force pixel aspect ratio to 1:1.\n" "Loose - Align dimensions to chosen 'Alignment' value\n" " and pick pixel aspect ratio that preserves the\n" " original display aspect ratio\n" "Strict - Keep original source dimensions and pixel\n" " aspect ratio" msgstr "アナモフィックモード:\n\nなし - ピクセルアスペクト比を強制的に 1:1 にします。\nルーズ - 画像サイズを 'アラインメント' の値で調節し、ピクセル\n アスペクト比を取得してオリジナルのディスプレイアスペクト比\n を保ちます。\n厳密 - オリジナルソースのサイズとピクセルアスペクト比を維持\n します。" msgid "Alignment:" msgstr "アラインメント:" msgid "" "Align storage dimensions to multiples of this value.\n" "\n" "This setting is only necessary for compatibility with some devices.\n" "You should use 2 unless you experience compatibility issues." msgstr "保存映像サイズをこの値で調整します。\n\nこの設定は一部デバイスとの互換性のためだけに必要です。\nあなたがこの互換性問題を熟知していないかぎり 2 を指定すべきです。" msgid "Storage Geometry" msgstr "保存映像サイズ" msgid "" "This is the display width. It is the result of scaling the storage " "dimensions by the pixel aspect." msgstr "表示幅になります。これはピクセルアスペクト比でスケーリングされた映像サイズの結果になります。" msgid "Pixel Aspect:" msgstr "ピクセルアスペクト比:" msgid "" "Pixel aspect defines the shape of the pixels.\n" "\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "ピクセルアスペクト比はピクセルの形状を定義します。\n\n比率 1:1 は正方形のピクセルを定義します。その他の値は長方形の形状を定義します。\nプレーヤーは指定されたアスペクト比にするために画像をスケールします。" msgid ":" msgstr ":" msgid "" "Pixel aspect defines the shape of the pixels.\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "ピクセルアスペクト比はピクセルの形状を定義します。\n比率 1:1 は正方形のピクセルを定義します。その他の値は長方形の形状を定義します。\nプレーヤーは指定されたアスペクト比にするために画像をスケールします。" msgid "Keep Aspect" msgstr "アスペクト比を維持" msgid "" "If enabled, the original display aspect of the source will be maintained." msgstr "このオプションを有効にすると、ソースのオリジナルディスプレイアスペクト比を維持します。" msgid "Display Aspect:" msgstr "ディスプレイアスペクト比:" msgid "Display Geometry" msgstr "表示映像サイズ" msgid "Grayscale" msgstr "グレースケール" msgid "If enabled, filter colour components out of video." msgstr "このオプションを有効にすると、動画から色成分が除去されます。" msgid "Deblock:" msgstr "デブロック:" msgid "" "The deblocking filter removes a common type of compression artifact.\n" "If your source exhibits 'blockiness', this filter may help clean it up." msgstr "デブロック化は圧縮による一般的なアーチファクトを除去します。\nソースに 'ブロックノイズ' が見受けられた場合、このフィルターがきれいに仕上げてくれるかもしれません。" msgid "Denoise Filter:" msgstr "デノイズフィルター:" msgid "" "Denoise filtering reduces or removes the appearance of noise and grain.\n" "Film grain and other types of high frequency noise are difficult to compress.\n" "Using this filter on such sources can result in smaller file sizes." msgstr "デノイズフィルターはノイズやグレインの発生を抑えるか除去します。\nフィルムグレインやその他の高周波ノイズを圧縮することは困難です。\nこのフィルターを使用することでそのようなソースでもファイルサイズを抑えることができます。" msgid "Denoise Preset:" msgstr "デノイズプリセット:" msgid "Denoise Tune:" msgstr "デノイズ調整:" msgid "" "Custom denoise filter string format\n" "\n" "SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgstr "カスタムデノイズフィルター文字列形式\n\nSpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgid "Detelecine:" msgstr "デテレシネ:" msgid "" "This filter removes 'combing' artifacts that are the result of telecining.\n" "\n" "Telecining is a process that adjusts film framerates that are 24fps to NTSC video frame rates which are 30fps." msgstr "このフィルターはテレシネの結果発生した 'コーミング' アーチファクトを除去します。\n\nテレシネとはフィルムのフレームレート 24fps を NTSC 映像のフレームレート 30fps に変換する作業です。" msgid "" "Custom detelecine filter string format\n" "\n" "JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgstr "カスタムデレシネフィルター文字列形式\n\nJunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgid "Decomb" msgstr "Decomb" msgid "" "Choose decomb or deinterlace filter options.\n" "\n" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced.\n" "\n" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "Decomb かデインターレースフィルターオプションを選択してください。\n\nDecomb フィルターはインターレース映像を見つけるとそのフレームのインターレースを選択的に解除します。これはインターレースではないフレームの画質を維持します。\n\n古典的なデインターレースフィルターはすべてのフレームに適用されます。\nこのため、インターレースではない映像は多少の画質の低下を招きます。" msgid "Deinterlace" msgstr "デインターレース" msgid "Decomb:" msgstr "Decomb:" msgid "" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced." msgstr "Decomb フィルターはインターレース映像を見つけるとそのフレームのインターレースを選択的に解除します。\nこれはインターレースではないフレームの画質を維持します。" msgid "" "Custom decomb filter string format\n" "\n" "Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\n" "BlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\n" "ErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgstr "カスタム Decomb フィルター文字列形式\n\nMode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\nBlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\nErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgid "Deinterlace:" msgstr "デインターレース:" msgid "" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "古典的なデインターレースフィルターはすべてのフレームに適用されます。\nこのため、インターレースではない映像は多少の画質の低下を招きます。" msgid "" "Custom deinterlace filter string format\n" "\n" "YadifMode:YadifParity:McdintMode:McdeintQp" msgstr "カスタムデインターレースフィルター文字列形式\n\nYadifMode:YadifParity:McdintMode:McdeintQp" msgid "Filters" msgstr "フィルター" msgid "Picture" msgstr "映像" msgid "Video Encoder:" msgstr "ビデオエンコーダー:" msgid "Available video encoders." msgstr "利用可能なビデオエンコーダーです。" msgid "Framerate:" msgstr "フレームレート:" msgid "" "Output framerate.\n" "\n" "'Same as source' is recommended. If your source video has\n" "a variable framerate, 'Same as source' will preserve it." msgstr "出力フレームレートです。\n\n'ソースと同じ' が推奨です。ソースビデオが可変フレームレートの場合、'ソースと同じ' にしておけばそれを維持します。" msgid "Constant Framerate" msgstr "固定フレームレート" msgid "Same as source" msgstr "ソースと同じ" msgid "kbps" msgstr "kbps" msgid "(variable)" msgstr "(可変)" msgid "(constant)" msgstr "(固定)" msgid "Enables constant framerate output." msgstr "固定フレームレート出力を有効にします。" msgid "Peak Framerate (VFR)" msgstr "ピークフレームレート (VFR)" msgid "" "Enables variable framerate output with a peak\n" "rate determined by the framerate setting.\n" "\n" "VFR is not compatible with some players." msgstr "可変フレームレート出力を有効にします。\nピークレートはフレームレート設定から決められます。\n\nVFR は一部のプレーヤーと互換性がありません。" msgid "Variable Framerate" msgstr "可変フレームレート" msgid "" "Enables variable framerate output.\n" "\n" "VFR is not compatible with some players." msgstr "可変フレームレートを有効にします。\n\nVFR は一部のプレーヤーと互換性がありません。" msgid "" "Set the desired quality factor.\n" "The encoder targets a certain quality.\n" "The scale used by each video encoder is different.\n" "\n" "x264's scale is logarithmic and lower values correspond to higher quality.\n" "So small decreases in value will result in progressively larger increases\n" "in the resulting file size. A value of 0 means lossless and will result\n" "in a file size that is larger than the original source, unless the source\n" "was also lossless.\n" "\n" "FFMpeg's and Theora's scale is more linear.\n" "These encoders do not have a lossless mode." msgstr "希望する画質要素で指定します。\nエンコーダーはその画質を目標に処理します。\nエンコーダーによって数値の意味が異なります。\n\nx264 では対数的であり、値が小さいほど高い画質になります。すなわち、値を小さくするとファイルサイズが大きくなります。値 0 はロスレスを意味し、出力ファイルサイズは、オリジナルもロスレスでないかぎり、オリジナルより大きくなります。\n\nFFMpeg および Theora の場合はより直線的です。\nこれらエンコーダーにロスレスモードはありません。" msgid "Constant Quality:" msgstr "画質指定:" msgid "Bitrate (kbps): " msgstr "ビットレート指定 (kbps):" msgid "" "Set the average bitrate.\n" "\n" "The instantaneous bitrate can be much higher or lower at any point in time.\n" "But the average over a long duration will be the value set here. If you need\n" "to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings." msgstr "平均ビットレートで指定します。\n\n瞬間のビットレートはそのときの映像によって大きく変化しますが、平均するとだいたいこの値になります。\nビットレートの最大値を制限したい場合は、x264 の vbv-bufsize および vbv-maxrate 設定を調べてください。" msgid "2-Pass Encoding" msgstr "2 パスエンコーディング" msgid "" "Perform 2 Pass Encoding.\n" "\n" "The 'Bitrate' option is prerequisite. During the 1st pass, statistics about\n" "the video are collected. Then in the second pass, those statistics are used\n" "to make bitrate allocation decisions." msgstr "2 パスエンコーディングを実施します。\n\nこれを行うには 'ビットレート指定' オプションの選択が必要です。1 回めのパスで映像の統計情報を採取し、2 回めのパスで統計情報を利用してビットレートの割り当てが行われます。" msgid "Turbo First Pass" msgstr "第 1 パスをターボ" msgid "" "During the 1st pass of a 2 pass encode, use settings that speed things " "along." msgstr "2 パスエンコードの 1 回めのパスを高速化します。" msgid "Use Advanced Options" msgstr "詳細オプションを使用する" msgid "" "Use advanced options Tab for x264 settings.\n" "\n" "Use at your own risk!" msgstr "x264 設定の詳細オプションタブを使用します。\n\n自己責任で使ってください!" msgid "Preset:" msgstr "プリセット:" msgid "" "Adjusts encoder settings to trade off compression efficiency against encoding speed.\n" "\n" "This establishes your default encoder settings.\n" "Tunes, profiles, levels and advanced option string will be applied to this.\n" "You should generally set this option to the slowest you can bear since slower\n" "settings will result in better quality or smaller files." msgstr "圧縮率とエンコード速度のトレードオフとなるエンコーダー設定を調整します。\n\nここでデフォルトのエンコーダー設定を指定します。\nTune、プロファイル、レベルおよび詳細オプション文字列がここに適用されます。\nこのオプションの全体的な設定を、まずはもっとも遅いものにし、そこからあなたが満足する速度、画質、あるいはファイルサイズのバランスを見つけるとよいでしょう。" msgid "Tune:" msgstr "" msgid "" "Tune settings to optimize for common scenarios.\n" "\n" "This can improve effeciency for particular source characteristics or set\n" "characteristics of the output file. Changes will be applied after the\n" "preset but before all other parameters." msgstr "Tune 設定で一般的な状況の最適化を行います。\n\nここでソース固有の特性の効率的な改善や出力ファイルの特性の設定が行えます。この変更はプリセットの後に適用され、その他のすべてのパラメーターより先に適用されます。" msgid "Fast Decode" msgstr "高速デコード" msgid "" "Reduce decoder CPU usage.\n" "\n" "Set this if your device is struggling to play the output (dropped frames)." msgstr "デコーダーの CPU 使用率を抑制します。\n\nあなたのデバイスが再生に悪戦苦闘している場合 (フレームドロップなど) に設定してみてください。" msgid "Zero Latency" msgstr "ゼロレイテンシ" msgid "" "Minimize latency between input to encoder and output of decoder.\n" "\n" "This is useful for broadcast of live streams.\n" "\n" "Since HandBrake is not suitable for live stream broadcast purposes,\n" "this setting is of little value here." msgstr "エンコーダーへの入力とデコーダーからの出力の間のレイテンシを最小化します。\n\nこれはライブストリーム配信に有用です。\n\nHandBrake はライブストリーム配信用途には適していないので、この設定もあまり役に立ちません。" msgid "Profile:" msgstr "プロファイル:" msgid "" "Sets and ensures compliance with the specified profile.\n" "\n" "Overrides all other settings." msgstr "設定されたプロファイルに従ってエンコードを行います。\n\n他のすべての設定を上書きします。" msgid "Level:" msgstr "レベル:" msgid "" "Sets and ensures compliance with the specified level.\n" "\n" "Overrides all other settings." msgstr "指定されたレベルに従ってエンコードを行います。\n\n他のすべての設定を上書きします。" msgid "More Settings:" msgstr "追加設定:" msgid "" "Additional encoder settings.\n" "\n" "Colon separated list of encoder options." msgstr "追加のエンコーダーオプションを指定できます。\n\nエンコーダーオプションをコロンで区切って追加してください。" msgid "Video" msgstr "ビデオ" msgid "Selection Behavior:" msgstr "選択方法:" msgid "Remove" msgstr "除去" msgid "Available Languages" msgstr "利用可能な言語" msgid "Selected Languages" msgstr "選択した言語" msgid "Use only first encoder for secondary audio" msgstr "二次オーディオに先頭のエンコーダーのみ使用する" msgid "" "Only the primary audio track will be encoded with the full encoder list.\n" "All other secondary audio output tracks will be encoded with first encoder only." msgstr "最初のオーディオトラックのみ全エンコーダーリストでエンコードします。\nその他の二次オーディオの出力トラックは先頭のエンコーダーのみでエンコードされます。" msgid "Auto Passthru:" msgstr "自動パススルー:" msgid "MP3" msgstr "MP3" msgid "" "Enable this if your playback device supports MP3.\n" "This permits MP3 passthru to be selected when automatic passthru selection is enabled." msgstr "再生デバイスで MP3 をサポートしている場合有効にしてください。\n自動パススルー選択が有効になっている場合 MP3 パススルーが可能になります。" msgid "" "Enable this if your playback device supports AAC.\n" "This permits AAC passthru to be selected when automatic passthru selection is enabled." msgstr "再生デバイスで AAC をサポートしている場合有効にしてください。\n自動パススルー選択が有効になっている場合 AAC パススルーが可能になります。" msgid "" "Enable this if your playback device supports AC-3.\n" "This permits AC-3 passthru to be selected when automatic passthru selection is enabled." msgstr "再生デバイスで AC-3 をサポートしている場合有効にしてください。\n自動パススルー選択が有効になっている場合 AC-3 パススルーが可能になります。" msgid "" "Enable this if your playback device supports DTS.\n" "This permits DTS passthru to be selected when automatic passthru selection is enabled." msgstr "再生デバイスで DTS をサポートしている場合有効にしてください。\n自動パススルー選択が有効になっている場合 DTS パススルーが可能になります。" msgid "" "Enable this if your playback device supports DTS-HD.\n" "This permits DTS-HD passthru to be selected when automatic passthru selection is enabled." msgstr "再生デバイスで DTS-HD をサポートしている場合有効にしてください。\n自動パススルー選択が有効になっている場合 DTS-HD パススルーが可能になります。" msgid "Passthru Fallback:" msgstr "パススルーのフォールバック:" msgid "" "Set the audio codec to encode with when a suitable track can not be found " "for audio passthru." msgstr "オーディオパススルーに適したトラックが見つからなかったときにエンコードするオーディオコーデックを指定します。" msgid "Audio Encoder Settings:" msgstr "オーディオエンコーダー設定:" msgid "Each selected source track will be encoded with all selected encoders" msgstr "選択された各ソーストラックは選択されたすべてのエンコーダーでエンコードされます。" msgid "Encoder" msgstr "エンコーダー" msgid "Bitrate/Quality" msgstr "ビットレート/音質" msgid "Mixdown" msgstr "ミックスダウン" msgid "Samplerate" msgstr "サンプルレート" msgid "Gain" msgstr "ゲイン" msgid "Audio Defaults" msgstr "オーディオのデフォルト" msgid "Add new audio settings to the list" msgstr "新しいオーディオ設定をリストに追加します" msgid "Add All" msgstr "すべて追加" msgid "Add all audio tracks to the list" msgstr "すべてのオーディオトラックをリストに追加します" msgid "Reload Defaults" msgstr "デフォルトの再読み込み" msgid "Reload all audio settings from defaults" msgstr "すべてのオーディオ設定をデフォルトから再読み込みします" msgid "Audio List" msgstr "オーディオリスト" msgid "Preferred Language: None" msgstr "既定の言語: なし" msgid "Add Foreign Audio Search Pass" msgstr "外国語オーディオ検索パスを追加する" msgid "" "Add \"Foreign Audio Search\" when the default audio track is your preferred language.\n" "This search pass finds short sequences of foreign audio and provides subtitles for them." msgstr "デフォルトオーディオトラックが既定の言語のときに \"外国語オーディオ検索\" を追加します。\nこれは外国語オーディオの短いシーケンスを検索し、その字幕を提供します。" msgid "Add subtitle track if default audio is foreign" msgstr "デフォルトオーディオが外国語のとき字幕トラックを追加する" msgid "" "When the default audio track is not your preferred language, add a subtitle " "track." msgstr "デフォルトオーディオトラックが既定の言語ではないとき、字幕トラックを追加します。" msgid "Add Closed Captions when available" msgstr "クローズドキャプションが利用できれば追加する" msgid "" "Closed captions are text subtitles that can be added to any container as a " "soft subtitle track (not burned)" msgstr "クローズドキャプションはコンテナーにソフト字幕トラック (焼き込まない字幕) として追加可能なテキストの字幕です。" msgid "Subtitle Defaults" msgstr "字幕のデフォルト" msgid "Add new subtitle settings to the list" msgstr "新しい字幕設定をリストに追加します" msgid "Add all subtitle tracks to the list" msgstr "すべての字幕設定をリストに追加します" msgid "Reload all subtitle settings from defaults" msgstr "すべての字幕設定をデフォルトから再読込します" msgid "Subtitle List" msgstr "字幕リスト" msgid "Reference Frames:" msgstr "参照フレーム数:" msgid "" "Sane values are ~1-6. The more you add, the better the compression, but the slower the encode.\n" "Cel animation tends to benefit from more reference frames a lot more than film content.\n" "\n" "Note that many hardware devices have limitations on the number of supported reference\n" "frames, so if you're encoding for a handheld or standalone player, don't touch this unless\n" "you're absolutely sure you know what you're doing!" msgstr "妥当な値は 1~6 です。より大きくすると圧縮率が上がりますが、エンコードがより遅くなります。\nセルアニメーションでは、値を大きくすることによる恩恵がフィルムより大きくなる傾向にあります。\n\n多くのハードウェアデバイスではサポートする参照フレーム数に上限を設定しているため、携帯型あるいは独立型プレーヤーでの再生を想定している場合、この意味を完全に理解していないかぎり、この値は変更しないでください。" msgid "Maximum B-Frames:" msgstr "最大 B-フレーム数:" msgid "" "Sane values are ~2-5. This specifies the maximum number of sequential B-frames that the encoder can use.\n" "\n" "Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.\n" "Cel-animated source material and B-pyramid also significantly increase the usefulness of larger\n" "values.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off)." msgstr "妥当な値は 2~5 です。ここではエンコーダーが使用できる最大の連続 B-フレーム数を指定します。\n\nこの値を大きくしても、適応的 B-フレームに Optimal を指定しないと、ほとんどの場合たいして役に立ちません。\nセルアニメ化されているソース素材や B-ピラミッドも値を大きくしたときの有用性を著しく高めます。\n\niPod や同種のデバイス用の Baseline プロファイルでは、B-フレーム数は 0 (オフ) に設定されます。" msgid "Pyramidal B-Frames:" msgstr "ピラミッド状 B-フレーム数:" msgid "" "B-pyramid improves compression by creating a pyramidal structure (hence the name)\n" "of B-frames, allowing B-frames to reference each other to improve compression.\n" "\n" "Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit." msgstr "B-ピラミッドは、B-フレームのピラミッド状構造を作成することで B-フレームが相互に参照し合えるようにして圧縮率を向上させます。\n\n最大 B-フレーム数を 1 より大きくする必要があります。圧縮率が最大になるよう、適応的 B-フレームに Optimal を指定することを強く推奨します。" msgid "Weighted P-Frames:" msgstr "重み付き P-フレーム:" msgid "" "Performs extra analysis to decide upon weighting parameters for each frame.\n" "\n" "This improves overall compression slightly and improves the quality of fades greatly.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires weighted P-frame\n" "prediction to be disabled. Note that some devices and players, even those that support\n" "Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is\n" "completely incompatible with it, for example." msgstr "各フレームごとに重み付けパラメーターを決定するための拡張解析を行います。\n\nこれは全体の圧縮率をわすかに向上させ、フェードの質を大きく向上させます。\n\niPod や同種のデバイス用の Baseline プロファイルでは、重み付き P-フレームの予測は無効になります。一部のデバイスやプレーヤーでは、それらが Main プロファイルをサポートしていても、重み付き P-フレームの予測に関する問題が発生する場合があります: 例えば Apple TV はこれと完全に互換がありません。" msgid "8x8 Transform" msgstr "8x8 変換" msgid "" "The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.\n" "\n" "It improves compression by at least 5% at a very small speed cost and may\n" "provide an unusually high visual quality benefit compared to its compression\n" "gain. However, it requires High Profile, which many devices may not support." msgstr "8x8 変換は、スピードごとの圧縮に関して、単独でもっとも有用な x264 の機能です。\n\nこれは非常に小さな速度コストで圧縮率が少なくとも 5% 向上し、圧縮率の向上と比較して非常に高い画質を提供する可能性があります。ただし、High プロファイルの指定が必要で、このプロファイルは多くのデバイスでサポートされていない可能性があります。" msgid "CABAC Entropy Encoding" msgstr "CABAC エントロピー符号化" msgid "" "After the encoder has done its work, it has a bunch of data that\n" "needs to be compressed losslessly, similar to ZIP or RAR. H.264 provides\n" "two options for this: CAVLC and CABAC. CABAC decodes a lot slower but\n" "compresses significantly better (10-30%), especially at lower bitrates.\n" "\n" "If you're looking to minimize CPU requirements for video playback, disable this option.\n" "Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled." msgstr "エンコーダーは処理の完了後、ZIP や RAR のようなロスレス圧縮に必要なデータ群を持っています。H.264 はこれに対して CAVLC と CABAC の 2 つのオプションを提供しています。CABAC はデコードが遅くなりますが圧縮率はわずかに優れています (10-30%)。ビットレートが低いと特に。\n\n動画再生時の CPU 負荷を最小化したい場合、このオプションを無効にしてください。\niPod や同種のデバイス用の Baseline プロファイルでは、CABAC を無効にする必要があります。" msgid "Encoding Features" msgstr "エンコード機能" msgid "Motion Est. Method:" msgstr "動き予測方式:" msgid "" "Controls the motion estimation method.\n" "\n" "Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.\n" "A better motion search method improves compression at the cost of speed.\n" "\n" "Diamond: performs an extremely fast and simple search using a diamond pattern.\n" "Hexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n" "Uneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n" "Exhaustive: performs a \"dumb\" search of every pixel in a wide area. Significantly slower for only a small compression gain.\n" "Transformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement." msgstr "動き予測方式を指定します。\n\n動き予測は、エンコーダーが、フレーム内のピクセルの各ブロックがどう動くかを予測します。\n\nより良い動き検索方式は速度を犠牲にして圧縮率を向上させます。\n\nDiamond: 非常に高速で、ダイアモンドパターンでシンプルに検索します。\nHexagon: 少し効果的ですが、検索がわずかに遅くなります。\nUneven Multi-Hex: さまざまなパターンを使用してより広く検索します。複雑な動きもより正確に捕捉します。\nExhaustive: 広い領域ですべてのピクセルを \"Dumb\" 検索します。非常に遅いわりに圧縮率の向上は小さいです。\nTransformed Exhaustive: Exhaustive と似ていますが、より正確に判定します。結果的に少し遅くなり、小さく向上します。" msgid "Subpel ME & Mode:" msgstr "サブペル動き予測 & モード:" msgid "" "This setting controls both subpixel-precision motion estimation and mode decision methods.\n" "\n" "Subpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n" "Mode decision is the method used to choose how to encode each block of the frame: a very important decision.\n" "SAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n" "6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n" "11 disables all early terminations in analysis.\n" "10 and 11, the most powerful and slowest options, require adaptive quantization (aq-mode > 0) and trellis 2 (always)." msgstr "サブピクセル高精細動き予測およびモード判定方式を指定します。\n\nサブピクセル動き予測は、単なるピクセル精度を越える、洗練された動き予測で、圧縮率を向上させます。\nモード判定はフレームの各ブロックをどうエンコードするかの選択に使用されます: 非常時重要な判定です。\nSAD はもっとも速い方式で、続いて SATD、RD、RD リファイン、もっとも遅いのが QPRD になります。\n6 以上の指定を強く推奨します: Psy-RD は非常に強力な Psy 最適化で、細部の保持に効果がありますが、これには RD が必要です。\n11 は解析中の早期中止を無効にします。\n10 と 11 はもっとも強力かつもっとも遅いオプションで、適応的量子化 (適応的量子化モード > 0) およびトレリス 2 (常に) の指定が必要です。" msgid "Motion Est. Range:" msgstr "動き予測範囲:" msgid "" "This is the distance x264 searches from its initial guess at the\n" "motion of a block in order to try to find its actual motion.\n" "\n" "The default is fine for most content, but extremely high motion video,\n" "especially at HD resolutions, may benefit from higher ranges, albeit at\n" "a high speed cost." msgstr "x264 がその実際の動きの検索を行うために 1 ブロックの動きの最初の推測からの距離になります。\n\nデフォルト値でほとんどのコンテンツで良い結果になりますが、動きが非常に激しい動画 (特に HD 映像) ではより高い値にしたほうがよいかもしれません。" msgid "Adaptive Direct Mode:" msgstr "適応的ダイレクトモード:" msgid "" "H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n" "\n" "Spatial, the default, is almost always better, but temporal is sometimes useful too.\n" "x264 can, at the cost of a small amount of speed (and accordingly for a small compression gain),\n" "adaptively select which is better for each particular frame." msgstr "H.264 では B-フレームにおいて、Spatial と Temporal の 2 つの異なる精細化モードを使用できます。\n\nデフォルトの Spatial はほとんどの場合に適していますが、Temporal も時に有用です。\nx264 は、小さい速度量を犠牲にして (その結果圧縮率も小さく向上)、特定のフレームごとにどちらが適しているか自動的に選択できます。" msgid "Adaptive B-Frames:" msgstr "適応的 B-フレーム:" msgid "" "x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n" "\n" "Fast mode takes roughly the same amount of time no matter how many B-frames you specify.\n" "However, while fast, its decisions are often suboptimal.\n" "\n" "Optimal mode gets slower as the maximum number of B-Frames increases,\n" "but makes much more accurate decisions, especially when used with B-pyramid." msgstr "x264 は B-フレームをいつ、どう使用するかを決めるさまざまなアルゴリズムを具備しています。\n\nFast モードは、指定された B-フレーム数に構わずおおざっぱに同じ時間量をとります。ただし、高速なのでまれに次善の策となりえます。\n\nOptimal モードは最大 B-フレーム数が増加し遅くなりますが、より正確に判定します (B-ピラミッドを使用したときは特に)。" msgid "Partitions:" msgstr "パーティション:" msgid "" "Mode decision picks from a variety of options to make its decision:\n" "this option chooses what options those are.\n" "\n" "Fewer partitions to check means faster encoding, at the cost of worse\n" "decisions, since the best option might have been one that was turned off." msgstr "モード判定は、その判定のためにさまざまなオプションから選別します: ここではそれらをどう選択するかを指定します。\n\nチェックするパーティションが少ないと、最適のオプションを見逃してしまうかもしれませんが、エンコードは速くなります。" msgid "Trellis:" msgstr "トレリス:" msgid "" "Trellis fine-tunes the rounding of transform coefficients to\n" "squeeze out 3-5% more compression at the cost of some speed.\n" "\n" "\"Always\" uses trellis not only during the main encoding process, but also\n" "during analysis, which improves compression even more, albeit at great speed cost.\n" "\n" "Trellis costs more speed at higher bitrates and requires CABAC." msgstr "トレリスは、変換係数の丸めを微調整します。速度を犠牲にして圧縮率が 3-5% 向上します。\n\n\n\"常に\" はメインエンコーディングプロセス中のみならず、解析中もトレリスを使用します。より一層圧縮率が上がりますが、速度コストも大きくなります。\n\nトレリスは高ビットレート時により速度が落ち、CABAC が必要です。" msgid "Analysis" msgstr "解析" msgid "Adaptive Quantization Strength:" msgstr "適応的量子化の強さ:" msgid "" "Adaptive quantization controls how the encoder distributes bits across the frame.\n" "\n" "Higher values take more bits away from edges and complex areas to improve areas with finer detail." msgstr "適応的量子化はエンコーダーがフレームにわたってどうビットを配分するか制御します。\n\nこの値を高くすると、領域の細部の画質を向上させるため、エッジから離れたところや複雑な領域により多くのビットを使用します。" msgid "Psychovisual Rate Distortion:" msgstr "心理視覚レート歪み:" msgid "" "Psychovisual rate-distortion optimization takes advantage of the characteristics of human\n" "vision to dramatically improve apparent detail and sharpness.\n" "The effect can be made weaker or stronger by adjusting the strength.\n" "Being an RD algorithm, it requires mode decision to be at least \"6\"." msgstr "心理視覚レート歪み最適化は人間の視覚の特性を利用して細部と鮮明さを劇的に向上させます。\n強さを調整して、より弱く、またはより強くエフェクトがかかります。\nRD アルゴリズムにした場合、モード判定は \"6\" 以上にしなくてはなりません。" msgid "Psychovisual Trellis:" msgstr "心理視覚トレリス:" msgid "" "Psychovisual trellis is an experimental algorithm to further\n" "improve sharpness and detail retention beyond what Psychovisual RD does.\n" "\n" "Recommended values are around 0.2, though higher values may help for very\n" "grainy video or lower bitrate encodes. Not recommended for cel animation\n" "and other sharp-edged graphics." msgstr "心理視覚トレリスは、心理視覚 RD を超える、鮮明さと細部の保持をより向上させる試験的アルゴリズムです。\n\n推奨値は 0.2 前後です。値を大きくすると非常にきめの粗い動画になるか、ビットレートが下がります。セルアニメーションなどの輪郭のはっきりした映像には向きません。" msgid "Deblocking: " msgstr "デブロック化: " msgid "" "H.264 deblocking filter.\n" "\n" "h.264 has a built-in deblocking filter that smooths out blocking artifacts\n" "after decoding each frame. This not only improves visual quality, but also\n" "helps compression significantly. The deblocking filter takes a lot of CPU power,\n" "so if you're looking to minimize CPU requirements for video playback, disable it.\n" "\n" "The deblocking filter has two adjustable parameters, \"strength\" (Alpha) and \"threshold\" (Beta).\n" "The former controls how strong (or weak) the deblocker is, while the latter controls how many\n" "(or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking.\n" "The default is 0 (normal strength) for both parameters." msgstr "H.264 デブロックフィルターです。\n\nH.264 は組み込みのデブロックフィルターを持ち、各フレームのデコード後にブロックアーチファクトをスムーズにします。これは表示品質だけでなく、圧縮率も著しく向上します。デブロックフィルターは多くのの CPU パワーを消費するため、低速の CPU で再生する可能性がある場合無効にしてください。\n\nデブロックフィルターは調整可能なパラメーターに \"強さ\" (Alpha) と \"しきい値\" (Beta) があります。\n前者はどの程度の強さ (または弱さ) でデブロック化を行うかを指定するのに対し、後者はどの程度の多さ (または少なさ) のエッジに適用するかを指定します。小さい値は少ないデブロック化を、大きい値はより多くのデブロック化を行います。\n両パラメーターのデフォルト値は 0 (通常の強さ) です。" msgid "No DCT Decimate" msgstr "DCT デシメートなし" msgid "" "x264 normally zeroes out nearly-empty data blocks to save bits to\n" "be better used for some other purpose in the video. However, this can\n" "sometimes have slight negative effects on retention of subtle grain and\n" "dither.\n" "\n" "Don't touch this unless you're having banding issues or other such cases\n" "where you are having trouble keeping fine noise." msgstr "x264 は通常、ビットの節約と動画の他の一部の目的でよりよく使用するため、ほぼ空のデータブロックをゼロ設定にします。しかし、これはまれに微妙なグレインやディザーの残留に対してわずかながら悪影響になりえます。\n\nあなたがバンディングやその他の明らかにノイズが残る問題を抱えていないかぎりこのオプションを変更しないでください。" msgid "Psychovisual" msgstr "心理視覚" msgid "" "Your selected options will appear here.\n" "You can edit these and add additional options.\n" "\n" "Default values will not be shown. The defaults are:\n" "ref=3:bframes=3:b-adapt=fast:direct=spatial:\n" "b-pyramid=normal:weightp=2:me=hex:merange=16:\n" "subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\n" "deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\n" "no-fast-pskip=0:no-dct-decimate=0:cabac=1" msgstr "あなたが選択したオプションがここに表示されます。\nここでそれらを編集したり任意のオプションを追加したりできます。\n\nデフォルト値は表示されません。デフォルトは以下のとおりです:\nref=3:bframes=3:b-adapt=fast:direct=spatial:\nb-pyramid=normal:weightp=2:me=hex:merange=16:\nsubme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\ndeblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\nno-fast-pskip=0:no-dct-decimate=0:cabac=1" msgid "Current x264 Advanced Option String" msgstr "現在の x264 詳細オプション文字列" msgid "Advanced Video" msgstr "ビデオ詳細" msgid "Chapter Markers" msgstr "チャプターマーカー" msgid "Add chapter markers to output file." msgstr "出力ファイルにチャプターマーカーを追加します。" msgid "Chapters" msgstr "チャプター" msgid "Actors:" msgstr "出演者:" msgid "Director:" msgstr "監督:" msgid "Release Date:" msgstr "公開日:" msgid "Comment:" msgstr "コメント:" msgid "Genre:" msgstr "ジャンル:" msgid "Description:" msgstr "説明:" msgid "Plot:" msgstr "プロット:" msgid "Tags" msgstr "タグ" msgid "Settings" msgstr "設定" msgid "Edit" msgstr "編集" msgid "Reload" msgstr "再読み込み" msgid "" "Mark selected queue entry as pending.\n" "Resets the queue job to pending and ready to run again." msgstr "選択されたキューエントリは待機中になります。\nジョブは待機中にリセットされ、実行を待ちます。" msgid "Reload All" msgstr "すべて再読み込み" msgid "" "Mark all queue entries as pending.\n" "Resets all queue jobs to pending and ready to run again." msgstr "すべてのキューエントリは待機中になります。\nすべてのジョブは待機中にされ、再実行を待ちます。" msgid "OK" msgstr "OK" msgid "Select All" msgstr "すべて選択" msgid "Mark all titles for adding to the queue" msgstr "キューに追加するために全タイトルをマークします" msgid "Clear All" msgstr "すべてクリア" msgid "Unmark all titles" msgstr "全タイトルのマークを外します" msgid "Destination files OK. No duplicates detected." msgstr "保存ファイル OK。重複は検出されませんでした。" msgid "Select this title for adding to the queue.\n" msgstr "キューに追加するためにこのファイルを選択します。\n" msgid "Preferences" msgstr "設定" msgid "Automatically check for updates" msgstr "アップデートを自動的にチェックする" msgid "When all encodes are complete" msgstr "すべてのエンコードが完了したとき" msgid "Use automatic naming (uses modified source name)" msgstr "自動名前付けを使用する (修正されたソース名を使用)" msgid "Auto-Name Template" msgstr "自動名前付けテンプレート" msgid "" "Available Options: {source} {title} {chapters} {date} {time} {quality} " "{bitrate}" msgstr "利用できるオプション: {source} {title} {chapters} {date} {time} {quality} {bitrate}" msgid "Use iPod/iTunes friendly (.m4v) file extension for MP4" msgstr "MP4 ファイルにおいて、iPod/iTunes フレンドリなファイル拡張子 (.m4v) を使用する" msgid "Number of previews" msgstr "プレビューの数" msgid "Filter short titles (seconds)" msgstr "短いタイトルを無視 (秒)" msgid "Show system tray icon" msgstr "システムトレイアイコンを表示する" msgid "General" msgstr "一般" msgid "Constant Quality fractional granularity" msgstr "固定品質の分画粒度" msgid "Use dvdnav (instead of libdvdread)" msgstr "dvdnav を使用する (libdvdread を使用しない)" msgid "Put individual encode logs in same location as movie" msgstr "個別のエンコードログを動画と同じ場所に出力する" msgid "Activity Log Verbosity Level" msgstr "アクティビティログ詳細レベル" msgid "Activity Log Longevity" msgstr "アクティビティログ保存期間" msgid "Scale down High Definition previews" msgstr "HD 画像のプレビューは縮小する" msgid "Automatically Scan DVD when loaded" msgstr "DVD が挿入されたら自動的にスキャンする" msgid "Scans the DVD whenever a new disc is loaded" msgstr "新しいディスクが挿入されるたびに DVD をスキャンします" msgid "Hide Advanced Video Options Tab" msgstr "ビデオ詳細タブを表示しない" msgid "" "Use advanced video options at your own risk.\n" "We recommend that you use the controls available\n" "on the Video tab instead." msgstr "ビデオ詳細は自己責任で使用してください。\n私たちはビデオタブで設定できる範囲での使用を推奨します。" msgid "Delete completed jobs from queue" msgstr "完了したジョブをキューから削除する" msgid "" "By default, completed jobs remain in the queue and are marked as complete.\n" "Check this if you want the queue to clean itself up by deleting completed jobs." msgstr "デフォルトでは、完了したジョブは完了とマークされてキューに残ります。\n完了したらキューから消したいときはこのオプションにチェックマークをつけてください。" msgid "Allow Tweaks" msgstr "" msgid "Allow HandBrake For Dummies" msgstr "" msgid "Advanced" msgstr "詳細" msgid "Folder Name:" msgstr "フォルダー名:" msgid "Description" msgstr "説明" msgid "Preset Name:" msgstr "プリセット名:" msgid "Custom Picture Dimensions" msgstr "カスタム映像サイズ" msgid "Maximum Width:" msgstr "最大の幅:" msgid "Enable maximum width limit." msgstr "最大幅の制限を有効にします。" msgid "" "This is the maximum width that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source width is greater.\n" "Setting this to 0 means there is no maximum width." msgstr "出力される動画の最大画像幅です。\n\n新しいソースが読み込まれるたびに、ソースがこの値より大きければこの値が適用されます。\n\n0 の場合最大幅を制限しません。" msgid "Maximum Height:" msgstr "最大の高さ:" msgid "Enable maximum height limit." msgstr "最大の高さを制限を有効にします。" msgid "" "This is the maximum height that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source height is greater.\n" "Setting this to 0 means there is no maximum height." msgstr "出力される動画の画像の最大の高さです。\n\n新しいソースが読み込まれるたびに、ソースがこの値より大きければこの値が適用されます。\n\n0 の場合高さを制限しません。" msgid "Select preview frames." msgstr "プレビューフレームを選択します。" msgid "" "Encode and play a short sequence of video starting from the current preview " "position." msgstr "現在のプレビューポジションからの短いシーケンスをエンコードおよび再生します。" msgid "Duration:" msgstr "時間:" msgid "Set the duration of the live preview in seconds." msgstr "ライブプレビューする時間を秒で指定します。" msgid "Show Crop" msgstr "クロップを表示" msgid "Show Cropped area of the preview" msgstr "プレビューのクロップした領域を表示します" msgid "Fullscreen" msgstr "全画面化" msgid "View Fullscreen Preview" msgstr "全画面でプレビューを表示します" msgid "Title Number:" msgstr "タイトル番号:" msgid "Detected DVD devices:" msgstr "検出 DVD デバイス:" msgid "Setting:" msgstr "設定:" msgid "Import SRT" msgstr "SRT のインポート" msgid "Enable settings to import an SRT subtitle file" msgstr "SRT 字幕ファイルをインポートする設定を有効にします" msgid "Embedded Subtitle List" msgstr "埋め込み字幕リスト" msgid "Enable settings to select embedded subtitles" msgstr "埋め込み字幕を選択する設定を有効にします" msgid "Character Code" msgstr "文字コード" msgid "Offset (ms)" msgstr "オフセット (ミリ秒)" msgid "" "Set the language of this subtitle.\n" "This value will be used by players in subtitle menus." msgstr "この字幕の言語を設定します。\nこの値がプレーヤーの字幕メニューで表示されます。" msgid "" "Set the character code used by the SRT file you are importing.\n" "\n" "SRTs come in all flavours of character sets.\n" "We translate the character set to UTF-8.\n" "The source's character code is needed in order to perform this translation." msgstr "インポートする SRT ファイルで使用されている文字コードを指定します。\n\nSRT には決まった文字セットがありません。\nHandBrake では文字セットを UTF-8 に変換します。\nこの変換のため、ソースの文字コードが必要になります。" msgid "Select the SRT file to import." msgstr "インポートする SRT ファイルを選択します。" msgid "Srt File" msgstr "SRT ファイル" msgid "Adjust the offset in milliseconds between video and SRT timestamps" msgstr "動画と SRT タイムスタンプとの間のオフセットをミリ秒で指定します。" msgid "Track" msgstr "トラック" msgid "List of subtitle tracks available from your source." msgstr "ソースから利用できる字幕トラックのリストです。" msgid "Forced Subtitles Only" msgstr "強制字幕のみ" msgid "Burn into video" msgstr "ビデオに焼きこむ" msgid "" "Render the subtitle over the video.\n" "The subtitle will be part of the video and can not be disabled." msgstr "字幕を映像上に書き込みます。\n字幕は映像の一部となり無効にできません。" msgid "Set Default Track" msgstr "デフォルトトラックの設定" msgid "Source Track" msgstr "ソーストラック" msgid "List of audio tracks available from your source." msgstr "ソースから利用できるオーディオトラックのリストです。" msgid "Track Name:" msgstr "トラック名:" msgid "" "Set the audio track name.\n" "\n" "Players may use this in the audio selection list." msgstr "オーディオトラック名を設定します。\n\nプレーヤーはオーディオ選択リストでこの文字列を使用します。" msgid "Mix" msgstr "Mix" msgid "Sample Rate" msgstr "サンプルレート" msgid "" "Dynamic Range Compression: Adjust the dynamic range of the output audio track.\n" "\n" "For source audio that has a wide dynamic range (very loud and very soft sequences),\n" "DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder." msgstr "ダイナミックレンジ圧縮: 出力オーディオトラックのダイナミックレンジを調整します。\n\nソースオーディオのダイナミックレンジが広い (とても大きい/とても小さいシーケンス) の場合、DRC は音量を下げて/上げてレンジを '圧縮' できます。" msgid "Enable bitrate setting" msgstr "ビットレート設定を有効にします" msgid "Enable quality setting" msgstr "音質指定を有効にします" msgid "" "Quality: For output codec's that support it, adjust the quality of " "the output." msgstr "音質: コーデックがサポートしている場合、出力の音質を調整します。" msgid "00.0" msgstr "00.0" msgid "" "Audio Gain: Adjust the amplification or attenuation of the output " "audio track." msgstr "オーディオゲイン: 出力オーディオトラックの増幅/減衰を調整します。" msgid "Skip This Version" msgstr "このバージョンをスキップ" msgid "Remind Me Later" msgstr "後で" msgid "A new version of HandBrake is available!" msgstr "新しいバージョンの HandBrake が利用できます!" msgid "HandBrake xxx is now available (you have yyy)." msgstr "" msgid "Release Notes" msgstr "リリースノート" msgid "First Track Matching Selected Languages" msgstr "選択した言語と一致する最初のトラック" msgid "All Tracks Matching Selected Languages" msgstr "選択した言語と一致するすべてのトラック" msgid "Chapters:" msgstr "チャプター:" msgid "Seconds:" msgstr "秒:" msgid "Frames:" msgstr "フレーム:" msgid "Do Nothing" msgstr "なにもしない" msgid "Show Notification" msgstr "通知する" msgid "Quit Handbrake" msgstr "Handbrake を終了する" msgid "Put Computer To Sleep" msgstr "コンピューターをスリープする" msgid "Shutdown Computer" msgstr "コンピューターをシャットダウンする" msgid "Week" msgstr "週" msgid "Month" msgstr "月" msgid "Year" msgstr "年" msgid "Immortal" msgstr "永久" msgid "Never" msgstr "しない" msgid "Daily" msgstr "日ごと" msgid "Weekly" msgstr "週ごと" msgid "Monthly" msgstr "月ごと" msgid "Default" msgstr "デフォルト" msgid "Fast" msgstr "Fast" msgid "Slow" msgstr "Slow" msgid "Slower" msgstr "Slower" msgid "Ultralight" msgstr "Ultralight" msgid "Light" msgstr "Light" msgid "Medium" msgstr "Medium" msgid "Strong" msgstr "Strong" msgid "Film" msgstr "Film" msgid "Grain" msgstr "Grain" msgid "High Motion" msgstr "High Motion" msgid "Animation" msgstr "Animation" msgid "Spatial" msgstr "Spatial" msgid "Temporal" msgstr "Temporal" msgid "Automatic" msgstr "自動" msgid "Optimal" msgstr "Optimal" msgid "Normal" msgstr "標準" msgid "Simple" msgstr "シンプル" msgid "Smart" msgstr "スマート" msgid "Diamond" msgstr "Diamond" msgid "Hexagon" msgstr "Hexagon" msgid "Uneven Multi-Hexagon" msgstr "Uneven Multi-Hexagon" msgid "Exhaustive" msgstr "Exhaustive" msgid "Hadamard Exhaustive" msgstr "Hadamard Exhaustive" msgid "Most" msgstr "大部分" msgid "Some" msgstr "一部" msgid "All" msgstr "すべて" msgid "Encode only" msgstr "エンコード時のみ" msgid "Always" msgstr "常に" msgid "(NTSC Film)" msgstr "(NTSC フィルム)" msgid "(PAL Film/Video)" msgstr "(PAL フィルム/ビデオ)" msgid "(NTSC Video)" msgstr "(NTSC ビデオ)" msgid "%d - %02dh%02dm%02ds - %s" msgstr "%d - %02d時間%02d分%02d秒 - %s" msgid "%d (%05d.MPLS) - %02dh%02dm%02ds" msgstr "%d (%05d.MPLS) - %02d時間%02d分%02d秒" msgid "%d (%05d.MPLS) - Unknown Length" msgstr "%d (%05d.MPLS) - 長さは不明" msgid "%d - %02dh%02dm%02ds" msgstr "%d - %02d時間%02d分%02d秒" msgid "%d - Unknown Length" msgstr "%d - 長さは不明" msgid "No Titles" msgstr "タイトルなし" msgid "No Audio" msgstr "オーディオなし" msgid "Foreign Audio Search" msgstr "外国語オーディオ検索" #, c-format msgid "Chapter %2d" msgstr "チャプター %2d" #, c-format msgid "N/A" msgstr "N/A" #, c-format msgid "" "Invalid Deinterlace Settings:\n" "\n" "%s\n" msgstr "不正なデインターレース設定:\n\n%s\n" #, c-format msgid "" "Invalid Detelecine Settings:\n" "\n" "%s\n" msgstr "不正なデテレシネ設定:\n\n%s\n" #, c-format msgid "" "Invalid Decomb Settings:\n" "\n" "%s\n" msgstr "不正な Decomb 設定:\n\n%s\n" msgid "" "Theora is not supported in the MP4 container.\n" "\n" "You should choose a different video codec or container.\n" "If you continue, FFMPEG will be chosen for you." msgstr "Theora は MP4 コンテナーではサポートされていません。\n\n他のビデオコーデックかコンテナーを選択してください。\nこのまま続行すると、FFMPEG が選択されます。" msgid "Continue" msgstr "続行" msgid "No title found.\n" msgstr "タイトルが見つかりませんでした。\n" msgid "" "Only one subtitle may be burned into the video.\n" "\n" "You should change your subtitle selections.\n" "If you continue, some subtitles will be lost." msgstr "ひとつの字幕のみ映像に焼きこむことができます。\n\n字幕の選択を変更してください。\nこのまま続行すると一部の字幕は失われます。" msgid "" "Srt file does not exist or not a regular file.\n" "\n" "You should choose a valid file.\n" "If you continue, this subtitle will be ignored." msgstr "SRT ファイルが存在しないか、正常ではありません。\n\n正しいファイルを選択してください。\nこのまま続行すると字幕は無視されます。" msgid "" "The source does not support Pass-Thru.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "ソースはパススルーをサポートしていません。\n\n別のオーディオコーデックを選択してください。\nこのまま続行するとどれかひとつが選択されます。" #, c-format msgid "" "%s is not supported in the %s container.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "%s は %s コンテナーではサポートされていません。\n\n別のオーディオコーデックを選択してください。\nこのまま続行するとどれかひとつが選択されます。" #, c-format msgid "" "The source audio does not support %s mixdown.\n" "\n" "You should choose a different mixdown.\n" "If you continue, one will be chosen for you." msgstr "ソースオーディオは %s ミックスダウンをサポートしていません。\n\n別のミックスダウンを選択してください。\nこのまま続行するとどれかひとつが選択されます。" #, c-format msgid "" "Unable to create %s.\n" "\n" "Internal error. Could not parse UI description.\n" "%s" msgstr "%s を作成できません。\n\n内部エラーです。UI 記述を解析できません。\n%s" msgid "Index" msgstr "インデックス" msgid "Duration" msgstr "時間" msgid "Title" msgstr "タイトル" msgid "Job Information" msgstr "ジョブ情報" msgid "Track Information" msgstr "トラック情報" msgid "Preset Name" msgstr "プリセット名" msgid "The device or file to encode" msgstr "エンコードするデバイスまたはファイル" msgid "The preset values to use for encoding" msgstr "エンコードで使用するプリセット値" msgid "Spam a lot" msgstr "" msgid "- Transcode media formats" msgstr "- メディアフォーマットをトランスコードします。" msgid "Globals" msgstr "全体" msgid "Presets" msgstr "プリセット" msgid "Folder" msgstr "フォルダー" #, c-format msgid "%s path: (%s)" msgstr "%s パス: (%s)" #, c-format msgid "%s indices: len %d" msgstr "%s インデックス: 長さ %d" msgid "Type" msgstr "タイプ" msgid "Failed to find parent folder when adding child." msgstr "子フォルダーを追加するときに親フォルダーの検索に失敗しました。" msgid "Failed to find parent folder while adding child." msgstr "子フォルダーを追加中に親フォルダーの検索に失敗しました。" #, c-format msgid "Can't map language value: (%s)" msgstr "言語値をマップできません: (%s)" #, c-format msgid "Can't map value: (%s)" msgstr "値をマップできません: (%s)" msgid "Subtitles" msgstr "字幕" msgid "" "%s: Folder already exists.\n" "You can not replace it with a preset." msgstr "%s: フォルダーがすでに存在します。\nプリセットで置き換えることはできません。" msgid "" "%s: Preset already exists.\n" "You can not replace it with a folder." msgstr "%s: プリセットはすでに存在します。\nフォルダーで置き換えることができません。" msgid "Import Preset" msgstr "プリセットのインポート" msgid "All (*)" msgstr "すべて (*)" msgid "Presets (*.plist)" msgstr "プリセット (*.plist)" msgid "Export Preset" msgstr "プリセットのエクスポート" msgid "" "Confirm deletion of %s:\n" "\n" "%s" msgstr "%s の削除の確認:\n\n%s" msgid "folder" msgstr "フォルダー" msgid "preset" msgstr "プリセット" msgid "No selection??? Perhaps unselected." msgstr "選択されていません?? おそらく選択が解除されています。" #, c-format msgid "Gstreamer Error: %s" msgstr "Gstreamer エラー: %s" #, c-format msgid "" "Missing GStreamer plugin\n" "Audio or Video may not play as expected\n" "\n" "%s" msgstr "GStreamer プラグインが見つかりません\nオーディオまたはビデオは期待通り再生されない場合があります\n\n%s" msgid "Done" msgstr "完了" msgid "Windowed" msgstr "元に戻す" msgid "Seconds" msgstr "秒" msgid "Frames" msgstr "フレーム" #, c-format msgid "" "%s (Title %d, %s %d through %d, 2 Video Passes) -->" " %s" msgstr "%s (タイトル %d, %s %d - %d, 2 ビデオパス) --> %s" #, c-format msgid "" "%s (Title %d, %s %d through %d) --> %s" msgstr "%s (タイトル %d, %s %d - %d) --> %s" #, c-format msgid "Modified Preset Based On: %s\n" msgstr "修正されたプリセット ベース: %s\n" #, c-format msgid "Preset: %s\n" msgstr "プリセット: %s\n" #, c-format msgid "Format: %s Container\n" msgstr "フォーマット: %s Container\n" msgid "Container Options:" msgstr "コンテナーオプション:" #, c-format msgid "%sChapter Markers" msgstr "%sチャプターマーカー" #, c-format msgid "%siPod 5G Support" msgstr "%siPod 5G サポート" #, c-format msgid "%sWeb Optimized" msgstr "%sウェブ最適化" #, c-format msgid "%sLarge File Size (>4GB)" msgstr "%s大きいファイルサイズ (>4GB)" #, c-format msgid "Destination: %s\n" msgstr "保存先: %s\n" msgid "(Aspect Preserved)" msgstr "(アスペクト比維持)" msgid "(Aspect Lost)" msgstr "(アスペクト比変更)" msgid "(Anamorphic)" msgstr "(アナモフィック)" msgid "(Custom Anamorphic)" msgstr "(カスタムアナモフィック)" msgid "Picture: " msgstr "映像: " #, c-format msgid "Source: %d x %d, Output %d x %d %s, Crop %d:%d:%d:%d" msgstr "ソース: %d x %d, 出力 %d x %d %s, クロップ %d:%d:%d:%d" #, c-format msgid ", Display %d x %d" msgstr ", 出力 %d x %d" msgid "Filters:" msgstr "フィルター:" #, c-format msgid "%sDetelecine" msgstr "%sデテレシネ" #, c-format msgid "%sDecomb" msgstr "%sDecomb" #, c-format msgid "%sDeinterlace" msgstr "%sデインターレース" #, c-format msgid "%sDenoise Filter %s:" msgstr "%sデノイズフィルター %s:" #, c-format msgid "%sDeblock: %d" msgstr "%sデブロック: %d" #, c-format msgid "%sGrayscale" msgstr "%sグレースケール" #, c-format msgid "Video: %s" msgstr "ビデオ: %s" #, c-format msgid ", Framerate: %s %s" msgstr ", フレームレート: %s %s" #, c-format msgid ", Framerate: Peak %s (may be lower)" msgstr ", フレームレート: ピーク %s (より低くなるかも)" #, c-format msgid ", Framerate: %s (constant frame rate)" msgstr ", フレームレート: %s (固定フレームレート)" msgid "Error" msgstr "エラー" msgid "Bitrate:" msgstr "ビットレート:" msgid "Bitrate" msgstr "ビットレート" msgid "Quality" msgstr "音質" msgid "Turbo 1st Pass: On\n" msgstr "第 1 パスのターボ: On\n" #, c-format msgid "Video Options: Preset: %s" msgstr "ビデオオプション: プリセット: %s" msgid " - Tune: " msgstr "" #, c-format msgid " - Profile: %s" msgstr " - プロファイル: %s" #, c-format msgid " - Level: %s" msgstr " - レベル: %s" #, c-format msgid "Advanced Options: %s\n" msgstr "詳細オプション: %s\n" msgid "Audio: " msgstr "オーディオ: " #, c-format msgid "Audio Tracks: %d" msgstr "オーディオトラック: %d" #, c-format msgid "Bitrate: %d" msgstr "ビットレート: %d" #, c-format msgid "%s --> Encoder: %s" msgstr "%s --> エンコーダー: %s" #, c-format msgid "%s --> Encoder: %s, Mixdown: %s, SampleRate: %s, %s" msgstr "%s --> エンコーダー: %s, ミックスダウン: %s, サンプルレート: %s, %s" msgid "Subtitle: " msgstr "字幕: " #, c-format msgid "Subtitle Tracks: %d\n" msgstr "字幕トラック: %d\n" msgid " (Force)" msgstr " (強制)" msgid " (Burn)" msgstr " (焼き込み)" msgid " (Default)" msgstr " (デフォルト)" #, c-format msgid " %s (%s), %s, Offset (ms) %d%s\n" msgstr " %s (%s), %s, オフセット (ms) %d%s\n" #, c-format msgid "" "Destination: %s\n" "\n" "Another queued job has specified the same destination.\n" "Do you want to overwrite?" msgstr "保存先: %s\n\n他のキュージョブが同じ保存先を指定しています。\n上書きしてよろしいですか?" msgid "Overwrite" msgstr "上書き" #, c-format msgid "" "Destination: %s\n" "\n" "This is not a valid directory." msgstr "保存先: %s\n\nこれは正しいディレクトリではありません。" #, c-format msgid "" "Destination: %s\n" "\n" "Can not read or write the directory." msgstr "保存先: %s\n\nディレクトリに読み書きできません。" #, c-format msgid "" "Destination filesystem is almost full: %uM free\n" "\n" "Encode may be incomplete if you proceed.\n" msgstr "保存先ファイルシステムがほぼいっぱいです: 空き %uM\n\nこのまま続けてもエンコードは完了しないかもしれません。\n" msgid "Proceed" msgstr "続行" #, c-format msgid "" "Destination: %s\n" "\n" "File already exists.\n" "Do you want to overwrite?" msgstr "保存先: %s\n\nファイルがすでに存在します。\n上書きしてよろしいですか?" msgid "" "Duplicate destination files detected.\n" "Duplicates will not be added to the queue." msgstr "保存ファイルの重複が検出されました。\n重複しているものはキューに追加されません。" msgid "No Title" msgstr "タイトルなし" msgid "" "There is another title with the same destination file name.\n" "This title will not be added to the queue unless you change\n" "the output file name.\n" msgstr "異なるタイトルで保存ファイル名が同じジョブが存在します。\n出力ファイル名を変更しないかぎりこのタイトルはキューに追加されません。\n" msgid "Stop" msgstr "停止" msgid "Stop Encoding" msgstr "エンコードを停止します" msgid "Resume" msgstr "再開" msgid "Resume Encoding" msgstr "エンコードの再開" msgid "S_top Queue" msgstr "キューの停止(_T)" msgid "_Start Queue" msgstr "キューを開始(_S)" msgid "_Resume Queue" msgstr "キューを再開(_R)" msgid "Resume Queue" msgstr "キューを再開します" msgid "" "You are currently encoding. What would you like to do?\n" "\n" msgstr "現在エンコード中です。どうしますか?\n\n" #, c-format msgid "" "You have %d unfinished job(s) in a saved queue.\n" "\n" "Would you like to reload them?" msgstr "未完のジョブ %d 件がキュー内に保存されています。\n\nこれらを読み込みますか?" msgid "No" msgstr "いいえ" msgid "Yes" msgstr "はい" #, c-format msgid "Usage: %s infile [outfile]\n" msgstr "使用法: %s 入力ファイル [出力ファイル]\n" #, c-format msgid "Offset: %dms" msgstr "オフセット: %dms" msgid "Burned Into Video" msgstr "映像に焼き込み" msgid "Passthrough" msgstr "パススルー" msgid "through" msgstr "から" msgid "(Forced Subtitles Only)" msgstr "(強制字幕のみ)" msgid "(Default)" msgstr "(デフォルト)" msgid "Error!" msgstr "エラー!" #, c-format msgid "Preferred Language: %s" msgstr "既定の言語: %s" #, c-format msgid "Add %s subtitle track if default audio is not %s" msgstr "デフォルトオーディオが %2$s でない場合 %1$s 字幕トラックを追加" #, c-format msgid "Type %s" msgstr "Type %s" #, c-format msgid "Type %s value %s" msgstr "Type %s value %s" #, c-format msgid "Type %s value %d" msgstr "Type %s value %d" #, c-format msgid "Type %s value %" msgstr "Type %s value %" #, c-format msgid "Type %s value %f" msgstr "Type %s value %f" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"%s\"" msgstr "%s\n\n拡張オプション:\n\"%s\"" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"\"" msgstr "%s\n\n拡張オプション:\n\"\"" msgid "Any" msgstr "どれでも" msgid "0: SAD, no subpel" msgstr "0: SAD, サブペルなし" msgid "4: SATD, qpel on all" msgstr "4: SATD, すべてに qpel" msgid "5: SATD, multi-qpel on all" msgstr "5: SATD, すべてに multi-qpel" msgid "6: RD in I/P-frames" msgstr "6: I/P-フレームに RD" msgid "7: RD in all frames" msgstr "7: 全フレームに RD" msgid "8: RD refine in I/P-frames" msgstr "8: I/P-フレームに RD リファイン" msgid "9: RD refine in all frames" msgstr "9: 全フレームに RD リファイン" msgid "10: QPRD in all frames" msgstr "10: 全フレームに QPRD" msgid "11: No early terminations in analysis" msgstr "11: 解析の早期中止なし" msgid "Your names" msgstr "Masato HASHIMOTO" msgid "Your emails" msgstr "cabezon.hashimoto@gmail.com" HandBrake-0.10.2/gtk/po/it_IT.po0000664000175200017520000014430512466165306016632 0ustar handbrakehandbrake# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Alexandru Grigoras , 2014 # Giuseppe Pignataro , 2014 msgid "" msgstr "" "Project-Id-Version: HandBrake-0.10\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-03 13:01+0300\n" "PO-Revision-Date: 2014-11-28 14:44+0000\n" "Last-Translator: VictorR2007 \n" "Language-Team: Italian (Italy) (http://www.transifex.com/projects/p/handbrake/language/it_IT/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: it_IT\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" msgid "Quality: " msgstr "Qualità:" #, c-format msgid "Bitrate: %dkbps" msgstr "" #, c-format msgid "Bitrate: %.4gkbps" msgstr "" msgid "Passthrough" msgstr "" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s\n" "Track Name: %s" msgstr "" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s" msgstr "" msgid "Add" msgstr "" msgid "" "Add an audio encoder.\n" "Each selected source track will be encoded with all selected encoders." msgstr "" msgid "Set the audio codec to encode this track with." msgstr "" msgid "Set the bitrate to encode this track with." msgstr "" msgid "" "Audio Quality:\n" "For encoders that support it, adjust the quality of the output." msgstr "" msgid "Set the mixdown of the output audio track." msgstr "" msgid "Set the sample rate of the output audio track." msgstr "" msgid "" "Audio Gain:\n" "Adjust the amplification or attenuation of the output audio track." msgstr "" msgid "0dB" msgstr "" msgid "%ddB" msgstr "" msgid "%.4gkHz" msgstr "" msgid "%d - %s (%.4gkHz)" msgstr "" msgid "" "Dynamic Range Compression:\n" "Adjust the dynamic range of the output audio track.\n" "For source audio that has a wide dynamic range,\n" "very loud and very soft sequences, DRC allows you\n" "to 'compress' the range by making loud sounds\n" "softer and soft sounds louder.\n" msgstr "" msgid "Remove this audio encoder" msgstr "" msgid "Closing HandBrake will terminate encoding.\n" msgstr "" msgid "No Title Found" msgstr "" msgid "none" msgstr "" msgid "Not Selected" msgstr "" msgid "zerolatency x264 tune selected, forcing constant framerate" msgstr "" msgid "Scanning ..." msgstr "" msgid "Stop Scan" msgstr "" msgid "On" msgstr "On" msgid "Strict" msgstr "" msgid "Loose" msgstr "" msgid "Custom" msgstr "Personalizzato" msgid "Unknown" msgstr "" msgid "auto" msgstr "" #, c-format msgid "" "%s\n" "\n" "%s in %d seconds ..." msgstr "" #, c-format msgid "%sYour movie will be lost if you don't continue encoding." msgstr "" msgid "Cancel Current and Stop" msgstr "" msgid "Cancel Current, Start Next" msgstr "" msgid "Finish Current, then Stop" msgstr "" msgid "Continue Encoding" msgstr "" msgid "Custom " msgstr "" msgid "Modified " msgstr "" #, c-format msgid "Handbrake Version: %s (%d)\n" msgstr "" #, c-format msgid "%d encode(s) pending" msgstr "" #, c-format msgid "job %d of %d, " msgstr "" #, c-format msgid "pass %d (subtitle scan) of %d, " msgstr "" #, c-format msgid "pass %d of %d, " msgstr "" #, c-format msgid "Encoding: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" msgstr "" #, c-format msgid "Encoding: %s%s%.2f %% (ETA %02dh%02dm%02ds)" msgstr "" #, c-format msgid "Encoding: %s%s%.2f %%" msgstr "" msgid "Searching for start time, " msgstr "" msgid "Scanning..." msgstr "" #, c-format msgid "Scanning title %d of %d..." msgstr "" #, c-format msgid "Scanning title %d of %d preview %d..." msgstr "" msgid "Source" msgstr "" msgid "Choose Video Source" msgstr "Seleziona sorgente video" msgid "Paused" msgstr "" msgid "Encode Done!" msgstr "" msgid "Encode Canceled." msgstr "" msgid "Encode Failed." msgstr "" msgid "Muxing: This may take a while..." msgstr "" msgid "Scan this DVD source" msgstr "" msgid "AutoScan" msgstr "" msgid "Suspend" msgstr "" #, c-format msgid "Suspend failed: %s" msgstr "" msgid "Suspend failed" msgstr "" msgid "Shutdown" msgstr "" #, c-format msgid "Shutdown failed: %s" msgstr "" msgid "Shutdown failed" msgstr "" msgid "Encoding" msgstr "" #, c-format msgid "press %d %d" msgstr "" #, c-format msgid "" "Invalid Settings:\n" "%s" msgstr "" msgid "Cancel" msgstr "" #, c-format msgid "%s: %.4g (Warning: lossless)" msgstr "" #, c-format msgid "HandBrake %s/%s is now available (you have %s/%d)." msgstr "" msgid "Encode Complete" msgstr "" msgid "Put down that cocktail, Your HandBrake queue is done!" msgstr "" msgid "Your encode is complete." msgstr "" msgid "Shutting down the computer" msgstr "" msgid "Putting computer to sleep" msgstr "" msgid "Quiting Handbrake" msgstr "" msgid "Bottom" msgstr "" #, c-format msgid "open failed: %s\n" msgstr "" msgid "No key for dictionary item" msgstr "" msgid "Invalid container type. This shouldn't happen" msgstr "" #, c-format msgid "%s:missing a requried attribute" msgstr "" #, c-format msgid "" "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" msgstr "" msgid "language" msgstr "" msgid "Language" msgstr "" msgid "" "The language this text is in, as an ISO code. Pango can use this as a hint " "when rendering the text. If you don't understand this parameter, you " "probably don't need it" msgstr "" msgid "" "The preferred place to ellipsize the string, if the cell renderer does not " "have enough room to display the entire string" msgstr "" msgid "" "How to break the string into multiple lines, if the cell renderer does not " "have enough room to display the entire string" msgstr "" msgid "" "Render the subtitle over the video.\n" "\n" "The subtitle will be part of the video and can not be disabled." msgstr "" msgid "Burned In" msgstr "" msgid "" "Set the default output subtitle track.\n" "\n" "Most players will automatically display this\n" "subtitle track whenever the video is played.\n" "\n" "This is useful for creating a \"forced\" track\n" "in your output." msgstr "" msgid "Default" msgstr "Default" msgid "" "Use only subtitles that have been flagged\n" "as forced in the source subtitle track\n" "\n" "\"Forced\" subtitles are usually used to show\n" "subtitles during scenes where someone is speaking\n" "a foreign language." msgstr "" msgid "Forced Only" msgstr "Solo forzato" msgid "" "Add (or subtract) an offset (in milliseconds)\n" "to the start of the SRT subtitle track.\n" "\n" "Often, the start of an external SRT file\n" "does not coincide with the start of the video.\n" "This setting allows you to synchronize the files." msgstr "" msgid "SRT Offset" msgstr "SRT Offset" msgid "Off" msgstr "Off" msgid "" "The source subtitle track\n" "\n" "You can choose any of the subtitles\n" "recognized in your source file.\n" "\n" "In addition, there is a special track option\n" "\"Foreign Audio Search\". This option will add\n" "an extra pass to the encode that searches for\n" "subtitles that may correspond to a foreign\n" "language scene. This option is best used in\n" "conjunction with the \"Forced\" option." msgstr "" msgid "Track" msgstr "Traccia" msgid "About HandBrake" msgstr "Info su HandBrake" msgid "" "Copyright © 2008 - 2013 John Stebbins\n" "Copyright © 2004 - 2013, HandBrake Devs" msgstr "" msgid "" "HandBrake is a GPL-licensed, multiplatform, multithreaded video transcoder." msgstr "" msgid "http://handbrake.fr" msgstr "" msgid "" "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n" "\n" "HandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgstr "" msgid "_Minimize/Maximize" msgstr "_Minimizza/Massimizza" msgid "_Pause Queue" msgstr "_Pausa coda" msgid "_Quit" msgstr "" msgid "_About" msgstr "" msgid "HandBrake" msgstr "" msgid "_File" msgstr "_File" msgid "_Source" msgstr "_Sorgente" msgid "Single _Title" msgstr "Titolo_Singlolo" msgid "_Destination" msgstr "_Destinazione" msgid "_Preferences" msgstr "_Preferenze" msgid "_Queue" msgstr "_Coda" msgid "_Add" msgstr "" msgid "Add _Multiple" msgstr "" msgid "_Start" msgstr "_Inizia" msgid "_Pause" msgstr "_Pausa" msgid "_View" msgstr "_Visualizza" msgid "HandBrake For _Dumbies" msgstr "" msgid "_Show Presets" msgstr "" msgid "_Preview" msgstr "" msgid "_Activity Window" msgstr "Finestra _attività" msgid "Show _Queue" msgstr "Mostra _coda" msgid "_Presets" msgstr "" msgid "_Save" msgstr "_Salva" msgid "_Delete" msgstr "" msgid "_Make Default" msgstr "_Imposta come predefinito" msgid "_New Folder" msgstr "" msgid "_Export" msgstr "_Esporta" msgid "_Import" msgstr "_Importa" msgid "_Update Built-in Presets" msgstr "" msgid "_Help" msgstr "_Aiuto" msgid "_Guide" msgstr "_Guida" msgid "Start Encoding" msgstr "Inizia la codifica" msgid "Start" msgstr "Inizia" msgid "Pause Encoding" msgstr "Pausa la codifica" msgid "Pause" msgstr "Pausa" msgid "Add to Queue" msgstr "Aggiungi alla coda" msgid "Enqueue" msgstr "" msgid "Show Queue" msgstr "Mostra coda" msgid "Queue" msgstr "Coda" msgid "" "Open Picture Settings and Preview window.\n" "Here you can adjust cropping, resolution, aspect ratio, and filters." msgstr "" msgid "Preview" msgstr "Anteprima" msgid "Show Activity Window" msgstr "Mostra finestra attività" msgid "Activity" msgstr "Attività" msgid "Source:" msgstr "Sorgente:" msgid "None" msgstr "Vuoto" msgid "Title:" msgstr "Titolo:" msgid "" "Set the title to encode.\n" "By default the longest title is chosen.\n" "This is often the feature title of a DVD." msgstr "" msgid "Angle:" msgstr "Angolo:" msgid "For multi-angle DVD's, select the desired angle to encode." msgstr "" msgid "Reset All Titles" msgstr "" msgid "Apply current settings to all titles" msgstr "" msgid "Range of title to encode. Can be chapters, seconds, or frames." msgstr "" msgid "Set the first chapter to encode." msgstr "Imposta il primo capitolo da codificare." msgid "Set the last chapter to encode." msgstr "Imposta l'ultimo capitolo da codificare." msgid "Duration:" msgstr "Durata:" msgid "Destination" msgstr "" msgid "File:" msgstr "File:" msgid "Destination filename for your encode." msgstr "" msgid "Destination directory for your encode." msgstr "" msgid "Destination Directory" msgstr "Cartella di destinazione" msgid "Format:" msgstr "Format:" msgid "Format to mux encoded tracks to." msgstr "" msgid "iPod 5G Support" msgstr "Supporto iPod 5G" msgid "Add iPod Atom needed by some older iPods." msgstr "" msgid "Web optimized" msgstr "Ottimizzato per il web" msgid "" "Optimize the layout of the MP4 file for progressive download.\n" "This allows a player to initiate playback before downloading the entire file." msgstr "" msgid "Large file (>4GB)" msgstr "" msgid "" "Allow 64 bit MP4 file which can be over 4GB.\n" "\n" "Caution: This option may break device compatibility." msgstr "" msgid "Presets List" msgstr "" msgid "Source Codec:" msgstr "Sorgente codec:" msgid "Dimensions:" msgstr "Dimensioni:" msgid "Aspect: " msgstr "Aspetto:" msgid "Frame Rate:" msgstr "Frame Rate:" msgid "Source Picture Parameters" msgstr "" msgid "Autocrop:" msgstr "" msgid "Crop:" msgstr "" msgid "Crop Dimensions:" msgstr "" msgid "Cropping" msgstr "" msgid "Scale Dimensions:" msgstr "" msgid "Optimal for Source:" msgstr "" msgid "Anamorphic:" msgstr "Anamorfico:" msgid "Scaling" msgstr "" msgid "Presentation Dimensions:" msgstr "" msgid "Summary" msgstr "Sommario" msgid "Left Crop" msgstr "" msgid "Top Crop" msgstr "" msgid "Bottom Crop" msgstr "" msgid "Right Crop" msgstr "" msgid "Auto Crop" msgstr "" msgid "Automatically crop black borders around edges of the video." msgstr "" msgid "Loose Crop" msgstr "" msgid "" "When picture settings require that the image\n" "dimensions be rounded to some multiple number\n" "of pixels, this setting will crop a few extra pixels\n" "instead of doing exact cropping and then scaling to\n" "the required multiple." msgstr "" msgid "width:" msgstr "" msgid "" "This is the width that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "" msgid "height:" msgstr "" msgid "" "This is the height that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "" msgid "Optimal for source" msgstr "" msgid "" "If enabled, select the 'optimal' storage resolution.\n" "This will be the resolution that most closely matches the source resolution after cropping." msgstr "" msgid "" "Anamorphic Modes:\n" "\n" "None - Force pixel aspect ratio to 1:1.\n" "Loose - Align dimensions to chosen 'Alignment' value\n" " and pick pixel aspect ratio that preserves the\n" " original display aspect ratio\n" "Strict - Keep original source dimensions and pixel\n" " aspect ratio" msgstr "" msgid "Alignment:" msgstr "Allineamento:" msgid "" "Align storage dimensions to multiples of this value.\n" "\n" "This setting is only necessary for compatibility with some devices.\n" "You should use 2 unless you experience compatibility issues." msgstr "" msgid "Storage Geometry" msgstr "" msgid "" "This is the display width. It is the result of scaling the storage " "dimensions by the pixel aspect." msgstr "" msgid "Pixel Aspect:" msgstr "Aspetto pixel:" msgid "" "Pixel aspect defines the shape of the pixels.\n" "\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "" msgid ":" msgstr "" msgid "" "Pixel aspect defines the shape of the pixels.\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "" msgid "Keep Aspect" msgstr "Mantieni aspetti" msgid "" "If enabled, the original display aspect of the source will be maintained." msgstr "" msgid "Display Aspect:" msgstr "" msgid "Display Geometry" msgstr "" msgid "Grayscale" msgstr "Scala di grigio" msgid "If enabled, filter colour components out of video." msgstr "" msgid "Deblock:" msgstr "" msgid "" "The deblocking filter removes a common type of compression artifact.\n" "If your source exhibits 'blockiness', this filter may help clean it up." msgstr "" msgid "Denoise Filter:" msgstr "" msgid "" "Denoise filtering reduces or removes the appearance of noise and grain.\n" "Film grain and other types of high frequency noise are difficult to compress.\n" "Using this filter on such sources can result in smaller file sizes." msgstr "" msgid "Denoise Preset:" msgstr "" msgid "Denoise Tune:" msgstr "" msgid "" "Custom denoise filter string format\n" "\n" "SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgstr "" msgid "Detelecine:" msgstr "" msgid "" "This filter removes 'combing' artifacts that are the result of telecining.\n" "\n" "Telecining is a process that adjusts film framerates that are 24fps to NTSC video frame rates which are 30fps." msgstr "" msgid "" "Custom detelecine filter string format\n" "\n" "JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgstr "" msgid "Decomb" msgstr "" msgid "" "Choose decomb or deinterlace filter options.\n" "\n" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced.\n" "\n" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "" msgid "Deinterlace" msgstr "" msgid "Decomb:" msgstr "" msgid "" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced." msgstr "" msgid "" "Custom decomb filter string format\n" "\n" "Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\n" "BlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\n" "ErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgstr "" msgid "Deinterlace:" msgstr "" msgid "" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "" msgid "" "Custom deinterlace filter string format\n" "\n" "YadifMode:YadifParity:McdintMode:McdeintQp" msgstr "" msgid "Filters" msgstr "" msgid "Picture" msgstr "" msgid "Video Encoder:" msgstr "" msgid "Available video encoders." msgstr "" msgid "Framerate:" msgstr "Framerate:" msgid "" "Output framerate.\n" "\n" "'Same as source' is recommended. If your source video has\n" "a variable framerate, 'Same as source' will preserve it." msgstr "" msgid "Constant Framerate" msgstr "Framerate costante" msgid "Same as source" msgstr "" msgid "kbps" msgstr "" msgid "(variable)" msgstr "" msgid "(constant)" msgstr "" msgid "Enables constant framerate output." msgstr "" msgid "Peak Framerate (VFR)" msgstr "" msgid "" "Enables variable framerate output with a peak\n" "rate determined by the framerate setting.\n" "\n" "VFR is not compatible with some players." msgstr "" msgid "Variable Framerate" msgstr "Framerate variabile" msgid "" "Enables variable framerate output.\n" "\n" "VFR is not compatible with some players." msgstr "" msgid "" "Set the desired quality factor.\n" "The encoder targets a certain quality.\n" "The scale used by each video encoder is different.\n" "\n" "x264's scale is logarithmic and lower values correspond to higher quality.\n" "So small decreases in value will result in progressively larger increases\n" "in the resulting file size. A value of 0 means lossless and will result\n" "in a file size that is larger than the original source, unless the source\n" "was also lossless.\n" "\n" "FFMpeg's and Theora's scale is more linear.\n" "These encoders do not have a lossless mode." msgstr "" msgid "Constant Quality:" msgstr "" msgid "Bitrate (kbps): " msgstr "Bitrate (kbps): " msgid "" "Set the average bitrate.\n" "\n" "The instantaneous bitrate can be much higher or lower at any point in time.\n" "But the average over a long duration will be the value set here. If you need\n" "to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings." msgstr "" msgid "2-Pass Encoding" msgstr "" msgid "" "Perform 2 Pass Encoding.\n" "\n" "The 'Bitrate' option is prerequisite. During the 1st pass, statistics about\n" "the video are collected. Then in the second pass, those statistics are used\n" "to make bitrate allocation decisions." msgstr "" msgid "Turbo First Pass" msgstr "" msgid "" "During the 1st pass of a 2 pass encode, use settings that speed things " "along." msgstr "" msgid "Use Advanced Options" msgstr "Usa opzioni avanzate" msgid "" "Use advanced options Tab for x264 settings.\n" "\n" "Use at your own risk!" msgstr "" msgid "Preset:" msgstr "" msgid "" "Adjusts encoder settings to trade off compression efficiency against encoding speed.\n" "\n" "This establishes your default encoder settings.\n" "Tunes, profiles, levels and advanced option string will be applied to this.\n" "You should generally set this option to the slowest you can bear since slower\n" "settings will result in better quality or smaller files." msgstr "" msgid "Tune:" msgstr "" msgid "" "Tune settings to optimize for common scenarios.\n" "\n" "This can improve effeciency for particular source characteristics or set\n" "characteristics of the output file. Changes will be applied after the\n" "preset but before all other parameters." msgstr "" msgid "Fast Decode" msgstr "" msgid "" "Reduce decoder CPU usage.\n" "\n" "Set this if your device is struggling to play the output (dropped frames)." msgstr "" msgid "Zero Latency" msgstr "" msgid "" "Minimize latency between input to encoder and output of decoder.\n" "\n" "This is useful for broadcast of live streams.\n" "\n" "Since HandBrake is not suitable for live stream broadcast purposes,\n" "this setting is of little value here." msgstr "" msgid "Profile:" msgstr "" msgid "" "Sets and ensures compliance with the specified profile.\n" "\n" "Overrides all other settings." msgstr "" msgid "Level:" msgstr "" msgid "" "Sets and ensures compliance with the specified level.\n" "\n" "Overrides all other settings." msgstr "" msgid "More Settings:" msgstr "" msgid "" "Additional encoder settings.\n" "\n" "Colon separated list of encoder options." msgstr "" msgid "Video" msgstr "Video" msgid "Selection Behavior:" msgstr "" msgid "Remove" msgstr "" msgid "Available Languages" msgstr "" msgid "Selected Languages" msgstr "" msgid "Use only first encoder for secondary audio" msgstr "" msgid "" "Only the primary audio track will be encoded with the full encoder list.\n" "All other secondary audio output tracks will be encoded with first encoder only." msgstr "" msgid "Auto Passthru:" msgstr "" msgid "MP3" msgstr "" msgid "" "Enable this if your playback device supports MP3.\n" "This permits MP3 passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports AAC.\n" "This permits AAC passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports AC-3.\n" "This permits AC-3 passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports DTS.\n" "This permits DTS passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "" "Enable this if your playback device supports DTS-HD.\n" "This permits DTS-HD passthru to be selected when automatic passthru selection is enabled." msgstr "" msgid "Passthru Fallback:" msgstr "" msgid "" "Set the audio codec to encode with when a suitable track can not be found " "for audio passthru." msgstr "" msgid "Audio Encoder Settings:" msgstr "" msgid "Each selected source track will be encoded with all selected encoders" msgstr "" msgid "Encoder" msgstr "" msgid "Bitrate/Quality" msgstr "" msgid "Mixdown" msgstr "" msgid "Samplerate" msgstr "" msgid "Gain" msgstr "" msgid "Audio Defaults" msgstr "" msgid "Add new audio settings to the list" msgstr "" msgid "Add All" msgstr "" msgid "Add all audio tracks to the list" msgstr "" msgid "Reload Defaults" msgstr "" msgid "Reload all audio settings from defaults" msgstr "" msgid "Audio List" msgstr "" msgid "Preferred Language: None" msgstr "" msgid "Add Foreign Audio Search Pass" msgstr "" msgid "" "Add \"Foreign Audio Search\" when the default audio track is your preferred language.\n" "This search pass finds short sequences of foreign audio and provides subtitles for them." msgstr "" msgid "Add subtitle track if default audio is foreign" msgstr "" msgid "" "When the default audio track is not your preferred language, add a subtitle " "track." msgstr "" msgid "Add Closed Captions when available" msgstr "" msgid "" "Closed captions are text subtitles that can be added to any container as a " "soft subtitle track (not burned)" msgstr "" msgid "Subtitle Defaults" msgstr "" msgid "Add new subtitle settings to the list" msgstr "" msgid "Add all subtitle tracks to the list" msgstr "" msgid "Reload all subtitle settings from defaults" msgstr "" msgid "Subtitle List" msgstr "" msgid "Reference Frames:" msgstr "" msgid "" "Sane values are ~1-6. The more you add, the better the compression, but the slower the encode.\n" "Cel animation tends to benefit from more reference frames a lot more than film content.\n" "\n" "Note that many hardware devices have limitations on the number of supported reference\n" "frames, so if you're encoding for a handheld or standalone player, don't touch this unless\n" "you're absolutely sure you know what you're doing!" msgstr "" msgid "Maximum B-Frames:" msgstr "" msgid "" "Sane values are ~2-5. This specifies the maximum number of sequential B-frames that the encoder can use.\n" "\n" "Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.\n" "Cel-animated source material and B-pyramid also significantly increase the usefulness of larger\n" "values.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off)." msgstr "" msgid "Pyramidal B-Frames:" msgstr "" msgid "" "B-pyramid improves compression by creating a pyramidal structure (hence the name)\n" "of B-frames, allowing B-frames to reference each other to improve compression.\n" "\n" "Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit." msgstr "" msgid "Weighted P-Frames:" msgstr "" msgid "" "Performs extra analysis to decide upon weighting parameters for each frame.\n" "\n" "This improves overall compression slightly and improves the quality of fades greatly.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires weighted P-frame\n" "prediction to be disabled. Note that some devices and players, even those that support\n" "Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is\n" "completely incompatible with it, for example." msgstr "" msgid "8x8 Transform" msgstr "" msgid "" "The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.\n" "\n" "It improves compression by at least 5% at a very small speed cost and may\n" "provide an unusually high visual quality benefit compared to its compression\n" "gain. However, it requires High Profile, which many devices may not support." msgstr "" msgid "CABAC Entropy Encoding" msgstr "" msgid "" "After the encoder has done its work, it has a bunch of data that\n" "needs to be compressed losslessly, similar to ZIP or RAR. H.264 provides\n" "two options for this: CAVLC and CABAC. CABAC decodes a lot slower but\n" "compresses significantly better (10-30%), especially at lower bitrates.\n" "\n" "If you're looking to minimize CPU requirements for video playback, disable this option.\n" "Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled." msgstr "" msgid "Encoding Features" msgstr "" msgid "Motion Est. Method:" msgstr "" msgid "" "Controls the motion estimation method.\n" "\n" "Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.\n" "A better motion search method improves compression at the cost of speed.\n" "\n" "Diamond: performs an extremely fast and simple search using a diamond pattern.\n" "Hexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n" "Uneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n" "Exhaustive: performs a \"dumb\" search of every pixel in a wide area. Significantly slower for only a small compression gain.\n" "Transformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement." msgstr "" msgid "Subpel ME & Mode:" msgstr "" msgid "" "This setting controls both subpixel-precision motion estimation and mode decision methods.\n" "\n" "Subpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n" "Mode decision is the method used to choose how to encode each block of the frame: a very important decision.\n" "SAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n" "6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n" "11 disables all early terminations in analysis.\n" "10 and 11, the most powerful and slowest options, require adaptive quantization (aq-mode > 0) and trellis 2 (always)." msgstr "" msgid "Motion Est. Range:" msgstr "" msgid "" "This is the distance x264 searches from its initial guess at the\n" "motion of a block in order to try to find its actual motion.\n" "\n" "The default is fine for most content, but extremely high motion video,\n" "especially at HD resolutions, may benefit from higher ranges, albeit at\n" "a high speed cost." msgstr "" msgid "Adaptive Direct Mode:" msgstr "" msgid "" "H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n" "\n" "Spatial, the default, is almost always better, but temporal is sometimes useful too.\n" "x264 can, at the cost of a small amount of speed (and accordingly for a small compression gain),\n" "adaptively select which is better for each particular frame." msgstr "" msgid "Adaptive B-Frames:" msgstr "" msgid "" "x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n" "\n" "Fast mode takes roughly the same amount of time no matter how many B-frames you specify.\n" "However, while fast, its decisions are often suboptimal.\n" "\n" "Optimal mode gets slower as the maximum number of B-Frames increases,\n" "but makes much more accurate decisions, especially when used with B-pyramid." msgstr "" msgid "Partitions:" msgstr "" msgid "" "Mode decision picks from a variety of options to make its decision:\n" "this option chooses what options those are.\n" "\n" "Fewer partitions to check means faster encoding, at the cost of worse\n" "decisions, since the best option might have been one that was turned off." msgstr "" msgid "Trellis:" msgstr "" msgid "" "Trellis fine-tunes the rounding of transform coefficients to\n" "squeeze out 3-5% more compression at the cost of some speed.\n" "\n" "\"Always\" uses trellis not only during the main encoding process, but also\n" "during analysis, which improves compression even more, albeit at great speed cost.\n" "\n" "Trellis costs more speed at higher bitrates and requires CABAC." msgstr "" msgid "Analysis" msgstr "Analisi" msgid "Adaptive Quantization Strength:" msgstr "" msgid "" "Adaptive quantization controls how the encoder distributes bits across the frame.\n" "\n" "Higher values take more bits away from edges and complex areas to improve areas with finer detail." msgstr "" msgid "Psychovisual Rate Distortion:" msgstr "" msgid "" "Psychovisual rate-distortion optimization takes advantage of the characteristics of human\n" "vision to dramatically improve apparent detail and sharpness.\n" "The effect can be made weaker or stronger by adjusting the strength.\n" "Being an RD algorithm, it requires mode decision to be at least \"6\"." msgstr "" msgid "Psychovisual Trellis:" msgstr "" msgid "" "Psychovisual trellis is an experimental algorithm to further\n" "improve sharpness and detail retention beyond what Psychovisual RD does.\n" "\n" "Recommended values are around 0.2, though higher values may help for very\n" "grainy video or lower bitrate encodes. Not recommended for cel animation\n" "and other sharp-edged graphics." msgstr "" msgid "Deblocking: " msgstr "" msgid "" "H.264 deblocking filter.\n" "\n" "h.264 has a built-in deblocking filter that smooths out blocking artifacts\n" "after decoding each frame. This not only improves visual quality, but also\n" "helps compression significantly. The deblocking filter takes a lot of CPU power,\n" "so if you're looking to minimize CPU requirements for video playback, disable it.\n" "\n" "The deblocking filter has two adjustable parameters, \"strength\" (Alpha) and \"threshold\" (Beta).\n" "The former controls how strong (or weak) the deblocker is, while the latter controls how many\n" "(or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking.\n" "The default is 0 (normal strength) for both parameters." msgstr "" msgid "No DCT Decimate" msgstr "" msgid "" "x264 normally zeroes out nearly-empty data blocks to save bits to\n" "be better used for some other purpose in the video. However, this can\n" "sometimes have slight negative effects on retention of subtle grain and\n" "dither.\n" "\n" "Don't touch this unless you're having banding issues or other such cases\n" "where you are having trouble keeping fine noise." msgstr "" msgid "Psychovisual" msgstr "" msgid "" "Your selected options will appear here.\n" "You can edit these and add additional options.\n" "\n" "Default values will not be shown. The defaults are:\n" "ref=3:bframes=3:b-adapt=fast:direct=spatial:\n" "b-pyramid=normal:weightp=2:me=hex:merange=16:\n" "subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\n" "deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\n" "no-fast-pskip=0:no-dct-decimate=0:cabac=1" msgstr "" msgid "Current x264 Advanced Option String" msgstr "" msgid "Advanced Video" msgstr "" msgid "Chapter Markers" msgstr "" msgid "Add chapter markers to output file." msgstr "" msgid "Chapters" msgstr "Capitoli" msgid "Actors:" msgstr "Attori:" msgid "Director:" msgstr "Regista:" msgid "Release Date:" msgstr "Data di rilascio:" msgid "Comment:" msgstr "Commento:" msgid "Genre:" msgstr "Genere:" msgid "Description:" msgstr "Descrizione:" msgid "Plot:" msgstr "" msgid "Tags" msgstr "Tag" msgid "Settings" msgstr "" msgid "Edit" msgstr "Modifica" msgid "Reload" msgstr "" msgid "" "Mark selected queue entry as pending.\n" "Resets the queue job to pending and ready to run again." msgstr "" msgid "Reload All" msgstr "" msgid "" "Mark all queue entries as pending.\n" "Resets all queue jobs to pending and ready to run again." msgstr "" msgid "OK" msgstr "" msgid "Select All" msgstr "" msgid "Mark all titles for adding to the queue" msgstr "" msgid "Clear All" msgstr "" msgid "Unmark all titles" msgstr "" msgid "Destination files OK. No duplicates detected." msgstr "" msgid "Select this title for adding to the queue.\n" msgstr "" msgid "Preferences" msgstr "" msgid "Automatically check for updates" msgstr "" msgid "When all encodes are complete" msgstr "" msgid "Use automatic naming (uses modified source name)" msgstr "" msgid "Auto-Name Template" msgstr "" msgid "" "Available Options: {source} {title} {chapters} {date} {time} {quality} " "{bitrate}" msgstr "" msgid "Use iPod/iTunes friendly (.m4v) file extension for MP4" msgstr "" msgid "Number of previews" msgstr "Numero di anteprime" msgid "Filter short titles (seconds)" msgstr "" msgid "Show system tray icon" msgstr "" msgid "General" msgstr "Generale" msgid "Constant Quality fractional granularity" msgstr "" msgid "Use dvdnav (instead of libdvdread)" msgstr "" msgid "Put individual encode logs in same location as movie" msgstr "" msgid "Activity Log Verbosity Level" msgstr "" msgid "Activity Log Longevity" msgstr "" msgid "Scale down High Definition previews" msgstr "" msgid "Automatically Scan DVD when loaded" msgstr "" msgid "Scans the DVD whenever a new disc is loaded" msgstr "" msgid "Hide Advanced Video Options Tab" msgstr "" msgid "" "Use advanced video options at your own risk.\n" "We recommend that you use the controls available\n" "on the Video tab instead." msgstr "" msgid "Delete completed jobs from queue" msgstr "" msgid "" "By default, completed jobs remain in the queue and are marked as complete.\n" "Check this if you want the queue to clean itself up by deleting completed jobs." msgstr "" msgid "Allow Tweaks" msgstr "" msgid "Allow HandBrake For Dummies" msgstr "" msgid "Advanced" msgstr "Avanzato" msgid "Folder Name:" msgstr "Nome cartella:" msgid "Description" msgstr "" msgid "Preset Name:" msgstr "" msgid "Custom Picture Dimensions" msgstr "" msgid "Maximum Width:" msgstr "" msgid "Enable maximum width limit." msgstr "" msgid "" "This is the maximum width that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source width is greater.\n" "Setting this to 0 means there is no maximum width." msgstr "" msgid "Maximum Height:" msgstr "" msgid "Enable maximum height limit." msgstr "" msgid "" "This is the maximum height that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source height is greater.\n" "Setting this to 0 means there is no maximum height." msgstr "" msgid "Select preview frames." msgstr "" msgid "" "Encode and play a short sequence of video starting from the current preview " "position." msgstr "" msgid "Duration:" msgstr "Durata:" msgid "Set the duration of the live preview in seconds." msgstr "" msgid "Show Crop" msgstr "" msgid "Show Cropped area of the preview" msgstr "" msgid "Fullscreen" msgstr "Tutto schermo" msgid "View Fullscreen Preview" msgstr "" msgid "Title Number:" msgstr "" msgid "Detected DVD devices:" msgstr "" msgid "Setting:" msgstr "Impostazioni:" msgid "Import SRT" msgstr "Importa SRT" msgid "Enable settings to import an SRT subtitle file" msgstr "" msgid "Embedded Subtitle List" msgstr "" msgid "Enable settings to select embedded subtitles" msgstr "" msgid "Character Code" msgstr "" msgid "Offset (ms)" msgstr "" msgid "" "Set the language of this subtitle.\n" "This value will be used by players in subtitle menus." msgstr "" msgid "" "Set the character code used by the SRT file you are importing.\n" "\n" "SRTs come in all flavours of character sets.\n" "We translate the character set to UTF-8.\n" "The source's character code is needed in order to perform this translation." msgstr "" msgid "Select the SRT file to import." msgstr "Seleziona il file SRT da importare" msgid "Srt File" msgstr "File srt" msgid "Adjust the offset in milliseconds between video and SRT timestamps" msgstr "" msgid "Track" msgstr "Traccia" msgid "List of subtitle tracks available from your source." msgstr "" msgid "Forced Subtitles Only" msgstr "" msgid "Burn into video" msgstr "" msgid "" "Render the subtitle over the video.\n" "The subtitle will be part of the video and can not be disabled." msgstr "" msgid "Set Default Track" msgstr "" msgid "Source Track" msgstr "" msgid "List of audio tracks available from your source." msgstr "" msgid "Track Name:" msgstr "Nome traccia:" msgid "" "Set the audio track name.\n" "\n" "Players may use this in the audio selection list." msgstr "" msgid "Mix" msgstr "Mix" msgid "Sample Rate" msgstr "" msgid "" "Dynamic Range Compression: Adjust the dynamic range of the output audio track.\n" "\n" "For source audio that has a wide dynamic range (very loud and very soft sequences),\n" "DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder." msgstr "" msgid "Enable bitrate setting" msgstr "" msgid "Enable quality setting" msgstr "" msgid "" "Quality: For output codec's that support it, adjust the quality of " "the output." msgstr "" msgid "00.0" msgstr "" msgid "" "Audio Gain: Adjust the amplification or attenuation of the output " "audio track." msgstr "" msgid "Skip This Version" msgstr "Salta questa versione" msgid "Remind Me Later" msgstr "Ricorda dopo" msgid "A new version of HandBrake is available!" msgstr "Una nuova versione di HandBrake è disponibile!" msgid "HandBrake xxx is now available (you have yyy)." msgstr "" msgid "Release Notes" msgstr "" msgid "First Track Matching Selected Languages" msgstr "" msgid "All Tracks Matching Selected Languages" msgstr "" msgid "Chapters:" msgstr "Capitoli:" msgid "Seconds:" msgstr "Secondi:" msgid "Frames:" msgstr "" msgid "Do Nothing" msgstr "" msgid "Show Notification" msgstr "Mostra notifica" msgid "Quit Handbrake" msgstr "Chiudi Handbrake" msgid "Put Computer To Sleep" msgstr "" msgid "Shutdown Computer" msgstr "" msgid "Week" msgstr "Settimana" msgid "Month" msgstr "" msgid "Year" msgstr "Anno" msgid "Immortal" msgstr "Immortale" msgid "Never" msgstr "Mai" msgid "Daily" msgstr "Giornalmente" msgid "Weekly" msgstr "Settimanalmente" msgid "Monthly" msgstr "Mensilmente" msgid "Default" msgstr "" msgid "Fast" msgstr "Veloce" msgid "Slow" msgstr "" msgid "Slower" msgstr "Lento" msgid "Ultralight" msgstr "" msgid "Light" msgstr "" msgid "Medium" msgstr "Medio" msgid "Strong" msgstr "Forte" msgid "Film" msgstr "" msgid "Grain" msgstr "" msgid "High Motion" msgstr "" msgid "Animation" msgstr "" msgid "Spatial" msgstr "Spaziale" msgid "Temporal" msgstr "Temporale" msgid "Automatic" msgstr "Automatico" msgid "Optimal" msgstr "Ottimale" msgid "Normal" msgstr "Normale" msgid "Simple" msgstr "Semplice" msgid "Smart" msgstr "Intelligente" msgid "Diamond" msgstr "Diamante" msgid "Hexagon" msgstr "Esagono" msgid "Uneven Multi-Hexagon" msgstr "" msgid "Exhaustive" msgstr "" msgid "Hadamard Exhaustive" msgstr "" msgid "Most" msgstr "" msgid "Some" msgstr "" msgid "All" msgstr "Tutti" msgid "Encode only" msgstr "" msgid "Always" msgstr "Sempre" msgid "(NTSC Film)" msgstr "" msgid "(PAL Film/Video)" msgstr "" msgid "(NTSC Video)" msgstr "" msgid "%d - %02dh%02dm%02ds - %s" msgstr "" msgid "%d (%05d.MPLS) - %02dh%02dm%02ds" msgstr "" msgid "%d (%05d.MPLS) - Unknown Length" msgstr "" msgid "%d - %02dh%02dm%02ds" msgstr "" msgid "%d - Unknown Length" msgstr "" msgid "No Titles" msgstr "Nessun Titolo" msgid "No Audio" msgstr "Nessun audio" msgid "Foreign Audio Search" msgstr "" #, c-format msgid "Chapter %2d" msgstr "" #, c-format msgid "N/A" msgstr "" #, c-format msgid "" "Invalid Deinterlace Settings:\n" "\n" "%s\n" msgstr "" #, c-format msgid "" "Invalid Detelecine Settings:\n" "\n" "%s\n" msgstr "" #, c-format msgid "" "Invalid Decomb Settings:\n" "\n" "%s\n" msgstr "" msgid "" "Theora is not supported in the MP4 container.\n" "\n" "You should choose a different video codec or container.\n" "If you continue, FFMPEG will be chosen for you." msgstr "" msgid "Continue" msgstr "" msgid "No title found.\n" msgstr "" msgid "" "Only one subtitle may be burned into the video.\n" "\n" "You should change your subtitle selections.\n" "If you continue, some subtitles will be lost." msgstr "" msgid "" "Srt file does not exist or not a regular file.\n" "\n" "You should choose a valid file.\n" "If you continue, this subtitle will be ignored." msgstr "" msgid "" "The source does not support Pass-Thru.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "%s is not supported in the %s container.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "The source audio does not support %s mixdown.\n" "\n" "You should choose a different mixdown.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "Unable to create %s.\n" "\n" "Internal error. Could not parse UI description.\n" "%s" msgstr "" msgid "Index" msgstr "Indice" msgid "Duration" msgstr "Durata" msgid "Title" msgstr "Titolo" msgid "Job Information" msgstr "" msgid "Track Information" msgstr "" msgid "Preset Name" msgstr "" msgid "The device or file to encode" msgstr "" msgid "The preset values to use for encoding" msgstr "" msgid "Spam a lot" msgstr "" msgid "- Transcode media formats" msgstr "" msgid "Globals" msgstr "" msgid "Presets" msgstr "" msgid "Folder" msgstr "" #, c-format msgid "%s path: (%s)" msgstr "" #, c-format msgid "%s indices: len %d" msgstr "" msgid "Type" msgstr "" msgid "Failed to find parent folder when adding child." msgstr "" msgid "Failed to find parent folder while adding child." msgstr "" #, c-format msgid "Can't map language value: (%s)" msgstr "" #, c-format msgid "Can't map value: (%s)" msgstr "" msgid "Subtitles" msgstr "" msgid "" "%s: Folder already exists.\n" "You can not replace it with a preset." msgstr "" msgid "" "%s: Preset already exists.\n" "You can not replace it with a folder." msgstr "" msgid "Import Preset" msgstr "" msgid "All (*)" msgstr "" msgid "Presets (*.plist)" msgstr "" msgid "Export Preset" msgstr "" msgid "" "Confirm deletion of %s:\n" "\n" "%s" msgstr "" msgid "folder" msgstr "" msgid "preset" msgstr "" msgid "No selection??? Perhaps unselected." msgstr "" #, c-format msgid "Gstreamer Error: %s" msgstr "" #, c-format msgid "" "Missing GStreamer plugin\n" "Audio or Video may not play as expected\n" "\n" "%s" msgstr "" msgid "Done" msgstr "" msgid "Windowed" msgstr "" msgid "Seconds" msgstr "" msgid "Frames" msgstr "" #, c-format msgid "" "%s (Title %d, %s %d through %d, 2 Video Passes) -->" " %s" msgstr "" #, c-format msgid "" "%s (Title %d, %s %d through %d) --> %s" msgstr "" #, c-format msgid "Modified Preset Based On: %s\n" msgstr "" #, c-format msgid "Preset: %s\n" msgstr "" #, c-format msgid "Format: %s Container\n" msgstr "" msgid "Container Options:" msgstr "" #, c-format msgid "%sChapter Markers" msgstr "" #, c-format msgid "%siPod 5G Support" msgstr "" #, c-format msgid "%sWeb Optimized" msgstr "" #, c-format msgid "%sLarge File Size (>4GB)" msgstr "" #, c-format msgid "Destination: %s\n" msgstr "" msgid "(Aspect Preserved)" msgstr "" msgid "(Aspect Lost)" msgstr "" msgid "(Anamorphic)" msgstr "" msgid "(Custom Anamorphic)" msgstr "" msgid "Picture: " msgstr "" #, c-format msgid "Source: %d x %d, Output %d x %d %s, Crop %d:%d:%d:%d" msgstr "" #, c-format msgid ", Display %d x %d" msgstr "" msgid "Filters:" msgstr "" #, c-format msgid "%sDetelecine" msgstr "" #, c-format msgid "%sDecomb" msgstr "" #, c-format msgid "%sDeinterlace" msgstr "" #, c-format msgid "%sDenoise Filter %s:" msgstr "" #, c-format msgid "%sDeblock: %d" msgstr "" #, c-format msgid "%sGrayscale" msgstr "" #, c-format msgid "Video: %s" msgstr "" #, c-format msgid ", Framerate: %s %s" msgstr "" #, c-format msgid ", Framerate: Peak %s (may be lower)" msgstr "" #, c-format msgid ", Framerate: %s (constant frame rate)" msgstr "" msgid "Error" msgstr "Errore" msgid "Bitrate:" msgstr "" msgid "Bitrate" msgstr "" msgid "Quality" msgstr "" msgid "Turbo 1st Pass: On\n" msgstr "" #, c-format msgid "Video Options: Preset: %s" msgstr "" msgid " - Tune: " msgstr "" #, c-format msgid " - Profile: %s" msgstr "" #, c-format msgid " - Level: %s" msgstr "" #, c-format msgid "Advanced Options: %s\n" msgstr "" msgid "Audio: " msgstr "" #, c-format msgid "Audio Tracks: %d" msgstr "" #, c-format msgid "Bitrate: %d" msgstr "" #, c-format msgid "%s --> Encoder: %s" msgstr "" #, c-format msgid "%s --> Encoder: %s, Mixdown: %s, SampleRate: %s, %s" msgstr "" msgid "Subtitle: " msgstr "" #, c-format msgid "Subtitle Tracks: %d\n" msgstr "" msgid " (Force)" msgstr "(Forza)" msgid " (Burn)" msgstr "" msgid " (Default)" msgstr "" #, c-format msgid " %s (%s), %s, Offset (ms) %d%s\n" msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "Another queued job has specified the same destination.\n" "Do you want to overwrite?" msgstr "" msgid "Overwrite" msgstr "Sovrascrivi" #, c-format msgid "" "Destination: %s\n" "\n" "This is not a valid directory." msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "Can not read or write the directory." msgstr "" #, c-format msgid "" "Destination filesystem is almost full: %uM free\n" "\n" "Encode may be incomplete if you proceed.\n" msgstr "" msgid "Proceed" msgstr "" #, c-format msgid "" "Destination: %s\n" "\n" "File already exists.\n" "Do you want to overwrite?" msgstr "" msgid "" "Duplicate destination files detected.\n" "Duplicates will not be added to the queue." msgstr "" msgid "No Title" msgstr "" msgid "" "There is another title with the same destination file name.\n" "This title will not be added to the queue unless you change\n" "the output file name.\n" msgstr "" msgid "Stop" msgstr "" msgid "Stop Encoding" msgstr "Ferma la codifica" msgid "Resume" msgstr "" msgid "Resume Encoding" msgstr "Riprendi la codifica" msgid "S_top Queue" msgstr "" msgid "_Start Queue" msgstr "_Avvia coda" msgid "_Resume Queue" msgstr "_Riprendi coda" msgid "Resume Queue" msgstr "" msgid "" "You are currently encoding. What would you like to do?\n" "\n" msgstr "" #, c-format msgid "" "You have %d unfinished job(s) in a saved queue.\n" "\n" "Would you like to reload them?" msgstr "" msgid "No" msgstr "" msgid "Yes" msgstr "" #, c-format msgid "Usage: %s infile [outfile]\n" msgstr "" #, c-format msgid "Offset: %dms" msgstr "" msgid "Burned Into Video" msgstr "" msgid "Passthrough" msgstr "" msgid "through" msgstr "" msgid "(Forced Subtitles Only)" msgstr "" msgid "(Default)" msgstr "" msgid "Error!" msgstr "" #, c-format msgid "Preferred Language: %s" msgstr "" #, c-format msgid "Add %s subtitle track if default audio is not %s" msgstr "" #, c-format msgid "Type %s" msgstr "" #, c-format msgid "Type %s value %s" msgstr "" #, c-format msgid "Type %s value %d" msgstr "" #, c-format msgid "Type %s value %" msgstr "" #, c-format msgid "Type %s value %f" msgstr "" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"%s\"" msgstr "" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"\"" msgstr "" msgid "Any" msgstr "" msgid "0: SAD, no subpel" msgstr "" msgid "4: SATD, qpel on all" msgstr "" msgid "5: SATD, multi-qpel on all" msgstr "" msgid "6: RD in I/P-frames" msgstr "" msgid "7: RD in all frames" msgstr "" msgid "8: RD refine in I/P-frames" msgstr "" msgid "9: RD refine in all frames" msgstr "" msgid "10: QPRD in all frames" msgstr "" msgid "11: No early terminations in analysis" msgstr "" msgid "Your names" msgstr "" msgid "Your emails" msgstr "" HandBrake-0.10.2/gtk/po/cs.po0000664000175200017520000025564412466165306016240 0ustar handbrakehandbrake# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Czech X Team , 2013 # fri, 2013-2014 # fri, 2013-2014 # fri, 2013-2014 # fri, 2013-2014 msgid "" msgstr "" "Project-Id-Version: HandBrake-0.10\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-03 13:01+0300\n" "PO-Revision-Date: 2014-11-28 20:59+0000\n" "Last-Translator: fri\n" "Language-Team: Czech (http://www.transifex.com/projects/p/handbrake/language/cs/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: cs\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" msgid "Quality: " msgstr "Jakost:" #, c-format msgid "Bitrate: %dkbps" msgstr "Datový tok: %dkb/s" #, c-format msgid "Bitrate: %.4gkbps" msgstr "Datový tok: %.4gkb/s" msgid "Passthrough" msgstr "Průchod" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s\n" "Track Name: %s" msgstr "%s\nZesílení: %s\nDRC: %s\nNázev stopy: %s" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s" msgstr "%s\nZesílení: %s\nDRC: %s" msgid "Add" msgstr "Přidat" msgid "" "Add an audio encoder.\n" "Each selected source track will be encoded with all selected encoders." msgstr "Přidat kodér zvuku.\nKaždá vybraná zdrojová stopa bude zakódována pomocí všech vybraných kodérů." msgid "Set the audio codec to encode this track with." msgstr "Nastavit zvukový kodek, kterým se má tato stopa zakódovat." msgid "Set the bitrate to encode this track with." msgstr "Nastavit datový tok, ve kterém se má tato stopa zakódovat." msgid "" "Audio Quality:\n" "For encoders that support it, adjust the quality of the output." msgstr "Kvalita zvuku:\nU výstupních kodeků, jež to podporují upravit jakost výstupu." msgid "Set the mixdown of the output audio track." msgstr "Nastavit finální mixáž výstupní zvukové stopy." msgid "Set the sample rate of the output audio track." msgstr "Nastavit vzorkovací kmitočet výstupní zvukové stopy." msgid "" "Audio Gain:\n" "Adjust the amplification or attenuation of the output audio track." msgstr "Zesílení zvuku:\nUpravit zesílení nebo zeslabení výstupní zvukové stopy." msgid "0dB" msgstr "0 dB" msgid "%ddB" msgstr "%ddB" msgid "%.4gkHz" msgstr "%.4gkHz" msgid "%d - %s (%.4gkHz)" msgstr "%d - %s (%.4gkHz)" msgid "" "Dynamic Range Compression:\n" "Adjust the dynamic range of the output audio track.\n" "For source audio that has a wide dynamic range,\n" "very loud and very soft sequences, DRC allows you\n" "to 'compress' the range by making loud sounds\n" "softer and soft sounds louder.\n" msgstr "Komprese dynamického rozsahu:\nUpravit dynamický rozsah výstupní zvukové stopy.\nU zdrojového zvuku, který má široký dynamický rozsah\n(velmi hlasité a velmi slabé sekvence), vám komprese\ndynamického rozsahu (DRC - Dynamic Range Compression)\n'stlačit' rozsah tím, že udělá hlasité oblasti slabšími a slabé\noblasti hlasitějšími.\n" msgid "Remove this audio encoder" msgstr "Odstranit tento kodér zvuku" msgid "Closing HandBrake will terminate encoding.\n" msgstr "Zavření programu HandBrake povede k ukončení kódování.\n" msgid "No Title Found" msgstr "Nenalezen žádný název" msgid "none" msgstr "Žádný" msgid "Not Selected" msgstr "Nevybráno" msgid "zerolatency x264 tune selected, forcing constant framerate" msgstr "vybráno x264 s nulovou prodlevou, vynucuje se stálá rychlost snímkování" msgid "Scanning ..." msgstr "Prohledává se..." msgid "Stop Scan" msgstr "Zastavit prohledávání" msgid "On" msgstr "Zapnuto" msgid "Strict" msgstr "Přísný" msgid "Loose" msgstr "Volně" msgid "Custom" msgstr "Vlastní" msgid "Unknown" msgstr "Neznámý" msgid "auto" msgstr "Automaticky" #, c-format msgid "" "%s\n" "\n" "%s in %d seconds ..." msgstr "%s\n\n%s za %d sekund..." #, c-format msgid "%sYour movie will be lost if you don't continue encoding." msgstr "%s Váš film bude ztracen, pokud nebudete pokračovat v kódování." msgid "Cancel Current and Stop" msgstr "Zrušit nynější a zastavit" msgid "Cancel Current, Start Next" msgstr "Zrušit nynější, začít další" msgid "Finish Current, then Stop" msgstr "Dokončit nynější, potom zastavit" msgid "Continue Encoding" msgstr "Pokračovat v kódování" msgid "Custom " msgstr "Vlastní" msgid "Modified " msgstr "Změněno " #, c-format msgid "Handbrake Version: %s (%d)\n" msgstr "Verze Handbrake: %s (%d)\n" #, c-format msgid "%d encode(s) pending" msgstr "%d kódování čeká na vyřízení" #, c-format msgid "job %d of %d, " msgstr "Úkol %d z %d, " #, c-format msgid "pass %d (subtitle scan) of %d, " msgstr "Průchod %d (hledání titulků) z %d, " #, c-format msgid "pass %d of %d, " msgstr "Průchod %d z %d, " #, c-format msgid "Encoding: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" msgstr "Kódování: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %% (ETA %02dh%02dm%02ds)" msgstr "Kódování: %s%s%.2f %% (ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %%" msgstr "Kódování: %s%s%.2f %%" msgid "Searching for start time, " msgstr "Hledá se čas začátku, " msgid "Scanning..." msgstr "Prohledává se..." #, c-format msgid "Scanning title %d of %d..." msgstr "Prohledává se název %d z %d..." #, c-format msgid "Scanning title %d of %d preview %d..." msgstr "Prohledává se název %d z %d náhled %d..." msgid "Source" msgstr "Zdroj" msgid "Choose Video Source" msgstr "Vybrat zdroj obrazu" msgid "Paused" msgstr "Pozastaveno" msgid "Encode Done!" msgstr "Kódování hotovo!" msgid "Encode Canceled." msgstr "Kódování zrušeno." msgid "Encode Failed." msgstr "Kódování selhalo" msgid "Muxing: This may take a while..." msgstr "Muxing: Může to chvíli trvat..." msgid "Scan this DVD source" msgstr "Prohledat tento DVD zdroj" msgid "AutoScan" msgstr "Automatické prohledání" msgid "Suspend" msgstr "Odložit" #, c-format msgid "Suspend failed: %s" msgstr "Nepodařilo se odložit: %s" msgid "Suspend failed" msgstr "Nepodařilo se odložit" msgid "Shutdown" msgstr "Vypnout" #, c-format msgid "Shutdown failed: %s" msgstr "Nepodařilo se vypnout: %s" msgid "Shutdown failed" msgstr "Nepodařilo se vypnout" msgid "Encoding" msgstr "Kódování" #, c-format msgid "press %d %d" msgstr "Stisknutí %d %d" #, c-format msgid "" "Invalid Settings:\n" "%s" msgstr "Neplatné nastavení:\n%s" msgid "Cancel" msgstr "Zrušit" #, c-format msgid "%s: %.4g (Warning: lossless)" msgstr "%s: %.4g (Varování: bezztrátové" #, c-format msgid "HandBrake %s/%s is now available (you have %s/%d)." msgstr "HandBrake %s/%s je nyní dostupný (máte %s/%d)." msgid "Encode Complete" msgstr "Kódování dokončeno" msgid "Put down that cocktail, Your HandBrake queue is done!" msgstr "Dejte si koktejl. Vaše řada HandBrake je hotová!" msgid "Your encode is complete." msgstr "Kódování je dokončeno." msgid "Shutting down the computer" msgstr "Vypíná se počítač" msgid "Putting computer to sleep" msgstr "Uspává se počítač" msgid "Quiting Handbrake" msgstr "Ukončuje se Handbrake" msgid "Bottom" msgstr "Dole" #, c-format msgid "open failed: %s\n" msgstr "Nepodařilo se otevřít: %s\n" msgid "No key for dictionary item" msgstr "Žádná klávesa pro slovníkovou položku" msgid "Invalid container type. This shouldn't happen" msgstr "Neplatný typ kontejneru. To by se nemělo stát" #, c-format msgid "%s:missing a requried attribute" msgstr "%s: Chybí požadovaná vlastnost" #, c-format msgid "" "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" msgstr "Použití: %s [-I ] \nPřehled:\n Vytvoří zdrojový plist ze zdrojového seznamu\nVolby:\n I - zahrnout cestu pro hledání souborů\n Vstupní soubor se zdroji\n Výstupní soubor se zdroji plist\n" msgid "language" msgstr "Jazyk" msgid "Language" msgstr "Jazyk" msgid "" "The language this text is in, as an ISO code. Pango can use this as a hint " "when rendering the text. If you don't understand this parameter, you " "probably don't need it" msgstr "Jazyk, v němž je tento text, jako kód ISO. Pango jej může použít při vykreslování textu jako nápovědu. Pokud tomuto parametru nerozumíte, pravděpodobně jej nepotřebujete." msgid "" "The preferred place to ellipsize the string, if the cell renderer does not " "have enough room to display the entire string" msgstr "Upřednostňované místo pro výpustku (tři tečky) v řetězci, pokud vykreslovač buňky nemá dostatek místa pro zobrazení celého řetězce." msgid "" "How to break the string into multiple lines, if the cell renderer does not " "have enough room to display the entire string" msgstr "Jak řetězec zalomit do více řádků, pokud vykreslovač buňky nemá dostatek místa pro zobrazení celého řetězce." msgid "" "Render the subtitle over the video.\n" "\n" "The subtitle will be part of the video and can not be disabled." msgstr "Vložit titulky do videa.\n\nTitulky budou součástí obrazového záznamu a nepůjde je zakázat." msgid "Burned In" msgstr "Vloženo" msgid "" "Set the default output subtitle track.\n" "\n" "Most players will automatically display this\n" "subtitle track whenever the video is played.\n" "\n" "This is useful for creating a \"forced\" track\n" "in your output." msgstr "Nastavit výchozí výstupní stopu titulků.\n\nVětšina přehrávačů automaticky zobrazí tuto\nstopu titulků vždycky, když je obraz přehráván.\n\nToto je užitečné při vytváření \"vynucené\" stopy\nve vašem výstupu." msgid "Default" msgstr "Výchozí" msgid "" "Use only subtitles that have been flagged\n" "as forced in the source subtitle track\n" "\n" "\"Forced\" subtitles are usually used to show\n" "subtitles during scenes where someone is speaking\n" "a foreign language." msgstr "Použít pouze titulky označené příznakem\njako vynucené ve zdrojové stopě titulků\n\n\"Vynucené\" titulky se obvykle používají na zobrazení\ntitulků během scén, kde někdo mluví cizím\njazykem." msgid "Forced Only" msgstr "Vynuceno pouze" msgid "" "Add (or subtract) an offset (in milliseconds)\n" "to the start of the SRT subtitle track.\n" "\n" "Often, the start of an external SRT file\n" "does not coincide with the start of the video.\n" "This setting allows you to synchronize the files." msgstr "Přidat (nebo odečíst) posun (v milisekundách)\nna začátek stopy titulku SRT.\n\nČasto se začátek vnějšího souboru SRT\nčasově neshoduje se začátkem obrazu.\nToto nastavení umožňuje soubory srovnat." msgid "SRT Offset" msgstr "Posun SRT" msgid "Off" msgstr "Vypnuto" msgid "" "The source subtitle track\n" "\n" "You can choose any of the subtitles\n" "recognized in your source file.\n" "\n" "In addition, there is a special track option\n" "\"Foreign Audio Search\". This option will add\n" "an extra pass to the encode that searches for\n" "subtitles that may correspond to a foreign\n" "language scene. This option is best used in\n" "conjunction with the \"Forced\" option." msgstr "Stopa titulků zdroje\n\nMůžete vybrat kterékoli z titulků\nrozpoznaných ve vašem zdrojovém souboru.\n\nNavíc je tu ještě zvláštní volba pro stopu, kterou\nje hledání zvuku v cizím jazyce. Tato volba přidá\ndo zakódování další průchod, jenž vyhledá\ntitulky, jež odpovídají výstupu v cizím\njazyce. Tuto volbu je nejlepší použít\nve spojení s volbou \"Vynuceno\"." msgid "Track" msgstr "Stopa" msgid "About HandBrake" msgstr "O programu HandBrake" msgid "" "Copyright © 2008 - 2013 John Stebbins\n" "Copyright © 2004 - 2013, HandBrake Devs" msgstr "Autorské právo © 2008 - 2013 John Stebbins\nAutorské právo © 2004 - 2013, HandBrake Devs" msgid "" "HandBrake is a GPL-licensed, multiplatform, multithreaded video transcoder." msgstr "HandBrake je GPL licencovaný, multiplatformní, vícevláknový převaděč obrazu." msgid "http://handbrake.fr" msgstr "http://handbrake.fr" msgid "" "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n" "\n" "HandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgstr "HandBrake je svobodným programem; můžete jej rozdávat a/nebo upravovat za podmínek GNU General Public License (GPL), jak jsou zveřejněny Free Software Foundation; buď ve verzi 2 licence, nebo (podle své volby) v kterékoli pozdější verzi.\n\nHandBrake je šířen v naději, že bude užitečný, ale BEZ JAKÉKOLI ZÁRUKY; také bez předpokládané záruky PRODEJNOSTI nebo POUŽITELNOSTI PRO NĚJAKÝ URČITÝ ÚČEL: Více podrobností naleznete v GNU General Public License.\n\nKopii GNU General Public License byste měl obdržet společně s Glade; a pokud ne, napište Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA." msgid "_Minimize/Maximize" msgstr "Zvě_tšit/Zmenšit" msgid "_Pause Queue" msgstr "P_ozastavit řadu" msgid "_Quit" msgstr "_Ukončit" msgid "_About" msgstr "O _programu" msgid "HandBrake" msgstr "HandBrake" msgid "_File" msgstr "_Soubor" msgid "_Source" msgstr "_Zdroj" msgid "Single _Title" msgstr "Jeden _titul" msgid "_Destination" msgstr "_Cíl" msgid "_Preferences" msgstr "_Nastavení" msgid "_Queue" msgstr "Ř_ada" msgid "_Add" msgstr "_Přidat" msgid "Add _Multiple" msgstr "Přidat _více" msgid "_Start" msgstr "_Spustit" msgid "_Pause" msgstr "_Pozastavit" msgid "_View" msgstr "_Pohled" msgid "HandBrake For _Dumbies" msgstr "HandBrake pro natvrdlé" msgid "_Show Presets" msgstr "_Ukázat přednastavení" msgid "_Preview" msgstr "_Náhled" msgid "_Activity Window" msgstr "Č_innost" msgid "Show _Queue" msgstr "Ukázat ř_adu" msgid "_Presets" msgstr "_Přednastavení" msgid "_Save" msgstr "_Uložit" msgid "_Delete" msgstr "_Smazat" msgid "_Make Default" msgstr "_Udělat výchozím" msgid "_New Folder" msgstr "_Nová složka" msgid "_Export" msgstr "_Vyvést" msgid "_Import" msgstr "_Zavést" msgid "_Update Built-in Presets" msgstr "_Obnovit vestavěná přednastavení" msgid "_Help" msgstr "_Nápověda" msgid "_Guide" msgstr "Pří_ručka" msgid "Start Encoding" msgstr "Spustit kódování" msgid "Start" msgstr "Spustit" msgid "Pause Encoding" msgstr "Pozastavit kódování" msgid "Pause" msgstr "Pozastavit" msgid "Add to Queue" msgstr "Přidat do řady" msgid "Enqueue" msgstr "Zařadit" msgid "Show Queue" msgstr "Ukázat řadu" msgid "Queue" msgstr "Řada" msgid "" "Open Picture Settings and Preview window.\n" "Here you can adjust cropping, resolution, aspect ratio, and filters." msgstr "Otevřít nastavení obrázku a okno s náhledem.\nZde můžete upravit ořez, rozlišení, poměr stran a filtry." msgid "Preview" msgstr "Náhled" msgid "Show Activity Window" msgstr "Zobrazit okno činností" msgid "Activity" msgstr "Činnost" msgid "Source:" msgstr "Zdroj:" msgid "None" msgstr "Žádný" msgid "Title:" msgstr "Název:" msgid "" "Set the title to encode.\n" "By default the longest title is chosen.\n" "This is often the feature title of a DVD." msgstr "Nastavit titul k zakódování.\nVe výchozím nastavení je vybrán nejdelší titul.\nTo je často hlavní titul DVD." msgid "Angle:" msgstr "Úhel:" msgid "For multi-angle DVD's, select the desired angle to encode." msgstr "Pro víceúhlová DVD, vyberte požadovaný úhel k zakódování." msgid "Reset All Titles" msgstr "Nastavit všechny názvy znovu" msgid "Apply current settings to all titles" msgstr "Použít nynější nastavení na všechny názvy" msgid "Range of title to encode. Can be chapters, seconds, or frames." msgstr "Rozsah titulu k zakódování. Můžou to být kapitoly, sekundy nebo snímky." msgid "Set the first chapter to encode." msgstr "Nastavit první kapitolu k zakódování." msgid "Set the last chapter to encode." msgstr "Nastavit poslední kapitolu k zakódování." msgid "Duration:" msgstr "Doba trvání:" msgid "Destination" msgstr "Cíl" msgid "File:" msgstr "Soubor:" msgid "Destination filename for your encode." msgstr "Cílový souborový název pro zakódovaný soubor." msgid "Destination directory for your encode." msgstr "Cílový adresář pro zakódovaný soubor." msgid "Destination Directory" msgstr "Cílový adresář" msgid "Format:" msgstr "Formát:" msgid "Format to mux encoded tracks to." msgstr "Formátovat do mux zakódovaných stop." msgid "iPod 5G Support" msgstr "Podpora pro iPod 5G" msgid "Add iPod Atom needed by some older iPods." msgstr "Přidat iPod Atom, jenž je potřeba některými staršími zařízeními iPod." msgid "Web optimized" msgstr "Vyladěno pro internet" msgid "" "Optimize the layout of the MP4 file for progressive download.\n" "This allows a player to initiate playback before downloading the entire file." msgstr "Přizpůsobit uspořádání souboru MP4 pro postupné stahování.\nTo přehrávači umožní začít přehrávat před dokončením stahování celého souboru." msgid "Large file (>4GB)" msgstr "Velký soubor (>4GB)" msgid "" "Allow 64 bit MP4 file which can be over 4GB.\n" "\n" "Caution: This option may break device compatibility." msgstr "Povolit 64 bitový soubor MP4, který může mít víc než 4 GB.\n\n Výstraha: Tato volba může vést k porušení kompatibility zařízení." msgid "Presets List" msgstr "Seznam přednastavení" msgid "Source Codec:" msgstr "Kodek zdroje:" msgid "Dimensions:" msgstr "Rozměry:" msgid "Aspect: " msgstr "Poměr:" msgid "Frame Rate:" msgstr "Snímková rychlost:" msgid "Source Picture Parameters" msgstr "Parametry zdrojového obrázku" msgid "Autocrop:" msgstr "Automatické oříznutí:" msgid "Crop:" msgstr "Ořez:" msgid "Crop Dimensions:" msgstr "Rozměry pro ořezání:" msgid "Cropping" msgstr "Ořezání" msgid "Scale Dimensions:" msgstr "Rozměry pro změnu velikosti:" msgid "Optimal for Source:" msgstr "Nejlepší pro zdroj:" msgid "Anamorphic:" msgstr "Pokřivené:" msgid "Scaling" msgstr "Změna velikosti" msgid "Presentation Dimensions:" msgstr "Rozměry při promítání:" msgid "Summary" msgstr "Shrnutí" msgid "Left Crop" msgstr "Ořezání vlevo" msgid "Top Crop" msgstr "Ořezání nahoře" msgid "Bottom Crop" msgstr "Ořezání dole" msgid "Right Crop" msgstr "Oříznout vpravo" msgid "Auto Crop" msgstr "Automaticky oříznout" msgid "Automatically crop black borders around edges of the video." msgstr "Automaticky oříznout černý lem okolo okrajů obrazu." msgid "Loose Crop" msgstr "Volné ořezání" msgid "" "When picture settings require that the image\n" "dimensions be rounded to some multiple number\n" "of pixels, this setting will crop a few extra pixels\n" "instead of doing exact cropping and then scaling to\n" "the required multiple." msgstr "Když nastavení obrázku vyžaduje, aby byly\nrozměry obrázku zaokrouhleny na určitý násobek\npixelů Toto nastavení ořeže několik pixelů navíc,\nmísto toho, aby se udělalo přesné ořezání, a potom\nse provedla změna velikosti na požadovaný násobek." msgid "width:" msgstr "Šířka:" msgid "" "This is the width that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "Toto je šířka, se kterou bude obraz uložen.\nSkutečné rozměry zobrazení se budou lišit, pokud poměr strany pixelu není 1:1." msgid "height:" msgstr "Výška:" msgid "" "This is the height that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "Toto je výška, se kterou bude obraz uložen.\nSkutečné rozměry zobrazení se budou lišit, pokud poměr strany pixelu není 1:1." msgid "Optimal for source" msgstr "Nejlepší pro zdroj" msgid "" "If enabled, select the 'optimal' storage resolution.\n" "This will be the resolution that most closely matches the source resolution after cropping." msgstr "Je-li povoleno, vybrat nejlepší rozlišení pro ukládání.\nToto bude rozlišení, které nejvíce odpovídá rozlišení zdroje po ořezání." msgid "" "Anamorphic Modes:\n" "\n" "None - Force pixel aspect ratio to 1:1.\n" "Loose - Align dimensions to chosen 'Alignment' value\n" " and pick pixel aspect ratio that preserves the\n" " original display aspect ratio\n" "Strict - Keep original source dimensions and pixel\n" " aspect ratio" msgstr "Pokřivené režimy:\n\nŽádný - Vynutit poměr stran pixelu 1:1.\nLoose - Zarovnat rozměry na zvolenou hodnotu zarovnání \n a zvolit poměr stran pixelu, jenž zachovává\n původní poměr stran zobrazení\nPřísný - Zachovat původní rozměry zdroje a poměr \n stran pixelu" msgid "Alignment:" msgstr "Zarovnání:" msgid "" "Align storage dimensions to multiples of this value.\n" "\n" "This setting is only necessary for compatibility with some devices.\n" "You should use 2 unless you experience compatibility issues." msgstr "Zarovnat rozměry ukládání na násobky této hodnoty.\n\nToto nastavení je nezbytné jen pro snášenlivost s některými zařízeními.\nMěl byste použít 2, dokud se nesetkáte s potížemi se snášenlivostí." msgid "Storage Geometry" msgstr "Uspořádání úložiště" msgid "" "This is the display width. It is the result of scaling the storage " "dimensions by the pixel aspect." msgstr "Toto je šířka zobrazení. Je to výsledek změny velikosti rozměrů pro ukládání pomocí stránky pixelu." msgid "Pixel Aspect:" msgstr "Strana pixelu:" msgid "" "Pixel aspect defines the shape of the pixels.\n" "\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "Strana pixelu určuje tvar pixelů.\n\nPoměr 1:1 stanovuje čtvercový pixel. Jiné hodnoty stanovují obdélníkové tvary.\nPřehrávače změní velikost obrázku tak, aby dosáhly stanovené stránky." msgid ":" msgstr ":" msgid "" "Pixel aspect defines the shape of the pixels.\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "Strana pixelu určuje tvar pixelů.\nPoměr 1:1 stanovuje čtvercový pixel. Jiné hodnoty stanovují obdélníkové tvary.\nPřehrávače změní velikost obrázku tak, aby dosáhly stanovené stránky." msgid "Keep Aspect" msgstr "Zachovat poměr" msgid "" "If enabled, the original display aspect of the source will be maintained." msgstr "Je-li povoleno, bude zachován původní vzhled zobrazení zdroje." msgid "Display Aspect:" msgstr "Zobrazit poměr stran:" msgid "Display Geometry" msgstr "Uspořádání zobrazení" msgid "Grayscale" msgstr "Odstíny šedi" msgid "If enabled, filter colour components out of video." msgstr "Je-li povoleno, filtrovat složky barvy mimo obraz." msgid "Deblock:" msgstr "Odstranit bloky:" msgid "" "The deblocking filter removes a common type of compression artifact.\n" "If your source exhibits 'blockiness', this filter may help clean it up." msgstr "Filtr na odstranění bloků odstraňuje běžný typ kompresního artefaktu.\nPokud se u vašeho zdroje projevuje blokovitost, tento filtr může pomoci s jejím odstraněním." msgid "Denoise Filter:" msgstr "Filtr pro odstranění šumu:" msgid "" "Denoise filtering reduces or removes the appearance of noise and grain.\n" "Film grain and other types of high frequency noise are difficult to compress.\n" "Using this filter on such sources can result in smaller file sizes." msgstr "Filtr na odstranění šumu je nízkopásmový filtr, který zmenšuje nebo odstraňuje šum a zrnitost.\nZrnitost filmu a jiné typy vysokokmitočtového šumu je obtížné zkomprimovat.\nPoužití tohoto filtru na tyto zdroje může vést k menším velikostem souborů." msgid "Denoise Preset:" msgstr "Přednastavení pro odstranění šumu:" msgid "Denoise Tune:" msgstr "Vyladění dstranění šumu:" msgid "" "Custom denoise filter string format\n" "\n" "SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgstr "Vlastní formát řetězce filtru na odstranění šumu\n \nSpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgid "Detelecine:" msgstr "Odstranit filmové snímání:" msgid "" "This filter removes 'combing' artifacts that are the result of telecining.\n" "\n" "Telecining is a process that adjusts film framerates that are 24fps to NTSC video frame rates which are 30fps." msgstr "Filtr odstraňuje 'česací' artefakty, které jsou výsledkem filmového snímání.\n\nFilmové snímání je proces, který upravuje rychlosti snímkování filmu, které jsou 24 snímků za sekundu (SZS) na rychlosti snímkování obrazu NTSC, které jsou 30 SZS." msgid "" "Custom detelecine filter string format\n" "\n" "JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgstr "Vlastní formát řetězce filtru na odstranění filmového snímače\n \nJunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgid "Decomb" msgstr "Odstranění hřebenu" msgid "" "Choose decomb or deinterlace filter options.\n" "\n" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced.\n" "\n" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "Vyberte volby pro filtr na odstranění hřebenu nebo odstranění prokládání.\n\nFiltr na odstranění hřebenu výběrově odstraňuje prokládání snímků, jež se jeví být prokládané. To uchová kvalitu snímků, které nejsou prokládány.\n\nKlasický filtr na odstranění prokládání je použit na všechny snímky.\nSnímky, které nejsou prokládány, utrpí určitým poklesem jakosti." msgid "Deinterlace" msgstr "Odstranit prokládání" msgid "Decomb:" msgstr "Odstranit hřeben:" msgid "" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced." msgstr "Filtr na odstranění hřebenu výběrově odstraňuje prokládání snímků, jež se jeví být prokládané.\nTo uchová kvalitu snímků, které nejsou prokládány." msgid "" "Custom decomb filter string format\n" "\n" "Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\n" "BlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\n" "ErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgstr "Vlastní formát řetězce filtru na odstranění hřebenu\n Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\nBlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\nErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgid "Deinterlace:" msgstr "Odstranit prokládání:" msgid "" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "Klasický filtr pro odstranění prokládání se používá na všechny snímky.\nSnímky, jež nejsou prokládané, zakouší určité zhoršení jakosti." msgid "" "Custom deinterlace filter string format\n" "\n" "YadifMode:YadifParity:McdintMode:McdeintQp" msgstr "Vlastní formát řetězce filtru pro odstranění prokládání.\n\nYadifMode:YadifParity:McdintMode:McdeintQp" msgid "Filters" msgstr "Filtry" msgid "Picture" msgstr "Obrázek" msgid "Video Encoder:" msgstr "Kodér obrazu:" msgid "Available video encoders." msgstr "Dostupné kodéry obrazu." msgid "Framerate:" msgstr "Rychlost snímkování:" msgid "" "Output framerate.\n" "\n" "'Same as source' is recommended. If your source video has\n" "a variable framerate, 'Same as source' will preserve it." msgstr "Výstupní rychlost snímkování.\n\nDoporučena je stejná, jakou má zdroj. Pokud má zdroj obrazu\nproměnlivou rychlost snímkování, volba Stejné jako zdroj ji zachová." msgid "Constant Framerate" msgstr "Stálá rychlost snímkování" msgid "Same as source" msgstr "Stejné jako zdroj" msgid "kbps" msgstr "kb/s" msgid "(variable)" msgstr "(proměnná)" msgid "(constant)" msgstr "(stálá)" msgid "Enables constant framerate output." msgstr "Povolí výstup se stálou rychlostí snímkování." msgid "Peak Framerate (VFR)" msgstr "Nejvyšší rychlost snímkování (PromRS)" msgid "" "Enables variable framerate output with a peak\n" "rate determined by the framerate setting.\n" "\n" "VFR is not compatible with some players." msgstr "Povolí výstup s proměnlivou rychlostí snímkování s nejvyšší\nrychlostí určenou nastavením rychlosti snímkování.\nProměnlivá rychlost snímkování (PromRS) se neslučuje s některými přehrávači." msgid "Variable Framerate" msgstr "Proměnlivá rychlost snímkování" msgid "" "Enables variable framerate output.\n" "\n" "VFR is not compatible with some players." msgstr "Povolí výstup s proměnlivou rychlostí snímkování.\nProměnlivá rychlost snímkování (PromRS) se neslučuje s některými přehrávači." msgid "" "Set the desired quality factor.\n" "The encoder targets a certain quality.\n" "The scale used by each video encoder is different.\n" "\n" "x264's scale is logarithmic and lower values correspond to higher quality.\n" "So small decreases in value will result in progressively larger increases\n" "in the resulting file size. A value of 0 means lossless and will result\n" "in a file size that is larger than the original source, unless the source\n" "was also lossless.\n" "\n" "FFMpeg's and Theora's scale is more linear.\n" "These encoders do not have a lossless mode." msgstr "Nastavit požadovaný koeficient kvality.\nKodér cílí na určitou jakost.\nMěřítko používané každým kodérem obrazu je jiné. \n\nMěřítko x264 je logaritmické a nižší hodnoty odpovídají vyšší kvalitě.\nMalý pokles hodnoty tedy povede k progresivně většímu růstu velikosti\nvýsledného souboru. Hodnota 0 znamená, že kvalita je bezztrátová,\na výsledkem je velikost souboru větší, než jakou měl původní soubor,\nledaže by byl zdroj rovněž bezztrátový.\n\nMěřítko u FFMpeg a Theory je více lineární.\nTyto kodéry nemají bezztrátový režim." msgid "Constant Quality:" msgstr "Stálá jakost:" msgid "Bitrate (kbps): " msgstr "Datový tok (kB/s): " msgid "" "Set the average bitrate.\n" "\n" "The instantaneous bitrate can be much higher or lower at any point in time.\n" "But the average over a long duration will be the value set here. If you need\n" "to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings." msgstr "Nastavit průměrný datový tok.\n\nOkamžitý datový tok může být vyšší nebo nižší v kterémkoli časovém bodě.\nAle průměr pro dlouhou dobu trvání bude hodnota nastavená zde.\nPokud okamžitý datový tok potřebujete omezit, podívejte se na nastavení x264 vbv-bufsize a vbv-maxrate." msgid "2-Pass Encoding" msgstr "Dvouprůchodové kódování" msgid "" "Perform 2 Pass Encoding.\n" "\n" "The 'Bitrate' option is prerequisite. During the 1st pass, statistics about\n" "the video are collected. Then in the second pass, those statistics are used\n" "to make bitrate allocation decisions." msgstr "Provést dvouprůchodové kódování.\n\nPředpokladem je volba Datový tok. Během prvního průchodu jsou\nshromažďovány statistiky o obrazu. Při druhém průchodu jsou tyto\nstatistiky použity při rozhodování o rozvržení datového toku." msgid "Turbo First Pass" msgstr "Rychlý první průchod" msgid "" "During the 1st pass of a 2 pass encode, use settings that speed things " "along." msgstr "Během prvního průchodu dvouprůchodového kódování použít nastavení, která věci dál urychlí." msgid "Use Advanced Options" msgstr "Použít rozšířené volby" msgid "" "Use advanced options Tab for x264 settings.\n" "\n" "Use at your own risk!" msgstr "Použít kartu s pokročilými volbami k nastavení x264.\n\nPoužijte na vlastní nebezpečí!" msgid "Preset:" msgstr "Přednastavení:" msgid "" "Adjusts encoder settings to trade off compression efficiency against encoding speed.\n" "\n" "This establishes your default encoder settings.\n" "Tunes, profiles, levels and advanced option string will be applied to this.\n" "You should generally set this option to the slowest you can bear since slower\n" "settings will result in better quality or smaller files." msgstr "Upraví nastavení kodéru pro vzdání se účinnosti komprese oproti rychlosti kódování.\n\nTímto se zřídí vaše výchozí nastavení kodéru.\nSeřízení, profily, úrovně a rozšířené volby budou použity na toto.\n\nObecně byste tuto volbu měl nastavit na nejpomalejší, kterou snesete,\nprotože pomalejší nastavení dávají ve výsledku lepší kvalitu nebo menší soubory." msgid "Tune:" msgstr "Naladění:" msgid "" "Tune settings to optimize for common scenarios.\n" "\n" "This can improve effeciency for particular source characteristics or set\n" "characteristics of the output file. Changes will be applied after the\n" "preset but before all other parameters." msgstr "Přizpůsobit nastavení, aby byla vyladěna pro běžné scénáře.\n\nToto může zlepšit účinnost pro vlastnosti určitého zdroje nebo nastavit\nvlastnosti výstupního souboru. Změny se použijí po přednastavení\nale přede všemi ostatními parametry." msgid "Fast Decode" msgstr "Rychlé dekódování" msgid "" "Reduce decoder CPU usage.\n" "\n" "Set this if your device is struggling to play the output (dropped frames)." msgstr "Omezit využití CPU dekodérem.\n\nNastavte, pokud se vaše zařízení potýká s přehráváním výstupu (zahozené snímky)." msgid "Zero Latency" msgstr "Nulová prodleva" msgid "" "Minimize latency between input to encoder and output of decoder.\n" "\n" "This is useful for broadcast of live streams.\n" "\n" "Since HandBrake is not suitable for live stream broadcast purposes,\n" "this setting is of little value here." msgstr "Zmenšit prodlevu mezi vstupem do kodéru a výstupem dekodéru.\n\nToto je užitečné pro přenos živých proudů.\n\nProtože HandBrake není vhodný pro přenos živých proudů,\nmá toto nastavení zde malý význam." msgid "Profile:" msgstr "Profil:" msgid "" "Sets and ensures compliance with the specified profile.\n" "\n" "Overrides all other settings." msgstr "Nastaví a zajistí shodu se zadaným profilem.\n\nPotlačí všechna ostatní nastavení." msgid "Level:" msgstr "Úroveň:" msgid "" "Sets and ensures compliance with the specified level.\n" "\n" "Overrides all other settings." msgstr "Nastaví a zajistí shodu se zadanou úrovní .\n\nPotlačí všechna ostatní nastavení." msgid "More Settings:" msgstr "Další nastavení:" msgid "" "Additional encoder settings.\n" "\n" "Colon separated list of encoder options." msgstr "Dodatečné nastavení kodéru.\n\nDvojtečkou oddělený seznam voleb pro kodér." msgid "Video" msgstr "Obraz" msgid "Selection Behavior:" msgstr "Chování výběru" msgid "Remove" msgstr "Odstranit" msgid "Available Languages" msgstr "Dostupné jazyky" msgid "Selected Languages" msgstr "Vybrané jazyky" msgid "Use only first encoder for secondary audio" msgstr "Použít pouze první kodér pro doplňkový zvuk" msgid "" "Only the primary audio track will be encoded with the full encoder list.\n" "All other secondary audio output tracks will be encoded with first encoder only." msgstr "Pouze hlavní zvuková stopa bude zakódována pomocí celého seznamu pro kodér.\nVšechny vedlejší zvukové výstupní stopy budou zakódovány pouze pomocí prvního kodéru." msgid "Auto Passthru:" msgstr "Automatický průchod:" msgid "MP3" msgstr "MP3" msgid "" "Enable this if your playback device supports MP3.\n" "This permits MP3 passthru to be selected when automatic passthru selection is enabled." msgstr "Toto povolit, pokud vaše zařízení pro přehrávání podporuje MP3.\nToto dovolí, aby byl vybrán průchod MP3 passthru, když je povolen automatický výběr průchodu." msgid "" "Enable this if your playback device supports AAC.\n" "This permits AAC passthru to be selected when automatic passthru selection is enabled." msgstr "Toto povolit, pokud vaše zařízení pro přehrávání podporuje AAC.\nToto dovolí, aby byl vybrán průchod AAC, když je povolen automatický výběr průchodu." msgid "" "Enable this if your playback device supports AC-3.\n" "This permits AC-3 passthru to be selected when automatic passthru selection is enabled." msgstr "Toto povolit, pokud vaše zařízení pro přehrávání podporuje AC-3.\nToto dovolí, aby bylo vybráno AC-3 passthru, když je povolen automatický výběr passthru." msgid "" "Enable this if your playback device supports DTS.\n" "This permits DTS passthru to be selected when automatic passthru selection is enabled." msgstr "Toto povolit, pokud vaše zařízení pro přehrávání podporuje DTS.\nToto dovolí, aby bylo vybráno DTS passthru, když je povolen automatický výběr passthru." msgid "" "Enable this if your playback device supports DTS-HD.\n" "This permits DTS-HD passthru to be selected when automatic passthru selection is enabled." msgstr "Toto povolit, pokud vaše zařízení pro přehrávání podporuje DTS-HD.\nToto dovolí, aby bylo vybráno DTS-HD passthru, když je povolen automatický výběr passthru." msgid "Passthru Fallback:" msgstr "Nouzový passthru:" msgid "" "Set the audio codec to encode with when a suitable track can not be found " "for audio passthru." msgstr "Nastavit zvukový kodek, který se má použít při kódování, když nelze najít vhodnou stopu pro \"audio passthru\"." msgid "Audio Encoder Settings:" msgstr "Nastavení kódování zvuku:" msgid "Each selected source track will be encoded with all selected encoders" msgstr "Každá vybraná zdrojová stopa bude zakódována pomocí všech vybraných kodérů." msgid "Encoder" msgstr "Kodér" msgid "Bitrate/Quality" msgstr "Datový tok/Jakost" msgid "Mixdown" msgstr "Závěrečné smíchání" msgid "Samplerate" msgstr "Vzorkovací kmitočet" msgid "Gain" msgstr "Zesílení" msgid "Audio Defaults" msgstr "Výchozí nastavení zvuku" msgid "Add new audio settings to the list" msgstr "Přidat nové nastavení zvuku do seznamu" msgid "Add All" msgstr "Přidat vše" msgid "Add all audio tracks to the list" msgstr "Přidat všechny zvukové stopy do seznamu" msgid "Reload Defaults" msgstr "Nahrát výchozí hodnoty znovu" msgid "Reload all audio settings from defaults" msgstr "Nahrát znovu všechna nastavení zvuku z výchozích hodnot" msgid "Audio List" msgstr "Seznam zvukových stop" msgid "Preferred Language: None" msgstr "Upřednostňovaný jazyk: Žádný" msgid "Add Foreign Audio Search Pass" msgstr "Přidat cestu pro hledání zvuku v cizím jazyce" msgid "" "Add \"Foreign Audio Search\" when the default audio track is your preferred language.\n" "This search pass finds short sequences of foreign audio and provides subtitles for them." msgstr "Přidat hledání zvuku v cizím jazyce, když výchozí zvuková stopa je ve vámi upřednostňovaném jazyce.\nToto hledání nalezne krátké úryvky zvuku v cizím jazyce a poskytne pro ně titulky." msgid "Add subtitle track if default audio is foreign" msgstr "Přidat stopu titulků, pokud je výchozí zvuk v cizím jazyce" msgid "" "When the default audio track is not your preferred language, add a subtitle " "track." msgstr "Přidat stopu titulků, pokud výchozí zvuk není ve vámi upřednostňované jazyce." msgid "Add Closed Captions when available" msgstr "Přidat skryté titulky, když jsou dostupné" msgid "" "Closed captions are text subtitles that can be added to any container as a " "soft subtitle track (not burned)" msgstr "Zavřené titulky jsou textové titulky, které je možné přidat do kteréhokoli kontejneru jako měkkou stopu s titulky (nevypálenoo)" msgid "Subtitle Defaults" msgstr "Výchozí nastavení titulků" msgid "Add new subtitle settings to the list" msgstr "Přidat nastavení pro nové titulky do seznamu" msgid "Add all subtitle tracks to the list" msgstr "Přidat všechny titulkové stopy do seznamu" msgid "Reload all subtitle settings from defaults" msgstr "Nahrát znovu všechna nastavení titulků z výchozích hodnot" msgid "Subtitle List" msgstr "Seznam titulků" msgid "Reference Frames:" msgstr "Referenční snímky:" msgid "" "Sane values are ~1-6. The more you add, the better the compression, but the slower the encode.\n" "Cel animation tends to benefit from more reference frames a lot more than film content.\n" "\n" "Note that many hardware devices have limitations on the number of supported reference\n" "frames, so if you're encoding for a handheld or standalone player, don't touch this unless\n" "you're absolutely sure you know what you're doing!" msgstr "Rozumné hodnoty jsou ~1-6. Čím více přidáte, tím lepší komprese, ale pomalejší kódování.\nAnimace má sklon mít užitek z více referenčních snímků mnohem více než obsah filmu.\nVšimněte si, že mnohá zařízení mají omezení na počet referenčních snímků,\ntakže pokud provádíte zakódování pro počítač do ruky nebo samostatný přehrávač,\nnešahejte na to, pokud si nejste naprosto jistí, co děláte!" msgid "Maximum B-Frames:" msgstr "Maximum B-snímky:" msgid "" "Sane values are ~2-5. This specifies the maximum number of sequential B-frames that the encoder can use.\n" "\n" "Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.\n" "Cel-animated source material and B-pyramid also significantly increase the usefulness of larger\n" "values.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off)." msgstr "Rozumné hodnoty jsou ~2-5. Toto udává největší počet sekvenčních B-snímků, které může kodér použít.\nVelké počty obecně moc nepomáhají, dokud nejsou přizpůsobivé B-snímky nastaveny na nejvýhodnější.\nCel-animovaný zdrojový materiál a B-jehlan také podstatně zvětšují prospěšnost větších hodnot.\n\nZákladní profil, požadovaný iPody a podobnými zařízeními, vyžaduje, aby byly B-snímky nastaveny na 0 (vypnuto)." msgid "Pyramidal B-Frames:" msgstr "Jehlanovité B-snímky:" msgid "" "B-pyramid improves compression by creating a pyramidal structure (hence the name)\n" "of B-frames, allowing B-frames to reference each other to improve compression.\n" "\n" "Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit." msgstr "B-jehlan zlepšuje kompresi vytvořením jehlanovité stavby (odtud název) z\nB-snímků, dovoluje B-snímkům, aby jeden na druhý odkazovaly kvůli zlepšení\nkomprese.\n\nVyžaduje Max B-snímky větší než 1; nejvýhodnější přizpůsobivé B-snímky se doporučují kvůli plnému užitku z komprese." msgid "Weighted P-Frames:" msgstr "Vážené P-snímky:" msgid "" "Performs extra analysis to decide upon weighting parameters for each frame.\n" "\n" "This improves overall compression slightly and improves the quality of fades greatly.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires weighted P-frame\n" "prediction to be disabled. Note that some devices and players, even those that support\n" "Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is\n" "completely incompatible with it, for example." msgstr "Provede další rozbor kvůli rozhodnutí o zvážení parametrů pro každý snímek.\n\nTo lehce zlepší celkovou kompresi a značně vylepší kvalitu prolínání.\nZákladní profil, požadovaný iPody a podobnými zařízeními, vyžaduje, aby bylo\nvážené předvídání P-snímku zakázáno. Všimněte si, že některá zařízení a přehrávače,\ndokonce ty, které podporují hlavní profil, mohou mít s předvídáním váženého\nP-snímku potíže: například Apple TV se s ním naprosto neslučuje." msgid "8x8 Transform" msgstr "Transformace 8x8" msgid "" "The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.\n" "\n" "It improves compression by at least 5% at a very small speed cost and may\n" "provide an unusually high visual quality benefit compared to its compression\n" "gain. However, it requires High Profile, which many devices may not support." msgstr "Transformace 8x8 je jedna z nejužitečnějších vlastností x264 pokud jde o rychlost komprese.\n\nZlepšuje kompresi alespoň o 5% za velmi malou cenu, pokud jde o rychlost,\na může poskytnout neobyčejně velký zisk, pokud jde o obrazovou kvalitu,\nve srovnání s kompresním ziskem. Nicméně vyžaduje nejvyšší kvalitu, což mnohá\nzařízení nemusí podporovat." msgid "CABAC Entropy Encoding" msgstr "Kódování CABAC Entropy" msgid "" "After the encoder has done its work, it has a bunch of data that\n" "needs to be compressed losslessly, similar to ZIP or RAR. H.264 provides\n" "two options for this: CAVLC and CABAC. CABAC decodes a lot slower but\n" "compresses significantly better (10-30%), especially at lower bitrates.\n" "\n" "If you're looking to minimize CPU requirements for video playback, disable this option.\n" "Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled." msgstr "Když kodér udělal svoji práci, má hromadu dat, která je potřeba bezztrátově\nzkomprimovat, což je podobné ZIP nebo RAR. H.264 pro to poskytuje dvě volby:\nCAVLC a CABAC. CABAC dekóduje o hodně pomaleji ale komprimuje podstatně lépe\n(10-30%), obzvláště při nižších datových tocích.\n\nPokud hledáte něco, co zmenší nároky CPU na přehrávání obrazu, tuto volbu zakažte.\nZákladní profil, požadovaný iPody a podobnými zařízeními, vyžaduje, aby byl CABAC zakázán." msgid "Encoding Features" msgstr "Volby pro kódování" msgid "Motion Est. Method:" msgstr "Způsob odhadování pohybu:" msgid "" "Controls the motion estimation method.\n" "\n" "Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.\n" "A better motion search method improves compression at the cost of speed.\n" "\n" "Diamond: performs an extremely fast and simple search using a diamond pattern.\n" "Hexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n" "Uneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n" "Exhaustive: performs a \"dumb\" search of every pixel in a wide area. Significantly slower for only a small compression gain.\n" "Transformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement." msgstr "Ovládá způsob odhadování pohybu.\n\nOdhad pohybu je to, jak kodér odhaduje, jak se každý blok pixelů pohnul.\nLepší způsob hledání pohybu zlepšuje kompresi za cenu rychlosti.\n\nDiamant: provádí nesmírně rychlé a jednoduché hledání pomocí diamantového vzoru.\nŠestiúhelník: provádí poněkud účinnější ale lehce pomalejší hledání pomocí šestiúhelníkového vzoru.\n\nNerovnoměrný Multi-Hex: provádí velice široké hledání pomocí různých druhů vzorů, přesnější zachycení složitého pohybu.\nDůkladné: provádí vyčerpávající hledání každého pixelu v široké oblasti. Podstatně pomalejší, přitom kompresní zisk je malý.\nTransformované důkladné: podobá se důkladnému, ale dělá ještě přesnější rozhodnutí. Podle toho je poněkud pomalé, a zlepšení je jen malé." msgid "Subpel ME & Mode:" msgstr "Subpixel ME & režim:" msgid "" "This setting controls both subpixel-precision motion estimation and mode decision methods.\n" "\n" "Subpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n" "Mode decision is the method used to choose how to encode each block of the frame: a very important decision.\n" "SAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n" "6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n" "11 disables all early terminations in analysis.\n" "10 and 11, the most powerful and slowest options, require adaptive quantization (aq-mode > 0) and trellis 2 (always)." msgstr "Toto nastavení ovládá jak způsob odhadování pohybu o subpixelové přesnosti, tak způsob rozhodování režimu.\n\n Subpixelové odhadování pohybu se používá na zjemňování odhadování pohybu jdoucí za pouhou pixelovou přesnost při současném zlepšení komprese.\n\n Rozhodování režimu je postup používaný pro výběr toho, jak zakódovat každý blok snímku: velice důležité rozhodnutí.\n\n SAD je nejrychlejší způsob, následuje jej SATD, RD, RD zjemňování, a nejpomalejší je QPRD.\n\n 6 nebo vyšší se důrazně doporučuje: Psy-RD, velice výkonná psy optimalizace, která napomáhá uchování podrobností, vyžaduje RD.\n\n 11 zakazuje všechna brzká ukončení rozboru.\n\n 10 a 11, nejvýkonnější a nejpomalejší volby, vyžadují přizpůsobivou kvantizaci (aq-mode > 0) a mříž 2 (vždy)." msgid "Motion Est. Range:" msgstr "Rozsah odhadování pohybu:" msgid "" "This is the distance x264 searches from its initial guess at the\n" "motion of a block in order to try to find its actual motion.\n" "\n" "The default is fine for most content, but extremely high motion video,\n" "especially at HD resolutions, may benefit from higher ranges, albeit at\n" "a high speed cost." msgstr "Toto je vzdálenost hledání x264 od svého počátečního odhadu pohybu bloku,\naby se pokusil najít jeho skutečný pohyb.\n\nPro většinu obsahu je výchozí dobrá, ale obraz s mimořádně velkým pohybem,\nobzvláště při rozlišení HD, může mít užitek z větších rozsahů, třebaže za cenu rychlosti." msgid "Adaptive Direct Mode:" msgstr "Přizpůsobivý přímý režim:" msgid "" "H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n" "\n" "Spatial, the default, is almost always better, but temporal is sometimes useful too.\n" "x264 can, at the cost of a small amount of speed (and accordingly for a small compression gain),\n" "adaptively select which is better for each particular frame." msgstr "H.264 umožňuje v B-snímcích dva odlišné režimy předpovídání, prostorový a časový.\n\nProstorový, který je výchozím režimem, je skoro vždy lepší, ale časový je někdy také užitečný.\nx264 může, za cenu malé ztráty na rychlosti (a podle toho za malý zisk na kompresi), podle\nokolností vybrat, který je lepší, pro každý jednotlivý snímek." msgid "Adaptive B-Frames:" msgstr "Přizpůsobivé B-snímky:" msgid "" "x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n" "\n" "Fast mode takes roughly the same amount of time no matter how many B-frames you specify.\n" "However, while fast, its decisions are often suboptimal.\n" "\n" "Optimal mode gets slower as the maximum number of B-Frames increases,\n" "but makes much more accurate decisions, especially when used with B-pyramid." msgstr "x264 má různé druhy algoritmů na rozhodnutí, kdy použít B-snímky, a kolik jich použít.\n\n Rychlý režim zabere zhruba stejně tolik času bez ohledu na to, kolik B-snímků určíte. Nicméně však, i když je rychlý, jeho rozhodnutí jsou často méně než vhodná.\n\n Nejvhodnější režim zpomaluje, jak se zvyšuje největší počet B-snímků, ale dělá mnohem přesnější rozhodnutí, obzvláště když se použije s B-jehlanem." msgid "Partitions:" msgstr "Úseky:" msgid "" "Mode decision picks from a variety of options to make its decision:\n" "this option chooses what options those are.\n" "\n" "Fewer partitions to check means faster encoding, at the cost of worse\n" "decisions, since the best option might have been one that was turned off." msgstr "Režimové rozhodnutí volí z mnoha druhů voleb, aby učinilo své rozhodnutí:\nTato volba vybírá, které volby to jsou.\nMenší počet částí ke kontrole znamená rychlejší kódování za cenu\nhorších rozhodnutí, protože ta nejlepší volba mohla být tou, jež byla vypnuta." msgid "Trellis:" msgstr "Mříž:" msgid "" "Trellis fine-tunes the rounding of transform coefficients to\n" "squeeze out 3-5% more compression at the cost of some speed.\n" "\n" "\"Always\" uses trellis not only during the main encoding process, but also\n" "during analysis, which improves compression even more, albeit at great speed cost.\n" "\n" "Trellis costs more speed at higher bitrates and requires CABAC." msgstr "Mříž jemně naladí zaokrouhlení transformačních koeficientů, aby\nvymačkala o 3-5 % větší kompresi za cenu nějaké rychlosti.\n\n\"Vždy\" používá mříž nejen během hlavního procesu kódování, ale i během\nrozboru, což zlepšuje kompresi ještě víc, i když za velkou cenu na rychlosti.\n\nMříž stojí více rychlosti při vyšších datových tocích a vyžaduje CABAC." msgid "Analysis" msgstr "Rozbor" msgid "Adaptive Quantization Strength:" msgstr "Přizpůsobivá síla kvantizace:" msgid "" "Adaptive quantization controls how the encoder distributes bits across the frame.\n" "\n" "Higher values take more bits away from edges and complex areas to improve areas with finer detail." msgstr "Přizpůsobivá kvantizace řídí to, jak kodér rozmísťuje bity napříč snímkem.\n\nVyšší hodnoty odvádějí více bitů z okrajů a složitých oblastí kvůli zlepšení oblastí s jemnější podrobností." msgid "Psychovisual Rate Distortion:" msgstr "Psychozrakové zkreslení rychlosti:" msgid "" "Psychovisual rate-distortion optimization takes advantage of the characteristics of human\n" "vision to dramatically improve apparent detail and sharpness.\n" "The effect can be made weaker or stronger by adjusting the strength.\n" "Being an RD algorithm, it requires mode decision to be at least \"6\"." msgstr "Přizpůsobení psychozrakového zkreslení rychlosti těží z vlastnosti lidského\nzraku na dramatické zlepšení zjevných podrobností a ostrosti.\nÚčinek může být udělán slabší nebo silnější upravením jeho síly.\n Algoritmus vyžaduje, aby rozhodnutí režimu bylo alespoň \"6\"." msgid "Psychovisual Trellis:" msgstr "Psychozraková mříž:" msgid "" "Psychovisual trellis is an experimental algorithm to further\n" "improve sharpness and detail retention beyond what Psychovisual RD does.\n" "\n" "Recommended values are around 0.2, though higher values may help for very\n" "grainy video or lower bitrate encodes. Not recommended for cel animation\n" "and other sharp-edged graphics." msgstr "Psychozraková mříž je pokusný algoritmus pro další zlepšení ostrosti\na uchování podrobnosti, které jde dál, než k tomu, co dělá Psychozrakové zkreslení rychlosti.\n\nDoporučené hodnoty se pohybují okolo 0.2, ačkoliv vyšší hodnoty mohou\npomoci v případě velmi for very zrnitého obrazu nebo při kódování s nižším\ndatovým tokem. Nedoporučuje se použít na cel animace a jinou grafiku\ns ostrými okraji." msgid "Deblocking: " msgstr "Odstranění bloků:" msgid "" "H.264 deblocking filter.\n" "\n" "h.264 has a built-in deblocking filter that smooths out blocking artifacts\n" "after decoding each frame. This not only improves visual quality, but also\n" "helps compression significantly. The deblocking filter takes a lot of CPU power,\n" "so if you're looking to minimize CPU requirements for video playback, disable it.\n" "\n" "The deblocking filter has two adjustable parameters, \"strength\" (Alpha) and \"threshold\" (Beta).\n" "The former controls how strong (or weak) the deblocker is, while the latter controls how many\n" "(or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking.\n" "The default is 0 (normal strength) for both parameters." msgstr "Filtr na odstranění bloků H.264.\n\nH.264 má vestavěný filtr na odstranění bloků, který vyhlazuje blokující artefakty po dekódování každého snímku. Toto nejen že zlepšuje pohledovou kvalitu, ale také podstatně pomáhá kompresi. Filtr na odstranění bloků si bere hodně z výkonu procesoru, takže pokud se staráte o požadavky procesoru na přehrávání obrazového záznamu, zakažte jej.\n\n Filtr na odstranění bloků má dva nastavitelné parametry, \"sílu\" (Alpha) a \"práh\" (Beta). První ovládá to, jak silný (nebo slabý) rušitel bloků je, zatímco druhý ovládá to, na jak mnoho (nebo málo) okrajů se použije. Nižší hodnoty znamenají méně odstraněných bloků, vyšší hodnoty znamenají více odstraněných bloků. Výchozí je 0 (normální síla) pro oba parametry." msgid "No DCT Decimate" msgstr "Žádné ničení DCT" msgid "" "x264 normally zeroes out nearly-empty data blocks to save bits to\n" "be better used for some other purpose in the video. However, this can\n" "sometimes have slight negative effects on retention of subtle grain and\n" "dither.\n" "\n" "Don't touch this unless you're having banding issues or other such cases\n" "where you are having trouble keeping fine noise." msgstr "x264 normálně nuluje téměř prázdné datové bloky, aby ušetřil bity, které tak\nmohou být v obrazu využity pro nějaký jiný účel. Toto však může mít někdy\nzáporný účinek na uchování jemné zrnitosti a roztřesení.\n\nNešahejte na to, pokud nemáte potíže s odstupňováním nebo s jinými\ntakovými případy, kde máte potíže se zachováním jemného šumu." msgid "Psychovisual" msgstr "Psychozrakový" msgid "" "Your selected options will appear here.\n" "You can edit these and add additional options.\n" "\n" "Default values will not be shown. The defaults are:\n" "ref=3:bframes=3:b-adapt=fast:direct=spatial:\n" "b-pyramid=normal:weightp=2:me=hex:merange=16:\n" "subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\n" "deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\n" "no-fast-pskip=0:no-dct-decimate=0:cabac=1" msgstr "Vámi vybrané volby se objeví zde. \n Můžete je upravovat a přidávat dodatečné volby. \n\n Výchozí hodnoty nebudou zobrazeny. Výchozí jsou:\n ref=3:bframes=3:b-adapt=fast:direct=spatial:\n b-pyramid=normal:weightp=2:me=hex:merange=16:\n subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\n deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\n no-fast-pskip=0:no-dct-decimate=0:cabac=1" msgid "Current x264 Advanced Option String" msgstr "Rozšířené volby pro x264" msgid "Advanced Video" msgstr "Pokročilý obraz" msgid "Chapter Markers" msgstr "Značky kapitol" msgid "Add chapter markers to output file." msgstr "Přidat značky kapitol do výstupního souboru." msgid "Chapters" msgstr "Kapitoly" msgid "Actors:" msgstr "Herci:" msgid "Director:" msgstr "Režisér:" msgid "Release Date:" msgstr "Datum vydání:" msgid "Comment:" msgstr "Poznámka:" msgid "Genre:" msgstr "Žánr:" msgid "Description:" msgstr "Popis:" msgid "Plot:" msgstr "Zápletka:" msgid "Tags" msgstr "Značky" msgid "Settings" msgstr "Nastavení" msgid "Edit" msgstr "Upravit" msgid "Reload" msgstr "Nahrát znovu" msgid "" "Mark selected queue entry as pending.\n" "Resets the queue job to pending and ready to run again." msgstr "Označit vybraný záznam řady jako čekající na vyřízení.\nNastaví znovu zařazený úkol jako čekající na vyřízení a připravený k opětovnému spuštění." msgid "Reload All" msgstr "Nahrát vše znovu" msgid "" "Mark all queue entries as pending.\n" "Resets all queue jobs to pending and ready to run again." msgstr "Označit všechny záznamy v řadě jako čekající na vyřízení.\nNastaví znovu všechny zařazené úkoly jako čekající na vyřízení a připravené k opětovnému spuštění." msgid "OK" msgstr "OK" msgid "Select All" msgstr "Vybrat vše" msgid "Mark all titles for adding to the queue" msgstr "Označit všechny názvy pro přidání do řady" msgid "Clear All" msgstr "Vyprázdnit vše" msgid "Unmark all titles" msgstr "Odznačit všechny názvy" msgid "Destination files OK. No duplicates detected." msgstr "Cílové soubory jsou v pořádku. Nebylo zjištěno, že by byly některé soubory dvakrát." msgid "Select this title for adding to the queue.\n" msgstr "Vybrat tento název pro přidání do řady.\n" msgid "Preferences" msgstr "Nastavení" msgid "Automatically check for updates" msgstr "Automaticky ověřit dostupnost aktualizací" msgid "When all encodes are complete" msgstr "Když byla všechna kódování dokončena" msgid "Use automatic naming (uses modified source name)" msgstr "Použít automatické pojmenovávání (používá změněný název zdroje)" msgid "Auto-Name Template" msgstr "Pojmenovat předlohu automaticky" msgid "" "Available Options: {source} {title} {chapters} {date} {time} {quality} " "{bitrate}" msgstr "Dostupné volby: {source} {title} {chapters} {date} {time} {quality} {bitrate}" msgid "Use iPod/iTunes friendly (.m4v) file extension for MP4" msgstr "Použít souborovou příponu iPod/iTunes friendly (.m4v) pro MP4" msgid "Number of previews" msgstr "Počet náhledů" msgid "Filter short titles (seconds)" msgstr "Filtrovat krátké tituly (sekundy)" msgid "Show system tray icon" msgstr "Ukázat ikonu v oznamovací oblasti panelu" msgid "General" msgstr "Obecné" msgid "Constant Quality fractional granularity" msgstr "Nepatrná zrnitost při stálé kvalitě" msgid "Use dvdnav (instead of libdvdread)" msgstr "Použít dvdnav (namísto libdvdread)" msgid "Put individual encode logs in same location as movie" msgstr "Dát jednotlivé záznamy o kódování do stejného umístění, kde je film" msgid "Activity Log Verbosity Level" msgstr "Úroveň podrobnosti zápisu o činnosti" msgid "Activity Log Longevity" msgstr "Délka života zápisu o činnosti" msgid "Scale down High Definition previews" msgstr "Zmenšit rozlišení náhledů ve vysokém rozlišení" msgid "Automatically Scan DVD when loaded" msgstr "Automaticky prohledat DVD při nahrání" msgid "Scans the DVD whenever a new disc is loaded" msgstr "Při nahrání disku prohlédne DVD" msgid "Hide Advanced Video Options Tab" msgstr "Skrýt kartu s pokročilými volbami pro obraz" msgid "" "Use advanced video options at your own risk.\n" "We recommend that you use the controls available\n" "on the Video tab instead." msgstr "Použít pokročilé volby pro obraz na vlastní nebezpečí.\nDoporučuje se namísto toho použít ovladače dostupné na kartě Obraz." msgid "Delete completed jobs from queue" msgstr "Odmazat dokončené úkoly z řady" msgid "" "By default, completed jobs remain in the queue and are marked as complete.\n" "Check this if you want the queue to clean itself up by deleting completed jobs." msgstr "Ve výchozím nastavení zůstávají dokončené úkoly v řadě a jsou označeny jako hotové.\nZaškrtněte, pokud chcete řadu uklidit sami, tím že smažete hotové úkoly." msgid "Allow Tweaks" msgstr "Povolit vyladění" msgid "Allow HandBrake For Dummies" msgstr "Povolit HandBrake pro natvrdlé" msgid "Advanced" msgstr "Pokročilé" msgid "Folder Name:" msgstr "Název složky:" msgid "Description" msgstr "Popis" msgid "Preset Name:" msgstr "Název přednastavení:" msgid "Custom Picture Dimensions" msgstr "Vlastní rozměry obrázku" msgid "Maximum Width:" msgstr "Největší šířka:" msgid "Enable maximum width limit." msgstr "Povolit nejzazší mez pro šířku." msgid "" "This is the maximum width that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source width is greater.\n" "Setting this to 0 means there is no maximum width." msgstr "Toto je největší šířka, se kterou bude obraz ukládán.\n\nVždy když je nahrán nový zdroj, použije se tato hodnota, pokud je šířka zdroje větší. Nastavení této hodnoty na 0 znamená, že není žádná největší šířka." msgid "Maximum Height:" msgstr "Největší výška:" msgid "Enable maximum height limit." msgstr "Povolit nejzazší mez pro výšku." msgid "" "This is the maximum height that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source height is greater.\n" "Setting this to 0 means there is no maximum height." msgstr "Toto je největší výška, se kterou bude obraz ukládán.\nVždy když je nahrán nový zdroj, použije se tato hodnota, pokud je šířka zdroje větší.\nNastavení této hodnoty na 0 znamená, že není žádná největší výška." msgid "Select preview frames." msgstr "Vybrat náhledové snímky." msgid "" "Encode and play a short sequence of video starting from the current preview " "position." msgstr "Zakódovat a přehrát krátký úryvek obrazu počínaje nynějším místem náhledu." msgid "Duration:" msgstr "Doba trvání" msgid "Set the duration of the live preview in seconds." msgstr "Nastavit dobu trvání živého náhledu v sekundách." msgid "Show Crop" msgstr "Ukázat oříznutí" msgid "Show Cropped area of the preview" msgstr "Ukázat oříznutou oblast v náhledu" msgid "Fullscreen" msgstr "Celá obrazovka" msgid "View Fullscreen Preview" msgstr "Zobrazit náhled celé obrazovky" msgid "Title Number:" msgstr "Název titulu:" msgid "Detected DVD devices:" msgstr "Zjištěná zařízení DVD:" msgid "Setting:" msgstr "Nastavení:" msgid "Import SRT" msgstr "Zavést SRT" msgid "Enable settings to import an SRT subtitle file" msgstr "Povolit nastavení pro zavedení souboru s titulky SRT" msgid "Embedded Subtitle List" msgstr "Vložený seznam titulků" msgid "Enable settings to select embedded subtitles" msgstr "Povolit nastavení pro vybrání vložených titulků" msgid "Character Code" msgstr "Kód znaku" msgid "Offset (ms)" msgstr "Posun (ms)" msgid "" "Set the language of this subtitle.\n" "This value will be used by players in subtitle menus." msgstr "Nastavit jazyk těchto titulků.\nTato hodnota bude používána přehrávači v nabídkách s titulky." msgid "" "Set the character code used by the SRT file you are importing.\n" "\n" "SRTs come in all flavours of character sets.\n" "We translate the character set to UTF-8.\n" "The source's character code is needed in order to perform this translation." msgstr "Nastavit znakový kód používaný souborem SRT, který zavádíte.\n\nSoubory SRTs přichází ve všech druzích znakových sad.\nZnakovou sadu překládáme do UTF-8.\nZnakový kód zdroje je potřeba za účelem provedení tohoto překódování znaků." msgid "Select the SRT file to import." msgstr "Vybrat soubor titulků SRT k zavedení." msgid "Srt File" msgstr "Soubor SRT" msgid "Adjust the offset in milliseconds between video and SRT timestamps" msgstr "Upravit posun v milisekundách mezi časovými razítky obrazu a souborem titulků SRT" msgid "Track" msgstr "Stopa" msgid "List of subtitle tracks available from your source." msgstr "Seznam stop s titulky dostupných ze zdroje." msgid "Forced Subtitles Only" msgstr "Pouze vynuceno titulky" msgid "Burn into video" msgstr "Vypálit do obrazu" msgid "" "Render the subtitle over the video.\n" "The subtitle will be part of the video and can not be disabled." msgstr "Vložit titulky do obrazu.\nTitulky budou součástí obrazu a nepůjde je zakázat." msgid "Set Default Track" msgstr "Nastavit výchozí stopu" msgid "Source Track" msgstr "Zdrojová stopa" msgid "List of audio tracks available from your source." msgstr "Seznam zvukových stop dostupných ze zdroje." msgid "Track Name:" msgstr "Název stopy:" msgid "" "Set the audio track name.\n" "\n" "Players may use this in the audio selection list." msgstr "Nastavit název zvukové stopy.\n\nTento mohou použít přehrávače v seznamu pro výběr zvuku." msgid "Mix" msgstr "Smíchání" msgid "Sample Rate" msgstr "Vzorkovací kmitočet" msgid "" "Dynamic Range Compression: Adjust the dynamic range of the output audio track.\n" "\n" "For source audio that has a wide dynamic range (very loud and very soft sequences),\n" "DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder." msgstr "Komprese dynamického rozsahu:\nUpravit dynamický rozsah výstupní zvukové stopy.\n\nU zdrojového zvuku, který má široký dynamický rozsah\n(velmi hlasité a velmi slabé sekvence), vám komprese\ndynamického rozsahu (DRC - Dynamic Range Compression)\n'stlačit' rozsah tím, že udělá hlasité oblasti slabšími a slabé\noblasti hlasitějšími." msgid "Enable bitrate setting" msgstr "Povolit nastavení datového toku" msgid "Enable quality setting" msgstr "Povolit nastavení jakosti" msgid "" "Quality: For output codec's that support it, adjust the quality of " "the output." msgstr "Kvalita: Upravit jakost výstupu u výstupních kodeků, jež to podporují." msgid "00.0" msgstr "00.0" msgid "" "Audio Gain: Adjust the amplification or attenuation of the output " "audio track." msgstr "Zesílení zvuku:\nUpravit zesílení nebo zeslabení výstupní zvukové stopy." msgid "Skip This Version" msgstr "Přeskočit tuto verzi" msgid "Remind Me Later" msgstr "Připomenout později" msgid "A new version of HandBrake is available!" msgstr "Je dostupná nová verze HandBrake!" msgid "HandBrake xxx is now available (you have yyy)." msgstr "HandBrake xxx je nyní dostupný (máte yyy)." msgid "Release Notes" msgstr "Poznámky k vydání" msgid "First Track Matching Selected Languages" msgstr "První stopa odpovídá vybraným jazykům" msgid "All Tracks Matching Selected Languages" msgstr "Všechny stopy odpovídají vybraným jazykům" msgid "Chapters:" msgstr "Kapitoly:" msgid "Seconds:" msgstr "Sekundy:" msgid "Frames:" msgstr "Snímky:" msgid "Do Nothing" msgstr "Nedělat nic" msgid "Show Notification" msgstr "Ukázat oznámení" msgid "Quit Handbrake" msgstr "Ukončit Handbrake" msgid "Put Computer To Sleep" msgstr "Uspat počítač" msgid "Shutdown Computer" msgstr "Vypnout počítač" msgid "Week" msgstr "Týden" msgid "Month" msgstr "Měsíc" msgid "Year" msgstr "Rok" msgid "Immortal" msgstr "Už nikdy" msgid "Never" msgstr "Nikdy" msgid "Daily" msgstr "Denně" msgid "Weekly" msgstr "Týdně" msgid "Monthly" msgstr "Měsíčně" msgid "Default" msgstr "Výchozí" msgid "Fast" msgstr "Rychlý" msgid "Slow" msgstr "Pomalý" msgid "Slower" msgstr "Pomalejší" msgid "Ultralight" msgstr "Ultralehký" msgid "Light" msgstr "Lehký" msgid "Medium" msgstr "Střední" msgid "Strong" msgstr "Silný" msgid "Film" msgstr "Film" msgid "Grain" msgstr "Zrnitost" msgid "High Motion" msgstr "" msgid "Animation" msgstr "Animace" msgid "Spatial" msgstr "Prostorový" msgid "Temporal" msgstr "Časový" msgid "Automatic" msgstr "Automaticky" msgid "Optimal" msgstr "Nejlepší" msgid "Normal" msgstr "Normální" msgid "Simple" msgstr "Jednoduchý" msgid "Smart" msgstr "Chytrý" msgid "Diamond" msgstr "Kosočtverec" msgid "Hexagon" msgstr "Šestiúhelník" msgid "Uneven Multi-Hexagon" msgstr "Nestejnoměrný více-šestiúhelník" msgid "Exhaustive" msgstr "Vyčerpávající" msgid "Hadamard Exhaustive" msgstr "Vyčerpávající Hadamard" msgid "Most" msgstr "Nejvíce" msgid "Some" msgstr "Některý" msgid "All" msgstr "Vše" msgid "Encode only" msgstr "Pouze zakódovat" msgid "Always" msgstr "Vždy" msgid "(NTSC Film)" msgstr "(Film NTSC)" msgid "(PAL Film/Video)" msgstr "(Film/Video PAL)" msgid "(NTSC Video)" msgstr "(Video NTSC)" msgid "%d - %02dh%02dm%02ds - %s" msgstr "%d - %02dh%02dm%02ds - %s" msgid "%d (%05d.MPLS) - %02dh%02dm%02ds" msgstr "%d (%05d.MPLS) - %02dh%02dm%02ds" msgid "%d (%05d.MPLS) - Unknown Length" msgstr "%d (%05d.MPLS) - Neznámá délka" msgid "%d - %02dh%02dm%02ds" msgstr "%d - %02dh%02dm%02ds" msgid "%d - Unknown Length" msgstr "%d - Neznámá délka" msgid "No Titles" msgstr "Žádné názvy" msgid "No Audio" msgstr "Žádný zvuk" msgid "Foreign Audio Search" msgstr "Hledání zvuku v cizím jazyce" #, c-format msgid "Chapter %2d" msgstr "Kapitola %2d" #, c-format msgid "N/A" msgstr "Nedostupné" #, c-format msgid "" "Invalid Deinterlace Settings:\n" "\n" "%s\n" msgstr "Neplatné nastavení prokládání:\n\n%s\n" #, c-format msgid "" "Invalid Detelecine Settings:\n" "\n" "%s\n" msgstr "Neplatné nastavení odstranění filmového snímání:\n\n%s\n" #, c-format msgid "" "Invalid Decomb Settings:\n" "\n" "%s\n" msgstr "Neplatné nastavení odstranění hřebenu:\n\n%s\n" msgid "" "Theora is not supported in the MP4 container.\n" "\n" "You should choose a different video codec or container.\n" "If you continue, FFMPEG will be chosen for you." msgstr "Theora není v kontejneru MP4 podporována.\n\nMěl byste zvolit jiný obrazový kodek nebo kontejner.\nPokud budete pokračovat, bude vybrán FFMPEG." msgid "Continue" msgstr "Pokračovat" msgid "No title found.\n" msgstr "Nenalezen žádný název.\n" msgid "" "Only one subtitle may be burned into the video.\n" "\n" "You should change your subtitle selections.\n" "If you continue, some subtitles will be lost." msgstr "Do obrazu můžou být vypáleny pouze jedny titulky.\n\nMěl byste změnit výběr titulků.\nPokud budete pokračovat, budou některé titulky ztraceny." msgid "" "Srt file does not exist or not a regular file.\n" "\n" "You should choose a valid file.\n" "If you continue, this subtitle will be ignored." msgstr "Soubor SRT neexistuje, anebo není pravidelným souborem.\n\nMěl byste vybrat platný soubor.\nPokud budete pokračovat, budou tyto titulky přehlíženy." msgid "" "The source does not support Pass-Thru.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "Zdroj nepodporuje průchod (Pass-Thru).\n\nMěl byste zvolit jiný zvukový kodek.\nPokud budete pokračovat, bude pro vás jeden vybrán." #, c-format msgid "" "%s is not supported in the %s container.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "%s není podporován v kontejneru %s.\n\nMěl byste zvolit jiný zvukový kodek.\nPokud budete pokračovat, bude pro vás jeden vybrán." #, c-format msgid "" "The source audio does not support %s mixdown.\n" "\n" "You should choose a different mixdown.\n" "If you continue, one will be chosen for you." msgstr "Zdrojový zvuk nepodporuje %s smíchání.\n\nMěl byste zvolit jiné smíchání (finální mixáž).\nPokud budete pokračovat, bude pro vás jedno vybráno." #, c-format msgid "" "Unable to create %s.\n" "\n" "Internal error. Could not parse UI description.\n" "%s" msgstr "Nelze vytvořit %s.\n\nVnitřní chyba. Nepodařilo se zpracovat popis UI.\n%s" msgid "Index" msgstr "Rejstřík" msgid "Duration" msgstr "Trvání" msgid "Title" msgstr "Název" msgid "Job Information" msgstr "Informace o úkolu" msgid "Track Information" msgstr "Informace o stopě" msgid "Preset Name" msgstr "Název přednastavení" msgid "The device or file to encode" msgstr "Zařízení nebo soubor k zakódování" msgid "The preset values to use for encoding" msgstr "Hodnoty přednastavení, které se mají použít k zakódování" msgid "Spam a lot" msgstr "" msgid "- Transcode media formats" msgstr "- Překódovat multimediální formáty" msgid "Globals" msgstr "Celková nastavení" msgid "Presets" msgstr "Přednastavení" msgid "Folder" msgstr "Složka" #, c-format msgid "%s path: (%s)" msgstr "%s cesta: (%s)" #, c-format msgid "%s indices: len %d" msgstr "%s rejstříky: len %d" msgid "Type" msgstr "Typ" msgid "Failed to find parent folder when adding child." msgstr "Při přidávání potomka se nepodařilo najít rodičovskou složku." msgid "Failed to find parent folder while adding child." msgstr "Při přidávání potomka se nepodařilo najít rodičovskou složku." #, c-format msgid "Can't map language value: (%s)" msgstr "Nelze přiřadit hodnotu jazyka: (%s)" #, c-format msgid "Can't map value: (%s)" msgstr "Nelze přiřadit hodnotu: (%s)" msgid "Subtitles" msgstr "Titulky" msgid "" "%s: Folder already exists.\n" "You can not replace it with a preset." msgstr "%s: Složka již existuje.\nNemůžete ji nahradit přednastavením." msgid "" "%s: Preset already exists.\n" "You can not replace it with a folder." msgstr "%s: Přednastavení již existuje.\nNemůžete ji nahradit složkou." msgid "Import Preset" msgstr "Zavést přednastavení" msgid "All (*)" msgstr "Vše (*)" msgid "Presets (*.plist)" msgstr "Přednastavení (*.plist)" msgid "Export Preset" msgstr "Vyvést přednastavení" msgid "" "Confirm deletion of %s:\n" "\n" "%s" msgstr "Potvrdit smazání %s:\n\n%s" msgid "folder" msgstr "Složka" msgid "preset" msgstr "Přednastavení" msgid "No selection??? Perhaps unselected." msgstr "Žádný výběr? Patrně není vybráno." #, c-format msgid "Gstreamer Error: %s" msgstr "Chyba Gstreameru: %s" #, c-format msgid "" "Missing GStreamer plugin\n" "Audio or Video may not play as expected\n" "\n" "%s" msgstr "Chybí přídavný modul GStreameru\nZvuk nebo obraz nemusí být přehrávány tak, jak je očekáváno\n\n%s" msgid "Done" msgstr "Hotovo" msgid "Windowed" msgstr "Okenní" msgid "Seconds" msgstr "Sekundy" msgid "Frames" msgstr "Snímky" #, c-format msgid "" "%s (Title %d, %s %d through %d, 2 Video Passes) -->" " %s" msgstr "%s (Název %d, %s %d skrz %d, 2 průchody obrazu) --> %s" #, c-format msgid "" "%s (Title %d, %s %d through %d) --> %s" msgstr "%s (název %d, %s %d skrz %d) --> %s" #, c-format msgid "Modified Preset Based On: %s\n" msgstr "Změněné přednastavení založené na: %s\n" #, c-format msgid "Preset: %s\n" msgstr "Přednastavení: %s\n" #, c-format msgid "Format: %s Container\n" msgstr "Formát: %s Kontejner\n" msgid "Container Options:" msgstr "Volby pro kontejner:" #, c-format msgid "%sChapter Markers" msgstr "%s Značky kapitol" #, c-format msgid "%siPod 5G Support" msgstr "%s Podpora pro iPod 5G" #, c-format msgid "%sWeb Optimized" msgstr "%s Vyladěno pro internet" #, c-format msgid "%sLarge File Size (>4GB)" msgstr "%s Velikost velkých souborů (> 4GB)" #, c-format msgid "Destination: %s\n" msgstr "Cíl: %s\n" msgid "(Aspect Preserved)" msgstr "(Poměr stran zachován)" msgid "(Aspect Lost)" msgstr "(Poměr stran ztracen)" msgid "(Anamorphic)" msgstr "(Pokřivené)" msgid "(Custom Anamorphic)" msgstr "(Vlastní anamorfický)" msgid "Picture: " msgstr "Obrázek: " #, c-format msgid "Source: %d x %d, Output %d x %d %s, Crop %d:%d:%d:%d" msgstr "Zdroj: %d x %d, Výstup %d x %d %s, Ořez %d:%d:%d:%d" #, c-format msgid ", Display %d x %d" msgstr ", Zobrazení %d x %d" msgid "Filters:" msgstr "Filtry:" #, c-format msgid "%sDetelecine" msgstr "%s Odstranit filmové snímání" #, c-format msgid "%sDecomb" msgstr "%s Odstranění hřebenu" #, c-format msgid "%sDeinterlace" msgstr "%s Odstranit prokládání" #, c-format msgid "%sDenoise Filter %s:" msgstr "%s Filtr pro odstranění šumu %s:" #, c-format msgid "%sDeblock: %d" msgstr "%s Odstranit bloky: %d" #, c-format msgid "%sGrayscale" msgstr "%s Odstíny šedi" #, c-format msgid "Video: %s" msgstr "Obraz: %s" #, c-format msgid ", Framerate: %s %s" msgstr ", Rychlost snímkování: %s %s" #, c-format msgid ", Framerate: Peak %s (may be lower)" msgstr ", Rychlost snímkování: Vrchol %s (může být nižší)" #, c-format msgid ", Framerate: %s (constant frame rate)" msgstr ", Rychlost snímkování: %s (stálý datový tok)" msgid "Error" msgstr "Chyba" msgid "Bitrate:" msgstr "Datový tok:" msgid "Bitrate" msgstr "Datový tok" msgid "Quality" msgstr "Jakost" msgid "Turbo 1st Pass: On\n" msgstr "Turbo 1 průchod: Zapnuto\n" #, c-format msgid "Video Options: Preset: %s" msgstr "Volby pro obraz: Přednastavení: %s" msgid " - Tune: " msgstr " - Naladění: " #, c-format msgid " - Profile: %s" msgstr " - Profil: %s" #, c-format msgid " - Level: %s" msgstr " - Úroveň: %s" #, c-format msgid "Advanced Options: %s\n" msgstr "Pokročilé volby: %s\n" msgid "Audio: " msgstr "Zvuk: " #, c-format msgid "Audio Tracks: %d" msgstr "Zvukové stopy: %d" #, c-format msgid "Bitrate: %d" msgstr "Datový tok: %d" #, c-format msgid "%s --> Encoder: %s" msgstr "%s --> Kodér: %s" #, c-format msgid "%s --> Encoder: %s, Mixdown: %s, SampleRate: %s, %s" msgstr "%s --> Kodér: %s, Finální mixáž: %s, Vzorkovací kmitočet: %s, %s" msgid "Subtitle: " msgstr "Titulky: " #, c-format msgid "Subtitle Tracks: %d\n" msgstr "Titulkové stopy: %d\n" msgid " (Force)" msgstr " (Vynutit)" msgid " (Burn)" msgstr " (Vypálit)" msgid " (Default)" msgstr " (Výchozí)" #, c-format msgid " %s (%s), %s, Offset (ms) %d%s\n" msgstr " %s (%s), %s, Posun (ms) %d%s\n" #, c-format msgid "" "Destination: %s\n" "\n" "Another queued job has specified the same destination.\n" "Do you want to overwrite?" msgstr "Cíl: %s\n\nJiná zařazená úloha má stanoven stejný cíl.\nChcete jej přepsat?" msgid "Overwrite" msgstr "Přepsat" #, c-format msgid "" "Destination: %s\n" "\n" "This is not a valid directory." msgstr "Cíl: %s\n\nToto není platný adresář." #, c-format msgid "" "Destination: %s\n" "\n" "Can not read or write the directory." msgstr "Cíl: %s\n\nNelze přečíst nebo zapisovat do adresáře." #, c-format msgid "" "Destination filesystem is almost full: %uM free\n" "\n" "Encode may be incomplete if you proceed.\n" msgstr "Cílový souborový systém je skoro plný: %uM volné\n\npokud budete pokračovat, zakódování může být nedokončeno.\n" msgid "Proceed" msgstr "Pokračovat" #, c-format msgid "" "Destination: %s\n" "\n" "File already exists.\n" "Do you want to overwrite?" msgstr "Cíl: %s\n\nSoubor již existuje.\nPřejete si jej přepsat?" msgid "" "Duplicate destination files detected.\n" "Duplicates will not be added to the queue." msgstr "Bylo zjištěno, že některé cílové soubory jsou dvakrát.\nSoubory, které jsou dvakrát, do řady přidány nebudou." msgid "No Title" msgstr "Žádný název" msgid "" "There is another title with the same destination file name.\n" "This title will not be added to the queue unless you change\n" "the output file name.\n" msgstr "Je tu další název se stejným cílovým souborovým názvem.\nTento název do řady přidán nebude, dokud název\nvýstupního souboru nezměníte.\n" msgid "Stop" msgstr "Zastavit" msgid "Stop Encoding" msgstr "Zastavit kódování" msgid "Resume" msgstr "Pokračovat" msgid "Resume Encoding" msgstr "Pokračovat v kódování" msgid "S_top Queue" msgstr "Z_astavit řadu" msgid "_Start Queue" msgstr "_Spustit řadu" msgid "_Resume Queue" msgstr "Po_kračovat v řadě" msgid "Resume Queue" msgstr "Pokračovat v řadě" msgid "" "You are currently encoding. What would you like to do?\n" "\n" msgstr "Nyní provádíte zakódování. Co chcete dělat?\n\n" #, c-format msgid "" "You have %d unfinished job(s) in a saved queue.\n" "\n" "Would you like to reload them?" msgstr "Máte %d nezařazených úloh v uložené řadě.\n\nChcete je nahrát znovu?" msgid "No" msgstr "Ne" msgid "Yes" msgstr "Ano" #, c-format msgid "Usage: %s infile [outfile]\n" msgstr "Použití: %s infile [outfile]\n" #, c-format msgid "Offset: %dms" msgstr "Posun: %dms" msgid "Burned Into Video" msgstr "Vypáleno do obrazu" msgid "Passthrough" msgstr "Průchod" msgid "through" msgstr "až" msgid "(Forced Subtitles Only)" msgstr "(Pouze vynucené titulky)" msgid "(Default)" msgstr "(Výchozí)" msgid "Error!" msgstr "Chyba!" #, c-format msgid "Preferred Language: %s" msgstr "Upřednostňovaný jazyk: %s" #, c-format msgid "Add %s subtitle track if default audio is not %s" msgstr "Přidat %s stopu titulků, pokud výchozí zvuk není %s" #, c-format msgid "Type %s" msgstr "Typ %s" #, c-format msgid "Type %s value %s" msgstr "Typ %s hodnota %s" #, c-format msgid "Type %s value %d" msgstr "Typ %s hodnota %d" #, c-format msgid "Type %s value %" msgstr "Typ %s hodnota %" #, c-format msgid "Type %s value %f" msgstr "Typ %s hodnota %f" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"%s\"" msgstr "%s\n\nRozšířené volby:\n\"%s\"" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"\"" msgstr "%s\n\nRozšířené volby:\n\"\"" msgid "Any" msgstr "Jakýkoli" msgid "0: SAD, no subpel" msgstr "0: SAD, žádný subpel" msgid "4: SATD, qpel on all" msgstr "4: SATD, qpel na vše" msgid "5: SATD, multi-qpel on all" msgstr "5: SATD, více-qpel na vše" msgid "6: RD in I/P-frames" msgstr "6: RD v I/P-snímcích" msgid "7: RD in all frames" msgstr "7: RD ve všech snímcích" msgid "8: RD refine in I/P-frames" msgstr "8: RD vylepšit I/P-snímcích" msgid "9: RD refine in all frames" msgstr "9: RD vylepšit ve všech snímcích" msgid "10: QPRD in all frames" msgstr "10: QPRD ve všech snímcích" msgid "11: No early terminations in analysis" msgstr "11: Žádná brzká ukončení rozborů" msgid "Your names" msgstr "Pavel Fric" msgid "Your emails" msgstr "fripohled.blogspot.com" HandBrake-0.10.2/gtk/po/fr.po0000664000175200017520000022324012466165306016225 0ustar handbrakehandbrake# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # exceed73 , 2014 # exceed73 , 2014 # mrspock , 2014 # mrspock , 2014 # sebasthug , 2014 # sebasthug , 2014 msgid "" msgstr "" "Project-Id-Version: HandBrake-0.10\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-11-03 13:01+0300\n" "PO-Revision-Date: 2014-12-18 10:59+0000\n" "Last-Translator: mrspock \n" "Language-Team: French (http://www.transifex.com/projects/p/handbrake/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" msgid "Quality: " msgstr "Qualité: " #, c-format msgid "Bitrate: %dkbps" msgstr "Débit : %dkbps" #, c-format msgid "Bitrate: %.4gkbps" msgstr "Débit : %.4gkbps" msgid "Passthrough" msgstr "Direct" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s\n" "Track Name: %s" msgstr "%s\nGain: %s\nDRC: %s\nNom piste: %s" #, c-format msgid "" "%s\n" "Gain: %s\n" "DRC: %s" msgstr "%s\nGain: %s\nDRC: %s" msgid "Add" msgstr "Ajouter" msgid "" "Add an audio encoder.\n" "Each selected source track will be encoded with all selected encoders." msgstr "Ajoute un encodeur audio.\nChaque piste de la source sera encodée avec tous les encodeurs sélectionnés." msgid "Set the audio codec to encode this track with." msgstr "Définit le codec audio à utiliser pour l'encodage de cette piste." msgid "Set the bitrate to encode this track with." msgstr "Définit le débit à utiliser pour l'encodage de cette piste." msgid "" "Audio Quality:\n" "For encoders that support it, adjust the quality of the output." msgstr "Qualité Audio:\nPour les encodeurs qui le supportent, ajuste la auqlité de la sortie." msgid "Set the mixdown of the output audio track." msgstr "Définit le mixage de la piste audio." msgid "Set the sample rate of the output audio track." msgstr "Définit la fréquence d'échantillonnage de la piste audio encodée." msgid "" "Audio Gain:\n" "Adjust the amplification or attenuation of the output audio track." msgstr "Gain Audio:\nAjuste l’amplification ou l'atténuation de la piste audio de sortie." msgid "0dB" msgstr "0dB" msgid "%ddB" msgstr "%ddB" msgid "%.4gkHz" msgstr "%.4gkHz" msgid "%d - %s (%.4gkHz)" msgstr "%d - %s (%.4gkHz)" msgid "" "Dynamic Range Compression:\n" "Adjust the dynamic range of the output audio track.\n" "For source audio that has a wide dynamic range,\n" "very loud and very soft sequences, DRC allows you\n" "to 'compress' the range by making loud sounds\n" "softer and soft sounds louder.\n" msgstr "Compression de Dynamique: Ajuste la compression de dynamique de la piste audio encodée. Pour les sources audio ayant une large bande dynamique (séquences de très fortes et très faibles), la compression dynamique permet la «compression» de la bande en atténuant les parties fortes et en augmentant les parties faibles.\n" msgid "Remove this audio encoder" msgstr "Supprimer cet encodeur audio" msgid "Closing HandBrake will terminate encoding.\n" msgstr "Fermer HandBrake terminera l'encodage.\n" msgid "No Title Found" msgstr "Pas de titre trouvé" msgid "none" msgstr "aucun" msgid "Not Selected" msgstr "Pas sélectionné" msgid "zerolatency x264 tune selected, forcing constant framerate" msgstr "Réglage x264 sans latence (zerolatency) sélectionné, taux d'image constant forcé" msgid "Scanning ..." msgstr "Scan en cours..." msgid "Stop Scan" msgstr "Stopper Scan" msgid "On" msgstr "On" msgid "Strict" msgstr "Strict" msgid "Loose" msgstr "Loose" msgid "Custom" msgstr "Custom" msgid "Unknown" msgstr "Inconnu" msgid "auto" msgstr "auto" #, c-format msgid "" "%s\n" "\n" "%s in %d seconds ..." msgstr "%s\n\n%s en %d secondes ..." #, c-format msgid "%sYour movie will be lost if you don't continue encoding." msgstr "%sVotre vidéo sera perdue si vous ne continuez pas l'encodage." msgid "Cancel Current and Stop" msgstr "Annuler En Cours et Stopper" msgid "Cancel Current, Start Next" msgstr "Annuler En Cours, Démarrer Suivant" msgid "Finish Current, then Stop" msgstr "Terminer En Cours, puis Stopper" msgid "Continue Encoding" msgstr "Continuer Encodage" msgid "Custom " msgstr "Custom " msgid "Modified " msgstr "Modifié" #, c-format msgid "Handbrake Version: %s (%d)\n" msgstr "Version Handbrake: %s (%d)\n" #, c-format msgid "%d encode(s) pending" msgstr "%d encodage(s) en attente" #, c-format msgid "job %d of %d, " msgstr "tâche %d sur %d, " #, c-format msgid "pass %d (subtitle scan) of %d, " msgstr "passe %d (scan sous-titre) sur %d, " #, c-format msgid "pass %d of %d, " msgstr "passe %d sur %d, " #, c-format msgid "Encoding: %s%s%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" msgstr "Encodage: %s%s%.2f %% (%.2f fps, moy %.2f fps, ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %% (ETA %02dh%02dm%02ds)" msgstr "Encodage: %s%s%.2f %% (ETA %02dh%02dm%02ds)" #, c-format msgid "Encoding: %s%s%.2f %%" msgstr "Encodage: %s%s%.2f %%" msgid "Searching for start time, " msgstr "Recherche de l'heure de début," msgid "Scanning..." msgstr "Scan en cours..." #, c-format msgid "Scanning title %d of %d..." msgstr "Scan titre %d sur %d..." #, c-format msgid "Scanning title %d of %d preview %d..." msgstr "Scan titre %d sur %d prévisualisation %d..." msgid "Source" msgstr "Source" msgid "Choose Video Source" msgstr "Choisir Vidéo Source" msgid "Paused" msgstr "En Pause" msgid "Encode Done!" msgstr "Encodage Terminé!" msgid "Encode Canceled." msgstr "Encodage Annulé." msgid "Encode Failed." msgstr "Echec de l'Encodage." msgid "Muxing: This may take a while..." msgstr "Multiplexage: Opération longue..." msgid "Scan this DVD source" msgstr "Scanner cette source DVD" msgid "AutoScan" msgstr "AutoScan" msgid "Suspend" msgstr "Suspendre" #, c-format msgid "Suspend failed: %s" msgstr "Échec de la suspension: %s" msgid "Suspend failed" msgstr "Échec de la suspension" msgid "Shutdown" msgstr "Eteindre" #, c-format msgid "Shutdown failed: %s" msgstr "Echec de l'extinction: %s" msgid "Shutdown failed" msgstr "Echec de l'extinction" msgid "Encoding" msgstr "Encodage" #, c-format msgid "press %d %d" msgstr "Appuyez sur %d %d" #, c-format msgid "" "Invalid Settings:\n" "%s" msgstr "Réglages invalides:\n%s" msgid "Cancel" msgstr "Annuler" #, c-format msgid "%s: %.4g (Warning: lossless)" msgstr "%s: %.4g (Attention: sans perte)" #, c-format msgid "HandBrake %s/%s is now available (you have %s/%d)." msgstr "HandBrake %s/%s est maintenant disponible (vous avez %s/%d)" msgid "Encode Complete" msgstr "Encodage Terminé" msgid "Put down that cocktail, Your HandBrake queue is done!" msgstr "Posez ce cocktail, HandBrake a terminé son travail!" msgid "Your encode is complete." msgstr "Votre encodage est terminé." msgid "Shutting down the computer" msgstr "Eteindre l'ordinateur" msgid "Putting computer to sleep" msgstr "Mettre l'ordinateur en veille" msgid "Quiting Handbrake" msgstr "Quitter Handbrake" msgid "Bottom" msgstr "Bas" #, c-format msgid "open failed: %s\n" msgstr "Échec de l'ouverture: %s\n" msgid "No key for dictionary item" msgstr "Pas de clé pour l'entrée de dictionnaire" msgid "Invalid container type. This shouldn't happen" msgstr "Type de conteneur invalide. " #, c-format msgid "%s:missing a requried attribute" msgstr "%s:attribut requis manquant" #, c-format msgid "" "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" msgstr "Usage: %s [-I ] \nRésumé:\n Crée une ressource plist à partir de ressources liste\nOptions:\n I - Inclure le chemin pour chercher les fichiers\n Fichiers de liste en entrée\n Fichier plist de sortie\n" msgid "language" msgstr "langue" msgid "Language" msgstr "Langue" msgid "" "The language this text is in, as an ISO code. Pango can use this as a hint " "when rendering the text. If you don't understand this parameter, you " "probably don't need it" msgstr "La langue dans laquelle est le texte exprime en code ISO. Pango peut utiliser ceci comme ressource quand il doit effectuer un rendu de texte.\nSi vous ne comprenez pas ce paramètre, vous n'en avez probablement pas besoin" msgid "" "The preferred place to ellipsize the string, if the cell renderer does not " "have enough room to display the entire string" msgstr "L'emplacement préféré pour les points de suspensions, si le «cell renderer» n'a pas assez de place pour afficher toute la chaîne" msgid "" "How to break the string into multiple lines, if the cell renderer does not " "have enough room to display the entire string" msgstr "Comment couper la chaîne de caractères en plusieurs lignes, si le «cell renderer» n'a pas assez de place pour afficher toute la chaîne" msgid "" "Render the subtitle over the video.\n" "\n" "The subtitle will be part of the video and can not be disabled." msgstr "Fusionne les sous-titres dans la vidéo.\n\nLes sous-titres feront partie de le vidéo et ne pourront être désactivés." msgid "Burned In" msgstr "Gravés" msgid "" "Set the default output subtitle track.\n" "\n" "Most players will automatically display this\n" "subtitle track whenever the video is played.\n" "\n" "This is useful for creating a \"forced\" track\n" "in your output." msgstr "Fixe la piste de sous-titres par défaut.\n\nLa plupart des lecteurs vont automatiquement afficher cette piste de sous-titres quand la vidéo est jouée.\n\nC'est utile pour créer une piste «forcée» dans voter fichier de sortie." msgid "Default" msgstr "Défaut" msgid "" "Use only subtitles that have been flagged\n" "as forced in the source subtitle track\n" "\n" "\"Forced\" subtitles are usually used to show\n" "subtitles during scenes where someone is speaking\n" "a foreign language." msgstr "Utiliser seulement les sous-titres marqués\nforcés dans la piste de sous-titres source\n\nLes sous-titrés «Forcés» sont généralement affichés\npour les scènes dans lesquelles quelqu'un parle\ndans une langue étrangère." msgid "Forced Only" msgstr "Forcée Uniquement" msgid "" "Add (or subtract) an offset (in milliseconds)\n" "to the start of the SRT subtitle track.\n" "\n" "Often, the start of an external SRT file\n" "does not coincide with the start of the video.\n" "This setting allows you to synchronize the files." msgstr "Ajoute (ou soustrait) un décalage (en millisecondes)\nau démarrage de la piste sous-titre SRT.\n\nSouvent, le début d'un fichier SRT externe\nne coïncide pas avec le début d'une vidéo.\nCe réglage vous permet de synchroniser les fichiers." msgid "SRT Offset" msgstr "Décalage SRT" msgid "Off" msgstr "Off" msgid "" "The source subtitle track\n" "\n" "You can choose any of the subtitles\n" "recognized in your source file.\n" "\n" "In addition, there is a special track option\n" "\"Foreign Audio Search\". This option will add\n" "an extra pass to the encode that searches for\n" "subtitles that may correspond to a foreign\n" "language scene. This option is best used in\n" "conjunction with the \"Forced\" option." msgstr "Piste de sous-titres source\n\nVous pouvez choisir chacun des sous-titres\ndétectés dans votre fichier source.\n\nDe plus, vous disposez d'une option spéciale\n«Recherche Audio Langue Étrangère». Cette option\najoute une passe supplémentaire à l'encodage\ncherchant des sous-titres correspondant aux scènes\nen langue étrangère. Cette option a un meilleur\nrésultat utilisée conjointement à l'option «Forcée»." msgid "Track" msgstr "Piste" msgid "About HandBrake" msgstr "A propos d'HandBrake" msgid "" "Copyright © 2008 - 2013 John Stebbins\n" "Copyright © 2004 - 2013, HandBrake Devs" msgstr "Copyright © 2008 - 2013 John Stebbins\nCopyright © 2004 - 2013, HandBrake Devs" msgid "" "HandBrake is a GPL-licensed, multiplatform, multithreaded video transcoder." msgstr "HandBrake est un transcodeur vidéo multiplate-forme, multithread sous licence GPL." msgid "http://handbrake.fr" msgstr "http://handbrake.fr" msgid "" "HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n" "\n" "HandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." msgstr "HandBrake est un logiciel libre ; vous pouvez le redistribuer ou le modifier\nsuivant les termes de la GNU General Public License telle que publiée par la\nFree Software Foundation , soit la version 2 de la Licence, soit (à votre gré)\ntoute version ultérieure.\nHandBrake est distribué dans l’espoir qu’il sera utile, mais SANS AUCUNE\nGARANTIE : sans même la garantie implicite de COMMERCIALISABILITÉ ni d’ADÉQUATION À UN OBJECTIF PARTICULIER. Consultez la GNU\nGeneral Public License pour plus de détails.\nVous devriez avoir reçu une copie de la GNU General Public License avec Glade ; si ce n’est pas le cas, écrivez à la Free Software Foundation, Inc., «51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA» ou consultez : ." msgid "_Minimize/Maximize" msgstr "_Réduire/Agrandir" msgid "_Pause Queue" msgstr "_Pause Liste" msgid "_Quit" msgstr "_Quitter" msgid "_About" msgstr "_A Propos" msgid "HandBrake" msgstr "HandBrake" msgid "_File" msgstr "_Fichier" msgid "_Source" msgstr "_Source" msgid "Single _Title" msgstr "_Titre unique" msgid "_Destination" msgstr "_Destination" msgid "_Preferences" msgstr "_Préférences" msgid "_Queue" msgstr "_Liste" msgid "_Add" msgstr "_Ajouter" msgid "Add _Multiple" msgstr "Ajout _Multiples" msgid "_Start" msgstr "_Démarrer" msgid "_Pause" msgstr "_Pause" msgid "_View" msgstr "_Voir" msgid "HandBrake For _Dumbies" msgstr "HandBrake pour les _Idiots" msgid "_Show Presets" msgstr "_Afficher Préglages" msgid "_Preview" msgstr "_Aperçu" msgid "_Activity Window" msgstr "Fenêtre d'Activité" msgid "Show _Queue" msgstr "Montrer _Liste" msgid "_Presets" msgstr "_Présélections" msgid "_Save" msgstr "_Enregistrer" msgid "_Delete" msgstr "_Supprimer" msgid "_Make Default" msgstr "_Par défaut" msgid "_New Folder" msgstr "_Nouveau Dossier" msgid "_Export" msgstr "_Exporter" msgid "_Import" msgstr "_Importer" msgid "_Update Built-in Presets" msgstr "_MAJ Préréglages Inclus" msgid "_Help" msgstr "_Aide" msgid "_Guide" msgstr "_Guide" msgid "Start Encoding" msgstr "Démarrer Encodage" msgid "Start" msgstr "Démarrer" msgid "Pause Encoding" msgstr "Pause Encodage" msgid "Pause" msgstr "Pause" msgid "Add to Queue" msgstr "_Ajouter à la Liste" msgid "Enqueue" msgstr "Placer dans la liste" msgid "Show Queue" msgstr "Montrer Liste" msgid "Queue" msgstr "Liste" msgid "" "Open Picture Settings and Preview window.\n" "Here you can adjust cropping, resolution, aspect ratio, and filters." msgstr "Ouvrir la fenêtre des paramétrages et de prévisualisation de l'image.\nVous pouvez régler le rognage, la résolution, le ratio et les filtres." msgid "Preview" msgstr "Prévisualisation" msgid "Show Activity Window" msgstr "Afficher Fenêtre d'Activité" msgid "Activity" msgstr "Activité" msgid "Source:" msgstr "Source:" msgid "None" msgstr "Aucun" msgid "Title:" msgstr "Titre:" msgid "" "Set the title to encode.\n" "By default the longest title is chosen.\n" "This is often the feature title of a DVD." msgstr "Fixe le titre à encoder.\nPar défaut le titre le plus long est choisi.\nC'est souvent le titre principal d'un DVD." msgid "Angle:" msgstr "Angle:" msgid "For multi-angle DVD's, select the desired angle to encode." msgstr "Pour les DVD multi-angles, selectionner l'angle à encoder." msgid "Reset All Titles" msgstr "Remettre a zéro tous les titres" msgid "Apply current settings to all titles" msgstr "Appliquer les réglages courants à tous les titres" msgid "Range of title to encode. Can be chapters, seconds, or frames." msgstr "Plage de titre à encoder. Cela peut être des chapitres, des secondes ou des images." msgid "Set the first chapter to encode." msgstr "Définissez le premier chapitre à encoder." msgid "Set the last chapter to encode." msgstr "Définissez le dernier chapitre à encoder." msgid "Duration:" msgstr "Durée:" msgid "Destination" msgstr "Destination" msgid "File:" msgstr "Fichier:" msgid "Destination filename for your encode." msgstr "Fichier de destination pour votre encodage" msgid "Destination directory for your encode." msgstr "Dossier de destination pour votre encodage." msgid "Destination Directory" msgstr "Dossier de destination" msgid "Format:" msgstr "Format:" msgid "Format to mux encoded tracks to." msgstr "Format de sortie des pistes encodées." msgid "iPod 5G Support" msgstr "iPod 5G Support" msgid "Add iPod Atom needed by some older iPods." msgstr "Ajoute l'iPod Atom requis par certains iPods anciens." msgid "Web optimized" msgstr "Optimisé pour le Web" msgid "" "Optimize the layout of the MP4 file for progressive download.\n" "This allows a player to initiate playback before downloading the entire file." msgstr "Optimise la structure du fichier MP4 pour le téléchargement progressif.\nCela permet au client de démarrer la lecture avant d'avoir téléchargé le fichier en entier." msgid "Large file (>4GB)" msgstr "Gros fichier (>4GB)" msgid "" "Allow 64 bit MP4 file which can be over 4GB.\n" "\n" "Caution: This option may break device compatibility." msgstr "Autoriser les fichiers MP4 64 bits dont la taille peut dépasser 4Go.\n\nAttention: Cette option peut ne pas être compatible avec le matériel." msgid "Presets List" msgstr "Liste des Présélections" msgid "Source Codec:" msgstr "Codec Source:" msgid "Dimensions:" msgstr "Dimensions:" msgid "Aspect: " msgstr "Aspect: " msgid "Frame Rate:" msgstr "Débit d'images:" msgid "Source Picture Parameters" msgstr "Paramètres Image Source" msgid "Autocrop:" msgstr "Rognage auto:" msgid "Crop:" msgstr "Rognage:" msgid "Crop Dimensions:" msgstr "Dimensions de rognage:" msgid "Cropping" msgstr "Rognage" msgid "Scale Dimensions:" msgstr "Echelle:" msgid "Optimal for Source:" msgstr "Optimal pour la Source:" msgid "Anamorphic:" msgstr "Anamorphique:" msgid "Scaling" msgstr "Dimensionnement" msgid "Presentation Dimensions:" msgstr "Dimensions de Presentation:" msgid "Summary" msgstr "Résumé" msgid "Left Crop" msgstr "Rognage Gauche" msgid "Top Crop" msgstr "Rognage Haut" msgid "Bottom Crop" msgstr "Rognage Bas" msgid "Right Crop" msgstr "Rognage Droit" msgid "Auto Crop" msgstr "Auto Rognage" msgid "Automatically crop black borders around edges of the video." msgstr "Rogner automatiquement les bordures noires de la vidéo." msgid "Loose Crop" msgstr "Rognage loose" msgid "" "When picture settings require that the image\n" "dimensions be rounded to some multiple number\n" "of pixels, this setting will crop a few extra pixels\n" "instead of doing exact cropping and then scaling to\n" "the required multiple." msgstr "Quand le paramétrage de l'image requiert\nune dimension d'image arrondie à un multiple\nd'un nombre de pixels, ce paramètre enlèvera\nquelques pixels de plus au lieu de rogner\nexactement puis d'agrandir l'image vers le\nmultiple requis." msgid "width:" msgstr "largeur:" msgid "" "This is the width that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "C'est la largeur de la vidéo telle qu'encodée.\nLes dimensions réellement affichées seront différentes si le ratio d'aspect des pixels n'est pas 1:1." msgid "height:" msgstr "hauteur:" msgid "" "This is the height that the video will be stored at.\n" "The actual display dimensions will differ if the pixel aspect ratio is not 1:1." msgstr "C'est la largeur de la vidéo telle qu'encodée.\nLes dimensions réellement affichées seront différentes si le ratio d'aspect des pixels n'est pas 1:1." msgid "Optimal for source" msgstr "Optimal pour la source" msgid "" "If enabled, select the 'optimal' storage resolution.\n" "This will be the resolution that most closely matches the source resolution after cropping." msgstr "Si activé, sélectionne la résolution «optimale».\nCela sera la résolution la plus proche de celle de la source après rognage." msgid "" "Anamorphic Modes:\n" "\n" "None - Force pixel aspect ratio to 1:1.\n" "Loose - Align dimensions to chosen 'Alignment' value\n" " and pick pixel aspect ratio that preserves the\n" " original display aspect ratio\n" "Strict - Keep original source dimensions and pixel\n" " aspect ratio" msgstr "Modes Anamorphiques:\n\nAucun - Force le rapport d'aspect de pixel à 1:1.\nLoose - Dimensionne à la valeur d'«Alignement» choisie \net ajuste le rapport d'aspect de pixel qui préserve\nle rapport d'aspect d'affichage original\nStrict - Conserve les dimensions originales de la source\net le rapport d'aspect de pixel" msgid "Alignment:" msgstr "Alignement:" msgid "" "Align storage dimensions to multiples of this value.\n" "\n" "This setting is only necessary for compatibility with some devices.\n" "You should use 2 unless you experience compatibility issues." msgstr "Aligne les dimensions de l'encodage vers un multiple de cette valeur.\n\nCe paramètre est nécessaire seulement pour compatibilité avec quelques lecteurs.\nVous devriez utiliser 2 à moins de tomber sur un problème de compatibuilité." msgid "Storage Geometry" msgstr "Géométrie du fichier" msgid "" "This is the display width. It is the result of scaling the storage " "dimensions by the pixel aspect." msgstr "Largeur affichée. C'est le résultat du redimensionnement des dimensions de stockage par l'aspect de pixel." msgid "Pixel Aspect:" msgstr "Aspect de Pixel:" msgid "" "Pixel aspect defines the shape of the pixels.\n" "\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "L'aspect de pixel définit la forme des pixels.\n\nUn ratio de 1:1 définit un pixel carré. Les autres valeurs définissent une forme rectangulaire. Les lecteurs redimensionnent l'image afin d'obtenir l'aspect spécifié." msgid ":" msgstr ":" msgid "" "Pixel aspect defines the shape of the pixels.\n" "A 1:1 ratio defines a square pixel. Other values define rectangular shapes.\n" "Players will scale the image in order to achieve the specified aspect." msgstr "L'aspect de pixel définit la forme des pixels.\n\nUn ratio de 1:1 définit un pixel carré. Les autres valeurs définissent une forme rectangulaire. Les lecteurs redimensionnent l'image afin d'obtenir l'aspect spécifié." msgid "Keep Aspect" msgstr "Conserver l'Aspect" msgid "" "If enabled, the original display aspect of the source will be maintained." msgstr "Si activé, l'aspect d'affichage original de la source sera maintenu." msgid "Display Aspect:" msgstr "Aspect d'Affichage:" msgid "Display Geometry" msgstr "Géométrie d'affichage" msgid "Grayscale" msgstr "Niveaux de gris" msgid "If enabled, filter colour components out of video." msgstr "Si activé, filtre et enlève les couleurs de la vidéo." msgid "Deblock:" msgstr "Deblock:" msgid "" "The deblocking filter removes a common type of compression artifact.\n" "If your source exhibits 'blockiness', this filter may help clean it up." msgstr "Le filtre «Deblock» enlève les artefacts de compression couramment rencontrés.\nSi votre source contient des «blocs», ce filtre devrait les nettoyer." msgid "Denoise Filter:" msgstr "Filtre Antibruit:" msgid "" "Denoise filtering reduces or removes the appearance of noise and grain.\n" "Film grain and other types of high frequency noise are difficult to compress.\n" "Using this filter on such sources can result in smaller file sizes." msgstr "Le filtrage antibruit réduit ou supprime l'apparition de bruit et de grain.\nLe grain dans un film et les autres types de bruit de hautes fréquences sont difficile à compresser.\nUtiliser ce filtre sur de telles source peut produire des fichiers de moindre taille." msgid "Denoise Preset:" msgstr "Présélection Antibruit:" msgid "Denoise Tune:" msgstr "Réglage Antibruit:" msgid "" "Custom denoise filter string format\n" "\n" "SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgstr "Chaîne de format de filtre «denoise» personnalisé\n\nSpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma" msgid "Detelecine:" msgstr "Detelecine:" msgid "" "This filter removes 'combing' artifacts that are the result of telecining.\n" "\n" "Telecining is a process that adjusts film framerates that are 24fps to NTSC video frame rates which are 30fps." msgstr "Ce filtre supprime les artefacts de type «combing» résultant du «telecining».\n\nLe «telecining» consiste à convertir un débit d'images de 24i/s en debit NTSC qui est de i/s." msgid "" "Custom detelecine filter string format\n" "\n" "JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgstr "Chaîne de format de filtre «detelecine» personnalisé\n\nJunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity" msgid "Decomb" msgstr "Decomb" msgid "" "Choose decomb or deinterlace filter options.\n" "\n" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced.\n" "\n" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "Choix des options de filtres «decomb» ou «deinterlace».\n\nLe filtre «decomb» désentrelace sélectivement les images qui semblent entrelacées.\nCeci préservera la qualité des images qui ne sont pas entrelacées.\n\nLe filtre «deinterlace» classique est appliqué à toutes les images.\nLes images non entrelacées souffriront de quelque dégradation de qualité." msgid "Deinterlace" msgstr "Désentrelace" msgid "Decomb:" msgstr "Decomb:" msgid "" "The decomb filter selectively deinterlaces frames that appear to be interlaced.\n" "This will preserve quality in frames that are not interlaced." msgstr "Le filtre «decomb» désentrelace sélectivement les images qui semblent entrelacées.\nCeci préservera la qualité des images qui ne sont pas entrelacées." msgid "" "Custom decomb filter string format\n" "\n" "Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\n" "BlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\n" "ErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgstr "Chaîne format de filtre «decomb» personnalisé\n\nMode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth:\nBlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh:\nErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity" msgid "Deinterlace:" msgstr "Deinterlace:" msgid "" "The classic deinterlace filter is applied to all frames.\n" "Frames that are not interlaced will suffer some quality degradation." msgstr "Le filtre «deinterlace» classique est appliqué à toutes les images.\nLes images non entrelacées souffriront de quelque dégradation de qualité." msgid "" "Custom deinterlace filter string format\n" "\n" "YadifMode:YadifParity:McdintMode:McdeintQp" msgstr "Chaîne format de filtre «deinterlace» personnalisé\n\nYadifMode:YadifParity:McdintMode:McdeintQp" msgid "Filters" msgstr "Filtres" msgid "Picture" msgstr "Image" msgid "Video Encoder:" msgstr "Encodeur Vidéo:" msgid "Available video encoders." msgstr "Encodeurs vidéo disponibles." msgid "Framerate:" msgstr "Débit d'images:" msgid "" "Output framerate.\n" "\n" "'Same as source' is recommended. If your source video has\n" "a variable framerate, 'Same as source' will preserve it." msgstr "Débit d'images en sortie.\n\n«Identique à la source» recommandé. Si votre source vidéo a un débit variable, «Identique à la source» le conservera." msgid "Constant Framerate" msgstr "Débit Constant" msgid "Same as source" msgstr "Identique à la Source" msgid "kbps" msgstr "kb/s" msgid "(variable)" msgstr "(variable)" msgid "(constant)" msgstr "(constant)" msgid "Enables constant framerate output." msgstr "Active une sortie à débit constant." msgid "Peak Framerate (VFR)" msgstr "Pic de débit (VFR)" msgid "" "Enables variable framerate output with a peak\n" "rate determined by the framerate setting.\n" "\n" "VFR is not compatible with some players." msgstr "Active le débit variable en sortie avec un\npic déterminé par le réglage de débit.\n\nLe débit variable ou «VFR» est incompatible avec certains lecteurs." msgid "Variable Framerate" msgstr "Débit Variable" msgid "" "Enables variable framerate output.\n" "\n" "VFR is not compatible with some players." msgstr "Active le débit variable en sortie.\n\nLe débit variable ou «VFR» est incompatible avec certains lecteurs." msgid "" "Set the desired quality factor.\n" "The encoder targets a certain quality.\n" "The scale used by each video encoder is different.\n" "\n" "x264's scale is logarithmic and lower values correspond to higher quality.\n" "So small decreases in value will result in progressively larger increases\n" "in the resulting file size. A value of 0 means lossless and will result\n" "in a file size that is larger than the original source, unless the source\n" "was also lossless.\n" "\n" "FFMpeg's and Theora's scale is more linear.\n" "These encoders do not have a lossless mode." msgstr "Définit le facteur de qualité souhaité.\nL'encodeur cible une certaine qualité. L'échelle utilisée par chaque encodeur vidéo est différente.\n\nL'échelle du x264 est logarithmique et les valeurs les plus basses correspondent à la meilleure qualité. De fait les petites modifications vers des valeurs basses aboutiront progressivement à des tailles de fichiers de plus en plus grandes. Une valeur de 0 donne un résultat sans perte et produit un fichier plus gros que l'original, sauf si la source était elle aussi sans perte.\n\nLes échelles de FFMpeg et Theora sont plus linéaires.\nCes encodeurs n'ont pas de mode sans perte." msgid "Constant Quality:" msgstr "Qualité Constante:" msgid "Bitrate (kbps): " msgstr "Débit (kbps): " msgid "" "Set the average bitrate.\n" "\n" "The instantaneous bitrate can be much higher or lower at any point in time.\n" "But the average over a long duration will be the value set here. If you need\n" "to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings." msgstr "Définit le débit moyen.\n\nLe débit instantané peut être plus ou moins élevé à chaque instant.\nMais le débit moyen sur une longue durée aura la valeur indiquée ici. Si vous avez besoin de limiter le débit instantané, regardez dans les options vbv-bufsize et vbv-maxrate de x264." msgid "2-Pass Encoding" msgstr "Encodage 2 Passes" msgid "" "Perform 2 Pass Encoding.\n" "\n" "The 'Bitrate' option is prerequisite. During the 1st pass, statistics about\n" "the video are collected. Then in the second pass, those statistics are used\n" "to make bitrate allocation decisions." msgstr "Réalise un encodage en 2 passes.\n\nL'option 'Débit' est un prérequis. Sur la 1re passe, des statistiques sur la vidéo sont collectées. Puis sur la deuxième passe, ces statistiques sont utilisées pour décider des allocations de débit." msgid "Turbo First Pass" msgstr "Première Passe Turbo" msgid "" "During the 1st pass of a 2 pass encode, use settings that speed things " "along." msgstr "Pour la 1ère passe d'un encodage en 2 passes, utilise des réglages qui accélèrent la tâche." msgid "Use Advanced Options" msgstr "Activer Options Avancées" msgid "" "Use advanced options Tab for x264 settings.\n" "\n" "Use at your own risk!" msgstr "Utiliser l'onglet des options avancées pour les réglages x264.\n\nÀ vos risques et périls!" msgid "Preset:" msgstr "Présélection:" msgid "" "Adjusts encoder settings to trade off compression efficiency against encoding speed.\n" "\n" "This establishes your default encoder settings.\n" "Tunes, profiles, levels and advanced option string will be applied to this.\n" "You should generally set this option to the slowest you can bear since slower\n" "settings will result in better quality or smaller files." msgstr "Ajuste les réglages x264 pour sacrifier le taux de compression au profit de la vitesse d'encodage.\n\nCeci définit vos réglages par défaut pour x264.\nLes réglages, profils, niveaux et options avancées s'appliqueront ici.\n\nIl vous est conseillé de définir cette option au plus lent que vous pouvez supporter car les réglages lents donnent des résultats de meilleure qualité ou des fichiers plus petits. " msgid "Tune:" msgstr "Réglage:" msgid "" "Tune settings to optimize for common scenarios.\n" "\n" "This can improve effeciency for particular source characteristics or set\n" "characteristics of the output file. Changes will be applied after the\n" "preset but before all other parameters." msgstr "Réglages optimisés pour la plupart des cas.\n\nCela peut augmenter l'efficacité pour des sources aux caractéristiques particulières ou définir les caractéristiques du fichier de sortie.\n\nLes changements seront appliqués après le préréglage mais avant tous les autres paramètres." msgid "Fast Decode" msgstr "Décodage Rapide" msgid "" "Reduce decoder CPU usage.\n" "\n" "Set this if your device is struggling to play the output (dropped frames)." msgstr "Réduit la charge CPU induite par le décodeur.\n\nUtiliser si votre lecteur a des difficultés à lire le fichier (saut d'images)." msgid "Zero Latency" msgstr "Latence Nulle" msgid "" "Minimize latency between input to encoder and output of decoder.\n" "\n" "This is useful for broadcast of live streams.\n" "\n" "Since HandBrake is not suitable for live stream broadcast purposes,\n" "this setting is of little value here." msgstr "Minimise la latence entre l'entrée et la sortie du décodeur.\n\nUtile pour la diffusion en streaming.\nMême si HandBrake n'est pas conçu pour la diffusion en streaming, cette option peut apporter une amélioration." msgid "Profile:" msgstr "Profil:" msgid "" "Sets and ensures compliance with the specified profile.\n" "\n" "Overrides all other settings." msgstr "Fixe et garantit le respect du niveau spécifié pour H.264.\n\nÉcrase les autres réglages." msgid "Level:" msgstr "Niveau:" msgid "" "Sets and ensures compliance with the specified level.\n" "\n" "Overrides all other settings." msgstr "Fixe et garantit le respect du niveau spécifié pour H.264.\n\nÉcrase les autres réglages." msgid "More Settings:" msgstr "Réglages Supplémentaires:" msgid "" "Additional encoder settings.\n" "\n" "Colon separated list of encoder options." msgstr "Réglages d'encodeur x264 supplémentaires.\n\nListe d'options d'encodeur séparées par des deux-points." msgid "Video" msgstr "Vidéo" msgid "Selection Behavior:" msgstr "Mode de sélection:" msgid "Remove" msgstr "Supprimer" msgid "Available Languages" msgstr "Langues Disponibles" msgid "Selected Languages" msgstr "Langues Sélectionnées" msgid "Use only first encoder for secondary audio" msgstr "Utiliser uniquement le premier encodeur pour l'audio secondaires" msgid "" "Only the primary audio track will be encoded with the full encoder list.\n" "All other secondary audio output tracks will be encoded with first encoder only." msgstr "Seule la première piste audio sera encodée avec toute la liste des encodeurs.\nToutes les autres pistes audio de sortie le seront uniquement avec le premier encodeur." msgid "Auto Passthru:" msgstr "Auto Direct:" msgid "MP3" msgstr "MP3" msgid "" "Enable this if your playback device supports MP3.\n" "This permits MP3 passthru to be selected when automatic passthru selection is enabled." msgstr "Activer si votre lecteur supporte le MP3.\nCela permet de sélectionner MP3 Direct (copie sans modification) lorsque l'«Auto Direct» est actif." msgid "" "Enable this if your playback device supports AAC.\n" "This permits AAC passthru to be selected when automatic passthru selection is enabled." msgstr "Activer si votre lecteur supporte l'AAC.\nCela permet de sélectionner AAC Direct (copie sans modification) lorsque l'«Auto Direct» est actif." msgid "" "Enable this if your playback device supports AC-3.\n" "This permits AC-3 passthru to be selected when automatic passthru selection is enabled." msgstr "Activer si votre lecteur supporte l'AC-3.\nCela permet de sélectionner AC-3 Direct (copie sans modification) lorsque l'«Auto Direct» est actif." msgid "" "Enable this if your playback device supports DTS.\n" "This permits DTS passthru to be selected when automatic passthru selection is enabled." msgstr "Activer si votre lecteur supporte le DTS.\nCela permet de sélectionner DTS Direct (copie sans modification) lorsque l'«Auto Direct» est actif." msgid "" "Enable this if your playback device supports DTS-HD.\n" "This permits DTS-HD passthru to be selected when automatic passthru selection is enabled." msgstr "Activer si votre lecteur supporte le DTS-HD.\nCela permet de sélectionner DTS-HD Direct (copie sans modification) lorsque l'«Auto Direct» est actif." msgid "Passthru Fallback:" msgstr "Alternative au Direct:" msgid "" "Set the audio codec to encode with when a suitable track can not be found " "for audio passthru." msgstr "Définit le codec audio à utiliser pour encoder si aucune piste n'est disponible pour le direct audio." msgid "Audio Encoder Settings:" msgstr "Paramètres de l'encodage audio:" msgid "Each selected source track will be encoded with all selected encoders" msgstr "Chaque piste source sera encodée avec tous les encodeurs sélectionnés" msgid "Encoder" msgstr "Encodeur" msgid "Bitrate/Quality" msgstr "Débit/Qualité" msgid "Mixdown" msgstr "Mixage final" msgid "Samplerate" msgstr "Taux d'échantillonnage" msgid "Gain" msgstr "Gain" msgid "Audio Defaults" msgstr "Défauts Audio" msgid "Add new audio settings to the list" msgstr "Ajouter de nouveaus réglages audio à la liste" msgid "Add All" msgstr "Ajouter Tout" msgid "Add all audio tracks to the list" msgstr "Ajouter toutes les pistes audio à la liste" msgid "Reload Defaults" msgstr "Recharger les réglages par défaut" msgid "Reload all audio settings from defaults" msgstr "Recharger tout les réglages audio par défaut" msgid "Audio List" msgstr "Liste Audio" msgid "Preferred Language: None" msgstr "Langue Préférée: Aucune" msgid "Add Foreign Audio Search Pass" msgstr "Ajouter une passe de recherche de langue étrangère" msgid "" "Add \"Foreign Audio Search\" when the default audio track is your preferred language.\n" "This search pass finds short sequences of foreign audio and provides subtitles for them." msgstr "Ajouter «Recherche de Langue Étrangère» quand la piste audio par défaut est dans votre langue préférée?\nCette passe de recherche trouve les courtes séquences en langue étrangère et en fournit les sous-titres." msgid "Add subtitle track if default audio is foreign" msgstr "Ajouter uns piste de sous-titres si l'audio par défaut est étrangère" msgid "" "When the default audio track is not your preferred language, add a subtitle " "track." msgstr "Quand la piste audio par défaut n'est pas dans votre langue préférée, ajouter une piste de sous-titres." msgid "Add Closed Captions when available" msgstr "Ajouter des sous-titres codés si disponibles" msgid "" "Closed captions are text subtitles that can be added to any container as a " "soft subtitle track (not burned)" msgstr "Les sous-titres codés sont du texte qui peut être ajouté à tout conteneur comme piste de sous-titres additionnelle (pas gravée)" msgid "Subtitle Defaults" msgstr "Défauts Sous-titres" msgid "Add new subtitle settings to the list" msgstr "Ajoute une nouvelle définition de sous-titres à la liste" msgid "Add all subtitle tracks to the list" msgstr "Ajoute toutes les pistes de sous-titres à la liste" msgid "Reload all subtitle settings from defaults" msgstr "Recharge toutes les définitions de sous-titres par défaut" msgid "Subtitle List" msgstr "Liste de Sous-titres" msgid "Reference Frames:" msgstr "Images de Référence:" msgid "" "Sane values are ~1-6. The more you add, the better the compression, but the slower the encode.\n" "Cel animation tends to benefit from more reference frames a lot more than film content.\n" "\n" "Note that many hardware devices have limitations on the number of supported reference\n" "frames, so if you're encoding for a handheld or standalone player, don't touch this unless\n" "you're absolutely sure you know what you're doing!" msgstr "Des valeurs sensées vont de 1 à 6. Plus la valeur est élevée, meilleure est la compression et plus l'encodage est long.\nLes animations image par image tendent à bénéficier d'un nombre élevé d'image de référence, bien plus que les films.\n\nNoter que beaucoup de lecteurs physiques ont des limitations sur le nombre supporté d'images de référence,\naussi, si vous encodez pour un balladeur ou un lecteur de salon, ne touchez pas à ceci à moins que ne soyez absolument sur de ce vous faites!" msgid "Maximum B-Frames:" msgstr "Maximum B-Frames:" msgid "" "Sane values are ~2-5. This specifies the maximum number of sequential B-frames that the encoder can use.\n" "\n" "Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.\n" "Cel-animated source material and B-pyramid also significantly increase the usefulness of larger\n" "values.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off)." msgstr "" msgid "Pyramidal B-Frames:" msgstr "" msgid "" "B-pyramid improves compression by creating a pyramidal structure (hence the name)\n" "of B-frames, allowing B-frames to reference each other to improve compression.\n" "\n" "Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit." msgstr "" msgid "Weighted P-Frames:" msgstr "" msgid "" "Performs extra analysis to decide upon weighting parameters for each frame.\n" "\n" "This improves overall compression slightly and improves the quality of fades greatly.\n" "\n" "Baseline profile, as required for iPods and similar devices, requires weighted P-frame\n" "prediction to be disabled. Note that some devices and players, even those that support\n" "Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is\n" "completely incompatible with it, for example." msgstr "" msgid "8x8 Transform" msgstr "" msgid "" "The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.\n" "\n" "It improves compression by at least 5% at a very small speed cost and may\n" "provide an unusually high visual quality benefit compared to its compression\n" "gain. However, it requires High Profile, which many devices may not support." msgstr "" msgid "CABAC Entropy Encoding" msgstr "" msgid "" "After the encoder has done its work, it has a bunch of data that\n" "needs to be compressed losslessly, similar to ZIP or RAR. H.264 provides\n" "two options for this: CAVLC and CABAC. CABAC decodes a lot slower but\n" "compresses significantly better (10-30%), especially at lower bitrates.\n" "\n" "If you're looking to minimize CPU requirements for video playback, disable this option.\n" "Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled." msgstr "" msgid "Encoding Features" msgstr "" msgid "Motion Est. Method:" msgstr "" msgid "" "Controls the motion estimation method.\n" "\n" "Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.\n" "A better motion search method improves compression at the cost of speed.\n" "\n" "Diamond: performs an extremely fast and simple search using a diamond pattern.\n" "Hexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n" "Uneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n" "Exhaustive: performs a \"dumb\" search of every pixel in a wide area. Significantly slower for only a small compression gain.\n" "Transformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement." msgstr "" msgid "Subpel ME & Mode:" msgstr "" msgid "" "This setting controls both subpixel-precision motion estimation and mode decision methods.\n" "\n" "Subpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n" "Mode decision is the method used to choose how to encode each block of the frame: a very important decision.\n" "SAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n" "6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n" "11 disables all early terminations in analysis.\n" "10 and 11, the most powerful and slowest options, require adaptive quantization (aq-mode > 0) and trellis 2 (always)." msgstr "" msgid "Motion Est. Range:" msgstr "" msgid "" "This is the distance x264 searches from its initial guess at the\n" "motion of a block in order to try to find its actual motion.\n" "\n" "The default is fine for most content, but extremely high motion video,\n" "especially at HD resolutions, may benefit from higher ranges, albeit at\n" "a high speed cost." msgstr "" msgid "Adaptive Direct Mode:" msgstr "" msgid "" "H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n" "\n" "Spatial, the default, is almost always better, but temporal is sometimes useful too.\n" "x264 can, at the cost of a small amount of speed (and accordingly for a small compression gain),\n" "adaptively select which is better for each particular frame." msgstr "" msgid "Adaptive B-Frames:" msgstr "" msgid "" "x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n" "\n" "Fast mode takes roughly the same amount of time no matter how many B-frames you specify.\n" "However, while fast, its decisions are often suboptimal.\n" "\n" "Optimal mode gets slower as the maximum number of B-Frames increases,\n" "but makes much more accurate decisions, especially when used with B-pyramid." msgstr "" msgid "Partitions:" msgstr "" msgid "" "Mode decision picks from a variety of options to make its decision:\n" "this option chooses what options those are.\n" "\n" "Fewer partitions to check means faster encoding, at the cost of worse\n" "decisions, since the best option might have been one that was turned off." msgstr "" msgid "Trellis:" msgstr "" msgid "" "Trellis fine-tunes the rounding of transform coefficients to\n" "squeeze out 3-5% more compression at the cost of some speed.\n" "\n" "\"Always\" uses trellis not only during the main encoding process, but also\n" "during analysis, which improves compression even more, albeit at great speed cost.\n" "\n" "Trellis costs more speed at higher bitrates and requires CABAC." msgstr "" msgid "Analysis" msgstr "" msgid "Adaptive Quantization Strength:" msgstr "" msgid "" "Adaptive quantization controls how the encoder distributes bits across the frame.\n" "\n" "Higher values take more bits away from edges and complex areas to improve areas with finer detail." msgstr "" msgid "Psychovisual Rate Distortion:" msgstr "" msgid "" "Psychovisual rate-distortion optimization takes advantage of the characteristics of human\n" "vision to dramatically improve apparent detail and sharpness.\n" "The effect can be made weaker or stronger by adjusting the strength.\n" "Being an RD algorithm, it requires mode decision to be at least \"6\"." msgstr "" msgid "Psychovisual Trellis:" msgstr "" msgid "" "Psychovisual trellis is an experimental algorithm to further\n" "improve sharpness and detail retention beyond what Psychovisual RD does.\n" "\n" "Recommended values are around 0.2, though higher values may help for very\n" "grainy video or lower bitrate encodes. Not recommended for cel animation\n" "and other sharp-edged graphics." msgstr "" msgid "Deblocking: " msgstr "" msgid "" "H.264 deblocking filter.\n" "\n" "h.264 has a built-in deblocking filter that smooths out blocking artifacts\n" "after decoding each frame. This not only improves visual quality, but also\n" "helps compression significantly. The deblocking filter takes a lot of CPU power,\n" "so if you're looking to minimize CPU requirements for video playback, disable it.\n" "\n" "The deblocking filter has two adjustable parameters, \"strength\" (Alpha) and \"threshold\" (Beta).\n" "The former controls how strong (or weak) the deblocker is, while the latter controls how many\n" "(or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking.\n" "The default is 0 (normal strength) for both parameters." msgstr "" msgid "No DCT Decimate" msgstr "" msgid "" "x264 normally zeroes out nearly-empty data blocks to save bits to\n" "be better used for some other purpose in the video. However, this can\n" "sometimes have slight negative effects on retention of subtle grain and\n" "dither.\n" "\n" "Don't touch this unless you're having banding issues or other such cases\n" "where you are having trouble keeping fine noise." msgstr "" msgid "Psychovisual" msgstr "" msgid "" "Your selected options will appear here.\n" "You can edit these and add additional options.\n" "\n" "Default values will not be shown. The defaults are:\n" "ref=3:bframes=3:b-adapt=fast:direct=spatial:\n" "b-pyramid=normal:weightp=2:me=hex:merange=16:\n" "subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1:\n" "deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0:\n" "no-fast-pskip=0:no-dct-decimate=0:cabac=1" msgstr "" msgid "Current x264 Advanced Option String" msgstr "" msgid "Advanced Video" msgstr "Vidéo Avancé" msgid "Chapter Markers" msgstr "Repères de Chapitre" msgid "Add chapter markers to output file." msgstr "Ajouter des repères de chapitre sur le fichier de sortie." msgid "Chapters" msgstr "Chapitres" msgid "Actors:" msgstr "Acteurs:" msgid "Director:" msgstr "Réalisateur:" msgid "Release Date:" msgstr "Date de Sortie:" msgid "Comment:" msgstr "Commentaire:" msgid "Genre:" msgstr "Genre:" msgid "Description:" msgstr "Description:" msgid "Plot:" msgstr "Résumé:" msgid "Tags" msgstr "Tags" msgid "Settings" msgstr "Réglages" msgid "Edit" msgstr "Edition" msgid "Reload" msgstr "Recharger" msgid "" "Mark selected queue entry as pending.\n" "Resets the queue job to pending and ready to run again." msgstr "Marque l'entrée sélectionnée dans la file comme «en attente».\nRemet la tâche de la file en attente et prête à être exécutée à nouveau. " msgid "Reload All" msgstr "Tout Recharger" msgid "" "Mark all queue entries as pending.\n" "Resets all queue jobs to pending and ready to run again." msgstr "Marque toutes les entrées de la file comme «en attente».\nRemet toutes les tâches de la file en attente et prêtes à être exécutées à nouveau. " msgid "OK" msgstr "" msgid "Select All" msgstr "Tout Sélectionner" msgid "Mark all titles for adding to the queue" msgstr "Marque tous les titres pour adjonction dans la file" msgid "Clear All" msgstr "Efface Tout" msgid "Unmark all titles" msgstr "Aucun titre n'est marqué" msgid "Destination files OK. No duplicates detected." msgstr "Fichiers de destination OK. Pas de duplication détectée." msgid "Select this title for adding to the queue.\n" msgstr "Sélectionne ce titre pour ajout dans la file.\n" msgid "Preferences" msgstr "Préférences" msgid "Automatically check for updates" msgstr "Vérifier automatiquement les mises à jour" msgid "When all encodes are complete" msgstr "Lorsque tous les encodages sont terminés" msgid "Use automatic naming (uses modified source name)" msgstr "Utiliser le nommage automatique (utilise des noms de sources modifiés)" msgid "Auto-Name Template" msgstr "" msgid "" "Available Options: {source} {title} {chapters} {date} {time} {quality} " "{bitrate}" msgstr "" msgid "Use iPod/iTunes friendly (.m4v) file extension for MP4" msgstr "Utilise les extensions compatibles iPod/iTunes (.m4v) pour le MP4" msgid "Number of previews" msgstr "Nombre de prévisualisations" msgid "Filter short titles (seconds)" msgstr "Filtrer les titres courts (secondes)" msgid "Show system tray icon" msgstr "Afficher l'icône dans la barre système" msgid "General" msgstr "Général" msgid "Constant Quality fractional granularity" msgstr "" msgid "Use dvdnav (instead of libdvdread)" msgstr "Utiliser dvdnav (plutôt que libdvdread)" msgid "Put individual encode logs in same location as movie" msgstr "Ecrire les logs d'encodage dans le même dossier que les films" msgid "Activity Log Verbosity Level" msgstr "Niveau de Verbosité des Logs d'Activité" msgid "Activity Log Longevity" msgstr "Durée de Conservation des Logs d'Activité" msgid "Scale down High Definition previews" msgstr "Réduit la prévisualisation Haute Définition" msgid "Automatically Scan DVD when loaded" msgstr "Scanner automatiquement les DVD insérés" msgid "Scans the DVD whenever a new disc is loaded" msgstr "Scanner les DVD même si un nouveau disque est inséré" msgid "Hide Advanced Video Options Tab" msgstr "Cacher Onglet Options Vidéo Avancées" msgid "" "Use advanced video options at your own risk.\n" "We recommend that you use the controls available\n" "on the Video tab instead." msgstr "Vous prenez un rique en utilisant les options vidéo avancées.\nNous vous recommandons d'utiliser plutôt les contrôles\ndisponibles dans l'onglet Vidéo." msgid "Delete completed jobs from queue" msgstr "" msgid "" "By default, completed jobs remain in the queue and are marked as complete.\n" "Check this if you want the queue to clean itself up by deleting completed jobs." msgstr "" msgid "Allow Tweaks" msgstr "Autoriser Paramétrage" msgid "Allow HandBrake For Dummies" msgstr "Autoriser HandBrake pour les Idiots " msgid "Advanced" msgstr "Avancé" msgid "Folder Name:" msgstr "Nom du Dossier:" msgid "Description" msgstr "Description" msgid "Preset Name:" msgstr "Nom du préréglage:" msgid "Custom Picture Dimensions" msgstr "Dimensions Image Personnalisées" msgid "Maximum Width:" msgstr "Largeur Maximale:" msgid "Enable maximum width limit." msgstr "Activer une limite pour la largeur." msgid "" "This is the maximum width that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source width is greater.\n" "Setting this to 0 means there is no maximum width." msgstr "" msgid "Maximum Height:" msgstr "Hauteur Maximale:" msgid "Enable maximum height limit." msgstr "Active une limite de hauteur." msgid "" "This is the maximum height that the video will be stored at.\n" "\n" "Whenever a new source is loaded, this value will be applied if the source height is greater.\n" "Setting this to 0 means there is no maximum height." msgstr "" msgid "Select preview frames." msgstr "Selectionne les images de prévisualisation" msgid "" "Encode and play a short sequence of video starting from the current preview " "position." msgstr "Encode et lit une courte séquence de la vidéo en démarrant de la position en cours." msgid "Duration:" msgstr "Durée:" msgid "Set the duration of the live preview in seconds." msgstr "Definit la durée de la prévisualisation en secondes." msgid "Show Crop" msgstr "Voir Rognage" msgid "Show Cropped area of the preview" msgstr "Affiche la zone de rognage dans la prévisualisation" msgid "Fullscreen" msgstr "Plein Ecran" msgid "View Fullscreen Preview" msgstr "Prévisualisation Plein Ecran" msgid "Title Number:" msgstr "Numéro de Titre:" msgid "Detected DVD devices:" msgstr "Lecteurs DVD détectés:" msgid "Setting:" msgstr "Réglage:" msgid "Import SRT" msgstr "Importer SRT" msgid "Enable settings to import an SRT subtitle file" msgstr "" msgid "Embedded Subtitle List" msgstr "Liste des sous-titres présents dans le fichier" msgid "Enable settings to select embedded subtitles" msgstr "" msgid "Character Code" msgstr "Jeu de Caractères" msgid "Offset (ms)" msgstr "Décalage (ms)" msgid "" "Set the language of this subtitle.\n" "This value will be used by players in subtitle menus." msgstr "" msgid "" "Set the character code used by the SRT file you are importing.\n" "\n" "SRTs come in all flavours of character sets.\n" "We translate the character set to UTF-8.\n" "The source's character code is needed in order to perform this translation." msgstr "" msgid "Select the SRT file to import." msgstr "Selectionner le fichier SRT à importer. " msgid "Srt File" msgstr "Fichier SRT" msgid "Adjust the offset in milliseconds between video and SRT timestamps" msgstr "Ajuste le décalage en millisecondes entre la vidéo et le fichier SRT" msgid "Track" msgstr "Piste" msgid "List of subtitle tracks available from your source." msgstr "Liste des sous-titres disponibles dans votre source." msgid "Forced Subtitles Only" msgstr "Sous-titres Forcés Uniquement" msgid "Burn into video" msgstr "Graver dans la vidéo" msgid "" "Render the subtitle over the video.\n" "The subtitle will be part of the video and can not be disabled." msgstr "Applique le sous-titre dans la vidéo.\nLe sous-titre fera partie de la vidéo et ne pourra pas être désactivé." msgid "Set Default Track" msgstr "Piste par défaut" msgid "Source Track" msgstr "Piste Source" msgid "List of audio tracks available from your source." msgstr "Liste des pistes audio disponibles dans votre source." msgid "Track Name:" msgstr "Nom de Piste:" msgid "" "Set the audio track name.\n" "\n" "Players may use this in the audio selection list." msgstr "Définit le nom de la piste audio\n\nLes lecteurs peuvent utiliser ceci dans la liste de sélection audio." msgid "Mix" msgstr "Mix" msgid "Sample Rate" msgstr "Fréquence d'Echantillonnage" msgid "" "Dynamic Range Compression: Adjust the dynamic range of the output audio track.\n" "\n" "For source audio that has a wide dynamic range (very loud and very soft sequences),\n" "DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder." msgstr "" msgid "Enable bitrate setting" msgstr "" msgid "Enable quality setting" msgstr "" msgid "" "Quality: For output codec's that support it, adjust the quality of " "the output." msgstr "Qualité: Pour les codecs qui le supportent, ajuste la qualité de sortie." msgid "00.0" msgstr "" msgid "" "Audio Gain: Adjust the amplification or attenuation of the output " "audio track." msgstr "Gain Audio: Ajuste l'augmentation ou l'atténuation de la piste audio encodée." msgid "Skip This Version" msgstr "Ignorer Cette Version" msgid "Remind Me Later" msgstr "Redemander Plus Tard" msgid "A new version of HandBrake is available!" msgstr "Une nouvelle version d'HandBrake est disponible!" msgid "HandBrake xxx is now available (you have yyy)." msgstr "HandBrake xxx est disponible (vous utilisez la yyy)." msgid "Release Notes" msgstr "Notes de mises à jour" msgid "First Track Matching Selected Languages" msgstr "Première Piste Correspondant aux Langues Sélectionnées" msgid "All Tracks Matching Selected Languages" msgstr "Toutes les Pistes Correspondant aux Langues Sélectionnées" msgid "Chapters:" msgstr "Chapitres:" msgid "Seconds:" msgstr "Secondes:" msgid "Frames:" msgstr "Images:" msgid "Do Nothing" msgstr "Ne Rien Faire" msgid "Show Notification" msgstr "Afficher Notification" msgid "Quit Handbrake" msgstr "Quitter Handbrake" msgid "Put Computer To Sleep" msgstr "Mettre l'ordinateur en veille" msgid "Shutdown Computer" msgstr "Eteindre l'ordinateur" msgid "Week" msgstr "Semaine" msgid "Month" msgstr "Mois" msgid "Year" msgstr "Année" msgid "Immortal" msgstr "Infini" msgid "Never" msgstr "Jamais" msgid "Daily" msgstr "Quotidien" msgid "Weekly" msgstr "Hebdomadaire" msgid "Monthly" msgstr "Mensuel" msgid "Default" msgstr "Défaut" msgid "Fast" msgstr "Rapide" msgid "Slow" msgstr "Lent" msgid "Slower" msgstr "Lent" msgid "Ultralight" msgstr "" msgid "Light" msgstr "" msgid "Medium" msgstr "Moyen" msgid "Strong" msgstr "Fort" msgid "Film" msgstr "" msgid "Grain" msgstr "" msgid "High Motion" msgstr "" msgid "Animation" msgstr "" msgid "Spatial" msgstr "Spatial" msgid "Temporal" msgstr "Temporel" msgid "Automatic" msgstr "Automatique" msgid "Optimal" msgstr "Optimal" msgid "Normal" msgstr "Normal" msgid "Simple" msgstr "Simple" msgid "Smart" msgstr "Intelligent" msgid "Diamond" msgstr "Diamant" msgid "Hexagon" msgstr "Hexagone" msgid "Uneven Multi-Hexagon" msgstr "Multi-Hexagone Irrégulier" msgid "Exhaustive" msgstr "Exhaustive" msgid "Hadamard Exhaustive" msgstr "Hadamard Exhaustive" msgid "Most" msgstr "" msgid "Some" msgstr "Certains" msgid "All" msgstr "Tous" msgid "Encode only" msgstr "Encoder seulement" msgid "Always" msgstr "Toujours" msgid "(NTSC Film)" msgstr "(Film NTSC)" msgid "(PAL Film/Video)" msgstr "(Film/Vidéo PAL)" msgid "(NTSC Video)" msgstr "(Vidéo NTSC)" msgid "%d - %02dh%02dm%02ds - %s" msgstr "" msgid "%d (%05d.MPLS) - %02dh%02dm%02ds" msgstr "" msgid "%d (%05d.MPLS) - Unknown Length" msgstr "" msgid "%d - %02dh%02dm%02ds" msgstr "" msgid "%d - Unknown Length" msgstr "" msgid "No Titles" msgstr "Aucun Titre" msgid "No Audio" msgstr "Pas d'Audio" msgid "Foreign Audio Search" msgstr "Recherche Langue Étrangère" #, c-format msgid "Chapter %2d" msgstr "Chapitre %2d" #, c-format msgid "N/A" msgstr "" #, c-format msgid "" "Invalid Deinterlace Settings:\n" "\n" "%s\n" msgstr "" #, c-format msgid "" "Invalid Detelecine Settings:\n" "\n" "%s\n" msgstr "" #, c-format msgid "" "Invalid Decomb Settings:\n" "\n" "%s\n" msgstr "" msgid "" "Theora is not supported in the MP4 container.\n" "\n" "You should choose a different video codec or container.\n" "If you continue, FFMPEG will be chosen for you." msgstr "Theora n'est pas supporté dans un conteneur MP4.\n\nVous devriez choisir un codec vidéo ou un conteneur différent.\nSi vous continuez, FFMPEG sera choisi pour vous." msgid "Continue" msgstr "Continuer" msgid "No title found.\n" msgstr "Pas de titre trouvé.\n" msgid "" "Only one subtitle may be burned into the video.\n" "\n" "You should change your subtitle selections.\n" "If you continue, some subtitles will be lost." msgstr "Un seul sous-titre peut être gravé dans la vidéo.\n\nVous devriez changer votre sélection de sous-titres.\nSi vous continuez, quelques sous-titres seront perdus." msgid "" "Srt file does not exist or not a regular file.\n" "\n" "You should choose a valid file.\n" "If you continue, this subtitle will be ignored." msgstr "Le fichier SRT n'existe pas ou n'est pas un fichier normal.\n\nVous devriez choisir un fichier valide.\nSi vous continuez, ce sous-titre sera ignoré." msgid "" "The source does not support Pass-Thru.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "" #, c-format msgid "" "%s is not supported in the %s container.\n" "\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you." msgstr "%s n'est pas supporté par le conteneur %s.\n\nVous devriez choisir un codec audio différent.\nSi vous continuez, il en sera choisi un pour vous." #, c-format msgid "" "The source audio does not support %s mixdown.\n" "\n" "You should choose a different mixdown.\n" "If you continue, one will be chosen for you." msgstr "La source ausio ne supporte pas un mixage final %s.\n\nVous devriez choisir un mixage différent.\nSi vous continuez, il en sera choisi un pour vous." #, c-format msgid "" "Unable to create %s.\n" "\n" "Internal error. Could not parse UI description.\n" "%s" msgstr "" msgid "Index" msgstr "Index" msgid "Duration" msgstr "Durée" msgid "Title" msgstr "Titre" msgid "Job Information" msgstr "Information de Tâche" msgid "Track Information" msgstr "Information de Piste" msgid "Preset Name" msgstr "Nom du Préréglage" msgid "The device or file to encode" msgstr "Périphérique ou fichier à encoder" msgid "The preset values to use for encoding" msgstr "Valeurs du préréglage à utiliser pour l'encodage" msgid "Spam a lot" msgstr "" msgid "- Transcode media formats" msgstr "" msgid "Globals" msgstr "" msgid "Presets" msgstr "Présélections" msgid "Folder" msgstr "Dossier" #, c-format msgid "%s path: (%s)" msgstr "" #, c-format msgid "%s indices: len %d" msgstr "" msgid "Type" msgstr "" msgid "Failed to find parent folder when adding child." msgstr "" msgid "Failed to find parent folder while adding child." msgstr "" #, c-format msgid "Can't map language value: (%s)" msgstr "Impossible d'associer la langue: (%s)" #, c-format msgid "Can't map value: (%s)" msgstr "" msgid "Subtitles" msgstr "Sous-titres" msgid "" "%s: Folder already exists.\n" "You can not replace it with a preset." msgstr "%s: Le dossier existe déjà.\nVous ne pouvez pas le remplacer par un préréglage." msgid "" "%s: Preset already exists.\n" "You can not replace it with a folder." msgstr "%s: Le préréglage existe déjà.\nVous ne pouvez pas le remplacer par un dossier." msgid "Import Preset" msgstr "Importer Préréglage" msgid "All (*)" msgstr "Tous (*)" msgid "Presets (*.plist)" msgstr "Préréglages (*.plist)" msgid "Export Preset" msgstr "Exporter Préréglage" msgid "" "Confirm deletion of %s:\n" "\n" "%s" msgstr "Confirmer la suppression de %s:\n\n%s" msgid "folder" msgstr "dossier" msgid "preset" msgstr "présélection" msgid "No selection??? Perhaps unselected." msgstr "" #, c-format msgid "Gstreamer Error: %s" msgstr "Gstreamer Erreur: %s" #, c-format msgid "" "Missing GStreamer plugin\n" "Audio or Video may not play as expected\n" "\n" "%s" msgstr "Plugin GStreamer manquant\nLa lecture Audio ou Video sont susceptibles de mal fonctionner\n\n%s" msgid "Done" msgstr "Fait" msgid "Windowed" msgstr "" msgid "Seconds" msgstr "Secondes" msgid "Frames" msgstr "Images" #, c-format msgid "" "%s (Title %d, %s %d through %d, 2 Video Passes) -->" " %s" msgstr "%s (Titre %d, %s %d à %d, 2 Passes Vidéo) --> %s" #, c-format msgid "" "%s (Title %d, %s %d through %d) --> %s" msgstr "%s (Titre %d, %s %d à %d) --> %s" #, c-format msgid "Modified Preset Based On: %s\n" msgstr "Préglage Modifié Basé Sur: %s\n" #, c-format msgid "Preset: %s\n" msgstr "Préglage: %s\n" #, c-format msgid "Format: %s Container\n" msgstr "Format: %s Conteneur\n" msgid "Container Options:" msgstr "Options du Conteneur:" #, c-format msgid "%sChapter Markers" msgstr "" #, c-format msgid "%siPod 5G Support" msgstr "" #, c-format msgid "%sWeb Optimized" msgstr "" #, c-format msgid "%sLarge File Size (>4GB)" msgstr "" #, c-format msgid "Destination: %s\n" msgstr "Destination: %s\n" msgid "(Aspect Preserved)" msgstr "(Aspect Préservé)" msgid "(Aspect Lost)" msgstr "(Aspect Modifié)" msgid "(Anamorphic)" msgstr "(Anamorphique)" msgid "(Custom Anamorphic)" msgstr "(Anamorphie Personnalisée)" msgid "Picture: " msgstr "Image: " #, c-format msgid "Source: %d x %d, Output %d x %d %s, Crop %d:%d:%d:%d" msgstr "Source: %d x %d, Sortie %d x %d %s, Découpe %d:%d:%d:%d" #, c-format msgid ", Display %d x %d" msgstr ", Affichage %d x %d" msgid "Filters:" msgstr "Filtres:" #, c-format msgid "%sDetelecine" msgstr "" #, c-format msgid "%sDecomb" msgstr "" #, c-format msgid "%sDeinterlace" msgstr "" #, c-format msgid "%sDenoise Filter %s:" msgstr "%sFiltre antibruit %s:" #, c-format msgid "%sDeblock: %d" msgstr "" #, c-format msgid "%sGrayscale" msgstr "%sNiveaux de gris" #, c-format msgid "Video: %s" msgstr "Vidéo: %s" #, c-format msgid ", Framerate: %s %s" msgstr "" #, c-format msgid ", Framerate: Peak %s (may be lower)" msgstr "" #, c-format msgid ", Framerate: %s (constant frame rate)" msgstr "" msgid "Error" msgstr "Erreur" msgid "Bitrate:" msgstr "Débit:" msgid "Bitrate" msgstr "Débit" msgid "Quality" msgstr "Qualité" msgid "Turbo 1st Pass: On\n" msgstr "1re Passe Turbo: On\n" #, c-format msgid "Video Options: Preset: %s" msgstr "Options Vidéo: Présélection: %s" msgid " - Tune: " msgstr " - Réglage: " #, c-format msgid " - Profile: %s" msgstr " - Profil: %s" #, c-format msgid " - Level: %s" msgstr " - Niveau: %s" #, c-format msgid "Advanced Options: %s\n" msgstr "Options Avancées: %s\n" msgid "Audio: " msgstr "" #, c-format msgid "Audio Tracks: %d" msgstr "Pistes Audio: %d" #, c-format msgid "Bitrate: %d" msgstr "" #, c-format msgid "%s --> Encoder: %s" msgstr "%s --> Encodeur: %s" #, c-format msgid "%s --> Encoder: %s, Mixdown: %s, SampleRate: %s, %s" msgstr "%s --> Encodeur: %s, Mixage final: %s, Taux d'échantillonnage: %s, %s" msgid "Subtitle: " msgstr "Sous-titre: " #, c-format msgid "Subtitle Tracks: %d\n" msgstr "Pistes de Sous-titres: %d\n" msgid " (Force)" msgstr " (Forcer)" msgid " (Burn)" msgstr " (Gravé)" msgid " (Default)" msgstr " (Défaut)" #, c-format msgid " %s (%s), %s, Offset (ms) %d%s\n" msgstr " %s (%s), %s, Décalage (ms) %d%s\n" #, c-format msgid "" "Destination: %s\n" "\n" "Another queued job has specified the same destination.\n" "Do you want to overwrite?" msgstr "Destination: %s\n\nUn autre travail de la liste a la même destination.\nVoulez-vous l'écraser?" msgid "Overwrite" msgstr "Ecraser" #, c-format msgid "" "Destination: %s\n" "\n" "This is not a valid directory." msgstr "Destination: %s\n\nDossier invalide." #, c-format msgid "" "Destination: %s\n" "\n" "Can not read or write the directory." msgstr "Destination: %s\n\nImpossible de lire ou d'écrire le dossier." #, c-format msgid "" "Destination filesystem is almost full: %uM free\n" "\n" "Encode may be incomplete if you proceed.\n" msgstr "Le volume de destination est presque plein: %uM free\n\nL'encodage serait incomplet si vous continuez.\n" msgid "Proceed" msgstr "Exécuter" #, c-format msgid "" "Destination: %s\n" "\n" "File already exists.\n" "Do you want to overwrite?" msgstr "Destination: %s\n\nLe fichier existe déjà.\nVoulez-vous l'écraser?" msgid "" "Duplicate destination files detected.\n" "Duplicates will not be added to the queue." msgstr "" msgid "No Title" msgstr "" msgid "" "There is another title with the same destination file name.\n" "This title will not be added to the queue unless you change\n" "the output file name.\n" msgstr "" msgid "Stop" msgstr "" msgid "Stop Encoding" msgstr "Stopper l'encodage" msgid "Resume" msgstr "" msgid "Resume Encoding" msgstr "Reprendre Encodage" msgid "S_top Queue" msgstr "S_topper Liste" msgid "_Start Queue" msgstr "_Démarrer la liste" msgid "_Resume Queue" msgstr "_Redémarrer Liste" msgid "Resume Queue" msgstr "" msgid "" "You are currently encoding. What would you like to do?\n" "\n" msgstr "" #, c-format msgid "" "You have %d unfinished job(s) in a saved queue.\n" "\n" "Would you like to reload them?" msgstr "" msgid "No" msgstr "" msgid "Yes" msgstr "" #, c-format msgid "Usage: %s infile [outfile]\n" msgstr "Usage: %s infile [outfile]\n" #, c-format msgid "Offset: %dms" msgstr "" msgid "Burned Into Video" msgstr "" msgid "Passthrough" msgstr "Direct" msgid "through" msgstr "jusqu'à" msgid "(Forced Subtitles Only)" msgstr "" msgid "(Default)" msgstr "" msgid "Error!" msgstr "" #, c-format msgid "Preferred Language: %s" msgstr "" #, c-format msgid "Add %s subtitle track if default audio is not %s" msgstr "" #, c-format msgid "Type %s" msgstr "" #, c-format msgid "Type %s value %s" msgstr "" #, c-format msgid "Type %s value %d" msgstr "" #, c-format msgid "Type %s value %" msgstr "" #, c-format msgid "Type %s value %f" msgstr "" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"%s\"" msgstr "%s\n\nOptions Etendues:\n\"%s\"" #, c-format msgid "" "%s\n" "\n" "Expanded Options:\n" "\"\"" msgstr "" msgid "Any" msgstr "" msgid "0: SAD, no subpel" msgstr "" msgid "4: SATD, qpel on all" msgstr "" msgid "5: SATD, multi-qpel on all" msgstr "" msgid "6: RD in I/P-frames" msgstr "" msgid "7: RD in all frames" msgstr "" msgid "8: RD refine in I/P-frames" msgstr "" msgid "9: RD refine in all frames" msgstr "" msgid "10: QPRD in all frames" msgstr "" msgid "11: No early terminations in analysis" msgstr "" msgid "Your names" msgstr "" msgid "Your emails" msgstr "" HandBrake-0.10.2/gtk/TODO.tasks0000664000175200017520000000024111026035545016436 0ustar handbrakehandbrake HandBrake-0.10.2/gtk/configure.ac0000664000175200017520000001202412524711423017112 0ustar handbrakehandbrakednl Process this file with autoconf to produce a configure script. dnl Created by Anjuta application wizard. AC_INIT(ghb, 0.1) AM_INIT_AUTOMAKE([1.7.9 foreign dist-bzip2 dist-zip]) AM_CONFIG_HEADER(config.h) AM_MAINTAINER_MODE AC_ISC_POSIX AC_PROG_CC AC_PROG_CXX AM_PROG_CC_STDC AC_HEADER_STDC if test x"$CC_FOR_BUILD" = x; then if test x"$cross_compiling" = x"yes"; then AC_CHECK_PROGS(CC_FOR_BUILD, gcc, cc) else CC_FOR_BUILD="$CC" fi fi AC_SUBST(CC_FOR_BUILD) # introduce the optional configure parameter for the path of libXXX.a AC_ARG_WITH(hb, AC_HELP_STRING( [--with-hb=prefix], [try this for the hb-library prefix install directory] ), hb_PATHSET=1, hb_PATHSET=0 ) if test $hb_PATHSET = 1 ; then case ${with_hb} in /*) HBINC="-I$with_hb/libhb -I$with_hb/contrib/include" LDFLAGS="$LDFLAGS -L$with_hb/libhb -L$with_hb/contrib/lib" AC_SUBST(HB_DIR, "$with_hb") ;; *) HBINC='-I$(top_srcdir)/'"$with_hb/libhb "'-I$(top_srcdir)/'"$with_hb/contrib/include" LDFLAGS="$LDFLAGS "'-L$(top_srcdir)/'"$with_hb/libhb "'-L$(top_srcdir)/'"$with_hb/contrib/lib" AC_SUBST(HB_DIR, '$(top_srcdir)/'"$with_hb") ;; esac else HBINC='-I$(top_srcdir)/'"../libhb "'-I$(top_srcdir)/'"../contrib/include" LDFLAGS="$LDFLAGS "'-L$(top_srcdir)/'"../libhb "'-L$(top_srcdir)/'"../contrib/lib" AC_SUBST(HB_DIR, '$(top_srcdir)/'"..") fi AC_ARG_ENABLE(dl, AS_HELP_STRING([--enable-dl], [enable libdl]), use_libdl=yes, use_libdl=no) AC_ARG_ENABLE(fdk-aac, AS_HELP_STRING([--enable-fdk-aac], [enable fdk aac encoder]), use_fdk_aac=yes, use_fdk_aac=no) AC_ARG_ENABLE(x265, AS_HELP_STRING([--enable-x265], [enable x265 encoder]), use_x265=yes, use_x265=no) AC_ARG_ENABLE(gst, AS_HELP_STRING([--disable-gst], [disable gstreamer (live preview)]), gst_disable=yes, gst_disable=no) AC_ARG_ENABLE(update-checks, AS_HELP_STRING([--disable-update-checks], [disable update checks]), update_checks=no, update_checks=yes) # overwrite global variable (used for Makefile generation) AC_SUBST(GLOBALCXXFLAGS, $CXXFLAGS ) AC_SUBST(GLOBALLDFLAGS, $LDFLAGS ) dnl *************************************************************************** dnl Internatinalization dnl *************************************************************************** GETTEXT_PACKAGE=ghb AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [GETTEXT package name]) AM_GLIB_GNU_GETTEXT IT_PROG_INTLTOOL([0.35.0]) AM_PROG_LIBTOOL AC_SYS_LARGEFILE case $host in *-*-mingw*) GHB_PACKAGES="gthread-2.0 gio-2.0 gmodule-2.0" mingw_flag=yes ;; *) case "$host_os" in linux*) pkg_gudev="gudev-1.0" ;; esac GHB_PACKAGES="gthread-2.0 gio-2.0 libnotify dbus-glib-1 gmodule-2.0 $pkg_gudev" mingw_flag=no ;; esac PKG_PROG_PKG_CONFIG GHB_PACKAGES="gtk+-3.0 $GHB_PACKAGES" GST1_MODULES="gstreamer-1.0 gstreamer-video-1.0 gstreamer-audio-1.0 gstreamer-pbutils-1.0" GST0_MODULES="gstreamer-0.10 gstreamer-interfaces-0.10 gstreamer-video-0.10 gstreamer-audio-0.10 gstreamer-pbutils-0.10" if test "x$gst_disable" = "xno" ; then PKG_CHECK_MODULES(GStreamer1, [$GST1_MODULES], use_gst1=yes, use_gst1=no) if test "x$use_gst1" = "xyes" ; then GHB_PACKAGES="$GHB_PACKAGES $GST1_MODULES" else PKG_CHECK_MODULES(GStreamer0, [$GST0_MODULES], use_gst0=yes, use_gst0=no) if test "x$use_gst0" = "xyes" ; then GHB_PACKAGES="$GHB_PACKAGES $GST0_MODULES" else gst_disable="yes" fi fi fi if test "x$gst_disable" = "xno" ; then CXXFLAGS="$CXXFLAGS -D_ENABLE_GST" CFLAGS="$CFLAGS -D_ENABLE_GST" fi if test "x$update_checks" = "xyes" ; then PKG_CHECK_MODULES(WebKitGtk3, webkitgtk-3.0, use_webkitgtk3=yes, use_webkitgtk3=no) if test "x$use_webkitgtk3" = "xyes" ; then GHB_PACKAGES="$GHB_PACKAGES webkitgtk-3.0" else update_checks="no" CFLAGS="$CFLAGS -D_NO_UPDATE_CHECK" fi else CFLAGS="$CFLAGS -D_NO_UPDATE_CHECK" fi AM_CONDITIONAL([MINGW], [test "x$mingw_flag" = "xyes"]) PKG_CHECK_MODULES(GHB, [$GHB_PACKAGES]) GHB_CFLAGS="$HBINC $GHB_CFLAGS" AC_PATH_PROG(BUILD_PKG_CONFIG, pkg-config, no) if test x"$BUILD_PKG_CONFIG" = x"no"; then AC_MSG_ERROR([You need to install pkg-config]) fi GHB_TOOLS_CFLAGS=`$BUILD_PKG_CONFIG --cflags glib-2.0 gdk-pixbuf-2.0` GHB_TOOLS_LIBS=`$BUILD_PKG_CONFIG --libs glib-2.0 gdk-pixbuf-2.0` HB_LIBS="-lhandbrake -lavresample -lavformat -lavcodec -lavutil -ldvdnav -ldvdread -lmp3lame -lvorbis -lvorbisenc -logg -lsamplerate -lx264 -lswscale -ltheoraenc -ltheoradec -lvpx -lz -lbz2 -lbluray -lass -lfontconfig -lfreetype -lxml2" case $host in *-*-mingw*) if test "x$use_libdl" = "xyes" ; then HB_LIBS+=" -ldl -lpthreadGC2" fi HB_LIBS+=" -lpthreadGC2" ;; *) HB_LIBS+=" -ldl -lpthread" ;; esac if test "x$use_fdk_aac" = "xyes" ; then HB_LIBS+=" -lfdk-aac" fi if test "x$use_x265" = "xyes" ; then HB_LIBS+=" -lx265" fi AC_SUBST(HB_LIBS) AC_SUBST(GHB_TOOLS_CFLAGS) AC_SUBST(GHB_TOOLS_LIBS) AC_SUBST(GHB_CFLAGS) AC_SUBST(GHB_LIBS) AC_OUTPUT([ Makefile src/Makefile po/Makefile.in ]) HandBrake-0.10.2/gtk/ghb.spec0000664000175200017520000000433712274210117016244 0ustar handbrakehandbrake Name: %{name} Version: %{version} Release: %{release}%{?dist} Summary: A program to transcode DVDs and other sources to MPEG-4 Group: Applications/Multimedia License: GPLv2 URL: http://handbrake.fr/ Source0: %{name}-%{version}.tar.bz2 Prefix: %{_prefix} BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) BuildRequires: glib2-devel, gtk3-devel, webkitgtk3-devel BuildRequires: gstreamer1-devel, gstreamer1-plugins-base-devel, libgudev1-devel BuildRequires: bzip2-devel, intltool, libnotify-devel, libtool, yasm Requires: gtk3, coreutils %define debug_package %{nil} %description HandBrake is an open-source, GPL-licensed, multi-platform, multi-threaded transcoder, available for MacOS X, Linux and Windows. %package gui Summary: A program to transcode DVDs and other sources to MPEG-4 Group: Applications/Multimedia %package cli Summary: A program to transcode DVDs and other sources to MPEG-4 Group: Applications/Multimedia %description gui HandBrake is an open-source, GPL-licensed, multi-platform, multi-threaded transcoder, available for MacOS X, Linux and Windows. %description cli HandBrake is an open-source, GPL-licensed, multi-platform, multi-threaded transcoder, available for MacOS X, Linux and Windows. %prep %setup -q cd %{_builddir}/%{name}-%{version} %build ./configure --debug=std --prefix=%{_prefix} make %{?_smp_mflags} -C build %install make -C build DESTDIR=$RPM_BUILD_ROOT install-strip %find_lang ghb ## blow away stuff we don't want /bin/rm -f $RPM_BUILD_ROOT%{_datadir}/icons/hicolor/icon-theme.cache %clean rm -rf %{buildroot} %post gui touch --no-create %{_datadir}/icons/hicolor if [ -x /usr/bin/gtk-update-icon-cache ]; then gtk-update-icon-cache -q %{_datadir}/icons/hicolor fi %postun gui touch --no-create %{_datadir}/icons/hicolor if [ -x /usr/bin/gtk-update-icon-cache ]; then gtk-update-icon-cache -q %{_datadir}/icons/hicolor fi %files gui -f ghb.lang %defattr(-,root,root,-) %doc NEWS AUTHORS CREDITS THANKS COPYING %{_datadir}/icons/hicolor %{_datadir}/applications %{_bindir}/ghb %files cli %defattr(-,root,root,-) %doc NEWS AUTHORS CREDITS THANKS COPYING %{_bindir}/HandBrakeCLI %changelog * Sun Apr 11 2010 John Stebbins - svn - Snapshot release HandBrake-0.10.2/gtk/autogen.sh0000775000175200017520000001064512414315046016633 0ustar handbrakehandbrake#!/bin/sh # Run this to generate all the initial makefiles, etc. srcdir=`dirname $0` test -z "$srcdir" && srcdir=. DIE=0 if [ -n "$GNOME2_DIR" ]; then ACLOCAL_FLAGS="-I $GNOME2_DIR/share/aclocal $ACLOCAL_FLAGS" LD_LIBRARY_PATH="$GNOME2_DIR/lib:$LD_LIBRARY_PATH" PATH="$GNOME2_DIR/bin:$PATH" export PATH export LD_LIBRARY_PATH fi (test -f $srcdir/configure.ac) || { echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" echo " top-level package directory" exit 1 } (autoconf --version) < /dev/null > /dev/null 2>&1 || { echo echo "**Error**: You must have \`autoconf' installed." echo "Download the appropriate package for your distribution," echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" DIE=1 } (grep "^IT_PROG_INTLTOOL" $srcdir/configure.ac >/dev/null) && { (intltoolize --version) < /dev/null > /dev/null 2>&1 || { echo echo "**Error**: You must have \`intltool' installed." echo "You can get it from:" echo " ftp://ftp.gnome.org/pub/GNOME/" DIE=1 } } (grep "^AM_PROG_XML_I18N_TOOLS" $srcdir/configure.ac >/dev/null) && { (xml-i18n-toolize --version) < /dev/null > /dev/null 2>&1 || { echo echo "**Error**: You must have \`xml-i18n-toolize' installed." echo "You can get it from:" echo " ftp://ftp.gnome.org/pub/GNOME/" DIE=1 } } (grep "^AM_PROG_LIBTOOL" $srcdir/configure.ac >/dev/null) && { (libtoolize --version) < /dev/null > /dev/null 2>&1 || { echo echo "**Error**: You must have \`libtoolize' installed." echo "You can get it from: ftp://ftp.gnu.org/pub/gnu/" DIE=1 } } (grep "^AM_GLIB_GNU_GETTEXT" $srcdir/configure.ac >/dev/null) && { (grep "sed.*POTFILES" $srcdir/configure.ac) > /dev/null || \ (glib-gettextize --version) < /dev/null > /dev/null 2>&1 || { echo echo "**Error**: You must have \`glib' installed." echo "You can get it from: ftp://ftp.gtk.org/pub/gtk" DIE=1 } } (automake --version) < /dev/null > /dev/null 2>&1 || { echo echo "**Error**: You must have \`automake' installed." echo "You can get it from: ftp://ftp.gnu.org/pub/gnu/" DIE=1 NO_AUTOMAKE=yes } # if no automake, don't bother testing for aclocal test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { echo echo "**Error**: Missing \`aclocal'. The version of \`automake'" echo "installed doesn't appear recent enough." echo "You can get automake from ftp://ftp.gnu.org/pub/gnu/" DIE=1 } if test "$DIE" -eq 1; then exit 1 fi if test -z "$*"; then echo "**Warning**: I am going to run \`configure' with no arguments." echo "If you wish to pass any to it, please specify them on the" echo \`$0\'" command line." echo fi case $CC in xlc ) am_opt=--include-deps;; esac for coin in `find $srcdir -path $srcdir/CVS -prune -o -name configure.ac -print` do dr=`dirname $coin` if test -f $dr/NO-AUTO-GEN; then echo skipping $dr -- flagged as no auto-gen else echo processing $dr ( cd $dr aclocalinclude="$ACLOCAL_FLAGS" if grep "^AM_GLIB_GNU_GETTEXT" configure.ac >/dev/null; then echo "Creating $dr/aclocal.m4 ..." test -r $dr/aclocal.m4 || touch $dr/aclocal.m4 echo "Running glib-gettextize... Ignore non-fatal messages." echo "no" | glib-gettextize --force --copy echo "Making $dr/aclocal.m4 writable ..." test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4 fi if grep "^IT_PROG_INTLTOOL" configure.ac >/dev/null; then echo "Running intltoolize..." intltoolize --copy --force --automake fi if grep "^AM_PROG_XML_I18N_TOOLS" configure.ac >/dev/null; then echo "Running xml-i18n-toolize..." xml-i18n-toolize --copy --force --automake fi if grep "^AM_PROG_LIBTOOL" configure.ac >/dev/null; then if test -z "$NO_LIBTOOLIZE" ; then echo "Running libtoolize..." libtoolize --force --copy fi fi echo "Running aclocal $aclocalinclude ..." aclocal $aclocalinclude if grep "^AM_CONFIG_HEADER" configure.ac >/dev/null; then echo "Running autoheader..." autoheader fi echo "Running automake --gnu $am_opt ..." automake --add-missing --copy --gnu $am_opt echo "Running autoconf ..." autoconf ) fi done conf_flags="--enable-maintainer-mode" if test x$NOCONFIGURE = x; then echo Running $srcdir/configure $conf_flags "$@" ... $srcdir/configure $conf_flags "$@" \ && echo Now type \`make\' to compile. || exit 1 else echo Skipping configure process. fi HandBrake-0.10.2/gtk/module.defs0000664000175200017520000000157312374433126016767 0ustar handbrakehandbrake$(eval $(call import.MODULE.defs,GTK,gtk,LIBHB)) $(eval $(call import.GCC,GTK)) GTK.src/ = $(SRC/)gtk/ GTK.build/ = $(BUILD/)gtk/ GTK.CONFIGURE.stamp = $(GTK.build/).stamp.configure ############################################################################### GTK.out += $(GTK.CONFIGURE.stamp) BUILD.out += $(GTK.out) ############################################################################### ifeq (1-mingw,$(BUILD.cross)-$(BUILD.system)) GTK.CONFIGURE.extra += --host=$(BUILD.spec) --disable-gst ifeq ($(HAS.dlfcn),1) GTK.CONFIGURE.extra += --enable-dl endif endif ifeq (0,$(FEATURE.gtk.update.checks)) GTK.CONFIGURE.extra += --disable-update-checks endif ifeq (0,$(FEATURE.gst)) GTK.CONFIGURE.extra += --disable-gst endif ifeq (1,$(FEATURE.fdk_aac)) GTK.CONFIGURE.extra += --enable-fdk-aac endif ifeq (1,$(FEATURE.x265)) GTK.CONFIGURE.extra += --enable-x265 endif HandBrake-0.10.2/gtk/ChangeLog0000664000175200017520000000052711026035545016403 0ustar handbrakehandbrake2007-12-28 Johannes Schmid,,, reviewed by: * project.anjuta: 2007-12-23 Johannes Schmid,,, reviewed by: * src/Makefile.am.tpl: 2007-12-23 Johannes Schmid,,, reviewed by: * src/Makefile.am.tpl: HandBrake-0.10.2/gtk/module.rules0000664000175200017520000000233211767361722017201 0ustar handbrakehandbrake$(eval $(call import.MODULE.rules,GTK)) build: gtk.build install: gtk.install install-strip: gtk.install-strip uninstall: gtk.uninstall clean: gtk.clean xclean: gtk.xclean gtk.configure: $(GTK.CONFIGURE.stamp) $(GTK.CONFIGURE.stamp): | $(dir $(GTK.CONFIGURE.stamp)) $(GTK.CONFIGURE.stamp): $(GTK.src/)Makefile.am $(GTK.CONFIGURE.stamp): $(GTK.src/)configure.ac $(GTK.src/)src/Makefile.am set -e; cd $(GTK.src/); NOCONFIGURE=1 ./autogen.sh set -e; cd $(GTK.build/); $(call fn.ABSOLUTE,$(GTK.src/))configure \ $(GTK.CONFIGURE.extra) \ PKG_CONFIG_PATH=$(BUILD/)contrib/lib/pkgconfig \ CFLAGS="$(call fn.ARGS,GTK.GCC,.g .O *D ?extra)" \ LDFLAGS="$(call fn.ARGS,GTK.GCC,?strip .g .O ?extra.exe)" \ --prefix=$(PREFIX) \ --with-hb=$(call fn.ABSOLUTE,$(BUILD/)) $(TOUCH.exe) $@ gtk.build: | $(GTK.build/) gtk.build: $(GTK.CONFIGURE.stamp) $(LIBHB.a) +$(MAKE) -C $(GTK.build/) gtk.install-strip: $(MAKE) -C $(GTK.build/) prefix=$(PREFIX) install-strip gtk.install: $(MAKE) -C $(GTK.build/) prefix=$(PREFIX) install gtk.uninstall: $(MAKE) -C $(GTK.build/) uninstall gtk.clean: $(MAKE) -C $(GTK.build/) clean gtk.xclean: $(MAKE) -C $(GTK.build/) distclean $(RM.exe) -f $(GTK.out) $(RM.exe) -fr $(GTK.build/) HandBrake-0.10.2/gtk/Makefile.am0000664000175200017520000000053611217051215016657 0ustar handbrakehandbrake## Process this file with automake to produce Makefile.in ## Created by Anjuta SUBDIRS = src po ghbdocdir = ${prefix}/share/doc/ghb EXTRA_DIST = $(ghbdoc_DATA) # Copy all the spec files. Of cource, only one is actually used. dist-hook: for specfile in *.spec; do \ if test -f $$specfile; then \ cp -p $$specfile $(distdir); \ fi \ done HandBrake-0.10.2/gtk/Jamfile0000664000175200017520000000114011026035545016113 0ustar handbrakehandbrakeSubDir TOP gtk ; #rule GhbConfig #{ # Depends $(SUBDIR)/ghb : $(1) ; #} #actions GhbConfig #{ # cd `dirname $(1)` && # ./configure --prefix=/usr #} #GhbConfig $(SUBDIR)/config.h : $(SUBDIR)/configure ; rule GhbApp { Depends all : $(1) ; } actions GhbApp { cd `dirname $(1)` && $(MAKE) } GhbApp $(SUBDIR)/ghb : libhb.a $(SUBDIR)/config.h ; rule GhbClean { Depends clean : $(1) ; } actions GhbClean { cd $(2) && $(MAKE) clean } GhbClean clean_dummy : $(SUBDIR) ; rule GhbInstall { Depends install : $(1) ; } actions GhbInstall { cd $(2) && $(MAKE) install } GhbInstall install_dummy : $(SUBDIR) ; HandBrake-0.10.2/gtk/src/0000775000175200017520000000000012535641637015427 5ustar handbrakehandbrakeHandBrake-0.10.2/gtk/src/queuehandler.c0000664000175200017520000021236312465416500020253 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * callbacks.c * Copyright (C) John Stebbins 2008-2015 * * callbacks.c is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. */ #include "ghbcompat.h" #include #include #include #include "hb.h" #include "settings.h" #include "hb-backend.h" #include "values.h" #include "callbacks.h" #include "presets.h" #include "audiohandler.h" #include "ghb-dvd.h" G_MODULE_EXPORT void queue_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud) { GtkTreeModel *store; GtkTreeIter iter, piter; g_debug("queue_list_selection_changed_cb ()"); // A queue entry is made up of a parent and multiple // children that are visible when expanded. When and entry // is selected, I want the parent to be selected. // This is purely cosmetic. if (gtk_tree_selection_get_selected(selection, &store, &iter)) { GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_edit"); gtk_widget_set_sensitive (widget, TRUE); widget = GHB_WIDGET (ud->builder, "queue_reload"); gtk_widget_set_sensitive (widget, TRUE); if (gtk_tree_model_iter_parent (store, &piter, &iter)) { GtkTreePath *path; GtkTreeView *treeview; gtk_tree_selection_select_iter (selection, &piter); path = gtk_tree_model_get_path (store, &piter); treeview = gtk_tree_selection_get_tree_view (selection); // Make the parent visible in scroll window if it is not. gtk_tree_view_scroll_to_cell (treeview, path, NULL, FALSE, 0, 0); gtk_tree_path_free(path); } } else { GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_edit"); gtk_widget_set_sensitive (widget, FALSE); widget = GHB_WIDGET (ud->builder, "queue_reload"); gtk_widget_set_sensitive (widget, FALSE); } } static void add_to_queue_list(signal_user_data_t *ud, GValue *settings, GtkTreeIter *piter) { GtkTreeView *treeview; GtkTreeIter iter; GtkTreeStore *store; gint status; GtkTreeIter citer; gchar *basename; const char *vol_name, *dest; gint title, start_point, end_point; gboolean two_pass, vqtype; gchar *escape, *escape2; GString *str = g_string_new(""); #define XPRINT(fmt, ...) \ g_string_append_printf(str, fmt, ##__VA_ARGS__) g_debug("update_queue_list ()"); if (settings == NULL) return; treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list")); store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); // Collect information for first line in the display // Volume (Title X, Chapters Y through Z, N Video Passes) --> Destination title = ghb_settings_get_int(settings, "title"); start_point = ghb_settings_get_int(settings, "start_point"); end_point = ghb_settings_get_int(settings, "end_point"); vol_name = ghb_settings_get_const_string(settings, "volume_label"); dest = ghb_settings_get_const_string(settings, "destination"); basename = g_path_get_basename(dest); escape = g_markup_escape_text(basename, -1); escape2 = g_markup_escape_text(vol_name, -1); vqtype = ghb_settings_get_boolean(settings, "vquality_type_constant"); two_pass = ghb_settings_get_boolean(settings, "VideoTwoPass"); const gchar *points = _("Chapters"); if (ghb_settings_combo_int(settings, "PtoPType") == 0) points = _("Chapters"); else if (ghb_settings_combo_int(settings, "PtoPType") == 1) points = _("Seconds"); else if (ghb_settings_combo_int(settings, "PtoPType") == 2) points = _("Frames"); if (!vqtype && two_pass) { XPRINT(_("%s " "(Title %d, %s %d through %d, 2 Video Passes)" " --> %s"), escape2, title, points, start_point, end_point, escape ); } else { XPRINT(_("%s " "(Title %d, %s %d through %d)" " --> %s"), escape2, title, points, start_point, end_point, escape ); } g_free(basename); g_free(escape); g_free(escape2); if (piter) { iter = *piter; } else { gtk_tree_store_append(store, &iter, NULL); } // Set the job status icon status = ghb_settings_get_int(settings, "job_status"); const char *status_icon; switch (status) { case GHB_QUEUE_PENDING: status_icon = "hb-source"; break; case GHB_QUEUE_FAIL: case GHB_QUEUE_CANCELED: status_icon = "hb-stop"; break; case GHB_QUEUE_DONE: status_icon = "hb-complete"; break; default: status_icon = "hb-source"; break; } // Set the status icon, job description, and delete icon button gtk_tree_store_set(store, &iter, 0, FALSE, 1, status_icon, 2, str->str, 3, "hb-remove", -1); // Reset the string for the next line g_string_assign(str, ""); // Next line in the display // Preset: PresetName gchar *preset; gboolean markers; gboolean preset_modified; const GValue *path; const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); preset_modified = ghb_settings_get_boolean(settings, "preset_modified"); path = ghb_settings_get_value(settings, "preset"); preset = ghb_preset_path_string(path); markers = ghb_settings_get_boolean(settings, "ChapterMarkers"); if (preset_modified) { XPRINT(_("Modified Preset Based On: %s\n"), preset); } else { XPRINT(_("Preset: %s\n"), preset); } g_free(preset); // Next line in the display (Container type) // Format: XXX Container XPRINT(_("Format: %s Container\n"), mux->name); // Next line in the display (Container options) // Container Options: - Chapter Markers gboolean ipod = FALSE, http = FALSE, large = FALSE; if (mux->format & HB_MUX_MASK_MP4) { ipod = ghb_settings_get_boolean(settings, "Mp4iPodCompatible"); http = ghb_settings_get_boolean(settings, "Mp4HttpOptimize"); large = ghb_settings_get_boolean(settings, "Mp4LargeFile"); } if (http || ipod || large || markers) { const char *prefix = " "; XPRINT(_("Container Options:")); if (markers) { XPRINT(_("%sChapter Markers"), prefix); prefix = " - "; } if (ipod) { XPRINT(_("%siPod 5G Support"), prefix); prefix = " - "; } if (http) { XPRINT(_("%sWeb Optimized"), prefix); prefix = " - "; } if (large) { XPRINT(_("%sLarge File Size (>4GB)"), prefix); prefix = " - "; } XPRINT("\n"); } // Next line in the display (Destination) // Destination: /Full/Destination/Path.mkv escape = g_markup_escape_text(dest, -1); XPRINT(_("Destination: %s\n"), escape); g_free(escape); // Next line in the display (Picture settings) // Picture: Source: W x H, Output W x H (Animorphic), Display W x H int width, height, pic_par; int crop[4]; gboolean keep_aspect; width = ghb_settings_get_int(settings, "scale_width"); height = ghb_settings_get_int(settings, "scale_height"); pic_par = ghb_settings_get_int(settings, "PicturePAR"); keep_aspect = ghb_settings_get_boolean(settings, "PictureKeepRatio"); crop[0] = ghb_settings_get_int(settings, "PictureTopCrop"); crop[1] = ghb_settings_get_int(settings, "PictureBottomCrop"); crop[2] = ghb_settings_get_int(settings, "PictureLeftCrop"); crop[3] = ghb_settings_get_int(settings, "PictureRightCrop"); gchar *aspect_desc; switch (pic_par) { case 0: { if (keep_aspect) { aspect_desc = _("(Aspect Preserved)"); } else { aspect_desc = _("(Aspect Lost)"); } } break; case 1: { aspect_desc = _("(Anamorphic)"); } break; case 2: { aspect_desc = _("(Anamorphic)"); } break; case 3: { aspect_desc = _("(Custom Anamorphic)"); } break; default: { aspect_desc = ""; } break; } gint source_width, source_height; source_width = ghb_settings_get_int(settings, "source_width"); source_height = ghb_settings_get_int(settings, "source_height"); XPRINT(_("Picture: ")); XPRINT(_("Source: %d x %d, Output %d x %d %s, Crop %d:%d:%d:%d"), source_width, source_height, width, height, aspect_desc, crop[0], crop[1], crop[2], crop[3]); if (pic_par) { int display_width, display_height; display_width = ghb_settings_get_int(settings, "PictureDisplayWidth"); display_height = ghb_settings_get_int(settings, "PictureDisplayHeight"); XPRINT(_(", Display %d x %d"), display_width, display_height); } XPRINT("\n"); // Next line in the display (Filter settings) // Filters: - Deinterlace gint decomb, detel, deint, deblock, denoise; const gchar *detel_cust, *deint_cust, *decomb_cust; const gchar *deint_opt, *decomb_opt; const gchar *denoise_opt, *denoise_preset, *denoise_tune, *denoise_cust; gboolean decomb_deint; gboolean grayscale; gboolean filters; decomb_deint = ghb_settings_get_boolean(settings, "PictureDecombDeinterlace"); decomb = ghb_settings_combo_int(settings, "PictureDecomb"); decomb_opt = ghb_settings_combo_option(settings, "PictureDecomb"); decomb_cust = ghb_settings_get_const_string(settings, "PictureDecombCustom"); deint = ghb_settings_combo_int(settings, "PictureDeinterlace"); deint_opt = ghb_settings_combo_option(settings, "PictureDeinterlace"); deint_cust = ghb_settings_get_const_string(settings, "PictureDeinterlaceCustom"); detel = ghb_settings_combo_int(settings, "PictureDetelecine"); detel_cust = ghb_settings_get_const_string(settings, "PictureDetelecineCustom"); deblock = ghb_settings_get_int(settings, "PictureDeblock"); denoise = ghb_settings_combo_int(settings, "PictureDenoiseFilter"); denoise_opt = ghb_settings_combo_option(settings, "PictureDenoiseFilter"); denoise_preset = ghb_settings_combo_option(settings, "PictureDenoisePreset"); denoise_tune = ghb_settings_combo_option(settings, "PictureDenoiseTune"); denoise_cust = ghb_settings_get_const_string(settings, "PictureDenoiseCustom"); grayscale = ghb_settings_get_boolean(settings, "VideoGrayScale"); filters = detel || (decomb_deint && decomb) || (!decomb_deint && deint) || denoise || (deblock >= 5) || grayscale; if (filters) { const char *prefix = " "; XPRINT(_("Filters:")); if (detel) { XPRINT(_("%sDetelecine"), prefix); if (detel == 1) { XPRINT(": %s", detel_cust); } prefix = " - "; } if (decomb_deint && decomb) { XPRINT(_("%sDecomb"), prefix); if (decomb == 1) { XPRINT(": %s", decomb_cust); } else { XPRINT(": %s", decomb_opt); } prefix = " - "; } else if (!decomb_deint && deint) { XPRINT(_("%sDeinterlace"), prefix); if (deint == 1) { XPRINT(": %s", deint_cust); } else { XPRINT(": %s", deint_opt); } prefix = " - "; } if (denoise) { XPRINT(_("%sDenoise Filter %s:"), prefix, denoise_opt); if (ghb_settings_combo_int(settings, "PictureDenoisePreset") == 1) { XPRINT(" %s", denoise_cust); } else { XPRINT(" %s", denoise_preset); if (denoise == 1 && strcmp(denoise_tune, "None")) { XPRINT(",%s", denoise_tune); } } prefix = " - "; } if (deblock >= 5) { XPRINT(_("%sDeblock: %d"), prefix, deblock); prefix = " - "; } if (grayscale) { XPRINT(_("%sGrayscale"), prefix); prefix = " - "; } XPRINT("\n"); } // Next line in the display (Video Encoder) // Video: Encoder, Framerate: fps, RF/Bitrate/QP const hb_encoder_t *video_encoder; video_encoder = ghb_settings_video_encoder(settings, "VideoEncoder"); XPRINT(_("Video: %s"), video_encoder->name); const hb_rate_t *fps; fps = ghb_settings_video_framerate(settings, "VideoFramerate"); if (fps->rate == 0) { const char *rate_mode; if (ghb_settings_get_boolean(settings, "VideoFramerateCFR")) rate_mode = _("(constant)"); else rate_mode = _("(variable)"); XPRINT(_(", Framerate: %s %s"), fps->name, rate_mode); } else { if (ghb_settings_get_boolean(settings, "VideoFrameratePFR")) { XPRINT(_(", Framerate: Peak %s (may be lower)"), fps->name); } else { XPRINT(_(", Framerate: %s (constant frame rate)"), fps->name); } } const gchar *vq_desc = _("Error"); const gchar *vq_units = ""; gdouble vqvalue; if (!vqtype) { // Has to be bitrate vqvalue = ghb_settings_get_int(settings, "VideoAvgBitrate"); vq_desc = _("Bitrate:"); vq_units = _("kbps"); XPRINT(", %s %d%s", vq_desc, (int)vqvalue, vq_units); } else { // Constant quality vqvalue = ghb_settings_get_double(settings, "VideoQualitySlider"); vq_desc = _("Constant Quality:"); vq_units = hb_video_quality_get_name(video_encoder->codec); XPRINT(", %s %.4g(%s)", vq_desc, vqvalue, vq_units); } XPRINT("\n"); // Next line in the display (Turbo setting) gboolean turbo; turbo = ghb_settings_get_boolean(settings, "VideoTurboTwoPass"); if (!vqtype && two_pass && turbo) { XPRINT(_("Turbo 1st Pass: On\n")); } // Next line in the display (Video Encoder Options) // Video Options: Preset - Tune - Profile - Level if (video_encoder->codec == HB_VCODEC_X264 && !ghb_settings_get_boolean(settings, "x264UseAdvancedOptions")) { const gchar *extra_opt = NULL; // If the encoder supports presets... if (hb_video_encoder_get_presets(video_encoder->codec) != NULL) { const gchar *preset_opt, *tune_opt; const gchar *profile_opt, *level_opt; gboolean fastdecode, zerolatency; preset_opt = ghb_settings_get_const_string(settings, "VideoPreset"); tune_opt = ghb_settings_get_const_string(settings, "VideoTune"); fastdecode = ghb_settings_get_boolean(settings, "x264FastDecode"); zerolatency = ghb_settings_get_boolean(settings, "x264ZeroLatency"); profile_opt = ghb_settings_get_const_string(settings, "VideoProfile"); level_opt = ghb_settings_get_const_string(settings, "VideoLevel"); extra_opt = ghb_settings_get_const_string(settings, "VideoOptionExtra"); XPRINT(_("Video Options: Preset: %s"), preset_opt); if ((tune_opt != NULL && tune_opt[0] != 0) || zerolatency || fastdecode) { const char *prefix = ""; XPRINT(_(" - Tune: ")); if (tune_opt != NULL && tune_opt[0] != 0) { XPRINT("%s%s", prefix, tune_opt); prefix = ","; } if (video_encoder->codec == HB_VCODEC_X264) { if (fastdecode) { XPRINT("%sfastdecode", prefix); prefix = ","; } if (zerolatency) { XPRINT("%szerolatency", prefix); prefix = ","; } } XPRINT(""); } if (profile_opt != NULL && profile_opt[0] != 0) { XPRINT(_(" - Profile: %s"), profile_opt); } if (level_opt != NULL && level_opt[0] != 0) { XPRINT(_(" - Level: %s"), level_opt); } XPRINT("\n"); } // Next line in the display (Video Encoder Options) // Video Advanced Options: detailed settings if (extra_opt != NULL && extra_opt[0] != 0) { XPRINT(_("Advanced Options: %s\n"), extra_opt); } } else if (video_encoder->codec == HB_VCODEC_X264) { // Next line in the display (Video Encoder Options) // Video Advanced Options: detailed settings gchar *opts = ghb_build_advanced_opts_string(settings); if (opts != NULL && opts[0] != 0) { XPRINT(_("Advanced Options: %s\n"), opts); } g_free(opts); } // Next line in the display (Audio) // Audio Tracks: count // Source description, Encoder, Mix, Samplerate, Bitrate // ... gint count, ii; const GValue *audio_list; audio_list = ghb_settings_get_value(settings, "audio_list"); count = ghb_array_len(audio_list); if (count == 1) { XPRINT(_("Audio: ")); } else if (count > 1) { XPRINT(_("Audio Tracks: %d"), count); } for (ii = 0; ii < count; ii++) { gchar *quality = NULL, *track; GValue *asettings; const hb_encoder_t *audio_encoder; asettings = ghb_array_get_nth(audio_list, ii); audio_encoder = ghb_settings_audio_encoder(asettings, "AudioEncoder"); double q = ghb_settings_get_double(asettings, "AudioTrackQuality"); if (ghb_settings_get_boolean(asettings, "AudioTrackQualityEnable") && q != HB_INVALID_AUDIO_QUALITY) { quality = ghb_format_quality(_("Quality: "), audio_encoder->codec, q); } else { int br = ghb_settings_audio_bitrate_rate(asettings, "AudioBitrate"); quality = g_strdup_printf(_("Bitrate: %d"), br); } const hb_rate_t *sr; sr = ghb_settings_audio_samplerate(asettings, "AudioSamplerate"); track = ghb_settings_get_string(asettings, "AudioTrackDescription"); const hb_mixdown_t *mix; mix = ghb_settings_mixdown(asettings, "AudioMixdown"); if (count > 1) XPRINT("\n\t"); if (audio_encoder->codec & HB_ACODEC_PASS_FLAG) { XPRINT(_("%s --> Encoder: %s"), track, audio_encoder->name); } else { XPRINT(_("%s --> Encoder: %s, Mixdown: %s, SampleRate: %s, %s"), track, audio_encoder->name, mix->name, sr->name, quality); } g_free(track); g_free(quality); } if (count > 0) { XPRINT("\n"); } // Next line in the display (Subtitle) // Subtitle Tracks: count // Subtitle description(Subtitle options) // ... const GValue *sub_list; sub_list = ghb_settings_get_value(settings, "subtitle_list"); count = ghb_array_len(sub_list); if (count == 1) { XPRINT(_("Subtitle: ")); } else if (count > 1) { XPRINT(_("Subtitle Tracks: %d\n"), count); } for (ii = 0; ii < count; ii++) { GValue *settings; gchar *track; gboolean force, burn, def; gint source; settings = ghb_array_get_nth(sub_list, ii); track = ghb_settings_get_string(settings, "SubtitleTrackDescription"); source = ghb_settings_get_int(settings, "SubtitleSource"); force = ghb_settings_get_boolean(settings, "SubtitleForced"); burn = ghb_settings_get_boolean(settings, "SubtitleBurned"); def = ghb_settings_get_boolean(settings, "SubtitleDefaultTrack"); if (count > 1) XPRINT("\t"); if (source != SRTSUB) { XPRINT("%s%s%s%s\n", track, force ? _(" (Force)"):"", burn ? _(" (Burn)"):"", def ? _(" (Default)"):"" ); } else { gint offset; gchar *filename, *basename, *code; offset = ghb_settings_get_int(settings, "SrtOffset"); filename = ghb_settings_get_string(settings, "SrtFile"); basename = g_path_get_basename(filename); code = ghb_settings_get_string(settings, "SrtCodeset"); XPRINT(_(" %s (%s), %s, Offset (ms) %d%s\n"), track, code, basename, offset, def ? " (Default)":""); g_free(filename); g_free(basename); g_free(code); } g_free(track); } // Remove the final newline in the string if (str->len > 0 && str->str[str->len-1] == '\n') str->str[str->len-1] = 0; gtk_tree_store_append(store, &citer, &iter); gtk_tree_store_set(store, &citer, 2, str->str, -1); g_string_free(str, TRUE); } void ghb_update_status(signal_user_data_t *ud, int status, int index) { const char *status_icon; switch (status) { case GHB_QUEUE_PENDING: status_icon = "hb-source"; break; case GHB_QUEUE_FAIL: case GHB_QUEUE_CANCELED: status_icon = "hb-stop"; break; case GHB_QUEUE_DONE: status_icon = "hb-complete"; break; default: status_icon = "hb-source"; break; } int count = ghb_array_len(ud->queue); if (index < 0 || index >= count) { // invalid index return; } GValue *settings; settings = ghb_array_get_nth(ud->queue, index); if (settings == NULL) // should never happen return; if (ghb_settings_get_int(settings, "job_status") == GHB_QUEUE_RUNNING) return; // Never change the status of currently running jobs GtkTreeView *treeview; GtkTreeModel *store; GtkTreeIter iter; treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list")); store = gtk_tree_view_get_model(treeview); gchar *path = g_strdup_printf ("%d", index); if (gtk_tree_model_get_iter_from_string(store, &iter, path)) { gtk_tree_store_set(GTK_TREE_STORE(store), &iter, 1, status_icon, -1); } g_free(path); ghb_settings_set_int(settings, "job_status", status); } void ghb_update_all_status(signal_user_data_t *ud, int status) { int count, ii; count = ghb_array_len(ud->queue); for (ii = 0; ii < count; ii++) { ghb_update_status(ud, status, ii); } } G_MODULE_EXPORT void queue_reload_all_clicked_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_update_all_status(ud, GHB_QUEUE_PENDING); } G_MODULE_EXPORT void queue_reload_clicked_cb(GtkWidget *widget, signal_user_data_t *ud) { GtkTreeView *treeview; GtkTreeSelection *selection; GtkTreeModel *store; GtkTreeIter iter; gint row; gint *indices; g_debug("queue_key_press_cb ()"); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list")); store = gtk_tree_view_get_model(treeview); selection = gtk_tree_view_get_selection (treeview); if (gtk_tree_selection_get_selected(selection, &store, &iter)) { GtkTreePath *treepath; treepath = gtk_tree_model_get_path (store, &iter); // Find the entry in the queue indices = gtk_tree_path_get_indices (treepath); row = indices[0]; // Can only free the treepath After getting what I need from // indices since this points into treepath somewhere. gtk_tree_path_free (treepath); if (row < 0) return; if (row >= ghb_array_len(ud->queue)) return; ghb_update_status(ud, GHB_QUEUE_PENDING, row); } } static gboolean validate_settings(signal_user_data_t *ud, GValue *settings, gint batch) { // Check to see if the dest file exists or is // already in the queue gchar *message; const gchar *dest; gint count, ii; gint title_id, titleindex; const hb_title_t *title; title_id = ghb_settings_get_int(settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (title == NULL) return FALSE; dest = ghb_settings_get_const_string(settings, "destination"); count = ghb_array_len(ud->queue); for (ii = 0; ii < count; ii++) { GValue *js; const gchar *filename; js = ghb_array_get_nth(ud->queue, ii); filename = ghb_settings_get_const_string(js, "destination"); if (strcmp(dest, filename) == 0) { message = g_strdup_printf( _("Destination: %s\n\n" "Another queued job has specified the same destination.\n" "Do you want to overwrite?"), dest); if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, _("Cancel"), _("Overwrite"))) { g_free(message); return FALSE; } g_free(message); break; } } gchar *destdir = g_path_get_dirname(dest); if (!g_file_test(destdir, G_FILE_TEST_IS_DIR)) { message = g_strdup_printf( _("Destination: %s\n\n" "This is not a valid directory."), destdir); ghb_message_dialog(GTK_MESSAGE_ERROR, message, _("Cancel"), NULL); g_free(message); g_free(destdir); return FALSE; } #if !defined(_WIN32) // This doesn't work properly on windows if (g_access(destdir, R_OK|W_OK) != 0) { message = g_strdup_printf( _("Destination: %s\n\n" "Can not read or write the directory."), destdir); ghb_message_dialog(GTK_MESSAGE_ERROR, message, _("Cancel"), NULL); g_free(message); g_free(destdir); return FALSE; } #endif if (!batch) { GFile *gfile; GFileInfo *info; guint64 size; gchar *resolved = ghb_resolve_symlink(destdir); gfile = g_file_new_for_path(resolved); info = g_file_query_filesystem_info(gfile, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, NULL, NULL); if (info != NULL) { if (g_file_info_has_attribute(info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE)) { size = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE); gint64 fsize = (guint64)10 * 1024 * 1024 * 1024; if (size < fsize) { message = g_strdup_printf( _("Destination filesystem is almost full: %uM free\n\n" "Encode may be incomplete if you proceed.\n"), (guint)(size / (1024L*1024L))); if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, _("Cancel"), _("Proceed"))) { g_free(message); g_free(destdir); return FALSE; } g_free(message); } } g_object_unref(info); } g_object_unref(gfile); g_free(resolved); } g_free(destdir); if (g_file_test(dest, G_FILE_TEST_EXISTS)) { message = g_strdup_printf( _("Destination: %s\n\n" "File already exists.\n" "Do you want to overwrite?"), dest); if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, _("Cancel"), _("Overwrite"))) { g_free(message); return FALSE; } g_free(message); g_unlink(dest); } // Validate audio settings if (!ghb_validate_audio(settings)) { return FALSE; } // Validate audio settings if (!ghb_validate_subtitles(settings)) { return FALSE; } // Validate video settings if (!ghb_validate_video(settings)) { return FALSE; } // Validate filter settings if (!ghb_validate_filters(settings)) { return FALSE; } return TRUE; } static gboolean queue_add(signal_user_data_t *ud, GValue *settings, gint batch) { // Add settings to the queue g_debug("queue_add ()"); if (!validate_settings(ud, settings, batch)) { return FALSE; } if (ud->queue == NULL) ud->queue = ghb_array_value_new(32); // Copy current prefs into settings // The job should run with the preferences that existed // when the job was added to the queue. ghb_settings_set_value(settings, "Preferences", ud->prefs); // Make a copy of current settings to be used for the new job ghb_settings_set_int(settings, "job_status", GHB_QUEUE_PENDING); ghb_settings_set_int(settings, "job_unique_id", 0); ghb_array_append(ud->queue, settings); add_to_queue_list(ud, settings, NULL); ghb_save_queue(ud->queue); ghb_update_pending(ud); return TRUE; } G_MODULE_EXPORT void queue_add_clicked_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("queue_add_clicked_cb ()"); GValue *settings = ghb_value_dup(ud->settings); if (!queue_add(ud, settings, 0)) ghb_value_free(settings); // Validation of settings may have changed audio list ghb_audio_list_refresh_all(ud); } static gboolean title_multiple_can_select(GValue *settings_array, int index) { gint count, ii; GValue *settings, *gdest; const char *dest; settings = ghb_array_get_nth(settings_array, index); gdest = ghb_settings_get_value(settings, "destination"); dest = g_value_get_string(gdest); if (dest == NULL) return FALSE; count = ghb_array_len(settings_array); count = count < index ? count : index; for (ii = 0; ii < count; ii++) { const char *tmp; settings = ghb_array_get_nth(settings_array, ii); gdest = ghb_settings_get_value(settings, "destination"); tmp = g_value_get_string(gdest); if (tmp != NULL && !strncmp(dest, tmp, PATH_MAX)) return FALSE; } return TRUE; } static GtkWidget *find_widget(GtkWidget *widget, gchar *name) { const char *wname; GtkWidget *result = NULL; if (widget == NULL || name == NULL) return NULL; wname = gtk_widget_get_name(widget); if (wname != NULL && !strncmp(wname, name, 80)) { return widget; } if (GTK_IS_CONTAINER(widget)) { GList *list, *link; link = list = gtk_container_get_children(GTK_CONTAINER(widget)); while (link) { result = find_widget(GTK_WIDGET(link->data), name); if (result != NULL) break; link = link->next; } g_list_free(list); } return result; } static PangoAttrList *default_title_attrs; static void title_add_multiple_set_sensitive(GtkWidget *row, gboolean sensitive) { GtkWidget *widget; widget = find_widget(row, "title_selected"); gtk_widget_set_sensitive(widget, sensitive); widget = find_widget(row, "title_label"); if (!sensitive) { PangoAttrList *pal; PangoAttribute *bg; bg = pango_attr_background_new(0xFFFF, 0xFFFF, 0xA000); pal = pango_attr_list_new(); pango_attr_list_insert(pal, bg); gtk_label_set_attributes(GTK_LABEL(widget), pal); gtk_widget_set_has_tooltip(widget, TRUE); } else { gtk_label_set_attributes(GTK_LABEL(widget), default_title_attrs); gtk_widget_set_has_tooltip(widget, FALSE); } } static gboolean title_add_multiple_are_conflicts(signal_user_data_t *ud) { GtkListBox *list; GtkWidget *row; gint count, ii; list = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "title_add_multiple_list")); count = ghb_array_len(ud->settings_array); for (ii = 0; ii < count; ii++) { row = GTK_WIDGET(gtk_list_box_get_row_at_index(list, ii)); if (!title_multiple_can_select(ud->settings_array, ii)) { title_add_multiple_set_sensitive(GTK_WIDGET(row), FALSE); return TRUE; } title_add_multiple_set_sensitive(GTK_WIDGET(row), TRUE); } return FALSE; } static void title_add_multiple_set_conflict_label( signal_user_data_t *ud, gboolean are_conflicts) { const gchar *msg; static gboolean conflict_showing = FALSE; GtkMessageType msg_type; if (are_conflicts) { msg = "" "Duplicate destination files detected.\n" "Duplicates will not be added to the queue." ""; msg_type = GTK_MESSAGE_WARNING; } else { msg = "Destination files OK. No duplicates detected."; msg_type = GTK_MESSAGE_INFO; } if (are_conflicts ^ conflict_showing) { GtkInfoBar *info; GtkContainer *content_area; GList *list; GtkLabel *label; info = GTK_INFO_BAR(GHB_WIDGET(ud->builder, "title_add_multiple_infobar")); content_area = GTK_CONTAINER(gtk_info_bar_get_content_area(info)); list = gtk_container_get_children(content_area); // Label is first in list label = GTK_LABEL(list->data); gtk_label_set_markup(label, msg); gtk_info_bar_set_message_type(info, msg_type); conflict_showing = are_conflicts; } } static void title_add_multiple_check_conflicts(signal_user_data_t *ud) { gint count, ii; GValue *settings; GtkWidget *row; GtkListBox *list; GtkToggleButton *selected; gboolean can_select; gboolean are_conflicts = FALSE; list = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "title_add_multiple_list")); count = ghb_array_len(ud->settings_array); for (ii = 0; ii < count; ii++) { row = GTK_WIDGET(gtk_list_box_get_row_at_index(list, ii)); selected = GTK_TOGGLE_BUTTON(find_widget(row, "title_selected")); settings = ghb_array_get_nth(ud->settings_array, ii); can_select = title_multiple_can_select(ud->settings_array, ii); ghb_settings_set_boolean(settings, "title_selected", FALSE); gtk_toggle_button_set_active(selected, FALSE); title_add_multiple_set_sensitive(GTK_WIDGET(row), can_select); are_conflicts |= !can_select; } title_add_multiple_set_conflict_label(ud, are_conflicts); } static gboolean clear_select_all_busy = FALSE; G_MODULE_EXPORT void title_add_multiple_select_all_cb(GtkWidget *widget, signal_user_data_t *ud) { gint count, ii; GValue *settings; GtkWidget *row; GtkListBox *list; GtkToggleButton *selected; gboolean can_select; GtkToggleButton *clear_all; GtkToggleButton *select_all; if (!ghb_widget_boolean(widget)) return; clear_select_all_busy = TRUE; clear_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder, "title_add_multiple_clear_all")); select_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder, "title_add_multiple_select_all")); gtk_toggle_button_set_active(clear_all, FALSE); gtk_widget_set_sensitive(GTK_WIDGET(select_all), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(clear_all), TRUE); list = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "title_add_multiple_list")); count = ghb_array_len(ud->settings_array); for (ii = 0; ii < count; ii++) { row = GTK_WIDGET(gtk_list_box_get_row_at_index(list, ii)); selected = GTK_TOGGLE_BUTTON(find_widget(row, "title_selected")); settings = ghb_array_get_nth(ud->settings_array, ii); can_select = title_multiple_can_select(ud->settings_array, ii); ghb_settings_set_boolean(settings, "title_selected", can_select); gtk_toggle_button_set_active(selected, TRUE); title_add_multiple_set_sensitive(GTK_WIDGET(row), can_select); } clear_select_all_busy = FALSE; } G_MODULE_EXPORT void title_add_multiple_clear_all_cb(GtkWidget *widget, signal_user_data_t *ud) { gint count, ii; GValue *settings; GtkWidget *row; GtkListBox *list; GtkToggleButton *selected; GtkToggleButton *clear_all; GtkToggleButton *select_all; if (!ghb_widget_boolean(widget)) return; clear_select_all_busy = TRUE; clear_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder, "title_add_multiple_clear_all")); select_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder, "title_add_multiple_select_all")); gtk_toggle_button_set_active(select_all, FALSE); gtk_widget_set_sensitive(GTK_WIDGET(select_all), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(clear_all), FALSE); list = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "title_add_multiple_list")); count = ghb_array_len(ud->settings_array); for (ii = 0; ii < count; ii++) { row = GTK_WIDGET(gtk_list_box_get_row_at_index(list, ii)); selected = GTK_TOGGLE_BUTTON(find_widget(row, "title_selected")); settings = ghb_array_get_nth(ud->settings_array, ii); ghb_settings_set_boolean(settings, "title_selected", FALSE); gtk_toggle_button_set_active(selected, FALSE); } clear_select_all_busy = FALSE; } static void add_multiple_titles(signal_user_data_t *ud) { gint count, ii; count = ghb_array_len(ud->settings_array); for (ii = 0; ii < count; ii++) { GValue *settings; settings = ghb_value_dup(ghb_array_get_nth(ud->settings_array, ii)); if (ghb_settings_get_boolean(settings, "title_selected")) { queue_add(ud, settings, ii); } } } static GtkListBoxRow* title_get_row(GtkWidget *widget) { while (widget != NULL && G_OBJECT_TYPE(widget) != GTK_TYPE_LIST_BOX_ROW) { widget = gtk_widget_get_parent(widget); } return GTK_LIST_BOX_ROW(widget); } G_MODULE_EXPORT void title_selected_cb(GtkWidget *widget, signal_user_data_t *ud) { GValue *settings; gboolean selected; GtkToggleButton *select_all; GtkToggleButton *clear_all; gboolean can_select; if (clear_select_all_busy) return; GtkListBoxRow * row = title_get_row(widget); if (row == NULL) return; clear_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder, "title_add_multiple_clear_all")); select_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder, "title_add_multiple_select_all")); gtk_toggle_button_set_active(select_all, FALSE); gtk_toggle_button_set_active(clear_all, FALSE); gtk_widget_set_sensitive(GTK_WIDGET(clear_all), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(select_all), TRUE); gint index = gtk_list_box_row_get_index(row); selected = ghb_widget_boolean(widget); settings = ghb_array_get_nth(ud->settings_array, index); can_select = title_multiple_can_select(ud->settings_array, index); ghb_settings_set_boolean(settings, "title_selected", selected && can_select); } G_MODULE_EXPORT void title_dest_file_cb(GtkWidget *widget, signal_user_data_t *ud) { GValue *settings; gchar *dest_file, *dest_dir, *dest; GtkListBoxRow * row = title_get_row(widget); if (row == NULL) return; gint index = gtk_list_box_row_get_index(row); dest_file = ghb_widget_string(widget); settings = ghb_array_get_nth(ud->settings_array, index); ghb_settings_set_string(settings, "dest_file", dest_file); dest_dir = ghb_settings_get_string(settings, "dest_dir"); dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file); ghb_settings_set_string(settings, "destination", dest); // Check if changing the destination file name resolves // a file name conflict. Enable selection if so. // Disable selection if it creates a confict!!! gboolean can_select; can_select = title_multiple_can_select(ud->settings_array, index); ghb_settings_set_boolean(settings, "title_selected", can_select); title_add_multiple_set_sensitive(GTK_WIDGET(row), can_select); g_free(dest_file); g_free(dest_dir); g_free(dest); title_add_multiple_set_conflict_label(ud, title_add_multiple_are_conflicts(ud)); } G_MODULE_EXPORT void title_dest_dir_cb(GtkWidget *widget, signal_user_data_t *ud) { GValue *settings; gchar *dest_file, *dest_dir, *dest; GtkListBoxRow * row = title_get_row(widget); if (row == NULL) return; gint index = gtk_list_box_row_get_index(row); dest_dir = ghb_widget_string(widget); settings = ghb_array_get_nth(ud->settings_array, index); ghb_settings_set_string(settings, "dest_dir", dest_dir); dest_file = ghb_settings_get_string(settings, "dest_file"); dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file); ghb_settings_set_string(settings, "destination", dest); // Check if changing the destination file name resolves // a file name conflict. Enable selection if so. // Disable selection if it creates a confict!!! gboolean can_select; can_select = title_multiple_can_select(ud->settings_array, index); ghb_settings_set_boolean(settings, "title_selected", can_select); title_add_multiple_set_sensitive(GTK_WIDGET(row), can_select); g_free(dest_file); g_free(dest_dir); g_free(dest); title_add_multiple_set_conflict_label(ud, title_add_multiple_are_conflicts(ud)); } GtkWidget * title_create_row(signal_user_data_t *ud) { GtkBox *hbox, *vbox_dest; GtkCheckButton *selected; GtkLabel *title; GtkEntry *dest_file; GtkFileChooserButton *dest_dir; hbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0)); gtk_box_set_spacing(hbox, 6); gtk_widget_show(GTK_WIDGET(hbox)); // Select checkbox selected = GTK_CHECK_BUTTON(gtk_check_button_new()); gtk_widget_set_tooltip_markup(GTK_WIDGET(selected), _("Select this title for adding to the queue.\n")); gtk_widget_set_valign(GTK_WIDGET(selected), GTK_ALIGN_CENTER); gtk_widget_set_name(GTK_WIDGET(selected), "title_selected"); gtk_widget_show(GTK_WIDGET(selected)); g_signal_connect(selected, "toggled", (GCallback)title_selected_cb, ud); gtk_box_pack_start(hbox, GTK_WIDGET(selected), FALSE, FALSE, 0); // Title label title = GTK_LABEL(gtk_label_new(_("No Title"))); gtk_label_set_width_chars(title, 12); gtk_widget_set_halign(GTK_WIDGET(title), GTK_ALIGN_START); gtk_widget_set_valign(GTK_WIDGET(title), GTK_ALIGN_CENTER); gtk_widget_set_name(GTK_WIDGET(title), "title_label"); gtk_widget_show(GTK_WIDGET(title)); gtk_box_pack_start(hbox, GTK_WIDGET(title), FALSE, FALSE, 0); default_title_attrs = gtk_label_get_attributes(title); gtk_widget_set_tooltip_text(GTK_WIDGET(title), _("There is another title with the same destination file name.\n" "This title will not be added to the queue unless you change\n" "the output file name.\n")); gtk_widget_set_has_tooltip(GTK_WIDGET(title), FALSE); // Destination entry and file chooser vbox_dest = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0)); //gtk_widget_set_hexpand(GTK_WIDGET(vbox_dest), TRUE); dest_file = GTK_ENTRY(gtk_entry_new()); gtk_entry_set_width_chars(dest_file, 40); gtk_widget_set_name(GTK_WIDGET(dest_file), "title_file"); //gtk_widget_set_hexpand(GTK_WIDGET(dest_file), TRUE); gtk_widget_show(GTK_WIDGET(dest_file)); g_signal_connect(dest_file, "changed", (GCallback)title_dest_file_cb, ud); gtk_box_pack_start(vbox_dest, GTK_WIDGET(dest_file), FALSE, FALSE, 0); dest_dir = GTK_FILE_CHOOSER_BUTTON( gtk_file_chooser_button_new(_("Destination Directory"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)); g_signal_connect(dest_dir, "selection-changed", (GCallback)title_dest_dir_cb, ud); gtk_widget_set_name(GTK_WIDGET(dest_dir), "title_dir"); gtk_widget_set_hexpand(GTK_WIDGET(dest_dir), TRUE); gtk_widget_show(GTK_WIDGET(dest_dir)); gtk_box_pack_start(vbox_dest, GTK_WIDGET(dest_dir), FALSE, FALSE, 0); gtk_widget_show(GTK_WIDGET(vbox_dest)); gtk_box_pack_start(hbox, GTK_WIDGET(vbox_dest), TRUE, TRUE, 0); return GTK_WIDGET(hbox); } G_MODULE_EXPORT void queue_add_multiple_clicked_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("queue_add_multiple_clicked_cb ()"); GtkListBox *list; GtkWidget *row; gint count, ii; int max_title_len = 0; list = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "title_add_multiple_list")); // Set up the list of titles count = ghb_array_len(ud->settings_array); for (ii = 0; ii < count; ii++) { GValue *settings; GtkLabel *label; GtkEntry *entry; GtkFileChooser *chooser; gchar *title_label, *dest_dir, *dest_file; int title_id, titleindex; const hb_title_t *title; row = title_create_row(ud); label = GTK_LABEL(find_widget(row, "title_label")); entry = GTK_ENTRY(find_widget(row, "title_file")); chooser = GTK_FILE_CHOOSER(find_widget(row, "title_dir")); settings = ghb_array_get_nth(ud->settings_array, ii); title_id = ghb_settings_get_int(settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (title != NULL) { int len; title_label = ghb_create_title_label(title); len = strnlen(title_label, PATH_MAX); if (len > max_title_len) max_title_len = len; dest_file = ghb_settings_get_string(settings, "dest_file"); dest_dir = ghb_settings_get_string(settings, "dest_dir"); gtk_label_set_markup(label, title_label); gtk_entry_set_text(entry, dest_file); gtk_file_chooser_set_filename(chooser, dest_dir); g_free(title_label); g_free(dest_file); g_free(dest_dir); } gtk_list_box_insert(list, row, -1); } // Now we need to set the width of the title label since it // can vary on each row if (max_title_len > 60) max_title_len = 60; for (ii = 0; ii < count; ii++) { GtkWidget *row; GtkLabel *label; row = GTK_WIDGET(gtk_list_box_get_row_at_index(list, ii)); label = GTK_LABEL(find_widget(row, "title_label")); gtk_label_set_max_width_chars(label, max_title_len); gtk_label_set_width_chars(label, max_title_len); gtk_label_set_ellipsize(label, PANGO_ELLIPSIZE_END); } // Clear "select all" and "clear all" options GtkToggleButton *select_all; GtkToggleButton *clear_all; clear_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder, "title_add_multiple_clear_all")); select_all = GTK_TOGGLE_BUTTON(GHB_WIDGET(ud->builder, "title_add_multiple_select_all")); gtk_toggle_button_set_active(clear_all, FALSE); gtk_toggle_button_set_active(select_all, FALSE); gtk_widget_set_sensitive(GTK_WIDGET(select_all), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(clear_all), TRUE); // Disable selection of files with duplicate file names. title_add_multiple_check_conflicts(ud); // Pop up the title multiple selections dialog GtkResponseType response; GtkWidget *dialog = GHB_WIDGET(ud->builder, "titla_add_multiple_dialog"); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); if (response == GTK_RESPONSE_OK) { add_multiple_titles(ud); } // Clear title list ghb_container_empty(GTK_CONTAINER(list)); } G_MODULE_EXPORT void queue_remove_clicked_cb(GtkWidget *widget, gchar *path, signal_user_data_t *ud) { GtkTreeView *treeview; GtkTreePath *treepath; GtkTreeModel *store; GtkTreeIter iter; gint row; gint *indices; gint unique_id; GValue *settings; gint status; g_debug("queue_remove_clicked_cb ()"); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list")); store = gtk_tree_view_get_model(treeview); treepath = gtk_tree_path_new_from_string (path); if (gtk_tree_path_get_depth(treepath) > 1) return; if (gtk_tree_model_get_iter(store, &iter, treepath)) { // Find the entry in the queue indices = gtk_tree_path_get_indices (treepath); row = indices[0]; // Can only free the treepath After getting what I need from // indices since this points into treepath somewhere. gtk_tree_path_free (treepath); if (row < 0) return; if (row >= ghb_array_len(ud->queue)) return; settings = ghb_array_get_nth(ud->queue, row); status = ghb_settings_get_int(settings, "job_status"); if (status == GHB_QUEUE_RUNNING) { // Ask if wants to stop encode. if (!ghb_cancel_encode2(ud, NULL)) { return; } unique_id = ghb_settings_get_int(settings, "job_unique_id"); ghb_remove_job(unique_id); } // Remove the selected item gtk_tree_store_remove(GTK_TREE_STORE(store), &iter); // Remove the corresponding item from the queue list GValue *old = ghb_array_get_nth(ud->queue, row); ghb_array_remove(ud->queue, row); ghb_value_free(old); ghb_save_queue(ud->queue); } else { gtk_tree_path_free (treepath); } ghb_update_pending(ud); } static gint find_last_finished(GValue *queue) { GValue *js; gint ii, count; gint status; g_debug("find_last_finished"); count = ghb_array_len(queue); for (ii = 0; ii < count; ii++) { js = ghb_array_get_nth(queue, ii); status = ghb_settings_get_int(js, "job_status"); if (status != GHB_QUEUE_DONE && status != GHB_QUEUE_RUNNING) { return ii-1; } } return -1; } // This little bit is needed to prevent the default drag motion // handler from expanding rows if you hover over them while // dragging. // Also controls where valid drop locations are G_MODULE_EXPORT gboolean queue_drag_motion_cb( GtkTreeView *tv, GdkDragContext *ctx, gint x, gint y, guint time, signal_user_data_t *ud) { GtkTreePath *path = NULL; GtkTreeViewDropPosition pos; gint *indices, row, status, finished; GValue *js; GtkTreeIter iter; GtkTreeView *srctv; GtkTreeModel *model; GtkTreeSelection *select; GtkWidget *widget; widget = gtk_drag_get_source_widget(ctx); if (widget == NULL || widget != GTK_WIDGET(tv)) return TRUE; // This bit checks to see if the source is allowed to be // moved. Only pending and canceled items may be moved. srctv = GTK_TREE_VIEW(gtk_drag_get_source_widget(ctx)); select = gtk_tree_view_get_selection (srctv); gtk_tree_selection_get_selected (select, &model, &iter); path = gtk_tree_model_get_path (model, &iter); indices = gtk_tree_path_get_indices(path); row = indices[0]; gtk_tree_path_free(path); js = ghb_array_get_nth(ud->queue, row); status = ghb_settings_get_int(js, "job_status"); if (status != GHB_QUEUE_PENDING && status != GHB_QUEUE_CANCELED) { gdk_drag_status(ctx, 0, time); return TRUE; } // The reset checks that the destination is a valid position // in the list. Can not move above any finished or running items gtk_tree_view_get_dest_row_at_pos (tv, x, y, &path, &pos); if (path == NULL) { gdk_drag_status(ctx, GDK_ACTION_MOVE, time); return TRUE; } // Don't allow *drop into* if (pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE) pos = GTK_TREE_VIEW_DROP_BEFORE; if (pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) pos = GTK_TREE_VIEW_DROP_AFTER; // Don't allow droping int child items if (gtk_tree_path_get_depth(path) > 1) { gtk_tree_path_up(path); pos = GTK_TREE_VIEW_DROP_AFTER; } indices = gtk_tree_path_get_indices(path); row = indices[0]; js = ghb_array_get_nth(ud->queue, row); finished = find_last_finished(ud->queue); if (row < finished) { gtk_tree_path_free(path); gdk_drag_status(ctx, 0, time); return TRUE; } if (pos != GTK_TREE_VIEW_DROP_AFTER && row == finished) { gtk_tree_path_free(path); gdk_drag_status(ctx, 0, time); return TRUE; } gtk_tree_view_set_drag_dest_row(tv, path, pos); gtk_tree_path_free(path); gdk_drag_status(ctx, GDK_ACTION_MOVE, time); return TRUE; } G_MODULE_EXPORT void queue_drag_cb( GtkTreeView *dstwidget, GdkDragContext *dc, gint x, gint y, GtkSelectionData *selection_data, guint info, guint t, signal_user_data_t *ud) { GtkTreePath *path = NULL; //GtkTreeModel *model; GtkTreeViewDropPosition pos; GtkTreeIter dstiter, srciter; gint *indices, row; GValue *js; GtkTreeModel *dstmodel = gtk_tree_view_get_model(dstwidget); g_debug("queue_drag_cb ()"); // This doesn't work here for some reason... // gtk_tree_view_get_drag_dest_row(dstwidget, &path, &pos); gtk_tree_view_get_dest_row_at_pos (dstwidget, x, y, &path, &pos); // This little hack is needed because attempting to drop after // the last item gives us no path or pos. if (path == NULL) { gint n_children; n_children = gtk_tree_model_iter_n_children(dstmodel, NULL); if (n_children) { pos = GTK_TREE_VIEW_DROP_AFTER; path = gtk_tree_path_new_from_indices(n_children-1, -1); } else { pos = GTK_TREE_VIEW_DROP_BEFORE; path = gtk_tree_path_new_from_indices(0, -1); } } if (path) { if (gtk_tree_path_get_depth(path) > 1) gtk_tree_path_up(path); if (gtk_tree_model_get_iter (dstmodel, &dstiter, path)) { GtkTreeIter iter; GtkTreeView *srcwidget; GtkTreeModel *srcmodel; GtkTreeSelection *select; GtkTreePath *srcpath = NULL; GtkTreePath *dstpath = NULL; srcwidget = GTK_TREE_VIEW(gtk_drag_get_source_widget(dc)); //srcmodel = gtk_tree_view_get_model(srcwidget); select = gtk_tree_view_get_selection (srcwidget); gtk_tree_selection_get_selected (select, &srcmodel, &srciter); srcpath = gtk_tree_model_get_path (srcmodel, &srciter); indices = gtk_tree_path_get_indices(srcpath); row = indices[0]; gtk_tree_path_free(srcpath); js = ghb_array_get_nth(ud->queue, row); switch (pos) { case GTK_TREE_VIEW_DROP_BEFORE: case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: gtk_tree_store_insert_before (GTK_TREE_STORE (dstmodel), &iter, NULL, &dstiter); break; case GTK_TREE_VIEW_DROP_AFTER: case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: gtk_tree_store_insert_after (GTK_TREE_STORE (dstmodel), &iter, NULL, &dstiter); break; default: break; } // Reset job to pending ghb_settings_set_int(js, "job_status", GHB_QUEUE_PENDING); add_to_queue_list(ud, js, &iter); dstpath = gtk_tree_model_get_path (dstmodel, &iter); indices = gtk_tree_path_get_indices(dstpath); row = indices[0]; gtk_tree_path_free(dstpath); ghb_array_insert(ud->queue, row, js); srcpath = gtk_tree_model_get_path (srcmodel, &srciter); indices = gtk_tree_path_get_indices(srcpath); row = indices[0]; gtk_tree_path_free(srcpath); ghb_array_remove(ud->queue, row); gtk_tree_store_remove (GTK_TREE_STORE (srcmodel), &srciter); ghb_save_queue(ud->queue); } gtk_tree_path_free(path); } } void ghb_queue_buttons_grey(signal_user_data_t *ud) { GtkWidget *widget; gint queue_count; gint title_id, titleindex; const hb_title_t *title; gint queue_state, scan_state; gboolean show_start, show_stop, paused; queue_count = ghb_array_len(ud->queue); title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); queue_state = ghb_get_queue_state(); scan_state = ghb_get_scan_state(); show_stop = queue_state & (GHB_STATE_WORKING | GHB_STATE_SEARCHING | GHB_STATE_SCANNING | GHB_STATE_MUXING); show_start = !(scan_state & GHB_STATE_SCANNING) && (title !=NULL || queue_count > 0); paused = queue_state & GHB_STATE_PAUSED; widget = GHB_WIDGET(ud->builder, "queue_add"); gtk_widget_set_sensitive(widget, show_start); widget = GHB_WIDGET(ud->builder, "queue_add_menu"); gtk_widget_set_sensitive(widget, show_start); widget = GHB_WIDGET(ud->builder, "queue_add_multiple_menu"); gtk_widget_set_sensitive(widget, show_start); widget = GHB_WIDGET (ud->builder, "queue_start1"); if (show_stop) { gtk_widget_set_sensitive (widget, TRUE); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-stop"); gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Stop")); gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Stop Encoding")); } else { gtk_widget_set_sensitive (widget, show_start); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-start"); gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Start")); gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Start Encoding")); } widget = GHB_WIDGET (ud->builder, "queue_pause1"); if (paused) { gtk_widget_set_sensitive (widget, show_stop); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-start"); gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Resume")); gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Resume Encoding")); } else { gtk_widget_set_sensitive (widget, show_stop); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-pause"); gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Pause")); gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Pause Encoding")); } widget = GHB_WIDGET (ud->builder, "queue_start_menu"); if (show_stop) { gtk_widget_set_sensitive (widget, TRUE); gtk_menu_item_set_label(GTK_MENU_ITEM(widget), _("S_top Queue")); gtk_widget_set_tooltip_text(widget, _("Stop Encoding")); } else { gtk_widget_set_sensitive (widget, show_start); gtk_menu_item_set_label(GTK_MENU_ITEM(widget), _("_Start Queue")); gtk_widget_set_tooltip_text(widget, _("Start Encoding")); } widget = GHB_WIDGET (ud->builder, "queue_pause_menu"); if (paused) { gtk_widget_set_sensitive (widget, show_start); gtk_menu_item_set_label(GTK_MENU_ITEM(widget), _("_Resume Queue")); gtk_widget_set_tooltip_text(widget, _("Resume Encoding")); } else { gtk_widget_set_sensitive (widget, show_stop); gtk_menu_item_set_label(GTK_MENU_ITEM(widget), _("_Pause Queue")); gtk_widget_set_tooltip_text(widget, _("Pause Encoding")); } } G_MODULE_EXPORT void queue_list_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, GtkCellRenderer *cell) { GtkTreeViewColumn *column; gint width; column = gtk_tree_view_get_column (GTK_TREE_VIEW(widget), 0); width = gtk_tree_view_column_get_width(column); g_debug("col width %d alloc width %d", width, allocation->width); // Set new wrap-width. Shave a little off to accomidate the icons // that share this column. if (width >= 564) // Don't allow below a certain size g_object_set(cell, "wrap-width", width-70, NULL); } G_MODULE_EXPORT void queue_start_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GValue *js; gboolean running = FALSE; gint count, ii; gint status; gint state; state = ghb_get_queue_state(); if (state & (GHB_STATE_WORKING | GHB_STATE_SEARCHING | GHB_STATE_SCANNING | GHB_STATE_MUXING)) { ghb_cancel_encode(ud, _("You are currently encoding. " "What would you like to do?\n\n")); return; } count = ghb_array_len(ud->queue); for (ii = 0; ii < count; ii++) { js = ghb_array_get_nth(ud->queue, ii); status = ghb_settings_get_int(js, "job_status"); if ((status == GHB_QUEUE_RUNNING) || (status == GHB_QUEUE_PENDING)) { running = TRUE; break; } } if (!running) { // The queue has no running or pending jobs. // Add current settings to the queue, then run. GValue *settings = ghb_value_dup(ud->settings); if (!queue_add(ud, settings, 0)) { ghb_value_free(settings); return; } // Validation of settings may have changed audio list ghb_audio_list_refresh_all(ud); } if (state == GHB_STATE_IDLE) { // Add the first pending queue item and start ud->current_job = ghb_start_next_job(ud); } } G_MODULE_EXPORT void queue_pause_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { ghb_pause_queue(); } gboolean ghb_reload_queue(signal_user_data_t *ud) { GValue *queue; gint unfinished = 0; gint count, ii; gint pid; gint status; GValue *settings; gchar *message; g_debug("ghb_reload_queue"); find_pid: pid = ghb_find_pid_file(); if (pid < 0) return FALSE; queue = ghb_load_old_queue(pid); ghb_remove_old_queue_file(pid); // Look for unfinished entries count = ghb_array_len(queue); for (ii = 0; ii < count; ii++) { settings = ghb_array_get_nth(queue, ii); status = ghb_settings_get_int(settings, "job_status"); if (status != GHB_QUEUE_DONE && status != GHB_QUEUE_CANCELED) { unfinished++; } } if (!unfinished) goto find_pid; if (unfinished) { message = g_strdup_printf( _("You have %d unfinished job(s) in a saved queue.\n\n" "Would you like to reload them?"), unfinished); if (ghb_message_dialog(GTK_MESSAGE_QUESTION, message, _("No"), _("Yes"))) { GtkWidget *widget = GHB_WIDGET(ud->builder, "show_queue"); gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), TRUE); ud->queue = queue; // First get rid of any old items we don't want for (ii = count-1; ii >= 0; ii--) { settings = ghb_array_get_nth(queue, ii); status = ghb_settings_get_int(settings, "job_status"); if (status == GHB_QUEUE_DONE || status == GHB_QUEUE_CANCELED) { GValue *old = ghb_array_get_nth(queue, ii); ghb_value_free(old); ghb_array_remove(queue, ii); } } count = ghb_array_len(queue); for (ii = 0; ii < count; ii++) { settings = ghb_array_get_nth(queue, ii); ghb_settings_set_int(settings, "job_unique_id", 0); ghb_settings_set_int(settings, "job_status", GHB_QUEUE_PENDING); add_to_queue_list(ud, settings, NULL); } ghb_queue_buttons_grey(ud); ghb_save_queue(ud->queue); } else { ghb_value_free(queue); } g_free(message); } else { ghb_value_free(queue); } return FALSE; } void ghb_queue_remove_row(signal_user_data_t *ud, int row) { GtkTreeView *treeview; GtkTreeModel *store; GtkTreeIter iter; if (row < 0) return; if (row >= ghb_array_len(ud->queue)) return; treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list")); store = gtk_tree_view_get_model(treeview); gchar *path = g_strdup_printf ("%d", row); if (gtk_tree_model_get_iter_from_string(store, &iter, path)) { gtk_tree_store_remove(GTK_TREE_STORE(store), &iter); } g_free(path); GValue *old = ghb_array_get_nth(ud->queue, row); ghb_value_free(old); ghb_array_remove(ud->queue, row); ghb_save_queue(ud->queue); } G_MODULE_EXPORT gboolean queue_key_press_cb( GtkWidget *widget, GdkEventKey *event, signal_user_data_t *ud) { GtkTreeView *treeview; GtkTreeSelection *selection; GtkTreeModel *store; GtkTreeIter iter; gint row; gint *indices; gint unique_id; GValue *settings; gint status; g_debug("queue_key_press_cb ()"); if (event->keyval != GDK_KEY_Delete) return FALSE; treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list")); store = gtk_tree_view_get_model(treeview); selection = gtk_tree_view_get_selection (treeview); if (gtk_tree_selection_get_selected(selection, &store, &iter)) { GtkTreePath *treepath; treepath = gtk_tree_model_get_path (store, &iter); // Find the entry in the queue indices = gtk_tree_path_get_indices (treepath); row = indices[0]; // Can only free the treepath After getting what I need from // indices since this points into treepath somewhere. gtk_tree_path_free (treepath); if (row < 0) return FALSE; if (row >= ghb_array_len(ud->queue)) return FALSE; settings = ghb_array_get_nth(ud->queue, row); status = ghb_settings_get_int(settings, "job_status"); if (status == GHB_QUEUE_RUNNING) { // Ask if wants to stop encode. if (!ghb_cancel_encode2(ud, NULL)) { return TRUE; } unique_id = ghb_settings_get_int(settings, "job_unique_id"); ghb_remove_job(unique_id); } // Remove the selected item gtk_tree_store_remove(GTK_TREE_STORE(store), &iter); // Remove the corresponding item from the queue list GValue *old = ghb_array_get_nth(ud->queue, row); ghb_value_free(old); ghb_array_remove(ud->queue, row); ghb_save_queue(ud->queue); return TRUE; } return FALSE; } GValue *ghb_queue_edit_settings = NULL; G_MODULE_EXPORT void queue_edit_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GtkTreeView *treeview; GtkTreeSelection *selection; GtkTreeModel *store; GtkTreeIter iter; gint row; gint *indices; gint status; g_debug("queue_key_press_cb ()"); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list")); store = gtk_tree_view_get_model(treeview); selection = gtk_tree_view_get_selection (treeview); if (gtk_tree_selection_get_selected(selection, &store, &iter)) { GtkTreePath *treepath; treepath = gtk_tree_model_get_path (store, &iter); // Find the entry in the queue indices = gtk_tree_path_get_indices (treepath); row = indices[0]; // Can only free the treepath After getting what I need from // indices since this points into treepath somewhere. gtk_tree_path_free (treepath); if (row < 0) return; if (row >= ghb_array_len(ud->queue)) return; ghb_queue_edit_settings = ghb_array_get_nth(ud->queue, row); status = ghb_settings_get_int(ghb_queue_edit_settings, "job_status"); if (status == GHB_QUEUE_PENDING) { // Remove the selected item gtk_tree_store_remove(GTK_TREE_STORE(store), &iter); // Remove the corresponding item from the queue list ghb_array_remove(ud->queue, row); } else { ghb_queue_edit_settings = ghb_value_dup(ghb_queue_edit_settings); } gchar *source; source = ghb_settings_get_string(ghb_queue_edit_settings, "source"); ghb_do_scan(ud, source, 0, FALSE); g_free(source); GtkWidget *widget = GHB_WIDGET(ud->builder, "show_queue"); gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE); } } HandBrake-0.10.2/gtk/src/hb-source.svg0000664000175200017520000001525112311644756020040 0ustar handbrakehandbrake image/svg+xml HandBrake-0.10.2/gtk/src/marshalers.h0000664000175200017520000000254412067154516017741 0ustar handbrakehandbrake #ifndef __ghb_marshal_MARSHAL_H__ #define __ghb_marshal_MARSHAL_H__ #include G_BEGIN_DECLS /* VOID:STRING (/home/jstebbins/Source/hb/HandBrake/build.dbg/../gtk/src/marshalers.list:1) */ #define ghb_marshal_VOID__STRING g_cclosure_marshal_VOID__STRING /* VOID:STRING,STRING (/home/jstebbins/Source/hb/HandBrake/build.dbg/../gtk/src/marshalers.list:2) */ extern void ghb_marshal_VOID__STRING_STRING (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data); /* BOOLEAN:BOXED (/home/jstebbins/Source/hb/HandBrake/build.dbg/../gtk/src/marshalers.list:3) */ extern void ghb_marshal_BOOLEAN__BOXED (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data); G_END_DECLS #endif /* __ghb_marshal_MARSHAL_H__ */ HandBrake-0.10.2/gtk/src/marshalers.list0000664000175200017520000000005511542231617020453 0ustar handbrakehandbrakeVOID:STRING VOID:STRING,STRING BOOLEAN:BOXED HandBrake-0.10.2/gtk/src/marshalers.c0000664000175200017520000001307112067154516017731 0ustar handbrakehandbrake #include #ifdef G_ENABLE_DEBUG #define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) #define g_marshal_value_peek_char(v) g_value_get_char (v) #define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) #define g_marshal_value_peek_int(v) g_value_get_int (v) #define g_marshal_value_peek_uint(v) g_value_get_uint (v) #define g_marshal_value_peek_long(v) g_value_get_long (v) #define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) #define g_marshal_value_peek_int64(v) g_value_get_int64 (v) #define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) #define g_marshal_value_peek_enum(v) g_value_get_enum (v) #define g_marshal_value_peek_flags(v) g_value_get_flags (v) #define g_marshal_value_peek_float(v) g_value_get_float (v) #define g_marshal_value_peek_double(v) g_value_get_double (v) #define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) #define g_marshal_value_peek_param(v) g_value_get_param (v) #define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) #define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) #define g_marshal_value_peek_object(v) g_value_get_object (v) #define g_marshal_value_peek_variant(v) g_value_get_variant (v) #else /* !G_ENABLE_DEBUG */ /* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. * Do not access GValues directly in your code. Instead, use the * g_value_get_*() functions */ #define g_marshal_value_peek_boolean(v) (v)->data[0].v_int #define g_marshal_value_peek_char(v) (v)->data[0].v_int #define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint #define g_marshal_value_peek_int(v) (v)->data[0].v_int #define g_marshal_value_peek_uint(v) (v)->data[0].v_uint #define g_marshal_value_peek_long(v) (v)->data[0].v_long #define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong #define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 #define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 #define g_marshal_value_peek_enum(v) (v)->data[0].v_long #define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong #define g_marshal_value_peek_float(v) (v)->data[0].v_float #define g_marshal_value_peek_double(v) (v)->data[0].v_double #define g_marshal_value_peek_string(v) (v)->data[0].v_pointer #define g_marshal_value_peek_param(v) (v)->data[0].v_pointer #define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer #define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer #define g_marshal_value_peek_object(v) (v)->data[0].v_pointer #define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer #endif /* !G_ENABLE_DEBUG */ /* VOID:STRING (/home/jstebbins/Source/hb/HandBrake/build.dbg/../gtk/src/marshalers.list:1) */ /* VOID:STRING,STRING (/home/jstebbins/Source/hb/HandBrake/build.dbg/../gtk/src/marshalers.list:2) */ void ghb_marshal_VOID__STRING_STRING (GClosure *closure, GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef void (*GMarshalFunc_VOID__STRING_STRING) (gpointer data1, gpointer arg_1, gpointer arg_2, gpointer data2); register GMarshalFunc_VOID__STRING_STRING callback; register GCClosure *cc = (GCClosure*) closure; register gpointer data1, data2; g_return_if_fail (n_param_values == 3); if (G_CCLOSURE_SWAP_DATA (closure)) { data1 = closure->data; data2 = g_value_peek_pointer (param_values + 0); } else { data1 = g_value_peek_pointer (param_values + 0); data2 = closure->data; } callback = (GMarshalFunc_VOID__STRING_STRING) (marshal_data ? marshal_data : cc->callback); callback (data1, g_marshal_value_peek_string (param_values + 1), g_marshal_value_peek_string (param_values + 2), data2); } /* BOOLEAN:BOXED (/home/jstebbins/Source/hb/HandBrake/build.dbg/../gtk/src/marshalers.list:3) */ void ghb_marshal_BOOLEAN__BOXED (GClosure *closure, GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED) (gpointer data1, gpointer arg_1, gpointer data2); register GMarshalFunc_BOOLEAN__BOXED callback; register GCClosure *cc = (GCClosure*) closure; register gpointer data1, data2; gboolean v_return; g_return_if_fail (return_value != NULL); g_return_if_fail (n_param_values == 2); if (G_CCLOSURE_SWAP_DATA (closure)) { data1 = closure->data; data2 = g_value_peek_pointer (param_values + 0); } else { data1 = g_value_peek_pointer (param_values + 0); data2 = closure->data; } callback = (GMarshalFunc_BOOLEAN__BOXED) (marshal_data ? marshal_data : cc->callback); v_return = callback (data1, g_marshal_value_peek_boxed (param_values + 1), data2); g_value_set_boolean (return_value, v_return); } HandBrake-0.10.2/gtk/src/audiohandler.c0000664000175200017520000024015312530646764020240 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * audiohandler.c * Copyright (C) John Stebbins 2008-2015 * * audiohandler.c is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. */ #include #include "ghbcompat.h" #include "hb.h" #include "settings.h" #include "hb-backend.h" #include "values.h" #include "callbacks.h" #include "preview.h" #include "audiohandler.h" #include "presets.h" static void audio_add_to_settings(GValue *settings, GValue *asettings); static void ghb_add_audio_to_ui(signal_user_data_t *ud, const GValue *settings); static GValue* audio_get_selected_settings(signal_user_data_t *ud, int *index); static void ghb_clear_audio_list_settings(GValue *settings); static void ghb_clear_audio_list_ui(GtkBuilder *builder); static gboolean block_updates = FALSE; static void enable_quality_widget(signal_user_data_t *ud, int acodec) { GtkWidget *widget1, *widget2; widget1 = GHB_WIDGET(ud->builder, "AudioTrackQualityEnable"); widget2 = GHB_WIDGET(ud->builder, "AudioTrackBitrateEnable"); if (hb_audio_quality_get_default(acodec) == HB_INVALID_AUDIO_QUALITY) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget2), TRUE); gtk_widget_set_sensitive(widget1, FALSE); } else { gtk_widget_set_sensitive(widget1, TRUE); } } static void audio_deps(signal_user_data_t *ud, GValue *asettings, GtkWidget *widget) { ghb_adjust_audio_rate_combos(ud); ghb_grey_combo_options (ud); if (widget != NULL) ghb_check_dependency(ud, widget, NULL); gint track = -1, codec = 0; hb_audio_config_t *aconfig = NULL; int title_id; gint titleindex; const hb_title_t *title; title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (asettings != NULL) { track = ghb_settings_get_int(asettings, "AudioTrack"); codec = ghb_settings_audio_encoder_codec(asettings, "AudioEncoder"); aconfig = ghb_get_audio_info(title, track); } gboolean is_passthru = (codec & HB_ACODEC_PASS_FLAG); gboolean enable_drc = TRUE; if (aconfig != NULL) { enable_drc = hb_audio_can_apply_drc(aconfig->in.codec, aconfig->in.codec_param, codec) && !is_passthru; } widget = GHB_WIDGET(ud->builder, "AudioTrackDRCSlider"); gtk_widget_set_sensitive(widget, enable_drc); widget = GHB_WIDGET(ud->builder, "AudioTrackDRCSliderLabel"); gtk_widget_set_sensitive(widget, enable_drc); widget = GHB_WIDGET(ud->builder, "AudioTrackDRCValue"); gtk_widget_set_sensitive(widget, enable_drc); enable_quality_widget(ud, codec); widget = GHB_WIDGET(ud->builder, "AudioBitrate"); gtk_widget_set_sensitive(widget, !is_passthru); widget = GHB_WIDGET(ud->builder, "AudioTrackQualityEnableBox"); gtk_widget_set_sensitive(widget, !is_passthru); widget = GHB_WIDGET(ud->builder, "AudioTrackQualityBox"); gtk_widget_set_sensitive(widget, !is_passthru); widget = GHB_WIDGET(ud->builder, "AudioMixdown"); gtk_widget_set_sensitive(widget, !is_passthru); widget = GHB_WIDGET(ud->builder, "AudioSamplerate"); gtk_widget_set_sensitive(widget, !is_passthru); widget = GHB_WIDGET(ud->builder, "AudioTrackGainSlider"); gtk_widget_set_sensitive(widget, !is_passthru); widget = GHB_WIDGET(ud->builder, "AudioTrackGainValue"); gtk_widget_set_sensitive(widget, !is_passthru); } gint ghb_select_audio_codec(gint mux, hb_audio_config_t *aconfig, gint acodec, gint fallback, gint copy_mask) { guint32 in_codec = aconfig != NULL ? aconfig->in.codec : 0; if (acodec == HB_ACODEC_AUTO_PASS) { return hb_autopassthru_get_encoder(in_codec, copy_mask, fallback, mux); } // Sanitize fallback const hb_encoder_t *enc; for (enc = hb_audio_encoder_get_next(NULL); enc != NULL; enc = hb_audio_encoder_get_next(enc)) { if (enc->codec == fallback && !(enc->muxers & mux)) { if ( mux & HB_MUX_MASK_MKV ) fallback = HB_ACODEC_LAME; else fallback = HB_ACODEC_FFAAC; break; } } if ((acodec & HB_ACODEC_PASS_FLAG) && !(acodec & in_codec & HB_ACODEC_PASS_MASK)) { return fallback; } for (enc = hb_audio_encoder_get_next(NULL); enc != NULL; enc = hb_audio_encoder_get_next(enc)) { if (enc->codec == acodec && !(enc->muxers & mux)) { return fallback; } } return acodec; } int ghb_get_copy_mask(GValue *settings) { gint mask = 0; if (ghb_settings_get_boolean(settings, "AudioAllowMP3Pass")) { mask |= HB_ACODEC_MP3; } if (ghb_settings_get_boolean(settings, "AudioAllowAACPass")) { mask |= HB_ACODEC_FFAAC; } if (ghb_settings_get_boolean(settings, "AudioAllowAC3Pass")) { mask |= HB_ACODEC_AC3; } if (ghb_settings_get_boolean(settings, "AudioAllowDTSPass")) { mask |= HB_ACODEC_DCA; } if (ghb_settings_get_boolean(settings, "AudioAllowDTSHDPass")) { mask |= HB_ACODEC_DCA_HD; } return mask; } int ghb_select_fallback(GValue *settings, int acodec) { gint fallback = 0; switch ( acodec ) { case HB_ACODEC_MP3_PASS: return HB_ACODEC_LAME; case HB_ACODEC_AAC_PASS: return HB_ACODEC_FFAAC; case HB_ACODEC_AC3_PASS: return HB_ACODEC_AC3; default: { const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); fallback = ghb_settings_audio_encoder_codec(settings, "AudioEncoderFallback"); return hb_autopassthru_get_encoder(acodec, 0, fallback, mux->format); } } } void audio_sanitize_settings(GValue *settings, GValue *asettings) { int title_id; gint titleindex, track, acodec, select_acodec, mix; const hb_title_t *title; hb_audio_config_t *aconfig; gint bitrate; gint sr; const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); title_id = ghb_settings_get_int(settings, "title"); title = ghb_lookup_title(title_id, &titleindex); track = ghb_settings_get_int(asettings, "AudioTrack"); acodec = ghb_settings_audio_encoder_codec(asettings, "AudioEncoder"); mix = ghb_settings_mixdown_mix(asettings, "AudioMixdown"); bitrate = ghb_settings_audio_bitrate_rate(asettings, "AudioBitrate"); sr = ghb_settings_audio_samplerate_rate(asettings, "AudioSamplerate"); aconfig = ghb_get_audio_info(title, track); if (sr == 0) { sr = aconfig ? aconfig->in.samplerate : 48000; } gint fallback = ghb_select_fallback(settings, acodec); gint copy_mask = ghb_get_copy_mask(settings); select_acodec = ghb_select_audio_codec(mux->format, aconfig, acodec, fallback, copy_mask); if (ghb_audio_is_passthru (select_acodec)) { if (aconfig) { bitrate = aconfig->in.bitrate / 1000; // Set the values for bitrate and samplerate to the input rates mix = HB_AMIXDOWN_NONE; ghb_settings_set_string(asettings, "AudioMixdown", hb_mixdown_get_short_name(mix)); select_acodec &= aconfig->in.codec | HB_ACODEC_PASS_FLAG; ghb_settings_set_string(asettings, "AudioSamplerate", ghb_audio_samplerate_get_short_name(0)); } else { mix = HB_AMIXDOWN_NONE; ghb_settings_set_string(asettings, "AudioMixdown", hb_mixdown_get_short_name(mix)); ghb_settings_set_string(asettings, "AudioSamplerate", ghb_audio_samplerate_get_short_name(0)); bitrate = 448; } ghb_settings_set_double(asettings, "AudioTrackDRCSlider", 0.0); } else { if (mix == HB_AMIXDOWN_NONE) mix = ghb_get_best_mix(aconfig, select_acodec, mix); bitrate = hb_audio_bitrate_get_best(select_acodec, bitrate, sr, mix); ghb_settings_set_string(asettings, "AudioMixdown", hb_mixdown_get_short_name(mix)); } ghb_settings_set_string(asettings, "AudioBitrate", ghb_audio_bitrate_get_short_name(bitrate)); ghb_settings_set_string(asettings, "AudioEncoder", hb_audio_encoder_get_short_name(select_acodec)); } void ghb_adjust_audio_rate_combos(signal_user_data_t *ud) { int title_id, titleindex; const hb_title_t *title; gint track, acodec, select_acodec, mix; hb_audio_config_t *aconfig; gint bitrate; gint sr = 48000; const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(ud->settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); track = ghb_settings_get_int(ud->settings, "AudioTrack"); acodec = ghb_settings_audio_encoder_codec(ud->settings, "AudioEncoder"); mix = ghb_settings_mixdown_mix(ud->settings, "AudioMixdown"); bitrate = ghb_settings_audio_bitrate_rate(ud->settings, "AudioBitrate"); sr = ghb_settings_audio_samplerate_rate(ud->settings, "AudioSamplerate"); aconfig = ghb_get_audio_info(title, track); if (sr == 0) { sr = aconfig ? aconfig->in.samplerate : 48000; } gint fallback = ghb_select_fallback(ud->settings, acodec); gint copy_mask = ghb_get_copy_mask(ud->settings); select_acodec = ghb_select_audio_codec(mux->format, aconfig, acodec, fallback, copy_mask); gboolean codec_defined_bitrate = FALSE; if (ghb_audio_is_passthru (select_acodec)) { if (aconfig) { bitrate = aconfig->in.bitrate / 1000; // Set the values for bitrate and samplerate to the input rates ghb_set_bitrate_opts (ud->builder, bitrate, bitrate, bitrate); mix = HB_AMIXDOWN_NONE; ghb_ui_update(ud, "AudioMixdown", ghb_string_value(hb_mixdown_get_short_name(mix))); select_acodec &= aconfig->in.codec | HB_ACODEC_PASS_FLAG; codec_defined_bitrate = TRUE; ghb_ui_update(ud, "AudioSamplerate", ghb_string_value( ghb_audio_samplerate_get_short_name(0))); } else { ghb_ui_update(ud, "AudioSamplerate", ghb_string_value( ghb_audio_samplerate_get_short_name(0))); mix = HB_AMIXDOWN_NONE; ghb_ui_update(ud, "AudioMixdown", ghb_string_value(hb_mixdown_get_short_name(mix))); bitrate = 448; } ghb_ui_update(ud, "AudioTrackDRCSlider", ghb_double_value(0)); } else { if (mix == HB_AMIXDOWN_NONE) mix = ghb_get_best_mix( aconfig, select_acodec, mix); bitrate = hb_audio_bitrate_get_best(select_acodec, bitrate, sr, mix); ghb_ui_update(ud, "AudioMixdown", ghb_string_value(hb_mixdown_get_short_name(mix))); } if (!codec_defined_bitrate) { int low, high; mix = ghb_get_best_mix( aconfig, select_acodec, mix); hb_audio_bitrate_get_limits(select_acodec, sr, mix, &low, &high); ghb_set_bitrate_opts(ud->builder, low, high, -1); } ghb_ui_update(ud, "AudioBitrate", ghb_string_value(ghb_audio_bitrate_get_short_name(bitrate))); ghb_settings_set_string(ud->settings, "AudioEncoder", hb_audio_encoder_get_short_name(select_acodec)); GValue *asettings = audio_get_selected_settings(ud, NULL); if (asettings) { ghb_settings_set_string(asettings, "AudioEncoder", hb_audio_encoder_get_short_name(select_acodec)); } ghb_audio_list_refresh_selected(ud); } static char * get_drc_string(gdouble drc) { char *s_drc; if (drc < 0.99) s_drc = g_strdup(_("Off")); else s_drc = g_strdup_printf("%.1f", drc); return s_drc; } static char * get_gain_string(gdouble gain) { char *s_gain; if ( gain >= 21.0 ) s_gain = g_strdup_printf("*11*"); else s_gain = g_strdup_printf(_("%ddB"), (int)gain); return s_gain; } static char * get_quality_string(GValue *settings, gdouble quality) { float low, high, gran; int dir; int codec = ghb_settings_audio_encoder_codec(settings, "AudioEncoder"); hb_audio_quality_get_limits(codec, &low, &high, &gran, &dir); if (dir) { // Quality values are inverted quality = high - quality + low; } char *s_quality = ghb_format_quality("", codec, quality); return s_quality; } static void audio_update_dialog_widgets(signal_user_data_t *ud, GValue *asettings) { if (asettings != NULL) { double gain, drc, quality; char *s_gain, *s_drc, *s_quality; block_updates = TRUE; ghb_ui_update(ud, "AudioTrack", ghb_settings_get_value(asettings, "AudioTrack")); ghb_ui_update(ud, "AudioEncoder", ghb_settings_get_value(asettings, "AudioEncoder")); ghb_ui_update(ud, "AudioBitrate", ghb_settings_get_value(asettings, "AudioBitrate")); ghb_ui_update(ud, "AudioTrackName", ghb_settings_get_value(asettings, "AudioTrackName")); ghb_ui_update(ud, "AudioSamplerate", ghb_settings_get_value(asettings, "AudioSamplerate")); ghb_ui_update(ud, "AudioMixdown", ghb_settings_get_value(asettings, "AudioMixdown")); ghb_ui_update(ud, "AudioTrackDRCSlider", ghb_settings_get_value(asettings, "AudioTrackDRCSlider")); drc = ghb_settings_get_double(asettings, "AudioTrackDRCSlider"); s_drc = get_drc_string(drc); ghb_ui_update(ud, "AudioTrackDRCValue", ghb_string_value(s_drc)); ghb_ui_update(ud, "AudioTrackGainSlider", ghb_settings_get_value(asettings, "AudioTrackGainSlider")); gain = ghb_settings_get_double(asettings, "AudioTrackGainSlider"); s_gain = get_gain_string(gain); ghb_ui_update(ud, "AudioTrackGainValue", ghb_string_value(s_gain)); ghb_ui_update(ud, "AudioTrackQuality", ghb_settings_get_value(asettings, "AudioTrackQuality")); quality = ghb_settings_get_double(asettings, "AudioTrackQuality"); s_quality = get_quality_string(asettings, quality); ghb_ui_update(ud, "AudioTrackQualityValue", ghb_string_value(s_quality)); ghb_ui_update(ud, "AudioTrackQualityEnable", ghb_settings_get_value(asettings, "AudioTrackQualityEnable")); block_updates = FALSE; } audio_deps(ud, asettings, NULL); } static void free_audio_hash_key_value(gpointer data) { g_free(data); } const gchar* ghb_get_user_audio_lang(GValue *settings, const hb_title_t *title, gint track) { GValue *audio_list, *asettings; const gchar *lang; audio_list = ghb_settings_get_value(settings, "audio_list"); if (ghb_array_len(audio_list) <= track) return "und"; asettings = ghb_array_get_nth(audio_list, track); track = ghb_settings_get_int(asettings, "AudioTrack"); lang = ghb_get_source_audio_lang(title, track); return lang; } static gboolean* get_track_used(gint settings, GHashTable *track_indices, gint count) { gboolean *used; used = g_hash_table_lookup(track_indices, &settings); if (used == NULL) { gint *key; used = g_malloc0(count * sizeof(gboolean)); key = g_malloc(sizeof(gint)); *key = settings; g_hash_table_insert(track_indices, key, used); } return used; } static GValue* audio_add_track( GValue *settings, const hb_title_t *title, int track, int encoder, gboolean enable_quality, gdouble quality, int bitrate, int samplerate, int mix, gdouble drc, gdouble gain) { GValue *asettings; hb_audio_config_t *aconfig = NULL; aconfig = ghb_get_audio_info(title, track); asettings = ghb_dict_value_new(); ghb_settings_set_int(asettings, "AudioTrack", track); ghb_settings_set_string(asettings, "AudioEncoder", hb_audio_encoder_get_short_name(encoder)); ghb_settings_set_boolean(asettings, "AudioTrackQualityEnable", enable_quality); ghb_settings_set_double(asettings, "AudioTrackQuality", quality); ghb_settings_set_string(asettings, "AudioBitrate", ghb_audio_bitrate_get_short_name(bitrate)); ghb_settings_set_string(asettings, "AudioSamplerate", ghb_audio_samplerate_get_short_name(samplerate)); if (aconfig != NULL) { mix = ghb_get_best_mix(aconfig, encoder, mix); } ghb_settings_set_string(asettings, "AudioMixdown", hb_mixdown_get_short_name(mix)); ghb_settings_set_double(asettings, "AudioTrackDRCSlider", drc); ghb_settings_set_double(asettings, "AudioTrackGainSlider", gain); audio_sanitize_settings(settings, asettings); audio_add_to_settings(settings, asettings); return asettings; } static GValue* audio_select_and_add_track( const hb_title_t *title, GValue *settings, GValue *pref_audio, const char *lang, int pref_index, int start_track) { GValue *audio, *asettings = NULL; gdouble drc, gain, quality; gboolean enable_quality; gint track, acodec, bitrate, samplerate, mix; gint select_acodec; gint fallback; const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); gint copy_mask = ghb_get_copy_mask(settings); audio = ghb_array_get_nth(pref_audio, pref_index); acodec = ghb_settings_audio_encoder_codec(audio, "AudioEncoder"); fallback = ghb_select_fallback(settings, acodec); bitrate = ghb_settings_audio_bitrate_rate(audio, "AudioBitrate"); samplerate = ghb_settings_audio_samplerate_rate(audio, "AudioSamplerate"); mix = ghb_settings_mixdown_mix(audio, "AudioMixdown"); drc = ghb_settings_get_double(audio, "AudioTrackDRCSlider"); gain = ghb_settings_get_double(audio, "AudioTrackGainSlider"); enable_quality = ghb_settings_get_boolean(audio, "AudioTrackQualityEnable"); quality = ghb_settings_get_double(audio, "AudioTrackQuality"); track = ghb_find_audio_track(title, lang, start_track); if (track >= 0) { // Check to see if: // 1. pref codec is passthru // 2. source codec is not passthru // 3. next pref is enabled hb_audio_config_t *aconfig; aconfig = hb_list_audio_config_item(title->list_audio, track); select_acodec = ghb_select_audio_codec( mux->format, aconfig, acodec, fallback, copy_mask); asettings = audio_add_track(settings, title, track, select_acodec, enable_quality, quality, bitrate, samplerate, mix, drc, gain); } return asettings; } static void set_pref_audio_with_lang( const hb_title_t *title, GValue *settings, const char *lang, int behavior, gboolean secondary_audio_track_mode, GHashTable *track_used) { const GValue *pref_audio, *audio_list; int count, ii, track, track_count, audio_count; audio_list = ghb_settings_get_value(settings, "audio_list"); const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); pref_audio = ghb_settings_get_value(settings, "AudioList"); audio_count = hb_list_count(title->list_audio); count = ghb_array_len(pref_audio); int next_track = 0; track = ghb_find_audio_track(title, lang, next_track); while (track >= 0) { track_count = ghb_array_len(audio_list); count = (track_count && secondary_audio_track_mode) ? 1 : count; for (ii = 0; ii < count; ii++) { const GValue *audio; gint acodec, fallback, copy_mask, bitrate, samplerate, mix; gint select_acodec; gdouble drc, gain, quality; gboolean enable_quality; audio = ghb_array_get_nth(pref_audio, ii); acodec = ghb_settings_audio_encoder_codec(audio, "AudioEncoder"); fallback = ghb_select_fallback(settings, acodec); copy_mask = ghb_get_copy_mask(settings); bitrate = ghb_settings_audio_bitrate_rate(audio, "AudioBitrate"); samplerate = ghb_settings_audio_samplerate_rate(audio, "AudioSamplerate"); mix = ghb_settings_mixdown_mix(audio, "AudioMixdown"); drc = ghb_settings_get_double(audio, "AudioTrackDRCSlider"); gain = ghb_settings_get_double(audio, "AudioTrackGainSlider"); enable_quality = ghb_settings_get_boolean(audio, "AudioTrackQualityEnable"); quality = ghb_settings_get_double(audio, "AudioTrackQuality"); // Check to see if: // 1. pref codec is passthru // 2. source codec is not passthru // 3. next pref is enabled hb_audio_config_t *aconfig; aconfig = hb_list_audio_config_item(title->list_audio, track); select_acodec = ghb_select_audio_codec( mux->format, aconfig, acodec, fallback, copy_mask); // Was the source track already encoded // with the selected encode settings. gboolean *used = get_track_used(ii, track_used, audio_count); if (!used[track]) { used[track] = TRUE; audio_add_track(settings, title, track, select_acodec, enable_quality, quality, bitrate, samplerate, mix, drc, gain); } } next_track = track + 1; if (behavior == 2) { track = ghb_find_audio_track(title, lang, next_track); } else { break; } } } void ghb_audio_title_change(signal_user_data_t *ud, gboolean title_valid) { GtkWidget *w = GHB_WIDGET(ud->builder, "audio_add"); gtk_widget_set_sensitive(w, title_valid); w = GHB_WIDGET(ud->builder, "audio_add_all"); gtk_widget_set_sensitive(w, title_valid); w = GHB_WIDGET(ud->builder, "audio_reset"); gtk_widget_set_sensitive(w, title_valid); } void ghb_set_pref_audio_settings(GValue *settings) { GHashTable *track_used; int title_id, titleindex; const hb_title_t *title; const GValue *lang_list; gint behavior; gint ii, audio_count, lang_count; g_debug("set_pref_audio"); behavior = ghb_settings_combo_int(settings, "AudioTrackSelectionBehavior"); // Clear the audio list ghb_clear_audio_list_settings(settings); title_id = ghb_settings_get_int(settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (behavior == 0 || title == NULL) { // None or no source title return; } audio_count = hb_list_count(title->list_audio); if (audio_count == 0) { // No source audio return; } track_used = g_hash_table_new_full(g_int_hash, g_int_equal, free_audio_hash_key_value, free_audio_hash_key_value); // Find "best" audio based on audio preset defaults lang_list = ghb_settings_get_value(settings, "AudioLanguageList"); lang_count = ghb_array_len(lang_list); for (ii = 0; ii < lang_count; ii++) { const gchar *lang; gboolean mode; GValue *glang = ghb_array_get_nth(lang_list, ii); lang = g_value_get_string(glang); mode = ghb_settings_get_boolean(settings, "AudioSecondaryEncoderMode"); set_pref_audio_with_lang(title, settings, lang, behavior, mode, track_used); } GValue *audio_list = ghb_settings_get_value(settings, "audio_list"); if (audio_list == NULL || ghb_array_len(audio_list) == 0) { // No matching audio tracks found. Add first track matching // any language. set_pref_audio_with_lang(title, settings, "und", 1, FALSE, track_used); } g_hash_table_destroy(track_used); } static GValue* audio_get_selected_settings(signal_user_data_t *ud, int *index) { GtkTreeView *tv; GtkTreePath *tp; GtkTreeSelection *ts; GtkTreeModel *tm; GtkTreeIter ti; gint *indices; gint row; GValue *asettings = NULL; const GValue *audio_list; tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list")); ts = gtk_tree_view_get_selection (tv); if (gtk_tree_selection_get_selected(ts, &tm, &ti)) { // Get the row number tp = gtk_tree_model_get_path (tm, &ti); indices = gtk_tree_path_get_indices (tp); row = indices[0]; gtk_tree_path_free(tp); // find audio settings if (row < 0) return NULL; audio_list = ghb_settings_get_value(ud->settings, "audio_list"); if (row >= ghb_array_len(audio_list)) return NULL; asettings = ghb_array_get_nth(audio_list, row); if (index != NULL) *index = row; } return asettings; } static void audio_refresh_list_row_ui( GtkTreeModel *tm, GtkTreeIter *ti, signal_user_data_t *ud, const GValue *settings) { GtkTreeIter cti; char *info_src, *info_src_2; char *info_dst, *info_dst_2; info_src_2 = NULL; info_dst_2 = NULL; const gchar *s_track; gchar *s_drc, *s_gain, *s_br_quality, *s_sr, *s_track_name; gdouble drc, gain; hb_audio_config_t *aconfig; int track, sr; int title_id, titleindex; const hb_title_t *title; const hb_encoder_t *encoder; title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); track = ghb_settings_get_int(settings, "AudioTrack"); aconfig = ghb_get_audio_info(title, track); if (aconfig == NULL) { return; } s_track = aconfig->lang.description; encoder = ghb_settings_audio_encoder(settings, "AudioEncoder"); double quality = ghb_settings_get_double(settings, "AudioTrackQuality"); if (ghb_settings_get_boolean(settings, "AudioTrackQualityEnable") && quality != HB_INVALID_AUDIO_QUALITY) { s_br_quality = ghb_format_quality(_("Quality: "), encoder->codec, quality); } else { s_br_quality = g_strdup_printf(_("Bitrate: %dkbps"), ghb_settings_audio_bitrate_rate(settings, "AudioBitrate")); } sr = ghb_settings_audio_samplerate_rate(settings, "AudioSamplerate"); if (sr == 0) { sr = aconfig->in.samplerate; } s_sr = g_strdup_printf(_("%.4gkHz"), (double)sr/1000); const hb_mixdown_t *mix; mix = ghb_settings_mixdown(settings, "AudioMixdown"); gain = ghb_settings_get_double(settings, "AudioTrackGainSlider"); s_gain = g_strdup_printf(_("%ddB"), (int)gain); drc = ghb_settings_get_double(settings, "AudioTrackDRCSlider"); if (drc < 1.0) s_drc = g_strdup(_("Off")); else s_drc = g_strdup_printf("%.1f", drc); s_track_name = ghb_settings_get_string(settings, "AudioTrackName"); info_src = g_strdup_printf(_("%d - %s (%.4gkHz)"), track + 1, s_track, (double)aconfig->in.samplerate / 1000); if (aconfig->in.bitrate > 0) { info_src_2 = g_strdup_printf( _("Bitrate: %.4gkbps"), (double)aconfig->in.bitrate / 1000); } if (ghb_audio_is_passthru(encoder->codec)) { info_dst = g_strdup_printf(_("Passthrough")); } else { info_dst = g_strdup_printf("%s (%s) (%s)", encoder->name, mix->name, s_sr); if (s_track_name && s_track_name[0]) { info_dst_2 = g_strdup_printf( _("%s\nGain: %s\nDRC: %s\nTrack Name: %s"), s_br_quality, s_gain, s_drc, s_track_name); } else { info_dst_2 = g_strdup_printf(_("%s\nGain: %s\nDRC: %s"), s_br_quality, s_gain, s_drc); } } gtk_tree_store_set(GTK_TREE_STORE(tm), ti, // These are displayed in list 0, info_src, 1, "-->", 2, info_dst, 3, "hb-edit", 4, "hb-remove", 5, 0.5, -1); if (info_src_2 != NULL || info_dst_2 != NULL) { if (info_src_2 == NULL) info_src_2 = g_strdup(""); if (info_dst_2 == NULL) info_dst_2 = g_strdup(""); // Get the child of the selection if (!gtk_tree_model_iter_children(tm, &cti, ti)) { gtk_tree_store_append(GTK_TREE_STORE(tm), &cti, ti); } gtk_tree_store_set(GTK_TREE_STORE(tm), &cti, // These are displayed in list 0, info_src_2, 2, info_dst_2, 5, 0.0, -1); } else { if(gtk_tree_model_iter_children(tm, &cti, ti)) { gtk_tree_store_remove(GTK_TREE_STORE(tm), &cti); } } g_free(info_src); g_free(info_src_2); g_free(info_dst); g_free(info_dst_2); g_free(s_sr); g_free(s_drc); g_free(s_gain); g_free(s_br_quality); g_free(s_track_name); } void ghb_audio_list_refresh_selected(signal_user_data_t *ud) { GtkTreeView *tv; GtkTreePath *tp; GtkTreeSelection *ts; GtkTreeModel *tm; GtkTreeIter ti; gint *indices; gint row; GValue *asettings = NULL; const GValue *audio_list; g_debug("ghb_audio_list_refresh_selected ()"); tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list")); ts = gtk_tree_view_get_selection (tv); if (gtk_tree_selection_get_selected(ts, &tm, &ti)) { // Get the row number tp = gtk_tree_model_get_path (tm, &ti); indices = gtk_tree_path_get_indices (tp); row = indices[0]; gtk_tree_path_free(tp); if (row < 0) return; audio_list = ghb_settings_get_value(ud->settings, "audio_list"); if (row >= ghb_array_len(audio_list)) return; asettings = ghb_array_get_nth(audio_list, row); audio_refresh_list_row_ui(tm, &ti, ud, asettings); } } static void audio_refresh_list_ui(signal_user_data_t *ud) { GValue *audio_list; GValue *asettings; gint ii, count, tm_count; GtkTreeView *tv; GtkTreeModel *tm; GtkTreeIter ti; tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list")); tm = gtk_tree_view_get_model(tv); tm_count = gtk_tree_model_iter_n_children(tm, NULL); audio_list = ghb_settings_get_value(ud->settings, "audio_list"); count = ghb_array_len(audio_list); if (count != tm_count) { ghb_clear_audio_list_ui(ud->builder); for (ii = 0; ii < count; ii++) { gtk_tree_store_append(GTK_TREE_STORE(tm), &ti, NULL); } } for (ii = 0; ii < count; ii++) { gtk_tree_model_iter_nth_child(tm, &ti, NULL, ii); asettings = ghb_array_get_nth(audio_list, ii); audio_refresh_list_row_ui(tm, &ti, ud, asettings); } } void ghb_audio_list_refresh_all(signal_user_data_t *ud) { audio_refresh_list_ui(ud); } G_MODULE_EXPORT void audio_codec_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { static gint prev_acodec = 0; gint acodec; GValue *asettings; ghb_widget_to_setting(ud->settings, widget); asettings = audio_get_selected_settings(ud, NULL); if (!block_updates && asettings != NULL) { ghb_widget_to_setting(asettings, widget); audio_deps(ud, asettings, widget); ghb_audio_list_refresh_selected(ud); ghb_live_reset(ud); } acodec = ghb_settings_audio_encoder_codec(ud->settings, "AudioEncoder"); float low, high, gran, defval; int dir; hb_audio_quality_get_limits(acodec, &low, &high, &gran, &dir); defval = hb_audio_quality_get_default(acodec); GtkScaleButton *sb; GtkAdjustment *adj; sb = GTK_SCALE_BUTTON(GHB_WIDGET(ud->builder, "AudioTrackQuality")); adj = gtk_scale_button_get_adjustment(sb); if (dir) { // Quality values are inverted defval = high - defval + low; } gtk_adjustment_configure (adj, defval, low, high, gran, gran * 10, 0); if (block_updates) { prev_acodec = acodec; return; } if (ghb_audio_is_passthru(prev_acodec) && !ghb_audio_is_passthru(acodec)) { // Transition from passthru to not, put some audio settings back to // pref settings int title_id; const hb_title_t *title; gint titleindex; gint track; gint br, sr, mix; title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (asettings != NULL) { br = ghb_settings_audio_bitrate_rate(asettings, "AudioBitrate"); sr = ghb_settings_audio_samplerate_rate(asettings, "AudioSamplerate"); mix = ghb_settings_mixdown_mix(asettings, "AudioMixdown"); } else { br = 160; sr = 0; mix = HB_AMIXDOWN_NONE; } track = ghb_settings_get_int(ud->settings, "AudioTrack"); if (sr) { sr = ghb_find_closest_audio_samplerate(sr); } ghb_ui_update(ud, "AudioSamplerate", ghb_string_value(ghb_audio_samplerate_get_short_name(sr))); hb_audio_config_t *aconfig; aconfig = ghb_get_audio_info(title, track); if (sr == 0) { sr = aconfig ? aconfig->in.samplerate : 48000; } mix = ghb_get_best_mix( aconfig, acodec, mix); br = hb_audio_bitrate_get_best(acodec, br, sr, mix); ghb_ui_update(ud, "AudioBitrate", ghb_string_value(ghb_audio_bitrate_get_short_name(br))); ghb_ui_update(ud, "AudioMixdown", ghb_string_value(hb_mixdown_get_short_name(mix))); } prev_acodec = acodec; } G_MODULE_EXPORT void audio_track_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { GValue *asettings; g_debug("audio_track_changed_cb ()"); ghb_widget_to_setting(ud->settings, widget); if (block_updates) { return; } asettings = audio_get_selected_settings(ud, NULL); if (asettings != NULL) { ghb_widget_to_setting(asettings, widget); audio_deps(ud, asettings, widget); ghb_audio_list_refresh_selected(ud); ghb_live_reset(ud); // Update the track description used by the queue int title_id, titleindex; const hb_title_t *title; int track; hb_audio_config_t *aconfig; title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); track = ghb_settings_get_int(ud->settings, "AudioTrack"); aconfig = ghb_get_audio_info(title, track); if (aconfig != NULL) { char *desc; desc = g_strdup_printf("%d - %s", track + 1, aconfig->lang.description); ghb_settings_set_string(asettings, "AudioTrackDescription", desc); g_free(desc); } } } G_MODULE_EXPORT void audio_mix_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { GValue *asettings; ghb_widget_to_setting(ud->settings, widget); if (block_updates) { return; } asettings = audio_get_selected_settings(ud, NULL); if (asettings != NULL) { ghb_widget_to_setting(asettings, widget); audio_deps(ud, asettings, widget); ghb_audio_list_refresh_selected(ud); ghb_live_reset(ud); } } G_MODULE_EXPORT void audio_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { GValue *asettings; ghb_widget_to_setting(ud->settings, widget); if (block_updates) { return; } asettings = audio_get_selected_settings(ud, NULL); if (asettings != NULL) { ghb_widget_to_setting(asettings, widget); audio_deps(ud, asettings, widget); ghb_audio_list_refresh_selected(ud); ghb_live_reset(ud); } } G_MODULE_EXPORT void audio_quality_radio_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_check_dependency(ud, widget, NULL); audio_widget_changed_cb(widget, ud); } G_MODULE_EXPORT void audio_passthru_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); ghb_clear_presets_selection(ud); } G_MODULE_EXPORT gchar* format_drc_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud) { if (val < 1.0) return g_strdup_printf("%-7s", _("Off")); else return g_strdup_printf("%-7.1f", val); } static inline int is_close(float a, float b, float metric) { float diff = a - b; diff = (diff > 0) ? diff : -diff; return diff < metric; } char * ghb_format_quality( const char *prefix, int codec, double quality ) { float low, high, gran; int dir; hb_audio_quality_get_limits(codec, &low, &high, &gran, &dir); int digits = 0; float tmp = gran; while (1) { if (is_close(tmp, (int)tmp, gran / 10)) break; digits++; tmp *= 10; } return g_strdup_printf("%s%.*f", prefix, digits, quality); } G_MODULE_EXPORT void quality_widget_changed_cb(GtkWidget *widget, gdouble quality, signal_user_data_t *ud) { GValue *asettings; ghb_check_dependency(ud, widget, NULL); char *s_quality = get_quality_string(ud->settings, quality); ghb_ui_update( ud, "AudioTrackQualityValue", ghb_string_value(s_quality)); g_free(s_quality); if (block_updates) return; asettings = audio_get_selected_settings(ud, NULL); if (asettings != NULL) { ghb_settings_set_double(asettings, "AudioTrackQuality", quality); ghb_audio_list_refresh_selected(ud); } ghb_live_reset(ud); } G_MODULE_EXPORT void drc_widget_changed_cb(GtkWidget *widget, gdouble drc, signal_user_data_t *ud) { GValue *asettings; g_debug("drc_widget_changed_cb ()"); ghb_widget_to_setting(ud->settings, widget); if (block_updates) { return; } char *s_drc = get_drc_string(drc); ghb_ui_update( ud, "AudioTrackDRCValue", ghb_string_value(s_drc)); g_free(s_drc); asettings = audio_get_selected_settings(ud, NULL); if (asettings != NULL) { ghb_widget_to_setting(asettings, widget); audio_deps(ud, asettings, widget); ghb_audio_list_refresh_selected(ud); ghb_live_reset(ud); } } G_MODULE_EXPORT gchar* format_gain_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud) { if ( val >= 21.0 ) return g_strdup_printf("*11*"); return g_strdup_printf(_("%ddB"), (int)val); } G_MODULE_EXPORT void gain_widget_changed_cb(GtkWidget *widget, gdouble gain, signal_user_data_t *ud) { GValue *asettings; g_debug("gain_widget_changed_cb ()"); ghb_widget_to_setting(ud->settings, widget); if (block_updates) { return; } char *s_gain = get_gain_string(gain); ghb_ui_update( ud, "AudioTrackGainValue", ghb_string_value(s_gain)); g_free(s_gain); asettings = audio_get_selected_settings(ud, NULL); if (asettings != NULL) { ghb_widget_to_setting(asettings, widget); audio_deps(ud, asettings, widget); ghb_audio_list_refresh_selected(ud); ghb_live_reset(ud); } } void ghb_clear_audio_list_settings(GValue *settings) { GValue *audio_list; g_debug("clear_audio_list_settings ()"); audio_list = ghb_settings_get_value(settings, "audio_list"); if (audio_list == NULL) { audio_list = ghb_array_value_new(8); ghb_settings_set_value(settings, "audio_list", audio_list); } else ghb_array_value_reset(audio_list, 8); } void ghb_clear_audio_list_ui(GtkBuilder *builder) { GtkTreeView *tv; GtkTreeStore *ts; GtkTreeSelection *tsel; g_debug("clear_audio_list_ui ()"); tv = GTK_TREE_VIEW(GHB_WIDGET(builder, "audio_list")); ts = GTK_TREE_STORE(gtk_tree_view_get_model(tv)); // Clear tree selection so that updates are not triggered // that cause a recursive attempt to clear the tree selection (crasher) tsel = gtk_tree_view_get_selection(tv); gtk_tree_selection_unselect_all(tsel); gtk_tree_store_clear(ts); } static void ghb_add_audio_to_ui(signal_user_data_t *ud, const GValue *asettings) { GtkTreeView *tv; GtkTreeIter ti; GtkTreeModel *tm; GtkTreeSelection *ts; if (asettings == NULL) return; tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list")); ts = gtk_tree_view_get_selection (tv); tm = gtk_tree_view_get_model(tv); gtk_tree_store_append(GTK_TREE_STORE(tm), &ti, NULL); gtk_tree_selection_select_iter(ts, &ti); audio_refresh_list_row_ui(tm, &ti, ud, asettings); } G_MODULE_EXPORT void audio_list_selection_changed_cb(GtkTreeSelection *ts, signal_user_data_t *ud) { GtkTreeModel *tm; GtkTreeIter ti; GValue *asettings = NULL; gint row; g_debug("audio_list_selection_changed_cb ()"); if (gtk_tree_selection_get_selected(ts, &tm, &ti)) { GtkTreeIter pti; if (gtk_tree_model_iter_parent(tm, &pti, &ti)) { GtkTreePath *path; GtkTreeView *tv; gtk_tree_selection_select_iter(ts, &pti); path = gtk_tree_model_get_path(tm, &pti); tv = gtk_tree_selection_get_tree_view(ts); // Make the parent visible in scroll window if it is not. gtk_tree_view_scroll_to_cell(tv, path, NULL, FALSE, 0, 0); gtk_tree_path_free(path); return; } GtkTreePath *tp; gint *indices; GValue *audio_list; // Get the row number tp = gtk_tree_model_get_path (tm, &ti); indices = gtk_tree_path_get_indices (tp); row = indices[0]; gtk_tree_path_free(tp); if (row < 0) return; audio_list = ghb_settings_get_value(ud->settings, "audio_list"); if (row >= 0 && row < ghb_array_len(audio_list)) asettings = ghb_array_get_nth(audio_list, row); } audio_update_dialog_widgets(ud, asettings); } static void audio_add_to_settings(GValue *settings, GValue *asettings) { GValue *audio_list; int title_id; const hb_title_t *title; gint titleindex; hb_audio_config_t *aconfig; title_id = ghb_settings_get_int(settings, "title"); title = ghb_lookup_title(title_id, &titleindex); audio_list = ghb_settings_get_value(settings, "audio_list"); if (audio_list == NULL) { audio_list = ghb_array_value_new(8); ghb_settings_set_value(settings, "audio_list", audio_list); } int track = ghb_settings_get_int(asettings, "AudioTrack"); aconfig = ghb_get_audio_info(title, track); if (aconfig != NULL) { char *desc; desc = g_strdup_printf("%d - %s", track + 1, aconfig->lang.description); ghb_settings_set_string(asettings, "AudioTrackDescription", desc); g_free(desc); } GValue *aname; aname = ghb_dict_lookup(asettings, "AudioTrackName"); if (aname == NULL) { ghb_settings_set_string(asettings, "AudioTrackName", ""); } ghb_array_append(audio_list, asettings); } G_MODULE_EXPORT void audio_add_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { // Add the current audio settings to the list. GValue *asettings, *backup; int title_id, titleindex; const hb_title_t *title; title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); // Back up settings in case we need to revert. backup = ghb_value_dup( ghb_settings_get_value(ud->settings, "audio_list")); GValue *pref_audio = ghb_settings_get_value(ud->settings, "AudioList"); asettings = audio_select_and_add_track(title, ud->settings, pref_audio, "und", 0, 0); ghb_add_audio_to_ui(ud, asettings); if (asettings != NULL) { // Pop up the edit dialog GtkResponseType response; GtkWidget *dialog = GHB_WIDGET(ud->builder, "audio_dialog"); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); if (response != GTK_RESPONSE_OK) { ghb_settings_take_value(ud->settings, "audio_list", backup); asettings = audio_get_selected_settings(ud, NULL); if (asettings != NULL) { audio_update_dialog_widgets(ud, asettings); } audio_refresh_list_ui(ud); } else { ghb_value_free(backup); } } } G_MODULE_EXPORT void audio_add_all_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { int title_id, titleindex; const hb_title_t *title; // Add the current audio settings to the list. ghb_clear_audio_list_settings(ud->settings); ghb_clear_audio_list_ui(ud->builder); title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); GValue *pref_audio = ghb_settings_get_value(ud->settings, "AudioList"); int pref_count = ghb_array_len(pref_audio); int ii; for (ii = 0; ii < pref_count; ii++) { GValue *asettings; int track = 0; do { asettings = audio_select_and_add_track(title, ud->settings, pref_audio, "und", ii, track); if (asettings != NULL) { track = ghb_settings_get_int(asettings, "AudioTrack") + 1; } } while (asettings != NULL); } audio_refresh_list_ui(ud); } G_MODULE_EXPORT void audio_edit_clicked_cb(GtkWidget *widget, gchar *path, signal_user_data_t *ud) { GtkTreeView *tv; GtkTreePath *tp; GtkTreeModel *tm; GtkTreeSelection *ts; GtkTreeIter ti; tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list")); ts = gtk_tree_view_get_selection(tv); tm = gtk_tree_view_get_model(tv); tp = gtk_tree_path_new_from_string (path); if (gtk_tree_path_get_depth(tp) > 1) return; if (gtk_tree_model_get_iter(tm, &ti, tp)) { GValue *asettings, *backup; gtk_tree_selection_select_iter(ts, &ti); // Back up settings in case we need to revert. backup = ghb_value_dup( ghb_settings_get_value(ud->settings, "audio_list")); // Pop up the edit dialog GtkResponseType response; GtkWidget *dialog = GHB_WIDGET(ud->builder, "audio_dialog"); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); if (response != GTK_RESPONSE_OK) { ghb_settings_take_value(ud->settings, "audio_list", backup); asettings = audio_get_selected_settings(ud, NULL); if (asettings != NULL) { audio_update_dialog_widgets(ud, asettings); } audio_refresh_list_ui(ud); } else { ghb_value_free(backup); } } } G_MODULE_EXPORT void audio_remove_clicked_cb(GtkWidget *widget, gchar *path, signal_user_data_t *ud) { GtkTreeView *tv; GtkTreePath *tp; GtkTreeModel *tm; GtkTreeSelection *ts; GtkTreeIter ti, nextIter; gint row; gint *indices; GValue *audio_list; tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list")); ts = gtk_tree_view_get_selection(tv); tm = gtk_tree_view_get_model(tv); tp = gtk_tree_path_new_from_string (path); if (gtk_tree_path_get_depth(tp) > 1) return; if (gtk_tree_model_get_iter(tm, &ti, tp)) { nextIter = ti; if (!gtk_tree_model_iter_next(tm, &nextIter)) { nextIter = ti; if (gtk_tree_model_get_iter_first(tm, &nextIter)) { gtk_tree_selection_select_iter(ts, &nextIter); } } else { gtk_tree_selection_select_iter(ts, &nextIter); } audio_list = ghb_settings_get_value(ud->settings, "audio_list"); // Get the row number indices = gtk_tree_path_get_indices (tp); row = indices[0]; if (row < 0 || row >= ghb_array_len(audio_list)) { gtk_tree_path_free(tp); return; } // Update our settings list before removing the row from the // treeview. Removing from the treeview sometimes provokes an // immediate selection change, so the list needs to be up to date // when this happens. GValue *old = ghb_array_get_nth(audio_list, row); ghb_value_free(old); ghb_array_remove(audio_list, row); // Remove the selected item gtk_tree_store_remove(GTK_TREE_STORE(tm), &ti); ghb_live_reset(ud); } gtk_tree_path_free(tp); } G_MODULE_EXPORT void audio_reset_clicked_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_set_pref_audio_settings(ud->settings); audio_refresh_list_ui(ud); } static GtkWidget *find_widget(GtkWidget *widget, gchar *name) { const char *wname; GtkWidget *result = NULL; if (widget == NULL || name == NULL) return NULL; wname = gtk_widget_get_name(widget); if (wname != NULL && !strncmp(wname, name, 80)) { return widget; } if (GTK_IS_CONTAINER(widget)) { GList *list, *link; link = list = gtk_container_get_children(GTK_CONTAINER(widget)); while (link) { result = find_widget(GTK_WIDGET(link->data), name); if (result != NULL) break; link = link->next; } g_list_free(list); } return result; } static void audio_def_update_widgets(GtkWidget *row, GValue *adict) { GHashTableIter ti; gchar *key; GValue *gval; GtkWidget *widget; ghb_dict_iter_init(&ti, adict); block_updates = TRUE; while (g_hash_table_iter_next(&ti, (gpointer*)&key, (gpointer*)&gval)) { widget = find_widget(row, key); if (widget != NULL) { ghb_update_widget(widget, gval); } } block_updates = FALSE; } static GtkListBoxRow* audio_settings_get_row(GtkWidget *widget) { while (widget != NULL && G_OBJECT_TYPE(widget) != GTK_TYPE_LIST_BOX_ROW) { widget = gtk_widget_get_parent(widget); } return GTK_LIST_BOX_ROW(widget); } static void audio_def_settings_bitrate_show(GtkWidget *widget, gboolean show) { GtkWidget *bitrate_widget; GtkWidget *quality_widget; bitrate_widget = find_widget(widget, "AudioBitrate"); quality_widget = find_widget(widget, "AudioTrackQualityBox"); if (show) { gtk_widget_hide(quality_widget); gtk_widget_show(bitrate_widget); } else { gtk_widget_hide(bitrate_widget); gtk_widget_show(quality_widget); } } static void audio_def_settings_quality_set_sensitive(GtkWidget *w, gboolean s) { GtkWidget *bitrate_widget; GtkWidget *quality_widget; bitrate_widget = find_widget(w, "AudioTrackBitrateEnable"); quality_widget = find_widget(w, "AudioTrackQualityEnable"); if (!s) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bitrate_widget), TRUE); } gtk_widget_set_sensitive(GTK_WIDGET(quality_widget), s); } static void audio_def_settings_show(GtkWidget *widget, gboolean show) { GtkWidget *settings_box; GtkWidget *add_button; settings_box = find_widget(widget, "settings_box"); add_button = find_widget(widget, "add_button"); if (show) { gtk_widget_hide(add_button); gtk_widget_show(settings_box); } else { gtk_widget_hide(settings_box); gtk_widget_show(add_button); } } static void audio_def_settings_init_row(GValue *adict, GtkWidget *row) { GtkWidget *widget; widget = find_widget(row, "AudioEncoder"); ghb_widget_to_setting(adict, widget); widget = find_widget(row, "AudioBitrate"); ghb_widget_to_setting(adict, widget); widget = find_widget(row, "AudioMixdown"); ghb_widget_to_setting(adict, widget); widget = find_widget(row, "AudioSamplerate"); ghb_widget_to_setting(adict, widget); widget = find_widget(row, "AudioTrackGainSlider"); ghb_widget_to_setting(adict, widget); widget = find_widget(row, "AudioTrackDRCSlider"); ghb_widget_to_setting(adict, widget); widget = find_widget(row, "AudioTrackQuality"); ghb_widget_to_setting(adict, widget); widget = find_widget(row, "AudioTrackQualityEnable"); ghb_widget_to_setting(adict, widget); } G_MODULE_EXPORT void audio_def_setting_add_cb(GtkWidget *w, signal_user_data_t *ud); G_MODULE_EXPORT void audio_def_setting_remove_cb(GtkWidget *w, signal_user_data_t *ud); G_MODULE_EXPORT void audio_def_encoder_changed_cb(GtkWidget *w, signal_user_data_t *ud); G_MODULE_EXPORT void audio_def_encode_setting_changed_cb(GtkWidget *w, signal_user_data_t *ud); G_MODULE_EXPORT void audio_def_drc_changed_cb(GtkWidget *w, gdouble drc, signal_user_data_t *ud); G_MODULE_EXPORT void audio_def_gain_changed_cb(GtkWidget *w, gdouble gain, signal_user_data_t *ud); G_MODULE_EXPORT void audio_def_quality_changed_cb(GtkWidget *w, gdouble quality, signal_user_data_t *ud); G_MODULE_EXPORT void audio_def_quality_enable_changed_cb(GtkWidget *w, signal_user_data_t *ud); GtkWidget * ghb_create_audio_settings_row(signal_user_data_t *ud) { GtkBox *box, *box2, *box3; GtkComboBox *combo; GtkScaleButton *scale; GtkLabel *label; GtkRadioButton *radio; GtkButton *button; GtkImage *image; box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0)); // Add Button button = GTK_BUTTON(gtk_button_new_with_label(_("Add"))); gtk_widget_set_tooltip_markup(GTK_WIDGET(button), _("Add an audio encoder.\n" "Each selected source track will be encoded with all selected encoders.")); gtk_widget_set_valign(GTK_WIDGET(button), GTK_ALIGN_CENTER); gtk_widget_set_name(GTK_WIDGET(button), "add_button"); gtk_widget_hide(GTK_WIDGET(button)); g_signal_connect(button, "clicked", (GCallback)audio_def_setting_add_cb, ud); gtk_box_pack_start(box, GTK_WIDGET(button), FALSE, FALSE, 0); // Hidden widgets box box2 = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0)); gtk_widget_set_name(GTK_WIDGET(box2), "settings_box"); // Audio Encoder ComboBox combo = GTK_COMBO_BOX(gtk_combo_box_new()); ghb_init_combo_box(combo); ghb_audio_encoder_opts_set(combo); // Init to first audio encoder const hb_encoder_t *aud_enc = hb_audio_encoder_get_next(NULL); ghb_update_widget(GTK_WIDGET(combo), ghb_int64_value(aud_enc->codec)); gtk_widget_set_tooltip_markup(GTK_WIDGET(combo), _("Set the audio codec to encode this track with.")); gtk_widget_set_valign(GTK_WIDGET(combo), GTK_ALIGN_CENTER); gtk_widget_set_name(GTK_WIDGET(combo), "AudioEncoder"); gtk_widget_show(GTK_WIDGET(combo)); g_signal_connect(combo, "changed", (GCallback)audio_def_encoder_changed_cb, ud); gtk_box_pack_start(box2, GTK_WIDGET(combo), FALSE, FALSE, 0); box3 = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0)); gtk_widget_set_name(GTK_WIDGET(box3), "br_q_box"); gtk_widget_show(GTK_WIDGET(box3)); // Bitrate vs Quality RadioButton GtkBox *vbox; vbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0)); radio = GTK_RADIO_BUTTON(gtk_radio_button_new_with_label(NULL, _("Bitrate"))); gtk_widget_set_name(GTK_WIDGET(radio), "AudioTrackBitrateEnable"); gtk_widget_show(GTK_WIDGET(radio)); gtk_box_pack_start(vbox, GTK_WIDGET(radio), FALSE, FALSE, 0); radio = GTK_RADIO_BUTTON( gtk_radio_button_new_with_label_from_widget(radio, _("Quality"))); gtk_widget_set_name(GTK_WIDGET(radio), "AudioTrackQualityEnable"); g_signal_connect(radio, "toggled", (GCallback)audio_def_quality_enable_changed_cb, ud); gtk_widget_show(GTK_WIDGET(radio)); gtk_box_pack_start(vbox, GTK_WIDGET(radio), FALSE, FALSE, 0); gtk_widget_show(GTK_WIDGET(vbox)); gtk_box_pack_start(box3, GTK_WIDGET(vbox), FALSE, FALSE, 0); // Audio Bitrate ComboBox combo = GTK_COMBO_BOX(gtk_combo_box_new()); ghb_init_combo_box(combo); ghb_audio_bitrate_opts_set(combo, FALSE); ghb_update_widget(GTK_WIDGET(combo), ghb_int64_value(192)); gtk_widget_set_tooltip_markup(GTK_WIDGET(combo), _("Set the bitrate to encode this track with.")); gtk_widget_set_valign(GTK_WIDGET(combo), GTK_ALIGN_CENTER); gtk_widget_set_name(GTK_WIDGET(combo), "AudioBitrate"); gtk_widget_show(GTK_WIDGET(combo)); g_signal_connect(combo, "changed", (GCallback)audio_def_encode_setting_changed_cb, ud); gtk_box_pack_start(box3, GTK_WIDGET(combo), FALSE, FALSE, 0); GtkBox *qbox; qbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0)); gtk_widget_set_name(GTK_WIDGET(qbox), "AudioTrackQualityBox"); // Audio Quality ScaleButton const gchar *quality_icons[] = { "weather-storm", "weather-clear", "weather-storm", "weather-showers-scattered", "weather-showers", "weather-overcast", "weather-few-clouds", "weather-clear", NULL }; scale = GTK_SCALE_BUTTON(gtk_scale_button_new(GTK_ICON_SIZE_BUTTON, 0, 10, 0.1, quality_icons)); gtk_widget_set_tooltip_markup(GTK_WIDGET(scale), _("Audio Quality:\n" "For encoders that support it, adjust the quality of the output.")); gtk_widget_set_valign(GTK_WIDGET(scale), GTK_ALIGN_CENTER); gtk_widget_set_name(GTK_WIDGET(scale), "AudioTrackQuality"); gtk_widget_show(GTK_WIDGET(scale)); g_signal_connect(scale, "value-changed", (GCallback)audio_def_quality_changed_cb, ud); gtk_box_pack_start(qbox, GTK_WIDGET(scale), FALSE, FALSE, 0); // Audio Quality Label label = GTK_LABEL(gtk_label_new("0.00")); gtk_label_set_width_chars(label, 4); gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_START); gtk_widget_set_valign(GTK_WIDGET(label), GTK_ALIGN_CENTER); gtk_widget_set_name(GTK_WIDGET(label), "AudioTrackQualityValue"); gtk_widget_show(GTK_WIDGET(label)); gtk_box_pack_start(qbox, GTK_WIDGET(label), FALSE, FALSE, 0); gtk_widget_hide(GTK_WIDGET(qbox)); gtk_box_pack_start(box3, GTK_WIDGET(qbox), FALSE, FALSE, 0); gtk_box_pack_start(box2, GTK_WIDGET(box3), FALSE, FALSE, 0); // Audio Mix ComboBox combo = GTK_COMBO_BOX(gtk_combo_box_new()); ghb_init_combo_box(combo); ghb_mix_opts_set(combo); ghb_update_widget(GTK_WIDGET(combo), ghb_int64_value(HB_AMIXDOWN_5POINT1)); gtk_widget_set_tooltip_markup(GTK_WIDGET(combo), _("Set the mixdown of the output audio track.")); gtk_widget_set_valign(GTK_WIDGET(combo), GTK_ALIGN_CENTER); gtk_widget_set_name(GTK_WIDGET(combo), "AudioMixdown"); gtk_widget_show(GTK_WIDGET(combo)); g_signal_connect(combo, "changed", (GCallback)audio_def_encode_setting_changed_cb, ud); gtk_box_pack_start(box2, GTK_WIDGET(combo), FALSE, FALSE, 0); // Audio Sample Rate ComboBox combo = GTK_COMBO_BOX(gtk_combo_box_new()); ghb_init_combo_box(combo); ghb_audio_samplerate_opts_set(combo); ghb_update_widget(GTK_WIDGET(combo), ghb_int64_value(0)); gtk_widget_set_tooltip_markup(GTK_WIDGET(combo), _("Set the sample rate of the output audio track.")); gtk_widget_set_valign(GTK_WIDGET(combo), GTK_ALIGN_CENTER); gtk_widget_set_name(GTK_WIDGET(combo), "AudioSamplerate"); gtk_widget_show(GTK_WIDGET(combo)); g_signal_connect(combo, "changed", (GCallback)audio_def_encode_setting_changed_cb, ud); gtk_box_pack_start(box2, GTK_WIDGET(combo), FALSE, FALSE, 0); box3 = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0)); gtk_widget_set_name(GTK_WIDGET(box3), "gain_box"); gtk_widget_show(GTK_WIDGET(box3)); // Audio Gain ScaleButton const gchar *gain_icons[] = { "audio-volume-muted", "audio-volume-high", "audio-volume-low", "audio-volume-medium", NULL }; scale = GTK_SCALE_BUTTON(gtk_scale_button_new(GTK_ICON_SIZE_BUTTON, -20, 21, 1, gain_icons)); gtk_widget_set_tooltip_markup(GTK_WIDGET(scale), _("Audio Gain:\n" "Adjust the amplification or attenuation of the output audio track.")); gtk_widget_set_valign(GTK_WIDGET(scale), GTK_ALIGN_CENTER); gtk_widget_set_name(GTK_WIDGET(scale), "AudioTrackGainSlider"); gtk_widget_show(GTK_WIDGET(scale)); g_signal_connect(scale, "value-changed", (GCallback)audio_def_gain_changed_cb, ud); gtk_box_pack_start(box3, GTK_WIDGET(scale), FALSE, FALSE, 0); // Audio Gain Label label = GTK_LABEL(gtk_label_new(_("0dB"))); gtk_label_set_width_chars(label, 6); gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_START); gtk_widget_set_valign(GTK_WIDGET(label), GTK_ALIGN_CENTER); gtk_widget_set_name(GTK_WIDGET(label), "AudioTrackGainValue"); gtk_widget_show(GTK_WIDGET(label)); gtk_box_pack_start(box3, GTK_WIDGET(label), FALSE, FALSE, 0); gtk_box_pack_start(box2, GTK_WIDGET(box3), FALSE, FALSE, 0); box3 = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0)); gtk_widget_set_name(GTK_WIDGET(box3), "drc_box"); gtk_widget_show(GTK_WIDGET(box3)); // Audio DRC ComboBox const gchar *drc_icons[] = { "audio-input-microphone", NULL }; scale = GTK_SCALE_BUTTON(gtk_scale_button_new(GTK_ICON_SIZE_BUTTON, 0.9, 4, 0.1, drc_icons)); gtk_widget_set_tooltip_markup(GTK_WIDGET(scale), _("Dynamic Range Compression:\n" "Adjust the dynamic range of the output audio track.\n" "For source audio that has a wide dynamic range,\n" "very loud and very soft sequences, DRC allows you\n" "to 'compress' the range by making loud sounds\n" "softer and soft sounds louder.\n")); gtk_widget_set_valign(GTK_WIDGET(scale), GTK_ALIGN_CENTER); gtk_widget_set_name(GTK_WIDGET(scale), "AudioTrackDRCSlider"); gtk_widget_show(GTK_WIDGET(scale)); g_signal_connect(scale, "value-changed", (GCallback)audio_def_drc_changed_cb, ud); gtk_box_pack_start(box3, GTK_WIDGET(scale), FALSE, FALSE, 0); // Audio DRC Label label = GTK_LABEL(gtk_label_new(_("Off"))); gtk_label_set_width_chars(label, 4); gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_START); gtk_widget_set_valign(GTK_WIDGET(label), GTK_ALIGN_CENTER); gtk_widget_set_name(GTK_WIDGET(label), "AudioTrackDRCValue"); gtk_widget_show(GTK_WIDGET(label)); gtk_box_pack_start(box3, GTK_WIDGET(label), FALSE, FALSE, 0); gtk_box_pack_start(box2, GTK_WIDGET(box3), FALSE, FALSE, 0); // Remove button image = GTK_IMAGE(gtk_image_new_from_icon_name("hb-remove", GTK_ICON_SIZE_BUTTON)); button = GTK_BUTTON(gtk_button_new()); gtk_button_set_image(button, GTK_WIDGET(image)); gtk_widget_set_tooltip_markup(GTK_WIDGET(button), _("Remove this audio encoder")); gtk_button_set_relief(button, GTK_RELIEF_NONE); gtk_widget_set_valign(GTK_WIDGET(button), GTK_ALIGN_CENTER); gtk_widget_set_halign(GTK_WIDGET(button), GTK_ALIGN_END); gtk_widget_set_name(GTK_WIDGET(button), "remove_button"); gtk_widget_show(GTK_WIDGET(button)); g_signal_connect(button, "clicked", (GCallback)audio_def_setting_remove_cb, ud); gtk_box_pack_start(box2, GTK_WIDGET(button), TRUE, TRUE, 0); gtk_widget_show(GTK_WIDGET(box2)); gtk_box_pack_start(box, GTK_WIDGET(box2), TRUE, TRUE, 0); gtk_widget_show(GTK_WIDGET(box)); GtkWidget *widget; int width; widget = find_widget(GTK_WIDGET(box), "AudioEncoder"); gtk_widget_get_preferred_width(widget, NULL, &width); widget = GHB_WIDGET(ud->builder, "audio_defaults_encoder_label"); gtk_widget_set_size_request(widget, width, -1); widget = find_widget(GTK_WIDGET(box), "br_q_box"); gtk_widget_get_preferred_width(widget, NULL, &width); widget = GHB_WIDGET(ud->builder, "audio_defaults_bitrate_label"); gtk_widget_set_size_request(widget, width, -1); widget = find_widget(GTK_WIDGET(box), "AudioMixdown"); gtk_widget_get_preferred_width(widget, NULL, &width); widget = GHB_WIDGET(ud->builder, "audio_defaults_mixdown_label"); gtk_widget_set_size_request(widget, width, -1); widget = find_widget(GTK_WIDGET(box), "AudioSamplerate"); gtk_widget_get_preferred_width(widget, NULL, &width); widget = GHB_WIDGET(ud->builder, "audio_defaults_samplerate_label"); gtk_widget_set_size_request(widget, width, -1); widget = find_widget(GTK_WIDGET(box), "gain_box"); gtk_widget_get_preferred_width(widget, NULL, &width); widget = GHB_WIDGET(ud->builder, "audio_defaults_gain_label"); gtk_widget_set_size_request(widget, width, -1); widget = find_widget(GTK_WIDGET(box), "drc_box"); gtk_widget_get_preferred_width(widget, NULL, &width); widget = GHB_WIDGET(ud->builder, "audio_defaults_drc_label"); gtk_widget_set_size_request(widget, width, -1); return GTK_WIDGET(box); } static void audio_def_setting_update(signal_user_data_t *ud, GtkWidget *widget) { GtkListBoxRow *row = audio_settings_get_row(widget); gint index = gtk_list_box_row_get_index(row); GValue *alist = ghb_settings_get_value(ud->settings, "AudioList"); int count = ghb_array_len(alist); if (!block_updates && index >= 0 && index < count) { GValue *adict = ghb_array_get_nth(alist, index); ghb_widget_to_setting(adict, widget); } } G_MODULE_EXPORT void audio_add_lang_clicked_cb(GtkWidget *widget, signal_user_data_t *ud) { GtkListBox *avail = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "audio_avail_lang")); GtkListBox *selected = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "audio_selected_lang")); GtkListBoxRow *row; GtkWidget *label; row = gtk_list_box_get_selected_row(avail); if (row != NULL) { int idx; const iso639_lang_t *lang; GValue *glang, *alang_list; // Remove from UI available language list box label = gtk_bin_get_child(GTK_BIN(row)); g_object_ref(G_OBJECT(label)); gtk_widget_destroy(GTK_WIDGET(row)); gtk_widget_show(label); // Add to UI selected language list box gtk_list_box_insert(selected, label, -1); // Add to preset language list idx = (intptr_t)g_object_get_data(G_OBJECT(label), "lang_idx"); lang = ghb_iso639_lookup_by_int(idx); glang = ghb_string_value_new(lang->iso639_2); alang_list = ghb_settings_get_value(ud->settings, "AudioLanguageList"); ghb_array_append(alang_list, glang); ghb_clear_presets_selection(ud); } } G_MODULE_EXPORT void audio_remove_lang_clicked_cb(GtkWidget *widget, signal_user_data_t *ud) { GtkListBox *avail, *selected; GtkListBoxRow *row; GtkWidget *label; avail = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "audio_avail_lang")); selected = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "audio_selected_lang")); row = gtk_list_box_get_selected_row(selected); if (row != NULL) { gint index; GValue *alang_list; index = gtk_list_box_row_get_index(row); // Remove from UI selected language list box label = gtk_bin_get_child(GTK_BIN(row)); g_object_ref(G_OBJECT(label)); int idx = (intptr_t)g_object_get_data(G_OBJECT(label), "lang_idx"); gtk_widget_destroy(GTK_WIDGET(row)); gtk_widget_show(label); // Add to UI available language list box gtk_list_box_insert(avail, label, idx); // Remove from preset language list alang_list = ghb_settings_get_value(ud->settings, "AudioLanguageList"); GValue *glang = ghb_array_get_nth(alang_list, index); ghb_array_remove(alang_list, index); ghb_value_free(glang); ghb_clear_presets_selection(ud); } } static void audio_quality_update_limits( GtkWidget *widget, int encoder, gboolean set_default, gdouble value) { float low, high, gran; int dir; hb_audio_quality_get_limits(encoder, &low, &high, &gran, &dir); if (set_default) { value = hb_audio_quality_get_default(encoder); if (dir) { // Quality values are inverted value = high - value + low; } } GtkScaleButton *sb; GtkAdjustment *adj; sb = GTK_SCALE_BUTTON(widget); adj = gtk_scale_button_get_adjustment(sb); gtk_adjustment_configure (adj, value, low, high, gran, gran * 10, 0); } void audio_def_set_limits(signal_user_data_t *ud, GtkWidget *widget, gboolean set_default) { GtkListBoxRow *row = audio_settings_get_row(widget); gint index = gtk_list_box_row_get_index(row); GValue *alist = ghb_settings_get_value(ud->settings, "AudioList"); int count = ghb_array_len(alist); if (index < 0 || index >= count) return; GValue *adict = ghb_array_get_nth(alist, index); int codec = ghb_settings_audio_encoder_codec(adict, "AudioEncoder"); int fallback = ghb_settings_audio_encoder_codec(ud->settings, "AudioEncoderFallback"); gdouble quality = ghb_settings_get_double(adict, "AudioTrackQuality"); // Allow quality settings if the current encoder supports quality // or if the encoder is auto-passthru and the fallback encoder // supports quality. gboolean sensitive = hb_audio_quality_get_default(codec) != HB_INVALID_AUDIO_QUALITY || (codec == HB_ACODEC_AUTO_PASS && hb_audio_quality_get_default(fallback) != HB_INVALID_AUDIO_QUALITY); audio_def_settings_quality_set_sensitive(GTK_WIDGET(row), sensitive); int enc; if (sensitive) { enc = codec; if (hb_audio_quality_get_default(codec) == HB_INVALID_AUDIO_QUALITY) { enc = fallback; } audio_quality_update_limits(find_widget(GTK_WIDGET(row), "AudioTrackQuality"), enc, set_default, quality); } enc = codec; if (enc & HB_ACODEC_PASS_FLAG) { enc = ghb_select_fallback(ud->settings, enc); } int sr = ghb_settings_audio_samplerate_rate(adict, "AudioSamplerate"); if (sr == 0) { sr = 48000; } int mix = ghb_settings_mixdown_mix(adict, "AudioMixdown"); int low, high; hb_audio_bitrate_get_limits(enc, sr, mix, &low, &high); GtkWidget *w = find_widget(GTK_WIDGET(row), "AudioBitrate"); ghb_audio_bitrate_opts_filter(GTK_COMBO_BOX(w), low, high); w = find_widget(GTK_WIDGET(row), "AudioMixdown"); ghb_mix_opts_filter(GTK_COMBO_BOX(w), enc); } void audio_def_set_all_limits_cb(GtkWidget *widget, gpointer data) { signal_user_data_t *ud = (signal_user_data_t*)data; audio_def_set_limits(ud, widget, FALSE); } void audio_def_set_all_limits(signal_user_data_t *ud) { GtkListBox *list_box; list_box = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "audio_list_default")); gtk_container_foreach(GTK_CONTAINER(list_box), audio_def_set_all_limits_cb, (gpointer)ud); } G_MODULE_EXPORT void audio_def_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); ghb_clear_presets_selection(ud); } G_MODULE_EXPORT void audio_fallback_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); audio_def_set_all_limits(ud); ghb_clear_presets_selection(ud); } G_MODULE_EXPORT void audio_def_quality_enable_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { audio_def_setting_update(ud, widget); GtkListBoxRow *row = audio_settings_get_row(widget); gint index = gtk_list_box_row_get_index(row); GValue *alist = ghb_settings_get_value(ud->settings, "AudioList"); GValue *adict = ghb_array_get_nth(alist, index); audio_def_settings_bitrate_show(GTK_WIDGET(row), !ghb_settings_get_boolean(adict, "AudioTrackQualityEnable")); ghb_clear_presets_selection(ud); } G_MODULE_EXPORT void audio_def_quality_changed_cb(GtkWidget *widget, gdouble quality, signal_user_data_t *ud) { audio_def_setting_update(ud, widget); GtkListBoxRow *row = audio_settings_get_row(widget); GtkWidget *quality_label = find_widget(GTK_WIDGET(row), "AudioTrackQualityValue"); gint index = gtk_list_box_row_get_index(row); GValue *alist = ghb_settings_get_value(ud->settings, "AudioList"); GValue *adict = ghb_array_get_nth(alist, index); char *s_quality = get_quality_string(adict, quality); ghb_update_widget(quality_label, ghb_string_value(s_quality)); g_free(s_quality); ghb_clear_presets_selection(ud); } G_MODULE_EXPORT void audio_def_gain_changed_cb(GtkWidget *widget, gdouble gain, signal_user_data_t *ud) { audio_def_setting_update(ud, widget); GtkListBoxRow *row = audio_settings_get_row(widget); GtkWidget *gain_label = find_widget(GTK_WIDGET(row), "AudioTrackGainValue"); char *s_gain = get_gain_string(gain); ghb_update_widget(gain_label, ghb_string_value(s_gain)); g_free(s_gain); ghb_clear_presets_selection(ud); } G_MODULE_EXPORT void audio_def_drc_changed_cb(GtkWidget *widget, gdouble drc, signal_user_data_t *ud) { audio_def_setting_update(ud, widget); GtkListBoxRow *row = audio_settings_get_row(widget); GtkWidget *drc_label = find_widget(GTK_WIDGET(row), "AudioTrackDRCValue"); char *s_drc = get_drc_string(drc); ghb_update_widget(drc_label, ghb_string_value(s_drc)); g_free(s_drc); ghb_clear_presets_selection(ud); } G_MODULE_EXPORT void audio_def_setting_add_cb(GtkWidget *widget, signal_user_data_t *ud) { GtkListBoxRow *row = audio_settings_get_row(widget); GValue *adict; GValue *alist = ghb_settings_get_value(ud->settings, "AudioList"); int count = ghb_array_len(alist); if (count > 0) { // Use first item in list as defaults for new entries. adict = ghb_value_dup(ghb_array_get_nth(alist, 0)); audio_def_update_widgets(GTK_WIDGET(row), adict); } else { // Use hard coded defaults adict = ghb_dict_value_new(); audio_def_settings_init_row(adict, GTK_WIDGET(row)); } ghb_array_append(alist, adict); audio_def_settings_show(GTK_WIDGET(row), TRUE); // Add new "Add" button widget = ghb_create_audio_settings_row(ud); audio_def_settings_show(widget, FALSE); GtkListBox *list_box; list_box = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "audio_list_default")); gtk_list_box_insert(list_box, widget, -1); ghb_clear_presets_selection(ud); } G_MODULE_EXPORT void audio_def_setting_remove_cb(GtkWidget *widget, signal_user_data_t *ud) { GtkListBoxRow *row = audio_settings_get_row(widget); gint index = gtk_list_box_row_get_index(row); GValue *alist = ghb_settings_get_value(ud->settings, "AudioList"); int count = ghb_array_len(alist); if (index < 0 || index >= count) { return; } gtk_widget_destroy(GTK_WIDGET(row)); GValue *asettings = ghb_array_get_nth(alist, index); ghb_array_remove(alist, index); ghb_value_free(asettings); ghb_clear_presets_selection(ud); } G_MODULE_EXPORT void audio_def_encoder_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { audio_def_setting_update(ud, widget); audio_def_set_limits(ud, widget, TRUE); ghb_clear_presets_selection(ud); } G_MODULE_EXPORT void audio_def_encode_setting_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { audio_def_setting_update(ud, widget); audio_def_set_limits(ud, widget, FALSE); ghb_clear_presets_selection(ud); } GtkListBoxRow* ghb_find_lang_row(GtkListBox *list_box, int lang_idx) { GList *list, *link; GtkListBoxRow *result = NULL; list = link = gtk_container_get_children(GTK_CONTAINER(list_box)); while (link != NULL) { GtkListBoxRow *row = (GtkListBoxRow*)link->data; GtkWidget *label = gtk_bin_get_child(GTK_BIN(row)); int idx = (intptr_t)g_object_get_data(G_OBJECT(label), "lang_idx"); if (idx == lang_idx) { result = row; break; } link = link->next; } g_list_free(list); return result; } static void audio_def_lang_list_clear_cb(GtkWidget *row, gpointer data) { GtkListBox *avail = (GtkListBox*)data; GtkWidget *label = gtk_bin_get_child(GTK_BIN(row)); g_object_ref(G_OBJECT(label)); gtk_widget_destroy(GTK_WIDGET(row)); gtk_widget_show(label); int idx = (intptr_t)g_object_get_data(G_OBJECT(label), "lang_idx"); gtk_list_box_insert(avail, label, idx); } static void audio_def_selected_lang_list_clear(signal_user_data_t *ud) { GtkListBox *avail, *selected; avail = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "audio_avail_lang")); selected = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "audio_selected_lang")); gtk_container_foreach(GTK_CONTAINER(selected), audio_def_lang_list_clear_cb, (gpointer)avail); } static void audio_def_lang_list_init(signal_user_data_t *ud) { GValue *lang_list; // Clear selected languages. audio_def_selected_lang_list_clear(ud); lang_list = ghb_settings_get_value(ud->settings, "AudioLanguageList"); if (lang_list == NULL) { lang_list = ghb_array_value_new(8); ghb_settings_set_value(ud->settings, "AudioLanguageList", lang_list); } int ii, count; count = ghb_array_len(lang_list); for (ii = 0; ii < count; ) { GValue *lang_val = ghb_array_get_nth(lang_list, ii); int idx = ghb_lookup_audio_lang(lang_val); GtkListBox *avail, *selected; GtkListBoxRow *row; avail = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "audio_avail_lang")); selected = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "audio_selected_lang")); row = ghb_find_lang_row(avail, idx); if (row) { GtkWidget *label = gtk_bin_get_child(GTK_BIN(row)); g_object_ref(G_OBJECT(label)); gtk_widget_destroy(GTK_WIDGET(row)); gtk_widget_show(label); gtk_list_box_insert(selected, label, -1); ii++; } else { // Error in list. Probably duplicate languages. Remove // this item from the list. GValue *glang = ghb_array_get_nth(lang_list, ii); ghb_array_remove(lang_list, ii); ghb_value_free(glang); count--; } } } void ghb_audio_defaults_to_ui(signal_user_data_t *ud) { GtkListBox *list_box; GValue *alist; int count, ii; audio_def_lang_list_init(ud); // Init the AudioList settings if necessary alist = ghb_settings_get_value(ud->settings, "AudioList"); if (alist == NULL) { alist = ghb_array_value_new(8); ghb_settings_set_value(ud->settings, "AudioList", alist); } // Empty the current list list_box = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "audio_list_default")); ghb_container_empty(GTK_CONTAINER(list_box)); GtkWidget *widget; // Populate with new values count = ghb_array_len(alist); for (ii = 0; ii < count; ii++) { GValue *adict; adict = ghb_array_get_nth(alist, ii); widget = ghb_create_audio_settings_row(ud); gtk_list_box_insert(list_box, widget, -1); audio_def_update_widgets(widget, adict); } // Add row with "Add" button widget = ghb_create_audio_settings_row(ud); audio_def_settings_show(widget, FALSE); gtk_list_box_insert(list_box, widget, -1); } void ghb_init_audio_defaults_ui(signal_user_data_t *ud) { GtkListBox *list_box; list_box = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "audio_avail_lang")); ghb_init_lang_list_box(list_box); } HandBrake-0.10.2/gtk/src/ghbcompositor.h0000664000175200017520000000512612300772602020446 0ustar handbrakehandbrake/* "Borrowed" from: */ /* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ #ifndef __GHB_COMPOSITOR_H__ #define __GHB_COMPOSITOR_H__ #include G_BEGIN_DECLS #define GHB_TYPE_COMPOSITOR (ghb_compositor_get_type ()) #define GHB_COMPOSITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GHB_TYPE_COMPOSITOR, GhbCompositor)) #define GHB_COMPOSITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GHB_TYPE_COMPOSITOR, GhbCompositorClass)) #define GHB_IS_COMPOSITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GHB_TYPE_COMPOSITOR)) #define GHB_IS_COMPOSITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GHB_TYPE_COMPOSITOR)) #define GHB_COMPOSITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GHB_TYPE_COMPOSITOR, GhbCompositorClass)) typedef struct _GhbCompositor GhbCompositor; typedef struct _GhbCompositorClass GhbCompositorClass; typedef struct _GhbCompositorChild GhbCompositorChild; struct _GhbCompositor { GtkBin bin; GList *children; }; struct _GhbCompositorClass { GtkBinClass parent_class; }; struct _GhbCompositorChild { GtkWidget *widget; GList *drawables; guint z_pos; gdouble opacity; }; GType ghb_compositor_get_type (void) G_GNUC_CONST; GtkWidget* ghb_compositor_new (void); void ghb_compositor_zlist_insert (GhbCompositor *compositor, GtkWidget *child, gint z_pos, gdouble opacity); G_END_DECLS #endif /* __GHB_COMPOSITOR_H__ */ HandBrake-0.10.2/gtk/src/renderer_button.h0000664000175200017520000000325212067154516020776 0ustar handbrakehandbrake#ifndef _RENDERER_BUTTON_H_ #define _RENDERER_BUTTON_H_ #include /* Some boilerplate GObject type check and type cast macros. * 'klass' is used here instead of 'class', because 'class' * is a c++ keyword */ #define CUSTOM_TYPE_CELL_RENDERER_BUTTON (custom_cell_renderer_button_get_type()) #define CUSTOM_CELL_RENDERER_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), CUSTOM_TYPE_CELL_RENDERER_BUTTON, CustomCellRendererButton)) #define CUSTOM_CELL_RENDERER_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CUSTOM_TYPE_CELL_RENDERER_BUTTON, CustomCellRendererButtonClass)) #define CUSTOM_IS_CELL_BUTTON_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CUSTOM_TYPE_CELL_RENDERER_BUTTON)) #define CUSTOM_IS_CELL_BUTTON_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CUSTOM_TYPE_CELL_RENDERER_BUTTON)) #define CUSTOM_CELL_RENDERER_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CUSTOM_TYPE_CELL_RENDERER_BUTTON, CustomCellRendererButtonClass)) typedef struct _CustomCellRendererButton CustomCellRendererButton; typedef struct _CustomCellRendererButtonClass CustomCellRendererButtonClass; /* CustomCellRendererProgress: Our custom cell renderer * structure. Extend according to need */ struct _CustomCellRendererButton { GtkCellRendererPixbuf parent; }; struct _CustomCellRendererButtonClass { GtkCellRendererPixbufClass parent_class; void (* clicked) (CustomCellRendererButton *cell_renderer_button, const gchar *path); }; GType custom_cell_renderer_button_get_type (void); GtkCellRenderer *custom_cell_renderer_button_new (void); #endif // _RENDERER_BUTTON_H_ HandBrake-0.10.2/gtk/src/callbacks.h0000664000175200017520000000671312465416500017515 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * callbacks.h * Copyright (C) John Stebbins 2008-2015 * * callbacks.h is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * callbacks.h is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with callbacks.h. If not, write to: * The Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301, USA. */ #if !defined(_CALLBACKS_H_) #define _CALLBACKS_H_ #if defined(_WIN32) #include #endif #include #include "hb.h" #include "settings.h" #if GLIB_CHECK_VERSION(2, 32, 0) #define GHB_THREAD_NEW(n, f, p) \ g_thread_new(n, (GThreadFunc)(f), (p)) #else #define GHB_THREAD_NEW(n, f, p) \ g_thread_create((GThreadFunc)(f), (p), TRUE, NULL) #endif void ghb_check_all_depencencies(signal_user_data_t *ud); gboolean ghb_timer_cb(gpointer data); gboolean ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data); void warn_log_handler( const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer ud); void debug_log_handler( const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer ud); void ghb_hbfd(signal_user_data_t *ud, gboolean hbfd); gboolean ghb_file_menu_add_dvd(signal_user_data_t *ud); void ghb_udev_init(void); gboolean ghb_message_dialog( GtkMessageType type, const gchar *message, const gchar *no, const gchar *yes); void ghb_error_dialog( GtkMessageType type, const gchar *message, const gchar *cancel); void ghb_init_dep_map(void); void ghb_cancel_encode(signal_user_data_t *ud, const gchar *extra_msg); gboolean ghb_cancel_encode2(signal_user_data_t *ud, const gchar *extra_msg); GValue* ghb_start_next_job(signal_user_data_t *ud); void ghb_check_dependency( signal_user_data_t *ud, GtkWidget *widget, const gchar *alt_name); void ghb_do_scan( signal_user_data_t *ud, const gchar *filename, gint titlenum, gboolean force); void ghb_log(gchar *log, ...); gpointer ghb_check_update(signal_user_data_t *ud); void ghb_uninhibit_gsm(void); void ghb_inhibit_gsm(signal_user_data_t *ud); #if defined(_WIN32) void wm_drive_changed(MSG *msg, signal_user_data_t *ud); #endif gpointer ghb_cache_volnames(signal_user_data_t *ud); void ghb_volname_cache_init(void); void ghb_update_destination_extension(signal_user_data_t *ud); void ghb_update_pending(signal_user_data_t *ud); gboolean ghb_idle_scan(signal_user_data_t *ud); void ghb_add_all_titles(signal_user_data_t *ud); void ghb_update_title_info(signal_user_data_t *ud); void ghb_chapter_list_refresh_all(signal_user_data_t *ud); void ghb_load_settings(signal_user_data_t * ud); void ghb_set_current_title_settings(signal_user_data_t *ud); void ghb_container_empty(GtkContainer *c); void ghb_show_container_options(signal_user_data_t *ud); void ghb_scale_configure(signal_user_data_t *ud, char *name, double val, double min, double max, double step, double page, int digits, gboolean inverted); #endif // _CALLBACKS_H_ HandBrake-0.10.2/gtk/src/create_resources.c0000664000175200017520000002705612311644756021137 0ustar handbrakehandbrake#include #include #include #include #include #include #include "plist.h" #include "values.h" #include #include enum { R_NONE = 0, R_RESOURCES, R_SECTION, R_ICON, R_PLIST, R_STRING, }; typedef struct { gchar *tag; gint id; } tag_map_t; static tag_map_t tag_map[] = { {"resources", R_RESOURCES}, {"section", R_SECTION}, {"icon", R_ICON}, {"plist", R_PLIST}, {"string", R_STRING}, }; #define TAG_MAP_SZ (sizeof(tag_map)/sizeof(tag_map_t)) typedef struct { gchar *key; gchar *value; GValue *plist; GQueue *stack; GQueue *tag_stack; gboolean closed_top; } parse_data_t; GList *inc_list = NULL; static gchar* find_file(GList *list, const gchar *name) { gchar *str; GList *link = list; while (link != NULL) { gchar *inc; inc = (gchar*)link->data; str = g_strdup_printf("%s/%s", inc, name); if (g_file_test(str, G_FILE_TEST_IS_REGULAR)) { return str; } g_free(str); link = g_list_next(link); } if (g_file_test(name, G_FILE_TEST_IS_REGULAR)) { return g_strdup(name); } return NULL; } static const gchar* lookup_attr_value( const gchar *name, const gchar **attr_names, const gchar **attr_values) { gint ii; if (attr_names == NULL) return NULL; for (ii = 0; attr_names[ii] != NULL; ii++) { if (strcmp(name, attr_names[ii]) == 0) return attr_values[ii]; } return NULL; } static GValue* read_string_from_file(const gchar *fname) { gchar *buffer; size_t size; GValue *gval; FILE *fd; fd = g_fopen(fname, "r"); if (fd == NULL) return NULL; fseek(fd, 0, SEEK_END); size = ftell(fd); fseek(fd, 0, SEEK_SET); buffer = g_malloc(size+1); size = fread(buffer, 1, size, fd); buffer[size] = 0; gval = ghb_value_new(G_TYPE_STRING); g_value_take_string(gval, buffer); fclose(fd); return gval; } static void add_icon(GValue *dict, const char *fname) { FILE *f; GdkPixbufFormat *pbf; int width, height; gboolean svg; pbf = gdk_pixbuf_get_file_info(fname, &width, &height); svg = gdk_pixbuf_format_is_scalable(pbf); f = fopen(fname, "rb"); if (f == NULL) { fprintf(stderr, "open failed: %s\n", fname); return; } ghb_rawdata_t *rd; rd = g_malloc(sizeof(ghb_rawdata_t)); fseek(f, 0, SEEK_END); rd->size = ftell(f); fseek(f, 0, SEEK_SET); rd->data = g_malloc(rd->size); fread(rd->data, 1, rd->size, f); GValue *data = ghb_rawdata_value_new(rd); ghb_dict_insert(dict, g_strdup("svg"), ghb_boolean_value_new(svg)); ghb_dict_insert(dict, g_strdup("data"), data); } static void insert_value(GValue *container, const char *key, GValue *element) { GType gtype; gtype = G_VALUE_TYPE(container); if (gtype == ghb_array_get_type()) { ghb_array_append(container, element); } else if (gtype == ghb_dict_get_type()) { if (key == NULL) { g_warning("No key for dictionary item"); ghb_value_free(element); } else { ghb_dict_insert(container, g_strdup(key), element); } } else { g_error("Invalid container type. This shouldn't happen"); } } static void start_element( GMarkupParseContext *ctx, const gchar *tag, const gchar **attr_names, const gchar **attr_values, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; union { gint id; gpointer pid; } id; gint ii; // Check to see if the first element found has been closed // If so, ignore any junk following it. if (pd->closed_top) return; for (ii = 0; ii < TAG_MAP_SZ; ii++) { if (strcmp(tag, tag_map[ii].tag) == 0) { id.id = tag_map[ii].id; break; } } if (ii == TAG_MAP_SZ) { g_warning("Unrecognized start tag (%s)", tag); return; } g_queue_push_head(pd->tag_stack, id.pid); GType gtype = 0; GValue *gval = NULL; GValue *current = g_queue_peek_head(pd->stack); switch (id.id) { case R_SECTION: { const gchar *name; name = lookup_attr_value("name", attr_names, attr_values); if (name && strcmp(name, "icons") == 0) { gval = ghb_dict_value_new(); if (pd->key) g_free(pd->key); pd->key = g_strdup(name); g_queue_push_head(pd->stack, gval); } } break; case R_ICON: { gchar *fname; const gchar *name; name = lookup_attr_value("file", attr_names, attr_values); fname = find_file(inc_list, name); name = lookup_attr_value("name", attr_names, attr_values); if (fname && name) { gval = ghb_dict_value_new(); add_icon(gval, fname); g_free(pd->key); pd->key = g_strdup(name); g_free(fname); } else { g_warning("%s:missing a requried attribute", name); exit(EXIT_FAILURE); } } break; case R_PLIST: { gchar *fname; const gchar *name; name = lookup_attr_value("file", attr_names, attr_values); fname = find_file(inc_list, name); name = lookup_attr_value("name", attr_names, attr_values); if (fname && name) { gval = ghb_plist_parse_file(fname); if (pd->key) g_free(pd->key); pd->key = g_strdup(name); g_free(fname); } else { g_warning("%s:missing a requried attribute", name); exit(EXIT_FAILURE); } } break; case R_STRING: { gchar *fname; const gchar *name; name = lookup_attr_value("file", attr_names, attr_values); fname = find_file(inc_list, name); name = lookup_attr_value("name", attr_names, attr_values); if (fname && name) { gval = read_string_from_file(fname); if (pd->key) g_free(pd->key); pd->key = g_strdup(name); g_free(fname); } else { g_warning("%s:missing a requried attribute", name); exit(EXIT_FAILURE); } } break; } // Add the element to the current container if (gval) { // There's an element to add if (current == NULL) { pd->plist = gval; return; } insert_value(current, pd->key, gval); } } static void end_element( GMarkupParseContext *ctx, const gchar *name, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; gint id; union { gint id; gpointer pid; } start_id; gint ii; // Check to see if the first element found has been closed // If so, ignore any junk following it. if (pd->closed_top) return; for (ii = 0; ii < TAG_MAP_SZ; ii++) { if (strcmp(name, tag_map[ii].tag) == 0) { id = tag_map[ii].id; break; } } if (ii == TAG_MAP_SZ) { g_warning("Unrecognized start tag (%s)", name); return; } start_id.pid = g_queue_pop_head(pd->tag_stack); if (start_id.id != id) g_warning("start tag != end tag: (%s %d) %d", name, id, id); GValue *gval = NULL; GValue *current = g_queue_peek_head(pd->stack); GType gtype = 0; switch (id) { case R_SECTION: { g_queue_pop_head(pd->stack); } break; } if (gval) { // Get the top of the data structure stack and if it's an array // or dict, add the current element if (current == NULL) { pd->plist = gval; pd->closed_top = TRUE; return; } insert_value(current, pd->key, gval); } if (g_queue_is_empty(pd->tag_stack)) pd->closed_top = TRUE; } static void text_data( GMarkupParseContext *ctx, const gchar *text, gsize len, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; if (pd->value) g_free(pd->value); pd->value = g_strdup(text); } static void passthrough( GMarkupParseContext *ctx, const gchar *text, gsize len, gpointer ud, GError **error) { //parse_data_t *pd = (parse_data_t*)ud; //g_debug("passthrough %s", text); } static void parse_error(GMarkupParseContext *ctx, GError *error, gpointer ud) { g_warning("Resource parse error: %s", error->message); } // This is required or the parser crashes static void destroy_notify(gpointer data) { // Do nothing //g_debug("destroy parser"); } GValue* ghb_resource_parse(const gchar *buf, gssize len) { GMarkupParseContext *ctx; GMarkupParser parser; parse_data_t pd; GError *err = NULL; pd.stack = g_queue_new(); pd.tag_stack = g_queue_new(); pd.key = NULL; pd.value = NULL; pd.plist = ghb_dict_value_new(); g_queue_push_head(pd.stack, pd.plist); pd.closed_top = FALSE; parser.start_element = start_element; parser.end_element = end_element; parser.text = text_data; parser.passthrough = passthrough; parser.error = parse_error; ctx = g_markup_parse_context_new(&parser, 0, &pd, destroy_notify); g_markup_parse_context_parse(ctx, buf, len, &err); g_markup_parse_context_end_parse(ctx, &err); g_markup_parse_context_free(ctx); g_queue_free(pd.stack); g_queue_free(pd.tag_stack); return pd.plist; } GValue* ghb_resource_parse_file(FILE *fd) { gchar *buffer; size_t size; GValue *gval; if (fd == NULL) return NULL; fseek(fd, 0, SEEK_END); size = ftell(fd); fseek(fd, 0, SEEK_SET); buffer = g_malloc(size+1); size = fread(buffer, 1, size, fd); buffer[size] = 0; gval = ghb_resource_parse(buffer, (gssize)size); g_free(buffer); return gval; } static void usage(char *cmd) { fprintf(stderr, "Usage: %s [-I ] \n" "Summary:\n" " Creates a resource plist from a resource list\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" , cmd); exit(EXIT_FAILURE); } #define OPTS "I:" gint main(gint argc, gchar *argv[]) { FILE *file; GValue *gval; int opt; const gchar *src, *dst; do { opt = getopt(argc, argv, OPTS); switch (opt) { case -1: break; case 'I': inc_list = g_list_prepend(inc_list, g_strdup(optarg)); break; } } while (opt != -1); if (optind != argc - 2) { usage(argv[0]); return EXIT_FAILURE; } src = argv[optind++]; dst = argv[optind++]; #if !GLIB_CHECK_VERSION(2, 36, 0) g_type_init(); #endif file = g_fopen(src, "r"); if (file == NULL) { fprintf(stderr, "Error: failed to open %s\n", src); return EXIT_FAILURE; } gval = ghb_resource_parse_file(file); ghb_plist_write_file(dst, gval); fclose(file); return 0; } HandBrake-0.10.2/gtk/src/appcast.c0000664000175200017520000001332712465416500017223 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * appcast.c * Copyright (C) John Stebbins 2008-2015 * * appcast.c is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. */ #include #include #include #include #include "plist.h" #include "values.h" enum { A_NONE = 0, A_DESCRIPTION, A_ENCLOSURE, A_ITEM, }; typedef struct { gchar *tag; gint id; } tag_map_t; static tag_map_t tag_map[] = { {"sparkle:releaseNotesLink", A_DESCRIPTION}, {"enclosure", A_ENCLOSURE}, {"item", A_ITEM}, }; #define TAG_MAP_SZ (sizeof(tag_map)/sizeof(tag_map_t)) typedef struct { gchar *key; gchar *value; GQueue *stack; GQueue *tag_stack; GString *description; gchar *build; gchar *version; gboolean item; } parse_data_t; static const gchar* lookup_attr_value( const gchar *name, const gchar **attr_names, const gchar **attr_values) { gint ii; if (attr_names == NULL) return NULL; for (ii = 0; attr_names[ii] != NULL; ii++) { if (strcmp(name, attr_names[ii]) == 0) return attr_values[ii]; } return NULL; } static void start_element( GMarkupParseContext *ctx, const gchar *tag, const gchar **attr_names, const gchar **attr_values, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; union { gint id; gpointer pid; } id; gint ii; for (ii = 0; ii < TAG_MAP_SZ; ii++) { if (strcmp(tag, tag_map[ii].tag) == 0) { id.id = tag_map[ii].id; break; } } if (ii == TAG_MAP_SZ) { g_debug("Unrecognized start tag (%s)", tag); id.id = A_NONE; } g_queue_push_head(pd->tag_stack, id.pid); switch (id.id) { case A_ITEM: { pd->item = TRUE; } break; case A_ENCLOSURE: { const gchar *build, *version; build = lookup_attr_value( "sparkle:version", attr_names, attr_values); version = lookup_attr_value( "sparkle:shortVersionString", attr_names, attr_values); if (build) pd->build = g_strdup(build); if (version) pd->version = g_strdup(version); } break; } } static void end_element( GMarkupParseContext *ctx, const gchar *tag, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; gint id; union { gint id; gpointer pid; } start_id; gint ii; for (ii = 0; ii < TAG_MAP_SZ; ii++) { if (strcmp(tag, tag_map[ii].tag) == 0) { id = tag_map[ii].id; break; } } if (ii == TAG_MAP_SZ) { g_debug("Unrecognized end tag (%s)", tag); id = A_NONE; } start_id.pid = g_queue_pop_head(pd->tag_stack); if (start_id.id != id) g_warning("start tag != end tag: (%s %d) %d", tag, start_id.id, id); switch (id) { case A_ITEM: { pd->item = FALSE; } break; default: { } break; } } static void text_data( GMarkupParseContext *ctx, const gchar *text, gsize len, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; union { gint id; gpointer pid; } start_id; start_id.pid = g_queue_peek_head(pd->tag_stack); switch (start_id.id) { case A_DESCRIPTION: { if (pd->item) { g_string_append(pd->description, text); } } break; default: { if (pd->value) g_free(pd->value); pd->value = g_strdup(text); } break; } } static void passthrough( GMarkupParseContext *ctx, const gchar *text, gsize len, gpointer ud, GError **error) { //parse_data_t *pd = (parse_data_t*)ud; //g_debug("passthrough %s", text); } static void parse_error(GMarkupParseContext *ctx, GError *error, gpointer ud) { g_warning("Resource parse error: %s", error->message); } // This is required or the parser crashes static void destroy_notify(gpointer data) { // Do nothing //g_debug("destroy parser"); } void ghb_appcast_parse(gchar *buf, gchar **desc, gchar **build, gchar **version) { GMarkupParseContext *ctx; GMarkupParser parser; parse_data_t pd; GError *err = NULL; gint len; gchar *start; //gchar tmp[4096] // Skip junk at beginning of buffer start = strstr(buf, ""); if (glitch) *glitch = 0; *build = pd.build; *version = pd.version; } HandBrake-0.10.2/gtk/src/videohandler.h0000664000175200017520000000215112465416500020232 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * audiohandler.h * Copyright (C) John Stebbins 2008-2015 * * audiohandler.h is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * callbacks.h is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with callbacks.h. If not, write to: * The Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301, USA. */ #if !defined(_VIDEOHANDLER_H_) #define _VIDEOHANDLER_H_ #include "settings.h" int ghb_get_video_encoder(GValue *settings); void ghb_video_setting_changed(GtkWidget *widget, signal_user_data_t *ud); #endif // _VIDEOHANDLER_H_ HandBrake-0.10.2/gtk/src/hb-start.svg0000664000175200017520000001334312311644756017675 0ustar handbrakehandbrake image/svg+xml HandBrake-0.10.2/gtk/src/presets.c0000664000175200017520000035260312505046453017261 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * presets.c * Copyright (C) John Stebbins 2008-2015 * * presets.c is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * */ #include #include #include #include #include #include #include #include #include #include "ghbcompat.h" #include "hb.h" #include "settings.h" #include "callbacks.h" #include "audiohandler.h" #include "subtitlehandler.h" #include "hb-backend.h" #include "plist.h" #include "resources.h" #include "presets.h" #include "values.h" #include "lang.h" #include "videohandler.h" #define MAX_NESTED_PRESET 3 enum { PRESETS_BUILTIN = 0, PRESETS_CUSTOM }; static GValue *presetsPlistFile = NULL; static GValue *presetsPlist = NULL; static GValue *prefsPlist = NULL; static gboolean prefs_modified = FALSE; static const GValue* preset_dict_get_value(GValue *dict, const gchar *key); static void store_plist(GValue *plist, const gchar *name); static void store_presets(void); static void store_prefs(void); static void dict_clean(GValue *dict, GValue *template) { GValue *tmp = ghb_value_dup(dict); GHashTableIter iter; gchar *key; GValue *value; GValue *template_val; ghb_dict_iter_init(&iter, tmp); // middle (void*) cast prevents gcc warning "defreferencing type-punned // pointer will break strict-aliasing rules" while (g_hash_table_iter_next( &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&value)) { template_val = ghb_dict_lookup(template, key); if (template_val == NULL) { ghb_dict_remove(dict, key); } } ghb_value_free(tmp); } gint preset_path_cmp(gint *indices1, gint len1, gint *indices2, gint len2) { gint ii; for (ii = 0; ii < len1 && ii < len2; ii++) { if (indices1[ii] != indices2[ii]) return indices1[ii] - indices2[ii]; } return len1 - len2; } // This only handle limited depth GtkTreePath* ghb_tree_path_new_from_indices(gint *indices, gint len) { switch (len) { case 1: return gtk_tree_path_new_from_indices( indices[0], -1); case 2: return gtk_tree_path_new_from_indices( indices[0], indices[1], -1); case 3: return gtk_tree_path_new_from_indices( indices[0], indices[1], indices[2], -1); case 4: return gtk_tree_path_new_from_indices( indices[0], indices[1], indices[2], indices[3], -1); case 5: return gtk_tree_path_new_from_indices( indices[0], indices[1], indices[2], indices[3], indices[4], -1); default: return NULL; } } GValue* ghb_parse_preset_path(const gchar *path) { gchar **split; GValue *preset; gint ii; preset = ghb_array_value_new(MAX_NESTED_PRESET); split = g_strsplit(path, "#", MAX_NESTED_PRESET); for (ii = 0; split[ii] != NULL; ii++) { ghb_array_append(preset, ghb_string_value_new(split[ii])); } g_strfreev(split); return preset; } static GValue* preset_path_from_indices(GValue *presets, gint *indices, gint len) { gint ii; GValue *path; g_debug("preset_path_from_indices"); path = ghb_array_value_new(MAX_NESTED_PRESET); for (ii = 0; ii < len; ii++) { GValue *dict; gint count, folder; const GValue *name; count = ghb_array_len(presets); if (indices[ii] >= count) break; dict = ghb_array_get_nth(presets, indices[ii]); name = ghb_dict_lookup(dict, "PresetName"); if (name) ghb_array_append(path, ghb_value_dup(name)); folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder")); if (!folder) break; presets = ghb_dict_lookup(dict, "ChildrenArray"); } return path; } gchar* ghb_preset_path_string(const GValue *path) { gint count, ii; GString *gstr; GValue *val; gchar *str; gstr = g_string_new(""); if (path != NULL) { count = ghb_array_len(path); for (ii = 0; ii < count; ii++) { val = ghb_array_get_nth(path, ii); str = ghb_value_string(val); g_string_append(gstr, str); if (ii < count-1) g_string_append(gstr, "->"); g_free(str); } } str = g_string_free(gstr, FALSE); return str; } void dump_preset_path(const gchar *msg, const GValue *path) { gchar *str; if (path) debug_show_type (G_VALUE_TYPE(path)); str = ghb_preset_path_string(path); g_message("%s path: (%s)", msg, str); g_free(str); } void dump_preset_indices(const gchar *msg, gint *indices, gint len) { gint ii; g_message("%s indices: len %d", msg, len); for (ii = 0; ii < len; ii++) { printf("%d ", indices[ii]); } printf("\n"); } #if 0 static gint preset_path_cmp(const GValue *path1, const GValue *path2) { gint count, ii; GValue *val; gchar *str1, *str2; gint result; count = ghb_array_len(path1); ii = ghb_array_len(path2); if (ii != count) return ii - count; for (ii = 0; ii < count; ii++) { val = ghb_array_get_nth(path1, ii); str1 = ghb_value_string(val); val = ghb_array_get_nth(path2, ii); str2 = ghb_value_string(val); result = strcmp(str1, str2); if (result != 0) return result; g_free(str1); g_free(str2); } return 0; } #endif static GValue* presets_get_dict(GValue *presets, gint *indices, gint len) { gint ii, count, folder; GValue *dict = NULL; g_debug("presets_get_dict ()"); for (ii = 0; ii < len; ii++) { count = ghb_array_len(presets); if (indices[ii] >= count) return NULL; dict = ghb_array_get_nth(presets, indices[ii]); if (ii < len-1) { folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder")); if (!folder) return NULL; presets = ghb_dict_lookup(dict, "ChildrenArray"); } } if (ii < len) return NULL; return dict; } static GValue* presets_get_folder(GValue *presets, gint *indices, gint len) { gint ii, count, folder; GValue *dict; g_debug("presets_get_folder ()"); for (ii = 0; ii < len; ii++) { count = ghb_array_len(presets); if (indices[ii] >= count) return NULL; dict = ghb_array_get_nth(presets, indices[ii]); folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder")); if (!folder) break; presets = ghb_dict_lookup(dict, "ChildrenArray"); } if (ii < len) return NULL; return presets; } static GValue* plist_get_dict(GValue *presets, const gchar *name) { if (presets == NULL || name == NULL) return NULL; return ghb_dict_lookup(presets, name); } static const gchar* preset_get_name(GValue *dict) { return g_value_get_string(preset_dict_get_value(dict, "PresetName")); } static gboolean preset_folder_is_open(GValue *dict) { const GValue *gval; gval = preset_dict_get_value(dict, "FolderOpen"); if (gval != NULL) return g_value_get_boolean(gval); return FALSE; } gboolean ghb_preset_folder(GValue *dict) { return ghb_value_int(preset_dict_get_value(dict, "Folder")); } gint ghb_preset_type(GValue *dict) { return ghb_value_int(preset_dict_get_value(dict, "Type")); } static void presets_remove_nth(GValue *presets, gint pos) { GValue *dict; gint count; if (presets == NULL || pos < 0) return; count = ghb_array_len(presets); if (pos >= count) return; dict = ghb_array_get_nth(presets, pos); ghb_array_remove(presets, pos); ghb_value_free(dict); } gboolean ghb_presets_remove( GValue *presets, gint *indices, gint len) { GValue *folder = NULL; folder = presets_get_folder(presets, indices, len-1); if (folder) presets_remove_nth(folder, indices[len-1]); else { g_warning("ghb_presets_remove (): internal preset lookup error"); return FALSE; } return TRUE; } static void ghb_presets_replace( GValue *presets, GValue *dict, gint *indices, gint len) { GValue *folder = NULL; folder = presets_get_folder(presets, indices, len-1); if (folder) ghb_array_replace(folder, indices[len-1], dict); else { g_warning("ghb_presets_replace (): internal preset lookup error"); } } static void ghb_presets_insert( GValue *presets, GValue *dict, gint *indices, gint len) { GValue *folder = NULL; folder = presets_get_folder(presets, indices, len-1); if (folder) ghb_array_insert(folder, indices[len-1], dict); else { g_warning("ghb_presets_insert (): internal preset lookup error"); } } static gint presets_find_element(GValue *presets, const gchar *name) { GValue *dict; gint count, ii; g_debug("presets_find_element () (%s)", name); if (presets == NULL || name == NULL) return -1; count = ghb_array_len(presets); for (ii = 0; ii < count; ii++) { const gchar *str; dict = ghb_array_get_nth(presets, ii); str = preset_get_name(dict); if (strcmp(name, str) == 0) { return ii; } } return -1; } static gint single_find_pos(GValue *presets, const gchar *name, gint type) { GValue *dict; gint count, ii, ptype, last; if (presets == NULL || name == NULL) return -1; last = count = ghb_array_len(presets); for (ii = 0; ii < count; ii++) { const gchar *str; dict = ghb_array_get_nth(presets, ii); str = preset_get_name(dict); ptype = ghb_value_int(preset_dict_get_value(dict, "Type")); if (strcasecmp(name, str) <= 0 && ptype == type) { return ii; } if (ptype == type) last = ii+1; } return last; } static gint* presets_find_pos(const GValue *path, gint type, gint *len) { GValue *nested; GValue *val; gint count, ii; gboolean folder; gint *indices = NULL; const gchar *name; GValue *dict; g_debug("presets_find_pos () "); nested = presetsPlist; count = ghb_array_len(path); indices = g_malloc(MAX_NESTED_PRESET * sizeof(gint)); for (ii = 0; ii < count-1; ii++) { val = ghb_array_get_nth(path, ii); name = g_value_get_string(val); indices[ii] = presets_find_element(nested, name); if (indices[ii] == -1) { g_free(indices); return NULL; } dict = ghb_array_get_nth(nested, indices[ii]); folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder")); nested = NULL; if (!folder) break; nested = ghb_dict_lookup(dict, "ChildrenArray"); } if (nested) { const gchar *name; name = g_value_get_string(ghb_array_get_nth(path, count-1)); indices[ii] = single_find_pos(nested, name, type); ii++; } *len = ii; return indices; } static gint preset_tree_depth(GValue *dict) { gboolean folder; folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder")); if (folder) { gint depth = 0; gint count, ii; GValue *presets; presets = ghb_dict_lookup(dict, "ChildrenArray"); count = ghb_array_len(presets); for (ii = 0; ii < count; ii++) { gint tmp; dict = ghb_array_get_nth(presets, ii); tmp = preset_tree_depth(dict); depth = MAX(depth, tmp); } return depth + 1; } else { return 1; } } static gboolean preset_is_default(GValue *dict) { const GValue *val; val = preset_dict_get_value(dict, "Default"); return ghb_value_boolean(val); } static void presets_clear_default(GValue *presets) { gint count, ii; count = ghb_array_len(presets); for (ii = 0; ii < count; ii++) { GValue *dict; gboolean folder; dict = ghb_array_get_nth(presets, ii); folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder")); if (folder) { GValue *nested; nested = ghb_dict_lookup(dict, "ChildrenArray"); presets_clear_default(nested); } else { if (preset_is_default(dict)) { ghb_dict_insert(dict, g_strdup("Default"), ghb_boolean_value_new(FALSE)); } } } } static void presets_customize(GValue *presets) { gint count, ii; count = ghb_array_len(presets); for (ii = 0; ii < count; ii++) { GValue *dict; gboolean folder; gint ptype; dict = ghb_array_get_nth(presets, ii); ptype = ghb_value_int(preset_dict_get_value(dict, "Type")); if (ptype != PRESETS_CUSTOM) { ghb_dict_insert(dict, g_strdup("Type"), ghb_int64_value_new(PRESETS_CUSTOM)); } folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder")); if (folder) { GValue *nested; nested = ghb_dict_lookup(dict, "ChildrenArray"); presets_customize(nested); } } } static gint* presets_find_default2(GValue *presets, gint *len) { gint count, ii; gint *indices; count = ghb_array_len(presets); for (ii = 0; ii < count; ii++) { GValue *dict; gboolean folder; dict = ghb_array_get_nth(presets, ii); folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder")); if (folder) { GValue *nested; gint pos = *len; nested = ghb_dict_lookup(dict, "ChildrenArray"); (*len)++; indices = presets_find_default2(nested, len); if (indices) { indices[pos] = ii; return indices; } else *len = pos; } else { if (preset_is_default(dict)) { indices = g_malloc(MAX_NESTED_PRESET * sizeof(gint)); indices[*len] = ii; (*len)++; return indices; } } } return NULL; } static gint* presets_find_default(GValue *presets, gint *len) { *len = 0; return presets_find_default2(presets, len); } gint* ghb_preset_indices_from_path( GValue *presets, const GValue *path, gint *len) { GValue *nested; GValue *val; gint count, ii; gint *indices = NULL; const gchar *name; GValue *dict; gboolean folder; g_debug("ghb_preset_indices_from_path () "); nested = presets; count = ghb_array_len(path); if (count) indices = g_malloc(MAX_NESTED_PRESET * sizeof(gint)); *len = 0; for (ii = 0; ii < count; ii++) { val = ghb_array_get_nth(path, ii); name = g_value_get_string(val); indices[ii] = presets_find_element(nested, name); if (indices[ii] == -1) { g_free(indices); return NULL; } if (ii < count-1) { dict = ghb_array_get_nth(nested, indices[ii]); folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder")); if (!folder) { g_free(indices); return NULL; } nested = ghb_dict_lookup(dict, "ChildrenArray"); } } *len = ii; return indices; } static gint ghb_presets_get_type( GValue *presets, gint *indices, gint len) { GValue *dict; gint type = 0; dict = presets_get_dict(presets, indices, len); if (dict) { type = ghb_preset_type(dict); } else { g_warning("ghb_presets_get_type (): internal preset lookup error"); } return type; } static gboolean ghb_presets_get_folder( GValue *presets, gint *indices, gint len) { GValue *dict; gboolean folder = FALSE; dict = presets_get_dict(presets, indices, len); if (dict) { folder = ghb_preset_folder(dict); } else { g_warning("ghb_presets_get_folder (): internal preset lookup error"); } return folder; } void presets_set_default(gint *indices, gint len) { GValue *dict; g_debug("presets_set_default ()"); presets_clear_default(presetsPlist); dict = presets_get_dict(presetsPlist, indices, len); if (dict) { ghb_dict_insert(dict, g_strdup("Default"), ghb_boolean_value_new(TRUE)); } store_presets(); } static void presets_set_folder_open(gboolean open, gint *indices, gint len) { GValue *dict; g_debug("presets_set_folder_open ()"); dict = presets_get_dict(presetsPlist, indices, len); if (dict) { ghb_dict_insert(dict, g_strdup("FolderOpen"), ghb_boolean_value_new(open)); } } // Used for sorting dictionaries. gint key_cmp(gconstpointer a, gconstpointer b) { gchar *stra = (gchar*)a; gchar *strb = (gchar*)b; return strcmp(stra, strb); } static const GValue* preset_dict_get_value(GValue *dict, const gchar *key) { return ghb_dict_lookup(dict, key); } static const char * dict_get_string(GValue *dict, const char *key) { GValue *gval = ghb_dict_lookup(dict, key); if (gval == NULL) return NULL; return g_value_get_string(gval); } static gboolean dict_get_boolean(GValue *dict, const char *key) { GValue *gval = ghb_dict_lookup(dict, key); if (gval == NULL) return FALSE; return g_value_get_boolean(gval); } const gchar* ghb_presets_get_description(GValue *pdict) { return dict_get_string(pdict, "PresetDescription"); } static void init_settings_from_dict( GValue *dest, GValue *template, GValue *dict, gboolean filter); static void init_settings_from_array( GValue *dest, GValue *template, GValue *array, gboolean filter) { GValue *gval, *val, *new_val; gint count, ii; if (ghb_array_len(template) == 0) { if (!filter) { count = ghb_array_len(array); for (ii = 0; ii < count; ii++) { val = ghb_array_get_nth(array, ii); ghb_array_append(dest, ghb_value_dup(val)); } } return; } count = ghb_array_len(array); // The first element of the template array is always the // template for the allowed values gval = ghb_array_get_nth(template, 0); for (ii = 0; ii < count; ii++) { val = ghb_array_get_nth(array, ii); if (G_VALUE_TYPE(gval) == ghb_dict_get_type()) { GValue *new_dict; if (val != NULL && G_VALUE_TYPE(val) == ghb_dict_get_type()) { new_dict = ghb_dict_value_new(); init_settings_from_dict(new_dict, gval, val, filter); } else { new_dict = ghb_value_dup(gval); } new_val = new_dict; } else if (G_VALUE_TYPE(gval) == ghb_array_get_type()) { GValue *new_array; if (val != NULL && G_VALUE_TYPE(val) == ghb_array_get_type()) { new_array = ghb_array_value_new(8); init_settings_from_array(new_array, gval, val, filter); } else { new_array = ghb_value_dup(gval); } new_val = new_array; } else { if (val == NULL) new_val = ghb_value_dup(gval); else new_val = ghb_value_dup(val); } ghb_array_append(dest, new_val); } } static void init_settings_from_dict( GValue *dest, GValue *template, GValue *dict, gboolean filter) { GHashTableIter iter; gchar *key; GValue *gval, *val, *new_val; ghb_dict_iter_init(&iter, template); // middle (void*) cast prevents gcc warning "defreferencing type-punned // pointer will break strict-aliasing rules" while (g_hash_table_iter_next( &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval)) { val = NULL; if (dict) val = ghb_dict_lookup(dict, key); if (G_VALUE_TYPE(gval) == ghb_dict_get_type()) { GValue *new_dict; if (val != NULL && G_VALUE_TYPE(val) == ghb_dict_get_type()) { new_dict = ghb_dict_value_new(); init_settings_from_dict(new_dict, gval, val, filter); } else { new_dict = ghb_value_dup(gval); } new_val = new_dict; } else if (G_VALUE_TYPE(gval) == ghb_array_get_type()) { GValue *new_array; if (val != NULL && G_VALUE_TYPE(val) == ghb_array_get_type()) { new_array = ghb_array_value_new(8); init_settings_from_array(new_array, gval, val, filter); } else { new_array = ghb_value_dup(gval); } new_val = new_array; } else { if (val == NULL) new_val = ghb_value_dup(gval); else new_val = ghb_value_dup(val); } ghb_settings_take_value(dest, key, new_val); } if (filter || dict == NULL) return; // If not filtering the source, copy source elements that // were not in the template. ghb_dict_iter_init(&iter, dict); // middle (void*) cast prevents gcc warning "defreferencing type-punned // pointer will break strict-aliasing rules" while (g_hash_table_iter_next( &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval)) { val = ghb_dict_lookup(template, key); if (val == NULL) { ghb_settings_set_value(dest, key, gval); } } } void ghb_preset_to_settings(GValue *settings, GValue *preset) { // Initialize the ui from presets file. GValue *internal; // Get key list from internal default presets. This way we do not // load any unknown keys. GValue *internalPlist = ghb_resource_get("internal-defaults"); if (internalPlist == NULL) return; internal = plist_get_dict(internalPlist, "Presets"); if (preset == NULL) preset = internal; ghb_dict_remove(settings, "x264Option"); init_settings_from_dict(settings, preset, NULL, TRUE); } void ghb_settings_to_ui(signal_user_data_t *ud, GValue *dict) { GHashTableIter iter; gchar *key; GValue *gval; GValue *tmp = ghb_value_dup(dict); if (dict == NULL) return; ghb_dict_iter_init(&iter, tmp); // middle (void*) cast prevents gcc warning "defreferencing type-punned // pointer will break strict-aliasing rules" while (g_hash_table_iter_next( &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval)) { ghb_ui_settings_update(ud, dict, key, gval); } ghb_value_free(tmp); } static GValue *current_preset = NULL; gboolean ghb_preset_is_custom() { const GValue *val; if (current_preset == NULL) return FALSE; val = preset_dict_get_value(current_preset, "Type"); return (ghb_value_int(val) == 1); } void ghb_set_preset_settings_from_indices( signal_user_data_t *ud, gint *indices, gint len) { GValue *dict = NULL; gint fallback[2] = {0, -1}; if (indices) dict = presets_get_dict(presetsPlist, indices, len); if (dict == NULL) { indices = fallback; len = 1; dict = presets_get_dict(presetsPlist, indices, len); } if (dict == NULL) { ghb_preset_to_settings(ud->settings, NULL); current_preset = NULL; } else { GValue *path; gboolean folder; current_preset = dict; folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder")); if (folder) ghb_preset_to_settings(ud->settings, NULL); else ghb_preset_to_settings(ud->settings, dict); path = preset_path_from_indices(presetsPlist, indices, len); ghb_settings_set_value(ud->settings, "preset", path); ghb_value_free(path); } } static const GValue* curr_preset_get_value(const gchar *key) { if (current_preset == NULL) return NULL; return preset_dict_get_value(current_preset, key); } void ghb_update_from_preset( signal_user_data_t *ud, const gchar *key) { const GValue *gval; g_debug("ghb_update_from_preset() %s", key); gval = curr_preset_get_value(key); if (gval != NULL) { ghb_ui_update(ud, key, gval); } } static void ghb_select_preset2( GtkBuilder *builder, gint *indices, gint len) { GtkTreeView *treeview; GtkTreeSelection *selection; GtkTreeModel *store; GtkTreeIter iter; GtkTreePath *path; g_debug("ghb_select_preset2()"); treeview = GTK_TREE_VIEW(GHB_WIDGET(builder, "presets_list")); selection = gtk_tree_view_get_selection (treeview); store = gtk_tree_view_get_model (treeview); path = ghb_tree_path_new_from_indices(indices, len); if (path) { if (gtk_tree_model_get_iter(store, &iter, path)) { gtk_tree_selection_select_iter (selection, &iter); } else { if (gtk_tree_model_get_iter_first(store, &iter)) gtk_tree_selection_select_iter (selection, &iter); } gtk_tree_path_free(path); } } void ghb_select_preset(GtkBuilder *builder, const GValue *path) { gint *indices, len; g_debug("ghb_select_preset()"); indices = ghb_preset_indices_from_path(presetsPlist, path, &len); if (indices) { ghb_select_preset2(builder, indices, len); g_free(indices); } } void ghb_select_default_preset(GtkBuilder *builder) { gint *indices, len; g_debug("ghb_select_default_preset()"); indices = presets_find_default(presetsPlist, &len); if (indices) { ghb_select_preset2(builder, indices, len); g_free(indices); } } gchar* ghb_get_user_config_dir(gchar *subdir) { const gchar *dir; gchar *config; dir = g_get_user_config_dir(); if (!g_file_test(dir, G_FILE_TEST_IS_DIR)) { dir = g_get_home_dir(); config = g_strdup_printf ("%s/.ghb", dir); if (!g_file_test(config, G_FILE_TEST_IS_DIR)) g_mkdir (config, 0755); } else { config = g_strdup_printf ("%s/ghb", dir); if (!g_file_test(config, G_FILE_TEST_IS_DIR)) g_mkdir (config, 0755); } if (subdir) { gchar **split; gint ii; split = g_strsplit(subdir, G_DIR_SEPARATOR_S, -1); for (ii = 0; split[ii] != NULL; ii++) { gchar *tmp; tmp = g_strdup_printf ("%s/%s", config, split[ii]); g_free(config); config = tmp; if (!g_file_test(config, G_FILE_TEST_IS_DIR)) g_mkdir (config, 0755); } g_strfreev(split); } return config; } static void store_plist(GValue *plist, const gchar *name) { gchar *config, *path; FILE *file; config = ghb_get_user_config_dir(NULL); path = g_strdup_printf ("%s/%s", config, name); file = g_fopen(path, "w"); g_free(config); g_free(path); ghb_plist_write(file, plist); fclose(file); } static GValue* load_plist(const gchar *name) { gchar *config, *path; GValue *plist = NULL; config = ghb_get_user_config_dir(NULL); path = g_strdup_printf ("%s/%s", config, name); if (g_file_test(path, G_FILE_TEST_IS_REGULAR)) { plist = ghb_plist_parse_file(path); } g_free(config); g_free(path); return plist; } gboolean ghb_lock_file(const gchar *name) { #if !defined(_WIN32) gchar *config, *path; int fd, lock = 0; config = ghb_get_user_config_dir(NULL); path = g_strdup_printf ("%s/%s", config, name); fd = open(path, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR); if (fd >= 0) lock = lockf(fd, F_TLOCK, 0); if (lock) close(fd); g_free(config); g_free(path); return !lock; #else return 1; #endif } void ghb_write_pid_file() { #if !defined(_WIN32) gchar *config, *path; pid_t pid; FILE *fp; int fd; pid = getpid(); config = ghb_get_user_config_dir(NULL); path = g_strdup_printf ("%s/ghb.pid.%d", config, pid); fp = g_fopen(path, "w"); fprintf(fp, "%d\n", pid); fclose(fp); fd = open(path, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR); lockf(fd, F_TLOCK, 0); g_free(config); g_free(path); #endif } void ghb_unlink_pid_file(int pid) { gchar *config, *path; config = ghb_get_user_config_dir(NULL); path = g_strdup_printf ("%s/ghb.pid.%d", config, pid); if (g_file_test(path, G_FILE_TEST_IS_REGULAR)) { g_unlink(path); } g_free(config); g_free(path); } int ghb_find_pid_file() { const gchar *file; gchar *config; config = ghb_get_user_config_dir(NULL); if (g_file_test(config, G_FILE_TEST_IS_DIR)) { GDir *gdir = g_dir_open(config, 0, NULL); file = g_dir_read_name(gdir); while (file) { if (strncmp(file, "ghb.pid.", 8) == 0) { gchar *path; pid_t my_pid; int pid; sscanf(file, "ghb.pid.%d", &pid); my_pid = getpid(); if (my_pid == pid) { file = g_dir_read_name(gdir); continue; } path = g_strdup_printf("%s/%s", config, file); #if !defined(_WIN32) int fd, lock = 1; fd = open(path, O_RDWR); if (fd >= 0) { lock = lockf(fd, F_TLOCK, 0); } if (lock == 0) { close(fd); g_dir_close(gdir); g_unlink(path); g_free(path); g_free(config); return pid; } g_free(path); close(fd); #else g_dir_close(gdir); g_unlink(path); g_free(path); g_free(config); return pid; #endif } file = g_dir_read_name(gdir); } g_dir_close(gdir); } g_free(config); return -1; } static void remove_plist(const gchar *name) { gchar *config, *path; config = ghb_get_user_config_dir(NULL); path = g_strdup_printf ("%s/%s", config, name); if (g_file_test(path, G_FILE_TEST_IS_REGULAR)) { g_unlink(path); } g_free(path); g_free(config); } void ghb_prefs_save(GValue *settings) { GValue *dict; GValue *pref_dict; GHashTableIter iter; gchar *key; const GValue *value; GValue *internalPlist = ghb_resource_get("internal-defaults"); dict = plist_get_dict(internalPlist, "Preferences"); if (dict == NULL) return; pref_dict = plist_get_dict(prefsPlist, "Preferences"); if (pref_dict == NULL) return; ghb_dict_iter_init(&iter, dict); // middle (void*) cast prevents gcc warning "defreferencing type-punned // pointer will break strict-aliasing rules" while (g_hash_table_iter_next( &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&value)) { value = ghb_settings_get_value(settings, key); if (value != NULL) { ghb_dict_insert(pref_dict, g_strdup(key), ghb_value_dup(value)); } } store_prefs(); prefs_modified = FALSE; } void ghb_pref_save(GValue *settings, const gchar *key) { const GValue *value, *value2; value = ghb_settings_get_value(settings, key); if (value != NULL) { GValue *dict; dict = plist_get_dict(prefsPlist, "Preferences"); if (dict == NULL) return; value2 = ghb_dict_lookup(dict, key); if (ghb_value_cmp(value, value2) != 0) { ghb_dict_insert(dict, g_strdup(key), ghb_value_dup(value)); store_prefs(); prefs_modified = FALSE; } } } void ghb_pref_set(GValue *settings, const gchar *key) { const GValue *value, *value2; value = ghb_settings_get_value(settings, key); if (value != NULL) { GValue *dict; dict = plist_get_dict(prefsPlist, "Preferences"); if (dict == NULL) return; value2 = ghb_dict_lookup(dict, key); if (ghb_value_cmp(value, value2) != 0) { ghb_dict_insert(dict, g_strdup(key), ghb_value_dup(value)); prefs_modified = TRUE; } } } void ghb_prefs_store(void) { if (prefs_modified) { store_prefs(); prefs_modified = FALSE; } } void ghb_settings_init(GValue *settings, const char *name) { GValue *internal; GHashTableIter iter; gchar *key; GValue *gval; g_debug("ghb_settings_init"); GValue *internalPlist = ghb_resource_get("internal-defaults"); // Setting a ui widget will cause the corresponding setting // to be set, but it also triggers a callback that can // have the side effect of using other settings values // that have not yet been set. So set *all* settings first // then update the ui. internal = plist_get_dict(internalPlist, name); ghb_dict_iter_init(&iter, internal); // middle (void*) cast prevents gcc warning "defreferencing type-punned // pointer will break strict-aliasing rules" while (g_hash_table_iter_next( &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval)) { ghb_settings_set_value(settings, key, gval); } } void ghb_settings_close() { if (presetsPlist) ghb_value_free(presetsPlist); if (prefsPlist) ghb_value_free(prefsPlist); } #if defined(_WIN32) gchar* FindFirstCDROM(void) { gint ii, drives; gchar drive[5]; strcpy(drive, "A:" G_DIR_SEPARATOR_S); drives = GetLogicalDrives(); for (ii = 0; ii < 26; ii++) { if (drives & 0x01) { guint dtype; drive[0] = 'A' + ii; dtype = GetDriveType(drive); if (dtype == DRIVE_CDROM) { return g_strdup(drive); } } drives >>= 1; } return NULL; } #endif void ghb_prefs_load(signal_user_data_t *ud) { GValue *dict, *internal; GHashTableIter iter; gchar *key; GValue *gval; g_debug("ghb_prefs_load"); GValue *internalPlist = ghb_resource_get("internal-defaults"); prefsPlist = load_plist("preferences"); if (prefsPlist == NULL) prefsPlist = ghb_dict_value_new(); dict = plist_get_dict(prefsPlist, "Preferences"); internal = plist_get_dict(internalPlist, "Preferences"); if (dict == NULL && internal) { dict = ghb_dict_value_new(); ghb_dict_insert(prefsPlist, g_strdup("Preferences"), dict); // Get defaults from internal defaults ghb_dict_iter_init(&iter, internal); // middle (void*) cast prevents gcc warning "defreferencing type-punned // pointer will break strict-aliasing rules" while (g_hash_table_iter_next( &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval)) { ghb_dict_insert(dict, g_strdup(key), ghb_value_dup(gval)); } const gchar *dir = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP); if (dir == NULL) { dir = "."; } ghb_dict_insert(dict, g_strdup("ExportDirectory"), ghb_value_dup(ghb_string_value(dir))); dir = g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS); if (dir == NULL) { dir = "."; } ghb_dict_insert(dict, g_strdup("destination_dir"), ghb_value_dup(ghb_string_value(dir))); ghb_dict_insert(dict, g_strdup("SrtDir"), ghb_value_dup(ghb_string_value(dir))); #if defined(_WIN32) gchar *source; source = FindFirstCDROM(); if (source == NULL) { source = g_strdup("C:" G_DIR_SEPARATOR_S); } ghb_dict_insert(dict, g_strdup("default_source"), ghb_value_dup(ghb_string_value(source))); g_free(source); #endif store_prefs(); } } void ghb_prefs_to_settings(GValue *settings) { // Initialize the ui from presets file. GValue *internal, *dict; if (prefsPlist == NULL) return; // Get key list from internal default presets. This way we do not // load any unknown keys. GValue *internalPlist = ghb_resource_get("internal-defaults"); if (internalPlist == NULL) return; internal = plist_get_dict(internalPlist, "Preferences"); dict = plist_get_dict(prefsPlist, "Preferences"); // Setting a ui widget will cause the corresponding setting // to be set, but it also triggers a callback that can // have the side effect of using other settings values // that have not yet been set. So set *all* settings first // then update the ui. init_settings_from_dict(settings, internal, dict, TRUE); } static const gchar* get_preset_color(gint type, gboolean folder) { const gchar *color; if (type == PRESETS_CUSTOM) { color = "DimGray"; if (folder) { color = "black"; } } else { color = "blue"; if (folder) { color = "Navy"; } } return color; } void ghb_presets_list_init( signal_user_data_t *ud, gint *indices, gint len) { GtkTreeView *treeview; GtkTreeIter iter, titer, *piter; GtkTreeStore *store; const gchar *preset; GtkTreePath *parent_path; const gchar *description; gboolean def; gint count, ii; GValue *dict; gint *more_indices; GValue *presets = NULL; g_debug("ghb_presets_list_init ()"); more_indices = g_malloc((len+1)*sizeof(gint)); memcpy(more_indices, indices, len*sizeof(gint)); presets = presets_get_folder(presetsPlist, indices, len); if (presets == NULL) { g_warning(_("Failed to find parent folder when adding child.")); g_free(more_indices); return; } count = ghb_array_len(presets); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list")); store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); parent_path = ghb_tree_path_new_from_indices(indices, len); if (parent_path) { gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &titer, parent_path); piter = &titer; gtk_tree_path_free(parent_path); } else { piter = NULL; } for (ii = 0; ii < count; ii++) { const gchar *color; gint type; gboolean folder; // Additional settings, add row dict = ghb_array_get_nth(presets, ii); preset = preset_get_name(dict); more_indices[len] = ii; def = preset_is_default(dict); description = ghb_presets_get_description(dict); gtk_tree_store_append(store, &iter, piter); type = ghb_preset_type(dict); folder = ghb_preset_folder(dict); color = get_preset_color(type, folder); gtk_tree_store_set(store, &iter, 0, preset, 1, def ? 800 : 400, 2, def ? 2 : 0, 3, color, 4, description, 5, type == PRESETS_BUILTIN ? 0 : 1, -1); if (def && piter) { GtkTreePath *path; GtkTreeIter ppiter; if (gtk_tree_model_iter_parent( GTK_TREE_MODEL(store), &ppiter, piter)) { path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &ppiter); gtk_tree_view_expand_row(treeview, path, FALSE); gtk_tree_path_free(path); } path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), piter); gtk_tree_view_expand_row(treeview, path, FALSE); gtk_tree_path_free(path); } if (folder) { ghb_presets_list_init(ud, more_indices, len+1); if (preset_folder_is_open(dict)) { GtkTreePath *path; if (piter != NULL) { path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), piter); gtk_tree_view_expand_row(treeview, path, FALSE); gtk_tree_path_free(path); } path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter); gtk_tree_view_expand_row(treeview, path, FALSE); gtk_tree_path_free(path); } } } g_free(more_indices); } static void presets_list_update_item( signal_user_data_t *ud, gint *indices, gint len, gboolean recurse) { GtkTreeView *treeview; GtkTreeStore *store; GtkTreeIter iter; GtkTreePath *treepath; const gchar *name; const gchar *description; gint type; gboolean def, folder; GValue *dict; const gchar *color; g_debug("presets_list_update_item ()"); dict = presets_get_dict(presetsPlist, indices, len); if (dict == NULL) return; treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list")); store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); treepath = ghb_tree_path_new_from_indices(indices, len); gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath); // Additional settings, add row name = preset_get_name(dict); def = preset_is_default(dict); description = ghb_presets_get_description(dict); type = ghb_preset_type(dict); folder = ghb_preset_folder(dict); color = get_preset_color(type, folder); gtk_tree_store_set(store, &iter, 0, name, 1, def ? 800 : 400, 2, def ? 2 : 0, 3, color, 4, description, 5, type == PRESETS_BUILTIN ? 0 : 1, -1); if (recurse && folder) { ghb_presets_list_init(ud, indices, len); } } static void presets_list_insert( signal_user_data_t *ud, gint *indices, gint len) { GtkTreeView *treeview; GtkTreeIter iter, titer, *piter; GtkTreeStore *store; const gchar *preset; const gchar *description; gint type; gboolean def, folder; gint count; GValue *presets; GtkTreePath *parent_path; GValue *dict; const gchar *color; g_debug("presets_list_insert ()"); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list")); store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); presets = presets_get_folder(presetsPlist, indices, len-1); if (presets == NULL) { g_warning(_("Failed to find parent folder while adding child.")); return; } parent_path = ghb_tree_path_new_from_indices(indices, len-1); if (parent_path) { gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &titer, parent_path); piter = &titer; gtk_tree_path_free(parent_path); } else { piter = NULL; } count = ghb_array_len(presets); if (indices[len-1] >= count) return; // Additional settings, add row dict = ghb_array_get_nth(presets, indices[len-1]); preset = preset_get_name(dict); def = preset_is_default(dict); description = ghb_presets_get_description(dict); gtk_tree_store_insert(store, &iter, piter, indices[len-1]); type = ghb_preset_type(dict); folder = ghb_preset_folder(dict); color = get_preset_color(type, folder); gtk_tree_store_set(store, &iter, 0, preset, 1, def ? 800 : 400, 2, def ? 2 : 0, 3, color, 4, description, 5, type == PRESETS_BUILTIN ? 0 : 1, -1); if (folder) { ghb_presets_list_init(ud, indices, len); } } static void presets_list_remove( signal_user_data_t *ud, gint *indices, gint len) { GtkTreeView *treeview; GtkTreePath *treepath; GtkTreeIter iter; GtkTreeStore *store; g_debug("presets_list_remove ()"); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list")); store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); treepath = ghb_tree_path_new_from_indices(indices, len); if (treepath) { if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath)) gtk_tree_store_remove(store, &iter); gtk_tree_path_free(treepath); } } static void remove_std_presets(signal_user_data_t *ud) { gint count, ii; gint indices = 0; count = ghb_array_len(presetsPlist); for (ii = count-1; ii >= 0; ii--) { GValue *dict; gint ptype; dict = ghb_array_get_nth(presetsPlist, ii); ptype = ghb_value_int(preset_dict_get_value(dict, "Type")); if (ptype == PRESETS_BUILTIN) { if (ghb_presets_remove(presetsPlist, &indices, 1)) { presets_list_remove(ud, &indices, 1); } } } } void ghb_save_queue(GValue *queue) { pid_t pid; char *path; pid = getpid(); path = g_strdup_printf ("queue.%d", pid); store_plist(queue, path); g_free(path); } GValue* ghb_load_queue() { GValue *queue; pid_t pid; char *path; pid = getpid(); path = g_strdup_printf ("queue.%d", pid); queue = load_plist(path); g_free(path); return queue; } GValue* ghb_load_old_queue(int pid) { GValue *queue; char *path; path = g_strdup_printf ("queue.%d", pid); queue = load_plist(path); g_free(path); return queue; } void ghb_remove_old_queue_file(int pid) { char *path; path = g_strdup_printf ("queue.%d", pid); remove_plist(path); g_free(path); } void ghb_remove_queue_file() { pid_t pid; char *path; pid = getpid(); path = g_strdup_printf ("queue.%d", pid); remove_plist(path); g_free(path); } typedef struct { const gchar *mac_val; const gchar *lin_val; } value_map_t; value_map_t deint_xlat[] = { {"0", "off"}, {"1", "custom"}, {"2", "fast"}, {"3", "slow"}, {"4", "slower"}, {"5", "bob"}, {NULL, NULL} }; value_map_t denoise_xlat[] = { {"0", "off"}, {"1", "custom"}, {"2", "light"}, {"3", "medium"}, {"4", "strong"}, {"5", "ultralight"}, {NULL, NULL} }; value_map_t detel_xlat[] = { {"0", "off"}, {"1", "custom"}, {"2", "default"}, {NULL, NULL} }; value_map_t decomb_xlat[] = { {"0", "off"}, {"1", "custom"}, {"2", "default"}, {"3", "fast"}, {"4", "bob"}, {NULL, NULL} }; #if 0 extern iso639_lang_t ghb_language_table[]; static GValue* export_lang_xlat2(GValue *lin_val) { GValue *gval; if (lin_val == NULL) return NULL; gint ii; gchar *str; str = ghb_value_string(lin_val); for (ii = 0; ghb_language_table[ii].eng_name; ii++) { if (strcmp(str, ghb_language_table[ii].iso639_2) == 0) { const gchar *lang; if (ghb_language_table[ii].native_name[0] != 0) lang = ghb_language_table[ii].native_name; else lang = ghb_language_table[ii].eng_name; gval = ghb_string_value_new(lang); g_free(str); return gval; } } g_debug("Can't map language value: (%s)", str); g_free(str); return NULL; } static GValue* import_lang_xlat2(GValue *mac_val) { GValue *gval; if (mac_val == NULL) return NULL; gint ii; gchar *str; str = ghb_value_string(mac_val); for (ii = 0; ghb_language_table[ii].eng_name; ii++) { if ((strcmp(str, ghb_language_table[ii].eng_name) == 0) || (strcmp(str, ghb_language_table[ii].native_name) == 0)) { gval = ghb_string_value_new(ghb_language_table[ii].iso639_2); g_free(str); return gval; } } g_debug("Can't map language value: (%s)", str); g_free(str); return NULL; } #endif static GValue* export_value_xlat2(value_map_t *value_map, GValue *lin_val, GType mac_type) { GValue *gval; if (lin_val == NULL) return NULL; gint ii; gchar *str; GValue *sval; str = ghb_value_string(lin_val); for (ii = 0; value_map[ii].mac_val; ii++) { if (strcmp(str, value_map[ii].lin_val) == 0) { sval = ghb_string_value_new(value_map[ii].mac_val); g_free(str); gval = ghb_value_new(mac_type); if (!g_value_transform(sval, gval)) { g_warning("can't transform"); ghb_value_free(gval); ghb_value_free(sval); return NULL; } ghb_value_free(sval); return gval; } } g_debug("Can't map value: (%s)", str); g_free(str); return NULL; } static GValue* export_value_video_framerate(GValue *lin_val) { GValue *sval = NULL; gchar *str; const gchar *fr; str = ghb_value_string(lin_val); fr = hb_video_framerate_get_name(hb_video_framerate_get_from_name(str)); g_free(str); if (fr != NULL) sval = ghb_string_value_new(fr); return sval; } static GValue* export_value_audio_samplerate(GValue *lin_val) { GValue *sval = NULL; gchar *str; const gchar *sr; str = ghb_value_string(lin_val); sr = hb_audio_samplerate_get_name(hb_audio_samplerate_get_from_name(str)); g_free(str); if (sr != NULL) sval = ghb_string_value_new(sr); return sval; } static GValue* export_value_mixdown(GValue *lin_val) { GValue *sval = NULL; gchar *str; const gchar *mix; str = ghb_value_string(lin_val); mix = hb_mixdown_get_name(hb_mixdown_get_from_name(str)); g_free(str); if (mix != NULL) sval = ghb_string_value_new(mix); return sval; } static GValue* export_value_video_encoder(GValue *lin_val) { GValue *sval = NULL; gchar *str; const gchar *enc; str = ghb_value_string(lin_val); enc = hb_video_encoder_get_name(hb_video_encoder_get_from_name(str)); g_free(str); if (enc != NULL) sval = ghb_string_value_new(enc); return sval; } static GValue* export_value_audio_encoder(GValue *lin_val) { GValue *sval = NULL; gchar *str; const gchar *enc; str = ghb_value_string(lin_val); enc = hb_audio_encoder_get_name(hb_audio_encoder_get_from_name(str)); g_free(str); if (enc != NULL) sval = ghb_string_value_new(enc); return sval; } static GValue* export_value_container(GValue *lin_val) { GValue *sval = NULL; gchar *str; const gchar *mux; str = ghb_value_string(lin_val); mux = hb_container_get_name(hb_container_get_from_name(str)); g_free(str); if (mux != NULL) sval = ghb_string_value_new(mux); return sval; } // Translate values for compatibility with other platforms static void export_value_xlat(GValue *dict) { GValue *lin_val, *gval; const gchar *key; key = "VideoEncoder"; lin_val = ghb_dict_lookup(dict, key); gval = export_value_video_encoder(lin_val); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); key = "FileFormat"; lin_val = ghb_dict_lookup(dict, key); gval = export_value_container(lin_val); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); key = "VideoFramerate"; lin_val = ghb_dict_lookup(dict, key); gval = export_value_video_framerate(lin_val); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); key = "PictureDetelecine"; lin_val = ghb_dict_lookup(dict, key); gval = export_value_xlat2(detel_xlat, lin_val, G_TYPE_INT); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); key = "PictureDecomb"; lin_val = ghb_dict_lookup(dict, key); gval = export_value_xlat2(decomb_xlat, lin_val, G_TYPE_INT); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); key = "PictureDeinterlace"; lin_val = ghb_dict_lookup(dict, key); gval = export_value_xlat2(deint_xlat, lin_val, G_TYPE_INT); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); #if 0 key = "PictureDenoisePreset"; lin_val = ghb_dict_lookup(dict, key); gval = export_value_xlat2(denoise_xlat, lin_val, G_TYPE_INT); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); #endif gint count, ii; GValue *alist; GValue *adict; key = "AudioEncoderFallback"; lin_val = ghb_dict_lookup(dict, key); gval = export_value_audio_encoder(lin_val); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); alist = ghb_dict_lookup(dict, "AudioList"); count = ghb_array_len(alist); for (ii = 0; ii < count; ii++) { adict = ghb_array_get_nth(alist, ii); key = "AudioEncoder"; lin_val = ghb_dict_lookup(adict, key); gval = export_value_audio_encoder(lin_val); if (gval) ghb_dict_insert(adict, g_strdup(key), gval); key = "AudioSamplerate"; lin_val = ghb_dict_lookup(adict, key); gval = export_value_audio_samplerate(lin_val); if (gval) ghb_dict_insert(adict, g_strdup(key), gval); key = "AudioMixdown"; lin_val = ghb_dict_lookup(adict, key); gval = export_value_mixdown(lin_val); if (gval) ghb_dict_insert(adict, g_strdup(key), gval); } } static GValue* import_value_xlat2( GValue *defaults, value_map_t *value_map, const gchar *key, GValue *mac_val) { GValue *gval, *def_val; if (mac_val == NULL) return NULL; def_val = ghb_dict_lookup(defaults, key); if (def_val) { gint ii; gchar *str; GValue *sval; str = ghb_value_string(mac_val); for (ii = 0; value_map[ii].mac_val; ii++) { if (strcmp(str, value_map[ii].mac_val) == 0 || strcmp(str, value_map[ii].lin_val) == 0) { sval = ghb_string_value_new(value_map[ii].lin_val); g_free(str); gval = ghb_value_new(G_VALUE_TYPE(def_val)); if (!g_value_transform(sval, gval)) { g_warning("can't transform"); ghb_value_free(gval); ghb_value_free(sval); return NULL; } ghb_value_free(sval); return gval; } } g_free(str); return ghb_value_dup(def_val); } else { gint ii; gchar *str; GValue *sval; str = ghb_value_string(mac_val); for (ii = 0; value_map[ii].mac_val; ii++) { if (strcmp(str, value_map[ii].mac_val) == 0 || strcmp(str, value_map[ii].lin_val) == 0) { sval = ghb_string_value_new(value_map[ii].lin_val); g_free(str); gval = ghb_value_new(G_VALUE_TYPE(mac_val)); if (!g_value_transform(sval, gval)) { g_warning("can't transform"); ghb_value_free(gval); ghb_value_free(sval); return NULL; } ghb_value_free(sval); return gval; } } g_free(str); } return NULL; } static GValue* import_value_video_framerate(GValue *mac_val) { GValue *sval = NULL; gchar *str; const gchar *fr; str = ghb_value_string(mac_val); fr = hb_video_framerate_get_name(hb_video_framerate_get_from_name(str)); g_free(str); if (fr != NULL) sval = ghb_string_value_new(fr); return sval; } static GValue* import_value_audio_samplerate(GValue *mac_val) { GValue *sval = NULL; gchar *str; const gchar *sr; str = ghb_value_string(mac_val); sr = hb_audio_samplerate_get_name(hb_audio_samplerate_get_from_name(str)); g_free(str); if (sr != NULL) sval = ghb_string_value_new(sr); return sval; } static GValue* import_value_mixdown(GValue *mac_val) { GValue *sval = NULL; gchar *str; const gchar *mix; str = ghb_value_string(mac_val); mix = hb_mixdown_get_short_name(hb_mixdown_get_from_name(str)); g_free(str); if (mix != NULL) sval = ghb_string_value_new(mix); return sval; } static GValue* import_value_video_encoder(GValue *mac_val) { GValue *sval = NULL; gchar *str; const gchar *enc; str = ghb_value_string(mac_val); enc = hb_video_encoder_get_short_name(hb_video_encoder_get_from_name(str)); g_free(str); if (enc != NULL) sval = ghb_string_value_new(enc); return sval; } static GValue* import_value_audio_encoder(GValue *mac_val) { GValue *sval = NULL; gchar *str; const gchar *enc; str = ghb_value_string(mac_val); enc = hb_audio_encoder_get_short_name(hb_audio_encoder_get_from_name(str)); g_free(str); if (enc != NULL) sval = ghb_string_value_new(enc); return sval; } static GValue* import_value_container(GValue *mac_val) { GValue *sval = NULL; gchar *str; const gchar *mux; str = ghb_value_string(mac_val); mux = hb_container_get_short_name(hb_container_get_from_name(str)); g_free(str); if (mux != NULL) sval = ghb_string_value_new(mux); return sval; } static void import_value_xlat(GValue *dict) { GValue *defaults, *mac_val, *gval; const gchar *key; GValue *internalPlist = ghb_resource_get("internal-defaults"); defaults = plist_get_dict(internalPlist, "Presets"); key = "VideoEncoder"; mac_val = ghb_dict_lookup(dict, key); gval = import_value_video_encoder(mac_val); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); key = "FileFormat"; mac_val = ghb_dict_lookup(dict, key); gval = import_value_container(mac_val); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); key = "VideoFramerate"; mac_val = ghb_dict_lookup(dict, key); gval = import_value_video_framerate(mac_val); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); key = "PictureDetelecine"; mac_val = ghb_dict_lookup(dict, key); gval = import_value_xlat2(defaults, detel_xlat, key, mac_val); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); key = "PictureDecomb"; mac_val = ghb_dict_lookup(dict, key); gval = import_value_xlat2(defaults, decomb_xlat, key, mac_val); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); key = "PictureDeinterlace"; mac_val = ghb_dict_lookup(dict, key); gval = import_value_xlat2(defaults, deint_xlat, key, mac_val); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); key = "PictureDenoisePreset"; mac_val = ghb_dict_lookup(dict, key); gval = import_value_xlat2(defaults, denoise_xlat, key, mac_val); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); ghb_dict_remove(dict, "Subtitles"); ghb_dict_remove(dict, "SubtitlesForced"); gint count, ii; GValue *alist; GValue *adict; GValue *adefaults; GValue *adeflist; key = "AudioEncoderFallback"; mac_val = ghb_dict_lookup(dict, key); gval = import_value_audio_encoder(mac_val); if (gval) ghb_dict_insert(dict, g_strdup(key), gval); adeflist = ghb_dict_lookup(defaults, "AudioList"); if (adeflist) { adefaults = ghb_array_get_nth(adeflist, 0); alist = ghb_dict_lookup(dict, "AudioList"); count = ghb_array_len(alist); for (ii = 0; ii < count; ii++) { adict = ghb_array_get_nth(alist, ii); key = "AudioEncoder"; mac_val = ghb_dict_lookup(adict, key); gval = import_value_audio_encoder(mac_val); if (gval == NULL) gval = ghb_value_dup(ghb_dict_lookup(adefaults, key)); if (gval) ghb_dict_insert(adict, g_strdup(key), gval); key = "AudioSamplerate"; mac_val = ghb_dict_lookup(adict, key); gval = import_value_audio_samplerate(mac_val); if (gval == NULL) gval = ghb_value_dup(ghb_dict_lookup(adefaults, key)); if (gval) ghb_dict_insert(adict, g_strdup(key), gval); key = "AudioMixdown"; mac_val = ghb_dict_lookup(adict, key); gval = import_value_mixdown(mac_val); if (gval == NULL) gval = ghb_value_dup(ghb_dict_lookup(adefaults, key)); if (gval) ghb_dict_insert(adict, g_strdup(key), gval); mac_val = ghb_dict_lookup(adict, "AudioTrackDRCSlider"); if (mac_val != NULL) { gdouble drc; drc = ghb_value_double(mac_val); if (drc < 1.0) { ghb_dict_insert(adict, g_strdup("AudioTrackDRCSlider"), ghb_double_value_new(0.0)); } } } } } static GValue* import_xlat_preset(GValue *user_preset) { GValue *dict, *internal; g_debug("import_xlat_preset ()"); dict = ghb_dict_value_new(); // First, initialize the preset with defaults. // Then import user presets over top of defaults GValue *internalPlist = ghb_resource_get("internal-defaults"); internal = plist_get_dict(internalPlist, "Presets"); init_settings_from_dict(dict, internal, user_preset, FALSE); // Initialize the AudioLanguageList from preferences PreferredLanguage // and translate old AudioDUB preference option if found GValue *list = ghb_dict_lookup(dict, "AudioLanguageList"); if (list == NULL) { list = ghb_array_value_new(8); ghb_dict_insert(dict, g_strdup("AudioLanguageList"), list); } if (ghb_array_len(list) == 0) { GValue *prefs = plist_get_dict(prefsPlist, "Preferences"); GValue *gdub = ghb_dict_lookup(prefs, "AudioDUB"); GValue *glang = ghb_dict_lookup(prefs, "PreferredLanguage"); const char *lang = NULL; if (glang != NULL) { lang = g_value_get_string(glang); } if (gdub != NULL && !ghb_value_boolean(gdub)) { if (lang == NULL || strncmp(lang, "und", 4)) { ghb_array_append(list, ghb_string_value_new("und")); } } if (glang != NULL) { ghb_array_append(list, ghb_value_dup(glang)); } } // Initialize the SubtitleLanguageList from preferences PreferredLanguage // and translate old AudioDUB preference option if found list = ghb_dict_lookup(dict, "SubtitleLanguageList"); if (list == NULL) { list = ghb_array_value_new(8); ghb_dict_insert(dict, g_strdup("SubtitleLanguageList"), list); } if (ghb_array_len(list) == 0) { GValue *prefs = plist_get_dict(prefsPlist, "Preferences"); GValue *val = ghb_dict_lookup(prefs, "PreferredLanguage"); if (val != NULL) { ghb_array_append(list, ghb_value_dup(val)); val = ghb_dict_lookup(prefs, "AudioDUB"); if (val != NULL && !ghb_value_boolean(val)) { ghb_dict_insert(dict, g_strdup("SubtitleAddForeignAudioSubtitle"), ghb_boolean_value_new(TRUE)); } } } GValue *addCC = ghb_dict_lookup(dict, "SubtitleAddCC"); if (addCC == NULL) { GValue *prefs = plist_get_dict(prefsPlist, "Preferences"); GValue *val = ghb_dict_lookup(prefs, "AddCC"); if (val != NULL) { ghb_dict_insert(dict, g_strdup("SubtitleAddCC"), ghb_value_dup(val)); } } import_value_xlat(dict); // Fix up all the internal settings that are derived from preset values. ghb_settings_set_boolean(dict, "PictureDeinterlaceDecomb", !ghb_settings_get_boolean(dict, "PictureDecombDeinterlace")); ghb_settings_set_value(dict, "scale_height", ghb_settings_get_value(dict, "PictureHeight")); ghb_settings_set_value(dict, "scale_width", ghb_settings_get_value(dict, "PictureWidth")); gint uses_pic; gint vqtype; uses_pic = ghb_settings_get_int(dict, "UsesPictureSettings"); vqtype = ghb_settings_get_int(dict, "VideoQualityType"); // "Use max" or "strict anamorphic" imply autoscale if (uses_pic == 2) { ghb_settings_set_boolean(dict, "autoscale", TRUE); } else if (uses_pic == 1) { ghb_settings_set_boolean(dict, "autoscale", FALSE); } // VideoQualityType/0/1/2 - vquality_type_/target/bitrate/constant // *note: target is no longer used switch (vqtype) { case 0: { ghb_settings_set_boolean(dict, "vquality_type_bitrate", TRUE); ghb_settings_set_boolean(dict, "vquality_type_constant", FALSE); } break; case 1: { ghb_settings_set_boolean(dict, "vquality_type_bitrate", TRUE); ghb_settings_set_boolean(dict, "vquality_type_constant", FALSE); } break; case 2: { ghb_settings_set_boolean(dict, "vquality_type_bitrate", FALSE); ghb_settings_set_boolean(dict, "vquality_type_constant", TRUE); } break; default: { ghb_settings_set_boolean(dict, "vquality_type_bitrate", FALSE); ghb_settings_set_boolean(dict, "vquality_type_constant", TRUE); } break; } const gchar *mode = ghb_settings_get_const_string(dict, "VideoFramerateMode"); if (strcmp(mode, "cfr") == 0) { ghb_settings_set_boolean(dict, "VideoFramerateCFR", TRUE); ghb_settings_set_boolean(dict, "VideoFrameratePFR", FALSE); ghb_settings_set_boolean(dict, "VideoFramerateVFR", FALSE); } else if (strcmp(mode, "pfr") == 0) { ghb_settings_set_boolean(dict, "VideoFramerateCFR", FALSE); ghb_settings_set_boolean(dict, "VideoFrameratePFR", TRUE); ghb_settings_set_boolean(dict, "VideoFramerateVFR", FALSE); } else { ghb_settings_set_boolean(dict, "VideoFramerateCFR", FALSE); ghb_settings_set_boolean(dict, "VideoFrameratePFR", FALSE); ghb_settings_set_boolean(dict, "VideoFramerateVFR", TRUE); } if (ghb_settings_get_boolean(dict, "x264UseAdvancedOptions")) { // Force preset/tune/profile/level/opts to conform to option string ghb_settings_set_string(dict, "VideoPreset", "medium"); ghb_settings_set_string(dict, "VideoTune", "none"); ghb_settings_set_string(dict, "VideoProfile", "auto"); ghb_settings_set_string(dict, "VideoLevel", "auto"); ghb_settings_set_value(dict, "VideoOptionExtra", ghb_settings_get_value(dict, "x264Option")); } else { ghb_dict_remove(dict, "x264Option"); } int encoder = ghb_get_video_encoder(dict); const char * const *videoPresets; videoPresets = hb_video_encoder_get_presets(encoder); const char *videoPreset; if (ghb_dict_lookup(user_preset, "x264Preset") != NULL) videoPreset = ghb_settings_get_const_string(dict, "x264Preset"); else videoPreset = ghb_settings_get_const_string(dict, "VideoPreset"); int ii; for (ii = 0; videoPreset && videoPresets && videoPresets[ii]; ii++) { if (!strcasecmp(videoPreset, videoPresets[ii])) { ghb_settings_set_int(dict, "VideoPresetSlider", ii); } } if (videoPreset != NULL) ghb_settings_set_string(dict, "VideoPreset", videoPreset); char *videoTune; if (ghb_dict_lookup(user_preset, "x264Tune") != NULL) videoTune = ghb_settings_get_string(dict, "x264Tune"); else videoTune = ghb_settings_get_string(dict, "VideoTune"); char *tune = NULL; char *saveptr; char * tok = strtok_r(videoTune, ",./-+", &saveptr); ghb_settings_set_boolean(dict, "x264FastDecode", FALSE); ghb_settings_set_boolean(dict, "x264ZeroLatency", FALSE); while (tok != NULL) { if (!strcasecmp(tok, "fastdecode")) { ghb_settings_set_boolean(dict, "x264FastDecode", TRUE); } else if (!strcasecmp(tok, "zerolatency")) { ghb_settings_set_boolean(dict, "x264ZeroLatency", TRUE); } else if (tune == NULL) { tune = g_strdup(tok); } else { ghb_log("Superfluous tunes! %s", tok); } tok = strtok_r(NULL, ",./-+", &saveptr); } g_free(videoTune); if (tune != NULL) { ghb_settings_set_string(dict, "VideoTune", tune); g_free(tune); } const char *videoProfile; if (ghb_dict_lookup(user_preset, "x264Profile") != NULL) videoProfile = ghb_settings_get_const_string(dict, "x264Profile"); else videoProfile = ghb_settings_get_const_string(dict, "VideoProfile"); if (videoProfile != NULL) ghb_settings_set_string(dict, "VideoProfile", videoProfile); const char *videoLevel; if (ghb_dict_lookup(user_preset, "x264Level") != NULL) videoLevel = ghb_settings_get_const_string(dict, "x264Level"); else videoLevel = ghb_settings_get_const_string(dict, "VideoLevel"); if (videoLevel != NULL) ghb_settings_set_string(dict, "VideoLevel", videoLevel); if (ghb_dict_lookup(user_preset, "x264OptionExtra") != NULL) { const char *optionExtra; optionExtra = ghb_settings_get_const_string(dict, "x264OptionExtra"); ghb_settings_set_string(dict, "VideoOptionExtra", optionExtra); } return dict; } static void import_xlat_presets(GValue *presets) { gint count, ii; GValue *dict; gboolean folder; g_debug("import_xlat_presets ()"); if (presets == NULL) return; count = ghb_array_len(presets); for (ii = 0; ii < count; ii++) { dict = ghb_array_get_nth(presets, ii); folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder")); if (folder) { GValue *nested; nested = ghb_dict_lookup(dict, "ChildrenArray"); import_xlat_presets(nested); } else { GValue *import_dict = import_xlat_preset(dict); ghb_array_replace(presets, ii, import_dict); } } } // Translate internal values to preset key, value pairs static void export_xlat_preset(GValue *dict) { gboolean autoscale, br, constant; g_debug("export_xlat_prest ()"); autoscale = ghb_value_boolean(preset_dict_get_value(dict, "autoscale")); br = ghb_value_boolean( preset_dict_get_value(dict, "vquality_type_bitrate")); constant = ghb_value_boolean( preset_dict_get_value(dict, "vquality_type_constant")); if (autoscale) { ghb_dict_insert(dict, g_strdup("UsesPictureSettings"), ghb_int_value_new(2)); } else { ghb_dict_insert(dict, g_strdup("UsesPictureSettings"), ghb_int_value_new(1)); } // VideoQualityType/0/1/2 - vquality_type_/target/bitrate/constant // *note: target is no longer used if (br) { ghb_dict_insert(dict, g_strdup("VideoQualityType"), ghb_int_value_new(1)); } else if (constant) { ghb_dict_insert(dict, g_strdup("VideoQualityType"), ghb_int_value_new(2)); } if (ghb_value_boolean(preset_dict_get_value(dict, "VideoFramerateCFR"))) { ghb_dict_insert(dict, g_strdup("VideoFramerateMode"), ghb_string_value_new("cfr")); } else if (ghb_value_boolean(preset_dict_get_value(dict, "VideoFrameratePFR"))) { ghb_dict_insert(dict, g_strdup("VideoFramerateMode"), ghb_string_value_new("pfr")); } else { ghb_dict_insert(dict, g_strdup("VideoFramerateMode"), ghb_string_value_new("vfr")); } if (ghb_value_int(preset_dict_get_value(dict, "PictureDeblock")) < 5) { ghb_dict_insert(dict, g_strdup("PictureDeblock"), ghb_int_value_new(0)); } GValue *alist, *adict; gint count, ii; alist = ghb_dict_lookup(dict, "AudioList"); count = ghb_array_len(alist); for (ii = 0; ii < count; ii++) { gdouble drc; adict = ghb_array_get_nth(alist, ii); drc = ghb_value_double( preset_dict_get_value(adict, "AudioTrackDRCSlider")); if (drc < 1.0) { ghb_dict_insert(adict, g_strdup("AudioTrackDRCSlider"), ghb_double_value_new(0.0)); } } if (ghb_value_boolean(preset_dict_get_value(dict, "x264UseAdvancedOptions"))) { ghb_dict_remove(dict, "VideoPreset"); ghb_dict_remove(dict, "VideoTune"); ghb_dict_remove(dict, "VideoProfile"); ghb_dict_remove(dict, "VideoLevel"); ghb_dict_remove(dict, "VideoOptionExtra"); } const char *tune = dict_get_string(dict, "VideoTune"); if (tune != NULL) { GString *str = g_string_new(""); char *tunes; g_string_append_printf(str, "%s", tune); if (dict_get_boolean(dict, "x264FastDecode")) { g_string_append_printf(str, ",%s", "fastdecode"); } if (dict_get_boolean(dict, "x264ZeroLatency")) { g_string_append_printf(str, ",%s", "zerolatency"); } tunes = g_string_free(str, FALSE); ghb_dict_insert(dict, g_strdup("VideoTune"), ghb_string_value_new(tunes)); g_free(tunes); } // Remove everything from dist that isn't in "Presets" GValue *internal; GValue *internalPlist = ghb_resource_get("internal-defaults"); internal = plist_get_dict(internalPlist, "Presets"); dict_clean(dict, internal); export_value_xlat(dict); } static void export_xlat_presets(GValue *presets) { gint count, ii; GValue *dict; gboolean folder; if (presets == NULL) return; count = ghb_array_len(presets); for (ii = 0; ii < count; ii++) { dict = ghb_array_get_nth(presets, ii); folder = ghb_value_boolean(preset_dict_get_value(dict, "Folder")); if (folder) { GValue *nested; nested = ghb_dict_lookup(dict, "ChildrenArray"); export_xlat_presets(nested); } else { export_xlat_preset(dict); } } } static guint prefs_timeout_id = 0; static gboolean delayed_store_prefs(gpointer data) { store_plist(prefsPlist, "preferences"); prefs_timeout_id = 0; return FALSE; } static void store_presets() { GValue *export; export = ghb_value_dup(presetsPlist); export_xlat_presets(export); store_plist(export, "presets"); ghb_value_free(export); } static void store_prefs(void) { if (prefs_timeout_id != 0) { GMainContext *mc; GSource *source; mc = g_main_context_default(); source = g_main_context_find_source_by_id(mc, prefs_timeout_id); if (source != NULL) g_source_destroy(source); } prefs_timeout_id = g_timeout_add_seconds(1, (GSourceFunc)delayed_store_prefs, NULL); } void ghb_presets_reload(signal_user_data_t *ud) { GValue *std_presets; gint count, ii; int *indices, len; g_debug("ghb_presets_reload()\n"); std_presets = ghb_value_dup(ghb_resource_get("standard-presets")); if (std_presets == NULL) return; remove_std_presets(ud); indices = presets_find_default(presetsPlist, &len); if (indices) { presets_clear_default(std_presets); g_free(indices); } // Merge the keyfile contents into our presets count = ghb_array_len(std_presets); for (ii = count-1; ii >= 0; ii--) { GValue *std_dict; GValue *copy_dict; gint indices = 0; std_dict = ghb_array_get_nth(std_presets, ii); copy_dict = ghb_value_dup(std_dict); ghb_dict_insert(copy_dict, g_strdup("PresetBuildNumber"), ghb_int64_value_new(hb_get_build(NULL))); ghb_presets_insert(presetsPlist, copy_dict, &indices, 1); presets_list_insert(ud, &indices, 1); } import_xlat_presets(presetsPlist); store_presets(); ghb_value_free(std_presets); } static gboolean check_old_presets(GValue *presetsArray) { gint count, ii; count = ghb_array_len(presetsArray); for (ii = count-1; ii >= 0; ii--) { GValue *dict; GValue *type; dict = ghb_array_get_nth(presetsArray, ii); type = ghb_dict_lookup(dict, "Type"); if (type == NULL) return TRUE; } return FALSE; } static void replace_standard_presets(GValue *presetsArray) { GValue *std_presets, *tmp; int *indices, len; gint count, ii; // Remove existing standard presets count = ghb_array_len(presetsArray); for (ii = count-1; ii >= 0; ii--) { GValue *dict; gint ptype; dict = ghb_array_get_nth(presetsArray, ii); ptype = ghb_value_int(preset_dict_get_value(dict, "Type")); if (ptype == PRESETS_BUILTIN) { gint indices = 0; ghb_presets_remove(presetsArray, &indices, 1); } } // Get the default standard presets tmp = ghb_resource_get("standard-presets"); if (tmp == NULL) return; std_presets = ghb_value_dup(tmp); // Clear the default in the standard presets if one is already set // in custom presets indices = presets_find_default(presetsArray, &len); if (indices) { presets_clear_default(std_presets); g_free(indices); } // Merge the keyfile contents into our presets count = ghb_array_len(std_presets); for (ii = count-1; ii >= 0; ii--) { GValue *std_dict; GValue *copy_dict; gint indices = 0; std_dict = ghb_array_get_nth(std_presets, ii); copy_dict = ghb_value_dup(std_dict); ghb_dict_insert(copy_dict, g_strdup("PresetBuildNumber"), ghb_int64_value_new(hb_get_build(NULL))); ghb_presets_insert(presetsArray, copy_dict, &indices, 1); } ghb_value_free(std_presets); } static int update_standard_presets(signal_user_data_t *ud, GValue *presetsArray) { gint count, ii; count = ghb_array_len(presetsArray); for (ii = count-1; ii >= 0; ii--) { GValue *dict; const GValue *gval; gint64 build; gint type; dict = ghb_array_get_nth(presetsArray, ii); gval = ghb_dict_lookup(dict, "Type"); if (gval == NULL) { // Old preset that doesn't have a Type replace_standard_presets(presetsArray); return 1; } type = ghb_value_int(gval); if (type == 0) { gval = ghb_dict_lookup(dict, "PresetBuildNumber"); if (gval == NULL) { // Old preset that doesn't have a build number replace_standard_presets(presetsArray); return 1; } build = ghb_value_int64(gval); if (build != hb_get_build(NULL)) { // Build number does not match replace_standard_presets(presetsArray); return 1; } } } return 0; } void ghb_presets_load(signal_user_data_t *ud) { gboolean store = FALSE; presetsPlistFile = load_plist("presets"); if ((presetsPlistFile == NULL) || (G_VALUE_TYPE(presetsPlistFile) == ghb_dict_get_type()) || (check_old_presets(presetsPlistFile))) { presetsPlistFile = ghb_resource_get("standard-presets"); store = TRUE; } else { update_standard_presets(ud, presetsPlistFile); } presetsPlist = ghb_value_dup(presetsPlistFile); import_xlat_presets(presetsPlist); if (store) store_presets(); } static void settings_save(signal_user_data_t *ud, const GValue *path) { GValue *dict; gint *indices, len, count; const gchar *name; gboolean replace = FALSE; g_debug("settings_save"); GValue *internalPlist = ghb_resource_get("internal-defaults"); if (internalPlist == NULL) return; count = ghb_array_len(path); name = g_value_get_string(ghb_array_get_nth(path, count-1)); indices = ghb_preset_indices_from_path(presetsPlist, path, &len); if (indices) { if (ghb_presets_get_folder(presetsPlist, indices, len)) { gchar *message; message = g_strdup_printf( _("%s: Folder already exists.\n" "You can not replace it with a preset."), name); ghb_message_dialog(GTK_MESSAGE_ERROR, message, _("Cancel"), NULL); g_free(message); return; } dict = ghb_value_dup(ud->settings); ghb_presets_replace(presetsPlist, dict, indices, len); replace = TRUE; } else { indices = presets_find_pos(path, PRESETS_CUSTOM, &len); if (indices) { dict = ghb_value_dup(ud->settings); ghb_presets_insert(presetsPlist, dict, indices, len); } else { g_warning("failed to find insert path"); return; } } current_preset = dict; ghb_settings_set_int64(dict, "Type", PRESETS_CUSTOM); ghb_settings_set_int64(dict, "PresetBuildNumber", hb_get_build(NULL)); ghb_dict_insert(dict, g_strdup("PresetName"), ghb_string_value_new(name)); if (replace) { gint *def_indices, def_len; def_indices = presets_find_default(presetsPlist, &def_len); if (def_indices != NULL && preset_path_cmp(indices, len, def_indices, def_len) != 0) { ghb_dict_insert(dict, g_strdup("Default"), ghb_boolean_value_new(FALSE)); } presets_list_update_item(ud, indices, len, FALSE); g_free(def_indices); } else { ghb_dict_insert(dict, g_strdup("Default"), ghb_boolean_value_new(FALSE)); presets_list_insert(ud, indices, len); } if (!ghb_settings_get_boolean(ud->settings, "PictureWidthEnable")) { ghb_dict_remove(dict, "PictureWidth"); } if (!ghb_settings_get_boolean(ud->settings, "PictureHeightEnable")) { ghb_dict_remove(dict, "PictureHeight"); } ghb_dict_insert(dict, g_strdup("autoscale"), ghb_boolean_value_new( !ghb_settings_get_boolean(ud->settings, "PictureWidthEnable") && !ghb_settings_get_boolean(ud->settings, "PictureHeightEnable") ) ); store_presets(); ud->dont_clear_presets = TRUE; // Make the new preset the selected item ghb_select_preset2(ud->builder, indices, len); g_free(indices); ud->dont_clear_presets = FALSE; return; } static void folder_save(signal_user_data_t *ud, const GValue *path) { GValue *dict, *folder; gint *indices, len, count; const gchar *name; count = ghb_array_len(path); name = g_value_get_string(ghb_array_get_nth(path, count-1)); indices = ghb_preset_indices_from_path(presetsPlist, path, &len); if (indices) { if (!ghb_presets_get_folder(presetsPlist, indices, len)) { gchar *message; message = g_strdup_printf( _("%s: Preset already exists.\n" "You can not replace it with a folder."), name); ghb_message_dialog(GTK_MESSAGE_ERROR, message, _("Cancel"), NULL); g_free(message); g_free(indices); return; } // Already exists, update its description dict = presets_get_dict(presetsPlist, indices, len); ghb_dict_insert(dict, g_strdup("PresetDescription"), ghb_value_dup(preset_dict_get_value( ud->settings, "PresetDescription"))); presets_list_update_item(ud, indices, len, FALSE); g_free(indices); store_presets(); return; } else { indices = presets_find_pos(path, PRESETS_CUSTOM, &len); if (indices) { dict = ghb_dict_value_new(); ghb_presets_insert(presetsPlist, dict, indices, len); } else { g_warning("failed to find insert path"); return; } } ghb_dict_insert(dict, g_strdup("PresetDescription"), ghb_value_dup(preset_dict_get_value( ud->settings, "PresetDescription"))); ghb_dict_insert(dict, g_strdup("PresetName"), ghb_string_value_new(name)); folder = ghb_array_value_new(8); ghb_dict_insert(dict, g_strdup("ChildrenArray"), folder); ghb_dict_insert(dict, g_strdup("Type"), ghb_int64_value_new(PRESETS_CUSTOM)); ghb_dict_insert(dict, g_strdup("Folder"), ghb_boolean_value_new(TRUE)); presets_list_insert(ud, indices, len); g_free(indices); store_presets(); return; } void ghb_presets_list_show_default(signal_user_data_t *ud) { GtkTreeView *treeview; GtkTreePath *treepath; GtkTreeIter iter; GtkTreeStore *store; gint *indices, len; g_debug("ghb_presets_list_show_default()"); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list")); store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); indices = presets_find_default(presetsPlist, &len); if (indices == NULL) return; treepath = ghb_tree_path_new_from_indices(indices, len); if (treepath) { if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath)) { gtk_tree_store_set(store, &iter, 1, 800, 2, 2 , -1); } gtk_tree_path_free(treepath); } g_free(indices); } void ghb_presets_list_clear_default(signal_user_data_t *ud) { GtkTreeView *treeview; GtkTreePath *treepath; GtkTreeIter iter; GtkTreeStore *store; gint *indices, len; g_debug("ghb_presets_list_clear_default ()"); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list")); store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); indices = presets_find_default(presetsPlist, &len); if (indices == NULL) return; treepath = ghb_tree_path_new_from_indices(indices, len); if (treepath) { if (gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath)) { gtk_tree_store_set(store, &iter, 1, 400, 2, 0 , -1); } gtk_tree_path_free(treepath); } g_free(indices); } static void update_subtitle_presets(signal_user_data_t *ud) { g_debug("update_subtitle_presets"); const GValue *subtitle_list, *subtitle; GValue *slist, *dict; gint count, ii, source; subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list"); slist = ghb_array_value_new(8); count = ghb_array_len(subtitle_list); for (ii = 0; ii < count; ii++) { subtitle = ghb_array_get_nth(subtitle_list, ii); source = ghb_settings_get_int(subtitle, "SubtitleSource"); if (source != SRTSUB) { dict = ghb_value_dup(subtitle); ghb_array_append(slist, dict); } } ghb_settings_take_value(ud->settings, "SubtitleList", slist); } G_MODULE_EXPORT void preset_import_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GtkWidget *dialog; GtkResponseType response; const gchar *exportDir; gchar *filename; GtkFileFilter *filter; g_debug("preset_import_clicked_cb ()"); dialog = gtk_file_chooser_dialog_new("Import Preset", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GHB_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GHB_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, _("All (*)")); gtk_file_filter_add_pattern(filter, "*"); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, _("Presets (*.plist)")); gtk_file_filter_add_pattern(filter, "*.plist"); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter); exportDir = ghb_settings_get_const_string(ud->prefs, "ExportDirectory"); if (exportDir == NULL || exportDir[0] == '\0') { exportDir = "."; } gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), exportDir); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); if (response == GTK_RESPONSE_ACCEPT) { GValue *dict, *array; gchar *dir; gint count, ii; filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); // import the preset if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) { gtk_widget_destroy(dialog); g_free(filename); return; } array = ghb_plist_parse_file(filename); import_xlat_presets(array); presets_clear_default(array); presets_customize(array); count = ghb_array_len(array); for (ii = 0; ii < count; ii++) { GValue *path, *name; gint *indices, len; gint index = 1; dict = ghb_array_get_nth(array, ii); path = ghb_array_value_new(1); name = ghb_value_dup(ghb_dict_lookup(dict, "PresetName")); ghb_array_append(path, name); indices = ghb_preset_indices_from_path(presetsPlist, path, &len); // Modify the preset name till we make it unique while (indices != NULL) { gchar *str = ghb_value_string(name); ghb_value_free(path); g_free(indices); str = g_strdup_printf("%s %d", str, index); path = ghb_array_value_new(1); name = ghb_string_value_new(str); ghb_array_append(path, name); g_free(str); index++; indices = ghb_preset_indices_from_path(presetsPlist, path, &len); } ghb_dict_insert(dict, g_strdup("PresetName"), ghb_value_dup(name)); indices = presets_find_pos(path, PRESETS_CUSTOM, &len); ghb_presets_insert(presetsPlist, ghb_value_dup(dict), indices, len); presets_list_insert(ud, indices, len); ghb_value_free(path); g_free(indices); } ghb_value_free(array); exportDir = ghb_settings_get_const_string(ud->prefs, "ExportDirectory"); dir = g_path_get_dirname(filename); if (strcmp(dir, exportDir) != 0) { ghb_settings_set_string(ud->prefs, "ExportDirectory", dir); ghb_pref_save(ud->prefs, "ExportDirectory"); } g_free(filename); g_free(dir); store_presets(); } gtk_widget_destroy(dialog); } GValue* get_selected_path(signal_user_data_t *ud) { GtkTreeView *treeview; GtkTreeSelection *selection; GtkTreeModel *store; GtkTreeIter iter; treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list")); selection = gtk_tree_view_get_selection(treeview); if (gtk_tree_selection_get_selected(selection, &store, &iter)) { GtkTreePath *treepath; gint *indices, len; GValue *path; treepath = gtk_tree_model_get_path(store, &iter); indices = gtk_tree_path_get_indices(treepath); len = gtk_tree_path_get_depth(treepath); path = preset_path_from_indices(presetsPlist, indices, len); return path; } return NULL; } G_MODULE_EXPORT void preset_export_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GtkWidget *dialog; GtkResponseType response; GValue *preset; const gchar *name = ""; gint count, *indices, len; const gchar *exportDir; gchar *filename; g_debug("preset_export_clicked_cb ()"); preset = get_selected_path(ud); if (preset == NULL) return; count = ghb_array_len(preset); if (count <= 0) { ghb_value_free(preset); return; } name = g_value_get_string(ghb_array_get_nth(preset, count-1)); dialog = gtk_file_chooser_dialog_new(_("Export Preset"), NULL, GTK_FILE_CHOOSER_ACTION_SAVE, GHB_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GHB_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); exportDir = ghb_settings_get_const_string(ud->prefs, "ExportDirectory"); if (exportDir == NULL || exportDir[0] == '\0') { exportDir = "."; } filename = g_strdup_printf("%s.plist", name); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), exportDir); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), filename); g_free(filename); indices = ghb_preset_indices_from_path(presetsPlist, preset, &len); if (indices == NULL) { ghb_value_free(preset); return; } response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); if (response == GTK_RESPONSE_ACCEPT) { GValue *export, *dict, *array; FILE *file; gchar *dir; filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); // export the preset dict = presets_get_dict(presetsPlist, indices, len); export = ghb_value_dup(dict); array = ghb_array_value_new(1); ghb_array_append(array, export); presets_clear_default(array); presets_customize(array); export_xlat_presets(array); file = g_fopen(filename, "w"); if (file != NULL) { ghb_plist_write(file, array); fclose(file); } ghb_value_free(array); exportDir = ghb_settings_get_const_string(ud->prefs, "ExportDirectory"); dir = g_path_get_dirname(filename); if (strcmp(dir, exportDir) != 0) { ghb_settings_set_string(ud->prefs, "ExportDirectory", dir); ghb_pref_save(ud->prefs, "ExportDirectory"); } g_free(dir); g_free(filename); } gtk_widget_destroy(dialog); g_free(indices); ghb_value_free(preset); } G_MODULE_EXPORT void presets_new_folder_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GtkWidget *dialog; GtkEntry *entry; GtkTextView *desc; GtkResponseType response; GValue *preset, *dict; const gchar *name = ""; const gchar *description = ""; gint count, *indices, len; g_debug("presets_new_folder_clicked_cb ()"); preset = get_selected_path(ud); count = ghb_array_len(preset); if (count > 0) name = g_value_get_string(ghb_array_get_nth(preset, count-1)); else count = 1; indices = ghb_preset_indices_from_path(presetsPlist, preset, &len); dict = presets_get_dict(presetsPlist, indices, len); if (dict != NULL) { description = g_value_get_string( ghb_dict_lookup(dict, "PresetDescription")); ghb_ui_update(ud, "FolderDescription", ghb_string_value(description)); } desc = GTK_TEXT_VIEW(GHB_WIDGET(ud->builder, "FolderDescription")); dialog = GHB_WIDGET(ud->builder, "preset_new_folder_dialog"); entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "FolderName")); gtk_entry_set_text(entry, name); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); if (response == GTK_RESPONSE_OK) { // save the preset const gchar *name = gtk_entry_get_text(entry); GValue *dest; if (count > MAX_NESTED_PRESET-1) count = MAX_NESTED_PRESET-1; dest = ghb_array_value_new(MAX_NESTED_PRESET); if (indices != NULL) { gint ptype; ptype = ghb_presets_get_type(presetsPlist, indices, len); if (ptype == PRESETS_CUSTOM) { ghb_array_copy(dest, preset, count-1); } } ghb_array_append(dest, ghb_string_value_new(name)); GValue *val = ghb_widget_value(GTK_WIDGET(desc)); ghb_settings_set_value(ud->settings, "PresetDescription", val); folder_save(ud, dest); ghb_value_free(dest); } g_free(indices); ghb_value_free(preset); } G_MODULE_EXPORT void presets_save_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GtkWidget *dialog; GtkEntry *entry; GtkTextView *desc; GtkResponseType response; GValue *preset; const gchar *name = ""; gint count, *indices, len; g_debug("presets_save_clicked_cb ()"); preset = get_selected_path(ud); if (preset == NULL) preset = ghb_value_dup(ghb_settings_get_value(ud->settings, "preset")); count = ghb_array_len(preset); if (count > 0) name = g_value_get_string(ghb_array_get_nth(preset, count-1)); else count = 1; desc = GTK_TEXT_VIEW(GHB_WIDGET(ud->builder, "PresetDescription")); int width = ghb_settings_get_int(ud->settings, "PictureWidth"); int height = ghb_settings_get_int(ud->settings, "PictureHeight"); gboolean autoscale = ghb_settings_get_boolean(ud->settings, "autoscale"); ghb_ui_update(ud, "PictureWidthEnable", ghb_boolean_value(!autoscale)); ghb_ui_update(ud, "PictureHeightEnable", ghb_boolean_value(!autoscale)); if (!width) { width = ghb_settings_get_int(ud->settings, "scale_width"); ghb_ui_update(ud, "PictureWidth", ghb_int_value(width)); } if (!height) { height = ghb_settings_get_int(ud->settings, "scale_height"); ghb_ui_update(ud, "PictureHeight", ghb_int_value(height)); } dialog = GHB_WIDGET(ud->builder, "preset_save_dialog"); entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "PresetName")); gtk_entry_set_text(entry, name); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); if (response == GTK_RESPONSE_OK) { // save the preset const gchar *name = gtk_entry_get_text(entry); GValue *dest; dest = ghb_array_value_new(MAX_NESTED_PRESET); indices = ghb_preset_indices_from_path(presetsPlist, preset, &len); if (indices) { gint ptype; ptype = ghb_presets_get_type(presetsPlist, indices, len); if (ptype == PRESETS_CUSTOM) { ghb_array_copy(dest, preset, count-1); } g_free(indices); } ghb_array_append(dest, ghb_string_value_new(name)); ghb_widget_to_setting(ud->settings, GTK_WIDGET(desc)); update_subtitle_presets(ud); settings_save(ud, dest); ghb_value_free(dest); } ghb_value_free(preset); } G_MODULE_EXPORT void preset_type_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); } G_MODULE_EXPORT void presets_restore_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GValue *preset; g_debug("presets_restore_clicked_cb ()"); // Reload only the standard presets ghb_presets_reload(ud); // Updating the presets list shuffles things around // need to make sure the proper preset is selected preset = ghb_settings_get_value(ud->settings, "preset"); ghb_select_preset(ud->builder, preset); } G_MODULE_EXPORT void presets_remove_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GtkTreeView *treeview; GtkTreeSelection *selection; GtkTreeModel *store; GtkTreeIter iter; gchar *preset; GtkResponseType response; g_debug("presets_remove_clicked_cb ()"); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list")); selection = gtk_tree_view_get_selection (treeview); if (gtk_tree_selection_get_selected(selection, &store, &iter)) { GtkWidget *dialog; GtkTreePath *path; gint *indices, len; gboolean folder; gtk_tree_model_get(store, &iter, 0, &preset, -1); path = gtk_tree_model_get_path(store, &iter); indices = gtk_tree_path_get_indices(path); len = gtk_tree_path_get_depth(path); folder = ghb_presets_get_folder(presetsPlist, indices, len); dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("Confirm deletion of %s:\n\n%s"), folder ? _("folder") : _("preset"), preset); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy (dialog); if (response == GTK_RESPONSE_YES) { GtkTreeIter nextIter = iter; gboolean valid = TRUE; if (!gtk_tree_model_iter_next(store, &nextIter)) { if (!gtk_tree_model_iter_parent(store, &nextIter, &iter)) { valid = FALSE; } } // Remove the selected item // First unselect it so that selecting the new item works properly gtk_tree_selection_unselect_iter (selection, &iter); if (ghb_presets_remove(presetsPlist, indices, len)) { store_presets(); presets_list_remove(ud, indices, len); } if (!valid) valid = gtk_tree_model_get_iter_first(store, &nextIter); if (valid) { GtkTreePath *path; gint *indices; path = gtk_tree_model_get_path(store, &nextIter); indices = gtk_tree_path_get_indices(path); len = gtk_tree_path_get_depth(path); ghb_select_preset2(ud->builder, indices, len); gtk_tree_path_free(path); } } gtk_tree_path_free(path); g_free(preset); } } // controls where valid drop locations are G_MODULE_EXPORT gboolean presets_drag_motion_cb( GtkTreeView *tv, GdkDragContext *ctx, gint x, gint y, guint time, signal_user_data_t *ud) { GtkTreePath *path = NULL; GtkTreeViewDropPosition drop_pos; gint *indices, len; GtkTreeIter iter; GtkTreeView *srctv; GtkTreeModel *model; GtkTreeSelection *select; gint src_ptype, dst_ptype; gboolean src_folder, dst_folder; GValue *preset; gint tree_depth, ii; GtkWidget *widget; widget = gtk_drag_get_source_widget(ctx); if (widget == NULL || widget != GTK_WIDGET(tv)) return TRUE; // Get the type of the object being dragged srctv = GTK_TREE_VIEW(gtk_drag_get_source_widget(ctx)); select = gtk_tree_view_get_selection (srctv); gtk_tree_selection_get_selected (select, &model, &iter); path = gtk_tree_model_get_path (model, &iter); indices = gtk_tree_path_get_indices(path); len = gtk_tree_path_get_depth(path); preset = presets_get_dict(presetsPlist, indices, len); tree_depth = preset_tree_depth(preset); src_ptype = ghb_presets_get_type(presetsPlist, indices, len); src_folder = ghb_presets_get_folder(presetsPlist, indices, len); gtk_tree_path_free(path); if (src_folder && tree_depth == 1) tree_depth = 2; // The rest checks that the destination is a valid position // in the list. gtk_tree_view_get_dest_row_at_pos (tv, x, y, &path, &drop_pos); if (path == NULL) { gdk_drag_status(ctx, 0, time); return TRUE; } // Don't allow repositioning of builtin presets if (src_ptype != PRESETS_CUSTOM) { gdk_drag_status(ctx, 0, time); return TRUE; } len = gtk_tree_path_get_depth(path); if (len+tree_depth-1 >= MAX_NESTED_PRESET) { if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE) drop_pos = GTK_TREE_VIEW_DROP_BEFORE; if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) drop_pos = GTK_TREE_VIEW_DROP_AFTER; } for (ii = len+tree_depth-1; ii > MAX_NESTED_PRESET; ii--) gtk_tree_path_up(path); indices = gtk_tree_path_get_indices(path); len = gtk_tree_path_get_depth(path); dst_ptype = ghb_presets_get_type(presetsPlist, indices, len); dst_folder = ghb_presets_get_folder(presetsPlist, indices, len); // Don't allow mixing custom presets in the builtins if (dst_ptype != PRESETS_CUSTOM) { gdk_drag_status(ctx, 0, time); return TRUE; } // Only allow *drop into* for folders if (!dst_folder) { if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE) drop_pos = GTK_TREE_VIEW_DROP_BEFORE; if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) drop_pos = GTK_TREE_VIEW_DROP_AFTER; } len = gtk_tree_path_get_depth(path); gtk_tree_view_set_drag_dest_row(tv, path, drop_pos); gtk_tree_path_free(path); gdk_drag_status(ctx, GDK_ACTION_MOVE, time); return TRUE; } G_MODULE_EXPORT void presets_drag_cb( GtkTreeView *dstwidget, GdkDragContext *dc, gint x, gint y, GtkSelectionData *selection_data, guint info, guint t, signal_user_data_t *ud) { GtkTreePath *path = NULL; GtkTreeViewDropPosition drop_pos; GtkTreeIter dstiter, srciter; gint *dst_indices, dst_len, *src_indices, src_len; gint src_ptype; gboolean src_folder, dst_folder; GtkTreeModel *dstmodel = gtk_tree_view_get_model(dstwidget); g_debug("preset_drag_cb ()"); // This doesn't work here for some reason... // gtk_tree_view_get_drag_dest_row(dstwidget, &path, &drop_pos); gtk_tree_view_get_dest_row_at_pos (dstwidget, x, y, &path, &drop_pos); // This little hack is needed because attempting to drop after // the last item gives us no path or drop_pos. if (path == NULL) { gint n_children; n_children = gtk_tree_model_iter_n_children(dstmodel, NULL); if (n_children) { drop_pos = GTK_TREE_VIEW_DROP_AFTER; path = gtk_tree_path_new_from_indices(n_children-1, -1); } else { drop_pos = GTK_TREE_VIEW_DROP_BEFORE; path = gtk_tree_path_new_from_indices(0, -1); } } if (path) { GtkTreeView *srcwidget; GtkTreeModel *srcmodel; GtkTreeSelection *select; GtkTreePath *srcpath = NULL; GValue *preset; gint tree_depth, ii; srcwidget = GTK_TREE_VIEW(gtk_drag_get_source_widget(dc)); select = gtk_tree_view_get_selection (srcwidget); gtk_tree_selection_get_selected (select, &srcmodel, &srciter); srcpath = gtk_tree_model_get_path (srcmodel, &srciter); src_indices = gtk_tree_path_get_indices(srcpath); src_len = gtk_tree_path_get_depth(srcpath); src_ptype = ghb_presets_get_type(presetsPlist, src_indices, src_len); src_folder = ghb_presets_get_folder(presetsPlist, src_indices, src_len); preset = ghb_value_dup( presets_get_dict(presetsPlist, src_indices, src_len)); gtk_tree_path_free(srcpath); // Don't allow repositioning of builtin presets if (src_ptype != PRESETS_CUSTOM) return; tree_depth = preset_tree_depth(preset); if (src_folder && tree_depth == 1) tree_depth = 2; dst_len = gtk_tree_path_get_depth(path); if (dst_len+tree_depth-1 >= MAX_NESTED_PRESET) { if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE) drop_pos = GTK_TREE_VIEW_DROP_BEFORE; if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) drop_pos = GTK_TREE_VIEW_DROP_AFTER; } for (ii = dst_len+tree_depth-1; ii > MAX_NESTED_PRESET; ii--) gtk_tree_path_up(path); dst_indices = gtk_tree_path_get_indices(path); dst_len = gtk_tree_path_get_depth(path); dst_folder = ghb_presets_get_folder(presetsPlist, dst_indices, dst_len); // Only allow *drop into* for folders if (!dst_folder) { if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE) drop_pos = GTK_TREE_VIEW_DROP_BEFORE; if (drop_pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) drop_pos = GTK_TREE_VIEW_DROP_AFTER; } if (gtk_tree_model_get_iter (dstmodel, &dstiter, path)) { GtkTreeIter iter; GtkTreePath *dstpath = NULL; switch (drop_pos) { case GTK_TREE_VIEW_DROP_BEFORE: gtk_tree_store_insert_before(GTK_TREE_STORE (dstmodel), &iter, NULL, &dstiter); break; case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: gtk_tree_store_insert(GTK_TREE_STORE (dstmodel), &iter, &dstiter, 0); break; case GTK_TREE_VIEW_DROP_AFTER: gtk_tree_store_insert_after(GTK_TREE_STORE (dstmodel), &iter, NULL, &dstiter); break; case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: gtk_tree_store_insert_after(GTK_TREE_STORE (dstmodel), &iter, &dstiter, 0); break; default: break; } dstpath = gtk_tree_model_get_path (dstmodel, &iter); dst_indices = gtk_tree_path_get_indices(dstpath); dst_len = gtk_tree_path_get_depth(dstpath); ghb_presets_insert(presetsPlist, preset, dst_indices, dst_len); gtk_tree_path_free(dstpath); srcpath = gtk_tree_model_get_path (srcmodel, &srciter); src_indices = gtk_tree_path_get_indices(srcpath); src_len = gtk_tree_path_get_depth(srcpath); ghb_presets_remove(presetsPlist, src_indices, src_len); gtk_tree_path_free(srcpath); gtk_tree_store_remove (GTK_TREE_STORE (srcmodel), &srciter); dstpath = gtk_tree_model_get_path (dstmodel, &iter); dst_indices = gtk_tree_path_get_indices(dstpath); dst_len = gtk_tree_path_get_depth(dstpath); presets_list_update_item(ud, dst_indices, dst_len, TRUE); gtk_tree_path_free(dstpath); store_presets(); } gtk_tree_path_free(path); } } void presets_row_expanded_cb( GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *path, signal_user_data_t *ud) { gint *indices, len; gboolean expanded, folder; GValue *dict; expanded = gtk_tree_view_row_expanded(treeview, path); indices = gtk_tree_path_get_indices(path); len = gtk_tree_path_get_depth(path); dict = presets_get_dict(presetsPlist, indices, len); if (preset_folder_is_open(dict)) { if (expanded) { return; } } else if (!expanded) { return; } folder = ghb_presets_get_folder(presetsPlist, indices, len); if (folder) { presets_set_folder_open(expanded, indices, len); } // Collapsing parent folder collapses all children if (!expanded) { GValue *presets = NULL; gint *more_indices, count, ii; more_indices = g_malloc((len+1)*sizeof(gint)); memcpy(more_indices, indices, len*sizeof(gint)); presets = presets_get_folder(presetsPlist, indices, len); count = ghb_array_len(presets); for (ii = 0; ii < count; ii++) { dict = ghb_array_get_nth(presets, ii); folder = ghb_preset_folder(dict); if (folder) { more_indices[len] = ii; presets_set_folder_open(expanded, more_indices, len+1); } } g_free(more_indices); } store_presets(); } GValue* ghb_get_current_preset(signal_user_data_t *ud) { GtkTreeView *tv; GtkTreeModel *tm; GtkTreeSelection *ts; GtkTreeIter ti; GValue *preset = NULL; tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list")); ts = gtk_tree_view_get_selection(tv); if (gtk_tree_selection_get_selected(ts, &tm, &ti)) { GtkTreePath *tp; gint *indices, len; tp = gtk_tree_model_get_path(tm, &ti); indices = gtk_tree_path_get_indices(tp); len = gtk_tree_path_get_depth(tp); preset = presets_get_dict(presetsPlist, indices, len); gtk_tree_path_free(tp); } return preset; } GValue* ghb_get_current_preset_path(signal_user_data_t *ud) { GtkTreeView *tv; GtkTreeModel *tm; GtkTreeSelection *ts; GtkTreeIter ti; GValue *path = NULL; tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list")); ts = gtk_tree_view_get_selection(tv); if (gtk_tree_selection_get_selected(ts, &tm, &ti)) { GtkTreePath *tp; gint *indices, len; tp = gtk_tree_model_get_path(tm, &ti); indices = gtk_tree_path_get_indices(tp); len = gtk_tree_path_get_depth(tp); path = preset_path_from_indices(presetsPlist, indices, len); gtk_tree_path_free(tp); } return path; } G_MODULE_EXPORT void presets_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud) { GtkTreeModel *store; GtkTreeIter iter; GtkWidget *widget; g_debug("presets_list_selection_changed_cb ()"); widget = GHB_WIDGET (ud->builder, "presets_remove"); if (gtk_tree_selection_get_selected(selection, &store, &iter)) { GtkTreePath *treepath; gint *indices, len; gboolean folder; treepath = gtk_tree_model_get_path(store, &iter); indices = gtk_tree_path_get_indices(treepath); len = gtk_tree_path_get_depth(treepath); folder = ghb_presets_get_folder(presetsPlist, indices, len); if (!folder && !ghb_settings_get_boolean(ud->settings, "preset_reload")) { ghb_set_preset_settings_from_indices(ud, indices, len); ghb_settings_set_boolean(ud->settings, "preset_modified", FALSE); ghb_set_current_title_settings(ud); ghb_load_settings(ud); } ghb_settings_set_boolean(ud->settings, "preset_reload", FALSE); gtk_tree_path_free(treepath); gtk_widget_set_sensitive(widget, TRUE); } else { g_debug(_("No selection??? Perhaps unselected.")); gtk_widget_set_sensitive(widget, FALSE); } } void ghb_clear_presets_selection(signal_user_data_t *ud) { GtkTreeView *treeview; GtkTreeSelection *selection; if (ud->dont_clear_presets) return; g_debug("ghb_clear_presets_selection()"); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list")); selection = gtk_tree_view_get_selection (treeview); gtk_tree_selection_unselect_all (selection); ghb_settings_set_boolean(ud->settings, "preset_modified", TRUE); } G_MODULE_EXPORT void presets_frame_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud) { GtkTreeView *treeview; GtkTreeSelection *selection; GtkTreeModel *store; GtkTreeIter iter; treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list")); selection = gtk_tree_view_get_selection(treeview); if (gtk_tree_selection_get_selected(selection, &store, &iter)) { GtkTreePath *path; path = gtk_tree_model_get_path (store, &iter); // Make the parent visible in scroll window if it is not. gtk_tree_view_scroll_to_cell (treeview, path, NULL, FALSE, 0, 0); gtk_tree_path_free(path); } } G_MODULE_EXPORT void presets_default_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GValue *preset; gint *indices, len; g_debug("presets_default_clicked_cb ()"); preset = get_selected_path(ud); indices = ghb_preset_indices_from_path(presetsPlist, preset, &len); if (indices) { if (!ghb_presets_get_folder(presetsPlist, indices, len)) { ghb_presets_list_clear_default(ud); presets_set_default(indices, len); ghb_presets_list_show_default(ud); } g_free(indices); } ghb_value_free(preset); } G_MODULE_EXPORT void preset_edited_cb( GtkCellRendererText *cell, gchar *path, gchar *text, signal_user_data_t *ud) { GtkTreePath *treepath; GtkTreeStore *store; GtkTreeView *treeview; GtkTreeIter iter; gint *indices, len, count; GValue *dict; GValue *preset, *dest; g_debug("preset_edited_cb ()"); g_debug("path (%s)", path); g_debug("text (%s)", text); preset = get_selected_path(ud); dest = ghb_array_value_new(MAX_NESTED_PRESET); count = ghb_array_len(preset); ghb_array_copy(dest, preset, count-1); ghb_array_append(dest, ghb_string_value_new(text)); indices = ghb_preset_indices_from_path(presetsPlist, dest, &len); ghb_value_free(dest); if (indices != NULL) { // Already exists g_free(indices); ghb_value_free(preset); return; } treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list")); store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview)); treepath = gtk_tree_path_new_from_string (path); indices = gtk_tree_path_get_indices(treepath); len = gtk_tree_path_get_depth(treepath); gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath); gtk_tree_store_set(store, &iter, 0, text, -1); dict = presets_get_dict(presetsPlist, indices, len); ghb_dict_insert(dict, g_strdup("PresetName"), ghb_string_value_new(text)); store_presets(); gtk_tree_path_free (treepath); ghb_value_free(preset); } G_MODULE_EXPORT void preset_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); } HandBrake-0.10.2/gtk/src/main.c0000664000175200017520000012626712465416500016524 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * main.c * Copyright (C) John Stebbins 2008-2015 * * main.c is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * main.c is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with main.c. If not, write to: * The Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "ghbcompat.h" #if defined(_ENABLE_GST) #include #endif #if !defined(_WIN32) #include #include #else #include #include #define pipe(phandles) _pipe (phandles, 4096, _O_BINARY) #endif #include #include #include #include "hb.h" #include "renderer_button.h" #include "hb-backend.h" #include "ghb-dvd.h" #include "ghbcellrenderertext.h" #include "values.h" #include "icons.h" #include "callbacks.h" #include "queuehandler.h" #include "audiohandler.h" #include "subtitlehandler.h" #include "x264handler.h" #include "settings.h" #include "resources.h" #include "presets.h" #include "preview.h" #include "ghbcompositor.h" /* * Standard gettext macros. */ #ifndef ENABLE_NLS # define textdomain(String) (String) # define gettext(String) (String) # define dgettext(Domain,Message) (Message) # define dcgettext(Domain,Message,Type) (Message) # define bindtextdomain(Domain,Directory) (Domain) # define _(String) (String) # define N_(String) (String) #endif #define BUILDER_NAME "ghb" GtkBuilder* create_builder_or_die(const gchar * name) { guint res = 0; GValue *gval; GError *error = NULL; const gchar *ghb_ui; const gchar *markup = N_("Unable to create %s.\n" "\n" "Internal error. Could not parse UI description.\n" "%s"); g_debug("create_builder_or_die()\n"); GtkBuilder *xml = gtk_builder_new(); gval = ghb_resource_get("ghb-ui"); ghb_ui = g_value_get_string(gval); if (xml != NULL) res = gtk_builder_add_from_string(xml, ghb_ui, -1, &error); if (!xml || !res) { GtkWidget *dialog = gtk_message_dialog_new_with_markup(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, gettext(markup), name, error->message); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); exit(EXIT_FAILURE); } return xml; } static GCallback self_symbol_lookup(const gchar * symbol_name) { static GModule *module = NULL; gpointer symbol = NULL; if (!module) module = g_module_open(NULL, 0); g_module_symbol(module, symbol_name, &symbol); return (GCallback) symbol; } static void MyConnect( GtkBuilder *builder, GObject *object, const gchar *signal_name, const gchar *handler_name, GObject *connect_object, GConnectFlags flags, gpointer user_data) { GCallback callback; g_return_if_fail(object != NULL); g_return_if_fail(handler_name != NULL); g_return_if_fail(signal_name != NULL); //const gchar *name = ghb_get_setting_key((GtkWidget*)object); //g_message("\n\nname %s", name); g_debug("handler_name %s", handler_name); g_debug("signal_name %s", signal_name); callback = self_symbol_lookup(handler_name); if (!callback) { g_message("Signal handler (%s) not found", handler_name); return; } if (connect_object) { g_signal_connect_object(object, signal_name, callback, connect_object, flags); } else { if (flags & G_CONNECT_AFTER) { g_signal_connect_after( object, signal_name, callback, user_data); } else { g_signal_connect(object, signal_name, callback, user_data); } } } #if 0 // If you should ever need to change the font for the running application.. // Ugly but effective. void change_font(GtkWidget *widget, gpointer data) { PangoFontDescription *font_desc; gchar *font = (gchar*)data; const gchar *name; font_desc = pango_font_description_from_string(font); if (font_desc == NULL) exit(1); gtk_widget_modify_font(widget, font_desc); name = ghb_get_setting_key(widget); g_debug("changing font for widget %s\n", name); if (GTK_IS_CONTAINER(widget)) { gtk_container_foreach((GtkContainer*)widget, change_font, data); } } //gtk_container_foreach((GtkContainer*)window, change_font, "sans 20"); #endif extern G_MODULE_EXPORT void chapter_edited_cb(void); extern G_MODULE_EXPORT void chapter_keypress_cb(void); // Create and bind the tree model to the tree view for the chapter list // Also, connect up the signal that lets us know the selection has changed static void bind_chapter_tree_model(signal_user_data_t *ud) { GtkCellRenderer *cell; GtkTreeViewColumn *column; GtkListStore *treestore; GtkTreeView *treeview; g_debug("bind_chapter_tree_model()\n"); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list")); treestore = gtk_list_store_new(5, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN); gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(treestore)); cell = ghb_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _("Index"), cell, "text", 0, NULL); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); cell = ghb_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _("Start"), cell, "text", 1, NULL); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); cell = ghb_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _("Duration"), cell, "text", 2, NULL); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); cell = ghb_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _("Title"), cell, "text", 3, "editable", 4, NULL); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); g_signal_connect(cell, "key-press-event", chapter_keypress_cb, ud); g_signal_connect(cell, "edited", chapter_edited_cb, ud); g_debug("Done\n"); } extern G_MODULE_EXPORT void queue_list_selection_changed_cb(void); extern G_MODULE_EXPORT void queue_remove_clicked_cb(void); extern G_MODULE_EXPORT void queue_list_size_allocate_cb(void); extern G_MODULE_EXPORT void queue_drag_cb(void); extern G_MODULE_EXPORT void queue_drag_motion_cb(void); // Create and bind the tree model to the tree view for the queue list // Also, connect up the signal that lets us know the selection has changed static void bind_queue_tree_model(signal_user_data_t *ud) { GtkCellRenderer *cell, *textcell; GtkTreeViewColumn *column; GtkTreeStore *treestore; GtkTreeView *treeview; GtkTreeSelection *selection; GtkTargetEntry SrcEntry; SrcEntry.target = "DATA"; SrcEntry.flags = GTK_TARGET_SAME_WIDGET; g_debug("bind_queue_tree_model()\n"); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list")); selection = gtk_tree_view_get_selection(treeview); treestore = gtk_tree_store_new(5, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT); gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(treestore)); column = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(column, _("Job Information")); cell = gtk_cell_renderer_spinner_new(); gtk_tree_view_column_pack_start(column, cell, FALSE); gtk_tree_view_column_add_attribute(column, cell, "active", 0); gtk_tree_view_column_add_attribute(column, cell, "pulse", 4); cell = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start(column, cell, FALSE); gtk_tree_view_column_add_attribute(column, cell, "icon-name", 1); textcell = gtk_cell_renderer_text_new(); g_object_set(textcell, "wrap-mode", PANGO_WRAP_CHAR, NULL); g_object_set(textcell, "wrap-width", 500, NULL); gtk_tree_view_column_pack_start(column, textcell, TRUE); gtk_tree_view_column_add_attribute(column, textcell, "markup", 2); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); gtk_tree_view_column_set_expand(column, TRUE); gtk_tree_view_column_set_max_width(column, 550); g_signal_connect(treeview, "size-allocate", queue_list_size_allocate_cb, textcell); cell = custom_cell_renderer_button_new(); column = gtk_tree_view_column_new_with_attributes( _(""), cell, "icon-name", 3, NULL); gtk_tree_view_column_set_min_width(column, 24); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); gtk_tree_view_enable_model_drag_dest(treeview, &SrcEntry, 1, GDK_ACTION_MOVE); gtk_tree_view_enable_model_drag_source(treeview, GDK_BUTTON1_MASK, &SrcEntry, 1, GDK_ACTION_MOVE); g_signal_connect(selection, "changed", queue_list_selection_changed_cb, ud); g_signal_connect(cell, "clicked", queue_remove_clicked_cb, ud); g_signal_connect(treeview, "drag_data_received", queue_drag_cb, ud); g_signal_connect(treeview, "drag_motion", queue_drag_motion_cb, ud); } extern G_MODULE_EXPORT void audio_list_selection_changed_cb(void); extern G_MODULE_EXPORT void audio_edit_clicked_cb(void); extern G_MODULE_EXPORT void audio_remove_clicked_cb(void); // Create and bind the tree model to the tree view for the audio track list // Also, connect up the signal that lets us know the selection has changed static void bind_audio_tree_model(signal_user_data_t *ud) { GtkCellRenderer *source_cell; GtkCellRenderer *arrow_cell; GtkCellRenderer *output_cell; GtkCellRenderer *edit_cell; GtkCellRenderer *delete_cell; GtkTreeViewColumn *column; GtkTreeStore *treestore; GtkTreeView *treeview; GtkTreeSelection *selection; g_debug("bind_audio_tree_model()\n"); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list")); selection = gtk_tree_view_get_selection(treeview); treestore = gtk_tree_store_new(6, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_FLOAT); gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(treestore)); source_cell = gtk_cell_renderer_text_new(); arrow_cell = gtk_cell_renderer_text_new(); output_cell = gtk_cell_renderer_text_new(); edit_cell = custom_cell_renderer_button_new(); delete_cell = custom_cell_renderer_button_new(); column = gtk_tree_view_column_new(); gtk_tree_view_column_set_spacing(column, 12); gtk_tree_view_column_set_title(column, _("Track Information")); gtk_tree_view_column_pack_start(column, source_cell, FALSE); gtk_tree_view_column_add_attribute(column, source_cell, "markup", 0); gtk_tree_view_column_add_attribute(column, source_cell, "yalign", 5); gtk_tree_view_column_pack_start(column, arrow_cell, FALSE); gtk_tree_view_column_add_attribute(column, arrow_cell, "text", 1); gtk_tree_view_column_pack_start(column, output_cell, TRUE); gtk_tree_view_column_add_attribute(column, output_cell, "markup", 2); gtk_tree_view_column_add_attribute(column, output_cell, "yalign", 5); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); gtk_tree_view_column_set_expand(column, TRUE); gtk_tree_view_column_set_max_width(column, 400); column = gtk_tree_view_column_new_with_attributes( _(""), edit_cell, "icon-name", 3, NULL); //gtk_tree_view_column_set_min_width(column, 24); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); column = gtk_tree_view_column_new_with_attributes( _(""), delete_cell, "icon-name", 4, NULL); //gtk_tree_view_column_set_min_width(column, 24); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); g_signal_connect(selection, "changed", audio_list_selection_changed_cb, ud); g_signal_connect(edit_cell, "clicked", audio_edit_clicked_cb, ud); g_signal_connect(delete_cell, "clicked", audio_remove_clicked_cb, ud); g_debug("Done\n"); } extern G_MODULE_EXPORT void subtitle_list_selection_changed_cb(void); extern G_MODULE_EXPORT void subtitle_edit_clicked_cb(void); extern G_MODULE_EXPORT void subtitle_remove_clicked_cb(void); // Create and bind the tree model to the tree view for the subtitle track list // Also, connect up the signal that lets us know the selection has changed static void bind_subtitle_tree_model(signal_user_data_t *ud) { GtkCellRenderer *source_cell; GtkCellRenderer *arrow_cell; GtkCellRenderer *output_cell; GtkCellRenderer *edit_cell; GtkCellRenderer *delete_cell; GtkTreeViewColumn *column; GtkTreeStore *treestore; GtkTreeView *treeview; GtkTreeSelection *selection; g_debug("bind_subtitle_tree_model()\n"); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list")); selection = gtk_tree_view_get_selection(treeview); treestore = gtk_tree_store_new(6, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_FLOAT); gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(treestore)); source_cell = gtk_cell_renderer_text_new(); arrow_cell = gtk_cell_renderer_text_new(); output_cell = gtk_cell_renderer_text_new(); edit_cell = custom_cell_renderer_button_new(); delete_cell = custom_cell_renderer_button_new(); column = gtk_tree_view_column_new(); gtk_tree_view_column_set_spacing(column, 12); gtk_tree_view_column_set_title(column, _("Track Information")); gtk_tree_view_column_pack_start(column, source_cell, FALSE); gtk_tree_view_column_add_attribute(column, source_cell, "markup", 0); gtk_tree_view_column_add_attribute(column, source_cell, "yalign", 5); gtk_tree_view_column_pack_start(column, arrow_cell, FALSE); gtk_tree_view_column_add_attribute(column, arrow_cell, "text", 1); gtk_tree_view_column_pack_start(column, output_cell, TRUE); gtk_tree_view_column_add_attribute(column, output_cell, "markup", 2); gtk_tree_view_column_add_attribute(column, output_cell, "yalign", 5); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); gtk_tree_view_column_set_expand(column, TRUE); gtk_tree_view_column_set_max_width(column, 400); column = gtk_tree_view_column_new_with_attributes( _(""), edit_cell, "icon-name", 3, NULL); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); column = gtk_tree_view_column_new_with_attributes( _(""), delete_cell, "icon-name", 4, NULL); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); g_signal_connect(selection, "changed", subtitle_list_selection_changed_cb, ud); g_signal_connect(edit_cell, "clicked", subtitle_edit_clicked_cb, ud); g_signal_connect(delete_cell, "clicked", subtitle_remove_clicked_cb, ud); } extern G_MODULE_EXPORT void presets_list_selection_changed_cb(void); extern G_MODULE_EXPORT void presets_drag_cb(void); extern G_MODULE_EXPORT void presets_drag_motion_cb(void); extern G_MODULE_EXPORT void preset_edited_cb(void); extern void presets_row_expanded_cb(void); // Create and bind the tree model to the tree view for the preset list // Also, connect up the signal that lets us know the selection has changed static void bind_presets_tree_model(signal_user_data_t *ud) { GtkCellRenderer *cell; GtkTreeViewColumn *column; GtkTreeStore *treestore; GtkTreeView *treeview; GtkTreeSelection *selection; GtkWidget *widget; GtkTargetEntry SrcEntry; SrcEntry.target = "DATA"; SrcEntry.flags = GTK_TARGET_SAME_WIDGET; g_debug("bind_presets_tree_model()\n"); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list")); selection = gtk_tree_view_get_selection(treeview); treestore = gtk_tree_store_new(6, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN); gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(treestore)); cell = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Preset Name"), cell, "text", 0, "weight", 1, "style", 2, "foreground", 3, "editable", 5, NULL); g_signal_connect(cell, "edited", preset_edited_cb, ud); gtk_tree_view_append_column(treeview, GTK_TREE_VIEW_COLUMN(column)); gtk_tree_view_column_set_expand(column, TRUE); gtk_tree_view_set_tooltip_column(treeview, 4); gtk_tree_view_enable_model_drag_dest(treeview, &SrcEntry, 1, GDK_ACTION_MOVE); gtk_tree_view_enable_model_drag_source(treeview, GDK_BUTTON1_MASK, &SrcEntry, 1, GDK_ACTION_MOVE); g_signal_connect(treeview, "drag_data_received", presets_drag_cb, ud); g_signal_connect(treeview, "drag_motion", presets_drag_motion_cb, ud); g_signal_connect(treeview, "row_expanded", presets_row_expanded_cb, ud); g_signal_connect(treeview, "row_collapsed", presets_row_expanded_cb, ud); g_signal_connect(selection, "changed", presets_list_selection_changed_cb, ud); widget = GHB_WIDGET(ud->builder, "presets_remove"); gtk_widget_set_sensitive(widget, FALSE); g_debug("Done\n"); } static void clean_old_logs() { #if !defined(_WIN32) const gchar *file; gchar *config; config = ghb_get_user_config_dir(NULL); if (g_file_test(config, G_FILE_TEST_IS_DIR)) { GDir *gdir = g_dir_open(config, 0, NULL); file = g_dir_read_name(gdir); while (file) { if (strncmp(file, "Activity.log.", 13) == 0) { gchar *path; int fd, lock = 1; int pid; sscanf(file, "Activity.log.%d", &pid); path = g_strdup_printf("%s/ghb.pid.%d", config, pid); if (g_file_test(path, G_FILE_TEST_EXISTS)) { fd = open(path, O_RDWR); if (fd >= 0) { lock = lockf(fd, F_TLOCK, 0); } g_free(path); close(fd); if (lock == 0) { path = g_strdup_printf("%s/%s", config, file); g_unlink(path); g_free(path); } } else { g_free(path); path = g_strdup_printf("%s/%s", config, file); g_unlink(path); g_free(path); } } file = g_dir_read_name(gdir); } g_dir_close(gdir); } g_free(config); #else const gchar *file; gchar *config; config = ghb_get_user_config_dir(NULL); if (g_file_test(config, G_FILE_TEST_IS_DIR)) { GDir *gdir = g_dir_open(config, 0, NULL); file = g_dir_read_name(gdir); while (file) { if (strncmp(file, "Activity.log.", 13) == 0) { gchar *path; int pid; sscanf(file, "Activity.log.%d", &pid); #if 0 int fd, lock = 1; path = g_strdup_printf("%s/ghb.pid.%d", config, pid); if (g_file_test(path, G_FILE_TEST_EXISTS)) { fd = open(path, O_RDWR); if (fd >= 0) { lock = lockf(fd, F_TLOCK, 0); } g_free(path); close(fd); if (lock == 0) { path = g_strdup_printf("%s/%s", config, file); g_unlink(path); g_free(path); } } else #endif { //g_free(path); path = g_strdup_printf("%s/%s", config, file); g_unlink(path); g_free(path); } } file = g_dir_read_name(gdir); } g_dir_close(gdir); } g_free(config); #endif } static void IoRedirect(signal_user_data_t *ud) { GIOChannel *channel; gint pfd[2]; gchar *config, *path, *str; pid_t pid; // I'm opening a pipe and attaching the writer end to stderr // The reader end will be polled by main event loop and I'll get // a callback when there is data available. if (pipe( pfd ) < 0) { g_warning("Failed to redirect IO. Logging impaired\n"); return; } clean_old_logs(); // Open activity log. config = ghb_get_user_config_dir(NULL); pid = getpid(); path = g_strdup_printf("%s/Activity.log.%d", config, pid); ud->activity_log = g_io_channel_new_file (path, "w", NULL); ud->job_activity_log = NULL; str = g_strdup_printf("%s", path); ghb_ui_update(ud, "activity_location", ghb_string_value(str)); g_free(str); g_free(path); g_free(config); // Set encoding to raw. g_io_channel_set_encoding(ud->activity_log, NULL, NULL); // redirect stderr to the writer end of the pipe #if defined(_WIN32) // dup2 doesn't work on windows for some stupid reason stderr->_file = pfd[1]; #else dup2(pfd[1], /*stderr*/2); #endif setvbuf(stderr, NULL, _IONBF, 0); channel = g_io_channel_unix_new(pfd[0]); // I was getting an this error: // "Invalid byte sequence in conversion input" // Set disable encoding on the channel. g_io_channel_set_encoding(channel, NULL, NULL); g_io_add_watch(channel, G_IO_IN, ghb_log_cb, (gpointer)ud ); } typedef struct { gchar *filename; gchar *iconname; } icon_map_t; static gchar *dvd_device = NULL; static gchar *arg_preset = NULL; static gboolean ghb_debug = FALSE; static GOptionEntry entries[] = { { "device", 'd', 0, G_OPTION_ARG_FILENAME, &dvd_device, N_("The device or file to encode"), NULL }, { "preset", 'p', 0, G_OPTION_ARG_STRING, &arg_preset, N_("The preset values to use for encoding"), NULL }, { "debug", 'x', 0, G_OPTION_ARG_NONE, &ghb_debug, N_("Spam a lot"), NULL }, { NULL } }; G_MODULE_EXPORT void drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud); #if defined(_WIN32) G_MODULE_EXPORT GdkFilterReturn win_message_cb(GdkXEvent *wmevent, GdkEvent *event, gpointer data) { signal_user_data_t *ud = (signal_user_data_t*)data; MSG *msg = (MSG*)wmevent; if (msg->message == WM_DEVICECHANGE) { wm_drive_changed(wmevent, ud); } return GDK_FILTER_CONTINUE; } #endif void watch_volumes(signal_user_data_t *ud) { #if !defined(_WIN32) GVolumeMonitor *gvm; gvm = g_volume_monitor_get(); g_signal_connect(gvm, "drive-changed", (GCallback)drive_changed_cb, ud); #else GdkWindow *window; GtkWidget *widget; widget = GHB_WIDGET(ud->builder, "hb_window"); window = gtk_widget_get_parent_window(widget); gdk_window_add_filter(window, win_message_cb, ud); #endif } G_MODULE_EXPORT void x264_entry_changed_cb(GtkWidget *widget, signal_user_data_t *ud); G_MODULE_EXPORT void video_option_changed_cb(GtkWidget *widget, signal_user_data_t *ud); G_MODULE_EXPORT void position_overlay_cb(GtkWidget *widget, signal_user_data_t *ud); G_MODULE_EXPORT void preview_hud_size_alloc_cb(GtkWidget *widget, signal_user_data_t *ud); const gchar *MyCSS = " \n\ GtkRadioButton .button { \n\ border-width: 0px; \n\ padding: 0px; \n\ } \n\ GtkComboBox { \n\ padding: 0px; \n\ } \n\ GtkComboBox .button { \n\ padding: 2px; \n\ border-width: 0px; \n\ } \n\ GtkEntry { \n\ padding: 4px 4px; \n\ } \n\ \n\ @define-color black #000000; \n\ @define-color gray18 #2e2e2e; \n\ @define-color gray22 #383838; \n\ @define-color gray26 #424242; \n\ @define-color gray32 #525252; \n\ @define-color gray40 #666666; \n\ @define-color gray46 #757575; \n\ @define-color white #ffffff; \n\ \n\ #preview_hud, \n\ #live_preview_play, \n\ #live_duration, \n\ #preview_fullscreen \n\ { \n\ background: @black; \n\ background-color: @gray18; \n\ color: @white; \n\ } \n\ \n\ #preview_show_crop \n\ { \n\ background-color: @gray22; \n\ border-color: @white; \n\ color: @white; \n\ } \n\ \n\ #live_encode_progress, \n\ #live_preview_progress, \n\ #preview_frame \n\ { \n\ background: @black; \n\ background-color: @gray46; \n\ color: @white; \n\ } \n\ \n\ #preview_fullscreen:prelight \n\ { \n\ background: @black; \n\ background-color: @gray32; \n\ color: @white; \n\ } \n\ \n\ #preview_fullscreen:active \n\ { \n\ background: @black; \n\ background-color: @gray32; \n\ color: @white; \n\ } \n\ \n\ #preview_fullscreen:active \n\ { \n\ background: @black; \n\ background-color: @gray32; \n\ color: @white; \n\ } \n\ "; extern G_MODULE_EXPORT void status_icon_query_tooltip_cb(void); int main(int argc, char *argv[]) { signal_user_data_t *ud; GValue *preset; GError *error = NULL; GOptionContext *context; hb_global_init(); #ifdef ENABLE_NLS bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain(GETTEXT_PACKAGE); #endif context = g_option_context_new(_("- Transcode media formats")); g_option_context_add_main_entries(context, entries, GETTEXT_PACKAGE); g_option_context_add_group(context, gtk_get_option_group(TRUE)); #if defined(_ENABLE_GST) g_option_context_add_group(context, gst_init_get_option_group()); #endif g_option_context_parse(context, &argc, &argv, &error); if (error != NULL) { g_warning("%s: %s", G_STRFUNC, error->message); g_clear_error(&error); } g_option_context_free(context); if (argc > 1 && dvd_device == NULL && argv[1][0] != '-') { dvd_device = argv[1]; } gtk_init(&argc, &argv); GtkCssProvider *css = gtk_css_provider_new(); error = NULL; gtk_css_provider_load_from_data(css, MyCSS, -1, &error); if (error == NULL) { GdkScreen *ss = gdk_screen_get_default(); gtk_style_context_add_provider_for_screen(ss, GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); } else { g_warning("%s: %s", G_STRFUNC, error->message); g_clear_error(&error); } #if !defined(_WIN32) notify_init("HandBrake"); #endif ghb_register_transforms(); ghb_resource_init(); ghb_load_icons(); #if !defined(_WIN32) dbus_g_thread_init(); #endif ghb_udev_init(); ghb_write_pid_file(); ud = g_malloc0(sizeof(signal_user_data_t)); ud->debug = ghb_debug; g_log_set_handler(NULL, G_LOG_LEVEL_DEBUG, debug_log_handler, ud); g_log_set_handler("Gtk", G_LOG_LEVEL_WARNING, warn_log_handler, ud); //g_log_set_handler("Gtk", G_LOG_LEVEL_CRITICAL, warn_log_handler, ud); ud->globals = ghb_settings_new(); ud->prefs = ghb_settings_new(); ud->settings_array = ghb_array_value_new(1); ud->settings = ghb_settings_new(); ghb_array_append(ud->settings_array, ud->settings); ud->builder = create_builder_or_die(BUILDER_NAME); // Enable events that alert us to media change events watch_volumes(ud); //GtkWidget *widget = GHB_WIDGET(ud->builder, "PictureDetelecineCustom"); //gtk_entry_set_inner_border(widget, 2); // Since GtkBuilder no longer assigns object ids to widget names // Assign a few that are necessary for style overrides to work GtkWidget *widget; #if defined(_NO_UPDATE_CHECK) widget = GHB_WIDGET(ud->builder, "check_updates_box"); gtk_widget_hide(widget); #endif // Must set the names of the widgets that I want to modify // style for. gtk_widget_set_name(GHB_WIDGET(ud->builder, "preview_hud"), "preview_hud"); gtk_widget_set_name(GHB_WIDGET(ud->builder, "preview_frame"), "preview_frame"); gtk_widget_set_name(GHB_WIDGET(ud->builder, "live_preview_play"), "live_preview_play"); gtk_widget_set_name(GHB_WIDGET(ud->builder, "live_preview_progress"), "live_preview_progress"); gtk_widget_set_name(GHB_WIDGET(ud->builder, "live_encode_progress"), "live_encode_progress"); gtk_widget_set_name(GHB_WIDGET(ud->builder, "live_duration"), "live_duration"); gtk_widget_set_name(GHB_WIDGET(ud->builder, "preview_show_crop"), "preview_show_crop"); gtk_widget_set_name(GHB_WIDGET(ud->builder, "preview_fullscreen"), "preview_fullscreen"); widget = GHB_WIDGET(ud->builder, "preview_hud"); gtk_widget_set_name(widget, "preview_hud"); widget = GHB_WIDGET(ud->builder, "preview_window"); gtk_widget_set_name(widget, "preview_window"); // Set up the "hud" control overlay for the preview window GtkWidget *preview_box, *draw, *hud, *blender; preview_box = GHB_WIDGET(ud->builder, "preview_window_box"); draw = GHB_WIDGET(ud->builder, "preview_image"); hud = GHB_WIDGET(ud->builder, "preview_hud"); #if 0 // GTK_CHECK_VERSION(3, 0, 0) // This uses the new GtkOverlay widget. // // Unfortunately, GtkOverlay is broken in a couple of ways. // // First, it doesn't respect gtk_widget_shape_combine_region() // on it's child overlays. It appears to just ignore the clip // mask of the child. // // Second, it doesn't respect window opacity. // // So for now, I'll just continue using my home-grown overlay // widget (GhbCompositor). blender = gtk_overlay_new(); gtk_container_add(GTK_CONTAINER(preview_box), blender); gtk_container_add(GTK_CONTAINER(blender), draw); gtk_widget_set_valign (hud, GTK_ALIGN_END); gtk_widget_set_halign (hud, GTK_ALIGN_CENTER); gtk_overlay_add_overlay(GTK_OVERLAY(blender), hud); g_signal_connect(G_OBJECT(blender), "get-child-position", G_CALLBACK(position_overlay_cb), ud); gtk_widget_show(blender); #else // Set up compositing for hud blender = ghb_compositor_new(); gtk_container_add(GTK_CONTAINER(preview_box), blender); ghb_compositor_zlist_insert(GHB_COMPOSITOR(blender), draw, 1, 1); ghb_compositor_zlist_insert(GHB_COMPOSITOR(blender), hud, 2, .85); gtk_widget_show(blender); #endif // Redirect stderr to the activity window ghb_preview_init(ud); IoRedirect(ud); ghb_log( "%s - %s - %s", HB_PROJECT_TITLE, HB_PROJECT_BUILD_TITLE, HB_PROJECT_URL_WEBSITE ); ghb_init_dep_map(); // Need to connect x264_options textview buffer to the changed signal // since it can't be done automatically GtkTextView *textview; GtkTextBuffer *buffer; textview = GTK_TEXT_VIEW(GHB_WIDGET(ud->builder, "x264Option")); buffer = gtk_text_view_get_buffer(textview); g_signal_connect(buffer, "changed", (GCallback)x264_entry_changed_cb, ud); textview = GTK_TEXT_VIEW(GHB_WIDGET(ud->builder, "VideoOptionExtra")); buffer = gtk_text_view_get_buffer(textview); g_signal_connect(buffer, "changed", (GCallback)video_option_changed_cb, ud); ghb_combo_init(ud); g_debug("ud %p\n", ud); g_debug("ud->builder %p\n", ud->builder); bind_audio_tree_model(ud); bind_subtitle_tree_model(ud); bind_presets_tree_model(ud); bind_queue_tree_model(ud); bind_chapter_tree_model(ud); // Connect up the signals to their callbacks // I wrote my own connector so that I could pass user data // to the callbacks. Builder's standard autoconnect doesn't all this. gtk_builder_connect_signals_full(ud->builder, MyConnect, ud); ghb_init_audio_defaults_ui(ud); ghb_init_subtitle_defaults_ui(ud); // Parsing x264 options "" initializes x264 widgets to proper defaults ghb_x264_init(ud); // Load prefs before presets. Some preset defaults may depend // on preference settings. // First load default values ghb_settings_init(ud->prefs, "Preferences"); ghb_settings_init(ud->globals, "Globals"); ghb_settings_init(ud->settings, "Initialization"); // Load user preferences file ghb_prefs_load(ud); // Store user preferences into ud->prefs ghb_prefs_to_settings(ud->prefs); // Load all settings with default preset values ghb_settings_init(ud->settings, "Presets"); // Load the presets files ghb_presets_load(ud); // Note that ghb_preset_to_settings(ud->settings) is called when // the default preset is selected. ghb_settings_to_ui(ud, ud->globals); ghb_settings_to_ui(ud, ud->prefs); // Note that ghb_settings_to_ui(ud->settings) happens when initial // empty title is initialized. gint logLevel; logLevel = ghb_settings_get_int(ud->prefs, "LoggingLevel"); ghb_backend_init(logLevel); if (ghb_settings_get_boolean(ud->prefs, "hbfd")) { ghb_hbfd(ud, TRUE); } gchar *source = ghb_settings_get_string(ud->prefs, "default_source"); ghb_dvd_set_current(source, ud); g_free(source); // Populate the presets tree view ghb_presets_list_init(ud, NULL, 0); // Get the first preset name if (arg_preset != NULL) { preset = ghb_parse_preset_path(arg_preset); if (preset) { ghb_select_preset(ud->builder, preset); ghb_value_free(preset); } } else { ghb_select_default_preset(ud->builder); } // Grey out widgets that are dependent on a disabled feature ghb_check_all_depencencies(ud); if (dvd_device != NULL) { // Source overridden from command line option ghb_settings_set_string(ud->globals, "scan_source", dvd_device); g_idle_add((GSourceFunc)ghb_idle_scan, ud); } else { GValue *gval = ghb_settings_get_value(ud->prefs, "default_source"); ghb_settings_set_value(ud->globals, "scan_source", gval); } // Reload and check status of the last saved queue g_idle_add((GSourceFunc)ghb_reload_queue, ud); // Start timer for monitoring libhb status, 500ms g_timeout_add(200, ghb_timer_cb, (gpointer)ud); // Add dvd devices to File menu ghb_volname_cache_init(); GHB_THREAD_NEW("Cache Volume Names", (GThreadFunc)ghb_cache_volnames, ud); GtkWidget *ghb_window = GHB_WIDGET(ud->builder, "hb_window"); gint window_width, window_height; GdkGeometry geo = { -1, -1, 1920, 768, -1, -1, 10, 10, 0, 0, GDK_GRAVITY_NORTH_WEST }; GdkWindowHints geo_mask; geo_mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_BASE_SIZE; gtk_window_set_geometry_hints(GTK_WINDOW(ghb_window), ghb_window, &geo, geo_mask); window_width = ghb_settings_get_int(ud->prefs, "window_width"); window_height = ghb_settings_get_int(ud->prefs, "window_height"); /* * Filter objects in GtkBuilder xml * Unfortunately, GtkFilter is poorly supported by GtkBuilder, * so a lot of the setup must happen in code. SourceFilterAll SourceFilterVideo SourceFilterTS SourceFilterMPG SourceFilterEVO SourceFilterVOB SourceFilterMKV SourceFilterMP4 SourceFilterAVI SourceFilterMOV SourceFilterOGG SourceFilterFLV SourceFilterWMV */ // Add filters to source chooser GtkFileFilter *filter; GtkFileChooser *chooser; chooser = GTK_FILE_CHOOSER(GHB_WIDGET(ud->builder, "source_dialog")); filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterAll")); gtk_file_filter_set_name(filter, _("All")); gtk_file_filter_add_pattern(filter, "*"); gtk_file_chooser_add_filter(chooser, filter); filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterVideo")); gtk_file_filter_set_name(filter, _("Video")); gtk_file_filter_add_mime_type(filter, "video/*"); gtk_file_chooser_add_filter(chooser, filter); filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterTS")); gtk_file_filter_set_name(filter, "TS"); gtk_file_filter_add_pattern(filter, "*.ts"); gtk_file_filter_add_pattern(filter, "*.TS"); gtk_file_filter_add_pattern(filter, "*.m2ts"); gtk_file_filter_add_pattern(filter, "*.M2TS"); gtk_file_chooser_add_filter(chooser, filter); filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterMPG")); gtk_file_filter_set_name(filter, "MPG"); gtk_file_filter_add_pattern(filter, "*.mpg"); gtk_file_filter_add_pattern(filter, "*.MPG"); gtk_file_filter_add_pattern(filter, "*.mepg"); gtk_file_filter_add_pattern(filter, "*.MEPG"); gtk_file_chooser_add_filter(chooser, filter); filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterEVO")); gtk_file_filter_set_name(filter, "EVO"); gtk_file_filter_add_pattern(filter, "*.evo"); gtk_file_filter_add_pattern(filter, "*.EVO"); gtk_file_chooser_add_filter(chooser, filter); filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterVOB")); gtk_file_filter_set_name(filter, "VOB"); gtk_file_filter_add_pattern(filter, "*.vob"); gtk_file_filter_add_pattern(filter, "*.VOB"); gtk_file_chooser_add_filter(chooser, filter); filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterMKV")); gtk_file_filter_set_name(filter, "MKV"); gtk_file_filter_add_pattern(filter, "*.mkv"); gtk_file_filter_add_pattern(filter, "*.MKV"); gtk_file_chooser_add_filter(chooser, filter); filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterMP4")); gtk_file_filter_set_name(filter, "MP4"); gtk_file_filter_add_pattern(filter, "*.mp4"); gtk_file_filter_add_pattern(filter, "*.MP4"); gtk_file_filter_add_pattern(filter, "*.m4v"); gtk_file_filter_add_pattern(filter, "*.M4V"); gtk_file_chooser_add_filter(chooser, filter); filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterMOV")); gtk_file_filter_set_name(filter, "MOV"); gtk_file_filter_add_pattern(filter, "*.mov"); gtk_file_filter_add_pattern(filter, "*.MOV"); gtk_file_chooser_add_filter(chooser, filter); filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterAVI")); gtk_file_filter_set_name(filter, "AVI"); gtk_file_filter_add_pattern(filter, "*.avi"); gtk_file_filter_add_pattern(filter, "*.AVI"); gtk_file_chooser_add_filter(chooser, filter); filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterOGG")); gtk_file_filter_set_name(filter, "OGG"); gtk_file_filter_add_pattern(filter, "*.ogg"); gtk_file_filter_add_pattern(filter, "*.OGG"); gtk_file_filter_add_pattern(filter, "*.ogv"); gtk_file_filter_add_pattern(filter, "*.OGV"); gtk_file_filter_add_pattern(filter, "*.ogm"); gtk_file_filter_add_pattern(filter, "*.OGM"); gtk_file_chooser_add_filter(chooser, filter); filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterFLV")); gtk_file_filter_set_name(filter, "FLV"); gtk_file_filter_add_pattern(filter, "*.flv"); gtk_file_filter_add_pattern(filter, "*.FLV"); gtk_file_chooser_add_filter(chooser, filter); filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterWMV")); gtk_file_filter_set_name(filter, "WMV"); gtk_file_filter_add_pattern(filter, "*.wmv"); gtk_file_filter_add_pattern(filter, "*.WMV"); gtk_file_chooser_add_filter(chooser, filter); // Gtk has a really stupid bug. If the file chooser is showing // hidden files AND there is no filter set, it will not select // the filename when gtk_file_chooser_set_filename is called. // So add a completely unnessary filter to prevent this behavior. filter = GTK_FILE_FILTER(GHB_OBJECT(ud->builder, "SourceFilterAll")); gtk_file_chooser_set_filter(chooser, filter); PangoFontDescription *font_desc; font_desc = pango_font_description_from_string("monospace 10"); textview = GTK_TEXT_VIEW(GHB_WIDGET(ud->builder, "activity_view")); gtk_widget_override_font(GTK_WIDGET(textview), font_desc); pango_font_description_free(font_desc); // Grrrr! Gtk developers !!!hard coded!!! the width of the // radio buttons in GtkStackSwitcher to 100!!! // // Thankfully, GtkStackSwitcher is a regular container object // and we can access the buttons to change their width. GList *stack_switcher_children, *link; GtkContainer * stack_switcher = GTK_CONTAINER( GHB_WIDGET(ud->builder, "SettingsStackSwitcher")); link = stack_switcher_children = gtk_container_get_children(stack_switcher); while (link != NULL) { GtkWidget *widget = link->data; gtk_widget_set_size_request(widget, -1, -1); gtk_widget_set_hexpand(widget, TRUE); gtk_widget_set_halign(widget, GTK_ALIGN_FILL); link = link->next; } g_list_free(stack_switcher_children); gtk_window_resize(GTK_WINDOW(ghb_window), window_width, window_height); gtk_widget_show(ghb_window); // Everything should be go-to-go. Lets rock! gtk_main(); ghb_backend_close(); ghb_value_free(ud->queue); ghb_value_free(ud->settings_array); ghb_value_free(ud->prefs); ghb_value_free(ud->globals); ghb_value_free(ud->x264_priv); g_io_channel_unref(ud->activity_log); ghb_settings_close(); ghb_resource_free(); #if !defined(_WIN32) notify_uninit(); #endif g_object_unref(ud->builder); g_free(ud->current_dvd_device); g_free(ud); return 0; } HandBrake-0.10.2/gtk/src/plist.h0000664000175200017520000000053411061601524016715 0ustar handbrakehandbrake#if !defined(_PLIST_H_) #define _PLIST_H_ #include #include #include GValue* ghb_plist_parse(const gchar *buf, gssize len); GValue* ghb_plist_parse_file(const gchar *filename); void ghb_plist_write(FILE *file, GValue *gval); void ghb_plist_write_file(const gchar *filename, GValue *gval); #endif // _PLIST_H_ HandBrake-0.10.2/gtk/src/resources.h0000664000175200017520000000161512301254347017602 0ustar handbrakehandbrake/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA */ #if !defined(_RESOURCES_H_) #define _RESOURCES_H_ void ghb_resource_init(void); void ghb_resource_free(); GValue* ghb_resource_get(const gchar *name); #endif // _RESOURCES_H_ HandBrake-0.10.2/gtk/src/audiohandler.h0000664000175200017520000000353112465416500020230 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * audiohandler.h * Copyright (C) John Stebbins 2008-2015 * * audiohandler.h is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * callbacks.h is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with callbacks.h. If not, write to: * The Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301, USA. */ #if !defined(_AUDIOHANDLER_H_) #define _AUDIOHANDLER_H_ #include "settings.h" void ghb_adjust_audio_rate_combos(signal_user_data_t *ud); void ghb_set_pref_audio_settings(GValue *settings); const gchar* ghb_get_user_audio_lang(GValue *settings, const hb_title_t *title, gint track); void ghb_audio_list_refresh_selected(signal_user_data_t *ud); gint ghb_select_audio_codec(gint mux, hb_audio_config_t *aconfig, gint acodec, gint fallback_acodec, gint copy_mask); int ghb_select_fallback( GValue *settings, int acodec ); int ghb_get_copy_mask(GValue *settings); void ghb_audio_list_refresh_all(signal_user_data_t *ud); char * ghb_format_quality( const char *prefix, int codec, double quality ); void ghb_init_audio_defaults_ui(signal_user_data_t *ud); void ghb_audio_defaults_to_ui(signal_user_data_t *ud); GtkListBoxRow* ghb_find_lang_row(GtkListBox *list_box, int lang_idx); void ghb_audio_title_change(signal_user_data_t *ud, gboolean title_valid); #endif // _AUDIOHANDLER_H_ HandBrake-0.10.2/gtk/src/presets.h0000664000175200017520000000470112301254347017254 0ustar handbrakehandbrake/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA */ #if !defined(_GHB_PRESETS_H_) #define _GHB_PRESETS_H_ void ghb_settings_save(signal_user_data_t *ud, const gchar *name); void ghb_presets_load(signal_user_data_t *ud); void ghb_update_from_preset(signal_user_data_t *ud, const gchar *key); void ghb_settings_init(GValue *settings, const char *name); void ghb_settings_close(); void ghb_globals_to_ui(signal_user_data_t *ud); void ghb_prefs_load(signal_user_data_t *ud); void ghb_prefs_to_ui(signal_user_data_t *ud); void ghb_prefs_save(GValue *settings); void ghb_pref_save(GValue *settings, const gchar *key); void ghb_pref_set(GValue *settings, const gchar *key); void ghb_prefs_store(void); void ghb_save_queue(GValue *queue); GValue* ghb_load_queue(); GValue* ghb_load_old_queue(int pid); void ghb_remove_queue_file(void); void ghb_remove_old_queue_file(int pid); gchar* ghb_get_user_config_dir(gchar *subdir); void ghb_settings_to_ui(signal_user_data_t *ud, GValue *dict); void ghb_clear_presets_selection(signal_user_data_t *ud); void ghb_select_preset(GtkBuilder *builder, const GValue *preset); void ghb_select_default_preset(GtkBuilder *builder); void ghb_presets_list_init(signal_user_data_t *ud, gint *indices, gint len); GValue* ghb_parse_preset_path(const gchar *path); gchar* ghb_preset_path_string(const GValue *path); gboolean ghb_preset_is_custom(void); gboolean ghb_lock_file(const gchar *name); int ghb_find_pid_file(); void ghb_unlink_pid_file(int pid); void ghb_write_pid_file(); GValue* ghb_get_current_preset(signal_user_data_t *ud); GValue* ghb_get_current_preset_path(signal_user_data_t *ud); void ghb_preset_to_settings(GValue *settings, GValue *preset); void ghb_prefs_to_settings(GValue *settings); void dump_preset_path(const gchar *msg, const GValue *path); #endif // _GHB_PRESETS_H_ HandBrake-0.10.2/gtk/src/hb-complete.svg0000664000175200017520000000740412312123633020334 0ustar handbrakehandbrake image/svg+xml HandBrake-0.10.2/gtk/src/composite_example.c0000664000175200017520000000474711167157062021316 0ustar handbrakehandbrake#include #include "ghbcompositor.h" // GhbCompositor example int main(gint argc, gchar *argv[]) { GtkWidget *window; GtkWidget *blender; GtkWidget *eb_bottom; GtkWidget *eb_top1; GtkWidget *eb_top2; GtkWidget *eb_top3; GtkWidget *bottom; GtkWidget *top1; GtkWidget *top2; GtkWidget *top3; GtkWidget *table; GtkWidget *image; gtk_init(&argc, &argv); // Make the top level window window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // Only widgets that have a GdkDrawing area can be composited // These would be GtkEventBox or GtkDrawingArea // Create the widgets that will be composited eb_bottom = gtk_event_box_new(); eb_top1 = gtk_event_box_new(); eb_top2 = gtk_event_box_new(); eb_top3 = gtk_event_box_new(); // Create the compositor blender = ghb_compositor_new(); // Create an button to put on the bottom layer bottom = gtk_button_new_with_label("Bottom"); image = gtk_image_new_from_stock("gtk-help", 6); gtk_button_set_image(GTK_BUTTON(bottom), image); // The button must be placed inside an event box since // buttons do not have their own window. gtk_container_add(GTK_CONTAINER(eb_bottom), bottom); // Create the buttons that will be visible on the top layer top1 = gtk_button_new_with_label("Top 1"); top2 = gtk_button_new_with_label("Top 2"); top3 = gtk_button_new_with_label("Top 3"); // The buttons must be placed inside an event box since // buttons do not have their own window. gtk_container_add(GTK_CONTAINER(eb_top1), top1); gtk_container_add(GTK_CONTAINER(eb_top2), top2); gtk_container_add(GTK_CONTAINER(eb_top3), top3); // Create the table that will be the top layer // Using a layout widget gives flexibility in the layout of the layer table = gtk_table_new(3, 3, TRUE); gtk_table_attach(GTK_TABLE(table), eb_top1, 0, 1, 0, 1, 0, 0, 0, 0); gtk_table_attach(GTK_TABLE(table), eb_top2, 1, 2, 1, 2, 0, 0, 0, 0); gtk_table_attach(GTK_TABLE(table), eb_top3, 2, 3, 2, 3, 0, 0, 0, 0); // Add the blender to the main window. gtk_container_add(GTK_CONTAINER(window), blender); // Set the blenders zlist, with opacity values // Bottom layer is opaque, top layer 60% ghb_compositor_zlist_insert(GHB_COMPOSITOR(blender), eb_bottom, 1, 1); ghb_compositor_zlist_insert(GHB_COMPOSITOR(blender), table, 2, 0.6); // Start the show gtk_widget_show_all(window); gtk_main(); return 0; } HandBrake-0.10.2/gtk/src/settings.c0000664000175200017520000004502012505047552017425 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * settings.c * Copyright (C) John Stebbins 2008-2015 * * settings.c is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * */ #include #include #include #include #include #include "ghbcompat.h" #include "settings.h" #include "hb-backend.h" #include "values.h" void dump_settings(GValue *settings); void ghb_pref_audio_init(signal_user_data_t *ud); GObject* debug_get_object(GtkBuilder* b, const gchar *n) { g_message("name %s\n", n); return gtk_builder_get_object(b, n); } GValue* ghb_settings_new() { return ghb_dict_value_new(); } void ghb_settings_set_value( GValue *settings, const gchar *key, const GValue *value) { if (key == NULL || value == NULL) return; ghb_dict_insert(settings, g_strdup(key), ghb_value_dup(value)); } void ghb_settings_take_value(GValue *settings, const gchar *key, GValue *value) { ghb_dict_insert(settings, g_strdup(key), value); } void ghb_settings_set_string( GValue *settings, const gchar *key, const gchar *sval) { GValue *value; value = ghb_string_value_new(sval); ghb_dict_insert(settings, g_strdup(key), value); } void ghb_settings_set_double(GValue *settings, const gchar *key, gdouble dval) { GValue *value; value = ghb_double_value_new(dval); ghb_dict_insert(settings, g_strdup(key), value); } void ghb_settings_set_int64(GValue *settings, const gchar *key, gint64 ival) { GValue *value; value = ghb_int64_value_new(ival); ghb_dict_insert(settings, g_strdup(key), value); } void ghb_settings_set_int(GValue *settings, const gchar *key, gint ival) { GValue *value; value = ghb_int64_value_new((gint64)ival); ghb_dict_insert(settings, g_strdup(key), value); } void ghb_settings_set_boolean(GValue *settings, const gchar *key, gboolean bval) { GValue *value; value = ghb_boolean_value_new(bval); ghb_dict_insert(settings, g_strdup(key), value); } GValue* ghb_settings_get_value(const GValue *settings, const gchar *key) { GValue *value; value = ghb_dict_lookup(settings, key); if (value == NULL) g_warning("returning null (%s)", key); return value; } gboolean ghb_settings_get_boolean(const GValue *settings, const gchar *key) { const GValue* value; value = ghb_settings_get_value(settings, key); if (value == NULL) return FALSE; return ghb_value_boolean(value); } gint64 ghb_settings_get_int64(const GValue *settings, const gchar *key) { const GValue* value; value = ghb_settings_get_value(settings, key); if (value == NULL) return 0; return ghb_value_int64(value); } gint ghb_settings_get_int(const GValue *settings, const gchar *key) { const GValue* value; value = ghb_settings_get_value(settings, key); if (value == NULL) return 0; return ghb_value_int(value); } gdouble ghb_settings_get_double(const GValue *settings, const gchar *key) { const GValue* value; value = ghb_settings_get_value(settings, key); if (value == NULL) return 0; return ghb_value_double(value); } const gchar* ghb_settings_get_const_string(const GValue *settings, const gchar *key) { const GValue* value; value = ghb_settings_get_value(settings, key); return g_value_get_string(value); } gchar* ghb_settings_get_string(const GValue *settings, const gchar *key) { const GValue* value; value = ghb_settings_get_value(settings, key); if (value == NULL) return g_strdup(""); return ghb_value_string(value); } gint ghb_settings_combo_int(const GValue *settings, const gchar *key) { return ghb_lookup_combo_int(key, ghb_settings_get_value(settings, key)); } gdouble ghb_settings_combo_double(const GValue *settings, const gchar *key) { return ghb_lookup_combo_double(key, ghb_settings_get_value(settings, key)); } const gchar* ghb_settings_combo_option(const GValue *settings, const gchar *key) { return ghb_lookup_combo_option(key, ghb_settings_get_value(settings, key)); } const gchar* ghb_settings_combo_string(const GValue *settings, const gchar *key) { return ghb_lookup_combo_string(key, ghb_settings_get_value(settings, key)); } // Map widget names to setting keys // Widgets that map to settings have names // of this format: s_ const gchar* ghb_get_setting_key(GtkWidget *widget) { const gchar *name; g_debug("get_setting_key ()\n"); if (widget == NULL) return NULL; name = gtk_buildable_get_name(GTK_BUILDABLE(widget)); if (name == NULL || !strncmp(name, "Gtk", 3)) { name = gtk_widget_get_name(widget); } if (name == NULL) { // Bad widget pointer? Should never happen. g_debug("Bad widget\n"); return NULL; } return name; } GValue* ghb_widget_value(GtkWidget *widget) { GValue *value = NULL; const gchar *name; GType type; if (widget == NULL) { g_debug("NULL widget\n"); return NULL; } type = G_OBJECT_TYPE(widget); name = ghb_get_setting_key(widget); g_debug("ghb_widget_value widget (%s)\n", name); if (type == GTK_TYPE_ENTRY) { const gchar *str = gtk_entry_get_text(GTK_ENTRY(widget)); value = ghb_string_value_new(str); } else if (type == GTK_TYPE_RADIO_BUTTON) { g_debug("\tradio_button"); gboolean bval; bval = gtk_toggle_button_get_inconsistent(GTK_TOGGLE_BUTTON(widget)); if (bval) { value = ghb_boolean_value_new(FALSE); } else { bval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); value = ghb_boolean_value_new(bval); } } else if (type == GTK_TYPE_CHECK_BUTTON) { g_debug("\tcheck_button"); gboolean bval; bval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); value = ghb_boolean_value_new(bval); } else if (type == GTK_TYPE_TOGGLE_TOOL_BUTTON) { g_debug("\ttoggle_tool_button"); gboolean bval; bval = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(widget)); value = ghb_boolean_value_new(bval); } else if (type == GTK_TYPE_TOGGLE_BUTTON) { g_debug("\ttoggle_button"); gboolean bval; bval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); value = ghb_boolean_value_new(bval); } else if (type == GTK_TYPE_CHECK_MENU_ITEM) { g_debug("\tcheck_menu_item"); gboolean bval; bval = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)); value = ghb_boolean_value_new(bval); } else if (type == GTK_TYPE_COMBO_BOX) { g_debug("\tcombo_box"); GtkTreeModel *store; GtkTreeIter iter; gchar *shortOpt; store = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter)) { gtk_tree_model_get(store, &iter, 2, &shortOpt, -1); value = ghb_string_value_new(shortOpt); g_free(shortOpt); } else if (gtk_combo_box_get_has_entry(GTK_COMBO_BOX(widget))) { const gchar *str; str = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(widget)))); if (str == NULL) str = ""; value = ghb_string_value_new(str); } else { value = ghb_string_value_new(""); } } else if (type == GTK_TYPE_SPIN_BUTTON) { double val; val = gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget)); value = ghb_double_value_new(val); } else if (type == GTK_TYPE_SCALE) { gdouble dval; gint digits; digits = gtk_scale_get_digits(GTK_SCALE(widget)); dval = gtk_range_get_value(GTK_RANGE(widget)); if (digits) { value = ghb_double_value_new(dval); } else { value = ghb_int_value_new(dval); } } else if (type == GTK_TYPE_SCALE_BUTTON) { gdouble dval; dval = gtk_scale_button_get_value(GTK_SCALE_BUTTON(widget)); value = ghb_double_value_new(dval); } else if (type == GTK_TYPE_TEXT_VIEW) { GtkTextBuffer *buffer; GtkTextIter start, end; gchar *str; buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); gtk_text_buffer_get_bounds(buffer, &start, &end); str = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); value = ghb_string_value_new(str); g_free(str); } else if (type == GTK_TYPE_LABEL) { const gchar *str; str = gtk_label_get_text (GTK_LABEL(widget)); value = ghb_string_value_new(str); } else if (type == GTK_TYPE_FILE_CHOOSER_BUTTON) { gchar *str = NULL; str = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(widget)); if (str == NULL) { str = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(widget)); } value = ghb_string_value_new(str); if (str != NULL) g_free(str); } else { g_debug("Attempt to set unknown widget type: %s\n", name); g_free(value); value = NULL; } return value; } gchar* ghb_widget_string(GtkWidget *widget) { GValue *value; gchar *sval; value = ghb_widget_value(widget); sval = ghb_value_string(value); ghb_value_free(value); return sval; } gdouble ghb_widget_double(GtkWidget *widget) { GValue *value; gdouble dval; value = ghb_widget_value(widget); dval = ghb_value_double(value); ghb_value_free(value); return dval; } gint64 ghb_widget_int64(GtkWidget *widget) { GValue *value; gint64 ival; value = ghb_widget_value(widget); ival = ghb_value_int64(value); ghb_value_free(value); return ival; } gint ghb_widget_int(GtkWidget *widget) { GValue *value; gint ival; value = ghb_widget_value(widget); ival = (gint)ghb_value_int64(value); ghb_value_free(value); return ival; } gint ghb_widget_boolean(GtkWidget *widget) { GValue *value; gboolean bval; value = ghb_widget_value(widget); bval = ghb_value_boolean(value); ghb_value_free(value); return bval; } void ghb_widget_to_setting(GValue *settings, GtkWidget *widget) { const gchar *key = NULL; GValue *value; if (widget == NULL) return; g_debug("ghb_widget_to_setting"); // Find corresponding setting key = ghb_get_setting_key(widget); if (key == NULL) return; value = ghb_widget_value(widget); if (value != NULL) { ghb_settings_take_value(settings, key, value); } else { g_debug("No value found for %s\n", key); } } void ghb_update_widget(GtkWidget *widget, const GValue *value) { GType type; gchar *str; gint ival; gdouble dval; g_debug("ghb_update_widget"); type = G_VALUE_TYPE(value); if (type == ghb_array_get_type() || type == ghb_dict_get_type()) return; if (value == NULL) return; str = ghb_value_string(value); ival = ghb_value_int(value); dval = ghb_value_double(value); type = G_OBJECT_TYPE(widget); if (type == GTK_TYPE_ENTRY) { g_debug("entry"); gtk_entry_set_text((GtkEntry*)widget, str); } else if (type == GTK_TYPE_RADIO_BUTTON) { g_debug("radio button"); if (ival) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), !!ival); } else if (type == GTK_TYPE_CHECK_BUTTON) { g_debug("check button"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), ival); } else if (type == GTK_TYPE_TOGGLE_TOOL_BUTTON) { g_debug("toggle button"); gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), ival); } else if (type == GTK_TYPE_TOGGLE_BUTTON) { g_debug("toggle button"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), ival); } else if (type == GTK_TYPE_CHECK_MENU_ITEM) { g_debug("check menu item"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), ival); } else if (type == GTK_TYPE_COMBO_BOX) { GtkTreeModel *store; GtkTreeIter iter; gchar *shortOpt; gdouble ivalue; gboolean foundit = FALSE; g_debug("combo (%s)", str); store = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); if (gtk_tree_model_get_iter_first (store, &iter)) { do { gtk_tree_model_get(store, &iter, 2, &shortOpt, -1); if (strcasecmp(shortOpt, str) == 0) { gtk_combo_box_set_active_iter ( GTK_COMBO_BOX(widget), &iter); g_free(shortOpt); foundit = TRUE; break; } g_free(shortOpt); } while (gtk_tree_model_iter_next (store, &iter)); } if (!foundit && gtk_tree_model_get_iter_first (store, &iter)) { do { gtk_tree_model_get(store, &iter, 3, &ivalue, -1); if ((gint)ivalue == ival || ivalue == dval) { gtk_combo_box_set_active_iter ( GTK_COMBO_BOX(widget), &iter); foundit = TRUE; break; } } while (gtk_tree_model_iter_next (store, &iter)); } if (!foundit) { if (gtk_combo_box_get_has_entry(GTK_COMBO_BOX(widget))) { GtkEntry *entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(widget))); if (entry) { gtk_entry_set_text (entry, str); } else { gtk_combo_box_set_active (GTK_COMBO_BOX(widget), 0); } } else { gtk_combo_box_set_active (GTK_COMBO_BOX(widget), 0); } } } else if (type == GTK_TYPE_SPIN_BUTTON) { g_debug("spin (%s)", str); gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), dval); } else if (type == GTK_TYPE_SCALE) { g_debug("hscale"); gtk_range_set_value(GTK_RANGE(widget), dval); } else if (type == GTK_TYPE_SCALE_BUTTON) { g_debug("scale_button"); gtk_scale_button_set_value(GTK_SCALE_BUTTON(widget), dval); } else if (type == GTK_TYPE_TEXT_VIEW) { g_debug("textview (%s)", str); static int text_view_busy = 0; GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(widget)); if (!text_view_busy) { text_view_busy = 1; gtk_text_buffer_set_text (buffer, str, -1); text_view_busy = 0; } } else if (type == GTK_TYPE_LABEL) { gtk_label_set_markup (GTK_LABEL(widget), str); } else if (type == GTK_TYPE_FILE_CHOOSER_BUTTON) { GtkFileChooserAction act; act = gtk_file_chooser_get_action(GTK_FILE_CHOOSER(widget)); if (str[0] == 0) { // Do nothing ; } else if (act == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || act == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) { gtk_file_chooser_set_filename (GTK_FILE_CHOOSER(widget), str); } else if (act == GTK_FILE_CHOOSER_ACTION_SAVE) { gtk_file_chooser_set_filename (GTK_FILE_CHOOSER(widget), str); } else { if (g_file_test(str, G_FILE_TEST_IS_DIR)) { gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(widget), str); } else if (g_file_test(str, G_FILE_TEST_EXISTS)) { gtk_file_chooser_set_filename (GTK_FILE_CHOOSER(widget), str); } else { gchar *dirname; dirname = g_path_get_dirname(str); gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(widget), dirname); g_free(dirname); } } } else { g_debug("Attempt to set unknown widget type"); } g_free(str); } int ghb_ui_update_from_settings(signal_user_data_t *ud, const gchar *name, const GValue *settings) { GObject *object; GValue * value; g_debug("ghb_ui_update_from_settings() %s", name); if (name == NULL) return 0; value = ghb_settings_get_value(settings, name); if (value == NULL) return 0; object = GHB_OBJECT(ud->builder, name); if (object == NULL) { g_debug("Failed to find widget for key: %s\n", name); return -1; } ghb_update_widget((GtkWidget*)object, value); // Its possible the value hasn't changed. Since settings are only // updated when the value changes, I'm initializing settings here as well. ghb_widget_to_setting(ud->settings, (GtkWidget*)object); return 0; } int ghb_ui_update(signal_user_data_t *ud, const gchar *name, const GValue *value) { GObject *object; g_debug("ghb_ui_update() %s", name); if (name == NULL || value == NULL) return 0; object = GHB_OBJECT(ud->builder, name); if (object == NULL) { g_debug("Failed to find widget for key: %s\n", name); return -1; } ghb_update_widget((GtkWidget*)object, value); // Its possible the value hasn't changed. Since settings are only // updated when the value changes, I'm initializing settings here as well. ghb_widget_to_setting(ud->settings, (GtkWidget*)object); return 0; } int ghb_ui_settings_update( signal_user_data_t *ud, GValue *settings, const gchar *name, const GValue *value) { GObject *object; g_debug("ghb_ui_update() %s", name); if (name == NULL || value == NULL) return 0; object = GHB_OBJECT(ud->builder, name); if (object == NULL) { g_debug("Failed to find widget for key: %s\n", name); return -1; } ghb_update_widget((GtkWidget*)object, value); // Its possible the value hasn't changed. Since settings are only // updated when the value changes, I'm initializing settings here as well. ghb_widget_to_setting(settings, (GtkWidget*)object); return 0; } HandBrake-0.10.2/gtk/src/makedeps.py0000664000175200017520000001643112402130653017557 0ustar handbrakehandbrake#! /usr/bin/python import collections import plistlib import sys DepEntry = collections.namedtuple('DepEntry', 'widget dep enable die hide') dep_map = ( DepEntry("title", "queue_add", "none", True, False), DepEntry("title", "queue_add_menu", "none", True, False), DepEntry("title", "queue_add_multiple_menu", "none", True, False), DepEntry("title", "preview_frame", "none", True, False), DepEntry("title", "picture_summary", "none", True, False), DepEntry("title", "picture_summary2", "none", True, False), DepEntry("title", "chapters_tab", "none", True, False), DepEntry("title", "start_point", "none", True, False), DepEntry("title", "end_point", "none", True, False), DepEntry("title", "angle", "none", True, False), DepEntry("title", "angle_label", "1", True, False), DepEntry("use_dvdnav", "angle", "FALSE", True, True), DepEntry("use_dvdnav", "angle_label", "FALSE", True, True), DepEntry("angle_count", "angle", "1", True, True), DepEntry("angle_count", "angle_label", "1", True, True), DepEntry("vquality_type_bitrate", "VideoAvgBitrate", "TRUE", False, False), DepEntry("vquality_type_constant", "VideoQualitySlider", "TRUE", False, False), DepEntry("vquality_type_constant", "VideoTwoPass", "TRUE", True, False), DepEntry("vquality_type_constant", "VideoTurboTwoPass", "TRUE", True, False), DepEntry("VideoFramerate", "VideoFrameratePFR", "source", True, True), DepEntry("VideoFramerate", "VideoFramerateVFR", "source", False, True), DepEntry("VideoTwoPass", "VideoTurboTwoPass", "TRUE", False, False), DepEntry("PictureDecombDeinterlace", "PictureDeinterlace", "TRUE", True, True), DepEntry("PictureDecombDeinterlace", "PictureDeinterlaceCustom", "TRUE", True, True), DepEntry("PictureDecombDeinterlace", "PictureDeinterlaceLabel", "TRUE", True, True), DepEntry("PictureDecombDeinterlace", "PictureDecomb", "FALSE", True, True), DepEntry("PictureDecombDeinterlace", "PictureDecombCustom", "FALSE", True, True), DepEntry("PictureDecombDeinterlace", "PictureDecombLabel", "FALSE", True, True), DepEntry("PictureDeinterlace", "PictureDeinterlaceCustom", "custom", False, True), DepEntry("PictureDenoiseFilter", "PictureDenoisePreset", "off", True, True), DepEntry("PictureDenoiseFilter", "PictureDenoisePresetLabel", "off", True, True), DepEntry("PictureDenoiseFilter", "PictureDenoiseTune", "nlmeans", False, True), DepEntry("PictureDenoiseFilter", "PictureDenoiseTuneLabel", "nlmeans", False, True), DepEntry("PictureDenoiseFilter", "PictureDenoiseCustom", "off", True, True), DepEntry("PictureDenoisePreset", "PictureDenoiseCustom", "custom", False, True), DepEntry("PictureDenoisePreset", "PictureDenoiseTune", "custom", True, True), DepEntry("PictureDenoisePreset", "PictureDenoiseTuneLabel", "custom", True, True), DepEntry("PictureDecomb", "PictureDecombCustom", "custom", False, True), DepEntry("PictureDetelecine", "PictureDetelecineCustom", "custom", False, True), DepEntry("PictureWidthEnable", "PictureWidth", "TRUE", False, False), DepEntry("PictureHeightEnable", "PictureHeight", "TRUE", False, False), DepEntry("PictureAutoCrop", "PictureTopCrop", "FALSE", False, False), DepEntry("PictureAutoCrop", "PictureBottomCrop", "FALSE", False, False), DepEntry("PictureAutoCrop", "PictureLeftCrop", "FALSE", False, False), DepEntry("PictureAutoCrop", "PictureRightCrop", "FALSE", False, False), DepEntry("AudioTrackQualityEnable", "AudioBitrate", "TRUE", True, True), DepEntry("AudioTrackQualityEnable", "AudioTrackQualityBox", "FALSE", True, True), DepEntry("AudioEncoder", "AudioBitrateLabel", "copy:mp3|copy:aac|copy:ac3|copy:dts|copy:dtshd", True, False), DepEntry("AudioEncoder", "AudioBitrate", "copy:mp3|copy:aac|copy:ac3|copy:dts|copy:dtshd", True, False), DepEntry("AudioEncoder", "AudioSamplerateLabel", "copy:mp3|copy:aac|copy:ac3|copy:dts|copy:dtshd", True, False), DepEntry("AudioEncoder", "AudioSamplerate", "copy:mp3|copy:aac|copy:ac3|copy:dts|copy:dtshd", True, False), DepEntry("AudioEncoder", "AudioMixdownLabel", "copy:mp3|copy:aac|copy:ac3|copy:dts|copy:dtshd", True, False), DepEntry("AudioEncoder", "AudioMixdown", "copy:mp3|copy:aac|copy:ac3|copy:dts|copy:dtshd", True, False), DepEntry("AudioEncoder", "AudioTrackGainLabel", "copy:mp3|copy:aac|copy:ac3|copy:dts|copy:dtshd", True, False), DepEntry("AudioEncoder", "AudioTrackGainSlider", "copy:mp3|copy:aac|copy:ac3|copy:dts|copy:dtshd", True, False), DepEntry("AudioEncoder", "AudioTrackGainValue", "copy:mp3|copy:aac|copy:ac3|copy:dts|copy:dtshd", True, False), DepEntry("x264_bframes", "x264_bpyramid", "<2", True, False), DepEntry("x264_bframes", "x264_direct", "0", True, False), DepEntry("x264_bframes", "x264_b_adapt", "0", True, False), DepEntry("x264_subme", "x264_psy_rd", "<6", True, False), DepEntry("x264_subme", "x264_psy_trell", "<6", True, False), DepEntry("x264_trellis", "x264_psy_trell", "0", True, False), DepEntry("VideoEncoder", "VideoPresetSlider", "x264|x265", False, True), DepEntry("VideoEncoder", "VideoPresetLabel", "x264|x265", False, True), DepEntry("VideoEncoder", "VideoTune", "x264|x265", False, True), DepEntry("VideoEncoder", "VideoTuneLabel", "x264|x265", False, True), DepEntry("VideoEncoder", "VideoProfile", "x264|x265", False, True), DepEntry("VideoEncoder", "VideoProfileLabel", "x264|x265", False, True), DepEntry("VideoEncoder", "VideoLevel", "x264|x265", False, True), DepEntry("VideoEncoder", "VideoLevelLabel", "x264|x265", False, True), DepEntry("VideoEncoder", "x264FastDecode", "x264", False, True), DepEntry("VideoEncoder", "x264UseAdvancedOptions", "x264", False, True), DepEntry("HideAdvancedVideoSettings", "x264UseAdvancedOptions", "TRUE", True, True), DepEntry("VideoEncoder", "VideoOptionExtraWindow", "x264|x265|mpeg4|mpeg2|VP8", False, True), DepEntry("VideoEncoder", "VideoOptionExtraLabel", "x264|x265|mpeg4|mpeg2|VP8", False, True), DepEntry("x264UseAdvancedOptions", "VideoSettingsTable", "TRUE", True, False), DepEntry("VideoEncoder", "x264_box", "x264", False, True), DepEntry("x264UseAdvancedOptions", "x264_box", "FALSE", True, False), DepEntry("auto_name", "autoname_box", "TRUE", False, False), ) def main(): try: depsfile = open("widget.deps", "w") except Exception, err: print >> sys.stderr, ( "Error: %s" % str(err) ) sys.exit(1) try: revfile = open("widget_reverse.deps", "w") except Exception, err: print >> sys.stderr, ( "Error: %s" % str(err)) sys.exit(1) top = dict() for ii in dep_map: if ii.widget in top: continue deps = list() for jj in dep_map: if jj.widget == ii.widget: deps.append(jj.dep) top[ii.widget] = deps plistlib.writePlist(top, depsfile) top = dict() for ii in dep_map: if ii.dep in top: continue deps = list() for jj in dep_map: if ii.dep == jj.dep: rec = list() rec.append(jj.widget) rec.append(jj.enable) rec.append(jj.die) rec.append(jj.hide) deps.append(rec) top[ii.dep] = deps plistlib.writePlist(top, revfile) main() HandBrake-0.10.2/gtk/src/appcast.h0000664000175200017520000000160512300772602017220 0ustar handbrakehandbrake/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA */ #if !defined(_GHB_APPCAST_H_) #define _GHB_APPCAST_H_ void ghb_appcast_parse( gchar *buf, gchar **desc, gchar **build, gchar **version); #endif // _GHB_APPCAST_H_ HandBrake-0.10.2/gtk/src/plist.c0000664000175200017520000003321412300772602016714 0ustar handbrakehandbrake#include #include #include #include #include #include #include #include "plist.h" #include "values.h" #define BUF_SZ (128*1024) static gchar *preamble = "\n" "\n" "\n"; static gchar *postfix = "\n"; enum { P_NONE = 0, P_PLIST, P_KEY, P_ARRAY, P_DICT, P_INTEGER, P_REAL, P_STRING, P_DATE, P_TRUE, P_FALSE, P_DATA, }; typedef struct { gchar *tag; gint id; } tag_map_t; static tag_map_t tag_map[] = { {"plist", P_PLIST}, {"key", P_KEY}, {"array", P_ARRAY}, {"dict", P_DICT}, {"integer", P_INTEGER}, {"real", P_REAL}, {"string", P_STRING}, {"date", P_DATE}, {"true", P_TRUE}, {"false", P_FALSE}, {"data", P_DATA}, }; #define TAG_MAP_SZ (sizeof(tag_map)/sizeof(tag_map_t)) typedef struct { gchar *key; gchar *value; GValue *plist; GQueue *stack; GQueue *tag_stack; gboolean closed_top; } parse_data_t; static void start_element( GMarkupParseContext *ctx, const gchar *name, const gchar **attr_names, const gchar **attr_values, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; union { gint id; gpointer pid; } id; gint ii; // Check to see if the first element found has been closed // If so, ignore any junk following it. if (pd->closed_top) return; for (ii = 0; ii < TAG_MAP_SZ; ii++) { if (strcmp(name, tag_map[ii].tag) == 0) { id.id = tag_map[ii].id; break; } } if (ii == TAG_MAP_SZ) { g_warning("Unrecognized start tag (%s)", name); return; } g_queue_push_head(pd->tag_stack, id.pid); GType gtype = 0; GValue *gval = NULL; GValue *current = g_queue_peek_head(pd->stack); switch (id.id) { case P_PLIST: { // Ignore } break; case P_KEY: { if (pd->key) g_free(pd->key); pd->key = NULL; } break; case P_DICT: { gval = ghb_dict_value_new(); g_queue_push_head(pd->stack, gval); } break; case P_ARRAY: { gval = ghb_array_value_new(128); g_queue_push_head(pd->stack, gval); } break; case P_INTEGER: { } break; case P_REAL: { } break; case P_STRING: { } break; case P_DATE: { } break; case P_TRUE: { } break; case P_FALSE: { } break; case P_DATA: { } break; } // Add the element to the current container if (gval) { // There's an element to add if (current == NULL) { pd->plist = gval; return; } gtype = G_VALUE_TYPE(current); if (gtype == ghb_array_get_type()) { ghb_array_append(current, gval); } else if (gtype == ghb_dict_get_type()) { if (pd->key == NULL) { g_warning("No key for dictionary item"); ghb_value_free(gval); } else { ghb_dict_insert(current, g_strdup(pd->key), gval); } } else { g_error("Invalid container type. This shouldn't happen"); } } } static void end_element( GMarkupParseContext *ctx, const gchar *name, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; gint id; union { gint id; gpointer pid; } start_id; gint ii; // Check to see if the first element found has been closed // If so, ignore any junk following it. if (pd->closed_top) return; for (ii = 0; ii < TAG_MAP_SZ; ii++) { if (strcmp(name, tag_map[ii].tag) == 0) { id = tag_map[ii].id; break; } } if (ii == TAG_MAP_SZ) { g_warning("Unrecognized start tag (%s)", name); return; } start_id.pid = g_queue_pop_head(pd->tag_stack); if (start_id.id != id) g_warning("start tag != end tag: (%s %d) %d", name, id, id); GValue *gval = NULL; GValue *current = g_queue_peek_head(pd->stack); GType gtype = 0; switch (id) { case P_PLIST: { // Ignore } break; case P_KEY: { if (pd->key) g_free(pd->key); pd->key = g_strdup(pd->value); return; } break; case P_DICT: { g_queue_pop_head(pd->stack); } break; case P_ARRAY: { g_queue_pop_head(pd->stack); } break; case P_INTEGER: { gint64 val = g_strtod(pd->value, NULL); gval = ghb_int64_value_new(val); } break; case P_REAL: { gdouble val = g_strtod(pd->value, NULL); gval = ghb_double_value_new(val); } break; case P_STRING: { gval = ghb_string_value_new(pd->value); } break; case P_DATE: { GDate date; GTimeVal time; g_time_val_from_iso8601(pd->value, &time); g_date_set_time_val(&date, &time); gval = ghb_date_value_new(&date); } break; case P_TRUE: { gval = ghb_boolean_value_new(TRUE); } break; case P_FALSE: { gval = ghb_boolean_value_new(FALSE); } break; case P_DATA: { ghb_rawdata_t *data; data = g_malloc(sizeof(ghb_rawdata_t)); data->data = g_base64_decode(pd->value, &(data->size)); gval = ghb_rawdata_value_new(data); } break; } if (gval) { // Get the top of the data structure stack and if it's an array // or dict, add the current element if (current == NULL) { pd->plist = gval; pd->closed_top = TRUE; return; } gtype = G_VALUE_TYPE(current); if (gtype == ghb_array_get_type()) { ghb_array_append(current, gval); } else if (gtype == ghb_dict_get_type()) { if (pd->key == NULL) { g_warning("No key for dictionary item"); ghb_value_free(gval); } else { ghb_dict_insert(current, g_strdup(pd->key), gval); } } else { g_error("Invalid container type. This shouldn't happen"); } } if (g_queue_is_empty(pd->stack)) pd->closed_top = TRUE; } static void text_data( GMarkupParseContext *ctx, const gchar *text, gsize len, gpointer ud, GError **error) { parse_data_t *pd = (parse_data_t*)ud; if (pd->value) g_free(pd->value); pd->value = g_strdup(text); } static void passthrough( GMarkupParseContext *ctx, const gchar *text, gsize len, gpointer ud, GError **error) { //parse_data_t *pd = (parse_data_t*)ud; //g_debug("passthrough %s", text); } static void parse_error(GMarkupParseContext *ctx, GError *error, gpointer ud) { g_warning("Plist parse error: %s", error->message); } // This is required or the parser crashes static void destroy_notify(gpointer data) { // Do nothing //g_debug("destroy parser"); } GValue* ghb_plist_parse(const gchar *buf, gssize len) { GMarkupParseContext *ctx; GMarkupParser parser; parse_data_t pd; GError *err = NULL; pd.stack = g_queue_new(); pd.tag_stack = g_queue_new(); pd.key = NULL; pd.value = NULL; pd.plist = NULL; pd.closed_top = FALSE; parser.start_element = start_element; parser.end_element = end_element; parser.text = text_data; parser.passthrough = passthrough; parser.error = parse_error; ctx = g_markup_parse_context_new(&parser, 0, &pd, destroy_notify); g_markup_parse_context_parse(ctx, buf, len, &err); g_markup_parse_context_end_parse(ctx, &err); g_markup_parse_context_free(ctx); if (pd.key) g_free(pd.key); if (pd.value) g_free(pd.value); g_queue_free(pd.stack); g_queue_free(pd.tag_stack); return pd.plist; } GValue* ghb_plist_parse_file(const gchar *filename) { gchar *buffer; size_t size; GValue *gval; FILE *fd; fd = g_fopen(filename, "r"); if (fd == NULL) { g_warning("Plist parse: failed to open %s", filename); return NULL; } fseek(fd, 0, SEEK_END); size = ftell(fd); fseek(fd, 0, SEEK_SET); buffer = g_malloc(size+1); size = fread(buffer, 1, size, fd); buffer[size] = 0; gval = ghb_plist_parse(buffer, (gssize)size); g_free(buffer); fclose(fd); return gval; } static void indent_fprintf(FILE *file, gint indent, const gchar *fmt, ...) { va_list ap; for (; indent; indent--) putc('\t', file); va_start(ap, fmt); vfprintf(file, fmt, ap); va_end(ap); } // Used for sorting dictionaries. static gint key_cmp(gconstpointer a, gconstpointer b) { gchar *stra = (gchar*)a; gchar *strb = (gchar*)b; return strcmp(stra, strb); } static void gval_write(FILE *file, GValue *gval) { static gint indent = 0; gint ii; GType gtype; if (gval == NULL) return; gtype = G_VALUE_TYPE(gval); if (gtype == ghb_array_get_type()) { GValue *val; gint count; indent_fprintf(file, indent, "\n"); indent++; count = ghb_array_len(gval); for (ii = 0; ii < count; ii++) { val = ghb_array_get_nth(gval, ii); gval_write(file, val); } indent--; indent_fprintf(file, indent, "\n"); } else if (gtype == ghb_dict_get_type()) { GValue *val; GHashTable *dict = g_value_get_boxed(gval); GList *link, *keys; keys = g_hash_table_get_keys(dict); // Sort the dictionary. Not really necessray, but it makes // finding things easier keys = g_list_sort(keys, key_cmp); link = keys; indent_fprintf(file, indent, "\n"); indent++; while (link) { gchar *key = (gchar*)link->data; val = g_hash_table_lookup(dict, key); indent_fprintf(file, indent, "%s\n", key); gval_write(file, val); link = link->next; } indent--; indent_fprintf(file, indent, "\n"); g_list_free(keys); } else if (gtype == G_TYPE_BOOLEAN) { gchar *tag; if (g_value_get_boolean(gval)) { tag = "true"; } else { tag = "false"; } indent_fprintf(file, indent, "<%s />\n", tag); } else if (gtype == g_date_get_type()) { GDate *date; date = g_value_get_boxed(gval); indent_fprintf(file, indent, "%d-%d-%d\n", g_date_get_year(date), g_date_get_month(date), g_date_get_day(date) ); } else if (gtype == ghb_rawdata_get_type()) { ghb_rawdata_t *data; gchar *base64; data = g_value_get_boxed(gval); base64 = g_base64_encode(data->data, data->size); indent_fprintf(file, indent, "\n"); indent_fprintf(file, 0, "%s\n", base64); indent_fprintf(file, indent, "\n"); g_free(base64); } else if (gtype == G_TYPE_DOUBLE) { gdouble val = g_value_get_double(gval); indent_fprintf(file, indent, "%.17g\n", val); } else if (gtype == G_TYPE_INT64) { gint val = g_value_get_int64(gval); indent_fprintf(file, indent, "%d\n", val); } else if (gtype == G_TYPE_INT) { gint val = g_value_get_int(gval); indent_fprintf(file, indent, "%d\n", val); } else if (gtype == G_TYPE_STRING) { const gchar *str = g_value_get_string(gval); gchar *esc = g_markup_escape_text(str, -1); indent_fprintf(file, indent, "%s\n", esc); g_free(esc); } else { // Try to make anything thats unrecognized into a string const gchar *str; GValue val = {0,}; g_value_init(&val, G_TYPE_STRING); if (g_value_transform(gval, &val)) { str = g_value_get_string(&val); gchar *esc = g_markup_escape_text(str, -1); indent_fprintf(file, indent, "%s\n", esc); g_free(esc); } else { g_message("failed to transform"); } g_value_unset(&val); } } void ghb_plist_write(FILE *file, GValue *gval) { fprintf(file, "%s", preamble); gval_write(file, gval); fprintf(file, "%s", postfix); } void ghb_plist_write_file(const gchar *filename, GValue *gval) { FILE *file; file = fopen(filename, "w"); if (file == NULL) return; fprintf(file, "%s", preamble); gval_write(file, gval); fprintf(file, "%s", postfix); } #if defined(PL_TEST) gint main(gint argc, gchar *argv[]) { GValue *gval; g_type_init(); file = g_fopen(argv[1], "r"); gval = ghb_plist_parse_file(file); if (argc > 2) ghb_plist_write_file(argv[2], gval); else ghb_plist_write(stdout, gval); if (file) fclose (file); return 0; } #endif HandBrake-0.10.2/gtk/src/ghbcellrenderertext.h0000664000175200017520000000625212300772602021624 0ustar handbrakehandbrake/* gtkcellrenderertext.h * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __GHB_CELL_RENDERER_TEXT_H__ #define __GHB_CELL_RENDERER_TEXT_H__ #include #include G_BEGIN_DECLS #define GHB_TYPE_CELL_RENDERER_TEXT (ghb_cell_renderer_text_get_type ()) #define GHB_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GHB_TYPE_CELL_RENDERER_TEXT, GhbCellRendererText)) #define GHB_CELL_RENDERER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GHB_TYPE_CELL_RENDERER_TEXT, GhbCellRendererTextClass)) #define GHB_IS_CELL_RENDERER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GHB_TYPE_CELL_RENDERER_TEXT)) #define GHB_IS_CELL_RENDERER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GHB_TYPE_CELL_RENDERER_TEXT)) #define GHB_CELL_RENDERER_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GHB_TYPE_CELL_RENDERER_TEXT, GhbCellRendererTextClass)) typedef struct _GhbCellRendererText GhbCellRendererText; typedef struct _GhbCellRendererTextClass GhbCellRendererTextClass; struct _GhbCellRendererText { GtkCellRenderer parent; /*< private >*/ gchar *text; PangoFontDescription *font; gdouble font_scale; PangoColor foreground; PangoColor background; PangoAttrList *extra_attrs; PangoUnderline underline_style; gint rise; gint fixed_height_rows; guint strikethrough : 1; guint editable : 1; guint scale_set : 1; guint foreground_set : 1; guint background_set : 1; guint underline_set : 1; guint rise_set : 1; guint strikethrough_set : 1; guint editable_set : 1; guint calc_fixed_height : 1; }; struct _GhbCellRendererTextClass { GtkCellRendererClass parent_class; void (* edited) (GhbCellRendererText *cell_renderer_text, const gchar *path, const gchar *new_text); gboolean (* keypress) (GhbCellRendererText *cell_renderer_text, GdkEventKey *event); /* Padding for future expansion */ void (*_gtk_reserved1) (void); void (*_gtk_reserved2) (void); void (*_gtk_reserved3) (void); void (*_gtk_reserved4) (void); }; GType ghb_cell_renderer_text_get_type (void) G_GNUC_CONST; GtkCellRenderer *ghb_cell_renderer_text_new (void); void ghb_cell_renderer_text_set_fixed_height_from_font (GhbCellRendererText *renderer, gint number_of_rows); G_END_DECLS #endif /* __GHB_CELL_RENDERER_TEXT_H__ */ HandBrake-0.10.2/gtk/src/hb-add-queue.svg0000664000175200017520000002054512311644756020414 0ustar handbrakehandbrake image/svg+xml HandBrake-0.10.2/gtk/src/subtitlehandler.h0000664000175200017520000000322412465416500020761 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * subtitlehandler.h * Copyright (C) John Stebbins 2008-2015 * * audiohandler.h is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * callbacks.h is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with callbacks.h. If not, write to: * The Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301, USA. */ #if !defined(_SUBTITLEHANDLER_H_) #define _SUBTITLEHANDLER_H_ #include "settings.h" void ghb_set_pref_subtitle_settings(signal_user_data_t *ud, const hb_title_t *title, GValue *settings); void ghb_set_subtitle(signal_user_data_t *ud, gint track, GValue *settings); void ghb_reset_subtitles(signal_user_data_t *ud, GValue *settings); void ghb_subtitle_prune(signal_user_data_t *ud); void ghb_subtitle_list_refresh_selected(signal_user_data_t *ud); void ghb_subtitle_list_refresh_all(signal_user_data_t *ud); void ghb_init_subtitle_defaults_ui(signal_user_data_t *ud); void ghb_subtitle_defaults_to_ui(signal_user_data_t *ud); void ghb_subtitle_title_change(signal_user_data_t *ud, gboolean show); void ghb_subtitle_set_pref_lang(GValue *settings); #endif // _SUBTITLEHANDLER_H_ HandBrake-0.10.2/gtk/src/internal_defaults.xml0000664000175200017520000002532512505046301021643 0ustar handbrakehandbrake Globals show_preview scan_source Initialization AudioBitrate 192 AudioEncoder copy:ac3 AudioTrack 0 AudioTrackQualityEnable AudioTrackQuality -1 AudioTrackGainSlider 0 AudioTrackDRCSlider 0 AudioMixdown dpl2 AudioSamplerate source AudioTrackName angle_count 1 angle 1 autoscale dest_dir dest_file new_video.mp4 end_point 100 MetaName MetaArtist MetaAlbumArtist MetaReleaseDate MetaComment MetaGenre MetaDescription MetaLongDescription preset Regular Normal preset_modified preset_reload preset_selection Regular Normal PictureDisplayWidth 720 PictureDisplayHeight 480 PtoPType chapter scale_height 0 scale_width 0 single_title 1 start_point 1 start_frame -1 title 0 title_selected volume_label New Video audio_list chapter_list subtitle_list vquality_type_bitrate vquality_type_constant SrtLanguage und SrtCodeset ISO-8859-1 SrtFile SrtOffset 0 VideoFramerateCFR VideoFrameratePFR VideoFramerateVFR PictureDeinterlaceDecomb VideoPresetSlider 0 x264ZeroLatency x264FastDecode Preferences RemoveFinishedJobs HideAdvancedVideoSettings AutoScan AddCC EncodeLogLocation allow_tweaks last_update_check 0 check_updates weekly default_source /dev/dvd ExportDirectory destination_dir hbfd hbfd_feature live_duration 15 LoggingLevel 1 LogLongevity immortal use_dvdnav reduce_hd_preview MinTitleDuration 10 preview_count 10 preview_fullscreen preview_show_crop preview_x -1 preview_y -1 show_presets UseM4v auto_name auto_name_template {source} update_skip_version 0 VideoQualityGranularity 1 version 0.1 PreferredLanguage und WhenComplete notify SrtDir window_width 1 window_height 1 Presets PresetBuildNumber PictureAutoCrop ChapterMarkers FileFormat mp4 Folder PictureLooseCrop PictureModulus 2 PictureDeblock 0 PictureDecombDeinterlace PictureDecomb off PictureDecombCustom Default PictureBottomCrop 0 PictureLeftCrop 0 PictureRightCrop 0 PictureTopCrop 0 PictureDeinterlace off PictureDeinterlaceCustom PictureDenoiseFilter off PictureDenoisePreset medium PictureDenoiseTune none PictureDenoiseCustom PictureDetelecine off PictureDetelecineCustom PicturePAR 2 PicturePARWidth 853 PicturePARHeight 720 PictureHeight 0 PictureWidth 0 VideoFramerate source VideoFramerateMode vfr VideoGrayScale Mp4HttpOptimize Mp4iPodCompatible PictureKeepRatio Mp4LargeFile AudioAllowMP3Pass AudioAllowAACPass AudioAllowAC3Pass AudioAllowDTSPass AudioAllowDTSHDPass AudioEncoderFallback ffac3 AudioLanguageList und AudioList AudioBitrate 192 AudioEncoder copy:ac3 AudioTrackQualityEnable AudioTrackQuality -1 AudioTrackGainSlider 0 AudioTrackDRCSlider 0 AudioMixdown dpl2 AudioSamplerate source AudioSecondaryEncoderMode AudioTrackSelectionBehavior first PresetDescription PresetName Name Missing Type 1 SubtitleLanguageList und SubtitleTrackSelectionBehavior none SubtitleAddCC SubtitleAddForeignAudioSearch SubtitleAddForeignAudioSubtitle VideoTurboTwoPass UsesPictureFilters UsesPictureSettings 2 VideoTwoPass VideoAvgBitrate 1800 VideoEncoder x264 VideoQualityType 2 VideoQualitySlider 0.60 x264Option VideoPreset medium VideoTune VideoProfile VideoLevel x264UseAdvancedOptions VideoOptionExtra HandBrake-0.10.2/gtk/src/hb-picture.svg0000664000175200017520000001764212311644756020221 0ustar handbrakehandbrake image/svg+xml HandBrake-0.10.2/gtk/src/ghbcompositor.c0000664000175200017520000005307712303157614020454 0ustar handbrakehandbrake/* "Borrowed" from: */ /* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ #include #include #include "ghbcompat.h" #include "ghbcompositor.h" enum { PROP_0, }; enum { CHILD_PROP_0, CHILD_PROP_Z_POS, CHILD_PROP_OPACITY }; #define GHB_COMPOSITOR_GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE((obj), GHB_TYPE_COMPOSITOR, GhbCompositorPrivate) static void ghb_compositor_finalize (GObject *object); static void ghb_compositor_realize (GtkWidget *widget); static void ghb_compositor_unrealize (GtkWidget *widget); static void ghb_compositor_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static gboolean ghb_compositor_draw (GtkWidget *widget, cairo_t *cr); static void ghb_compositor_get_preferred_width(GtkWidget *widget, gint *minimum_size, gint *natural_size); static void ghb_compositor_get_preferred_height(GtkWidget *widget, gint *minimum_size, gint *natural_size); static void ghb_compositor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void ghb_compositor_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void ghb_compositor_add (GtkContainer *container, GtkWidget *widget); static void ghb_compositor_remove (GtkContainer *container, GtkWidget *widget); static void ghb_compositor_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer data); static GType ghb_compositor_child_type (GtkContainer *container); static void ghb_compositor_set_child_property (GtkContainer *container, GtkWidget *child, guint prop_id, const GValue *value, GParamSpec *pspec); static void ghb_compositor_get_child_property (GtkContainer *container, GtkWidget *child, guint prop_id, GValue *value, GParamSpec *pspec); G_DEFINE_TYPE (GhbCompositor, ghb_compositor, GTK_TYPE_BIN) static void ghb_compositor_class_init (GhbCompositorClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS (class); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class); gobject_class->finalize = ghb_compositor_finalize; gobject_class->set_property = ghb_compositor_set_property; gobject_class->get_property = ghb_compositor_get_property; widget_class->draw = ghb_compositor_draw; widget_class->get_preferred_width = ghb_compositor_get_preferred_width; widget_class->get_preferred_height = ghb_compositor_get_preferred_height; widget_class->size_allocate = ghb_compositor_size_allocate; widget_class->realize = ghb_compositor_realize; widget_class->unrealize = ghb_compositor_unrealize; container_class->add = ghb_compositor_add; container_class->remove = ghb_compositor_remove; container_class->forall = ghb_compositor_forall; container_class->child_type = ghb_compositor_child_type; container_class->set_child_property = ghb_compositor_set_child_property; container_class->get_child_property = ghb_compositor_get_child_property; gtk_container_class_install_child_property (container_class, CHILD_PROP_Z_POS, g_param_spec_uint ("z-pos", "Position in Z-List", "Sets the blending order of the child.", 0, 65535, 0, G_PARAM_READWRITE)); gtk_container_class_install_child_property (container_class, CHILD_PROP_OPACITY, g_param_spec_double ("opacity", "Opacity", "Sets the opacity of the child.", 0.0, 1.0, 1.0, G_PARAM_READWRITE)); } static GType ghb_compositor_child_type(GtkContainer *container) { return GTK_TYPE_WIDGET; } static void ghb_compositor_get_property ( GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void ghb_compositor_set_property ( GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static gint zsort(gconstpointer a, gconstpointer b) { GhbCompositorChild *cca, *ccb; cca = (GhbCompositorChild*)a; ccb = (GhbCompositorChild*)b; return (cca->z_pos - ccb->z_pos); } static void ghb_compositor_set_child_property( GtkContainer *container, GtkWidget *child, guint prop_id, const GValue *value, GParamSpec *pspec) { GhbCompositor *compositor; GhbCompositorChild *cc; GList *link; compositor = GHB_COMPOSITOR(container); for (link = compositor->children; link != NULL; link = link->next) { cc = (GhbCompositorChild*)link->data; if (cc->widget == child) break; } if (link == NULL) { GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID(container, prop_id, pspec); return; } switch (prop_id) { case CHILD_PROP_Z_POS: { cc->z_pos = g_value_get_uint(value); compositor->children = g_list_sort(compositor->children, zsort); } break; case CHILD_PROP_OPACITY: { cc->opacity = g_value_get_double(value); } break; default: GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID(container, prop_id, pspec); break; } if (gtk_widget_get_visible (child) && gtk_widget_get_visible (GTK_WIDGET(compositor))) { gtk_widget_queue_resize (child); } } static void ghb_compositor_get_child_property( GtkContainer *container, GtkWidget *child, guint prop_id, GValue *value, GParamSpec *pspec) { GhbCompositor *compositor; GhbCompositorChild *cc; GList *link; compositor = GHB_COMPOSITOR(container); for (link = compositor->children; link != NULL; link = link->next) { cc = (GhbCompositorChild*)link->data; if (cc->widget == child) break; } if (link == NULL) { GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID(container, prop_id, pspec); return; } switch (prop_id) { case CHILD_PROP_Z_POS: { g_value_set_uint(value, cc->z_pos); } break; case CHILD_PROP_OPACITY: { g_value_set_double(value, cc->opacity); } break; default: GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID(container, prop_id, pspec); break; } } static void ghb_compositor_init (GhbCompositor *compositor) { gtk_widget_set_has_window(GTK_WIDGET(compositor), TRUE); } GtkWidget* ghb_compositor_new (void) { return GTK_WIDGET(g_object_new (GHB_TYPE_COMPOSITOR, NULL)); } #if 0 static void showtype(const gchar *msg, GtkWidget *widget) { GType type; gchar *str; type = G_OBJECT_TYPE(widget); if (type == GTK_TYPE_DRAWING_AREA) str = "drawing area"; else if (type == GTK_TYPE_ALIGNMENT) str = "alignment"; else if (type == GTK_TYPE_EVENT_BOX) str = "event box"; else if (type == GTK_TYPE_EVENT_BOX) str = "event box"; else str = "unknown"; g_message("%s: %s", msg, str); } #endif static void find_drawables(GtkWidget *widget, gpointer data) { GList *drawables, **pdrawables; pdrawables = (GList**)data; drawables = *pdrawables; if (gtk_widget_get_has_window(widget)) { drawables = g_list_append(drawables, widget); *pdrawables = drawables; return; } if (GTK_IS_CONTAINER(widget)) { gtk_container_forall(GTK_CONTAINER(widget), find_drawables, data); } } /** * ghb_compositor_zlist_insert: * @compositor: a #GhbCompositor * @child: widgets to insert * @z_pos: position * @opacity: global opacity for this widget * * Insert in the given position of the zlist in the compositor. * All children in the zlist must have associated GdkDrawable's * This means they must be GtkDrawingArea or GtkEventBox * **/ void ghb_compositor_zlist_insert ( GhbCompositor *compositor, GtkWidget *child, gint z_pos, gdouble opacity) { GhbCompositorChild *cc; GdkDisplay *display; g_return_if_fail (GHB_IS_COMPOSITOR (compositor)); g_return_if_fail (GTK_IS_WIDGET (child)); g_return_if_fail (gtk_widget_get_parent(child) == NULL); gtk_widget_set_parent(child, GTK_WIDGET(compositor)); display = gtk_widget_get_display (GTK_WIDGET(compositor)); cc = g_new(GhbCompositorChild, 1); cc->widget = child; cc->z_pos = z_pos; cc->opacity = opacity; cc->drawables = NULL; compositor->children = g_list_insert_sorted( compositor->children, cc, zsort); if (gdk_display_supports_composite(display)) { GList *link; cc->drawables = NULL; find_drawables(cc->widget, &cc->drawables); for (link = cc->drawables; link != NULL; link = link->next) { gtk_widget_realize(GTK_WIDGET(link->data)); gdk_window_set_composited(gtk_widget_get_window(GTK_WIDGET(link->data)), TRUE); } } } static void ghb_compositor_add(GtkContainer *container, GtkWidget *child) { GhbCompositor *compositor = GHB_COMPOSITOR(container); GhbCompositorChild *cc; gint z_pos = 0; GList *last = g_list_last(compositor->children); if (last != NULL) { cc = (GhbCompositorChild*)last->data; z_pos = cc->z_pos + 1; } ghb_compositor_zlist_insert(compositor, child, z_pos, 1.0); } static void ghb_compositor_remove(GtkContainer *container, GtkWidget *child) { GhbCompositor *compositor = GHB_COMPOSITOR(container); GhbCompositorChild *cc; GList *link; for (link = compositor->children; link != NULL; link = link->next) { cc = (GhbCompositorChild*)link->data; if (cc->widget == child) { gboolean was_visible = gtk_widget_get_visible( child ); gtk_widget_unparent(child); compositor->children = g_list_remove_link( compositor->children, link); g_list_free(link); g_free(cc); if (was_visible && gtk_widget_get_visible (GTK_WIDGET(container))) gtk_widget_queue_resize(GTK_WIDGET(container)); break; } } } static void ghb_compositor_forall( GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer data) { GhbCompositor *compositor = GHB_COMPOSITOR (container); GhbCompositorChild *cc; GList *link, *next; for (link = compositor->children; link != NULL; ) { // The callback may cause the link to be removed from the list. // So find next before calling callback next = link->next; cc = (GhbCompositorChild*)link->data; (*callback)(cc->widget, data); link = next; } } static void ghb_compositor_finalize (GObject *object) { GhbCompositor *compositor = GHB_COMPOSITOR (object); GhbCompositorChild *cc; GList *link; for (link = compositor->children; link != NULL; link = link->next) { cc = (GhbCompositorChild*)link->data; g_list_free(cc->drawables); g_free(cc); } g_list_free(compositor->children); G_OBJECT_CLASS (ghb_compositor_parent_class)->finalize (object); } static void ghb_compositor_realize (GtkWidget *widget) { GdkWindowAttr attributes; gint attributes_mask; gint border_width; gboolean visible_window; gtk_widget_set_realized(widget, TRUE); border_width = gtk_container_get_border_width(GTK_CONTAINER (widget)); GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); attributes.x = allocation.x + border_width; attributes.y = allocation.y + border_width; attributes.width = allocation.width - 2*border_width; attributes.height = allocation.height - 2*border_width; attributes.window_type = GDK_WINDOW_CHILD; attributes.event_mask = gtk_widget_get_events (widget) | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK; visible_window = gtk_widget_get_has_window(widget); GdkWindow *window; if (visible_window) { attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; attributes.visual = gtk_widget_get_visual (widget); attributes.wclass = GDK_INPUT_OUTPUT; window = gdk_window_new(gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gtk_widget_set_window(widget, window); gdk_window_set_user_data(window, widget); } else { window = gtk_widget_get_parent_window (widget); gtk_widget_set_window(widget, window); g_object_ref (window); } } static void ghb_compositor_unrealize (GtkWidget *widget) { GTK_WIDGET_CLASS (ghb_compositor_parent_class)->unrealize (widget); } static void ghb_compositor_get_preferred_width( GtkWidget *widget, gint *minimum_size, gint *natural_size) { GhbCompositor *compositor = GHB_COMPOSITOR (widget); GList *link; GhbCompositorChild *cc; gint width = 0; GtkRequisition min_size, size; for (link = compositor->children; link != NULL; link = link->next) { cc = (GhbCompositorChild*)link->data; if (gtk_widget_get_visible(cc->widget)) { gtk_widget_get_preferred_size(cc->widget, &min_size, &size); width = MAX(MAX(min_size.width, size.width), width); } } *minimum_size = width + gtk_container_get_border_width(GTK_CONTAINER (widget)) * 2; *natural_size = width + gtk_container_get_border_width(GTK_CONTAINER (widget)) * 2; } static void ghb_compositor_get_preferred_height( GtkWidget *widget, gint *minimum_size, gint *natural_size) { GhbCompositor *compositor = GHB_COMPOSITOR (widget); GList *link; GhbCompositorChild *cc; gint height = 0; GtkRequisition min_size, size; for (link = compositor->children; link != NULL; link = link->next) { cc = (GhbCompositorChild*)link->data; if (gtk_widget_get_visible(cc->widget)) { gtk_widget_get_preferred_size(cc->widget, &min_size, &size); height = MAX(MAX(min_size.height, size.height), height); } } *minimum_size = height + gtk_container_get_border_width(GTK_CONTAINER (widget)) * 2; *natural_size = height + gtk_container_get_border_width(GTK_CONTAINER (widget)) * 2; } static void ghb_compositor_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GhbCompositor *compositor; GtkAllocation child_allocation; GhbCompositorChild *cc; GList *link; gtk_widget_set_allocation(widget, allocation); compositor = GHB_COMPOSITOR (widget); if (!gtk_widget_get_has_window(widget)) { child_allocation.x = allocation->x + gtk_container_get_border_width(GTK_CONTAINER(widget)); child_allocation.y = allocation->y + gtk_container_get_border_width(GTK_CONTAINER(widget)); } else { child_allocation.x = 0; child_allocation.y = 0; } child_allocation.width = MAX (allocation->width - gtk_container_get_border_width(GTK_CONTAINER (widget)) * 2, 0); child_allocation.height = MAX (allocation->height - gtk_container_get_border_width(GTK_CONTAINER (widget)) * 2, 0); if (gtk_widget_get_realized(widget)) { if (gtk_widget_get_has_window(widget)) { gdk_window_move_resize (gtk_widget_get_window(widget), allocation->x + gtk_container_get_border_width(GTK_CONTAINER (widget)), allocation->y + gtk_container_get_border_width(GTK_CONTAINER (widget)), child_allocation.width, child_allocation.height); } } for (link = compositor->children; link != NULL; link = link->next) { cc = (GhbCompositorChild*)link->data; if (gtk_widget_get_realized (cc->widget)) gtk_widget_size_allocate (cc->widget, &child_allocation); } } #if 0 static void showrects(cairo_region_t *region) { cairo_rectangle_int_t rect; int ii; int count = cairo_region_num_rectangles(region); printf("rect count %d\n", count); for (ii = 0; ii < count; ii++) { cairo_region_get_rectangle(region, ii, &rect); printf("rect %d: %d,%d %dx%d\n", ii, rect.x, rect.y, rect.width, rect.height); } } #endif static void ghb_compositor_blend (GtkWidget *widget, cairo_t *cr) { GhbCompositor *compositor = GHB_COMPOSITOR (widget); GList *link, *draw; cairo_region_t *region; GtkWidget *child; GhbCompositorChild *cc; if (compositor->children == NULL) return; /* create a cairo context to draw to the window */ for (link = compositor->children; link != NULL; link = link->next) { cc = (GhbCompositorChild*)link->data; for (draw = cc->drawables; draw != NULL; draw = draw->next) { GtkAllocation child_alloc; /* get our child */ child = GTK_WIDGET(draw->data); if ( //!gtk_cairo_should_draw_window(cr, gtk_widget_get_window(child)) || !gtk_widget_get_visible(cc->widget) || !gtk_widget_get_visible(child)) continue; gtk_widget_get_allocation(child, &child_alloc); cairo_save(cr); /* the source data is the (composited) event box */ gdk_cairo_set_source_window(cr, gtk_widget_get_window(child), child_alloc.x, child_alloc.y); cairo_rectangle_int_t rect; rect.x = child_alloc.x; rect.y = child_alloc.y; rect.width = child_alloc.width; rect.height = child_alloc.height; /* draw no more than our expose event intersects our child */ region = cairo_region_create_rectangle(&rect); cairo_region_t *dregion = gdk_window_get_visible_region( gtk_widget_get_window(child)); cairo_region_translate(dregion, child_alloc.x, child_alloc.y); cairo_region_intersect(region, dregion); cairo_region_destroy(dregion); gdk_cairo_region(cr, region); cairo_region_destroy(region); cairo_clip(cr); /* composite, with an opacity */ cairo_set_operator(cr, CAIRO_OPERATOR_OVER); cairo_paint_with_alpha(cr, cc->opacity); cairo_restore(cr); } } /* we're done */ } static gboolean ghb_compositor_draw(GtkWidget *widget, cairo_t *cr) { if (gtk_widget_is_drawable(widget)) { if (gtk_widget_get_has_window(widget)) ghb_compositor_blend (widget, cr); } GTK_WIDGET_CLASS(ghb_compositor_parent_class)->draw(widget, cr); return FALSE; } HandBrake-0.10.2/gtk/src/hb-stop.svg0000664000175200017520000001466612311644756017536 0ustar handbrakehandbrake image/svg+xml HandBrake-0.10.2/gtk/src/queuehandler.h0000664000175200017520000000225212465416500020252 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * queuehandler.h * Copyright (C) John Stebbins 2008-2015 * * queuehandler.h is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * callbacks.h is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with callbacks.h. If not, write to: * The Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301, USA. */ #if !defined(_QUEUEHANDLER_H_) #define _QUEUEHANDLER_H_ #include #include "settings.h" void ghb_queue_buttons_grey(signal_user_data_t *ud); gboolean ghb_reload_queue(signal_user_data_t *ud); void ghb_queue_remove_row(signal_user_data_t *ud, int row); #endif // _QUEUEHANDLER_H_ HandBrake-0.10.2/gtk/src/ghb-dvd.c0000664000175200017520000002503512465416500017102 0ustar handbrakehandbrake/*************************************************************************** * ghb-dvd.c * * Sat Apr 19 11:12:53 2008 * Copyright 2008-2015 John Stebbins * ****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA */ // Well, I waisted a bit of time on this. It seems libhb has a function for // this that I hadn't discovered yet. hb_dvd_name(). // I borrowed most of this from the udev utility vol_id // Here is the authors copyright. /* * volume_id - reads filesystem label and uuid * * Copyright (C) 2004 Kay Sievers * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation version 2 of the License. */ #if defined(_WIN32) #include #endif #include #include #include "ghb-dvd.h" #if 0 #include #include #include #ifndef PACKED #define PACKED __attribute__((packed)) #endif struct volume_descriptor { struct descriptor_tag { guint16 id; guint16 version; guint8 checksum; guint8 reserved; guint16 serial; guint16 crc; guint16 crc_len; guint32 location; } PACKED tag; union { struct anchor_descriptor { guint32 length; guint32 location; } PACKED anchor; struct primary_descriptor { guint32 seq_num; guint32 desc_num; struct dstring { guint8 clen; guint8 c[31]; } PACKED ident; } PACKED primary; } PACKED type; } PACKED; struct volume_structure_descriptor { guint8 type; guint8 id[5]; guint8 version; } PACKED; #define VOLUME_ID_LABEL_SIZE 64 typedef struct { gint fd; gchar label[VOLUME_ID_LABEL_SIZE+1]; } udf_info_t; enum endian { LE = 0, BE = 1 }; #ifdef __BYTE_ORDER #if (__BYTE_ORDER == __LITTLE_ENDIAN) #define le16_to_cpu(x) (x) #define le32_to_cpu(x) (x) #define le64_to_cpu(x) (x) #define be16_to_cpu(x) bswap_16(x) #define be32_to_cpu(x) bswap_32(x) #define cpu_to_le16(x) (x) #define cpu_to_le32(x) (x) #define cpu_to_be32(x) bswap_32(x) #elif (__BYTE_ORDER == __BIG_ENDIAN) #define le16_to_cpu(x) bswap_16(x) #define le32_to_cpu(x) bswap_32(x) #define le64_to_cpu(x) bswap_64(x) #define be16_to_cpu(x) (x) #define be32_to_cpu(x) (x) #define cpu_to_le16(x) bswap_16(x) #define cpu_to_le32(x) bswap_32(x) #define cpu_to_be32(x) (x) #endif #endif /* __BYTE_ORDER */ #define UDF_VSD_OFFSET 0x8000 static guint8* get_buffer(int fd, guint64 off, gsize len) { gint buf_len; if (lseek(fd, off, SEEK_SET) < 0) { return NULL; } guint8 *buffer = g_malloc(len); buf_len = read(fd, buffer, len); if (buf_len < 0) { g_free(buffer); return NULL; } return buffer; } static gint set_unicode16(guint8 *str, gsize len, const guint8 *buf, gint endianess, gsize count) { gint ii, jj; guint16 c; jj = 0; for (ii = 0; ii + 2 <= count; ii += 2) { if (endianess == LE) c = (buf[ii+1] << 8) | buf[ii]; else c = (buf[ii] << 8) | buf[ii+1]; if (c == 0) { str[jj] = '\0'; break; } else if (c < 0x80) { if (jj+1 >= len) break; str[jj++] = (guint8) c; } else if (c < 0x800) { if (jj+2 >= len) break; str[jj++] = (guint8) (0xc0 | (c >> 6)); str[jj++] = (guint8) (0x80 | (c & 0x3f)); } else { if (jj+3 >= len) break; str[jj++] = (guint8) (0xe0 | (c >> 12)); str[jj++] = (guint8) (0x80 | ((c >> 6) & 0x3f)); str[jj++] = (guint8) (0x80 | (c & 0x3f)); } } str[jj] = '\0'; return jj; } static void set_label_string(guint8 *str, const guint8 *buf, gsize count) { gint ii; memcpy(str, buf, count); str[count] = 0; /* remove trailing whitespace */ ii = strlen((gchar*)str); while (ii--) { if (!g_ascii_isspace(str[ii])) break; } str[ii+1] = 0; } static gint probe_udf(udf_info_t *id) { struct volume_descriptor *vd; struct volume_structure_descriptor *vsd; guint bs; guint b; guint type; guint count; guint loc; guint clen; guint64 off = 0; vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET, 0x200); if (vsd == NULL) return -1; if (memcmp(vsd->id, "NSR02", 5) == 0) goto blocksize; if (memcmp(vsd->id, "NSR03", 5) == 0) goto blocksize; if (memcmp(vsd->id, "BEA01", 5) == 0) goto blocksize; if (memcmp(vsd->id, "BOOT2", 5) == 0) goto blocksize; if (memcmp(vsd->id, "CD001", 5) == 0) goto blocksize; if (memcmp(vsd->id, "CDW02", 5) == 0) goto blocksize; if (memcmp(vsd->id, "TEA03", 5) == 0) goto blocksize; return -1; blocksize: /* search the next VSD to get the logical block size of the volume */ for (bs = 0x800; bs < 0x8000; bs += 0x800) { vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET + bs, 0x800); if (vsd == NULL) return -1; if (vsd->id[0] != '\0') goto nsr; } return -1; nsr: /* search the list of VSDs for a NSR descriptor */ for (b = 0; b < 64; b++) { vsd = (struct volume_structure_descriptor *) get_buffer(id->fd, off + UDF_VSD_OFFSET + (b * bs), 0x800); if (vsd == NULL) return -1; if (vsd->id[0] == '\0') return -1; if (memcmp(vsd->id, "NSR02", 5) == 0) goto anchor; if (memcmp(vsd->id, "NSR03", 5) == 0) goto anchor; } return -1; anchor: /* read anchor volume descriptor */ vd = (struct volume_descriptor *) get_buffer(id->fd, off + (256 * bs), 0x200); if (vd == NULL) return -1; type = le16_to_cpu(vd->tag.id); if (type != 2) /* TAG_ID_AVDP */ goto found; /* get desriptor list address and block count */ count = le32_to_cpu(vd->type.anchor.length) / bs; loc = le32_to_cpu(vd->type.anchor.location); /* pick the primary descriptor from the list */ for (b = 0; b < count; b++) { vd = (struct volume_descriptor *) get_buffer(id->fd, off + ((loc + b) * bs), 0x200); if (vd == NULL) return -1; type = le16_to_cpu(vd->tag.id); /* check validity */ if (type == 0) goto found; if (le32_to_cpu(vd->tag.location) != loc + b) goto found; if (type == 1) /* TAG_ID_PVD */ goto pvd; } goto found; pvd: clen = vd->type.primary.ident.clen; if (clen == 8) set_label_string((guint8*)id->label, vd->type.primary.ident.c, 31); else if (clen == 16) set_unicode16((guint8*)id->label, sizeof(id->label), vd->type.primary.ident.c, BE, 31); found: return 0; } gchar* ghb_dvd_volname(const gchar *device) { udf_info_t id; gchar *buffer = NULL; id.fd = open(device, O_RDONLY); if (id.fd < 0) { return NULL; } if (probe_udf (&id) == 0) { buffer = g_strdup(id.label); } return buffer; } #endif gchar* ghb_resolve_symlink(const gchar *name) { gchar *file; GFileInfo *info; GFile *gfile; gfile = g_file_new_for_path(name); info = g_file_query_info(gfile, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET "," G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK, G_FILE_QUERY_INFO_NONE, NULL, NULL); while ((info != NULL) && g_file_info_get_is_symlink(info)) { GFile *parent; const gchar *target; parent = g_file_get_parent(gfile); g_object_unref(gfile); target = g_file_info_get_symlink_target(info); gfile = g_file_resolve_relative_path(parent, target); g_object_unref(parent); g_object_unref(info); info = g_file_query_info(gfile, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET "," G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK, G_FILE_QUERY_INFO_NONE, NULL, NULL); } if (info != NULL) { file = g_file_get_path(gfile); g_object_unref(info); } else { file = g_strdup(name); } g_object_unref(gfile); return file; } void ghb_dvd_set_current(const gchar *name, signal_user_data_t *ud) { #if !defined(_WIN32) GFile *gfile; GFileInfo *info; gchar *resolved = ghb_resolve_symlink(name); if (ud->current_dvd_device != NULL) { g_free(ud->current_dvd_device); ud->current_dvd_device = NULL; } gfile = g_file_new_for_path(resolved); info = g_file_query_info(gfile, G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_QUERY_INFO_NONE, NULL, NULL); if (info != NULL) { if (g_file_info_get_file_type(info) == G_FILE_TYPE_SPECIAL) { // I could go through the trouble to scan the connected drives and // verify that this device is connected and is a DVD. But I don't // think its neccessary. ud->current_dvd_device = resolved; } else { g_free(resolved); } g_object_unref(info); } else { g_free(resolved); } g_object_unref(gfile); #else gchar drive[4]; guint dtype; if (ud->current_dvd_device != NULL) { g_free(ud->current_dvd_device); ud->current_dvd_device = NULL; } g_strlcpy(drive, name, 4); dtype = GetDriveType(drive); if (dtype == DRIVE_CDROM) { ud->current_dvd_device = g_strdup(name); } #endif } HandBrake-0.10.2/gtk/src/hb-edit.svg0000664000175200017520000001210212312123633017440 0ustar handbrakehandbrake image/svg+xml HandBrake-0.10.2/gtk/src/icons.h0000664000175200017520000000150312300772602016675 0ustar handbrakehandbrake/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA */ #if !defined(_GHB_ICONS_H_) #define _GHB_ICONS_H_ void ghb_load_icons(void); #endif // _GHB_ICONS_H_ HandBrake-0.10.2/gtk/src/settings.h0000664000175200017520000001063312440153745017434 0ustar handbrakehandbrake/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA */ #if !defined(_SETTINGS_H_) #define _SETTINGS_H_ #include #define GHB_WIDGET(b,n) GTK_WIDGET(gtk_builder_get_object ((b), (n))) //#define GHB_WIDGET(b,n) GTK_WIDGET(debug_get_object((b), (n))) #define GHB_ACTION(b,n) GTK_ACTION(gtk_builder_get_object ((b), (n))) #define GHB_OBJECT(b,n) gtk_builder_get_object ((b), (n)) GObject* debug_get_object(GtkBuilder *b, const gchar *n); enum { GHB_STATE_IDLE = 0x00, GHB_STATE_SCANNING = 0x02, GHB_STATE_SCANDONE = 0x04, GHB_STATE_WORKING = 0x08, GHB_STATE_WORKDONE = 0x10, GHB_STATE_PAUSED = 0x20, GHB_STATE_MUXING = 0x40, GHB_STATE_SEARCHING = 0x80, }; enum { GHB_CANCEL_NONE, GHB_CANCEL_ALL, GHB_CANCEL_CURRENT, GHB_CANCEL_FINISH }; typedef struct preview_s preview_t; typedef struct { gchar *current_dvd_device; gboolean debug; gboolean dont_clear_presets; gboolean scale_busy; gint cancel_encode; GtkBuilder *builder; GValue *x264_priv; GValue *globals; GValue *prefs; GValue *settings; GValue *settings_array; GValue *queue; GValue *current_job; GIOChannel *activity_log; GIOChannel *job_activity_log; preview_t *preview; gchar *appcast; gint appcast_len; GdkVisibilityState hb_visibility; } signal_user_data_t; enum { GHB_QUEUE_PENDING, GHB_QUEUE_RUNNING, GHB_QUEUE_CANCELED, GHB_QUEUE_FAIL, GHB_QUEUE_DONE, }; GValue* ghb_settings_new(void); void ghb_settings_take_value( GValue *settings, const gchar *key, GValue *value); void ghb_settings_set_value( GValue *settings, const gchar *key, const GValue *value); void ghb_settings_set_string( GValue *settings, const gchar *key, const gchar *sval); void ghb_settings_set_double(GValue *settings, const gchar *key, gdouble dval); void ghb_settings_set_int64(GValue *settings, const gchar *key, gint64 ival); void ghb_settings_set_int(GValue *settings, const gchar *key, gint ival); void ghb_settings_set_boolean( GValue *settings, const gchar *key, gboolean bval); void ghb_settings_copy( GValue *settings, const gchar *key, const GValue *value); GValue* ghb_settings_get_value(const GValue *settings, const gchar *key); gboolean ghb_settings_get_boolean(const GValue *settings, const gchar *key); gint64 ghb_settings_get_int64(const GValue *settings, const gchar *key); gint ghb_settings_get_int(const GValue *settings, const gchar *key); gdouble ghb_settings_get_double(const GValue *settings, const gchar *key); gchar* ghb_settings_get_string(const GValue *settings, const gchar *key); const gchar* ghb_settings_get_const_string( const GValue *settings, const gchar *key); gint ghb_settings_combo_int(const GValue *settings, const gchar *key); gdouble ghb_settings_combo_double(const GValue *settings, const gchar *key); const gchar* ghb_settings_combo_option(const GValue *settings, const gchar *key); const gchar* ghb_settings_combo_string(const GValue *settings, const gchar *key); GValue* ghb_widget_value(GtkWidget *widget); gchar* ghb_widget_string(GtkWidget *widget); gdouble ghb_widget_double(GtkWidget *widget); gint64 ghb_widget_int64(GtkWidget *widget); gint ghb_widget_int(GtkWidget *widget); gint ghb_widget_boolean(GtkWidget *widget); void ghb_widget_to_setting(GValue *settings, GtkWidget *widget); int ghb_ui_update( signal_user_data_t *ud, const gchar *name, const GValue *value); int ghb_ui_update_from_settings( signal_user_data_t *ud, const gchar *name, const GValue *settings); int ghb_ui_settings_update( signal_user_data_t *ud, GValue *settings, const gchar *name, const GValue *value); const gchar* ghb_get_setting_key(GtkWidget *widget); void ghb_update_widget(GtkWidget *widget, const GValue *value); #endif // _SETTINGS_H_ HandBrake-0.10.2/gtk/src/hb-activity.svg0000664000175200017520000001406112311644756020372 0ustar handbrakehandbrake image/svg+xml HandBrake-0.10.2/gtk/src/Makefile.am0000664000175200017520000000766712372460421017470 0ustar handbrakehandbrake## Process this file with automake to produce Makefile.in icons = \ hb-icon.svg icons_dep = \ hb-icon.svg \ hb-start.svg \ hb-pause.svg \ hb-stop.svg \ hb-add-queue.svg \ hb-showqueue.svg \ hb-picture.svg \ hb-activity.svg \ hb-source.svg \ hb-remove.svg \ hb-edit.svg \ hb-complete.svg hb_menu = ghb.desktop AM_CPPFLAGS = \ -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ -DPACKAGE_SRC_DIR=\""$(srcdir)"\" \ -DPACKAGE_DATA_DIR=\""$(datadir)"\" \ $(GHB_CFLAGS) AM_CFLAGS = -Wall -g bin_PROGRAMS = ghb # Dummy file, not built. Forces g++ linking nodist_EXTRA_ghb_SOURCES = dummy.cpp ghb_SOURCES = \ callbacks.c \ callbacks.h \ queuehandler.c \ queuehandler.h \ videohandler.c \ videohandler.h \ audiohandler.c \ audiohandler.h \ subtitlehandler.c \ subtitlehandler.h \ x264handler.c \ x264handler.h \ main.c \ settings.c \ settings.h \ resources.c \ resources.h \ presets.c \ presets.h \ preview.c \ icons.c \ icons.h \ values.c \ values.h \ appcast.c \ appcast.h \ plist.c \ plist.h \ hb-backend.c \ hb-backend.h \ renderer_button.h \ renderer_button.c \ ghbcellrenderertext.c \ ghbcellrenderertext.h \ ghbcompositor.c \ ghbcompositor.h \ ghb-dvd.c \ ghb-dvd.h \ marshalers.c \ marshalers.h if MINGW ghb_LDFLAGS = \ -mwindows -Wl,--export-dynamic -Wl,--exclude-libs,ALL else ghb_LDFLAGS = \ -Wl,--export-dynamic -Wl,--exclude-libs,ALL endif ghb_LDADD = $(HB_LIBS) $(GHB_LIBS) ghb_DEPENDENCIES = $(HB_DIR)/libhb/libhandbrake.a resources.o: resource_data.h resource_data.h: resources.plist python2 $(srcdir)/quotestring.py resources.plist resource_data.h widget_reverse.deps: makedeps.py widget.deps: makedeps.py python2 $(srcdir)/makedeps.py resources.plist: create_resources resources.list $(icons_dep) internal_defaults.xml standard_presets.xml ghb.ui widget.deps widget_reverse.deps ./create_resources -I$(srcdir) $(srcdir)/resources.list resources.plist CREATE_RES.c = \ create_resources.c \ plist.c \ values.c CREATE_RES.c.o = $(patsubst %.c,%-native.o,$(CREATE_RES.c)) create_resources: $(CREATE_RES.c.o) $(CC_FOR_BUILD) -g -o $@ $^ $(GHB_TOOLS_LIBS) $(CREATE_RES.c.o): %-native.o: %.c $(CC_FOR_BUILD) $(GHB_TOOLS_CFLAGS) -g -c -o $@ $< ghbcellrenderertext.$(OBJEXT): marshalers.h $(srcdir)/marshalers.h: marshalers.list glib-genmarshal --prefix=ghb_marshal $(srcdir)/marshalers.list --header > $(srcdir)/marshalers.h $(srcdir)/marshalers.c: marshalers.list glib-genmarshal --prefix=ghb_marshal $(srcdir)/marshalers.list --body > $(srcdir)/marshalers.c gtk_update_icon_cache = gtk-update-icon-cache -f -t $(DESTDIR)/$(datadir)/icons/hicolor uninstall-local: for icon in $(icons); do \ SIZE=`echo $$icon | cut -d. -f2`; \ FILE=`echo $$icon | cut -d. -f1,3`; \ if [ "$$SIZE" = "svg" ]; then \ SIZE="scalable"; \ FILE="$$FILE.svg"; \ else \ SIZE="$${SIZE}x$${SIZE}"; \ fi; \ rm -f $(DESTDIR)/$(datadir)/icons/hicolor/$$SIZE/apps/$$FILE; \ done @-if test -z "$(DESTDIR)"; then \ echo "Updating Gtk icon cache."; \ $(gtk_update_icon_cache); \ else \ echo "*** Icon cache not updated. After install, run this:"; \ echo "*** $(gtk_update_icon_cache)"; \ fi rm -f $(DESTDIR)/$(datadir)/applications/$(hb_menu) install-data-local: for icon in $(icons); do \ SIZE=`echo $$icon | cut -d. -f2`; \ FILE=`echo $$icon | cut -d. -f1,3`; \ if [ "$$SIZE" = "svg" ]; then \ SIZE="scalable"; \ FILE="$$FILE.svg"; \ else \ SIZE="$${SIZE}x$${SIZE}"; \ fi; \ mkdir -p $(DESTDIR)/$(datadir)/icons/hicolor/$$SIZE/apps/; \ $(INSTALL_DATA) $(srcdir)/$$icon $(DESTDIR)/$(datadir)/icons/hicolor/$$SIZE/apps/$$FILE; \ done @-if test -z "$(DESTDIR)"; then \ echo "Updating Gtk icon cache."; \ $(gtk_update_icon_cache); \ else \ echo "*** Icon cache not updated. After install, run this:"; \ echo "*** $(gtk_update_icon_cache)"; \ fi mkdir -p $(DESTDIR)/$(datadir)/applications/; \ $(INSTALL_DATA) $(srcdir)/$(hb_menu) $(DESTDIR)/$(datadir)/applications/$(hb_menu) HandBrake-0.10.2/gtk/src/x264handler.c0000664000175200017520000007356212505047552017642 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * x264handler.c * Copyright (C) John Stebbins 2008-2015 * * x264handler.c is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. */ #include #include "ghbcompat.h" #include #include "settings.h" #include "values.h" #include "callbacks.h" #include "presets.h" #include "hb-backend.h" #include "x264handler.h" #include "videohandler.h" gint ghb_lookup_bframes(const gchar *options); static void x264_opt_update(signal_user_data_t *ud, GtkWidget *widget); static gchar* sanitize_x264opts(signal_user_data_t *ud, const gchar *options); // Flag needed to prevent x264 options processing from chasing its tail static gboolean ignore_options_update = FALSE; void ghb_show_hide_advanced_video( signal_user_data_t *ud ) { gboolean hide; hide = ghb_settings_get_boolean(ud->prefs, "HideAdvancedVideoSettings"); if (hide) { ghb_ui_update(ud, "x264UseAdvancedOptions", ghb_boolean_value(FALSE)); } GtkWidget *widget; GtkWidget *at = GHB_WIDGET(ud->builder, "advanced_video_tab"); gtk_widget_set_visible(at, !hide); widget = GHB_WIDGET(ud->builder, "x264UseAdvancedOptions"); gtk_widget_set_visible(widget, !hide); } G_MODULE_EXPORT void x264_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->x264_priv, widget); if (!ignore_options_update) { ignore_options_update = TRUE; x264_opt_update(ud, widget); ignore_options_update = FALSE; } ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); } G_MODULE_EXPORT void x264_slider_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->x264_priv, widget); // Lock slider values to multiples of step_increment GtkAdjustment * adj = gtk_range_get_adjustment(GTK_RANGE(widget)); gdouble step = gtk_adjustment_get_step_increment(adj); gdouble val = gtk_range_get_value(GTK_RANGE(widget)); gdouble new_val = ((int)((val + step / 2) / step)) * step; gdouble diff = val - new_val; if ( diff > 0.0001 || diff < -0.0001 ) { gtk_range_set_value(GTK_RANGE(widget), new_val); } else if (!ignore_options_update) { ignore_options_update = TRUE; x264_opt_update(ud, widget); ignore_options_update = FALSE; } ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); } G_MODULE_EXPORT gchar* x264_format_slider_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud) { return g_strdup_printf("%-6.6g", val); } G_MODULE_EXPORT void x264_me_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { gint me; ghb_widget_to_setting(ud->x264_priv, widget); if (!ignore_options_update) { ignore_options_update = TRUE; x264_opt_update(ud, widget); ignore_options_update = FALSE; } ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); widget = GHB_WIDGET(ud->builder, "x264_merange"); me = ghb_settings_combo_int(ud->x264_priv, "x264_me"); if (me < 2) { // me < umh // me_range 4 - 16 gtk_spin_button_set_range(GTK_SPIN_BUTTON(widget), 4, 16); } else { // me_range 4 - 64 gtk_spin_button_set_range(GTK_SPIN_BUTTON(widget), 4, 64); } } G_MODULE_EXPORT void x264_entry_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("x264_entry_changed_cb ()"); static char *tt = NULL; if (tt == NULL) { GtkWidget *eo = GTK_WIDGET(GHB_WIDGET(ud->builder, "VideoOptionExtra")); tt = gtk_widget_get_tooltip_text(eo); } if (!ignore_options_update) { GtkWidget *textview; gchar *options; textview = GTK_WIDGET(GHB_WIDGET(ud->builder, "x264Option")); ghb_widget_to_setting(ud->settings, textview); options = ghb_settings_get_string(ud->settings, "x264Option"); ignore_options_update = TRUE; ghb_x264_parse_options(ud, options); if (!gtk_widget_has_focus(textview)) { gchar *sopts; sopts = sanitize_x264opts(ud, options); ghb_update_x264Option(ud, sopts); ghb_x264_parse_options(ud, sopts); GtkWidget *eo = GTK_WIDGET(GHB_WIDGET(ud->builder, "VideoOptionExtra")); char * new_tt; if (sopts) new_tt = g_strdup_printf(_("%s\n\nExpanded Options:\n\"%s\""), tt, sopts); else new_tt = g_strdup_printf(_("%s\n\nExpanded Options:\n\"\""), tt); gtk_widget_set_tooltip_text(eo, new_tt); g_free(new_tt); g_free(options); options = sopts; } g_free(options); ignore_options_update = FALSE; } } G_MODULE_EXPORT gboolean x264_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, signal_user_data_t *ud) { gchar *options, *sopts; ghb_widget_to_setting(ud->settings, widget); options = ghb_settings_get_string(ud->settings, "x264Option"); sopts = sanitize_x264opts(ud, options); ignore_options_update = TRUE; if (sopts != NULL && strcmp(sopts, options) != 0) { ghb_ui_update(ud, "x264Option", ghb_string_value(sopts)); ghb_x264_parse_options(ud, sopts); } g_free(options); g_free(sopts); ignore_options_update = FALSE; return FALSE; } enum { X264_OPT_NONE, X264_OPT_BOOL_NONE, X264_OPT_INT_NONE, X264_OPT_DEBLOCK, X264_OPT_PSY, X264_OPT_INT, X264_OPT_DOUBLE, X264_OPT_COMBO, X264_OPT_BOOL, X264_OPT_TRANS, }; typedef struct { gchar *x264_val; char *ui_val; } trans_entry_t; typedef struct { gint count; gint x264_type; gint ui_type; trans_entry_t *map; } trans_table_t; static gchar * trans_x264_val(trans_table_t *trans, char *val) { int ii; if (val == NULL) return NULL; for (ii = 0; ii < trans->count; ii++) { if (strcmp(val, trans->map[ii].x264_val) == 0) { return trans->map[ii].ui_val; } } return NULL; } static gchar * trans_ui_val(trans_table_t *trans, char *val) { int ii; if (val == NULL) return NULL; for (ii = 0; ii < trans->count; ii++) { if (strcmp(val, trans->map[ii].ui_val) == 0) { return trans->map[ii].x264_val; } } return NULL; } struct x264_opt_map_s { gchar **opt_syns; gchar *name; gchar *def_val; gint type; trans_table_t *translation; gboolean found; }; static gchar *x264_ref_syns[] = {"ref", "frameref", NULL}; static gchar *x264_bframes_syns[] = {"bframes", NULL}; static gchar *x264_badapt_syns[] = {"b-adapt", "b_adapt", NULL}; static gchar *x264_direct_syns[] = {"direct", "direct-pred", "direct_pred", NULL}; static gchar *x264_weightp_syns[] = {"weightp", NULL}; static gchar *x264_bpyramid_syns[] = {"b-pyramid", "b_pyramid", NULL}; static gchar *x264_me_syns[] = {"me", NULL}; static gchar *x264_merange_syns[] = {"merange", "me-range", "me_range", NULL}; static gchar *x264_subme_syns[] = {"subme", "subq", NULL}; static gchar *x264_aqmode_syns[] = {"aq-mode", NULL}; static gchar *x264_analyse_syns[] = {"partitions", "analyse", NULL}; static gchar *x264_8x8dct_syns[] = {"8x8dct", NULL}; static gchar *x264_deblock_syns[] = {"deblock", "filter", NULL}; static gchar *x264_trellis_syns[] = {"trellis", NULL}; static gchar *x264_pskip_syns[] = {"no-fast-pskip", "no_fast_pskip", NULL}; static gchar *x264_psy_syns[] = {"psy-rd", "psy_rd", NULL}; static gchar *x264_aq_strength_syns[] = {"aq-strength", "aq_strength", NULL}; static gchar *x264_mbtree_syns[] = {"mbtree", NULL}; static gchar *x264_decimate_syns[] = {"no-dct-decimate", "no_dct_decimate", NULL}; static gchar *x264_cabac_syns[] = {"cabac", NULL}; static gint find_syn_match(const gchar *opt, gchar **syns) { gint ii; for (ii = 0; syns[ii] != NULL; ii++) { if (strcmp(opt, syns[ii]) == 0) return ii; } return -1; } struct x264_opt_map_s x264_opt_map[] = { {x264_ref_syns, "x264_refs", "3", X264_OPT_INT}, {x264_bframes_syns, "x264_bframes", "3", X264_OPT_INT}, {x264_direct_syns, "x264_direct", "spatial", X264_OPT_COMBO}, {x264_badapt_syns, "x264_b_adapt", "1", X264_OPT_COMBO}, {x264_weightp_syns, "x264_weighted_pframes", "2", X264_OPT_COMBO}, {x264_bpyramid_syns, "x264_bpyramid", "normal", X264_OPT_COMBO}, {x264_me_syns, "x264_me", "hex", X264_OPT_COMBO}, {x264_merange_syns, "x264_merange", "16", X264_OPT_INT}, {x264_subme_syns, "x264_subme", "7", X264_OPT_COMBO}, {x264_aqmode_syns, "x264_aqmode", "1", X264_OPT_INT_NONE}, {x264_analyse_syns, "x264_analyse", "p8x8,b8x8,i8x8,i4x4", X264_OPT_COMBO}, {x264_8x8dct_syns, "x264_8x8dct", "1", X264_OPT_BOOL}, {x264_deblock_syns, "x264_deblock_alpha", "0,0", X264_OPT_DEBLOCK}, {x264_deblock_syns, "x264_deblock_beta", "0,0", X264_OPT_DEBLOCK}, {x264_trellis_syns, "x264_trellis", "1", X264_OPT_COMBO}, {x264_pskip_syns, "x264_no_fast_pskip", "0", X264_OPT_BOOL}, {x264_decimate_syns, "x264_no_dct_decimate", "0", X264_OPT_BOOL}, {x264_cabac_syns, "x264_cabac", "1", X264_OPT_BOOL}, {x264_aq_strength_syns, "x264_aq_strength", "1", X264_OPT_DOUBLE}, {x264_psy_syns, "x264_psy_rd", "1|0", X264_OPT_PSY}, {x264_psy_syns, "x264_psy_trell", "1|0", X264_OPT_PSY}, {x264_mbtree_syns, "x264_mbtree", "1", X264_OPT_BOOL_NONE}, }; #define X264_OPT_MAP_SIZE (sizeof(x264_opt_map)/sizeof(struct x264_opt_map_s)) static const gchar* x264_opt_get_default(const gchar *opt) { gint jj; for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++) { if (find_syn_match(opt, x264_opt_map[jj].opt_syns) >= 0) { return x264_opt_map[jj].def_val; } } return ""; } static void x264_update_double(signal_user_data_t *ud, const gchar *name, const gchar *val) { gdouble dval; if (val == NULL) return; dval = g_strtod (val, NULL); ghb_settings_set_double(ud->x264_priv, name, dval); } static void x264_update_int(signal_user_data_t *ud, const gchar *name, const gchar *val) { gint ival; if (val == NULL) return; ival = g_strtod (val, NULL); ghb_settings_set_int(ud->x264_priv, name, ival); } static void x264_update_int_setting(signal_user_data_t *ud, const gchar *name, const gchar *val) { gint ival; if (val == NULL) return; ival = g_strtod (val, NULL); ghb_settings_set_value(ud->x264_priv, name, ghb_int64_value(ival)); ghb_check_dependency(ud, NULL, name); } static gchar *true_str[] = { "true", "yes", "1", NULL }; static gboolean str_is_true(const gchar *str) { gint ii; for (ii = 0; true_str[ii]; ii++) { if (g_ascii_strcasecmp(str, true_str[ii]) == 0) return TRUE; } return FALSE; } static void x264_update_bool(signal_user_data_t *ud, const gchar *name, const gchar *val) { if (val == NULL) ghb_settings_set_boolean(ud->x264_priv, name, TRUE); else ghb_settings_set_boolean(ud->x264_priv, name, str_is_true(val)); } static void x264_update_bool_setting(signal_user_data_t *ud, const gchar *name, const gchar *val) { if (val == NULL) ghb_settings_set_boolean(ud->x264_priv, name, TRUE); else ghb_settings_set_boolean(ud->x264_priv, name, str_is_true(val)); ghb_check_dependency(ud, NULL, name); } static void x264_update_combo(signal_user_data_t *ud, const gchar *name, const gchar *val) { ghb_settings_set_string(ud->x264_priv, name, val); } static void x264_update_deblock(signal_user_data_t *ud, const gchar *xval) { int avalue, bvalue; gchar *end; gchar *val; gchar *bval = NULL; if (xval == NULL) return; val = g_strdup(xval); bvalue = avalue = 0; if (val != NULL) { gchar *pos = strchr(val, ','); if (pos != NULL) { bval = pos + 1; *pos = 0; } avalue = (int)g_strtod(val, &end); if (bval != NULL) { bvalue = (int)g_strtod(bval, &end); } } g_free(val); ghb_settings_set_int(ud->x264_priv, "x264_deblock_alpha", avalue); ghb_settings_set_int(ud->x264_priv, "x264_deblock_beta", bvalue); } static void x264_parse_psy(const gchar *psy, gdouble *psy_rd, gdouble *psy_trell) { *psy_rd = 0.; *psy_trell = 0.; if (psy == NULL) return; if (2 == sscanf(psy, "%lf|%lf", psy_rd, psy_trell) || 2 == sscanf(psy, "%lf,%lf", psy_rd, psy_trell)) { } } static void x264_update_psy(signal_user_data_t *ud, const gchar *xval) { gdouble rd_value, trell_value; if (xval == NULL) return; x264_parse_psy(xval, &rd_value, &trell_value); ghb_settings_set_double(ud->x264_priv, "x264_psy_rd", rd_value); ghb_settings_set_double(ud->x264_priv, "x264_psy_trell", trell_value); } static void do_update(signal_user_data_t *ud, char *name, gint type, char *val) { switch(type) { case X264_OPT_INT: x264_update_int(ud, name, val); break; case X264_OPT_DOUBLE: x264_update_double(ud, name, val); break; case X264_OPT_BOOL: x264_update_bool(ud, name, val); break; case X264_OPT_COMBO: x264_update_combo(ud, name, val); break; case X264_OPT_BOOL_NONE: x264_update_bool_setting(ud, name, val); break; case X264_OPT_INT_NONE: x264_update_int_setting(ud, name, val); break; } } void ghb_x264_parse_options(signal_user_data_t *ud, const gchar *options) { gchar **split = g_strsplit(options, ":", -1); if (split == NULL) return; gint ii; gint jj; for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++) x264_opt_map[jj].found = FALSE; for (ii = 0; split[ii] != NULL; ii++) { gchar *val = NULL; gchar *pos = strchr(split[ii], '='); if (pos != NULL) { val = pos + 1; *pos = 0; } for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++) { if (find_syn_match(split[ii], x264_opt_map[jj].opt_syns) >= 0) { x264_opt_map[jj].found = TRUE; switch(x264_opt_map[jj].type) { case X264_OPT_INT: x264_update_int(ud, x264_opt_map[jj].name, val); break; case X264_OPT_DOUBLE: x264_update_double(ud, x264_opt_map[jj].name, val); break; case X264_OPT_BOOL: x264_update_bool(ud, x264_opt_map[jj].name, val); break; case X264_OPT_COMBO: x264_update_combo(ud, x264_opt_map[jj].name, val); break; case X264_OPT_DEBLOCK: // dirty little hack. mark deblock_beta found as well x264_opt_map[jj+1].found = TRUE; x264_update_deblock(ud, val); break; case X264_OPT_PSY: // dirty little hack. mark psy_trell found as well x264_opt_map[jj+1].found = TRUE; x264_update_psy(ud, val); break; case X264_OPT_BOOL_NONE: x264_update_bool_setting(ud, x264_opt_map[jj].name, val); break; case X264_OPT_INT_NONE: x264_update_int_setting(ud, x264_opt_map[jj].name, val); break; case X264_OPT_TRANS: if (x264_opt_map[jj].translation == NULL) break; val = trans_x264_val(x264_opt_map[jj].translation, val); if (val != NULL) { do_update(ud, x264_opt_map[jj].name, x264_opt_map[jj].translation->ui_type, val); // TODO un-grey the ui control } else { // TODO grey out the ui control } break; } break; } } } // For any options not found in the option string, set ui to // default values for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++) { if (!x264_opt_map[jj].found) { gchar *val = strdup(x264_opt_map[jj].def_val); switch(x264_opt_map[jj].type) { case X264_OPT_INT: x264_update_int(ud, x264_opt_map[jj].name, val); break; case X264_OPT_DOUBLE: x264_update_double(ud, x264_opt_map[jj].name, val); break; case X264_OPT_BOOL: x264_update_bool(ud, x264_opt_map[jj].name, val); break; case X264_OPT_COMBO: x264_update_combo(ud, x264_opt_map[jj].name, val); break; case X264_OPT_DEBLOCK: x264_update_deblock(ud, val); break; case X264_OPT_PSY: x264_update_psy(ud, val); break; case X264_OPT_BOOL_NONE: x264_update_bool_setting(ud, x264_opt_map[jj].name, val); break; case X264_OPT_INT_NONE: x264_update_int_setting(ud, x264_opt_map[jj].name, val); break; case X264_OPT_TRANS: if (x264_opt_map[jj].translation == NULL) break; val = g_strdup(trans_x264_val(x264_opt_map[jj].translation, val)); if (val != NULL) { do_update(ud, x264_opt_map[jj].name, x264_opt_map[jj].translation->ui_type, val); // TODO un-grey the ui control } else { // TODO grey out the ui control } break; } x264_opt_map[jj].found = TRUE; g_free(val); } } g_strfreev(split); ghb_settings_to_ui(ud, ud->x264_priv); } gchar* get_deblock_val(signal_user_data_t *ud) { gchar *alpha, *beta; gchar *result; alpha = ghb_settings_get_string(ud->x264_priv, "x264_deblock_alpha"); beta = ghb_settings_get_string(ud->x264_priv, "x264_deblock_beta"); result = g_strdup_printf("%s,%s", alpha, beta); g_free(alpha); g_free(beta); return result; } gchar* get_psy_val(signal_user_data_t *ud) { gdouble rd, trell; gchar *result; rd = ghb_settings_get_double(ud->x264_priv, "x264_psy_rd"); trell = ghb_settings_get_double(ud->x264_priv, "x264_psy_trell"); result = g_strdup_printf("%g|%g", rd, trell); return result; } static void x264_opt_update(signal_user_data_t *ud, GtkWidget *widget) { gint jj; const gchar *name = ghb_get_setting_key(widget); gchar **opt_syns = NULL; const gchar *def_val = NULL; gint type; trans_table_t *trans; for (jj = 0; jj < X264_OPT_MAP_SIZE; jj++) { if (strcmp(name, x264_opt_map[jj].name) == 0) { // found the options that needs updating opt_syns = x264_opt_map[jj].opt_syns; def_val = x264_opt_map[jj].def_val; type = x264_opt_map[jj].type; trans = x264_opt_map[jj].translation; break; } } if (opt_syns != NULL) { GString *x264opts = g_string_new(""); gchar *options; gchar **split = NULL; gint ii; gboolean foundit = FALSE; options = ghb_settings_get_string(ud->settings, "x264Option"); if (options) { split = g_strsplit(options, ":", -1); g_free(options); } for (ii = 0; split && split[ii] != NULL; ii++) { gint syn; gchar *val = NULL; gchar *pos = strchr(split[ii], '='); if (pos != NULL) { val = pos + 1; *pos = 0; } syn = find_syn_match(split[ii], opt_syns); if (syn >= 0) { // Updating this option gchar *val; foundit = TRUE; if (type == X264_OPT_DEBLOCK) val = get_deblock_val(ud); else if (type == X264_OPT_PSY) val = get_psy_val(ud); else { GValue *gval; gval = ghb_widget_value(widget); if (G_VALUE_TYPE(gval) == G_TYPE_BOOLEAN) { if (ghb_value_boolean(gval)) val = g_strdup("1"); else val = g_strdup("0"); } else { val = ghb_widget_string(widget); } ghb_value_free(gval); } if (type == X264_OPT_TRANS) { gchar *tmp; tmp = g_strdup(trans_ui_val(trans, val)); if (tmp) { g_free(val); val = tmp; } } if (strcmp(def_val, val) != 0) { g_string_append_printf(x264opts, "%s=%s:", opt_syns[syn], val); } g_free(val); } else if (val != NULL) g_string_append_printf(x264opts, "%s=%s:", split[ii], val); else g_string_append_printf(x264opts, "%s:", split[ii]); } if (split) g_strfreev(split); if (!foundit) { gchar *val; if (type == X264_OPT_DEBLOCK) val = get_deblock_val(ud); else if (type == X264_OPT_PSY) val = get_psy_val(ud); else { GValue *gval; gval = ghb_widget_value(widget); if (G_VALUE_TYPE(gval) == G_TYPE_BOOLEAN) { if (ghb_value_boolean(gval)) val = g_strdup("1"); else val = g_strdup("0"); } else { val = ghb_widget_string(widget); } ghb_value_free(gval); } if (type == X264_OPT_TRANS) { gchar *tmp; tmp = g_strdup(trans_ui_val(trans, val)); if (tmp) { g_free(val); val = tmp; } } if (strcmp(def_val, val) != 0) { g_string_append_printf(x264opts, "%s=%s:", opt_syns[0], val); } g_free(val); } // Update the options value // strip the trailing ":" gchar *result; gint len; result = g_string_free(x264opts, FALSE); len = strlen(result); if (len > 0) result[len - 1] = 0; gchar *sopts; sopts = sanitize_x264opts(ud, result); ghb_update_x264Option(ud, sopts); ghb_x264_parse_options(ud, sopts); g_free(sopts); g_free(result); } } static gint x264_find_opt(gchar **opts, gchar **opt_syns) { gint ii; for (ii = 0; opts[ii] != NULL; ii++) { gchar *opt; opt = g_strdup(opts[ii]); gchar *pos = strchr(opt, '='); if (pos != NULL) { *pos = 0; } if (find_syn_match(opt, opt_syns) >= 0) { g_free(opt); return ii; } g_free(opt); } return -1; } static void x264_remove_opt(gchar **opts, gchar **opt_syns) { gint ii; for (ii = 0; opts[ii] != NULL; ii++) { gchar *opt; opt = g_strdup(opts[ii]); gchar *pos = strchr(opt, '='); if (pos != NULL) { *pos = 0; } if (find_syn_match(opt, opt_syns) >= 0) { // Mark as deleted opts[ii][0] = 0; } g_free(opt); } } static gchar* x264_lookup_value(gchar **opts, gchar **opt_syns) { gchar *ret = NULL; gint pos; const gchar *def_val = x264_opt_get_default(opt_syns[0]); pos = x264_find_opt(opts, opt_syns); if (pos >= 0) { gchar *cpos = strchr(opts[pos], '='); if (cpos != NULL) { ret = g_strdup(cpos+1); } else { ret = g_strdup(""); } } else if (def_val != NULL) { ret = g_strdup(def_val); } return ret; } gint ghb_lookup_badapt(const gchar *options) { gint ret = 0; gchar *result; gchar **split; if (options == NULL) options = ""; split = g_strsplit(options, ":", -1); result = x264_lookup_value(split, x264_badapt_syns); g_strfreev(split); if (result != NULL) { ret = g_strtod(result, NULL); g_free(result); } return ret; } gint ghb_lookup_aqmode(const gchar *options) { gint ret = 0; gchar *result; gchar **split; if (options == NULL) options = ""; split = g_strsplit(options, ":", -1); result = x264_lookup_value(split, x264_aqmode_syns); g_strfreev(split); if (result != NULL) { ret = g_strtod(result, NULL); g_free(result); } return ret; } gint ghb_lookup_bframes(const gchar *options) { gint ret = 0; gchar *result; gchar **split; if (options == NULL) options = ""; split = g_strsplit(options, ":", -1); result = x264_lookup_value(split, x264_bframes_syns); g_strfreev(split); if (result != NULL) { ret = g_strtod(result, NULL); g_free(result); } return ret; } gint ghb_lookup_mbtree(const gchar *options) { gint ret = ghb_lookup_bframes(options) != 0; gchar *result; gchar **split; if (options == NULL) options = ""; split = g_strsplit(options, ":", -1); result = x264_lookup_value(split, x264_mbtree_syns); g_strfreev(split); if (result != NULL) { ret = g_strtod(result, NULL); g_free(result); } return ret; } // Construct the x264 options string // The result is allocated, so someone must free it at some point. static gchar* sanitize_x264opts(signal_user_data_t *ud, const gchar *options) { GString *x264opts = g_string_new(""); gchar **split = g_strsplit(options, ":", -1); gint ii; // Fix up option dependencies gint subme = ghb_settings_combo_int(ud->x264_priv, "x264_subme"); if (subme < 6) { x264_remove_opt(split, x264_psy_syns); } gint trell = ghb_settings_combo_int(ud->x264_priv, "x264_trellis"); if (subme >= 10) { gint aqmode = ghb_lookup_aqmode(options); if (trell != 2 || aqmode == 0) { gint pos = x264_find_opt(split, x264_subme_syns); g_free(split[pos]); split[pos] = g_strdup_printf("subme=9"); } } if (trell < 1) { gint psy; gdouble psy_rd = 0., psy_trell; psy = x264_find_opt(split, x264_psy_syns); if (psy >= 0) { gchar *pos = strchr(split[psy], '='); if (pos != NULL) { x264_parse_psy(pos+1, &psy_rd, &psy_trell); } g_free(split[psy]); split[psy] = g_strdup_printf("psy-rd=%g|0", psy_rd); } } gint bframes = ghb_settings_get_int(ud->x264_priv, "x264_bframes"); if (bframes == 0) { x264_remove_opt(split, x264_direct_syns); x264_remove_opt(split, x264_badapt_syns); } if (bframes <= 1) { x264_remove_opt(split, x264_bpyramid_syns); } // Remove entries that match the defaults for (ii = 0; split[ii] != NULL; ii++) { gchar *val = NULL; gchar *opt = g_strdup(split[ii]); gchar *pos = strchr(opt, '='); if (pos != NULL) { val = pos + 1; *pos = 0; } else { val = "1"; } const gchar *def_val; def_val = x264_opt_get_default(opt); if (strcmp(val, def_val) == 0) { // Matches the default, so remove it split[ii][0] = 0; } g_free(opt); } for (ii = 0; split[ii] != NULL; ii++) { if (split[ii][0] != 0) g_string_append_printf(x264opts, "%s:", split[ii]); } g_strfreev(split); // strip the trailing ":" gchar *result; gint len; result = g_string_free(x264opts, FALSE); len = strlen(result); if (len > 0) result[len - 1] = 0; return result; } void ghb_x264_init(signal_user_data_t *ud) { ud->x264_priv = ghb_settings_new(); } gboolean ghb_background_refresh_x264Option(signal_user_data_t *ud) { const char *opt; opt = ghb_settings_get_const_string(ud->settings, "x264Option"); GtkWidget *widget; GtkTextBuffer *buffer; GtkTextIter start, end; gchar *str; widget = GHB_WIDGET(ud->builder, "x264Option"); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); gtk_text_buffer_get_bounds(buffer, &start, &end); str = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); // If the value has changed, then update it if (opt != NULL) { if (str == NULL || strcmp(str, opt)) { ud->dont_clear_presets = 1; ghb_ui_update(ud, "x264Option", ghb_string_value(opt)); ud->dont_clear_presets = 0; } } free(str); return FALSE; } void ghb_update_x264Option(signal_user_data_t *ud, const char *opt) { ghb_settings_set_string(ud->settings, "x264Option", opt); g_idle_add((GSourceFunc)ghb_background_refresh_x264Option, ud); } HandBrake-0.10.2/gtk/src/ghbcompat.h0000664000175200017520000000221212303157614017527 0ustar handbrakehandbrake#if !defined(_GHB_COMPAT_H_) #define _GHB_COMPAT_H_ #include #include static inline PangoFontDescription* ghb_widget_get_font(GtkWidget *widget) { PangoFontDescription *font = NULL; #if GTK_CHECK_VERSION(3, 0, 0) GtkStyleContext *style; style = gtk_widget_get_style_context(widget); #if GTK_CHECK_VERSION(3, 8, 0) gtk_style_context_get(style, GTK_STATE_FLAG_NORMAL, "font", &font, NULL); #else font = gtk_style_context_get_font(style, GTK_STATE_FLAG_NORMAL); #endif #endif return font; } #if !GTK_CHECK_VERSION(3, 10, 0) #define gtk_image_set_from_icon_name gtk_image_set_from_stock #define GHB_PLAY_ICON "gtk-media-play" #define GHB_PAUSE_ICON "gtk-media-pause" #else #define GHB_PLAY_ICON "media-playback-start" #define GHB_PAUSE_ICON "media-playback-pause" #endif #if !GTK_CHECK_VERSION(3, 10, 0) #define GHB_STOCK_OPEN GTK_STOCK_OPEN #define GHB_STOCK_CANCEL GTK_STOCK_CANCEL #define GHB_STOCK_SAVE GTK_STOCK_SAVE #else #define GHB_STOCK_OPEN "_Open" #define GHB_STOCK_CANCEL "_Cancel" #define GHB_STOCK_SAVE "_Save" #endif #endif // _GHB_COMPAT_H_ HandBrake-0.10.2/gtk/src/ghb.desktop0000664000175200017520000000122712412555662017560 0ustar handbrakehandbrake[Desktop Entry] Name=HandBrake GenericName=Media Transcoder Comment=Transcodes DVD, Bluray, and other media Exec=ghb %f Icon=hb-icon Terminal=false Type=Application Categories=GTK;AudioVideo;Video; MimeType=application/ogg;application/x-extension-mp4;application/x-flac;application/x-matroska;application/x-ogg;audio/ac3;audio/mp4;audio/mpeg;audio/ogg;audio/x-flac;audio/x-matroska;audio/x-mp3;audio/x-mpeg;audio/x-vorbis;video/mp4;video/mp4v-es;video/mpeg;video/msvideo;video/quicktime;video/vnd.divx;video/x-avi;video/x-m4v;video/x-matroska;video/x-mpeg;video/ogg;video/x-ogm+ogg;video/x-theora+ogg;x-content/video-dvd;x-content/video-vcd;x-content/video-svcd; HandBrake-0.10.2/gtk/src/values.c0000664000175200017520000004314412465416500017067 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * presets.c * Copyright (C) John Stebbins 2008-2015 * * presets.c is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * */ #include #include #include #include #include #include "values.h" static void dict_delete_key(gpointer data); static void dict_delete_value(gpointer data); GValue* ghb_value_new(GType gtype) { GValue *gval = g_malloc0(sizeof(GValue)); g_value_init(gval, gtype); return gval; } void ghb_value_free(GValue *gval) { if (gval == NULL) return; g_value_unset(gval); g_free(gval); } GValue* ghb_value_dup(const GValue *val) { if (val == NULL) return NULL; GValue *copy = ghb_value_new(G_VALUE_TYPE(val)); g_value_copy(val, copy); return copy; } void debug_show_type(GType tp) { const gchar *str = "unknown"; if (tp == G_TYPE_STRING) { str ="string"; } else if (tp == G_TYPE_INT) { str ="int"; } else if (tp == G_TYPE_INT64) { str ="int64"; } else if (tp == G_TYPE_DOUBLE) { str ="double"; } else if (tp == G_TYPE_BOOLEAN) { str ="bool"; } else if (tp == ghb_array_get_type()) { str ="array"; } else if (tp == ghb_dict_get_type()) { str ="dict"; } g_debug("Type %s", str); } void debug_show_value(GValue *gval) { GType tp; tp = G_VALUE_TYPE(gval); if (tp == G_TYPE_STRING) { g_message("Type %s value %s", "string", g_value_get_string(gval)); } else if (tp == G_TYPE_INT) { g_message("Type %s value %d", "int", g_value_get_int(gval)); } else if (tp == G_TYPE_INT64) { g_message("Type %s value %" PRId64, "int64", g_value_get_int64(gval)); } else if (tp == G_TYPE_DOUBLE) { g_message("Type %s value %f", "double", g_value_get_double(gval)); } else if (tp == G_TYPE_BOOLEAN) { g_message("Type %s value %d", "boolean", g_value_get_boolean(gval)); } else if (tp == ghb_array_get_type()) { g_message("Type %s", "boolean"); } else if (tp == ghb_dict_get_type()) { g_message("Type %s", "dict"); } } gint ghb_value_int(const GValue *val) { gint result; if (val == NULL) return 0; GValue xform = {0,}; if (G_VALUE_TYPE(val) != G_TYPE_INT) { g_value_init(&xform, G_TYPE_INT); if (!g_value_transform(val, &xform)) { debug_show_type(G_VALUE_TYPE(val)); g_warning("int can't transform"); return 0; } result = g_value_get_int(&xform); g_value_unset(&xform); } else { result = g_value_get_int(val); } return result; } gint64 ghb_value_int64(const GValue *val) { gint64 result; if (val == NULL) return 0; GValue xform = {0,}; if (G_VALUE_TYPE(val) != G_TYPE_INT64) { g_value_init(&xform, G_TYPE_INT64); if (!g_value_transform(val, &xform)) { debug_show_type(G_VALUE_TYPE(val)); g_warning("int64 can't transform"); return 0; } result = g_value_get_int64(&xform); g_value_unset(&xform); } else { result = g_value_get_int64(val); } return result; } gdouble ghb_value_double(const GValue *val) { gdouble result; if (val == NULL) return 0; GValue xform = {0,}; if (G_VALUE_TYPE(val) != G_TYPE_DOUBLE) { g_value_init(&xform, G_TYPE_DOUBLE); if (!g_value_transform(val, &xform)) { debug_show_type(G_VALUE_TYPE(val)); g_warning("double can't transform"); return 0; } result = g_value_get_double(&xform); g_value_unset(&xform); } else { result = g_value_get_double(val); } return result; } gchar* ghb_value_string(const GValue *val) { gchar *result; if (val == NULL) return 0; GValue xform = {0,}; if (G_VALUE_TYPE(val) != G_TYPE_STRING) { g_value_init(&xform, G_TYPE_STRING); if (!g_value_transform(val, &xform)) { debug_show_type(G_VALUE_TYPE(val)); g_warning("string can't transform"); return NULL; } result = g_strdup(g_value_get_string(&xform)); g_value_unset(&xform); } else { result = g_strdup(g_value_get_string(val)); } return result; } gboolean ghb_value_boolean(const GValue *val) { gboolean result; if (val == NULL) return FALSE; GValue xform = {0,}; if (G_VALUE_TYPE(val) != G_TYPE_BOOLEAN) { g_value_init(&xform, G_TYPE_BOOLEAN); if (!g_value_transform(val, &xform)) { debug_show_type(G_VALUE_TYPE(val)); g_warning("boolean can't transform"); return FALSE; } result = g_value_get_boolean(&xform); g_value_unset(&xform); } else { result = g_value_get_boolean(val); } return result; } gint ghb_value_cmp(const GValue *vala, const GValue *valb) { GType typa; GType typb; if ((vala == NULL && valb != NULL) || (vala != NULL && valb == NULL)) { return 1; } typa = G_VALUE_TYPE(vala); typb = G_VALUE_TYPE(valb); if (typa != typb) { return 1; } if (typa == G_TYPE_STRING) { char *stra, *strb; gint res; stra = ghb_value_string(vala); strb = ghb_value_string(valb); if (stra == NULL && strb == NULL) return 0; if (stra == NULL) { g_free(strb); return -1; } if (strb == NULL) { g_free(stra); return 1; } res = strcmp(stra, strb); g_free(stra); g_free(strb); return res; } else if (typa == G_TYPE_INT64 || typa == G_TYPE_INT || typa == G_TYPE_BOOLEAN) { return ghb_value_int64(vala) - ghb_value_int64(valb); } else if (typa == G_TYPE_DOUBLE || typa == G_TYPE_FLOAT) { return ghb_value_double(vala) - ghb_value_double(valb); } else if (typa == ghb_array_get_type()) { // Cheating here. Just assume they are different. // Maybe later I'll recurse through these return 1; } else if (typa == ghb_dict_get_type()) { // Cheating here. Just assume they are different. // Maybe later I'll recurse through these return 1; } else { g_warning("ghb_value_cmp: unrecognized type"); return 1; } return 0; } GValue* ghb_string_value(const gchar *str) { static GValue gval = {0,}; if (!G_IS_VALUE(&gval)) g_value_init(&gval, G_TYPE_STRING); if (str == NULL) { g_value_set_string(&gval, ""); } else { g_value_set_string(&gval, str); } return &gval; } GValue* ghb_int64_value(gint64 ival) { static GValue gval = {0,}; if (!G_IS_VALUE(&gval)) g_value_init(&gval, G_TYPE_INT64); g_value_set_int64(&gval, ival); return &gval; } GValue* ghb_int_value(gint ival) { static GValue gval = {0,}; if (!G_IS_VALUE(&gval)) g_value_init(&gval, G_TYPE_INT64); g_value_set_int64(&gval, (gint64)ival); return &gval; } GValue* ghb_double_value(gdouble dval) { static GValue gval = {0,}; if (!G_IS_VALUE(&gval)) g_value_init(&gval, G_TYPE_DOUBLE); g_value_set_double(&gval, dval); return &gval; } GValue* ghb_boolean_value(gboolean bval) { static GValue gval = {0,}; if (!G_IS_VALUE(&gval)) g_value_init(&gval, G_TYPE_BOOLEAN); g_value_set_boolean(&gval, bval); return &gval; } GValue* ghb_string_value_new(const gchar *str) { if (str == NULL) str = ""; GValue *gval = ghb_value_new(G_TYPE_STRING); g_value_set_string(gval, str); return gval; } GValue* ghb_int64_value_new(gint64 ival) { GValue *gval = ghb_value_new(G_TYPE_INT64); g_value_set_int64(gval, ival); return gval; } GValue* ghb_int_value_new(gint ival) { GValue *gval = ghb_value_new(G_TYPE_INT64); g_value_set_int64(gval, ival); return gval; } GValue* ghb_double_value_new(gdouble dval) { GValue *gval = ghb_value_new(G_TYPE_DOUBLE); g_value_set_double(gval, dval); return gval; } GValue* ghb_boolean_value_new(gboolean bval) { GValue *gval = ghb_value_new(G_TYPE_BOOLEAN); g_value_set_boolean(gval, bval); return gval; } GValue* ghb_dict_value_new() { GHashTable *dict; GValue *gval = ghb_value_new(ghb_dict_get_type()); dict = g_hash_table_new_full(g_str_hash, g_str_equal, dict_delete_key, dict_delete_value); g_value_take_boxed(gval, dict); return gval; } GValue* ghb_array_value_new(guint size) { GValue *gval = ghb_value_new(ghb_array_get_type()); GArray *array; array = g_array_sized_new(FALSE, FALSE, sizeof(GValue*), size); g_value_take_boxed(gval, array); return gval; } void ghb_array_value_reset(GValue *gval, guint size) { GArray *array; g_value_reset(gval); array = g_array_sized_new(FALSE, FALSE, sizeof(GValue*), size); g_value_take_boxed(gval, array); } GValue* ghb_date_value_new(GDate *date) { GValue *gval = ghb_value_new(g_date_get_type()); g_value_set_boxed(gval, date); return gval; } GValue* ghb_rawdata_value_new(ghb_rawdata_t *data) { GValue *gval = ghb_value_new(ghb_rawdata_get_type()); g_value_take_boxed(gval, data); return gval; } static gpointer rawdata_copy(gpointer boxed) { const ghb_rawdata_t *data = (const ghb_rawdata_t*)boxed; ghb_rawdata_t *copy = g_malloc(sizeof(ghb_rawdata_t)); copy->size = data->size; if (data->data) { copy->data = g_malloc(data->size); memcpy(copy->data, data->data, data->size); } else { copy->data = NULL; copy->size = 0; } return copy; } static void rawdata_free(gpointer boxed) { ghb_rawdata_t *data = (ghb_rawdata_t*)boxed; if (data->data) g_free(data->data); g_free(data); } GType ghb_rawdata_get_type(void) { static GType type_id = 0; if (!type_id) type_id = g_boxed_type_register_static(g_intern_static_string("GHBData"), (GBoxedCopyFunc) rawdata_copy, (GBoxedFreeFunc) rawdata_free); return type_id; } static void dict_delete_key(gpointer data) { if (data == NULL) { g_warning("dict frees null key"); return; } g_free(data); } static void dict_delete_value(gpointer data) { GValue *gval = (GValue*)data; if (gval == NULL) { g_warning("dict frees null value"); return; } ghb_value_free(gval); } static gpointer dict_copy(gpointer boxed) { GHashTable *dict = (GHashTable*)boxed; GHashTable *copy; GHashTableIter iter; gchar *key; GValue *gval; copy = g_hash_table_new_full(g_str_hash, g_str_equal, dict_delete_key, dict_delete_value); g_hash_table_iter_init(&iter, dict); // middle (void*) cast prevents gcc warning "defreferencing type-punned // pointer will break strict-aliasing rules" while (g_hash_table_iter_next( &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval)) { g_hash_table_insert(copy, g_strdup(key), ghb_value_dup(gval)); } return copy; } static void dict_free(gpointer boxed) { GHashTable *dict = (GHashTable*)boxed; g_hash_table_destroy(dict); } GType ghb_dict_get_type(void) { static GType type_id = 0; if (!type_id) type_id = g_boxed_type_register_static( g_intern_static_string("GHBDict"), (GBoxedCopyFunc) dict_copy, (GBoxedFreeFunc) dict_free); return type_id; } void ghb_dict_insert(GValue *gval, gchar *key, GValue *val) { GHashTable *dict = g_value_get_boxed(gval); g_hash_table_insert(dict, key, val); } void ghb_dict_iter_init(GHashTableIter *iter, GValue *gval) { GHashTable *dict = g_value_get_boxed(gval); g_hash_table_iter_init(iter, dict); } GValue* ghb_dict_lookup(const GValue *gval, const gchar *key) { GHashTable *dict = g_value_get_boxed(gval); return g_hash_table_lookup(dict, key); } gboolean ghb_dict_remove(GValue *gval, const gchar *key) { GHashTable *dict = g_value_get_boxed(gval); return g_hash_table_remove(dict, key); } static gpointer array_copy(gpointer boxed) { const GArray *array = (const GArray*)boxed; GArray *copy = g_array_new(FALSE, FALSE, sizeof(GValue*)); GValue *gval, *gval_copy; gint ii; for (ii = 0; ii < array->len; ii++) { gval = g_array_index(array, GValue*, ii); if (gval) { gval_copy = ghb_value_dup(gval); g_array_append_val(copy, gval_copy); } } return copy; } static void array_free(gpointer boxed) { GArray *array = (GArray*)boxed; GValue *gval; gint ii; for (ii = 0; ii < array->len; ii++) { gval = g_array_index(array, GValue*, ii); if (gval) { ghb_value_free(gval); } } g_array_free(array, TRUE); } GType ghb_array_get_type(void) { static GType type_id = 0; if (!type_id) type_id = g_boxed_type_register_static( g_intern_static_string("GHBArray"), (GBoxedCopyFunc) array_copy, (GBoxedFreeFunc) array_free); return type_id; } GValue* ghb_array_get_nth(const GValue *gval, gint ii) { GArray *arr = g_value_get_boxed(gval); return g_array_index(arr, GValue*, ii); } void ghb_array_insert(GValue *gval, guint ii, GValue *val) { GArray *arr = g_value_get_boxed(gval); // A little nastyness here. The array pointer // can change when the array changes size. So // I must re-box it in the GValue each time. arr = g_array_insert_val(arr, ii, val); memset(gval, 0, sizeof(GValue)); g_value_init(gval, ghb_array_get_type()); g_value_take_boxed(gval, arr); } void ghb_array_append(GValue *gval, GValue *val) { GArray *arr = g_value_get_boxed(gval); // A little nastyness here. The array pointer // can change when the array changes size. So // I must re-box it in the GValue each time. arr = g_array_append_val(arr, val); memset(gval, 0, sizeof(GValue)); g_value_init(gval, ghb_array_get_type()); g_value_take_boxed(gval, arr); } void ghb_array_remove(GValue *gval, guint ii) { GArray *arr = g_value_get_boxed(gval); // A little nastyness here. The array pointer // can change when the array changes size. So // I must re-box it in the GValue each time. arr = g_array_remove_index(arr, ii); memset(gval, 0, sizeof(GValue)); g_value_init(gval, ghb_array_get_type()); g_value_take_boxed(gval, arr); } void ghb_array_replace(GValue *gval, guint ii, GValue *val) { GArray *arr = g_value_get_boxed(gval); // A little nastyness here. The array pointer // can change when the array changes size. So // I must re-box it in the GValue each time. if (ii >= arr->len) return; ghb_value_free(((GValue**)arr->data)[ii]); ((GValue**)arr->data)[ii] = val; } void ghb_array_copy(GValue *arr1, GValue *arr2, gint count) { gint len, ii; // empty the first array if it is not already empty len = ghb_array_len(arr1); for (ii = 0; ii < len; ii++) { GValue *old = ghb_array_get_nth(arr1, 0); ghb_array_remove(arr1, 0); ghb_value_free(old); } len = ghb_array_len(arr2); count = MIN(count, len); for (ii = 0; ii < count; ii++) ghb_array_append(arr1, ghb_value_dup(ghb_array_get_nth(arr2, ii))); } gint ghb_array_len(const GValue *gval) { if (gval == NULL) return 0; GArray *arr = g_value_get_boxed(gval); return arr->len; } static void xform_string_int(const GValue *sval, GValue *ival) { gchar *end; const gchar *str = g_value_get_string(sval); gint val = g_strtod(str, &end); if (*end) val = (guint)(~0)>>1; g_value_set_int(ival, val); } static void xform_string_int64(const GValue *sval, GValue *ival) { gchar *end; const gchar *str = g_value_get_string(sval); gint64 val = g_strtod(str, &end); if (*end) val = (guint64)(~0L)>>1; g_value_set_int64(ival, val); } static void xform_string_double(const GValue *sval, GValue *dval) { const gchar *str = g_value_get_string(sval); double val = g_strtod(str, NULL); g_value_set_double(dval, val); } static void xform_double_string(const GValue *dval, GValue *sval) { gchar *str; double val = g_value_get_double(dval); str = g_strdup_printf("%g", val); g_value_take_string(sval, str); } static void xform_boolean_double(const GValue *bval, GValue *dval) { gboolean b = g_value_get_boolean(bval); double val = b; g_value_set_double(dval, val); } void ghb_register_transforms() { g_value_register_transform_func(G_TYPE_STRING, G_TYPE_INT64, xform_string_int64); g_value_register_transform_func(G_TYPE_STRING, G_TYPE_INT, xform_string_int); g_value_register_transform_func(G_TYPE_STRING, G_TYPE_DOUBLE, xform_string_double); g_value_register_transform_func(G_TYPE_BOOLEAN, G_TYPE_DOUBLE, xform_boolean_double); g_value_register_transform_func(G_TYPE_DOUBLE, G_TYPE_STRING, xform_double_string); } HandBrake-0.10.2/gtk/src/hb-pause.svg0000664000175200017520000001277012311644756017660 0ustar handbrakehandbrake image/svg+xml HandBrake-0.10.2/gtk/src/hb-remove.svg0000664000175200017520000003140012311644756020027 0ustar handbrakehandbrake image/svg+xml HandBrake-0.10.2/gtk/src/quotestring.c0000664000175200017520000000330312067154516020151 0ustar handbrakehandbrake#include #include #include #define BUF_SIZE 72 void usage(char *cmd) { printf("%s\n", cmd); char *base = basename(cmd); fprintf(stderr, "Usage: %s infile [outfile]\n", base); } int main(int argc, char *argv[]) { FILE *infile, *outfile; char in_buffer[BUF_SIZE]; char out_buffer[2*BUF_SIZE]; if (argc < 2 || argc > 3) { usage(argv[0]); return 1; } infile = fopen(argv[1], "r"); if (argc < 3) { outfile = stdout; } else { outfile = fopen(argv[2], "w"); } while (fgets(in_buffer, BUF_SIZE, infile) != NULL) { int ii, jj; int len; int eol = 0; // Step on any CR LF at end of line len = strlen(in_buffer); if (len > 1 && in_buffer[len-1] == '\n' && in_buffer[len-2] == '\r') { in_buffer[len-1] = 0; in_buffer[len-2] = 0; eol = 1; } else if (len > 0 && in_buffer[len-1] == '\n') { in_buffer[len-1] = 0; eol = 1; } for (jj = 0, ii = 0; ii < len; ii++) { if (in_buffer[ii] == '"') { out_buffer[jj++] = '\\'; out_buffer[jj++] = in_buffer[ii]; } else if (in_buffer[ii] == '\r') { // Skip it } else { out_buffer[jj++] = in_buffer[ii]; } } out_buffer[jj] = 0; if (eol) fprintf(outfile, "\"%s\\n\"\n", out_buffer); else fprintf(outfile, "\"%s\"\n", out_buffer); } fclose(infile); fclose(outfile); return 0; } HandBrake-0.10.2/gtk/src/hb-showqueue.svg0000664000175200017520000001764312311644756020574 0ustar handbrakehandbrake image/svg+xml HandBrake-0.10.2/gtk/src/values.h0000664000175200017520000000533512300772602017070 0ustar handbrakehandbrake/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA */ #if !defined(_GHB_VALUES_H_) #define _GHB_VALUES_H_ #include #include typedef struct { guchar *data; gsize size; } ghb_rawdata_t; GType ghb_rawdata_get_type(void); GType ghb_array_get_type(void); GType ghb_dict_get_type(void); GValue* ghb_array_get_nth(const GValue *array, gint ii); void ghb_array_insert(GValue *gval, guint ii, GValue *val); void ghb_array_replace(GValue *gval, guint ii, GValue *val); void ghb_array_append(GValue *gval, GValue *val); void ghb_array_remove(GValue *gval, guint ii); gint ghb_array_len(const GValue *gval); void ghb_array_copy(GValue *arr1, GValue *arr2, gint count); void ghb_value_free(GValue *gval); GValue* ghb_value_new(GType gtype); GValue* ghb_value_dup(const GValue *val); gint ghb_value_int(const GValue *val); gint64 ghb_value_int64(const GValue *val); gdouble ghb_value_double(const GValue *val); gchar* ghb_value_string(const GValue *val); gboolean ghb_value_boolean(const GValue *val); GValue* ghb_string_value(const gchar *str); GValue* ghb_int64_value(gint64 ival); GValue* ghb_int_value(gint ival); GValue* ghb_double_value(gdouble dval); GValue* ghb_boolean_value(gboolean bval); gint ghb_value_cmp(const GValue *vala, const GValue *valb); GValue* ghb_string_value_new(const gchar *str); GValue* ghb_int64_value_new(gint64 ival); GValue* ghb_int_value_new(gint ival); GValue* ghb_double_value_new(gdouble dval); GValue* ghb_boolean_value_new(gboolean bval); GValue* ghb_dict_value_new(void); GValue* ghb_array_value_new(guint size); void ghb_array_value_reset(GValue *gval, guint size); GValue* ghb_date_value_new(GDate *date); GValue* ghb_rawdata_value_new(ghb_rawdata_t *data); void ghb_dict_insert(GValue *gval, gchar *key, GValue *val); void ghb_dict_iter_init(GHashTableIter *iter, GValue *gval); GValue* ghb_dict_lookup(const GValue *gval, const gchar *key); gboolean ghb_dict_remove(GValue *gval, const gchar *key); void ghb_register_transforms(void); void debug_show_value(GValue *gval); void debug_show_type(GType tp); #endif // _GHB_VALUES_H_ HandBrake-0.10.2/gtk/src/standard_presets.xml0000664000175200017520000013464612503035323021514 0ustar handbrakehandbrake ChildrenArray AudioAllowAACPass 1 AudioAllowAC3Pass 1 AudioAllowDTSHDPass 1 AudioAllowDTSPass 1 AudioAllowMP3Pass 1 AudioEncoderFallback AC3 (ffmpeg) AudioList AudioBitrate 160 AudioEncoder AAC (avcodec) AudioMixdown Dolby Pro Logic II AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 AudioBitrate 160 AudioEncoder AC3 Passthru AudioMixdown None AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 ChapterMarkers 1 Default 0 FileFormat MP4 file Folder Mp4HttpOptimize 0 Mp4LargeFile 0 Mp4iPodCompatible 0 PictureAutoCrop 1 PictureBottomCrop 0 PictureDeblock 0 PictureDecomb 0 PictureDecombCustom PictureDecombDeinterlace 1 PictureDeinterlace 0 PictureDeinterlaceCustom PictureDenoiseFilter off PictureDenoiseCustom PictureDetelecine 0 PictureDetelecineCustom PictureHeight 576 PictureKeepRatio 0 PictureLeftCrop 0 PictureModulus 2 PicturePAR 2 PictureRightCrop 0 PictureTopCrop 0 PictureWidth 720 PresetDescription HandBrake's settings for compatibility with all Apple devices (including the iPod 6G and later). Includes Dolby Digital audio for surround sound. PresetName Universal Subtitles None Type 0 UsesPictureFilters 1 UsesPictureSettings 1 VideoAvgBitrate 2500 VideoEncoder H.264 (x264) VideoFramerate 30 VideoFramerateMode pfr VideoGrayScale 0 VideoQualitySlider 20 VideoQualityType 2 VideoTurboTwoPass 0 VideoTwoPass 0 VideoLevel 3.0 VideoProfile baseline x264Option VideoOptionExtra VideoPreset fast VideoTune x264UseAdvancedOptions 0 AudioAllowAACPass 1 AudioAllowAC3Pass 1 AudioAllowDTSHDPass 1 AudioAllowDTSPass 1 AudioAllowMP3Pass 1 AudioEncoderFallback AC3 (ffmpeg) AudioList AudioBitrate 160 AudioEncoder AAC (avcodec) AudioMixdown Dolby Pro Logic II AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 ChapterMarkers 1 Default 0 FileFormat MP4 file Folder Mp4HttpOptimize 0 Mp4LargeFile 0 Mp4iPodCompatible 1 PictureAutoCrop 1 PictureBottomCrop 0 PictureDeblock 0 PictureDecomb 0 PictureDecombCustom PictureDecombDeinterlace 1 PictureDeinterlace 0 PictureDeinterlaceCustom PictureDenoiseFilter off PictureDenoiseCustom PictureDetelecine 0 PictureDetelecineCustom PictureHeight 240 PictureKeepRatio 1 PictureLeftCrop 0 PictureModulus 2 PicturePAR 0 PictureRightCrop 0 PictureTopCrop 0 PictureWidth 320 PresetDescription HandBrake's settings for playback on the iPod with Video (all generations). PresetName iPod Subtitles None Type 0 UsesPictureFilters 1 UsesPictureSettings 1 VideoAvgBitrate 2500 VideoEncoder H.264 (x264) VideoFramerate 30 VideoFramerateMode pfr VideoGrayScale 0 VideoQualitySlider 22 VideoQualityType 2 VideoTurboTwoPass 0 VideoTwoPass 0 VideoLevel 1.3 VideoProfile baseline x264Option VideoOptionExtra VideoPreset medium VideoTune x264UseAdvancedOptions 0 AudioAllowAACPass 1 AudioAllowAC3Pass 1 AudioAllowDTSHDPass 1 AudioAllowDTSPass 1 AudioAllowMP3Pass 1 AudioEncoderFallback AC3 (ffmpeg) AudioList AudioBitrate 160 AudioEncoder AAC (avcodec) AudioMixdown Dolby Pro Logic II AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 ChapterMarkers 1 Default 0 FileFormat MP4 file Folder Mp4HttpOptimize 0 Mp4LargeFile 1 Mp4iPodCompatible 0 PictureAutoCrop 1 PictureBottomCrop 0 PictureDeblock 0 PictureDecomb 0 PictureDecombCustom PictureDecombDeinterlace 1 PictureDeinterlace 0 PictureDeinterlaceCustom PictureDenoiseFilter off PictureDenoiseCustom PictureDetelecine 0 PictureDetelecineCustom PictureHeight 640 PictureKeepRatio 0 PictureLeftCrop 0 PictureModulus 2 PicturePAR 2 PictureRightCrop 0 PictureTopCrop 0 PictureWidth 960 PresetDescription HandBrake's settings for handheld iOS devices (iPhone 4, iPod touch 3G and later). PresetName iPhone & iPod touch Subtitles None Type 0 UsesPictureFilters 1 UsesPictureSettings 1 VideoAvgBitrate 2500 VideoEncoder H.264 (x264) VideoFramerate 30 VideoFramerateMode pfr VideoGrayScale 0 VideoQualitySlider 22 VideoQualityType 2 VideoTurboTwoPass 0 VideoTwoPass 0 VideoLevel 3.1 VideoProfile high x264Option VideoOptionExtra VideoPreset medium VideoTune x264UseAdvancedOptions 0 AudioAllowAACPass 1 AudioAllowAC3Pass 1 AudioAllowDTSHDPass 1 AudioAllowDTSPass 1 AudioAllowMP3Pass 1 AudioEncoderFallback AC3 (ffmpeg) AudioList AudioBitrate 160 AudioEncoder AAC (avcodec) AudioMixdown Dolby Pro Logic II AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 ChapterMarkers 1 Default 0 FileFormat MP4 file Folder Mp4HttpOptimize 0 Mp4LargeFile 1 Mp4iPodCompatible 0 PictureAutoCrop 1 PictureBottomCrop 0 PictureDeblock 0 PictureDecomb 0 PictureDecombCustom PictureDecombDeinterlace 1 PictureDeinterlace 0 PictureDeinterlaceCustom PictureDenoiseFilter off PictureDenoiseCustom PictureDetelecine 0 PictureDetelecineCustom PictureHeight 720 PictureKeepRatio 0 PictureLeftCrop 0 PictureModulus 2 PicturePAR 2 PictureRightCrop 0 PictureTopCrop 0 PictureWidth 1280 PresetDescription HandBrake's settings for playback on the iPad (all generations). PresetName iPad Subtitles None Type 0 UsesPictureFilters 1 UsesPictureSettings 1 VideoAvgBitrate 2500 VideoEncoder H.264 (x264) VideoFramerate 30 VideoFramerateMode pfr VideoGrayScale 0 VideoQualitySlider 20 VideoQualityType 2 VideoTurboTwoPass 0 VideoTwoPass 0 VideoLevel 3.1 VideoProfile high x264Option VideoOptionExtra VideoPreset medium VideoTune x264UseAdvancedOptions 0 AudioAllowAACPass 1 AudioAllowAC3Pass 1 AudioAllowDTSHDPass 1 AudioAllowDTSPass 1 AudioAllowMP3Pass 1 AudioEncoderFallback AC3 (ffmpeg) AudioList AudioBitrate 160 AudioEncoder AAC (avcodec) AudioMixdown Dolby Pro Logic II AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 AudioBitrate 160 AudioEncoder AC3 Passthru AudioMixdown None AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 ChapterMarkers 1 Default 0 FileFormat MP4 file Folder Mp4HttpOptimize 0 Mp4LargeFile 1 Mp4iPodCompatible 0 PictureAutoCrop 1 PictureBottomCrop 0 PictureDeblock 0 PictureDecomb 0 PictureDecombCustom PictureDecombDeinterlace 1 PictureDeinterlace 0 PictureDeinterlaceCustom PictureDenoiseFilter off PictureDenoiseCustom PictureDetelecine 0 PictureDetelecineCustom PictureHeight 720 PictureKeepRatio 0 PictureLeftCrop 0 PictureModulus 2 PicturePAR 2 PictureRightCrop 0 PictureTopCrop 0 PictureWidth 960 PresetDescription HandBrake's settings for the original AppleTV. Includes Dolby Digital audio for surround sound. Also compatible with iOS devices released since 2009. PresetName AppleTV Subtitles None Type 0 UsesPictureFilters 1 UsesPictureSettings 1 VideoAvgBitrate 2500 VideoEncoder H.264 (x264) VideoFramerate 30 VideoFramerateMode pfr VideoGrayScale 0 VideoQualitySlider 20 VideoQualityType 2 VideoTurboTwoPass 0 VideoTwoPass 0 VideoLevel 3.1 VideoProfile high x264Option VideoOptionExtra qpmin=4:cabac=0:ref=2:b-pyramid=none:weightb=0:weightp=0:vbv-maxrate=9500:vbv-bufsize=9500 VideoPreset medium VideoTune x264UseAdvancedOptions 0 AudioAllowAACPass 1 AudioAllowAC3Pass 1 AudioAllowDTSHDPass 1 AudioAllowDTSPass 1 AudioAllowMP3Pass 1 AudioEncoderFallback AC3 (ffmpeg) AudioList AudioBitrate 160 AudioEncoder AAC (avcodec) AudioMixdown Dolby Pro Logic II AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 AudioBitrate 160 AudioEncoder AC3 Passthru AudioMixdown None AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 ChapterMarkers 1 Default 0 FileFormat MP4 file Folder Mp4HttpOptimize 0 Mp4LargeFile 1 Mp4iPodCompatible 0 PictureAutoCrop 1 PictureBottomCrop 0 PictureDeblock 0 PictureDecomb 0 PictureDecombCustom PictureDecombDeinterlace 1 PictureDeinterlace 0 PictureDeinterlaceCustom PictureDenoiseFilter off PictureDenoiseCustom PictureDetelecine 0 PictureDetelecineCustom PictureHeight 720 PictureKeepRatio 0 PictureLeftCrop 0 PictureModulus 2 PicturePAR 2 PictureRightCrop 0 PictureTopCrop 0 PictureWidth 1280 PresetDescription HandBrake's settings for the second-generation AppleTV. Includes Dolby Digital audio for surround sound. NOT compatible with the original AppleTV. PresetName AppleTV 2 Subtitles None Type 0 UsesPictureFilters 1 UsesPictureSettings 1 VideoAvgBitrate 2500 VideoEncoder H.264 (x264) VideoFramerate 30 VideoFramerateMode pfr VideoGrayScale 0 VideoQualitySlider 20 VideoQualityType 2 VideoTurboTwoPass 0 VideoTwoPass 0 VideoLevel 3.1 VideoProfile high x264Option VideoOptionExtra VideoPreset medium VideoTune x264UseAdvancedOptions 0 AudioAllowAACPass 1 AudioAllowAC3Pass 1 AudioAllowDTSHDPass 1 AudioAllowDTSPass 1 AudioAllowMP3Pass 1 AudioEncoderFallback AC3 (ffmpeg) AudioList AudioBitrate 160 AudioEncoder AAC (avcodec) AudioMixdown Dolby Pro Logic II AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 AudioBitrate 160 AudioEncoder AC3 Passthru AudioMixdown None AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 ChapterMarkers 1 Default 0 FileFormat MP4 file Folder Mp4HttpOptimize 0 Mp4LargeFile 1 Mp4iPodCompatible 0 PictureAutoCrop 1 PictureBottomCrop 0 PictureDeblock 0 PictureDecomb 3 PictureDecombCustom PictureDecombDeinterlace 1 PictureDeinterlace 0 PictureDeinterlaceCustom PictureDenoiseFilter off PictureDenoiseCustom PictureDetelecine 0 PictureDetelecineCustom PictureHeight 1080 PictureKeepRatio 0 PictureLeftCrop 0 PictureModulus 2 PicturePAR 2 PictureRightCrop 0 PictureTopCrop 0 PictureWidth 1920 PresetDescription HandBrake's settings for the third-generation AppleTV. Includes Dolby Digital audio for surround sound. NOT compatible with the original AppleTV. May stutter on the second-generation AppleTV. PresetName AppleTV 3 Subtitles None Type 0 UsesPictureFilters 1 UsesPictureSettings 1 VideoAvgBitrate 2500 VideoEncoder H.264 (x264) VideoFramerate 30 VideoFramerateMode pfr VideoGrayScale 0 VideoQualitySlider 20 VideoQualityType 2 VideoTurboTwoPass 0 VideoTwoPass 0 VideoLevel 4.0 VideoProfile high x264Option VideoOptionExtra VideoPreset medium VideoTune x264UseAdvancedOptions 0 AudioAllowAACPass 1 AudioAllowAC3Pass 1 AudioAllowDTSHDPass 1 AudioAllowDTSPass 1 AudioAllowMP3Pass 1 AudioEncoderFallback AC3 (ffmpeg) AudioList AudioBitrate 128 AudioEncoder AAC (avcodec) AudioMixdown Dolby Pro Logic II AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 ChapterMarkers 0 Default 0 FileFormat MP4 file Folder Mp4HttpOptimize 0 Mp4LargeFile 0 Mp4iPodCompatible 0 PictureAutoCrop 1 PictureBottomCrop 0 PictureDeblock 0 PictureDecomb 0 PictureDecombCustom PictureDecombDeinterlace 1 PictureDeinterlace 0 PictureDeinterlaceCustom PictureDenoiseFilter off PictureDenoiseCustom PictureDetelecine 0 PictureDetelecineCustom PictureHeight 576 PictureKeepRatio 0 PictureLeftCrop 0 PictureModulus 2 PicturePAR 2 PictureRightCrop 0 PictureTopCrop 0 PictureWidth 720 PresetDescription HandBrake's settings for midrange devices running Android 2.3 or later. PresetName Android Subtitles None Type 0 UsesPictureFilters 1 UsesPictureSettings 1 VideoAvgBitrate 2500 VideoEncoder H.264 (x264) VideoFramerate 30 VideoFramerateMode pfr VideoGrayScale 0 VideoQualitySlider 22 VideoQualityType 2 VideoTurboTwoPass 0 VideoTwoPass 0 VideoLevel 3.0 VideoProfile main x264Option VideoOptionExtra VideoPreset medium VideoTune x264UseAdvancedOptions 0 AudioAllowAACPass 1 AudioAllowAC3Pass 1 AudioAllowDTSHDPass 1 AudioAllowDTSPass 1 AudioAllowMP3Pass 1 AudioEncoderFallback AC3 (ffmpeg) AudioList AudioBitrate 128 AudioEncoder AAC (avcodec) AudioMixdown Dolby Pro Logic II AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 ChapterMarkers 0 Default 0 FileFormat MP4 file Folder Mp4HttpOptimize 0 Mp4LargeFile 0 Mp4iPodCompatible 0 PictureAutoCrop 1 PictureBottomCrop 0 PictureDeblock 0 PictureDecomb 0 PictureDecombCustom PictureDecombDeinterlace 1 PictureDeinterlace 0 PictureDeinterlaceCustom PictureDenoiseFilter off PictureDenoiseCustom PictureDetelecine 0 PictureDetelecineCustom PictureHeight 720 PictureKeepRatio 0 PictureLeftCrop 0 PictureModulus 2 PicturePAR 2 PictureRightCrop 0 PictureTopCrop 0 PictureWidth 1280 PresetDescription HandBrake's preset for tablets running Android 2.3 or later. PresetName Android Tablet Subtitles None Type 0 UsesPictureFilters 1 UsesPictureSettings 1 VideoAvgBitrate 2500 VideoEncoder H.264 (x264) VideoFramerate 30 VideoFramerateMode pfr VideoGrayScale 0 VideoQualitySlider 22 VideoQualityType 2 VideoTurboTwoPass 0 VideoTwoPass 0 VideoLevel 3.1 VideoProfile main x264Option VideoOptionExtra VideoPreset medium VideoTune x264UseAdvancedOptions 0 AudioAllowAACPass 1 AudioAllowAC3Pass 1 AudioAllowDTSHDPass 1 AudioAllowDTSPass 1 AudioAllowMP3Pass 1 AudioEncoderFallback AC3 (ffmpeg) AudioList AudioBitrate 128 AudioEncoder AAC (avcodec) AudioMixdown Dolby Pro Logic II AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 ChapterMarkers 0 Default 0 FileFormat MP4 file Folder Mp4HttpOptimize 0 Mp4LargeFile 0 Mp4iPodCompatible 0 PictureAutoCrop 1 PictureBottomCrop 0 PictureDeblock 0 PictureDecomb 0 PictureDecombCustom PictureDecombDeinterlace 1 PictureDeinterlace 0 PictureDeinterlaceCustom PictureDenoiseFilter off PictureDenoiseCustom PictureDetelecine 0 PictureDetelecineCustom PictureHeight 720 PictureKeepRatio 1 PictureLeftCrop 0 PictureModulus 2 PicturePAR 0 PictureRightCrop 0 PictureTopCrop 0 PictureWidth 1280 PresetDescription HandBrake's preset for Windows Phone 8 devices PresetName Windows Phone 8 Subtitles None Type 0 UsesPictureFilters 1 UsesPictureSettings 1 VideoAvgBitrate 2500 VideoEncoder H.264 (x264) VideoFramerate 30 VideoFramerateMode pfr VideoGrayScale 0 VideoQualitySlider 22 VideoQualityType 2 VideoTurboTwoPass 0 VideoTwoPass 0 VideoLevel 3.1 VideoProfile main x264Option VideoOptionExtra VideoPreset medium VideoTune x264UseAdvancedOptions 0 Default 0 Folder PresetBuildNumber 2013061301 PresetName Devices Type 0 ChildrenArray AudioAllowAACPass 1 AudioAllowAC3Pass 1 AudioAllowDTSHDPass 1 AudioAllowDTSPass 1 AudioAllowMP3Pass 1 AudioEncoderFallback AC3 (ffmpeg) AudioList AudioBitrate 160 AudioEncoder AAC (avcodec) AudioMixdown Dolby Pro Logic II AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 ChapterMarkers 1 Default 1 FileFormat MP4 file Folder Mp4HttpOptimize 0 Mp4LargeFile 0 Mp4iPodCompatible 0 PictureAutoCrop 1 PictureBottomCrop 0 PictureDeblock 0 PictureDecomb 0 PictureDecombCustom PictureDecombDeinterlace 1 PictureDeinterlace 0 PictureDeinterlaceCustom PictureDenoiseFilter off PictureDenoiseCustom PictureDetelecine 0 PictureDetelecineCustom PictureHeight 0 PictureKeepRatio 0 PictureLeftCrop 0 PictureModulus 2 PicturePAR 2 PictureRightCrop 0 PictureTopCrop 0 PictureWidth 0 PresetDescription HandBrake's normal, default settings. PresetName Normal Subtitles None Type 0 UsesPictureFilters 1 UsesPictureSettings 1 VideoAvgBitrate 2500 VideoEncoder H.264 (x264) VideoFramerate Same as source VideoFramerateMode vfr VideoGrayScale 0 VideoQualitySlider 20 VideoQualityType 2 VideoTurboTwoPass 0 VideoTwoPass 0 VideoLevel 4.0 VideoProfile main x264Option VideoOptionExtra VideoPreset veryfast VideoTune x264UseAdvancedOptions 0 AudioAllowAACPass 1 AudioAllowAC3Pass 1 AudioAllowDTSHDPass 1 AudioAllowDTSPass 1 AudioAllowMP3Pass 1 AudioEncoderFallback AC3 (ffmpeg) AudioList AudioBitrate 160 AudioEncoder AAC (avcodec) AudioMixdown Dolby Pro Logic II AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 AudioBitrate 160 AudioEncoder AC3 Passthru AudioMixdown None AudioSamplerate Auto AudioTrack 1 AudioTrackDRCSlider 0.0 AudioTrackGainSlider 0.0 ChapterMarkers 1 Default 0 FileFormat MP4 file Folder Mp4HttpOptimize 0 Mp4LargeFile 1 Mp4iPodCompatible 0 PictureAutoCrop 1 PictureBottomCrop 0 PictureDeblock 0 PictureDecomb 2 PictureDecombCustom PictureDecombDeinterlace 1 PictureDeinterlace 0 PictureDeinterlaceCustom PictureDenoiseFilter off PictureDenoiseCustom PictureDetelecine 0 PictureDetelecineCustom PictureHeight 0 PictureKeepRatio 0 PictureLeftCrop 0 PictureModulus 2 PicturePAR 2 PictureRightCrop 0 PictureTopCrop 0 PictureWidth 0 PresetDescription HandBrake's general-purpose preset for High Profile H.264 video. PresetName High Profile Subtitles None Type 0 UsesPictureFilters 1 UsesPictureSettings 1 VideoAvgBitrate 2500 VideoEncoder H.264 (x264) VideoFramerate Same as source VideoFramerateMode vfr VideoGrayScale 0 VideoQualitySlider 20 VideoQualityType 2 VideoTurboTwoPass 0 VideoTwoPass 0 VideoLevel 4.1 VideoProfile high x264Option VideoOptionExtra VideoPreset medium VideoTune x264UseAdvancedOptions 0 Default 0 Folder PresetBuildNumber 2013061301 PresetName Regular Type 0 HandBrake-0.10.2/gtk/src/subtitlehandler.c0000664000175200017520000014101312465416500020753 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * subtitlehandler.c * Copyright (C) John Stebbins 2008-2015 * * subtitlehandler.c is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. */ #include #include "ghbcompat.h" #include "hb.h" #include "settings.h" #include "hb-backend.h" #include "values.h" #include "callbacks.h" #include "preview.h" #include "presets.h" #include "audiohandler.h" #include "subtitlehandler.h" static void subtitle_add_to_settings(GValue *settings, GValue *subsettings); static void ghb_add_subtitle_to_ui(signal_user_data_t *ud, GValue *subsettings); static void add_to_subtitle_list_ui(signal_user_data_t *ud, GValue *settings); static void ghb_clear_subtitle_list_settings(GValue *settings); static void ghb_clear_subtitle_list_ui(GtkBuilder *builder); static void subtitle_refresh_list_row_ui( GtkTreeModel *tm, GtkTreeIter *ti, GValue *subsettings) { GtkTreeIter cti; gboolean forced, burned, def; char *info_src, *info_src_2; char *info_dst, *info_dst_2; info_src_2 = NULL; info_dst_2 = NULL; forced = ghb_settings_get_boolean(subsettings, "SubtitleForced"); burned = ghb_settings_get_boolean(subsettings, "SubtitleBurned"); def = ghb_settings_get_boolean(subsettings, "SubtitleDefaultTrack"); info_src = g_strdup_printf("%s", ghb_settings_get_const_string(subsettings, "SubtitleTrackDescription")); if (ghb_settings_get_int(subsettings, "SubtitleSource") == SRTSUB) { gint offset; offset = ghb_settings_get_int(subsettings, "SrtOffset"); if (offset != 0) { info_dst_2 = g_strdup_printf("Offset: %dms", offset); } } GString *str = g_string_new(""); g_string_append_printf(str, "%s ", burned ? "Burned Into Video" : "Passthrough"); if (forced) { g_string_append_printf(str, "(Forced Subtitles Only)"); } if (def) { g_string_append_printf(str, "(Default)"); } g_string_append_printf(str, ""); info_dst = g_string_free(str, FALSE); gtk_tree_store_set(GTK_TREE_STORE(tm), ti, // These are displayed in list 0, info_src, 1, "-->", 2, info_dst, 3, "hb-edit", 4, "hb-remove", 5, 0.5, -1); if (info_src_2 != NULL || info_dst_2 != NULL) { if (info_src_2 == NULL) info_src_2 = g_strdup(""); if (info_dst_2 == NULL) info_dst_2 = g_strdup(""); if (!gtk_tree_model_iter_children(tm, &cti, ti)) { gtk_tree_store_append(GTK_TREE_STORE(tm), &cti, ti); } gtk_tree_store_set(GTK_TREE_STORE(tm), &cti, // These are displayed in list 0, info_src_2, 2, info_dst_2, 5, 0.0, -1); } else { if (gtk_tree_model_iter_children(tm, &cti, ti)) { gtk_tree_store_remove(GTK_TREE_STORE(tm), &cti); } } g_free(info_src); g_free(info_src_2); g_free(info_dst); g_free(info_dst_2); } static void subtitle_refresh_list_ui_from_settings(signal_user_data_t *ud, GValue *settings) { GValue *subtitle_list; GValue *subsettings; gint ii, count, tm_count; GtkTreeView *tv; GtkTreeModel *tm; GtkTreeIter ti; tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list")); tm = gtk_tree_view_get_model(tv); tm_count = gtk_tree_model_iter_n_children(tm, NULL); subtitle_list = ghb_settings_get_value(settings, "subtitle_list"); count = ghb_array_len(subtitle_list); if (count != tm_count) { ghb_clear_subtitle_list_ui(ud->builder); for (ii = 0; ii < count; ii++) { gtk_tree_store_append(GTK_TREE_STORE(tm), &ti, NULL); } } for (ii = 0; ii < count; ii++) { g_return_if_fail(tv != NULL); gtk_tree_model_iter_nth_child(tm, &ti, NULL, ii); subsettings = ghb_array_get_nth(subtitle_list, ii); subtitle_refresh_list_row_ui(tm, &ti, subsettings); } } static void subtitle_refresh_list_ui(signal_user_data_t *ud) { subtitle_refresh_list_ui_from_settings(ud, ud->settings); } void ghb_subtitle_exclusive_burn_settings(GValue *settings, gint index) { GValue *subtitle_list; GValue *subsettings; gint ii, count; subtitle_list = ghb_settings_get_value(settings, "subtitle_list"); count = ghb_array_len(subtitle_list); for (ii = 0; ii < count; ii++) { if (ii != index) { subsettings = ghb_array_get_nth(subtitle_list, ii); ghb_settings_set_boolean(subsettings, "SubtitleBurned", FALSE); } } } void ghb_subtitle_exclusive_burn(signal_user_data_t *ud, gint index) { ghb_subtitle_exclusive_burn_settings(ud->settings, index); subtitle_refresh_list_ui(ud); } void ghb_subtitle_exclusive_default_settings(GValue *settings, gint index) { GValue *subtitle_list; GValue *subtitle; gint ii, count; subtitle_list = ghb_settings_get_value(settings, "subtitle_list"); count = ghb_array_len(subtitle_list); for (ii = 0; ii < count; ii++) { if (ii != index) { subtitle = ghb_array_get_nth(subtitle_list, ii); ghb_settings_set_boolean(subtitle, "SubtitleDefaultTrack", FALSE); } } } void ghb_subtitle_exclusive_default(signal_user_data_t *ud, gint index) { ghb_subtitle_exclusive_default_settings(ud->settings, index); subtitle_refresh_list_ui(ud); } static void ghb_add_subtitle_to_ui(signal_user_data_t *ud, GValue *subsettings) { if (subsettings == NULL) return; // Add the current subtitle settings to the list. add_to_subtitle_list_ui(ud, subsettings); ghb_live_reset(ud); } static void subtitle_add_to_settings(GValue *settings, GValue *subsettings) { // Add the current subtitle settings to the list. GValue *subtitle_list; gint count; gboolean burned, forced, def; gint source; subtitle_list = ghb_settings_get_value(settings, "subtitle_list"); if (subtitle_list == NULL) { subtitle_list = ghb_array_value_new(8); ghb_settings_set_value(settings, "subtitle_list", subtitle_list); } // Validate some settings const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); source = ghb_settings_get_int(subsettings, "SubtitleSource"); burned = ghb_settings_get_boolean(subsettings, "SubtitleBurned"); if (burned && !hb_subtitle_can_burn(source)) { burned = FALSE; ghb_settings_set_boolean(subsettings, "SubtitleBurned", burned); } if (!burned && !hb_subtitle_can_pass(source, mux->format)) { burned = TRUE; ghb_settings_set_boolean(subsettings, "SubtitleBurned", burned); ghb_settings_set_boolean(subsettings, "SubtitleDefaultTrack", FALSE); } def = ghb_settings_get_boolean(subsettings, "SubtitleDefaultTrack"); forced = ghb_settings_get_boolean(subsettings, "SubtitleForced"); if (forced && !hb_subtitle_can_force(source)) { forced = FALSE; ghb_settings_set_boolean(subsettings, "SubtitleForced", forced); } ghb_array_append(subtitle_list, subsettings); // Check consistancy of exclusive flags count = ghb_array_len(subtitle_list); if (burned) ghb_subtitle_exclusive_burn_settings(settings, count-1); if (def) ghb_subtitle_exclusive_default_settings(settings, count-1); } static void subtitle_set_track_description(GValue *settings, GValue *subsettings) { char *desc = NULL; if (ghb_settings_get_int(subsettings, "SubtitleSource") == SRTSUB) { gchar *filename, *code; const gchar *lang; lang = ghb_settings_combo_option(subsettings, "SrtLanguage"); code = ghb_settings_get_string(subsettings, "SrtCodeset"); filename = ghb_settings_get_string(subsettings, "SrtFile"); if (g_file_test(filename, G_FILE_TEST_IS_REGULAR)) { gchar *basename; basename = g_path_get_basename(filename); desc = g_strdup_printf("%s (%s)(SRT)(%s)", lang, code, basename); g_free(basename); } else { desc = g_strdup_printf("%s (%s)(SRT)", lang, code); } g_free(code); } else { int title_id, titleindex; const hb_title_t *title; int track; hb_subtitle_t *subtitle; title_id = ghb_settings_get_int(settings, "title"); title = ghb_lookup_title(title_id, &titleindex); track = ghb_settings_get_int(subsettings, "SubtitleTrack"); if (track < 0) { desc = g_strdup(_("Foreign Audio Search")); } else { subtitle = ghb_get_subtitle_info(title, track); if (subtitle != NULL) { desc = g_strdup_printf("%d - %s (%s)", track + 1, subtitle->lang, hb_subsource_name(subtitle->source)); } } } if (desc != NULL) { ghb_settings_set_string( subsettings, "SubtitleTrackDescription", desc); } else { ghb_settings_set_string( subsettings, "SubtitleTrackDescription", "Error!"); } g_free(desc); } static GValue* subtitle_add_track( signal_user_data_t *ud, GValue *settings, const hb_title_t *title, int track, int mux, gboolean default_track, gboolean srt, gboolean *burned) { int source = 0; if (track >= 0 && !srt) { hb_subtitle_t *subtitle = hb_list_item(title->list_subtitle, track); source = subtitle->source; } else if (srt) { source = SRTSUB; } if (*burned && !hb_subtitle_can_pass(source, mux)) { // Can only burn one. Skip others that must be burned. return NULL; } GValue *subsettings = ghb_dict_value_new(); ghb_settings_set_int(subsettings, "SubtitleTrack", track); ghb_settings_set_int(subsettings, "SubtitleSource", source); // Set default SRT settings gchar *pref_lang, *dir, *filename; pref_lang = ghb_settings_get_string(settings, "PreferredLanguage"); ghb_settings_set_string(subsettings, "SrtLanguage", pref_lang); g_free(pref_lang); ghb_settings_set_string(subsettings, "SrtCodeset", "UTF-8"); dir = ghb_settings_get_string(ud->prefs, "SrtDir"); filename = g_strdup_printf("%s/none", dir); ghb_settings_set_string(subsettings, "SrtFile", filename); g_free(dir); g_free(filename); ghb_settings_set_int(subsettings, "SrtOffset", 0); subtitle_set_track_description(settings, subsettings); if (!hb_subtitle_can_pass(source, mux)) { ghb_settings_set_boolean(subsettings, "SubtitleBurned", TRUE); *burned = TRUE; } else { ghb_settings_set_boolean(subsettings, "SubtitleBurned", FALSE); } if (track == -1) { // Foreign audio search "track" ghb_settings_set_boolean(subsettings, "SubtitleForced", TRUE); } else { ghb_settings_set_boolean(subsettings, "SubtitleForced", FALSE); } if (default_track) { ghb_settings_set_boolean(subsettings, "SubtitleDefaultTrack", TRUE); } else { ghb_settings_set_boolean(subsettings, "SubtitleDefaultTrack", FALSE); } subtitle_add_to_settings(settings, subsettings); return subsettings; } void ghb_subtitle_title_change(signal_user_data_t *ud, gboolean show) { GtkWidget *w = GHB_WIDGET(ud->builder, "subtitle_add"); gtk_widget_set_sensitive(w, show); w = GHB_WIDGET(ud->builder, "subtitle_add_all"); gtk_widget_set_sensitive(w, show); w = GHB_WIDGET(ud->builder, "subtitle_reset"); gtk_widget_set_sensitive(w, show); int title_id, titleindex; title_id = ghb_settings_get_int(ud->settings, "title"); const hb_title_t *title = ghb_lookup_title(title_id, &titleindex); if (title != NULL) { w = GHB_WIDGET(ud->builder, "SubtitleSrtDisable"); gtk_widget_set_sensitive(w, !!hb_list_count(title->list_subtitle)); } } void ghb_set_pref_subtitle_settings(signal_user_data_t *ud, const hb_title_t *title, GValue *settings) { gint track; gboolean *used; const gchar *audio_lang, *pref_lang = NULL; gboolean foreign_audio_search, foreign_audio_subs; gboolean one_burned = FALSE; const GValue *lang_list; gint lang_count, sub_count, ii; int behavior; behavior = ghb_settings_combo_int(settings, "SubtitleTrackSelectionBehavior"); // Clear the subtitle list ghb_clear_subtitle_list_settings(settings); if (title == NULL) { // no source title return; } sub_count = hb_list_count(title->list_subtitle); if (sub_count == 0) { // No source subtitles return; } const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); // Check to see if we need to add a subtitle track for foreign audio // language films. A subtitle track will be added if: // // The first (default) audio track language does NOT match the users // chosen Preferred Language AND the Preferred Language is NOT Any (und). // audio_lang = ghb_get_user_audio_lang(settings, title, 0); foreign_audio_search = ghb_settings_get_boolean( settings, "SubtitleAddForeignAudioSearch"); foreign_audio_subs = ghb_settings_get_boolean( settings, "SubtitleAddForeignAudioSubtitle"); lang_list = ghb_settings_get_value(settings, "SubtitleLanguageList"); lang_count = ghb_array_len(lang_list); if (lang_count > 0) { GValue *glang = ghb_array_get_nth(lang_list, 0); pref_lang = g_value_get_string(glang); } if (pref_lang == NULL || !strncmp(pref_lang, "und", 4)) { foreign_audio_search = foreign_audio_subs = FALSE; pref_lang = NULL; } used = g_malloc0(sub_count * sizeof(gboolean)); if (foreign_audio_subs && (audio_lang == NULL || strncmp(audio_lang, pref_lang, 4))) { // Add preferred language subtitle since first audio track // is foreign language. foreign_audio_search = FALSE; track = ghb_find_subtitle_track(title, pref_lang, 0); if (track > 0) { used[track] = TRUE; subtitle_add_track(ud, settings, title, track, mux->format, TRUE, FALSE, &one_burned); } } if (foreign_audio_search && (audio_lang != NULL && !strncmp(audio_lang, pref_lang, 4))) { // Add search for foreign audio segments subtitle_add_track(ud, settings, title, -1, mux->format, TRUE, FALSE, &one_burned); } if (behavior != 0) { // Find "best" subtitle based on subtitle preferences for (ii = 0; ii < lang_count; ii++) { GValue *glang = ghb_array_get_nth(lang_list, ii); const gchar *lang = g_value_get_string(glang); int next_track = 0; track = ghb_find_subtitle_track(title, lang, next_track); while (track >= 0) { if (!used[track]) { used[track] = TRUE; subtitle_add_track(ud, settings, title, track, mux->format, FALSE, FALSE, &one_burned); } next_track = track + 1; if (behavior == 2) { track = ghb_find_subtitle_track(title, lang, next_track); } else { break; } } } } if (ghb_settings_get_boolean(settings, "SubtitleAddCC")) { for (track = 0; track < sub_count; track++) { hb_subtitle_t *subtitle = hb_list_item(title->list_subtitle, track); if (subtitle->source == CC608SUB || subtitle->source == CC708SUB) break; } if (track < sub_count && !used[track]) { used[track] = TRUE; subtitle_add_track(ud, settings, title, track, mux->format, FALSE, FALSE, &one_burned); } } g_free(used); } void ghb_set_pref_subtitle(const hb_title_t *title, signal_user_data_t *ud) { int sub_count; GtkWidget *widget; ghb_clear_subtitle_list_ui(ud->builder); if (title == NULL) { // Clear the subtitle list ghb_clear_subtitle_list_settings(ud->settings); return; } sub_count = hb_list_count(title->list_subtitle); if (sub_count == 0) { // No source subtitles widget = GHB_WIDGET(ud->builder, "SubtitleSrtDisable"); gtk_widget_set_sensitive(widget, FALSE); } else { widget = GHB_WIDGET(ud->builder, "SubtitleSrtDisable"); gtk_widget_set_sensitive(widget, TRUE); } ghb_set_pref_subtitle_settings(ud, title, ud->settings); subtitle_refresh_list_ui(ud); } gint ghb_selected_subtitle_row(signal_user_data_t *ud) { GtkTreeView *tv; GtkTreePath *tp; GtkTreeSelection *ts; GtkTreeModel *tm; GtkTreeIter iter; gint *indices; gint row = -1; g_debug("ghb_selected_subtitle_row ()"); tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list")); ts = gtk_tree_view_get_selection(tv); if (gtk_tree_selection_get_selected(ts, &tm, &iter)) { // Get the row number tp = gtk_tree_model_get_path(tm, &iter); indices = gtk_tree_path_get_indices(tp); row = indices[0]; gtk_tree_path_free(tp); } return row; } static GValue* subtitle_get_selected_settings(signal_user_data_t *ud, int *index) { GtkTreeView *tv; GtkTreePath *tp; GtkTreeSelection *ts; GtkTreeModel *tm; GtkTreeIter iter; gint *indices; gint row; GValue *subsettings = NULL; const GValue *subtitle_list; g_debug("get_selected_settings ()"); tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list")); ts = gtk_tree_view_get_selection(tv); if (gtk_tree_selection_get_selected(ts, &tm, &iter)) { // Get the row number tp = gtk_tree_model_get_path(tm, &iter); indices = gtk_tree_path_get_indices(tp); row = indices[0]; gtk_tree_path_free(tp); if (row < 0) return NULL; subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list"); if (row >= ghb_array_len(subtitle_list)) return NULL; subsettings = ghb_array_get_nth(subtitle_list, row); if (index != NULL) *index = row; } return subsettings; } static void subtitle_update_dialog_widgets(signal_user_data_t *ud, GValue *subsettings) { GtkWidget *widget; if (subsettings != NULL) { // Update widgets with subsettings gboolean burn, force, def; const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(ud->settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); int source = ghb_settings_get_int(subsettings, "SubtitleSource"); ghb_ui_update_from_settings(ud, "SubtitleTrack", subsettings); ghb_ui_update_from_settings(ud, "SrtLanguage", subsettings); ghb_ui_update_from_settings(ud, "SrtCodeset", subsettings); ghb_ui_update_from_settings(ud, "SrtFile", subsettings); ghb_ui_update_from_settings(ud, "SrtOffset", subsettings); if (source == SRTSUB) { ghb_ui_update(ud, "SubtitleSrtEnable", ghb_boolean_value(TRUE)); } else { ghb_ui_update(ud, "SubtitleSrtDisable", ghb_boolean_value(TRUE)); } widget = GHB_WIDGET(ud->builder, "SubtitleBurned"); gtk_widget_set_sensitive(widget, hb_subtitle_can_burn(source) && hb_subtitle_can_pass(source, mux->format)); widget = GHB_WIDGET(ud->builder, "SubtitleForced"); gtk_widget_set_sensitive(widget, hb_subtitle_can_force(source)); widget = GHB_WIDGET(ud->builder, "SubtitleDefaultTrack"); gtk_widget_set_sensitive(widget, hb_subtitle_can_pass(source, mux->format)); burn = ghb_settings_get_int(subsettings, "SubtitleBurned"); force = ghb_settings_get_int(subsettings, "SubtitleForced"); def = ghb_settings_get_int(subsettings, "SubtitleDefaultTrack"); if (!hb_subtitle_can_burn(source)) { burn = FALSE; } if (!hb_subtitle_can_force(source)) { force = FALSE; } if (!hb_subtitle_can_pass(source, mux->format)) { def = FALSE; burn = TRUE; } ghb_settings_set_boolean(subsettings, "SubtitleBurned", burn); ghb_ui_update(ud, "SubtitleBurned", ghb_boolean_value(burn)); ghb_settings_set_boolean(subsettings, "SubtitleForced", force); ghb_ui_update(ud, "SubtitleForced", ghb_boolean_value(force)); ghb_settings_set_boolean(subsettings, "SubtitleDefaultTrack", def); ghb_ui_update(ud, "SubtitleDefaultTrack", ghb_boolean_value(def)); // Hide regular subtitle widgets widget = GHB_WIDGET(ud->builder, "subtitle_track_box"); gtk_widget_set_visible(widget, source != SRTSUB); // Show SRT subitle widgets widget = GHB_WIDGET(ud->builder, "subtitle_srt_grid"); gtk_widget_set_visible(widget, source == SRTSUB); } else { // Hide SRT subitle widgets widget = GHB_WIDGET(ud->builder, "subtitle_srt_grid"); gtk_widget_set_visible(widget, FALSE); // Show regular subtitle widgets widget = GHB_WIDGET(ud->builder, "subtitle_track_box"); gtk_widget_set_visible(widget, TRUE); } } static GValue* subtitle_update_setting(GtkWidget *widget, signal_user_data_t *ud) { GValue *subsettings; ghb_widget_to_setting(ud->settings, widget); subsettings = subtitle_get_selected_settings(ud, NULL); if (subsettings != NULL) { ghb_widget_to_setting(subsettings, widget); ghb_subtitle_list_refresh_selected(ud); ghb_live_reset(ud); } return subsettings; } G_MODULE_EXPORT void subtitle_track_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { GValue *subsettings; g_debug("subtitle_track_changed_cb()"); ghb_widget_to_setting(ud->settings, widget); subsettings = subtitle_get_selected_settings(ud, NULL); if (subsettings != NULL) { gint track, source; ghb_widget_to_setting(subsettings, widget); track = ghb_settings_get_int(subsettings, "SubtitleTrack"); source = ghb_subtitle_track_source(ud->settings, track); ghb_settings_set_int(subsettings, "SubtitleSource", source); subtitle_set_track_description(ud->settings, subsettings); subtitle_update_dialog_widgets(ud, subsettings); ghb_subtitle_list_refresh_selected(ud); ghb_live_reset(ud); } } G_MODULE_EXPORT void subtitle_forced_toggled_cb(GtkWidget *widget, signal_user_data_t *ud) { subtitle_update_setting(widget, ud); } G_MODULE_EXPORT void subtitle_burned_toggled_cb(GtkWidget *widget, signal_user_data_t *ud) { GValue *subsettings; int index; ghb_widget_to_setting(ud->settings, widget); subsettings = subtitle_get_selected_settings(ud, &index); if (subsettings != NULL) { ghb_widget_to_setting(subsettings, widget); if (ghb_settings_get_boolean(subsettings, "SubtitleBurned")) { ghb_ui_update(ud, "SubtitleDefaultTrack", ghb_boolean_value(FALSE)); ghb_subtitle_exclusive_burn(ud, index); } ghb_subtitle_list_refresh_selected(ud); ghb_live_reset(ud); } } G_MODULE_EXPORT void subtitle_default_toggled_cb(GtkWidget *widget, signal_user_data_t *ud) { GValue *subsettings; int index; ghb_widget_to_setting(ud->settings, widget); subsettings = subtitle_get_selected_settings(ud, &index); if (subsettings != NULL) { ghb_widget_to_setting(subsettings, widget); if (ghb_settings_get_boolean(subsettings, "SubtitleDefaultTrack")) { ghb_ui_update(ud, "SubtitleBurned", ghb_boolean_value(FALSE)); ghb_subtitle_exclusive_default(ud, index); } ghb_subtitle_list_refresh_selected(ud); ghb_live_reset(ud); } } G_MODULE_EXPORT void subtitle_srt_radio_toggled_cb(GtkWidget *widget, signal_user_data_t *ud) { GValue *subsettings; ghb_widget_to_setting(ud->settings, widget); subsettings = subtitle_get_selected_settings(ud, NULL); if (subsettings != NULL) { if (ghb_settings_get_boolean(ud->settings, "SubtitleSrtEnable")) { ghb_settings_set_int(subsettings, "SubtitleSource", SRTSUB); } else { int track, source; track = ghb_settings_get_int(subsettings, "SubtitleTrack"); source = ghb_subtitle_track_source(ud->settings, track); ghb_settings_set_int(subsettings, "SubtitleSource", source); } subtitle_set_track_description(ud->settings, subsettings); subtitle_update_dialog_widgets(ud, subsettings); ghb_subtitle_list_refresh_selected(ud); ghb_live_reset(ud); } } void ghb_subtitle_list_refresh_selected(signal_user_data_t *ud) { GtkTreeView *tv; GtkTreeModel *tm; GtkTreePath *tp; GtkTreeSelection *ts; GtkTreeIter ti; gint *indices; gint row; GValue *subsettings = NULL; const GValue *subtitle_list; g_debug("subtitle_list_refresh_selected()"); tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list")); ts = gtk_tree_view_get_selection(tv); if (gtk_tree_selection_get_selected(ts, &tm, &ti)) { // Get the row number tp = gtk_tree_model_get_path(tm, &ti); indices = gtk_tree_path_get_indices(tp); row = indices[0]; gtk_tree_path_free(tp); if (row < 0) return; subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list"); if (row >= ghb_array_len(subtitle_list)) return; subsettings = ghb_array_get_nth(subtitle_list, row); subtitle_refresh_list_row_ui(tm, &ti, subsettings); } } void ghb_subtitle_list_refresh_all(signal_user_data_t *ud) { subtitle_refresh_list_ui(ud); } G_MODULE_EXPORT void srt_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { GValue *subsettings; g_debug("srt_changed_cb()"); ghb_check_dependency(ud, widget, NULL); ghb_widget_to_setting(ud->settings, widget); subsettings = subtitle_get_selected_settings(ud, NULL); if (subsettings != NULL) { ghb_widget_to_setting(subsettings, widget); subtitle_set_track_description(ud->settings, subsettings); ghb_subtitle_list_refresh_selected(ud); ghb_live_reset(ud); } } G_MODULE_EXPORT void srt_file_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { GValue *subsettings; g_debug("srt_file_changed_cb()"); ghb_check_dependency(ud, widget, NULL); ghb_widget_to_setting(ud->settings, widget); subsettings = subtitle_get_selected_settings(ud, NULL); if (subsettings != NULL) { gchar *filename, *dirname; ghb_widget_to_setting(subsettings, widget); subtitle_set_track_description(ud->settings, subsettings); ghb_subtitle_list_refresh_selected(ud); ghb_live_reset(ud); // Update SrtDir preference filename = ghb_settings_get_string(subsettings, "SrtFile"); if (g_file_test(filename, G_FILE_TEST_IS_DIR)) { ghb_settings_set_string(ud->prefs, "SrtDir", filename); } else { dirname = g_path_get_dirname(filename); ghb_settings_set_string(ud->prefs, "SrtDir", dirname); g_free(dirname); } ghb_pref_save(ud->prefs, "SrtDir"); g_free(filename); } } G_MODULE_EXPORT void srt_lang_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { GValue *subsettings; g_debug("srt_lang_changed_cb()"); ghb_check_dependency(ud, widget, NULL); ghb_widget_to_setting(ud->settings, widget); subsettings = subtitle_get_selected_settings(ud, NULL); if (subsettings != NULL) { ghb_widget_to_setting(subsettings, widget); subtitle_set_track_description(ud->settings, subsettings); ghb_subtitle_list_refresh_selected(ud); ghb_live_reset(ud); } } static void ghb_clear_subtitle_list_settings(GValue *settings) { GValue *subtitle_list; subtitle_list = ghb_settings_get_value(settings, "subtitle_list"); if (subtitle_list == NULL) { subtitle_list = ghb_array_value_new(8); ghb_settings_set_value(settings, "subtitle_list", subtitle_list); } else ghb_array_value_reset(subtitle_list, 8); } static void ghb_clear_subtitle_list_ui(GtkBuilder *builder) { GtkTreeView *tv; GtkTreeStore *ts; GtkTreeSelection *tsel; tv = GTK_TREE_VIEW(GHB_WIDGET(builder, "subtitle_list")); ts = GTK_TREE_STORE(gtk_tree_view_get_model(tv)); // Clear tree selection so that updates are not triggered // that cause a recursive attempt to clear the tree selection (crasher) tsel = gtk_tree_view_get_selection(tv); gtk_tree_selection_unselect_all(tsel); gtk_tree_store_clear(ts); } static void add_to_subtitle_list_ui(signal_user_data_t *ud, GValue *subsettings) { GtkTreeView *tv; GtkTreeIter ti; GtkTreeModel *tm; GtkTreeSelection *ts; tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list")); ts = gtk_tree_view_get_selection(tv); tm = gtk_tree_view_get_model(tv); gtk_tree_store_append(GTK_TREE_STORE(tm), &ti, NULL); subtitle_refresh_list_row_ui(tm, &ti, subsettings); gtk_tree_selection_select_iter(ts, &ti); } G_MODULE_EXPORT void subtitle_list_selection_changed_cb(GtkTreeSelection *ts, signal_user_data_t *ud) { GtkTreeModel *tm; GtkTreeIter iter; GValue *subsettings = NULL; int row; g_debug("subtitle_list_selection_changed_cb()"); if (gtk_tree_selection_get_selected(ts, &tm, &iter)) { GtkTreeIter piter; if (gtk_tree_model_iter_parent(tm, &piter, &iter)) { GtkTreePath *path; GtkTreeView *tv; gtk_tree_selection_select_iter(ts, &piter); path = gtk_tree_model_get_path(tm, &piter); tv = gtk_tree_selection_get_tree_view(ts); // Make the parent visible in scroll window if it is not. gtk_tree_view_scroll_to_cell(tv, path, NULL, FALSE, 0, 0); gtk_tree_path_free(path); return; } GtkTreePath *tp; gint *indices; GValue *subtitle_list; tp = gtk_tree_model_get_path(tm, &iter); indices = gtk_tree_path_get_indices(tp); row = indices[0]; gtk_tree_path_free(tp); subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list"); if (row >= 0 && row < ghb_array_len(subtitle_list)) subsettings = ghb_array_get_nth(subtitle_list, row); } subtitle_update_dialog_widgets(ud, subsettings); if (subsettings) { if (ghb_settings_get_boolean(subsettings, "SubtitleBurned")) { ghb_subtitle_exclusive_burn(ud, row); } } } static gboolean subtitle_is_one_burned(GValue *settings) { GValue *subtitle_list, *subsettings; int count, ii; subtitle_list = ghb_settings_get_value(settings, "subtitle_list"); if (subtitle_list == NULL) return FALSE; count = ghb_array_len(subtitle_list); for (ii = 0; ii < count; ii++) { subsettings = ghb_array_get_nth(subtitle_list, ii); if (ghb_settings_get_boolean(subsettings, "SubtitleBurned")) { return TRUE; } } return FALSE; } G_MODULE_EXPORT void subtitle_add_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { // Add the current subtitle settings to the list. GValue *subsettings, *backup; gboolean one_burned; gint track; int title_id, titleindex; const hb_title_t *title; title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (title == NULL) { return; } // Back up settings in case we need to revert. backup = ghb_value_dup( ghb_settings_get_value(ud->settings, "subtitle_list")); one_burned = subtitle_is_one_burned(ud->settings); const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(ud->settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); int count = hb_list_count(title->list_subtitle); for (subsettings = NULL, track = 0; subsettings == NULL && track < count; track++) { subsettings = subtitle_add_track(ud, ud->settings, title, track, mux->format, FALSE, FALSE, &one_burned); } if (subsettings == NULL) { subsettings = subtitle_add_track(ud, ud->settings, title, 0, mux->format, FALSE, TRUE, &one_burned); } ghb_add_subtitle_to_ui(ud, subsettings); if (subsettings != NULL) { // Pop up the edit dialog GtkResponseType response; GtkWidget *dialog = GHB_WIDGET(ud->builder, "subtitle_dialog"); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); if (response != GTK_RESPONSE_OK) { ghb_settings_take_value(ud->settings, "subtitle_list", backup); subsettings = subtitle_get_selected_settings(ud, NULL); if (subsettings != NULL) { subtitle_update_dialog_widgets(ud, subsettings); } subtitle_refresh_list_ui(ud); } else { ghb_value_free(backup); } } } G_MODULE_EXPORT void subtitle_add_all_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { // Add the current subtitle settings to the list. gboolean one_burned = FALSE; gint track; const hb_title_t *title; int title_id, titleindex; title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (title == NULL) { return; } ghb_clear_subtitle_list_settings(ud->settings); ghb_clear_subtitle_list_ui(ud->builder); const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(ud->settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); int count = hb_list_count(title->list_subtitle); for (track = 0; track < count; track++) { subtitle_add_track(ud, ud->settings, title, track, mux->format, FALSE, FALSE, &one_burned); } subtitle_refresh_list_ui(ud); ghb_live_reset(ud); } G_MODULE_EXPORT void subtitle_reset_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { int title_id, titleindex; const hb_title_t *title; title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); ghb_set_pref_subtitle(title, ud); } void ghb_subtitle_prune(signal_user_data_t *ud) { GValue *subtitle_list; GValue *subsettings; gint ii; gboolean one_burned = FALSE; subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list"); if (subtitle_list == NULL) return; const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(ud->settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); for (ii = 0; ii < ghb_array_len(subtitle_list); ) { gboolean burned; int source; subsettings = ghb_array_get_nth(subtitle_list, ii); burned = ghb_settings_get_boolean(subsettings, "SubtitleBurned"); source = ghb_settings_get_boolean(subsettings, "SubtitleSource"); burned = burned || !hb_subtitle_can_pass(source, mux->format); if (burned && one_burned) { GValue *gsub = ghb_array_get_nth(subtitle_list, ii); ghb_array_remove(subtitle_list, ii); ghb_value_free(gsub); continue; } one_burned = one_burned || burned; ghb_settings_set_boolean(subsettings, "SubtitleBurned", burned); ii++; } subsettings = subtitle_get_selected_settings(ud, NULL); if (subsettings != NULL) { subtitle_update_dialog_widgets(ud, subsettings); } } void ghb_reset_subtitles(signal_user_data_t *ud, GValue *settings) { GValue *slist; GValue *subtitle; gint count, ii; gint title_id, titleindex; const hb_title_t *title; g_debug("ghb_reset_subtitles"); ghb_clear_subtitle_list_settings(ud->settings); ghb_clear_subtitle_list_ui(ud->builder); title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (title == NULL) return; slist = ghb_settings_get_value(settings, "subtitle_list"); count = ghb_array_len(slist); for (ii = 0; ii < count; ii++) { subtitle = ghb_value_dup(ghb_array_get_nth(slist, ii)); subtitle_add_to_settings(ud->settings, subtitle); } subtitle_refresh_list_ui(ud); } G_MODULE_EXPORT void subtitle_def_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); ghb_clear_presets_selection(ud); } static void subtitle_update_pref_lang(signal_user_data_t *ud, const iso639_lang_t *lang) { GtkLabel *label; GtkButton *button; gchar *str; const char * name = _("None"); const char * code = "und"; label = GTK_LABEL(GHB_WIDGET(ud->builder, "subtitle_preferred_language")); if (lang != NULL) { code = lang->iso639_2; if (strncmp(code, "und", 4)) { name = lang->native_name && lang->native_name[0] ? lang->native_name : lang->eng_name; } } str = g_strdup_printf(_("Preferred Language: %s"), name); gtk_label_set_text(label, str); g_free(str); ghb_settings_set_string(ud->settings, "PreferredLanguage", code); button = GTK_BUTTON(GHB_WIDGET(ud->builder, "SubtitleAddForeignAudioSubtitle")); str = g_strdup_printf(_("Add %s subtitle track if default audio is not %s"), name, name); gtk_button_set_label(button, str); g_free(str); // If there is no preferred language, hide options that require // a preferred language to be set. gboolean visible = !(lang == NULL || !strncmp(code, "und", 4)); gtk_widget_set_visible(GTK_WIDGET(button), visible); button = GTK_BUTTON(GHB_WIDGET(ud->builder, "SubtitleAddForeignAudioSearch")); gtk_widget_set_visible(GTK_WIDGET(button), visible); } void ghb_subtitle_set_pref_lang(GValue *settings) { GValue *lang_list; gboolean set = FALSE; lang_list = ghb_settings_get_value(settings, "SubtitleLanguageList"); if (ghb_array_len(lang_list) > 0) { GValue *glang = ghb_array_get_nth(lang_list, 0); if (glang != NULL) { ghb_settings_set_string(settings, "PreferredLanguage", g_value_get_string(glang)); set = TRUE; } } if (!set) { ghb_settings_set_string(settings, "PreferredLanguage", "und"); } } G_MODULE_EXPORT void subtitle_add_lang_clicked_cb(GtkWidget *widget, signal_user_data_t *ud) { GtkListBox *avail = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "subtitle_avail_lang")); GtkListBox *selected = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "subtitle_selected_lang")); GtkListBoxRow *row; GtkWidget *label; row = gtk_list_box_get_selected_row(avail); if (row != NULL) { int idx; const iso639_lang_t *lang; GValue *glang, *lang_list; // Remove from UI available language list box label = gtk_bin_get_child(GTK_BIN(row)); g_object_ref(G_OBJECT(label)); gtk_widget_destroy(GTK_WIDGET(row)); gtk_widget_show(label); // Add to UI selected language list box gtk_list_box_insert(selected, label, -1); // Add to preset language list idx = (intptr_t)g_object_get_data(G_OBJECT(label), "lang_idx"); lang = ghb_iso639_lookup_by_int(idx); glang = ghb_string_value_new(lang->iso639_2); lang_list = ghb_settings_get_value(ud->settings, "SubtitleLanguageList"); if (ghb_array_len(lang_list) == 0) { subtitle_update_pref_lang(ud, lang); } ghb_array_append(lang_list, glang); ghb_clear_presets_selection(ud); } } G_MODULE_EXPORT void subtitle_remove_lang_clicked_cb(GtkWidget *widget, signal_user_data_t *ud) { GtkListBox *avail, *selected; GtkListBoxRow *row; GtkWidget *label; avail = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "subtitle_avail_lang")); selected = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "subtitle_selected_lang")); row = gtk_list_box_get_selected_row(selected); if (row != NULL) { gint index; GValue *lang_list; index = gtk_list_box_row_get_index(row); // Remove from UI selected language list box label = gtk_bin_get_child(GTK_BIN(row)); g_object_ref(G_OBJECT(label)); int idx = (intptr_t)g_object_get_data(G_OBJECT(label), "lang_idx"); gtk_widget_destroy(GTK_WIDGET(row)); gtk_widget_show(label); // Add to UI available language list box gtk_list_box_insert(avail, label, idx); // Remove from preset language list lang_list = ghb_settings_get_value(ud->settings, "SubtitleLanguageList"); GValue *glang = ghb_array_get_nth(lang_list, index); ghb_array_remove(lang_list, index); ghb_value_free(glang); ghb_clear_presets_selection(ud); if (ghb_array_len(lang_list) > 0) { const iso639_lang_t *lang; GValue *entry = ghb_array_get_nth(lang_list, 0); lang = ghb_iso639_lookup_by_int(ghb_lookup_audio_lang(entry)); subtitle_update_pref_lang(ud, lang); } else { subtitle_update_pref_lang(ud, NULL); } } } static void subtitle_def_lang_list_clear_cb(GtkWidget *row, gpointer data) { GtkListBox *avail = (GtkListBox*)data; GtkWidget *label = gtk_bin_get_child(GTK_BIN(row)); g_object_ref(G_OBJECT(label)); gtk_widget_destroy(GTK_WIDGET(row)); gtk_widget_show(label); int idx = (intptr_t)g_object_get_data(G_OBJECT(label), "lang_idx"); gtk_list_box_insert(avail, label, idx); } static void subtitle_def_selected_lang_list_clear(signal_user_data_t *ud) { GtkListBox *avail, *selected; avail = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "subtitle_avail_lang")); selected = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "subtitle_selected_lang")); gtk_container_foreach(GTK_CONTAINER(selected), subtitle_def_lang_list_clear_cb, (gpointer)avail); } static void subtitle_def_lang_list_init(signal_user_data_t *ud) { GValue *lang_list; // Clear selected languages. subtitle_def_selected_lang_list_clear(ud); lang_list = ghb_settings_get_value(ud->settings, "SubtitleLanguageList"); if (lang_list == NULL) { lang_list = ghb_array_value_new(8); ghb_settings_set_value(ud->settings, "SubtitleLanguageList", lang_list); } int ii, count; count = ghb_array_len(lang_list); for (ii = 0; ii < count; ) { GValue *lang_val = ghb_array_get_nth(lang_list, ii); int idx = ghb_lookup_audio_lang(lang_val); if (ii == 0) { const iso639_lang_t *lang; lang = ghb_iso639_lookup_by_int(idx); subtitle_update_pref_lang(ud, lang); } GtkListBox *avail, *selected; GtkListBoxRow *row; avail = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "subtitle_avail_lang")); selected = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "subtitle_selected_lang")); row = ghb_find_lang_row(avail, idx); if (row) { GtkWidget *label = gtk_bin_get_child(GTK_BIN(row)); g_object_ref(G_OBJECT(label)); gtk_widget_destroy(GTK_WIDGET(row)); gtk_widget_show(label); gtk_list_box_insert(selected, label, -1); ii++; } else { // Error in list. Probably duplicate languages. Remove // this item from the list. GValue *glang = ghb_array_get_nth(lang_list, ii); ghb_array_remove(lang_list, ii); ghb_value_free(glang); count--; } } if (count == 0) { subtitle_update_pref_lang(ud, NULL); } } void ghb_subtitle_defaults_to_ui(signal_user_data_t *ud) { subtitle_def_lang_list_init(ud); } void ghb_init_subtitle_defaults_ui(signal_user_data_t *ud) { GtkListBox *list_box; list_box = GTK_LIST_BOX(GHB_WIDGET(ud->builder, "subtitle_avail_lang")); ghb_init_lang_list_box(list_box); } G_MODULE_EXPORT void subtitle_edit_clicked_cb(GtkWidget *widget, gchar *path, signal_user_data_t *ud) { GtkTreeView *tv; GtkTreePath *tp; GtkTreeModel *tm; GtkTreeSelection *ts; GtkTreeIter ti; tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list")); ts = gtk_tree_view_get_selection(tv); tm = gtk_tree_view_get_model(tv); tp = gtk_tree_path_new_from_string (path); if (gtk_tree_path_get_depth(tp) > 1) return; if (gtk_tree_model_get_iter(tm, &ti, tp)) { GValue *subsettings, *backup; gtk_tree_selection_select_iter(ts, &ti); // Back up settings in case we need to revert. backup = ghb_value_dup( ghb_settings_get_value(ud->settings, "subtitle_list")); // Pop up the edit dialog GtkResponseType response; GtkWidget *dialog = GHB_WIDGET(ud->builder, "subtitle_dialog"); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); if (response != GTK_RESPONSE_OK) { ghb_settings_take_value(ud->settings, "subtitle_list", backup); subsettings = subtitle_get_selected_settings(ud, NULL); if (subsettings != NULL) { subtitle_update_dialog_widgets(ud, subsettings); } subtitle_refresh_list_ui(ud); } else { ghb_value_free(backup); } } } G_MODULE_EXPORT void subtitle_remove_clicked_cb(GtkWidget *widget, gchar *path, signal_user_data_t *ud) { GtkTreeView *tv; GtkTreePath *tp; GtkTreeModel *tm; GtkTreeSelection *ts; GtkTreeIter ti, nextIter; gint row; gint *indices; GValue *subtitle_list; tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list")); ts = gtk_tree_view_get_selection(tv); tm = gtk_tree_view_get_model(tv); tp = gtk_tree_path_new_from_string (path); if (gtk_tree_path_get_depth(tp) > 1) return; if (gtk_tree_model_get_iter(tm, &ti, tp)) { nextIter = ti; if (!gtk_tree_model_iter_next(tm, &nextIter)) { nextIter = ti; if (gtk_tree_model_get_iter_first(tm, &nextIter)) { gtk_tree_selection_select_iter(ts, &nextIter); } } else { gtk_tree_selection_select_iter(ts, &nextIter); } subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list"); // Get the row number indices = gtk_tree_path_get_indices (tp); row = indices[0]; if (row < 0 || row >= ghb_array_len(subtitle_list)) { gtk_tree_path_free(tp); return; } // Update our settings list before removing the row from the // treeview. Removing from the treeview sometimes provokes an // immediate selection change, so the list needs to be up to date // when this happens. GValue *old = ghb_array_get_nth(subtitle_list, row); ghb_array_remove(subtitle_list, row); ghb_value_free(old); // Remove the selected item gtk_tree_store_remove(GTK_TREE_STORE(tm), &ti); ghb_live_reset(ud); } gtk_tree_path_free(tp); } HandBrake-0.10.2/gtk/src/quotestring.py0000664000175200017520000000277412067154516020372 0ustar handbrakehandbrake#! /usr/bin/python import re import getopt import sys def usage(): print >> sys.stderr, ( "Usage: %s [output]\n" "Summary:\n" " Creates a quoted string suitable for inclusion in a C char*\n\n" "Options:\n" " Input file to quote\n" " Output quoted string [stdout]\n" % sys.argv[0] ) def main(): global inc_list OPTS = "" try: opts, args = getopt.gnu_getopt(sys.argv[1:], OPTS) except getopt.GetoptError, err: print >> sys.stderr, str(err) usage() sys.exit(2) for o, a in opts: usage() assert False, "unhandled option" if len(args) > 2 or len(args) < 1: usage() sys.exit(2) try: infile = open(args[0]) except Exception, err: print >> sys.stderr, ( "Error: %s" % str(err) ) sys.exit(1) if len(args) > 1: try: outfile = open(args[1], "w") except Exception, err: print >> sys.stderr, ( "Error: %s" % str(err)) sys.exit(1) else: outfile = sys.stdout ss = infile.read() ss = re.sub("\"", "\\\"", ss) pattern = re.compile("$", re.M) # the replacement string below seems a bit strange, but it seems to be # the only way to get the litteral chars '\' 'n' inserted into the string ss = re.sub(pattern, "\\\\n\"", ss) pattern = re.compile("^", re.M) ss = re.sub(pattern, "\"", ss) outfile.write(ss) main() HandBrake-0.10.2/gtk/src/ghb.ui0000664000175200017520000226553612505052323016532 0ustar handbrakehandbrake True False Render the subtitle over the video. The subtitle will be part of the video and can not be disabled. <b>Burned In</b> True True False Set the default output subtitle track. Most players will automatically display this subtitle track whenever the video is played. This is useful for creating a "forced" track in your output. <b>Default</b> True True False Use only subtitles that have been flagged as forced in the source subtitle track "Forced" subtitles are usually used to show subtitles during scenes where someone is speaking a foreign language. <b>Forced Only</b> True True False Add (or subtract) an offset (in milliseconds) to the start of the SRT subtitle track. Often, the start of an external SRT file does not coincide with the start of the video. This setting allows you to synchronize the files. <b>SRT Offset</b> True True False The source subtitle track You can choose any of the subtitles recognized in your source file. In addition, there is a special track option "Foreign Audio Search". This option will add an extra pass to the encode that searches for subtitles that may correspond to a foreign language scene. This option is best used in conjunction with the "Forced" option. <b>Track</b> True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 800 600 utility True True hb_window vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False False True 5 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK in 600 600 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False char False True True 2 1 100 1 1 1 4 64 16 1 10 -6 6 1 10 -6 6 1 10 1000 1 10 1000 1 10 1000 1 10 1000 1 10 8000 2 16 8000 2 16 1 10 1 1 1 1 100 100 1 1 4 15 1 1 15 240 15 15 15 2 1 0.1 0.5 1 0.05 0.5 1 100 1 1 1 4096 1 16 4096 1 16 1 10 1 1 1 0.9 4 0.1 1 1 65535 1 16 20000 10 100 1 65535 1 16 -30000 30000 10 100 8000 2 16 8000 2 16 2 1 0.1 0.5 -20 21 1 1 50000 100 1000 51 20.25 0.25 5 1 16 3 1 1 16 1 1 10 0.1 1 False 5 About HandBrake dialog True True HandBrake 0.9.2 Copyright © 2008 - 2015 John Stebbins Copyright © 2004 - 2015, HandBrake Devs HandBrake is a GPL-licensed, multiplatform, multithreaded video transcoder. http://handbrake.fr http://handbrake.fr HandBrake is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. HandBrake is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Glade; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ghb author: John Stebbins HandBrake authors: Eric Petit Laurent Aimar John Allen Joe Crain Damiano Galassi Edward Groenendaal Rodney Hester Andrew Kimpton Chris Lee Chris Long Brian Mario Maurj Mirkwood Nyx Philippe Rigaux Jonathon Rubin Scott Chris Thoman Mark Krenek Van Jacobson libavcodec authors: Fabrice Bellard Alex Beregszaszi Brian Foley Arpad Gereoffy Philip Gladstone Falk Hueffner Zdenek Kabelac Nick Kurshev Michael Niedermayer François Revol Dieter Shirley Juan J. Sierralta Lionel Ulmer libdts authors: Gildas Bazin Sam Hocevar libdvdcss authors: Billy Biggs Stéphane Borel Håkan Hjort Samuel Hocevar Eugenio Jarosiewicz Jon Lech Johansen Markus Kuespert Pascal Levesque Steven M. Schultz David Siebörger Alex Strelnikov German Tischler Gildas Bazin libdvdread authors: Björn Englund Håkan Hjort Billy Biggs Christian Wolff libfaac authors: M. Bakker Tony Lenox RageOMatic thebard Ivan Dimkovic Krzysztof Nikiel libmp3lame authors: Mike Cheng Robert Hegemann Frank Klemm Alexander Leidinger Naoki Shibata Mark Taylor Takehiro Tominiga Iván Cavero Belaunde Gabriel Bouvigne Florian Bomers CISC John Dahlstrom John Dee Albert Faber Peter Gubanov Lars Magne Ingebrigtsen Yosi Markovich Zdenek Kabelac Iwasa Kazmi Guillaume Lessard Steve Lhomme Don Melton Viral Shah Acy Stapp Roel VdB libmp4v2 authors: Dave Mackie Alix Marchandise-Franquet Bill May Massimo Villari Waqar Mohsin Richard Chen Rob Arnold Howdy Pierce Steven Schultz sergent@io.com Sean Gilligan Michael Rossberg Luis F. Ramirez Petter Reinholdtsen libmpeg2 authors: Aaron Holtzman Michel Lespinasse Bruno Barreyra Gildas Bazin Alexander W. Chin Stephen Crowley Didier Gautheron Ryan C. Gordon Peter Gubanov Håkan Hjort Nicolas Joly Gerd Knorr David I. Lehn Olie Lho Rick Niles Real Ouellet Bajusz Peter Franck Sicard Brion Vibber Martin Vogt Fredrik Vraalsen libogg authors: Christopher Montgomery libsamplerate authors: Erik de Castro Lopo libvorbis authors: Christopher Montgomery libx264 authors: Laurent Aimar hb-icon True True False 2 True False end False True end 0 True False _Minimize/Maximize True False True _Pause Queue True False True _Quit True False True _About True False True False GDK_VISIBILITY_NOTIFY_MASK HandBrake 500 400 hb-icon vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False True False _File True True False _Source True False True Single _Title True False True _Destination True False True True False _Preferences True False True True False _Quit True False True True False _Queue True True False _Add True False True Add _Multiple True False True _Start True False True _Pause True False True True False _View True True False HandBrake For _Dumbies False False True _Show Presets True False True _Preview True False True _Activity Window True False True Show _Queue True False True True False _Presets True True False _Save True False True _Delete True False True _Make Default True False True _New Folder True False True _Export True False True _Import True False True _Update Built-in Presets True False True True False _Help True True False _About True False True _Guide True False True False True 0 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical True False True 12 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False 5 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Choose Video Source True Source hb-source False True True False False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Start Encoding True Start hb-start False True True False False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Pause Encoding True Pause hb-pause False True True False False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Add to Queue True Enqueue hb-add-queue False True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Show Queue True Queue hb-queue False True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Open Picture Settings and Preview window. Here you can adjust cropping, resolution, aspect ratio, and filters. True Preview hb-picture False True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Show Activity Window True Activity hb-activity False True False True 0 vertical True GTK_ALIGN_FILL True False 6 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 6 6 True False <b>Source:</b> True False True 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start None True True 1 400 10 False center GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False True 2 False True 0 True False True 5 True GTK_ALIGN_FILL 6 True False end Title: 0 0 1 1 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GTK_ALIGN_FILL 5 True GTK_ALIGN_CENTER False start Set the title to encode. By default the longest title is chosen. This is often the feature title of a DVD. False True True 0 True False end Angle: False False 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK For multi-angle DVD's, select the desired angle to encode. False False adjustment27 end False True 2 Reset All Titles True True Apply current settings to all titles True end False True 3 0 1 1 1 start center horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 True GTK_ALIGN_CENTER False Range of title to encode. Can be chapters, seconds, or frames. False True 0 10 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the first chapter to encode. False False adjustment1 True False True 1 True False through False True 2 10 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the last chapter to encode. False False adjustment2 True False True 3 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 7 True False start Duration: False True 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start hh:mm:ss 8 False True 1 False True 4 1 1 1 1 True True 1 True False <b>Destination</b> GTK_ALIGN_START True 6 False False 2 horizontal True False 6 6 True False 6 True False end File: 0 0 1 1 True True Destination filename for your encode. 40 False False 0 1 1 1 True False Destination directory for your encode. select-folder False Destination Directory 1 1 1 1 False True 0 True False 4 horizontal True False 4 True False Format: False False 0 True GTK_ALIGN_CENTER False Format to mux encoded tracks to. False True 1 0 0 1 1 iPod 5G Support True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Add iPod Atom needed by some older iPods. start True 0 1 1 1 Web optimized True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Optimize the layout of the MP4 file for progressive download. This allows a player to initiate playback before downloading the entire file. start True 1 0 1 1 Large file (>4GB) True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Allow 64 bit MP4 file which can be over 4GB. <b>Caution:</b> This option may break device compatibility. start True 1 1 1 1 False True 1 False True 3 False True 1 True True 0 False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 none 6 6 True True etched-in 6 200 206 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Presets List</b> True False True 1 False True 1 True GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT 400 True True vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK SettingsStack True GTK_ALIGN_FILL 12 12 False False 0 True GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT 400 True True 12 12 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 3 vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 10 10 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 none True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True 6 2 12 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Source Codec: 0 0 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK -- 0 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Dimensions: 1 0 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK -- 1 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Aspect: 2 0 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK -- 2 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Frame Rate: 3 0 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK -- 3 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Source Picture Parameters</b> True False True 2 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 none True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True 6 2 12 2 True False start Autocrop: 0 0 1 1 True False On 0 1 1 1 True False start Crop: 1 0 1 1 True False -- 1 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Crop Dimensions: 2 0 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK -- 2 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Cropping</b> True False True 2 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 none True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True 6 2 12 2 True False start Scale Dimensions: 0 0 1 1 True False -- 0 1 1 1 True False start Optimal for Source: 1 0 1 1 True False On 1 1 1 1 True False start Anamorphic: 2 0 1 1 True False On 2 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Scaling</b> True False True 2 2 True True 0 vertical True False center 12 12 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 448 200 True False end 4 4 4 128 hb-icon False False 1 horizontal True False 4 center start True False Presentation Dimensions: right False True 0 85 True False start start -- False True 1 False False 2 True True 1 0 summary_tab Summary vertical True False 16 16 16 horizontal True False 16 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 none vertical True False 6 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Left Crop False False adjustment13 1 0 1 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Top Crop False False adjustment14 0 1 1 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Bottom Crop False False adjustment15 2 1 1 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Right Crop False False adjustment16 1 2 1 1 Auto Crop True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Automatically crop black borders around edges of the video. start True 3 0 2 1 Loose Crop True True False When picture settings require that the image dimensions be rounded to some multiple number of pixels, this setting will crop a few extra pixels instead of doing exact cropping and then scaling to the required multiple. start True 4 0 2 1 False True 0 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Crop Dimensions: False True 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK -- True True 1 True True 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Cropping</b> True False True 2 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 none vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK width: False True 0 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK This is the width that the video will be stored at. The actual display dimensions will differ if the pixel aspect ratio is not 1:1. False False adjustment17 True False True 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK height: False True 2 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK This is the height that the video will be stored at. The actual display dimensions will differ if the pixel aspect ratio is not 1:1. False False adjustment18 False True 3 False True 0 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Optimal for source True True False If enabled, select the 'optimal' storage resolution. This will be the resolution that most closely matches the source resolution after cropping. start True True True 0 False True 1 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 True False start Anamorphic: False True 0 True GTK_ALIGN_CENTER False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Anamorphic Modes:</b> <small><tt> None - Force pixel aspect ratio to 1:1. Loose - Align dimensions to chosen 'Alignment' value and pick pixel aspect ratio that preserves the original display aspect ratio Strict - Keep original source dimensions and pixel aspect ratio</tt></small> False True 1 False True 2 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 True False start Alignment: False True 0 True GTK_ALIGN_CENTER False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Align storage dimensions to multiples of this value. This setting is only necessary for compatibility with some devices. You should use 2 unless you experience compatibility issues. False True 1 False True 3 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Storage Geometry</b> True False True 2 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 none vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK width: False True 0 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK This is the display width. It is the result of scaling the storage dimensions by the pixel aspect. False False adjustment25 True False True 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK height: False True 2 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False adjustment26 False True 3 False True 0 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Pixel Aspect: False True 0 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Pixel aspect defines the shape of the pixels. A 1:1 ratio defines a square pixel. Other values define rectangular shapes. Players will scale the image in order to achieve the specified aspect. False False adjustment29 True False True 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK : False True 2 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Pixel aspect defines the shape of the pixels. A 1:1 ratio defines a square pixel. Other values define rectangular shapes. Players will scale the image in order to achieve the specified aspect. False False adjustment30 False True 3 False True 1 Keep Aspect True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK If enabled, the original display aspect of the source will be maintained. start True True False True 2 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 100 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Display Aspect: False True 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK --:-- False True 1 False True 3 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Display Geometry</b> True False True 2 2 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 none horizontal True False 16 True False 5 Grayscale True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK If enabled, filter colour components out of video. start True 0 0 2 1 True False start Deblock: 1 0 1 1 True 100 True The deblocking filter removes a common type of compression artifact. If your source exhibits 'blockiness', this filter may help clean it up. adjustment20 0 right 1 1 1 1 False True 2 0 True False 5 True False start Denoise Filter: 1 0 1 1 GTK_ALIGN_CENTER 100 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Denoise filtering reduces or removes the appearance of noise and grain. Film grain and other types of high frequency noise are difficult to compress. Using this filter on such sources can result in smaller file sizes. 1 1 1 1 True False start Denoise Preset: 2 0 1 1 GTK_ALIGN_CENTER 100 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Denoise filtering reduces or removes the appearance of noise and grain. Film grain and other types of high frequency noise are difficult to compress. Using this filter on such sources can result in smaller file sizes. 2 1 1 1 True False start Denoise Tune: 3 0 1 1 GTK_ALIGN_CENTER 100 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Denoise filtering reduces or removes the appearance of noise and grain. Film grain and other types of high frequency noise are difficult to compress. Using this filter on such sources can result in smaller file sizes. 3 1 1 1 True Custom denoise filter string format SpatialLuma:SpatialChroma:TemporalLuma:TemporalChroma 8 False False 3 1 1 1 False True 2 1 True False 5 True False start Detelecine: 0 0 1 1 GTK_ALIGN_CENTER 100 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK This filter removes 'combing' artifacts that are the result of telecining. Telecining is a process that adjusts film framerates that are 24fps to NTSC video frame rates which are 30fps. 0 1 1 1 True Custom detelecine filter string format JunkLeft:JunkRight:JunkTop:JunkBottom:StrictBreaks:MetricPlane:Parity 8 False False 1 1 1 1 Decomb True True False Choose decomb or deinterlace filter options. The decomb filter selectively deinterlaces frames that appear to be interlaced. This will preserve quality in frames that are not interlaced. The classic deinterlace filter is applied to all frames. Frames that are not interlaced will suffer some quality degradation. start True True 2 0 1 1 Deinterlace True True False Choose decomb or deinterlace filter options. The decomb filter selectively deinterlaces frames that appear to be interlaced. This will preserve quality in frames that are not interlaced. The classic deinterlace filter is applied to all frames. Frames that are not interlaced will suffer some quality degradation. start True PictureDecombDeinterlace 2 1 1 1 True False start Decomb: 3 0 1 1 GTK_ALIGN_CENTER 100 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK The decomb filter selectively deinterlaces frames that appear to be interlaced. This will preserve quality in frames that are not interlaced. 3 1 1 1 True Custom decomb filter string format Mode:SpatialMetric:MotionThresh:SpatialThresh:BlockThresh:BlockWidth: BlockHeight:MagnitudeThres:VarianceThres:LaplacianThresh:DilationThresh: ErosionThresh:NoiseThresh:MaxSearchDistance:PostProcessing:Parity 8 False False 4 1 1 1 True False start Deinterlace: 5 0 1 1 GTK_ALIGN_CENTER 100 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK The classic deinterlace filter is applied to all frames. Frames that are not interlaced will suffer some quality degradation. 5 1 1 1 True Custom deinterlace filter string format YadifMode:YadifParity:McdintMode:McdeintQp 8 False False 6 1 1 1 False True 2 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Filters</b> True 1 1 picture_tab Picture vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 48 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 2 12 24 True False start Video Encoder: 0 0 1 1 True GTK_ALIGN_CENTER False Available video encoders. 0 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Framerate: 1 0 1 1 True GTK_ALIGN_CENTER False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Output framerate. 'Same as source' is recommended. If your source video has a variable framerate, 'Same as source' will preserve it. 1 1 1 1 Constant Framerate True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enables constant framerate output. start True 2 0 2 1 Peak Framerate (VFR) True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enables variable framerate output with a peak rate determined by the framerate setting. VFR is not compatible with some players. start True VideoFramerateCFR 3 0 2 1 Variable Framerate True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enables variable framerate output. VFR is not compatible with some players. start True VideoFramerateCFR 3 0 2 1 False True 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 2 12 24 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the desired quality factor. The encoder targets a certain quality. The scale used by each video encoder is different. x264's scale is logarithmic and lower values correspond to higher quality. So small decreases in value will result in progressively larger increases in the resulting file size. A value of 0 means lossless and will result in a file size that is larger than the original source, unless the source was also lossless. FFMpeg's and Theora's scale is more linear. These encoders do not have a lossless mode. adjustment5 3 True 0 0 3 1 Constant Quality: True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the desired quality factor. The encoder targets a certain quality. The scale used by each video encoder is different. x264's scale is logarithmic and lower values correspond to higher quality. So small decreases in value will result in progressively larger increases in the resulting file size. A value of 0 means lossless and will result in a file size that is larger than the original source, unless the source was also lossless. FFMpeg's and Theora's scale is more linear. These encoders do not have a lossless mode. start True True 1 0 1 1 Bitrate (kbps): True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the average bitrate. The instantaneous bitrate can be much higher or lower at any point in time. But the average over a long duration will be the value set here. If you need to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings. start True vquality_type_constant 2 0 1 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the average bitrate. The instantaneous bitrate can be much higher or lower at any point in time. But the average over a long duration will be the value set here. If you need to limit instantaneous bitrate, look into x264's vbv-bufsize and vbv-maxrate settings. False False adjustment3 2 1 1 1 2-Pass Encoding True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Perform 2 Pass Encoding. The 'Bitrate' option is prerequisite. During the 1st pass, statistics about the video are collected. Then in the second pass, those statistics are used to make bitrate allocation decisions. start True 3 0 1 1 Turbo First Pass True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK During the 1st pass of a 2 pass encode, use settings that speed things along. start True True 3 1 1 1 True True 2 1 False True 0 0 vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 24 24 Use Advanced Options True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Use advanced options Tab for x264 settings. Use at your own risk! start True False True 2 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Preset: 0 0 1 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Adjusts encoder settings to trade off compression efficiency against encoding speed. This establishes your default encoder settings. Tunes, profiles, levels and advanced option string will be applied to this. You should generally set this option to the slowest you can bear since slower settings will result in better quality or smaller files. VideoPresetRange 0 right True 0 1 5 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Tune: 8 8 1 0 1 1 True GTK_ALIGN_CENTER False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Tune settings to optimize for common scenarios. This can improve effeciency for particular source characteristics or set characteristics of the output file. Changes will be applied after the preset but before all other parameters. 1 1 1 1 Fast Decode True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Reduce decoder CPU usage. Set this if your device is struggling to play the output (dropped frames). start 2 True 1 2 1 1 False Zero Latency True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Minimize latency between input to encoder and output of decoder. This is useful for broadcast of live streams. Since HandBrake is not suitable for live stream broadcast purposes, this setting is of little value here. start 2 True 1 3 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Profile: 8 8 2 0 1 1 True GTK_ALIGN_CENTER False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Sets and ensures compliance with the specified profile. Overrides all other settings. 2 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Level: 8 8 3 0 1 1 True GTK_ALIGN_CENTER False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Sets and ensures compliance with the specified level. Overrides all other settings. 3 1 1 1 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start More Settings: False True 5 0 True True etched-in True True True Additional encoder settings. Colon separated list of encoder options. char False True True 1 2 2 4 2 True True 0 1 True True 2 1 2 video_tab Video True False vertical 12 12 12 12 True True False horizontal 6 True False vertical True False 5 2 True False end Selection Behavior: right 0 0 1 1 True GTK_ALIGN_CENTER False 1 0 1 1 True 0 True False 5 2 GTK_ALIGN_END 6 6 True True GTK_POLICY_NEVER GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 84 True False 0 1 1 4 True True GTK_POLICY_NEVER GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False 2 1 1 4 Add True True True GTK_ALIGN_CENTER 1 2 1 1 Remove True True True GTK_ALIGN_CENTER 1 3 1 1 True False Available Languages 0 0 1 1 True False Selected Languages 2 0 1 1 True True 1 Use only first encoder for secondary audio True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Only the primary audio track will be encoded with the full encoder list. All other secondary audio output tracks will be encoded with first encoder only. start True True 2 False True 0 True False vertical 6 2 True False 5 2 end True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Auto Passthru: 0 0 1 1 MP3 True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enable this if your playback device supports MP3. This permits MP3 passthru to be selected when automatic passthru selection is enabled. start True True 1 0 1 1 AAC True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enable this if your playback device supports AAC. This permits AAC passthru to be selected when automatic passthru selection is enabled. start True True 2 0 1 1 AC-3 True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enable this if your playback device supports AC-3. This permits AC-3 passthru to be selected when automatic passthru selection is enabled. start True True 1 1 1 1 DTS True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enable this if your playback device supports DTS. This permits DTS passthru to be selected when automatic passthru selection is enabled. start True True 1 2 1 1 DTS-HD True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enable this if your playback device supports DTS-HD. This permits DTS-HD passthru to be selected when automatic passthru selection is enabled. start True True 2 2 1 1 True 0 True False horizontal 6 end True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end True Passthru Fallback: True 0 True GTK_ALIGN_CENTER False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the audio codec to encode with when a suitable track can not be found for audio passthru. True 1 True 1 True 1 0 True False 6 start True <b>Audio Encoder Settings:</b> Each selected source track will be encoded with all selected encoders 2 True False 3 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False Encoder False False 0 True False Bitrate/Quality False False 1 True False Mixdown False False 2 True False Samplerate False False 3 True False Gain False False 4 True False DRC False False 5 4 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True False True none False True 5 3 audio_defaults_tab Audio Defaults vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 6 6 6 6 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True False True Add list-add Add new audio settings to the list False True True False True Add All list-add Add all audio tracks to the list False True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Reload Defaults emblem-default Reload all audio settings from defaults False False True 0 True True GTK_POLICY_NEVER GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False True True 2 True True 0 4 audio_list_tab Audio List True False vertical 12 12 12 12 True True False horizontal 6 True False vertical True False 5 2 True False end Selection Behavior: right 0 0 1 1 True GTK_ALIGN_CENTER False 1 0 1 1 True 0 True False 5 2 GTK_ALIGN_END 6 6 True True GTK_POLICY_NEVER GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 108 True False 0 1 1 4 True True GTK_POLICY_NEVER GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False 2 1 1 4 Add True True True GTK_ALIGN_CENTER 1 2 1 1 Remove True True True GTK_ALIGN_CENTER 1 3 1 1 True False Available Languages 0 0 1 1 True False Selected Languages 2 0 1 1 True False end Preferred Language: None right 3 1 1 1 True True 1 False True 0 0 Add Foreign Audio Search Pass True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Add "Foreign Audio Search" when the default audio track is your preferred language. This search pass finds short sequences of foreign audio and provides subtitles for them. start True True 1 Add subtitle track if default audio is foreign True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK When the default audio track is not your preferred language, add a subtitle track. start True True 2 Add Closed Captions when available True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Closed captions are text subtitles that can be added to any container as a soft subtitle track (not burned) start True 5 subtitle_defaults_tab Subtitle Defaults vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 6 6 6 6 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True False True Add list-add Add new subtitle settings to the list False True True False True Add All list-add Add all subtitle tracks to the list False True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Reload Defaults emblem-default Reload all subtitle settings from defaults False False True 0 True True GTK_POLICY_NEVER GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False True True 1 True True 0 6 subtitle_list_tab Subtitle List horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 none True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 2 6 2 12 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <small>Reference Frames:</small> True 0 0 1 1 center True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Sane values are ~1-6. The more you add, the better the compression, but the slower the encode. Cel animation tends to benefit from more reference frames a lot more than film content. Note that many hardware devices have limitations on the number of supported reference frames, so if you're encoding for a handheld or standalone player, don't touch this unless you're absolutely sure you know what you're doing! False False adjustment8 0 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <small>Maximum B-Frames:</small> True 1 0 1 1 center True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Sane values are ~2-5. This specifies the maximum number of sequential B-frames that the encoder can use. Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal. Cel-animated source material and B-pyramid also significantly increase the usefulness of larger values. Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off). False False adjustment9 1 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <small>Pyramidal B-Frames:</small> True 2 0 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK B-pyramid improves compression by creating a pyramidal structure (hence the name) of B-frames, allowing B-frames to reference each other to improve compression. Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit. 2 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <small>Weighted P-Frames:</small> True 3 0 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Performs extra analysis to decide upon weighting parameters for each frame. This improves overall compression slightly and improves the quality of fades greatly. Baseline profile, as required for iPods and similar devices, requires weighted P-frame prediction to be disabled. Note that some devices and players, even those that support Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is completely incompatible with it, for example. 3 1 1 1 8x8 Transform True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed. It improves compression by at least 5% at a very small speed cost and may provide an unusually high visual quality benefit compared to its compression gain. However, it requires High Profile, which many devices may not support. start True True 4 0 2 1 CABAC Entropy Encoding True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK After the encoder has done its work, it has a bunch of data that needs to be compressed losslessly, similar to ZIP or RAR. H.264 provides two options for this: CAVLC and CABAC. CABAC decodes a lot slower but compresses significantly better (10-30%), especially at lower bitrates. If you're looking to minimize CPU requirements for video playback, disable this option. Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled. start True True 5 0 2 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <small><b>Encoding Features</b></small> True False True 1 True True 0 vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 none horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <small>Motion Est. Method:</small> True 0 0 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Controls the motion estimation method. Motion estimation is how the encoder estimates how each block of pixels in a frame has moved. A better motion search method improves compression at the cost of speed. Diamond: performs an extremely fast and simple search using a diamond pattern. Hexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern. Uneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion. Exhaustive: performs a "dumb" search of every pixel in a wide area. Significantly slower for only a small compression gain. Transformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement. 0 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <small>Subpel ME &amp; Mode:</small> True 1 0 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK This setting controls both subpixel-precision motion estimation and mode decision methods. Subpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression. Mode decision is the method used to choose how to encode each block of the frame: a very important decision. SAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD. 6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD. 11 disables all early terminations in analysis. 10 and 11, the most powerful and slowest options, require adaptive quantization (aq-mode > 0) and trellis 2 (always). 1 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <small>Motion Est. Range:</small> True 2 0 1 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK This is the distance x264 searches from its initial guess at the motion of a block in order to try to find its actual motion. The default is fine for most content, but extremely high motion video, especially at HD resolutions, may benefit from higher ranges, albeit at a high speed cost. False False adjustment10 2 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <small>Adaptive Direct Mode:</small> True 3 0 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK H.264 allows for two different prediction modes, spatial and temporal, in B-frames. Spatial, the default, is almost always better, but temporal is sometimes useful too. x264 can, at the cost of a small amount of speed (and accordingly for a small compression gain), adaptively select which is better for each particular frame. 3 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <small>Adaptive B-Frames:</small> True 4 0 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK x264 has a variety of algorithms to decide when to use B-frames and how many to use. Fast mode takes roughly the same amount of time no matter how many B-frames you specify. However, while fast, its decisions are often suboptimal. Optimal mode gets slower as the maximum number of B-Frames increases, but makes much more accurate decisions, especially when used with B-pyramid. 4 1 1 1 True True 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 2 6 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <small>Partitions:</small> True 0 0 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Mode decision picks from a variety of options to make its decision: this option chooses what options those are. Fewer partitions to check means faster encoding, at the cost of worse decisions, since the best option might have been one that was turned off. 0 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <small>Trellis:</small> True 1 0 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Trellis fine-tunes the rounding of transform coefficients to squeeze out 3-5% more compression at the cost of some speed. "Always" uses trellis not only during the main encoding process, but also during analysis, which improves compression even more, albeit at great speed cost. Trellis costs more speed at higher bitrates and requires CABAC. 1 1 1 1 True True 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <small><b>Analysis</b></small> True False True 2 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 none 6 2 12 2 vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <small>Adaptive Quantization Strength:</small> True 0 0 1 1 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Adaptive quantization controls how the encoder distributes bits across the frame. Higher values take more bits away from edges and complex areas to improve areas with finer detail. adjustment34 False right 0 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <small>Psychovisual Rate Distortion:</small> True 1 0 1 1 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Psychovisual rate-distortion optimization takes advantage of the characteristics of human vision to dramatically improve apparent detail and sharpness. The effect can be made weaker or stronger by adjusting the strength. Being an RD algorithm, it requires mode decision to be at least "6". adjustment22 False right 1 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <small>Psychovisual Trellis:</small> True 2 0 1 1 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Psychovisual trellis is an experimental algorithm to further improve sharpness and detail retention beyond what Psychovisual RD does. Recommended values are around 0.2, though higher values may help for very grainy video or lower bitrate encodes. Not recommended for cel animation and other sharp-edged graphics. adjustment23 False 2 right 2 1 1 1 True True 0 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Deblocking: False True 0 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK H.264 deblocking filter. h.264 has a built-in deblocking filter that smooths out blocking artifacts after decoding each frame. This not only improves visual quality, but also helps compression significantly. The deblocking filter takes a lot of CPU power, so if you're looking to minimize CPU requirements for video playback, disable it. The deblocking filter has two adjustable parameters, "strength" (Alpha) and "threshold" (Beta). The former controls how strong (or weak) the deblocker is, while the latter controls how many (or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking. The default is 0 (normal strength) for both parameters. False False adjustment11 False True 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK H.264 deblocking filter. h.264 has a built-in deblocking filter that smooths out blocking artifacts after decoding each frame. This not only improves visual quality, but also helps compression significantly. The deblocking filter takes a lot of CPU power, so if you're looking to minimize CPU requirements for video playback, disable it. The deblocking filter has two adjustable parameters, "strength" (Alpha) and "threshold" (Beta). The former controls how strong (or weak) the deblocker is, while the latter controls how many (or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking. The default is 0 (normal strength) for both parameters. False False adjustment12 False True 2 No DCT Decimate True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK x264 normally zeroes out nearly-empty data blocks to save bits to be better used for some other purpose in the video. However, this can sometimes have slight negative effects on retention of subtle grain and dither. Don't touch this unless you're having banding issues or other such cases where you are having trouble keeping fine noise. start True True True True 20 3 True True 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <small><b>Psychovisual</b></small> True False True 1 True True 2 False True 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 none 40 12 12 True True etched-in True True Your selected options will appear here. You can edit these and add additional options. Default values will not be shown. The defaults are: ref=3:bframes=3:b-adapt=fast:direct=spatial: b-pyramid=normal:weightp=2:me=hex:merange=16: subme=7:partitions=p8x8,b8x8,i8x8,i4x4:8x8dct=1: deblock=0,0:trellis=1:psy-rd=1,0:aq-strength=1.0: no-fast-pskip=0:no-dct-decimate=0:cabac=1 char False True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <small><b>Current x264 Advanced Option String</b></small> True False True 2 1 True True 0 7 advanced_video_tab Advanced Video vertical True False horizontal True False Chapter Markers True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Add chapter markers to output file. start True False True 0 False True 0 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False True True 1 8 chapters_tab Chapters 6 6 24 24 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end Title: True 0 0 1 1 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 80 True 50 True False False 0 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end Actors: True 1 0 1 1 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 80 True 50 True False False 1 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end Director: True 2 0 1 1 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 80 True 50 True False False 2 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end Release Date: True 3 0 1 1 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 80 True 50 True False False 3 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end Comment: True 4 0 1 1 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 80 True 50 True False False 4 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end Genre: True 5 0 1 1 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 80 True 50 True False False 5 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end Description: True 6 0 1 1 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 80 True 50 True False False 6 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end Plot: True 7 0 1 1 40 True True True True etched-in True True char False 7 1 1 1 9 metadata_tab Tags True True 1 0 settings_tab Settings vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Edit hb-edit False True True False False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Reload view-refresh Mark selected queue entry as pending. Resets the queue job to pending and ready to run again. False True True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True Reload All view-refresh Mark all queue entries as pending. Resets all queue jobs to pending and ready to run again. False True False True 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 900 300 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False True True 2 1 queue_tab Queue True True 2 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start end 4 4 12 12 4 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start True True 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end False True 1 False True 3 10 False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 6 12 12 False True 4 7200 10 5 10 5 60 10 5 10 False dialog False vertical 2 False end Cancel gtk-cancel True True True False True 0 OK gtk-ok True True True False True 1 False True end 0 True False 12 12 12 6 True True GTK_POLICY_NEVER GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 400 True False True True none False 0 1 3 2 horizontal True False 6 Select All True True False Mark all titles for adding to the queue start False start True False 0 Clear All True True False Unmark all titles start False start True False 1 0 0 2 1 True True False info False 6 end False False 0 False 16 True False Destination files OK. No duplicates detected. False True 0 False False 0 0 3 3 1 False True 1 title_add_multiple_cancel title_add_multiple_ok False 5 Preferences True center-on-parent dialog True True True False 2 True False end OK gtk-ok True True True False False 0 False True end 0 horizontal True False vertical True False True False GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_STRUCTURE_MASK True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 64 hb-icon False True 0 True True 0 True True False 12 vertical True False horizontal True False 4 6 6 12 GTK_ALIGN_CENTER True False False True 0 True False Automatically check for updates True False True 1 False True 0 horizontal True False 4 6 6 12 12 True GTK_ALIGN_CENTER False False True 0 True False When all encodes are complete True False True 1 False True 1 vertical True False 6 6 12 Use automatic naming (uses modified source name) True True False start True False True 0 horizontal True False 4 18 8 True False Auto-Name Template True end False True 0 Available Options: {source} {title} {chapters} {date} {time} {quality} {bitrate} True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True 40 True False False False True 1 False True 1 Use iPod/iTunes friendly (.m4v) file extension for MP4 True True False start True False True 2 False True 2 horizontal True False 4 6 6 12 True True 2 none False False preview_count_adj True False True 0 True False Number of previews True False True 1 False True 3 horizontal True False 4 6 6 12 True True 4 none False False min_title_adj True False True 0 True False Filter short titles (seconds) True False True 1 False True 4 True False General False vertical True False 6 6 12 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 horizontal True False 4 GTK_ALIGN_CENTER 55 True False False True 0 True False start Constant Quality fractional granularity True True 1 0 0 1 1 Use dvdnav (instead of libdvdread) True True False start True 1 0 1 1 vertical True False Put individual encode logs in same location as movie True True False start True False True 0 horizontal True False 4 21 GTK_ALIGN_CENTER 55 True False False True 0 True False start Activity Log Verbosity Level True True 1 False True 1 horizontal True False 4 21 GTK_ALIGN_CENTER True False False True 0 True False start Activity Log Longevity True True 1 False True 2 2 0 1 1 Scale down High Definition previews True True False start True 3 0 1 1 Automatically Scan DVD when loaded True True False Scans the DVD whenever a new disc is loaded start True 4 0 1 1 Hide Advanced Video Options Tab True True False Use advanced video options at your own risk. We recommend that you use the controls available on the Video tab instead. start True 5 0 1 1 Delete completed jobs from queue True True False By default, completed jobs remain in the queue and are marked as complete. Check this if you want the queue to clean itself up by deleting completed jobs. start True 6 0 1 1 False True 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Allow Tweaks True False start True 0 0 1 1 Allow HandBrake For Dummies True False start True 1 0 1 1 False True 1 1 True False Advanced 1 False True True 1 True True 1 pref_ok False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 True center-on-parent dialog True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end Cancel gtk-cancel True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False 0 OK gtk-ok True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False 1 False True end 0 vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Folder Name: False True 0 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 40 True 30 True False False True True 1 False True 10 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 etched-out 6 4 12 4 60 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK word False True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Description</b> True True True 10 2 True True 1 preset_folder_cancel preset_folder_ok False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 True center-on-parent dialog True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end Cancel gtk-cancel True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False 0 OK gtk-ok True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False 1 False True end 0 vertical True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start Preset Name: False True 0 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 40 True 30 True False False True True 1 False True 10 0 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start <b>Custom Picture Dimensions</b> True 0 0 3 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK start 1 2 1 1 Maximum Width: True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enable maximum width limit. start True 1 0 1 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK This is the maximum width that the video will be stored at. Whenever a new source is loaded, this value will be applied if the source width is greater. Setting this to 0 means there is no maximum width. False False adjustment32 True 1 1 1 1 Maximum Height: True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enable maximum height limit. start True 2 0 1 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK This is the maximum height that the video will be stored at. Whenever a new source is loaded, this value will be applied if the source height is greater. Setting this to 0 means there is no maximum height. False False adjustment33 2 1 1 1 True True 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 etched-out 6 4 12 4 60 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK word False True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Description</b> True True True 10 2 True True 1 preset_cancel preset_ok True True False center center GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK 100 1 10 True True False center end 30 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK vertical True False 5 5 10 10 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True True Select preview frames. adjustment19 0 bottom False True 0 horizontal True False 5 30 True True True Encode and play a short sequence of video starting from the current preview position. none True False gtk-media-play False True 0 True preview_progress_adj False right True True 1 vertical True False 20 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False True 0 True True 2 False True 1 horizontal True False 10 horizontal True False 2 True False <b>Duration:</b> True False True 0 True True Set the duration of the live preview in seconds. False False adjustment21 True False True 1 True True 0 Show Crop True True False Show Cropped area of the preview start True False True 1 Fullscreen True True False View Fullscreen Preview none False True 2 False True 2 False GDK_POINTER_MOTION_MASK | GDK_STRUCTURE_MASK Preview False center utility True True hb_window True False False 5 True dialog True True False False hb_window True False 2 True False end Cancel gtk-cancel True True True True True False False 0 OK gtk-ok True True True True True False False 1 False True end 0 vertical True False horizontal True False 4 True False Title Number: False True 0 True True start GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False adjustment24 True True 1 False True 0 horizontal True False 4 True False Detected DVD devices: False True 0 True False start True True 1 False True 1 False True 2 source_cancel source_ok True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-cancel True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-ok True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-add True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-add False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 True center-on-parent dialog True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK end Cancel gtk-cancel True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False 0 OK gtk-ok True True True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False 1 False True end 0 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Setting: False True 0 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 40 True 30 True False False False True 1 False True 1 tweak_cancel tweak_ok False 5 True center-on-parent dialog True True True False 6 True False end Cancel gtk-cancel True True True False False 0 OK gtk-ok True True True False False 1 False True end 0 horizontal True False 24 Import SRT True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enable settings to import an SRT subtitle file start True False True 1 Embedded Subtitle List True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enable settings to select embedded subtitles start True SubtitleSrtEnable False True 1 False True 1 horizontal True False 6 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 4 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Language 0 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Character Code 0 2 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK File: end 2 0 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Offset (ms) 0 4 1 1 GTK_ALIGN_CENTER True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the language of this subtitle. This value will be used by players in subtitle menus. 1 1 1 1 GTK_ALIGN_CENTER 150 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the character code used by the SRT file you are importing. SRTs come in all flavours of character sets. We translate the character set to UTF-8. The source's character code is needed in order to perform this translation. True True 1 2 1 1 True False Select the SRT file to import. False True Srt File 2 1 2 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Adjust the offset in milliseconds between video and SRT timestamps False False adjustment31 1 4 1 1 False True 0 vertical True False True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Track False True 0 GTK_ALIGN_CENTER True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK List of subtitle tracks available from your source. False True 1 False True 1 vertical True False Forced Subtitles Only True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Use only subtitles that have been flagged as forced in the source subtitle track "Forced" subtitles are usually used to show subtitles during scenes where someone is speaking a foreign language. start True False True 0 Burn into video True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Render the subtitle over the video. The subtitle will be part of the video and can not be disabled. start True False True 1 Set Default Track True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the default output subtitle track. Most players will automatically display this subtitle track whenever the video is played. This is useful for creating a "forced" track in your output. start True False True 2 False True 2 False True 2 subtitle_cancel subtitle_ok False 5 True center-on-parent dialog True True True False 6 True False end Cancel gtk-cancel True True True False False 0 OK gtk-ok True True True False False 1 False True end 0 True False 5 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Source Track 0 0 1 1 GTK_ALIGN_CENTER True False True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK List of audio tracks available from your source. 1 0 1 1 True False Track Name: True center 0 1 1 1 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the audio track name. Players may use this in the audio selection list. 40 True True True False False 1 1 1 1 True True 1 True False 24 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Encoder 0 0 1 1 True False Bitrate/Quality True center 0 1 1 1 True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Mix 0 2 1 1 True False Sample Rate True center 0 3 1 1 True False Gain True center 0 4 1 1 True False <b>Dynamic Range Compression:</b> Adjust the dynamic range of the output audio track. For source audio that has a wide dynamic range (very loud and very soft sequences), DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder. DRC True center 0 5 1 1 True False GTK_ALIGN_CENTER GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the audio codec to encode this track with. 1 0 1 1 horizontal True False vertical True False Bitrate True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enable bitrate setting start True False True 0 Quality True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Enable quality setting start True AudioTrackBitrateEnable False True 1 False True 0 True False GTK_ALIGN_CENTER GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the bitrate to encode this track with. False True 1 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GTK_ALIGN_CENTER False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Quality:</b> For output codec's that support it, adjust the quality of the output. vertical audio_quality_adj weather-storm weather-clear weather-storm weather-showers-scattered weather-showers weather-overcast weather-few-clouds weather-clear False True 0 True False start 00.0 True 4 False True 1 False True 2 1 1 1 1 True False GTK_ALIGN_CENTER GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the mixdown of the output audio track. 1 2 1 1 True False GTK_ALIGN_CENTER GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Set the sample rate of the output audio track. 1 3 1 1 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GTK_ALIGN_CENTER False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Audio Gain:</b> Adjust the amplification or attenuation of the output audio track. vertical adjustment35 audio-volume-muted audio-volume-high audio-volume-low audio-volume-medium False True 0 True False start 0dB True 6 False True 1 1 4 1 1 horizontal True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True False GTK_ALIGN_CENTER False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK <b>Dynamic Range Compression:</b> Adjust the dynamic range of the output audio track. For source audio that has a wide dynamic range (very loud and very soft sequences), DRC allows you to 'compress' the range by making loud sounds softer and soft sounds louder. vertical adjustment28 audio-input-microphone False True 0 True False start Off True 4 False True 1 1 5 1 1 True True 2 audio_cancel audio_ok False 5 True center-on-parent dialog True True True False 2 True False end Skip This Version True True True False False 0 Remind Me Later True True True False False 1 False True end 0 horizontal True False vertical True False True False GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_STRUCTURE_MASK False True True False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 64 hb-icon False True 0 False True 0 vertical True False True False start 10 10 5 5 <b>A new version of HandBrake is available!</b> True False True 0 True False start 10 10 5 5 HandBrake xxx is now available (you have yyy). False True 1 True False 0 etched-out 12 start True True True False <b>Release Notes</b> True True True 2 True True 1 True True 1 update_skip update_remind 1 1 1 HandBrake-0.10.2/gtk/src/hb-backend.h0000664000175200017520000002032112352373116017543 0ustar handbrakehandbrake/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA */ #if !defined(_HBBACKEND_H_) #define _HBBACKEND_H_ #include "settings.h" #include "hb.h" #include "lang.h" enum { GHB_ERROR_NONE, GHB_ERROR_CANCELED, GHB_ERROR_FAIL, }; typedef struct { gint state; // SCANNING gint title_count; gint title_cur; gint preview_count; gint preview_cur; // WORKING gint unique_id; gint job_cur; gint job_count; gdouble progress; gdouble rate_cur; gdouble rate_avg; gint hours; gint minutes; gint seconds; gint error; } ghb_instance_status_t; typedef struct { ghb_instance_status_t scan; ghb_instance_status_t queue; } ghb_status_t; #define MOD_ROUND(v,m) ((m==1)?v:(m * ((v + (m>>1)) / m))) #define MOD_DOWN(v,m) (m * (v / m)) #define MOD_UP(v,m) (m * ((v + m - 1) / m)) #define GHB_PIC_KEEP_WIDTH 0x01 #define GHB_PIC_KEEP_HEIGHT 0x02 #define GHB_PIC_KEEP_DISPLAY_WIDTH 0x04 #define GHB_PIC_KEEP_DISPLAY_HEIGHT 0x08 #define GHB_PIC_KEEP_DAR 0x10 #define GHB_PIC_KEEP_PAR 0x20 #define GHB_PIC_USE_MAX 0x40 #define GHB_AUDIO_SAMPLERATE 1 #define GHB_AUDIO_BITRATE 2 #define GHB_FRAMERATE 3 const gchar* ghb_version(void); void ghb_vquality_range( signal_user_data_t *ud, float *min, float *max, float *step, float *page, gint *digits, int *direction); float ghb_vquality_default(signal_user_data_t *ud); void ghb_combo_init(signal_user_data_t *ud); void ghb_backend_init(gint debug); void ghb_backend_close(void); void ghb_add_job(GValue *js, gint unique_id); void ghb_remove_job(gint unique_id); void ghb_start_queue(void); void ghb_stop_queue(void); void ghb_pause_queue(void); void ghb_add_live_job(GValue *js, gint unique_id); void ghb_start_live_encode(); void ghb_stop_live_encode(); void ghb_clear_scan_state(gint state); void ghb_clear_queue_state(gint state); void ghb_set_state(gint state); gint ghb_get_scan_state(); gint ghb_get_queue_state(); void ghb_get_status(ghb_status_t *status); void ghb_track_status(void); void ghb_backend_scan(const gchar *path, gint titleindex, gint preview_count, guint64 min_duration); void ghb_backend_scan_stop(); void ghb_backend_queue_scan(const gchar *path, gint titleindex); hb_list_t * ghb_get_title_list(); void ghb_par_init(signal_user_data_t *ud); void ghb_set_scale(signal_user_data_t *ud, gint mode); void ghb_set_scale_settings(GValue *settings, gint mode); void ghb_picture_settings_deps(signal_user_data_t *ud); GValue* ghb_get_chapters(const hb_title_t *title); gint64 ghb_get_chapter_duration(const hb_title_t *title, gint chap); gint64 ghb_get_chapter_start(const hb_title_t *title, gint chap); void ghb_part_duration( const hb_title_t *title, gint sc, gint ec, gint *hh, gint *mm, gint *ss); gint ghb_get_best_mix(hb_audio_config_t *aconfig, gint acodec, gint mix); gboolean ghb_ac3_in_audio_list(const GValue *audio_list); gboolean ghb_audio_is_passthru(gint acodec); gboolean ghb_audio_can_passthru(gint acodec); gint ghb_get_default_acodec(void); void ghb_set_bitrate_opts( GtkBuilder *builder, gint first_rate, gint last_rate, gint extra_rate); void ghb_grey_combo_options(signal_user_data_t *ud); void ghb_update_ui_combo_box( signal_user_data_t *ud, const gchar *name, const void *user_data, gboolean all); const gchar* ghb_get_source_audio_lang(const hb_title_t *title, gint track); gint ghb_find_audio_track(const hb_title_t *title, const gchar *lang, int start); void ghb_add_all_subtitles(signal_user_data_t *ud, gint titleindex); gint ghb_find_subtitle_track(const hb_title_t * title, const gchar * lang, int start); gint ghb_pick_subtitle_track(signal_user_data_t *ud); gint ghb_longest_title(void); gchar* ghb_build_advanced_opts_string(GValue *settings); GdkPixbuf* ghb_get_preview_image( const hb_title_t *title, gint index, signal_user_data_t *ud, gint *out_width, gint *out_height); gchar* ghb_dvd_volname(const gchar *device); gint ghb_subtitle_track_source(GValue *settings, gint track); const gchar* ghb_subtitle_track_lang(GValue *settings, gint track); gboolean ghb_validate_vquality(GValue *settings); gboolean ghb_validate_audio(GValue *settings); gboolean ghb_validate_subtitles(GValue *settings); gboolean ghb_validate_video(GValue *settings); gboolean ghb_validate_filters(GValue *settings); gboolean ghb_validate_filter_string(const gchar *str, gint max_fields); void ghb_hb_cleanup(gboolean partial); gint ghb_lookup_combo_int(const gchar *name, const GValue *gval); gdouble ghb_lookup_combo_double(const gchar *name, const GValue *gval); const gchar* ghb_lookup_combo_option(const gchar *name, const GValue *gval); const gchar* ghb_lookup_combo_string(const gchar *name, const GValue *gval); gchar* ghb_get_tmp_dir(); gint ghb_find_closest_audio_samplerate(gint rate); void ghb_init_lang_list_box(GtkListBox *list_box); void ghb_init_combo_box(GtkComboBox *combo); void ghb_audio_encoder_opts_set(GtkComboBox *combo); void ghb_audio_bitrate_opts_set(GtkComboBox *combo, gboolean extra); void ghb_audio_bitrate_opts_filter(GtkComboBox *combo, gint first_rate, gint last_rate); void ghb_mix_opts_set(GtkComboBox *combo); void ghb_mix_opts_filter(GtkComboBox *combo, gint acodec); void ghb_audio_samplerate_opts_set(GtkComboBox *combo); int ghb_lookup_audio_lang(const GValue *glang); const iso639_lang_t* ghb_iso639_lookup_by_int(int idx); void ghb_update_display_aspect_label(signal_user_data_t *ud); gchar* ghb_create_title_label(const hb_title_t *title); // libhb lookup helpers const hb_title_t* ghb_lookup_title(int title_id, int *index); const hb_container_t* ghb_lookup_container_by_name(const gchar *name); const hb_encoder_t* ghb_lookup_audio_encoder(const char *name); int ghb_lookup_audio_encoder_codec(const char *name); int ghb_settings_audio_encoder_codec(const GValue *settings, const char *name); const hb_encoder_t* ghb_settings_audio_encoder( const GValue *settings, const char *name); const hb_encoder_t* ghb_lookup_video_encoder(const char *name); int ghb_lookup_video_encoder_codec(const char *name); int ghb_settings_video_encoder_codec(const GValue *settings, const char *name); const hb_encoder_t* ghb_settings_video_encoder( const GValue *settings, const char *name); const hb_mixdown_t* ghb_lookup_mixdown(const char *name); int ghb_lookup_mixdown_mix(const char *name); int ghb_settings_mixdown_mix(const GValue *settings, const char *name); const hb_mixdown_t* ghb_settings_mixdown( const GValue *settings, const char *name); const hb_rate_t* ghb_lookup_video_framerate(const char *name); int ghb_lookup_video_framerate_rate(const char *name); int ghb_settings_video_framerate_rate(const GValue *settings, const char *name); const hb_rate_t* ghb_settings_video_framerate( const GValue *settings, const char *name); const hb_rate_t* ghb_lookup_audio_samplerate(const char *name); int ghb_lookup_audio_samplerate_rate(const char *name); int ghb_settings_audio_samplerate_rate( const GValue *settings, const char *name); const hb_rate_t* ghb_settings_audio_samplerate( const GValue *settings, const char *name); const char* ghb_audio_samplerate_get_short_name(int rate); const hb_rate_t* ghb_lookup_audio_bitrate(const char *name); int ghb_lookup_audio_bitrate_rate(const char *name); int ghb_settings_audio_bitrate_rate(const GValue *settings, const char *name); const hb_rate_t* ghb_settings_audio_bitrate( const GValue *settings, const char *name); const char* ghb_audio_bitrate_get_short_name(int rate); hb_audio_config_t* ghb_get_audio_info(const hb_title_t *title, gint track); hb_subtitle_t* ghb_get_subtitle_info(const hb_title_t *title, gint track); #endif // _HBBACKEND_H_ HandBrake-0.10.2/gtk/src/preview.c0000664000175200017520000011500212465416500017242 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * preview.c * Copyright (C) John Stebbins 2008-2015 * * preview.c is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * */ #include #include #include #include #include #include "ghbcompat.h" #if !defined(_WIN32) #include #endif #if defined(_ENABLE_GST) #include #if GST_CHECK_VERSION(1, 0, 0) #include #else #include #endif #include #include #endif #include "settings.h" #include "presets.h" #include "callbacks.h" #include "hb-backend.h" #include "preview.h" #include "values.h" #include "hb.h" #define PREVIEW_STATE_IMAGE 0 #define PREVIEW_STATE_LIVE 1 struct preview_s { #if defined(_ENABLE_GST) GstElement *play; gulong xid; #endif gint64 len; gint64 pos; gboolean seek_lock; gboolean progress_lock; gint width; gint height; GtkWidget *view; GdkPixbuf *pix; gint button_width; gint button_height; gint frame; gint state; gboolean pause; gboolean encoded[GHB_PREVIEW_MAX]; gint encode_frame; gint live_id; gchar *current; gint live_enabled; }; #if defined(_ENABLE_GST) G_MODULE_EXPORT gboolean live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data); static GstBusSyncReply create_window(GstBus *bus, GstMessage *msg, gpointer data); #endif G_MODULE_EXPORT gboolean preview_expose_cb(GtkWidget *widget, GdkEventExpose *event, signal_user_data_t *ud); void ghb_screen_par(signal_user_data_t *ud, gint *par_n, gint *par_d) { #if defined(_ENABLE_GST) GValue disp_par = {0,}; GstElement *xover; GObjectClass *klass; GParamSpec *pspec; if (!ud->preview->live_enabled) goto fail; g_value_init(&disp_par, GST_TYPE_FRACTION); gst_value_set_fraction(&disp_par, 1, 1); g_object_get(ud->preview->play, "video-sink", &xover, NULL); if (xover == NULL) goto fail; klass = G_OBJECT_GET_CLASS(xover); if (klass == NULL) goto fail; pspec = g_object_class_find_property(klass, "pixel-aspect_ratio"); if (pspec) { GValue par_prop = {0,}; g_value_init(&par_prop, pspec->value_type); g_object_get_property(G_OBJECT(xover), "pixel-aspect-ratio", &par_prop); if (!g_value_transform(&par_prop, &disp_par)) { g_warning("transform failed"); gst_value_set_fraction(&disp_par, 1, 1); } g_value_unset(&par_prop); } *par_n = gst_value_get_fraction_numerator(&disp_par); *par_d = gst_value_get_fraction_denominator(&disp_par); g_value_unset(&disp_par); return; fail: *par_n = 1; *par_d = 1; #else *par_n = 1; *par_d = 1; #endif } void ghb_par_scale(signal_user_data_t *ud, gint *width, gint *height, gint par_n, gint par_d) { gint disp_par_n, disp_par_d; gint64 num, den; ghb_screen_par(ud, &disp_par_n, &disp_par_d); if (disp_par_n < 1 || disp_par_d < 1) { disp_par_n = 1; disp_par_d = 1; } num = par_n * disp_par_d; den = par_d * disp_par_n; if (par_n > par_d) *width = *width * num / den; else *height = *height * den / num; } void ghb_preview_init(signal_user_data_t *ud) { GtkWidget *widget; ud->preview = g_malloc0(sizeof(preview_t)); ud->preview->view = GHB_WIDGET(ud->builder, "preview_image"); gtk_widget_realize(ud->preview->view); g_signal_connect(G_OBJECT(ud->preview->view), "draw", G_CALLBACK(preview_expose_cb), ud); ud->preview->pause = TRUE; ud->preview->encode_frame = -1; ud->preview->live_id = -1; widget = GHB_WIDGET (ud->builder, "preview_button_image"); gtk_widget_get_size_request(widget, &ud->preview->button_width, &ud->preview->button_height); #if defined(_ENABLE_GST) GstBus *bus; GstElement *xover; if (!gdk_window_ensure_native(gtk_widget_get_window(ud->preview->view))) { g_message("Couldn't create native window for GstXOverlay. Disabling live preview."); GtkWidget *widget = GHB_WIDGET(ud->builder, "live_preview_box"); gtk_widget_hide (widget); widget = GHB_WIDGET(ud->builder, "live_preview_duration_box"); gtk_widget_hide (widget); return; } #if !defined(_WIN32) ud->preview->xid = GDK_WINDOW_XID(gtk_widget_get_window(ud->preview->view)); #else ud->preview->xid = GDK_WINDOW_HWND(gtk_widget_get_window(ud->preview->view)); #endif ud->preview->play = gst_element_factory_make("playbin", "play"); xover = gst_element_factory_make("gconfvideosink", "xover"); if (xover == NULL) { xover = gst_element_factory_make("xvimagesink", "xover"); } if (xover == NULL) { xover = gst_element_factory_make("ximagesink", "xover"); } if (ud->preview->play == NULL || xover == NULL) { g_message("Couldn't initialize gstreamer. Disabling live preview."); GtkWidget *widget = GHB_WIDGET(ud->builder, "live_preview_box"); gtk_widget_hide (widget); widget = GHB_WIDGET(ud->builder, "live_preview_duration_box"); gtk_widget_hide (widget); return; } else { g_object_set(G_OBJECT(ud->preview->play), "video-sink", xover, NULL); g_object_set(ud->preview->play, "subtitle-font-desc", "sans bold 20", NULL); bus = gst_pipeline_get_bus(GST_PIPELINE(ud->preview->play)); gst_bus_add_watch(bus, live_preview_cb, ud); #if GST_CHECK_VERSION(1, 0, 0) gst_bus_set_sync_handler(bus, create_window, ud->preview, NULL); #else gst_bus_set_sync_handler(bus, create_window, ud->preview); #endif gst_object_unref(bus); ud->preview->live_enabled = 1; } #else widget = GHB_WIDGET(ud->builder, "live_preview_box"); gtk_widget_hide (widget); widget = GHB_WIDGET(ud->builder, "live_preview_duration_box"); gtk_widget_hide (widget); #endif } void ghb_preview_cleanup(signal_user_data_t *ud) { if (ud->preview->current) { g_free(ud->preview->current); ud->preview->current = NULL; } } #if defined(_ENABLE_GST) static GstBusSyncReply create_window(GstBus *bus, GstMessage *msg, gpointer data) { preview_t *preview = (preview_t*)data; switch (GST_MESSAGE_TYPE(msg)) { case GST_MESSAGE_ELEMENT: { #if GST_CHECK_VERSION(1, 0, 0) if (!gst_is_video_overlay_prepare_window_handle_message(msg)) return GST_BUS_PASS; gst_video_overlay_set_window_handle( GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid); #else if (!gst_structure_has_name(msg->structure, "prepare-xwindow-id")) return GST_BUS_PASS; #if !defined(_WIN32) gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid); #else gst_directdraw_sink_set_window_id( GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), preview->xid); #endif #endif gst_message_unref(msg); return GST_BUS_DROP; } break; default: { } break; } return GST_BUS_PASS; } static void caps_set(GstCaps *caps, signal_user_data_t *ud) { GstStructure *ss; ss = gst_caps_get_structure(caps, 0); if (ss) { gint fps_n, fps_d, width, height; guint num, den, par_n, par_d; gint disp_par_n, disp_par_d; const GValue *par; gst_structure_get_fraction(ss, "framerate", &fps_n, &fps_d); gst_structure_get_int(ss, "width", &width); gst_structure_get_int(ss, "height", &height); par = gst_structure_get_value(ss, "pixel-aspect-ratio"); par_n = gst_value_get_fraction_numerator(par); par_d = gst_value_get_fraction_denominator(par); ghb_screen_par(ud, &disp_par_n, &disp_par_d); gst_video_calculate_display_ratio( &num, &den, width, height, par_n, par_d, disp_par_n, disp_par_d); if (par_n > par_d) width = gst_util_uint64_scale_int(height, num, den); else height = gst_util_uint64_scale_int(width, den, num); if (ghb_settings_get_boolean(ud->prefs, "reduce_hd_preview")) { GdkScreen *ss; gint s_w, s_h; ss = gdk_screen_get_default(); s_w = gdk_screen_get_width(ss); s_h = gdk_screen_get_height(ss); if (width > s_w * 80 / 100) { width = s_w * 80 / 100; height = gst_util_uint64_scale_int(width, den, num); } if (height > s_h * 80 / 100) { height = s_h * 80 / 100; width = gst_util_uint64_scale_int(height, num, den); } } if (width != ud->preview->width || height != ud->preview->height) { gtk_widget_set_size_request(ud->preview->view, width, height); ud->preview->width = width; ud->preview->height = height; } } } #if GST_CHECK_VERSION(1, 0, 0) static void update_stream_info(signal_user_data_t *ud) { GstPad *vpad = NULL; gint n_video; g_object_get(G_OBJECT(ud->preview->play), "n-video", &n_video, NULL); if (n_video > 0) { gint ii; for (ii = 0; ii < n_video && vpad == NULL; ii++) { g_signal_emit_by_name(ud->preview->play, "get-video-pad", ii, &vpad); } } if (vpad) { GstCaps *caps; caps = gst_pad_get_current_caps(vpad); if (caps) { caps_set(caps, ud); gst_caps_unref(caps); } //g_signal_connect(vpad, "notify::caps", G_CALLBACK(caps_set_cb), preview); gst_object_unref(vpad); } } #else static GList * get_stream_info_objects_for_type (GstElement *play, const gchar *typestr) { GList *info_list = NULL, *link; GList *ret = NULL; if (play == NULL) return NULL; g_object_get(play, "stream-info", &info_list, NULL); if (info_list == NULL) return NULL; link = info_list; while (link) { GObject *info_obj = (GObject*)link->data; if (info_obj) { GParamSpec *pspec; GEnumValue *value; gint type = -1; g_object_get(info_obj, "type", &type, NULL); pspec = g_object_class_find_property( G_OBJECT_GET_CLASS (info_obj), "type"); value = g_enum_get_value( G_PARAM_SPEC_ENUM (pspec)->enum_class, type); if (value) { if (g_ascii_strcasecmp (value->value_nick, typestr) == 0 || g_ascii_strcasecmp (value->value_name, typestr) == 0) { ret = g_list_prepend (ret, g_object_ref (info_obj)); } } } if (link) link = link->next; } return g_list_reverse (ret); } static void update_stream_info(signal_user_data_t *ud) { GList *vstreams, *ll; GstPad *vpad = NULL; vstreams = get_stream_info_objects_for_type(ud->preview->play, "video"); if (vstreams) { for (ll = vstreams; vpad == NULL && ll != NULL; ll = ll->next) { g_object_get(ll->data, "object", &vpad, NULL); } } if (vpad) { GstCaps *caps; caps = gst_pad_get_negotiated_caps(vpad); if (caps) { caps_set(caps, ud); gst_caps_unref(caps); } //g_signal_connect(vpad, "notify::caps", G_CALLBACK(caps_set_cb), preview); gst_object_unref(vpad); } g_list_foreach(vstreams, (GFunc)g_object_unref, NULL); g_list_free(vstreams); } #endif G_MODULE_EXPORT gboolean live_preview_cb(GstBus *bus, GstMessage *msg, gpointer data) { signal_user_data_t *ud = (signal_user_data_t*)data; switch (GST_MESSAGE_TYPE(msg)) { case GST_MESSAGE_UNKNOWN: { //printf("unknown"); } break; case GST_MESSAGE_EOS: { // Done GtkImage *img; //printf("eos"); img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image")); gtk_image_set_from_icon_name(img, GHB_PLAY_ICON, GTK_ICON_SIZE_BUTTON); gst_element_set_state(ud->preview->play, GST_STATE_PAUSED); ud->preview->pause = TRUE; gst_element_seek(ud->preview->play, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); } break; case GST_MESSAGE_ERROR: { //printf("error\n"); GError *err; gchar *debug; gst_message_parse_error(msg, &err, &debug); g_warning("Gstreamer Error: %s", err->message); g_error_free(err); g_free(debug); } break; case GST_MESSAGE_WARNING: case GST_MESSAGE_INFO: case GST_MESSAGE_TAG: case GST_MESSAGE_BUFFERING: case GST_MESSAGE_STATE_CHANGED: { GstState state, pending; gst_element_get_state(ud->preview->play, &state, &pending, 0); //printf("state change %x\n", state); if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING) { update_stream_info(ud); } } break; case GST_MESSAGE_STATE_DIRTY: { //printf("state dirty\n"); } break; case GST_MESSAGE_STEP_DONE: { //printf("step done\n"); } break; case GST_MESSAGE_CLOCK_PROVIDE: { //printf("clock provide\n"); } break; case GST_MESSAGE_CLOCK_LOST: { //printf("clock lost\n"); } break; case GST_MESSAGE_NEW_CLOCK: { //printf("new clock\n"); } break; case GST_MESSAGE_STRUCTURE_CHANGE: { //printf("structure change\n"); } break; case GST_MESSAGE_STREAM_STATUS: { //printf("stream status\n"); } break; case GST_MESSAGE_APPLICATION: { //printf("application\n"); } break; case GST_MESSAGE_ELEMENT: { //printf("element\n"); if (gst_is_missing_plugin_message(msg)) { gst_element_set_state(ud->preview->play, GST_STATE_PAUSED); gchar *message, *desc; desc = gst_missing_plugin_message_get_description(msg); message = g_strdup_printf( _("Missing GStreamer plugin\n" "Audio or Video may not play as expected\n\n%s"), desc); ghb_message_dialog(GTK_MESSAGE_WARNING, message, "Ok", NULL); g_free(message); gst_element_set_state(ud->preview->play, GST_STATE_PLAYING); } } break; case GST_MESSAGE_SEGMENT_START: { //printf("segment start\n"); } break; case GST_MESSAGE_SEGMENT_DONE: { //printf("segment done\n"); } break; #if GST_CHECK_VERSION(1, 0, 0) case GST_MESSAGE_DURATION_CHANGED: { //printf("duration change\n"); }; #endif case GST_MESSAGE_LATENCY: { //printf("latency\n"); }; case GST_MESSAGE_ASYNC_START: { //printf("async start\n"); } break; case GST_MESSAGE_ASYNC_DONE: { //printf("async done\n"); } break; case GST_MESSAGE_REQUEST_STATE: { //printf("request state\n"); } break; case GST_MESSAGE_STEP_START: { //printf("step start\n"); } break; case GST_MESSAGE_QOS: { //printf("qos\n"); } break; #if GST_CHECK_VERSION(1, 0, 0) case GST_MESSAGE_PROGRESS: { //printf("progress\n"); } break; case GST_MESSAGE_TOC: { //printf("toc\n"); } break; case GST_MESSAGE_RESET_TIME: { //printf("reset time\n"); } break; case GST_MESSAGE_STREAM_START: { //printf("stream start\n"); }; #endif case GST_MESSAGE_ANY: { //printf("any\n"); } break; default: { // Ignore //printf("?msg? %x\n", GST_MESSAGE_TYPE(msg)); } } return TRUE; } void live_preview_start(signal_user_data_t *ud) { GtkImage *img; gchar *uri; if (!ud->preview->live_enabled) return; img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image")); if (!ud->preview->encoded[ud->preview->frame]) { gtk_image_set_from_icon_name(img, GHB_PLAY_ICON, GTK_ICON_SIZE_BUTTON); gst_element_set_state(ud->preview->play, GST_STATE_NULL); ud->preview->pause = TRUE; return; } uri = g_strdup_printf("file://%s", ud->preview->current); gtk_image_set_from_icon_name(img, GHB_PAUSE_ICON, GTK_ICON_SIZE_BUTTON); ud->preview->state = PREVIEW_STATE_LIVE; g_object_set(G_OBJECT(ud->preview->play), "uri", uri, NULL); gst_element_set_state(ud->preview->play, GST_STATE_PLAYING); ud->preview->pause = FALSE; g_free(uri); } void live_preview_pause(signal_user_data_t *ud) { GtkImage *img; if (!ud->preview->live_enabled) return; img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image")); gtk_image_set_from_icon_name(img, GHB_PLAY_ICON, GTK_ICON_SIZE_BUTTON); gst_element_set_state(ud->preview->play, GST_STATE_PAUSED); ud->preview->pause = TRUE; } #endif void live_preview_stop(signal_user_data_t *ud) { GtkImage *img; GtkRange *progress; if (!ud->preview->live_enabled) return; img = GTK_IMAGE(GHB_WIDGET(ud->builder, "live_preview_play_image")); gtk_image_set_from_icon_name(img, GHB_PLAY_ICON, GTK_ICON_SIZE_BUTTON); #if defined(_ENABLE_GST) gst_element_set_state(ud->preview->play, GST_STATE_NULL); #endif ud->preview->pause = TRUE; ud->preview->state = PREVIEW_STATE_IMAGE; progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress")); gtk_range_set_value(progress, 0); } void ghb_live_reset(signal_user_data_t *ud) { gboolean encoded; if (ud->preview->live_id >= 0) { ghb_stop_live_encode(); } ud->preview->live_id = -1; ud->preview->encode_frame = -1; if (!ud->preview->pause) live_preview_stop(ud); if (ud->preview->current) { g_free(ud->preview->current); ud->preview->current = NULL; } encoded = ud->preview->encoded[ud->preview->frame]; memset(ud->preview->encoded, 0, sizeof(gboolean) * GHB_PREVIEW_MAX); if (encoded) ghb_set_preview_image(ud); } G_MODULE_EXPORT void live_preview_start_cb(GtkWidget *xwidget, signal_user_data_t *ud) { gchar *tmp_dir; gchar *name; gint frame = ud->preview->frame; tmp_dir = ghb_get_tmp_dir(); name = g_strdup_printf("%s/live%02d", tmp_dir, ud->preview->frame); if (ud->preview->current) g_free(ud->preview->current); ud->preview->current = name; if (ud->preview->encoded[frame] && g_file_test(name, G_FILE_TEST_IS_REGULAR)) { #if defined(_ENABLE_GST) if (ud->preview->pause) live_preview_start(ud); else live_preview_pause(ud); #endif } else { GValue *js; ud->preview->encode_frame = frame; js = ghb_value_dup(ud->settings); ghb_settings_set_string(js, "destination", name); ghb_settings_set_int(js, "start_frame", ud->preview->frame); ud->preview->live_id = 0; ghb_settings_set_value(js, "Preferences", ud->prefs); ghb_add_live_job(js, ud->preview->live_id); ghb_start_live_encode(); ghb_value_free(js); } } void ghb_live_encode_done(signal_user_data_t *ud, gboolean success) { GtkWidget *widget; GtkWidget *prog; ud->preview->live_id = -1; prog = GHB_WIDGET(ud->builder, "live_encode_progress"); if (success && ud->preview->encode_frame == ud->preview->frame) { gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), "Done"); gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 1); ud->preview->encoded[ud->preview->encode_frame] = TRUE; #if defined(_ENABLE_GST) live_preview_start(ud); #endif widget = GHB_WIDGET(ud->builder, "live_progress_box"); gtk_widget_hide (widget); widget = GHB_WIDGET(ud->builder, "live_preview_progress"); gtk_widget_show (widget); } else { gtk_progress_bar_set_text(GTK_PROGRESS_BAR(prog), ""); gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(prog), 0); ud->preview->encoded[ud->preview->encode_frame] = FALSE; } } #if defined(_ENABLE_GST) G_MODULE_EXPORT gboolean unlock_progress_cb(signal_user_data_t *ud) { ud->preview->progress_lock = FALSE; // This function is initiated by g_idle_add. Must return false // so that it is not called again return FALSE; } #endif void ghb_live_preview_progress(signal_user_data_t *ud) { #if defined(_ENABLE_GST) GstFormat fmt = GST_FORMAT_TIME; gint64 len = -1, pos = -1; if (!ud->preview->live_enabled) return; if (ud->preview->state != PREVIEW_STATE_LIVE || ud->preview->seek_lock) return; ud->preview->progress_lock = TRUE; #if GST_CHECK_VERSION(1, 0, 0) if (gst_element_query_duration(ud->preview->play, fmt, &len)) #else if (gst_element_query_duration(ud->preview->play, &fmt, &len)) #endif { if (len != -1 && fmt == GST_FORMAT_TIME) { ud->preview->len = len / GST_MSECOND; } } #if GST_CHECK_VERSION(1, 0, 0) if (gst_element_query_position(ud->preview->play, fmt, &pos)) #else if (gst_element_query_position(ud->preview->play, &fmt, &pos)) #endif { if (pos != -1 && fmt == GST_FORMAT_TIME) { ud->preview->pos = pos / GST_MSECOND; } } if (ud->preview->len > 0) { GtkRange *progress; gdouble percent; percent = (gdouble)ud->preview->pos * 100 / ud->preview->len; progress = GTK_RANGE(GHB_WIDGET(ud->builder, "live_preview_progress")); gtk_range_set_value(progress, percent); } g_idle_add((GSourceFunc)unlock_progress_cb, ud); #endif } #if defined(_ENABLE_GST) G_MODULE_EXPORT gboolean unlock_seek_cb(signal_user_data_t *ud) { ud->preview->seek_lock = FALSE; // This function is initiated by g_idle_add. Must return false // so that it is not called again return FALSE; } #endif G_MODULE_EXPORT void live_preview_seek_cb(GtkWidget *widget, signal_user_data_t *ud) { #if defined(_ENABLE_GST) gdouble dval; gint64 pos; if (!ud->preview->live_enabled) return; if (ud->preview->progress_lock) return; ud->preview->seek_lock = TRUE; dval = gtk_range_get_value(GTK_RANGE(widget)); pos = ((ud->preview->len * dval) / 100) * GST_MSECOND; gst_element_seek(ud->preview->play, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); g_idle_add((GSourceFunc)unlock_seek_cb, ud); #endif } static void _draw_pixbuf(GdkWindow *window, GdkPixbuf *pixbuf) { cairo_t *cr; cr = gdk_cairo_create(window); gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); cairo_paint(cr); cairo_destroy(cr); } void ghb_set_preview_image(signal_user_data_t *ud) { GtkWidget *widget; gint preview_width, preview_height, target_height, width, height; g_debug("set_preview_button_image ()"); gint title_id, titleindex; const hb_title_t *title; live_preview_stop(ud); title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (title == NULL) return; widget = GHB_WIDGET (ud->builder, "preview_frame"); ud->preview->frame = ghb_widget_int(widget) - 1; if (ud->preview->encoded[ud->preview->frame]) { widget = GHB_WIDGET(ud->builder, "live_progress_box"); gtk_widget_hide (widget); widget = GHB_WIDGET(ud->builder, "live_preview_progress"); gtk_widget_show (widget); } else { widget = GHB_WIDGET(ud->builder, "live_preview_progress"); gtk_widget_hide (widget); widget = GHB_WIDGET(ud->builder, "live_progress_box"); gtk_widget_show (widget); widget = GHB_WIDGET(ud->builder, "live_encode_progress"); gtk_progress_bar_set_text(GTK_PROGRESS_BAR(widget), ""); gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(widget), 0); } if (ud->preview->pix != NULL) g_object_unref(ud->preview->pix); ud->preview->pix = ghb_get_preview_image(title, ud->preview->frame, ud, &width, &height); if (ud->preview->pix == NULL) return; preview_width = gdk_pixbuf_get_width(ud->preview->pix); preview_height = gdk_pixbuf_get_height(ud->preview->pix); widget = GHB_WIDGET (ud->builder, "preview_image"); if (preview_width != ud->preview->width || preview_height != ud->preview->height) { gtk_widget_set_size_request(widget, preview_width, preview_height); ud->preview->width = preview_width; ud->preview->height = preview_height; } _draw_pixbuf(gtk_widget_get_window(widget), ud->preview->pix); gchar *text = g_strdup_printf("%d x %d", width, height); widget = GHB_WIDGET (ud->builder, "preview_dims"); gtk_label_set_text(GTK_LABEL(widget), text); g_free(text); g_debug("preview %d x %d", preview_width, preview_height); target_height = MIN(ud->preview->button_height, 200); height = target_height; width = preview_width * height / preview_height; if (width > 400) { width = 400; height = preview_height * width / preview_width; } if ((height >= 16) && (width >= 16)) { GdkPixbuf *scaled_preview; scaled_preview = gdk_pixbuf_scale_simple (ud->preview->pix, width, height, GDK_INTERP_NEAREST); if (scaled_preview != NULL) { widget = GHB_WIDGET (ud->builder, "preview_button_image"); gtk_image_set_from_pixbuf(GTK_IMAGE(widget), scaled_preview); g_object_unref(scaled_preview); } } } #if defined(_ENABLE_GST) #if GST_CHECK_VERSION(1, 0, 0) G_MODULE_EXPORT gboolean delayed_expose_cb(signal_user_data_t *ud) { GstElement *vsink; GstVideoOverlay *vover; if (!ud->preview->live_enabled) return FALSE; g_object_get(ud->preview->play, "video-sink", &vsink, NULL); if (vsink == NULL) return FALSE; if (GST_IS_BIN(vsink)) vover = GST_VIDEO_OVERLAY(gst_bin_get_by_interface( GST_BIN(vsink), GST_TYPE_VIDEO_OVERLAY)); else vover = GST_VIDEO_OVERLAY(vsink); gst_video_overlay_expose(vover); // This function is initiated by g_idle_add. Must return false // so that it is not called again return FALSE; } #else G_MODULE_EXPORT gboolean delayed_expose_cb(signal_user_data_t *ud) { GstElement *vsink; GstXOverlay *xover; if (!ud->preview->live_enabled) return FALSE; g_object_get(ud->preview->play, "video-sink", &vsink, NULL); if (vsink == NULL) return FALSE; if (GST_IS_BIN(vsink)) xover = GST_X_OVERLAY(gst_bin_get_by_interface( GST_BIN(vsink), GST_TYPE_X_OVERLAY)); else xover = GST_X_OVERLAY(vsink); gst_x_overlay_expose(xover); // This function is initiated by g_idle_add. Must return false // so that it is not called again return FALSE; } #endif #endif #if 0 // GTK_CHECK_VERSION(3, 0, 0) // // Only needed if the problems with GtkOverlay are ever resolved // G_MODULE_EXPORT gboolean position_overlay_cb( GtkWidget *overlay, GtkWidget *widget, GdkRectangle *rect, signal_user_data_t *ud) { GtkRequisition min_size, size; gtk_widget_get_preferred_size(widget, &min_size, &size); rect->width = MAX(min_size.width, size.width); rect->height = MAX(min_size.height, size.height); rect->x = MAX(0, ud->preview->width / 2 - rect->width / 2); rect->y = MAX(0, ud->preview->height - rect->height - 50); return TRUE; } #endif G_MODULE_EXPORT gboolean preview_expose_cb( GtkWidget *widget, GdkEventExpose *event, signal_user_data_t *ud) { #if defined(_ENABLE_GST) #if GST_CHECK_VERSION(1, 0, 0) if (ud->preview->live_enabled && ud->preview->state == PREVIEW_STATE_LIVE) { if (GST_STATE(ud->preview->play) >= GST_STATE_PAUSED) { GstElement *vsink; GstVideoOverlay *vover; g_object_get(ud->preview->play, "video-sink", &vsink, NULL); if (GST_IS_BIN(vsink)) vover = GST_VIDEO_OVERLAY(gst_bin_get_by_interface( GST_BIN(vsink), GST_TYPE_VIDEO_OVERLAY)); else vover = GST_VIDEO_OVERLAY(vsink); gst_video_overlay_expose(vover); // For some reason, the exposed region doesn't always get // cleaned up here. But a delayed gst_x_overlay_expose() // takes care of it. g_idle_add((GSourceFunc)delayed_expose_cb, ud); return FALSE; } return TRUE; } #else if (ud->preview->live_enabled && ud->preview->state == PREVIEW_STATE_LIVE) { if (GST_STATE(ud->preview->play) >= GST_STATE_PAUSED) { GstElement *vsink; GstXOverlay *xover; g_object_get(ud->preview->play, "video-sink", &vsink, NULL); if (GST_IS_BIN(vsink)) xover = GST_X_OVERLAY(gst_bin_get_by_interface( GST_BIN(vsink), GST_TYPE_X_OVERLAY)); else xover = GST_X_OVERLAY(vsink); gst_x_overlay_expose(xover); // For some reason, the exposed region doesn't always get // cleaned up here. But a delayed gst_x_overlay_expose() // takes care of it. g_idle_add((GSourceFunc)delayed_expose_cb, ud); return FALSE; } return TRUE; } #endif #endif if (ud->preview->pix != NULL) { _draw_pixbuf(gtk_widget_get_window(ud->preview->view), ud->preview->pix); //_draw_pixbuf(gtk_widget_get_window(widget), ud->preview->pix); } return TRUE; } G_MODULE_EXPORT void preview_button_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud) { g_debug("allocate %d x %d", allocation->width, allocation->height); if (ud->preview->button_width == allocation->width && ud->preview->button_height == allocation->height) { // Nothing to do. Bug out. g_debug("nothing to do"); return; } g_debug("prev allocate %d x %d", ud->preview->button_width, ud->preview->button_height); ud->preview->button_width = allocation->width; ud->preview->button_height = allocation->height; ghb_set_preview_image(ud); } void ghb_preview_set_visible(signal_user_data_t *ud) { gint title_id, titleindex; const hb_title_t *title; GtkWidget *widget; gboolean active; title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); active = ghb_settings_get_boolean(ud->globals, "show_preview") && title != NULL; widget = GHB_WIDGET(ud->builder, "preview_window"); gtk_widget_set_visible(widget, active); if (active) { gint x, y; x = ghb_settings_get_int(ud->prefs, "preview_x"); y = ghb_settings_get_int(ud->prefs, "preview_y"); if (x >= 0 && y >= 0) gtk_window_move(GTK_WINDOW(widget), x, y); } } G_MODULE_EXPORT void picture_settings_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { g_debug("picture_settings_clicked_cb()"); ghb_widget_to_setting(ud->globals, xwidget); ghb_preview_set_visible(ud); } G_MODULE_EXPORT void picture_settings_alt_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GtkWidget *toggle; gboolean active; g_debug("picture_settings_alt_clicked_cb()"); toggle = GHB_WIDGET (ud->builder, "show_preview"); active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle)); gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toggle), !active); } static gboolean go_full(signal_user_data_t *ud) { GtkWindow *window; window = GTK_WINDOW(GHB_WIDGET (ud->builder, "preview_window")); gtk_window_fullscreen(window); ghb_set_preview_image(ud); return FALSE; } G_MODULE_EXPORT void fullscreen_clicked_cb(GtkWidget *toggle, signal_user_data_t *ud) { gboolean active; GtkWindow *window; g_debug("fullscreen_clicked_cb()"); ghb_widget_to_setting (ud->prefs, toggle); ghb_check_dependency(ud, toggle, NULL); const gchar *name = ghb_get_setting_key(toggle); ghb_pref_save(ud->prefs, name); window = GTK_WINDOW(GHB_WIDGET (ud->builder, "preview_window")); active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)); if (active) { gtk_window_set_resizable(window, TRUE); gtk_button_set_label(GTK_BUTTON(toggle), _("Windowed")); // Changing resizable property doesn't take effect immediately // need to delay fullscreen till after this callback returns // to mainloop g_idle_add((GSourceFunc)go_full, ud); } else { gtk_window_unfullscreen(window); gtk_window_set_resizable(window, FALSE); gtk_button_set_label(GTK_BUTTON(toggle), _("Fullscreen")); ghb_set_preview_image(ud); } } G_MODULE_EXPORT void preview_frame_value_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { if (ud->preview->live_id >= 0) { ghb_stop_live_encode(); ud->preview->live_id = -1; ud->preview->encode_frame = -1; } ghb_set_preview_image(ud); } G_MODULE_EXPORT gboolean preview_window_delete_cb( GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud) { live_preview_stop(ud); widget = GHB_WIDGET(ud->builder, "show_preview"); gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE); return TRUE; } G_MODULE_EXPORT void preview_duration_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("preview_duration_changed_cb ()"); ghb_live_reset(ud); ghb_widget_to_setting (ud->prefs, widget); ghb_check_dependency(ud, widget, NULL); const gchar *name = ghb_get_setting_key(widget); ghb_pref_save(ud->prefs, name); } static guint hud_timeout_id = 0; static gboolean hud_timeout(signal_user_data_t *ud) { GtkWidget *widget; g_debug("hud_timeout()"); widget = GHB_WIDGET(ud->builder, "preview_hud"); gtk_widget_hide(widget); hud_timeout_id = 0; return FALSE; } G_MODULE_EXPORT gboolean hud_enter_cb( GtkWidget *widget, GdkEventCrossing *event, signal_user_data_t *ud) { g_debug("hud_enter_cb()"); if (hud_timeout_id != 0) { GMainContext *mc; GSource *source; mc = g_main_context_default(); source = g_main_context_find_source_by_id(mc, hud_timeout_id); if (source != NULL) g_source_destroy(source); } widget = GHB_WIDGET(ud->builder, "preview_hud"); gtk_widget_show(widget); hud_timeout_id = 0; return FALSE; } G_MODULE_EXPORT gboolean preview_leave_cb( GtkWidget *widget, GdkEventCrossing *event, signal_user_data_t *ud) { g_debug("hud_leave_cb()"); if (hud_timeout_id != 0) { GMainContext *mc; GSource *source; mc = g_main_context_default(); source = g_main_context_find_source_by_id(mc, hud_timeout_id); if (source != NULL) g_source_destroy(source); } hud_timeout_id = g_timeout_add(300, (GSourceFunc)hud_timeout, ud); return FALSE; } G_MODULE_EXPORT gboolean preview_motion_cb( GtkWidget *widget, GdkEventMotion *event, signal_user_data_t *ud) { //g_debug("hud_motion_cb %d", hud_timeout_id); if (hud_timeout_id != 0) { GMainContext *mc; GSource *source; mc = g_main_context_default(); source = g_main_context_find_source_by_id(mc, hud_timeout_id); if (source != NULL) g_source_destroy(source); } widget = GHB_WIDGET(ud->builder, "preview_hud"); if (!gtk_widget_get_visible(widget)) { gtk_widget_show(widget); } hud_timeout_id = g_timeout_add_seconds(4, (GSourceFunc)hud_timeout, ud); return FALSE; } cairo_region_t* ghb_curved_rect_mask(gint width, gint height, gint radius) { cairo_region_t *shape; cairo_surface_t *surface; cairo_t *cr; double w, h; if (!width || !height) return NULL; surface = cairo_image_surface_create(CAIRO_FORMAT_A8, width, height); cr = cairo_create (surface); w = width; h = height; if (radius > width / 2) radius = width / 2; if (radius > height / 2) radius = height / 2; // fill shape with black cairo_save(cr); cairo_rectangle (cr, 0, 0, width, height); cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); cairo_fill (cr); cairo_restore (cr); cairo_move_to (cr, 0, radius); cairo_curve_to (cr, 0 , 0, 0 , 0, radius, 0); cairo_line_to (cr, w - radius, 0); cairo_curve_to (cr, w, 0, w, 0, w, radius); cairo_line_to (cr, w , h - radius); cairo_curve_to (cr, w, h, w, h, w - radius, h); cairo_line_to (cr, 0 + radius, h); cairo_curve_to (cr, 0, h, 0, h, 0, h - radius); cairo_close_path(cr); cairo_set_source_rgb(cr, 1, 1, 1); cairo_fill(cr); cairo_destroy(cr); shape = gdk_cairo_region_create_from_surface(surface); cairo_surface_destroy(surface); return shape; } G_MODULE_EXPORT void preview_hud_size_alloc_cb( GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud) { cairo_region_t *shape; //g_message("preview_hud_size_alloc_cb()"); if (gtk_widget_get_visible(widget) && allocation->height > 50) { shape = ghb_curved_rect_mask(allocation->width, allocation->height, allocation->height/4); if (shape != NULL) { gtk_widget_shape_combine_region(widget, shape); cairo_region_destroy(shape); } } } G_MODULE_EXPORT gboolean preview_configure_cb( GtkWidget *widget, GdkEventConfigure *event, signal_user_data_t *ud) { gint x, y; //g_message("preview_configure_cb()"); if (gtk_widget_get_visible(widget)) { gtk_window_get_position(GTK_WINDOW(widget), &x, &y); ghb_settings_set_int(ud->prefs, "preview_x", x); ghb_settings_set_int(ud->prefs, "preview_y", y); ghb_pref_set(ud->prefs, "preview_x"); ghb_pref_set(ud->prefs, "preview_y"); ghb_prefs_store(); } return FALSE; } HandBrake-0.10.2/gtk/src/hb-backend.c0000664000175200017520000045660512465416500017560 0ustar handbrakehandbrake/*************************************************************************** * hb-backend.c * * Fri Mar 28 10:38:44 2008 * Copyright 2008-2015 John Stebbins * ****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA */ #define _GNU_SOURCE #include #include #include "hb.h" #include "ghbcompat.h" #include #include #include "hb-backend.h" #include "settings.h" #include "callbacks.h" #include "subtitlehandler.h" #include "audiohandler.h" #include "videohandler.h" #include "x264handler.h" #include "preview.h" #include "values.h" #include "lang.h" typedef struct { gchar *option; const gchar *shortOpt; gdouble ivalue; const gchar *svalue; } options_map_t; typedef struct { gint count; options_map_t *map; } combo_opts_t; static options_map_t d_subtitle_track_sel_opts[] = { {N_("None"), "none", 0, "0"}, {N_("First Track Matching Selected Languages"), "first", 1, "1"}, {N_("All Tracks Matching Selected Languages"), "all", 2, "2"}, }; combo_opts_t subtitle_track_sel_opts = { sizeof(d_subtitle_track_sel_opts)/sizeof(options_map_t), d_subtitle_track_sel_opts }; static options_map_t d_audio_track_sel_opts[] = { {N_("None"), "none", 0, "0"}, {N_("First Track Matching Selected Languages"), "first", 1, "1"}, {N_("All Tracks Matching Selected Languages"), "all", 2, "2"}, }; combo_opts_t audio_track_sel_opts = { sizeof(d_audio_track_sel_opts)/sizeof(options_map_t), d_audio_track_sel_opts }; static options_map_t d_point_to_point_opts[] = { {N_("Chapters:"), "chapter", 0, "0"}, {N_("Seconds:"), "time", 1, "1"}, {N_("Frames:"), "frame", 2, "2"}, }; combo_opts_t point_to_point_opts = { sizeof(d_point_to_point_opts)/sizeof(options_map_t), d_point_to_point_opts }; static options_map_t d_when_complete_opts[] = { {N_("Do Nothing"), "nothing", 0, "0"}, {N_("Show Notification"), "notify", 1, "1"}, {N_("Quit Handbrake"), "quit", 4, "4"}, {N_("Put Computer To Sleep"), "sleep", 2, "2"}, {N_("Shutdown Computer"), "shutdown", 3, "3"}, }; combo_opts_t when_complete_opts = { sizeof(d_when_complete_opts)/sizeof(options_map_t), d_when_complete_opts }; static options_map_t d_par_opts[] = { {N_("Off"), "0", 0, "0"}, {N_("Strict"), "1", 1, "1"}, {N_("Loose"), "2", 2, "2"}, {N_("Custom"), "3", 3, "3"}, }; combo_opts_t par_opts = { sizeof(d_par_opts)/sizeof(options_map_t), d_par_opts }; static options_map_t d_alignment_opts[] = { {"2", "2", 2, "2"}, {"4", "4", 4, "4"}, {"8", "8", 8, "8"}, {"16", "16", 16, "16"}, }; combo_opts_t alignment_opts = { sizeof(d_alignment_opts)/sizeof(options_map_t), d_alignment_opts }; static options_map_t d_logging_opts[] = { {"0", "0", 0, "0"}, {"1", "1", 1, "1"}, {"2", "2", 2, "2"}, }; combo_opts_t logging_opts = { sizeof(d_logging_opts)/sizeof(options_map_t), d_logging_opts }; static options_map_t d_log_longevity_opts[] = { {N_("Week"), "week", 7, "7"}, {N_("Month"), "month", 30, "30"}, {N_("Year"), "year", 365, "365"}, {N_("Immortal"), "immortal", 366, "366"}, }; combo_opts_t log_longevity_opts = { sizeof(d_log_longevity_opts)/sizeof(options_map_t), d_log_longevity_opts }; static options_map_t d_appcast_update_opts[] = { {N_("Never"), "never", 0, "never"}, {N_("Daily"), "daily", 1, "daily"}, {N_("Weekly"), "weekly", 2, "weekly"}, {N_("Monthly"), "monthly", 3, "monthly"}, }; combo_opts_t appcast_update_opts = { sizeof(d_appcast_update_opts)/sizeof(options_map_t), d_appcast_update_opts }; static options_map_t d_vqual_granularity_opts[] = { {"0.2", "0.2", 0.2, "0.2"}, {"0.25", "0.25", 0.25, "0.25"}, {"0.5", "0.5", 0.5, "0.5"}, {"1", "1", 1, "1"}, }; combo_opts_t vqual_granularity_opts = { sizeof(d_vqual_granularity_opts)/sizeof(options_map_t), d_vqual_granularity_opts }; static options_map_t d_detel_opts[] = { {N_("Off"), "off", 0, ""}, {N_("Custom"), "custom", 1, ""}, {N_("Default"),"default",2, NULL}, }; combo_opts_t detel_opts = { sizeof(d_detel_opts)/sizeof(options_map_t), d_detel_opts }; static options_map_t d_decomb_opts[] = { {N_("Off"), "off", 0, ""}, {N_("Custom"), "custom", 1, ""}, {N_("Default"),"default",2, NULL}, {N_("Fast"), "fast", 3, "7:2:6:9:1:80"}, {N_("Bob"), "bob", 4, "455"}, }; combo_opts_t decomb_opts = { sizeof(d_decomb_opts)/sizeof(options_map_t), d_decomb_opts }; static options_map_t d_deint_opts[] = { {N_("Off"), "off", 0, ""}, {N_("Custom"), "custom", 1, ""}, {N_("Fast"), "fast", 2, "0:-1:-1:0:1"}, {N_("Slow"), "slow", 3, "1:-1:-1:0:1"}, {N_("Slower"), "slower", 4, "3:-1:-1:0:1"}, {N_("Bob"), "bob", 5, "15:-1:-1:0:1"}, }; combo_opts_t deint_opts = { sizeof(d_deint_opts)/sizeof(options_map_t), d_deint_opts }; static options_map_t d_denoise_opts[] = { {N_("Off"), "off", 0, ""}, {N_("NLMeans"), "nlmeans", 1, ""}, {N_("HQDN3D"), "hqdn3d", 2, ""}, }; combo_opts_t denoise_opts = { sizeof(d_denoise_opts)/sizeof(options_map_t), d_denoise_opts }; static options_map_t d_denoise_preset_opts[] = { {N_("Custom"), "custom", 1, ""}, {N_("Ultralight"), "ultralight", 5, ""}, {N_("Light"), "light", 2, ""}, {N_("Medium"), "medium", 3, ""}, {N_("Strong"), "strong", 4, ""}, }; combo_opts_t denoise_preset_opts = { sizeof(d_denoise_preset_opts)/sizeof(options_map_t), d_denoise_preset_opts }; static options_map_t d_nlmeans_tune_opts[] = { {N_("None"), "none", 0, ""}, {N_("Film"), "film", 1, ""}, {N_("Grain"), "grain", 2, ""}, {N_("High Motion"), "highmotion", 3, ""}, {N_("Animation"), "animation", 4, ""}, }; combo_opts_t nlmeans_tune_opts = { sizeof(d_nlmeans_tune_opts)/sizeof(options_map_t), d_nlmeans_tune_opts }; static options_map_t d_direct_opts[] = { {N_("None"), "none", 0, "none"}, {N_("Spatial"), "spatial", 1, "spatial"}, {N_("Temporal"), "temporal", 2, "temporal"}, {N_("Automatic"), "auto", 3, "auto"}, }; combo_opts_t direct_opts = { sizeof(d_direct_opts)/sizeof(options_map_t), d_direct_opts }; static options_map_t d_badapt_opts[] = { {N_("Off"), "0", 0, "0"}, {N_("Fast"), "1", 1, "1"}, {N_("Optimal"), "2", 2, "2"}, }; combo_opts_t badapt_opts = { sizeof(d_badapt_opts)/sizeof(options_map_t), d_badapt_opts }; static options_map_t d_bpyramid_opts[] = { {N_("Off"), "none", 0, "none"}, {N_("Strict"), "strict", 1, "strict"}, {N_("Normal"), "normal", 2, "normal"}, }; combo_opts_t bpyramid_opts = { sizeof(d_bpyramid_opts)/sizeof(options_map_t), d_bpyramid_opts }; static options_map_t d_weightp_opts[] = { {N_("Off"), "0", 0, "0"}, {N_("Simple"), "1", 1, "1"}, {N_("Smart"), "2", 2, "2"}, }; combo_opts_t weightp_opts = { sizeof(d_weightp_opts)/sizeof(options_map_t), d_weightp_opts }; static options_map_t d_me_opts[] = { {N_("Diamond"), "dia", 0, "dia"}, {N_("Hexagon"), "hex", 1, "hex"}, {N_("Uneven Multi-Hexagon"), "umh", 2, "umh"}, {N_("Exhaustive"), "esa", 3, "esa"}, {N_("Hadamard Exhaustive"), "tesa", 4, "tesa"}, }; combo_opts_t me_opts = { sizeof(d_me_opts)/sizeof(options_map_t), d_me_opts }; static options_map_t d_subme_opts[] = { {N_("0: SAD, no subpel"), "0", 0, "0"}, {N_("1: SAD, qpel"), "1", 1, "1"}, {N_("2: SATD, qpel"), "2", 2, "2"}, {N_("3: SATD: multi-qpel"), "3", 3, "3"}, {N_("4: SATD, qpel on all"), "4", 4, "4"}, {N_("5: SATD, multi-qpel on all"), "5", 5, "5"}, {N_("6: RD in I/P-frames"), "6", 6, "6"}, {N_("7: RD in all frames"), "7", 7, "7"}, {N_("8: RD refine in I/P-frames"), "8", 8, "8"}, {N_("9: RD refine in all frames"), "9", 9, "9"}, {N_("10: QPRD in all frames"), "10", 10, "10"}, {N_("11: No early terminations in analysis"), "11", 11, "11"}, }; combo_opts_t subme_opts = { sizeof(d_subme_opts)/sizeof(options_map_t), d_subme_opts }; static options_map_t d_analyse_opts[] = { {N_("Most"), "p8x8,b8x8,i8x8,i4x4", 0, "p8x8,b8x8,i8x8,i4x4"}, {N_("None"), "none", 1, "none"}, {N_("Some"), "i4x4,i8x8", 2, "i4x4,i8x8"}, {N_("All"), "all", 3, "all"}, {N_("Custom"), "custom", 4, "all"}, }; combo_opts_t analyse_opts = { sizeof(d_analyse_opts)/sizeof(options_map_t), d_analyse_opts }; static options_map_t d_trellis_opts[] = { {N_("Off"), "0", 0, "0"}, {N_("Encode only"), "1", 1, "1"}, {N_("Always"), "2", 2, "2"}, }; combo_opts_t trellis_opts = { sizeof(d_trellis_opts)/sizeof(options_map_t), d_trellis_opts }; typedef struct { const gchar *name; combo_opts_t *opts; } combo_name_map_t; combo_name_map_t combo_name_map[] = { {"SubtitleTrackSelectionBehavior", &subtitle_track_sel_opts}, {"AudioTrackSelectionBehavior", &audio_track_sel_opts}, {"PtoPType", &point_to_point_opts}, {"WhenComplete", &when_complete_opts}, {"PicturePAR", &par_opts}, {"PictureModulus", &alignment_opts}, {"LoggingLevel", &logging_opts}, {"LogLongevity", &log_longevity_opts}, {"check_updates", &appcast_update_opts}, {"VideoQualityGranularity", &vqual_granularity_opts}, {"PictureDeinterlace", &deint_opts}, {"PictureDecomb", &decomb_opts}, {"PictureDetelecine", &detel_opts}, {"PictureDenoiseFilter", &denoise_opts}, {"PictureDenoisePreset", &denoise_preset_opts}, {"PictureDenoiseTune", &nlmeans_tune_opts}, {"x264_direct", &direct_opts}, {"x264_b_adapt", &badapt_opts}, {"x264_bpyramid", &bpyramid_opts}, {"x264_weighted_pframes", &weightp_opts}, {"x264_me", &me_opts}, {"x264_subme", &subme_opts}, {"x264_analyse", &analyse_opts}, {"x264_trellis", &trellis_opts}, {NULL, NULL} }; const gchar *srt_codeset_table[] = { "ANSI_X3.4-1968", "ANSI_X3.4-1986", "ANSI_X3.4", "ANSI_X3.110-1983", "ANSI_X3.110", "ASCII", "ECMA-114", "ECMA-118", "ECMA-128", "ECMA-CYRILLIC", "IEC_P27-1", "ISO-8859-1", "ISO-8859-2", "ISO-8859-3", "ISO-8859-4", "ISO-8859-5", "ISO-8859-6", "ISO-8859-7", "ISO-8859-8", "ISO-8859-9", "ISO-8859-9E", "ISO-8859-10", "ISO-8859-11", "ISO-8859-13", "ISO-8859-14", "ISO-8859-15", "ISO-8859-16", "UTF-7", "UTF-8", "UTF-16", "UTF-16LE", "UTF-16BE", "UTF-32", "UTF-32LE", "UTF-32BE", NULL }; #define SRT_TABLE_SIZE (sizeof(srt_codeset_table)/ sizeof(char*)-1) #if 0 typedef struct iso639_lang_t { char * eng_name; /* Description in English */ char * native_name; /* Description in native language */ char * iso639_1; /* ISO-639-1 (2 characters) code */ char * iso639_2; /* ISO-639-2/t (3 character) code */ char * iso639_2b; /* ISO-639-2/b code (if different from above) */ } iso639_lang_t; #endif const iso639_lang_t ghb_language_table[] = { { "Any", "", "zz", "und" }, { "Afar", "", "aa", "aar" }, { "Abkhazian", "", "ab", "abk" }, { "Afrikaans", "", "af", "afr" }, { "Akan", "", "ak", "aka" }, { "Albanian", "", "sq", "sqi", "alb" }, { "Amharic", "", "am", "amh" }, { "Arabic", "", "ar", "ara" }, { "Aragonese", "", "an", "arg" }, { "Armenian", "", "hy", "hye", "arm" }, { "Assamese", "", "as", "asm" }, { "Avaric", "", "av", "ava" }, { "Avestan", "", "ae", "ave" }, { "Aymara", "", "ay", "aym" }, { "Azerbaijani", "", "az", "aze" }, { "Bashkir", "", "ba", "bak" }, { "Bambara", "", "bm", "bam" }, { "Basque", "", "eu", "eus", "baq" }, { "Belarusian", "", "be", "bel" }, { "Bengali", "", "bn", "ben" }, { "Bihari", "", "bh", "bih" }, { "Bislama", "", "bi", "bis" }, { "Bosnian", "", "bs", "bos" }, { "Breton", "", "br", "bre" }, { "Bulgarian", "", "bg", "bul" }, { "Burmese", "", "my", "mya", "bur" }, { "Catalan", "", "ca", "cat" }, { "Chamorro", "", "ch", "cha" }, { "Chechen", "", "ce", "che" }, { "Chinese", "", "zh", "zho", "chi" }, { "Church Slavic", "", "cu", "chu" }, { "Chuvash", "", "cv", "chv" }, { "Cornish", "", "kw", "cor" }, { "Corsican", "", "co", "cos" }, { "Cree", "", "cr", "cre" }, { "Czech", "", "cs", "ces", "cze" }, { "Danish", "Dansk", "da", "dan" }, { "German", "Deutsch", "de", "deu", "ger" }, { "Divehi", "", "dv", "div" }, { "Dzongkha", "", "dz", "dzo" }, { "English", "English", "en", "eng" }, { "Spanish", "Espanol", "es", "spa" }, { "Esperanto", "", "eo", "epo" }, { "Estonian", "", "et", "est" }, { "Ewe", "", "ee", "ewe" }, { "Faroese", "", "fo", "fao" }, { "Fijian", "", "fj", "fij" }, { "French", "Francais", "fr", "fra", "fre" }, { "Western Frisian", "", "fy", "fry" }, { "Fulah", "", "ff", "ful" }, { "Georgian", "", "ka", "kat", "geo" }, { "Gaelic (Scots)", "", "gd", "gla" }, { "Irish", "", "ga", "gle" }, { "Galician", "", "gl", "glg" }, { "Manx", "", "gv", "glv" }, { "Greek, Modern", "", "el", "ell", "gre" }, { "Guarani", "", "gn", "grn" }, { "Gujarati", "", "gu", "guj" }, { "Haitian", "", "ht", "hat" }, { "Hausa", "", "ha", "hau" }, { "Hebrew", "", "he", "heb" }, { "Herero", "", "hz", "her" }, { "Hindi", "", "hi", "hin" }, { "Hiri Motu", "", "ho", "hmo" }, { "Croatian", "Hrvatski", "hr", "hrv", "scr" }, { "Igbo", "", "ig", "ibo" }, { "Ido", "", "io", "ido" }, { "Icelandic", "Islenska", "is", "isl", "ice" }, { "Sichuan Yi", "", "ii", "iii" }, { "Inuktitut", "", "iu", "iku" }, { "Interlingue", "", "ie", "ile" }, { "Interlingua", "", "ia", "ina" }, { "Indonesian", "", "id", "ind" }, { "Inupiaq", "", "ik", "ipk" }, { "Italian", "Italiano", "it", "ita" }, { "Javanese", "", "jv", "jav" }, { "Japanese", "", "ja", "jpn" }, { "Kalaallisut", "", "kl", "kal" }, { "Kannada", "", "kn", "kan" }, { "Kashmiri", "", "ks", "kas" }, { "Kanuri", "", "kr", "kau" }, { "Kazakh", "", "kk", "kaz" }, { "Central Khmer", "", "km", "khm" }, { "Kikuyu", "", "ki", "kik" }, { "Kinyarwanda", "", "rw", "kin" }, { "Kirghiz", "", "ky", "kir" }, { "Komi", "", "kv", "kom" }, { "Kongo", "", "kg", "kon" }, { "Korean", "", "ko", "kor" }, { "Kuanyama", "", "kj", "kua" }, { "Kurdish", "", "ku", "kur" }, { "Lao", "", "lo", "lao" }, { "Latin", "", "la", "lat" }, { "Latvian", "", "lv", "lav" }, { "Limburgan", "", "li", "lim" }, { "Lingala", "", "ln", "lin" }, { "Lithuanian", "", "lt", "lit" }, { "Luxembourgish", "", "lb", "ltz" }, { "Luba-Katanga", "", "lu", "lub" }, { "Ganda", "", "lg", "lug" }, { "Macedonian", "", "mk", "mkd", "mac" }, { "Hungarian", "Magyar", "hu", "hun" }, { "Marshallese", "", "mh", "mah" }, { "Malayalam", "", "ml", "mal" }, { "Maori", "", "mi", "mri", "mao" }, { "Marathi", "", "mr", "mar" }, { "Malay", "", "ms", "msa", "msa" }, { "Malagasy", "", "mg", "mlg" }, { "Maltese", "", "mt", "mlt" }, { "Moldavian", "", "mo", "mol" }, { "Mongolian", "", "mn", "mon" }, { "Nauru", "", "na", "nau" }, { "Navajo", "", "nv", "nav" }, { "Dutch", "Nederlands", "nl", "nld", "dut" }, { "Ndebele, South", "", "nr", "nbl" }, { "Ndebele, North", "", "nd", "nde" }, { "Ndonga", "", "ng", "ndo" }, { "Nepali", "", "ne", "nep" }, { "Norwegian", "Norsk", "no", "nor" }, { "Norwegian Nynorsk", "", "nn", "nno" }, { "Norwegian Bokmål", "", "nb", "nob" }, { "Chichewa; Nyanja", "", "ny", "nya" }, { "Occitan", "", "oc", "oci" }, { "Ojibwa", "", "oj", "oji" }, { "Oriya", "", "or", "ori" }, { "Oromo", "", "om", "orm" }, { "Ossetian", "", "os", "oss" }, { "Panjabi", "", "pa", "pan" }, { "Persian", "", "fa", "fas", "per" }, { "Pali", "", "pi", "pli" }, { "Polish", "", "pl", "pol" }, { "Portuguese", "Portugues", "pt", "por" }, { "Pushto", "", "ps", "pus" }, { "Quechua", "", "qu", "que" }, { "Romansh", "", "rm", "roh" }, { "Romanian", "", "ro", "ron", "rum" }, { "Rundi", "", "rn", "run" }, { "Russian", "", "ru", "rus" }, { "Sango", "", "sg", "sag" }, { "Sanskrit", "", "sa", "san" }, { "Serbian", "", "sr", "srp", "scc" }, { "Sinhala", "", "si", "sin" }, { "Slovak", "", "sk", "slk", "slo" }, { "Slovenian", "", "sl", "slv" }, { "Northern Sami", "", "se", "sme" }, { "Samoan", "", "sm", "smo" }, { "Shona", "", "sn", "sna" }, { "Sindhi", "", "sd", "snd" }, { "Somali", "", "so", "som" }, { "Sotho, Southern", "", "st", "sot" }, { "Sardinian", "", "sc", "srd" }, { "Swati", "", "ss", "ssw" }, { "Sundanese", "", "su", "sun" }, { "Finnish", "Suomi", "fi", "fin" }, { "Swahili", "", "sw", "swa" }, { "Swedish", "Svenska", "sv", "swe" }, { "Tahitian", "", "ty", "tah" }, { "Tamil", "", "ta", "tam" }, { "Tatar", "", "tt", "tat" }, { "Telugu", "", "te", "tel" }, { "Tajik", "", "tg", "tgk" }, { "Tagalog", "", "tl", "tgl" }, { "Thai", "", "th", "tha" }, { "Tibetan", "", "bo", "bod", "tib" }, { "Tigrinya", "", "ti", "tir" }, { "Tonga", "", "to", "ton" }, { "Tswana", "", "tn", "tsn" }, { "Tsonga", "", "ts", "tso" }, { "Turkmen", "", "tk", "tuk" }, { "Turkish", "", "tr", "tur" }, { "Twi", "", "tw", "twi" }, { "Uighur", "", "ug", "uig" }, { "Ukrainian", "", "uk", "ukr" }, { "Urdu", "", "ur", "urd" }, { "Uzbek", "", "uz", "uzb" }, { "Venda", "", "ve", "ven" }, { "Vietnamese", "", "vi", "vie" }, { "Volapük", "", "vo", "vol" }, { "Welsh", "", "cy", "cym", "wel" }, { "Walloon", "", "wa", "wln" }, { "Wolof", "", "wo", "wol" }, { "Xhosa", "", "xh", "xho" }, { "Yiddish", "", "yi", "yid" }, { "Yoruba", "", "yo", "yor" }, { "Zhuang", "", "za", "zha" }, { "Zulu", "", "zu", "zul" }, {NULL, NULL, NULL, NULL} }; #define LANG_TABLE_SIZE (sizeof(ghb_language_table)/ sizeof(iso639_lang_t)-1) static void audio_bitrate_opts_set(GtkBuilder *builder, const gchar *name); static void del_tree(const gchar *name, gboolean del_top) { const gchar *file; if (g_file_test(name, G_FILE_TEST_IS_DIR)) { GDir *gdir = g_dir_open(name, 0, NULL); file = g_dir_read_name(gdir); while (file) { gchar *path; path = g_strdup_printf("%s/%s", name, file); del_tree(path, TRUE); g_free(path); file = g_dir_read_name(gdir); } if (del_top) g_rmdir(name); g_dir_close(gdir); } else { g_unlink(name); } } const gchar* ghb_version() { return hb_get_version(NULL); } float ghb_vquality_default(signal_user_data_t *ud) { float quality; gint vcodec; vcodec = ghb_settings_video_encoder_codec(ud->settings, "VideoEncoder"); switch (vcodec) { case HB_VCODEC_X265: case HB_VCODEC_X264: return 20; case HB_VCODEC_THEORA: return 45; case HB_VCODEC_FFMPEG_MPEG2: case HB_VCODEC_FFMPEG_MPEG4: return 3; default: { float min, max, step; int direction; hb_video_quality_get_limits(vcodec, &min, &max, &step, &direction); // Pick something that is 70% of max // Probably too low for some and too high for others... quality = (max - min) * 0.7; if (direction) quality = max - quality; } } return quality; } void ghb_vquality_range( signal_user_data_t *ud, float *min, float *max, float *step, float *page, gint *digits, int *direction) { float min_step; gint vcodec; vcodec = ghb_settings_video_encoder_codec(ud->settings, "VideoEncoder"); *page = 10; *digits = 0; hb_video_quality_get_limits(vcodec, min, max, &min_step, direction); *step = ghb_settings_combo_double(ud->prefs, "VideoQualityGranularity"); if (*step < min_step) *step = min_step; if ((int)(*step * 100) % 10 != 0) *digits = 2; else if ((int)(*step * 10) % 10 != 0) *digits = 1; } gint find_combo_entry(combo_opts_t *opts, const GValue *gval) { gint ii; if (opts == NULL) return 0; if (G_VALUE_TYPE(gval) == G_TYPE_STRING) { gchar *str; str = ghb_value_string(gval); for (ii = 0; ii < opts->count; ii++) { if (strcmp(opts->map[ii].shortOpt, str) == 0) { break; } } g_free(str); return ii; } else if (G_VALUE_TYPE(gval) == G_TYPE_DOUBLE) { gdouble val; val = ghb_value_double(gval); for (ii = 0; ii < opts->count; ii++) { if (opts->map[ii].ivalue == val) { break; } } return ii; } else if (G_VALUE_TYPE(gval) == G_TYPE_INT || G_VALUE_TYPE(gval) == G_TYPE_BOOLEAN || G_VALUE_TYPE(gval) == G_TYPE_INT64) { gint64 val; val = ghb_value_int64(gval); for (ii = 0; ii < opts->count; ii++) { if ((gint64)opts->map[ii].ivalue == val) { break; } } return ii; } return opts->count; } static const gchar* lookup_generic_string(combo_opts_t *opts, const GValue *gval) { gint ii; const gchar *result = ""; ii = find_combo_entry(opts, gval); if (ii < opts->count) { result = opts->map[ii].svalue; } return result; } static gint lookup_generic_int(combo_opts_t *opts, const GValue *gval) { gint ii; gint result = -1; if (opts == NULL) return 0; ii = find_combo_entry(opts, gval); if (ii < opts->count) { result = opts->map[ii].ivalue; } return result; } static gdouble lookup_generic_double(combo_opts_t *opts, const GValue *gval) { gint ii; gdouble result = -1; ii = find_combo_entry(opts, gval); if (ii < opts->count) { result = opts->map[ii].ivalue; } return result; } static const gchar* lookup_generic_option(combo_opts_t *opts, const GValue *gval) { gint ii; const gchar *result = ""; ii = find_combo_entry(opts, gval); if (ii < opts->count) { result = opts->map[ii].option; } return result; } gint ghb_find_closest_audio_samplerate(gint irate) { gint result; const hb_rate_t *rate; result = 0; for (rate = hb_audio_samplerate_get_next(NULL); rate != NULL; rate = hb_audio_samplerate_get_next(rate)) { if (irate <= rate->rate) { result = rate->rate; break; } } return result; } const iso639_lang_t* ghb_iso639_lookup_by_int(int idx) { return &ghb_language_table[idx]; } int ghb_lookup_audio_lang(const GValue *glang) { gint ii; gchar *str; str = ghb_value_string(glang); for (ii = 0; ii < LANG_TABLE_SIZE; ii++) { if (ghb_language_table[ii].iso639_2 && strcmp(ghb_language_table[ii].iso639_2, str) == 0) { g_free(str); return ii; } if (ghb_language_table[ii].iso639_2b && strcmp(ghb_language_table[ii].iso639_2b, str) == 0) { g_free(str); return ii; } if (ghb_language_table[ii].iso639_1 && strcmp(ghb_language_table[ii].iso639_1, str) == 0) { g_free(str); return ii; } if (ghb_language_table[ii].native_name && strcmp(ghb_language_table[ii].native_name, str) == 0) { g_free(str); return ii; } if (ghb_language_table[ii].eng_name && strcmp(ghb_language_table[ii].eng_name, str) == 0) { g_free(str); return ii; } } g_free(str); return -1; } static int lookup_audio_lang_int(const GValue *glang) { gint ii = ghb_lookup_audio_lang(glang); if (ii >= 0) return ii; return 0; } static const gchar* lookup_audio_lang_option(const GValue *glang) { gint ii = ghb_lookup_audio_lang(glang); if (ii >= 0) { if (ghb_language_table[ii].native_name[0] != 0) return ghb_language_table[ii].native_name; else return ghb_language_table[ii].eng_name; } return "Any"; } // Handle for libhb. Gets set by ghb_backend_init() static hb_handle_t * h_scan = NULL; static hb_handle_t * h_queue = NULL; extern void hb_get_temporary_directory(char path[512]); gchar* ghb_get_tmp_dir() { char dir[512]; hb_get_temporary_directory(dir); return g_strdup(dir); } void ghb_hb_cleanup(gboolean partial) { char dir[512]; hb_get_temporary_directory(dir); del_tree(dir, !partial); } gint ghb_subtitle_track_source(GValue *settings, gint track) { gint title_id, titleindex; const hb_title_t *title; if (track == -2) return SRTSUB; if (track < 0) return VOBSUB; title_id = ghb_settings_get_int(settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (title == NULL) return VOBSUB; hb_subtitle_t * sub; sub = hb_list_item( title->list_subtitle, track); if (sub != NULL) return sub->source; else return VOBSUB; } const gchar* ghb_subtitle_track_lang(GValue *settings, gint track) { gint title_id, titleindex; const hb_title_t * title; title_id = ghb_settings_get_int(settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (title == NULL) // Bad titleindex goto fail; if (track == -1) return ghb_get_user_audio_lang(settings, title, 0); if (track < 0) goto fail; hb_subtitle_t * sub; sub = hb_list_item( title->list_subtitle, track); if (sub != NULL) return sub->iso639_2; fail: return "und"; } static gint search_audio_bitrates(gint rate) { const hb_rate_t *hbrate; for (hbrate = hb_audio_bitrate_get_next(NULL); hbrate != NULL; hbrate = hb_audio_bitrate_get_next(hbrate)) { if (hbrate->rate == rate) return 1; } return 0; } static gboolean find_combo_item_by_int(GtkTreeModel *store, gint value, GtkTreeIter *iter); static void grey_combo_box_item(GtkComboBox *combo, gint value, gboolean grey) { GtkListStore *store; GtkTreeIter iter; store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); if (find_combo_item_by_int(GTK_TREE_MODEL(store), value, &iter)) { gtk_list_store_set(store, &iter, 1, !grey, -1); } } static void grey_builder_combo_box_item(GtkBuilder *builder, const gchar *name, gint value, gboolean grey) { GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); grey_combo_box_item(combo, value, grey); } void ghb_mix_opts_filter(GtkComboBox *combo, gint acodec) { g_debug("ghb_mix_opts_filter()\n"); const hb_mixdown_t *mix; for (mix = hb_mixdown_get_next(NULL); mix != NULL; mix = hb_mixdown_get_next(mix)) { grey_combo_box_item(combo, mix->amixdown, !hb_mixdown_has_codec_support(mix->amixdown, acodec)); } } static void grey_mix_opts(signal_user_data_t *ud, gint acodec, gint64 layout) { g_debug("grey_mix_opts()\n"); const hb_mixdown_t *mix; for (mix = hb_mixdown_get_next(NULL); mix != NULL; mix = hb_mixdown_get_next(mix)) { grey_builder_combo_box_item(ud->builder, "AudioMixdown", mix->amixdown, !hb_mixdown_is_supported(mix->amixdown, acodec, layout)); } } void ghb_grey_combo_options(signal_user_data_t *ud) { gint track, title_id, titleindex, acodec, fallback; const hb_title_t *title; hb_audio_config_t *aconfig = NULL; title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); track = ghb_settings_get_int(ud->settings, "AudioTrack"); aconfig = ghb_get_audio_info(title, track); const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(ud->settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); grey_builder_combo_box_item(ud->builder, "x264_analyse", 4, TRUE); const hb_encoder_t *enc; for (enc = hb_audio_encoder_get_next(NULL); enc != NULL; enc = hb_audio_encoder_get_next(enc)) { if (!(mux->format & enc->muxers)) { grey_builder_combo_box_item(ud->builder, "AudioEncoder", enc->codec, TRUE); grey_builder_combo_box_item(ud->builder, "AudioEncoderFallback", enc->codec, TRUE); } else { grey_builder_combo_box_item(ud->builder, "AudioEncoder", enc->codec, FALSE); grey_builder_combo_box_item(ud->builder, "AudioEncoderFallback", enc->codec, FALSE); } } for (enc = hb_video_encoder_get_next(NULL); enc != NULL; enc = hb_video_encoder_get_next(enc)) { if (!(mux->format & enc->muxers)) { grey_builder_combo_box_item(ud->builder, "VideoEncoder", enc->codec, TRUE); } else { grey_builder_combo_box_item(ud->builder, "VideoEncoder", enc->codec, FALSE); } } if (aconfig && (aconfig->in.codec & HB_ACODEC_MASK) != HB_ACODEC_MP3) { grey_builder_combo_box_item(ud->builder, "AudioEncoder", HB_ACODEC_MP3_PASS, TRUE); } if (aconfig && (aconfig->in.codec & HB_ACODEC_MASK) != HB_ACODEC_FFAAC) { grey_builder_combo_box_item(ud->builder, "AudioEncoder", HB_ACODEC_AAC_PASS, TRUE); } if (aconfig && (aconfig->in.codec & HB_ACODEC_MASK) != HB_ACODEC_AC3) { grey_builder_combo_box_item(ud->builder, "AudioEncoder", HB_ACODEC_AC3_PASS, TRUE); } if (aconfig && (aconfig->in.codec & HB_ACODEC_MASK) != HB_ACODEC_DCA) { grey_builder_combo_box_item(ud->builder, "AudioEncoder", HB_ACODEC_DCA_PASS, TRUE); } if (aconfig && (aconfig->in.codec & HB_ACODEC_MASK) != HB_ACODEC_DCA_HD) { grey_builder_combo_box_item(ud->builder, "AudioEncoder", HB_ACODEC_DCA_HD_PASS, TRUE); } acodec = ghb_settings_audio_encoder_codec(ud->settings, "AudioEncoder"); gint64 layout = aconfig != NULL ? aconfig->in.channel_layout : ~0; fallback = ghb_select_fallback(ud->settings, acodec); gint copy_mask = ghb_get_copy_mask(ud->settings); acodec = ghb_select_audio_codec(mux->format, aconfig, acodec, fallback, copy_mask); grey_mix_opts(ud, acodec, layout); } gint ghb_get_best_mix(hb_audio_config_t *aconfig, gint acodec, gint mix) { gint layout; layout = aconfig ? aconfig->in.channel_layout : AV_CH_LAYOUT_5POINT1; if (mix == HB_AMIXDOWN_NONE) mix = HB_INVALID_AMIXDOWN; return hb_mixdown_get_best(acodec, layout, mix); } // Set up the model for the combo box void ghb_init_combo_box(GtkComboBox *combo) { GtkListStore *store; GtkCellRenderer *cell; g_debug("ghb_init_combo_box()\n"); // First modify the combobox model to allow greying out of options if (combo == NULL) return; // Store contains: // 1 - String to display // 2 - bool indicating whether the entry is selectable (grey or not) // 3 - String that is used for presets // 4 - Int value determined by backend // 5 - String value determined by backend store = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_STRING); gtk_combo_box_set_model(combo, GTK_TREE_MODEL(store)); if (!gtk_combo_box_get_has_entry(combo)) { gtk_cell_layout_clear(GTK_CELL_LAYOUT(combo)); cell = GTK_CELL_RENDERER(gtk_cell_renderer_text_new()); g_object_set(cell, "max-width-chars", 60, NULL); g_object_set(cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell, "markup", 0, "sensitive", 1, NULL); } else { // Combo box entry gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo), 0); } } // Set up the model for the combo box static void init_combo_box(GtkBuilder *builder, const gchar *name) { GtkComboBox *combo; g_debug("init_combo_box() %s\n", name); // First modify the combobox model to allow greying out of options combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); ghb_init_combo_box(combo); } void ghb_audio_samplerate_opts_set(GtkComboBox *combo) { GtkTreeIter iter; GtkListStore *store; gchar *str; g_debug("audio_samplerate_opts_set ()\n"); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); // Add an item for "Same As Source" gtk_list_store_append(store, &iter); str = g_strdup_printf("%s", _("Same as source")); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, 2, "source", 3, 0.0, 4, "source", -1); g_free(str); const hb_rate_t *rate; for (rate = hb_audio_samplerate_get_next(NULL); rate != NULL; rate = hb_audio_samplerate_get_next(rate)) { gtk_list_store_append(store, &iter); str = g_strdup_printf("%s", rate->name); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, 2, rate->name, 3, (gdouble)rate->rate, 4, rate->name, -1); g_free(str); } } static void audio_samplerate_opts_set(GtkBuilder *builder, const gchar *name) { g_debug("audio_samplerate_opts_set ()\n"); GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); ghb_audio_samplerate_opts_set(combo); } const hb_rate_t sas_rate = { .name = N_("Same as source"), .rate = 0, }; const char* ghb_audio_samplerate_get_short_name(int rate) { const char *name; name = hb_audio_samplerate_get_name(rate); if (name == NULL) name = "source"; return name; } const hb_rate_t* ghb_lookup_audio_samplerate(const char *name) { // Check for special "Same as source" value if (!strncmp(name, "source", 8)) return &sas_rate; // First find an enabled rate int rate = hb_audio_samplerate_get_from_name(name); // Now find the matching rate info const hb_rate_t *hb_rate, *first; for (first = hb_rate = hb_audio_samplerate_get_next(NULL); hb_rate != NULL; hb_rate = hb_audio_samplerate_get_next(hb_rate)) { if (rate == hb_rate->rate) { return hb_rate; } } // Return a default rate if nothing matches return first; } int ghb_lookup_audio_samplerate_rate(const char *name) { return ghb_lookup_audio_samplerate(name)->rate; } int ghb_settings_audio_samplerate_rate(const GValue *settings, const char *name) { const char *rate_id = ghb_settings_get_const_string(settings, name); return ghb_lookup_audio_samplerate_rate(rate_id); } const hb_rate_t* ghb_settings_audio_samplerate(const GValue *settings, const char *name) { const char *rate_id = ghb_settings_get_const_string(settings, name); return ghb_lookup_audio_samplerate(rate_id); } static void video_framerate_opts_set(GtkBuilder *builder, const gchar *name) { GtkTreeIter iter; GtkListStore *store; g_debug("video_framerate_opts_set ()\n"); GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); // Add an item for "Same As Source" gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, _("Same as source"), 1, TRUE, 2, "source", 3, 0.0, 4, "source", -1); const hb_rate_t *rate; for (rate = hb_video_framerate_get_next(NULL); rate != NULL; rate = hb_video_framerate_get_next(rate)) { gchar *desc = ""; gchar *option; if (strcmp(rate->name, "23.976") == 0) { desc = _("(NTSC Film)"); } else if (strcmp(rate->name, "25") == 0) { desc = _("(PAL Film/Video)"); } else if (strcmp(rate->name, "29.97") == 0) { desc = _("(NTSC Video)"); } option = g_strdup_printf ("%s %s", rate->name, desc); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, option, 1, TRUE, 2, rate->name, 3, (gdouble)rate->rate, 4, rate->name, -1); g_free(option); } } const hb_rate_t* ghb_lookup_video_framerate(const char *name) { // Check for special "Same as source" value if (!strncmp(name, "source", 8)) return &sas_rate; // First find an enabled rate int rate = hb_video_framerate_get_from_name(name); // Now find the matching rate info const hb_rate_t *hb_rate, *first; for (first = hb_rate = hb_video_framerate_get_next(NULL); hb_rate != NULL; hb_rate = hb_video_framerate_get_next(hb_rate)) { if (rate == hb_rate->rate) { return hb_rate; } } // Return a default rate if nothing matches return first; } int ghb_lookup_video_framerate_rate(const char *name) { return ghb_lookup_video_framerate(name)->rate; } int ghb_settings_video_framerate_rate(const GValue *settings, const char *name) { const char *rate_id = ghb_settings_get_const_string(settings, name); return ghb_lookup_video_framerate_rate(rate_id); } const hb_rate_t* ghb_settings_video_framerate(const GValue *settings, const char *name) { const char *rate_id = ghb_settings_get_const_string(settings, name); return ghb_lookup_video_framerate(rate_id); } static void video_encoder_opts_set( GtkBuilder *builder, const gchar *name) { GtkTreeIter iter; GtkListStore *store; gchar *str; g_debug("video_encoder_opts_set ()\n"); GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); const hb_encoder_t *enc; for (enc = hb_video_encoder_get_next(NULL); enc != NULL; enc = hb_video_encoder_get_next(enc)) { gtk_list_store_append(store, &iter); str = g_strdup_printf("%s", enc->name); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, 2, enc->short_name, 3, (gdouble)enc->codec, 4, enc->short_name, -1); g_free(str); } } const hb_encoder_t* ghb_lookup_video_encoder(const char *name) { // First find an enabled encoder int codec = hb_video_encoder_get_from_name(name); // Now find the matching encoder info const hb_encoder_t *enc, *first; for (first = enc = hb_video_encoder_get_next(NULL); enc != NULL; enc = hb_video_encoder_get_next(enc)) { if (codec == enc->codec) { return enc; } } // Return a default encoder if nothing matches return first; } int ghb_lookup_video_encoder_codec(const char *name) { return ghb_lookup_video_encoder(name)->codec; } int ghb_settings_video_encoder_codec(const GValue *settings, const char *name) { const char *encoder_id = ghb_settings_get_const_string(settings, name); return ghb_lookup_video_encoder_codec(encoder_id); } const hb_encoder_t* ghb_settings_video_encoder(const GValue *settings, const char *name) { const char *encoder_id = ghb_settings_get_const_string(settings, name); return ghb_lookup_video_encoder(encoder_id); } void ghb_audio_encoder_opts_set_with_mask( GtkComboBox *combo, int mask, int neg_mask) { GtkTreeIter iter; GtkListStore *store; gchar *str; g_debug("ghb_audio_encoder_opts_set_with_mask()\n"); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); const hb_encoder_t *enc; for (enc = hb_audio_encoder_get_next(NULL); enc != NULL; enc = hb_audio_encoder_get_next(enc)) { if ((mask & enc->codec) && !(neg_mask & enc->codec)) { gtk_list_store_append(store, &iter); str = g_strdup_printf("%s", enc->name); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, 2, enc->short_name, 3, (gdouble)enc->codec, 4, enc->short_name, -1); g_free(str); } } } const hb_encoder_t* ghb_lookup_audio_encoder(const char *name) { // First find an enabled encoder int codec = hb_audio_encoder_get_from_name(name); // Now find the matching encoder info const hb_encoder_t *enc, *first; for (first = enc = hb_audio_encoder_get_next(NULL); enc != NULL; enc = hb_audio_encoder_get_next(enc)) { if (codec == enc->codec) { return enc; } } // Return a default encoder if nothing matches return first; } int ghb_lookup_audio_encoder_codec(const char *name) { return ghb_lookup_audio_encoder(name)->codec; } int ghb_settings_audio_encoder_codec(const GValue *settings, const char *name) { const char *encoder_id = ghb_settings_get_const_string(settings, name); return ghb_lookup_audio_encoder_codec(encoder_id); } const hb_encoder_t* ghb_settings_audio_encoder(const GValue *settings, const char *name) { const char *encoder_id = ghb_settings_get_const_string(settings, name); return ghb_lookup_audio_encoder(encoder_id); } static void audio_encoder_opts_set_with_mask( GtkBuilder *builder, const gchar *name, int mask, int neg_mask) { g_debug("audio_encoder_opts_set_with_mask()\n"); GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); ghb_audio_encoder_opts_set_with_mask(combo, mask, neg_mask); } void ghb_audio_encoder_opts_set(GtkComboBox *combo) { ghb_audio_encoder_opts_set_with_mask(combo, ~0, 0); } static void audio_encoder_opts_set( GtkBuilder *builder, const gchar *name) { GtkTreeIter iter; GtkListStore *store; gchar *str; GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); const hb_encoder_t *enc; for (enc = hb_audio_encoder_get_next(NULL); enc != NULL; enc = hb_audio_encoder_get_next(enc)) { if (enc->codec != HB_ACODEC_AUTO_PASS) { gtk_list_store_append(store, &iter); str = g_strdup_printf("%s", enc->name); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, 2, enc->short_name, 3, (gdouble)enc->codec, 4, enc->short_name, -1); g_free(str); } } } static void acodec_fallback_opts_set(GtkBuilder *builder, const gchar *name) { audio_encoder_opts_set_with_mask(builder, name, ~0, HB_ACODEC_PASS_FLAG); } void ghb_mix_opts_set(GtkComboBox *combo) { GtkTreeIter iter; GtkListStore *store; gchar *str; g_debug("mix_opts_set ()\n"); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); const hb_mixdown_t *mix; for (mix = hb_mixdown_get_next(NULL); mix != NULL; mix = hb_mixdown_get_next(mix)) { gtk_list_store_append(store, &iter); str = g_strdup_printf("%s", mix->name); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, 2, mix->short_name, 3, (gdouble)mix->amixdown, 4, mix->short_name, -1); g_free(str); } } const hb_mixdown_t* ghb_lookup_mixdown(const char *name) { // First find an enabled mixdown int mix = hb_mixdown_get_from_name(name); // Now find the matching mixdown info const hb_mixdown_t *mixdown, *first; for (first = mixdown = hb_mixdown_get_next(NULL); mixdown != NULL; mixdown = hb_mixdown_get_next(mixdown)) { if (mix == mixdown->amixdown) { return mixdown; } } // Return a default mixdown if nothing matches return first; } int ghb_lookup_mixdown_mix(const char *name) { return ghb_lookup_mixdown(name)->amixdown; } int ghb_settings_mixdown_mix(const GValue *settings, const char *name) { const char *mixdown_id = ghb_settings_get_const_string(settings, name); return ghb_lookup_mixdown_mix(mixdown_id); } const hb_mixdown_t* ghb_settings_mixdown(const GValue *settings, const char *name) { const char *mixdown_id = ghb_settings_get_const_string(settings, name); return ghb_lookup_mixdown(mixdown_id); } static void mix_opts_set(GtkBuilder *builder, const gchar *name) { g_debug("mix_opts_set ()\n"); GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); ghb_mix_opts_set(combo); } static void container_opts_set( GtkBuilder *builder, const gchar *name) { GtkTreeIter iter; GtkListStore *store; gchar *str; g_debug("hb_container_opts_set ()\n"); GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); const hb_container_t *mux; for (mux = hb_container_get_next(NULL); mux != NULL; mux = hb_container_get_next(mux)) { gtk_list_store_append(store, &iter); str = g_strdup_printf("%s", mux->name); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, 2, mux->short_name, 3, (gdouble)mux->format, 4, mux->short_name, -1); g_free(str); } } const hb_container_t* ghb_lookup_container_by_name(const gchar *name) { // First find an enabled muxer int format = hb_container_get_from_name(name); // Now find the matching muxer info const hb_container_t *mux, *first; for (first = mux = hb_container_get_next(NULL); mux != NULL; mux = hb_container_get_next(mux)) { if (format == mux->format) { return mux; } } // Return a default container if nothing matches return first; } static void srt_codeset_opts_set(GtkBuilder *builder, const gchar *name) { GtkTreeIter iter; GtkListStore *store; gint ii; g_debug("srt_codeset_opts_set ()\n"); GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); for (ii = 0; ii < SRT_TABLE_SIZE; ii++) { gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, srt_codeset_table[ii], 1, TRUE, 2, srt_codeset_table[ii], 3, (gdouble)ii, 4, srt_codeset_table[ii], -1); } } static void language_opts_set(GtkBuilder *builder, const gchar *name) { GtkTreeIter iter; GtkListStore *store; gint ii; g_debug("language_opts_set ()\n"); GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); for (ii = 0; ii < LANG_TABLE_SIZE; ii++) { const gchar *lang; if (ghb_language_table[ii].native_name[0] != 0) lang = ghb_language_table[ii].native_name; else lang = ghb_language_table[ii].eng_name; gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, lang, 1, TRUE, 2, ghb_language_table[ii].iso639_2, 3, (gdouble)ii, 4, ghb_language_table[ii].iso639_1, -1); } } const iso639_lang_t* ghb_lookup_lang(const char *name) { int ii; for (ii = 0; ii < LANG_TABLE_SIZE; ii++) { if (!strncmp(name, ghb_language_table[ii].iso639_2, 4) || !strncmp(name, ghb_language_table[ii].iso639_1, 4) || !strncmp(name, ghb_language_table[ii].iso639_2b, 4) || !strncmp(name, ghb_language_table[ii].native_name, 4) || !strncmp(name, ghb_language_table[ii].eng_name, 4)) { return &ghb_language_table[ii]; } } return NULL; } gchar* ghb_create_title_label(const hb_title_t *title) { gchar *label; if (title->type == HB_STREAM_TYPE || title->type == HB_FF_STREAM_TYPE) { if (title->duration != 0) { char *tmp; tmp = g_strdup_printf (_("%d - %02dh%02dm%02ds - %s"), title->index, title->hours, title->minutes, title->seconds, title->name); label = g_markup_escape_text(tmp, -1); g_free(tmp); } else { char *tmp; tmp = g_strdup_printf ("%d - %s", title->index, title->name); label = g_markup_escape_text(tmp, -1); g_free(tmp); } } else if (title->type == HB_BD_TYPE) { if (title->duration != 0) { label = g_strdup_printf(_("%d (%05d.MPLS) - %02dh%02dm%02ds"), title->index, title->playlist, title->hours, title->minutes, title->seconds); } else { label = g_strdup_printf(_("%d (%05d.MPLS) - Unknown Length"), title->index, title->playlist); } } else { if (title->duration != 0) { label = g_strdup_printf(_("%d - %02dh%02dm%02ds"), title->index, title->hours, title->minutes, title->seconds); } else { label = g_strdup_printf(_("%d - Unknown Length"), title->index); } } return label; } void title_opts_set(GtkBuilder *builder, const gchar *name) { GtkTreeIter iter; GtkListStore *store; hb_list_t * list = NULL; const hb_title_t * title = NULL; gint ii; gint count = 0; g_debug("title_opts_set ()\n"); GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); if (h_scan != NULL) { list = hb_get_titles( h_scan ); count = hb_list_count( list ); } if( count <= 0 ) { char *opt; // No titles. Fill in a default. gtk_list_store_append(store, &iter); opt = g_strdup_printf("%s", _("No Titles")); gtk_list_store_set(store, &iter, 0, opt, 1, TRUE, 2, "none", 3, -1.0, 4, "none", -1); g_free(opt); return; } for (ii = 0; ii < count; ii++) { char *title_opt, *title_index, *opt; title = hb_list_item(list, ii); title_opt = ghb_create_title_label(title); opt = g_strdup_printf("%s", title_opt); title_index = g_strdup_printf("%d", title->index); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, opt, 1, TRUE, 2, title_index, 3, (gdouble)title->index, 4, title_index, -1); g_free(opt); g_free(title_opt); g_free(title_index); } } static int lookup_title_index(hb_handle_t *h, int title_id) { if (h == NULL) return -1; hb_list_t *list; const hb_title_t *title; int count, ii; list = hb_get_titles(h); count = hb_list_count(list); for (ii = 0; ii < count; ii++) { title = hb_list_item(list, ii); if (title_id == title->index) { return ii; } } return -1; } const hb_title_t* lookup_title(hb_handle_t *h, int title_id, int *index) { int ii = lookup_title_index(h, title_id); if (index != NULL) *index = ii; if (ii < 0) return NULL; hb_list_t *list; list = hb_get_titles(h); return hb_list_item(list, ii); } int ghb_lookup_title_index(int title_id) { return lookup_title_index(h_scan, title_id); } const hb_title_t* ghb_lookup_title(int title_id, int *index) { return lookup_title(h_scan, title_id, index); } int ghb_lookup_queue_title_index(int title_id) { return lookup_title_index(h_queue, title_id); } const hb_title_t* ghb_lookup_queue_title(int title_id, int *index) { return lookup_title(h_queue, title_id, index); } static void video_tune_opts_set(signal_user_data_t *ud, const gchar *name) { GtkTreeIter iter; GtkListStore *store; gint ii, count = 0; // Check if encoder has been set yet. // If not, bail GValue *value = ghb_dict_lookup(ud->settings, "VideoEncoder"); if (value == NULL) return; int encoder = ghb_get_video_encoder(ud->settings); const char * const *tunes; tunes = hb_video_encoder_get_tunes(encoder); while (tunes && tunes[count]) count++; if (count == 0) return; g_debug("video_tune_opts_set ()\n"); GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, _("None"), 1, TRUE, 2, "none", 3, (gdouble)0, 4, "none", -1); for (ii = 0; ii < count; ii++) { if (strcmp(tunes[ii], "fastdecode") && strcmp(tunes[ii], "zerolatency")) { gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, tunes[ii], 1, TRUE, 2, tunes[ii], 3, (gdouble)ii + 1, 4, tunes[ii], -1); } } } static void video_profile_opts_set(signal_user_data_t *ud, const gchar *name) { GtkTreeIter iter; GtkListStore *store; gint ii, count = 0; // Check if encoder has been set yet. // If not, bail GValue *value = ghb_dict_lookup(ud->settings, "VideoEncoder"); if (value == NULL) return; int encoder = ghb_get_video_encoder(ud->settings); const char * const *profiles; profiles = hb_video_encoder_get_profiles(encoder); while (profiles && profiles[count]) count++; if (count == 0) return; g_debug("video_profile_opts_set ()\n"); GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); for (ii = 0; ii < count; ii++) { gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, profiles[ii], 1, TRUE, 2, profiles[ii], 3, (gdouble)ii, 4, profiles[ii], -1); } } static void video_level_opts_set(signal_user_data_t *ud, const gchar *name) { GtkTreeIter iter; GtkListStore *store; gint ii, count = 0; // Check if encoder has been set yet. // If not, bail GValue *value = ghb_dict_lookup(ud->settings, "VideoEncoder"); if (value == NULL) return; int encoder = ghb_get_video_encoder(ud->settings); const char * const *levels; levels = hb_video_encoder_get_levels(encoder); while (levels && levels[count]) count++; if (count == 0) return; g_debug("video_level_opts_set ()\n"); GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); for (ii = 0; ii < count; ii++) { gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, levels[ii], 1, TRUE, 2, levels[ii], 3, (gdouble)ii, 4, levels[ii], -1); } } static gboolean find_combo_item_by_int(GtkTreeModel *store, gint value, GtkTreeIter *iter) { gdouble ivalue; gboolean foundit = FALSE; if (gtk_tree_model_get_iter_first (store, iter)) { do { gtk_tree_model_get(store, iter, 3, &ivalue, -1); if (value == (gint)ivalue) { foundit = TRUE; break; } } while (gtk_tree_model_iter_next (store, iter)); } return foundit; } void audio_track_opts_set(GtkBuilder *builder, const gchar *name, const hb_title_t *title) { GtkTreeIter iter; GtkListStore *store; hb_audio_config_t * audio; gint ii; gint count = 0; gchar *opt; g_debug("audio_track_opts_set ()\n"); GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); if (title != NULL) { count = hb_list_count( title->list_audio ); } if( count <= 0 ) { // No audio. set some default opt = g_strdup_printf("%s", _("No Audio")); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, opt, 1, TRUE, 2, "none", 3, -1.0, 4, "none", -1); g_free(opt); return; } for (ii = 0; ii < count; ii++) { char idx[4]; audio = hb_list_audio_config_item(title->list_audio, ii); opt = g_strdup_printf("%d - %s", ii + 1, audio->lang.description); snprintf(idx, 4, "%d", ii); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, opt, 1, TRUE, 2, idx, 3, (gdouble)ii, 4, idx, -1); g_free(opt); } gtk_combo_box_set_active (combo, 0); } void subtitle_track_opts_set( GtkBuilder *builder, const gchar *name, const hb_title_t *title) { GtkTreeIter iter; GtkListStore *store; hb_subtitle_t * subtitle; gint ii, count = 0; GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); if (title != NULL) { count = hb_list_count( title->list_subtitle ); } if (count > 0) { gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, _("Foreign Audio Search"), 1, TRUE, 2, "-1", 3, -1.0, 4, "auto", -1); for (ii = 0; ii < count; ii++) { gchar *opt; char idx[4]; subtitle = hb_list_item(title->list_subtitle, ii); opt = g_strdup_printf("%d - %s (%s)", ii+1, subtitle->lang, hb_subsource_name(subtitle->source)); snprintf(idx, 4, "%d", ii); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, opt, 1, TRUE, 2, idx, 3, (gdouble)ii, 4, idx, -1); g_free(opt); } } else { gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, _("None"), 1, TRUE, 2, "0", 3, 0.0, 4, "none", -1); } gtk_combo_box_set_active (combo, 0); } // Get title id of feature or longest title gint ghb_longest_title() { hb_title_set_t * title_set; const hb_title_t * title; gint count = 0, ii, longest = -1; int64_t duration = 0; g_debug("ghb_longest_title ()\n"); if (h_scan == NULL) return 0; title_set = hb_get_title_set( h_scan ); count = hb_list_count( title_set->list_title ); if (count < 1) return -1; // Check that the feature title in the title_set exists in the list // of titles. If not, pick the longest. for (ii = 0; ii < count; ii++) { title = hb_list_item(title_set->list_title, ii); if (title->index == title_set->feature) return title_set->feature; if (title->duration > duration) longest = title->index; } return longest; } const gchar* ghb_get_source_audio_lang(const hb_title_t *title, gint track) { hb_audio_config_t * audio; const gchar *lang = "und"; g_debug("ghb_lookup_1st_audio_lang ()\n"); if (title == NULL) return lang; if (hb_list_count( title->list_audio ) <= track) return lang; audio = hb_list_audio_config_item(title->list_audio, track); if (audio == NULL) return lang; lang = audio->lang.iso639_2; return lang; } gint ghb_find_audio_track(const hb_title_t *title, const gchar *lang, int start) { hb_audio_config_t * audio; gint ii, count = 0; if (title != NULL) { count = hb_list_count(title->list_audio); } for (ii = start; ii < count; ii++) { audio = hb_list_audio_config_item(title->list_audio, ii); if (!strcmp(lang, audio->lang.iso639_2) || !strcmp(lang, "und")) { return ii; } } return -1; } gint ghb_find_subtitle_track(const hb_title_t * title, const gchar * lang, int start) { hb_subtitle_t * subtitle; gint count, ii; count = hb_list_count(title->list_subtitle); // Try to find an item that matches the preferred language for (ii = start; ii < count; ii++) { subtitle = (hb_subtitle_t*)hb_list_item( title->list_subtitle, ii ); if (strcmp(lang, subtitle->iso639_2) == 0 || strcmp(lang, "und") == 0) { return ii; } } return -1; } #if 0 static void generic_opts_set(GtkBuilder *builder, const gchar *name, combo_opts_t *opts) { GtkTreeIter iter; GtkListStore *store; gint ii; g_debug("generic_opts_set ()\n"); if (name == NULL || opts == NULL) return; GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); for (ii = 0; ii < opts->count; ii++) { gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, gettext(opts->map[ii].option), 1, TRUE, 2, opts->map[ii].shortOpt, 3, opts->map[ii].ivalue, 4, opts->map[ii].svalue, -1); } } #endif static void small_opts_set(GtkBuilder *builder, const gchar *name, combo_opts_t *opts) { GtkTreeIter iter; GtkListStore *store; gint ii; gchar *str; g_debug("small_opts_set ()\n"); if (name == NULL || opts == NULL) return; GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); for (ii = 0; ii < opts->count; ii++) { gtk_list_store_append(store, &iter); str = g_strdup_printf("%s", gettext(opts->map[ii].option)); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, 2, opts->map[ii].shortOpt, 3, opts->map[ii].ivalue, 4, opts->map[ii].svalue, -1); g_free(str); } } combo_opts_t* find_combo_table(const gchar *name) { gint ii; for (ii = 0; combo_name_map[ii].name != NULL; ii++) { if (strcmp(name, combo_name_map[ii].name) == 0) { return combo_name_map[ii].opts; } } return NULL; } gint ghb_lookup_combo_int(const gchar *name, const GValue *gval) { if (gval == NULL) return 0; else if (strcmp(name, "SrtLanguage") == 0) return lookup_audio_lang_int(gval); else { return lookup_generic_int(find_combo_table(name), gval); } g_warning("ghb_lookup_combo_int() couldn't find %s", name); return 0; } gdouble ghb_lookup_combo_double(const gchar *name, const GValue *gval) { if (gval == NULL) return 0; else if (strcmp(name, "SrtLanguage") == 0) return lookup_audio_lang_int(gval); else { return lookup_generic_double(find_combo_table(name), gval); } g_warning("ghb_lookup_combo_double() couldn't find %s", name); return 0; } const gchar* ghb_lookup_combo_option(const gchar *name, const GValue *gval) { if (gval == NULL) return NULL; else if (strcmp(name, "SrtLanguage") == 0) return lookup_audio_lang_option(gval); else { return lookup_generic_option(find_combo_table(name), gval); } g_warning("ghb_lookup_combo_int() couldn't find %s", name); return NULL; } const gchar* ghb_lookup_combo_string(const gchar *name, const GValue *gval) { if (gval == NULL) return NULL; else if (strcmp(name, "SrtLanguage") == 0) return lookup_audio_lang_option(gval); else { return lookup_generic_string(find_combo_table(name), gval); } g_warning("ghb_lookup_combo_int() couldn't find %s", name); return NULL; } void ghb_init_lang_list_box(GtkListBox *list_box) { int ii; for (ii = 0; ii < LANG_TABLE_SIZE; ii++) { const char *lang; if (ghb_language_table[ii].native_name != NULL && ghb_language_table[ii].native_name[0] != 0) { lang = ghb_language_table[ii].native_name; } else { lang = ghb_language_table[ii].eng_name; } GtkWidget *label = gtk_label_new(lang); g_object_set_data(G_OBJECT(label), "lang_idx", (gpointer)(intptr_t)ii); gtk_widget_show(label); gtk_list_box_insert(list_box, label, -1); } } void ghb_update_ui_combo_box( signal_user_data_t *ud, const gchar *name, const void *user_data, gboolean all) { GtkComboBox *combo = NULL; gint signal_id; gint handler_id = 0; if (name != NULL) { g_debug("ghb_update_ui_combo_box() %s\n", name); // Clearing a combo box causes a rash of "changed" events, even when // the active item is -1 (inactive). To control things, I'm disabling // the event till things are settled down. combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, name)); signal_id = g_signal_lookup("changed", GTK_TYPE_COMBO_BOX); if (signal_id > 0) { // Valid signal id found. This should always succeed. handler_id = g_signal_handler_find ((gpointer)combo, G_SIGNAL_MATCH_ID, signal_id, 0, 0, 0, 0); if (handler_id > 0) { // This should also always succeed g_signal_handler_block ((gpointer)combo, handler_id); } } } if (all) { audio_bitrate_opts_set(ud->builder, "AudioBitrate"); audio_samplerate_opts_set(ud->builder, "AudioSamplerate"); video_framerate_opts_set(ud->builder, "VideoFramerate"); mix_opts_set(ud->builder, "AudioMixdown"); video_encoder_opts_set(ud->builder, "VideoEncoder"); audio_encoder_opts_set(ud->builder, "AudioEncoder"); acodec_fallback_opts_set(ud->builder, "AudioEncoderFallback"); language_opts_set(ud->builder, "SrtLanguage"); srt_codeset_opts_set(ud->builder, "SrtCodeset"); title_opts_set(ud->builder, "title"); audio_track_opts_set(ud->builder, "AudioTrack", user_data); subtitle_track_opts_set(ud->builder, "SubtitleTrack", user_data); small_opts_set(ud->builder, "VideoQualityGranularity", &vqual_granularity_opts); small_opts_set(ud->builder, "SubtitleTrackSelectionBehavior", &subtitle_track_sel_opts); small_opts_set(ud->builder, "AudioTrackSelectionBehavior", &audio_track_sel_opts); small_opts_set(ud->builder, "PtoPType", &point_to_point_opts); small_opts_set(ud->builder, "WhenComplete", &when_complete_opts); small_opts_set(ud->builder, "PicturePAR", &par_opts); small_opts_set(ud->builder, "PictureModulus", &alignment_opts); small_opts_set(ud->builder, "LoggingLevel", &logging_opts); small_opts_set(ud->builder, "LogLongevity", &log_longevity_opts); small_opts_set(ud->builder, "check_updates", &appcast_update_opts); small_opts_set(ud->builder, "PictureDeinterlace", &deint_opts); small_opts_set(ud->builder, "PictureDetelecine", &detel_opts); small_opts_set(ud->builder, "PictureDecomb", &decomb_opts); small_opts_set(ud->builder, "PictureDenoiseFilter", &denoise_opts); small_opts_set(ud->builder, "PictureDenoisePreset", &denoise_preset_opts); small_opts_set(ud->builder, "PictureDenoiseTune", &nlmeans_tune_opts); small_opts_set(ud->builder, "x264_direct", &direct_opts); small_opts_set(ud->builder, "x264_b_adapt", &badapt_opts); small_opts_set(ud->builder, "x264_bpyramid", &bpyramid_opts); small_opts_set(ud->builder, "x264_weighted_pframes", &weightp_opts); small_opts_set(ud->builder, "x264_me", &me_opts); small_opts_set(ud->builder, "x264_subme", &subme_opts); small_opts_set(ud->builder, "x264_analyse", &analyse_opts); small_opts_set(ud->builder, "x264_trellis", &trellis_opts); video_tune_opts_set(ud, "VideoTune"); video_profile_opts_set(ud, "VideoProfile"); video_level_opts_set(ud, "VideoLevel"); container_opts_set(ud->builder, "FileFormat"); } else { if (strcmp(name, "AudioBitrate") == 0) audio_bitrate_opts_set(ud->builder, "AudioBitrate"); else if (strcmp(name, "AudioSamplerate") == 0) audio_samplerate_opts_set(ud->builder, "AudioSamplerate"); else if (strcmp(name, "VideoFramerate") == 0) video_framerate_opts_set(ud->builder, "VideoFramerate"); else if (strcmp(name, "AudioMixdown") == 0) mix_opts_set(ud->builder, "AudioMixdown"); else if (strcmp(name, "VideoEncoder") == 0) video_encoder_opts_set(ud->builder, "VideoEncoder"); else if (strcmp(name, "AudioEncoder") == 0) audio_encoder_opts_set(ud->builder, "AudioEncoder"); else if (strcmp(name, "AudioEncoderFallback") == 0) acodec_fallback_opts_set(ud->builder, "AudioEncoderFallback"); else if (strcmp(name, "SrtLanguage") == 0) language_opts_set(ud->builder, "SrtLanguage"); else if (strcmp(name, "SrtCodeset") == 0) srt_codeset_opts_set(ud->builder, "SrtCodeset"); else if (strcmp(name, "title") == 0) title_opts_set(ud->builder, "title"); else if (strcmp(name, "SubtitleTrack") == 0) subtitle_track_opts_set(ud->builder, "SubtitleTrack", user_data); else if (strcmp(name, "AudioTrack") == 0) audio_track_opts_set(ud->builder, "AudioTrack", user_data); else if (strcmp(name, "VideoTune") == 0) video_tune_opts_set(ud, "VideoTune"); else if (strcmp(name, "VideoProfile") == 0) video_profile_opts_set(ud, "VideoProfile"); else if (strcmp(name, "VideoLevel") == 0) video_level_opts_set(ud, "VideoLevel"); else if (strcmp(name, "FileFormat") == 0) container_opts_set(ud->builder, "FileFormat"); else small_opts_set(ud->builder, name, find_combo_table(name)); } if (handler_id > 0) { g_signal_handler_unblock ((gpointer)combo, handler_id); } } static void init_ui_combo_boxes(GtkBuilder *builder) { gint ii; init_combo_box(builder, "AudioBitrate"); init_combo_box(builder, "AudioSamplerate"); init_combo_box(builder, "VideoFramerate"); init_combo_box(builder, "AudioMixdown"); init_combo_box(builder, "SrtLanguage"); init_combo_box(builder, "SrtCodeset"); init_combo_box(builder, "title"); init_combo_box(builder, "AudioTrack"); init_combo_box(builder, "SubtitleTrack"); init_combo_box(builder, "VideoEncoder"); init_combo_box(builder, "AudioEncoder"); init_combo_box(builder, "AudioEncoderFallback"); init_combo_box(builder, "VideoTune"); init_combo_box(builder, "VideoProfile"); init_combo_box(builder, "VideoLevel"); init_combo_box(builder, "FileFormat"); for (ii = 0; combo_name_map[ii].name != NULL; ii++) { init_combo_box(builder, combo_name_map[ii].name); } } // Construct the advanced options string // The result is allocated, so someone must free it at some point. gchar* ghb_build_advanced_opts_string(GValue *settings) { gint vcodec; vcodec = ghb_settings_video_encoder_codec(settings, "VideoEncoder"); switch (vcodec) { case HB_VCODEC_X264: return ghb_settings_get_string(settings, "x264Option"); default: return NULL; } } void ghb_set_video_encoder_opts(hb_job_t *job, GValue *js) { gint vcodec = ghb_settings_video_encoder_codec(js, "VideoEncoder"); switch (vcodec) { case HB_VCODEC_X265: case HB_VCODEC_X264: { if (vcodec == HB_VCODEC_X264 && ghb_settings_get_boolean(js, "x264UseAdvancedOptions")) { char *opts = ghb_settings_get_string(js, "x264Option"); hb_job_set_encoder_options(job, opts); g_free(opts); } else { GString *str = g_string_new(""); char *preset = ghb_settings_get_string(js, "VideoPreset"); char *tune = ghb_settings_get_string(js, "VideoTune"); char *profile = ghb_settings_get_string(js, "VideoProfile"); char *level = ghb_settings_get_string(js, "VideoLevel"); char *opts = ghb_settings_get_string(js, "VideoOptionExtra"); char *tunes; g_string_append_printf(str, "%s", tune); if (vcodec == HB_VCODEC_X264) { if (ghb_settings_get_boolean(js, "x264FastDecode")) { g_string_append_printf(str, "%s%s", str->str[0] ? "," : "", "fastdecode"); } if (ghb_settings_get_boolean(js, "x264ZeroLatency")) { g_string_append_printf(str, "%s%s", str->str[0] ? "," : "", "zerolatency"); } } tunes = g_string_free(str, FALSE); hb_job_set_encoder_preset(job, preset); if (tunes != NULL && strcasecmp(tune, "none")) hb_job_set_encoder_tune(job, tunes); if (profile != NULL && strcasecmp(profile, "auto")) hb_job_set_encoder_profile(job, profile); if (level != NULL && strcasecmp(level, "auto")) hb_job_set_encoder_level(job, level); hb_job_set_encoder_options(job, opts); g_free(preset); g_free(tune); g_free(profile); g_free(level); g_free(opts); g_free(tunes); } } break; case HB_VCODEC_FFMPEG_MPEG2: case HB_VCODEC_FFMPEG_MPEG4: case HB_VCODEC_FFMPEG_VP8: { gchar *opts = ghb_settings_get_string(js, "VideoOptionExtra"); if (opts != NULL && opts[0]) { hb_job_set_encoder_options(job, opts); } g_free(opts); } break; case HB_VCODEC_THEORA: default: { } break; } } void ghb_part_duration(const hb_title_t *title, gint sc, gint ec, gint *hh, gint *mm, gint *ss) { hb_chapter_t * chapter; gint count, c; gint64 duration; *hh = *mm = *ss = 0; if (title == NULL) return; *hh = title->hours; *mm = title->minutes; *ss = title->seconds; count = hb_list_count(title->list_chapter); if (sc > count) sc = count; if (ec > count) ec = count; if (sc == 1 && ec == count) return; duration = 0; for (c = sc; c <= ec; c++) { chapter = hb_list_item(title->list_chapter, c-1); duration += chapter->duration; } *hh = duration / 90000 / 3600; *mm = ((duration / 90000) % 3600) / 60; *ss = (duration / 90000) % 60; } gint64 ghb_get_chapter_duration(const hb_title_t *title, gint chap) { hb_chapter_t * chapter; gint count; if (title == NULL) return 0; count = hb_list_count( title->list_chapter ); if (chap >= count) return 0; chapter = hb_list_item(title->list_chapter, chap); if (chapter == NULL) return 0; return chapter->duration; } gint64 ghb_get_chapter_start(const hb_title_t *title, gint chap) { hb_chapter_t * chapter; gint count, ii; gint64 start = 0; if (title == NULL) return 0; count = hb_list_count( title->list_chapter ); if (chap > count) return chap = count; for (ii = 0; ii < chap; ii++) { chapter = hb_list_item(title->list_chapter, ii); start += chapter->duration; } return start; } GValue* ghb_get_chapters(const hb_title_t *title) { hb_chapter_t * chapter; gint count, ii; GValue *chapters = NULL; chapters = ghb_array_value_new(0); if (title == NULL) return chapters; count = hb_list_count( title->list_chapter ); for (ii = 0; ii < count; ii++) { chapter = hb_list_item(title->list_chapter, ii); if (chapter == NULL) break; if (chapter->title == NULL || chapter->title[0] == 0) { gchar *str; str = g_strdup_printf (_("Chapter %2d"), ii+1); ghb_array_append(chapters, ghb_string_value_new(str)); g_free(str); } else { ghb_array_append(chapters, ghb_string_value_new(chapter->title)); } } return chapters; } gboolean ghb_ac3_in_audio_list(const GValue *audio_list) { gint count, ii; count = ghb_array_len(audio_list); for (ii = 0; ii < count; ii++) { GValue *asettings; gint acodec; asettings = ghb_array_get_nth(audio_list, ii); acodec = ghb_settings_audio_encoder_codec(asettings, "AudioEncoder"); if (acodec & HB_ACODEC_AC3) return TRUE; } return FALSE; } static char custom_audio_bitrate_str[8]; static hb_rate_t custom_audio_bitrate = { .name = custom_audio_bitrate_str, .rate = 0 }; static void audio_bitrate_opts_add(GtkBuilder *builder, const gchar *name, gint rate) { GtkTreeIter iter; GtkListStore *store; gchar *str; g_debug("audio_bitrate_opts_add ()\n"); if (rate >= 0 && rate < 8) return; custom_audio_bitrate.rate = rate; if (rate < 0) { snprintf(custom_audio_bitrate_str, 8, _("N/A")); } else { snprintf(custom_audio_bitrate_str, 8, "%d", rate); } GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); if (!find_combo_item_by_int(GTK_TREE_MODEL(store), rate, &iter)) { str = g_strdup_printf ("%s", custom_audio_bitrate.name); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, 2, custom_audio_bitrate.name, 3, (gdouble)rate, 4, custom_audio_bitrate.name, -1); g_free(str); } } void ghb_audio_bitrate_opts_filter( GtkComboBox *combo, gint first_rate, gint last_rate) { GtkTreeIter iter; GtkListStore *store; gdouble ivalue; gboolean done = FALSE; g_debug("audio_bitrate_opts_filter ()\n"); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store), &iter)) { do { gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 3, &ivalue, -1); if ((int)ivalue < first_rate || (int)ivalue > last_rate) { gtk_list_store_set(store, &iter, 1, FALSE, -1); } else { gtk_list_store_set(store, &iter, 1, TRUE, -1); } done = !gtk_tree_model_iter_next (GTK_TREE_MODEL(store), &iter); } while (!done); } } static void audio_bitrate_opts_update( GtkBuilder *builder, const gchar *name, gint first_rate, gint last_rate, gint extra_rate) { GtkTreeIter iter; GtkListStore *store; gdouble ivalue; gboolean done = FALSE; g_debug("audio_bitrate_opts_clean ()\n"); GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); ghb_audio_bitrate_opts_filter(combo, first_rate, last_rate); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store), &iter)) { do { gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 3, &ivalue, -1); if (!search_audio_bitrates(ivalue) && (ivalue != extra_rate)) { done = !gtk_list_store_remove(store, &iter); } else { done = !gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); } } while (!done); } if (extra_rate > 0 && !search_audio_bitrates(extra_rate)) { audio_bitrate_opts_add(builder, name, extra_rate); } else { custom_audio_bitrate.rate = 0; custom_audio_bitrate_str[0] = 0; } } void ghb_audio_bitrate_opts_set(GtkComboBox *combo, gboolean extra) { GtkTreeIter iter; GtkListStore *store; gchar *str; g_debug("audio_bitrate_opts_set ()\n"); store = GTK_LIST_STORE(gtk_combo_box_get_model (combo)); gtk_list_store_clear(store); const hb_rate_t *rate; for (rate = hb_audio_bitrate_get_next(NULL); rate != NULL; rate = hb_audio_bitrate_get_next(rate)) { gtk_list_store_append(store, &iter); str = g_strdup_printf ("%s", rate->name); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, 2, rate->name, 3, (gdouble)rate->rate, 4, rate->name, -1); g_free(str); } if (extra && custom_audio_bitrate.rate != 0) { gtk_list_store_append(store, &iter); str = g_strdup_printf ("%s", custom_audio_bitrate.name); gtk_list_store_set(store, &iter, 0, str, 1, TRUE, 2, custom_audio_bitrate.name, 3, (gdouble)custom_audio_bitrate.rate, 4, custom_audio_bitrate.name, -1); g_free(str); } } static void audio_bitrate_opts_set(GtkBuilder *builder, const gchar *name) { GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(builder, name)); ghb_audio_bitrate_opts_set(combo, TRUE); } void ghb_set_bitrate_opts( GtkBuilder *builder, gint first_rate, gint last_rate, gint extra_rate) { audio_bitrate_opts_update(builder, "AudioBitrate", first_rate, last_rate, extra_rate); } const char* ghb_audio_bitrate_get_short_name(int rate) { if (rate == custom_audio_bitrate.rate) { return custom_audio_bitrate.name; } const hb_rate_t *hb_rate, *first; for (first = hb_rate = hb_audio_bitrate_get_next(NULL); hb_rate != NULL; hb_rate = hb_audio_bitrate_get_next(hb_rate)) { if (rate == hb_rate->rate) { return hb_rate->name; } } return first->name; } const hb_rate_t* ghb_lookup_audio_bitrate(const char *name) { // Now find the matching rate info const hb_rate_t *hb_rate, *first; for (first = hb_rate = hb_audio_bitrate_get_next(NULL); hb_rate != NULL; hb_rate = hb_audio_bitrate_get_next(hb_rate)) { if (!strncmp(name, hb_rate->name, 8)) { return hb_rate; } } // Return a default rate if nothing matches return first; } int ghb_lookup_audio_bitrate_rate(const char *name) { return ghb_lookup_audio_bitrate(name)->rate; } int ghb_settings_audio_bitrate_rate(const GValue *settings, const char *name) { const char *rate_id = ghb_settings_get_const_string(settings, name); return ghb_lookup_audio_bitrate_rate(rate_id); } const hb_rate_t* ghb_settings_audio_bitrate(const GValue *settings, const char *name) { const char *rate_id = ghb_settings_get_const_string(settings, name); return ghb_lookup_audio_bitrate(rate_id); } static ghb_status_t hb_status; void ghb_combo_init(signal_user_data_t *ud) { // Set up the list model for the combos init_ui_combo_boxes(ud->builder); // Populate all the combos ghb_update_ui_combo_box(ud, NULL, NULL, TRUE); } void ghb_backend_init(gint debug) { /* Init libhb */ h_scan = hb_init( debug, 0 ); h_queue = hb_init( debug, 0 ); } void ghb_backend_close() { hb_close(&h_queue); hb_close(&h_scan); hb_global_close(); } void ghb_backend_scan_stop() { hb_scan_stop( h_scan ); } void ghb_backend_scan(const gchar *path, gint titleindex, gint preview_count, uint64_t min_duration) { hb_scan( h_scan, path, titleindex, preview_count, 1, min_duration ); hb_status.scan.state |= GHB_STATE_SCANNING; // initialize count and cur to something that won't cause FPE // when computing progress hb_status.scan.title_count = 1; hb_status.scan.title_cur = 0; hb_status.scan.preview_count = 1; hb_status.scan.preview_cur = 0; hb_status.scan.progress = 0; } void ghb_backend_queue_scan(const gchar *path, gint titlenum) { g_debug("ghb_backend_queue_scan()"); hb_scan( h_queue, path, titlenum, 10, 0, 0 ); hb_status.queue.state |= GHB_STATE_SCANNING; } gint ghb_get_scan_state() { return hb_status.scan.state; } gint ghb_get_queue_state() { return hb_status.queue.state; } void ghb_clear_scan_state(gint state) { hb_status.scan.state &= ~state; } void ghb_clear_queue_state(gint state) { hb_status.queue.state &= ~state; } void ghb_set_scan_state(gint state) { hb_status.scan.state |= state; } void ghb_set_queue_state(gint state) { hb_status.queue.state |= state; } void ghb_get_status(ghb_status_t *status) { memcpy(status, &hb_status, sizeof(ghb_status_t)); } void ghb_track_status() { hb_state_t s_scan; hb_state_t s_queue; if (h_scan == NULL) return; hb_get_state( h_scan, &s_scan ); switch( s_scan.state ) { #define p s_scan.param.scanning case HB_STATE_SCANNING: { hb_status.scan.state |= GHB_STATE_SCANNING; hb_status.scan.title_count = p.title_count; hb_status.scan.title_cur = p.title_cur; hb_status.scan.preview_count = p.preview_count; hb_status.scan.preview_cur = p.preview_cur; hb_status.scan.progress = p.progress; } break; #undef p case HB_STATE_SCANDONE: { hb_status.scan.state &= ~GHB_STATE_SCANNING; hb_status.scan.state |= GHB_STATE_SCANDONE; } break; #define p s_scan.param.working case HB_STATE_WORKING: hb_status.scan.state |= GHB_STATE_WORKING; hb_status.scan.state &= ~GHB_STATE_PAUSED; hb_status.scan.job_cur = p.job_cur; hb_status.scan.job_count = p.job_count; hb_status.scan.progress = p.progress; hb_status.scan.rate_cur = p.rate_cur; hb_status.scan.rate_avg = p.rate_avg; hb_status.scan.hours = p.hours; hb_status.scan.minutes = p.minutes; hb_status.scan.seconds = p.seconds; hb_status.scan.unique_id = p.sequence_id & 0xFFFFFF; break; #undef p case HB_STATE_PAUSED: hb_status.scan.state |= GHB_STATE_PAUSED; break; case HB_STATE_MUXING: { hb_status.scan.state |= GHB_STATE_MUXING; } break; #define p s_scan.param.workdone case HB_STATE_WORKDONE: { hb_job_t *job; hb_status.scan.state |= GHB_STATE_WORKDONE; hb_status.scan.state &= ~GHB_STATE_MUXING; hb_status.scan.state &= ~GHB_STATE_PAUSED; hb_status.scan.state &= ~GHB_STATE_WORKING; switch (p.error) { case HB_ERROR_NONE: hb_status.scan.error = GHB_ERROR_NONE; break; case HB_ERROR_CANCELED: hb_status.scan.error = GHB_ERROR_CANCELED; break; default: hb_status.scan.error = GHB_ERROR_FAIL; break; } // Delete all remaining jobs of this encode. // An encode can be composed of multiple associated jobs. // When a job is stopped, libhb removes it from the job list, // but does not remove other jobs that may be associated with it. // Associated jobs are taged in the sequence id. while ((job = hb_job(h_scan, 0)) != NULL) hb_rem( h_scan, job ); } break; #undef p } hb_get_state( h_queue, &s_queue ); switch( s_queue.state ) { #define p s_queue.param.scanning case HB_STATE_SCANNING: { hb_status.queue.state |= GHB_STATE_SCANNING; hb_status.queue.title_count = p.title_count; hb_status.queue.title_cur = p.title_cur; hb_status.queue.preview_count = p.preview_count; hb_status.queue.preview_cur = p.preview_cur; hb_status.queue.progress = p.progress; } break; #undef p case HB_STATE_SCANDONE: { hb_status.queue.state &= ~GHB_STATE_SCANNING; hb_status.queue.state |= GHB_STATE_SCANDONE; } break; #define p s_queue.param.working case HB_STATE_WORKING: hb_status.queue.state |= GHB_STATE_WORKING; hb_status.queue.state &= ~GHB_STATE_PAUSED; hb_status.queue.state &= ~GHB_STATE_SEARCHING; hb_status.queue.job_cur = p.job_cur; hb_status.queue.job_count = p.job_count; hb_status.queue.progress = p.progress; hb_status.queue.rate_cur = p.rate_cur; hb_status.queue.rate_avg = p.rate_avg; hb_status.queue.hours = p.hours; hb_status.queue.minutes = p.minutes; hb_status.queue.seconds = p.seconds; hb_status.queue.unique_id = p.sequence_id & 0xFFFFFF; break; case HB_STATE_SEARCHING: hb_status.queue.state |= GHB_STATE_SEARCHING; hb_status.queue.state &= ~GHB_STATE_WORKING; hb_status.queue.state &= ~GHB_STATE_PAUSED; hb_status.queue.job_cur = p.job_cur; hb_status.queue.job_count = p.job_count; hb_status.queue.progress = p.progress; hb_status.queue.rate_cur = p.rate_cur; hb_status.queue.rate_avg = p.rate_avg; hb_status.queue.hours = p.hours; hb_status.queue.minutes = p.minutes; hb_status.queue.seconds = p.seconds; hb_status.queue.unique_id = p.sequence_id & 0xFFFFFF; break; #undef p case HB_STATE_PAUSED: hb_status.queue.state |= GHB_STATE_PAUSED; break; case HB_STATE_MUXING: { hb_status.queue.state |= GHB_STATE_MUXING; } break; #define p s_queue.param.workdone case HB_STATE_WORKDONE: { hb_job_t *job; hb_status.queue.state |= GHB_STATE_WORKDONE; hb_status.queue.state &= ~GHB_STATE_MUXING; hb_status.queue.state &= ~GHB_STATE_PAUSED; hb_status.queue.state &= ~GHB_STATE_WORKING; hb_status.queue.state &= ~GHB_STATE_SEARCHING; switch (p.error) { case HB_ERROR_NONE: hb_status.queue.error = GHB_ERROR_NONE; break; case HB_ERROR_CANCELED: hb_status.queue.error = GHB_ERROR_CANCELED; break; default: hb_status.queue.error = GHB_ERROR_FAIL; break; } // Delete all remaining jobs of this encode. // An encode can be composed of multiple associated jobs. // When a job is stopped, libhb removes it from the job list, // but does not remove other jobs that may be associated with it. // Associated jobs are taged in the sequence id. while ((job = hb_job(h_queue, 0)) != NULL) hb_rem( h_queue, job ); } break; #undef p } } hb_audio_config_t* ghb_get_audio_info(const hb_title_t *title, gint track) { if (title == NULL) return NULL; if (!hb_list_count(title->list_audio)) { return NULL; } return hb_list_audio_config_item(title->list_audio, track); } hb_subtitle_t* ghb_get_subtitle_info(const hb_title_t *title, gint track) { if (title == NULL) return NULL; if (!hb_list_count(title->list_subtitle)) { return NULL; } return hb_list_item(title->list_subtitle, track); } hb_list_t * ghb_get_title_list() { if (h_scan == NULL) return NULL; return hb_get_titles( h_scan ); } gboolean ghb_audio_is_passthru(gint acodec) { g_debug("ghb_audio_is_passthru () \n"); return (acodec & HB_ACODEC_PASS_FLAG) != 0; } gboolean ghb_audio_can_passthru(gint acodec) { g_debug("ghb_audio_can_passthru () \n"); return (acodec & HB_ACODEC_PASS_MASK) != 0; } gint ghb_get_default_acodec() { return HB_ACODEC_FFAAC; } void ghb_picture_settings_deps(signal_user_data_t *ud) { gboolean autoscale, keep_aspect, enable_keep_aspect; gboolean enable_scale_width, enable_scale_height; gboolean enable_disp_width, enable_disp_height, enable_par; gint pic_par; GtkWidget *widget; pic_par = ghb_settings_combo_int(ud->settings, "PicturePAR"); enable_keep_aspect = (pic_par != HB_ANAMORPHIC_STRICT && pic_par != HB_ANAMORPHIC_LOOSE); keep_aspect = ghb_settings_get_boolean(ud->settings, "PictureKeepRatio"); autoscale = ghb_settings_get_boolean(ud->settings, "autoscale"); enable_scale_width = enable_scale_height = !autoscale && (pic_par != HB_ANAMORPHIC_STRICT); enable_disp_width = (pic_par == HB_ANAMORPHIC_CUSTOM) && !keep_aspect; enable_par = (pic_par == HB_ANAMORPHIC_CUSTOM) && !keep_aspect; enable_disp_height = FALSE; widget = GHB_WIDGET(ud->builder, "PictureModulus"); gtk_widget_set_sensitive(widget, pic_par != HB_ANAMORPHIC_STRICT); widget = GHB_WIDGET(ud->builder, "PictureLooseCrop"); gtk_widget_set_sensitive(widget, pic_par != HB_ANAMORPHIC_STRICT); widget = GHB_WIDGET(ud->builder, "scale_width"); gtk_widget_set_sensitive(widget, enable_scale_width); widget = GHB_WIDGET(ud->builder, "scale_height"); gtk_widget_set_sensitive(widget, enable_scale_height); widget = GHB_WIDGET(ud->builder, "PictureDisplayWidth"); gtk_widget_set_sensitive(widget, enable_disp_width); widget = GHB_WIDGET(ud->builder, "PictureDisplayHeight"); gtk_widget_set_sensitive(widget, enable_disp_height); widget = GHB_WIDGET(ud->builder, "PicturePARWidth"); gtk_widget_set_sensitive(widget, enable_par); widget = GHB_WIDGET(ud->builder, "PicturePARHeight"); gtk_widget_set_sensitive(widget, enable_par); widget = GHB_WIDGET(ud->builder, "PictureKeepRatio"); gtk_widget_set_sensitive(widget, enable_keep_aspect); widget = GHB_WIDGET(ud->builder, "autoscale"); gtk_widget_set_sensitive(widget, pic_par != HB_ANAMORPHIC_STRICT); } void ghb_limit_rational( gint *num, gint *den, gint limit ) { if (*num < limit && *den < limit) return; if (*num > *den) { gdouble factor = (double)limit / *num; *num = limit; *den = factor * *den; } else { gdouble factor = (double)limit / *den; *den = limit; *num = factor * *num; } } void ghb_set_scale_settings(GValue *settings, gint mode) { gboolean keep_aspect; gint pic_par; gboolean autocrop, autoscale, loosecrop; gint crop[4] = {0,}; gint width, height; gint crop_width, crop_height; gboolean keep_width = (mode & GHB_PIC_KEEP_WIDTH); gboolean keep_height = (mode & GHB_PIC_KEEP_HEIGHT); gint mod; gint max_width = 0; gint max_height = 0; g_debug("ghb_set_scale ()\n"); pic_par = ghb_settings_combo_int(settings, "PicturePAR"); if (pic_par == HB_ANAMORPHIC_STRICT) { ghb_settings_set_boolean(settings, "autoscale", TRUE); ghb_settings_set_int(settings, "PictureModulus", 2); ghb_settings_set_boolean(settings, "PictureLooseCrop", TRUE); } if (pic_par == HB_ANAMORPHIC_STRICT || pic_par == HB_ANAMORPHIC_LOOSE) { ghb_settings_set_boolean(settings, "PictureKeepRatio", TRUE); } int title_id, titleindex; const hb_title_t * title; title_id = ghb_settings_get_int(settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (title == NULL) return; hb_geometry_t srcGeo, resultGeo; hb_ui_geometry_t uiGeo; srcGeo.width = title->width; srcGeo.height = title->height; srcGeo.par.num = title->pixel_aspect_width; srcGeo.par.den = title->pixel_aspect_height; // First configure widgets mod = ghb_settings_combo_int(settings, "PictureModulus"); if (mod <= 0) mod = 16; keep_aspect = ghb_settings_get_boolean(settings, "PictureKeepRatio"); autocrop = ghb_settings_get_boolean(settings, "PictureAutoCrop"); autoscale = ghb_settings_get_boolean(settings, "autoscale"); // "Noscale" is a flag that says we prefer to crop extra to satisfy // alignment constraints rather than scaling to satisfy them. loosecrop = ghb_settings_get_boolean(settings, "PictureLooseCrop"); // Align dimensions to either 16 or 2 pixels // The scaler crashes if the dimensions are not divisible by 2 // x264 also will not accept dims that are not multiple of 2 if (autoscale) { keep_width = FALSE; keep_height = FALSE; } if (autocrop) { crop[0] = title->crop[0]; crop[1] = title->crop[1]; crop[2] = title->crop[2]; crop[3] = title->crop[3]; ghb_settings_set_int(settings, "PictureTopCrop", crop[0]); ghb_settings_set_int(settings, "PictureBottomCrop", crop[1]); ghb_settings_set_int(settings, "PictureLeftCrop", crop[2]); ghb_settings_set_int(settings, "PictureRightCrop", crop[3]); } else { crop[0] = ghb_settings_get_int(settings, "PictureTopCrop"); crop[1] = ghb_settings_get_int(settings, "PictureBottomCrop"); crop[2] = ghb_settings_get_int(settings, "PictureLeftCrop"); crop[3] = ghb_settings_get_int(settings, "PictureRightCrop"); // Prevent manual crop from creating too small an image if (title->height - crop[0] < crop[1] + 16) { crop[0] = title->height - crop[1] - 16; } if (title->width - crop[2] < crop[3] + 16) { crop[2] = title->width - crop[3] - 16; } } if (loosecrop) { gint need1, need2; // Adjust the cropping to accomplish the desired width and height crop_width = title->width - crop[2] - crop[3]; crop_height = title->height - crop[0] - crop[1]; width = MOD_DOWN(crop_width, mod); height = MOD_DOWN(crop_height, mod); need1 = EVEN((crop_height - height) / 2); need2 = crop_height - height - need1; crop[0] += need1; crop[1] += need2; need1 = EVEN((crop_width - width) / 2); need2 = crop_width - width - need1; crop[2] += need1; crop[3] += need2; ghb_settings_set_int(settings, "PictureTopCrop", crop[0]); ghb_settings_set_int(settings, "PictureBottomCrop", crop[1]); ghb_settings_set_int(settings, "PictureLeftCrop", crop[2]); ghb_settings_set_int(settings, "PictureRightCrop", crop[3]); } uiGeo.crop[0] = crop[0]; uiGeo.crop[1] = crop[1]; uiGeo.crop[2] = crop[2]; uiGeo.crop[3] = crop[3]; crop_width = title->width - crop[2] - crop[3]; crop_height = title->height - crop[0] - crop[1]; if (autoscale) { width = crop_width; height = crop_height; } else { width = ghb_settings_get_int(settings, "scale_width"); height = ghb_settings_get_int(settings, "scale_height"); if (mode & GHB_PIC_USE_MAX) { max_width = MOD_DOWN( ghb_settings_get_int(settings, "PictureWidth"), mod); max_height = MOD_DOWN( ghb_settings_get_int(settings, "PictureHeight"), mod); } } g_debug("max_width %d, max_height %d\n", max_width, max_height); if (width < 16) width = 16; if (height < 16) height = 16; width = MOD_ROUND(width, mod); height = MOD_ROUND(height, mod); uiGeo.mode = pic_par; uiGeo.keep = 0; if (keep_width) uiGeo.keep |= HB_KEEP_WIDTH; if (keep_height) uiGeo.keep |= HB_KEEP_HEIGHT; if (keep_aspect) uiGeo.keep |= HB_KEEP_DISPLAY_ASPECT; uiGeo.itu_par = 0; uiGeo.modulus = mod; uiGeo.width = width; uiGeo.height = height; uiGeo.maxWidth = max_width; uiGeo.maxHeight = max_height; uiGeo.par.num = title->pixel_aspect_width; uiGeo.par.den = title->pixel_aspect_height; uiGeo.dar.num = 0; uiGeo.dar.den = 0; if (pic_par != HB_ANAMORPHIC_NONE) { if (pic_par == HB_ANAMORPHIC_CUSTOM && !keep_aspect) { if (mode & GHB_PIC_KEEP_PAR) { uiGeo.par.num = ghb_settings_get_int(settings, "PicturePARWidth"); uiGeo.par.den = ghb_settings_get_int(settings, "PicturePARHeight"); } else if (mode & (GHB_PIC_KEEP_DISPLAY_HEIGHT | GHB_PIC_KEEP_DISPLAY_WIDTH)) { uiGeo.dar.num = ghb_settings_get_int(settings, "PictureDisplayWidth"); uiGeo.dar.den = height; } } else { uiGeo.keep |= HB_KEEP_DISPLAY_ASPECT; } } // hb_set_anamorphic_size will adjust par, dar, and width/height // to conform to job parameters that have been set, including // maxWidth and maxHeight hb_set_anamorphic_size2(&srcGeo, &uiGeo, &resultGeo); ghb_settings_set_int(settings, "scale_width", resultGeo.width); ghb_settings_set_int(settings, "scale_height", resultGeo.height); gint disp_width; disp_width = ((gdouble)resultGeo.par.num / resultGeo.par.den) * resultGeo.width + 0.5; ghb_settings_set_int(settings, "PicturePARWidth", resultGeo.par.num); ghb_settings_set_int(settings, "PicturePARHeight", resultGeo.par.den); ghb_settings_set_int(settings, "PictureDisplayWidth", disp_width); ghb_settings_set_int(settings, "PictureDisplayHeight", resultGeo.height); } void ghb_update_display_aspect_label(signal_user_data_t *ud) { gint disp_width, disp_height, dar_width, dar_height; gchar *str; disp_width = ghb_settings_get_int(ud->settings, "PictureDisplayWidth"); disp_height = ghb_settings_get_int(ud->settings, "PictureDisplayHeight"); hb_reduce(&dar_width, &dar_height, disp_width, disp_height); gint iaspect = dar_width * 9 / dar_height; if (dar_width > 2 * dar_height) { str = g_strdup_printf("%.2f : 1", (gdouble)dar_width / dar_height); } else if (iaspect <= 16 && iaspect >= 15) { str = g_strdup_printf("%.2f : 9", (gdouble)dar_width * 9 / dar_height); } else if (iaspect <= 12 && iaspect >= 11) { str = g_strdup_printf("%.2f : 3", (gdouble)dar_width * 3 / dar_height); } else { str = g_strdup_printf("%d : %d", dar_width, dar_height); } ghb_ui_update(ud, "display_aspect", ghb_string_value(str)); g_free(str); } void ghb_set_scale(signal_user_data_t *ud, gint mode) { if (ud->scale_busy) return; ud->scale_busy = TRUE; ghb_set_scale_settings(ud->settings, mode); ghb_picture_settings_deps(ud); // Step needs to be at least 2 because odd widths cause scaler crash // subsampled chroma requires even crop values. GtkWidget *widget; int mod = ghb_settings_combo_int(ud->settings, "PictureModulus"); widget = GHB_WIDGET (ud->builder, "scale_width"); gtk_spin_button_set_increments (GTK_SPIN_BUTTON(widget), mod, 16); widget = GHB_WIDGET (ud->builder, "scale_height"); gtk_spin_button_set_increments (GTK_SPIN_BUTTON(widget), mod, 16); // "Noscale" is a flag that says we prefer to crop extra to satisfy // alignment constraints rather than scaling to satisfy them. gboolean loosecrop = ghb_settings_get_boolean(ud->settings, "PictureLooseCrop"); if (loosecrop) { widget = GHB_WIDGET (ud->builder, "PictureTopCrop"); gtk_spin_button_set_increments (GTK_SPIN_BUTTON(widget), mod, 16); widget = GHB_WIDGET (ud->builder, "PictureBottomCrop"); gtk_spin_button_set_increments (GTK_SPIN_BUTTON(widget), mod, 16); widget = GHB_WIDGET (ud->builder, "PictureLeftCrop"); gtk_spin_button_set_increments (GTK_SPIN_BUTTON(widget), mod, 16); widget = GHB_WIDGET (ud->builder, "PictureRightCrop"); gtk_spin_button_set_increments (GTK_SPIN_BUTTON(widget), mod, 16); } else { widget = GHB_WIDGET (ud->builder, "PictureTopCrop"); gtk_spin_button_set_increments (GTK_SPIN_BUTTON(widget), 2, 16); widget = GHB_WIDGET (ud->builder, "PictureBottomCrop"); gtk_spin_button_set_increments (GTK_SPIN_BUTTON(widget), 2, 16); widget = GHB_WIDGET (ud->builder, "PictureLeftCrop"); gtk_spin_button_set_increments (GTK_SPIN_BUTTON(widget), 2, 16); widget = GHB_WIDGET (ud->builder, "PictureRightCrop"); gtk_spin_button_set_increments (GTK_SPIN_BUTTON(widget), 2, 16); } ghb_ui_update_from_settings(ud, "autoscale", ud->settings); ghb_ui_update_from_settings(ud, "PictureModulus", ud->settings); ghb_ui_update_from_settings(ud, "PictureLooseCrop", ud->settings); ghb_ui_update_from_settings(ud, "PictureKeepRatio", ud->settings); ghb_ui_update_from_settings(ud, "PictureTopCrop", ud->settings); ghb_ui_update_from_settings(ud, "PictureBottomCrop", ud->settings); ghb_ui_update_from_settings(ud, "PictureLeftCrop", ud->settings); ghb_ui_update_from_settings(ud, "PictureRightCrop", ud->settings); ghb_ui_update_from_settings(ud, "scale_width", ud->settings); ghb_ui_update_from_settings(ud, "scale_height", ud->settings); ghb_ui_update_from_settings(ud, "PicturePARWidth", ud->settings); ghb_ui_update_from_settings(ud, "PicturePARHeight", ud->settings); ghb_ui_update_from_settings(ud, "PictureDisplayWidth", ud->settings); ghb_ui_update_from_settings(ud, "PictureDisplayHeight", ud->settings); ghb_update_display_aspect_label(ud); ud->scale_busy = FALSE; } static void get_preview_geometry(signal_user_data_t *ud, const hb_title_t *title, hb_geometry_t *srcGeo, hb_ui_geometry_t *uiGeo) { srcGeo->width = title->width; srcGeo->height = title->height; srcGeo->par.num = title->pixel_aspect_width; srcGeo->par.den = title->pixel_aspect_height; uiGeo->mode = ghb_settings_combo_int(ud->settings, "PicturePAR"); uiGeo->keep = ghb_settings_get_boolean(ud->settings, "PictureKeepRatio") || uiGeo->mode == HB_ANAMORPHIC_STRICT || uiGeo->mode == HB_ANAMORPHIC_LOOSE; uiGeo->itu_par = 0; uiGeo->modulus = ghb_settings_combo_int(ud->settings, "PictureModulus"); uiGeo->crop[0] = ghb_settings_get_int(ud->settings, "PictureTopCrop"); uiGeo->crop[1] = ghb_settings_get_int(ud->settings, "PictureBottomCrop"); uiGeo->crop[2] = ghb_settings_get_int(ud->settings, "PictureLeftCrop"); uiGeo->crop[3] = ghb_settings_get_int(ud->settings, "PictureRightCrop"); uiGeo->width = ghb_settings_get_int(ud->settings, "scale_width"); uiGeo->height = ghb_settings_get_int(ud->settings, "scale_height"); uiGeo->maxWidth = 0; uiGeo->maxHeight = 0; uiGeo->par.num = ghb_settings_get_int(ud->settings, "PicturePARWidth"); uiGeo->par.den = ghb_settings_get_int(ud->settings, "PicturePARHeight"); uiGeo->dar.num = 0; uiGeo->dar.den = 0; if (ghb_settings_get_boolean(ud->prefs, "preview_show_crop")) { gdouble xscale = (gdouble)uiGeo->width / (title->width - uiGeo->crop[2] - uiGeo->crop[3]); gdouble yscale = (gdouble)uiGeo->height / (title->height - uiGeo->crop[0] - uiGeo->crop[1]); uiGeo->width += xscale * (uiGeo->crop[2] + uiGeo->crop[3]); uiGeo->height += yscale * (uiGeo->crop[0] + uiGeo->crop[1]); uiGeo->crop[0] = 0; uiGeo->crop[1] = 0; uiGeo->crop[2] = 0; uiGeo->crop[3] = 0; uiGeo->modulus = 2; } } gboolean ghb_validate_filter_string(const gchar *str, gint max_fields) { gint fields = 0; gchar *end; if (str == NULL || *str == 0) return TRUE; while (*str) { g_strtod(str, &end); if (str != end) { // Found a numeric value fields++; // negative max_fields means infinate if (max_fields >= 0 && fields > max_fields) return FALSE; if (*end == 0) return TRUE; if (*end != ':') return FALSE; str = end + 1; } else return FALSE; } return FALSE; } gboolean ghb_validate_filters(GValue *settings) { gchar *str; gint index; gchar *message; gboolean decomb_deint = ghb_settings_get_boolean(settings, "PictureDecombDeinterlace"); // deinte index = ghb_settings_combo_int(settings, "PictureDeinterlace"); if (!decomb_deint && index == 1) { str = ghb_settings_get_string(settings, "PictureDeinterlaceCustom"); if (!ghb_validate_filter_string(str, -1)) { message = g_strdup_printf( _("Invalid Deinterlace Settings:\n\n%s\n"), str); ghb_message_dialog(GTK_MESSAGE_ERROR, message, _("Cancel"), NULL); g_free(message); g_free(str); return FALSE; } g_free(str); } // detel index = ghb_settings_combo_int(settings, "PictureDetelecine"); if (index == 1) { str = ghb_settings_get_string(settings, "PictureDetelecineCustom"); if (!ghb_validate_filter_string(str, -1)) { message = g_strdup_printf( _("Invalid Detelecine Settings:\n\n%s\n"), str); ghb_message_dialog(GTK_MESSAGE_ERROR, message, _("Cancel"), NULL); g_free(message); g_free(str); return FALSE; } g_free(str); } // decomb index = ghb_settings_combo_int(settings, "PictureDecomb"); if (decomb_deint && index == 1) { str = ghb_settings_get_string(settings, "PictureDecombCustom"); if (!ghb_validate_filter_string(str, -1)) { message = g_strdup_printf( _("Invalid Decomb Settings:\n\n%s\n"), str); ghb_message_dialog(GTK_MESSAGE_ERROR, message, _("Cancel"), NULL); g_free(message); g_free(str); return FALSE; } g_free(str); } // denoise // TODO return TRUE; } gboolean ghb_validate_video(GValue *settings) { gint vcodec; gchar *message; const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); vcodec = ghb_settings_video_encoder_codec(settings, "VideoEncoder"); if ((mux->format & HB_MUX_MASK_MP4) && (vcodec == HB_VCODEC_THEORA)) { // mp4/theora combination is not supported. message = g_strdup_printf( _("Theora is not supported in the MP4 container.\n\n" "You should choose a different video codec or container.\n" "If you continue, FFMPEG will be chosen for you.")); if (!ghb_message_dialog(GTK_MESSAGE_WARNING, message, _("Cancel"), _("Continue"))) { g_free(message); return FALSE; } g_free(message); vcodec = hb_video_encoder_get_default(mux->format); ghb_settings_set_string(settings, "VideoEncoder", hb_video_encoder_get_short_name(vcodec)); } return TRUE; } gboolean ghb_validate_subtitles(GValue *settings) { gint title_id, titleindex; const hb_title_t * title; gchar *message; title_id = ghb_settings_get_int(settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (title == NULL) { /* No valid title, stop right there */ g_message(_("No title found.\n")); return FALSE; } const GValue *slist, *subtitle; gint count, ii, source; gboolean burned, one_burned = FALSE; slist = ghb_settings_get_value(settings, "subtitle_list"); count = ghb_array_len(slist); for (ii = 0; ii < count; ii++) { subtitle = ghb_array_get_nth(slist, ii); source = ghb_settings_get_int(subtitle, "SubtitleSource"); burned = ghb_settings_get_boolean(subtitle, "SubtitleBurned"); if (burned && one_burned) { // MP4 can only handle burned vobsubs. make sure there isn't // already something burned in the list message = g_strdup_printf( _("Only one subtitle may be burned into the video.\n\n" "You should change your subtitle selections.\n" "If you continue, some subtitles will be lost.")); if (!ghb_message_dialog(GTK_MESSAGE_WARNING, message, _("Cancel"), _("Continue"))) { g_free(message); return FALSE; } g_free(message); break; } else if (burned) { one_burned = TRUE; } if (source == SRTSUB) { gchar *filename; filename = ghb_settings_get_string(subtitle, "SrtFile"); if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) { message = g_strdup_printf( _("Srt file does not exist or not a regular file.\n\n" "You should choose a valid file.\n" "If you continue, this subtitle will be ignored.")); if (!ghb_message_dialog(GTK_MESSAGE_WARNING, message, _("Cancel"), _("Continue"))) { g_free(message); return FALSE; } g_free(message); break; } } } return TRUE; } gboolean ghb_validate_audio(GValue *settings) { gint title_id, titleindex; const hb_title_t * title; gchar *message; title_id = ghb_settings_get_int(settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (title == NULL) { /* No valid title, stop right there */ g_message(_("No title found.\n")); return FALSE; } const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); const GValue *audio_list; gint count, ii; audio_list = ghb_settings_get_value(settings, "audio_list"); count = ghb_array_len(audio_list); for (ii = 0; ii < count; ii++) { GValue *asettings; hb_audio_config_t *aconfig; int track, codec; asettings = ghb_array_get_nth(audio_list, ii); track = ghb_settings_get_int(asettings, "AudioTrack"); codec = ghb_settings_audio_encoder_codec(asettings, "AudioEncoder"); if (codec == HB_ACODEC_AUTO_PASS) continue; aconfig = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, track ); if ( ghb_audio_is_passthru(codec) && !(ghb_audio_can_passthru(aconfig->in.codec) && (aconfig->in.codec & codec))) { // Not supported. AC3 is passthrough only, so input must be AC3 message = g_strdup_printf( _("The source does not support Pass-Thru.\n\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you.")); if (!ghb_message_dialog(GTK_MESSAGE_WARNING, message, _("Cancel"), _("Continue"))) { g_free(message); return FALSE; } g_free(message); if ((codec & HB_ACODEC_AC3) || (aconfig->in.codec & HB_ACODEC_MASK) == HB_ACODEC_DCA) { codec = HB_ACODEC_AC3; } else if (mux->format & HB_MUX_MASK_MKV) { codec = HB_ACODEC_LAME; } else { codec = HB_ACODEC_FFAAC; } const char *name = hb_audio_encoder_get_short_name(codec); ghb_settings_set_string(asettings, "AudioEncoder", name); } gchar *a_unsup = NULL; gchar *mux_s = NULL; if (mux->format & HB_MUX_MASK_MP4) { mux_s = "MP4"; // mp4/vorbis|DTS combination is not supported. if (codec == HB_ACODEC_VORBIS) { a_unsup = "Vorbis"; codec = HB_ACODEC_FFAAC; } } if (a_unsup) { message = g_strdup_printf( _("%s is not supported in the %s container.\n\n" "You should choose a different audio codec.\n" "If you continue, one will be chosen for you."), a_unsup, mux_s); if (!ghb_message_dialog(GTK_MESSAGE_WARNING, message, _("Cancel"), _("Continue"))) { g_free(message); return FALSE; } g_free(message); const char *name = hb_audio_encoder_get_short_name(codec); ghb_settings_set_string(asettings, "AudioEncoder", name); } const hb_mixdown_t *mix; mix = ghb_settings_mixdown(asettings, "AudioMixdown"); const gchar *mix_unsup = NULL; if (!hb_mixdown_is_supported(mix->amixdown, codec, aconfig->in.channel_layout)) { mix_unsup = mix->name; } if (mix_unsup) { message = g_strdup_printf( _("The source audio does not support %s mixdown.\n\n" "You should choose a different mixdown.\n" "If you continue, one will be chosen for you."), mix_unsup); if (!ghb_message_dialog(GTK_MESSAGE_WARNING, message, _("Cancel"), _("Continue"))) { g_free(message); return FALSE; } g_free(message); int amixdown = ghb_get_best_mix(aconfig, codec, mix->amixdown); ghb_settings_set_string(asettings, "AudioMixdown", hb_mixdown_get_short_name(amixdown)); } } return TRUE; } static void add_job(hb_handle_t *h, GValue *js, gint unique_id, int titleindex) { hb_list_t * list; const hb_title_t * title; hb_job_t * job; gint sub_id = 0; hb_filter_object_t * filter; gchar *filter_str; gchar *dest_str = NULL; GValue *prefs; g_debug("add_job()\n"); if (h == NULL) return; list = hb_get_titles( h ); if( !hb_list_count( list ) ) { /* No valid title, stop right there */ return; } title = hb_list_item( list, titleindex ); if (title == NULL) return; /* Set job settings */ job = hb_job_init( (hb_title_t*)title ); if (job == NULL) return; prefs = ghb_settings_get_value(js, "Preferences"); job->angle = ghb_settings_get_int(js, "angle"); job->start_at_preview = ghb_settings_get_int(js, "start_frame") + 1; if (job->start_at_preview) { job->seek_points = ghb_settings_get_int(prefs, "preview_count"); job->pts_to_stop = ghb_settings_get_int(prefs, "live_duration") * 90000LL; } const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(js, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); job->mux = mux->format; if (job->mux & HB_MUX_MASK_MP4) { job->largeFileSize = ghb_settings_get_boolean(js, "Mp4LargeFile"); job->mp4_optimize = ghb_settings_get_boolean(js, "Mp4HttpOptimize"); } else { job->largeFileSize = FALSE; job->mp4_optimize = FALSE; } if (!job->start_at_preview) { gint num_chapters = hb_list_count(title->list_chapter); double duration = title->duration / 90000; job->chapter_markers = FALSE; job->chapter_start = 1; job->chapter_end = num_chapters; if (ghb_settings_combo_int(js, "PtoPType") == 0) { gint start, end; start = ghb_settings_get_int(js, "start_point"); end = ghb_settings_get_int(js, "end_point"); job->chapter_start = MIN( num_chapters, start ); job->chapter_end = MAX( job->chapter_start, end ); } if (ghb_settings_combo_int(js, "PtoPType") == 1) { double start, end; start = ghb_settings_get_double(js, "start_point"); end = ghb_settings_get_double(js, "end_point"); job->pts_to_start = (int64_t)(MIN(duration, start) * 90000); job->pts_to_stop = (int64_t)(MAX(start, end) * 90000) - job->pts_to_start; } if (ghb_settings_combo_int(js, "PtoPType") == 2) { gint start, end; start = ghb_settings_get_int(js, "start_point"); end = ghb_settings_get_int(js, "end_point"); gint64 max_frames; max_frames = (gint64)(duration * title->rate / title->rate_base); job->frame_to_start = (int64_t)MIN(max_frames-1, start-1); job->frame_to_stop = (int64_t)MAX(start, end-1) - job->frame_to_start; } if (job->chapter_start != job->chapter_end) { job->chapter_markers = ghb_settings_get_boolean(js, "ChapterMarkers"); } if (job->chapter_start == job->chapter_end) job->chapter_markers = 0; if ( job->chapter_markers ) { GValue *chapters; GValue *chapter; gint chap; gint count; chapters = ghb_settings_get_value(js, "chapter_list"); count = ghb_array_len(chapters); for(chap = 0; chap < count; chap++) { hb_chapter_t * chapter_s; gchar *name; name = NULL; chapter = ghb_array_get_nth(chapters, chap); name = ghb_value_string(chapter); if (name == NULL) { name = g_strdup_printf (_("Chapter %2d"), chap+1); } chapter_s = hb_list_item( job->list_chapter, chap); hb_chapter_set_title(chapter_s, name); g_free(name); } } } gboolean decomb_deint = ghb_settings_get_boolean(js, "PictureDecombDeinterlace"); gint decomb = ghb_settings_combo_int(js, "PictureDecomb"); gint deint = ghb_settings_combo_int(js, "PictureDeinterlace"); if (!decomb_deint) job->deinterlace = (deint != 0) ? 1 : 0; else job->deinterlace = 0; job->grayscale = ghb_settings_get_boolean(js, "VideoGrayScale"); job->anamorphic.mode = ghb_settings_combo_int(js, "PicturePAR"); job->modulus = ghb_settings_combo_int(js, "PictureModulus"); job->anamorphic.par_width = ghb_settings_get_int(js, "PicturePARWidth"); job->anamorphic.par_height = ghb_settings_get_int(js, "PicturePARHeight"); job->anamorphic.dar_width = job->anamorphic.dar_height = 0; job->anamorphic.keep_display_aspect = ghb_settings_get_boolean(js, "PictureKeepRatio"); int width, height, crop[4]; width = ghb_settings_get_int(js, "scale_width"); height = ghb_settings_get_int(js, "scale_height"); crop[0] = ghb_settings_get_int(js, "PictureTopCrop"); crop[1] = ghb_settings_get_int(js, "PictureBottomCrop"); crop[2] = ghb_settings_get_int(js, "PictureLeftCrop"); crop[3] = ghb_settings_get_int(js, "PictureRightCrop"); filter_str = g_strdup_printf("%d:%d:%d:%d:%d:%d", width, height, crop[0], crop[1], crop[2], crop[3]); filter = hb_filter_init(HB_FILTER_CROP_SCALE); hb_add_filter( job, filter, filter_str ); g_free(filter_str); /* Add selected filters */ gint detel = ghb_settings_combo_int(js, "PictureDetelecine"); if ( detel ) { filter_str = NULL; if (detel != 1) { if (detel_opts.map[detel].svalue != NULL) filter_str = g_strdup(detel_opts.map[detel].svalue); } else filter_str = ghb_settings_get_string(js, "PictureDetelecineCustom"); filter = hb_filter_init(HB_FILTER_DETELECINE); hb_add_filter( job, filter, filter_str ); g_free(filter_str); } if ( decomb_deint && decomb ) { filter_str = NULL; if (decomb != 1) { if (decomb_opts.map[decomb].svalue != NULL) filter_str = g_strdup(decomb_opts.map[decomb].svalue); } else filter_str = ghb_settings_get_string(js, "PictureDecombCustom"); filter = hb_filter_init(HB_FILTER_DECOMB); hb_add_filter( job, filter, filter_str ); g_free(filter_str); } if( job->deinterlace ) { filter_str = NULL; if (deint != 1) { if (deint_opts.map[deint].svalue != NULL) filter_str = g_strdup(deint_opts.map[deint].svalue); } else filter_str = ghb_settings_get_string(js, "PictureDeinterlaceCustom"); filter = hb_filter_init(HB_FILTER_DEINTERLACE); hb_add_filter( job, filter, filter_str ); g_free(filter_str); } if (strcmp(ghb_settings_get_const_string(js, "PictureDenoiseFilter"), "off")) { int filter_id = HB_FILTER_HQDN3D; if (!strcmp(ghb_settings_get_const_string(js, "PictureDenoiseFilter"), "nlmeans")) filter_id = HB_FILTER_NLMEANS; if (!strcmp(ghb_settings_get_const_string(js, "PictureDenoisePreset"), "custom")) { const char *filter_str; filter_str = ghb_settings_get_const_string(js, "PictureDenoiseCustom"); filter = hb_filter_init(filter_id); hb_add_filter( job, filter, filter_str ); } else { const char *preset, *tune; preset = ghb_settings_get_const_string(js, "PictureDenoisePreset"); tune = ghb_settings_get_const_string(js, "PictureDenoiseTune"); filter_str = hb_generate_filter_settings(filter_id, preset, tune); filter = hb_filter_init(filter_id); hb_add_filter( job, filter, filter_str ); g_free(filter_str); } } gint deblock = ghb_settings_get_int(js, "PictureDeblock"); if( deblock >= 5 ) { filter_str = NULL; filter_str = g_strdup_printf("%d", deblock); filter = hb_filter_init(HB_FILTER_DEBLOCK); hb_add_filter( job, filter, filter_str ); g_free(filter_str); } job->vcodec = ghb_settings_video_encoder_codec(js, "VideoEncoder"); if ((job->mux & HB_MUX_MASK_MP4 ) && (job->vcodec == HB_VCODEC_THEORA)) { // mp4/theora combination is not supported. job->vcodec = HB_VCODEC_FFMPEG_MPEG4; } if ((job->vcodec == HB_VCODEC_X264) && (job->mux & HB_MUX_MASK_MP4)) { job->ipod_atom = ghb_settings_get_boolean(js, "Mp4iPodCompatible"); } if (ghb_settings_get_boolean(js, "vquality_type_constant")) { gdouble vquality; vquality = ghb_settings_get_double(js, "VideoQualitySlider"); job->vquality = vquality; job->vbitrate = 0; } else if (ghb_settings_get_boolean(js, "vquality_type_bitrate")) { job->vquality = -1.0; job->vbitrate = ghb_settings_get_int(js, "VideoAvgBitrate"); } gint vrate; gint vrate_base = ghb_settings_video_framerate_rate(js, "VideoFramerate"); gint cfr; if (ghb_settings_get_boolean(js, "VideoFrameratePFR")) cfr = 2; else if (ghb_settings_get_boolean(js, "VideoFramerateCFR")) cfr = 1; else cfr = 0; // x264 zero latency requires CFR encode if (ghb_settings_get_boolean(js, "x264ZeroLatency")) { cfr = 1; ghb_log("zerolatency x264 tune selected, forcing constant framerate"); } if( vrate_base == 0 ) { vrate = title->rate; vrate_base = title->rate_base; } else { vrate = 27000000; } filter_str = g_strdup_printf("%d:%d:%d", cfr, vrate, vrate_base); filter = hb_filter_init(HB_FILTER_VFR); hb_add_filter( job, filter, filter_str ); g_free(filter_str); const GValue *audio_list; gint count, ii; gint tcount = 0; audio_list = ghb_settings_get_value(js, "audio_list"); count = ghb_array_len(audio_list); for (ii = 0; ii < count; ii++) { GValue *asettings; hb_audio_config_t audio; hb_audio_config_t *aconfig; gint acodec, fallback; hb_audio_config_init(&audio); asettings = ghb_array_get_nth(audio_list, ii); audio.in.track = ghb_settings_get_int(asettings, "AudioTrack"); audio.out.track = tcount; char * aname = ghb_settings_get_string(asettings, "AudioTrackName"); if (aname && *aname) { // This leaks, but there is no easy way to clean up // presently audio.out.name = aname; } else { g_free(aname); } aconfig = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, audio.in.track ); acodec = ghb_settings_audio_encoder_codec(asettings, "AudioEncoder"); fallback = ghb_settings_audio_encoder_codec(js, "AudioEncoderFallback"); gint copy_mask = ghb_get_copy_mask(js); audio.out.codec = ghb_select_audio_codec(job->mux, aconfig, acodec, fallback, copy_mask); audio.out.gain = ghb_settings_get_double(asettings, "AudioTrackGainSlider"); audio.out.dynamic_range_compression = ghb_settings_get_double(asettings, "AudioTrackDRCSlider"); if (audio.out.dynamic_range_compression < 1.0) audio.out.dynamic_range_compression = 0.0; // It would be better if this were done in libhb for us, but its not yet. if (ghb_audio_is_passthru(audio.out.codec)) { audio.out.mixdown = 0; } else { audio.out.mixdown = ghb_settings_mixdown_mix(asettings, "AudioMixdown"); // Make sure the mixdown is valid and pick a new one if not. audio.out.mixdown = ghb_get_best_mix(aconfig, audio.out.codec, audio.out.mixdown); gint srate; srate = ghb_settings_audio_samplerate_rate( asettings, "AudioSamplerate"); if (srate == 0) // 0 is same as source audio.out.samplerate = aconfig->in.samplerate; else audio.out.samplerate = srate; double quality = ghb_settings_get_double(asettings, "AudioTrackQuality"); if (ghb_settings_get_boolean(asettings, "AudioTrackQualityEnable") && quality != HB_INVALID_AUDIO_QUALITY) { audio.out.quality = quality; audio.out.bitrate = -1; } else { audio.out.quality = HB_INVALID_AUDIO_QUALITY; audio.out.bitrate = ghb_settings_audio_bitrate_rate(asettings, "AudioBitrate"); audio.out.bitrate = hb_audio_bitrate_get_best( audio.out.codec, audio.out.bitrate, audio.out.samplerate, audio.out.mixdown); } } // Add it to the jobs audio list hb_audio_add( job, &audio ); tcount++; } dest_str = ghb_settings_get_string(js, "destination"); hb_job_set_file( job, dest_str); g_free(dest_str); const GValue *subtitle_list; gint subtitle; gboolean force, burned, def, one_burned = FALSE; ghb_settings_set_boolean(js, "subtitle_scan", FALSE); subtitle_list = ghb_settings_get_value(js, "subtitle_list"); count = ghb_array_len(subtitle_list); for (ii = 0; ii < count; ii++) { GValue *ssettings; gint source; ssettings = ghb_array_get_nth(subtitle_list, ii); force = ghb_settings_get_boolean(ssettings, "SubtitleForced"); burned = ghb_settings_get_boolean(ssettings, "SubtitleBurned"); def = ghb_settings_get_boolean(ssettings, "SubtitleDefaultTrack"); source = ghb_settings_get_int(ssettings, "SubtitleSource"); if (source == SRTSUB) { hb_subtitle_config_t sub_config; gchar *filename, *lang, *code; filename = ghb_settings_get_string(ssettings, "SrtFile"); if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) { continue; } sub_config.offset = ghb_settings_get_int(ssettings, "SrtOffset"); lang = ghb_settings_get_string(ssettings, "SrtLanguage"); code = ghb_settings_get_string(ssettings, "SrtCodeset"); strncpy(sub_config.src_filename, filename, 255); sub_config.src_filename[255] = 0; strncpy(sub_config.src_codeset, code, 39); sub_config.src_codeset[39] = 0; sub_config.force = 0; sub_config.default_track = def; if (burned && !one_burned && hb_subtitle_can_burn(SRTSUB)) { // Only allow one subtitle to be burned into the video sub_config.dest = RENDERSUB; one_burned = TRUE; } else { sub_config.dest = PASSTHRUSUB; } hb_srt_add( job, &sub_config, lang); g_free(filename); g_free(lang); g_free(code); continue; } subtitle = ghb_settings_get_int(ssettings, "SubtitleTrack"); if (subtitle == -1) { if (burned && !one_burned) { // Only allow one subtitle to be burned into the video job->select_subtitle_config.dest = RENDERSUB; one_burned = TRUE; } else { job->select_subtitle_config.dest = PASSTHRUSUB; } job->select_subtitle_config.force = force; job->select_subtitle_config.default_track = def; job->indepth_scan = 1; ghb_settings_set_boolean(js, "subtitle_scan", TRUE); } else if (subtitle >= 0) { hb_subtitle_t * subt; hb_subtitle_config_t sub_config; subt = hb_list_item(title->list_subtitle, subtitle); if (subt != NULL) { sub_config = subt->config; if (burned && !one_burned && hb_subtitle_can_burn(subt->source)) { // Only allow one subtitle to be burned into the video sub_config.dest = RENDERSUB; one_burned = TRUE; } else { sub_config.dest = PASSTHRUSUB; } sub_config.force = force; sub_config.default_track = def; hb_subtitle_add( job, &sub_config, subtitle ); } } } if (one_burned) { // Add filter that renders vobsubs filter = hb_filter_init(HB_FILTER_RENDER_SUB); filter_str = g_strdup_printf("%d:%d:%d:%d", crop[0], crop[1], crop[2], crop[3]); hb_add_filter( job, filter, filter_str ); g_free(filter_str); } char * meta; meta = ghb_settings_get_string(js, "MetaName"); if (meta && *meta) { hb_metadata_set_name(job->metadata, meta); } free(meta); meta = ghb_settings_get_string(js, "MetaArtist"); if (meta && *meta) { hb_metadata_set_artist(job->metadata, meta); } free(meta); meta = ghb_settings_get_string(js, "MetaAlbumArtist"); if (meta && *meta) { hb_metadata_set_album_artist(job->metadata, meta); } free(meta); meta = ghb_settings_get_string(js, "MetaReleaseDate"); if (meta && *meta) { hb_metadata_set_release_date(job->metadata, meta); } free(meta); meta = ghb_settings_get_string(js, "MetaComment"); if (meta && *meta) { hb_metadata_set_comment(job->metadata, meta); } free(meta); meta = ghb_settings_get_string(js, "MetaGenre"); if (meta && *meta) { hb_metadata_set_genre(job->metadata, meta); } free(meta); meta = ghb_settings_get_string(js, "MetaDescription"); if (meta && *meta) { hb_metadata_set_description(job->metadata, meta); } free(meta); meta = ghb_settings_get_string(js, "MetaLongDescription"); if (meta && *meta) { hb_metadata_set_long_description(job->metadata, meta); } free(meta); if (job->indepth_scan == 1) { // Subtitle scan. Look for subtitle matching audio language /* * When subtitle scan is enabled do a fast pre-scan job * which will determine which subtitles to enable, if any. */ job->pass = -1; job->indepth_scan = 1; hb_job_set_encoder_options(job, NULL); /* * Add the pre-scan job */ job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24); hb_add( h, job ); } if( ghb_settings_get_boolean(js, "VideoTwoPass") && !ghb_settings_get_boolean(js, "vquality_type_constant")) { /* * If subtitle_scan is enabled then only turn it on * for the second pass and then off again for the * second. */ job->pass = 1; job->indepth_scan = 0; ghb_set_video_encoder_opts(job, js); /* * If turbo options have been selected then set job->fastfirstpass */ if(ghb_settings_get_boolean(js, "VideoTurboTwoPass") && (job->vcodec == HB_VCODEC_X264 || job->vcodec == HB_VCODEC_X265)) { job->fastfirstpass = 1; } else { job->fastfirstpass = 0; } job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24); hb_add( h, job ); job->pass = 2; /* * On the second pass we turn off subtitle scan so that we * can actually encode using any subtitles that were auto * selected in the first pass (using the whacky select-subtitle * attribute of the job). */ job->indepth_scan = 0; job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24); hb_add( h, job ); } else { ghb_set_video_encoder_opts(job, js); job->indepth_scan = 0; job->pass = 0; job->sequence_id = (unique_id & 0xFFFFFF) | (sub_id++ << 24); hb_add( h, job ); } hb_job_close(&job); } void ghb_add_job(GValue *js, gint unique_id) { // Since I'm doing a scan of the single title I want just prior // to adding the job, there is only the one title to choose from. add_job(h_queue, js, unique_id, 0); } void ghb_add_live_job(GValue *js, gint unique_id) { int title_id, titleindex; const hb_title_t *title; title_id = ghb_settings_get_int(js, "title"); title = ghb_lookup_title(title_id, &titleindex); (void)title; // Silence "unused variable" warning add_job(h_scan, js, unique_id, titleindex); } void ghb_remove_job(gint unique_id) { hb_job_t * job; gint ii; // Multiples passes all get the same id // remove them all. // Go backwards through list, so reordering doesn't screw me. ii = hb_count(h_queue) - 1; while ((job = hb_job(h_queue, ii--)) != NULL) { if ((job->sequence_id & 0xFFFFFF) == unique_id) hb_rem(h_queue, job); } } void ghb_start_queue() { hb_start( h_queue ); } void ghb_stop_queue() { hb_stop( h_queue ); } void ghb_start_live_encode() { hb_start( h_scan ); } void ghb_stop_live_encode() { hb_stop( h_scan ); } void ghb_pause_queue() { hb_state_t s; hb_get_state2( h_queue, &s ); if( s.state == HB_STATE_PAUSED ) { hb_status.queue.state &= ~GHB_STATE_PAUSED; hb_resume( h_queue ); } else { hb_status.queue.state |= GHB_STATE_PAUSED; hb_pause( h_queue ); } } static void vert_line( GdkPixbuf * pb, guint8 r, guint8 g, guint8 b, gint x, gint y, gint len, gint width) { guint8 *pixels = gdk_pixbuf_get_pixels (pb); guint8 *dst; gint ii, jj; gint channels = gdk_pixbuf_get_n_channels (pb); gint stride = gdk_pixbuf_get_rowstride (pb); for (jj = 0; jj < width; jj++) { dst = pixels + y * stride + (x+jj) * channels; for (ii = 0; ii < len; ii++) { dst[0] = r; dst[1] = g; dst[2] = b; dst += stride; } } } static void horz_line( GdkPixbuf * pb, guint8 r, guint8 g, guint8 b, gint x, gint y, gint len, gint width) { guint8 *pixels = gdk_pixbuf_get_pixels (pb); guint8 *dst; gint ii, jj; gint channels = gdk_pixbuf_get_n_channels (pb); gint stride = gdk_pixbuf_get_rowstride (pb); for (jj = 0; jj < width; jj++) { dst = pixels + (y+jj) * stride + x * channels; for (ii = 0; ii < len; ii++) { dst[0] = r; dst[1] = g; dst[2] = b; dst += channels; } } } static void hash_pixbuf( GdkPixbuf * pb, gint x, gint y, gint w, gint h, gint step, gint orientation) { gint ii, jj; gint line_width = 8; struct { guint8 r; guint8 g; guint8 b; } c[4] = {{0x80, 0x80, 0x80},{0xC0, 0x80, 0x70},{0x80, 0xA0, 0x80},{0x70, 0x80, 0xA0}}; if (!orientation) { // vertical lines for (ii = x, jj = 0; ii+line_width < x+w; ii += step, jj++) { vert_line(pb, c[jj&3].r, c[jj&3].g, c[jj&3].b, ii, y, h, line_width); } } else { // horizontal lines for (ii = y, jj = 0; ii+line_width < y+h; ii += step, jj++) { horz_line(pb, c[jj&3].r, c[jj&3].g, c[jj&3].b, x, ii, w, line_width); } } } GdkPixbuf* ghb_get_preview_image( const hb_title_t *title, gint index, signal_user_data_t *ud, gint *out_width, gint *out_height) { hb_geometry_t srcGeo, resultGeo; hb_ui_geometry_t uiGeo; if( title == NULL ) return NULL; gboolean deinterlace; if (ghb_settings_get_boolean(ud->settings, "PictureDecombDeinterlace")) { deinterlace = ghb_settings_combo_int(ud->settings, "PictureDecomb") == 0 ? 0 : 1; } else { deinterlace = ghb_settings_combo_int(ud->settings, "PictureDeinterlace") == 0 ? 0 : 1; } // Get the geometry settings for the preview. This will disable // cropping if the setting to show the cropped region is enabled. get_preview_geometry(ud, title, &srcGeo, &uiGeo); // hb_get_preview doesn't compensate for anamorphic, so lets // calculate scale factors hb_set_anamorphic_size2(&srcGeo, &uiGeo, &resultGeo); // Rescale preview dimensions to adjust for screen PAR and settings PAR ghb_par_scale(ud, &uiGeo.width, &uiGeo.height, resultGeo.par.num, resultGeo.par.den); uiGeo.par.num = 1; uiGeo.par.den = 1; GdkPixbuf *preview; hb_image_t *image; image = hb_get_preview2(h_scan, title->index, index, &uiGeo, deinterlace); if (image == NULL) { preview = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, title->width, title->height); return preview; } // Create an GdkPixbuf and copy the libhb image into it, converting it from // libhb's format something suitable. // The image data returned by hb_get_preview is 4 bytes per pixel, // BGRA format. Alpha is ignored. preview = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, image->width, image->height); guint8 *pixels = gdk_pixbuf_get_pixels(preview); guint8 *src_line = image->data; guint8 *dst = pixels; gint ii, jj; gint channels = gdk_pixbuf_get_n_channels(preview); gint stride = gdk_pixbuf_get_rowstride(preview); guint8 *tmp; for (ii = 0; ii < image->height; ii++) { guint32 *src = (guint32*)src_line; tmp = dst; for (jj = 0; jj < image->width; jj++) { tmp[0] = src[0] >> 16; tmp[1] = src[0] >> 8; tmp[2] = src[0] >> 0; tmp += channels; src++; } src_line += image->plane[0].stride; dst += stride; } gint w = ghb_settings_get_int(ud->settings, "scale_width"); gint h = ghb_settings_get_int(ud->settings, "scale_height"); ghb_par_scale(ud, &w, &h, resultGeo.par.num, resultGeo.par.den); gint c0, c1, c2, c3; c0 = ghb_settings_get_int(ud->settings, "PictureTopCrop"); c1 = ghb_settings_get_int(ud->settings, "PictureBottomCrop"); c2 = ghb_settings_get_int(ud->settings, "PictureLeftCrop"); c3 = ghb_settings_get_int(ud->settings, "PictureRightCrop"); gdouble xscale = (gdouble)w / (gdouble)(title->width - c2 - c3); gdouble yscale = (gdouble)h / (gdouble)(title->height - c0 - c1); *out_width = w; *out_height = h; int previewWidth = image->width; int previewHeight = image->height; // If the preview is too large to fit the screen, reduce it's size. if (ghb_settings_get_boolean(ud->prefs, "reduce_hd_preview")) { GdkScreen *ss; gint s_w, s_h; gint factor = 80; if (ghb_settings_get_boolean(ud->prefs, "preview_fullscreen")) { factor = 100; } ss = gdk_screen_get_default(); s_w = gdk_screen_get_width(ss); s_h = gdk_screen_get_height(ss); if (previewWidth > s_w * factor / 100 || previewHeight > s_h * factor / 100) { GdkPixbuf *scaled_preview; int orig_w = previewWidth; int orig_h = previewHeight; if (previewWidth > s_w * factor / 100) { previewWidth = s_w * factor / 100; previewHeight = previewHeight * previewWidth / orig_w; } if (previewHeight > s_h * factor / 100) { previewHeight = s_h * factor / 100; previewWidth = orig_w * previewHeight / orig_h; } xscale *= (gdouble)previewWidth / orig_w; yscale *= (gdouble)previewHeight / orig_h; w *= (gdouble)previewWidth / orig_w; h *= (gdouble)previewHeight / orig_h; scaled_preview = gdk_pixbuf_scale_simple(preview, previewWidth, previewHeight, GDK_INTERP_HYPER); g_object_unref(preview); preview = scaled_preview; } } if (ghb_settings_get_boolean(ud->prefs, "preview_show_crop")) { c0 *= yscale; c1 *= yscale; c2 *= xscale; c3 *= xscale; // Top hash_pixbuf(preview, c2, 0, w, c0, 32, 0); // Bottom hash_pixbuf(preview, c2, previewHeight-c1, w, c1, 32, 0); // Left hash_pixbuf(preview, 0, c0, c2, h, 32, 1); // Right hash_pixbuf(preview, previewWidth-c3, c0, c3, h, 32, 1); } hb_image_close(&image); return preview; } static void sanitize_volname(gchar *name) { gchar *a, *b; a = b = name; while (*b) { switch(*b) { case '<': b++; break; case '>': b++; break; default: *a = *b & 0x7f; a++; b++; break; } } *a = 0; } gchar* ghb_dvd_volname(const gchar *device) { gchar *name; name = hb_dvd_name((gchar*)device); if (name != NULL && name[0] != 0) { name = g_strdup(name); sanitize_volname(name); return name; } return NULL; } HandBrake-0.10.2/gtk/src/resources.list0000664000175200017520000000200212312123633020310 0ustar handbrakehandbrake
HandBrake-0.10.2/gtk/src/hb-icon.svg0000664000175200017520001502032512311644756017474 0ustar handbrakehandbrake image/svg+xml HandBrake-0.10.2/gtk/src/ghb-dvd.h0000664000175200017520000000165612300772602017106 0ustar handbrakehandbrake/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA */ #if !defined(_GHB_DVD_H_) #define _GHB_DVD_H_ #include "settings.h" void ghb_dvd_set_current(const gchar *name, signal_user_data_t *ud); gchar* ghb_resolve_symlink(const gchar *name); #endif // _GHB_DVD_H_ HandBrake-0.10.2/gtk/src/callbacks.c0000664000175200017520000045444312505047752017523 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * callbacks.c * Copyright (C) John Stebbins 2008-2015 * * callbacks.c is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include "ghbcompat.h" #if !defined(_WIN32) #include #define G_UDEV_API_IS_SUBJECT_TO_CHANGE 1 #if defined(__linux__) #include #endif #include #include #include #include #if !defined(_NO_UPDATE_CHECK) #if defined(_OLD_WEBKIT) #include #else #include #endif #endif #include #ifndef NOTIFY_CHECK_VERSION #define NOTIFY_CHECK_VERSION(x,y,z) 0 #endif #include #ifndef NOTIFY_CHECK_VERSION #define NOTIFY_CHECK_VERSION(x,y,z) 0 #endif #else #include #include #endif #include "hb.h" #include "callbacks.h" #include "queuehandler.h" #include "audiohandler.h" #include "subtitlehandler.h" #include "resources.h" #include "settings.h" #include "presets.h" #include "preview.h" #include "values.h" #include "plist.h" #include "appcast.h" #include "hb-backend.h" #include "ghb-dvd.h" #include "ghbcellrenderertext.h" #include "x264handler.h" static void load_all_titles(signal_user_data_t *ud, int titleindex); static void update_chapter_list_settings(GValue *settings); static GList* dvd_device_list(); static void prune_logs(signal_user_data_t *ud); void ghb_notify_done(signal_user_data_t *ud); gpointer ghb_check_update(signal_user_data_t *ud); static gboolean ghb_can_shutdown_gsm(); static void ghb_shutdown_gsm(); static gboolean ghb_can_suspend_gpm(); static void ghb_suspend_gpm(); static gboolean appcast_busy = FALSE; // This is a dependency map used for greying widgets // that are dependent on the state of another widget. // The enable_value comes from the values that are // obtained from ghb_widget_value(). For combo boxes // you will have to look further to combo box options // maps in hb-backend.c GValue *dep_map; GValue *rev_map; void ghb_init_dep_map() { dep_map = ghb_resource_get("widget-deps"); rev_map = ghb_resource_get("widget-reverse-deps"); } static gboolean dep_check(signal_user_data_t *ud, const gchar *name, gboolean *out_hide) { GtkWidget *widget; GObject *dep_object; gint ii; gint count; gboolean result = TRUE; GValue *array, *data; gchar *widget_name; g_debug("dep_check () %s", name); if (rev_map == NULL) return TRUE; array = ghb_dict_lookup(rev_map, name); count = ghb_array_len(array); *out_hide = FALSE; for (ii = 0; ii < count; ii++) { data = ghb_array_get_nth(array, ii); widget_name = ghb_value_string(ghb_array_get_nth(data, 0)); widget = GHB_WIDGET(ud->builder, widget_name); dep_object = gtk_builder_get_object(ud->builder, name); if (widget != NULL && !gtk_widget_is_sensitive(widget)) { g_free(widget_name); continue; } if (dep_object == NULL) { g_message("Failed to find widget"); } else { gchar *value; gint jj = 0; gchar **values; gboolean sensitive = FALSE; gboolean die, hide; die = ghb_value_boolean(ghb_array_get_nth(data, 2)); hide = ghb_value_boolean(ghb_array_get_nth(data, 3)); value = ghb_value_string(ghb_array_get_nth(data, 1)); values = g_strsplit(value, "|", 10); g_free(value); if (widget) value = ghb_widget_string(widget); else value = ghb_settings_get_string(ud->settings, widget_name); while (values && values[jj]) { if (values[jj][0] == '>') { gdouble dbl = g_strtod (&values[jj][1], NULL); gdouble dvalue = ghb_widget_double(widget); if (dvalue > dbl) { sensitive = TRUE; break; } } else if (values[jj][0] == '<') { gdouble dbl = g_strtod (&values[jj][1], NULL); gdouble dvalue = ghb_widget_double(widget); if (dvalue < dbl) { sensitive = TRUE; break; } } if (strcmp(values[jj], value) == 0) { sensitive = TRUE; break; } jj++; } sensitive = die ^ sensitive; if (!sensitive) { result = FALSE; *out_hide |= hide; } g_strfreev (values); g_free(value); } g_free(widget_name); } return result; } void ghb_check_dependency( signal_user_data_t *ud, GtkWidget *widget, const char *alt_name) { GObject *dep_object; const gchar *name; GValue *array, *data; gint count, ii; gchar *dep_name; GType type; if (widget != NULL) { type = G_OBJECT_TYPE(widget); if (type == GTK_TYPE_COMBO_BOX) if (gtk_combo_box_get_active(GTK_COMBO_BOX(widget)) < 0) return; name = ghb_get_setting_key(widget); } else name = alt_name; g_debug("ghb_check_dependency () %s", name); if (dep_map == NULL) return; array = ghb_dict_lookup(dep_map, name); count = ghb_array_len(array); for (ii = 0; ii < count; ii++) { gboolean sensitive; gboolean hide; data = ghb_array_get_nth(array, ii); dep_name = ghb_value_string(data); dep_object = gtk_builder_get_object(ud->builder, dep_name); if (dep_object == NULL) { g_message("Failed to find dependent widget %s", dep_name); g_free(dep_name); continue; } sensitive = dep_check(ud, dep_name, &hide); gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive); if (!sensitive && hide) { if (gtk_widget_get_visible(GTK_WIDGET(dep_object))) { gtk_widget_hide(GTK_WIDGET(dep_object)); } } else { if (!gtk_widget_get_visible(GTK_WIDGET(dep_object))) { gtk_widget_show_now(GTK_WIDGET(dep_object)); } } g_free(dep_name); } } void ghb_check_all_depencencies(signal_user_data_t *ud) { GHashTableIter iter; gchar *dep_name; GValue *value; GObject *dep_object; g_debug("ghb_check_all_depencencies ()"); if (rev_map == NULL) return; ghb_dict_iter_init(&iter, rev_map); // middle (void*) cast prevents gcc warning "defreferencing type-punned // pointer will break strict-aliasing rules" while (g_hash_table_iter_next( &iter, (gpointer*)(void*)&dep_name, (gpointer*)(void*)&value)) { gboolean sensitive; gboolean hide; dep_object = gtk_builder_get_object (ud->builder, dep_name); if (dep_object == NULL) { g_message("Failed to find dependent widget %s", dep_name); continue; } sensitive = dep_check(ud, dep_name, &hide); gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive); if (!sensitive && hide) { gtk_widget_hide(GTK_WIDGET(dep_object)); } else { gtk_widget_show_now(GTK_WIDGET(dep_object)); } } } G_MODULE_EXPORT void on_quit1_activate(GtkMenuItem *quit, signal_user_data_t *ud) { gint state = ghb_get_queue_state(); g_debug("on_quit1_activate ()"); if (state & (GHB_STATE_WORKING|GHB_STATE_SEARCHING)) { if (ghb_cancel_encode2(ud, _("Closing HandBrake will terminate encoding.\n"))) { ghb_hb_cleanup(FALSE); prune_logs(ud); gtk_main_quit(); return; } return; } ghb_hb_cleanup(FALSE); prune_logs(ud); gtk_main_quit(); } gboolean uppers_and_unders(gchar *str) { if (str == NULL) return FALSE; str = g_strchomp(g_strchug(str)); while (*str) { if (*str == ' ') { return FALSE; } if (*str >= 'a' && *str <= 'z') { return FALSE; } str++; } return TRUE; } enum { CAMEL_FIRST_UPPER, CAMEL_OTHER }; void camel_convert(gchar *str) { gint state = CAMEL_OTHER; if (str == NULL) return; while (*str) { if (*str == '_') *str = ' '; switch (state) { case CAMEL_OTHER: { if (*str >= 'A' && *str <= 'Z') state = CAMEL_FIRST_UPPER; else state = CAMEL_OTHER; } break; case CAMEL_FIRST_UPPER: { if (*str >= 'A' && *str <= 'Z') *str = *str - 'A' + 'a'; else state = CAMEL_OTHER; } break; } str++; } } #if defined(_WIN32) static gchar* get_dvd_device_name(gchar *device) { return g_strdup(device); } #else static gchar* get_dvd_device_name(GDrive *gd) { return g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); } #endif static GHashTable *volname_hash = NULL; #if GLIB_CHECK_VERSION(2, 32, 0) static GMutex volname_mutex_static; #endif static GMutex *volname_mutex; static void free_volname_key(gpointer data) { if (data != NULL) g_free(data); } static void free_volname_value(gpointer data) { if (data != NULL) g_free(data); } #if defined(_WIN32) static gchar* get_direct_dvd_volume_name(const gchar *drive) { gchar *result = NULL; gchar vname[51], fsname[51]; if (GetVolumeInformation(drive, vname, 50, NULL, NULL, NULL, fsname, 50)) { result = g_strdup_printf("%s", vname); } return result; } #else static gchar* get_direct_dvd_volume_name(const gchar *drive) { gchar *result; result = ghb_dvd_volname (drive); return result; } #endif static gchar* get_dvd_volume_name(gpointer gd) { gchar *label = NULL; gchar *result; gchar *drive; drive = get_dvd_device_name(gd); g_mutex_lock(volname_mutex); label = g_strdup(g_hash_table_lookup(volname_hash, drive)); g_mutex_unlock(volname_mutex); if (label != NULL) { if (uppers_and_unders(label)) { camel_convert(label); } #if defined(_WIN32) result = g_strdup_printf("%s (%s)", label, drive); #else result = g_strdup_printf("%s - %s", drive, label); #endif g_free(label); } else { result = g_strdup_printf("%s", drive); } g_free(drive); return result; } void ghb_volname_cache_init(void) { #if GLIB_CHECK_VERSION(2, 32, 0) g_mutex_init(&volname_mutex_static); volname_mutex = &volname_mutex_static; #else volname_mutex = g_mutex_new(); #endif volname_hash = g_hash_table_new_full(g_str_hash, g_str_equal, free_volname_key, free_volname_value); } static void free_drive(gpointer drive) { #if defined(_WIN32) g_free(drive); #else g_object_unref(drive); #endif } gpointer ghb_cache_volnames(signal_user_data_t *ud) { GList *link, *drives; g_debug("ghb_cache_volnames()"); link = drives = dvd_device_list(); if (drives == NULL) return NULL; g_mutex_lock(volname_mutex); g_hash_table_remove_all(volname_hash); while (link != NULL) { gchar *name, *drive; #if !defined(_WIN32) if (!g_drive_has_media (link->data)) { g_object_unref(link->data); link = link->next; continue; } #endif drive = get_dvd_device_name(link->data); name = get_direct_dvd_volume_name(drive); if (drive != NULL && name != NULL) { g_hash_table_insert(volname_hash, drive, name); } else { if (drive != NULL) g_free(drive); if (name != NULL) g_free(name); } free_drive(link->data); link = link->next; } g_mutex_unlock(volname_mutex); g_list_free(drives); g_idle_add((GSourceFunc)ghb_file_menu_add_dvd, ud); return NULL; } static const gchar* get_extension(signal_user_data_t *ud, GValue *settings) { const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); if ((mux->format & HB_MUX_MASK_MP4) && ghb_settings_get_boolean(ud->prefs, "UseM4v")) { return "m4v"; } return mux->default_extension; } static gboolean check_name_template(signal_user_data_t *ud, const char *str) { if (ghb_settings_get_boolean(ud->prefs, "auto_name")) { const gchar *template; template = ghb_settings_get_const_string(ud->prefs, "auto_name_template"); if (strstr(template, str) != NULL) return TRUE; } return FALSE; } static void set_destination_settings(signal_user_data_t *ud, GValue *settings) { const gchar *extension; gchar *filename; extension = get_extension(ud, settings); g_debug("set_destination_settings"); if (ghb_settings_get_boolean(ud->prefs, "auto_name")) { GString *str = g_string_new(""); gchar *p; gchar *template; p = template = ghb_settings_get_string(ud->prefs, "auto_name_template"); while (*p) { if (!strncmp(p, "{source}", strlen("{source}"))) { gchar *vol_name; vol_name = ghb_settings_get_string(settings, "volume_label"); g_string_append_printf(str, "%s", vol_name); g_free(vol_name); p += strlen("{source}"); } else if (!strncmp(p, "{title}", strlen("{title}"))) { gint title = ghb_settings_get_int(settings, "title"); if (title >= 0) g_string_append_printf(str, "%d", title); p += strlen("{title}"); } else if (!strncmp(p, "{chapters}", strlen("{chapters}"))) { if (ghb_settings_combo_int(settings, "PtoPType") == 0) { gint start, end; start = ghb_settings_get_int(settings, "start_point"); end = ghb_settings_get_int(settings, "end_point"); if (start == end) g_string_append_printf(str, "%d", start); else g_string_append_printf(str, "%d-%d", start, end); } p += strlen("{chapters}"); } else if (!strncmp(p, "{time}", strlen("{time}"))) { char st[6]; struct tm *lt; time_t t = time(NULL); lt = localtime(&t); st[0] = 0; strftime(st, 6, "%R", lt); g_string_append_printf(str, "%s", st); p += strlen("{time}"); } else if (!strncmp(p, "{date}", strlen("{date}"))) { char dt[11]; struct tm *lt; time_t t = time(NULL); lt = localtime(&t); dt[0] = 0; strftime(dt, 11, "%F", lt); g_string_append_printf(str, "%s", dt); p += strlen("{date}"); } else if (!strncmp(p, "{quality}", strlen("{quality}"))) { if (ghb_settings_get_boolean(settings, "vquality_type_constant")) { gint vcodec; const char *vqname; double vquality; vcodec = ghb_settings_video_encoder_codec(settings, "VideoEncoder"); vqname = hb_video_quality_get_name(vcodec); vquality = ghb_settings_get_double(settings, "VideoQualitySlider"); g_string_append_printf(str, "%s%.3g", vqname, vquality); } p += strlen("{quality}"); } else if (!strncmp(p, "{bitrate}", strlen("{bitrate}"))) { if (ghb_settings_get_boolean(settings, "vquality_type_bitrate")) { int vbitrate; vbitrate = ghb_settings_get_int(settings, "VideoAvgBitrate"); g_string_append_printf(str, "%dkbps", vbitrate); } p += strlen("{bitrate}"); } else { g_string_append_printf(str, "%c", *p); p++; } } g_string_append_printf(str, ".%s", extension); filename = g_string_free(str, FALSE); ghb_settings_set_string(settings, "dest_file", filename); g_free(template); g_free(filename); } } static void set_destination(signal_user_data_t *ud) { set_destination_settings(ud, ud->settings); ghb_ui_update(ud, "dest_file", ghb_settings_get_value(ud->settings, "dest_file")); } static gchar* get_file_label(const gchar *filename) { gchar *base, *pos, *end; base = g_path_get_basename(filename); pos = strrchr(base, '.'); if (pos != NULL) { // If the last '.' is within 4 chars of end of name, assume // there is an extension we want to strip. end = &base[strlen(base) - 1]; if (end - pos <= 4) *pos = 0; } return base; } static gchar* resolve_drive_name(gchar *filename) { #if defined(_WIN32) if (filename[1] == ':') { gchar drive[4]; gchar *name; gint dtype; g_strlcpy(drive, filename, 4); dtype = GetDriveType(drive); if (dtype == DRIVE_CDROM) { gchar vname[51], fsname[51]; GetVolumeInformation(drive, vname, 50, NULL, NULL, NULL, fsname, 50); name = g_strdup(vname); return name; } } return NULL; #else return NULL; #endif } static gboolean update_source_label(signal_user_data_t *ud, const gchar *source) { gchar *label = NULL; gint len; gchar **path; gchar *start; gchar *filename = g_strdup(source); g_debug("update_source_label()"); len = strlen(filename); if (g_file_test(filename, G_FILE_TEST_IS_DIR)) { // Skip dos drive letters #if defined(_WIN32) start = strchr(filename, ':'); #else start = filename; #endif label = resolve_drive_name(filename); if (label != NULL) { if (uppers_and_unders(label)) { camel_convert(label); } } else { if (filename[len-1] == G_DIR_SEPARATOR) filename[len-1] = 0; if (start != NULL) start++; else start = filename; path = g_strsplit(start, G_DIR_SEPARATOR_S, -1); len = g_strv_length (path); if ((len > 1) && (strcmp("VIDEO_TS", path[len-1]) == 0)) { label = g_strdup(path[len-2]); if (uppers_and_unders(label)) { camel_convert(label); } } else if (len > 0) { if (path[len-1][0] != 0) { label = g_strdup(path[len-1]); if (uppers_and_unders(label)) { camel_convert(label); } } else label = g_strdup("new_video"); } else label = g_strdup("new_video"); g_strfreev (path); } } else { // Is regular file or block dev. // Check to see if it is a dvd image label = ghb_dvd_volname (filename); if (label == NULL) { label = get_file_label(filename); } else { if (uppers_and_unders(label)) { camel_convert(label); } } } g_free(filename); GtkWidget *widget = GHB_WIDGET (ud->builder, "volume_label"); if (label != NULL) { gtk_label_set_text (GTK_LABEL(widget), label); ghb_settings_set_string(ud->settings, "volume_label", label); g_free(label); } else { label = _("No Title Found"); gtk_label_set_text (GTK_LABEL(widget), label); ghb_settings_set_string(ud->settings, "volume_label", label); return FALSE; } return TRUE; } G_MODULE_EXPORT void chooser_file_selected_cb(GtkFileChooser *dialog, signal_user_data_t *ud) { gchar *name = gtk_file_chooser_get_filename (dialog); GtkTreeModel *store; GtkTreeIter iter; const gchar *device; gboolean foundit = FALSE; GtkComboBox *combo; g_debug("chooser_file_selected_cb ()"); if (name == NULL) return; combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, "source_device")); store = gtk_combo_box_get_model(combo); if (gtk_tree_model_get_iter_first(store, &iter)) { do { gtk_tree_model_get(store, &iter, 0, &device, -1); if (strcmp(name, device) == 0) { foundit = TRUE; break; } } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)); } if (foundit) gtk_combo_box_set_active_iter (combo, &iter); else gtk_combo_box_set_active (combo, 0); g_free(name); } G_MODULE_EXPORT void dvd_device_changed_cb(GtkComboBoxText *combo, signal_user_data_t *ud) { GtkWidget *dialog; gint ii; g_debug("dvd_device_changed_cb ()"); ii = gtk_combo_box_get_active (GTK_COMBO_BOX(combo)); if (ii > 0) { const gchar *device; gchar *name; dialog = GHB_WIDGET(ud->builder, "source_dialog"); device = gtk_combo_box_text_get_active_text(combo); // Protext against unexpected NULL return value if (device != NULL) { name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog)); if (name == NULL || strcmp(name, device) != 0) gtk_file_chooser_select_filename (GTK_FILE_CHOOSER(dialog), device); if (name != NULL) g_free(name); } } } static void source_dialog_extra_widgets( signal_user_data_t *ud, GtkWidget *dialog) { GtkComboBoxText *combo; GList *drives, *link; g_debug("source_dialog_extra_widgets ()"); combo = GTK_COMBO_BOX_TEXT(GHB_WIDGET(ud->builder, "source_device")); gtk_list_store_clear(GTK_LIST_STORE( gtk_combo_box_get_model(GTK_COMBO_BOX(combo)))); link = drives = dvd_device_list(); gtk_combo_box_text_append_text (combo, _("Not Selected")); while (link != NULL) { gchar *name = get_dvd_device_name(link->data); gtk_combo_box_text_append_text(combo, name); g_free(name); free_drive(link->data); link = link->next; } g_list_free(drives); gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); } static void break_duration(gint64 duration, gint *hh, gint *mm, gint *ss) { *hh = duration / (60*60); *mm = (duration / 60) % 60; *ss = duration % 60; } static void update_title_duration(signal_user_data_t *ud) { gint hh, mm, ss, start, end; gchar *text; GtkWidget *widget; int title_id, titleindex; const hb_title_t *title; title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); widget = GHB_WIDGET (ud->builder, "title_duration"); if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0) { start = ghb_settings_get_int(ud->settings, "start_point"); end = ghb_settings_get_int(ud->settings, "end_point"); ghb_part_duration(title, start, end, &hh, &mm, &ss); } else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1) { gint duration; start = ghb_settings_get_int(ud->settings, "start_point"); end = ghb_settings_get_int(ud->settings, "end_point"); duration = end - start; break_duration(duration, &hh, &mm, &ss); } else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2) { if (title != NULL) { gint64 frames; gint duration; start = ghb_settings_get_int(ud->settings, "start_point"); end = ghb_settings_get_int(ud->settings, "end_point"); frames = end - start + 1; duration = frames * title->rate_base / title->rate; break_duration(duration, &hh, &mm, &ss); } else { hh = mm = ss = 0; } } text = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss); gtk_label_set_text(GTK_LABEL(widget), text); g_free(text); } void ghb_show_container_options(signal_user_data_t *ud) { GtkWidget *w1, *w2, *w3; w1 = GHB_WIDGET(ud->builder, "Mp4LargeFile"); w2 = GHB_WIDGET(ud->builder, "Mp4HttpOptimize"); w3 = GHB_WIDGET(ud->builder, "Mp4iPodCompatible"); const char *mux_id; const hb_container_t *mux; mux_id = ghb_settings_get_const_string(ud->settings, "FileFormat"); mux = ghb_lookup_container_by_name(mux_id); gint enc = ghb_settings_video_encoder_codec(ud->settings, "VideoEncoder"); gtk_widget_set_visible(w1, (mux->format == HB_MUX_MP4V2)); gtk_widget_set_visible(w2, (mux->format & HB_MUX_MASK_MP4)); gtk_widget_set_visible(w3, (mux->format & HB_MUX_MASK_MP4) && (enc == HB_VCODEC_X264)); } static void adjustment_configure( GtkAdjustment *adj, double val, double min, double max, double step, double page, double page_sz) { gtk_adjustment_configure(adj, val, min, max, step, page, page_sz); } static void spin_configure(signal_user_data_t *ud, char *name, double val, double min, double max) { GtkSpinButton *spin; GtkAdjustment *adj; double step, page, page_sz; spin = GTK_SPIN_BUTTON(GHB_WIDGET(ud->builder, name)); adj = gtk_spin_button_get_adjustment(spin); step = gtk_adjustment_get_step_increment(adj); page = gtk_adjustment_get_page_increment(adj); page_sz = gtk_adjustment_get_page_size(adj); adjustment_configure(adj, val, min, max, step, page, page_sz); } void ghb_scale_configure( signal_user_data_t *ud, char *name, double val, double min, double max, double step, double page, int digits, gboolean inverted) { GtkScale *scale; GtkAdjustment *adj; double page_sz; scale = GTK_SCALE(GHB_WIDGET(ud->builder, name)); adj = gtk_range_get_adjustment(GTK_RANGE(scale)); page_sz = gtk_adjustment_get_page_size(adj); adjustment_configure(adj, val, min, max, step, page, page_sz); gtk_scale_set_digits(scale, digits); gtk_range_set_inverted(GTK_RANGE(scale), inverted); } void ghb_set_widget_ranges(signal_user_data_t *ud, GValue *settings) { int title_id, titleindex; const hb_title_t * title; double val; title_id = ghb_settings_get_int(settings, "title"); title = ghb_lookup_title(title_id, &titleindex); // Reconfigure the UI combo boxes ghb_update_ui_combo_box(ud, "AudioTrack", title, FALSE); ghb_update_ui_combo_box(ud, "SubtitleTrack", title, FALSE); if (title != NULL) { // Set the limits of cropping. hb_set_anamorphic_size crashes if // you pass it a cropped width or height == 0. gint vbound, hbound; vbound = title->height; hbound = title->width; val = ghb_settings_get_int(ud->settings, "PictureTopCrop"); spin_configure(ud, "PictureTopCrop", val, 0, vbound); val = ghb_settings_get_int(ud->settings, "PictureBottomCrop"); spin_configure(ud, "PictureBottomCrop", val, 0, vbound); val = ghb_settings_get_int(ud->settings, "PictureLeftCrop"); spin_configure(ud, "PictureLeftCrop", val, 0, hbound); val = ghb_settings_get_int(ud->settings, "PictureRightCrop"); spin_configure(ud, "PictureRightCrop", val, 0, hbound); gint duration = title->duration / 90000; if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0) { gint num_chapters = hb_list_count(title->list_chapter); val = ghb_settings_get_int(ud->settings, "start_point"); spin_configure(ud, "start_point", val, 1, num_chapters); val = ghb_settings_get_int(ud->settings, "end_point"); spin_configure(ud, "end_point", val, 1, num_chapters); } else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1) { val = ghb_settings_get_int(ud->settings, "start_point"); spin_configure(ud, "start_point", val, 0, duration-1); val = ghb_settings_get_int(ud->settings, "end_point"); spin_configure(ud, "end_point", val, 0, duration); } else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2) { gdouble max_frames; max_frames = (gdouble)duration * title->rate / title->rate_base; val = ghb_settings_get_int(ud->settings, "start_point"); spin_configure(ud, "start_point", val, 1, max_frames); val = ghb_settings_get_int(ud->settings, "end_point"); spin_configure(ud, "end_point", val, 1, max_frames); } val = ghb_settings_get_int(ud->settings, "angle"); spin_configure(ud, "angle", val, 1, title->angle_count); } float vqmin, vqmax, step, page; int inverted, digits; ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted); val = ghb_settings_get_double(ud->settings, "VideoQualitySlider"); ghb_scale_configure(ud, "VideoQualitySlider", val, vqmin, vqmax, step, page, digits, inverted); } static void check_chapter_markers(signal_user_data_t *ud) { GtkWidget *widget; gint start, end; if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0) { start = ghb_settings_get_int(ud->settings, "start_point"); end = ghb_settings_get_int(ud->settings, "end_point"); widget = GHB_WIDGET (ud->builder, "ChapterMarkers"); gtk_widget_set_sensitive(widget, end > start); } } #if 0 void show_settings(GValue *settings) { GHashTableIter iter; gchar *key; GValue *gval; ghb_dict_iter_init(&iter, settings); // middle (void*) cast prevents gcc warning "defreferencing type-punned // pointer will break strict-aliasing rules" while (g_hash_table_iter_next( &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval)) { char *str = ghb_value_string(gval); printf("show key %s val %s\n", key, str); g_free(str); } } #endif void ghb_load_settings(signal_user_data_t * ud) { GValue *preset; gboolean preset_modified; static gboolean busy = FALSE; if (busy) return; busy = TRUE; preset = ghb_settings_get_value(ud->settings, "preset"); preset_modified = ghb_settings_get_boolean(ud->settings, "preset_modified"); if (preset_modified) { ghb_clear_presets_selection(ud); } else { ghb_settings_set_boolean(ud->settings, "preset_reload", TRUE); ghb_select_preset(ud->builder, preset); ghb_settings_set_boolean(ud->settings, "preset_reload", FALSE); } ud->dont_clear_presets = TRUE; ud->scale_busy = TRUE; ghb_set_widget_ranges(ud, ud->settings); ghb_check_all_depencencies(ud); ghb_show_container_options(ud); check_chapter_markers(ud); ghb_settings_to_ui(ud, ud->settings); ghb_audio_defaults_to_ui(ud); ghb_subtitle_defaults_to_ui(ud); ghb_audio_list_refresh_all(ud); ghb_subtitle_list_refresh_all(ud); ghb_chapter_list_refresh_all(ud); update_title_duration(ud); ghb_update_title_info(ud); ud->dont_clear_presets = FALSE; ud->scale_busy = FALSE; busy = FALSE; ghb_picture_settings_deps(ud); } static void show_scan_progress(signal_user_data_t *ud) { GtkProgressBar *progress; GtkLabel *label; progress = GTK_PROGRESS_BAR(GHB_WIDGET(ud->builder, "scan_prog")); gtk_progress_bar_set_fraction (progress, 0); gtk_widget_show(GTK_WIDGET(progress)); label = GTK_LABEL(GHB_WIDGET(ud->builder, "volume_label")); gtk_label_set_text( label, _("Scanning ...") ); } static void start_scan( signal_user_data_t *ud, const gchar *path, gint title_id, gint preview_count) { GtkWidget *widget; ghb_status_t status; ghb_get_status(&status); if (status.scan.state != GHB_STATE_IDLE) return; widget = GHB_WIDGET(ud->builder, "sourcetoolbutton"); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-stop"); gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Stop Scan")); gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Stop Scan")); widget = GHB_WIDGET(ud->builder, "source_open"); gtk_widget_set_sensitive(widget, FALSE); widget = GHB_WIDGET(ud->builder, "source_title_open"); gtk_widget_set_sensitive(widget, FALSE); ghb_backend_scan(path, title_id, preview_count, 90000L * ghb_settings_get_int64(ud->prefs, "MinTitleDuration")); } gboolean ghb_idle_scan(signal_user_data_t *ud) { gchar *path; path = ghb_settings_get_string(ud->globals, "scan_source"); ghb_do_scan(ud, path, 0, TRUE); g_free(path); return FALSE; } extern GValue *ghb_queue_edit_settings; static gchar *last_scan_file = NULL; void ghb_do_scan( signal_user_data_t *ud, const gchar *filename, gint title_id, gboolean force) { int titleindex; const hb_title_t *title; (void)title; // Silence "unused variable" warning g_debug("ghb_do_scan()"); if (!force && last_scan_file != NULL && strcmp(last_scan_file, filename) == 0) { if (ghb_queue_edit_settings != NULL) { title_id = ghb_settings_get_int(ghb_queue_edit_settings, "title"); title = ghb_lookup_title(title_id, &titleindex); ghb_array_replace(ud->settings_array, titleindex, ghb_queue_edit_settings); ud->settings = ghb_queue_edit_settings; ghb_load_settings(ud); ghb_queue_edit_settings = NULL; } else { title = ghb_lookup_title(title_id, &titleindex); load_all_titles(ud, titleindex); } return; } if (last_scan_file != NULL) g_free(last_scan_file); last_scan_file = NULL; if (filename != NULL) { last_scan_file = g_strdup(filename); ghb_settings_set_string(ud->globals, "scan_source", filename); if (update_source_label(ud, filename)) { gchar *path; gint preview_count; show_scan_progress(ud); path = ghb_settings_get_string(ud->globals, "scan_source"); prune_logs(ud); preview_count = ghb_settings_get_int(ud->prefs, "preview_count"); start_scan(ud, path, title_id, preview_count); g_free(path); } else { // TODO: error dialog } } } static void do_source_dialog(GtkButton *button, gboolean single, signal_user_data_t *ud) { GtkWidget *dialog; gchar *sourcename; gint response; g_debug("source_browse_clicked_cb ()"); sourcename = ghb_settings_get_string(ud->globals, "scan_source"); GtkWidget *widget; widget = GHB_WIDGET(ud->builder, "single_title_box"); if (single) gtk_widget_show(widget); else gtk_widget_hide(widget); dialog = GHB_WIDGET(ud->builder, "source_dialog"); source_dialog_extra_widgets(ud, dialog); // gtk3 has a STUPID BUG! If you select the "same_path" it ends // up selecting the "Recent Files" shortcut instead of taking you // to the directory and file THAT YOU ASKED FOR!!! FUUUUK!!! // // So instead, I am just setting the current folder. It's not // optimal because if you are processing several files in the same // folder, you want the selection to return to where you were last // in the list, but there's not much else I can do at this point. //gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), sourcename); gchar *dirname = g_path_get_dirname(sourcename); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), dirname); g_free(dirname); response = gtk_dialog_run(GTK_DIALOG (dialog)); gtk_widget_hide(dialog); if (response == GTK_RESPONSE_NO) { gchar *filename; filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); if (filename != NULL) { gint title_id; if (single) title_id = ghb_settings_get_int(ud->settings, "single_title"); else title_id = 0; ghb_do_scan(ud, filename, title_id, TRUE); if (strcmp(sourcename, filename) != 0) { ghb_settings_set_string(ud->prefs, "default_source", filename); ghb_pref_save(ud->prefs, "default_source"); ghb_dvd_set_current(filename, ud); } g_free(filename); } } g_free(sourcename); } G_MODULE_EXPORT void source_button_clicked_cb(GtkButton *button, signal_user_data_t *ud) { ghb_status_t status; ghb_get_status(&status); if (status.scan.state & GHB_STATE_SCANNING) { ghb_backend_scan_stop(); } else { do_source_dialog(button, FALSE, ud); } } G_MODULE_EXPORT void single_title_source_cb(GtkButton *button, signal_user_data_t *ud) { do_source_dialog(button, TRUE, ud); } G_MODULE_EXPORT void dvd_source_activate_cb(GtkWidget *widget, signal_user_data_t *ud) { const gchar *filename; gchar *sourcename; sourcename = ghb_settings_get_string(ud->globals, "scan_source"); filename = gtk_buildable_get_name(GTK_BUILDABLE(widget)); ghb_do_scan(ud, filename, 0, TRUE); if (strcmp(sourcename, filename) != 0) { ghb_settings_set_string(ud->prefs, "default_source", filename); ghb_pref_save(ud->prefs, "default_source"); ghb_dvd_set_current(filename, ud); } g_free(sourcename); } void ghb_update_destination_extension(signal_user_data_t *ud) { static gchar *containers[] = {".mkv", ".mp4", ".m4v", ".error", NULL}; gchar *filename; const gchar *extension; gint ii; GtkEntry *entry; static gboolean busy = FALSE; g_debug("ghb_update_destination_extension ()"); // Since this function modifies the thing that triggers it's // invocation, check to see if busy to prevent accidental infinite // recursion. if (busy) return; busy = TRUE; extension = get_extension(ud, ud->settings); entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "dest_file")); filename = g_strdup(gtk_entry_get_text(entry)); for (ii = 0; containers[ii] != NULL; ii++) { if (g_str_has_suffix(filename, containers[ii])) { gchar *pos; gchar *new_name; pos = g_strrstr( filename, "." ); if (pos == NULL) { // No period? shouldn't happen break; } *pos = 0; if (strcmp(extension, &pos[1]) == 0) { // Extension is already correct break; } new_name = g_strjoin(".", filename, extension, NULL); ghb_ui_update(ud, "dest_file", ghb_string_value(new_name)); g_free(new_name); break; } } g_free(filename); busy = FALSE; } static void destination_select_title(GtkEntry *entry) { const gchar *dest; gint start, end; dest = gtk_entry_get_text(entry); for (end = strlen(dest)-1; end > 0; end--) { if (dest[end] == '.') { break; } } for (start = end; start >= 0; start--) { if (dest[start] == G_DIR_SEPARATOR) { start++; break; } } if (start < 0) start = 0; if (start < end) { gtk_editable_select_region(GTK_EDITABLE(entry), start, end); } } G_MODULE_EXPORT gboolean destination_grab_cb( GtkEntry *entry, signal_user_data_t *ud) { destination_select_title(entry); return FALSE; } static gboolean update_default_destination = FALSE; G_MODULE_EXPORT void dest_dir_set_cb(GtkFileChooserButton *dest_chooser, signal_user_data_t *ud) { gchar *dest_file, *dest_dir, *dest; g_debug("dest_dir_set_cb ()"); ghb_widget_to_setting(ud->settings, (GtkWidget*)dest_chooser); dest_file = ghb_settings_get_string(ud->settings, "dest_file"); dest_dir = ghb_settings_get_string(ud->settings, "dest_dir"); dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file); ghb_settings_set_string(ud->settings, "destination", dest); g_free(dest_file); g_free(dest_dir); g_free(dest); update_default_destination = TRUE; } G_MODULE_EXPORT void dest_file_changed_cb(GtkEntry *entry, signal_user_data_t *ud) { gchar *dest_file, *dest_dir, *dest; g_debug("dest_file_changed_cb ()"); ghb_update_destination_extension(ud); ghb_widget_to_setting(ud->settings, (GtkWidget*)entry); // This signal goes off with ever keystroke, so I'm putting this // update on the timer. dest_file = ghb_settings_get_string(ud->settings, "dest_file"); dest_dir = ghb_settings_get_string(ud->settings, "dest_dir"); dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file); ghb_settings_set_string(ud->settings, "destination", dest); g_free(dest_file); g_free(dest_dir); g_free(dest); update_default_destination = TRUE; } G_MODULE_EXPORT void destination_browse_clicked_cb(GtkButton *button, signal_user_data_t *ud) { GtkWidget *dialog; GtkEntry *entry; gchar *destname; gchar *basename; g_debug("destination_browse_clicked_cb ()"); destname = ghb_settings_get_string(ud->settings, "destination"); dialog = gtk_file_chooser_dialog_new ("Choose Destination", NULL, GTK_FILE_CHOOSER_ACTION_SAVE, GHB_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GHB_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), destname); basename = g_path_get_basename(destname); g_free(destname); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), basename); g_free(basename); if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { char *filename, *dirname; GtkFileChooser *dest_chooser; filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); basename = g_path_get_basename(filename); dirname = g_path_get_dirname(filename); entry = (GtkEntry*)GHB_WIDGET(ud->builder, "dest_file"); gtk_entry_set_text(entry, basename); dest_chooser = GTK_FILE_CHOOSER(GHB_WIDGET(ud->builder, "dest_dir")); gtk_file_chooser_set_filename(dest_chooser, dirname); g_free (dirname); g_free (basename); g_free (filename); } gtk_widget_destroy(dialog); } G_MODULE_EXPORT gboolean window_destroy_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud) { g_debug("window_destroy_event_cb ()"); ghb_hb_cleanup(FALSE); prune_logs(ud); gtk_main_quit(); return FALSE; } G_MODULE_EXPORT gboolean window_delete_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud) { gint state = ghb_get_queue_state(); g_debug("window_delete_event_cb ()"); if (state & (GHB_STATE_WORKING|GHB_STATE_SEARCHING)) { if (ghb_cancel_encode2(ud, _("Closing HandBrake will terminate encoding.\n"))) { ghb_hb_cleanup(FALSE); prune_logs(ud); gtk_main_quit(); return FALSE; } return TRUE; } ghb_hb_cleanup(FALSE); prune_logs(ud); gtk_main_quit(); return FALSE; } static void update_acodec(signal_user_data_t *ud) { ghb_adjust_audio_rate_combos(ud); ghb_grey_combo_options (ud); } G_MODULE_EXPORT void container_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("container_changed_cb ()"); ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_show_container_options(ud); update_acodec(ud); ghb_update_destination_extension(ud); ghb_clear_presets_selection(ud); ghb_live_reset(ud); ghb_subtitle_prune(ud); ghb_subtitle_list_refresh_all(ud); ghb_audio_list_refresh_selected(ud); } static gchar* get_aspect_string(gint aspect_n, gint aspect_d) { gchar *aspect; if (aspect_d < 10) { aspect = g_strdup_printf("%d:%d", aspect_n, aspect_d); } else { gdouble aspect_nf = (gdouble)aspect_n / aspect_d; aspect = g_strdup_printf("%.2f:1", aspect_nf); } return aspect; } static gchar* get_rate_string(gint rate_base, gint rate) { gdouble rate_f = (gdouble)rate / rate_base; gchar *rate_s; rate_s = g_strdup_printf("%.6g", rate_f); return rate_s; } static void update_aspect_info(signal_user_data_t *ud) { GtkWidget *widget; gchar *text; text = ghb_settings_get_boolean(ud->settings, "PictureAutoCrop") ? _("On") : _("Off"); widget = GHB_WIDGET(ud->builder, "crop_auto"); gtk_label_set_text(GTK_LABEL(widget), text); text = ghb_settings_get_boolean(ud->settings, "autoscale") ? _("On") : _("Off"); widget = GHB_WIDGET(ud->builder, "scale_auto"); gtk_label_set_text(GTK_LABEL(widget), text); switch (ghb_settings_combo_int(ud->settings, "PicturePAR")) { case 0: text = _("Off"); break; case 1: text = _("Strict"); break; case 2: text = _("Loose"); break; case 3: text = _("Custom"); break; default: text = _("Unknown"); break; } widget = GHB_WIDGET(ud->builder, "scale_anamorphic"); gtk_label_set_text(GTK_LABEL(widget), text); } static void update_crop_info(signal_user_data_t *ud) { GtkWidget *widget; gchar *text; gint width, height, crop[4] = {0,}; gint title_id, titleindex; const hb_title_t *title; title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (title != NULL) { crop[0] = ghb_settings_get_int(ud->settings, "PictureTopCrop"); crop[1] = ghb_settings_get_int(ud->settings, "PictureBottomCrop"); crop[2] = ghb_settings_get_int(ud->settings, "PictureLeftCrop"); crop[3] = ghb_settings_get_int(ud->settings, "PictureRightCrop"); width = title->width - crop[2] - crop[3]; height = title->height - crop[0] - crop[1]; widget = GHB_WIDGET(ud->builder, "crop_dimensions"); text = g_strdup_printf ("%d x %d", width, height); gtk_label_set_text(GTK_LABEL(widget), text); widget = GHB_WIDGET(ud->builder, "crop_dimensions2"); gtk_label_set_text(GTK_LABEL(widget), text); g_free(text); } widget = GHB_WIDGET (ud->builder, "crop_values"); text = g_strdup_printf ("%d:%d:%d:%d", crop[0], crop[1], crop[2], crop[3]); gtk_label_set_text (GTK_LABEL(widget), text); g_free(text); } static void update_scale_info(signal_user_data_t *ud) { GtkWidget *widget; gchar *text; gint width = ghb_settings_get_int(ud->settings, "scale_width"); gint height = ghb_settings_get_int(ud->settings, "scale_height"); widget = GHB_WIDGET(ud->builder, "scale_dimensions"); text = g_strdup_printf("%d x %d", width, height); gtk_label_set_text(GTK_LABEL(widget), text); g_free(text); } void ghb_update_title_info(signal_user_data_t *ud) { GtkWidget *widget; gchar *text; int title_id, titleindex; const hb_title_t * title; title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (title == NULL) return; update_title_duration(ud); widget = GHB_WIDGET (ud->builder, "source_video_codec"); if ( title->video_codec_name ) gtk_label_set_text (GTK_LABEL(widget), title->video_codec_name); else gtk_label_set_text (GTK_LABEL(widget), _("Unknown")); widget = GHB_WIDGET (ud->builder, "source_dimensions"); text = g_strdup_printf ("%d x %d", title->width, title->height); gtk_label_set_text (GTK_LABEL(widget), text); g_free(text); widget = GHB_WIDGET (ud->builder, "source_aspect"); gint aspect_n, aspect_d; hb_reduce(&aspect_n, &aspect_d, title->width * title->pixel_aspect_width, title->height * title->pixel_aspect_height); text = get_aspect_string(aspect_n, aspect_d); gtk_label_set_text (GTK_LABEL(widget), text); g_free(text); widget = GHB_WIDGET (ud->builder, "source_frame_rate"); text = (gchar*)get_rate_string(title->rate_base, title->rate); gtk_label_set_text (GTK_LABEL(widget), text); g_free(text); //widget = GHB_WIDGET (ud->builder, "source_interlaced"); //gtk_label_set_text (GTK_LABEL(widget), title->interlaced ? "Yes" : "No"); ghb_update_display_aspect_label(ud); update_crop_info(ud); update_aspect_info(ud); update_scale_info(ud); } void set_title_settings(signal_user_data_t *ud, GValue *settings) { int title_id, titleindex; const hb_title_t * title; title_id = ghb_settings_get_int(settings, "title"); title = ghb_lookup_title(title_id, &titleindex); ghb_subtitle_set_pref_lang(settings); if (title != NULL) { gint num_chapters = hb_list_count(title->list_chapter); ghb_settings_set_int(settings, "angle", 1); ghb_settings_set_int(settings, "PtoPType", 0); ghb_settings_set_int(settings, "start_point", 1); ghb_settings_set_int(settings, "end_point", num_chapters); ghb_settings_set_int(settings, "source_width", title->width); ghb_settings_set_int(settings, "source_height", title->height); ghb_settings_set_string(settings, "source", title->path); if (title->type == HB_STREAM_TYPE || title->type == HB_FF_STREAM_TYPE) { if (title->name != NULL && title->name[0] != 0) { ghb_settings_set_string(settings, "volume_label", title->name); } else { gchar *label = _("No Title Found"); ghb_settings_set_string(settings, "volume_label", label); } } else { ghb_settings_set_value(settings, "volume_label", ghb_settings_get_value(ud->settings, "volume_label")); } ghb_settings_set_int(settings, "scale_width", title->width - title->crop[2] - title->crop[3]); // If anamorphic or keep_aspect, the hight will // be automatically calculated gboolean keep_aspect; gint pic_par; keep_aspect = ghb_settings_get_boolean(settings, "PictureKeepRatio"); pic_par = ghb_settings_combo_int(settings, "PicturePAR"); if (!(keep_aspect || pic_par) || pic_par == 3) { ghb_settings_set_int(settings, "scale_height", title->width - title->crop[0] - title->crop[1]); } ghb_set_scale_settings(settings, GHB_PIC_KEEP_PAR|GHB_PIC_USE_MAX); ghb_settings_set_int(settings, "angle_count", title->angle_count); ghb_settings_set_string(settings, "MetaName", title->name); if (title->metadata) { if (title->metadata->name) { ghb_settings_set_string(settings, "MetaName", title->metadata->name); } ghb_settings_set_string(settings, "MetaArtist", title->metadata->artist); ghb_settings_set_string(settings, "MetaReleaseDate", title->metadata->release_date); ghb_settings_set_string(settings, "MetaComment", title->metadata->comment); if (!title->metadata->name && title->metadata->album) { ghb_settings_set_string(settings, "MetaName", title->metadata->album); } ghb_settings_set_string(settings, "MetaAlbumArtist", title->metadata->album_artist); ghb_settings_set_string(settings, "MetaGenre", title->metadata->genre); ghb_settings_set_string(settings, "MetaDescription", title->metadata->description); ghb_settings_set_string(settings, "MetaLongDescription", title->metadata->long_description); } update_chapter_list_settings(settings); ghb_set_pref_audio_settings(settings); ghb_set_pref_subtitle_settings(ud, title, settings); } set_destination_settings(ud, settings); ghb_settings_set_value(settings, "dest_dir", ghb_settings_get_value(ud->prefs, "destination_dir")); char *dest_file, *dest_dir, *dest; dest_file = ghb_settings_get_string(settings, "dest_file"); dest_dir = ghb_settings_get_string(settings, "dest_dir"); dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file); ghb_settings_set_string(settings, "destination", dest); g_free(dest_file); g_free(dest_dir); g_free(dest); ghb_settings_set_int(settings, "preview_frame", 2); } void ghb_set_current_title_settings(signal_user_data_t *ud) { set_title_settings(ud, ud->settings); } static void load_all_titles(signal_user_data_t *ud, int titleindex) { gint ii, count; GValue *preset, *preset_path = NULL; GValue *settings_array; const hb_title_t *title; hb_list_t *list = ghb_get_title_list(); count = hb_list_count(list); if (count == 0) count = 1; settings_array = ghb_array_value_new(count); preset = ghb_get_current_preset(ud); if (preset != NULL) { preset_path = ghb_get_current_preset_path(ud); } else { preset = ud->settings; } for (ii = 0; ii < count; ii++) { int index; GValue *settings = ghb_settings_new(); title = hb_list_item(list, ii); index = (title != NULL) ? title->index : -1; ghb_settings_init(settings, "Initialization"); ghb_preset_to_settings(settings, preset); ghb_settings_set_value(settings, "preset", preset_path); ghb_settings_set_int(settings, "title", index); set_title_settings(ud, settings); ghb_array_append(settings_array, settings); } ghb_value_free(preset_path); if (titleindex < 0 || titleindex >= count) { titleindex = 0; } ghb_value_free(ud->settings_array); ud->settings_array = settings_array; ud->settings = ghb_array_get_nth(ud->settings_array, titleindex); } static gboolean update_preview = FALSE; G_MODULE_EXPORT void title_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { gint title_id, titleindex, count; const hb_title_t * title; g_debug("title_changed_cb ()"); title_id = ghb_widget_int(widget); title = ghb_lookup_title(title_id, &titleindex); count = ghb_array_len(ud->settings_array); int idx = (titleindex >= 0 && titleindex < count) ? titleindex : 0; ud->settings = ghb_array_get_nth(ud->settings_array, idx); ghb_load_settings(ud); ghb_audio_title_change(ud, title != NULL); ghb_subtitle_title_change(ud, title != NULL); ghb_grey_combo_options(ud); if (title != NULL) { ghb_set_preview_image(ud); ghb_preview_set_visible(ud); } } G_MODULE_EXPORT void title_reset_clicked_cb(GtkWidget *widget, signal_user_data_t *ud) { int title_id, titleindex; const hb_title_t *title; title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); (void)title; // Silence "unused variable" warning load_all_titles(ud, titleindex); ghb_load_settings(ud); } G_MODULE_EXPORT void ptop_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { gint title_id, titleindex; const hb_title_t * title; gboolean numeric = TRUE; GtkSpinButton *spin; ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_live_reset(ud); title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); if (title == NULL) return; if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1) numeric = FALSE; spin = GTK_SPIN_BUTTON(GHB_WIDGET(ud->builder, "start_point")); gtk_spin_button_set_numeric(spin, numeric); spin = GTK_SPIN_BUTTON(GHB_WIDGET(ud->builder, "end_point")); gtk_spin_button_set_numeric(spin, numeric); gint duration = title->duration / 90000; if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0) { gint num_chapters = hb_list_count(title->list_chapter); spin_configure(ud, "start_point", 1, 1, num_chapters); spin_configure(ud, "end_point", num_chapters, 1, num_chapters); } else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1) { spin_configure(ud, "start_point", 0, 0, duration-1); spin_configure(ud, "end_point", duration, 0, duration); } else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2) { gdouble max_frames = (gdouble)duration * title->rate / title->rate_base; spin_configure(ud, "start_point", 1, 1, max_frames); spin_configure(ud, "end_point", max_frames, 1, max_frames); } } G_MODULE_EXPORT void framerate_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); if (ghb_settings_video_framerate_rate(ud->settings, "VideoFramerate") != 0) { if (!ghb_settings_get_boolean(ud->settings, "VideoFrameratePFR")) { ghb_ui_update(ud, "VideoFramerateCFR", ghb_boolean_value(TRUE)); } } if (ghb_settings_video_framerate_rate(ud->settings, "VideoFramerate") == 0 && ghb_settings_get_boolean(ud->settings, "VideoFrameratePFR")) { ghb_ui_update(ud, "VideoFramerateVFR", ghb_boolean_value(TRUE)); } ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); ghb_live_reset(ud); } G_MODULE_EXPORT void setting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); ghb_live_reset(ud); } G_MODULE_EXPORT gboolean meta_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); return FALSE; } G_MODULE_EXPORT void meta_setting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); } G_MODULE_EXPORT void chapter_markers_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); ghb_live_reset(ud); } G_MODULE_EXPORT void vquality_type_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); ghb_live_reset(ud); if (check_name_template(ud, "{quality}") || check_name_template(ud, "{bitrate}")) set_destination(ud); } G_MODULE_EXPORT void vbitrate_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); ghb_live_reset(ud); if (check_name_template(ud, "{bitrate}")) set_destination(ud); } G_MODULE_EXPORT void vquality_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); ghb_live_reset(ud); double vquality = ghb_settings_get_double(ud->settings, "VideoQualitySlider"); if (vquality < 1.0) { ghb_ui_update(ud, "VideoProfile", ghb_string_value("auto")); } gint vcodec; gdouble step; vcodec = ghb_settings_video_encoder_codec(ud->settings, "VideoEncoder"); if (vcodec == HB_VCODEC_X264) { step = ghb_settings_combo_double(ud->prefs, "VideoQualityGranularity"); } else { step = 1; } gdouble val = gtk_range_get_value(GTK_RANGE(widget)); val = ((int)((val + step / 2) / step)) * step; gtk_range_set_value(GTK_RANGE(widget), val); if (check_name_template(ud, "{quality}")) set_destination(ud); } G_MODULE_EXPORT void http_opt_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); ghb_live_reset(ud); // AC3 is not allowed when Web optimized ghb_grey_combo_options (ud); } G_MODULE_EXPORT gboolean ptop_input_cb(GtkWidget *widget, gdouble *val, signal_user_data_t *ud) { if (ghb_settings_combo_int(ud->settings, "PtoPType") != 1) return FALSE; const gchar *text; int result; double ss = 0; int hh = 0, mm = 0; text = gtk_entry_get_text(GTK_ENTRY(widget)); result = sscanf(text, "%2d:%2d:%lf", &hh, &mm, &ss); if (result <= 0) return FALSE; if (result == 1) { result = sscanf(text, "%lf", val); return TRUE; } *val = hh * 3600 + mm * 60 + ss; return TRUE; } G_MODULE_EXPORT gboolean ptop_output_cb(GtkWidget *widget, signal_user_data_t *ud) { if (ghb_settings_combo_int(ud->settings, "PtoPType") != 1) return FALSE; GtkAdjustment *adjustment; gchar *text; double value, ss; int hh, mm; adjustment = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(widget)); value = gtk_adjustment_get_value(adjustment); hh = value / 3600; value = value - hh * 3600; mm = value / 60; value = value - mm * 60; ss = value; text = g_strdup_printf ("%02d:%02d:%05.2f", hh, mm, ss); gtk_entry_set_text(GTK_ENTRY(widget), text); g_free (text); return TRUE; } G_MODULE_EXPORT void start_point_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { gint start, end; ghb_widget_to_setting(ud->settings, widget); if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0) { start = ghb_settings_get_int(ud->settings, "start_point"); end = ghb_settings_get_int(ud->settings, "end_point"); if (start > end) ghb_ui_update(ud, "end_point", ghb_int_value(start)); ghb_check_dependency(ud, widget, NULL); if (check_name_template(ud, "{chapters}")) set_destination(ud); widget = GHB_WIDGET (ud->builder, "ChapterMarkers"); // End may have been changed above, get it again end = ghb_settings_get_int(ud->settings, "end_point"); gtk_widget_set_sensitive(widget, end > start); update_title_duration(ud); } else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1) { start = ghb_settings_get_int(ud->settings, "start_point"); end = ghb_settings_get_int(ud->settings, "end_point"); if (start >= end) ghb_ui_update(ud, "end_point", ghb_int_value(start+1)); ghb_check_dependency(ud, widget, NULL); update_title_duration(ud); } else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2) { start = ghb_settings_get_int(ud->settings, "start_point"); end = ghb_settings_get_int(ud->settings, "end_point"); if (start > end) ghb_ui_update(ud, "end_point", ghb_int_value(start)); ghb_check_dependency(ud, widget, NULL); update_title_duration(ud); } } G_MODULE_EXPORT void end_point_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { gint start, end; ghb_widget_to_setting(ud->settings, widget); if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0) { start = ghb_settings_get_int(ud->settings, "start_point"); end = ghb_settings_get_int(ud->settings, "end_point"); if (start > end) ghb_ui_update(ud, "start_point", ghb_int_value(end)); ghb_check_dependency(ud, widget, NULL); if (check_name_template(ud, "{chapters}")) set_destination(ud); widget = GHB_WIDGET (ud->builder, "ChapterMarkers"); // Start may have been changed above, get it again start = ghb_settings_get_int(ud->settings, "start_point"); gtk_widget_set_sensitive(widget, end > start); update_title_duration(ud); } else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1) { start = ghb_settings_get_int(ud->settings, "start_point"); end = ghb_settings_get_int(ud->settings, "end_point"); if (start >= end) ghb_ui_update(ud, "start_point", ghb_int_value(end-1)); ghb_check_dependency(ud, widget, NULL); update_title_duration(ud); } else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2) { start = ghb_settings_get_int(ud->settings, "start_point"); end = ghb_settings_get_int(ud->settings, "end_point"); if (start > end) ghb_ui_update(ud, "start_point", ghb_int_value(end)); ghb_check_dependency(ud, widget, NULL); update_title_duration(ud); } } G_MODULE_EXPORT void scale_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("scale_width_changed_cb ()"); ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); if (gtk_widget_is_sensitive(widget)) ghb_set_scale(ud, GHB_PIC_KEEP_WIDTH); update_preview = TRUE; ghb_live_reset(ud); update_scale_info(ud); } G_MODULE_EXPORT void scale_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("scale_height_changed_cb ()"); ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); if (gtk_widget_is_sensitive(widget)) ghb_set_scale(ud, GHB_PIC_KEEP_HEIGHT); update_preview = TRUE; ghb_live_reset(ud); update_scale_info(ud); } G_MODULE_EXPORT void crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("crop_changed_cb ()"); ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); if (gtk_widget_is_sensitive(widget)) ghb_set_scale(ud, 0); update_preview = TRUE; ghb_live_reset(ud); update_crop_info(ud); } G_MODULE_EXPORT void display_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("display_width_changed_cb ()"); ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); ghb_live_reset(ud); if (gtk_widget_is_sensitive(widget)) ghb_set_scale(ud, GHB_PIC_KEEP_DISPLAY_WIDTH); update_preview = TRUE; } G_MODULE_EXPORT void display_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("display_height_changed_cb ()"); ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); ghb_live_reset(ud); if (gtk_widget_is_sensitive(widget)) ghb_set_scale(ud, GHB_PIC_KEEP_DISPLAY_HEIGHT); update_preview = TRUE; } G_MODULE_EXPORT void par_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("par_changed_cb ()"); ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); ghb_live_reset(ud); if (gtk_widget_is_sensitive(widget)) ghb_set_scale(ud, GHB_PIC_KEEP_PAR); update_preview = TRUE; } G_MODULE_EXPORT void scale_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("scale_changed_cb ()"); ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); ghb_live_reset(ud); if (gtk_widget_is_sensitive(widget)) ghb_set_scale(ud, 0); update_preview = TRUE; update_aspect_info(ud); } G_MODULE_EXPORT void show_crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("show_crop_changed_cb ()"); ghb_widget_to_setting(ud->prefs, widget); ghb_check_dependency(ud, widget, NULL); ghb_live_reset(ud); if (gtk_widget_is_sensitive(widget)) ghb_set_scale(ud, 0); ghb_pref_save(ud->prefs, "preview_show_crop"); update_preview = TRUE; } G_MODULE_EXPORT void generic_entry_changed_cb(GtkEntry *entry, signal_user_data_t *ud) { // Normally (due to user input) I only want to process the entry // when editing is done and the focus-out signal is sent. // But... there's always a but. // If the entry is changed by software, the focus-out signal is not sent. // The changed signal is sent ... so here we are. // I don't want to process upon every keystroke, so I prevent processing // while the widget has focus. g_debug("generic_entry_changed_cb ()"); if (!gtk_widget_has_focus((GtkWidget*)entry)) { ghb_widget_to_setting(ud->settings, (GtkWidget*)entry); } } G_MODULE_EXPORT void prefs_dialog_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GtkWidget *dialog; g_debug("prefs_dialog_cb ()"); dialog = GHB_WIDGET(ud->builder, "prefs_dialog"); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); ghb_prefs_store(); } typedef struct { GtkMessageDialog *dlg; const gchar *msg; const gchar *action; gint timeout; signal_user_data_t *ud; } countdown_t; static gboolean quit_cb(countdown_t *cd) { gchar *str; cd->timeout--; if (cd->timeout == 0) { ghb_hb_cleanup(FALSE); prune_logs(cd->ud); gtk_widget_destroy (GTK_WIDGET(cd->dlg)); gtk_main_quit(); return FALSE; } str = g_strdup_printf(_("%s\n\n%s in %d seconds ..."), cd->msg, cd->action, cd->timeout); gtk_message_dialog_set_markup(cd->dlg, str); g_free(str); return TRUE; } static gboolean shutdown_cb(countdown_t *cd) { gchar *str; cd->timeout--; if (cd->timeout == 0) { ghb_hb_cleanup(FALSE); prune_logs(cd->ud); ghb_shutdown_gsm(); gtk_main_quit(); return FALSE; } str = g_strdup_printf(_("%s\n\n%s in %d seconds ..."), cd->msg, cd->action, cd->timeout); gtk_message_dialog_set_markup(cd->dlg, str); g_free(str); return TRUE; } static gboolean suspend_cb(countdown_t *cd) { gchar *str; cd->timeout--; if (cd->timeout == 0) { gtk_widget_destroy (GTK_WIDGET(cd->dlg)); ghb_suspend_gpm(); return FALSE; } str = g_strdup_printf(_("%s\n\n%s in %d seconds ..."), cd->msg, cd->action, cd->timeout); gtk_message_dialog_set_markup(cd->dlg, str); g_free(str); return TRUE; } void ghb_countdown_dialog( GtkMessageType type, const gchar *message, const gchar *action, const gchar *cancel, GSourceFunc action_func, signal_user_data_t *ud, gint timeout) { GtkWidget *dialog; GtkResponseType response; guint timeout_id; countdown_t cd; cd.msg = message; cd.action = action; cd.timeout = timeout; cd.ud = ud; // Toss up a warning dialog dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, type, GTK_BUTTONS_NONE, _("%s\n\n%s in %d seconds ..."), message, action, timeout); gtk_dialog_add_buttons( GTK_DIALOG(dialog), cancel, GTK_RESPONSE_CANCEL, NULL); cd.dlg = GTK_MESSAGE_DIALOG(dialog); timeout_id = g_timeout_add(1000, action_func, &cd); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy (dialog); if (response == GTK_RESPONSE_CANCEL) { GMainContext *mc; GSource *source; mc = g_main_context_default(); source = g_main_context_find_source_by_id(mc, timeout_id); if (source != NULL) g_source_destroy(source); } } gboolean ghb_message_dialog(GtkMessageType type, const gchar *message, const gchar *no, const gchar *yes) { GtkWidget *dialog; GtkResponseType response; // Toss up a warning dialog dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, type, GTK_BUTTONS_NONE, "%s", message); gtk_dialog_add_buttons( GTK_DIALOG(dialog), no, GTK_RESPONSE_NO, yes, GTK_RESPONSE_YES, NULL); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy (dialog); if (response == GTK_RESPONSE_NO) { return FALSE; } return TRUE; } void ghb_error_dialog(GtkMessageType type, const gchar *message, const gchar *cancel) { GtkWidget *dialog; // Toss up a warning dialog dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, type, GTK_BUTTONS_NONE, "%s", message); gtk_dialog_add_buttons( GTK_DIALOG(dialog), cancel, GTK_RESPONSE_CANCEL, NULL); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy (dialog); } void ghb_cancel_encode(signal_user_data_t *ud, const gchar *extra_msg) { GtkWidget *dialog; GtkResponseType response; if (extra_msg == NULL) extra_msg = ""; // Toss up a warning dialog dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, _("%sYour movie will be lost if you don't continue encoding."), extra_msg); gtk_dialog_add_buttons( GTK_DIALOG(dialog), _("Cancel Current and Stop"), 1, _("Cancel Current, Start Next"), 2, _("Finish Current, then Stop"), 3, _("Continue Encoding"), 4, NULL); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy (dialog); switch ((int)response) { case 1: ghb_stop_queue(); ud->cancel_encode = GHB_CANCEL_ALL; break; case 2: ghb_stop_queue(); ud->cancel_encode = GHB_CANCEL_CURRENT; break; case 3: ud->cancel_encode = GHB_CANCEL_FINISH; break; case 4: default: ud->cancel_encode = GHB_CANCEL_NONE; break; } } gboolean ghb_cancel_encode2(signal_user_data_t *ud, const gchar *extra_msg) { GtkWidget *dialog; GtkResponseType response; if (extra_msg == NULL) extra_msg = ""; // Toss up a warning dialog dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, _("%sYour movie will be lost if you don't continue encoding."), extra_msg); gtk_dialog_add_buttons( GTK_DIALOG(dialog), _("Cancel Current and Stop"), 1, _("Continue Encoding"), 4, NULL); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy (dialog); switch ((int)response) { case 1: ghb_stop_queue(); ud->cancel_encode = GHB_CANCEL_ALL; return TRUE; case 4: default: break; } return FALSE; } static gint find_queue_job(GValue *queue, gint unique_id, GValue **job) { GValue *js; gint ii, count; gint job_unique_id; *job = NULL; g_debug("find_queue_job"); if (unique_id == 0) // Invalid Id return -1; count = ghb_array_len(queue); for (ii = 0; ii < count; ii++) { js = ghb_array_get_nth(queue, ii); job_unique_id = ghb_settings_get_int(js, "job_unique_id"); if (job_unique_id == unique_id) { *job = js; return ii; } } return -1; } static void submit_job(signal_user_data_t *ud, GValue *settings) { static gint unique_id = 1; gchar *type, *modified, *preset; const GValue *path; GValue *js; gboolean preset_modified; g_debug("submit_job"); if (settings == NULL) return; preset_modified = ghb_settings_get_boolean(settings, "preset_modified"); path = ghb_settings_get_value(settings, "preset"); preset = ghb_preset_path_string(path); type = ghb_preset_is_custom() ? "Custom " : ""; modified = preset_modified ? "Modified " : ""; ghb_log("%s%sPreset: %s", modified, type, preset); g_free(preset); ghb_settings_set_int(settings, "job_unique_id", unique_id); ghb_settings_set_int(settings, "job_status", GHB_QUEUE_RUNNING); ghb_add_job (settings, unique_id); ghb_start_queue(); // Start queue activity spinner int index = find_queue_job(ud->queue, unique_id, &js); if (index >= 0) { GtkTreeView *treeview; GtkTreeModel *store; GtkTreeIter iter; treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list")); store = gtk_tree_view_get_model(treeview); gchar *path = g_strdup_printf ("%d", index); if (gtk_tree_model_get_iter_from_string(store, &iter, path)) { gtk_tree_store_set(GTK_TREE_STORE(store), &iter, 0, TRUE, -1); } g_free(path); } unique_id++; } static void prune_logs(signal_user_data_t *ud) { gchar *dest_dir; gint days; // Only prune logs stored in the default config dir location days = ghb_settings_combo_int(ud->prefs, "LogLongevity"); if (days > 365) return; dest_dir = ghb_get_user_config_dir("EncodeLogs"); if (g_file_test(dest_dir, G_FILE_TEST_IS_DIR)) { const gchar *file; gint duration = days * 24 * 60 * 60; GDir *gdir = g_dir_open(dest_dir, 0, NULL); time_t now; now = time(NULL); file = g_dir_read_name(gdir); while (file) { gchar *path; struct stat stbuf; path = g_strdup_printf("%s/%s", dest_dir, file); g_stat(path, &stbuf); if (now - stbuf.st_mtime > duration) { g_unlink(path); } g_free(path); file = g_dir_read_name(gdir); } g_dir_close(gdir); } g_free(dest_dir); ghb_preview_cleanup(ud); } static void queue_scan(signal_user_data_t *ud, GValue *js) { gchar *path; gint title_id; time_t _now; struct tm *now; gchar *log_path, *pos, *destname, *basename, *dest_dir; _now = time(NULL); now = localtime(&_now); destname = ghb_settings_get_string(js, "destination"); basename = g_path_get_basename(destname); if (ghb_settings_get_boolean(ud->prefs, "EncodeLogLocation")) { dest_dir = g_path_get_dirname (destname); } else { dest_dir = ghb_get_user_config_dir("EncodeLogs"); } g_free(destname); pos = g_strrstr( basename, "." ); if (pos != NULL) { *pos = 0; } log_path = g_strdup_printf("%s/%s %d-%02d-%02d %02d-%02d-%02d.log", dest_dir, basename, now->tm_year + 1900, now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec); g_free(basename); g_free(dest_dir); if (ud->job_activity_log) g_io_channel_unref(ud->job_activity_log); ud->job_activity_log = g_io_channel_new_file (log_path, "w", NULL); if (ud->job_activity_log) { gchar *ver_str; ver_str = g_strdup_printf("Handbrake Version: %s (%d)\n", hb_get_version(NULL), hb_get_build(NULL)); g_io_channel_write_chars (ud->job_activity_log, ver_str, -1, NULL, NULL); g_free(ver_str); } g_free(log_path); path = ghb_settings_get_string( js, "source"); title_id = ghb_settings_get_int(js, "title"); ghb_backend_queue_scan(path, title_id); g_free(path); } static gint queue_pending_count(GValue *queue) { gint nn, ii, count; GValue *js; gint status; nn = 0; count = ghb_array_len(queue); for (ii = 0; ii < count; ii++) { js = ghb_array_get_nth(queue, ii); status = ghb_settings_get_int(js, "job_status"); if (status == GHB_QUEUE_PENDING) { nn++; } } return nn; } void ghb_update_pending(signal_user_data_t *ud) { GtkLabel *label; gint pending; gchar *str; label = GTK_LABEL(GHB_WIDGET(ud->builder, "pending_status")); pending = queue_pending_count(ud->queue); str = g_strdup_printf(_("%d encode(s) pending"), pending); gtk_label_set_text(label, str); g_free(str); } GValue* ghb_start_next_job(signal_user_data_t *ud) { gint count, ii; GValue *js; gint status; GtkWidget *progress; g_debug("start_next_job"); progress = GHB_WIDGET(ud->builder, "progressbar"); gtk_widget_show(progress); count = ghb_array_len(ud->queue); for (ii = 0; ii < count; ii++) { js = ghb_array_get_nth(ud->queue, ii); status = ghb_settings_get_int(js, "job_status"); if (status == GHB_QUEUE_PENDING) { ghb_inhibit_gsm(ud); queue_scan(ud, js); ghb_update_pending(ud); // Start queue activity spinner GtkTreeView *treeview; GtkTreeModel *store; GtkTreeIter iter; treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list")); store = gtk_tree_view_get_model(treeview); gchar *path = g_strdup_printf ("%d", ii); if (gtk_tree_model_get_iter_from_string(store, &iter, path)) { gtk_tree_store_set(GTK_TREE_STORE(store), &iter, 0, TRUE, -1); } g_free(path); return js; } } // Nothing pending ghb_uninhibit_gsm(); ghb_notify_done(ud); ghb_update_pending(ud); gtk_widget_hide(progress); return NULL; } gchar* working_status_string(signal_user_data_t *ud, ghb_instance_status_t *status) { gchar *task_str, *job_str, *status_str; gint qcount; gint index; GValue *js; gboolean subtitle_scan = FALSE; qcount = ghb_array_len(ud->queue); index = find_queue_job(ud->queue, status->unique_id, &js); if (js != NULL) { subtitle_scan = ghb_settings_get_boolean(js, "subtitle_scan"); } if (qcount > 1) { job_str = g_strdup_printf(_("job %d of %d, "), index+1, qcount); } else { job_str = g_strdup(""); } if (status->job_count > 1) { if (status->job_cur == 1 && subtitle_scan) { task_str = g_strdup_printf(_("pass %d (subtitle scan) of %d, "), status->job_cur, status->job_count); } else { task_str = g_strdup_printf(_("pass %d of %d, "), status->job_cur, status->job_count); } } else { task_str = g_strdup(""); } if(status->seconds > -1) { if (status->rate_cur > 0.0) { status_str= g_strdup_printf( _("Encoding: %s%s%.2f %%" " (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)"), job_str, task_str, 100.0 * status->progress, status->rate_cur, status->rate_avg, status->hours, status->minutes, status->seconds ); } else { status_str= g_strdup_printf( _("Encoding: %s%s%.2f %%" " (ETA %02dh%02dm%02ds)"), job_str, task_str, 100.0 * status->progress, status->hours, status->minutes, status->seconds ); } } else { status_str= g_strdup_printf( _("Encoding: %s%s%.2f %%"), job_str, task_str, 100.0 * status->progress ); } g_free(task_str); g_free(job_str); return status_str; } gchar* searching_status_string(signal_user_data_t *ud, ghb_instance_status_t *status) { gchar *task_str, *job_str, *status_str; gint qcount; gint index; GValue *js; qcount = ghb_array_len(ud->queue); index = find_queue_job(ud->queue, status->unique_id, &js); if (qcount > 1) { job_str = g_strdup_printf(_("job %d of %d, "), index+1, qcount); } else { job_str = g_strdup(""); } task_str = g_strdup_printf(_("Searching for start time, ")); if(status->seconds > -1) { status_str= g_strdup_printf( _("Encoding: %s%s%.2f %%" " (ETA %02dh%02dm%02ds)"), job_str, task_str, 100.0 * status->progress, status->hours, status->minutes, status->seconds ); } else { status_str= g_strdup_printf( _("Encoding: %s%s%.2f %%"), job_str, task_str, 100.0 * status->progress ); } g_free(task_str); g_free(job_str); return status_str; } static void ghb_backend_events(signal_user_data_t *ud) { ghb_status_t status; gchar *status_str; GtkProgressBar *progress; GtkLabel *work_status; GValue *js; gint index; GtkTreeView *treeview; GtkTreeModel *store; GtkTreeIter iter; static gint prev_scan_state = -1; static gint prev_queue_state = -1; ghb_track_status(); ghb_get_status(&status); if (prev_scan_state != status.scan.state || prev_queue_state != status.queue.state) { ghb_queue_buttons_grey(ud); prev_scan_state = status.scan.state; prev_queue_state = status.queue.state; } progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar")); work_status = GTK_LABEL(GHB_WIDGET (ud->builder, "work_status")); if (status.scan.state == GHB_STATE_IDLE && status.queue.state == GHB_STATE_IDLE) { static gboolean prev_dvdnav; gboolean dvdnav = ghb_settings_get_boolean(ud->prefs, "use_dvdnav"); if (dvdnav != prev_dvdnav) { hb_dvd_set_dvdnav(dvdnav); prev_dvdnav = dvdnav; } } // First handle the status of title scans // Then handle the status of the queue if (status.scan.state & GHB_STATE_SCANNING) { GtkProgressBar *scan_prog; GtkLabel *label; scan_prog = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "scan_prog")); label = GTK_LABEL(GHB_WIDGET (ud->builder, "volume_label")); if (status.scan.title_cur == 0) { status_str = g_strdup (_("Scanning...")); } else { if (status.scan.preview_cur == 0) status_str = g_strdup_printf(_("Scanning title %d of %d..."), status.scan.title_cur, status.scan.title_count ); else status_str = g_strdup_printf( _("Scanning title %d of %d preview %d..."), status.scan.title_cur, status.scan.title_count, status.scan.preview_cur); } gtk_label_set_text (label, status_str); g_free(status_str); if (status.scan.title_count > 0) { gtk_progress_bar_set_fraction (scan_prog, status.scan.progress); } } else if (status.scan.state & GHB_STATE_SCANDONE) { gchar *source; GtkProgressBar *scan_prog; GtkLabel *label; GtkWidget *widget; widget = GHB_WIDGET(ud->builder, "sourcetoolbutton"); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-source"); gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Source")); gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Choose Video Source")); widget = GHB_WIDGET(ud->builder, "source_open"); gtk_widget_set_sensitive(widget, TRUE); widget = GHB_WIDGET(ud->builder, "source_title_open"); gtk_widget_set_sensitive(widget, TRUE); source = ghb_settings_get_string(ud->globals, "scan_source"); update_source_label(ud, source); g_free(source); scan_prog = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "scan_prog")); gtk_progress_bar_set_fraction (scan_prog, 1.0); gtk_widget_hide(GTK_WIDGET(scan_prog)); int title_id, titleindex; const hb_title_t *title; title_id = ghb_longest_title(); title = ghb_lookup_title(title_id, &titleindex); ghb_update_ui_combo_box(ud, "title", NULL, FALSE); load_all_titles(ud, titleindex); label = GTK_LABEL(GHB_WIDGET (ud->builder, "volume_label")); ghb_clear_scan_state(GHB_STATE_SCANDONE); // Are there really any titles. if (title == NULL) { gtk_label_set_text(label, _("No Title Found")); ghb_ui_update(ud, "title", ghb_int64_value(-1)); } else { ghb_ui_update(ud, "title", ghb_int64_value(title->index)); } if (ghb_queue_edit_settings != NULL) { // Switch to the correct title in the list ghb_ui_update(ud, "title", ghb_settings_get_value(ghb_queue_edit_settings, "title")); // The above should cause the current title index to update title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); ghb_array_replace(ud->settings_array, titleindex, ghb_queue_edit_settings); ud->settings = ghb_queue_edit_settings; ghb_load_settings(ud); ghb_queue_edit_settings = NULL; } } if (status.queue.unique_id != 0) { index = find_queue_job(ud->queue, status.queue.unique_id, &js); if (index >= 0) { treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list")); store = gtk_tree_view_get_model(treeview); gchar *path = g_strdup_printf ("%d", index); if (gtk_tree_model_get_iter_from_string(store, &iter, path)) { if ((status.queue.state & GHB_STATE_WORKDONE) || (status.queue.state & GHB_STATE_SCANDONE)) { gtk_tree_store_set(GTK_TREE_STORE(store), &iter, 0, FALSE, -1); } else if (!(status.queue.state & GHB_STATE_PAUSED)) { gint pulse; gtk_tree_model_get(store, &iter, 4, &pulse, -1); gtk_tree_store_set(GTK_TREE_STORE(store), &iter, 4, pulse+1, -1); } } g_free(path); } } if (status.queue.state & GHB_STATE_SCANNING) { // This needs to be in scanning and working since scanning // happens fast enough that it can be missed gtk_label_set_text (work_status, _("Scanning ...")); gtk_progress_bar_set_fraction (progress, 0); } else if (status.queue.state & GHB_STATE_SCANDONE) { ghb_clear_queue_state(GHB_STATE_SCANDONE); usleep(2000000); submit_job(ud, ud->current_job); ghb_update_pending(ud); } else if (status.queue.state & GHB_STATE_PAUSED) { gtk_label_set_text (work_status, _("Paused")); } else if (status.queue.state & GHB_STATE_SEARCHING) { gchar *status_str; status_str = searching_status_string(ud, &status.queue); gtk_label_set_text (work_status, status_str); gtk_progress_bar_set_fraction (progress, status.queue.progress); g_free(status_str); } else if (status.queue.state & GHB_STATE_WORKING) { gchar *status_str; status_str = working_status_string(ud, &status.queue); gtk_label_set_text (work_status, status_str); gtk_progress_bar_set_fraction (progress, status.queue.progress); g_free(status_str); } else if (status.queue.state & GHB_STATE_WORKDONE) { gint qstatus; const gchar *status_icon; index = find_queue_job(ud->queue, status.queue.unique_id, &js); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list")); store = gtk_tree_view_get_model(treeview); if (ud->cancel_encode == GHB_CANCEL_ALL || ud->cancel_encode == GHB_CANCEL_CURRENT) status.queue.error = GHB_ERROR_CANCELED; switch( status.queue.error ) { case GHB_ERROR_NONE: gtk_label_set_text (work_status, _("Encode Done!")); qstatus = GHB_QUEUE_DONE; status_icon = "hb-complete"; break; case GHB_ERROR_CANCELED: gtk_label_set_text (work_status, _("Encode Canceled.")); qstatus = GHB_QUEUE_CANCELED; status_icon = "hb-stop"; break; case GHB_ERROR_FAIL: default: gtk_label_set_text (work_status, _("Encode Failed.")); qstatus = GHB_QUEUE_FAIL; status_icon = "hb-stop"; } if (js != NULL) { gchar *path = g_strdup_printf ("%d", index); if (gtk_tree_model_get_iter_from_string(store, &iter, path)) { gtk_tree_store_set(GTK_TREE_STORE(store), &iter, 1, status_icon, -1); } g_free(path); } gtk_progress_bar_set_fraction (progress, 1.0); ghb_clear_queue_state(GHB_STATE_WORKDONE); if (ud->job_activity_log) g_io_channel_unref(ud->job_activity_log); ud->job_activity_log = NULL; if (js) ghb_settings_set_int(js, "job_status", qstatus); if (ghb_settings_get_boolean(ud->prefs, "RemoveFinishedJobs") && status.queue.error == GHB_ERROR_NONE) { ghb_queue_remove_row(ud, index); } if (ud->cancel_encode != GHB_CANCEL_ALL && ud->cancel_encode != GHB_CANCEL_FINISH) { ud->current_job = ghb_start_next_job(ud); } else { ghb_uninhibit_gsm(); ud->current_job = NULL; gtk_widget_hide(GTK_WIDGET(progress)); } ghb_save_queue(ud->queue); ud->cancel_encode = GHB_CANCEL_NONE; } else if (status.queue.state & GHB_STATE_MUXING) { gtk_label_set_text (work_status, _("Muxing: This may take a while...")); } if (status.scan.state & GHB_STATE_WORKING) { GtkProgressBar *live_progress; live_progress = GTK_PROGRESS_BAR( GHB_WIDGET(ud->builder, "live_encode_progress")); status_str = working_status_string(ud, &status.scan); gtk_progress_bar_set_text (live_progress, status_str); gtk_progress_bar_set_fraction (live_progress, status.scan.progress); g_free(status_str); } if (status.scan.state & GHB_STATE_WORKDONE) { switch( status.scan.error ) { case GHB_ERROR_NONE: { ghb_live_encode_done(ud, TRUE); } break; default: { ghb_live_encode_done(ud, FALSE); } break; } ghb_clear_scan_state(GHB_STATE_WORKDONE); } } G_MODULE_EXPORT gboolean ghb_timer_cb(gpointer data) { signal_user_data_t *ud = (signal_user_data_t*)data; ghb_live_preview_progress(ud); ghb_backend_events(ud); if (update_default_destination) { gchar *dest, *dest_dir, *def_dest; dest = ghb_settings_get_string(ud->settings, "destination"); dest_dir = g_path_get_dirname (dest); def_dest = ghb_settings_get_string(ud->prefs, "destination_dir"); if (strcmp(dest_dir, def_dest) != 0) { ghb_settings_set_string (ud->prefs, "destination_dir", dest_dir); ghb_pref_save(ud->prefs, "destination_dir"); } g_free(dest); g_free(dest_dir); g_free(def_dest); update_default_destination = FALSE; } if (update_preview) { g_debug("Updating preview\n"); ghb_set_preview_image (ud); update_preview = FALSE; } #if !defined(_NO_UPDATE_CHECK) if (!appcast_busy) { gchar *updates; updates = ghb_settings_get_string(ud->prefs, "check_updates"); gint64 duration = 0; if (strcmp(updates, "daily") == 0) duration = 60 * 60 * 24; else if (strcmp(updates, "weekly") == 0) duration = 60 * 60 * 24 * 7; else if (strcmp(updates, "monthly") == 0) duration = 60 * 60 * 24 * 7; g_free(updates); if (duration != 0) { gint64 last; time_t tt; last = ghb_settings_get_int64(ud->prefs, "last_update_check"); time(&tt); if (last + duration < tt) { ghb_settings_set_int64(ud->prefs, "last_update_check", tt); ghb_pref_save(ud->prefs, "last_update_check"); GHB_THREAD_NEW("Update Check", (GThreadFunc)ghb_check_update, ud); } } } #endif return TRUE; } gboolean scroll_at_bottom(signal_user_data_t *ud, const char *scroll) { GtkScrolledWindow *window; GtkAdjustment *adj; double val, upper, ps; window = GTK_SCROLLED_WINDOW(GHB_WIDGET(ud->builder, scroll)); adj = gtk_scrolled_window_get_vadjustment(window); val = gtk_adjustment_get_value(adj); upper = gtk_adjustment_get_upper(adj); ps = gtk_adjustment_get_page_size(adj); return val >= upper - ps; } G_MODULE_EXPORT gboolean activity_scroll_to_bottom(signal_user_data_t *ud) { GtkScrolledWindow *window; GtkAdjustment *adj; double upper, ps; window = GTK_SCROLLED_WINDOW(GHB_WIDGET(ud->builder, "activity_scroll")); adj = gtk_scrolled_window_get_vadjustment(window); upper = gtk_adjustment_get_upper(adj); ps = gtk_adjustment_get_page_size(adj); gtk_adjustment_set_value(adj, upper - ps); return FALSE; } G_MODULE_EXPORT gboolean ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data) { gchar *text = NULL; gsize length, outlength; GtkTextView *textview; GtkTextBuffer *buffer; GtkTextIter iter; GError *gerror = NULL; GIOStatus status; signal_user_data_t *ud = (signal_user_data_t*)data; status = g_io_channel_read_line (source, &text, &length, NULL, &gerror); // Trim nils from end of text, they cause g_io_channel_write_chars to // fail with an assertion that aborts while (length > 0 && text[length-1] == 0) length--; if (text != NULL && length > 0) { gboolean bottom = FALSE; gchar *utf8_text; bottom = scroll_at_bottom(ud, "activity_scroll"); textview = GTK_TEXT_VIEW(GHB_WIDGET (ud->builder, "activity_view")); buffer = gtk_text_view_get_buffer (textview); gtk_text_buffer_get_end_iter(buffer, &iter); utf8_text = g_convert_with_fallback(text, -1, "UTF-8", "ISO-8859-1", "?", NULL, &length, NULL); if (utf8_text != NULL) { gtk_text_buffer_insert(buffer, &iter, utf8_text, -1); if (bottom) { static guint scroll_tok = 0; GSource *source = NULL; if (scroll_tok > 0) source = g_main_context_find_source_by_id(NULL, scroll_tok); if (source != NULL) { g_source_remove(scroll_tok); } scroll_tok = g_idle_add((GSourceFunc)activity_scroll_to_bottom, ud); } #if defined(_WIN32) gsize one = 1; utf8_text[length-1] = '\r'; #endif g_io_channel_write_chars (ud->activity_log, utf8_text, length, &outlength, NULL); #if defined(_WIN32) g_io_channel_write_chars (ud->activity_log, "\n", one, &one, NULL); #endif g_io_channel_flush(ud->activity_log, NULL); if (ud->job_activity_log) { g_io_channel_write_chars (ud->job_activity_log, utf8_text, length, &outlength, NULL); #if defined(_WIN32) g_io_channel_write_chars (ud->activity_log, "\n", one, &outlength, NULL); #endif g_io_channel_flush(ud->job_activity_log, NULL); } g_free(utf8_text); } } if (text != NULL) g_free(text); if (status != G_IO_STATUS_NORMAL) { // This should never happen, but if it does I would get into an // infinite loop. Returning false removes this callback. g_warning("Error while reading activity from pipe"); if (gerror != NULL) { g_warning("%s", gerror->message); g_error_free (gerror); } return FALSE; } if (gerror != NULL) g_error_free (gerror); return TRUE; } G_MODULE_EXPORT void show_activity_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window"); gtk_widget_set_visible(widget, gtk_toggle_tool_button_get_active( GTK_TOGGLE_TOOL_BUTTON(xwidget))); } G_MODULE_EXPORT void show_activity_menu_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window"); gtk_widget_set_visible(widget, TRUE); widget = GHB_WIDGET (ud->builder, "show_activity"); gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), TRUE); } G_MODULE_EXPORT gboolean activity_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud) { gtk_widget_set_visible(xwidget, FALSE); GtkWidget *widget = GHB_WIDGET (ud->builder, "show_activity"); gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE); return TRUE; } void ghb_log(gchar *log, ...) { va_list args; time_t _now; struct tm *now; gchar fmt[362]; _now = time(NULL); now = localtime( &_now ); snprintf(fmt, 362, "[%02d:%02d:%02d] gtkgui: %s\n", now->tm_hour, now->tm_min, now->tm_sec, log); va_start(args, log); vfprintf(stderr, fmt, args); va_end(args); } static void browse_url(const gchar *url) { #if defined(_WIN32) ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL); #else gboolean result; char *argv[] = {"xdg-open",NULL,NULL,NULL}; argv[1] = (gchar*)url; result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL); if (result) return; argv[0] = "gnome-open"; result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL); if (result) return; argv[0] = "kfmclient"; argv[1] = "exec"; argv[2] = "http://trac.handbrake.fr/wiki/HandBrakeGuide"; result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL); if (result) return; argv[0] = "firefox"; argv[1] = "http://trac.handbrake.fr/wiki/HandBrakeGuide"; argv[2] = NULL; result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL); #endif } #if 0 JJJ void about_web_hook(GtkAboutDialog *about, const gchar *link, gpointer data) { browse_url(link); } #endif G_MODULE_EXPORT void about_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GtkWidget *widget = GHB_WIDGET (ud->builder, "hb_about"); gchar *ver; ver = g_strdup_printf("%s (%s)", HB_PROJECT_VERSION, HB_PROJECT_BUILD_ARCH); #if 0 JJJ gtk_about_dialog_set_url_hook(about_web_hook, NULL, NULL); #endif gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(widget), ver); g_free(ver); gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(widget), HB_PROJECT_URL_WEBSITE); gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(widget), HB_PROJECT_URL_WEBSITE); gtk_widget_show (widget); } G_MODULE_EXPORT void guide_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud) { browse_url("http://trac.handbrake.fr/wiki/HandBrakeGuide"); } G_MODULE_EXPORT void hb_about_response_cb(GtkWidget *widget, gint response, signal_user_data_t *ud) { gtk_widget_hide (widget); } G_MODULE_EXPORT void show_queue_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GtkWidget *widget; GtkStack *stack; stack = GTK_STACK(GHB_WIDGET(ud->builder, "QueueStack")); if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(xwidget))) widget = GHB_WIDGET(ud->builder, "queue_tab"); else widget = GHB_WIDGET(ud->builder, "settings_tab"); gtk_stack_set_visible_child(stack, widget); } G_MODULE_EXPORT void show_queue_menu_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GtkWidget *widget = GHB_WIDGET(ud->builder, "show_queue"); gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), TRUE); } G_MODULE_EXPORT void show_presets_toggled_cb(GtkWidget *widget, signal_user_data_t *ud) { GtkWidget *frame; GtkWindow *hb_window; g_debug("show_presets_clicked_cb ()"); frame = GHB_WIDGET (ud->builder, "presets_frame"); ghb_widget_to_setting(ud->prefs, widget); if (ghb_settings_get_boolean(ud->prefs, "show_presets")) { gtk_widget_show_now(frame); } else { gtk_widget_hide(frame); hb_window = GTK_WINDOW(GHB_WIDGET (ud->builder, "hb_window")); gtk_window_resize(hb_window, 16, 16); } ghb_pref_save(ud->prefs, "show_presets"); } static void chapter_refresh_list_row_ui( GtkTreeModel *tm, GtkTreeIter *ti, GValue *chapter_list, const hb_title_t *title, int index) { gchar *chapter, *s_duration, *s_start; gint hh, mm, ss; gint64 duration, start; // Update row with settings data g_debug("Updating chapter row ui"); chapter = ghb_value_string(ghb_array_get_nth(chapter_list, index)); duration = ghb_get_chapter_duration(title, index) / 90000; break_duration(duration, &hh, &mm, &ss); s_duration = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss); start = ghb_get_chapter_start(title, index) / 90000; break_duration(start, &hh, &mm, &ss); s_start = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss); gtk_list_store_set(GTK_LIST_STORE(tm), ti, 0, index+1, 1, s_start, 2, s_duration, 3, chapter, 4, TRUE, -1); g_free(chapter); g_free(s_duration); g_free(s_start); } static void ghb_clear_chapter_list_ui(GtkBuilder *builder) { GtkTreeView *tv; GtkListStore *ts; tv = GTK_TREE_VIEW(GHB_WIDGET(builder, "chapters_list")); ts = GTK_LIST_STORE(gtk_tree_view_get_model(tv)); gtk_list_store_clear(ts); } static void chapter_refresh_list_ui(signal_user_data_t *ud) { GValue *chapter_list; gint ii, count, tm_count; GtkTreeView *tv; GtkTreeModel *tm; GtkTreeIter ti; int title_id, titleindex; const hb_title_t *title; tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list")); tm = gtk_tree_view_get_model(tv); tm_count = gtk_tree_model_iter_n_children(tm, NULL); title_id = ghb_settings_get_int(ud->settings, "title"); title = ghb_lookup_title(title_id, &titleindex); chapter_list = ghb_settings_get_value(ud->settings, "chapter_list"); count = ghb_array_len(chapter_list); if (count != tm_count) { ghb_clear_chapter_list_ui(ud->builder); for (ii = 0; ii < count; ii++) { gtk_list_store_append(GTK_LIST_STORE(tm), &ti); } } for (ii = 0; ii < count; ii++) { gtk_tree_model_iter_nth_child(tm, &ti, NULL, ii); chapter_refresh_list_row_ui(tm, &ti, chapter_list, title, ii); } } void ghb_chapter_list_refresh_all(signal_user_data_t *ud) { chapter_refresh_list_ui(ud); } static void update_chapter_list_settings(GValue *settings) { GValue *chapters; gint title_id, titleindex; const hb_title_t *title; g_debug("update_chapter_list_settings ()"); title_id = ghb_settings_get_int(settings, "title"); title = ghb_lookup_title(title_id, &titleindex); chapters = ghb_get_chapters(title); if (chapters) ghb_settings_take_value(settings, "chapter_list", chapters); } static gint chapter_edit_key = 0; G_MODULE_EXPORT gboolean chapter_keypress_cb( GhbCellRendererText *cell, GdkEventKey *event, signal_user_data_t *ud) { chapter_edit_key = event->keyval; return FALSE; } G_MODULE_EXPORT void chapter_edited_cb( GhbCellRendererText *cell, gchar *path, gchar *text, signal_user_data_t *ud) { GtkTreePath *treepath; GtkListStore *store; GtkTreeView *treeview; GtkTreeIter iter; gint index; gint *pi; gint row; g_debug("chapter_edited_cb ()"); g_debug("path (%s)", path); g_debug("text (%s)", text); treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list")); store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview)); treepath = gtk_tree_path_new_from_string (path); pi = gtk_tree_path_get_indices(treepath); row = pi[0]; gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath); gtk_list_store_set(store, &iter, 3, text, 4, TRUE, -1); gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &index, -1); const GValue *chapters; GValue *chapter; chapters = ghb_settings_get_value(ud->settings, "chapter_list"); chapter = ghb_array_get_nth(chapters, index-1); g_value_set_string(chapter, text); if ((chapter_edit_key == GDK_KEY_Return || chapter_edit_key == GDK_KEY_Down) && gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)) { GtkTreeViewColumn *column; gtk_tree_path_next(treepath); // When a cell has been edited, I want to advance to the // next cell and start editing it automaitcally. // Unfortunately, we may not be in a state here where // editing is allowed. This happens when the user selects // a new cell with the mouse instead of just hitting enter. // Some kind of Gtk quirk. widget_editable==NULL assertion. // Editing is enabled again once the selection event has been // processed. So I'm queueing up a callback to be called // when things go idle. There, I will advance to the next // cell and initiate editing. // // Now, you might be asking why I don't catch the keypress // event and determine what action to take based on that. // The Gtk developers in their infinite wisdom have made the // actual GtkEdit widget being used a private member of // GtkCellRendererText, so it can not be accessed to hang a // signal handler off of. And they also do not propagate the // keypress signals in any other way. So that information is lost. //g_idle_add((GSourceFunc)next_cell, ud); // // Keeping the above comment for posterity. // I got industrious and made my own CellTextRendererText that // passes on the key-press-event. So now I have much better // control of this. column = gtk_tree_view_get_column(treeview, 3); gtk_tree_view_set_cursor(treeview, treepath, column, TRUE); } else if (chapter_edit_key == GDK_KEY_Up && row > 0) { GtkTreeViewColumn *column; gtk_tree_path_prev(treepath); column = gtk_tree_view_get_column(treeview, 3); gtk_tree_view_set_cursor(treeview, treepath, column, TRUE); } gtk_tree_path_free (treepath); } void debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data) { signal_user_data_t *ud = (signal_user_data_t*)data; if (ud->debug) { printf("%s: %s\n", domain, msg); } } void warn_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data) { printf("%s: %s\n", domain, msg); } void ghb_hbfd(signal_user_data_t *ud, gboolean hbfd) { GtkWidget *widget; g_debug("ghb_hbfd"); widget = GHB_WIDGET(ud->builder, "queue_pause1"); gtk_widget_set_visible(widget, !hbfd); widget = GHB_WIDGET(ud->builder, "queue_add"); gtk_widget_set_visible(widget, !hbfd); widget = GHB_WIDGET(ud->builder, "show_queue"); gtk_widget_set_visible(widget, !hbfd); widget = GHB_WIDGET(ud->builder, "show_activity"); gtk_widget_set_visible(widget, !hbfd); widget = GHB_WIDGET(ud->builder, "chapter_box"); gtk_widget_set_visible(widget, !hbfd); widget = GHB_WIDGET(ud->builder, "container_box"); gtk_widget_set_visible(widget, !hbfd); widget = GHB_WIDGET(ud->builder, "SettingsStackSwitcher"); gtk_widget_set_visible(widget, !hbfd); widget = GHB_WIDGET(ud->builder, "SettingsStack"); gtk_widget_set_visible(widget, !hbfd); widget = GHB_WIDGET(ud->builder, "presets_save"); gtk_widget_set_visible(widget, !hbfd); widget = GHB_WIDGET(ud->builder, "presets_remove"); gtk_widget_set_visible(widget, !hbfd); widget = GHB_WIDGET (ud->builder, "hb_window"); gtk_window_resize(GTK_WINDOW(widget), 16, 16); } G_MODULE_EXPORT void hbfd_toggled_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("hbfd_toggled_cb"); ghb_widget_to_setting(ud->prefs, widget); gboolean hbfd = ghb_settings_get_boolean(ud->prefs, "hbfd"); ghb_hbfd(ud, hbfd); ghb_pref_save(ud->prefs, "hbfd"); } G_MODULE_EXPORT void advanced_video_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("advanced_video_changed_cb"); ghb_widget_to_setting(ud->prefs, widget); const gchar *name = ghb_get_setting_key(widget); ghb_pref_set(ud->prefs, name); ghb_show_hide_advanced_video(ud); } G_MODULE_EXPORT void pref_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting (ud->prefs, widget); // FIXME? ghb_check_dependency(ud, widget, NULL); const gchar *name = ghb_get_setting_key(widget); ghb_pref_set(ud->prefs, name); gint preview_count; preview_count = ghb_settings_get_int(ud->prefs, "preview_count"); widget = GHB_WIDGET(ud->builder, "preview_frame"); gtk_range_set_range(GTK_RANGE(widget), 1, preview_count); } G_MODULE_EXPORT void use_m4v_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("use_m4v_changed_cb"); ghb_widget_to_setting (ud->prefs, widget); ghb_check_dependency(ud, widget, NULL); const gchar *name = ghb_get_setting_key(widget); ghb_pref_set(ud->prefs, name); ghb_update_destination_extension(ud); } G_MODULE_EXPORT void vqual_granularity_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("vqual_granularity_changed_cb"); ghb_widget_to_setting (ud->prefs, widget); ghb_check_dependency(ud, widget, NULL); const gchar *name = ghb_get_setting_key(widget); ghb_pref_set(ud->prefs, name); float val, vqmin, vqmax, step, page; int inverted, digits; ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted); val = ghb_settings_get_double(ud->settings, "VideoQualitySlider"); ghb_scale_configure(ud, "VideoQualitySlider", val, vqmin, vqmax, step, page, digits, inverted); } G_MODULE_EXPORT void tweaks_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("tweaks_changed_cb"); ghb_widget_to_setting (ud->prefs, widget); const gchar *name = ghb_get_setting_key(widget); ghb_pref_set(ud->prefs, name); } G_MODULE_EXPORT void hbfd_feature_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { g_debug("hbfd_feature_changed_cb"); ghb_widget_to_setting (ud->prefs, widget); const gchar *name = ghb_get_setting_key(widget); ghb_pref_set(ud->prefs, name); gboolean hbfd = ghb_settings_get_boolean(ud->prefs, "hbfd_feature"); if (hbfd) { const GValue *val; val = ghb_settings_get_value(ud->prefs, "hbfd"); ghb_ui_settings_update(ud, ud->prefs, "hbfd", val); } widget = GHB_WIDGET(ud->builder, "hbfd"); gtk_widget_set_visible(widget, hbfd); } gboolean ghb_file_menu_add_dvd(signal_user_data_t *ud) { GList *link, *drives; static GList *dvd_items = NULL; g_debug("ghb_file_menu_add_dvd()"); GtkMenu *menu = GTK_MENU(GHB_WIDGET(ud->builder, "file_submenu")); // Clear previous dvd items from list link = dvd_items; while (link != NULL) { GtkWidget * widget = GTK_WIDGET(link->data); // widget_destroy automatically removes widget from container. gtk_widget_destroy(widget); link = link->next; } g_list_free(dvd_items); dvd_items = NULL; int pos = 5; link = drives = dvd_device_list(); if (drives != NULL) { GtkWidget *widget = gtk_separator_menu_item_new(); dvd_items = g_list_append(dvd_items, (gpointer)widget); gtk_menu_shell_insert(GTK_MENU_SHELL(menu), widget, pos++); gtk_widget_set_visible(widget, TRUE); while (link != NULL) { GtkWidget *widget; gchar *drive = get_dvd_device_name(link->data); gchar *name = get_dvd_volume_name(link->data); widget = gtk_menu_item_new_with_label(name); gtk_buildable_set_name(GTK_BUILDABLE(widget), drive); gtk_widget_set_tooltip_text(widget, _("Scan this DVD source")); dvd_items = g_list_append(dvd_items, (gpointer)widget); gtk_menu_shell_insert(GTK_MENU_SHELL(menu), widget, pos++); gtk_widget_set_visible(widget, TRUE); // Connect signal to action (menu item) g_signal_connect(widget, "activate", (GCallback)dvd_source_activate_cb, ud); g_free(name); g_free(drive); free_drive(link->data); link = link->next; } g_list_free(drives); } return FALSE; } gboolean ghb_is_cd(GDrive *gd); static GList* dvd_device_list() { GList *dvd_devices = NULL; #if defined(_WIN32) gint ii, drives; gchar drive[5]; strcpy(drive, "A:" G_DIR_SEPARATOR_S); drives = GetLogicalDrives(); for (ii = 0; ii < 26; ii++) { if (drives & 0x01) { guint dtype; drive[0] = 'A' + ii; dtype = GetDriveType(drive); if (dtype == DRIVE_CDROM) { dvd_devices = g_list_append(dvd_devices, (gpointer)g_strdup(drive)); } } drives >>= 1; } #else GVolumeMonitor *gvm; GList *drives, *link; gvm = g_volume_monitor_get (); drives = g_volume_monitor_get_connected_drives (gvm); link = drives; while (link != NULL) { GDrive *gd; gd = (GDrive*)link->data; if (ghb_is_cd(gd)) { dvd_devices = g_list_append(dvd_devices, gd); } else g_object_unref (gd); link = link->next; } g_list_free(drives); #endif return dvd_devices; } #if defined(__linux__) static GUdevClient *udev_ctx = NULL; #endif gboolean ghb_is_cd(GDrive *gd) { #if defined(__linux__) gchar *device; GUdevDevice *udd; if (udev_ctx == NULL) return FALSE; device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); if (device == NULL) return FALSE; udd = g_udev_client_query_by_device_file(udev_ctx, device); g_free(device); if (udd == NULL) { g_message("udev: Failed to lookup device %s", device); return FALSE; } gint val; val = g_udev_device_get_property_as_int(udd, "ID_CDROM_DVD"); g_object_unref(udd); if (val == 1) return TRUE; return FALSE; #else return FALSE; #endif } void ghb_udev_init() { #if defined(__linux__) udev_ctx = g_udev_client_new(NULL); #endif } #if defined(_WIN32) static void handle_media_change(const gchar *device, gboolean insert, signal_user_data_t *ud) { guint dtype; static gint ins_count = 0; static gint rem_count = 0; // The media change event in windows bounces around a bit // so I debounce it here // DVD insertion detected. Scan it. dtype = GetDriveType(device); if (dtype != DRIVE_CDROM) return; if (insert) { rem_count = 0; ins_count++; if (ins_count == 2) { GHB_THREAD_NEW("Cache Volume Names", (GThreadFunc)ghb_cache_volnames, ud); if (ghb_settings_get_boolean(ud->prefs, "AutoScan") && ud->current_dvd_device != NULL && strcmp(device, ud->current_dvd_device) == 0) { show_scan_progress(ud); update_source_label(ud, device); gint preview_count; preview_count = ghb_settings_get_int(ud->prefs, "preview_count"); ghb_settings_set_string(ud->globals, "scan_source", device); start_scan(ud, device, 0, preview_count); } } } else { ins_count = 0; rem_count++; if (rem_count == 2) { GHB_THREAD_NEW("Cache Volume Names", (GThreadFunc)ghb_cache_volnames, ud); if (ud->current_dvd_device != NULL && strcmp(device, ud->current_dvd_device) == 0) { ghb_hb_cleanup(TRUE); prune_logs(ud); ghb_settings_set_string(ud->globals, "scan_source", "/dev/null"); start_scan(ud, "/dev/null", 0, 1); } } } } static gchar FindDriveFromMask(ULONG unitmask) { gchar cc; for (cc = 0; cc < 26; cc++) { if (unitmask & 0x01) return 'A' + cc; unitmask >>= 1; } return 0; } void wm_drive_changed(MSG *msg, signal_user_data_t *ud) { PDEV_BROADCAST_HDR bch = (PDEV_BROADCAST_HDR)msg->lParam; gchar drive[4]; g_strlcpy(drive, "A:" G_DIR_SEPARATOR_S, 4); switch (msg->wParam) { case DBT_DEVICEARRIVAL: { if (bch->dbch_devicetype == DBT_DEVTYP_VOLUME) { PDEV_BROADCAST_VOLUME bcv = (PDEV_BROADCAST_VOLUME)bch; if (bcv->dbcv_flags & DBTF_MEDIA) { drive[0] = FindDriveFromMask(bcv->dbcv_unitmask); handle_media_change(drive, TRUE, ud); } } } break; case DBT_DEVICEREMOVECOMPLETE: { if (bch->dbch_devicetype == DBT_DEVTYP_VOLUME) { PDEV_BROADCAST_VOLUME bcv = (PDEV_BROADCAST_VOLUME)bch; if (bcv->dbcv_flags & DBTF_MEDIA) { drive[0] = FindDriveFromMask(bcv->dbcv_unitmask); handle_media_change(drive, FALSE, ud); } } } break; default: ; } } #else G_MODULE_EXPORT void drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud) { gchar *device; gint state; g_debug("drive_changed_cb()"); GHB_THREAD_NEW("Cache Volume Names", (GThreadFunc)ghb_cache_volnames, ud); state = ghb_get_scan_state(); device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); if (ud->current_dvd_device == NULL || strcmp(device, ud->current_dvd_device) != 0 || state != GHB_STATE_IDLE ) { return; } if (g_drive_has_media(gd)) { if (ghb_settings_get_boolean(ud->prefs, "AutoScan")) { show_scan_progress(ud); update_source_label(ud, device); gint preview_count; preview_count = ghb_settings_get_int(ud->prefs, "preview_count"); ghb_settings_set_string(ud->globals, "scan_source", device); start_scan(ud, device, 0, preview_count); } } else { ghb_hb_cleanup(TRUE); prune_logs(ud); ghb_settings_set_string(ud->globals, "scan_source", "/dev/null"); start_scan(ud, "/dev/null", 0, 1); } } #endif #if !defined(_WIN32) #define GPM_DBUS_PM_SERVICE "org.freedesktop.PowerManagement" #define GPM_DBUS_PM_PATH "/org/freedesktop/PowerManagement" #define GPM_DBUS_PM_INTERFACE "org.freedesktop.PowerManagement" #define GPM_DBUS_INHIBIT_PATH "/org/freedesktop/PowerManagement/Inhibit" #define GPM_DBUS_INHIBIT_INTERFACE "org.freedesktop.PowerManagement.Inhibit" static gboolean gpm_inhibited = FALSE; static guint gpm_cookie = -1; #endif static gboolean ghb_can_suspend_gpm() { gboolean can_suspend = FALSE; #if !defined(_WIN32) DBusGConnection *conn; DBusGProxy *proxy; GError *error = NULL; gboolean res; g_debug("ghb_can_suspend_gpm()"); conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (error != NULL) { g_warning("DBUS cannot connect: %s", error->message); g_error_free(error); return FALSE; } proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE); if (proxy == NULL) { g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); dbus_g_connection_unref(conn); return FALSE; } res = dbus_g_proxy_call(proxy, "CanSuspend", &error, G_TYPE_INVALID, G_TYPE_BOOLEAN, &can_suspend, G_TYPE_INVALID); if (!res) { if (error != NULL) { g_warning("CanSuspend failed: %s", error->message); g_error_free(error); } else g_warning("CanSuspend failed"); // Try to shutdown anyway can_suspend = TRUE; } g_object_unref(G_OBJECT(proxy)); dbus_g_connection_unref(conn); #endif return can_suspend; } static void ghb_suspend_gpm() { #if !defined(_WIN32) DBusGConnection *conn; DBusGProxy *proxy; GError *error = NULL; gboolean res; g_debug("ghb_suspend_gpm()"); conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (error != NULL) { g_warning("DBUS cannot connect: %s", error->message); g_error_free(error); return; } proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE); if (proxy == NULL) { g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); dbus_g_connection_unref(conn); return; } res = dbus_g_proxy_call(proxy, "Suspend", &error, G_TYPE_INVALID, G_TYPE_INVALID); if (!res) { if (error != NULL) { g_warning("Suspend failed: %s", error->message); g_error_free(error); } else g_warning("Suspend failed"); } g_object_unref(G_OBJECT(proxy)); dbus_g_connection_unref(conn); #endif } #if !defined(_WIN32) static gboolean ghb_can_shutdown_gpm() { gboolean can_shutdown = FALSE; DBusGConnection *conn; DBusGProxy *proxy; GError *error = NULL; gboolean res; g_debug("ghb_can_shutdown_gpm()"); conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (error != NULL) { g_warning("DBUS cannot connect: %s", error->message); g_error_free(error); return FALSE; } proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE); if (proxy == NULL) { g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); dbus_g_connection_unref(conn); return FALSE; } res = dbus_g_proxy_call(proxy, "CanShutdown", &error, G_TYPE_INVALID, G_TYPE_BOOLEAN, &can_shutdown, G_TYPE_INVALID); if (!res) { if (error != NULL) { g_warning("CanShutdown failed: %s", error->message); g_error_free(error); } else g_warning("CanShutdown failed"); // Try to shutdown anyway can_shutdown = TRUE; } g_object_unref(G_OBJECT(proxy)); dbus_g_connection_unref(conn); return can_shutdown; } #endif #if !defined(_WIN32) static void ghb_shutdown_gpm() { DBusGConnection *conn; DBusGProxy *proxy; GError *error = NULL; gboolean res; g_debug("ghb_shutdown_gpm()"); conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (error != NULL) { g_warning("DBUS cannot connect: %s", error->message); g_error_free(error); return; } proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE); if (proxy == NULL) { g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); dbus_g_connection_unref(conn); return; } res = dbus_g_proxy_call(proxy, "Shutdown", &error, G_TYPE_INVALID, G_TYPE_INVALID); if (!res) { if (error != NULL) { g_warning("Shutdown failed: %s", error->message); g_error_free(error); } else g_warning("Shutdown failed"); } g_object_unref(G_OBJECT(proxy)); dbus_g_connection_unref(conn); } #endif void ghb_inhibit_gpm() { #if !defined(_WIN32) DBusGConnection *conn; DBusGProxy *proxy; GError *error = NULL; gboolean res; if (gpm_inhibited) { // Already inhibited return; } g_debug("ghb_inhibit_gpm()"); conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (error != NULL) { g_warning("DBUS cannot connect: %s", error->message); g_error_free(error); return; } proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE); if (proxy == NULL) { g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); dbus_g_connection_unref(conn); return; } res = dbus_g_proxy_call(proxy, "Inhibit", &error, G_TYPE_STRING, "ghb", G_TYPE_STRING, "Encoding", G_TYPE_INVALID, G_TYPE_UINT, &gpm_cookie, G_TYPE_INVALID); gpm_inhibited = TRUE; if (!res) { if (error != NULL) { g_warning("Inhibit failed: %s", error->message); g_error_free(error); gpm_cookie = -1; } else g_warning("Inhibit failed"); gpm_cookie = -1; gpm_inhibited = FALSE; } g_object_unref(G_OBJECT(proxy)); dbus_g_connection_unref(conn); #endif } void ghb_uninhibit_gpm() { #if !defined(_WIN32) DBusGConnection *conn; DBusGProxy *proxy; GError *error = NULL; gboolean res; g_debug("ghb_uninhibit_gpm() gpm_cookie %u", gpm_cookie); if (!gpm_inhibited) { // Not inhibited return; } conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (error != NULL) { g_warning("DBUS cannot connect: %s", error->message); g_error_free(error); return; } proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE); if (proxy == NULL) { g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); dbus_g_connection_unref(conn); return; } res = dbus_g_proxy_call(proxy, "UnInhibit", &error, G_TYPE_UINT, gpm_cookie, G_TYPE_INVALID, G_TYPE_INVALID); if (!res) { if (error != NULL) { g_warning("UnInhibit failed: %s", error->message); g_error_free(error); } else g_warning("UnInhibit failed"); } gpm_inhibited = FALSE; dbus_g_connection_unref(conn); g_object_unref(G_OBJECT(proxy)); #endif } #if !defined(_WIN32) // For inhibit and shutdown #define GPM_DBUS_SM_SERVICE "org.gnome.SessionManager" #define GPM_DBUS_SM_PATH "/org/gnome/SessionManager" #define GPM_DBUS_SM_INTERFACE "org.gnome.SessionManager" #endif static gboolean ghb_can_shutdown_gsm() { gboolean can_shutdown = FALSE; #if !defined(_WIN32) DBusGConnection *conn; DBusGProxy *proxy; GError *error = NULL; gboolean res; g_debug("ghb_can_shutdown_gpm()"); conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (error != NULL) { g_warning("DBUS cannot connect: %s", error->message); g_error_free(error); return FALSE; } proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE, GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE); if (proxy == NULL) { g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE); dbus_g_connection_unref(conn); return FALSE; } res = dbus_g_proxy_call(proxy, "CanShutdown", &error, G_TYPE_INVALID, G_TYPE_BOOLEAN, &can_shutdown, G_TYPE_INVALID); g_object_unref(G_OBJECT(proxy)); dbus_g_connection_unref(conn); if (!res) { if (error != NULL) { g_error_free(error); } // Try to shutdown anyway can_shutdown = TRUE; // Try the gpm version return ghb_can_shutdown_gpm(); } #endif return can_shutdown; } static void ghb_shutdown_gsm() { #if !defined(_WIN32) DBusGConnection *conn; DBusGProxy *proxy; GError *error = NULL; gboolean res; g_debug("ghb_shutdown_gpm()"); conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (error != NULL) { g_warning("DBUS cannot connect: %s", error->message); g_error_free(error); return; } proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE, GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE); if (proxy == NULL) { g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE); dbus_g_connection_unref(conn); return; } res = dbus_g_proxy_call(proxy, "Shutdown", &error, G_TYPE_INVALID, G_TYPE_INVALID); g_object_unref(G_OBJECT(proxy)); dbus_g_connection_unref(conn); if (!res) { if (error != NULL) { g_error_free(error); } // Try the gpm version ghb_shutdown_gpm(); } #endif } void ghb_inhibit_gsm(signal_user_data_t *ud) { #if !defined(_WIN32) DBusGConnection *conn; DBusGProxy *proxy; GError *error = NULL; gboolean res; guint xid; GtkWidget *widget; if (gpm_inhibited) { // Already inhibited return; } g_debug("ghb_inhibit_gsm()"); conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (error != NULL) { g_warning("DBUS cannot connect: %s", error->message); g_error_free(error); return; } proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE, GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE); if (proxy == NULL) { g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE); dbus_g_connection_unref(conn); return; } widget = GHB_WIDGET(ud->builder, "hb_window"); xid = GDK_WINDOW_XID(gtk_widget_get_window(widget)); res = dbus_g_proxy_call(proxy, "Inhibit", &error, G_TYPE_STRING, "ghb", G_TYPE_UINT, xid, G_TYPE_STRING, "Encoding", G_TYPE_UINT, 1 | 4, G_TYPE_INVALID, G_TYPE_UINT, &gpm_cookie, G_TYPE_INVALID); gpm_inhibited = TRUE; g_object_unref(G_OBJECT(proxy)); dbus_g_connection_unref(conn); if (!res) { if (error != NULL) { g_error_free(error); gpm_cookie = -1; } gpm_cookie = -1; gpm_inhibited = FALSE; // Try the gpm version ghb_inhibit_gpm(); } #endif } void ghb_uninhibit_gsm() { #if !defined(_WIN32) DBusGConnection *conn; DBusGProxy *proxy; GError *error = NULL; gboolean res; g_debug("ghb_uninhibit_gsm() gpm_cookie %u", gpm_cookie); if (!gpm_inhibited) { // Not inhibited return; } conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (error != NULL) { g_warning("DBUS cannot connect: %s", error->message); g_error_free(error); return; } proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE, GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE); if (proxy == NULL) { g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE); dbus_g_connection_unref(conn); return; } res = dbus_g_proxy_call(proxy, "Uninhibit", &error, G_TYPE_UINT, gpm_cookie, G_TYPE_INVALID, G_TYPE_INVALID); dbus_g_connection_unref(conn); g_object_unref(G_OBJECT(proxy)); if (!res) { if (error != NULL) { g_error_free(error); } ghb_uninhibit_gpm(); } gpm_inhibited = FALSE; #endif } G_MODULE_EXPORT gboolean tweak_setting_cb( GtkWidget *widget, GdkEventButton *event, signal_user_data_t *ud) { const gchar *name; gchar *tweak_name; gboolean ret = FALSE; gboolean allow_tweaks; g_debug("press %d %d", event->type, event->button); allow_tweaks = ghb_settings_get_boolean(ud->prefs, "allow_tweaks"); if (allow_tweaks && event->type == GDK_BUTTON_PRESS && event->button == 3) { // Its a right mouse click GtkWidget *dialog; GtkEntry *entry; GtkResponseType response; gchar *tweak = NULL; name = ghb_get_setting_key(widget); if (g_str_has_prefix(name, "tweak_")) { tweak_name = g_strdup(name); } else { tweak_name = g_strdup_printf("tweak_%s", name); } tweak = ghb_settings_get_string (ud->settings, tweak_name); dialog = GHB_WIDGET(ud->builder, "tweak_dialog"); gtk_window_set_title(GTK_WINDOW(dialog), tweak_name); entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "tweak_setting")); if (tweak) { gtk_entry_set_text(entry, tweak); g_free(tweak); } response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); if (response == GTK_RESPONSE_OK) { tweak = (gchar*)gtk_entry_get_text(entry); if (ghb_validate_filter_string(tweak, -1)) ghb_settings_set_string(ud->settings, tweak_name, tweak); else { gchar *message; message = g_strdup_printf( _("Invalid Settings:\n%s"), tweak); ghb_message_dialog(GTK_MESSAGE_ERROR, message, _("Cancel"), NULL); g_free(message); } } g_free(tweak_name); ret = TRUE; } return ret; } G_MODULE_EXPORT gboolean easter_egg_cb( GtkWidget *widget, GdkEventButton *event, signal_user_data_t *ud) { g_debug("press %d %d", event->type, event->button); if (event->type == GDK_3BUTTON_PRESS && event->button == 1) { // Its a tripple left mouse button click GtkWidget *widget; widget = GHB_WIDGET(ud->builder, "allow_tweaks"); gtk_widget_show(widget); widget = GHB_WIDGET(ud->builder, "hbfd_feature"); gtk_widget_show(widget); } else if (event->type == GDK_BUTTON_PRESS && event->button == 1) { GtkWidget *widget; widget = GHB_WIDGET(ud->builder, "allow_tweaks"); gtk_widget_hide(widget); widget = GHB_WIDGET(ud->builder, "hbfd_feature"); gtk_widget_hide(widget); } return FALSE; } G_MODULE_EXPORT gchar* format_deblock_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud) { if (val < 5.0) { return g_strdup_printf(_("Off")); } else { return g_strdup_printf("%d", (gint)val); } } G_MODULE_EXPORT gchar* format_vquality_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud) { gint vcodec; const char *vqname; vcodec = ghb_settings_video_encoder_codec(ud->settings, "VideoEncoder"); vqname = hb_video_quality_get_name(vcodec); switch (vcodec) { case HB_VCODEC_FFMPEG_MPEG4: case HB_VCODEC_FFMPEG_MPEG2: case HB_VCODEC_FFMPEG_VP8: case HB_VCODEC_THEORA: { return g_strdup_printf("%s: %d", vqname, (int)val); } break; case HB_VCODEC_X264: { if (val == 0.0) { return g_strdup_printf(_("%s: %.4g (Warning: lossless)"), vqname, val); } } // Falls through to default default: { return g_strdup_printf("%s: %.4g", vqname, val); } break; } } static void process_appcast(signal_user_data_t *ud) { gchar *description = NULL, *build = NULL, *version = NULL, *msg; #if !defined(_WIN32) && !defined(_NO_UPDATE_CHECK) GtkWidget *window; static GtkWidget *html = NULL; #endif GtkWidget *dialog, *label; gint response, ibuild = 0, skip; if (ud->appcast == NULL || ud->appcast_len < 15 || strncmp(&(ud->appcast[9]), "200 OK", 6)) { goto done; } ghb_appcast_parse(ud->appcast, &description, &build, &version); if (build) ibuild = g_strtod(build, NULL); skip = ghb_settings_get_int(ud->prefs, "update_skip_version"); if (description == NULL || build == NULL || version == NULL || ibuild <= hb_get_build(NULL) || skip == ibuild) { goto done; } msg = g_strdup_printf(_("HandBrake %s/%s is now available (you have %s/%d)."), version, build, hb_get_version(NULL), hb_get_build(NULL)); label = GHB_WIDGET(ud->builder, "update_message"); gtk_label_set_text(GTK_LABEL(label), msg); #if !defined(_WIN32) && !defined(_NO_UPDATE_CHECK) if (html == NULL) { html = webkit_web_view_new(); window = GHB_WIDGET(ud->builder, "update_scroll"); gtk_container_add(GTK_CONTAINER(window), html); // Show it gtk_widget_set_size_request(html, 420, 240); gtk_widget_show(html); } webkit_web_view_open(WEBKIT_WEB_VIEW(html), description); #endif dialog = GHB_WIDGET(ud->builder, "update_dialog"); response = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(dialog); if (response == GTK_RESPONSE_OK) { // Skip ghb_settings_set_int(ud->prefs, "update_skip_version", ibuild); ghb_pref_save(ud->prefs, "update_skip_version"); } g_free(msg); done: if (description) g_free(description); if (build) g_free(build); if (version) g_free(version); g_free(ud->appcast); ud->appcast_len = 0; ud->appcast = NULL; appcast_busy = FALSE; } void ghb_net_close(GIOChannel *ioc) { gint fd; g_debug("ghb_net_close"); if (ioc == NULL) return; fd = g_io_channel_unix_get_fd(ioc); close(fd); g_io_channel_unref(ioc); } G_MODULE_EXPORT gboolean ghb_net_recv_cb(GIOChannel *ioc, GIOCondition cond, gpointer data) { gchar buf[2048]; gsize len; GError *gerror = NULL; GIOStatus status; g_debug("ghb_net_recv_cb"); signal_user_data_t *ud = (signal_user_data_t*)data; status = g_io_channel_read_chars (ioc, buf, 2048, &len, &gerror); if ((status == G_IO_STATUS_NORMAL || status == G_IO_STATUS_EOF) && len > 0) { gint new_len = ud->appcast_len + len; ud->appcast = g_realloc(ud->appcast, new_len + 1); memcpy(&(ud->appcast[ud->appcast_len]), buf, len); ud->appcast_len = new_len; } if (status == G_IO_STATUS_EOF) { if ( ud->appcast != NULL ) { ud->appcast[ud->appcast_len] = 0; } ghb_net_close(ioc); process_appcast(ud); return FALSE; } return TRUE; } GIOChannel* ghb_net_open(signal_user_data_t *ud, gchar *address, gint port) { GIOChannel *ioc; gint fd; struct sockaddr_in sock; struct hostent * host; g_debug("ghb_net_open"); if( !( host = gethostbyname( address ) ) ) { g_warning( "gethostbyname failed (%s)", address ); appcast_busy = FALSE; return NULL; } memset( &sock, 0, sizeof( struct sockaddr_in ) ); sock.sin_family = host->h_addrtype; sock.sin_port = htons( port ); memcpy( &sock.sin_addr, host->h_addr, host->h_length ); fd = socket(host->h_addrtype, SOCK_STREAM, 0); if( fd < 0 ) { g_debug( "socket failed" ); appcast_busy = FALSE; return NULL; } if(connect(fd, (struct sockaddr*)&sock, sizeof(struct sockaddr_in )) < 0 ) { g_debug( "connect failed" ); appcast_busy = FALSE; return NULL; } ioc = g_io_channel_unix_new(fd); g_io_channel_set_encoding (ioc, NULL, NULL); g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL); g_io_add_watch (ioc, G_IO_IN, ghb_net_recv_cb, (gpointer)ud ); return ioc; } gpointer ghb_check_update(signal_user_data_t *ud) { gchar *query; gsize len; GIOChannel *ioc; GError *gerror = NULL; GRegex *regex; GMatchInfo *mi; gchar *host, *appcast; g_debug("ghb_check_update"); appcast_busy = TRUE; regex = g_regex_new("^http://(.+)/(.+)$", 0, 0, NULL); if (!g_regex_match(regex, HB_PROJECT_URL_APPCAST, 0, &mi)) { return NULL; } host = g_match_info_fetch(mi, 1); appcast = g_match_info_fetch(mi, 2); if (host == NULL || appcast == NULL) return NULL; query = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", appcast, host); ioc = ghb_net_open(ud, host, 80); if (ioc == NULL) goto free_resources; g_io_channel_write_chars(ioc, query, strlen(query), &len, &gerror); g_io_channel_flush(ioc, &gerror); free_resources: g_free(query); g_free(host); g_free(appcast); g_match_info_free(mi); g_regex_unref(regex); return NULL; } G_MODULE_EXPORT gboolean hb_visibility_event_cb( GtkWidget *widget, GdkEventVisibility *vs, signal_user_data_t *ud) { ud->hb_visibility = vs->state; return FALSE; } G_MODULE_EXPORT void show_hide_toggle_cb(GtkWidget *xwidget, signal_user_data_t *ud) { GtkWindow *window; GdkWindowState state; window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window")); state = gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(window))); if ((state & GDK_WINDOW_STATE_ICONIFIED) || (ud->hb_visibility != GDK_VISIBILITY_UNOBSCURED)) { gtk_window_present(window); gtk_window_set_skip_taskbar_hint(window, FALSE); } else { gtk_window_set_skip_taskbar_hint(window, TRUE); gtk_window_iconify(window); } } #if !defined(_WIN32) G_MODULE_EXPORT void notify_closed_cb(NotifyNotification *notification, signal_user_data_t *ud) { g_object_unref(G_OBJECT(notification)); } #endif void ghb_notify_done(signal_user_data_t *ud) { if (ghb_settings_combo_int(ud->prefs, "WhenComplete") == 0) return; #if !defined(_WIN32) NotifyNotification *notification; notification = notify_notification_new( _("Encode Complete"), _("Put down that cocktail, Your HandBrake queue is done!"), NULL #if NOTIFY_CHECK_VERSION (0, 7, 0) ); #else ,NULL); #endif GtkIconTheme *theme = gtk_icon_theme_get_default(); GdkPixbuf *pb = gtk_icon_theme_load_icon(theme, "hb-icon", 32, GTK_ICON_LOOKUP_USE_BUILTIN, NULL); notify_notification_set_icon_from_pixbuf(notification, pb); g_signal_connect(notification, "closed", (GCallback)notify_closed_cb, ud); notify_notification_show(notification, NULL); g_object_unref(G_OBJECT(pb)); #endif if (ghb_settings_combo_int(ud->prefs, "WhenComplete") == 3) { if (ghb_can_shutdown_gsm()) { ghb_countdown_dialog(GTK_MESSAGE_WARNING, _("Your encode is complete."), _("Shutting down the computer"), _("Cancel"), (GSourceFunc)shutdown_cb, ud, 60); } } if (ghb_settings_combo_int(ud->prefs, "WhenComplete") == 2) { if (ghb_can_suspend_gpm()) { ghb_countdown_dialog(GTK_MESSAGE_WARNING, _("Your encode is complete."), _("Putting computer to sleep"), _("Cancel"), (GSourceFunc)suspend_cb, ud, 60); } } if (ghb_settings_combo_int(ud->prefs, "WhenComplete") == 4) { ghb_countdown_dialog(GTK_MESSAGE_WARNING, _("Your encode is complete."), _("Quiting Handbrake"), _("Cancel"), (GSourceFunc)quit_cb, ud, 60); } } G_MODULE_EXPORT gboolean window_configure_cb( GtkWidget *widget, GdkEventConfigure *event, signal_user_data_t *ud) { //g_message("preview_configure_cb()"); if (gtk_widget_get_visible(widget)) { gint w, h; w = ghb_settings_get_int(ud->prefs, "window_width"); h = ghb_settings_get_int(ud->prefs, "window_height"); if ( w != event->width || h != event->height ) { ghb_settings_set_int(ud->prefs, "window_width", event->width); ghb_settings_set_int(ud->prefs, "window_height", event->height); ghb_pref_set(ud->prefs, "window_width"); ghb_pref_set(ud->prefs, "window_height"); ghb_prefs_store(); } } return FALSE; } static void container_empty_cb(GtkWidget *widget, gpointer data) { gtk_widget_destroy(widget); } void ghb_container_empty(GtkContainer *c) { gtk_container_foreach(c, container_empty_cb, NULL); } HandBrake-0.10.2/gtk/src/videohandler.c0000664000175200017520000002060712505047552020235 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * callbacks.c * Copyright (C) John Stebbins 2008-2015 * * callbacks.c is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. */ #include #include "ghbcompat.h" #include #include "settings.h" #include "values.h" #include "callbacks.h" #include "presets.h" #include "preview.h" #include "hb-backend.h" #include "x264handler.h" int ghb_get_video_encoder(GValue *settings) { const char *encoder; encoder = ghb_settings_get_const_string(settings, "VideoEncoder"); return hb_video_encoder_get_from_name(encoder); } G_MODULE_EXPORT void vcodec_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { float val, vqmin, vqmax, step, page; int inverted, digits; ghb_widget_to_setting(ud->settings, widget); ghb_check_dependency(ud, widget, NULL); ghb_show_container_options(ud); ghb_clear_presets_selection(ud); ghb_live_reset(ud); // Set the range of the video quality slider val = ghb_vquality_default(ud); ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted); ghb_scale_configure(ud, "VideoQualitySlider", val, vqmin, vqmax, step, page, digits, inverted); ghb_update_ui_combo_box(ud, "VideoTune", NULL, FALSE); ghb_update_ui_combo_box(ud, "VideoProfile", NULL, FALSE); ghb_update_ui_combo_box(ud, "VideoLevel", NULL, FALSE); ghb_ui_update(ud, "VideoTune", ghb_int_value(0)); ghb_ui_update(ud, "VideoProfile", ghb_int_value(0)); ghb_ui_update(ud, "VideoLevel", ghb_int_value(0)); // Set the range of the preset slider int encoder = ghb_get_video_encoder(ud->settings); GtkWidget *presetSlider = GHB_WIDGET(ud->builder, "VideoPresetSlider"); const char * const *video_presets; int count = 0; video_presets = hb_video_encoder_get_presets(encoder); while (video_presets && video_presets[count]) count++; if (count) gtk_range_set_range(GTK_RANGE(presetSlider), 0, count-1); // Advanced options are only for x264 if (encoder != HB_VCODEC_X264) { ghb_ui_update(ud, "x264UseAdvancedOptions", ghb_boolean_value(FALSE)); } } void ghb_video_setting_changed(GtkWidget *widget, signal_user_data_t *ud) { static char *tt = NULL; if (tt == NULL) { GtkWidget *eo = GTK_WIDGET(GHB_WIDGET(ud->builder, "VideoOptionExtra")); tt = gtk_widget_get_tooltip_text(eo); } ghb_widget_to_setting(ud->settings, widget); int encoder = ghb_get_video_encoder(ud->settings); int presetIndex = ghb_settings_get_int(ud->settings, "VideoPresetSlider"); const char * const *video_presets; const char *preset; video_presets = hb_video_encoder_get_presets(encoder); if (video_presets != NULL) { preset = video_presets[presetIndex]; ghb_settings_set_string(ud->settings, "VideoPreset", preset); } if (!ghb_settings_get_boolean(ud->settings, "x264UseAdvancedOptions") && encoder == HB_VCODEC_X264) { GString *str = g_string_new(""); char *preset; char *tune; char *profile; char *level; char *opts; char *tunes; preset = ghb_settings_get_string(ud->settings, "VideoPreset"); tune = ghb_settings_get_string(ud->settings, "VideoTune"); profile = ghb_settings_get_string(ud->settings, "VideoProfile"); level = ghb_settings_get_string(ud->settings, "VideoLevel"); opts = ghb_settings_get_string(ud->settings, "VideoOptionExtra"); if (tune[0] && strcmp(tune, "none")) { g_string_append_printf(str, "%s", tune); } if (ghb_settings_get_boolean(ud->settings, "x264FastDecode")) { g_string_append_printf(str, "%s%s", str->str[0] ? "," : "", "fastdecode"); } if (ghb_settings_get_boolean(ud->settings, "x264ZeroLatency")) { g_string_append_printf(str, "%s%s", str->str[0] ? "," : "", "zerolatency"); } tunes = g_string_free(str, FALSE); char * new_opts; int w = ghb_settings_get_int(ud->settings, "scale_width"); int h = ghb_settings_get_int(ud->settings, "scale_height"); if (w == 0 || h == 0) { if (!ghb_settings_get_boolean(ud->settings, "autoscale")) { w = ghb_settings_get_int(ud->settings, "PictureWidth"); h = ghb_settings_get_int(ud->settings, "PictureHeight"); if (h == 0 && w != 0) { h = w * 9 / 16; } if (w == 0 && h != 0) { w = h * 16 / 9; } } if (w == 0 || h == 0) { w = 1280; h = 720; } } if (!strcasecmp(profile, "auto")) { profile[0] = 0; } if (!strcasecmp(level, "auto")) { level[0] = 0; } new_opts = hb_x264_param_unparse( preset, tunes, opts, profile, level, w, h); if (new_opts) ghb_update_x264Option(ud, new_opts); else ghb_update_x264Option(ud, ""); GtkWidget *eo = GTK_WIDGET(GHB_WIDGET(ud->builder, "VideoOptionExtra")); char * new_tt; if (new_opts) new_tt = g_strdup_printf(_("%s\n\nExpanded Options:\n\"%s\""), tt, new_opts); else new_tt = g_strdup_printf(_("%s\n\nExpanded Options:\n\"\""), tt); gtk_widget_set_tooltip_text(eo, new_tt); g_free(new_tt); g_free(new_opts); g_free(preset); g_free(tune); g_free(profile); g_free(level); g_free(opts); g_free(tunes); } else if (ghb_settings_get_boolean(ud->settings, "x264UseAdvancedOptions")) { char *opts = ghb_settings_get_string(ud->settings, "x264Option"); GtkWidget *eo = GTK_WIDGET(GHB_WIDGET(ud->builder, "VideoOptionExtra")); char * new_tt; if (opts) new_tt = g_strdup_printf(_("%s\n\nExpanded Options:\n\"%s\""), tt, opts); else new_tt = g_strdup_printf(_("%s\n\nExpanded Options:\n\"\""), tt); gtk_widget_set_tooltip_text(eo, new_tt); g_free(new_tt); g_free(opts); } ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); } G_MODULE_EXPORT void video_setting_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_video_setting_changed(widget, ud); } G_MODULE_EXPORT void x264_use_advanced_options_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { ghb_widget_to_setting(ud->settings, widget); if (ghb_settings_get_boolean(ud->prefs, "HideAdvancedVideoSettings") && ghb_settings_get_boolean(ud->settings, "x264UseAdvancedOptions")) { ghb_ui_update(ud, "x264UseAdvancedOptions", ghb_boolean_value(FALSE)); return; } if (ghb_settings_get_boolean(ud->settings, "x264UseAdvancedOptions")) { ghb_ui_update(ud, "VideoPresetSlider", ghb_int_value(5)); ghb_ui_update(ud, "VideoTune", ghb_string_value("none")); ghb_ui_update(ud, "VideoProfile", ghb_string_value("auto")); ghb_ui_update(ud, "VideoLevel", ghb_string_value("auto")); char *options = ghb_settings_get_string(ud->settings, "x264Option"); ghb_ui_update(ud, "VideoOptionExtra", ghb_string_value(options)); g_free(options); } ghb_check_dependency(ud, widget, NULL); ghb_clear_presets_selection(ud); } G_MODULE_EXPORT void video_option_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { GtkWidget *textview; textview = GTK_WIDGET(GHB_WIDGET(ud->builder, "VideoOptionExtra")); ghb_video_setting_changed(textview, ud); } G_MODULE_EXPORT gchar* format_video_preset_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud) { const char * const *video_presets; const char *preset; int encoder = ghb_get_video_encoder(ud->settings); video_presets = hb_video_encoder_get_presets(encoder); if (video_presets != NULL) { preset = video_presets[(int)val]; return g_strdup_printf(" %-12s", preset); } return g_strdup_printf(" %-12s", "ERROR"); } HandBrake-0.10.2/gtk/src/icons.c0000664000175200017520000000374312311644756016712 0ustar handbrakehandbrake#include "ghbcompat.h" #include "values.h" #include "resources.h" void ghb_load_icons() { GHashTableIter iter; gchar *key; GValue *gval; GValue *icons = ghb_resource_get("icons"); ghb_dict_iter_init(&iter, icons); // middle (void*) cast prevents gcc warning "defreferencing type-punned // pointer will break strict-aliasing rules" while (g_hash_table_iter_next( &iter, (gpointer*)(void*)&key, (gpointer*)(void*)&gval)) { ghb_rawdata_t *rd; gint size; GdkPixbuf *pb; gboolean svg; char *name = g_strdup(key); char *pos; pos = g_strstr_len(name, -1, "."); if (pos != NULL) *pos = '\0'; GInputStream *gis; svg = ghb_value_boolean(ghb_dict_lookup(gval, "svg")); rd = g_value_get_boxed(ghb_dict_lookup(gval, "data")); if (svg) { int ii; int sizes[] = {16, 22, 24, 32, 48, 64, 128, 256, 0}; for (ii = 0; sizes[ii]; ii++) { gis = g_memory_input_stream_new_from_data(rd->data, rd->size, NULL); pb = gdk_pixbuf_new_from_stream_at_scale(gis, sizes[ii], sizes[ii], TRUE, NULL, NULL); g_input_stream_close(gis, NULL, NULL); size = gdk_pixbuf_get_height(pb); gtk_icon_theme_add_builtin_icon(name, size, pb); g_object_unref(pb); } } else { gis = g_memory_input_stream_new_from_data(rd->data, rd->size, NULL); pb = gdk_pixbuf_new_from_stream(gis, NULL, NULL); g_input_stream_close(gis, NULL, NULL); size = gdk_pixbuf_get_height(pb); gtk_icon_theme_add_builtin_icon(name, size, pb); g_object_unref(pb); } g_free(name); } } HandBrake-0.10.2/gtk/src/preview.h0000664000175200017520000000243612352373116017255 0ustar handbrakehandbrake/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA */ #if !defined(_GHB_PREVIEW_H_) #define _GHB_PREVIEW_H_ #define GHB_PREVIEW_MAX 60 void ghb_preview_init(signal_user_data_t *ud); void ghb_set_preview_image(signal_user_data_t *ud); void ghb_live_preview_progress(signal_user_data_t *ud); void ghb_live_encode_done(signal_user_data_t *ud, gboolean success); void ghb_preview_cleanup(signal_user_data_t *ud); void ghb_live_reset(signal_user_data_t *ud); void ghb_par_scale(signal_user_data_t *ud, gint *width, gint *height, gint par_n, gint par_d); void ghb_preview_set_visible(signal_user_data_t *ud); #endif // _GHB_PREVIEW_H_ HandBrake-0.10.2/gtk/src/resources.c0000664000175200017520000000176012465416500017600 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * resources.c * Copyright (C) John Stebbins 2008-2015 * * resources.c is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * */ #include #include #include #include #include "ghbcompat.h" #include "settings.h" #include "plist.h" #include "resources.h" #include "values.h" static const gchar resource_str[] = #include "resource_data.h" ; static GValue *resources; void ghb_resource_init() { resources = ghb_plist_parse(resource_str, sizeof(resource_str)-1); } GValue* ghb_resource_get(const gchar *name) { GValue *result; result = ghb_dict_lookup(resources, name); return result; } void ghb_resource_free() { ghb_value_free(resources); } HandBrake-0.10.2/gtk/src/ghbcellrenderertext.c0000664000175200017520000017660512424000257021626 0ustar handbrakehandbrake/* gtkcellrenderertext.c * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include "ghbcompat.h" #include #include "marshalers.h" #include "ghbcellrenderertext.h" #ifdef ENABLE_NLS #define P_(String) dgettext(GETTEXT_PACKAGE "-properties",String) #else #define P_(String) (String) #endif #define I_(string) g_intern_static_string (string) #define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB #define GTK_PARAM_WRITABLE G_PARAM_WRITABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB #define MyGdkRectangle const GdkRectangle static void ghb_cell_renderer_text_finalize (GObject *object); static void ghb_cell_renderer_text_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec); static void ghb_cell_renderer_text_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec); static void ghb_cell_renderer_text_get_size (GtkCellRenderer *cell, GtkWidget *widget, MyGdkRectangle *cell_area, gint *x_offset, gint *y_offset, gint *width, gint *height); static void ghb_cell_renderer_text_render (GtkCellRenderer *cell, cairo_t *cr, GtkWidget *widget, MyGdkRectangle *background_area, MyGdkRectangle *cell_area, GtkCellRendererState flags); static GtkCellEditable *ghb_cell_renderer_text_start_editing (GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, MyGdkRectangle *background_area, MyGdkRectangle *cell_area, GtkCellRendererState flags); enum { EDITED, KEYPRESS, LAST_SIGNAL }; enum { PROP_0, PROP_TEXT, PROP_MARKUP, PROP_ATTRIBUTES, PROP_SINGLE_PARAGRAPH_MODE, PROP_WIDTH_CHARS, PROP_WRAP_WIDTH, PROP_ALIGN, /* Style args */ PROP_BACKGROUND, PROP_FOREGROUND, PROP_BACKGROUND_GDK, PROP_FOREGROUND_GDK, PROP_FONT, PROP_FONT_DESC, PROP_FAMILY, PROP_STYLE, PROP_VARIANT, PROP_WEIGHT, PROP_STRETCH, PROP_SIZE, PROP_SIZE_POINTS, PROP_SCALE, PROP_EDITABLE, PROP_STRIKETHROUGH, PROP_UNDERLINE, PROP_RISE, PROP_LANGUAGE, PROP_ELLIPSIZE, PROP_WRAP_MODE, /* Whether-a-style-arg-is-set args */ PROP_BACKGROUND_SET, PROP_FOREGROUND_SET, PROP_FAMILY_SET, PROP_STYLE_SET, PROP_VARIANT_SET, PROP_WEIGHT_SET, PROP_STRETCH_SET, PROP_SIZE_SET, PROP_SCALE_SET, PROP_EDITABLE_SET, PROP_STRIKETHROUGH_SET, PROP_UNDERLINE_SET, PROP_RISE_SET, PROP_LANGUAGE_SET, PROP_ELLIPSIZE_SET, PROP_ALIGN_SET }; static guint text_cell_renderer_signals [LAST_SIGNAL]; #define GHB_CELL_RENDERER_TEXT_PATH "gtk-cell-renderer-text-path" #define GHB_CELL_RENDERER_TEXT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GHB_TYPE_CELL_RENDERER_TEXT, GhbCellRendererTextPrivate)) typedef struct _GhbCellRendererTextPrivate GhbCellRendererTextPrivate; struct _GhbCellRendererTextPrivate { guint single_paragraph : 1; guint language_set : 1; guint markup_set : 1; guint ellipsize_set : 1; guint align_set : 1; gulong focus_out_id; PangoLanguage *language; PangoEllipsizeMode ellipsize; PangoWrapMode wrap_mode; PangoAlignment align; gulong populate_popup_id; gulong entry_menu_popdown_timeout; gboolean in_entry_menu; gint width_chars; gint wrap_width; GtkWidget *entry; }; G_DEFINE_TYPE (GhbCellRendererText, ghb_cell_renderer_text, GTK_TYPE_CELL_RENDERER) static void ghb_cell_renderer_text_init (GhbCellRendererText *celltext) { GhbCellRendererTextPrivate *priv; priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (celltext); g_object_set(celltext, "xalign", 0.0, NULL); g_object_set(celltext, "yalign", 0.5, NULL); g_object_set(celltext, "xpad", 2, NULL); g_object_set(celltext, "ypad", 2, NULL); celltext->font_scale = 1.0; celltext->fixed_height_rows = -1; celltext->font = pango_font_description_new (); priv->width_chars = -1; priv->wrap_width = -1; priv->wrap_mode = PANGO_WRAP_CHAR; priv->align = PANGO_ALIGN_LEFT; priv->align_set = FALSE; } static void ghb_cell_renderer_text_class_init (GhbCellRendererTextClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); object_class->finalize = ghb_cell_renderer_text_finalize; object_class->get_property = ghb_cell_renderer_text_get_property; object_class->set_property = ghb_cell_renderer_text_set_property; cell_class->get_size = ghb_cell_renderer_text_get_size; cell_class->render = ghb_cell_renderer_text_render; cell_class->start_editing = ghb_cell_renderer_text_start_editing; g_object_class_install_property (object_class, PROP_TEXT, g_param_spec_string ("text", P_("Text"), P_("Text to render"), NULL, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_MARKUP, g_param_spec_string ("markup", P_("Markup"), P_("Marked up text to render"), NULL, GTK_PARAM_WRITABLE)); g_object_class_install_property (object_class, PROP_ATTRIBUTES, g_param_spec_boxed ("attributes", P_("Attributes"), P_("A list of style attributes to apply to the text of the renderer"), PANGO_TYPE_ATTR_LIST, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_SINGLE_PARAGRAPH_MODE, g_param_spec_boolean ("single-paragraph-mode", P_("Single Paragraph Mode"), P_("Whether or not to keep all text in a single paragraph"), FALSE, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_BACKGROUND, g_param_spec_string ("background", P_("Background color name"), P_("Background color as a string"), NULL, GTK_PARAM_WRITABLE)); g_object_class_install_property (object_class, PROP_BACKGROUND_GDK, g_param_spec_boxed ("background-gdk", P_("Background color"), P_("Background color as a GdkColor"), GDK_TYPE_RGBA, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_FOREGROUND, g_param_spec_string ("foreground", P_("Foreground color name"), P_("Foreground color as a string"), NULL, GTK_PARAM_WRITABLE)); g_object_class_install_property (object_class, PROP_FOREGROUND_GDK, g_param_spec_boxed ("foreground-gdk", P_("Foreground color"), P_("Foreground color as a GdkColor"), GDK_TYPE_RGBA, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_EDITABLE, g_param_spec_boolean ("editable", P_("Editable"), P_("Whether the text can be modified by the user"), FALSE, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_FONT, g_param_spec_string ("font", P_("Font"), P_("Font description as a string, e.g. \"Sans Italic 12\""), NULL, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_FONT_DESC, g_param_spec_boxed ("font-desc", P_("Font"), P_("Font description as a PangoFontDescription struct"), PANGO_TYPE_FONT_DESCRIPTION, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_FAMILY, g_param_spec_string ("family", P_("Font family"), P_("Name of the font family, e.g. Sans, Helvetica, Times, Monospace"), NULL, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_STYLE, g_param_spec_enum ("style", P_("Font style"), P_("Font style"), PANGO_TYPE_STYLE, PANGO_STYLE_NORMAL, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_VARIANT, g_param_spec_enum ("variant", P_("Font variant"), P_("Font variant"), PANGO_TYPE_VARIANT, PANGO_VARIANT_NORMAL, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_WEIGHT, g_param_spec_int ("weight", P_("Font weight"), P_("Font weight"), 0, G_MAXINT, PANGO_WEIGHT_NORMAL, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_STRETCH, g_param_spec_enum ("stretch", P_("Font stretch"), P_("Font stretch"), PANGO_TYPE_STRETCH, PANGO_STRETCH_NORMAL, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_SIZE, g_param_spec_int ("size", P_("Font size"), P_("Font size"), 0, G_MAXINT, 0, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_SIZE_POINTS, g_param_spec_double ("size-points", P_("Font points"), P_("Font size in points"), 0.0, G_MAXDOUBLE, 0.0, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_SCALE, g_param_spec_double ("scale", P_("Font scale"), P_("Font scaling factor"), 0.0, G_MAXDOUBLE, 1.0, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_RISE, g_param_spec_int ("rise", P_("Rise"), P_("Offset of text above the baseline " "(below the baseline if rise is negative)"), -G_MAXINT, G_MAXINT, 0, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_STRIKETHROUGH, g_param_spec_boolean ("strikethrough", P_("Strikethrough"), P_("Whether to strike through the text"), FALSE, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_UNDERLINE, g_param_spec_enum ("underline", P_("Underline"), P_("Style of underline for this text"), PANGO_TYPE_UNDERLINE, PANGO_UNDERLINE_NONE, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_LANGUAGE, g_param_spec_string ("language", P_("Language"), P_("The language this text is in, as an ISO code. " "Pango can use this as a hint when rendering the text. " "If you don't understand this parameter, you probably don't need it"), NULL, GTK_PARAM_READWRITE)); /** * GhbCellRendererText:ellipsize: * * Specifies the preferred place to ellipsize the string, if the cell renderer * does not have enough room to display the entire string. Setting it to * %PANGO_ELLIPSIZE_NONE turns off ellipsizing. See the wrap-width property * for another way of making the text fit into a given width. * * Since: 2.6 */ g_object_class_install_property (object_class, PROP_ELLIPSIZE, g_param_spec_enum ("ellipsize", P_("Ellipsize"), P_("The preferred place to ellipsize the string, " "if the cell renderer does not have enough room " "to display the entire string"), PANGO_TYPE_ELLIPSIZE_MODE, PANGO_ELLIPSIZE_NONE, GTK_PARAM_READWRITE)); /** * GhbCellRendererText:width-chars: * * The desired width of the cell, in characters. If this property is set to * -1, the width will be calculated automatically, otherwise the cell will * request either 3 characters or the property value, whichever is greater. * * Since: 2.6 **/ g_object_class_install_property (object_class, PROP_WIDTH_CHARS, g_param_spec_int ("width-chars", P_("Width In Characters"), P_("The desired width of the label, in characters"), -1, G_MAXINT, -1, GTK_PARAM_READWRITE)); /** * GhbCellRendererText:wrap-mode: * * Specifies how to break the string into multiple lines, if the cell * renderer does not have enough room to display the entire string. * This property has no effect unless the wrap-width property is set. * * Since: 2.8 */ g_object_class_install_property (object_class, PROP_WRAP_MODE, g_param_spec_enum ("wrap-mode", P_("Wrap mode"), P_("How to break the string into multiple lines, " "if the cell renderer does not have enough room " "to display the entire string"), PANGO_TYPE_WRAP_MODE, PANGO_WRAP_CHAR, GTK_PARAM_READWRITE)); /** * GhbCellRendererText:wrap-width: * * Specifies the width at which the text is wrapped. The wrap-mode property can * be used to influence at what character positions the line breaks can be placed. * Setting wrap-width to -1 turns wrapping off. * * Since: 2.8 */ g_object_class_install_property (object_class, PROP_WRAP_WIDTH, g_param_spec_int ("wrap-width", P_("Wrap width"), P_("The width at which the text is wrapped"), -1, G_MAXINT, -1, GTK_PARAM_READWRITE)); /** * GhbCellRendererText:alignment: * * Specifies how to align the lines of text with respect to each other. * * Note that this property describes how to align the lines of text in * case there are several of them. The "xalign" property of #GtkCellRenderer, * on the other hand, sets the horizontal alignment of the whole text. * * Since: 2.10 */ g_object_class_install_property (object_class, PROP_ALIGN, g_param_spec_enum ("alignment", P_("Alignment"), P_("How to align the lines"), PANGO_TYPE_ALIGNMENT, PANGO_ALIGN_LEFT, GTK_PARAM_READWRITE)); /* Style props are set or not */ #define ADD_SET_PROP(propname, propval, nick, blurb) g_object_class_install_property (object_class, propval, g_param_spec_boolean (propname, nick, blurb, FALSE, GTK_PARAM_READWRITE)) ADD_SET_PROP ("background-set", PROP_BACKGROUND_SET, P_("Background set"), P_("Whether this tag affects the background color")); ADD_SET_PROP ("foreground-set", PROP_FOREGROUND_SET, P_("Foreground set"), P_("Whether this tag affects the foreground color")); ADD_SET_PROP ("editable-set", PROP_EDITABLE_SET, P_("Editability set"), P_("Whether this tag affects text editability")); ADD_SET_PROP ("family-set", PROP_FAMILY_SET, P_("Font family set"), P_("Whether this tag affects the font family")); ADD_SET_PROP ("style-set", PROP_STYLE_SET, P_("Font style set"), P_("Whether this tag affects the font style")); ADD_SET_PROP ("variant-set", PROP_VARIANT_SET, P_("Font variant set"), P_("Whether this tag affects the font variant")); ADD_SET_PROP ("weight-set", PROP_WEIGHT_SET, P_("Font weight set"), P_("Whether this tag affects the font weight")); ADD_SET_PROP ("stretch-set", PROP_STRETCH_SET, P_("Font stretch set"), P_("Whether this tag affects the font stretch")); ADD_SET_PROP ("size-set", PROP_SIZE_SET, P_("Font size set"), P_("Whether this tag affects the font size")); ADD_SET_PROP ("scale-set", PROP_SCALE_SET, P_("Font scale set"), P_("Whether this tag scales the font size by a factor")); ADD_SET_PROP ("rise-set", PROP_RISE_SET, P_("Rise set"), P_("Whether this tag affects the rise")); ADD_SET_PROP ("strikethrough-set", PROP_STRIKETHROUGH_SET, P_("Strikethrough set"), P_("Whether this tag affects strikethrough")); ADD_SET_PROP ("underline-set", PROP_UNDERLINE_SET, P_("Underline set"), P_("Whether this tag affects underlining")); ADD_SET_PROP ("language-set", PROP_LANGUAGE_SET, P_("Language set"), P_("Whether this tag affects the language the text is rendered as")); ADD_SET_PROP ("ellipsize-set", PROP_ELLIPSIZE_SET, P_("Ellipsize set"), P_("Whether this tag affects the ellipsize mode")); ADD_SET_PROP ("align-set", PROP_ALIGN_SET, P_("Align set"), P_("Whether this tag affects the alignment mode")); /** * GhbCellRendererText::edited * @renderer: the object which received the signal * @path: the path identifying the edited cell * @new_text: the new text * * This signal is emitted after @renderer has been edited. * * It is the responsibility of the application to update the model * and store @new_text at the position indicated by @path. */ text_cell_renderer_signals [EDITED] = g_signal_new (I_("edited"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GhbCellRendererTextClass, edited), NULL, NULL, ghb_marshal_VOID__STRING_STRING, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); text_cell_renderer_signals [KEYPRESS] = g_signal_new (I_("key-press-event"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GhbCellRendererTextClass, keypress), NULL, NULL, ghb_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); g_type_class_add_private (object_class, sizeof (GhbCellRendererTextPrivate)); } static void ghb_cell_renderer_text_finalize (GObject *object) { GhbCellRendererText *celltext = GHB_CELL_RENDERER_TEXT (object); GhbCellRendererTextPrivate *priv; priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (object); pango_font_description_free (celltext->font); g_free (celltext->text); if (celltext->extra_attrs) pango_attr_list_unref (celltext->extra_attrs); if (priv->language) g_object_unref (priv->language); (* G_OBJECT_CLASS (ghb_cell_renderer_text_parent_class)->finalize) (object); } static PangoFontMask get_property_font_set_mask (guint prop_id) { switch (prop_id) { case PROP_FAMILY_SET: return PANGO_FONT_MASK_FAMILY; case PROP_STYLE_SET: return PANGO_FONT_MASK_STYLE; case PROP_VARIANT_SET: return PANGO_FONT_MASK_VARIANT; case PROP_WEIGHT_SET: return PANGO_FONT_MASK_WEIGHT; case PROP_STRETCH_SET: return PANGO_FONT_MASK_STRETCH; case PROP_SIZE_SET: return PANGO_FONT_MASK_SIZE; } return 0; } static void ghb_cell_renderer_text_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec) { GhbCellRendererText *celltext = GHB_CELL_RENDERER_TEXT (object); GhbCellRendererTextPrivate *priv; priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (object); switch (param_id) { case PROP_TEXT: g_value_set_string (value, celltext->text); break; case PROP_ATTRIBUTES: g_value_set_boxed (value, celltext->extra_attrs); break; case PROP_SINGLE_PARAGRAPH_MODE: g_value_set_boolean (value, priv->single_paragraph); break; case PROP_BACKGROUND_GDK: { GdkColor color; color.red = celltext->background.red; color.green = celltext->background.green; color.blue = celltext->background.blue; g_value_set_boxed (value, &color); } break; case PROP_FOREGROUND_GDK: { GdkColor color; color.red = celltext->foreground.red; color.green = celltext->foreground.green; color.blue = celltext->foreground.blue; g_value_set_boxed (value, &color); } break; case PROP_FONT: g_value_take_string (value, pango_font_description_to_string (celltext->font)); break; case PROP_FONT_DESC: g_value_set_boxed (value, celltext->font); break; case PROP_FAMILY: g_value_set_string (value, pango_font_description_get_family (celltext->font)); break; case PROP_STYLE: g_value_set_enum (value, pango_font_description_get_style (celltext->font)); break; case PROP_VARIANT: g_value_set_enum (value, pango_font_description_get_variant (celltext->font)); break; case PROP_WEIGHT: g_value_set_int (value, pango_font_description_get_weight (celltext->font)); break; case PROP_STRETCH: g_value_set_enum (value, pango_font_description_get_stretch (celltext->font)); break; case PROP_SIZE: g_value_set_int (value, pango_font_description_get_size (celltext->font)); break; case PROP_SIZE_POINTS: g_value_set_double (value, ((double)pango_font_description_get_size (celltext->font)) / (double)PANGO_SCALE); break; case PROP_SCALE: g_value_set_double (value, celltext->font_scale); break; case PROP_EDITABLE: g_value_set_boolean (value, celltext->editable); break; case PROP_STRIKETHROUGH: g_value_set_boolean (value, celltext->strikethrough); break; case PROP_UNDERLINE: g_value_set_enum (value, celltext->underline_style); break; case PROP_RISE: g_value_set_int (value, celltext->rise); break; case PROP_LANGUAGE: g_value_set_static_string (value, pango_language_to_string (priv->language)); break; case PROP_ELLIPSIZE: g_value_set_enum (value, priv->ellipsize); break; case PROP_WRAP_MODE: g_value_set_enum (value, priv->wrap_mode); break; case PROP_WRAP_WIDTH: g_value_set_int (value, priv->wrap_width); break; case PROP_ALIGN: g_value_set_enum (value, priv->align); break; case PROP_BACKGROUND_SET: g_value_set_boolean (value, celltext->background_set); break; case PROP_FOREGROUND_SET: g_value_set_boolean (value, celltext->foreground_set); break; case PROP_FAMILY_SET: case PROP_STYLE_SET: case PROP_VARIANT_SET: case PROP_WEIGHT_SET: case PROP_STRETCH_SET: case PROP_SIZE_SET: { PangoFontMask mask = get_property_font_set_mask (param_id); g_value_set_boolean (value, (pango_font_description_get_set_fields (celltext->font) & mask) != 0); break; } case PROP_SCALE_SET: g_value_set_boolean (value, celltext->scale_set); break; case PROP_EDITABLE_SET: g_value_set_boolean (value, celltext->editable_set); break; case PROP_STRIKETHROUGH_SET: g_value_set_boolean (value, celltext->strikethrough_set); break; case PROP_UNDERLINE_SET: g_value_set_boolean (value, celltext->underline_set); break; case PROP_RISE_SET: g_value_set_boolean (value, celltext->rise_set); break; case PROP_LANGUAGE_SET: g_value_set_boolean (value, priv->language_set); break; case PROP_ELLIPSIZE_SET: g_value_set_boolean (value, priv->ellipsize_set); break; case PROP_ALIGN_SET: g_value_set_boolean (value, priv->align_set); break; case PROP_WIDTH_CHARS: g_value_set_int (value, priv->width_chars); break; case PROP_BACKGROUND: case PROP_FOREGROUND: case PROP_MARKUP: default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } static void set_bg_color (GhbCellRendererText *celltext, GdkRGBA *rgba) { if (rgba) { if (!celltext->background_set) { celltext->background_set = TRUE; g_object_notify (G_OBJECT (celltext), "background-set"); } celltext->background.red = rgba->red; celltext->background.green = rgba->green; celltext->background.blue = rgba->blue; } else { if (celltext->background_set) { celltext->background_set = FALSE; g_object_notify (G_OBJECT (celltext), "background-set"); } } } static void set_fg_color (GhbCellRendererText *celltext, GdkRGBA *rgba) { if (rgba) { if (!celltext->foreground_set) { celltext->foreground_set = TRUE; g_object_notify (G_OBJECT (celltext), "foreground-set"); } celltext->foreground.red = rgba->red; celltext->foreground.green = rgba->green; celltext->foreground.blue = rgba->blue; } else { if (celltext->foreground_set) { celltext->foreground_set = FALSE; g_object_notify (G_OBJECT (celltext), "foreground-set"); } } } static PangoFontMask set_font_desc_fields (PangoFontDescription *desc, PangoFontMask to_set) { PangoFontMask changed_mask = 0; if (to_set & PANGO_FONT_MASK_FAMILY) { const char *family = pango_font_description_get_family (desc); if (!family) { family = "sans"; changed_mask |= PANGO_FONT_MASK_FAMILY; } pango_font_description_set_family (desc, family); } if (to_set & PANGO_FONT_MASK_STYLE) pango_font_description_set_style (desc, pango_font_description_get_style (desc)); if (to_set & PANGO_FONT_MASK_VARIANT) pango_font_description_set_variant (desc, pango_font_description_get_variant (desc)); if (to_set & PANGO_FONT_MASK_WEIGHT) pango_font_description_set_weight (desc, pango_font_description_get_weight (desc)); if (to_set & PANGO_FONT_MASK_STRETCH) pango_font_description_set_stretch (desc, pango_font_description_get_stretch (desc)); if (to_set & PANGO_FONT_MASK_SIZE) { gint size = pango_font_description_get_size (desc); if (size <= 0) { size = 10 * PANGO_SCALE; changed_mask |= PANGO_FONT_MASK_SIZE; } pango_font_description_set_size (desc, size); } return changed_mask; } static void notify_set_changed (GObject *object, PangoFontMask changed_mask) { if (changed_mask & PANGO_FONT_MASK_FAMILY) g_object_notify (object, "family-set"); if (changed_mask & PANGO_FONT_MASK_STYLE) g_object_notify (object, "style-set"); if (changed_mask & PANGO_FONT_MASK_VARIANT) g_object_notify (object, "variant-set"); if (changed_mask & PANGO_FONT_MASK_WEIGHT) g_object_notify (object, "weight-set"); if (changed_mask & PANGO_FONT_MASK_STRETCH) g_object_notify (object, "stretch-set"); if (changed_mask & PANGO_FONT_MASK_SIZE) g_object_notify (object, "size-set"); } static void notify_fields_changed (GObject *object, PangoFontMask changed_mask) { if (changed_mask & PANGO_FONT_MASK_FAMILY) g_object_notify (object, "family"); if (changed_mask & PANGO_FONT_MASK_STYLE) g_object_notify (object, "style"); if (changed_mask & PANGO_FONT_MASK_VARIANT) g_object_notify (object, "variant"); if (changed_mask & PANGO_FONT_MASK_WEIGHT) g_object_notify (object, "weight"); if (changed_mask & PANGO_FONT_MASK_STRETCH) g_object_notify (object, "stretch"); if (changed_mask & PANGO_FONT_MASK_SIZE) g_object_notify (object, "size"); } static void set_font_description (GhbCellRendererText *celltext, PangoFontDescription *font_desc) { GObject *object = G_OBJECT (celltext); PangoFontDescription *new_font_desc; PangoFontMask old_mask, new_mask, changed_mask, set_changed_mask; if (font_desc) new_font_desc = pango_font_description_copy (font_desc); else new_font_desc = pango_font_description_new (); old_mask = pango_font_description_get_set_fields (celltext->font); new_mask = pango_font_description_get_set_fields (new_font_desc); changed_mask = old_mask | new_mask; set_changed_mask = old_mask ^ new_mask; pango_font_description_free (celltext->font); celltext->font = new_font_desc; g_object_freeze_notify (object); g_object_notify (object, "font-desc"); g_object_notify (object, "font"); if (changed_mask & PANGO_FONT_MASK_FAMILY) g_object_notify (object, "family"); if (changed_mask & PANGO_FONT_MASK_STYLE) g_object_notify (object, "style"); if (changed_mask & PANGO_FONT_MASK_VARIANT) g_object_notify (object, "variant"); if (changed_mask & PANGO_FONT_MASK_WEIGHT) g_object_notify (object, "weight"); if (changed_mask & PANGO_FONT_MASK_STRETCH) g_object_notify (object, "stretch"); if (changed_mask & PANGO_FONT_MASK_SIZE) { g_object_notify (object, "size"); g_object_notify (object, "size-points"); } notify_set_changed (object, set_changed_mask); g_object_thaw_notify (object); } static void ghb_cell_renderer_text_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec) { GhbCellRendererText *celltext = GHB_CELL_RENDERER_TEXT (object); GhbCellRendererTextPrivate *priv; priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (object); switch (param_id) { case PROP_TEXT: g_free (celltext->text); if (priv->markup_set) { if (celltext->extra_attrs) pango_attr_list_unref (celltext->extra_attrs); celltext->extra_attrs = NULL; priv->markup_set = FALSE; } celltext->text = g_strdup (g_value_get_string (value)); g_object_notify (object, "text"); break; case PROP_ATTRIBUTES: if (celltext->extra_attrs) pango_attr_list_unref (celltext->extra_attrs); celltext->extra_attrs = g_value_get_boxed (value); if (celltext->extra_attrs) pango_attr_list_ref (celltext->extra_attrs); break; case PROP_MARKUP: { const gchar *str; gchar *text = NULL; GError *error = NULL; PangoAttrList *attrs = NULL; str = g_value_get_string (value); if (str && !pango_parse_markup (str, -1, 0, &attrs, &text, NULL, &error)) { g_warning ("Failed to set text from markup due to error parsing markup: %s", error->message); g_error_free (error); return; } g_free (celltext->text); if (celltext->extra_attrs) pango_attr_list_unref (celltext->extra_attrs); celltext->text = text; celltext->extra_attrs = attrs; priv->markup_set = TRUE; } break; case PROP_SINGLE_PARAGRAPH_MODE: priv->single_paragraph = g_value_get_boolean (value); break; case PROP_BACKGROUND: { GdkRGBA rgba; if (!g_value_get_string (value)) set_bg_color (celltext, NULL); /* reset to background_set to FALSE */ else if (gdk_rgba_parse(&rgba, g_value_get_string(value))) set_bg_color (celltext, &rgba); else g_warning ("Don't know color `%s'", g_value_get_string(value)); g_object_notify (object, "background-gdk"); } break; case PROP_FOREGROUND: { GdkRGBA rgba; if (!g_value_get_string (value)) set_fg_color (celltext, NULL); /* reset to foreground_set to FALSE */ else if (gdk_rgba_parse(&rgba, g_value_get_string(value))) set_fg_color (celltext, &rgba); else g_warning ("Don't know color `%s'", g_value_get_string (value)); g_object_notify (object, "foreground-gdk"); } break; case PROP_BACKGROUND_GDK: /* This notifies the GObject itself. */ set_bg_color (celltext, g_value_get_boxed (value)); break; case PROP_FOREGROUND_GDK: /* This notifies the GObject itself. */ set_fg_color (celltext, g_value_get_boxed (value)); break; case PROP_FONT: { PangoFontDescription *font_desc = NULL; const gchar *name; name = g_value_get_string (value); if (name) font_desc = pango_font_description_from_string (name); set_font_description (celltext, font_desc); pango_font_description_free (font_desc); if (celltext->fixed_height_rows != -1) celltext->calc_fixed_height = TRUE; } break; case PROP_FONT_DESC: set_font_description (celltext, g_value_get_boxed (value)); if (celltext->fixed_height_rows != -1) celltext->calc_fixed_height = TRUE; break; case PROP_FAMILY: case PROP_STYLE: case PROP_VARIANT: case PROP_WEIGHT: case PROP_STRETCH: case PROP_SIZE: case PROP_SIZE_POINTS: { PangoFontMask old_set_mask = pango_font_description_get_set_fields (celltext->font); switch (param_id) { case PROP_FAMILY: pango_font_description_set_family (celltext->font, g_value_get_string (value)); break; case PROP_STYLE: pango_font_description_set_style (celltext->font, g_value_get_enum (value)); break; case PROP_VARIANT: pango_font_description_set_variant (celltext->font, g_value_get_enum (value)); break; case PROP_WEIGHT: pango_font_description_set_weight (celltext->font, g_value_get_int (value)); break; case PROP_STRETCH: pango_font_description_set_stretch (celltext->font, g_value_get_enum (value)); break; case PROP_SIZE: pango_font_description_set_size (celltext->font, g_value_get_int (value)); g_object_notify (object, "size-points"); break; case PROP_SIZE_POINTS: pango_font_description_set_size (celltext->font, g_value_get_double (value) * PANGO_SCALE); g_object_notify (object, "size"); break; } if (celltext->fixed_height_rows != -1) celltext->calc_fixed_height = TRUE; notify_set_changed (object, old_set_mask & pango_font_description_get_set_fields (celltext->font)); g_object_notify (object, "font-desc"); g_object_notify (object, "font"); break; } case PROP_SCALE: celltext->font_scale = g_value_get_double (value); celltext->scale_set = TRUE; if (celltext->fixed_height_rows != -1) celltext->calc_fixed_height = TRUE; g_object_notify (object, "scale-set"); break; case PROP_EDITABLE: celltext->editable = g_value_get_boolean (value); celltext->editable_set = TRUE; if (celltext->editable) g_object_set(celltext, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL); else g_object_set(celltext, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); g_object_notify (object, "editable-set"); break; case PROP_STRIKETHROUGH: celltext->strikethrough = g_value_get_boolean (value); celltext->strikethrough_set = TRUE; g_object_notify (object, "strikethrough-set"); break; case PROP_UNDERLINE: celltext->underline_style = g_value_get_enum (value); celltext->underline_set = TRUE; g_object_notify (object, "underline-set"); break; case PROP_RISE: celltext->rise = g_value_get_int (value); celltext->rise_set = TRUE; g_object_notify (object, "rise-set"); if (celltext->fixed_height_rows != -1) celltext->calc_fixed_height = TRUE; break; case PROP_LANGUAGE: priv->language_set = TRUE; if (priv->language) g_object_unref (priv->language); priv->language = pango_language_from_string (g_value_get_string (value)); g_object_notify (object, "language-set"); break; case PROP_ELLIPSIZE: priv->ellipsize = g_value_get_enum (value); priv->ellipsize_set = TRUE; g_object_notify (object, "ellipsize-set"); break; case PROP_WRAP_MODE: priv->wrap_mode = g_value_get_enum (value); break; case PROP_WRAP_WIDTH: priv->wrap_width = g_value_get_int (value); break; case PROP_WIDTH_CHARS: priv->width_chars = g_value_get_int (value); break; case PROP_ALIGN: priv->align = g_value_get_enum (value); priv->align_set = TRUE; g_object_notify (object, "align-set"); break; case PROP_BACKGROUND_SET: celltext->background_set = g_value_get_boolean (value); break; case PROP_FOREGROUND_SET: celltext->foreground_set = g_value_get_boolean (value); break; case PROP_FAMILY_SET: case PROP_STYLE_SET: case PROP_VARIANT_SET: case PROP_WEIGHT_SET: case PROP_STRETCH_SET: case PROP_SIZE_SET: if (!g_value_get_boolean (value)) { pango_font_description_unset_fields (celltext->font, get_property_font_set_mask (param_id)); } else { PangoFontMask changed_mask; changed_mask = set_font_desc_fields (celltext->font, get_property_font_set_mask (param_id)); notify_fields_changed (G_OBJECT (celltext), changed_mask); } break; case PROP_SCALE_SET: celltext->scale_set = g_value_get_boolean (value); break; case PROP_EDITABLE_SET: celltext->editable_set = g_value_get_boolean (value); break; case PROP_STRIKETHROUGH_SET: celltext->strikethrough_set = g_value_get_boolean (value); break; case PROP_UNDERLINE_SET: celltext->underline_set = g_value_get_boolean (value); break; case PROP_RISE_SET: celltext->rise_set = g_value_get_boolean (value); break; case PROP_LANGUAGE_SET: priv->language_set = g_value_get_boolean (value); break; case PROP_ELLIPSIZE_SET: priv->ellipsize_set = g_value_get_boolean (value); break; case PROP_ALIGN_SET: priv->align_set = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } /** * ghb_cell_renderer_text_new: * * Creates a new #GhbCellRendererText. Adjust how text is drawn using * object properties. Object properties can be * set globally (with g_object_set()). Also, with #GtkTreeViewColumn, * you can bind a property to a value in a #GtkTreeModel. For example, * you can bind the "text" property on the cell renderer to a string * value in the model, thus rendering a different string in each row * of the #GtkTreeView * * Return value: the new cell renderer **/ GtkCellRenderer * ghb_cell_renderer_text_new (void) { return g_object_new (GHB_TYPE_CELL_RENDERER_TEXT, NULL); } static void add_attr (PangoAttrList *attr_list, PangoAttribute *attr) { attr->start_index = 0; attr->end_index = G_MAXINT; pango_attr_list_insert (attr_list, attr); } static PangoLayout* get_layout (GhbCellRendererText *celltext, GtkWidget *widget, gboolean will_render, GtkCellRendererState flags) { PangoAttrList *attr_list; PangoLayout *layout; PangoUnderline uline; GhbCellRendererTextPrivate *priv; priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (celltext); layout = gtk_widget_create_pango_layout (widget, celltext->text); if (celltext->extra_attrs) attr_list = pango_attr_list_copy (celltext->extra_attrs); else attr_list = pango_attr_list_new (); pango_layout_set_single_paragraph_mode (layout, priv->single_paragraph); if (will_render) { /* Add options that affect appearance but not size */ /* note that background doesn't go here, since it affects * background_area not the PangoLayout area */ if (celltext->foreground_set && (flags & GTK_CELL_RENDERER_SELECTED) == 0) { PangoColor color; color = celltext->foreground; add_attr (attr_list, pango_attr_foreground_new (color.red, color.green, color.blue)); } if (celltext->strikethrough_set) add_attr (attr_list, pango_attr_strikethrough_new (celltext->strikethrough)); } add_attr (attr_list, pango_attr_font_desc_new (celltext->font)); if (celltext->scale_set && celltext->font_scale != 1.0) add_attr (attr_list, pango_attr_scale_new (celltext->font_scale)); if (celltext->underline_set) uline = celltext->underline_style; else uline = PANGO_UNDERLINE_NONE; if (priv->language_set) add_attr (attr_list, pango_attr_language_new (priv->language)); if ((flags & GTK_CELL_RENDERER_PRELIT) == GTK_CELL_RENDERER_PRELIT) { switch (uline) { case PANGO_UNDERLINE_NONE: uline = PANGO_UNDERLINE_SINGLE; break; case PANGO_UNDERLINE_SINGLE: uline = PANGO_UNDERLINE_DOUBLE; break; default: break; } } if (uline != PANGO_UNDERLINE_NONE) add_attr (attr_list, pango_attr_underline_new (celltext->underline_style)); if (celltext->rise_set) add_attr (attr_list, pango_attr_rise_new (celltext->rise)); if (priv->ellipsize_set) pango_layout_set_ellipsize (layout, priv->ellipsize); else pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE); if (priv->wrap_width != -1) { pango_layout_set_width (layout, priv->wrap_width * PANGO_SCALE); pango_layout_set_wrap (layout, priv->wrap_mode); } else { pango_layout_set_width (layout, -1); pango_layout_set_wrap (layout, PANGO_WRAP_CHAR); } if (priv->align_set) pango_layout_set_alignment (layout, priv->align); else { PangoAlignment align; if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) align = PANGO_ALIGN_RIGHT; else align = PANGO_ALIGN_LEFT; pango_layout_set_alignment (layout, align); } pango_layout_set_attributes (layout, attr_list); pango_attr_list_unref (attr_list); return layout; } static void get_size (GtkCellRenderer *cell, GtkWidget *widget, MyGdkRectangle *cell_area, PangoLayout *layout, gint *x_offset, gint *y_offset, gint *width, gint *height) { GhbCellRendererText *celltext = (GhbCellRendererText *) cell; PangoRectangle rect; GhbCellRendererTextPrivate *priv; priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (cell); gint cell_width, cell_height, cell_xpad, cell_ypad; gfloat cell_xalign, cell_yalign; gtk_cell_renderer_get_fixed_size(cell, &cell_width, &cell_height); gtk_cell_renderer_get_alignment(cell, &cell_xalign, &cell_yalign); gtk_cell_renderer_get_padding(cell, &cell_xpad, &cell_ypad); if (celltext->calc_fixed_height) { PangoContext *context; PangoFontMetrics *metrics; PangoFontDescription *font_desc; gint row_height; font_desc = pango_font_description_copy_static(ghb_widget_get_font(widget)); pango_font_description_merge_static(font_desc, celltext->font, TRUE); if (celltext->scale_set) pango_font_description_set_size (font_desc, celltext->font_scale * pango_font_description_get_size (font_desc)); context = gtk_widget_get_pango_context (widget); metrics = pango_context_get_metrics (context, font_desc, pango_context_get_language (context)); row_height = (pango_font_metrics_get_ascent (metrics) + pango_font_metrics_get_descent (metrics)); pango_font_metrics_unref (metrics); pango_font_description_free (font_desc); gtk_cell_renderer_set_fixed_size (cell, cell_width, 2*cell_ypad + celltext->fixed_height_rows * PANGO_PIXELS (row_height)); if (height) { *height = cell_height; height = NULL; } celltext->calc_fixed_height = FALSE; if (width == NULL) return; } if (layout) g_object_ref (layout); else layout = get_layout (celltext, widget, FALSE, 0); pango_layout_get_pixel_extents(layout, NULL, &rect); if (cell_area) { rect.height = MIN(rect.height, cell_area->height - 2 * cell_ypad); rect.width = MIN(rect.width, cell_area->width - 2 * cell_xpad); if (x_offset) { if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) *x_offset = (1.0 - cell_xalign) * (cell_area->width - (2 * cell_xpad)); else *x_offset = cell_xalign * (cell_area->width - (2 * cell_xpad)); if ((priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) || priv->wrap_width != -1) *x_offset = MAX(*x_offset, 0); } if (y_offset) { *y_offset = cell_yalign * (cell_area->height - (rect.height + (2 * cell_ypad))); *y_offset = MAX (*y_offset, 0); } } else { if (x_offset) *x_offset = 0; if (y_offset) *y_offset = 0; } if (height) *height = cell_ypad * 2 + rect.height; if (width) *width = cell_xpad * 2 + rect.width; g_object_unref (layout); } static void ghb_cell_renderer_text_get_size (GtkCellRenderer *cell, GtkWidget *widget, MyGdkRectangle *cell_area, gint *x_offset, gint *y_offset, gint *width, gint *height) { get_size (cell, widget, cell_area, NULL, x_offset, y_offset, width, height); } static void ghb_cell_renderer_text_render( GtkCellRenderer *cell, cairo_t *cr, GtkWidget *widget, MyGdkRectangle *background_area, MyGdkRectangle *cell_area, GtkCellRendererState flags) { GhbCellRendererText *celltext = (GhbCellRendererText *) cell; PangoLayout *layout; gint x_offset = 0; gint y_offset = 0; GhbCellRendererTextPrivate *priv; priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (cell); layout = get_layout (celltext, widget, TRUE, flags); get_size(cell, widget, cell_area, layout, &x_offset, &y_offset, NULL, NULL); gint xpad, ypad; #if 0 GtkStateType state; gboolean sensitive; sensitive = gtk_cell_renderer_get_sensitive(cell); if (!sensitive) { state = GTK_STATE_INSENSITIVE; } else if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED) { if (gtk_widget_has_focus (widget)) state = GTK_STATE_SELECTED; else state = GTK_STATE_ACTIVE; } else if ((flags & GTK_CELL_RENDERER_PRELIT) == GTK_CELL_RENDERER_PRELIT && gtk_widget_get_state(widget) == GTK_STATE_PRELIGHT) { state = GTK_STATE_PRELIGHT; } else { if (gtk_widget_get_state(widget) == GTK_STATE_INSENSITIVE) state = GTK_STATE_INSENSITIVE; else state = GTK_STATE_NORMAL; } #endif gtk_cell_renderer_get_padding(cell, &xpad, &ypad); if (celltext->background_set && (flags & GTK_CELL_RENDERER_SELECTED) == 0) { gdk_cairo_rectangle (cr, background_area); cairo_set_source_rgb (cr, celltext->background.red / 65535., celltext->background.green / 65535., celltext->background.blue / 65535.); cairo_fill (cr); } if (priv->ellipsize_set && priv->ellipsize != PANGO_ELLIPSIZE_NONE) pango_layout_set_width (layout, (cell_area->width - x_offset - 2 * xpad) * PANGO_SCALE); else if (priv->wrap_width == -1) pango_layout_set_width (layout, -1); gtk_render_layout(gtk_widget_get_style_context(widget), cr, cell_area->x + x_offset + xpad, cell_area->y + y_offset + ypad, layout); g_object_unref (layout); } static gboolean ghb_cell_renderer_text_keypress( GtkCellEditable *entry, GdkEventKey *event, gpointer data) { gboolean result; g_signal_emit( data, text_cell_renderer_signals[KEYPRESS], 0, event, &result); return result; } static void ghb_cell_renderer_text_editing_done (GtkCellEditable *entry, gpointer data) { const gchar *path; const gchar *new_text; GhbCellRendererTextPrivate *priv; priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (data); priv->entry = NULL; if (priv->focus_out_id > 0) { g_signal_handler_disconnect (entry, priv->focus_out_id); priv->focus_out_id = 0; } if (priv->populate_popup_id > 0) { g_signal_handler_disconnect (entry, priv->populate_popup_id); priv->populate_popup_id = 0; } if (priv->entry_menu_popdown_timeout) { g_source_remove (priv->entry_menu_popdown_timeout); priv->entry_menu_popdown_timeout = 0; } gboolean editing_canceled; g_object_get(entry, "editing-canceled", &editing_canceled, NULL); gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (data), editing_canceled); if (editing_canceled) return; path = g_object_get_data (G_OBJECT (entry), GHB_CELL_RENDERER_TEXT_PATH); new_text = gtk_entry_get_text (GTK_ENTRY (entry)); g_signal_emit (data, text_cell_renderer_signals[EDITED], 0, path, new_text); } static gboolean popdown_timeout (gpointer data) { GhbCellRendererTextPrivate *priv; priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (data); priv->entry_menu_popdown_timeout = 0; if (!gtk_widget_has_focus (priv->entry)) ghb_cell_renderer_text_editing_done (GTK_CELL_EDITABLE (priv->entry), data); return FALSE; } static void ghb_cell_renderer_text_popup_unmap (GtkMenu *menu, gpointer data) { GhbCellRendererTextPrivate *priv; priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (data); priv->in_entry_menu = FALSE; if (priv->entry_menu_popdown_timeout) return; priv->entry_menu_popdown_timeout = gdk_threads_add_timeout (500, popdown_timeout, data); } static void ghb_cell_renderer_text_populate_popup (GtkEntry *entry, GtkMenu *menu, gpointer data) { GhbCellRendererTextPrivate *priv; priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (data); if (priv->entry_menu_popdown_timeout) { g_source_remove (priv->entry_menu_popdown_timeout); priv->entry_menu_popdown_timeout = 0; } priv->in_entry_menu = TRUE; g_signal_connect (menu, "unmap", G_CALLBACK (ghb_cell_renderer_text_popup_unmap), data); } static gboolean ghb_cell_renderer_text_focus_out_event (GtkWidget *entry, GdkEvent *event, gpointer data) { GhbCellRendererTextPrivate *priv; priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (data); if (priv->in_entry_menu) return FALSE; g_object_set(entry, "editing-canceled", TRUE, NULL); gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (entry)); gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (entry)); /* entry needs focus-out-event */ return FALSE; } static GtkCellEditable * ghb_cell_renderer_text_start_editing (GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, MyGdkRectangle *background_area, MyGdkRectangle *cell_area, GtkCellRendererState flags) { GhbCellRendererText *celltext; GhbCellRendererTextPrivate *priv; celltext = GHB_CELL_RENDERER_TEXT (cell); priv = GHB_CELL_RENDERER_TEXT_GET_PRIVATE (cell); /* If the cell isn't editable we return NULL. */ if (celltext->editable == FALSE) return NULL; gint xalign; g_object_get(cell, "xalign", &xalign, NULL); priv->entry = g_object_new (GTK_TYPE_ENTRY, "has-frame", FALSE, "xalign", xalign, NULL); if (celltext->text) gtk_entry_set_text (GTK_ENTRY (priv->entry), celltext->text); g_object_set_data_full (G_OBJECT (priv->entry), I_(GHB_CELL_RENDERER_TEXT_PATH), g_strdup (path), g_free); gtk_editable_select_region (GTK_EDITABLE (priv->entry), 0, -1); #if 0 GtkRequisition min_size, size; gtk_widget_get_preferred_size(priv->entry, &min_size, &size); if (min_size.height > size.height) size.height = min_size.height; if (min_size.width > size.width) size.width = min_size.width; if (size.height < cell_area->height) { GtkBorder *style_border; GtkBorder border; gtk_widget_style_get (priv->entry, "inner-border", &style_border, NULL); if (style_border) { border = *style_border; g_boxed_free (GTK_TYPE_BORDER, style_border); } else { /* Since boxed style properties can't have default values ... */ border.left = 2; border.right = 2; } border.top = (cell_area->height - size.height) / 2; border.bottom = (cell_area->height - size.height) / 2; gtk_entry_set_inner_border (GTK_ENTRY (priv->entry), &border); } #endif priv->in_entry_menu = FALSE; if (priv->entry_menu_popdown_timeout) { g_source_remove (priv->entry_menu_popdown_timeout); priv->entry_menu_popdown_timeout = 0; } g_signal_connect (priv->entry, "key-press-event", G_CALLBACK (ghb_cell_renderer_text_keypress), celltext); g_signal_connect (priv->entry, "editing_done", G_CALLBACK (ghb_cell_renderer_text_editing_done), celltext); priv->focus_out_id = g_signal_connect_after (priv->entry, "focus_out_event", G_CALLBACK (ghb_cell_renderer_text_focus_out_event), celltext); priv->populate_popup_id = g_signal_connect (priv->entry, "populate_popup", G_CALLBACK (ghb_cell_renderer_text_populate_popup), celltext); gtk_widget_show (priv->entry); return GTK_CELL_EDITABLE (priv->entry); } /** * ghb_cell_renderer_text_set_fixed_height_from_font: * @renderer: A #GhbCellRendererText * @number_of_rows: Number of rows of text each cell renderer is allocated, or -1 * * Sets the height of a renderer to explicitly be determined by the "font" and * "y_pad" property set on it. Further changes in these properties do not * affect the height, so they must be accompanied by a subsequent call to this * function. Using this function is unflexible, and should really only be used * if calculating the size of a cell is too slow (ie, a massive number of cells * displayed). If @number_of_rows is -1, then the fixed height is unset, and * the height is determined by the properties again. **/ void ghb_cell_renderer_text_set_fixed_height_from_font (GhbCellRendererText *renderer, gint number_of_rows) { g_return_if_fail (GHB_IS_CELL_RENDERER_TEXT (renderer)); g_return_if_fail (number_of_rows == -1 || number_of_rows > 0); if (number_of_rows == -1) { gint width; g_object_get(renderer, "width", &width, NULL); gtk_cell_renderer_set_fixed_size (GTK_CELL_RENDERER (renderer), width, -1); } else { renderer->fixed_height_rows = number_of_rows; renderer->calc_fixed_height = TRUE; } } HandBrake-0.10.2/gtk/src/create_resources.py0000664000175200017520000001010112302427075021315 0ustar handbrakehandbrake#! /bin/python # import types import os import sys import time import datetime import plistlib import getopt from xml.parsers import expat from gtk import gdk pl = dict() stack = list() stack.append(pl) def top(ss): return ss[len(ss)-1] def end_element_handler(tag): global stack if tag == "section": stack.pop() def start_element_handler(tag, attr): global pl, stack current = top(stack) key = val = None if tag == "section": key = attr["name"] if key == "icons": val = dict() stack.append(val) elif tag == "icon": fbase = attr["file"] fname = find_file(fbase) key = attr["name"] if fname != None and key != None: val = dict() pb = gdk.pixbuf_new_from_file(fname) val["colorspace"] = pb.get_colorspace() val["alpha"] = pb.get_has_alpha() val["bps"] = pb.get_bits_per_sample() val["width"] = pb.get_width() val["height"] = pb.get_height() val["rowstride"] = pb.get_rowstride() val["data"] = plistlib.Data(pb.get_pixels()) elif fname == None: print >> sys.stderr, ( "Error: No such icon file %s" % fbase ) sys.exit(1) elif tag == "plist": fbase = attr["file"] fname = find_file(fbase) key = attr["name"] if fname != None and key != None: val = plistlib.readPlist(fname) elif fname == None: print >> sys.stderr, ( "Error: No such plist file %s" % fbase ) sys.exit(1) elif tag == "string": fbase = attr["file"] fname = find_file(fbase) key = attr["name"] if fname != None and key != None: try: ff = open(fname) val = ff.read() except Exception, err: print >> sys.stderr, ( "Error: %s" % str(err) ) sys.exit(1) elif fname == None: print >> sys.stderr, ( "Error: No such string file %s" % fbase ) sys.exit(1) if val != None: if type(current) == types.DictType: current[key] = val elif type(current) == types.TupleType: current.append(val) def cdata_handler(str): return def resource_parse_file(infile): parser = expat.ParserCreate() parser.StartElementHandler = start_element_handler parser.EndElementHandler = end_element_handler parser.CharacterDataHandler = cdata_handler parser.ParseFile(infile) def usage(): print >> sys.stderr, ( "Usage: %s [-I ] [resource plist]\n" "Summary:\n" " Creates a resource plist from a resource list\n\n" "Options:\n" " I - Include path to search for files\n" " Input resources file\n" " Output resources plist file\n" % sys.argv[0] ) inc_list = list() def find_file(name): global inc_list for inc_dir in inc_list: inc = "%s/%s" % (inc_dir, name) if os.path.isfile(inc): return inc if os.path.isfile(name): return name return None def main(): global inc_list OPTS = "I:" try: opts, args = getopt.gnu_getopt(sys.argv[1:], OPTS) except getopt.GetoptError, err: print >> sys.stderr, str(err) usage() sys.exit(2) for o, a in opts: if o == "-I": # add to include list inc_list.append(a) else: assert False, "unhandled option" if len(args) > 2 or len(args) < 1: usage() sys.exit(2) try: infile = open(args[0]) except Exception, err: print >> sys.stderr, ( "Error: %s" % str(err) ) sys.exit(1) if len(args) > 1: try: outfile = open(args[1], "w") except Exception, err: print >> sys.stderr, ( "Error: %s" % str(err)) sys.exit(1) else: outfile = sys.stdout resource_parse_file(infile) plistlib.writePlist(pl, outfile) main() HandBrake-0.10.2/gtk/src/ini_to_plist.c0000664000175200017520000000443212067154516020265 0ustar handbrakehandbrake#include #include #include #include #include "values.h" #include "plist.h" gboolean string_is_true(const gchar *str) { return (strcmp(str, "enable") == 0); } gboolean string_is_bool(const gchar *str) { return (strcmp(str, "enable") == 0) || (strcmp(str, "disable") == 0); } GType guess_type(const gchar *str) { gchar *end; gdouble dval; if (*str == 0) return G_TYPE_STRING; if (string_is_bool(str)) return G_TYPE_BOOLEAN; dval = g_strtod(str, &end); if (*end == 0) { if (strchr(str, '.') == NULL) return G_TYPE_INT64; else return G_TYPE_DOUBLE; } return G_TYPE_STRING; } void set_value(GValue *gval, const gchar *str, GType gtype) { if (gtype == G_TYPE_STRING) { g_value_set_string(gval, str); } else if (gtype == G_TYPE_INT64) { gint64 val = g_strtod(str, NULL); g_value_set_int64(gval, val); } else if (gtype == G_TYPE_DOUBLE) { gdouble val = g_strtod(str, NULL); g_value_set_double(gval, val); } else if (gtype == G_TYPE_BOOLEAN) { if (string_is_true(str)) g_value_set_boolean(gval, TRUE); else g_value_set_boolean(gval, FALSE); } } int main(gint argc, gchar *argv[]) { GKeyFile *kf; gchar **groups; gchar **keys; gint ii, jj; GValue *top; GValue *dict; g_type_init(); top = ghb_dict_value_new(); kf = g_key_file_new(); g_key_file_load_from_file(kf, argv[1], 0, NULL); groups = g_key_file_get_groups(kf, NULL); for (ii = 0; groups[ii]; ii++) { dict = ghb_dict_value_new(); ghb_dict_insert(top, , g_strdup(groups[ii]), dict); keys = g_key_file_get_keys(kf, groups[ii], NULL, NULL); for (jj = 0; keys[jj]; jj++) { gchar *str; GValue *gval; GType gtype; str = g_key_file_get_string(kf, groups[ii], keys[jj], NULL); gtype = guess_type(str); gval = g_malloc0(sizeof(GValue)); g_value_init(gval, gtype); set_value(gval, str, gtype); ghb_dict_insert(dict, g_strdup(keys[jj]), gval); } } ghb_plist_write_file("a_p_list", top); } HandBrake-0.10.2/gtk/src/renderer_button.c0000664000175200017520000002051712303157614020767 0ustar handbrakehandbrake#include "marshalers.h" #include "renderer_button.h" #define MyGdkRectangle const GdkRectangle /* Some boring function declarations: GObject type system stuff */ static void custom_cell_renderer_button_init (CustomCellRendererButton *cellprogress); static void custom_cell_renderer_button_class_init (CustomCellRendererButtonClass *klass); static void custom_cell_renderer_button_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec); static void custom_cell_renderer_button_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec); static void custom_cell_renderer_button_finalize (GObject *gobject); // My customized part that adds "clicked" signal static gboolean custom_cell_renderer_button_activate ( GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, MyGdkRectangle *background_area, MyGdkRectangle *cell_area, GtkCellRendererState flags); enum { CLICKED, LAST_SIGNAL }; static guint button_cell_signals[LAST_SIGNAL] = { 0 }; static gpointer parent_class; /*************************************************************************** * * custom_cell_renderer_button_get_type: here we register our type with * the GObject type system if we * haven't done so yet. Everything * else is done in the callbacks. * ***************************************************************************/ GType custom_cell_renderer_button_get_type (void) { static GType cell_button_type = 0; if (cell_button_type == 0) { static const GTypeInfo cell_button_info = { sizeof (CustomCellRendererButtonClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) custom_cell_renderer_button_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (CustomCellRendererButton), 0, /* n_preallocs */ (GInstanceInitFunc) custom_cell_renderer_button_init, }; /* Derive from GtkCellRendererPixbuf */ cell_button_type = g_type_register_static (GTK_TYPE_CELL_RENDERER_PIXBUF, "CustomCellRendererButton", &cell_button_info, 0); } return cell_button_type; } /*************************************************************************** * * custom_cell_renderer_button_init: set some default properties of the * parent (GtkCellRendererPixbuf). * ***************************************************************************/ static void custom_cell_renderer_button_init (CustomCellRendererButton *cellbutton) { g_object_set(cellbutton, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); g_object_set(cellbutton, "xpad", 2, NULL); g_object_set(cellbutton, "ypad", 2, NULL); } /*************************************************************************** * * custom_cell_renderer_button_class_init: * * set up our own get_property and set_property functions, and * override the parent's functions that we need to implement. * If you want cells that can be activated on their own (ie. not * just the whole row selected) or cells that are editable, you * will need to override 'activate' and 'start_editing' as well. * ***************************************************************************/ static void custom_cell_renderer_button_class_init (CustomCellRendererButtonClass *klass) { GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(klass); GObjectClass *object_class = G_OBJECT_CLASS(klass); parent_class = g_type_class_peek_parent (klass); object_class->finalize = custom_cell_renderer_button_finalize; /* Hook up functions to set and get our * custom cell renderer properties */ object_class->get_property = custom_cell_renderer_button_get_property; object_class->set_property = custom_cell_renderer_button_set_property; // Override activate cell_class->activate = custom_cell_renderer_button_activate; button_cell_signals[CLICKED] = g_signal_new (g_intern_static_string ("clicked"), G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (CustomCellRendererButtonClass, clicked), NULL, NULL, ghb_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); } /*************************************************************************** * * custom_cell_renderer_button_finalize: free any resources here * ***************************************************************************/ static void custom_cell_renderer_button_finalize (GObject *object) { /* If you need to do anyting with the renderer button ... CustomCellRendererProgress *cellrendererbutton = CUSTOM_CELL_RENDERER_BUTTON(object); */ /* Free any dynamically allocated resources here */ (* G_OBJECT_CLASS (parent_class)->finalize) (object); } /*************************************************************************** * * custom_cell_renderer_button_get_property: as it says * ***************************************************************************/ static void custom_cell_renderer_button_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *psec) { //CustomCellRendererButton *cellbutton = CUSTOM_CELL_RENDERER_BUTTON(object); switch (param_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, psec); break; } } /*************************************************************************** * * custom_cell_renderer_button_set_property: as it says * ***************************************************************************/ static void custom_cell_renderer_button_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec) { //CustomCellRendererButton *cellbutton = CUSTOM_CELL_RENDERER_BUTTON(object); switch (param_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec); break; } } /*************************************************************************** * * custom_cell_renderer_button_new: return a new cell renderer instance * ***************************************************************************/ GtkCellRenderer * custom_cell_renderer_button_new (void) { return g_object_new(CUSTOM_TYPE_CELL_RENDERER_BUTTON, NULL); } static gboolean custom_cell_renderer_button_activate ( GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, MyGdkRectangle *background_area, MyGdkRectangle *cell_area, GtkCellRendererState flags) { g_debug("custom_cell_renderer_button_activate ()\n"); g_signal_emit (cell, button_cell_signals[CLICKED], 0, path); return TRUE; } HandBrake-0.10.2/gtk/src/x264handler.h0000664000175200017520000000242312505047552017633 0ustar handbrakehandbrake/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ /* * x264handler.h * Copyright (C) John Stebbins 2008-2015 * * x264handler.h is free software. * * You may redistribute it and/or modify it under the terms of the * GNU General Public License, as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * callbacks.h is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with callbacks.h. If not, write to: * The Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301, USA. */ #if !defined(_X264HANDLER_H_) #define _X264HANDLER_H_ #include "settings.h" void ghb_x264_init(signal_user_data_t *ud); void ghb_x264_parse_options(signal_user_data_t *ud, const gchar *options); gint ghb_lookup_badapt(const gchar *options); void ghb_show_hide_advanced_video( signal_user_data_t *ud ); void ghb_update_x264Option(signal_user_data_t *ud, const char *opt); #endif // _X264HANDLER_H_ HandBrake-0.10.2/gtk/ghb.anjuta0000664000175200017520000000173011026035545016572 0ustar handbrakehandbrake HandBrake-0.10.2/NEWS0000664000175200017520000013520112531664363014551 0ustar handbrakehandbrakeNEWS file for HandBrake Changes between 0.10.1 and 0.10.2 - Assorted bug fixes and performance improvements. Changes between 0.10.0 and 0.10.1 - Assorted bug fixes for all three GUI's and the core library. Changes between 0.9.9 and 0.10 Core - Intel QuickSync Video Encode / Decode support. (Beta) Windows only currently. We hope to bring this to Linux user in the future. - DXVA Hardware Decode support (Experimental) Windows Only. Suitable only for slower machines so disabled in preferences by default. - Choice of Scalers Lanczos HandBrakes? default scaler. Bicubic (OpenCL) (Experimental) Available on the Command Line (All Platforms) and Windows GUI. (Mac / Linux GUI's will come in a later release) Currently only available in OpenCL form so requires a AMD or Intel GPU supporting OpenCL 1.1 or later. Nvidia GPU's are not currently supported. When downscaling, up to 5% performance improvement can be achieved. No benefit when not downscaling. Small loss in quality over the Lanczos scaler. - Denoise hqdn3d filter now accepts individual settings for both chroma channels (Cr, Cb) New NlMeans? denoiser. This is very slow, but results are significantly better than hqdn3d. - Presets Added Windows Phone 8 Preset - Updated Libraries x264 r2479-dd79a61 Libav v10.1 libbluray 0.5.0 - Libavformat is now used for muxing instead of mp4v2 and libmkv "Large File Size" checkbox has now bee removed for mp4, as the new muxer will transition to 64bit files automatically. mpeg2dec has also been replaced in favour of using libav - The LibAV AAC encoder is now the default as FAAC has been removed. This encoder is adequate for most, but until it improves a bit further, we have enabled support for the FDK-AAC encoder also. This FDK option is a temporary measure until the LibAV encoder improves. Note that FDK-AAC is much slower and will likely bottleneck the encode process, but will produce better quality audio. - H.265 encoder is now available through x265 1.4. While this encoder is still fairly new, we have seen some promising results come out of it. It's still under heavy active development and is only going to improve over time! - Added VP8 Encoder (using libvpx) Available in MKV files only. - Removed mcdeint deinterlace and decomb modes. This relied on the snow encoder in libav which has been was removed by upstream. - Bug fixes and Misc Improvements Windows - Accessibility / Usability Improvements Added option to 'Use System Colors'. The app should now be usable in a High Contrast mode. Fixed Tab ordering to make the app more keyboard friendly. - LibHB is now used for scanning instead of the CLI. Experiential Preview window is now available as a result. (Can be enabled via preferences) - Improved the layout and design of the Audio and Subtitle tabs. Split buttons similar to the old 0.9.8 WinForms? GUI Improved layout on the track listbox to make better use of the space. - Improvements to Auto-Naming feature. - When Done Added an option that will reset this to 'Do nothing' when the app is closed and restarted. - Presets New 'Presets' Menu Presets bar can now be hidden if it's not wanted. - Numerous bug fixes Fixed the Issue in the source dropdown where the drive menu items did not work when clicked. Numerous fixes in the Picture settings panel around resolution calculation. Numerous fixes around the way Presets are loaded and saved, particularly around Picture settings. Removed Growl for Windows support due to bugs and issues with this library that remain unfixed. Project appears abandoned. Many misc other fixes. - Windows XP is no longer supported. Windows Vista with Service Pack 2 or later is now a requirement. - The 32bit build of the application is now considered deprecated. This is due to 32bit process memory limitations. Mac - Build system updates to compiled under OS X 10.9 / 10.10 - Automatic audio and subtitle track selection behaviours which can be stored per preset. - Improvements to Auto-Naming feature. - Misc UI enhancements. - Bug fixes and Misc Improvements Linux - Automatic audio and subtitle track selection behaviours which can be stored per preset. - Improvements to Auto-Naming feature. - Batch Add to queue by list selection. - Russian and Czech Translations - Bug fixes and Misc Improvements - Requires GTK3 Command Line Interface - Basic support for return codes from the CLI. (0 = No Error, 1 = Cancelled, 2 = Invalid Input, 3 = Initialization error, 4 = Unknown Error") - Bug fixes and Misc Improvements Changes between 0.9.8 and 0.9.9: General - Improved HandBrake pineapple icon by Matt Johnson - Improved Retina-resolution icons within the application, by Nik Pawlak + http://nikpawlak.com Core - Blu-ray (PGS) subtitle support + works with Foreign Audio Search + can be Burned-In + can be passed through to MKV (but not MP4) - Additional video framerates + 30, 50, 59.94, 60 fps - Double framerate ("bob") mode for the deinterlace and decomb filters - Better audio remix support + additional mixdowns: 6.1, 7.1, 7.1 (5F/2R/LFE) CLI users should note 6ch becomes 5point1 + better-than-Stereo sources can be upmixed to 5.1 + discard one channel from Stereo sources Mono (Left Only), Mono (Right Only) - Allow the selection of higher audio bitrates where appropriate - Allow the selection of lower audio samplerates where appropriate + 8, 11.025, 12, 16 kHz - Audio dithering (TPDF) when converting to 16-bit FLAC - Use libavcodec for DTS audio decoding (instead of libdca) + DTS-ES 6.1 Discrete support - All graphical interfaces: support for x264's preset, tune and profile options + alternative to the Advanced panel (which is still available) + HandBrake-specific option to ensure compliance with a specific H.264 level - Updated built-in presets + take advantage of x264 preset/tune/profile support + removed increasingly suboptimal and irrelevant Legacy presets - Assorted bug fixes and improvements to the core library (libhb) - Updated libraries + x264 r2273-b3065e6 + Libav v9.6 + libbluray 0.2.3 Windows - User Interface has been re-written in WPF + Includes many small UI enhancements - Switched to .NET 4 Client Profile + smaller download for those who don't have .NET 4 Full installed Mac - Assorted bug fixes + including better support for Retina displays - Prevent sleep during encoding and scanning for Mountain Lion - Drag & Drop files onto the Main window or application icon to scan - Nicer progress indication on the dock icon - Preview window improvements and bugfixes - Updated Growl to 2.0.1 + Notification Center support (when Growl is not installed) Linux - Assorted bug fixes and improvements - Use some system libraries rather than bundling + fontconfig, freetype, libxml2, libass, libogg, libvorbis, libtheora and libsamplerate Command Line Interface - Audio option (-a) will ignore invalid input tracks and encode with only the valid ones - Allow use of hh:mm:ss format when specifying p-to-p start/stop time - Advanced audio options + enable level normalization when downmixing (disabled by default) + disable audio dithering or select a specific algorithm Changes betwen 0.9.7 and 0.9.8: - Corrects a few crash bugs that showed up in 0.9.7 Changes betwen 0.9.6 and 0.9.7: - This is a bug fix release for 0.9.6. - Includes an AppleTV3 Preset and updated iPad Preset Changes betwen 0.9.5 and 0.9.6: Encoders: -> Video: - updated libx264 (revision 2146) - MPEG-2 encoder (from libavcodec) - advanced options support for libavcodec encoders format: option1=value1:option2=value2 -bf 2 -trellis 2 becomes bf=2:trellis=2 -> Audio: - audio gain control (increase/decrease audio volume) - updated libogg (1.3.0) and libvorbis (aoTuV b6.03) - new AAC encoder (from libavcodec) (considered experimental) (supported mixdowns: Mono/Stereo/Dolby, 5.1 will come later) (should be on par with faac in terms of quality, sometimes better) - FLAC encoder (16-bit, MKV container only) - Mac OS X: HE-AAC encoding support, via Core Audio - quality-based variable bitrate encoding support works with: Lame MP3, Vorbis, Core Audio AAC only implemented in CLI and Linux GUI - AC3 encoder: set Dolby Surround flag in stream parameters when mixdown is Dolby Surround or Pro Logic II -> Audio Passthru: - DTS Passthru to MP4 container (in addition to MKV) (supported by e.g. VLC, MPlayer) - DTS-HD Passthru (MP4, MKV containers) - MP3 Passthru (MP4, MKV containers) - AAC Passthru (MP4, MKV containers) (known issue: Magic Cookie not passed through from MPEG Program/Transport streams, which will break playback in e.g. QuickTime Player) - Auto Passthru: one encoder, multiple codecs lets you define allowed codecs (from supported passthru codecs) lets you pick a fallback encoder if passthru is not possible Muxers: - start MKV clusters with a video keyframe whenever possible should improve seeking and DLNA streaming - bug fix: use ISO 639-2 bibliographic form for MKV language codes - bug fix: fix crash due to division by zero in MP4 muxer - bug fix: fix muxing of Closed Captions. Improper interleaving broke playback on some players Decoders: -> Video: - updated libav* libraries from Libav (http://libav.org/) (v0.7-1696-gcae4f4b, late October 2011) frame-based multithreading for H.264, VP8 10-bit decoding support for H.264, DNxHD Apple ProRes decoding support - improved average framerate detection - duplicate frame detection for improved frame drop decision (CFR/PFR) - new Same as source, Constant Framerate option for devices that don't support variable framerate automatically picks a constant framerate closest to the detected average framerate - bug fix: fix problem when resolution changes in the middle of a video stream -> Audio: - Blu-ray: make TrueHD, DTS-HD and E-AC3 Secondary Audio streams available for decoding and/or passthrough - bug fix: libavcodec-decoded streams can now be decoded multiple times previously, multiple decodes of the same source tracks weren't possible and audio output tracks had to be dropped - bug fix: fix audio screech at beginning of some audio tracks when decoding AC3 - bug fix: fix DTS decoder audio volume (was too low) - bug fix: garbled audio when decoding DTS-HD audio from MKV containers - bug fix: fix support for DTS-HD High Resolution Audio in MPEG Transport streams -> Subtitles: - updated libass (0.10.0) - improved handling of subtitles with overlapping timestamps - improved handling of DVD subtitles without Stop Display commands - SSA subtitles are now passed through to MKV without conversion to SRT/UTF-8 - bug fix: fix rendering problem with SSA subtitles when no font is embedded in the source video Demuxers: - improved MPEG Program/Transport stream support support for MPEG-1 Program streams support for HD-DVD EVOB streams improved handling of Transport streams that have no PCR - WTV container support (via libavformat) - bug fix: files with more than 20 tracks (video, audio, subtitles etc.) are now supported - bug fix: some QuickTime MOV files were misdirected as MPEG Transport streams - bug fix: fix detection of TrueType font attachments that don't have the correct MIME type Filters: - new, much improved decomb filter (but slower) new settings are default old settings become decomb "Fast" Presets: - improved "Normal" preset (much faster, similar file size and quality) - removed obsolete legacy presets - added new device presets for Android phones/tablets recent and/or powerful devices recommended Mac OS X: -> DVD decryption: - VLC 2.x or later will not work for DVD decryption and is therefore unsupported as of HandBrake 0.9.6 - libdvdcss is now the preferred method for DVD decryption already works with HandBrake 0.9.5 a .pkg installer is available from Videolan: http://download.videolan.org/libdvdcss/last/macosx/ -> Build system: - support for Xcode 4 and Mac OS X 10.7 "Lion" - Mac OS X 10.5 "Leopard" no longer supported Mac OS X GUI: -> OS X 10.7 Lion support: - bug fix: Live Preview window widgets updated to work under Lion - bug fix: fixed positioning of widgets in the Audio panel under Lion -> Other: - wider main window providing more room for various widgets Windows GUI: -> Preview window: - complete redesign - support for VLC or the system default video player - dropped built-in QuickTime playback support -> Other: - much improved control over the default audio and subtitle tracks selected (see Options) - ability to set the minimal title length that will show up during a scan (see Options) - several other usability improvements - installer now has a silent option for easier network installs (launch the installer with /S) Linux GUI: -> Audio panel: - new advanced audio options section for gain and audio track names - dynamic range compression and samplerate moved to advanced audio options -> Other: - minor UI tweaks and usability enhancements Miscellaneous: - Target Size is gone, and isn't coming back Don't bother complaining on the forums - CLI: support for x264 presets, tunes and profiles new --x264-preset, --x264-tune and --x264-profile options - DVD: fix issues with some discs (e.g. True Grit, Thor, Transformers 3) - DVD: improved main feature detection - updated libbluray (0.0.1-pre-213-ga869da8, late May 2011) Changes between 0.9.4 and 0.9.5: * Core Library - BluRay disc structure support. (No decryption support) - Updated Libraries (x264, ffmpeg) - SSA Subtitle support. (Including burn-in) - MP3 audio now supported in MP4 files (Note: Limited Player compatibility) - VOBSUB subtitle now supported in MP4 files (Note: Limited Player compatibility) - Updated Presets for newer devices and better quality - AC3 encoding support. - Many Bug fixes and other small improvements - Improved DVD Main Feature detection (when using dvdnav) - Universal audio downmix support (all audio types can be downmixed) *All GUIs - Updated x264 Advanced Panel - Video Quality Slider drops % value and only shows RF for x264 - Batch Scan (Scan Multiple files at once. N.B: Does not include multiple VIDEO_TS folders / Image files) - Peak framerate option (Capped VFR) - Many Bug fixes - Many Tweaks to improve usability. - Ability to edit queue jobs - Point-to-Point encoding (second or frame start and end times) * Mac GUI - New Audio Panel supporting >4 Audio Tracks - VLC detection in /Applications and ~/Applications * Windows GUI - Encode Status in GUI. (CLI window is now always hidden) - Improved Auto-Naming for Destination file name. - Drag / Drop Video onto Main Window to scan. * Linux GUI - Multiple instance support (run multiple copies of ghb at once) - Many Bug fixes and UI improvements. Changes between 0.9.3 and 0.9.4: Core: * New build system, allowing 64-bit binaries (around 10% faster) * Soft subtitles and Closed Captions: - DVD Closed Captions - ATSC Closed Captions - SRT subtitle import - Text soft subtitles in MP4 and MKV output - Bitmap soft subtitles in MKV output * Better support for DVD inputs: - Uses libdvdnav - DVD angles support - Workaround for libdvdread malloc bomb on invalid PGC entry - DVD drive region detection support in Linux - Handles DVD programs with more than 16 streams - No longer tries to detect and discard duplicate titles when scanning - Libdvdnav patched to perform read error recovery - Libdvdread patched to allow raw device access in Windows - Handles poorly mastered DVDs that had the menus ripped out of them * Better support for non-DVD inputs: - Preserves MP4 metadata - TrueHD - DTS-HD demuxing - 8 bit audio - Better handling of transport streams where audio starts first - Better handling of transport streams that have been spliced together, leading to duplicate timestamps - Better VC-1 frame detection - Fixes bug that was causing one sec. of audio to be dropped on many ffmpeg files - Looks harder for aspect ratio info from DV sources - No longer truncates the last (dummy) chapter - Allows specifying field parity for detelecine and decomb * Better AV sync * Support for sources with no audio * DTS passthrough for MKV * x264 bumped from r1169 to r1347, which means speed optimizations, new default settings (see r2742 commit comment), the magic of macroblock tree rate control (mbtree), a new CRF curve (meaning you will get different, generally lower bitrates at the same RF, with similar quality metrics), and weighted P-Frames (disabled by default for Baseline encodes and the AppleTV preset). * Better sample interleaving * Better, optional deinterlacer for decomb (EEDI2) * New mode structure for the decomb filter * Variable verbose logging levels * Fixed timing for first two frames coming out of filters * Libtheora bumped to 1.1.0 * Improvements to our theora implementation (2 pass encoding + soft target rate control) * Caters to Theora's insistence on content having mod16 framing dimensions specified * Flushes LAME encoder's final packets * Fixed interjob framerate calculation * Fixed pthreads regression in cygwin * Tweaks for packaging tools * Solaris 10 build support All interfaces: * Live video preview * New subtitle tab * New filters and picture settings inspector * Custom anamorphic mode * Updated Sparkle * Custom number of preview images * Quality slider now works off actual rate factor/quantizer values instead of percentages * Partially updated advanced x264 tab * New built-in presets * Use libdvdnav by default on DVD sources * Removed Constant QP encoding option for x264 (use CRF) * Various bug fixes and UI tweaks * x264 turbo 1st pass mode now uses subme=2 not subme=1 Mac: * Core Audio AAC encoding * H.264 video source decoding crash fixed * Queue displays varying row heights based on encode settings * Fixed EyeTV package scanning * 64bit / 32 bit VLC detection * Preset import/export Windows: * New audio tab * AAC audio source decoding bug fixed * Tray minimization is now optional * Queue can now be started from main window * Growl for Windows notification support * General UI improvements * Preset import * Preferred language control for audio dubs and subtitles * Fixed file extensions resetting to m4v when enabling chapter markers in mkv * Faster updating of GUI elements from CLI data * Cleanup / Improved some of the programs options. (Growl, use m4v, drive detection) * Numerous fixes in the Picture Settings Panel and CLI Query Handling code. * Bug Fixes and Usability improvements. Linux: * General UI improvements * Inhibits sleep mode while encoding * Single title scan * Chapter duration display * Notifications when encodes complete * Tray minimization * Full screen preview * Preset import/export * Preferred language control for audio dubs and subtitles * Preferences rearrangement * Preference to auto-apply .m4v extension * New system tray icon behavior * Preference for what to do when encode completes * Preference for how often to delete activity logs * Preference to disable automatic scanning * New Gnome session manager support * Improved "auto" audio selection * Use .m4v as the default extension for the MPEG-4 container * Use .m4v when soft subs are enabled * Alternate angle encoding fix * Only strips drive letters for Windows builds * Show correct audio format info when it's been sanitized for incompatibilities * Preserve chapter list modifications made to queued jobs * Fixed error when navigating chapter titles with the keyboard * Bug Fixes. CLI: * Options to handle new subtitle, anamorphic, and preview features * --srt-file, --srt-codeset, --srt-offset, --srt-lang, --srt-default * --native-dub option lets users request dubbing instead of subs when the audio isn't in their native language * Allow encoding sources with no audio without explicitly stating -a none * Update checker on MinGW built exe should now work correctly. * Matches GUIs' default verbosity level of 1 Changes between 0.9.2 and 0.9.3: 0.9.3 Snapshot 5 -> 0.9.3 Final - Better PMT processing - Basic underlying support for live previews (encode from a seek point for a set number of frames) - Better searching for IDR frames in H.264 streams - Preset changes (iPhone goes CRF, some old Apple presets resurrected as Legacy) - Assorted bug fixes 0.9.3 Snapshot 4 -> 0.9.3 Snapshot 5 (r1913) Core Library - VC-1 stream input - Newer libmp4v2, which fixes the issue with output > 2 gigs in Linux - Proper allocation for preview frames - Avoids corruption of previews of sources that use widths that aren't cleanly divisible by 8 - Decodes DTS internally instead of using ffmpeg, to allow mixdowns - Better support for DTS in MKV files with implicit timestamps or large timestamp errors - Ensures proper chroma size by rounding up when dealing with odd dimensions - Ensures "auto" samplerate sends a valid value to faac (22050, 24000, 32000, 44100, or 48000) - Bumped Theora to 1.0 final - Bumped x264 to r1024, which includes Nehalem optimizations as well as speed boosts for things such as b-adapt=2 Mac GUI - Allows multibyte characters in chapter titles Windows GUI - Fixes issue parsing presets that use maxWidth and maxHeight (-X and -Y) - DRC defaults to 1 now - Chapter markers disabled for non-DVD sources - Makes sure Normal preset gets loaded - Fixes arithmetic overflow crash when scanning Linux GUI - Update checker - Limits range of chapters to encode to the number of chapters on the DVD - Disabled entry of dimensions larger than the source CLI - Allows overriding of audio (tracks, bitrates, samplerates, codecs, mixdowns) and x264 options in built-in presets Documentation - Documentation updates have begun on the wiki, although they are not yet complete 0.9.3 Snapshot 3 -> 0.9.3 Snapshot 4 (r1896) Core Library - Converts video in other color spaces to YUV420 (this means DV support) - Official, standards-based AC3-in-MP4 - Tries to base the AV timing for streams off audio when possible - Keeps some audio fixes for lost packets in over the air streams from interfering with other sources - Handles rendering of sources where the picture resolution changes mid-stream (this fixes the long-standing bug reading a particular episode of Doctor Who) - Wider window for clock references (AV sync) - Fixed a crash when closing out data for AAC encoding on aborted encodes - Rejiggered verbose activity log display to be more laconic by default - Updated x264 to r1016, which means b-rdo and bime are gone and replaced with new subme modes - DTS and HDMV DTS audio support in streams - Doesn't show the audio track button on iPhones/iPod Touches unless there's more than 1 track - Tries to avoid garbage data from AC3 sync by searching for two agreeing packets - As the MPEG4IP project is defunct, switched to an independently maintained libmp4v2 which has folded in all our cumbersome patches - Fixed SunOS compilation - Fixed conflict between maxHeight and maxWidth and loose anamorphic - Warn in the log when titles are being ignored during scan for lack of audio - Fixed bug with Slow/Slowest deinterlacing and decomb which could leave a flickering line at the top or bottom of the screen - Extracts audio and subtitle types from DVD sources, to do things like label commentary tracks - Better handling of the beginning of AVI and WMV sources that start after time 0 - Optimize MP4 for web download works with AC3 tracks now Mac GUI - Nested presets - Individual activity logs for each encode (stored by default in ~/Application Support/HandBrake, can be co-located with encoded file destination by preference) - Allows reading from ZFS volumes - Fixed target size mode. It keeps breaking itself. Maybe it should just be put out of its misery... - Assorted other improvements Windows GUI - Nested presets - Individual activity logs for each encode - Slow and slower deinterlacing and decomb work now in Windows - Added resizeable update window - Fixed parsing of non-DVD source audio formats - Restored Copy to Clipboard to the Activity Log Window, among other enhancements to it - Fixed bug with MKV presets showing up as .m4v - Assorted other improvements Linux GUI (GTK) - Nested presets - Individual activity logs for each encode - Allows pending queue items to be removed, and reloaded in the main window for editing - Better handling of HD previews - Assorted other improvements CLI - Updated presets to the equivalent of the nested ones in the GUIs - Allows setting custom audio track names in MP4 files - Allows selection of the COLR atom in MP4 files, between Bt.601 and Bt.709 - Fixed reading of device paths in OS X A special note on the new presets (they're collapsible-triangle-folder-thing-errific!) - Deux Six Quatre, Blind, Broke, and Bedlam are gone. They were dead weight. - iPod Low-Rez is now iPod Classic & iPod Nano - iPod High-Rez is now iPod Legacy - iPhone / iPod Touch is now iPhone & iPod Touch, so take care CLI users - Animation and Television now use the decomb and detelecine (VFR) filters - High Profile presets now use psy-trellis and the new subme 9 mode with B-frame RD refinement - AppleTV is now CRF, so sizes will vary with content - PS3 preset should be fixed - Constant Quality Rate still needs its quality % lowered, probably The keen reader is already asking "iPod Legacy? WTF is iPod High-Rez called iPod Legacy now?" The answer is Universal. The Universal preset is designed to play on all modern iPods (anything newer than the iPod 5.5G). It also plays on iPhones. It also plays on AppleTVs. It should also play just about anywhere else, hence the name. It is full anamorphic DVD resolution--no tricks with downscaling like stuff from the iTunes Store. It includes chapters, and has the first audio track in both AAC (DPL2 downmixed) and AC3 pass-thru, just like the AppleTV preset. In fact, it should give the same quality as the AppleTV preset, but faster...and at a larger file size. Like the AppleTV preset, it used CRF, so sizes will vary. 0.9.3 Snapshot2 -> 0.9.3 Snapshot 3 (r1797) Core Library: - Universal input support, utilizing libavcodec from the FFmpeg project for decoding non-MPEG-2 video - Newer, faster, better version of the x264 codec, including psychovisual optimizations - Better AV sync through full compliance with the MPEG Standard Target Decoder timing model - More accurate auto-cropping - Support for New Zealand and Norwegian HDTV broadcasts (H.264 and AAC-LATM in MPEG-TS) - Detelecine is now "VFR detelecine" by default, dropping some frames and extending others to make up lost time, old behavior of keeping duplicate frames is enabled by selecting a framerate besides "Same as source" - Threaded deinterlacing in Slow and Slower modes - Threaded and entirely rewritten decomb filter - Better audio resampling interpolator - Better gamma in QuickTime through the use of the COLR MP4 atom - Better constant quality encoding when using FFmpeg - Hopefully better cache and virtual memory performance by recycling buffers that were most recently used instead of least - Fix for MP4s with "negative duration" errors. - Set the detelecine filter to work better with PAL by using "loose" breaks - Fix for missing initial H.264 NAL units, improves reliability of 8x8dct - Fix for subtitle-scan with XviD encoding - Fix for crash at the end of 2nd pass using x264 - Deblock filter works now - Rewritten update system, so the core library can read a portion of Sparkle appcasts. - Updates for libsamplerate, libogg, xvidcore, libtheora, libmpeg2, lame, faac, and of course ffmpeg and x264. Mac GUI - Entirely rewritten and far more flexible queue that can be saved between sessions, capable of preserving queued items after a crash - Now requires vlc 0.9.xx to read protected dvd's in the users /Applications folder - Fix for 4x3 loose anamorphic to keep it from downscaling - Countless other improvements Windows GUI - Resolution calculation - Better preset bar - Better queue (including queue recovery feature) - Better activity log window - Improved UI (layout changes, animated x264 options, DVD drive detection, duration displayed) - More options - includes support for custom auto name format & starting the CLI minimized - Countless other improvements Linux GUI (GTK) - It's alive! Known Issues in Snapshot 3 - Possibility of a flickering line at the top or bottom of the frame after Slow or Slower deinterlacing or decombing - Input bitrate display may be off by a factor of 100 for H.264-in-TS sources - Constant Quality Rate preset probably needs a lower quality level (60% - 55%) - With non-DVD sources that don't have AC3 audio, you can't encode 1 input audio track to multiple output audio tracks - Slow and Slower deinterlacing and decombing are BROKEN in Windows - QuickTime won't read Xvid-in-MP4 output, although VLC will - Windows GUI does not detect all audio tracks from non-DVD sources 0.9.3 Snapshot 1 -> 0.9.3 Snapshot 2 (r1477) Core Library: - Anamorphic PAR for the AVI container - Allow constant frame rates when they different from the source's frame rate (otherwise pass through the variable MPEG-2 frame durations ) - Decomb filter (selectively deinterlaces when it sees interlacing in the frame) - Filter bug fixed, that would skip any filters after detelecine, if VFR wasn't enabled - Loose anamorphic + FFmpeg video bug fixed Windows GUI: - Title dropdown list bug fixed - Missing log file bug fixed CLI: - Default audio samplerate changed to 48kHz, audio bitrate changed to 160kbps. - Samplerate entry bug fixed 0.9.2 -> 0.9.3 Snapshot 1 (r1457) Core Library: - New audio subsystem (no more AAC+AC3, control each track's codec and settings individually) - Removed libdvdcss (HandBrake no longer decrypts DVDs on its own, will use VLC to do so if it's available) - Added Theora encoder - Fixed x264-in-avi and ffmpeg-in-avi - Fixed xvid - More accurate scaling - Major sync improvements - Major stream improvements - AAC+AC3 support in MKV - MKV seeking fixes - Make sure subtitles get displayed long enough to read them - Updated VBV 2-pass and VBV 1-pass patch for x264 - Adaptive Quantization for x264 - Recover from bad preview scans - Recover from invalid PGNs - Fixed vorbis bitrate control - Snapshot builds Mac: - New audio interface - Loads libdvdcss from VLC at runtime if it's present on the user's system - No more general-purpose "Codecs" menu -- set video and audio codecs individually - More robust preset system, in preparation for nested presets - Made 64-bit MP4 file widget more prominent - Only allow useful x264 options in the advanced tab - Various fixes and improvements Windows: - New x264 tab - New audio interface - Various fixes and improvements CLI: - New audio interface Changes between 0.9.1 and 0.9.2: CORE - AC3 in MP4 support - Multi-track audio support for Apple devices - Better handling of audio discontinuities - More flexible, "loose" anamorphic - Variable frame rate encoding - MP4 optimization for progressive downloads - Dynamic range compression for encoding from AC3 audio - Ability to encode an audio stream and pass it through at the same time - iPhone-compatible anamorphic (pasp atom) - Robust program and transport stream support - Better handling of DVD read errors from invalid VOB units - Detects and works around missing end of cell markers - Recovers from loss of signal in a stream - Drops subtitles less often - Keeps chapter markers in better sync and prevents duplicates - Better handling of B-Frames - Tunes FIFO sizes by CPU count - Finally squashes the bug that cut off the end of movies - Preset changes - Standardizes on standard out for progress and standard error for everything else. - Correct channel counts when passing AC3 audio to Matroska - Tag MP4 files as encoded with HandBrake - No more merging short chapters - Newer copies of x264, - VBV 2-pass patch for x264 - Sets keyframes for x264 by frame rate. - Support for >2GB MKV files in Linux - Code audio languages in a way QuickTime understands - Better subtitle positioning - Fewer crashes in 2-pass encoding MAC - Leopard Only - Sparkle - Reads .eyetv files as well as .dvdmedia files - Much better queue - More white space - Code restructuring - Activity window logging, complete with a "black box recorder" for crashes - Ability to open a single title for a DVD instead of scanning the whole thing - Warns people when they try to queue up two files with the same name - Maintains picture filter states between jobs - .xib Interface Builder files SVN can track - Switches to NSImageView for previews, so no more useless OpenGL effects - Temporary loss of localizations for foreign languages (the old system was broken anyway) - Separate filter settings for every queued job WIN - Revamped preset system - Sparkle-compatible update checker - Activity log window - CLI built-in preset parsing - No more admin rights required in Vista - Handles more display resolutions CLI - Built-in presets - Short names for denoising (weak, medium, strong) and deinterlacing (fast, slow, slower) - Solaris port - No more x264b30 (use -e x264 -I -x level=30:cabac=0 instead or better yet an iPod preset) - Chapter marker .csv input fixed - CRF as default quality mode for x264, now -q is CRF and if you want CQP add -Q to it Changes between 0.9.0 and 0.9.1: Core HandBrake Changes: + Added: Forced subtitle support + Added: 6-channel Vorbis audio + Changed: Much better buffer management, leading to impressive speed-ups all over the place + Changed: Color subtitles now display in color, instead of being transparent. + Changed: All errors to stderr with hb_log() instead of to stdout with fprintf() + Changed: Accept stream input where the file type is in caps (.VOB instead of just .vob, etc) + Changed: Better quality Vorbis codec (AoTuV) + Changed: Faster (threaded) ffmpeg + Changed: Force x264 to use a key frame at chapter markers + Changed: Try to recover from bad preview scans instead of crashing + Fixed: No more hanging when using MKV with chapter markers + Fixed: "Same as source" FPS now works correctly when the end-credits of a progressive film are interlaced. + Fixed: "Slow" deinterlacing no longer doubles up the chapter markers + Fixed: Proper display of fading subtitles + Fixed: Nasty artifacts from inaccurate rounding in the video scaler + Fixed: Improved compatibility with streams that have missing/misplaced PMTs Assorted other changes Mac Changes: + Changed: Bigger buffer for the Activity Log + Changed: Redesigned Queueing window + Changed: Redesigned Preferences window + Changed: Structural reorganization of the code into more segmented files + Fixed: Closing the main window no longer causes HandBrake to quit + Fixed: Changing dimensions in Picture Settings no longer causes a crash + Fixed: Target size bitrate calculation + Fixed: Picture Settings previews now scale to display resolution and screen size Assorted other changes Windows Changes: + Added: More robust exception handling + Added: On-completion options to shutdown, suspend, etc + Added: Turn tooltips on or off + Changed: Open source, NullSoft installer + Fixed: Add-to-queue issues + Fixed: Foreign language issues Assorted other changes Changes between 0.8.5b1 and 0.9.0: Core HandBrake Changes + Added: Matroska (MKV) container output + Added: Limited MPEG-2 transport stream (.VOB and .TS) input support + Added: Option to write MP4 files larger than 4GB + Added: Video filters (pullup, yadif, mcdeint, hqdn3d, pp7) + Added: DTS audio input + Changed: Switched to Lanczos scaling from libswscale + Changed: Precise chapter marker location + Changed: Newer libraries + Changed: Much faster (threaded) iPod encoding + Changed: "Same as source" works differently (better?) now + Fixed: Audio drops should be thoroughly banished now + Fixed: MP2 audio support Assorted other changes CLI Changes: + Added: Chapter naming + Added: Many new command line options for subtitles and filters. + Added: Turbo for 2-pass x264 encodes Assorted other changes Mac Changes: + Added: Chapter naming + Added: Growl support + Added: Advanced x264 settings tab + Added: Logging window + Added: Turbo for 2-pass x264 encodes + Added: Many new presets + Added: Unified toolbar + Changed: Default settings + Changed: Further integration of the queue and active queuing + Changed: Browse DVDs like any other volumes + Fixed: No more floating window syndrome (Mac) + Fixed: Presets retain "magic sauce" when you change settings Assorted other changes Windows Changes: + Changed: New C#-based Windows GUI front-end + Changed: Improved queuing + Changed: DVD information parser Assorted other changes Changes between 0.8.0b1 and 0.8.5b1 Core HandBrake Changes + Added: iTunes-style chapter markers. + Added: 5.1 AAC surround sound. + Added: Dolby Pro Logic I and II downmixing of discrete surround sound. + Added: 1-channel AAC sound from monophonic sources. + Added: Advanced x264 options. (including High Profile support) + Added: B-frames in x264 + .mp4 + Added: PPC Linux Support. + Added: Preserve language IDs from the DVD in .mp4 + Added: Snapshot build method. + Added: Anamorphic video display in QuickTime. + Changed: Renamed back to HandBrake. + Changed: Libraries updated. + Changed: Enabled Update Checker. + Fixed: Multiple Audio tracks. + Fixed: Sped up DVD scanning time by being nicer to libdvdread. + Fixed: .dmg is now mountable in Mac OS X versions older than 10.4 + Fixed: Proper output size from x264 in target size mode. + Fixed: Allows output sizes larger than 2 gigs in Linux. + Fixed: Several small memory leaks have been plugged. + Fixed: Fixes for 64-bit systems. + Fixed: Keep Aspect Ratio is no longer forced, so user-set height values are respected. CLI Interface Changes + Added: Customize maximum width and height while keeping aspect ratio + Changed: Much prettier help screen + Changed: HBTest/MediaForkCLI renamed to HandBrakeCLI + Fixed: Better display of audio and subtitle ids Mac GUI Changes + Added: Presets! Includes initial ones for AppleTV, iPod, and PS3. + Added: Preference option to auto-name output files with the DVD name and title number. + Added: Preset support for x264 options. + Changed: Remembers last destination path. + Changed: Remembers last source path. + Changed: Copy and paste in text fields. + Changed: Updates target size more quickly. + Changed: Mac GUI no longer retains target size values between enqueued jobs. (http://HandBrake.m0k.org/forum/viewtopic.php?t=249) + Fixed: Preview frames are no longer distorted in anamorphic mode. + Fixed: Mac GUI no longer floats above other windows. + Fixed: Browse by file no longer dims the browse button preventing you from changing browse locations without switching back and forth between it and drive selection. (http://HandBrake.m0k.org/forum/viewtopic.php?t=342) + Fixed: Makes sure destination directory is valid. + Fixed: Fills in the file save field with the current output name instead of leaving it blank. + Fixed: Update destination field with the current path instead of using the last one, which could have been a DVD. Windows GUI Changes ñ Version 2.2 beta 1 + Added: A few presets for the iPod in the menu. + Added: Ability to set default settings for all program encode options. + Added: Ability to turn off Automatic Update check on start-up. See Tools > Options + Added: Mod 16 check on the Height and Width boxes. + Added: Check the amount of hard disk space left is not running low when saving file. + Added: Option to have a Read DVD window showup on start-up. + Added: ìView DVD dataî Menu item in the tools menu. + Added: Links to the Homepage, forum, wiki and documentation page in the Help menu. + Added: Chapter markers check box (New feature in 0.8.5b1 CLI) + Changed: View DVD Information no longer appears after clicking the ìBrowseî button. + Changed: A few changes to the GUI - replaced textboxes with Dropdowns which auto-populate. + Changed: Auto Crop and Aspect text now automatically update when a new title is selected. + Changed: Several tweaks to the GUI design, remove a few text items that are no longer needed. + Changed: Ability to Queue videos enabled with completely re-written code. + Changed: Ability to queue stuff up while the encoding process is running. + Changed: Ability to remove items from the encode queue while is running. + Changed: Anamorphic option blanks out resolution boxes. + Changed: Re-written update checker. + Changed: Ability to turn off update check on start-up in Tools > Options + Changed: Auto Crop option now fills in figures into text boxes when selected. + Changed: Mp4 now default output file extension. + Changed: Enabled 5.1 AAC option. + Changed: Enabled h264 advanced options. + Changed: Updated the FAQ. + Changed: Included new version of HandBrake. Version 0.8.5b1. + Fixed: Pixel Ratio Not being saved with the profile. + Removed: Both ìView Dataî buttons on the Title Selection Window. + Removed: The ìRead DVDî button. - Automatically reads the DVD after selecting a source now. + Removed: The Help and Support window. Been replaced with a few Web Links. Changes between 0.7.1 and 0.8.0 + MediaFork project forked from HandBrake source + Updated libraries (meaning better quality, hopefully fewer bugs, and increased speeds) + iPod 5.5G support + Revamped graphical interface (Mac OS X) + Anamorphic encoding with pixel aspect ratio + Brighter color reproduction in QuickTime + Lists disks by DVD name instead of by drive name (Mac OS X) + Titles output movies based on the DVD name (Mac OS X) + 32Khz audio output + Constant rate factor encoding with x264 + New preference item to turn deinterlacing on by default (Mac OS X) + New preference item to select the default audio language (Mac OS X) + Bugfix for reading straight from a DVD Changes between 0.7.0 and 0.7.1 + Universal Binary for PPC and Intel + Bugfixes for missing subtitles, audio glitches with LPCM tracks and more Changes between 0.7.0-beta3 and 0.7.0 + Multithreaded H.264 encoding with x264 + Added option for H.264 Baseline (suitable for iPods) + (Very) experimental queue support + Fixes for some DVD titles HandBrake would not recognize + Fixes audio gliches when encoding from LPCM tracks Changes between 0.6.2 and 0.7.0-beta3 + Chapters selection + Custom framerate + Subtitle support + Check for updates + Custom aspect ratio + Audio samplerate selection + mp4/H.264 output + Proper NTSC support + AC3 pass-through + Progress bar in the dock icon (OS X) + 2-pass H.264 encoding + Constant quality encoding + Grayscale encoding + Up-to-date BeOS UI Changes between 0.6.1 and 0.6.2 + Support for DVDs with MPEG audio tracks + Rewrote the DVD navigation code + High quality resampler included + Better AVI compliance + Updated encoders + Internal improvements + Bugfixes Changes between 0.6.0 and 0.6.1 + Fixed LPCM endianness issue Changes between 0.5.2 and 0.6.0 + MP4 and OGM output + AAC and Vorbis encoding + Experimental H264 encoding + LPCM DVDs support + Autocrop + GTK2 linux interface + OS X interface localization Changes between 0.5.1 and 0.5.2 + Bugfixes Changes between 0.5 and 0.5.1 + 2-pass XviD encoding + Bugfixes Changes between 0.4 and 0.5 + Bugfixes, rewrite of large parts of the core + XviD encoding (1-pass only) Changes between 0.3 and 0.4 + Better multithreading + Allow the user to specify a target size instead of bitrate + Misc GUI enhancements + Use low-priority threads on OS X Changes between 0.2 and 0.3 + OSX & Linux ports + Allow 2-pass encoding + Many internal changes & fixes Changes between 0.1.1 and 0.2 + Fixed a major bug that made HandBrake probably crash after ~ 15 minutes encoded + Fixed a few minor memory leaks Changes between 0.1 and 0.1.1 + Fixed a stupid bug that prevented to scan volumes correctly if FAT/NTFS/etc volumes were mounted Changes between 0.1-alpha2 and 0.1 : + Automatically detect ripped DVDs on BFS volumes + Allow picture cropping and resizing + Allow dual-audio encoding + Created files are quite compliant now (tested with OSX/Quicktime and BSPlayer) + Better A/V sync with some DVDs Changes between 0.1-alpha and 0.1-alpha2 : + Show length for each title + Fixed the screwed-audio bug + Many bugfixes... First version is 0.1-alpha. HandBrake-0.10.2/AUTHORS0000664000175200017520000000515411724512017015114 0ustar handbrakehandbrakeAUTHORS file for HandBrake Eric Petit + Core (construct, multithreading, BeOS/OS X/Linux ports) + MPEG demuxer + MPEG-2, AC3 and MPGA decoders (w/ libmpeg2/liba52/libavcodec) + LPCM "decoder" + MPEG-4, MP3 and AAC encoders (w/ libavcodec/libxvidcore/libmp3lame/ libfaac) + AC-3 pass-through + AVI muxer + MP4 muxer (w/ libmp4v2) + BeOS interface + OS X interface + French translation Laurent Aimar + H264 and Vorbis encoders (w/ libx264/libvorbis) + OGG/OGM muxer (w/ libogg) + Gtk2 interface + wxWidgets interface Van Jacobson (van) + Universal input architecture + MPEG Standard Target Decoder timing model + Auto-typing MPEG stream files + Minor bug fixing in MPEG stream support + Minor dvd reader fixes + Chapter mark bug fixes John Allen + Core enhancements + Threading enhancements + Simplified Mac OS X GUI Joe Crain (dynaflash) + Mac OS X GUI enhancements/rewrite Damiano Galassi (ritsuka) + Mac OS X GUI enhancements Edward Groenendaal (eddyg) + Major bug fixes + Subtitle scan & Colour + Performance improvements David Foster (davidfstr) + Subtitles from file inputs + SSA subtitle support Rodney Hester (rhester) + iPod firmware 1.2+ 640x480 MPEG-4/H.264 support Andrew Kimpton (awk) + MPEG Audio fixes + MPEG Stream Support Chris Lee + PAR/anamorphic support + dvdread enhancements + Linux GUI Chris Long (chrislong) + iPod firmware 1.2+ 640x480 MPEG-4/H.264 support Brian Mario (brianmario) + Windows GUI Maurj(maurj) + Dolby Surround and Dolby Pro Logic II mixdowns + 6-channel AAC audio from 5.1 source + Mono AAC audio from mono source + PAR/anamorphic support in mp4 file format + Chapter markers in mp4 file format + Mac OS X GUI enhancements + Support for DTS audio sources Mirkwood (mirkwood) + Windows CLI port Nyx + Frame re-ordering and flushing in mp4 Philippe Rigaux (prigaux) + 3rd party library integration + Mac OS X GUI enhancements + PAR/anamorphic support + Universal binary build process + Conversion from jam to make for compiles Jonathon Rubin (jbrjake) + Massive core enhancements + Significant improvements to H.264 support Scott(s55) + Windows GUI John Stebbins (j45) + GTK GUI + TrueHD demuxing + libavcodec video encoding quality enhancements + Numerous bug fixes Chris Thoman (huevos_rancheros) + Ported video filters from libmpcodec Mark Krenek (travistex) + Mac OS X GUI enhancements + Mac OS X GUI Queueing system Kona 'mike' Blend (KonaBlend) + Build System and related guides David Rickard (RandomEngy) + HandBrake Interop Library Tim Walker (Rodeo) + Miscellaneous fixes and enhancements HandBrake-0.10.2/DoxyfileGtk0000664000175200017520000021054511433747163016233 0ustar handbrakehandbrake# Doxyfile 1.7.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = HandBrake # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = svn # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = ./documentation/gtk/ # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = "./gtk/" # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.d \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.idl \ *.odl \ *.cs \ *.php \ *.php3 \ *.inc \ *.m \ *.mm \ *.dox \ *.py \ *.f90 \ *.f \ *.vhd \ *.vhdl # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans.ttf # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES HandBrake-0.10.2/gfx/0000775000175200017520000000000012535641636014636 5ustar handbrakehandbrakeHandBrake-0.10.2/gfx/hbFull.png0000664000175200017520001023763712030637472016574 0ustar handbrakehandbrakePNG  IHDRzl@ԇtEXtSoftwareAdobe ImageReadyqe<!?AIDATx ]e}s]' ( ZZEkV"VE-dlI2EEKժhȾd "!@e&syv'U`fy˹739ww~Z =;ɗv_q$E;3eYp 8,wgrf\s=yYx\/u"ΣOԥ%+TTT<J[ޭeX_K/K!J^T>b]p;BEϼҡ\F{ |m{ҷɗ\^R2|η~X5EE5h5$qsJfRDyw^t z']a)hmr~T. ~t x+;Y9#x+d( vL64;r#ޘdwk7|U1QUC/YP61#M'G-pn Ys1+ Ya̯ ߨc+~tv3S{@֜dA󬷧o~J 39ODI44+=VH?W]8czz&KOJo~ a)с\Qra%.*˰d۾`/ E=N0e,1=ܔ4Mwj;'t8Ǒ~}c~q… ?&5 X۸5_NU$zqL{ANVh_sz\}D0s[=jaѦtG`JlW3Gۋrj?>s?4a8L \Ӿ3GScPvnt>xϟXA.=L/ɍQp56AV[X򓗱`Gq _9VA۵fKS?eh嘴C])w8{:V8:/x|\U@0v|'E M-NNɴ¶nvAPkw_<5XA=PϲUc ilsPG|?y ~jWsϜ*0LP_QAT5 r6m6sk튛u#'|֍~sz7F0A^ɵő1ASSltcvN?/}y ēGG~9`"JG y)=m&iJC#9OXHA ofLM=Xi㶋!OTc77٤G6ne{uIO<$SAֳtW(?pqS(ў$O'yw|lj0Lp=_& {T/H9QWĴ>O>8E9V`kiXyo=d3-|MڔmٖTD=dKWl9`j!Vt- QѬhyQ$awo=a9 u6L=@i`7T刡 lюa:)>_N'l7V`XGO<۞MK嘛i} JlO9:-05LC/:ĝTG%qVoqG?x3d5z&i?W^DqUeBb# ͪ$݉÷_0L~=Dϲ]^"R61\ wLU@[Ǟ(+ G0 e:°Gg ip%_T {rJdMg]8dVɍ`)V*vL85()+ <@ 2l)oy|+ F0,_\}IXcz$J{j%3rvcY>񴞴GU=n8_ͪ0yL2c3hAND! JM}[8G!OQI^Ƃ=ÏgEz&en=:*b*{L 44MwÙ=xrO羙U`r"Z*<j<U4v8llc*W̘ #.XdbU|z&ryNg w eGJVwHE[rvmgVɇ`Zѽbrlә0G³>M3ܺ09Lr2˪Z־W6QmJh=lӺM'{L%Pvw÷~t/bez&j;JZߢM}[XۦöoNgDOqTA -`#亻t9iߦmaF:ÍUcd'Fn|܅s^0Lm5G ,\)69CpSV; O0LW?}_bzBu[X#L6'U? +<9w_y.AѪf왠Ӣ;}[mVHܾ-%''] C/`ezVwuZ E&1!$HUE3|$&lޟWloϛX]&&)6=d]2&/a6ġrjLsmOriJ E[>#M# D0Et/[>"]$ٔĽJ-ʶ6mޔdJ:U8>\[~ 0L!kHihG-n^Iݱ;ZHPrюLSa7# 6wm{V` ir[(U+axcœKiVZnwm@dnThP1K!G)il0R3#޳{Y`b!BzWoUG_(䑤s[x%נ$_R}J2د2zF/pd摎4seΓGv`b!bԬ~m=;uJE嶖| }sJSOn`x}mSg46JT똳AHEFb'9셎 @0 2[>rҲKײ2lÝ8*tpNvוꨈ6hmZt_^rɬ:=Zş =m[K;f[nwW\9p57*Fɗ۶-s*Ӳ-lSEQ2HW_U??*{%57t-qXU-?{n~'z];ogN,~]?WBAܼ娺;NN3z*: oRV :0P8'0tm]4pu:?Y sm9׵kq;LPn.K ?q p/|y,<ӵg^E=V 8&'رa\Zhv-I_Gլ<Z͂+>WDEI>ԪW㶵'ЦvҥK9GXҵnZ{|Q {o dC[c? nl d}#Œ cxLc*!)剮]f_OxѺ s6+i̵UEqhc{(#6Qʆ>dvi*nL:&8>eS|7X<<ϹoZl# "KXaR(4ky_zosM@#IMӒ/9+'W(mUp4')* vLcs1 se㪲 n{{{˞A&emqD/iI(VtYg]+'VvW:i~.n^$_Va T@Ƿ (6OwF7µi0a4o~ySz `\?}+?kϼNUx^Xc9()69<`^a6 _/Yʴۓ#^t.dOp kͭ[_wMZԨ%"zĈi]W}~%Vu?ױT]NCc+sLT&bˮê]=qY> ۷ecR77~wͼ罕E&]o[Y=@U&:Ȅ6nӍ}E_x:o]K wW"gmr8KHd۷x%dڭj;'oVpJ- , M-?Ң/fp qˋ@d(+H'(Ko.װz֑u_1v s=S9۾Mg*a-ڶnxOxI۸)[tMdHYԺoz3#=`Ydײ>!jXZ"{LezK/>[SRplW=$wj쌞h4U?:iߖlTTg84[ZҊ] LH>ּٴܯ3$& ]/СG{K/=x+owmi0c۲l72aOԾ-JL3ЧuT +8OĞu(2Jnk|t {G&ޏWJT} P`žf:g?\f`vg^UįɋVZr%ۢla&Qfڷ i ׮:f8iV@'|~ӁGdk^x.dzּp0 GF=tuѬ`puN(yy<))DA ~U*h9P2=ILR:!\DK. 67~oM ̞OG&% ڶC?|D˰u=qG-TPJ]*Z Nmձ0m3 -NRp4I>KCx>dnםˈ%pv[]OC&.e ǏhL* o{I7w!HҮvgݞ''ZGszNbnDaZԛ-{G蠖ѡhS6]JÞ* k:R-)o70):mƒPA}ǢK-A6tZ_gi;(ґ;Z?edǮ[PRhtG<~ehNڵokhĹi妝Ew{f<0ilyy)Okg8G-=JeqwWv5kؔk_Y7W-_\WtJO4y ̱:&)6FiŏD5=oU="ZTqߧ:%)iQ瑙dӵ|_;_nxuɋq% Cm{-YBBAtw.l}g0xHXPRe-ZQ\8㫠 * LP|׸& YJFp:CDՎҭ,]"rL˖w?8n{Ո9Ŵ}ܼS#ʛhQ"R/F_P #QP$*>(]F.auI*ZO|#~~EP{Oj7U{R`*l3jyjleKg(=`RZk+j4ũk1ߡ3b /;qag?t~ۼQIc+p<&;p':YkiINڲEmTfݓ>9ڑ?drcؒP; ۴Aq%Da*˨e(=`*_ѿ䌟3~rŤJ &7.]f15mWԇ7(-ӓY$1kl< 62mH˾zY<ۋ81-u#׮4㸢LI5&ɩ0QN"q85 LZ;9 IPeX()u\A(^n=3>֮4<٤ycI:Qe+bfVulXfà}-'Q<29n&yYEk澚Fn˜TvT!iyhgS xhÞ{R Mc(=`R+ڏz.n:nצ :Ee{g,}[ג-ҁ߄=q5S/ENog2:$aN4'lqOz;idnSGrc"zbnq)Uo j'svnJzs-Pm+zXIA/\\mOmA4,[fW\w\`Y&0%Ra5 ~lHIM%%=eT!9>$nUZ>wtQ?5 G9&> 3/nꘊi5 }|O۶m6q3+p Im YQڢ N{ܻ7<|U.{p&!U~*647}$Ć5x$ @ S…='_<iؽHu(DN5kɷݡ<GdIܢ#NN3F2G23xjs$ qOk@֗ %3-_tsTTVmmyć7yl@jf\|SNq;t+ @ Sʋ>æѿ`BWU(]s7׫薞h^FW#jOeΜv ʆe9=j܈S0+:H߷\$LЖEsv3ϙ'>CrJNl[8N^mpi7ś ̞sg=`ieݫΚklI޸9=2'I(uma52ūD-i뼤*mGy tinxx.]~?lr;UZD- V&y7aocxjceϊ"+ `)gŅ_-& j=ARh{%;&xv8rٴ;pU!s /eÜZ' q*' ZQij_=`JZ~?m{OF**{A0j\eJY)ܢ2"Zq*Ucb0UEggNXu:G'nmfNlhNUWt[&ɜ10hʪJJj<ٴ+Z|G9Mi*h LfyTuDŽ<:/<%}N|ao˪_=`J랳7};[cOszO+J ΝjV~Km$ïĴ9)+:qԒJfl{ 3 Ɇ>* <]u3egozյ|=W,_uG;ǒ/rNt<&JF}Vɜ)S@QiOfFm7W ~IRA{6 '[-dQSio}UoȆ`q{@WS˫4$G0c x} |[otf>/,T/\#֢ 2_Unj7Dsx$lVĕ=ѱW׈*ݪ`Z%N|=&ʉJVixl=%zAkNϝcuqUa&#f8> }P"^q{ϊSe3V!ASUdڲc?֙J(U6QIOGOv}Z٣6Zʴʻi*ǽjAs?C../rVu:lnFyO(RuhT;7I48Lȣm8eR Mϝam[G?8Q97h /cvEER~ j; AF*u=Zy$geD'aʒXL0ġOjEWeOӖvkgؑmr {26GQZ:su}5T9D__a%i Ft9]GJυήĒywSiШ})TOzĕn61:L|3ۘ' 233qiR)E:\!I '3nFOCMf8u<3צr0{GTtT`>{;]L5sR[Tjtȴp*"$y̲F/3kVYԥGno +1(/eGAYJ8wHIwt}pp\Z1/*~?wAP4'Hk\Q%- U{uzA:9!2%3'a*: 52%%4Hk2MK:![ӥ齞}ŴVxd5+{.Y>we^NPHB^m;:P1#ĝjIÝp$:zAi叴xqÈLJ\ђ 5C8vG_ӃgMֵi+LM]+V>yZ8R%\Ѫ.LȬT]z;NGex]1q]/r3񼴚ǶSI/2Z9LEH}=:Ӫ-81ב3zuI-ThqTxκk\7wk=}j8dI.^nWݟ _8: >Ŵl  l0A&@2jfOFB /-knnfoK_/xǷ.\tV,>~*~| :|Ǣy.:mݖ N$S[ oG&fNF'jIќNGشm6<HIf6yNK2%m&gFrNfޒ | h6KTd'L[?SSFǍJ)Ǖ'9T:sGEsyJdUT323=`AR:ʭ/?oAuW,Z>[dک9z}ȕWݺkyz.^1+{W4O➵Mk EǃU %i^3idR\Mtzf ))׶qӕNvg>bg3-h}'oXv\۪贺_~/;j ႨB.jz^\гvηc* `$lovhI>$G\BYỎ5g;x ]v=s:I۴ KS"N^P=SDi%Kw˖Lwy'm踢A՝U V2dB]V|B'eB}>WdYe%Hmm?gǾ@@[8~` 0ut^muU>plAjڃ6F6H LuM\s 9.,lQ;T* BmN/Jc2nтΆ\Uf:pk˜vFC5]T $og̫aߕa?/~Qm jS ѽg+ ϭs>s/מ4'9[q(}Z72:eP?6m(x ;Op}.xs?mu~Ox6 LPU~fz]UInrT'4=I ,X9z;X4ҖgIU[#3TUVaqD͆; 5'vYi۬9R=KK\Aalӣ%?T9i>߳']מW( w@3{.뾼%.0==|SOy숝al*GmNF3Jk]wyTA}iUO'FHaL;~-lǎy# y΅}iFЃx^g{aTU ^Rӯ,):3r"\Nπ)u5?=Q{gV MUQu'M{VuѶJu )l=q#$Q*3F";䶤/+nJQIMe7m+°gJZF] ůW&1mo1jނjg Jïz.]~H0|>f,jn1;x 5w~ۡ@_]>A(w6J;X?xk?WG4S OOT!i_| D>no<0H-ŭ8T=k[}]Txp>!8| 7d4oH./Ur<:g$czV|l on_w߀sө*p ã~O%-{9 7Qgd-1[2swSfI!joN*t&1UFk@ n\xeC]P{!@tf[/3GGefF )"[R{ս_?oA3G/w;\;DMmtTQK6Em xtU4IgJͼ׬=GڭbbrwQ> !G7=Cs>5Wf/[޸G}~q[ r|q[N9_-?qq !ˉ'}iә&1U.o\GG\qVv=B{fxsLc}+V`-xQߊWt<*Kܧ˶+):rȱ" n푧*Ljdwo-YuΛ;rMqeN4=leNfˆ:IYH|Wu05sv+`e7v/'fF'U6hRn]q;mQdi+40R{=v Y*bDIOK OkzӲUK^ɿ-p2$g= q9mV1& C G?I85QK>snS42y'S6VetBޗ۟\o1Y \jiW8Zza+mly1jzO:k;=aeoaG#= C')6;Nfa3\o z[k*{r?_>& k;\-m4<_kT;^_.n7^{/~Cgmw=.%''㛧̭S]˶W}\r=+ion']WlU:#\2$[eZ;n _,7wyu9n2_OdY#9IrV ZK[_NhٛzD'ǣvmq{x\sF0V)E' x|mNXm1iE3Im8HԵm$ *zхϜwտNJϏliL ^UNO+o]pra% U5vv4jlͪ=gOFfzjom#-nѱwl9{Ƿ,BOe/<|u_ְ;/kiSPy?`D?Sn819<:OԻ7-ڣj? G@up*m7Hu̓Qzd8r5hyA2:?۾‘C?dӞlҗ|ѶUEj o?%W|uۿwQΙ۶&bSTlXgw&b{Y қPH/dlΜϹ<;wܙ3g;}x͊Mqׄ Z钛s䞪PYȂ)DǸ= `n)<{\w5`8o ZmTCENA-Κa̽'vr^F5sVݲ ͒vR_ɎBҦWNјjo!io '1v?-቞A7q3Oz:H3" )D266(5S`ddd<,IH%%e,'CkT GBQ2gfOK5/tú,Hy 46/Ңy=S,]`1<@eylXaI˖|q9;uM\oz&9_QR a&ɑeۆ7㴾>yHߵI7h*ֱw[P`Q[ucQ=%UAFR\+H%}b"lDF(V"`ǂW*@)ὅ=';YtQGAS04Tl\@nfW2-RݐG/I 2AQR%ׇRȐ`Dsȵcݘ${ǾOڝ_3sJV <4~lM`Ǻ;az;rɬw቞̘S< ehLz=^(^2E0i3LQR0"sI$FÀSTn5B>X3Z nl݌HdJĜQWgAp(ߤfl4d~[­tLxJ)Ϯ>}U!5 ^1{҂ `ۘ%R|>="ʞcD9Þ>7 O J?kSm~=wE341 '!۲&7@>6ϼUk.BTGOT`eGeπT~Wo9Vv ^,o4;,?vVPakUao5T 0۸C<;J@ԲIRGW=#Kj""Rq1\!&Y}"v1IzKa /A(:/"#XUZv(&dn\@O7'&=v(EZ}/x-hvYqVI =+* x.^;p~[ol-&xຯ)8Ԛݹ܁Y9lز8 {~F;,_'&[oO1|8 LVy>\92zMcxdPCa I$A@yT I[9>[POv@Zr{A.]qTt*|}O0TSy-bĮba [8 Q췫|IvOLvb.~^6&yDV",e ۬@v3o 9uU3 9ԡ#c/02jrS26k}¶G|'lVur_K.R+0Td}7h./#2З><&@9M$#\=AB|s却2@"i:PHU;(f $bzAS}G)0#rsNRP Vxn7|_|F ! BIG^:n^`׋1ly* PtuUy矇WdʜÎ~&؇2Dqh JIuqaD S04c|oX~8ser06N`)ڔTXadBGgN4=ܙH^t,Geȑ LE{ K8돵,RE'x095Lo~s1K79/5o=Z]8&mϕ!.cxdz>ү&/컪mum63Ie=nug |E4uCG9qdv֠;_J^hK)$`ڹ9Jdތ-U B^錧!?R ͲP[ s)(]W3\X(FO>zw#~9"ϩ;8pm)E\CɞP36-cfi"ju HHAP;q6:c"y:N&S评D'xZhim {2/}dw+G~fBʀi(Ox'ylb\{֟[; -zҷ?qE>?Fx{aER0|e…ٚ))(Sd".\uBÕ22!FoTTpM:-3B#G@RRWH*K! X$H}^-K*Hgj)W%|?XY=octkξ-~QSk=$CR!3vaSN$(ɣׁ+RRJ>i" ?Y:q5yqBV>U˪z ̈t9q k~wiْ1aƐ#i6]XrtVqn&Jq'm\2 2Rk@Kag==,Ӡ+VD!A`ޕJZHQQO(rbB;R!d,Yx–^11 Yn,6@*ir#W6F!$U\DQ>v_6;-,ڈV ?RTdxqm3n֔&Ckm3o;d9)Ҷƃ}㢅Gꯛ'D^`ke93Z\r8wز<at]?~'<=qɂNCQ;9w@2A$T9vMl#n­^kv`?=,?˦Hۦ#[X KH2}V3rwHzFɻF,!%f>1$/mWۂgz`if%Nq!T?O<μ0+XS`Ma̡X] !"l7 vǯT Ԓw>ТK TTwO¦>ɷYXl@aθQXOQT;cev E3 vCJUubKRAxI}]GWieHe:Z9#a֏:6o$&MB"42r]L W֪[jA&bjB3/*R<pnIaGIoƂ~I5oݸi`T%ҫsĠs1:ǰ`;&8_0w3_yxg?ٳO?jaDB 'pZgA!=Rih(ܔ'l2MhGݐEJC"IIE\R`I! a}{`AʨBVӪVd~R )k7Q*1g `O|ҁXV?PS3Ds6L}?m'y0BPӀxqΰ]A9$5NjunMvcMX!?'_9c^<3sro}!w~*J/&BSs`h\#ȏ<Θ\l5ԑ9<96F0U *PȲUNA nfh@C(ncH6 %xj($,3y%OvExO@|ےis=c0znemBuR9LT 8 Ejx`=yK3u 7!؄!4Lܐ!& 1q{c^ܶaәgT6 j# QEnnw VO8d5?Su[|95 Ii5{\I>zY Cm0lb#U,Z-?Xe6zVWwz&A 5X1A38_h):1wG[URW+*:}{NmW}CQRs*LtQ:}- oH`: %CC莳cƷ oA51未7?^il<&04ʫF0G&+P6 9abA7 r [jyF)ĐcJɁd;9,||쿜qpݯ5ap-+*xFmpH)l( 5M="ǶTTfF{GP+tM #àv(K/݂D !H׷3W4M4%[l)C7糨sX_1RA@]4V¿JBPӡU֎v#Ү1PA++uNPƏUnBrBjEU|ڜMP<&Q :.1SfϜ2{V.:vsLvM pw$r]K"J:X *lr1v,ʊևCYI-4S7Z-/zN͋TQIruN+9b|t%^aU߿ߛ{#(h? ,1,@A|>`D3kz~) <|5WS&xǚzq[*lBGq ʢof~KİZ7`ڔqF%=h+6h2LV~mU~g9I,;Cee޲@Vr1IZ吞me$aRq =w$}f;46/>|SĈ9 :yA3M~8G<dX$ S3JcUHaw=[!PdԳ[62=g,k]:*A1ƔhE}L(˨{lF1ޑci[סn8usͤnʁ`8q=CN4*Ms&Ĩnz"JvB==ǟI7{N捏ekq,'y3Cv} W'/G(wB~\'R[j8BCE-X+n-z'~7UoNب,˃W]RHi%d|i6K)6uSW4sCiLlH =<Ʒzc0F+:s8*XT|w"w[՝i^Xً2m+V>.̈Њ㡈j,t !*skro97qdU9RݐŠA6DY,ϮڸU}r&1?#X ;2$Ȣ>X| +3.o)l-pEg!" h:l@{bDeR!QB#$ 5!-w0\#еZ=32R_f^}uyk]?m}2D9aٶXM"ޖ3i^\Qx]NI%D:8j#&kΎ켭ڑ+9wQ\ iKꢃ{8$k 3'лj>E(QB~Bgl쇓eMt& qS}.Fb.&j< ]AlF"HAXȊe`BV.2r~;~b) ,QۤeoBۮIHuֹCBqFx蟒[&9NW53zbfFLc[sbÄUԤT"_}󞛚[ j{Ʉ[m1TezR!%ӗbpܟLa}5N$HڛgrF@(`WZjp'#V8de'P|rYJL΄V_Q *;Bog|`iKӸM?ͺZu90ka5*R UՉ8Idu[ג;X74G`$niBNpU"4j-x>KV?Hn;|p<<7)/%i*ssc l6.P>n¥w3k O0M7xgx򟬲lXڶߊRdl,K@|Cwe@ؒB#+URe7`M5 +2HEAZd4jIIaI:R#}M$&t$nKC:(ZĐ Fx__,6(K=<<<<<<<<<<< Ƿ'Oxh-ǰLA䡀Ԣ!l6t=Ӹ@oLh^oMfXb!g]ѓ/3Az8tG9w]!ph5Imxg%̺-NqqrTFtz! *W bMYl,l9+ ao~7YtQ{򐔩 ;I8ϲ""'UPu=݄3 'bȥb U2˚QV PAV*4M{ۇ$=ħȲ BKks~rۅOލ~. 'w8 #YfJDYv' )K_h5W^Gm(Ne 3Űe 3!eWì,>ڿu tĚ?s6.X?,=#?ޙ8 ӓwYA5ah$PpElrFR=J$o9C(Dٶq 8$nWɮG-]6M)P{P{0Rۅe> ĺ!ֶs%,H :>]O4;@^Smx![EXdA#{ӹ29BRcnlb;OȒ?<pz f6wt{)vX>b !ƭo1H-Tx~k?}>w7J|-&F!]5*U䔤|j\ݞ$O>]~~i2O#xg[-wU7E P(e IeUJ-RYD2D 3 [:E/@"y@^]t8C,2GLcrmUYQ+^E-S1`Z:7'za'`Hc 7>ћ~Ta&0AG/zֻ~2e $H'Df+tZfәu!C聢R`MclIʱ_THSUb!Pkz ?jg5{t~տyIp΢oniz=O?|1!eGD"2 ;*cj_*Xv!%sQ_- l6"!Ddڑ8ơkHfv0^=˗5LlMkn;߄-}H9bc@rԐ#9u5ݽ3s1Mf&&J{F}F,jH"SO<;϶6x<32eqWwS~ (ԙ;Š,$ɶjdT:AE%KF"sifd6Vj[5^duTEFeL* c&h!;Q|9)u@!bedm1lSZ[czI#$>q>`M{3z &1/ $acDAha#&+5{^g;VKkk>Ȯ4%KKi%DEJB mFd$.6_]UH6,UF 7^'0 .3 rmjFc/j;@[Yz^ff-'͔JFgO[ږվܺKMf,2N{'ƐãK=6/iٷD0ǂss'>jC4.̇ܜٶiQk12ʹA%`vv4#~ 8d ۚQm4mH \`uCDF(u+d6#;WG)4"ssmyz.m .;HNa̭kև(X\dIߪ0/b[rHY6:s#, I-fJs1_N  C䠐Oӆ=&ιtƒ}=Ggl-qZ@3X6F^T_D 7t^M00uN2]E#"Zבt4*+Kݓ@$-.no;ۜL(o=ښڊs,jE/؀<(#]D}* `5KNhFyhm^s~!wCSLBTGՃѥm̈́FQ+BSZoG==nVM b msڢL!۲{r/^4a!ZWH{ઙxa[+\ ^ :FC4;{DZBȵ_kC̲TjZ"|;JsWKbmEl J @oo޹|a~3m 67C6 H"q(5Duq+|~P/G 1#3\&@䲅ǺR慨J-"{ѽ+4, cB*@=]^qsV\ugnG??O G6 E&U1 dqpݳlXĬp6n`,Hb)!WQQj2:&(ovAB_'+BQQVh9/U߮K}銏9twz񃅊'!C^H3""XU_ٹYcGmk\⇎h5s~RD\}[X%1m#fZ?>³yl&P,e._{f=3>?u Cfbsk"*rxPLdS8}e݄1Iɽp75gZW#mf}_FHeۣ=|յ 9=%Y?(ٴmeÆCYDRXd>!0>91("/T &:WV?dkKom}Jfu =qủ0L^pʌ y*LL"^Wۑan{]4zb\eEYmH*Ȟ`2b 7˓smn"~SAceFR}: EOcۂSyύP.PFWʜVQ9!MvHb,ԣ5uNsWM9} TYe/:Gh#^VoK~fFwof:_$'z!qŢh"RĵnC(X*shBU T~|Ϗ.l ɍ yStǨ{WֽnxYjۮ !xq5KjiU#eiY X }Ƿ4&<}"~.Z5Q+ǨCSTvٟ3?3_mi2;KylpDº<$NCZ@Wruced!TbE,-b0֞whު_3" n M6ڎm+ok娥%ײm勎kjiܯrC{9pܶ/b$Ձ{KITAQs"jlԔB#;*.suVs#1Bx zl0%:,JrEq҅+"H;0F\.WN YDo-xRʬeFe+{p/ySsAс zGPanى`sL~ҫ;sܵadjIB'^({jFXb= -!ryʄ`rIE{,m~ZĈ n1{$HyV(Ɩ$9?{v]j }8Q;c4YICnQ[L,ܾ3c14me$+.Mǂ>'h[Dt"GtBR7Hס[bWjk7ax.Eu WyOU-)+ #@~yB~^_ Yew/4ܫ\mэi+^DTnV GkOC?sʜGJͻz'Ts5:X T}8Q9CJbTIK}00S.jUu[.o γJt`9(\ -Ȑ4D s{\mT<U9,ң ܓ5 .7cQu O˺I[K 0%+JR(I?,˹0=S ȣJ&[^oqAD,.^>K.yztCg'|7ESc%AÄvc('Ϳ o{׿MCρDk؃&zp ֳL 7;uVװ z2Aft.vT"R óCQy2Toe^)c2dqm7g v [VQ 1[Tl] oBKV%@ XLU` v/* U)0"!lU*BOUK[9mv Tŕ;Tdҽ{ԕ)D#EPQM4v6/f"w@oIGhRNaіbmIGy8Pk4E av_]41RYfD:?HF%CR߉TtGJ^5u(B {(oU揝X_j|4 Hb-%ě.|͊и|;.''Tab#빢bfC"롎eap)+~Sya7VOox>0˺rz@I246OcEx$*זͺ)uߠ7X!jxId! kp8# r.fA*'Wme妘l]$V!Cadc:Txvl7yT݇N\{ z폯i爥M6n3,Ax4P?!ℑjY&gMľ$3JSJeSs3#Y>.{>Qy>zmv=2{rACFTHbcr@/2gp=}oTZŢRRL,VQ £16+dnNvFª@ ?9O*Ie5`Ž*6Ta?}s:L_}zX5:-=׏кA~wf;_E;~zl'({E'T7Igހ[R_j!}"vm,Ui'*cdʄH*T2ǫg}.p=-k{n/r@W'@!sl<TS":|&}~ط5]*]ZIۑI?pGq%D,Sس]9߼Ej[ښLoե{oķ|LCHD6d1Jzu(Oh&سOygčՓ6DcPAR(*BAرٗd2,CYdnQEokYA*!/Jy_Rq!>#O6U6w(sm:\$ mqׅe׭j4v&r3uז+ziCͩ#`2yur0F8wcu뫽y5O6AaWm5iO3n&oWlRJtgv~dLlGC[5jCayfTv(xv10e(,cd$D;Cax?tEu͍ v7.:ʦ}'}xu;O tYY 8'{PG҃L 8_q wϛ~=-3N8g qeM|ekpguՌ1k:Xco/ۏ࣎~U]ae٨ (ķ˲R*"~ &Kg3ٟ.^n!l==+CAWb3syp?/tZ!/\g#  uKX./?e6yBz\$w({Z6֔oYWSwf:ƺ}d{L:0V\t\夂sN Z17#CA1C JGvWØ;s}//#kg+iZ]{&['F) t Qwl*D 6jQgRQ0Eo'I}η2DN Qx?J5_xh)N_0)(g_ܒ=eo Ş?ssYo%%"Hv6~D,&pIAb ( = LCdǨn/HD$Ee[9JvH8Oj`DQ:C6{f;yV5m@k[aWGOz`9c-Ǘ]{ޏ7?J2䶳 ȩ!@ ~D[JKei;߮eص ǝ_mmk=cKý,mC+MF"hD!D( L6%("Ԣ?_oLy3'j=gͫ9h}3(^HPP,Aض99;BU$h&9}d7#!F2Amff6l@LQ3!7OXnc^{A%[߅]mu*ɬPbo(k8Xʞ \p? <<<<<<<<<<<<Lo?*2{նfdm+ay}`Z5 {kFmZ7ZI&z_|cI=̓n9ՉaOSaQ S2:1L=vn.2* 0lOp[m@{YHNj?[jbi\̓(zj([8Yë/'#mY}[ ?= ?(KٮQnfb%o})J?Ν8|eɛJQ݌LPB]::F֩kCCZ(ׂ҇K:P_$Cu- 2BX< .Qj\_map?3%{'\sS hEF#zZcm W|nV80>W,ϭg<`J3iWhӄ|"AwU6!lּȅomx!k{kMbW?ӿ>һ;?=͈s+QA@"Ma ޏIzJxۛ>=v?\.s,Q"1$7TNK.Tr4V!G2ȔVC\A9C]^&GSZp(Fhࡲ.hE\`e2_1;4 =sE3ϢQQ!pPkp6)Xf1cfc̖I"Z8Vf1CGPd-]Q .h~vYtO-+* rYݩ2$qew"~۵ `j5LO :DP(UpŠHd^/Ĵ{eOnG\F%aHh@Aͤl6yC(>Kw\ܺlp<l}y U`t SĆ`Qqu-}L,I¸i?e_.;"+/(si"YV! "c(~-1$O"4H'%Зlj>4V\ȕ<RXVnZ=ȲYD^ hے TcM[<:6ڧuEK/nſHt}0SUj(Qu:cIdʞp6زo񘷾CmmmxAOyxuޙ`0?;r}$Ct\9+*Y{nڈ5F_Y2w($xxxxxxxxxxxx̛qۊEa uښFnc,Fϧ<b@o=b_.@c5k## mltwt?ۖZEs%;rbV/+f. AQN0{]@V޵HPߨvJ i !`Q֝IK%:aVb2Tضryn f vQ 4EwHU i+}cyeZ7GIK*fUVmFuFY>8cT6'f ڶfcIJb˴R  ^O-UzEy*n]A(g3TbjOn7akד**/r'ރP{fL=ڪee5]nǿ~_i&C!TV0"RvP<.~S조}Q}=M:U:7Nc]}OG~qNlΠ,8c49$flm3ZdpL+vbw-x=WOYtBl+VhrswEYoϒ^j:rd=_>ƾ9^Ȩ=Amg;ʴܞN/RJ cc,8!%${o30Π0򫃊JP ) r){O!M]_{=gZ߻75,F1CM /nrMYL; l]ay7"D!B"K,S^=vˠc:u*QWRUo?\{UK(Qe: Ե@b ]b`.j+G[z,2}U0kO Kр?ڧǁ-&nUdDf!n_/|KB˵1hjCvjuSU_LQDPVOwyaQ;=g_tcJXeI֙58:1V81b*biUZO}os>];Ȏ7d.7;ȁLd|.2 @Z#Ӛ/:c_ |nǟ=O@ʳSpY6 /-@ 8\(Nkɛ铮罩2гګF.ey:c[D <Ђ!1 5}^ <}Uw>Adyn@Y):|'&T !S0##(7VC`$'9=@|{(gvX}b,`B"D!B"DI'ULJY@6qJei`I:F60hH[z{=u$`Cmp:.2>1ZR }LH9 fG y$LQMTk&5GobvDaoqŹqF&_k_,Y.^Rg[й`Uz޵3Hlx 買~ˑ۬>KZ˨0QLj$HGYL<ݫi<A{@R?T8*3Ol_ rk)tl.J#AeqiYWɂaO,1HНEs{G2~}|fwxŻ~=6eǬ Hۦ+ p3SCC9Fle@@Ǯ-5Xc8snw}M~%>펏UOmb -3HʿK//Ѐѱg?RQ`͈ǎh0Jf/ap"#}u8|1f$rvYL?B YWAaq~!B"D!BaŃ7NZ-[ d/F jҾ_ 3 Q{sٹyikFXnku6k# N6K?-{Zmk&3vO ňQ YMgҼ0Rq{x93!`(p%cVJ 󽍑uNXogaׂW*8XXy#fKl7|>#2xrMrʣG}ܗyUI;k ֣"4o'^Ǽ~0eI .L.Ǹis.:,.Hr^ E< ZV w@9ԶU&L+ .qxLd|#V4;3N32襅`։?n32CG#gVg.)gñҨPڴD3dq;n "1@>s+}VG|_i n8?޺ KC^8*F`TxFDo}-mx-&Oϱ@^6>T'Q!G~O J@ 7;j0OrRO htc$՛Dd&c#%Le(cPj72w|hn;ʳq螔}gNo((aZ#"|dֵ3C"D!B"D)n909黒Q!j5NW_Qje2"yu (=m}!q@U;x>2 D Oy ~ެyeQ%݅[\d0M,1A3 p'JZC{2)Szkrpi0/=JWNZ8DKO"]T\3~Â9G4{xf(/ƅ/]//cTBz= :*W_gʴȨIJmHf";Z ,kQSv^{|W|e^+ԭɍ B829H13p{$ # xD2U-:yMm3]'?8 fiH

.%^7$'寧lOEuڛuT =eٺ ~h0 Lgg*YDZ2¤+$e2QIDԭ b Ȯ4;G'Z_e+zɨW*ަDy;h@Â5Lw2_ s5NfSP(%]9ٞV韇3I)8#}0b|cbKƓw dPYL PMw4X$sbrEVײvb` ?LbK}a̾L<.U^25/='ᱦhǨ (KPJaaBox#{Lb,k~ KGD=#MȈ-LbҒʑSlrYP&$1D2WjySKtM$Xk>fݎyīcHE1WY۵e'c]J ُ/Zв'=Q=մj"t",MbWp)Tx_gn4Ib"Onwx&(1) UTk.ׂ UL+yHћ{VX]/^uz^E+b#\NFPVOfc*8$#JbR}kXPKpAI3qܶ[o@ z- (29%Kՠ (*g۰ƍku-=B4($0FPrmMmxyeY *\wEy/#yϺ>-O+Kc^Հ fLg@|$;f\hd2{%זVA !5`Č);d~Qk+v|ھem9>m z?W0m qzk8I62vQRZBЧZ+;Wtm8a@e#{ 4.b PlLYGM֫aH3KBt+k('7<*Z9zO@|s'\Byp5g"-}[lX Ř:눚#Xn$!B"D!BqP)ci:iyTYC-N;w5bߏ0*&ȓ$sx rϚ3[كc ޾'--Q{O0(-aPF>[ln߸,$CqtG<&ձ"f$0Zuǹ(a/D S,װc *Yy&_p!Rz^nȗyu}/5̓?cҀB 7}*i^E#a[jjIs!R9=l ȩ \1N/Qh/ɨj%k8@h@F pdI1)esml4m- 3J31mloVL'NIAʵ1@v?sjS ^E>XtIٶrPm-w§.yuʽ,\0W7#,(N/xD|{t”WY(1|%W}}v@^6΍RFn+QL)DVwAw|XYvPblzH!B"D!Bz,g zHJ3o 4,bʮ?+( \}w5'a˫k%I;]x`B6o)ުI' ZԬ<Ӕ ֳ rCA6_M Hi~4*wW٪&M˵v}tOX*uhhp3oCF[Z/p<K[6Xb$N.A.],1c#:)1k`=*gST\32~)Grލ䰄-pa!^3tgnyz,MA ~pLbt KOiY/C/˿<@ܰ'eGQht4gOټ7gb痮ckҴ~ކuauQ䘋ADfTQN=nXí8dDd9.rh,Fr煻&+k'dbKHrɐhHR^ɳ;F xG,(/BF+x@ jTZ /Ae85!B"D!Bpqp؛$fV69'R-a>&v2ZQ0k;iLϽ͙7y0q35tkNϏa  kLO֖r,7foOKuwg#y0@}>႑27 !GI=1r&ZHC5eP'@TvDըz+UxM0mHq%9 1J#` \IN S}fցM64kS >D 0L 88D*(SNqɷؤ"fl!]2a髿/6fe~&$Deߌ̜(H9b{.#FV&7dp7^vqĥ~Nb}v\Oׁ$c__H9,/SIۀAv>.m_=!?3n?u>4S^S!P}VFU ̍xR$ ܛ\< _o뢛~k?߽%V_W1'7HL[C6[G;˰_g~Ѕ̜~a"=CypHVr,anȸr|"&,jtR3?$h49o?&xૻyL'2[ʲcDA*A&Ix kjH|~;&8bm9 yQczqn%!B"D!Bbxq/D~{ $v#VrTIO.tYv,Hv%$S6͛ ُ ̹׼u?}~(%olfȀځgt0`\U8_ Κ>?Yk ޼pZ|0a?dGC< Qi,"$ O-wtw8|إ;x2:d2X<ۣRf,`E5RKH)^U?-֗ݶN獣Ld=w_gW|~LK 3)e(fR?'̂FDQR 0{>d)">uFPtl%d+IɃcl ^3A%zbЉ(VʭnS;u@k 5T7sV$Kqc>#<Ȁ* HܕUTpmʼa2ljk ÈIIM#'!e-,C u<3 9d4Xwg(P=.2f)ɭaٛ~[9H0}itt]'L>Te3$iW.ݕ|3<P 㪍ZtvҚ3O8ӓ'Oj+|kq꽧bբn=vP_J Nu7O9O7q}ZgcxouN4Q3C7!fgzB>4 $7k=>X#n d\#H4ZC#-QVE^l?IYwy}?mߠ@$W Ȯ+F=b#Э>d#SB"D!BA5BVZ_L!IQ1F&N"tc-K؝ƆC&zg\ W@fkged&Ű9^׿~oM[my2Y)HX2nJf/' 5;J$ׄc 0籄 l'XXϛil/>_CLR*i.ާ*Y$#f% aUc0y^xP k> k^+tq}hxM9RdrSeS#43L3V1'tNJ&c|U$ex<ߪXΏ$ϬAW3/.*2uɒUŁ_Z\w.J2G J%' ARb(NM.)i6Ibdjv),V B[$-?طV~kKxw# l<w|YpV:`ۓMa4'r5oSޟ#ir? 8> I8@.+]՜ϓR\Ijf kKfw֫9gغ{=W:g ڍ6MԩYpGށ *_y3;=lvogkg$ž$CI1L 2O?  h"O%dk鋎 4R=IybI4:zBr$YZz,ʨFQ#'%x0䱌' bky8-!B"D!Bci+.e8B?钨LzZGDmAӭ^SkziMmL6VxöLbk's!2r14艷Y|)C]/qT鈆TjBd[hi _.̝tNH&ݮ c9A'͚ZF-dih3?3[e}|5+ cߘ8D c'ʅ+O@zpLKo#F宜,U.q9|[omu鶳l/Uu;{ 㲩l\ q)5fۑ0\kgLi}NfGᚽ93rV}O) H3'\Q84|Kx_ gpQ HN8AlB02Y`&"y˨Ga#JGv_RC "D!B"D?OɃ oq$WCw#1Ơ0duۀ'w2{XuQ{9]ZeDK '/g?ٽpH7?ZlT"a˛u5#Sw/ (jP'o<v.|BuI8yBWA!;wEN 2DXɷ=vZ_w1Vl_vֈz2P>9}ʴqNx!QU`P*և)(ql1%3X``W 2\T\C!5rV7r"cn,`08BF[cäzd9瘧uK5fy þoRL1hvl)`HͶˬaH@Zw7Ox<؃c=sAj$~YZ-sЀ1%Sı o\sI%ŬI߿{=GO'YѲiޓNtl46Ja?C?ӏzS'Of2{.3=: y3Ym(r;2\fYE2<,oRCǙ', Uf%HQP>Bx< ɣTǎ !<-W)֨_?>97B F"D!B"D8o2T/@?Sܭd}LLVh,ld]sq#~Zk_a.Aõj m~sB_̡e}}J*+W%xVݖ2!DT7[#Fe 6 KjY9oEvzn ui8YZB1A/xì]rȈ}^<1 +'A;|2)@AÔoUD{/=}AQB0dyprWV~.181Xࠐ$I/ oayq$aBT1̩ؓ) FMpTAHi`yoGdl]OX`M&, @0o6g^CV*N3ee[SxbGu[0ul0><=U.s]wn=" J²$h׭A|R`HEx>7@TD`s X:uYϕB?yM>m;+>~k;oZ]+ՏPkvߒEb씵)&OY8 _]^*7̞ ajw]/"2#s>_m2O @ HF?8XŁ943Z+Ǘc$E@e@>#.tۀ98'xINf?b9Gx =!B"D!B :Q: o*՚l=ldEa1Zq7Ȯג(v;- ',IE 4;mˍ܁SE-blW~d5i[MWDt׽^E͑h.`j쵋la&Je7sZ_Z3 ݠ =Ф e҈M>W^:2\m$1LCDeJm_^Z"b6 3~blTՀSǻJlXˏQd-ĺrykf]^xK?xɭw\ 7_$"- m HAaAMS*E})sa24!bfd9ȃ5ϺO!C|H?(f098 sc?ƲL=DQ '(z c(ZN!zY8GtlxO-L{s1:9 1:!B"D!BEעٝ3\pzNjCc۸JZF[\5'm#>YߓGlz`s?nBe;qI*Ym_e2!zC 5e[Ҷ& yco# |ݡʂs?`%ٹ}j]&J¦j)U!Y2r —γ(T eN ?!?X8fV#E|Hr$/HFݼ׸\NPUӖ֗{r_e黻}go*SW,fMOQƅK#SԿ1R^(&)CH;0~bVϫ0#䤸lx Y*T.0W;5u ܔ4S}z\KL3EPFURg^\Lod:s9\xoIx =} ~u5kN)B{J */EoZ+"cӠC|p 2Xa-^8[t][z7UpXs$% }(%"7/̀x _pp)+;saEz04=Zd4bF,#x*fG9yvc;t7 4NJY&kW` `T5lйOD{C:<g>_4%0_ڍANNQ+_1!B"D!Bk &Zx?T&Y }[gW%_=tؙKiYKTʅK֑b׳(m_`EP^2fEϋ{xs5 kVl^da ?ttPkkuyDKd]H{Ĕфեxწ a )3(&U`-eJ sg &$*!l@+bȥGdsE@?ȑm^Mk۝mekQ˅D,;q rv@DuXr k=tRyA<8ed.Բy\mڵ#u#]ՠ%R@=џ;'uH$^0bta .aa}fSfo\jl~&o<7y(*QehѢhI7/.n3睅m?E=|kx8)FM, ! ϻW#[s]nwQf9^aȳK"č'yƋ8{κ aH6A,B-̓&GҘ/34fu+( @'jz;r6[}[ڵCFy_Ie5R{ٗgSWytb Rp4,$B3EzB"D!BW -~x\ -w?)2}vo_ʌ&R<d[ eI|L5R** 0d'g+02t!B"D!B5Ƅq.ǁdyD/m[=bGHJp~S@{ԗØJmqSM^> iJ4ZYLbaee"՗[mY:w~K6b 4q7@Q_|f5bvKF"ђ/{Ĕ˯X>u[GEiLSħ# 1lyr7Ex6Έ Jn\]>EɄeS']B֑T*~!(cV/ƬlSO^]T D 0N((iHH4ʻ(?c{+~[]fI,Zpg)#:k2]p:>l_84fL֕"׮JM"g<\3҂deĉCpBzR\U 5{pIlNI]ے['7=Mh*#2cQ;2ژaI29S$k@ISF z|ٿpJqjPz)y-v Ϗ=%bsI?(Yx2kOL~"KT?1/\,Zk-o zcf/ܓcGG3 IՊ,9S` _Eߞ@paM $+&8 ٣5<.v9f>XnBwIt߉LPK>лG˳y` Ĉ̵̎1K!B"D! o=AS[ .6P% ;|]?oGN9njJ>)w>ZGEü5:527m󛞚:":Annwʴdeéz~JIFP変zZ6gD.0*$HNv]9hsS^gTJLbPctO&mB0+ק$п~=*ʅY.da:( Ib aݺqT[xhd` w̡x,fdz_Y]],߶ls<Wf(1-; 0vl5Rke@{[T >*䶓s׹ݹu[X7׍𚑕AlFb RģiIJه]kvGK;zOTi313һY/Ȇ3w?X𨶭@$E}zgߘk<+_* '}7DU2jZ$|d!Ѿ~rQiSIvz8gWήyuxϪV{nxRl&j@u/L>Aʗǰdl8o|m °pIkiY'\A.~xjF19p$Y)D#WP.7)W3}#gNU<{15Y}+“㙭*܂E`'+!!OJO$ZC)i˟2pstqn&Xq:B "D!B"_{?mx彏 VVf:R~jY~+ rV}OMr!34b,#;["h|o\IVϳ[kr ؈GU.~ JuY=7wWmM=q gi경ԾI*"r,n~4p9ao@ 9cCOc~Fe]"ZC/!aJl"?J=c,_$?H #4"Xa9 <eE Ae{ua>fͻ{>03}{LT~WÄHB˜fF8pbQ$eb8ص) "0AY`y)c%a]}9|wfD;aPPB$ַ\ӒfTR1a)y~S϶?PLtwwWC~&]M{z1i\Tj[)y&qWeɒVA%}]wg|5{VuT[ (Pd YƘ-f} -l1k5 ϱw}MW̾¨ ?'-f y7DR9rЫ& {j Y3;)ࡔ< @Xӂh5B=g"yYܵ(ےLjXV%`^[uL-37 l-5wcYoVE?r1=:VߎT@Án,`РNk(c7ɺR YSlLfuxg5ZSX`/b-f^e(1oLc}i721ADq >JVvmeǍ8F]6}|u7OWP`%k'"]ECyxUkf$Ry {?ts-Lf߉ ab=2頌 )@x#@3:y C=1瓝\@&{BXOϓGq!;G `, `X<#5`Iޡ3TkAh@;fdAK M!B"D!BMZrL SʀR+@sGAP;S1کRAg`OzkۏJk.YO8x> S?/wE;~~k_q{GD?Łzn,*\e uMr])Q5̅w7 E=Vw^ 'ڃF Sd΋Fh3):)ZcJ*ko3Zqwh@v$x, 2b֩= F6f7 VfvXgwe;y8;<^K5i(bӮH{m}BXVJt ߤ bGAn9nWP9-A7 1X6WI=U5h%Ӻ@Og%%Q^.²U}r`1 f0}@ʘV1UnD0 !gs8+MCI<e-/cTto®9=V;jJCM  qaX(P`ր X`YN|R1%:KL.{]:[<鶋0ysIG0ٷ"=CDlqCI޼Ed\xݱW"Pv>9/xΟ$vc2yw0moȎOCzm^x9+ÀVE=!B"D!B) o=pS_?׿һzd"ʫZcTWZRp^&ebRzh9d:uGtuw{(IȵpAy 6N[6uZl]v< bfྀȨk>bۅp?jK*L5Lbj B E_I1lT "IȨĚkޝss qb|id돧^~?-^8ն}?ÊLʜ?MN<#eixIъ"2YLF\t`d dR'=9/iOԫL30tbnWusuzs\_7ǁeM\? vzf_uL"zd|$H]aKI 2DGޱ1{!+DWH-˰}n'jcq,(dA#n`ܰ*៾bƴYdw¹=/~~+iK2Dc RmyԵ)`CO]7}%_~}+3Qs-<ð!c!|&]/Ox>Z﯃mW="HI.fQZasꂡRX.ic2Bdȕ{CsȐA "peD(c2H%؈B.W -zP#]~ՂcevSiyu2{$r)+OCMOlen̛W6m{LSn2[qNdJM~E*,S}Nb Bho0zDvz`1/!GIyˉ,)MF+Jw͗ybm)DdqD!Ewg%U/_8pV Lx \fV ݳ+ѵkؓo_U}hm)qN ̂L(5 e(Ĭl qA %˥6(? /ئO57.^g/WKh%0m%c CHsLV}Gn<#/ox?^ve (&ȾXu-57np#Y=k|C *RCEv$}4dw?-+&\ J9]ըDCF賾VzL!B"D!b {cz{+0eD#1|wA XXyJ&1E|^8\JVOcv0gMbØ̧f®_5ǭcc`[ Y]F̦Cvf [Uac>Ѕ@Zl@-3\L]KiA59y0]6o$F9H'eYwOeo6yq־zio$XB{IYi2۝#kUceu`Q?NG ,79/^\3˧Zˣ{qw鉾xg$!d9Y[5* ;g-j?^yA?QoJzs}~wG7%709=\p 7[Sxa)Gƍ˾¨j;*CSfP nrON ؾ׍"3p4b *I&h1({]Th@G1xKpMQߜxece B;y)oO%own(E =%أ:!}LFMT(5<4lARb>$}kacoܱ&^>ipv\Pit%B_#,y۰9G6.-^9wN\ t9:l]ijc!dK~ٸ%RPIN(xFx_36x7C`c 2Vև"D!B"DŸi7n c#WIz1Oz+ą-mnqN͢mD 8@q͌;8&mb\bVD&G>~PUkYܻ$2 \duSzcCQ +5S6!=S[ٳxg6ѵ}YDz;mCAMLM$Sa ,8967=d}CE/)ݦwݚ5|9|{xO6RZ܇7dL;Sc^|-}sW$,% 3uHprk`zR^=H؆Ҍ ld:p'u&fsll}u ?HSԢǏ%=TvǢ,΁Ȳ,}# ,7+d S-sՆsV7̾bw g5_//vIn<@~XμqF{3:}'Ǎf(vB ]\5ھ]ÄQ4adW"Ѕcbh 0F ֏ƷԱ@9phWLfeLJ. 8C@ne97ɞfdX@p9*2tsܢȰq\ 2JEƋGՃ V "D!B"D8H FJ c' l;iB`ɜww4QFwI$/s3έW{_ãD*.4AL"\O̚={[ϖ|ٔTI|O2Sd-KE#ガ̛2p4*EFGWuKWA`ΘI &y^%1f"͵~A*tOAYRB%P %ځJ $MLԓz Oƫl|{LsI jrn|OpE/X[>TB@f ="fs^2ŮM<@XAxDM\0+ȹK;fkzx3J RR󯥏>[~ϩ-qb7[#~!IPI6jCJ4LBڌl\G9Dh/92@<ؒ)#M]gQ/!15iܔW4nޗwTh<_YF "adtpo~7`#S*GwjVg3眯ݺ‚,11&n/]`]Q$&&jbGAPR}o۾;g?Sޙw]z{rʜ9s}EjQYyc',zCphMY!@s9Xnl94j%D%}cmx7wN8 /(X{ ->| gQ;_ 39_Uzhv-B/2 PR VQ2U=.\9nḒd᳁>O1{ H|䢞,TlLǛwvm+GKeQFeQFe/^9ѝ?{Mk=hJ%AZ%DIR&VG 3[D[2/ty-=ǡ>0.o4{Ǝ=w׭#s" Nz{HHӀ[lssCBl2ҿ[ `F'Ix`39>k\u 6+"آ ~Dz^=-2@?>ˬw976]~sSR3,Xk׭cOVŌcN+{Et^2 "Cqq'= .+W%X{͚ɣQk{cic^aBġ:FXxux?|UxV鋡?ݑ7xI>E|3z@ne"2(Jzw0NALGN:N=Ul'BH[U$W7Gˣ~8q 38Ꮩ2Y v^Mx1!hcA.[a@~sD!WJcڐ*[Y2(2(2m}=]1%>9.*d=JV)pkXC3c̏)ny˪+_x}O_Klá1QATj-pOIW q_r'kw`?0Y :RfԕnS#($#e @:2k]8/[sXq('M vTY,bR3̤B]>@` arTbdsJ^;@ډ"SyLī?&$00kx,ͮZ؇u8ĕI{0b}]d0fЩ$`U:7G961]MxL@HcȑI#+DRp<Ӯ8Qy0N(L:yW'OKg_Xa"2>xbP%[RTg,ﹾRE8ʵIώf荣2_6i{=ƠOS+V/i؉,)/m#1J'Ɉ*} uj E8N9Nl[}3nr3Qz )@1l8 a0kɓ^wNw,^dGŚ/9vOG;u0Aq{{}MuA/V9ۚڒiML%O[~zw8:f`P{gHs&7Z w+% +VjB '|C*h& @tQ }rc0v)t*-=ErqNR:q2x LҟraAG+lY4:v S2(2(2(=.[K]}he9*yA?ǩ4X4cÃyĽPИaxf\ D[o~_犧{N|27Gq<ΜNr9*<t_(Vi|&Wⓗ=gzuT:7;ONʄ| JTB'iMQj7) m Ldc01 %){Sd yURP9G^ 0`rw<٢_[ ͉SȜqaA0FXWppߧ8F<:tˇV|KֶsBa YH3!j3scǦ(Ԛ=HJdma\8Oy/y3dyLB̳ =z Yd93х\x]񥚱6qyG\y%ݼ>̚+eX$͈2yq_ϙu%c8f-FumbOW'm\fD^*77--H x/nO+vO'b؝YWU<ЃlO\{X֙n(qpmE#0rʶc{O:^o|x/iD#"qX2xy J 4pN|y/W!Z٣}g ďkwөJPY$_h&1{}sX@[9-+ƗB5ʬwȿ,z*\4-5E>s2U=rCx_ԫ ߤ%aPn(#X#c3Hzsp@"v =x k2X89>r`)@JC!pc9泙>^Cm(zGG+6Rv,В#F<{&vܤ nY;J}{҄*GKeQFeQFeQѳ[ޣ52ޞ5Pi$3krAi+u"y Ň,cs<RSb<]vͺ {:31v=k 1'ɳ=P^߄ݫJX=X52:2 - h0lݿ=]GqSRܮSC ̂] ؼň Yj 4+/6hAو22G/4I "yq҂"37 QMAߓbb0!(;{ޮ+]iwT?>̀@UK0H@ǁ%\$&a(~*Tƫha+|ӬdR3IzieCker yS|q+z7l_v7U\a0 [}yrzQV{_`9*)+2p4Y%uc1}D8Qy( H#ÞlW\xO. h@9(a\Z6H=(" mߣ$t{YToZdm:kzC!ɪ<]ر@zIՇ{!PYS(JAIA3,3{wۑSlْVq <1Z)rsc 8f J{QTO` LU2֣ʃq[&nN!ȣwO7qH}N4Mڢ$(?6dx Q%i&L9 v(P: [._٣ݸAØ:t"}; +cgsvV2(2(2( ˗?؜M_{ݠ{sػ9)+za 5 iKXmy -vqu1IBP'}dì=Cg-KO|vҞ!y&Iz`$~"9c)F 2Uu }G=Sתb8q@G:9b=>$ s4[Bi.+-e)_xvB(~W6 oʫGN 'X4*1?KA&5 q[M+&O%^;&绣X Y_V옗reJk.eYJ戄 Z&+/ Qi>Tn?xyg\qX! J3V,B/;ܵA^FQM{Rk$ `=@yB6q;F6ѮhOj[\vQ~{'F-HdZq1@fd7!SL+cbV{X8;T/|מ}Ѳ]ؗ0Z7 S =.}vF| 'y` Gw7o[tO]rd[ySlc7v3vD 씚̓~n(Ŋb>Ƞ$_3zXs[u%4:"&(|vܢB}\ށ5LorI9/ w)xC'BI#;Ӟ;F@\ Z)Zt+QGwc㔆kA]4Q=/EWHՓ6''#2(2(2(Njf桻NxPS{U (saq]Z&|NjmDAF0$>r۸+y޼y⩞˒gnsM0mH oOd3Y}@1U>1j-E}7b 샍'_~եG-{3ȋy?+9j`[uii T0J!{E2hacE94< |l2kOcs1D'VJ\sz+/r]q2TV Q| 2,EO0+W(G E2Ib~GG:}[HJ9LZ Xm\xQhce+Cc0`3: A/~lc/@6!\b .Id3ٯZߎZƧ2w;jkcE1xl?m*t. `#ky?#O6d D-1}k}O-֬[=uk{z/|`;< z9)m>%ؔc;IT`8?! Skc9XZ?:q@8%z8zJEO-69U,;hfgckQgb¢M? 2(W!O(%A>HquHq)6塐X!,2u*d-ҷǼ!5![XBKA0 >B#c"݀k3{Ek i6kg8C+UBJƑ?nƐ B %MϠ얖'T彤L D: -(ԙ’N-UY~Ew2I\d.<_[h@2 t<ҾV Gp$srrMMd2z cY"2^=T,%7L+'n\pϝG0=gͩ,( Vy1  3 `C$6~7o?{9wq5WUGy!ym؝)KvͰl_VP5 I۞=QԺE ~xe{9sOv%ā^uH֪򊈅]0p{7аdQ2|sHG( 0d~1AVSƭd`􄽍 |@,u qkTHęa٘U~σ=ă[(k-uB%ajA2yleۘo|ћ[ P9J(2(2(28У=t<aSkPfnj oXa`.XM۵nFO;3ji*0`KOOE!8E+@ #L6q(%nU[ګH*"=r|噸FIuF}i ff|}öaC0_ F#T"9F~B%1d@<.S a^Ỏe}+RX+bZXrdE'LN1af0Sz¤"!lkDpqbi Gvؾ4uvċ%pp#nT /ghtV`쒨¬z7d(la#@cjv){ ;q=\׸cۏ9xűBܜBmhx)Cy@ii7>П2z>y'غ?U6IQT5Aܳ$\œBHZRmZZz07^q'70Vz?v5#R9lH^ v 6E UF+7Gft;1  gRGD̨gqɌy1=1T=(G !sDX`69aFߏ[]K+JRT:Ͷ$Yd; P~qs‘P"f®X0RZ摟F/kPжEc뀀 HZJó2(2(2(B,^x_;Zlc3e(eIз4&'P+xޣf:C}~U-*Jm щ6c`@ttW}&Ѽy}\L&l?pAh TdYx@Yy\?K]eL"e V2 >4WzIS |0WdAAhž$NB]M@@J_ ֓n5M6elP*q}J9>iuU2vI}C"H;09NAUʗ,; OsAlEa=I[ϟ?C;N$p@hf^ޒ'n%wP}L?E- }|?;Ӡ4IUt3ۮH0S4=>.zz&;8}'1NBy:_LM~D(5ؗY$N$,ȃ_pR26klw6ʟV* +CAQ7,d9G[f+䙜9+OWsn¿ljpZހUCIڔ柧0bvI$d0_~]KϿSOQQ2zXq޹cݷf'B$3Lke:P`LcK'P2-p^rQFeQFeQFef;_o>fB ^;Lr0ED*ٶC${{2%RiAkV8ǵ榮4zUV41:S".ɡΕdg@$si]AdtrUޗ%O+7Q_4~kԑt^`Ƹާ*y4`/UF!sJ`@(>L NrhX{YLdprJ!6+}Kg ݉ʸoI<^|h²?% z`Aهk^!"/ȿ>n6b(`aLAŵc""i`* =xi/'u2YPn} ~;X7d]Ȱgb0*xD8@sb~;o]~yI-o`;Uחh7&?lY3l;Zi҄%f6ҌկYFad т8`ROd'H'$1{OzJQ.S;jÄ᷾9X,L(X_e_٪ VXC߿P,>#'Xhз#c}-F iJ&O/9%k,\++=(͔H°oSdEP80}ɻ3a85 . d3®H3맣}v[!~ȁU';;^K'& KAf N8? y'dP8bF%veeϨr~B!I=6%eQFeQFeQF iWiXy0h'8 R=R]mQ04Q Vyp^nmS=sh݆eUUD՘[DE!>" M?SרڵDh,`='g蓼q9R>62[ϼ8KI5^C!Frysg[]!k1^cX&?D8&xx$U#"L0^̲\a_pFr*gߋhoޗ>V6ͬfW00 nKcDV0dEJ=yGW~ b:Z&׮b↭_9&F&}-e&:Qќ.Qލ#l|9G[݄@2i32,6F-(tD?88C$֭g#`lkZF3pt.c_f!a2/o-̰xsE@l ( {DIrA C֙yiw >}7u|GjW|fF`u#I[Y|Ќa?f߃HHipҜUHcd^6=_{qN̥_ ȣdtH8G;foZ9Ӆcm|c},2(&֬[HyRIkdEe QFeq8t/u_3k9CҶs -ãzR(2R:ߘ?3 a<|ˋx;Tcrv`N ,sBmCq S|>ъiF3w @B_\ž20,M9*cbi/(C6FrLHr%HQdcs~13P0I;³\f< $Z F2`Iv齕B}N w^֮9OcvbNV){D8mVLԣO 8u AC#شNQxDraw?D_i6ץY8La/DV^ΎiF͹"l;վQT$`䉙1Bb04/6m36oμ'j.M(t9I]_Cs2C_G; ^Cd"jmRkʫYEzpy*Jus?8@9=hbɧv?p9\Mϣ\zԎʝ{d41ŭ4ۑ/@e tPdY([hEB%; G7d)Χz8s~q@ڋF͐f9ǬK+{ rw=`l!(ed ʷE# X5WLݔQ((by %Pα͙؀_ C"?azVOEAr~/d> @<ԛ@cwyҟ2(t]r[?DߡX]1 ~]އd,9-Z*[2( >{/]):#ʠo[,#5BڪH]x,>&|>DJT1z OzommuI`53 +2`*1Q)a8#Ptٕ+OX:oSkdkvo>$ĭy''\0O qHܑzF?WN" pjwlCa!@vI(&@Aun+Q1ҀCژ2aK[o l3h%BC-'C{iO'^Bq,DeaXLfK ,-5摑cJTW3p *It\pIY=>T0vLR{`c{:*k=A(e 79*I$8U=փ8W"hb\(2r& ,גXG5{dƷhYq|mYն=+7;`IDړH>Tur^pJm, XW,A` P3!P$ofwI݆D×٧.5hϩŲ']&pĒ!ȼ9 jusF]yͺ=]18o$j@)|z8霽ϔؐ#4I!2[|z~yʩ=32g<yc,vJ#8z#ެdpS Cnir 8q8hY5Q2>lRAɵv~獝@|c'|dۖS]FM'`H[$ݽB `sWQQFeQ 5bM\{&*^{Y'1#);|ZUf}'=/^WjeQooLzȵxjg ]' ݬN&& "4#I,{#Kdsy`Wj WeOغk&#ޚ=zbfJ(1h4`QXDsVA$X >29h}/]u^$U2I,̓f:,[J'9%U#M%=l]& @l Q̩x0N$٩] 2O2/B=u{Zzggs杋\ڛOcrWn-ۧYCccOB@tn r`?1e07' i%o } m{..$"򌁗P03t#)yܥc46hn$ǚݝ$TmDB-%c!c M*x5pl6MA "}pC6@=b@y43y%ŽckC@3h&åO@>[VR- > Gʃ=eZpgK9;w苛fY'`D)6Rက w(ϸ'X>8+ ;~id(hĘK+&gs(T[2X q_S\p^<n$kmfA#A$ט*aQiBvRx/'mDJ/J 2@E3}uW2(2^qH8o|^ {Z.WLK[ Lh h6G#wۥmvȿ.ZteQF=qc_wxz ]S8 40N}z; HLsoޫ(x$}R4Q_ݗ⥋|쩜xƍ(xr䎍})KFf~d+GEdQ `*39 ͻh:Kģ0mx$X =&XDyX".  _,K W )*ċ)؊|'.=ͶbҘ>'A(F y&ꅜ nVߙHFn# THޱ Vۏx<+ i (MKN# B$mQlo>^J'ac0вX"xG }Iu$Kb )rR@|ͣ@9X;EcQGĝ'6xZ16>5U˹tν01j3TCyN&i1i mztw鍸;?)A[_J8@ej~ngxKx31NչOQEMnRpsN:`JzeHPдQJ:^eFԂ>ʊ [Gܠ'1[߄&dGIc\6Ok+Y} vUߝDǞ~i-蛚LhHؿ5=C8UHo")=3o4Ç~,^?lk <DZP)GF0JPQņn3',Y@Cjj]fH= zE`H2sk&̹QI6$VEҭOP#_zp=Tx/B|[2Iz 0rd?PFeQ 1>s_wԌiMj%r" c:93@]pլnyeu^nr B8EOeQoIdɗ=5 *{`Act*h ϽglI5z#')AGJbR;N5S9hm >-ͲX^hpW5Ҏ)wls;:(%)0f @XL<3ڐG$4 J4 }L CLKi~>jt,"$ 8#3PkaVDzHK|QʪPΘpĒHxRI~@@ `VxyWE, ¢a6>p1pSIhO.nZjBFK1o,#P dl@F㮳 }rHu=Dݲ[Ѷ>WE5}%ezd8 -h+'2=?*46 c04lM\Ll&}$5m(he|pH uy'mW^1}?֏鈺̓P:BLzE!dr9P #9et_H_L̨<.g` كNDvHg08A焺l7rYIo\?8w)N<;7m9څKvJO]Uxƅp/ bj@QT=Xev<+>Vt6.e|qiG/ls <DZd}lbޣ@{`qIb<3, t< B=GڛzMuXfJQ-#7\_EXHʀ*!d0Џ %|5Ih)<ܦ}ڐ&jR :s^\fW9 B1p<<,u\r4QF/-~S_PGcw#xnlo4ڎ8,d`%CeԞ{k{PjOY^gZcxX1|[Ie5^pyi/O}veQF/8{g{fRlUMWV+i"=(e|*vKS{+-I+\ҵO=8iXQ#v\#r=J2,m$3 zq1*YDJ<-{sK>q!Gwo͝+"·Qc`wb<94!ĪOֺ2<پ(DlK2#GKb&X3o3J쪢 =8\z| 厗3,6!!̋{)~8(R HUt6汤a_qDGb FV {`_I9֘R\bPKԖJe<ˀ;T^nJ9oA$VhPõ퀞:cO?^&&?;oWI'Md)i67L,ȑ_d%,[0ԵȂ$& "c|rg`fΝ;םͺ}]Qyl<)Sq}q.VU4،.Y[〻|Y+gֻH8OrN2X!{K~pvjJ~LΡsl R$UX#Â(sSb|(rF?`؋ļkyc׽\ ɺnU BJi 3`C b3(ۙiAu/8}qlq~/x6ϻzHޞLp(D1cPeߘ.5򓄰n 3J02cPab࿃ cd8б]Xo DAZX , * 7hwH˿Q=\`%^Ɉ<eII P^2(7)V_:w=kDؑYD;Xs`Gڱ;͎͍V}KNY?;'l8dh¨(]M/>1M:OMP"ѱ0 Ci~I`=sZ'`YSxsئ UWea$7Cw𖙣YYS|j ۢW|M\ۡ`kDB9)P$m͌VO:j=;Gksq%< {2Gl2Jt\v֔걀HdK"psp`K(ɭ\wJ-P =J酛NPLʒt[F"ϔ8*2P Y4d'[? #='48:C+3 <+1 dteQoR\鋎N73~HgT/]Q׳ v';cfEFu)bf_W3P7}G}Yzllm;6ڵ8Ϝfy%SJHMa]1 Il*u  ]d0<"+VR$H`.(Ow=Me+Q^2(ns9l_޸>8s6IFT[>7j?n3)hR HwfH0}dOQ11zh{hQ\'7փű4Q58k!\Jw^3:Uk[l9<1oGtM&Cp#fUZ SHnRLP;#d0rDI@h#AȰ:_+,lҖd0pȯhSIVxgXB\^Td-Bd5@7=1Hσd&eV{5j8~˖mۨ@dgV: ]Bِ܌Hb>r]ȦIXv`D^vd1>cG%P\|EuϬ&AT`򰃏S`BP'|X|[D RxdlzM׊2ͦϚ"MMNmez%zuZMfGLL_q*xgJ'2!>m=m QK4C^DQ0bKpڎ~G: U;@ VX!s]d{n0XI23pdI94[[Ļ9ދ76R,Lz/ aY]#t$>:kpZ^NX8mb5ieh~B 16Kx),-#3Rxې,iK FiPm߰d'Z2(7%Q߻]3&k*Esˮ|ꢥEDH:DI+({L>8s7Ī!cIso#SjYǎ-f}k%NF2n%OB~;FQc_`ql-z8X-#jUU'C2`xT_DmgDF"zC̺!Mn=ߗ^<̳"eQF/8NiN:g0{M,ʪ'տ炰QxCzɥSs d,pADY n,P2?Y7"e2ozi~Wql?qH"= /+lܾk;LlfO2@9Sṽ̌ߎxx}9c?]#c1|Z, $d㘝_x ڋ$`1ªˀ%y\ vs$#223WI&"l`K(⡞ExXsݙvw 0HC  7etcB만.٨O&C^`-r>寶'?Yv:[M1kSPEK)Ik{s}il xwPSI祚O8sx.ۣz'185ӚL^ͦ Pc Hх N  %θhλeԤuA 4` ,I4WTʎʬ$ڎmB}^2c*ad oc IK8CLn2* BWHA~I*>d]C՝@0+>Se/2~bk/fwfzl,d+ oo}Px,2_lgk;[3V350]1V2q? ٱ6vv4;[vcK&}I?(K!v+d?p')u;נ3D&}K/_ǧ|T!&臨X?P: iSmꖫܒ8;_Xj@ݣ>cMe݄Au^=ZVhL[yyOF^sEYr92eQF/Xv治MŠc"X=W )7 68 ބ]/_kԺ942[9}T|bY"깴 G?@O#8~=,YͰ*{_~Cq q<Xp.gI⅁L/p#\{=qmӀi`>@Dd+&C /D/F( LꜪ,{;CkRD<)FPg%@d%Ʒ+#J7n(qXd+a[Tܴ~yDیss Ơ#:b7rek/=]H'WJ1-,2+i둕cA_'q&^'8+I%7Ƅ=P0E˱vΛ:CaZ ܵ%bP?P mVbLo*]2()'ͼ:Ҏ=q%2P?iҟ+I;T6\&|,l,QllG};쓐kۇ#4U Cdt՛z*UU|,5u:/]%pb.k")@99d{}|fDUPs3TՋWr?>0i4IIdF~hWN%~vu@ǮάkKgH]6> ?,{!7;_n*t>ۄI#Xɿ01E/S V=H iǀ=꧝J69d{2+cwXv,RFe‹e˖m|l/mܿiĩ=w.ɱ0}TE4i9NZ$IRHalHˆKT`15b{'&6JiMx0n [HE8*su-Q!r!0dh߽dSnX_W^kwqVvǚUYɴFUϝb9Yl]yߩ6ݷEGR- ܘj{42o<ß?~ɋ͛?|Z@,P3*D??PrM:9GӴ&ai4a̙ᖇ$Y6=P8wR~Lrp2$4 w?'=PѺ  81wy5cKntvf&(`(Y#sž ڞ^J(~c~xV]~!$rh=d@ҬAYfҪUeu,I3 ?a3ɒD$#p%#QL@]?X<|chjX&Ҁ!nXcnE:ٸ6$ }i:6e4$- zO2/&‹yIR_J(;S1{rT碫ޓOt$ H`L6`|mpxFHH(ccccx6\@ 6I(gfUV{(tߜٳwַCɹLg~jHbu>3H&"K[Vԋp8R;Լpw~g;׶fܦ-Ϸ `K޶66_?z9{7>VikHOȺqEѪ==%Աnޢ[}+-vOߣ_#[Uэnt9wο֧ѯ~k0#$VO,T$!UG*=CL`aX%,9) `ZMQ ŗ\w/lXk !"l0 1+ Y1Ԩ{=י[՝ךNaWbgM$]Q& ap DYdD$v`r« ɯ'nHQ*&I>5&w#!&Ry_bTj?@,@)q sUK<(FHj@sW0$!ӑ4Ąa#d 5}wkfzѺ+W~g}aY7{۴uO:iI;lsvnr+f[,Gn|I&GƫzYB3]m*2c! JzM7s{mLouwPIipZ69F&-1XLٽS1]SC"5 EHuG18yLn `ܧX"[u{!aIg%Epe-D>m#&߇Rz U?Pi%`fհ fUKfkpq<[*l¸=&K,K>n IsHOnd+2\. PB%* ޅRVOcs7 P/VvכVܼƣzW6e$tYP AZ@|Ϊ*brB|uvo}kl# AT xį|ipӾ>OSȜCFX;b9ϾG@iAgkP(>Q2)sX@bt3.~cwW&ל^J <KxƋ>5x \ᵻwor'7UU"ǺF]j5z u@σp~&fn`.Dg=u.zv|?FfNp6L,S6sMh8iÎSb+O(lPM C)ݣ`Ԛ3E*3}^>N}zcNཝ7O7э2O)qϦ!fHzm̞tܞ~f0%el Nu=$)CO%Q`=@\=j" mv&i1.NҹB;56*uثWmMV?7+Sol3/Xku@w~+>3Y7w\cyOMA 'iM Ldd) kuac',[/.Z1 r# Fz3xҍntqhӽ|KXy!3I{HC> - juyo: V76Rha![ ǥf$M KaR"T<>p!''B!Uqbp[hIȽ Ti1&RK 8X"-J. GDQ-u2f\iP$[ j33kK y60I!(r  v BˀE |d0v Usx\r$uIxܤ=-),Ž=ג$9Zb|̀2LI)ud{^9dIRN7Q靆5?}_^MW4MA.u[4m&l!My/`@Y8޲87 $z'(&`jDvb`z0'Δi;dLq Z3 ڽۇ--:[,/Snsc(jҌ$҉pW2s};qvzF=W={pގ\j{Q2@VZITb#RPQ~-_RpA)bh$P("zA%`_H ,2ߝIb;`*eԋ5 '8F78g>g~թ)G:5yc%^vfVKMV-csW[Jn(Nkbw룜SI q]9烀LtbQ4$ 53=2i11VJTأFl!' x'Aw<pe8 vTH$8eLϱJ%cT0̢BN`!v} :o:&)j;b*zl=}nfӬyQ.s;? ăn|.-`` 8"SIg쪴Fk.Ӌ<"lgE^>Nq1 .™?a_wF7эx;O|慫OZQLʫcX̉> ɋ$BF,mF Zln7^sƓN/&~(^M0e޳430WD!Y7^"ͤjVA؀Ѩ+:6trlW%9 4md P;Ⴢ.#iQMʚ%hrɹhNHњRYI9l؟N3&q¦m}C]|#ӟuQ>UD&?d}M4aL2cfyuj&9!9n/`#MymyQmzGXƔ>R\ ޟ K{?x 66{a96&K5j:5 `cۃ$"K=|g_s`9/HNЍ9X78tK'~}t@σt,:}W.j ×47!$il}jBQ'fO,>jkFH-S?9؂^%w:b-셼NDƬGmLJ2r!3W&c ޴/Z.hY&} v1Af B%{F_>?w)gnwx Ογuٍ;Y S]C57qSTF!Y`QqOS2pdR]>9'aD53R!{|H*ΐ)/R4yH H%чB"!9M 0HS%8$݊{/AuVU@ %:k dGs؝=0)(.1PN9 Xi7.H̹{vܳ75Bg'r nW^fx,w7WkTEAi2nQǸ:}(A#D&X+ss!>{ېyN̗PM>*)jZ\65ByW 1] Aםy9LP,Ŕ*<~ `C6 #0^AWWlb:YYNw*q ܛi<:IvMudN%'EI0h0 F te3KVTbw4G><>˂x :/?3eIV`֒y $ C 6ayQ 1͊toCƿ۰`xv_z٥M暋o7D ò %50Ŀ-6oͷzXܓf[.zɺf/ן?>Ӯ~> ~z83O7o~\?ɵE c8IHԚ;Š )s]Cmu{$iŢch:{2q : MuZOPz 1QgGM$Vֺ:߫8L⢈qcE7=ǒ5Rus#dܝ >VjX|^u]}ty9_\gw79Gҭ٢ZOiA.&=` 30&6 t+`bؐBZ7$6 #N%&[od}<;qP&<*"GۘA/˵% ;`UU\rM^s/&?1NZGTpJa!VM/ S?}|Ǚ昄hFU\.8g mwl2w = J}͹;?̠]3s7O{vxrNZGޝqN#{пaW k2u.$Ǝ#[@ k+n4Q]׼B<ڛK7эn<)'r ˛G^7B2O_E%>Ͻ h3k(|r[|Q8,udݷ\tl86ۯzO d@ejX NB =m(>'6b\~\ K )^jny]7g}:uŦ7ʮ h͌EQz ߊ\KqbĢx`Zx1օQ 5)bP@uAD;,TYžynk }h1dk$Zf &R (yTT6+Y@ Vb*DfZa Q=\Vncb|u)-3t˖XaeUn>AAy-N\?#d+9\/@۫'{cS5e) eVگ+9SC95bu2y9?jC};lM*aش, `1Rg y<( C/mDP̭0pУ-̬$Hbad3\uкx?)_^Yޡm? w`xn_x/75L1G_ a'ھmw8KtX69߯9mc~oP}:A<}{SΘfY9PB:cѩ` l/ɯYJUzI9rh ۑK?譓b=c<Yb%ڸS肔w8193c@Io_wҘ](= 3XA_G|<]C<|DڣU5,<{wziݍnt=>}W?wd =il-e@!Ym>t47=U<$t*VFI:cH@ke|_y,#3c4!/^}bQ4P T$CJ暯Tl[H9c>z?~O'L Pk ޯ:{zݛ]{+f $산au&d-,6PP>,',_c Y:) M^]F[€R$LĴ_O,[yܰ`wM!ŗAwz>&6(6F^/2t Jk\3Bg8#3B|=u+Tkeэnt1̭߫vຑ}XeRWvp4cZR35g-ΧMLE La! /#~\z';۬C:۶2\}èٟa)3NmFʀJ* -Č+f@ĸ;M G޼|Ŧ7?mY*dv1^ ̬bTJ>E/>`ž%fP Ւy5E ]7F&ӓ.(܋Q~aPoeVqn]A`AXR^ J[@f[!uM Tq +`N7ad/ʁZ=OO%ҌRkޔa6wa&Wb68gJ,Pl2Ӛ,wgD^1)NFVft^"y>> nd;FbfEP/3"'G1Iٻ/׷M*-MXG3sʹ[-b%IKcg80[;7 (%\ L}* }n+,Jgs[GW05?Lm"' ʀټF%ɱ΋ !xɬGycydS>,w_~Y7;lӾ~yVJM \&(@nup %u%16OkTjxC5]Sl>h0ɶ@%9uTTLgĴ .}S#$?3laoM>~p}G]=F,CFLڞ QO'YE?j<Oh{M^o+@A #)8s lfKoyF *1,u}Īu3?~5_9#|O A=H^Ė郀@^b7l*lX4JfGL"2McxHڵ֣-H"-K%XLȠح1xGmyG`3'=z'âQ0K}7WRsLIz* /Ś04,{ YQAR84JoK#m72dRUCe@zQa_#v)ȠWQnH@)8R A\˳Rt|l(^OH|o$cV,xU9_y-$EW9|=ED{ H?&GHVӒE@JԽţtX`+&]2r28fs|+)址,q O۝ ^y5oyH_^+7`Kr[,KlS""K=?c/R>2*AJߒ]"kfGa'ZRͨ]1b_)9TZO?/..y>wʟ%?h4B6 Ap!%h Ql|~=2>.fp@b8ihpCp|j4pDNփ6>((m^9*-@6sInoPFſ B^cžsw֙>wx@<}q?u?qҞk2V{C<,)A -3mOe\A/-`K 3R'^q N91i̡Ehc]dPNzJܘ@ß%#Ȗah֫<rP+sO#oeԘuA'$`|gEIr Z­"F0XXhX!,v[UnHZuډfFpFukJdH`sՓn[z~t#kO=n/rBMRs<^jo.JTD:+=X=iّl_n6$7OIS6t|KR 90ۇj%Tys|w]Oj{4F7'tM_h}z,͛dRJ~dΨ cWwG흰w18w'xHX dRg>Ry!l?OKr] R|rMF$' Lp Y֔1àxVzЀ[Q'F=i4 L5Tov@)҄j (& ]6KNt1FuT4(2cMBFP0|dWH Cs`Q0#ԶR]/  mA)Ԙ_eX7(Ŝ ˲W6֑ݡ50dwG@a4DyX/:qnc`! J@(%xRN2Nx.2-;B-`ckj,Y 8IN{x`RS\ V̰Rj\gchÛgĤ)kAYs;[lh' $A!9{I|ƥ'WlsW=q`\'c9U!ی@tnP)(ks\ۼ'yuoxџܳ~<ܿyy{ۂcM]!$`)73<ȸQZbApy+P AH/$ј9SC9dph<.Y~wdz3x6=:KD7sэn<'_oO]}*7;R&@ v$é:0$`Ac=Kivi>XJcdRp\ceOmm..dp!Ł]vB[@ KA 4\F!yגʛ%#vGd"3Q̦{^x#Ri՜_3ח*e &mS?/ G /+Z)Df1Q9f&Rs\amr"KyR2j_e^}y:'YW$[C2n;/㕌׽JHNt#o∿&f|YWJSZGb8alyABJ2ڻGб2mIA>K)8-O w`3* \ZrfĢ ~&@ Td*aɤc3|- ñ]FbiBĦHGypԬ+wأT#yymƖ}%j1O6b<3 J_05EM&AtrfLaW~q+P=< CtGn{Κge?/쒙.Zs#TNs1 KX;fY}.}]9qs< eʇYKooyҍv)逞82ݕߵH/ }tk3/"&3ƣO^3c@ ( G@upE4(|\!0"Qz!l%$ *QC):~"Drk!~oPJG4e|xšӺۍntぎw~?uyI}^ȭ vS%@GQA`\)Eߦ3{\z?K8%[PDqW[k H2bgU\eD Q2ȠF/1ei2&h>,+} 2x0bfA)H!1̸Mn7bLFQ{jm$A9Ȅ2C%:iVQ%ѝuKAᕔL"ߞs:OU;R hyOko\໇`]i#"Kj<+hylhz ů)`1{(1NӍnṯfź+V_rMjLefI|9| >VYBo*~4JAUQ_{K/^w`>lnX;{HBKۨpk^1Һ%H|. k?rM$z{7gvQad7 1Fd\U}{S% uՂ;u(ibԡQfȇ @{? L1r`>|f7V 9?EPPu%>Fcc^I`YJ1x-{<^@@%>\x>">qO0 ׯ\?B{: d0'w7!g'_s; _7&QIyx si)><8D]k'd# {_|7pwz]ֿ}q^mk&>jKgp%'7`er0ߴ,T?b$^s%^yF=B SQ3Qa!M.6XZ$TM`L((ۀ%AD^i`1IЫٿ#X|mƍF78>p~g$T;FSb ,;'zӜNAu/O, Pdo# HWZq jeKF1eRҲ&]:CM,&Jy|R2 Fd(a,60acd0xy(ADcLR ѡN&gqy g8ʕČZc@5 Nq:]2`ڜy98^u}5s/]wً kL݇”4'J{?iZwF7эG8c+>n(#n7ȓOˍAfy<68%ՖrʯOܼ;mj^@Ϻd<_ETg-6e:v+=FV>D܀xjse{(f0c+ڴU!hIaP9aEQz8GTcbaP@g f@%y(<"NhM<z~\v?w^}3<(V{̞<ٸva-o})& j`n?孟_> HhD^YNYԽM0߼g"5se>h3S꽞Qkw@5v9}kpI9wlx穷uwntx䎃7a{^a=oD \+-͘ZY%'eV.RJw~'_~w]~``5So5ت!`>pQkψQpI9cH ЛRF)$` 2?#}8(r"r^i-dė(3x:wxyuu/{G/6х7o=3C@<.@N2vK􁒋o`! p8rg~.¿<t@C9=?~/+'Y1 'wTNDv1gEb 6 #I;`PQEzX c ϒQ\KrbAPN!F|ڀ9%rb%/=w6=]!F1vyCZFyĀg6VT;|XbL1lNCgypZ ^2,!PoTc$"Pfd?qacTxӅ }g$r%u)eMX*l4sg#Q ؽM´9(y>V%u^%g6M BG_jԛ~'֍ntqNS;w,-~)RXz1yTeܣ>K_mo넝k}}s"2,.X##@5r)k uTl%dZz6W̢Q'g ~G.k<& c3;4C8*ô|n ųF#Hk7ʻqzPm1df=}ҽƁ ^u6~̟t3F{l]9a֏g? ,8Y7"d+'',?X2HO)D+ -;;& F/EcV73a<5h u+0)5 fV"p6ls"llJ^8&G!Y҉1\;]g>sJ͙.J] &aw}uq|uj0u3s7@]ShYĮ)# ߧE}fNbPcJNy1YyKX13DQ@Ю+i|0TxhŸHbHH1f4oH?, m/ S<1`%, RLCh`ēR(ME:d3<>-ֿY2@{MQR-+j1_r 4bD2ZV,`X=qkmWZ>k>O>,'n+EC e<\z+?on}Ulvq2^UlyTZ}x1ru[nq.}L ,z#?} 57)Hy:S߆WX-??s X>qee&@ dgpOx=휳fd7эeO%MpJRخĖ$; >6ӏ( f$aWعIV{%Sl/O)6(3)')6Jʒ`.)rv{䂂cBIN!0A$PIkYPDw6: GP'Z`D#>`W?(|߶ ^#S`;+Iş'2;+%bbrP:)/FNi>z܎mͳ扸'f+ ,جlb^?M`[arp΢w7ϼ(&fʀN<р1{t=$]SՓ} tlnt[u=B wgC_'\q .p\i[Yh_h7ݬ-~vF98 VhP+E&ߎ]/1Fse+KcQnT1J Ýyg{sN9c5T@ ő&Kq1(W57%$:BIfG&[cmW"qQpWH)(p A湳4A8@. JƵՎtp5Yf2"1,J<0#J9h(.ՐDSIFC X*8k[!9|}ڔdPyZFD˰{I%:N閚̐1RdDо{W:lP6SـY $E)G=c*{G¤c>7‚!_(WGvQmk]rы,f>'>Q@5k/`$'%ޙ܊:<>W}c2]rܙ^ 9i\ (Hs:7^oόW3sϦ`J+qP a 7W*$jŎ6(dJ/bĐ5TSԃz`W!{j .ӊ N)^n2G;Fxtu}塿ugF7~u?|էܽj԰-sg.MT@~Ծ)ְl\2ƕ^o9GċQˤl'yUƯ5Q, EU .$Ba3N2֟K80A2i) It1?Zfa#,1R]2uB;윳#1uiC(2t IRk+ɠp"U0e) _&yvb(;M0n'0hN6^l5WUn7GlH+i⋱gp'ʴEysq I^M\y+&fMn?|]F7YG?+|?}6T0^ h$nT|2%f/ʂ9+p8i{wPі#VuK_ﷷ΋'̄FOYj;TI>ߣ uL\2䙜_6DhSs!JưE8&N?2$|]s|{}il3,]!f% 0,.6{'dW}n$;hE^%T񩻋uCm$v~0/^SM.y8ԍntܱ~գqާosc|B{,#~I i>ݖzx)4x>$[l\&-,CgX SQtd bpOCrz!jj'p'x"[{u^c:}\=nOl+AtbomEM2Si`XŚ_>ɵ-E JKa`Ǿ7^ySN#-=׿+W󛶽d}T n|7:P@& ˠߣlŋI3SdbĶ!`&0[7*xB:LKnFNG}|? *;/[`XIRLͯsG>yޝ{6 vC1 L ۯBYM?Oa+a96A]mZ1?;qpz>bR6RJb*DAs<8IހҰH'ٽi|iD*H1<,1kl ɞ3f fHNsX̶Zz΅,(S UFoā^_\ˬ8זv̓4x.t_-2W흊R]A.iIy9z! F:I2긫geL2&y0$ieajDG]w% K9aYI3=Ҡ1]HmBXҬ<_VOyZ,gnB%3ֈ7پ08&U ynTvKM뢋&<3:=D}ЬfiQ(By[7͓k<<ٸW>c{e>Sϸ?~AU= $Rخ쿃@y7`qO䡃e@a%Ii^G_׿ԓO}D*/u8c]|kw}pT/uLtI,3̈>,4?.}°Tə9dQ5.at>s5X\Hrh|#"m^9)͓+;J@ 5 hԼm7nu?IR^عA:Թg/e(#3fRĀ)&cBϛ*3gLmDO&JlX0`Rf0%H 4ܹ>b$@dp ! 0t{>dNL4"tDځ$o^mr|aTQap"cU=9JG\{x(2dHJ!QpsR2)bK1|\>Gt@D]j;?[oMf*yqr˖S\,`m,ML7B|Nf38:8S(I%vը%>B&󁍏#ȓ$WTgO 3~wSu#ksӶɷM=lL*>3f,(*NSzSܓ;H:*i P,ȂY*Q|lsAM;^ s':y`>9jm0-V_ yVOHB~)>`e r|UuT,F\L!PHIXg!BrbTY$,-Ӡ+P\/]r^22xRF8X'RŴkNq޵4b0F`#do5YKi79dnR{z(!nD.y rނ }"Գh`ÛFl(L"Qwbm 8i 6@ZhqmГm8$pR9G&/Wn\?"1' ?h!J<1c$ ϸOs8~Y?ak䧛IOЊ]9߳c<[\a Z3J>3/x~ 'lz7a:^/{~ùGz\l7?B%0 w0."{'Bzg6Nf8NoNda +C鱵Ԛ:ME8Ⱦ:Y_ 8wq\/dO09Dž͇}?7эr|䢏؟S7MztS(/LcRcp])d(#&KOߋֻ fD &ܘjY"Oesw= f@џ4ʹ1^pf&YWEWG h?%I9bwaȨA^]|J/$=s u2H IUD)n[ lA@Z>l& pDcbШ'8T&ɹy[cZ|1D{mk?;T@u[Rj#n碱ѥt͔0y` =9HΌFYȨ"̄X5p eEu.>ܹgifR46!&11&yYeA&9$&gMlZDfhnzo9{W5UjߋCIp^{gk}4.nخc( &^fKȇrW\, l< o^ 2E">(vf5cnrHbәg0$#G`QkK BbIxkkCyvXo)W|lz}bg)k ߉l~{7eɷMahO *:s|D (f\]L]u*J,k#2򌙴,˽@/Oa%.2a9`%əL.9{KR'B4}1%S! c@*7jb{A )9c2 n2[=SjxxP@େ/ꋿ:Mcuq5>j{,X0]g|,RO,t~]edwmWuU遟瓈d܆k,aӈ7G:꿽?rG&75"Ǽؑk28|Lwg;GtdhlLzpೋ;DچB?d8wѸ#М2g$] Cx*+q=6`GE\zfգeK=k2j 4QN-CݘDG'!ɸلEkd4H&M4LC6HA&%֒gZlQI6B%MHPLE 9$a]$(ϱ ."@ 4)|8!LbcK`KjӐء @v3- igI%A/)CL4+YϮQטצ!26iDjz8)޶߷& Kt9ߧG:0k]ږhg05ŷ 6߇z̳W "e_TT.ZXtblmᎡLz=#-A6Yr-_8tsTĂ"U%.$Ս ab3/ZiO~bܰ@BiH\`h\Hd>| SxCpG.&MV23-0Ox4+AϮ8R-sN, ၞj?HaDAE`. $@;h ʕ4ĦBRx clTdtoXI&48U5[A)dݥN":]?.Tœ=DTYnWs'CYjϑkLR'U  /=+. ǯLW>)z1{;y#mp J5jIl^0Mg9-h%w|+SOMoj8pͥ^xV]?q"~֟!k?$8;N^+sjC%l!! sW vxco !nƟ_2(Pt} tdx8b Yp@`'! <Ń[HCBeSBes;oQG=~1?vГ=*bb $hb2v1sf:Tu=f.^. Uq)Lӳ&\E<'lAvb 6!o⋛:0:uVU |B2D}1v(Q#<X2J&Yң,=I+v#yBa@\Z̚bv-jHvZ6aԜ|-1a7%|wA ̶>Z9&v^Q1anBWtޗfs7O~ԏ+X=QzǞ}/ZHtrG(_< Q2$%=b "3fpTRM\ VmSgSk7onk d/q_h{v|.z˄mi> X7£FbF'bDicq{ +xt<h!Jv\+ z)[eD+U]I?0Cw,+ޛcQ:6{Ȝ& L)ʓ%*ΦS/T˟{5iH ybfQ+:ĨZLk6J? %~ǹƯPEDbZKIj?5 TW_NJ }v4} %gw:WJI'ML1JqO~dS>IXpy> fE@>1RK!*VKz9ږ0UWDp\2P:ӈRoJuAVK`^Ķ.|^zϱݼe00־od\U3=)Ikȣ:c)v(`OK>uïyM7f;_gOyOWb_p"0F\T#[Jؾ9e㪩,װtIԌߘqa݃grbk-zd A.L/_*֕'gLr h[ \ث槎,",B%cD^QgKS9(&a{~`~ˋ.:wK=Qz7n+&BDr5$m9?9% ܶ/X!,AaVeIqXQ`oJKb'D+ǭID34yI1G܃nIfMxFSӓD6 * $ش4qN L:I)Qbym7Ϸ)pƍ $d2>}}2&.b=x,QxD*;&] 3In*f͓ПErc qZV-`Y /H O H2mh/=Q-`HNm"f&Iz쁣rO?zG=Q8#o,mhg|P=2xf5.L tdCSJHNhʷ͞c=|A?n<3_tZq=I6iQ,Ia@Z9}xɳHjD|<y]KyʼڶRZ exT+Aa ^b A2DzpPKⱊixIRf׌i풇jКy\>F5e4Mo!ͪU HZsp#cI0Qx~TGƩ\$heszJʐ%)̗H}P>I oc@9 =E3UU`1Ld@/D;`,,$1I-eTm&)̓L-e%\ꥢ 2#CR&:@4s`@nU9'=3B"y}dJV.m,A6P# k|h!:^P5䞁#I( AdX&OA/}yF q'?wyT;`5g Hmlț ]l&$ %\[9]/u|vIw~7^{˖->9 /篿r״޺Nm`9p|5vGyXo+ffm% ҁ$ِ::a߮¯ҁ. ~}*hY@`'}?~.n;|gg8UApݹ<=G==ce~i X/1 !F@QMyt7vp9ȇn9k +Gǃ3LX`'3U!>ji ~#8( ËQ*@ˡι]D;G6ykx1 @S{̐_g>i+®IU3H4c$>SVȎmetiw$+XTW*z AbأgK7'P:+$02+]h\?8@  p:S׷]KkiAQqnH,Boדr]P/` -D^\5ߦlfן0ʂWz2q7*Y̙11wjwd@n&w]ho.j'݋ٞѨN<[7z+Ϝp '6gKhE{^瀀pJ>?5I'mO9LCc=y8&"~QOdi_~}_kuko^s{sO9O_Xmm%fA!]wNph`2)V8n=ڞhA cn$`,:GP A)hƙJ6 wæ">;\Qx<6ȇRSē:NgFN=^U4*-'lo Xlb}Z1qstBO,LeNC}m. *mXJYakI J` 0'V"GMZbМY^:4?O3y.4[1w:.4w7a/p@G`LKkxL%!u"$=(fOB'/CnVLCVktT! u1lm"5̃.0)vbGt&{ cO4=~bbH_T!WuɂAnK]2Ps}y@T0N?mϽ|!j[M}w}.Rg#ljN3g~tmrgwܙPE-*b }J#]{;0,&j30f0>e·}'/(zƕ\yŕ_;l3V^[: +t l嵁 X(k^>kk)/Xb)+WR޶<H@rNxؓgwuDcU):G'ߢCryo ;=቞po6m}WC;(ٲ.z%5G=~1?d}%}kBN*2DŽ%gvҳ$H{JltIwl"tcqmf[kZfY.FTi%Ӹ-$-n N.t2tN vَ.-R9 5I*\J"UH*d,U^Ϟg XƎ;tkLy dZe?u{ &' aKQ2&tADAWWYk[9 fl!Q;(bSz%4`'& =~cn4(gw(?Cuk|͎-7mj_ߞzꩵs=Qq 7ЙɧZfL\ӲRժ->v+⡗S_{m0 x{\RBy\CF֐%3IؠŠ;nk>w52! @EQ`}zBO+6 ^f*o~cSDvaP؍`vpHKgkxfB±1hT#%SPD1@ϡt.2[:'MyAc,l K2=̠ufPQb2:0;1 Rm_̰#UƟIJ@ ;t *It`=RHA!IlOpQQoPW`,>, /@UÐKs/5GU&ChY3HӴ9;&A:ͣ H}J+kלERv]^+%;Ԉ|"(> s\۷eÚ, +G&T `q-lHҤ@A %N`H~= ٿu=Hf'VD3E<.8;CM[oyrwɞl0Zrݙm]qͿv6w64 rq= q$KRL|b.hL;+`|515' أq^]rߨ /D[/,kOZ󠆙Cy`FxbwBq13jDd?dapwb2(='ClUblE)8u໖0 G(a!P`wy2'1Р4@Xn~Q=G=~1:8g򍭅z 3CN^nP0>э-(w@ C!JG"$sɑYgͽM|nMNݔV3ys@y,a| E45gON$$X{,|8`f$mdpR]@LьEJzPF d G[ñ?1MZ|יS@@ӴCh1;Z=3ù@RўD(A窲l[ /ym>_S . \VxO,o/~DE 01y.kQK'ʮil''A/6 &}J|% gLK2.R4i[ g +VfdE _9j+TL^ˉ!S322lب>3PKuU礉e#/J`*RHq&V4|T:kV[6!B]tA*(s=o[6 -0!L~ۣ] SS9F@G2yDDsC9q1,wҜ雺^|I?2C,bCs ;P 7],4fg ͱD]:'& # y s~BC7ZkxK_-};;ꨬz.d⊿;bw=o[fp ?ݛ]U@ WfPj³~nV1h~Q` ]1J96! PIyeUa8q h|X\dsa<{f#A1MgJ=Q_vlh>9vyj K0]IPm@d;fQadP)D,uLKLbdߠCWhTJ2kC%ӽGB62{m/in?"E4Z"Q,uttu(Aь`.c )LVD̐>Z}]%K $4Μ@Jݗ*bV h+j<1ͩ<9uNsM}*]N x\<ǭdۨ)<BOKdgWh gylRH`%Kb"`ܦ.xfsksgo} /+^3N9ccգMgzKzY_ОKoOB~4ÓSi[B?N;mxE~NmT(+1e)f =3giKELT[ b:s;C3~Sp`O,Fvuv5;Ώ뿬LIMtpQO#NUg7{.MzNk# [ˡ家D6ӎ5TʲxTop82k%2 ;Ɵ>qm}ZpZľݢ^r4b~9] E,Ήu .\8F91yhgc֔~wv fYIJ[O|vf&'aDs࡞am6hks$afmz?}zԣew|gWހ`HKB~:ɂN*YT$D4U:-auY8EffJ$٠%GH׶ 4P,))GQĊd,\FNEVN8^̔tB}Hv'g)= IT%(#!>W#&tzߙJUAmLC>_:xdKDB rJIأ:Dvw'cXM bc`&ΡEݒtӛ *3n"w~X_z 93om@y<]}q:@m2Fg}'b }-tBF| oOO#?Bի^=Qxg᩷dӂ{`-6ڻyUm\uc|#?}|ݏY;;2+BV VM̋BnYȩhb y?o?pLx`Xk{v2FP,T>?rU ZMH%*N"I}8EcѩTX`/E\;Zu &.l|w c.^ HC !ܕ\>LF)y&ms*'~MBUYԼE$:{BsT).1 ϋkNԯ+>"ɁM~ 8 q,]{w Jd@*, 󖄒x$cU } ya_gmHo(W5תnWI(m׳q$+wIH@8o;YjA ˊ@ 5y,oLϥqtKbD^qk?$"x1 gAr@Yj,+r.5|候޴r*yнʤ4@M/+_x ^p /OC)N?y;_ \?{ۛnW.wŚ+:&=[3CZ#QXG:JsA%H0A>wLch1îcיeT|[<'HSygCm>~eKƶ ii?sԆ4]ͦhٮ"6nȦlDZ7||3 EY4+f$k߯P [@6edA"zN-`W64\X+z׏nm܄ Rg?Kpl߮gG=Q_4>O.z%Kt t/>2hbی1X%D*E!`ni @!w\4[BM`J"/-E r2&D %"b`Th4yLcY3'wexÑ߷ms3]ثA8Yќm4Q⿮xpw[^VIa9ǥ(iX ffX''PX"3@E\ ۚ10 ^Jg~VZD33?d[[ z Bg;(Hr-QZ5O. X4n%Wl`:(1#p%z$ DW$v;:7Dſl`;iךOJ0F)6HI.<Ϸ$IӰfu..4!9pDDƊ_t%ul"M b(a BO g`?u{sxKSzk;cԴm.sH'sߍ~C-}-:缷Ͷp? y#[.7HF8%S;'8B!gjd©MUqyg_{fx ΣϼwX<|\/43ԫ2! κv!AI!I{~L$?fKL3?[@w ?KYeQQy駝jY1n_]tw>fd'vQKX[t.{Dƶq\$J{[$1to:jۨBEYlX9ZCaϼwvXDJyŚu*I/ Sl.T=G=~1?ƃ{^BGұuHl1ۙ_IhETgdM_d2v-(ź34 Ժy<|$D_" 0xfc%a ag55gгc|2H:"hgf>BbbU[s|0E3K\Yw+;v4^'#&^dz;9̨]{ 8}:ڳd#7(y ͠θƺŸ'bX`#{nAgpƧY;kzzH@ ƞSPX^8"0iΓc'\Wyonʁh4~8̂c3k~շ<{ۡˮ<ԳWzԣ/qi<4Wfz@y'kalUִg-x_4Os/Z<%mpR%ƴ-mH]RE j=&kgQ{tz T?m 7O>@T9 ՞PQlգZcsɯ蘄GaȢ̜,fdTR%m XpKf,m겗惧rxynǯ4}ߴ-{?Wf`ސb80hP2-&䣂@M`dX6Ymx?f eNC  5xHm .9x=nC9&0'Gbx B|lUM$%~.C6])d*HXɟ%LrcUL龘$kT>|)uJfb׎r^8"2K*ƘO$Qs-evD%cu(6? hFfH7 i:Hq1^ 2|X5(EL 1&?Gʍ{n+a\gf l$IcFf.hF33esg?rcUL0o9c%5c)[,Qtt) ͤtO*>{ˏǑG9F[M.ۮ\]?S=[X7Wn#޸cO:X~]nAל GLcQ 91C}PYK1M'|r~Q3z]rZ~aK/ۿ39uKw~zIg ֌naB,g߉*/F%xڪ=0!0)a4lv^(D\Z'mZ:LH$+S {gKxG/W0\Q_zԣ?olHbPG@2j3ɓm-l7gA/Ќ1TUˀ)?^ a: ;X.8CbYs $dhY Yپ?#wNI[`.'rJΙH jb1M1ϥ9Hl!2%P M5k$9.i*jX4 h46uK]8ߥ^ytBվבx ~YRsuٙTJ1pf޵:xdQ XɟPkv9PXN'q /][%,`ZOxFj&4evy7c;.IZ"֣xN_2˲XJOv3M -WSܼEet/9StWwId¶r;oY9c鉣RmU:?{an߼"c":薾^9YDfIYD B'% N ˀ eY={35ėuJ^9Ծ"XPd]чH2[ y9cLwFq<>y"(d rÅO"A@m];<\)F clוkP)BܮIT~YbNOX%J L<p<3iY¾ qsm A:#t kEl1ɛTq vF@OL J,Sd0eq=hYzD׍t ચYJ7aM@S=BkݴX\$>cr4ӸUGU%2|DϠnk)2K_=8'|#d-"ϜV=lm\¦ET)aj/:Y&$̢ͧͩ1,uT_ )L?cTv-q.+JJĜWB d4`< Aze ʤB;HÉ,pÞM#x^^ޱZrd\ 4O!rNFJ%DXzH>>2`| Kb&[hMO3]Icq Pfwf|'lܼ{%dW rmBf$T@0?S5{eխ{ʹիc=Q%s%;~:O_n'gNS1 I1+[mxa񦣯y3N:w x\rG))6^-J!E` JE׆[7W-YtU_Gڰc[kYzȖC-1c),g.8w;e 5" 7^@Ϻ@0#&}Gv_HĄe0% ?7.J=KOƘ:E,:8U1X &2;ݔVz^^*֐ElX*E*cgzKpy6&1ZDcs O9R$ȁ>ub}(&gz\[I9}IA8~ɮݟd=trmKy/8/ZQr]֣֬x1?dcϺ%$5@ qA=p9j` s% Ua` ?UFmS"ؓ$:H$/ TskBRqҿ>2:&vtuC77 l\)MBi(ԩ怇ʥ9)KCdJDkSl/ܡed K܁uYd6t銔f]Wz/uѧD`8NL~ԈrRmn?(ylk( {'A i"tlC6NUtzVveB'I.%yI%cyQd^c#1 $/Oc5үDEt`l˼$WЈVf@%gqY-pQE alhAGˍ/y ko^{!O~䔏Wzԣ/q*5T*((-"nAfٷ'm_rҵ׽e5[;b 'g= Y|*pd쳶oIE('.\NmoSNn= ?~Ц;`hRG9if ~iqK@Aվ ,&Gƌ!*Nj$ksx&ԡM~,1F!brP<1Rw'͋Z-kQq\[( " 8'*&n0?FׇN3kbu,i#`pE~ɥ)ŅbQF_Lb;vF_H#Ug>X ԩJ/CrT$M͸TD,y(}m K'(Q~M,H)1 !2$Z3==z@N%'ᙔUE +f߆׬I],EQ@"&S[8Izk?l['96wwTPѝ+s.pڧ.[:\󍓊ƺ^Y6 Ux(F` 8Ճxw*M,kkp&s8҉-;=2 :\SnS3>teW}c ~{F6lͺ|)r'& -NG'"[GFzXƠzaTÚswc]2G=f|[_P:w\8_`֫Le8@1 541:CċX&E\'u͌X6\u1Rწ'ώcFLHʮ &>$+U][BYt$f~E k#T6 2IvJϑQ:䕊eR87tqg%g8V3R\_F8y5 A r#BT Ȃg%`e^y +r?,$|elBo,#m_F_/-*xEќq_YIxހ1did 6V5$X>)W 츇땲Njm>u67JQ|PA2t6\'x#9<#?r{kG:,2HBcKu>DsH!>viڛ% s6MG}c:|o}'<>֧!31(-gC͆*͂0BpqEBUź%&4) 2m6ڍT>>;9{IfeJ|V˴y-rPRfQ{4/;@d9w͸ ;DLM뜀2R-s38IKL|ypNL1(RT(>d!$cbU:y4/Tk\2@C/!Tv3$% Ll=\i.D:[X>i>NH5n -D S`)X'ɫf`$y%EDCr N&;,M 쵏/%kL% 6>iI,gL8lA' ~XHsWBIyt>c^Ʒݹۯy۟+vFcpqI(i5/#G0&ON@8ɣ'˹N<=J)`FeoyD',?ϯ쪿) }\ztPJo7"|u}9|""j(9qiL .ԣxmcpXM,`)hK@WbuN̚EV!z S{) j2 } K!l'ҚN[BG`L/u(cg"V% AN.ו:$;pJ2Xz$tgE:0.LBSCsՀ! K"T`::QP5C܈֞*%jFJ(vɛ'SI0g8ݩ""Y-d|rB$Cu"e)16$z]sZ#Ko4З|70byZxY6{==9jk^1\^.9aLӋ#d| d~૛Wo}ǿ^uUWzԣ/qܹlO~oPjSW=MS{Pl<:3=c_m6 +$=QH'xH}xWEn@vtPԡ㌢@h^S-1Il8 `-:| dyLLJrla^JV@3C &rkHd U2 3 H8oc# 9ԏwŰan:o3?V_]2|YmԤ9wS&ћ2 >,m{6Z7t;Z 0#zVy湛3]>eko0.t[{eЙD]3naWZ1od5ҵYA]mDu& OD% R2!2{(*t슬Sgrj(դA#ɔ14^fJ oeyu 2rIvy6'l/ y1EO 0=('b[KT@00u^K2Ԕ% cY@`<,x 7W`Z}NT@VKӺIۑJW?dD&%::47iTLέ6Lט$F/fiwN}s\h ڂFև ΀βܴs`AT;7Oi.WV߲sC/ZG=^,Sν_)0VD"j. cpʯO#;OuPkΏ ,[vj>y;!)\`%0 C۲j-3 ,o*ehUM^O;(|Q4Czg4Bso(+kDF,*h`BBf8 )dvq@aIM/b|~CK9PѫQW|XN(+EF2F{(xq8J D$0`PFp~A{03x'X5*leC|"?kzBy3G CG6=PY8u&d %yx*Hf1T1B} _8w+``&Դ(k6zf \.!t&9WVsvP L(w 8T__b22Cξ\O=Y }0kT2|‬)y86 j犹O$D~e8}u{tMy+v}]l -Ԑ=yLBǀE0Ẽ\*Er-0WtIg\v|A<艖:X?uƶ#UvH eS:W^vS 7lG7ZV3ڬZJuLp;ѐ>g'T5UŰq7zVyXtEnq_p7[týTR7ͩSE!&[?{ʓveٿ(k:i7\s\\eZ#eʹU/Ucc?v~=|O_8olSS^<#`/w%?T=z͂m˷8 |!Rw;0 >R >[ hCHQ,DNy7KK )# <$%`!0gf 2M@(Pt~!#Oy@ r" $+붊)J `%3V`;&+;]>XIKb l#wdfBMF8JtJH(g z.F8Do(fZXȠɽ8?FjJY_VoDr&²ytZ#Y3[Ѡ^TxU2󉾈S6}$!X${ EPP8' g~E_ *Z! L:dYfl6>}#eoxƫş=ST֣xq=3i_7'Vzݖ& i"^v*{p]{Qi9Gp 1^*&RY!%i3$eX/4b-].ߚ1a<,8Yc HYӺJ$&e|$*)8:[q ^O1@Y]" F 2 8-A2tIw{iG̫#ֶAԂ8zнhHA1vh/93f;ϤW-p߾IYct]k#PKg8KKJ}|ik< 2~mОWN6큲lWk.g(6.6Ou$to1yͬ'.N+!PvK:- 19_wW}SGM5S10zЗmnph6Yh9<mOji>@4⥀>Tuqő旷W&E5y/e=xRрޢ(z[-7E')ݮv 嚩'S)h9Փ;ʪ}4sw} 1Hc6_㳾m7ZOyc]ٽ[Ϙ=oÇ[| Q%LMm<?ngj5y }AGtU\/}sp}eˎɁɥݿdt /ט n⽗l?gO-XWk˝^D}&LQj )7/[a@6pEw..H`^ "HEDGdb1bٔIQx)6J6=SާX?ƈ ˱wi'fs6W@ l}g9]!@!/}ABO$I{fq,%'9pZjD;[$KR Y"(YUFE DbɷKz S#rSD0~e j\}{//sE*=5/ͥ*G/~M8/a^y?(D MD7ϰ!Pt'-ѯ+nykw8|E\tW֣xQ='oݍolS L(_>wM] :=0 6׎-.kn =}PPlA9$1f|x7d4g8p3oAfǪXѻfac7? qC=6n|[@O#*I n'"/%+"KDW oD|KA9yel)bO(9c, 96VQyWQF+I=2ĀOa0~M(܃Dk QAҲ*0ܦ@ubԔy#'P.t@på&k5jHbzѓC~}ќXTQ,LxUBRv`Ô_ "O &' .F愊0ط*iK(9MAQdKQ^<',(GNDF@ŀA@vl,! zG3 oQz8J܎pk%ɣI&KGM4fGbL`8#(7Il%<'V UT]H0}kA]28bd?OK|Nu3(ו$BmM{E wtm߽L9mJR,adGv':Zb&өz8>g~8zV,|cC5R!0dLέk~ny+>gk,ѭx/κr` _!ke: JpY0˽7ktdSS] 4Lja+kj@}h( >I(ƚO==/ivjqрGՁ{&̆~ӻ띭Cޅ}(N>}=>lr=~83{+s3vb_]j6uYD[VpϿk߽E#K3f &!C RZbFA0dz5bD$:آ|KۣL>.$0{d}<佌ⶕy+ʚYF`GD>WB X"݄nE$v?\*=UuV IxVwKd|l=cx}7l <f`X %־^{]*3bDq" gڭUq\AB #P}즀!MQwRu9!|q<u˵B5@EUGzI)^fU"<++6e^{&rZ3r6ʦNT=W]܄3#hK}uy8 /nտq :~?nꂊ f l1r %i"YC9(`2Wwwٚ-l=oʒw*mWlQnͼLIͨ%fx-o|s{ʫň tESiu`G@YS}xbYF~pO/k/?zo' XksߵN G:jPWz4̇\ ~lR"F_um`*A_TlOkA٨v9bY/e5$5ٴ0_k4I`cR⩭ Lv4~M0h 8~Fe2-Wf[M0w/dYʩ!?+ʽR*DE&@8 }S7+Ś&>'̖Ʒ(umC:C`m´ׯt91+{& 3=+kz_ן!(&F9NUWKQE׏#Ư scNX53 N=CD_z\>'曬<Ғ2 s!3;p8f>W y(s] LyMxfUݧzN`Ƀ4 gyǟ[sSs0$mnPa^kε9#REHrgKg0w&r%9(E^MOڻٲ(4Sb۵kM7/Gc1jy0M]=Au\4M >>ƪ@+vPĨ_[-+{>rc_mw;}S?̡*k`%q .4uJQgUmSQ"xMmTjbEJ;Ϡk|W֎UYkA3DYdȃ6 E'8WH[ $ӂP\1~q,%SZߥ܃S1K pEQ/0tL\p6LTq[@E#>qOtø+qN5pB^,"tɣpHR gK!c7PFOmzTQLaq{&q4+Mm<T9C|}+Gd` a@ 牰lC1M=*H)Sa\ܭvgSG^wMݶm[̬lT޶_r;=;R'}ecZJ 5F2v=g^yC}ɶKy9-%6 e$֥۬*g\˸.T`~@gޭwCc"u%~3f۩1+IƁuE%XH9kcFLSuP+*qQc@~ߧ5dj _[Z51Y<78֮XbѪ[(6[hęGt_W53AcIP : M?ѻ@YC%0!i-R,XM 7~I|YXa0:2Jb3řkYBVKEr#9NK.MX5uA;1& ÿӂ,IE2c!lMCDHkМ>Zs>B{鷖x _Jv!ӗl9Ѐٹ҃El>[QhJdps&M}r jaPDZz E,Ǻ 2^9/h%H:.o@1 O|=?վzF_ks;kץy8%y$M&q\s3>O]&:zFٞ2Dgy}| Wb-6 mMQSʴ]%(Mgv} _`_O'?6jO+5dTռ#$Y/ =kMXQf'2?Q}If=K ŏ|ߙ옟G7wA@QI_^.+A n37,ɢGߏ_ Xr_0G%Ƣ XAGۆvl@VZ<[6㧿|׻~lw,~X-.%_6sZ`zi>&`c>Bf8c ˒gJ1VxԈװvWA!tL43s9b? jVD"@l"1d$͐оf<#(x07xٔp1Eܯdk5Ƀ~-|3DL^Lp Ae%26T au-Xuuu#!N_-d@عKp:|X!`T*Ձ<mί׏I.[zؙرs3ArT S7 N.B-X7p :*ޏHZ3d RA# t6tߜݺ?P'>llO?~^u{h'~V;{o%AOjaϿ?G? kx> uu ?p,|K-_HVi-Y*-bibkEv 3[pUgOdl5JF e*Ȇ3Y*}z ƁoRb&3{C7F@}DT<8Zj>O}bL$d-p;RTmb@m꧜t{8o)z=y~x1s"pFQfX`T]XE/R y?J/ 3C@B/x/%˹U^3U%n/+y ~:BrK)r ~EyD kDSh<ʚ"rD 5Gޟ3yJ8 ~E&*jfk6]^g%WѲfPML0y5X;C!P˹INTg4=6xɓ_w/Z5Bsht^(Å Ņ>> V{-"1?-7=:;sa8sJ#\dbТ4ŁxqcJNd [}`gWL {EIčt[x( jh lm2&s3306zm_'.k~;#o|MiiI0Ś?oV;Pi@ EƔs.>>VBI.G|t"uJ4{Pb 9Ȁ] 2Ivub3&|I*D>,xɥ\kt9tqAc+NV?gM}R]ՐN6Xe.NŜׅن]ѠɒnJP0DD`†n9L*V7\Rc"x7W]ӂ-"QLjh a;(7\c+kŽQ0'LRu9wHrg~ϣX"ט!(GĖ+QנYځ}ΓGݻ;jcrZS zwn :T7Tₑjaj6edT ~=ŭ__}<<#~{kffkf{nm}(^EU=<5tޯ}k?yUkrV{[y}^-ϸxnpV֍czVKoj_ǏFQ%@g^&>Q9mҒz7u?դ| lF?E|Ow}QNqQ*6 A)dߜh]UDֱ6^O,:m]V_'VغL =1M18oAMYl;ln.TG),&w,c Bf=N"+_ipUi OFCSgSҙRYRC v]HjZsz0={((ݢR; Ӥc=8t&3^k@vA~>q,!i/!d14}xPF2Ì"pUEِ$% 5~5,RBT-*Cud6f W9Ыcgnnc୑nkvM;Ν:d"tnSM&L[.iPbbOmE;_.ƎwZ׮fyK1dIڎ@_NX '8yыnП^~ t}C^f;˛p\ iO"M)-H)Lh׏j{'#}ؒX?=S˅zm'w`G˳pCdkT']kEy8V-I|O1[dWOξzjџ҅cs5kzϾ/'톽7vtOV=9l6#d>M^~΅:˳<;T,ڗo"6*~"@{֋.GbiJ R#J/aX5:Ϩ恧UsɆ ,Vܨ6R,kX)$$eD^:2.c2)Y"d E:q2m(&bP$JϩiF3{<81YD;,:A%**)/z\Hw0,x0Z)e$F"8g>mCY576$DAս*|s=|SR"+%Jt%JrLn?.!n 342R]Q%n}!}w߿璭4l.z;=2{<0oEQkH3pwkiՋsKm{ml48.%X%Śȍ$*6qe  %aTCM kA*XL(x(I4vXJr7D*ԱVEyq%L֤E> ".۽ U&ub]96Gcly )epT86oedC\0CQ5ٲˁ( _S M]$`Ea(Z.~TruVa`2H-_yVuAz|g<(`BC/]Iy7Fz0W!>BB-w?`0`c"x kqv+N6%Qɤ Ǡq|$+A,N! |[/<()խTYbs Kfl޼{ωҨo\e ; !m aO9)6/F Qvs6m:(*Gm tvoߺk<qNWCѬY?7LD8p75lawbǒ J5#T⤱r.(td$F~_.T5o[";HPʇ:?P 01t<&@J$iR-?Fal=X (7/U cDA7VY [%tÜ<؆GJm  S b`A ?L.TZ,u(yR DŸ[л&v󬴠y Zg8W[Bܚ]~eEl֭RlPUY"9I;t"#t"<}oؤa1OX[хCQ2\dW0 2{󥰭exX1ZH`Y!9baP5:J(s>[c"c2XdG.쵠=(e䖊yYWY9X~|oVrKzqbHB00DT/}>sfsw(<7PJ?x4%Xh՘J߇rbY+_gLVY(ϚR|AU)r+!=a ̷h5Lfmi2˛`jH^ A^M<py,|{0ǃ6mf/ndG]0I'  ecQ桤8/ % YzVlLąAHmC@4`b`36 |:v |༌w,;q (d"vdjX4Z2"vCSRD;y/pl>4t|JIY6f.e\ `@]Nً@p85xɲ?ddr?$T ]:+}RY/ݹ+=Aaw  !<\d#%yF^=mDY](Iڸ).kc}t.rXF?{.g4}lb%l5&K |}l*%ѳf=:PL _ο/k=ozϭLllOi#{]Wݛi5䘂B7Y%x |>?v?}wjtu#>ֿw*rN珂E^/_n DWreM v. BK%RAЏj >sn\Qk{_L=lblGJm"fAұ)g HeW=ࡻb\/o8Zw#bZM^"iE8H)W_3BŐGG_0 gg(Ο2qk{X. 0܅ҁXFRc"+l&S=Xs#M3HǦ2ZW30VIhḧIƻ Ǝ!ɶ"|ʒ`?_CRA.Pk gF787t7M%YX@eOxʱ,p)^f-xʥ4[%ˈȲk[+l\Cf&Ԗ֫"@DzK\} %"+1ol몜lF2]iSa F]TVM>T:絪/%$:JUl` R }r(Y+bkT,AfCcP *ݬa NשD>P ϩG @bK,_< 8)7ƝӤ{0#klpHJgAՊ`Q ZW*&DSZHIm0(ES;o1C;7 'WR:D. X Ĝ"}D> lMݻo=|dǎvpN/xٟ:u;dmFN^u(P-a<>.6F#e=+93ִñady/‰Glu޺z}_vOm/ T@K!8hOw6wGZ0mk"PM7?׃r;~hE`n;iq|ɝ4<RQV!T`$xJ/X" ʻ>ruʽ!(4?߇ȞD;Fuvť7pߟl~Y07۾xyw4%'@~'d\PF;4őIL^oWA,Ro͑ئ솨zvb$0@f,xNy:lBA1*搣<ۄ]YAzM$zouEgR,̋$XQְd CjhT"&bT;>*hU-+E1 ;d.%P" ]K'K;\]ł迨:F>`&-ćk0@K ΝY$v L4|,}Iex-:3#}}daY(K̷ğDŽO5&)1 F ͳ$ :x Cub7S |#!kgM 0 2VS8B⋔A3c 2U`kYF߭YGw4wK|4K:ޅg_4Γ-隤s"o꜏.}/m"nks?w~{*_nhh.7-=da%=WZ'*W#G4{>Z;ߏgm>Mg}נUNLv8ԟk n򁱯G c7~(xƽeʎ 'b-DakWh{֬ko98]i'Nl(`};{Ãcc,dB x,s<&B _l!_j r`Ou>x,)_qhZv`iK!Ӈ;%ib[Ǧʪy3/5*s{_wfvpunfmza.0*3`,1b9t. !RB|5QC1 H `>įhڨGGC"eegㅥ,i/Zpߎc|7>` gPp[F) eTZdNI2ixim88ӧNCNV7/ҪZe%ŸK)ai301 -?,;NF.IgWNZ)L|ޜ(8φOj/̞-F$OTgҼ&rf>pt%(@,er5:)un.33_!ЇM';ς3qhA9o?KAځG7tOK f:DP)Rn ͢(BYKL|p]~w]]}^l|ollOs?xN.he#"rY 24-7)rGQXQ3>~ћvgԦYlMvx_R߁mkaY!aX pD&\W,ǎZXs QW Ab~QU{d-USc8636||< a)ؚ2k1=GP7  d|« ƶ4`cy|ˋ~*~Iz|[JI!IJm`#c?J#Qa_bRebM A|C[{d1 u:`v`Č %—N[FƾTCwBYx{i[,JKz+&btq›I Rad3|R(͋މ<҄7(V`PrRKLd1فeTȒpz 3kHRyҦ@$J ~Lp!!+/B^6X}{E,N{L#ciEit Sj_S~xGmfSkt&`PJI P=i ڼL%LsO-g eķ{N$@$NrX\Y(N@xޭ uUZ{VYo5ljRvI")3V$\(l(a u-Z@hzկgc,;u-$-4hv_nT^3-3a+eIdf&sLF>7,ӗ%5^HQ 킠hښr!YɌIc1eCK;߆x/i|Q{YHA܆Ž rA9ί疁Tڴ{RT:\IƗ@3=RnxbЙg3*]%7pՃ>[τggmffkf{no'>q ^ˮ;DͧRNJbn)wϹ߮k?]}4vDw򖳺;rdi߆I뵫0698=8q}ڎ/M(*3p9tj` s\x$6 n, +=Ep1zdD03dH>:"pVHFfrĿRmΛ@9}NoN @ xZreV ǍI@ 55 Jߩ_ d-=b1 X*>S 4B,H8W"eg5o1ZHhȴ0$ ߄DyBT., ^3h[;nhX c 9&V[f%%;,.C;H9`0'5czr9y_ڵW[Lu&1X\WCC z 0ڈWޙW6QοH5Sr۵{~|2wk ZP5|jR5 =[ , {/zC]+&&^:19©#c[;=cg=JEAڔbI5nA`1@Spڸդ{yƜ\]K@l}jϞɲ(($;<3]t-Aܚ1CR^z6LgW|ׂh#!KpZʴ7}TA2 ^I î __ bQ./`vJ--~=;/*|o7a֩B0g-t? toE;>/"nMd2Ƥȯ$0V2l"؊'`)<{OFA)e:'s^-(kBU:KK$ &C46*@hV  kUWc+lc/)P(V,$j2AEqgc*2_`Ӹ\LӵLQʦI ㅍ`CGV5A&O۽BJ]J;iOGn(u%o:Г m7J :b\Ep@H!s~=z)fI ~S- ŸJgVm8)Oث'F~ע%UzGq2X1}\%K)!%4L)?SF|4y 1->oKy6T*ȓY'*5!Z(u &l'ȣC<H6/) CHl jYQ-pC`O`0F4ܢ@0* %evMBYp]<3Ųgd%ِKhy׼oMt=lW]q=V7w"M=E gٚ)pӵ d+Ԑߵ{nS,4.ރ0ud{X᠊'h]L+bɉZ #sn@%p aE_!2k &6dx/ IJPzͱj ˞JE5,\;ZZ:NbGC ?ˎ3c}t&:[)V%a#tyLlHvCʬkVWèr`&yQ}θ@6) =B%Yg,D'R8xϳGs` Yæ1_3/ NQaɊnt&*1Jvx'`0lD }g&oP7@uo.z\36[5ۓqGn-_>oˈ-4v.6mrۧ՞w]};_x]/h;XSdq2F㺷U:A(LJ OW`D,sYiN?$/Cã\ $4 O[M R< eDq !c̑* ׆LƨbvyvyїD5 l}k_?߾.:bc2TAUciIWٍ5b[xp9snA  qѰDmmm9H*K >?6kMD'  CIaEf쭃π|r+ɳeTbol ҁ.8 4>f[G{zߩS {jxܐ}{JU@ߋʨpQz ]v P0XL縖}>X YDo@ѱNuWO~_y ;ػ}{|'n/޾Gwo7ȚkЈY:<raKt'%5_(3dLNBvWPu; fsʢ| (-d# א4F׭X.A">IHE'ѥN:MybJECU A(E :([=qJ;? e2J7wDdAw4ҽNf.'?!z/d  UCOf1d f|3UBWyB,g7LfJ 1xLP7!нRǀFD27 @n)u\ /"Yҵ Í6+}_~QdLf6g4"&JAX#)$ !Tr൝9Rֱ\xy 8}+x=D4 l-mw\wO 2moSԺC=ViZ i#q* }jaP =Ɋ.˓4cЇz c EeӐi}k`[5k'ViAɟ{9Sz/dԸfq Foy$Ѻm~H _ÒmjJǨa0?'y6 {/Je''G`:c up-:WpADkIʻx׍@zq7J46/b",/m:1Q lbQN]MwC ] p;QKit̲q*WP8N/x8, sv٤ E V*5vU\ВҬNj!Àe#N$RA." Em@A4QFT5 ^w:!Ʀ |Z%Q&!_uj@S鹨|ZÈYe` !{""3sɶyɃ+iE8I\'{ˣ $MvU0S,SY?Ka;Nhn:HF S!KသSl"Njm֫$'!vRljnq 7735dLomD!vqT} Ϟgeٿ{܋x_36[5ۓm۱튇ns`H{{$pJ d(z`>\O )>kC9JI:HMC!SA;GvWyUl9EkAG#I)d{ZkV^Mg1GĂ,`ƞ 7XRc5憪G Ŷ0qffew`J].SAE5e2SgUd_4A!zHj lxd>g!^FaT5Lu$В,Ũ|e" }B(2^)0 uT~@Ih̳cf9i#Y(xVs \6Z<*19ZrȞu5٠U8T6T<XBɔ}hJmw1EL<Ǐ! @3uad&P^%?*0[(䩞J!iXwY'7 8ŲliIMMĮS^+ng>m၇^}06Ttg00^Y1&2_ 6Nw'.^Sx_ыe<߃˱vWqaSE.#4tR pLL;ʠYmׁ@X,Ӄ lqׇdiӪNA܁cp"w#i;ϓ&.ݑɎc$9,te@g1T s̝>d~1rvnܐCi{.kö/N1Qx& \[\ vkT.H|AH*A3 #տK?P9J! z s/˶Y;te¿;:Ʋb*Eɟ/S0$H5<2hX72K),vzrЇTg90m(tX:-ʪH*5>OKF9 .3,^P yPg@Vsި(E+L\+>[-Fc )rAFU6HӢz}YvN`sbu(dfCe#DbPRt!WNlh a:Ņ}u={.rfmfk'v-\7_?cޤ"*Tbu,ijbv[{27/;%.Y׿|gn{F;U!FV+jh"go,>\,T.m53_ZqG3]>d Jey/PBuVdzcM-OJzW pAjf@DQ"L@-DMPO"&DRn06,q5-Sj3$Sca( @,[ JkCGɽ#XABb_ҩ @EK.HiqDђ`GCnGeˬ X!3Y-|Ԑ 6BοE{ =8T7[PN5Bҋ?2q,jm|ߓJ@5$Srši1TڽVgI1Pj BTs O-0<(doc3q6NÖlbr3SQEuV׽XOS' y""zY2af!9o}buG?vh/B hZq~ܥq BWpx:,mׁ,Ņ~~΃13Adx.CakDD ߏ@:(cŃO3-3<}k;v\`gm~] ӎEa*L3dzYT)-B=d ]NDȱm*vR]s&lOˡuVL)#. ^&6OKfG.k, Ýq$H}xjl#*χI)'܋$ɱZl^J&$@G2{Pf}{\҅fnfk'öu⾛u*WƬ e=σoooq'vTʌ=ҫ')N&Ep/?V ƑBXj0Sk-wdb*bݑnʵE`R'vļ][K geAI6y\ȭN4fV+[^-7%C7ZEZsc; ñ jy/|DGO)nxQxl|b<%EAZ! RQ !Lh^zbg;KrIPD(, Xy. {(c$ēHjqQVl@&%+< AP8T)x̱JO$/*0͢w`T?1<*(!啄ڥC~`+!_xz\_KVOu$ B.jcȌcr -ԇ4Ep̠LsCfP(ٶQêMoQ#V a.S}.%gX66*4}o,i;25 0ICH.Y:f̼oџ^|nٞM#&+*m(*R&)%Yk RT6j N+ w5ᨕ̰:Ȟ]1+ˬ 6:ߌ~KYQdpG`j5Vqx 8]G|n)}|W7'Vz?scn]niyeWw9,{b%M'o{8s-9Lqg\ף<۠c àn (;Y" $6Z;YH9fP;#Ip2ggAnd;N@dmccx05NbLgY)lR $KẨLIFAR=e{NͱQO*'ڂ%-<M/3RDZ%\6BR!J H CG6M-Q`:Ǧ@/ֹQX>-(aV DIgkB+>.!>%ǟ/˦qJ%mZ2HG]70Ʀc!F"9+O`">tO9נ-Uॐ\7)P k*!btLΩ1k?M q`K),1W_^o@ )likIU'K,6b7^JeS:v nm|[HМ¸|tf`FjgZ!+hwJ~WCk7$q#HU9^Ǭ_ʯVx"k0 J5kޟ ٿ{]!ϒ, N&NsװpfuYcΩȣ;A4@O5۷}lٖ?OF?cP):J fJ յl[PQnTî#0?c:23Dv#CeLw!&?FVQ ıcj՘ }@Z )6ȲJEY:1iRR0i \_=%wljs'<ò.`v z R>f"`Aϝh;PuX!Єtx,0Q/ ]JzpԢIe$A%%Ď?c&Ȩ^++:Oytgms9lyJNF̉ |ȰCaZhCwnz^~IYUճ%t'wL'E@DHD0C2J'Tԃ#AZw͖6f 6 "KeB"tMd~Aa 9q;bعb>&AM:c%tirv }~Aހ%⤎75ewH`&!U00#X1d`S'KŴDfط'## 1-ɎHǕ6 /@:# >7 ׄJl߄5`ј/^M{p«{ηkg3pY%JX4yBx\[*x}q6;0:p_?G?Kٚٞ ٝͭ)+0)^2#pKVmldƞ}o_ZhS> KYGucXDg'%7E(6 6ϭ35_Ԋ |,Nޫa?A2^h_0\_Ťm*;)v3 R)Q.Y('2ss]%$yycOic-m-~~ G~/A#*@)Y7@ڛkj 3kK%ֱT'(AT#A%elh6GY? w`I`)MA!ˑ&P|V6i325!H6#?CxW[:xkk00P`Pl#@)S׶O( P <%:i(S'Pcri'O,X1yɜ:eDDQas0(w$)2r[$ABP/#-ꥥ|LMkj8sg=h< fN=jdЃ'wǏrqާǁ4ΣG,ˆ`v8ud<gşd"Xy>O|<-Up:eouY.hl{n3xxï{pu]<j\tb` (:\)I",y^HuOVfϿ&Q>/ʠ1C$-% A. ]mbP$zqP̀j*fY0P[0d{D r&)h G*FHYYR1YҦ#XKjh]G:+$z2c&|**oӳb B@$b)fr4$e,5[(ȣ 5ͳ&uGd? 0~Ӌ3qP!L*>a@RnPJR1%Y9prn[ԝߓsFέٚ-[o}GP:uMG55IAM5zz@RhgiO߱LqՖmݲu}y.F0:5[O1acV1DD]:H4JƒhbG2ES ʶ 1e !KeR1 )($IbQ7JX&=k2SF)X>:*\s8$T< /4W t oX `Y!= UkD[-L)>ҫY¦G KF&8/fLSs.`&ViJKe6 oFql&84汌"ϡA[τ@҈Y=8b %*7䨮DODTsIYB,@%Tb h?nOt{@Yn0, 6nc{m<_ߍ`kB3! 4UTs8'D' XxM(73223g7ctmY ԕUG#X'#~dpX8PuI؟SJ3 Pvڴ֘z Z.YT~x~闯F*SgMov7T7<ْ+ ,:Y AdgX"MG7\pƙ5b 5Tcu*;00r{s0WѤ<-O&.zseX'¨#1u7.yz?F$#ŃFp Rfg$;;d }fiiD? A A"&CRyldGO?OldiC &$P uPnnXPėD~>>%}q>twPɦu{Zx-ګ>AUNe]mK_q~l.5G}u7&IMņ"SqXNZǘG#%}@f^I;vf :@J+q:>%ZYUǰ)(Nrd xjFs02V-˔OODV U:5ڸfhw6@8^dJqA$м2J L/ZcҢ'%u+)kB ;@\I>Ȁ3N 3UA'a'yyx>p [/0 vO6ݦ?vKǪǫMY#-m2vO+1D,a;}}qݾ'>}=[dIC'^mM e RFsJu`v2#$}"pbE¦XJ4@Af5K8LG51}H;uLkJgj^t9`#U2Jib$AŌ P?V̝Qq?7z?zCc9~(>t(9 rNJd $&S1Ѳ:f3M 8]Q^7V0Q2ܴHJ :2JF)K[)I(+LD=Jz? `(8T=ҼAaJ *tq9PKV+^C[ s)Affa{^cY 5iF `e{GO$.lR^?.&?\ e7]=,Z 4[@D`@ӉkN8a0E :G[t]'ܿp-l]& 9 p}~צ?m*4Enն'zsoy~]fsi4jDʸ(+u:K?Gw D)_W 33 A_#@!j%2>%0\[Dǁ1yTY/(meVDZj+:&'d~.6 bH%Y4,~'ߞ^uk~ٷ F._8̦^LBo"{I >2p3Jp@8U' <,&B!x cj+OO-䈈yȿOƠs/_D u-p4RqzqnWz"eN5)O4kc%f6;aŴMu}3F OS$}*(| wA~*CG1$G-A/B$6 !f -l $bMS &e?)k>g!\so﵋\|;xǍyMOW_<Ǫϼ "9mDREfhff?\;/ʹkܖw̜8zh0ܧ`JdOL}C[/EYGy 5Hia QjC&Q:-J.1q[%ѭfw$X}΀Xյ\–+Xh @u<7SL *.k5Af IF.Xu1j9&f/T9k4{6Q1b@5aAWP,{UMB]fQjU  B̪CR\Ӝ=&g?RPL&f*q#^i%3 *&ioTۗ&F*flWJ߶SUTulk @?nֈ>uXkmnN8fMhh! 9 `]iOa͒+RWoXwj^O6^`§:$gY3uhZ:2 ̤L_1u r h Owm?o35lm6IcI5qk;Eߓ "^FK2E71jXȕCv| 6lWM 9~lxϲ7  XJ#\@'@aaF):Hڍß2%8+YcEI AKDя!h,a Wb҄`DEMHaEgϸ`23[\F̊, ϫdĤ{GC$Q@1W9l.JJAf\Ca,5&@5)%="S@hT W;Q2:Fx9^9hT)#%&J^)4 z.+^wDbƘ5^FԞ,fYyf𴔧_N|IRz<'v *¼_L@$&~(0M˻RjBXVq.JꎅfFlm'av.1rm' p0#A 31aY0ߦ&Ri> )Jm(&nrA)LTQ4MEuPEN><IRHah#Pg^uµٸIKr[x09*;ame 5Î7\nBPE"q  w\|8Z Lx$R(6B5F"XQ(Fz*A'bk"1Y@e\1j)4|sJVFU)B xV6#1PsՁ&;²A"-dDB&*[%-tU官,PJ=7UI) FzJwT 9&F\dsxH>TV:X˧]0穟m:Y*{7M8ΚWEؒ)yVzOX ֪:9޳qHt UF|l>xeD̞h[Z#x뛶+y5.d~P 7^j }PyUk33?yUWh=ݦtvGOPr$6ʳm;=(JyAfg&唚GiW(/u,疜Z(>( 3~oȓ>oBip7O5;/_󱟛F#SgM{'۶\x +1O„|h0r 1]33zZ}^ wYPuNDˉbJYDԬ <צl yx%zsCaM6dՉP2 þ-w}fFud!#q1ء^ E&y;T H@"d~7xN& ,lj$xR9` wS6 mI|lV̈́$[QfSLIG0~@PM;75]?| ~ۂ6O']Iˬ/seR&+L$<HxJ}2=_ȇٷ#ӑ3{έN-v65#n*[l̓/¿Ƿuqvs-f3fo]o~x/s\=3Yi,oy jۋDgrZ>-զh+O۪R$WEkK8ԳtmqG[\ϯM1H5fŋAB1Xnt㉷yT(Q$H QYRٌg RyqK %PeOy*lT\Z9q*̛l;UO7 6l+$-UrFKJE;ʷ,%`E@PxZ^Lza7@2 p"ՃI\dxnF7QY!Q HL(M-<)a~f:^cBD"i\YKbQ)%Φru½k>Qiܬz q)3PJ`8U1pd@bOEi5$8^$E-c`H91y}s%ٴ񼰡K oX%M*ފ瘘rq) h xSϡu W@hAgx[RJd-j<~3lGm'6sXaf8[KҊJb&sMڳm=`am`N%| fE^-F?k;jUms*_d찥X !g@Kc3=cdc>sPէ|Q^XpЃxmÖ$4 Om;fciv#̢]k&ȳ&LܳfY&OZD'ۅ?(d Luo;_t=Ė'N)R(4TrSo | .;856y 뭄A٪ M+ T<٭}}8F>ʷDLp BY1M#` 2R ^ 8CpD/` 魬U"b MA .R`Vd"yF56>Ax!>:`:":VPYG?4C ϓ XRߠ[,}F$ը@] Y$AtUO$ʳ%iD5*@G @?GY6/B;ΰU G^_Rv5=O;8>ycg͠85\-z [N|ƞmGw;nTl½LouoGzǒWT&H/>6- a"peԚ%qJCvPWxLNֈ6^- I-Z߈lr {).Ry.HeEE(F$s'VKF3){HWf8 Jc]ݪ5dWyL&$ ѤoPhƫu.3$]2pI+ }AKΨczINUjyf+=@ 3 ƻ5S+zp2)W(uIU}Lrn 'P|_ǟU?o-#tn[U鶽r!ձυ=]VF3fpHrnFڴ0Sf{x[;4B(8QɷL5S֔ q嬅Ѫ'fP(I*|} ,RጱLaD vBnRB(*=V c8%l+WKP}+.k!9ËA7`lnU>1У,jHqת Ye5$\r۔|VdP57ʅ"*_ؖ񢰸\6-&\+Ėx&Twa?LD8ʥ)h§cB03ψQdh8N+U)3m##*l.V3x5GE Ӧ׌szJ39W'سmZz3R8Ja*c: t"F 3tppuL92s;DyAcԊc SŇs-Ϝ9r1s?պ]}V&ʼ{sh=49t tzͿY.vgV {xegpaK׾r1D5A-Bwk5럟x ^xW\1eO6ݾw~9̃FN=N[@;"I_9i̖f{88x̾YhFK09"bSWxb pSE`AYPʫd~  +,7h^#XG@vgs|q'qBo"!CoY|`8 6$u%z 5ox|C)7<"2pc:7d0PmFp&i11p?3F)5a jXlic?N| Kx N, ' UQc* mb[xk*#J|Jby8/y 7fLG׿}_GK8^Z>'ݾx}'pxl1[vmo;=|Y'O\40M_=W9%Y(]%9[,&[FQkjUƋmaOY6#] v) BUwF=k޿TfRF@ty3 ><}7$QzdW>m"B+EnNAb?_ladbkLq$oWifUH̲I2{j1(Y6N&rӺQ%7-5q>v5JFDɦF֑MEW7F-mߦ'@{~=cD'U₎>m IѰuSӎ۫:~ɂi26=8O׾W\#tn[ֻÏVgOdI䵽kMSIuevO{_go||ѕ_^ dFM/(a~ y`eC"ʷ&I@tA$IS@(*H8NXw iW,XY#9ę xGƹf|&Odq}T'1;ȵlGT)(0&'_\y$8wJ{u(R$Scyۺ}dǏ Iy6%).@0(Q:Iʁj1A }F_~5F2NH' [3HUkiB1($WTX1e,r||J{3/0ĀݙFFfe^8,'Y5.{1|b:#sG;>0X{' ~EvfJr?tĒm\GEטQni!5\aYlP~>0p9Ѭ+6? Y6d@s.b קce|Np,±9>Q Qǭ}4Z5uaw/9guɅ/zW\QM[g uqwc2FI$X'L #FOB"\h.9VPȩWL4GѼ2O0mTP WlY:^-1M =.x$keqҒ 0\p蟃`3V  n>S0V #;O`@{tPf JklE"Ƙ%qF# =g(dR2+\ Əj\ăeEפdR<e aDggb n9=JXG) e/M+K<>5vXvB  IR.V}Z\Ns]8{rK"ZB S%6{6\ouǔ=t^NG6ݦ۷j۵So=/\c'=-2du~q.0eXٹg^x%Ǿ6p s /"YGhS̲U>HdCGKu 4\|8fM,OJkCMLA+[XDzx.Oc 0,`"H$y ŀ@eq7^%Vb(S`#m+V 1*:2 `6,4`Ô`ܬm{.iyd9Y$鄽rT0OrFC&ʐ%!o~3Iz<jP GQ Pc$kb_]R9vjHq0ڌCUD63gͺGj͔^ߍP68[ 9 fľ8v<I/*܋ /(7z}.rO; pqdXi.H짟*OXQCiZHЄ5ZBZIaYO$\!lj0.>k;oaHp&<Fk40 9NT2t>|A#Xn_)a=q]F݇g ^{kʵi/w2z?ۿ tsU.y1=⠌@M$x`r ^q'Si NgvNQ* dǕ.N|JMwab}kF;x7W#鱪sB 9Op„=J C Y<`wٷ'_؇ZKAL4l˝drH(qB0 07 $d*7 inC 8SU!@95؃f; iaIx$ˍN]Zn%cMq%S FE2\V,gc2HAC ,I P|Ґ @Lg.kfecǜ֨^!:A ٦F~V7a7ĩ[bX/WiyrDF: &#{ ?&]`q.lK $0UXRϛM c0G=1# 4֥FG<3ulWB}ģ]\3N&^U|Ǘ_~+~+\FSgM~)n}B?>?3pf:kfvtg^#ixtB%R^+]Irh0 s2cv$9#X`AW9dW27!23p`O {¶mMhá%*7}vSg=U//pwg6ɫvTܔM+med(fEH_F˰OW 뤪ib$Wct+&oŭϒW{`VZX΋h\tԂ5Q ؍8W@ϕPZ"kj87M0d"Dz@QX&'|v{~s֡} =R)i7#!- SY(ņq6m0msA rjMR9R|Ң<~<ˑTaMn?s޷=Х Ϥ(c@WoVo?Ӟ _:}^4d*$G0> M[RBc$UIyT@}ūF@~FY[^xYi؂."_cW3ƊTMsMp<,a~Z֕g.u(kC(b21**Y4\IEu:(SWPyp夔( %T:|R2b:'dz5QCdbאKVwD9Z$P-/R\$:Q՝[b 0Iޡ %dws_|&sg+8=-8 noyssu:gyԢt|!RW9RD&r{Xf}^HeZQoI{>{HYBe$(K=+D iZ5i) g^ayg#< @˄$aGq *ذ6xTE;zdЦɎYZo CaLRjXZ ^;م =uK`% chꦍx-p Y _;n !`߸qj =ކ>1p#2U}|mz"*~PMhR%F>QuأQ ~J6:(1JPl=@&i14Ia18MGTm?OnWy[P~ D.}1ˉ#\xСa"y%)!K\ v\W-E}N!&ڏ#ϝ3쩙)fnXE٘A$3 f=nhNGeű]Pa$VY" U8!`7XH4N f%(=ͱ.*WTh֕>Elw`쭧|kcG=w^OG6ݦS{5kp9(IIe=ңu̓'+_ Wq-o/޿xi]=Tc1SsX;g73&$d`='0q (5cY֥TW!c{f~Z (F;Yl# -gM^0"}J$~BDhfI p3 ={yZbL$T|k@-TJV%&`$ǃvuFU<|ܣJ+2`$%^{or)u߃Iz:ĈTY?\X8fAHLz09sĹ]oM"&`+:+Z1k43O<#3jaH4U.V98;F CQыJ4'CA6xA6rhkk!|2յ57g@ *AlnÞ:UDF$@ ֱd1aK#bæ9w% l[%"顯N l\7zB?$'j6+3bC1 h ?GvtԙnO}Ѷw;ytFZukdN*q-["O>6h/X%Z& LfJ#-_`W&uHkKi2]<[urI[^J->J82 ).Mji1|?I >@Z2tƏ Ϣ $!A"Ĕ*9QDƄCu$3!_&:t@kc.L3*VשBٰҩ=l?I5yTK˥}3*nogDq|/1{{I-{b80ǎLF]q%+Q]l2 Tl/@SɬE/+Hkv)U3VV6:Q(d W8ẐN/B./C=!yxwxG@ΌM6bhc$UV0\jWs0J0>+ ~oݿ?FSgM7xl)-,a>Հ`,[E챋]1AtgcKo36ج)!3#d)6eM&L; (*`Caf1kN1 “H3C%(-ā}7w :M;dAA$?>3c! kdP R2G);Ct 05Lmvab]Bߠa8l;<6A'vC$Zup{kRK]W # AGg~=~î]}y:L$Ir%\WD^8T UTF)14ɳWmek&S4 X㑖ㄨm4kU KH INַ̤cĀo-" 5} @ƶ>5ߢK (o{md̙:_u^ 9K&[ҿIw^>:@9W&a3pqf;+ ĜW %} cm4 ľE8rd-Z0`")Cw  lۆ3ah>㾑#!&?  b 3^u@_d8G4x+To'F6/mkJL|oH4 P >D"uȰ `H`#KT(McS 8&9%DPC"Խ$*f,SQq;V؄KQU{+.yMGTm|n_zb~3gN9C`b-*Lͪ,KɆ@9f" wԊ ]M<*l˟)$?ˉtN{J qr;NA^gNwCיDJ !uR>Gv\._C>-|07>4YŴ@6%Z[[(^+'a|Q]ˌ+brc$V::@Si29Xt vl@;3iaCUYך%l/Uꤜd+I{[-Y u1, -ߛV/SR$x9F}Q@ ,m^G#tnp%z}^ڬTR}m|͒:!ެ$` fsҧ/"/.8zX}dk'jzD FUS3i.VVJ܉) ޵6$-9hob@OYĜ ▄U >q֝£tdC+HX7U)h bU|G;TO`UхNNʚ~x(FTdc^o-s_j *i;uɓ&%#lgSm ϭJ$eHZ}e<.$*r1Q—YR(a)3-idMbh{cPuБgzid)*bX)TFTl 8<,5JLک~}ffK ~#Ee4ɵ`.*d xڴٕ]fvtSz[B:uq#>[H@X>Qm)j?"Gyb=p`oؑ}؄pb5.dl5)_;(`**|Y ~k~Өb Lw]yƙߋIfRv&N(e֡}c]#uؠZ=3@`!6tg `[LODʵ ɺe(M hɌA6Glȑ_ 290OX Kx6o!d{|Ča_#~񽥥!I>9\ `b  A,C;n`OgF,w3($S >;%VL e' 𜱝qx=i9~`YlEvg\8\̐C( I=F`E\Tka% R^39>|R q* D^G~g׮Kh=ۮv nnx]7&,s_nت OV숬$H9ꍛL!Kp'Df 괘&#2!=c|bXNSU&FEP^=stI,,}Ze+Cma"`%+) $̀J ?9l@F}oe8nifQ暅jA]LG҂Mqh-My*L"P7Q~A 0cJ/½u_uɻo{f ZmMov_>8:#CxtX %L H"KhvՎw^<8gwr tl.=1ąU6Vwν'L$I՘X~ z?BPp˷oNs2| ݦ@ҪɧsЛx>x fg`uk/)3p̞#ȃ^2#%"0T|d H@_$V2a4Z!CFJ!?gi_xFhMLdT | "(~@G)g2 2~*#(yD9Пnsxv#z`G+xo|߻ A G|#ص9 Y'W]^6nOrlt[ Ud6CKar3&cj[cLJ <4n&(ڌ~8@帊eܧl6H=T$(;:J|;״ƫ\CL>godBe( `cUT$a`bLqN3ϓV/9 =)ul:M?f=~=!_~F FV@xzj~SRqmg!r,ÀU~6׳'W4 HL 3$=AA: tmQ&:U*uW+imrn;(wߺf馣tn.wvMYb YOZ { K1K.uk[oz]=[$W|~%y-drHg& &y'&h~nID7پ JY ڭf2:SL̀0QF+n(&ab*Z3'*0yXz8)U-HiifDVݠBGZ wxV~"Idh6[Gwrah;`} `n, ,"^3f8S- #Yz*t!6l$3Œ7i-"s?u JZ:)$p< >vf⢵gdrux PZ㥺Bc􌩮XB'KՀXel&_[@U ; HJ]k}9%Y@d{/ Oj FEhP_ڴi|](I] K'$SLn1\/ꪫJrɶtg /ܼe%瞷&G%%\Jpxn 4G &jxbdvh\*٭l01!`I|LL>DG?/"f$K,AMncMd҈@gFeYwxԧ-]gc-.Say1$GM2}樯'wiBϾBVDy9!9JͳRXebd\W8#t{_8K@VR8"wā$R7`z֭J5IZ[AFU {B#r/**cڲ!mMc>fʶ(InE(*M SJ3JET~ML,$9o`L !21fT7B W/]VU=Θ]a?VƎU}[{l^XAkYU3zK!R&{fjjG򚙪m`2J,5x80m5Nuȅv2xCKV NJ&ͼ_tGXVx͋T(3dqفן邧3qm }G+3c"ra;G5<)>_$_\ |狾G7>>"}lwĕ)3YoTwkϟ\xYkt֞U k$- dyNH  &k+#CLz-sk9 q8R81PAK߇nXHB0cl3$5d Awd!AI jqD>NW0Bˁ|'7}<p'UUۼH :\_w1za@h-i%d %A:OJo. 'y\T/*i9YbR# otbpR>}o-MG6ݦ׻]y>~p6b8"PI{he+щfn&Gw>q/}1ן ϾC{.K<BSD$ o vR h*2Q9ad"9#RN,ֹFs'rC`& cJQA<< |.~!Ҭ:IU`R8Ag)6ZCt1[G\kNEԞMAR}~m=zRRLΚ&)8blڥ?yWKȐH! \H`8`5&|uU+gTyGj0#Vl`%ND0qĭeX/&u<[. C6}>{ȓ4=X"mP*_irlU<~^;iE^Ӆw<ڑeD{P O(\t3kƀM'Z %^^:an̚!gÚQUxi ɞ\4 T5Zkk6~0bnsu4 ~c}/ٷi ;KˏV{.]q]3O-ǧ#߱ўr3SsXfv<;;hn rxz3<Ř[se&N +89ξ.-"zeκWR!G/wM6o3u}ڥIbtyhl&`T#b .4OdT "ϕxz]qus>M08fXwX p@G>B~`Z-> xtoI{kT9n`xapP2ۼedpxS$F2&x,zH,0W5T.ot{*nc܍ ߒi gړ\-ŷv^^gE}et$14YbC-(EB%5XAQ"E6,qJki ^k)TqX;db4$|b;7xQ<dmE`:UqS-ZOƅ^3lL3N"gkDI|9:\ڍ0]g\ۉ;ld[xj5^A؞ V>;Nm!Rl`I>˞uYʭLUt7AUQjm#pp;o>-ͤχ'jbQU: #O .~F0e%LuC$0EO\_q_+Gӑy=}:>8.d57flJ&V.̖>mY׼ʟyZ~ڏoin9?wnӠB'\s̈NqV`ዊ NS'6X,0Ƶ|^-R|y̘4`}UnM8rQ,{?Dn%nͱyM[l{LE׈>c$q$YubtyY@ ^kC/>f`)ͩ i(h>fk;$ Kb}9k/O~>ߑ?CB97IR?9@b']\0Uv Uh^!-_=uSY II,rpɮK3xxHP! e&1Y>kv"Ef6*CnMIӘ$-fTL{ʘʹ$֑3 >5wG|vk'Į3"@Ty;BmI3 igLSʙ&_?)?\kDZ{Zyb ժ/@`'ca~kgC,XL`U7W8ߢLE72?ҵ{X4s\_C=YϘк鈕57W=4zSgg^X?~x9K:o,<xwm9moq3wO1e<Ͷ~'lشvٖ-˯NcV1@gāA%yZW"uEp 0ѣ'?C/'2jFqDVU/IX>~, fqp[OϞNl2mmC3 S޻zP {l}8G 06nFwC`Π9Ǐ `mmL;vl-TXHj ک A(z?!I5fRjtB Gqi|Mט5@S):' 3v %A#NĶ##L>DUi2AOdIN,v"' @oqC ^B!h!`ɦ{Fp&a1Y @g7WgcmieUEc4f63 H335F8 $1y%>iDc2Mw>]Zj>?~ӟ{Pvժ]אѕgd4waZQ:zSr"1G5xBUWDfj |sUj\LWWz? ls.T˞j.X!aK-$# E=hV'Ҍ l UUGr= Bz~GGFF k(Eբ L I'Or$E rF_![8qCQ5HZrmAo>i\_3Wad jӫ>] 8TIΚ:1]|e;|Tq6DzyA` -0/ nܿttTr}>T||煪"B^-ځx؃5UPf(Y,Ӂ 6sG J~LH@vOhZ :C|z/O^΃@F}GW^.@w'ʂ'T{ίo/1z.ࢽ;s?&^`伢m2 6F` nXŞrY[utwFDI=kg&YII~y߲omtfjʌEҲ=lq./7sʯ/ . ! /mRݓ~F@|K2 /["4Ω`0Ih2;֊D~R"YUUkXCtfWs*j^J .6!bkdb_:'W;9Qc&zμw>b!#&ߣ4 r]m&Tӗ}`l+J<Eb2bo;!4%&)pi6宄a(J$>uJǝ`A:N",}V;l}ZP궩 %JmdA ^VE}|6wb`Xeaun\3q&q\p)x֎=4\Xƞ8\(b9Ř1 9 E(_`懌-Uz%81axuK;%4T>2?U1ЯU4l8ykft;g*6xn;i<~lq~w+`cK ͬAE!fZw~mD9"?{>t˭Ǐۛy??ě4Ї>4 ُ;M;ryg5f P~EYnӃ4 0I#)M&>e/8h1o^bLyx/E4A-Iiiᦛan{ Lh zɬx2fGTμe֮@ L>x<;gf6=S!q:;ہu>D+~ X  3˝/N F O8fVsC ѹ9^X5v ~,Nƌ>x|dK$t^.XYo)ѶjGM;޶M7o9` ~;cӛlusGF0rb3fn'ebiE"i$e؃)ͷÿ:;oy3Tgplfo~՗1ߝIM S503+JYoj ~0QaF:W +N悐 RJԘ2Y[KA4/l_- bDƛ W̩՚qzǼZU?yf*ĒFUFm#*V%T(2S*B+ >!(K1X#e Ԟ>Ȗa@ 6:7kjDnc%@`#t "`IaL ߖ0 &[L,W]i8T57}mTh~LJ 4zC|K0mWNN]#ǺȦ=fCĠF ˳lA`P(KX󜟺9@O}G:zOrxQP$˖y$dA(:@<82K)Ő?8UP;7QB2 |r($04p@&}{A0s!@e=~Ekmy0XUp ڂ֬s c9xŃ?睤W@4x2Sfq 1mL46؞힟ӻqՏ^; $A3D;!ll_ բĦaۤK ڌb*.l4[ *R;aHEILGpX'F$aPmA&%JţIԩ!e(AǔgZ8/3aƓUiOW6B^W Ǖtı m# Bʢ"_#z#E) 3yj6&#Y SV:*@G{OTp4kUV(yVy→@VӅ [(3)ќv-,!€#'!'jYݶTu#/]ҁ`; 6h^r+ 3;:L^E٬ƠHGW4}UѩbVa|tz_nyY>r`齻oPlU˦4oGL<$lVɌO1:ooco+&qrN &샴*/ٳB{A(;1`De>SP3;e# F*}VJ#)3ԤcxM ;ESy5t,1Xb~b?wN/ _ *F9櫺q.̚:$o5)@c wJ_&2SGc4<2gPY/տZcQ XypfZH`CD*gL)זυ,}o%ts)M:%]~6UOSQI Oń~Tp)֞T j`+5*yH-9)0Ü*~E}HPv$vhIHPBZt Te`,KcBDu1/ lXa*W_x% 6?hSnNbd3~GM`=1~ ϽΌLaP/'ltk~t駟;P㫫r77^Go?VsڻO;qls;НS}9K%rR9TcO=k[~d;m]Ng-!-Тt`ppab6L$;&S%%#ԃB^.3J {0#C o4AN < N c-܏gFD&\a1ѪT\R!FZw &25qqpm4VͰZ- ~&9Q]ˎv2im4=6kAt[VO5]{"+8!^s~J+CENL(pY <x5g 0G=z,UP.;t(ngNN2=É:D~d`aI7p%\?WHl_ل:|LNcY:+9 k9y[l[rR!vRnվCubڇLaUItJz`;ЅԆ_=탗|?r;ff{n}pG(v;VBeڬWwʨ<Xt lPUL0/|$@G"6Zh̔eLXA*M)eȟvKY*|c4+|F `!qLVX?b^|ɂ:1%{>L''ŪT*Om_%Iɢk[7ŸՌu /9G^tKMoz܁O]}U[z˦ |"%UНʝ4^CxX&AE)P!syMI-ê |sv;q/Ox^yF}Gnh ,MgV X73`pV"?N>#^ovoEFI"JwtW6 aDeo.xF˼# LHGp*9t+ep' lSH~%}w&& z&8 _vmІ]#LOw>Џ˲YX9P8 OTYqwNuZ(L؅ay{?E$C@ugm~ݲa=~9{n0j%r`JheJTLX߃i}Ζ;osۇn~_mIW'Z[LCp]jPf;SPi6^]8\A /v*_caҫZ[4Kd}#9#g}o@]&3g?eewNk’$4=A)y63ijp *im\gÄx@)tQL<{U)ˆ%DIsUέd8_T?E\XAyL[M -Ÿn FXMQ jRKK% dԀSW*_)'k8lIBP/5#$fI0 c ɖ1Ű!na %ʆ0O ~ D)Z]:X2@ qYuc?'πvI,8+s`Z Q eM׋s$aNkӨk*qkUbE)3:˚*wx|7'sk.9_# {'3'lSj(c$WnC 6TZ{ydZpR|vg;;Y-yP3Y|τםuYv0 r=4j76[NBAXw"!7H*bET|H-.b55V$[͠I>+߀׬jABX>Ldށn%}stT>SA`I{؞Hw=ZyD':d*)';D`M.T\Ī%JI~l|vlIRM}cQyyHyl'4Qa_ yOF()PKHLt~g {mwxᥝo!_nL$;zvɱ3AօBуryo\K/^w7oywN;ߘڳy7w { W N< V*8TR=\>uǩ_g<>Ky9hԧ>j4 ,51NZ<<рFh?95z06ܔɖOJ[b= pSL`e`R0$16pߘ w[v(qc HE8ZN8aa呱2mICSl)L'ۿVP]7U2T56=Z-'`vv qرcPc~e"1|g æRd:iUU=G 2LXB}$4b{f{ ɇ`% Y2p4bd)7}{zpq`J 2y bqqJq12 , ˨s]\(k=77-.q1u |W6-oĐvUZTbתǭ4<E"&tP ZOkgI֌ex:W#i``\v։U\to[Mr+VSϸ&J޹l_՘i/ugIϵ3&kOEǖ*J($~̗G *ԃ(v`lv޼k&>}ޫ/Tin|í /d.$xΡU}`oq!>u?:x>b;j[wN[x"1DzmR!tJiU 6].#_j ?|ر6?x{Y/K{"'oQ$6TLƤa9gBQJ)3AR!6*8/.qh} &kӫKJVRn*$ƵC&k>ְ@]:[V͍ vMad3efK04j?_vxdc?g1,!(d8 A3]ϔHfPSo&.U6ܜ\&el ɪˢZyCҼp؏2rIw2QR7$AR- 5&*Fs䠴&cbgIp(W|NAVȠ NE"j9NTuc%N)A Z}eP *}=>-6}V f@bL.tyrۙ2iXw.Fi|#h5{=zFoY<e,O)z73s ֫ƈ0=3/o `&%s2z΍t/x=MVx JsrK(A^u"{.}/=+]ksO]?~S=TAKE@nl28n+nzӮ^5~w/'+yoǏ>66c?%SfqQWq9F&ȴrK#-]?WB/cqT c0Ir2xfHs0x-Vb_ sM@%d|¢J5/Ton㣂 #c; OZMGؠɟq U zniɷU:}vDpxKi'~dX+HyL@l{e`Qiuw 't}QMXRh8! 1;]z& $o}2C5'Ўemx0:fYOI,0/Vsө3'|74 rtz z6mN =1${,[!T~O{1 [@Obށc S=m/QG[g=s v̖c䃏?ƃmNPy' ȭ%ū؞ޓSZ$'yxGpbX?:/IuvNVJKW@Afc!b}b{2r4Bt9-'U-$̋ Gp'lWڴ I :wET b* 0,GpTa'&.vKd'T*Z筓!fRk#~ApIͼ5[fI(K:Tv^"F-}_^[W@}|óX.&Fݡn=k8= .ȖG; n-/Ѹyy5NMX9zpjٲl_>c߰?ηc{]z;~ڋM?:&yg&΢lTsxR4;dž{C߼}y癟\z]68rc\#!&$YդV㣘){eׄF10ϘL`|yBd KO"|5V et>eF,i{?O #E,,2 {$m4tjNͳ |RIs\@ (=n b*2d@ #cE 8`FE5I#'xcM}{£2 2Egfb.m`kf o2 T 3CԗPʼn\J(_Hg<['iкY |`>D(=$A_IM +`ԾdCǰ*1CMK%أ3Ș}rj|%%ߘ ( X8vXJAk$3&=OSNh~Y5 e lC*KWwhGXPDk4WY“3o}чG=PbO\_Ǐyo0r\?׮52"tKCm;nY_;l1>4qo+yڢCxJw3!ˆm:3xsJL/O8{0Ә'ϳtgMd|5cP2'YMjrIjYcR̝ 8Y)%#bTD=Fe^caGH<Cg~F⨠ؑ+ gUT_!2(7BBD‰?Snny}ni,L=;.C%+/ƃ`7쮚N;ED$8$8y10(avJNL$S(o[3@D\2HHݸAUe\$Ex [ЭUIGiXYz_L!+dɨo[t5e5u9+X}Vm~Vs]穄U\ }IYop5b!SژAU"L%4&jO2҂Cm3`R} Pmk*|{B LJw/^=]}̚ F5\6)4Z9d2޽{GO|G/c[;S[fEU 4|!Ea'Uu(Lxy/^3qohFO 70:* !i1]ɠ YiϞ)Q,õIτ==HO-c OkE+J2ijU,Yy< :_0ᓃq%O:L˕z !nT`daǝL)>- OHA-ԁbx M7&}6K(dbB-Q XÆcuޅB2,}QǬ#$cђ$!ђ(G6NBXcYyN;<>R ikfVR %s^<~ 3^ºdaBgΆAavr"0oOOw|[IE}P`U0rAɝsY FTo^u.7⢢uƤAAu|*ig"2mppdа*3:_*CN#F$J9AtµQ}mu% B̡̧:W ,7*JhW'YǕ.(=xPNR (nlv/wO,drq MUP=Ƣf]p*?'sLp{KkƼ?[;=ou]Ƕox|Iˆ&oqZIW%܃ɿ$kWď(q1Q* rx2"s X3\SQF>QDZ\އ%I3'Iķ\O vCfjujٱ.^ef/*B@L`/rzBQKԲ td%̟YG,ULt8@6d,nz*c&##&]m*7})m,IUJޢZ!A66u*Lc6Yc}=rCYB2N^/>6 ˟D4wvƈ,DTIF q2$O`d?,%7Xd*}s#z+eb] NRcYU#;yeYt./(-m,<+(Y)b'JxAj>ͪ5g>Ğ<>ߓbb7x5T*$LKq9'al^WgW.]~'^n=t GX3 }Jdh*C/ ߶|鬵=o?93g4zfۧ>)r9:dJԘwL&͕K;„5JQ!+Ky@r]Hq &F̛ta0R:y0p1 \1@w!-C(F#wT!us$;bm|a|#չ{vc(lL`|(@׊s7԰,!#ix9x VE{$I@<#NnϷq_(Qc'ς@9%(עd#! d AE0}۬m𵩩yP.Iy2Ad!p32t^>y# MN3*oE OF@+ρ"RS1(MMӽT?ed ]`Af`\9F's sh$]{MHWJ`Tr fWv/A*) `:FRT dI'u_RZZ!1VE漒LEZ͏$A2޽ڒc%a&Ɠ }ϵcC"))DdDKJ94USQI+^d+uTmyQcT#i>UM$,O F$ fa)ϔn@y@NacRtWd,"e*^e\õa?CIЪ3^)؎~$e4'xjqT,𱢺ZI?K:ټR&fR vvu]z9g>wˎ羶8>m}ks|<=>?ÆqXǧo| !~S̰4*z)cĉX\Ϭ8(sj 1 K5lM4k 9%1%_R:",Zj-h5Y|2nNnWOF vKF"߻%w\Q-þDb 3sl 1T1l?ۤ/L=`FzŀIr+X"{JyPKA~/b]]bQ-8A >Nj%MRD%3{A1{ /֙ }+ |[N}ce)yop@(ch(dI Y=L1FFxСYx֬arm6ߏg44>:ZY -Y 0}OM@'p>.6NUwͧvӪmOm|ԛVA]"\e:T|:93f:f@PJ ,X#=9*A9P90$0J KU+dIqEfwEXwn0<ѭ{n W^yW,$ d֯A`j2ETc}6ێp蚱NCK'emϬ!PziR, b42<K0>`av Bs칄 2PMeN*/I;/j؏}$ ?(CRe×Ы,ÍvEI{1q̙v#G1*R۰\fϘP '#}m}A#bM1^@4e kE${NEF^0 43q=EU'U"uA)oFN~* KP1\e#=J89'f V`Y]ZVY*T*WJQIdYkEDlmBŔ RBe {-ND-h=Oi$ZL!JFbG"˦ 2> .liycROf"5F2v0T 7Z/uw9;f۠q+H e卪U$$'Iٟ;F82HMfהHbK$~0hfKkR̡F6|%M+~ẃ hg.fotwywo}wuw#sks*! mcແb58%ZEEYR {kmQPP3Z盾}ͯBm 0USX~jלtP|r,[x/: *asmm&J$#R=Ȁzk)=+A!k[bRTVN )0íMs`LϜAY܏&{e 1Mjmu95S$lxk;5P ׀ Su=ycMҠcț.ΧA, Ă/*qvɇ4TRjkߜagÊA\ E`AfOE]|7F<q2deUr Ņy[}Qq2*s Xx*Tq9"dp ‰g̼yy%od|+N۰ܻ05ctᘪV_k{߸5'}.tni]q'>bZ,[V$Wfs)Lȡ1HR9%i%w g$եH s|l312\0*{eg*9Ws9&ȷMM[f& j[!$(F1D#eJzBYpa*m22<ҀV3 `$ Y<w/@ NMYR7g 0FɼL_!d@%IgU? AdyMYp|׽gjL]t,/sX.NL;lP.F'AKy)5{eZU 8ɏ`0L+ޫ^_1\9 'oGJ9fPKHXN^?#"a9$LϪ\|~O/Iǁh?N"÷ܼ:,3S}Gms(>S S:͐P@Wωo)$ 01+'o?6`X~}s/<2}8)fAiGci?-ysm6u~Ρf4JR*+Lgo|8zď:8'X,8ˣdg`px6?zx4h(6A2s)X]* lcKlAsRf|E,X(,T{Q1q)A~fÀYK r O0zۧ?ɱ?ɷGX稕*]Ã%[ 5L{T HaL!#72`3 dIhyy+ *3dps$$?ՓŒk"˄*ޗ$TWP]俵 62϶9|^GQ| R t?kMmR!cGYwfg9Aw QBpl V35BI,5*x#NʁˬyӼ8С  ]bJŸ_Ϧ?CCJ3tl>/4IUt0@,r,燯{8k\Fˊ/_^"((ɴ p1O nxO ʔdplQ,ں=߼r^GS2I.r?lY2/NQYꛣ;G ,UD f7¼%_Thm .76ʱ՜+kM2(Q ;| 1%:F:h¦>\2uMk!q'ڑ.{Z7U 9pGlK^E@ ZK].5ФW.ENca%D: s:GŲV7'$jH]wS,ҵJ_*!(V,WsjRN:K=R% ^HdM\XJsj k?w4=:8i_OeE6U0d 72{}}[T.PT*疟Y<@T#1E ۂ͇ol?OO|igN=n j|ۅ>=ZgpUU@y 07eavR=+>?R|jalٗ17$KxsL(kaᱡz7}{F;1:#G,N63J/#PA.7ү{JnskM8ĥcAڸ)Kڭy}nS:jS[k%X  Ё@ KN 6*e"vTѪ _yTA`Nb'N!|I@km)k(qkcm`6z4A*ʪ2 @Fvml7Yu*DXVEpk3bSl 'Y9 bh8n iA@gYRRLQP,őX#ֵaM? qCɘzRpiXY"bsAеɦW,e,z6(C<=H=mJe M56cJ/Zc} 1#d +y]i+kY~ v ,wm4- 44F{"/[v"4;8j>{%^у݋ҋeis>ݳcjC._k1~)_75daWȑ3q?|\7/8zn_Vyk:fkGG;y3wdψّi+ɵ`IT+qDng`r[ځGcxX&&Ѕ*IiKb,H/`1[ay,{s]PN.(5~<u"<c(vmع!&e,|{YDffCajXs4IA lKd XbZڙUcT srXo6 H0 <_=dgAN['z8EG$t,Q:HiRJ`:A6KPR. hJlnk5`}}ssX]+k>o˒rLT5_u^1sl?=]7 GUv}&?JP] Ocz}aD:-Gϭ0+'},k %?J$m(Y"8űAzDf+C12yN q.JD=|RH+'sQ I9 W[F,4'Bi.jAJUAMø@ơdK03ې©jwۤcA,ᦅ^&tYN7 )5>%[iU hPH !&8k⼀de@EU*.UZY%V]Rjl\;h& 0(dC &a2_:{߽z 6ӎ~3&n:, krleWH6]>nc?d.<]}߽pU+P|Ob% i!yĸa6{T~n9c'<|'^>XqՆs _T 畑 zDOXBF'KlssRϏfXS%2a/JJʄu8H?zb]0D J t&ҞT=Zz HςYS1. 9?DrKȊ $ |f0$ qWt d]%])ՖHV !}qA^U85  \č.>!`k"MZ>ìj<uI>uUꛔs !V/OٵQ@IO$v#=^gH/X;a ,>Ciٗ 􋟩[.́>ԓnt֨msņ(׋^*Q'RE֌zK҃yYπtmνjcp͕GwbeQ4E. ĸ{d`便<%0h/Ě7m '$K?"&yG)#_D=(r kÿ=EAڞq" le 8d؍ƖxJTYۈ \:ˮYJ+_a:kRr0 2=;T=H`/Y([AO t D0Y6Ѻm` W8 {אo"2CN/ޓ)'CAfxN Р~,]_ $)xI*<\[ I)hUy'ߜ֬ L1p׋4τ2KD!F[x;f.<3^ڤJcꝫ`il#y?G\5O^ugQCh ,949fƧ'ʉGƺ١m^{s˴ve>,;n|#_{pBLG\)'`B $:2jx#ށBF.It ZӠ1Pȕa*>H+K_^5sĒ&"V.Um\=cf9b< )Y|#5cFN+j(@nUJP^3e|X–A\?͡g^vsK!F1R %l<K}6z5߸{w=pI$D/1)[`|=ނũ)|M s?Phk2@ ĄALeu ԫᘣGt`l "3Dz}0S6k!Ķh"&.(c#Vk})xʭ! %s_Ě 2eÕr5ɗᾭ0 bւEaG;R_sϒt`&@I%z'z";'9~;yLPB}-o2MOc;jQם7ߟ߾zS՘Ӂ)yN|=aDy/Ʒ-xx{Uv [esw4&9yћ.{1ٷ<|7<[<Q q1c `F6]Xgn0apȐ3zZJfVTd0cHSŮd$=d\jJYI㮔bHVѹ^E4O6*L-$ZKUڮj{j sHMpCI4+TrfI &0XTr_&,"NTȆ &ˎT@ }'Lm@ N?vʩ*ZFm#j1ݳH9*K  r C6Ln\]PPSKςFjErӂ0=/^&~/=풋g~v(& $T=sC˰ KvlTkϾϊ>-·mXoj%s \Sg(.acBg0 {mo,Np뫡ٹ oCLOEuhXYc|,NnゆP"$2'խ -d*#a'3`cĕV1 k v 7 yO FGFJb遐?h,2EoE- \F7!ՕK&S D1 ֦KDQLJ#*Jt^)KLom#{uxs% FܾI 8?N ˽EUq dH'UƸÅKc & p'3m1ߒޣ:T흀9VH\I~ 6ҳl箼#~p K7{8a a,I}D5~1`i~߽ .?֟I=1׃Ws>翱ʧ|7mu ͪyQSOI"O(O3eT#':|^W_m'mWN=N;}gZcJ٨F`X?_Jb3(|Y"ڤ0&-%=ТBŬܷ 3FL{F -<9y0%u&WPœe/Ӗeq@4YbZx2 NԠ<|K`[{"cǴ>fYgU$eG%j2VYE䠒҅=ձW29_ a_!`0/S] hYF7bQœU`@!Se[ec=X/5cĠlYَN=鑔RcΉ>.f!`q-|-X ?js`qrGȃ>6,΂!yJ }>r vC6> y0wсy5[j&~,*QaUCcgTmOr˚<;70Y='MN+&Gxg։rcأd6wec.=w]߻>xך>klΤ*UAF@a 7 Qd/H#MI^ԘZ% 9ܛF /hDyZ^o ʀd-QNum""T 04dW}A*㎡,1ؔE(Oh9lʓ Kd++j@ eE)3$d@Gѳ ]^/p~C E2go{bUuD.k,#- H+f[DO Cɥ?B.$ȄQSk Tf;&T A]gֺO|i]<w`l߿O_"6:kb$Obj!5Po1_{oq}gM~e몴*V'fD"U_|\5Vʝ m~T w=ɦ+@3a/,l8v@@!Lp#Bؘ:HRV SD&Od,๘ %u2(C*T]1_ʴ1cH-H3fRcapx6ۡ Ct٠`LjbEgf5t&w}skh}C O,YkLPXMr͖/XV5Cd.3|>ws %\2w0ɟ{y>o_SNwsp(:'}?{I8_ѱ?skߛ)l /U ޠ\{[| KǪ$E2O,;K-/01vIDFOw@K yܗ !/Uu*6 9ڈN> Y*bVJp5O_*u`[a'UPzlwŊ\p2 M'}HBSEf  iE@j&{!,ex9\+XQEfgZ0==eK?y_nqdXz A^1t@^x,!֩Fd12=e)9?D8.BR-w}2l^6&jׯkf#06^QĿ!O8\Ik>xs&#i2*O< p`vE( eR){H-Tx PbAO/+hYQ D0o>3vR=ЋI ,\$F WoBBHȸ -y2h㫛>+)TSb<aaƃ ~<1/rq@gIKJ63,V,/`[L4/-6ܒ 9XeWO\qQ |foo{F!wy7-cRa&.0gI++;Y?^}ɥU"yfDL^ҭ!w-kZ/zI3~bz<`"FIQ }RT&z-`^i2 ƴJc6'dvϘHI%B)u3ډL9EmYc %7쫼c.c ? haGOhBETpL`b]c=:d,< E&_!yWGK$pٶn {DF\؁XiޓH 5'f~& pNrDY"Ly9;c BB(U2>uy[>ǂ `b⦭1o@!Xџcyj_%NPvny {+/ݟ}㍿};ki<`)X1z>3O>-oyU7J`ws'n^=~_(&;U`8Y|A{d`1G}WL%`]zco[Ey+E 70j-'#f,0yG $ԥg#zw>t,'}e=T&D, < 3+AVqDٿ nqZ x7?0]  `AҽW+);gY /{F8[c<e9?bDösʳEY&_R(r2I 8Y>dʻ%*7 WR}*j>~3wN$YV$J=wNGL`12pدx#է׼o7m.k<> C'z5X~wCm|5 #yEVYr~=RcɠدJyUm77Ѻu;~9?u/]x>c3~tު6םn?\ƛz} CMǎə,l- C ̈bq@ *DM+a,_"x|G"f% 1TSBjO3lF&B`0EoJ! *=Y&*.>ʣ oN,ovjP̀EMifna# ?2W(VkbQɗ.PG6 ~v<|}H| P,\b2$-iīXp< EX'|zm{.$)uSfEQ<\*cHUP8RRڰ[n n;cB{j\N}~X&:C60]$lYsⴆ(%PEk>3=abFh@)Vx(1tItI*4͡T^#qq>" ;~㽧T6OX-0֜/_:\yg偏օ{8_WϷGρɅAD sKj?~ }y7uŁ#?y쨕{_8>̮!ϕ|Ld8Ff240b&Hi& } -0  08ΣgdUS8Y%j%*h' ai7M{xu@uSnu⣄#V(l`9jH։}<!<߃Yvں3^:/0pR V3|%1XSHsmtsEF8~?Qw(`b4OdRfOz8Wkkb+MͽB˼"IàVm]'˫y0fWF̩fMd`ݾc01Sv2MtD+ wc!f%'aqsFӾݼiͳ_Byt-PJAt\~J `呔wv 8+ b=cb8_4,P_N4̓vVyeH1W(eS$}y V"^GYaf.9w6> $Fy>b6_ì%qXو`865Mx cd#0qVES.)T>n jIݨoY+_-G#h;M=i[zL(:}`O{G WV$THitVKPiC B=)T*7b{^EYt ksxY=f xq!5XBL#UC' f ?37')1 PKVUH耱̓>_޸'\C9lVGT;&O7<V0\;PO/\t_7_k֬=+raP"L&k2cF p_1~.a$8qNq` 'W(AUJ Ls}t7q$&%8I|fcx6 I|N{~{w;ЃK,,=ۥ # d(=AE+LnE1E2l^Gp4MQO⽦Z8E//}vmGAYtjxo )d;6VI\S#SA)F_"s.ro*qR۟2?||uex(7jb펁g ?3c,;Jw:w[Es`j>鮇\n>>Pb9V<2E >w xj2&%H_!%#0ϻS=]WGqϺy4ʎCa n_M}s5Jre,/TTcqVC,f@T bv^3.rX{F~&(dhஎzD:DZUּƵWVJso7st3ɠɠTQ%, 'c[Ȃ]^͂ns:A WzԐoBF& M(^Pl4rC5S^,r†bw*H (01#AGZ }rS}$mVD D`QNjee@V I42SvrX)Ukh57%/oo}3WFv(mrܻ߮{VY6kG1*0yߋ[,/ؠq{C-سvYs7'ovFqHCsQ[>*8k=fs )|l.K(H2f>\2u!6͊a"̶5%ilI߉f\>4%%%$-U+)"vHŶJhjAɁB䆢I,XNjc L%d((-t.'!xd,R6E<ғy["In=*YQ Fp2a &$c&DƠk<.kAȓ]f XN/ǔj0"< 1gYTaO@H6c)Kbw;$W ?nޛ>e( ^9(@cȘr&hRL<ⷈQ*W*I:`>4 H_qΔ_XH9ViMI<+Gu%]{rA_t4Zo> w>kq͹؇O?/Sϭ'tN@o>5A~ -z}W_;Znǡu{i+*ծ }ĒyEgCxQĒhrCoJmAQv*iNO^i&"bm\a/aߋDGJ,& +*Uy0ɽH~L3 UB|*Q m}jH`݆ @E@hxL!~kXғbUbɗP)Պ@ BxZױiy =6""ᮒD-#V2*$HbE%#>t,UCB@vHLt+n5 l R*!L653[~3*ghҀnVJب^P߬",R11|zJz,DSjg/< D%$}&Zĉz\wϒ]óg>ggx嫯9K^aW}qyim.B*ؽ ii7/<{=(={<_egX,\GcM4Aƴdz TQ=Gm{98 wH8 R9u ifMڃ@C'aӊE"T"SzL`;MY|0*r X%Ї& UqAҿ3/6z"vD[:CkK%o,s)"Q8+ 2}ЊsK $k"%h@ (7!ST|.R9VV ) bI )MH&,ʘMJ>F0],r58e:x_EJJ A@Qᤆxƃw#uEr`CzD_k ϸ` v\"~\G̳㗈٤o*&V;G,}f,~X\N7ki/kb ߯a~{>Pn{䢗VBכi)=x<ؓ 3* [UUSּ`g?Ox|{w-&r)Q$rpBƎ' s]Mu/N|u17F@O|Ї?=1xG>n@Gq  'q^7Nea4$9։ApTlH8#C8^&Ȼ D0P~ SÞ10 psALb L{6 7˚!p~@!)XE<8]Vc(8M>σ~aBvgٝ05:~K؆C#F 1lj fr!1V0#PIL1A6+=lr~o|h4ꎶCa+vzm7,y2"PLPjd2 E? ڃçK=Qt*o^o/]p?ǟm͸X#ה``yn`[R}׽.:_;|MUہ!9lDc" Eg )ךf]T<52^ bJ*};eIҧ6a[cS  6^ې\[O^U|>r0[/ l;-c" ʉ蛮[q (>myaKdܛ86ռIW$]} =xV/a~)7Oܺ4m6rXқ6[€ey:Ja_!“_OgrΕoxo:F@OXvbr=~ :O(}iIYfQxaLJTKh613hf(F$䑯\`ѐ:,Ue4OlEnڭ|` cd8do '}`-V & hi?} >Y*\Ś.5YhW e֝ۅodQS+|yuo{{@kv0_\oYgHz})Rgf"?/cR ferK < zh9~ s$gR F;)bXA  ,wtf>R^K7O@Fm8XYwJo, N#4j٢ G01m@E9C2|W!+rn)*#}2D9$LM*gIܣx."U XתxC5eHRd±f)GN)8c&\Ť69$:=~$dkgd~ը=ط4,݃1!IeOj:_l;W< ~tw57~w wT8h &XQu}8t,qzpỡZ\PmLzF i}AoNxv kxcƷ5ʿy&$?ʲ m@ JMb VFB+\';wCfPIfCB ixx'%gX˘p:pgtu sfg龜F?rGde) lIH%nd>A9MC{>=3o>ey%KbGp~~oWj4Ci;eSzsvC֬x |cI(I` Ïu"xX&ȹ ?"0یֱ* M_+#TR4 )x{|Y}IqnSҋ/5IGۡMx;?晬Y^мF2(3w{{}҃׽vv8}_Y{\}q 'md6q3aN:VK//iqwס>.yݲQTe3"~%2AHl3 f1qҞm%4x"TİJòJ6J2aqIrN]ք$0qL711IbDo(ړZ,08śn]sjJD&MՔ*<@tr0HԫÊF*$2㪔|RVA"Y`d( F"p-Ǟ 0m5\1'ԋTE8ߔWE<7KRq\Lr 0M 6Lˢ'Zdn_8Vli {>̔pb"0A0̒t1j~j~=RRBLiA/~/VVоU/x>58]}k_oz»`ժS6ʕۚќ߲d0 " \?!e XAV+f'Ebƃ_-`dlw8-*sw|I@ၹ )&|\li0>L T8Pv8elp>Cx$7煿;鿒FߐܭB'6JN ^w~9I&ܒ`)e H?|͛>nݺhTmv֚]e3{$ iYU}RCܣ%I\-&+ΔT[fua>}ߵ붇oX^<\km^@Jb=myԳo~ٻ?<3GQmy[_`՝{UUk ؖ̎b(@ JvN./^M(SxBb&\~5fVHs IS#c=A(H6= Vy=Z(t"G<(AfwXe)#0A%H43jG>6\4Lg gHk֝e42Ci;6= ſN [ i#3"Ɍ(;riZC wNRL #`2a`/QVVJɪP.>Q:hV!OQ4/qrtfm#P$Y|LR+ ~ځ R $Ą!whMs EmA}JLE(MCA#7U|yhT5nO]I=e:O_7j(hD8+(qV2W BTK; d2Q_$Bʒ6\ykg4[=pķb-n^zhDm?ի-ܾ1*Q佨(Cdfn/ e6Y;[=i,p^mfyЎ>pZ ߺ`"fFDH+)guk5%ܨ\3$)E[U~g#R8C*giii< oeW٬p@ y\+C+RK دv?H*l4V`3a[1P<>8pH{#P)Wm"%E\$C PyA+DjZ@J% lc9. r\Nގ&#bcB`Q`о%@=UMܶ~T,.uJ2bd+Gc%#Y<CRa  x$YJ%2Cjlq A;4x4~b3%HjOI׿:`0̦6pcfL;B6x=3V3aaTѢXQ Gšue9E.zW@p >~ws'?ɩ~ͮH*{%h ?ᩫfXa{_ /ݮy/?=:Y%A~τO욲%\b s-vA [ Ɵ oS %ɡ$h`(ߞio JQM9Nˁ6{ %5bP(T/IHΘٺ@Ac# rנu2G f`}+x̉Sp[DZ:&}B'`Ÿz(&| =vxЍ瘯DgP>,LJgowkt1o@>BXZ$C&dy fzzmNlz=35ɴy[M׭FPz5}vA}Erp:Ox\d ii%| Dب:ǛofٙN,X f9+g{yjع~jiE{Qio `-H3rqN@-+ð:'"DɬdE<(0ZP/|kc^ QEKăJTv^^M C5Ȭlq턏q[ߎ}cO,'e}moWvF;Ri{arB^}55kGyG>txZTiJ<`@=l⟺Ұ[tyXKk{W6mEq#']{owb([.=)I%ڭ178 qrX e= &5Qq, PwRPQRE<>hGQ^(~R<`N32Z!n}ksM%xLs{"8 AvN.k@$0eR Q{Aid*!fժc* #3*oWȺo0NZXF$k3R0 ZR[ObBr*ꉿ /HE |`T9Ч2yvBQǖp_}w/:y[mpnyY akK~M+X^𕶊M;ͪjgv>WZe6{?X/h>y !0dՠd`9K.;*\S̠hOHRP[liHv7 Wh'I,[1cDGOBJSӭX|$1y#`އȸXaQeNfywxx%1DA^3e/l3'W0U &п|ähh_nTEUTx&Ob6#X.2X* e{+HlA=.Hr,bKZqr$[&x$ Y'Yvd+hՁ5s^J&:On?a;EPMTeArw6/~e49m`B&&³h69gMǺJ3GۡUok<ڙzT1U0()Bm>Lr WRbU |"F1QbaK8eكfDHC*Ҕ7 EcɄT)pu P+T4V,")\bգ{o ђ% t3r6صdQ  c _ h!yZBm %T4B6b`IA0<~?_ystnA՛OtNDQG8"&#gOopi4! dE,UQc~m}gbml0Ic0^u7QGO[/o}m?P1$8F/+`űvo"=M,~xQSǽym1vS^pWO.Մ%OTf!*!D{3LKyZT!]Hm( S[ ,6kcEdO 3/L3NW&5Ol!5- ỏzt8V-qT6I[G`<\qqGC@>FQBO()4m7uT ?x`1 ۺ$u%$ M>HljVH F_$}[Z%Soʘ/RE/t0_+U `fRChR}yxf"#icQ=5},QF vb"z<|ja{6bX .`ʥ0lsX(e$ ̴E0R` Q`JƷ"]6J0Y>Ò,ơo8wύeK&;w'nE  b跂d{Nb#͸cn?eShD9 {N%EB1f+frqAЃryM*$Dz$~$@ #sw0mU2bqaD鸸Բ"`,5qQ̫hiYQcK!!VD7-[|JMdI\[꽝vsnVRo*ߟQW33_~x4Ca܋[w>sڥ(Q؇:`ipi^Sw?휧H5n>Իi-DCRځ '3&rӤr_&^h^CIMTv>.[`qaWL' >J fW#$Z`mm[:Oɘ a82KBRˆ ̵hN` dt9GWζ{79 uPݏc ݭPx &l2 !Ơ,fayCn0'aUҬCfLE_Lz'֏PJ\[:dURLujvSAk _^FfWߵYEYɄN$!)`s6 DC5@BWw]{!xjMY3]/h;z<;iCD W :D 0EZj܎ӢĶNcE'5Lc.!-'cEP R='E& IMQ"B9\YG29boU;(HQT?Y2Q~n ! GB|dro=[7.AA2 اX| /!)ؼvЀǾYE~\js3? rN{6Bҏ@)Wط7hH$ 2{Z8G:GK`! .If(3d3ppXJTVPFv(mO:SY g19,S~`jfGd5ӿ_zݏk?~?e1 V0?Jɟf߇>&cgl͘c=6:C10K"G2QBMs<*K:Je3N\zb#4fyPC~#քհ);d2ZWd87'_@dZe(~aUe#0h\9DoGvq?2)7!s6EX%=dT`"֋`2TP %y۪p 8Ǽϐ`j)BL@ӾcJQb3Xw@A@>0Pa09V\$cZZ04ljm X!L)?JBgh3VȵIFWN06'%E٬װA R@ҔX,Y Yxƿ%TR)AfV X/Yh|3Ұ}}/>?u}ؿnKfZM$xϜs$??tc'vK~~Ƕ߹}7/{YYV+QIKrdhߞ}E][5lf cD0O]R?D`l$d˱x(taF' a6a)̴tv*(QDWR_’\̶: ZZ{"ȣ&{D3$W`4*"8-Tje&ցaX;䩃(ܹ7;@"ǺBK0XOkN_ijl/OgkHSu31` sB2ɈɚW`&BBCŕ*"CyG? B M_}AS5,D\ar:)DuȃוO!&w3lqe]:ohmu{y㶛f dc@g&.m*8|^<`rPQ ɖޢUR>c5ŌŜc赩z\Kə(hq*MNy_*,5괻. xAlSd]VC%=pRAY.Dq.TRSY*M\Cd{k657BGV13/0ѝc}J:*75%K%ߋ_x(^gxWη8`Y%̺$!%N`BGF܈N,-w*VX&Jd^ln OغxҧW^mGh;UVlyToתbF|Lwz[&ջ7Y=M׮9k7ͽZ궸n1$*"=;}r=#$--wL@y|?<.1 I'dZ0Yd_LRQ̏g`٥x"2yHC$AJxDլE'er"g-4?Yo p(XIc!~ WDR9oPoiadݨ!D7*Y}y8։z6_UC$^<0/CmJ>||J~s^' ' Kk8g p$X$ū2e֚W <@< g6rn Ɔ$s},bpjEO˦#[ % eJ{,G@`Ϯ]٨Tv~4ы3 )LdXo}c)'|md&NYz|4<3Ox?{~is>|s/|-\yrCAR3Х҂N%"? =_7(2o#FϿs{޼ 7'&* ,ur 1+#(4Q=;AFʸgWxHUU!HmN "J0 x |}yJ/ UJ%dj[4t!ac?u3%ڼ k0U@l|M#f.B:%l~dٺua{1A@`̱*" *7?L4a"ϰzD{dzV" E/"7K5 rk^M>ʞj\?!!)xq%֕t}XZGxSst'(iQIi }UKQLYP)pc eۇn=,50{LZ̲A4`~@Nbq-Ê1@2g%LQ)_7Fvoz՟ŭOA)c5UgjP]&zl`!xolhj^)QBdYҀ=h\$@Ⱥ4 j{jnjB "֦N \W%c_Zchv$㊪X!{2 3F^!2\(~nXkIy;g+fm {YGfhX#$ -FQ}bK?{/^^>"T[( :!SabWgiz$A{K) MHȒImQl,GP͜'6H|]X\ud1G6)3NA?|NM8 2e@MdۇjRt*k_sv}pەOQMِPucM98uvRB sƋ󯟺r XG?z≟s~u)+lV0<g4^6ͮ0.%kX 3:>y<0 < 0a9c9 Ƶs9Pd 0 M惖eU)WGeKʦYA@mʋMdA4?QkA.yJ@ i 0y2c嵧s1$Gvk6gr![0̛E*"Vj.wR. qpsr$) ]RCCq/zny}f|;z' 'Fb~Ā?#Ƀja\nd+1fWxZ /z-+?P&n4}36t6S0Do(AE[G$@xq~+H263nlNު;ay՗m3t}W˻f[-w֛̫ ,ٛZiWCռ+g=iiF@៛r ioZƚ̛A ZH*+Yޘ : 2鴽D Bz nbɛ cfZ 0+@n\㵜 : D nF^(\y fq UAˉ%3MPOb"ȵy^w?;a`Fub5Kg#|EYD S89=dy'J ZK1i %,{B~ &mF)\ ʄ E$'ȿ㹶Z *M=˧ w1vxWp  ᳄vWtx7YMdft29'ͨY(Jm`s+eB`i'Yҡ/LMm61d'}{zz  %8p(UZ1Ig(TA}~8\CguK7Wz&ȪrJ,X"! L2FllqwvT2S)b7I JnbF9Yа>Ľ.t:w`绷g~q| UY>XcKtG`+c7_Gu;+'״dLZ~pzHpYHd01: 84J921KòWGbIo:TvXTpeI^SPL*7ZB`iRP MYЌdq6cr&YIW?j'oDJD2RKX*M*Y)#Bd>b=_ ʈXDQ@ :8QYȁ. 9`_׵1QLтiD 9qLau'7}b Bp*+K*)f@͋t%]P. m'KB]_8QN,b0ªIA>  0&>E8 !{I~fR]<c)H5 ,tg J֦.++ozod~y߮l.c+ Y (_s-n 'L>v_(l?h,&f"/L`k-حe*e#~œ!1HLo? eEu.>ܹGAAAQ$^pxQ$/_",STbđhPJ4K|YM^$N 42uMO|ϰ{UU /@ͯakWZzMxx͡,C^nfvlVʫ4=UH<9'N}f *8M,L[ @AsO Ί! v&{e KG8btCyA2O, MYү}>=LT,26@EOŃA =8Y%1yܲ yT@<au`L~ag-*1V$&+Vxlq(BiIq!S x7I1X@0D`/UV03g7/*xl.ZB"XE/D6kw%y9w*[|Nb2 }pz^y{Hoo/'Ez-*Udm$` '^³^vzPf<ـWX~D9FF{HuIj4{+&HH>kv WUFHk `ɰཡbd &eT=|7EϘ.UcXLC@Т0K1rl}]z-YD) 3* kȁ@H,A) B /nj(u{-Z=F:~vA.)11=%(X&rkZGbםWY8 E&mɩ=p FIȎ* uhe)gL](HWx丩 \?!cl#2Z=;Wq1BPJ 3;][ܳ`"3hm7$nbqxFA.؃ngrRSmW]'rG>׮_EXU'4"75 iGZ%} Z=~-R уVUA1}t ʶL V\ 'l`3K !o#Zug-3739MI$e|ϑ"^oĤʀQI3w4*h\CޙG+OǒBl)$*+(рةa7m,Lw@=/,!UHrrws %&cL?j}y/Y¯TԊTExprsD9ִ]hǼM}fEe!g=-+.r۾L+n @:D x ӝMx+؋.=ֵsgehT]!eD>iMՇj Nx~,VJM w^)^ǚ57ˊ͠T)2Kˋ\ ,(2Hݻ;̠٘E84!7$lk1w {hZ'q7`$vIrE`379&D%; k9(& sFP8~ (UT-l~}k0WbضIXdlc3G+偢AgW'a!:Dz/deCG6GtT`cCyPlo3On9Z”E#hxNHP8IX1V@N+PriP[$?f2LKbT'C!ƵؾsxOug@n'oc|q(xQ n+p)01ޏӫ` .oa5z/8]n6gs*5w:^Vi B}75Rt`}fq\j j`rVUdo/x!d8 o˄{g\^Ub:~mUFC~19!=s$6wd$rKJ W`a(~5sY5kRkP|yBe Ṵkl,{3H9V1s؋ȃCL!AIU_q>C&l<# i| V V<'&ф}KA0O+%Js?_9pKt{ jo8XUFY(z8 /`Qj3\$`(]ؘ!1wD *1$!7)&71% imh"N|4`h?L E\Ȝ껆>""Ե4"#ƞi$tLD+[S>t:>XSy{Ȥ%yՐd!mqS"7zY X?Z1tOH~P3%[v,(&q7B9>p ȕIH0cDklr.^I@Հ>ԁN>pnO/~s~u׬^B99+':%F笳Zn1߈n{<_|\^Ħ~ߙK[l @> _3UfuJ%Q!Cz@VO%$'ЦHA KD'@{("vE9TcPTu$90.ɥ" CY??NX ɏTP6,;;2 mU<*3DRړD| @ aJ4bG'7+,}Ϧf % TY4u3΍]DΕ *ӭݑuvPBb .GGCLoCnfkεqx) ]s?e-3]gt#=1|8<}9˪d{t%F KCR8dYR4TG+DNHS[w(`D `s~Fw;pO\K/t@CReC;T9q?Y 3< V4VTW]xu~{oF[CInC㧝J{w \fK'y0$ZEk0W")ߍSr o-F1Pc" &* '^^%rrW݊X,`Ԛ 1~ǁ̔Al":cA>H1%=7џG1D vd8{0m۬c3=h7Oɏv ^{R0@Fz-^=c:NXrNMg>'Ӟ-) $+0ܮ"LdXjl(fsym}TEHy 0Ԅ3YhץE %WG" 0̎G)!FNw  X6KZk?Gm[Z㨇e5\)g{U_a"A=*]R+(nc2n3_{XĪ&8Tҫ>D<硍x**RD:lXլB_Z:d՟D`)\6ǃ*ʌ2+|\'~  FF_FZT518n4\U$wUU*(˸ł.+Yҷ{qQi`盤O 6p, ,הţwIGEBfp1ںTXNfI0$"66[ d=@c:u$#9*@+<(f|K5 F{]%{2wfDQbn9`` ;z+xjz?Ÿz56>s|Sß~ ֕F[ 탬rmܱdun~ɹ?sٛ{׆QVp Vȶ{7yS޲{7? &9qT,S#р5'FeT%@# 42(۵]]XfƛɱD *C+ ބs 9L":lGJAE^l M Z5Qnxf aLC3ukL?DEl+| "6vm)c@%<0T}`` R0k"GJ>-,r_|5y0:#<PY )!g"4ݟ96iEBE Ǧ~ tBv>394㼟s,Ŏil?i23E@* tԢX#t}~)Rq&L]Ai\3ƚrO!2O= W?}{ y#lj;/s{]ĪgQ-E"ѐ3G ~̙dXEQ2I7Its~W0gԱOA)ڳYq}/U5҂P6"y ZB&ɔ0Y$BD_rO3`ݏso\VPdMnފl;@<~|+;e dL`Ycχ g%[8e2ɹo"e';g]o}}fI^Jx kT4%)hzU˾KVn`M4sg֟~pnOh?vߎ{fԬ.2Ĵ̒q!BKV(gNq`#l_?o>yn:GGh۔D{e'H;:%,ygat W`/ ]ґr ןg6.3:e=o(|RAu_U o6SrCVJY8 Õ^=ɺ?yϥQE},'_oR,$ YS#K`zs#\XI" QR^8zBF OT ,߫vc`T:H)*"#gȓ+ Z߫alBQ+x(*LesSJ&@+@Z k$$o&)5xvX<أ$*AB˒GRtsM}]ԗ,)z@UTۗ34eV>+2=7n1f,\o=|ǟyw? ?훿th=v5Ic1G\}yLԬO+w,Ջ}چׯo۰bMl7+\59㊹m\=&C%vc(_ $s'&lhYQ;J0a%4,<"3{rO60v|L3RMVKDxྍ(Gp CKG3A#vs Ӄ]0iH$pZS 60G͖%=8؜Z1J`!vd֍ 0 aLRǙÃAe2p~-cj +p,+ȕ iB ~q\}&z-Ϋq4{%OiKl6b,%8D2dB'h"֓Gdu~\L:FZ`|a\Xs(T@I:MeBFY+X UBvK^3`-5@܆?={s?/N י1z̅t(|B,5,ij.|„N9ⴓK",/-cbjgc)p`"yGA&ծ F5ǣHW(e|-l(Qу J|>hpV,my5 TЃw&xRga f(;)};8c4,`NRd:@(C&خ@ /)ì$.wz"3)ܖsNcCh^Kfq ,3cxzX[ȼeMO/ ~%|зj>^GۄvӨ#pnO ׼7n?v 4굎qxI9[:FNC2UEf[[o?W\vŎ}6.V{^kbJ Y4<6IvJ+Du4^&;$1nUU?ˌx^;޻˹ V++S ϷyUhџxKlΊ|-M|{yA$9EdhwH{:rqc-/rc*|\ڙb(yT3J*hQ6sz_:crng?[n|Ͻi\E,DUsaa24$-h+ .o'w_r%0=O?z/\~vY=ւ?bX5=o@SQR}IP^Y0\̠B( Yp0^1[Ӕ}2a;9cp$G@(<{YZ+0: _޷Ȅ e6 PdU`h 9%ׇ\= cu$>.GgC&&AFMQ)7^AXl *) Aϯ0$[fdݰZ(wI'EUЩWxN] #S1$mMA^R\G+2Qn+S $+]MLԟ:(("Bhz#,jw]Ō6"RPF.B0iT}b UU)JJ{qUQVd=V!wclF9^}7 G6˯?a1q޸j%EUꅯrNp9a\ _,]|۰XG d+[9xު+WnkΗޫZmGH~zL PQ9tX4K*v ^bq-&_! .@ב|񑦒 -`[LML?sc{2YC Ēwb F쉐J"~I# X/ P}:’>BrlfHź B2X E˘dtXeam(&uL\*X-\7u8!Vg DƪJ,Li<ۆC ,GT6B\R`a1$2Zi-2nn 9?M_>RP4ed!BtkiQ4s9cs4z=yCOn{Vƃ2AQ U=đςwZigakJ RҤJ,!Aخ!^^Ubw1# =e%@B2FTfJ no,1ڳBB*00MXI`Ieں#lG<$d UYD:% OhHp;@Ml% ,&R8abI Kz)*%$R;WeaE Oh֖E t $) ׇRe$ƈpd

*| h 8㾇`<+s"&^J]w၅2~ۣrS0PjG6.- lw,-=05[x 9޷3˦"rk=/8^7-Wn=xs/q㡿޷oh{.yymonИ<'OE!،Q!Hְ$WP$_'6' YpNG2Dk76l" jB,`<36AZE"fr٧gY+]x"=$Dv(6.2&hk:M1/-YĖyZa`$J&*PɣDbNXh` ܋x'݈$ޟܵB#+2%F/)3~patKm2@aIKحp3cN(D<9y ] J/QD,%'@f<3>0Sv.uxƒ#WPX1;ߠFY0.)E[e'qŕdx}+V{Π|ºQ&F'APB-܃Dn22TUd؃d(fGby'vPRY* Oy%UUJ{U7nGT08Pp x%3(eؓh`sÃp\\eҠ4zĤ%#jL*W !I@z,g]c7oQfڮ&RQG6mۍ?[[~#2J.@0P$:yl>¬6޹ryU[״޿ramNcgy٥]7h?жT#VZŤEB'Frä P'IU! -z\UU@I[ 63i҇e-y`J#^j[(z2A-l*d50,apXJgpiI( V:?- 68 ᖪĜ,3s3xVxK >K-|H-eFfTqe$*ԵD_OQ:V*5Ѵ!@cU jL5d F5WngNcLS ai(:{ʙ^k?Uڷv?뿯{CcBJ !y#ol\j` Ed&h%U|R`EbMi6Jn*x~`9Hjf&PdT-ehӤ= ɲl!2݇@`.K6-sWohrRϐM_1Ao^Ay"m@R@z'C*g(i/>pQc,חZDrʾH {hadqk1)w]\1z9+|vE~xP33. DV0r52Ӿa>ھhnd ߆A#O^~$!Q"WK6D¯8ŜlXc0V6KbҴ,~XܚcpdRIEz.\!%L68#WuW^MoxmOu2r׀Lv5տN!ȣ< ( 1t,!(Bb'dERap3`IyP,!/?Ʒ+^E ^-U/T>{xxnsrqX\sL/B&(,7 {4¿#`||bb W^~cҵ ;߾^VP!8☓̷:O;7IXZXh(8x%z .[U8}&lpMU,Xa?8NҺDkφK90,M Pr2*ŀlH'cE_ERuE&I1d 4nyEVG&FNP(m:)pl+UZIۋ'iS;j4f֨g*ת$BFlJ %{K!'ϡ,?R*/sQ;AdRGI+kRV-2Dzmx?(I0;Y6~z8,> 2Ul"QjOvJ2NChN-t"&sW) O!uaybƌ;ˊ:B f$ [!?%IU ^gJ3]wLp <]O>so?s۽yӏ|UO{K *HSSEWpƢ]v1z+7w0j=Oћ?pq'\PL+&뀆*`Iu3o456@$C ᪊'!@)cMF?#)x4-'LҗC1*ɂ@ҲET A7@]|^Фmli6{&\繰E..vM0I %zG37^j gN|ἑ![ d~5FA&0,1y{EF #2b႟_ ~]x1|!iF!(U4Dj,h[؆͖n_ƕnr fx/.!" -lcLv=1Hp63S*3ѫGMW /e 6}Ug`)F VhCYrT_Ncm۷ aþyzpfpAu;WVX^5-oL<4v:]W^|~y~z3]ȥ1 Y$!zb$1+P_0by`/6Pv@)c"¨_$.%cGP{hYFtXe#h",-$Bɲ=^,lF9 :aYLPA0'Fr{M! x>I,^N,}ozߣgίR$dLsjpIL&gQf.Kqmd,GC 5M&\B *3h&ͷe?' v% iRX\N䀢n[Ƶt}^i#,]W  `jȫz)~; M;ud^&iGٔWz\>#BI>(VO)/%) tLk!ҋXflOґI'Y)Z /C87:?,e$S3Jh}r&2mD^`F3Ui)K&8d>xC;#Rui?BYbH =y2.Vi.ұiaR&Q'G,. /SϹɞPw?z~W5)KжMT#Rf%Ky -ֿ_pyޚNgyx=;/JP7 JHy)3MOONc%)+) )ޤ-ׇ|˖ih6f z8E{`IXϺd lXVL$؜Xf&VN CFF *` FfJL4+bTuc0 u7l˲&,륒O̾exih:v1b ?j՘;h2Ӫ n~6EṔO$EN^I2{~&2O: a&r{޸J8ʽON5c U۳Sڧ"vqu*@tn5i%6ffc{#;Ers؟G{}`jxPYnwT+nI$+BboѾF fm_”E# &VGct]PgSI!SH[7q&ay餏\koo8 7ܺ8zq+WZ]蚥N'6-xr9WO幞pUaVHZ%P*QKb$3%TiAHH`%ռ3MQR);JQbQ2A; {T"qx 9f~+I[ /_V1X67*'ͣ _mw/qޫG7cʁ)DT«<P:J?&ԄsJXn1}\ZQYE ,$(=t1*62{ f8*gu9JE0*B11c~[s7n/o4)pە_Oo6JAW-/9NNabRGDZ RDY7zѺYZ׌ׇ6^؎G6Ix(l0ɉAIHT&)C.8x `^m(*HX)V2G'F^/Y.|-15)~* n6]a0-,/B.Kʺ%SR=!OK#$O-&װZl"m3)u6X֚E\F…"^:cX̕G.zzpکV<໏))X=5mM<%52xddqeoSb]! -zR#)`^isVAz_H14{) dz#o`7p($"<@ɱy\[H)|zL$@x=$B̪>YžV]b J{o!7SS5 86gL{a .6} K*c~=!^j",Bdeח*Owp^\|%>s֧S+/#_xT= ͔\*1!@ v}mW?Yv #!~[Ǝf.y^qէN|oʯF&ʦ*yR(IPXt H\)lgxDbi,rmdl(%3"Rs!{MDo, 'ځo*7{[T~J%rAF6jhJb_P h^J$+m3`UA]1`( -=TН `#zM.n.Zq=b[2E0xF0RycTf! , <72{-ȭ}.Lr'&&CYK֎—-7>٫:)pۮ}~߶1TiL PL+nZ^w(y%ywgID3XY/%LvV+5)?ڧgS*29/ q" *ׂ]ъzY=ey,-Dځhqmī'=~%YR>ݮ o+`&q{"x9Y@ $(8 ÞsJxHTYeSQ%DN$ Gזǟ05 p~EQ'u u?Znvɦ H&L >#).~UQ=gğ}{V0V0{ hL4T Y=!-b~ b*Fg#K]rwA "DpR s,siPmG%ϥc;- ƍbKon(ٖyJ8.[3yQJYtyqbN1dɇGS:ƃ<Ň6D9f]׍?w/~5~2竏c~Wpֿ8kUszWRDR)?gOY~Om}獿5~wk;+8ьW@Hk֌ARf<X PojApǃ%0B~%N  L0ܳѿX)i400dH&#;(7G$0я 3B*jOCb6Ti8Ġ\à&X-Hvg0͐ чΝzx`YYAM8z'8&J1:sa9 5^.ʏ)kg"_)|k*Ԭ?Q+FڔRZبe{VpbLXU0!Oq DA#ؿ"uq HTQbϵPc^&!+ h@ApTgO)Vb /%`4 Z˂T^t[Ǣf=~,̾i\wn>묳p n߷uYsO>w٧?3s]?6&1h*͇*@dtXM? QjXcXM}zK ]K='H&Ae Pz}؎}Tӏax}Q7 JiH}R15Q/P6u6$o R D3T)SM:UΟ `k 03u|kb_,'׵BTQQi@^Xk*%XځTv$ı7-tC A0W xPo_qt}G6'5=ʽףDƬz+ t.TjYw Tt`v윙~_}ٻM-|.)e(Yr y.җ$$$xxd\Tt6I@L>z}vnMTANcbA^RC -_iҷ= ؁?NT<EJ!(r@=ww|OԀeM@1c%dBI?9^(}IlYQS,X!)K "P;MLֿB9ۃqӃ0/P?+pjE\*tLvAlj)X$2( =*j$3ŰPXi%PU)3yP/cg 3XCZ 8NX<MgwbUAMs7 M%@*(OXtnZ2 yU}9W{Lw7X:vI3e| QLLy4K)|PKf 'y2穛ng?AtgH*A*UN $ϒ(uEo~ݻ !sl >j9jbbā +VŠzɩ2q`0Lg0G#cUI7г=P$kSPYN&}2ݸ )QS v: BL$xPA<~E2^ZDGC;=_c<`׳qBZz,$~$MxcEiBM@hNv!SUA~*}DL')2}[~@BZ$CXL T7ryL-0j⾸ؔ:_P" "1oYWLR!Ltc?q%< 8֎\1ðO3Կ%@0}ԀdJdmacjNm@RbA6HQ[k?E~]Nλ¶p2=]+;c?ꆷ9p{znO;gz]{7֨jDSPY+Q " J@LmX'8++jQ舔R>sVM  'ah/]r ԉa U'bgX$!* yX司(Tbc|ܽ'bO{̌=_g^ʘK%|I"ov|ZP$ctcαySc&)%]&IN'$*")Wp,9w𞐁Sh5QQl!Bs]xisN=z2Oykt-_2IjSiGef"ҰoGL0󪟸q fҟC5WMM5, hdp`ZN ]p^ZﳄFfJ d#|19yM()~m@;[Fǚ!!* Px ,k^>n#䅃X #@Dɚ9#X`E8cRI3̊Jo)V$ԅŶDڵd$i RG}zE ȯGNRf2wy&8*Z ӬlZ}x{ e g% `'4f$O@&DR"DxbIT:^Ldu{8; !v@HAK8U #N%g!a:.V0yK*TSJ GiUHN(=33[?k }-g}pn齝wyky=eV.bG~UEsJPVh{T. 5l'URsf? r_*1$1lJ{YOK&Ɖty=1#8Ѩ2&}k+\l6U$}eO/I*Kh-i$6!5f4t6V9Qu{G^` `Ei)J_Y0QbᭅI,_א6JHYH$Iu}g/,r]Sy8 v={ۧ6yn U%""c"_ʖoiNd 뇲Y#VϜx/ʁpvش/.U+RSΟ6lcQE@0YǦ]S\@HӞ3XjMhOKSbKG{ +(FRxܖHYpTN҄'!NqaFNTڷ c$ɘ1$=aF(6M*PQ5AC $ F?g;돱gz6Dlwȧ1$a^s h_}챐$U*,', C:so8JA!i$}t+ג7R ^ ;_$k&9̭ X#;p!X~,`:*& ~Ӄ1lЯ*Go>6+LǷK- `EæR-j0s>.gJ d!f4&#=]vv*3ǴdN`:*Av!}P`*,wC韹>Y[nx?lf!)}؃3U(']iصԃ׮yo2݇@ρ_㎟yhy4Ig!8v@ 7&j c2zA bʤڎNڬ~\"/:3p LbDZ%,ƒsp2vtWg'ycdυ|P֌C_y"dv`R۳[9ibx֍vf=bۻȸo"煌2^*^GV J)(EBaI'a-ZbLV%g*c!*seeQTF! } xaaUQa):|FH`Bub_+؟_coH Z `x3G*_T 6*%~Fl zQWs_HAfr Y$Fч9"}Ku[`jw_ari w{;3/LÖ cyP9!cerFTbRX]P†bFNKp,%ucVp%%=v!>~6U#꽳\"mu/5䃴4`<\d l㐋k UL>>iD`#\$N8  `W(;c8 7E3I'$i2`6F@_ʵ-@$ YPIr)cE}\ØmkyUۿk“.2k~E;=AZ;{B%z .G`͆2wΙ~ w \fׇ9 bkG='?eR=C<~(Iz)6FT@ #$Q$f9t>'d&5AC O@s+́+L `22e]A'A GD i=*|Z{Tqxѽj娓 n>{'5xB"@jZ]'}Wl&aW[x,Fc*=Q]1`ouɫl A(Z$_zȠqc h9,b >OE>*'Thp%#ʊH}jǎ׼:.(6i~V Q:Id2J8{B|ݦsz'}|8] 6v)޵{w4|a1:H6A"u}OP҅\1UU+h*Dr Lc*te k!:M˱1 yȠt[S?j? tlOC^{ LL|@$&MDŧ I`uw!6 EQ(V^π,lbOG_N_ 6K]O>YgH<(r )lQbsKPm(N[27J\c^-+%S`䎴Fñ_v=]޺%3?iė z;{[6c{7cw7t=yz@J#1˄5Y=lo$Ra=njYw1WQkrL =2OS1$ QRJ,>,j?~m$1LmH臁cx=B9G*Tcyd_LTVdmx s¹b%dL/mL :m+rf6cbk3{*Ӆu/{z?'?ȭ/[! } A fI)FK Q>Zxd+{^~UW^0=cvki&1dfW 9ؙ9B)HQ8.fR"PIq+#C\ہVk{8?}$Ȭiorϱ&:)@}{uAT3_xi&ZIXqUd'xP^>= 98/IxLl{lG`ڱ>Y4;2Gێe8R&Zhz!}|;,ɣB2ksoBs'HY\&֕ 81jGMXsd_}g傼~8eLD m؄ɦyJbi1PC{'y)0Կf̚ z|GI*``O4HMȜ=)˙A +lj.*ܬKb;=ufgi괗[zAcuYgt5܆p˷ҿQu$ l]"_y r<kGyX1!~Ա2\J%2Aqʒ P/mjisIt·/sMOPRЙK < _?"&1B& EgԈ\ɀ+:,b̷YNQ.DdDV mc> $sJ߂m٣9o#<'h#@ 3D̞;JIv<%(dc)PpfY|b 6S0*:ɾ}(6ϩVhC*++2>G੉ۙ([Oh՘W{*\}5s)j /mn=[/`F% y(j=K / ZXrmM +zH\Y,`YNKX8T/Cڿ#N83VU=j.P[A 2b.r,\Z4t7{.VfEp.oIM%0P!UZ$e|gC0նf ^*{i'DTy4bJ>Ly,iYQ%D_HUYhe8>?#y" +& Ʋf4*;٫"+Ƙ "O&`Ԧ\WԢ< |d+l, V?T%`X,c >"jwA3@E;^|S1}?4nHԎlƗ~[<B)ICA;L/+>{민;}N-wtY,3y{v\no[q[^r$)>`t31cJ/er` JPi42h>YrKq"}jjA#nX/yf:>Iyd`݁/oh4E`x92rEޛ[VTWM^3 (` Ĩ|&=̈|1GTTIhc0 *SMC7~Ω]Uj׹Dk}{:uvZ&z cHٶ0Xp%>}@콉mٵe Sw (AW)[JA wa$uM&r:=a $ pml)-T@ ?G3VIܴTy#6(V.B}OkҦ'X*|p¥)ӿ2+ElN2%K P>öR,~׏& tz[ol[?M' 8 fյNHC@Id8`J< }rc]"$1s.)BErO;?&<](vW(bPȴMP퀯huD73"w*J<+ͻ*̅_ ̒h:N/\B317jTt݆qaY&Rr4wjA+ͫW>PBP G3INI1X`Q11dlDAMeiW :6Q?XW҃r!Ϳ:{aK*pbL9 k~R;&^1ZѮ >?3 hUpH]QHZ %ld(s rE5XT1)YzF#a5)ٳǀal4qѐcZey#,bzƎHRGO,=>0MoG3XW3c$ˣd]x4DCWN*xf lƷT]r ڌk$ײ..sk*ݳՂ?e]rzqlɡC!ΩuxHd â/8Vx@1?U/=#<״fJ ľ;czAK' F-H#p`y[<~ݺdΞW؋=5 Pt1^6j sY1M,$OTf0r@~hA;91d Q1`ӵFs{DA.L#0>슲,~7w"TxZof8Țɜ=ϒn y9 Sܟt]r53ײ}l9߿0U:(3gKwĶehsn>`uL"Mdap2UE MV$bɁ%Q#ɮѴ@'kc YJ﹨E}B2<:gILc!Z yd|C;5.T4 !s~OKZd?b2{ ]aQ%v@T7۞v.\<5}k%L/fgOԎlU(5 r[f<G@n* LQEǺ0gՁ: FLaHI[}}c@\WIpk2&u&UTKj1)sRJ ?씍P56.SM\'Wk㫹]y]*|%1ɓ V+C#sZ"($l.v:^ܻLDeNj&#y<0X|GŒ.Sd򢍃7o'x?=KhAT1#"&$1&`h(Ɇ견 M8=ϜQkl'YED$0{iz>Pu^>Mk9&Im$qi>uT{FIJy 63\ [Qb^DѱzCqPD4g݄WQEw5*Y:bix&1Uh05TGпç kp !o[ϟSAy۪o`>©͏3 𬽵' |'&3Rqѓ/PmUnWNz` *HI:{3̩IjJ+:&=Q9C2JHbP]Bn ?[rRzT LJ8PA E 83',~6m%ETK xNdbda;y fb[<*s 0>[dbrq?lK;g)t;=NrYf]~=)%x2'uGg+3y3NM>=g}o{ vsQ5p5mvxc҉օ;OGFRZ\]&!K=?zAkA޲?Y_w,ƅ|wo˖"?"gbGӳ/魊 CDyTItty:~nl伄a|̚U7&e9ff'Ctj~fQŰ6J \R̥9b2ـ%Df\7&QFx H,$y [ 9qJGSd5Rl xqxxN]8myRX ì >?!s|q!`j(В.AA ߦc[!h4!/nl`6'OE&qځ1xPO&^`RbSG鼉z_JسK wb,!v)HJ7 Nv4R!pQBP(Vyt4q`5_3b/O 8ѱ00/( Vs.=N\uC)dɭHXա %heq^hx JDk9RlMsʂ  h(8<ڏƸV1YZ6`CmC+yW٭dkT Ik-sWD9,0(͏OX7D ?6K*3^mMȶ3³pc¢K$IaR. z3EJ!v  +mdx}V2 8\c^k }gw>^YBUǫ8^c\00GV\S ?rk>~鼶SWv&7DLKI$,!ZV=S5 dH5 M &VָP~-g||h>bξ$80z ʮ:0%/ik0Adt .E}Ű!V"ݼ?S?ޝ7N.;k*/knNp!9. .H\}:*ƍ+]ӡXEHfZB RQ6/bqg?d'u8*0u. &wXL苎 ,Xj ,] ׌-C'ꨈyX"\2%w6g{.eĚKɓ'HY(@AR0@Bt78 :$L/@`qEhDR6,fkgp]S}<&?O3:4s~<ǴblE2h >5/\u̓W2e=6N#Z`bv+ԟl0do]s>p?KmzĶ#Ʀ063tmhEIu$ =5=/ /QXi{s6y~f{nmɜ=;2ZO{@3x?srA ɉQ@`1r[ |Ha td x%шՂ8@q~@tq=r D6,d #IqJ1}00YCϷTO9mRMpR\ϲ|qB ;~ۃ-0{NVD1VxCUΧ64.}{ i+.KB4`0 sCX, 2R:, #] Q͏ !D. bzn3   LcIFX$A|zT~E>U S,,lZ3ڒC @'rvOS4c@XnY/Sa\Ͳt0zMZEK _%1. { JjNYec.|cdx?dCg. |s+]iY=2;RLa&'w's]|>~I=q;9}IґMCJdT:+(d1nϺsтo`~ȽkKf޳ē=ӟTrsw|V̙*'e[IpGѠ.xv^@BZRd8 7R|2Lz= `g}Dz;@5}$)9/.OIqYٷ  2i,}nJN_;dlv=`}e#e-5&+#٥;$:8#Z'<;f`wl<6- 4$х@d( Rpk$_sUĩ L&DR \<9yLߘBe/P_jI1K +/J|w̟2DP\`x}#+V &dD%&c¿oݮW7-C+~:ɣeZ$+Tҁϥˎ_>۞U'=ớn0E |@%d㗁WY h72\OW l}/7l߹^RyO7Vp|½Gu;Dӄ`UڞՓZ6F!I`&$^(glf֨\V<p OI`ҩYg\Ǣj]) CD@\a0IU s-a OvDuP%@hk@Ym5 2bAecybdaIGJ(Hq_A`E0V1m)'@ UN.,[$}l[L-DXy_a~L2&Zdle1 ;;͊||_gGWtM̟I8Ϣ5', G$}Lz9Hi'jzVA2l=#&$p}ŋleZ9 >ywnڗqy2-,gs*D**P`Y<ɳu&>o_MǾoozM:'nz T_"#Pt)z >vSd-3x\T~gk77_?gѷO\y133w昁WfY>$[;2:2` V D 2C#ZMɲ[,c-~2֗% Kt1Sz`\48I1`$<ˇK %dkxKƯwyOi{ɫIΎ'~\չ91|$=N'I$ZN-CF*9!&,{yyFu[lYzeɃS~{V"-8Pl#ݔX/z-l`~e5NXmo>s+e4l- q}Q08OŽ2[%P>>/?˟]|ն{>myU =5t`hu ReRCpk])i[xp*E3 磍Q'{dj7%9eaKvWP+$BYq>\"NRQ.ȿDGz$y|=G]*W'8&lm7 ?%\q0}<‘pc kO-xߋv)Q+cVYKyk??7/XU+p1J$Dzpş ^CFR$\ t6"9c؄/3# -& :A~Kwp| Q &͞MNnhɼy!af2gc*ʧ|WmN~Y=tc 1;tHuv FJxêVj~߸mm>\)W@勘Ou PŲ3\1ʔe@%ZzǼuo8o Jbmͽj2$b}`9)/L͡ r+Vj2kǝb:8q#f-oyc @O_ѭv |m|eRpLOL$S HBR n90std {_cj 0!I>ɻ݌7 Jߩ❼R$ʜI1mL¿6"xdEĚ%HR2ׂ:Y0A*/qA6~@Ym+F>L; <a x,R!3#S*KbYyd~7 &miCUD:7 :Q&'\uavɍCƻGʂQR/qWp@N=D0@uc jjYC^00Eh3.%,*|8͸(;j4wJ=x0Ɣv,!cihўKΪh⢀R(1ƃb-MzC! Azm']zͲ9+= 8xgNÇ a,v$bb^+a ZznT?XEO 4?cD4DBҠKe_%6I _ JYt8o"ʂ($a,'feb>o4)\5Y@iam֦\oSÁK$Wr '/y$"^, R; ƶaYd["?d! ڒkD{tH^J󓊼U``$eP?_5۾mcB wsVo<vJ\xo.v̺-ދ˄5lN}osҶKҫmkO_nm Q/+{x%b@l )3\~+_6x?x [j}K};WeKfw P?P\%O QaOCr!)m^)x9'NQ21m0YGҁ\v弟=uEI$+ogũ(V`; ȫdޞo=췽㻿y~ǧމ4PXe z0{τˏWNg}~_qŗ|a6?r#Fw]9;I v A7rg1CςI'Rtk U}byGtxź#|᥋-)'@/ #zZH0t(t Z0=vASY6S, ̂H3~"DeFH 2$揫BH3;9G0AYj2)/cY:>Jw]advکaϡVf :h"10z?wM#c7Ia,F6MZL@=8lܸðxX'3Y$1׳ctv}ϛ&V1O!' {;Y*JdJ26+ dWb&N4#X#4ף 1İJ+.U1|"1ʜ[T>JSeUq9LDPW90C`4nUyEp8);\c3ݽxpPL.`3z(+S3KL~,ixTݒMGt6&~>b:Ε0P ?gG培 UlRuSف:+Q/AF` nTTci`4aR`-v]tY+xdΗE+}jgp )/dm]gܱW*. ?$I[ .Z+h dy"#y>37yd6K+%tD*w8qsof0U`M;󯏈e߄{9#,+>1ЉJ۴reHX9()y\mp#ڀ.7wfomۊ+'VwާrwTZSBVTѰY}9Ң_2zN=_= ߝ8pWġR:&%Fơ'媢|ѝPqOWΪܼO~v7_P/U=[ `AlWk^>o0N7-&?̒*U63 %I-ﶝb,,@<)SICkkS0.-a^R(,Qm,M$Ns^=T\)S6>˒Ydb|($2Ii,&h"]pQ`1v`!.csigmLϥXKxc7$?²`Rk.)x87 *Ro$,18<Q|b u;b!"Ԏ| 8;)4g*ǫlGs5{m3m3}&/1ϭЬ/>9E {虜(?X@ }eP"߀/LF l ëL 3"mL#8CG}!#LdYAb01߲Rk|A0T":6>0B;xLp fL׏׋>Lm#*N;p% ÿ]ȼT.-[&`޼( A`1Kmd! OI@a Kzs6O8}7`im?pA:?Gx=MbʯY,Id ڳ`*Xfb o'c,2pPܩr}o lWsv (RذКi;E B*V}Fe!+N;G{Qom7ݦոyw]`ېenˉ%otGKZIa_|~a%-ӚϘ1~@-^c`܂Iؑ?u;[>{ꀍ޳=0k_y͠.lhA32$|Q,kqS\r2T_Q+^nܵ83l6Q ~9cg u!s͸R-tb-r:o:e[DkYpLߐ%Qe 8k3iLhЧ7v~Ka_E) Ve (+# gTGW5'?rAYR5ʳ nQ:p~RQd|co~ ~ýG~⭫ 5ڸPڃ벖SiLv> B_\Y횄F7Ǣ?p?ݿ_6= Y_q*) j1g BD$ ʆOIkx|FZ` ~!Rv,Tq7\K| Qkj.CuQ8@ 'K,BT!KHN֐LI*q5p]T=̿b_GCW0"ϵ mĴH]L~)О gDE1"P!:lU}cJе2|On :H' =e& ~|s ϖ9'P1(crt+yk+"|@+591ݑ& ̸Ǽ)IP;yO8 =yƧZz {iK/^z] _ c921պ . 7BR̎H,x$_?zފy/[q7Pͣv_|t#,1!cm AF< ><H!|6Q\c ~ֿ;_k3zK/#zӃ׍6ߛ6Dn#lLN\C) vHdLM5/-dL9dsN;mk{SQoml돥rYsmj[>Js0^|aY%~ܓAV.9`r/ rk.E bo?{3{ݥߐiA3ݾ3/))YvǐΤ 򱴇7X1-Jnrr*.P) aW-?~y7?g|`~W*MM%7``Y)l?o̯٫O^ln=L65IW{ItŢCgkܮ_7×^U+V=tuqsF۷@I@ka| ̥|q]>ϩhxb&O6TV ֥Z18Mfe̚+)]3B׵_0בd_.Kpw] EڜS42.ZKXBk2qRgۭGVTy bXJ{`K*qTP.'Mѽl`HGsj4򤮀Prw{C{f"&뜟)$u$O 1U!0ҴA_WĔcPL^8\RRW9E꺱{(Ֆ2f(0 .Bt*ǿ`̆)+`HyDA$MP @Lڟ1CXļl<3M<6Ca hՂ<楯]v'n{^x߽-7컰Zvr[c~^ܱ~]jy8 f3?y]v ޼⸻6,4;%kDq)H4Zl;gsG{ MmA!qH~vfN<6k۟{ȑ/\[O\+=Z[@$Zy -+f0)2d50=eݟ/A^2ErM!{8CjvCK 3|(AG@E=?^Z*/jO 69pH KD!0I1r7ʠe P?xf !X[vgT2Ν304T~sB)UA$ f;Y/4& IgN^'p|m'TPMq=eT$HlgqQ!pY,/F&x={S5"&^-t 1#ڀB }i>k\1 IgBK@^L #) 3vуۈZ(ȟ ?3Ro m=K.iL?A6%`U%Η0Wo mOe=؂J22с1fPii0#9^0.}~*s/yxuKw~읳oooÊe+zB\rJQ |*cD!GDYo˹bE*$QnR0W!7#A3vI1t#.Q+iBW5W*s+$vq5jb^ zs{H V3OQCʐmiA<^n~j KcA+9bBRڗ(ɐv@B1{ 涢K |7?SI|/b_/y6VoLڤK&K )}4MoOvorOՏ/xuNIC(~Dw087 g.p{_W?zIحho*Df.'g:t_:=l}$qB'cDҥd>Rr@je4b .&x 6L!L?W-ILl(®جxS&"_1T~f++۬y.7SB:5!FB,xLyY3 fO0`VjPE!_{+;(ٶR :Ŏ(J&9!'1Ib20B`}M'-i+$m]E8txtz wEc݊bvV;:x`ȃnLMdVG4Ϯ.?w'pBh;̳fpY?7o kIi8u閗YD:JdJ}C M= Bc{Ҙ $I".Iʂd)<7B%T.2=@ cRV_[հp:uסjUM͙'V2$$VK9;b8Y#C#D9ݽ"`!s2ni{xxxpߩ!!0"G4 ؟A3Lc5O0R>Oݫt',xNOwѯC0q_4 xmB|e+rڶz[olS_6oܫRBf SǬ8^#ӇX  $#M7fC,EbM<]E/Gԙ&V@|*I`L3}^L~KnZ7ѿ;yUWMLf@f\/&e8a rFJ̗P5'Fj%1Q.@"$ÌO+V`a24킝P ?,,I\tPvȠ/1I >v6"@F09pQFϾ~)5+rHҸ,7тʀ]EihLijK $&e/aq\t ĉ0I "1f&|UU% >1}; mm5V)Y}~|'ilSc6I`l]*yh/4Jp'33(q=I 'وe@Z0T4`B[D?n=o>86xWmak7`YASm& qE$ `ɬLx gA"@*M $VSO饻T?S/1<,ȳXVO> dH60acKV-2S,AqDikQHƉ=&J%^ZZ ,ē 4iĂc*`~K"TWYxMN {+'[[{$pŀ7^uz3a[v²+1Tf se#S_@~k牲8)$lr9:]BBNdͦrHvt=hϔ p>114/Ru$O/8&v[KG}y(׬- Ly4rUW5p;?jݟ_ş(V.! b];ZYx=媼7Ϯ<(CI!6I'k%eKr U}iRl};LG n_"fvuכizq=Fn>(~߷ GjRK=kHWq-x~v};b͚5o}T 6@@mQ^@X?,#G3}{mo}W\y#۵; kYQA#cщ3e6@3 -)k(WC[p5o ;ny/[8GF[O`dkE/2y͂td~ #;ټTʆ$ˀ m T90i?v Bf J{"7ST}at`;^6ׂrR'F Dqe-VF 9׊ Dt>7Y&[æT`)7-P!mhѐ0% #æI32!K~GZ~ɜ͋ t5xO?dj p40cЏ H(!I7S&]#'_٩Z>Aqɷ $Grzj#h#{A5(VsIxaA7`t\xީ\Hriܩ:wio:m VX5#.W(Tk0ɽBr]1|ajTDX5"{-.1Tm=@ȰH0c04;U#:z2ɚt[ inUabž h$UĚU{#0s[ 彙nKɒql>kIɂSFƭWBZ'$K5Om\q{p9=۞I}l`_X='F'_[4zVg,_O%['.}#3G=}qgx:sܡ;wN~eby*y 4&PV܏З/ݰ߳>o}e˧}g* 0Ud95y%C T_1o.O!HS u9N0D>8VkK90ގf XGR?K5l$i גqX ư\J\+㌬.Cu}4UpT9/}^kAvIp۽"]1zَm DlYǿ-['zEZ{c"T"y5Gi6d IlL@ɲ(Qc\dD8`bG0Ű9 l@i篑тIev $쥫z 5y&p3Le0{ȏ߷$!޿ȭ͟?AĵL~?{<' ɬYvղUDA-s =g/OR8$ȫIʦp~V<ƒ7\<^5|k@÷ۈe@H5Ń}? l;C>" #T{P!z$_ RƲrO?t0gkBQ)CzYǫIC)"P1iu<z[o{* ?eP^K У<,[,BW;H-;c,ڊ_#Vr/)L%30lqCw,%Ko?gc#'>ng;\-o+ԕqiLnCLRE9Vla @dB?)8ODoPFOmɂApa%d{f~5q}优Abs%`>LfB$)]4W@x-q/~VdlWCT@ kO^߻nC{}yd7~ p?~N;[ O}7vҘf%6BfO۽gߴ|k}nOz@[ &Ľf(=d'@XOPL #BKP 9U`}S WRīJJT*VhǙIU @P cd:qb//\ 6-P%WC*{0:c MҦ$I(C kް*@o1 ?0p n1B; @1t$m}-nm&oBW HEL̷p}v^B8(*H͙*^C$U V.y9x/1ri7gtgLӄM bKVl/-<5?9|&mER&l4̒OM{u{'ᓟ+kt/\T){c"Hw}bfq,|j˫z/}kc?oV>̩%l l1DUPy6Ӭ5j3q;;K1 ^O9/w^4z牑/ӯy5ߴ-%^(0;u`Gxf :|mojm=e^2`dJ8> g| -BBr!'qCn-Xp@Y ٪f\s߭&+[cMeytF(,X}JK99Th3m%Wq-{` ,QǴ (cpսKи+u2|X3gl5 #UJS]l`1wy =T)QI fg@1-N"6ڼcѱI]OD!RdgA1<Dqσ`(x0Z?g/%vcgѬh@R%C sx֜iYdB +'u|L6[EGl#;N=/x:b:yv0Dp`b uvHٳgҶ[MJOrJeH-mswwcf>^*=@k[֝[u\^z~Ѩ25Y,Ex18P3 P0@8<,,հ ?LW%WH f 2W(`A=lEy1t\|a0"u3`=w딨&-4l lO LH HIG wb3Fd椣^3i+pi#>ʆU2+Ya#ɵ \HRkڃiU P)z*](Dwa;:U z ѹ%ߊ$߈X613VezSxA}U|^fFrb)%5*jamI%ʰ&d[>OԡwqCh\oR_yv`wݢ&QYVV<{3檝(dy`EɵEɮ%̰B?HƎI va=@cmQW1–+gɔYp:%뿣4a6TwX'tBpC^X9H9Ekw]h|ĐGlX2vlPȘuvQȄaXxКF~Eo'y} 4:2Df"ȍuTLXL602%.s'rX:fhTS8%20JŢƪd' {K*RLR٦9ҤL^Hl Yy @Ecz<~{p_q ĺq!20bϓ7D33bT v`.?;):GEHfD̳BH$|fgdZ ktAPyRkݒ ǧڮG!H=(f9СdmnϡD*)ݼe ԜFJH?Sj,Wc> EP(ɦj xY'tbN8oĿSoǦ(UzW/|ջuՋ~[ry=//~yK;!S^H@cƏǞ;3Dž.p y}]E٧ܚ:JG! ! 4EI(RDł﩯G/;(*TЛBBSY3k9Qy wɽ2{5[MgekK5}X*m4~l c89{)tO8Vv  2&dhIJa[a@A,c!Wɮpvx.yM*'ߋ PDeDv];.>3יZ::~y7K\*uX  fc1oKLU nApз2: vI]o̞no;u!>AF9yHpDDZlRRT:tm3xTfW؝]1k$Cek"5@֋GD91_#!3u%Z8#͑eWs3:7{b.D> tI9?1ltUIἤ;BYFOqά9|_{['۩("I(aMc*.dtu X2C`]cLjqѕϞ5U枱oFSU)ڌ9xkY'L;;W߬ WH,p QφNjGNC/2}/Ϙm$'q~")Vu?iɁ ~kѹ짆T-K9|Rm ^@~ q$SD$9pYMF1,KH2L,Y; ĉ]0}z9<[_m㴛-HZ$ Wɱ !h=&&~$sGk^GR{)@0\ALZĨ iY2Zz3G QGE*%ݠFsG9&H$Ϩx?]#xW͞Hf_=OޙV::ݮFK0 N_RXhh6~=*(%b4\TUy뭏гG\%> TqH泽ܧ&鲦 tCQGsNK#W:EM~d|d9mP]ڀ 䎎]z"ɉ8';j9adz0T @BKO= se!]F+6Q; sch[^>f΁*ĕmY a|+N/Nz_dv=nF }&y:Ԏ=fo8t9sӲx_ Q;i'xhT?:+Ï[t|_Ϝ}Sv~m SŲMZy _(aklU*)q jSp&ɽ-|X%=^9fw1جn<-Vi1+ sQpޥ9^m2RQuU ᾭ~>^ B2Jٺz+-D1gi"@ iTPZTD$lG2OZļgoX'MB vsA* ,dڟWU{p_֌v@Ǫkނ""ߐ|] ebbø{s (MOTj7Ob"$ȳ$-!fl8!06XDK)[ې }S{Q1 ֑|acEA,CѶ }%BcY@v#b F>U- :apn>7*Xl ,h/{=Q+6Z,F BPђ%·EnPxsŵl~N; BL-FSXI;YrZ'~у_S0un:`bE<=0`aH[ XWbZ R}ؗ/>^b`0Fs׉Atm:k41!C [rCL3x(X#"8l `8=Vh\ZLh[] 0{:2. $!D|bӹ %I\505eߏn\8Oa/LpR|Ȫx*02{<ߙR::ae5/] -[y %PbL%c]<܂Sj4 cRHJ{=5a̙^o4{l&=SE㻆qa,"K+ä<|LUvk笺I M=-֫ ʦ$<g ȍ~XIĢ:. ),ȓtrdDIn$ SoIj3-vY00OǾ\?*G?zV}Ƴۖ߶670{bߟ9?O.j͊{VR߄1cҘnD'^Otf;<=wuO=|Ț+H[Y~gEV&{I| >{J~|ٺ{U٨(KnqE@ ct<6֏`@Xx jl]wu,6d9vn34&LAjN!0z0 =ùYk[ˑ+Vy_* Y&eŹ9>I Q)niP}N~ľ;ҰI²|g <3u+܈f@D,<-|rVYQ&-j Ah̜$`M*lnPUtGQ2l3OѸӮJ /SFy$sr-E$1'hGU3]ih#&r[ P5?s(ͬ/jFFDE>B "IBjzFHaJ+Y z+bIJ.f1[+00V شkQ, E/}~uw~G7.َ"GJA\-_i њ.P0 Ɗg1t{pv9Smt0kI =ۨ)x*{omƠwsbm޼-{'-QW ve~)X4ԵxT060<-D4V!6@lbBy2ʴfsoO3$7iv;f#JgGk/˄ 1+d8*ztl2`O8֍tfҁBxߺϚʐ@A"hC,*h`>9AeBI2`gkdAῩkR'e" 8Zgp^XdXIG#QE$sz+.v7qk3/t C]1 +[u,p |ٝpaRNEV'cV͍G?exaQe?$kE"_:a.%S-Q: <}l$KʥvPb#[ &ٱN\^P6U3kM*[C]KŇ+8\=wߡXdR} سAuf4<;_y:vmּ0=P]]=cd$5# 0xv>%ǓO/:V|cm^LN.rbc(J+u'c[#qummAֻv[YڬY^bl?w?8U%x[Yp ib8*,x4ߦK88~wE50J%]Cӗ'gkhCF mBۅl2$D 4p`#WliITj^eBeC%;>*{,_ho? jͽX@}zM{OD9|w[^ق%}X`4Mud4[3̦QaF1ͩ==y6gS7HuZ'FMRDRp+4db n"l@gv~%1zTψ{x1ŤvOٺ %787?ŨU`~Bh6i=Cqv͈XMZ/1|~L& 6F_#U O9>v_h!f_VN?9%c??~w\%k`F"s97Myb "/pӡo|-y/vȃ89>5Ӥ $$IlN.G^ѳ5eX2<CqE{ d Y? &a֢>ˀ &a*Ȃ=ppfm*OF E S Iuoi$ 0@m'X -P sxFfM$c53$RV,?<&@ `dǾBj7M1'I`_5Z Ǐ&Sd?.!B'tp`&d݌K}MHLH/Rؼz @G"_ur*^8i|{ n}+f:y6p@K>n㥅vnuY^3X?bO{;:SGuZZ//7j㪖9s` q 8K;fb\Rt+/$2$^Օcs ] o~ʸq֯VWg a} =+ȐdPL}]5MrEy։M!|rR.:16Q{}[_W81*;%2G}PtnxCWԝAXU0.pt`|l3d ˄XN4JQN0~PLڸl+6&" ( ''C e %0AGU8_|yZ~\ɋ._e 9jO=:#OyJ7¿%I z'Ln&L<L|>.X)_4DKnY߯Ws |#P.^sÿ-U+"2dPrI;bYBߔV_#obuwjg/~M&nV5% ϬZJ7&S18nq.P3CyZc J}^k ί!Ka nб;x֩ )/lwIlJV+ *\bGudh`R_r2ArLM@DV:"(IG˜׈߾QPӓ] [#]<IМǵde/X1`[aۄ>y09_7oMi#6c:r Bd6'[ڹ㓡?2$ԒŪ_n%)+yFؓi.#d¼X`I8Μ˭阉ubEW/[1k-YN:Aꋤ< ȏCx=k͵WƝԱ-䘝6rqad}< sX$a^:ݎO}1?kU& Z|6&oj݂RuԆacYc3yE`ڬ`]u@_rjYD,@z19VC=n.~޾ep{՝(o0lwuߺRתR23l8 .>( 0Ri!6!_Z>3P .%0z':2Vj yC1dg)Aa%mG|'BBPA˂焠Jha%>27<xqp69Y8)홿~Eߣ6 &uyƄĔL*>E 6)|q@}>J=eQ(>12RM34T5lф,-gP1G5~U,Tn"$8b}n]kj.""ȁGe"# `G,pF u9KEaBq%g6 &׾7kLe%l}|].̫vNN{Uc;~9x7ηxe;'aB*HHҭ lHmF/!)`9={Nz7dyhuS ,X+b0zzܜ HkLUyi٢@HK"ͤArĎy׼k^J>x耵0&Cx\Vajs.x[G*v$ODtt`p 6972sr`śNpiݽE;8S YCIIX)a^:g+>r,~}b=h mf*| {my%=y=S(v%P(p ^|h|PZ<;|A,v}ׯsw]K'?H$a܃,)fs!&RS9[òeW%?7wފWv=ww7;2J03 %chz_xfx6;/6&䎒\|*Z dcXrs#BoD`N^47w'cƋb.)GD f΄ɥfG@u ZȘAR(fSٟ]S Uj OS&肧E|$9ۧBD W" M6cK;Y|=fù|,0$ؤ%;)K(W"HKNȿ*w(1'LgA0iP ,GO>Yt`? >ҴMkψRhySZ^O@;";MO|H^! .i&x.w8<;ypk>`o[X|z'( y;7jvxΝ;ϸ9uP꡹^K5ʍtW˳8s ~tu[tN-q1[oP9o?㊳xLxU:LTZGIaM1\!o.EkxHÈZ< SŀU6^ CJw衃aюD04aEˆ_6La!˯ώ!ǀ<<*ZWi<Z;fp_UhIA@{P)1,$Z!fF=vz MmwŃLDZ^Ks~C lP ;y/E("6O#Mqʁ\{s`F-b2y&LvČ0 Pu#GtYvu>hqF[DyϽrACb* XzUy?}?ݶmiv ,|#ly@f2uPGiËMd $& Fr=o" yhu0kD>÷СG|f Z߾8/vw9'i)UB匓MzKجzE?v 3d ٻy˚iVntS >E2gNi7 77+!_Ő5ȪQ0bAb 2~:TAV:5JV'.,Cbczo7y+Ƞ g}Ұ.Jx Nj,nD`@5{a$%-7i{=^`GX :[и~^2 )+mY;=#TqI6/}7IfHNK`YW$uexQ$|:1S顬uBD1%Oh\U}k[ _iS PlMhn.tSL,x[%E$/z8r5 ʟ6A|AgY\AgzN봿uˋgc=]ɰ=i7SiOӧŽ4ՑٿԳpEJ",-9jc8u/ a(IaPC%sfͻcvf8/|>y' }%<+u8ι6θd]i3:-]fzhet毞o޲dz{$5qmv ϭХlm+34238EǬ[ {4pddHΛyO٤*^E 7 #֦Z羽7fqW_~;햭c7oݕ{Ss˹y(yqi: 㾄QQ-hV{6`^͑4OI8 jS)90Z<5W:dD ctg|P $vZt9-cyWG1‰@ٿgxƗ9opտiE dxv5x\8_i[@1Y"F`fJaCfK9)xrɂ65'fNm޲;4K)=Le5=5i83M70&t>;/rU; q1ɫTI7=kHi@7])s}ƒ 62kL t8VEfzs$j[! >R#"'ӈe,ROoo ʃMJ"i73*4=@K'gNd̂M/1-ckOօ:IX4̭Hї;SCuZZ6<[7dr RɮDl[`$|.1'i4!L$vlUg5[{YwMpB-\p^hB|nZ+e):B3&I]h*)3q] ӳϽsl޺|o\ ||?o'&cW _z~LK/ N2|Dpan<ȴɳy >sF (la#.~3;^taǿ?s;c=~WB'Ȁ1|JMKP@eMT5p.׳?Ψi#?ҙ7x܉̂E#|~?z]90 xү|2\{I(Sp0d} xd W\Q](b$g'Cϋ^^0hy`'ctT9HhEk/ ^w8Ve@ֽ=xxm&TGpq/ߎǟuW0>{'L iZh֨rO!{d[O4]ea<,S,M M6).kP;O:6=k,XdG3ߩdD$ehЧȝmBȕ!C$Y'7]05uW'\ka&G< zfc.RpRr [d ǒy/CGЁfplAx{_J_߾{Ւݤl%HL[h5GOH[TX>B ]4INY<4}`mL.~0:C,>iȨ=fbiQura#C{ްr`M牃Iab`X@&ӹ<D&V*̀4d)-E"p#!i#}%{00}}%$*BE$2LjYF@dQh4FN@a"|`ݺ `>s%ˮQ&䏣He7 x44T5{gWru$ MS'IQL`Xy#uԾ^ޅ 1OtFr`Z @@:dLv2iCFђq"_|_ޟoO4Y?ny:+#j>%>łd`JJ[$*y ćkw&4n L` |r繻?ܧzpՏ636KJd4kBLȭoCT%9D nl7{n<>ۣ{s>[윝4_c=>RkEŃ]x|XZr^9NJ(~m9i( _yˋ}u<{ܹx񡋟scyݲ7qrc.i>$s 8imh^۟xCGaYOO_-gL&b6Р 0jX5&mԮMZ2p֌^ Df*b2jfuȚ}Nw}4oy+nd,3ր[sI&%A!'^{@N/=z Q D5Ԭ@OFVg_%QB p,ro<}hŷr".懜%=⬔~iB.[ˀ:L xmg&C XD!C l9%0<*E }c0z DN:0#%[mmT%( ̔ݜx (n3caiͥD=Ap} cg`-0UPqyi}HDN By?@(,7>Hq Nױ` >rA { yXѳ GЪ("C~oC⏃A~Xen'dWn4x$i|_hw c>-M}5Wn] }9sThc{Z@p>[/#ЭӊTP2Nc|NH;J{2qkx 6#w2]O *ui_$K̄|eȐ>qĦ{:w NB8Rr^A#+[\?ZDߑ$=2R_9_mh=ޒ9$ (OD& 6O4k|v^u+fV9&N-x0篥GAH]88I~)Z8Y6!0k= #"m(F`EUK+/|l &C?}X]ЪIWE|/I Pʼn$ZtmcMRnD ^(J .epk) @xPաRCQHIJ-]]\|I'L iZhȬ{gl%A-BCwc2za.ŪҾKuႅcES٣If4e}E_rTԠd FH㞷,kWI8);39V }w(2eMp.L-ιuU)#3vҲwSKe.ziw<`;]'xhph7v}]i/ލ̄%.T㕏0o:Gu+N> uO,A$ uc`T$89&J@S%˰r&?{fַm|{~=3 `vOXOO6tb/@Q4̆Wlc>W^ {鏷+F#$dJ(E̺$/O[pF+6>^NJ?;IʻgN}",aF S.2Is:KgPDwu[0oa"Drf Y. #_JAp2@bo!~{36ĘzIŭ.mb.wK[Cd%їGB0AN͜Zu\pWqkŽ7}k͑կe &.*Ah<|s^y'q ۾Dߌ<:]j|<7l6_@'x7 VIyCS.8Stn*~H'M֌Cm4p 3R&a+e3/l N`mMѓ@!u2pƃ Mbΐh FqXj|-#٦3Eсњ9/R*x(xhT@:$fd f&TYʸ׵ΠZ Nxv)Ms4s&3F |@ X{3s^_)X)b<' sr(ݖu#tt&Z<*@}#"А>c]"1C 8:8i)eՍƌ /'ۻOvNN{-h?%av.ɂr"hDo԰2Ş~pѕ7J"؃={e1-Zjgj}cY+cbfaQn|bǕɊM88G-93UYޝ; 0-QBɰ ձXAX%G`0{>O;?o;!jCCCL.w`a%*c4]brt;9b ѣ:kgy*߼3WCWO6 [c2qSD$!_՗ UWmտ/WTS l0J {Kq|g愍~?~ߴR6[S⒩)dtKOHn~ǵƵۤ/VXp S^VY']"`O[:@w,&3BvN6eJ@!CzƇZa&u$t;Sl Q r ˁC(Tºx> ZQ>y֦{F9<#I/d02?f)M'4J '&䙀StOH8 M"[-y('I,* kRg֋}{8F %5;O)#%.%OFΡyS􌇾ql%38;/_^b0EG%20ۭa/)I)GT#MG4~%; HdNv;HoXbdӂD2{ۙ8;Xޱ;7kfx_uՕ~qM9̙c/?Z#RiV8-_j"Wѱy0by0SN2y~+5-`_0>Ⱦ{p)'ro'=v)ˮ8Ba|) u(&$=PGPF U/ً3J.$бpQløփ&Hy HlWηFIϳs.JfPmDFM8pSWz?i'ϺWoYx^u߂"s@yH.ǎb4 @3k & F 7bHb;{w8$ CX3Um<ѐ-!yUWtO~tScr1sd1Ze¸C/צԟ6E}BWCC!cXPOd+CkޫL`f҂ZB#[lV7;ED=usUb/DF#(83AK)肠iO7=Ȓ>-n+2,JNV)ƂIKph>Z?KL[0PP~(4J37\_\2NٴnYq cs{@ ᨿʆ)|z KD~. yHXrJtKfTmGxAx2g;xy93 !P R"XU0L<ĪD, e;!i jbc+.({:;Uih\;t%UZQ8 %Nrag SBt`j]w?Z(M Zfv%K.R2bVv!-\yobkg4g'~2y>s+ w+_-=?Yl:`+^scDՔk(T~lGnN={a׿9tIT.|t\ds&(U^u!:ib96LAϤT6`Aɹ;6\iƬziRw˱`vdxn쾩,MI}]^6{Wo6ݫall-x h|;[ү#Y?gSkbdBk!&2&OI`Z, @XqE"|KSVJɬovL42-}KFKD #h(4R@!z)'HB EZ[/f+DzʮYr`K^ʒKbt#:PI nHKpb#W˪0Wf#5#HRܒn6@jb&[ˉ3#MZr#ӳ y^;C{:nR^D0=+8^ud8 7DL:@{ش#[&3x K1;dƏ*VL 3 k.3̛^θ}ŕop?wP1N4'V(}>G UC;R)4`l>u?Za|f1$ԚХ>q>t"kiBzN_+00$q6ca`uIMQeMrI-C^6t%CCenR3;֢B˦3 Fdr4JbMc1!dӞ;kX˰j( TW͔,Pa#a߀Pܖ3JПG0bX_GdC`ei+!]_f`[4 | WX.|i!T9>`]W~cTNJM1 Dg"i~̃)^ϚyQ90@SHGyfR_2M Ciu|/>PiiV}ᐂ|$[d%u: baD:j\tnDGv?]蘱7j_#xdōKqؠ [}v\:S?pWw/i'/:!!ѯx`:ݮjKU$oc)ž]_| _?s93;cO\3S~U6:I7ܬ~씏@gN{?_;}t;W_% H`#T#l-Vڰ-:[Ln /w5vkBYs힞uQ‘dLm{ʍwCw%NC |Ӛ{EtW/Vtԥk5[Ml}um*U3ׄ*߉iZ3Ɔn[ɗ]c l.#c_^toV{̳o{VOv?'4sLbj2LAg gb]Ҙ :ޏR3A`shbR~OEALx,] tk(KL$[XLniOA~>8A]1>A24H.u i,Q>(^B0xG~1MǸX,z&69!m {sS{F߷ ,<"O8dHs4[h/kI$Qc=` ͒~X4][[w;^Θ}o|~f"@$g.!r-ׄ 81~";F`ZXjcYHMrCj4a0f0-2w}hE"pyPʨɽf7[<RU+˛AKLA <IO@/G 2T*\Gl\R"jRFi٬?tMF\tܱ/|::on/ئIRb^((fs[e6U(Pq0恔4Mpi Xm; 7rϚ5 V?MK=3f?138_z;ysfG'QSAd_7ظ,FJ4(d9QGyzs{*m7LdΕ.~0"/X<~Ƒ~:#Fu߮^>ʻ/o>EOwj+h"c bua 21I4*@mAšW*xx};5w=n{>);j7t&c$5D_뒜S۳E13'n2$]s XLvg'sk{ F@hUyl&f)InAIMp[[89̯νI u@H&0 j&=͢,*hVS!lw;Ao6$MLx">J6WkfxD*I eHr89M6J 0@},{~ [( oTc:/=ۃTM(<04X<~烾ԍt\vz`x J@yeA Qm,VBrw C:8ˈM<TcDTC@$ڱbp?2Ф`Jc>3 v}J<$ӍwFr .aTQd+δ 5 ^n™Cَֆ)@\tKoIRDŴ]d5C ӂuUݫҍ3wZu]vYJ twK˼`WFd_մ7)$۔{+yP .HS^E'=%!7mߧFN#"*R(`֬bFa4?~=muYy9_Byj![,~* NG.VNf{V޾t뛻zʐ6R"PPĖqx0|)\%:د. n\pԂWͺ7s×6?mL?_NtY3Rļx_@ 41g Kj%P| i$ٖ0#߇6.֗!By] :-5-H.ϭE;y!Uwfi\~"Hd6sItAWڳ̦Ĭsd='B`\D*SY?mY | *!r|݆!8$Ë@C0I7T]$wyyB`|&[BXW7c9;{e SEv/"{$α\HʑyUbt}_X`JS{,t!6ϣ= ܷ]a td\} `48{*V$ݏFZ(˟b;P"n [Et[JYJXj0'ژ'tbOY7bY`iaՠVeu䶋O8JgN[x`LwwwsH sl*K37D.[K2aKKnXq{vaE^l'.:5KkKQs+%S Ty"^J XUk!|-f'8q_+~,dyV] /\NF;cV %K~N_]/lm b[{ 㳡CP+)d]e>/[?Pf =$TG8ON&[^[g-ǜ]v/aJ9#mT]A $[f)"o d-J= Lgl?gq ]Jz4<{XqI=eH _ !ET!߅ vߏeҐ; yvƒW!n.]vrjvb$i,Xb_SV4<%`#^c; 1xVI2Y>%xS<{_S&o[ cmJ%g̫%RX%ߏ`rb_ ^D;q/ZDTpK }S7ȱS ;@nj9P ϻ{ppYSD7kgN/s3Cc۲ aߓ-'#&C,埧'pe5}oۮyr{J&5e=P-px` I/TRYI׍.7umt`0(HU ?Mk[?½gDhb5 Dp€hP+-4 BmnV)֪u8nc!^m."GG2u:#ȫqY6 BIYƚ %I"^TCгVCR^˞ǥ'<3:lGF-Jt~މ+|se;^[3μyW^tӊ/fYl^׆}>f>㳊P*fkj6k5\ŷ}\w@mg11K?7qIn5ݷ2R1H!$CEjׄ xip͉IUVM!f?!B?jK%2# W;!֡=>/̋sxE ŀEhhΌ[ 7l)L%"a,ƒN#8U6Kk|oӂK|/y?!]  H;kGs v^1w+,6Bˢ{FZg}|4prat@(, 4"7\eM8wkUkjc!`?Ez&=Q^AGAo/:E}M[- G2w$JIFzbRaD?'5W(g2hTD>.,@X{+h8tb6M/w}}隅s僇߯[Fߌ̇~7>םMz~ٺB_M cQ7^,Bq½&FEu b1:0y`%*aV 823Pxm|w$BȌTt甈=Gaq|;^6L9LDƏm>>-${xV83| h_3>ʬ*_|{L&`sk8L wll9yFeDL*+"j˲:#bXA0RJjC;K JD('\_I ϕ} 6ת)/"I'R̙ `j<Ή-e͝:v" >k (n>w,QL}VZ컈-6Z3 n>GPi9j=:MρD YI/93s7Dz'0S"pSGw&םO0Rc9HsלN=!eF`ЇN[aqW]c;{Q[?:ꮼ:uC'L4՘@nV|Ll`lˏB "AAhC{zY֭s}g{ד=;} =҆֘Fwx`+J(je%K&)$I,@3]L$?? x/-:G7^}#9um@^r!H/H{ HdJS,+BUTkU5a1TY:@5C#%5u!PŤJKBSpbȳ 5k㕺a6D\JCbR 2 Q,@`-)L aF܉Dbe% $LvlBYd; Jo*cEA, !9Ky$,)@yG & 3C8?k,a:%CL;97,e$ǵ4@94 VIr X nQ M ̤JU1%ًƂ6 <\L-ektjE<їԹ-lo\}>?uG?sue` JrT+gﻒ"fvJz@KՈǔxteRMpF(WJfNGiUI[wzʂE%Ix vBIO~wնG hw?;jx7Ϟ{aڢLq T`4 HewOݝdWC )!&)n%]MB'-#abH7d|(Qy£Oi_(I?33fCm)IHLR!dRfVsV8R8' 4 59#5o1R=کQT lc(TĶbuxql1G$=a-EX'n$˶q^w6ĵ9sh{'#wAuQG]%j4br@Il Fd1A&5%mR⏯8wG孹'?K`zq5?q ֭TGus=̭.[0蹄j5bEٓ)h5X`iRb N9ȇc|هDZPwNpǦ?zyǦS Ci:i#%0cN`ʖ:gYu`ҏtoLKXuNĘF#:YV*D𠤷Eem!eHh4 ؈̯U#p-*o>RWOU"& @Q% #2i(79T 3j1F0U T:QJޜĎ VRNbW:pqK:e A!- %IލʃYm@d汢<)IJƎ~( ǹ$LVB?K{i>u E]`[0D S*-ؓ9/y-I &'0`7c8;0W^HEx_xہ/?O_n٧ٮڮʒIY)iwD@LdR#s31atұt[{$odԿ}>t w(Քx÷p1тj@kS/xowy3O`5=(uP"dgOEk]^52䙙ChۉIŃoW&4stp:>:ڄn(9V%Fhd<[U3@M#4' '%{Iw$3Ax*i71u:RP=`X: k8WeSu< EE  ~B0ҁ$MIC xP2LΦ^Ht 1wS BT ?E1dd=BJ0-r&&>!F>4TF`=+j(ϕi#y'c7>_9kkzj}{^k.srf˃!G Tx6R5r]'$긇{ v{c0@ AQ=73}wQ6 Q7KnGIE(>\p屴ss /a-N=$}~J'OOv-ĪD),.Rz2ф)` ʦGanvM7g$J<y iYΔgB{G*YOy fb!'> +MhJ Tbzw4N<Y5,80cS\Svx`)+զ !{ɳ1;{ώ>i⩓|};QG-??n4,Ar 6Q{\'&1LGcWqYl4:E('Mn$FdM @뒉 &tWXTm۾_窾qn3N?s_oq/IqEDuɊ+ ;dh{ZӃޝRr%zVY-~1 ].WkN]k`Q+-C I0D5pၜ,w!dkKOL*QXCA1]]{Z?ieX"^ )<ʬ$3+7ޜ]]J9C1U^87^k*"2'! p^3V % I>A}orRdElpdg~@qiFv Ct[6 J$~"_rp!Px{NTz(A'4R17V\rC߻)oVzB{'8qi޷8p Poxoh6+֍?~gߵ/=/o'Z<ٲjH8'O}I63Wʼ2fAG \0fUPs@qo(]9)_=Wj w2%ǔN%8JL]{(=JtHs8hw/[8zgF TqoCoV!jKcZ}]5&S"B0>ЖI=Wx TIB?h;jƢAxtDM08d[kuOt@4`~ iB!H ^6"o28(O A$ǹՀa:LČ GuRUsfNҁ֌OyoE>>j.F'#XQy A<)HZ-tON2Fty$h'c.^z/-y'} syULuSAt)9QˍK6EVAiV@򰔸#-}aTSe^6""$`ybLZ$TÉ@Lap@cf@Lr2!{ğT=k:ar}#* s,A0$$e bF"$E9BR c d$0_18K )T21,|J9*J!`ϚvV}A fR6)J*⥍'3iD6W_  73*61y2a̢,cy+j1!w4I j2@K|0ʐۗbhr}\瞱5=xۗ{xPk]@9;L0r9h9l`A6lT78VN簽q=RuLkΨJF_P-KJzE('lvuQGu"cڱs>{-XӅd:/JN+`"%Czl^?* ~3j_دwϿK6?o_s/^=&ڢ1[P+-I^1Il@sֶ^ @`͘5Os$'KX3аeI־vɈ/KDMj^B]'+|4Y޲ SQ9I\ be{uW2o_ vQ0YL.ȪG<1m_1I;HfA^mJı6ewZ{!4q@$J!'v>{BI"z/1AƳ@x qݧ" =ʁ #ʜl4^̒"0rt|K-0f4\?b)%/JAn((ޡH =7]GY89wx"T^čX`qu`(ק#sUSe`oun4Z ȓ{JO%΁'}i|m\' %"{{G6$@Ɓ. %ƾIZg"'V=KUV}#T`U_O7xOjי7vuW,jLߧI$̊"2h<UXmE*]Ğ{0R 82*@;fSO0pFI9&ɷɨig:9i&~OhVm`|p݁G4`l &Ujc8(* 5ِ^:[BU80L$+:fR3F }^'o Q]ƒ"6 ʂt1;$"n6sנTPjBf B hQV w* L O9K$G@F|uMMD:.ltM޽x {wrȥk:5#/*3$ۦ-V[6۷i8O{4Oaydz> N#}W8uQGu񋎷m-߾y4IҘ!πOP$UqMIC={-xTtl Ǧj]7zvx'</`qaױ:B|80tM[Qr_.۪ŕK(  lYְuX0*͝4V~qP2Z˖JPp$UEƈsUR|M Ũ@owֺ!#d0 *Q zZ/ڿRK{^K3e 0Q)Z"dBZ{/3$<ѧ 0$-YF i1K ^"w?$82ťoX:V`~ARRzO%&X)Sf`KH {<9ǘE2$i댮"wB>-6Lr=O4!; 30|LOw J%=y /A[!ղz,ecQwa5{1kA[U?!kzHγW݋75Ȏ^ J ʃ8*gڥ4$  ͆c`k+=sftRĪ4۱ <, =bTh(H`67=P̡=D2azAOjfz-rB=Սqoqه.;F,l"(8l<+7qIv!o<`4󋕶') *ЍlM^,q4^ YVP^0JzTh^w_$r؄I4PhRz,zPh'(GG<`p~`;t:չ_>w.Cw>DUHmӦCwzoCt׀Ou+^A;4Xe S[ikD4*0q&|M쿭уPxuQGxz9wu>h2T; KW£E8KU깙0vfUcdkL* M` зWLsc?xwFdu0!IP *!;U6lbk5ט lJj.:050>Rڧ#Fg 'og.LI~GZzzxrF P /BnJ) Ivy)5kHr$R#)#sHm+H)s`ޓ˶J߃ʨAa`:,Va@`Qn yVOhbY9sU^/PYE+ƐjN8Smd;bE!fk/(`'M\>5Amp( ڣ`*,A]t[xco*Z=n/ {SeR Ɓ45}εar5XyF5My{h{@#(x%xi6!eݾR0\BzC^vko׼[X~=zD k?"pW~T'X1s4g`)N:yeVT!Ϭ\I-~)K< x*ɷ--|?;E1vFJԏ4)TR,Uyap}m9憺먣:e駟YZZ<r]oǫƦ۰,;Ư0Ǥ[%͆NW$gh`k(]@Y+= -X\N_ޝmpg=w|? % n!t\kJYpaqƻ|;%MXL*GPp^ 0柈mTۥE ^ش2C`8'E &r_jE( VA's6]])*x\'4cN8L04p%B JD lA<ϔ9 O12-ސ擆E2EkOz9=.a fGN7Hjܺ atU:(1ދ)} y/P p.>^J$c'-NBGaس4*c6~@ я5oxѲ`Py$0&, zo5u&fLx$a\>Պ/ډ=wbx<<˝\xϻ /zASN9WR~zD3-4S?Sz~~%\ xVKX|<`P:أ|[+]u)&x#SAC~!LtNAcLrE7+c:;g;0u[ae|cՈr,ۏo ;PvL+ TjҀcQ8hӦC7mzs}7_2va<랬:U/w51e;N}c SKnψ TǞ}|inyi; RTssro+upT˓l'(XLybի0I$+S !˳,]ط㤹;%~Qѵjn@Ȇ(@ﱢDr>l≧FLOJ|EV' )/TՖ9!qDjO\)؄9ylȦv8Uj1ꌪv^̩T&(WjJX>zA%XzOz,C&ao5?*\e逖V5 Lhh I=p` wTE7);Ol9Ol޴%n[V^QfnT/~S3ZL~}Vq1^k6#O =Kmu{dth>RRq8i(91b砕JѤ9~Zai2XC T$U(&R&`e Foy!1&džg6B"WɊm84#Rɾy@gcey57PMO !gƉs֒&kQ/N$((@+l\UlҖ3}_+Š_M%v`uB#h0۷Ay(us=!L0 ׻}n4ow/x۶/n87:~Uo^ '6#j صZڎ Z(k2~A/Kc3|cNn:ꨣ:~qN7l[ێhXFN*~l,Q4b$qY5meY-(FR]PnWްa<-_`N$3+% (49>/A\: ;1~ 3J)^_ɸ^+pSj~9SymVJA*Y ׏0bboj%ЙRQ53.pB 9tUHt~8IS Y2l^2 #D);b \mofM}"j]GI*([vL!F\E3e{w1|rh(9T"o9Dvس~{W~ &hhRɝ*/:,W@xX:d9"+s ȷyKj;p'W'孟>ꨣɯw (˵_3!F0ep,uf6DDž{DoPd@0 {:i'GCo#0Q#8LL'ډ&xGC+ͦs/W~p':~×_ܵߨK&b[3PRhp cjt?-3ߪ[:ꨣ8ⷾ){О(Z&&EP &.%`%1 HzkjXCEnַc?zO^/rS*Yit8 (6 \fpL׈0׎ɠBeJAHnlf[ڃ)A`PཇVK H_oυBV5, ;mp4_IMfz0K ټ #X )qA.H9Bq-@ bFW^ ѯ@@TgFn#p}M^/`# Tvns-ɹ6䶃(knp̒%) GJx5:'ջmU׾CBtǎD& +qTuhy8[Rpa fh)BX 8mG٭#0'.'ՑlyC1d dn8췿s+}z\BQ3zStћX%J؃~ EC-N3ÂX\EW~$8cNBbG98 [Tux*Qht8 QK%mhiak-ȃ%j._FdƄuZ2ql8@xBBB-T[xBkQ,j2}xbC>ĠoӰP995> NqeH:ȪLRoZWZng.m? _NZus[WYX:?!V2{=”OIDhTmX;c:ؓz9v>~*I[n)p<3v8&--0.\#Ë_^'V:?*:vrǽwǷ)'w%/druװa . p%ܻgow؝ypgჭBRlX@@Lk 8ieeK;qׯkzUlmTs+ZG44 ǘ fR!֘Zd8+ƅm~J}plΡDPIW.@X/m^wV3(K8ᝑT& 2,'>P] 3Qae1,bMI4lr"&j"מ!vcqs,yGjԬdRx_P, aABtd >y&9ىUGOzW?_1io܆q;bRvO~ѥ 7ʎYY=j 򣨂P˻"n"^ b21xٶrS*+gOsz3sϻ}<z8g|7zJ+ߦOOL;fNЊ7=,8iK)+}z@}.M&)QEPXY ƔMCf;AiO2vi̷<"AؾHATF)cs#(0VV'IpDBM.b5OZSjZ&N]XTA-MUfJ @e2l)T IƶW7~ϱw[?tuᏻ)[X9F)k9c0A1Y 6@ Y}~sU;ngݫQG{jplH8"f6Պv4\V?nCWl[uQG{z؇uţTXL C[T:_zAk.+ʰN/bA^]0ο/ӊjrowI͘Cu>zGc?Gown:>4g$Dv55 }H]Đu7',E)rn~U05`|V'ov߆N֪^CJ߉zvm]E9%By(dTG@j֓r(&Q!`$DfNegh6KP&N- X-eD*=zy ?&Ƭ =y νἎѫ!@-FwH [҃%c.L` C9Bu JJsH^~,˱ \x%!NR;ROw/>{ eصYP) hz$0_STjs`ڡReV"<_1f>JZi9E9/+SI#;*4"BO0K22k˴96T¾OX|s듎y]x5eY/u WU bKj  %hn(L }c;K)'sP%ˑt T 1rSe6o &n7TRRƐ&&Zo9y-I 7G/Y2X EC L3I2L:/# @Nq+uHh</rPx/ (4jH!c ZN-W?=s( [I P疶 &Mݔ/nS> TPI1{KeK7`:×=V.Iإƨr_g%\bjވShǪMm;f{ uQGuԱ'oǞu%MkX3NCگ$6YxXՀ89h >%ш2Gh]=͟~zYՇ.ߝ5O~N6FZy&Gl۔~H?5L_7&%Xj"TmƱ%-!rIErΙM V]W6aC kTN'%:̗@}{Tdˀ1I+yA^#U6_aHir Ӄ9Q+O oث٭elh3$j/%wZ R7Y$QA+(YB^H.b4~%.)$a%!4"3!+D{x6p@8@W”V(hxd٬ql&0_)nԬ}+A?Z ՘-s}ޣjF#!NK~&V L=Tꋹ E*}yG)6} 7飞zo|[Y;jg0t1UKBUBe\{/JkRY{>hi͕ћt w?c'% iQa} 3S{N/JWy0E/{(gUʄEL~r`$"TVΝ \U6#U dD@;@lPEDeqZ>V18TД~0GxY@)7Iqܛ̱qBENE@1 >S]t]N_٬Owˠv.CYЏ.@qvֳ&ѱsK`#Prl|}gi7fhy PMNrXfן^u1/;n{:cOD7Km2/2IOilb))T“¯)Ղc$mbf@J`Ûb*;]2{V+r{5~O3N?c:S\u~uI_}/\2S֪~]d:&xG2|u" kz[T97k$0)> H^\iPyG8H~/tE\A#|sh΢\2r1oQXD(x[`&AP SL乲LTJBJ+Rc4,| e p%rJr X)K{@Y~EDEO~P2T?z<$RbaLY`"[("0Z$у8f*]Ï?_^9X/$e+N*?YQw$#lDKx4~3 9L|=e_0by?X0fQ , ingFu6tՍWv@.(EcZJׁeLLa۶%uKǡyfj2xqnbeZ2.Nb95.VRd~='8w`"Tuzh2mRt^*]i\i-#9DcyK(_쁩~,tbqL?ŖN"m [-f,c~`{ yu~Ǧ3dٷ &&J=؃֡ˈ_쩣:-M;`Z0tH}Xn*ˈƈʼncpcϺ먣:ؓ⬳60S߻jz{Pڐ%MZU?ӚQ7zKvs"\!uL5.~f ~C77T/cwݫu=;"&b/ eDJ=\A5`lX=媸 M20z݆j[2}[\u\ LGY!x$^ 6*30JF)-3V]o&T[t@e0G`ce~@4Jʁ" SBG$AlX6%`"V%$8MX"*~*N >01AxpԐg18JI=.9A.W1^(Ivwؒɽws/(%kD<ڷ( ! f@ȶd5] b@5B9[r I6m*1&E.;2 Gy$/pi^V%|ٽ(}{"]SF-TɁ>8D75m!dyco @VcU-Us~ /=>SEr헔dSɍ<ɵgkĠ%H&G;E聂@GDyG7$5t0 J 9PCy"}%K0/rx[nq~:^7ѯ jP`  +6Ѳ@II˼*XQ<~/uh㤉uUDѶYڇ-WR}JYZ(`N M@1vA#&uaAJ 2f冈OivwOfC/>ڄ 2.%' ~9먣:oxu[;zxVSAnMz6K?Q@R_<ʃ>n1r:zMzbdμ^w uՒ|^rgxn9l;c-VC.1!k/^VKzҌtQRR.VAb@elw)gX/)K=pKv/%U P͔JeP˳~Z}ko0(T ʄ{U8(Z{|ʬh4V۫Wal*YxoIV(H3NybO;;KH:|1~k7=Աu{ݛA\{# Q8(M`GrB#(DI8t 6/WrNS^-!Ƥ^:ܙ+a2O,a9!e90KZk&R,2\0vܾc gmTRGbF5i$AOŎ%:Ib6IHtL̤$Pf ,b8~ HxDM!9 @i^;='6;ׁ~1:E@R۲r@jc3 !@A_H38J|w /ˆO̯3|^TJF:F |3_=ꨣ/ǴF<3GY 7N␄~12a4L?o(*aoXΝvJx(XsMrtY"C ݤ4Z8'L(>¾?QH 2/VJ( ĵ2dŏ jNHl~%Y?s* =+kIU:?Bzx7c u( Wi@x`ƈR̍"s[l˼6Pi  GS #{IeOr9w)k`E. ?QC!q1'3Y'rI=d9*3xh20" j[Aꛡ00F藲|1&vbW >]68}2?A黚MN l;9cjgЄabDJ@w%E-o@ 6L~+Qi:*hAS:?&^??҃=By& h3]]>3ڳg= l{tԿڟNtQ$:&*&S˾2kuvz` 0 Q: 1]X N+q;O\y{stQG+>>шj!xZMA/3Z*8`ƤBh~hhں먣:ؓ~7LY_S*J1ůZFٛDi"L u[̦[:W up.xѢLbJX[KQ!Z8Y$Φ戂$I`|Ԟ ŒW>Jz9=ɺ~.Ks%f.j 2&7X m~C%#EF.# 3*P>Z .LpMGrdApa>FF̣HƎ:E PP||~Q\!g=DDNrredN~y,h)Ԗ@$8(^yt7MDz'@@Y-USCo {.{L`1"Dk_W/IM7E{QC|yX%RMz_ceg`j܁!gPaI=Ѷ%GKNO/IQsO5rR{r^kx`ǂ==<˥=yGWֳ:~(.v7}t ?r!#c0>JntCe&e4"&JHlB;T[``\ ϺsqIBXTG)_"%@r#V: dvAkWw^QzuQG{z<7o:)Xw?!JHv"Ȓ$:z꛶߱kx{x7m$Gj9 *P$/ 83$҃Y%Af,WO)̟T`z%x 85t{QYwܽBG:f$7qUI2t_sk vȵĽOЃ t1g), 7CE[T&rdNp;IJ)$ef{$^מCC =va ae߅PJm2M JLh?) =fnUO̮=A($l<8o2Ρ w2<x96e]4Hx׽)Zu [5eC 7+ШޢɶiJT0saO?Ϛ{oe4RC>p gcXqi%((Z 嫤4Ɩv&E+FET '0sX7 o_ai{ӟ:q'?@اگlܮyMaMal@YZNeUrK%g5UF1x*Ғl"I ދX掗$)˞_=y|_x}=3m4&㢋ӿ@7n?q>Z9,0/$ f#A("XaBA(΅ږƬw U'Ěqr0fhd1hohdDJ FiFF8@Э~B=0ej}eDƝh5ljnωF*3DڜlHhCm"uTsA4fKA2Q1h`|}!bjPf{ACh-9ׁ\zBcwUPѵg;#S=%#H!'$mV m+hۍxXƪ" kzRF 唻oe!7溷:~O|}\wZ9[M.NpCM@qB!6Uhsd5uQGuvjx],4/׮=I8X $kwkA%ѹ[f#l[SCק6\TO_;w5*&1zlRZ?'u 'dELN4FUNi+-70ʲ5+EODAUcz2#0WT'=ĺeN 2rLpC) `0LWiKL0&< ӊQQ {6!_W0Ii"f~.h=nud}VjބAQI8V׈ \"c{g]́ ªH.Xrl0=57,%Q C#3\mdsĤ) x~$rlU}\Wl={V}*Az]՘c*HlINcAtQPΔQCJr(jG*kNM̬h݆ 3d࿜ IK;xhsw9[WՃzVP#.|Å. #<{^y;XXtd>(iYMJjDb]~W'S%o)$XCl2AO֡Lɬ ,C&kGu@d!31цŅLN¥<Cn&\ތ"Ià ӣqkuryCP ں  N~Ì g\f;ПBoK,HRO3^wH5~n_!#A1l!Vz??ﴘ*0>:tm m,zGe,!o[ŀb=%9[= BaW P `06 &K &DP*o־v_NfUuoխ$ovtWW9(#G".qftR,(/Λ6`7K"2 Dhl-|y~H6e^8c1E)R<Ϻz˻67ou=Nb$˔M܊ijdR~rtճG$dgP`~F+|ϟGs:AN~c7lN􌥙v1;+9C)Ndf2-cY˵5:ZOէRd,mb<‰c+]XUѪƴ&VL@mK/r34+gPL.@D01&U4kKJ Nm[C%G]~BETj_,C-_T(.z.e$Ok,rMe'Hr)3hA ʼGA*BĜ'eE.f gעب2 ]C`yg"@L0![/ӞN{f˥MĪl$Y:*9^-֧sxU>D‹uTjJt(V}^`pr^dCp]X2jCf.Z͒&OG+h 6RmɱG(_33 CgX6~ȃUof|΃N5*#1yhN n1`do@:sK.ʧ>ӓ.E>= w=M:Om^S'|wLr;^F"YmSH"EGbx≭܅vִ܌wMfT1nOM|G6; ^l[x%.gK]}bh6f9CV1Nf `YeU8VE'qb*DǨ`"YBX@`cS{ʏybݾRʸWcG)'Y/6 4C'$ru)-*v/8Cm*7u$gz62^iU%(0*%)\,ta' =A$挂/X svIYR mDis9iD,%=cVRR g]=6g1zPy6r>)77WϭB b7;uX_jꩯ`ϵ -]{e7%.OBXH,h`Gؗch&`S8Rm/8Ϟ@U$~gU{jOMm2z ,Ù@ bi2i(tYpZQထĎ!4ԨN̙n-_[؃&@bCfe,Y҃z?dr:;6%&ȠZEygii,`bf}X3j]]Йmt Zܙ7Ơ[`?n55s1E#sGؤ1]4^moЏGKp2!ɰ2gy8 vبyTn2k**b{@)\λWB+Ζ~-,7K""Tg(E1}^! |:fcZ Rv Zϡ??uglI"E)qY^ǼCzUsp,Kr{9{ dX޻.6E0 m -3@F%}pf8+X*b巾.U ZPT% (\1 puB4.Rc8 r.AaDq T=;ׁ\g39,u~D% L!k{CHK]ݞt-I)DII*D I ^j&WV7^.O(,.$ރȘ2[(XW9x`|Y_j2/n(Qfթ)Z` Wk/7g9SJKsx\]-2p&Gi+je>Fx^wmU;N~*s?3멉񮻶m\()v yQFvv (ZϞ @,gZ

@Arttn}\י5:: Ӱcho}u$l1 ۪;Fah 횁w5_pY6%C,As VX.&7O3ne m;[A8}xH0FcqG* fz)"0{o~HO)Rǹ}vt9L?@yo7i$;ElHW|iVOQ~l+l. ~=B@o kчEIBrQCPO'gi|\0C n`T= 3 תPd[y!iW?HA7UJz XeP9@Cߏ?J׌hMA$ !`߇ ri%x> :9z"'J6ʞd+ՓKwj to#ƮO7J!"3e$q;Z e,7?evoB[*Ii_%`{zl-7<R" Yq%X+``@<%gE)U0A<|s2*.3 ͕Cz0\ ۧ%5?o7WjS<ԑHp?c;o~ֶmadd k4{=uA l!l̲̀#p#CV^K:2 ,FPTS)0{UݹsjK=XPWn b 1r,(fY0W:N 6bBQ=G5guC)6A:vȎ)YFXڵmƶNĖ 綎Ck8; w'6fF!Nލ31}&0Ycd1cꋛPC@Ob{{r^ϼ\x<w-p}6rx9gI"E*.䳽^UGKi_=8:c>lRg]MX+cQsOMUmeǾeݺ#'RͧH"EGrva kK1/d;I|7:"6=$nwPMI``cG`X#jdaF6βvMտvG<\nW^_]w@p\-D^6<  t2RO[k<@JCIg<}+Zɖ{L~wcrFypi`&ʂ;O B +j_͍]$؄!x̅c5PXs&˜R.ꂃH,di||%G"D W@8YbWtޔ`J.25@_XM{^D6AA]L8 _ON+R{$&t U-E;w z%{@`G-,\{P{Zǒj{:9z4wH<+_k<7yX`9  m.y?ls*:Ὤ)qlfMJsݝ̋uǾNJ|E X?}˶o[ۺd`P>YTOnnk0 'F|akPl 1ٟg[,F-75L )қyy6$ Ze<FZZ-[ָ;ƏikCr @?!bb7H_7F fg[X0 .iDKep Q=[9=:NuD=d_kssmY0 ̭CPvrU-Ϫbj}`ig$49*|^z6 Șt)38os]p/ νr`~1`N eԤVA=&1jή:֯т@t\i@2ꁨL7O< IO)R<Qǎ,??pд-3D{y6OKBy!9yn>'rT)RHs~}?ejJ0:Smd H6IA#Q&,aM;cmgnkٸeLؽ7kMw_{:K ^:d:Jff7F\Dȼb3[8!$Xf.umz:cCϼO: X8Ϝ6^56ӆzx1⺴TL^80']f>n6peB*@nRU=~3{}4iTJ6SW<#6 ,T1Y3{&TBVQ "z4-`?Oxd 0@ x/Ism*`09!T(Af6ٖ,sL DVh A8 GuO{p0hW޲=H~/dEee>4"ds2- G"8SUI Ź$B,[+ =ftSP~  SH(繖TXD+%PP_cVD";xz^ &s(U ܇t=Yc#s8x?yyoԲ]FzD7z/G>R틵/UfeSsk7ow 5 WO.i$O* ;̳(8]NJ [ dHNP-J>U:I@..{V<{}_Y%Kvul@("yNbU5vFn{j!B:`"k; / ae%y(i_ٙ6̵at߸ˁOLd㧗74;KسLI֘x 03"2.&Do"v 1}n+k̷<莜똘=1D&ԡr%Ž7$&ԡ]uẺ'\){^gsգ?V e]v\:8S xz)RHQu;Q"0{n@BB[vDm0?`aN:~h͟|/> ϻW/Yg ӗ"IsɒB9Ib2O@@2u'a4)ǔ2膍׼4:*?|L8@:@@oن5e۳Ft$,NKƑ1=1,@lcxG(idt߬XuyІƨt&%&~ Hr9H4dDJEIB8A"81t@p,*/+myOp#A,V4?CGkÔZ йg8}~玠Ee"81u$$e}Nz&)>XFy* q0Iu1L~/3U흁jGNĸZl*_2L II /UkI-XT.|0GJ!@XY gHXRa aP3xo?ǮN? EO$QG% mv`hl<,lF<@!"p-M=7g>7~6$^= :to -3ێt @D]TNiy(,[V5CO<3g׍ϊZ0e֠??hDvNNug~`~.i{:Oѯefg;bLM`, oGwA7Q,|)PLR2*>m0bx$[4jx_4iM˜ #ݿ }yOUoDHxL$I4k$x?$<\+d|]ǒn=mAw=i[)L\rgU/Z=Mj^spR3Z K OF٣e[7[wx)RHh?|~۝#[n+R]O&y(ЖaYirD²'e5E:%.4ؙ,?w衇;|ꫮ9]W/$*g*(X  EH?y )YΝ2҈LX2(E&[Wg@7J&`hP"2l#1$t$>=1 bJVdpb`2!쀓ol D*(׀00/\/[Vļ.-&1Kl'ecyYƢRF:`P%CǤ zR 5$Ɖr E39ı2 WiS,~a{SH"EG[\x_}m;6}>tzq3ݸZz#w/ڶ6!P \sj'w+zP7ƀ)}:k__~__-9=jZ drX!6~0`ew 08!'Wu&Q L&,:pLBi/y_!{Llfuن{Z5]RtRĆF);P坢ܠ{3] qJW7l[%Qyx@nef|Ε&'$%Q$()C(seۄp/>D hR-tI9? Bgt {Ҵ/+hsGyw+C4zW`Uoﺀ2y"1,V - ϓBD; R1&QQq{cAiYz9]WwхoR 3!~ƍ_^PFcbRf#ȝlpIlMjLl8R|rb, Xvh'FͰkNSFS*;kyjj~;LLLš5+:Wwy_x_d~}H:jv޺'Ph);^ 4{UF<9 [æ ð]p1QHM0yƶMh6YjTܽкcm[>HtclN 4 pza:y |Xx{]| O(\_@!QII 0D,'>5G 4geu{;'EQ\/A?=ZA jSV 1mz^meCx 9m%F;%g,@G{m)RHhmw]cwgޥ̣g](B(kVȼYa (fIj 3S%̏!/[nߝ߹cft%7nXݗխJQ֛}cJ 4ѿxS0v pFRFBxOg|?4Tuc0e.vfU.F's&7^a[R' yxχl@"Sl"07d:g80Gzꉘ T/!C0*> O6EWrߎ93V}_eSqߜVD`$;VcYWlk~kVφ gT_j)Rx q饟=Ϫ KiaX=\z#=gaTiDz 8xD* &RH"ţ6N9唡]#oxϿY >2I=hъBKe0aD񠘱x&7,af j UkU{wkaw8WL*I&͖ R̷=gDYZMK.dKYByJF?;%8¼3h۪r&U"1b0/&%h⩓I)d?{W&Uˬ^udc$SՋ #Ca=@ͮ?b` -:C,}hvoɟEC&PaS ʓ:E~q$[lC៏XL/r4؝s*_4){LH),C઀مYv1ˡ?a.)~=g<_b5[m~v=685݆18i2Ѫs8h LWs!_0ĩDY`[鹫=tz np!/ZtǾ3͕W0\^6??rz:%Lϴ`rrz~MTz-LfhI♜IvD9;3|V?΢̲ ,b CQ#lY< WS4Yx{'FCh9Qpe' *t ؉'Mb2@{ rܻ"h1$訿(F@/ hF2wZă71B:g;aK?2ruYuG=^)Rqõ??B}Z5/ Ќ^=4= C3yJ!-oɇ2xAossq)RHO}ͷ_;MWVKEn6 ? GxF`^y.5-CCc6sD1ћ]{ʴ Qma1 .$QAH(wT.UuMI)ydbKܱ|:LjaLHA8=cAxVO[Ѓ^ղ2fH1$`e}I9)x IȘܠRSHb bJ! 7+' OT}_K` l)ΰ!CΕ`iU+bY4t[/(sל1BEʁa,vOHzY$'L[%5#"OYo%n;Lpƫtu\) [,(hy 5򪘙[ˆx&=Ms <3RA{w\(!6yʵ ϖCN87|ߡIuW$9}ƱwzLM6MGF]sd<_:\S=fuW%tJ133-o;ǚ9s7/EL3^22s v( C,X2j{FaÆ!-a?_qwzYȅƑfsECmZ6mw4vɡT>44Z 45 c055 S07ת~7;:W쇫\bjtԨS\{YcpYdr^  ϫꮭzT hTo ) +>2exw&ehy vX+ow\0TX\R2n^}~3=RHq_=z!,,ݶ>>#0L /ˡ-qԳl[)Rx}]cd+w7`#xw󅝑Inb%3_*Cp; dRR\0%:mG ?DqƝ`m%I\}6[} "*CYEz~p[VG@'6Dl9>W,_fePIw)(Dh|);ʌG#_DUV䊗??ZH@)ԣ^vۭ7;[[c^۶Qn9?y50;=}}[׵s%L[ l8 wޱ0ff4eJe# 2ztƯ*,gQNZ =ĉD-@9;%K2X_Xt+N?]M<)ro{?1=)lߵO,y{a;w {om* X tq^C:4^9xuos%'}'XҞ=rl$G)Đϰ#ED qgɽew ,DBĞ 07{)o^((;3j~w^iO>zRHq?K/-{e̴9<,j6ArȰO*nYkWDٲNUwO8-;r4|)Rxv C?zq&KpS_DӬ#U ]r$싪MoH3z=vq#|3ox v!7޲G}y&uYbp &^QMfrq󐡁c(RDbm19DJYQ5cf1Prdb/'>niE$$YXoRNQQ 3z# jCڇ!YL.0 c#r :q!0%2'mT^; x"H#0H׮o}a>3~b_E ݾN`adMsrQUD{y/PF\Z)4M?%qq2| o"5اi_܉,m+`cm˽vCrtL8tU}z\b:L*?< ٌltJ5pg~'tʶ:x8D=Dž^tE??j|ֳ:kWy[^?sgf/t2n ر VxGy̻7MLxЖjC 1[7/lNPҬ 'u6>W-kzfdzzUWOڢlr*P^dQgz{:۶š5zձ/7NNzE_~n+O~}=n[~oM*4Ķ13^h~@ /]#ߟ י5OW^em-M;%cD2hy1ˡ.(}(cX`ԻbѾ6j7k}4bru$g:u+;Lwyjذ鉕"E:CeCih؁B 40~x }wxnHwؕ'|]SH"Joom$:@"A%tV =KŎH 2-F#ҷ<juKO;-1/Wd㽋!IVO- "|tãHtUm@d@y*ƚP䁀C[R#\``\G'0>_1;N,6#齢#r~$FLGP=oηݩENp`O'N_w毾zBsUEOJ:d71KAN^; ܦQ#V zu12y sJ\WgC0m-q}Ie!)m*pbFS 'duX̕1ox;V.Nۍ}$cPe>gŠNdWƒM8o}\|N>;*EMZQ,7( AV/E880o[`0hb:o4j)RH7>rL]jOM&U,B(hkl75lY*&#on И(aaƂ4XY6 O3@ԓOw\7gzPHe1$% 2phl=DP+ W;~I ƀ 3^, UQ~ KG+T&OL5r,N`CAւN6q2M8wҳKJ|S %QTY݆H\Hƴ2d~Zڍ.ܚ 5\50)`rY7r81^.֏TH4 Jw;7uƎ< `0bm"d;^Y$3&2@BUWN{"qՋ^Qgl/1GGrByK z>V/ҌߞX3>Y^0CBX&< Nq׽my?~m~V>avvSrMC٬A&&`vv֬ob>s~W~uv~'9583d<#5. ` hf#?۟A$LBGOjhAm܏D`:}y2<ޜQPH-|;5%x:ԥA@ v/cIYtƽ;낎Wu-[z`||]1=)R4 w>WKhɵZMfx(Jj %$ H;4O.~j=E)RO}Ck(}lh0# 4sVFF*otlxflw[yޜs9WVkѧlĊ:/M~]h~V.VLϭWk5k|g]{7+=74T幕O14G:zY AtE!>C쒂#\w7ϣ$[Re"dJy$6pS1!=s0PfR#j%aU}Pŝo3Xu XIu١ `f"E&T9u7Eh蓧VF׭/$S>zFRz+O~ݙZO"E֋{ f`2` O#lSsCT0Q9`bv8CR;Yl.o55OzH JPJ˶Yy6=hm#ێOd\#P*:/tn0x׾?}~)x)RCpn:@7la^(ü @ $)&k@le,4oߧܩz=_{U\_os38"u+6SO^[(j",صINBŒUQopY&ܵI n`YhDw2Fl?>` Ȱ.yNIq}|`|(Ǟ{4)D^' :E:N+]EL 5?cжgWxd10&o[%c#"rFc;S}Jn'{<ȸÄ=N:Io6gyrz3ςE48Yu{Fꉊ_3tk'K}'zwi\F.Mg#CzIIIS(٦?w:\k={~x 'V79Tq@GJ>ַfIJ́75>jٽf!R!Ls|X8`jwݘnWGl :vJֽ!O^Tl[ÖSW?O > -]LY*w,xx]ɷ3:ozjH p U#R9.:XGKela)C9=צi+'1"?pٻǩSH"rn}/}!N2ce,i^ҬEv#4,*2XT>' 6@T܏Ǥj$I[ť"+J'q^&<e^.ɬ(3˅!AH2P|䳢<ӥ,Ul1}"yI WY:kG Iq\i fKˈ=|J85|hQ3-:x@86{mvS\l$3G dlԦ y!;\MTHڎ1E}*f[ |=Ukv͉'!z)Rx8ǧ?}s>O,]}uJ'-ye1 cnJd|A07}etAȒ:pr=8^v÷ħ>q9+e/00P5r@fGb:~pfAb? 5R-č8 ŋdy">6֍]3Cyp ?kvqc0F` $t\-PGw a9)RȰAF xP{H8ڦR_y~x<#DׂrL30Cvp,@u弡XBĀvA ".BϤ@ ˎ>, ~1 ."{+|PnbfVT]L26*.$j y$say D??z]-֫}y2?܈dFF<5&&$"{I$-XBA"QLSl<'_8yy#gI56k+m2مU=o{ӎԚx$DJZxG?}s{Li 'ء˘K5nvRvH|u{{p/d1<_֘4+)ۘ} iy tTQE`TSNNO\x[ՀdlFPݶ]n}jh<~"|oYSC%^4zrv{L'^B0F̧CpIP;b4N9V}-cg-ZExo||nx*Heƴ[5UQ@ \bXpFBP2J"?d7oZ%iK^%v2B UG` b~/@ϧ,*L$7yp)rb8P*AD P< cl(?Ʉ(|+%řXpMM1y Op -?>'6!xi1LBZciJAPx);6N-fMt\U].ŷ0yTC@,ԫT-cŗseF6Xp?2^Xj!VJڰ|oϥ<#)'ţ&wM|uq40JHM [İVPZ7M5AK$srl`xtLka'*~HF<֑#e(\@ I)qYX8r2s?;ޗ~|cX_2:OHqP$bn|GCRt;w.v{YRH} iPke]/kC=,yDRWkWrGm{\懩SH"5>ɏ.]<תqG[&_/ _;>O>yzKUh̀an._i0Epdҳg9zz{ӷ_O|rL_/sj62IqG|dʄv;!@"!6\7c9kHz3gXq:}OcIt1$ :qDp^L܊(@;!]MKg,ǁ+`Ȳ[D2dx_!VI>+oϏ,9p%헀!d6x B@'fQ ht,`|[9oN΍&^ZeRy!KGk^S[vO$g,Jq::]1y_M`u,0}3PiiH4ΪUޗ02#sb6keP/Oku`A}/Ձjk[GEPi.'`2WL7icYzˣ%ܶ϶I˞z%'#1ГQ|_}%1x(o,UN bfpttAi5l>5lHJ*B2qλ$~gPHdH!Kvh_xR Xwxɣv,Ɏ'qH- :V!HD4g: ™(s {Æ%"=)R K>n:V7iLxm|@%x3bd|RCttX;\IyK)RHp??uΖ_5U[VxCЛXcV8򕣿tӻ/~ӟx҃A;=Wqf{ '@2M4#:U9;~8i,P~JဦgYcT4omcwz-=Y>gjw8]y ɡ3 ۆGױ=?4 !LͳD7 ȱja2zQJOqJy B@cNҎ?#1+*bCbx,<{h4VxVyVV%;FFaԣl*Q:ݫV.ؤ5v=y|zmV!0T򘤴i#ڱ=}5Ձ_S{$H@OG||wv&&ZY,6yn3ӟKvDh3˳fC29󞑌py ?!S~ ʴuX)DFjwtPgp@qy:՟nh]QBd{ܴVCHo;BpJuQA gMѯ<_)RSUneـdШkG{T2n\R)j3;FSRm{Pr$ۖ"E)_|\olei亴n텠:UYn^z]x׮>`׼u] ඿A̘:2mSLIXVO?3|oax^W'qI3$& LAV'JN&Q?1W4['/ye9p |; j}EN'ru5ģ9~byYٵaE\~ Pʵe7@*(>]`BQ1R"!f?0Alp]^myF0_wy n)0;%JLNt- 5[YPRyu0A M5)BO0BȄ߇p<aXUc A!$sHx .vG%ۂ[4 0G17tǥHbwwH{E:;.qjI(l/_]AE,6qsy`ۙ*`8F&|\{ ys]79W8=)qK ,vA`r bS^ʁA>2 'dLx@`k1zMw:#:L_ɒRQrA'佔Ósnj|~o B198F0ȯ YN旘VL w3zx X ˵OsV2N~'Dk53vo9{Mт  %K{tE2FI8FtX.DudJ)rG zu=i%s$Jiy zSN8qgjS<#;ޑj!#:~>wΘ&,'Y 25\G@ʘԛf;"Х`,+:xFKڡ&xX9Q]6}\%;N-X'n3KWN!6JN }艽vT/#5^?Ёp|ߎ>6pBg~)RKm;m5!ϭlo`,Џc' &a ARi Y-A 9S^)qͨʐIEǞ?N-u3puFc>Vn֍];49WRHht{lJ{k:}O W.;g dRH">N:M'Wn~Oeyzw~'LfzbD~ۆ7ɯu_oA>g]w}όM]?^KNeEfUb`^>NpU9M İ @1 ?k\/o КF7Oo_7V_qe^4L7U櫕:b saqFG8RFM˸y~< 6"^,"!TAd7&RU˪>AQ[y%dB*1naː#;_n# IB_+V2VTHcƽe*HBt- `džrqCo=ok ~^Nxf}}B 1qxDL>2tT8FΪy< i6ቃ8Ey rɠUZ #u[vI Fg׬]Q Ɣ"3}2͌%]Z/Hm)vر~O8Ħ1L=`@JZ{3 6bI3L2=^.GE~;/, `; G`qǮ5[a^E&+-Gl6OV;nOI6`3RTcNudDĆGOU6y$ݖ"E0E=&T ݖ.dƳ݀gOpmoI5"E)vx;s9'we; 6O?zCu w÷WgIJ&J)k| $ܬex9.XYffHxg 4#۫s ϧ?w/إg0yw"I.ƀc-?i2X$`$Ε@!Q` ,0̂\:v$sC_[0e7LCJ sNzCxksk20P,*C2Vv$6RVda"ߞ S_ 9$$z*trp&F.hE[Ke6@w5; D tV @PVۖY?$;jrn@lp_Ydo 6CBro@:uD.}P,[O<\AG- H vfϮӗ"E |վVk rh3`pLZ63BJ8O<b08Ϟpkj=E)R_}wY5vg]R0s*r0SVnm۶w_q7wqj+o}xF'E3u$'ʃ$[H[.ӞPBWƇݣ?Z`gz!;5i*v{ sjYd^$TyF2yʸ:>H |"MMB`37?[!wR qOm׉'6.A;>.hl&y7'fjZhsu{,Sڏ ^4}h(ihص@/o"y,?kKXՓX1tTbZKy7\n,^R191FX^;QYzW ^`.dσ^:~"7\o#6XǪC[ 'G3rIŀFRҀFM9!aH+hC q3s3w$~rnuvXT=k2w &@@_X +O[;.5ENB$ݖb-[N(R3bCbͦ'W;m'֙=:jv&oz7ÿ/{WΎuCv `8o8전rX -eD`Lg->=Kwo1mǦ+k <[2g=]:1 p@Pe@tVdy;dX%}U`p@J$qĎE?z=)oH ݰ踞M1+ Z[$x.sJNI ~. x<4\$QIy1Sr#v DK`=s+_=h?x(/G5J7e%g#$:ȃJ@d{Gyf\YySٰ7{cdy`g2nfMG+Yn@ms? ]guQYhcV}*5r86#ȩ7;#DٓFϪ7ax&O%MS{5'yoIpj$FO]:~dɻyʭaCb,7d&&ߍ{q|o,음_Kuh 8ۇ#a&:.:hiC,ƙ>χA΄g%ΰPH`йexnu\s?dLS<~}H/HtL_a-RO`{Ӏ3fܞd#@`˔c%ʰnw$fF&l%Ψ!Z9!n򇊤Wiy-pmjK cYR3֙X/n5ɶ\Sr;6: 60m2 Tpq 7تK"rk9l2J;Q (hv a>,X SH"ER\5tWd8B9VO9-i_PQ]{|O_W]d߭n~楾Td%c馒 +>!~6:)?}Y .҇C>tpkXMIV'CŲ {20w^b'0H"02!DL%U.yUL.\}c1%Tt=j4?$Et\]̽#`dH/x^De7k ڠƺSqJ8#t)ܰWSx>zJ!4Z\J7I΁GO~#Km!ГbgqD9}RX&1ar)OM Q hY^%`ɃK^x>46gc:d֏:H ׷~8u\380X3 p1c:TNqoWs yr6 Sb99A#yH@l \ eef)GWOaBnzF϶''祧/E& ˋ< H8E5e)bD?[VPIL 8G/։SH" 5ϟ~]O'v4ncXVO%+x€0E@>Gogׇi7pd%Oq?=.!p/L˛.I,$m\(O Fh0._2 /(wwkC@cCڝ3$Kb׆BI@ˎencߟ Dcw.Y,JShH7um`̥8αha<8R^)`>9]9D( WXY½<²z=zУk-\6#"PĂ] iQ Ql(I!S ]@-73+X{,߄ ? Dʀ[nVC jx*lxWe=*`G|W#QV(zT5 ˗m/# ^k~/RbwecrƓ+1M@[%գ5!z5Îu GRdX۔ac.";6M;%2ؾfbs&'fp;p{װ:%PCIv\ ,0kjxm_.Dă[MOIO_)\e(ٞUZ/V0}D _$צ/˳?W;edI^.J3 [l.mee@/cuقޛYo8uOAsC`a5`* 2? L$nHvB vJe C=@2q '%D`]qrCRn)-*ןڙZCt^(1HpUbH"@A&A>hTrƱŔWRe2$ix7#p)>bd?L^ 98X> l*<3S: \NꚄ '@|>u6҄ٮfT1)&Lx2^ ߸dK6"`z яgQ1uVǼkYw=W m2+1* Kx/7~/EMEzR첱Oa=~4$f G ~!&" y v^< d ɐQJuX!& r9"9c^8&倞DTTrR$\,"]^bT{ՙ7MħŦ~p;=})Rp/G7IV+|͍[C0|>bJ17=/dLh O"EK|ßEP)*&Δb ;dL#3fѲ^r/|Gu.\8sKNLkq|rR*xvmFٶ2 ".Q̮YxAO! ;ȹ ϵn~x'ky+LKŒh.$tYҫ*ۄ6.bFC`âsBxP)D|W[˗&E|lUŃOY^ʻe^B-ρϮ+%/g;` n} U%1O-XBDw SG 0ExV5SA攈ͼP$ve@8=G]_s7KQ5_$\rh\>DFTR,}!w$gOdy4Js%`(Xy7T tR1 j^_APu#;QAω$椀N k~Wm .ڞZc$'.Sf|G>O5ϯR bw%35uʕYZ@CUHt 10nuWkNifzD>5Crgɥe l!Z$;8oH:.4k{Ԑ-? $7cu(YԻ޳;t<kV\#DTvl^6كYOϼ˹YLO`)Tgbo#&Q;pȳ0 )ȅ*؃ ]I"E)v~ ^s%M8> xP /5X4a]{|㣟'̲vG^1zjt 01h.&Xe<3cKV3Hhdxfl o%Kϵ^cߛO9qô~B=|>Q[X#w$e(1%cElĐ! 0J(9  pHDN޷FHk$ k>F +/3QEn" b<go +ʮTCbKEȠ^1v0ARP240,OjtrFTG ظ!;EElx=^ȑ5OX&V4QCգQD1ZZ֓KT(=@&/򷫈3crq49bl4-{*'h2yΔ0V???Z{ Rў8D=hΚ6f2gfo F,=h46\ɑCrfOUo (!47 đ=-9xFC:?"tsY59Z O>I aT0Zaե{QU'DCu6v(ZvÙJRH|†Q\ B;fӥmʀ?‚8nP,a*6tVmf?J5"E)vx;_7ƿ ?5~hˌWQ؆(I ECC'5_o'v'̈0auE)]dzL#d2-X<C!F_o=헽/?\ҋ/}GѯahFnvuL+aJ"2WpSzSn]'gȭ;)N R$<0{ؒXAg -}޳6B@\Qy6Pgk1ڤ2$gc;c` ޗY (1(T @ #.G*-|h ,[X4#DIw99*&Bg$L%5Ƣq9yl;%Ws&+#]i$B+e7gS;mB7OΘI]@ 8k9c٫A)ƒ|Ry\sIjd c=Ȳ %2H*vSѪR(F }ghN)Rح]WY;Ke[H_ʎthyl_`9$ϊM{Ym|E'z{z[޲_>l"@hCI+ffyf۱9NWN%zrTH?^d…SϥN^4EJWba10+w 1Rf ԵY1 3'4ndrcfd24N1xyy,lf;@``Xbx/бש:4HWG9T"jl @ɱ8(6|i x0Ž5;< L9BH$}eE aQ`(ck%/**$gs! JsJuos Hc3zH~/bn)ՎVU1]ľ⅗J% kcR ,g lAT2,nM!5@60\kϕoZZ$f!b6|T$>Z>E&:6K~<w|?geobNEtuNf%83GpI GzvNX)'0E;TUm5g[6(L: ΚE6󮔤/!k {߾袅3SH"\Ο5s|A,:~< t?y#y*Yv: `삚7gc$EpVO|)GrсÛwHJr}fIBDfj1\xtׇ2ne7{PkԸF1K\),KW)dB֋dNe ^6,H:ࡲF@fopsdܺ$ i% nk #LIrD#8&~=7'0Eݼ *ul3;Po6[$D+hV'i*LHu:1fֺq;}?x)RH]uO&[w=PVYfEg$bb{ځ E>&Hq۶͚Jl$1 f,FUΤ d'$Iw eZ9Llx{.V.zcwr[4 K&u˰qhd2|J6)P2g0W/KQ% Q2ty)=m2_r (fLw1(1<yD`]Wy {0ۀ##(fdo}h}XI@Suq=IkDGy<+T,{i`%<9erg-X U/}勇l޾U+=6.;~w;5?#OhPꧩrFYZ w\[U(osϕ=yE+.?⿹ 'yn 8P0*iΚbf91KlyWN,b~dD>@}$ɸ>&ٳ Q$ԘIeD5ȓx=e=j ed:Sg v-nTGO2Yj)g)Gty0Ʈb⠃nKO^w\{ɦZOY<@zd8wz7=ٙ@nFL"E)R+- {tp;_I*ᄼ"$]A9EؚYeAФ,097MÎQDQ7G@2Pn6s8qiepk6tO3^={.uqC4hTFA*xètIYyH3O@Q 2hh܅Im}Z l1ʛ}ٰ`,d̽r~Yv/PP`x9( sNY#eNwm[1h&4ZnRaL86УvFI3dxA5 }pA}O%\pAԳGzR!P3KPbxF6f9k17MN78>"řȨA)# O~?"PDZ3%.7mSXfbHeOT0Pκ~)!5`ەN 9U:}X::!9UUFm*?B}VdwU'=})RQm>YP0go 1*ovBtn$&1 Eeeێ%ٶ)RH/dnwlx۲. \JU[ Pͦ9sn0 -G/\/C-OPKd  &6.\'^{ul0gdo{=7=}H\reLv*υ`vDI(/dd.<)NZ3A$<6 7ICU~ep91YUfg+84#1^~Mcy',`YFj/a (/faJ,s tcIn,Oc2n$%ܨ K1tfzG*\D%jm';۹tdușEa;9 RTPGL/\\.6Pb9A7Kj˺- sxtjv l_Ts\u/uDOL0{s% 萰ޢ3Ƙ a#PF4\C,ޣ@rPLI*d{C}éω|mں+-OmY[~|ve^+_8]Db;Wecvczn{fu k{cuwL>ݫo>'o+xU7w=IW#=)v tbcmc!ك ?ԦyhB*hĶ1 8n ɞF<$vЯbeL>>$e\0U`06<9gmZ6Ju( *x1ZpTeW. TEvuGvJ.|LH .9+.8I7h BTt=D< 'i0s|8p6O\qP;_uӠ&v_by0&Ynip<}{(뼫 SU%  [Ὓ}#D]vO"V}^&VB3fM{[{M%c_;  ܁Il "@ާDUѣl%I7lжFN=,c(HZ,wce$I8%^z98#cIVVFӖ+ѓ*EIcI3+?vs3KÕL}= }?[R0?)°z2 dd@A鎟 :T\XKƋ.fzK!$DFy e>9IɐbMvB8jc>HE4zqC"ID@H]~[`Q Y] hSR59p,蒻w,%#%<3L0]ntB&8wD4땙- S6/ `Ō$D̘ú3~wdcYTDSC5kP8%;wϠ eLtr{[ǭIB_Wc/ݸλ-pp}cƵo=ܵgmk#D@~?6f jڭLҾx4 61wڶ1P9#`Ǹ un1(,ms}Y#^ۼ#=)vژr͚x( -&+|+g'~f\:hYGZEn_}g:2v=[ {V @uYY zv68I*b%n.pr^-  P屼 0,d°\C-BT#LbgFN RG'Y,QT3ϨNK* 9GU;a(/X;l GR\\A|m$IϗݏD{JSQ=&fRرt=;l^C<鷉2+YOl4:]Or5+ eleAm1y{N_/~GN_Ci էfػc^ϼ;pmt}CC  J'S#o46kl 1kHb)*g~\5ߦ3HiX#_b~9eCC2lpB~vz_J Ghoϟ`2z)RH~d|ϼs8>aҜdPWFV\'d/UqK`6M.,[bD6Il0%6zݥ}!_9䫗\|ɓtW^~彷_ZUf;%Ť̙vI}|e {YƊ(9+[F /%y ,$p$F`3"o#YD 릝DC`&{H3;ˋ (69L ݎcBfԃ\P%fEz pdT}<8W,QB` N@ , LLpAS+ҁ\̞^.dLQD, ʩ RHUk ZB^Q;~tE 9*IpE" MnjDa= Я ߧ`lX@~b2=sA/;=m;lrqn?%r1O/eN_7?+Jm!/誇oowÿorܾvbBtY*!{'I}m3^U# ߼O=… _ IKe_3D@ p}G Tl=pz dag nkn%5p0F9eXmt|% (bYD-]>b !(s`G"G|1t<)HyuyˠС g{(ӿ%#8_rɎH( &sɡV3xk96h6EV۔٩RmT)RH"ſ/?m\|ȿn_K*$Erݶ5 ,\>00[훶.`.wSog -9a?)F@RMȡhI$|+o]k^Wg;sw xdƒ )~IRMg$L A*X)bTDb Vd5/PSº5nbM>Oȩp 'L ye,'o)J^~QOK V&z_3Boc I%SbO/=8Vtb<ߗʃ.p:Z E>O\=^BԳ a/D}y"TT;;y؉ұؘFA1Q`/suf;k$zj,pF3*"Pb$00+j3?}8U)#v#;I E)Rw\P%ۧ6=z=UPdVHe$e%6%7 6!O7y07I`81o6ر1w;þtW.gT76|<>x_>॓m m8rLAa*]K_920n;mfX@S9db:3vl/9p)!0,X,xKF!? QL6ң"H'iT5nqꕏj|)`RalkTρTRQ bT(h}6miݶe17B؞g|W غx0 ]O2dǽKց:;|oKWV,뮻nh͆~|O|%ۆ}s<2ʽoLyaֿg.BϵCl޵i%S 罣ے5[7Y?;whѢӕ IFsddyaxQ /9&sBHך[v3}@Sυ>?TxCp@`b~JlݐL2HrgMyގ1j%Ի^gx+S՘:uO/֎!6׷qm#uUw'.E8'@gn<#oY#I+k3nfJbyHW[б]`abH"EXtѢ 3 .];m5Pu* {+H>D 7+Z!2.4 +ZS=p@"JV[d?.ՃWu=⑫=9_qr-K_iV>-cl|$tt9:cr-4$ ȣg8>=$Frn80xY!6 .7*K U̙YPr LF`N qxA1l6Sk$x?>aDL+a!!+<$C rc,l,.e6$~&^x >~TܞYUc稘Zǫ,7UzCly=X6.w`xxO_nГbG;%-F#Og$ٌNIQ'; )!6g(7o=i5ØBC@݋j>l-9D c(Xj̟*Dl/l { AoY6nnq wzt*M[uy}Y=dH/%~/7nF3[dC!쭔2+hg~(l{ 8 P wz;:;Q/Пmf;+,@ gBl7&AR JHJIJ=rx:%< cҾ9x)RH+.}SKnXu"oi! 咗.^ˀCRJn,a,9Vnse=$>2xhs(M϶ۜ~Mw/⋿pe?<q%˜e80)a`[j[> 5`FypIN}H@cOr(2LրY3;:83 rm"EqHF,g 2+Y Ѫĉ Y`$Q_"U fPD%%|D9oe8@i0ppЯ6<3w793^rgbFv[O@<ɜk'itP[W≈lXT|BD?(y죊z{М1Y :$F{hЋHLPgx*v4 #,30--OW0w<3y*kHXҋ~מ}EVNxuCL׹ph} f&c50m|JId8t&fUTe(z|oeiU FG4al?Ki7`*y7[-+/A~-G6B3Qn蝘Pኢ]Ps֟ :zsO<'+oFùnlN.t%2GUd|٠W-]f{z{Ytyt>ken3:EJ9Ǿ XIf9>e䡷=l*z)RH=ߛ wrZ WOg1ŽJ2892t3d q;a 9 r%3?@ pWCyhCy5mOL?:)nyI]C!O93TLtm|:N(;%:/[7Pns  .$@:nd@-Ƞ@x9,&gSn2CJUojP?Qre 1p|0bԁ1ArqNjVcԆV!/Mo u(/r^C $x 2GPW)UWF$ޕHHRɼ5)7|u6Ȳ^m$~Ok/{Zdə~g䐔g]ucrs!&epI ҙq)oY8hw`~=@eaj> 8di{+XQ'-3p'volݰ ~k\׮ZqY9WVadlf[n'ɇv 2.af"s8ؗm˯x00{FzR첑7đGny1p4 ,8IvCN8s҇2j0K!M YRhۧ&|;Xcwx` Ixc)ckC$vB<HƎʪ7=>>&p#Aq`AXKtɻН"ƾEQezbH)W꿷5]3eY*%Ropbp)˖nyJ]rlhgf :QZzya09zSvۙea^TmG FxHP.JS>s~0sW6Qt3m5[~soYk,rP"O0O[u i\/ۢkNWoy[6.m^Ϗʋh}f̜QD:m'f I~:3E %;  nK-|)RH=ԟN>Lk.[^P,TỪ⠂K&8.3+ehKnL =|h mg˜2= ߾/wW\v;;Wݿ/?'T(-YK'ee!A f4ϲ ?(d \/fg# O{B=.Jq;РOzI+G<9|ח3lhe~k*O3M;0\s~6n!bi377TGQf T O#Ճ9U_ $zOc~.(R;S$+3#JϞ Iwj=0.O/ʁZxy8?T8i4DDmq`P䰗eYsHu.O^7^v 쳛A`NQ~*lu{$ pTt2'ǀ?MeC>04[=` 3dPº<|͙ӻstkXzߵ˿uV]{ /dY.Ty6c6 } ^Xs%^i kL`;vI{/>%{\pW;Гb؜;wccV [X$oh1qh~•Fn@@0|{IޛpO@D 3i9sI.5#MbwnnQnM +Vvnԝb`Nz{&ƁuZ!vɸQGlo}wg_zc?4 P?{ooUpӧ;tgHzA'8 BLK AepNUD{B00<T@!Aәz<޻ZojUDMKgSv V8J${X3+p\*S2scl2dl6eYz`0o_sJgaX.qz=Y@lοL1g=rgP06o_ 6!&0^¸X&$X-t)0@evɯ'[~HBzfx8zPJr¯|?! ۜp}R29ƿOO2[RErn WSbumm-~J}, Q AN[XQ"zUn6<p0caɰ_vBkp8\/|7~_Z8f s'Bu}sYU7o?ϭ?6!؎4j;6/u.ƽh؂`^jb=j\o}[ַ˷7)_675vGO(¿lߺH+Hu!0]s3FM[nX j Yb)>lMs7x+߹uI)d5~ P$M\1!vLx%0C6rZcYLBco#YK:PXp<7zqt.T ] ^>?cf ndQb٤|;Y}Hǭy NA:n"8uȠ1+93[pMgGId8B$w. v 񚸆D|Lx6. ɢalvlF#{o쑩K}6yDW'%>>v%Gp:bPXg0mj:OB*YS8d-dy"Q^Y!# @-Du%U9` s1HA<^?|eA /PnuBҐ>]Yvޣ0~,e?'s;~Mze<7e~s>s' لؕi:]'8Y7 {!.ndeq w!+V cQ}εg?qy1ݷQ@O/y_G8{j6,y=p^<̙=4?ϝ#2@0Y,ouX)0|z$Z !:XL;+jѕ4UhKm0~#`8vDu'DkkںǎM⑾A.Y.? _|}[ht?c&˲]}և'/ɀC|?|/16ñekPBM%s |@RͿG =)T<3N*;tNpc{P:N Sj=x`5ռ6 eEn2Կ.@5n= kZN3?"}m|1?VJ*ʕUcj $솃0`җx?V8?Wnj.Nr} ~ fs9g̕W^1pշxX3J,phgTH&qŠCv(׽o}[ַM{ܩO|}>aK^k,TЅMa?-TNl]@E~{PgOd$/l.J!A#9K.G|Ǚ˛?^dL;geJC sidؤN@W˒A8G'(5ؿI|}Tp A9`H,uVCMd\PR7^w՜ 3zh>Vg; A"n)vf]%~(fP1DHFbHɺ\,1d{(J˴Û#$Fuj"0ĒxħcKDِXEN]RK߭ ; 8؅8tVFLda儱5 %0x¶¶Svזod;Oxi_z^uU,> Ntւ 7 uG^kUUA۶,b]g 0]:s{[3߀Q<ӦiC˰=`yLv`6@Ր*΁@+o{RCe<f?60d%lV_ 䔓̏upv|^ΜX^B!)?ԟD8lRp;IAp 'sx^d's|++Džt j;N$C0H[ܯ>Nks "  PG2Up#WSLpTk9__' -,-OLj={:XֵFo#}˜F)߯J "9Lii ֧ufC'<Џ 6*QPch3 zΣ?V?g ~#3h$i]~Į{s=1#5gGٸo)ˇy\dKfk{m}[ַ}Ͽ+e]lߧo:' 0#$HA[2am)Pk&>Zg =NH.LtZj$1A-qWOfQ-Եz(n=xov_篽}sxg!ȦQVD?kC~kЄa`9{ޠ DKɱ %зTkG1jaD@Ƶ&? D06€tC#RuPJ%nb+_ t <5Jl6uKЗuOlum-#Q׶-V~[ t"Mӥhh??QXיfgj(OGDn["c;vܑwGZq~M\SKRJl?N2k!m95'Gn|VF?VȆJm΀]` {gAC= & T؟r7rf!^^7Uo=W^yyWŗ\}twZLg OٚHs &Ur /pnŞ}F8ռ ?3w _{hzַ;g=@ ;#$MV`Y)KV`5nd Azy$;wZ4RÒl*84Ju 0BaMro4,ğϼ?CX 5/j/U~ZY[>ƖP++-\&`Y U(L*UյȚaV kQ΄2$4i0K`XML2=/$3`T8ODz-VJoxΌ$ǒuP yXd$';[2Ʃa#aݔMߗ| (؆mLѾ.VW7VeVp(>P(0SB:t&P0P-Fcmu@~:;m\5j A1zn8п0{vKLdI %8P~}PWձ0N-x7e (ϱ `~`p ssǍO~Ь9|ǀϭά,˵}s8O]h<]fKxfT(+Ƞ-r&HZ p0>zַo۶_ά|\䑢 I;z[_lRqe {XɌ/Pl]dȖM \h dsJ:~BH:⯾κ?_e]vW_}7ksiw 8P&30*8R )F2 pTK0) 2{(O/L% Y4_`u8byʞ1; )ـBq"1N":QލmVXPt.gdOlx+p.XT ^>-PɉHrcHE7.)MpPՐ)*\ C: 'Lt(2li`xɮƂ&V9ib*KCm(„iu=0pBZ=NOV>1R1\x?FrR'fJ{e14 <ۡU+j,7*q[Bn~y嗝pm7u_n_>\"]'sm׷&pUzs1[3A{\>3s]}t=}ۿ6ܺAo@|{X; 䆨:;J$}3KwK% Fứf~86{Exv-d:вjkyׄo1dj} .~"{`M*&@NIȼ9)OArʥq?s0>.PJ͠Rd2'cc1{l:ps٬YrV򪬪~sLUUuHd6z&<`~ߒVz?γXW( `4Hkݥg5(3c*9h UZY ɨ ;,kE"8t0<PQBIL\b2qH2̀.QQe@Z#¥+plj7_sٰwpP㜋j(  ,/QA`l"wSfneyYYocxr1nf :sA&@GFc(I#yY/E>x-Q}[ַ}/ϭO^vunJL4C\b!~]xΑ0u?K*%!~5{7ҷ__ |ݷ_8|!VT1 x kmk"CLcVZ[w?0xO=yrK/[}z̞Q p`prCS1y42Vlb$F,q ';QZnIuXucN|WpeZ,R}Z~>] BZq/ԧeYAӵպ^2W?Tfota/7`r y:{z߃ր(J3o+#`?F5W5)uXY{@[{tݏy (_>EkkY4|qvCGx md.]Z7{LNsV7 iq9'vadRN!mD6aj2?*RiqN4sp2k$ݜK]k,F7KzxS&L_EjP6sLtz,YI {Ι+pD/:Rg >^,NQ?g|OoF ,1yQUWa Px?j҇ܰ8@!i(ZPK]4#lRwQ|Gc~.zľvse^63+f$଴mdB`C5T~vd~fHf|kqmO φ@@ e&J'ZF/L#q٘y˵͂KeD lT<ȡ|B"KǶ%WD%ғ47^4 '@e6b r)A^AP c~S(QT $GO)JL*oo~ w字G5y͍o7?fNG Z?A>"-s~^'?n x~ :`ۆM=xa[w/ġg_tq@O-l~֟?ɳ*?؀ߚ/ LiS<*xq\ZnP.Bwbq*BlD 'U%Ⱦ |_$$E!4c3tkU B2z!ו\K8'P9s m7JgSzkV,"YiHlqX 2*-ULbjs,O7DIɨV2\Ci< t=2j.~Ҵ@4@Y|nt:\I;M\H uݤl9:D(96%Rެ.n3PNZKh\p"װ,,UH[h15Ư~h)s0fEC(V}Ԋߛ~G﷡{(֭;>Zdy;UUiᴪOw<~pnZ8#/zַo}ߴ#7^9S;h=A LgH`E|Tcɉ{DVL UHV"@Rb`E-dC u?~?Vwjh0$˨(9DN.Թa&6l9rK.Ds6#lL^(Tf¢ccnB|CH=iXjxWfz;)3!Ȗ~T"x¹.HH*M1$R$pR01݇bJ2M6P)tHW)|BݑXw.Ւord#ݗz*%:-0fc lĺQBKAkaNSNbiIfADl29@?eZGF*'3s,m c̫LjRdz%K%oϼ_Og{>iA,ioFѻ >yf= aa~qrQg0ǍinYǟy}c.^@O_\={諟;3*Q ZǸFc b EL9PۊY?P @(K%^@:w3s`$Q "H'NTMm!F&Am~eI!}@d@Zۓ/KP>~#TUwʺ/t6[$>Gȕ2 b-kM9L9{HYk,u-g2 Jlxceâ9">S$`$ȡ5hh. e"\d2"2 `# Fܜ b6aWEaH+H@Q,) "H J[N> 3,fJYgΊQ4HA/JX 'aB* 6@N Bx3Vӹ[t>z2~?6 8- /ׯ 싄{ hE4_r~uDB܏N@(4C2'&#~@=Rx4OqOu@>tYƠ*`9 1BWnsuԎ.<5T/=hX15bKdԵK9t@᤮=sau ~ḗm[ַߢ=!Oxš>yO,fЅmNLQMl=dE!;$0Nz-؊w&%됨uLu8byo=G_~?{׾{n|F_,cv ;KݰDEstmk,֑*)|p;(5qxK "x>zkĠk-4k5!Os&2z8AP$b %ޭSl&=hf6P:@t|\kk.E&COc|0LE#˖d`%:YvC"+蹄NM:'uq 04JE#z"IMgoBozɯqٻ/;+o}>+~g|܂kHf~%/w=;Z HĂ9Ja࿜Ĭ RLZͅNy LZRs6پıcHRSL`E&KR.B㸯LL$ЗY6n@tfP㺳ldK.>>{Inޫ2(ߝ (ϧEq8Wj WSe춬. *2Z3:>qTg4+VP=\Dz|NS, ~G 2|\iQ%QmȚ< i?6uJgtI] sY<72,Ea+m'k~/? e翨*FDD0G!QI}z+t'1 Pn9 pAybVOWP/9qt-lms$_KcUu0צ3Ƃ,O,Hcs>^7BI;,D&9" ց};4@#"Zt k#KH8WQ Fi >M-#Hm HV+ 1Ğ${;f~ق K1DTW`5qxˇ-1DU @ᰨ]|/<5)dqV,8$S&XH9eˁgo]-;>о>D#O2uA~g ˤDRp,MǛlN>VwJ15`["@* ' d1JR5{NE6 ~$$ƒl3uQ; FҡW[Yo&o+31y=X u<__\7k?u'_}_~63yD>rF2ma|s'LAu'eMD9P뵸{qz8̷^ {o}{ vՏ~}ВXD /lޘY6,=2#@J B $DuMPL,CT #R(9JAb-b"`8!)Yl x%`M(7~k&DJYBQ?>Tb0X V0ѫ0f%ne JLG;P? wi}I%l  'r 0^#0OX b肮&^,ǎ Ji8n#iQ ˌBӠLgY&E@NPG`=f*:oTZJ_,FSg3r A. [SA ZR,'Sc-තB Powe zURV ]^2\Id1bqNJdf5Pʏ 9cIy3|Q%la vb0ht2u ,u@{!Q40+>¶}2AфGN3:ULD$Y>@#a.~3+ V%ᴔm#c9Q,*z l.W`\h, dqY@QvBR'H Q,:M@JmZ} `ia&]_AT`'cu9*F~W 9y['V w>8K53paOM8)cC=/ns˶3|?x{_o}[_}/\}g!F,1~*!L_zsT-SQYW;Jq!+f,ہMeG%cN [N~r(J$h9T%uR1RFћ *VK;Vܚ=Q5lɵFaccXDRI˿"qL|.:V:׶tiY4 "R`pGSb3Ų] K?'Jq!e@h~r?7FhY k #ezhK`ѩzp=Aj֢%L"Ui͹,rMotphג'B ; '=[H#Ɯ5 anaP˰-y,I4= Fo`N͓؟<2K5< ەl}ݷZoq+oη~s_ϝl'k z+U!! J`IimW3ŋ[ezD/51Y_AX]?S3nj6) Y+ch 硆Qw5G 2D e VU“aU&!,_CfyUj6+`xe%לʺ8O]]BKulVW0p\UB UWc3vH~ECARHAO\"5V<#"? nT]1,$9%C?b`FV=:DpAKkmC h5o%|FpB]eMNM;A1 c<zy_qbfhd_?f~gq3{}[ַ}۫^߷|_yÛV3e#d(soOLя}aF>A=گ:;8^vcKIBnۆ }F裳kg2haD4SDosìr6LqkREQeaaRʤ.ㄾnZb*92U2s1Aw`6 .,N`52[*a$ Gr !7k׮D~D7%" M48)Fv@"@?`ۗ7I~3A-CGȔhW%&BOzi-98|]X\x}Þ;*,DS ".0jvO"΀Ǡ2y;zL<58FJԊ3TˀM乫;W^ם׼z_v57|㏘wb xɊ}`^XJCUgvf9w!Jx֌gg< \S fLy 1昪Q,EIsj,o#0hT*0[M2\0E4%ǢO1x=2 dH5MM'Ui]I5J%@V'˳A-b8I 1b˳ypK Gpk#ҔL$ꢱ l,l()aquBh 2'ydP WR\Jo1rNRVI`ݨe(  EKc+e^R p!5508PL4 OVpFg7m Wd;T gM <]P7`#Hd1%eB8$2}9exXb͘uO2`}oG>Y~jNM&NH|0X\߁Ge\3DfHp.fe.BbϢ`:ߒ 91˔+.Ij'O ?xXk7;뉑&Я!Կ軑L\ 2ztSK$_Ƅ,ҵ|DLjLPr)xR(7\rmfs͜)(|TQwD6T&O;bzX 贘8 sZ`i{V  @biO >J{&atd]x%A}@x)S0yf [=SpͪBJK>_JztEJhO|_>ן<Ww$ńg;Cϲ${ "&]'e#Y)zQN[/o=ӷe{W^!ICLUA ) 0ĉ\MhpTM8)OG۱DH(fqh?,HFq+a2]XF~%ۤh϶ <{xp<YTfMCQ l} )'so*3@ܙ}CE2qRi~3EQpT$ƆCH+(K }U}6i/Wj4c-r8,e1Y\/lGzSDtfE 2BȏT@R dc sf.Ȗ=uF.4?52pS"U#р14l$KR:?p>Yh+u]Ne^)G,JE*aJV'dF@4١ h` ت 7t<VSHG@|*U*agRZ}Q 7Uص`W}eWoNF`gXCځ4s|g.h{@L$ !D `,Vr;OA] 3}e(Sv[4SU9[Y[]]>x`~޽Ml)w9ضխ۶oY\O9,nsgo}[/Kw<}yMqUecb"ug_B^W▩P$f|M-ڸ(]0Ճ $n|M?.z6 >N::䠔\]%VtmY ";2x7 36(hSߩF(IxiI)%2vjiO'NL`lxQV$U%L'E?U41R3*׋yxV}R&<w(mDÎ Dng*}rFϲ!NdV4-JuҾ!R G8lwkd BZf\`T1$V4 {'0y' :W%gP8.Rk{)Ӓ|;cy<&|T"MJ(̶ ٰ\;,xb@ŢPd3$2Qr啞yRC=r4' K5@A܂k(VHG i3 y> L!vPh]"XSTu>|Xؿ|_:4-81]v֥mٺUWVg٢?1};o ַo}_k^k=tysm&)פ 'y;ehQj02@8vDnV@-WT̴bJ{Sc !m$t3xyc8GӰ{:ۤ2g%,x>kR *wibJHy6gV%Qycguvme/d I@|2}RUFP8O-_D%dbF|MJ6AF/G5Ӳ䉲.TY⸔A.I΅&&Rl ش2.`sRn=RF[cmB%x.Hr?چWjcskSld*bd]ag6gghK,<ɗG}_+Y'u /<|r{Aފԩ@' Ym(ƠF4R0놫w3Ǡ]%1BFP"-k fmi0F&`O`v Ei(AH٤o\+(֥HU$ >EaI{Zl9"x>~8)?(B] )D{zp lL eE6?%S㱁ȿ>be rɊ" @ =k9ThQ q4B$@GNbSDp/065Z^Jc%>ynpCG *87'LWRtiEՎ4Zb-#L2,P"bP?8^vmA y[<%3Bb#! v 8䎶+f,H:95c;PD $\c ʖJ! y4-Ѯ*af9d_gfeE,}` t rp~_S:0N8A/-[;pF@BM`"<啕bXs $زdRp1L`gS+֧=eElcس"2;q8.Y[c0mr>96.,˩oU{sY>pY:zGڥmonaaWjC\?{ַo}k~c79K9Io@I/r@uC݅57EH9RY lhARg&&"#s!NJylAmy ۜ/93awc_LYM {cCW%x9z$~AMdT`I 94VOE3c\[MRs413;U i/TQ_vѳ_}nwy:gp$4](7KOu~Oڜ&[KoX 8fā8a_Zۃ~v2QaR@A}#0pP)ez/˦\Mg&6qTm+K%M.ۆ|Z؟MB0$dEÈME:F(]N`RW Lix rOj~>,߲WKd H ­y=Z,ac-#ٷ˳IÀbF⺴>?@(j%Mp`Ӷ9KERi[pa 7c P؉G-:/qjtx*9 3%fHDZQF5֚Ef #S }Z>Lfǥb;2G-l=څi+!Q*fܖͦ[0vژV t:>wLoC8Ch&ZIC@'&DrXmGsfufJ JYgRH~xee䖗3'>ĮcO~C+3VWw9Wg ώ>pɋGsxW^qy$kwho}[ַ#ǟWq}ߟ8@J=Ch }.ʔKj璽e<30bS|c?9:6cJٲ' ȼJ|PD@(O%1bBɰO9W2xg.a,T(G8@_~wgn qBb?,aUzgIbLcj{Q6 !Tq :ԟz; .x?zFOw_u׻-Z RJ f&eFL00 F /lu_3So1x@MOQ+ٰv'Hi$J5$@Ld"Qhn`'֌!q^u-=G0PRX JY^HXC E,ALD^U p*; YH+]  (8W혘acYr?@ck.s$Lf%9wa0$PJhQ6 aIx $YjN!(YFQ͒uh`q'% _3]8F@ PRMIL\R0.FtTlY2 /$5g/}UtMH>㹪Ysl4ݜrU /ML*L(2 idk OUȕ(D-d\YѶ6N\ ?3Wxhf+tB 3]> ]3db, Y7dja~r*cϩ񗮖JY8oڌ:( gѠ(\5Z+9NW7[r˖-cOedB֎2k"N80 V\O">9^ }t3a*#P3#рeK>>$R&w(V!C#283ΉX`4`6U9fgIʍe(QPQX*w`^8dY#NcmrkH- P`If(z`"'dYPrs| |tvb̎r·9x4 } ;'=E {Q.y'IfJјA( ra|MH2I2cp8:bBLX`y!b11doyf1,`6(lkfjՁWI d6R ]wG VU?aK :LWAY}fBY9JDJvD*\V>d.7brVj|q$tst)n ǯ"P"3Ȱtam!atI0H&c+GxM@Zfָ&|_8XncIކ2.@>پ㗛T6iUCv qxіl-֖ndd]0&R7@=xiy\Ye0'խzsiL3GewfJO_g\x_>ӯ|ߕ[n΋?{oSNjBlf QY@©z|7g|꧕^1] |='S#/=vCN{G۷[:.o^zX'u&ߧ=9WD-h:Id C(l;|OˀH' VTN (7r x2gN5XM$Nߞ5%[]];{n@o}[Ww/3'i!s#Ēm25VSU&n5S/ K|MQ~m&7=r 0$ā'N8Oe;81uT:,.[#UoJ%7Q_jκv_Ab|6Y96&x'O?h|sOxx88 0vKf3ialS*~I کo`5eQu@=;I#2x6muBt[:xTwܦwJj-)k'%ZwHfP&m*z_M <Șs#,: \HHS !|xۉ_OO}}ݻo|>?:W?tG BENTH}QH;(U>1Hm8raMq@S措p#8/[_ݾmGOijzˍ{ٻwS/؀+ Vz]wL4$Q3RS1Dl' i#1cvONf6QKʍ_mL0h3mٮb{eJl'n>y8˺[9>gudPbLnʁVc葃 .G^3O&\v> #b6"\&BJ*CeL,F `e!1#x06_(j9!QQRX1H:/yd|Q-+" xIG7̒ C6Vx!yK!N`0 P2\9CRiu<+,"<4Sd9i+cf bO]` xA AbЏ|f(GiȮAƒDJ 5`#|U3ԝ5 D%jA1!&u\1ZM@@ Q)"s+o/{.Q] 99ܨ,$ 6` PDDFc, 7ol,H`$?06 @ {9s&vW3WOHK:wΙСfZ{e sX2T'(S)! Pxɼc1m{2^,i&yFznz=ޤ0 Wو'8lLR4U3 "cFH@B)3 d0f /¢N'. H*I9L$sÁ*ca/9T5U\?RJe2KӤ7&{Ξ}imsSӃ<={mݗmnkMMO~Ħ-[6YGΞO~Ef'&izW:;4^}⬳ΪAխnu/d;zڅ{ΦH䥁!Rjl3! _DGsJ,u: mhcl1Kr史>~0<$E GBK,ǭ1h XC+S[tvRK-@'b"q>|HMJ̞\~uO_٧%5oCp;H̢g.M&ۘB3,3;}0P0m4u)'qk^Gu{贚S_Onx|b0D5@Ӣ߉qf R~;h5r,;LBX_1+FIU!e@90 9UFΡ<@G ܙs>/xOsjjYj_NT.<v#\$/t\R{5"Ǽ"Aq/gNv/DDp;nS_%5;w;խnu/z{߼umoxJ(F@y-h<">oAW;*\|y\DS`x1IH6&16E癆O"옫rG GdXL&E(_E#( *>!#3!4Ȅ=D(Dj*YąΊ+: 6}J&+M  [228D\eh/"[LT;@PsAC0Z"yLEmQ<2kcL} *LBhl!Oypћ}@E.1TWT^S!6"%ͣUdr@Cyy/Vݔ/:/:E/|+^m޾t폿wsٯ4&6疝,pcA|YPi]Ҁ<+?;v#{Ps޵VW^';uBZ[?}w& `~ocmZAdA`@ҵC p&au@N\U)"Jk`@#SVs'c9 ni{̧GC 1ݰXXLاp<@Ϥ(U /y>}Vs*5$'Y6TUEqv::$ed(Ԉ (^V{ߨa5 Z+6K$=3(<;IL ߙYFD*HEܧ( TrKl3#&u:6)H,l%Q^F, QNXx+OH"#Sqq pv++3pDI ,F {0„a)$#)ӭKbO@ ޮ9crdH-Ǥ!yy֋+^z "Ш=^ҋ ܃50$FS^O8$,-cG`?OkG ($%RtJӪlm^_ʤeko}I ihHM~hC+/drm=.ԝ[z3S[fu3/: {UV{͙ZbH**Kg)w+f{M@{r-J5ƒLfz{_64YʧFg6/;lѭ7ݴ[n6lQG6~x{æMyl;Y-Iikqԭnu[~w>ş}s5!&7Kg΁f 2oo[4pX9O)hL_#N<"@*dH~Щ44_D6ƽ9Sk$ХWHqOAVgu/8xŎ\}'.=7\ /kteWr@M1Mʳ9Ɏ-cw~=]Ly, Mř]| ؽi=_{裏۫UV=u׼w]r˛Z=#`CWbB`N@HVsD!&f' Q}IuseL⌛RpejL+KU&5aAI';<^?YIƬyvf8{Cp>}2&6iKk. ^/ BJ0 r9p`yiB1Mb8)z+vK( hv&CI^᪆zt<(X+=BA5z h) $2 /TLMhŎijZD3EK!Ev?vM]j ]'I ^J`Z,e-="0n|o@ KRz?>3% xv<)zW :nJ^V4]z,9GpOCMl:,XF!yN  ؽUvr#)'F){ e=Ծް"k$K0=-C7O%4$<dITwQ.aKԪI, Gf. uBb0`ttm1?B`JuMm=Bˤib.dqv R;BJ/lhFj#j5:ws]n߶u037tđza6lٙf}lIc[O=wבGV]+6}˾s9MY>b|AYA#D@V%TȸH]nӁ%165mw̚ 9zUkCx.M<<(q7(vOOK^=r^Q,ߤ^-ta,1kW"pl0rPKk51? p~7~78?\/oOߦuyƯK~m/&p^J8;RDib`2I}oWs qHDp:'LMυj$<.b$@M+3~ yl@-Ap` g+rm h>{ {pP@~_$I }oՓd*Enu /( Ʃ)H]8B 5GBb/N" x)>o%TDba~2ky-sp@S'&^Ae#6?+%XSIdg.(Fm $=ARvfPXbS&bMp)MP/W{h5vHr &EhB54CX8Akx!YIKX!!* ˳9 C=1jJ ӂqāy%ѵנ*ҴNP.bv2-ead'9DԱH26d6eXl!=MuQHj x*,P^; V*Mӱ(>+,oeʽfKMOfȲYyBI?XG!u[V_~_)t)xP≕gPNksiRQ )@|aD˃؟BgCrZEWS<%Z@I X{ɀ3;W2@`FB0/IA<01!BRy b~Lyk !x TB@H+r[|!@dȯ-oxGkHQt*x3qя1s (#P&fr%ivblAgl4$hTz#e8[6Q̕7G2㦔 &Die) Hc2wȟGGzjoH:sx+_ys{K_pʥ[~vy{_>0kR¤o"wKҌ4x-ؓhAo<"}iX4 ~iOxo^7ߠuv?/~v(H`@L@R`qD 4 )DY^,š ;Vu5n|oWcL 4 ږ2}񨆧M h**qWe[5(٦8ͼh [/(ep3%nI`/F{ddFp cI H\5bh'%}pqZFOPpRZ}' ROM_H= Ybwi剑 8x Yy@|N^gr~@v{PW#SYv%KIfs M 6@e$@x1j, S Q-c 0x vCHq`(s]4:{j\zֳ^jC}.g>3uݷοa3vá@3vQP,G&0׀5"X0ij'=Ofjnu.gi﷾u\31R{<M|) Җ> I1 8bP0Z")zo9WGJ8* ΄(OEy0b !~].u0@ 0Q 6%㤖% rYiPJdchOr`mX6i:x5P u uK[&q=P]&f1y! d%*ʁntKϊP ) wA!9ˏ{:S4"<y]"KqZi]4WDK<˅@x{@"2ڬYD!I(y ~L~@q!U .sr2K{cH!@ O-o}N#97wHN(yIduŁ'1TNB1bBflĘsgw2tBpIw b.M\HGAE6B}X`0<U#Οe9V=̆31??!`9.)i=E,;^O']',zv0WzdIct/lJl)5Ӻ92l#I3ܜ0tw~=wܡAK7KgK։|jz$Э46섩/{yWd}Nyu@Zխnu{X*u混_ Y>GE[b'^2\=f&7o@d\SУFi4^,` ^= :Ml]>dD {c(`:I7Z(2BR~ `.^k5DZt%l`N׌]LN@dhZ[ݷ&QJAIa&3Nic+G,#'*{ Jr3u(u z!MSD,:{{P>C qɈ 2n0㪂:r FG+I9dxts,a\cPd>1 :P{K1G)&/ gp *;u3 p^g>es!>'ne{Wnm Ĺ3c!05+_+w=ɿ}eg}v^c֭zVo?x|3Vʺ{ЉA/~3ݝ!ГkK!#>[  $Y@L^$R84!(/%rsFdIB2 `l|P$6Tm"Gu$<5ާ@9N"0 658tHu%#v.0"2!p ]N +SZx)u&?]M[KN|둷 IU 9n1Op_Qh#c Orw!P,TLKG,l;"G005&Iw(m,/6O=*=<"W({ LH<fMGdn<llqY}y6]/#hX6v[G*HmץM:Fy ,%FfCl 4g^y˯^T^y89;7M +&yA6@L"J_üU'tn9!+F N!VWt\$IΘ4+%M'ȕ*=d_Z픧Q4{RNqԌFfgf33lc{n]{ 槹~Fn]˼oifec_WZuYu`Zխnu{ȷ]vɟѽq!V1HJQ}Yz.:"">%M%,kH'It֊Wϲf͙l=zl8r(3Aٰ~Qɂ7"{7&yT 3.6cWjKE1ʫb.ju4٫|/+ɧ͞2\ՁoG |މ~篢Q|x<@8Oٯ*˙^,4(D&cVC.?sbFL'vK,B /Kf,H ¼PB4Y>Б ֮lֻ@kSMi('|J-D)gxV! T^b,W f91Q*ZD"G\A ɰ@]EWݐVhbJ*"/Tha׻8-x8tYx) gh~8_酅Ew9I;R= oPUgX0tT,ďqP{ cntR@%e,Q␙}eD8~t)´S0$T M5W6X, {|i"xlT0AR-Mdm1 BLmAzdNꮹWRĀJL@=w 1%szLy" Y7>: .uK"ǧ'hE5zx=B[XHY_)GKp`X:G'Ez2 |ijUe/l'; XB` ٘OcƌЫ(ìyzϚkc#1bqgkXVf cɅ|z5dܘtUR= TB1L|B9P+7 "ʞ+_beX,X)K…]=fUYC .6;ă [J,# v: Iy,pvb:|eQ'Cqo/ۿMcKןsѿkjOyKYa}n\}y1q&r~VǕx!uT5KEaԧ,Sxp%%@66穂{9X*#EX[< 墨[̬B1cc-ʑ6nD04jUp*;IgUa+0h!J|s@CQ| DX4OcGGWHMI[($5 .D3TD" RD(Bg8'v@0ϣP1- *>W:S 5f$WREq ` t d*C)*:!]z I BS"ޓ4JE \ PUIEc4äJx*ԠV{&;ot)[@0Dp&|wr%n4c߽NplqK+ే[OPȴbJ/f`ƋL!Ss@ts;roR!0%AA/{$Mj:uz64ۏ퍮8Xrrm6i=xį@Ɏ1wMFAMGag|iSڝIaλ Rxᢴ0sE/ycvj>]un$dMy`HWD0&pjjZwlLH6sm3M܌yn͜di Ě?MEџ+b39Qn/-5سu|F=&IjcM?qGr)KVN=v?WE"SƙBf,",eWsj+_A (OĹ[ol = i$!ֆj/\ḟ;bYA9! UVzT%}F ߨ\jdD7!=r`PwRʰRy&DN)ƊBE j/< `c 4T 󤼉\ v9rRTS({/ ذ  K4``e­S=17{H<(<p?FVx}V'~ bZ+?x[??Cq^?}Kyݑsf^@kN\o8 6J*zXxa~GzjFCmuiKϲ|M\yAnʹN#8.P-am%H#}ܣ I&Fa\bE K!rD 'XC$o/,9YJU1FၥsQyׂlC,V?NM2ndYDX`Xk{>%>d OYcId# La'- H7R5:X8 h~H9C D⚼Y AD< egRŖy.W1yRE 5r=QU Lu٨;'w(i$uru%,KcAԺ$tLu!6JjՁѮQ@x8 =4(ҵA[X ۰~S}B+,@`DIZIvϳ8vq?xJ=*.i40dP% +ѣʵ,m˶i/f&QN`ЪS^glfYk&cv az4º^ۏf)A‰(O J4s09iĪ*JT9&#e&e2t r2u5 *TQ4U,i~ocKySdʉif;[GEj=y},$ɺU -ә^F?Ņlfn>S33l+lr|giyt?|sgyf֭nu[O'qŤa <Ɨe+#ܐ^gH##}s8%JtYR#)~9Oܔa€ ɜQT}RPI&`ŤQ⹔`<1tHVTc *7@Azq,ͧ sJ?if)"tM ͶT[0=9w4VGdž~R+9]NwäDU.:Nm9ɶy2)P 蔀 {Hڊ-[ѯzWw]J=80IiS`{W 4 "($?(%dYAp:`$$D*|2kDG4A;I2L:x (,(JRP|2I Uhx- }zBăZ]1䆩x.ܴī nˈ1]ŗѧ*B&:UP2J(*oZIy9c\QͶ%0~*7>ޞ%Q]HI"~8 4~*Lԛ۽9Ecn\ }EPs 8mtwEI=hց-lvJ 3VMaB?7e z=Z&a907uћ}U_BN~.\|d<[14j=2oCsXYbMu[V"U?>e#yc˩DKbxe$mՏglSX$$jZ򭹗p@: $ +{\=:AktA&%Ο'E`%!|Tq>uISZeD^d X Y#BѳoBqPd`h fd;$ؐeuYܩzLy Ҕ ޱ\5mp5 !&Z󍯉1{1{ɖF<+:&?f+bUS/GJTS.hẺmX4@"\&sŴ1?B/^7wuo}[0o~mX{v;Ng0` x˦D1 d]/4QhI,wTSf'3|cϹGw77}EW[&+\{~;_{fNJ_Ȥ3\IKГ*T`bAxCTiˇ߄ $O< Sf0{<LU, QNyRDzx*P=iO}b[)5T2p FhϰqUڒ TBІ:vbMU 8@Ap{m%BԨF%:~(D禜f]R&D">!i)V1hM,X(!$*JS*TM}wf@8UҺ>T a@6LwE% F\eT3ǘ[iH(leN"("޿Gy^c)dvZxZp\9 4 "3ȳ|۲CRϷn^Wf"43ty$FNq&hT_w'݆=gћ.w*1RP.8EF&F8)`,7E!md8)Ed2jvAiN蒖:J} &pO` yRY!RW>+<^kwmRJ {X)U:L Ud?7υ 43:φAb%qA XiLr QlVby' V[K۪ԚӾN'wݿǗʏ}o߾gϞzt~Y,hDmj|X|}zWSv=9v?PQO!Kّ']xOoO55PJyV#{ۚT ~#7G^dyǴJ6!?`\0ғ{{ByG=P u+[>O2U`EOB=*cX?6T%` q'J]:Cb-4z}ZpcKE*@2Tyq}  /V+`דU,4[&sD&*Yq`Ka!y_@yߕ=Af3 GT.*a/^YCF`³ZDriox+A8KwRxǃ^9 hzǃ9Qql{|¹ < 6-=Rt>FU촌%bimiȶ9fyRV:\>V e130 _Pڢs/`n8[ſ_~SW_pKV[SOO}Snξ\lܸg~t&.s p^ߊ}Vc) stZ*1JTAZLbCm> Y:^+% +vz}ɋ߷--*K# r`f!ݷ/tD;"A_fc{MTDIn!|ɨFm\ fc`$TX053 !,ƃ⒉*$A!& Ä$dQf0)1|)gO v2bK񲲘0C-Py"Gjn8H3PT|/È%4lRResJt42֔g)`/,99T2tOBpO_)YfD6< 93T#`s>ٔ^ce(=(eyD9]l2 mj; ԏTCi=nğy(NptxHsN"eadJ$ms@#,cѢCvodYMX(ӭ=l#6lpػXN 3 mKS= =Pʩ M'%=5J,.nw_LVutXb2uz)Y2ku'ASu JI1^7'*y>'&'\Nj:UIOf9d*I琀 xS?l~^@K $ #i4]3%9@*J]-cijQ(?fc(b~$nn/й>yuN:hòmuK/++U);wǍ7qS&ZK Xo|dBx  8T e0CRr<"0Hb8g@ɜQofqP id>$8~QGnصR:ꊓk ~N~Mx,K6aV,BEįΝ'N)(Rs^NA#Ddp%\:x? VLe7 8wC B x'ȥPL=f|SNEҭ lep,Z]zsm,Y!K薨$; 3D> {;r&ƅ&ځ>TK,ͥ]ն&9.WNrC(WPd$k!\G h%)D0j(4ST(b&([^j-B)!hI}ehF.kG+εNm}REB:I3 -hlZoL}s ݮf``rs}ܵnGrRn /5&}6ra*ߖmǎUֳ-B=G+T*)T xp0Er`Q8Q6?!7=>MϸTn8YO_ڵg!f`ʋ?ZRgi_b4Z L߱1* b*8$Ϊ aJ{$ɹb`rp?.FLX;\:ί쿀2k)0v`̽>4Z#س=ﯖ7o w1-)T 玭CDJcI>T1{<Į+[ EYJM.y\F8IWLx?%B&v%@HW(=1IͶt쵲%v\K򢤭VxIXRD~2DSE hb`ӆsOb^-q|u-C"p7!<  `?8OYCC 87\ ?N)JeP'JzX`|[<~~X5a3J,uG9F|H|=QK 鯽59PS/[m3r{歖83}Ζ|OiZ@׆4͠im}{m/6_y Q.@D\QҨ.Wґ1FW2q@]Nzk:&!'AQ)kX!KZfnY0!"Y\c ,;yoL7S#DzP xI6xH )AI{UkMȳMA]q}_7Ϟ?z%G]ߐ3vgg،x_r% `XgZj>ceڬo({O ؿOÙ|Oz_vio"WjW\qx^?|O~}L6Sʊ<`lΝ9a1]w|O~֞tX2#a~B졸/J:\h6h]*F"4Fj HŕUSsƀ,[mq#[|#]X+"LW;gd+ࣚ\Rs& |I],BQDZEEW@#& Х(,xjr@3 Ԕc~0w\: DUrMW %(EfP%F(m8mTy!FNS2#>"!Ӣٝ}y{:Yuq! D嶤ZX 2qNVj {<.gQPLCQr4Z{Ribܐ} ? &ٳQKJB@hrvt0b GO/ yL녛un BW uz6, gLO,'|Xvґu&cz\fc4|ᇒN^}.q92HcYˎeKolHY@.[Yc23M)2&2_Zm+fhw=7=n^uR;LJY$GJYjOO*oXmd~gfWʰӑkީnoT.ɲæ@XШ& b?zmbg<{NS3^Dݬl%iZ4mRDݿmawֻvzGq99|^4խnuJ{̱{?f˔hFEI+\g+ZBOdq}&&hm$5_+\:ʐ4YcE6r8 WPxOa`@(N)f;~ PGX%[Wbh3'| ʙ`j"0ҡ/>)'yHf+*S#*fIҺc# t '^)eX:k1Kם-&8?LeYZcET lhpAj՜DY*N& Q^=oGdB;=iTǔXiUX:[ GY}k9}>~|+e`d I },l lT5uo,JM&彬 mXl=]m^m9K^rE}w^ƪ[ ԭnS|SZ`ݽ.r+V俿dfAq`Ϟ~_'g۝ۧ#! $4! ZOH$D ,_U%A&v`C$! iHssOsOf9g1ǘR+_ErrjZk<qo{yvv{!7!k]ke> -..rΝוW^؏??juo89p7;߯yڪ,"" ^vXe$t98U8yGL2 ο+)I%X3U`/ 7&+Cm 1u; )oۊedq̈́}55 džY@ XB}Ǡ؅y( GP> j!|VW>_1d= ЄHp Ұ >'SYE?(Hpbzz(^*CglqFiYh ٳ^o;Rz%)b]Iq](one|R=~L UDX;վGdb1!5F1փs#A=^qlcbU-R>M\{Dsd& *9,Bzbp)cjyt{4MkZT%+dx 6tAMz݀oxcMT՚Yι̽?Znoڰg90S7]) [x Cq-T;=p<[> inYSZ7hfJ/dl 6[mƯƻ̽Z !F1*cI)7Ich{x)G@$[\g I2^NZmĬvvGܜye#;HCٱml9kzۜ\J]4?( m۳gϓ8nGPGGSq_}ȇd{Raw wʃ!c =;#[R$lĎ7rWae!^ah'"{q1`[y-^^cA,x׈DOǬ2ˆ`O+`LX(3 USUnůVʔmN  v* p2j31ü z;6Ijև(jwɯ;H`V$ߏ_* }u,WHYԼm@!drxir sA~Q=8{d\ӖH]UTQ4wI*ɽ,屠 ׂA,:d a2H2t,frY=ysozӛ替}Hf_ǿ w#XQlu^)N/j{Vy"NE/A͂M`<"c1nV{syɫ'Ss}FO,M]t:I{sSmj}myY.ϗ<<444:33slfnNiwM ́.e#>th}~/Cw%2i2d$$&{##BɣetѺ/b-3y<" 4L< au/6qytoފ~1{߾䲳`z;]$d8С7)cHdk"HIZˎ`AarxYMZ>ŇaeLQ(=sE+j#k/c$英S[#@gPsGXIJYTq"=fP-ZoyWbqyhC5 ٛ*'}eq%)YGCDZ#GTB@I##Ak,+}%l8C!Qt,b=)<F22DpR>Wp"3蒢tɽar6Kߡ6P 'ԕFѿc)p^q`f1E s]Y'aqi}@YX/.&E0IbWY6EݩNoy>,3H'KHBnZJ;B*ڣ0SR}>ڟ' 466ef~g󒝚,K vl{ۃ^VY9CMܲxpNu #QǞ-PRHmmK@lJڄ߶mexp|cXc96m|$՚nw+JL?oxt,{Oe9lFtN:f 's p%h9u3Oھ[{lί~SGG|ƥz/}s|{0wn6)Ծ)D "!SAv%)FrZR6C*]1LuriC}1faⴀtXJ#uvS"_$)4{ɲy`۬*mu|D!m1Z</eu㼅(UtlҜ|z"xg= KO^Ȃ. I`# &怋5L.H<(mz;^WM߉zŀU\C .䓹^: >U4/4ACc ]byHj"ws?m\'ݟUw,NCCIxCilIr]4_><(*. A%ZP_{?k_*Gx 'CS2ϲ3kYʲ![.WV/~w[VONJcr9X7Qv<煖,k }}{9u/|L2 *2g =xA-7e#8XӉ;X[(q[]._P&Q0[_g{ϥyk)2YyI]"9s)fiI gX&+YX{ NY`p'@Q[9ͻZjbeA40-[cH@LéDr{ WPv.L =fsm’m*H@Z`dgfAN";5)͕G^0Sd}Jb_K 6K$pQ'NΛhv{{# >$N1tŇ{6dy]=!E3D1Iґm"f`(D$1i:?!8w׉7mr?!՜4l/W>^Ep!Мoड़;[Y38+Fc*M✔kdA-Zi%a3Qp>.FPеuEE͗Jtlz;]Qg:gYGOKIg`fɊfCy C5M@R>ؒgWB鑀Y(Ҍx  Ot7Ҟ͛MYl/:\ڱs>::.ix({O]M/}]wu2պf]!L ȅylRQk#~t`LLL>q7}~{r$C"Ѭ/R[\X Lɶ`N6Hia1/Blec"{zlLY.5y:`%徉Mە3b=āWLW؂$obL:` +WTU0[&CpbG`  IiZbj,4lV9^a+HoTLU mo17s JB56{I EU[68 A{_*zJdl+y 5"s;ڳz++==v*{਴my.]~GtMem㯽cH 6a+rl4c+xoFNsdʳe mY="sD~ⵤ?6>YλUܨ!8QMu^k]Ko_?_K?K[ny٩ٗ͝:U7|Qw3,kVo)ז啕sw9*353jёNl]hȲZJhB-62{.u?z=?3m0G(6Hmb?k+X݃Za+^r r4ΜϏhҾGW;MkTMV%{:(ztڤ}]/71~^-VFqx2u5=X *K:NV4ߚ2{JBe[O y^z? ;cODo!t,g%^*ILH$8&G@ OߏIn:Hs:큳D*j MNRNۤ(ƽ]^I%^^.T=@ yMlytSd8= E8v7 N^lIF'XE,MlsX-7愪kUm OuwE#YͰ"@k@|9W,wd6dIn$;j$qCk鶎phel1a|Y}oZ> m*P߸7W #xG,e2bV秨o}xsfì ,a! A*ٷ;rRv#caر&3VM/Ш,*Fi`l$g[ (g`l^(;<;s\C˄ߙ9'8߾7mԛRɲM[ݎ83o{@ v,5S\XOn?L<{xK<{̳μAGGr_̧.u3$G䊔EW*xMUKc h ,3 gCIj^%O$99kf_ KpOtcp׍&W_H_4 w8kxe 'K9o,ƈ&4ۙnJ'w:Yb&c):N6ʠ]1agit}hb9>$p&+%Z>I`~Ԛ]gIa~C8}855AZ]l8k48Oi.AG\(*SW3%sbH$؋k~%NHބdI)GR3K.nl懵n4gFmڽ=ly% <&,i`X5Yb_$tK)dEI-|iv1C Eڟ|mkae㳥cZb;P%ܴs"顺fۺ-t)-ڢ#$.b%yP6(jE^'pP0;Suż>L1kgT@:R! 3t$f2qq *l,Dt@c*tۧڱubO*Wjc{Yu*T$i`\3v0VG Ept1O$HKGE9,4e $סKx?8?^[^X2hayf=[}/A3Gѻ~ڨPKƊb '5k"0~;&$(fAD{ξXL[Zco%|bJieo}/y{uGO=ϰq뭷׿N|cGN,/Z ib5JfRntsm].-._tEsO=2eevdr(C[LҴiוrTѩU|qaXב۶p)ei֓,+Zmb]y+33??X[[[wJ?`(Ѝɼ1""McoKi:;jrrq>Tvz[VT2xRͧ2pȣ_wh?{;kEmxsׁacIL-6E(0@']IkKM"`ǁwl{| 225`[ \WTwT.i`l<;%RubD(UVǓoQb)kjB{AVeMM Eϰc@{DHpTAr ld2+׈d#jr$f[,o*Ecy3^ٕ^*`:NE‡F|[*TAHw9au|"CmL,c=yso@8 ceۢ'I[y$Qc4𿒿DWH[*hfAj'ʺ+\wu m|z̗ŅpG2()$DP[QliVY5aZ6ςҝwL6Z|K*Esk/1c/xշ^o> x[nɗO< O]t/,̟:5g. yHQMH%:5鎌͖Z +>ppUgyjB$DGy y>~С}GGhev<kӶX?֒1e@x#7lSA7~籮o s+q %}WˢaA v} |s5Jѱ%Xt*.AtF(Bsi :. C"^neZc gHO A|GeR{A)zZ7k JXZHg@f8ʱihIMCױdʑS8Zh S'hU;f _}j#P9+,=ňi.ub$`呟Þ$)K)irIHEGH4[g8 4v}ݜK:޴*ܷb"k NM"e&3;߿OKS4: ]1 "AVEJ5V7wm5Fcndla$6.!y7<cµ*SKfO75;ǢCM Js. dHJ}lYY7lNJbP=2ojffl-:5ef%ixʐ4KHٵ(U1ٔ<Օ> w\9>>+ݏ??9s_|_җR{kJQA5DA-5Qf :($eSd\g!s|{\nkZ0Cx*p#!A}B]D'u;3GO"c"Պ AGg;GQIpxo#[,'mlvFAxF9R_,prB3ѻE(t{A /^N߂$z jk^b6u6dBm#:2Kk1 NJ8U\'RoT-*&L$b(YoK+%H$Q%SeW%bu(S,?Ҏ xSo;=nXF)06´ɋt3u|h?@hnQzapr1'T)Kسj]{Oskf> yGܷ| Zw$ l-etFw;w795u_{gm6Z^i&ٙRV{r5(D%B$݆"qp(KI+S +('6z{v(v5ճh}S1f ԥ YPc㈈"םAJ,^jKc]mflM4YSNl֛UCXqn{5l:YRԠ"ӄ,  CQ"'t"bF6k$4x9,+}VA %\.$]Ǵ;N SKl!PH%q…Ąg`p%*"+'ǗNVla{4t-d{aRB'MT_ b@%~EyY"P"DIrEIѰn+ӖEh"tɠ `VΓ*^ #'7 ,H|F]'lb@n, 88Eic=hfSм 0ΞN!Fc)ɪU-0D43]z#" T,$豩/IX@4/u~7,Y>w^JDR)-.XyN^倅26vѣҲLob3:Mk5fـbH* |UYO:1VPi `]JSH*lCq&Hcb$s2\N0NYnv;(ZmonYZ:4\]^f 33l -5w=lh ɔ4B)LcHY AK5;'5KHYnkŤ1V9;>/߳ =U% =(j%;AtЉ2<ꜩ]׬I-dDN{H|)2T8u:DHLНNc.n{Df?+9"Ϡ/eN>*ȘᖵMb4A4FFU"Risr^:VLCј;6hR<FA,"g #;w2b$A/ئP$T/nj3S$φ`;9eB!tM#P9M BLI9-EUX@ǑbĜgԜ@8KX |RXe˾1gUJJC sQ;Tn"F񀛒I.9iA!(z?0ru4*|;Gl7KoO\kRvRN*E{`asܻHg$2Ay:{{Ld9GW[GUf0**xt rcl$k& J-6_C2$nwS{kw7_9z0 G4Kmsv:V"IFv"8Pj4;ZB*?d'/e/XV[`Y/ H30ɖ85 Z9cݎX\\(jl]Ss̾vM]5H ~mY@StNR$dB4īզT,t N:%fggߵk}9=>=3/Y¹KVw.LXzXfy !B$Ym o卍$ml>r6jVoOCGCG,_IKW]uʶ'[ `C~<鈱y7 A+16zTb[}G\R"mWQs( Q"k$GHD/**bf8eUƏV1UQ2uWO5n$ˁPw9ۗ pWHxP @ig|kS8[i* "s [׫Q2O)rAixh/r'K)Ō8TtFOxV ` 8wRUTLIxєUHKEW-$MkHA@'%Uݘ>y#¥x^mX+:rdʘI<`~;2 EtjHavolX; 9$^̥ܱu HBf9G oZBBA'Je(, *80I߃Ǥ/LA\{o1NE4%Z(GK `}=I>n@jybll~o+92Wa1 ;D?|ɓ'gϙxW^y'?I?}??;?_w|/ml06ah}`KfXP'l\Q$ZŘ\/(-٦Zˊ@!cymQ۾F=y*;uί]}>~^2yYL*J7sR r/=8k"u(qk^ Lt-_ /5g2Z>418[_RlqJfi-3A KH:@,fy r F:̫3x[l[-epuonD= L?r驩RSYBq( FFP*̃!ZfwyFv|r؞zm9vRnE\_Zա 6$ HmS e"*wm"ʳ*h:}lCARDٶw%U*x9k4jf!貔lĈ܅Bfzw~ &̂:ȼEsS wEԱ6+f(UÒ }|L,h-k'XJ|FbF笢o .ѱݎJ>xˣ8d= &@d#x(GsToƼf>r!rRX$1nD.c(u$/P >Sd I d@rbX=kB%e^mv+,D`pqĢ*nwlQP̱kl:ش]0qqPY8 X`2lRMKp ,dLy`kaþ8X.PR75ٹ{Қ!p|E 5_|q~EiiM=Xf& B{ڛ:'ƒ/"F%jg3+K^i5EO8T)pQJ زzkɆ̡B2!R:QdV4)x7pסC;.QuvC{6 d]/#j&0}_;sXU"I'zYʲB@$i3L{Gh5[3))ݾZxʭ ^d)^S>X']sv YߚYOwh팏t:Mldõd`|suNZkI7yK Üc`f5WMT]3$߰"锖1pW2IɥE~>gbb+++?x7~_G}+woO4]q $ ?Ӂ:0ΧT`>ވih$nnI#I re6ҕ,!c{{$o T}HQeV6Z Kr0Ko2km X& - 怷FCI-u^j]VTs 6`^jB ᯌ$t>gk Q| *'i8AHJkyy;t6+$kd ER>0D{-qDZQ1Ro In1iÑ\W )8Hy!c07;wF9`ʨy"99I!`aApǑ~`CV$(r^5f8uS,5kC)exQAid+ KF~$)7 fQ=jdX*:P+N1XXf(5RD`HpljL/V#^Єk<eXU,<\x$ڦ5H^Y4I=Tth']kCs֙mҭSd% r*(ط}tdrb"Oe'9vuX{siűȯ;X=r˴iybT@V˸RvA pe?cwC&%aaeܲstA$g-B[JYA!y}I`_-Qg!z1v_??-CBV!l,☵I\Eњ5IYYliuV"Vtp>cu xZԲ^ʿm^8{<@^lvn<pP8ϻf Ńg,ƬFW α[(5ۙhrGqvQMο2844bsWB%f@+՗Ԑԉzn*0K~9wu '%bYsss+333vСk>??{?\ǿ3F@9>PO*7z&>FlTć @R1+9ę|h6\*G$i>~uaqxzh\{|tכ;~68^s .WOU}Ϟ]ԭ4e ;U\@*q1sTwI PWv̻| Yy@'ѹE87etR=&قȋPl:1MC'(3#k is6F J]%?"I6^`DpV| ONxLz7AoA3VaS]"I:A *f%CqiraeX5{I9EbO#oYkhmNpL sm |o޷x`YA9ocQhd$@Y#]1(7:7ui k.H&sV{W~'_γ8\sMD= 1{mG=2??XI9-\˒ĚI.@_C3MSe^H$bР,n!esߙg4?.Vpb/m_gi&T]*Jg 4)p;wР @Т+2&;Yʔj586 ,X-_G(.SsE9)|#Ҍ>;c#bF+.nUPr"!VHo鍰p$s6ܣ`6Ae=[i!ά,W)=+C]x=VRMVgwyy6nNٽh^]_2]э4N*q Z5FTWɚݕdbvE _1qǎ4ˤ3ڝNG>cMMM?}UWݏ??7Ķ/~7Wخg1p$GjXm/<$- |FNb9AJ =2tlGS~'M T(;P]$`O_T.ohs|S_zcsmvn𢎹riDN73`O ` ގ"mlnAE|94yz]b-[25{oS4((0'Zb 9cpoX741|<'j+pq qz"Бr9aὉoİ-޵ X4'&=wv34 V mli2UVWU}wX5w>:";:1d,}/oFhԕr򀗎T>Vq}V2i=g8xd[5Ns;vrwֺT=2/C \kSf̃<:滀CVJ 8pwO|+enP~; I^T,X[leE.D},ȓo!R6!6{RBsy$_z5Wq٘XY;Z7҃g*;?۞[kgI,̦՘(Zz]la+#V6= لN5MYP%i,~$?[.GܲPkhd%ٜĘed m2($Բ9 -'4pz" `‘R BB frP|  3Z~lۧ7kϝo;Η3}gSQ~ d,t`A"{c#j;$[/%aD|A\+|J`@j=X~Hc1Nj|e16B+$MO#J̃sE.Aܵ$W8_T6{K/-}TmAXVp\؅҆0<\ca3&Â)ޙcnY(h$Mݮ5p3A7Яz֟&Z1|0 8,HH)"NDrTq{]MX 6òĆ"ֈcg&E3aumP]o$3@^\Ku;VQp|, J@u9-H٭xr^U^ B5mP1//fM{z 9&N;шAS7 :SU=8xxTdNŋ~W}foYQ ֎tTЄ>Is^x$i^9Y,Lj{v@`%$JL*|s,ب-Ls4auk&lctQL8C%?iMO43;O K$V\(tbyqq]/ǚYm +[3.efs[֗H/xuؤsfJmiD&l4~/`G00'!3''''/,,_?t꿇cs+GG}]w{_pЂ- 3 ,Pt{.tx36ϰ)ڳ} Jw9+!;75 'iW}ML:(rhnSVa+[+ \U_T ,R2K<׋4GdI#ض.AGݱ DGN)[#}$Fmģ}قkMB;1ft Ys&`9̨ΣsRNR1p}Bo+LjIY)r9cj52ӈ9bkn){Y X;%܌oc՜cQ[C0xsYƥw?QU s4Ŧ՝68ԠH:KD iftjZg&JZ;&&.\^]ؕ$=#C^o7ϝ'VW%e$Ihuz;51_210'I ,h\33䆗 ټPl ,#(o,HCOXB$ѯ:]%.D$HyY 1a9 iG*1[9eγs Ʀ^lޝ#;{1<4UacS;< iXG+O=!Cq|[pta̹Dt_ (߂"FlK@$֞@)Q(L!0[!̱yʮP7׋dJqK؎{g?Xcu`@ ) .>$gHDqTN@I |C#C:6%[aܵ`A5iXFWHCZ6uwУ-hdq4)阐W;ywmuΨ˂l9Hr>zuj!`Hk:Rʦ%YD- J+I~jJ (MvtŃ9JS$N$2q$#%ѱғDpyfN7LB#GKD &;*gQ@[G`!E6ˡX*אʭlKYhף|!evtq>SlHㇰahnUv5]ػ};IiyNuGIw4Lae`)--!g0W,1pŕtnPyNe0hlLrN=<VO?,3r=Zv6kW+S6S0K De~7'y26OLو6DŽc"9-1h+` [GW$1̎c*YX6WP$ D\*ڈ4GD6[8ϠjhFu*2^a/;2X[n,ksY:J/z^V  x:RpNJs?6xC:~b1&PM;]X:FaѶŲm;N‹ft.dzu+G3ϵa ԯ-}sPJ_!|t, 2Y]6L#c3-jƹy"!vNgq]SW9kPͿfUe9>ڶnS7y7_6w;8uƽ0|wnنQ4me+Kr62yކeq׹6o!(eh(zǮ\]]ݾp[~/Cy_=|flmf>;nAXUF N%聃h۽*3hIŷ\> ^}4pJG[;y(^`?'yl2dr\zlM";vp}¥ \솈}v>XOqhezsqO i+sX9`a{ k0լߓMLu1EcELA [f`(KGJE6{ENe1= `S ,dJ'zI7C$Z/C ruKܒ=$+FhzҴR_ ԣT7k1a_?LZBWa0Ë }1Qe|_8$T3Id҈<%J+ʺ]Wχ!$7%n>ĒjLYm!6 `xIlTK^‹>X8}޳Gσa- :~l)3/l5*<<^UK!0W\y'-S<&V>=O&"I/TY54y둤r!\=$2SִIW Q@t)a+J%+MƧ1Rv *vwy3a900E#;|4Xob?e~V˥`ao'@p1ALrFYIcpBH{#Z8\GM!`)e5 3Y/-_d)+N O#ZfAϐ=n N$vN,MSPPG@T+`k7Qp"&B B۲&cBRz6҂0FU );.|?&2&A% g ʉJ@QZ[ ak`ׁ"nIBs! xI3 v4̔a6/n<&d3,ˤ8F%F2 /ع?uSq2ʹ,<ۖ/R|zZ"OXq K|9iuQuS#[h ː y-jp2ANXLv✈pSI){%;)δmیlxiPbP:ud=WW(e]-ַf\:tp~׮f3=|[s݃]]Wwv=L\p)tz~Pmn%%Ⳝ,Yg!4p!J9yT^0 o9ś7 oy>򑏘[ou̶6f_;|s9v @p?g~`#^i/[z͟T%pde|\+KC_ yWxAji.*w[ZOylu gϏ`\WZp] Jp3|n? ]Z}uXʪ30o xP3dF'+.a ;}-ʷ ,WTҺyW;*#3U%Hq+Oc'\30J1JOXD"96%E&+R #\1l\[YX\XҖ$n3풭l_sum46eY:{쾵O~oc=mͶ~[n?s+&_*D4enl""R&~:/ɲɲ %KfY!ȣ*x@Lsl\IƵBN pTJalo6ΐ%άM`ph9<\_2o!AY8̕#D{<38&֋T}Z 2p0{&3G Xh|!+N$,^$ܢ\8Bd$L$m^*</"``WHv}]'Lc΃0&dJlLȭ7º $z/^A!Rϝx<7)DҐ3"`rI 3Yx刅o ApOt ) ^Ud91P&p;@?LXA#cľl/h/\%!Z^ XۿobcbAe@GL1CDav/{譯{ w^/}_{㰯ky~4my}zҥK?W"3{flm~W~ _ӷR~"?t50 QȠ`8($K`"lBAG`ˆwϥ$fYKԂ~n!rbgCL`<ݒWK3e 8ckB=iaByp;wGBJTPΉՔUHrXQϲl.`|Uof^,>}qTo:HlR)qHӒ!*: $iLNU3]r)KO10x@GH} ؖQnibIzLSYxn`!4`s@8xs,azʊd3fyu'n.6洞w;bVe%jCϱjױ~N{=_$S{`Z|M3/::H@I@ts iQWTإFϾk7yW=x?_j힕ca (=&KUL+LkDV\e01 Q.#ie){9MNdcvo_xSkwqlmm̶'נ<|;o=qͧNʚ9wxw&3GpvEG9aHZXbPB5ϋe~?ig#\xУ]w*DZWד,PODY+%<'Dɫ!Yp>a8aMVc>X1,䕟ЃpbL>BD$m|Z&JsR[0>ZtImyiw`>0i/1 ry~ Ss$'{ajq|/+0'PhO 0}hP2Ħ&2"}-<|ec[ g <]7XԏF<(򼕥P#ceT. /ov Dc `X6f "`#1utYkT̋KL?› ;c *33A2r~2gH(A_ 2;aaTz,#Yb.I51 2!Enf1K :YtvauUTf# Pko} >T&$7Z `YuQDJ`ZFÍ S_[oj|yo抢x%pB=jj#Yu4mޅCl=}x~rx[ ׷NiSg𺞄aePfqa [ n\$ss\F|l¿Yt8flmo|WEū}~P\p8Ƣ4JJgeNl sr +#ˇ?7?`@@gRYtL beɾ~8'9h q:50B []0Coڅ> {$d_8(78KRC0sya߽"ԫ;eƑE ;-MGGe/9Be8=R8聥]VJ_`XׄeEMum|MEY+0W"16p = OF|τrbRmجg)@LOn&L|xzSM RSυU KR`2J5Y ,Rv@(w'L#JCb=O1 8ydSKKK_o?O[ZztEOTv+fzE_LQD؇3/% ~*=$#X^G~+~//f6f@l{n࿿kǏxVN 洨!ڃIBd˺3Xcw*E!/8xprСZ>,>ޏʹ2+ x$,fQ~Lĕ cG#ΓlP^L3&s>tCݰJt\AOa%o"`D!:͗ȇh=mNHU嶈4@qR9\sa4D*bw#_1c^"%%d`t=A?͹~3Ň+`E^祾(3=4NŀsU91xT >:I τA!C@{H (}` |T%3T*fۆFN{bl$m GAAjcࢍE V!bϟp{eXS-}W.mFy1 NȄiHRe7` &3aj 8,w(J<|awK>1^{>aQ0Ia8|i"3V{r02[&p5I 10Q"ϙ9d//'Ⱦ)]RG,EX~W0g"Kc$.+ʇEPs&FeMh%s2r^l]f͐[։N;vu ;hNR I+I%ھ UZ7!ßSHCd,wO'?C8΁r2s07s2N,5BtB3]^ti]­B.1=l2&Ag(h#虦K.PCte,m&|"&Nk;22栫{AkG&yliȋ'+{#)=_{\hMo{S~lmm̶'ؖʴ=ӧ۶b4뜛 =\ۅWS TCĈJ"7EQ{d֖w͹ε sœ(G\%̢ф?M$w0I+XS&Fxg}wT ؟&mLq) ֈMLXFtψ1Ss@M#s&(RuNTO}+9C!KbԆHlRzx jm2r{1|ιaALh s\|ݯ҉?~>ٜ%08+ 0V|g@3nީύ}+hZ +0&}+ԫ/bs}}vm[|6fߊ_=yہUFdeFl8e=l=~Xt. X^b;В+{p2ۋ|x N<;ls+N<;c&p9t>'kIrssviZ*8,%\Ax'6oSew\/5 Ul/LIh Kʆst)&ɡV@;>hXDWuH@zBF<6Q~&^/4)QJA iy z>SInlvC2~_%] 7PDͶ59O}lkvhW %Lc$=@ڐكqd Уq(C4bF"fHq$Y1b]^$;{j}";8O>L/aM]"ׇmY<w%qZ{9LR~oٜ9Jf5yM׍+O{Sǎ D쬰8`y&n}wʗOO'g3lm3g=A7yw/>}_]9lyyޑw]Qfii)_X\DϜ0#ϗɋbyJ4jYģ#3zAxc,e $U o0ޞpH"V'&Lv;e$zW-Ixק $Q}y*CR䣯a4 y?D߾/3#-\|5ePi$rHjsP ^4TE_3Ve󨔄dx#D%a2ܝ&Ck]BAB+\_(ɌT#0xUH)K40=\ pVL1Y@ò[Tg W42=aPq{' 1Z*τ3+hb0ϵ=Bb|]3S$Ji74-q?-ۨ6vöHXXf ~evRߘ#T@=w|ΆzI F6 իG%ה,~Q@ DswԊ Mu֐8v#0LP$kNeg@!Y:{8!g!a!o*AXc_)3q_RGx%(%@OdWrj<_: 錀CVXNahm^{_jW&d ?/(J/eD]n?ox$bNG=`%u&ן10FZQl7nȑT %B/Fz؛zi":pW:r' n2l%gRi0N{[#ZS j wt%)FoJukRe~MնN<4-sl,Df?/*}s-=-;'vխ͝{/nsyy߼YgWƫr]|%;Y vOU>;tDu!аB9Jv bq 12˲F՗.]dr;Y8fl۲=Ͼܗ~󶅕a+ȷr2X ^,]8sq cSj<+p3G*XBX;Bv];Zsx^e U & p9&#irupfuΟ8tnK,p`y>aN 9U"K!3=p!؄)I+2D;,Ysٞ@_"[w{QvR촺`3&X0CEq s|%@LiŴ?^Op|RWecւ޿ 6T nv-+EPJ`3]ӪE%kuy._OF(8a,KFtMGKu > ?O\V(\ħ`/#%ʁ /U=i(qgԄ`7\roDAHHaH5+bV!zeR"9ahKaXYOy&J+qwwn]lUEdNE2ΗN7q0x߃Ke %ƉPaNCqvM׾oͶ3۞`z뷿;wSܹOӵ-V1}x&Qu9$&͍ 7"G uF$e٠,Ko~_\VW֚BU7K$? ]%Kf <Mfhor4!o&-yr2Éd.[X$3b$4368eڝqm8,ܷ kk( `a6MODayFPF UGľU>xyDFބcz7a3wr8^+̢h~@bv-zDoQɜҥU. HzhnpqcU./\YOy2\+_4{5xӰ6;lo_/]];z+ͲCǚ+9+;_\sUu W֙}lΛnPC02xqqbcZM -M\x={֟ox3)6fW_{wqia+aPf\P [='pb#g$Zrwg!C0S=kp:|KOxș~; .Q`G_'E_2QMI`ځT S)b}}_=?'O}eCn9DpGlp^ cifczTOԛÚ_Khs5z,&If־ht!q]QU.JcBf;W }#(}yߔvR'-Ma&Qq}_z-<#(pw~T>1.Q Ĥ71@)4t!-{a,ګ}K9I|$RO`+b&P$|Ze `qw5*|=P&l6aЄ B~k*II_?TE7r,.a# R,y)Q0Q2LW/ R1MJ$r \KA=`_jL,6kglDmp@8'. +§}($2d!5qO} 6a~i:u#VȺ&A!w9 co *۸SFgRm$]5>Ɔ/yPY;{pqH.'M.!{'>) (e)An^r$Piy3*^K LR2=3Y$dA98 jw:4RJz;yFj,  L !YNcMe'iNЛ4% B,FXÂP˧&2+ZYQD_Y> /eDRB}qѩK$ʍ-<40ݵj*߶WB";BS^Q扸_1@N5D5'! dKk+''TWunxlZ8Zvm;n}_/Obi0oVYvQd^W6fUN~~eXa0\ xg ^$x \n<%Ӟe4iaaG5q!KGU;1-}z!Ш+߸wf Ak/As@!,dː2r<nx3{IaҼ!d1kuICJxWsgQ5$َQT$y61ӠL<`6^s]6I6̀(%f!CG0 (s kLd%B\-K/cH Wp]#'牎G|̞5[ܥ %GR dO>T&NBvh1VDyz+h<*Q)<q{8n.XY@{8VW ا_] gd@xCMoGα0 lj&x_W^#lmm̶' zw}sΝ;S(Q~uhNɘ-3ykɶ=d]MAilDԺﺶdNF-ԫ  ͷv˭E$TJIg8Wcy E89ks6@I%RIhX,E!^cX(0ޛ'樎[X=\:EFl$/<$!TS3@ Zqi5WyܿpsY{:rwE(3(#ȯs#u$ƯkĹ%fuWp^R#$B&\/Tg\tu=9,glmO ىu=9 Cۑ}>yx859ۻ2*Xn%_,VXy0TA@N$̜d7wjg< .0l2`1EY,F3\a&cUl'N95ϬSή: `_8KY"f˲ V=|A9TTE&[j{ 1"5Snw d+/Na~FXk׉,~^JhWwR~/⋂+ 8$ֲbe{@\+R$0Ų; $a>5e͍|}I"{H9h! E <o $f+L<&C,?$ZNV^bTRXDē'e@tMe6g{ ʼnHs/&ƤeiZ#fdžO&KG/4G٫Zr}-Z-}\Pm8)O1zi)&?k$-/]x{sSw۷s;6~{ yQui#4PqڃQLœh=TMq˘8xo=o}w6ζ6zflKַկ~ޭ¯u;(G$lɚVWgS6u훦ɘfd{\c1Ceu\>fwwgr̙jgwwo8s@&T5 ,$Lڧ7GvJ{f)1& J}y&HQX4p ȃ&hn$עkTiG=۞c̔Ph㑞. ;zsa[(`L3?_Ew=,GX1݄/cP&&;ic\dWE( } M1ez0[ә$2]jH[<ь>30~t,q!6(oǍ\D'}O`n]#y-@Ɍ +ˇU$")e%ż(T2BK%+^YBD 3dJ>.:LC,g"fT(8 C"0I꽐%d## [Wt<IIEz߈,^+z(]0'9j-+rj†P}pɱJ CPNYgVQ1;,ʔ.J[2'"jsm#HBKYӐr'} 1*tX1#l6RQ;1`JXi^xqjÎs}],g"DH!lыɂln/W&3g͠i}w3{?d~@tӚ᪵LnkZDb]nT1\Adyz P N?䭤.r^¥k"3Dڷ"<= гA!Zw=obE3InC;sѡ8p\8כr{ML2Z#IQ4aEK9A "Ix_}MY'/:f.;_U/z /-yᴏledQUC{ś/.:ZL#x{[|p8dذ`C,}"d aPƭ,ˋ!… k75|#[o{flmnGa]v͛?s?Y=e0`y KAeI6,SE\EQ)$g8~pr¸vpqugX4?(E8t(>PbI+V+Ei7Ók>aYaga GPsXc;U><8gePNnnʐ2`ҨډWa|f ja%Ice{t9b0)cM>,<NC/ʠS @Aa,<.ɞ(06u&O1'0H| Mڬ^Yj{\YM{ HٖM$k|q+ 8(NJԫ5s}>H]eIapY^ uPKNQEX&(1- jdəP'?-fRlggwU yM8 $싂$' 3?o}콟#ypXy_Q=__ Vg:$ϊ67sXk RoJ)j7}֏7?3ߝ͊m̀y~~+cϣ}uXYPDv04Lϥnmo;;*DKȺXԙ4u}ڶŋs#ݰ@)H'ݡR(z+]83Q-L"@Lo}ӟlz?&LQOUt,f콞mτIĈo+A0GlH_w;oI `-#`^H18G%z.+$ D-cC:f>Frl.wez?]G2K $lF,$[>Rr^7rj4:SL, ư\-u69ӉWH`|+)A{K%ec<$c:%Iq8lHb`jw-,7(5FrMIGr,Fkcڇk(ERj$[%o.1 BF>$^B&dAAmg&Zf9"P'J%õJG΅V@Ds\OZdTqM%XzeDkDa C F2 b4`B9L|Էek㇠KPd 9H(E0#4`)q*eyU4NA&lsvNt)8)Rw@L/$=$HPR2p(,˜Yfd+$կFQ,SVĜU胓/ZyС N?6Q,"ǥBƥ,z>itEjUN&*jK<_Lkbڥ01ZXT<]T*GF''B+V-+enu"!',U&¸OT]-x<90ae5?sk&P+.[9}18q8m¥K% ?o?`}QPҍ+]ULd+VE[Rv6aT)|`]_8g.XH~ɔfW~G4>IWeYb ,OxḗΟ?_~C]wݯvmggRnmͶtq3^zɣ6sUU:7,a0X !(Ø tɗGlº~f}%Q}Q&ARc88yz?aiCpف,5`X!{xu6woLE79xSma~ÁŊ$m ,^O91f-K%k5ppjw"7GF7&GWF4;'kz)halLS+] T,tq>,~<_MzUe = W;%W݊YIhdž%RƨgQGdR%f'L1ԇ(=NL:W2DM7Tշ\m`yB dR<%_pf6ǎ@79%cz={| 5$L~ڃ}^ɦec$mlϪ)atm/0xetȢiI?*S2hɖe|\Dg5oS҉w/9X?eXMKk[eR}g ˞ӕ0PxoU{~͐m̀yw_z衧w]w.(F!#d|;I9Ǫ6HxU[[[1 K~4JuoNmG M,NJΓZ*5 ˶aaMZ:zb!dzL.KQ^RW2[H軓hA>ؙUsuMdH=FfðX$}{R~!ȟorZ!dIiE8Vps )^l t U ۾G)Sh@ۈUR7" %1]ZARgU RM+ࢄ ڠ' ɣ)kB8+aD&"oIH B31{xj' B!Dc-m%@ڃ"-bFxJS])m"7R̒ћYBd;cAD9òlzV73iXSu d5ŝއ\s@%}_M#'}fU й iAI2Sϲ|ѯE$na'I@YJAm c2н{<`32sܯ)OK ;*.6pF` 6|޶s [sqf&=7SUܝawy,Uu\݈WAPQ9Dm5˫!VhT<Θ O!k^,탴 (sT)ׁ<[Z(xq)Quɂ, aG 6wz%ꢂޫĉ"dIM; r3&s T7S,1 TG毽c/z W깭nd/Y/IOVgê9s'y>{ۿl,"?psZ^RM/*S5*qL^\\: PБXf|ӟ~ [;Y9fls{ϻg_C|׉߸☛ܹ aeF-J4\n:\yHy/(?Jz6ILztnœ~v!)IӻTՇ&Qf0"ĩ-8 :8k8}/On+;o2[ Xdk#t|vKFI_Sfi[؆UR"TziZuuF9@AR% nL*;˷LUg3lm3g=A7yw'n qu!az01q,ⷷ]Jq|.* omm׆7IS7쌶wv͍b0'M3c^6#-+b@&l9@*6= )=5Ժ(=5I"xu'́;2Ez IѢC`6*I30BIGZapSyfZNr! #v)p%KL,>RNsY/m?pg|B^&&X*T|EiFv! {d5q69CU#Vr̐ 2Þ6j _A,&k L%€#9~^h^k1ߑ%;` QN3`h;kH ,hb\qqyL0(Min^WIį [j)$ sΩg˳߼xg2@e~fTHM+l&YI8pz4m(uK{-PVGԹΊjҰ hbg0TCI׈+(˘T $߶:Q"#AA"I&oMEd:Im[᭠a N0s%i3չnnɋH6j^3Jc[~j~o /bOXnӇo?~G5bM-K<]Z{UEK FAK4-Y`3FB{F31cGa;3}0$6 $uikzzU9'ϭb"1J=K<}#SGb`WdBeRnx~);I;io2,L)[k{380(iYӲ7>1onnX<!zhq劢<̽~{3flk7.[_:W]}sPpP,XHxNBs@r%ny<8NA)gHX } ,17ECa>fhcpMIkzHפRT4"ɴDn꽆hqSa`A+72єbX&UR4rkNէiQSGo/lmm̶#~;)~<ikk H/CB|x Qe*KWU& A^Cʘ0+AL!Ü[[:1 xx ;88(ljNq&Ύs }x݄8֜% mۓ;0+HE|eJ|ܭu 9מ>h=KWgĕJa7R(HJft^ͪO\ 㒸10Q|.53F7y/0)u鲻sllSϣd%e ()Dn:o,)3PPR0:@}rZX *<k"3Y1a6 ۬ $"e(4Ht3UÄ-D1oI9|d2@zfϲ 7y#%+1["3L Մ"IaYE!S CsJwCz ,CP^2&<&?,a`vˀ?*e)5-3z(8a Q#Ud!& DW 2W2,{BuZ f[[@ v dՉ8GT9Ëg1Fo#8 ܏JM1*8WR %Z_0! J*T^XZ,,,]A.|UV|ȶ}s,F[ ɟqEbpFlKfټ+?sTNC#TBkf"5&  &m8t3.WF^Rm,D}l~4qp8("voomoO|B?C3gͶGcoY=yzs5w)̵8"SADoAGS/^o̤ !Sֶzʅi1jF5$d14ᚎ4ޒgO;J~s.@؝$)<=Zd("kf*2EvmҬhn-M9488& |@rP;9rȩcGuewwY;awIfP`m=tPѣ7]Xt RZ;J +J\Rf)*qms(!rZ5 }fĖ:퍾ooQ+D+GHP[c|[J!W̷-0vHa,v$WNᵐ]4MK۳/أ~%JIiG ˥O7C)R%CK&VElNJ`0x#ί`!8B-!YJ$51StC 'Rok%>0rK8Tƿ_?гtl HMJ@Hwp$ !@B z g,򬩸/8%>B¦Gn3TDsϥpCKijzsH(m N=QRb ʵ ;9گ~[:|flmնO[\RGq1.җ0&&yRY: Ma9R,ἀ~< ?IseC͞=.) U<-V+! Pbs.傪9 Jd311xT>OL:t\K#nSUc|T\m1iymd]H5\A!4 pm6$%j__ŸHb3#%E" Br~q JDkReQh-XNAzMۨ@z*^k.a!ZxN 7+,6fLɳZ0{- M[Yf`1cVx ؁Xv{Eދ 3yA*X%73ʇ4IUAH~0okljJ%%ۦ(2iN*ndTtpFb8$V-qUǯbD,v5,7R|[m?wlƜmm̶ O?pU/ d888HAɓMխ⹡ErCsyϜ8~Oze8;;EӝW~i`?7&SQLGC~ߓd}}zC=~,ϝ;Vyd'EU30btUdHZBETˣifQ`ڲ kc)<ʅSY5q!|# 85j;usb<0]_%HB.b)ؒ ˠc%1%wL4i2q ҪܑVW7Ӈ駭ť$I ?bP/VQ5'UR5uC$P`(4% ITO'A6X/YK*v>"O@* @E(w);ı@1rx^(y,kt!1\n eha?;PgaKj=? LmͶʯ y]<'ĥ;/O~_:~ʌBa)2I <#j y=H &|a֩Y1~; u#N{ux Cejd\@k3c5`/x./M j 'Ϟ0qHM^W곃!.k !TՂ sʑ5j)A1<zxneV186`G.ģWdkH4L`]f[MXb h,0q !`?lnXV %`_ 3T2:ׂiu aqbTi~Pr gb?3 :FjfhE7yHr 㪽%WП~d t*VCD/,TCބ _JB)mF Gdȹќ5RiTV,??dK4H"O\Mb8L-;ŋ.W$?ScH-M#E5)%fw#4WL-z` kE5`ˋjkSojDK&1XL]3چ) `q(*OJwoo|7h>}<Ϣ6f[/r6x7oc<3uQWen\;;ɍrϮO^/'x`F6ZEI#7-j`BàeHQ҄,ռn0Bt C4p&&/ iURn K6*aP3&9 QE9V] ~] hY&.x&/ kMVp&e_ Uu-HR)+avl냨?k6 qd,2~F9V xJ*l h^0"ľmn|qmʅT3r)\`B$U wӬ2IWSZXBbDxh_$2UM#gFRm3Yap OQOr+ )E6'gΆqnc?0#0,8RM>'竖LnDqzULIDOׁaƂ5ѳ$r;o{xE?Fu8 úkt{ c@Y%t?'y#rpr{ڵKo^gk|ciY$ώW䖝9ާ_n8OY2f@l"?=vcIlf`Lj, ˴g0|R< #Um~WZ[ݲ0߻myIyw7Mu<ϻ~nȳp罼Ox6ʧ_|흝YYg ']$u4vA0U[5*BF[QLyd(lt t;GrbH66,wޱYiI W:%hiP-ɂ)Z5U~*ߴZ{z`:o8IY8LC}#`R$!6u#-;pM\swrS| ;1/xrҹVX-&Fr'=Y+D)YN #)J ؟J #t QA61##4S fYN1HN# ce1Jq%{AIZ@ad8%7' NP:̮fo`᭖vCB A&3v,Kcpt3$6_#dqiX(쉤"C(C& h!Ӎ &b-L!%p"5}Y6)LVTcQJ k1Xy\K?|+RGbY;8PBXEp遢aαu{L,:i78nQX #ZP'IT"Xbm68/~ECmҒu[@30 XGo{{ &ณ IH$ ;"#2(aF80;JQuY:FrL&@會a/DJ;&t=L& BϩE$ntx$aa+ _wn_~>uo˻E7%TuRzHwUO?=G28?!2v[WU=4Yvi@671xe^?h8`}[gV\so8-5"-8Kh ~ f|\k0~s{p8LwOpj3)6~W17^|ڥϟ3"9R'~5ZYqo9$3rĚIn% 'a-:0*kK\7N LIUc ~.*kznbZ|s3k2'ہW ȭB7'`_݉ZI$TH$)'d~FKYJnlg{IS@"sb<Q_@ ;jvMB^Jcj* P":DqDmr*wX#\G[9 E\8s4l\9"#I^ZGӢZ(b٧JO͞RLR5i_ElaѾVD^X+ER/g6 39M||YM@>=IJ.bp3{QnF7L"dKldrXh\$/Ghj68&#mIwk ĉO1B|A!@5j~{9'/3Bu9uZGvcr)Nvϟ U`Jȟ ̩z{Ý|;go}Q_XVM;calqO~̶3ж)?qٽhd0 `0  W,V-s9Rg,gss1N[t7ey>Jhe >2HsUỖ..ϟvs{{\ e~{^r*E ǩ[:RV5}"QO F4oKkgb 0£\+<9]ji)ԡ7D$fyLφF+dRb7cAM cD[s~>pPlsn*D[0c#'(Hz9&uȆY"@J>p&@ !&"+fq-! &ĵK@,H7'rUTlUDAUܜ@G9 ]^I"P@{Uxg,ePaYR;J GO͠j`VfFqvd4c`ʀTPf*]!`f1 H%X2'@;)wW|ck׮}f=iS4#KA`e c T~eYX =Iӹxܹnnn{? '> C6ov|V/Ƶ _|;8?anaNju(̅3~s7MȖq޵ k&Jk4&֖XOUBA'#`&c:  ; LQd0G-YYN_s1lQl0W^ݤ 륓%M tC$-2G Жc5@>gTEfcӨʌߟd3I6>@ӍlmUP֌m}aV,ܩ=>k(Q&&t؛Ǩ(\hJz ^mIyciw7H">ff !H~ [N1bF`DRșQ4Y9[oL4 I&J-E<dT=M+lY.Dujj(SZgEp'jw0eljE`1XM$"6Ji=Vm5FSՔL[OOҵ%*/~ 6FZjʫ`,Z4*+\ʹM^+#?7#?s)H ';wޥm_"1a 9' \6ۑﯭ`.'|1N&>?rs??7c϶3@?O'xBuYPK 'C᪪v4WW`:G߼1>4Z_MQ4yQl=[v4S^T4w[not^8ÇL\!gΜ+^Lb2<,(tH`t$0v\ͩhEO[bDџ^gj>4[9%X_m&pv8\O*}E]|z ŃOOXu᪆-$ԃA㊵RʿSJmlr8c$SlZ L$}Ȭ@Hn\FP?D4t$6S@vJ@*H)d~߱Oa%p蝣啁:9H1K_4)k#}jfn3-`ʄ%j*M/HzkBp k ˆ@(c08&H Q1G,&@T,>_h_dAU=l"#$9=EǏH8x#!b/h,#a)L؄%,_p A ;=@{8gC,8V ., (ψyQqI<\G\dEU~yY8+&d"/,ӌGlǬV~R ]p2M xݏMuZ$hc*;d۾ R8b6Z'%.uUZ* 7uSvR}8IILJHW<=.p{NUfUNVذkGESͽ,RϲkjPʢDIJ*ĊI vbtԡ|^/臶=̇~5[h&d3/,<>>qu=U 㨡D/,i$_y ט&wv;K+$f8&Vo0J&}xWʝ^K˓kW=7)OˈtȬ4D`_ǹqe]sS:΅#>c_"6dn=vW^:_yOw/U~fٱbLYB/=7RyTkJ9!ji~Ib0"enp2>I 0zķaMm_9'P]>kmf=bCk|aŲ*(;Y[㶊蝛:aLqD>17}Wy+ĹCm^Ko\߻zK;jd<VE4Х0duGXPA">;q*g|Ń3oHg̀_uCП_>ܹsGHVu ^,Yک5y?*7Zz-p`k|J8 QDBrK)L MRIӪA$1 _IؗHw\R(9iX)Y} w3#2|% ݶDu++NL- 7H$ MɻכӉ`4E-]X'M!v1QQ31qԧ!|b@K^qsmz|HD&j*Bi8BPV,gH`0cZ7p49HD&EQuG!ʰl Tqe%\`ytrLRUi] 8_1%@}'oXp@ 1B:ϳh0fJ A a:Lb#ȃ:~5Ac"%SדV3{gk-9J{f!r끗_~Vtw^|<ϝG?Z`IȮ6A%|?vYt&_3{Y0jiLT= '~Irm/?8aI k=I,.]ˇ7˵7}txYN G `RC홹2 VUѣ=wv}lw/gRnm}oKomlܱGUf'Ҙ$$#&  >SwM#HFF Kc5R:צ|y{ȳY(x|Gb'OT+0g`nfB1 alìR'#pJb``t)jljD Վ(=?e$*LpФU:z~&OXIJ`ʅ!9[J7:W } lR,؆^A~!&ք x.@g13o#ErqГ{XF)9-og5"EXrtYz4`p l ypL`os Dt'YsLS3 X: ʷD?gHpP$Uz~?.c "!C~9I#e]TӊI-CaGޭ_q\"!ɯ()=G`2Rɭ3'·ܮn9u/I9W̔%8D|ZT@CC6k#UMD8q%'`_;mw=EnD$8j|s}h~a`B(aB^uVϟg{{{kww__IͶʯj /ܻ.w{3M7W~݄\7 џNq"j{_W_:7Qs$T9.R<$S@TPhkÐx6sI1:Y8 %Ί p.M` >:r8@!Jan!Bu39հuK[>`˵? c*X4T#5X?OPKbgP_X->tB緇4A;-WFݴql@ l.LUnꨍH$GǘqqWS>h_44 O8%,G%_wpjIЄA;tF ω-eTFi$/+0{=#"|D`2Ս᫄\@u|W*"Qx8ov$I 葉xOF"Qt&AQ kd>y9v\d oHcP≾Ѧm \d?JMmBԪmY8jԡb>R7~F(LBkvr16amQ$ϭ7g^}KLl &[&]%Mtt KTnaΩiַoAh¾?6ŠȘE1b ,NC$vպ7̈JU&@Ʉ=\.RXƟekG@yȗϘ&yd*4#ɀ)C^."@jR$I(:$LHSR߱t YQfU]Y60H^xfcfDjdY/h񨊗y̮wߒQrE'(\[:yN~rXS|ܚr.jfd| h 5` R;`l#$lCkp%@H#`_P%HBjz],2m$^){!d5豭ebi+J 65>$jt&%W8rʗ#C;+mQ3-RDU4Έgf^4ϞS?}کy] u0Nus5>=Jlٸ@+YB шqrY~9NC@-)^-o!mh^p2M4| {JwpCցtr!3㲛7W[W˹ߖ]f5|fUקM@AfVuYsDZՁ?,z (iV!23><|Hpv r?9ǀ~Lu 0)7^7U] ,$IFO`H+o}~W=sT=҈G@PJNbj:2}T\yi+OHFF4,PijR5H5K|&$ѣ<;ԁUݹ_Z:scJ5AţGl"=pv!Q"I6-O{LУq2=K "Y8ʳ+Nxn4y89PVF(W^~k=9[Jz"nV_~*<ìۅ{2m 4[QOJc%8@WasޟIÛ-@ÄivN{bTϱ7T#99b}=aAUCRA|>Mi꺽޷^|b{oMe|,q,H3c1x~;,,,8K! (yǾkmmmi8]z{˟'}=m1>sO}'?^={S;~9~ufp,0pdF=VMuqZT4K H4I:B<\`'O=zEh!8xN1(/i)K rVRf5Q&v0%#s{a,C KoIGBrƅ%tXW~'i2 +_4j?|ǝ7S+;R[~.i Rw~ L!I d1AkB, ,U;fk@g!fx3B WQbf&!6H6[{O].G+P"mČ0Ϧ{TȐj7Zl +y:q#OX~c8.ba/>eDA6eal m3?@6@$Jkf:q?4KQ %](-yi'3 1uc6G `{ OD iEA-f͵@i/<wNnzoq+[/>{%]o/ǻ$xQwh[`+"Sfp2GS;1mA'o-/>qA_^ks_]KNYϳ|ϱcdͶ36y~y;2m`% Vrur00V搥Tշt- ߷pdtvmEqO!)EqO 6r <;S$9쏻CX`IE]^X덟xխzkkˬ^:zz+lB7~KaɐD-M p`Yዀ56bS3L2m5E \ĒNNw8q|Kwm>| 躭 ^YMlß}ޥ_O3LU\ԒB㢄;JrDtVQFArL=CIaqaᤰ4sQ!fpEn 8dG x4s$c&Cf+u}5}dNL#d؉f1 c&e ~CcMX Bծ?*'Ǔsޤ\O[d;[ڀk*$ +y#,ϿJڵa  G5>^:,7^]v/|}{ߏ3{fl}_rs_wf=^Zr-ЁgX0$Q LY 'AE ;Q;kZzћdB PO,ΩY+5q@ s($X:Z o9u "IyYF]5Xn,ќnv%21G?"u.qI"}224HC>=80o~km\K3/ow]g>{뭷<$M,?~1q6zf w?ٳg/ȳ_d0b`I²}`ҨNGM߹nk8TәGtc؅IzIu)ɻlnxUչsX%:t&L'Di@U(j'ezw{5 ҂}Y2ƑZ&b$f9\1B 1Q'E"L3'HIO,$@ǟH%iJjo(.JfF֫gS{ \2 !M0P!;1(geʠ 1a65USĉx'MC1̬!9+\: ba\D}d:<w8a+8$eΐE )%=]7 o(KVw~J.YSI,ksϨxEYgԯꥲl Wq+Ϯ/\7m]Pt(YxwՐǨ벚L &H=s}{됀_Һ|-Fd G̱Ml$߻<UT\? IGryᣣ2hj&1XϿn:CWV~s\U <MEd[xvW͹q,Cx~=裏xů?Ï|{fl>яxC㮅9/-~fZ29s??8E8m̶?2g?K{{{!0L@$a@áxDH~z0iZ\{74_ڮlԝKyN":x y$Ne~$;t s3x`ᴮۘ1ڸ%&V0Kc!]X\߿0ɓwkk.\yzU f5Yݔd@5tDoP( 'sFƎAh3Ԇ9ߓ1'.7'ykJ5Y7"(AirNC0Bs{oeq rYU*^*ْlY6 ai94nft7a 3n0Z,Iek-բګr]"&%2}f4`wIee7 Is}{@6JoJa,/05xqͣ s/c&oG::SuulV/AɁ1Np{IBz[budIoHy;KHGSM \`>$f}Y,ЉCY/0}FRrRlԐf؝`k %pNC擠H\v <kpFIIǎdYIš6s0&3mQC@0fug 1dRz~ $*f63I/D-m_Ėe\d]@ZΈOT6Bf'"2!8hK?5]/x\ ce2; x/85OpP\I]Fh5eY ;#Jytfcxʩ9/z|)"pSBSOF7 TjD05BT:yYNxϭO:7R_I]iz8Ze[^o?` UO٪S(/i|ڢRēVcdmw:t͗.]jEnm2{6g>C˦qViŤ^62P\I+?Y* B^~'RW/3,&%%9 , /45d0` ֎VǽחD|?Jib!oAoK,X7}d#Mg ^?҃+Ty(^DyW |2kjKC7n!bqIRtrů]@6Oڪ߯\9F` Z BMۇ8.X o cxx9=5y{V7#cs̽w߽xĉKݻt\ T4Ȼ %_a)V =Hc XM:Le|qVn iEz$fsBW^55A6;?8[V#StcBTB%Cs,ՎkOy 0ŀ'1 rgp:C8L RNV)R3BwÚܞ$75a?V4]#> DŽGRR"+ԈTH_haQ1'c | G18s;   dJ%)x nydD;$[St1K!Y UA G %Kc4mf ;ϊdSh~נk%4P3 ¼MH&;֘KoUk}g߯oof3)=gא`V*ME 2s 1Vf 3$=9ߦ #nu|Ed  cCI0%7ON&U%BrĘ֎'@[wx*Y`k{KIO^#9!&SX{M\ʓ9K7;^0~:4I jMZl=կQ'W[ +Aqj]yw^82 *Kt5+gϕ=s:B.-d`Hd,Qd2\%ErEU0Uj(gFiJ-@BV[1eM _P }h̥ z6_e遫oآ&5Kw<(Ḇ^jdߘ 'gkY UgWP'e~aᓵZ9y2x#5(/A ` r!/#~`f B 9[W]b6vXrD0fc C:LbU3yxCA7 |t5ʺd=n A%RMZ]Tgs. aKammX{ao\FAy$f^MB0ɚgrl׏+X) _ .:v{l57?…gN^<W^ٻ']qEgܹjxQ˴ՎM%yi8M}u 1D PZx&AMaUN!T{eiIo"%}  )K_#@OLdGo-%Pae(r,JQAD"(E _MIBv|-iA."WIf{Hd4zrx|QKqw|2aBIqpN%-CZHC52a5 9IrU1kUgr^Ns7b{;x0HIgʒ]|<Y//GQ j CZJ`h#1'CIs FJceJd|Y>K%ɨŁ[ۧ%6~Gky;`-K zz0)9-GPRsyIq? YZ4,.V<|$FW "/IsRTVn`3d/(m "$KR)Q<O@7#w'\l4l "l ȝˆ y΋y3K] O)Hxu4 %SG:6J5^s1# }x] {\ZY& ' _7-Ԍd3'OƾP­uTzY98<V'^D"*V7=訐I@M,)k2no|?m9PK2#0Li :Ҙjs&ey@dHp )Ɩ{ݶ*̙I765HBŊݤ8fػnj -K&'H42nA3 I0&A~fggq*mw?wEY.*.wOBV ڍjI;"̵/l,I!~yeN<>zfylwǧW>x]ԣw -mEVվr(rgf(/jv`XD)E̎IWgf\$.:zD6r$ $%I7CYBRlij`dzòl$'{ucP8#`[gyd 6SYq!4kO?dI:U[m{*A\<fU,q8䚠ϯ'Y!F oQѸs*mExT~-nw̃ljg!5A׍U{$>)>b04Q9|r<*$2$SZDiRKyGRQx̺YʍHzA!;"wDXs-lĀ9ahBǐ2#I~Tb@pcېT8%h/3Db;Ά!Ӊ<1)Q˵AR;;!CŎք~15Y#Tx{O7г?>lg =U!ʲmDX|5bc0KxY9zQI F#m0Ҿv'x׿]|n3<6[>Z:uU.X=Se?4vr0D^~HQR =fCP%T+f_F-/.ɓu8glI$œeQOPWT5l,WcaAXAkH]Nӿ)^zJ2;RbG6\Ė9.gޱs[V=|}/yɹ;wε''{':@69Ls,e_*>|%Q@й 8|z<8;aRݒg?.ư%&$Tc=MPL Sۿoܵ1" EJh "!FhzOYKR .ې1!.^W̨l[ʒ̄ A>Zt t/eo&y9'_a5R 1;/ =0ċ6^oZ$gK8&GA'rK] L,kRڹl:68wf3Hy<ǿ?/흻.C&DA&ynKqi%'/ @O0>?3y"bܲm6;ig_]ꢲDO H1{s >:9 mx0Q"b '6'tD2dp8\"Ռ$f@)/Gk|( Ǟ7&4"ajF PnK-,G'{eJ*ULT0{?CҭTa5{*?mb|X6u d+]ըw䒴l*߭:eUSS` L jF8 EKkW6 ̠ +jb vv)F`A[ۥ(XsӮ-#C9ʎȴ^0ǖIk-*:<\wNmN%:dFD(UGiwɦzȆHf,Tjb䑴L/9F?Ӱ(H kfrjF ȓV\xGm_U{ O7j}QDo{c ; 7=??~+G$W_qsO|ϡλXv}M%Z% ai?k/tkg%)pîkʃO]%nݲOnF.&гy|ϯگC=x&sYeU0|`0D+q/CϞ4}˅@!Dv$^KLEq fXYEE(4RmJŲ*+x&l s+] q(>p< #q %yZpVՒ wDO V7ۀ֭k=@o|cpd禧ׇQT|+fbx c_F t03x(-a]ќqV̇7 1/a#`(>^>%S4p kk)ǕqCB`ch˷YSWxq=[h5MJw+ʤنS@)Q]Z2y)mCr;\q9g68w7 a$,=E|ôۻX2 %q";g<$Xr2NKyLϰ`o|XϬg@A;KrLUX"Б;fϟ,ϩ IdfF/xb=`8o2Tǭ=k<038A,HD$>~$q<<{OYb/ vA>AQŠP99xl! %ڂ߇$-:^_$Szl% |Hs{(sR?}dE  cJJ[̲^ZC"ӅƇg!0m1W(>|LOwJ~(.\{E`3z<6>ǝӖY{+OI"?,z]H_6Ku`砼Zٿ&h9KE !>se݈{^6bל))0ZMsنk@AkfE[j9O$Qn @$[J$^>eؐAVF:w1 w  Qzv!ڐB^?@wibs+Z !bx_~߈g!J KR~F &v3 wbh@` P C3c!s?l1 $EkI9JDQR!0d4٘Og#18gD56pҍ 7\4k$Iޅ{[GfmA^=g <9leĈXɯ7I{$JLE/\9)"J7>mw !BT lOr7&xL#2d5"+AͣĨYÖv_\hd $< UžV6J AH*_ d69 +J>:e߃PvgJRgT[jAk_sO~g;>/=񮧎>ʧKь&ߝ} Ϭf|GeH8kSPYq/]x_5w lV ϝw9r|'v߷3sj8//W24ڌQ-_I"`HLxβ񊥹F-f-˄d'Id)GMCka)%or&W*bOqtWq钢S÷ޒKMRnyآaIMl(51)ēO[qvG#sA4`VyI!UˏgN bp }MА}}0"pq= ~Vhgq 3n|VYZzi1~o H`ZN2[ 2ǐ$> Q<Fr {DS2p#x]!w$%~AHr>c*2,'z̃/RIDJ)b'yAP[arf$ .#}6A%Z[YI,(&~E{wQTC):KA_d "hYl\f^G(!*LS- aA"ߘ]54P3DhofhجFԮRu۬=l" !>\bj#Xl G(G@!F7VnL6u|"ԕeX<?݌UP>k`LH }` +qV w]`1ݧ>;Ϝ?󖧏~cKOw&7hQiƇoHɾLbV8'[f'Uwlۺ/G~fyl=Ƿ wzj $+P ~OM9ilIU{^β&;/YC[ ՚p^EĪ ,OR,&t6c\쪒 d\]'A!]<ĊXr1 4Wy3d(,WHKwj:#\du4E˩9|!\25ԥ<әݵ+޳gmԤ+˃ں3Tu@'Mm;a Uş% '.EdcQ9TKKcx!$bfpUi $,AeY\?64 ɬay/Ŗ50(j GuVkLxQ!q J1QYR-+ UZ Txpm(Hv 혽^j!0.=q$bPI>J|MY-6ψ6Aڗ!J9G/ d>^pȎ>- %]!JPu} _+ d38F!os ]\$8$g*}30}wW,cדV?x}va0#b"Q%YOHbhw=P9ll>3|>.uУrRڂ$<^@9``<@Rx<<c>nG I+ub%U)M]ĎZGqjfxo y"07%6I잀"KϞm%< Ò+iiWF 1El  P[hR#I ˦WlںJ(wC&eyk31b ivV4qf2`sR7&JT>D6~t0MT\۶")ё"|@01Em7QL[`\[q$xdWz^Pi Whyk^w{g33?fyl=Ƿ[[СC'O^swzEQ@zj0h6K+% '䊩v陴ZOd)V4-)SáȳL ,2n5,#cV] 2%z@GnR4bE75 F x|FN$5&֭ kmz;*iRk.j'OwO뿺-{^'EG>s]{n߱~~fҒ*ȽJ4yIql{`"0|dDj`@\ɚ=Q>LJ9aԪ,qE̽={&  ,Vn_1Ӱ) FA97榏Tzǎkv2| 7t FyG"ad<[ XJt%T%kQ|Uxߕmb#>ߵ'KdPJ 3f`p/}ad2)cOF@Ns%%D*B3O2j IF, t!,1cpGL,5VRQ[f|o%s4[' Ə4HVZ ? 8`` Ѕqg_4)6W$VD~ !(TCw` I4f)q^mL0RAW| $,`8젂}dLUUfo}qu-XbD ؑ1B~#4Z|nbQEA- $K dS18cםG,3"&oQ_y^7@ # qUL<0c?ߣ6T5S,SO'":\^ syU} $dm(Du.λswNq?R;v,8Hm,{ ;_#ež_-J`|Iws=/qg?sm<6~O|?497-n/jGDXvx;٤b|m@ ,??+.hX+"]a^ll 8t^ݳ}uB!0CQIs߷5K}k'a_kww"-ze?HbB?dG3Qy_yRgkek*i}2{T(Osĕ 6<&1̍8u+.- QE@ڝ2y~J[@M3{ V}\b߷tDm6&cjcz/R5 ej\s%;[Ү\0c˰R5z|e^mͶF͎r#xʄ [#66(N^ڗ읬wPu9 >l[kֹg'H*mbi2{8K#%H,&mQpcv1*= cR)&?x#]Q_{C @1Ԉ5M#ȩ#5"߁e )Y s$GAR-dnPn@7mCŋO tE+ww]?|5.cOO'۵27̔ϳUv C־;7̿Am]^rӫ_ss떭wvm lߖǝw)я~O|"r.s`a~2 ,eΉKv^Yݴ|pvbäYVS$>YP +G kwM.&{P*ܿݻ&{W((`}qYLH$8'cܽ6E^" 1[_ٹG?~Kl[vAomiq8b Aی|>6/$ >PPJ2aa@Ye(942,{ѰP^I:>3o1ᡇۨ*x6,ffxZ:nԕrcR"rY#nItTr,=,fC[K^ood>n%WiYa4UAXE bDI}cxb&9aT"Kz.d, @Ipl=kU%R||X2Da Fܠ - 0f Y?LS*S8Ρ՘2J>3Bۿ䁕pRVRqDk{ԵE@ `$~; 'CF  \ZrIC@ݹnj"쵄gP2LlXjTi@#K ab )޸?p.3h%!OEܯ 2ȏڤR/-)𳿎 J(NRF ͑'M-['İ|[Us]%757W^"2kf ſIM9Б`lٲa7dtc Q sil J~$%#7T woV-)0kKK}ax3)Ӻd=@-LJJg-ԭXV gbRkdF&Mj+jn~Fv2Ejj3ȃaLB`T!-[|cza)Lr TTQY~?H i&|z\Ø(q2J]}w]jI:kWP(Ϗ6:(w  X0;ܳO{~dv;ww}723$ܳ붕"y/vV|׋Z;' t:&Inp/}>'{w<6_gvsa$N]^̚ɱHN$(ǕD8 MRc }yhmK~+K[{ Wxϙ{ē'Jq`k1aB::Rn$~&ibU٠] <=| ?O<BE?`xI+8ԪIz98(΀U{Ti;_{2lLjиU >(|=O_`wv KO옒^{X#Ԩ׋ @c "Wjj(9&FBl$,3Hnϛp$ӑ\h喜O#ynf]x"uxMׅl Ӯ*XRՠl~_{ɘo6!2HKk5JyTۨ j?2yJw+fƻ}>o 4̧]zś# <(mve)=gjx,n'݃/툇Zf|V[9TmH$刎|zƨ~C#&_k!V}XO ~.a@F0C?`E` $,Ųq4gԪʄg[42Ԟt[*2+:Iʕ|o^>lτx > $c|hKςAHx>d產2m 3zZz#0@ u2*9HZz)J.deٶ8>I=K N [JJ4lz~Tzz >/fMAa>n@R?iT 0Ct@$~\ g@i;~yFix!K7bz97W7=H%PIroJط_bI>yIFU1'%3<ހ](b*L\N<{"u צg{rn>6Ǫ,p/fyϯZP7?po۵.XŶL}=K#͉NXۀ\0Zh6ɷ"F*ħXt;)M@HzGPLMB`0yi!#TICI.Hڃ;%}.\sV$+pb]3C*wSޣGb AV@5A?Fn+ʌTà0TY1WTbWB%`c1/xĈ +`lCU d?1BxتhH T T,"f"(( 'V-:uEa)^=3ZVk$ ~Qt e|_VgzʗGU*^:!ze(e \4`C (0FS$ t zUL0yn> ?s4ޓ9ynC6qesm#S%fpl(-HF2cqm!HֽOO>=֯ Ϯ*q-]wi%M:rc0,{][lśg_WѮ]dvfیS7c? '?ɛy79rd 󲪺 Rf1n^YoTh]Rvv::vdREiuߧ8LʪR;L3u2a]  k%I>%/r$#kb*A к3RN $%ٌdyʽ3&8q:|@iK2(ERW\|K?(KlLǀ %œ? 'Så 4\ x."VȑHC57!FҶT^:mھ]}{)ǐgؤ=eRN < Lhdx[zNs3}f%,Ǧ9ufoGcQE$Ɵ1&:gUP H>wſo9|"מalA1HKah9$R""܍c^2.{!L1Sq: F~̔Ċ+yKC2r TOQGQ+؟H &G"` EZ)r fdȒە>A!mB2j?< >K}KoB/EHMccxwqq (o$y~[悒׃ "ih6,rziQ^u3uNMgvll{@Ckh#}̂4(,DK/0e H5RCRe` &gcȿ>Tܷw X y9K@pBSCKH\fɔ.W~=h]l\mR5P_jݚ>̱/g-q5g*VeYY΄.ѿ_Zyd= oמq\AqLK_.៼|d<6?ǟϝ8&&y:nV#O0Ў#JRG}*J0?,*b=Ʌ k=x=pxI|)2;ڧEjN"4BhgJߎ}BroY @"N LE )`k]NjQpGȟH0'Ҵ֑w 9+N Ӱ.f_A)ZYj&!Hb*H.$$w0oend+KC_8',F' TJ6>.X#}DZ۠I5Wdи@/\Wqi=. X_}SjKꨫjg0;% \pM)#Ԫ=ZH^K Qo/Z[Q^9ar>(J Ӝ6cY!R͐m2s@T^^lw\;[lFԉu_PiPfxG6jyCQ-dAp#ؒ\v}Rx'YV>-MNn`X@1 `FM`<v?#\#c+e .;nA!L%F,<+qG>O{/9qۑ_KG[nͻ=mft)PNj T[ ̒4[oPU*n+^۶n~f$yl@Pȵ}C ?~z(m |`&*rMAUC2fjjJ$g;qfYZQ[zƱXAuTςbl:Im/*mb ĉ`B}0d $rEITL*Q. )&@}F8ngoUIHHV|z9=c׽X8xկ{c=a(&0y.d>u'@__vß:ؽgph8cn̪9fIXZNl{M̠}GRNMV30KDTSJBP=t(ڲe챃WGT5qD-aNhv\45B6p_yDk̳6:96UxOé:\0ްf`,ek/VsAs_!˧&GKAqnh}Lx,z"EZeKE PϫA-JP,g0dг'H` I~A^{%/ MYǾ2 fP"$r*4INY©bͭ߫@3naQhYJLq2`F>,i`[F@aA2Y"FQl5+C휁)HPTzj,eYJ׋`? Ϥu-f4'= (rg+f ~fɀXKb1F!\d*hwk`=]Zi#8~ d2)qrcGn[EoI.;/}^T3폺Qgs lҮ׋F ;7/eZUaDGIWWy]z7c;Gb;xKx@ҳGn䋼l%˹@ =^__| bݚpjRXQ61D6%DYD2mRJ"w$琑DRri̚:KK96^NB KQ}Wod3qacv31huʁSB5H_[Ep|oD*ܺ*}lL,U i-CGo%Wi+קHs!=L$wTA1r}ި9^! ?8ռA&}vsqVIU,3HҏszKAפ)n9j8 6klE%W5%<5s5N"r84_l < qe 4t!*<~M4NԧmX f4EfOXff{pK;ٔBV Q e`aԫƊ wl6ش֬m J2hL)x@%QvV7 xFkGA&#D L%f5"q%eb#VDMkX0 L|1M`Wx;ֻ̻'yzW_m N"D|FPv4L<(Z!H[!oݞ;__vlݺ?xr3 <6M#_<O-//GQt>,St]*U>bXdMp Nɲ7_D\4M[I5dYo*c\11lSk*<)Vg뉬=>)s2Ƥ#3$*%xP jj^ |{]V0T^Gb`%e?-ո&1|2LrV6F!cZww]2x2v񗷎?QeTe5/&.sʿCQL?{wk;չ+'淬(l__~<~RGÖY)M%9q^D`.%:3=cȰo>@'safYHAd]PHj۩aΞ%$Q22$ij%Q &d,1%4a R6K@Ȭ:ڂ|nZZKT>E5L!YdݿIxpm2wR [HRNoҭ3\Om@衯F Cdc6s1\\y֬?sqzfJr;Ɩ (EmcF6>"7<(^nS" ޓOjyӆ#qeS6˳y38HicB_#~׵cC 1 l+CeАcsUtuyp]h] Ƀ$*-VJ}:,Ӽ (ご|wz])m"'EQi=a lL5;.kd/wuK?eGy&WtҍݷJdlE=`l5 a06Y(T8GVwo_◽ox?滛MWн᯼NXh:XUck5F nluQk.+/y֟}9-oǵdm[ր[ӏ{>.F4*O0 4.5^VG^$$QtE톽^/jZ}uiq1eEY,PJC=ʷyP!Ƹ4p(~F &%6 Ę0BI-aҘ=!(_-{K,!Hڡn88PT"$d8CWT]̮@Xoڼuye|zʭ CQR<(Mm7/ze8ٽi w=ebe>zYk[\.͖ 'noa,p(U3u b% M0;OXHp<=SZ (qEˢ`DŷK/gqB#G%{#PI"=imSy'Se=߷py0h)R1VQ8C f+HвHd?h00D&0’eJtvK[5$C`Ad s7 xf%zA)~15%Y82;)9M c^\@az}f1눯f#v,ذ*=^@㌲S#82ue~X]ttu][oU]s5k`C_utw[o zXy{ sHY/̶ OC&’;\LE R<{ea<,asW= ֹ@ZBj H#$?~@?2ڢ )pK1I 0Hjf1C ;#R l:{G4O"S{:IFZTLY11!@BIR81C#/?p1|`f^ h(`FAx(g٩$-,GH1v^eT-&r4 wB7TPS ϐB4"D@IV+yl S{TI%}\Ӽ~:qŬW#!ԌF~|{rʎI>yT12j*2U0CSɴ5/5.%gE%%)ISo WqtEDZ44=rŠTM(ESMx ϸ w<@cm?mO{@.4lU>S@%Uk7W]{YƱUϘ}YT[ÒV3_kƏ~>t/}/ᅣў %1ޮv eO#W j ]\]ɺ Src@fqcg'RblĆ҆ -dGZmV+;{ z_ UYƥ)<$u?ӏ~%Ͻ%=>yٛ9Gʊ]YY)ZBdrU ̦t΅J,v',Zi+$ںeM[k7nSʃ(ZJX:`( z8ӟfUBI B JA*χdBCT)AZh0C--\$rQ^D@TzVJP4֯ ^b,\ŞUCz>FuX!&*d~`x-%c -~Oh %{94ȷ\GK(qFanSݒ\(!|n0hIݠl!0CXRi#PPtTPJlS6 kEn6A.>S F8$E0/G(fK{fIДt;bBV$(9.m )`6Ynh#bP3 3vl5^x)\\O!?$nŰ1+DWA^[`}g2I{Ѭgdz. *,. F 55_4y\J_bIup΁]o"7SFևmݍv5ǖ)7'&'N<۲,;7Mo^׶ͭ.|DH` ʡIJ˛1d@e\zh) 4ve%KhX~l xrliVbk4*bo Y-U%iGg%N alJLUj]1\Š\['T<@E2m5T{6TS_=TˤʯĬ !ཟ: GRx 'WOio"sBf CyMlZ NT}`<4MoGXgk u0!=m-lP^asFTs!l4$}zTX>Mєrg#13<"݀FV`+#b@Jg4kfIBs bOj5gV@`ǠV}c,37ƞ {*8k`%o d<'J8ٻw{y//:਋qj98sPcl% s] 7Yo敗=[7o_kڶ=7kC禛nz뷿]?ny?MS`0p|퓂Rmܰ$Ӡ=:ax zSSZȭ8^ 0v? e%T~ 9*5MKXDcK~KCTŏTc{@BL“L 5lP*L֐(2DɽqZ]`a`T|B0!,tHލadf.U-03lbʉɹg.,_m=ޑ'>ԏiEzF'\R!$ 'm?iHseˮzU۶.R_YI=F(<>- |Bq߳.*61T, ^isYYN$s` =1m֑k xj&WmCOg{;lڶ3Tcw./1['zȯ$:a RM%j E Pn+Iv҈ VDj ABeC5Z @Bfx$ pɹ|BT-pdE iS#a;I$}x$g=Fp%i nY45stP% m~|c+)T0@ sgd R֣ c 8G Uڰl^,JsG?BHdm̈́m:C/WF=(xKbt*.H3bj& &=L63LmbШcNEWj?"^Na<"[jϬl]8&Fy( =2՚YX<4y<]2Y()cM_0!>>Y~ +qaO5\ ܭF@;QK3-LmE 'dΰ2U/M Cm}dQB0- 3=3::,l є{g`þVLn83&)7%jBֳ$}6Q'謿\. :\g@4-N+&BA ' VWɉ>蔋C_韾z-B_oug~wv]>S.hZ%*qd?`04^/$ - pcZ7-2h|D@X!ɺ&BA+D\@K/Ee2#l(~Ac`|?Lzc7~ex0 /r #e\B ^ k=yYXyi $zts{ R ,>=.@r(^ '+%!%b)/0 JZ)g,׶X'Ue65b8A;d?WKSU\u,3PSZrէǿ_>+U#7TNqW ɘ'+oc!uOxU涒kkոoG5 Ti^ڻn7jp5HY=xv50N*m ١ʯ&-Tiea͇~'kǞ gҳڗhUw-SK}%/<]w_n=-`qPI6?cK𦍛o{k_k֢mm[z~(6馛~w{+ 8Hp8D&ĎP՜\P>Lˠ$[ą>gbzZV$( Aﱴbڼjx@2MO lX5"ʮDSX4r|Yg qsRdHrQ:Y,DA8aC{` DF/fʴ6D~AIfEJ[ļU"w3yRAXK wPM`1#tv}Džۏv9՛?vu{qY\.t`eq]s+kafO+n_usbn.s7~h4BM1A$ A)䈁4ʊ2zAieb)+`EAkĊ ;z\%;ʮwl]Mc b {4 %'%Dd6^oKLPj Fea{˻ 5KHA3 ;>CC"n5.eD q!`Es!1q1"x""I{С+^-FԷ+L:L|ZFR_㯃=<ϓ0HqI DЧ٦Tqq!ώz(I.{ry_6GF &N^yo+ޞȢ>nKFo0>h}+RN ?5WLjF!!/ kJ)QmҡwiL!}3%:Ŀ!)9`NI7c2erA1V^,$3KZG\甠G0 !er$=ee~:"þ<^"I,d*67yTluw<K, u}4,Mwf!"v,@|JTB=iQ#˂A+0>*u 9w bHM db x Dldk;E`C7%4+l߂؝;Ǖs=N ^ie D-y\a2sJA"Jz㾣pxǰ䟖tW) 9e&07 P~ " IϤ`'{Y),c;{q*%b`a !dMl >-;BrYh,8ޡ9;_ZqO/j`9>>0tNVn-Bh~peAnIf/L^ho\een~,:b?ٟ"_WWKן8w'ىzdPNL#cVd80,Zf@`924W Ƌ`{b 6̴'z * T ?~‚㐙'%/D^3A~g-/lTJF*QBׄI~\)Rx-aRmOP `nGN ?S²<[K[P5#jNCvnnnǘ 3e8P}+gAydgJ/fD:H|B`4=4aƵjoD]H_vɃ}V$k<0joUmHHEd*̓xu*HULMH5Hju&dmq&;Q\z R㹢OO>7x˯y_cg?sއK-p6{+?` zUl*h@q/?O𢏻8;kڶ=?Tg|ӟ~w޹;t<)<(Ն?R%9 ,ܛhqۯdjbr2kZEE8gQ؉ܨ>>L.bpbL)8!oNSE=6; NJgIk$ sc`Җ38m.Iv]͔텱R]{DHIe2vRYLUKJz)bt\-s&V$I H)H `Ho 6<`ՉG]!.:\vml42~`WvF8kE?v܆p((ύ`0h~vdKuTKB߃Tq-ݫH|`ሓ`:k~H=+∽zʛ2A:`]UtZ5ۤ6@O+ʳv!-DEAR 'mpCDHn{J*_$#z }yp"mF?.=7nuP zLK֊ק6LES PW)̓"u/%p&.O!AHuQ&l0 VHR9q'(mCjy>X;V G##qȂ$JU@ JnSw|TQ! I0L2UReY": iP2Bi;ϐy4اc?:R,V+Ab?=.(egw?bcPƆ%$'e92.w c9uPK Jiғ+fYV\w | '1"tl.(a$yfu/~(k3n ٗN0C%hٯ-= _w]EQ;c.W?OF7\c򉉉vm],o?k=kv-/|G>z ނMz0;@;6"ʖaV[D#c$Ȥ X=b $aZB@Z ]r iB&'PH ;p}Y*"BNIN@+Kahp9Y1m%(& [( O.wQr +nutG d?V@kRK)OˠNXHŸOg*]kJr@U.-ر>" Өj߂P kXᬆBJ X"Q .܃Zw19@y5CL<5[$pljrqR-L;~j0`Ydl}Z$qݳYiUxl8W-ߦ/?!k%[6JT2iQVyQ*ѳkB)ZmH޹?<‡;v>۰1477gpiG+++mY!F1{H6=Hv%$c$lxPbz絞wMKi{KYtq-fDXJ,V: 6lO(x/x]coKK\*2~MgJ XH%z)~.$H %@G#%Oleg WkaE~ʗٳN ׄ,/֒nmϩIFjt#R*A[,>MM Iz_$ѧ z-C2n*o 5nC٧R{}{jU޼P1ɍ }LbF|{K؅>[@Id `Pȯ)cyJ'KB"πȲ$XϨgJ"ߏ%:`hCE3B_+. kd&(چf\ykB[E҃bŝξ/˗ L{^$jwhsg=HXtsKյ\#)k+_җE6 4Y ]$( I97t@ZrVBNK~#pCx֥-x5f~ XwG.^{D-謀8 0wWIc bZSKyjC:աp/gE-vs_r\H5VLKMpeUUd=ɠ0^Z_6<µ/*p )KXGߢ2*b  1&T<#84#CrbmАNX.bdH, +S.LRLlu~md"KLaqU/<>qFF4Y$f/<)הK!8S5Zw<m-הTSlzU%[3`òv͝V@/*eF˳1wcM+M3LN⯾W?;}c7yW9u$uYZyy Ϧ$b0ٲcxz핯~yٺe_ o8emkC |;sgw)MSwVA9<tdޗmrrv"ZQ &qh 'ƪZ&.6K$8 w.@KN`@6sBhJ h׎ep=Kڈ̐9DzCt;&ϯ8eD*I$oKr8Tɑ\71$x aZjAIq_]w&\2J+e|R/(@ @(ZPT@D UCE_3H$y[g)|#y ڇS^RQNJD' vSn@ Koր%<'r)jG5_@rb3yn{!3J߶SD(\;$QLLc(JQ_e7LE:zn?+Gœf)dBb]K 3YBO~idZ@mش:qGU;"17NO:sq ܟe0圸(bbɳm!3Ɛ?=|p^oaa2reNMG+X| <3Ba1нD>,Sؗ4: HWWG3) ,qC$xb"N '씟w*Hx\ \lK}}v*\aI*RQ=B@pn }2E<(MW<>^긟w}HaOizէKf/ \ܓvpyeQ'Z3v|-PPFnb~=yY{ֶ.afE -H X.IZȍe=ؑ@Ər_Ce&@BX큗/vxaݐ6qd^g+M9o"ٲZfĈ,gY lx s&^v1 ?|M尧%~3V|kb悦 A4KjQD`ġj GJzENl 'g&go\f[\EVT;a7F"It,ҠkBǰ)1+ݵRO!=35$p9 l aM%F#]8 7.rf(^]W[(ީc,<AeBљun]ap4dU@8`t]wiE8Y2\(5PS#C֌b3ykLNŚGyٷ{jF{U*O1b0V,UKϏLU? A$u0t' xu[\nEe8"F֚ .i7VDu5߉ȃPXVϞJXJu1YEeU`/ By/-D6?}*?_4m{3Rd(NMۃv ըDy\|x٤ଁN|;eQʱiѐ\c+@/B D*`~qjL0&v֟gxO<~,$IDo5\ƖR@:30t740m cn(smHepB bTSG [ ҢJ+R1-!i23C\-1q'oI HC'"nILM_4X>4RZdƩ&׆.l2vKL) ?,vdx'8 s$".=S[;[RuKO M즩 ouϾtt lvƇmZꙧxJS׾\˿!ȳ&㶶 n>~s.V̌,K܄\yF\A?,%@ $ 0$8["Xb꾰ÑtZȩx  xx ă䓃@a!EgPMbi(ԕRX@ ŝ/Hn3.{a;慭<UY!tSC5hoOɵtTp*()tP|@>@|< q)pm jBx~ȾYX)`c-yBMtL#H'O`X^cCWJॗ'~:^mMOD=EnQ:݅(e n#w =n¼$s;Vi0c>2Juv}!CqEߧxK +nF5Lap*)VX:6 8H0EY!bT &M+8XGqܠQ57+4ȑX=tKJ$*#{jWKH SIue}%<aB5@SLx*2O3>8)Rmk)m% VSȠf4}_^ A-}5E9?6Z;) XXM;I# ]wݯz7\v߼~߁or1o Q:U+uP"I6|o&PFш}_ɽwijIk!owˢ=1ߛ>q1G1@1E{d}6NI "KZV{PWt;?xFwڳ鬳ؼnc9(8nomS]@AFޘ}-t<*5Į"e(IG]i(Xԕe>&c1.=>v2NJVX 8%Xk&,h2_@dJ}NQ*;. GX ³5]a+5rn-aP-k v6[''3"d=<3(`GI; <@,c?`@~9LglR²!*bE`-o3-+_+B&, ,\b*aY2js34=47olzyL%i P Cb)Cە64I{X('dNpʒ1"I`"+`MxaZѳz!.;Tiu.tIYٷxtVbn"-؅԰Go_lMl"޹SEʘgԍ< X;3{vlэ/ݭ HZS/кbW5 ˾KS-j֏7V) Kne&JQYl# ?hQFU:J .vf1inw3CDY56]'>X8%E:ǣܳǩl^p(R1LWXtFcGϑuǖŽo$Z;ֹxmݔlEO4PY$o^o }B؋`Lm_qj`GA6_ѱTX2?e{G3s>:*E>g+_sy{Ag Y~??g֑l2>w'QN7gУϜ IVY/b4( Z`U1#Y&"$"kv%1UZKHBPKV̂@F;oKffw4D f$ֈ)*@"IلF.A?dm)1>2htGkjC VHߤcYe~ggL x ğ"\` o17D_sk\ 8NX/d:Ɲgd q,ؚBpEu\=>fB׏PaKnE:j8={7^+~- u]w=h-34TPN[9pvv1$DSa[վʃOT ;|4*EǴVd=ОY_g*} &AC78[5Eۗmi59h\=])_\kP3v$k {G cC' bӖ%={woճ0z݂Rt[A>u$UL#(~.]x+?OgzZTmk@ڶ ;?䓛݂Ar0hJx铷PIjG~,,J:[3&'vk8.-[Fa8C<Ȃ P/V@L%Ck |B$($CB;!P=J*bP^&dR$JP$ fYN/ˬ B[~8NIX >}+_(dצ&o$#I|g*+r *A4M%i}bY&hĉˋ?.∁,Sl_Xt̅  !,uoofv]Y2^TQ>ȲewSte핕nҴVx;p] Jh4<D9x Mu冠O t=w[x5vǦMIT$"|H*Cy/Vyeu }D+s "FQ$̛O\$5lAjxR JnjdkP>ؚB5&+`KI{fsD= b"űQ#Yui,d0(<:@VFH(dk$sH m`h?Z{`> %"g jbhM3Z`l2:ܔ H1,%эL7d @L~Hx]&DjK`y3WއghYB$#JT<`m keY"(IYM<sI\0kH8u8jSW8c t,b6.v1qU\+f9T P 箂 [^ކ(zjyyWqyrQ+'jlv`~xnv  O͋*"r(UxŷMmq%J$$]d!BxqSC]ENxV/૿O_i蛻~v"yJ$njYr<&2vV-͌ )гd XK6 naH2kD 3q 39[:ZzEd#?(oy,=%tCI伎:`E٣ps*ŋ XT=AlR(. s,{D.<8=Gd,B$#ڰ?wRoze ͩǭv#;}noXUGwnz%뮰ywT͎(1l%(w 7g}3yM555KĜU@yo2¡,PA%~7⹃.g>!I^>'%_AFPh+oJa~l9d \*xJP/_N=sB#p L(ȸN [?XF<{逰NPE cvæ27|ߡOE&eCc[GIurN#vq@ -]-8 "x +p n{`7|h?nA%@ ۙ+ t̢\F ʜOۣB VeHg]՞>cMАSC.lXRj,󝱶/ԅov&bO-sfTD#&ZuM[^^z߻˟_>nzokuEe[:z4rҍ?E 82OG3({F= PL%-0q==xq`cꢋ6-^FvebfHsh.A#r3U%kM 4|-?]oz͛wE^M)2ڦhaXά.c_BGC6Bx$0A@@WfE"M r @g [݌<%@!Υ@ϗ%KYQ$?8˒`$wfs&f3ՑH4 t+e4P}tM;wT^7D\[* l{CjS(u'Q306!+ wMT,Òl|x {@fB*WԍE:*TTj2wt!8~mu3G 2k~NM3nWG,J I]V L>H5 Z"56sҧb^8A)Fvc=\CyG zhl'![.HxQu$}6` Vc>oCy׹cp#MnYAp5ؖ@ۑ{G(ea_"~``o`b:0wD:8oaݾ/Nn[xe^ ᕭj[:o?ovq6=[Akyɺ+o]n>3θ戎_Ty_҅Z&r0߿0\5#OlJLE N,}Fd0qx[or{@UE/]P~L3>.,mȩa1o#g_9_ŀVU DTA L1~l͈?j3·g@YuK)OJ؆lzs5nkRS2Q50Is 8l%<0Ҍ2T$f@5VK*ey7ͳ#==8Wc9mz6Uji ;Wnt8|<?qөS._~G>=/{'.b݂;3S,{U`4εxؿR7VX~~U/nׯ돯Ekڶm-yMʊ."EE['+)9 #{7cl9jJ+/T.HZQbI0Nhx\<`<_Ц\ R̻/LĴeiEž5XueX~O xơ &Ej$pr5 )e_WOН He=C|I.JBd|ާh$*sJ&i0H`"V`¶KbU #~+rB%GI<"9:(N7&f-<^\;xqo8xyddA(RY,'L HE=SRh͉2h k@"aG9kk4礰v M|I"8 -ך| ;l>#瞱Җ^À^a.| 7Az5[']瘀(Xf6"lrFb(QG̱WhCȊi c4ANhL1u"X .`n]'\a0$`=@ɵNރvXJ/lU2A0RxY'J|TE2yiy{e/x1;3Z;)u +ʒPG,tJo{ChU -ߠ2bH~;sz"-0ohTjhj ĿO/L'ږ0{Na0Ss$QJ2֠ Ep">+ƚ\_DJΑ2`~Q}NwPڨFE7i7o?$%>aH*9>Gc^oA('w;콽aꨥ~7j0 3ȍH&?fḁom/:wBw]a؃o}k_0_"[nxޛ[f.oעh$ {hȞ d{,5qHI30# k0n^赺kzl佯:[x^Ww9K<}Gc7ub ǟY"L]%y*$3i+ߵ\fвHI$g=`N3|&FXCDž0Xp<퇥̰X#M0ظ‘dPJ" BWDElӧ }oF'5S5|i21dA~~]ETAؚXw|]O+Oní'7Y'9&pd͏]>J913??H¬#l mRxR[ß}m6up{_A62otC9?%mMt&}PfHvFVɀS`(IJ83K_7ޗ ހ7Óm6&fHX08ڑ?Pl ،%.Yq~epe[%*J7!|"@,m'l\Dx>zhN[P%:R{z{')wh|7M8ęSo׾Ώ7_|GYK2S<21\ %,waR԰s8?]?^$m{@޶ELwy睫Nn6qDޣ$^%IJ- ص4͎ݜ,^ΌAFN^o%H@SUXsdTS @5d.X-H甼 iyH(f%Ql$>eՈ~#v . dpJIIJ g&$%5 _ 'I$$]~8Zt{ĸVJX,BYIbK*p2=%+ey0Btŕȸ|rѭCGdt΁Ž-,KSZ8* 3zhl*<$JA[~a[N-cz~w$AsabUk`T0۷ðlyO^tP]"/#󱔐iXҪ(ݛ _+>m~swӸ`I{&3hi3JH6{d~?O+  c}I:4U#).$P'`.B%a`adPH D:vP.T)Ku9Y7 [,8]Ka8j^1c+e"&`BFW{K[UŒŶQ)f rf% :{8LwI?6\$ȸS9Sy ~A#:2Τ8x7G'[`P|tX$oKiB'f2Ě O"TAG(Ҏ*el'oZ%2p( *7د@Ⱥ8*e5p~qtdT@yy\09X* Ij8e{>< =;$)KUkad+6µf>>P_߇|OD^Zi:6j\ z#yp0@F~Cb*4G ݰ%=wwFP$A1pB 2hS :!30J:.Hqtab'*cFPZ?2+e7M3U,X(K<RȪ `ssMbLT/A1ԖN|\LNFsC@d,}hN8+ؠOegy3%<oDMlQ<(%#EX7 VVNe~kv<|9oI@ވY&T4N kv<vГ_)U!e#VړAp?6ط`kSFo +[ٶhR 1 ZvgH8m$G B n?J>5{<4,%r#D rJG ٻV͊׏UGxѓ.I1aܢM N](@LN:QN/Ph_F@v)H,R,˾ tKrn<W{ʧ--{)=__YـO/w/N?'^|G7c;C#隦.0M-. -˻1xxa@} kyg=ao)OyzUUO&, =m E-v)I5Bu]_2>7٦9z4I棼w.snz~Q<2Lx\ =_أkKL+ZY=Gm-^?Q..qݲFϟz6mq[c>0=$ݗn퉃 ;Ξ@pi{"%g ҈43; Y{I^(NFk:ϐ  o@6d+yA#6=xzsª0O5lk(T*y$0wz*얐o`y$%T,Wԓ?c Lr ~P@ "@Q)lBU~.@'  ~vU7|^gsa IF,ƈ5\sA:yҏsCmD)D0p|zlkPk &<'OMlDN_`1~Jr Ihj"Urݐ4!`yO&T@"rDE_b|ZVڱoH)uwTs '}B cϫ43Yz~N|mt$?  T;3NYa:-`~{~$הs"iVS}IQ"6}\d)IפX~|$-c C OX箃.BY-aUV=g qKH#D.h~%$Šݭu.~=x]NOX 63x`a_uk'[+,U I5[P[9TIKL7Rm̰/f5\<r2N~>Qfi1VO@Z첸Km 9jDžJ)2YIMi@19?|鷰= h'2v4GF 0(Z/8~U=s!HU$4zg}lrKS99?CɆvsjaC w'X:jvgw3n[ѫnOCЃM{;;۽o봽=_ozg]L-?6?AkCc=dIg 0I8῵]/O@$OP?A!_8'N 0sVEa$'7~l>:?O~}}1m-ץ̧ C8l ʤq:> 9 % ×ہܵ?F+pX tzۮ{^M ?Z8U!79~ <֧ yF=ƪىs'$J F/xP+ؖ|?$q/F s]M9O ř@-;2(HiսJNn #{4b,kC05 yV8aKc-ѐYj@MHrn3inę_}~ɛ>]{޶)dU2y򖷸~?ww:uDUUgFQ50!9 5GBx?7f%ؤY/E6bv$1+[掊xA9V@WM`> Z Pɘ n Bx{QR8p)FxL s$9SE{"696 ooI [Ij0zc diY\Ͽubu2=qnE!ЃoTDژ&@ Z5\?ȘckEt*fp[J/Y #~0T8ΕmA $bkx_PQ|,F>$UN7)0`cTB$$}6PBKȌ㒼IZPA -P\įFJT Lα'=>yڔrwZuaRp. DV%ZɑA'F:9*v >4>+|>rsk{ 8c2?68S6ɲgGjfOB,*_HhO|rpA߄/QyG?W7Dng߳OD vﲗ腱i4O վ+/͗ۯ?=?3yѫ6 #*V8f 0]-{87!WAdNB @l8旕^Ib O*nhsP%[ܸ8$62{ |zЖB.EUUW*@ߟ(YlYB")9[$"Xz\ 8zQ$.>؊=oEM=7$I* 231-0AHϷt`:D' X_2jpW#,gܢ\[S}kheJ;X"a7nl~% d^c t@C'IFv."4pnK$/{/x{oַao/;8u3hTN&*Zȓ"A8u^xk{t{9.~3c^פYH(Su]_H^`Ұ;z,`BC˛)Jvڄ,k7B\&ڈ1~&w!@W\$ɐ)WG`4&b撠'6NrFS!=IB6*&,Z+`Y; E,tj,$x6'PNVz!A\9x@N )]ﰹ9crAIo䭕UUt>򙽪|~B lmL‹y`b!b"~ g/Is8O ƟN9Ƽޥ 3Ln`ci/؄a9׏ jG\ت`kf_??~W>4h \$$ {9 BSHˢ| dxdK{:F t¡G5QF >>57 l; 20>jv8qa `,#H?HƬRo{D\$F=a4r9|j1n/DF.O P&6:ڤ.䮕80qd눡" \F>xjN 2yXUÂ_CJ10\FEFʡ򗩀cbj})H3ߡ6>/Kq>%/Lsfaq)aT}d5@awBOSsDZ} @Oa˜W\͗A=bm $YInjçKw_eT@q /0:hU䰄A=_<[2VrҩAx#08c]"؟(̜کBh(piLg,'gsuu8|/; >a\Ah@vQ?Hw``8`7dM{M',=7>We+b|>{^*&.$cs L:wS}w ?dfyl.ke_=+_ї}P߿~~[C᣿?ua? k|qcK J+8 TpF@v"&G$@&M1?1P?51W$e ™ /^qSZEe=d,Q2+˞w)Jp>xpJ4s[h`GTKsx|5<('O$%]20jN+Sn +4~᪮Ǥc?an^h8u]Ot>Kpy@vM8{JM'^MZ?IQ4H{pXjS!3Mz'ܖZݥ&0mIye)mG8-wb:0 Ӌ%B1..^AR@+%r!:U[wا WS?0x4/[_iW`hc Xm^6GM")C;xx? {>$bV2[`I]J6O죢HDރH[ eRvqN O & PMGQ,?[-8xP \au!AnR' L?p!g{ԟzҟRNRuQXR J:I"Kr)b TT"PctϮ3tX+6:6 Gu3v &>he4۾ lm1Wj۾>wUDzRI [\s2%:H#RXxsï >sYݏ5gAq O E}!;Z䡵kvltAPA:ϋHu NƍFeVȨA1 ߧus-$|Z[6)<|k 8ƅs~#Vb|Ͼ>Gv|v-~yـ5 5a[`\p߯b3֣JQ 5(/HRZ{9Y&= F]jK,C(@W$j(X( eJ8Z کQ<_d0JdģE;Qں/"g6!]R&t,2̑Ӻ߰:x5׼Q6ֺ^ JXdz,;^ R\:kT2&PDiF$ ́V3#")~P ErkX)!L3 &RycM)_,*jLFϪJpB{ S?P:[ X2M03ijd. &9+ŀT sy.)+TH'1[I\!`G`Ep+VD$ʹJ;˗NyCOGCXX|؂yx!O72J2e@;O :U֨;Ŭ 4K[~)ys|f]õG4lp@@v+[G}=N@=fS=M|iuSz_,(UU!Qb\.=yN÷=7=wMS)=HŌ$ F HX# #ׇV+s1 9#7 M0O$Y u^yt~=UAkt"fOx m@Y_՟7]1DM&ArIKpgXfjc33 R+sh=x,F^I`肤qj5Le W])A.;?TTS 2HzYX'-VOxḟ[ulL XCf""ލfەH%bVIrfo6fD̞?s~γ[p;?{GA9s0: py=cStm`n{19[˒C.G;~}w[{oo^~uXil ;VrrAfCϨE.|Jxau :b[]m~FS L^n$ ö8?4Bo gײpv`{畑L7t x/1z\DkGğUZ QXn)c:d۰\PX2"FOނbyqnb@"ök%}c6Oo6"{#HB":o~TRr<^ͥ*f6ЮDGįcM,-( ,IqaזA -a,,嶸 N[^j: \c ծ?SSy.u/}p 7y{/J=FϷA;s'8VhdfV|:(*i$Muky烣qo; 6,$t-IZgUwA8uY>gZ7> !6:Cc/!|zW$1]9Hdx[+гg.!9WҤ]+F˕ڑH .KArhI;b84IrJl&h1u6z 5 dLCp&t} MJ;VN$I7A W7QHY$'H%(gbτB@_<ʞ9?%+M OK-L#Z `Hɶ?˯<2#(Rb٤* yEۚu[rhT_ois {_xYzh~t2"ە/fguL:zhJ654۷|su^^54OFhʇY4E4ɲ*s+Oбt\XRkT3(ңsd x'p] ^5d̀=_xrkJt:J^+`4uQڽĺM ,Lpl״o }D/CVlV.AmP*kiZ/l' n\xaؠ`!=qѷDҀaF@NǪN7b\PoXaa?i2f$5?/ڝVk*I/k>J<(dlo;00g UtQ <3`a4DA(Ϡ3nz瘭@ck_[} +*Si -rrHAKwakbZ`'P.e VRq$ *|XSL`X.]",4f%%Cc|_LEsmI""ބ2mqY>8#u>p8)_[$eNj&yDy?Fpf(2֮Rr^Qr =٣x}k'O$9Yl~V/~ӍػGSOi;{`>ۿ}w?_L&#k%O4'T<&q=˶qUD8~!792 Mc%Y(Yu{0@ 7drJ\cH9.Z%Ƈ%Ā/e&!u xow>X@oCbI㲁q_3#)\OUR8;k& &a40B56hO 17 ,XJ{3b-72s .=(~~p5%pXn:,bJɳ B9R+xS)n4buf& X̵D0(Ш EP=4K%j1d|Z_!q^n?>z ^߄\3f՘JCeDjH-C~&}# rrʯP6p_ā:!bRBn0ӂ 1i{b>f3AI Ӧ{(ݦM{u1`I*~^C, K^|1]a܍@߸;._{/5l%@n՞ϲRt[(09kƓOE{޶mA~v'|Z]5M z#\ZX=Zyۮ3|c8\YsuY>izA$IP-Jq&m*3z+iQH1sR@bb,*4lwq\q0ۆf$YLH LTP2Ccf&+3`I1!jdz8i#cv7,~Yd"Ɣ0rl$¶`1З(ЈX1Ϭ H AG\N2CB|W(4 ^N鶞< 1:9ci=:3Ϭ~KWq#Q_ۼ ޡswtp59 GuxO'&y>W{`W}ӟՏ~׿uGLA@?1:"H}(nT1aD*\yGc0IE͈O`$,av, 8˒;!gvCT<4?sqkTNNdf1yeRP[E8 ڂvzp#Vӥ3 ,oy? !3ڡ'|q{.r,džI$ȃ:CXaJlnZeLg .cL"ɼ> wݷ *jkC_#mV|B\[+4V@q91 YFYktW|!sl*Dx\IR{]q[Z c-߁/s,(lЩXTO5s;{~7=W-?^dm{@;=yNEcfY# cPXr߯&meYuRaf*(FCF|[nŝT+ǟ'7r4ij.4Q~6 `mkH $ %]Ta9آ$N5Zhv9 {"A۲?H}2s1֒)Kê]N|0 ;c9*s?1EXB%fk-K鸪/ 4sJ^-vּv"gB`^B*Gd`o!iE꠆n;a AR1d R0}k(?FaO'Im͢*bV0NK\_D5,`N&p.'cɉe+3h. (*Na0OJ?N|q*pLPrde5K &K K=X 5 hŏ4s32ZVoj;r$;I֋X4l!my]ۂEv1`DX&%SWՓ~?Ar}{j!WXVΊsTvp-VF8 t"|t U m'Z Q='$s2GqzT2kz1iޣ\ӽC):$‹rl(Mwʭ4SC!{BŚkE]` u OY5*T9TObI7^( 8l <=mn>[)ꞃTY^@h|kLXNxm~ j*^` A>xSnafd Z &P{A ]iK\֒/^L4w@ &-FE>B-FMC?f08SC9 5Vם;+>7'V 3ЛNRG.˫|^xªO{PΦP~@YoA1;1} ^Ukv`#VTSƲrYretQ𳀯7Q>5sF>e,ngU u氶{LT̖qZvM=蝃ɕ|9 &({ɾP}DS>$hB ]ZMMc#8h_sk=iDT!T4Jm 'z ?o&22cbc8/@,`l-ei=},(| ,6Fڔb0>^Э n+KܖUµ`̢܈}{hXE>3,qvU9H(7;8;!LY?8mv|;5 v2(ᅍb;at{OoG0=}.)W;cc04}+δzփՑqUӟԟћ_d#ȁO~>1x)3q!z ۂ?˓H'YLbQf1 Rg YA1 ίF8P,!FSC&M$뇴?T ~9|/c.'y>vPeDž"20UE?H;# |CH@Tö_C>WxANKG' XuGP+UɈ):CN_ق`ZXuf% hfqO m-suA"ZS(^JB֍h`V* kӅQ&$ 4QYN)7̯,U8`p16O8m[N.8'&邘:7Oa}ψ!0fP@Rhj YU?G*PJ7Hcu_R};RXȀpf?1B ȹ(nZ̠/]d," tF`,Ε}spAz`߱JQtBq]Fq@004jI?[Y+- 2^W-%vx #֕H=P%?}bbh9Z[PD$DX- J|3T`"z2g*yyY]__Spz$7bOQR,͋zG^z<;l`#d =-}Kb~(2J.9ߛ~;rjpLfh..f;" {'t"۰x-j$P!IH[G.YOҺ-U#]$k~t,UBR25[MMt'AFG&#:{Zl6tJM{(d1G#xoq lAUxt # nf3GV*NGJ J;y֝w[";pW\tLkv]Ҥ{;vv0uXiB- G/i|Aʷ]]Q`b/7N>,K!cz9Wy`+#tn WZ.Vkӕni k.jcK$ߛҡf1X.yj^Ƭ-UWy (I/Rl7lܧ t/t>(HVlKTNws+@"$d J1#dYU=ӌBb(i0Nb0^|}.󫒉b*7zѿA2G 535B J>cQoT!@RC}1T/j:jC`S3SzWrT ijO #'"9@7 Ugw,xc@lۿ#\$V,lE~p&&@_ԏ&Y92Wb@Skgݷ(ӹxo?U~;w3F2>{߅aEc_Wo^=goؾ//w|{33  AP@Rtp**0Yn:Əi:/ R9f#cTdw)T+?Gd|+;%0߃d`,< . &2Hi)ɃpX \k(eE֏ۈ|2:YZiQC8?Lg `D̙NE@H<9|׷>,e85(!i.\qhKf\53D6Oih}?ɣZbfDO)% Lp նY(fL#NFre3خֹxC,~OGϋJ}!#8UP/IQ^/!) İm_(lJtlBQ_6(g]% bϡj7N/,j(`L[0*pR@ic<:0 ER?(,bmoJ!aܠT{k([4E-Ġ0y/MM^u>e&eyw f@e!ZFcLR\Dv,˖i̥«%fӕS0D{E45z2q Rjw ˂Zdb7jl̋+>*}k~ws/=go?8sMln%UbVz'ufkvҖɊX}f"]['aIR3F R-,V4Z J\>!Sl]2IcA.l.2;(:``1QF<|trc IQ)8.{h﮲L N#I $@,bAʑ n^V-\zˮa bk,P,dA  !d 77s{8^,W?us9`IsjAV󁁆5.ӻeiX]*Vdpy#X2D& $=5P@*sz.%,$ܲ&? `>yi g?oy*|Kfa/!=3_#5f|L껳A`Ê;9eSc<{Pn){JSP_h ҷ*3 b#XAMXZ}0Ҧ~P;9ö0n}*AT-#Q`[(BF Ϩ 66`#aU}6IbB|@@Zv$]! =vqDe2o K;E#AhrԶy>_+CC<<60@o;IOߞswSlqmU>X(V5͏Vozk>rӍ7ou/ {={@? o̳>{KѓNS0*LACuBrpy`;@<>˔QWpgj%^r*r wO>xxFR@V)v()"6 VZDg)yst[Zɥ~.^""U6ț9>՟ 1HB>E6d 2z)4sd0@WoG=GG![Z E^+S&z6ii/+}I1 sK4~"1cH >"}|"ԡ/f]tk3_Z"'ʨ_ ZtH?9-fY =G|o]G6>##WյsFmْhi9D>Ijg~tR][jj hEjm 2I¤Sfȍ$@|혠ʆzvNiasEc uK2qІ8ʘ! xe+P|=y I}-9k ci<`bÊ؍`ͮyCxp@y(3ᾨ&(69zNܜTǼ= -@؃6e[ t8)kT| WGݩD3 DUҽrIZ6g< \`3e!xۋuƂ=!k@dVXC3+[d,l" DJწ696\,(a 7(*M4AQTt,{Ai6r -j "$SMM*KM;˜#7Xc̀㏚ 1ePr ;fpc&nx1D4,jGuq3dSό5恁  ݥ9v jmvVgYBw(]WfwR&ˡ˹@u^1>l`kp~֢w%v]ןzOg/{C=>}'<:$ӇQyأEhͱ^ ?-KJ.EՐ3ylh\"Ft&dl 3`Sp~Ÿ3c=xT _UnA,CSvv3h!-%M%7#/";E>E>.Ҡ$OcLƔM?"E<Hq2Dȇ&x/ >-<&G,Y76txX%Rn_Ҙo/ẽqrD)W](,[vPa PG]D V#2qF6 xR5MnBU3HܦhtGmOG)ȒS$l߸l&S+Ii'y*P^݁T ltGSE] tNO2w˛wU|t3 E2tfɶ=ydX6u/Kn|,|z{{c=O}-Μ9iޞO&h=/b̊~]b Etphe2DOU]Pu}Y)Qcl&Ĝn[0s&v`ac& Yj QRZB̈́'BJ#K<l@,RuKdJJ {Xf[p_% &)9N۱Y ߜ^StlZ13k3jmT5IǰB(Z Nͤ˘R N C:h,eį.RTX+p"- '8qk`g>?ǞC DQOF<$K J0ɬCG |DZ8+I/ZpPY! -$ ]UI&а1nS\׸ǁ&zY wbv2y7Ƭ>X6{$ Ƚ A%-s#giPgg1iV,'1Oin^z$iIk*]%{>׬o%>[IsҬ'6IYG@ ;1%xMxH%Y0/τD;6$&2mMLdk M6{k\$\SQp76Z׏16 ?/\4mYH<܆vdHp,⫺uB9scu-^ei-(V%$w'܆YG<~?p)k40 􋑵_"1`"cu2v  ,C<>n偞֘{l%u?ܩ ,F?R2V$Y$6BF,y>t-H²L6Fz \3x't7X†aGcns}ǪAZBw%P㈹VT69dB/qnQZm[waO^<,\En,l'=oGɠ(X}ـ:Y?~}uIl<(/i~}nެ>kx?߾׳>w'zr@Q |5O/Uז|aj0t@d`z=caY$"Xl~.-(STN ^7}n672[#Pq's8yf9{xo6cǵc,סשa6C^6Ԓ', ŴFd[ICM,HG,`%ppq9ufy63KLyj!y_2&4Ib8u@AELLX=xAV.~C2*F1 +٣$ދdž: 2?|UdT%m')i Jy3GydYYjB\1ole52v|2.썔`riϬ*V\BB;EѸ1KsCI(4-D78c&`ٲHТspc[Z5FuEKnژ 3kXcε^u_:6UhVנX[/xg(2=[>*O=r]2{z[P0{{OE7?=>;?P2 &\$UCڏҞ٣`-mCr_ym`SELB*<]hmڊ=g5q9YO1c/_W~>x%pxAދM 6̳ N%#BΠ(` *ƕVLT& Kٰjq-k%D{dBs{"Pcy# .<{n ~,Px渁ɬyiɏGef o&^$fɓ_(A<~)`wZ$PrpO9FDA@+a5*B7ldmTIߗ|D#nߤA`3=ixS7^{={@s=?ڪөm;-[L0T;]uս~^=XZL?̕ZQ,Fi-CE^v( NYA6hJ|ڽmJ-0rߌ=&U;aaCf21J%B<nCOd`{$!X`d u4oB6x\G,}}B;ȵxɕLPؽa2~׀Rω(L`*Ȳ Q-h Q-eR1#=u?7Xv hE|y'~_t> LLSCW˳{_q5BdaZ~+zFx.O;ʗYhM+nD?T?9.pLXvشWWaB˻ԁiIH<X/(s&gOPvv:FtTRV-OH*ӜHũINmSV=%T>uҍuvԊCQ_=UwV,旒N<n\;ZsK;``tIk v=)e_Cgʤl%znIJ$!85}k'ƋvZs0# $%^}R1 X{TFǮ{qOr=OGq6;3֎EoےW آ.7eף4Ԏxw&좷Q B&.iAE}wע< ,Kr_L`nP̾ yb1Q|0nn=+/>W>\iw=O}ᣗ_W^v{7~u/z{=Gkk{yKQm1N5y+p\< C4yQgxat[x6 7%eu` |x8Yֿ9Ňώ6[n3G+_=gw~ln^|': Г%gϡx"G8_x_2/'X7I8Ee9iקEeH1Cy8| ONwm}OྯLr mauIây)xf0q%;Ϣ\)NљFyqXN: *yeyD[|Y" 9`yf^;ppzExwK%LȨJ' R:𚼀_GVzPʜ>ADbx;y]XsЗo|};ob؞L&LLM`I +.l@,r,//h4:4%8jKz ]^y&/՞Cax? I]ק]`zщd,vP'_a6%R˥CF+-S-ˑYT{&*H!W%/ic\;_|~IlSXIyR5: ,VKg`BŒ127J3PEL%'JWJ~/] /@-#7W2;jN_\>c* 0zE܅Я6{8x_9>KW%2QHN:ApG Ug7]ڇOyTS$>=_hނ TlM d>ai2nHBi)Ia8Sx)>;ƬtA/)I}M46 swOמ]2<AƠ NAJ&J#qn{d"y̓ZƯ +|aОH3s&9l y=ɥv rlE@lL' NuubEݶ6+v>=hX VV:zA2aIAJpV:[tӍqAR* IKU1. 36ciy@UKMQbM>^LTnu!2 _D!5Y ԯ_w/Lgկ:XU{Ǚӧm:^ >1$K::1qۼy8A.F`иi2,1`a]F֯'YEx梁q跂]{@ 38yv ]a_R:y&윗?̼~D4:wP|Xax(:gƅ0:pg#~6ikz_~"<sn,hch5Kٜ H"}M/Mh+>zzSIcGX=0)%i*F&xd!6็˥I>P˒ `Oe:>יL<Ŭ1|=(aԳЉP;O~o^\/:p"F^U8UcDN$)j\^~PI#wJr6 5X)WYx9UtL s|^\DM 99 c,`(ef% ̤p#umB1ކu42JHvNm3NluMF :Ah,;,b/B4A,%ϮԎ4Ml9W]L^)LEL\'aѷxDֱRhwEA[Z Z PJOj;:ߨv!κб|QdBJl-Lgk|uphGZ}w2i-W~ZX}7=xӡnyK??/E{Oo|kkke6UQo&E+}֐~@ wMFzquγ+ˋ,CO{_X,&.(\XAtLF)n[qƖ`}&ŌJVF(]%eI!e@ dFXV Xa BA@E)*?H\1J /Ӧ@g.+k% +VI,PNJ|'o<_y BƎZ:xYZY.lBA~V J ;,Cx29>~b2F'Ol^rYoLд7/= FI"uK`I'KgUVH"a\Ġ3llԪ (#oɻb6Qp>!jiD, ۠Nx㤅l|fSB tŀ4Č b$0ٕ6)uϥbAR% N.HL#1Lu! M8NS*P ԶR>I $NjR ?e.bmp1u-q mQ2Z4eaa$Jc[B$lk*l<`+d ]ɩ?$Lk>7B_1 @1 ֈq HOd8sKe LT;e^V#6ix`JvYck=1aiiwɯ{Dy'ecn|X1RVeYjpK9Yݥjó+3mȾ@Qxh ~NJM}k\/e Fh.`T&-LC`7@Gp %컃˯~qı曹Ć,&|Ӹd`܀͚%.noHbƃœ8رũr6tېUSXb r^eGd{";t͗M_S׽a {2wdCl< nJ0ӯ0|np3y=AM_x3?6/^ Ϟ8w3 \˶B\Dgx A4ڸ9؅ydKJseɌCe<p?wLc}=lྯn7`{9s({)kdk"&M`#nxmlb_C4V| + LHJ 7 ࿡,)d<}j3R$gi̭²%)݌mWDX-9pet;@LX:E1ؓ1PX_ml=y DB(S>,}C&N7EMz4]gbBFeXPt?\&B:ks?/pxK1XF_bb#df/^c9u M_Uswt;N&Tg麰*{zEJ]p#ЧY֚^w~Up^yfn{p?pm_ ԕ׾}gz<Gwq=qZ]'I%Q r"C4(zXzIVbii֞ɣM}˴GʔhV%n95dXb(b\,gOXsjɤ(5O$Dx#xu;3 llB-ÂfWbJ >`A~l40X G(Rm?vI]B Jx#p̠HZ5W);؀8a e uwH$sTvZt'e[EW+)bA0l.&H4S)eXIItZ6q&#XSl_UJPwoKD$TX`Ṛ;\|vJF%v=hہMm iR10:$%5YE8!` ;FC.f*!4ѯ߫ 4ɇ$rs<**I? >ڬp &2l$&mmsk0w{ ؾ;7 (Jȭ1ee~R$+jPQ,jl28(Y2lAބ؜ x0-e} Ftu5䶉%@|dVE.?1H1FtBiNŦ6(^ ߃ciUd%:􆬘`:s/Eۓٓ#c}5dfW;<}w}? a{]w̉bk-\0=5}[8] (wBuAA;Dv8^hl`:k`{Qd2lmԐP;vsB ?:geb@O?pQ]R½hGYaH!p᥶4gVH,J0>y&iugZhҩ};^q%R,fHsx 8aNޚ6sk%WxvA ,X.Z 4D` \Ent-_{x|q,X@[`kf:hKx*(V>' c)tl~Y^>5ߵAoYsJn.P UCQ2ђq7B1G+ 'v8M̘w>>[F\uk7¨`<#8EpnQS#l"w_VnzO!ev "Y6C2k$g9Z<хMMOVó@m*5F}el(Y<sR\һ,{btQKX>M[O[L>@K ;jeҦ4 :'5.9x(<J"\izG6 r}G_W `YbA֣Μ_p~5J?UrDXctp+Q⭇@>z|W=Ǯozj/{=.-/졇zI]'1z fLN-"| 5}UHYY0 xuKܙ1\>,$Ij|)Ε0xi6P4bL; l ,3Չ ]cCI8mTCU nMF,DN"J>*'~-jjIBrگRoLJ~Ci7iG>bV `c[2Z6`lh‹Rh׆tRIzko- N\&ymƹ )~\vg-O@K;iL@=o^NcH(kL]X@ON2]msr~m4`U䤑fA7Mz=}$ ڲ)xTL4kQ~OrIOt.M3 솺 q)0?|j>t7[CU@eeȿ_,IZcLX H~c} 3or4[ YfJWV_0͢<5׫*V< }s< <;ٞYE4K.z)"Ř 5c۰E,i[±tLo3z@8: _PYc a O?]L_ _>X|@d<~3],kSR]73ac!*shjM2g { WA_  ,B+x<=Uٓsc#23FZJ!SJE6X;nsUpUCxB.3g0,~rd dYJ/ɆDžsxc@?Zw b4"}eHm?X@|TK#Cpàu@ߗLXQx&Lfd 6 *Fq| +#L`4hNg&8GyY_ 9<1@+b et(30}z=n$E;)RrQK1F(  ) 6(3(U7[j'99>~O??[Xhnv8p~)7)7&pIه#YICd|A Cmvr>l V3x$\bO9˃ :  ^gPl㼨`{[uc'N_-49#)&iƎ2|TQ}W} i:̎6LЕOV3]P GT%  m~~mjN4et2dJo UpK%0ZHϣx@"֑K}`\ē(55&SI!:\k67눋%{h1RhQ UG R$lK啔h{86z2e;C< ꀟ:fkݙ}hiC' c͡%:iE k.D@a>_`ЇɃ?x&$]Z,=2te9V Euj00hGp4p'u~,Áł_M@2a`@2 iPEE)*ʬEejw/q 2ICo& VxREHy iML; KX(K'Mnە4"78OLNl]\Xl|Yce6,2uc\5MK6zPxlU%H^k oXxꦪql1;`m1~=_"V_mJK}%f~ʾwԾƍzJ-pF(M;llnd^6G!46A&9ۇtme!Y=HKۙA e|TlߛE+o(RsQ3|_. ၈궸Zlq@ DE`=h'*+)cUer'ՉN ˶k5$FfG'FS d-bhE40w)NT 6 0boInĖNgz`@Q*H F9OĊR(EIf$*u$lpJ l6t˴-:;oJ*+?~ rјV)/,[UC۴Mչ=UMXNoy&ֺ)G?丶f*Z̅ җ־ӚKލ$Ʀ(=`laSFek MaߛdK/Re5Dfn%MyDb@Ԟ;'"zYE& 7N(IUqm Dh<^=y4֖s\˷9~8Q3<ˇLEJo͚UA/p/lo^n.8} +ǫ^ʃfiڃ+lQtY)d/3Yb3ps"Q̭j3!HrLda aR/! P^Wbڢ[\\41Ì,5u]n p #yVyA%5ڏkӪBsiI,ϘqR4vVy/$jI^=ܵjmZ̋ o6zEE=+vo/|WJw ]XBŠV7?A^c3}0g{@^4F,j!RzMא׾#=$3Q@Cnh果׍NtZAO&s XT0,Bwi cI,◣t(^죴^@{h!ȃd<2kչj.sw 9RY ./ dwh,jdkpSEm2x >C{ߵ 7^Q,kR)mK'V؁Ž%X灢͍s@Cx s35v'*׃Wܲ x2_9ڞGU>󹍿l$Pq;m ֊b؇oZc)|lVsb5_(C倃c7_{}g . ws[vbVX3GAb`{N_-@r%r}|j"Ïhwj).n7i7(P!mDR4ÙdPVhҦi51OTt+ٕ?)IC*͓V𫍬E BH;wﺢ. WGyPЃt!*fT=d #G$赌Yk*6Vt\TnWw/yO?;/}[{Qczx_wr뭷Ϝ9R B@^)XII 3p>{=̬y5.<]s^<7rm|~RBk|kS)!1u)QWI2"G/T2L^8 Dz߄e7a',J!gJkg.c1"D*O+m_h+%FCKQl.;2Y̘ٖ"B R<;ʘP0ȄuZ.^SH0#=S3YAJIRB )'Ҽ2a\%fʯiy˹b<8w[qV\$<>15i6Pҧ_#!# ]lTV5=7,'pkHMewѨb.>/2:,c徯qq( 7(fl٬(~ĻCKb{cw=X0/7j}N=e_/Wn"PC@CH3ư|&̳*xE0ijj6\s=s8~vN]06[2-K]59ٟR%`@,I Olù SZφ..Y(q0Bpne]dln/`ZFUr]1 4^moY@<E1Ym\CS xT V{,h5`Y|NY%>"yLiMOd><mΥsۏgH4~yŭZ;w>{.oEAMU 5Y|}M-n~I+)gS*'dE)˿/{*\{l=X^X lcE+o^dhL3i zP-ګx2\4{6w.mxNZcSJ )H,rcuoX>pqQ9#_D4-;'đ'%i87a$6XLP۝a\1WCT~dd-pKʺA_ػ-KKc`3/`|%,X҉J[$1w0%Mh6y$XL@)hgҒ~.feas<ei@c9RܯllVy bNRZ6vUQDzxյf1[W*x??ܛ^K_y߹ʫ^hdzр<o֘<#gdŢ|JdzZ]I{Q^X,<˞(ԭ`VcdX<{r@|BRg)$߱Jݭ x`Ŝ)d4̲jsk^>%[0Bєw5@ɵRڵkA4I$4+YܶEҍZ4\! Yp-EZZ%[M((EӝFdjvt- ݳi|x3NX2_V\fjD Oar{d蚑d HF(jJz{àa(酑tUZ_pOf@6Q'C*yv Q:%pʞ`4 JTir$,i J6 pVܠ=r%WX%f*j>9D:.R+apk|/Z^;>x֑%kϥ'Lw@0ҁ=|;DN#ߥx'tmHv'@ox' HCbh}iz]>1 6x']v1h|>:9mXZed<B*/~"@3 wbrn,CcE&0!쓃`ʶ{9]+`O ^V:^@Q{&܍ܹ]O!fΠ5>t0z YNTcPJy\y3bo o<imU8ioz'YeK XcӺ@U H T*E(B 8n08H,eKV<۞vV٬f+Jܣqnsn^kfwُ2(vylwj; wL5 vs7eV`bo7Zd% r΍SF=w~?xCG[}g^n;- T5 (>b.ό $3ҋSc#xVf^}}e뺰+1 d ˼GFόwpϡɸLk[*)#*?Ts2!xS  oP$uXKe*v+Y!srV}kV0#Y5#zw =8~Uͬw4YU ]?ܯ z/!Ndj Ci0ܐsyA]AB 㢩T̤XƵ FFIǮeƤLxAe(RG#4t[d/ CUd"#-[>^q%%1:L)h<5A6 xL4h^%m\l%Vm{wxPp&|-Oo}CtG5asg8Mly|h raVCyߚa^hIJ;!{ъ7݆^wp n/;OS#40Yr? šK džAڳC/+n? K\ƼW`%K}P2"y܍e\c.oUpud*9r%X49̐AD޼Ϻ1"E{dn}}>׽M/=nw^܏J_}iO|<S_^;~*ݑK7C`=GY]1O >s+-<.4fS3J@eRn. jrv=CUG>wH2A*|$ݖłlӛ nR)sEJ=6J}h.D f9HSOg#"%삉elSb⨤D2BH ؞ ;_0WW8X= {@ N9Dp "tj5C[Aҿ 4`$LROu[I'&cEi ؓs%20l 'H%v \5z*mT&6j/jHotLU*[ >;f/y`PdCժtH0ϐDJXhئA\#v琕iؚLzdTˣ[ ,Uo/s}bTuWc;!DZuMB$|6&wxԟ`zf=KLp {9 co  nJc/~uZ?VS?6_Y} OGգ뫲ۛ @$yy{ ~F:Sij"mkґxDHq|Le玥"9Krr+1c&5$#Gq=\,&-T$M0= ޗ#M,\BNcSߑLXF{TO2r3l&#wYm"ej"1KϽ:oi8zů_ԥ{nݛ-/ytw~ϼ<Pg݁ğCqzas[UKeݻ3_x޾-⑷O=.F68fvI33d̙fr`AХ2^Pgǟhb?5pUx }8LŚ<`fSf g.Ngw`{OGBÅ<+b.i@U\|C@#w DiNfаi^c$uB8&6I#;YGT*Hd@z1;3o_5I5b0}c"#;4g̒ϝ$ tJn:z_rmSAC=I?%>cP)Enyr=T"ƍs4~y&d,ˢj'I<}XC xneϧrS,^}W^jydY=NbZXE"Ba>Yc18I>wn>M~+rx+x2K00wR5G駆ü1iӘV ލe^@v 8@hEk|u8)Ỏk/n^I c7nCp4/ sp@-ZtJw*i"X5 Ʌ`%ܺQg]h^Lw}zYH.T[<]crZ-|jOW x\UfP*&5T9 \.x:п؅lH.Ry - ^nB#J +f֘$|w Im79GbGּKׇ+Lz'E$EZa?G6w!(vT$wrv ^Pxa@y7Ԉ'`ܽCV~wig~_ O/WG'> A _{_EG ȃ7%עߞɂbȔWqI}?v#_6\?3g<_-;Ye:jqvD_,Zu&&>83R\=.M5"H iB!aЋ A?6bRvɼ%7\׶M||"@;^ 0 iig\+J G.H%`T" RԬF-Uٶlm{0$ό {Ɲ |xjuE*E (;aьUԇO0c#϶LjPFMYQVKdm\]QJaaWd^qޯ`▫.^anN.Z SIf'Pu*?sAiڕtÓɄ:P3ᢏ^;mr<,'>yaMUD[RD_iI+m"Ya +."ӡ'{;[7#|A0uֽځGΚZYAzۺx깥.h+]ڎ7rF 605HvI1 Yˇ g{ EYӘeߟAŃArSNI% ôrH܆,&9  $nZ4tK^|-~@Ԙ1 VSu68l ܰB̝CmLyQ܌,pqp>=? _u辗z8 u<lrlJƑTwCɢ|Ͻ-oiu*frJkpv5jΞo[ < |  '^e!\}A[̈ΤJݡ9c\wFڠD[x ra1 N$ìȞz``ƈJG12Zl 6[Xk<9 V=   mj)8quv 9|?; ]S7uG2L LoxcA9A)c#K ifyo:Ϸd!٬nο+/E3B)IT*z]G14Ri_L|2F~DY$S"%^Bo"IGPv[^ñѣ ݛЖ^:Yi;.rߌ4 FM/ކ0;Xm_ҁ}S^.u]vn/ܳbԛLkaX AdcIb?}݀AK8H$ oro9tT~'8s󬰡DPo-׮.Ѕ iΗq!E4yB" iK,3@)v+Ε; ລ~{%YDؗ`H#u%FE ~?‰*Qmv ]; 'i3; #ʱ N6Ig_gQ6c quiKDvRL>_W2TRͶ}_-0NBa[lZ*z$ cRDAlY`bA.l2k#b&c) Fx Ȃ;u3v R&=r4)P jљQą4H|1> :ut\9 .oۤ;6p"7*5s*06P\d>PImd$φfA&aHd7*{mi|Wkܻ[њ^5g|'X* PF_d@n4ERgcw$P =Sܗ\}K#TMO3[_G:XSQ]7g&8E=!=9460nc~#Goj7?pbm7AWqSr)si,Go}& .bUӯrӵ,`{2ǟ]_G鑻s +VzF^T.V0T00}uLb,4PHcicδ4@"YGTxI@BrP~ n[sf#B^xio= &\`#h95ʠWY JJ%c[eF6ß݅H4"Bn\XXu^D!F?R^Y;El+ѐ|Q3&lڝHý$9 @JfVLN`Qr-4ٶF#Y#iE?veL\8+ G댪cn\rU R|8˶iHX8 BpJ ̸NeuloS>fsZ/3kBިێA [4+8f=Ґ>?/Hezdۯگuw^8l [axFiKB=2u݂ ǎ?eRgE`dveE/ iU)F(XA xf׊=j$\y(QYlK b&K7&tLC@ P-@ !%K{xp%C/ɌAP" bܽʺ$xn-w4ҁ#p:\%Qq亐IF~ 4S~e$і<鬫jI7Mn?_F}g?`IՖlRO=R1|N 6 |W(%UjRA<- +/?Ij9%2ntjs0 Y{bBB89 @6u lκZN0xH,Rl`S{ D&Pb[8%uIK_ɵ OT+hSm! PabU% lRd"sxm¿5 X$^R)Eڞ:~.v]y NIj"mb(@'WD=t: LX y&Iɝ_T*&͛9 S&z/Yz>}I}2 vR' 4Ol"Ei.2ߑ]cQ@2l N.G ^Æ'>m?7$LاWӼTRZ{P4A]LE/׆!T+NBgٶe|!, `* XJ?']a M;832Wd y-v7Z]%Ep8{yUs}<I3\~t ]FccMd`a{漼>) Ipn=Wg8ͥco|M rÃ˛ 9c h\Gކݢ9AvFy*U2;jfYLie#V$g_IHLoRa7)&+EF:6c @6KcGFG݂?g]݃[Ov" ()N<_~H `K +ynfgYhn"d2#O;z@"(K)|PҏP#3mYi"s\3IhEF8'R|C9JиAYF2L>rͪY:(mq"4J*oGlR\se8~{.m`gT x0~جFbmZo$rH> tHu5wy4n9tJ4}, ŸZ Ha[nkqJvipK&8H mݖwS-2i7^uhb :{Nsy7t]_t%\(.Ew 7'2NFR8!'-Kqgj8A+g`$%%<RnxfwO>5 3\v%o9JyL'+OA@1;BsqCȷW0è)4{#P&;xoJ`3x"F%ݳ DP~A=6bVV.$͢Sܝ]ndӞj6(vvtvwd^59U{kN|&i"Z0V+ZR?^v-(#c% FW,|q.'$v@6JLtsX[reQUA%RHAß&=XNX((.-&i2E".Q Aez6t*JCB SPzt{޽σ5yl~#kUW]-/_UN}wjbX3,]Fw`Yޖ fíHe#*KD !\ǯ=$~YV,2M3$bV`Doe)H,\n?ȘV[u]ܽH\&)Lq bvWhGZr|ڣ.]|.O'ͫ{WxgKO>#%-R/P#mN\@ga*~꼟ğ~=ߵп|zꡏ~|끥s:Ȑ\B&'B6lMS+x l07ro~*>5yldE4:cPw/Q1 ՌښՒYTe yd*漜Xh`qpX8:҉MR :60H`IȜi\[- bse@營¿MȻ⤆XY;xc=3 䣓5z)a,5cn-݊7rCf 6$\ťmoUr?{vΟo^uvcS4/yFBB[(oB\|=w>6l˹;u7ëoXtqc;3D@c4i+0Y6Jf:F|rT(.x0P{ gznϕA6t{ <0|Q.ޣĵ$h?y1 ϣl֊7?tg8{O?c_@'?? }{W{ウ{%nA4 J[ ʥRKB>d|uW׾ƞ39v$M9=來LWIk=_PMLuͩ;J[3@0eVPB%N rwbNgVp1{ @sAa0ȕ\Q5Uؗ9%鎝kБX;5,2OVa|&Eo&#y__E3|&NL|&R|R\`b#{kc3a &>H3M*^i YOتftmnnnɡr4;\Φnv]F:/&3 x%fÃC :b[ڟ] 8# }Gp_$v-#HP%;2ˋ5VUXKp-=| /_vv%;,|iǴDT8}du" x{D%, Bu1l@\[:Kf.q;6nõǍMK =c8z$Re6jYٴʦ3Y+{n. X L$z l@0VRAZO}&Q)&I1L+YK X0 [ڂ\o6IMAKXDc1$Hݓ %5J\tI8ݜ RdMc%|&JY #:| CJY0$V1!/&Qdy@.xcITz/ 80ά١cVǯGOfvq?4:x Woðܔtj4؇h])3YHQ'bD|1ƚ1gYb./8Uc,+lfM HWbXpZd)_uC.v1y& ]e] a;,#aR(V3,Oʘʱ =)qQCo>=O̅ 0>-5܍Gv6r;{>~[?я7Rn>wO=~OVl^S=[F9t %p.*܈tAfKg&?aVE.hdhӐLKLɉ4dwK7 ]INaDJ<~tl\/2K=?8;}<|i"9na7Ps{,6ˢw9V Т mEfqH~-_d?uE\`1rzJ3GDѱ3!拖8T&t˺54"g$i>˚Se#K@AG>}~}gY %~{e99%͜4dxl)MQPvG'| p+p? ]WqLCIC6"9P)jTbOtvm_qA]7  ^¶~4* ck<'{EC@TS>,U-Ht\`TB6ɂ<V Z/k- zK5.w%iqfbYT_bCŽ{W;(-SVҜ>ɰ ?y)#Z~PSU{DC;#s?v?_ jWMm1%rj"MgRK P>nx&`R<11`Ӷ YE`2IgU[oZ`l}ʳZ? u?ɶQ'6AЂ^WjNW%h"0)"< `/̃iwKY %e{=T]DfZ-|h[PA^Ys"+Vd<ҍ1Ilot{܈H@d Tg{:e'qmu w 6]wۖȵމóc''y6;rD6vu[y&5fN%k{sO.k ctRUa,IfQ)Z99n8]Vd؄qb԰bw_获&p,v%v{oW+jvxlaŝeywj UXF..Ńg\{5ƺtʒF 7-/CKXs݊tq`ٖᒻnmoڻ^X}˷|>Ww>'j 7[Awx{Ja|Ϻ' y*5v;$! xeZAEɺ`@㴦;L4/Np s[C,)cQ kSK~t5{ ^WC VdX <.8dD![ sA \񼉝.;ñ/;ЗJs|DVKf4R]1CDԘNWOXҎX3w iMӾa %_&XPQڳpYX>ExGpzl 3{DSNl-3ٛRCύm'?\uU cr3\{uԼ"fPK!u%a"<5_Cz^3^_t˿9;+~hmcI;U@6:=ChN'G @CJ|\qۍ 3xbP LlL,Lek˙T'VHLL}^_xSrJm9[5K{r$bcA;#Kwo^܅S#s~}uZcĚD,M-0!zOebA;w ƕ f.%VL+nآ/EU٤I9\ tj9VEx"o_-7{õ 9&l1qYs84#YGo]ßߘsA\~$A/m*ҩ ݚYaﴍ, Z<((ȸ),JqQNj:ń\,- PC*~KV1{p/ _GW]xG~G6>_Z7)= yPC;SQm'^ gPHD+[y;u:UY\{3s$&m4 ]ܯ1-H #,ƀD,CKaWgkT>9%`vJ%c"e܄C&4,'ẽ_`"D{:&\<90메}R;wV3f*RKPV3$|.hM<߱T` mY6!NsI?]7b>ٴTҥͣlz-dw!A+_n ;1סzޠbӮ@YR<4:C<-׵C=[ƍ z$&$Zl)a0 {l4NRؘwXoBtHAцRmN N:d`A~ ,:]R{=2˾ΥO]v\j|w'4}I7M6*IJ Tv e1 0x8Jd Tr6vQͦ)k,Nm= u*DAKFi Jx(_&S3;Tȵg$E8?E@wɂ}VS!'$co-[f y>dl\]R7 s@C)E'%nё▙nPŪ;UQÔXcwZV.UzRZtLJtvvF$į'~S\޻7ҡB@/ϱFF=#LY`P 0#w[c>ྋL%j2bf_љQ%ᬩÒp-25nl2Kf! K3Z\ZrW6zI)Z)U#f @eʄxP^.K|\uy@<㽏nɈ%GYY {KcϝUy';pDN]5#= _{k $ %14,ẈrYKxfo>[}z}!= ԅj>\ؘ17&:kT766Hqo!Cr -0K:JʭgXIsV:4pߦ;AFM$gFYkfTFld6Yգ)莫W֖(*ѓ$ Ae4I&rΦ_*Zn{ؤ> [/ Mx8}u^y`AʉH"f! o2&쇄rsBcj[׏c6S;?q~[Gڞ_DqSt-eSRsҴhg 4OC]3i<ԡn׆&%-dFYw\"yqc WX??6e,ɾVTbs(=?keH}d* Xm.kFo9܁??OΟ?؏r_}W~W{]wFsu4gH17#X1r~SU ǯzS-U^YrݬMʲ\6.|~J̲Fxiq-z(Kznl&)920ǐ>U ,ŹB'tEVT'U]eB4 3 X.v kot>+'yq^7G:ax(%Y4YM&So[Zg6;6}/?&\>mO9ӡz`g #@q6h]+G@ ]#nqeCsXkmcbO3WVEuwXm @+TTf lh)ggܝZ}S-%g.d/QS7] ժ*.VԬ=7wO4zkjn_ǍX?:?a3 ..3.~{>y{o?{r]=Z*7.WL@$d&@Ý͡ L5jk^փW2dfsWĤA+\˂%Ыڥ>a0ry{4}^8;~/cx)ўQIII{=I9JA;sIX4˄.ok8|&UQ0P!rjȘA@6 |@ɲ JsClx)k^q:L+M̭x}cqKvP,鄽#,`)fH`T&(w5EHΓ !/IJ<+HEP7*sRn 99 llaj*0nl3>XC81G.5'p~To> _qA나UMJ%5"$&F<2(/X⚘)x|V4]:uCXa+hRd+ nbٻFvIټ\P1'qA#xdlL(hS|`H(u!~1S+zgtf" (,lOK*C1f إ4*͕h<3ϹIr\Ëw $ 7f10/ {_f3j} _Ҝuòo[p딟Ofn̉ ia͑3^q$>HL:G˪ŦIT"݄?=?f?W^(Նm[{ԧK4.E!Ȉ8\wbLP7j}=ʞ/eҔ2w\`S7%UJkty_:z1TbomÈ97PE[ wHM'MA<39?2)Vb/U!S~%8"iğ%v,A#/>ldQ-w3~JD| KHdPx'N!22>J D[{z:K.?;{S2zEY͗9c>:n 02*j)ұ8jTx `QNR^3=tܤlZh%m:9N<|1AyBPݑ[Om %eL.ϼwŠQѸ3zMpഀ֌ J׋ߋG/h mlmw6cCޓ (*)ħ۪m(vfHEPZb +&ns\(eR,e[4?&Z9 z?j(Ҥ{-e$zGI`Ao<:Lwc.Dݾ`vbmwһͷ>G~~/ϳ.? ռEEU#U^O vt9۾/{-au@5'w5sl Aƈʥ?%x-Rl^#2)k ʽX R&'-3ї&6!0w -( AثJ <@ V+ 8w͝_ݵ)5^WZE.20(rCdžq@7ac3ɑi+…|.jdMTF#hE ,$c7?! nhT.veӗ5<9;/< g6(rpkqj'K~5rC%̭3w;8^5M֫Bܕz>1G62sη nvkn;5xP|xqGP5V41C_)rFL?QsD͏D{gC(aR[FO_a,+aBF94 R ^M!O Xd%fA7g!3OĴ -ynfnr+8"N0ZI^hX ceR` J8_%wݰvpM fI_;fC2L8>a.I_Z~H4mZnX\c)F|(xk@S 39hsgAs@toj"齎 y<v,)ñ||B0K% 19v؇wlG>>z߯;{,6qAP-zύDD= H9c V־Ξ(9u RmVU %c3l&Z &㜠ba۴S;'X+9"'V/1l~Kl*7ڪd쪅9Ã2.Q.ޖ@ c$%Tg_ [뗠nfԍ>޾ W^@J)6׹fOjD4 8JYfI^I/B[c3,\!؃=)4Nz\_# N_aIcX _E皻sn@XVAH[.-L((FVdiF56G5.B(xY^xu Z~vS5'ng+k7T~Rs^_ Jcס]t7;֨V=~pl Ìl\P2~*LUj7=u΍UO=}w7';FPes f`c YYƞUd0 ^ߡ }i[ {pl}U-u>_)uvTi:aHsC F3[ 1^koO}}/={g;L9 lUnpbpJӁ7RJyszŚVU0:2^|y 53[zI7̨ZP<`HNQqZ9._f"e:4 Z#AXcKİ㸅 I Ͳj¾uǦŗI-@7\6ԅ)<<~Apw͆g3/VQZHpG9%z8 \?;~1]Ճ[#KڍQ})TOGYϬiPOԛRK벗Ã7R,f{`eSklsҗ 43bKY@#Y+mGwcT-U|l #E)0)2OX{"@P>7M+4?{;μ.rKHz1ڵZ1RJJLRizh_hm/U+(nDP!EZH<  Ӿi@*Aj.D&RR=T#<@7DJ+BR3#By[RA#כIm=8n5~Z&ҢRВ$Ãq&]gؿo}Fk۲l6F6 6*IUW'&t*& vL `Ɩ1ƲlɒfIOzzt{ZY}tW*Ǿ{>ֿ}  6X\9cS'- gZce#v𥛓Y0ųLV¨SQ.{oHѴ 2mԺ'G)of @K/cq(3&0`&YZb+[@l7*5 4"^O-;t^dqectOau=R`4Dd$^OQ h& FVĔd|j&Xaqi(JN{t3-=#o\>M/X}{ǒed"*-̷bQX+ wF wЧMٹY;;3ac]L&+Xt⑽[pGmXiù6]l÷솂~Ψlk}=e~㓟OП7/WA:1e:2,ȣɃ\/~9t=oB yT6e /7,G4Upf]~ogdlYQ`lP`cKJdaxh5{7L"IeYrN 4rtި[Bk9ӅmdiYT5;KB298sN0f<rQ˭=9>=)(WGs.ul|(5=}~[;, ;}6Rx mfbxD\F9lSt+-,}ydREI)[ ?O\{#yb7}>vW~?.u]OR<*S C(NHdv[kyqIĊ[7JEu]9#ƺ-0 ( o}o&Ru<cH]A)[@v,ϕڔTXwAeٺyJrp3VB5cῷZ߃E<JMsf_xDqSw7Z-Eb`n^4U0PIGʸid= t.gaz4 Pm 02H@ Z=@TUWQƦ@`,R>*`}3L|DBk% GSPmm+NlH͚Pp ÿO@a"\v3>\?6#3MuDlU,'c%l"uՔ /Iz|~Cx)lMa\ .;' &6,;ռ(@d ૵3ҏMPHO>ɰJpC\)}p #p fEٓ00=F9 n:(|#>Z-`VRq5L;nR5T"\D{ga܇kElέơyޝp׽d.NwA :j'` BBтOYD83Wr!ٷo[WD[Q^/`Ϭ%V_A%x$zhGAQdߴ &Yla'lSxMw]wށ㧺xt 6jr,k_{J=xѿgCs5IԠ!pגj. | 9ؿX וy\#P a$0R˩랟@BŔ'S_8K9*ݲ c*X [&q޻%sOGr=J&c# [x̵vg&O3%3A,gJ˿݂ Y3HұyBoA&%P]}̀b %3&YK6dX6JwߐY $rҙL9%g52gJkӪԩ1|M #Xuk&:gp HTw@'(e,Mt5tydYRᚥ!aΑYHB(k!]Kr[ӱxzՅKcHwN:j21qm^/-w s}H#Ir mK؊#];na6l! Kf 'ׇaʲ'2ϚAfKey=t]|Ltv1{{161`lwyr܎ mU)!}9jt ώFtm*oĤ  ܓBܵ,M{XnH fqvÛbxSUezab6d iLlS0ԧl"m8,I/4"V-1<@ߕCԾo<#fD.<> zǖȪhrd Z<_Қ(B7ޭ~otr1v- Nl.syVUU]v(GG'x璂<@cdPmIS.bl "rjSkC$=a0oJ9[ZQ#, R&5R~;HCHPLbx|eK97}wZR=:$WׅٞS.<:;sV(VU.!Rs eʨDo  (l4$95cG0'0v|:$3AF/p͖RsRIy=Tȋ<%>[TMp$J[Gݹ/ѓSNx=1= u!I4WI!:JY3s=W f2Qa٥ܽRAmoh8y:c/6/:v" {G?]W^ z6t7PK7g͝.asrqf`@C~y EMƦo|c3kvZJ3j:-%|? QDEtߗZl W-q.u\ w8~D}Or^l2Ǟ;g "%0i9'.aX!Z]{܅}$;pLCO\[yAl+,!2)Zo35(rma{[-u8Arwhs0,޿Ӂ[8Ί$Z 2E"a6F= %%',K[{DUAwEiQ/:px oLxh4fI&;[t\z}qhh>Z-'Sk2u= \.r¬jSȰZ+.i z*[ 1vzKHtQQR#fI bV'rNV$c,/nV}= ϧ>xҥKPd! 325 ސP ;+Ӫn/..w86nl<5NPL%(k]1fmsE1Ĭrbz6{HmajkI~ \\uC\@pPdKzd\[1𜶄 ˯5CL"bHV,]ӸGmX˖3M[4ktxˌv[g@~;/k{V 'FE.2"?<ȥ!:LIAMFm*0=ӋrMo ,bK(* HA6$jڥY̆s@az}:_{kaFW 91xO,%)p:850Z;|wawf `s7߸ qew|의bl??f9J,C`=nXT+˽M" e~M6fՀR}LzJX&q5~O#@_|9 Q l$Frc7%މ;v,8 c0[Vqʞ@|̅O"O1(;~~%wr۽[dKe&M]wQrqsV[N-yMyɣ8Y z_>}2͡6 x.|o3x6܅6;['Xl߃3\шϞXWКrtBgosEwYEh?I]ދ<ަeE1h8h\Af%zG 6qiksVclg\~t\bƆb ^(jS$c: fx$'rL,R{_~ :m}"vN`,750C \ˈg"IbjQf`1ppv`|Kq&eB8I_o'dA:H&0BǗ{%:f%XkbyLyMN,dXN' ԡ qe:~ac?'im' uGExpݡy?tcTF):݁Җ^s/K{] H@ܡ=-zxoB˒rcno7 K]浩X0ȩX{et78vE̝KW;~wsZ݀ 6" Ї7=ل5lDߌHQfKS|E*݇;ŸA< "T0ǁR9`]ؘaEXj3ȃY]^݆nm VcnXAAj[5DQ\~1xPQ+Έ$G{RNV8?/Ͱ{L_` g7.96o $w6 %1.)CB'{AMVm]4* )uّnv}=Vy>vyGRUUv= ahEHC:,cw nwEuqYUS13z;+]IKS-p<4)qRwtSQ D" sH}Dİ6rG}.(fB-3iSlXRM! E,#rom 9L >O1m't.bکETE- 4Ff<^;[ٳ//mʙ?tc BǻgĨ4))&'m#BBd͐x& 6[0"Mdd^.FCߵ?&&I>D_:MIg 4,QO6҄diCm*؇ ,6&+_y/ЄBm+X0!]޿%o¸  ă+;˾bqYa;? 'H%dFWJ]_~9LL bXaQME/#`2mWL"$|MT Ey;H,Hk ƷU\M.^k=3DR)߽GV4xQ+Ң`r'gIA=o6 ))0@oλ1E,ECE{H{6f#&Ճ ۡk;$ C$>Ed3pHUXFεN6dL;zպ֥%8p'\] PM2 ^//Fc3m?vXZN7<)'e 8p? k[EM ;T]ЧC tu#G+F:30+n|cpu(Κ^k8^9}}䱟sP5\}ݥlpGkad,덺V}KGួn[TeqBJ {<&6t:mvQ5s۰B$A؄× m(>эOWN/Fpϑt(O]ckV^.xydsk|v߾??c{{v?ܟy aʵ0{AOQ2=IQ̨))oă2z.1%;M] {BR Rc+MCz`,Ov @ jw[n._Gȏ?oӒIC%mM*ooe ~/ýlE?6Q ë_{`_}C,00qk 9?Hy<ɛY:mA&Ħ`԰ d~2B5yѵ4uī64j{ARC^n3w߅m!>v@Ͽ/|;PUU%`Ƞ\~o#GB֒.I+)-/.޶vkyQ۝Nw!se,۪z5H`0Q-T pIT20^}VC`glg5f+ԙ(fsEaf-N+Bmja-w߷1r\-ZJ~S xNZ׌XͲ,@Lm; W̎xdpŊvb`:#TPIi$ة {(-Ua=X5,J'R 6A %!yv>t4tR4xʴ컖2|Tl3=*¥I"4=|l =钌7;GǰCo* [dtdAq(\MIM\L{zp5,7WR& *1W )3R PL{'QWOCj.-䃪&㙍zFRȳD~%MD&{Shx(hR'ڙĿWgIn*g?x4,lm,yeJaXJE0r.a`t ,b"K̟ÎHN$26:9 3#֔;TzO|<0pNS /%0v'u {Y k[Rnԛ n/ǐ6=B|5e`TꍔcTu|m-޷ŏ1{Zǯ=7M`]zP)|.I0ҩźu[n!'j*lov1=5wNᛃԧ C~ڊ; +ҋ&DI.cdF׾3wuҝ B\k)M8#w2q7%eE : =.7͍lYyfXT]R$je`a6$YG76E_9u} ?ffK7ٍ{nRULF>z)]Z}ȁf~3 S ͬr. 2xn<~u]Wn'\pL H%u(3W_#% ~Om`a! $&L&(53*`s .]KpvJKտzkaa}tww̷S_8$Eid^13Y@IBB̸ǘ!97H2|r][gF@ol3!o1L!ɢi #yDvMt2밶}e շe9Yp1s*)x&<'BF,P򙤈>BO|vijg cd5xE 7#2 |*63x,Tmy w,}~;57w}%i!%0( >8Lz @C<\//%w7Ǿg|PM j8vb=? w\Ѕg{WP-T,׆F[E]`yFG+%Ⱥl=־̟A)^ gms/j+P%2~/UhBCn?cYxZQ4KzY&sQ+"S~'Є`/** 0K[5, <*ػЂ07D,q6ZWLk`.,a8~5a ppԦ90tנ8܂&[_܆-wy!ɍGyofD>z< 0ng!3*9c]xw=(f,AewA e@J)Cw-/v_W^G}c VlGA&f܅b?l~nn7ͷۺ3w&suYY:C>EOr; P7\p $wj}]rJ!fG|tC5%FE:}֯EgYcz&( ̎A N[`t2KY8(ohk0Z]G<6Er%#xLd5jKf0DvlUVK:wǎupZU!!v=rRbיN5݊J nIݺOJ>8|'q=TSv >W1+hvYBg)HFit(b\\4cJĮ:&@R4Ig ()oZ:2H'^|nA 覑4gf[=w::< 4DT%yuhDHǔ=ո 6q xٿT[QO : >;4/$?* : fVf"CI yhG\#(v|(Gs|C/LΪQ4l4!$665gD,1uc * WILr"V R$l R^ ecnh~.,6@ sxyUV` x[+uvDP,A>hv֩x[;$~KwV~ l=><;#7qKodd086_fkuLE* '5s_EؼKk^/@G`^2ӯ}fY{,Mp! +Ȃ-螔 cgd~29JEH1QqS߾gkkOQX,̖lD0\Eb74z]D'4?C;h2ml.~?V_nՍw {Wڛ/^>rk=?\@KY JAm 97pd`qhƃ]d 03̓ Sp4r<.T<,1F#9HwC'WYAksK ;#IN2P >b;ߛk\}܏=w G{{{.nn=~7~(!Ѵ4&59prc-,PaK8 dãUiGOQi )w!w1 L Lї0F C4j+141A##TfBsa|򜽌)"ku m|܎_kmwnnnhNM6znM8̅lC,AV]Ok0Jp]Q܈@^cD96"i &?f_ȗ{Ng,:oa4T`C|ڳi؟(G6#z| +"%M;J&AX =1~hF[;pd_E*(醾<జ]AZ`%0/9]p=UcQ"1 dwd,0ngZٹ-K$ þaf1s|ݱj#\XڜrH.!|?={`^7~6[+I`,w >Z™)\ZuCW%XO 3 ulXıiTB]ZtNL/cVyΈc}m)QH-ηR:)KT6Oh B FpK%\Xuש[ _湙 1iIǶАo!5eۊ}Ֆ ,gwo{~q7S}>vW}x_}׿.]t]eY䙺<!,;ɴh)d9!3$5W|bNVdaIi;vvjks8NqóٜS Ch3+PB#ȓJ UQ0H]E4<є %o Hdp#@5P$XEK=-ru Ờ@y]ʴ A&I S &f%x~m;Ϗ ck]D& *-74*r4eˢٸβK,<]ni!@:vvCMy j BDx( E 4kqFLBRߊUON#cSCz?.mS=!$)"[bcd=X tS):sRFs6>nCU>: =0ԩTݙ5 z*7>d؟%4-֘ ;kT_22m졤'Xc (J: ~`J@0%7ѡÿ M9PR@ (h8gїƷ-X_YAwY8R*nct/Exop5_*w&_r$  ]ʾI Xan8ƍݸvn4?/>??|O2h3B !i]Vb8v(;#Ѓ"b. +K-[0)׷K/ '+`XQ" 0,w =D-ރY(L C, WZ-OȘTC#SHlgrzX$fFŵ$5Fuzm2c@d8s.ſ 嘴g:tt؍''3~5gi^^tSLEԬAS54G6 26\ܚkx5x>=K_\=#曻pdaC^6ؤVU|$%{Z=FqLq< |onJ#CU4=6W[# G@/݁mgGk8vzOY8t@\=3m$.'t->jkB /ڄ0WEi,lDyb)i$j>/ҁ ^v95 $c)xƸP1P<߂AK#~oi},݋z)gH$lš5 ;0U?.lF>/ .г}=@`{pA_m# H2eJR H̖%=u;7"ȓy b.SAs϶BO85X(SgrK}G\I*ؕbt"KJ{ڑ2>ʼ 齕Bi5"M$oڶ k& z=*;wdΞkx5P8G`g8 ~tqu#T]RJ/a=M"饚B1: ?&L:1bsQ&a@56/n#Up~"zh)z03TMjA\,$o! 94rI'%CDTQ dAdW7l~؆`ӷ%>w͆0}a~߻7% ZVts]N(]9VU= ل9x/‘2L'kBRӐʬQ{lDۃgF3dh5|OyjR=6J$}"dZM 3 "6hL8DǖN&&[Zk=TD_JRoʭ[Py5?w[w>>`qg_F0-C ]4mW9FnyPUW\yX`p+\R<_vuY1f2EnNN5X-Zw$7Tx8SPxa<ɡ)\پ?|>䷾N<_8@,BP%Av++@ܜ#ONE/ vS'-@}2z={q}t 7}$M%I7i-97Dž,p|,"gHZ-Ӳ1ȣ +"ضXk:;;0d&(axffx-+@D s,Ko?  o+)5N-L@wbߎun@)3HV }uVXO1DZj(SWjx߷a\)6ݖCC|V*i #`\ެ](ݺu YdeU1^N%&߫<2i~eA_͏B31hoQgG/4>r<;J.Y|@ֈV A6 @䋃 F_LdVh%nr\ğ /ßܿ hڎ<:OX%Rc4cKQ" ԴڿZ ~wMނgP+./~re҂JHֳ< a#= 9 9pvW}|cE)iцஹN \CwadN c@$V2}|wYha)%EDe0ԋw)$|?qF6]cIy dR=!WDGpAEO-uz6Oً߼Xkp6}7:˼z9/B҄Yh8{" ʃڽ0qV~7L'_Ed%emm|#Y#ce (rEZZ7Čѝ aYs>)J>kX,7h=jDD9\rq$ XpDpͯ#60ώfK Feޓ_=+}r;}}<~ 惹핡ϖaYՁS8r4ǃrmFу}O$W`0JqXs.BU^U*xRPBbT>r;QQILvF.4r~j̍?{ا oC/n?>;g< G(Y&@ <`uͩX\=2򺫙YMeaY'm5xUYX >vR0^ZZG A±? E$56xX[D,1,8gB":2t,%/إ .Qb*읈l R0|Ǟڄ;]^s#G]+zV.u %B1F縠RʪVIl \%9JR^jϞx8 sGXU5zt;z ::Ռj~ 8X^>[[nᘂG09d&X8Hr|kj kK>V:EEjxMw_sy-)]zN%{Y24[$E&FI u͠0BMO5_Ӹ08uQ0oÇ߽oG?7nSns=zhd7DtQ OMPƱE,MKpoZ9PXF# x=Z.vÆ"c0־̻xpJo݂W \܂9k\O.ZZ*3_Y3hkolq?tplmt86(fN%{Iu$%rV57W˘ּyjHOҡjZ3 2NCRnKm#$6h266*ն@lw9Z> .ϝ>qWzv]'>d'?_b A]\[}߶2$<˫!]^>|di=. ]]vVeeyV%x.䛋z^~a&=H䥱c:hcN4]*d(=|umٔl 4lIj %C!"ʥU;dL5c(In2Z~7Ͽ|=lr i:5:5 ߫T3HRfAr-ў1oZ[Q F5*UjV.@J̉l\J5[iv†ƦV阸،\!Y%&Il*q<ԣ (.ܥ֧nH%6(.Tm~% АM[L}h8+Sz&K,T"dLII %$>1MX3WM7d|5im!ˢ_UTND3zvʞJ@INoتZ*$~rIw %+s63 I㽮`dTd@53ww&:qk@w'fyjЛ UFd9Zy6,Ο}EFְXmؘ sB g,*#`c,*Ȱ[ ]\gd b; ~  ;vw^ž?v=n = g,fX^X5Z>Z)3b0:A@rR9.#r~txG-mdM-r/H'2J|oQ;,hVնрr-w"ew?&ݜ[n_(J ڿ؁A 90N7*.=mkmqI㔟~[Av 37LCC O)`eo&9fm;--m3Ek$3n(+=fh|rٲrbsT|s-IGx R|wAb(n}=@3C3D]y)L J]$ ڞm#lf|܏;R`bނY?#a zwOxNzx#o=y'YarsP,):|bJdK'tՋOR*m iX:)`xN,I)dlMn2o"Ok^ACLK6(}l*"PBuR K`L 8؉P\ c9π>jF&O`FVZnHۄR5r(ѭM9C*$ 0AP 17]mѸmJ5i, ո FNQ,pKϛt?= v$ E@m0EHJfC7ZZ+q!>  Y)%_6B S 2y"eoSJM\NyMwn} x`:/ v~FC&ZWq7%moHZ.?UyZ7@Re"KSUՕ0 rJΣIB%L u`A0={'؁^8R Ġl*2btdPdzR@?KAEYɯHޏ UsaBIavE- d{{{^N@yJ|aodL!F0/sϭ/ amSMy><FlcsJ;Ն~bLM i7ߪO9Z2͛g}_n<ܡ9K"I0;fd;a0PA<0#EHXB'fDdruR0,>;- ;}n8(]a֏AL>>gdT}  V@RTc螖_㮰f 49J^ r,; 2\c}g.yO !G6lyO؀o?,`>"V76MϾ yn:4wtVMfT2ta  '^!;.5$;VJ!<|<3@ $D]0Į}8,A[۷3F"H lC.>s ŒW\ s"R[ &r6G5^{,Yir~;`iᐚ(Q?eY+ s M~NRN+e$oQ d_f}z+6!ssh`^7%Ӂ]9g^'|nw4 qeHhYDk x_tS}nJ {.+1Qk-,YcOQ8 V'u8'DYsnI"ҥFK,5^s\Y0; Y24%N+Uzx%nj؁2rfz.={>k #<|;z u&: ,2oje|eBLFnwڽ{}=ݔe**s+1%IQüJAX1d{lIs.̹+If$Ӹ h3Sx p;@]!UxJ$-gD >>G[$7ų=X<|-!#KoZ #Gr(&w[D$$m6V,i&78Aebadĺj98hFGbUwQrδF[d]j̝ L Z~3-eoeS*|; 畻 ݱ,DƑ*J-6 ԑ ɝ1| !޴*y¨ihZP^;^IQ{̲d4)'L1tL1]&עUUQ -Ya;޲Ĝg5{ E30~Y0L1-|h)zwg R$3bLb-1Nv@dd0)wXd$*ȢT} {@K-=zm3) `ZgdyE@6a8-e}i<Sv`9{ nRFv|*^p ßLy|&mnKBQ h>3bbfy/O{$|$"? Q~•tuΔ}ճ͵}#B"! ;͐>kgN7v-<,X,Yz .- 0X·s61#g3V@6"Vĸt~fab6_&&,.Ź(qiڠuO+f]L"ΫgD[Oq 6jN6Eft΍wo} nS.,-ː^pBň|. ~r<,uv:x-IN`|w"}hb | n-Nq3s ٗG,+X2s,hIk{Y#~@,]4Hw<2|.}ug__w=Ny}_6{mb ;+.8-K[A+`of'l 5En`_^3P  P}S"#\ᆃ)Yk'(e"IJ<4!4׵¦pɿ#Ƶ嫥*>rA/SA*Ʋ^dqɚ=bRUKa:Yחpx?&&P4\T-ſ ձk %!l*\ ^a]+5u666am$96 +N]#'cx~X5;<:xh ~s#? {w80MFa> yq!6"eXE)Jb˫`6/P Za!F/$l&P0(,+" ״4,ok(:F/m4C5:H?N$RCH Gv=gM쳉i'kEfZRyaعgZL½zKMӠ/F5"@blpŬ{w("a0.ܚL#C-.IujNB#D8ZbMWiKF1#lF:^.)v`0c{po|hW0s[dy45 &c٥0m*kKf֙ӨճN `@1Vufٴ\̠MGil-ٞ7Z|Iq=ib5@wF]xB8]^Mprջ"Q4 #l b*S,%Ϫ8dWj}C`@Hzς3N٧WLoxE>RSX?(2_A@3z)ب0bۨkdrwSdLD́a2`FKFx XyzM L`qf5݆ٲ_ҔJڶE3)Yjf&(^ ZrPd"Gcɧ;vK { Jqa5ZU^C'K.ԞS>E>5zQK i?dM6{*y1)_տY;T29 YX45Ee8rp;EME!ـS`OdOfǷe&c1iU_x%6Ei d(yQaш a"P̲Z`Hbf0 -ߛa&M^cI6#05,3뺱'. pa}gV 83v0X 4NgY`sG'p ~~EPxދp0O`-dy Kd<(HDpҽn^bggbdUE @Ȭ  M*k%a{cВ :?â:jB&=hv1q6g<# ùtBSEj1wƦ&H OX5:i#9;^0 _p 6)6r)!mIv$plbs-.ll&Ǻ[LqkA.2y q"&O]8'.60ח.p >pIF"q"^=$iqj3zc? gd`a=pt'?5>m*1V,=N؎y~&xLG6FX+Ї*7$<ӧ?2hbjXϵ#ODq B Lst$\pB?Y W e3/s77؇Z(e.eƋ&kU: c[;08 %COܴc2|@L.fƹ'^0GIr Z`6ywK#`yIJ7pfjX!{a{|*?DE5WybΆý}s?|}᣿Em_@<_}}=y8$ú s7!ȵ,7M`6\Bv={n?4ӻzBĢ\[UV ;Ms>aEBZ6J BR] c`>|!1|l8#"h[y=ܥ1Ɋǵ5R5EF Eܗ`wԢ6O< ea71-9X4F˫i&'%8.B5@"o|wSO?|HB&m&˵E'bnHeRAi (ѕsO-]"hEpo2Xa @vGB0QEs9F[4[ ӂtAb}ߞn;ت'As)἖;&)i O=g<ހ=$RI.:iFV [!zخD"btUc+H7dڞ$2%*(p3JkG&ǷdZf`D1mPj/윲A1EC[k |&?8ޥ3.ObEsU%% u:P/+ߧed"M=b&MKsOƞ%88 aC[wֹ!uLIHLJP=zVnyj_\E,f>k*(u>ݮ!g0` |ؓKp_T<`xozmq^w}wo_ CGC NM]eCvd00z*7{ةs.&"P: ?s ;P90FXXCbTe0BRIL`ِF1Nsurek'}qUʃɜS+k1tB';KswC46Jlc)x0r>7QTlEnc6QZ/g{4,{džw V7PmY{Tq)=̓K>uo||aeppo 0tq58̩Mb?`e|RL.%'kT,TK:Oׇ1f8\sHW Iduh|aԙpVGp?< 2: }gkÚ6O/Pp#\Z Y(wL@f罖 W{)lb*; QN>7|N8^!~m UcR*f,3sЊf+a{ጃ/s;UcAv-XNt$ >l%ٯBd#p0bEyX/9L{L`ߊ&.^8 t>.U(7R0c$J1]DydhI>>X9Ixܹ<>8aLo[GabɉKއ#4:9oa#<7.9 /};{&.J*7M˽ }d^s,χl) Nl]x$cAMתtпfAJӶR(E V['eU~aA3E؂;1JjЯKm0s΃y{hj>z#|_D_J"uH+ʰYW\Ew!-̽y}vn<Ϡ0͢d[rM]lem'9cd㠯3 a?z5R(Ng&\ff VAMjr,eC/l!H&AYוȺy4E> òouppӽy}g'N[+ZC>~.f2$Ik8i9Z:8́׼faxlJn~r(pZy@Kľo\xU ]cگE_?cI'%Tw$l~X^_--2V; -`D8s V[w:R`|piLl=恗:e%$hd?-æ5޴do^|,`HؒI.W$H f;m[ 9Wqz9DSRQ8J-$iPLi> w;dݕ$)“MEbkiw ,/!;FǔC{I}i63E(15ې*kmű0>)ΣׁypsAquإ8<3vi4]cͨWɃE.DHHF?naQTN0uKeגTǤdXdqYS|KH@F%rϣWkJuׁfO+_?k6\˰F%l{z+{BҘTH1Al053V$ΐp\z5ti IHLMe-͠SXg 5>ee`go+g$t3ذP|裉t)EN~Et12E:[ч(ʗn/ n?60#kpa. I0~ܻxvǖKgcaސ<2n{^ IɃǢIdFc*fE X-L=τπĢjEp^V {H'Np_{4/L [Il%,$[ .l;7n$M?(O]Uŵ |g.+ ;CxSmy,#?2xGot*ÐusxDZ6 &I4B/R(bQF= @ϫIf$He!6a9=J5~:=<-Cs2?47R>*)sA&}.F*ezr_}x>Ipׅp> fH[=v0&,9x+gaµ<1+$137F03"ϡ)†(TZb5&vQddC,&Lnf7DCb^b]H*!(1|K0ۙn8J!CLx!x= ' lֆpu laC׾$Nr&GԎ(hSWI&BHn0Qܫ}ƪ`ZFyӚ[*;j %Q. C,K,|A>J[bX` bg|$EpneYISʿ oB`{ikxE&G 3~)s mX:g%رЅ  Jơ L ûKwFpT4 #erCsA9O(}& >[ 9^ ϓ4İDh1T9W9!x>3|,q>=:0.v-uSru$c׈2'-XG?pgk_k~-C^ۯ%~~"@ٶG ?'I#&>֟]z⏏ffa `?NgPUոU |aqZ&UUx6hMfP"k%ڊ%$8uޘu)lJ"l!05<.!qAX}bF9jYyGţOXgt.(͒h)hZq&{T WB |W;?+=6NI}#Ƶn1@{-r)4L/l~ +HY4\S]K .LIMLeYWxB M'(|F6 ro&$2"r^;B"$ʷЅ(v%3f 3]"K d8ΠȔC;u3(ꤰ͎j#w^"Q?HBkӺ/J{J>Gae=$]}ಐebhP(șns-Ih |P!@|$"!yEՋ@yl3$6*m-H,U4-9b]MTo.75HI2rT^Iϻ }X*[1\ @qN^ M$W T#D/$j#Q?`tTrsꕋ0JUtpn56XxՔ~ΦmPcA73kqzv̬fjC[Y4]#/ W,…uXŽ%d*d@[QԔEfg/ѐVŴY/pNЛ ~m~ NK/g`kr3m7q*J$9h!,-$y cenTjt{D(Po3go߻˷E XҌ6.<7eI]!8r<#}8vH߿$\@A EgvO<0KzM/DxN[nu@JM &{Pòh"ȫߣFX$ه 1sd߮5 L! }: E &C+Zrhѧ^vy}vo|+LC]]*abII,ZQ =K:Hi+S=o8L^;V@,d+1kl"3t ]Hg7 ؋XWcGz9Z-uc-=7 E)!f#0ecԛXn܈֪Ր~Ml`-\;r5=~7b0B~Gp7صXջ,7K̅}d>3 '& 0sDZ\2jaMXƜxdh.*9(@o0:$5I> Fst'R>ImFˆƊhdrI7Kdؗ=R:-m; /EJcq ~!3nRtF։W{̐qY8R6ִlm[Ma W6m&xj}̕9Q]Fpz q~uxg$N22Z"ؐ`Pz3Ͽ^0x>rn@J^4i2#婒lgyI#'ޭ#z8 MbMAR K~NCfk]{0Gܳ~7k8GcXB HT^Iep[ؿ* fPx0os|Y$r0fo5ETCט)r=ď +27I gqdw1zU|!ɩfU(>ǎ 7,㭣CQnp&NN$ڐA@BX<^Rxb)9%):3sV~nbX?/c`4lK` }-wN~/5)3GM);*g.$TQڟìɗ=㱇>ܻtCe<,"uN[*gۉ[TO``L*goa[c`*oAuCہk~g7/T KFU4bDb8%eU4<L!lQA@y?,=ڞQ,T)Ֆnr.$zېn.of飢8F='pmim,ù¥+&vVQ\i {#&s>p#h|m3s8,^uJf 8k%TARP6jG2slMXF+hoi 8mH"LDZ#ĴLPQT)f_Nb(N4Y*-o^X\[˂FSdn@IquXŋZ;P4/j?%\lߵ5m_/7??Ӌ|wzb,ppWs}b_.fz\ Vc;# 0G 'iðtau_h%*IRhuQ6H EdX8&Cd,;zE9McUk(<4bfWw ñ&NRU?ш8B Ƚ,S׹9^oC}x-C>WA<%BNQvqxW'NLF̏}2l 1,KB`fy@S '87}%`e4MM%91I 8qq_n֞Wj>ObIUAFRYd)jc"'<QJq`4R<ּ,&Q֍ٟ8z0FYccHL˹dﻝF8 >Q8PËv2;7 tϠaY,oKwC]Z0R 1o85**#eVNAqB16A2L`sA 1`dL~3 qs3cKd\sw}g\c\ʁc#}p]sbfZ9@<;Ks"G*L^ʵ3PjVþعI\2XWVLŏ9dYbm a]xs5Gߎ_ۯ#_O XH(idIʎmJ@NFyAmC^@v {n<{;RwB=kꑌ楅څSB:lVi3mW$b:vAmRh [zDf h944#gʾP?dGJ*Q!ՂQ9fpo|JS)ҡ'gMi*͞QK0u4 D<4G}v$ []3m֊VO?o`ZRq^1" K֖<+ͳxLQsL<,BH>)4sgA?.,qn*zPu|޾- 65% S:3˴<{ 븻xMgHn,Z0w)  }bpcsGXbJ ([tOs$5֙!(7ǡpPt˕ͫcz6 n9 xg+装`*NKF-ru(0A j=CM1 3C6nSA,6|ܻ9c# g UYJL[7; d Q-2OOӔpU'afxX`VMZS+sd Xiޒjy&2XFIk( AKW1\RB-"1U,^Jt`}ǟ}o?ݯW|;v /}i__!1 kﲥbV`[Y40/ ɀa % qbfϲc 1+e,l'Dx*8!q ٺGF/ +;o SsǧOBeE$c>y+xPwh, &gD(F^UUcl[sE6JIH*0\ܯ?6gOְ0g+?^vE 1I| 4\mwivYatLhJPdb(5lNM묍5a4o31F 9xk"tEp/ d+.7L)v1Mq,yYkGGpua>"JgP :1.r=LOoA&:/s 6d&w[KQ qcIL 3=\iowxv!`rx4J;@ي2OP ާG(GRGM>w v/62 N /GO#'plh~b:6{Yx^>Sgތ#ۑk}DɶO|몪QӠ=+!!^{! Ad&kv.gOgfNU"|(X/90!y6bH$պMȬLlD0L3x<$v2l:I!(u1cϟ>Cs'U8^?yv%ķCt+FkK-vH:0-@$ UH[LFoIIЩЏmɌ1aUV6mg+_gp¥'RfcFVT;T?>R&IyLbIyL x}bGXD_ Prl;eY.&umeTA+6|V 8׾ۖ t|A09hZ,l|Faf+hJHw1Ow^r+QmLEYiEtyb$ڜQWZUDuG9巢"4f-x2i$oQhWT?F`ouٵi+k8MPJ, j3pN-řVχs- bT9E 䁩]Sx[t̖NJ.i2P2Ոê!%.2}yT!i."3.~hq zBIn&ޤ'X,5 0Ix<"|7730,`6&EsSOI,zfi*L@[,2Ӓvo^ @bcIS?r㚂=w3ˏ0!Z.*P6JEBɔl$hR\p {=l~γp.} ӳ`V*ҢߺhA1f=JKeآt|AbPYd֕U'9'wt-%BbhbǰCXz(|5P.?@mű$a8kӇ#9XY+ݣ,,9 "^`$މ>qS_%FA~>|vO : X28 18AY7+s\" KY..?ertg Y|dhGm¢tDvauz#G.oҗ]z4?z

Ƃl1se!E`p{(IJ]OT@f(Xg o^>( EOb#{.Z׊ |7ᝆ@1k )z#Y BY‚xx Ս00"ap<Vdԙt^'8W( .ū*s|h$Ș(a'nyۻ,VWb=6pkƾ1,]8B;n8*FOjMfm!'6cImHp 頇Hy:<pjnf2nF[ tڌxqGemii,[q 'Qfl:;?80[u^ݧ[KTDEH=n[ cE"KT7"g%aXʗ4g*oB مp:\ ɡm0иɣSLmIhh&̹VHD<ܘ@5/ߏ?EG4o #cT {ϲim˗E1hj\ @N6%u{-:?ϐ%#C$1q{[ȧ[a'ˍAO\&Cu0ׄkMccc<,B3.5e&ؒw2|0Nk񠊟&2Yz{ΑXp={k·OnGVۯЃ g?]> T4F&4w*% O-1Ɖ5YFv-'=6+#eo O@gǎ-&FIM3"?©{UE2:M84DM,W-mfMf>) 3\ ?#fZRp>EL- hrU+Bޘv(6*Cf$cJX0P}pȩrICfhBq[ I<:%}>,tFD,"h H$K1ZěNK~b Cȉa ڮ9qŜi1*?-גĤ*qOƨ/^q%]DfPExLɛD!oY9;Hř̚f4\?T>H`flI@7vQ\'mRO^ʸ 4(ؖ@+fft 5^dvI*Pu%ɰ>K-u_X6,C~FPzLrdio05=>z\NbTe,WgQd**%6y,h&YްK[%l1(?Ou"$jWWO-O21,8PGrr}퇥^ ^ {t`mwZ_n'W8? &-@ٴAȲ.NJr1iO3Eƈ\XIEk&$Xy 7`t5 g(bSWI wl1kaaҍ}]p%w"~+ҹ5Mk 1#;Q(fMC=EmKrX^v8ٟ v~cA EZbbXNTiB1 `y|HYljگs;Řt37'`$ޫ.|膀}Nmpd3OmK{^~+{-o,LHM8ڋ,_g_HR=q/ȅɅhM3 $Dj;RJ{,RmP 8Og:E*榬X([-2Gv9F4osβo) ȓHѸ (kg2ȊL]\Ý_µ-Ջ!-B=5P,O&x,Dby5pro<nՇgWxKͯZhh&#X'K:=R;`ΐ7Ssj',MZ6Gɜˑli#S&"O)iBTBz{^Mj %`e,wa+ɜ"F wއ;+*dѫ T0'HMfP&EPXO58%5я&UZ~ύnk4jh*YLůCXDLQX ̝}.B#kL"Tו#>YiS~b N i7ݶލY c^Mk"\ii s"I[> g'TG 8q)'@46B74&?ļ)<^{ E𣯛 !K^6I68O0NӇ18;¸EgA%U摮0h"kRf 9̦W0j' bԬ38O!i>meQC<' ]dt:EjW3'&[bO:ı4[HgCWzR_iFआĆqBx!ҋK52><"+<p|}άL}X- fH}nX׆{pKnGVۯ Cbjxd};L>nYcd]ܥ3JN|ɠ ljLa:^,,eRn)\}炊M5wM{=m2_m5Sfb\7*ǿǠ;$HF:Z $) S(?aR01.Ynq {r" p1H^Uhۘvc|b8+3HƵ6=I rXrgy"2Sv02룠8v Ȉpk6 SRj^.󚡣߃`k|+h6٤XA#n cO=ЌQIv·]dh%F!2q<=wNE^rdIiŋY,!kFFfl 07?V.ϟ!*DE;mrYZG졀Z +~A` 6Q+F5&i7W2j# ?hzNI 9,W JB1U1 '%2Y&ƞQ򅱐IҷqԼf~q&` a^{BH9 ;žKebD^Odр<}jDTuoB]1#98kPl\z. <"\ `AW7p>x=1+k!vNʢ G딿8=ʳi ɆAAL7\,p,Er>X($ fS҄q^"јuO,/c2[G b?38"YHgocZrgs]\S}C':RZ[':N'/zgOa{CŞBO)7+^ʬ]׽n@X譈\.;W{R _|(MLe/@da6*˦!c)txĒ p5XzeE2!~jCL xMR1AuUs/L셵r~V" ZX|~%];O\C2[n}'d",9by>unۅ=S¿Cp>7 ]bL@y,Jx_CɤJ@ȩф]yshU<`/EF';>&qt4~VE#?`Q$ԌɇsuoJ:Os~K` 3xD2 YM,Wrge? 8Šc/#1@)2-8K{<6`R^5ؔHfe!hs9D-%^`ڏ=o$a:PZS!z0p|e_*蓇v1/ 9mہ#(F15(ߩM-d TM^`x^4.Qw 楑lfrEJnZO`r~K]xյ-KiD3q nFUe+ Vнo`uû$v< I- 䤱DjYVDR>E=⢌!X,@6I_G =.%S:n{}3|:5ؽT79=KeAqLs}+~%oC8x"r<O]©HBT?.G)0?uZK{xR,+Du-H@ig&X}}QA*I]y͔2+&=SBv aq-Wҥi>^r,@$-.ZBw*[^RqY0%y l܂}zn*|̃1nP"$L|֍6ou} .!(Ja7`'d*iD YoH թ"/F+/1@3NJA(nAD4CI8FmEfY:0U+{ KFɐ]"Hº $Nj5D3fD%&Pc?R 6Dp; LN=v-iI7]߰:~ a !|ǸQ>@M f$X\@XC5@wz&Y{~:EEzŒ#Pީk$8[`u}/?^:݃g:jAA跓PFW!F_G6Nw);>:͓*ܳ;u-35?5TГ}4S1j 5I!3T $7dD#GV{@:8'ع&d :'RNMͲ9u}b1AƓO?\g픲{߼# p8.edÒu $AdHٳYsDunBTu[Xa\w{k51AO\)oܽAn+E(%#baPB6?>u&M'9`:~DNgᎊZYUV@$~sE.*C <]pK."\/omn?ers\q3|}V~t߄SK?tn6uI292M#[gON*͆0f5`>M}Ԥ(y5!&dcF[s1 Rcf|g 9Uа{"5$zPB(oXPiw5J桲f^y_1 f\f6I&K+Mq)kM'ـr`!pΎ c&  {Ř`Y-ER-P>qx0q%ubѴr\^Gf#z;؃ݿݏ/=innEk2J~܀"p\enmch߯8pp@94ة) l>+lo'.a6ˊ=o'gȫ%yr/L?< Xl ްv׹ 0.fkza"C4&<7Uкׄ_K h clN'61b# o1xxg X34\0?_ʩn6m=ۯ@<>_MaqDN[NZ帑5so>|wNQ,mO΅0 g/h00cl8\F{2"i 2}!׺78"#~?LW?\;y{=ޅ~ {3fR׍W(&]0 {vOZ`,"y\+83 j[1 |p-eZ`b,.6 p?98gN1`LkԱ/^E&S:]δX3 J 4U,drgudHXx^w)0K3<\^'>t^C &b`UOn>7jXQ]Y]JD̛TNk4Ǿ3Rw/`3U]i$VǗoxXi %)5ADL_,hQEoM$ф^J@3uQX$}dIR[Z[I_#iS+ԖY=Li=7[؎4JѶ*?hiecl6]J͛\ROE#iU<cя^qӘ%.j tnLݨ~˘Yg$;XDEc۬f˚J1$`Tq OtL$P6@c6T0DexȮg2Vy({QA( |JK @/Ĭ0(X±#J" ]OL7X(U7+V' '߿o>_5)8%NEPZTPtҟ9wno~{ z18z6oבA5RKX|/dN4sf踼PBQ(I2! D'pW$`b5#[F!t\^79Y"Ǣk;6涋>ų"eLM VR9"ӥcٝXZ  jl7 u2^za|UyW7L;îdE)ēƜɫ;`!i* x±juyPVI7Ȫk-nt>PYї!X7^Lq3B q/yQ`?ӂz)LB V=y!JCTֱlsqPsGKc{g[;5x-ߟBjZJ6s&,W> зPI5Ojϡu/H".. LjqdOVhN.R ۻ r8NydkDٓtȒYhδHF0LﶍdwZg7;o÷߂޹bd3PDZ:=׷;[ۆ s\}o6;QJQprI汎ac/^LE)kH K[l+'kŎE S8{iwXRk*͔y^l84_KO[|*;SO=o~M\B˶fi'srL/*<?i y}^_]QU-?׶Rx1=0grxV6˘#DՄʂ ?g$^Ysr,cFIpx;T U$"i1/^8>䕵G٥gX"c9 :5b\ɕavW>[]]*]Zp{;hpnNm4c4fnOpnx',t^AL I@bY4yEޒԞA 1p`bj8*TPsKX*rKKQ%̑G3V=50#]ʕ,ՑAP,|6aZQ+|^\EJ7˫ 0Ü6\ -g6Yx+SfP%<"J=y΄\--ulE0B6BI *rBfU(F0s6EB6A@(-ֽmu%gL2fYJV0vC/! Au%*rLm@%ǣ]/K,v`tQIr5`@U9J*dn4uAK77S$a$)7}r2yގ8``a}`Pru;,Ȼ]~ 'Z]L4E/~>}v>5WŃ itWl2^Ѡa4c^WѵY"D|28β>~w:tޞntϞ*I߿ɦsU[BLMZTQ!Mj$LIΪ]kG ƶKa(T~Bs^|6W`}s ;-ρLz`DBBi.ڨ`ZYEoNL$ 5oE%G?qե ;mN\"T2(geH%QTmZSeM$B"XM'w9"Tm;t%eX T;MsB'yM7}_~ /]'Q5 EArxe,T\ y{KZWkXUޜc#sck:3T 㤝I@J!֫y5y)36d;;X[ Pd`3V\ud\\(bshJc42(ݠ]jXTiD[d2q@Y-[-:}vxS/v5Lvb<<~r>=I` o|םK؆?9{ o[oO9"v']V}v;!U1xy2B9Xq88v?44q:v <7y^TBC_%AgT1!5r+tp-|Z8 dD]U+Q 5P@2dkcGZNIQYD#xKbrftPRoK(o GZ:ڹ i[8HXm}ιʍ(T73hRlLJX"ܙ)|^r#o_5͂gdx6geIYMD&įY(*u[Rj,`yZ fm?B,9{̋e}c +K 56|ߝ a[]!vuD9;˛Ϸ7 8G7\<ϖVdvb[&mEElxmiM31r@DeJ65Ѽ ?.j]^ Ц"b'.OJ <94W^(甊x5Qc=\9z6u}!հ\k@@C٢uM _%f&]i.`025q\p۱p^ bIoi +~kǵw ѣmۿ{R\M_hUBX kH {+$ LBgm˛u&дRFA7S۩PT8梨E!,^)ٸqO%j8m#.tuǦ"$~TP lv-}mUhNzbota 0^\L&NxV\ABJ sdb·(j&ܰz?x?->P)]ģ^n؏ ǯ69w<8D(Ykx%,/8t&9pLY=HpT,"c2haq/5GB$J(m6/,m`/Pg=9(ݒp(MHՃܶ6xL(v=w`Ur~B7d@հz3VbS|TKY ˊ)4ϋdK¼Zr >ڈ"'7-)ⅦPDlʰ) X%K0}*)kl\ pVnͫW(,\ȀX,$K Gj3ٸ1y/G+=0m W毖b 9QX%'l@H/D2;k]y]c =[c<7,W10!|~WoiC& tHwꌙ$uW.M!T U#*V 6ߍ==܆wФJ?.p  #ȋF3LYecmܣWۑ(B9K` VtqOV0 J[,\{:h"5Jr2m;C\3`0G-%o`PqazWڱ*r3VmS>5.ٻ󩍡S%Qt7 q &p9^.7>~agw 6WjS:0LD)v& @m<Z9`)R $%x=sOW xO~`y}k%̣|pa0k5G`O]z%T Hu|mK_|\*MITMcr+=ڇY?^LOT!@gg#o|>G>}oSȿpx B[ X&}+-G63 <1D€+ʁhj)bMm{1XSj"u濠qԏF5g i$A]&4^>Xe1+@kVI]/k-&hi\Q6vR̃*5jFca pH튗kER~ ǔT\F v rz9ǟ_:|+᱓Hҧk7>◮7޽@sr_7Z O~0<G྽ʜl.fN(}{6b5P쭼/qE@b \ִ2qYsC).6'R{=VӼ3J|3'&uzO} T=H ٨If#Om\a5Uih˖>yP>Ηբ/)1:Q_[ 667z! I JRE`gR &ՊUmقB.ODP5 8u=6~W }mO.B,\3Ϫ.5//R gGRX\t 7`g!+ia癎PGXq}Ye k7x<~Yf<G;Q^f8e3cR]E%ިs#'Z\T(hW@WҟS3!%1 ֪h#j.+YY{B$DcEk)Xqf SS8s޺G7W33]>u kk@ICgϞ]z:l[O_(H vsz MuMx뮴mBmsyDr9 <K=YtHVg[#6\I'3B-ϯ+$cC46t w% =G߱':V[?@Qh*uT›iWq=a_<(o$()ܜ*>.[o4Q4C%\{ H]BFEadQ{_܅կw~NŇ`q 4 %X#7*o,~?*gBVݻXq4bv f+[m.fDr}(J\J%yY(׏zxKH~ SWSelr(k{p:zW^쫖,6G3m_,cױlQhcn )@:Sat]o%Z+[dž룐a`LBjne6V*+Ob4Z*etOW`ڴ=턉 vژnUjh7ai6hNok ꚺfH~C[/ΰ"5a`KžM9{jW%H- p5I4< ˋ=Lx* ^Ǖ `l0rv.jb&oK;P\Fǁb qJ@ 0~}Wl8!QSW|1O8RO_f?n.7Gݰ>, p]3 dFE%ZB'=R+?w}MW]3Qf9. :3[4jkh=+pB )$}FJtVRs&L#\UWX:  xLq=QTcMӧEbO&,+iXHa';=Q[& yR3|kKp6\9{O?p5?v/^+t ұU0F x%o"O;3"zض*A6Ry&IOEgg LGrVHBVMxQJqK|]`L&ct'\r8&d2/J6M" W*x\)"lݪ2ڴ5[Ex|^[p s. +:udwە2TKձ B{׃({ۖ4jgF8#W7%YWֶ8mZP܊xXEsmGWw3[v"5m&98\"nvFרIԬ|gy] >8"OtaR̚FՖo֗ .m<\>gML/%7I׆7vɢs`rouvP7y ۠*mDEBDHiv`>.9F'_ ̆#h= mw߾7Ky}M;/t ¿#z>Ok|뛦YN}zt]"bmk`WhL >h4lG~G<0iL5yI`HI_$cY񤵀cn^pFT=S_#U\47+ӵO|کg~mgf4 5Hm (a`g0!Ԫ$vyF)nX겊({p\^[߃^}@a; Uq<9X;άe<d}OYWc%d* ['Q7*Y0sN| m7Ke{H3("ξf }cjBnN7\? TRmƌs!L!L]B= 2e6WMC%R!HJJ+!+$$iS{8 1 VYD?0g87L TX{1X -ܲ E4J,/΋RaOOt- %bV [M!u)|nqY9itU[lRi3UFE1oVr bجCDcP }Rv0p!gAeL уl>6yf&uVi# ~ayK,HsH` ^~LBޙžM'L&Crzqg|l|sk!ny5W*D{AETֲ66G3 `U癴ɺ-mZ&#?`ՊJ]t"y(=^>}RBa/AQ8N(čW +H9qa0 BpRD*iT~S{YXe!PC ߙ`ejoV 0yC|BSbulᄁB Z;Ep}׬IGaf' 8hqnlk0t Hõ-$z(dc{>el Q,$TgI"դ=񵻻l/D')vn hC? g>?W6#y~?nY# 9Es9ss4"RQ A㫢_j m;pi//: . Q+ FDўr"0{Tϸxh \l)' 0Uבo#Tм( :%TQaNϠZMʳHf˓eg`{]nj諫ء1uz.L?~YWWPy.3g.W-B?'kQ7sp|>3x~]`).w;;kZïpڱMS?Ck Z(0"*H ]_]5];p%ZG8c/j ^׹(AǬ ܃Y!@0چ T8@jozqBrf TK.&Vo>2|z$'wf)jr"AŚuA t,=fd}n$[r>31 ȭ9F6Y=,ݔ((5^X4w\jՊVɰ,E{B{/y%<&3屇<:Qqtiquee9pW}ݶ i҆ا{! \WҀ^.ND|1Sg&Ď#sغmQUT54:726iNfE6>X̵i>4iEGe (N%% l@Z'w2W qqӣ| !DTA [خ*U-5SUVFjT0W0Ie66JؤrU( fe TC`Ҏ{˹NɜMs}2UAnA 'Hq᎛"P+cQ(?s&@ l` ]KQޏfˀVFA H&jdD@2w$|mԂFAUQ iXUUyRq5űso}ӟ?]8omԕɺW̡ikCPV\τ:)8A_vRh$8:h* T׽#P#D*#Ob[ګZ*]a%ݠ2$aX9X9u|!_榘"*Vl-Y~+;Sx6?7_pMta 5XFN 㾃^:Ky ppݰt'naNޅyog l-f?%xÞ*slb?euv:f$DȾqL9K02xQV80 FׁwhJ!P(Ȁr+PE@ڄe S֜^b_)srt~ mӽ^Oww&OXeΩECts_~>`=k- |wd%r+ <cI7\$C9rgd-C^MT!$*H!S'>㝁$e6w>T*rTֺ4MOϝ笯Gx!/= k+"<]&SեF4 8͋~c[w}!Xh+Uyo?yΟ ^}JNvV{ʔ(ԋJu.0a;dRR3JۻoMH̓s)oC&[xP;>2+QA^lRƉBfsog~w/33e%O*U(YZAd-|,]`yTnj5is|TTe OkR'e undIX9\DsQcG !G-5d]ޣ(̥ܵG'CfRwr4E`h棶cO)9WT[sJ14ʼ-Ge:)PHo=GF8_ĬR`暓f[`y,DfpWut IנkkX;uԝMӜMI*zwBDuhyL$ϔ~fGݦmۭi_mTu}0t]ZĴ[lՃdpĩXҡ1 : YV{jDsq\I#]U]?£K<@ hyS၁2_r 7 | ߢ\F pCYAtAF#8#oy*Àg9 >l`p&qO0 Id#}!~N7- 0GBH4q!V1`:n7 wǀn0T7T 9 wp{4I olJ ( L>/ܫx{eߎMç΅t ; 4Q=Lؘ{ݛ{ݣds+.B!ysDԐ6-Yc =Kb'W6x[!{Xt5KAsni#BC1k|n<[p/6@YQ0#Lf#7mҼj#QZbՐ D JZ0J%;Lxv8 ި<@sn.fEmԛ:t|yd@*Z[M'tݫ0bN&C;T _TAڐ> WqWA<}Fčg>/0-D1AޔbaH0ӏs" WQ z|.),6Y/`=@ﱺxl6:Ѫr#pbhǎ9jXj ˇ*ZU !;q(lʨ˶-Jp%U;=Oq׻Չ b'GA "5:h~G V]ƍ8#td8?D]$tg= |z}|o¦=Ͻu2ra\_լKEZ=$G-+ j/BjF؝r &@_i <ԌG]y;h݆a =̆&*p< |~$8^rnr*B;[ VQm>}fWgp.4i {80pYʱ:?m%gtuEB"`{z*l-i<'b㍇ĉX,?p?wk}v Ϟs?|Ia{`{'}O$δc}:'#b=Q ٜjG]׼JUY_v̨ hP !jE)#B%g"v%UYs ܾ5/Ī-َNJ${(um= 7+$#8*g)Lqr|C)b{ W7*w CհmW $Vwccf VKJl+ɩc9tB(g<0lfw+,^ixg6tvs z PTaVL#VkVֻJ?t>Ѽs[ +8t`pw _{jZK㣐 eBV6FHp׍ ռ\Ak/$IT:gZUT$n2xO}"eI[V PE -p ;$YDډ9߾w߱=?$T E9uɻ5Zֽ Y5wek #U҃e+JH&Vmi*xgIaV᱐5\j,A"$3F^LKl|} ^ehU~fϦ8_===A硇:8^N̓ TL"^;T 25\wͫKK74ѤiۛH4QNUCqFeT{joȩŢm! K;N R}2n xEEiӀh^tiOq3GL5̈Fe-ZX9[ V:6R񍤍wru +fB.W>*"/[~!-as^wp2#ZR o"^CW9y}jz|R%Fy^ll_įX 4Mv%7ǪQx˶By ІS)F^GƲrX啱rEljXS#+YHQ*d8\= qbPfsHDCك !dijBLt9F d?UJHP)WQ2~b0JC]_>h;wO7>?>| +QKE"7pCZmVl%fhF3Pb+=vCQH$'.cA,l1.(3p+ϸM~iԏ :"-]ӛ<}rϏfVP>m%kFIwBj4g68#֥-tp-.7p8Z78 K)j4Zw3n gm.£Ov;k&3j;65%bɜIT!QxY4ܓIAZ&k)Ѭ8%+^L\|u Ѩ uo/py8Al*'Y:4B"E BZՌ+:nbGT mJkg* d+^Y@:ޥ5MϢ$(r5 !@V~Ӳ]yc'M>L7b+DP%@Α"\(33xx蛗 !w .^HO^xX^4Nգtly=~w?xhCِ4Im g7: q*_϶;)sD8艢> Xژ-R% v }"Ŋ(M!GJ} ReBfHإQj}qlkSL͸Ơug4ĬL;xK럽o|MC>a@uyPF9E@ˏj.0Zڢ j*SHU5ȜUq{%qU(bLl;xoJwCr>V6X~=pûKT݄`6 -a]0B yάl/!ϊZyOqN=dm'4+uv-ZԈ4 nG]!9˵M6Hs,m53}3rϬRSTvRmHށ=>"P 8"-q#I\]f:$L4[;EVs0BE}ds:WD|]Q̳tڄJL^ +ɤx2d/ul+K)yvѨ\Q1 >~QwsFy(T>6{O2\Z*U #\Rʉ3hNClmVfDCJ Jh"y2T}*SBk〼eŶX1+ZG`%yTzuKbنiϕ0&\&87ʄ gxwl IuV! auLуpnc4DSݑ-2x4/iU7d .XqprU d%[ DL&|-s&L8U0ț Asy\vbO92gq5;A ,䒪S]XOٳM;_z]?{+ҟNP~S:? Y`ta5&%\ 8K.V 05,wG O˛=:?nkC q[U2xYryNDMScZ'֜g!v:t/Tbef^ f3mscƠs?3cK۰UM`e;عlM;8^~iY߰]Jxhv,-V=,-!ڢY9Zb/r HEz3_܅7;xM6s]sodw [g`_sx s킨eB+E%X-.f;F:^\ 0*R{5  >8<36:ɗJAT,cZ %׬~NRi]UVDXWҥ%~Cn:PuV8&J,ȖTG 3EdѮ/e GZrf]*Ѭ!|+ͳBxjVVe-+b8EAu湣eƳ2oɗzxex x-uZؘFta s]sn8rVM&&?} n6\X?'+Tt_څgڦ>[iܠDiqyrXjьf۫"8NɞPH \."N\Ñ)4Ⰲr _C!BNdK`*HJL<0_ u;+{f3UqxxZ U!` \Pxgx\ª3繎Y[,ǩ$[ceRtWVy`@s'W[8~ ƿkNe?{D۶g'Iu]"Xr(^TEwu 'r9]JXXAd( Q̔n ӢSss c%/@d TNuV3Iqdt'# >5Ђl0JsEQau^y=$2Ǭ qag S iaN31'<^._CRXsFXpG"n_YUuuX4צQϯBr Ȩ9e%("Β ᱢv \9H>J5[Eni3@NUM n ^ZI AȺ [3bB+q|YA@r:+=J[B\(B!C15۫2Ȇ=fXh`qCѝG[ˋ8Oѽ:\<6ZUTYBR@p?Yk[f͒W'`,RVRȉz20PXB|Pѣ6nZɕ*J[%NUܪ"eu7}n-gUEeОA%]9xPiD:o|:ǧ!t?ssZbn,2cEՓz>_5lgHa`G{Vب+`^jBLFv2P՟Q3 "S-!೾TVmJm:IR/[3( E /_7M`fUN:c,0Hv ~$*[:&PɃg%W+9Yy*rȅUWS^ z DE3yjta'fF'6aޙ"=UFK)ܧ Ρc˶mOړ_S^ǰxJs>oc: wVxCZI=nv0rTKp#}Yx-5t-4ɴϽ/Ni.jhnl&LX̓?O繲%L UY3" g56yMU `To{7]Yok > x-4U:v:RrD&хV f&]75#b ΚJ h#2D6>r%r v%;Mv%ڀT-(S/CX'Ų=ɬb'ϤepR7N^W߹D̪2;ATJQG]yc y>*EDd[[ODg4A7gi.!=g,ѺՐT9Nceq v=LLs3eĎs~f +w~[J5()ѣj>xi4mLu*{G6"aD0V}VVVNTmَFi m l)=s<@PS7imІ-B`xnXG<{W~]U.5?~x'GV2K1tg̞^u {("u0*0W5 {Jyc٦~I 'n;~0{R( rr!ê @N&jP{KN*0G(V}^8/_q!2EUnѳO0A6 Wϱ3x*+{*YWFo@_Fմh> W ǫ7`Z#gCJ%WևH0 ;m_:Ù@( c}f P ;C i2hbN21&*SsW'7JX gP ]7HMrrzdj9ge"r}@vqpʁ}9<؟05>0sǀ(:ҾԛQ<:h|M_ɛZh: WeP19C^ .`U.ˏbPtuĿb_gS7^i50G?݌A.α3z24!&]C`vYC Xi-y>4TB̓pi8XlFx=s^JW{mAeb`k 𶄑>`:7J`ӯpt[=ڄ׫ҹZKƎ[!~Kavk:QBۋU0vicY9!%ԶbU6QSΩ\Ea5V۔QB@U y0{7[u<>x=/!Q"[@x rf^D CY[b xęl&㿷v;X\hd| 3r4#,JB$Uf}7UuUV(gk1O#ِb$[ŒY5Vc _JǾxi/nc߾'OnԞam10¥m/;x6_{n? ܹ|` uR!6ZG;9 6shE:73@7:r;{_> ˷Gx.yíZ vvbDUb  pmDłd2D0A4(RDRGq{juډZ&qی2G-*.Kd 'ҧb Spi5_}k')k3iZϱ}KfL'ԪD5sdrTֶ>/#i+cw~ۦ6>VQIAjf"j0XL55(k>N}]?ybFKĄ+y~|]pej a4q !2|ɘ+伬apMxxO(HmcW.'dThO7/lS8~]w^)ȪP &%[ʎ#/QCg2^ΤD΄A=Nf湭ʖb=(j~m8a&.^1lxb7ҝ:cnةMǺkK/+Ů*B7SK6X/jV^! J|aɰ-m?0P~%( }xn-88SȒ4xMoUjyՐ>Zժ* c롪}%*uџCFgg_$ڇuޟIh.n8H/z;JI2%`DDcEbElVNȦIx/뵍>Ud121yBH (EH ??ì4(s[ǟ ܱ6ADžy^H򩊨'^LހKux х %(템sl_ϫ\{jɝDbnIP//7'^~}HGgyUm^ң6uEmCdCz:NO>p充Cx4MIOMwӠ4ṄKTBSk#g`7ZP;bJ]&zY<%Xz;8?s=ڵ'8<wL0<vmsV{x01Pr*!Q)?X 70iOÁz ́g<(C$m6p&27Zf}Sv='֟9rzdh@k$¡^;;>T0ںm3D eZUs膶R9<6c8q3xn9*',qb);o[ӄ^B/eR!Mu98qκ-0b 0'{ gøhkaŁdh+& 9k2fA3}%ν)7n`h孹3AFgtRW%TF.qkN5 kj3~~g|8HH7%+WGC:3[i2PRXR !qfK9DO*1Y!i"+%fcFF)+Ξ"Y,K(U%J5K6hzd-XP!t=v] WLz;(yA96@PpR%dLXf[ s},/Xma,aY^2%5CV 2I@M}^jK'ƭ*Ȋҗgڀ%03 eRnbH$麐 U?1W %%yD-óY78'V2a%}KVhr3A`|m&O{#,KO %~}4 ~ZlW/;8b䊬1ڹdMhА"s' U>Hf/c. mGz *[oIWv(spAbUb&(3)&j*@Y(cQWyĕUJV̭,u~p~cS8xn7h?\{yv]`*H޴bBVm`VB[1J`G pFRUKcn#*sD^MdҐT1C?bwoxY.+oY+Tzq .惠'VhN=+lGK*=:+0YCUSu+r$gsWc`D2)L0XQicbw o`#W9ñl mdK"U<$U'7-4nL+4 Kvc^\)GgM"Z.Ȥ̠x=ml7{=:(Rf4t[6c~Br Յ!?!و$5@*\$׷RTlT}'=\gV pJuNxu=7 +s&Z5!Љ@DTU'D¤W;l{ o~.xKp?z8p   u½/G''%*YōȤ񴬧 9b*sL*ÖM;%OC9=خl?q>H9vݞ3,/i7{ᅧ{H1kU@k.X6h`Ҹga/j!$*HE0w\?;ARseYe>ce x+ŏb1li|Mx@.qEmS_N{Ϧ6O2@"^3y%Y$brE&f,1g&SB)6%j[y-U'WOƤug =m3T5Yb5mW.R4X%|2s_MG |A~$[AHDqU$KfVrf1ꩀstc3~VtcLq?Kx@7q+M+NJL(8RdkWQI{Kdj(ZX7&@%`Kvy(Bd2'ҭTrEIk=i>] z5FYQcVJqBm܌ 8+ Uu;7@n`ل2ƍTaָYl\ʈb*Ǩj@yj8 k9bvUc"4yHEoF_yיS5mè`Ca3b0>^>gZIS>[^C9s3,J pH9uK&R=޲ݛM|pQ-Jv؄{ U9*& qtU9nsz= B I)Jkط0,.eKbٽECIP| _dԮFBO.QJ 8xm$=G2?S aװ:W7F|qDD2Hc-s 7}5ysx~=Yשo8+P'^&Ah@rcA!V*Ʌh28On](fX_5s[(Z*}VQ#P GXlXUZ}c֊50 G=}IN-Kز< ;mSOmă\omo+/i85pUn7hfȚ.Z4a|ӒN4eH ι<:MKL. ~LaB ?؇|]R -QEl|_DGK6RZKweFGI}R ^8Aȡ#[Q%}#`5 V)޹~۹yO龉kd#j}Mi>5L 0 k#T߄yOa܎`1\:XtΠx0RTdg- sjFU&\ /R"!d@+$8}YhHHZ). u|%+Ȧ%[e)G+>EY^< s5:|)+uΜp^7}j51/ƌ.UXis%չ K UWu÷2yOm سw¾m_T\b{Fb -1Y!)/Ռ|>>)xFԥ+cV`T+LX僟Iފ2eU}\Geug}:fqm+>قYxHV0T%T S}02h`b(O61Db`o~meŌYxS&Kc!ɹ4NgfDҳDD*3ق1F5PuR8>nL?Ji+x .6pu+07 MI@dkIdGitPւsT$յ&|F&"bJYics%exq~3{yXMg7L\ )pn]ngNw[w_?pI|ϻ{ׅw}:ڶ#q#`}p~~[=m戏UbUÖmhƋ^iJǤ).u$\C%Lhg0 ]g>1}t;P繒ک(IԦPX8tQ-)h.0 < FsBDœAi7\0o?%FJ`l&q!¨ɔMlij-K2C0'}^aegJUjc;zݔ҈ڼ)a*^l4@;lvlao{fgB^ޖck"\t>= jOx>kI W(* ,!J\WkÌҤPPd;3ڀW|FQ\{Q7Ԙ}T]akfCe)*J)?^u존3꜌:xu݌jRe;veyf̐uqRN'Nm}ؕƳos.&$T&n2z=F)ÄyƄl#P("vQD}`Z ij dpS:sZݫɞRaUq. 2d?x~yjv]Usڊ⁕z)k-^㰱\AX_|0my6mi /m2)=D) ),Qj[.94k7 *p ؙwCŰ;eyB&YQN gn:O_Y:j)8\q9|F+>~ɧqEU4Vyv,-4RTJ`Q$O'wt1B*Ιhm; JS̪".*\+գӋTBRڐ]NYD!E]*dfUޒyM6&#\D~_?xhwR>$;`A?e+'D`GKDL b玑2Ol5_Ñml3?ȚM)=W̑XH@Lz+! ##+#Ǐz;$6-p->g޴^m;`ZxI}Hܫ<9gW"߸X+*7mXwn|;%7{>r 3~[a~|}3zN,e.j>ntZ&<+N-[K¤uqkF=uCzH,Ιhxn]_2x}4 96>D[ -4e rl_ף_gumYQG$֦Hj6K4r_ư}bO=<۶_ֻN{77K_uۃ>C- um a kn&Ğm֧*`jP\TPQm`J*roH<jwo{|+aFJTwc :M__KfZr8J+5z>X p÷3PAS {TU\T`ySU8Q$*S* )Z %BEhkջx :JIgA\^Pt@cn yx>S\K9+ L:sb=Ie䘌E;UY8_xSxg8Iªb '2#A"sdHNo1g/&\ f Rk2&^X#J.(5e K?96m@ό R**_څ<P 3MDrI1I Q &b{A),eȡ:׋ Vc.$h_vU]cnO*6j6/r&{ +=і"piK*VpsBkF1"R䉗+-G#+{[́^0^*#W"  9ְ$`"k"$e@-ߺV9@R4#HfyeI0IC*jXݣes!"Ƨh7'zf}R5:GK*Yە X\k[G|>ٷejط$,8%@\ WU#E@e $/Xpf&>X5XSW߱fŚطUIV_2S(jǕϏv\&@qXpp(q~~suy]kH09lIJ!rSD5VcE\?]3^ v[Fb+ق(w(<݇Oy ^=s\.V{rk9]rV">"*Z)y`:o'tl.Ti{qDBVAi38E&[( , e A Jw6\I#=FYEұD0׈"eE`"7;~&֊*vKl !VQNjae}DRƾF.fr4'xhW<_tA2r1|`vt"1 fmE&(bxc7{6d'syTsƘ<+Q[7YŊs?Yx*+őcGybgZC4~F"q.aT*K_H1?O7_\m)[D[?&?q."*9t⑰*NYRFʹ;8a؊#5]E"T(HS`ᑣ[aǎ{_uj5?vO֎ˮ7 KPKD?}Co~?{T4Dτ"ȔFS&g)M"Eݬ2*|K|M**!_\+rW"NxwPڼu.ROP Lu9!)IM&#5j7 FeXm#6 =7hl@;n疷 aBrSBMQiŴ2#!pE2rm YxeWVRLi6ndCi7m"=E$׳m[V灈GY,McrGcsXο_=7O7~ݚݴ.KxڜO=`U;hO4D$3K&Ce{E5攈^mn!1\s'ah$q`ܴg36S>7S?%c9|8cz o®mسЃ 1; ъ-=ڦM*TL6hcGbb2V:gQ1Y*اu3>9_~uR)<)DŽ3AZ&^l{LAso<3KvJs X26{;'slsi* )Ϧl "[b*6pd)J R=Y^j9+Mh1gHû},b" [lM$٤G74_>$ϖ-}0}Ko|*rF]$Oe| )xbOkH֠'qB!݋dB%xJkgT QIN|k>`Q?sVmз/V6FX5?Wqf}DFV{̻&kz^b8ǁ+ ??3On= PlMm- /LdpLl:+}pQUePm9Ql1_ 5>Ok#*.d>tOaYC=qTYنl5 Ha vˏ6Q}y61WHcBR=]XFR9],܏M^mٲ/g龃{8GRйa[7ɖޓrםws'߈ ?H:Og+QΪ ;!Wfd#}xTI]*+K8E|<dҵwT }M0IMr,ʐ_^SU9(TQ)ulcRtWa=MhCwRym-:Ok%$fˌ'@q?\[Ltrac]wQ7$Y6}|%ETH ?޷:R{zyS-b)J.;Ę,qxY</*^+گO؇|VxVyH,`ϊeս, $B+@qpriFPP|$=xZv>8е5D1Z*'*t}*lf ;2w>ۇ)m:{5y(κ T3cZ"}8l|&@:Z|cdn2͞Kx?oQY[Z)JAu*hI%d)N U9o `^VFQ0c')@\&ªUirl^m@ A8M$jx#,ע5e$Ź@Zq4x-B7\s+> .7b 8(fC,A\Mdb~(rɊW'Ԥ:c%VfH>n~ ɹ<נ>YC׃LV ]}?b=vmjU5d_34K>/|I FS(P!2G7FQvNۊg%QײTYczv$':,vz=99ڡX`Q_=qo8.l煫?\<~W_= 4QXcR?hQ'@4jHTQPYgdzQrbqRWICxW(:"U=,j'$xXʵ¤o s#) 9>} <`TÞ<-[a;vԝчx ~FiˊT9X1y+μ!+VYh^4t\87FMg]␁D/Ԝ0DHJQtJ Ω&?He53{r4 B{QmJ(W*k ӓ קlR!E9ik,j\TEvưGyRy%d#܂dD\i51K)p={6oUF*VgGEڡqY7ꚢH RΑ$ŕ,$3fBTO^|ԃxlϜk{_1~NX^AAdzaMGUP)gD—*D$8R,1hϖ#a8QMw?|Ea C;c9T$'COxZjY-#>'m$r" IrN̨sDLMx d潊)(Yiuu#'KcVPœSxw|TsyU"/Ŏ (X^lLo0QI8CF|KZ͊Mvcw늟Oji"ʀ%;T) DP88oBr s<œc{e 0<1IaA]$XT!ڼF?L*Vo9TD*KQ1gBzdӏkkNJ|}DA0 jE=1=[{~\u?yGw^_:}[;w uV*[G :/Fқk`6ėtId:pZb SyeŸ/eل]%j8;M<(]#O=wl(ߧMDtj l /QMmInYtoZHH$}syI[i'\UfN*]L@R&/+ٛAiTS""f6֞GS[SX)U<蠪NoiP=غ5V:jj򜳥ã"]RH*\-U ` 4G9=rJ< x4Ut|w~jYE "oRPUfE 62vUHq=VW7)YPlJcŪL e'{asElVsz,>A@0Zam=xBV!*mX Rϩ>hAY(,N76TI cfuvVgGhG"A[o/%Dʘ! b& {|Ώ7D4VϜ`ƕjk7wPkzQ|XK= ;6[T,v4EL 0dƱ@a˪ !dW*ۼd[ſKƤCS=ĵZMb%,1 sT42SqCO]a [Wز#h= Lyˎ]TR4$IH@cNQq-mD@Tx七N=x縉o_z}z}zW[߼K|Uk@n9rdo6&ɸ +} 6Gg|+EnFKkKQ@if%piW$PFW)] fzuViiQwi\uk؄9BXu.Qj3MYK6UMz]N8q׼!xm߸p˕C \{ׯ١L1JXm0A5*~b. oĨ|GA{x͋-/\ ?}@fޢV򠐔xp2\يJpk Sdk"&Qm%VtNB&A(l!G}dX'"EC u4شޤy_*OMF M|3G :OCj_F"AÛ"V)U{Tbctl.Q"H$p{eiO Ά?yFe`$~: O9?ov 'k:'kIdd5iG*k)2. {pbV|$Sa62XK%oՋ?_: g֑ `< rnrq}IvT4s fGՆ@lE zaRB$Iy^`vo13]A fwSk"@ >z7\IczW]6cQEAZǻ}꣇񫏟<ȍ7| p߾;܊C"2 {"?=eDUsx 47o]Dm]\ND`S ؾTm" zy7RպD0ǿ 1eˊ[^a"a%a"Y#VPy5 /bFCr50׏-&o $M'BhqE`-+BOV yd:2\b}ѿT\ᡧ6+zu|1ŧOGp>{<|Qy"rMa>2WݼW4sJ;1fQg[۬9 }QTHLPEoNBi䦁txs- dG${0X˚Vgh{yڶ=ݢ_t~fZyxCG+; sg`8_|F ݈kWY 13#ɃvnøUtj/m^9Mf8G̘U϶*k;EhSmgc+)Grސ&d[SMxS3/}\KW,hM(@ĢS ٢Wf30#ȁ *ț g.Eig潓J˧l[v+dOV[ro gEbݼz`]KaIq3@(|fdhߌ$*"[`'EJkw=9A[/ZO/\ œR=YIy߉%wb#0kxѮ6]9&P]J* +(J$u1/ 8(L(T8/ҰT*T@{FQց*?fS'5~")FH~}̔w7#q8 n;!~dǎn>D#nBvHD(l3T_h5J;:221B̬bmzM6V eMF"mWyԘ i^.Ͳ\ 4@sSm!ͷ_S8>zjqH_ݷrQEmʦ\M]ahxZ8oaC|7#[4l$KJU[]:nF3Vutx6%87!z%eMlY]6MTU~""l,j 9ǑA qeZS^H,jnHc"_e՜I~&* [-pnu <:N{n^[@wu<myǎL`,/C8WH m#;tNM4WE" g_yr .ڃ{6oL5&5TˤhLdB5eC)XL*D)4D~8&oujo11mAT97Nd-UĽkG7OC1[ǾXC=lh{j!!΍iU$X]wTի}.+hKLt%_QyK;RUT䕪(48桃yY}DM7ehLJio}+D \DTq/~0NF5[X/ gMٿ++/U]Z2ac˶3㰘e[8 #Y2?MІY]=峟Z;^/5Zjal5`;<]2h %y:A=]D[1' gQd+4B,$̞HtRjO lL'TُV4ѪHeTP32CyҥeɗFBM[E8&u \,lMi6erjKChs.aFZƈr.TVoV "V6#ЊlB~.AU;rU~'F=KfhIUqHvT(|,, /;IYgʢ g_g IU+/uH J%>|'Hn=QXc<לZ"K+aeϥLİ<\XxJ_(JXQԄtƅ*%|Iì, 3cZETx{.'w[b>)};/AјYMN^9#& ZF bmqtLF 2h3%dWs}!bl3\1dͲm Z)ֽV-jGM9"R1(Mce(i"U'5( (U`WmdpḏG,h"eswNPE _Zu@%yo: UkXO&c/U ^9?-TUh8 ť!`GVDlz& ;zg~5<\,vm}byۦk V4 h'(:Q1#)6^ڈW xB$R>(yV.ȵ\8Gj?3{Zoe;V8Jvs ,gk،1p- .}v9f a/¯nxKvJRR p%B9,[HBE5L[K`8EKHe[<&&AI{pU0x>nIv\OIQjh$FcqYM,Ia<.TsXq.ve:'*ٵ%.@&=ÒDOubm&eOT؀@\Iְ8p:` ;gx5 4oҥCQYPSޏLI9C-缝&,ɭm_@uebk_̅nk]Tl GW[W?~\wy'A- YCYeI!)~*I{hDO;CI O$('e+L/$g::VkM̧}ioWOPz0 'zW\?y) 9|}K5у]{S#8v)t",)KKh }Xڷ/֑ЪmYv?`B"ISȿHxxRog Aնg\ؤOX\Є/</voaa msO7t΅ c9 vr>2%] {#2U']뽟56>2 [ՋS\l.,qaN>[.^_{fK_g5ϧ?s=$vz0k'l7Xӄ$4<0ՕW^l/L¢e[(ΐh*dݶWqUwTAxVu>v䳗O~inmu9o?ymTj kF)_Ei |JT\^AԂ3U+"J<:?sA `3' lBM"<Ye;%VD1;X]o QyusʬKѡ hw?O?+S8VW3 ͭm&פ{H:l[V,G&<T1BRQ΀bi ^b EI W^Qx(m9׆s+*%[t-LG9Wg,LztNү{!JxV5RHd3'g&p,۵QE=}DT (CW)cu 6ICռ5{YISY]:+m_: ;WޗyÝU7ll挗2 2B6ЄUʟJ%MDg 6aOO6D~b^ [ɀOrBi y)z9h6_Ÿ<6+v/HO묱lchӜ98S "{5ϩ*뉊oL-jO׽Apt ~?];-Wɶ}1!ene'ti?&Sat6#MjQv&璁^rILcpsiXpn9عp >4 Ify.VH&,g^GlϪ#5[JNQms^:g ^MVŮm'1ʚ)z'k5kg'L/Dpꔃ% \Q0ِС zl&AB$c?]v!E}!HR"3>ܶ9|>D81<9w=v!KioH=UF-d袕]Tb'# ;sy"3%˷ֳΨk%*R,k)ө=f>8# {Z?eFH6u+|n[]_7[=U{ |s B'+J}/yH_\tֲs%9R|y̳E^9y\^8%THeͣ<ӆ-eO22Jkd.3~>lesvᦫ7.nX%gZ8׷C7,:KC|C-k2/2; +(lbVd0c}g~eXNN1!;]K@ qGZ^r6x:b:@-pfMRzŠp[^oɓ^8_u#P++wfMmMJ~*;BP&=.^RϒX""^s\l"z$'M!Lѷᗗcp,G6`%K}H1!D=%9i̬rb(oIZlSž]N6;IQƩfA{;YYVf'ML]7*s25ʵ&[2#2:!fTA0`%;74m&/K g`rPaO\u<6˿ӻn5!W#ݏ<L5&6 :7PG5 AbG˞d⬝HԜFmce jRf \)*)_yn9 (XZU#lɅU} vؗa~aظ38o__{ ͎Jf-wUoh;$Hpo p-iiHԶ9p9]-p(=WP ud#C<9bjϭ2XhbK[jl+vWUeSzeSCtب%gT SG6`c щzvWBkWڢhUQʜYdm.$ْ QEuSE(P-wཚx*

qdK']A=uO yCg8׃==yxk#?ڎF-k±0OcrmZK!16gk<6cb Dl)ETj"#yUBhO[,-;UVeI 2qhnZjKspx}XX1spaTQ{> '6&qp@mw;K,0!\$db\.r œ|:L;O F-[bf6}e>'[ 9C8C?T|k_0Ft:EQܑ<8;|DO։P .j]-VNتz%nFӄi#{h.py|qsn?\mmjf9 zK5mUyV2irFɮG,xwK FgL X*| "\˗]BkfmY +3<)'f* DzfЕ ]LP>"{L&TMk0EOoUPN5CD߃W*ѡC:kNR,N-Ϗ`gR΀+ZV@HP0I<{2,ByQ { PdQDf"@a*8QV=i@<05>x\l 'Tm>MWX@ظK vr:=tP6oRc:{&l&++WmovR@f+82<<Onkcu ¬5[AI^@p@ *Y5PBz Kcd*ulӢDQ/: x!UUwtg|JEDcNSe0n؊KBo"R87'5r^H{>MC]տصT CǢpUY- zճ 6Za絵Qqz-(ڼTʰ\b'[uRcgV!\fwpL]wy7r$Eh7(r>e.P% 5SMEBRh&"\GRo,ǔ #Q%dVк1'O!3cF"9!/w,nZ$ 8Gd͏mb&60Um* ZAuHUsCYm,OT q}22.W~ CN~pz$~×,5@`)x+V{3ܖi].+s2 QnOVTɖ3naE*}$:Nm8U-D[ɚA@`A)1&:| ^ 㾝KGP\9VCD rDeeE$2Dn'PV)'a۲$8^;e0|b9pObd;Է VX{D*._8wx.| ^tIu s6F01$~IQOߎ=cڞkZ"*}3LD+k; ˟J{NVi^}in1f`ZKeuT[]||S!wS)MLVɮbBO܂;y׭9KIPK‚YT^\qʷȫ ]3EY*gkͩiw?7q?6j0*19?pj Wph BeH~nx6ygKXG1\0muAN$㾀su9'\xN)}[컝g_Yi٦AY=.5.?dTP$Ey~ @#y9b3RGmA;;^,<F'=,.hx#S8xt՘KYPx2>SWq4z.0CSXdIjӗO0/hOFFU.C3=j>z=dw~ Up S}'Oofs<o&t u9ed7"ݺ0ݿ.hsaa0{尨܊{O"rMV g~o=w;C;H p!,* SB}c!gtsz|tc2VMoKX]w%5y,ɖwT"=1*xٻw%u9zg8cS @hE,iD9V)O@2 T9̝{.ԻSOdTj];5ӭ*}TŏU_i(m/zCSIWe].yM|FŸ<ށ6 Ӳop^CO~'ŽN[ {x\z:U{CBεȋwQh08ggn[ktM1;:`^*6兰dRYe=g9/| ( xV$zLT_E!+gbxZdU#f4:JPIwM)K:3jepomQþFrw9::=׷bE>#Y QIm~QVojuzEд,{Vy@Q)PD0ų"L%Ud́S0@OFPlpp0s2iY@CCQc{aMx"G΂^}2cd@д3rLǾ 1" qB"+% DU%JTmiL!h>gIL nO_<?|r xa"jZ=lR ԋsQјڶ)>|7OcinYqkXŗl86wYY&hx^V2͓VƳ-g%YBim)JEb;eK8{V!e>+ϫY G,x I7j\JO&r1\r 4"/TKm{s< _oU s=Zy8yWZcЛw126L2rAUWE-b˙PY"J8mYʡTL$,Û5 Qhm[zŵ60sw8s';9=u^o .xpnn N0 y; C$|m8̇Oω 3Rq}_y?_ \ tp0̀oIѣfen[1`:Vf>q*$?:zJǐVv KUJBouF_K !e}*PH`"z6=s<[IeV;"ո('[v}t嗏g>~>?c?~8L&05%?\}kV8᭭=??SaA6 ? 3)3b/~ s =3lzqm_s["tloR)`YЎՐU>hkdeB+@W,ْ h;.%ȶ*Pf7v ]:(J0,/7NUd,}<_=3*j5`9{+xdt1/6_~N@]|_T %RXz>Pi#&T*ǥqV@(l%1 x#иsp˧a2Vh|a݇OSln<հ1` v]8 U{z9)L`Cy"@kĖkpHЊ Tz![GyV)*c䬟L<8J,3PD/sf šMd)mWªMbiQ>1k:6 Zq `z(k jޘ Ict@y7T9N:\):g8WuB_rT;2P C&jm$\-XqwM-Mƍx|l6uPg2!#}TI$N$9wT,#"EGt퐓JQZ`iR50+d'/#dZTZ:)˰:X_}1.z_^X>W3Y Wj1]tg7{mscL4,0lPUʶz] +n̲%Ɣ-:ܟ6 1z.*䬾akF%"Qٺ-@^&[B&Fq$XMӦ )7+Gÿ [|gk(Sd)%,2hUsoLS&kڢR* 02iTyS6[Ӷ9K6 O4P [‰C`cixx 7̻n9?QT֓R։T3|w}l1ϱB W1iR!6p 5*l/c3>c#05YN㜎@5)vʆiC-0g$vVox+QӈC^M,o3Xi-XݘwI~Hm~ΊJa/6DUAmW<|XH`--xS1Thxnbe;ʸQɃ7Iiph0{ 7pZ~v mFQЋ -jB""mmVlfm^y J˶4Ƴe1񜍓yx*YVg~ {7SxxuS+33Dgy o tڄOH.πǯmu$x*O1msi膵swG[L^;c)}guo TL%YeOQ]l%J|a *S?MN Zmdr/UrC++fƘ^d,]z\k`PL t1WĎ[Q5@D%^h8TE)=ti(y̹ d%tCf|8_VGR^,* gn0YKuQkO?(Χ(JrDd=3~$NT@jh c9{\* M¶U}S~ϜZ6Tx(Tr'Ⲅ /6դƟUe|q,2zP* 3>-z@Uhrt _BLCR8HKI[%rLDU3D,YLD0Aq+ϛwEL*sEr ;(*/8x}4Y%&E.M hN|JU"kLDuU~h&QPFKjodgINak*BrpUaԛ ԏ [-\~ ӟ[$rvE*vWe%B)ҦU\gpw7 !T4M6{"k5FN6\ʭ>ai\^_XKBQp`,c kدgiZG]߉%LPznR$|/ -M+_!Y+`ɶKAg"Ub! D |"W]K:6> |PN]7YR 䓨y@n#C*8?K낔.ga^qA t!Xۻ7\5+ksQ 1ɞF:Tln>>TS$r)Tv8.8|<2*F"*aZ*(hdH9>No]*ʑ%iDDHzrd锕X2=ͭ.)h{;\q-.c-?9w3*Qy,o d1,lQ~B"yjM*H1RLE ^~94'{{O8ػR`Ƿa-<_ KpY-f1&`QC-4ׇk/ }nGG6[wm</m .>w./" q <#6.G9W6d- 9Z ִ*$Fdoùΰ(3:b\"%&q$H[8yj">=IG* O*Uߘv*Uxu[?w~~8GC{!\sl%E"\ĶѤu.޷:C6R=y'Ũ|m&:e a`בY!Q}߾mզ~g3gF~?p?tM)'IdD)jzmDonھ0S u]χ5[aY>C$f>@w=pg߶a$yQJȓR, :<т:0L^,Pg5mF<98~뵯A< J=UviFgU5(fP`j҂+5Psc1[)$ySŪ7]CSg#eDۇ^nѨxfVp{Q%+7Q@l$+wJq+`hz8!ZJ`v8 Rjxr!%me& kް;`5, {}6ap\`Ye!/eٲqs٫M 1gpHp^XeRe;|>Iq|E !R$CT(-n W:VslD@eU;OE&))>l瞼.?6υ<+sñͷGcE;0S y-]*O3)#ZX뾝 UT]-)*SwXLΏH(TUr{lyV U)T+>) (<T.4T@,~]φwf`{]d>$ZtE&\6缊"4ZwLPUJmPϙ2rwd㝲1uYzOU*mӟ/^S೏W\{}=?qxw>ϾfR\/ RM&Ū=SmI-tuCQ^@F-g#S9J@ʜAȪg 3C0 T>*SHp&cb C Sפ@P8rp(N+/KxԴlX-Ykp-)xդZ-,FgJ.%ɇŨm ΂C٫K>|o=3D+jz<)}a<7a"yjO^K?üưٿu]~]8ICmss3 }sXUVOΝ>ȯygd͐U!d& '2AgYs3p@[%e%MdS d_gp'~1ٹr=2m,.o Vgt.QQkݼQP*)HDMas!2Bh7&ETu47.ywޛO?U5oMaHV~7ߤyAY%U 0U5yIyLfJ!'@W';tpG칚U:X&6hH3J؀RҀKQ'Xn+UOzݜ6Ɣ߱tPnr3Z(/E?cYgY+{8! iVޘ2ThHM4+ʿK%VɹUJ*SK%#ut^O9DLU!]i:-MnA: v8M:~12Bd4ڬ*,tB̙Hh}=Λ6=ۢ9 ӦIg% oK&W״f$HM#6hw.!hZ(I kde+ZPBBVhx Y@DbhpIBU3 T6>wU>8ynsļm>+=S {H& OJ$])Gut!tc Sv%&=B#N&xrx6$2.Sy;bq *"h6~[eobzTG9lpT6Pu*; MVj[>daɝA\լY]Sge"6Nxp˹ UMu{I3G9'¹lV,Ӱ @H)b Gy֠71dcj{oK>H /j['kqڌZ] q)'Ȩ 6d_m{1#]؅|i[URc ^Hpڨ!gR1 !cd$e^vُD9ŬHMde؇c|;پv&9^5p"_078_| vlA3>-˛fa͹l4niJs)1lU7$ .L`gJ1(_6n]Vpq`ye C_:ܱ/>_i}}ބC<5<_V Qh"ӸTrnqIE2DUiɹ)yp]ZdW8? UndXqf%L٫ ?lavk,|ogo9c[.:l D0$]($ 5_8ڈ,G-Y ċ@(81ki?ԩ3)bXUQq]ߘڮ~^&G3niۚeJ8ּeexm_݀]փ_-g[YȮ%>XKcX yQ;uϭ8K[5eEض¾BԀR G78!hN@"=~c?w9qy>|>6'a'>OȮ zFtư(\9{:duuuﮕ' 9-ٌ~h65w# {͟\_s~pi%Rlѵ1(Q* egUnx'A^;&-yɞ9VѲˌLA] f@" Sy5) Uu 6 vI^R I=[ EL>q{ay%UVnBOe N@"z?dhn`a4[ \#M)ƳfJkx}L̵αԥrs[/>QNY 丱=jҸ6ľq9{-x`"96OeoTMc5^E%heO]NJ2JqBjYPKmt=tCbv\snTggpwbU:2y'E'`դc/Zk[]=O`}\=D'c̄ Hw9Cг},a>^eK61"v*O=ۅk l/ eT ZYD%WpE {ފcNE㘍Ǝ&)/]-~ s ѣ?x^~Kd2pÄNmcV1GDk~}/ou}qxp|^Z?M:c̔Bf;A#9(R&}58Tsk!S4PYӒ,]_&̚g;86EX2"RlxQ4n2~ y4n^Q"#7^U4a[]o&'p5iٙH%Y jY>\¾UЍdP׉T^?s0NTs 7ֵaҹ(5Seeċ:hU?R 5r-3aeƘK8crabiu/Ih~᱋`ta?6Kty.GVxiJר6Ǫo܉la{3_ؿ"[Y٤I,մ*IAZ]QgJhs]N>AS% Y[ 4HFYȕڍ%yޘfAԺIk!P_18d849Q|&LB=P$.g_x8vrt\8rpsp)_]a<-helvWq̏8#S5 hg1 JfSpp= ?2\uD`"5zpYsĔw((祩Ky.1CXŀWOlx0qilwy,)5ۉE 5lzs5`7K4vj zyb58|8xSk6MeSI)#aZGPKF?>)%_gkMC3nҘisK*࡬WLoLܗšgNo4_xoз#8`I> W+\E1 m[^^|&1?4u{L᱓k~m|{ 7ڨDg F8f)ɧkoTH$D%)sYӇvI&LF}T.pNcJ$ڼ*pIu>23Drz\1s<4TFbNY[U0y<kg f:f1.EFgW_ )TmsR8[F{X+p,. "H5oZݻ Gÿʇ`m}'1Y1U"/ϑ'XVMPQy:bXJ pLllGC,Htko[Z3N"/܋/xldwomW*o>|(JNa df(,-̞~p~ۙ?>Oݻu7M3SUщk2P1Rȋvng}[1Y]]}UϨ _ adM'I+ʜH(תCc:FL;Y Wm:,i"H;kp\6! =/l1 /3Ugf&7FkgCs3\ɊpS6)SmSꒁ w@tFCjKMJStCTv8mW,'{V弒G)AMl"O;dbwHw7͊F[8 ؛R|XPSSF]THiRr2!l(1[~a:H+!jEWaJUu.px#Hjm2jOf.fm:DHX =EE5sF,0bk麕HX9mcɰ BHi$[\ah dyQL9E퐇7xu͛JLQU۱iU"Ճ.\9^'FPW³ 9ef:H\ٹ볬15Ӈ0frmlARBAyo<Q L$Vl;TAS _~VZ#Ĕ|%˙: %۹U0?{P]IFT55&1?{>䑧 gA N_eS0>XuGҫ9lbAjI^5S^ k63xm{ʱ|Kcx)\v<]鑚 .IdobaypeC8||w?©I:gCUjCYdY^;[Hg^BhE8UQUE" ?'#JMZ z{JQ]d{a1ɜr4DR>E=gc>.,pRZiVq=!C}sOmTuX u*YMK> zN'~pWj poѣd6QΙ< ϔoeHDi>8(Q3=% 4a?rsy掾t[TƲ:^4E\e,dU*LQy7}t1)H3MyIa@< {t}'CYDz޾îŕsJ`:ޗK0*Beπ)Hv&۳y}. > r̔թ;d ut>oLxk7O%?&0pCUygۦJmM*.ƻS?cI(^ώB)'2(rG<\V$+"l< Ÿ=6x?GF,e\&<Sutz[< "><g~|'"PeKehjW93>]"xC̐>2Sr]rL4Em&M+}}a)"2D-3#2v( q3IƳgZ퀫%"q_7Qˢw7e/zER^muRYX^P\7=9Y7߀X43HմEJ،.(پé*Kgz_,ѲFUpCVsRsT bAďذ鼡LkR(45hU6PjMHeNJ;X"dAԘʘ+ ZVqF2&CpP#Vf({W ٺBTl5=֨ _ k(LTU߃LA85 _)0=Xc7W> VzqXlD .^/:%.b[tvs8*ڡ2.)T m.i닥//Y&a8$(grzMr®D:|e 8/nYVO}ഽ#c"ﻍILT sW]>?t po-_6'&I>F6ZR'|}ۘg>a~b#]]]3 a}iiiЩ[x}6,=-u,*yQ;HنP/x64ЖjxL)# eS5S/ hǁROIƚT^υ%"y@ EV1̪Fl(: Y$bȽ)KB*͌UY쐫eQF)kB&Y5WO \R0!$. n ~P?VmHnIDfry@RquYJ" V9fشyFӲRU9|ΈW$Q|c`k/x7 ;͖l߱qPm TXP>ur.]^O}S^ l鼜$P7=6kTR.Y;W^,PRgHӑ@psJ?e}WYr,[R)%v3 5Q5<3Ug& BPq\x_Q;5n?_4g=RHcח= V͍JUMqO76+է̧bdž7wdoTH~t:"Lg'>ڳ1 X/l Nʃ61gJ؂Rm AH3P&\t7*xX^FDe&c; feEӷ%+󾜋D|t0rEVe{QfUdy^hR [LBU#rT'*VM+5W!9t!(+L¿NMw|ʧc~mf!I-S}xE9f JoG`%K!ս$UIݽak+׏Q .y. '{ۉb(QB\{*`5f͌Z O?nw?2YHslKszL4VVQA*~M*(c)ȝE$?7Q d(G`Ы#PC>B}tl Gu>O±SS8 '8 mBS؞`1-]E@4a"0houW^3~c^> pa|oIŵXlҟTe(Z(9'BWJ{MxFi`RNRy$XB11%U&[=+4rUrIe#ON\ p"{^L"8[s;$*F8|spֶ, кl׈T$O?~ &" DyC :yTim ɥj@DL A$Oc6bкQic"R+Hb&exH T-*-eIα-aSއ҃wkr8lOvy}X'={3#xu!]0p#N"]6YB"^Ɯخ&Ӽ1q}"L]!1W 0煖MKidC,@.8I= 2%u)Cm Tq>d#Z=}[v/YX_n߄nvLܔO9 $*Fy>^s$\ʈ=b$h]q,f[*#V*޲а ,r?ܼY)cBuD=>#cMW=83UKs{Z2Y+Y~I-|mj? )|[g?ubTD\CP)`+Gqu@3@zW8֣"%ل5G*d'1}.FDzEWӖܛS8>=ʘzrI``$N0J!4^v/;qixk`ϒWQE$):-g)EZ%"ȶ!*M8tEV츁- &x5a~i^cg3+|c.dPJv&vmbV!qê{Eݻw9s cr16lkV~w WhT z&k"_v1#b.YVMd+f5 lUB5ÞZVۼiΌT m'b'+MrFd1;J~Vdұ򳊤5%Vѱtɛ+<ŹVh+o N?Td6Vq]Eu6uVZA ;j%{TIbn'[]HW(/B|280kU0ӂ-2g޹U0 3*.[ ^UL"GCr^Y%dԀR@AJ͘]~fcZe"x:>+jQZh W/$$-2u:2Lh _ZL*ɛazyWu'v}ozQkiE0ƀ0 a1x{2Cfd2L<=O2~<$K3x FX,Fl -Vk}Z}=so+^BU_}˻W5VοT R/ZjUɹN&mF|W5JXŢGLÝ3zdK`Єq.٭-leRx)PpQ4gA2BÌ |/cmmmOؤI"{77TBhDB-m:P ATE}]V0XBFpK5Ci[#|r2 l |VƵѡK`pjW`gعpx݈划7ˬJPR(K"+hLlymU m7TkТ[𝌠XB\Hr 8|^I(ǐ3yJ !MqlPhg_ ﻳ$(km .ˀwC$[{ɷ90/h+tةy$`MmlAoQ Pq4hWCVϻ3__Sg0 ÇO`x2yk߶Mmz'o僇/{2]ǢA dTډm2LRH%+\+ll, Ō3qs=./h3xSV)kQ =,>|(Pvϝ%t-0EC6έ;rBMMxu;`',UT@:Hj ,{THXB@(vD1-`_+b{F vؗ + kLG0zJ{u¯veg hɴ|}D2 j0 g/f ϱQxdNGŤ3 WX il1sę#ʶP[vZ>rӁ;w *?7 Nށ65RbSdg_D&-xeن%h裸-|SL77 >,la0=a}Ժ:Gu*`#[\6W>zM^+$E5L#V|Ova` ^uX[8g»t\&¸!%kF\%+ O\q ~xOl=)+FIIɎ)sSណ6ZhB(ٞLhȪDm״3xxk8zB8zx_Eۍ>sاcbNY]1s {\%3(aR6a7Q.L*w`81gVKDV=m@euUKkA\J긮kLA04ʹǤrO*,.?9:1<bs2wR/֊bVUN-,q 7-w*esk{J2Ţv5Z9ʹ1)wq\z8XSo penc.;74Z{~"poӗ[nt#~Wa0'ʃJ鍲I햂)LQcXYPQؔSa=Y$;V'&5,6ہ<е~Sێ5\ף 'Ɣ,Q ׽Q8E zLN\-y+/3S%K7ș=Ɣ\[5C_:x )XGSk.B]xũ"Bo/gҰ:̫BWO 嚀@^mfZE0A ,vr_|*(+* gÙYC}$_Y>br&&d9]v),9.VdGX35c`Wu+.s3P'[w{>ǵQR.aVT\HjY@|v o[_H-I$&PZ L- k9be}JW}Y2#z,5ܧҔI鷮S } }UFFm\\UrSn. |42bb r.6ʹm,<tx=ڵŴ.YQaV+-PB cآx^)`!<,C6?+/. ;851Y%YG'2zF-=ݗ-Zneuf\{:pP)P.BzbjS; ÿO9N4 4n  La,n0PS*xC-<}l1pRe.k_##ʃpen"81L({;}Yq! 4WVhE;HoDS %oa"i%[BQ9:Ҫ@X,`6k5i(kcix& v 1͞qm-b *|Xn7pbP+%h Q ?R$9)G[MrD gxtLH|^K:ݖd.Ih %m|MW |^3gpTsm=0P?L@ kiUTVbm끏QJ25l}r82v WW`!hn8J?\VmP*<UrvQBJ?DmݜC#}&?nHNHqfA9åuI('R6+%hkxVٚ`n9xdKkQ@ ^ӽc \;`NܨyN[Z5]OTǵAeMie"lwl19mhyR%JejeME5>]rQh%? D=tݴ T1+Ktt|S;=킛n{?t:-Z7>ZUd6e^U~qjۜAm@Q\?e=**㒪(53s}a ȉIZKNq>wb\'"3Ubi +cd~igÛ^_&N}g{oQX:2fqmZi5ƧEVk;Y9$Kշbq-3\߸Wo-\[o5o{cz熇N۶ӄN7~Fcge30ʛ q=uXl0- _ ;RvתnAUɒR;Hafw5 1.9GDkR&Mmp+oVRɥPAɊY8{eLhI"%z^x-6EF+x@eFfPثiky=a)rVp ]1-v`br<_l 1Y<+:۬J;d(%+@Daq)5 BjJl_oOh BmaI0J*4Fv4"3-gYs/܌oxtuͭ9+FCfH*$TV6.) ǙLx:"TT.v}&\a|* u'"IwY)n 㵦$M-)$ SBGeG5%C@~?Qf]٦|g3)3asDBKU=NT{ڂ/]_ (7aPIV)l╥ zT6Vi*Ǎ>C_6 IS!R1Yr#F,E4?Pl]E+|RD!ʌ.?aU 782IdBj- .$cB=mfCJbۋޘD" $^CO }w\{RrR=2-{gwINe9 t:z"W%ǀ|Dej;z=h$I]lugŎ,[XDi5M{T3&GH,‰b͘ vEf80|^\lQ~*AKy {*̊ OXEHdpp*4ȹ|*O LJv6HB9)rmb"cuk03&@R4TĐS>RsO-~=j>xsO3'o|Ƽկ[o?f`ΘiŸݭʅ4x7rC\ja"`c~샌!P:kȠ0bb,4|Gz.^ǹNySf0 70?OA0mj1ڷ `eC0 ?!u5Gç~~+x)3L& 459 1 ƴt8Fc޻5QDְ (:,x;SʪA>`H5)[m co4>_\Nw%Tlg(IXRFOO"˝Vֆggr?5b=@@U6,R ڛfqlFOvФQc/%'v}%բtmp v/.&?ZӦ"eok|ck)GyaB eK2Diŗ>TU;C [ÏM=o?wBxSCA4޲Nw|8F#C6bhsDX;c07s#<}30~rz@漶8f!Ne+䑥G(OkHׯ{E}D!ZW!R9yPlA68[E[,=?kۆGEs BXۘkE<]}x67 y]a;l\"u6ZjWk9 ru A [+ΑT*i}iB:^>n$?cD@l{iό÷{`)u%9Mm0'/CVlaN2tّ.Wdx G ŏ?_|.B ynСC¯ ɍt:5.Ǭ]^v@=lK.u]/? ~xyc~5<~ۚՐCuD˄o>d ֤ؕI{(yIX:N2MDI6.`2dMO7ҷyI^ڤt)2-ݿ{u-{m8TMiBfjK5*)(P*3L2?J<ɦORI3$E J,(ZI}9ĖjkѽRH5LN,mK)# Oap2t ܪϷR.dS;isZŐ3+4`|+l/D2GI] :,$$phA#T TZF+Yֱ}Ҡ6=-kz^8%B97FR5@W2t-QyQ{:T*%.F"+;J+OoUj ŗIm%Q'V83oZ z dIoq_ppK{ 20:I9h[>! Lj,͞ثmZ+pRvHQqcɾ'VV07g`~ǬkTH& TwFk.m ߋhm,r{#;M!g)Fc0QzUm, *R mj0AͪC+m&lW]ύ`O80&| vmfn,8(w hc +4#ڶ-M#9Xpb󖁘)U/L"rHy~^J T򅺫jh_Ul(l#@u1Cus'~[xR $5O>t AKzKeGs@> e@NoPxD#eWb^{y_xd|bUm >F~>oO}js={0_Ƒ>Tʥ r⽬Iafڱb@c.690Ts 텔-PHI6]}s/  Øv _6g\҇` }y,21>hB,c.ˎ:ܧƛHPy6 {3~k \we_'ϼ ػora 4yrW$Δs^/NPoJa :uꍉF9; N&q0~F7kpmkK˻/\.~}_=u>|QNN0o;<q"%+g_k]>(ocTU/, F+WUA%AQ=8U ,F4OT)dO7{!GԅB񪘮CS1q2{ (f(y W _\4yӔ&-B4 pL))wכtő 9 X㓥Nr_s.P|)A("PMMtT?] JJgǪIYؠ]> Sl tZ,g!__2U|NF~wBAVRTvE&]&p:?ɈXx} ۀ-]BRcm*㍢ *ABU2MkgX>|,'P-ڙKe0 ay+՜+7dSj% J F]}Pǰ IwOJJӁ!=HrU^A\|쭑 gi+ej\H[si1% \Ъ£Rfxd#fP[&)kHs ʞ@,"0Q Icac!i:Z㚘Nr Y7sQir&wSwMj%:NQ7>T~8b aM3*XHJ‰\EPD-l4kSBV8OꝨ?DzrqEZ@yYT*))H^S~nFS.5R$9Їkp|;Y…a^*do f+lVg^+5.v.P)v|z HQۧ;g,WF=%%EwdʦDlu ܢcJm+!'Ye. | +ܘҕΥ!$ƧXuH] k UH{d{`kHѫ k;soxOorw_y^z镋aY:FsgKXD >_i1& ):з-J)RP o8<>|,Lc2?9/_|^xv] \s`2Y s4C?# Z ?״\0\[f>w#TGtJ i t:L _VfmT*a6&mA?yUZAc6mZëԸoA}e g0U,bb瑂 H\hNmИF#qz*º*9/ޢ")\(SOE ` VVgJsZ* US3!0$Ɋ3,g* 8DēŒT<'V |Er^43q1>&PVaL^vQCک8tt i`#у={4Qbxu\Sxmxc=Z-YVr-(g`{:~}(wpzpj2{rx kp]3hgcjAڂ3SgB=e4$2XM"=b2QRO2zQRw)W2+~|J 8w=s_׏/B &3z{w4aԘfk->3ZMy1LnJwWX2f-}ޠW1=co❅)֠04oɾG^92wP6eϔeXV%IQ? GѪ Weoy;-Pf:&>)b; K`*_Sfl-hV(z;7j2tgx%b0[UeU=e(_Zwm*y=V3 @iI@Ԓmc5} yR.zU!12[.# 7FZ9MRxɅg[=ĮRފ +&^ʉtu^&+Uw J-fr+ QxBAF2z4{>?ZǧX2*`UTI(lNg…2D.dp :F e%תȎ)V4PyUbRq!!!( t>UZӱsP feR´QnԃIcCֲ:%ϓvXqܵ 0BFT' >֬Z|fRP|-^HB,$ah6 VW rd#A$|IJp.@% _Ru\M,%CjZAY)AYb)|ObplECVד7JVIOIq)SD,~sx+B!ސUhU#Xq-kru"mY[d'(?f,6kAZcŘҧ<f.ZiMR.Y@$[δU xOv*Py5 ]'x @RϸT$6UQ9<-騱-l)ros(6>5pb 1LS.AX^@…^!0>G ,e+5ΒJm<~,{}cwOt}T}XAciàmaӏ&a/YXOa2_b&Evn Rr 5w8*rΆM6}8z` /SkR `1|nwi\tp\dSq%_ѻՀ{):ι ~K ,k[d1xUG~j/y*NJLr+!m>keFx-8><3asU=5o֨#P3Y(%Jy/:Gw!MIEɽAΛ~9[g7سR+^2Mw38q|(.nFp>A84&8-!q^aa` ! CsC;kÿ, +Cʙ!~᱊<>OJm}ѭ?N_hqmzvc"rh8 #c]eEuwom+|@5W5%UO{]7N—ZL  a#,_+Ҋh<{؋i}HJ=%$' <'{&e*Y%{P>d@~{mn6C=YK&؜7Gt)+RXǶ`R,GlZ.΅dVx2JIɨ(-m节ZH3Ɇ*THse+g^?IO{࡯~쑟3\zn喷>ׇO MC.l8!H2FO͛E#վE8~Xe`OKӡ7mXϝe{XѠ1:&YJdS9 ё$-& C0orA(6bј;U0ѹ;mU٦җN+d(KTŽj 0`HU_̿e>SR} hkZ21 QX;rT@Ƨ2( u7FۅO˿ťB*+L&3޺EHX|nb.¨4jBI-9xf$vTOJPxoŪڨ-$ߌF4M?>rf`֮Y|Ń04p@q ͒<d/8r"Y[qR*׶@7u^kT ;6%r=+43Aw, U$/ۚ<&0إ[(JY-g +繐 Gv*TrlTPptM]RH*|D",]AM̲P 1ldiM6wui׃4L36s2V:-aŐ*B@,/)(|TRk"j})s@fpy-gg+6S2&K3.ֵnnM!kxmɢN~*kA!)@Hc$?pz  ,_ ?&v%Jp$[e+2MۖIB9rt&ޗ5ҟmA۽I.$/Qa+mJwkK89вRF[r=@+]ڇwOb "mR16| ĥ1xz'o*PR+` Wu4r 8+U_J,H@8<954m4yH: =xFˏW0Wmds=Zlz'? O|K=q8͐ꃂA&q8 Lx$mU1YR$n]k.B2 gA5O#p<wy8Vu_^xus~ Ss%}z b$ g n=ihO,jXY>7S[Ěi!K2 û.`,e5&PSȲ E,L9=bu,8[RU ׁU -k`u,0QXsVwW^;К'Pe "AMm ^bŮM5&ٴ7҇LfѶLB @`BUjzˊQp( L sCix g%ZÔAmmZ&L} ?>g\ C5ᚏż)Ӄͩ3N6p\kcOhT Y5@]䲬p~z2?x*,WhQ&AJKVИ2QeRhf#X3 *qA79,z\M*>d@Eu%ڸ+MVd]dd^K<>*g 輎kr^Okm"H6FYQk< T?x09 sЎڅ}|uuKm7qS.h*-.`x.3Iٓ@ԺfrRX7>|i,껙F&,*vi_Wk⽨!W?;sj7Wbvmѣ;qb6OX Â㮤ItӺu6k8yyy~5yBrdrnE&,>HQO65xkB.'!N$aб:D?~|I YcRV zzKw4lI Hy 2H1ڮMAg-6I$ =]\2Wb;GeZ&fZ;8vmO*0m Qr%/ AʾP\r!(ۧ ɊҬ>R-JƗRi#@59WtE|\?{~]`׀{R2@vep'RBg@U8[ fuWJo "p0̸þ\uBV̦%l2ap(,ՒIYIQIMTbCn z"F!@Q嘕X)'-ϧfUmSOQr?%ϻbY;2-D1 xXƒ;ݯz^G>ƭ>_p_OY ?6n`R ੳ]Å3Gɣ?mn濖O|O⓿=W,S7Zk'Zw* 8F=c&CFUsb}#q]ڤ|R0.kiX}c׿p +Ko#ޠ⡎FȪpZTe^pwMTx 1^0YtO]3.B$F{)@ZB%BR3MQEI pY+J>hdYĊ7E5Ѧԑb&f1R>x3̷}:)b/"]U]^΄i+тգNe$[ nQ$μ$E~&#`~dkzJrs;I|Vp^4'%MKJԭ&ZqN߱NG,AMN>ꦁ{ڀ&"Y na '=}mp4PB⨅k/_u `߮s_*,Wbz-]힫c{Ïj\8NCNj_pLm\Ppmܰ5.S2\WǦ $/Xϙy!\qNɬ @ضw(= E`Q)yeW./0[w uX3<o>w {0Ă@0ix]mkEy]3µh{P&gDrS51,JEQ } LL*Ρ'5 'Smh6Qu&MԼrFQuIVӺ)a-gX.8+.>8&63OnZvv¾0&bERNl{C[&v,x-e'P `vx"8t*xm];B Z9 'b^B$A 4!$=>)!]e!t$մvzkHDchf[Đ<@eA̳eOŞB&G07Y>j]+ǧWiE֥-7柹}<opů+A[o㏟ tzK-Umƃ@ZU]ve7b>r$Lh=}O?Kf0J69q5otnBf][@R22n7+)'F0uUvJY3*_ITy _WԻ͙)ȳݾ[-bV*i̖bB M4Ql} 8e)6nn CR` i$9ږK3G?y'|5hT (,ՍuoS:hD:8dc3/;hf/ Bee:EfKYJY![;$X  xe5|qzPnX/#JndhƂBu9TG_3NMFpuJrDT.@ -\[ [VIV'}mV ^(4Y>x)V.sZy}ǠV6>pͰ26t-j9H2- &d^}.XŞװ {_N cLn#T,RYfUň$Z|^B>4?A>&3d! X,2 ӰU3#hSMT.^IR@[NW^^}(\~<6ӳZȊeKZx^5bt|uāXӲ% &8Dnnc-*T21 ʵx\.ԦH5>ܭ.xɸ ~ e)mj@udv^ 6/F|^% 58K@lQ*e ^=/NJe`bzêR%"㺫)UEO""!얕%6^C[&JZ Tݽ[TFm-lBإ]ˏm8ʙ\ƔR42G8'u/,r|pS!5E$ۙp=p5ly' GqհWtۇVzp&l[X{og>kn6y;~뵻.]+Tj#y bI@ ,!Rn`m*Gv,Ÿ, />0c`𼫇7,3cp}ll~MLr"[3,6q Lj2ηwvxښI&9 QٲLbsqMXjN]bmVp*+TT2p* m)ۨ*x؏~H,"Y)1v0G!>hyMv E[1k+Wާ4޲6۷i'7,ХB_vldgUa2УQ3RD ^]dۆ@·ހ$\qŐ-*yuӳ^@l϶ܘbکyTQqoNZ6ZY.2ẖMMB9a=vZޙSĹ:@RZ#/X?YXFku<5.Y /F( .ʐ$P]y,>)1enDIhH[IVcXoY-# Qm Ӧ+28"Z 䓧q§NaHQEon~Ë.6ژbLm!@].(UC" ɘbM"yc67?o]?~'S;~d4@zI{I0^֛|Uq\ѾVyWq|-f> X\#h6}W~ƾXttn].-~,~q.Yv\uUZeB0RI9e2ٻl!nuq`wʃר& cp*zS)ֵL0g1Qփfhyed]_su[/BoZ]]ÇqX!#p~ ϶m}|^끘lQf~W;& ϙ iG>qW?˦?L"V R\.w&kp@EXS\ ZfTAц"`h0DB*_(֕X_f6Ռ 4e[ǩ0 ɻ?kP8Q(L_Wun^)-RٯD!b&R>:O^ * {pHi:׽=bX}>EOOgb{* pH6?#>&0Ivb(\tKg4G ᚥ8S2I#E)jk .m , ^~uo۫v`2u(g$2ȸ3M)rsN:-dR\++V[bBbry> F0qisBȤQKc$]UE%;URqDIZԀMRyPCkEsZ+(p<0 m]>˲A\\Cv6~S5`ֆ0ϳw8paBC`7)©uG'6nj ˠ6H1̱ߺ~Xt*ewm֠ !kG@)H> -Ξ=µ OU6eńbmsq<ݫp 7qӘ}*tP"ՆI~R&KF-jOB^żyF(~S{x_`k?k ;rhΡf1&*C{X^{08F&pÞzmg4/P֒R>mDLΖ[~>m c~z/yŌ?7Nj#hœpxum.$KZ@Xc; 5He3V6u&l_6-[dk2 rw8xߟ~lG=vµtM"xy_qgFnԋ HJx=z3h} &ΡmT%B|-au5qKh*+i%l:zE5hyR㆟cTi["p ^/3v/M]DǃBG% 53ߥ=0۬acjeg?qoJxC~;ޗ./[n!t:EPg@,6M$ ==Cel6W]u޹gЦ9KŋVkxg?x} 7F1EkUx\UfmdTQ/$ӹS`@[>ĖA,1*أ9NP!ýຟ`{kGNA3ZmS(-=m2F3loI[iHLlab ;I1PlUdq`/~^YVU±w)WֻL(]h- qA$:,loGqƛ̧d-屢ʳZ|rR\@0>3[p RTo>3慉S@HЧ (s`#-5b* -z&QQޗೀs|+i*xL!/VBz\-`N?mR礈0A%YzZFՔ Pb*ψ1[[R<cX R *%Ԉd@K؜/n\[:Ɋ(Er >bG%)R*|):eK مo=68f (.NL֫3.[H&dfxVjX[L\)hWǣ(IQļ`nd-G`BMW&;1ܐly'iMOk fF$s^"0g+-+|Nuqpfu/W]dTt8eqf`1[ 02yK~XMRļw%$[V OWs$cIE y+Ѕgԉ'Swfe2dW ƺ1[q-KF[םʲur@.MVW~2cOgLys\\M#`~|`>xS0U:.9 :]Tk7H`M|oJ 3"8SXflcVmZ7v9q=aT(<~j>t73@05;~g=. d8V)0[n_q; .ID_<Q<3dHEOlD C1deG@D˅DbF&-;]\: {OOn¡S& \o} ץWQ5pJ{t]i/ 7\; Fsflco_so p;;յp 40 3[Nz7=szWQ8ث>Z7\֎;. 3DKep-*=s[%6*!~ c0&FDCpب1Zy $QZqlcdUbFx ,y@j#dˠanQ=pÎ!w~\sN7z D9ى%raQH-~FUL@5t]| I/\xOlZ=9eKyV^ĴNUN#,g\qGǎo0Yz 7ܴ0@"QpsÁPciN JT:]~J&{k UȔƊ9xoz-GN-/&ҸIfwv>zR אo"PDq2#6{MkmN‚\m*85Lm c3"Ĕ"\q&-;*" 4NetDi]Z̭@1Zs+M|fص *⾬X٫+XA]&ѨzC׉E8׾{o;\4sm9v؏8qtH`P^3Ck&h4{}>ֆ6LlU/=}/^:sZT!Q@ =]_s&œT$*6t T+R:>[Xgَ\Ŭ 3-a>Y6o %lK 5j#}̍eJۻor kI1Z+Xx2@8ʈ2ly 'Ρ6["ufKJ"u2Nx[\`Xb )ޛmuqQh-r'rJ2NL.?oEcmIkmIB7#9^R 7 :?ίhMH{*l5i^op6DyBK 6lQJ7i=q Tv<I j۟RB=ժ}Q{ʦZ_SKNWRez_=l 9}-{R 3rU Gپ2m+2]爋yϊ^蹢qR4 j1B6zޗ]|S&N݋텋0Qca7al~jD+9M<A;: VQq$E*g%ZW[Crj0Fm($ g&26<uVTNXf= t7Pz.j;&Q9r :a[wN8wţ^V@h/ߗGjn5nK9 Rg BGA4*e` -DV-SU:.c+ bra8Vӏ™_f$eJp_7aͼA9>m Ew,yۮlnm.T<P74a」=2}νR*UI%U-9{q9il̘ƤfXfMzfuiBrMcA ,[8N,[*T9 3g9iZz/p}q=8VjdxÁn\yyIV+_/Օ*Zˌ- O?a?NҘA:naiF+6ʠ%] q40]??__jS|ǛzoN, _[PFYHض:M ^jDł%Xhƶp'*mc㬃Չ{`7?uR oR_["¤i o PөK ޔI|1?&\/QU=fD!\$箬NY/X BԆגRU)Ƥ2 LDՂ"7cH} ɁxXm Ԉx?GqJh8{)L.ka;&5}XzXmX.j)`Z#tdpv.ۼ%/TpK,f 1VúFOO3Lt| Ud'%㲟ؕBEYF"m !p^C, \1gx+7ۏMa6߃ -,Šɑ2**έ40F5ں'=|d{q6x 0P{B?t#|m0[oO݀wppGfc?:Kۮy[ܻ>pt 8w} %Ѐ%#iҲ>Hjڨi1[ Ρ[:Tq1K q=Dј[vFKG$7X"A±! h2ld6+V)Ц,]A;2q¾_}i8t=G5/a%-ŦIʘSla?%B(,(m={0_4M\c+/'WEE^IAƹv}l`[\QHzᵳ6aީ\a x9GWYΕgq0i(uIE?W11Nğzw~oXcx kbdo}IĶ12YBY95u$q,ojPlZpRլ5&g<%z e^2!MRNdz‚ ةqѰkޯY%{3 O9QM~4aLPzSPV2{84z=?<ٯ{ˏկXIqXlnL&ONmca;KC]?Aem]o_態goh|ϔv@Y'ܐ}DDѐX.|.܌Ѥ WU)-LRTl}g~z0=[QAa WkfRBS|rnĀ\zNiR 7][&tJtwJA=0z;Jx[*>w O>>|w7J,畀ljeS9X$}V[B9hBRh@pTmm%+=hb0I6խv('L vV^Uo+U,]3j+X ~?ٴhEDy1|( ax^v EVZ|^S4%(:YET^VrB'&k-of+rlt<@%F[WM>_*@_F]STnvl#hiN*wK <- hf0Aؾת 6vE;1|jcrJƼV*1yF.)r[6ryTd+ryL&&A'uOđKjA~VX7Ipg†Y~tU1xXs 5MK9&-bj|'$Y9۟+)xc'9B.s|MuFWxJA+9<&7i^X3ZiU 3S`گ3psn鯉\$R*VcuT#&KAd L"hG5 DPeg}Pz8a2ގCdpP++hDsZN@?7U~{?Ηz;/4M7lU X !=y&WxD߷T mڐ(۔x'lcJA֤ 2iЉem,|[p#.b?Exɳv9g#k8|QD3|X0_:ih-y,7ְ4g?K՞J%EH %U?|!@ &-Mf4FT6WF$=,%b4J2YG9H$gTf`B@)+atY|mKpS'p7m4 ,IxUd!Q#6'xcQѤ,lݖBk8G^a~ D|έsJ1ljIfe.[ 1Hj/6oƫ{!6qzlp$i7*<`q08,*7K&(0{<_5G>`ak9fi v.k;ϾcPχ_7,v< n:҇[CwXo8֏jo˞litf4%T]@)gCY6܆OʘDSq̈́Y`3u',E1ABERí,">YYB" ڥ gFfG0.Y%M1k 5YW YοpuWixs=h<+x$k+o*>>YzglOAb \|OM ֖S)}. M>2YId1<~j\Xm{myW0u)O6rTWRFl0i'3vNg+\ ,|вۀByIfXC ^FR(ۏͯj[̥pLsba.\b(.@7XBJHFBgZ!dFD$؆&uֵUeR9FsAm &Y5o}7Kydw*ޟ:3 eo~V af|'"/<•RȔ [N0+x/ (\\Fec9*!l\ /=W|<~5S!( ?w9=3C:Qڳg*`qg4JQXb=Is ~NK[8$e3ׂfp5eآ2 ${nE$trqRţQc~ #]1E.}6)t6)v|>N%֭xؼ7NestM1D\12^)w ˗ EX[zw< L0LΟ:b+d|VLXNJE`0`;Q,uaS-,&gL1fSf7EzJW:S2{ep1,vnӃO"yFarlY3K 6uLtT]PʾCCX Lc8%fыm-2++} b>8rKlx5z"$S"=N&uz6s9|gavi RC4V2Cꃲ1-RcW-$` *=)(h׹EZ#^cL(J_N |WUA)'H]7\O^ae? I+U"O0' sPohߛsjY_ ʠh۔N4ײI'&ixn92ȣ- gv%3 U54둨dJ;SD:,Dn,cOX֓E!gd 'Ö3I r\xz pq0P&|p8m\RG^Bօ FW͓p.gDpF٦qqM2x(Ĥh2\W^^Yl񯗿}cWpQ8spoOV~ر20:gcIV:klK8dWx@>:pȮ[zЭJVBcs|9338- L$B"/6N¯q.WR{ kww/ze"Čرus1Nrn*oCңFh|1|Cԧ>e^x3w7F;30֔cL avP.džmjPCJsXqGDj `;5_+ DͿYx986{b<5<9M'o<؏5DrHAVC$s=YQuZ^ɏ{{ip>7$(Vmw ‘5f y˙3-aO> K[ʜ3v~{B.䨱.bW6fXcXKJ1o1#\'t hnf0S&>l+'={XEp0cMl(>VS1%PE-g/]{s \5xK?8:x/"98QUɒ6nh&o"L&XG@4eA)ܴKp$,3!D$CmbτDﰼNX6m#( ߵȃ!#.BiXڊm"R܈Z 4ܯ ᰃwܻmg߸7ܧЏD2mYt5߂ 9_8H/v&gb#lR]+CJ1 ͑8,8?SW`?ۑL<ؽlQIoV%O6n=.Jxj(|h&d֧5*-a< (VHpF%# 绞Zgg^]5~Z˓2GZf>Gյyu׳ɊWw4OJ” 'Y}Uɲ6ƱdulCb}&}ǧ"5Y7V6;.fۘp(;n>tzY۲tSV:IΑs2R %Z_Ȟ8ק|-Q-,`,~F9KK?}C_~}=GxFf܏2OΩQ2eedɆ :t Z>ۗu+]~H굽ʔR~6n2M/mfF͂ʎHia<5&zV:̅ZL٪t3@odcxjNUFB_CMں͛ҷe6JŅm+1Vg7^%&eR8'p &Tt_<8B|'`h iw|ON^ѽD+_Gĭo VZMUSܓWD׍dJܶx,$76nEǝe$E͠E!֨6f&\1zbOhLBDTת [B޾CiE?yjX>yF]=[φW<֐-$ fLtN{#se"D*: "ѐi򐜲's9k-ѱ @̩sI(d`l֛eEk-aqR:W(4;*lGU#nDLzp}s a\+6VRƝH3aߋF| T}]hV[ V-e`iJKYw]B)znɟDb JU-XV{½vn^:X9ض1pi2Zʬ˰0 z23ob<B,;h tW=ܳ𲗽l|wG>g\sG@\ƨP>v9"VKeJ 3Jm$V 4 HVSEx +35gm{C3$"@1i#`,|:`]6F ʳb@G< wm 9(f_l8!nm$UB *7l0 C$*hb"L6vz޼rg8W.G[Jhimj8n1ì l[sz~&›@\!Q% Xiz!DdZWVi]a~%*0ܒ:*w'Q$'{˘ӯ㬩 xמJ,)YP%X8/զ *kdw,5:| aV\IT-\\ m(l_;# 617 m F&AQm/LtDR^,W1ޝC ;ȭC8|;au+_3paG/}vXVaLtl7},ۧY7IGObёD$_ǨIRy&5΢si_Mp2qYZKh \|@8KY E] +zbYXyɾf'>|S߷v=m7`b%osIa$=\MX V^!$?\|U kj7Fx}\ư4a<1vd L]/d4r6ZV:ԏhYq~'uJD.TC @6d9fd9TR,KM[ϡ TEϙEB ؒ9(T\_7dRؔ2-@9ZTf .2e}w> 6ip~7M)ԹlhMgre6oEi0)r|5T8.{ĉ/]%z~}g=}{رcD{v6c ;]x5wބ0h4=,Wb--;7Ky"*|KFu~OlgᲘCdk:]Ş H% Uj%0* eN(om_{_"E`t%Sd*iGly2f6e[`b]/oI:: "BH[mU*J&U"MM0Jeo`S/渪][|USatޏ, dX'\'!,mrn%!LlF9j %qސ7Q*MesKMBTν&*>F;6"EZ&, 7Z7>m|l27CLDk5`կ9~EK1Z.DkQ2E^1&œg !<:()|SJ0}],`i·h 6wUP)'>ZwbZEWQKI@ɺVl9ȷ^N(tj읊++U֨*B-kpx8,ߟ,EWwӡ, nz~n`9kHS&j0)œʽUڒqJh IݳBUtB6|"c|"e mDJd3e$H.tIj:>+jxPX"reZC!\_mS 9pY6m WEHv 0_EC]G;?|dhKEkhKOΙT#ύHaVɓXǕὊƫ0E"qI^6UT>us~ 8l[o7Y s6&t-d''OR.p41lq*'Z?yyΰj"V8>Yy*Ko΅:Z AEPSa FE?|^Hx5dQ1 . Lj J˥}^T41 age`;m*ӡ“ȥV ˜qMTMfRGL1\hECx}Kk01k` ~ /pKSxكٺCPuxuF0@#}֐Z&}ZŌC\oqaF&q1O|>ؤ 427B|Uk2.D mQqԫf૏q~FStWؿceG{^EW.x2vsQJ.չP86 $Yl޺(UY= 2۰ W4fX^>HA>;¯C]; a>,Vk ]`J\\tMLRyg "ʨvecXFMcRG)ŨlUtɞQZsn {㛒Wp Jv)=: :rG.YS. QNΨF["HD)/"ߓQTӵ ]")|ީ9L.^LJ^P({n$1*yUa6XҢ&*T\w0tml+i"6z7vU`BWՠe-^&2Kncf0BϢeA1EϷ̈UP-OJba9˓,gj3EQxd(W:E .:P/ B`Ir7)j&|kB2.&3!L&(تS}.$XO܆Ć̩ mvdLaBm6 3EFY#֍Pe/P[m!goyŝ#olF%jQ\gX U59 D2@6*'eɕ!_)r.* $l)Jf| K=h&t'*ߓ^ {uH?Qd Ai+6ڑ*QɐuPT9ÙDz mgF}nPa0٦dE_ϊx7ntQ6S s9K<:S=}aԃkA6}}QCRFa_4X<$Uº&up{֨l&u]+`u,,_ArK18WX 54Owl8./J'8`k7I?nma+,wP"ĐIhGa&9uv{k5ZIl5囄^sI)mִue=PAe @iOyffя_l?F{!/ 5-\w;2W/@oLy _A`y5Sz0V0Ђ`43ۇ_kO3>_O{hUަg"|{ʇ M Lj>"1ۅXgcNNY(1eufk s '"1;1oU>x>s=!\99>m8Bc)v!'=ɚY9 $f+ġ9L&B1m߬%Qyf6#K"kHH*1A@l5)R,L Le n*} (Y֫ż [{diCu]*I5ۀ oHȀ ڒhڠiӖcs$$kX*x|dj>Qhg=6ID"ɅUYE‚ܨ^ڜ^:?y* `Ǵ6t~oٔOaMLdgks&5Q|~838|5ӰmZ%5 BM{σRc$t-?ӱJMH6igN]F99a$A!Rc3-y8ٷW솅~=ۋGٷ2ڞז .w1I</`cl3saDE7ƨ6Bkе1O5.=eWܲ*z4ݨWUrKa 7 Keu\>3/B :H&OusDWT߱iGQ+bۚ$yiMBqMa0H~ Ln(iPNbp9m`*66/~)slQRaS>kWnݗ~׿ ߼JA\!z뮻=d&X<'*yx5sss;}aA\Йz'a:"+r%M@>a,:'s;r[ja#caC߹ĊVIMocBEU%V!ϹlT&tљeBcdKGSR*Jn:,EP}rKWp(BlV~4%*C[$b>WD:)rdjcXP2p^YkqP @(.O%XRQ%ʏ(27 ݷ`uqA͐̄iO҆ΌGԟF0v P$bAgl&m5%;<Ƀ [XeJT t7I1Jy3fJdѫu.':A{\qY*|a =DŽfBSXT:,|)ij-Ԧ1y|"vt$CIUUF;յBpTVlE Uqlfx"qksUbK[ƍ̑}]oJd0ѴT  YOck-1[23sRf9f#KNAEZmsx߭0u 6gaV=qg~ L'#~z0 <ԽACdٔJ,lZzWWdA<-4AogԹs;R+42'vl`_w1XR=r)S䨅'AgKbgP䲒@0cVbqm'^nۯ$`wyږ*ZB(pÑcfٗJ9sD̅ n +aiǵpi`y =xXy4^'kA[?58؃p=<cq{>󮷿[ BQ^qnڰQ<ǀg{HL)_{dS6׷  R4nh6CİAxE %8vꊁ6׾p,e^BpU"0p:*[O̔+QVpŷguOI+G[s=zr ._Fa;(b"xݰ:Ʊ V׬-a%hBdCDJ-ٕ9!ј @hSش!3*tT<{cܤ sXL:U! F}Nm.=KCz~AyE5z;\CFZ-!@m|PvdRG 0<.F\5nD5 *"-gy=~ ^T&+}6Kߜm,O\?l-[\Xp>&[EQ a`!3&uc{d^m𶟼~T!H" xƓOc=hg bwlغ @X#8Z 6H(8ືi)Y>Dz^ulZ|x\&HNTN{V*r*'Lx&vBd<\:gU/ ;Wm73g_n U~|Zsڕ5~B\xMg\ U{.YGxorg>d}}>/YQW s0\oa<68wƿ)Zrw)<ɱ9ݳ5\Z&¨Ӹlj%PD)snR*!)[ػxnoM?~ n;dw1r16& q`dJJ+sB&'bρB/XcC#L8˄_ l:nMP~r6׾l({ZȉkWK3,sa'rmjtЅJ|ʝJm9e;ekqMdj za/y8,Ev_[]<{8eS"q /\2iKV(eUh4wnaᦟ9-,mH>ݔб/RǢmJpW܏HB"BI4oೊ㫟BM' Pk*$Y @[m>f0Jm*SWn|V*BRJ k<4+, VbCUj!JKBe u64I_*1}{m%U譪/,Y6[xv >VV<>f&YY@i.zSkDR׶l.8V"遳#iەTQmgt|A&׹2^)d.M<2|(c;|:u$lkBP]nV+o8a&_J612Ҳc}AՀZ [LšxK \P%ߤ6R^]8E|!Q-š2׃Jic"c€*"3K,y.,z@ō&m[ Mס 1*Ҽy{~Յ rp} kن7F2DAD,X`+ס}.Ds D$rFnյr;yef4xw@i(E/ HʽT?=NR,;.WTNo|N\W_Tjh6=o׍ۘY:Xps0?7ms6E&~npq5;jXS@1,jrα!g|K2;Hdsy<3T#U "bR$礉+{f&U5$21+廊=:W:#P!6 :suç!~WK%)v;ܣg /Oç8s u;{=/;ag3|,D끅i0%ˊ3G((hi̡E  @[q~5W|ҷW_|d.g./on\ُc.c 񂤲a5L;".pÌlіl Tgcu~,ͤXH9e3raVMSE{ZA|MnqNxγW$!Ӝ le+gN[ bd!kHɡ`X7|.N~8x] s,ME|60=d s_y9V1*~ӷRNTpyi(n MHj ,7VB@ Νxӫs#i(Vmq1hsl Ok`u1Zljy-!Y%+:mcDEm|jK֏i~Kpa<uxlrFT*LYmo4AڡTW$yEh>c LΕPz3{_ rr7[%ߗأ+چY I- D*.Z=hR%50Pj{YsQ|MUOe}f|h]zI?0ѪF^Aٌy7g {e7&Mf50_z} m<& ǂ7T8"DP|҇qLJ\hC" hUBU Y~ҵ*DRF 4ђl19晴 &Tyù- 2DY+toZ>+W`0aN[ /[x*nv.^Efz, Vi>;JK^yۼ:&<7I<mFmt[=C;Sؘ B,6ׄ{܇fDB)z;Q$ 0Kz袩ߞ4>2Crph@d>fY]V}Z$6ǎ<WW߶Vaw/k3` uOd݊sx1f*j-8LyμX}'`\/ f~ѶƊ>uxl/⬬q_ #q>ʛlgWفm3Iݴk-KXf-$Vrd׆d/CEc\D|/`Bydʮ=߼2|<-;䧂\ ڤ9Cڹ+ϙWؔ-%jn_`NܶQzi r )l텁߱,:>Mbש&բbM&9\HĊ睋_}u;W~}'=}{|ر}q:1farƶbdw<"3VGp5aО&?/Ֆ*BjMi&c0JY'lSl2=AiVyR)B\mï9> AFN ق Mx"&aݴf@T9r@^$5/A. 2ޔ ++pCNq#eG+L ֚JW7[ NJkMAuBSTXf+[bAiHȳ6i-..ec *Dg~Ik:)%zʇ@B-O>.o׃?Vo}[8(u]`A ѣfŨ >[GBzǶ٘VbedR=u(0t32|. D{NJ7]0ΫN䕯>%I1j̄Tlr6"v|Ai].ım5X2w\WZͥZVyWK0 I A5fhB3 Im㳢C:r+k]aJ4@cO6:'v?j hlYcbbఉҙhhQ  $R{CRoC .fgK[nsujS];qb |R(wB=h߻m?Q@A|x԰3SX0h5e?0?A9 RSZ|.3[ؘ1EBḄZGC/{Kݿ_ٱ*w vkʒIЯ+ZOrYHL2VBGpBH{C$;ylF~} }pއΙ-Qj15jvnȺs%@l gDA$hwyllMiU𼐼D^k l ‰# u;uqyaGݔ,3q [C A&lOfmpDh 8NڀQFmJ?Uf1uה 9g'~hg3UFx BG\+ > }cس{Ta8~n_ oy6ؿ4 -'#:UA9Ɉx|R\ue3GhKp0_ɽqX_5|Wჟ<?} $/vf˙j&}=|cR8GFx {UNXA?p>'sNᬸ.!QIFS.О2ˣ7Q'68/0C() A`~{NsưwiB ϼeKcu˪XU(OвV w{O.bi6ͳhʥb`X=<Oa fe/ܴoj,ұuMIREI|tOFd'/H3xڀ,<)S-/&\%g{MC>j|N۬+HDce;*sp>Bk[~x pp1׎-x7y_FSń g Z+l=g)zh,ywֆv30B8g^ٵ$2o\D|;f9b\-:_TjAky+1o(w)цǴ1j.!,csȪDjeS*B^L ~v?~[xUws]xl𽂪إ&ZE !)>o{8ݳW N>s~ )\",}yE4f-_)|qzS]Ab%cQwJetSa^T7DL i[nk&Y| GoIPZ! y?oTMoEh:S*Q 5_l[;p މ? ׋۬$0FHx41fL ֙Nr4NZ-S@%JMzV@Sʮ۽R"} (DaR( P"*taFUhu9EKM&*/vj1>y84*~4X'6~ 2~Uj'_eζf"BoR% 4Mf:"֋@wEx/'*:Ϥ[yEhiRd xМje/*?]QVnqh2 "ʶ/rʘK,9ʦOڎ:$wVcIҋ1(XI% b{ӱ:U$V8V)V9+&6[$!~XRZG&qCB?*ɡH^Erϵ\B8U1kpѤ X׻Ud[ئPS5žc=kTcY*c->.x ix/5z0 㧧?u~põ/r3l &Pcy<ذBVU\u9AQ)+HYSϽcQ<<7q{{ f.\u2Ǒu)YWxMW+`X&o ecdNmVM_eO~&j~^rq*l5{%gXQH8Q+6RQKDyLr٥jܙ'`||0n#Rj<)6VM(DnD…,9FմT-WGGr#k+k&2&r-[6_Ur*MLPe,UDTOqڞԞ):eVTD}8(e2.X/pHK d r/hxyVȽڳ+9 N*6v[i#21X++@ѷ&:K,d;'0CQdreTK˲* +z9d; ?ɳsʘBc4_. kعҞ@ hTDdl7`v}vNFnt G2^08WTӇҨǸз}.,T3طUg=\Zp5$\>wx{y_}~ƙjEbqDلXPT$qаr߳\*U݉;qݓ&UĬ3&𞠷?W<3c~UxƓkSs0#0Ɏ I$E+& $}$|(PmHMiګh\<"@#K'REC&*jkHDe R&D<$!;kAl}-Sc2U7O*WT7,Ҡya+</03C 4o(kBc4pfu aaD'^9&KHZh%Ab?\ {Y)YhiXv~C +8UdWKC2JOO_߻\ 5Qsb?Gul?ؠ0eLЦ=vLGͶPI@s"銔^ٖV 6ލhoxnxKހ=߃?vYTP7XOΒ,gVB'(&|"dkn"b.LT-qo!u Ώ^kvி]g߶:>Pɉٱzlh R,kYsږ2 % 6%HU}hɸQ|u~{/sA;;F(fʴyqUDE"X.v鞉Z2\ )uh9R@l3xC8vzq>|aiC=rH ˴F܃+)ON|mp-=.9 *; | سMtAYz5: 3!Ru+_v__y睿v߱n766·dqdՂDO M{i~{v]aks @@",96%:blXg$ؙ}EyO+nzwH,+UsN +8>ǍJ$qEQ "B +,\I}$bݫp𘙼̩ z}2ޟsC=3y}fکJ{3XMbbflZ_IiV,%WϾsL؋a |R+7 S^ +rHm ) f")Z ⾝]`sdTuW-m { X\sH==yߴ*ds} 1zpp›?j,n+Kݳ+R "01{%,嫛&/7py ṋHwűmټY17 5# +pİCR-f=fTZWpJj9?z_lgW<7L6kk( 92g'Vfp<|wJ5BwvH*g{4*Db7堊QA 5__XO#J?: 1GA|hwolݟ3kؘ!\p'5jˀٮ M{>_O-ne=Uj36v| 7ϟCOߴ'ε"kHm*AB h1\"1e;Z&>.eE{\J^|du{ *R *)|t ֬|^Գ&BˀT>N=]tm90M9]|^X_¹6 k=ܯ+7Qcp [^6"NcX203n=VFװ}`Ƨ+V!hApem,ꐑrC0խ4?Ճ9\x_|{=sWc_ի/ظi `i P&/ Ruuy~cwQRE>0`6pб;g Pu' 2mVDE ͔dS?^~ )Lɀc3=偽؂+ ˪Rɀ5<cxP|^U׹BŲҤ'4̴~ϧ"JXB.A6z0QOI|>u9Ale;إzbMř@4:^|E`9i9A$%䶘`} ԑ LqeA}˫%"v[muQt5  Ifn [:C<51ٞ;6)H͊=8'gd6O\OR] 6(|rA() p \L^%[`A0;,zU;E3}-P͵ s6gM\ c>n^i__bUs;ͼs:k( bIę6.˙u4ya`t +ϛ l01${+ Ӣ{ A,I݃ DA*dԃhU!>GCԢ1Z$Cj yAnۤ(pmKTy̿QPOxfH%jk ~Hk.ae)N5O,>⋗Hz` M*LF5i{?3m2 9ȌF7h xqK}ϡd[VI PΧI$%z3>d0-?{7?ԙhd@SU-COz :gz;Z ۀukXT\j$̜ܠ7W?{V斣E*:^NaO*7*X| |R;}㇆py3p͇򻖸Xس ~0X+g .nMя~?xCrMwPeAEtj~adA&rLmjRye˞/4Ls~QZn'G7.nX_wתXGQ% Ho~ႻQ-bZQ1+=_\Eijz{d~ؚgxNu?iڜ}: ΢o{vXH ؆X9iNL+ZxA,"*ut)ThQ`7b7Rvc^*4+x](;8kQ6VSn;~~SR; W"棅X|(4#M1'ı۸ <@1HFmGNO(wڠ<߁sTyi/P_0DDGLO(]bUb8I _qf<{K?zD B,/m2. Qx<]3^%΢\VT*AV@4<YfMVU-Fj)Up_qk[rxGsV#L&]y6d[۶h FT"9k.~c}:{ ԈN| d|P4űItmtU6؞u煘FPkyh$+1BvI;F]J#LiAѴy2V(’Jݚ. ο q{9uM ɶ#]'ޡ:3%9' WY`#?ѹ3wwRgʔ(ӲA.Yb/ͷrY^%$_LwwHm)ƙm r6SـK6\/$ٜl9j˔uKۤ`栠BDIKy O^lڡX%kGGLIRDЂj8LmLJ;Tubŋ;M,BsߝW* Z]:P:&JL6_/yBU`C\ g_}ZU3@mJxSG@\V"'>cL y(e5=H0D0yf^ v>#9#ź!á]C,*Ԝu⿞hYj<*|!d֬5iq!grѪwKWwΨqۀ/ݾԍxl(Y.M5E}xu3 3W(P0=ZB?ęPMO6>߁q ۳VĠk_98~,xmK+?-C3= M'|o^~61S0h韤!%wrM ]cӆ#9BR1U?x2ʰy 7a6F9Usow_irQrJ6|PQ 2"T2bFVC@ٰ3v*P`2j!E], 6"«~QCԶ"jj, A JQ~bԇݴc*7ɞ?醢O'TK[- )s< {ReڠO`sg竄CٯHS+@Q|Xʖ:3]o䢏ܲbz PuHd׋YTrB̟o훤.m戥n|]tO]˷_{ֆs"~̬qIjeKE) MfC qg6;3T'Y}7?&Q9@kgm,bz1#t?tpT<t|}@>\;K7ex JǹO[zhÍm`e7u' 3:CO#wqNg >u?!wo4v#|IS  $8D!A}\Y'{YRbi Y,hO^L{=G%wњ2 ꞯ%!z1N2HQUp^Zrk>'&P/?!ݷusk{VQe^u=OyɮsT>C `5*+SN M>"ٛd:%Ŭl[H%>]RPu^]ސ> uxgsA9.-ADB8 s[3`k5l\pt3Wvp>'s])}b=*ͺvot`}>@kץ:+{~{D>*\&i9H C^Hnr\Q qxo~o7ot҉ic7PL@Rʺ4žo Y,zVI `c F]-~ȖF [uR (7s'M֟ϲ sW}L0'^ƐKrweh HɜAK5GbJ(Uk,ףTcx5[$1qC@h*2@ |"ZfA2L~cw&B,@̬1+/t3p&3WΠUzsoӦgF98ҳ3J>>kK dm#5\o:',dJZrj3&~_|ޤ`foOF1m{ 0,&S):, !~Y2*6J),yV L!1̿0 %ۅ_1A,‚ S@}%mB,c[P]h ^>c]"c b+Y/@/0*@NnH#:*ߩXp2ʰ‡T-6L `)/Bi0|a7ulLۂK%Z_E{?;rbGV-3xt32iR{[dWdRpj 'f;~`|7| oxCj?G7Mx)jvESqa3M RÀZ_)6 E)e%CR&螞>gs0YWz FRN^sgQa(T1k J~3`gb`#*zO/}MX۷@wOS~&xe+~w}p}'n2ٓ;0f6ڡ;{C {hԌB-<^վWA^I]L#K FDX}J%.* 0eڨvǓAzfRx-#/"ldH)Bqdqm5gў}-KLz[Xa(S0T&l'?|q{/^| vryRם!&&(‡~ti4`g8y|bȲ-83cO wx4(ScNwIPPueT}?ں,S Z6Y |-{b{`UΜ_|c,I?"mzHS]W\IYvЖF,tS.̙C‚FI@9%+~XԮͥz\u`R(Ƕ}Y[( ճ貊\h^0+-b+8` aƲ3$3F{f0 z}CGr?y o'2z]FUǂ):IMx9` ˡB:* gQ%r {D+Y &-䭱\eVj (ԭT$1})!xnrbRT\RMc0I]rЬuF]!ziM* kD>҆S{Cz p1e8U9Ul:-Z5~tV7U?s rglԩ{6cb&EV?3͙ˬa L?QkG9Sց+|C'c\U 9P{RT5Bfr0@ޮ`Kڍk-g  kހ<Wwm i|`4\pv_ñ`\E~ck;s=PVfc8"QI< y|Rl ( [W" [:[cxxþՊ\'PJ<-X1!4l?'E)BFrwb$DLR*fM哧w$:yTݙP1UI@vO42`:me`J 39CO3x䌩4.{?-qn:# Ud"Ѳ;pq:xQL-=; Pjj6D-GFppzrK@ڔ-͐Kk찜o#6LV#3T@!6 "Ґ N󍜰3?$ 8KC0bd%䬜-yxw>{k;{F6ދAQ)DFUODAdP2dЧc&]E_HpIʰ +sM(71S޽#pT9| |v 'OMl` h 2#G -sW(Oh҄OCߪP >O^v34irAOq C}<IK8# dcD}e>\(|&㩥u~P/Aw5m#9C)QyDHϦq>|]#O03g63bsXVh?:[yPZ0" jxn /u5/Vľ#~{~qD*}&i394#Rnm<¦0[:u3 YYSeui[35'8]B[S ԅ̐nMka #W+<Ž I^-jgqncG~j il8K4}ѴXS&'dYYL$DT?ڿV,FYPΊ66E3)Ԓ(RgGMWIY}ǯ]7_s@#<½̙3_ޞnsw+5 T+dUpiirZ= $5{A}*W\i5VȒ(KכlC͜ū HwpҊTTCA)Uӹ=$D{5z*=Źưw?T PՒ@zM̢=(,!ӼUږ^>Ms7Mǜ7D Ur5 rL3 F&P5(Pz5-m|vܟuyV5ai1C{vibޜoRbZ+ 94z5Esi%g & X Mη1%l!XDo:@ إ0u͛ f[3?gRaZEAE],[j*s13z3,fs!c;ed^Cb-q(Ʀ/ŜlB w |i2MsSbD+քƲ3C ^ ә-kؿ6:+O ,/9>_ZŝQT+1磊eQ|a3Ƚ]TJ `%'A=#s{ƵS?;V ̇{$#NUEڧZFpA>+^x {Z>sɀ~V2n-Pј<ж9J3BsC7 $>;~UoZxV!|NwZR3jIWi5\нU$fʠN#3ӨcEϸ㶉dFs3<3~ )S*L^ƬM(}cN&=hXV:#VV  h(3KAg٘ /bVX+$S1iMbh_e;ի~o|/nA'4q[SB>C[R!_#Y {{խѾr CLM ٽڝds )[ չ@PH7BQwGn؄+[Fyd o{vAد>Kf8SsgϞzWٔ0eKE1fN`Th$}zӜ͛w 2jݞ ssXK,%#ѻcPחXə5,XzCKI Ÿ2OKW].[Q̴ 6WiʪҎwG>_U nT,yap.E@4 {~)@9nk-YMXnX-Y!Օ:!D-8( mUgcg)"}h_0e.hecq3C}2Wuϴ[aq!@፾Z\(Gs32.m:e6MC6oa8s WQZy 9g)8㲕/aDTǩ3=Uإh]Қqgл_;=R򊕄 ּL^׮mP=9{ou#L{ĂKzlxs>^u:-wrq"yyCx;{>ݿo~k+Risl4ҞC2̄u٪mpj`(gvτ) A2CrdͥkL --õ1o.skp`ŨLxIK1YSEN~'{UT  W5wle%}JϜڊnsAw3xh'a(`AΓwV8:UP8S^JG՜ d*Zq7H[/z j;Ѹ)U+OHI8688RR}5<[ꮇ,05"#@Exkmiq.6.gn X%Es VU.ZUeK (6T3:EE1]Ǿmw.O!> w-hGPD n}*WUiyd[! ʃ-;A-( Gd>1'? EjFp^q pmݵ~x۰>\n a%&[-8. 獀{AN0(vD"`qܖl bR@1qC҂Cħ4=e-l6Uɟ+hSs90ľ}<|5m/Kh=Ox]޽CpmxBnyd nUDN(S4-1Cx5; (&\/K+Vxm{h;*Q:3=\6c#@IzeQ:|VaRsAhY9LϘE}v`9W;d>;( FJ3עTeP'`?UCg6W;Uh6M )}M&+ʢs|g\*c͞ ]Of>"WpZ5" (z{W4:~ `%fJE͠;\i;ft,y糍*yuWwĄ-YQA]{'8uq9>BB: O'?'okVla^A*@ B%}d q P?o۳:H]bXtkN೏_3WZVzd7<nWP,,笤>oC"TIIy=4 # joUi4Rܭ%5EA*F~-oӨjh䚩9 Q4EŌZb"!(GD :^@-n?yΓ¶Z~]aޛlG.h{[VL朖@p'90DL &H1D`B[Fawr1dEJ{Pr-ZUsu7dx~x!l538 SrmŜe-7sPnY<6tߤa4{|wj(Z>'4'yu&NsYJLvn#H-3VoQ"w>~ҫ؀t l{t "GUۙcPaj~ߵUw_~0l\"3 y-Sy;]cBx}#5姯_lYյ7 ~ ۻTmVBVygTg mfrW yɂB)](9KDV:9U|Η R#fEQrq3{m<f dKY2f 4cFI1?cpt /8r>y kpy1 A$WL9R8V]LE?R؂ͿĖ9E>B߈!7IE^XC"h(z7广FCl]#ޔj)/Ÿ= ;sUBj/kW,_7W@a]+\:x#γ=sYw v.H^WVV--M/G=,;]bb,lΤCn)qYdЏ) &32O^(L0g9zCy; ={{E0\WQP3\b_B]uؖ nݥ!l/װlUtArݓ+_zN"5|&Dgrd 37Xܯ+mP{2og+n|YeERi Ein %H 5BR(L[0fC'lL{C>;(살[Vpで{‹}6Mϛh >nA8 9sH .$qzxj LhMh֞,yL 6(R 0)V 8́fX3UŜa X@Jz@e+: }{Cl<' >u<'kwQ`Oyg]΀i  t I5eU =@T9ЙM}9v-H^Kc'EOK zA4}EREUA.A ѨAXV@J_ eYŶ>Nw`GRF`%\kd2:BrpB֘㒢C6]ZR;A^}WJ*B@p0JcT'[Hvd*yl.bʴ{6{]&=OT# ڲ0s7i@C^UgbAQ!ePa2u0 !TEqۤlSp/^ŕ6tBdrͣCۘ-4PW*wxuf/,1:_ UV2y XM1y7)\WW<~{Acz.kk[&b0|dm\ZSXof1p]Kp|rwN6Alqwt_ g/HG'J_-1+g?Zs\`uPG[QML@ s*iQS.1kzr?9r72W59+hGC9hX=Pr}uB>(lo<ۺȼBMhebF*AjVX4o!v9lنC TbV0P j[]P><ީ _C(!jQY @S7QKd%Y6XgQ.2`$>7Ҭ%߅ yNAs pFucX.;Vk1D{$YMm}\ﬠ*_ޑ=٫Sޣ< y_)Nۯt ä ځ5؆m[œ{~ Zu!+>\P4ΪqVErpدTmF.9ܧ+9 fY_w 8Ro~#ssOg7p n9&ɯ,uoHY&27m{3 fݘUb75N^Rx̖$k'e5"d83s =EU^(=i^cP@jkWp+iNaC8Ʝ&+U{ɮMsUDyVEQvuu-|ki=鎃_qFdg:E-Ul@MZdBb"#:jII]Q!㣏Y6#p]o) q{t^ !)[ c?[dM}v2^çs;wk8?ʵ5xELA$߰bFxtIA3dg׼tǝrK5jE v*4y+v9O2%D$wNH h'S)+Uk5 k3xߧ6a{ʗ,w=C؎ikt)gՏ>DžNgf38u=9}drLӚl'=vG!,mS?@$4t}[pW0$ɱ.KWIWqĚu&t1* X\?C<BLɤ1 9,BǂUΨR 8.(vVrX?u6`)Sx]a2ή(ߺ]Z.ؠrr\&V*b{R/Δ5ɔAa #sedLHj(dȥJxзF }ZNFtV Շd#`THEa)l)d=* f(}GGpm6p[l٪x Ŧ9zA0PE*^ր/T_+!+ۼ9;\}l"d[`0DNAoL8*8  f<%7 EU`;Pf51`F}C:@y$dkhT*}ߞ6.^y )e әBQ4&Ϥ.]dQUbA1XK.;.[J ؤ ]}Tqh*f )ej`|=T}$!lH.n %zij{U>'! p^|.|*:|SbIA jۼ6Mk=mrd5jȾ*{1RWo*v+fȿBSWvG,)ISZ h/ l:GɇPF@S@9J_u Z\RAz=grTkh2 BmrK+yR=cBUT`LvI?Э]k 4FXҸ 9{c{F%i*%Ys:Qc>۪cx eM7(X{e:{ʚxcHw8>-+ nw_=;\0Jjc~iA7f9`2k8qhyb;|R̛ ,M v0}h2Y2>Zk61v[*h;:'IRaVg3 ,QA\?2}ˈe]ؽ!|XUqU8 XPi9hB5_p9ۓsa$]1L$ },=aQQ1q1|bn>,(JJ[]2UA_%ǀXХqkwbdAVIV= vp es>ŭ , "öW**j~ Uz%Eg{XUBs{Kc<(t_ ~*GezzzKFX-> 0~&x盎ñ}+u\_O^4);Ej伐?YpR8gݵmr+TzEúϊQնq.bTTi'hNOTW6drȵp$:sLp97r5~&B1BS'lk; ^+b-e;VRXjHNۍ߳VzQx9JVB8cRw 5od +cE9q9*UEU>)(9V'd8]ٗ--$o 3!etNJr/Jrsqvt=K-ΝoAxچP5ڨ2mnTi`x-V]Cu(yd/̵Kk;T@QJaA+g;Nv`-X oeJ\죭W3* _h1}Wp|gg 8Ν;wX[\@ZQc35c^F`RO&~❩{ ֺ2L,n LAOq El=e-\P 8(ct3;ꗽ|0鿑?.CƊW>qjLR=Ӟ_36; Ing|&ׇ}e[aJ}`?&+@׷+tR!Bkw:j*.HV.ەW{YJ R9ujJo`Twr御#쮉!jx6J2H,<_D|>PpE,,@WTŚ(u^*ᕮwP'`*}U@$t CgU JC<Uc,N|(}-(d 5GAcBi_9jt:u/̳(@xk^^ I/X(VVitv9 -zqN)ѬN>#lj ywS1ݨV=ڭӷu讚 9I`Trbi9s.n \>I/uK{2[2Qa̝;XV;%GC נsףKr1[mf/17Q-^O᱓ g}]U3/m '5 }STTSkފJ@BITǐf ׂfyy+vl <ӵ> iF*bpЙG@l6]v#DӈTsbr!ɵfA̱A[B(EcXߺ .8 9kF  \O霡OQ7hWܴ-6&38u<'4tsΏxj"A.*ak+<$5 5wNo+v^ŵd?OC|GG.}r {kXgζp:7w_[`v>>{F2aR;ٮ[kOtoqN@q߅Ȉ 綬TST\efc p jQE$CP%i7x=k5iV32&TsF24|`Ċ^% 6~mGkVHlg~7Ilݽ,b2[Сכ5eugT-l'нi1,8 6%3+I*_A:SD bйKG${`. M@j"@{c_AJ2®nVOaJ g{xk0VXт;3Q'5elAYZ6*0(]ᔢķRZwmVC'hw&';Bӛә][\0Pȴ3qwsg<{*;?vԅ988׻8686>).Tud蒪'ہE5> ^Ҭf<ʋLTv,ˁ$_xfvlR0Oc͓~|VcgdQГP1\~pč 붮SSNmw2e4Z=+hc֌.]|b0[ZKCcBVlʣ R3 @^C : S|,H9[.ge@y +kωle`mceK 2Xllۚ1q.K,]voP;^aa3p,SșlU>5zHRkS8sURٮUVTLa C[iW Fm;-޳rh?3,ƾIR{4Vܾo?.h>O*i˹R:B_u'UFr^bɽW[F+1=P7z)Y=N,Wi+> U,K(8;:\0Yxa78: a7,~9Z-5yh՚M%@ q!&7JUGkT,/,ÊP.0 {Pz JI_'&ή'תE2Zs-1A톺?IKoʕ}q-u&ua>j-)-'y6AP(HgU #62A=ƣ-k!ICH_IF@:Flw4[6kcJy%IUIuJ3:Q#'v3׾dFbWr!UN&`LkXZDSUU'xa!*Y)*N tbR)gmc)^OPsKڊkHOY2#*&**Sწ ȶAVU! spzv^ו3U|rȒTd߽3.:>藺8Xbc@R'XW J[" e!7T<{]wBv5]b9_}kC_sxݐrW7gAVfx.%SԩG&T`u_ [sbQb]g*(yb|F Ў5TdWC )(h=%5Ӆ>k\$GCs \0}dF0Vybsէd6vFp]kpx=*íV)2 [Z9aJ縦5vRE`e[2,6f &OX8$e)LbW]Cq+Ν}D'QhVJt{>֝-D5Tl˖ ~թaMW>.RFpUY#VPyY]֬?9~|>w{pQg7/fxV{7֕)}9l$7G r: o"`cyU\Py9wpSڊ}4<h,8'-.{9_Bܵٮ=I5Cl%'CR@Oy{`ti`&".cSqR++O[Q2BcE\%rѢPVT/E)L:Ck"tMBKMH H@;R+\B_N$Dힲ. 9;ts}#M, 6ia3*IkطxKA@XO>/8J[_s|XK\SjQj> rΣc+`NxȹAj(!q/zU <3USҽ%).lJS* HHj*`7Ikڃph}⑳\6!-g'*Լֺ6XﴻǛ;w-x٦2bSXxĤbxpf 82P J^F>*@XPL%x2ATyEKz> k00Y)݌K( lcU+Wq{yG60^檇Lƽ"#/aE"w=%+ ^#+dze煠YQ̽읬^RXr|e+XYR3G\@Z-= ׾@9[5iM1U-f%\*raf-IZANHZIF1LnE2ȡƓ=g*҉LX X[=߰!leq]0WU{nIwUQR?p| > =!Բk( {::ØtŒӝ7!M:t o*~Ρ%%ӘT7Pj >l?q{6)GVfch +~E=7Ι,,e`⥲[y\ a[ `䤎1* ТRybO'bmp +\O%f~DeMA5CeWY CxVٴKSlXYB(""cx*IY%w3\)`@,ّ+d Pj=cA9>q2(l] A 1(>QZb/d%!}ZنMfj4K6Ҫ7]+58*JZRlUBmU`ak7֮m6`DLcd>0@IJT,̴?W7.2'V0{=t7 OTʹƅ/Pu LGʌ(t'm@TjI-\"D(8!YxRMKwFh}9 ],srR{‚^W$N쇮䂼(점ib1}+ RP~a6ۆomO6t,[ňhS-ϻö.YRkf8X+fЯ{tV8uiN* Թ&"h[uX@'1TUp}0VeڙEd̛8 X_agY߳y]X2lIdy&YU]KW@M4+(#.C6h1FttҘ"L2F4&3$@܆ D{7Dkr幻޿"gPkTefd[[-be)voxfD"HzkNy#o}p ~|Z%Y2[t'VYBqCq[@͹,ffKǧ0m ˘n76K\lu zȒ &[JNNӼPə!د!%rChf`;GbÍPj8 Y-|Q\?d3tgnnƪۈ%k#i cOo~w;8F ,K>0\"CyZ,TݫsrCꨪlF֝PԲُK(=⸚$Ok/ f?&^nO[:੍د_ 8D۩eL6lPny? cM2YJ fHG@JT=jF O.flnx3dC*!#9D/9@,Ju~ }@|xA3̓׮];uh4Bg7!;VH/tzii\.lz"|l 簁 8j*ãe&…6#l‰'d]iiΏO'~WMZٖ>/gM]㾆gnQlmalAaMTmxfCڃ~nZ sp >ӻ1a F,ZrJA=~˚c"(k\ *U(^9Y)aXN-$Y*~#i|jN2HjcwE ׎$Xzjgc@l*[K67TH`CwN:\륓JD̂*/-fT6$k 4_or;v&++B'x*>b{lj W0V!,2{ 1TY+3ٽi\X׀;Ŋc-Օڴ t0|*71W$7nLPB]Pqr61:v%Īd]𤪊&vt;$"ugT_U1ڀ7/U9%l km-D&{2 PMb8}6ۿ,~|;A f숂y^͛YF U~ESd> BΎB02g &ȼdC`FÖlA}62o,#\6Γ:п2[{5| \2Pkd I+k?C4fFi%a%њS#TTe㬠l4au4:[$"cZV2 eg[WSV j%aʡXI* ?@!N$QQ˒C@[8Cl,z vϺT okǔLeY7(ܜժse55Ԫwd_= ?t{][-E8t܍pjoNajV^,VމNALr_  X͗0[y2JGhɪGv#uMxac2bFM*i"+ 4FkFXr"r<[;4Ck{|tl -WWDbnٮK kXt DclRњDh͆CXڔ&.(FFP 5l'4fb<+$VRX5dv0L05fO+Lc6Pbdˬm2)I4 ]oc2b|]R4_B9FA5j9`$xlP.&OIoBT@A7Y#s5P- KAʫKqk4i썡. CpӍI_yq{Wfpno57/|b:A|64B3eWU x5!Tˎ(U ;d5@V4t#A-X}:N0$i\$` laq#*~vY"@`ñR/ȱ`㊟E'Z7#z(sc 6RH*U x,ϠZ2ps*+8ڗ&QY6}i @jK[jнl`w|Hl`vgd ~ɹ!* Αkyy?Mp_8wʫ&kMޘ /EA zYܜä[jE9b?}G<}*b6\׶}ޑ'"[|1sӍq[q֏lApzO"u63NZ`fqV\P%7bٕw)L'~AL e#A.sf]2u3kZ on|BȀ5^5  kj5٣1b0[.VwgsۂgUr 55]giZFm+YK\Y:# -[!B>:&ʀibeG9J1Hkb "ɸf(lyf`9\ ,ߕ,dxp)0T'[n AG)yM0Ap Ik#Zx5zZ©] G}-cم[P3d^6u`L➥R1xl-0ÍM(֋mXx-эxO"v|{9NOƊT%i#g}s9L 읁E0@⺱fT>60sA{a6+kv2#XTh mn͜uBhKe~0 D۵G5;ƪ44rၠ)CL e[w\G n]x~C k,FmG;+؜8:^'U0&j*Z.9towptd&I†F1oW_,2yC>Sfx ]5wS-l5)/j1"vn[-^Z\3»f6Ƙdmx4F9nLm@[fZKl fzS+Fuf {Sawࠣ;:N.Nck2_aS42vR:+s:eJ*t^/J*.OUk]U3I,LUMgg+" _b(Ir^ǰXxl`wcլ hhl @j nQd~iuj(:,80B |5?F0F8{zr Ƿ~ԏNa/|>7#}/\(K~f"@U|:F2"L8֨4'L˱h=8몪$2޻6VP1I 493XH?4l} ⁛eڟIiag2~q1z O߃o1ld(Z+޲9LEkDGZϢɮ_j7"ӵ[Y..i(Fʄ/ h'&$¢'e'ʑ+x1<6^/܅GotlT.S6i%`cLpUw|=7nܸ}G) wЖ d֡‡@z|Țپ݈Msoz6NH>&8C/aMF]хA* أ̨pA<R<@#{z$1x7ii'tT qB3a9{^ T.|`r#|'}|:Œ2`og`r=Q|\C'j4Q6gJIpF3%*&Wg_XC9-ΑZdV~Fg%a10TSxֿt6E$aqKn<5:׺qhSāGeRYF9pJ /p@iF5&3ƁEfVPC`Uwk6Z08ݺv~ 窵|]is"{oS E/3p!ښQ7"=L#*?5AJAL&wBۄE с{+s| ,'SaK 7lWWAĦ>1 -|Ă-ȹ_$E?Ier -TZ`(f螢 1U"tfUd-柙QdRYjiV1Fl̓zkP7Wp^CU0zo.kF8etbe8҆je' aY[`.;TTy҈H/<Xe7̣1M ˮ*B +f/C;Urg%|N T|{ʺ5@=43Cs)ȡtu,fR\~k]>v3>c^½So ?kI1-0L0>lApxb_ʉY\X.L[yІSv^)ބ7& Hdž%G>2e++076a7&҄WM!`xQVudq'˳jo8Sý)1 NEmⱒS?ƍ#Dd8j&?I`/ݤ6B@l~Det3ci%@e^6W^;dA˹%/Jy 277 D]j'MDdwʺfP$Rj3waeՃb^!x躅!MHc,k,qcwN 8n4T2^%,Vs7Vl {zHjrNLh KiId]kku' 4g ^݃y"26ڍW8>F}-ùdܐ5Z7:duOLՔIs-*;+B2 rtrZ;]x hrWIrJu<)X59MT4vah)Յ%:x2l( %C LR0t2BE ߽.+/ϾYp6KR^U'z > >o߅5I^ztpn{83ퟷ( ( <2q:TIZ$˴Mz /Tק5-c?r-!ڛI{`7+Fpvf*wC5 Y,Mm'[Q@w+?8^Lkz9wmyzvJJڨ! K%3JƒCz(Qm:H(?g) wV ]y_ Y꒧GbZ'#f6(rpq꩏L6 lߙ%] ]T|x+i?`{gڭCRC-0M>;F1 i;[By6O0hX \:;f4)gp?q9*h>_pCywk  QQŚX))doS`cʖ!(SUZVtCp9[sņ1Za:j(J6cv\5Qp֌M;![pN;Ei FXSfp:%G#^f)`hHmnDF2kڧaRiM J.19mčQhP_nE4Dm lؑ$QCD!668N(]*i6:[ۦھCY5 * i7o•^|3xwFZ;M r->.}f?+nԲuK ~5Av <L9πefHH*EYcf`ftI(%PU!j?[н (7=cC,!Im"Zd7P&E˲},pS;~A CjE0e&~6c㕞b^~5×ycFma̤_Z!0:"mL,~y~VS; [8jz xL>9!e.@wy~*9:]N +wREVfS=R&xb2gVB좃cR#X~) %$3Wf p1lbDwl5> ^x6#x;IyBc]{iBEƁĵ  $\CWmoi>枂}E|_O\k_ƑS@,k(!ps]*$I=\;TҒ%nf0}|TQy);[mGn͹"@ J.O9-B&BSnVC8 So]z~9ւI%k~6Ώzݿlx7 6bȶ_|iSw` ,nZhM?pMbR D\]M/OPm6T fm~[Kixo*b(fO(ڢ!XzkQZzTE.'J3E*%f::YT<=a`Mc]NhvVXי@|њ=V6-ZBLɩc2r(7dM2PRC*EBNkS&9X sLp*~:,gQ6+ fF"_.jjdCG I7L5嗂Tn_LKG,:SFp D%u\~ $V)enaZ/`J߮ZY8pT]^-)vnrn{4ӟE׵H6z,eb̡.HS `.- VUArx:!$\f;8kXT?AllڜQ <ny`S^;Ƒ6HJ5+dp RxkMByH''9is1?w"t4QGl2D^w#pxsKxi1}ؔ!ӊvLrd ob0Lbń`0xz;8Νل+aksPDULF h!r?3l9}5;}7pÀy^ڠZoH6 k<HG`mvJ NM[9Ƕfhe9% )H-ԲhRQMcM4XSի0;ޙ${Bۺ51l̃uWE,! ja#i4! EG-@dѮjV\ V)sAm_R'R.$e7#G#3ƻLs͋"0NYC\Pѕ\n]7b .pJ0QfA?[oX׆j9ZT Hf;-4r+x{+ˇpw¹=hOid^O0gG~yZ++aIXNh-\6_ XfQD@δfo j{L흤5) Er:6]/R:o kj\턔9 +8:"ˬz/sazsw8% X-6ځBx4psh g0`XcqJOj-5!"dþsKԮ.԰3}~.r}ק]k/ +A %x_8WN\g6 g˹.$&t rt13<}%ݟȰsnl!U:Vfqg8nE3exQ ? 3b*9KfFJ\) ضfЅ5R]orT0)I4`՛s8vrF~}{d~spa[b^gT)z6+s 27\l|sCX៿U?oo41<[Aox 2Ps , 6+ lsc >\348 3e4;aL(e>otHtr>ʢ# =`…X f.ث6ɳz4cZJ7.hPԓg.;7ևPPj a/Ro_k<,a2!ӖaX%b`!o&7kSp,F>. UsSj $ߖkfF1Cp`*%PŅ]#@`{*˭Jdm2PFzF}dpQJ nV@ PY> ay\u.vO^UhP]R`FlDCYµh9>U~Iί0'怃^_\*GYkScl ЀZ}T˂,[lj]GaU z͙LS5i h:[CA=dRPJaJF>#ƱnH^ȵpxy#6;QT&HfSUZp؅595}xgބGI$[!Mf.\R-,\[z\:Q}0;,8_5Q;( Y ^b(&W3ԨB v ^L̨]6߈8[ֺ0Nߖ,\ãHy0\mX7_KI_U2daff#Iw;‹A?[h&ho"n˕~Y'{/xa g`_p|=*}$HlB*[ YeO|+cMY3=l6lDMT=De 4BBL{IaQwg)' Us"ܼu3L\wLVxg.,lpW ~x|ށQ_޹}G/`o/x .MM+~we[*B6U3؂^v FVj-J Ni\'_Z]s%1eV'Jړ%f6׶ŵֶ~#M5 cVWbʵ,؎78uaw&#oo%#qz~ޜÍ{ 8' Mxb -cO5<#ۑw8[\(/ iuF.(ȼ[|Q|K{&2%_{!-, weyE7g n]dM'>~ W?!Rы{A2GK-` E߈[}&3A/7x[b~N.ucwtO$(;?;nٞL.~LiƕHܲm&",C< b,X?TUz,,0:O.ᄌ_}L6j> J$&ZW 3sH;O0C|֭qs_`@ $2>J6 3JsJ,Hʁ <8"U~kݝ!/ f4 W0ϛ$mC7=z, p0vpviQlT}a7s dS30ܘz+7ԋ;xjY5<I*Y Ql_S /=VTcI^.old~J(݊?kTUj H'8>TQ(c]&kbnUf|e6 )&[ p9TѪqpMr}XP7gs9dkH0feOSޓ@$5$ʆ\Qyד@$6.;fMUA3 XǸ4+jߧf1j᥍0b@:׬ uK%6Ii=ؚNz}iL>'.3@ nD$PC<>RD_/&ڂQR`<.pt90yqΟۤdOw:Z/٤!ЁFkact{&ĥnF**eF(N7p,ppEs e7њ'F4J|<7#T#v_l6a֫hG6Nl2VuҼm-Zx^-l݄ ꑌ$]C/d+E #m,kR3{F@ԉ֌JrS~Ȣ\6\ܥ!<`c*RD$pBk7NQ -[E[^1sXx%Xa1|Yx={aKd-y 5 B,F%6 &-\_/߃iuQa[c{kqc|?>ُw\؅3vs:YqLGh%]QFL{ZrZAͬ9 mt.U (9@$ȸ5ft]@ldQ(Pf!ik}X&% =gL9ll`vS=pgd( k[|l/ldCAm*VI4$ =u'InO΢DVOv kdknڦJ,/9G&PU"9h:kqlJOV"q\9t\;i*Ոl 9m9 Ͻk v 8֯9}ǟ>;;=ӯ >ܺ_VGyzmy%b{,rY#ڒ(V-5W%4M `y6B;&.zG^nDbI!;/ Ϫ63nHAdP ۲?+= `WLuMĀ[>b]2t/_q_,tf >&elRojJ([ʽFUyFgd_]SDҬ zNbSĖ2L9+pwu| ~e~36|1Gq=s{+$̌|ሇց|3]~|?4 9GK٣+S,gu¦ĪΛJiGus +;% 5m-}pbc bkLcͅ * #}qš=rN#QZܩY90ȍAI < GG<&5A[9s)aMYEʳ[}NByűbE \8UY60 .Y@lF⹡Z!*5ʸmKeSyzт@h褛]Т枪q ~ [ܫ}%DȖ)ĤWxgBes*pԆ,= fk x[-잢m {\Ùypke*,uY!9lY~#j{/XM06h81)A  Ξ+kTi QPAgSˤ|n敞m`bky99hQnO0(ZZml1Lˢn:Kckq6o6g3Lcv75?@,XVk3b% BeFbgHk ZVFbmYDŽ%.KfՌ<>ψD'̖|T۰$6^,&<ٗhrVT9=TE69۬Z\ F1Wtʞ!o 2ۅQ଀N i2pViPdVe k C 7͈gR05,[c"ee1렓WH1/ I^$@@`9Er^NS <|r4ӵ?8RO]g20ʴ3RΓqjk.'߁lΎW-|@!au%K6߿F鸁02(I;هʟ0 dg}sUY3t-֠i@xm~s-|=\܈XT.V2dx |<\lꟅ+;wn4pe{i?SY3l`A=d¶?hRؑ?{$'Vﲬ^8n(}M^M0t5[:džUE Ƞ\jL(ҌGMJ' #?+𵗏@j3y($dy,@YHUѹFj)mDB=,{M%ASynL2,%m]*CX*Po8%StXaԓN 0;-aj%x .a[),h FAk0ЋǬ(xZ#͌*sϸJ7e:@ܟo~wa1P> )vp^xO?GMB,/KG.?Pml4*F) A7 ڮubJ#gA'+X1HLMwȲ'F}ђ> KD#[c7~`p-|yy@i9ZY~P~<3(~OTe7m[:ݮ]꒬ c)Pq9"kyӌ,9C{K%T S jj\fϨ}xQ.c;3|X#|S.{ޱ \؁&dT:OeyK |CgS' ?Żanh58W '9{ebƄr42%v+f,D0gq>"N;prE4-w9/JHfg Y Up (6B#[6 MCP",^c6$+kM O vN`7 -,o{f,6 ٬FU.C}Aԋ8HVAbTC^; *^Us|)>/,<vUILb% mQSe/u3tIYH "t󈑊 RAnmgFUgDr4[bE]Wo/K+XwZj6/EɃ|bP jLK(ՙ\3FQ*<RHy+cu]"knbKlM@0_-5bN@Mv駙18sw8"xێ% \rN(XȥZDx\ilx)A2Aήv^0#0QP$ǥڃ5M]\_l򐦗[yڛ\|)6c6PZк0BE<@I@ TnDvzUcF<E,m/~}gcx|k,{a"ݫ_6?vp1lahDԅ41{"TNYv LE]h5lƯ:V'DɹЌ%KY !i(H7TwhkoQ+q6f$bJ>*$l1#szn0I㍀2[f%@H.aZlEO€-Jd9TW@!8`jf]W4IE6ZgTt_Mhuº%qnL`rߟ\pw'p>p=(uzd&Uʣ1fTE7GG5up%Lw;%3chvy2{Blq]HI ݏJRȡ漈#͏ 6`@i%jj7HA"Jsݳ'9БAjʴl<oPLT*ٮdZMYFc ׵F,1DG~ܙx^zzu7ữF}1=p@440?k0UdJU0͊Sf˩Ղ \|wpbueqo|v͸!YAUED/)t"ǖ噣FPk;FI4TNQV}kO SOJ>$6{5W*$&V0h@6EXe_ Lnr[ ,>;> J=' '6 BiE\Tµ9e }XM S?oUR}S5>6_öoڬx`e!LTBC:%ņKkey춌F05Ȓ$mEϹ@Ԡ)AsgULmچUJfyP֨E-!d%6 |-KFAH>k=A%aM:'ZNrqBS@#0R$R4U!!֙);Uux /Eɒ-ܢ*!UEfX~vΖLR {cF?6h A!)$QiLl{KqY؉j) c[óCQa6œMo NyLVeƼ= rkVp7 W;ͅڠ J*e}_k]( eIy6-"ɇ-.9pMy ]p-q 7x/V,aМ/D#%ߦxJj5)bmZ"9Ńk7DwS[+bSJ˒9D~+n@?J5l\㙭ʸѵ3FF J 1㎚xat}X pkIj(`1dFNG!ٯq_) .(KnDZ23O .=$oa9&8L KK܄-q&{VcGiEND! Z'epI5MMm8* ߄~=+QJҌlO:xX4fgQ"5d`-g+u bJ$)MkZr¾{;WH*WF-t(SyLyEYIY9HJ"W0'w- Is;xcc_v .eeVA2~*)ըtbL 1˴"Q!?T*x)J{)ƈW8jA>ˤ>B 2g>Q)>}/+%H#lV-E5ih{ݣo&p V4yhC?>$ {Uj#y#7K-0^sLs,RaHt' Hp@6ʤ.U@BfgX1HJ)aTq`)e~e;7[pNi>gOM 6&+hIyPΊ,scfLM"5y}Ư=gW_|.K}>`ʄ}39^c8C-R2a'\d3Xm؂3ݮ3T8|S8w Լ(={Ƴ{_V/윅 C\'63eC!)~0%!Z - y_|o~pxTu*9x?v [XWngߒAaKtwʀ8 稃C,j3s)ΞMeڔI GWɢF%bp Vs.U_XNә}-x#s[~'K #Ƣڊ8sɟ\=#~zBuM,Į+< `F<ݓCslo]7ܻ,?Z^=&WGq h rBDPFm2aD4Q+ʕ?̓DJ,@ʄQŋp#YV *b:΂ɽۨ՟56i8!T

x%ۿuV;Ovg~N5]cY* WC''k4rRx(z_oVNqY#\?i^P7*1[OzԚHq+`X5+ffDɅP2&Jm*(QmCpƂE)I" >+3\@&,bu.§^NQ2Ikan *j0d(vo^Wά3ɭVU'2~ZiFOq 9Y\}l(%+ &(k| S~90?>Ebd}kvƷ5cu:!,ȧll+8WESb;

0* M&+5fQ-dQ 657pѫ6f T8l x lN3{,7BVG#`Μ%q EsmN&d2i YÆxҐHE2uXpMpfJA:~| [@N'7<Ж̃<&06Q1{Wtdv/Bh.jT/X)˪ lDbKl|ZDWEm9P _p]7ˎTQlet. 5fQ6ϒވ\+I!|(M&Vl%ji4sS͐N2c-( tnI]TG*T}ac4ƺ)z# "з磊( eΕgK(|-OSTMh@R\ kDS7H㥚aLT@_|uⳗ c{7 ßلأ۶=W+T1`7H &Y*jTX`M2jVRd@X 2H =t˿Z1 y629Z7ɵ{|C0S{%t@0EdI%~䙥|;,t_ \=.LP!Hܧ\A"9>H3+10q@4N\͎v.ÅI!״l2}:e)g.*;>SRTWִРe:~ ~ s)LQ@c[.^cKKO7Z1x:©4%fz'o0KO{~S#՗\w;v2g[@f{W[Yw/ )mΓUok%fR[mlNk:[0asY7;y80mhJ0ܞU2B_KQ|axlB͹)Ip 'RxЫ=w!*<Ԟˎ{ *C)6x5pb 6} *Nt sWRz.fYIæ6= hj-t,\/ (;axP5^Mm誨gcpЊJkkht=Oˁah9`]+Q3i. VYF0d՛5Zu|ʕ%vBꨎM-ҋZQ 23lx#5 *.f 8F@`֘kDrdi0par r8 Jh7 QӎT#V*c`|i^ENVP}][LW$!YlTŖ)rvdAc0U7’W&g sVlZ5{?Є= 0lIe= _phe E?-:DUvRi\Q62uy©t"͏xĹHѢdhh/[%U*('WENNU0!&bJWn2Q([8&U"\1:F%zK*|1IKM4+ S@Sv$SyH1p B! x2Hɪ }v<~G7 @yն3JIq6?9Nت]A`"_;]QdZF#dPgsw"Y&wmy 3U()jbFAu#doWV60JUC,u~CX[?[dxgy|W~yVѺ 60 XiɮH'$~z*$EGAZP1EK=ݗٕcK&;؞i3*k^CٵJ嬝;H~[ըιʾ- r0˟0=RzaԿO{\ݗ ϗܓWC|4[Rq5N;i+ :cp7jݧ)1Sk+bz{ZeyQGH'o5^bf{( 3ϥ1.QGj>3KbfE0!?w`&FNY[$ɻb E-iya7ka~=n8p#H3,\LѪj%0cQU`xy8w[#xghaHWafdPh?&$%KjE+:+}OK WGGb}V ,dUBMkaXX5Hn J!eyPf)t݈ED lˢ=+R;w4ުAhL!?)[Qe4Re(kcBzB T4⎚rM/Iפgճ/b:'VOUBZG;e*!5Bث-4ArmTdH&Vz` n:5FCVt`|*UMdYKyQގ6FPT,YM&YpZ@nE6k|Fc>9p]#2il۩ 29ɽcs@RTpZ>jgfDll@se&Z/VOF"|jf;ZDg,v&PfskkS| =[<ش VV*ت$>s]e+bIy]~I*'ߑcg싲+Q{ކBLՔ9+;^=Hyh03M#Gq0s6\KC8r   VaJ^Pksp crB m?L=قI"sw5Σh61=6)v:>81wًxdžD0п,tP@ְȎdwp ۡo`G<}ڵ5׫ _Kz@4QAjYl vݛ3ؓ)FPxLhGg 54oxJ yO>[w%CO_%o-k(Pu 76Y0H/=Rë\8˞ >Pqt0iu;ŋL`p}{A!Wfۂ{ݤ׫33>3H{M̤qק؃{~.WwT%d.<W%V3k"2@KWE$6|,hk86FWH ԧ1؃lu#@v=w||WëN_E5ge(j )$ޙҶ_p:Q"2ٲ4)&kM&ʦ~I0|Ctpc]MUQ6Cҭ=ćۻ E\zUDtg5<Ϣ@?H)0Vjh勛:f\y$ڕ]x&]FL슟>aJ)uڲRfۅ͑+ *5P=<N`=3uWOTmkJ ڰ]{`ߤ= \Y#X ?H\7UۨsTeOLp@ݤzʑmpalIFrG_nn<#xYḷ #(o&=}ʺ-BJZmu~TtbHyrh+¡ [1\>`ePиr樞Y _cl8mw;rF¹p؏)ܞoycϧcxNܵtqo$x8}lDӭw"|{VYmcY9oNYZq#8C#͓8H  Vո&@YP S$Qj{M,|k/OH!0mf*OC`NZ!9c@d3ê7wU+{Q-HAR0Hh%6z S +`S39|P\o_KsQ3b`@~Tt5 &o n彼wq}pFKgns;pvG׬#5d{=SݙINmïI/]wGxn]ȡ%VlkyM5ѬږY %aB@".x"WUBRbF*-3f9+$, Y9&xP +q˦TJyKcU8ԅ V.1,('lý5s{G4f$\j(!\lbQr2\EԦM(%oN4W)fW) \}ٛ'N$_0-Qhދ* \H#r}/y ^ maed9~ۏ;!jHc RST]qH.a_ IW8x .||ޛs~?]m_6<ܼ#nr7P0{E܏_ĥaW^$PO s [ZurUdA-b"YLk[L2i\Yj"lE 3KT|J kݺ7=zny%ku+w v-X jHt@FoQ+(2(4ofμ1\#$ug< Y?mN,`։82,*)7_;^2l7sv؎Џzyͳ$Bjg#_}G0?_@ŋm "`/s&A:xlgb&CU=2յ bsm0{~⊋p8.f W p`ypv }h(--ȴ-׳2]  _t6ڲp,,?'\VF [isq>)%\4/e Ci {=I?:Jh0.2 9 i ڈ]sLg,)}ZzTI BU:)NflV=qP0r-s7G@oiLj*-\ ДllQ4"I!+3I؊LCɆ-5Z1A&&@ǙJ5Q72o)Dj+/t'*$6]+mZ< .m.ؚK#;Ix-J.b塙ZHjbMLXy5tBBZ u^FAsw=0 ErSYߜq֔9 9L2A{aE ^h 9xܫqR%s¢j6P{W)xa/c(g9]H5yau{W4:T]]Ԏx9=y}wbe[=ˏ1"hu%< B"/V,FJٞvTL_g{5kp IkdQ{vx~'(ʪ_Y} n^,: A _ȃ R]spdW[-h1(8ؙ2zs{ݎK,d%^ޯ\gNr^5$[ckaTచD, pA Q@=P@EZ e'PύD[6br5=zc9Qʥ|_z⯓**Uh1.RˠB4uUJ$yv(kG츰 Xzq:!"5FRP  OV5A8d9SJ\ܤ"Vq|f\NH*goÿw`8~ʹ[pnwdԝOFJ`{L |W :ܭ; ג<龶O[6V\7 ?_Hd eNg~)&s ic5ѢX5_JC- I[@VnS( "v\$L/"ɁţbNPMTS ǵ4YzWqAgK< 3+ʽ 3nݤ1|^>=[="2.[jCWR#Joׅ+" 8S'Re>1fJΫCE xkRSgqNP ¥* ]}x![A>nl 0<QWIYFC+ei˩p^fMG}Nߓl\{?2·~=pb䛨S ?;!29kDj$k/jȈlIufII]o]FQjF-ޘ*8W_k+-3+EZf&@123aPD:PHLXt^ڣC#xכ7ġ[]Mߍy!?<>˻эktȰ;[LUqzK7rBwSoϥx]f{NMEDNSE)s!T䅀exAu VurW܂ɪʹs.xm?Ww=zꡮpyPգOB h6ןÃLWn+ߞS O O[D 6M>5ڈ5` .R8Wp zïX{@ >$fow.'3][k| u뛳 YJcW{Z7@ntd*\-=C]R_T]J #n-5-kF2 xX%i@;j;fXXJBmXSOyT؛&$ TX(/2n|z)/-pM@(j ͉EY&=+j%M kK)^1++wittʅ#/Ml#eD^ ͙UH{;p 'v_7wx+i h79FL$Vr2n N /à{m E7.E@1hڄSZ37$'(Jb[&j zU~HՆG\_IW1kdTb l*/w1xKz 0 4-Pɳf: áC~si/*XtKE+TUMC4Vm09-0ڷ Q@|@p+ QttBg_q{ALc ؛Fjd(ta[ ݍр䭩??g/whZ8# a~/pNKA|9 B` jaF`ֱD P=]ނYk'YK޻47[ʍ^u%RFpeunl@s4' 5z6{;Zf//wIx$m3Fzc]O]؃z9r[pswݧG8Q3悖A@*m"PjK -LL+hJ6rNNX;ӖzC T$@Cs?7IU4e{-WX :Urh%E`(`צ`V[B}GgXo=Zk }ÉJo!ɳN 6Q֗{Xk}-;6UdgzRJFȾI5,TW]w5KbuSUA)A2U-U!^ {>,B xsWكn ~7e[|C0 n2zpLq @`Y}Zܠ*}_6pfL;#DO!pT7668iၣR4'N$eO˹>Xrʫ˖;V%d6e.Jls=;|Ȫ;3X3,Dn5~*V?R9ݍ-J XdE<`Ϟѻ pe;UjvN?R.5xKY*'YxB՛s3=(wOG)|sAI}^#w/܅ z>pţ](虢Z'0>~OhĚ>eIQR1DBCMk]ikS;;,5Jc j v:?*6!DUNMqT#ET^lU&O%xgmW[m ֧?'W16ܠdfDmU 1Cۖ$ mz}Z]UdmTT~pyBLzKVߒY$aDeͯ*WfmS5UYքlzzSH͆-J8r1NB*K`!Wm܊FV h$X e/-qs9'-uXmtO@W#Ht5D=S&aٔI#CXY?EK\F4q] $=Z)Yg۠kOӻXiQV#5Ӕ~פiMxmir*en6jV4Pl_`K9Ć@^1 <:Ǐ 3{T=SSZy1j[:,71Ɋn3e*SEEIl`6uR+a)3n16ojQ!$VgLFZYHsIT]=%]TtbV"7;H:Uv*6kvZhc6:\)"ʰbcʧ$~:1.c͋绋Xvՠ#fDl&s`( 7<SQl~ZD蝨9kHDN#`@c/69NZ_.Tzj`{+AAeAOX,+{IE220uc]DٖUn6W^ k9υ`dצ\)*jWϻ͟zmK6'YlBek,ʹsYD҈WT-INjS)1+å6ȺEjlp``y}S jtF KsrV<XGyꊵ g֗puDk}B^epÃ4) A JTs{ "p8džlu Y, ܵ/_݂/^^ށg.E -t9~JWЂe=)" Ya{ja}{bAR>F .\;ݍkZa.E H|sW0G4UxW5v15scrt R=Z&-:Y@RjO%N_ł;Z` G^vf  Щ(f~!7B4*U%@H?ɠʸ y Yd׹YHZ|"e'936:34 y(',>,1CXNbKQUVŗٻpy}s#سPn?}VXm{yl3M))gm&=XmݎtF@INI0BdUG ~[nݴW՗+z0}֯*]A'P䧸2F2Eo]I}X. HJ\~r]A][ͪ@-Xd&dqt\z)}̲Mѻݱ<3&2kD 8B¢RlxK@b>v>y ƥ\' D* 1*I[qllB~@FfQgT؄MIbQ>l%#%j50; l6M(ksaT ɮs+R,,'BH{5Is.}Rzյ`4 ʵj cJ>lBJj3(AZH0 'fZHs@#dXFB`+1HVO @z9& LL/LK.JQ$>{9۰I6'FZiemYM<|u?´j }$ WPØO֨c jG*6{^6ޚR'8~RakG1JbA ҠX93¿諾k攃 xJ>ʦNw=O鱅g8&gF|SO+IKX`ް2'Wf3{{(,\(onKDHbf^  f?i*Z*&2k*EO|ID6^]Hzyh@7Vol-(%ewaA5t++SRncssÙc l5Mܭ\3GX5ٱ8r5W1wu ەQ#2 Il@5AC< ܌Թ5Æ3lܜ`?sZjk|ۥޖqsiN=d_d,H4guHak}FUsN&1U"G[ ,1ґk fAcyݚtkXs3x[/\ GhtqӍ%r  Y^͹ރ+t+9IpTw/3 q?kۥ5{8}/'K?p;3ggpd> X_N(TH/ "3)8΅$Osj+bgl֤n_EBf{v"N^Zb9:/Vl-)<£vzbo-O߅z.^xL,ْLYX^;X]] `0prRThVZ6*h1V?&y.Љ1]ʥjz2dffx 6BizrEݰROmJ58}/L|/0wިZؔ"'!W6¹۬rkjMNm b'OA-˿(EKY%3b|{bG,qC ׭= $(Ҧ6-H=h63ֈAP -L5/SOFij6M! :1cpj  2$fq7c;QAK/yث9ͨ-/E Ego8\ywq)Vv$VO/de! N=KrPmKLQPr~wl'o>~0±1B<;zS 8 X0)HL@ 3,zF'cN;,R)}VeVA@=I-H:%uexiiD6DݳETSCAyʙDiI)`6Vpn8G;{e @yʉݩxW3~S]Qn0H#F:BE48Y̦ߍ55L3ΝQّl`:yƴj})@% D#>$j سqQ!qEE8C(l2owwi{jSOQ|T4!&9yl^u]!ܰJIz@lA^)ȖmlW݄Pq4 0."( i޷3xjvUVgS_m̆?)y|}O9>{F#8ݍ3O5XYϰ>rp`C@SAmÕ Go? 8}"C8اS+?{-)|99 ]p{VVgf2 A j8:~9HjkQp_GJ!R ,*cMJvIb̮/ ,ZLJ[CPT@߭qhaIBfP ϋ,rwSKzLeYʾidT{uc5 >v7}N7׽Rk, hԢQbE"Тz^!j,eO}O> ~B<5 A#9.W@+-y3F)sRͭN$IrOM&ș>'6pJ -Sk5m5,wv 둃]OQf3&̷lm\b-I{:Jן<3;2:5{Gz ln_y&l6sjځqKê;u g=[@X"+pqSz1.| !#oA0b7@^>ٽ h6UsUcݘ׋@B d}n}orXMv%"m>Kb?^{^=`*]+"y˶sP}MqL<i?Fp86PHكekJSz},Dycfp>sl /^±y^zM[3.ѓO{v/Ї>+Wڒ\A+IS'Tr,o}}:40JݵP,nVnQdSJ-N %ƫs_S0HUi:kwc6\ @GNʡR2CBMNc~N漜6f<JEX xp ;JuI6s6&MYhȱ|5QeqB"E"e6 -Q96VYAe00z@U|ʁnP~Z)'L˸BV@ iH(vqbF5Fqqx/iAgO@ UqP|u!jRB0$MtU9,5%Ԭ.UoS#knDGsh@ͻ $$ɐSƸ/A@>'xv=76a^r&{tPDa(7L1:6[]պԔ>M9`~aR8bܧUp)iRT%%D J'1R|KNoe^,&G(e I&@rua"P/W$%ŏKqa5ݮz@ncYYXjgAT5z͟}<^ W~K +% \/=^d|SUȑ-G ߕ}hK4vvoN(i{|SbY!e<{%a@ !@X;Q\Y;F8ZN{uNL`H69WVЎfC^Cm/ޣ M$Y!G3pME<KKTKP6f1ҊA(LM5S+ CGj&lS+j2v|Ǝdy&NTR: W1zL ^:ym. dFu_6W@ @CP9[&C[@Ê7GDH n^E[gO7ާgXus8 v] \NT<:לMj̙pu8ylЍD Aj3|}a[Z?薠On—?fh^pd_.fnexd;ؽ>IP1,;9kUvG&(h܁3TiEl8GɕGփ^aKQIm}'·h͈%F~/*,=(/~d@+8Iݼ$ITY!x#E۔8ޢ݁Iz;0>{#\𪃁h2(LGI&]i7~|YanУyTmWq 5p όS8Hu[s|M3O#P9jrJ5"1L&|CM]㶛Q$X7PjKvMmg{ʚ Da{o֪U<ias O\7_ f-kTAY\*(=W5*Y+Z(hns8ߥz/6qK8"SUPuGN6䒬@^@8E2!;6e]d]'K$,N ?~2<~#OGpeu(e"YLBaS-LFYkr,9TIϩ5,U:eie'*R|+7L~9 =>0/# j,Ha1Y,f7 Aŋ'FvEgD<]Vi$|?TdFёX͍O7[7ߝ+eXp=渫#[(ZRLJR!*˥f2:?";&`$s*c6g0ր̲clPj˒%G}Agn)ځ;^\In!n~blܗhrE)D1ʼIl!3^FloW8o61 PUlnhxf|{7/^cCx+jsyrJsU^J G,rY:Is";$JEPBJZ@#UjFdٱ: DSMr6,Y,*%`3zHJ*(bu̇|zg~Nw,QG20Y H*`umbkȠs4U 8 ,7;TVV;i=`>\ flʽAKC$ٚp4RV'X@H v'3Lm3Xkvl8%'d5aٴֶELMϱ˱T;6 خyz#5?7~4<|EnT$o)#[ŃC/%Ax#駈kȺ[Nec,0 u>(T |$c =|싑[ o fsT=0o9h.tGly| 9b*fmzޯ>=smɛ5ڃ* J- * Py 4l6A=qQ3MZ# L7=x\''ws 5Qy̲A_Cekwe nܡ2iˮ 5<0)mC/2(;#^hgj7ufƗ2ÀAw/r?- ph:7RQr[x` <51+Xgzr1盏/uO`󠇟0f{:} fIfsa)в*[%O]{)psnZyV?YU%LK&j/d}zTox}xh PJ8T>phs]/ ڵk۷oפH@U<;? ^:tT_\y=gfPm;c 68I3q/>8+܃k-iTyo78`eIS0=e{Lw'[/4jlbxZblAgrtwX~-/EWNYnb7íO< B TKéQGwשj/[QsiĪHЃtC`*fQH#8ь$;-K:n<jN >nd U,v=͵^(UfexV*v94Ɵ+!!Ex[k gnK Pllq^[‹X8#q.E\Ԃ~<8YeP;L4Ҹ ՌMXQaMSAlezj^3ʽlTb a49hw%@l mĆ$?J*Tژ 3,k#ճm|"]+vLIgN@DcRI-؈g4o^M$h 41bm$Hm޲cL"845Y)g{v4@ *}P PP АMbɥy؇>AAOo|甫N!t9H<Z>( ^e4%9 \S u?ygVI3d<{ VakYHv477 Զ/UA'Qj gk=jy&&Z4s9N1EqcjY9yVс $Vo&c^&sv0Z\ U5$Zks4f[-c!SS ʞ}4iKM@24n@9RRj6Se-X@u}V׷0!6P0>=~ݽݘnω$tfcJCr6q{h5<Z5" ~Nc-K;Gk$t( UY#n[ͺym0ʧ)jmmkx} }[eK;%GDL!yG\p q:z=~on{z@[5^mCxcFpp Lp`ąp!b E#k*R'R&d.-!%oC|xy8w~f '߼ xn'kSί9a-=U ȹl a%` qO0P.Ql"8G˪I)[/>[-=ԜϖA@eeJΘTf1-TUD,5D̹V[ v,%R21!pz݃/2'l`28X9E΂ԡ /IlAhr"{:_q&@r ã,MU: K?Pw"(>WM'чZ+eINcs2m3ߺ6j/^jᏟvsf5Ga}Ąf]^SKX7:_ʗc~N|vR,\zaE$+J4:x4b}agᡇ'=/^a̾p`wH(9:ٮGYoAF6ɦnujRoJЊ jjmXŧ_jix2 BK/Ӕ|˖<+0vKcc,u{br!=uNY߿=>קv:79&*1?7ҞNʚ>P(B!eZrS̒h]=a1PR:~kAΏ tUM&]M ϟë1r{ 5p֍G_?_?p'f Q Rez0ŔkvO&Y 9/&/Z>O s=NN٧7_S$ .sp6dёElv+6 9/]?70MkD{q=kZ+#IWyn i^oQpX*\ 5cL!QsN,ylJ̔ )[\¼* z12𾩍x\u_jnhFaa*L񕢅ǖ /$4q@LC[ j;Bʆ17r$Z-ɨ1. 0%/W6f&,6hf'D^m**.N6Z86P=lhM^žJR댍VKূA2{zQ;^R*s^x'y~f2"jLUTdiXQƖ\l;+s**b;@U[bk}xeUDt'6k**^*)=΋/NprB%JO\4Cey\r,m! v{T= O%o+eOfj>{-n1,k3s0?T(e4ck.K{GeNZx7&1G1!2e9GUlɕBE廓X5YR;l_Lvî}nɢ$Vu-{Dڵ䍉nwobDClݣ+{:,[{͒SJÌB1TZv`EJz6usC`N8H98jO/V[b"Am#YYP̖LP >߻UH֌cAEN2:[Pi6v}AWYΗFP:taXS<:S51dyBrcYQ;KϠ:g6H5[wUy59h@ 4RUtnX]C7Ԃs4KrE3&뫪+' \ G$)U~%;i~]ɔh;P eRc9fnǦl_m@K4WXOYI*ElxQySf?yesK[>5LEb&hP.[\⪖lKfࠑ ^,ͼyV2@[kdP 5g r )x(oA@ьINj6EJIQ=hF5gL5vF`~3߻=t~YB9OJ+XQ#t|G p"m(l ;L7|O7_6UarЋ7 ֦aS9L "%J^w<7N\h}z\K76eؽl ^*@3щ(._X}"K'6\@f<_ޓ|A2ewm V>BU[O9ORjPVmFյo-zb L=|SPwj?i։Y%,\ǒ/'N9X,ę <E Ùss43tPxxn$BYnxRv6~jQMWH_CBD;eZ3ؤ֯/8{AH׏_ szx20/o~I6(n1)U5RS%U?~uWm!+ 5'tX7ڷIkkvrP (zR.`Rv[gYP%a3n8m>~lRH%@R>ZO*|/Cpxy>qTmOv隭+JP}cc1 )5DvɃfANMݡ!yi(oQ|㱡[ƟS~Jpqz r^/jٙgk ?=ի^x~B[C ]G =\ }χà A[i4iو2sU?)~y.E8|]t(F[ H:1lՍEMMR)<3R7WajSlo]j%!}+^eB&O=mYhЄٵT8*E?, Nj!{D0(gg,U`yH0BgF 8T4;fgZ7aaivcbN 6vۥXItAEb-YAæ01g!MVNM_ ΂60!'m:)a O˰XTd ꛝ#Q-[q9x.Aa6R,+_bz/B4%& W?p кf.10U:F\-Q3;Iޏi\oVUv} L, 5kDi]g)QMuST9dBLq *py$`T)\ɤtLU%U"TU`Ƴ)tU6KQQkم_ӛ} t^xe\?#xpdL &oM1S+s -\ :e5CAiɚFtMEQAro I-'1v#Qj*㳀b6(K<dɖ5)zh}v󬃿u؉ӤAcIT=NcY6qWHu@b(^ 9Dzw'b+*m@ gNztxo{4oMs- >`x]5m}*}~>qnJ* {@RñҎ&_GG]3u*\#nRB_0Vu̧"wsԢW5,ǖ% ޸w'GzDcB[r;@rFmYߍ I(GaT!0k*̔m0XPUJ W7>C'2a}w~zspp)qwl(<:&Yq[w…E) (}kT; @PȯIw}"*Ileo_ϓbng Ks6Ё9?\ӹĉwgP6 N H `/ qA HeZ[ rhsd=Rcے20PΣ-EU2yCjƞ?;\ hvYZ,V2N!k,cr&JZ`&4yl$fuGeg2߭ef Y"ymiv˘.e/ڛ+ M@ @nص]0-C5$A싔Xf Q \ؚjp? Dg]PY^@]6Qȱc\Œm:. '=Kޏ،V$֦J=b;U^VHY T`BjRMzά1XN{3fSߘhC;pTmc@A۝@af.rWuKy7oLtʬND\;FF7ϨKVUrP%vEvC P1Z|ʞ?>hKDETP!s]ۡ% V*ROnr)xTq3kj SS.Y;tz\d7&3>I!Wg]blUe+^8a\1Ǭ3{FSLVc@q,7@&-O~P}] ij^II I^(>fJ)[ K.+.  `cSgQR6JH"X2c4>)VĚ/ck׎̨ϒг"A:<=N㩱lԦ8r ;͌*{lb Ay߻w>y /2 0}fj= 8<jJ1bI`vj9?~3` ~9w wVKr:t VdLP58ҜH"EYi@Z\5nYmk26SI }8TxѸ*tSpju30u4uy S)@Roq0ZHO5TQפ^kw9exPW/o׮ػJA2d!UJMJ`#Pv`NfVm͏E\%)m2M[!'IIi6% RTNQ)΍Q͚L(pW_|c1.~VShT@wNQP76{ J@DgJ_L{]GH 6+ovcXZ\"ށgY g /ZP1 `X8Rj]`ڰFJHDU0p57ZcGS▶f\HU@_eZS߁[ƺ"i9:.%ټmcYM*5LENF)ncKwï%ysa^'O;W&ܦNB@7] X%`5bĘH+\VHR)mdi1AlmmeWش>nK%is\>w!^Qڵk;^x?Ņfzjfy6yg 1&nxJ RbeϠܘ;{`T/w) Lu G_e AZσWz@1LpފΝ'nY ` I,sJmۼ8򦖭pQR!}[~8M!("aܶk.&[0{e!rn)=ufr #yn2U-^0B hkRhA\,l*{lfDS+HpzWvjkʜ(Mչ͡*Mb{;-+Dd6j㖍f!RZ5lTУZB(ݦSs`E;)h<9dyUz* fR Zc֚YeܺZaAaUXpڊA;iT9O)̧٬JU`R@W5o9Ug Gkz|%mncff~<><1#+] RÜN95E]c،%*p2 y.^$[מɯ鯾PֈTMow};9., A-HaK\lߺ`]Ja^>C8˫88 UMԂףXݑҳY 'kck*tȪ7`.úiB_B֠lwhd <|iz ϧSμV5#-k!*Z"KLbֆ\S,lݳD3!֚Ek6n8n0дŕFrm*:0>_Ts0Uk7r;v|K5S91i ;;8ڻn-?AVnɭnd-V[:$hﶭoj9l]+֙%g{[8UyV +X & ֍ -K/m@Gk7d[+ު?1xaf\Q73B5& t mNPtc3¯:$ ^ZAAuf#jM ͮ3kЯvj׽*()۞ 9 f-;UpR0(usrl5Ƚ"iQ,T)$K+ HI` חP$M}vfB .JPH-"0ΫxVbU~ɖJSj,8\T6TT(l"YPȋV:d%>EyTe GYn2FZ:Q";>S6uj(̟nnOZ,f˒)YKqnEAc -k {^{. 啁>\^n]@lS{ Y7YC3*cFQEnQlMgUS 1UA:ew D2)YTc]5mlՠPrEsۭtj&;Ȳn& ?gMj*G#)RnHU`gq.\2-1˵"6@6_ZeWj۬tcۘ!p#A(Q<^gHr|q-Y1 ٢AqHeǟ+QAO9")US L_޴# %9;6] f&nK" iK/^i]d}KQ)qg^!`ɕa"+YPKt. Y̏[rK"S(F} {HcbVo۔o(v2:w k>RFWcQ: SQ5!@Ӝ +ދKG=%Pl@%S VEȫ rϢT3gN1iQ/dh5gp rhcnQKWkNoM{ sS> DԜHgktŅuz7/|_|o1 VPݳ4+zwJ25>YY -_tm(4߮wJv#<-G>puj4 羜I25dnȺg9WjR d/L0m2zDxgO#t6: oY뱨|o1lCϥ{)mqəu:GsXySd8j暴ff٣(0(+.Ǽٜx~G*6W|b'5μ/%ilF"V6~&]'m; Q `wI-*q1uwacg'qg z)dwqԂ2FrfyX6-҆xZQ%4}/t ۪eQ[6p:}6<35~Kpq׆ gBy;UO $~:-{l6kq~UJ%s_hE`cos;޸}piȃߟO}wZ Pyp)G$j 4WX@.ůmf US*8ZݢmrF粒WyQ>S+Ԭ2fV=ZO[vx'iqa5YPH)fZBWqA)E1MM Ag~2vo?y NG!3ՐPX݉k.V%l=nεOY!q랩cSdlR}9wE뵩9#u/#oeMx: μؽ A=G/̋a 9.ƱZ cR|OϽpbuP/ׯ=s*@jO.s6:H!;iNpv8/(o\r&̓=78?mix2iڊA7@._'ыְGZO x/킚eM!VOQYV:/_z N}9LYjAW}h5R=PXwj}0ؕA(sqN Y VGΜP=bFyh Silzͷ!4(Z6ugE|SpQjfǨ1XaWR\ZmeIZC8s3e@'SsYΉ*Qs4ɎUE_+K˖2uyXAoX0+[؞=wwS[$.d`椠 zn0v{R -ۆ _<*QIJ&e ]Et 9@dȔ1[` =Jsx]j!?W:7-("ڶqB6@RUa[̙dG~~Xa(Xn8iϺL MeȬSmײI4}^2.99^Aŭ.^k!h:ʗRu0Px +7*E+|ϋgpA.~c9-!k;ҽ묰Y ` &YmXf-N:ƹݓy~fb@T 5+NU`nzWu` 6oa}1C! 5+cvi)έN hIg@ZX{aaMe*Qkۼ2e>`K6>S jG*l1`}DwhkH5uC729Rt*W6O'Utn*2]=D@@D"E~l٪;cLx @IUfXطqg¦G"BU+ٿ!d-oR.w?i<&uN`e۰J\u Qv_s䮔'YC~U>bw_s? 0qkx:߻m@Qr$QVR)\nTGt9Q$g] ] ^ nDlsrծާ +nQNŽAː Qulĺ ⠭- _ S>-ZKw\Նc3)२lU[DE(,"U_\1hxaKwup iO~nS"tݝT0P *e&6v~=Y8Ӄ3u/wn(<^<=Gbpg>;30sq̠!yo >dcIgn! 66Pu ,VmN;Z9C2A7Sϝg<==&8<<>xwCM;6]g)vtmZKq1Hΐejm3le+l&JpBOlZ I?#o݋g5_܅}9q8m_ Am:ݮҦt-bV][QjD_~8YY[h $@e&,RM`V wR򽧕CCTIB)*jjb'"xmkH*Rh7It?y|>?={"dZMv9T39ob $ JW_2#`cq\NNER]ϻZՈ=v{d9]٭`}r;]Eszz+u]w ˂mRh>f~BKgz֖Y 5[g &:ط E-D ބ~sěaNۍ`PV,TۦOΥ'A#-P"A ى0mEkfrg+m 6/ t &2Jb>;Fap_xҙ} H*a"#kA'2(Ylugu0xEZ clDTbi=K!Ф]4 EE>XQV  +^kLt޽/۪! 9؈N3X& J2~\V8jm6jV*ZfzruPU[Rl#Ea(sgT9#-CIDV,ض--.d{G"pYDSAFmrkfnzhx.e4tG~E4jLYݘIV5 MU8wy0ɤ?u3Qj[zmE 蘽ƨccGv|Skdl3;)ujQXx8R|P+mCll Of*pfeUI!Hg-,w=v nv+?wbo Q0iM5h ]U0:m<1.fT: 4\9dݩe$?^TɌ慕/S>o:=M)rچR=VŒ kY0˘c6ߘjM.HА V+h[vMgwj- YYDrl T Z{_kf10wET_3.~GN4kd@x)I%Ga QkzuD48?Ci}I2*M=l;aŒ 椶QBM`6Jvu]L!\_5|~nѤVz~Aeb-m:d;(.pK?޽a}tag3kG( aА`Djk&7C.C?<-p9_$?ZB%EM3 TAs ;䦆8jiB-P~0҇@e_k*k$=*'T}ͫL7nP6'T:{:fxR*J; `{NUs mn<0\ h]"ϪCu=-6e|Uw.බף/H;_u}n;^o~ c&V fY +P7Ho_xMUc6L!jWdkR_e/菾x;pr;>Cs@),2M3i6c f\hFUޠv 4@Uf R=.]S*>*@I,Qt+ӴRI7L`{>#6`%x0Z|j,QdJv0Vuu0ЉWJ:cYrm{p{3ǿxz~{ kR;Ԭh|PX'Xͯ۟ǭE}|VxY-ؙ`wG'ϿO 8Dۿ4GU2h*l mGzW!zчR>UY}jL<="/{qq>#(^ƭgRc\.$ ~,GhPGn&_+ cpFsp0 -F%%|{K{yY×5w2( 'nVd` ,>v$D[TU͓} fj;&! <$+Hy_ 'IH퓗D9{}֮ٿ H6h|}R/dAt^o}#v8rLSSw628c Ui MDw #TԶ8$k#z3qt=?rNnZ (ta6gy AJ=_v;MRmF} )zhzFoKos)DtʷD_6L͐ Y7ѻa8v7Qb!Pv"j@}8/]V+qI"fg+o{FNJ)daYSbl ZoQwd<RRcw>I(EO[85ޕmŝ2XEmn2a-,D/ΫB T e;>-o wghar1 ܺ <j }kEאb̬Ht f16DO3(֙юl j6O\(+R3#"(m٧rрmejƍu1[=^ƇnwEg 9 yWhƖ=NvN'-砊,f5βB$HhtMɃjc$^F9Km7:&c6otPt`H- 1H1VR,l"+!2f{P{]8P6NRglsjwblY[~q UU= J𹿿oqc>F{yI~ 8KvToik)r%n[sw'@!7yq3.LEeKΙV< J>8KG3JR zxnE\xK?Sq>LC.1ifD Qe"-))X K{scޔy5ύ+!?suxW@ L[^gMIs+,r@:{ßjm\d>kD̤50NڙJ}־ɭoJ9A0@ހO=u. v<\<Ǐz~}u-1=Z3jRizmB #+Nƚj=k^y yFG/Ѹf^nk&s&Gϛx]o& cvSqZVVir{S[uI1MflF66(BJ6v$ uO2Džu擨 ~4Qb6(2wto- b;x_|i?CyH36S77_LTŀ(?#rzW. =y!g`exdj u֭Z-$Ʈ>;m2 ԤPU&_W퐔gx=|KqdY^uLBU'`^tf,Ȕ,`FSDTZ^3BP8S^Kvno_׎f*2(l>:L[JcUBm p)[Feg:E 67DCB{$]@@Q0i&$s sbRe6;:Bawyz-#^sPCs'쩇57'qL툁]#$bu^am2YoNQqԐΩJFXá[lV8z{倞fCܐɒmFRvןŀu{kzfҕwZ8yw{SNC'6gFKީl+j0oN ɰGÒ?m0\Tٜϡy(6A0JǐIc\TG:Jp^zi}Uas H<T#[_"tʌ--y@ڒش`Ve2=D֙ 왊$-X 3f+й<ՊuVBk;0@͔, bWKufiMZpmlo2@O,ϴa1@Ib3+ܲP{:kppqce6c%JloX"$M7Bs)RTC\ skC&y tN6@/J @T6Y3y4}Zf60H)&X@YAtMQed*k˭̚4sT(PƋ/\kE21HϮY |wp"Kv73V(2MuS)* {Ӥ~pr6Vߤo ,V3ǁz~2X^ojP6.&Y6[8juVŊ% w%(PA7rڜbnsEe"ijF*mLP;:dZT\<3J YU|nq&+;qact(q|>$VM1ȀS٘^{R(,ANH 9 ڞ!*dy3RL[ yb6/)1Xeb,T!jg$rU43Q t,Scv| e[5}&z\ HZ9 v_gSlE%Hgٺ%e@LcAx\r'V3n@Xbw춲s)9f$[ BSlgUUz'Ty2vcSU|g ϦT^G^C;glcc %ǧIRy{}u udmpE h>}z)*nk3hl֫NK[ufp$#^} nd95<ŖԸ^UNӴO]Wv3C++v8%pGT'sr ˽=5:\ܼb\X:i$tEL &Q3q^lXq p{~)nR;a tq$R{>{ ialqΫZ NPv[Er=GtkEמkMŗX`Me*c#Vb TыyW&3EhRW5Mv a1\pʍUc X yN7^B:!ƻ6jeVՇXrxLMFǔMv͊^EQ0ssLjk%;mǍ)MuMNUUe "$LɢJm_7X ;;b7*|2,ɞUWuP$5̛V`#SUaT#}C1k&LVx|TUF3,v3FH0;0Q=j0S:p++q&WXVu]q .n :{E0a* xIbga84([ںO1r<ľɒmw!Hי @VRu~Γ[N11;; WNɎ|Cl1nlߓyH,.]_U8-RՁ ,$5f})I|{TASZαT`-)9p26=j\CDY %SqdU<RWtn}9\!Acj@8JVCI.YP̻Μc\ jjL|˨ycGdsy]F39"$}ٺl̋ၹu/P?I}'<2׃nÑJQSg Q]qAHPKeBIԊf$5,u&Sz|ߵ&IorXKh EERU k|-@s n=8h؃ cGʭ=SJvC;4X!E $` me5k#%51' :#%/BU]ov 6$˶l_NN&\lW)2 -b(;ɵkN?Sї {۩U5PA u!5@ sB+Bn{ *SLeⶱ##_##< c|nw<㯙A2(+l8KԢ"7fSr$53LdvnSv`xdjs_㽢>9³W) U.1$L.zL~yn 3SUs*{mLCT0d1mRݒ/5{Q*Im8IQ?BNA#}2t,'eBIQ{+ 58gWuݙǿ4|/zX0Bޅ(gȒTyICV̿@;sݛ0@{l&ΒlYtjث'`TjʩF0u)ʀO̟c/ik,kD7b9$jCm@%}4hܞQAj_IŇ ܁%Mj)&>NW ՛axk#Rg[:ϭv`p`vub^vH >m}4a2M^_;ΨAvuܥzzƼA2}JYJCdɩZ'ٹeF HdHnwWfHS>ǥJ^Ġ"QGd]!`sr w3z{=_ZG{R_f=g{{{iZ=6owoo^+[Iݶ6MxHy+ 9V cWaLWK?o;oR[;XdVH4JʘiacjP R;h1&R2l-XS}d?3"ِgW__tX+d4AIn肺gMةIvTϳXX:fX1PJ,'=<_n؃HF[ST Om}GWɭ&?(b 'UՄ\&_:h&LUT[)fX_U-ۖƶ0"ą0YH%>OI8HƋnB{nb>Nd0iVnAUE Ht!'@|)\$Tꚢm\Ɇr+:mer;,ܨa/=ۼ͆! eU3u i6hv;ɤlEP~] %[Ȳ+"lf sK6' &#c yϊܢ -S TIϙQZȦړN{2Ut ˾2%):Uk꽣kz +\>%SY>$t9Y]}!iͶyJj֬sڰ2Mm þ@& 4E_;_%75t}r.:(3@QUuBlLk*Mj3 5)j?+*˚JMd\XcZ*ẇ~LG\T(ݎgH[_Q!˻ = *ߖtU;&+YO 1ޔ\<"X$u>*+@36;?z{kGüM3g'ҒsmRql,W@jt|<ׅIJK^ li CtYѬryF rmsgowq pnX­ Ȥ7S˝ZQNE>{0}q0R{s1o%izd UbP(JV:42!JCF*um9h楀=aFs-badQ~~*t9@Ҽ?s yHJaU7Ҩ9oXlsvbkOVbN&˵=x=+VYP^pg>OmGe* !Gljو ZW++oi}v? >x{ Pf5{S`>34S DGmF8x %'9c'BtbaHXse6C>Z,C{m%Ãuݞ&_g j}Ub(Ulc*%S1ZV\rrU[ى=OuOjr{,4sR[l)~֭:ؑZ>S鱞=.\ 1 +Z-Rl2;7s _҄gs(Q:Q-=J:ጰ}j-0QjCZfqUQ.y9Ҍnr;M(iQpj'[6oe_N "c0TK,0[+8*+"W {7;EɶQڍx!Fc4q=YZ0e-P*oae Dsi0izcAJ!X`'xf_L, C0VmRU*`mI vccOL6ͤZqi@s>DҾU`Y/8g~~ iv u^,KY "!Vv W]`m 琣VE$ 5Ci2=ChR%0Gҵ]Ҷ9_"EҨ"{Ȇ-Ž{;0.P53s;gN rS۶^D$}I%ۖyjq\d(X!Ty\EcH{à4۾1J7z=,+ToHUL]چ>B]zd} [UJAm9Hm:0Щiβj]t"熂uXs j{ړꍀ^wleIRk 2M UfcK ;lUhX7flU)TVQ Ϯ 0V2*4R8GS]aBZrFX_vJ@WI?T;4TŰ# wPgX @q]ȕxJl/G!6vm.A XJaܵl;H\_|řfq6Zl15hs"eE7V3uՠiw9U[><- Mwx[`j LpVg *株@n{j#i[%\&ﰱ7֭Nj'ĪZɁ?jgb;;7vDž}RB$2U5SB*hY% A&32svfbUMlqy/w8?r 7l6&)c9_Ax4}PS%^CnTPL{V$%٘Lւ:ە&W 9W[vSS폼vMLOuo}vUTXK=.ҫQJ-'I*1[oE(u>HLe[@o⮓>&QbQ{tn,Tuoz|Gp:Q#|ۧpYr1 NcA.j%-|2A1hT%$4-sAhN$٭ Q»e" RcoIȮ jw:IS-8 Ƣj,t@!QI{Ȗ⸿e5N06l:ܔ]f-=x_~h|a9_O-8J=3Ժ GR|0%ߊE+8i[ U6lR魖b}[UzMNU̗Cn !I&2~u[ѓ=[(lN /= o}l_x՝) ǞQץ(`i%&iOrdn3bXj-8ZVcCLZV43p]{^1ic^=?{?\rSO߀g`i65Pr'EcĖ `lU^:Bkb@\Yz/`+cNy[ꓵ֙:u1@:eϪ̞S= 磶nBv]GhR[|/7Z9Am}iL)[ CJ_+]Q-i.d,uyߎM|N><6k޿ݵnzNOOu]ws@R(s}?8w2ɗ.]ڟ)ø U5 (rsM7Sa۶-+X=( ;u6Q R8AWyfT ؀5uJ=2ST3f&Uuo.TcTD}gs5]DVunph;KIm [0KWRfPQZ8'lZ fLl ﶂ;=g'kNϝSxĀ_WmBԇ_`&,A%SVnmh Om@,ُ8^S}qU}4ʀb1ӭnV?6j% 6QN1 ΠJp5(38fCȺwZԴv pF`6ˋMq:w\.|AK H.gpG7?zbHLc7:gw(?UpFJJ(V[8*3sѹQ!ս# ?jgp+Ϳru"o\v"#:kqKfAiZ>!NǾ炄㑃ź`[[CVAol5F^3E$@~Ku7(,<t[2$`XCc' {f>jNz[:TpJKFV)MXj~MR 脾^(1Xo*בasYY/ԫoଇ ˽y߰ cH, 9-c :cRbP51#Gؚ l )df&؊CWB~j"BW>"q՗FK>?[NR|g6(E~.}#we5*4d*T ^QU TyK1f`D1 ozd-݁}&†<  p ُڞpLucZw,X-1(zNN-3KkmB;xdLF5BG& 5H#G'PIYD;nĶX(˒F` 6q,(dafhy` l>{ Wn;Crj`{7 -;޵pϹoVwn !!!dcb6 9q \*qSq~T*]!I( ZZv;3|߷zq;^Q{zpSCYm-.ؓۮ JLp)a9*$'H0_YWnC=nz͛3Ub'$DV'\V'W‡NK_O0gʵb75I'-ۚz; =P&5䊀f/BFd 856V(NVc+iYm6!XO h =U ;}dJt2A"kW !Wp [0%+-a!3LϠjj'(fL1Cj,rz%2BLmO?Ӛ'}S^'Ɣ'YK!P5R/ٰP#9NN{Ulswr`"?ZVVs=8#:8F%1%Ha/6ϱ)8Ri6,{ؚԡjdTp<'Id;#@1e EA4R'9e/`cUe\KM뽉3 *g5,\Mϓ5ϛ =3OOoUx%AȒʼnURͩ(+J5TUԱ/Y(;2vxf2lC4`r8C@bk@RRӟE><Ϻ_XBus:?~^ͻ`HѢ G3IF&}+Zz $Y ,:QR;՛RP~<P|MllC.:o@'h}_3gHcR&HA =X18F֌N>ݕrdF*dYs`U2)I?Ce荴cc`CXMYAgs6RTZAo60%8vXc:\Arǫ*mFbKQcYd_UU 4Sq6zV 榩nZ0r`kHMX}:J榩FI~QD"γ åͯAn:wLgAa!%#1BJQ>zodA,Z >$j8@cZ"&W:bFߢ&`Oo>J֪J kkp5~E1#v+!i86/۞ .; z#_e}L-oyP6nwzZyIʟRAUyp{Ĉrkef996iK>ٽ=۾*5 L`4ZF,轖^>m(*:2jh>=/@Kv lwk/9xn^shN_XC' bqGQ$xtZ,3U%>m+iM[q&ZSV๪լ}?bϪ3U*,_!?vsW.#|~!켮R)f$WpHU;RL H_ݪ䪫T_ul D7pu.u ?qnLzXE``ڲ~Iڣ}FwŖ=6N"ն$}Nr<뛽F?3MvHT<t_| Oa ](wꗺeS5q0l(}=«Aw@F[ u NX(!~U3Ǭ񦃳-S:t)0& c^jІQ YVh#X-YI qQTgc' 6{2R(9RU?Ʊܠedprd2Ď#L MF?#5%'QU1mjQ) զ0KC嫠#BMzѴ&~ Oʖ1.$G $OV5TP6L~75+ Y* Ljf+v6;'hC>gƖ)]em*kjǰC|V27?c}z{ ȠJLSc\v`$+KSыlvqATe YY2{>Ϯ $x Vm.\py\svRAۉjekIu'Gg+\YJJg Lw-<S%o1b=ז>G_=1\᭏k{潀F0#Gz˲vk zvѶ h>嚎넞SeۿSv:'&4[8lƞl-J^pߓ4w5{[߿ }f9iGyY!ڙ*:yBjm[N;wr`NSMi:؃Aqҁ6ITn!w0_€bfut"c_5(UXQӔl)(Kx;_>xʼnifb&f='87@z$:FM>['uzX =Ts4 w~$~!><\ SW,2?@χ?_~N;Kz9߲PVb3. xqa1Qh1x8lvkxj %ji oz3gݿ2EF)ON;BJ b~m̠J6YϏaГw&HЃm@Y p hg)泮ZVmYڀ}V60ɔݞ .Y͋s%6P?lQ]=f_$7\yCq֦9Vm~΃A1W!vEcҌ[DΥFSh j26!\Kh D:SZσjm5ݏdg:eJ3[WyPVLUTf뢍Ҭzƀezϼh1@ZEvl[c|J4k6.Ea-f>Kڴ#;Ia/{(Jwtmb@23 1Wex:_U*ahCU}zP5 gqԘwYtMzƩgJP/;1M_sR%yΓL7C ۱Ջr ƵR>T;L5h8ϗf[ڀ28sRc <Že?-Ƈ܀E!Ѭ767Z8|7t^wUJ>9F`=MbWwwL8őԭsϧlMRc ]jɍ[kn5k$0\A,3^)`_}ɵaH9;*pBC 5ZvmTaE[ļ7cԂZ䙪ٚo9xg (}ؚEQJ(3BR+Y隉]TJw`_tZ0c_yXice#@gg uNzеUqnowIRF vOoBQsiA ADŽ"Cģ$r8eϮ̅y+lO~\631{=0[8P}f!eTREs;r"G,'G"pkН(]]wxt8Ogy_*+!e!9:%銯T K=@j>GD3<.i BTa1n+ h8n/lÙ?R̩'lm=8ˈ )]ߵ>5z̅z|ckpf#Zf^9CnD eŰgI[!t O/#b W2\8_=uʑ(ecϥoL?G?6 q_*s||k׮3oGŠn)C<вqkk.RZR/^}F$h>u n2rt>hvfo79xN k4 J3F%22Rh]UvZp&->1AK89@x"4 ;FFX+_rK Ф!t6Q:S<)ڬp; QKfmsccX*v%Y- Mb\ 'I2 L)Mѯ?}a56W+m4v.[ys+&Z L&L9`[%:@QAJD& *Fi\EYr[2khwQ~ۜ69Ԝ,Ut,OI-*#ئN0žޑ2+,j4 >UahkgkU '0hlhg>pkUYC>*Y-O@2#jTbcFk̚M+K |ήյ F!aQkn@Y1on2ؠZ5d$n> X҆U>zK<`lAm4P5BKjiv,X0U2\ 5ek~SSdS0YG*GfE !RhnJU6傁\j; %HuK[F`@^5Ra@}絣e۰>KjK=u'`8tjɄ=O}:r$9b;\\5Ug͖SAd}f` bZ8jhk-KWQsZU nS:_E̞s&7\ g /Bvםi_O}g9^9ȅg?qa:cb~-,٫s0NakV!2 jݖk#+)Yi"FWD,k6gT;$E)IZ Қּ,"&`!`=?KN/x.č?o֠I-7CSrFc֬jy4!+ Tl UɚYb,"k.To'Ѝנ 0f>z1u 0H`jTl sX8>ImLVx6aqx`NYG=EiVCI#3KŬVtz-_^IYnowf*CsK"hPC'$R벸^m3{0""r5M4Ywܙ֌UKژˀ 4VƪAB m1ЇǺZ͗t-3`ʮQ5PF0c #dϿTm NC/7{j$jLJ`vvlLO,[z>Zz8lYc$v0d4j+TrL ME>$TI QU>Yu$4E\ץE9؊rreͣ;yKڌ[[7^y:s/_taמ?X-;'j#F-i-cj6`6ow4@8x ,2^ q"Ďayć> w7쎔SJ,7,q$Bﭤ Tl:1d˦if֬YT-IgE y&qhuxV[E V4>K7xuhngQz JX3j9[#h LYa^=buyU|ViςǙ H>kU?݃3n.?{|#9{\̤M [DU5o節(v QV3IJmۇfY )w0TpX`%Yo 2χ@xx=mm㠍HVU0-)飑k&jz8Cu/Rs'`jb2Gn*]Q]dcCV5S{m{\wvF֯g8o`HC ޫ B^FWWEBtWR|\ E\v\DkN:7IRijDU(++Ew߁ڔ<2S3N<'m,ϧ!0=[ڸ6Q7>C遑l&vfӀIX'!n6M.mS3=I^ ё4 kcYTZ='Ewb$Dgdd 9Iə^?ArZضf@ƞBs,ŋ3=5 K6mYi.K[G%+d4W89UfaК@j~] hIE{hLt!&*(=(ؘ:2 r)g՟H T_I1!sS^䨸SBًauŮelXΐ=7lrlAxP[%eǶ4 '|.U }uɁ1;p\tUښ"P  I~ #%rGt:]PtM-ɼ=Ǜ}Y߻X2$)J.]\ 2}Tl(ʶVH@`xwYS:9 jgZ|\h,?R15cPpͷ23/6dJmeWoކ=w+0tҵ $ݛr-~TG'2b`z*?.2)-6PfIEj OީM!i%x$@]/L_7*H0+[Hج$ tQ]w-*pαdǎU4`4U?c$aҕ .]^3X@݁5=^Acgjx\҈8Z',5aSFq5H'Ik +rҦno JagBomͨn*rR-ehj#W'(Z|;z. j?t̊isЊy\A~F66Q5,~t⑨G߀}>9aW<~ISM@5[X\L5:F7 Ř5/u gZS̪)'LIUo-ǔL}3}Lj~\F,ʠ =ǠRu"E5KP3"BUoFZA$e8ܥr~,١V8hj᭹j::(k֬P*fuEOTu6(>N-9=EsMLe ڂAu,"fxo~x^|>7O?ESA\)'E]7CK|{783=+ppk~Z t˺^9+5=XI6;Plk R]ϧ6Aax*UfDx_U﷎feo99`8[`ocOPcOmjNу tLؿy3ΡOނyOr5D kWHazI?8!DV*C_[jgw/^;#w,2w]4j n1J'hsk1,+O? E\r?7U`'iN϶nEgΜys@{! kS O3 ERɝ,)*r咪@v=[C5֋؍j'Y x $FtU] `Mڰ˶)oyu"MiTMFK%lI,uc,UHP;"1Y&1!5PT<5!]³lņ/>õː ء=?6>.s;k@)X2:lAZMt '3(xO'|ľB3|&bS VYOŁ~wjԇssD#Z_TbhmF!4;VflFf.e'lji|8lNIVf^)oz%|Gpx Wf avo*GNc," 1OI-o miMĊ,:fˆF<#lR$v%IU52e|(W=5l2)0Ly3Jwy^^@Q>;FH-WhM]zFj ^rQ޳R=ݨ0UEQ2Y׍UkSsr;[ pm_˯<O܂ssm?/x^_7|C@н]O)ޚy}Z)5S0& 65\ݔff+dx-f29)gX8o!n4VjEƖ:5fG!5"5F95[Fyz9{VF;56׬jGT5מ+!xӋCumKioBcvZ9 exexIm⢹vJmJb(%Ϟ1;߻=߸į}y<o6%2FS,JT=[ ʉfGeyl'i"*̠ƨ焘UZr.Xync!똞:×:>~ >%*ͷfoytvTZla\NYفvjUZfLQ6`| }72ZL`*e( rяAmQل H'(S 5רUXGkw>>݀Pk4qmLSM:w~>Yi@yZПwdgK'w<0Cz/09y:@lHY-I*ԡM+q9jз9L0(y;Pzl=W sptc \Ç>v~xbmDy}8pSW`$dr#mآmug!JA~Əy7F.\e}?_7ކ#n3n(z XZBKm3…͛O#$l]n&@zP#onנN?Z.@>1|nAn||S5wL-"~ԓ5TpCjLS}Ԛn<'U< =UNnlɒoSCK<>m")X+s~Mּj7AՉrNoz@ O^Q5p3΂^6`%g?6RW)FP1SE&t J4hR\PP"ۋdr GSCSp!7l1.FOt8xCVmA4v$X<7↾Y LU,nvnmCn)Qt{Ypcs<,桂{# ; y8kSRF:]Lc+7nILqlj˰gE2ic׫Zu1Jsm4]Z6B;6R!4e .ny_t~zN,$Q99:74317:5#tL[q甐W#-4<:@~]^+jncmƖ5\޸s5ol[NEcM&qRAoJ3řb JYi}Ѧ(x׎- /~j?(Y6I@-p(*pn!WV6)hAQA6/h9x4?S{.ͦȚ6,([؀ GV/6|Œ;z $e!ݦyP7Eydf czwڙ '=WV ]Fn6J&/Q׿ljL, AsxB-f|OGOhAh* li|ʭKz/!4YWtL0 cSRH Grm m ٭yݴ =gAy"Y_ayp # QUry;6 NfR 6[^%͗}-Yfzƻ ,d,ٶ85Jь͊UnZ~d#2wDJ pTDk9l VE\nLrmtj5(3)3fә"EAA %KWȅig+|͞6im^:9umgdn<a@OB5J5+{ /p6 y>OwO= |R\NjQkP}ڛz~k9;5I]B@V-7Il>B%dl((|K<&誖MVјlJ_um lx<ޏ5OU:ɽZM3uĒ=|jMΖhb 58]KV$$t dURj9 A#۳հ^7lN.eMUƔ4'{Cw?gw8Pu %jeaSqg/Uf.9GMﭨ4&%< 8ߗp#mw>?04JfJ@4Ih6l)ꠖ(PZ:Dу 43 ή9O粃y{ Ms-5$k~w|ޭy\ E3)9QY7 ,l9{c  2{ܿ5~6retM=d 碙-2SզY{dY2xL\g+sEn[9xnS9o @O{sE!B~?vƍaXϓp;}nRv"%<5n/i~q^qSXq$4O:2>"h!$Ny>מШ˶I TU]rӌҔ]=X)r9 tޛXY71~? 7?( z'6 N$gO?_jU-CSĤL1n}F6bv7rQ 5- F[f ltMj7 PWXȸȫߑA kGwPk.oLjtk6]"›:~2T<8@Lt%`@PʨؕJ XvM՘f7x Q@ bm(ήPpZZmq ^cs=6=ny.@yKpHY.x ggg\h4+)> 9X&jFcΦ.,f=sVO;ՙ>ՙnJSakccaJ5tyl'NRfj ZX{Ə+"0HYm\k8Z[U%B, @SlvpMi eJf#,kj@2o&~픖nSprbQCPgsX]v +ϧAKmO_Ym:(+n')\v0hwllM bOD1$m0Ur"$6PonrF{ma*GW79KiQgs`c౵YlC?`*Vbr̫,>>'du N~OR{ڻbH ,}U E2tZdSi6<dj#yO9:ztى#{>rh:荄 rc \Զ|&=}܌`>æP3 - _2 a5OsLfFF^ 5=_of M H$s**rA&Y2Y]ˆ78e˲7A"%ld|Z 塓hݖ36Ĝ[iOྻV]v8rܡcM$kkx}Wk4tFPa-c2k=犢6LڰLU n&"<+6kPCԔhl+A7X 9%̳+kxm+N| !eӠq1%)[m׶/HX^Ҕ"YB59I`juGc~W_^kzSCg?xk^3,JK>3UQ5[h =S<{fkY)KB k5)p> ڬ|%Op1U fUd-kx*S\$%ktA֠gTo !V45o0_L%42㲖fcva9> cgK1=DVL;Y#'0 L"+Kyҕ_}>pkڛ_} rkg~𞷞 gɝB_UҌ>kWEj #ܥ5hyƵnjb`H] @r_n KD2S4%*<4K[FA{93λSO܄} *ܹ5kMl66fva!`C~93bNW]=,㪶rؒCׂs3(AMFs˲?m=;#B@%a+Z?3rvdu cKCS~Q~@V<'wÍ|#9+w~I{c*rc tTtJbא;Vq$ hxX?]wnr6k_SamN~>Fʌt{}??۰?a@߾z}szPbC;dvKQ4q;oN㵫w" sjIƩ5ɛ%X9i#rn}x;wԁ09|7tRǖ/1Dn8;6)%O59NA[l8tmepk_mMO[H5dS$C o<{R]lR}n;6(FU3, Qm,)kP5G?j)0 4U/FW_egWWR+1[ yo6Y|hidapK]A&*`%l f5%+.Yywi>ɕLޡ淩G4ܕ 2t˯9(6*wv99hٻHHe{[LbAy0oGLܡPK53ZEa Mri&цSUuf UOIAp7\7~ Gt X3-S0ДLVhK TWl*DIU-p, Zm Vt650m';#wk AAjjWU$ ՀH+mRnrG[P3POشEs~GA5T0Z#}4{& R3],œeCR ^6%4~2Y;?fu x_{5-j 5ڄq^Fn`3lDh(\^*m\7Jœ]<ھeͻaP%)N "2c4 `.&W%%"]C=[Anawl=%@F5?!\$ hxҳZSgm,'l(EPSø@ mez8"CWk&<+c쪏ԖX&vynh2gǛ0\6: AKSK_`iCl!!C&> +\Zc;`M(9OkwĞj]46wY#w^ 6i#V1 Gs齦Tn*K9ʩ7NzR0mjɨ~v/KS1iUf`HMA ZjEM Hn4QW(qb3Og^𖻶g\8W1䵎jpgaf-ʔ{ʾ͍*?i&ϰ!+2Q X3 %w@P&ET jLl glru|ߎR Vps^wpJEL l1o0G*r\Pa št4ƒuV@oml9*G{E+1M6$d2>նXkSpG83k̦ LZg@`v]zMڤ*ZOO2P]![^w0 ;C MokҾ34ѨEx"ˉoufnVZ!QI]7 %U)8yb ީuŏ5m]Լ~go#,bKriVܺmo upDvnڂ~Ԡ\o?bT@fE2e$%f*X<5)Yh#jHp`P닰7WmʖӔF1k-;w|:1wn:!,ĵ$ d̞AUfWNUyݮV Su䤝$q^%߷ݸp]ùuqE94 AIjM S^ pf\EugݙyUɖ Pv]3Lc⏷J6sG /=f9ϋssM#I3R %6*@zN3a*bgcy*dC5,̖} IXfU51+*S8LpS[]rJVi$yS 6`f ]ej9(Iת)TP5 ECKl%HSA Z=h~*{r kV Y*k Nd{kw\mݭfViO0ˆfYզY#=ұA`[euFkNkkߒd ܪ T~XU7r[(=1~obدU@A]3զM> uj=>[0竍biZUa;gb4aH:j8MiaZ7u,&V;6_ [i6D2=gb֫ TdLq06$mxY& #9d5+|`%k\StŃT9E=Zksx桃YP҃wxB0 PiE9rIͦ {޻F A{;SܧuYM BsjfizcV+Ҁfz;*҂XG0!ɘH~$^,RZܖW{Z*J0 Z\s;ipܱ35afEֶIVrp4$'=v; e6 Cإ(:Z084͏ّ5\CރyESK߼ O<_X]XX5uRPǀ_QXeMjBgYkxy)MC媶[2z[Bצ*گZ؆)r5=~ | pfk{y{j7=R< wV؆|@٨M'5%Ȟ&jxO7¯3&yޖqN:i2fGImT& )%)aYt0?R'a O~gku"?c":_;SK?+]?nC=W\hr2 Z ,}WLyozYg"~;%Fhl>@0!*xzjMƱ7JþHRE6wAdl] t@` 1Ѻmmfk}묍WQɭ&X jSAV*|j-+\Ű6@]m>[ev1ݘ-wK⪄ aGGbI\I:a*h}h~ņ?8l֐ 'Ǐ2kU 4ij/BS601X J}Hz=) (G=jD:5;vl,”sּh[snh9B)z@7hV 4pxu.S(x}Q0!dJF{ޭkBlv?Cla 4vA|ڧ[aS詁Rg/S;v*y_Kppk= C6.ܸ pz ss4zL}-x:-*OX.c6PG<<~v7fdB.>ɕ#rlgH>ʇղŐK֠pQ/l~K]#G8WIi 4֬1P K5بv 14hU.f|C xYeΜ?0`E5+7WF!{6gB@6 Da "Y* &噌bQs2E|b1?w ;dQ~exi \ dSv벻#d<I,YWͯMVo1Iפ^?mieHN ΠMqҽHJDOspAmW-y^ྻxm$(zj8.(Z& |P[tb0u"7`{BEȽ@a Vt_Ti2-jVP6:tR!⦄!aNm^єjgEEu̷!Y7ch}Z*(KG f8;9Xǵ6׾>Oq;."<u < FH,5!~P- @?j,Qm`ՖEʢWSWቯ+Οۃ3<68_݂]|n[˼>М?K^HFQ.2fT DUQLf8:n9R ys߫~h' 3IThmKݾ,en{2 f,ޛZa>ܩ檞'-DhI[`"[Vb%J#I =)KH (cJPB[ ʔA&{k{^+׿ν4URn{>{_7A_@u, | /+c87݆+f2g32pΖ 9@,3\ x@EQə f J3viSw!^*"aU"[ii]6,&1WKZh}4}4wyw Hzs : mBvwp՞9;bn5XBPAlQ 9Я_߀o_-pn~vkۻۙ) Q &*FC'ݍ;PV'Ys%G@-W<<^y$@lۚrX.oePiڻKkz=!vZu=;n<;lhS(nf%B<%nkт#~*[ns֬lB#OSxx'io{MimyiXΒQUbKߣtW\\-Aj)6B[5&q3:~Q)UUE؃k&YR=;pŷaKX3Q0Dru:΢lC2TYoJ]yT5Um [ :12"سՖ膍Uyfmj(M0NИ2fJR~lenfs^O؜4ΏtKCR%"yl%Qvp+=.ZЩeJEb907Y} 83נkxXCwU`44ܚdEihFm_Gwqx×J@jx ^v8'k-\"L1D${X7W;J,Ţj gϏpx7yh;V64rToKTmTF0ڲuƨJP Z,:pvO?^_C!K\) yʌ*JekfLj8L }ΪH L=*=*z>j#vٵ׆!):jM| ,.{ 7n^ %ym`qVIUCfGZ)2 Ŝ(y/ VS<.72t_p8=|#<~GuRȕf6~0a#iwotEO䪬JR/fawsqwo}:|w]؟Ùk*pp|d~=¹}: Qh'D~ܩȺ 8NǮE|I2)$zf<) |s 6zR$WHl,+kyȨ`uVYyZvZZFmYs$Z*|F1'7_,L8rO]ÿ+;`eo-=o%?MbY5×w Kq{`[;3K4֙(p$#dDe}Blt\y{Cܴ+)ΒmLzϹy!Խf/*)$8RU"c^cĬrR Ĩ=g724E?y:~xbVk%ϼ6T:PҺFQqvMNGrYߧt(P[{hϦU Xnh_#U _-xO#zVmsCnA>k!;;M;wi]{kohV@+ρ-W-j7m *vnMF"۞n1*{j4qSh8 g׷S/JiJzI 3Tsh}9ҠD޷0Y"X(1~^z1v[p H lRŚhaGjdP՜cZecgJÈjY6¾v۠iV)hj۔SQ*6FANi9$ɫ:'e«4.nj |Jk]\A8jCz|u2㽗g'HUmҼeIc.d؈F@` DiTndkͨ dMT];]jnt= d]AԹh]?-URDZ}M eJ @&7N}Йξ!)SKfb FJ~3搥f15+h +KsLh J? }j`0fXGeʜ5g V6YXDgŕ Ol;R#Z19#Yb'?ƦΪ+ {JPbc1ke%S@sֺu]|97ZYa%6ZxUjMץZڊ5!6f*:-;J oZtCa%OWQ3"ִCTm,'rFU aPw,edH^]c`UH T)PMh?'tt.f6w)[vצ"[; 8W@1CeVcehsBmtFm ϛ2 F BYt1KF&Rt3D@Eܞ*C9sw[/|R,\RN >Lm.]Mj$?aLGFX?T]ElUqܽ @N{)zXqJ:)D/5] ㉇olEV@! B䉙OMq6ٛsP}4bߩMq1[Ȩ]Ku^I6uPyPQlhB&9SښL1+UJGmĖ8R`3Mw3NU Nς )U5 jVJ?Wֈ#^}6 |X2{.`g (fEU[ٯH(Mj9Mo3*@fQe^v+$`u K"hFZR $#" j˵ۄfj\j.DY Ȋj` ) lRsc,ul4uVasi?GNt/9\Jnpb6c PXq5_Qi:$әP%@flgl1ݗ_ _z.c|| {g"7|5f.e?/UŜg^m@ڸ,B4EZO)閪:eMߩ z!Tj'UTeH \rAՊgo>MJfi@NtZߵ_{ _ڝ1\z6 ΒB~HZk1EUiLfw,UN-N3$}6 ^:Q<.nK2g]Ux?KkkO>O?l/)|iU%SH{G L28N|YTl]$3^'w_#|s<,tLpU5Bl۟vPugNykDrIY Px;I,^ՉeK?FsOz=ޯ53xvtȪM:PajZ< lC$7 UW'POHtJ9L1[v𶳛T `t?yrIFW-<)ϬdA`;_g;fm&۾I}Q~طWZD(\D. 82?`W~pT;/`B ;Τnɣ4QveYMmfiSHcaMn+eQӌ8,7:g,7we5L_RLUTt[0T7ᯛmq zwh/}\pE QEUv[6iGEFdJ&ZdI6FLf:tY9+oy:2דze͢QXg~-k7:flq Cm &OPY>&.E@(\(Cޘ)I倚Fcfxfs4K K[ K,**$~U!Cǖ['7`QY=J %0B tKU9*oMc~ yteLY~akćH;< @D^iV:KU Ue$T 3;^*W*&vV:*k KPu]h+P,WCl5WL) {FcA}EFkS*އƄS+SYf0s36mWBhI]ӋNMiZ`]7BƱzu6A4N3G%W#`dv-;2%ѳ8>f#6UҟJrXD_冸xD=^;xDB , ~X[S  !╙];Ke tLJ[jf &qV ۟ /|O^pabbTd{Zì$Gx..*YM_ّ jo+@6{Jpp63 VU@r:bU[z('X͑wFkWpxx{縓_7j%g}W !'8i4-N<6>́|9%}TQZS欨vJLNG) Z, J!듆dRp:t+\kn5rmnQ) j)u(X)cd16IloYf1BXڌeU5 1!Y{Y nI#/%Ӯ~ ~TUv%6!n 7_7>=ޯO|]4yN!`GCĩc{_TMMt{Uߖ%hV m̟m{p^&fe`Aգu:=񖖡HQgC`$X\ivqe N`G϶JWUe ;--Z,Yi4ǖ/>])2f f8(cmbq vu 5Y [)PIvQbڶ ٻCK#0wXo!M0kNU[pՕAѱ2 jN51nM=~/B~GK`_(j Q3@'M6J1&Ú'Ų726X=Qn^ckĚRuh xluP3s]R#u;+7Փ:=Y[i3>Kg ϭQ<]|{ oܚjf"yb= x3H A**11p3dWX6-3d(9?VT+)EU!pfSK;Bl#Uh̤Cձ ,zmxʙ7#|i;$[cuهFMo߶L\j>)x8}<(QCfSs k"jlvS@KQ(Q Ѹc>ӣw/U(64UWݚOՌ yR; i+E,ꪻzC fIfe ee\7iA6R#kt9 :6p; y n8vugIzԦ1fIL%$-?+ܤ?0D|wOd}g1{ݛ>>'ޅ ; \+sstdD*w{pF5a ҬN1TVR\]Ua.i9YlPv5B@(W@L⮵qbLIF )^bG4[)WT(e#Q djlګ  F8$VR^_مfQ  ҹ9¥~up\t3d@DwwNi|BA[Zų3g"0`J,dM7!WXG'( `Z&{~@8Z׻F%$C!Z%;{Ӝx xMԠȴn 5`DofǠcҁƾ]Ʋ iU3trX `O4n/rϖj1QNW[/i^ "bn>65 o هB̠ŎXpe-ּ%k*Z=2{$Ԉ lm+;>fvT ZQBYl;Y@UjQW$i>C={v3)_v,D%yxo, w]l4G,gT&tRfh;0YT 'ppS˳Q:7fmYAe5矹՚an ƭ, Y%sDXۮVHbtԪF`)G<~HXt *$2cG6=?VIȊ/ڟmQxbTP͌ /HP҇L$*!5Z n9EQޖ^*VfezX5 \bkӘ/iv#AٜTE94fV 3l,iSta]ޏjӭ^{ .\aN>͑GxSs_]rvr?ԔMe;JȤ컺*RŪbVv $vmBXR Y: ɛQQyAU$ R˞R O Q&聕THh"֝ & %OsƊ;p;w=xw=Y?]9Su }^:1Ey)Ji{5&/L-KmSR d.fT8v~3UQQ&{߱ gzWcUAP\n ġơz 2R%#xw-_U85D.9k%c,>{?/ٯޅKg{U3|W#g1/=K*>e3;]]63Ct$U{w~Dexckq{?O-y=ޯfsfC013[_kmvtxA&lJ-s pRNLkf;1ؿK[&6[৵V+zmǹm?9e ШQ,Ʋ"YڦAY9ụzyR7Cymf4n+(7(C"E|i!n la >e,Ufp=gggUbl:y8FTF33Q,t*RO *+TA·{@ ICҹ=F~)ִ+ZY[eUeqsWՉGeňݨbjc~mxJβ(҅F-VPdš6Wi\̪$Ҡr&r<7ͩ*Ömf6bŘ>}Ϩ3.{6Q<擲qjQ( j/)PE_X$ܑ3Uh}V%Fv H*S]<30tW4kj+0w9hYO#VD'5;>(H P z1;TY5fҲI4lոU<* 86M^Y@ w]+:-ڭbZD+c-5{ڏ<{gEq^= uG;x@l؂2؞Xg*yJJP* l(Hk^W:i 00`UK@jL!& t4A%bG&_uQVroMﻐ9 `p佲Z<s1K^3 V x>K? on_|M=3tx<;G["W^RP6HhmgQ<9j_sW%cgZSxg;ŒU%{} 2crzq$o ˥wa%ZxF~ȲZS c͐Y"#&Y!]8f7RisY˲2.mrq[LlHh޼wrWWփYUXK 6Ӓg Ok8_/]?s~~察vj0+A,j/y䠺/a5tYI @~nzبѭmk<t׿ӗLq׏ Y.Mz&w:UX I^VA^LX ~pKڊy!_]῜Sqm275Sok+e l,*UZ|-V`dn jJ:E}19܇% ;J M|cwUuѰe%YYUCh@TXuVǀʭJqTp2r2֢Q#%4TEZ :U7b%R.6[-MYr$}Dax6n@NQZ*\3WsҪ9XV5\3**WJ=o l,k}^B*hMOV@ƶ#ɫhsبՖY9;J sr),nIR3Z6{*Qby2ˋၪ`Buw}Xmt T`uvΆd2٭6[ja"nN̯25 3ڨ2MC-~^$h;>8v;#E>c伆*sO7?x iÕf 9\:?Y7;;8l`P#D 2-Vow+m`(6XʁJ<-ubd3'i?fy'I.T>p+MT2"aUlTH%4Qm:ۗB=u !KѕU%@1[Xg%FUkC_5 &xÔV=l@uוXU}TQwL@;%ɚb5ZorEe]Sύ׿~c wLdj5\)JJCj7٥6~m%Fk]S6n(+&=gJ3/svQ:/9&?1mӀ㯬ۅKFEe\פJRR_1|sZ׮<߅ g:/vFA`_<"q`ηݫ?s=~ȀafnVbw1fu~th^ͮ3Ii. [03 ci`f ly“7[@-NR G!B!\ ;6lm+hexmЗύ2˟{`6?'gK<=v6؛;O p:(;+"m݇Bj]NlやBوn?eqz"M,-na,͸6˞$)&f_`^PSn-)0f @QgA nw" 1O;BћكQC190!{Aa ]q㛱6H-cJ4@ef.YRjݦbgTkHk,iL| zדz5DR&%7)z9{4xs@PMb+OlE &-C1OJfM䭠-5UbaͭFi [3#?~+<}N,mJcJ`ײN$eSIjAR`.O֫į `72`zYJDpL߻0ů = ӱ)LfkF\U1mvV) \>7pc,ӟ?Gغl~$ǀ mndk:p8(y 76P{ 3oS^sT:p=6f9˥CGI6ĬIN>jNJ_a%geӺumF FK8_4>6p,6Dx%5EufwQV͑NAAkekyC7V:}oz݂Qi-BztgEx 8@hYg3_3)l@h3VrR2˲VTU@ch=* Hz [k1ks#aIBn j%Mj dXrPV<\Mxd+}nW#]o H6QiZ%Mc좋ʽׯX Bߧ@bFAu؀+H69T@,fFY0z&'j?3Wc7¾0mKLpzGF QIk7TVYRMa{dDm:ݠD7:ykt#BV0tkV1qخpe=mD8Lg>I9 v%|g7~axg,'%˦g&՚=1!]W<zҍ^~IpT'@h&S425ٜ>xE0EI,КUH4Y: c j =7OJZD -56*`]bysskx4vTU5w{窔6fpS?gKp?F`}\r2>c8Eb}-%t{ XsڃUT_ܶ=uHsAÞvMش+iӭkOJE+;AT6zɚ2&\7Ͽw^!m{݃m1+1kNB A8:Xs0UVBtQA!؅%\rT R0þ.-W0"LR3u΂YH ;l`$j%呌`6HN`U!,Rn~ASqNfh- (\%$s'y1mur0dQ *NM͑[nqf  :kjCT_Q|I7V a'C1;S9džyi@,9 ,J W_YS/x3O l͋,aŷrvYϴoMb5w8Z{,P}+(agΎ9:&:…+\yx*0gkWgpt ;rS꠿["Y"g U9 Q~XM_?M g;Xmנ ipX>ǫg:qi~J[j&Hm;gSq1jƤ6jK&)|qo D s X)$z\%ʇ\*[yg 4"kp36vsonDəUfv`F v6H_FWŗWz S熌ܦ pvK_'ڃѴN/|OY/ pH@{zu:%-F؛튚%#YM5I$cri%Zmgsؓu![ܴsU0h3?{~kxb~#G5ۏ^_Lry7:톙5%G n}.P@=Cs9] |gd,`:=2W';tR>)U%5nfzUqYX0,s;YG4E*!zדo|tpJ~?[ ?gFY,$MH?b陷=46Ǥ`J\+b Dvk!آ7oܛ<7s|U{JYU4n!. !vYZvDiQ[j3W-y_,gg38E]{_ |a{`IZҐeٞ̈́Өh~yC;C NM jCkY#f O3uT_K_.܃(Q=N -XЙM 5R6E1/n%SMqܶ ҿj-KZN<"pۂ-0Byt ʣAmk [n;n1bEp 7ta|722&:8* zV3*!jH<8̎ƚKL[w]b#W>ĥL}!?BQ*(( Lqdgv*TlUfRamx)X9blu> h^@#S2v)68,ͩP{-j;9{v9)ٗ`)0ӧ?3ܼ3•IXȶH5axYP۬А0[ɚeV1ys(<ơ<$(SUs$}Ff岂>N*" _{\[kK޳/v\Ϝq==#¦F*ϗ-G"gnEY@1si-XM. _QssHA8R/^Ï??s8ܞ9OvkT"Ed^m"(A;AQp,AN,vb;4diֻ%]a(rgf%@:^ 9YJAPZnkiu|TwZ_|:Zʴw!G8[*WuM]-EKVxXCh@Jd tUj[?k}J8 &i8c2yvuΧs3jg JlD00?>N{gz +7'6coܾTB 2Y 6G^7#yWmh~L- uQEHٛtM]![14n{1 Vmtm1n;GKնKDAS x,2/6`/7%0:+nDj?~D3e˙KEw ckhaT4?^:02Me+rc5@K>/6zNz@p#?WoK_~[LL-r@ȟ~>73Oa֤*pb[p{XSYZRs5[TeDo??: {PŽ_@jEisӠ3u)SxxWY,~6{h1.kΡcXa>j!ZxׇCν) D:p۶0@h9*av [@j E%iy=v`N m6"y z`GFxu ')|ҁSPZ%ܮe+LY?ˣj)ϸ8rw:X@ZLUjLd/eX:T&"Dƺ& Bek($X[]diQ2Fk6ֹɞcg} sBޭEOv,)VF[\jeKkr%gY6#f frf ^ntZ{BQ)2bBoa6kc@0٤9mg kPmcCwJ(|()ƪ n*SiR0s+SbL}P"@ř{ڽVty+]t @; vBɨ U+-%kJʼ`yf{g긤MpP Ԁ"ũ6=6fg} Q++>WB<f0YR?o@ %ß1Ǡ4$}(6 8Q@~cr=G;``\!P!mĵVd:sbGlb@If)nLc^l`1;}LaU*~o:QZ6L{x&+r8kGU:VjKƌG mS ꒂ5~>GkDnb;Vh\ : )4{fl1 gKJgTE68R-B$uJն-603h5}^R#FcM.kB( KW[m?!V}5\$+U{آck-}14R7K5T,Tj&a,rGu-7;]Ǧ+)#T5>}{iQV2;=¹3xl7n GTM]Wp .׌ a `gk{<1kb~!:ye6T5ްZpih?xּ}>kWTQK1"vmyl2q, pgϿykځ+ìPnH72w{߷3Ĉ+X]v}4oƑ7u'˚]j: {Z;q pj&jTlyrN,{e!׌a4;i|睯 8eiTL_#~szgxù~sZ:A&]$1^ک=tܙ5/Gpˬ0O,5#Y匮MS3S RA3۸ɯ>.yEk5Y5όy5$k;zp5YAc _0 vZkc81Β%I\+Q@Љ杵\o+g֌lYQ2i>>Jpu%5fua gQ @z~Қ7ہks_7VwjZꢿko;64Hh`e%@{y"41_O1 lUxn%R~ 梊:o6f: jޞdluT>%(<L[NE\k3k1ڋP3:j9(MqϛM^g_xh;@??u8w4+˭GϐHSeEj50IaE REs l3 dj@V(u.Y׋HT]k,_+^zfe-z̉.V"]sn)PHc0bg>32|aFW'Zӿ ~bW֑}4sh\Uɞ\jl9flb}NYQ2r(PG3>e ?r̯`<&kI[y,a`"fB}1 yxekŤYHfa(M{\ tԦ?JKGUIGY V~<-< ݅z88l̈t)u!ƈ1BwTUS`6깑4sf lʐU?csHmS'm$Q{PC*v98HOьVR/kY*͞Ѳt82g!&нLF#<#₂!PؚU;[UT3bڍ#G|e%ZՃ_{DPm nNihkXN!fHْc- ڏ:ܓjF9} lM!  AYZ&h@,dբ"8@v:n]1Ž\0,Io(MH@y;[`=d͏<QRs2>*ioL,vsxe5w"]|V /j ,ځ6:գ7OA;Fb/u}y^awv 2M7rp=g4JɣwEȿ jT?5gQm #"ˮY|x,dVqu*Jr@4۲bc`>)S#W hLU"%z73UQ ~݅+g8>}?[z %i9qֽz 31r0ϊrM66 ѭi'"\3}uϽ'p_24EOPuhΏOL; V%kTyiu$ K;h+6_S{LK$ FY? /O#su?=8Xsc%M Z H?ztܕLJ vT5VAUh+a[ڽKo~gvt{~$@8 Nictd-)adjΝ;K[ckDvjNNc5C=3ز%ь mp ODŽ-aіBja1\efj<O Vۓ@6ߚ[6e ի0PnDWm@(n@~[QkHSb-{ßR!tp3eS1|.jÔ,/B03dz UBϣZpa_g[cO$s,hIUl$n"sNRmý٬q3;wɖdg- 5̦&'9cC&XփsiȸB.zjf?ڌ!gR+jh,3 Ml%go9Xǟ[\PsD 6Jz(E,4@fGkR {ok[rUk ;ߞ&ͩ)68h∐cGFIAK^p8 %@,'rl'( n5}oÙkU꯵O;zoLZbf 3nɍW抲 䭓A79?34E^h'8_ 2CȞCW o塵eʁEҘ5T&ehj27qsZ%r1ͣrNYzR˪đ90ڃ%=1lq O{\rn.5O3 .Ut<u˹<-7Lm\^__T:<8w 8% hE*J-ڹ"c4>+bմ+ GBު|hvAsLvɖbBȻ/;-+q>ޫiR匷U Jf4{>3 (2j*GWfuFىTR4g> D5֓lU2XL.f+,jh7bCxS OgU5(sƐb󹃍C`"f7:w'#tTo"ͼ hc?б}=t iεpk4UE7 xE@Ƕ6k˜K'[-ll_^.#,<ݒ͙tW^+nip:-gq n؛"OSBs[;;l5+)t 6};q~)R־/uGiqREV d*d\ZKd ZvdN}cO2qV{օX`+e@ -Ƕ׾?u85em'mQǖ ;O@VZiqN3oXXgLf{ jPLLS*}A' Uڒ$~xֱ>[ׂ3*kѲ"+ծnV(yx.[NZ %IFBBVE+qY5(fs'[׶zd _xv9]6[E>]̱% F'y)|ŦIԋ(gfh- yj!{'l33`~CZ=h7df'q ܫuZA1)A}L| t$lBlLHd2`2xDffRtg4fZAn̗)-TnՔeeMnLg>Pg7&ނkFL Г3:SQ܉-7UUiV:lY3BU)kP6$ecdf>d$ZznBCzc1 N̖}nP۩fũ4Jy^i 96D%A|=Ĺ3|-*Srލ-3vXW݁E2"xAm^ Y54sp8q,s\{MJy%ת~ONKX9& " ,*۱8(6҄A0Tmc'80'3'9+[e{eGxTg ߜqf6}75AI"諕_c}GT{ੇ6 duȯo/YGOZŪ]~w`s}ˀWȸF22~$ySr=?> x9Cy-)t>hueI7,ݛ~cZYS]!JYShA8qv0B%ZĪ|$*\}7 S#=}$}l*8}۷! x1y8g>װDE{2v0% i$`J.ǏC;Aϻ#6+ 5){jLu+:Se&=f#_4l7L6G*z_sӵ̔1H㉶ucsDKĽ9fjfXmYtiT@EIFֳX.1FvC|#Rvҷƹ d קkOҖAgm=[9lܐW,fc}_'Y#6sA!f\ќ&vE;4zrS%k03$d-|->^k~0q&rn2VPKΓ3wn1bCOl \V(L6]i*(n!tZkNo .w(6(0H:{]T "21[\kA 00on g Hwp7,!>V~,sV! `JFl$KUtX?:JMn)=g~siv mB,]t*PlU%Nw33 g}/T,V+G(hɶŪTvoA/с78#${XjOpcJZLGVSsUc;Jlh:G*_qq)]hwjnu+㊬ڔ[jѐR/uFyƲ2ŢlMB!fb?95 KAgl.84pمm]k6XjhؽL HC0.qk9uɠطKǶz*T TC;q6mDkY$ܖZ=F:Ƹxp;W0#ˬfl?~%|11o_UW6,2'M%/ 7Zu9GU$bjWNа[l>F >dRCN.**y{,P-^=.nج0wd1c)x_CK%Wpk|}[#8~+-<}vNg>vO,/,x.kIjLϢ&l %U]PS[,X DF~% sY-w@j,3>(6p{QZl]r^@N(j.{ Β*B<5Чu^B1Վ"%0ß/ځ|{.gWt|z{-Tq@NM(sR.D|;Hp/$1/oTV1ۤu\& Y0C-rNSgXm%JUh'Ev.A1O%qKSgXҵß}w? ޷ǧbc7tǴwz_~S!iNلn_{anm`3 ;g<h4/F,to. XY`t{/N]\__TYAu2Rˇ ߷ n㊱X1Axlg&-LKZ&TĐQU=ST *^ 9Nݜpvp séY_م_O}1:( E C˖诒ecV|#߹LH5I||^Hc6O= Į:N 8NZU~I|^*6^H)Q:OXr3C^Q 8 JUG Tss4o\>Sc5ٽw-^5OeO]3hUí/>j( pkd"5dU/ӡݺv?O~ _~GzN hqX`,Is :asy'?֣QΡD=+j3?y*bIgz\e_Qt~EQ0!(4@Zyc~?C; U T: p3!5l,\)spjw}jIXFs.̾naƞrM]ö\:*ׇaQbmCahp|*^яT֥*A+W@p"7qL%|Bfg6ik (fR5]z ++|ĪXe`L:y^je\TFA&URhfbم]Mj| ۰Q@Jƙpͤ 8"ǪBQh& YaFp~Ug( ,%X) XhժvW ,Ua4tVy<2@1/ ۀ2j$ssn7xiQM:_ n!ID DcP€jY=rhnQ/ZdWy`Aqd, xjTP:-9ѩk $hegP QYN:QQ@ { % UPT%d#4ǦI6kG :kp>ƌ w+ZڗGr ^Rk,QQXfagkƠ 0 /MJ'0 h@q5MPXQ- >%w"fV镩M[0&0Vw+Pjxzvk ps'`,z;&Mzjj~RԘ2s-z$?5&+wr ~iL*q-+ <Oؾ,H i6ʮv ͽ`~,e1+4_h!f ^f)l|esQbn[ 5k6yKV gYNTOtk?.mhr.PCjmPmөa$چZk"G`/{yʿ*𤋮;ڄrux+<I]TQMG ӚW7h@JygWA kN]E,m=|0h#'צt,yt垌y,8%8Y|!qtF|+|w7Ci>uD"Qk{\pFgG\Gϵ:wPNk/ K8=%#l`XPiD)6Fd P6HA^,44T@{lhbT3zfFTi][)I,kRQTv(zόk`4uCLh\X>M`o}{dԶlτϏo}4r {2/~ z7=vp]ۂ>MFʕVF3_pֱ AM㤡u^aǬ)~ Ҧ2^o7p a7=&_*(=Rű<_c'3pz{ Lf݂?Uo9:9IIzascA~qpωJOn߿>,bC1iQpU4\ u_H۲}ދ4bVDZmw!1+<\ 9j̱ B;K6 2MTtΔ= e6k[겊q݌'Z6p)F kjɗ|߹:9'u}JnvYUg'ji4Jr=}:g$3`;vO~.\q_I CɏM̥<;Oh |yiG [>AhWiZ4Is*v !1!P\w1Uc*|TE44!V+=t07{>$Z:l~6HBL:ۋt֗g޾vwws:g;gZl,=(݂-PLW_hux~3cmT뻖 <߱ݍd^d<rcPd/9loڈЈ#Ũԕ$A*s`nT9dM%3DDrMMmA7ZiuQ,=5C.^CdaCxsyco]᱇`=#='gdjK"7k]ܡ5mt\E]Vy\gMrggXL\_{̮r`59Dv.-~\1g yJp,%2Pޜ.4޺4_0I{iR%|[+ptύq/ \|.I_cϜ?OL`ڄN x`f}߽3:{2\ڟ}'t;qz|Sw^ pd 4.^|1'wl0 |kQ%[3Mq9,^A?#L9QB*N`xڂEqßĂ&9ۣb;,vRPmvCv_6z0l4 &fK`V3ΐ=!XȧYsƔXqn >{xϝ[a|b [Nzj]:M$VU!-{Vո1/VzޕtflbPᛯ_bt~smHA!y;z̀iSI?ʒkBYՊ'5)/TpAY51[xTzv^׉dasƠ'ŘDuU ~/OpH%=h\` ?b zg{ .Bӄ5꭛;Oޅ*4ضҤnݦi۷ yK(Bm(lގm/h 0dK@ͅ13ū7Lͭ3,PSENG gp58UٻU& =-#(*D:<jAd$ @AjNiWWl 0 P7X0` L(z^ rGN!.W |6 !ak xF#aPneb)\xLyph <ݺ{yJ* DdBeQ3AEP6s) P A? Z,RWO˙@ Pxjxz)B\VńgþMP3rh*l"*o&qLPxpKVy7j BR!-VV^ v иXI)C)ѨQVZ`%?{ a<' _?G*X).7یufG)T{mj&1V=B9і#q6&h,rSIQp ጝ~ɴLխ@=^-$$Sw\,sFTՏ>j2* S}S+us_VZ M Q0#g ,|9eSe:5: P)I>3V!7'&Z\RB#PkK &Pԋ#wVj>΋->"+JH8=hk_`kbGvp#I3bSk_`rݩzhuX;m-x2& >'-/` =|QIL3:T.!,Ίme#bEbN(߳2hwY 呭ٺ#ssf~$XK8Ԕ tobV:, JrtD5%9$|od`wE#lsoqq.R RpѦl$ZQ}t}K 81Nnc}x{zGl§> |ۛ0 8#4o㜖U68~$< GחCxls8~__fOu @x/RV7Uh>R ̀ky۴8`JȁmLsfSlh˂h10T]?5F^6X7s(VfR8rƁ | [F\Cʙ{mnazoQr]وzg1[* 7d|f{4bK414ծ15&2o0QnǍ3hiwhTIId`ŽVBV8{M}o?'kgn4J}`PF%DW& ޙ`hmt_ƁJ /A6inH0uNV@r~ ꧰BRqu`\VpRG7Y9 ڌVdh[rTW1kSjU 5zB9>诌wFohI!;ɺ{놇w{SU):{k=4;/`pQIPWy܄|"ۭ19VYCMA:oپؼoXsYuAic[:pNrra:]DV9U4|jSK舉/oW]cT(j݆jީ]Tz-/D9SҬ7Q{ *H>!?Zfm0uR"@R} ߼clԦO߻WpÙS؄d[Hp]1~܇a`IosDaWw7srmxim@qET~!kL5$!j>i`98\zKINjdz- 1K7ьr-dVIp,K#[Su.,skH\ڜP\ L7 0=J{c=, ll#\ ʁg %hʒՃhًU K>|u2'Mr hcʦy^Upe. ޞG' HY8XWXbe!4 :Q8|U^@'NwfNaS@DZ"*QKX Wk߄WưqfNoN:w _O'Yۂ0Q<J|O$2| mC[K_f< <$m؅1#gx^AerT;2\L`dI}Tm8_GƼ[8Q{EзB,},D3u(op~$ƴ7=3>_!*֨݉[#~ \E͂!ň-*"਩] U.soXEd _L;)4۟M6p)P՗}P޳ʲbC, '[0|pE:qW.^ ݂O|>ؚ.a*bKuR?Yҝzhm])N DSX3XY7ulkGqop,_E2+OOd?{Jkv&v[AT^ 0XΩT(ʥ O|_~ _8 YWz61'},9=@pmo[G:aO+ $7\VJ"+YC+0M/T7%ӅZb H+2M]]zæ[g 6rQlbȺ$dfejCn%G@am~̞Rsm&@Z񸲉CO(`i#}ߦ2| bȂĦ@CؕdhC`;fS9\ݝl A2i}RLXm /~PDK; -k ϔBH\j5#dzgZͦ ޔTCf2Di&w)z doKF먛T lM8S*3+Pd+Hi]]y< 9*'fOYr-:#}%sFʩ RF ױ#AWCm%dۇbaҙǬ\2j ' ,d!_ PZ񾀌_XІTm.o>3HM Ǔ^>!M "x-ZxsC`+w.iGB%ԂDU^;J;LQM׀͂knMՖ<EGłZAYs~S7P'*˹tuFN0A^or@G\ig DSDÛe@`P;ʤaj 464mY VT. ״ʏaP#g=rm]ɃQkBC5;w%ĕEa K/r̀KW[$ۓ0`!K'Jgl^9|'Oi4 {k5p^X:͕ yUbV8Yk4{NR )ywFMdyfXL,4mbxCue|\:oBEO&MCݽ%LӼIY g';XB+_`}^@ ,}3X} E i_|'=$吀 S%H\&|Xudqm27[xmkp|}#/hf:ajz S`]tۗ/<{VOGi|l|Yۛ l3݆1B RBi-ܐ5_N9x8(-4aܱ@[GjI3AHڦXin˥?xa;+V'Sl8azqq\==a4pg/l:X,8w83fH >E"h`f&j%VZIð3 EjG)xݢz!4t\X OhcA3 {CĂa"R+#jBc% Z-@T&98eA)P%~Iʱ |ś}ƽc&)\Kcf/~a~3Spb2OϻO*֠51ԍKG l7c  MRه(ͫn5xtu(JmS<], .Ii[5^{t_TZϡ؏)xe_ʹCd VztA-j+ _2BYCՁX}':::x 87{3 R N搨MZ52UGHӀ(Zz=si̷'CnhI/E-5_r 1%ƣ VaQjM?0ByXWM, ڼ3L>*6T~_6Pd.Z{g?IG5NAhz V9k|(sR*QAYeFGPW/}r/}û%4э1'}ڶ4SxfG+?8'tUPpaR6M1Gm]S{XUeͨV `*=VǘuH9l*b8(]\'ԶC6snuW{c`%s]bF- \Պ9W_ܩ+y3tB!6x͙{K}9yJꪄAV~U㊄߰5+nhn`- e 6rvY4$W+{.|̪"rfU 0Q~6TdA]Yck,0bչTKk}]QjrU=O+3Rzj`TN (p?~IYD V.YJznz_:Al]FWpYQo9ӢEUKo4Wϕ. r`2Rx[9Q7X,BU 'O>l-u1\\AOe 7 y'ml.Φ=m9Rླྀ}m ( X N3=X%k:|~xyf lDycd$`ET5SyrS(²P!ZXlx~g@gvfSk27<`fpp7nwpW`'c<.=ޅe-pm/to=ҵK {6Q17篶MxcCS6a=Õ _?WX!^RVX\19>vH]WXDQ$;)7p.C*<%L[C>[v/HR1,z =+]sxyT4(^0 ("/F۳\}u/L`9o`[5P['Lik@/5S'V9x!҅boPHޘ.JjJT1 G:vҭ6>/JVE^\Kvc`O/a`l `l5^?-xl}}wwv_u7> xf nd|sH}T Kncx|hZ/8AGGHO< XX-`km%#֗JnUufa'(96Prˆ\lc#YñB!׿1ڦ*ۨ.-;M6!fC,ѐoQfBRrjݪJĠ9r3AHi* Kc\»זpD@gg6k*dB[ŊLm&_ǵ Mxz6 66|~3g0C6NPNTN4 ^=8 %{Hs0jEaà?;$h '=Q0ss#1npy"ݷF*IɱO\ H˦-fkܧ28Q4;JZ69&^fο{{zQ=*3MmzڶbHОM=keR>= 0Z0)PWƶD##mUA8nn*ު#o$ajFIUeOEg.CA#7xl`d iptL^PL 2mT6NfJ"SG/enQrZehPZD7@Hx^h˲?9_9YE8C>pȠ6fCCh ^/;O00^_Krrv{86J>X0kk X wa^g zk_O-ٖmjș@Ţ^chFdL.ktˬkz:G $;4Q;\8r aFoӑ/?C$e8EۜOPUc(+JaL[o7B^\f P!z(X@f 8 ZQey7Pم9{,g=W{uz;sF< f]QA:+Vkp'UlKVngw3,zp#GOaCO)rp669X,( C Q)eB>6+1׋7PDZklj EYX#.5eq֕6+to#oձ>7[UU=(d<*Y8l78[Kk,bw:|k/ۗ+uc61\G![-=p~G3S>>tI{[29=J©9_߼?c'sܣ _>ݪ[ gG'5;`m S'e+DA.eF;/TQ3paӺ#:co}PEY]5 RU7V,PQ+̗yLeI X9QRj+MRJ;[[M8s+u1uZ;pF>iZsyB(*7;*A]_.&&ϐ.ׂˠt!;X;``"”k"V ,Ғܽ[E}dUq;oL/qW__(V3<Os h`T|m@n9A,f:ɚuWv`1LnT(c_UUF 2 F @Yx_y5Yvvf[cuoL©瘧O*kiߥg (+KEFȄL( \#yFX|΃x5±t}[Koq̊;T|\r^\n@|Q~P+\{_,못 KjM%s*~BT[AV=1᥷΍}lS=k//g?I[#ֵu@*%d++ib4yMO_;uN5--;;O?㻐UEZZ@'i;v,TXycUDËujTT@b-N ́ɍy B3|OPfJpqgcz9:D6jGox/u |RED08"ݠU#$ m (z+CuOhq񨤜@0 Ԧ3qS\ ԯnr#4kl)g?{S`oUE熁cmq쉣$< XLj+?dR>5B3^ )(N2AN;BMr#piOJT)0#b؏Rt=OA@!jD&k(de RthՎ(/I+}gl53 6D+ `1+±iQV!Q 3sŖLBS+vjYVX(~,5 RU ⩣co|랚7tcV7nU6.G?~J6ϊ kH# U#b UDhlܷNCJ6۰0iq=^az]܇_?|7`:kg|$ ZI5Ny~oSc8=tO/m;G:>-8uYOol;SI:.o]9ONN򷤱5=rR"94^%D3V /#4:[F^oTFlwtFY&E#p@<)U>0RH/x}{3xШ]nzQWqlHMYB\,TduEY'Du:؟lOq?55!۵zbu+cʐe ƲWз$5о7gsڬ(Mٲa0Zoho(51pǢጥ\CcӼ%Vx>d@B١/pݒ:@6Ǔta]<kw:<@?cލlo*?kq*)pXְbrHrI=ſ,/OF_k&D B -.Ŗ7$KwT#DcŨ3Ş/QT 93WxD){)YYhȸJ |T-9Kɦ.ېEӵwJNshԆph2Cb<,j/GUFKV|16 2'sNjûƵ4nal0-Dq,Ɗ]ȵ4jէh/-_qCX_݂?x2yB:/ȀnQ;?68V8 6A_:>ƪ-B`gs; wÔu 68K<eYikcojC\)).ZGF+'RGSM~q(>|\d i hC|܅+Q=tN##N6}}k|:GlN yڴ6%YngL+"eA c[ihV,W,pZAQ[ay4;BbA\},Sb[`dÀ:DS JӀݹ]ሧ.ty~gm3l|9*T԰U P;tb8i3^ǍW6fSƉ w4 0RۨXY[,K MY[g@h}vDvqZk)ّS49D'ksqm A""%ZI;5%"y,l)U4p=Vv X"!;20Elh4)&rKị0;aDQ# } KplXzaȰQT%DQ璜 ͟:l^qL Ӑb ᄪIRtA̩AClZ u }-6hc$ P!uqAYm^1`` 'KeJjQSQԐA/@8n]fXps_=;zYUS$w%7Ǚ#Y ЁA!_wvVvw5 gDsfsÅqu\ڀU[]lnk( (gԜymYV- byٕYeENSX/`&W$;g=]Llǡ.umQVYݦc3=xYa@e\*v+ga+{b8dǡMpU*Ǯ1ED DK*](L$5t$='O>+c>^\;qM6I{-I%̙9;@] cܸOyl7ܫpr\YU) Pv9ˀ=ٮMM 8iLŦHaNХs7nدsHy W‹oL﯃[&1!;hٵsc |SGCOf8L=B_|O-5Ig۳?-x7F {Z/m8Mα|[ Yo|uXؔpELZj%܁3^v FQ+~Ba)PH6*JV@9 lcya3uxC l.j8ߋ nƵQ^BXYQ_[bS8L`{] 2?|=5t|%[ps +>YJ=fӸ:dDQbPS5Dو\&GKSLʋmf{Eα'at±vg9ccu9p}wIIq(j(YbYiI-TZ۱'[CEQ$mPp E?-q\@H4%E0ɲ,˒I(R qxM=<:R}Wo:oj/s1 (睁p$yss}z^x([պ/O|^[ c#SqN(iZ:ql }Az$!NPvN?YNLa>7/v?pf.x=3ur{5YA3dK7;OKW=Χɒ Xf<{iC fZ0tT|&%$%( :DTC0H9W*& 2?EmR{zWe!=]Wr}3@[{3x3g`~,V=+@e.҉JTΚD zsx)?/}sy GS |rHQXy)iG9/M*B²O2weNFB 5FΈNU#^BL֏RFҭ}vfA.Mfj$S/YxߛgprrthjUU5byXv/\bq:GV;٪?=T= 4{\qгt k>G_iv{pttO/OB7(nk@fـT0hRdF%)E,mJ5#;VL(Avgg  #> A)$@׳]qjNegIo~m U9$K=+r4,eO).JݵkD+pF P x(G8m'PWQU`dXtu99 #TȅNGuVRɿL,+dFVH BUp,EREl Zf~_D uC%4 ?J8W(l"JrA9,=/X0 VGUJZoÊ%`g;>ieAIN.P[[R5sD7HP~i`;>q,Yr,18&TRK&16i 5Zcoz . w`7~@ݤ[MZI'RvHREI"mRCԉc+v_4P(ȱWE\^`,d>H2?Us@+,M4&AKû$xoיBQesT,Uej]4CPlDKUn!yrf UB)3^S|kP@_mPmB=ZY 93^ǬYRkZ6~~ _pg%JبLV5IhY*U%6c~R4-YP$HaϜlyr@6I l (򊅡\Jcp"(췒z03q^חq_z& l?ݻyԢi(dRk K.n1F*T4|BKf@ND*ʵ CMTrPgqK]rqթO^S_9!pӠtrҕuE߶q+[zOtVgz6GLދM7Rfe<ط `OC=w~/pg>G^[~&- 0i뭭m}|xxLmM?PQJXl6A-iI%"[y$PR,_C4(D:)N/Bc TV,+PXT*F7#;̝~s7{I۞dOtn *hT)oȃ?&pe+:oC@;o93% "(M *[IC.fs& ŠP'+.*XU~+aܖ27WtT+3eNo_h74RzFQLc(P* ?WWrvNDʜD; ?95 c3!&eBlcM>9}_4B_2eUL2Q$ _o x <+"*%ܼTi,TLP?MPU$uP)VJ]O $,H78dUkr]f%)ALn_ZAQj0oJM^5X 却ǔrX`Z+3YB|=&Yl]L"vEd[<26(0`V/rO,1:.[K'քu~=v@BKЇ֛D v;IE6%L7_dP*dcHRg,^rO*wuvI$.,u@ Y1#^qp&SVT>ۢtX閲Нs֭Njŭe7_;:ؖ>4e;odrtԩvXL{m<[eE=^_K &Ĉ3P`Xm5@1 T fv{{^MA釩j=5GXaP,d hu?U AGY&+And$?_sS94 9P6(p%>1  H{a{Fb` 7bA I3 7d]0S9 !@젭bֶf1f ʢ 7#.E +%|jte?R0!yDM}AdK궶j_!(FTv(WLGWXUp{ *7ʪ +, ZlJ,k=Ulq CżQ`o 2"duN7Yj'VE.3X"2oBfWq݃4!f`MrgzmvexM7JբG"\R 'rT#d <ϲҎE0EcE ̶dPl j=ɪ8bl@2dk5uJegYYS8v9)U1]Ak xE+T@ThjVQx#:Q  PJm95-7&+Mt{LEJ LjCh+>m,*P J Ԙ2kNYìU1Ƚ1%Y ]/ 7 ϨۏR]9k$HW73o,Uj"׉RO6Rf*nw9\v4kDALy۴`9W5Au;6Rvw u6閁mgwɺx[40 >@$IA^{k*eqkA l1F$?VAG L D t>="2B["e,hzH{V'yXE\̤d"eZ-H{k0g'[@HcҠ&GAT8ZaVJ*cUͯX ay6{~ M z ~<m&=8![u@jܤ5~zK]T:l%hԏ[Ad=@ O~;w _= ӻcwG>gS7~ԧeM@P+^%*]cmaIvXFQB0KV]oxp,ffXrvlRUeTbz_w3|+>?xxshqmHio-2-= gՙ*q H$dmjrdcܘv{ #&e m5ckEuAeXx=6|NG I+opyO(b}@}Lp@gAɭko8*}ںƕf 蓳Ãݫ[~7\r?޷mq og>;;;˫WnDOi`jCh;aeUXFULau KhAU)dk5P) gA;)q*`1+Tg %F)WŢL@ zoayP𬳄tV*fJ58PxUr [$3L NU2͢'v)*hP̫ J6c6P;aYB]\dAmRCX~=s5߳-ICw4#?J##Mc2y3}[}As};lj1>]y/=& 7AoıwIK*{ak%@e_6"(Ќ&TKjuJ T9@XQjH)YMfw`{N_[I`aK̸KOa 4VGg%sPE<5ð@2YUE 5rTG*@)^dޟϕm$TC7ks5Yf!@)?"YQτ  '?mZ!Ţ|m|1$`p: ƿaO2F:6ٸ)>snKA  hN"CfQ`'M!zOdܵ+/}{/o<7 l<}f3p{֬ ,D7 HA @dIAܬ+OW16^fJߚ,{x{pk658<-w==y~K x/bC ZK]1%5 C6۸٤`FXP t;.'~7oO7=X7s\9xZTaW B؂у6|b))#Xz A$}WSX,&ddgK)r$1h_@XٴG#f(Hb+q`;D(Ih)aqƘ\F0rgX=ÃDJe]V1=grN ;^)XV/p SXn?9x}'4O-!puRR*ڎ'7GI~Ыc<+&e!/5l3bo@ L0P8$1beA (VD"vIko?~ΝKw0|~[{~ ~'{x[O)EIY%TbFl%8O7|%̙{-`Ou)fN]rō q͚kOxܸٶmX,ro4Yq;[7FW_GPn';E-95K]Ӈ ٺnY^)7R] "Q!eLZy$^|,–@UiMVg;YyLrɌ'$Z.QġF'n4GM ˮ @NZ_TvKGP%N}tMO<:C,0Td/篫(~\#lO,Qj6#@Dو]`|KeAd @ U(6H_gXhY#rOau W*x͸Fq4rm$pTA#zSKpWߓV;h++Q0Z?j3y\x*](ҥXQ^b*UbozY%)nt K R;P)?.4ݵJn7$2 > {4\4T:GN O곆sXَmB/@J##I> )UV89I+1=qbT;&,Rdr ]K "`Bp"l-( Ic5J%v}@ Am<ېd]aB4k/ݏ}Xr.>E.u_Jt-\g`TjJ*pC#-H!=3ns+G,I9UuV@!eWSXK;$2zla3C} [!eH/fu*XBxyA3V;0l^}M"nP>_ \p]_,ƩFg;!t'{NGygݯ5M.PlkDbXUY9<K @Or/>4 ){{&s'lP1u.1ڰ -{hxr҄4iQ@~; |KgUr un1Co;^-bR'E7^qa6v[{MέY,!u#3Q͐9]g7\8Bph]` jH%!_7IZp&Yx#$;l`߯Xh[$p(u( ֻ?u# ;v4VmX& gO#:-+v!7T@F#($SSj$e\Rô898Ʈ!d(O F=AD@bںJϗƕ M@o < =.ϱ4.$8!*aP.*%VH&= ?aQlD&#J8L@5 ad c<܎ >ZYbce\ @j]5P*k1fQTug .3R D%4 r-Wey -jy^^moH\Jm3%*s_3+CrYZ|bE,- U 3){٢p2H 3I5Xe ɽ^|4Zy%bNOB뿯cA8_ ?kX{Jj55 \L,u^)-[sH~8_ y'˘bC:2&X*t=E4^}#PbreDUἃ>^oYϽ/}?Vt-{KWzxvڇ؇3g05)X]g{.mzs I$F;#>bM&KxӽznIt(x/.= OO=YƂ%Pt-[$&NA M-g81M M.X4@tI SoC-(a fz7)+ vR)7 Aw`Ed*ҤXk. b'l{5p罞&W뭎=? Xyh-dzoZb +HЫSqdIXq5HDL#(l⵶O :,swPR΍4 qt}Ȅ;/olu5P@T(sqËW-ۯ^.k?[W3p= xdV¾d IF*<0h}d@%rآ-UiNl7WҋWct'~sߝkkrAtX)GĢt_.4$ )d- ͡b-,VaFLV}vqy8g,ޓ] Zǚ'/͖]Jc`̀lۘAIfβ{㽇e@="F T=J{'g!=f%Wvmŷ&}lTX&[&|O%szۦOiqyĹ8nF#ڳ~PR)6VmJlP:Ȫ+DR2*hCQi% v$#DVV}DP|11Hl$"wSV""Xn4jjɤ ;5ҹiTOrkF=vB0n2Q:}hsĂݮbU*X{JS Tul T~J06C}$X&A  rߘ*2 Psg"A!h' >V!vcDYB!Z(#D{}*l]J[Ϗز\a!-*>B3@-G_%1($RC}P̀C˯V "%t菫$J|]u3<,u FlA):? TVoem>XD5gdgXJtNCN !<{gZU x`^yi7íלo҇`Xhź΄_Eaf +5~F )'B hU8JX5^|fxgY>Re )< pyN<.ww-Sù5}"0t% ]¬9ʜPn-tv( w},1*F_2>(RoœǙX-G ,Ά&f3IV4=uo@r^xXk$lMp)s fkb;^X@X,ܤl .3q ӃVhΝg{QUw]~9㞝 ֑Jߎ:,,ȁEAJl8mYx5O|nKhp!XzDt7^l6tb2cBMl>t6YmŖи Q )FRRhzG ġp+LJ$1ȉc}L&fp!4enP%ĹxX5SYu_1qsemˏ]?zl{'k_~Ӱ n<򖣾M!$5XT ^[ [*t-f'-$d[lWiB&\͍w7Ͻ yiA;n|}wǿ:K=vqsƍMgLR1fctш;Q$le<%5" -ftqMF>-)<zFU vܡO+a#IVaN)^HbJe <ї9N%QĪ n<@At; q?VݠZS L$ cnR>{ϯ|s vO?Ï{Ο7/q%T{R:j"J^'(]}h91R3M̂bOl( iVnj|r572h 3H58 @&BPLܨp{{(cn~,eX ikcxw3zE- E>*Rx\bu^籪ù<PIeCI>eȞ+O|#/݀.n|}iL=Ӷu#լk{67m:/#E *L X-`Y$"(hɆaYp̳"+v [Oաpn4'3*% u] ҪsefF%p= )v,)n~Rdᗴ'MlK;IrA&uxWm*E@#^Ⱥz #"kD#!x6R;#i0C0RHw|= hV}1:+טC"f˂RPe}z] B*l]MX+ʷXa0nq)HITgIdpZ bviR T$k(~[M~>uUi"y>@)'I!彳_ߧզZ?K*Js R>Hi(^*7fKLTi?󑳃< )lnE+3@bʋj֒$QUf"*L(Rՠexp {v5[_6ߞ{2D'>im Kp%mBă PwS^GSWʭ!ިH[{pmQ{xn_w{݇mw-w<@^v@vf74=1pA;c8ϷS6QԎ,l3͏,j<o/dFMaqPKhP|W$uR)dU=a\=e BdEǏ7y U,')kL`%Hu EIf`U1mS:ewE 잝ǵkGp?q GB>LPT,l-UXa@?栉 +&5WXem%Y - &M./bXF*GdW@r@ +پc7䞷W\C ?>]ZBlSgyX=fۚ{> 4l' +4$YfWJl&0QN-o9K'`MvK{oG5&\ &m^zJu(iqTNq!e7dU91$P2-?0HJh6@EI&l1EuQmߪF^XAz>5?# LԳ[LIV6udLO65t^ mwn>;7MIP~SO<8p*_p-Qu*SC ftorщRAhnPc&v'MlD&$Lmj 4"U421(F/.7lïͮ /l଻o_wy;|kKF?yA2r@HbO-y{UTVBI;{|y/]@gɾ̓<޾-zZqq8̙3t񸗑Nn6)a7;`ゲ\͓*9(Se/Ac YeـRP0143_CP,LY|Ŷ Qfٚa1Zb`?^Dx5jP'_E9'mOc>U,CeVbf W@Y%pUD"*;{ߪ:,w.`}S*Ox*0͙2@HŽ~-aP.h2' & eQ EXv[¶ׁRp9?Yu‹WIǵ , IpD@( $P/!iGRC}-d~ c s|z\+sgU!xD-<5هe3y^f/%`ʄbsQcuVEV\KFS΋Pd)Y55Ob.RɅ)Z&ZQe&Фν)@^/5Kz<ee*l8^mX2 Uo*{6YuJQFRJz4+Mϓh18(1UXB-/wVnFLX+˵RDY8zk4E'Qɹ @xk%R#dR0KRW,GVgsK~Jb]\"%g(./88 pGl}yi_ Hxt8kOQvy ̰q?mz#7\spw{Hн6{,𹇆vڽ%kS6hҸǎKXgVZz:.ijSlTgX]CXX<"I7H@}of{mf=uOOoNv`  M,<2q7cR jlseS1;2==KqNY.K3c${dW2|(@4MX+E%MrNT@*6$4qzxouWh:s'ay0Kϵxz6; KnA2lhO| |g஛}qúB10GOe='ky-,lXt0Z݅VftKuk5om`qߛ #BP$Un-KKj6yml,#bl~cϿyTo5b){& 琽Gp#ƚ̞oMʐ Ld3of{O6t%"TfBXIRsbUx Øtj pm݆ {UקZR[QVzy=?7BJ8ۢy!ee$r>^ssu2Lq\i9"9!y$˶1iT6L.KDIrxDHL2`4Ɇ]̧^կ_ӓ_Sx~ZлO޶.}=v2q ![U5_$k0t\č?\\1V 뤡}s {F Ǐ{G7gp|yCw|/]lQAp&" (ҘJ1Y1fp؆jE^ bZmId eĶ'8ޣr h&鰺Wd{v@Pɮ-r[yb᜘ŖC(CjC~"9ޤ$%]roqWwϯd 1/]]hG޵9Z¥4+~w½?uI?VvPTIdSCuT4z-$՚S|B 5U̴$F &X :~ [6Mcpu0J[9EcRx׽s8w|KWw` 4>>?VSXt lTmJ\$z'oj^QDԓUe_m~;;]0 ]JpXr huI*zB!efϏ:V[ӈ(LJAZyl3Xվkc7*+;QBC1j"i+h Ck>UpaS# YH8`2JBVs/VCj4a1xuqP3*OP7 FC`dVʼfH"BYUT*zYl [o>xO)gqAAj8/caHu! m^t&~63h냃97O"#sYyg/yQч3nfn98ca>L<3ԄhN'AM$Sv f4>w͉,-cn3\C;&[ǖɻФܥ&]ǽX?M3y*FZ̧t|;ՖdëOxIhʚ$mbZTyDFsyۮnźCx,'k[pl%,$:Ѓ^/f lo5ZAI xbIAuN0;k)8sKz߽ܸ{|{ËW~== wᮑ nҦYYY}G1b> :?wEBW(z_Où2IAXo`۵ϝtt)wպZ&kl|̖8ri 5_jj% i\hAvЄ 6:Ԯ$'al)$E)-vgfD.ٜ1dyݔ0`;-g.9 p.`泃<S^ Og3?+vlb=[=6u @W\CM8.gݔ9F$liLaqZm0u)i"\tᘤb3+fk>K>Uz44!Qn>; `TLH{16YaoGeSFd$Ћmy>]H vfØ*1.ޙ`ʣ<5@K%ϵ ~'uz 7$?? ۧ+Xdi| ¶ tҷE{T@BS₦Id.yr,&dP3$QݘqIVpYՔkZ R`kՋVk4t05Hd(兴εۃuȎ:wjEU+y!֭Ivz8;Rܤ<)g [p-;aBxkwpM!6Xۉa&<g$ gOebD֬9Gy  ۢ aqqyid-e5L&+RSjx9ž~!J*{T:NcBY7&pd{9‡᎛?)lry~'lO} ;[E;KW!(sȦ) f8q= IH܍;b9vōdŶHO$}T=z W4gΜ鎎?'mSmz)NƊk+۲BdT.%tȦGfOc+rДfPXl8O!R9%g|*@cc5W̸ׁa#vN3(JA,|0 o`D"-DY@`#Y}ikqP^O(*zL[vma4~86 h20 +(!PV&{-(5PeH+ro#$>iƀk*V(&YH]Y#~U(|7* T 5Bu#yęNe {*TP'|1$xRP-fTlȋ@`bYdC NAC"Hހ? L=R$ql2ljI@!|V`V誉WsGGK[)  PfIR .I0APTaJ*Ty~0AVEۭ u(yR'ft+ I  rhLU+ kTYx]?sP<kPVDjb8y]Z*!?|zӅb~ Bqb(VlPC8^}^|>)J!#iorvWK]t t28o5l0a Vٲ!7)\ڋ=He@-RW3|VlL 1N# ۻkq}0yn ,J.Nrv =-1>9wߧ-5iO҆&=;+xhѾ &}Yc"(`jMjV;^gܹ-B˟vYdNc%v .<>ss^|m _~Ux)[J3&Ճ>S7nl!˥ JQc &LCKбIIop* ZSΐΝG=|vѵͤK9̜g{xCм Bl@7.z 9 ,4?6,؂;FxC&A rV=m* 1Qssws8|k '}$+o˽j#/&*aMcbg_V%.\w]g8;vMaoW?|hO\ù6[yp%fڤNT%;{G~ q1L+" +sqU m)Mʍ OMR3EI2q&'L)b,GuњOyO~m׏m+= -oid%D?g/J+.S&q)ې(f t6'?w+&49Lwu:|7{ l[Jn+VXi-AahX+I3)w,,=[ PQǂ~RшEv 8/+Qb7|R*F%v;i`dMERLVTe %mU 2 xM[,\ھ837z2c+/=0Fp XX@s|B vNlW:ËpaW^Y9N^ϣ- I=\K0 Kfo@3qHĒkJ5#`^xY1$Sى2'|j{3Y;]alޑ<Քjkg{Ï$ڛ?_C>+wg׹i@c>U R6$z]O5j͠'m%aV2q#/ Y6z<3u q~z|*n{{Μ9<::2~8 X0bd| H(dha\[szSŮ' Up'aUgm{a}t=e#~§n)6N >fB,jd߱RFCe?ERPՊ4~N5;[Rk: \niG:Ieiz+ /P$BXQ!ȣ+:Ghi#ƫޥMpT1QwDz9Urfk>T΂ڨJ!y^`mR.a# XT>"=)qGJ@a Vl'qTD{@(1{zd+K $M2d^I:9L2D"ݿjqfQϐBrף`F٣t$ 5=G硴݋u?q+Uyӭ$-E][+uZ3yZ u Ea."+eD@TڌT=]fd;"UHPc* zA*ŦhpP 4,"=C iThPD$^+mTRG%%nup~ X6^ P0Efd`3&P"y[sLfslCh54w W{@a  M`R."R,t2?2E )4R=0'k_fj9*SF.֞ 7!]7 li۷1Lܽ¦x)M8)t 9kc![Ef CoOcT?&PeD?* .Cm`=o-}kl{&7=_lhpZ@n^}y?eo]^Z|2m/# ?G Bfkjl E>o]PC2>$##N"2 g D7?pSԅkxm(_Zl0S²?.z;uQ)d ML`կnebnׁ纡>CX 3z6-9)XSH>A"b)gblOq1[)`k ]d&C~EۊOܚS'fpg<"_XAdy[k6*뚤ل"I /^R6Oka?P|Ѭqר]Eۜb=%Lͤ6 ȤKփ&jf*S>m,d̀7 AbbM )k><4Mg'/~3YK;߇]s^eՄc/ ,M9"k]()'19=^{r}9s Xթ1DE?; u}ZqQ M߯\jǘԐ!%UTYlˢ; ,j$%;^"~+.dbݩ,\[7nl??}2疰މZ m`go qo 4`‹O77wzxj0,w޻[ԽQZWٵ~ a$LLJQ[o d"I=V٨V9jhP+&h9 tYlXHuQcp# tjP5x-}q9O {מ7$Aj0YFͦ@L6?}[b+|]85Pvi*+-܂3 & %U[Uy_@hXBϛ4RAq | 'Nz#9#Z נ n?v1lj4.~RE8X:n0E X΅_ 8u "%T]lY 2!Wd">bQ9L=|t_;AmGÿ.v6ڮWXiNPkRk X, AbFru/{ +(6&lź_r8F3RF1d6!5z~~K7_ߋg$6nvR6}oC/q+yhFe2lnɓmkay 4]2~hPۼUiH z-E/SPWQ+P.hbWTaeK/Ra0-F ~<F$艆/̢}X1K@.4ȇPp< [ELqAe1EA;̛2+S0zBFxUd]4 L-qOihx]UAH 4JǪXHJ PjxLހ !(ՖzV1ȥyWeNBq|&QфZX+ŤRS9%-qI}r̷#`leV ձZxvW#U i&Ѩnf(BxP7eN፠(3BW$ 4E"dB#l?,ؖ3s7TUjK]! ItB G/A??v18l?lģS$$5U*{mNwι֜k͹VttgܹW.ǘc 7?t\~U_%B# P֖ jRҹ55Y0M\UzlbYg 4̯ɏψDɘwr_1`M+eMƞJte8uޖuE[n p:.<Z'g)RkNEP`pzKDcʸ2jI"`492,)Qs;be.nNFDtjwZ/\ VO7L8`Q`܏0D`,lB<2 =BH쟰[D2F~ҩdT*deE)D0F IqCbz[# 0WGg@ g\ґbȉԇIƗ˅2H) ? l\Wx:=]$t9;㝞n_i̧Xf`toؿxwC߻ۯҕhA~{o}ŨXF;6wt>b00 H.dN꥿<( *J]ݳ?}#+iu,Vͼ`c:u*釐57_l䏌c0{":f,?5Iт+%YE>~TBeB2{(K\EœxF!՜N| hm$##e{C>/&CVp|&l/Lcކ_~<2n#b (gwQYa*aL0F]k82_-p0;+ *na Bhk8gY`T~ӷ6 djVoϋ7V'_vGkxCKxpelW$JD*EDu!YZ018P{fxڃ p$f P=^'&:s(c O ?q/w \8]|E؅x^ 3اc9(^3FyH҅ B6W}- e?9!.>YEYr IU2 >cJGdA)" WB:HQITb ]?ɪƂY :齙{IWsqs/Du䥫}C]\Kytg,!te4¿o߿p`gaZE[dI)t~"I=9sUZȻu-Kf=ƪvZPCR~.Bh?&"}p(sUttVc浂Y78Cy-%x{m%|H&+7QG}o~guw 0Wǡcm[=IַS/9R1T9 2h>((X\KەK=1Pݯ$3uөSNsObBGۖ2xvʹY۝9n(V;;;'0ºCtUXp,DE FAZ[LB=A3ki,vX8SsHB*j_F-U=FD‡ꉼ7[5FNO2up*R5[ʠQ` % LvJ ؓ ;?T@:EJ_mCɖ}ٵC"<))Kt gK 1F25ydUy8d6"bup&l$h 4Jy*` USMOKV-6+5yˍ~A2FUcɮR֦fho٪db{gܘ0c-2 H2tܚX?,'i =>;74Nz.OH6ʕ`CIrb[6&v:8؋}e|+bmTv=s;oDٯ{E8lHs 9̶ml*:H# 2@:=`/e !RaDm *!K;6'a`{@í6I/乌ވr^V]R0@ɱyDl"@a} ZHnp?btz97(LNʗr z?+ y'9LL 4>g:vLm0 2%SVtùno}r x w4-:*hԆ2i|0g0؎XLeYEi[S.Zⶐ@nø,-nnv//M;a<zcdu^T .R.ˡXvHI;u׸U┲E5q.eI 5j%)0Y6i HQ%eWtaw6+/Yg| ~jcMJgA0A%ōlyn#ɓ^F09{n>dw1"Ii ŨIʁh&ؑB-U*6B"Qź/^K" PYJD`e/AU&wdjq] j)iNu#|yȷ]_% c2A ؛>޼s? u \:ƹ%Ɓݝ[ΆaS`ue뉌Vp [FQ+HfOU hp ۴3VWMNE=džBvd\zB}M#qzVŅ`5ZU#۳ɫ}< RM-HR8"kZqsmhN-*lM]޴U9@U|w󨖳q",ȚQKF5Pl0hŊ .;/5Q*hykV+IyUrϯ yZ{|MU0Z+JE f6d#h>%4{ƾh^uGLe4<6!g+-, %!ɶRs1 XD阋捷5CUZ.,Ru+*%Z:߻fX gbHV^ZFKLKȷ͵TSLdQ'irs$+. p$ . 4;,C|b<&[ͬ%dR=Zuby\^Gptt,9\z/NQ{ƨ*Y@̺a >3}'v}x% 0Wv,D"dɿT]skd\ϧsp3NJ WS& %쌲"'$1h;< b-2Ia٩<(a4c3@JJ`Ɋ)-(Ug_yR>'HҤ Q%v$B|Xcn X:QYӣ]'eu]@2]oe.^}xÅN%IV܏IYhC`̸24F`,!;:oOLs]l vliLW}Y<|"Q'jmS>"HO[tEjC9n.Gz: uS໾i7]~m'v:O) B0cg,aU .ǠpMlٵdCϥp+GJ5ܓ*yH#9}\_x?gv ?|"w1)i* [XFA$8k-d2ZA,H!sf X%FR0)3VEҹQJ9qkМ.NT $eO+9($v]fc7XI?EЂ$2 I7 8k#\Hy' qN!bEt^9w_{?L^`3HR4OMB_+Ħ8vă[K^&EЀz OY  9Eo6C8tJ=)uE =f[G"JB` nbKyD9kV@`W:agg6_T<|GΓ[ܩ`nN9JpHQR'5<3Ć%~.*ctXk C5ݝx^IQi@~ndY1Sf-Zu!K`,K8=F07)N#y_/¾͛w0F"(C i͏~]w @1 Mm ! D% dY"2]7v}&7"ȺH9 m; @3!QDRiz `'RQZL#|M}12&H0`p̅.iQ!fP>:cf Ikt]2`f;fk>o\9$;x3a'HAh-GU/!fO4+j$;D, sV9SjW޻;7ʥKZg2;LvRxI]k,yIa!$ޘ@;C3DInwQPD5O/ Zq[%tL,X ӧϗ,D%0M.Yg-$r0a6mTT[tyf@x~<;p >!_~R;%g+(EmҿOEA<&[eOUpi댵DKU'-\{؇9 _őGp:jOLJo p4'|&K;# ^o͘ДHZ1!) $)rHce D] gD(L'+V\P(H)`|ERoTU m߆pB^z7|2%/]ʊ(,[[x}# 7򥝩paw W'‹/|~xbzUl ~^)Sx׍AF$$قy4@=XN:PSK&%.9Pǟ1CK!)*5q !+l;iARН+􌵾ijrb$\ǿ|mZK2zQaZV:ݣu2I[VUa.rXe'W^y[Rw)_iZp-~(70I֓*Y,M *eLU娆J9ic`&eV|'x+K-޵А uymE?\V> 0kQ Xij-ƳRK`>#6h67G6RZ_[&q,TJv,VFҐT5l %8[lTr9*7DޮLExZ rk?u8KԤMӳeykڡ3pfg cr @*mV5#PopFT`![ohgHlBpĘ%V頛[[fmxiVRMG?nqPwx;V <EŽ&V 4^c[ETR4jnj`:2]9&%Բʒg sfZ7dl?aZ5Oh#3vQ܄$jvg8ەf-' LNC+$]3ھ8X13qpr|n#kTRB{ ${VRJ,լ]M~NDF{7W~ޑyf`jJlgٗT;QTwL&lbѹ`rb-įN\ڢ9TcDLwv|}M/t7}=jV-!?n 5%?'H_.S[H$*=^N &g+][w W//Ex[.5rm:-dE$b–6^r~)aY.UV*m*ދmP<1h+1HXKAE( ȥJ_%=F,rA"*4n]!]@]Z| 3G[Fb1)JR]&LE^R(cN3(]CSMܖ۱>ΑȐ8Q9)(VJGӵnDnyn O~\;Zšn%\䱞fHK9iLq^/↿Yd[aIۨIٲKvw !5LZ1o^zL-.Ǣ3@~{Kw @~  ?quxo'?q?<|eoS[ԛ)[ɘHDr #Y-r&eBM?Y!:/6Z5wa A$k39hcm_ |[`E4Mv8/'RR :&B"lqvf̹Ub{8DPf]x̲t g"y|1NJ;0 6H~tH(g|9 iNo&f.Y% 8.^vw(טU#TPlٲ}|ރB"4xU8 k??߀Nz?5O\'9M_IQ~:OX3.}oK˟ ޘdz4]+o9x0#;@akӤ=ZW2rIKvRSZ6e:?i[m_0y y k^"С_d=L&Qk7 "dc߯S0aT*K44?k|0K\8p%GRե4njJ;RnK[KVeۿ ̼]9[hѾ8sϿ.uqw޹s9gf֚ږR=L^h۸Vwwwߣtq{o#J Ơ+B|[ FtwܼV?;cpj ӈ`rTcw -FУǽ 卛@.yVz]h\@J׾ZhK ke""[]=UJ2Q:EW2!fЎڳ&~ Y`_.M60>73VUο|Υ.BHe"fhu;f@hA]Fefq`cT5o3DFITTfs'akmOj"64$VV2g+Ukuj\HZQW/kEQO[N]}|u VS; gr*8cKX ՔrVf: eخ sE(9=b!u?ZNطAWM^Yɭ2s|(/8b]O+5`h-0jVi95഻>|\{T0 >%3"UFFOjpdgo7?Ͻx4|ɖZ~{e Ω&Do<飀|RԀ#+X(&PI5J8K VN).nX;XK@ &>BJCwۡsfEۊċr>F)~vR[9U:I@ BhY,(:V.13!h~ W.= 0ޚUEgp 39')oTECR}䠨p c<`g?}!t;p|Cc>bUv7&htCوra\Y-kނ`๊=:(A$K}lElҠxtJj)_)d]rc`0%CP[W8Ρ\{|{fҼ =zg/|6,D'}=;GҬ:>G3bQGۀUpTK>+[O݆[dе {މY;LIOd e*Ts tԛtDm:)$J VJY%ZBW !=#m( jdwq&e-LJ>e󯞽_4M3vݏi~#؈= >(SnDh5RsRfs}QE,Ks4*<DN}$W!Q:mQ h-F!`= jDr^ȅҦ*IY̋ˉ๤ףWZjyr`2bU@Zga?*/Z˄ iEԠ؆ԙ"̴_E4 hT8! 0glw~l32Vík\g80w8 V1V̡!>p! j4C6*z5f.<4csr=qM3z31@LU )M\|u Z-]6FDriS*˭=cC]wʰjG72Hl:C_eX^EZ`A63kUs~#]iM| IyѪ2j+/RQQF)0!3~0"7/xTЌU -{f{,jNr h*8s_hnwQ?la)8ٴr6Dbu@dҍs |;1RGvYѪ m2<9ʦ]s~%+\WjBa[׵RePSH5}[^+dGUCJd[/\8L$ ^^G%7Vo(Qt¨{#)yvܹ{Οߏ響 iϽxny+,wGHVnG Su9vdgY;&{@F$r$ۧ(~b)Q"HH~[1Yz.[ON(ÓӐA%@lhM) !!)F,JzKf5I#}R?ќ ;'l N*SLP<"VxA *)P7bKisr|P;dĐrDɥVqzKw]|uVR'}?\g뽽H +e$2Eɂ>f6f0ld H;vNY`5Na.aow5;up0|8ˬamzLaQe|ߓH3ӻwJ .)}4 /<o:+787MW,W~kO}ֳ.#ivEUؓIJVRA M.-MA u`w"%_ ,"JTd8WC.Cm߆g@yk ?S_IEU=CAu JU1 @{7۝~R5U#u6GkbX9%L>~Us ;casCXCШHʐ,?f*A2Ua}z Klf ߩRn8Dʹb7XTVs`sB4ǐt¬2kkVK7:+)!I."ht+N5A`ScIW7zlZNW<d.R.Ѣ[R}*1s5NVqst9kP[6ҁ o(m9 Z V+chї r«l7碷?.Dv4<'iYuO)+TlzvϦD.jfAhޤ#z岂6Ç+Vs#OVɅ%Cd4*˯gKIl8KDͳV2Jȼ*pJ; ']b8{r!z $@A,D0}סQrnl4)ÈN!N'fQchRFA,"3}RJᬢʹuA׼iQJOQ}>~&%-S6ellLm-"h O}S\p~p ?=~H6¸Cؠc fBk~P6qw]_\W%xן_4uݴM X n$-Ū3?LBQDA"*qJ)d+2D/.(3EZX :CR@jAR\=~BZG]"=~x$Fze켯Ȃ}TW͢}.q7MX]ß}q7DF6;øCWU-5JZyo>vݻK_G+z>9<Ӄr#гG:QpVb\njz52zF;XykQ 6ŦUV-q;:4F&cv}&oy<ԕg`>ZA] ъ\i[iЏ,W6j,,IWe P)((?_e9'2K%+M[cSd(!mUW@Y<<>ΐԖA4so EuS: M&ձTݹj&cۺwn!kAٟۯ RL%|Ah$^4k#ngA{ua ^'%BìJd [fI\H>K֊:+³y FoaM:|~lBAيl[,K!~t]nRbW}&y+ڿy5QHŭ*8Uq&44fCjlFy`128X I3 %0y4sm7d4G9!I?LkrBjnmLϟOZNﻄ=ߍ J Z:xU}n;w p4XdmdnSx"P)@"+Y# 7̀|"  kh4?@mhuybf ً(A!Š HHh/up n:57aݏӷچ[˝5g`3&,@U$>UOlƯcϞu`w녨xڛU?ZubX~bKn~߸?M[wN'Ƅ7_Go ? 9FP[3! ^JFOUd%ޘC7ˠERhb0Y)lפ*2"HUKR) h3@?l?,p'^t77tCQ{I{SBƹAE[Ƀx 2_uDETὁf>[lCqUVlg )>JyZrV#jd%;>L65yXi}XLKk< [vk}| ?½N`!C.gs+r.tɏ__]q ' 7[R|WlUs.BADd@y`2|uo[:`"uKI2j$t9L5 ɀzE@seg 07$OF @UUsέUX#(]U}rJQTlVUmh ;D BTOl 0GGu8A[PϏ ǖG0l% UYgjVIkv3XA=D mzc=-ySC`6IU0:i lǪ#CdHG2c̻>1"4U!RV1HKaVU#nj.O,ondgۼ:TĮ jpuۄZb @ڮUDW8IW8YC{G,zܮ`5bCP(ҢN+DP#T՛zRzJkIZ \1ķ|V8ƀɛlc9P"!ґp|!x=U#N1{$Z1J\ X{K;=%PaD dQ"+%ReǶ '֚Mul0B0FJP"۴ >DÄV 6E]/AscUVR*k!|L&a2-vKxRrΫ@sHDv^$NΞ~?::ɤ6/禼r8pp^5AC''[J2˰s{鸝Mȕ̅J/jɤLk& \`лӌ.Zw]q\x.[D+/ J _7Nҕ>ͳ}#³X:}]R'{N ju &Q-_ܪdF_Yk#mZ"8eR_B^֛.*@,xnd2K$.;!9;˸h2Ah~{Fk|[W`l@i\nI_'s* 8_04ʄhC$osbfjؾ A#@-tf"XBYb;žHB,? RU\O:AMV6-}c"X%(wT% Bt'*BUbP{aRHjHFRh3gܸ5]U'xⴏ:o;gTVlRCt!c2= _wm(lKG!xC+[܉dFQ!>]"W=O5Pm<"(rIןlp/<ům= <98X0cxg!\JUN=>]?IU\x:}R5_wX#M?[sa;T-d(8 T˶a2eK+ RjAFR O8LŦih@C0`mf,y6UcBl'G%[PSHyb?/`m5n\TeSLzCg'[g/UQ Spn\mi,)X7Y&~1>9oZgUJ+MT9)"̑B3tl[ϲ}3Iؑ< XY `hɚ7ClTDRst TٞRSP+1:+TǵU͌RTl8߬&k#Щ.q ]Q}Pk8,kJf;@C*57蔉X+i9k~e`լUws15Zy9s3vMb;ONۥ8 wŎX20kbzMF'k19sXlufwϭ ˼9=%xVݙR1Y)gD QiO!WƠڄu] 6G hD ԪdD|x :+IdK"٢vx %K6g%S&yBhlUH1tr7n_+pr`W`slfZ:ɗ+X, #EKU½q'E$8L.GESxyfQ0tM|8jUr!j6ٍŽ-mz ~w:Ϡ(Jx/'9 ѡVY3-0/]{H@~c3jW,1^5 lUkķ_y.L}tzzoKw·e KLb3OxS0L.]PReXbUE~TQTu )jWGKV;EBL3S|aέI̓3| 7/"N}` 7 t^{h}(b{uj$KYĹh\<, p`ڿ8KUKT!i "4)$sDr ٻ,B@94o,bvILDXvARZLd%DՒ+h}sQ :JS hHFea% >/xE*cgJBt[2a /{P\^B֢@ۯo/`ӟإ霎D:=Gh6QlٷH]Ff`gzCw~7 ^Oְ_ /-oޅ0O}kOTj$$rL92P2yd[ɐA=f;2_%|emRXh!Ik{QWkbeʓ8Dd{[1[ղuc%TaOwK^0{qزIn*rh[f叱,|k H{:o܂}}߹K]|uٟ~ ѳ6lK}fU)+{rq^tYM&rL=[[}sshlۜK#pr׼T ZX z:օ_MISTɉRxO)+WmjjDƾ+[j!O !8ՙP|;Aoy2 p?Y,yA}J^`225&RTف;xZICZj[Ck*ǁNk_l@aa=?|΃ֆgw6Ѱ!l/s9$Yz"]Rd_z|1@Z5m'"&d Us[-t!:CB--DJ xҊ'BNAӿONO[LgfN+W-kզQ_C"ٵY3qush*x l%d3bQ>U*J`f,%\qժ] ؋Y$>.̈́ڣ1?L…H.ZRބc?c |QKJ3)vwb߶۸tHkq/s l6)"8<)Dflgl(6d1ᤊ@U逐AJ`; a^,8ۨ9cpn(؇G)9eJkAl&V{%\R*} vboF)N0kfHJ[@egGc0}-vi1AYe&2%{CAj^!K[ɟ |6acϿGpQx If=&u/?fUwLұ"2yEЉiIk-ֶwd&6n2v6yn}QQ$ؖbX2st jNL_9KLk,'./}0+^Aɏ\'߃n<9kg>q "IB9''^QPV|ѥɨo^|e[\ƜϏw76k"<絣ی\)*Hj֦yE"f{Dj,3'PėlQJs| L䘕S>B)\ٵ*=G*wbsS[N3GxkO[Ӟz C :kt /(*u ]7\qnG>ǿ|O&z{rs>ms%?a+MzvGR>Od+`$Qe!O@m bYQkc*nkG7*/y1ҠajokcE$anq ,O,9hMekRg?-ƹw \8-bMXp}yM@-Бş#V ʟ y srf'S"2vV  iА1U>\5&du}ΣYڸ΄ xݫ;?V׉&f  ê݂Mҟk㡙>PZ &PeUnSX5rjFh2k4Χ -UF\ڬVq4|dwQO躆A*JYtM`@Ɗ`lnP&G$1IO;r]SuŜ SY7+VPƆ[ 4?>!5(Z|ِ)+0$AUz79;y\gɡ)7/֒\ szn!흭]1n_>3k z3>VVٝ0[n8t@QC)Hdxpp'Zo H33l)ءB?BM bv!);0(QPz"-ߌ+"P$ :&M0{C&j|>,*t\z.~ߕHbGmX$xw$c^猝doA6YiHaɝDLCx%l^~'ɩ#JQ&^'l} Z&?-zJ1˘s8&Hɓ1M l(lFbuoW,c{L)y!ul"L)Z()ʧ@|&* ?%2I,vQCYiUVt͔LZ+-XG!JKwnou}nӟKo MLǥhvL["J+edLw։cg48??wXy) ?=)7gˤ@n7 bq^kj4)6-$'y(K!|>w[W;0o~'M6\2Cs~XYE]gP$"ʾ!sTjX&<<vZNNl%XyVR;51D~"J7BJ)@ ȈXHc Val1KG*)bGM!C dtf $[(!$+_N OӹsFL(3ÕHRbsPI%*cLY?zkH%Q54H@QuKNWD޷lBj|~  yVsR_x}e սX̂qimFeX@)Zyz"WuS Q|3̨ntL^3+ᩚ{gn[}1{aQ[k_U{Kptg+jpӓSV 0 Rdviakl꼣H dDX~ukj _x#08eKK LqJܒCSGAd6yW`IʕE&Ծ(۴@z,9=Jٺw"àKвh6HFmWRqs eRFD) )>[{@Y9 4laNU:e_7*7MTe]lPHa٦M+#߁#ׅ< T@*v(Y04.[8O~tϽlXs?kꭩoA2Vr]:F+g8]%㦓oa@וe%fATLr^ mk^O"_b@U)f#% eF7$$%Iq8c$Xg8 䐭px _%?0Dm/ԧ,!0v]|Osы ADiZ< ŤH$h&vA8Ɏd.VUYV3eWڢ"O9Ƒy4B y 1 xq)=xV͖A*cjqUW8'uL/&>(s)(. Tc黴ݯ.̴Yn'Y0;`˶雭(y v qV z\zw>0^*&t2DOZXJdEW꠯ųUru QIoo&iऀgy7V_59t,/X /W%=.>@fGkFk]-fOP]qL@ H[MeJaʩ+t{j5Ad047pfs&;WXj6_*6y|lϑj]ն8*\jL:bԒc8mg zK4]~r݄۵ھgg:+--C$JiYu N?lQٝEzÌn[k39Ffrxqvؓ@sivU.1 9ءykmu6Itfh{*:E_G53$*ߴ9r(gp[1X~x?s4=8 Ka6~O(Vp8Ϥ1lgHJ.ls;$k<4X- c=rGXQw  S B1 @}s s]X%%2dIVw$XJQr=}?'u:/3OgU5$Ǯ¦?]$4Oe8!۟O=PR`nZ&0'Ck65RRT"gҸ'Q]!p3A8v>^%<5o=p#1׷o~ 𾷝>t1+"9rߎb̼VzH".bQ*JHJ#ά$IJ N 4GA3rqF'#Z,'80րdTFUGHUKۘȼ wo\OI!nMϺU۔kUֱN֩"=/a2-#y&XfvR$tH@8[3nC~i f6F6i !+mbiN{CaUq}KPd!AꞗC h6lerC_q[~o9Hm8܌BF8$ L!|\V]dz>?(>ш_|ƍ`pPv\PCp]PPZbIΨy^+2AƮP=Dڧ\ !oe4ոz(*]M扂% AʡVl[5W{t؞e;RפKY917'+D٣߿^o80+"NI硳{=Yۺ QQ2!'8Op!\xX%.i=gl.Z u VJNJEGΫVD2cU*=h֭^: {Q&RgBleQT0`Ӧ"qK~mtY}rH.L HNŢLR!ْz`pDǀ};E*Xq0+ !* qCFRTlHօVZ\-\VBȺ^1Ex69݇7lቱyخ˷d^Zt u;SkiEO㑶a" 7"jeOU6aܝ*!wf|aA Zsw? *|Z ، OBO/شasa$'R6x% ېa׆HLuMrf:>#/;C `m,qvVO_!-:?;McC?i+62֊N$ˇx"0_wIPQlF($'g0tV7ZAzc'y_x}:k*ܖ%>eKKQޓ,2̲ɟtߝ쯼r }.…ݽ i|ªLAL013pc8gp嬃?웇p iƱOR$grU~zBF]GGVd2ĺOɤ_BՓxm)OXčy2-mA;] 61'0'"{9盯KWSϿ~j79)OVܒAFx\ٿ?OZD40-=kY=;a 7O,foKoW {i uu;;wZ 5JI$uTJ;4uj k$=(AI!b[Z< 2%k[W`,UrP-֯`dX[xXɒs656l*kwz ۪aޙ~Xj>-F!ܖ"r=}&5@)i5mIXBr^&A!)sdkD[.:?.4L9*hƆ6 6ARU=ACEUQV~71HۂbWI}O*﵂)ncT$X<ݐ#?_ytLP1\v5D(!<9/&8EKz Jm$^n>-iW±Hйv #(>rXe1Nr0Y-)G,DY`\-IGDTXb >Ü~~Nv7?8l͢g l~agxQkTZlm,6+ڢ6 du%ucIr<-_<5?m}'_?no>_ُ8 J0Dl.9c07F,gb&Kl\$qlm?}ԝ:?S\OZG=o)d,] cM}Mz:L`6)bt.JY@b>@NX}O Z˿OrSN 4hTjɆeW^ok\~&|NMO\瞚4֌<>)80=2*V4&{Eh:t ˊc(RÖ5jW[O|"` C3~ ^0]E;b Ď[ c~?Bs6h1+m LTǬ9jdOjca7 JIk`I@gfG/(ɪZ,A;G>{4\BT^>'#o)Q#T*<\$iE=G#|+89x'œᇔ?~UcxeϩWL.Wpc7?/y4Ζ?xw[^n&dR b; !fT@ )U>X(QoބϑK*pRdmjf%Uj M-ɚʬ@RUTA6'T3+ $Zwk7ַ@tB]b-̿E\ouKe^5ȓzK[Ԟ| l>RfW /J̤N3HKz_sO%m6oFhUUh{JC]a"Ū B_&ǂSF \iBi;:Q,m@)M6;t@zjۜ2}/@ukXFع+-s҄n^U(-YmiBŮň dv\qRq'&C|@+sԅE4Af0 (w=k氻j!{j`=HEl= tuHȱ۴BWO8"Gi.e) )Xf2Ϟi fpH) y,a{YH<"c#yEI1F' E1![:dPF{8&EdZv汊E_yXYUߟ<\C Xx=KqR :8&}YU\ ~:(Li1oZYƻ( @4EBrg-{X-:X.fCxx“Ruxo7_q3d|û]_z>`,#7Xc+|=+ל"i8DH΍ΖIƈj ;ddQ%Opn"wrXIIĐC @οnYm:̪P0ä n0^<,_n%qotV˞wzҒ*O񮼒^r#I Ɯǒm;]. Kdf\e5L ^+z@'#Y>:%U"yEqiUakn'ϿϮs/òڐl!u*T#/8CQviQ(vbZΕ\Nx5|◯;Nׯ_/^??cx0,kwTząS.4!٦FLTiT`jr6f-2?&gލ|7Q%@?:B t]Xuʽ1g;Gv`1}7a`G,DqשbHӆ s{/\۝4 cx/ϞPOD)/G~WXꢇ4c9 nhS_Uשi}i~/ѻv-JmX(,>+D @lT[" [O:dU>b6U!ݰkE{3)~]e?*2m Uy#{HQPQ/F8Jg6@\Bx}ֽko]vV;PsJ ܝ?g?k1Y kd ϊYIo$F\ ,76m"XP5WdbGQQEҧ!L}0 ٷXIn'IJۓ@BlǛj.Ng;0!3{DGRHYQl`,( 'Y;+ys8 <cـ`%D׍c06q`4]P #n/n|)0݃>!Bc><(& Xgl%} B`O$(6l7\GIE%7άj#ŸmyLuع6#A ~>_wUw߻}N,#5$.Z@)FN:)$=b51*`ŸuZc0&dOsLgqP/ g9CG=7N|n<øI t3=ߺ|tsHdm=jO=y'يLe[bRq30Ǫ&|cRAۓdE$*06Tqk˄N=!q4T.ߘx M *S@XMtCLsM *$˹m~i|2&w$yhYO8,nK֓ɲXVɉZOwZBBV?+vm1rlS1U>XvS^kx0o'~3g>dca73y͉ݣċ}>Se.fOƒ>dzt}vf9?}:c>?/5.?3pg*\ReR|֍jD9qd 'M#V8Vra|gHXBnݨ3xAH];qHE^nJ(+\Fƅ%(K8Wg~hNi4vB5 ~vVϗ˖gGniNpt/,WWB_jFSpzU  G ,R=AU9E`[h`37vb[JK04=`!@E\Zky43` jBZ+Q,hPA1*[ގ-H#G|ly2I>! < R@c`=թĒ jTV--)e72ߞ%g"4Ը6T*FYiXiQ+t RֱȮWV [D y]D]iʼ -"C;Juejib$X=P?LeDߣV |52tDVjZ*sc(&B(ӃO&x4\ΕXqٹEsαk[򔺩QiҪ-\otU;~sB/E$?hhS"XF٢`5-TpU llӭ-,Ჺ5yMb0{Wu{ODِPXF JXVaR~9ǨOJ1m]Y#y<)`;+m$o$4a#J~:fVeI>;z[ :'%'>q,j  '"x>cйϵeWP|Q`p< !Gl_paսr#ml ԃ˅.@4xN<1Ð#ioB }ۛIYU-v,=pN@Omբ< [8gs6=@ >$x%۶Ae&aRfDܗW|t 4~J]&R6 AU2ul/:Ċ?JCȶXq1w%vDʫu^օ(JDž?ܐ\F= >qV.)(U&xLVXf{kǰĵ+ O7[Q'*`sxNe{٧S%L#v퐈 L-fru^]sg #8zhXgO6a c][Zf:xp<3Y*.NY !g3-+Z.rMq郱^8qΙte+3*Qql!/Q@6\YI_Nc>}"xf2CJ:lFr欞@W3>7 }믣ynjt'|N|=#\!?Aqz]AV P5TϦ҃86 |ϝr@;wO@EZAAUG#4a߹Q)XePVRY@]QOT+zizʀ* D(-"bl=dl0?iC׹EvB)I1Pc: t &\Q]W cfQ%Co]=ǞYL3إJ{L/[h#'k XWY+K3me(h4U*&56uBh)!vyGl>(| 34K-%^ۿqK8vJ.eL{':a4!eyEbUC^ &E ͘ cdh:kGM)+{VbEtn^1 7/o9'8 61gda=*1dH5)g$Pgdv:.pENb86K:nVL!J,u>]:o x;+wvp?ss~O#s?lœ*Be5BL, .Y;CȲ'oOUs.Eճ"09 .uc|9/+O('_/bgq+{k, ʾ`\7}GC7ǸH[jŘܹӗzS=JO#8R[,1c3WLu XgpJVm@/:-x5D2TdڠUWbj$.Uu*/DVp))+A\ed:mҀꚚvE8}<`-y P|\Koa޷J8ےBV)UԙPZ-BMʟ3B 4A[iȟJ@*p+YY:>Ks b레41SþV RwiP\9P-/Q1OTk | ll&6#x$o ȼ<*ݴIėײ"5Y>ǔ!, Ng83f|[FŽ%Sm%[6dhB+-Owа*d%4O\#崽QqC b@5+\a% Q$`i0iӣ:QU[4)IQhBl+")Uafy^j/3Ԉ (*ՌR 5 Z2LefkE0k0,-Rb *is_O'ڵwt-Ͼ7VdT;G $or.4+tk%%(ъ& @@R &e!k;!kZ:&+r'RLƬΐ?bM/y||Ibk#d &{ 9}2[װ*W:C;0[!$n+xvwW:|?ОGG&ݑ&-J & Ò#P.X)y_G''W`8=:|v .Qa""A/v'62 ݅{~Gk*)_GC w$gXlaL*i֑dD* 0"| Uֽ2 6\h!],|"-zJdݔs,5X0]7Qur^O}gX/~)Xs4]J-UhW 2&s}V Vڈ=[l]"{gRKc{G eFJ'(! vb`bÇn ;o-?.ruo_?|>[(>ՌXy-N dgɸ#m/=ɀӮ9^8$gY3\Q`붑rKO2IEꍁN-H6H$,\*xuh-ڿ4X=: 먽Eo$xg> [.\y1 _ĺR3 &ʧp }Gz+byZ;;}$feE&uCeDlfoL.ŢФι%eYsceU߹7 +XyM_8XXwsLDZjQ f]4c*%hQtQ$16~~ <_=~nދct.dU9Ne%"&*H:]HZ%ps[>$OXşuZu%bE5mTQ>1Uׁtmt*P5vSd[9KDI"5d{_t2 Ě)KҵB$zNaA5Ɇ`U*&污]W8%iBTtUs % GuSCP)"[ s/CݶMBBPq+!)$CD1jHUg:Iͅm o챁 Ug"L!]omNk/K`& {_x|\lzjI]L!SʰeX=1(L[3|Ru?ۨ# lsN(kCO-Ļ= (+]&-}qmȨsqJ"^"$%T8>Dq:'fXL܈gL@ WFIi?D>}/ֿBK-Ĝmr?,a 0+}wl3-*:{X.pR,_^3Mؚ:j^|:rd;x[n4K$^ Ev_]P&lly7VC hN6%+NN)EhqsyM'G ƒ#a""F)Xc@rG=qA-$\^gI R;D`R)y {{Їxw;x!ܸu E@X==Bdvl0aqX=69?>0˻sAl:%Tó-m#[ A`eEǓ!VPJޓϙmaL@ϗJĎdh=CU cVryИlJ#wlDZ/l!*̑d;L'Jvz߳qkk6w Yæ}Y5e "(e!W B>W h;JK2FeCG0* 5;pÊ/PuV ![׎2~׭4캔ηC> W,`Y<6~[嚤\.̏;)\1)[%ed'*En_z4Nm~4nN8]d4>B rWwDE(,gYbZ¥Od X [95,a|gH@qZ]~ap46*MNh@]Peg,Glc(lTz-L}NB2cװrʤyy%(>̞ͥ0D(; fPD!\)O 󤋐Fccql%H4I*lzUCrm|^3c`[0ly;X9Cdp< "恎fTS\9; Wl!#DYUQ`nܛÍGGpu.0=gD۶l!S.Y*yF­=?XqzMǫ"I3&x Vo>YOύrܐ(+SsLddž ȡ(sYw<9Kި,zB?7n/k>k]rۏ}>13LHq~j 3v];ၻVxX>xZ`xsR}:i]ˉ.P"1.K{ B@1Dte!% ӵKK 8i5.zx!а3g"`vQ@9۳X QԊ ZrCDi(*dt.﹤amՀs~ 2B+rcglL*q*&ιbSf* [Emν ~H!™b:Ɯcp28dR3Y@CZmmm\" "NƤzqZW"7XoN=ubG]Id:>c"˂G];g(Xr1AA֯T``gS.HHdYslT9x6RdsrP*X}ɜCS0AC{WN!+>h$=vyb9o=g qd9Mccxn7)t)y޻;OKgwzM,@It # 8;- g*0_*J(1AV9-CO @h5¸iÂ(3lA*%*6j v+526l~nC %[)&TQVTmfe;B[TTr[UQJ `]LU[#]͟Ĩn?M2*yd%TR,Tbr(´ /-KE*m`;ɠɛ2L*J ⲪJ_8gᐪj3sCMSSZS͸:*[ n'z'[O{x9nZV*}8.jgUA#CTR려j?K86P5f 77jEE1[5V,b@֣B\y 6>k!ۮ!R4OgWHI@?!J"h O<YJ`wd;Pvn9ɱxc~NM }DᓯQ 6[ҧ$(^XU 85_ң U},&W|3Bc6F=gvճ޼+6yLB@q2"LQ6^It4Ua~bX|v!d@V[#p@UN۞RTfHNJrf%t\>_qWO ЅA$eߗ`qnA-)1 `E"kb'vI1&y<H氜-۲9.`w8+o _>]x{ ~w:.(@?<|."LY{i[ǔO+1qHF542!)po!AV w#]3@Ki L` 艣!(1 lnѹneTY-2 DAIylF~c2(--?`B֑-"mcLQA-UL#y=Bckj?dy>. \yyTn7Jm#w}8ZagR֞,+;\sAtVW,PChg~xYPv.ݷ'5FoT8g"8g*jrPl(*)dgqH\\$;e9y!| `+.b%'Pvy ztTN^Q!wK(e孃 c֐ԥT?Yl,X ʲͯڼ|@vaYCT8x,}v [pgODϼ1 nou!f+JΠďAmASc<~{7oPӃy$CGp;;t2,g]A{ vcr{j/\B؛ZڪysXz G7^iؚ}c -[ޓơP='لl9C VU s<,9h󱐩/S:`NEU 'ZI }C| H#@QUl6 pL @mI:Ŧ|f=U&߅J`fUC*-Xs+lÂJ1!&עVc@ A)Bہ*bAmPAE+uEh향+e  W)h "!*r`Ke&f q"﷌Dyqv1آ(- MrP[@٦-*L}\Q]CPeQURBxZI[*RlT1⸩N6%=8X* ةڍ!=#Q5ߥA9vktz\Ck{RT*m̰P-_Ќ&4y\:*jZ)sTـVe~<́~(r%:gxMɓ=oB %Z*CJ2Rك_lQR&n+*maE&r!)ӑbCWQTOẗDYA$X )n11X !}1k#'jNF!*]x=9Y'5r u6mN:fILYYǨ0KBfj3BV#⌑~@Ǥ d7 YP`?\k;%3{]TDyH -Ne,]jS+"7l13FPt6KlGwN܆Ekq*ggCv j:ۊsɈXDĄ`x0@M!J̔j#iIVF[<lu"쓔}Fgtx ƇǰX,| \=Bso欠p 6}=PV-v 9O!Tcs W,g[qaJy>LJFC{ML<;g/)~w1mk=omn,/_O03ЁH,>=L< +u7c"Y "L)FALjщ ek9; } !}C\TcW1y| JxQZbj*/+_s /9 *Ybх[pv1!wp^d5ӤKR,y0%x; ƂV, lIޱś.R.#JU#QRxɱn1I0b_:9;;쇖e D\M㱥\̓JEr RLZ)Wz&ʹ~Sp..wSLlOm'k<<e4Ρ} ˘UvKk(@*'kŁ&o/9Fi(O!yp3@"؃7omL>:OZj@LcWLx4)^L8anO?1|{Sc=^{ޢ w|^h^ェغm26^^\ڛ41s DRU1w _ɞ\~R*u8sU6@/ pO ek $fRe9Ox%L>5I.|5%KU1dL:oBldK`4)U);ri sIq 2"ǜA' 1 0Q:Vq097p4L\>x 6n;8{߾~mp >5^"jټKJ@0Fx{yTgxr2(f$ˁX䁤8,Ͽgg5E>×_>;`=mo_>SO}} ?v Ƒhjh(9bLvUk~Î۸Kr}ⱂ]!N}23ZF1+NGۖaI2Ef͠zsш+HI5A1R^i͒@o$_g&$~4'{X|3p ^}~ LH|oufYJзr6IIX joյhɄP<&k:6H5R4 tG2Ž*Y V. Nss xWC_ٕtT<`k;6Y/:KX+&֔YʙczWACQ^V7g |3IZL$II18](^(Fb-febģRT9t%uRU>)?.Gp2J] i9G9f[g-U_qq~F 9]b ^`y&Xaz,Gl|9|nZ\>Tm ʯWy?/}ix=^s.>ϥ|;ߐd{U=iGmvμ8D/Pm8uՌ܎] d0$_>M¡ڱsXp,3ɯc=٣Yy"$ *Fb3C yX1f8*<>7E(䜦ZȬlhUؿE91MtW(I#oi(ږPؐ{zh-uPim#sd[&05qg0(UDmQl5ϱѭm`QmA2͠E}hA W`F\UU 2T+a{O%k(VuBq[iԨ0THmD&]\4\Em1{il1@cCelo,?/ <d.[.Ֆ%2Jc9˟Q~xP|/dzѝ٬&9 1zV*6w7(b,1$\VKA*68SP*\ ȏ `"=1hDS"IٺCMI,j+ZQ1T=;SxI$<3&e ¼Bm('+|.b+33'׹z}{nRqHUVad"h`/,sC}G[ܾ.$jiNJtV;Z3Z 33(? lU΍:#xsO|-"ᥪ[lUIxy\9uK 5ߗLF>bچVW 1FYɳLErIWU{.&ڸA???2+;%STH^IfZBq} |̓.`9ؖ UTɸ4&E܂Z YF=:9>LƥU>=)4rnDbEҗAQDQ,\̇đɑ/* !IdkfһdK̄+@6d3`m rcqvP?df_óO=ZO)\[·p&A]3㌬yۀIŢ y9+HFD* [Q la$$o I{|wQ75߿~í 8s">zyᦇ7/Ͽj?rGj&f3osnĊe)ӆ3$&ZBhG^#gC1˗VHjU?ASu|?1 g\T%ʼnqaRUBHo_A-/=*Rc'fTW.7oǦ-N$! H?g-x0WBo; 7Q ,y)Jbb5:gd郌#'П)58ISV;cA7zjW/pcVAk[|1g 缧 KZBwX@y~ H'}W5ɚ@Ǒn?pu? .Fx]~Gf+l*hs$rN{N (9Y-y:8+À*uYUT;E~DTޑɅk<)O|sƤT=ptssE1&XH20g‰z4ضcF'|6ϵKӓ8_w?x=g=371mE%|<n4az3_ٙo`o(x>uY_q6vLXd+C(_myuU.P${8I `Wg'EÃ_WP”T%)^ -cF YTe+7`4yT8-\5+ lۡWl!2d lsImW Cƨ<{ԖB&HK :AI硴Q Bѐg&['* ;RIV*JhAرdaj6>?ĒxJhŒG֖PTTZIHE梢$U*69UeNMYYi &T/6lɖ-a[Ff36h,a@>A[1›Ż#pd󌠢A)W,. <ZY(BɁm1X=!@sT"ϗ)SC3?M^I8⫝TV)ڴrR=.Ţ|nR1 :-c4!lBh Y^*E?'mk7˜ͷB탕Rs=(KNOeš\NJ=J6yf%f@89=YL>]&:ѕP:*\ELl4UHhF|k6)k!  [ZVچI}hلR!bAkÕIٌI %bE9Z*D޹J@SQ h߮|/BZޮ-ڹ98>> d54BrN$z9Lty gsu\IۛW}YIY4|7d*E5aO|MB&.?Mc < j3 *Ks<5qgQI0>#]zGDdӱwWu~^[[7];'@^qM26>}ߜW/e{KApHzGkHRVi3Z{Bغ+FklɗhgH6-yo'ʄihrvPVJ*:c" @(*B{6Xbb$jُ̽ c{/÷oÆ9NʳLn.g+YT*< G|tAQXCJeQ~ڑq]$A˺\ރ. odAs; Z),щ7>> WϯiqmK΍eV߁DD5f9KryL,ikXyvh܁H'An!!sk[9aZLڝ桋mEWu>xevBeN¡w"*Ywjl=SP|J&`.bsh3L,:U71KS dgB -j2S#ұ t頰y6TP}ئfUƶN[4*jQ i9, dBbڗ0oTYI4U:8\+P@XLUXc3=Zu5a=ҢNW+ȴ\{&,SZE~Q 5eHEPɑ(5`b)3ABƚĪکV6U #4b4s,ujt2fֽU\1& '=o=GCtpWӖ B:+%qr-,t>iT'569JH۠2O;u$)RIZ)*InB+ 5tJE+n6|[D"J0Q2V).IJoS>P*Lt?ea[lDJeT5ٯPzߎ "kvNvL䖊ױ}QaluZ A$w {8j鹰\'^"DFYRrbz6WN+KL;_Zk.LD`NyXyRcY;MXNr>DvB܉+sR^EZQPa/~1\< O\|5uh:/ۊ}#+53Ʊ!l 5r# e"m4dװR)<χbNH=6~PCc'd(ȚŹl2=FĞUie"O l%Pn3"OOfucr$WmjWVJ:1 ]qGL~1}} ﯁<%.VBl.ڳ5JAM&G^ Tj, .D5o0룗riI_Co wn`oip2mKGk_?Wpe9Cp\xLPzdݷBod=6xCŷsOu?l̷3{ Td 4F"&AXfڣCLSχapz xD(kٔ5e}_mD \~ mg5^}k?.n?pZw@8~:L*1!!'> @Q}6YueR7z}Ez+++Z+&."b.LvgBlF_vZ-J3 D` 'qٝlsm<2kGx:su"!C(k3}q))%7ޘ<858 E/XufiARqS2J㋎p @Ҍ^A|ü6DAkڮʖYiN[. %;$O]@xROaw/"\\eld9dǶoQ&*"ɏ/+vF~X1$ĢT<}ەx T?-< j椼sR#0zԀ(B;"U[n1jyX\l1˙UM=(ΥBZ?R>)3Z~\6]4ҎĴC,P ZŷvVFzк*煀TS3/Q!j [kZ^"|mX\TMC*WE~74Xzb5gK}rq ΐ[3UWԱXQlǨ1X&MM;TAF}ZJƎ$ 4i5I_DvIʌ ,5lܴ*Q'lC涭٨1&m-Ge9+!Vh9S|jwV}XVK.Zt˕]] C*=(5c A|]rKݯdk=eBqPKB+L/}o'IU,T{Ts%2s32)!6~rbâ.ed 72P35S&vjm q;7[?tz0CgER*shsF (+Q+LS R$DT9S*BEwP@!z?>ܣ ZyRh<5E%NSMT7bJ;;ŜNXG+``}J.LɇK>UߡNk:wo?v $xߕ,hOIq&B/J`6fb{QE .*tf:+g Arpb=[qW-*kD 6!#%*FGIllۡ"vxH8k[) c_N8w\{<n?r\Bm'cJs6Ĝ{foۣD v-|Xu-[1lٝܟt=|/KWo~1V܏@\K".^\)&Աx6Z7EK/j.d`&kD%/iw:4V嵛Z~o޾ oF hwIyj0D/DN٦y"!5ixf~%5]i]_rwV8߄ঠe>E?ξÜL%rTltSR{E^W|bVm[x"7M] P|c?}sa33xlXѿ+g/ЖzNؗgZq^H02$Ppr$}n;w~:UXhż-]Ƒ( iSar&<\ ܹI!2ULùb"Zl!$NN5R W4+ %;q AJg/-Nb[..g^Ɨ쨋gL٪Wx ‹ݯ~oxW"źhm٪m7lz:FO-#O4$.f*Avuر">tk>pQ29U6\qhbX5uP^OrY9(ৄm'?L&؆͇77!D9$J>Ia@gn>u}Nڲ.?>jU!rs|k`Y؉AƄ CN ?c?\S( 3DMV.úGxls;a9<],$[R]Ad CFIL#0Y[̑N,ִE[ױUW$s\bq&o똏 nH";!;x!@vbPp{U8:G[)#RW-ylfSUe\Q~ Lp%CT SM])B1u1 d [2عyIȞi }@}vH Vo! @`º{ 3"dW%6>bgbHU$Ig|pr?+bښ7M3 EOBf5 P &]m dr4(ݱ ^9zD@pm!c3\e{p0ҒUZ%${FLk1yÏO_U~p" |Ђ.6dŐmUEЫeoR)s9?ԪrY5E~Q$2E V)2\aR=&qQFcsX{QYϛ+ $>&bMR"&҅Rgkk,!|{]PQ}5)R1ϋU=LH5{Pmvi9/Ef` )ȲFXي, 6BB>ڟaGg+xSwl[ n7*p].t~+.U E٫s:WԾ +||+:Hl3J w@_.63}5Y=H\{grb60}T;0 '[*ڵA!1xDe:MT;XÔ`M9I(iD+0z%IL38;}Vruڐ3i6` ALE&|flMNJ5rv=os 7dL]mְ5ܽv ѿ~~3h}6ޯcƎ`=*=]yB@!pØ*( 8KO.BY/ɦͻ`-(P&c!B|_N@:("(l"ɺ()>֔C_#y99?#V[Pyni_x~G9akmsÄAyi }qr"Dk\4EE Bmal"cXDܦ"*ȫK9I|CUCRE5CUjc?%Luq(fwMvuRWFsD[>q &ƂW|rKa$NABԲ  gkBWő\ݭ>gznߣ~`#9Pa~fTJK3,mKT8VTY4YJ6:SOMgfL:YƂ,ؒ7)e:(&R\S3oVZ(eђ9$=. V! ʅiT*\cR 냷nsG_/~u{\h[qs\E6&y]$.[o.L_x0 ^Tylbݪ²P< Ī, łmt 'n[SCj=6؟^62ONJ@Uo" FMՠE83 7(ՕPK |b3@#nMPW5 1?BQg:[+Kt @ ,x(0V!`+5cFB[A%oV-,Zj:ܐ-6ۧGmoXۓ (H!ת.~)8|[*JPn5դ/.mO֖8D0th=)\Ê!Q N\ኤ R-%²;2a6lc1aAҀ6 `X(jyczjt4QT+(HTlB#]ot N!iu_ȉmoyz~U`ib!_*Ab|JAKe:`nDʦ*clUSpځR*^5F!RT7h-|;/Vwp2v93Dm7Iк鹭^x좁ǮL>}r .!l`+;e38yiڎe,]->~23+5F Ght[X/RIԙyI!Z&@$$\rN[0 bb[_u'@:'l 9#WfOꅉ>'ǔI9^ \{YLLӤp1~ݥ+kYTzQCŵ!''.Cۭw>4N~U^@tc]uٖ{3zk|n`= 3S谁 `h\PjhQ8Ozo>s.-,WsLN=cQ軨[s!_}'$B%5F2IC=xlTuNNO)u9Q_5&E=kL<r@R2tTv(FL cEB {mw0K.R :mn+sH4T1Yfo[0-"3o6LەnֽZΞb+NVL]W_^߂d1߆<'$b\RPvDHeIWN| &%B5^-B=g*U{&DO#a.Pio JK9$Q{+z?b\+S~VCr/۽E5<C`yꮫo‡y'f0uMT\~9-HSa|BUi-8 x'}#=, k30<7tk+s/w[K8y -qڠ|fGdA( ALcV=XEN#Xed97g:y ͳ,A<1ks!C 1Mkˢd9.tI9hU$^T##2t=RU$w) [[MCܠ KwF*תB/Β< hEj1(Jgj9 ΅mv>x/ΗahU[lPd0n*&ɟ};yxqU]S0W`"nCbk7[/zzeg.\t xuKpy+ns vz5z!hTѯOUm;/wؤ9(©û7* k21;-M\ͯ^/mni`!4gCP>0j1IְBzgF+&TzH,zMԁvi2 <̪Q{tJ**A A-P MSWam{#*6tTI(+mZ9J6i 켆ʀG*FY3 '>pܤ؁+{{;\蛧Sveۈ€ .i1#ej5JRMe?}u߮Y<4ג"{ې TrzH-|XT ͪ`e4^XEtj:1& NϵKh*L2\" 3?cр^Z&T6w=֖C5Ba"(}k5d~'B,6zXr83B!(GpjZQH 8ز…}[Co`O\§O /Mw pvԂiBL@[X{`<-p4^-,89*%Vv{Sp-לuαQI3*_7lrե-=V{:U"Fl4m.I`'vZpLL1T6W0b&yOLwd]FYÊPU ~`Dp<غup݃렞h0[AWBu'Hжa ^4.}f]/pL gz ]L>S+%8G,|LPy vJɞgP 90p5  P|Lł'~/LHPad[0y5 [;Xjlhж~x"ܓ4x"%!IĔMswQLDB>eN༝Y4oƊa>#4+.QG7s@a߃6ÿo<?py~Ֆ,_ "Ab_4*@5!7Pj)*b_rki @˜daɌ|(O}e$ 0iH ߺŒM2$6X={%[7u1M%xxѫcuOąnW'_؆{tͤo[oS 0&ߜ4chʶQ-ߖ:UtH)٭4!a'*9Lc<.ΣްT|6*X"1 $8ؘOH>JJw?/~xXGwatYu'bsEA&6)?Wױ$rֵ]slc-|7K7vu@v¢S7Ҫ5>xJ<^%E5?n̔ c ]H Y!)X,;c;P^1A4R\ZFO*JмMa}G9Տt 'ZB٦4^xf'+].($EE_&Midz-ֿp0VvFKMkk3 Oݿ{QW5f0w!zVЎIGk>1gv3umwv,\p <6#_FcO0olPCA RlAp:g4V\|,++팎jcoRokc"zuJq0ty ihɓ@uyDyuwm)BZ72 x3X)e`/eQMð*H6c<ZzҾsڇɆ2G͘=Ո`׆n4&t(u%Ø MJiFeg_xm{l8(+Hh>'*!(Cn;x: /-Ue hxzj p*/L)s `+ h@ @X " 2R&["F3ڰG齱ʁ+HtB50Ʉ1ѹz\8K4&)O+CE[a(uU>gkaT/.}uXVjl:+ cAUú`4)H|.NH ;wU.|8f vUKU};pa}exR z2'Uq+h,<>[]j}T)/`<:ϐMmC1T-%yS["q-{ E =j6Q'ZÅ6vf^KO Xrj@]:|| DPQd;ދUZm_*P p>&|aݚ񳰽?VRc ; 0.BȭmgsVLAPwk+X[ἧmmH9V`z!uZ39Gtv ϙY1YF n~a]ds֑@[SBHT<)#X;ؾ۪\ ړ6R1~+MxNlb9/sn[²h߇la3)vpzr@v lw{[)N`{ Y:lvlӠ3hO!V=$3'ޟކo}[ ù܆`w`k`=_Xa1w-Xp3$眞 MƀКT=UvU}HS%{ ă\ c|}sq 'utk=k< m2*B>+2/šm@aCqR,-0kZ&*{\Tʸi-!e+Q?kxÊ1mt CܸH^Qaa f2xCnzt׭=CQς}e َl+u麾!~!\dRoV a5XVT#<!hd ?ML"U H)'E$fa$%R*09(}7$:ƚ #{EjlM- l݇,sn{l'0-|6akb+6!Ħɠ`֤<VXpt+~S7~׎zf2V{{d~^pi:k|c=瀵5!{B1R-DPPPU~B@`mm BMTeBl%_.@d !A|4T"!&r;!)xhqLA9&E#nVApX>ҏ^7ou k^adJ p^11sDŽ{}`h2 d12P}X_mlӌXہ:7җʙD :皘a֠@F"}7KT *XXg@S7&-6IWlƉJxc4) {2P EPQt=y3?ɼ!eo,8{:դ3a\tɻw,h3 DB([Y&]hg=W"C{?q3Qm].T`bshk&Y@.cx# Wg~MuF7 ]g$eK2ĩBM )7dkw|/N u~1D~Hu{]:ه YEsk>}컯OgM\3ȣ ;;{IY>D}ۙHnoXU+`X-X1(@ j 75ɐW_sO$va9yD#ŮW)C1;*!~ٚ4jն*F7Sؠ9EE&V1Рzw۟0@ @K[qmMaJ#UY ڂ0QDYy_>;*&(? @'`*twzeMj 6ZBi1%xj?u'\p18|FQPmrPӨqoQrCeU@MxSqV[tsbf%P;u*vOZXpŹL$V>c5׹PS59U]*jD#EXC+1U&N;\jJ>T ꭶ^wL9 P&Lĕy &3bPjH۰Qo R*ȍ@ȣ%k&s&;7!cƊ;'o֚-˵L,=cc6*?n )ބJd k} e+ J!oCeo3 0).2&eCoss>Z +dMDI>tv>{ʨcqp{Vzqc5̵̍9oVha۰gf[!4[0&mslhwñvmp恾㵁O]8_kOpvvlg+TL5aV[\LL2\GK@C;l mřt\z,$eB> Al(%άjC[<[D6Gⲋm.w2;QڜCfҤBOu6MrI#=5,mٽ]hN= [ʫ‚3lfr/)UnEaI HpL$C 9X>vٹkGuXo@o>b<<֧@IJ}@$8!s͡ǭQ֙9RA@֝׭^y ] _zv{s 2*A:{   Bɪ1,p; mtHc] mR&w-/-9u [V=gj/-_}}O)K)0R``+?e꽇W<'XCVm'Yxk$jҪ^f{.9I{6Ɩ *YQ^iaownpt JX>G80޴VrP/Ґ uR;;?5}}"&pyx;r'4LL#IՖt+d2_F{S]= Чt'݂?bdJ=1Gg@Vx;o|6*+Fy㰊VfN#ڴ OV( b䞁Ric;Q@z5iK%lUͮ–犢gE3`m0fVEiN{G_u a@ yTxf7TmW D c>w} Q|^ x۶d#d6]'rOӗoFj jL쬺xXSpe5 VU 1n j#`PZ^uU>ŀEFQ]We@S#np,:hGE(fAE.TU@%0P TP4/@y,e9i᠝m&$ĵVP=ƫ{DUDH@>ׁ@%9XXN m ̥qNghϫ9A/Hɕڡ?bs U.Ň0,Ҥ\%]%"eK`MrכH,SLJnm1EU^ m* ׊[gP@\mZs~TVBlGp.? &uzvNEPms/9U=YZYDZ9JԖo9TE04ai-׈6șE9 6BhFgdax1@ @ǒ h5 ۳1 q##@T@Y,fX_NlAI!DȞuo例l6Q;6>2v:[>Wf`&T@܌3SZLaz_7%1:qr'>;-VwNv\I5"Njj@9+(gD*k\,BUp+TA"(W#Hddsoýjlg0qn; P$Wx 坲y&*saoaWZx8>s;4IP^qQ^q0JȇP JCH KbMrcu*`8+<шY=ca_8̄C J@5+}x~ HTcgzڤci18X@TU}BmSTꚚXH ]dʚ &ƉުCFŅwBi"ġ;W{P, 6I \4S<= [p7-l=RИXۃt/L"1 u Տt3JQuyn3DpFnyjIy}8ʢ+ǁ 7 M^S8, TJD di| dVgdcm,~k&w庰gϕ,V =ZӘix Jɡ6!cI-dOl_TB"q8/_@,̶{|-8\~ MB9hࢌ⪉*k^w]ءsl՗j@/^_He{J)mzQWJy [La^ tqJpu{Pq*U,2BP*<7+PրXa[BT@!%(޿.ր[R9kX d I+Abl2A3c<>׾;bŃ Mx{ ||)&{OMsvLYa3uM!(}0WНw=]h]~sc_7Oؙq_vomX9&غ&'q> {"j|cR0qwZFM! qhe(~>{'=՟uV v96BsR nF+pBY#EV6Ƭc2كګ5>,&ʌ<,ޤ09 ~2*۸Msfێb&הKxnzSs7+OYObu1CE[dz+,S*%ح?SYG6B#bNliU B04z3ǹaWZ?ou㍛=𑅫禁4yݤRQdEu`T: }(M ]U_!w);!Fnyt{υlq8019gLx~)o28 JXfG#{ * X{j_^M*O֯<nբ*;$),O{/_Gƣ=5LnVF gDeE{8=IpoVL1_C rEL"K;uIB-X0B>?&L&3g0YZ=nv<pV + hRڸyQUј'amCgoKEɘEX1qd!0; 7܁ޖQv72j0/9ݢ22+譍NKj3xK1tq$' دJtquJqTi+#J5 9Gw1 RZ3).h)C;Vs#-Y=tT6B@wLQ$UMC+}EvۤnDbV4ؼAa&FB\&Mm_Q)KF|L u#-DoPgE!NCF-ڂc-*%è9LJɹs7E[ɤS&Xף:,v_rϼ&A--eM\s1  \vE+p~-TPH>̊ϏbNMb`p&U]P5c6T 1XUeLFzd€b,f0{&/ܔB;Λ'7+֠ 3$|PSn/T cҺ1 ''ȵ[M ;#ݯ߁W6ppgl w/^pzbBuW?`%JYZ6|O }T5tR"QPλ1ҷE}`XL -AdeST1q4SDܙN[J!w>].V9<`Ik[Tķ*=嚂)lC[\l}|cx|o} {r+EN@Fp; -~~GOb3q> LJ! ~ d2Ţ46D(b sj1*l* )]L؇(X`~A_.ڥw 3>A'n]Y)JiY\Kf굴 j5U[@pGM+$y|]zݹfas[l0)/IYj3jّPX oMk^FPSQQRRϨ㑽'0NLжee'2շ,bAa-\MǃJRsOV *E1,{Keb!_'/t~j ڳ`U}. ߛ \ dOEg+Bit7^o?3?so?7}bm ^lg{=a m[$*{:qGq8ڿb7JUk$ Bfbu?&{hfySlqi`U6>~eeU./=T.$px4y4MKiI UX(b\9R[Ue 55mIe,8گ2vvLÐڧ asuxUS'ehƁ&0X@^yBzE`.7SdIǣڢF U(dogu,w'!{ `o`k#S{ B .$Ps6s1 +1(Hk86V{Sܛpb_'eU(BXF+)'UV \s4 vAm`3Qׯ T(8jAOIj{:#{JJEkD7eP Da(j\)չ\ 9t9#)=l1C6Q1ۊ*#(f 1jWSFAE"DeT@}Ft]uQ66Η)/~wQO<ī侁}̍g)|z9~Sۺ($'U?i5۔&[6Af0*0lX;xf|&m|Q6$7ԙ)Tƫv& 9` Cr AO6+`I6,DL (*r[1*U(lZ76AՖ鹒Y~U pmxܼߧ-u/*9Q:A"\lTMa/k -is1k\!QHQPe6bG{35k_LG#֘wy [jX,$QәX)AOFյ8uvY:b睻=||H EKsO8C4a]Fm\Cg^;G_Hm{MLఒg;>I}W0CNf+Nʥou-<]m*M@f=lagNt^s՗SPև¦q T%U@\c4VBH@gA*r>R %0 eÁ ![Oj*RP'mBǪ%&bGO&G8zXʫBh5WUAդt8ۨj8d$X 343+w,u0U },㝯{|k۟ O_uՆb +E,BćpHM b\2'= (-HJٔBSs kG#nTßEJүQ/ˏT>26~Uv%*RD/EW1G zn*敚b xs^oG0~G ׿|ƌbF\0z+: 0G`ơd H5PkKT9Cx7vvW1e1l krJ_KxUnV(" :Bc7fԕ7*#rFLsρ 5ͬMZYR,4AM^3kp Z@ă}-ҏ%G!{c7+yFb9##us4$ @ζ i"F jM!˒Dr{2i$GHȓ/y&vX"%5?M *#3sZ+R q'*%sdya_]خm$Gwu1T+oh?mWZ{R|Veb "ȋ-[S' L̽kѐ=!9B,9 @ud ]1q߀#xHxi+ JXхπHbʳIXϙ~$HDO{or;U{ PV҆u2S1%y>|A/d= tv8iogw.LƻxuYww6p?vkog&pU~[[n=oa{r;ٞdzeÖ{J&c]=_?]{(S`'0MrD:Ø\|P/@"hs"Wv5n#8\k'EI.>?3>al~KR@N׬^22+lzMdD?V[x[,71V_)d=؍'R{ @9qkڙ۟c?x ||`7~?;&U&6(4,oxraU0+r&X\yqaMĆL}FK[QaLFg8K*5kUF>-RXgg, p:C.]i|> %< /!p9F0Xl&&@"EvlK:-ރ =sn|l7"NcHBCa[FoP^"~L*\l.h [<(Y9l6hᛗF:w/~v\ E}a ",%0"Ip r`ybyS.?TٰRNذ!hPfizh3|D#rM`Гȫ&MFpU[H!n4v]Ke=2lsQ=6jP >xQ nSE\a c{\ *N$ϩP]Y#JW1sBTݥd'p@S=y%&aYIג;Z k0O2Ă[OaƂfXf'%D^/[d5TnHpTl%z*/@J \]w*lceַT)F+yL4TB[ jbдεإ?-+]ڱo  .ե$bm,JNYPjRZdrgI"<+5em>ydR&OE"S-HJ`ŖI rJsKkOXR5h؀v^Pl&}{P&ͅ` {d5y+T|{]1^n"G(`PB%l`^ȓrTʄHT?@W^. lJ-.jG^ =P]?W]3Vk뱐8"זYch*#-w~|y ;w=xs[~p盾v N;kn'k^yr {z~K38ڛ{W>8kF~Ѡ +¶ݷ^7^C61 IP%Qk#ٲ"*+km0h sI=2nmTwrMA/ & RqKxΝng=|-<&ڨ"](߄Dca<0 (e ~sqM#Af{ZV:$O~^/-?GG=|#2:i([9L 4;PIHTr4g`T|APy1< :9ܞtžDشR=kM\sCOL&o|Ÿ-J6nTâ dTje:]asDAt^_+%!`5~Ny֮GObFD%4b51ʁ|K8>H7`z}X` p[1Ee] x0lꃝKO{r?lժJM@`MZ z"Y5ֳ4RFHj1?TdXŅ \W,fvV@l!>t>V%&Twև=wßs / `r0K]w'nںyѠ/\GS{S x9({\Xߧ= HDīmɥ0?I͑f e6Q3sڨ񞉦YHߕYALc|R5I9\'#w m7 m"^@nm\x쒑#Ez/ډ='b3цaYo#wh=YlTemxu? Oкû)\9O2ٰVlunp8k <0C+&:c򚮉ߒk}h7XTU͞)Tc?1Ym3yiέ޻u fkω;5td|Qԯ~}.l% > &m[@G\9UӬS.dY8V٣C[}Hxl7QE<=H7/{Dq x YӇEJ[p_} EE~e8dڤy'c ~]fzQӳ VSaZ8e$&JAJKi61ybmA))@sAaVϨU>8rBF,ks+Њ^9U%nIhfK8}MhHE`$k&7KeQ̧j8:[R*;![ V+!꿖?K4jwY]VlQa* !UsK>eª{mDYe#ʥu$D *7IEV5i-Tn$Be7&S0=v퀷z`kyCYXm>"@D;g%|{kdZӿҋsRy 꺲 ߆tPmV_eVNqt=AzΔ*sm i"9׊l@fy^۰PY#5FL-ڷ>l+d<#Au{R?;[cfDDw&ur::X/7|쒯^c|&r%Eϣl(J9Tf$Z rGk< [ *N_$H!a17p:dѺXy 8x[OؘO پ*x@A s`[s!?B6 m۴ϕ!!a![=0~N )t '5OǶA8x*+!c?v0ڪ7;7^>Wk,auK׸, ͽώ{]ת'ggp)Db> ~gO _ZgU:צ¨ _D'xS&*&̤2屲'`tMlcBgFU"f>Ɠ=mL$|fBNw3ƠHBUkUp] y޺j~jM%Ĥuj^G{ߓhbBkrQŪ_,bSA|q&95]ԓ 3QRwy6ņPx=u{ky8:ח{V!a V˘DɦȶTAۢF#Rt=l*-1kO2,]0>jI&NZ?-8oܸGaV<ӂFe>vCs|ޛ]v\aoY13 n"E,+EӎR]rGUq%1+T䯤SR[ˑX")1)$ X`Y-Ot> {{.}{9M ;iMNO`wf<#|,؏9۟SB erP:=/I/ǰKҋ/sk=!/235*-ܺHj+n}MKt\k5Ex0 vyٯV.V#rARj~BxQw$o? G[k@X)z?M5un#{TXI`lK3\5BN(+5i BJd*wJW8^7&$Lλ9My)ߓ} jO!;7mU]PSh']۱l#  ;&(2{`Vαu6DH(qldB.ci ٳP)^xI2r~X` "~Llny P&IfQ tk$`zaeMPܘd?WG}6tџ>_CQ [Fyz{Oؽ2^&c AJj>"8zs9 &g^8*H:]3}>WbR(_V˘"Thcx|,`?4_0 㑶Jl&6R0.N)=hϥWDtmXfЉ41?NVt`m .|+܄&mmhf`\sRnZ [}? -o8 ڄlnʶGKFUU3+6lÛ_?{::} }8w2_8wwGn/p4/7~1nE|_Eu5aJ.fj<:h))EO&x LMQUAC1'\֋«VX"|F ]C';+~o`#LU;s+挶L@}؜lM͘CUg%j‡_@[EL &J#m+@;  cاl6Ή- z6| +d vtqC*eSxCYpz4 K _b 6޵]εA@mTdg$j3+\9 Y (_^7]؏53R1KIeł TZ8 }S&]eE!fa#_%RH'pDcks89jr˪dU[*;ܤyڞ~ɽ܁8O}SonNl&K|h Vߩ/10= [ I|(T SU샪x6<Ǫ<Ƹ>˫Vr"$%UVIߌ 8p|Zyy}41yĿin!yʭ,7p7a0XH`RnjVH b%'mCgk{ќebq;=Z8s~ pb'yU.F73b~1y2[PL6;9o'ZCA f#?T[?5 {:P Y3q0&h*%%ԇ~{Tt/ N\yR5`]Z@Y&O &uM*Z}x6U(:t:wpPdKO͆*U{ S䙩jbJXvr^C`)r" t kz4unPLT^&ՙUBX̂ CW(DT#ƴ"Eu>t6腿 ;sΟm`sICeTlL*خ b#(H=7-o#EFw shg)X;Ƙ"TYXYL$J }uk[2mdh (/%C-e (?z#a /]i+Eh&("KV@@oK+$IPR.w@@TzrJp ˎyA$mHbC4y>ArדAp xU U \YLa3 rZ3-xW:xCȻp۰v013 -oɊc}N@xӃg[^vCx]x~ oy+G|oé1SӘ}oS{2e! j cA\bjS&ΗyWB*)duPsH6HفƒS)&m ʱGLb.fZN_Vb |B/;mhֵA_7Uy=zꄻNn{lַn~+p=w9c+ۦn̫1ܜKI Iǖ`?}Vf\tnMϘj;lGʵ)V\(ӣDRFwD+sW 7ne PwYש~Cv=fiy'k0X1fIcx)/Y>B T23bT=uXlbĨ[߄\?6C ȂDHH΢⢺21v 6H4Vp,I[6Z*3CFs䙺cڜhacH3+D yx}68x2R M߭Ge{ T +x PUˌ~8Zp&n?/\zLjEP[8 15iJT8,3݇։vϬEw㓮O"qn4idkޱmt:}U|,6v Z1;>7x KIsO&'-_7})+ZJF(͆@=_&T ByE#޹9h,-Lu&1+ 2W(j !rNߡV@P(GkE85`SبkCsbh;gd-زqfc~9\rܻvB~[i\koȪx^A~ά-Lb $yc~r1/S꧍JOfm©S܄[ej;д˰, 0dP3Q:k*p@T0_ !RTm=t OW5]$=Tln'ն^kT"? .T1 'hPc-))M,oBR|ab ȃʁͬ]SI=_)Eگ/*TP=azX*FZs@ui6Y{"@q2(Q׺,S5^PT%c3umr  je o57"NjTXL/AԸO%IF2t0m&0TFJ;Z[번TױWZF( e1X;lXzaӆM79ؙB?-M[{+2:I@k  .O,la/t7*ר%F1" eRSǪBX݊@Klgh4ʟPk&3h@.)&ْMl 1V;M NO`|LbyX]Ri`?4[XUX =۴`ڋN89s yܽŠ~!u5+u62"0Y YʶSKkS-EcqZLb(`X-9eŒ$\{o8JYw{&VR>-eLBGk9j28Imi*V0j.2x723qy~_= ᱟ,arlYaM>0wp/{|4pPTg@M yǽ>7-Z OT=[ 1l2yP$5O1r3k{fp}32*MA"cZ1/f=]Xgp]豒E4./-3*C9f=%KuOFsWpc |]{p&<\f ^0\ ƚ4vNqdiBrmUѷiMP-7lgB^OY

ܓ0G4jEq} r5\?'\M,u;itA |8!>ޒo<FSCcI/_w'bу"O%Ur{ ⩪63b)̫vQf(TJ*@b1>v`k~4KP!;5S M6AR!@0fжp4О~P} bM RJaV+h^ﻊ^;Y~_Y6s (AT嬿P1Q|b6 ,$1 Ge*Sԛ'J0y~`㙘 V5mUj 1/}X( nf3 0rs0Ħ<ʇ9z]*c-h-iBNl|T`Oba,LbR5dRQ)۸؂+! jF8Xl6bi&{.ɾKXmO'-b/Mee R '^kAXR{)sBQQmJE19 9_XOܽɧŵ)Y}._8m/7~,܉r¢[E..7#-U[!5r#(C ௟I`I91"ڥ O_ǟ=o>~ïc;o wvq-Z_nK@(! 4!Ŧw3yͪ OVr 춉Jo]AnKVPd?A s m*F gv _P>j)m,*x_˜#6\]'McH?k)EY*D&ALǙ[K3.gr^ʚH @.lpoB.OsMM?iK^ MAƀ9;U00sOb9߉P8ot"j!d2'\s A ,:lv6n"~Wx5OtX3ȩYU#OWoex[7ٗhlNB{ 2]hTID$I2owD=CnW1n `-&p,*;H?ΪHLʘ}QоVVw^^w~n-ʍƃVmC| vn_y6>H z6f5 o'6|pd AqoN~asZ$n*h.έ,硤z5QEÐ] >_W,-ȫ֌6N J$eÚ5ճD :6;߁8um'Bnc 7/"Ya7o|=MO?|>lǺYyJ&6)zYNYЖX /vnQ~@v]عR6}6~~b Qfj.F *-)DS0|W}E),y{?gu6\Ɵ`*a 7b"$f,-- בǐH-gg0; 5AIoz_7韸Iֵ4#աB6.k7|>ñRx`{a]*^]ϸY]z1DuHM(k@1 !.T<}-+?uHuT^LE3X* 0e#,&sǷ3aȑ² pzZ/fgs D}f2`6,{zlJQ*#vf(lSAm¨|C|Hq_K`M.+.'>d^6H9԰HYk-j]7`R ZTcXZXשL T>=N>RT7[9YHR3>#!,O-pnEC[2k-82˫gk bGaO!haI+t+軷?q7Տ?4;S`3uפ >|QsaOdAFgOʵO~,ePpECW`?w6t [Ԇ~hJZa^偐&dGYH<U_L41'U  C i{! X@.QA m驜-?ᛗhoUd`#Ozr ; x *2iy+(KPCl/$*D -S&d? bjVڔIݘG墈ꆬ17hGv/]ݧ`sw^[7r A!fJ= *iMeG9E`!KQ|(~ rAIc,?G>-xFJ|"?2&ﶪdj<׹$:U Yð7*R(t$Xak>Tfyfت)Dʳ!QA,o 3`MlJQݨBjR)Rur@~#D$Auˆ|+@lKWwew3wݱ zLOwo\+^k{rr3 Ɗu5&3lTIF%tِYOz`1 .oWZxe ;#{pzq9V!5"ƯQ;A5(܇lfc^T?>D+P׍*l9aRȘX ~.[K| *muS7 .;H[m-9]lmH[GAˌ|PYfRH)(*:2{O!YтH幌JERnTaY|_,pb_}i9ume ,Nif<|WBVɊ]y\mLI &^j0cd궹ʜ6Iޚ-رMFnP`6?f{QvRǭ!ج1,P>mݡ-\qF;wO7ܻc.)&E[i }VvٺM;֫8JUօz+mJ[ Ym Kƅ⴪9UaϒmԣGf7pAwN-xGt% :Q@rٯK- ZcuxӆWPa +uXîL*N-]MmP}4>&&<&sV^7lìmc * aLk m.*_Ȫ\}bR4'הnMP{4nu. Vuk])J%`ccBiEq;S_ο=nA$;,x?#Hyy#q666^p̳J,,h0 Wxa剽E\U' aoJp%2N} `B8p\H" Зj"~ m 1 Jfp5jX3 Z׈l QQz|ߜSa z_2L )%R1|vQ"x;} ^2ixM&0sRY4l±iC(~~  `C wQ?z5Ȝ` ۛqv4zr]L.R_R~AMG#e~(j*.[&*`(NJk|5tL=B=&!<b!g?p^xu{*in)Q=kW\@uAV.aο3Y@1A V&iXjoVfE/R)8 6uZSgPM]e]Fo1ѡ'+ĺ\e ُ"{2.5Q3R =W0B^hOmL!먆*RSJ ޵Ţ;K=za(ş#r"TyZXX 5}Vy˅nqf+{l9#{Uۧ|p 1xB' i&D"{^06Nk`-2a zlnvX CJ\}8*hXT8iQC 0n@(- Aō) .GVd6Cɱ$5!y4]fˀOx y9^NDԙ:"J䁮TA,x:M9b;'+$GB~޶M{3|,ܜERnOw*j7?bمǭM kh0ZzrJM x苄_RþW#.(.rn#V0zw1$PR>$"3c;d%ARy(uzb*LRBʿ|>Ħ$`;eFuPl) 8R' &ZB.PcGruӎp#Ϭ\jvgq#sr㲅qo~~m[}6s 7$PT[u.zsLV_[&_//a3a3qv~s^[ZoœSX_ҿFV2aP3_ե,XyfmMvK-[Rp.1sF)/kh'-]OMMla5A"m'dI4:G5dz}]^3`$ (!jEW( sEdNcAj6T@_&HH WEdo]pq 't?|պ~.~ڥ7p[|jzKsh6gy%nR\p;.Ļ8Ԣ&y00wIqXɞ-xLv1dG<<sLb>41hPTaC*͠d)C4й77=@wCʼn eWPZ|>Hl7ڲlXiopפꫩV_Ϋ++_&mTRjQv K}k2 (0Z+0<rۘKAΉGk+P\?\_m8{m/o'ciR+ qW"b%@!{s~>vX6MPyE1 ֓iwZ`Af .?|swށ9-jiٌ`L\|8?w\Gۧ~R;K"+'P6UנZ鳊 f6拏|26dɞ%zSK `:\XOlPWzE*LodkV]EOo}0:v`ϢC` |GKA1|ao6OS~*8x6^|޸M7} EYxYRM.& aV~]q8`礛&s(n$gjF&͕꟭-؜Wk^ \!R- :7]}La*l`eئ6U|uU>7-,i[#@fd󮝟؍1fz[8}ܵ=wZ]|؁jpVsи> vh &Y+Wׇk>N5`CT Tv]8`R!T+} QOevKQ/rlA@ vdR9^HG2]ZC.BP0ZIE}1[`3l/J9`ڽ5[֢Ťd @$o+%Γ3~Q^9`j D&I [3C"WW\&Y`khKSh t>1f0떱kzm Gג 6kCLXgߦ0D|6*WץsxN1ؚ@ )Y3Zsv4ՄbsRa5 %_.id՝$bk9YyQ|`Ϲ=i>r ²\멙ɮ eW!#&:dKCnʵw؈6n񶸀%"`Ai6*E7?l떔y6[. ӛ Hhl\ A"!8B7- v aǰ{t:T}zP9g%X܀/lf0kG0f9.` ay!O{+ui>W5vh=s) a&,!|kB˹EpqmN.'9Y߽7+ׯ6Zˆػxu_x :3s 5f NN,"SVۨc[DP 71PtKnErػF 6C?9/*mCC$Q,,3َXغQRIZ ЉcDŬERrH>"JYhl!Y%u2.Au,0K D9$Vs Ƕv x6,<}In}<$| sso @! &Ϲ-i Xr`"DK6`Ŏ`"ר8 Qٳ+> *-?> `/sãO.k^'>| 6HUf "&z*4ؚ,OeR5=[KCQ -DPyI@Lp#)]y)|͛@Rxu?uVYsW̓'<o(lk+xW:ΡRL|?e5 sMV$%RyEEM؜0JƓw f~_vF3ƨ򪱰.`ٌTkX_B݊EUЎvݱwn VW??p#DJYھ (P ւؼEk7o?|tϼF#>ym|Z['t:!Vf+2}:h빠VƊ};ƪHk6`3lxK|MQy5b_S.pK=6e&c4"-g:,H)z +; ;l]׬5=JE%۱>"!PY4_x. wA(:n-?ζP 萸RV6kZyXe*buTlmWϘFH [0b_mSTH}-v|o[/"+Hq2ҋ|驏Rz*::C+m|z'X "K1gJB MF=Kpkς"b&(Wy-3kw}޻iӰ tɡJ*,jߛ~Vf=!4ժ;NӔbTiE_Xax(-4XIt[\TZ;z@ UH.fF bG5As m E((%Jkb8@#Z s^S^`( XxD$Bϑ*LQ5dP=MPegXkY0j+A %UҞ U~_>f}_fna4m\P4;hP\2LOvOk4#V`=[/r, UPQ`Z[-SIH%Dgl^[KREr 3]Nl&sy@O =)A02X ijhp L/>%S&Pi:&%"3W0$ayjz-p,h~?ZA#NϴfbC͆/*@=@ J&9-VQ(drevڪ5\4cv:ˁRHt"+X`AEBlaQ`1S !ى0&\6~yט ASXWEX>$f<}QApnO:M\vǿ ]W'--uYz/W޻n:mn`4l+8 }ȵ!Vp.b/)5B6\v2$3r_vZ hOЙS 4ps[cA5ܹxۛF0pn[% jVp,|[/~MVN* RHQP D] a*B~WJEo7;=J*8a0I8P)tJ8̗6$ MkTQf7Ӯ H&J YGp=[E`Ůyp'ܲ>rϹ> *fsƛ6} җш4W<߾dЬ~|>Xc Y=L$dBHy2U=6 R;"VE)%(?N{O _~z("N/VN#gc[؝/+TI,ѨȄ:c9E0 +6o>c3 )u [S?/MAv1=*@B5d ~ʫb'{=رi pϿ2V[k4֔BUTyb$EBl?6XI+<ij3a|xC!6k 'J(37y:8K>rpNRY25(2V}ֳ'P=dCuaHZ@%`E1){`e1d㝹`Tٌ*v]ݭ EUPRXC[Q X*UukSPec܁vOeZИGŜ35'6@ U<}4]:S+ vsTT弖2c8xG(ss b`Ό˚ LB%cI!Xη({XKPIy+ 5:pT0y@}DȃZMf ^t|q: #XW*@Vk4'TdhoH|[M`6k3h?ͭ3s@f0S7^ 4%{6@U>R_|,]پe@EG0{˷6#"Xu&B EȾj卨S,{5 9 V@ ` QwN#jy.#88BNNMbʱyDPuI UE&m_0l6I`(|fﱊ(g)4AI4?0FQeT,u6skBx6sXa :HmS@-J"+ kݑJ C.)vZMYK|ք\Aǘ̼[Ŝ Xj2&j QnXkPD!El8 \+z]?!_C>8`5 -w=𛟼v'0wSNjg7sܱ]V#qOCۘXe7~LO91:>Us--`ۅ _; n{Z7oVn?z^xYӀ;h@(CmDU=lRUxBp5M}b {ܐ:l8ObZK`"oI^~,O|( RV?P EL *eW|ؖ1rjܹ< erqT#h( |QRI 6a.4XʀOcVʀFO_:InbǟemyvYE]i h'W >{"XU`iT1EF2lQ<MVغ}N*G2c;*<1С8{&G\lLCЁ͘ U7#aI&11Hlf&* ]K)ߤTr ԜՄ75ܗ^Ut'AGϸg9}ޏf49aAswn&0MR b_eR7E!k|.Zh0sl#A#ذqC߃5y>P pԹވr67\bۦWɡM:I|,^3og# g?<TZJ(lΰ!*XqeeC βMK,%EoW$PVQ(1y5uPߡ{ B0'G@Ŗْ=Ai{'|+|6]{r ~ˇ-/ܵ^2/}v_ xV")gsڡ+аglN\훸H06YQq%viy_K?1xݽ}ϋ:~Gq; ݷ*ڛ1Jˍ"="s0@Z/UZ%E&m]WRP[Y:4&H/<._ll;sn1QF#J`~Ĝ'n!F`"`Xx= zG|>jB 0Ϲb{~' Ox@ΤRYN= HCU+,N3qަ,2`x·ó//OB&VlbyB5_Υ gµ+ׯ]{sЇg75)b4u.A9}!l6{[.d[{(u\{9 %?mŁXx%o@@%V] v6=CovKpJvP*6}1Uu`SqB:3{L8Aε5Ctȩ.3`ɦ/ot%RO5cXΤl?z"a^i!sNG8}5퐹̺UtWYxc vL"=~3'ȫr3Tj 'v0 IY&Wa"lV|QPw"eOol=oq0mAtl5Z/|8Z1ReSCTT(~\xZQ'X"N]\Ū\lLvݵg,ZMGgO]ؚcjcܳ ֹ CN ]T3Ջ5()KkpSKJybu @8hPƬ?T^n+@yuΙ/ǸlJꅧS F6T(|kI7@x]uvyGA,lP5T(+?ih>0O%*TjػOR^6` 0 &圩n%Wr*pz]ܕ* rΎ3kI)hlV"[lO>i,pZ_d}b~:ē c(4@EVH@!Uy#^OTv4Э48A.'ߑͶS z1ؿ /.9vg{>,&6%Ѳvp>3*)sX="=Xkt`M,((l[ +㗃oXxiC{v-v =t؄]Ψ:l5&(xf]XP&ԟ!$IP嵤ʹ bZ^Hv`$?ݮw>\gpo ƍt/'t36'mIWYk0A[-+e69ȃ=68JM&/tu_~-;pL5(_|ƂƵӌ@+-jjX(+;MOX<*gW7G>*@&W>SD*ҋ/qyn-l\yMЯa, l6{G' 7eTBQL>YP2Pʺr]:㛡{y_!B)E{9R 0Bpa(\,zF9QĝoeƁN}/X {ZܬYG=PU∴g2Ă\)mI=(=ިъe{f{k)Y*;2m)Mx'Ɓ*&;}W(oȅmVK@}<8`33k.DF6;-]_9Կs6vϕ=AX.- 끺'TSt a='N7}!'&΢AYbuÀ}w8I6PąwcU/2gNu"}rRލ*0XYNtbN|ƞV%T 'nQ' lK @M*vRWԱTذKT}Ёݔ>36JX9#hmYIMXQZ1a2 d7# *f9TJMe6o$d V5T_%i  lnOzfSza >H9ъDzx fB\ZX}oOO蜷}CϜu ߁} Etzy?ڞ-gZfRITJ3%S ̕E"8} h䶈LDezJQOdٔi0 I7JcR~Z?ǹ< O,YC"`2&O!1D]qbaV2;¾`TEd5yj0_10 `N>]Fi&!Q5R(TKJiR,XQptYYhbGVaH13wS2WQ*DU,؟c`L9n_}cO>zxO93fljc+[)C.UsiJ'|XE)D\6 &pKO?tݏ/5SW_~9z.ʪy.]|-gĠN zKvM6[ݡ(e$DrĪNr<` 5FDR!Al/@@hYu-ڼu9b%*;%*8̖zm3?D|}G$4R{dhO^bveՏo1'tp݄Gowi7<zR6>}T@[uع"&s #i=7TbZķD5ɖl$evD |%|+ ޝ%za ~c{ܸD6/u=&G@\hQ0#}!$PV\Y&^_bgUfNLKRaEz. B(5 7gvakwhO^; 1aj<#IJ}~Ê0yxj~>e:D/+M?2yWBẢk]>!=oބ2,@-CʚF "iYȤ.QAm뭶)kŦRT]OpG,,|G/,K`;@m[01 ?@wE? !b=q?YHEEվLӭ׭Y9y7{YI\a ` q絷0s?x_rlTRa){Ft*ZIg*:0%-0Qd_LT00Ji:"\]Pʮ"eWk T3C?/f`o쁂=/o_+-Cu-8:D>9c t0qrW"V;#`.*d BHUcN*[nٓN2u1UR iv Yi tmH ~S[iR.:G)'s=*2UtR\0C CVʔ؟kT' |VS9)6lUJKQ:>mзPLiR啚hYԿQ@Sd~6x QX51QRv/*Q٧^bZkQR4 na6E8kZED3x&&Rs*fpF@QPĔ9`-oX4W4aDkv*xm,԰Pqx8ON\[:mc z3ÿ@{ ېҸ*[*Q(Ct]WxLEΐE/chBX̶ax tobG.plE-\QtJ#ڬ굑!q4)o#N͋.R󫜽OH6[ 謰 wsvc{/[-,|ǚX,5 ۔5ro` j_ >Q!F)BkgS&A$"h[[Y+Vن"GԆ򽢤׼>%εk9/;CL`4=Zr-Rh'aM ~S\.%/@ƨ"L|7f܂0w}_I'+> QSjsyDS% >:> #-V^u!ku}kh&ܣ ҄{شBA,PגQs,u|];p뭛{>Cxֵi7Λ0B;MktQt?Ee҄9AC̥>̱ X-M\GUz˘ ;r+A^Ydʛ*~cϹkp| ]knVsPYaiCN1{.DҙM}'^x w_O}5?3Ϙ|>ulvnA 0? s>xwטW_xb:z>{ ^5*6*DUP9S;(ߕ7w'|V] luo^_˻O7oW0_ ${eC+}7 4:sQ/ jXDԅ0A {Q&&V,x[0qGTB.};.}D|\-l?YXņ FUXpmNsi iQF(ɱqcM3=cMm;zx PGp'q)0rdV%TJ>-\IRT(Je EHq) & 1> ogڽҫWXsgݫo2#O`svs&[b0#[vcEsY{5UI5>c뛙jac/< F8]qQ*݊(أb#1sΊM](O@C Y-WjPwDT͕V\Wg)&|de#)]31>RW3y**TM1'*ǩt) ^j+q3V`3)Zx#ӝ@GzY??)M"rML*' MyKnH<G0HBV9^rs.4$Zaˆr~ILe[y&vzrLI|VsZ6e[ʔ ޳ib.@1FYxUInuAXbPD{\y3vdOPq+?g5l"T M GbՖҪ; fSmi+$WbSb?.,n¤=76e9k>;n<=U ]\>)\:;e k2>y:od3?8B"$nJVrTP"ul)F N"JyO&L//\ÿ[#t:(tXb@4Ӫ3H#(f;"q-ɜZ/OCB, T/j5st՜9/&ϡ)/~.D%/Phʏxn8^2Y3U.pgnwr-"*VƪTsޣ&\yV9)竑yW}1k77g?0gMV'nY/w\_z~,}re ٗM*XJ#))ǮI)}Q]U~Yj eARjU]RQ> qp`/*#u.N̴IA5++ ")$[JZRdL|fӑ5G_ {}51?Ji\/ҺUx?|ܩ^Ƃ{]Ψ2'M 5l־?~GkK7x fZd/yOY}~?n^—~}WA=\[[8s?A"&~=p2<d؟ՋnP*&bpn/ЮV*@&\SU} mu-h*48$ dQT/eԟj[qpѸGBU@iET4zI(_1D vmdϟi`>jn-D;d>!k"RKݷ 4fTY^"ڧ @Hqu (C}ٞ)@ Ec˰%KBg)lkEomzSUtBՔ"*n.QT4owAИ1MHc#ku ij;gxwܴYGmV[뉡`jC~z` a`@?z>;ZyY5D]j9UdߩУVa2uF GN.S_9nX,Jg 8VUܢ>Y$I lg)%.W̥ 'CJ)6䯱RK-DI 쨞W#Ty@.AvX0j GBiKj}PŲ}t kGZ3AiK (:eCр+8.7=o܁dYURASaU#[T6 xa\2&,d0AjX&Y+0y1_[v7o~^tW 7n}Ϻ-h7w]67Ct1e Iz[ƹRZcPy4ݺY7 g|>2!b`)5͌RGORzecBMßW|4X%afѸ'gIkzH՚ 9܅ p?zn_ 7;Ijm7*dXf?ZDJ (nI[J Rڨ5I/IV-\+J F>7<$n `5s@xc鳊ZrH6 SzO閲4#8H3kU/Jj  ǜDsNK W~7>[CsD 5< }0 1lk!וDMPWxe`c0JFc0((7 FY(wؽڎW'9^20w{H36pB8 / VqDIMˆIA#DPW 382fl&_)J+U蕶61gJޑFS4yYgVP(i-jodS@7kcه_&n̵}q ; gjM X٣>v DC9OEw-3ǾG`o ?ImZŃrq '29/z[p ug8H?kÏxC_Ï~X1agoU^mw=?f ^[;wS=WnPgDb6ՊB^#,[nt 0Cѧs{UMZ6 θ*u;]WrzcgVֹwc9pʛJ1e=U=`#c=e2_#/5O2 r}(X?2`(RP꪿O6 z`y6*n&[gaEc07`i_ګl:}VQZ1$|o>1T=djnJʟ;}=u_osEjRM`kaj0@Ίƹ>ki a9g(,NDi]N>EZk"v"R#G䕾grۭM suzUՅmTCVie6>/m޼1C磕ᖊw߄#oi7&@o]=f7wV]c o^ǰY>듿tv*-[ 4.icwsd躺B2VOUmPt9Ya.-Vw `"{mr䰅LlV6Bk4ϧ>;^{۟J_2ztý$ @TVsnPiޣ妏_;۰9B49xDFՅ߻58Z")08mʹ1Lr]RLb z q-6S|$h\\O2$o-so1Ŋ > zhaέ4 cTBQp͓&tHMxx⩅s1=7xngᾓѶ ,ץj\lބy%r<$:A]ƒ~ ?SJYAo!‹ O%PDn2$vJPޢ%<U`RA#PdlD!X*c;^ڨi˶":!U˰De _?Ϲ<7ZS.EŞti=iaQ3J"cf`0).10aY?x^eX`!= ɤ1s5B Y5V >ml,5rSUUNơ[`nàNlR6'F}/Teu7`^CES,T@oy /88Ƿʻu/ߌ)̷BHTQJC4meG11zV *|,NЗ(՛\Q%cAM] Y]~8pLBe*F@Ot$ ZUx{>Oz8 o~[ |q4]5[0P&)PD:c`1g> l'$u1kݙVJw~N 9D֢B]ٲ}VT!L{Z OqnV6U7JLj׎ty% gf`cb4,lHe5a=Qce'j@+m>vK虵Gnq+0yc$ǂg7?yqMg p pZ&,|ͽ5o)4uS2p_f3[[8Tf>,dt@Vm#I%^BYFМoc&)*5vsXipj\,&BmR1-G(|^~>J ص>kc'j:7AleЫqN(@Kle?M$6>pF^eZmԐΨsnLJe޶ڰA`h}?Ցy>Ĥzn+7-qZl(ٶ'4,kbxtsJ'lQb0Ǭd[(*̚6bfW@f@a/8ZQ2ualUfKy'492C„<%uk_8ouw˞ Ϫ>7t}l o݁)yyJOVFMA_e`) RyLjNW5s0G36;X%r{#vwWXQK4W+ Ƞ+MHkg0.'$7?_}^5tkx1yhj++܈r\ScA(6y- l{﷦^&A?3b fH7Qmʸdch 6Fձ4Ϊ^0Ls}c{n]WWO/[ï&Ϗܹ&^?@n>Z^ECPq"I}6գлc#!I=𓗗Գkxw`5d:N>D5y#FU>%S8VcuQAu(=T7&` {)<s ftaq(*2bjUJ5W^=8[~ 8&fˊ_ѻ5< 4td9zgLnPR!+,!Vh2K>@O~ 0IQ7⛗姳C woބkNJlQɡsκI RP$t_J|))SJ0 ;8}U\Sp\4HM/jk\8< rwU8yEdVl OP'y@T i G.ddob%X[>BA FB, $3O uK<]NOL8t [W1 k?sP7C@4~n =Mm걇*YZx9)nFd2 J[2HrJ>6H`.P̃G%o_[v "DWɓr5{"z>ygE pMl^K؊T%f}*ZX2D8ިrCR I ". 6\( 2At%ӯ)6=!*1Ͽ1)EDX)~FR72tko+F:pM=q;4rwzZW,Th2O0 `̿RjüW)u@][ŚmYH1O po7Xyub I K!;4iYyl6Xq+EbF DH{UY !Ѝl&B\D-X " ЏP bF,$"D$f:nH'U'sg/݅=c-WF=qc݇CކFcBwhF>b& BXYoR5S0T fH?JJM@=dE<(=w?؝&Y{.s&SجГjCo* P ~ ֊ƣT/D!S900DUFu`]ɖaSP)icl8&rtZKBlo1ˏHrXsֽny |#+n&}^{If",a\l-SS꽠r(1O>w|'wO<MMN;;<_'k5,`Jjݛ&izTz;" c͖ޫF^e:s'/܄X CW1q5#8 s[oy׾'xq9ߓO>{_W>ZXٳgsߏ!*auO6KP0ۊ[<9>XFnBDhzst{u`WSeaxrܚ83!8tp#-TO(^``!Ҽ/اUDTSrAdf[+=XPNv=lmft D'+TJʕoǏ 0~L#n(KrP^h͂])ɬ&fvY{SIc$ *Z0 5̦ p0'MdSsUug!DŃ/H]$WMJ7&Ҩv_Ό€Q}c8Be9| I_U8nM]1xm٪U8TVN+ʽ)IJj C4).SX7C; oc*l%kg1zXcȡzu- Aӑ26Ε>?1LȳP7m&1 _2Ϟx;JLҙ3%v,U0(Ƶ)X`fY'DL +=sgeS7=0nZH;ƃ؀X˿j}M.@e{YԚe`l. j-5mj#t3(e:-VT^}UD!gd2SY|Q[*|62۪S7ڌ:X*@ڦR%>r3A n`hz{f-d+`*$c削@}H=| g lq>rҼnTucsR524BB Jih^0*XMXc{! 캅 9a5w_ ,p8nw<W,5dY 5gW|quLmNÎ۱{͉{< ?0/DzPiY݄\{ s<8 Tl((iµŃCQ"ld\ UV0HĊn(J[FwLUXă<yJj|3cap O:kI!aJs|ܕ"oD!HS ~͠" n 8!ndg]Z83xbY1<ӊ>N-w0iBn ʨ3*+h4mnb&D'=F-p8p7\$NȠMh&.I=h0_;&D(;ږwGc7rqC'ɞ z3DŽH*&l/R^{Ʉ)J._57sx#xwN疻gW.<?͒&W:>d"c!g&Ocg?5ٍDq]疰4s-=LZ=붠A C!a3 z{z۠rm :>dEϡtP-Y8><|w;u >Ix+㣪)EnqAxgMy.4 "+ k FVR4]7_tъz0 3 zt#;R*+T@BcqՊ!Rc1?BT=ZPEk8]pG'<>45 ];iRQc^ sqcʽ}1>KQ(+N[;:$t;:-dw۶9Q]V$'E)3U, [+'Umr0I:Ѹ*-:/Rb'ʱX=_7i@PSbZi~RQyVI[h HҌ)˷VNYi˃}>RHZ՚-@V[@ ք^׽ 8G(˘1{P`#\eZm3۝FdؙjKt-Ѥ0YsX+&NS013`2Y7(n F1{PJŅ9,`u{kdW c:0 9n-Xe=FXa1n`6ˡW_/q ` Jڳ-MFY4 n05W2]! OW2k?>Bdcϻ`8㸟h0Gh3 "2qɆ7;{|ۊQVp%7ܔ Vm6=$T)pl)'N`FIEJ@5@3+71\M *$/g`>olʙ"49'gEK4&f $*g5XŸ[LW< ( ЪM4 7)$q ɆlLFiKQ~uAlaT b?sTx ~E[Պ-T\%!d_3#"W?oLbدōKsxD?&76Mup$d5)BM0{:Wo\% J&Ђʾ=mAG5QymD{ -mNqL&y"`#*=M|6^ yGz5<>g{KKO%yϹsD+?2ioTfr<'dڄtm$(MǝjXO k'  |'G͊}g_Ex}SwѪ5q_ TML٭@;J{^)GrLQKsMT6I%:Z%p1U3%cuLZ9kL95`UN-94)7 YS2`IE^j_e8 iXd4܃<=(Ʊ|K3<u?oozp<6';^ת7\2ެǰ.1lXgA\S$ҕ8nVH+tKoG3HELvcG=JLv+p'lȁs n EuGJu^n©]5ECb|d|z6J6(Om?ReeR2 8e(Bp @MjdQtiYL[̈́{2ٹWu.T;Iv[0{,{,EC_7װs=@@=I]oz۴P;]=i3) y]禠o}>ܽrN M & ݨn(řx Jj.LlauG/SREaoRivjNWpa,uml36VO."%s+)dREPwYyya.iX7EtbIJ׉#Ƚ&MNB6F,)uX<i!uT聿F Ǒjnoz q 1[ͮ0^\OM1To}`'o ]HK~Vg"9k3rsBKnXٰ% syIY@[1sѾ'WY1z!j"~v[ flBa({} 0bv&>`:qL\M8M&)[037O`<ߍmg_e{J1cޏM2T? 7I=˓rkF&D!S D}\UnhV1@&6"VVb3Wk#!{+6U76p31_lw/ \wcR3!:4(@ b^*ÄeNؘԶ11GԳ)$8.a+3à^@ܸ!kLBibƯ4032nd[(Cr]'Fܶ0q];~4A{7@TL+1T#Iv IVSKBTCsc|r34P>'#+yfQV嶐7s;O^?9:?MÕsn][oJ)@E8aJF2\07l-ĢOZ60ckf4J@z5Am_jS {6ՊVepJY.DS7[5Z/LRFٸ&e#)-&6E8E!rZj(wL!Hn7cX17ExxGpq>Vd6ΡCzK‰Rh db@sK 6_BZVBy`S1Fgh{c4TK-"?seJ)M>[Z"h"3򒋅`TY+IN֍I(XME!*vZVe$0ynB,hjӐUۿn诌}y? {3w^?#} S䲉="Un;]`ߦ-X3/-.ćv`oKs$QnTz֯IIRs9lꢳ XIk>oOU.cOP<25 k9=enFfRrޣ ~?vEV[p,p!v<#Tnqs.;9yP񰥕F`+7OǯUе0g [NQ3%UVs(TqUeJ&mfkrwF;ƴۑS})֬R9߆Mw|KP(삟P),CVp x K6(ֈzKi.^T9m'd~9"K7'-abp 8<&,}uXˉ7MeH Q^ZMr鏨v iȔw!a JF6TK xa-3&|n0->P+u]RP%l/=7I5oS9;E>OLVH)e_7X7?4Le7EKw ZnLv&0u#AׂVrC7)}57Q{8  *2 qtlՠWE)@+p\JȚ´#'ȖL9;wbdMR\k㦖(4q׍ 2#[ c#ߧpƇBx`G7Y w<[7@7ЬdqM&X—gzxdzɞsn [l(F2d[xkCs(`2v.5ۆx-F㱷󙩀MƮ;9YxeNhh~3b&k|߉5(BsĦN?Y u}bHTd1{Z@iޱ03(ؐg02 قypğnC{ex>b ;M]ig(~lTy0OPYv5.kM%I }3g|^ܚ{Ǽ1zf^_~<7ʊaA%Q [߹?}mڍ  ;o]><]z;7;Xy"'&*&%K[,ʿ4Ħ>`sq5_bG0{JMyZÚmyQw> |#-M7ٞ੟#8wG~n]=6|n~ oDZ ^Qj"JlWk{ +-(jkFqJ6evʤ H8A 0 eyV)2NޛxJOePd˾ sGgnX 8/DCU)v EyK*xp3$- r?v9؝\Ǭ%7n@pc?GP٨\xOM7)E%&^&)_hNR[VQ%=[wofqo<7{هs[nMh/pmW 9 |=σ b碭p wzԠjm+;z;CSݩ FVQ* 2ば:qZu.sZֺ64f)0ɺ:Ӕ9T,uA7KzKcF* 5TfK*))Z;8%F5y3Ylo`{7t\ zG]Se$G:Sl3uE]&y?DP| '`Xk B*⅏U~27)O N9iNIx=鐼޼˗ {4f:x (s뱿#nиBsg7+xc-ؗBu|E]°{bhvQ x {:82g&/Mar;t/!V5j c6*j҄:jSqŵ!e`%+hPչLl`BMt #w'㡿^3Y#`&ZFVxt?Q" oE-a0Y< {7v{~soz؝G1#Z- ۰Q NVhbk"5O @y|nJ6VMG9ʍ&%Ŕ2ǻ*d{ZԮ=W˟? 9SkgW{F}?)mݫ)(bDbE2Y|N]TNJQ&nb[yB$Y1oS'7)jD ^fM7-o3]ZsgikzyjKBԴ2O)+UR['h" h!I_ݚ=jӏg~|+??ыx~Wd;d@bD`TLj&%3ǰ sWpu,"w\~eA˵1W-&cw?a͙DBhDfAC9?&\Jy n9~Bs3$ջtzpva2`Pӌg*#́ 034<`i Ƴߛ a f(=&GLDwݷWύkKv (-%L=~n4l"xwVǍ9z-n?ﭥ4y"*k Fs&pS'a y=yAIjc*B$"h?`ClX X_Ħnh}3z ms@3~Y_ƍ ~xg`oʕ6t5’4)'Gl&Qv(%({AԄfl #8II¤*(YF& M<7 *RT:5rLs lP4M:p钛5+cx>K " c^!Иٜz8>PlQLq`&VaDKym"آ7DA!*k}X%Qj&o$ LT4%qS2a2`@#5@)%XFSTiXjO$ 9Bb)ǻGƍ֕"IN e6Zx$_K,PqPlׁsɘƦi3 ͍"X_2m`'觮z 87ȧ㣍P/߽8T&X[xÂ*c/HZЛP,ifk ؤ4E2z^(2u9K: 4TlK.1?V0Txm})<2P^/TT^ϯun}SP|\ÊMS7N)&:\O*t ) Aq ,l6|knsy.[FfPDJBQ" QIQ,lmؔ6tkT)6G&s=|[@y ywJqpg}r\? s8HfضbRmtNUt 3Y|z-V`"QWl& PNߤaHa([1a>E8T㡁 Can_׼M ɦk]c[ 'C6>޺mIeRN+YGReZ Wɯ}a>y[Zwg=vs_N|hnQ̓i DKGk8<^L`8:;^F{VlG2{FjRcrIv0Gwʜa>j 0Sj{ƕ#z_251h [͠hY#% Xa(so'Jjxuׄ`ùx_Cؖz"pU9ѼyK{LLIBmKqy(d id᭘!Տ[O`D5)v?;WmA 3IImPrrI8Фu8p H͠K 'F Fؐk%k'x=b̟:Xq[<Mʠج̶`~m?zWwW~m;/*29+A ИccCf^p|.)ZV|McDOɛkcCH?NҤ`U/!ⲰڴIO ƞfQA9(Հkǣ:" U&W,w7dGњsdj 6OI!*.6Xۇ|Puy>;+n! @]uiZG̻e/2؇ \66uv䞻SwOWΏ7k sqPx#Ō(0Ѷu^&\&S41'jLQ0EFU&~Xm[@bg*BARO()(pR' ه)%ՎHun##g?4@% r&^v9b{0l;S]VvbgϵT뛗;& +uJP>o&Ű M fCf]٧``"tC*D؅V6vpDupW}pT0Mɕ_cݖkeFuld$\Xk?X@G$F_2鎘E-P?&p/|sXϠۤnj6Abl E{+9Fbab8wu EY UBcK۰hK:c.Z2 ubjkAVh;Zq/J/M0ѥ`k`oֽyA%mBi&geE5[/2ĆB:[>b&s/U\E *,%UDžپ+[Y, MapE ػeIu{b2^ :Q}b4_W1AUk9wS/]Xp- "40qK踃׈O=Up0`to;C_< ,'Pד -gN[CezBzD>e㸌QO2^1) ]n 3&M5 Ǡ(o}qaԺޝ jVnjGsV+trroVUX)+6,&L۶Xp)5l6~70n%(b hl@l|+ԺԬh̼0Wp´hf-vYxiʢiqnvl[3#o,o?E0pj6 WdT@z<1(-:utdahj +ϣJ1-qPd嚆]pES=&#gRnPb o-0,lTM!Up/Зo^{-_+nSXr3Qͬz_h4u$X.\]$8,ː}MʍaEbd0[f`0ȫW*݃y`J=d<̶Eه`Y#@C=XI.%hqB)rUjd #7^B\A;'+O M7 Zf% |ù5[㳅JhHqѪ2XaQryb624^H8|k D\mk94U,`#ڷ^s9 z +'wp0ٝ6Ȫ$!H EC>a=`PSPj,&`֘2Fi;Lj. R12S_Z.ۆ#x]WD"&kY)%"e+S7(*u<r87y}V45ME3$ =gݚ\-]ᡛ'}+'߂ xB-#n09U) 1Ф;dFQD %N Cf@bb * $DJ j,ILDi 3H{Rg3ܰt<Ȋ2a`I 8 n6%ܼ;׮*’(eʣҊPz-|'_݃æ6e.YDSĹ7t IJrsV%@i/eRh%()3KԤ!T䒬Eĝ+Ny. L[+Soã_ZQ(X1eϤDL^r6]wz~O>WrIo[׮dϱ z0_{mۿu֧`2֕ػ/[pUu!ɔ:a&UtMNfCElb%j!cg*HCQ7KN V䀧7 (,39xBN] |ASYpcNqyRj{7DUAgaO OL<'H/Y6m gvnM{X*}õ:5ࣁTzq`ɭ0uY5uP1V[<(Iz$JLLBJ$K٪/zt/[Y6y_bu+4Gjm~HTFta@PZ Ưb$6RG-p8{h0ZqQ8{&)&({B4lGs*ԕ`nۘ7@P$MǾm*lc͂593Yw_A$.$DQ$eJ3 :ƒel8C ?8';bXH(B w A &^/Uy'̓UuAS~f4}* Tz=S}T4 R`G_VmƔD!yk:9We|7)^_Ň:OmN"LD]akCh鑚]6ON ^Ex_,*Z}9qt[:`ܶ2MG7yVC%Vؘ% $;4[,sq}D95 \z= ֐`| g|Šצf fK}3eTc"GLkxiYذgQqdTs9L,ʃj߻;)e3 _N'ߓ*0Y-T6=ΨӇQ6<)8Cq~=6ܜDŽ vZxgݫuU:Uisiu55.ZlzΞэh.W*u&rLR2U&ՊHSw+3x!EҖL …Vb7%_Uh4XdlӰ;bhhJ‰ʹnЀ^dL Gw󠮠1uU?V95%]'_4\!crүE6XC9jqN75#f[H3Wqkc:ujI 0߄0 ߓ ݘH4lV& spt\i,L0L`k^]79 IZ{(p]pQxǿ7yYm! 9 `+&MZ8һZ GܽQKh8B`%ZMr&ZQcՐFesbFn D SS.,|koA[^n?mOO#+k?/fKM]]dFڊ`{p<# +ƪ"Yy-AVf *dD~~6 e# PTed.`c'N`犟3܆ZJuQr.Ə5x6J@ϰmD 5j+T)!6 JwT,vuѾ*>W!*:*(RSՂ Z0Ď>"=L$;$`J&R)/(jKP%;*89Rj`؋AGA, #Ri>`@]ٴR;.v:[}Br<v6FUv;~F660n\G57sNLdG9ɘTJA;6mlTZ0 :!ῡ!j_yM l=_jk=lO\MjiѦl*,? T&4`X?s G& xSDq B+6t-sAע:]iX{8<ؐ狖iHQC`k,b*aܙ%Lh` ^} bT ܾ7s&./x8庉(yrlEU:83ưkD0CHQ[qlUo7'@JR&L *`D܏lOQĐ翶fBUPno/6؈QJOtm HcS&`4Ķ .|H-\MNǟbRn \혣"4ṏ' hyYە_ǼڜutJDZ+hNߟ-[-]M&8{l,<<_s>rنO!lmQ.+kG? qU"8>qD9C+*X6wV 5-}^aA@MV =30hZ,Q&ɿA(J.V̭v\{7p0D7Kz6Z+Soބ^w0#PNϧ~a4u I @+E?Q)gyזg}|u !M,ngU  ߌ]:G$ZԛQx~\:`Qez[Y輁Z :d;tn,2urܯtCN?rrrdUO`Spb忧իoWj̒gABo;䮶#&D ÅJ[1M/G#J-veeɛ."57h97v.ŵ*YYqz=6Ǝэ$8} n6!ȽnOV T} $VE6XR ¤If ?s.4'I( dIufHpСy pFڮYY֬rM*Z24nzeSO2ԌcrK5u4ؔ (W. `*R6u`:4(_Lm~q~IVepx6 )qg=ٝf*{S0갟5`Qct> t=u 3]%Ҡ0Lgd=D4|] XV+FC4R,D}`r:Ot"EU%Ku86(o ^}X/F F;_`{ǔcV[7F%`*mkVoEjNacxpK K @wrt~&K=L[.+5 {6gc:-D+.Y]۳pWVE2zXXU9fZ:DLМ0eKʕ';MYT(cicV}Pb55Z&&ٱQƔs}:ؙdIả BٮRۡt 7A!YJԉVyĆsY:(pd+KmU*MWh \0݃+\'8W㠧N,RUlViJTbfzB#yxJO)8F4 wı#+ TCx3g=i%b: CD+MY uTS)tB\a[v0ܴ% 8nݘ̝nR<7}1X/u$H:jo@˕ 'IMQ=' ? WfmRވM!Vx(U1jXmQl\6eűMTTlIԏj`2sPOY2X,?_˼pՇa6H9fB)/Jh5(be*nb BM7 ܏k8مj&.17J&AB:ڵ-Me"[:X51LfBƄm?uZ뫀-L]HMsa5V[I.O^pB<4-eW9]5ϗZ[˧? Kkc0|0 XWs@!2?XϨef(z4q]l1uPI R.u7^r}/f`k6= g߀+>ߏn_al1@AXa0zBˋqLB5&!gI;>F4lڸ;U Oʼn*RNis I-_6mFvd5N4L*eM"hrRly} %Z X1:HXYbD}ˍ~!0>X-Igs?F?7ژɏmžŤ5I[;?낀C8yJ9WagӿֱO|x~N\[Flw,X3gak\lCnKrxf9blZ01*NZ&Qb /-r,NރRBcAS(SxX);0}lKVZ錢WvS .K$&;h kn?]gnn%3)Vږ>1h S8?|7Gٟ\ݚ|Ǝ xeWFBD=FG5}.(-EgM` uʇbBbP+!KB.K1׍V ol)uOO kF%/ |(7ucJ%Ebe&H_G "` NlX:}]a9o0Lm#,0h0࠯1J4EUmذ0(m\wl'ȿqolЕy&/bR3JKMyHZ7% @ U'mx /p@Q &i:T.(gѺhժYeIȢ4_9иk\EvkP8oT̘f&^6IkL!6]j.R;޺S^xe G~O]5}?6`\r]@솤$&e:5F"9M Y!PƺoV-!"ڷ٤`vbh/5^:Jy`F_n, n ́yxk=t!¦-OTbV.7֩Ljp%RܷlQ:+ɶ u}9O%,+= 1 *Cl/E3+zVHG )6O<ۭɊq:&u*+tʹjKylV(G65Qkmd{(Q/5;ѾR,KħmXgk; s 3a=NknJhOh tm=u7_=o-|1ş݇צ~?_DGBPZs˱qsx1)|fLwZh9!X\Dq#Lś(3x%'Wq|$rz,2AMpÎ͚枊uC ~$*E>w!\]v}!jpP* ~&Yw'0IGд"$9dpkE_<;G'>_\,?EF졇V0<,junmnUܾ}{>~USuW.3P-ZF;LAW,Z ō*e(Y&ϟP_/l@1rEG.1sQJ룡 q` 2tƣzt۝&J4SHUy-0k2{,*'~cg30p+r1:v}1U U(t-bqƈe`bK3b Pu8߂Ln.ϳDAe;V%cj^]} 1Qc0ڙ 6l2T` -jK7%Lu+c8[-VI``FCrg(c@OZme.Hݵ :n(F["Yly@/jz؊Y91;wŰxE{t.qlZ83*ά1e&`6XAF^y[*𒆥)c o궃GX"4cQVLܘR%림AR9U+=<f.Nxflwĥ070hC=k{&d=)vH UI'<4Q D"ŔW*e4fe5U6p? 0~y LLRS ҂Ve~vuԸ %Lo S05QC9֡f(@@bӥY&LG;_mUbdDpOԑDLZmms%r|J~;ܩ`cb`o(*PaoؖɔIt@aYNWPey:RRеu [3%[BBtMRW1Xt6j/YyQPvbm( 0Օ*Q%{d~vUt+mUu̵~3ٴ|!;stmVnՔkLͮZ̍d`s=,ؿҋ|7YcL68K<ŰK*aUAcg9:5#jrmlѷ~{n|xj>QX~?%J5>5/HQW@9tϧ7kYPqQJT`ضjPjRW ϥ ]Ck?Bi<*nE#3Scc~%D>GɺFY`nZhmuHe=_{gS!rKj^o{IpUFɯ׳ vN H49Hz̢֠ESEe>j%L8Ys gkf2-9@:7V,vC;ɧ6J_.G!؞6u؝pH9'q_&I?#EVEЅ{VyU ۯRf:}ތx܇{le?Ȅ_vц-Nh:+^(4*̨(w %jlhKJv(H/6iBvN#%\rŖP#" t LR AȮkSf4L%wR k;~@"9|g!(sך(`WGMnh)Tev nIav`dsAj֛Bm_VJ6@_ }uΐbc|A`cn8dOua 7Ŧň\rBb&PΕIOا> >p뮁ɒ$^;3oV^XhSQsu>Mx¨z6M7Y,S7ex\!i-81oYLokK ؙ+JR/Z38UnXY1b r.Qg*>?P;`͔(2cرwKkO cس-CL_DVj6iJ3;Mpb\QZK*V0 q13:nL׌~62ft+"@b)b# {Hx5=aEfgYvS dv%<{/{wסe>'ǦC(-v9 !ڶ2u/WI?jׯa @e`e[} (KQy;Z)g7#C 8.oECk4l?qpD15l~tZ_7Iml2xS,mu%{5A܈?ZC l?o7z2PprlUל5Ȧ7k_Z:(YQSlOt>OL2 8Q R-RΎb#?W``&k;jJ PTl1;_hmqz!-:PcLP^ 157W֊y(Ǥ~Rt},8(v6ZpmŪ!=Zɸ&ٶ9)mR8;}oYF Yh_'Mѳb5b?I*&uֳu)< YRCqOE󘮛Աۂ/=w{Sjҏ27YWXY~G@#7E-/TVkʈV\U-H0Y˖XqohKCGΤu{Ͷ&c[ʲ|F s5'<9(Y* L 1XL-+ATlM GUByhi/$'Z\; 졛К.~mnN/a??Ƥ`i(WH3801kj''r_=9։w^#m7pX7'w`֨y5 C2׏K갮dY631RG5`/4]H5F[wֿ3FVR~dYCEY]@ 9e!2A`d? [ qy b:\9=a.1aJ/{Jte`7hUb-W._Cǂ Pu)@صmÜaaSt@WB'xmzyu% B)];{ԩ \*TRiJ S>"KHQ(j9nV O~xbSUMmv6Xuw`VkD[Rvx~?ǩ]qXa 0gQ?N ft?CNfX|3lb7Mh,ɏMsT\fzT<5ʵٯòKr2͓7wW+uJx؈ ?бED7SۖpE uu7U $U3Ml\T4gC^u՘UTT{}ԅ'7~_9,Z/2x.g )9X+fn:AlޘjKl~[8~ڙNmF*NDTɨ%qŊHd_eZ7`o.ރBE 25G\V #آphCB&06!$ ㉃SAkᖛ]e EA+z$hX#_Mlk;pa];s?P-<9yJWnBMԱAFr-`rF,h֣P>!0Pʆ 53TF!M}Xv) r=N,{_w؀[k~v&8/wFp稁$Z5[hS|r`32>{%lTENVUE=DQ!/cRAh5)H@ʿ`[d5"3(?jL)?vNhFgN/5mkr =f9|<Y'R:[l5 .>k`^V IvB,N&ZYkK;4߹lzc8e,0Q }'![U UF]2l'6QQkfc?>smXU)1w?#5Y9 UJ,\@(m4 XUbRQɮ oK?YuH%2Б,ȶ|>UĤ`\@#c#_c簹!3lOUOD@amٖ(UT⼒Y,:O1%:)EUZ)w¹u׺0/ZU$։R1Z"֨*ZeWy5Υ>r=AuvMVlX$`Yq?vcm:ִ|A1y6񒛮TPseںaת=h~llk?)T۸zǏ%=v{-~)TX,̗?BLz hM8ܝzw֡x-LA7+x̧ZZ]~V/' ='tIM.i%NRI&Ƀԩ"˶S~F\A)Ok,vo1UT `>È فz9з/jSlh2L(p|a61Cn2cW?7bܧ!E<OҞKN~f nU䀐S)LVI5ϼIJKݣQDƖs[PRk?PȮwkccN|P\x&O8X^;$9Ddӏ~@u?TU &`~$i⚆;;~zzOr炃,7EChr 0-|VΛ8OrJ&iltJo'ی1K\~m/':uB'Ppm޹2l4lg`FAnԮ#~qK"*z6aw?و#^ש zˬJJs䖫`@ fBiCxxKab@}yq!/WBP"e,2벞Fna`['ﵑLXrbhRmZ՜Qi,\}8m=Z9e$- ?C]Y<*ա=t}>a9¹?|\4;ce_dn*tHwWQp-a (uo؅c5>3uUڣ16ɲm | [%2SJ8S`lǩpWNo\4-ܻ-Z2P,݊;*vMLˇ/WP L&dHDNHkzՑfE6b$M gޤD1!R5bǜN)s"rޖfm Z(AjTNRhW=s#H[ڲZ8e3J$ևܿF,qd5O)=Z3*"Bhu/NeߐF}z'ل]i;=jVNҘseRߧ5|j()H0T&sbNZHs`Z"*!@@qT'lXWzb `*[?N8s%٨j5Iq1Rؔɀ h c ̵L0 ܝj}>Ui *m`s|Ǝ R Q 0 mRv8*To ;̻N/ZK(7\Smn˩LDQL|Ӊ89[õX{׏76.e0<<6(pпn?:=_3;??P(YQM=$woG͂y0QK=֝_˨Y:u'7Ŝe$zMhIv I7cFr~zd9Irsf/GCF#;tmoS ѵIu~(+t ^!+JidǛ7 ؈ͯmJlѺv׻xj01c150k@Y3!3&2XX\SXy)d2PQt:-|8yCHa8lP+X{,D88C6.>O!I_--JW'$N-]6E%2,m4X;Ԛ^֠)쪄;)S 1ep2fiJfb+G +fo 5W(κdnx0dB& ˬ YaFx5|Ɛ@cp*P)\fbi'*Pgh@Ayk%nz@Pz ˯LڨZbajY)}Xl:A03#*eL ȡq2NZ]_<7ݥ'Y:~?qbUױKM 땃<' nѭ{MԔiQ(,QA `ŢɈ@lwLHe+*35[BøVpRmr++9,6b}G#y U[7`nMrш6Ća gO_ DejF~lnLwHҒZ&=a& b߭>CkrDp-:Ӹ`[65YBliiI^ܱ*x=D"@C +@eRŤJ8}`4Sw^?x^u{3r .ѝ߯? cs0U%{R=ѽl9÷:H TܞX4mN6>!MT!mYϐH ,Ȋc*A+4jKĦ؈e>ӵUc"忯aʖ'uhty@A] M kQ&儈򥪢*GR2aTD;B fo\pj\BOl֭ڼGJC\eA6K!5D.V.gUQR$uCqh 4S*  {em0&+N^t3 ŊN%{5z=+F9/|{կ·nZm:8ب`~礆f̑9VIgÇ;0kCdot{;[aw} ]>lkԡՠL!qNOh#Vo.!)U$);%pߔ2DV#lQmv |-Sp+cCu!R:˶d݆,3/I;}M9J5u`T@:wlTi8&t 1?{zVbr)Vm R~Ty)oNc_( A]q'3S|_(BoafIQ9;fqR8Am`:/#mKLGۿ,M+[SS˓VU >/`ػ1@S՟%ЪA+"0}@D[= lmSϪ\dw&!LW4e @(rVn{IP控_oQAUsMM(W '@X3X't)iTÊ&`1䛧͔n\MvMP~@ɖ5ATY (60/fPC )]h( 'E8خ cqljJ6lF b :N^9`~| ӟsL.]nfQg8/`lr,*y\ W<{o (`?p !&3]R?c&0SJ+V>]nR$Ͽ=k_b4oa]tp`MTU)O%U딦L\;^7mCh;FNeȂ3]BҰkMڥ?|ma 3"UQ {Xp`pF* (sy[**Ax2wL^bΊMaVWZ ^_PC {۱Mw+yq!נ)-#0L4bJ{z\D:C$. >vV!ٰδ @4#ursD : xu6dla ٭!&N`d{Or~Lg_'q ҉d &ȩޱBU4v )`@#*RThad:$5)sA)N U@ymXx}lxqhBS |縁o_/*҄&߳e 'mT@jE%2)) ƴNw4EœdIA TX.anè^*Z;-OkhIE2UBL(GԤRm)07(mht/WMhBE<PLvxr][Tju b#FlŎ.%ۜȢ(3&u> @]YIz)!T%!P)|^`>= 3%ydxz;&XV<+*n4Y-ۼE3'vsq9>kӽ[7ʦ7/MI^TlVqLL 0! M,T.<3[3 jȭ)w{.dcm#JksD@>D'@9.ebn⹧iA}&e DoA~ @O۲(c1 f|Fb-Z GZEXХl]Űb` L3_;1Pخj''jB~-Zy?9qa *dRP$Ez8ck 7 6`,<((RtgL,lLpy~mwnV:MB[)P޶8ܘaoGI4 [ CnEb\`3XqͰ :[ƽMi4xigғ5D(rJw$J yRW'2󜇛,@AL{Ԥt&Y;o4uZYc6ݫEe]ASa 1*q@[烊BSZ!1)} rז򬙝p,"P:ɼ!/$%T6T=C2פ8Nbyr\OP)!\1LEyW@d_it $ױeEOMxkc;gW{T܊UA)br=6:@e$޳Ǩst |VZ @‚^~>=^;h%b-aM {@Q!7P>42m  f9} ق-.@qyX`232 lmIʿ8qe:HQOUiV5d@CMf@㻲蕃ɓbY9GVyӵs (ztN11VS6w-Q)|/%A+TP3ع wɅ tɌ*BxWQtII%ûc,AV,v+Df\`*] HAU(T$de#RK7sI^ _ w^nSNq@CҞRua PI5uUگYc<| 00.2ܰ0"*7J1JmEHmɝ0Y5e ؘݴ!i4^6 P=PÜvVU]X4&rtaE溊 F.DG3/ V+y%]$ PbhԳ^腊偞[73hLtG|Sm(yL!{lsPR15,巘w%hGΘ@>U1$ދ?Y,Jg?3u!ݿwmX )#r{2ptN)_Vp`8S%y%UNnjC b- 3GY+_G}_UkCzZ|&YvUIJ`8}}@C͞IfHQܔ`u*&m2F`5E@`붂*UenVL|}L"Pl{@\&aK+ ZL J ?g؊Ƅ\%5WkAQޣ b1Jk߅ouUL iuz7OG Д .k¨nARWHe$nZ,(קqLǢEcx-k4mΧ f8i۬1X:pH CQbhpDcl8Ϸx # \KΝhy&Iv B3nD_Jj+ֹ~Cl`3GCT6ݜ0tXL3)-7PA%ű"V~l40;[_PUuxUG 0cagcsmFkstڇ$hgv7ԡ:Y{Nw;iWf *SuKZ W/|EhXzuo}w~w?>>5?o"IXzsC/,moowOW^ 7#lb), _ɣO]R0~$앢9_:*IpYWIA"쟊*h16w<(Ƅc P \RW9]V෴`Pа? gsF$2(]*4ի.Nn~~InS5gށ…F:NjhF@l gkx(7\e!B;!Yy j^"bSƮ-]2\~l)m5cDzJU`M~6:1e} v].͟=RalW才M$gPp{k8R5e 3<2² cF9&,U#Na3$;AS|¶\1ޅh]KWij d#xs}޼ ӑ?R ɵ3+|Gvaf1?U #Ȧ *v,<3uT+4s)S!68˒E"PI\B0<6J SV _2Z-Yk$Ku| 㭆&c&^j6׆3۴uXKgeQ9brbk 3FbSF^[55tUyGQ!dαVm\C[-V6"UM 'g8EAv;3@r}- oCVQ[X'K1ע)H%,ZE3բUh& 8ՓkBB0 bS yx1 ֶ3+}ְ~{ǺQ =:{g#x5|囧W֤ Mwgv5SKΦ iqjOY`z!I6FB&FbE(q *`n,#AM Q[tM= `fajymq!iVWM9o =v)2$^@ꊶ*Vz9w*%׹Xo&>&wx9lLjK"v p0J-U6 @χѷqpzz{K y͛ WM[[_d3=>Í~sW`Qc@!hr)퓣mkE'5F}z"i骆 -%,nZ64jja7쳫Qy $t~u'} c%ePa4% э=en`Q6ukQAÙu_saQV%1e5Qg? f@a.Jk\dÐlDf t>)px6ւ`N- :*"``Ѕ:fLgpuc_8/ .6@'[uZQf@5uI`XU, ^ E,RiTmO6Ȳ*v@})Aԗ`yYlm,_lwA( 93WC@bk5ݕkV(* h + vlھ}1זuĕ%Ͱz#9-ӯUjfQ4Q14f`hgtԊB3DSln`R`d7‚p~+-z#aWm"loA!F'$}7Gbv`k:8i" ϮZSKipY^cZgꘪQ.Q <b *9hk.XQ`Od~Z![fgLRQѯ7cz@FS͉_fU+ӵ!XqrJ\56%󌾇U9AڒԒBRɫCr/5?s2sw^OM׿~>3q8 Drp-rKp;-z7։lKjvg>ܙn3vs/T`U>s9#>Py5(M!fy Օ6= P)? r3[ٛ4AhCa 7i6 JWGP?H.D Lxw *ibN⺹bRdjr&Pl-Lnzc3φ,3m1 u5You<|Z|ڀ%놁z~P Q2*)4Eq )K=¿3\θ A20gY)`QVQO'ñ헐T\!D4Fy kN>uTM@u5h,(x.~/?o{dw8tJ9Zg~9 oܟ=G_8ڏw GSg~ex7$kAa3xC#FWzs P]MsHԦqY )uTanSM;)pAe9u`RⶮcѦ|Vǰ"ʡfϱ--l0 4DEde#b:7&7I浱QOu77+߬U׎ ֯L.9<3xՋpI;6/r߼ Om=Гs؜ŏu1)76xOX@Y(B jK`٦n B?ԉeEI:t8-*;mA ^5D깎N7~G ƜdI0+EA)wx~~~...7&wﶬwF eT={'ljU $k2Z!]5a[/k02Q ^~)[2U=YA,tĐSaMA  0%BuPnG7apX "ŀb%!4訡3m{34}% ݕr(S FylnZ"*EM2n#$lnņ)ȇtC=eQO՝ɼ-W}W_!Hͦ1b1nxʹi/cOg:b&i$5%VfFsY:~@QJB.pu~  ws)SiTPLPVҖ4SHfeQv&&=۱K^(n2ٳ%exW)34ߋ5DͲ6& ?eU,]̙:f Jv{>muIyAu,'@؈9NޗT6´^kB?Z61-6XM!׃8*ٞoٚu)f[چ_w&.2s2Q]k(eQd|RfY˗y= P^2=DŊoI `?SԐҖ뭓.f1T]ڄW;{jga?.vdzO߂:n=菍Ǜ똹[Eu?x8 t:wꝯկ MC0*, #>᠘jY6" K`O_涀nN8L5DUBh|*p,*`1w#Bn=dST1d9k^bʰ@l9MFA_z|%w6BgFeO!c*@CB+&닸軶AmڨiiũAJLӯյ UQa1!ύ0'DOB`KgC=j hlH2i%@*J gJ8s842C+|֍Ҋ wLJMZ텕ȿ, {~w/¦X@F9=__k*?7OM%i¬Ѡڂ~㬺bNm&̨ XDzc|T91~'5bVvY8kM`rv7~'4NRwW_< RKTcH7k)O=vq2lo;VpXgi@ j+)2C 0G|+׉@V[)7 i&4DXr7u ˶YT)Zjk;rjIVb9M\jlņEؙlf`c|Wp㨃EK_bN [ڌ6v6|Ȓa  aTstV0eCڔEGM'z/4Zk'BR6;"U7j5PO#YHhݚ9C| V2 P8UgШPؘQ,1XYu濓KBc0װK"EYRu4o*53Y(#c?MϣlIaM ~M3^ots#_9u8L<+ Vn9Z5 ʋΕ9gIb{2o,=N~h95Wq4QD:WQLEFy ˪\rzKNᬞND-aۃR]uLZ#L(37VY F8-E'.V8c*# Ȥy-PJSdvKޟ?B5ک 0Fb W}˞//iTuڂ._)p% :Hgzp<جB݃d3l2{rý5\+zT6 8JRl*\,Ӡا`AاLm.=1dr ]RrߔB"cQI@nf ! ^Y-Dl Gir й^C?}8֯dp` Գ]99p{DS'8߬>?|N~aLcYњN1D;̪ܚUN2FdsFCSTR⽏N&)2KԏuS;u!Zo, i 2k!Usը܉LW'Eeɠp15|GξwU s&,J8Xjl]f[ }AE:S75Q bNT aHz׿ /AX=@Ni4,Vj>W/ltB0 ;;_\ ,մR$h{C^UoCiKfha;z *۫5,*+UDp´iz׎yƬ\Ȳ:^U.8!ۺk"kPxc×igp6PcyhOl2efյVkmsk /㾰4ZX/逵UOѻ2Gz8FUUlF[9]wXػ0^jnLڟ(r4x IaW hnjv6`o{ #o@cUYIQ5Rr&EbjxxX1}͇)ٶF dp4\sƔ?j,4cj2ǿ?3m`3Hy,)6FzfSvm7[d\߇^*k9 (S0)CmTzIR(b28SvNA] •a$(lĮ =$gq|(GRc?oXQI1H\ ǹqk(rV"Au$Il{n.WNau =R2ʶ>ocT  MlXʃe7~o g g2F&0opnIgp؉C2ld7,N&r/l r"D024]MHHRIW/S!)BcPFj7 abg<Ç>9tPx?׽x׃w`n`w_ȗM26 Jg0MsV}ٴ AQrd{^[+)g6E)*ThLDEVIYD464b>\`zO8fMږ h!BNE+ST!΢NeR_|jx̄,fVATz!;@z6҅#p#E%Ѝl܉}Eޑ#t_nk'G祋VPf \A5[p=ؤEhف6,M6q~>۲'u ־ /!p~치˲s}wLBmI@ąh iBOD6&Mmi.Y%b2Nչ>#|IF9D˵'FQe.:NQeLpT_Sk(g r=.5iΨ>Xad>sއeདe􇿰TvMw9Oz89( U86pB!.1=T;&+UO"[ #&tLM`N֋>RF޵(J—I aiI[p o7[4"E5\[䓗_õqTBE_/eXyGsXdOw[0ǭ4}̼-o韾y~q~O~pG{F<|4M0!kdl|~im#y*[GY+/B<Ȼ)8l.P ZdH';8~o`%XʣA9؋;c~(YX#A;hȪ5,6S_䫬UOq=ai4CϘ V7iOhZE -Lg?| ~-oc v{fD_!d\/StnaT`4G7g̍>|N 7{Q"K}Цh菅qY !H9 qSdжVEK)gˎYѤr{ֶ?7kTiw1ʮ ej_:6?qMP93|kfC4dC%:Ŕj7}ePwUOc83&4|>sgt:XD"g@Lq|De :ఉϏjLIK9kʀYP\2XCXէۦ $2aeSF51Phcג&v69tvAUǍLg4bH/ K =T4,]'ݠ^,k_.[XkPʆ`j 3VG"ɨdƥ8(Ld_+mط&Bi&a FVp#e+cT^FosfǏ+jRj[Ȗ^ ,aRnl[IemﲝrɚJBJb#f;Ly)]3s?_b5NasD#?|Kxw`o aS6gdw-5Yo~ZG+]>S>/i 3X[10k[iOPfK-~92ғ dLu ߷ H<Fņֱ(= H86+ m-I rqYºXzYı4FNb%#"iiNZNMzÓ:$)t]#JG }:7ؘ059&kdֆ5>OcZѿBG >7=ͯ /okCX.N&NF0HPp<4tMe/њ HbX,W.)9,׵Qc8 K,d]$`i"JX **E;<ǽc"jqq>lUwi =eCnp{Vt#H>Cr6J!0Nڸc|ϻ QdH ?(RU2jHu}[fDWL*P,860ՔYEʤr;|:`dVGvBd\nk+1@ϛ0777;88xOQ"*r. Pl9yN66=s܃߽՟sv"fUWd#;  `XfJ@e>ar4 r||" LfQHn% jA)X9&VV *)3ڿW,'QF<͈Ŕ4@͐*0'y )G+fҊ-_d,;AXNLC aU`~ة* $Fg!f}k 3m`Cp#;^9WY J;eunLuΗo|;m3''py+,;bCWפXvVuPq>vw0al8hNQ6Fr\Nޗw1gǬ㿩o%gd{`+h4@eSd*B V(oPm^Ed.0l5*Qv qKxs&:PyL nUllVdoSXS*SG<)z:GV8/Щ+옡SJIEuL&VQ>j۰ Ss(*Lg& :ad\PNO=3_^A^3 !ٳ}{s~.N88>]9X9Z\)|kOʍ=V.FF  &A[DLS zY4Q!6e{gvF[&2bU+[:1ΦyNԤ_{L`q]O+~{S*\9Zz`l`f,Ϲ5/AirOY;ǍFq'֨92sX&[@V^Eu5WmŠ@6Jm`[Wc߀fK?uvޮ~Sx/w?Q,Z,GVQrv@V TNAbDĔ'bfŪ̚(\6*e9\c񞺏TN6+p'ɮ8=jqLc5 dMF)"ﱙMne2v?&G{L%ۊ6e(k؊5)o(KQaJ;ޡsjXC҇uc5O_ kteUK<2ؠQw_3Q}P8 ۏ>s Cc`jVpso~Y2IS wj cԖ*M}Nʽww%v.>Uj/w_us֓6Ba 'Vm`d۰D+^o  z<54t0s|hTFldlT dJTʖpXД,\s~{kΏC~^K؜wNxC+ß؇NlMUM*r/azCnV.E YOB UdSx!!,. <+7>^>hIx7;"nG<3 y .j T 5MPpC7o>w:g?oTo'hk5'@5z BMn=~7|I̪FM.j[ .-pЊC0Dc8P FBmء#KhFxfI'>1BŬ飺btuhJPVQkb^$w+ `ʦ~X_-ss3RCڰ2!X$lϥg}.p1Mr6ۚF]pױ<[bfO(S1 L,1= tQ۾ZS:auMu] >ןe] S loT (O,AA_[EP1XB(ρ\ Rr'ɳ[t600UQ8E>Z]T5gPph4<j7Sx&(yzAaQEЀ *[:mcbLxߜJt1c1S૮ *T /$'#x(j iv=fJV@KS9Mu JR#k9=F,⧼AY'Oc_Bg9%6@r^`֕5Z+jH،9x&5}2DcNa \[wX렛-z[_[4hط;n46XE =c6 x2XOƧ 5X睬F&Gh9M|%o9H[՜Q*X_ՍئdIIMf:Gd-uxRvQdQ &%eIp|o6 '~.7~BRw˥KM决awMvle7Ȱ<"fkD#J$0ܖ#+*J5&mjHq0oLw`1/}{~n3="I-Vsxj9/__"؎ݠiT`8:-~OczOdČP,zAfEXP'PS{!)jV/pnK0y\ӄ] ^_+8H٫F~#f.2.(s:3=4%uC?/A铚QS # h韜:Q.JtcWkxL=vxN^y~t"GÎVє N^J U텓% +ug`yRkʖJ~ ᬭ)/=?Nފ7iw?{o8w՗[ѼH >:%! APlY`a.[-ۖ?ux` wap~+?}k0%D &X֫ՊTe 9w%1E뷬sQ)izNBq]@ \':_ڈq9qm>c/,I3 {1->E iX7mtx|C4ImǫYz5QU=2_o?k~~wm0hW }=[_^D%=tGP+Q r !Do25*Iv1fJWڍq#?h6@7H3xQh뺽6c&/Hpw.&F{y ^s_{ }?V&eDedI}go`k Ph'Y\)$lDh1(~~=GEG1m{C:Mzp&S~. $<_bF*ΗݽG! KVdT{*9 Ko{Զ~B7ʶ2e2ys&aeUnoȓ_SٷquJȨlCS5^ٳTN0@M/ߔ֨4Cr*נVeZۂu Oks# rImAPf!UN5iwS3 P* : ꑟO S#c^#ng\{ ]$*\huڂ@3L+*oc)({;Q|]hs:`g<$eQ\5]Y;FP[ V5Wa8od6}B4ls-OzV*jj5EXˆg*)ɞ[3>ԅ2TKJJW0 *Ba*Um 7IihU[#GvdB #Q Mk A}&GF T+@=ikQ9 ,:@gS@g}|Fe/.h %P_a䤿8&ST*Ch")TʧTZ<`7kIkF5* ))[84KJ3^lM1N,&"b=D3 v{ygGgT٫M=u` ~kpυizī_2bco)ܖ `-ɺ;h%5 b'"wAVi3Ǽ}ec?tbIXMල~=go.40;9.A3˯` y8qsqv8&u05EҘؖj-qʤ,?\'Q ɘ/W C oq Fܓ0!1 5g SX畲~,)x]JlcG**DIMGer|nZDiǯE0@+v[~h@|-o㵵tR)v&sE_AJ.,RXJsWgA) EQnқP )6@im#cP)"ޔq'h4`Ee5d_<%wT%E/Ssv}܅gn2 ߃2 G]` \yLyLr73ۼnJhf3%[ɰч8.L6SAwSt:tм)ڦ!4()Z~\'ա"xvS`u#0"=Hѵe`gk:0JTAޛz~fu kO Gvw,]eWż܏7)lc(`!iP?cn"(ck6.}4Mb!JT8IA%W_V^6kj T3p `m R@V`O: @6 Ll'kRQ[LaY[@!'[P2FUd#VĘW),XRaA6*OF4&˶тGQ+m$!pm+899始nuՕ+{tl_%7'O'KLRسק䑝P4OA,bjyO[$b]6i'Ju9rN߂բ*J1+lFYBLvO=&BهbgOHA \Al֬5^!5c5ltAnPu5U#eMQh{5t m-}T YM٢5S7suGqg}mk< fO>Os'ඍqLAT_s6)b&4ϢZ'*]DjeG>eZzAMMa`W xYd%(iR y_?=[KdXkn85?4%y|@f*JמRRAA:H}UX0l @%9!P3ҍ0iʎ5F9d&/U?8_Wߌ3zNLyWi<C5hh|e6 &o4R 旲.fWZBX*l)#psӮXј * =B2,ܱ\hP WX)1S6apJfE-15ۈҭ`: .z6hyzx2e)M }[(Q/YكL gi7[(Xtc$:Lʡ֚3ֲJrLM"nV@>YZQW,bs%'Bi&dИoO4pM4a`s΋>zSCr9ilZ26س㗦(|s(q*8O>#xߧwoO溿WF7~vw%ip$siZd}IO-RsNPc8c.4} | yD1"^2Y@4IFGl nRcױ='TobDvi.MK"g]"loV 85ٖt IlV+>TeA65!*>M(OMATp[N,9'>KD (:->a&kg9_5XhMvV+ s'5]D5~.Wh dI VHVrQ.,D_>ae<_/׿s՗4 T>:Xek +nU2i_YyYd#6!Ȱc`~isVoku=\?"Ds*."D)@LHAU;%2jP(dURs~`tЪpXCΘI>q+8C}>5 d̆s6Bj0|?Ô(-]$v`wk `%>Y7F"k=M @/coxzb.^:Uv]V1vOf4cDU ߬:ϱw6_s*͘k\[Y`"=ޘ2$FD^,uA)Pysl6(cDN }X`9<l7pE@c#D3^u_iGO2Bػ_+?羱?Է+ObRP( ~""E10<܄7:#Ρ]B{ <ƵOpC6lu!Vu! SOP)^`ke:gEZ㬛@)9=gп:.ADI!=RԄ2O$%xT{Vu+Lʤ]By>$rP4J3 uv^х Y'ٯ>z ek61;o\M0A5 {[2{ $7 1ǒ_f&'阐j1=~E_: >ӳJݵ=9ه'[BlVQ ![EwdL公ru%pΕj,=Q*s&^X㔵 ,MĆƘKlw"90xlm-U*1P$u"dhʡGIT=>T,Wo޵ pZw { ڃw<7dC~~u%1mLJbR}L7bsu,UM{UOs5b!|tX xa@ԕKe*ԉEl*6%,ѐC-dP`ahvKoނ8n}ЁGy<' lrgjbq>guςX3Bڶ;7ο/6M hor⦶$0^6bD$YElIJSCo?kɮ|p$7_gTnjŞaq3)2Lrҍ̘-*|ff|ta՟( i0c^px-7Zs]ٺAw.8eֲLȩ cQlQXkFl!F@[7RvSzSX)OCiڡL4F5u6)" A$8 ;9#g5;lArS-!?e5 ~Ф )o|:cP}݌a| 3[?\J4ݓϹd ho;iWo(aj#D6]ggԍ)^-~m?xklw:VXYsLܚD}/hbe֐m qe)yQT5ְeaXhj]`JK{j.GHlʣ8ŪO< ['.IUn%f+)6CotpO |-iBTZ=&EF:96帪FVYE{57:G=1yguh#Ur^gE d1WضI46۝Uz̊fi"ksCXeAIncü&Z^T\;W򻚦"cYi@9LOtgN\^x1Mwy8ˊ9\ &}ŬyE fC>vWTX9;_\YplO<˙{uHBv$?4MRI`dRK҃2L{8*=E{lޘS=Yr}Xdw0>$b;kq(BJ屬նw5@VkX6klkpnW7nn4Q1s*A3sy L{µ nYˏ_}8$0?9k:_3xV$J[tJN HQ.MJd&ug6;5cim$;Tr10ֿ"64ibVD.Qy~qyN4"U; ku s& -$N|6r;_۽O~˙60Q5^q]fJtڮʥg҆k̖ k@dj| 8xb35) p=M Uo ˿Aw"Iq`n֠Ge_1*ZҠoYbV+rpDR4RG<8b7Υ:Zl47i#=Jjes Rull72_|F_{f*6Opa^|F'BF+omrY$qnF*؜J D%7+P~ W s[gE;S"Ĝma@8Bo#7F؄&cCb#xsUV6k"g zɢY\}6acjFss'ljlmBwP&#O HT{!2fSiL0b^ |G7Y!P\J~8TNԙNu6^3C0J5;rF[7h\ v`$؍Ԥl#,# >PH~!)r@| GeՅ-t6E_/1̗[5;Lί ?zK{-z;2jdz3aEU--mBdݞ㖢(5dNj܄Vj Bl؅\'y#. 1]qVR\ٔu.Yl&9K~&4i׈`!GCvʻ~t3wpF2-R͗|ʱ礮qTZ{=/VBe̚27/g22 fkò51 te@5y_ y;^crc{"W/EUF8+gρx[8УW7VɌD)PbWʌP(=%L4cB@!6Ipxԝ}WZ܅G-ERE{O߂7n}=񖷼+wE< ؙnr>}QB"x6͍ͭOw29:<Ҕ:1˭S14(O\RqsX^~2J͞X)JE¸>!Z:Tʤ*A$e&5-OHuդ07 9D&2`i dh™MYO.5%3'2m3?dĀc#:V4ƖL^:PZ}s,50}-Xl D5:sڨP$9ׄ1}`N,Vqxzp'OVJ~ڨlsak0+$W)',M *ih9]Q0c`CO)8lɬtuM>VYeEh2^@DaZ~nlzc٢ `ti\KzW<(`r2|PKחo|&Ӱ4 5a{ 7uG@#cYzYhk\=m ,ޣcMek50x axB[ӶpM?<3{YX_( &+PP 1M%k{. TNI8H ɥJGb"J[T}g[yw~`1D 2xcBɔ,ꋤ3Hw y-y}眻oo}"PPeDRܤ$@-A@bD$? _A8XV % DQIq'gHg!gU3cw{s]]cǟ7'a8yuQCԽ5ba܉.uakǪtDkȬT1cٛ83Ռ$RP&ʄBf"S åUEsE!TLCEfJeÓ@"(l围VV𼨲\ 0+ Ogf7@ ٖv>'df2~ͭN*jLHș o{ 3m9X\sfgtyN=Sݸ6o^Fx=Syr~x]%6}<v7,zvOD}2s8`F 1Baf2d"rih`v I1ĞRr1kMrvte0=6To8t>T*Hyu>C7|ҳf]~  m[3ź&<ÙAWReOxgl6jcؓ,3rgn7܁k &kmj`bk OI ohzڰYsccjD;fV, khQ=#;=êeɪx:B}C`b3G^~{+1šqAC-V1道x}k% Jv "J%#fZaW:^XອIG:q=oC:b-^ǔT{j;ٱcK7E{xٴSs9#TyDbԩcG>b;kJX֯Š%:cx/*sz4wh=p'@ o&FXvXu#Gz=2zklzϛ~?O7X7Tz+Ya$3f[H ;D%دm†&&GS̈ &4jۤf 4FTޛ3hI7}Wwt!g4D ?ЂNg+RML o3D"J5Vgs欌2a4S-PXc^֪m{ϻ1NЁT$Df2:**ޖBaV|VwM9P6`sm@^eiE !U\Fº05qLyI#XMF4>Dq>S`'>sOa@Ӹ֯_1}V(Wz")ziPGr59acy &Ou){ 3 EB{ Cv_<; tu@6"dΙUPH\($>D[l+PL@H3ټn 1Y>WlP,@Ԓ89#`lm nLVoyx8әP &$EΎ}"Ged.LQJ{bS Sg)dʾ ce 2A'W3ϝ[۱38H]kZ?;ή?;d۰oP1B-[с WS0U{w䨅9_!/uj@dlodd &-#͊ޔv\/@x0<46YU_P@̇{ csy,\8@_^MwmE\{D {ۣq/>ЇlnnL-=`0'q}jVĕ",j<" %pr霞'3敨;1wv\S02XB=x>N;l"XlZ2hH7X2.masyk#sE#((Β7aBg 8,vO!zŞ0vO|ayjԹ?m24Zf*=aP3cwdowk9ʎyldEebܘ $γvDRєw5Y\qHiƣl琏1dPǭk0F6݆a=@dt`.HT1Wx^}o]! N\>W!U՛Gph#bU#4nJ ps4on+ 5mͼm'e"ؼ&Ud2p8[ӼdFZA"9-r8\CV)W5îܘÕehTqTMSWՕ ߨpu`ˮOEa1ƒKi<"}UP ҵ+߂Φjж|pyÇ_Y?|-g` hkTp'7]J#*uQ=Ӿ&nԚ#xA@n l)D0M ` Bb21={R=k:f B>KMlĜv]g.}0=٩yXEel9ΖN3hpj<OLUz-l*}s0h1b2ed\:,6=H d 16YՏ(F=[ЅyB7y@XOYS\bX˵yK%) 4.*xv pNsxK/< ;1 |1K$E/[z * mSh/baRFv5)G3퐪K K}ln\!? o´ءH++PYB<Aa d'u]8bʯ8N +1NU=U=Ć~m; )<4YWJ.(崷 {N>f/+r%|y-Ko݆ݝ5q#Y7;(iM8܋K[Fz7I;+R5.ES|%g";}JC* ,x35"g4?:ZudEy|GH1^-Ҭ"ٶ|݃mKz>ѷzNiHuB!X}E?g[_5[o~4ŀ??ig1GH7Qx4 ؄|qM nDd2JT.".pjo~_@{hhyq yf  FJVcfFi.ژB9y=׈E`lBE݌J3>.G>>!0ؙ+OV(2P(YZU0UPP_6RWY1!,3Kfޙ{y%\}0FSތ6ƴW&1yˤN ;M.qf \+ VYIP4 0+*6R@wTpvoϺ6]MLZAq2zV27Qc0i+#F ^@`I2"Dc1s SV+O1pv-ꔍ$cDq3SF~☲{BTWCȿzh68wPTE߸piB'_{sl>6M=f.aٶ-X e#5F Q魥W H"8½,%Wf՟l4iV?Y1m,ENX97H0"L4ڬޙqaaT(Vfo)s23`RJ ,96cJe* 8đя[pJD AbExU4(T&'jfO֝(;F~JċVQIP}j:%=zmjXj覢<0C[Bs<_~uK-kp~  ^;%S\ZPs2_v'}R7NZ8>2X8g'ܫdM߂͜"A 9UD!%S1 7أ>1T!<R/+>}? ZpN{I}P0(-vr;Iql9A: =D˷Ʋ- 3rQ UImjc?twCxa]3BFKLI$-y^Gȏo,캣k)|c8j @RP(|X"PH*2~!*Ok=ha);2F(0m֫0'`89mjh){\ԏ'>h_:_G_7S󸼞~^U?5H 97o>xRp lt<`Z6E8C D9#*8̺X8"fh.#o#ma¡FX"@eٴ-+A)·I*Y)Vg34153h@af2XXaFE`a֡X6n[,6:&42Tͦ3P@!*Aj2{/\ݰm@n9cUA oÏ=6.7x"aEj^UeVF<YS}4[4] Vɍ쾒?GZ 1xq8x.FKR?2DY%-i۷̑)2")T@S:Xnƌ4$vhn UCk"fTEo{)p??pyEQtٙ)H,mc|8(&Nūo?ϤpL EAv 5SFI RT iSSșPĩÊ&MѰ9[Uxᚂ5NuN!%Tn \yf\o}t};2t.{+(Bru`ឳ^}g+)`IaZo[lAAxŀֹ8Y+y<f;IrO77%;_Z E F-(x~vmtzkp*`I X5pfI0J^!XcMɌp?(3MAz Dfo C-^٧>7JQpyVq/Rd ){ MW:!yMA&SF(tU;C|mLW:|'ݷWސqId5m2_ qEm|=+3&ddjpE3`6^v؟.g?x \> }Α5ïހ|sEdAEHh.dziVNK*^qԡNOPolIh E\H*챙BY w6wu';^c1$ 1SQ!̂& ipbMUe6WTdLYyD8&絚+3o> $dSZiu))zn1#Ma։`r'U"y&̨oVf @ݘ]fȞ꫕kNB$s0EMђʃܗސ|x^SNvN8XNr<3")fO+ ǟkG6ளM sI|}> ͢Yi\~_Yo8tVL O4.fMt$wTv g9]ÃZZ+m -~+gV8 R3-'1c9^HEkAs@̩n.m9 (kpgl[ wH_NKKi9IFḦ́Z }5xf+OLa Ԩ<E{?ܻ`~ib?@ tlecSNkp.zJy[=a2+7F IԓQ:S~623d; U d6AŬ( y!8m]סƔpC!Ս|F)aun3\:~^ha|nEE$V[+Q+f.WH jN hRs~.Ff "$gH#%.lTb;0z҃Lm^{EO~\Yf&)tuUhG':K= $P3Z;+xoݶl^[¿{?umU> l=Lmf$k d98BW351ALIs6Orࣽto`1zKOU_>bv\9-*!eS71#.TPAbFO"W| 4cTd Vp9l%/`Owka j!,>@1]lds驗N˧p n4JP;22˸p,ڝۭϾ EX:I _ [f<|^u11`kMf琈 %)ՆZX'#$QB"_9jPcKSM 9[Y98]3sx{~Mę}-7gX׏Y[[7m!g۶MϔT=NɳnL\e_+5N?{޷ǫ!)D"Ii/Y6b=AMX,kked/CZ,ẁ Й2)ad"z[? M`,\!#}7{2y}f50`ڍi{; 4baH1yvoV @   *g~8`V|utʺpc=_f0j*xۆ:ƌF̱y+F1C&2 EbT\APwm9 TE>cA0萀X¸7`yͤ2D!By1Ɲ5*n=hR7 {Td8c|2,tSxDƳ\ԡݽ%u@~:<eg̃Qf̤L6ACvGZs)ؕ`c'9Vs * ULjDX8Arbq䧾[ƶ@,PYeißD~$ ]F $Aa=f8;eU3ʉd=bcjļ͠? ܈9[f}&}A69X90>k* ĔX\2X.0 FraNR @0䈢vHVl(dLy2SnPR SӛmsM6dQ&f9Scu$܆@ޕc?*=l:lTTTWv ;/hXL(Ϥ]ERyvR;)Y._o}RgLtН2 %10 y/(B6MRibu9k1kaD@R03s7+kjtr Y"ajw%-+/a:580^g֍o,VcqYBqL(e OrHVLy-v٢]vpUo߀>uD5X4v/ G%][ukp;q9Czz 6]s2/!KʁAyw)a拎kʋi3hyN 3u3YOVkXϧkvo{g ㍕u }c!Yj*vA!$k*څ`wkzٯ.MPXnbEyM.Ple#)DH$΁r? t񼩁"WCm^: Scphl^E{'8FĂJ?2p@uyt,wô\7~?M81L9̦3.HbR#,!3k^ؕb"A"嵤CHcg@|dfԊ ^aFΙ O΍t@=-@(6|XA<~nG\-aH2YCd~77D׾?5Xl3p6dzd/2fhECUg{~`N  &D&Ź۪X|n~nu;x4Co{o>?}qG |s{컛vj:&ZLY7IccClGO޾}Im@j;:+f D#LnY8yp\( W3ڤN=on +E,?V)S Vx(;R#A_R1ӉdrXzD%sWHN}Zٌ6e#mi^wU7-<($ؓ)rex= dJ"vCNFX d`H+^WCNz -6."0#TY8 `dBn 1ۀ._AbJU|Sg= z㒡(b<63 ` P{-Ic?yH#ݳfW= 8qeaײL,J`iz~@؜کT9hMd5=F*/ 0G6ԴhFl;6pL -\M(QF8RB]+LȺFPdv`md*H9۫YuM*%ڣ,IM0Mv:>ܾkvlo5M+ E@EI@p,7yquA9H@7?TQHRjAN(8 FHN /32Xj,9C*&9=g\;lo#O +S#^f#5 J7|YE{8Ud|M;;O->|v#];(BPi?;ϱP=M OuI#rakXp (_oo`tzd3U6qC5]Nk>?kN;͖c 0EqBb5Y㊕fyͼ(KzRh)b+r ى0g`'i*aGֻI v_{~kuYr8p)B24uw r;>QkAᣌLȴ,R³ TyU 'q|cⷮC4i؁ =[O <4za4M-Z'tE\6^,c[ s;gT_/]wkC9YI@; xHk+Tܠ d/}~_!tsoQJK^΍?e1*ޒbg0#hI< nNxV^2W\`zx|0kwM߸9)^Ñt 0$(qwe~35 _~^rYʽM_%gQ "}zLg!e/{8 żU?euN<'՞&f[6^x zf|!u0Hb% ѴWEϼۚū=a.n50ݾ`~~qG|Z]O}=gɮ;Q?β?wya6=n=oc%Sc93,!' )T9'MyZg(lJ3T@ܓ s|=O!Aq" (ޮ/iCcZjMdF&O"g_3{ؽIEՆ-աi dFX bh 7^Wmýpfaf ,2TǬ2PT`c[ugLtoFͅ8;.+* &C`88! PmdYq*$)2{V`MA>6!X#? [cq;?58 ְֵn='IC);3LB#(o$JC-g&t@A_ڵn=!N,hȈ:_dVlӴ͕u8ZGpHGI{\"+Ā)p6 d${A[xek|Ukw|јڧrG? {>ۺlҔa6Rs4HIcbPQI{xG=WO{46~=<ŎoVvC^dݘrP9XJcuNsk"`.>VNI*ܔ`sG.=DK'0>|tiIɓClEdHrڔ~& t9]t vKǷ>g%9pv*ЬƂ=d)h+{&Fw^l੗vP4䶂 Hi\?uf >ǚ!4˕akD]49A6j eѡQ]hh/:}W'(+>l۷|s?nK=Żhޓb|8!_jgu7*XRxc Hd$g@^ s6 BYUԀFkT^KPG=Պ"h^a+(q{۶Yk]l<\?kƏ?ֳzexso߄?-7nß|{ O>wz 7ylxk5yE*%j&g/+_X>CGt˅0К-KsL涔uz[=̏f等+2́dg*8py)ю l*qX#HOrB8$\늬%FPk!1 )DfOй;*L¼ Y 9\;R;^u9Oh۶["se%HbFĺĦ,wb&Qf <'Վ*5Q1YKᴻoһGv|pjSs߄o?y ~ja0q}%,oYgfmGD jHxTP~O5rnR+][GVp0*Ax=> p\|ԎqbTH_9LD[DIf) IOq[\p{ug_^\7n5z=j C^ sZ@)UkĕT&li:cx\OA&PԠp_I;a&Mҝ E. Sx K9"T2_߁4|}?7}֭[M.8~wg-;OoxҋD7M=zқϼTi9Ɣ,׍J1enN&.s;,Bq (wAzKŋt,[ڠ ͊md0~pxnzGg&3/\':E@q3l# iBd*$e/YH5xjT1} Op ):W:!B#%[.vҎ}=rT`%"w2Pܢb`sҰi3 #@ E\T(ĝcŔ-5U:vXk`7yVϾD%= ŜH]\4ay4S<)L[=N;NMh{-$/ &yeG 5J(c{${0"Ӧf&x 1:) ! ?d!|6虊hLEm$&U 1؀Jx}ǘ{Ɩʞ0s1d 4AMsC;}78í1W!FShVFnHM*= .묛j+'3ڏ.C>uX*z h2+q#,;a$q c1ח[6(RjEݐXpeI%oH*1H 2˱/mr  [.X"]\, ")(U:)7iSP@>Q-Cled=ɀ Y `Ek BcT'ϳbJ^2+EV wEJ{2ЭWı8ht0VMaxv \ ]=?x'޵j#WFYj8:mۉz~|:Ϛ!_rbRSl n9&CS,#u'@5YՊ,#C7TPg(r*&0R㶢F/vq̣9=Zk7kx^y+p~ݎt% o8_lMyאY.NaI=Y4ϺڭF wن2W9G4ŽwGhl9FpLb&eE`\TStŶx GoS, h5Fk~`ҤYB59kVe5n{k^b>RUd:#9Sci_o 쉡ϙx_ /Iڞ_e ȫw׼*M4˯yJ읺<#,)>^hko8֔ ķ]/ڼRQl/c)<<)pDQn ҮV([R{64g{b 儊9PF_Zӗu;?ux#0 l_ύ506lb3;ܽAQZ 6QU[dm ٘dҺ@ʟԮ`ڜ{޺ t*0S{}HuÌψUw׏+{a{ca?S+Q֙1Rz0ژFhUgx#5,.lW/Ɉ<4ϔ*h@*sKUZZIeAJ"k0(Dkyr|I =luAsk/K Sݞg.._뾇;pƝ@?~~+WLgm0D(p=Ovuٳv2S}h^OJe1/)+Sv]f#HgQȪ6:_D5fXݘ%b͇1\#Xґ ' ҍc\w`ioj8]2 X6Bb?:r캾`flg }<\6ʐrJK3WDB$XvWe2Xqǧ.ryyiS/HSqTuXx%o@NTfd9גX5R3Eh5+td3)H Fn)xBM:~AI-\Փpx:ǟ=zy^yi /‘]gL5 -vi^7n3 ^xr _~%;6Å@ "|"=rk~-Z.fKmc?{Eq V!rȘʱ1f )~bl5t]yc+WA]7Cy\;ѵxE|EQy]\\*1'M+{`M3`cȃ>Ue}/d>͈r06N y!!qH= QdG6NUԶgx}Zu=p <7/na Oae:T>FW)pdޕta%F1M.؆۞ymT/ KO 5ܢUX)`QkB& &1X.oW* 0 f>k`b "a$Ιt+wa>Zu嶽ǟl/2㚮5ҫ3x'._xƗ* E$Eh]Â-9qv3^Ew#n㬞{6qjBpnSR8 7C={Rz]ׇGwL ۜݱ]ܚ-(Ð}=v6>R`t.6wSب:EFHA4ݮ:.Z~XЍ2"0=J>ZMuesGIf9= e\0p(5?oc/ʽ3g)̽3"(EsPn`VNEԴfČ sMhfѥr#6Tq07݊ a^_ sGXkD{Ro[. 3{߉}zk5l$`O~Mb˙F4yR"5^Ltfms[ .;:d&y/F6\jqWA v-D qܿmEٚ&!e7I"biFm{G7 a$W,QllkcˍIܷSUNچ$J{pV8"7a3KMˬq.$@5&V~**t8M->?M'6 /Z@6w5B;Vy^:BcIM9YNYew'U%ۿzM>E$H1{1WXl 1ŎG(J5Q֌A=YY3T_hSPPlTjTdإ@ٜ@5Um Y[F(xcy^PsN<聚ű]358Uypq,H y̓n`h&lQ 7ez[U::X9ٵ]XN$){3^U;w&pnvYqz|u\{bhsSx/s?UXN@1A40TbJRP)rlB8=Ʀ8[wR}MS -1YgesC4E|őPTsh^IC]0< FCvN>^qmO*Q\g2~ x>1vk [3Xw=oN+WЎ go;W[?5C'lj^;җ =t`jcuBɈ׊.3VC[MPXŁ(sh-h:IǜR#7Sըb[uh`;"cuARӍܺFE|>wkύY iP< %gؿaCp-7Qs8^䄘2 ?+LrmXpM\S1P3U i;?cLeswP{asٓsW})MIn,s E]u\1[S%NV[PlYHddcMX#H&J[kp7 x La |t~㺷s\ʑA oiޒ.{̂N qꥳ:NaVE*EjbmFִ砫6rݲs L`;nTq@2J%BIy^yuIbl3흋lLNጣO()G˩8=Y-L0gc-עN/B&ZѺ,夝"u:f3DN G'} ?u8?\~Č4(6fP?h$yԲ<. \8hCÃj4ofGjBފkҴlLCqL-)S(&gu(X+dߑ^1zF.uGrڬ/׶I VI+O.4rTS3N(͐nQ40bq%" F2ا:" HeOTט2ANBE:6lG#،=(S f:*S$5 X|S%9IJ3Y!-`{>&hQH7A XɅ/Ϊuf'XaHkf={ȃ+]al}."/0D6ܐ5;b&{K)4:m1kr- {oxwk kn.-FOgۂW^>I]yJ8VmpAXUvҸ~嚁$ %#r C^O LyVE+>#5-L& D 2Ţ c)oCA(dٸpF LBi|‡u;[}0Ԩ\7h cC-*^HxOÀË{\)y@zP KK^J:}h 4+F.9գ ~{̃ A-Vw\wnǍa#z]2 Ijz?l%h"-/mO*<@ PNz=]P;r~[}b8غjWpKpnX:8`NJ3'*YM%IYP B'^%KK=WfYuFXow 8>90ݯ'ztmۮi%YP&7:Xm&+˯x&g 9U< {|kIT qF %;) #ϫ)>Q`@}#d T;&<,au}랏ȋOQE@Ǝ-<Ҳe/Aba@\HE H;2Z2K_/#h>O]/uCPg.3[`"(W|A^@ X4=K=[mbHG| ۾qGX"lL= OVVGA'lX4{)=*+Rl=zikV'*x抱37ӨMW *1$q;@cT d$f (F.gNط5w70Iؑʌn>; Bub2bEZoň Ȝ;I֧V-,527gsSSh=Q 0Z#G9ȋ:~n΂ryPL+&p'J9Rfr@z%8e-@Qy] 'Q'[&Ob;;5\հ=%o,9^C+~|6w\0k^`uuMi}5!u͜-掬 3* )kETY֯Z=TU" CJGg5М32^BԸԪdI{`H8RWj*l\2ϡW8u ص ~_ބ翽ׂ^fܣb^0Vszgce Z}`%4["mw-OyyaGc"cߛr4}g=s* Q _ #q2LY 5CR70%ẒdaDu rfF`S#Ҭi )ĐW'^5ihz-Z_;ѵ *!*!1#{yZ6ѫ=aD; ;Ls 5^ɽ'&Ud80#4MZ7܇ 9W<ept y bf3s礪jwčm<09EykPR⸸BNJMXMZZta =0>UAulJ0 `yg%%>T>\~>pLZ(zHS|q]#=~o>Cxk0~TɒM;NJc4(B\IQuF c$z@\kh:4dIMF2v,+VL`ԝjvn oP`e 2+ 1eP8)btP1 Tƃhլ4Ec9`+2]\qk~2VM@ $b4TaU`Iaq]kQx"Ƒݵ5R09w$!Y9*J۝ 4VnyށGΤn*x6YwsHi70򖐒hӱPqlE_,-129X(sT̘ 4Vj7OD{Ş&;xC#Xк#O~.q_Uf_G?o1|bȗMG gfN.SO99=Ճ_UeLq)r8Fw>~~b{x>"@Y.TUQxOuJAtRů|6>#CХuǞ8`)e#+O(XJDJ4Fs'-_3hٝOQ>ΏO'Q4^EVʳ؜B!ݳ3T.^'o(s%wL1|)Pvc:tANϙB饚DuebeS9y.S T\qqϱB6YcX_ lo.8!O A1GhHp5L~>\AB[v bǴ9;@*l$=ucΎLŜ!k vgvpx7|5P])%慒,1I\/m*Y9e ؍7:(8X2/$dUM>(c tCX6oXؽ!K)!9^Cװ8]x ݩ၁;'g憫$PmwCƊXፈo8ti2Kk41 J2\"ץkruLyʭ$Uogxn:>Ꜫ%t0KlS _ىHǺ:2~Qc Fl7gwJ XUǡ20C('m4Ꜭ<<^I>=Mt{%lXS ϕ=X@,v$`T9Ju1OPÕNv[GRg=Θmrs_ۂ/h?Mx GulObC ?8}͙kPu`u7lJת`ʜ@TolT4OuFh+Lo^XAznb}"Zq mp9cE}cڸs,l]z@XN5GCM5]#`> z^}^1T7kMoRnٯMs 4'-\kσj[{M&QiAW/=t[+4ʤۧ ~wn=1(R g%۠o>=S%j۵ŝM{N{NITCC-sXhSs)J>kod6^!*ɨwu6qDLl D%L*Btl$>8XLﵐGl" FA-,z,**MqyV8[;6YI*?;9BLqGqQrXץk\;l[g>XzMD0:7M@f/lDMD 7DI",?(a}Ku*z [9P .[JQbuf_sL|IHrs̕*5OJ׮7PXڹj W'`uBzL$ jD@T6 >{AQkG&Z9`Oޑ juqi&Dt`RP` cdhg+T7NO7ᱫ`S:Rr?ȭƩM N=igCv2`9qqy* Zckү=!x[/cl0*e\k#ъ JDskcRJp,9rq{.W`{~[Il˲UN ’^9R(R:Tpqc||~ߺ=dϬk턺E|8^a{Җ\̈2Drf jWxJ̽ #g&7t'POŽJb _܍׍ Wt¸={˫]׶^*N"ܼK"c`t]3;";%faɪBG<8ͻyhʔ-{0p&<䭛 CCy 8.٬O/o_ 3O٪d05"#ޛ3Y5$BF_؂G%'qP6J![bA$r-)݈QaTJ'o irTTn*h*Jzӛ{Hyn-MQh&Tz[[b7= nQX\{nؽə%TjHt_, Rn!.n)x<\ބ{/ `}́.nYY;^~s<<۶>vg {~swg/. a7 |q0qa-VÃ-Wډj$3 &AB4 nH.HdFiQzJ :57`w|INhY < gR`w> 7AK $48\Ǝ|=5+乨|q8GG7z} z!ULAb ح:@J%ޞ.;݇ -dΧh:TK UnYGZdQfڲ1toxIyuVYޞ|OR!/Ř,dvQjt4$=%3 r9~}4ڃוia_1M|;f_ٸ>rF֝ZLOΏGeOxʢT{/΋t [?Lخffd玥Z iMHE{!P( `7,![T vHQdTEetG{,a-s"69crq6EA&bm wP#^z,饒c-;r|N-F r'*L0^[+-sL7o"x2; iJH%㪫_ĦlƪjGM2H `:Ce,޴ hS\%!ǃhKl_8T~:V>A}쭿W vHO&Xs1ې=/*ͫy/`9ng4ds!f ~:SB~稥JʉRb󎉑1XuFiϾ5##;->D8 vG#kv3`156W["ryЅX]@Y果Ҧ+ok(Uq 0- ;:iEAG %%gW^ēAАÂ;$}N9ĉ[e@PiXCr'!I- Av#-YPڿf C&Yk^V u^5H3t^LYk%q$d$j$\ قମTb ێ q2r U'=܀ͭ |t7hKy$%:f??;` ]lVNPs/,8)ߩ&*c98gJLX-%#/iUʉju kY M1Y ?(hJ "_Ӈ3WV 6 ;Z3{k}'߷]dž3[U.k( 08ۙu֝ϡPsy*S̈E-DR-4\E%pFǙVAP:U H+?>A5#&o,BB І %:LmиK{>=XZh1.ۢEk*OM3tp7l~-;p@bz{@@5h/WbbvS>OMEȚ;*W:'mc9(sZ \U\8 W `E8YWHRB1\Bb {j#>+Xa-7ss*G0Z,;v{H}m IQҙ r,A1Q"@2WkJ(<Γ lCnWt8aMiؚw&kۛ#x{ gdŻݯI'z>O~WW :؟o*)z~v ꭝ}WWWO,/?S?7=|1ML_&`FFu ފZm5unolbԐppqQVko=l̢?d {zZfiF ;a~Z9􁪲j[B1r;];3LO/DP*:dpX*Q/xv߃;6V;G-XY!Ȭq5FaK0Z]'%>o4 Ez,M[86}j %UVy>Tb \fSz"+F~܆65lns[ߙ1&~XX$&!tT+ *UTS] [5.w9`>r!^U^)E:edzDUu/.#xaeg;DQT;GS\>Bj]ձkw!mJMɏ=zsch9LA,Bu#֜hVw@IV *Y=03xx1ZZ<8QD-,i<5~n5j7(4~mzgUd]HHS'ؽ1WE0FVjCc J+AZz|SO||䛰 H&мia@gFY<'gc;q cg v@Nl얂mmڶ@9w,JDŽi` W1CDf›,K&̣ 'GKpBprh* n&rԸjv1\]ogë{okctF 7UT2mؒ Zo)OJfp[ ѰrאD.Cc^sD#?δS6’Y~ձU 7lr4'n%X)c -k,z5sbČ`mBR}9/hYB`D.yX16\߆^*ZG^Bx%st}Eaj*9坿w b$걁i>0͡vqύb>ÐJCA3 J$i# 4eDYF*UTF㣙k9Gae&qf씒/][:o:*nLT9e]ֲ[L0xk mלi$<۰Mae-χ-˅*){kǕ47Oǧ^ooںE kD|-ipO|l2`[CU`^k6>35u|\^w-ކFOg3+䈢"ˁPI‚iN+ F2cDWSl#/=zb 4ܟ^IXtf{µNgGw!_ߓ@OPೳ! m{c2zx#i4W*S-wƏJd`Gi+sj)iܻ~O# +r!\R{bZ2ʜ>6} zDbii^&,GмҐ%q8v /TUnVF2TS\ՐlY밋on3q1wT7[۪ ;lhהlLb.SvB3M%9U*BZ{LE:ҊJdakdM]c,-tRJ}w}R1Q+KBS~`3> F5TbX^@z,Vz'J_Dt1xxiJ7iѷ_'LS6lRJՅe27~o L!3 y :GH0H*W N\O-`|9<\Ѱצ_Uq|k̢6FX<*ɂ S;5=( 26w+a԰)/hd7%ዠcxWXa74 "f;>mJSȘ--| kCAOlA6)k$=T~99*B077ȮҚ:v%#kBNf5+!b&K v VbځgCT:zȰ ԰45o7WnoKʩ |b x-SxM֩Iy& 5sgGZ-wk 5C?uǸv*?\ܝ~~37j'0;Z7^dLM:G0#߆ omrj$Yn"(ڴ"- iCLqcHPE =j"s*R+ٖ![&f wŖQr Bi[{2fvU:pۄ'6nMqRB%2j:sa I&)b%Uk]:&4)8Ӟ0_ނo-;ƇvM5~swxWKtk ^!,f7iۖ-0j l"s%Tʃ 5Io h 3#wTtB" *3EFm [D*,P2Y:>XTR>h ߙ[{C.#kCM@maa2=0z:' 'gZ_EH1>AEc:Ϩ3ϩh E'4 *k୓G'o:K-ڸwc!)ݜ'l v[D ԖUo!eKV^ԉD*{09&d*~fͤ,Oko獊$_7% E &wCl᭭swcnh-YA%r^D,sC%0Yv᱇ෞ>Uu{K! O#>^]N긥GL=NJ60++ VZo wH [|.r*1檆 )z/܅0~}=A~yF锁#4oc!=c?fq塟]xGn؋UJtt:77ڳ615`ZT3@zCb"+_>X3+ Zsx&Vkͯ>z}]ܲSYPSgccX],)6pk8dn7N <2j!ZQl*)H\ږ]'lF%T)폾QqCD=*,`]\ǘi5:1sR<){9Qثǵn&\BBVB^۾-p̍)暽S{ Cq\3PZz}砙,ְU%򞈹c( @ 2.$l@S(ۘ+ um]蹎ZdMu-v檞r:w%Z'sG^%հWfZP0Jof03R]*9Ub9Qy V]3n`\Zŝ<7Gy &kc @Q*u<5{ZcےۑzЭC!O-1'ט\3bgn}%gZ)u!ێ/ a;3J;C4 vm~o!VD\e\hhOM3{ ?s`_~oylTK$'l@g reNΈa 3d\= TeU)XAΌT,;V~bx=b`棛+9u'j"G%07ZjxCSw­3 _܇7pNI-pHAq꫁}'o߆\KZZ5]{:EVvIVgFkp xϷ:.[>m׮>[Ϋ=tjF:5:l? ۛc{5f`>UPǝۅ?Ql˲cC療CB yRѭ}yMatQl4y_3ZW x\P>>!DDAU}Sl1߆IHP*Յw}z㥫L o~ Gv:u o"fbnmܖTTP܃84JBVX:ЭhDuuhU))Y|&bf^^]P+fTPxJB̑e. *FO4w9^_Mכ/…>N 5S/*}\Y4O$22\s1 v3RZ+/\n3b?`˜۽ߋTNB/ ~|s ehtpVý[vE®ʴHsmt9/2k>k@usR0T^IVlx,m+|b ly2ΚV%%-TN~Key)4ZD5ۅiV8ubxjtNX|;]0tHV QKZFm~%(XAj}`y5+Rsb U7n֦S0^\ 6+"qb0N3!Soeg?8,i 0f9Ak$'%o1trl1 M칹bUd$|L%z`8$l#.q{_;;;^7ӄyC} e']sVNyj\ `U~ag1,][۶%5@Oޜr PdaѸF >ȱA!8J $ 7f/~|WaGUYNPtNGY(%̂L#Ѵ⢬*,9h3Zd-TXgsLu}*kf \'G(Ve#,%E`%h5/)8(;*RʔMJn `:a´rjJLeT!~,R> d|&>Ev*<+D\E*snЧc Nf3c7*e$B*ZHA5}}agu>[>4Ko&x(])zMu:K/S`&scTqzl ][(.lߴEn#6 ̀2m( j3c6lPh^,(,4Ui.ԢVtrlc4E;yB)1[E0svbzEsrDt~^2w*% dr( U@JLu}֢QS^8(@9@ d<9)A(12OѤϔ۬J_jW(|Ckob[{T v\۠,VS. %''Q.H&+(=B]r_< 2%v-Ic2?O9bx?9ٵd^l]·jvϙ"Pl",&>!Ys&%%1n r%b2A)Nz p'&j̓38slrP(_>+/s:vM.q kRNN[ m06uNXQH[31"$I+4.A%rOR16I#CZP ἅ0Čf5ױ5j^ y rB2*B24 G85ٝx61χPj ]^NF1vec~*1 +AYeۈ% iu}vŝ\޸ oM`u^?}{l'kw=< 17c#D@r58r3vn_1KA5+a!55!<501c mG+R8vfkI;<̛¼]Š-z #3Hq5tT#iZ V_YI4cR-T6Ⱥ-/Cށm@nL U/< $Ae -q!U68ԚȖXʙ<+ <;7|֋}᱁ RxfXR7l*w5jnalj?J+v^1bx^hJfIN**4ƕVDY=b5bPk@$XП_/4|Ʒ }q M:J ΜꊘKx> .6WNaT-a0bqjTt3pvI_z{ plQdH_t s;O ?{عPï| \H =H o2=uMfS{sbȇyo=xUL-7}RX7BicyՎ-p n'@7J)F6m̞ VLd%Դq1Ulc6wiv;PJoݒoĥ@cY|ꅆlnܴwv*-&BVd/Yɇp^שGݚk\V-flsoW'}Tv k<c,r.k1Z幑d 6`'CԴJ9J+ϣS||, OA9Ƞ5$(2._HhKRR:2RPc _}i`\vЙ=m#|/֊W7hWYT֩-72Z$h%yTP\ dm"wLEՈ,:B*v 09v9koT0s;fEطHUbCSWHm"SȟRE Hd0ev)rM Wͩ [n*e Fzdž5 `[Xt}1k1S T*#|8?ծ*E|!CS19K'1Ӕ&)쒝i` 4J2 6lAfg{I` '~0i 3h؅/\!i|[ qwN?>xzT(zHQYBN^7\ZUV"`Gl$*lsA)]UQ J)7{|'p1ϦN m?d_Wik='-萲גvfXJJIXVPN*cc| Tqƪ*UjQ#`SV\KsLxY!QMwpU[Lh7c$լ!HrpD;5St u~G/赲"ΘyݣڂQgA;#U0,p =BӭE6H_,v=V{m"\85,!Q Qa P %`+(sLu 2>_)h\.D֍¦ΤULrک-O+frk"D*fy; *4FеA~<6X+XrRA;4^cSq,0ފkK saBq0 +)d9DLE_yCiaVy&Й?z5|7˩f)C` v'^pw,wnXSSrSz=]ցIM6EfA{Bm![OLlͩ<)ko@J\ ߘ4>6ysQ*}|j}7/8kP4k]14A5|=ȣ[puS;n@x04Ά5=q%*icWnnB5Iwő=O-9ƣ5.hx.)c& ށ«aҊ$hqz4k2pRf*7 j0Un<,5t+Dzv ϞyF8́Hu"Im$VQ0U Ԣ؃EJ%u eXk?_Rײ\|vtTuGoLye:܈k%FR60)|t 'Zx)S簳j?Rk xσ5lYܬIVVjPF9e;dfuJv;]P|GdRulssRvb 6l0dP'tE+7_9J NE vj{Alj-6 DZiD-VsZ/-)@L V4wv!?P,)VqԐ4qkרW.ثrxeAQno'.O;lW2EY:O?zۘtiMkڃ(Js[slf&l,~}-{L'PUe6iΪS(U[kh|%F/٥[s 0M$Gɨ,ݖD'<qDžK@"1a0gX\xdžQ_sL6F"}>#_fa祊[39\'J_+ æ*Rm=@r(̔heJrC*Z 6 B +;AOt7}O-=es1uG،`n' **}ADEq#dL] F^I e=H(_J&ktNhniWJT*bKhmņ`O'#T)Gcv| "/!Od8Ha`ݏ,3U>n,!)i_~^oJ.4;SP5ePH5lcVaN:- YpBcۃCq@WN+5&L %>0&ldgǞϼoNTʞ==y=v6&+T &$d{7.¯ޒpb0 BL;K>_w81Red;NWe5 p0da\p^aRQ;ƍw ^c+i GءAdηJ6~4vjĺZמUU:;HZC;Wo/LJ/`eI Ͼt3dɼv %,&Ժޮ_C>-ܣʃuÁ3!YMB6nʗL)qVhtH"Tw*wzgfld/Ɲ#C8ק4Nݴ2~L3ܫ4)wZu *whɿXjWT\i-AJ'@ΔoNxakQR'Alݔ|̑zmm-VONB&Ogp !LWYbB8Ų&-n ԕ+K&3Gy,ٹ.gbT- lyPVغp^a:d,ւoU-%a5 9'0eMcMJe&u*%4mTh?_\-<.RTNR.=yk KI1 > O9,"mșSyRג4D8Sl ~fC#T>b?p1k!SPdS.gl,%]' c Rt|t¸Up7nܨh[Ȟہ_ۛd'v@}3!b~δ'i#D(4ޮ!BA2+m1@ @Zl"0vdԍ\ӣQ7 %\E;zcbE͹Q|? ''0?ε<1oS ,V_뾇ʳ)T?.GlY$:}?ɯ굔GY. >^ڂTߵ&yC!` '$شOKln=!'+#3C0Q*~2(|28j\2: 2 F(+zi/bbJCGh ϊ}SZNP羽j%,@R,{5;k]|G{k9HmA Fk(Qya=މ/ۍ̥!Lv*>^P};}^$SG`qm`a9bwg~&CxNs5|g_f%+d!Aň~A/W@]f*7DJ.hDusIMru`֪T1Nr&[gDAlˏrJ^_QY*!|Lnh_df w% %+szTac5>Ҭ Acu}5عGU}a 5|8<b˰h%jw7[{Q0n䡯j*wQsnj4zퟔ'SWpM1C&v)K$xq v.Dp <C`}!U^[-2;e\ŲPgjPú-Q AJbsS1 S5*X9qShePw4A]z^>C74}#3c P uG .^aOهs= ^RPt n ? |xl,F-Tl&"l|c[siаH$:W-׽w<}7s:.ج[6M$gjW.wUUH$ei)oYAU,"=2I'ǽ=}>#H&NrSZAT:6FK##l&a7Lף16FMƍGb֭YBzC0Ȩ%X,J)Y0edGWLuDg$B]^Ҿ^>vܚDT|@S\Jqc` "St]jVz=U>Avc'5n/ glG {YO<5I0X֤ ml#Ku lhKkʵko# x]̭1K%n\%BWX&{&,7o2VkZzA J$!/=K|5Lut%SlQϒ'CLYL6b 9}N" }q5l5nDBu>ǧe%s|>'~U]J؂jAL*f$rf)ꡫ Й=ۀ{j|](wG|/z>я?lfh7g#6Qgxܚëy{o Wa]E  ohwўCa§~`M!} }A^ |&"kgGo`f{! waM"v]+OwN>pKѯ8n]*RY~^'ZW M"X.1R֜}Ƃ}E G|]#3"#c֜LІ=G HVWp\ ʭh"\Ha6U: pӤ_U+~슔3J@ ti8!B^2=L[q,E51W& |֗[/v:FX,Q(61 z߃u𾋒Bf&$UTgo L94w>0&<@e}?:L)`7]Cy'#$gMYC/  5pí^uhqOݰ:ȝ!gLk~[UqrE>"+`59:( #8Ӯ2`?uڶFTPbmKf&o :haeSˊvb[(>ȇ5OsnQ /< dax[+0 le@ߊ"i,uN߭ b{O+C 57l[C wfGO2|xڮm3p=L!B&[;\^ $OiYiI`pKthB:61n{ YU{a25P+^,Z>_Y NͦsTUagAYV鼅e#aω%dml U K ">'[)ݵl=!ҞF!w&6t^ߗ2u.dN4 .Os &+` _,Y)/5 8{&;*xcָcڿloͽJ4T:D{<PW 7s9}S08]|&rT^?'NQIU8-;}ZdJk6dns\ZSvꐀhg Y,BֳYjj}oq}Ɣ9FjVÝCc+}ԞecZWЁ5d݆@>*t#{ @ FpƵ)b $bNA̬\^=!O<~.o/ᔬ%Z[ ҁ߇c )yZnr0$AT*d`,%֘~Fk{v<AӦ_I\ a`[ j,@0OD;0L!mA϶~nc@LmTmtTWrpH6jRX"@y, =h+6%0V,gRqu-v6pqKGvdW``"q7ݦ2bl1;A*@ z@s}XSyU>nx.l2wMS,%X""54$}sl1D, Ҙ.@dDt};]%Z@f5ۄ˓ jxuMLz˖n~3."\٭agk1SȉFVd >[Y1ku8C!qtڹJNo-Yq\W&>$kP$'4 OJRL\˂_rvb"er1d<(j1*XD1xF34RE2L̽I|}6 _OCm;dHvKt/(rpVd:_MT}[tvދ hU,}u5%P)?_J>SZ@iG/`vpG 7G::v&Wg,mPeL:ڍJ jşm-BN8GyU=O~BU *I P?3)Qr,a9G;{0ƽ GT=O?/^u[KD=nL06n;i.V\:4[`gJbިYimrkH`m[+=Tj@vU->(dJ%Ev3s v*^MO@(&M@Q9mؤz٭'+7,[XJ[o5!<' 2'Ю^(SkcG(Vn8#{nw`ɶ'إSf3&}P`gvyQB\28ٹ> C4 !G)G+fzot..;=RtaV,? ,)`ocY,|_J[6oG5ͥ`,α*^qB]=E+d7i"3a/c͋s|2S}nn-Y#ύaV5iᷞم/?}pQP8 BT΁0z#˾ŖxL@`RIh L k[M5aMeI Nci}qv^mw݄3wSbܜ<rI| qO Fhu(-j$c4:VӒ1ИHyp6]3)IQ.(l2Xq/m?Ge@2:0*b N9b+h##gJy%d Q,gu6Q|]uA=RV5ME3zod6cǦ朻< Eo#6$LuMtB"TC$تaȟqs]la66txA /(ףn ~6xQcP! S*>5Am?5aTP( jPTÐuw^9o=h`ػڃ+\cyusԼw2qVjS1eJ޿JbY ^xdY-*$( lv&L@iکWFx]6}iOcl&mABD룢.LTtA-xWk9|2NƐ`ѕs~ͪ85M$m$+DqhE @s޿8?CYmMT JwѐTز8Dw3E -x.9\-m V` Ni]:Kv<(T=*5eU@TMsEECwtN| \;pFOvNP*ߤJgaڛ_ڿ߻ezDճ_ MMh~?qx= 4h+o67.OP-)~ f^ J$qVs`PCԱ&m~f8)l.֓-&-l,:~Ey+wa<ypuM ?K,K-kODB@FaŸPٹZ2 U %kfQ5Euf7 CӌnSՕm`kp'DWqisuϾ|Wܟ;6y%z~ ;]Z5ݦ&`A^Y51[wh% ~VE >bTq.`ބ; /s8W-׍\y)殄,ix Ia@Ad[ Mk2azLwsr&<3x3wmB@Ul>bUUm Z/ 7]FKtE.eZQx'F!+m)zau~)Gv4ĘPo:]wB#!3~B%!M}Ŋdi\VxGV֌Kl'V'E7 -~`u4"ߢz]+w-+G} LJKձ#1R,ZѣB%m+eR2^W49j7.iatRkCbRZ$o"N-=RO5oP۰8;=1as@B[ȌiCybak-(}2(DHA1WIJk>!8PG0'w5p"Hy#L*clt>PS04 %F{. ^*Ql_NjT7ªȋt6p|HD8ƌY ])@] V=y~TjS0U+c's1bX.gTתM<*J T9\쉪Byۗ0ee8@AV׀'1b&+n&$ =ACw`y/%75vtބ-/sTXu`ϵ uƑ1ytsE!VqT}QGى/*E Cl[8Wpa{/!+1࢕nvp{$Xbk60;<ܩ/֚z&$ћ]SXa aḟKTa7fy`W x` zo>c{;aǔ/c")qM,pk$bpmkQwp^sZUü˟253D} Y 9,2 61AVL"$g&.J21 X)K[6x&(vY/ot# !Ꜭ|Biv1<${[oݶ``eP8vD<5ilTS`4A2ݻ*UgJiZĭW5طʕSxͬzZuDm5-dG/}qgy{~w||kM0jpk818ppN/k2mt/ %E!ʒ^0GlH AA_]"A-HE #\VG2Vvo~q8g pҟCx+h}xj b-M55vXDuӨ5AMc M2R2,pMy !{1kV( 訇EX1^8 26'*}o/B.5QךR*Ћu1~iE,ANAW0`\;gK%xC۰3ma#*xy{7 n|m.F6:5Ys 秢h*s~&AT1X"+& >>wncŸ?iaҭn/97bVF,sQ Rtl؂0d~'|} !I,l,|u[>;T{0{"H׶IR {a;Tf޿Lb1]FN@pR9cQ@9[`c x?VN ^reG3(,&d`{s ?'x#N, x\|ܭ9$'>)4@cyhqmfdu [N"Y۬yC'MpDjג,_cvLP}@h Rs+o|M /g=+ap7,d5uW\UͤT ){W8\޺\#ܹv 1 J1Hװ=$ QRVlIru`Us<`>mr3raV܌,ٰګ 3W:jrrGOA=Ū|{͛R@2[3:h!;JLIJplbYsDv3ɟ?2qPLclָc { `Fr{7pጁgZdsn_C]]1qG>eew&wS/825e!]jUi!3)?ؠ*E@[<ДU]jXl.ɘ]f1 4|0YAT mևX?FZ`A I #fDh&_GUDc+\_P!eʧ{6e\bAA"u0`: 9N8d *V>M LIgMN^~dBFLPymX_MBOZɹ9YzYLPT_6IH;=b>6 |xnX߁7 Nl c&oE>f?7pag^", =[k2]%YT;m&{ZdkfFTq=%[@!|xBYM #\_u/ז,rJf+M  g.d dmTE3dLf9LĂ*1.: x#v{1B @ڲE%.5 &&!x 7$asG nZoZx-| RGldac@MU>la^L"ɥj"OF,F yR^/"dICI8T`($F à"E4EoIH(RIԃlIY&[91gm<^Hޏ&V]t[{UOQ#fA[bS>!&F<~!z 䡢 巗K?ǝL;WR*AF>*-H$ {gݬVWp͋/IQ)y9U7ԶD+y0*2E<~:A9J}Pު IAl7^x&ۛWf,e16Ƣ8狸/دO>=H#G}{뭷Kl:r޸\.(l?~[@P0$`a{6\+`V4ʏ@##%1 F`w)HMy8:bWExT)!ցl~л[fŃk{{q&֦A! 0`oXl+AlD"K`H{>cN\J<)Kvo` 4FG@X+)Ɣ:XeՏ׳"JZy7xdf`s`}oon!mx?1̼)]:Iڎ_m΢8Y ߁v L]l\^v9.:8YT""<|bfgF}-6l<4\}òC&Z(x"-ۄl2i3g_*8ҘHq퉚~mR) `AE}RlnLDd?b$##σV֨%hp CzVJ^wsdqA&f" dJ)oλ+@(*0;9eEQYVMwm6#λ-փvpZ=j:M!$?9η:#EP$gV,/ J6!,bľ-$'<6(vOI*" ?Z?aV2jو ;*Q4MfYuM7}#xu7Lf o4_ځOjύV:ΤdHe~zj2i#XS1)M2Xf0!QL a$`46 㠷 s1ӘUP{{|/ao׭ h k-'}gi4TD29J|0d넹[RCT}مy{6 F-=biB,e9SKJ")+ebIbҰn#,:Ɲ%/@@LnUZmhFjT2PiM&[dY-9ξv]CԖ/3l<1897l`,{??@ (> q ~׷u.R۫ jaY>Wpx1S= K~8^@v.ϛe,H0s쌎@!e>gdn]Ӏ<&噘hBd㘔S`s`;D˩Y6h 4e M ᦌ>C }d|֨Ͻv ?|k=Nt SX,~=mZOzjbR㜋ym[ 8wb9:Ó_mgE<>(õƂY,x^m]u}kK؛oܽ;;߇]9qI&_\eNc@WY%c5lyY6|o F J9*j2*[Wa 61܆:*dHok[IsF*C{ {6%"YS ҍ-׉!Xj+BweիBRA˜ڬ} x%ECn{2KOmi*DU}Timw M꽔z0k IioCXP0e#ꕎ)򂥉v2Lkr\u:ց g?vv6C6_Z4 ,f$C> }1:V bĵ;#y! 3SqRL-&3-<;nA~g6.X)L> DFF}i<.vG=ʛ86Ya960} yѢ"Bc=A ~3 v^TƄ .-r r O_|h _V:ym-۸ Ī_A Wcibi'?ocr,6.' , 炪Ia⚥ۈ \sQVlpjzuealc|tvWa(yG |Jq1\9$L6 \R OkR~|&ц* "_) yǟ}h.\/.@ `: kakc 6v`/Ưirs`Y-_lzH jūfPHdig:blZ$9+=&DjagۼRgT$ۧ2MTVX&| sbl.7 gPaJ7Q.L7P9)3m`>c%ll)|챟 <|SC[PYRjE%+T-W EPG0wr۞JW nL#x (RgU>q\DU"kSw8AT\vÌ^x} yn^Q$< ׮1m&lg:5QuAiiK#`l26)(7d,`v5WPK R"Bh3Y=<Ӝ*vdvL0^ cw.3~?[>~2xlc`N@cϊ8ؘCjlVmR+$<6b*c 6wpXYS 1%P됉1 }Kć7SOU IcҒYa6XSI!UsAJT/2ײkd[/܁͍9v=.݂I 8JeAUSjfѦ>!~d`aTz[E. ܫy l8H rˀPiueˏ}_=݃5}==7x=<c49gޕ s7p)rrQˠaaLJ4K ѪE>h`T1JkS"U7a11ď7L7{HXfPxc\9$n67VޟʚN$(5kq؀W_i<|(8c6b:s+ @JS5@D%/j`D# xX QEfNQQñ:rmF>4(TH* ׁry L{Ci<3 Zbw @EUK\j)72ux){Og lت9<(([.hD7=wbq :z,B)F& O_JB*h}P*GiܕM9*ꎣP[2nն5 Fg d_4Ա4 K⇴Gh#0ryn- 卲tK9%xJу 0*@EX(I Bn]یNP- ͍K Lvunb!Z~Q*oh0 P ·J#Lc-|P0wSw.y|VY,X3*0>{@gP 0œc0+gLp/%h+l&)n?9c)@5{2]E;AVsp[}yeO#"ݵ3M&5L:7pe 9)O={ָ5 M>8Vlg}}ܿO޴޻:=ov2k'ϿN,-/p)n)eX(Umh:O"a56YZ<hw5j -S`u?!'YCL7ixc?B^?TA8@*,ju)Kjs}*L̄LSRE=H %§rs_<>, l;=0D׌8˖-s8RP6]z5ii/D&{RqOttiP!V|ypATVqMBN2U3쁘 V귣l|.vxl/ wusP**`2Qw1r&( + M-_?뀅(3mŽ4/K1Eu\n P^j,ohe+!* XV -JL"T[D8Ԝ@]:lCMӈgڃ:#NT.]UlIҦJAy嵨(uȪ*Ao*["e@By~:pʱ|01C7:fQ l7fTAC$Xp&grxm_FJi+ /݁.2mg Ge!s4Zı޳ᭇRQUb%Y4^{T }Xmupc XjKӧ{;w?'Bg ` \F g8ȏeZ6eqר̖\(&.mΌ~i*a,,Hp:QĴh ɩiVخlyH)!B`!\, Ţ|/=̱Ղ,~׏䘼ڇQni+w㏏ < ܧ g!,`^TeIrջo XK);+R(B'1T7JW.=hqDi\h"Sȃ~MXܜ4~UK}CY"PmH/[ʳ)!6F&scr?Cםm+H[!J($sx%a;Xd%3装SιMG_!]dI#;Z:q۵ŞH&WVd*;_^Ǵg,h>kKw zx𫟞kBW| Ͻy Y!Fei ?g_?x#Yw.ۂ$m6xI*NHV+8>IQP@%,VQfM-T HӞA2@^ kG'o]L+3QשX˨kX aM⴮وbċDʭv 6Ɓj1; fh$8^HLA9|,Hk'j&kSOi܍},k6Xz/U͵b߶ {_?SQ03իW֭[qpٮL[a'zU>nG]}n|vq"KuG| "ݓ"{76n̒4J]xm*-8fj_cmRi ҿ `hcWmy4z^>F3s*=RhxU(%>>Ս}vmf[2ZM{vs3&w~L>nB^Jok:设iSTDP,pfR۶툈S˼2f}Qys  i%XUjL̰2I)]JasIR n~}BT|r?BRQ(qhDPPGR"QM,$A-P EKyB5YV D@'%aJ0ZiDS@IQmAյ*"4T T(P١kݦJa+6`u%q-nQA RJxm֫2GCn{!SX)U b=NT ~7Ud )@"uGzq!-(r܍]lc[hIMQ׎-ܚW_wtu׃$L3_懼Vm}Zp(b yq갈 5 ;++w< ZևS!D%)&}?Y#A&2="4Ż^ENkA۠$"勭l z[ܸrk҉;l&q;8jӏ|g)L0w$\U%`[saK>ъbGޢ $)GqVXleW(,db7N,7]{%o|Wg{@-Ek=ml$`&R| U I=`v`LR\ϣT㑀.َ*g^ ItRERrTk4 4݈ʘ?`0x; B*[^rPcrWty1-Lt;/B3݂:ՄX{[|)\؞BuM![AiQhDYR`Z58604!CS +~A=h|qܰ&yl 9a,717# %N̖DmΠz-zG5o=,ZX23/r.yumQ8ۡ.uEQ&ڵ碲D_`F ) 0HI2iMo 9;{ls!|UMj!IJ93ACCBg[ Q}K G-;fU$Ep#g`a(GL$* X`osDZן޾w^F̓3'`<*7qed[K_@#њBe U6zJP4) #\;BL$e$QK%{Thq) 1F D: 5g= x5+z\Źha.DHngέ{vG?tpڸ3_?W~n7-XEm܀O/ElyBz+4^-|Aj+X4`kҸ5أ8h*3Ha}\ʗ7]YXH^IYɐJM4c(tXЎŤ ^Rl`6 #6tkr /2ŋ;`VQw'EQeB n; ia0: |Z#m*BXTZ-?t6\*g`צ@XVHU[ϡiMVڄ˺r@)szfz**Ż\3=%Ս5?'<>sԸxe_6j\SL*arXZhWp>}lS,7풪B;98i.;I.,z_ NذUGb~XWhU1ZpAǟFY{Pb k}: 36fE̦Md/hB]1>>dm,Lzd1EE_ xEVv gw,<6̍kStKP\qYV2B2iRK3|LaLaٮ-ϱbfD˜{},6j= ȑ%lc1!m3LQ0 Kk&YJ3ȶp(9?p]ۻ5%ej$8^b1ŔsSn`"^thiv x`{R>* 1n-g= UG<Qr8זXٲ TV9WU@ O@԰>>>1'ʍ窩=ɵGQJj" zc mC~H|#Fk6RK$8}.H`@_S,(i*H<מhϾD>N0Q׏ NN:x$ĠO=T\TTLl6;HJDFL *YE>ފA&r|Ji|v[xn^K.Gd*ӘRKfp}O>AY8Hp|r2|MT;'w"+˸}og/d͕_4۳.L$& v˸>5ǰ'%xFq-l5wZ9Rn+`h3W6en:XB 4G 6Ga{jm4\m*s J}mF>{a3z>v."@НۡBYe^$(^a, S嫏\Tdh}^{f*4dc˹ Q#1TF"n\f5>}u6 W NC^@[> \P-٘'>tZENΩaM\R rbAek?<X-g)Bi..ac[N%<nt msc/7z_`~&>v`yz-Km)AMLW|K5M>TptӀxGT! Db um1#h5i-x,`mT %eXv[uE˹EUqzݝLJ(n{nO__9 -i';G1ӝI|&AɄʀ$!_0H>'Ɗl:qdœ8[k`T. ,Rv'o.n[8)O X \7е!;7~s{_؅;|F)kQY95wrUFۈ1P5^'V &gΞM ͳmE,ʊ5y}(";.VڻL(I}qKn2؞d8-D;l8/^Odպ.ߺǤ  żǸ[1Ġy{Lezn 6د[ELSYBu:f \ lPb&G+U \ߜ<Ц_q?C+Q?T/@o[T( p>&<m׎^o|wi*;OI098tKJVz)P2::1 6&xc+s?7"֫t$Am{3ki5X[y~q/e~|h.3kꙸo^7Q,7MXOzޕ Rm5/ 1țef'6zGv虓!QX]گ})v*8c:e @=P[iby5Wb[lkΩ~|Ǭ2 ;v 6/3=vKP0 hx\A[)xQ;[qplTĎQ/Cu {^vºL_9a6Saj`p>,bš^b"XkNW;`0h ~*yAO([Sfփb[׬j@& 1Q/Tعa2BJF{b!)9T&# Vv4|tUͪ*mtC CҳNF,kFeLU*&]i_9]\B_1@ >G-U`3)rMלkVQ ^a=cjRigPgTbVĵAJqNJ VR\X-$atOV,QSdş(GptN[pB}z2W|MӋns޾HQݐ/6sMjmH'/LfeQ),jmAǔ`Vqץ}!|u|5ɤhgfX&$]m-`ER,{X_eB[ٽ-8O,^.b!lsu }gr63 ^[LA23mTI1^ے>ĺ9p 7xN]vơ73Wߠ{3?7K+36eڗ#Vi@±5=JPAFV!t\8BÌ*E  8ͨX# Atؐ}.u]1^d+fM"X7`k_-8sfk|ػK-xݚD(^6DbA$aLUVrW^=Ռ(),TdXDv*VbCMCD7V ݶc;$(dE];Kx~Omz@(["Hj F@jPBfupgb!ϾwE . D{]PH#I<d"x.6]?L$ `j}dPgYA~Υsoʥۃv"Ѫ8f`";SEݱ^ܶ #<ozk흜m{-ܺ4@*BlC{oduϹ7PERHJZn;??8:~pt?CnjInJ$QAb"*j=ZJD"+3o{9n/O &_k%Meمso7Izu~ 6by suKw\l84Tij3aX6(iX![`NA 4΁Ka{GЦf Pj"k|\'i6{Sq~O!, ub$xr6h9hlY@7g``:Q9t>W4DLs\Μ6 O,9q'K)װeVMםfeTQOa&J&Z$Tg^Zn)M6dQY!p=3~*亖>nzWE7dJ~҆:=|c/R6ycJavgO"֝C.PGnh)59ƜW߿XIfXHπBId.RALj)kpn~Zq7*Bϱ)/N#sigıދZm6l$ vd E!El&jc9XP1+Wr+dѳ'd7}( ?NJ9!lz<F!ZL26i[ ( a:oo܇MK0Ϣb聁8f8*.=fu%N>cRC* j_, X=VX:!WP~B㗏2)}5~v ;pxӱM9{{?5xQJ'@؀M)KYr+ӍZދέc?gv\kmktrFӳ`|F=ՠx**vұK^#/ǝXeNcjS)-{هx0`;5n֞qZ{,}GkBdbaqb1Xj.bv m΄ Skۜ)qقHe[@ĢLЮ|^mv9D.{iTU%OɕخI=A ~EWw)uYFmbU3=Xt݉Dq/UyEƌϳk;oxKѪzqƓwfڄhl>w]wۗg8B8~ͫ,h:3K; bn& &w4үَ,ZI"EXTNT4h_, zBf?c?T|8A1sM46pmĚ2ͪo h{tka8֭ 5Q(fp=׌É 爋j5ъؾ&D#.D۶>(B\m%:($0uZ9(Z+')ljjmCs}FJٜ<+}E6bR6Մ!H3gU4&^:bvj:SYc6`Uu@.7Vbt~FP ,ЪjXdUvqfS CjҮE37!R:y>a䟛hzSUVgVy8=؎ lT-{9̍F}V*GTqiރvGS`pLs'j!RBv>Mc5jӫ-茵3ˁmi"Νp. pآճ^&8jT:V#k9NwN[*0a1W'8yl7%2O8Wցj,/RԠ18]"Ml[ѐ!t  sŰf&|$ægVG+ {W|-'w⨲s&1|ѝQr]Uʈ}Nj1ѧc9t26t? ~vsgkMmW>}Ͻo+yw)Oqxv7cHE·I=xzQz rN͏OŊcMKN\}el*]Rybۦa3x8^y{Of0}Q(E {<7z]z Y*M-.sO’Urvll&vu8JЎm-jS5k_؊ɽm?,*/tPrӐ bCdUs&[^/)Kmo|> k"zhwNrSOVǩr'٫-[QX-Y!*㱨y']qq: !RVG|T TK(Y.(۰ʳ ϒ3{ujĮRhS,]leOPዟˎՓ gwc٦J9^qM2-ڟw߿o}xo<zǭ[~zz>)4)9JF|-NiwG: QksoV@4ȽeBcy.4մq0LA-de 3Ϣx55H k3(gXRAsP $E&d9dL33t- L?%i[gǩéŲϨ`CMPȝj.y[=ne'&47`T%-L/6XP&C sPfjF/Mv,3?0fi"O6LpJZu{d(yLV8cӆHR_ Dzp>>ӵ\H!Xx\`LLN]o /WA|,\s nkE@Ϛ 7-f !E( u;wmo0э| %(dZ$+ďu]Ovԉ EjQ]k@r=T}،jO#o0c3=<..mXW\1烊M]mKοiL Ae3}P{5#߆ bP-?񇒝Tn"㜝jQkkeꏮءլ!<׬lֲW?;ސQ_> y'P@?֫ځ \R+?c|ŵ` w᏿}[}?< ֽ%</ !%U3@!CT#EP[ d#7٦ dXO# (*Rs@AXo6O`הA0!s;_|>$d0]M+J Ćѕ:DUϒ#?Nm狢|!$8-z *Se;e ,A,=g`CLyVjEPD-x !\}.l-c7i~9wn\Wa*)޳QYq(Ks-8竪8WmX7T]67:/Ⱥ@*ڐ^T! ^^:7?X:᫟;?֗I@0oVP1պtU4Z:Tm:6F.3X,Zus|뮇G.xxa}I˜˘/ҠHN8c8GLbrR}Tҋ*\.䏶 QcFs{N<ϞywB?n_:~?~A7CAۇd/|F偗g;x?,޵&䊇#8I'WG\Ņi Ah@;t%GTb}x SV.(VIaYC=LB<^ػYf ,T_$%,4C3q.bw oLcW'2}+%ZM hN?i;6V"]ʥ{j w C/0WV}Z ;jk٪0E Mm['&<˭u6J1opATa $! *#ƹ>gl;yf\ AEn Py:q%HF4jY/ g -yJꚷ,[M0Vd6 ٲ1^;Fo6`WHIy*#t޵Z@4kgCj7[Q{M Zͩ&*&R.u`f}z:Y~djx۩oMd,4@ J ) akިw WA#o8p:ʃbSňK߻ؤxx1lֻaFF+-nljK)6l-$kq~ N8T|TOUh%U0lFt-_چp,L&M,g-s~4]/z5!6gv1Ń]:UxOglu6fdFYyp,9c7؀^HہpgmkT-Mз}MKCu(47jEXǢ%pBy~хa4}׎`= ؏z|ꃗ!mRO6#+ylNҿk+7MDlcBE?֛115^<[s#'I"+yye |ȀN,7UrB@P9 p{k {[hapὟy|g!RMbQ s8}uNM|dL!'4Q%Ū+Z*rF|Mp~4=զ:/ <7{*_|ot|^xw2d{4@!vU),-mw/yT#Az<7Z9'ĝ.?Gܛ5| ³pr)*eG^|e l\y|}i8v2v e..H&)w9MӵbƂJR@bgy?!OdTQtKQm7 '߽?x,FR޾l?Ux=`oźҀU6jfYW2>P!0(1$'&L^ G2 -^Z7 eq%F`¹ZʚJ-b-'VpzkŹ6 Ncr?9fh .pg?[=8 E4f42Ӛ2׶>;P[D&=g\6{Ղ3j.Xq{\@ErY`.ZLE󋖏a58%\M/wm[?qD}ժԩ󥧪׌e|k"gyፇԊp{ƍW޽< 7.wv"?;uzׅq/6~h]oFmU9XOXP]_dV(ױZVYIT^bo6A49"uӰy5KKK0>*vp4 o&5 V󼚃άYԋ @g0#Tf3,qTʕ@lX~4ZmZ5Y/Єϩ}M*Ѵi` .:r \`7|\rltmZ1PmO9O6U }NMCŐSŭDdNkʍI:GBdi * 27NrK#|U`pŨSa(Z.4ۈUZ$t{< oTU8QV+Ȅ@*.T}gqr-us)XZXԩ/v[QM:/K攛z>r=#hm,]#|+UxopNBن˷cƲNfY&2-~hPw^st<߄ VhLQ FS6D [U @9-5vp|l,'<59#)h:bVj4tѺK'o 5yg1zهdf3ʙ}TȢ{< d2C+YbGWBQ2$ץ5`\~&Aj,d*ٱeZk*J]͚+9zs/ j~Sk/T k}-9:6V@z֠v_7}r_bٟn'lÃw`{{KץǞ^GOk-K(+#Ƣ*(%ʗ**G gn&6OגgTbVC0%M%p>W? UxQ3F@Lm@rOIA759aϙyWˈ벏2aUi 1P0ٕ $2M%*n:, R+ꎨ(co "\ְ7:o|uxbj۸:?y.P.?EXވ ?߰Xl9Z*!+ OTPmP3 T.Lbٖ3_lFY<ʑ[?9w߸|f)s)$G2=j7bDL[k?Lvi|{YSI4.p.%[U\oznT2֕KuֵӃl3O<*dTȡ+BEQzFh,Q5[i`er{0lZO-g&+4t斐Qu$٨i$lMHiI|#vdE 5]A4k#F3MIHˣ77O/sI<2ILmDBe>cfҲutc*0 n̞` hCZR\(Z58}TFZadP5xj$5S\g2V-cDTƾKTQ9pVIf !P x@?'Xf=E">#Ͳ'?w%5o[7%h:Ԡa6 >`fpTAZčOǺ4B}~h.2'awlWܔTYCzo6Fhh8-XG44!zQU_|in{3R%a(Džm϶x>2]j6)~JH{`;f;ݔ*"17F̡8$+&lʸdgsTy#fcgR]4JQNRVyV|hW[WʼVCu@a#"0F{S{kҊp_?39W!1xcXq}%YXTMQyU)ShJX$Go-r5)K7[# R,9QJtgGٺIl3ɦz t>Lao/y4H.AlR}F}J)ryG{!?s>֭[ѝ;wvÂvL9@L^!\wO>x/ we k@ڲCgbëCXdVYc@p.߼QAYz]DiLbL+`Q#;CSm"J*~}/Z!ԤK;CIx;mib`mbky,4ETNa=A(tpϲ|MNՎ)My;:IZA&5}C'~LS4TmlV`5ϳR͐#sωlwKg2:0])cfHFFfAJQT5cײZbPM8O {S0*Rڼ9{ª " V$1P,%L{gUS P u hMM` Z7IJ 31Z[&cO )5c\gT ]VlfP%Ӭ ^TJ5s<~w}_n! QdZj|-Xm$l12)>0_Yi[H,-3ޡbP7zMep(6c>ڸ) Tpӯ}T81rHj7UEp΀5*˽6r3UBg=q{p|Z1xIܧ `ڄهr } .̙?{! iZtOJ[U0Pu=^*BATb%*g%l=lSqy%'.K@Llq[ΰ1͛] &CJ/{a ӇH 2EH;l`ņ7 Raz̠>!cܩr`c\s<#K";pt{ zA7Bqoaחz16~N6 !yqn/Q쮤/:2D\o _$J|`rUUAMbLK9(;r$(6W{pB.] u {-(Mk;"JۉԒ }T|,LsKŕzB@MuLtҹ>}aʣ7c:ق`rYηVp屸[/sC/糟l_Wb1çYώ~')r曦RՌ2Xlfz|_0^ 98*Nj2 ˪itI(4DDwQ-2efL6Mh{GE+/J%Mb߀5ʒ-s93E Ҏd[:˳@&_%*UOWP:7`cIO1o۫yUqqOdYx]YFbAU}1q&F=fR&ܡQ/xj MoMQ( ZR1%3 1Ұ[O ~U{(j M4̰䢰E@⯖V-x6I1TS4@mљ9v2c ;d^]6YV!"׋l/3d v{^Ynᔵn/FoQ+<7FSxP qlXU9=nH6.Ÿ ([# | &n3@u^Bsi:m|VÄulmA BE::(^p!1u~7+nAa(<eO\`0_]Qnx2إ Q#Us=an*cڏd?z{ΚX ae L8}2 6EHPއF]gs9CEp8[?%x XJ(+\s(%EZ :7,v3'Gױ+CvmA~.usck%'Y~b?H5W^~kߧ߆&OxeՎJ'AߪEԾU"dj) 8@ wkQϕd /n9\;'Qڥb{/x'_XV1t X4;>dq:UΡV7k݅Y^TQNeӹOz˸^+[sz~ (z۷tZ)hgTDi\4>cicO|6|p41U5Ō$4Gڇ\rf@ǂF- 0M`O8B'7\o@lN.L#MqԲh߹5!Pl!lkw/N)4uS0@29TV?3Y5Uhv2hRRy{?^̈́Vb,T~74`>G7UN̪gIsj4Qh%$Ȋp6ecqNEfFԠ;WCbVmG25kݚ޽PU ^P+iši#[ևe,.s3yfw;}}9Q<lqeC;M{=;mMY]By  #7KlQz F)WtM&fg6p[ $h<4Uŏ&iإqc8cGx"3rÉAu< )G)m=(-vyh<ƶO=VKu٦{y_-#Az{ RmD\ɹyQ̊O*Y^O*)8&]8>p_?:3߇ UʪZLGaG<+pjf:@6p-s.Cd)DAÝC")3.[u@X$(%u5cGe%47K.PdFv|.YuƑ\<. B|],9 ,ס~ >\\tS]?* L=^,M9b=YEmG\dXƇg2<)z8/Fgl'6;c%.2Bs]b?+Zyfkն7QHD.#kbTI7@,&Tr{4+ o0*bNjsO\ `, caw_w"2 osO<|xO[t_n cLP,'pPjѸ d5fK8 m\"')ȪN'_.+ܱa5^'g1|IbH9;[0;?G# <%!I Ճ]ƙ?y Ǖ(5CK9|.,[RI6u#x)Jd,Hp3Wv^$l '5X@gjoE590$ŖpWeY =؆5ZͺYEa :O{59/ 4h&M2IbQSjıFȍxձsK\'y T=񯾹yi <>~{]Ӕ{U%LEJl˒g!WC`M$@+Pܾ e\.%[pxP8 k"ﻶ)U- WYh B&"RD:/؊ G.m5Bݿ*B*FD^*4Z=o_CmΤt{,*}UHOێ="$n1Y69p̎}91']`3p*57|ȝ1K߻ 0QsXL"]D-O 'c}306O>g% Ov+](@]k'i-:{nfw{B~=H3`N{{utS;/Ԩ*h6,Bxcs>Z-ks $ׂZְTzU?UMTNܾ?f!_#:l-q,]r=5CVŸxPuDyili,_ Kyo/v/q ?.BsYQ%cO^>^3F{46Ζ['eOvjw:<7#{^W|~וڵgd[pW2xr֏3eR"[[ϹU+i~%E< NJ./`˯l;[E1ff6>f32Ʊ8BY'vx,ιu|mgPX"X@KTR'Ʊ:13K~?Pp~RU|5KM5ꬎ|otέx d/ eϜSOPύ瓌J (ϑ'EǙ5q/zxg?NO;u luRcףR(e8cX RzL9A9#JVب2;j2ߦ,.7O-1[w~~'iw0={pmgnX~czbvIh󵋽e0֖,dp`F‚_58k9TAWTl"kUs=o\W ̙`Nieͨzn,[jNAk/3!4W3]QNtT5uK` bߴ۴mZmRӺ *J.4W&<-M3m naMqIJ M4ᶈ9@*#J楦/2gr,Hvڿi=$NLZSC l#jDn-j,$kaN:+@7hjA4xxR@lEo\IٳU؉6kY Jʈl?LrJmd!FA @Ӊ㍅pAUR@L 5SR̩uNTvRSY]- CN:gQK;w <.5Rnt".)Rnf7Z14zV![rڤ)*ݐK [< ȵpdUe`i'8Z3f۶ܐUUWluS#.0y4Tۖ;zz]%'h&!*9 ]%vH4cW<Šm)LK?g8ub m[G.^r6B bwY蒭 L?Wp8NTY,l\ݣ^{ oI8K:-&M8>e VI6,;JcѐKMxiixv\%gSC,njBQq]Q环TV0VHl #O׫# #sm2;7?[bMuLL'Vl 6s ג| b$FPPTe T91##pn. |cx1h> d:W.]sЏQ}z'x(kHk>_L2~1wVeg@uپPb!(V/qQȺ,U&(ǁ'lG2քIP(#De$n MkzVٞ@!a2d2 ~sN$*U{sc$Ä5f5G=]x15aBGo9:*F"LKv}i<{wrAk#ϑ.]',t'ښ+b\xTa2sr ll*hT֪7oG0~,ݐFۍN0VL "tY*VdC&t6Wo7PN6;SRF[Ḇm݃?!g~_K۰doIhM[CcH90-N')ھĚԵ)ɱEk;׮ wo|;g^uf'ܫu;˜LW {؀$b7^'A\9[6<X)fxZe'T4G BRT"c+ZnǟHxFA쒪 l)Lxu3BJ2Y>ִ8(j2gF&6l#2vj."֪R\3X 4, [6ݔ*4nM{Հ.xڱ3Ov\TUN69O ;%fqˀL߷m3vwFE[vľNrʉ@X-]1uCljįJ4uk\k(+hДYJ#JPVj Ҋjs'ouP bK *‰'6{SyVLnt ` E%Ej/TUmsr\֖f4z|ypȤ!ni+P)-i:<'h͏H$bc枃3&0 6#*5syٮ,|ye%fܸ8<58:8}tJpY*^ZFΖPzd ۋ:~[6mc tLϩUPZj2PBVxs(FaQhPg5:,]jЅ-P;iu4Bn՛kxukdR/rN. |S_+}j3+5ϣA)$Fx\U\lfu +:Ǎ &8qD@ `$Vl^gz&7GO7"x%MnY޴i G׾u u5ˏ>s;TCUv^EK]r()AZ'HsWX >5R255I\#79g|)G2Ͳ^h>kJ3PUiԒ!#3rC+T"Ug\Z^[?D+)lIO  ]? [CVl`?k80ߋ%0PP%Z #Z61pFKWZ5%s* ֱ*a=}A1|[cV x `O9r>Kn `L> ƴyc->L1\s<^f G*{QÓDTlR뤹b$S{ Or gdWDۼYRQ(h.eA%*+9ڌò1 mR:\vvp4nkq@ dKvx.9{d8׉WH5fBʐJ|sJ導L'̔'y<۠( 8gTu,>[*ܾwDz$gml F,BnI]nEz'0= R3nI3 TMUV"jڋnn`g㍷<,]YV/:M6>ԍ }\E'=˒Դ STHeϭo1ώϼϾG@ϯ̛ܽ{p|rFYW 'qm+ZqO\ ޏ7f{e8<.Ry|Pjk"NdPnUZT+d}}efZ)4UP8jSwFTC*[Q,T.QP SzsZY:Dke6&V6&@UQhߕGb Yl2MuutUP4J+Tah3NXÇ*TCߓV@A) 8_)!;'ޯ]*j/( ւ6ՙsm7Լ\ML#h-kbO՞hwPmH4*'j\ ȡY} DF zD[Xŝ RB󞭅vc3abkE!s pFrNV'K{kBGccI*K_һ CbkpdO0% L.+Сl8 T ء@nz4ʋ8!(Z 7®ƐjG8 T GA @S|Z61nCh9'hJ`'VBḛ?ZZ@#l* PU(Odaq/w`ڒV P;ϞOjF+{4bƊֺ@0QɲIڰn=]ݤsGPjJO]㌝.\ʖm?|[,O;ls OV><^`{T4bdAԚW1%"G39=1(<&TIg HB4SsnoٖK1}/umל[&\7Wo v`l9aGxmҧO\%&5/ޫVχ#߳!&K˚fXDXTFY:%OؽbP9?UCPҧcй::'J\"==C[9!QvC w/<υBo>B.٫F7L6w3sVU+x_fndW֘)L>O ls>)_9BPSBGL˸ɫ{/Z@FQlT+; GS5`i|{eʲ+~-w;pv,ne7y}lEX7w,c({~jX<)O2uZ&01"mxpT,0?q->uET( # O=߼OY 2rMv1{8WqKE 8u*r][ڢؤQ1vqI4%;2l:tpp^yn=A+)9>Jpuq2IFr% ιnAے3w93_KeW&C( wkO>w|+*p? &y==[}V{OE_no/^翑3}$P|-+;?& yPzR6ĆA/o"=ayozQ^AU2k!!w<&8%R| $D9 F*ZQ,?u*x^lKFOŞFaN 7?'*v WA#/oŵY$i Ay&xW GLRl<[6?y@Ok'`L]j٢sP,zO%^z9eVm*vxՀgوV(d[HFK~P@u"MZg@NZTakkS[UՖnҕWuH0ϵ WRHC}UatCok@(զ qj h ˭3xdthՆA[{[#loTCgtP<i)1b=-{aS|!S)بM"plDFaZO6#-W6b>]]9W[픁qxw p3}or)qa<"):$WfMC1+u\F3 RMv:1& ͸M9Lk>&7}E/VyKkl%:(ͧsZ<6.Y-_7i?i3*p3r.Hd}u2ۻvTd%S=Cj38#3pLLaq'Xb w6THA>bƪ!O5+F@n ˥f}[>QDEB%^+isV9>^l "b]&tNyYC"daEҀR< #kP 5\azn;+Y־|s]%"uEIM,˒ch0%Oy)yK qw#ntۖܖL)%<ZU35gY5kMwkVYc1Cz8'Xm`b٢]œKc``*|E?fOF{m\'٢*.KLbHGK/]w|x?~ i⻼w^/Ҹ:MRk/< szMzmcpUȤc'/j&B&ԕ Fs/eY5|Ʊ.pvђY K4ڨKYzǿ(s`P{9ŠXX C&CՂ ޙ˳Qp4T3RbvO_o| =>|jy<ȄKXP|]'Pq.'rR#%ƉA2ޗ1%Ӌ"DYHڵiq\s:!7{my s¼jygia*Jt7nݻ|f?Ho@.*^dVCc-u~;.#, jNA18$,XҖKRODi A~g7`HkijXwO^<{c_Ᵽ6T=ɾWm)j]thFPta_Hڨ[ Jh!*Ţ%w;}@w< z~o|??>>Oof'&HR =A;8o0'Op/KL7VƋqeuKzy痥L;1*}@?b 8,iy@9oKtpe9pm.ĈUS\ii1 HlA,_m\8(p5n;ߡ 4Z -cMHXɄ~nҩ\+p o,P,yq <W \C!K[".vʋxzQvqZP"냩ek0NSlF kUC6q]fBٗuJcje6Yf𰀰 yqۧS1*Q\$O[M i+ B@3y2 aǥ14}!Xv6֡o& 8VVnA46ʪ@iِZV%jhU(HP4j<׾ |X8huLlULا EIkyO Q1!֫ v/C4/lj %ۜiȮ1`\ ϮAm-(\ֱzgn{9Eьˆ:U{6ٕnq|r!lٿ=QOG`X8[# 8̺ pF6MxNZ!}a[VG$Pݨ3(!9%5@"K{ \@i[ a)P1g 0 )OuO`Lnwp|?A|`Vvy/O}*|YGJÐ Sk9J3O'k@_2Ni,VA*pNEɕ#G| iCKI 6U$!DägzJp=.+r YK3rrǹjaVk!F4LBR;ĩ76k3qވD%}#p&A%sQ&U<@{_Rfѻb(ڗOåd^[3PmbU~c.9Y6I\Y\^;Q5e{ͬt%?k&o]Q3m Z E%NE@ch,"tZ,6ZKLqJ~aXe57TI7PfScҜNy7^Uyf5@c[s6Ƌcd}ᗞ ?zg|4^szx:zL{Mytș}%ڗƶ{\R{ubV3A3E-*.ͨFћ (_F9'>(Tִ1n) <~ pa^<ׁm)&4si^b^h5]ԊF%8N53[覄DZ>Nu/}pW//Q=irx÷_>,.}Ǫꦫ-Ӟ!W+R?+Km}4o8\v9EvV$\1R6z=8{7^x@< z~{ݿu{WMvwT>kpЇz/\x;y 'YXTVLKa[|H)hBiٸ|L~beQjԎMSF70@dtjt(T]rM/ODc%YmPk|m c7]I-P,KvטB\ d9ϝ{hDiAJ@M,y4l6Z4%ȿ] `qJR )jqiCildƖU%tWW;*@6 )9AԮl o6ES-3;F}cve9,3$v&Sxڭ9 g3SJGFhB3,ܬuj :mFZ[ZcILUCprJ66yM5,nK343@4tM&cA>Qa5nq<L72l'I_I"xWQuU5Yՠ*%!ZQ1Jaզ' #A:`u~ .'2E Vl78|Ϫ*| ySZ0čRek٨[r bVUeZ ^sgH'asasyd Dto*T2giL \b6,5)Y]`A%Lk5qGb #BwVUjjBәaյ{򗱮Kgfk1 X,{0G`[!@J Qk'إ1vL]6y-9}1Bh(jLol}?MD#م0.'9+y( pImv.=?;A8|CQV)t#U؇0B8]r &b=ڗM;:!-rs/Vq_/2ѠuqՊ{ W(}\%K@v 94PT? AX#He#AȪ(Jwni|iYvYEM~|XզjE4Mj @Sw5H?sz+k! 5"~Oc>f])kOynQĐUD2eFI}I)6ڑלXcnҍnbGv69kj" dgE,M,JVX+Ҵ㋚ c~ӹ:&5FK W8n;O1ֹ]_clꐐIU9V[xbQ@c"Kk)+ڇ`&˶__óO &;4?0ssG,%mqJ 3V i&;>M>՛0='E[HNmk\yQ>VamL7*"Aol.>4>9.VFhD[#7sQD1xc$dj2wǔi%k,yLq7/a9sW .p p^is Y2εfbZ%ԡ+v>EӀ^s۽ja* S,8Ku$4lz㕿~??;=?w"{/j'/;_]L7߭G 5y0SΛ4 {~~.]ϿݼCV`!n4!6)QK&^K7vMt9  EJ!j6LD3?? ǛԚ၅Pc>Św-ѭNQ7L^("EJS@&v?\f Vzv7$TO҂<0IXzF-03[g]ormp<r҂nVE4lJjOw]ꡈ0 TžDj F4k:y= S"6MDUş*f\d!L8^K&CUd3-湉nUR@;lVe쫜{Y@P+-P .c*DVbCĭhЙa0ij>. 8Sg,e{3QTMVSk+U&ʟfsIٻ%jM6?#(<^Exm#f)8m'6NoX*˹mؤ7^C{iUah)+FS:)YZQ6IX=xQZ9̀^ ?\졊Ò;HW5kCBHy/7a3)F: &6ѩ6-բ!ߨr,1`UT_VjVUJ$Lnb² yf1au8.]#ּztuH`{ gW;kP?*PJU>mj("w/ӦzQ+\&f>WRHMc:qX00x: ꓕGS؃7o5ɹs8GtϽkl? mK!5xMrlr`&XfWՑS%X4C6 2ޒ7!ҿye3|dE3 P3߭;gp^:|"3s62`8=Cz]=_||(}(*ġcxDmb(Z-<ՋCAZǯ<1%[7{88/x0F$y"WcNjH,+Q|RCkj"/3@>qY;ŇKN%{糒m XDhivCτx#rk}ij9j'|"Sjine|y5XmzD45F44FJ4zfWVՃiVj(:J6UyjVꞳ68FP!pCb*#{UW *(Az"} D$y#a Y#]/ 61?!|%|gp!-=I|'KpR.7%! >'ڌr$QU>Jr!JjI3yGV9.=YiEC0@/j{%c_m~>ὴr-rs'J-83]yLnV`L'd[V*n][ljC-×Tf_~__޽{o~3Mig㔚\9!bǟx m-]>.B[ &c71D. As?-Cȳ mbm͎GSo?%6 J('sM  '5P 8[mTQ],\!0n͉O=׆L?ѫKît`+/Bk1-6ӻuh:qDk:=nnbQȈAA&-6Qd7F?JNN\Urn I]j|vՅ8Ju (sEL ,9PtS5}0ġk]KjPԀ5$$|=)aPÎqEF d+Fℼ iק !^{g:FQNjÚ)4Ī>Pʢ4XlrD*jƒ;nI3OW&'y̴^`tX( FadQ|B͵"{ZUXm}%*Ƭ^+ۦ=$dE@)61շMZ,0"vȾz1 ~QWKliyCnR`cL8f{'2V{Ԡf*y2ɂWQLT?ZغIFHmj|"f%fIn r@|`sCXz'e[$ߙNryµ-Ȫ RӑLpO`=A|p"~ֆwI}`F4HL I@jnȋii8"ga/ǻP-?aWOvnޛ>z ^G8^e(]3_$m6²߈Z#FqsR٠3Aݮd6hx"O}Yg9;&E%w'XxT:Cl w?n4OD-vQ TC;xgۇg>xuקCO3 Ddf$9Ǜ<, ydH.b!p.R/5#a (LW"Ճ7`d*N\CS`+1KǟWP}Vt۞0|}S؃X.f<7WS 6P#;Q89ՆLJUCVr™:r\j!bp3*L7(, !=ڥu(;6/]ڡIRsibJX ϣVBfUFXNduԼVm$B2z@\KF4E\j;O7sIg 2}a"P^"j>rFt ;^q"H BAg~f.bqؘ KW !M(7Q :߅ yɋ-?GiNƯ]/|j]lՓZƋwŖxF& Pf͓m*Zc W oiFu[:M%u)QoI_XZ%4jO<}QR޺y޺1{\;]oC㔁= }Vg'oog*jv(MnzUϜbwg(߷.!&fC# r||ܝ;wQZe?xo2wn/}vu+0T$gӤ5.QȞ 4گv')$P R,y "SZ0䉝ݝ9(V彩&.Nv[ۧ1̜zW⤵ɆDBRE!UgHc_/mcB40g"rАptg`t988Y(mF slAy+FS)Mм aRPIElZYnה7Coab=&&>ONĈdRa'-])jfL4iT=4#5ZwwMIқv+܄|L1xݣi \QXS%bJܨ;wzӊ5;wL8 Tgc4yJ綻^ˣoѴƣkh޽Uθ6٫Iu5C ;lHpw$ i6IL/J-v96=OHqɶ s5zW1KVV1hejǺ~)[@2^(cVi+ @q&jJl:+ZVL iu::2yQ)Mjɚt<(?ҩ;;[C)X%!M%SMT>`_AospXycX%`J )~/*L =buDZT`޿uӖA!D [MskFzs0Q 堢7jDobON gT+B=>ٵ@9Nip2k ǯ1D{`Btd_dr]޸u%VC=εW7QdZBHfm=s[<ۆ]:&zvc"g=w<}6e~aIc<{#uɱ5 *Rzr|dhO02#uCMYVQD| x.dŬQi&OP׃xxٻrb@]jQ~($"&Tr>l^}CzQƎZ9#3WH( d"6z? wt6alu5P +,}ʤ RG!:&b; XlWVj1(|AWq,֞qQ< bb8IlK楶iapݘI1ˢ`:2cuȧk/Qe+XWQճZ`,MP}kZe(reVW"rsf<. ZOsߥCǹ#E )X05b^x[ajU=Gߍ jN?su O."4NS>%8߂n} 3I=7@BP9:Bljhen^g&$D!Jl굴ĉ"!GXIA׋o=#(y$ *"j]n+Orlg25e;㥖q D +6PLR#c:O|6pbmp\Ç?` =N波u+$oY<>ݽ8֧؍+y8tohTuc~UkC'ҙ=:l'B}z+;/1_AZ{tM5?6)57vffmڶ74YРPhʚAn[1-,kzbC^6Q[pĉ 7=Jw9 =lJ\XyΩfƘ̺PUJi-!li( >f3#>pJs@c^"˱|ei F-a16^Zhk5-TϪL\#0)*Qm hE4k@cq+{t o"MbX`ll;QXD1Yч[UnGq$\(VEU;Lp]0UQyoΐGL*vp4Ӝcĉݜ 3euz@%+* q y]:g7mhh?E kh]n-!%Kk¢٪ƑTk_~@w|'O?y睋!62WkM飹> #B~.uJh.vaۋ4M&Ne06s,]Dm$%}∳4DsHMZTEYFLah^N}MVdo'C0ZÔ#HA v鵦EtH^*LR<t>ydQ,y D @RYCʆqjeP^lG gVjZXK7R^i~"W60VL`(\+[ LS%};4 BM$}LUAY{4+#tnC,shXj5VX;(I~Oȟb-)WX@0쌵d1kDJ=;E.x!}}H?KK apX R Z&haCilj!9=M&YEm ɘt[sw4Vl f2k;9!}n.+si]>mY[y,8 $37LĜER|^i]iOkacȏ3~56;E)T{3AP= =WG^_&F ts.@WAvU JmZkh% ]QbߡłMI3qZ[ŗNjȹjl6+JM2w08\H.[(π'h\ a3!FlO-m.d] ^n$҇Ź $W)zT3/pTOu-5}/x#w;XF&yNVȄv٪R`1G ~`6s5jG#'ޮf>fPM:@9tUɼ_ {=D7Ԭ: dyO,ýe*^'nG$rs0=ui.'|^yV:(Ilc`P<ZO,{C&o2V)F]Uս;ǜmSژU꾉"?1.!]dо'tBfn9TOٚtH71x^DAEI&;K^&\V.R6+o'߹ ~64))W7O \XamأY!-9zVYm,N{r6 c>D;gdlNj=gi($4a2T1(֎1VBZ_WSE` kHnw A ,ئ68* M!^Ӆ H= 9}4+iO!՟,a 4cýyW_ZW9\0"OIMJ5\>dВ6bBs^עC0 Z1O?ぢ͛7+K!Z•AʞA&{=ĬN_4ܟ?o'Mp2 5Ϳq F@A`!5ϧ6r0w{wsU>M$71BC'#oT@ Lt7lYi*N&XE}M #,`4Җ2E=X'؎dSm` _-G8y.:*d2>)3,Y7sɊc +E)@Wck;^:-,f)jP8cVt3D\yv@N*޲_x9M 55ȷݎ`m#XWug(Rh;^T*[ ΖpƁ=RՅ3]Sby('2 C4cXq&c;X猊f`@o2m1ت.N\r&lj=QE0}2ދ§ש'0NxXF@차DPqF>%jҴ)5J6רVQ66/Ծ '%H&H4ܒm @@<9YQ* ̈́8yOol>fj!F#u d1i_qɞ tP΍dZR(8 R0*iB f˨hbB0ͰQHt u6CɵO>OΛX9dz [3i&mkʯ9k[.sKH;e<"LeKk:=@ZqѾwnB1C7~inM{ VNL#dK6b9予0\Ϫ Og:Ǐn5mwt߬ͷN? _}׾ NN89p,2GMzup&f"n 6-PQ72m۹e]l5Fj7pv~w.6cU@*w/q6"='\vFx] 9m]K _ޏO]W:k*}D6kup O,N"BIý>1[9S/D0认+_U=b=N%ӯ8 BQ2Ќr@n=G!b%Bk)IY%%D.RNz: dB9ЧkyOpGj7VAs26Lʹu@@ClTqCATL&16#TTg-z(X] Pv5LbNHl_温؂KT'|~@?#k1_>/xkT-(dLBxme*'.D |5`=9Jr>u~v8=3ő%pV2Z+on`^x6MtGVak8]G1k47}m,5nS ,nVeߡM5VmrpeǏtsjW<_zr /}G:pc[@%pf}M4= p;_%ף}|Ħ]n X(k=^sONx@# }souvvvcZ=,rڋx[| 9=n78"vgy{u?n9+,-eilx"(|Iw*债[N`[?N7d1Di.\hNαπ'Xƨ3&P>y:y*!{8ꀍaq)嚙mq K&.k:x` \bh,<$Wa؂K1v(X^7TPKv2xWUJ!]ls,ǃƐ΀P BN 6OcL] `]FVR7b [B45PvnTB&d8h*apua-(Cj6(U-.GVV\W5ιclIEY杨Ң8E06buDZ4indh mbIf6vLݻDJ'6[["m^̈́iIFz':72 "g lUmgl~`gYSZ]_H9%g@EzRIYFQX*D,\tM,56ab9' i&jlu(get OKl8`ŶXٿBdw{\ݰy]{,Z❐-m ? pQg[4Ĵg"fߧb)Ac%țy+lY'{7F >~*lB`1^Cmz4d܋=/~Iu&H|O1~k{0)i60|`7ɲm͹7# 2ӁX! k$b -ji`E jbV`k f7՚c2#"9=~îCf!rty:gLhǜ:"ط&kF%ۆ1=²>|b W.ڑ%uyZ,#,!齝1A94E&З>U#B-EH"a@˪0#wBTgV#JC1+viQodF`<02SODeq}.D%FBVyQS#=f2 up0l =58+L9E^Ar>z\U E滬␹q#3M3}O*xs|kXip&u%:]Ψq7hSWu@58'YLI~BHNZnm[ځ>T{.i!bl$0cuh$/T*7(xU:#]aE{Tl{$]VJ! XR:a=pt%xaLcH[FZfl2[nĂPR Ai}wRq6jz#gr 8[(Ok RyDfKfPP^A3dp&_TE19 K^'^2M'@QW)vM;UơXPrXZ { U9!+N+֬=EySe^kf8 EEDm-c\ OiCO?{uׯ"߿ᑗWp=Xv:[jXmCSyL k%?B8Qϟ c7,mikȺ7vBdno7|cLb/|lO^OTm4kMVsR>YRQ:(i]ʚ0 R dnk]us%W(Ǘy$k3xR2w]܃0DӴlU iƲO}Gspq?gdngdBqbE 3l\@Sif~Yy}ONOOu P&18}=Ow;ܮM}B/=?u~n0ZDڬF80U(0J]?[+4%os-2(MDT, .; &K3QalU)$ɒN!pDVH'(Ą/2Dg%uA9;Cbbi`-CrݐRg/3 ㌛6:$h*^i*zcV>'}jwAF%_ ri|]%d\ۻ߂ՆvT,/]u;j ؎2uD!ohKKCļh^Mk͕j_O^jmf[鱪5[RUEĉQdh̄>;}^i0VNi<(C0@ӛq/yT^QaAw壒*-AdMHryzޕ(Pk 5$p3g~9e2!} a# mNX0VjrX|tϢ(k4+I"BO9FOeá9@:lS/Zszxzr`9N( j̠;,WE"<= J_'/cWa95DD*=Ō\E5C đgv%Ysr>fޗq |hℲZFS3XkmR$h+e:6 nqDꘞHjE"K]{cq@Ac%x\zcקj{$*KQhhkUsQO"\%X bDOdN|Qpҥ_kT-|UNы-Ϯw^j17+{CdUKΖbc7e>ymL'@"@kaJxq-m%s{W|UGFK\3>U] 1&u9:<ĪB&@ jKAY3ZD{fP~f Ol:7KsDjq 3+'ieK;uxҐ-\lt/Tj6Oi,˾*,%cp)yUu]gze+G 6Btƚ8dcn&}4B)Xq%K7 }U]kjXE-77&B5|^Wߎi]0Ou9'p>%gjJ;5sSrU)ASn_\Iܤ!ԜVA^g[λ/o7v{aZ#|)Œ[m^QDы}L#/_>Q[rۻڈXPJ4Wמ՗hy,]WMc+̩l{?UTzhLuIGʧ/|QGWɇGM^Cl uYIx :xװlaE+UkX4I5Nt˳bk7*+W&Z裇@%҄"]$B-rVV%F'kA~bTq Uvsn;4NX!hvŽqDYa SHa7vwBnwndž"pUQH, X 9y!k7Z05 ޫX~6]QiMi Ѳ*64ir)* jƽmpMVTRM ua1F(zvM<.(Mx"p pBzM'LD"f we\k)hy%_knjRl`?D/PZr)N 'yDۊE͊͋a˂y,JĦj:?Pd[eo'DؙBFI4=E{m]*<&<<0MlC`,wCBDlQJ,8y_FSB c~%xfFZn-Inҙ!`T^ v!BF?[ a.0yث4M@%mV/ȝ)p4n~ '.eDh]9]=ۣ`})i3qH6v47Ҝ,Oo`ol$'B|5 FC1wɍ5ҧlӹj31BWg1wb)j1m=;ν}3*7+]RSZXVGU ?sO*9G9\dTs/!%~6ba@듗{\<`Bf>[6Endna@UX:M9Г+@brG;ĉP2Ad[;WԠ߳FJM˽\g!B*s qf*5q)XQW?"|SX6"hF@ԍjQ`˩|= Aܳ.BVRJų[gV,Ǩv։w^]#\%[t Rd6B Wny^l2(b L b´I3y.8!H-va[ZTwUA 1V͂ [+"r[f9. ]B*eu&>]ga|0B\>pa?+ B`8cWǤLc\im iZÌzYfS]HyLs"8֔y4}Ο控/ã9<0iqLlO|/s[VfLdQ>֑gBJvl(cfCل6j< 9R BiWHCq\Vk,ɳMEC86u1{FY} B8=Ve8Ldm*+5R&B%b^ __{{4V0_໯m#=@\4ϓ7,8Y_](T6vuifkjx5 yYԸ3\nñ!zy`n| ? ?~~H7/>+:<[̙LޒRWKOl &[ʍ7źͬ=yX[󮌝YQa^Xm5"[{x=|4(QksgL <)7?^?%X.:2P6hr>blZۛal24MFNqsGX=gg^G}''''=!MrJ+ꙥkCC=qӿo+ߖ&5DHo"W*)]-eaZH)hI>}t{"n[^("@a3R"&\}] ,Ԣ=L̑U-}Zp6M ANx~/b ̡1$`{r͵.wP ZD^ $th@,̖;lJ8\Bl4hbJ(_3ѠT8iS4~fGAy,G! T0ݰF$D8bkd-eq } K`r`#hFm^֞ ;*8\lԥ-n[c^;DʦRY,YEU{{XțeaI)h7̳5&}yie>vؾ+r&nuf 8r?,lyH_t,I3]qXIs?Uv ;-X=YVe` mh- i;[e wlhk,vh1n?pVF QM[)FqhΦ6n8wyU-0d@-kt"'tr.& ɹI#6%yy{ N*7P`Lйu8]!L=ۖULrs aEQٿַ7$k/%\vس I,?No c/]t2ҷ>Z6+j7K.@FDa$(N8K0N9kH7ɢ n-w$1EJ6\7 w7@%?ƹm֦`͜R:!so6Jߝ_`0!ZĦoV&<[%˹4:p9uf;XsKy:օc{+R(ئ×|˽E&3w[~{bz_Ɯ[J(JB8_6:ɰ'4;+XyӜ;MXgZyI>q Xr(VS*)6Ѹ2CT:O{y˱^Wm_6abb) DתCrָ"0uF_ʦhs'ULݩ֖$kGXǞ}alI[ ].-ž>~nm\e*d(fShN4d"6^>Msݗ:w_tG'ҺYզiN1uquzHɮ* m }u]څ{JsX!)ŧe5$cMyƱ 3x* ?;5g%d`^9<;3ĨZ`0&(6rz8n_| VWu=~߼o;JMFyb($ހ1y.Maa e:^;L Vd˒uHE~>:p: l n~aYl]Hi*Gav:*^d.immlx+$O%NSYFv;k ^wSVO'bߦ]r^ K*2a Ӄvye/3)֭cVd{E:%_Dd̢!ctp֤smGuIEKҴj[ Eҵn$a׀EFaV[Z{`2 TnWR}3%|:$)q7DwygNE6sЙ CLFchK 5LZ^W.Ye34Pl-$_ǎA5yU6C6 .[%ppJX):U F-i6^;kVBkۖBǨM)#Tʪ 1mGYuPmr\C 6Vs: 콨v)sWAU>E G7՚&Mu`/N<,U~Ecl}.a)F"2=[a3Sd{ qS7;ȝ:Vnh7tyXsϘpNߌNlRl)x=C]N1=ղU1#u JӅ[ RA>_nnirsԇCLq 'nXdʔ4"-XlPu#4k+o\x~K ,^ۛBXB`+2f}^Wd|?7&TB0N=FnUkIo&u1WgRAcw";9W2yDlpq>{Q᜕~H5fXapM&p{ أE~|eŐSX"(%wnڄ-c~s'}RLY5<ˀ2wW-@2:3/\,%`Y4f&ޠd10V*U4Zn0A&<^"rx luO7$Fgc!Z@6=]1T 0ޓϛP D=F#8=ocpU=V%S gO r4ACf-LĢ7|  RvbEgu_ۤK2j},u% ^s]^DƖ^k]cuܦ,''H3oݒ<+-ӿ'ك,7Wu;Xᝓo>7ƭ~nt4zNĵsxx/-a9cXng"ƶrf Vy᏿?zdጛ([=O?{ҍ<'S =_Xl\^Kl W0ԍA:((CV\fuw%k܋ 0ٚKEԊh!T Prwj*  m]s%futjeW j ]ju~pBr#dZDUR2u cZbW<| {ww8B1_Z/Qa}fbr>f7Ag PY^c:/?WGEO&\OhJ7z{xCAd_YUtW(Ϥ7`x&`EޛZwb_DYVVU+M67Z\LjRd჏|kUB]v^!K{틕3E؞-ʇȶA B[ރ<=?2 _o|0 'cεw[~~H^:<<\;>q;w~'oB3s"S/XmMEx`ed(^]!BEJ*w0@u` ޞ,izbfS@ԎKdA%k`T8¼ j(t oKP1-.]' ѕSBρ, x"GV=9o $*e8QW ܮ!` A---T==< ~ZuNeuVh+`u7X(㍤Sy7,wӁ6hB귅vlY)s 5LNv`4A MY8F:ΪzV [8WcQ f1 )]pk|]80k @ZHw:؝8V6pasuus`3u777KG'pxۧp'0q>/8M+`p邃?~zo d mbkYZ^$\Z f$>C /yϼvW.>co Af }QYWe;HCZ X%7#;bFp|1Œ8ayH󚬊Pʖ8dkv|eǃbEkQ=(/WBϺ P8?H+{d y X&J0y Cw0.ݨmイ?]Hfץy~S|g#]}k4 fأ5%]:NuQspPlJ/FBњ{\GLҘ(W=Mn<GgվUޔR7@IsU3[s`*"G`ARo 8RXm7"+A;JZi}xo Qepi#il TcM"i QmKfyg/1=[̙FRqe[ hF20 2A^Hܦ#*huo{wշptH>߰w1;(K66qmx"6xgX`Ʋ ͖2;$["1ǑCF9BQ,wy!TB25m5q >%6ӵ`>6UB*t&lO#sdFC'ѿyDU-ȬX,2U"E>"J:EF1>l^-('}4,\L:k uEnJ?نP\ncCT1Nz,ji8nB5qw""᪡΃<[ 9-ҽ\YiY@PlEFjg3$)< ejݑ#F -CΘ|<&Hbbs+\ƺ>fF>Z汮{ 904K5Sf0Cbwxy|cAk9b&N8 G h K뤥ZE:ʽpt!ۗ( wn/_kJp|\6 +X$ED Vו M[l|V4&d^:˚ZܱcA m`q@ih-ZKya69Gr|$:֦d5u[><^[6 p}Zp\:8W^?;W/ޅj`okҽ>marKy9se 7cP/LR_ao>~C;`lЈ.MKӷy0ѕ:N{O|p>6DT:,rci_ y\垂(]*,[1n8z[Ά`rd1#͓0Ȝ 4eM Ƭ,CW]^Ve˜7V SB4aѕ$Ӭq4x@Io;}*Al 9Nfm$75@v xtszߧAdOas1m͎ϻ\ɡ褱NyM(9l[8Oci{QoiN2Bˆ?+W S*_?;0e-B{3s2|NRrγhaU.~` 7г&M-o"?V"f.!CeEVUQ$F_fx'KM;8V5 Iq=Q/Ȋ]eU5+m]8PN}u$-7o֕YW{_߀oܫrkkҳ- MhyjS=[ q r}tO>x$k>8#rO{'Ф׍%VkRh[( u#YT)Qܢy2$ }ӝ1&FP{%mbrHi@*̖߫ڼ*x60d5t^h}A!* 0r\@@c*Q:oō^$ ` WSDI3_`7.5+兠~RȩGWdžۣ=`zw.&?cRg[,ΰ}C 7T|3/c7F]BXwNU&=޹s;l\+o7w䬭ڮuA*D8< 6Нh}Qp9޲1]ase1e3ӸB0Qd7giڨ.DYu!\ݺqf2$o<8ME& U2 f8H[R@Z,t0&2N "N 1W< ` ?|zh:ez'CL=K5<_X[QM J`";j?~fgVmxǺ daչX[*+@u$zVL#7@ɻU4P#k}M?t4 ҊaZYVAF*5\ulFlF =+3Ft "xf`# GfVk6T]F:0"Lq\NE`8ddSzˑCS8R#eoF 6BQvYVm \:hǭ8^eSsj@RxeU`5TbŴ!ӈ\ (Kr51wUjGτUa4ϒt.!7 *9 :ju`(#[X3 j5kwƨni}I)$Ak~7. !tdbt ;u!l yyO./5 s7U~^^?߅܀>]aYq|dӹ=YjH~L:;.-xƦi㸄 \{~cOni?2 #O`{AI puW1H{A䦀qIS02yZQBV `nbJ:aL/6"[דbD@r^ΏMr&!*Ey*ZbmUsSI!hOQ+D6Et8Z.<*uXxG_{'/|I^EkTLօadӜeÙ.=t&irk?:=7$ ?@S} #*g5)>w0_\#;bʐ d"CK 9ؖ9z徨ӑo0DۯhJK}Sx R rֽj,d y ^[W'3.{(f"lU,cCQC)rNLD{VkUzPf ^nDM{@Ϗ?s+wa6Ms&4YZXϧU<<(}bxc6'?[⭧P:Hy2;7`bo"Tc'`d?%3B[}Ah(k;U~rP/TQl\Mi&.LVLXL6iR݊XB?&gH`BaFD*؇C643nG RP"Rʀ5)*$Ŕ #R#wQZP< g4D֪J8ӱFxdA&tYɪ|t؈X9jEm:0mXThA/7D3zFU]q?G͟lX/:d hnddJǝ]B7έ*MQ-hHjňBrI|gSlR1=l@O} *YY_bLNNQS"PUÙg7{Kqn¹5?2|Pďcͨ=dfxiH"qt+uXr؜Vk*BWc( 28˜NlLE gJ/clw /A%XPԚO \a QgDXu3uFZ/1yH9 v:yBKs\/m8Z׵Z `S 薳uGy|hW~jT1b =zӘbVr&F7Ksf:> {BiQMHajÚI(2C<gUM7XdKK.434Zq2r)+_A"erh4P&'P^! n) ,;EoԿiWl6cz' fK im@ i\^ ܂r^e /ӺtgřV=e)x Ivuqȶ- 0j B՚IbQyn.ߍg'MV&)V`5E 1̧†u>S6e >xtN-ube{ &uK6y, ڀ}(yߡ@teC+U;^=s~#"L 4>p~aRl݈tRmc Ic!,DX]Uwb0u|cQBG=V)1ےj V3>o1z>%=\KG[xnwD9SKA0cwޅiZg e ZGL~:kάkT[`amx"u+)|Rq3R]{H3x Ce?I\PsgwOOO9w=L7$8 {vw҄HNN|qDiԨW[FM]IoVt gYθ!=;IPߔV?ڲ PdCaCR4FNfsmcxƒlfH@N <ӌ # O+iJw(=[ˌe8 &5dйIq>7A>&Gb%-+i5UXwX[$Ők~o459Ii,)LsJ}T"1aI'l6+!|>x]rqWMngXd>ӓc8=;IϾx_mhݘ9IVJ> µ×/;"={簳Ѧ6M# ]* iU~Vvp>Fn`|ifCބsf+ eQ$]b&I"m_l&q^PIIq_M n%+*kcfg}p2U+׈!g!h}諾`kgJ>cQk,^{>ʍ47O9Dp1ox Z_sF3.Wtdv5`6y/N/.bf39:Nl? e:gᛆ߫WeQq9‘S"=TCVlw) ` Ί8VnE0$q?qmRkpTs-z~//~q~o74 ` 0gncQl`lao\^;88zxK= -[#͌-LCАԥ+iJb~KJd:a]&ݯL>nE0ba2<$J~+] Q;-2s6Md %k>@fC8zbfC;q` ^OP40 چz7U2ˢDpPց/SaO#)e=R˛;u.(h:Ε{JsٍF`0]0]DZnEW˭%k5"01+o)Ű0bQP!Evfh +2YDp&!Z *˫j94,|+ΉՇw$uZ[nծͲ@ TA*v9?C@aϤt.xrV2V X|hco_dtNO3e04˳L<3BCldAf#®wuN f 6j쏳2V:Y ̘/j0ʢ"\*@GSĻd[ Շa,SƏ6ƖSp$2t@W-mz(hT8B+ gT1F!xdMQXk!60HA2'lYl4 O:z NDzŞƏ"2n%gL^vo:ݘ̞,tltX⯏.O; hN@Y}-=4+P[|LQc,,Ԃ.V9JYVy᧯@ "ĚJ\*0QNѫ<͑/(k' w-> O>6ݭ 8a7?G|Hf!i99Oo§~|c?y flHi0gz'=6/zM/]CLܴ/~d>V1MqtlS\ufڨ_#2l腰9{db5q{:AJȔ{\lԆ ̨EÆP)Fq~?q}S[`y_!V;16"Cc\ Qak! Fژ1Vq(Ubfc?~c}P?/݄EY0!?ä ({'%(6|U+ӹKaEculzV9'@YdC9A dTLQBFH\hՂh|mEQ|]|㪦X)}͋6%[ pE?0@c˘t\ "  kC0f1!tdh"6c1/A_ƓUm1d "CFW$5,N}!= pN>r Z FWtg6;9r-~\TuBlV t&ƺ9eVF`_\ 5nƒdHnE26kCs@8!0AUG9W ʒnݸF7VU8bPf(j4bg+–>j`F$;A9Ao6uܞǭwVj`l cbW _J 7+j?w+7Ӭh;xĹns4 sǼ3L>ݾ4"@ NDא `(M*_})qXd7թ؁kck,9{+B 䱽uBͷmxen?~u; )C[KxnO 7ϱ9]Mq|3H@6@†gOYݨܐuU MgGVq8o ZoC 090.QzulO d!ȄߜaN^| Y$ppCԀϭ9;IC x6k(Ys$\ X2F:/ZR 7b.qN\MۤQ[5alvQ_g P"F-0]^>d;fxU@Mkojje"Zap3/=g3 'keKZzl تiEIH-E" L4#DjGqz͙Dd]znEXu=!X:Af7͔P+"QhYX_^W7fE:T{}xNVL=Z,ve SHoK <#~&˗6\=LT&?^zA 6>ٕۙ٧ϧ-fymRE dB)j YE*!9DPdvl`K4MsRx +{qeSLܭEV:$(';QUbL ^=F[@Xlx3qQRZˊ%3`pbL%i, VqJ5[M|~-hi%\ڥ{b&Q@ 5 5 rbtwCB:_?0y@gdk63NUsK&T#|%,tKl(h [qB\ Cu#:Ȫ5- u}ghM~- HF><۴7y׎]*~b>6Y4)=d&}>!ZzGJՒMmQ+pl{uvR} 1}۸HCGteT;c+}It{ttG?ԝV߿<S p@Q#soHlC2Vۺ2z} 'L ̈́+j(z,6 7 "V42^4#CUؐհh+L(la=e.3-R D(^Uސ,L6Ƚi`:ee!gUu>M#r3Xbm᳆PK\YxW1 dէUq6K`@*ewEtq] 4&kk@9^b%~ y4+FflՆl36ѭ90)vn)%!Xn*CZyg/B㲪%lڲ-Be-`$ yGx/ <9/U^'N쵉|́rxDAK9J/tFsQY mʨר+s5#ԛcCwjc@5e0 sǪa;44uiNts~ߜCn`{:uOQzM0P]:Y5IcPAҗhFxz?N$P!"gAGg>R oH'3\CD .UNɏRv/ǢkqQšԽjch8冷 lyRQxRM4W}/ł,- 6Qˡ0Ir ^~3ܷhq[ [x,pEuB6M_i 'Ӻ*F!h]w6tgd sD\ gWN{W.ON{yX^?B9 @Buf joLƙ ">TO*gLO2mcjϦJ\(uI,Y "O/]twxF 7jUnSmJOĕvԹ6$F+Vq6Q,m`{+Ej:(% l6&2ذ-!8 AC!`nZPq뾇(ףY+b3 }.j7bɬtEf H'lQc AlO ` 46wS`-U$'{u@:AٕĜOKڌ>CD#KtrZ,A`4[Սqe-BֱP9B@g,̹ڍ6,=vV?ά'J_eM2ɫ~,!`@l|Hc"БOÊ! Wɠoa\̤aQ: D[L5ݯ8bѬu1HΊ*FG2kv9Win=rܮmi]Tk9#i4 s{1h3~P/BОi%HӦ=KCv1Y??46Z WƢH^hFNZװqYjk6㻰\kXq{6ѵdYB|p>nh&ua[텃9:7iO^ ɺ|*gcW]6VlpuChx6gf &9yʐ͓q/z ҽg;Mb֦ǯpB {in`m<)^L7' My bs\$Qdz?<%"bo==l7o.6Q) HU-!::w{^/Ƭ|Nl%JU46X Q TK&f"!Dj"isbf|U9VeƩ`"Y/EQ1Ѥqcb ׌):Ca+:f Sc;-jGipGH{CO (d^C*p U& &&IմHaN$pijB"ȹQL ʏ ~DVץsKXwNg[k!*~JkyQ.K?Jdrg?=L &8[V<|Mog,~J2vSu౭O3lKK)NI4p$њ '+k\vۆ+Xq^Kwn ? ޹tNbk۟C9[l ;?ZQbqޗt{4g4M/ p(>ej}ib3CA,#F7D[r9ӇOR{;F)PX\4uLj o;r)N @koTY\ZZThig"v_#+<M; i~;93oEuy x麇/6xm'P%YX,`eNќ^;U8r{ÿ|{p?yMoۧy,Bʸ@`8jL4BЭBɝ})QyYK4KuF*1K`ljL\ee q-̸L y:qpszON`g2% o|3x}WwN,vdS ?DjUUΨ~Q񪚇rg-:{>oO`WZQ<>^O'2947/_򛓇~oo!17sYBڼ/9Z(hC*H^ԄUŊ) d<_GG޸z p,g(mVUd{ u73Mdݖ&)d,`n\FF`kE} |Yu` l 9O] F*3S[Эb5Bʭ܅ g jd# F8prOfc8F{~\[um$MSQ6=Ϸ=^h5*h-Be#_$4f=H(.!5U ]Q3Q\CIR=E퉃am3]'Wmƪy ;g5+fV{tWW5˶ 8fKވ3q].JۤakMY߅Aя[QٹH_g+eR2ZN6SnNFqfzC֊E+7pugXTuVaƭm^)Akl@Z*|]Fc<;o$gLɱ⦭q [^Bpg{b<)!{0B,,3W"g}ͺf7Tꋾұn7]5ƹWejr*mX/@6˂>#&= =,&|f2Z{ 9"yAd (*~զQgCc9y8_?f} ]/w,9Xn $TE nTXHN)3B81./s._ZW/q-afu[> +ȡa Yo\?476hijalrb TtmKy/- [7`#Շؙ;4myy1XE`g64a{dCWJ:)mZ~ %t jRӕCCeE9OzZ LYveݙ4>7#I}$6E#H 1J9Js_^ z:QVbQ %<94f0ˑ's )p^;L1ċsAnp^,c#% if3ЖOAH4VKe҃ /Djmuq٪)@4`s=*9RpzI`65"(Ĝ9DuG v7@ 'J.5XѲdWv5Xe9 hAO@xo×^zy4̂l^} ޳S.‡ނHVC!ؑ):q`?ZK^%{At9y6iܵi/WKBG7'KI$a+D:#mP1󬼹>E^/NHAp  L)^J|(M!đ{!U-8CaeR`f<ZR@^ "Q#&%prvcT.ٶ˕:JG4v[Ɗ9WfVQ etF^!YPEsq%=K%!]N# o=&ݻ=|ShȈ T`(4MX9c[iv& Yr3XrU1U,cU91ϕ@dH]+e[Mvkʻ RmP O'[v=vc[ӄ!uu J=^| fz9viq#wgrV-SM溄X]q"2@OEfQ} TȚ4w!p] f'77[AVEdE!{GGP?j~iNj. ƾ.`IFy~QU%uly~b@⵴q6kwQIUNAXYK(XbKP4z˟|n )\9?\ =Knu饹?:j`sͻ8`sWkU9efZekMWgT |(~ 0kowkSfmIJ?h^ބS m~5G;7|2f-36ZVഎj%KasyTVSQxsxT1|NlbSU,Sɗj//i26K5/EHCa:k _؟xny`{hjFjT-vemG5i֏?>_3m[5%H:l ||>HnlP LƎ ̤JJ'x>: pr֤ܔ<|=NS' sbrؒfHLSv@ˠy;+:Q_'b?G *#k;5'QRBNQ 23p>ŭdƢr4'I֧*MSvpd!#a/!o4n׿}^ަ; ,Ӽ> }#SxqeςzM Rð]#C}#fl/Q4f}.w;QnZӠlŲ2t=r94;{=OfL)dv8U3|EǩR *Qj(lk1ݤfL$9 0p .zdiQ*|cUkJ'ًCR놌]sst#d&l2Ҩ"{kƑkJ#pޖo7Ͷi<;oޤ1fҗZ̘ؕ~0;qT:03rpR@6V7vx#n?q.l-d~ս՜eRrl~ ]LX+0zNDs_1Ma5l|5_{cl;F뜉+闕ǕHWzӚz6VKu}N4DHIS/i||Cx̏o*u!U{;WOXwGU>8\.u=~K_z]/@|[=KT[ Aw;\_],oA=7#e̸0M8%,s^N٠1%eZT͸8fJv;9`A,4ig`_y3 ZEb"VJ14H2hVe$$Yk@cFu cmD2,&uJo:h5I_y`CզEG@fxexb9P䈹܃+zuty:g{7.t2D>z.@I>+b}n D|ESRRQek{IH:eHh a:lI1 uT9S4&Ǭ*N@"Ҧ>s>^bXk+b:77ȅ5 BNv{׶bu&nq|o<ܸ HK 6{ Aƚ䝻=qMY 䧏7``7VC3 V֊msrI4ڏ MzizF/|gDԁ폈 ?~e/lϤA}Ֆc{e#0t2Ţ%5Į7G{h> /˙8'Kt pp8 } :*iCh:rCQ >E<f&*2 +lU.G!h)YJ&xmŎ֗ܗ֗6t5OGs15kiҺshNB(TXAW&sѺ{:y G XT9ܠS="ͦΩa9`dY!2CJ`WlҘ cD|[Jñis*Z\i $ZC!QR|!3Aq j7h-% ddPcPݼh'=Op\Y[wތfb4ʪf4S h+P[N\LG> 0Y4c%Q!F*RXtPΌKBޮX鍉VBQKlaXkO]L1|FKx |Gg0p5wFsspC_,W' ˙L}՞XY:`ۧ|C8N{.{𾯏o>b'ልL4Muv*s (l]^4AX@7sox; y~(SpRW.;TgvGIK|HzCpmޗYZeF$8{8-qƖ9TC ddXl|Q ۩狛NX_8@w>0NsYJVfEzknbm#8ՍF2A^ Zi}~EL1eV$lihm`JnDdXewʳ>rm21D@p,~DDŽ&m^0wc¯/`Ӽ U>49^ʺvS{*jS8.ǁsPqjxtw6.gMes}~P}f N -c㭤r #[Q{ay -am- Y`Gi 1lhz 6 xЂҎN?G^ʮعmoHp#m^sV <&8ʀ3=-Bn6z~տ 0˱edfT&cJ Qv.#1g:fmm2/ek{Y:2xZRgUY׀Cӻ5ȵBT;*suٺV[#32Vj33(Ď9[)r2 wXE0-4J!yjFNۯ6iwUP]JqęHi9GTg{Y6H%)s}w 7\WoW`۬nmjiud4Nx 僳E,^,|]8X;Nc 3[]\`UR'9R9/e;]DHP;Q; :,:)uj[&׎ĜEAQ@E(C00q3eFmXǰٖ>F V#TGRbIS#hMȶUTWH$Zy_Ϝ|=r-zit6pemJr.d_b&3߸~a;{I1[6$3e˹ܞ(de%T-''X)ƁJ`Շ|n'~H~HhAP;m)Qpʪ g룪PT!H a!j=U%.oPzy$yw,Hk,~ n{\,J}sj=Mu,M96+_M /^݆9#>=YW?~s7ex p4V"g:J:hL,`Wlu-d Adv~>3O<G^?˰w8Kt?{i\l}8Ku`nV 8~>hث5p($MQ&o#jA˱T9M%d" D)Xb9GX7bctfԇT-M/kߋb@ЀDV톚S5B!G< UkJ&FO%g /6K:f3"6 c籒 |vO݀ٹEn5o1M bbl0,k>v8* Lq5>#J'~, йҞ9<tXv=QLp͵Uְ}]xsk;H{pQRʙ}VU,]y6|]16'#n/&jXVOLK@o_yVCZbUYQ56z<p62|q . Q/29UrQ$=mY5=; `Yʾ,[DnZgl y^u^n&og'J+[9b);ű[q4#Q)|nF蚷$NlOm SsPf ŨY6bC\Wy!2 6'CuxWV+/ݟXZ֮Wo1!_Pe'[J^WV\||揖;~mo4!!|h'L{FNp9Z?(2idTMĪ&U[{#fmH-yUUB%xeEo6%\xK4aASۋQ&j'q]N>#2 ÷o&g& sƅPy?~&=ڍ~pGw^_Y=1ѵGs8ظEG<X2+YMjjX9C> EٮJ,"&T&D)bUe8) YiuU:eGӐ4x96&nq,z K^BIfbtzH j+kXrڃ h%JǺi)WO:E@M#̔x0SnPcj6pn_F*JA3k n\-,Y}l\&=Cz2蘛Fp7Z ZbͱA1I-|*FBhPbK=Eذ͒:L afs֗箬׿x eq)wNBwS~#Ï>~<";g0_SHlѺ"B͎F^ + ܕU4- =?7k_%OuZ\FSP\.W;:{_wXTs=GGG}/TCvIXЫ7My!M/row[{>/"`O< U2ဒUZez@U2 u)[.,nZ8$o%+o1΢Oк~}($SAyW42 7[- i ` Xȇ =svܸ6+Y /el=k~@mh A^ 8+ =xƶmGV ;=f4&x xQIrl5G 줛n/!@w`E"1B{a!Vqm L|LQIBV:^kظ2kCe^$%k%h$-IMً<\$eEgQ>٭r>ո!Ӏ`R6,+8#nZ+V_Tj-"n4}OmT6Ye P<0Q%wFPv]a,l?[F5@q+=m|3K[#tO yl`\WEX*]w g#64a 66Aa(ߨNgì2E+0G&l3`SGЩD(u&0v `#$ݮQ|m@o<z65pn+C]T㼮FH9Q8UgH {_Dbƒ [_Ck1mTQg3Ueܾth1My^?sBs=|syr}-LQ"\΍vٮ_+w ccŦHs<0U lYx\0}-Twvj[q5]<oxpw%;J6טSfk, iW-4e%hk!9rM<DVGg]jvB};܍oCy}돀-; L,<73s6fGXnE@ֲ1ƊbsrYLz@ZE3{5V#=V]is9 (uzj?Հid=gs|~g7]Zvfp#]wS:C6OQ}rŒ1Yn0ws1SBάM))FtѠ vT61]١jm(: Ee} Y((gq3xFh5j~ޏ0ZfXh})\8Iظ1S^~:|#:JL8W-'O8j5N9Ouϙ0ӯ27B.J\!^hjN2񢈋zϘM"r]xۣ{7GW߹>z $xx;:Yl']u&XXX6b)G L\Sd[X~AY92WIyfŇɺY+$O4ӫb/+tZ~matT 9*֠{V^QM&;YLOpgE#k\~j 6=ܼ̳E*>1fp|2nllS;pUPsqʈ=<]~?wmx<LMBQ"Trvp 1yZpP)X#I4S{J곂%Gύ1g7mP,\ ſ?zT6Eo 85PZ޻G\zg9by]ك9-KDeW)Ը z(Ŋ#;>^"59vyʵ&E-;a+;nּl2Qރi#JE2F3l>L5jF٢$t1@( 2§^}Hm&rMXEMgS=N_*$OX m(9~ɎScX%h D@Z fq((_/$l'mrF)a|EW"+֨n)|=B.e|139g!-Բ˷3lƈMPU-MM R<6/gGy_eGJ$_np3݊(?4 +4ކ+?f-' ]Q|Y ina\k24y܎biFauj=&H`Y5զ*ȭ., ';?Mgz_C]:u'cGZȿ ZXkܮa'"} kQy}Edv3E74E=es͉'6L4v| i #Pؾ4 6P+\g6q` 3 4U,Vq))ރSpnʢ\)P3h^{LT\gCAD4S҄3#Ο88Hǿt>-wi; ,J~dܼ3f`9T%·.j5oNGjBZZ O@?Fi02 /r7AZd[7]~|T ifۃXrL, W>f73o j-?}gwfp ox\3~,DiT5 F5{HSpf9yk1 ^1lT(y~of9IQ23uMe OT>56v (WNA Za7y6JXVIܳ~*A !? iuAr\PMhtqbq7u-9쑚Fh+fVA} l7RoaJ_PTPI1Tan{S Yewi,{+exᤗ{VS 2B2ݢS㣪}Ǫ8LX1.k6E8jt^αO!ߨWDž $a7Ž l"LۣH 8}.P5ϡ{lgT3L W${^=f6apNV1뵮yVhG*s~<:՟{k_vNl8_zW= CpRBvz&U*w,Ci.g.l~k1xHN1n%ذh3g!r$*ڨZ ;A* ܯ/ j?gw{jb4-o˙PK+Xɵch A`&!49V h({ :^S덉2M4rvNK)z} q{>O?^6nI7qϥ$F^J~}p}7hNCi*nYSO6m}>%6kMXQ>@S-ܓeSPj{rmؗy e`m7DiRfM+q Ċ#pR ۍ,q^BԨ4)L^xggy{8* &bˑaIf8Ç fSEk\cJ-/FSd ǜCfdJ~El7eY0-J8OM-ʪlffG Vk rޚVT EWG-A1]=۩(_Qe^%41$H8bݳQ Uva0 +?Âwϭl}* QKdk|jׅ~bڭ"F9)ݳ賊5+'BeG?*6l^e,&x8T"1 #ςgf&Br \BS.s!LXv}~މ9*[iAQʏqfNGX؆\e1i@WMn bT& ADOh=yk3vyMs Q<'L, N 0<c,ZJ m`Ju%c!ky}b%Je%-RkbW6P] ļi :a̶nnrˀ.Kد 4` 2EsJDLwFkĉ# i@U4g~ms8ߧpq\^glu^Ag/!~ h3jLe9gMCgp(Tox$GX QtZf֌,CEN 9,PslM}Υ|^Ѳ̒j||%"ԒQ6>ۆI EŖǂyʖvn΀Scq/J3@x t̍~#Daۦ1g_}v5֕?F%5\?zpu CWi} nlUp/ft]o =(ugB6 9*ĝED-Ų-7CuiJ}4v{ݒ}BFr`f׶N54͎TQt`hcKJD{Aq?<~}&+1v8[̡5~ /l;s2y]6uZ0*$J!mkW1F% SS}>_xfۇl;bUX᳟~sIϝI՗_zjsoLa-#1I-MA4K0aNJ#H$h9Ro4Ջ1I"U%~ ZU$Wͨ0'M6jyUig(%[C'r=De(&A|"֍#yl4pǀN! Nf#J&Gk.Vvl*KE:UKfwE"}G^ :IQ<-e4F%e XT@ +mRBk^A ,ޑga̹d<|@4Țfٮ|PEDTLĦ'7Yqb-8My^ye\NL𛜌6[#9UG3JcKU?բ!}1ߥ'e˳lFLl2̯m>qJkvmSnoGSadb)VgAg^ů/fo.9E-ˍYOnѺR />xt4yK{P@%OAV22Ӎk-R Pr010€ZA Vtիh Yܙϵ2]N֡m2ǚ$JiQ{_zՍ;giP:o@&KHmE epF2Su`̠CsTQTceET$X`^i  LQvw2qu3*2xګ;<,Pm  8 ˙aaѱݜѩR"\QU56 4@6<jBg-9ΥZ?t~˃^Rm2K ߾ _{y>?g`a^fߟGytVu`ET62wn7Ϩy*Tb-3 ¦γ)*l9ѭZ搵5,kN{T@殒w?Qd:&| ;}ui9ZK"+^Mԫl 7bJ k7?Eg |[ihptuMXHN>cPOlrM!ZfwSe3ΆC]MTrZU m y !=Ml>j:px֐{v;KFuŷ{8jT\jZ _a"մT`›lk+v|o+Rc[j+% 3D!hv{@Ͻ}Cx}/R>=}Ji](}G~jz/w|.ŒB،ڈ5vEZj6-oRd^o̥=Yy[ІK{7) Fcb=>j,>obɅI]ٷ \,RPpl  Ҡrm@-@Ք6" bGG `_UA4//ģ @Ĺua NFm7顑ä#Y|A cwvnZg`-![ʢ%k(7$~TvYt 4F=LC9 lzm >V!rHa>*[mɇ-3vؼae[ ~'|e^1db=+2VQU`kXL?n:r1b'bn⽨ jF*͊%m``rsuePSmY9ǜQ].uNCFn z bۖT4wxqqC}!gegP2HE BQ5 g* PTgۘcQͳA`sA ՚Qiozo DeAtZơƒx`S3 dgc;s9f]GULiqeU  [evl6`ɣrIubiJܘBbUS79/  EkK!\Aþ*Sq)9"97^*J ֎֠ʹ͹:׹՗U"%×o c>?_7?< .PG3vSow-Tsx`d>,Q`i*cuB(-2q@Pt~ͬ\S붬Xi5f~?>4dq5\w2VwRt/ͣ.3Uի[҉ܽ2h)|'wԹ2+`tř g>Sz 2/L#BԺ=X aOq2ϓsyhd)Z|&N L]!HlcxlEVuKs0JəV﻾}J˜*k]~nоe@AMUhW ruy={yO|6p{aٱX2Ě}y22R%YmgZVήJm~9n: q:P܇N_>[~{e\l25/vu <c W߷l5i6w8|&W~dokN^:&W߾z_ꔦtD4!_Ҧ",U^C1C@UMOMFY|y4;P;d9_Gc?[,u%Zv[;W0!bȘ}zU|1/ccA[[ZH^[#6Ly))+ly&.5NsE:WAC6*R 2Ȯ5́%Cǁ<qSd$+ E3tU4!vVܬՖ:r&"q(2qGѥ%[4oy:[+pcViʎf$Cn&4( ͣ .L/xq.Nlsp"pRSLm& RUPVᨊ=ܾF\w vTp!Ԍ9gV'*,\;Vho}vUkv(HU=8pz̵B}~KGp.yQwCl FPl??? 颴y)熥{s]H;S{_{_/p3U=.\j`OGBZ p_>雺M?O3f{>- EYf]f|@3Llz;䝰ŀiyg- L(@8!@HF h)r xѿs ZG_c@;ijG[cƃ|c}]>_, +)t9Qa (@S29ViXRh?-$l !n#*uȖa4s:=VxNS@lAVD[;]S|:_mh%fkCl(5rVOc綎;!Ӑ=V\ Qhvnt0 [2ZHJF <){E`Mj-vVM710j)k (Y3i%(Vc'kWBԩeS0s&j4sj7(Ƭچ1h{|!%VMͻ=IchBi8&S):z+a$ qd 9 ّK]M5 2y&(UxmxRTᡐT/tW= `9ؖBkFJ*I$ <{}ڃ<@Pא|.ت$}=za#*&Y6L [(Jj ӆ9 2WNvy,f7#Plՠ(qB9Ţ(M}2CcRrPL[ˊMq̡_$[;mm}x&u6olu+}JxBw`$Vp/aL3%v/՟E `M:UO"9;W܆6@yxS <._^R79Dڅi \FIj7|܌2+ԏctY>9kv19E:I?(k=.SNuV<5oqO5p}k 8շQA&tkWCjQhf_j#ʓI1);gjKj`WQh`;AQ0hi oĶl82@Ө4ǔ|kR,T(^? 8%KvK10FkV9G‹2̮aUwY 0؄ZsWWWWfK,xDVZD+DN7?58ю.۷%gy9F͝Cfm %ƋH4z3>;'W}x W{O g/}LUuKWjNOșV g_̻b% >"J!@Q(y7<޳08ckNwb]èo?Kk<-N*ё@^&m,:\(M1.qK894ccm[*Zwұ=$VBa$o8pp;Y6ÀO/k]Q4V8Onk:ݱW;vja'__~;=o.߹ _O kjf?Os^_сɤxKƘF1:Lަa½P js%+֍ wiǗ/y۰ݹH%DN-.ao6(E) JF Xku}N ؓ~I^IJJ7<-wON=۟Nϼo#?_hG )nCxd zh#7Pzy,V[x}j .Hi":W] RS֊XDY-[}MB/96Mp팼*Zfm6ieCO5LQ08vjx hDpA8NtqrQY}MXN@Q-k)oյ4zuA*833@*"1-4e3d{6g e0FɟMc{9~lj)Adib6?0Mhzd .t\j#U齷)g q;4 uNxM Jɏ5[& !@_^W;: 8jrYnZUr!ƍh2l.\8ZFa{LNe {{bkgFNn XNշfC K,񽣀0gdK4;#UTvfu&`Mr i^/^S֪rf D78yhrpAy″K-@2F6fs :6j[ɼLnFnt\G o+ho9Ӂ*L816*ug1Ld 9SXTw3K&ukKjn%ʜJXx'ͽ_5O dMTon fLuYNrsNUZ9H%+}<||cTPYp{+jDrM53G̗^['r#5@`vlM VV(_6 6,6n0n0<u/Ncqa TJwi[=P24iTj՚ԂN, m r{ب͟ϊAOϸ 3ǫk!3*y_cڸT!ׅIr‚GTiul*E VYƃj_t4ä5Ypi"%7,(5e.9uH- Z!@qGc9Dd? pF_&->o)J5j[DB.=?y\P3( QckX]f"E~ϹVlc}:>WV;_ LfpDkx܎ 1 >'42Ycd NruKbl]QkS%큟ڛ2W7;P [e5uZ=4ΝJXVVG.<3cT,‡?27+ͯGxB-8AYņk O#%ǖ\UTQɛ~Nv`Gı|k<+Pu=z'/3<'9P'vzf&p՗n߾> +_tQxYY^Gp63vЙcEX2}5R;q6;qMUS-)LƎm30h @Ұم _,NDM ::0S2N-"jhe ܆Z642ABtL`&`6skF\ WnP>")hw@ӝtV%\+8a9y 2<ԒKkSԃZ QjmC=\;Rgs~*d bxr:Gظq]96sx G>(Ph4HVYYҭؒ`|. VSR 0׀cʸu\,D}T6 2s!!R:>ICSkB_V[NF. b{()@1 l{J`o旐8 ب6-<DMrUaUÌ`ش#IEkH60<$hO:r͂#r넏iS!і8VEP&`UӐ B*;`hVACZ~wo6N?6$Xq5nGalSH$"x,@/7ܚ)ef YOئ3c c!/zRԍh( &ԭ=5j nƢB 6ex + _!c,k6x`Qn56~.žOdҐY5翶-xzf[t57M?n?u-ʦra0/uAP KW9]Uz\KX0-w>'moڃ*[e8%(vIHX 1H4ؚdHF; 9[kZG'C~;8*#3[ԅ[~}ks 'po/}2ʜ򱰡҆^ufWتŊ 6D(VfȊõ+;R{~Goo=6GJ^طh?H?ssnqsCp &*t%O%}ZF-̗, !0pXctYm,@mdcTc:Nz9i3zqQyTc~zXp *k;'mGE0"/8ӳ;'6A,[s=qU%z[EIOpw7`ƀF}!XgBhX(Zk%pl'Y(*_773bbEٲ :҅&.f3m <'?q Mzi n9FSA1hCtO_|(@sxb9 s Mv@QՠWۙJ*E9bEBƙ5CТ}6^nb߽l&L4^=N"HQ˛mvQ̄|z;Q>wVkJF:zDt MC`zT9zKP9]Hf=nhDKm9TٲU y5o!& {%`besNek31SPۨ6*Q`^x`]C){ E;b…:dG8i W>T&ELSj}Yh+9 |voAdZS`cKXJz9fkysoF|z fO'sH%]ddm& QLcӸN*0k+x=_>YPZW̆7ٮ a;[\:C.| \1{уڷi,7r#kٶcl=B&*lߐs.pw)99 7Y.X~Ӡ*1lF]ǙA ጝ8EQ5y3] so5K7nPYrn:UMa\;.˳WUQz2: ?ÍQ<W9c bmX޿A,*j#:tk3Pp3gt:)|w% ˿&tӠS%_T!i\IYjz`$zu!Z,8ZQ,T a5ᠵ^\G{'x8;_Swsg.kXk bDMGwPAhB*͞c&wPX1NkֺLԈ6cA_ -iU 6/z05i3)<uqx0k.fPUQ *2]0 +FX#9Z%[c &C#qZY]Kgn /*rjݴWed\ExǴa«|kǙ3x;A6l\LJ 8ax,#(2X [*PEDӍu%!X=IzQiUqb_'VqN17'4(zTkWj9s$@s;T Mߟ vn{ޱ sÌLSgMMTΩ|T˶Bt',qj';][kDh:\}uKt,}=/|߾v|S3zr[{\+a!;bxp@wya{V'^ipn豱L"EׄF 6fVw,cN- mNzrTX[8*g&},ega㬁>76Zfx9}]-{cZ{ifkO( +z#YЫoMs 1E3nbW4U@m=l8ZB~9:n6&&Ρgcf1*|%VDUЇZq;g-:*bɀA \q75E1뙔`IL(YNd#(2w@`iSpÓ< M;@vX۞ b m5<(x妈68;Uz(*ж.rt\@qMNP aωm].B4E v&$m,1]{Q9ED4 Q/Qr$cXKO+<|&|6G-w|eiMͽ7` %Zd9uݫ]\^=|,?k_:e@ՠΙ<5SM-X:g޻tsFZz ﻎ= `B6X}gu Xu4D:7㹜6oH $C>< 8IyTS ){/?=LIԶ<SP4u>d}tb<E/Ԥ,Tw!AV$F(>E2VJUCE̽f ,`{Өx}kb^Nz?S@KcŊK]ׄ<7^>޻-ko{|<|=G :4"vǹUZlX-fh οy#K;ɖY<_xG-_~Oy4Fq B|r_~Mp*ԕK8V*W=gb Jo.XF*Q(aPlYQzco` [`UZckzKO ZèBjx7'o]XrK_|Z8u@'Y\,ΒJsC镈^=:tޅКɩΨnt=w`~=ßz݇l$> :?qkvƉ,/}4؂9RF,zQ eMqN怵mxPtMgYְfL]ј{Å3Q6҂X(xhmS.7`PìMձC R\|1kxQ,WD@xʱ9!*ZOeTW8 r6fҋM f^j/Ê@ݛ ط㪟, wb ^uS>Sil@]R|59/(CY5-=yOM٩O< ![QρBqn^E+, 0XP61!i~ď<.Fxz 6ǡH`8GEyQZգaJN`F!c.5w|ʹ"^ϻWϪ@nC!HbVwp1`/eX  8d@sL턔QbU[y c.2b)ہʽ3+ w~i^O'$뤍s[:")fk粴zQ`Qy6) CMe&( nl H&0[\q3n`e*adO׭VnuWnhӶdz;)hK:- ν}Tz+.Z8{9.-_wQ h3w`Ɛv. e 5fa;yܿvO!K %;؂u/:۱ѢT:)!ea!HnNhqO ͑XI>xCR#$pG/bv"n!S#r{鵪\w܊WKb`$5Ut !/YbmGQP.H +آs1i>8ɶ%x/cxFɎbW;I!S憔z7 ^P턜kϧ6[hi &-j]$`ɴmQ/F a7J TN ΐ2XX k3K<1ĞQls wOR`(ej'Nb'UAڿ53ƹBJ/z%Xur;, U|-k Š?\(d;[kt, O x&pVNX-C*?l)P]_ Tu"]+(Lԗ1G&ur %όE9A/B/6LZKDJ4[֖OEPlf *.+yp@,7~+vXBzxKGNj_8|{_|aqC[cv -dmŞl/C] +?:HV;Sٞ+G\C󋏣CIxfvz&ߺ^)\5LP@*Ktv4R9:뫁{)A&,?Gr^$`Ƙ_!Cxu]`5dbq}6ȱ$gmrhHL%=Iy)c:elmz=:؝(@{I3Vordn%L$YANY6@@'>c;;@ϝ|_\~w{C`t]w%|r<xmNj#v d;Rx\n>E8b/[wP#3>' BF㠠Ec)7`ܔcA(dgtͰ oU6ٲe< 4aESVV= `LIJqcU0M.k*@'3=i2Rr{\-X__\R)oNVX~vE -PeT:Լ$P&eTg4ى% L@id]b3x\ `شB$s2L 7(cRF3N&|$?MJe4&Ń5 \10@D*ݸP]+܇h)7Zx&ɓ3Gpw9kq'o-_lm eu:=\K,4@vWgwZ{vao˧ZYsCf{bR.L7Y=<<%NmFMŊ=13K_FKR3Izveޚ}w<+0dtX9'#s=H1Gdj4Y¾{(S92c85kcsbkdGbm׺r*Ujڦ)0:ִўFZU4ɫPRmĜQQ[1x-_}yF|hٓh@@xln KyaWt,(39fFtm af3 XSۦʹƬCMA-t@…{+ƨ X)XNJSYG] \i0n)mۦ|Zͫ[F-4-{{uc~{vY9Lm~KĬ#ұOX2PX@`Ll|Z#%"*`uQh3vTkRmX%f yc^AWVԠ)P =1a?}Jk]|W!)k&TMNvntqvVHy=DZ~=Zp=<҅sz@qD,^}:|ǰ9Dx>S+7m_轣rǹb*ljYŒ&')ƽ\ǠmBప2cIlna})K kbVrMq?{85/}wӸq.yNN_VǕ>Z%jJmي *Ej..?HC3~R]lS bm8O~|tsG?ފ?|>Nu-_wnl>OޤCLA~v'fek#,ZkuXK\Z/rxK7Vjs3$8F[ ak1OaR̅eV!XJESIZ" `.CioT.@`KvO6KoY|<kX˄CFfAxQ`qQP 2)fe;cXr*:du F3nC@=%FiF*3FE8<>1Mpar3.-TB~(ꧬrI _LjS36e 9GXrUVա,`dC[BqR*-1$Mrd"6ce2` .tY:h ٰbHa,ZpE͕F+jEsKYLlmP>cd Zە*/R_XFzÊf0S-qYfv :UaNt!Py%{Q˜LސR1@ANv`ITUhxtA`r1ӥCs 6EE` ;\eV2xqPfW7%q! bɁ 'cq)9Ae,iRTs$Q)J_&tٺMCmy=tsR pۍAn'窟 Osf1޼w`}Cs[j;LIw'!y||pw[x<5o]W/Z}GҺPT gM}tH<\:+WXuCK2z"?'&({ޱwh˝j]rEN Ln[dEX!pKʺaBR3S~=Q+þ;^tϡdӐDi{Ȗkib";]:r9phyɔP4)}?a`ljic)- -؈]#hcB3F%|iLkU_ aF@&wш%OwՊ7n/jw4u1(!ݲ/x{a4uqP㜯4ڠ9s#SA%wċ't^ΐBXP^N6i,u.+9 $0/k DC4Fܤs(@[P٢r2_sLRre=d"&[{ clnA)^n ޻{rˈ`%Εq S%!7klѧ+D4ZWߦO29]AOU~{l9$뀷i:Z9MGzGt~zo={q^)5q>uŢKVK% Rkڗ6/s2OmTPj6;9P)11e佭a gZ{L>W!Xdy^ZEa6L\ ̣鞦ר j+`R ef^P,KUC-BD7[8ꍃca/~M`2stNop|4ӧ[feXsyνu /MCSV(t nz┝pxӿ 'RT}ACCPm]{^URfmrj='hY*B:$ŵj)8UVes~a U"=jRso+FRj! 2Pr\+6%K056.=u<'Oqv밅/668S]pTWYA;SGB;TrgRy0QMw=w(Wz…EE[㯦MӌϤS:õ=jLT RExD7K_>\*ƒ'rsĄfBο&m{+hCC+G# ł@'~`f}`C,y@C!2:i ଺bXcLNYBGȋ`̜6LhzeQ B<[q-ÈO6b+v~d[P5xP]hkіCZ `K6:j65Hvm~]Fa0̙ `2WL^P*01WkA HuzS,& IdD@/od$>gpeMi/k-?1;sx(@ UO &TR*^AbAWwFT1 0KrͼVB" lFlaR6z윈Tzi drvjIx}pViNrX#Ja+[g#^G)S,4V0u|G%Ji՟N: 53XLMu|$?0 %fa:p#pf 3X*83g[ŭ`vVf t|%W)GC|z\?2 [QyF<5 GR/l;dm  @/Br{Sxs.{b&+y€l8 a۫k0= P6a.O?QsS-:c]/|9{(܄xyLc@>4)zt}DlEFϵ ̂dE'MLȎvKYM-3 2^A&JMjP 9 !TL]i!S$.BU6J^*ˬ<1!eY/Y x7TrT sʯ!F2]OI, Hf >x/#uYѸfy"rQdQQDs`~`{8/,% ՇRBLQI5t<;'e*Jnmp|P X|aZVes3b3 mam{3^uZd\.?3'`*9q0@Х|F?C/OP*ݔ4R@3گ:_Uz\Sp-5QTGNl?q<8;=|wĶI ujS)3@"M=8! ҾyRӷ򜍢2tf B9xUkV7 &qFІxX7e3⹼xfijfJ8_9JHaR(ƾ '*o |YͫzGki:u=e{pzv [k=wb]&Вj㜻zq<eo P`(^hǂq9uO0<Ԅ*`?O!TYe/}60c;!ۃ:%p(cZg 闹̳ 5 sƞ J%qhVn$h5< n,|5eW&7}{w x`a9*0͑2USڔ mهe+j(eFV:! c7M;|1{bLU0̼ ֊jM* 65)@q4}B@ܿQx 5 +l`AP۸H[w\&W5;Icݔ+$:.734/] d!)#{woX@m$WQ?X1_$_doA Z۟oܷKV; (clOZxXyЧI\~sGn`gƓbTQSrR̡&`;<;zhơ\'UcR,6iKMjcwz<$C=аb6jC*zÙOaÑm<77نxާ6m!muKrxCDUiRyjxÜMC[;8t\ DpҔL"jt>R4}hlJXѓ_G u)髊4Y61lلL[6Fe[QY mx5(?+ݔ̉IzI^|8yKFAɫ![ V6 ILnaX,Cwo+GYvmΨ!H@ʊD3Yu$%LxcnJ&{/91'5VSƕ6@6'2ZdXkd'F9E9![v4[m-WCB"h-vUh?St58w׾{%^4c~N[3yjqRf.6Wq4*j)K[}I6Vn;/v*4WٹW?NϧߴNA^3 =%TSxcSo.1QQ7ykp)ع޼K땒甮11$Kv)u<+x$ @"e˜V6}H^>~X{^%6j㲚"57 8_,%3ȶpz0r)5-HR`m/ZUIK1w\{-a,1@UD!We:S H5~:MP8'+8\|#OO߽;S67Pݼysw>l6{rlj+qH#;;do[\|'6m<ԝlHN{kh^aa,| 5R[JҔ)rXM,,ĺIqEsZX0\n+R\`4J`}cAulӲ";cTUKP/ Wn&[]V+I^NQ,m-8d' ) 4G ܘFl1Fr=f;aޓUCT."*EށA eMTJ9L2ЌQ"*?؂)yqcILIJjFEɱ٧O}|qX"\᫕le * 5фkbTe[Y̠ ؕ!\V1aZt)UPaX*+rA5'mJ9X˥E=ZjkKDžP'ΡG=[)9(sD/6i Ul` PM)f#ͣ%OMVM?c~ܔ]& mf}nMEWB^d2y+ \gNtu9[{^2`1&7eu|Z O`DE4HTuڇfP\B'z K<_YVaI\zqXýsƳj@Mv`0: 5PmY9ɓlצr}鹗?_ܻ;I מv ls ͵1|`s qixؽӸ(U9<7!_!j.Ţg;¦9@d,5)wXcqu4?/z|%}{CЗn޼yh6Snp8F}+&o|֭5}`:?z]yՈJÎb4b6Ū*ɳ f[7f0W%0ѨKT6ZroAT\84 α(ʟk}BUd[F9XO]Y@d'j2GtRnФjfQ8`y[cjg!lS< ā}RY@}UġMd:7sF m?d[Ι ͜=j[}|ù=]kU, (oqڪ x5kKx}ֲ)5[I< /@vI߫J oˊ/3ێEaH6v;?#elo/decá}\=~2,)Z|y>:Va0kA`[U!k֏y &LLcEb>J̓Sp8n~4d J0`FUt3]~כ-8(~ MAW,x/;ϗ `och^3႙SL5ƚzTj @v3;ş  YݵBOfh+ CXb9&jU ¹~ {Xf|11z-'߈8)ǹm\]ԇe;:VB6. a0wY zo`ccp\۟ܽ&&0ڿc'MɈ=yq÷~x _D[3gA?MB)hoxgrsTEW{Hy]Kơ_~z{pt뜳[UҨ[NꉅsHѓ> YAѦq=:t-YHڸf$ƿ8ژ:3ZKMCoqyj2mprIy:3ɾPmmF.7!Rn/@f9EٞҸNn}YFUoBݽQ:,1 nL*,ن:5|ڿ I2q/]OXWR(pΕjR TqMtnb9+o#cw'т tl>P+rzvV! ;@ X YåƎCڨL6͖mh 5}vzpq\xjW%5Ѵt xFL]=~cu3zmT`+D^Tb^Ky,KhdD HKNs@8&xaL.kt'&00֎˰Dy~3ktgS;-, KֶxKu!5J=%y_k %Bv]$犥DjQy.WK#F@oogyww;_{>`U};/\D\̈́.Ch~fuk&̖5aZ{*u_zМ^s3T5ІsRaZ,=ʤU4COzkU5]Ƨp Sf#UAeDl#Ȯqv 0P1s'} )>`XuaXJH;?(Kg^vS@#, [ ?#` {ja T:UQ3ejue@XC42UUy#lh&.R{mB:=?t DžY,zujs@F\dLi173q+F-@&ʁfގ38+$wj+!('obϒxv(mxhʞ0@VcldVuR/|xݬűEj.Rp7Ys%]0 aRV)yS w乨*cU8:<s `_%Hh9eR`y?7m&y3h٧ "Z'[u(@\̖|el.SDp0KHkzXY)4+d;ӡas~`k3&48/=~--m:B 4wFN)qZ0oA *>|n\KvIT_/yumfg ag<8u}t ?=N@)y_'K2|[?ZuȁSjEnpyHU5IV5q_xz:qBPsjƻ:F.3)!y2~'${2Fl65x3a~-۾M6əvzF|-.Ұd$nEj,Plx 8{2S^Xa}'K:21 7d;Բ%wF-ϯl*{)=MS&s P)KpF!|լvQ!TPAyz|`suTׇYls`?8/oK=ًÙ{4.Q,C8lMC<(ͦ£ ~@'%fMqvbE5zoU!dHgIkT]'12n%eLaX0(yvjI]C(HXf 0jK` Q?_׎ⶴtm)¾oo]k}V \ձkS1[%:)XPήCQr:Qɕ%CO ЬEjB_|&|sm:8= /|^|9G T y.Xt!=6Anz[Bb:2mhV ee&]Ԏ(l|{Ozo+?:CΡR`[Ǐ9jtPYL jAj!aEFkJci^~3z^=wM,rotK}prc'udRN/s5l:j k)lƢ3I#/ڕ#ޙ]'&q~jZjcJ.ڢp/uv%3YB_?[?04>%ZCRdQwW=C :8H!Ȱ P_@2yv'LcAxeX]^;}RSSl@\{ [u]Y:R(Ҝ5WUOvVPx^<<>?xgq{?/ͣb ggQl]8NL73_ޚ?EƆCEWNn+?5ZH6`RC J7nS0iV13~J I7̢z0 0f~X&j#VŲ3RtKj.ہC,4Q0) =-r͹؆g?Xpbb8{m0`J|Zά ى;>OP=-ՠ T㦶dOgwmPj2QQN"Ʌb!I NghR׋=( ێOlnwaʹGuN0YMRW8 ׸y\|\"vAX;&CFq<['ʭfץ3%iÒ2'gx]3c0pK(ybsaxYȰAYW7F@nRuP(|p`0VהyhdɀJ+5# h 5CЪ`ZaVVqpBC~(_ޅTV/eAA;E 5M\0/: aiZ*PΌ(U;sT/VA=lmvF s5<% ]N4-gȼܠ\ @SMeS5{ytK 䙎 +s/ 7sv&G*܅Y4^| <͟[>hX핀eNj.]͵*^wZ;g:;i(Fs%x4{MY!6->569C `s͍gRJ- cz_iskBl݂/<]{i7qS'f=W$/NmzGT]oިi:5HnR=[3 %e+XUˁዅ[(zB!N2_WEArɊf >8{Q8Lr & >,UQ_&"bY2^(RiOKo /?Wʭ piR4𩏝_k` 5:E"w'P2c2CȢ:=Ɇ`}5_>`Ȩλ4pN5tS쾔0yQ(]!`x*_|o.Dլ (oJmmBS+ҷU!Ma 9EА9f>p10sekc; KDE,~`k81v-w氱oqΜ⢤`}_l3 W]\L:<[%gnlLA =;Ν. ̞gy_І,~y|v]7Fk㵭'p̗[w8*ƮsRvb X`&`62>pn#43 5꺒3{0e Ɨ9kf/Π>nˠO0452|/ X`=W,-HDkC# ;8vI * ='ۣ CȌoΊ"nDvmVQ\!gP6Pb|2yXl+3+&7j: *`=1T^+I`̹-?3.᎞Cр^+@Yd5' 2X|Rm,;=u gLRC5/]|Ͷ9a`L&UCOeq!H=b)2Y␝E,8*[i_4т|jN| -a@x9 mQJ{@{:=z.Zdy\F* :iJ@$+}2T P E)$4{5 RP\Z;^V3y,Ul?"(`S2-3CUBȒO<{G;{A`X%cT÷9X>?XF!/weD\?1؃V+v\0-xVZxx2elS~5p4|{kݕHt G7w?<&bڅ=t <ϵFfp'# J$ q;8ރ] 5qiэMo· XtqْF|⹇ue-dE!ØlZD2?8HǓ2i!O RzYsUfvsYߞ?;7Mj2~O>4i? K\.7\ NQ4JjrP6#rS_ƭVWB4S+iZ+^PPl֝:>KsjVXw:eB `zp~| ӵ&:׮]Sx38%W\s&0fsOR  EY2M  Jr be6z@Q5M 5jaUOӊ#74=r f(*r/9!tM-L aK}# U!(we+%RyZU7,ۡ]=\괳_U{kG3b'juM ɢ@ O8+(8SJaVcZAdU; -سP|=Vm訲Jdj&O f[dXιF{4TRޕEXgE~y6@ E 3BuZ*K-\_u!,yFA(ARɫ.gRT0/ǰ1[AA>)ȓ/T󒂅 od!)Ie M;*p8`Ң pXٿ3ZS#ϫ- 3TeZ&;3tn*<tFcqc%aHC.bMGضv@ELLG㯝ǬpѦN>.0~X Nj)LǐؘIu X'(7-h5󤁯̸E ܷ P`ƴ(-_[#8%>A:jTӡw̞7.-Dl2dxM ]i)X8efs46^DMR'k5 Zi:q5*'c HjӮi@:7C?KEAbM;cge6LM8k7ݞmAmc9N`Ru^S%8>q&_.;Jg;g,~0ۂs$OiIcX-Ȁ Oms ۲F4MY5Ҙ̛dX몲,IzmsiUg\\2jڑA0t = t  lczzpb߳uk|q:F[i]Sk<W`"r|UjP90$Tmk*"_^6>.&#8w>p3.2Ҙ[9<~v >C@Ys5Tdc"~#[vxLZe6YSYg[k;44.477pt~ Ƥ9j^<C|Ȟm|v f]*wL 9ӟ;;@ϝ?#:>>{ETѲ}oL7Ťmf8 ;Đ( kL@AZ"8*y.Q$*?,p@0gV݂ʨ4]YWLcO߉5bDe{0sFU(0Hzb u}d ZZ UeA(@(rYHr5*f( ge;Ȧ4&C%Y I?L`F}3hޗTB㐲xQXhKC X=9a2xeL ƪpmMmܲ[ܤ1cHNn*LQ#\,"2CX pP40VMXU R˄p;`#lt|3栥jG>Ȫ3^-qZd3DV,jB6XANtЊ2:!>eki oImaL[6B1ؔ,ʙa6Zo*@f2 k F,Br2H7C FdAC&rA3$IY%-mdpgk̩lNMnSԄ$Yy?-ećޓRd>ٴ}j$!)@7K]~4C#ac$Ŷs`6yS=ZlϳSvc /.ͯm)2Q[t{>-;|}bm6@XQӒ6 9?^bV®R6Fsb$٧5ŞWGMY9j$͚isb KE᪹mJO.Ymrƃ$QR]VKU>lY~6d‹VB:g$^I!QqIVsk׮߂\XYgpT{\7*RP߁Mq i:kJ:WE] ; _D69jȍdeذ^2N7֦ =m[0g0CE2 dVebs{LsQ+#ƉʛG|ju_}/§,(GW;P`wdPA _9d;4t¨U#yP\t_?n^>F]s'O;xi[S@d*%SLOWe[ȹbCF;^jOM(uQE紆9ԕc"7Vi.D^)眎^28W;9BB(~IP9ݯ>TjjaIc5T|ل.ԤXVEFQH? "?%p%n\o~d q2c]N5cHŖpdעhX1Nl<@[p#pmcW*G~v\3wpu8G4>IR瞃+* ܿ|euaY!YmYQшYo~@\e{f HՙZZ?ald.gIvz擻tjA[zo*|w7cQgv{m|.i"ϲ&ԙ$HR7CjsB{^ çw?;;@ϝB˗;::@/8Ї::.:.QӍ`z5oI A )\7RkZ*`a(AN]J` +7jaQdi,I:ض F /Q땠kzCs P d plCdug-*6UQy#) I79ƖoDMgtdݙűAhd6R[;n?Hhа,H-̘050XUvB ,jYmBʝd/AEƘNs 5ڱ^3p/ӡty33jkWh%+p#}TJ*k$d(ػ0a `wUѪR "Z`aip[hfZP|e3X2iH.3Z4P[)\U r2&ij8TI#{̷Ȯ->e#F\ݤk"'YY e˲ƨXn_#NɀLS=WaM0 qʜY46` P&XХjk FS^` fv \r\ \10b 7^,`5բ_znM|vX|{@t0kaPͭX 9f0Ѯ?Mf_b< Ok&ܝZNkv^V6w'D<~ّܠo|G_Əϼ|]kOoW7)wM6mԀ% 8w@nf(,/e;4k~ozOBMkFZ?x߁Nqqns85܈-AUZ.rX+NPꍳA!^EAJ6. А4#QeAs@h1=c1_ [;q|"@]:ZθgҒ6w֒% RNOPEj'!|<; HtߐF&Ћ7lSĚmw #>ٳmsٶyظ"P* +˿A+nbզc{H߾o]&"PNhX+G ]j7m4oV  dWY9C g[EPz9,vӺvl~lUuK9<.)'OҨ{7|bwx?s&{NiNđ!_<= 6>366]Sɹs+^jvݦ5t'rŢ-d5O*l{o,cuH^_Q,!W"#6KJݘ+.2T{ L,W.ק\q{ ]pfoT-{ur-rv{ <݈"M> ̉5ަ 8-8Ҽc:OώӛUcу/6C,d2^"[tǠ[]ca&.5ڽQ' ZPl릮.<4K%AJ@3 `ek*K ( @:(;T=v( kfsԆ1$) 3`Y*?0)+ʾ .! F`^ FW!;pY$;^歸^`}Ѩ7Va_]Tk&j JS5va\VLF>:/CYp$llAss ijݷ {/\< ~j3{okuϹ7V*"E%Z5-e $ylrbw;l?t t7?LH`8wڃf(QERECsY{>9@'juWg&5ӔL .oc_{ V! OkSM_x{=l֪\H9 ןwׇָ̒Nlp=e0Eɓ@,C(9kr^;T6m,o w:}N2D{ dԉk=b6mL kmSu8ʡB|VTeTѿɗ6>vdVKsHkrFmJ^&u$ZU9kF_!&Ngc us=l}@Hd4klqf?Jm f;u>T~%xݰXjd no_xɄ/$5mul )Lm k5) lXӌˏA&r8Rxuie6% Ug4GJ"W&u`H6lXZ=#WYֆpnL^RhSQXad|E2LЙJBXSbRY=#*;QŠ冘-7y{6#UȜ`l*r ],~վ-@ EY!ziyf^29}ڂ>a~ 9 EZ/bY7u6 'f\g[N\o~pͰ3ƾH^'[i++QYȅ&eԅNۘI_axyIi a 7ۆ rpQO dagm&lL#vQt&VATm,yeWݿ٪J%jIfJ5BIS\ޭ߳MWH%;bؚ%+4auEINVV3Gs;ozޚx\ pˍ tkeN}SU,ȽJD<ꢂ;o]5gsB᜴a]wKw| .rtrjϜ`ʞΰ^۸oq8z?x?DP<~ċ'Mgȣsx<]ѤT1v&;6 #5O4QN/2Qbmh9*5f &J_fDGrp_d l C,WM9~r/{H"O6Z8ug[r yF0NN#7z;,φOYH;B]*'@ODnc`mr;Hz]\,ulK}rF.Xə@v႒hTf`(:m``[>SVeDhC!J嗽mȠ,<ǶnƠZUacqݎS 9;K0nm+SUCpᄋ{N)%Km= 3NL1.TLf' 0tNyXҰp6&_CQڅ1@)+}+EJ J檮*|W[1+kO*3 ]B7c~W=d*f[uR/-W8l.PfP=R9UO†J>+*:!6,7"pTT>9M ^Dק.uL@uYcV cgp=]l*ro|Ye /bPϷ,ۨi)(#kT|sB'{8J.B((͛yU1bx{J"um%,YY^etQUzF3|Iujm4>+S--4lRP2f(+ᰨv𾟸>O άhCߟC_ۃ[Cl[L OcYuOLAo&NM*KkaSH[; !V\)3I)ЂzoɯhK!ڍϤʊpE6=I}2|X$).]!Yepi]1K1A&f(deQeS c1)]Xx/JRQ3*g򘸯5zdU~e`C+^tiLYa|a/[Pu$bKٮe]u}bKƉSMYA,s2*Ž-Œ BT)߽ߖamZ/%9(yFQ(:+ƾJr$6cϣf(1fbf mdXmu=Z<3Qx}ŒӌtL7p?{z| wy=+Wd6>`5 BҺPqO>kD͵ }&8?Wp @xRWd?u64)gɵE61<JF*}Pvѽ=I<h%.6~d *x 4k2Sd5-Um+^]]%5҈H#tvkTB*6sU(&VYпs^[޹w  {0oܶUq~zIסLm&ĜL0u7k)ni7Jug?ُ]pb\Zܞg /mv:۸nOgߋʹ 17kk2ŀ{pey޸.}Q*COpnaഒ>45gJ 8Jg^e MdLM#۰&WXidImLHYՍmB$}L`ǝ7o'0l€#T #Z^>4ZGpa8)ۜXZ˜GfM$ YKDQrz O[ĊpKAT j],d+Cj W9:+$TC3,: 0XiHm(%56 OnoBOI _b Y{ HZI &N ik[6p=.AAh.Xh4`3HAvXPz@uuT&3(;hJYMݰf 3r| QQu-:͚]5++2JB_QPanHk8aԖ<tG֦#%sيpU5i># =(1$i Td YW=;GH* OԖȵkVpyZZ+T!sZ<+vUhz):5ecMAam#I* k/bgܐ1ƑFV<88x:|j،X]yA(svc,ͭa7+ slh0>z̯"^~ox]Rx|q#-:ʂ^|e W:[-ɹXoT>Q5tӔs& ߯阬bxp4_"r =T-\Q6ut9iX-ŲeRʹ!ݯ߀wupS\9x^{,lVA<ɠ,s Aœ-8v$CFw4R[yGVh-CsFU*%0Kl) g5m KJ.QPce!TQ< JңP˭U]P~7Ǟ>/|y.S gv_mgـpfO",IHǰj4q&+}urMfR}FUAӗbh&1r/Z!RX'4O4bǃڿ%$UMvJ*.R)6> P:ujg/&-%^k`q9n@0{FN ف崇/^kG|?s;y5-~8ޥ.} ][flˁjktl!O,cQe񳌥 r0Z%`0nffv<=9@5SS5sjIUG1ؠ;&oηny`3W$L-$2K>oka~][a+8#l29ɪžӲЁjX?U+[g v޸__z?~O=Cp]g) 0_n~sn~{[6_pxa%/C@?RA2"ǜ'Jш5xAgB> P}g&`g8)HdLEPXI5ȱca_&(L%4XXa3_83#yiqTEcU\:A /r o_jd>5+r硘e"zj3l@΁J̇yI C^!^ UjP' (>e3keVeh`_4 TJ mz\X"gT, 4I>ƌL[y&#ٺyUtc@bU5T)7m\bL#!6<)-ܖL H#p]aAh -W77߯Ƿ9{3HnjV);AqԖTz+txy)\i.Vtvm'qb)G!=g!5M/DׇjeD@OjW[Yi 2v95?X斪XǨV egse8XVTesǃa6T~ERp{ÜbYn\x=vMyG6XT =@]mj2~搾cx*.UxKz77Rڏ;ݝڙ4%a? W_~n?<JPӶ0>0$|crlS;\`c&=A{Fƌ7v}E'wmQ%K7Ҡl{R&;ُdUut,y?K1T*0$ LaA4󅼷dҴv^f5b$wɊ` J}>|؎:C*>W2 !NV6;R 4j+<m|7ڤH;KWLk_s{ٰy8.+QRtZ2e* ,9BRnQVBRP,q@%>]ÛS7l>6Ǿ}wΆb4n wI{,S@H VJn_cܡT ߕ@( i%#|2#mIɥVn ȘѶֶĉ-c^%pRĦ e ~ZQnwbPΟu4 z_| |}mXr[]:y:?{+v3ឭs JW^я%nTc -Wబ*򱟠`ke,_'#|| TCS򗬞h*nÊ[Upܬ}9AW3HDj0 (777Ø^U\={1|׻u?'.\|~# 5C~.voٺ_ixgo BfcMe,\lY.c5?r\ѩFS 7 8RZl.R$2@)a p(ANLMMLGUq郓g 1R)nZ!R Ia0JV :kS|x=j Բ{jV:(KI-Ai3 !K:,*ԫY'u):%*l2 ƥIƶU=]He)5c _%_"Ӡ"KN*5Cj6N'I4:43nN*$t̒Z-cSWH0R6X6w.tR䬛>\2, f!8.X< c[4~AR;VYX% +mQm&m VX.W cM[6?k4 ڽ*< :-1 h^YO%{v7>7R=\'n: {(^ o b,0UST ckϵRDI@X9,RbCrR2/D:i7 .jIj{ޚGAdc@(@e06EkwpsUа2Q |/_yݰьԆܩ)n^;pZRļ2ױ s82`G1 Vf 7Yd4*v2ŵuWR '沿wPh 0-3<ǰ><4#7а'7F#l k#CNҒ raͱN-Eqk(;-P^_JN9$)8g:5EqЭlAK!:YR$݈h*9L,V Ԋ\`A (OTVn00F=w.L`:y.wp |]8ۤLP_(~t&V$@Ml۫ 0[ANsN?[[HWk l|oX|6lbmh<1PbcY.czM}IO5acyv*CŸm<ѭ2CVؾXMuֵh L2KEr?~ jb8Eٚ#9eLZR%u!jkWd3f'v ~杛Ǯs/-݄}*U jHX6ɓ䠀F./ն>I?=?X{w}Q|;kN vp%֠?UJ Zrڰ0-S2Պ)mYJ ֖r~<3` PeWzY6lRQ+Iml+2r8)jBʔ9k`b9( I XCqY@qq6ףD?꜊M *D_(ZxUUUx2} `-r*8|?b0ys 'Y̽$vpW?6>ݧrc=w6 WpxfW;GxktN0o.hڦ%Y۽yχ[jO)/&VE`T}Ӣ1hQnjN7UL+1i1-KGp')'k4S`Hr_ -xeޣZY}&, <)K<+fmN@lӔuKhBHq2yqp:> @#|U=g~ 74.GTr3*vz{+㋜;dW؂Z kOsO :T OkO"ܻ9>A3c5c#ܡ,t4f'Y4t:w{@shb ~ և1o7VD+XBeO& I `Bhgg( Ei`UE+T\$]yݑsΊY^LN9'fz#ĜK%<'3k`%TrxI Sz)IQRZDMGAXDHmĀY, mEvduF<&BQ;jX޲xtN- Q!LR40k^4+ m#Z# QĖ_l:Yn*W6xPmV@#zeP$̪&RBGj RuZsz(+YdHM5d׿vGWդٴ3Sa$kЉl!q/VlY03c7#l3Fq{:O(dy8!Thyt`t47g?Gó7yޣWqy;g׮] fca..%U?À5-=Ӭ9/wl¡0Hxb-u2b]w酂:Odjn1<6؉XYat+>o6S!Dy^>:u>[ 2W Rc{'f+tf{SQK~p^^+B$k:׵2@Cxr*C>wΥ4=|6s^ Mϔi 6 {ϱI> (L yMj X18&6)63{LA>62Ǜ@T6&  v9{n#r-wòaXHEy{޵m)6(>K6,٬>';!_6UU;r-gU6jFs&Mlpv#j # m*"̃b*{DB.<(E9dd. Tag+L9;qo2eXtW1#x (V{p`D-B]&[HIxsmݾ+r ?C,U_S>(7dMbZ[#3`j 6!啄P]j5hN דر@ź0%Mv9 x"PVT㷀{|+*Ppzll=|lo7v}<3;*)Y+7*N4ٜ7Qmn` j^OXԾSVOk"ɣp,+  L-hV~F6{M7\Vk4}UxU%8L`//؍pvs2LunBH3|kNZo4cs49) -82/M :~L9G, Y0*%+ePWK'Z>B28w9OF6q+kp SMoDH{_E!g^qǔO.إcܯMɰdd/ꥪ;I-%Vn:z_^'?'c3i@{S X܀!Pb%W&ì*(Z׫JJ8z}J֑pS07EEj](}{}X kš+W;w 6|gqQ@ RVqWቯvw@Q$svn=ǟ\zR0֥h1˗s3=Q.`Siz=s1bf}9 Ya!ⴲ?̄?(l*QY[~AY&\(SE][=!g8l`1XY;sz۬61;?'Ҫ;\:axYNM4ͮ* f<@_m6lm*"4pB^\_W\+{}ѯ?v 3yq?>88mmm P [7ɟ>68LO(c6┬ɟlUcsx(5BXe9AyIp7 PK 0g 3rX<τrr@<(J'.e߭raTBUX V(/znAX  *m +0+1?cUm<߽#Mk9xrHNQ5 \)r@,@HnCU4F$U2Uitٳ|As53* ř_]?FJ1Z;:`^ؒOޠ|WTBr\ڑSY\R9SYWeLq4Laq6g&%Xf^i(1v8n ^U4Cj4U**d ~خ--st*\6<;mGJ&{l> Wo ^\%TUEFꝕI ^_qօx^MYe,A5"Pq>20 m- t$'s|+@a7sƌm"L׼&sf6»NJrD=I rPWc!mZI!_Y MhCU lUD^<:X_p|?ooX_ߊk^Z.?~/޹pR3gJ/ ?s-ӚbwVp!µJSlܤBJ{/(*hLPq/Df@IL.qC>T3 SoG礼IHE,[[$E3lX$N>Ko ~9$GRb w8Ds6A&;ȈДo]֔s@rV,]4NZ$6ڊ'˷XQUScTQ/9.6dM r+\ECl,pr9/1uI XhJ^D,d`[Xͱ'>#LιɠPhh cIsZ.Tz-q11|pRWI^]|k37Fۧi%dr4ٶ9)s!?CY Xz#9?nDVto Yv*v#Sd輑@@}tͲ<atkm2QO 6BR9kog/ }k*6vh :n,l#Ob2!%V|/ik1fESktndB j)iKHa1\q(>֮Iv 뱬 [.`2RJXfSFp{8wC wÒx0'eպM;Ve<.h<2 ecy(yCv;D2r۬,(jn>du+ۿsa PqR '');9I |uBQVQp0\5P 7PfaޏۘKX^Seg~[iAMTYH0Xgٗ"l}+-7'טFKtcyVݕNXbxm=V{ԩEu7 y=4-DVW w<+p'o[2`:g}GCZZtHlݽv${ɗ꣪( gF$ + |ZbFcJqj%O&DG᭩Vquqp>Maw/}ͥ}C0]\eʢZye5MIvȀf] x_GWү=G{_n揯\F;?KV /ɖak?tO,W w-LF+vQm\Sje69g8އx;,_SD'[X8&B;ۊ̕/Kџ噄.!{|f@(t܇>C7k:uN+yHpu.nTZ3E=S3r 8 +{=z=6&fpe:0}̋CgY`ؐ%`be?@VBޝmV4 P,j(,Y T8Ti˔24nL$W R}iE#Fb}<@ @U -(Qʯ+ I|Dyx!!cl'ڰ hXHRp0w l,YS}6܉!cl|;hc~|68 U@jQ+2ր#)s6mĿ3d04N8jC+r 8! 8g"zGE!ã=JmWۉDʈ+$6K1*Ʌ*fo"lm 2},ߨ+PU22'0|+ # 45eBh hՂ*vvXK:-`aVr Hc%O|y?ӷe떗ڶ{O[w1սJP.'<ґ,Cm:!*73vm r\׬O?ߏ_$ 5vY`0n͚۴+A-O̮X;#Onf~{ms}nʑ᜝&t9k6)rI?S`[(ˤhzA. c5YSBAz4"pDFﵑZ(JnǼGulo6Ok@EŝI[|cªeǒs*3d?TJ8st0(Acq^hHkYyf'dPcVPlm o\ S9]露bDЏ.3ߍЮ#l?_|^iR9FgL-BBU:Dv_!K o~![!K\0: -)Hљ S &q;(_ӼM (4Χ|Jβ}LY@xУW[ЬL,s.^Y~{Uq6Zχk0x !|L2@h@\Z1f saȎT;ن\ <\ڬA)+5{ u%WTA\2۔ t<&5ñk8g6;W/38t h|',4@tR,T; ( bAjIf;h[@e_HK?n睬xNodV)7Ou߻ځyhSҏ"dٜO==JJ zsJo6T[S4k-)&s{AJ\.l}u7~F8$ôUCW9T4"L֔d` !+/шMM> mJ ֌ <z*wFΆ;2⚌pڋ!Kf@ 9H#^q!yWX)9W_}dD~LS}? te`Gf! jRk%ҢPJPŠ\ʔq75Ւi>mczU_ PZՊp>f&%ݝPQpˬ: qy=JnQY4%;gVA~yQ ?jM@+pTl' rUVH@V[Q]xΌ"\П/> ;8*34wˮes^.㧒4&*LJ ZU *FR 8Te Pe D.W("""U굑g`QՙpB~#m`U'7f #E\POYEZ5YS хR#aVBN68by<&uk؄uy>0HRc֫i]7J'B9@-؉p8X_o |tJ^q0C=VՀQVtr]JP)*wɖjjIQm 1Uߴ8_s5wG7c btfP;s2&ag`6PN^Ǣ7 ݧ^N"OTĈ[(@)}^l';R=ԗ&1Ʌkd?u[cI\N~쓧-9^\[®ڿ!g^d܆K`ȅDi 1 z{*o_%\i F)|d*$%ړ`˯g;%˸k@IRcې*yX į3㹄+k>NѨK Vlhbߐ-<ڑD=vkXXY|\tn YmFI -6N-MR4nvnr?@IMr=Zujʻܘmmc.A?x~OM٣\ F1M)WdpOX-2I+rr&QOn+ZMSx5pyi?z n&Y :oi:mt2L!h~&Y}Zg5ZEW<,`LP$ 1ך&GԚV@^]ĖI"{lJ'3ИCH ߞCå 4<0/^\/{gf{Y07f4L GH@lNm#! (%~fBP;-3 i,񀘁mhCɉ}_,f]V@΃/ުBtg{׆qpĈ)c|>3VLvO5}kߢXU=eB&QHH)bw$ERkD,>M (q+l,DRs"H<׈Ֆ9cyLQ"A RV_SMοVN^up_[ׯ%?sDDfT˩iejF<Fjv(zUӱ\Dw/< c*J*[CN/n:{ၳbzLșh+s\GUYB:^I l40%K2^%s}'H^(Uh>YHulrX˗*2Bg Bs.NJY9a; 8,>;RYGd1C~弍P?,Mu0 y=sJ&䤘i ߑ Gs8+eۀ?rR0o\I=הl@o/m΁IvA#!5 !˴ P}=g'(9Ge%G$s’] pnsѿQۚlEody=\<V \6@?\tzY'].RuwVmC5*g&s^L;rKZBnS~]eh;/?{ /$ďQK#F-@)|lf%zSLRH)= lː0QeR 0ßö`>#},^?2SBd)aGQpwkX͛ٱ1%Eyӱ1spԋ{˩6fSF(߹^7Ν mwɝ|e~w[r X+Ҷ!jQXF~\VTS`kC_Cw٦dzݲ'jTY,̼BOhMj;7r :RQ"!Z}Pf_-Ң9]U3NMNo88G VD?\n?:.Bي.j,\˨QuJ%[ŘXBrP>-/ y:+Oa{&Y!0XG9jʒUjP2UFÚou^o&u".^Ch?}^{.Œv p{7Mc/6ur,8va!mC j2Wa%`ߗX b5S'`);^BT5OZU{--LAށưpp|~>>ll@3܄N2AW ޔ1bV̱TZՊ36"3rY)iܔF$H'Z_)%r7Ƭ$Kskm NMU3T|_Ef]*36̋=/ [peg?9 Ӥg̤UfK=TDXАY,y| "|e C{ Nװuǿ|0}v}:-gpjsho9jO`VX2=I/ȹ`z?WaQ":@YPۺs/mLv=h}rz{=<,Ã[B:Š jU{z1A-B >:\uoP[9):B}~h_eyX$-.Xm%]QS<~>8{ޯ|箻xr~]s7a6ny>c=}_kgX -yN3o O}TAP3a9A+*?pK(kBJliMyɻ ~2O_,A8fO5",msW\[}.W)f8NX$@eP)lCW+V 7wFmaDQlSY8zȫ+3eM'1[EE -"iV+0 n/ϗpC5`RɱgB$5)()4p['{)PQ:F&蠁Ͻ)gMuc0A32Ljjo03QA@~'JJk1EguիY3UԍA*SSO|lO`w3OxǛ6>2RVӨd5=#m>FDYY>ۢ6Eoj>SLh 'j1)rjGEY)E#׶S+OzJ}-^lm A@.JNCR@0ۖIұ ,IPSG7&ٳg~sMqy1_>ܰXl7o;&Lf /|=F a #"{ (hnSj7<Xk;xSp߅%h_~밹=5G9h5H|zWĦF|nOÙ%[R7fL>%ʬ4+ [AeuV!LKmV%6}>>? NX'%=4BK"`'v{cu'p-0o^^g7X5{Zm])Tע!0^x[~]_z?>?>C]tb۹ ߔ8C//vݲNgֶN I{z:Yhڸ%K9 zE'ȧ4d[%'t<8WȌNm}Xp%Wk;e ⃭mOa]1R/}= H LPIj='X=vJ(Zg`5T3P2/3}߳Jk1O;*PJ1j48P*TQtd+ɴBv> 'YVQܖÒ# dU'+֒ybC PAl͔˗O v6~.Ǧ<^gy(5*{;D.7 IC HaU]~TZBqfNH)AB?>&̶ 1ZzxpƖm cw?IT<lM-.4 U׬wF*`ƌ}&yPV㕪<1m2^5:^?O:B -S `nY<9ϫڸ8&vm啁*FՈf oZĿItܘMz:otE5Jx9H O;=l)1:#PŨF V8읍<{ygGs Vvqڶhu!et:0fLRf_ tuCJ4g-ue8>o z)jVۊ1]?,%>,\~ItD|TՓcoj)Gbpl PmkF#[ QUfk%}2\({l/ ЩQj9"(dPo7Gx^ػ-xL5I&"oA߲FdF7 XNWҐuy99a{3Ú8 /w'7lm |y } xS2pj.ױ@UVW?х eL= dDmV~jm[RQY_i plۦT,)qDrt=byϓNpJoF[r lPerDt$<|; kK?/b!AAr{ag*S*GYZ,S;z+|/]_z?-x]z/r9CH*l &}]7ͶN^ާ)V+%TXX q=*Ĵwj~ܡuRp0/dA46/>@ fj0?zk 5H4\;>.0ղ9M.3)  {T+?O-xQ=f{PebPԫ c.5w5ıRIDҨ\ѵz :үu_` ھ6N]Tp޵. };ګ lhUdIي=WGڼ/^$ uX-Uv( ʟF=vbXBM/3Ne WM1P9*p jO<ٽA^s i+4WjC8s+q@5V*@w:2B[}~D82J(չ^^V */|SB s+Hev'# }tvwR^̊/ý?|6|p6<=8w4QǤT|^]gIaeˣ4y=WMj nSTYr9猚fTR3LC.7 3Y.4I# -+{SҳࡧBGO=zu| ~}^z%`2i\Ջ0LUblf(8_]T&el 5&S{˹xde;SPnB+chr#P_*;)gz ]^Z[_8MwX~LU|kmHImѬ>r4(oNYd@XVE/9 HmR߫Ugvo[mx\WNT{icQv=ZYהHڋt=krбH-Jv4WSpk3׹x?̿3;=iXg-*OTA3H(nQyfcIVmHz HY g%)y<:gWEx+(M˞ 4Pɯ"NTeXMeUS~ID=!J|V}Mi>VZqmIc+* bc} t:Xo\z?-yۏGyW~ٷ]=wât{~|rӶ7Ofo#ChR3G!HiؿoCެ1fy?ܛ+j RRcd@-ѣ#4a=Q .=(pr }vo<WtXX sP` yX/T&U77c !Upr蝹ɪ"PQ "X!4:vul qx @O4C bGu0Ot*UU @0Ǫ "ܨ.;1!C,=xr M!bF^j5 lN{a#Ŏ->Qk^YTjXM9O6==ϚlcS)֣SN[a:ƒJ 낹ߜC#f4WZlm7Xwr!w$6끊 `;;"% \44UA*rcU^R\ލQ7+Ec`̔i_T+|4\7)C ۛ[[X*g6dkoen)燷>_YmTNv*%~y4ZQP H!o;N}lc}6=&ɔՇ?;?틇 ì .WA^;#0[mE dpNa$[ .چ0_=Mb1dI}v{UA)cZ7o:dzd㠪?mx4t[pYՓrzC.>Η: qXCjitSͥ0?8K TԆrtm3хxϳ%)]ke^?ieu>~gL@L  EQb)"eٲ(rJb9rRRةr,+*ʕJXDtP#@ @t=gZ{%K/ w߽M2%k/o@!#&.[4FM_PB_<gT5pv נ0% L PMkzk\bm4q9'd5ԙ<6r58|uHp̐jEn[.>^ԹH>n[3֧fOƓP^z|i ꐉ|y;F#FNl&sn95x>|#о-C[m«-#GO8e%h%pq`=5q>y3l;{U|,=cSnm$J}Zd(l^eXb{Qv\>BDڀ*ߥ'1)M XR۶a 5&PPTy'BZ%dT>ժ0C]]߭gau#L1iYVJBЋ'@jA<˚JC _>xc{枥7UxnQUy:kfr13^ks&,(0 chlVV* 5+vXZET4pY(ݠ|#[XdZE˒?f9i-0KK`^JittmCW^d΀DJgsrP76qFÚ 0tNUBX6jTzq8\0*l(m.YTSU:R*{wƹV۸~>#Kyؒ_ݍovN•eg wrW̹:sx\(ja~ ﹦w|S$%) 8$<"É{G ̗>oMs7!p{w$}hvV1QMa@-1sl*ŕ㜟9lƿgUMPrJK" EY"1Yhel$b@4<((:kRV}P@̣6L".ZHu5,\Gmk 6\N]^BKu9DŽH׆Q7q|ǹWPL'39d4F*Qr)*.Jm\%ϧ`79 ΄La$@ 3uL *n*`lT\'8L2N4wф٪n|!P3) [XbfLpB E*xJlY@C&~(N|$ǭiᑷϽk K8`—ŗpQy)e&gJm:M$7h알bR!a(yR{U-7;%>a6K ͆[Iφ}M/v|,9gg6sq3mT; tR[E/硠PsqB&L8GgQɕ 9@&̢t n@C|X Ofz=U!rR}fL'r]ZIjާnnByQ‚,bktM *Xmrjk 4 bc5A@Y hr,jbPYU2lrbCV jDos6jjAhnTqI² 8WxjieG(%VYܝR+ب1`Vˑ VbB5\ɼm*@R"8 ,!zd7UAs u`1|]f2⦱D#6 isy x j f1">m_XgJ yoxMw)VU EL4GAj\Mk4vyNLk0iudTdaQ\(d;zSgs&[ʵy ʀ\?`,8pb֪^%.!4,hS+#pgd;mDen-]:짾v+ܽ}N]1CX_l?{@fK^H'߅/%G/8/(' >1Pc&H \],je"X}9(4_,UpA i[ sC)$\:U; $*0Қbxrn[eSp=?RR`V& M;K_zj>}X.x|l{81Ï|=7m3:Yh!2 nEpd3aq։apK\TVFߡ!i"zy; [qb ȾRRxWe:ҽόlaHPN/Bz|K؛`/_1za͚ҝF1Vm(Y4a(8ߓ(YbxTas{±Rn7FYپy]ᶐI)K4ݠ0:ņiDHlu*[ox<SpfxpĽz P[@9ceA Ms MNX`3H@sp^;1=rbo|÷_8޺ '(FN^jt`hӑ˕mT9=/3LTd) U2z 2@'LׯTcoSb@Q [48:4A;(ߍ$>i/Vc*d]2S+^Q7[n_>} eNJ 9mzSn#}!U85\+Qoa-nDw=+ׁSa7g,La5߃͓;ozɃ˧Z۝ߟ\]yƠI{kd3hV(XT} +)GBE_ ;RvAK!T@S# ΀@!(w B ۸98oLPQXl#eDy^HR΃igT@R'vV، @T&آ.B6 p ĕB=elNWQ(,@\6T)F5rMT @|5VMi?V)[RrLmԲ!,fnUZ+V6BXP%g,6]d&)3c?sL[ty1#קʡf~d&-N0u1UD o0Sƌ[1rŔXf,:!qX} tdV/@s L3.5|ȑb;u 矕UlX\kZJ Ր q d&ۣ;f1hޥR|m?=1|%)3,9x%Np @o9W)T{5H>[>i`E ٫bnsqDV#D )6orOFt0r|{߶\,X@&};'N%X@eD♗ {fbM:(2FI7KI|5>>6o f;ˇaҵ1}˜ HP! QViʩ!V"DI)LA! gTdއT1JQ=9k-G>DP@GlϞ_އ mӶJVCwm'׆\0cpӻ5dÙ|U֧;?",d=bF$|?8?[ mΨ.\H[:dǤcǞ;mRV09jٕV窰N0w [8}r2̇z<{tXHx'Nhk(X$DDG9+ڪF yއT &ܞK^mœ^r}xlqr)wΛ,)u%,T4%\J-8䬄sgEt6 V Z6_&Co W k/a9S&frk0X\ō*-T̙y*"!tag6Lw.cTa;~GW`mg;6gp^12LX:뎜 F4lXƟuDUdsQTy,dLtZ Uu>~ 29\%@=?C!yV51$lL^+ȀTI$HP &N =p[ ͢J5 lA=+y6zO28{x@.ҽ#K^\|=_Wq7|{<{[ZЌ)v8&[&'^?[bAMfăQF6K[ySdr$O4IO $9K l}B"Զ2M(LHe: d[벤 Yl OY6e7`Zs"P螂wɡ)K+:͋-Zzb`@ B}˅|&f_2V @@RR(2lUPNd9A.v (@IK{7*QPN0Y]&ə@A%-ހ.i$Ȳkr,?*1f~\q'*`.X+c18ӊ,7-o {H0=.zlUA &Tt3Pm7PeXR}Xa)B}13kX$' /FRCWNDM+ZI* |&3@TCsH6%𣳌7op]|1̌Kkkl/KOt"C i&QP[7$ݣeAüc{P ZBjMR5 :m ;7ZjzV줗BS܁q>إgW;[ڿw~bh)6W?O|W~p` k_?K|7;udTW4yWu,8; Eǐ`gWi϶{G:Zǔ1_}T74pDP,c;(FK ><3PàN1|!6h{vmo /-C pp|D:Yt )L@!UP+ #5q9Bk`lŤ.s8Hȼ˙Ip 4g[!Nrtx=l .:y-kC Hkl ]UraW*rʋa+>eM^yyyqvH&x+zB( EҷUHlEP5|gWu[\zy6Q(_oN@hxl nXGa:lywc p:&[*SRՅ,!uЯ[|I늞Vn0:z厵 ƹBH4A(UTR*ysҽ~Pa26){A]EVvk4ia<>=5W[vt+URuSXEV`CՈ5&e7y” :!(S؉{x<Lp9C }#,vT=>+t_ `3Yjjʊ-p@@}|ʝR+ >2PVđl3%v ިBHK-%*Y֩C7|SE3gJ%¼6'Qqe2~ .[\g!. QkmȇVnҍ'gu^;;G?ze:s}x_?|˗o`wqt;?]Kw Ki9on\aՖ-Y&jfܴYqz) SeUoet02`q|>*5-66bq3[Qv{Pk'R@уwyg`2C﫾eozs7/g}}G|a_Po8}}__>uzr1U/?~}?_ԧ> z)(cqe ZZkBC»{^B~^~ h Ye߬VQ(PD2#fc35#*|P,؂gTMֳS>un>9ck>laVˡNC{G EƳؼ4`iyJܯ[-\4)uz3o ϠvHIk+qɃ2:X4Z@E(_8_Yه/ṓny ij=3ؙÜUa`Pq]@p k<Buաɚ"-XcLJjdr8tSRGeK*,HI)ComIOEI-f3ess}CdgP H' vLºTyqG>vh>u_0gOZBZyk93{rΐ7_0*AP5Ph| N K0.Hm c[7bD9V}L㓢4[[%)! boHE#39րfѼbc:9s~8;8H@M:;z<>Ӆ~|NnOe>iGýO.Ï.\_z?Oqcϣ>z㗾߼|򇏎6Axpoڭw98ya~ [n2{fwAҌy&66&LH2cǍK %TX5Tob24 \hp3)Zg XQl|0p[E>dyck.93\T;M )P`-r*u jOڽٻj&SYadTdX){G27~`+4*:kWY ʕUˆeJUo ZzZU(*mbEvemQ]7W ExrdIeLVSj oD+A"erCz)i@1ka #ZY qmveF Z Wi ĨetKf/|1kK sY]hl[6+"[ԕoȹlaw.dLp))u\6PGN obZ./NS$34r!N=l?(U<'JZ 1尊 D;43c'.kn@*{ 4x(YUSSW;2y'_?c||;8ovvxuC~|_7~7NITRF\ҸP'?Z͙פV-\e~c3{flE6[!$d/<";ukyR!0Y+UgNPx.);M­YAblj{l<9U6{G&HWpmdJRL嶟Ay >y< -B3@`s,u$u"ƧXm"Uu1ȅb:Dҡ t U q[R18/h\ȇ14RuG ؘ2K1ڮNQ%a]R@R=0z{h;xmCGĠbPsK릍aE!S kV4X)9UCktZ3O+dC%# Fur?HGY]@2m|h/{3kWycs\-}&|;X}oa>(&LPg`"((}3^X>0<^@h OTj^ӼRQ8h&[g0ɒb1ߒ$(|b (ڬ)H99Ohv"߄CIZe @؂'G ]b&À 0>BPW5uRyQ</+'Z<=mBZ(TGX %̅Dԁ`+G3xN x;ɭrͧ4Yrs&#f^ ǒ {6 ⬣- SPU0, Ĺm{sڂt~UB*i_VSqz-p\|> <ßu\vL) R, P7ֶN8_}<]}&.4lmA`unʾ,Ns$Bc=c eM*vEIڵiq&&UU6(w(RB׭ r17y5[`ptiX zf'o>' 1ʥc^5󝪜ȆN=!K- ͩC[v6|  Vv2uʢ;0mmFzw T(mu*qo`F&OPbLsOue®L2al]eQ `*z_l A1v=XЗ2ƺ!յӁGwe"̘ܱ3kqVmP BUy3Nlʕ I*} 2b 5#Ð 9PJZT؜;> % :BȾ*bV+v;;L-270zLJ9| о\Q{@7ƚTOb>l@ݽ0TQJ񐸶Nͅ>tưq8M>+K1?}Jg21µ.UkCq[K0)HPy/~{6<}ַ߷6q_I_}K.`~%xLJ~O_>1~{ Q6OH_(h#A}&#%R=~0sl8@@p8V^\pa(vJX0hl Lڨ4Y1rtԇ+q$g$#w{|kk+IlKVǰ[iߋ;0˜|&H 6ö70_p·|fxml66&[߳r(z3 KGĂ2! zwI)/(I֙FzEh&ڥe+6b -6R>KDUsuuw1c^Q%HR-ZQbmp2ĥ PeGNb_mH {ɞQ1{Ĭl#$$N53e.!it;܋CY[ xz7Mr›!Q0B*md?wq ek.{n0 ۠A!W// GijsxЮkfb2wɺh!|'чOM'gwFڐKm?*@襘Nƻ3`N,:T`ދ%plr?|x/z bzRÊbޛ\L-R74WO`*orYcl38BF+;Xkz6vڲ*ВNiVa~'V9Nحml$LpS OJqQA%(F=eApZ_FKibjY*@Eٺr?o@zq/]u0[w߿ӥQ4JMNafN[lO^y 3RqgW>4n«KXy]!H+`-g6üznQ:8tc$:s Hn`~s28)mxpj T|k`FTҨl*"07JUQVw|EmZlP*h T@0渢Ua]ȭAUR5 v:2%g*gipг}\+jY]RJҭJV*xL/S"ȂUdw PҪ@q2|`y1d9p4g-JiK',-it1TrXf1 ~=NƶeSͼR5T-fѺQ 9VPo}+ 4+V>접2(j%+E4󪚏lOI2c4z= n6q{N/"[\ZZXӗR[!plϜK!:R&e8Mg`!9)jv6nX2:8 p}O_};upᣏ>zxlkkvpp09uT4 < iJO~KاAg]O|~=豢/]\1k>؟b C=ryd(O!85gLV7.}ż~R-Y9cg}3~p0jbp"I{>![i۬aXI'8L!*@3gb%B|&&duw$g^,D)evpv9y$C!Äd=-׬꞉8K?Ϲd D%} jJJ8 24? x.9Y5.j.ɍ#xLJf=h%M4Ss/Xa,ug9ko^ 7p>U.wڲ ^ݝzw;}w޿wK~' \3/>1qwA=ael͒- ǃ +g$hc%p aU:{7Xdx>7`%~ѹ9NmubX|׼-7) {hh K 8TwbU1Ž -A\HW(0%#CqrQ|: &!G5 q!FUV*܋L"! .k1=2ԹP/٢-c(Jn8{/o4irnd6XӲg<^rXJbZ5 H XƵ6UO&O%>g!l`Odd)rFVWLT1/fkh;o~vy3s1 8tM5/Y*KH|&\bRQg9VOT du_UnDs.L;Ba7{1Y *e5Ք{to14B\("IZӞs/ù55n؀c* 4tX6NAơDJr˵ҧEe,MVi?uJ.łw9aoc. ?d_:kpz9LP) .6$̮9|IhJdd O;ӿl+Fou%He0 ^m[kMqNu&Sz(Y>k[XԹ\Qr{`Ǟouo>2/+7&Pג&q$ lWigirѴm{W|z_{{{/n )MLUb|~خ_מ|~d($0F ;Z+`+[ԅ)=[(&;bT8Z *יB8R& Hkvօ&Z&rA\*:V(يYdFyW FT5 j,32vP[C߯ԩ#m= S 2JmnQ|Mg <_쫺r [*#`VXHދbU( dBY xWYEBF5Xe_*喝Бj*)380kֱ8LjکŶ9ReRV!;X U9 -X夑e& @h+Ԏ]9" g˩TaW[VPDz;h@<:>83 r%樃l9PB~I>dl~^ȅď1,`{asKIٲ2 Gן(H\llvݫ1958v ^{ &g\{exs7};~x~Zu' L>v7ÕO_#?ɏ>{ tHTi=dl#2+sw<.57yS_y8{p6rphzس|'V(cfܫ P5|0|ڔ}GmYTϦ>:!'G@٬b~u>+su{J:`1C6Ƥ>Qp]e g}F:ukD5DRT@S(2HÅg[^ }N8y?`j@jW "eGG 6ݟBc.B,EB2'dH!<{Ou0YNX2T¿)0{Kj5 %_BλkloU1 )Kifx3'f!O}zϿ.'1%Gנƚr.qXXLFmi@cqnd5%g%u%y\ R٦ׯ882<.}R !5s ֠&g7:؈ 6cPg-%](X|e3kB ;Zta Q:|ʕ+lx@鱛8yyϜ- 6K_W?mJ\fs(c#2rNC¡(sE{->!cXʦg6`"T',bIF,ҹ̪`v"0ު=j2rgQ?,rLZ$"c)s~JGPB=lgnj(EA *%gPA*9kgC5bQ0r(~PeTH|B5 HǑX\{TA•=t ҪRTedZF UHWW G(̃v̅lCUgڲ0I7 ,Y^0\#s-oBR񨏳7ys7 wx} փsZhti @nDA`&2)yn>'!܄ ib-4 RO!"5fF#Bkdž26|~K,YS4AQ?سFAhX1K(k`RD_ɿ3nS jX3|.~?̗[>>~C_ }dzIH4dH9r7s@*\p=s@_ A}Y=gc!]vlVpdv.3?g5OV.p,َCq"BwÙ4[.70gLŎA m(FlSg専h1mקMf0B8 zˎqf,oY-t̖ Kt,9/Vd0(i W./sx}G09MTPn`94C?N Lq8X0>Ou߫$GW>rb5`.HqaE7Ժ .[^_c|Y8ceS Y%Z*,TՔ"SR$̹jIF*ڮ:fo'6nܚnb?mOۇ+wVvb s.fx] }I@\ʃf :((1S QxĨO1[PORC<Tɮ>lWX ?d1м$>PmÇ63/.`gX[px jF-aIJE Ļ ~CE+Lj5hK` `=Id:sXx&Hb`k=oG `C@C:7=) dZF[^CDE.AiE- ?Tk&-ʤ[$L=ShL!.LC4 !G * ֖n XDRa]q#NA8F5VRQd񔁴`sVA^X f+e̵˦Ğ*BmQ40VL~,=1aN+JRRb7زLR3ިeVYI&\LT<(V;־,9  z(YdEF;, XCJG>@#~B?^Lj%nl056S!@*`*Z+l``$p_2qt$ןō|\Jul&utBRJ%! /{iaVet9Q x̑^[cAD->!uK`s,¦CEUMZ0Zy(d#1RrpQf@vPVGj}Z$g )R+(,L,9[E5k>0XK8pro@<7Yb>v wm|y1fd$ې]|)(؂juʣ~^X _SSn{kx{f4/^_{o /~x~z>}oƛNG__w_g¼?e)|2b׆wX[`9XRd"gE˝U?t>"!;zOקEl xku>A 3]xJlK Nr|\Hpp.?g4Ґ6f|c` *W%Aw$al/ǯw$<_Q}B2-6!ʼ0Gɧd$[$kp2LP )fQzrtZd hą&6e#n|3BR95CM HO ,ǒ2wY#CRRCϡX*Pl"Bp'+ΙQ)/8y[Lhi^o\Qv33X +s6I,Fc$ 7k>L-ˠagK,2jb4(V5-aM.^^3/k 6̋vfm9këpmw? 7;d*p/6K;\D;aǵQ`xix܇)fq14?%Fm2Sy6\"K j!9B_$*b.n ; !.6P73cd;}p\srrbMıSg)LѦR,<0cSx s$sxjq`, k~ض66mѬ'Ȩ>p\XZ6yogdyϛ>[|gWM;53ג6'&H@76~8^\\+| g\c- 󰁞moiȜpṘrD,C!j9=q~Y}3T ɾ"X# b `uT*fC+ ^+`GqT+cBUu&ۅJswRs lbz[UT1UG${ y)j)ک<橲V"2:'ʂ*('Z*RJG0b~na@2C E.T#<Ђ~edvtqMW1 M!QR*%9@Z{M.`6H_ٍ,Sz(gǘ{EPZCoUUQFjJESØUv`p:$`T9Bs&x3!=顟pLiOXp(;s*u2OVVB;֭soC~1lE !llw<2$q#`sl"X _0>ol&R :1tҍL-0l?~p4^(AaJePm`_ʨ=}\3+u>kmo_~鹫_;nܣ[wCȥ+?~mn O,Ot@CEێJlzڃ}dpZiiJX=V)y]$ܗpvI^ 7C8 ̥.MTJ6fm(^e>\ oD K\.[&q: XZ fMX tBqsfg8G@̖{ʠ6B>7ĊV'jjb.]O~'i{Ke99։ʋJ7+41Qڿ+*#<^lZy |;p90LI nC\~g5@dQlNzVK1*VfiV/)0&UhT)4FU2Z. $RR!6Q駰Z06l576+'x"ldT(Q.YkZ{Qg·eo\TaZQ4t]x{.s F~!k =\7G-?3w{plkj M!Uo3YrQY'-@W$uUUW<_4.U56rax{ٲ))\|U/R[X9Q%}Xd'_BV'ʙRJI>nZ6˵1l_eׇWPqؗu9Lln57o˰83̓:._~ǽd@IAj'Pؗ˸} `mo5yzF6^H]Xa&P}X677StT`C0&_\z?>}[m9CSt_ϩj0~e ff|QҘ]rɍ 9(yLP VQ3B+@&4 ުD~GPsf@VyEnL+wr{A->CiKUv W*XЩp`U5Pa "mBX ܮ8)U7q:_?-OSe7JiDk@)9Ș \Lzr Q~ʅeT~z߆û Τ{S!3XJ!.QW0Zk6hCْ.Wjv j,֤?,+KK 8>:_|O_x-7Oن[WFiԷwW.o= _y#迧w^{``1'(yV|j}Y'G} j\ 3.O8i<7(Zխ>:,; Y<9OQ6[LOǞ$o#*rW\ )s2Ң"PzI*EB{\O2/Yc'\LB $vU!* q2FJ9'(AwA9gr @%H#paY&ll 7XSdz{tN\>0O _'QJrz- b[ uG(gSqD| {gCAO; ;pC+ dt0k='-W|g { N^ˡ历kSo\Ę#^uU*c>X ,0i f '^S+<́P 2n;4.]G %u%Tdm,Ő A ~ɽETU_f]IC&~4 Peh n>8?]cxN <{|[6TF{}ޗ] M> 󈋊6 z0|`>)I|Y u2Vlh- {BZJ6⚑SR:=+`eTwyf,ݛȍG/_ `aL)dRUNj-κ|ZPv~u(]6TC`z gyVS[86?eZ`j O鬒` }g("iO*.\#/>PB1+F6DE [U52s U ~ Q[ͧpwaM@(B5]oЛά+ߴ6:S~\\$G#LկիW罽΍6QwO,۳XB"Ʉ,(nM{ @XШNmt@2DrTzF-',t'A U.k;hm"ߘ QUX4.Ze˩2ު<,\P^E' (at8th^>x: p6Ϋ1vjM#PaUVэ97d\f) F}nb)e.9cPf7U9VQU`8mYNHfh2} >HsTO!7[!*~Yт} *Oyժ6(3,ؔ Um] :Y>0eAN@ZIʋfq69ZYc/}.2Hr2x9Q/פxpaV4:LHvI*FBъau,cp`G 7֪}86$Sw˭VG-KJNJ81 1F~vd$AHqb `"S-6dy{|ϰwUjUUΡ"KT!.}ggZ/U묵,uyȮjQ wQ6YfPĴZDcJ/11xvOyN|>gRPhU` 1&-^qmU\J45iϲjN) P6GES뽑E16G+߸?]c}D@'\[5`X,f_O˾2l G_{h4:{AZf䂻  R;Թ 3ƥ.?msD@%u`♎ ѳ?w"8ި(+:A踈Du{$sjV!HY."/(E]g@"f8d`l eECy㈺5a"VPcrD蘒I g!PC#?G%Ao@&TsԚCя ڲ';t^R$I,b 6G<ȱhjȍ"=4VjR(K0+[f•/rֹĖ?i.ay}6F1T|T)j&] e]jgFĎ `W`P1PI l!w Mo~Ï[GdyChNmJ HryIL2%H1YVR἖yM*Ȥjrw^هޛs}<4(,G`-<,{)x_~2$v}6P-ubZU|<-ZYrK|injHqN0]f .7P,63Z^ 0yȌSAи_#]L b=>.8W7\Sr#]绸fTV`;Xkk: 7­9 w9n=%yA N'( cC(]K1H%5.˔f=WgH*ormkV#\V*iAVPjFW4{rNH-e 5sշ0oz oMgS,F:o6`sRv/8ĚDkk@X< \m,tw/̯SKcy~iO,r\Ih1^Lk@%l)늭&.yC3R=F^%DIPd_ret)ӃsܦcuFٓ+kunR؅5Bf]}ϟm;KR*Sz!;/]d`mɹuʚϮ}dNn'@Vߞyb*5Rbooaş.:Un&;bp[<=D}MZRIٝ(Y(WmVޤbFBD/.⪳^)8\Epkl8 J[1$ITk[k*QEֵ`9u**J~ o}T.+g >=<5*=`"z=#FNXj 'QPyNf ¥ c*$?֒kuT1 XYëd ?u>\`+2(X3pę(+=(TrT0lR{)STa5sn ̒F).c\TT#6IP7<0"(cI `H|= Ru tHޘ%eNfm6dNnNŒq5s 0\ "sNS%hìP@ɥ9V = ȶ(| Glmx[@5 RFI /OMN-Ֆ]DɳM`s}yw޴J$32VeLA~ߛύcÀK*]M LUsL !9NM~aG`~G>7nܸc>P֝J_۹4m_hqe? * 7Ĥ`6-yNUŋ&wèյj\+ 9(}d#,gVmsӘY3_6r@ȥ^(\wtB1Yb42k~XaA `MXf+C0*֋~_ 'k;C`e\^zd0&ה5XV=zȞM+N-T*{,4?Rm>4`ZP_'3d$@%ְPmUNҽrg*`s`ZiE,%@O nb5.PP[3rrR^ALi@\{htl&'j6|!Ŀܚ݆oR`% - :䒅̨Ņ.ic{QCg5%P0`6tLcgN,"ݰOd_75Ky*9?[}&Go`(9I ZdN?4I@LjkYڲI\aǺF=WMڣǭ_EbE^&S}^x=?}[ne }OaXkB],3{(Vj^wEٽiٟҗ|k? [S<?mg]o3j=f܈;sQkYtni;S $qI<\?Ϫ7fIW$uxNjCcV@Ɉsq"lwey!OdƑb{-@E2mZbA{kk}v8?1R -#1iiXRo`DXwx."irHq ϲ@, J[d<1ߒ"w^~oׅ^Jc74g[?§؀ưM{fXS3T&M Mr pVVIFU>ThM"0<q6p š3ø5N&Q.wf>q.uV'grL= 7eI뫜ҨrScK"#^bU:*;ُd77(UP͑Y_#[p5Xb;a? kD?5FXV8M6|'r\U%L kmMSUQ%,;Vds_6fGʞ:wVʻKv`5U-GI2 FRN9(l&ͅh|){)䏗esxq` CQvh;='=c=o}[OaAZk#sZmgo͋՗;~7hBN=$6_&$%MABg7mr\.iUWsS] @RA)*PR+?ɋsp< qg++عu s&,Ml8pa),uq _9𣹰`IHeam.,܌%?Aqk&Z̥Eu㪎3CXnȌb;j*9Ƿx9L#zwo>-=e]bZ]JaTíͱ17O\Ϋ\ U/ZƝ*\&u'Sk'Sejٳc-x%cl>h;h||&uaqٝO \p l֪?^>e`/ YaMlj"UsN˟}Ox Zz3Ru78 `1+ƹYT}!>;P]tq}s /ÓaCiBQa`N, f?SoܳbK;8='獥 XO^Tfr۾i\WmpyՐ4mI:G`.tHD (дp ] u$w.+MuZKQ]{)ފɀP-YIC*Dj%9urI̐թDWa՗^LY, \FmZpz')Z]ϺԕyfC*:GV<ұ Ulu4F%!`H9^PW*ir:c `%2\a{a9XiJf̊yS /U 5I5򼩀BΔzW_Ǐ#-i>,SyyrnY-K OCle l2:f,a鳕 T!sA/X%A(cb4amhM*Td sķq*k A#g[n}Gwy95@ύO 1C ͳ&8t.X>f#7!BIkfys 6J#VkE>%RCqrH#]bq:GF$! 㢢&vt@bF?vnD@t6˿rJMkSs8EFIz:`6>.drl.[DDۓb> E111U@LfbK(EL0)Sk5Rk`/ݩ r lA`̱X +Ml|~Һ$Pa 9䆍0L[: Tu`bLMF; oKn6vThruW[!i`!;gP! !'s?;o/7Sgᾅ|'/od濧, yο( Ħq6̙T g'U# \)m ,{iP=ls<ʲBZg FW6PҶ i kp. "{c$2T. L>9GNb!j\W9/"$,XT6`Tn) 7ȧk#dp:ʞsHO^⇄I <ŅLPm,'aZX, K`n2C YH2e+%a%A%pj.7Ob`h$*S7zS`Qk`?w:01Wq5^iVKog0>XvIro'l_Eq 51L0YS-&L`5/ᷟ]5U[.i1Gpq.E8S_.ǀ X, $ kg;92u0n, ׈mgGcs=c8{juNAE$ n|L~ͮ:/5 ܡs!- e쒶tŢ_Je5[ZVoJ ʤ+:ʍ&K`!: %Q.%f12w1[ta,B]Pe 2A +'R`et)jwEYrrXUR$(&-ǨoNd** (9BНWkL51`z0ZlMW[af15~.´8)$WVU3UuURpX0Pj[ 8 (1%0S,skqJX }U)Ur+Ur`+MXH*(v 5_uA2)*, puXJa3m($&n.:U&} ,bd6Ѝ0Hu}uPh 5X .ʮ <, ^tԖakdfx S ƪ9>Mz}D/^78d>Z.Sa$'Idƾ,eֱs\y@ZppS[0_3gsz~\,f}Gwoc˝e1zW^}s{;x≗ ݦO*Άcּ^s4ِgsA~,X6.y{jsa("OE 2k6z`C MA4! >!cfbb;6hqѷg`F^Zؙ:F k`:9 yz`sʫbEbv >ui8u0g06p.44i@克.3f%,9fYH V_)mjp./;2lfS3.H *aT\0&aL֯ǫBi(b$gdDa n~N;xSl'gI+p> :|Ft k]du +#WNGAn| 0;r fYq'`fIJU3+JZz&F 9a8102N$2*r⑙=.\{ \?$h>Lt>)^Ot׭%GҾr6)խw0tCձXN+RоTΊ,KSLǵ nW{Qe~}z}]؆#ߨ!JF*e?j a&yZ̺Ϲ3SթM@-ښ8/1JHB`UxzW|]Z :5^/S"F^6Bw e:Q9/8,ܴF )ϟ &'b1,NHV;{MNNo裏~矿c6 i3 [?̷/4CIɲH=/mM&Q1+)*it+ &@mA:ӬWH1z(h'ՁFI}Y+XC!K'PL:=u=*(3VB&Lk(z٣AHTfNƠ |U$0᪙X1 \$Se=oVB4T;X鼠J6WZ O,X%VQN ^',X` )#{AJٺsSd:!n6?2lKz\4kOԄ45J unUPwlb%bbLxB&L[SFau$BqZB tpЎ%4ݢ ./.@9},&k?7Imx-i #@+x} mٓibU.{J K8uX P$ڪ\FBXvٶ>ذ1uJj$k4:}*Z%-QJ6AdR1زoil:᷽$j |V O2eS>:^|7q˩363BwNZK bbMqnnl&1lNB :(Ĭ/ o.3 'ڶiI9n=#~"o|vooI']I̍;fzSp|N;y4:y8ꮓwOdp203 3|Yi8IIEBԬbUj&EvnrٔO7L&x1o@şPeC ߲9;rXuuv]<-ޣE5;|3X V=%J$ 20ZGA"!qX`ҝ`+M/`TZ/rv|_&M%UUj0GaYYtIpWL.,S1 |/I>m*|P݂rN+YbU5f(&bYW0k<4* SAۨ*o5R cK/"/JkUd tELb0P$M*LJŃNܥZM{riLm86XNFtJ *fX()  dȧ&|~R:SC 1T|L0W+jLo #*TޜL 0ĮJ" 4+M/DQ>T5O) _bpcn(뇗MJmu woe;嶟ޚ~x{=w !%0C5,t$;AOϼͭ$IL05l@HK|9dDd_EZ8/7Q6 oPu2į8sd;]>LBOp,L@b+ | :ȎqS/?]j,FR8C. @se 1*q?B$Q=F=LOC{ZudhOf# M˅?F+7,o]+1Fh6;tL =Cwon-~ms!:1IM@jz53xcpXg> _z68p _4:6=KcMJ6t%݆ /:(iІf)b,K65d0"+w*S3{vX0jLljH[VH6c1Q1>4-˖ 2VSЕ%O0c+sS#Oz#fJf/qqk[.lÓ9 uh#˯x \[qG62vF{X bJJ䊱Kf&zο0AX&dYnM6q/dmS\)- Xۘ)K9PL텕dߚz(%洗)K[W[3xg[&~S]]kV1ɜj|䭓̄+euhY_*O2ks'9 (r]i/䱫d5PҠZpzO19fbCG{=c֌an-c\7{8p"2Qɧƹlym.2ZrRoZ&fft],-y2܄@~7mxmK7t&C"{E ؠc"p }K&΅HizctSO=o}^<U@`B*JJ.MMRm † Y}fY-s&] l622H`Y/ !1Ax.ȴ 伈U` AN&HC.ȴэ- F(TsHdv0L CWnҮDM]KbԽròT>|H"yэ' (q0{3JlG=bOP2MMHs( 7Y& Rm 3 fD]&Y']wZMTXgh @mVؒ{=zx۵0L\o;\ y@1i")5v:sQ[pS;%|;/: =KࣷXf0B"3o7/09;^W=_EiM=vDP_y'`X=3{ 'IM ,T%&;Y^slX#h?'#e8d?sp&/o&Gd :f 3Ȳu6w;mfV R읬+/'搻0&6B5mC u=\8DZ0ZXndGC8[>*` #b1c-L^C*㶛nD+O.>bӜ+:?H4i_j\ᚥTq!:Oz}c8uMq+(0'}Ja_CrURͪ V14Xrf(nڙbf eۄ S^ L2~ g41v,e,`k "i9~Wdi4ei0LlGp`pB'~SJ,sɂ0x1:yȒ]q LI;x L,g!v 5 `+ֻ,P,Ғ_֝N!A68aR.NbC NZ5G%x&ygh4*#x Ki-]lTs'5iL 5== >2Sb۲S)J:6R-hVAΪ{V1(.uʐqk3"'0Wz@JhYs4ZZU!B^-OW2dm*'`Տ'_7YwW2Ќ{aʅ.`94Й(IFtӲA8uZ%&쒥$Qbl~w{TSϧ/`\F"AIM Q Փ[dX .1ߦl*gCZs*ʘd"j]@Uހ$RSStO xJ8LiP3 Hjҹn6>bX ӊCRL] ` 8pG<~jg.>卡bKlf\h4r>~Zm>gꏜ@ TȣfI5|tʝq[p|?O7]X\c] 4ߘ45Tԙic7 &ۚA~CS9@&&D#K }wsFh-!Jz @˲MbD cP15$ Hy0SUIt\Wc ],rD/l\é5ZSOX)MPy шx&K G:)l0j{U^䉱G=ԟv חcx9!g~_#t1Ji.:w_8~/rv&o5@wn?߷m:~b좰‚yۘ1w޸&ٲoCsO\η!aFO&gike]nkdК3yttCl)ҀQY\9Y 1x&|Z{=FcLrj;mC NC$%Z^nj (Gi ,&9yLuS`ʨԶ.,X`Ӎ"Ͳe u iXcﴀQ-;x=8.llnC%3CCs눐$~_.)/S̘M6_&imHJk(t 'Y|1T7OLlAT>X;Iu6e}f"evV Db˻k#d]QmDcsw4(>BG},$RThS'.ZCYHk F'QN UjUeߔԟӎZVP*xKبN/XfK^?&3lT P`%{ϟhœc 10,[.o{^dyl Ȁ֐|y ,Pty"'6Π:Pɸ')bXl}{,>c:5ݝ03[ƓKC&_AFIG1E ЍZ,F >}44i#I J&k}& *6\ƿ~9Ѽgs8XB׿O2" A :ѿte&L:3mhĉkE^kWKBLIR^A4;#R~2$^|,Se 1 G58۫XBYA"Ţ "Yچ5X$R!Ssȩj^=|\C,4I(Ry"&1 `)$*s*R9<7kEtHKז>WƅH^2+f9p+{L8T:(S{ǬIK<<Ư{pZ1|ow`'Ų8v|L; qȏ{ޅ g0 77 < =# &L㮃Zx熃߃|b_{~ b%o75k|xZd>߅9]m~O`EZe7\ڑ"HHL<,4W !2עD¬yXǬ"9ɶ Bd5џẕaMf c76D\X8͝%*d4k2My36wKSn.D @f"y2Pg: FƟOo㟽ឋ-T?ww ܳم~? .4–tdD 1Dށe ,;ْ1^ T]I% q&j\'l 0g֎Sr=@U < җ4?B,n@!w4&8=ITWJ6eS9kbK`* 7\+&1Fub1Alja/|ibNcFHS[oOnQ㮬A6@f+%uwJDN)8,j0N61JPɑү&4Fy>!L2r _£Ssٟ4L.:sR>zNn'O쑿}Xbwڍ_%IF\,cӝpu $`%1Ea_D~)&T~f:Hq*.&rjϜ52mTNm'Ym@ii |6٣v裧F1Zx#xkkꁁnr&lFp1";-s[p]ps ..dA3چq`;o:sq_ة_e୷v>w{_Ikd ڻv }`2g/|fvd ܳpKm9KûpѰۍCCv҂:d)u(< 214RqQ9xe-XzOZ/z>}H6Tq6/Р:*FĮh`Y\gCв< EtD"nP+6ڛW 9cvGZ])]L1=b:',aL֮}Q}Q7j,hn$(,k ZY#6Xue5Iܾ # ?w|9|t}6;0ѵ#$"*LpFͳL-%]&RkA'h.|_?l5v];Ìdϸ #6>o߻߻<7LۺW~W~]m?O@YJ d܁O ǼGoؠ#;f$&ב>#@Cf=b Tj;gl}{w;=m`r{#X?vvIVkǍ?m8sjf#4\,`65y2@K[WҚa!ļ:>T,Nrc!xx0[9/*J-,m.bpIsgQ< W8F93ɰ,#ya,  '%c 0 WJXzt\/Ƀ{Z9T`"&pa"z ifm>|#ع%xp xտ@>|ֈӿ.N¢Pjbg+k/*@b2/ʂ ˷6Ku\$!E`dޱ*iZi/H -&+>@T7A`O%9o!~@Մ9 M3ę%˯xJc;pMS>{7UI+q|ԕu{ʣW{/'ީ\e]J_eCK7hӽen =YVVZ"+_;o݂SWaq}owo`ť_P܅H]I?>^ uCcu%X]ߖ[Z]srZc݄Ma@yaՍR p&5\k*,%gZY5Ǚw[85aȅ>,^N!d;Uױ^i_WOvrdoڻe/?Hέщl>梜h go{vo^w&udg.җ'Sji3(ds@XX*.=JGUVub*. iIzAiB(P ;.]\ڢ$ź$e =.+YTd}$(44vkvB6- UOR++:q2WtgTRtL~*ضQS1kb.wk$Ɂk0ͭRCVrmMá)H-ڐ*@3Y7VSXdL]uϐSmTRa[D_ DzZb8.鉁2c)y7lL>}?gEo2=NGܠc`y7kȹp(O6R6 B=qn)n8 .Ç- r1K1J?>M&KQYrm#05|$*]pG6,am/_pj;Ew| "E .CH$+eڐuwE "H^MILH&w:<.Zlm֭Pp -3bM4XL'b0 6[Di$G~ta~&&0X&^ Gk{-hF~MXX4Q#?R ɤ!̯7p?p s=;SC~᷶&vFf yUB1 Oa{o=/n¯oo~w?澃׮ /|,"9nǰ[M?> kNg-w]4Ϛv X&*Ć#88jXRІq58pa5&ֈW3T2ŽUGאD8iޥ`@\5^bFfM Y1A\`#_"GeܠN Q)0WW~OLMU&w]~` o׭m"i`{kCSxM-ӜM(bJξEN\Kc]@@w)f(^> ~^.6>eC 3fz̲`[><5o  @Fi7>Fϐ}tMĠyf^{o/r8E[< #14:WY">qm@f[^9IY.ªα߉T?k@'MT x /.AF!^Gp `ׇģ7ޛç­䥹P },2~zXZLe[*vjJ@V@,4$(Y[),h}x mn̮X䏌puq3z1S֭tD*XGFmmQI}[)\bdt2 ,fSLgW%LqhSy=m;zNn'?cG==<>>~YI>{_.!: pw?x죠c'ݤ&k1<lSjfzR ԅnՐ16hV:JRpOAvOߛH Zn Z mS%Ș5`! ?b)vtL):ء޲uB f6rT&v>*X@nfl (eC kq,WMK#C@,_}+ÅV#Ed <+Y Z`K(so+rL5`fإϭPbmA@qg Z `3O%:Q9\*yb1 r&tk1ȆzZlHZQ7.fAUJ;{H6@mju6FM# _`ٰTYSɓt_ lfȹhY. ˛1 _fXiB~χg|wDVٚ6vʆoوWz_N )qbZ<1cF%c%VJ*3L< F(.=oz"ub8mAzl|O$OQS<7nmm7zߣ"<,c|Wa<#y_zt~HTd`4ܥ?06%[ex7׺DZg`sjx"ȈX:Tuqژ4pxܳtܿoNevED5:jq&-,A6Ԋ&~xrC kh3c6ʑ;cF]cy㥤 U8 E:iI#岻43!$ c&mBH-u؛\, hIbdsvHM/{olq}ހ $!IHDUr";WR)%UrJ9)q$)J.l)eƖ˔%h 3A xt3ݝ^^ϥ+r,A];=^{u>yK%/.4Gw9C)>ĥ]@& [tay,χoO\_؎^`5 :Cc: .n{82v-Guc &X ȥۅڋ|_^֗E41"X  {nN~[E8qr-l\N7]8qDk̸ޞ@J55qs W30T";xJ2_b0h3yH"MyK9Kj8K2Y*strC̟h si4WMA';'&$59! LbȆCfתǏ((lk4b(@d1A})K1_6Ȭk<ϡMܝ_6ص?}s= P-q\98s b,]5%D6f| 4q,~G Xt̖Lf<*k#p$x='= a*p !_=벺$ $)/!޾\p%+o_tmSxkG'Q#L1Xڴc.᛽|LZӀSOt~;&O)H{AW nKlJ 7 GO nܵܫ386vŦK^FU/Q*"y)^zdq;U#7p߭8yfzHү2гφ!^I

v* SQ?Ƃ\LUY2As+&e4 Pv|Pv+$tG6QOom0zmȨ1ZL^z`rb&Y, Y)}RUId*5uE#A~n+&+%X.%cYȓpڕWCw\\unX Đ,]D2i]҉KJE۝gO 89Jp̷i#,|J`<#>,X؛2 u1޼44F)=ΡnFn<4 v'172)ؖÚ }!I9}H<'#fA9I\ \'p_uf,UN >ۑ :5'dagK*R2j^kơVdw qe4I2l䠈}sAZO$b6C2k7ٰg[EV͑3,TLBG6E\X#g |{/#q Jtw/m׭߻w<[n VBZjp中߆$ }BP;q(O9Z$ණ>q X=Ry]dtmX;vvZ𶃍MuN `BPOd%J. m!8Y$FZSUI /]f5G0I0=%AJ':H*N<Y*UNv V2)MOyk9[SLU`1+tR54ȸ+TIQ6n῭E6=]̓Dsׇ]wS+'II<vLj7S{+/wPzqs<i b: +tΫ/XwT^,`gz$ؗ5y]KJޅɌ Qx$*yj_'uDՇM~f;+T*2CS(e5x}\+o/`&<1KXt>hGTeS"`t6 :]9B=_Tiw&~8ʹ!SG9jդ`l4'oU n}+&SDyŪ!Q%Uq6ؓ=Wpy(UMFN1sOeUnB:ſ+tBLTsU[&x%DU9+MD/tR+CAPϥ(@(CxDTMӉM?!Y&$5)4}~ |A1; f~g ):MT}!@mհu13v*bIy%_cliM6BYK 4V8?% [@T@ȢMʨlpmY~-mb .W"DAnwӁ60H sM1'&MWTv.b TMen^Uhs%TL LY'r3[F*$o^: K轣e'ïTXkpB 3j Ilx`(&SgLPєU/YNR*L!'`dpحj*q[U#ۙ#D ݾ%7;x-g7E,jCߝu]2X z}gCL!y7|wh_L&{%ׁ wK\g3>_3o,$/$%y7HC)9Ţ՗]%Qs%(睌h|]\kVFꬎ-(YNȎ7Xޣ>9.1BJA!@bM_ { xrp?~~F@ײIT66y6!bHLapͦlIE{%\J fz%C$#sO㾤fAyXBNAN̾R[V2K<[=6ˢj*+A%t$q>Z^lMx)Ak`s8og/cϞ+kVE%_/tB? Ǽ?| <[7ëo"A*."(W6bt\LjGcBMD?O~9.MXqNmz݃[.ua<1ٽgpGP_r)>V @eK=w_%z2HW{G.Iؗ;L~y$ lmTT6TFOX@X!|mAS`-YܠO()t뤰ŸpmYY Yi[=e]I$g*Ďd`yc"<ثT˫t~~Ua/Q*=b) h ľ_ +5GzX[Ƿ3c8 H*g S+i8Nķ.h |^/d s9sJCIR^N6Ermgn:zYp/yĬ)Km- @%/)M'ȟe"1oRKa$M%~#ǔ%ّ*B)͎i!o %_Ⱥɵ*ѭߘ6CWŊY$YKLfAɗ$RZb(%b_mQ5׊/Psҷ9ik u1:H*![T. U'QI+i{2Y z|@a܇ͼ2ű~8* cw`s7Xg|z~?MN[ PTԀ%I K,DZWē&&RʉC_`,V ,ed+=*|Pv]w,^x㩳gO}S?un?kuHぉM>a0EK%*=׀AA%4ZnԁfR pcNDh$ "~f[P6(_)9D6J;*fbYϪL[{qm0~+Aۜ൮^]3sDr=%f C \z@eOEV)\1EuU*U$Ir :lx -b?8SU ;dBVcV.:8Z?"Dp#84\ /nÃnË>e4&}e&'!p-{n>~~:۬aia`"^a߳#V\Bм[͸2I1:TjPd}uͧ{p0}lCǾfGoC=ـo`Zk&pfZP{NR|C,W lNP2Kd adLx8/oQ[|&f8$#/H2p-@(_n| 4usM/*®sEu/&LS@%PȪ=e:zkepVdjӐ*{ X{L7ꜟ9Muw tp5?yf&قi5|-w3aDR[FЊ=*쑡Hdijn[ak|cHT<@5˶f(CUKq h +)^-8NTIrd6.澵2t}y<*/ 0^܁w 5Wog2xPWJ=r\T4 8rK6K1E\l_Kc p/;z :;fY)Ü^6 `m5g 8v5.m˜C&Ubyxb=VCC2# u "aeҼHCہQꘞ$ g ZJ%ۆ>_Fw6(x#9T@y޳*>6 Py\2CV {|y-`u82,•NN1|Aˊ r|ayxz^{X @rn"A~VK9Iwt]|+YUkUK#qr`^qSs SB,"t^,ˌwB$/ʯov ͒0lcAxݐpESrMfٔF ퟒX ǔQt=++9MP0WLb7vY 1"fLJWF%MƳg0.+KL6\/z$̀|}0 mPdd`1Bk(V6.&L,Wy$--x1o-I}&ϡ,@Fxxhu][҅[A|W?gܿ 6ϲ0y٠?GJ~ĞQ sCvS OqNPd9J, WhJj9)1jX)5EԲ,nFl^@yApaI:HRfQV Jix@CMީ*G]v&'2bIyI\Rƚb 6* S s _{r}~\Jҿ鼃w>#G;VXZu :t%OZ8L(Z C߆7C+=|>թl0s5b6hE  (߈\a} ל(_~{@E@Lnf+6afoDʲ.tR!IFQ7QNuRqC$mrn諏=7.b{` l)]@٢,KXd_] d}K@' b`iъmj,]cj\'k' YF|U>WJ.1t~ ,L"йVp9w<٧4p* (DMapڴ^F{69L։ʾZ Z}6I&xP4”>;:MG5MF{8VG񅃌s:xG|wO>rn"Ϧ J dLΥ3LB |Q4=YmtEk7#B#=Cҵt?e VddY>WZ`JIU'O, k8WFmq#x`t2T'Z$VUͶW$(&oLd4SrZ-(RIA}|i|Q ]z.@J0ʔ`9ؓM)mKNÁ7JNh܄d+To#ܿ0iΚ uNy( 0rm9H(PxuOxӨg l&гȢT4Y@4&2RYRbٞw 捁GҌI`KQAj0wI:%[DLJbص,f `%'h^=K}C/~ԟyA=&/]ʏUyQ_&{V&~<{\ڥ>0^NH Ν ѠR2=ْ$<AuIGN He۠N7ɉqɰ%)aH(c&dS|9hs>XQ>YSx &Q l2~f$3U-rף<Ťհ'ϊ):5P$y(^AyuZ+38Y>>+<$q_;GAp#Jɾg}r;K")>N)KQwc.w'mOf&Lh܄8>ssrp|#wNx8]ݱO);c$%XB,Im9YOs1cM ']ͧ | ޼&#hx iC3@ÅEN;c#Ѿ\I,<(H`,&rnDWMdA,Dɓb"SSInA&ʑ*\{t(|1s,C8 -{-4vJmܚ!–G1TNV B<,R". Yw8Q* c;rm-T4 ;/%6c~u,mXd_YR}﹟XcU}o4_kx=NEYh_.S DY{'ő (e~1<;ۜK{un&INfp?n:\W蹍w L6i6\K>O}H=ZJ53A"V\úbUw,cŀ 9WYg)R'+K0q\L,: mG‰*5zi/%s)EK!/Z=@O/yd0!IkE9ꪅGނkY8yxs~W.^&]nsfh4~ |^'|vm;8=W[\;J$m1\O_\ 'MM>/eB&(sn1MԬI(,uI8K^?IaLJ) RVH.w7J*) rJ2\J 3kZ47KI؋QZ&+3͘PR27@]o~@X %ʽ ҵ%8tL4pҁ^{c}jAL\J_Kc#Į%-d`nxč:fs" OF-,Ux@/A4/W/V`ubHe)2/>  w&7&{P)֑ Jv^^>14LM}  @Or Oyr5]UBTnR1W仔֞wJ̐L_ l [k@NWf709Iv^Rh_$>T"6%< $8@Q>u3Tbjd|=@H̬!~Ak\d)g͕qbY;Nxg&QE*W WFV}y˺޻=7݇m>񿞵G̯<L[pqyGe6EG\xo~G?O/9\3`Lfs{| ]򮃵K>z[piA WmZE%mN=a?@G+κ8"3~[Z,`831 ;kauL`怵1ׅ$7ea̠dJImJe`D~%L$'Hr9ml3F5vk&2$us1_\`)7Y0i썣 |lC`)i{#t{Q"'#$yx@L$qmsžBUb*@m E!>:LFYrE|l҅~Gf!kaBk-VaJ ]OwA?t^1r2?1&YF˜VDQLRW$X,/1(FpUxw5/3&ƝuhY]x#Za#p`jX0HۉTcF<H ?=K[lj*{EY8U$Zin踔b[5 *.D1X61H7I(B̔LRw5+ U<2j y]"\u~_cN=G2 Zyl~/ަj<^xa=ç?7oA> ae1Wz;q n:VǤT*:4W0i"Bҫ\QIy|[g|eTI/iƐ`SF.{RGE,+^A31%ys f18Ee|b&7qs M5{md ePm%Ts^Ǥ}Q3YQ'`&:{qNA?+" +,<)'@ȤJ.,g~_$m=7+_o&D׷jx)!q^eIP.xye۠WjzueBB"ҭa֎!6pb.6Q8GuW ,U \(߉@ЄX&P~ʥr~'^77wI3}fSy3 K-**9B><\xȬQ~/-xxuO8ëw쫣% }2'd^= i;hw_sҙ3g~ҥK?'4V_w%S(gI}4YfDXB09M~Y(*&1RLD29ydtF6Rn<\C2J T=h~]| XrT1p@yZS&MPi2~Ċh@jm0YUt>kMqR)#\=tg$}6As=gT;3=zV=gE->>7vt-/=BgUu曲]H`WJZIcڳqhq\H^u4~M1X6tbl5aqqRB4Nmd/ A1gPfy[FCI9m/Qʤ1[HxP^drLJI|j!A.j.ʐUWl3@߀6Ei~MAOGъ2㗼ڌ5bv ֥NkTRɡ!pRm ݖ['Z_Z3> :_ ,`6_|mq߻4~ߩvxv5W#G&Xa5Æ؞8&&$%Iu J5tAmR3N]dk#fZgyuΔh#]192[866)~4,dP@qHd^d)d{uK1J.Jdfҿ SG qh n{*bijs1-\}*Y}>jp aX0"R&*fD32 ˱r~Re5E1,-jv!y6fTT%j*nq 3c{9ښ94zr'`څǞu ~#3u\"8_Ȧḱ,}mMMQS*^i*'kiq YR)t^py|~N0[lWEb==-\}|5DEE"(^Q!>k( 0( UYXBOI]C z;OX" '}hwO-/g~1ei9(ʊMI SܟKu { p}CZr>Sx%W`-lM- :҄ٱD\LŇ()(P3!;A#a ԧ>kwyu|݃}<@`5ȳLmdS`k=J𹘻LTezҘDA;WjXvׂRL/f"Ie$ ⊄O^kIXs&AbEA:fba.?p2Z7QU?y5NE歐L9\a ƍH%ٵF3V @ZȬW[l &(9%9=C7nk}ٟ4J={666wQ`A'! { }!xa}}3g|YU__b1=2jy [Y!l*1&VϬŶǍ<W]#0RRmulaelY֓O q-Eyſӹ2Z3ail&1i,S2$2$D6Aޔ$3R2Fc&nQʪjZ VĄ'^io@'"'6 &8;셜Ȱx#w@*vmI'稥ho$RIJy 4odUŻɄe+5Z$MeyMjMqe gB] s7 !/5/exp-\ kG7>u| 8dfje2h)*)dN|=$$ljfiA6j7ŭ|q/e7l]7G؄kGrNhOKl *!ylҞֲ?R]@RjpS 4bfd3xa͈b2&xLLDz|Bz{F<b/f,1cRӪJŒ<K`"\Xv k2%1>76FvC-W"ˀ`Lft|ѕ88+ВsT%aL0ڇY?%8E-3>,Rk"%*JՆB> yJ*AOc_TS}0l €-A2bS?g { L d4 `PC 3`5O ׀?GQ D8 s /]`eЌ)Uzr2cBLɭr)ak~?){jTIaII(=\OwI"=֩vy|앖[S}تTU %V8S^YT򚈲u(^K6d۴Īj?SYsS,o$ql@k)J:d>Ȼkw-{^v[档W\7yo~xeeemQhC TX0vTyxVQ-|[UU;+y衇wbpۿ'xDoY~q&^K0}N~ùOUE)e3sp,Lq9#g+ >քo⸛NUphѫ '`}fC`"<alOn?|{챯1 p$.Ɖd~:XBRJ骂2ocE\ِ\peLتM-5X5jj hotA5-BIJmؘ(V E,3)_ԑYNSn1~&רg)7:0Z+?9+N{Q iJK+-uK ZP^5 rrޘ}|tjj 1,S@T)9(6cUeE7i4n.OO?/kWU:<^{ӧwWVVZؐ35|4(^m|{s޿|G]w_@^'M^C }0o1q\LL+=ediB ]ucB}eAVVd 3:nVwXCj|b ^|i?JPKBxASr3'WFĜɟ䰡>Qnh 4>zNۘĮes]m1Y*oxc)fp㱉žyKѐ(0 ,!;66V ]bLzY+.pV0'{1& 3KXօxϭC/?v ~|TCbb&p0[Lc0b.kg__][Ln ѵ8IRy `?|˪}Z"O [?tf(-KZ *;V'?ڲ äW?svN/&!IQIGi@nL =wiOSG)Xb>II_؛E|*\x C񨇿#o{4>~+ ǯ'[CM)~%%ES6bt(޶U^# bu4:!DЛ,iYi)7^_6 M y/)KNlxmcMy5<|u&F}{|jܹ WPuMt,,`#b^e;[)]4Oyb_iķǚ2"Zf߱aeOc%πy ybCIb=LʪM'y 9D?݆s V, RD(wydNϓK c!H6O,uV FzFɞܒ!P `&2X1dgǾ5nMq6e)gF7Ɉ+Wlӗ/zW<[] GtL/=h֩~ ޼bK_Lq_3"=\ԗTA-oߙOMdu뭷Y;x裏]X,K~?/XEZ5C8t]u o? nv4^N+YXN}I*It#&}ϧ ^%-W 234}R*2c߀Kh:~rE]/,mxY7pezm`#(2rx <5*)S]C5h`_vMX" !b;[}4eZb9ÉKY#m":1s\\Ul,rwlO&+r'HR,a&1Um`x4g&H0`OVI5dЈ~x%cR6f+g@x (6/A "ICՆ ~&~_HN0Naݺ TJiҽ )yyD6.>#犢?dų;x _ZLnL \}Gf3g,&HKpl֬뺊,޷~9}<{{wF-VW\YC< ѯC sdQ 6VO+(t%̟VyhAߜԕIn]d96m+s4 Xa&W*y9M~.ԩؓO|fb=}sXg00i*"6<}gAJysI2bSo|;$'G<\K  0 6h na[?~"<64 9d{-h _Oo>3p'0נBqt,AJTTY.(}O'xoJ DcoUH'7鼈;y2Xُ >tn 1=͡ :\` OJ5L4Cb 33ug,DS$ΓWMآyh!IIqQ@ijp10Dr PzuP$Wu|'׌GNg]zfWfcn6c@\,u+Ϛ@dj@xġ7<أa&&x&1q~2]Dm"@y./M C1؇/%XCMa1<\QýE)Z*6QгgTO0I7bB.$oIsf >(9YE}A:O2/+~)ye*EoYbcyAV{a^\o~5 "5g͚W &k%y/oE\%ya*\Y2R$ؿ-k 3Fr;5Mki 5Ыث+o,*+7c!V7dHnwc4.Bk!Ҟ7] |r"^?G}٨"I 6Ux{:Bt|2 #aQ ʶ jHeh[ynڤ^M^l%oJ@+RљtWzweܧ[`暅߹=]Ђ5TfKXeXE));z{rn;Ml6{4L*GkGar. $WfxY2\sr29FW xɤK`hh +(͘H侐zC6}\2b@ռlh:Jͭnvά-9%b_ZB1TiX=_^6+hjhf%ASW #ݖe4w[< $WA2dI+6 I鳴|yάoQlR$p K_~J^&W6E"ʝǀ)\Bn @`)A씴<&6+&1kbT#3Ǟ%rp6U0ڥS HD^l*&$Dzg2Y?&45A^ܗanG*55`t֊y$}k\5f 7edVdU&bSגb<{cR;XP0h-QHL`ZL9^+3>dgaz_ܪoN6kammc[ Y:O/7~@o+Ok&ɣxΝ;_|z…|q<: C[SXZ e/7VQ6i4,XErjwyS׌""shR ʰa_B` )UCI;4P/N9{.ʗ H6X''Xmaej# oH$-X I>L""i-`A'>3.rZ X.:Jzd`RjX1!lkN[St~E ޺%ɟق1D,KL(oZV&^1,!"6dg1qkM1/kѡg! tS!r.Ɋv'A!&,km _"<(O݀WZ+ ?|59 {3̤\gӾ')~LJpdh,]-`L!xXy} N{$/ 煉\R` }C{0۰Ao%(e Ón 9>$}1OH_D.i@=rnWq}'5Nws+ZjI`(RIzF!JyLhTI1yUObm_}XJ+%arA%1zL T`F .\BTUV5ލ\%&QHn\݁~Q:9,#[''}.w lo=e)WNbhM_@<*тfYTMc1%23pRfJ ̮S"SC IBm[ ՘($~%̕š-xNA ',h[JP%yQp+nr%5}*hyc# a*끽Mk{] `A%6%XA~0QjH?Ķ5KO,drÕ6Ss k3Iw^017y,'\t8m^#&!ȤDe-UNdFNە{]UJ@a+fu,Ҹ(7)w 882ѡ$[;>{cݬn766U'OQB6NX*=ʳ!ȡG%tUUVVV~q2|ܹsg_mۮ?~lt4yDΌej ?qJLI"<N*;&А6RB5apƪe|!x6U/J22#LL&EXʢ _723ǓuER~.a=&SAR9c<{=DzddTқxId>˼ g`d0;xlfnakk)]c\ םq"9(FfTl aF$/fĺp0#.xv$0"PR|QC6搄]d1@Xs.`jýuoWar)A[WZ W }m&yƑU)lvfu9%}.%ڤc5 Zah*VR1(J kD"4׈OxE"nCQ, D2,yW%TaHnE0$rnm96\O ֥dlqb\.V39vM6|>5]dݽW2ׄJ2)ro6F>h`lf5p|#¯<m Σ^ ov_; WS~9C;3CH92&wd>8&bEpijXRGD`PʍmmcjdOk;yW{@R 8 mcyAd{3F4MŅ<<ӳp {( 8e%{z1C㘠(3zw%LP8j{~QFUtdmF#il1P2|yg(WY-uS_俁~./ ׇM|qzoe|M7X<^__p;wFϝ|uÃrmk]MXYYQx{?=b>.yvg>&+MMI.7mK`ejbo#| Jue>0 (يRuGrSpht^ pldauhPVf d#~ L4 }ñ#;wjӄvC,N_''k-.{B{KIAMH a  'M,U[)~rR[VRͬC(@9;FR HYΧ+# ODˍ#~5kpllpmpOslCQ^WD x{5j*rxyؑe^Y*(QRt>yqYuu>f7{fsG$&P%# vy2؁( A?رaA Xe#+ %Jdsn}w{пGy[:}y6v.^|suzIBWX( hD CZWeMtbzPiW9&cXӛF~1N v@bZ7֔(ފ!djʐ>Y5u7=OcE#F_r81v\#V1Zi[{ ]/ڳ&T E6}@ E NWl@4;$2Ǥbg?8fYgU]k[1L֍0(,0'V\Wk*C_dPS53* ^ZW`#p;SmcfL`"[K bHR_ 4Ieyn;>&_! `*,ݦ`3s)3vh4J5b#Ԡ Sgzy8Y^IY ,KqZ/ e 7/p5gd+~RխA uw5/TB 8qyO[Omzz0̭%p璗B\;,atqk3o۹nll\Cbi:&˸oWB2 7& ˒n!''LSx ;'˫Eh2SVoYUU(R)#3@Tj,G;&PD5İ,EF<6K-^MRm1X [چ9*i%e I'{M,gOԔx#IY  KL0-),Di$I >/T*,'pFYrO</n'C#:u.53p^@69)r>'Y%>%3{TRm%Yas$a4H%BہX *!}${jHzEzilUVcBPhs|/IGbZx`j`f 88x K/H&#`kyNz_t?U܏p݉7}+m3/WR-We9Vw.bbO0UCWVy}J^L\ɒ( eDI/}*x#'; `]*}8s[-{N_[^"]sPkrO~c6% &G`4Aǡ* oS7R;$+/gg'Yd;lA{5y9::VK&qL6ԅ{`q8x%o%ojXG! A?:IƮ@\qKuv˺rzs:2Zl %̃=H6qj@JdMu.9H(z=Y46hRK)j;rx,xJOXcIl~hڇ~@m> E8'1W:Ҭ'=BRd꨸҂NUr]LTNTƖiUߗj4YY= 4+t-D^g/_p\2M17-(Y{5br,` ]n8o{1-f~ZƧ@֪xTy"l&M̮NN # W SKMEE Mi?}0&Lkv窸هřg.OotӟٙƓ皦i[Źzrl񱌯 k# !a /=|~M|9 t:l6?яv?1|8m`OJ6Pd֘U`K6&$F" %9,VpЦl \K >(݆ɼGf8FVO."V!XC Җl| +_3"T]\Ÿ LPbbKFUIP@PZ;o0)eMqc $`AUXdV0\{\) ;$&TрMoّdl%CXesgIb/ XH%<>kE 8q"(f!$^|0$ EZ8dm?%KSMlmFO꺋ŷ<۰\$B(1ٖ%|Y&*T7)ad$F'I@KjF=岠0\J(d 8]} f <p=~9-=O`~Tj38eH}4`4 "ї]L#RqإsOR=JtFV˜4I(eK>VZQ;i̞Ok#ЍȀYhPfSoN.c9@J1ZK TZ5&e;!R4qrhvM 00M7ΰho ןۂ/ZxgC{Ewv 7-c¥~ t9I p]7O"`S:I^a4J|'5QjY٘uVX-k0̠4FdJgx/Ray咃?~ )4m$rc9g|J0q5;Vd e+"(IJR*He=&c_З!3Ԡj^*&>B0w{!$1ϮMwĹ0~kժdjڣ-\o-R>IB(cX,)҉=޹,;&p3"hF+r |߯ 7Z^hj3c:6Q84ƿc ;3j#UA8#!PThyL q6۸x>N/ԦOoYz_`PKt%HJvm咆ç4EIShy|~f:]v`!T k=/[V1;4jQXz9lC-!?7]3s+bʿZUfU0h|&+RJCLbrB֘񱔉jR+?dbӠcd  ӫ[ RAT-<4K$Mj[j6d)"@೦7V$ }nI-$@2`DP8bbhR1.o.Ub#h^_*G*>ܥ/>{sNƣ7l\CNUY: `vmq:{=yOy+ܥX"q$B1iIГI%NlՌw1;g>dasy =y ۋs4t{ >yjgMJ $3-R- =W~Q%IS&h+D: 5 "$YC"}Cш?$ 4G#<O\ _anj9 -MO6Bl ̑c*]_%f9-|!rxo'fs':r޶ɠgI#DI]("a]eTVB(؉9ˌX S("#Mv%mK칏K<#a8iDƏ+xo5b_9'ݷOrDʭ٭例!\8BMd4 s~sН:ۧKpo88]p ޏ b;P{{9d?]gN,^Hb5_ _)~&I鰦"!,6TQ̻V^<ֈwɾf Źx%F- CcQcgagv8WŎHRUzUP-)H)_Spˮ/-|-8Ew>fL*.c{Xi Z\0k[M_]P8w}UAc)\K|ִ} 7=y9%p=2Уg퓬vlħ'|rl8 *N]s+,O]_I&%ٌYK$"vJQoS]ڳTLˬk$Y^6Ԅx WUa彌lļ~٬Tkw+z=,*ru5@5^uRWkETB 2 qe2SIR*ٮQHWgSN()?"/׎gͤkR$k*Tt6`׌"s0w77iazmi4ihȫyeSݚ[K_7SBˁ-0, ӓ'02aAs׆Ы""c Dp:,^]e/I >CP'6*v:n 3(* V~s`yMS)G9 -]9V (&H²[ڿ? ;;o TsάV+K@& sIuUٳ3쿏ÇzcַeЃY$:Ġ"g1k˗>.- @@g<$wa >=Lk^l|;Xϋ&a7XŏǎQ7,$#&E1=dtkXji4iwLY>._rq(1!Ǐ1BB)ILA#ds={^ ;{~2]XK+O~Kʅ4 UTdK9/OiPE@Yd76dIrqEr\KLk3jh ZyY> !sJ(uj-RdLaqRWL-@b(-]g1b`"i5(-#_ۇ8b?~>?mcwl$`s';O)a_ $:(gO剫$Z#뺘JJtҲ) =IWxӗKfBRGKOnwl©4)9oU92ÜPHHjͲ枯*¬1=TI\0Ns(J^uA`rN P>%ŬΒ_zdcv8߷ΆZι|`2܄ RqGbƙ?J?Kfp; &T*| x%RCF^ 8X0W> ΌoT;FNXSk2l8ePϲ~mxj85]gpG2 엗+f]Kp,-+96fNd|p?sXgSp0Oaxm_˯j͸":bz&fMdBO#|]jK KL}erD8BXT^VSUh&W8iXs $( u PF!%Q}8/5+S 4*&O@>*AA :Tl]I A]XY@eᢒk:zF;'F'm͒]\ ҕu1s/r7%/gFx'+@뛃j?XČᕊ|n<4T&1pJCW^.5SrFG>+_bǠ/ڠ$*)g bQDL%2ȃ Y0%-%Og, J_JSzoxG*ap/3hNJ8NPv˲kzL!G ~јIIEfq0DBBֵfІA 8TLDJ3 a+LOg dtlvl;&n9ꂕ$qlz"g`f>~f3t7ǿ6.|U $کέ5' 9nk,d/Gn &&MxwB 105)1J`"yxuǾ?`*-%Mi|&)#Ie%~~#9%UD$/`Np[qr}Kg/  Jz L{$M:DU[*"?xnlPh3srQ1%ʧ%@IOI>wdKZQCwI2Xd%Oڝ߰_yx&x0= ?xnE"lTm}z{<(dI%U&kv@EtAb}Ph% 5cBD 7W؏aqY-W_s? qN^&eRAT?껞" ,EӽdX~D2!˖Q#E-0b1 CS|ZzTj=\zu1GͱOܻw{p 42_dOwNUIy 5B_XNk 4҇gU@I3&Vag *e0E\<|7|4˅"ƔΏ"deG-g0 C.ZW) {joը>I T|#U\frNO_3q>< %n d;zN駟6wqGM|~r|s>*Rl1h; 8, !>b$ԁoKzq0PJ@XBkkA7W[ϔ@.7ӴTY34y35Sb+:2eTbCر*ɭ^腞Lj`FɄPy Օ2zCBʥQ4}ykJzTksPfE%tǂ0\3pt넒Y#]|וdL_?PIUw DKϭYKu4nЩ $ĚkPȿ#c;HUaV w|ta(OasYŘ2XMb#uբ"y1x vr#v3[_ۋ+ &g .ƩqGd젰X &?fRтfoL8prCm^UNɉ1IТM+J. J2d:kd6ayf#G:Eу'n5sc۶G}T'Ϗ|zoؼǴX81z`AȁTw 1_bu4$ӆ&MlJ\"$ω.ɹlu b8Z `_'MN<N#{Х]N^6%1k!1 `!Ȳ18Ք$-$`Qk9$ Yg)$%J"I$eAAZc=BBcPWt4_y؎m{3xx n<&F@kHMd[FngDm,3M/rardLf {8dV1RGEx'eo,N:ZUm= {cbiҷ  ,S_6}6.# mŀ.㭖2ˮG!Ϛ?aM׺r_^Bq%{\ĸV~Tk7˟UgI&m6nw>~&}DL I(:7%j$qFASX9I0%i=$53ԟ;+z?W(c+: N)~Æ,ѨT2xIN^ǑDaaCXSʺ Zkg aj_0Zk99oSRW@e imIFI`f ‰tmhawS%0_~S7+^#޻o_v^K1׋2|l 'Ya֖]79] S0yZ~д%u9.Ν-~_i!]^ 'uqf(m6xrWߠB}<UW*i}rs(2(97҃A@=w>{u.Jd;zN A{G97i?' +C>#t;W۰R8hk=)fuhfi_:|FڰLż0OTɴZ"Zch Ksy)+Յ P Z׵V)Hw3X#bqӚ\\)W(fY5}UĔxY΅K̄o}d XAib< Y -q0ڼ+L"B>')Qޥ~3pzU>OnLH2ɩK*&0DqR /$jHMa.fDc7לdSTF&M$֑ F![%G'*6]#jnyjkGs B/frR(6x=bh3%bIG2؄j! jAQ[L b~>OC5Z@#fH}3Rxq pšAbd3CeԲD$[Z<8e² YEM.EW-BmɀgZHy|0^){w;u4;٦浮L-d,Nذ0~f=v,B)8_:7t:G/ՅsѼoy aL!6}] @ 0Rn:3$DL0aG,`ޑT\|}ץ[岒'D#pe¸iqNI׽C2r2ωҨ2{5 Uw6Lf)A!1H ϧ5>_Ÿ1(xϩDn M$1$löA njr޺⾃s[q?㬗x$@MڇepLJN@Q5=j'9Rԃwˉ*'D|GخbO4_[#pu™! H|~ wDSmc1RM W Ҧ'C>JNU%H8!۠ % PFfet C$I^D{aU>vYA<~-]97"I~ ן3͸cTJ1uES  {i4k`rzI+++}5wd .fo^6K{+G>4Vǥxr×?ynq*}ne/EY(0bYiD($O4NZUW){ů╧JkA}dONě"ٿcZI$)V<dݵon?siwڴ7gpn{ 6a&{|mY:G!be *l &jDI(VPԋ xe槷|PAxxz(Ldr?@ѲAkzCQlA]z؇Lr:fP}. u ǐ"=WK?7_o~;'+9~6sC]wk鯡rL=B-z6:{=8|. dh13[[Th$jXji)B5 g+hk x Ԟ.`X]Tf ۣ7̚j REc-IR 1:ϡd%i'%l?RQ-j2F-Khf,:mc8riLkR^ս_pwD@%63XeJ kg;H+wK J0uSҿf|(d<{:~\,ϧJ+J6tmBPIsO>>λ{oPI%pi pK@mdQ ~֭H+ƚ[Ǹ߂,6uPiB%EA l1ER2|6?ˀ'׊A"m@SeVI AIdلy\.}H @ub(Gs'@׋5b-T_0XO]895;nLv&)Wq/&ns@?!k'>\%>}:l6þoDϹè-,cRy aQf$'䙎lJ*om'/Y^Oc705)I, F߃ljM딐FIE{Mxc>ٲm%}N#Cl@$/$.DeDM(U91mx9IΔJ7\V.2zG]TيIR-Wيm !e85erKQj[Jn[4y@^M'E&R_dwt=-GI-_t2[D@aH&墨ltl9ZVTԫ\.JG!&~[@)^b?NOx$;5ܸ3=x-p~ oK` sbs u5{#>%X+ۆ/>NCN옩xE*-Gۈʱ+/N>K*C6,BRYVrjwJGW l;!K|w;73m׎cxc?_ y{2QK( 6 P%T[CдO:)6Y*KrʘZdyg(W(`"  ϛ2yze(ymxCYĘ U ~ n[t:dT6AS9SޓJzo_[_X/||>rӄW̎\~89.2>Jy8 WBb<);wqCF7zL\8.)D2,źa^/3_;ozNdބმA{D W<}DZ}00haqp W^?T q=ZʖYTy#F$C_ELM/^*gj-ZWd1fmR'M3b\|~)1ߓDCƴ9)U P˻*&]14bMό2Xɋ%2eT\Jߠv[[*E2I)6KcU4Ymtd-2BH7+ KXakJb;D*FUaʐ^% ̕,]zm&F_R[/>V gY5$v=ޥ>jt?2C)۠V9_,OF6\9[AUUh7);5sJLݏُde(_ TM)qW$0w;G(IRifߎxPƭwY^D%C]yX"#Ix3R 13A*xwe䤹 Z6_ye_%icLcߠϙȪi,YΡ˿Wzŋ).:<֔%BDb&lxg Iѻ%w=y+ Kc֞bp-u\ew !6ҷ: .2lcvWkJzܣvsM/pL VZ4L\Uj|jYAZW(f9" +^ ^>h1uXA*>"XP,K94vp#])טBOXK4Qkbb1ۏ/`:܀a2nY8٪|X)-Nf,^ rSAI_xsx?2{o3Ѭٔ5uwP8)@\{NHz<}z/IUډ`#K[f_) Q`2__җ:Ndsjz/y՚Gl\ ͳ0߿˯_*i,m$ة||Z+9 1ԓKY}RK?U'}RCEf TtH}ƙTUVײ`rgkYT ڧ\բ@Zq]I n"}滣:#)=GݒՔ W!_FԵJ355FZNWxmNGP?kOW\#aF3'=)?zPE"ġ{-tm=T\G|_$-W+w,?gx ;D5')ǩB5>0RtxI=]u)K׽au%Iyvt^sm*: uj Pw}֕>iRપYL0!A7)LD*_?!x}V :` P[W23/؍hyv1c^|E6N|4IIO\dwU%@у,ʸFߙNzG+9o~'Cڔ' )\cG?  p̜L 䟃qcL܃&N2 'I4lOƚmK"Iɓ `KTdELRsbHo|)M#sS+B八,~{|+ήQ̈́D~_p)LG+h5Mpl+SFE :U!)x,Sg{B Q%)RFYHY( `Be/kCCq/2J)eêگ0l| t]>p/>z~ނz!r·p9:pf3CGPw`*VdyX91]UWy?O,3mƒ_y)o8bVv~=>T,>KGBzn1J[6ԑWlr> ('$nYfw#}^I%V֙v˿7_+埝Nd;&gy7qX}e\jU> |ߨps&gy5yiduۓ{{%J^ ںDC3y :_ؤIZ,!x >5$L+p>Z/d@]>˩6ZE.7Igxnt b%>v(& i;WN,{,c*@J>SQQ:H`Fx5$B] W@'DAR)}]$~\c0Ju@VL֑JQ$jOȻ`rpj+ӆ۠>R=HOzA"E,n@>UgU9*L*h.s J=&>^_0U0 I!l;8<׍Sd+:B3dBIa}(@sz ِNmfdTfIZm='"<*OUI^W)5?0wO5wӴÛs#8㜛e߇ ' wqKsm?f7>Cڟ-9Lշp#GqK7j"6i)񸘇$W+pfoQ|*a9yl]р>" tCa6ia:jxRfoaB`Tb 0 w7&"6@&kEbCKt2q'$d|^Cu-rEJ:րw Yq5>WzK01@ROu4ǡjp -<" ۙc@%逓Mdð'HB e]TeTP6+V[1n'֊x\\`fuS$XPXPP%cg&eJO@K|-t= v㹷R{wn೏)ɌFiiF[2Ip f-$j*O}1٘Y>@ "PZXSOQ)DI خ$. pLZۻ$HhtݷL85Plrރlɲc sR}Yx%KMY?a{0vgP0]@ƚ"Ȋ`0ڜb\!G \CucykmY`vO|chr̡"g+OD:8oKOPK@ZSfc0&P{’ic XɋG: {ex2Zdŝ7.ɀYg$m&PTJZUAQ$^usWLZw'kj^3^އ^:0uy&|mؙ0UڥӖ4[OlC)TĨ~og>zvb[2HUE"1(FYA)'P~=`b;.W{JT`2vVE9LUf xCܧ?Ï<_Nd;gЇ>߈O㩧[]'ԉ>I3>Ӱ j[%iGWd^]E%BOh`{UTCO:阳Fk~__>cj8^Lvi \ >W+z&-}R`vJj=9ZdTEvfxmt 8)R\uUnkȳ׋6%cbʯ+m!WJ ,TԖmx -h30HL7|P vbSo(*0yՏEg"A#P:ʆOdW#,+`K%QhuyTW Hvrx3uۯmln:Brsf+irIAI!z/m{e<퍍3<'m0nƘF~>0kgoy!*`%O>=9(FJ() 9(ߠ!(Oޤ|,$pc$WMÉi_\,%ή^|< Sr$e3p g8-X*1&T~d2,A*Ę|>&XlIX[mrSK,/U$>GaI$VO Gaup|soxqcc?8Gڀ,dv$ G18$4hrRa9j `@ )B~I&A8W3$ȚuKlbhFvd<3/ٛ$S[SԷ=eQ b4r f{sy'xs wv:19G^{k7_=aC=y;,'vOdO X@vT*aR|ʕ,ddMUH1ިqpx|noE8l'gm=^I?djr1cQj ?糒W WBC)HWO+J?.ms>I*dJzrbQrZg H< 3Pb̄IO\$_Ɛy&{HM JadB0hAG}WA] OD=VVɿ1)x]m 5 ;Tf٣=&qCϞA||4mood2<aW1YtG>%g]d#wD$}ҡIg ~{63jv6ڔLŤ)7NTA7&7gLDPA3Π 6;>G!%y8 y$'lL1x0`%F6.M(qGyll y5`.3I[Xv$s{ce..؜2T!25IfY&XX9a .vX^o7^=^›;|3JtJӤJuȆ^1 ?Jĕ솠hۘf+X=y"&>' L$AjXEd< l dK2H)S``oso‹o5pd}?tO{؎1l, (dGY,#$ *-ٲ)D:.c (Ä1!Ƕڒ? @Yta&2R_Ⱦ9d04f&]7 5 ˱_34N.]φB_=Vcu 9IFRx 1AH= L/K&WI?F@x6 T d1і𱫠5o {CV&g)=0Fz%َ Rױɠ_PEJ&IqaI`Ṙ%1eъNqXPƹ O2}8v?}3hq;;Wاlxå8Lv*rd Rה!g KK+p쫜U ^V؞bҤjX <|$T$``6O޻w\ 1G؜7 No6&6iᤛ?=8nح?y|xFh`c?{%e#s ܘ3 x1VeT^/_!c~#esNHsj꠯b{egLbj!WVP8&ѝwa::HR*p#`*CDk2>+x6@Yh3'i+nzQhɲa䳿_Lz>KYB1U t\7n2Y;ݏUq܀9 A1T]+d|f@Ms>U,BH '&1bG-RՈ7|EgNg"4X ̌1:eL(tЗjV L).UP62NVŨbAd(b<(p]ՍzRk_A>؇Z2U>J={ u 4[> 1w[p?kP*7M(,&dJͼd#?IA' Fً)( JŨ$\zUh}}2f϶{x4ێC+~x!NIriK [}y/c 8yaBm?fG>wMkz?8L7/]슇~3E0g[d`27Iz9Ili=1w݂ ~|m<$e@>@] jo8KL!2@Rl L1H!N/|0/ .'&K\'鐙 w$%$Afgc*m d2j1@ eV)"u$ $?'F ߔĶ ̆nn/%^_C%b,yB`/$AXLIkiBIj@P$pYUd /c!dЉ@!xHjÕ,E~,If 3?N,yX{bvd›KK[+7t q]pUøE<:2)}Y2NP탭}*)_'qk\WdON'-하\ 1}|Ps S:}8)s3]cG΍b{;x(+,u˾D!DW}`,bu*>dCO h]o'Bc~<ɶxvx?'}&Q WҼY0H d)BfAXkBI-)oD 9Uc>:,'^GN[JI'₇rR!M8oyCI%]w918Ka|ni laF|kQƿWf I#^CVdL`ƇSAIeDz^ٛ+*R-y}y ?g_`=37å36u\uA\G7|ݍ?%W`cxsVc`K 䘚d~rx2^9! 8_)(V}!AK{3CXNWR@=uӾd$v0jXo/]q0n[bpF;pf/bhG4F%T'HėFːՓ4F|qo^ +?{ 3i*7f^9.P Jh!K tBo*&,F3e*,TY!P2 -H$>\lJU<ʇyFS-|E*!jU%~ߣt=)T_|Ӏ0~ǹ dCS0 Ef,0< D[ 3>͞7UE-3K4!$_Py$6bLqh7ްO.G鶶˻YۧcfVVFzIiSzUqJV*e=l,x''UZcO G}97]"I1h `Ǫ[%S@hSmaֈPBr"Rd sWH+AKe]c-;3Uyn6$H&ElX,h= @`d0 g@$A q` `8dhؖlˤDQ28ϽRjUt8I>h{ckc}TTJ) EC2qbQ72 T^NhR.MXkYth? Jg) 8[Mhpmj .;xl1 ˖}QJ%3N%H7UZEs?  duf^\2 :{ze6)&[8HE:#Sԥ!sNֲAo[}׿ JsڋܟTTf3ӓ:qiu?-%6܏ld $yJju(V%UU9C l6yTg^:'_ӭ1A9q] &QʻsO?z&*ȫ25F|.Q̀aA' #Cj\ Pjiͭ :,5Eg=Cϒֱ|l,rhfIe2˪Dբ0G߳u0nॷV-T ёPK-6.yNa_ls$g"yJ d Osiy,S3AJS#irk^nk@^UEPueC8x U*Ov-qE3S;NROi Ƒ .'?כ} ImX}c]{p540j!a#'PRmHjB*KuYO{)k~h$H@Q0皥 \㶆jyvn/?:G>\ilB豊eF\a(6!8K$K^R3g7W 5 A2Oh|q¹K묓sw^q^ {9A$ώ2@O$jmjs(E[}!gצuo9G8  5{ӗ^_pnrE#LӜZ.\?wܣu+{*4Pыf бL̔}+a ,TVDp7QE^*Jl6 B Z@(Wj^qmg<,Ob{mCѕR]=:Mg &0ރr+:~AOKR JOtP򵓀V tM_ AEY)t]@kV24CANWGUV{C$wZ,ɋ,E53&Sw)0NJ]S/*SPgN%5`˾K^Imt/D XbYmI;}ϩ%'YNϫ{H ͆QRNP2;fЋNpk[H:-u!s0 :!ip?fnUU4&k{yc4}e8~G-|3?D ~p/V){oDh/S&lҼX'ߞQD#4%6 ʪ)AȞ;v:E,z< Lߨߒ,~ɵU=LF51w8T۠Wۘ0\$%,S%p@OuB& P"ݤRud`%8٬&Ue'&eFrJ/0kJSlp8o>*8ौKkOKs~}:L}8έgsp._[΅~Lƀ<`DIG'^_@^NIy|ځSs ـ&^I-\W.Q(ȉWRUDC ^|m?wq3ޣy T_|`呇 ga}JUꖽ-}YeÚt߽AekP |s\Xg0/U_%ABxD6xPbT|JK`K,6#moa{ íc 0PW ~4|q4 }JlBL Qep,y$](Vq`ṲL9m9GhNgc#hڀ}{UI֚( ܭx%:\'}"cFֱ\A"*fhdT.&r9%QHIR[HЋU3&䥍ŋ$oi漰E4EY̰l+3 Y.4AyUYRCшOQl-])a^/4NMpe {|45k icv&aCgyEV,*qqB*p٫ 3H}3kyGXƗ~ރ_Ѿ.ž%υW@\{"'e@Aӣ94Dg}Ajv nD,]iϧR,:!\w__曝z63|qb>O6M[ $i?OK֖ ṋ^a[+7sd" O /ƘjsJ>o}1,Mgm5TYFF-Bi.A%Mcqކk%p#>&tZ !6۪JaHeOf'4k"EbNҔ>prէ/*@/>1 Da} ]]Aa+1gp2ӶK6J4w سB֐6h"5Qn0/ʣl%7$m*0UeצfP -q %] xxJuYVM݃2XEwŐ,!fYJ%<䭁~-Z?f'S31"%F<+|rC# \L5T< gMީ~%* UZ:[jeV4rv}/Np3y01Lۭ>]jq‡^{M}!N#KyP >/L[e 4~_=݀<?ښ''cŒnWI> ،$ V_µnث"hЎo;8nb"p0vNJLzI2z\ycs{ s* .yϢ }r±Hv>I-VM_yďimIsLM3fI/&̓@ [T&![6jڤ?InuM1}MĊY!C82Mq>֠ Joijx8CMQ*|nβhƦ„c*I ,[\d~Ng \Qԥͬ1)Y1-I[dp/k黵yO'@e+zy*,`AGdY4CT\ysqےaI- UZ%'ucrE o<+|J$ SrUY ,:0wzΫ*xzнDhd#wOb=~-}w0k\Mf$WZs9.?@8qXRY)J,!d<_b?%=Q sZbD ]fP:!dol\9$ HgtbE$!bLeP 2eWϖe@kw\n#P ¸ȢT>xq87m~BT^Cdz1)6!>m8ϥÌ+f,c>k, (e%:݀Ǜ9gז5|eC56xs}d |:AjWU,\XQS{J8 `RpUŧF,ù?|kڒ89X)Ji;Rt>;g{is}x}< Ye6|gUjnBy^~gs?slg<$8/m|DR-3( GMwa8jv VGPQAA(ߍ'@׀sHbu?NPN*6y `k8+/BAM TIR6̀,ՠ*2N@t;"V< t܀,БgIt+_r6O+':ZaXaȽ5ӵF{!A" Ldj)c7 - iC$ueZ3NM4+jkQsי"6cȎ[FtҾ.8*}{7QjHl*K"_$$ y\[blCw\] w%[ l5nCpб<$7Ns{(53wg{q{4#O5gAkFlH陇kAƓ\A88 j%)_Ox+>ocj?~FO x衇~'06οGUՇ[[lmJ^{i<Wϰ6DzZ* %}@-ӱ5ĜI@ʩm8Fg6ܰO~ bP ڨ_88u$ Z?@[U$st tHo=18\M (Alj|[*f dfj|b0*2 q(i߉8;0n=w솱wYr 25#hySsbtM{2'bf@<<3 yDZC@em&KӰIJ,G65Pj; 1cBw9sYr$'G4gbL$Enz,qsZzI}xf=e@YR!rY$c#VN!)bNs*, ~?zi(lLmIӃ%mIg;Y. yjz塋sCٹ&J>ksGSahW'@K$HE yX*%%pshGȀ 0?gXJOE'XR 7kYbw#@9(6R$' T:IXID^CHr 0c9wov*%:#|(=nV|dWӿ'&̄ S5+9OD&ϘN&Ik1c#H̓>J{ot8 x(rҢWA4ӶsĹދ;(M(#^k I!Fя~7.]_3ʯv08^ՃI={դD&JEY9ɪT) %4MÔEPgl^La2$J &xl PZ,F\Nd|q88i ɪ%,b d89 lYÉ~JJ2i@N]'Ll]JK 5'إ%I6|/aԦWQRYTZOq x#Ф~y kn;_'S 10a}sj S(PY+9bCg$G9Rf9ώ&楻>(AP2ť7Y&xo ?7׾f? ׿"f%@`0֏ڂ14`j*PV Td;$&{: zT ԦǚRFyL6y`)I\ٛ,Y7Ɏ~p(}X7pv3_{ޏ,WZ- Ɍ~Bo,Þ;p %*24vA '2#6ʳ` S ~>ǰpao_RLMoa2` PY _2orZ,|3xkmÅ~@5(l#n:HOw9(OW;jIFR5:u^)InU}%qW" Zd9CZ\؍}kK_͎g=4̓be:Wnh~n/ǰ8ˣw]Ϣ$Yf0p>ջt`ۿS]os9`|!ga"jm>Ғ`@k`CN'o =B *7O¿kxpvÿm~#~w.]iL7C=٬?]¸%c υ*Jq@N)`ٝ$A%UjGp0aگ`(ʩ!F&O r0_&vO I6 ={m|^E'y9d @A,'`F߃"q*b臓㓪M9]7Lr_P9+th&"̨oEN'4)z=\9Oixa0a"(mq$P (v#{$P"!"G I4f,%rX^{*`( Pqd9< cjIVI"]S"{CT)N}J=s/_`0L,03|O Jq߂=*j]kGlΉj+V@A#̡AUG /`̄ 6, g`$ s+spҕ$mQYFBr]8~Hf+:Fb~ wGdy f䥫._[m{ul\ Gn88'G38~4iH,H^ NOome*j~'A&*PL&fN^r\&J('wf$dώX1Q.l_~?c^3٤HeW:TS9x<'6+wH@,nt.~ק9" c8зİ19ldI6s\ؙRJb:Ȍ. \ɕWQJ2m yriq|=؝*@9qy%f/hE qM%[p3^q a$ S -fDZ]B@x$uƉIaٓ b=SVh{ed|L@T?kBp w҇ۆl*>p {L&cp 99㓍lc<>wi u+zK[fib3ZO}9 Qu$ 'P?M*\{yu )UpBZ|KA@ 7%̀-D"_GK]\-t ?Kt6 гylG!|Yoy)hm?{33X`y4z#h׋f1 0E>o1t} _t_Wd&=aX'{i2kKP yϣQ-Ǔdg^||)Vx Ҕ\u'|k5S$NShE7Ni];*VHYAN S+F11"ʔ>G@M.|WnɃաo( eͲˀ(K,8H$*]3|dvIrk&z\# ։ ZZbƲ7/=|^t{Levg;$w~-|"Lbw(e"xD?-<~/9yVL$pexa}˔ ۭ4DxJ?0 `":IeN7TLqR{qS*gjdmg.w0?>sJyFITxҎAǸ",α)x'G&\{+#V{Ga"fN.^Ugy !߅Ti*hdӧd9);\./$Tm^pU!78ZŽe<(,B^F$Tg4;0MsBO+&m`Udrul:%=?Kynoj7G^\4xD bEpxt}\%46`hId,f,h {0eV@)5xTA3_fְd Ɗ'oI8SA)g([׺*:{cMԩhdw@Q'΋N+W4$9':iC_ BpM=3uPJU8Ĉ0&3R>'9UQ# 9pbYk(.$!ĥO!+(wgw^#HJ1m2y4|3MlZcCl;ae_kk~xaBD#iJḰ?,3ʩ?7W ˂klbص PJAdzONvq|D FFmo}nށ Fkkh!T>E\jObBsYՓl#̼x@5KJI.2;q6i|Ĥn: r"o8$,(i wJvT}(i^UI6%#Bcuy|w#yY\ʯd &8Gs4'<76*pT}lp.Zl!'UUuBћ'}WE@1¶KAaP$jq– zo,'$H7.q@|lra' kM x 4bjK?<Y|xS5x7%rZO2tuP;sg( `x(YEb9oLo5)^#W0: P*mNeI\s+}AO"9C,Nx .JHbF -=[;Ynr#d( 0;tV@Y0sCYlx$@a= J: L{Rx>$)eY(K b s1RU0o/ Ou*e*ƾ^l]flj"+ˎwRC JA칓eѲF?Iy#u2(L5;ܳXG*botMs<5>b.?l7_ŝ^1&ϴm&#$@ S}qM N _s6;q̹fih ߆h4~^K.]_,϶YpmVmpCLt>LP->ɽabLذ8\RB@lNzİRI(bDy ~o :%=J~:LM(wXBKMRM Q`'X. 7Hc7%Hެ|磴KMp%+fED)&3=*^BqXĜxCTxEl;kuo=_ݮc"za觔ĢUAI4Me^/X $UeSQ|G2e|pnޚljfzd!jCI/vjvN>8N$}<sJ*E\TwE2kO k{-tf.N}\oX2pqo o7Un }GmYe&Nzzִ'Il %+YRx|vC֡Cox*~<ڧM|,ֳ 1(.؋*cp(.ȆTFX3 d$ִ}yx<^es"1cfx8T$ t]fYucHMZlK%Nܬ|s^'S62q‹_XLZ{f _`g;q:W'?.<KaO> 'A4bÇ9&mϥ.8yPzJ$зw Bwxm̡07ݒ' gaS<3=x/=ģ_, t猰ORB*^Zy|ce 﫜89+,l:|O@)MPq8([B[L? fRa}YK8)[.G; (l;W%`p;gvsUt>eeS` o{cD,,9k+)=:RygbȲT˖R s P$EG8Țp/~3?_p6 гyl? ~]YB_?g?ͧO{t `x4@Zbv,\nk+@( )Ta~7}]"ɯ+BL.m#:)$u\T ,Ud@un`/a߭mӞ8SfB82&8 oYXQRo@C{O 3VNj+vH)I˹C~(%ЗT!)+R^k(>pADJw$&@ 'ȁגCU0r@1r"H4 NO҃PWՖpCjcR|b~ԑbv q#6U9vLp/Ӏk(2*.fev>UeU/Ķ4? sm> vÙQ~EM oЇehYeXU+Mf&:Vc z0a}h2ZAJkr4KlkL.$f 9 DEk Pןkl/-v"W&ԧ-'V fehL#9=_(^ {G˞,2|)/ /rseQj77h#@.$M4>IA$%D,?߳08!Nm-` v)KjKh3ܑ_.\G0 fr:ٻ>7o߫{9Ƃֽ>4ME ҊVA6axaxV憎^Cw!Y?&*5U(6~7k=]6ckkؿ:X ]TCʴ?٢jvLDY&'(ɆF)&:df;a6& |I*\*JyӃ7I&TWA۪,01὞ ,#)uJ/%VkؙPJ$xjܚY?*5Pů`ܝF筌X*pvxΥO|?l6 гyl?fxF6':FQ؜Dn 39|$ԑ:2Qn&FMnTL76& ur1G @TmUy]l UA~R6ꏋk/ Zʒ D %!:\Ե9+ƶ9 m5'czQK[ZN)3|1.˜`_qV_ww<ٻG+ 48b:1t\`Usح(eCl`R5M)::2p=>F~YRG3a ێ䋓`?%ɕGϙ_nwGZ ITxb 1E1b-'CǒK89պ l)0YN[1$&B{ : ߰UA,8>ḣ X\6;b: jz.1B&{ L0 N.. kUֵk޸^  oD<mŶ g(M߯7x;#xy ߲NihR }ŷot{$C<9OoýwgV`۰U<F̳Ӝf#RUES&I YG@X1=nhƎ.q+5CE}P*wRĥl f%ߑ4C՘S Y GUVA7R;*tLt7[9G8:8Vlh}\P.Ke"nL8L//rxv70O޿ [*TtȞiRM(I3aEy%iDzMn+"PGs UMN?FЂx`-#WSpU5ziVwat}Зfgylc!z ~)ax~ޏN=-Q:"%l$n" vf # Fu6PJ22DMKWz,C'Ϫ*kYLn;ߺ;<MoƢCĈ!wamA;:ЧSxD/nhJx Z 2Zcs$ F;Æ>O<qsҥ s1~+ mҖi-\9rP5L<ȠAYh}\ژEd٢55۹A/y,.& "<LxEfNIV-%fK'GI&ZS & 1%(?:ڧuouJ5w5bcbAlI$RPڰu1/PCɹ'(mwJeLy6'ɳ6&-7Dc_6$Ji+E)"ulX*/%jnr/fp-<< ^z0ak[&ʜE s _sp͘ub6"-1Dʈ%Yd!t9vJ8d'̉c̪?\.3Y⋗Z ҇^(lm[A4AFn GlVpp0g,!': 阙`@>c"GcT<Ҍ1rC 's'dԶkxc2&@rH*%UV̽* rtF1}2҉zT(_SH`eLRFa)CJ4$IOlP<\d0e}0f+fm5߃|/ %1ѽsl _YÛW|O lЯN zpA:g3#L8(]Zu@7/9)U.%%!|{\O hyW9GL@V) ,;_v%Pf,E߶0砥_7h<6@yxe^IJS?.`tI@7d!P9+X氜r~ 0)r[҅&Y$;cP `swӉ/(妢~rb6LA?1Qȍ ^NTn "rYjy}_<W7\mIuPsdGIPv,B-u-^p)[l4c5ވe>ݔaEf_97B-JLtdvɬl %~+r5xO=xg1<haɀ0ò-Hn%w# [=1^TL},p}/il /hkߍH bYH)Wno YAe ٳ +m6u.'OA?G*&No陋Bɻ-Cj]su-3䱃@q4s )O$[Rބ_A&Bxo$V8=BK.gp>+˜ѯg0e8Øh])4>L$Q\4n+x~buq2gspC\WP ew8eeL}6oZb{*$z &ڔ5$[S2:%w9pnMVd-~GDZdV\SŸ1ل1%[[=2hCK=n ^YCd9|(:RtE^W;F|qĀWeAX%"V*/՜Pa"0;*=K^bReK[+lʬ׎1᳟:o q.Jr/*Kטb)cŘțY0FΒ&$G2qr= ,zX6S(L2p3[-)C ǻz눪c7)[ ak m|ygk{pÞa1>+ `{RŹkO_ڂ6YdSX~ի%Yf Ҟy,y>"l,n׽^GLҧ{~d*-3xoHvNtٜϱ"+'q.LF)6*սOx yp9硽sKޖÂ.m!8M-sUXOS!]뗗5H9Riwݰ߇Gۂ{/`{mq~~X[F0fjxg5|)1ȕ}"Rsm\V*b]I8/"W2xީ\ e* mk%@Ո?~/fGylc `˺6,s˧̟!>9T3ڏz r~j.*ESn'5TpMy}X!%K-m>w\,&QWrMlMQ tHpewj& OA {xĶ!cT]f2@7Ik.WSHH7Rbx%"<(lvN%}=IVLdʀ]@QGղ"A3q#?̢?}V^xkG\lGkņ k O5fd!Ab5z l ,Vg=,~nd ='&i+lpJ>QF+diI KRcd۰ `JLzfzJGA PV64!1FSo}: >6sơ_xc @ElU_nffcp#1)ز*.{? b"5XJL,cjްߓϒB$}Kw6,ogO$A1h&|=yX<Қ!b=aX1:ϫeN?_UI`)vkGIE{Fp S/ǟKoaAz}d4Xܮ\mwZ8-t8 Ͼz>>c<%&?f9\܇BfȨG:=b!k4=kë 9=R~<2c1? x䧿G>7cl_`G>~;Vwn5t{OxN{x_ZFgU{Xr49?Ю\h^2'͉ec{| XvԬ%fQtrmu;؆b3hc _ {c/G^1DVQ kETI{0;2=TWv`S)nסѱ_8˼#i49о@AcUbDt-lFc7y-l0ey{/>]./|8#OuA>bf,(.*$ $gw*lc'? 6hmLG YR˸E Ir/QB`:a8`;1=ЦtR+,;X7Lցhضx0jrO_4̪۷ou MH$$.dZ<1H% 2`` l23!K{,CI$%b/MfRΈȌ5) Ǿ|w׺UYqݷ6ncslu{̲<ΐxѓu$ /eۈ@֚c/Ƿo ڶ_G(^# G'9~tBTI I:ˎA-1dڼ8Y_DŨז&TG%>ti.;Kk JLdz˭xT@g`p h3y-֛oAd EnvIN+flTos~kp`"k'"֤Ϥ}1\0L.x!;ru|<"W7Ɉ݈? }bM#Qd܇ ac`#T!z?Xsݘ>#&~z /ቧ/\{v|u&3^?~ !TSeڵp># WbȪ5fZsyǥG:[O ټ"*εUcAx\i-|ϮS{waRw͸ZܙxMXXC/oMg9+^JbT2=kp!rIN.iX6}TH@!5!eHY-FeF0/-U]G~GWj[=m@Z=~i|Ĝ1XPEZ[V|zYTyyɆz0&m줄fǤ1T٦BO DCE+߃ 1e|E|0Zsw589|%$Cxetz-G̼ {%SՆ L c `@udUK>Dx [4xOFweǂJZsҽ?J((zXXx@di९D݌vvkH"]y)BtgK&޵<"v1 m&'X-s̚1([+]" W }R#f(yu+tQbGޗ=sM豇%y; wP$ɥ-lcom=㵍`96x,Y& RY8sh:weڎtA߻nj[yУe97a|݅JUU ëxWル G}X;?vwwӅvd 5JyX<&=  ,հLHQ q58&PX8OPvTASQ =f|=*iRk=P(p?XjQb [=7Iu.zK,ϯ ,Lc\W^!F%toGՍ^X֊2L?[q nȗ)HsdS{sqaY% ,NJoac'VS{Zi&3sf%-u'S+zϷD+d-7I@}[ t4ƫK6~#xpnw Lq9`O>t@5pNh~tyx] W᳏@5l_c>f/[?>s X `61na>;>?I>M ~}lMJp)?oSw.d4u89x'{9m2H CPҹȥXJH'g!=TxgfjB([ڻG֖)B}p`o*rǾ=T[0}魤~Zɫ0'cTL@+l"j`=( "(<1c whsRIiФ&%Ւd q[ͱwp,E^o1sGsTű8],`7;[Xl$D 1j$@})nkVƒ8v;`DX,.W ~>0ZNVxy:*t:䪊?@zf =35PHH͒`$ϓ0V1y@N̺Ad /x˭J/ anSsg/d@=~Ճ^ה2Ħ`)uI*:zNo9_ )& m~hn:_xeotW:#qqX݉7ָA gFZ+F*Ps,3e$@ڑC9/VA)2̪׾.$RF٣'R4OS^Wn!懞5l #~O(޽רOTYy/ɴ0zo Wl5G/_mg6u1{FG~u]Y_+O;7F,q^g^p0zWܼ9ih>9;5h à0|>Hp czaqK 3do,0F|Ѝ$c|_-r rcqLZ};;4\|*Ym+g7uD&g}$=^>zϷc-NDuĿ ks\~A>ɪu90fDB,;8Rj19N `0;كk̎2!YVl˦ğ*=R-RSBjZ_O,U JBq K-E\˪̨ߩr_591ԩĩW% `y遌P:y@JZ' 2iGMYrs䅅ҳW;~6EFҟ%R|Kn9=MFɭ,QR~ wn~?򹮗m&s5hy=r@ozo1ZCwt|^[mU[on7 [G޹}dtm$ 6|A4ms3 4}Ýc6,BIu|6@SN܇~|kx{W]Gyӗ|wVΝ;_7nO*?U*_=6DϛEG䡃긯m|*|ᆅ>o4Ir ATGHC` %D,8p(7`R-1zUj}@,Re:T!o$yFs~eOs JUb1hgfa7?G2N`N3kc,^buR ܭ_|\X ޜ(25WG&Ka;ypfdLOMolUZΥ8K +V1I Q0t4b4rM$y}>yP~w JEK);.;~yNz˰rӒy ҳ%{_y*[+_J1:j/mR^yK#?5esgde^H醮e^tPh2ޠFƴY $NV5V׷FE|4j0u$)HWfj a$$%?uBͳ/c;G8w6J #q؆nÛnSߜǸok ܰ/ 746':Z !*E>Rg J#d9^E肋C}g*3-ۼ ,[)X̦ǃj狜%-*!3ҒeHAeؽuUV@j[miY7d@})804J&;I}^{{tbf$`]I!E[n$̞?e7*.qp A|GTd&FcX+6nP2d B$hRn S:^5^@ v~ŽXL˧*~9aJöt$|St\㳜\(uB16KTk߅Dfu:jcr*A ɹ0zxkRzc @YMWl }Xl =y1)<ɰe-z걧DM:Ous+A37J"S(+ &BdWO?7ں鵝7v7Gc:kb>c3x6l|ǿY{3`N ímu4΢[EY| w\׹@ BZi2w[ܹC;նr]?C0{.y8aa<0 >&2~IEX>yEO:KjfFE)ۣ.0rᱭB 1 %A4'*C^x*ԡm23ٖo`z`h/]Y=G(Opud\0&r:<3pv;h -4[r R0@R!WܧsizM+O&fOCǒ}hR@X9CĞ1D/ [+ 'F_-`@9?K&6)BbB*Tg.]-l69'vRbZ2[We41X^Nk6 Y^KӭIŹ8jzJz>0# pupݷ 7R2lÌ,M߇~$YH*5P8=l5wNڍ''0S[k ,jS 874 as[h%sbb1MIlfiP{eM)Bh %56^} @k=%*y8ރ* :_?Jdon dg6Q n?_ӌNR#gCuDT8%Yaʳ |O&M -Q3\\x)v6\d'%O#3-g_|5A'^b5 C%)ve0dyF`UEyna(QգP,2LkWUZVm%# =,ݬaYP2e *(Ae? T-Ձԗ1=q %r͵S N=B+؆u*vZ`DeȨ)Qj @ ܘ%|pk<wI D77<٬#s3.Fko'{tB@f ʰ51(AM~x,ֺI| ˹<~ψw𻎐Gccߋ׬sǿXTUO)|̽޻b?sͯ=L1I&ib͢FJjxǬ,l#cPwkl$YXװXG 4+ϵ(9EL"«Os䀘%imtN6? dHV%&IeUN'1xB׽DŐdk##^\[8&Qpyo/[Z|f@};x-5x-#8fD~v T~D砂d~Xd3;,jطò/u-$HcfLY;JY@*`6Xz^B\hA:pWT)>.Mj hjeL786$&>@C(cdC}K7 C8AK #cw񏦹s $hi~cf9W; 3wNo}(2YC'_dܾ\rX$1+05s@<,Cbk1uAXE0:j-Ċ)A:_Z ߕ$s05|UW4P}R6VIV4%_7hzK;V?RWϿv @7Ekwwnś6vEҬa}V9Z])w AQ}9 7 >x ڼ=^a-#c#gsz6+T,jd]" "}"OF $AykJ(kN%W&c31)9w_ |}c 3-AmzGs uƇWrH̖m!7LEyK=ޢщ$F4'8.2U(:fW!t>aBIa=A0w eYoWib =1)N}}-\#B%U%ιܭDg=πFj)hòMԂr` I LL Ɂk^Cp*2?[Xu mY)qq,n ")'|}ث666(Qo@9*/ݡ\VQZSN0GHSĥC|f(q#YC5_΢\K|ܮ0s:P 'b,o<u-Λ!0"3ߍ0> WUrǿ\׀S@ն??_3572\%chXXLE- xM8>]癍k,s1luZay?1ExPmf9I~B ˢW{#pDޢW31ˉmd{^x!Kť.1br }ъJ ؗ2721s3_r5\\sօ~ pqx,XlFO>OQjXpQ4ܰܓQR^F9Ǡẍ,,pQ6[^k.`p'mg &Ԫ*nd⑀vr ysloYlZh<-@E5$3`3&m ,z @2ڸKYTn{вГW c͒9x4I_z7h\%&窱v < y0 St,d[CA '!z'}BmDB0w.0PTFkR\@#Zc96,ٓ~^BAKq5}Lax48 "Gǭ22|>L3EvliTe`ɋ(siᔴdm5˷ِ ΥYɳ ͝3(9ś+KS.{Xj 4yglbt^kqS'/Kق[.`{A>'e ;*fH^Ư䏂K 5਱Y).Fe2c|\>"zOVb9+2㺸-=h >\i^X P%Sf^O\!si]:0=Dλ27IQ|yBb)[[eɕ.XKuN_Q{ Bz سt b{% 9 %*Fw@dГ׹zEx>o[h̏]I2]siyh\`=ga8_exX*{<0j0Ƅ+ BzkJ"hqc0=s &*F|UЂK ȟg=b*蘒=xnm7UeYރ7;VV Ymm}fr"t_s=GNη*MUIpɿQ0ۥ/f'g0=ޏA"@] :}T8֌֠N{ZuA7?̞5$%UdՂn,nՔ8uOK7TaEoISX Zaq,4v AK#2a0) 07(Jbؔ]vY5?BLoPQXЬx3;#3rQd P@eS*)OW2xhq/@4 ӱN%(#al3'g;"~wToz}~}Ompb<?QF(o͸vv{әBչ X' ķrB0&o9cfпgƟ A:fE@ &G5C;?g߃udj%Zmw]W>O}''c}52db8O ~?T0>8nk('?4uV %UmS=?Ks\MekJr0I%h.8x#-XD&0hlo7 ck=7[߹u݀RoE }):lOxK&T`XQ1 >$K},ȡCeS²N7pvj:W Q9%QS7d#tvr9*I.j!{F#*1+ 0#~o{78&ybk b<^W70ˑaYӠg3IdeE+$OO4,^,"mU .US#ҒFLs1p!E`Ay(ƇAAONKL(@XMU{\?U-r LZ%k3pdN5| sKǹ!ՑdR%/b5Vy1Jky&󉫰L~3as4F${J?{ykk^s沬oSχkkR Я~dܐk^GO1C> i~4V}&uN?Ye+mնF@gy\xQ;^Aˬ_ aiH W=AT`v|H M-\q͘"t)m{CXL\m˅w6Rb)̶I([\^.oaQfI0 z3@At+c P@etnth إerwd7r\ DaCg}X)/#=Lɫk@~Z>c&LAq*ڜy>!?0gXT@~^@bα,{g0: htic,[6uqoٍ7msݎc[4!6U\ Stp_%}49ه@vbO)ӵ2Wq`X:@ >p9Ax^  -$@)kF3Avz20 ">xn//]On!|w7߾ҁE/׵,Y 1dfJŌ+D:K! I :7 !ƿ?+5Fϸ$$3K0:oy.2grq"W d@d!7JY.'FA~Ax&$CCpҾ:o,62^2L*Jgy3 cfa{ oAȅN_0W.}]jpM+rE?z7-| 01݅TxNYĪL%ĵ?7TqFΧiֻ/Wϼ;?x#ZP\PG_o/m3:I3'deTNGn\֗MH]VZ;BLu_H~+=&=tP(hd$P^K /]@e*bX?r}WUgg`6tk6yʅ2S ODeÓ2嵠=Z3o1߭I Jcn={>I>'"q'e] 9",YICE9>:Z֯';kw?VM?l}a4:dRƣaUU ~gYB [ĿbC8)ɇTPk,pn!=8^?Iׁx|&e^c)Ң"P"ؖ ][sLk=Cz<ڈ{L65Ĝ>Sgp1p.t M0&:*D1Up5/E 4ֹ程YE1OnlOTM 5T ._ه wxDDb⽣|r:'*1<LXͱ\Kr|.|7K(fd[(Ol%wrf8S>q+_&$H11 R?GCk\?S8{z~}m7>vw+ZБggWk<|IK7rm!])]_8nh~byEyBuaf[[ş+A & 1 e(x>XDK̑6 7yWX CMN+\Y4k0ةǪV{[z?>8"t:>3]:|"Ĭ-zҔ\Mus;1c[ڌiWawy: {w)/1wG6 `-]WwaV=FQLfPXQAΥj6/s8ϱVʣ!^WxFߕXօz8/ #{htf89LUe9.nxγ͌dqd֜B0(!><˩3r,m˺ -"{=H2l_߁l alrYxid}7l|;|ˏ}s*MWÂdT4?)2 el񣩊4j2z [I3+# 81`H=2=T?׉!|Lk4HL+FI+Xq vP30&fxkCuw"TF1HbG@`ŬH*KcP'wIUd]H lfA G/BA.:22ghYd3S^٣ $LԷᵩ'9`F0y۬Ƌ rvn8=oVq_T37 mwOc>H8!IsOXԍ^8@aI&R5LϬSZf(jj8A؀¼푃> 8/|gvpa{s>_\;cӼdp;tCf??VnٛIEj2KcJBIsLJ0G&vI yYҚ LzQ疮5n;[N{_նzVj[mwܱݷ.zo40ÝsNQ?mf\R,=G4 G[S)ŀefIŁ$}E6GޅQ"l1tR(jrks]̝=7#]7V}V)Xk߈m̒!3ob;]|J(B3$'x.*1!N-Ӑ]]b%bP wTG`ǒ [)ZJFe\`Σ0|UOMN]p{sxmcuw O>uꑺ%sS$LsmN:&"q,T)$Gu1H-ulAb`R3+d\F{ RD#}tWAfA`bF:F ?= 3QԤi {e٪za4=w߷4}K_2<@Xa?ϿҋēOƃĄrȯr-(ʩ "ݚ,t)/O E*2fF D3-pm@SQ ɭއTd_6dB?Ȳ CR l^WE Jt7h5M^EYRjZb;YHLO傴6aT .a) 6y0à&S1Vz%YҥE aֹ 箩=9*[ֽ O] uj.ls:G߭mjQ]"z3wM&i`c2qP%u 9Ňq,M=V)%-}CGk!/ⶲQ&ĜxI䔜xj|gPUBAwu>+P&Зx'בP';HqN[ zNk_9Qq3翎礵]1 av/T=v8"g+=83T"V<aNx\g|iƞ+V@xc䓂ǡo*bw޳T"9)xdY3E_Q :TH)f1aUtmrA[3BrMq.g_xfq#¤n}y )o^|2] K `B㚏I%B6躌9I<7l? dzWb\I6O8Wa ` 臚~})7Gx Cf[yf^#nb+]jv >\(dej0+kkPM >(ٰדRAҳ䝣iA@I- xWk%TR6#?)3[ kȜ"sp>y]M?څ Oc?_{Qb[Oy7 f1հ^g-k vY0(*rh&)w }6l ańEN皡bcXz?ϠN^\4dfNX= ,gahOJxгVjۖ>wqǕx^3¼y=qv;1A"nS#ǰ{Ƭ[ )1!|:%)7Z! p'-s )C%1+SƿIx? rhӃv8NUso_$ 8cN)1#.-)#=VRb;c LW@:ǿw!`A".pfՉfL@Z>"Xoo= &l cm~˜ⱛ3ǮBEnC0gn<f/6`5]IFT[ Hjx}7G^kc~~0^۹ss{[!rE5Ob1u=u5>@ȔA2e,'&d˜s{xR!0@Od9?&m2_`u0EРx"~.7(_sPC&τi#~܍o޽{U>j>:+g}mЇ>~xxKGh)EC3fs fA"Ǒ[b:.֕b uIҋ (% "/6u.Tz ʓN䥠;,d4 TbVE'd"Xf%KI3J0)"y)Ri-Mx"oww*) ~IA82G URGTQ?W|jL GooWt,NѠZ#!i Ur~s1ˊXqxy'Ӯo`A-ln+әo pmpǹMYjpU\nTG230 z_`׾)fqQWps"9hy$1['=id ^bP~Q]ΰ'P`II}B)`ץk#9vF,sw$,u؋ 2W!+1T ]x;]tA`VqK`_X7^~R7vMX:;.M8A%]|̌Ƅs[[xd jYc469.H;'0&9r 4G& @K2\@\:<)crVA}:XŚs1%/@/:fY~@*gGRu@2o1W R*| k[l6? akJy@ǫ{s75#;nيc0p1o"+9)~E܆e/r2ctfIx_tMǹnYOy5_?V\c1wsx}9lޱ ە'?_sb^ؼeE#>ks>+24_2_t-y^#aa w _JJ؄R;yLWl*hd/L5ȟ}zDZt^K dOtB0K ɬ!򻅹y9U}V;y JrjSv~rzгVj4G=YPs=hW<uI&3SG, hIG s9v&LL@BZ*gS8ܻ =pi19<3c*2Uûk).z9> L,cK, 3X=?lX2<1TU2 e?hOrpg4LIj(iᛍOY jHW5Vuj @=Mۨ<߫JQH"3""-NϿsg@|uBlwRJCimk'H>>8<ح{fr]:{x6}C39cΦnnb>!&]ס.{̦Av Dz֘ dļvkAϕh;0Ig 3 Ic1g=53rdMYvGsۈ#M.|jwp`tm?^۟{*w}yVjn~~++쨃nME(,bp6$aEE| p ([G$?Qtȹ0m =WyeMC8ǎGi?f3  l*IdWZ PLq]/(?^,SPp@lGEc7ڤ΅S[paX%RڀQr FXàk p"/UW% Y:q HR>.`},׈tx ӅU'Icޅ}dWmN!lL̒Q>2@xR0'އ)TuBЦ^ `Zx x]Z=:e56dYs|1U ۊ %|/ah0e9.\cU04YJ-eUUp -wgܰcwF_Xs,q*/6Pd>,|nHdg0v瘱qw|Mv| @a+)M PD. t,EQl [5y1' ^g&Ne&-Zp7:5~Y[1ܙ*_s &P oC |m#ořm~CցK섀`|:rh,WQxTljfSRW~žmA(Hb9j6-Ϯ?p#t"<Ht烘; x{6`h dVcH$R(2:/i3zs%FNd*l;aW %3{9Aae65 clr})Td`,f cE5q-I.f]epp_Ab\aϺa.kq+ךYT^Oowk9Gyp4q`; urǘVxA /_\4=Pb_Lѫ)'OOŖ&^Cχ"řF}m9ϓD&|j?@R@pR?*O^u2նzVj[mo~?7T3@-m=e8_z=n_qt=y9̦iOH)k0$v.BXUi F kpr|W.@n㫰,NsX,əkO 2t0Uo8%$~8E^d ^*{򤪀e*}2$d\nU$P {vcTbY;yt1KA|e<g0&x&P^=Z}tYr{KӾP % xQӥcHY.aB7LB9?=[~w7˅"lKa- Iֿ4 vhƻ=?Nxm[cEUUAL8! d$e6+͌/\f F|c8AVz ,kAP;iQ 6y|>MЛ'ni>:S!P ?|!9Ʌ7?2yfފpx?^cv1-r{1s;tzkj;ÊRfM~2der7*hhJL͵&~RRe.zWc]yy0ܫjKKE=2ċEXWpCX5PX6E<*B'd(k\Jm@=qĪ)բe}W[R,d2BpTu7tW,=ڷFXH/³ߊqM=@q.%µgQ w5؞X x}!tT@Dޓ%/C"& kù"YTU+/RL$BD/̄,車'kzKJ]|.aˁ5[ *>pŅC/ !ΌIx$7aAREa{m@ ڀ6 3墴>/}m}`2ˁυ.b5aѮ ʋ3K> IoI/;' EJr0J4M.,V &)"x>iLr-1inrTe𰹈ɋGs]fLD^W5zp@`JZ` = |s~\C֐U?-w ~jy `(o j/ebk4uLeRreS^@؛^Ĺo3WQ :q |ض@πbꔼVT\WǠi`t+Uϣ.d%b~7#KE5\1_:4t>1쑱c]{DyM@NϾw3yaޜV<,r7*4q AH;o< 񻗡Eи7OqhYGuV );6ki4SHvP*viS &?r&ιmǏXALb zpONնzVj[mX-\x>骪.w8ۖ_'YBg$p՟#]oBpG?Y>i&s& YFl6v(e泩?>:"}m 2]X̎;ۂ؟#ȫr싓 sɫN>c ppW%Y5҉(_fV.t͞jT[YɰiJb:)B^;S.[ ųI/BKp$%I1KAj|34T7 # W$orn(13kգ67$NDfꪮjvMRLQ$˰x0l 03ñ HCQ!9Hf7^jz}ޛqN|2`Ruޗ7o.;-uptraq|ӝngNly{F% b #A|DC&L턘4x@_ Jp L_%M. dCh=w!xSG,+CC乇@x\m۳C !P <*Zpsf{{(Nz_zX^RAy?駟;#m5vyh,}f d$scnc "C0 '̛}IYj'[p|95xI @DȼLr0&JlX %8=x`A4ψ%v) >*<6o,i#>G6s@*ya8ch4:.`eu Zد4llep.=znhJ>L#W!uf}א3Ϝ e[0|w[x:3KdQt#̋ݽ0~5*ywݼg_\Q5t}4pNBڔd0R*JE ;}5$3PX3ah$CMLn3[[%jΚAF(-اXG/Q7Y>?h?V^*HykN tXNQ^\X{K#PM^AnpLa}A\X2 UP%7K2+I)`@1. (@+y~cp~-ݱ# a>l+K?n]sSU?fa>7R |Tq`8{-B\W8n|K:`JF-hH :$=c~_@H 'ݬ߄#үHC$ l9&ge=DC:%/h{Kr>#XҔohT#C5@* j"/=4\A{/>L+I2SBmϼ/O(9Ӽ"nA`j.spysO~&V|}|l3]Wi>I޿ xGX*+A*b-x'W3W1ts }Z;Px whw,^b2Y4! $FJC\IvT;} ʲ /c6f5/yJY t1xt];d493/2/?5-v裏Ӌbg܍OQnH70V$:0<īG260t0F~]3NZfە#H3d`u3*d|c6Vƶ5U9ˬej5UΊ:K zh ̖>(iʼn\uh_C2,mq*@ϴήFoں\nÇ.GL ztBm++E^Ns b!rY8Sd (A2 wwe\A!p2"dEg>Y:X)$vĽP?GFM~Z[n艣Im@<݃L-b]}v˲DMoA8: 宇<,Fn h4[na}}o\xڃ,ۼ4?vW񣃜?Tj'G6ô )ً ɓG `E}3~XvCf$gxb0*OD>g"ewbf(@ xiE w/-ki$$ a9pSv0#ZR]NV fؖ|]?y"s/m0.([|p't%O[PU3@]?}ؘj0]oji^8VFPag6K34_1K-*W/66m'XAZ z|xM86BIg-27|V;Ƽٟ&ƓdX0{ųWe*sW3P ?4F$zR Ⱗ$? +yI$#=>@'G G0f^)!a^C\5A8&c#t}uݠQe/qLЊXS!kݥ즉˦Es.O{0]`޼ @zX_mbXɰ-_V[v!ABȉl1><&jb{r=9qM0ᄒp9LJ~fMPN 'y=2/S[}Q{Ȳ[5ʼYi˭IvStڠNiK=HJ%n{?M9X6)d ŌJe`wρ"A_ PeCq5M'{f]3m߻WMHz9o Pyjl뽣T(XזDPQ-N?.E.Tއc SCBv,āNDt?D1}%:T:b`'1 bO)ܱlCVUղU>d:V:=qdT~nmRS&Oޗzgl+&U>A91ANY x [`o$qvԽsl~ ;{GYvح{?:䁃.1@cȬ ͆>dܱ$KZ:n,C<Pݽ{Y總Ut:n[^ozg8w姵ܹs OMZ/fgVa@6]AƌR! >D:EH@֣/1М^73B#5Vp_u0*Aʊi gJtUc~N^*)ח3Ca8;(X6"2PF %ܬZ`؃ (dKr=9I(2d*V suK榅C?z=NȂI=b$2zh 5z؁~d fë%L)^o>朻ح }( xf^ЇXa'by 6>H|~10,xqqg`IЊ67HĬ &3ylIhe9imT34gҤ3X>^=393/2/˹s~˽A$Tin ||ۃ@ö+L}'֑ű*9}B LEIsW/{<*Z] c$m٭yM*Oz!DL#ՇL!sǸ [*˒).{g0o۝{VLw$uvzr+sEb]NOKך&<# Vju˞qR?+a~#$, 7֭[_tjie4"BdpYVZь r+=ʡɫV^˒)= n`{Bc+?u3kUUL~~:(QA̗tOg3Y0}ZO@ kQ@sFn?={bys#~rڂ{Dnn`cKÍ{5*{V^.<+#wx? jO>7pD ˤVI,jzD,8 ҂0 1S`({_ ƀX -ȌqK2_l (iI˖m9?q~=Ve Iwy3T{ 7ah] fW5%^a[az/` Ư|]݃J`"몦m)$]&V!IKq,!g"Wd4RuUܑ {{4X1 BO>kzUQ85̤=b"3-!ٙ*AxFNEցjT*ίiya 6ކ>&ДvRr[m`b۹w6x!,kw(h_@wd=6HC4/cy`=^yoK0B)m|VmZkQN4)SI+LDPM!YU P y*i}K62O^_z~0^0qRv.>KfHg%yXNb 2˒"k{Aǡ;37͠n?q7_b&\BubRrH"\,bJܫ$O}b_?OUv}s86r eh5$J ~d Ө@my^a[5IJ5O[Sue_Wɼ́yy ]]N>mϜ9ݿ_|Waa:1r؞<"j?(P8k 'z,tf hOoE  n&=t3 VM0Ogt2ǁs{?.g'v{{vdC\Kg8J;}tby8DQP*K*!C sεtNٗ<`@ƙ7|%~&ss{sF,g3V`]dܐpǝ;C/  ?cIPm6GݲEdd(I!,=gqC',"*Mu {l z 0C'Jm֫*//wG 3ds΋ ;K4yv[XXr?89e^wn?^'W1#NPF'\)rm :"!3`4 c:\yq 2<~N>&"D0%& x jw(dS4ދH\<HȂwyA"Y,,Ş+2A$ bo!h1&<`qWo||skJ{.\?Sh >N]X]^ s,}0 ࡅ{N..{og ~zd]Y= z&vL~rGsĝhf[e|i,.tl|k Ss2t,$ &TT:yƞK @ Ss VZIڤyhER)LF-mɹ y0 \XG~mCZA Gk|D& 6֘VC][)aaD(ܻ޶J&KeJA_C`ms+b-Cri6fu["%I8-exs Ѓ$YQ*u4`THǺff:u!qW7IEuM~8e$?$%YHu!gV$pRI~HI#@xIb춻1[֥1\{*q 7bO39OJZ:}irdh6&?5 DY:!eQ(361dsi{ܴ=b]8 Ge9w [mi7>{1ҍW&K m!ּTBKl_S VYO@+@ m#c1 @1 )g%ۃڜ $k DV0J=$b_@L^\m6xʖ=dߪI?zG`X5ٓU?QY=C0dJ+XT2;@ˀK5nj$ȷ)"+^<*lqZ=#M؂&0&c19@鎯:n|ߟ?虗yAիWՙ3gٳgC>`nY?2fϬi;W,r"!  b ۠\-&+zDO&} Hq!؀Xr,1D!xLײ[ h:,dM|P-F[4%yvt?KE@L ?&>IpuB0bBʗZn@xJqD @ bIӪ#8%kU5\J (I%*܅G ޸ ^Fy:X<1t /d8B#3HFߓ$G۞=nv;Ow7pc4l[ @?nl$-T4{I5$Rҭ7,n%jfI^H,P|' g>uY|虗yyA˗ٳg1% |:~n7H^L& O0I6H$}sПetXio*8wiw lMПAh1mԴ25TUu 7vRpE\Sm(ĩ -1 ^kwhxAF/g|}Dl2:vݧ h;j Qzuf?VT7h!g@Ce |B] 0'c!9zL\@:fOOD8ݥvD]>$18nԞKAVFA A.Fu{]cX7+ڻ;h4:&{⢿*ff``gY?rOp_\p-^{5Ŏ2ye^eoG.[F9B^<5aSAqS!\l6LE@goƆ & .N[ ny`: J[i!p2-h!=Nu2 1=)P#SͼTDF0Qʬ_(gs(1/ w@(o+u[ooôQXS`2  (~S#xuxhF#ă&Usxh7N{0<1(O.roއ]G0h݃q  ni |Lh:pT֗:>nueƳ><ЦXFʒG `?Do"w&0=|j#](S)Xo);,!A%+Kj`\*@myTTL }Tl| e<6kB!Y蛝LEMd iR_eM-FǑtygSX_#;0tۮ^Y{pʵM U(c Ճ ୦2noIWc5?RAA /cHf v2Hl$AJ|$ ɠE/Hl%VWM2ix}$"2L20>N(o{ 0iu .%7Y4^n?` &AL=/>݅{i.k^st-Ba0π"0Ӭ$EnM2T^Cjӵ&X$U׎.Pnf1,/=k8s8uhVlqʳMPX޼R¨r5l쎡GJ2^yTJȚ*HYc!Ҝ I1-FXek ǑgYܧgt3/sg^e^Gg6'W\A ψe7O`yT0g9 X9L-dy6Pt!tkGx\F#dk,97Atnǐ?.w|1O?Kmnq+u}^x?j ϼ˼,w_{gĉU!jmABp}q,A23m bYq:HzLKR*z`ƻq)8Y%V$Y2ipk2_ϙIRp$26zj0I:g@{6Tb,7now6k3P<{'7BOUܹ<"_j-s,|}>G BETpC:Ei;S+C# {/&.8)L `?*=Za/ S`vD&A6!|'8t^ ?X}<QڅY6&,c~7 A#k|_~?˺qf?{utgTaƙ)>jdFds*dXb0( l6&,@1 |V]Z7A^wwfzBA (`Cɾ;(IeX/ k  @ <>!ʭ\D> g?( .pb#"УZb13RYL Z~= # R:d kb9~g`VCjH)Xއ¹>w~[!߭CJ~v c7z]XO ~GJ{R$ڔIzc {l|Cj'O?3 {; '.yt)̴V2 (Umˉm "?kUvL#rm`zKc}Sϐ&۲bYOI7ϲ93/sg^e^myG!.իWti~@EnjEJa0`ҖYRq5/; @zd'9&Kp\ z Ϡw2LN T>Jp1#Cs0_l{nU2qv|DrT hp:5"y{~ A kt)'i:dh@ BtHg˴m\wv{ Հ|m\rbˠDڈαtr/</z}$lfɦ{ޒ{Ǻ@f*:_Se?> ^ൂ}iayܟ7n{q?Gy)8;@M {:JT^'σgdQ: T^@ y9,I[nyjbLFo>qXGV~ nrO)e,_ĠB $B ,6$kzQHY 1 D\xȃq(oEHacG+n{W0WěbzsO£':#x\2!ܺVtr.b/Na>?׵ Lats=׿4}s 5,-u E۶][0*;ʐrH=gRP"KB FIA2'/1?r `b~%eg `@!yi!KpޫHbI UD&%(#$ A+fyo mǀP1t|#BL BXX_4s6:ڛF=2dP( 2rSC7/cb _>rcg3{3/gay@sCvhm2@u\CPГϰ=AOҚ4 顀h+ $&RvR StÒo֬|i)!L);㮣׫ϟҼˏ<~'x߰'L62 l Py&.2d:Con#C.Qva#^;n3,= X4p}nB[+~+29xBL698DftoYF=sVK(#8C+@O<. QVmsGڄwz_ 2rs&^~/|([[ #tyUز<`S @Ry lps]}:u\ï*w&.G3d 2'v{'[ MMz61AL 2i*4{j3E.Ɍ#(ASܮAVɸ U3{Xb"3ƃ}r@pVہ^ 2ΕM>.{mFZ켇OFÈ_TJ 4`+cR.!{^]x]gmpFdT3@%xAʉARw->TK,twC  j!EmY`Udg`9`8C}"GXFV%kǴ?|Sp8L ;jk1w0sxXH]u\[Ϡ{XK_WPqLc%i5/JoT2FJO\7D/`&8O'>Ӫ%Vqk?чyz ?ܷAu x&<: AlVU>L T<I ^S'?<$C)oH`U3"D&@~d9<04|j2MX XX_M6<<ϐ-ɥ8N Y_d!a н]'ޜ:ޅ/< F}7 f2Ij$}MhNӹ誆#yrZ[%g)="U9h'y Lc bl[|&u%/#lGEllRSY ?/ l}oA'~kgJHNN @K%`iYV*e5Sq@F05$;7ot gb`9F YdYqmMjhuY7LNϜ蛆L ?*ֱ{֖X5U1OQyXV8&X-5 ,A.-6s c5J0P3^2&ur_^VBPȶ5{R" (P3ęϯ$>ǃrumN;W ǎtme@$D%}jE}NIR\ExȻ %K۰ͭ : pXANL:m`eB*(U.+LvM;` pXT_4~xg(d^@ϼ˼˟A|2{쯺 o FF0'0p@%tA\( -&68f&,~{6Cb;{J~dϐ\&7\6m"YUU u]/OPko p3peKɂ{ ,/nR-\-.. De=ɺ n:AP҃)^8d?9ws䁄JWr獲pCV>_p᯽sg^\݆:o>EЌqdf<'~(ٛcvKgF@ψ=4$M x? XY_))LKK kr+}O"[{hH@,:JFt1Rاxތ638ǁa`˃ >! el cSxjDa[Bc#XW; }:82PeA}N"#6QB-yZ$'N>g|g pϠj-e r渇qtLa Y&P&Rٺ B( dY*@ us<1[%|} ŊLp{$ bdvjcbM,4?JO9er 5-=(%CRRoB,? lg-TiUmٛ&1:1R :ׇ xlr 1詿FpN`&/9Y!ك6ͳ+d"?Ę儔j> NAĊRbLL Nciu-McUM2,XROH\ICyJsZo% ]"\-Ɵy 5;{ps? ~lBDnrl m=Ț`J2S䃦 $vTu&3yjcbH6heάқbD*z=ywes6;QȊk 0}=օjҁ tlWLtC|&IHlYBRW rkJ~|Tbq2$ijJAlWbV$-@{2*XH6xMk$>Jd|9S}{%d~5Dϛҳ5= ʭ[1 vdEs4A >)ILrS Ίɚ&@5+6;y:xqn<\:1A93/sg^e^Ϣ Ν;n?/ݼ/ʉma(L 3 $qf Yll b‚ᒌüy$psWσ@Ir` 8 J(+Œ8nzɽ,IufLzdP`na9> sA3bj#0 YXY;6BFҊ*d5m0I`}}0g]}8N?v7\mj>Ĝqu`ܒ{G/C2{h4ڹpokʽAyгH ^t`Gc+ǭ,ݓgTYnďA/Akx&n۸}BaT0YX"d.eҦ`&< 3ucT33T`] %03a~L  y ٲc2;6dfsEz >Osm&ޚ(9I'ɤf2{~_$YhT'qN6vgL INDLC_beQR& BqBY*9Q¬]dYAҍSui>ZBo<1lf~ ΜZr0-mjJe!Ƭ%Pd\7M۾C6exlL5֚&S]'"M`7`*=8 W28:ޫaHEL=& ܘMai86cIP?Ovۧgyjc3(Ʊ=!k`ɅVi0E@l@2FDG 6u+e3$JuMXR+T>xCxJ3lJ X.R~y㪀>qэA"zb0x,W (Z2b\#L҆cLXZ3/êZD]~ǟrϸi,'BIG2/m)AWPVJYTm5m2{rJy&88N2R?J K2/sg^e^ܹssoeW\AF> .,6 F0#dr{>8E 3XI t ry@o1b=ysy!k Aą+Y H3uf~`qyFR|3z azFI:IˡѐbXW(e7C!ZgJUnxGsɝ/Ri[g3>l6^5p[^EQl^֙fYVбtXyPoA;[sg^'XPC=({AzX2Su$rJ  yLGcb J"?\g25^ M`~,2)py{Px L4 h2B]8JjXl)eګd K:1$8/ 3M,tͳ1Qcڪ)?7ޟϝǗkOd%ݝtyߐ| ;A*e23jDRF,qةgmi` MY{IAs\`0y 4*a i)5T"(\ SuR`)褼 !tn")K}Ls;-ow 0KYMnLM&˶e@6j/%10$v:hA]LxGfK)upFC]s ]@Z>$EAP2@w<;Ԡf}uejo<|f}Gk3[sAA4q.b_ct/2y̽.6 n~ɵܓg^'[odoP{c&b Æ 1SQp5+ 3nRب/)a+ .[X`CeZ#0XfA% 12nb@`'Yf%4UY)p))ϲ)֋IjH??ރ=:'1-sx}fV>gtL_4 k@gP[qq{L*d8UNU /Sftƨ^ y:h$ϒKK-L!_p[SOF=R8gT s,6q8YXHk eK m 3İS #`ւd z#S `JmbMyf*d^D$(a ? =ا` vn[dT8@:ޫ(H{UfDny60+%QO5#e_nxQdcVAP iDQ<'-c1E&LUr &ӌ7{Bkގ݂Yngx^ k*Ip 8t"N2Tq]<(D&FbFU.bg^%xd2lb|Np޽i3~<:&pHm*f&ƌ $w!P5]<2~~dӍ a~hLN_OW!.R(tdqXծy93/2/|}rҥMvvvLKd? T9y @Aa Yڒm 䓘5<|IM@`уs/do6qZ;>A q=m¶]e-ۢcu$S_f ejA`pB@Wq}#V_˫ex;eYvD栜{QXAncCvN~諓aڬW˼ͦA$/ukT+s\vYpG.\䙃<2/?R`?AS}tJmG h(yj }4Ih .U5BPr:#(#<;&x&:NaaU^&2AyiOV@Sx8pQ<ɿ@JSP4l 4<@kB#' *| aF _~[=sK/^\' />%*ȣHE]"Oa``+D6fouϲe `&I7ꉇ^. ,6;xT=ІvѼ"JU&1)@@%ʮ$M@p1B 9A&1^R4AT{&ϺgTQġ Iq03L{,@bӘXUd1h]He:Ÿg5MT4_dE)NzR[EkdXLm.ˁ 5nٓ 'ޘ1|o3:EY+O,['_LfY .{Љ5 01g8mP|PM%:!S`R ?$ϦdlBiJ5I0c"<<>*<1]GVĎ `2J&$ h$o7/߇7*(\]x N gkl@uA !քv$"#E10fp؏Fq\~)H[d,p޽tH.)V5QaMV/q]'@I8 CMHQ Vԧ}4p 5u,/eØZ!xdZmt}EaHM6SEb2Ik)π 4Vt%K7a^ld)+x)/.B6M}kɔ%:Љm aPi8vP/rhl{{c==q ߹<,n|ǁcG*M|&fhk6&dzdM_≣ eD|9Êf-@˗]ߪo˼́yy{ W_("D+ FJI,6G/21yh>O2bAqBA@Y*;n*^ApE})gd *Ei0M2NM;&f  :AvLH('$1v ѹ!ңc$""dBu.n:~;3MF@>|A`,}Ү?r/>s&Wˮ?uRnpKjW3a_\Bd6C}Dߠyc~V—;nzƍ^x_pyypz7*)) O@2-YTn,Bb/R֣g}XX +x2CHIa[UeVEƢ?VeVf3wIO5UE#kE *hkb ;x N{~>E6gn?4a`c X2Lz_r[K/_V䙗y)e9~G+b^Q}kʄxB(s_P~ o%{}L(zI";,0Y{d!vwr`u1aݭЀA]J2X!sxAdhT {`p6{^Q ڦ}7əyI)h{ KD|tw>wW#;mw.II$TKf 4ư?m 6~4OdY0j^z\%QRI%"zI[23qq#k^ڕ|~K.w~~0v~ ^ Z-g@Z( amf4i ȈHa3^q{Dn*/ Xs Kyu | L5|O޿:FvjfhdP/Sدdb۸l %f,{Hu.cD /+2m"Ug_Nx%Ķm G#Ol@34 r 9b># 4~o=iI<`M R&!+)gA-Fm<{n?9|yojᳫS8slVmyD&#!f4ND l6r@/gK`dd$$gX~Y>:#]st,ԏc}'||Pff;E {o6-?hGIG~=/,M]&Nm6as 2-2$9̾KL$AФv>kS'dV{ p-V?|lԓ$[bS$ W>ԴXo>E6~ u[ͣw*B}D8O%3l&PakLoq!)Ca0O_f+/쯅@4PL@IO'HKAxXOL|6%)]LdR^i1s} +X@I|qg.q~8ѧMu>,vtM[!ZIy Dz`5GxTs@Q 3աnK/} <K… e衢nӤӣpirDWɨGZ `sDiAb3\ABP-&1 3x4D 2MB hx}3 f86xd2XQuAo3a"/+i(phXodm193qiߐs',; &|ߠ4sY\ }66E0 I <#h2UUu6,?^uuA%=2zG6mP-:5~3GpYQ4 ,q_X&,Πc'oܸQkkkSN_}mifsdh3"*%[GOʴK%AA:)1㙵C:l*21*4V*@.<{2ء> GfEۙٓ%+t q, aIy^DIHDer&i8d-E@C,>eAw]A6cY@}?Z4Y?+JLH$/P`:q$PO27j67/r_O+gxqdy,'ai\D%E$$-'OrPd` %Jj΂F1T.? ܏8ټ~&IxbȆqq'Q4Drt 0SJ<ҳ P2R#/0C?7 &<8՝H)P[>I[R<'JhK78N{ami/ %H<4$_0{x⸅O ق&iqYM#͌iF1ZQliGI)XVH6 {of1b'%Mg_0/LC{PƞS@:ߑj7%$ζ p ZLheZ5k/T%}遞/}M{Ο?o>^%?Xۇ|sO,nϣ_`ĐNMf+Z зp: ʨifÇBF Ҝ dw(b7d3Hc=5KXdτWq_ sy4a)8p,{#QU)<|"Pw9dEixoaq" ֙aj̄Qs,}\}y2mo 7zy̤9{{{&÷nZ_,G%x≟F.\/unrkLwK&Q31Wb%&NYd"ޛ sĀxٳ'ɡ8  Dbe`akC">jC崀Ef TڧadD 5GU *h- GA# Ed>` ef5_Wn1P~pJ0ڤQ?"_%$:z7Xel"ffp) ]NJ-\|N{Yt8QMaY,ٓ0C rqS@|?IrH24]j7Cl4ocs\܅[4Fn:¶X;d|'!)7W%ilx 3w(cBlǟD;BGm8GhCc$/s3s玛f3u'?~=z*oW^y|wK//};zv6X*8ڢ4?HG=M0NyPʊ16kO2/iN:V 5cp/,7[@OC~3a׷=lL\<ut˞21~?ͦXD,i?YtQ$ܴ<܋PY`]YŻ[q v ^c#XEs]U8v&9 CU\Uot!2x^3f!е< 3 3%@3Mu@~Mvaa _ރr~uVx,r[rPK M$ d<m,#?C8 Ne%G.m`38H%i2 rmHJ] ;")rcD5-x\%56]5-,^ ,y!g Ye2eb#,?ad$hE`}ܷ^f쐢q|φP vZG˱纊?OѴ5.B2V6CaHL`M3+I2lNR򹎢J Ty @~צdFB.%k@kЗR{ N<72T{S eG`Z"i|u7P,9n:!9qĘlhL4z`G9?#0Ŧ)#FN1˭K1*H$ ٗFeg§9%^hwـqIFu@@YdYo4,㸞 f{86gQ\mU_6q 8)Mdm> ҏe$ c4ULf5Ȁ( TGkt5*7,!(=fVgXWOiaRl}vpĀVd0A$#0rϖ+¶>Q A"Vv:#; xL̈ALLA3w83N1᱆C6Y?u=cv(@<d*a=zP6;幈M*ᴕ:4uǛi~sXN߿@B6nݺu|?7|~뭷)~AgFe\HtqUQĘٗ,s&58DY|vbc tK?+!h\|ʐQ[D3I$RaCPpf?*yHas 借U # -yb*gK >ݩ(tϔƋkpP `\d0@e 2|R߰7@  5"Kr;"aWs>qv#)6l70 c7t [V7g*8{bH(\Wl"GO% F$Cl d\#$oyPH:<8)91d`HFRv5 kڽI=ۂl\fJYP[7.AҢ0Ќ} Q=%k<AmzX1mQEχiCF>Fp] ^ ~_4pts~82%|&t/ hi'a^9?tm=^|z6~>7[PƮ縆fogN?N"aPXpe22F/15hM jRk#D;Ac!MmLچB0K>Hj/yupFt1L 3#[6V[2<~L>c0;QQ_ ;E?+ ^ֶ~ܶ.߇׊!{̰e}Xl/@xI3vdۏZ_2@T6ʲ@ksY uϒdˮ(iY ?/3HB ؗfJiZb*\d0J!2ʼnöQ> Y.+V1?(} }4砨t>WH.a[ `AB`o1ʀbU6B/ ZJ.i1RBE=z 4f(M$8.,!  5NQ=%\Z8V. Lc0(0:QJ1UR0`Xlxa8f07̾0(<<4EK"aR].{D5졐p, X7 Q ck|/-& +_u=(Bdb {1ǎL|!l4kZ(gTW|ߴ0S@o>cX$#9 NX7e$قn[*s݉"cad[>8r(#< E!`ʶgMĖM9w&5q3u`,%(l6ߗK_җ"RnΝvww|> %P3˲k0~=ˠ,(?;H/YFc)3_Pj0^8uWCF\$2ZX-/q-B}`v2`!!c1pX¬@*~`Hʡ?fUD %Ъ ÊN {3Ax`}gq,;:(U8TlA3q}4ǽ=cc|V[G:\зg1muXD,udt6ԋis sS׫o?ꩧ~{_|'/% "͉ 7ȅ2*XV'ɧˡaI  Fa`*eƧ@zE^ R F0,{"؃7v ;0ЀOS(yxkac8룜^|'(;i[<޳oV+2^,X (zT&1uaFLNaǠeVW+$SU A-;{ xLEhGM b!d[PEuM늟0Z״pcߙV%{˜Q*yY)F$$ E:;&%St:y!MY9!O?󫰿0  t{|_X q.lңLm:FoSr,EGAboB ,x2kbU̬a6Dva^iks~u l"gƿ CGcRCgaX凓(O&4@eyP!jO*DMe-UO\R=SZ3k7v> ; c33| ZXaA8'}0:h13:5IסQ5F J͉$1.[Xraz兕2Xjǧ>XͤkxXA/5u]g06?8}uKOlYՑPJIJX^=#?\&<}m`gބF샵{b p@$1 4PhX;H,2J'2vNyu~t-lxxkq~$=B!Jݾzҗ7k//ܵk_uL&c`3R2+ 0q[蛃 !}xAϜ1 Bgc%/~8ʠvZ; O~!l'P @ֻ`d7 ,EW^@ci8<~l=s"pg#.%GAaQN##h,ǃA"Mb} K S\Nm j.}00Y0Ǔq6 b_d?w'2FK؛Yzs^݁z(0t7*g &QCX"0 T/(6hx3֤`|4nh{1R"8^GУA;ڎrO8 Δ`!}{?oZf4w?G+Z"f h!Y %9 @!p8 RŦ:s/uu-i^f? :O!kurL! Ҳ'y L" ГI^B)[krKXZH o/[Qj@yL̺sV$iZ6i|6|3s皤l$\` sϠxYIeaxy6HbFNS} ?\3bmWY܂SGK.UA?Y FKVZ$]Zʊ 3! `$oJC|:8hnߚ[Z$f Gg.ӑ`9x;Oń~:>0ČA'L=NR"0 !DeaS$,hHeWJbvz ˼5C{@os,L'8ΰ9A)A'? g|,/` Zv/O<-=?uNMC KclaC~2I2A ӄ9%pL8(NgU^.A B,=o|mΞèX$ٹ ILd^Xb粼j(KH):a),}{־/9rJfmǁ>I8o=>Ma jiz @ 6 X"1&=|Rsxh.?A&5M K>FF%I{Fy8b|9aثC$^"@'>{/MR|5@8 [<~b*eA1Y*I[H`9(ľ€-tk^e ^&H4Dڛym8s!ABǀbnS_1#8}X0Bl--k+:H $a9R!7\v3qXfҧљw9OI(`9ɸ)~]^F/}NR Z:IgO.߇.5`p(o?{={0jPd<ʃ2W*bZ6zp, K О&i_v􊍡pk$9h`&òJi9Bf$MӚ0luU]ĂQ^>Wܥ|,̣J\4.13I:Ehd(A%s^ɨecW䚇c'>ΣYjwKxȺ%6xA7d!)FNL#N1wXJ>rbevD^y /?5 oRy_@GOGn}K/}olAG~ NPN ={a|pAGuYRjF.72a5e9awWwCeAPl2W D9'J!&|D6˜Q]*< f9,=;z`F 4,N%!iA`j4 6S?ER>NF= `Pu'.KQrd5lu\'?F X,pCf.ssqB;G?"˞.^Hu?ö⻄ˍ_ O_W >xFZofYVb,f {f + evwdMH31b$ydIm V 6PYO[;EVT,\Kfau&p  @!|s1Hiq:xel”$(5']mMJWOLȦt ) kSa(B; x5s+pd\S%0o!.IK-#:t٧'_SlE8D^J.}M;–0I"00*nnOObH,)LR5WC34 9 v|~\XqSȬZ&\t^8f cQH^>8Ecu}S s Сgp()ԤkG xm-|)[dy|!&_X b p?쓯Z<)[B6d@Ŷ̵2~}[`GmIاg[Mpb/ p+_MUDfucan;l9z I Ԋ) ӹ$/6 !@Op4nX~x~#67|L5a6sr_z/}K_z猿xAߞ'|gٟoooGF1 xփO"\U3;K 3T f0d_la}Y|yuQ%Lp(ᶅ5,6 wǰ!05o;8-ƉM ܚmcmL8_U_Wr bt8"ߵY _$nM]#xt+lߠ=aۡͰN. T?|w~?Ծ׷HQbZfe@14ne3y @QFo Ad %l,|:uxw4p~|e@ b<II ^7a*JYf2 hi+% jKr*'Jۅ[98 L xk~k61IdU V0 ;oq Pz%YVQ²ѧ27؞Ns `1^25 (BvŠODL]p3ozX<\<k6mʺD 8,(O Y? )$kf%a` $R=<GV2(w*(R~Vqf>.;=i`:I*KvB>왠)}L@[1N&a2 KEakb"CKokఅ7^لhL qp}xɆa|I8 u=yNJ7Sg07qI^Y(f dbL}$@yCN x`LJrUPI%"Zn3@LĚĊ~'*:qmv N#!ocn@j6+1d/k$R u(0]y;rDžؑgcGܢg*zWP d$Xl58U1mɨ sx/kP鸌6'fx,e뤮ZvKwL5fܻi@eKd"A1x;>Ie)L'e&F ?&Ƃ(#hqc{_ދ&}pZ4IlWW4.p1r>o!cZ'pxa\٧N;N 0~;DdqiyK웕-{ck}s-99ܧR,Q/C_%9)/@q>?hww7[cK-v=[ ʄ]{/ nuZɢ'PӴ7k OM]# ͏ՑuYzxyw{/Ź(̧[~.Y~0Cf`?ew|PdzR,J,$/h#`˘<uoΎ CddԌ4O.`}OSefGD2EO KmeWު E8.A@dXd@9Άܛ.\rXcu:bz ($i=kl<]L%4kZ)1* X²%`D 5z0%G@6K%@ PVWn>MAR3bD`I){BSpf}(Xq<_jR0xmZx愁=͍ЗC_]'pHϟ P#4>]`{bjw|8 3'fRɨX>WWDhH0XvЁi0ȴWMf*@N#63LP"i^ 9c*_wn(2Y ?C ̨1$AOOPl/$2`"x-_gЁe鳄 ]2v#'ulOcYėdAҷ|<ykĊY<KcyJ|9n~h½+=m6'7_hLĪ0̦5]) =yYd@^Ʋ{|/Razs?"~y ?*u<oLoR:Owy4 9_x  7翽0G+<\VÉad`.0Ԉ. >Qn=YMq ZQB>QsS~Po|OkFl=QsWgPS,ۥL/d?Mn?zҗ/O?>3M۶w}"(Y6ӟwa裗IFK?z8CDOxXx`q6b(fl}5#CMGMؙy76@A /˲? !ʰ!Rj'> ۨyx`G,1 JQRc4l d qúmʨ >@pP6*!K vQ&ϋE&ii=& [g6 U|/,r+lkCV.Jka#>^XF`?LQ"ο / O_]X =\Zo}0JAf8 frCAR_aA("ϼu30PcEm&l PBI7 mL*Z*p.LPL"los|H)#/%eJp1ȏd(ad>i ]݁8lQdwY%η>)[ꡞ.Tx* ^pes0ebs -_KFI CJV@X<p<3@ƘX*Be:;gO6ڞp?‹p@[Tf'S 4>1P,* 1|s 68lUA8^n2zdzZH}Y-_[vq498O2>>(le1%L1 M\}Q9&43Ë2CbF!x+ >i [u|Y d0 sϦh O8~` _KX*C HI<(g e\"ИѴFΛ2}POao#U.dtvcd!S{5E&TĢ*D@NkI^#1&,&?~; >݄kbJ53?x&,2nWd2e$FKajyq, ה9,_]xocANEzѨ Y+,],I+RRK"FSߧcC-6g# ]q~w;݆(}EADT ˲t]D[9hk:{^ ~I IEiׄ'OchےGkyCu8/Kk{&+u2ib7!0gq6$ř&o'^ $I?i/dv˯W݄Q )b (G2vZ 89IINHOG͛=kFaN2.8#\.1O 2ڔLk/|R^Dλ'l,.Wfs0G:~* #2χ`< ]#ި%GF9Cƀ Ͼzҗ/Tz)̞kŢ ̔hyR֡/Mɷ J"l At/(- *a

;ǧz (`m"'ln ", _a/яY&zXp|A f>Jߎ 񙰿B6lIYBcwa_G|>>#3'F<I)DzU4If« nRix7|Ӷ.g]"`5 f"<Z۸DŽIjAm'a?boGWDfo,f-Vyϑ#GG?{/}5/G%J+FĪ& 1}`M `(KPYkR1cfhXR((yɸd/bd!Ƴɶ= . ,K [X I2>̗>K2E7bXR,r6cs G[9!yxN 40gk$1AΈ& mSËỎ~rSO8 +5Ddq2@VK}1,g$ 15$@` Q b iYr3W-\4`^*౵>,Ʌ:K(1j XBffIH K%ttS,B2QmCEbr2`A v̦F|w4H,lL}(X+$1}! bOb+$EalRŒmDI%pxJx*`&;_^Y8soa@ 960 iJ9 'BHND.dɘl02CFL}XS@z|ʭ}ld1R[]NrwĐ E$ 2/uω(fOr.E,<p7OXgWcO 2e(9ߕtC9rp*K/}K_:=_.EGaD#=k#G=AM o襃܅Kat:~"TUUIA0,hƋg9A#EV .,'G47|E$ a]DAegaհ;?ȻY?;wyҗ_> ly@hU)-j\0^m7k#cC%ޠ%(<vHm1EnA^ky&efhZnP|~c܇T94f(!:CXzTW17d(;2"3X?a`Egò߂՞6F<0h|+cgϬ\i=TEXp.\^dP/<5Eް` 7ݫ{:Wq"}Y"lΊI t)`CUflaS@}!+Ƹ|ľfuO cqy \ »ܾr<=hcЍ ey!٬sF1]_ a}6f{bɚ"BQVP||҂ үmWplDyjT_M &5ѧDLE*˦~'_Ƴ/)`%{ˠ,(NXO.Ԯ̌THtҘ @tm)Q9COA"#H.UY-{x3UWf(%(  /b;M |>)( ƌ{^<{caN [~Ĝ2f?`FiGA5;F% %}/ G;8n(Vd|@{ o,L:é#cƠ(JBVaRsgOO9j JC$c:^ }@FƩqsBepoq|lҕ M  :כqd,@/$ۈ)ÓӲv@O_җWo|"/Vc&ub8@$4);A蹀, '?Ol뾜q$l^_wDI)p4bّ6ԹlPw|Ol.hR=֦)IPЮ1 s: h*wdi1`vA~?kGAVTLAM;dBYiwefք-@@m |/?3?鯾O*>z(ZЏ$R7Ѡ1b֞l'Z+-$f^i3;O.,XSuOK`;"\*J ?N(Jᆦ#[1:\C D̞Z$ 3D Lまb@btZ=;Ǜ 1X*Q^H|z n V`\:5 *lT;-kk.&,Lנh0h#`@h;INBhs+:ek0լScoTW݊+9Kc1<&o).ݒ%2XAc=x"$!  :Rg&t(Clnz}c*8yt eO>~WuG}$2ҀrjJSx.\q^gϯ,S# 6Ex1ITB|FţH}sR"lI֘f]`0\f_nW$_&c7[;SI_z/}K_,oiիW}v hz_,z< Ĵmu(So8EoîK`FD 9@@`sB#@ɦ)R{.2kkkO Nl[nJ&ᔍ}Į[=> (ȞEs|W:< E-NP6郲Suv#(k~<&86e!aBYO2n1,(AO0(/ Ua=~aOk:]M2zPcѣ|[W^yx /гyҗ%36}' O^<6(xH` 1/0 ;,s "wlzuH|M!yPiLoYrCg,26S fVE6V >Z5OlzMTF'}(B|5Jk>g)#0h\{5|vf(dE(SX7Xokok,g2C(c8ʔc ly!I]}MX+ɘ ؽMlg-5k?gcl,,m0?}zV& X u| (kҹ11޺t}GT>:^_!`:{<@@V)Sl1LQ+W2fDZ=; (>ād%SIO$_ُ$9-^#l '$%[[ ;ɺw̤ 3䏀YgW_xTFe0([>o85% -oOmH@Wc`Ҙ2`tAi,k0Q12><$@Rn(2nP+ DfW!_]~I lUN:[z"9)aC!ߚry?}olhp=LJ oNð؇ABD$`l5P4.=02 p4LcbPj@9aD).kXˀM~ JWAbT(g;5S̨w:&:0f{+^ qVWzt@nCt7JbY= 1*dEdNa l38" '@a֥;0,' g\I*k4i }9m#89aKAt-*X'%1Ц~#ב)4i2yYN1L9qғcه*zw oDW ~z æC~<^˳ľqwKO+@~;Y,2C|A r6P Le톭OH6@2zZa-ʱ!}4 t~ڶ/ѣGxx O;a3=~Їgc1Rd=(M6`e7%YJJ&cf\džU4v>6ݝ)f(Tn6ĩt4 ܆i6XRJu( 2 1Rȷy_LU (K ~6E,1 V\jwwV*Z%^0XHSH^@ n`#He)Tfw.6u 3#(Y˘ρˢȬ2 e_'I|ϓ녮Kbm*p̲ZI%ƣ%\Nb16'$]{l;<$&sc4ҷI $$I6u>מۀカ 8nⵇ`O>nౣ;ՆIKA&"|Q,T;$Dn\*H~-}|d 1yE9>8 *zsº$֊-cNAc%ormGU2Clw iBWlt0(yFSF@ A_4wkx57dz [ߠܝM 1=k~@%992c(NQ7E@5u8% b;$k:}tZ l-T;F_W2i\:`-^Sw񜢕OG4fB_EP@$/nXm|4F_RS`XZ($&Da0بHHsgNmLŹ?>#4k?u>S} I6ރJPDgPӲ`df y-A8aB^W'VSKfx*YRp2 }z=}K_җ_]ٳ;;;Y||r?%&kҶ- X0zxiAN|1fֽu0HA"8B\4U0smGEdɱc6NesИ` 1؃FIaP%0e5$,E,/77Mz~Mx E0+|3 ,T,j²7T05  za6CCyX!6Emo¶G>* "xJvqIWt$Pw|k W>7*86Ju85&$'+$uOuY䇌s˲D>>]34yk,UxΥ&ת.y&1JVN *̧lVJ e SHfOMܦǼ1I2\ kLz ۵/ރ '.Bo˗NdBv,g^:n:ҐM\F22`r1bI^)S織g,*PId_(X qd\|U-bD"|rR(1]}byX`?Lw5gIvg:cuQ!nUbEWJ}U/Tr$(槨©D6j|I?lbڵJB";i¤2 TFh5z7i\:*E"gXik=ߋpjqֶxq2)[cQn42ɣ+ߚO%}뉞o}+s5SOQho601_fu!P;ϘNEJ/L&7g,B #I|/,֐X́1>T1Hbp"j=|= 5M9D&$ijB2oftzܹs͕+ݻqƽ3gϮomn/˲BRp GB6[ϺS"R<u}+tݻU{lk:rvv6|wRаB~;VvLB?ݼSdaP+"e(ȮX-q޻d{ZbgH  px.DBr T/a s _~z[ףFG_W[~Κ>d#H)ڱHҔb"'"V=r I)3UB`*J@0Y;KV%*ƀoVVGA cܼk%?HX!׉z@K}d:SD%qIf¤~Cþ|z_̡]x׆ -L>(Z/-\<=/Ùp6& V%[&R$ IŢ%-x H"]{|F76jbŕ@+@^-m3)O#8FVxa?PV 30&ZzdXg NSã'lR 89k %۞h?$}/9@mU.͎0zȼljUNЄ]SE\.DIZgM~=69j9fgõ3URIU9k)*YY)(J ,)j$ )oFsMF)qy ~߄E[@U5 xg|~*A;6.+d79FeOQw4X 2ۆ[F YqyʊP" 8ʲNpQfu<Ūm6:'Cm!fK%^TäCDT՛ 0k9\ڀTxXԂK5/szF|ZveUZZ SHhk=)u.HCTJȯzBvD)dw)(Zidׅ?O1OOش<~RnrptXtӲVFY;t]4YVDCTCk?eu4 ^ZQXĿg H/*VȞ瘌ʲ<ӱaڙyTt 9egj, (ad` 3df}g&0e;VHU@ ;$h.]c$)0%C%H[M􀌶m\=pX_T-'K8ɤE˜n`T~%y2FVdkHWٶZ\^,< #AeO9mGռ&\}UϺ0O$}뉞o}vZI+W6fAY!yR[N &u;ц I(P $  ~0a]s29*\ѨćBjM,w¯7nr a6"+<<ȌO&?`} w666[[xT6rXуp!bur6?qbk}mm3sCG,I >OtK"}n[#F|!m4> kP$2DHP!$ù3&f~>ć  $s7 G__?ϝ;_^z饞[~Z^JY;uDVBy#>\n2YAbلTk>/\&EaP8Wp0kQ& k-|md3+;ny:Njkc f \o"@7baeE)g/R L4Xn؎+ s᮰B/_چGW3Z5bn EqܥBX(Q$ا)LܫEɂUM*u1AIRd $7 3'4Fq3R9^X;SSNXx~6Q5WchJ9_tL^ `_r#883fO4QNX>R_$JmlcWLoRRH,"ʚ透`^kyvWˢ@cf 2먼X+ȕB 1Kj rƜVmA މ*JjIf2V1yXB~_}f :g㪀~[Kx1xU9 m-&[5=O3r=P%֤Z3Q/WhaQJxRu<@ǹ ?IԄ*^(,'s(Ua*$X` &W%Cg2|[qLE/=D2&*CA&JN$G,)jas^*d!k bN4ޛ"<¸l6/.ɑC3Zڒ چσIYi7$'gJ+Hɕ2s9 5X-Fy sU$סΑ$1:]t c23S:I9iHldcd Ak kBg.|uN 徜la%;ν8Ҷ LwRUK,?"̲3fÃ#9psltӏpzk A4D&&d4hq+~5BZ"%*G lx̺"At|l""#ٵTkI2%]R'DOַݾovwwۻwZT|i <\cV>h3th_OȚ`~G<ۈ{J; xOC1 #0";k| Cǟ'<иf?R g,fǏgߘ-|;|q΃M/_ hX'OcE'7N=§C7';#&= cEBy!y?sK1cju)Vp,9bÝ} G&S_3êVq6DP+QETި]Q!շMUF r]0`ϔ0b5d  gc7 x)|z}rY:ayl0,) 1D ,* Qb\t axa`F$kɇȎSi&wV\D戂GDOQXu ^AODO,YR7waIR^XίnOINh2&GFﲌ&+lP T5U1js++Kt yđV)<=;pn*TŢ%}Ya"iO<ɂoݓLs1 LN1b{3q.?#CeheQ9m}ʁgz!wr4dļT:mTH8$|}Gx&#iT*ep.x"MY~ C$0t2 =ђ24 {-sW/M`sl%'E tunW$s#\:HDF*25Q2umxuD^?xG>Y3#O+j)B1iHE ZꜬ+H)<XTZ$wpjc?38ƂXO,! BσtnO$}뉞o}_ey=dh5H3}BV q{3 +%-hcF7dzqlVI6R>!6@?SLaP̓)u= 1m{y0"D)bЮ ,t6X__ge4{$O؜!qK#gc,^Osx$f0U4 yX0'kXQZhۢjk0G>BKڈ,Fax.%Ԧ  -j3T!Vd[o7|g$O]Af%n>VP5 0FC-[ 'RiํaOAn$- >A VdÀ{3'*䶇t\rp5`dGdwl*uV96HQ/] V;bS(2 n*^Z gư9ÍCl 'mR)T P/!8Rdĭ~YeVs' $Iы@"<6˿±JARp2,6wp|L5m>B2QNƝt>'Q=U x99HX:9^p!2Q"_TM"syM$LRd8!M\ku~h OJ*3UIVl.GDBZ.rnI;]ۍ1*trpq| Z3$PSS6F%fu5?ه_Gx.J%E1B>4x0r)}s 0`c#Y=3 !k .f@s31fݑH!]+`eQ7vGu%KU]5Xډۢv53|VraZl|c Iy-pYۭy8gg$ASM Ħ' Jp]bANFVL8Q=rR\HJhOq̩**꿭d3ԢIdC=m6豋ݜ { Yw0_,h>Ddlx^p&g2"w29Q9t.f1BrƎKM!n~O֮k n\OŪ$C"rvDsx𸬁QULinLiQ$|ݘ6rqO UZpYA"}zΎr;'~\dg8ߙ;dTf\4ž2},ˏlpΫ`o#8wLJv)4"VFGngr DT+i|s^j˦FxtvL\ǰeu^8sOv]PǛ)dH2(ji<{'zl}[$_ώ+7$ uCnІ{ \~fvalEϫS-jTZA#Y "*duY52{9H#`PGe>+Jhڃ1HB(Cż3.$c.eH@ΔfZIVz?om1u+jY$y#UxmhZHY4j⼨p|65bņ.gbXVJq]Va,l"2;d+J7g RW^x>LAZht8|œKkUwno-S7k[04aܳJomZYȀsP1FΤ^VɺYg3{K *u44Dcn܃< R<,C2R9_m5Ͳ` 0 ym ?#1$tLGVb 6YblƊ*$,j8Zѕ~}"s³D8:)5^yVrȾ)b#tcX:޻uXXbasX굦#| 1(0K UzͥʹiG^z> dVO&*ؾ;c :x~ .<s̅,L6f9fL]8FM,Bl>~_;&>Nr*&9q?:䞃(`j3ej %M"joOi_}a [psJERQ>R>SsV=d4 Ħ=$zה1|FO W9K98iA@k`gsI8uFMӽ{ڶKpnzdaqI6 OSg$k0;G>55n53z=}[ַ~p--^{ѹs-#F'J'o5&1Ǔ'LpnlDŽ񜻃H&a$^P還ľ-X m*"(r1 j#+!`8E+#p #dM6*'==C%[41y)^bQk$@D0%b֌"=&NG&Gh]$ڕu+u=d>488<'tK5\JdyHJ `m ?RZtx%ʩIycܶSLU2XQDeKk+=We+%̀3)|7`2"f_ |~;SRU5آiXI"B1-LTL 3Zq<'ýVkXC1J-p9ك $$xu=,SFRRO GP練 GGӟ=rYR~<\1D{oxðLdGE2$;2ͺ<-5~$1BR凞fxɕ$uLH^qN&iR9H$X` {Sx.H|ߕnd6et3yV bmDb);T\82Ĝ n.x##}VVq@4&3}IQ7_90?3]JFNbi0hWsm6F, fPP_D aEyXU=Վ/'2V(!;0rvL0H"Bh* 蒖\ vXU޸g?7[Xщg~s^toP$\k&IYf H9_zYn⠊g*bdè di#qDӬ>J f;eHbhW~M5eAN+(1[0/>e!ܷ0=\2q[c"x|8#k;̝cfB̤S\<)!#qr"gdERzΜ|N@!m&]Fj+a$\Y*D5NBG/G6-\hyxM5Vso!9~b#u@",TĆIm@qߧrK>|6WAK|**z{hkkH\=g*{jgĜYԨA˶}$VekB,g>{H8k4Oq(3i|Ak7$zОPtx D .`1$QVp *^~]KY+A@@ZIlxW{ rub[}=glh8Kڢr=B FD>o1'7N"~ hx!A+_i ;Dpͅ1B`=0 oTf/|W_ 'zַ\̧Z qOՊNT: p>?#݊U*d!Z*XH<̗-U[MLe>N'i Xx`P!9ncpp㾃 O%b6-kL@ j2( ~npfMTˠGJSb$j^8gϟNc"ڶmLuCbhdÒeݙ.K֦~Ɇ I{ǰ!+kVHL:%Y,zT55H$+q՜ r#I k/>؁ݹ>p=~o|,<4,ha.\sݸٸ8'> YY@9dS2G<;|LC"vιhE2XULzدZl"n 6S#q֊Om$[U}%ua e IA+9mg&n$<^۟ɍ)s0FU,Y/-[!. n%CEzm>#wZrd'硫HM%Wh7;D5^|j >ܛ0D;'w=xumi_L IJϊ2'.< &^J`kcgON(Wټ:-L *H\cpE ݪO$}뉞o}ۿup^yڵk?TC)[Y%zʲ]~$lDa0IC$e&?&Ƙd֐%|m[-vi>K/ ')cXmuH m$C ҅:Hx {Ol<mʾ8m,}JFMEkag<^ϱ0PdP$DbwZ979 1Hlp_jy ԆB7-Y97 79.a#$p|W_ѲWo?^.' l =|R-엒sb*Ab83()b6*0`VsVEec?jUjM胂Zƿq!#a`zQYg7熇u 2AY%߅HL_ 9jޏ$@KmqUz^o l}L?s-LCy:-Y&$ZeqDtTi"g vUx*r~oQA<5bC } 4N$gMB9MZ1 6B^e.o'[ARKNT=V&` eakLȍ dC. L/}[]b3 D Pll"gcߖyw['$Q9KFJj_ Vzz45c[dhQ"CNxΡ¯v"Fv.p*" >MXhZ!-O^uR"KA+R-'.Ҥ{ZueLhYׯU1dM2MBtI %lL$LU|Vl=ےI-PB,:<g,\~bmH/|)lb.a#=:8cMR3Zq+ SȽUKva+1BQ*4.#O:Lt}tTz"KQ~^ytt[o?'yַ|1*3daT Xaf!͚5-Hp8YTm,=t_J6e`nFǖ^ Kycֽ$FAFv<||Pcʙ(ށ+7#x ުa(Ȥ`gO%=[驃5XZA>r\K$& `h֍njUKD"UVN!#f90*R킴Ju*C *l,8ކVwmV%,ct輊LAOY:ZԘNuxf AfM[w KNj2`,B3q7^ڄ70_քmy*Hr.jQC9qB*ymV=|!ٌ_X |ĥF2SQf(v|pP%e{7 0i%+D^A̓6\I]$jd,ɦrR9N O ko.6h:=3Yu誙|x z f6ktqd-ԠS֐r`EH,硡mC!,%N`2Q>~ɓz ģy%h]ɊGOOiZl~)f*O]iZ/Njvnj=}[HO̓O>yk6;<պ*NJh{{EZυ@b]!|^<? C  _vNĄELrnP@ð&,ܖQ~,aaTסe}8,>#]a`#d~W)r^.MH&W*p?=_,~/d\ĎKy#5O4x,k^[b#Gs6k4H ;-qra"QƕHf\ŋoq^x'zַ6\.#;v :!}5Lr9s~8of+гl]{4[= s&k3 gǭ˒ -Y5@"HsMX!B-dT=0W5 Z6X7NXPveV%͢r]7iޑ)=2q.*L&kGZ#@ eHҌ-N% X`AS%Zw.#L1H gz?2Zwe&2sVM7 x},~楒<d>20 PW) Kv)Z4ܛgL9NP<]L=|dYdG䌤06&\ H)+}z蟅2{CxSp|7X lb^d3VI҅]r y]W$J{ٯTULx7ON&T,gV2#քounp&if0vy6;4탰my/ʣmA#zf`> }w`$k6|gG%O9N+l"ʰK"kd_Kv% qPE"kɷz׾{o}hm z9LU6rEvhL>4~GÁu&+y@U}+.Z=,=Tr/a%68K  E= 1#~suC8iXO "KѲùZRP| |wna:hdž# aZaȺc0A vC&dC*J%V}D8G~Cr Na2'Z]W#GǏoH`BZ@S[$EYl UTم ZaC㓂BǤ0\G+hأ_L 7֤͜ibvSqF3辛h9@R DdF ($@RZπ,|@P_;}O$\ynȒ6 ưBJ<'j]2J@*IefvɘN_#kYpM'9iQ@Ia%횰2u a1620Kxz)QXՎs!TYV*п)VL"lYe{SdDQ-2pc|}1YJwƒYjV/X$(0@ O&\79#3g7aT233-Ҕ03B3f<m91%]o$>˖U,]bL({;QIx 6^{<<7_-`0X{ GvxZ~(Vd(Vb ;mx;EQ0bU70<51-ʠaSgvLab.lc48آ5C^sZgsyH$m";Gw$n޼y(;w޵GGG޽wk6;ð}7 /ñE%PF*.4|e})^y啻O>d_-}/V$5_1 bCxkZ``"%yr0D 8+sdޏ1^j] p{Kx{Yqg>;/0%)Mh-/|XkwV N \ *= vq-\OL'_ҜuI'躾j(!Uw:cUkas#w`,ܸO'w#$Jǜ%^\fW@ԉqXV(xM*DJDV>˚[srA*6O$}뉞o}hz*Z'x/gYxaj"&Kc*oCU*HD=҆@bIA&&B:uɞI`y4ݙ*~r$Xto= |HB(fNnn[%ope^PNmꃬ]smx2G{8uCG2 #R?!Oxo~_+o}#zΏO5rf5)CpADlVYcS5g2["OA2+Gemsg[wKY# 'OY89!V8Y%z6 *1Kf  8ܩ1#$:*҈в<]otTCm|A!V5NY$zV9I2>jDm4G$A$Q1B66d(#} Sʉ%hF Uw^xL{^7:(O4iT-E·+S9Dk)mJ[ {f^'o5V/74`EX^2-^8ʢ0]RNje$^m\VmNmm4peQH̻j&nlh.YZE*s \'O[[jfRK %VWn.Od?r̹1cM!S ?jlL>T5n*k1g9)T6S1GY+kpp*AQF9H 2HgX}G2tUA^U -Vqh"ߚDg6zMV'NO~Z(\TǷaUPɖq"/NRޒ631wlk[Q&S}-V\yRTpPhUb$'|k$&(a2~V0L~/䡩{ !Z\ݰנqx k>!92RHOO7o󣣣^,Іm؎O:@h/l :믄y_$bؗ9qܤ76w|q@n&G^hu*_|qŋ*[~e!J % x̯J4,eJXCbBފj!7) tJPc$.SED>>r0/q uk`Ξ Ub>pit1'K 7GODbJM+;Q/?2H@tU?(bm7*eZj!$a>wED ''sKGGU,2YvS1l(xӱ7Tz \>".}LfU^!Gx6sHC%M|Y "^!J(%,S)9lLdsIWLӱfyAGbc(\zbo^LnpkwOUsWH6j' h5VgU<۵EfLK$Zub'uTn61"Cg_0*`]jpE{iᡁKg,| xlnIF> b=T3RQ$^.9Q (⿗ n}IsX/[<,<|`fa0LjE{իW[QL r7>;Ud{ZTǃ*A{_p>gϞ/_~磣#/m;H.!T8>Q?ߗ__o+o}ka>.a%`)8)Pv`t0Y5Is6 jRdlqJ AE e$3h \y}~FNŖFbW\8x섁Zc% 0On9H,x񂁷.vxT % Z8Cؑ8:srkp|fI*ٲJ@F*hіeVIN/@dRJ0HNYγ[hpU3XJ?xT5_]5PC"܀i8dQ_c5N)DQMNAS7.f,*=KX) &ZoMNy| ¿g5#I1 7>J``z`"S2"š|J+Y$"ٶDVsWAUL)zj/dj |Hpw퐂j<sj{Av 0::FƴŶWH\LOyrQq[ Cq4`DX ?UΜ}~7ڂ=ܯ[DDMQeE} kb>lX*D`bFNQdIѕ#subƄfG%1͟/"?xެØCpǴЙ- ᄆ<=ۨth>pb'1ѣdDGDUt:4)%jh8Ɗ՚8C2M/x|1%8-m1pɒ\U:_LjmBJ X3Qq5].\!V09a[y y O-3 t T (t3,0 IJx‚&]nIYQ_(AUf2ЂdL !%d)YemS.?} q PNa䃨DJlpv ?󊃿?]p7YX3EYYi˩Yn+a>g;2ȔAnkg3">ߋ͚*ݲB><{9-ɺWN"erGFb?lIkNlaC6 cbޚnG,#,4[Ji}F)I7f0X(Izo}[Ү]f0ٳ?}>|UqJZ *vp@hQFgE6JfΊោ {Qwj#i$'h)q3=[D?4Y5RPM'"('XmWB&=wxY. 3gT[[[BZM|1 41H܊*6LiAWq]:BW+Z B<.X@i xìPwZ؝ acȀT6~:b/XaQkQdVljݝnL -ϲpiׅԙ4>VȘ5ec1kIaX$`dL |kY, h~m3X);6)ġ#5JQEb}f|1fEDHN,>?E #X擻-q ODfE_[R^VNXcx-uls2ZRHssq-Ӓssvj0?'zַo{okɑ"2v{k/E*ҋ[=f HXl؀0`~_ ̌%X gFXwKR/f],.UE^u׳dF8~[/OUOe{N<ﯤ!Ƀ˯ݼyk׎S~R̲Re9CU%)x3 |gggϿyonooɓ'7?# Dl6p~+Ox?3ַ_Ά9\żJV?.!0XF~x?VZP/[{eb(W r^T ڑ?:8 ̫b 2q .>qH-A-}4_~i >[k 5H 1 $F>9h܉*V 3p+ `cǗP+>[&uTrG lNJw+3}Ij(+jrW3{X.صeEHR'Fc2 (7ȀiTuV"ʖS s&O,vuzc8*ô m.Z"Ŕ ƴ1dD#ۘ1z}V 捀> @k]ˬFɞX[L@v1 @- ؋SӰ3IAPju䪜T[l|"kق.H=N3wtlcd]BDbYcd:w7Hwpw7yN&k*[yYHv>ыCc0\"ĩ[14;lY"1J0QʇDsH9!e R᫯׶Tmx)n0)Lsm!bjdʛ(D"EE(=j[ D5EMg>MN[3c 2h/熳z$UR B|Y'=jAֈN'䉗㫖m:D'lؚ-A5O{S4DnHc}ER%\7A ]b5?,uTf:^Tzt-s>>EEYRlu&mcD5$xHEx2ȣ6tE |46:7^ـʞ=|<>fH?$ru8e dş16bV8?˦=`aIuG#ysCRnS'S_ ۿ8VhY9JW'JN1Ksx[Cbuyp7C:k:sS$sΐ>N L4GVUu+z=}[ַOw٬Q(0[[[AHrt̊߇www?>LPURsdf-}_HDxΐk"p͘i!w0ot*e(~TZC!dU6YUwq@1KkŽ#U-IΑFn߾}xƍ?Frҥ^z駟>ꫯ~`0*11g_3o}miN̦ C@Z\2wҺߧdVe~/Qkr.Z g[6 9C&p"79\1K %@JkD:A;΁MhrTNgGi_NnpjwBR m3*͌"jwӰ(F;#PL6U13%[H3h[&X tV ;B-O 3yPZQV8|<9Q,d0Qxz5%\+ !<>~@`t1r*d(c6 Шr%gČE6h@PhM7f_#@p8sZ/Gˤ S0jؽ!j:Q鏢ҼD\'+L!)Pd(bS@ \ Ʉ 0m$є2~qJĭ0 [9+i޻4mΈI$eɖj!6o s;>wa?3;HL{GpG7qk,8>0&G# gN&|Cbm|:k lJ rx}Wrr'sBu/NuR*n| [3jg@H-QEh~@G$kϪ _-(ShkuN|d9mt:hh*BJq>ڻR hl\\ǃdLz>b-dBBb47! *zCd+*/U(SKذe<svT1d&e"LSaL0* WdnFU'W YLK_52p v inwX&Iz%5bٔ\bŚ\yU΀:r +XV\̪ͤn{O5+4o ~z}㹚(/oQ>VM1Ak[U q@g e[4%}HַB Q'LԄ$dxQx<$Lt(I9g!ie='ae,+}tPΟlYm?[/9AJh(r 9MTaB|a dGpn[~4=aG3Xd sVN2y|&T~(ݯt< j*&yB׊8U-qb&yKE:~[Oo}[ʛZ:u[n p5Ē*EɌǏGCr?;ßNӅ(w+w子8H3*|jy/(<~+M&vTcYlؖAa:eL1]ײ#}t6XEQM9oþ\2)L6ldO~|0ub&OH)i^z饃ϟτ헻9쬟uplpC7ǵUBTUr*{TIlV!!#Q6b:)(Z;wfP Ӳ^~v =̱;hɒ&kY۱1TbE-pUF oG30 {G-kl9kP4^*Z,CŒTExD @cX0(s^![Zr`zP ˾XMyb2 1^%~4I,yGN ԑ5\B5 : *'+r%풂q?y _6PKUt @٢ZZ%g_UYTU%w, &ǥ<OA W,b&FtZɊ1Q6h|.:Y=)Rŭjs÷_`P-`2jdtc2O]<x$F?ғH62Or`P"ϪyŢ1S޻=H3V𓫇㟦{v0d9TVGcp6t^$Yο|4 s;Y2!l6UV)rS,=KH5y=REVu մ Us;CXEI/B@뙐߮KX wZ>h;@顫tˢb^@֯o!B@Er~Sx z` ^?3k0-ԲǾQ$8(UI!j9!߸⒞>bVG.j[/ì !ױ! yU5Yz$zODZܞE!#ToÉujpqm&i|%|a)%;h=ܣđ>vҢ?`&!3;}&3SM4`7="5ة৷ xi.k|Q˵ w;3"{۬Q}m.J鹸TS9yۗ<ﻠo}[{sνgmnxARܼy޽<˕UWH7p3 K?!b",iN wfBpNnzR@!x@>a)]JQ6 Y#Y9 =΂-fp(%)ǒTbYG>AT3Jߙi{Z)0\l".YBN B|}V~l ;.H YWe<11VH8\@PIKAr%bf?q(6i 83cdW3Yɉݩ9*0=f(W\ m%FlcL2I FC(x%"EZGp0'ʄ4 A&#*υλ "R&XmX/W\&P7$rJ $; q844? V(ߜ9p:O'>Xdyo2E\P,pZ! CU$ȕX!LC*E?elCk8/Ǖ* J6V^Ag1ה ߗ _(:jwJ%yEqBx@pqGwm-r GMڇyڗ N6aY!OHz89 KmXEK.B❸ lHb[b[1!*f54e*=oUjafrq2\7~h#y9z9}Cke5O%GCx\_Ox O TJ*bJ&Q9Eɣ/`TGEu@C۱oS'*J:?CV(qa2%]βQCTK+1KtMxfPuȤLW]ʋ ^c?ߤ6V=sޗ e[R` 8vҧmnȖ2*f5@f%q*ө6YY[Oo}[~.MU=ǏϬ%%6d@b& <־l65!.L EQ}5Bx!r&۱ӱL(<_ X e}<}|gY8"*>j66J!:,.dɾYf̝$^ZOˠeË/>џ}/_izܛ'}@l*Uwъ< բ .BY^(%.(!;,?ݣܙ'8SI1,8x!Jł+Ͷ  UJdIV$R&~p0sx.컇 1H(@rd L.Y=XA3 E.FA5`e LgU^IH% Fj6> .J6*)*slDYk / B0!hbYV\kYO1Wݣ$.7o8R$:g9gZRca "7Qx=mT2$lЮ @~@&T6J33|CF{K R!S,X՚{Ⱥ^%T 8-i@<9+cPUoΪ Jt ;!PN2枈JXqfki>EC JŎƄ[|\Ut #]t} )cSH&BW8>O#S4'P~w~4:lxF< DJQm2D)A!9sMtG;ori .bFkVȹg@A<Ɯ-=/zћ|.ƣA3\T DRwoavo~a._%IOsN/6`fu>g &ly͐Ea~Qy3dPFudcQs?β\3ksX TO/eKu-$υ4~8b%Y_L\k1ʈ v4ij|Dc1J-ҿ[$ X[Oo}[~nߧ۟zf,!6CIfRêXtcǎ}9Qs$dCB,֐eP7, nOf-6/'䎓}VYc U~ZV,8z99BMAl梨~l;Yv],[$=}|}g@ec=J:oW*A`Ļ%K`Zw*hռZ8&zy~G;*6򾆑og7`}ʽ9<\ɖ(@5[5 BIqUZa*|tDs#B zxdu !bgΑV/NL-T8@ii)1W; ,c D4lk"L6՚Ѿ;a C.>gDꩨpLBK+o[% 9Z%F;J^SX,2Cﱚ59'BESgR9ʦ> }Թj" +Ӛ?؜<豏&AWIdrBĢPu?UGHsqo $W {Dۦ@`#*}d(/LٞЃM*V_`]TWԫeULS_hlIEQbeVO*xE L XESzgN!i$tu x:lO8F +\&-f4bVn0dUTL4X`ɜXl BB(Xn. b y6Z'?M>If4ޑL-TfZʍ)Olהs~EbG5 Z!lA@iU|Vc &aux|ϣ=}[7jϟ۔W^yܝ;w._v׀n?Y946[ƾ?L^{ݮzbBh.wC`6/9!P>eqNSՋQ2-?$\\,9=Jh0 jJuk<KGNQҏ:gee%=l TKnet>ABm^xᅃ_|x?ַѳ~kEAY~#!Cl L ؂4K Z S0zt98@lb\pU)|EE iמio7jKwoD0K=k6D%싟{S;lҷ @yC?ʟ RM;8z-{h3lƿ@* 8=$+]713BDJ>*u{S!f@Fptu2 hڢ,y[dr Ui6g ~NwwpxƬJu;+8*a_Dq&OCvr2GJPrJ.Mu;i[nF;{sk#5Agq4_{c|uNoVi_hO7v~NRԕHNF`,rNY@Ob$&XT=y~/ <.D )V\r$Kz9.rB[eU:Ēus+ V2D r[lBTR[s!ݗ| r35Ve е=㬵.AslJQ!f 쮅p?Kcf2JVu_ܞ ևxӹQE+gEV̹R/ok9.hl@@svEkjeb摊I}$8iR՜?jh 5}Ww:l!ϰ"EfHiLB>ݘOou"{ <e% ⹧`m@/˅ w6:U= Kp}pڷ Ã$ [ɤeDRAOW*.HACΎ*3LM kأP6 x~%:B-U]Ol4*P*(9Bx*Բ> 4WL*Wr xҠ&Zk_YE 0cpfBܖȤTsylF7mk%s|<4Xu5<Ke?#Ĭ:#Bi9 \wUH4=* ,1GFBmPŸ1ĥB<ڎpz&o<}o|PE I6@?+bƂU(fM|l]朂2Ƅ A jywHD#D7 aL|1UR3hВvinZqB,#IkBV<# DkP7=% VP}o\3l*O~׏ Omz>@6.(0 Ǔ F2*.c:gm}9Ny2Q(,"pbs yN~qP+专2UHͨ~tz_M Go|;;w4 oMס$~(QedFC^+e(hmm m0GMhE3_ Y&c|[G|ji.3Ơz#`ϙ14ᕬjՁ+6ûEm@+M݉Tُ핰x?`ƨ5LتlFNm[X_yyg@"HM R%*\o۷VALΰEڶa0~n2bj7^kT08͊ u_Dr@|l6JTf .!h_Ӫ[joE֝*24BHAL\[Ήu-cA(SJ(.s4T>>񊇽lbt8V410D0 P|V4 kb^^.Z 9T7?R /ujA[ .1#c1sl=T,!27B텶,o%A/R`8[k8Y^Nȁle&(a9,fEsUyMy~q*!gaeܐBiԯ+inALQ`r9d%C* =!>yE`TWxwE˘Zknj )f|V߉3|r/W̝bKZ&pdPLQh_w<,i6V!ufΨus1Ԣ"ä#8v^ڀ# x0~I}+GpbkObŌ\ٹdY !3W©'d|`sFa 3^Bm͢΁U.c"S; \T;VJ! q?{{^8A$P@,ɲ%HB,ن>'թĶ[bv2Rr|wyqg-ތhY}r'$isҵS8{b\T sҼI7.ӽW-ycCg(T՝63.[. Rha69qrҷ[ַ̙3ӽ{ypp@^zc-@ȣꕵomnn^` *{ЮM]ROD|]&{ DQ0>Zɹ ͛7{uׯ$$bg9oED$Yi9G|+Zlu6i׆oDLڴO|_zWou'Ӯdx M@ŇցK_aR5{sڕ|ȕ fHu6cugsA=.C7Ҕ[x lEp@قSX ޶ l[4{BcZC,st )oJ˫ͦ^OgJ*J1긊ܣ߫BԒD^xmv:[KW2{4tp3=j)W8gvFrd@K4XZE'dد[X) yo]/m%j,\s4n^3)jLڜFSe3ܸ89ձX2@>O"w=?ui TQDмQ]Awj,kPt}4רL&=.*66wB=}[ַ_X{s={_\ :xi~!Zyswwmې݇E@Y2 i*!pi5%F~%ZmZ//˗/4,-JݠeJ g}Ar BN*#BQDcיe}.\pŋQ޷}drҳz40&!񓑇*x&wtM HH(@p2(\t9I ؒN cg'⢅Ѩ'O B Er>\Q9Q.6ﭯ (ca& \=c ܝmS͋*AJlXI (y5^0[{.DXD(%7,<8$CXbgz'^bζVe&҈H?/mWHj!JVhq*+UGRΞ~ܛ8q }>+ ;|l Pa D[;^k{/"|9Z^ɱrNA{#$r>q ~eQ idéQ i;H &]Gr츄S%ߧbQY5䘨Uwڭ ,4?)R{׽e>8JIˌaXgh1O9c6[xp09A ê0TiINP0Mڵ1qdJ2YF2CI)OWZ+1mo1xd% zoYbNHBV)!@WO4]q[Vd,U\ss@bK 6A>qNi;YΨA%T WK2,KAJ */9QFjZB"14Kۨ6xV9v5$.idRZ4sk `=rM6gtCtT=vyUS^Ui8ELyΊe+cT@9+-"y[ o9~EV3T-N}hXPcX;zनQ~qylmV mnnmo?'zַovyz;vxݿl9C,>X"Y^NӰ.$p8<~ĉ> &2XVVSN<E6m^cu#3JMIXK,3`,ϘKdUGldTTՔ)hvdRH^Շ_җ~o{O<S19P>n\F޿Rmp&XfpNZţYLl]|UcSd?C0wk0Ӄ'jXYקY ˃ߏ\<#قۃz8K~4T @6qp8mH3 iK8ݞim9y6dJ@y#$HȪ"KV vHH+v6F-µ:I\QMxDGXgeD-B礈-Sʨ(Y.a m_&0_pxI!U}EzgN8n.%I /ͼV zeP\#qR`l+ȶCYM7#-(#;9@s//@dN9"Q:HrL1x^TI{ BXS#Q-W#{(`$UoL: oehC;i?HNh;V |˘ɳ#9}'rƕ(&B7YXs`)Se^EiXkU fD9)EbBͨI_צIkpkVk>+е2gN-mcBi6\ 64>ҹDW t~hh+(ZX<M輛A-̿J˖t3̬3ql֊*n7i+};Djf?4GJcQ^pHTp~ ڹ߄)&ycel%!Xί-GHȸDLp'RM- ѵ+#ќ06eQBGUz؂Qzk[1K_u"B. r}uag>˳%^znoOp< FR#w;57/)Yꌭ$29ī5HFa@}|Nn.8чٔ@TKlcy6ۇO)o=ӷo}6ygE.;fwS!g;(vwCNP562fHX@=@qFJZϓ'F5D@`2H݃h9>1i2PThhN(] ܜ=tndF`0^n ㋕nTR8j.WH%(f`P~C+ f |z͒JJ>D%}#m"0lLURHvH@}`4h8-4K ~lH[r@XT eZ!|!;.vhjϚDƅ1Y"&pȴ7Wv*QHV$+G6\[Ln Ε %(9@1*:cA삾AXl:t$8&[ 1%?ulŒ;>J ÛoS1%MXO܄  S*4? ($ JΉD[oܛ|` ϜYI۬`X\&ZuvY.6nL#9Ke?˿upn=⢭$##:LjϳR(6+\ãJrP<+QDb9^ (b"zYy{4J~t^aċ4kIʗ?d9IAJd{0Wȋrn@V)tfD"YIi5Ծ6bVe/.ll(ǧ-j 5mEc.﹚i3#x1>6*[NFレ15BcWOamN;}o3{iy-x1]{Ws:+ԍ7Ug"C@&KTb+]֟ՌMCb!]&Oǻ\̤x%6_ц!JPI[d6-d:V<SsaB0F&:8P_h%G PD HO3{k;Аn*͛3~:HG=ĂYE"y4߄<+bPK2: JTiRt=>H ސ[X&9WNQ %[%r]V?>ymO|צ8YC.rkgQ?/Du[/`}n1!h=RZl{F&*:[4&]>[)oO}wDOַDO:bdB75sG 4{^L*Btlmmscoo3fԉFESXt lV# TbGɀ5ڮ;LRJEH NP+ "a#OH }Xqz8fK-Re@UK!6@&jQ/HE#َ\H*jic|mm[r>:1p_hcV+ ć>f[7VdJ:+*AEy$S5c8sCijRx8I]Ka} ۈՖLp{sنLYQsJX ɿI%pk*vHp Zj[2No4}ZVg1| $ sw&dBQ`}kWU&Kɦ(G~`Rɫ#]lui|>\هwO=d5}fذREc8]q'9L5Z 5]2T 2k~?yógWE *kf76&9]01{~xk䱕#p qdC]{%4 U+J%jV,TC7Ϯ#wa^dlK&5oӐfOV7 A2:ܣ@m8 Ȁ|gT&KBnyTȘ dȳ[ (K~ɵRGfX+5%H!D6isJL^ %D kC% F 2N,A*:+fUźQ-/yƋE$*')Q#o 8wrD>o B'qŕ;.d9abt%UhKqސD I;46E ACr"onZ\>X3o56L[~~ΝB=C~xsv[8Hc |xaKE~PV>mf̵MT䙐e|$5pXŃB)<_O{ODOַESUϥK~޽{v7[&u/<灐$cǞ988xfU;J|ͷbiL2c_˙;QemT yuy9QnvD ydyzxee,!g@bx_'NA?ַv޾yISR'O+#O{=$W(4BE!laJ\ap8sp!UA^{sVac҂k*gA0]0I@NBb \ Z)ЌVsJF>~sVΝ<_1ɖ?RW?R ~4ol'+A|V;A |'A3oPOJ% R $VBm(UD Nr3JI&@T= h{K5C>wrDq%\Ҹ٨a}4IOp20=L_Xu,{Ȃm%v㑐hukK ) `CSo$ @F *_(ę x=Ūz "o۠bBWԋboJdłlڰ|$hx;!rh3>bؚ٬0L"*?;Sệ)lFcj1 Vp8Cd[2^~zNc#XMˏ|Q:@yMHsfiL~0kx)|tcF#NKA?مU8plt*!!y另pV ˊaWOaqa?L`C>i9 &mTz%8Q} ^SMo4E)kIXry W?7_[IwZo}qj% lx> 0]9r0ܥl#(ۜtn&iW!w1ݎs C꺖Ae[d79v{Gzo}[׮8q9N-Ŵ qQ /#+/9;X?h'SK BrQELGMlk lЧyT$SHob+%gRR'^H3w4G%ebIIV2zP-Z1af!i_y^o{c?ַ*ѳNA5̱P1n93H !{6|u \1a ]':n>lֽl 0~ d4HĖA~T ؈[3:!.f+!,pGDe˨Vv TX_ɲUW}PK(A\@ 4H(߉# Ju(@*0pi+wX34c5V,1!L2+jݐUAJR0 >lflSDc @g&,倁qżS;sv!4(];񘵒Xr٦' U0oے5"ke!pe2IbkHb-䜵7~*KR/x%;eLPWyl3"?U-]TTn]uccR+ƌ: jS{Zw1WڻX]-0  $R1G{p(F:vtD]hvl<nEpܮb %׆\2MT=ˡ{ y*nĹTG*b>O#tm>o<§-b46Rٚ*vBmԎ-T*gfnkm%rYdUG ,pZ\4@̧67$lMѨ-LC"NMv [M\Hc smy>4˴B!Td³ˣ%RZ3r+ëw #Q43\iN`?έ©~o]M5t qP,!m% ח(x5]RGB]Oh6ǁk3NqrwB |oQ5>^?!^}vT^X,.*o=h]Ag `@A[.|tehds \(cFAB`K~ӧSxVW>=}[ַVի… ս{駟~m%xQj  Ψhsj r1ĀXJy|o:miPmvj_ >38jVaDG!reA4(%#h.T) GņH+U⢡j\t (2Ƈ8 $GRƷtxxGHd@\NL*cn` WWUU8Yu 6;hvsD 9__ N|i+wxB³Fol3V''pf 1J3Q s=IJZ 쬯sgGG8WGV\j_zi_Ow+Ϊ[k7wak<gv6WOaDs&4rXi~M^HM4}N輧G<7vFI+,76zm8KU cǓ>d2煿pIy$H׎M#\H[BRhB[,9\*UR6j6[ftId,;F~dH !%d5so}m m (׬t|!P4w޺gZ@sgfGf+`N3Qց;!X&Ww<u}}jѷ[ַj>,mooû#-٦]8=M~~{[[[ߺukaɢ8B(9*Ƀ-)yo\3c2rtΐ?AG!}Dz>q!jTW\6p^ j|†-X2|'Ę*ݥ/PMq>=bte"Bh}Pؐޛr\Wb;ιNo0 P5pIb-١Ya֏_?v8hG،vlRM")3$ ;*ӹ]A IaRr%yq;k-)ܠqS @sb4U񦰂@!|DwBiB-Z X Ę8\l\ak2#՛a(+DV.hk$]G7:yWakRmFTz9cז$Pt^Ct D*&Փ,wLS'@\"{8H,r REyU2hqRc7kcν)(R`#Ɛl>W*%=+IiTU)%4iN%|%|PO5cBs<3O܅7ݿwG9񰱖KZؑ%Aw" Zߺ6=CІ6ڵk͛=g$25CȊY{g?ۏl6 gbԦ-bg^a5K3~Ǝfx!u:Mb Qe*}!}2 !ك* m RJXh܎x<>cO̿o'|rP mh&z\xʟv)v5 ܁4(CײިW0W[阇 LP9¹ Z Ѳh*HjJ!'!- p<,=v&lhTc+-`TQA`XUlj*ڈfRGZ͡=,QOtxLFRIm(U1rt[BdWvkUWjrW YYE8Q@[@shC[bh;!(- zN'9HU;AdVōr#Ρ! 1hT\:vgkTpsb9{$^99$]`<g%I rR FcѨX҉JF.fRCWUv_%gg0!obe c|?UIzTRWbWRe3)l/b&u$IUKb Kxн DžfO?'G-?_Nu$"4s߆i8wj R_o}tY,]Ͼw\QYU`!\g)]B34ׇE=s BL4V߾躓4HF&ygڂf/Ȋ[&$s,(vL{y/!zJD1#&=bU}2砬ߨ0 M?Lv9퟼.6TU߸O;/bks άKWk=nz{=B.$XI; WlY u!8N ݐg' DІ6 g۷o>B*d0سb/g$?SQ` :M{W~`0yJf6y&g UF`&r(BXzDL-vk/N {v , -h4ꚦMUpx/':+wnX{]wE6|?_J/LJRG}d^2T'{ G^6n)Vi#` M@#y$/ X.Kc]'6 jO?g=#(1WL8{-t"e8.?3Rt ȏl$7:݁d0YRW.g<˦e@z'F%Uŗ >~]o+UcѲ͎T{orl{A[r6@1E@@ _H }9AMMxw/C *h^VNxn\1OAiVeܒ`l N[< pdVkVmLwz!Է:Ȋ Ȑ0HU'V* '@iPhLƆBr4ddώk+%ȜT]WQ}q@~ubR;xJ~&<T1Le\"+٫W?oo{l X@EK!ha#&p؃dPq~nxT9 1Әo+ L5pa>W x($s=4\xz' @${ag'Y*W}7^J&R],:vqy.]HTg!XA `4qޠvx\1w ź2*B3lKBK*!}]>Z=A_Ur[͝QojS1PJtԊ }!w9MmAJ*;8Q6V`VH)rFyRR=*\>+Ƚz—w/ߚdp|»؂wyKᄱ\l^y_;Ek-0!B彲2>'̽>r ]߶L:އ!2\+B ɣE =eg snv߷w g Q_yQ&֪!:^QܴP/p>LXP&IV|ͅdw'cK%yn!ݖҽhch3 mhCnozӛk׮>h6Ng@ BX`~<Ϛg҄h֭[J;`ѹ3N䉽 h~ےjڭ;9/"$ODVrjV كkPl熱CLCe'Ӎ ߴ7މ a?Q_ O>ַ; Mۙ3gːA +IGE+S$GT*;Y9opIƱGvٚMW=[Nm~k ]mӠ*|= Y=}۰,1WܣVi+1Ch@"s+%{ 6&>+xDTJ@`%` ؈af ?~BL"99mFFOz* s ?Lm)$dM>@p J:g](~ j"ɣ d3Dr5+76OIdw'd ?J**}w7+|:|'3ڈù4AJCZJE Wq@W*cϞ$ѐ.q6I/C9Xx!|Z,!'>F@ױ)JEx/J 5 m 1[J:SG^^X) t _ ^{5,9y'ބJ$ƙ&TUL+|6}OåK;Z@G`jA>cIzU.w8^)0L Ī84!WլLd x[>;?na}3PgLj#~`Z "dGWҌ5gp" AÂXA! s}?%Pʺh bHhq#$Ȇt}Jf.4 +_ev+T']HŒPшk4 Im]F =h$l؟߇X<Ӱ5,tJd^&ZP+0*'7 ٔ"nxn155DBH,%L4JXbuױ/@vzRJ2tY,C`DV^? 4iM͎>i0 K{) ܲ>bV6b )XQk3gﱟ~tGu9xA h>ڨC$=Kx 8dg]Ζo;߮ak㎊P DOPfuzxoNj`bɶ&,1cƥVmh XF O»^1І6̶_|w}qwwG ;h6R*<2Dr yz><ٴ/(nQ qӳ+3ML>T B֨]*}*QXB QmOd^hf.?Jȫ z%V@;U9>hۖ~|s6vܹoxɪ dx֑*i̮ Ldm[čC 82Ƿ g%6\y s8bnG;:Kru8{iah dԕ@m*v A9|O'J(Ǽl^ GˤVe#L;B9-8±h$iYe]=7<SP1I;EV]zI c"qNwZ$QycWäiٺ >ߏ[ ;<<:~c u$mFid㈗% Fm'KDX0Zmd!ƌ![DP5[`V`HHXEi;7w=7psv ,ܜH̃ ~m5v5Sgۢ2uG1s& +Qj^%6*G)*[yMWR)^7~ƕ4ƴ g^w<wI>iby\k{f[c`,lIS%Zva+%l&U&:cֳJ޺V8(sVBKՠ<ElL*8Ǚ<+ӭ؛%EVꫣJi;΍3u~D|^:6=CІ6ن$^__d2Lu-3,D KDS A$L4齽qfQ7sZyOU?x%[ QRrGvľMT:(xp V 9)+]ilc qF.O -%rE/Cy{~O>CІfqN[6 lVf"ɣnw1ūZw v[TnmӸ:U o.a"3j%*𿪸 ?R']>"?Um$Hʂ@6jNd9LF):Prh9B$-QFU9=qFdacrU!?TP 8KA^&y\em1Xrkpa3n 6PyԎ")@tN"[dr@_T+z=Q^teAV0 ٓm\!RKnHvptƅ5aA._q6_N`>N+]NcHj)X\Vy(vO>[y9(JJ]jXb P6.g8t*/n>p?L6_|uۛиhᛘ +8u{.øqy{(u*&G::VjB 2'fbC-3U奟jOdE(c35%bE1i]*ktXnUECV@QIdKjX"Y|(J W}{o r7^ZL Hh"QJ^F[|lC:K }>N0uDYIL lq4N Y&TB4̒݅RdeTA>J!$c5OMf[|9t- m1p]p>]8XR>f>Qu\\AcŇ/XmV=}Tu[!z\k%Z,Kd>?=P4P( BwY|} p-%–#QV;B+}`T4:jNOv6=8Ҙ=nt0y:[!UM4Df,Z5ɒ&4%)ޓOXȣ~`hCІi/?clC`fz=$xBPrk muJ,F#w,١^})A<*\`NDZ)5P#$ ٻU)ڨe%[%ˡL&4MWdA"uxͮsϫ%ANNNG}Cz&sCІ-?~/╫'L`>gd{@! !4ц3{4yCCHqzs6wpV3՗dujn` Xʈpy d{4j&+fFXq"4  GRmD y>p4.1hPzJ6 d FZ 7j$;1\tAF\bj]&r" [ W3 @-JU.ghEmI%>@uǭ!^8DvpuzQ[S&D++a̫!28J3+^1T4_B\QߍCwb 6Hb#@((A@@ ͺ%~XQ:^2,s)ɗnq-YG6]7?pP9m"̜AU9h_?JEi$4)ł5YFM~lIB-ўmG#dzu.M88r̠ta5^?O, 8såsc:zQ~]\1pb0%UX!E@:/UIÒmK|V*Qm2 v}oojuH$}VK|(3CqPEE1gbQ, **=u,EGkꎠS+(M^!xalVT!-J6V;Ա:RDŽ <||IDnݘ2" 1`>d~-v *6|..ylIAUAJZ%\>c*s?|uU x#c>r~ 8т| 8GkX1i+v@+g#1VeȡG?UZuW-{!z4O_3O JV7Fs??AEfːbedgEkc^!A~t$:*$/a;r p@*bϦ~iC{>+֩SdxO mhCߪ{4M۷oQm?n<>΍U=˳~O},*Gƽ'''WڶVb7q}W@DK# R'%kcIħLTd,Z#r<B$iyU;9/_ugHII +Gotnutl3Gx o5.PFuC~.qZ S@B?KY3nR"R -߂#`K)4rZ5̓| @_e@NJhպ7˶f%F0H2?"~E/`VQkMa wd[!yzkTK ':LV2dXDimXd:9!\ߦuQ>ó!5/~Ej*/B.6x:qPb&U_C!w1%DzJ09=\Ui,@oL)_I%:p,H>]C,y"1ȔQ`5kq>( LJ1_/Jt-* g%ܺƅ>kM`͠ZN-'YĊ53ĿM-Bi/#f3<)dZ-(dKCI7\xҨ kwQs OMa{ "+J{}X|νJ(`RÕs_dgAΆDjڋRFInE*+U6BG<[EJG2n(Չy.$w%$.EL葍}vs&-u9>Xz1qQu b 1*0{ wdZCCʩUw jOX[3 #0CoPy;9caX'xϛsӦ4C q=5ks%2bVø n)A$K˴u*;묕[nO{~'s QZ!gOdK,~婷<ǿ;s2VƆ;::z[:/3< uhCjG>?Oa@94+JEB:Qa;Vk8&,[tazS3Ԍ ΰQ5z2n/Yzgå 1ri]XqYQ2jjE=bɖkz%'tL(XSW> r&d,C;^h14YX.WkMQ3t >7 sيȁXFxYTKf(-'EvT *!"w?|@%SzXR׾:Z-\pqnG2, 5Ei;[м#Ov"lmp[͠{~;mOdBD@@t+A?: 3V] z*)B.K }+т!y͋⪗"JAF2!"b'rs}6C:{=RAmGNJ m|p1ۣ]=k\a dE%Ԏ0; ։7&;^X<DsU:"+4G6]Gy[kF?gGw? ٓK$77+"BhCNe\٦n-dꝱ!dq,tyn z(Bx _\&8n89*FyU>UTTqeu(jJR81{1c융,1*:wR-\," Ա͏q6%_.lOSp ^;`i\2&mCyWT=VR>5yU`_ 6DV#e+.:H}A~x{:#<~-p~,Y:Rŗf "cU|0sn[{(֬B_Xq?$OmJJ3SVY1[Q>24ZaϪ[c6Dۭmq]cH} nOp*}gFc.e kbzzv=*VC($o4o["b.a5Oy/{//]ich?6X mhCMzW1ilB>,$IF'dQ3>5Ulnnooo;%l+mΒ<Ɩ#y#Ocl*!4Glj[|Ƌ\+ʟΒEBb|ǠN1mw#'`(\S2ʒYJQ&*_xn*xg۶ mhw #ճ6Z ơ}62o97AOjள5=mćaX%H&A#$h{&^C;m"Evm?#K^*t@\\Zk}́@N+% \ݝmf\F,,vvۄm~KwB(DxңܠE !DYI P&d8v}9VX3\lBC)Qž*CW˚bO$KF.]`f }oj]D ?<"wzvP,~VKV~r0v.[AfԮ-{Zjc%m[ن vO`*P b69Pᥫ/n̓@3c5<x|C9OʏSK-LתJQEfQ:jŶZT*bxA;Hiת@J|>FC; .o~`NdcI㴎瞻 }iEMnox׌dֹV\$fҏTU[~?x}R9/UN,+RZTilS1[e(vflG'cl *vmQQg`2qo-,z! 9z5y1زnn3o4b妟cexM` $Bm+87JK`;rnkd%dlŚ*>LЛ:g /LiAM>|+>Co݄ 3Vz1s]z `<`;Pߔx;ƞr- CB f!F)򲣹Qu٪=3WƙܷM{86JݸtVU^d#45hP=p;z|JxG;nZR'ߛ5 m!O9[.U9 OCiA3 mhC;z}W\y3j'<|ׅt1Q2D#$PH-nIbqF9Ԧ H͝=1Ow-CAgW %g+ s<嘄ϫ['۾5M3:XF;}.}[qu= .|O mChgΜ !iSUdFjD9 p n!XY4Ȳe$Om`2K8v'@|+Y"ik`TbÌz7] :tREH`r'RoP1{Ti#Y)\d~.<'8v@Gh<}rRj{6?4NjfuNAvw^BTs/0X€?]+dY+G(mQϨmc0qx}gI=/|غՕ)\vq_FGvT2WY{ Z3{Ξqp8kôg&J,rzPK$=A,`}B tJen/REkIԺqb)X".ꟲnrUIXq랁* ' (Fvݳlw Uԋz,ڬC>(+wY֝8׏#|14ڠ]ε8 <Yږ@cskDł{،l,!_%Y:%*Z*dH2$-VLN}춇\GjWv #U7M܄AjQ)W!=c>dupBRu[:ƒl W+d調vCQ٢$U+ Bc/LV]dZU}&ۆ2nff $'Cl p׉1}{iCZ7T'Lbw}KM)\mj?|'VؿnzBĒσǨXۿ;$AF%䄨1"_+='g9#ѾzY4ZeE@97T7Bo|J>zcB&Z2-ܼVIo8yǟx2lghCІwjh߆9*zm  gSBtY*s|9% -I[ߟTΪ=[\wO%or\Zf%wtjEq U 2 YUnZH7L&A1UI%%,I3Xq+/-o}g>7^: j?z;q5 Dq%΂@|;k*WHmZQ``3kpf XMYM`,gܵ?m ˭ZE۴1MxC==|#6s~ ?W#act~wu3:>is2ڲYQDO|Bl]q=07B@,^T6`6UVV&;&`%U.p;RJV?`cA6L-bPxȐ .WkVG@qWh@bYO ZT0=a5+NҶ-` 3*sjs z~U} -+*|!jqb\Oij]&ҊiggEO(y 41Tz5U}u5Q)]A4WB% XA EXn}Mm@ޏ݂@~%i"Y@EX\VN&\VY]u0U\qY`y"=E aJpagyx&pkᅛ 7 Us |~/Cڤb7O,[luMb5}H`.<\**U`2%dcyI\^~zdudJ1 !W0=v*E=Md NAQ `UcDz%|^Ϫ (VxU} IHk@20%=j:\VD!~ [;{*y\Y&hp |3[ɇv6a2ZyRuIVrnڷB2z/sf%"U?E!DնXhg`Uef,F5+m12l[J&J ؤ-ptYb#7mhNףVn yC-6oٖ6KvJO@ߘ"8m ;6=ME\.'/_~C6u-E/}5ٷE0,<c@ÓU2򬎠ڙu;pcnPލ6>XxuHxAHu탆f([9` r)<=Y` %vZAc} X[bUM@7X`TF({ֲ{)#`,Loվ2bj9c K(_„gXR9pR?S˹c "#2Oa-V7=7JZZWgQ<{֢<J@8 bBX0@= 5X4@BrXyXе)O1ӗsEwvmgBȮT{Y6˂@}A{̀YUW[xPMh0-z>x=th-VPPyKCď~ qYj)d\ UJ)}k,[ԗEox 5=p*9e`_\^_]F-g-˔8eGw( lfY푆Dл#Bm;CBl)茒Sbx\rhLX=EVa"+d:zYbR-ߺLHv f;:BƭLƞ2˶ӐɬBT3iF*Us(12=#^H w,ׇ͘! jɟB;w8W41ZL(YlVB@8#6Qn(q9h6]xe_څO8\dUe/F򆢰Ѱi,XfOhWr%45סw6o@ƕn +omjlmЌHmd\>(jZUNWi9dW07=lM%P!u%ͿtقԬMk3F/47ni~Lm}L{)ch?6X mhC U=<_I ^T ZzdLIY|R @h@dcccs:޾}(V"* /D ٢$MV<'d@Țh&Rc;r+ѣȏ3=NK:oAˣ|?hA=i&.-V[ه|KK>sOzC~VZ+~Zy uE퍴K1l-:V]tޑ.eDnbP3 hS[ '@#*]`cKsy\cTL ,h>o m*hqЊ2܎;YUDfyDR xQ{ lYg LV;-/ @c W^gB; &$ieH ۧErzQC"RU2JvcB/#P%7K0I8k+nG82]l7 DN@1_BI0y<|2UYq2+DKArzvMQJ/V8 ]d7"+'WN_FŦ$UΤ(1'[Xۙ6J;Ƌ3yҤޜ{f?Ĺt^1GPF$Ǫ@&c0 v/DCx-aU|o2*m[ ɓ-亣 lQܺU?<)i1/O?O8!k_}6py }-4$[+ZE5¤#Ns\$D|)d㼊liT.JgA,|Fԃ+&luwR lɤNLpYM}9v&>B}˶p2V%uCkTb!Мz^@ND!{.!WIvWlUBd0ٌ%k1ƒxHAьR^mTt?\%w>y̲#u2p8z$w:/~nO{лs5YXmfF;r+’ ATɈ̤3}}R茅b3 @qw>Q+^]J3nyH0ÊFv^bm3"6Czž!ܴBޔ͍ܵ`-W'v/4#[rvX.djGR\_ ɣdp7[r&73Vd7̡\a'm?8j EІ6πO̐[[["Iɝ# 2,Y2fsVEk>OzZSN([5Qx! Բ -3,OZG+ԖX-u+VtAB'DzF38Y )@J Pr.?fn%M< <>sg mhClw2 h!vD beF ׼T|ْqHXW>r=/}#Pz]OdK!` LT࠮dDD&^XH2g%H 2/fRUmvpPnQVв+ Z Em_]1U}r`.3 &:Dѻb`kV=6ju+[("؟4:R[ [ ·\.[1 ȫ2(By9 p_T{zd |teA1 ;!R9^yEm;g9S@a[Q*kc S옸!{7Y`2pS;*R+8~!+yNZ llpvWO+N |cxC>h]gawǀ)-߂k̫Ī++| L๖sV dQqVbȪvʼˀY+:19LѰҧ;ï6iJ\U ʰS<ѫ!ykYG:b۔ gEC)yQAT(ޯQ96sY"Ҩ /âUĂJ䐭 .(`w^yQTN@ࢀXT\+ND,_g t|pctת%3! M1MZ"{}Fq ۴buօ4$+7l1 lYl(ٷh9j&ִ}Wbh3 mhC{^xwda9/ĩ$4ڷ!wR/"|_'eeV؂9c*!ք y &B)t@z8sT#ԤcRuMBؠzljI'TkY[:Nam?pwOġwmhCڙ3gG:>1 r bU*8^S{P~\rb:Z߯+Ҳp򖢔 iBaD,;ɞJZۀs>kDKR$ .<ik&|;Ut)/ nt!D#D :RrhFd)&$~ ugw<ϟ&KPw6׫JX﫜E$bZ jX/C\VZ?/P&Z; qbآ_ A$꟞iD'f\Z@gџ*Qn |gC9l[yz?Ee>5T*wtMuЉ9s+@9]-/*Kb'kOȄw~wt1І6ngS[__Ϡ=JQ32)lB I9IRHNX %eccjSsBh"/k!o]-;Q 3Dd'bQԲm(d y^%V 24ۜu2S T lZ 䉢`?#q:O?w4ʡ mh=䛿Ko1gxcՅ~^G߰ɭ;&`R-e:/5hָ&+,H a}y_'c* 2'DBjNO:\P*U|"O;":[xӧ= WoU-Usx&g|^5gBƆ.-ƒl=(s0ddInU3d4d^`{$aB./LI_IɾQ呒(]( k貚1v|E _<(rC rU4\ϐ_bXbWUV<ՖK'S1&l`tР2+e+][+bzzrH7.EAtƌ/9DWvof8="C٧bh BJrOX+c#,J___{ctO=]o΄sz{7]A7ilL}+]1dz;t>|zrzh\!]V;A+ay<+ JoNm?ӜÓ~ڐ3 mhCw}Oz#VYRuG !4Pͳ~T ([ք!E$looty܉|&'8Fуɚ$u*m*y*'HР |p'Y;yd*sB#DL&y4j4]Uσ͋LAٲ|wy'mhC߸}m+3L-![ Ӱ5ڪ ]!TzA OI5]_դv-ݪl *= :"ID9@25 9>A$4BEQCsQ@JU,k%a*V*6hJ(3CP+M(]: ,k $@]R&YbUl4⭜; ldTg2M SV1YLɚG/cJ@6[,;3G^mi;//Uʳ.t MSu(|c*%XQBmDROӔY!eJE,A>iJ:hM W 4D2BbKLHY5]A9qe;cQa+BKL| >:i jx'SK#CXBPgArB"A$NTZ/?%vOY`#59^䆩ur,kyy2 Vj` NJP w«o=Mz/3oۃ4kpVB/;k[%ף^VbTVe8 _s@ 1Nsy:퉱wLu\UG%EKQ{#K>HY{BCB@'}/g*4/UrvjAdyȕ`Y ]K-d0JR|m˱pW4$Psy(0J*ݷ)ܤL.5ؤ |O#LdEp׶YT,]q ( R'd)xC`5:[|[u8޻/4yߝXi}${^9BϽ[Dsd1?BɅMY9dTk4fi譿wHL.9x,jkv݁"9fEj$$b QSe;6{CTVBV}Ǒ:݂㢟"M&bۀD÷~0qpf˩0@ mԑ6 PDOO,4@YY!1U21> J3cJig胀qK8[cclsˊQM{XɪpLAfNwdyL`<W?| OmMyES7.M$/ $6NW\sPu**2z>q3DE0@,戱Z`W$Q-4^Hog,PVä_a:k7~61j`ͷ_½ԃ[1'(/Qt 1& &/ gx8f]rΤX3ut{},+9ΔG];p/"=Jۭ)\1Mk#8%t͒g Dmy8{s{#8א|d/PZ !N9Fx[ʼn^3T&nL`b}V|'SҤ Qb'ҦoeβpS -,'_O9J3 AQ>Nn,:dTJܭe8+[b2pFTv}pf[gH5V| J-,6m]PȘIӣ$\HRłm9~:z>.?OC mhC?SO'~{6eەI%6Ҥ: ` SCX&D|N'$k,]&L6'oYHD+6ن/3!DQ&q_i3;@RK r!VywP! fwpyWGl3xf n0N :F(|/vs"K*~k'!:EAm]l܃3 \Y+IVd7j%++jR0F$_QA`$qPji}'tW^? z$zptVԣYT5[qT[>}n- =*B^aaסQJ.LQ dC {T`-c?ӭ:{O3߸΍Ve(ġ$hW=UUA֍3"r>Ueedș<3%[Q%hS.9Gjf"4  :[@L7g*$j!2 =ZAz1W`Ն긐Us;z[d}UecZd1 VnUlVv2蟕bXNs&HN<% '<\(U5'p${S?Z[a\w>y<_G:ky8;?Jsup* j"*;xxGNa}$u`}Jc[u z*^Жs=uHVSro2dRaPpE+o }n֓Yvݒ.4c+4U2Ω)*qɿyLbܩS>?7 ~.JJ~4ߧKN\2(I'%Hsp.ǿnd*!h| Hs~n>׾o mC~~裏}_w p&T"/K*!;lNKX,,A8-޻pv]>7=I`<8ꎒ%}tPTL)}2 %T2вhyߑBF-*!<Zk|ڟWnjbӴlTjZKUv"X4"&[ۨ3P#ٙpU1[)0=d]JVt9Z `UUg"A *G?2L,y+9[ԒKmсs*xS۩50|A{ǀ.8@ڋ*Ͽ>TCQ3X Lx&0duwoUmooF!g8C#H-IShZ`G!Z"d#,Z1D6"eKILM&R3CξpkwWݛm~ՏFRb_]u֭ssfz+4b&%:/ꚒdGƓ hfp|~:beR#zr}3j9f.~@p Y'\Isū\{qYs8BXSw%sI 3 >f5;>m9 >W2fC?%ׅC 1Ed cUl>uzCpiǑ1Ҝ pcK1кXɻEsb(KJH|/uŞ ڪa2,Th:r^`;N|] lE}YC XUq!Ģf˗ YMvqHE`9 -:_mP`B&:^h|ȇ^^K^os ):#`⮃_Ns'Y_Q+H "A:.]`2AjQeV؜V§xzMUqc_}f VGR ylmTt0&qG.~+h5y6e^v16"$+EX? s 1>|L)y/KB{`WǬB5艆L (Cctf'wt}X:xrqDy[7=g7Să׋v9N d#z~4&"kdCkS b+}I כ6C?eZ1X0T|g(6(z6 mhߔv7O< !c95F4xEY!0(YyV)& ɔ|^xRMȞdwHI46$}pǢQ2 [{9Y`fl e :dd꺖dP/6|] Qh qأ^,;Ln_K]wݽo=qhCv)D|U/k: =li&bU7R! +)pts>(?4& Eͮ4CdēV)r1=XDΜ}q`^T`Z KvUHxOҺwsͱ1|9ڴ:02̊kw B-t.ۊB/ё?~Q(V2@Sq R+䊂j~,`70.J ڝ5Tb+}OG~eɵS7^l)?‰Xq.6lA4їzT%6( |[r d6JC9g yp=04EOMdox zlC=2txt}Y;i}WCJ:}exvZ ] KfCic4fkKp)Hż~V6  +.$㧪&P1&˶2"wT\21HZg z|В/O4Sԗp pU1I} LRi6PdFUN7+}Q%UaW汒*CK ]>5e2Z<XWF`S ct o?>Zht$75de%_o79 @;hΎzMdvwbXcvΈ|4/B[@ùtLNבAaB};s8Has\5vpr3±C#XMKrD(} .^^PϵDN:=gv< /37э :7?4wТ85G9a^U,6FMsWhU;Heޘ39{6 `$7J3F,}jUDcE dVՒ22 *Wt>ޜbZ(箬k(X~MU(ikRC;k5Fi^^K+,ɓrNS&Y.\Cm-d{h;3x[to; DІ6 olN߻H bm;=B悐hvYش5U4(ng%8c݆lUq΁uNz@ЕNHϏТMft/.8}4Bը5{m\OA+!*j3gt? mhv7ђ"Ib|oBKd8,{\hɒg4rnjEUA9^ёprbӃ |-e2y5fU 6M߱&#]ɸNߏŇ|\F#GOSZLT};!A:b)&4BFU V7+H%[mwj%f{b$G(Ww(UeA6:@ -Rq(ୂH> R_`M!T#8ᕷCއn8>kGއbeG7 B:aȳ [42t&[0{>W$[Œ5%c•Z'=1ƃmKBFazGPQ#X(G3=Gr01^W /9[k ,ccQspamfڞڊo-+Sp]44{0ppBUUK # (Ifu^G/hA *ɽ"9S1فdNk>h_wd*dpe()fLҸJ';պV'ʉH"!DeC( N,\4_ 2ii U?R)T^`)X,zfe,W{a5H RK7ƾ/6LxM?(ʡ6lLam`ֲlLs7 sR Y3'^'0 6XINXaە1o}VSl# K9V3#ê*|DQ\)B*l;匍U#0`s^,]}H,cqt.uWEDC%-@Әx>yܗ.ڄ=3m \Vk6+V9  ~31S_N31tUU~'+2'ӹxʜBɝ>f.'lؒX_J6bqfZ2fV-Y9(9J ^ 5b~eFVlUz\/_[}ky[lp{S\{ wC mhCvM7}ۋ~l& ŞXS҅׮dݨRG }i,LïPI7l$qضaDT4mɡBHx!aAi$D35 C*yZ-琸GjT6B<:7PDELOﺻܘNzb}Cmo{77ۇwgDļq- j,1W 3@DWzcEtߋjd(sZ(U `8Up0렮 7X۶A7 -D݂ˑXS_{yo@[4v]6n̳h) YuoQ>0uGvL+H-ڮhS5rUx- 9*HMzBQ %'Uk0!؞ x$O'vuj%O`-yBª]V+1 Sc{= %L`| v-[T16-D !:xa_kOz{%GLAtDFMжЋu$n[_PW"B˲l/s Φ/(r' YMį vi\AƄKZ|M\"zvQe K"eZE("ݟUn:?o5I2Z:m!w,˶\Q/ܶ_݇Y `R}ny 7] [>=7nlo ; ^pxoJ4RLX>tf5ZCL4!/UˊTczIcSؽ0z\k^hH~1ր4^qv*_K(j\ԪЌ))ߚNjVBF\ V#ˁ^o%kjʸIm: 8{) }U':R (\Vr =n-Us_k|;5ghCІ7Mw]f Lsz.zYIϥE7U<9G.\&挢'An>/ocfڨ-Z4H:-DuJNLFƉ\#$HN?~ ݄ :] A+* ftL. HLDzΎ "|9v ୋr-Nx"+ȁ78sS>"@&l. ]`P&8G}Y qщjK&Ju0 ~E3ą|D9;JnQ p/A(`;!gxQ3 hǠ5 /LT9X9xVzrn5pxx{CV] 0A(vr TJ:Yުd,DDZ _V:B 7¢X[q?*h]QfEe3Yշ8uySl˶`jG韏< }ejy: "evtYTȇmǜy_?u dT`u FN:JN~W%ES࢕X DX^J JR(qamt3Rup)ach]=+6hvWpwUElE+0ۧʻ vGLH ڳ-&#D !sNy_kg^b. r!tVeQ2$uP[ C_`Hjc]-*y. _-z /%Ҝ(Bse e{196m\ +<Gonn ֫4pt& U?9]:Q[ֲa-5L涠k0\{?%xq'Jyn Yn:gS\9>*}$pY8"˙:L<*0$P3)^7e%sn.(D~d?>Ei7ޜ ַKTgI* ܳ>e (;i" /4:٢-+Lᒭ"[2ȗsDL,#ȢwO]s͠@ mhCоط]>?_pFE=h]y4H${بDARe.JZT1ɭ¥Gxb'k8b@ ߃JjUKq.eք![7!w*!w'H3tDV{!|Y@l6kmF{ /yI($(۝mN:: mhv]w{}>O\{J $-PٲhފB} 0Gjr<\prGb3&TAH3"rv&B2іkr$(A:̀XyaW0" E'|o7x d!J (F$)9썕L8Q+vB `F ϝ p~aT, OɯPtE/QIB  ?ι$D9VEaiET} a6 \ ^DXT;/sDŒTFw:B*gW1h5+'-e]n¿6LV;8qo hoxjQ`oN8 Ad'VtWr, 17 SᕄY ITkkQ 0{>FCe)?BK`Q0x~@9/,G9o2cRpkPXM^Q(98TdDW,V\ѨP qe^bA6^8J~Vj>7l&ygb埋4' :+Li2 A[GL\"$;:cYΤVӊmZ[}vTQڭ{W{j"bdm,TQYc$fd%O+\vN( ]qB\hKs*rrHt{RN(}߭׎/c;pbsmH:֞QZ 8E.! W *VTKy". 4ZYM3$ZaA/U hT=NcV(٪bf }㓪:fwUhaW 6mx+vi*qXvFT 㦩zJV+@%y% (>qډ"wEV;q^GG`y̕F$֖BC%̊o;Up|TZVƧ)eXDa2PY[Jj!(g)ĐuAY%Ӈ\QoAy=гlvJJs&XQ k? %UDdv3BD*[!y!4 0ͼ(v}|/<uB&.S\·6 n?oW!h$;AQR R[ ՃyQ- "ӶB xՕv9R(9{X{9t!b@oZ1mڎ95<`XBy:C4'\ʻ 'kZ_u+"* (:RiR""ehvub@#fk?\3yY4NHOꭶe9lpWrz К](>[A Al>=_Fb& 0]kbwi;t巏  yl8=a'ȖP],me z$;oc I hãf,AOSX*,1@n%7 o662aXWB9[P,%=oy= ~XU1m}u.]xP'(]]rD8 % y"sDMJ`w$BT 2-Ȫ.`l41/:e׉c;)Xl*dlUb+֣H\u{8}KB\`™9#Bz{ßہ?}p66k8kя .}c߆Ub6P+LQ3q@8 T@LЖvՑ@H b<5Al̡(97&P5we{7!\p}$#+*pEUPl""rb aVy=LAA Z x@}ڕ R a1ZbŜ'@PxIow >6\}d7vb*(Kj&V4ȍ^%xL˧Z펾]PrutkΎE,^4_BUnL ]A @`M)v&\"W!9t< xV -b78nj7 y`(W\,/ S u"<1/G_h,ivӔ)VaT1Idɽj] <ŗL)kׅwv.")ZZ8}ë&Hf<\Vji>t3.U`}ڢgQs1U{( 졠/9:'ecۤmgI׫(cZiF*9&ǒ.#)zc5U2B`gsXm-1ĕ=UP=cQUTT%b_:O< ˗0fM 6ttyL(8IqV'Xַsb*_n+ "|=[xtey>2:\>["xV!|&y  o\Vzyf,+}+E\!uf1?$͙u52yA&rƃ*ͮ,|` kf~+xbP4a׀IR렪Py4oIUudes%@ѕy>]z DІ6 [r-tEtȑCO=YHF+#6ixk_Y\H)7MӴh͆7c@(vg^*CG,*4TW[K?ړ*Br'H(M;AZ2{vaV(|6=\j]x=t#0X f[oC~eqCо'~'> ++F,s0jg%R^+)98O_༜Rh Y/tC"KDTN,8wEciȂ [ЪGϢ@oɾIɘ3K3*yTvsLU".`og~pQ_}Å:>!A( Ya%x4;ЮZS )'♄*<b.ܣl5]Wi,.%b*@sBTlC@ !g(H]4j7^kK:g/gd2 LFgU!d%#$T:{=TҦdXCXd."U!ҭc fj"t\+MY aȤD2&|MYP`?=hL\C.\Xc wݾ9ܾjEO2*ƞml,d/RɣZ;ց(mH- BǜTU!]cidj{MgmxAcS#+RO;4ne nsCW4"qe>МǦt b,c$cBy:EyK~'h魵r6/t?弃n1h%TqQf!۳q.l2pmDàZ((w+N@oUcY͊pvǿN:Kr&pՑ*b,DR[6yl";W6ҼtuUc>s>nqmVIھ%B'g*XeA*EY9Vh&Od\llsS(l:(.Qy?BxSr2l2 >Q_( )+f`-f_ ixde-=j<B\3 )Me^)TsВ4tKx7% m z6 mh߲muum>)bنH+f&Et:mA TUܼV+$J( Y!d5bQ[D\ҧ5xY7;D:HQnH:y4G9^8~Μ9sswygFІ6oV뮻~o~ӛ>ކ` NuM[`D D ¹<%3x 셺* 8*kCp?ZUbȲKrh8"P*O1F3@dOBC+›!5ǣН56+#'t U+s`ku=;(FVd&ilTEx{=9UZwkKu%r$rpԧ *sƐ+晐!0>"9t79 _z4lgpcۮQ ՑRH,]u'Ov4P6F~ FB\Ye;ȝЬIVUE"wx|!!Bles틝xPWҝU/Yτ^K2] [1 UMnkxa =]_~ Nn8,[,"9C+g@} k 6T-ԜQb#_]9tL)`= ʕu <3?.[5䏪rBy80Ǘ~wlk=m76ܘyXOs-vӗOUhVe4yer`QuoT V>zq$ c:) bLg7mG,/@N-,cq6n)]q .Sb"#% eBJtw']Wr(qi~؟;䗶~pϙph܈[X<;N9CEQ5U'Ә|>gI?{*gwëm_ȇ? oCeԀ-v"mc@*֚t_N-< mr᩷%/YH΄0$sF5T2~|#D:Оm9<mYIE߁ە~хd;{%<*Puz6HU=ډ ghC7o?=rkehUKuuP5 ^Gnpy $wA= fi㜝Ȫ{k*/Z{hxmwL P3:@Q GU>te9p0MNkn/ӝV 5g [[V(O`^S1+TuF{H -@,PdU-_024ΐz!gUSP3X%PV>`l4)WB!0 >3p;cprk5gv> uG R}-QJ?v]㺒b YRɢc\*PNI'dhPk~AvMhU,b+$+p+&pyLR Ux!HbԾ{[P9 \oJ vpast047UGP%i9$ fݸHE:9IDa!A8|9-XQ*KouqyiҾ%| n eR8 گxRU^i.*i' KsZpߪD)]"酀?u6f9GUJ[eP9:,dZb 蝙+ [ 9:^MF .MGvxFX+i>./tpd8" ԉ84(2y1nD zV^0j6+'_<Օ;WtyGIQ,YVEkD]!ui 讠 (f׉'BQ^l>Dȿc%l5$/P'./J$}DѪز!s*w"s7*!ض-0*e hJll63gtf,Z$YQICR9CEBX ޫb*(vDT@U+!b3=L]1GVn"zpQ=xQβ+n>mzB}yyf} X躴]Zeຕpu\dΣJ[1 al$Y`x|xgD?Y;C?Y+1>uy祀Ӫx"C{Glẅ́&l+P)zmahwg5<{aw6!}hQ9' *Łz"D (+y|V&ID9nŬ ~Vj˦G0HRUl o!zkȥ5do,yWrVX_*K\%>tXAަ, C)D(j*}rZ9Z|9C< U;ay{ s!X+";< ѠL3̨eYQU'_:<3 W߾;s>ج̟7S{p vt6n^vEZrdRQj1 MI*SEd  f#Bk͚#joUWHYsEQ~_|ͮ쫹 ɲJˍcyG!`0ccuX0n"lr"pfeWϥ\RBzb#Vޮݧ^{?pg0І6}˷յ~mml6I%&N󕕕kT IAU,U$\zF԰ 6m$w(GEc!zJۼE!o(oH08HR!G,w0U4urŨV{%:UԺL+܈ Vgs:u7Yͱ:y]*wܨv`VPޞo7^5'݃Bb~0 AdU$jRx| q9\qG/ t'%oHi&[](c[1̞o" }o I*+fgaR :8H:PK&~ ^}Ȍsxc&́C=nXh%gQ ٔ Lh|!۫lĪݡ$OJ@@D$gjDB@} h eܗUA/[m|xESw̹ꕊl*d XK3Û_mnK'r dok"u S@Td%c cIA?;ݩ <Qt!^P XFn2hlb~&ڱ)co(ɤp%<|f 'Vp5 <8A,D~]$˧iF1߲ г.fS5N-]3o1MPhLšPE!M9#)$磢˱YAUСw*^m$•Xgf쨠b $,(# x~.D=9=LQc 3-UfXrtb9]Pqر=, =5˔\RXZ.1%2N,wEdB(B(JH&gmC|vBh99:L+J.g$W!h^8xj!grR*OhA:-pY \څ[^_һpG0\`hCІWվSN$jyVgT,iF%=KOiTPi7 눤HzgD!$\,Iz]i Z¥G鱋QwԐlkRaN/i%=uNd[Z+^಺==~k2 |/ӧ#lhC_uoy[~__&xP壌9V~e,.v6"x ~ft2"PI~Q{PnK4x Ex5J{(UQ($Ł+⑈ 8G!ġ6^ lϕI(͜q@1lcZ>Um˄ ٔ^g YQ"Slmr%9Y3UJzV5`T2 8 GApG`r.|z behhEu$UĘ31]˜[^UL?Gmܑ%8 4V/*Q!Aw\UE(P9J;%@Tu9BBG=VH^:O|| {hv  EJj0LvA%B֠Ծ-~*8F,ZQq==PACJjʝt+M6n|G\T3p@yo .J@M[Qz|+BU 0َȯN\sb>v@J1*\Ѫ7A?F_IUj)Dй(L D|VkS+<8pF՜T{,v>NOfԹ*\$:09 |R;%|cF5։&%ŚGC@s%lnz ~IrԍGhak A XRDg#'P>!T@ (b\W>I1R(L>۹5ڄ97d+$cr掂Pb%d_]VhXOg 3V37{sxNϬáFmR2X_cpFkVߘPي+\s`p8jA-TSZHAgj&pqPv}".JE'JJrRwx!8WpFh!vM4Ξ}9ͭGm&O+:+OI%}pS.W!lqqS;`줐tl^11bW|QSE!lOPBE2"U9``CGڅ[LH;93Ȍr%'$}eC߷YRQrSY./^twq-y 9 +.]QD&V36Bϯ%[&bۂW¢Ztmo.Ձgys2^t ??LqNc[X)|+{p]%0))(Dݒ*}xԳG),J7IĞ Fc!o,+ c%ĒƼdUO4J*=ed]\/cu\[>csl~P4`3yrbV,c"R'g/.5Ml"J"XB\dXR52Vv;?߾;wݻÝghCІ641mEӎdjqUU{5M+7RhݦdN vWlـmۂX!1vF4<4^f_fhYLmUyB!o3I鱎B<"7H!$dSN! Q`A(evwws>7O>tпFІ6K?3?3Wߛd۴4y֐+ 7u "XBG QI89R0GTPzZxe\ѿg @{\vy\ˑh3 {E>He R:NmŹHsr[M!-B!&RKyERJv+"0Akju^Hv%D gV@H't>w)@XE%`m83erؚMc R/+lZ%8&."0Tګ2ci5xŃO0򑾗.JnJ`l|)&+mBLE4 -Pr28IO8Q+oޣK` /Qx ҾtΌi(U]a~&pO^llK9j<߃tMw.Φ pYDq T" h l:HjZU!e`Kh! `B]S]l*>X d1X "%U)XIF" '\cǷz9hdw#WwY  \SH:UDwY͠cmLŝ<*JfFU(XEsV%9-9kE FqV<4B vU)J-+ ƭOjkAm ttnDKĭ&ԞI@,t :WHW޹Qn =z* R׊KdP&KG,>t&O*z@#gv[^q<hن@*"TL6n?^΅ɍ:%sgaW @rl'@#(Df d!,QP*KqY1 wYFJvYB`n԰p:/>x9/ tD2h{.7h -` 43$%Lyި> A}n,Jp[况  gۅRC+il{U? f!dkJFGdfp%%ƙhY!^~ţ9tBХp$LHǥ .ȩ FɟlYd4_pFCq9_E ڧ #ףYiJuACҙVEɏNܬbZVͤ*2d"22dL[=eJ8!r Ib禘"t'1dIdeȪ@Xy%Кa1 _x%59s֚V>er&2&ey L~d(.U&#<:W?t^-x|l]?K1.H)ve^a^&,i\+C+b/]64wAQ23X=zM-:jqAP$lBYAfrj+ R2K˱((b9 ,!$X\V Z/Q30M?h3 mhC7hj߶ud'xU$_ZYYFjzlk֎,lVЏ^`[G,. -pa|MdؾP/sGٓJA&}fEH"iKHuoW,ͽ ")$t]*9%ɿ:$H$td2]c7p06uw;ΞC/:YSX<nP{731K\,2Pv$aF-g| T DS:%̺lg>gahG`3|*`AoQ!0CsVm7c<߱=HDS>pe͖!eGu^ZW ^/"۩i3VJ#J%-s\YMi~jWշ&X5P@:l|02f,E??hx/LȘon;/BW`:`kh^@dڹXɾQRGrJ,ZƸ!X\5%ȩJ\cEc]9e{!({3۴\%QH{uw _ރE /HB V7ހCk[PG ֹ Qd^м,(y; {TR³)E"m S*Sm0@fd1Q2#%/@Vekǵ*ؼ/J%K%}!Xh 4/tcoW[8qU|jiR\`m%Q7'sEuXQ+dVKJOrR t"?c)%B>9&jb9GCUX@dwVR"[9JQ@ LB5!ݓtN5!U InWYye`:VC g&\!bCA`edўuh z|x241+N O|}J(nBsڳ~&OE2gvV۽.Ja 3p<3oƿsyghCІ66]YOJek4=i6HɠVn:ɗmri(mNЂm!mSQx!ST/DZUig BI\@jث!Bo5.]-d >J5fI:'YEgϞǏÇ #jhCGGyWbG5@%(] O VT@"`mZ 6ʤ"Eq)7 +$$88+L 2@`^,"],ި@> b+WׅG'piCJr\1VQQٲȪv JU9Ŧ-WF/AMT QSi7g&q6QEؽ/rUxFEHLT8c`%C-_"['=j3gl8d;,Hw9xzu+sx ($ 1oG2 x!{ .+fj"bBrj_BQG)PH2}MΠ F >'}tոtLe;50wu5^W9 wܶ 㘎!^ۃQlU(ϯr+aVhC [plH_BMD0YrL"N:ȍbmhƓxwdNY-Yǿ>,w6wO8 8c6"/@ 6@!Q@B XBb}%,v^sYUE}kU~` J$93O/߯?)b7SDFP cpifsq{$#xY@_%[Ri @9`Z sDMQl#04$)yAlXB(Ǔm,-c8Q/ 0=vo4L @4F;wMc|؋^u{m#UĪ(90#VZ0=acV pt[1.~;NE6)juѱ=*U[B 6C]J;1f>{0}'=˶'/N9ѯ/% qp- UXc*RƊ(Rr&1ML,7˙bG=V.I>RV &db#[!t[@8"T=U4 @˲sӴUbbm9)ǒA>p"K^8۱<˘&z^UZ߆׼==x?mlc_hW^ӿdooyv[mL&M[2ŌTLM`Ӈ@Rn8QlZ~ ssȃ7CtMOOK-6sY l%X- hI&=E`s&yYZG!A؃ tzh֖tt:أ6};G>ן{O߼ݜ;jH-.*8N'Ν]=tkaiR442Q:.Eױh6k+Q 8XV;h(L( j=Ue}p^ N,Օ?Rl lv KlV44~hT3o`MIfHmitƱjJ_efVČ홇7vn}7npr;fCl%T`Y#IBUaIDuj@ EQM#Aѩ$^\ %J5[#rJ1)o`VU\U z.ów򹵙5?}9XM`'o}<l?mt<uPEwUhiE,xWAPct}Eջj?EC-=:)ޢʇdd 2LԔzY7Ds*syw|Ξ#)w4&`DA,u#x_`wr:*zdaa. ;ODxVqX>T ~WyۣK:_x3jQ ׀T LWO ,GfC $]kٗmӦ{P{ S(8OnsV35nĎ@G߇xu˖T%Q)*_90橁Zb9X A~trEUh9,G&9=#>X~0ȓJ8s:(J>Wc@86N''9Fz>+@ wjyG| >zkxw?mlcl'O|Ȼgynt];L^BѶQ^@h#A:a5ڥ!!5NN姕|c+J&nK}$%` YIQi)'UDɺ}!]nll؛6}9ۻ?>S{{NFxRTp66& @50~{2_vY8^Xl0 jɟёb',&Q8o~X"#ξfڪ '8"_sdEB(ĂG.(-Uʓb҂rX- 0SbsF> SѮKP gZ1IrxC*sU.5 (vSb휫y?4TXMAOo.x!Y=B_=`e>hA< r]˕Y /ks_5"*别A'ZԦ/y>ml)@~dPd "ugSoځ?vsאR.ZU|]Eݠl6] t}Ud5K Y9)@ USc K12r#CZ-Mꂧ_2L*? >ȾV4ht,׭o<\8;'fp/;^7Úzp+JWg;)Y+)B_ZQK/h%)0^QU9l>Mb㤅p]Xo֭zrfUϥbjX8ryH$d 2EWU/Auq_%O B kcXƾ5V۞Û8 O\$'T`? 8;,m 8U+J*`&JWp2[a?ci҆+1k~B%-bxzg*@3^1.U9wrĩWƈJcvt?/`g1_FT hR Ss{= F3mlcMg67pPuɦi?.CM/1~ K c=amęmvs h壗$2K_[P}=~6گzACЊkں!Aۤ`\^~؛6}Ͻ+p6?O6.ևavT8lNx`0ɭfF @ OlAZPC*QH1&Wi7?w.2triI!`>a\Eܔu-,e08J-cٚW&6: [}G "-9_l\!?Ss1*PnftF>>t*;~$ 8z'C RSZtwYgǁ'Sl' O}>X]gP%C)BYS鰚]9 C}rs7pbsBg8H,Pܰ*)Jmp$6Q\zӨE-C ?<g {^y+Sz\H anzq'Q(ףm9UG(_މR% gŰ\bCLUט]dv,*]$Yi[{QJj2-ղbW+kx\W?Q90ga\vÒU?)E  8&IUVj{BO>BuvA@yP,Wh_>AZ30bN/\L bUZ?a3Wl٭=X1=Pt ZQ*! >z36 Ґ9TV:Ug5jJPV2I%2#Iz9蜨rqKʞ40N'(1P.d m7ʚMq[CW(T*|ﯞ"|@UxR_V'Tj/wak믽-~c4ŏm=c6/d2yt6_VӦiVbݦrVrpHvBM,$({NF(~Cy~(ZئM ?kV#0?ٖܠ}  {*w8qYKmÆ߄YA3{q?;wؓ6}W~=}Ï]"o,W \Ӊ#ϋ{8w*r[dֺ(r'۠ A`<)rq\S$9.Xu )j:J̔+Cߓ2fR"ۢXw Ui65sd%gGbmҋreJpkUFDsX|~_㤘%E[`h~AmqW$'iͶ*<9 bpy^a:=֏Jv_ hTu$ieQ? ۯp &(wz><8P1L#NjA4#;"xQ*iaL*yI.bf_f4Q{Rd*U,NI+gUqb!1<]C)K5Ta$>&;{Q<O,+Ku+P@+ Dtp DiiLba'$4Ω2CQrƁ`1p೟_>:-r.N :jg=8wb  gF_+KC+dX+IVMۑpwcu m[zۜW3y\uTTqܪNLXJ 5PqR5wܧ#t$YULۺbq1߼=նiвmI2=&IA*\Q85C;4 ;wfo'p T |mu`Rq=IOx2S.9m9s'mlcJjЇɟ CYyMma䆃#<,4qfC#]C]XΖhp}'ͪ{|EQ9$r#[1,<`4. &-33˵:L]M)bRXF-A͎l|e^u/S{rbvE ګt^xD*2*x9- -\u‹)kA,uZǶ-oŲ9+ zJ̈vX̦ͤU=-8Uh?UЉAFWu=I/pĄTh)֍+<,r DG< N9?&i~{_ 2J1X7~EouK}.͝j ɧ_1{;xt'fO؏Hq]qA;:Z#9Q[T)D> q0Qv5EJKA=LZLUرl Q- nN+}EmZ8?#BGIؔ5b7\8eC4g-_{k8yxfPeC@m=fβBŃd,Х PB\ [ #~BgI 8E?: QZa#2xih LS_^+OA60xzr5<ۊ>yy(Q,*Mcc*\ ]KvAՠEa>mZHgڸQkg UE%Yoky, A)e  e rX >c!YIQIaŤq6CXp9 a ss^ա 9R1#PB~\\-߲EmFzDž婢m-J&vN*g9?":8yW&d'ePgjhVNO1 tC*`Ď}KNqZ_TDje`$T8ZO R;*i6ajswڵy9X o= {|qPR1GLHIGy飥m9' FJKB(ץKQd6lGJA;RPVYhp "~pjPߊu`L(uciJ~JbCR \MZ@Cl// x`w& |}23Un`2JC\=LP6b@ @#>K7.|GQ9 / DO#㺃?FTlxdCIJŒkW]r{/3` v0gYB3 y=[x88 _ju@h rjB઀ s[A4+ GWٱ;Jgd&i3;7! ֜OѮdj*$M& ʸaL**@zXS"Se!YAQiU[<-{XQ36)J 6KIFU! pܜݛؠ%H=ݍa xXyQ* Aih!DV~C $.msϮqlanDp\ EKVT |<7!+!٘{ hEI.JԒ q8+0Y+q 䉭)y&4D\M G,Tڀ960߯Xl䋪jPesx5`>"Gop׹R%#JrL!Z N *OgHEb&ڢ5ƺM5F5% )7Kk|M&dK^ /=x;pvS#J|h8#XYRG+5O'$Al4<QUpIT_f ab_UѡGl ʴ_EE<'ŲISʼxIF!‹@w&,$jc' .+Wpj|k؝yy'ݚf^jVm5U3*Ĥj7Z!dCS625ۃ+ ˊB%NJCE SZ=jh,I}+K^ uw˲T :RF]W<0Q" >܊:gfլ{tPFu\ImP S2*;N|$ߊ2x|}k*SHn!l0t;0 m!?ҀfTܕu+GhF>|vA jI7P(9IW91j $pHjׯr!.>zץ-"B0|"3@_X9 WoSP|Mc[85g;>$}W=G3O^po~'~q>6K~w{H ٱ^υh}=H&{j'#O8C2Iuqy9-F$oċ,:!o@,ݐ GT>QߨO> 7Q dwq=-=\*vpѪrj!~ʜ<ŋo-oc/~0VO' nڈ!]ߎ\-:T8[c'TǷB6T=3 3BL Apl*"WB>UVϪbdxR=0ttȞܳK8X:t:>HV8,R"DJD oOTKNڂW;gP*|c LMjն}H¥z3yF`u|1d>\Q M 2VLr-ʖb˥ٔ)d;FH۞Z؝{8Zp<7!B[䜸|&Ȣ$9jiO1拪C '&SgU­n^6 ǰsN'=*o᪇X; i833o1-(jXyU2֘%bƨ\,Nj֑դ͔[]KWw]5!./] |8yɫǪQ^td{@뱐:D#ܡ<C%InLe_}]/;x RRNU `*VjE1IXyWLh{")EW+U.8T '?n澲 ߹G-dj%yŘ. B>Q[X?3 v,Q>+tiJx YkJ*PQ9W,as'.˷P}S^;#fCWn1c &LNG%lurB Pb"1& $HF[/X_ a;Tی*$*W<&? tN|qUYX Խ|i :&7 nRvjA !TUå.QƅiK-,fs4$ٷg )yr0xyܾ=jm$z6mlcֿ7?l̑f <徃žB YȟJ HLrpqh6Ak˖d:nZ#vmKeuC:5Bؠo'>=4uɡ(}.U*!}0VuZs.glcBɟu?kk&l&W\ҜWQI 5}`k$gZ!N2~S+R`A= :R Yco5lzY .n([A,8Jr#J{8~A8JVՌ{$ +p_H#@6)Oh܈ A<2Zd%CbT.PɅHRM_T:k1v/Z1qtP*Z!NowpL y~=#&=elŵqXELWp]"BLhjʇ1you[d ˂kҶLyV߼p#wW_^[`XL&yف@BƷ8^o`NwbkR9Gb7$ے1vc՘T ! a ;UHfI%WIrb L&KZQkG9&_c, =.qw%OZiQ26DU&@c Ke=T K`q:BFURo<*/ӻqZts~K7ɓRQ[!hKҐz< f$ӟHB|o(7ɷa8 QDʊq=I;(.̤ʹTrQy+Vs5!*&[Pz;;苲T-UP,Dߙ^ՊUh2 €|{oհJ1UX`dӐTJ0dv{Ӂ% otҎ[&kZU.7 盼0vtHCLncYTsldȹL;Vk%{D5D'8B߼kmόD6ml_vjwmBw|1Lv 3mtԞ-5R * R 6kPr~nN~r3}!|Hc%l4йrËvnHkH6)h GpS2 MKDlm§3kȏȵO?yD\l&d`x."Ş$SoTP ݍ @F6Q|#B¤/XZc_M]qE͑4J7Zl<|CRo'( f0%j&z 㕐 F,<ݷ/X k=] \ю1œ xV Pǝ7xxPK4t=żE0D1Z_ĆGC ;&X8X=s lm!vwI2`зHnpO5ř(´]? L)&G:vXqb@m}!>e'yϏk8̧-Ptn[x$cns[`VLtسw=-j2GP[>%ǟ%RЭ5rQ\Uiރ%{p_8U' % 8HBߩm#{,N=iŹJ %VGU89~b)y1jaD7egsSxӷ$7!nKxs8fӳe)τT?/V@gS0gY!DlDj7!U3 v +A i3D%'}XB8!=u/Ȣ-2pZJ1I߃X|bKKBMח'8TZOd1/¹E6U aJg;9c) o@֘*c 77zB[|% @ks2#HK+7Fglc6/qOSX.WNVmא(iVRClDGNN44$Pi`~jzI !wD7҉LV 7ڰavF:_CR  Fh6H q->I "s~hنpHY:?bmlcZk?s?~7^7?|\i/pa }a(ij"GTn#DԐ5n^duDἘq$ XX"BvHR8Gg3\p0bڒ"A+qSp3+dV Nl%AGm$M|0$-A2|SsJFOIQmD࿗jt]g WCi96`Z~16ʂAaۺ=)[4C >ԚN8villA"b.D{@Y&6 #`SUCdrz;Z#^)oK^<7I۳Z㨀X"f7<0^w|.uХE?vua :tl(dH墪$Σ zO?1zGq^smZ C`wV|QEǐJI! H]چ^(fPu2(\l8q- Oj(v8}2\rL-KBT]_QYK)URrM%c塨vȒM =%lޙ()'|TՔj &C̾cKBE4sJh;Q++y(cSW[/*bUPqcUes j׋>:Osv:, P"Yrٔ8$L҇O-|м3̈́!u+%IB8 FT3z+Xk r_ضs*w>V8چ^k&**KB1s@(ZUjh]Y!cE%O*Kz$#YUEExlJeVu b|jw\ZּE(5t+0vڿb-9s.ŕsY,s. n iJfl"~ޠq<(@_ N! 1"'8%$)׽+8#H02LClXf b-PwYyMz5HJNjD j' qQ,$W-t-񒓒D)N[3`WCO^M&Gn?8oE8ʯMPUߟwH = D)y879L=lIg^j@Śͧ#Wyk<-D|ZՁJhmP9)f| jc>DglBPо((a$F@ocOR(v(QW8RYUdR! (/c E×^GV)D„RY=T @f]BUM^;yם"iݽ^ՠsiю-Ī,pj~R3TT's'Tn[}Old;rQFz)j6  yIUe}}L0/Qe&{G&C%1rQJ= f{c7EmˉFoY=Ħ1Zl6ii\BoOZ$~mwT 9JA/\_wnNG{ߧX]^/t]BM~D;E|~({DC ڸI wmB @4\$XBh ݺa 0:IzQ-}$L H̅dtbܶB5B[u}{[mlcZn??~3?oq|S 0^LA2'rұ8$^Dŝ؏x Ȼc%kY' zG-T=H6ۼA4HV_0=k6pa)U{"oW;j`+$yK;j ɓ˗""39#m#/d6JZ~)s{ 854|c:LPNh}8%VhH۪_\[J3c7x"=UP]OxiyQ9yP' yƽñ Jv*1U >TWjw%r5eԦ8WVl"ФhL w?V ݇S_x)VpfؖՆ. K^ ƱT>!!4pm|*ئ-jeľJ]jwX\L;PPB塎*oqwUE╣V9TH@Jy*.B%Sŧ l8cbaz 3lGtH  hqP=c6ƪ{RM߆fKPHUrKcXgEJ50nHDQrg eyܚMiih6x BEr蠪`:/9wXK.Bvhy)\} }aRJl ӗ j`'Y3|lB  KU[waTģ֘ЯwՎh._>V53^Ji` -0:0&5%Kd?~Z#T6DTHP}L%/YFglc6/SzomObd29L$vu2p QdQ#H '$h+;HyXChۅ|sAI3.\p8ml_/~>~?{߼9 M0<=l~/@;?'Hh+/\s54V-TVyteZ [x[}Nm;|}>©-G`j5 @(`լWpǕǂ˩URhTO|,X-A{$#xPsT;p [6M%\poDCx/0HqJ9)TϓVGU*$%_K3kµ# 9`' 6q XbȦ/9r`1a u<ޛ F>V*/^N73oY#N8?V/AtWy] 7p@$7bH\p-tg0`ܷ&'FVs7`PݐLsA!0Ô!vR gQt rV'r\qq, BpP nfy຦NU#pԸh|PƜoYddvTcCP;FW-9U%p3V~u s.wNts:/DxVӗZ"X$YYX,⢲kP} zlI3f#J(jE.QuSox`?L|mPבosuKޚNF]04Xu}}rBV3)'y&IH^(-C*JX-pZR~_ZGxcgiW|]UQX%]2*mxȵzWI#l֏]x:A 2WIEK ;0K4><@kvX>@lܧ6^@*PARӀ$%[749ꢘ0̙I $>)nxI\ 9˹ٹs.flc[{;?ٟ?oF{+6$$kVd Bpq'̡"oI%T%8%9;f:WsnXvJ$:xxK{ȸ}=ZsY+ à9H9M(Yl=Ԛs]/ W p0/U-$"K/r() )\.UX[X1akF|OQZLP\~gWOϮa-l &qy=l-nEJJXqE *Q5quu@g 7{z3WU__Kg"qF`" 1 ~f%7 Ok*=\: Sٞ?;3TeD޹mtCK8V~9=ў-Lq!SqSL%t,c?Mfa+JUe6Rjy%M.JB"#j+yk^ jjΎh( $ ZJ(ptjJHi^LM^e^Xd+ڟ+KfYXۆWy^ucAMlHyX Bzfy#hU0V`Py76L=-Xo.GJD@!;J`_%Fq<ؤ-Ւ҉HO }̓*k_s*V B'${EUTXh{N(,RRKu>b;*nӦH@B pC(vskb{$UCB!`N _09 R9K}tD=>-y13gko $W5ųۀE<5WܟЦ֌ôٞlqo꫓.*6™$>1 =\p嬇;MH-DE  76GEpfX]HJm\K"Z*q9?Ac*T$st" YWC EXBA!.b|HkQD)闂̤DVQI`%z/rHs T2 L%'.NEKXR=35GI`ؐlk;7F3;) *0TFsX굿^kL(V:{WɞvwptWt:6 J:ē˨#_-YoSm#>\ã%-yuɱ/m$z6mlcy_#Hvpps\.KIf2}hs$ f dDl0g?d9SfB ?=>~UꞐϑou?]{s(Nj8\w$<0>l$'(1XE7˗o,flc%sfۧSkm_Au;qvo{Ix9 (QZ<8T(2MI(”ā x-|8ppnI.U%Zȿ˿g_$ Zh!EZaM3%P,8 3PJkJtu#.jő;QxǺ3HO*}@ɘ*y]fq'9<0ktJvG*V=|ks8_E dw]Az'k.#\1$%oimm7p)<c Oj~;s@v$ܸdTR}匇;./b,B炨=THɗ t5Gv (>9qڼ(QI6k rEU,4Q0<07kBj"f4pq<)p!jSUiy[d*ooQIB@Q^HȖJ>GٰAlh+1,R23R" N1{;ϢQRw:gŗY&si+_Hec+\HJ!k>QcJs_IRUcrId7G=ȫƶ~B.9%qD)(kwfZ)a8KV@;JrTuX ^SI o_s5GF>?T9{kHn`jJ!f/^Uv>BZr-TuU/m|6Aԧzl'$U2u|qD(09cJ<bAbD##|ӫgמ˗攝 4xoUch;&2<0ΠjIjn@<4Dl f=isxcēkp{.N`/Au뇉Rwvwxn8Ț:bz"P6@g Z>$|N c_0#O=z80s8){FQN$T3. McJ2% +uUXCVՆ`CU [rHhM*ՖX7㣈 b"dwwoå/|v\;`Nc DYyQ+FCņ >U%.#9jO*V)AccU LB 6*CC-(-ǽt#"vZȎkQ%Bj!UBŲ N!COcumE߼Md. T 6o>q@xYUŪ -4gjhf+aJSO4/::0Wn j;szf M>IlM! ~.'3iMU }|@]c/;`+S5#rK/dlL{ݖQyQ\j֜'n%x؞s6`M5=CH?'[4%i1 Aj8EeW9k^8Hmlch/HbN*k-$lڶmb|щ!$K&2L").h͖Gd*!`uO#pg[Q3p\29$y9,$&qRADζVSg8gLAY_|lsZԞ={c=3^nク^vZĻx"<`Z3n @+Sɻ?U剂T4'"eU oUbL )`kR%u:qP jßeyWqj[_ p{ pfKq]Pu &;NHRRe6Ms8!bAAx59qOU%8~z3fw Uk|~=6E RG2 %8tBV݂z|՞0zQ[IfRd2j7SHsH pն* JҶjFuƠgjj}2L`H~ı%SI-g,kPҰ1> U*9VV3|O>8es#˧8lHUŅ$%D= ޕMkV{վiHhTe\JOj$ټxJpڹ`pRLvǀSوA3j@ `3+*,Teq':f:*ߠb]lЪevedYf8D逇PA_TmZmHhA(EݤV>g1.!l򘍷xfuLB&ckXQپ媽cJcڍJGȜ>%:hGW^Ss[Uc 0JQAwbJXKsZϖ/pkaڹg-a@ٹtRmZYTd48Q3" ]my6_x׻tUqmlcؾRm:>:h z^Q Ycńp@Qn/ @7P% $n{M\ ܟX%wH$uf mNnV=%~وMJf "3Ay>2^?~,u~x}5+}6TqTjECX` 7 *. $ZvVX4 YKHdD`=y|X$)LuP v)<<y)rFܝ)^r9L;(`bo"/j&h`֥kX*m)K@(#W~HZI&Y[1A( 4lL5'‚zÇ?w~vp vp=? {[;l`aw澷Ʉ&mSDlҒ= ?0Stf-95o󣇝xz{v&}f{ߴ ol+ωL _G%L``NCqq9>r$z(RL]-&U@tΨ}j I,u(TUo$@,Bpޜ?#R, v HAaF8W$BDŽ qeXPmWQ2Je~}C 4]s8RJ~'㛨]Q␖QޞFx)[ɑɝɛ<5@YYNo4vNɻJqc)[*U"e= `/9U )BBg:8:wiNQD=VemJWokҲ=@bJ',̣ZEW! b+%=n`関گA#ue{7ouNQ- ТHw\<}~eQmlglclgwm=gVzXl mAՍ>I+6MMwb*3_&QC|4D aN^FyTC D6oB2E;8R2zA5 B8I¡*yPad;Ij&Os[$mLۘ9/ko<ş>Z9 *x`:a$Om>N UwBDY"_=[SP;06?t4LiwE5Tc Zg%n5pv|*'TbU*x\`i@ 9Q_p05MפB 6ҍ3 5ހa%$zT,kPA'RQ3[]*])tbiEpX!!_n C|G+TVv> o}9pYYj7q_,#ǫ)S U١,T &V{lZFp#|\o ,f-\ǟ 03«Y>Q[-8/}mK䗅  dO4o)!]rHDaU2TfWG3˸S[]Y\DX-* AfJQ>% zkēKܕxÃD:hPL@f@6q&I\!WbrpDw/:V\%m֌[ 5^|&/.:Bk:"zzt5^^7u,TjS!Ve)Sρ3HVuS2%dxw3Y5KS\D˜ɜi/;O߲y#Jdt5U{r~GM?T-:^$J9:鱍m$z6ml_~}{ߧ 8y;<<ܛN/$J'oԐ˵$ )H osC-}JTGn=ڬbg&$ .k# rADHbƏG iJĞ͋feKY$Qe$JD 9DDvZ,H8`|ejS|x21X?ix|HU*Ka"lg FkD7G^ VS zY24<&MK,[(X=tϿ `X'=y6 GYml* !( A(% s"( a —#0Wj8: hy*dM 7h#O>r`2>|}ŵp}mgNawfݑ4Sʳݔy!'`lHF_( 5mľMT`mt>8Ǿhۚ98wjk# )B.BD:=K`/9]BQɨEbp펲=,eyYq@´*j} ^>[pfk'Թܗ^sNo} m^ޥb}J!JKdQfhj&XyL'0rE0&(vP `( 6$\Qͤ AsPEYF0뭙c֤bƹXu=6XyzW;waL"%M≾:0XI4,d%x o’QzSNpL6{ W:;cl@lS7Qܽ& V{74k?*.~'&塢>?6~)* ퟪR*Sd `'Lr&(4]eX 1-)o&o<Nw<'ZcIɺ:1"JPǺX 4?.&"+$+2$ǖHoq&=D6loOF?c$^f!}$:pM␞Ax:y=ЦMfBlD2f)y?!b& ~W$Pͳ7TH~SM LR?[&wBZSMdIpX#90ߌd`dTALV:V X+&FC*XH^* b!ф@;Y8x)|cxJ ]#NlޕB(*ם++ɀI\S7R% {g4MU{VkF ),ԶKhF0`|f^狕(.o(?_Mi .Pg<|Cڅi m> fqeXyU@!0l' $O prc{خj6užiʺ .Ç?cH>a=A@@@ReMrtNe9圯ZqG]!XPL,2!@'ۥ;~qKט ,sxwN-¥L@PO&J_z(/cRys#I5" ̀7\_U{_vT1y(UlMнL8Pә@īUTӲ5pTM (Oi; \ T R%\{+/HlQm^~ |Pb,}JnUb`& &ɀO9mMM%HԺsŒ!/)s8̵UbĒ2V4$R,w'#7 k.Jj !=F^, ӉAE3-ePAI}.ߞ dlNX ^aGVRQIEäUTNG xEUv茅f%ɜ\SI3ق@,|y^7f7#-⌾fYHWmwW=DCZ4|+1R5aJ&TAl &䗀LuODeMJ-\ S] PpEɩI~<£m+VbS+RΗ x[ _\dzd V ƕd» ã}4%UP:AZ<}5G?k"Vy9blü۷^nO"4/)zD ֞*s%jD"RIiאu ^O;t x7ҕ35L͚оoσBBPgX,ϪM_ɫр! QĎ,B\Pu|\ drUu_.[BmTC*',W+% 'd6p܌M%QVG *y(cR,*`R\l󤷃6Z]xt2o .=|#8\8 ^+c.QxEƪ"HEQ"wqFIQ킬I,TX,+K| pW;TrU {fz.THYՇi6d,d᭟I겖eSVvO:H#瘃Xno%;cjɛKLsUBr5/-걮<^HI`QJdf8l6pR4RN 扽@UBV[RBau0 b3Vh)y?,6}!HNM{"NO2YIE 6L%6IyQ/M9w۾i'<Ł0 z̓ڳUŽ e=4b6?5Hv??8FglcU^Wl:%;T(G;uDrR!wAOTHLmA($Jlv)pZ=کD3G+N#U! Gs!t9IH$UF>ܮ^Hp: m o| A鱍ml_yrhlk׮n]#e& @89"ǏXT*NվRa_!ٜO I x 9#k ?za` X쮇oՔA PP hjeqjߐZe_jBN4@Pdj&v3X: dV-tP_N򔷃s6G.6{ko9'8g` M|Cw!wLQ; DҔ-֜M0Tg|Q9!qӊ- >bKU @!cߞ<7w&Z7>|;/ -o? X:_|j Oa>k`!)Kde^8>by:Nk! Đ%;rJ**gNP?*FQ08E!T,0,O<՜c2Jjw N&(7JHT Sb'Q'(YAJ8I#ljr+1ļbW2/+;ן۸o lb6Mbe$i /v c 2WȐBb2kPsŢD0U9IPZ2sr@]gHggv9lZ0 8(:6%9\lcm;n|# Ct(K0MI>628QTu0H҈>ě%C 6yH$- FP=?)'[N<:4&@ q  nCU#(̈M;*[金 ܠw3%V(`%?K1T1Q 6|)g,AvL$Ծ ɝN 9{qm[_s3 lؼ _GlvqDGJf {엽}@H\}q=D6m&6^Hr#C{$\s|@KВm.D 3x"GU;Tʙ['M"d(GZA4I-Q,a!}ZQ)MD Tك=/uM -wi٧ͳV!DQUh$kObZJ5xQTAd]( MRNE॒lp^wV^뀃2- "dz F ņ&ZP4mOWZ*4HGEg7|ӷ[k7* s4ZSI\K6IJ)x!sI3\@1zҽ )TFKfkN&t(|Tmlu7>Sf|n iۋMaI! 5^3gt!<~lVbJ`Ś|U@Q*A*/ӕvUB~W IJpl#XqR bƢ߲*"" m~Ϫ3E KM9Q9}>m^p'\zDKKdGJPmu;P<, C2[ `2* JVZS,W`pE@SA4!6RnedU-ʼ?N Fթʞ6?OހwApϝdULV4{=*! '`L>PY3JrkƖ%5T!(b!\Qہ^oڶa\ÙmgfZE{` 5{X򰸄yE}X'4W4 qqHqeIe J.O ˡC ǽ3 x&UCnBr!XQ%CNX_ڧp[|AO7hٶGsʞX-q=D6N?v-!ޟc8V;d&&ms:Gl|#Q$^8/ *&B*hU~ˏ8\Klˆu%y_ ^ȠXQz2뺭d2mlD7 sg9_A&%;$*an`k ?t B Uf aX W5[ӹcuD8IQ{Z0 NOQъghiVTp8ÊHdޕdVK+$dz!lwCXuMUa\F4=pLV"HmT8qQv [T PBzmbH@x<7,m_ SkxDNN%2~-&+{N0oXZrT}bۧ?%B;Y0{R ,xo.ز*8D%,ZLtMdۅ$MԹ%!q#9cupmKBsEhi0€Ta23(RzdtBd]Xd"+m*BW,}'LGvM$-(wCh'erQ̪+,⇿K]fBu'=CybU%W-!s{sxmRҵHYiCJx EEY)Gd wYȟ$TgnD~ʯgFglcUئn2cM# 5!XLs 7]+Q~#n^J_"0ɖi4I|#= |#QL ;H؈U~"I(}ɺyD28QC6 o{0ږ- g)'F_V0 ju< 3 PfCK\X-T >3ji z)im{Vc2!۩]vc cN%m&xbW`f2*| 8ά \˫X0ko4<{9w0Fv%ˮUN.Wh)URRRh9XUgRɏˮȲ#Njl&)RD @ v`f03vߥsi|sҧ9|됶`Rzxgy^ K h!xqs̾&?nhu#9gk[`T ac,ܺlCg2iZì7ݴA~?SRXC [; sʱGAjDM&H3 fgG&JS$8Ө6fр|?丣&#Lf7XD\m675ۂ̆w7A$(O eIg㱊[!}?l%x_}I1+V,n:ђ@uX&aX>.9UHCLnD܀q.0Ui)e!yg(|gkQ58ϵAѨg9qmS΅J{YS}Γ ur5X2{#zGI )BHö(]L A2f`ɝȊÙۻEVL )$ ~mB<#:dŶ7Lk!:{l *6tH0yɤkvwl!cXM[fooBajom`=^-.j2M$"XZŔvC$g06 A ֽ'p8NÊzx 2#M:ب;2'UTR! [Dz dQ-8kFP6l(u`O4sTW=*A ڿO-Czޗ<+5Q9)kͦ ml,2T5՛PC+Q">)K_5'Hl[9xؐPb(j'03I[; ue]9y#_*oP[20RrHMer5XRq5r]^^mlۺ\|px`D=L` _{>³/8*q^-T#Ty?&ŀ=%dw%'%@UXU8bg6~uq@tX;m(8sl(}f5Kl߄028k;+w.zp\lBg8A NVRE Œ.qHGfo >b`mAM} /Fdɨxkxx9gmkWĩy p3xr嚓R|k UzzPҀJT~BŬ=R 3oUXT5דN(ٝƒϔiHZ֑nH%[`Σ3mx=Vm2{-QV~ _E&:r?*U>g>󙯌cHe,cXnҒPfiyX=>>.]4|siU=^HκilA$ IQ{B9CU gv&~$rO@mH$ -vQ_SY[rH!Y1G&$|d2q B-d,cˏ4N>H}YPt@l$vol7}Sɀ#[ fWFI ~Gc$$NmS ?!) Gcm7Jf&M@`Hw`{ }e4 qF i+PPCI,ɜヨWk߲d6,=|g©YX7r6AQJjeC7$R:!jl-X%Db} vmY=_[SS|rE>>| wo/.iRuE@]Fxh3-w[Z%qxGڅ=>MȲZt9Q_^hnϧ@M>%YPDjj`U%`%K@|L| |@7iw~Cpp \; ( !WȞb(*ET'B7Ma/6CGrCµc&NJLB*Tmru^{A6Al/A1ITSJdԐA&s͚X $ZX`iNJ4̍nITrՎRۜ'JQPL[cLO# 8<̫-|#8]QǡePGUw_L2h_SkI<$+.L9K]+d+NBAX $3j9-1-S[ǮQRE8A\5؏qO=sMngGbKgҰ=XZb*(*GcUEtKJY>Y> \Ti}wGR+Κ s6`VHn-N5z5\YL =5JF*UmUʵ ;/9$?^I}&ӟ VY6k{6Wĩ/zBmv{v獒 {i߱]l&Q%l<48xhWut|Ku\,^= N:>O__2ުe,#3e,c10ڰ*hTysolF @ DpC;kR& ՞Ѣo|Mx־{h\|5|?~No(vVJUAkk.ǀlrj5BrN9F&oGLE ]QPH& Gk7rL_l7~<ĕ/=W|=i $yo81Ux!ۦl0qM[7KP"0A6隕TVcLn&CH\ uO!VۍIG4hKsp픬.T͡d)Ye0J}fHNK%kſ |ܤw>t=/&3k=|Dqa[=ʺx* o7m!-)w h!ȅ΀rOHy7rbZXP-8ߛc XJRye pԭ{_bW|-;r7^ږ UM,iv%T&omūP FHmjd3QUBCǤD Z=ݞs8^wHGٛ87+W xn3",AڱJ TO8Eզ>>x 2{r-;'◳PՔkZxmk"k!y9W hJ.muKZڜ|bUGbA\N %7_ݿ?cHe,cX~JvE<bس؁:1/ A ڷGl؊&/GC$XA^ @E>loucPr_8Sie9dBg&J ~LL9;wvv\,Fg,cˏ1OzwU3 ~kjr&3->\=^|c zy>o=@}BBnd X論E 0PgPB="؈6 NIḢJB E<08Yͽr&%rh PMeȚ}e B!pU 6cH*Ѫ˜D>t>pϩ̗׏(,ɗq%{JXK`rm}\\k 41]k$B53="~GV6Xӵ)W 2::%@g&ݚ+7+ô%zugx4ܲʆUmj\U־ߝLkڈRtIn@壝䐬'la*g\3Hrpr|.o-*p_~}QԎxM@gfb/'ve\AաLbԉ_!9\K6 |$2M\?% L6(nYjѐ]C V"&\>Ѵ 0$e ^$yV5 N_\|c˿ce,cXn2͞jqLA2.!51pGVn}XUC`$ v=/GmbVCYuˮx"o3A5HH=*|9JHܠ|^d\ ɝ5*Xn9}o+޽ kMRI4(JK$&F,b *ބqSN h,)_ZFaClT2$f0:dFēJyZz|~+ 7py.3-62c]V+̰j(ܞ>p~-SKBd b}rT=d2? &ч`K\Ϲ!hY~P %M_c(+m_FFs[$tM:GnS:y.ǝ"Sxx+[N'L*YV@Y1~dX1 Y d aM2HDCjiJ8&#+Ԓ-U/,6ctQV}!NiYCXw+DOSՀ Q3C-X'ϼ='"wV?p,beKVip%K`RI]1xbCUA)!!UϺ[X6r*q4$pf'aNh;N6sN mW#,{$6wZ9"N{1\kӒ z*QEWso$rI=!y}\ܻH3or?۰;Ll[v̭p4Z Ĵaɦ4%ow<ug :a|/y~ XQ3e,c)l>C=%AADŽIgdZ$9Wavy;hņA-4h$I^74GIȫ(lZ^_H ÿ;jQx끈˓_h4v%p+_ I'$2e,?*e{oۘѳ\bUUfv2DH r43ۄY? 8o8߀\\2FhUT[3^-Ւ #Bʁy7x̡zb \W^gKnELdfG[XdH6XF)d?C,{SE@ ҫ[eϼir^&p)]{C)U1j>.,0_lS3( 0FwV8 꽪}a~ :z7Sxݻv2_}\]ZrW&O³B.Ȥ^ BEŎz1|U6mյzg]¶ڌ;76kmT ](+j䈚"?"-D!zG=vR!%E0I&k'}1U!o2v)lN!tȐk02q6uq++$(\9A8*d,*@ ʨtW@X\ lM6ڬFTGmS3)A} |]Nm=< 6Aӥb݅Ab%Dl{$$?ĚC0rF 0)(&uM>ZhT!cŘV1TIBdFY ,cP[XDycE0c2=bKbTbV+АfuH2ctWiO_bu!%̷fF( 1W_3J^W(iƝ66(Y.ex`kge|Jh'TpBfeݟKQ*&^/~ӗ:"6Nk;omt>q=Ŷ p7x)6nl1Ʉ̟^gBa9ȯmC6l5+[q{&۷0?[&h?32_8}aKq&:^ؚR0pE!x hJ G@b"Glpɇ2lsdCy*7<=MZ` BwBWHpbiBO(Q CIƑS-d_ w)iAd`:iJͣ$زx,jWz/`YoI&L#u5ۣfxg/W1r~Lȷ6xp.잍Y6 M?jXhJ >Tb̮y"c@Uvکe%*| I!\J.΀-VJq(lQךBe;_zBIŒ`^³/n]gqQ[@ܪL0+Pr/l1[ZUdHƏXI!q)Qpԭ}\pf ʜ(vuk"v< "6 ȧ(dvxVoK^Xl K@Up{xeh!409 c8w9Ⱎ@%ys7=9EXйْA %1^΀ZG/mAĐc"AeQQU ^k H.5g;v,abI̫+%D`^m"J|0)Ǣ.d6AFXyvMZ'5cøPg4H4#1qLP=AZy;rmRۢX3)y`kنd P q Y?4EPhTQ ǪN`225 C]=2> ǐe`?aqu[M&}} o.'/'yUA2xA zAr1 [Mss|!!i<`5Lby6V8+Q掮//y?8;(f] IS|xjj Iʱ[!:|gs̷#Kh"vfş~.2wЛٮڿNlr }4 W_ڃAn3 2C>Tf sҍnH8IXJ‰NpPƛNР06c$8I"a-R9LTvD"% 6MVP^[ Aqe;ܾڦF7-E19{tCذ[:od:%1$ źn#A$X2X2 @?#ѵݛ^囿d4Mal 4dz}XV gm8'.PlP f\A3{$_J<DcfJ ڨtAѹYL~Y=Y?[?$mb2|___:<{_Ñ|Y-Ps+?0PL@avLǗX5I.dΎ[DfFo0:>~5Dzr2HIdL+;&65IO ieKri~*~6O6YfrY.jL Q|"ׇKLul:A"vw;•|(6,:xp\ʵ>8d^MU6\QW+MD6ʤ#,;&ʫ-R`kvxk3p[pzc2fUKV䚽Ƹi'`h9z} 7>լdO3=]/wcmcX2Yn2|sWMv\~mB`v_I4:THLKK+n*׏I=-bц$O@~0dR Ghm.-$p_L 2Jpɡ:z(&F۹`{~#<h=u=58F>TصreH 2#6V%Ŋ훒(,6fGWH W)$CL0WmbQ[˰m'kW3;3d蠙FDHhxHIj=AׯеXŐ#Ii$VEd)p !z!j͒Mg\y!1@hmB w F2G,ؚKi+^OU,=`L u2*X l9&K2!mfbCW~~:Z+6ޙ%8VL1h8d4y5:>_yb6© mWp=ȤX&[* uLvsUTnXg͢cҨq7ZCKnR՚wJle7ʋ謾`H>\<d?rjH !֓9Bbl>r n#zso&ڗUUuYEOm{&3Łf=_ Aσsb83mɩNf kH&h#hR:rHRP0W%f:ѕvr-pv_ 1_p[0X-fѽBVk<"K_! eZHTy(A H:yM!OhlQ5xunpms8 K;o>"/18%ۗ"T+J˽Cژa@6nbj#q(<BgX4A`B 9 m[knMzZɍ= #Rq jSa- BؒY[S 8HiHIǦԭW{BvH!,[m?8cX=Jڲp]VD@{ @W:ӆC H\-*|vuĨPqSխ(joHI6Lu ԖHM= н3i"kp|"Z%=k,ۆG"W*jo> ETN>GadƬi(=/{J68 T.t,+d3ABzWiwL*xG&"D΋hST'ŋf}R ں9O5%_h̢uκJS[5qϾVz5:X?K~dncT#| EGmYՃ< `ɩ/%ۭ-\ u_ w_̼Lc?bt7\|kVQd)6/6y&S( MR;B(pH ~|ݴ|I .? ޳o{Gz@Z3B JF77\Kd93I:3F\׾, >X{ZJip(Ϭ=b%duWsc~RoB5g,}c|*&sVLK8Y"H3  64e"z1]_b XɅ*H 8(ف)$>D9kYQbzNUEvF2mڏzk[JCK9zZYbϙ__[}xo=1He,cX4e6o WSF6mۖP\&?DbfB XϟqoDЎH`nq^_[ 'voa5AHmqf"B ;^=\mSL! Sػ#3m{,_7ً H tR28lj&3ZSQTЬZAa@A҇iS3Pj5ΊvŞD hK'Zd$8YIPű/ų\=,y9$ÒdwQ.EоPNC;OjߊךH2zkX8#[aK=Lfx?w ^0m[h} ['?/lul:?% EKȹ YXCT`YT׏=w$_q8^~/Hݗ,ϢJ29nurf%Ki3='8:Qf1:i[ v~[ z%6wDkURWgJ} l1Vɫ[hԭbXe$z2e$vn2N/~0H_[⺮Kųp& ɞD YL"hmT2ufhƪ$XzN*!ɃY* X3=ulŦf9ebDQ1W5^A-چH {W1[;:ƋkvJN޶㓨kح8nj /fwl1%6JQmLEjVO@lgcTlQKW"POT$TS*zJM$y`HiDMUe d@[97E%AUAr!f=V;pxvh@tͿЮ\U1\j&HJSLV`bhbA=L{+ NvB";Vmɘd AƄadQ2F\!8IJ!ng`F**wC՗t2U&ő-$Ͻv _*]gpxù~ûλao6rzX!0++Y$}(snxpF)1ϤUpTU] c䵪U-v5fh*6_W۾?1T+Rl5⾃4~%KW#lh& ^_voElz԰r0 cZ E2E&f`E%6ا#e5*Į)Gzc!ajmG7ضI Mw BӼ3ւ!*%!t&[;_3ɠHX2عi+Μ9S5nܚ$ja?ߡAd:b΂oPYoPCnm *oYrhZ٥$BbIA?xC (T كon.{ V"-a:6ﱌe׿z=wwϜ]nJD`|A,V<6sChBz0MSˠXkݛAxW2mLPDžԯ95C'9j@LwWWyo{“Yc 1ma(XTk+>W23>phKd :£*uw zN  $vsAy^,^'_:~|s|xcv}NwS`?m-jA&VB0hͩ(فm` ,v;q&D-ؖ-^+QO6bdg;4νNzDprwo'ޏk{J4 xW3ZIC'+b%sAqRTR>nt`R7uPʝF P M1|Nme9RTSg'/UTF'&#㚣 x\:G,29o`5 )hAy@B!9X׫UxlOo\y^~3YL0kh y6χtk`Qdu4]r$>3~5@Sƒóyx>>-X߯ʀ}UgULDءĚzcKA$ZIſ(-9 5!Ο:YGiRi`GAPGƥMrξu-.`~ůxSDXVe$z2sL&!"=Z͐9::B *j5AN{T&.,4TrG,Զho'g}{=kBH{$g>.ba TH0qH$!9D~IU(/{86e,?ׇǺIb)>9@5VoכypEөӒY{Au=%̀fƦ(bE!àlɆfx_Aqq"`T, `0*hFIy 5/</',ݶG8ljjbj wdGd䈼l ` a/h z53/Bjfm`^Ïgy6n(.q'1_lg`I18_պhh%vA>ED~L m{ JDb(aH͕X O45)߆hJgI}G \=\SbSԧjCUDnByYm8/ ʓjgVyG؇!ݕviQU 炔=v{W}1}+,.5ӤfW9x L>y)1A'0=v&>\Uz˱ͤ\z)J e^ωQI.\0qc>.24hא k0ߝ'Y 샪ĶD `X6nN #($6IJ"SUDŽܫ+xuo=#]lفn,B%TWP#UATZ)n*~@Cδ>3 `,նY!ORqo0*tPU1 LU-K l={i&񾺚j"ѣY>ﺷ\͟P&F9x_Gwpv;S%c=?XS m"O{fDmD]d LD pn9=+eTJf&&竞!Ysjk6+I'c =vx,K0]#42=cX2w7M7_Cg\L"s1t])e2&*{;s˞`Pn.9h ZYb"戳qp5hǥI)[-~aΊgT!2@s ȉlӆA7Ql[D!۷eW?n9VX~3o/)UOm z*6>2E@ V1z0D"XU QֹC){w_*LM>d~} Op ;[0Аb):|$7#ҫkAq|CNR,MkV$)%B0iS!^'_•&fS?-x<*cǨ*$Z5ΐPFr*kjSi]%Tc,gI𝘒vXx<͛AK%~!V QW X͔P$C&]X=-8^_ ng'T&$̦-:$cDM4]唈 qծ|\?!}^rSX2i>ϣm[]`1s{$]۞]bMi!sZˆ(r*\,iۖW$q9&lV,0B'}ȄVG )JR TMI:Y"MP `(o=|#X-)* ><|ۼ3c)ؤU8g@^Vѥd0 l1V5+ bJ|͊6l('uJVBy| z[6ef󄘔M`vCnvwSQI_nۢĠB`!OCp]s9?'6m쑸~$D 7-:O^V%C`R@r(K?"b?GTqA%-j .D؈z%cR wL"a_F_6mKh;:GәANɬP2)^o Q@h!C4eV"{rRS3UJ|4$Q5QY'[Xxy /Zcቇwh"E~K4j۪M5YB~g5`UPL4UQx#ƒP4 Ls\0u.?&0jjfeZ°Z&*S֧Iƪ>Sp __䠡q>sܞv{p;"Q%a>+ UUD W&Q垵}W_;73{O}!DCmƔ$l~CPEoy6*HC"E^U'wCRxo6z6grӔX2оi//ߺohjvjk$iA2ICzߡ9q噘y}1>@983ePs#?G{6$e"KLʠ"sy$\~E&{DE^r:#|>J?clcj+۸\|?_Y` ,plW.Ѷ{ԋuLNl!pAGgѰ %ܗD*I< oC}E"QcEQ~Mf:hQBVVϾC Ol\HA34KUhqj $* }]n+6J ,\:Xkװ\O()=oot |tkvQ^} `,Ǩl<^%g;+QgwpP[]UFjĞʓb|X'^kP}AITEB{:59/ ~e{S:nDDG$`+sRȬFUORG%15pj;nKõoesr-/r܃.6) &u-nH RzfU݁U$%*!tlkD "_zG\^^$e97nû߂;/SL:#LOż'a:m{\'[kW*1FQ/dN"NTXl9ne \$]VqNqcCB^EH(c3g~VouM[?Qs⤇swohl2DX2MUwJC%MG+P^A˶@8EEI14lvLv `7X/U*A٢BX,|e| { `yr}]F؛|$|8db=%.mR"А J=,\;\zXfH㑰Zd pf؃{/Naks!}#s$KGjEԂ *h.*0&<06 `,T+"GȾ VOeQ R*@w>xl9z|Id%Pk%$>lMlE,%,W;ৢ"JlS5 /r*wE1͸ |t{m3¤`[;=}OʠJBENjY̒+^NjU'pϩ|^Wq^yс!hZ4<fX6PEꊭձ[Ι Gd|˟ie,cL&Wڶyg1GBo@>v4 >ĢE[L5$H-/?ɟc ) @4|?N;T ! ?o;\?گ-2;ly>x׻d2sw0mՐ:)fyb)egms{ mp ^e{!完mr,cvǝٯɁ`HC$^_ExQQ@mkْjldz`P2kj䜤TD%B9@0ͮc0U<5d~0Wzx%v%i'S _>2R?~8g: pV,b$##^9{ e__pF"bgqʔj E]x&{!̱ԾQQ *bf"@dza0K2z_sI &J{kްo-S_^DXZn"O qX1S|/{lqg>. ,>$ReR˖,.ϙzi/ul.W%|_qgA.~qvqb!%=;nͧO ?$%PG3ʊt7TqnP+ /y6݋\ж,O% %|3QS¸(ؔ|s ׻(G:7E Vos"gUiiLUx '-,a}Õ +^0|-ȮAñg5 CG oNM?X4Vy (s:!d֘o԰s0$JSQq zMI5u1\7+]a6`P69(1IL2$q,F L/Ⱦ-v(Bj4\mx]8 KvOhq$$C)!AW{J:X+l+%Υnת?̺s?D;;sxJnl[7yΞ^[#XF[W2V1LR%,ʕ1n`ljX1yPKJ=Ūeo)NlXm\Q)W`s^{XeyY⠪AtqA1dDO!}*`y]yN7law<:a'8x:4.Cʐ}2 b%3ժP.Kr i ~Ij9Qj?`:e%k^#{G~K&-AOXT9wt&*ʅL5Paۇw|oyxNow*e z2 uKH u01gǨQh>\mɲ-Fix GCٵE.y[$\>=+H2w0ggCU=djGy{GPHb'M+NH Ttm>6)ihWV~1Wo?0 e M(kG,8=!`V+L8&wp.^h}6Zr!*^VZ V ޚr %g6wB b楽\x W x[ jhMY]k`"ehvx8E$;g_\K; v> 3|qD }hp;8*Hu>R5* a ˎwLfX5JC6oj0d+=oHD@wɺ8%%-8:GU3mC0m F5+3 xp5JbN3b!Om \Zv|(vm_t dy6Z sJƮHh~mIQ8V5nh h&#Wx4I%HfzCp5`h]rwd8oj+DQvv \T.|Ο`2B¥{ րyCTE啤L=Zт(͎j]}M&ńqUb_Q2K,`,$S!p+_z(JwFl8 mWr}U}pr%g !Iwp߹)h:| t_ p{ !w:Q‹JbQ:,:^Lr89{Ų3I&9'łQ_M} tpceTŲ͹ʪ٤߳ku4%K 9cVYj_g`v[ggdz>P[ e(CylvGp8Hk4dE'q~t_"iy:HF03gM4Y߸oI%. 6nLI$xqYQ4RGƋ_cwв ϡ,C;:|b$ *bPqzKȍuGgnS;\Y]:VH<}AB L}cȶ1u]IIZR4H`mbԊިV؞-U]̫pȖY1Ver#O f(E be wU?o ]#s fCQ8o3񚩂5όp 3 lhe=x\4@RMcezRD@F;煝|`87Z 0>jf/5 j h_WPtd!iIEM8gbz* qogPb09jX}G FV]VZ*A?煨&g"u i5Dj=w5Df"Y-,FYU]ԡ1cW;0v^L4z!C>dyw /^ pi#B[8n=ֱ&Qɂ"t1}Qy)*Qx0VykL;SIKhBb.!c*}t u(q&K.9qdgÝVRΨJWϕ+m&2,!Zk{+W"ISV~zGhAQn|]qn2y/25%{xH'*)c'c$Z7mH#XϐO0ɐԗɺ-:J FKmjy]eYR}ܑ uL׷:A ߷MP2$I\Ė)I%x1 9%&0Z!AӶA rT%*x\%fC%Mkib2y ?X/Qy`ھHɯ- yV4yEUTPs: p[.JC{ƫj=gvG pRE1[OMacɄA"`SlF"xX8؇Ɠza|kS5G ^)x eu:7`*po7d>Z­z81"hsQHG M'"ddaӀ+Y*<)<%AAc8XVbr;Z?#or՝ q@W%j P Hty0(m'! :!#fN ?Num#InED6[lhi >5BWH-bQ`^v5* k,Z)p^M'C W&ZyrG,(r[,.*`38(ljGUyK2X[/ijed|Uh0*JF懨?z7v^bM=:]$)08Vϵ'ǮgiS#ϧjSv4wdu¬u'b9R] ҕ.l'8\(P gNpqS3{nrgH!@gi(40wJE~U'h~=2sVCɨ>RUkX8"W>O #wuwZ i j gr_SrTœt४SW2dHͲsBbU,ڄ}>Գ0J#c7c\\F&"֥J(涭Hzy7¹S8{rJ8(y\O#y3yМ ɜP*qssW_S$s5]չ%]?W obNޏ/`} @ e(C8Pm4M'BSrhԠGlզr?C $~|ֶ-f10k3|9/}#9ռ-Z5r^} O }c{CA;6DV,I"Z yu$0n2__z= |l Ep,p,XiǗl*h#uıeY&%hC@ks+p>ZټHplV) `BQ@yb k#o|ɸ鈯Gy?㻼ln-%ZqN_zO5$4ZI}AS|% Gøo`Zo+VjاQl4EuV AWZs}kzF`8W>HRȎr#l0| '8+RCu|RHT/Y,d 6 p8,vN,$c?VV_0y窺@o+*9JIT/$6O`$8bWłMK}BERJ8Jtvivpy/_Aal}ë:,Q]s89"M՞J<ݷyr(*Kޒ90N^h-l!gx';Q.r@yHzr GgjNme^.:Nc隶S-*ඍW&<|xD}S\ZFN:lTt0F ҿS!1uȵ[7_q7w5P2ڬi;F$xFT$Bm]a>vY.dļrIroPUHr0kC8I&Ϛr B{ޡ$ޣE\L Aco/$ѶW5O2L LB+? cb(CyUv]=WuQV 8S7Ӽ\\dJ5uE*U 2' ჋^<4EP|k?qc .a[ & *8R8oۄ[J Kvnq3#_YK ReSx]x9\_y/KI1 qOoXFSl7$a4vi} η%T##Y푤o$ӁT@5%v(렩U1 T,2x7d jB(GYADt嘵@6!5(+|TISbu30+냄hlt/2c6Y#pI/<tU !URUY# Ue+QŔIc4$E+ElJEJF02e3B5x\U֫G$DT{ uW"׮( Mq{kc(l%uظsQO4X "}bmo幵E@J"e`1xNu&> JeL*S>+VHPUq + xe dBlos*=a= )Ҁ@vec-}?e8/c(\S*a"C,6qR%Y=9VCXXIٗq[.ɫCGmZ2DqPGc .U8#svU?U*[*uiIǩ9ϟma}vw'ϕ 1 \2F14FYoLHv\czrGUmӌ҇jS4lrߙ;Odnm *ɓU!!Ѥb~ǿ:}H7e( DP2H ^q ?mn梀tCKuAr-ݐfh&:bQ0U۴t0GH$?0\R $Y?U>#Q!yI}Bw"CKo84}}(Cy<|6<[Y!HvR"!`:ܴ9ߚ*]'L5ހDUy=8;Gs!@RyT?PY`U]*9`]5) vF ]w:VBū:d:j A*҇:V;1,ha=GXx]+u/<1ypKNl t6 v#\ޫhФm0F|׉0_FMk%3߭ k]ɶ.PN وu_0Ipxd#+pዦ+[_"UYR02TK:f$!NJPOyV8R}BOҗ u,!+^b ^̤6 qV(yF)xV.Q32+Wo0*gToC)%9Qw).!(펒WPmRE*ZՁ^6WmQtqYEHFQy"S %$U%OR L+N9ə}[&|-0[ׯysFݽe j[$xq㘟Tqnh8MZ /ĄG2/vsU%"vnw޲'60KҵBh{{Uڵ-U9^ \fjfԱcm e׻zHU!U Ug [nǪ33RKU@ ;dMLՑ+#ZѥB:PMʪ+J$m3W}˲(R?yB`z"O{f<3tCa7"r>dCH46}%sWL^GGjgJCl̶hqV[-+%4z|B|[x+p )=s_㌤>Md{6RQZ o=H3/҃QA*}jւ˼V1uT1fEΌ:j%_+STv`HəqSmۘ%3+ۓ8(lm"͟Lrz/-am`?߫<6xAAR7mQU=y u"yx~b# Ø80ET~η=6mrG2yb_U=ʽW ˢO.gԓ }oܳ3YS^kȽNg(_ DP2 F)n۱6(c*s߸~$6j-6AɔoTƠ'.BLF\isAmܼKN,ټ |+Qh ս?3Gmc|*6.xešc ^ihސo_:_4$,PE;#/"\4 F lE {(` *e 'VN]R0^r`Ptc  cNm(  ]*qj{WA@Y-w9Rq+@!g\E m; c'|w<ٸ%˳u @GT %T>1sD"~͝6w^*bcÈ)]/&o6fO!jK =FwO>{cUmYJr\s&˦\f)t"HG^tf Xwl4 1#@ԣޜ_$ B\,6p~7^WdJs@ 2=CPmhM[۶ Y 'Z`(b@?[(ώKQ=A|VH\l\ޯ Q鳊XA>{)#lNT<M%tnC~ `C[: o{Ç{޴`u*3c8c_U A@|e4Ivv̯`+JUd3 Z#ЭR3 KnQrOVZXUe_LUSMk1Y+'g+!jٷtGva:&aPp#uvDO4we{ v.0|K W3N/vԞ W7&ӆ}eYW!.b:0J7ܳS<Ar8]tZtUx ӫ^Rn {+3 e͆vJuz't(Ac>{Q >ؠ0ΰJBւ|i{]̛hmA>VL&Nɞ\. n$w->:S!wDSl!T!I~POXrZd٪y2$!B=O.UIB `U G4+ o8%fiw!b풬( )Zc9:7 HƊ̡ŜI.È(&UHNdWa>F# Zpi2Q'3ą뎖Ǟ=ǬDDxp >p/M۫y-7g(2=CP ihz]pCu `~<|!LA%6I8H{H叭uc>J޻_ܿGHV:HaO+Ph2Jnt\Hrw"rSs^E Ԋ'z9/$P!~3&}yr/Og?tZ Ш0@-9s_Yr"Q =$9Z ZX5JiV"DV}+p1r#S1wAsky ahGh˦ 0 êVై e,iQ{J[ư x%<}1܆s")({$"!c9WN. UgR Q#뮱k $[E ]K(y8GȮ%X% ]ثU['z˳%3Bױi|۪EkE 4@].=\9mFۻSii;WXj;"FU E%jN*IҷdL|k<ː:)S *_*@dߤ2 cosjmW2nHc!H.kĄ 3ѓL}c5M:QT.V¥| dm}{߶ ZNM%]@P/d Hw!0ܚck@8?ϼx}eO>9u8M3U]r ^8=4"=\Ij1}9*5K}eOJF$tG5J^IK*os_b2md5%OCS^qUMR:wc6ceZ2B3M֐ίU:}1뉂'.j15 W/(Q&-'pI~Řfk+#pߊ/,a}C8y<ﶋ%[5bwm뎨ak9G!qRo3VqllU="7+yZt*ks"  e z2 7V/^cǎi&*qmϐTAE@;!&lVmِBk7cJx ڣ!YH)ׄAp\gy>9 8ρu0wgM9 >sQ V[yߔ,+q, (Z,FZ.;XPl}܍|Sos8׶_xbe:&1WZ4-am޳ TGdUdo B"'oVBTY+Qr5!QфTKN+h][<>pUT0-)ʂ `+`{ 2)X\p0mk)^*D,Ԇ8w'k Or" $PUC $NCe]φU'`+:?N S .mK] 1md~zШYȼ Ii@lϤ d{hTQZ,dQ'6oHD)p$|!&JSəlǓ0'\ :O0ya;ϩFc1ʝRdAK^!zWݔsT:U1片:]_35(WD 1vZP#և|CxRt qb<vy<>U8{!(y/wг>#kT3eIIv9JC_cpnQ1uFSŌ**1ң*ݮBUUa"V_LpeP60H[u5j{1(vUT KiSlEYg4vP˻v9,d>;vnU5 Q(T$ F߶ {Ž|Oh\ޗ _؆S8wzOT4]?$kU%d e^@:m hAyjڟq-o5KsȟIԼ @ e(C-nzCI`rBmz- I#B~BxTD$В-Љ*Cv IE8=,U5#^C U?3M!=Q!i#42GCݼoм"if?+So07_Pa<]c؏я?~/]e^W )W@W``ܓ_z؜ xx -[dUsG1Y QkF (# ,m(EXsRF+SNZa@,eQ"DmOULUӐ=X"҇?I.37Aē{T>>6q\uO@iT,NvB>Mg7'Y>1Yaأx IF ,Q:Eu&x]m4jfIJ>QGM 80"JEP"*j'#,Þ 8jCVO|H[o^/qƳQ/]܇_ ק#XA`q@=S H~Q#shL΋+jUb ND@zZ5UOir#B9F %-UbDIX;pql9/>X[%+|B2XȊe@&u1<|9d*;"%0Jr";9\D죻s + n=5EUT#9Wm}o ?!Qn_.yWJ~)g(^7>mk\ {4GCyV1l~R??swp%'jLFm{~_MIUaW >Yl1#gUƢ \v(`:j)gG0l a ?#q^TyjzqX݁܆Ѥe򋐱|[lr='6{1o4a`4a)jU)(0/,)ݨmcH<0zv6h%%t) + HhK{?DeoPxlsWuk4`,&"vo_|d<׉fBb Zexp@]A͙o ȫD¾ QXiEaŪ!R咆c~()$J' '?DɬA]El9 ="GτmR/L%=Vl2EL2/bsdh,JWDIQ:?ۏ=wxHus/|YxUhI7I˵T0_Yb2z }GHo&y> S<<|6>pJ.?&%Q{˙,5W9}䰙jJ*inOjM8CbF01B~;V4,4'2315 {NIFjn@V 9W,-%4G)}* UoQ(O#Czd2yJuX∐["ڙᅋsO]7Ck+ x} kJ!5_shp!k$mXiW*5dW'Hb6Sme5?7 Z?׋y P2cCܐUyNMZEyɑk&]~U4m[V˯D\gʶA9Q?Xu$NPC8'%8G%,$'B'yK y.uXbᆕDzPrƝ}Ǿ~wɯɸe?]Rj fcUd9g P@djjPj4x>I{Tflܺ' hR+O6| Ϝ[| 8wx }ko!jNZ)٨/vyy=޴A˩jݨx5 `rnޚv+0Yu0紾;5٘|_hBP\Rry*[V?KKSz^Fa&dZ 1|{4{:bRPL߯a$H3v\Ojf4)vվT JԲ|4՜j;zvOj//)C^LQQk2x$G/{Gd[ xؓ]#xJ Ͽ3Ǧj8C.br%+[6/ATU;bWIĒ5嘞H,;Q$_*rbq=j7=k|6L\yk񹣟Oz,3 e6 ‡@#|ZGBE?`R9@*IV>H9'{0I45mXDEϱ=a Feȝyk9~݋/?7_?*C95CXtHM<}v;U%F.0c+ j[A%$B=$qG=Kg'J~olbU>1nZ+K䶸|./\}U^h 3wق *Z,)@_bQ[(Qy**ȍyXHvED!'M@dk(9<W̨LVPF h %"Snɚ̘Kvɼ휌C|2|sX_vw3p1-nOj'ʗmH%m DZsE\!% . :k;up |`uϟ@ ό|(c!ڴ4*U糡`L7Λk`2ڭ#cX 'o*y)J<&I!TA~k'z,TZZX3լh>?g9XU5J.L k7(D!.KwvQ%υ j;Li.f@H{kUٱo>*g|7K> 3!y|0dm%bMO͓lz "LM69Dg TlON)biGlRQGSx$ki{~_k|6̵Vg {re z2 p7T=}dفQ(Lq_qjB +**-dk]SLEFw؋54]Mzʇ?.,P#o$kݩ/8)f,@ +M*1Ofpv =#Ԍ^6EbˉŽC>5؄ǰtMW<m>6˸Ic:1hb_i?-\܇P?|aZJ?x n3'&.72<O`HҏviV_zC?K+eD6(]lj;pD؈EKgzPsɹjznL(=2NGѰPAc#snJNFMO%iԯw[KӦs{l>2ů9^t+l-e9]J`sux,e yr 8BΛk^ovFe(?J,!+ K$&xx9&S{ -ZoȹZHk%|g {be z2 `p7TE1_f+9DE 2d&6#!;T 3g:ov݃iG;H6ރNfmF,O-8(Af.Dn6l @H:#<" -A |19 oqҶ/7B}#$ѵ>3o DP2HNwևUs ~H_H֐HX)*e \'q_kד['T\w(kHԬŚBR'$LVQߛrAۢɡS:exlGlgn>46Cy4׭'~_տ#Kb`2k Q׏jo%Aɍ=_|zI F+niH,bvfpoF`[d;or:!yʐDP\8 cJf{t ۸qpB6DH; R)BۘTۣ.i/Ο~~\xڇ5dpDioV;ST:D@(Š;\! YqgHVjB4 ej++Ac%,73Y>ê*}fE_(%PUIUVάЕ s>V>MLl_wS8rIIe=pEuSo2a ITK(@X`?B!l_P*f`BީiG^mt[ʊh&˕zHv fgTd #K2vc*Գac@Q9L[T}Gǘ5X=/YcꫨFe6V"28bzs槩,;Vݷ>"M Ͻ`{/u3EX0B8z0Gk>زZzz{;߁B\/u@E@ e(C9 q -+)@"$hV-IN@{rQ%#$zAKhfH ZEdKc e"U9yT,D)EYj{! \0{9%]ϝ={?뎳?_nj AzX]-{#_]pfgp=K̬ayĨ[KpW;&p.DXI89و .rI2] e853 7kӒD5I-ߔU$- D?-On_;>??0U aiX%!ZT%Bf0nVܶ|tbry3*4"DU0^\ *JVE$mAVdjV8bkA` NUꦖNd5i&@{N;G$'-owwd%gہOM.DT{'F@U*G-t58+b%scE@`V5,ڨb\!ET& u2r+ tIrܓʱry?EEI*d?d5#enǣ ,]dE=nA c\0j) ޯԦJ@mT$ A BI|dEgۆBƗ dڵi*qc#{neG_ZKQ0ʓLj(ZЭTH{t~7|M(S^ PF(@]USmOi(oqxɬX)1#r;UŘ6ȼ(4 @ك(rt9srw ?~tlan t;.ѥAr[ i%pTO(_"'!,cY%^,TΆXFo|K|6OǦ$:>[>x@@k@_K=6Qt;~&r)V[@Zwm帖0c1Zڢ%|%م 1Ї]fv?q ~˂jW4/&=SPjqoHd7_6lmӚHEyߑvlv!"m#<gPB:0kJpf0B.U2>UKUť9&̱Ղfy ד3TXbhILzG*9>몊̐2MTIڿUb-(gac)1Q-O}Ǣt|O\92_!w޹Fvi_O2iߕ69'\>=k:8uM9mGVAyᵣ<~T39>e?z͐*'Wi|Lo\V|4xa;0wz絜ϯs4 3~;\$dLEvG<]vDkvv[kT>P;l*u@rfg!`kiKj.,Pg]^"6g`,iJ<^cAv-Fԋ*·Vv{a >!Wt%L(aQx$:!|Y.ۯNHLOHuE/e!ffc)b XRA83+΀aJ.3` ,BATAD0,uUjic!)l|. 哤J*&PSR!J\%STRNp;%_=X"F>mY܆dHc&}l2,`|@2Vc{;БGU)䥯{B e#/9JMä3ZEN%{>'ao|]ؙ7= I29,zIZSVy!U?+`⹵X &`- 1<^B;(!MV\.8l?4ֲ[{s7d7gHʔ,(}pBI F'I;Bm@H,Ł#ɔDj${fwUuo^{:ޫzW3~S;1<=C)\&@+I^0J=Ŋ-\ν睔tԞ|l|tchXe6C7e:΋v2]j,Iz.mpmP9ˊtqNdqNYi-HFO(3g y  eA,ƥ-Aث+R?g±6qMp'-g0IM2h]OG=ӧf ,`[:~<-s\Rі PV~c8˅ךۺЅ fS)\X-U*A)|@|;*` iŋM 8<Ӂc;EG  .;[yG )XA^ )M]GiyWe {sLc&+V삡{QtPIs]x3bҸ+M9xoW/)`G?$ua XiE:4!+k+'mU*IT4{ܑ3xCc'g?WvAk z=~/ZpYkR05~H u]'6T٠&~<-+_Xk9~d6 ٞ؊;8퀞gm E;}Tl%ІI@)U)O B4[gWf2sXÎ~ެNnN]oO?zObVpQ:[r)T fEM{'̠{oѦ3lܚ a5 :AJa{V}^hñ- pI M.^{ȶgF@nx!Ho@E<_TK`ǽ[*- 6j.GWI|eJ8B||PT6eFb= ΕQA g.h2EAdpY'=IMR9snyLzsb=@@"ryF@Kp.6T)̾ ߹o9j!0hʪ@cbiZr#.$2yan٤tBOeK1c6K3fXP7еB>Tt}'8#b# $ I.C֥iZ%:*\dڠ/uZPNVx_4?bVC} eMurf&|UxIԖbV:gJ G_||IfmX0o@}⊘g+c-,xVjPS(naRXk8˃Ue0bG+<Tg@yiRolc0hHIÖBA~g0:Zx S6 *<\5ᄎ3 G哹2C\, ÞA9O J`U'۟dPJ.VPVl<;âV;^Q,U\K6p݇\)S`jΥ<'z75o7vvr f }ڶM`0Kk&.0A#R U9590뇕D*g=. 0@s> Z˭@V#w2l2)h2$7{:C z(﻽~>_Zj<~~|zV#GhυYK =}f87Ne:/Ă_>}/8o-RQld GOx; u-XZqy & 5եIWqMY>*3(Fu.t2ѐiX5wzYUYaiN^=y'V@BN& XS ֧?؉qA!ý/j/;T-k]s*Ғ\_R,*EEM ]PXӂC=̈L\{},9rOy8H`{6++V)X193ul?W,NDe Z)IhPD$lME^cZՑ *(kR*O+E+ JT^[yu: q~}e_{ wCwb`6):(ŌV1FbqUl'FͲ/+F(>a;r}X)RgQ$0)*T>[<-֓ͅZ&{9zA0Vz;gȒ#(E%3xΜ` ѠrlXY=8 N9U.* a(TpOamRýHU;[In4RL(}^wkTA3"DmQltp=0FH5qviHgc/PtЩf"_Š'!DW444>76+[Tc ܚnx)XঠrR3- ySt=)t6dXdMC, '8 T,uP"])إqqQ<NVyڧ85Q8l4ok yDjȹ2+[>V=e;-Xuf!#k׆K?~c A~b5إm)kб`a.UQU+ϗsVJ{LXm:-5mIkxσc8Y M~J֗hWUĪQG}SA D}_S 5VJ rƘڋe'v Ey ߞq#b3lǪ_Z &ӵJ 6Ή2(@GU pz/d9?#ҸJUk`FԹcU%PP+ӽJF{޾*|7:0 LF5,W@QCWEџ+{8e6q$?+NtU˖m}5 v~>ϟ[ܕ+?W `mZ@wuz<)l`̽Dt3SUU-9<5۟a3~A?NYYnBr -3a +R.q)vجtj\|Z?9CZ1m֐rjRʿ׌r]}(ǝ܈܄xumvu۫MǙN_OKo0mJW",&m{ԛ|y\=Mɷ,CwIJztdW6o~8Փ38y֥.7`R> rJXxU>;n 9[:sS>"2AS+n A\*%r[ΛB}cA'^xm+),JԈ9˞megq΄F% AQKHA˳ݗVSPn.FV+_8Q:EA&*l.~ɛda[B3m[4 oJ֭>q<|]BˉVg[hc(g~8isJ Z9 =y*hq3|FuЄ͵`π@7L4Mv ȻVm~݋n٧iٶ>??|ϻuSNKEQ tu]@ɨ;ư4P3-8=x7ؚz8{to Fg<…} >L@=șb<" v@llG:X Y>hT4 CQ}Q{'ϐd&i p|KEP! U2X7uÜy>lŦ րs TQԐuXs5>M U`SU*#vk C 2b Omck)PрqSY4A 9WP tW$*+:$>]6/G{ ɶ-8 Dخ ||c8 ^ z:Prc Vg7 `Mu>s#;-U.O[PdETdce󛏺/Uڥ M]墙Wb#HghfakV{DEMzcIOHJk(xӼED r3U6G2\% @2rf^mRoADݩ|ɐEƓsP,6{3e-V4bk0VIzfvf=7_k=-iהW`9љk,Wkwؤ ڵzĬ20H-*~Z`5<#@Xqim}A{hG")[noeP`PQY;OO|4N;K>D=~pg^;oV;2Q=la̘ ~" TؠaLz6p1ZBkT~B@vdɂ%p]ڒiLW vp][q'x\5,4bw?0i7^lg|o>~~޻{IUdm¸ AO/x`s fm׾UiGp ƂUw¾e10FB8 T|DƅvB+cbϰ6>k6p> vE *34&ۿl5..!~xv7o.Ɖ҅,Ԑ"esxJO 3 Պh\ dc<-TFbmt(|~!%6!0=4R0 h|y>*l귩8k#V5A句¼t/ݙV)EA/9$0V.ײ]\\ T, +E\W$(㗜4d&rN&:l MxY61kud#wa0kM%dcc_%ǘ-YQthbt }>VQaLV:TundpWwO`cm_CBBlsRkݜl,J hQͨc)SꋤTJV/yu(Ļe[)lfe!΍@n  Աd4T%A喕8mgpRGKEPF >:OJV0.wL  r9oUXu vCJh"!&>[ foX+jeE kUQrJ=.[)pڔ,dYI-ew qTWpo_|D8 g;v { =~臛,pg/+)ZUtBB9!Hc#˶ZQ#QI-+t|R JUj4 /g#EzX5NB.QWmb;G7/nI`o Z憎Yu?m^0q0e@ #@g9u`H|IBxD))*}jU9x.xxΟیf(k0\&pIPIU?:cA2}ؖ.\u/p ַ<{touxv;g,M1%`Lu IŪ_]^ 4ͩuDTP%(53bX2bJE/Mz/'[옑5ReĪ tF֓?KRW9v;; .C?CxzG^;=u8bwflu@8YT2s*1hPeJ9hѶRrmZe!8 \tx[i|-q&38I}p9?4Omk!/\gs |b9WXIHkq sOX(d-ю jT?5xΘkL ]u_r=u\)VQJevי;n:MZ/}G) ;^ӱ)b jiVt`2$榃m kiX] i؍ |`TB 8d@b'V, ;H[euz&/3HK ?-]ctb|JJS4Ox6ih pN]܆3qιеPjFah/ҍFh%wbkF䈉tc[BXA^̟bHlǔ+ʶhˉn~y 61I𤲖 &VmUkcjG-쨚JMX:$;m W J"0ll円-9in:r )KRVՊ޵%(2zخͪN缕sgg'?? v3؁`Þ~AO?C?ۖ w݅Ri|SC:)nb+QVj`ajfH@Bm+\|s-5? ,j'q8 ?(Sh rBT!dB%£ 580X Lkihj |jwSf7{nڦuع̗ݕ@sg>篜fƍ LŒ;n !أ>G A*5V0yh>[ѻpp9Ð-kX]`Xeɩ8#\!xHsA+v yIw.a2fjCa%R$˅ !#uIy<<Y'PT=_?x]sQft\rNXpgR;cEC' lUl@< R/X#JٓT>d^t4 Zaៗ$SP`F 1;V#XV6԰yt'78'{+T%=_wŽ]`i̕2)*GX:F l٤zc U0 {9?XMhPN&ʚ,(Uzu|莿a2l)GԦop||5pA }dNŖ،ƱZТCr{w Uߎj@.0Bq*m_8Tq<X½MGM&şXXǓx v9;yr[76a/3,u2߂N-}9 VMμR,2C)¾Vzu?+ǎ^6lUFV9~^ ׊ 2۾,#0)N  e颠3E^?>?H@L-:R^nL5 s&[ ; ׂ%*7PYQJ'6 ˩>wP'㩣M@jG_\C;Ϯ\ok1M?ze;;5ʿoj5CR@AО gIy9s)U Y@o@ !A1A ~+=V9Z!h[=}72%u+{gqs9"[CMns=k7[]m+;W3e}sKK_޶,?`csr l\:.)(V0aM2 W-uB&J*SH`QT?I)BQ.2D⠃"%7+B! ![6g XpP."o)8c/+d{ 9ۧ;t [߱RDd&J#8ãXUi*Ux\=|3@jȑ5:9$ھJ[ t~!/ECC88ek[Mg^؂jQ>#pL_jR#12W۵uYMc "OFYZ5dUƕVt(N[iK8cÅtXTN= k,4^a= }9gs?_$Cfl[ZVvpgǹ-Y%-dA`A[,+JËjjLX.ֶ* mKf0Q۬>Bk=gL/(JvtrZ-|4>wnxT)/+Mڴ= u>Y+jK˒ fr| ; PORz+2qQooK_etȳS h'!lg;~C?ܰߎݽl{]^7氍'3i ީ!w80sg` v:TUghٮ ('(#9k.,IYch]LiZK !D. l6qsqXŌ6M6tI3w|4}[^&l4;E}//} _w̵Jh䲒ɂ,>۫ leJ?84 V,g!M~mSj-@6\?e[ QgK$jf\A``$CsdYmg^%$8hBAi^@)Jq*'GU`6 pZ[ coS-Xk=(adug`A!TF h78rචM uF\ 4EQTYJ58B(۵"y}贈{+*RXp$J/pI7͗1-G!>:}~ڸѠg ' dSr,>fk?'r{C oQa00ڪL*ޤl{BXz>.E^y޶26 a'`i'\!2?Q׍ i2~pXDnRq n.@rN _۬/cM$@rʍqGue|9LwZb(ͰwJ:ցLm+6tvbx8؈m x̸"#_ ՐkTVLZ86|;VeYѧ:XsC(TR4*#,ϔsP|3dc2X\~G"+} %F3cGae;!ޢG-%{6x\>t]XAvh8Tx nI`&\MPĺm#KVRYؾ 8*&]t;fK]`Q6udq~4Mw:/gJ>zEO?ÍW`3v\rNX]y\JKlq &Kq EiwP$-š)fkgl!XAвƁlג7!pjgHvn585 \֯Vߧ:uqn1mjsuckvϼ˶iwJ_Ҹ O>bìoaO6b MڤiJC2d\QxS*&e,M}Nŭ ֧!e`d8󐋳Vph@6Q2X1=n{sMCVo{(UR"H哕j l&Z/jv:rch:9/+]?N+H[;=7<2\a29B+e98@;k!t!Xy_@\}[ PmId%ϖT< U_^Q*ֳ:Χ|Y\C[LF9St;G3K_xsE̕oDys9W0l8 ~c1+zno\d2 mZ~g 77&=Yſ5>3Mrt7BP\E"LǢg;A9+q(gDi3я B\-I3f)?%;wnLmZZW5^~c?5Wz6;8g?|38|K_ء0d HT;9xKS g \8rPl.m66d4U` sQ$[5MgM Z *X"ߠι5Nns4 Yª k/H@?no}+{Os6.5+[cum i`.Vl p- `^| P!U6)K- .Ws6d5V:v_C@'nOo r hSE:!l(.Rlm-KxȊPrZ((K$,Xqm38J$<*&~}V5XEhp_@&` k<>wx0z3h ^]cټ~]VeD }fOfʩJi;|9IcyG'"S` ĕZdR_F&/cw&PPvc^}'U`9e>ܘY}Ѳ 5WG|x2eЁQ@U< }WGj?0 =+l'8p~8z֧|lc{0ά6JkV\xw :Wq#fKMQeV48|Zio/*3DROA\O`F| Vf"/:ZݔeY 0l9~]g૶Mچxᘃx>8TWQAzr U:N *Z+3f ,UjxyoJy`;xA>T`xNӲeKkkDϐ,Xձ}j>N~?/)y.Us5K}5\+r>a_p aL W\SJ>?9q%lwQ3UeHp5h؊Oq&P= F 5h)ק!4939)'ΫjvJCs@"BnL 36'0Ad7ٟGwZae􊝽ϯvokzm(zl;عb?G_W?ʞ[yV`2<-nC)Rqp:CKe#Ż<,ZVfط\íixyhWf $%/nh] h{nm*#.!,Gv=YXR ehcY A>3J-Q2kJ+d|<1+AA^QVkZP |(XÅci:ߤp:1d^9ܗ%D].RT,(Yp ,x['y~z'5s<+8"P5ϙJURNCS.Nfb:ER X)7>v) r!S2xD1s{Ϋt$HŒ rγ:/vHN#UepYwby|xl^Xm#)b8_9[x>}p ++Iֲ&JX E"?B|/];VGzTgg4J Ykl y@[ M6T_|M"a([(\a7:Od=) %mg3}wNgځ=\W쁫nWC?]8үZ٥3`5}<]#S83ˀ@JMC ڽQGk@zPcڶ5[ 1'4M]UUBOa!B` #~ݴ[Rອri]5\ kǔ{R\8C|] r^V븝q6W_}a?'>=ۇ*Mw,]({RD}ΥH ?os @[{;CVkya.F!9r`'RVtsrR!:eI=rǷ '~4`7i= ٞ)oi?ø=À 8h=rxJΆ\I^ˬvo?*IвgpA *lUzbss.R~*y &K#CJXR7^Yu$G\RaaTU &xZVJ/TԮp9wSVESrwR.t\8[IQ 3d({_g/? g9dlAz_LcScBlW{4|\"a0<}d b{VqgВR49ُ@tr7TPso$7$+L%!cg[m4_6k>;ۆP,zl!CR,+9&oPv&>Z~6mǼRr^ZӅf :#mygWʓ-8/`п6?UpAq:/? ,g)׹SO J͢zm"J֐v_jْQC] OD)Q˲t6Oc;mɍ1>?FeIZ> :E\o<}82j49#ͨ9^.ʄmvf|P@YWyJBhmܑ:qMkaqRFld]`c&> `HS:>l(E:}',^_3^5.ϕ5N~vg*C?C_ܕWM v.z a0$0 ! P͐ *Pw>RʴU h`a =Q3 !>@HtCpPՃA(noJcF"3G;6Cycˉ" EO<-3\jR1mx_72\ {p^Ks씝ڮW_u_W?O|bOJE=]R椬dϊ &NSA@*[(a9 >3YP[Ǘ#GVͰc8t`&i{S+[: :APִm,†%1x&Cڬ*!2%{+px-ܧh`CCڷ0k tL\R4n| 3f&Ҿh Zo((d,$.j'A$AdYĖ6l) 8o{s(vu֖bl g5Vy^vGIy彪eMRz(Eu5%fulC8ɺmi-y1j Dpͅe$s7Y`U و-r{bYaYrar^E&-,\m17qb*: ֪#=Yy/h>| onfWy ApZàK.WbփC?C_pMw?G%` &rwlPF r5)pa (N?ܟ*uAUf lyhȎ C+8N8-:ߏڶuq-q xfTA`6Q% 7AW3Wք=tcA++>ӿ++o|ٺKL*T hR,Y[x ~ɦ%eg\(sfM4^j @nC PBĭrT"H"`^AVg3d=P`d"T Ɛrq\MǗ {큌5tn. VUT_lTN*y|o˰0/)*MWs@bĦR61kc%5!^UI(+ɚפjP!Sה 'SjJ>&بR/x^{mm?H@6#<(d){+I*&_"M VK82|L+ "`bUsw3\7 [:Ų|&jgLf@'BsGK=K ǗeՠV jM7##=eB*cI*FzARÇ:}za}M9N;՘ vz6mʏHdo[~ j L?7yV,+3Wq.l7 iCC?Ÿios籣;PUU(䡂&Hl+E6nHHq@"/@ *Ppg Y3EP]8-og%uc*Y9ٿa^OC ߑ\{0mD9.ogoK^Pܶ{\>O~S/U\ؙc!dd`4jM*\U^W(?J /74kz[CE=Yn0~7ܺZÏ˪asש]>J@, ]+:GK=PyޮLW]bOR FT"Y^l< )j9} -GU لݢ ۳edP's b9=xpG[<: ?.Osꜝ@; ~6o Ѓ~~Mx56>o( dmҼP*gݬ ԩ%k tq8]@4,<٬M Dq@kACA.12y&4?\ ff~RƐqZUBo U~ mI`vq2W9\k|sO}S'~~7*8_D`1Ub@-fHطdؒ5Y͛*UuWm9lug{ΜNxe=UKVhXBN*FRFKJ \Թeo捋iSor&Ozğ)P1 8@ XLJ"~?-< f M 7pZTS/تNeȓ pBhmm*d1a 6Gֱ؋2t )[+2O8 {]o{e)gȒPL_YV+t UJtIǮ k#{gnSxq|14,',W@=.ˑ@>-)OBv6M,V.IC8EC h5T1ܱc(6p1Gl5gj/ 峛0^ۘc~]=ugˬ4>K9]Ϟ!)7;!fR EĖhN0Y;ZI"Tb oZdtFY RQFr?|$;Y$u)i >B Zl> \<35trbM(ԈR2vT9p  燅Yox1@[i>_jmϮYrl3l'.Ù~AO?C_۳Ý\eGCfISURl@fRds6V#pqPC&P&Pen N9Sb'ubyzP"pSWԨ*qaֈz:WkkkNz4/]Wm 7vflWwv-qg?ٯ?77 TuV`F$z2mF("٬MmhH8Zv@E\@dauy[-[`"@*QpCRXEf3kPDd݃= ٚy*prNF5RQ pYho)Yx*Q)e m@uE{(y<8<.&*(\ң}|Pbê*9_"t >"*69op7Ś(8/J[d]/P%P=%SRl:N­jgo_3[}e= ԡirQFA >习-rO fx?{j?58PJNsBPphBR X(eKe5RRDS*ߒkAp })|oo}%'݇G04NyIu:pI,*C% (.WaQw]IS]?ec.]+X** Q`ՑUA`Tؖ:w F>e 'Fz7a!'XVg?F @(K:t606r\[ZOʍ-m/vyQ>vE*wGW}e-H'RL9.kR|z:U$sXE5ɡڱ 2𑧖`e!`}Ø Z4r<"ݪ^^[|Bnfm%c$փC6'eZ 0W} ۂRm1x/>MX>XT;^ٱؼtM{Yx1ZɎ0g=ȿ_7ς]>Rxs%])sx3@s==~Ϟ[\ޫjz?tPOgZ s芠PSr8gZ%[& h< v;O M/}~j!T!X3p )rH Wq^ -璅*beh:V>rܼaE̒ |fm<MC{ ֕MwX]QAW;#U_Vy7X'sy+@j)*"ZL:fUK뼨c15"BX2PJ䠰[4-Xit _- $c(gx;^pU!t vpTjxޝg[iol:x!}pہ|xӜM{>g_x^Zs%vC+8p PrΕ.gЃ~~ 7 -EsǦ5`n3R$1)s--P5oqRn|H>Y7#؂hC`s-6q< ɧa@Arـiȣ3# Bfp*ɲemc ݚC=Xmlu܂* iIrtx /:JƋ*3fCK+R f̳ޠO gc-Iwy{{UWo"$ElI8cS$ѤDl lE,2` d@`<,EQL)2)6)ꪮw׌眈[Z۪n6n}͛Kdd=yBh 85 26RKj ϽlownSq`S;yh.lq q/AeM@c+ɨ`c S<b&-vpaKZ1 嵬r EJ'ض#qyY~}/{v`rΓx !'j^~};{Nc:\]sfB< 7304=i6ͦ휶Sʹ^)NZ]X8Lw0+y8aH&9A[f:b6bck78[l֦ߝU<-^NQ)T,x!@WdD43S>؟>j۷V*˱6":.Ssu^""q;gva ñ(TW L08!VN(Wi؄Վd ]LGPZmh>/FrVda!F c-Vkj2obd _C"ӲHe6|8$ti/;oY*>EЃ摁l^ f-.d]i3M@E> Հ_;GaEYuU_5~WeI.BNdɂ-t}]_lC =xZ'VQ */}'`qX=V c6WblX! m]lBziH[Zkj3b,[ā.jxߋfqݘnq#PNRHV޹ -k\XU귣[5 JhF2v<>Y$χT;BJJQQ눕fy, )B {aOPh@7sfXS2p=m6?GQ9s _8PlTAEb 0IȢRKQEdDcRϗ~92Y.9xmTC=#ߣM\ak7HK6Aq$r\Ⱥ%'3Y.(QႺ#]cȓPT ^sE&{|&%g}SR#%RE}bY<3= ~e؉}><Ѭ$w8JJɽM^4OO:*ns,>fW 3w K85 '/frɇֲ@"#-l3$6:w0-(Y\CAnbs5R jH3.6Ţ>,[+!n(z|@ FgV} c}`BNsɢNu*E*qZN춘LVF"H!Xi@.uTʾ MHLeW4<΃SpMEpϠƲ/f*QX^7 } g.+p l}r6 ,sǨxMeAeҠt>;Ti{.M-aiAý鯏u̹ #XokBFE'j:یևAh3ʴ(8qh"nSBaِ8cUlWRlzf ̦4+u|5;cŋ!5y3wl6-X)ʞEuO#34%TV)~ B\_??< XS10 ү BbUuӬ APy=P(Bn};lro6mw]_W_wZcz=ط{Sñ+v`Jh*p4۩J8OQ8X+a7&{=+*H٣F;Dž9<,ca8-d $+αHb\\7Q"P9hk%h,y||l n'h6qbVZJ"6-^"ՍehRґ5dg%y \`T" IFHAS,T,c` q_=Hڎ5#PB.*BPCxxcӂ~z E`up| GU.p1 Ut}eA[ɱ=^yNRbK'A VVE%VT8>w6ʱ€ =OkLi3)Dž!Jp}cU}#o}7#[<64 ;LrYnrUprDYY٤u$ e1#d#W)Չ t1/&+bR"?.:љTH3gN}mЃU(ҷ1K5XkcVEy !y@2@E+FqY^lHy ۺX)w&<.Z^ hoEir)n7ԠحMQel<\̲no⺂˰osZI;}LZcQe!koor\!ߛ.GIdy_槽ĨunO–AogQ~}b }u@cP`q0(Je"+?ȅ>ܻ\kOabN wH=fujKSm?|~#Û s&rVϯw=9閦MiV$ܪwPPs;`-XU5a(۶ w_B 2hdžuw tq,3*}T hd889fƘ>PꪪfxpJ"|;1f6}aۿۯ_oog/F㻑vmFmy?Ͻ9_XZZ*~ޟ|׻_߾>}zO>948 `~EjLiVPᔔ::ff٠%˹q6Nbfd dWtbmڤ.0EA $6i@#ʥX*ovMm_=}װ{zlcY` C+C$fe\ Ncĺm¸%HF\sBY#[bg("CAL )J6dF8I9ʩ,#嘤l /jg($/EXHuX|K@"23+|H.ZYS8e۔ e/PM7)>zgmu`i$*Vؑ6T~M!*,ic{57c*[ue- e.h_rt,e}O׶gyb ;چo׷EKxx{/tlcrt8KF 8 窐'6k/1/[a;8c(Qykkk{˛N}9 ~u6 mDl4f hs4-ϻyN>IgXro<O8A(b1*hb؂ANQ=V,MB`0Xm%N `Ɩ` C^ZEB(\ڻJ[~fNx;=I>o9m:mQS?}^(6V;SO=g/|;_w>韞'wkCt(k.cqmD!j,%^go3< !б%#5^F7gn uNGǢ.RA 2Zt88܆?0I945+q,gB'azXRN,Y V}2CrVwR6Q2BP Y "c*^vj)g38/#8J|q ܙ2@Eck`65t;/vc+ЬY1BˣQREXnxř)<́E;(EtT*yȪ4QѴ$%mYF狂k5zsau p_*CO#\ H"Qj:.W%Y0_ VeVdZwYˉ ,V1X6 ʖf?۔diHU۹iJmB~f 󾓹on`N2T5\h[.{:'SKgM<F:S1h͘:X:TG> }LZ^7I"gkRV^GɡDf*Tʔ/Kmo~KP ?7ke8>JqY귢4=S>6qri;-3mr6B)4izfl; 4N/WOO~*aGScU29'~BE vFeY*T0A5MG5\ˠ0[i-Xʱ}ڻyp9S2`iJ>wCum`}}pۥPwI' F٧).mhEvQl{s;}?zգiyK^0|NmJ ploCY4JzRca! F°+2QCB~ 9+Jrxȹq{ gB3r"ıӡTR+1,E,x[(v"J |FST%.3TYQц-6ReW` xVÞyMk2Y16.\UD.~w."H @ρ@N=hƀG<#VbXE򚃗.ṗ- )#K]?Kp߱oh[A6_gХ/E=yI]s",F3J㬋m_šM@u"^ʗT0g՘*Vx*o~o골ORt@7ܴTgG %T7 _Z] jV% 2QшB#ҹU*jdÈeÅx#4ry ՗*7IЦVxx"\zVER9e$mT:r=+]\8N&sKJXG:br9؎D(OGbyY>ZȎ7;<&6Z} OVl8:wy nf,M"WKlT CkW5MϜrrynźf;6g#>pg.fg6ͦxx!.--8~U9~9)~(_!O׋~Y﯑yڜ3eY"82'G{5G m¼o,B!ex0rOM7\BM \˰GeD@ mP)[3kٴ~oܔvPYEvRl{[pnvOOٳ~]|Q }2,Wn_^܅A]Y)Y0"VKb_*dE4P04 ]4%. @06diJQ~#b{UhW 8ކ7}8q ^R;I;+V+91/ע1leH'm Q#Vg %~VJyI)rwC }_A^Օrމڕڝ<3GlvR*Q׭'2 Z,( q*"r*724c:ߴGx./F5k]QSee>)ֹ dpeCvd >@S|ʼn6i2 n6J͍?{ęML33fUw6δjru]#H/*yخ!/xËJ:c>!$B"A%πg/kU8WN`P/`-jV beB>I 9Cp{#PwhwOn fCYf{<7t=_… >0O}oᄃ c XC nU햃VKR-rĦ%B XVB7YEB)vKފ| ̐CgEu#̯rnpR V8.KPn7mPc5mBClȺ 8p=Xp:Ԇ-c] r`7 0ʟYF:Ka)Rb'\^}JՉg8 RהbNVxN&i$^kg 6P<[U۪ A ]OWݎ\p0I-!XGB9N1|"6vmQ飓MyK\9*6&*!B ?w˗Ǡj^E8y+^ FEyCZV ĥc* Fsn9/y& pq7qD.ԺУuSAFTlMԑkTS[S UZӎ++ FÊ6c԰#|P0dV F}f0gд3|n @lMiy=ٮꝍ;NZ1qߕ\PhD1\`I΍A 7,ExCOp,KijAe"pR|g,jAPAz^P &غԠQ,,̶rJX{OnTN7B-X75eIʞU,L۠ͦ+W/gW|P%HFR&5ӆb֟\ C1=K?әݗ`q8,L[:!`VHuB[6i- "qN5\TR$W''n _{rgj:&!'(5&+X1a({B7ɂ& Kl] 2:e\r\^#v-"Q7/JY$Gǂzf}Mb J]Sĕ+ iɣB. WFha4v]z0I "i.fgފpB:Sd aWD/L4Xj p\vp;+~jC]XpQ(1}x2SA[qvAAl mH'+'6V*Gga&|.5A 9@ ߵr)lE{tn1J{vr/Tܾpv|RM N mWqLDo 8և @,rŤɋdg@n6X:!k Q!Gr:,1uXS &nd(s(W%T2,{4+4 ``qgVEt <:Xw#DRi;"؀L#jԥ*'BOy :!|T^TRxTYlmגP8Z*̒*sUVXڥ?: T&c+?c]#D Z#UXwJ*9KA8\*"kI*.km_ù5"=͑wlڰ@0ܨ:`>v+B*ڷaA6dD(_#KVMRe9&y0!^/TF+cvBm) SAj./շ',"a5٥qt rL+ 0`;v8 <}pmdwlrXOPQĂS%)Hв|_\FE e.a(`54 GbYhТ\f&bȣI XXW[=̭*%عL2 ruN|]JtE]l^S\nET)aWS?<_u#{ڰgbA06dD9QmFtt1(; \ڃbi_=z/½v6q ]Ͷu4=i6 l!\J-P162a pê.+wPSbb@}yWGUu*L+!jQjVcB%= U t M܆!B5\A&}\f)+wt=ӂBn'u l>N:[jn{>ꮕvy??4@ аpӪȺ ;ۊu;\YA"esChTĜT >,(GqZdYeEb?"kF͛x-T"id*eAPf¥"([oU GzפXSdPxSbO9;_R)GźQ6 Bl(;6n"Wis'F uvlCA9{[_QU4k[ڌJGAzONnMe&k2RyzM20j\#VBc6Oo^hADyvjo<>R6PP٤޳J. 6-XsKG?׷lf#Ѝ̀C32fg6ͦc ]ԝk!(r>cUޠк VPuT0AvjeK7h<كuTpqe̪ \+ls*녖p-FTa\Fn+! PJ˯˪__{~;VoY}ǻar[w=F{3yvF.o>~ǿ-XDLJCk),]jY=.e(Hc "k($a:BPЉT/BZp21V@(,i)\BT5Ʀ@o jnVT HXlÀ$^'{2FGؓ dTh2TV:ς!H#6X 'eIb;ȕ|>-4;I"L2w +wF}Vd.Y4V=SG$j-'_>r yPǢ.`q]mk8w+gwCUF1hY'/k}<}gO ?gNx~1 2-P794jf4}fh64=i69E;٦mٚem:گگPEAMUUV ѡb̘6U1cVX.P}L! zk]?Ϣ?J =~CAg`jB& f5JjSp@(`$8~¡Z>OT۸﹛v3e榴XFsWYWm,Wl[i(E#P5VKCU^AC֌he?49=ъ?*|N+CEc1'"whGբ%w*y|{*-XaM Lv. f$:YɈ`Ɓꚡ±C/Ȋֹ,{$ыYZX'Q:pr. 犆rW>QTꄬ@ Iu.\1p|!\PZX ̲FCMXxy{plW]ԴFS;ZNJr8ra}\Yq Zr5Z?.ڰ\mq B@O I %*ZZ>8'+)$vj A I1"a*eHqI*pdl^mT|& sAeuo*-`C\_CwzBVR%J)fcKK7ư<;s:)V5Q-?F[jT_su쏕? >w?gjo&44Ӏ9[Qvg'y6ŞFЇ>_~/}˟_\9₊zP8S,Z !Ss׸y-O) ţi0^Tp9_R{̜,PDZ劲2dؒY ;z^[.1|a-X>EkRQF#q[7\pe*VSRl߆oE,aV(-s4Y~Ԓ5G3.J]q#3qrPR&Ll*JV]l g/K5kЮLVhv;^Ѱ\ ]N 9C5q *>l\]wpi^+!D9X\VwEޜm(bs0)c װv0xC撋GUoV Jxm=05Aj"9pFb΋)%%aR* UO2T(TsL݃Hk/,`+ 㡲/h" hEQagT/U1ˊ6gkhk't18q6EsGrYB>8 6abJ)*u5eTj9z<(jBe-?VjTL2r#v3EJv9yy8y 햦,hب˾H׸.H^Y/SYp-2K6 mR^)ڪ@\u)TRYy9bP?tpB ݪ]nج9k @ Q6@ȶzl=//?} ynMSs;˺QF,g6e ̦ٴ }$vU-pgGoO)Ք3"\wTl99}1P3:AeOn#`~xuY]?Ahkqh9& I~\/T:dvox : _[ g!z=T,V}ʦNs&[;&Eudaڠ};k፾]d19 \TKX0;pagGwтnJ px_ ~hghw W \\ ==y:m{y,m j/d p(\d%a-x̋2)Z22|c Z6 KenQ)8#N Js>ُ>Hض'YNlriU#Ad{bd?anKCT/d&QI%.lo v~qy>].aeٟOd2] v#0q/ȗc0TĸܚKD<aX !V*~6m*l]_Czphk 5BtCh7x8F <'=uCX<{OhdW|%i*5Ѐ4RKxIK^S/w}0Q" @_chx?$kJs ElchD<  '>߇̓< h,H4 q;v4=i6"SNgK3?n=^Ŋam'vp-[-c0O ڮg^ϠU@5 XSk.9s!SF _CB,ޯކ=8?mr~nfMnMay Iv`uMC3mhV>g}}ݾuos~mW8t,8QF*eӸ&5JmX`% XJ-V?6ϑ@ly%0O%؈P(jQHN*qETROV nFN,C5@lܾ¡EeCRlY'iTGEwy1r ,۩u bt4.UO钍`W\ -SHVJ!+5QTb˥{ݼ|ggikhwF w]n`ςNy:X%UNJ"PjT``u/ ͅ[M - <`Uo{W4s  yaB'CǞU,2]]|!tq=6ڝ9k)8ᗷ\Aʅ\)v!{>%:+m&厎́!ZR'US^O=HSI QhU3d.aEN,~+:"|fհKS&& @<)9s Ծ\^6][u&k$KAR$,0.˧rI}% `5 Pˤ$O':y+ `HEEQ&jɭXlKYMi;#/z"kjd4T#u}@Uc&RX$R ϷPA=걅{8W"_FuǚbQD[R7/p k Ώμt_if#= s0fݎ v~7̀]2@lM;Uq|OMl* ?ǿsO @XS 9RAj˖dA.$MPAhf>^p~,֋x$*Xho"cR$T DilmXHU ^٥L5eTA]MdknW1}oڟ{;`fg6ͦS(;lxqSPU7~Lv囊?5?jT*.\Anh㧢( gmfe[89fPSq֏%K [.̼- 3Q>+] kUKAmspt]pI WCP0\Tj3I(<ivAu#G={-_ _|2dөvcwpq}V WA5D`me|;ڳ$5E@A 9QJ L'p=)S#vypB%Zgò8B%v)IZT>Z7!cG$еl=tzt6*1Je?^R\I2wT"T3Y}ZNRsU+ U|_ѷUlOZK@a)K&W`,E|G*`װs -d#3 LW+KԀO$NZl˩L܁Dn"3; &K^A K9%@4L@VRiPUҦuZO^b[S0ױ0<܂t`ǖo&X}6K qklG5Yf vmkqj 7_ԧ>-d.JuύtM8@lM;ط]!xfpgޟ 95uGA@:/Y ˲DEOϻ]>b pǐڈK-Tc3:8-_N Bm2*UAJ"[h?"9C{d=''\O~ٟ?&NfߍRlGm:l1s3cpd#af}fCrz,~ijhbԻ$[Ud36*(=PYlĆm,Ez-L"mbJ,T*҆k5Lr &'gsdv/j8yTC'ͣs9ER. 2@7aI e r)  9Gj [*Ke,[RҸ")2KD r* y7+7Tt y-:`AfZ•z*<m45+ptO`DkBebxQR@;LR"о"vP( ,jxppϜ*a=lTn`Eٲ-QA*y;K ,*bG7lTa>TȍR.&)`gN6D8EԄaބ4J5X"ȲrCKYp{|Ϫ%*XȨ*dӗesaGς `(J1_X d $LGTR$f}C( I+Һ0M. r`m2JMc%V!gRE l- x"]RiHQ&(.Qubi杗k,ɥ})6+Egjy*1c7^YHSgy ݲ}*X8sGZflyMVTKIƔl#3*Ъ70!w?a_+g~7?W[t <7 ZwΦtizflû]s70>بn8Uՙ KhZ,Xh?zhu=m7F[6\YE?ym ^*C 3{Pey:_T |oPEtYVo=6p7Q T!8mv@>M:ӄ(e׶Yi\;7Ӿm*xo;C?ş?w"R-H✞jP`*N^;σgyvQeEJ%,B̪!D$p ɮGcѵ)T^RR$%x]5pp_xdWQC !-ȑ-b,PGb+@`Es#Nn(꼟$kh Yak(RHAF+2)%Tm/nDnil]V{^fFiϷ .w?ţjg PsPl㼜q dA9: TliVH^ ^–eE1ZrCeI|rM!fiU#H`i;V XAh*࣮-F]P5J#!OцQiq.&;X8B(ΧBOYt8^5HͬXf5] T3'+}re O;g]P򠊡7(oN,Ar:q1 &V4@L(p}5rcRY(ΫpbUGH a 6Bpfۅ_ٯ\TB ƣ1\ɟ߼ĸUļ&;l&=.6,m:ro7fe얼_U.5c(^4Zh#5}-:݌.%/eÖ1g\ R&N^5<^8 , nϭ>)DHK} OL=HB>8*z' U@`>

E@ۭ K ;fGӌl%3&Lf :s"`*7$L} "SŠ8K4]<^ПVuڰj3hnm3m)X{A%9% m|=!BA]@VmU NCZEٴV4i&;$Q^qnқ#2WltKQGO:- kn{|l|K]ey]El:){xp[hĀ1>P yf9iM!ӕ=ާ?utz:ݎ7AЮj3hC 11?LA1XZ&F by&VѧH{xSqE>Lʵ L׋`q_ gcm-`V ~@)'I0_EET V$ol%4lE9X_X/x-*Δɾ?W`v*9bG= ƒdqrāxo`m`W:P3]wxFxlKe\>-iF3t%,cg\lnx6:)˲!:ѿ[*2v7UU=6vNe\Y7%j:El̓b:okVk莇Pޗ,J! Ot+rjy?U+rv-//& G/xa^Zqخc-͐nS>m?)F{\Rn衇~o}*u}3XR+HX2mOOd,;!i Ja~x/[QQA V 6\{XCag+0@@"jgnʳt Eo5=i[}8!YK}c|&&\r}￷xӱ7}'"p3!ڰ6/1,0By#G8u3[? ՁQM$µ$G!#c#B-Bc H̫N/(Hg`M\̬P)@M/*O,5 X`(迱JH%402Vh?Z<_zqrэ7vA˃|{g$e{ΐ1VH)K5Ibt-/"ۓ~T\b'YJtPnǮ38hfDaka/dz٦m 6lqd -$4Ԓ/r =LwZΞ໏.?GgiYdgBx-o&nB'0=/g{X)}scYzk+/;d=r\eP1FonΦ(}XapGβjW٠yٰ ˲t>7un|٥eܺDzP 8+:@E ΈZbZAp>CӢϤ\C[qkWAB-kZ_/g\Uf!a0̸.?|joqV!u:F1#g@g~wCOC\|X`Nߔ"ۧ|>@qAiL=sEbdI~J 쳑,(8Pt1H䵔YTc?9̞}3!*͆7GrXA@R5y(l^*8;cu49/g&~a 0 +ax=9R8"lɧj5mA ct 9p.Ệ-ò=:{K1#IeM$6gy7jȖW05왪oT{t &zcW&nf5MGbEF놙6O%zbЙ̛N r*BI-VWK18 H}%J`jU'4qRBQ>e$N;EÀ`IAy%Xx16IYG?- k%cmSPb8.pކ{ަz +{BmZFI@ '4d}9glxFRL,n;'MmT’u635YaCɁmpz->mɏ'Zx^@ańH0_1`a%/#m>}w}wgS8:Gg*1z\,4¬rkM`"i^hapavdĀMf:ld"w,;i9<|B-Z38vE^8[  Fo")e(Q@8. fP* |5 $,e*c`$p"kŊqlzӁ&ɶ_*N8<``|z?~^D?f*w4u J@8 `)?`늁؋eYО3=4gj*@Zb}Z%OA$fi 8nŪ ^N{L{N _C kGZ܎ >2$""YB4xl1˸~p[|ǗҳXCE@X*] %I93R,2"fߔ&&\ubEe:1_^kʖĺӮWˠ1o8҂nc575qP@9 x.c9G0-Xoj5&f]i޷ No>7]ub=^Gݘa,t" P$;$ V Lf,H*1FdÄ%4Fm7*<;dƃHaiS!(ѥO %_DB$YVxX#(ou,XΈzs jgrzD 6wۂ*xЧ_^έWI_5 4ffǶTĄxAcFzJaUu+'z ;0=Q@ (ωאAM4ľ`@(!gx}WiFxJI;] FB$ArI7RA1C';撬ʊ>􊗿|웶2L\y,"Kj8Dm?K'x}z, }7zKRǀ.Z@x1ȳmGIGECs3djݪ6ʡxpAej(y{kov/!8!#gDZVȣCʺ5J m8nm h>B( "meY~? ?Ci8̿^}D^/#gjj P~.z˷adٶ{voc;n<_x'`4/b0B ;"e 6`Ce<I}ʵEiT%? R ?.?qhAeIm|}cToX Trcf$=0:m r "%ɓI.@$g^JcرehFM!5@<#)u)D?3 znLY fRd5db~aBϰv~-"; V\ g=@:z;@ˎ^@xe@nxSKʯ(#DAK/+t Ç-{;@l%yh mNUUif <Ƽɮv9ڹ"ʳYy>A?) '36gHdmmVR5A7%H%}l,Mfh>a9GH3Z@ʼnEFb~|ia}t 0ӥ 0 2XxC#؇Vp7rk~K_h(Ym?)wU\֎yPgP.Andl[{'''KYYYyܹs?d~$ņO=h4s*:}V~}op%%X wEsƸ?/Fs7 QA#t}oC nseYHPZ!; LOn}z҄ʼ⹹=ǏMj+˶6nq&$߶=~6w@{?iEx⫃aɭ\(OI qA a ~x0øI(vɐ4. J~DH<ƆS0[W&sK5:WCA, ֦ go ҉{ XTJ@>gpd V,4\$:.@tY5)`\bjkg*Al!2`z"uxW_?ehşM8Γ' .1$7rğ%`#E!!ŗ4)o5yԈn>̾ٙv,b`kk07##Xa!1 mGpp5*mz_x3]ϭrku+wKyGcaG,cFx٭Y}+g7<2Ϋ 5<_$I![yC,KD3*[%̋/<׻6d[!fC%7‹/61Чg 1EQϥ9~wO~ו9drA ep=2̄[\Y@VK?[E9d$}fffqAmᘦvИ;]:n!߶ hn{-z!%mo}~w[?d Re ݶ}0Sccg,RmB32uWd!G^!1:4`93~,q%|A={DP/J2`/'+KOe w/-c,w oAD?bV @`V8gM>X(gdX Ou $" 1*8(llSca%^n|U ]oglw c(A ڳ"b0W%&0?x/MgPEz1KnC5X-3xfQs5<t륶qfAFr/w b L*~k "βYI˼,"S%X9VҳII' cFid% %걂ag `@Rʑ؋'I%IY(D6A0<`uză~@:5#3pmۃc;d0J5uM3^Z- z?D`*2> n3#8IM`MF׺9 1gdY8<:KG&8V0~{[`Rm@HI2 1Ȫ^T։k؇^`q @M361kkwXr/w]?{no}׋]292^2z8صʲYv N;z\=(DPY1("v,C4l$ۆIN,Y A!V }.NZQ&Ϝ[ێ,I[ v}.PN#(g혦,3.#,<(FeFo @B&!^u F$2tǢ[߫rc5q7r)=^]u^n lGPRb'zDpc1,{?[ggnb /`i`D'1'[{x(E#Pd|f y } }Q*y / i$'d~ CZY$}M* kp涷3=F T t8K^3@y#b< }DzQ4Ay*H)P{( s@C~x7y #ES %PWA ?9x.*ogUGvJd "  D;+((yccTc/* FO.LCݑ x5 ǎ| &kb/π$T6\S"0U -edEZ5%@X`b6Q`Fu${ORvyIHd2۽\$Skwꄙ X!fIV I`NB+ĴPrkHֆ'N ׯr"(L+/|4X\5tnؒ裳oh6yın έTnvli''`QJH5BAZz!%䑌^Er)1mUdF-%^11 W DHQq];t " ٗb`uОosbi-}z/?#ګ|sum?y]?.Yt ~E/m٭ Nf6g+TWOXxfC\Y/ȢACum"wG03d7?9r0EܷU|%E@0 ‡!e&!6i|~0'rЅ<& NLB/ i}wArXEf/e7?uccnǼn|KX.Ycnd?,kUd/ŒpcCAydtZl$f(v$aÌCq9bXFyF0Q{b)lnEлZ*O}_!tvlʉdteMQ H b& NIQj\ I A!S!;վ>ۇ'_=Ckᄍ]ݦ##$=Z hJ>16"+܇gmZ:!% :Ad+/,p!d$ Oa_2.5?9YU&kdHVa~VPG bhz3m2b{EAeF($BB+\h4ed4%tQ8׆7-7y^',T)a|I,cQdA&mpbSws <?Vst^"soॅ~B鎁]N-xܟm;-r>s__3&TmG6+幞ym]̾ellb,7^bg=1AJ7x.?v{DoAp(Jg,(6 򅅅 w:wIC1 oC X6Ya^:5x#Cl!>:;"y[ `BPh߆X=5C~u80E[dBrƕ*6fln<ooC\wuSǎ^;IN=73QV L }NV8E!4fhdJW t`0 ɉ SG8X0bm,cH"tG <˫ M$! FrRL_ry R:W&'6a2A@->>M!Naz%j}òi\YbssCx= i7_;Smfc(4Q;$eB;gNJYxf4/]ljcCz_x4fVINАz҂{ZyS-n{$Ft2hnl9,0b ARkH ')%#kO7 h:dvBQJ\vľ&Hjf$c jt?5T gY0Obʺb@ߛ%p@?\c:?SL SuaMc$/ ck8{zN/dgG>SkvO]ׅ=3-xEx˙neE4YASna J{^yQӊG֨䡣@oUm8+FQ$O))w̌Nb`92I9"G Ðu2@lv|v(i+uQV4A& M;yAZ0(1M)EJ7` ,5;+5TyR+UeGC4 ;C -\;C qn82X%1>rl-:V $@9~\%^L _@#Ec@0z4)61u/2 W@ 掹PZ8yz{g\;bM uݖ` |U^”X O<IM%H0;4Qi5̲Iv2q|bJ>=B6q?W6^* O@Ù@%`;!OO-Bcu&E:UU5}ͅ |8{nt_?2r۝JyoD;[Q1h\2znY ﷓X<1e+~3zesֈS|[e( =zM2iZ ľ7Ua&ܿ "y) Gx֍!O,eY6B<2$ȣR[^5zUUM&B0 Sh<><]ݨ2x۶zMVK|ۥ͖n[/w|fc;}^msۿO~~}b|%cLA6&$ܮS\G"`7"A2+'iҝGhB>cPx܃4'xAZb'vׁ͓Z ҟI*Y oKq f>/3##}?,ic#5PH F;Lʋ?;~N P>KD+\ .y(HrܼH\X?+6 ؇ne#J\!L5ހp:r%mKB*^Cj/D/ķ & ES{>鹅9r =S{A kpc}*< Gvl`4PGG/^ PY,{\}7kSn~Qe%#w^{0wcSCd bT0F0ub!`kMbD $a>N&:А c1$o(F-4ж$ ~J @ ]jssbu㘆R5EFDn_X:?xx{-WU+W];wۛZ{Ǟx 0`\2^v2znXvȳSڶLNxv=賲Vdi܃X2z+02YQ("F+EFRw1'MR2/Ƴ'h10=vK`US%00k,kb ~5 ;XBp y;:BVI>*ϛӮ?ԧ>u ]N1x|ϼn7bu um_XZ i2pgO=_I\nA B#Q䜆fGkylp䅃3 `αDæd@t"sH9끀Ӝ%nGj4{(]2u|@˃U~r&?Ϩ)ɗH' 0\P@,ixT3\tbem{xʃ<@z}צxNe=܁{$+C!}l˚Љja*0Do!FruPMX"zH#&,qUGKMF8y[pZ1oGKlzWїCS &V#ʗqP{R<=D`Uj0ϔä$gWO5D]bųT`i1T'Vź:kQQ-d1p R eQ_$?"Kӌhb_fpJs{q[0L"HL%2HJ'6]ېJBNe<_e'HD7qb |x ,bϠӪ}9y$I,;xHygaNGpuW]V$pfr}$ׅV\wܷc HJM'> Ɏy乐ӔOcd 4l- 6F]Kʥ#{`jj%Bu_f՛N9pT^nv(1|]W疗WN////x҉ok*/o m{</;j=e^e؉,(ӶS lJ}JܠTe ccfmjUUe,o&^8=%{ǫ@|> @"f>*@߬ ޓ+4 1>@[T֜ށV@x=9yy!Tj "d\IVӹwwO}S& 5nX>.}P'f l絤\@]+;~uW=#NR7~vB`nJ g*D%ę-fGD%<>5ϩ^*'TU͠3{+; edN} ,XZ7x4\`ƨraAd~k%[\>b oJoC>5*3a[hc/4ыxD<Ɏnkg0wH|3F8bMHLȺ)b]wsUUn\ڧdslm# dn֓r)@f;;M$vf䳙EsS?:}?w=HlX%]3.(0(H3dA`^v3|[pojI$gvZd,/Wpfz` &:ڗg_Ù(,%2UKC$0 '&}X Af*_N՞A|-px_RCD:K@D` '633 e,%Ԝ{/ F |߾>@T䔯Bz9*" d&ؿ⻎~jOm~]ש0~fTquА c7R sFV"ˁy 9Fc<d`I_#6-T:9XRT ɶ$&=T ڋQ*,bo1IK2Ki2<1 VP7OmmMO7/!4ΎxDTȾS&^lMm&#fZ0Mxrp?y~zg10yc13^v2y^~ɋg!T#>!ʫ&eGcace۶dvd6|vn1[m3 rg;.lWG~wznV+tqWPloMy?KiMnƒ9~B1 txL}@ř/M.!wH@,?Sé &al$8`a*X\  6kGt2|X8ń I9(+V:qUg:o2x ^:88߂[oT`(ϲ⚵}p:V%8b[O{$Y9hbNZH^ڐDc933E2g1` (,)T7 pA}^_~ރ e8dW2Z2bd乢"CM}}[-t`_XZk $g1:|Ub( ?f BHO"V2G[cR %P61^Ƌ(9Ϟ@%eTSAM6zY[T980Jl1e_ D*SQKztM@8R\>8p?6w}sPMAץyGwCZ0 Hbtk7MJuF% 0l1#YJ6.vF$5p(զlQc10GSz(?Lkad,QL>Q^ɻ &&&G6b!M>xUmukn+gy׳x/n`v/چ}c~ZM|<}[i{!K%g j"v<~.n lAY~Y F0aEPT+,<4V$=fʎ X"i ڮoqc#.A ܣAdWK8\Kj˺y6pK`A:_hM~c=e p!ȵPĵ T%[ L?p$kLtM}iIxO>ɏCK䮲B,zK/,\@Ȯ lHޒc Qjʗٔ/ڇ"ӸpT~N(k`nx+X\z>mX~qzhry/NIi1qymÀ~!5<աj"0A"jv$2yAOCcb[ə㎏mK}8ML%dN,Ϻ:+x-c{l1~fʹf2oS.n#fJ;a5q;Mdޜ}E&܄A,)) ϠeҽgL^AF2(VFVɭ%TQj^.RǚhN)x~!첁'[塂A]7M5|QZm1>g]]3pž ^<3Oꂪ0p40:j ( dza(@MB@|Jx G.zuxݕ05yz\ ڤHB"bVqbBi>ƾ-Na#Cl >Pчu`l Twϟ)Ooku"Kjt"eblC.O$VqeІ nϟw10SP޻ç_}3~|s'yYQ?kHVi$X= qQIO 1H0$-ha6]g M~a4qzfn Nm&;;>^w͒mic2^.a=e-cgo%g3=; lZ} sرVO(UUt/h ?rMxWLg roTUN єm2pj!dHM OEi=!Ar|՞M)bx? bLӇd9$hMDb{ay 8`N eyn[OϥCM;s_ jlf#mro?o_}UAC6&_PgۣG xpY_PF?,Õ݁cH* ʄtIGI#̀14"A!wW<| g2݁Jw^7]Ӄ{k[!f}wZa<胿gJ8yv+kV$+#HLBAY_w*16=d& T8d^(zOuG\ƃ<^0eE${A"#Hϑ$EElf$'bpV4tbCva<m3bi ߦErnFlFJ\_5rm`uh`P dƼa@mm'H &p<ЍLl{I&Z}K s2J2 2V}Sc L RPikֶE0:*J?9ZO=6%}#oGM:9Q,$ 8E$SH45Nsx.<97W^UV#m8<@{q|gp(@”)"h9݃vۼVXoXѤ*znlQ>/)&Eh9N`;,g[L3_ؘUsiIe-cg촁Em;xv N~$D<5v|G[1:WYdvtۥ{.6|ⓟGg?ǟ "~բ<37MY;d4)FBvBe{b_x[IkP/7A°ނ ZxaD .( ZȨܦgs3 G0Si?HE@Y2;S샣:[,amPW, K<2I5fAV<0XN,"T-,X18<߆:gY^<*\>,d @q7%ߛ2{gi$fP/VNL2pC~/x># ~ɴqTY?|K %0AyM^`j=8(3԰ZZz$S&%' uaFvc,Wf)ׂ!L0Pd ?H6L Li4%"WH/.,4=^e-<)(f?ӳT=}*x&a\V`QĸtBJj##lEfG>ф =.|i -n~h8]Q'_La66X!)?E6;oo~:Vl1dd,cFxq# ݓFBp{GCv}.}3gt?p]m\e]13^v ȳ^< Tg'|mhFY";(rDx{ aܶ@)t*;4\*CMocUU-<^kk>?1g,6^vt۫bjog~>ǟ?}SW{OGGe 3u(+^ *v[y*3}z u{N ,<ʦybDvދE Tؤ4ADUdP嵀'P2J97VБkH+~~T NJLה)nM/wڵs P{ɟZE K%Krl !5%gI$|f Ǒ@W$4 $ KQ*;1`$.[a00E0Ir e-13*ah}&T[Q*J?OmtlfIA+0NGz3 =OKn9}征I, i*x%ٺĘͲGΓdW.d~ Ĩ:<ܱv!2Ǔt A7,${:^^6\3ػ3_/~180^xye 􌗝<٩,ˁ68M=Xɇwta¿(N YdP,e !I3/F>$h0;V[(/ZL+$ ƌ~{:P;Yv)eK%}jj$9ʲ|. ݈w,ݶuX[>{m;ڌ}3o?w]/0(A_~=8@f#AqPg +[)T[!880& h \ ܉<vKe63;Ky$EA,)ow=#!fy`X#n8{f,Uү 1.+KRxƳ!/g4(a+;at}$jMʉkq/b̤1 QT*r6@:1 $@(fIdt&&,_grTj.EOk|$wI[%W(oXBvlABz[dp%DvDb ɟbDY~'_I2`":'5$_Q#^%R+044@'^%@J̳|>%ژq7pS}8o<փn;4f 9~XjZP $3w %4Ѹ ]Ȁt̍W5'7.-~\,jU/1Fg*(1EzRbb&Ij |m'zMK&5IQ5_]`XlSn6َRm3[eMOO`\y]H `jWn!Cƥ[p ZC ; :@;C@%X.˲PZ=^mb YbI %,/np F A+b:]uh6fcՇݠ$V;;Ib[Ȳlph|{U{3?B{@J`bB\Z-* l $YNA%~FVl}bᔍpPtQ\fxd)!n_^(GOיha :؆_ՃvKGNG"1aցFj0 mG kT%Y)*1y&&FN?u (8b-XY=[K!e-M d,A ׄmF$^rJ,0$ļ MFdr;0O$0E@@Nhj2WA\_o?tƘ|5@u\e2z8H9 f<6شi++}kRx @q=`/}b, ڊri1[{(k(<&fcc#& /%`Œ,{w H=݈ǭ<ӇFqǙ;2ߜ=_ PUPMyƊqA G0L#j_A J8$9ʲf@ld"P׋5`nۧbQR~')"`SXuXtsPL(yu;A [\)ae ny$?Sh "d ϒPaj$myfH:ת&G} #0K xFR,[FV{0E I-ڰ>lxd95 \\h4/ V-'H3|Vs|=xL$OMC nY% J` E3%HڎISM('G,XOucU*e#+"Dsɴl ЫҁFx =^Y~۹ {j7]Ӄ~}\ j\ jj|E7[cv~"*0:*,LyeGy'es^m*Ehaq=c0C6i{1ox;v{Ka ^0n"@lBKI[r2s2*Z}W'{߹dɓ'OoF >5[>X:wzt6X* ˩0shR/_h R{T,*1o]6;7̪FaTBPBه@@tl(=/C{fBc`;UӰԤ1iR  vdLv9nsss/OݪIC4ƄsX9QL^1tLUUg4[bH@zGs$ J1P$ZߊoW|>Sp]029w@r;N 2u_;P|޷oMy]5ɶ -lqKM|s#fJ]o'39"6y$yjr? q!"F2 (1, xm~ųpNH@:CY? 0x\jk@ēeP9)3/ $Gƕf&a@__oP"3=+,=&>4$odU$X&Q|-$4-lj2}a+_/#||3*ȉ Hcs/=":X8j|81N[>s#R@8jҖiҤ۽+'[biN}ffy<~s @jN?p8)%ʪ4ȯ9>V n n,ykk!0ec35/%ϐ.8?Aȯ=DJnggg_<55u E>Q!0e-6loƭn7i̍K6QP͸nch㏽hYg}?oջzO6Cl 1lXdKd}S, xX)p߅<q|Z!.jǏUK+*'k *pl *XCFi2v(]D&SI:xOC 3SRVlx;TUeDuB?X`X6L7 ' ('b3_.9p{!>v3K=B(]jvا)n7RdZsy1CRw'q :/P*L^*C km>xo0 h ]d>  dd2DAh+KpDd3Yb[KPL]PViP 6!q!&C.T5&H>=j40=N/x4⽫Lgvv[em\&ꌘi$߸DRQ:ֆ] +F4>ʟ8@m֙7Q}]YDyX2/N<Īq#,;al&_\/QVsu!~:ȃq[Ҙey2tG+g OO3=@ԤIWjj3gulTk佡^3 Y*Òk5 ,т@jfS1t ˢA1̸4 ts H ?5m3\/fP馧oj} تDDrNAޠ?g&}A6.[m@%v!m|F- ٮlIäBښ{;x|eՑf;*x\ V9XH"n},K`"< mdAD-5걒1|^ 'Ϣ3LM܀ 33[I vlu;ȶA OeY 4*KMRC.1ς(T2x @߿]Bp#PS ǁG EW7\( sD& p 9X$UbUyx}Ba0X(C )lV$l wM*KrlDo𵊲m*n¶H5y}B vYbXW-,.;'VN.uDec_`kW&dD Q"b Qے=}- C읎 6٦T]UIR(ۥ\))G3$yu_\'DM E3YB=dl2A/jEO\&0z8" '5 w7*#QwWZO]Xtfז>p*2dD ke^RPT,u55žbV\y.Y+WkbF\^.&Y~ϡ}S0me;Ǿ.vO,رc?`^Ȋ5IMڶzԤҷo v.jG?7eg%"XL"Oao:K#5aKl5`e\[T` $l7I1uēq&Rob2|pM<M>6.QѪڲtbbTIt9\O\:>h&u>#<_ۋ hcpKhoo榋$0 w­כWd5I;55@O&s#Ag:ٌm 肶9r@˶!<=d _&BAG@,%"tN2ۃ$<'Ȳɂ :gX&.|G@I|{ FIkSJD\ns}~OWU^7ܐ72H$JXCo5w0< /8YMи ynyn{wx~?l`όXdn3f2 e4ox̓+?'Xܔ`O( 9Emy"2p|-y~Z<{ZErc\B69/!PmY]NP(1ǁQQTֿxJ]g [%4üg~3 V)HNodU]D)0:W e,q⛣2'u&a#Ie Y҂+ĩQ),:v a2D;6J%vHCod,%q]Y鵇K\ w|=urG05/T!B'L$ZQm$_e"I,y'IQFFdRȽ9/_bى==/ecUUnj1,f8`"51t"8eYFk}]Q>ó@ ,a`ߜ:`MX[eD>S(q^ =yvX~{o [ػGF`ʁ/H.b:'yuEQ8<1x1=Mhblt۸b$3$ߟqOZ)h|>IG&C'_ xr5#$JMJMռ,rzPj7Ug( {|EC@.*#Fe@*I~?lj?U,2XT쒷 )TǫFuXi,U60|DN)`V݃@M`'!`ua;p,0=yyǔZEVBB ,'F;^b0;P+[6؃)p/a0+zF3Жq˨^C䱒DzǒW.c'8Qn _~kpbiP 8sL2Py`S x2] %m R{Mi;Z^+O:{EN"KA"zaDq qw!z@f!ZzYıp`ζUqtbuZAd{#UtؓTe5feq%8Nj@5?;ih +`m ~xmv. JSnѓ|с|l,"otÓ3,Nta6e2_U[.ao+{ZoO;qk8x;WWS= Ѥ&5iۧiR6.SAq:_쾓3D2|~*0kp`&ڊAN@2kkv[WUt:1S 䗢(Jos"& K 𣹜"m:,f\=c[XnZG1-p|LĹ t2)y@;{mozӛܤ1j\8ڎodr[m|6,|O gǹ\.khy;}[ߺ?oڻ ztiՁK_sD\ i7H}chySX1fI54FYAa24P3xϬⲁ}{J',B౴ ĹFAAZ^3 eZY\EE |۴; Pܚ :-'z~?펔A̝QFװ@y &Y\Ec)G̛E2b DN}hL w`uR~BLd!"$\--WհT:$o Vګ૏ g_' ]B+D-[H/b+o#$(xfxţ1ߤA|.˽岤MU$97k~3(X=0}I.MwKaGva~Slv=/,H8 嬤G'T\mΩ=l[6t5O < XJ,϶½"B prG*0#"2ÑHLxOA?G#R="AAf *aaӉ+o`7&uEIWTj&M ]m@Kg:@1c9~nnZ7ˮd0䑓DN7rhڲ, o[Q  08#xi)IF5fȹpff:tp`9LN/;T.疃@ Q_=Œ̔RJ%$( (IYƸhNlnMWm[){6v$!+l.Črr*3WF6uELh7d%%RTK^EB_gNS%^R;0΍<&._զ|^];;~{0ɵAkȀ&,D: 'Wz.#X S0i(1l`&Z  qke \ON棓ÞɓIbœӊ:N1Xr!1!)^K+}cܓ1HO#Lv'#dL5n2{^fp?I(_] Ф&5JH Ӥ+%4r33IRm;KNzUy'g10Ă6 b(znoY=OWUU!j}_XB`RYN4|׊ZbTeW~xvnZ,Zo)-2va&:!aw/r|RlNϚ&ۀ|tŴFF,֝ t>׼wkګobݬ-Mܣl W8Wɞ{cȇVDX~56rqn}2AonDŽ{4!  `( krlPkhl l#PH KBj -Z ;V!QYslv/z'|MB2_@ d6e0 %0B),ܧ2Ħ  شP@!̂}IOhBBfnYOd#^NT*nEyee(-9qx /<څG>gijCXQOd\4w47.ԭ#~(?`5qJ$fm.r8eDGp2ER-M r\")@rAZbL@Q\(1<>]Շnhܳ9[%Qg@ aY^5ʑj3 Vx|tZc_E6go f^F1e0q]XmطH߫IP7|OemXMjR=Mj+ߝI Ljn[[[td ç3Pa|pWO/aVuFc̳UU+W3/f ?P~ [H ÌZH.=;;v ls\?)e+ 3$%9gmYA,+WacV}=Ϳ[4m۹q$0nKlOu+z6yN<`,|#G_~;+m)u.ȹYw>zU+ ڊ%(j+۳k"]:VXd:jgmp|pQ,"{hQȡW+U2tvB2pAe1AuG!wpoW5h:A& 4mRݓIz`(wW2#)rdŁ[D蝊2J$C8ENDLE,0l 9$̰GN aPũZ#q*h"Rl?߂7+xv YG%.V8@!B X"{<p71&iXsA{W$ Iwc$X=x*w1_69CB`KTƴr {v ]Ts.E+GV]-uubbP wqzQ"U#a;c_q-5F_yEI,Eb(\(* |6]joq|qoZߖF 3G@dM(H333;{(Ǭ1g~S~t`+ S'}RQ~ߚ,=G+vk85msSJVK's1vf&y1mX?O=w+}k!?@ߌA.AMmĕ<B$โg ` 􊃅Y.! 0')ͺ^V 2Md1qM[M6'C 3o$m 5tk4LҟkX [xh`B@ϿŚk5pThpjNCh;jso0yD2 ^tDrJUc`MZd亹,K&AO2?}2$^Ӆo<'f|Y {t 'vb/%s  E),(> xTX6"6OO]wlTr92fS,d|y=EnoRyƨ4i{n$Iw8\ tdOu('ny$\Aݿ( [A\ QSR]_ַs fZG#w|↙YjWa\IM~hsxlscض,sI~=.Y7T<θOŠwȋYcW{#3A[}g}fߤ&5JJ Ӥ&ȵyy4 x3dof+nC׏NOO!P ]Αlcȉ_" 6dǘ`hj}Qm?]Xެt3 !@4A-V1Rt:3lZv(FY<1U_$:9ľ;Υo,9K3|9@㢚qyImRng@ 9n#EngVg'ێ=}ؿ  !Nt^ͣb<9 +EC~VXl4`>T0$Urzq`evEؗϋQ&# ЂW\_WcӶX(aAOEj~Sp`o;#lso4L_O|7IMR4ijeoȳUNNيusn{;+$)R PdyOI6-e ! ~,(Wt}݊A߆^7CЏAE4vd !(tS\_|?`yA0JSiHf~7yvi{2 >mݶ |q̘: <Ǎf\.(Yۂm;`o7i7ϖr-Ͽ+}|pMawU zsdRXeߪ^f0[-XXp MOwyK;vF[` AG⺎be03G֡!J 5II k3?`f#Z[!oɪ/NYApvU %ep#@1Y=/J*82%r[y@%26V!s$'AIn)DJ'C!GP 9 ! T-(LK2["ΝIh m߉_|m?^ZLC_wA6 ̀Q(Y\Jd JNcej5f\eF&E֊$ %oDau$D96a8ړ'N7Ou:/!pIe,d'XbS Q߹:(1\`X/T+K"4b$ &cЗYu}lJt=NiHwx4Mr tua1a2"fmG=FC4n||M}i#*?r*D$ $䚒H\-3=%=ui8Nm>䙹MIMjd5vnԖĪ6-s_[XXa"WE6c23qoɶ WI|z Cf? fCϏ_|}{~_ŀa ?-}BpA)L`p]fgBRnZo%3l%{rA,@Ox#wգ`xYEsy:NjBI{\im&7blsf@~~ 7.ۂ௃iRr#RЖ |ަAoY*]{}2`yqqqiuu?N>{ի^u/?qZNFI7@0he\1gJP*(_d rVAMOg}0׶:㯕l IE"V/ىnkV s\pKwӳ@o]=fZ 'MS0s'gN:62"<;ϝZ֕VF|];kmFO# 5 m@9~D.'@2o1q[:5K X#|Qddd[mDŽb!ڱN%`0e,[P{o_-qPEA:3&Dz7njs?Ǝzޟ CV@+J!!eS#W'{\^UU+kkk^n[rt EmrWdF563o~$y.z_ u]Woy[~]zwfX&f h7NQPAȄoǫ#8>CO{>gUO|zKo7'w,&2S  `hr}04كSY*#䂘",PYfW"!&<BT;GĩGKʷG `)*+iB8G  rɏE*^\ +9YN &WVjhm* c+&srM*gHNYEѐioX.`:T݋){&0fS2Oiչsr#䬢*!%}Te><`QMxDav 6Rߏh+dyH6\?%ά#ɿ'ޓѧ+\ygULD9Kchbʉɿ*>PG1.g m:f%YA}5] wqﯟC?8u&΅Ԥ&5iRiuzkoR}y6dz-ߗ>7tM, 3_j?'`ɘ<*{yS\5xsP 85md}_s.1#9!dOu1(okkka~s3{Õ׼U'=|?;vlyV|#g>.d tGTnd S-f("GPjB8* !s][ F2sڭƙ4D6 hzr pgyw|r~?hX w\+RU1cymRova3 9v$=qC6x{[oڟ|^#sm?fáê*Y=e==')y?^٫n?|GW͗PYU[ChQ(iĞ -2edDeX-9t|mQ1U#=iQ`7N1=.ܴYvpd%6%mb(*[efu(%HRrx&.D |2v10A dlSї.bYe^BV*IC%Bb8:TlK,rg1 a~ {Jwၯ#ӧ-ۇ - 8)0n#HFH/\x/d-aOl<~Ʀphcy]XN+B.X 4xnVQ8A T)x2$ߥt f7 lWV{󌁅m`;4&d\ݟe2gU6:>2-QP0:DP˫4 4b a}zpz /2qeX 31#2.#5% FdSj+ $dE dNpe2`eHe)t/Ll.yn]BAOhy3~T}ls&5I$5@O"+̍WVv6옩[de' h&Eʝ@:Pa&|"K( e!Y>%i_~SZ-rjuu~AmO[)O>?0.9 K(, 'vrne Ys.*]_=~\fj;NmR=wRjjL\5'zjm%O]v ~n{3Ϯ"_o $i ƱQk8P8(Pz?a`q^=X\4 |⠹C~I $0X(jIA=n߭cCBpXPVA%.`~ȇ")&S4a'lXO2!bX-R2xV1&5ĠHI-chPgrZ1f}?3Dz&bWudzT] 3 uJG6"FX7oalX6Dž=.y$IԤ̤ q2{.b0B iyw6 / KdDbOʉ8upV@ξol&`ERW\fIr1ksQ,mSyiy+m Jh6v(ȧ`&GE*aXFfm[d!XUD wAcco8, chwLbU IE(goZ=]/mBԤ&m=Mjx[}<(Sr_1']zEƶAPxX1lz y|.'as"Vncd1tow@KqNg0 8/;[0>  l<Eozv6pAvǿo#͞FXA~әoO}6,TAa\l֦o_akO|۸ڎ&Usy\~o~/y=yn3Sz0D 2X+|G "klքux;Grp:CZEp? ЪS,.;OsaO!,Yh͑Ui}X~z:=K>7P>GU4cEyeLsaz)e,͉pW *5 N*"oKTBx&Rz.P8?{s,'ICVw '#9;KH5E\c2ncg!6rs%esnDzm1/cwܟѐ  }R ]OE,6@R Kk?eᆫptHjC Ua90+U56zl}ɼj ̜K^Bn*qy~9* x\&\:A/nmVK  6)ˠEL.&39&ׄW$Z9o`Ј苃fވ0c45,& jAvJSXC10;ȲԜjm+ySf&Ns|"H\K[GW,_=|Y__Ɓd$m,YBmB[omۚ/9|sϻ?ַeCv!4-CAY>؋/ԇ8D͋)/prpK !s@0`l[>e`tՙA ʏGCe,@@ S9@'} cGdA۔Fks sIl1F̰v徃g}}y] f]\S`"%(̚QҎum 켕Cxk%p`o:-([f ޒ?z,B9}]9?{ߧ9sI/kiٰAKIae6;SAK ֪g<;yK l(}EQLʋ kkkg߫~"_MdMdc@&BY6A<Pc` ȝ$O&)[fxf?;9+rmb A#~=r7 ;%d3Q_l"d >|^~mvh s;1$$ FE`9S[0KE ?"Ҝh$0Ƀ"y<>k\TKMPw7#*۠D9.J xYx>: h$ij2Nw/ 3u."k0jf@OqTWuYM6 T7[: rb?B92/:]FYTsx@}Le^7 ᚠWVɞPҩ. ddȰP ˟.jw2WɻPdv%$ϖ3r)sCp}ңkpz go Dmg]4=JYʰ3wcsٸ##t_QN1i^| Wn? $Rh3u!pM,k+緙e^>Ϸ|y=弧@a4i3Sۺf3&10 ̜cxͿOTTU5`>83/27a 2r=1o3,7ر"%ƁeS@}2c!0 Bرbz YAt!IM +ˌG |G&?72`keܶ<.=m&g#}l6Rs&ͳs7ۅq7}ϻwy޲z z`hs@[h)W#`MV@ R94IRf3PK:d9,qtm`ITӂOa8FRFrBY,7XfDOA>20D$OlqI'F#X"&h@FHX둘IL@)/Ga./> y^ #OC&(>H a:^ ]DF ۚ@dGU8ܻx /Ie!q ^M|Z%T.{&Ӥ1l1X0/W-<}Շ:p.MQ#0sY;WNFMRΙ[F5LΑҽX>PoUg$ >IM|Inc f; `09d>5"&Ld Z>Y2A}tPʍg2p[xF"G ˱(G0zлGq,c0 6J@^?A}r6_ hB _333G~7~63]@&5˕|Lߞq^`oڶ]򼜲TB@q;Ї Sy_|p7 T()DeI aɠ\y֐t<dA$Zjgބ"=G_ s8LrSaEMaaz *j84_3v"_R$}C0X 4'rl8 .Awy#x|}eI6į9nu"Ղ֍dBb.NFAVcw$ Vk7< 2OJC@kNU BpA]kbbuN9s`qqЛPTbSeGyX)]]G7fP&Z,6قX ,їkZN(s]8Ѕ0V_ ]$+q;rʓY,9H9Oٓ<1/6u[^{ = 9b} o& Ho 4cU+./\Md|E&R5)A*ÏSfnGuP0  OkO}ed>6a>`kRv~ a^5:̥Kto͐lHgKTPgm_ƒ?ʵ|?RU {||_y a%HeeIJf.d0 !puD,ò,}좨"ȃ3԰< Q 8dW20X6 P *;ir6s@Mm,","jzz߱׾7Y&&oXsI;M>v&-rm]FO2gU}޽zv."ݢ@&8G)- Rk@nEpd<WqH.rjO+_0R[).aT"Hи?A?4fBzqyRpݡZ392c* AJ̀S~ɂ!HX2βVE_dIuysFRά8ֹs;|F@s  ˳Qu]%b4bI%10gkpzu_:tO^rktav{-!,' $vcm /Y>Õ+u&'M~)˸~4m;(5&ԴWoR㿳3~W>c_ebga(y  6\ddLw7 ,f7$ Ba !ˡ~42 $iOǴ)ˠE x{8fR!` ˿B"-`PY>W/r[b^+|Γ>nNH@#7a6˕N v\d$<~35i퓛9o:s#~}/y_oDoC l Š5WVNPNnaG/1!y:6t2JI`PHIʁ!x^'2?êe?#zU} X-0UjR'Gw _La' r^.yH0>aLẻYA(StkAmAǰWPAg:c Ee,,uRz(A5MW#j:1CPphNH(iX1]%EmhWb(fmT={%mDoT.*#ֈ$0tb|ha|@b7>}j@Ra2"*7L\?uj%H7vZWmcPD ˆ"?s'V7]'dpL DE EKWA0~mVvDlr["Qfx A=ݒ4IQI DZ7>9؁]}\WD:.rK0s7Ggyf2}UԴo6!5@O4ɸ ȳn)V6cƲm6{^/g+LxS9h>ۧ< lY6f [+1&ۣ *a0p#u7?˚U9 !1,Y'+@ za~~pIlb6DYW;֑mTHI5;;ҏ oxiL{pno~YQ$ld7c^s\ڬ69F!vIښ>to~3}=ާ鱺{P6 2?~ sCA.6] *J!¨!G1pdU>I`p`u O8R@YI`EK7Dz ).(1 Q1J1"%X\ۏ%\}fdmY,`*1գdjN1RWr16qWe4H %;ԆugV\hKͩ5s .>)U,2 e'cL~F̋&;Gi1!Dןa+R{`ao ζ|?*'0~؏GdFrA^߼{S{J;(#o*g1ck 2@Iۑy$&,aYDOUxѵ2t3k Npx_; \F,Uֆ1CMLwRS'"fJ#ݿoPW{~VTj{+Z 0y0Yy`בW9+ 5A|PV4iǥiN,mey[s%<<ƕ% 훝5=KAK`D!,Cq M4/ ,{G'X'RkS0h̙A7}QDCşPŷf>8?$АY9p-s%C t[sP! zOOdHaY&^`izzz44ϕ -nIbl6z;ό+qkێo9'z]a˗.IB t9y&zX вOy~h'^,izӚa"Rǒ6 CR+\ݧ8e^%*0nPgU1d`Ǝ ,gEoUQ䚳3zT&eDͲO$H1"MAn ,k0K}0Pz_a~{쌅sN؟d H 8dDL)dmJ%BAJ̩6HpB_ԉuā#-`~)0`8GJ$ *go %J 2kr&Yc D.!/,G yxԘ*YhH2H:1iE8]x >$g+سق^j*Ibc1Gj#/*zD< d:=oxšAR%,=uLu Fa!eL( i33(  )YD#kPs& w[Pa͘q$߸>{f/阣GЫ_?׏|%,̑_ C"!X) fhgL}JU{RU+ !mǀ= .@B%PZ]&95!u`.TĤpnOj|]ՓW5t[#Orz_<'7Ctߖ|vF]Ϸ|GOFOƞ~n; 乐29PFV.omsgΜW_cv֮ˡEidž)A [ C)c f9pGs(Ն"\0`FL߱~e*_ܐ dR` @["'?[,΃ «߆2) ė'ux҆u&c_GyuՄWZJv'ٳm#<5ϘdeʜFKJO>y~?qjޒ(\A RkZ'Ɔf/TpBjw%3 ;Af-S0E-4mksa E=(cpU3XbJL%6.S55lᰟ qQ)j"C#SJ62|dq?%6<\N䉁~YI2y挰Jģʐ7yC #Zӫǰ?o*xA$Rn Ԇ k2e2zgf ~ @`UAlJaf ?Qb0T5td@Α]"yIөZR*II_T*fDzTa4SI12/Af~2[d}=.AX}@.}a9G[^WQRvwS{.Ư$e(L1dg|^4^$7.aV`nɸRY XoE?6tOUpD=_;vln2` ʀm^F֤I;)m&gxl[L*b#/5=3<v0RWgX) I"܊eJi18,&(6Do^YjK!B>(̔ `ϱgOſkܐϯ_~M!kee/qDƍ_>Uz );M0{)u=φxa4ӭzP@s]IXD2ǙǸo'bQccWyoo -19 { Sf( ې W y`Y0c)>3U:+ άXW{v/: 2PD)47 P1. LkUj:ya5F$j(jd؉%5,22@A|fp@ȃ~+o FΛw_>_O?yOW?9Phrģc&"&0\aƋe/ "=S"ۀNQʋlG\l?*)p*ɗg;և2pt?ߡa9'9ʐDGI JX% ip-CDJ%l8,rEC?*qF^[XXfy9,r@˒U {yVM/8ځ{:07]2`+{C8TcNyHثGAF:~΅J ;p\]P]ft@CB<"OH0{Gf*"O',sTЁF0 )x09f{`;D$&Β]EG*W ݿ"?^ g(RI$e)O`Ξ|373q==.rudUUS>2sdim-7c&kDu/K#'@l;|v<[w> ˾uS А|\V;Nm5 y6 2Vy @C˙o$5_? ȓhB 353=~phҠ %ИMPc1_|# EH:;28Ҡc/ݓ$&x;P&,f2W_}ضm۲/Rgj&8f)Vz1 lG;+iVZYXqE-ozV~w|MoX(lŤ VU$';i oOB LԌț Y=μ\ԁ*THLxL&3k#̸°qH{[4T c8bvƜB!-b7cHd޾,D#Rܲ}>͈(cN1S#}?iATV̙Җ.X]VzKwY9̟ lD.g$_"J} Y=s4E `4pC62A yoj=a B̝ҔGAb5틓JKU -=XAf _& Y>gT%$P-&)*1$.?g!~'2ˇ79V޽˿?fv3Ej'5+L\^Ji'}T7+YqaXkc[o5Y(7~~{mi `X"ɓHo %頪6NX  Lb|L  b4A<_0p4}`j DCΟy_3H@M{ Fא$VEHN4{ƱxOsaA2ay9O~kmP2vѷ^HfBb13_At ) `Qi؏p`6k-fT\xAEٮfzoBYB~>.IXPTb89ӂl FP892 OPɔz_; X`qo鄿yCq|$ׂ]` ;11zgԍ [E:0MJ'Dzmqb@ޘ|bހ 瞣etJg\J]Wӎ˝V+o9vN8p`nff>߮Y-EYK0t KIPt6 x!=4eƜG3*@}J Y;5|*U84LXKqIzKzT(c8vLIl%mJPL]K_.7:K1@!Y g$+^5&\x``O$-2m3Ҷ@oJ32b-{yI2ಪM<"fmFJc,qYD "@QL҂4\ gaV`;z ބU `Abj$$[* 8U0a} Qԁ8GJ,ͤ x±^=#,I2#f$ҕWǮ߲wbĒ݃ ˥a!'5͖ru)ΧNNK%Zo"5# ^@,"VW^SJB[ɱlInWfS!TpOK??1f+uw9Y-])Aymam$7sm ,s˾'T*OUJx ߿;{ԲV{jJ4ne dVO;y@[;%V=mcO? o>v 6~f:`InVk=. :* Z,y xH9a2=IoΙ=b#p܆e+g4iO1={$ZXMPXn Qg āēc$Vb>ɃRS 5uX3H;gdivvțQ(0`b T͚KG0Ajṳ9ɃvYHKG{y 0LĶ3(xp" (!4a\Qә:"op Ƃ_HČ0Yc O3 :O,㋥,r z{Rn43., ض1/q4S|+ׇx=Dm]T%ddbb i=fY9NZƑXPPJ0 x̦gk9˗+fi2V?VBڙ~w9.]NQ4]1,4IV{N{Ν;\yf Ycgd_~ ;%zZ7oN`f%D]M0p L>+"i6Dd-?N:4xh:$uRm L/S/ή{{%<_;Eݥ,i=|H6]vVߝvۉY;99iMpqPTC@V"ˠћv'=eChza`'&&OBRff"|VPy7"'&&!"b 'S #5(M{OZM#pqŸzby7 yu(h(Tp  UV۳B|nTmz v iԥl99.VO3b2zZFY։o+&^_oGǿ^n+13'~&d%}%؀߃2 B kIM1 ߖ+¿-,T2z3 Wab jd$b[x 3!@1/9,Oٓ HD9mR織PZɅb +0ؗ7=Lӌ F?Р酕V~0>ɸ@KiNZ8.ݥ,b=ݥ`-g@,eۢ}'NJfp_V8'N@A'$iZ%coPk;!My$I bgt,)I!C~;ebH/!hZV&=UTvIro5&ը*vB@˱Yձ$P㻍YA}©){;⡡Lf}\Vw|ww=q*WK1/͔Ek[v0z;hfg~ig։}V.gb?'oΤ` D@?X8%SX B`"u2J5"IyTοǙ6_ d03|#}cMhG 0t=gc>BK)IGa1ŏl@+` $Hic&?s +&i$ lYMke" ֦7˝p=:pޖH g"i"K _iX}Lhj f {c|_05V`^aC0`0?ruܟ-yaMBYKC+{` XǸ wJ[w4p?bg,{9J0Ua@5Xjj #X;վ7$@ZJ4lB =*0U+xp}j RyR T: WNT N˴9+!| 3 ӌء̫tn?ܹcpvD3< *u3.ݥK.+9vy9>oఘY fm;Gɣ(ۆgy''I)c2êAqLM@yADcS1 ,jC0*EOl&""P' K<~RqK|u$lJm߾}ƍ/뮻- N===#j!jyg߽{gJzޭɇhc^EmmԱI'A>ӶLrܯD s^|_o?obs)XjBl 9/31lcK.Y4^3_^ .$`>$J?g} -2Zb8Y@@ AeCo$W(T*Y"dZ `J!%|/;^ҖucɟI#V,06Y' Ex`<$eIғ 1h+z y;< 0 E&U5`cA"ĶC b0р]€QXdƆŒ MZ ]Z-V/CM[וm3y" ؾ`V0ǞoFz;JbG=$g7݇'6'.큁ޔF۞KJ?^l8xDnk̥!f7I4uys@8jxz| 3bekKGa즪*tY\y-C9ߗo iRI]tt]7%+5ljD[@1bIE:}+ s|~o}i:~o,J%FQIקtA0Lmۀ>*:~Zmꪫرߙa #+nq{gRޗϦ\jbJg + Yt"y}s g|v秮~6 oJk6%B3hK1=ʦ!I\ҍXԵt,OꋄߢHq:HLYH?cE{mD yW")D$gI[6qy23L % >5  F6SW 4WI/uuIB|a {1Ɇ,ehXXN#ҡ!*bG&eF`jGOF8>(QLJ||5d A%'5@a9<'҂I$O^2_Cܟ=00}߁'ΛU#Yޭ'piX@& cҩAP5[?^ڞ`; BH? |yt]=72Y,Ѷc -L@^3zkK}!1S0A <=Oo 5;[A{Y= (듢| bc 9/$AU>/$I!ۨl$VAi8bY1yaMr}vT DLgb 1scy7 QzӘoL Ǐé5kVI-G˪e( {umVuTK[q=CxT*5VS\sM?ewN}}}g? ַeډFse4#6\v; jwA4as93ʶ2>>^* UK)̷iЦAC: ~6B_ |w Zֈsf-&7@dU/+1+ a@%̄ټR04z`91 O&{Pz[@f Hx!`- VG/]^(UU `t0"|]c< +˦0g ?&Gc>'ʷ%~ЫA8a*G+5 UK_.c 駪_qa>iۀG1bnN"=$+gԷ=Bp~u>'GB'p 9kC ɵz; `h0c$  I,0XV&n&B06QCFϲ~(~d["($=?"m,KI4HX#'tpO3|$Df*55x7OӟΖOo\mLVUynyql3+ݥ,zK'|}.uo6C+֙ߛ}̢ɟɅݠBp _fhFO^-t)dt9R (6-FDZ-;Ċak"+2S#ifT=DZ(M߫d ,ȩ[)h} 2D([ZD0b ^hY9 Ō1t Pw]ǜ$dBT}RT.G>$ ҧ@ %=ƊyubX밮L64X~Q T4P泟w92q*Yu\3ww۷žW+}i$G!f,& k& Љ`JxZynݎ{PX=gJTNC;w|ownݾ^80CRIoq KL~u>5 {L HNc ԨJX2Q<*0֗Zb@-3۝q`u3e{VMaFQ J Uvl2dbb8/N\мRC נapz"+2cb4ȿ3 {fH&R֏x|(ޝ6ݙ"AY3*Lt_F'@˴UI@Rm iФ}"PÈBrb@窪GqO c$mQ]?u:~3:m} _ȭq\*W=k*aI?fp&L`_䎌PP10CBl kXO`"uy|*:͊w' F"ȦM719vaNב#'cN3ihL¼ʚ?pGo='{W*~rI^A ɱ?-VzKwimJx ˳a J fyyD@"B$&HOx,PEo͞ǐx;̃Ik%:vݻb$rwvި[rӘ 1jLIdo!F fgaa[cdtnj\LRPUr".SԨg@}ٺuk7X~wd=O?eyεc d%@_m=l\);qY,r ,ZgÏ㓟ч]qm z`U a4Ja >0y@5}ǽ(}Xz,!@I4!<4,$&OXIO QK<4q$q@0G7X6]d@PGK0&d2_~Bw@t!fR>ae8T|d:6]ɹz2IAo60 3K%AF9}cbpʼnX"{.:t,KBl*Pv F%u΄`ڏ{GJႠOиA8Kˤ-'Jc hGz^M j|@es\ p`uISgx0~T1@*s뭷fn喛DP__/kկ~x7+!V\:Aҭm :\{ b9gZ'ORرcE]7{n,}5 ݡ&RG2ISĊ@3S֟uZVPN:9z (`BWJFB=q$!B&݀Ȋa7WFAax45 @n=iW _2%ueRz&},  icPiR!X,= a}P|g8P܉d9٬IJqm' bfd3NXX D$)UQ`}_r v2@aތ:&+4,90K20d" *bg8! wIQǂcv0qfXv@{SGᵵbX *Cru2=t)mӓVyd#dۨ'>g5 [Y1Ť=L禪0E oGw߾xGc6cxlgmle,]t l-6Nl;mUmY;ĩ }d2sssԪtootp|WTޝ}X,N"3X7RAyTP.MLLOOOW#Ax~<3V0|f0$z /f*DgL m}%2 ؒ|hI!ɂfá4p1V+#<{{D̞LJ$jZJ0.7Ik VJ`徾>+ G T?T>n4|X G"P_{LS,هVWmTCCj/R?GԱҞA*k~=J̥Җ-[׫Q/>}#ev>8L ,_,9}ܜwNY6?zwwњyM# xk0d`prM|HI$AW@As \п1i)&/ad@41Lc-Y 1ͽR*+=s4!dubE$%&deلR0m 8`z,K^|Cp%H.!3<~؛a }>0@3 1peeY0HP̪w\pjAOeP.HرʶMMZ{ƺeyC֭YK;|}Q9ks.ۚiղX3($ą xH 57f Ҳg6>}R(@^=\᳁~c[*&$E5>01k X6bUP؏'HnٳGNJꀐ;AFK(603U8Ss@T_ {<ɬ$`LG%erH|GmjIo]S=8yH)T'$ַKwdl$(w祣& l$KZ)d`0`FO#H)Md@2ب܉G9Kc},_,h :q,CG beecs .GhJό̩>ƫjnuoe|k#֜-`,OW*%0~k7mS*k׮} @$!}hp&ۮAX:' ovr#rOF;I\v.,׿~V0Iq7kRW XFEЁpi+H/˛JgrxЯLLLLOqȄ)*yp8/ &fJ#$ {0Ѡ]j$%NX;]˾HY;I`P.$ےT*AfpIT"۵Dֳ]z(?aF 2s.2(I)/0Ҁ;M1$ia(9A0G 1H ) f2"0D?|M7 أoRmW1>Rޱo߾ǪA<#Wa=5hӪm-e LZZ5cߋ.k˜7Ñ,+ I, 2 Cǯ+耭B]jǷō<yD`{f$C^\1̠HJDgn 55_Ts1jj[%:VI{o׮]]'p>e˖7lrjj?(!2 9VS[\rWj;Sȑ#sze-sf龟i+.2v.K0]UfO9 gVs}}/d>y6J}"&O5Y8XTa(k ``?bpw?.9؎d ֨Djj >\N ~~M|cs }y1pO-,s@id!šV)8cvy| laPյTikE=Q/fTbh+1 8 f tI"fS%VRL^= a%XOՏ䣏> ]tXfM6|I!^8L Ra֮]{;eddPlӪ2?_}8Vr%$m,듂ҶI:XGz?)YEӪ]Q\CoD}WU[Ru6߿[O=s7:r &t3o6k\ܔ۷?ڔ 0hon'"h}"5X%؃B0ʵ5y,ފF{%8џ+`i1h"34#; %- cm !4ucXfL+ȘeBKK[gp1)A,0c%Gaଞ P ȝ!'8v$Ke7Ycz&UKUR0)@vp{5LPRj,+7^277wcdžx\Oo̱GBD|뮛YnaUoMLLRiUWVQp^,tI`˒:ɁvJK6e4d/yPn A?..{{{?RT ~7g "?{ K ,@6PT|6ՒmGEgAWT{{}sI3h![E{~T{LaP1=ɨU a/KxL$ژ#UGljIA`1~|eP]~܃Bn\@rrU44haÇ] l>{9o} $Qדj#sDJ2ԌM?fK.ںu뽙L&MD@y>{dx}Zn#Y.྇"qx5}ݷMՀ[C2LdʤD,..#-GlV}t>·z96H5$*=MKt3,HuY 6c(u8_B\C*Av~sϡ'|cjy Լd xСn;j~dtt墨S wպsv*u.iS-@SwYRY^gʶͳ$VȃAgz g??JPl}`GOr{aa3cZAOA` b,Coli߇cǎ|~~̌3TÇ˻[\BIa؉ 6 d A #y6wQ| '|8σs#Cl+(`SX:HJ%ў@`zj@yb %%@2uV“jU-0YX\ {np`>U`?EWWLc w%SdR\]sNS_|+?0+6?UƭS^`9mpVx\Y+mÆ vg~'&Cť | GsPɤM"< c|G5_y}͜i;::>v= 9[ \KK0, fe-i1Œv B ,$*R˓C@qqh6D1y0 Q`?z~Yr ]U'¾1>;Hi`eףp$eM ^Tk&Փ컃ɇ~-*A X H" /zr}|6q*v닢ΠI(JaT=\PZ}4i 2c*XٹIQ_:6{`T .``pp_ߑ?O'C j^suNN3=gT.s]aV?I𪖫ųl@WrU_W6-,,2@@$5ac* ɤk'&K1jZy4'Fto0pERly2i^oH{I5ٳG?=U^o-T*U93ڮ\z%|O?e&^9'ogQHavc3MG7}aԯY-zˎ!#<_ooZUY6o0^ת<%Uoۿ@AZOSn׹}3mcُ}c[ׯ_+x?mjC9dX@CSpFBAGPnv)o;_{ܩ=N>%&w|aĽ`0auLa5 H*J5 ;+z {Y'gc 7e;'7g~cݕ1 .;ǟzٗ|rW9VRYαmKYw}δߙ ]g]aV&ehgsL's﷾?я~q~~~WE܎ÿⳳoU9 \ v C511_ %~2iƙAWJYnkrrٝLeVMh*t,"rӦM _Xv-Mɱ{N 3o(_ %H>apu XLT*1ibJ) %E>*(AR6#%T_+>l$J yaXKG<XVZ~[RڵK<AMr eW1.5=Ui|612Kkl6<'Ԟ>cB쭘ʫ%Et~=8jFCjwt1K尃DQu?H!2G9/ax7;8i"W9VR[t|׹ B: ie~UԬrKȿzn0~3 zjɱz["tHx/;Rݰ=Y_7߼Es/9,)14PN}[``g9ķZ<e*Y$ e '95+GfʳY֎hXa`7y##z96#Hτ[IK߀a֩%BWt[IX; 4Sbd\Bu 4cb-Ҁfz O,pH,P _V4d}O ?sh`ɺt8}a1N UHCO6kj*Wb-7>>),!qBff: ɗY<^:z!)-f i$OL$BcI56 I *(ۆ|b * DFt5Eskb }x؛& 6NBmaR}:ؽ{gaJN1_Qbu| iU Cj\|Ż. TX6^Pe= K1 L#>9ACB36뱕b8MU#M]EU>AlS]WW}ǦV@'yZMP1j>g;l5pӎc:Ef2~~9xǟy![t( }G?@:+ ͧtSZC}{ڽ?:?I%/ ?Z67{ad̍PRuv,FL\!(eB,dY=ɯ18-p@dE$,@b ߯DzFd.γR݆.K0 Y4ۢ&K6__>t7u8KR9:77X\EJqpy0%a0Co߰!,C }98xVi-155GUy#o bfq/2J*cXjpfa:fth /( 9HO4`0(A \.g}e8UZTT{&(cUG\R!PF,KPeޏ4 OepcBM > Ct} >駟?t''&*k׮M Sg)ˆ zת}.޾}jb0>FqZ[T b!CJGЉm((OE +(#|c>|¼Q9~eQ$ 1QYL&2xt{ԲAC|P8Ժuy׾3Ĵ>W"Wy;Y­/qV۹MyrIŵSpEI _|x}nygyC[{?# 14ɎeRp'D rk{>mܬ1_}9'6JZ`a{|51#jf)sޅ?-; \ɤˤus^BO>!`gpLY 6I1%$wGB>>쵃0PwФfU!# &&mbO֭[ س9L DQ{AP$ 8-Ʋ[OM?P#Mj4!lBx~d(ߦʋ`zh~=>6Qeoo SXӓ":,{' p3[Dn|lD(-0l'YK09_Ϳ͐0+3a,vJeo,|˃m̜y#u kɬи$IA˴1J$<4Nގ<a|Ԁ]Y;iuhK@IVt}yA##iߣiH\A?OՁN byr~m,ezVʄPeIzK<&vNz+ҮۻwϿ=88_jڀa`Cb] G^@8 i&g<ό6onO6(6>`ai96z8?O!?d luA6<֏ONNWR;jlJ%Aoo&7tf,BL:R()C|uEHɶB >E* @?Und,("臀#lRo0?ibD,ߦ>+`@A@I^9 \O@}n#ĚxgŽ+ţla'{Kk6k#E5f͚Q.90^@%,+WjZӳy˖-ST*x`kڳ-153" uSppG+lsU@P:tX*ZRt,?i`Oi%o}}}k~R>Uս=Wqã>z7rx s ~r+$Eۛ :4s<+NPix{)cS[guOsq^^=~g7H{6e!'ܖ'@szRj7ත @v5ʾ=vSʲs%rY>  Cz=Fx6z:)>M 3|cd0@#|΄zaY6sCչ.'vHj, .d/CTM+5iY6CG}@@I$}# Qi{@a51|g, 읧~Z&YƲ<:P!o2b}F=k׮UUz`y|@ (N̶m}ӦMS$'=AWa 2aA˄'l7 JG 7b'$?/yXW=`Ux캀`r\T*dTnJvxP(Ru~|K_W;t Wq g+Z}|+:eN{n޼9x|`|||kFB˶_\7W_o9mzꪫZs~aΝƗzI"?OKZdH%17\yçJ՗>Ƌb fGGG{8gz.0̗(UwSų#a a<8 #,Eu`a}z[%a'x"\Mx86Oh> 1.$k)9<X eeC,fӌx-+I'@s$Ic.Q/k0Č dL(f~x~@ci~nJrmp#;~/[ϴn)ۗL.г݆.K0]ssyZO`& 7=R(`8K#@:a&/xi0u̖ KWa / 9Pc{f }Km vT-}ק)Jp__(E-%,Ze`um|/ܾ}f̻}wžRIa,G$|_HPbgc1ADzik/4@u5sfsY#HbƿO0D  o.U(s]w V~:mG1:fDKn9s]~>ٚ+PJU-_|9O5>>ytt4{mx\,gGwvId=/I TgcR;dSܸ O?3E[nez~ `,Ճ{2;JEzEHD" ~7?ηFxbȓT)d} Hqt& ! ubA* !*X9h& mK.craA 1 ƀeNMLG @U07nN x <>A\Oh'ޑz jh>=1[>419 /@u'ѻy;?֭[}_Y@jw,ì,+vLX di4JٳU)5qMK"o+blVMdOV{ap9 >L384ҲPY_g  s ΢}Q>>\xRTLagrjj݅)}N z ϘT!Oa&&&_,?я+RB~: elQd$ 89>X%^Bdm1Čs\ߏZ: Uٓb2n6}j"6J0_q 1,_P@o$[o=\8;;0{#o=@ r -sYُ'l[d7B7n Tz @`_#pL70@kìFEP]~!8Y],זJ.3%O^-f&1bbhpbhH,2d@1ˋeV:{mCU}݃}wպ100'?Ooʻ*X=K-O;fێ:=1¾u{;7^:EcALaGmڗt]xj5_URL`Î_{Ps @jn{vp tӏq ]a@O+'! e0xN{| 0ȀV4HWN Y:HO,-cAv3WܞfP]?l61"`1$cz ;MIO>xtƺ{E믿PV x/Lϗn | H ODY ʣrbQyҬ(uME*Tغ}lٲKIDbO^Oʙy,,o ˲躉:dO[>5et0+WJIQM3Ѱm!}pc%I/a8~Lu Ղ"Q-lVoj_xuJZjdNYuVZW[Ըy?G,&@?lhfr㇃(o2M7·<B Az_ 0#݆eDA00{ Уo HY6OukZ1n\JH˂q -{XLTW\xb(coEjPS83jzH-p><nLA3HTN_݅i,ƀ>~a5_`\>9l`Q1 `2 =ytuPvXn|~u#ϵ ۪:g]3/fۙ?IY)qnCu%u tliqhF_.튿;P(rM}a~~'bqoV+PCu,7_Mx`3DCcɆs+,F1K>Km= MY/պwA]~%u-! 8cK 0 ,lS>_ Ut5ђ]7o^΃DHAp!$!6` mr ګ6vuWW«^c^mwCeAH &4+S9g|}>#Rf/"ޕ/#=;'{oW]nR[Y6vm׾r$_rwsuCO={oe.{mo{?9qT:=r&<9[lGt=6O:|)m8`7ebSS1Sw<+%lFwMCI٪T:CTs1qbS\5fԞ˵9#L6M]v-Z96"zߘ)бb[_졺+H ?xl41lZOw<*NAuIۍp.m%)$*U=oygSOBIKʰk{wr_=c>idi+`z\)<@yZ}{?555u}{8J!L P IP("lSOfI|Hz,rh̊Q >9Bxt $xL,kL&ٷ@C#YLHF%@1 Idԧy0yVVVۿ ay՛ǩ)  Z<-@.F==W(/7xc|awޚȩ)P PIHLNN &P6@< \kպi:ԗw֧0R'N;:}J\kEԦq{L+xu4&vW366&`!zEw;FFFfv{ HuI$GL&4X:yM}}BG|GS}zz^ϫQyЗp$"f@4dC|cC/] f|Lb˘Xۘcvv:gc)6og?y۾Ϥ2V@^9v{5g<~3sM7?P:xo>n־?nO|_zHXy 7z/pD[C~L@ X/u63 &!ЬaFKŦ!s/&vZMJ{rI)Ʀdġx5ڙsl (ю;roo~n矙f e/W([ԁ~ß( Z[cx s)TDhʌSX?ʺs;ox׾!R}DN=N:w;o|=ʥ޿|,}~4T0HDnm3-ow}ZNx l2ىs,k~+3,$Fq4dۼI;yܓ&!(c! +P;䲗РL%ENmllwee-2771l/]D9*X)`iġeb\U=WJBr믿>>p@xUWEwFFF"a {<~dA=PVc(ıDMۄck*ȵ eCM  #~P`GXcb`UѣaH]Hڿ_1oCg"[LX DTH)I +֛x?a YK5~#Qu3n~|tttmk/lʆĄ7; KDFc I%HؓJayz|yYMq"P<^{qx ZrX{>!]3>\*G|_&A j :/-i^yt߭|W{̻wT$o#G<裏Ž]ÿ19dSARk،JѬ2cxنW{'3[ۼNvQƎʰ̘g ANʄIUS(G}Rr1crf5e/>}3/ E1Nn_t?l0^$*YD hMMC /6'uS3a7xH~SOfxhb1OɱZuI3ad=R_EeG9B^r^%y6$R}C̔@,adm|XNСC;w"633DN.g`Ld؉dj\.5::*`R#Kj G[Bc9ξ ,\XXPغ SUU"l!$`A87tjSx΀0@TxĄR&G![CojĜT_ؖ?3,hvWx6qlkF,&lLv;9脐ޣT|XЃHp<\Drlnc"cm>?cXr.(x745 `12@0tgJ){9vKŌ^8E N ;vNN {K 4\=\rC_>k"0r`JȤ:e5S` A|uFa@JOH P=aix_Mܘ1 Mj0d2Fu"(1 cv^39l3gvVߜu3~_WnBxN렙'@-)L"k=K^=ikmUQ3@ϫt(43"+Qͬ.LLJзj }X${?^}κvGg&IC%K[s=Wl[?]t[wܺ??;H6J! 떗?govf{wQO;=yO`@P6=o su ! D搄Ce 5yvIq$$!#9zycox߳.666ݧ !kQ ,9@v'3JyzmWTY^xbb\ұ`d(@;yNGSS0F,lɓTPϙrzԩSYN a>mfI"Y_:Ձ ʶ1܎2#GX[)NI !uG"ezzzF@G@-)FܰA͍O\wuKA"PK?DŽ8I0C ʊ*S@ԑ~xAj|X$25ċM/h̸X}L#@$Oa`{}"K>9[uoG>~>Ν;O}|V2}RN5%OJQnǃ\wr߉y/.mK_+vZ#%9ԽJv5!ԙ'L]rLmqe*V5F@K1 {3b J݃gymݜ;1{pl*{Sżߙ?v\O\eTSDZ{T;<3&/^s^ݨa0A0`ڽ#eGR ,5QX-Q;,,ïDzr+n;uvv5w̟ Im冯ݛnum!,gϞ㵵lI)`ouSO"! z^O+h 2hR'E~*x ''%Iz8;!r%Ȥ/ u8Ih ua$WVVv mKKK1[v-<&_dɵU &xa&(A*ˡ <5Cn#`AU?g8 GzPCI A?{Ƨӣ bE<[ꀄ AJ:gl(0lP6t8F< 6o  d0mȐ1A"$ԤʚYZѓƌmCϟ>}En7n Ǟ|)i`M/[hf4nKTίN,&Bc,tvT)V6Ucan#~Mx*ܯH2˩~6~3/6 '@嚷O*Y =`d.ٶ1{_/{{* ?TṼ-4 0E` $8aIUe(Q$Do{я<;3Z?!_6Z'ffuuU4#c?XZZ:e+bQjI(fAêe,a/$L 'uFD9,wJZH2$p td2i{}GUW]e#2kY9ROOMDR,xE<{Va +(`g 65 @1 7"ہ|Tl;J3!` LHHctӘ뱆1 d PI:2BJF$hHZγP X~M;Lfl`3S/tڔ+=I ҁY/&b @OܱeLF@/? 0Gn_X;0m}b6QǮ\*}?=sgOaM#0d?{])3^Kqs~=2\Cܚ#@˜=$$GNM@@Z#:l̏ Mi!ڱ=n_Cg 괇v'.vͩzDnm0bron7tB)N?,-Rb Oȟ#3U(# #J'f`N x!W=y Ql,w^9vK/4ׁgtt4w1|ܑ6:H;9zcZ-1)'ښ=MR4zYÏ{bN.r'uvR߷kep!Y|œ4T0O ]'eHٶӎ[ߪV˹\αwx( OK<4&>oGidƉi'}: lcc#mF$RTG-5t1GW1ɵ!k $x]Ky2x;#R~*Xs׮]3DrͶx lLmMo/ӧ >[UP$`V>*\j7Q^y5{ʨ̢S#h@ )x;姌̾P \6ѓMI\C4|^P#f?^Ccbg+ sc֜9sF@ wY__߰r\@p,`!Tu\w z|2 @elb{%$N ZM08PfH VJ.'97Ԏ;ݻwצ֖3֟qRGwIX=z5uRH ̙^+!UeFkC0kA,zF9$g 23666n?Mm.63555azr-/ lݎژ: @V@ei#1"x;`fzR@&R=}R jԺLu $^ KRn|FgC+~} yyJs IYIэ96M%x |o>{{/ Ln]?t,%O0v_Я>`FZ1rl)eȯZ)v|h3!Fdne\ !l53|0a:^+jdf|.&ܺnZ;gG}tjRzZl]RO~HuɆV)VLaζ͐1Wݏ`񠽍i$D/bbjwT}Aa6犠=}yll,D-ec5XZZ?a99&ʶkmIA v3H$%楶L ? \X !RA0N4FʄK:ԩ`9g׮]j}u|]r7k"&[yg}b j"˟FaDr<$y9*NⷧM(|{?ؑݏ.#3 V&6ZucJvkF'|.'`1)|%)nn3/6oʟ5,} eIlVO8ݻw;Y͐z߳חbdҦ H|+){=M&IPwgs,#ӐK)[%H Z}򱱱dڮm}Ԅ$nB!-F|IJm+o 0$cWSEXS$C̕U0J(U"$\+x"d H Efn˒xdDMYzd18 n*^/((osUIc[XR!$Hl\1*+' _##_7ĀAW|][|ZGXא߯׹g O#0=?C?yRr+eK+XqRwɱر:ݥ|ngfz 3h7tIC%K[=ry}3g[G$~`y6xcT:ccS@P5&i"$=!9oD-/Pv3~(e %{ AKzG%A\C f\QVZ#_/Մ{%M ;رc0666911ɓ'Ef?77w~eeeݶѺ\O yh\b(#2B򩑄2& \"b^i: IAr q nW|^$-mU {HٺGFFjsg}}=#46Drbs"2$N*$+F/D}'gy@-7a oxjT')nP i]@^Rsr0{l]Mk/ںYu#fpL7n泶Wlnؐ++QT%+@#;kcbb6zԘѧۄԯ`?F} ֫H L1c9JSu1 GkpiYS %5]U92\{T3 cj| 7cjۆ~p;[["|;܋sdJ\5qƗaLof'>+r-{k⧞|ቼEMA's1CI Q &l8$64K%1_[XX(nM.4vmӧ3haOxٺ+Us-,ٻe;_|gg;ݎpRΤ6r/e~)ٶ{d盺`O G_MRD"R[[[r\^0u0!dqE;*3"ȹI}G H|L=`Pn-CZ=i*F004iFV .7Qf2s˃א&^G~a!!<Ν6ϑg{F}fh LAgsSAR=z(q 䶴M]s[0k`Yo ɷD$әLF8ǾX^ٺ #^3UX(d(Qp*Y6_b,wG iΝGl=m/ KghhHXQcǎwW]uU=yY0Jv5}ߺڮ_w ma|(3KQX*TS6N oLiСY93+++ϾKN:j qگkJuwu7&퓆J~7]}-622[o~_0 q(2bAd 1 VQߡ}3l$zO XĤxa?GD `/ G\ZE۲Ѥ?8]d6X)Q+թw,..{CdlpǾ}]^]]]:" l81*量!M@iU=MUn4,3Gԁ4I̹rJ\"2zR`Gj>ψ|%lBۘ\.괾J [I)ЃX*8 d$YeqPjS6Q0{$X^@ J1m#333"sgmE0{lNOOg뮊]w^R࣬'y*)P(*$iivL+NHg>fLWr!} & c m xXf"5n= dp5R%bvf*(5ʲ8e62I)_jҶAoGow<Ix0:&m;Qw gХ}zu?{ޯtG?/c8:>Y ]|B9`86ob;*.b)ۿ{nX:;Ѓ>#xqwv;a} AZ'k7w+_s#Ν;iaڹEe~A˩Lbߵns9W\ yIN{u#ٛ->333ܜK 33vIӿInll|~wJ#X, c"T5 oNvITICbS./Y18'E $5 )&w @ cKx> 6 ' @ _Ge$ם;w6<<;v\uUАkX]]5'N0ǖ_"IVP11Iy(41SȨӦCCM`oHj4X!F%y#~=̞PVd7J%%'l[JN&!YL)E6.8iexC0HRP?KUj([@l+ȱ2ae&''v,~-ٛbP(65:::9>>>cǎ]6ʹ\nGGeh1yY[vs; L`Ԁ72,uCǺ6$d RS$4V)_0XQJ^2m,@duvK/&_!uxZ+EPMI? ~`Cm*1{̅#v\)}򓟼u^N{ .fė1.7Qa`+$O}ӛͯ}d 3 lz-V`w.옚>g/9{GyJlNK]Y&|;`txWn}'.V{_Rܿ9;9b獧Ν;w?y6$U.ros;2JA' ,ml ~1IlES $_{; D$R,H i`\OCMx>O˰ I/=o! eЄ,׃b1d~^1.9L !2 '/r3\mU'&&$kožI{1K/뮻αRD~LN>]zaa[(J]]bN 1u 9:OiXD.R6@{O>MUM^ؿH|@#a KHE W &747WY-c fD:M@5R uUW q?b-s}4ܗn3\:m/nGNܷt칔{tUoCzFc{UW]Uvq?tرcO8buz L":4~ 7X,n&o} om/{󳳳?\A2w;~(rI%}[_o ѤI8u_yΝ;Sϟw x\saY:u'׿hoz64)6 9QaD<IÓ,O H9}"M"X96 @HbVYBHG*9e{V<%&NK^'cjjF@&a<o\.T9uꔀ<5I鉬yT= sTgQO64܏i8U?]k uriF&816O:baBS|rfB=;z;R6ޝ<{qo>DkbJ0;"_aDn&H?)iYZb3k_%>,KdFFFR{rJ3#kkkvLGvѝ1]W_-13b׍cEYA %cmH= .m,).@+Յ1ĈA"1.,r|MPcWĔ=""=`Xr)bDZX1% 3}!9VaiKL'UmH>"fE~g4y+T{A^z*0fFKnݳt~f+zo73331<<<&7,f+QM;VI>d4T0Wг{['. \"onnT*9;nw'_'H *R{xׄ}S5!QkRZSlĎ㥴&-&٣ PkȴrH؀ M:@oPa |>-SbLIjɱz)#8gݮyl/ϋ Ķ $$ibp4$:i0b0bI)0R_tHm NW"]$uf0{v!OĜ<+d83./qC3-vYАƵ>jsncnHE:x&4^03P @5NA%>` uG3>j*c=%I 0^`犙\3 Jc}܏kZ'9dKտǕ]=;m{T(qp{{ d? I?6]SO1ݐ{OzM7^ȶmv  _x]u&י\gαu]t?%zi@ P/vHGb~~N}'%9Y(kTzN%W< bct$M;`HB G$L%i Ik I?oTXMdu@Y> hCKs{W8}FFFvOLLl6['_% \<0+++YZZZX\\<0.疧As$vM_c7zrGj161̀g@4RǓn_ևTΈ R'q{j*1)D e8{dy|@Q+݅(dÈ9@L$t I||mA;ꝅ؍XreՍR ػvۘʍ٘\.%CCC9zlO СCnh>_0n#o#e)۲I;VR 9_DEjr FKԡ |Mx50A1|Tj1 "(K)3D?5?z *Q u H,'l4qg"xbֶY1a^q ϟK0>ïWK<`ǽܖx N׫}]'[qԩcui%߶Ko~t.g[IJ&M/ݮ^iۭ}SK'cIdi/`g3 гgϞٳg]br~~T*j]旱~i27?n0 Hش!|4xs-Z>vKBWk"%o1C)9D~ |RF@Hb/MzrS`3MY{ j>ݾ^s@s,RIIrئy^'IC%K[=n4 VWW]B$~T*<(kav}}?Y il sSn_ (ۦ%US,`4CL#CrUaI`8b,dDG2p@oL~<.9,.= y (ϛzɵرó~5??/:;Rd >jiIjB '8(3*U2yӃXZNoƥ,- MYfza*b5rխk/@Cbǚ̾}|Ǝ'"&y!ns~žM ǎKHQ9;hٸ5;NUJ> ;V `LcO(d !fY"[4Xw!5e` =`y/,7u~^bS*N+ $k2OxCD@>1ߓSQMvk}܏sOz᯳N't=Ʉ 6]7N߃jVI{5 ~ޒNsggI&z6]͂@xsj-//o$7R1mey6Hirl<VcHHu >"=wF(QOu萂~Nύ]y P#&"Dցw$FFFMNN8n^)'50ѣ =ДDqrQquöc$Μ9NGMkIi* 3\Ճ `Ux+1@@H躘:=W- L!$ OOcB%766T*xf&;J榑@dcTN$}z5|?j<`K+x(맪IϰjF17c7u KA`R:A kȋGhsΝ[|'855&ԿQCdkL2X":E-c0᥄qPdGA7)k l\DX@E ~-8N07jX}Zmԕ+ϋwnוկ>I9( Nv.ZJd}7V);ѦN31/w%`, Г,Ll滾z:uu_jY*^iRT^}5dgezBŐDXu!F!ƆQLZ ~54e5ή Ԑp&czG tĦy饗/zOt-ɱ&mBXovL̏&V&Ikpٺo@(M,5W. .{X31<PD{xy(ǑƫϪ6Ga;X[]|`z%Y/ڿu{f=a~"eWyv"/z|M2CrgL22+4Kܟ^%%f쵤!7'=B Uk/:QNi@$-qm`b 򌮎xޓ)ρ$}"'m˶;^; ' J9ݒkF{A+ 0vީQ׺w~]cu~.mI7۶9m$@O0 г9x`ĉ.!Ƌ$h` Yd p95NF$,x4- O{}CT}Il#z4@MBX>0*@<)n{aWM#i.KJ 9z<@MFHȘ3==ӦQ!b_7DM1RԄzYP>}=:sN9rx䱯sss/z@@"f=1%2?`NL  ' yœ{?}~߽/Kl򢁿%#̶0 ˜I[+5E(ơ=82~pnyÙxضp r,cRQFM7ݴbcWhae (=n'<e,ec>Cr~aؾΒwDO 1ɟGcX/~LYԻvxwO Ip[b ĘSW֍j$9L\%F2s𢂜[Ի'rY>3>3gݿwO걽]^I}c\ΠDI/"qNߛ]~@3,n~'ێpI&K7/uސ=W=^$@fVgbb"P?z\%9%W(SR9U- J$}OStQ# <@ 7p^.'qI32)@`yI&k2R1ӓJqCKWz3]FGGONN~H=q, mW l+HBYa_y,---,,̙3G$ʙ<0cvQ@E}Z4seLIIm\%"@^&$Bb'ю$mLOPVKNLL0::z~njH0BL=!Z6Qߢ|>9G nllϒK)#Q-V-Ϧ:m7V t;iaۆvBI;mTjȎK|pCE,(|Bs<G 4)P &iȝiY"j*Æ6 .鹜WHLH?񭪒Kc* 02*U |ܯC-) -אC:mc?cہǕt>Pfig]'׷W @"6ݺ5l{f2zN:5?ؾ ~0 I|#G\^__BfI(VYJ̮!&JZ~K$>< _i6FjZ:(p߃ oB:ErkDgOcTZ|.U$9::z] 0_c3z=tzXgG> SGϟɶsKKK/ ȳnח+J/Off{p3ԍSUA٘pBb56kg!d8y@b̘oCgt̴!$ 6=T@(8 4r#ў0-i1$_ \; Z<H5)|<>>1ln9;ŷ~OcAy8),^=yYgEzBecE,&sbR=c 0b;.41;qc>,UvG2 Aـ({X#䙥>I^,ԉШl;.ZΦ}1rr]?pkwN%^>r"׫st{5wSRsqr-I%LG\csMX'Y w]zבO>|>nll' =42^S' a 6SϱEdD2{3 iV+fR!F2W?Kd<`(]էͽ<ӧֽX {Ðg h,IJ9] pP3ϸv?, ȵϿxܹsyՋI=~ CIv4E!xĢgߴeޛׇɏmĂ!IX$>ьC_e=-=W=`oKqd8F;L> o[X͏ -pRפN=l8اxieddı|FM:lDBXX\lL,ʆ)3!QID 1ςRaΤ:Tr`w7$1ȗ' GC` UeƎip0*]4C X>r2e [H/R|琺T+a 6e22lVR١v9}c_} {\Wi S;ӽ~m3=ޮW̢Nv};互:21g rK$K{HC X5 oxC/N? _T*MTZv([Fj'V6J4@N9#!1k|@0#r &$c0AНSST|@Sz@&/lnS,vEǎ3'Npۋǒȵ---U lWIT u<Yn'RNX=f1DjĀC d $`(֘><`DK gк& (e(6/՗Ƙfv?iw t>f!gRQOMҋ8o6kep'?kV0^K q=x%~p9^& l6Iᄏp7}#rYUyoUH˳`R#X3Q CA2xb)S#G2L ^e$7jCȳ qY(7^sǔ  u*#z */9L.s0,:BEA Tȶyp!54446<<ovv|3뻑Xn[9F ,vus$eLʘ+csLI&z.]mz|y=|umm0 + k[G4+|)M0zC @vHb4> '&BtvXOV(l4鞒GBM|2\tinL#y7a#yQxAïw:ޅn'ɥ%SO9pgcc1yVVVErnS*5KϡX xC33ŕ<6RUL@"}& [iaHY4[Cs<[X?!o򥁤+R j h,D2A_CE*hL=V/̖1FC`MLVL̥@=mb/il#6 (f=Éb=^|~{}S$7C;L8YJYv%ƈd"&9T*U[ˈm,cVc IrSVe'k ogSX& D6DsRVQP,f\.wܹsg>rqpB[SLDڧS1]In}33W:*'$k;\D=^$@ž4u? \[[PX4 K)_T*rB(kRlgi?hYM#V)$L4 g o5` a+i$~O}2 RH'-O>L=d"$A0hTJ=:66{@{Eο`x g6rtyy؜]WEMX-u11=bf&sS7'TRfΔ&4dkc'ʵD^{?')sLo4I:Hx6 vI~0SCߟ tyyV@1q<@Kx<(2#BE4fw4cǽDe߾}m1d2 |-> X2؆ұ'E <~x| J`7Tb#t1()j'a|~!]wL=5 e/| 假Q:뫙f€~w-&OrL&cbbΝ?ҧ?GI~>N?'.7i~{ufigfw*)1a+ǿ\d7}1z 2o˽I$זy>߿'K/ Г,L\컮=;w Ο?///B"⑥X,aE+^iNST^RČ%C>?dv_oW  rRm=<dz~qq\6xY2? `$nav4=-ytHbb-0{JP,8oL\\8 >ʋ.8qB^ ˚qVP̊ [<ōK2nI <H$k*443y L ˴\,b2x )GR)?n4|b_-0}tP10"Fyv2 nPh$=ز.9UEQE4B@Q1 8L+ PjΌ1= cVAB P-ʬ|̼ל:oo{ns7׊ȼ}>Ez,.Pe C[<68glȆ7OMR ҂ߎ{ZTN%}€lyl'[i׉a!ɯ횃~x-}?D~?;$]?ڶNw.v~L[Ve`"=XoH0f.Eq]~f~f~f~^Y/ZrҀ?@f]*y{^CAΝ7$-u]rmmol!v Sc+-6 Z q"t,9b>Ik;Vx 9rAbay*s GQWD$Ab%@p4M1|g¸?qӧO]Th/ݔ޻)Hu~s~6`Hc?M0 wy O{AD?a89c21G078d xqt^y`;T +$mz% ب1+2]⮷nI_6yq}%/yIeړ_0`5z V#CN$ֽ/dks^ m4_RcXG:o3OU5VVVZf@R 4-ʳXiuKHOXmȅf?cvPɸW򕿱 emAEw6:vܲ1WGË -zډ[w7{ eg/<{ d`o 䴵K[c O}%OySreeA9ew=imSߙ5ђIIǖ"=ɥRFOdI(cA!I j т &`$ o@80ˉiH<-|St;I # v*)~|?y&C2S_=}'NxܹskgΜ0 <N /I@'Ž`hX1S,fɬ%@`D8a1u-̋z,*n;G ߦIŹnjS$t '}J<+^[Η%m0 yPR6T!=h.qR~MiɹMI_Z,-?\vmeC[FY+6='L6 LmK;b,`AR ~G& `_Mes6J ZR/כАb4{c`mmk^Ѽfdeir_vyf.'"mܢI;$`v~GʹEK/Skw/DtF@ON[0lޥznFwر_9s_LTmu|VK(Gϒm=:p,+m,0:+++|fS% a!+-6`K;grzl=ᘴ[AOqB4k8` e@Nlu(PnK<1+\XpP !<Ó>%Ri=Q;J5*{o7ͫ]  1<j<#փD0`@$L``P% BZ00_ӂn$[*Sǃe6L3z&lYTޯ s3J߱)@~u6 ;3N˵-#s@^3xι{O]r!ಀmr[ֻy|@3~G$iKf瀞gs[>xCԩSo=[Uk^<' p,F7A0@KCcG~/-d~#pPtV@ހb|~+[Fay#$zLȑkE&}O-y@+z@‰w`êRϫTL@{~K_j>^ճgϮn(:HgIK!/^IaGgqAui\~,DʍAЮiϽ$`#YD>I>?\0; 8ćdz*ʍ1F @,,nJ~N&}!$nk^6 : Zz3<^dA$KTLk-[ mk>3@[#=#2$*[Āx)p fmM6Dm49|+tbCTx-`p0\yС?|oxA^fIfL;l=n7>oZ<!tԩ?:w+S?j4a j֖Q CNwƳ{%H`nӆ"{50&b~-8<:`"Y$]7-GaӅ&65n{9-G}Կmo;|}'V5 jeUO\fp` xNƭص,)(D#ό$0ɶk=81qQ )u@׉Afq1I/8^ !3~-H,:q<~G l1^.`0g{{SAGdq@PAF0P[qH w𪡺:k^ifo+6'!@a 6-cĥ#YpE̗PC@v6k6 e^eL֪XK  TH\ں`9<or?k"s񃽑9tEpn;>䓯3gάj[[[u]i۶\KQ s0ni?(z`PwoWI;У$8!zt`W=rqBmf@=\GBAfY֚YCy;rHwWR_uUٙ~֤Hr$t(ɽ uX=z?IreטHzAO_m"=}d܄S>EGER?-~n9t<C$BCH1O%ʹg_y+F_7sMJ{I'sSBQ" Ҕ|3__ԧn<0D ͤ߄cm`=$8 Fkf|H1i٢!{gN%ْqԮ`xlLAΓ]`Zk49XWUfͤkF-oS}DkpxY8huuÇ^|ӟ{Qܥ?ޙvlwvgn;m>팝Yows %cu߭ޭ3]:Q~6.gT2Mѓ5s馛4W.\ӧOVsz"ڶ=#&f xdL. wZjCǀ Ap4@Z}vG>_\HAڊ M`.#ڢ 8l*l}˻ܜvr[w\NGps4=:z;Ys=n(I444t~3>n״T/Cʍ;,&8UiY|4 3O"db't|Ža`_ז@bf Q(<e*5G`tS1֜$= Mv3Z6Kcڿ6ގs ` >|]u:$r0 (*L <+i*Xd@r WIb=0,TVx(@!^3z1њWeL$0u=F,N'0x%/e{Q0P^FZ!VkI*fZ+~gi4_[[[.h;n\# @$x<&>(Im!_&{I ti% N($Òa>eۤ2dގ̈,̽rH`ٸ}9}p61eMIMIsE2}.Tw͍2 g~gF~U0aq] g(|pl]gMdP*OBsD .TZ& rؘq4~0J;V 0@eGs >5$؂?o5֑sF\pC^tf*$2m(0O<ה'^#ȷ}۷}~ަҚZ? skid2xOrnٳrn%Px{--um}/=rҀ@VU;; :{2>>U_CLX' uLg$p2ɞ̱J[?ܬ(C@ES3|lx+ȃ]䪰=wAVN&QI.hYzCvHTȰI=T//[d5Kc&|OxL*I^˚7=ϟen(EMW*J%_LKfD5u|_ Lz  qH"33GY'asJ:PhlLD$6b#f:ƃ:0s6|ltF3JQsh$?gl ɰQ0ߓo$OC,)aOi_tixǣww-W_}5I :&_ԩɖP!Vg)k`bHx Ia9'29`O|$&*v/;S1~b=/AIE:ϠƸuzKf7!3 I$XfXg{rxĚQz5tuu5O(^{mXx< p &[5kfC l^d52 X4=SH433:ͷ" ;`؈ ӲR OxC]e@f}@x'/ s )>lNr+7nMwKnϽ~O(k'<6vBR3{34kcΕyD?HY?o&>\W?G7w~ww @$K!uQ뱶1fIȘ"9M=:*$w=xyaE 1?1~{6%q[fR-ϨL l8AN3JxIDmbuQh{w"B.4Tkk_>훾nj/;f^>\\u.yYιY֊Zߑ,{or>Kn.]Z,b@9-w@ON[0 ?Yr??s/uZ]>#]`^.ؽGC;imCpȠYKu#cDY(0G*f҂k2< |c'7c잖؅c@^,˨\W>?'%%%Y3~GiKf=vn>=]U_YYSNԴ]u]`0"M݂QA=GK(# ߉cag66lƴi  xuA&;dRZ( Vَ~/zx 0$'V UW]?$A 1*(ݳš3e OctGRZ/x_gc ӫ/3i_XNn ZKyֵ <9ءcKiFLTM:1@DveɸX'MGc KFfZwcm THXVѓs\?^``SIUy<780 ڃ0$N^7R)kP}qcsH3y0^bԱŲo_m P@8ɫӻ!xh37}Ufly6Z1s/JJlUH̅gj0_s뭷׾l;z߼w.vn`fYFɶ ަY>o,54o;6{K4`г{wħUzU~9NM =#a0 $S{4U"&,؀nP7 x!\2_$=֦cmZm{2[ϥc;ZCE:lj9"oy[ŕoCPIgVU|t̷b1RA}ژ} @2\Z&vme1* GmYBL:7@Nc@wRU%$Ӫن8v(#y9S#{LwTa-:ww߿o9xN:汕]/CFr䴍En.`|@MO>7`mbUq_%%&)M(iRwUr`T$Cւu tFZٵtpw.q<'($Y>z:zESA \lԎZ RcW־Ƃ8q__E:KA`g~..X. PL;FtfMXAĤcrSX9!Ul;&TZmғk#LRi |Q_\J$6/nJ@s1glcS/ưIڤ̑aTI:zɺh|n8^%{=_SKc>J|a ~wq׷rK9aWz`@G:͛XXO8P2a* sK[ )m BBIW2V `u/N{["G+aR0@34 =ǖ㲆W žU$Sꬾ `Dc=4"9t|3կ~ ~;C Dy]sGvm5 ܻ5 d_[?6^_3%i`N 7xeHNr!I6a+Db bz,D2 |[VwW: ֌MH>J4@|>Qny, L\v{#d^H=Y<^ B+a]Fզ0{a0f/QK"a8~ooEo1Z`1Mۜ}6ZRc(~=ʴ5ukg)IM7y3Ѿ o(ojn4భ'XZ'<IEzdZ[[eh}<|d▴&9c5(N PUBk3)9ZԚP*Úlsٚ P >[p,+h@V* jڎ%1=F=k[[xF?={xC|,&<ur:lmmK.^S>Qn\\\1)=9mmOg3e/+?񏷫ԩS?1v/y!?H'֒'Nc2 8"WOwrc !(Fr\=9pZ+DkT4\ ʤ{(`=1} oodm H ʲ)}eAYx`&xaW@Gp]ܯʯ}p8zzA]qbO$h~)'hc D+R 걍 iE%)(@!ƓRc)ǧn4.Xpp:mh8ԯcg,G^-N;I@؎U͔qTrPR@R 7K|medB6vOe R&WRwRεGux+^`рsOu58ȯr1"(r>F(( ,)S$"& 7N: uT^\۰[[H"QGVTZ>=Cyk> $]vpb I?z^iճRn=6yg)r̹̹?)=9mmdgS{뮻ǏdfUITi4[,bzhDZ"5ˠhM0p TX`Α!9|o*10Ij'e t*;2@4H[ ^of.Of6`٩O/mD[y̷ s{W9vX{WCNN3>G9M7%zg|$ ل=$nxSa`tO֌" "?@-3Q;zzJ7^-Q ԓ۠3H'=R捣~ IU (HO d1җzB}q\3zu}v06p 1ɘet=f\ I __ _ƒuu `8JfB ~5|pxqLe!`2x2D59tEeԆ{$͗1(.`))Ǽ~ND ȼcr|k"P9_msr=rxc3~'R m.LnяǟZGϟ?S! NC1xga9FV$*-ifb1 ޮqw`…$vC3P1S}bBu.$ڿ ez!1Gl!gHxv@DG~~}Gk&&J_洋b>A*ܝL ''̲YZ0hIfD0$h\<<Q#1>lHAu4eO6V%Y!L< m^:6jɣeZj[uàlAL:)`*{u9jxM PB:9/f1PϠ XIT0d4Pi-!?*FXyֳUi?}tuו h(X2ԍY$ X)W!tlY:cD LM+L '拵ŞsZr$˦k%ϣ\ɶEE츂O=;(C7IҀl5 ښ0Udb>ڻ0Y8"Pvy̖pg֩8|GQfW\qK(Aev^VOiv댞@y|^ı힛Zlxz9`/g^nn9T&)-@/x.\G$w!`ʆg{$&JZG_N|mva6 Y ﲆ$]WX*0F >snC{݂qx!9^7mL˳mP$yd7܅U]̙36k'Hqi_ݘ%$ 7U 1f*D4kؿ "K b1<bc>șhr ^U;ISqɯתt~뜝Gp8|\|-eσ`|LӘuA(7Vrʒ*wexq EfE"ű[J= ;_Sc^Rԏ*e6&e8~hZ>|?__֫-t OO$u6j$ QK=P @,Saݢ21Dr hXzfLDgA0T#Zw%6B[(آk=%A7HiNwrrRc|&ֈͮ~Ƶ/n=l-C%ֲ_불:~fSN)E3J{__cVϞ=AmNR)g-.5oҚ$@B"Ї7/S̡xt;ƣ-t/hwM‚ 걑z6X`R11 e*@c4v̼+j?6O p\&2>yhHɑDT~=yO>!v^ςIgESflC)Xҿ6_k1 |cz j-X ?y0$a0Ϫ t-p! xAX$敧I+9_1wS X q&$,6 A& %_Fݔj>,HnIfBN0KX{ l_ rwb09M7TN8 륂:`XC{5CnغTp7@Ӏ@N-D"[ >e4%ʔQʀkx9]2=rxָ @ XyC8_fVp~8*Pɽi^y c'av~Yztʊ P9m:v6G㳂9oVE"-w?~[Y%]Iwz ؏XyNN) =w=P?~<<,b!pr_ĸ),]dZcܔT\ )iw RkC1x#AD&۞>(2؁& B"-u0a|tje$)2ȑmh$,[F}3(V&2v-XH`z8[JL$ I`ql<c YZ0$cYqvl|tH<~`TZ@u6c%ĄTp?`j=ki2<7  3h -dψӥ,sg=hwH/iҼ Axncm^|'l9HLJ݆ @*3 DctO{@ѣG~{#)yL9]<:;ʹ?}n3w6y0F#`w9AsZ~`e^y>z+rє=zrǂs\ 'Μ:uJ:ʵפc%bHĞ3.S@m"`>;ޓuϟuq`,ڀ. jܥc Ɣc3IL@ЎZ+ %h(- 6!TC R]ЃG_YYi3g<`{d`W;ffVPOTOۉ`8|"W$A}a@\CAsLHfMls h/ !ClgKSAb_Y$iǁkEy@vE?-3B.5% OMi ,PO+3$aE*ʑ)q܉،#~Úi^`m-jGx+7pCl,4k L,ȩmPB-I\ #gs@cP R|^!@0N2l* {.pGEqd<2924͸k9]XCKz_WL8g IGhg>_/Z?uW$7OU)  vjn%-J^Y% šV:(2| {ŀ̟$ %Tҁ6B,'EcYRz#  /Bܺ ͤOx|`ma)3{)2oHocn1X#`%|~<T >DVɂE0ZpJJ* xC@6(xN@!6f]3d!Fv=I5Ő?Ø5} \F щG1Zxc> l'kG(kjA!.SzR|X|DRoz[. ~<ۑ}}lvlkfn˳~ٞg$۶̺m6cjU'?2jb Ȍmfd @؄`tY~w! ,1fsxnBe6!EY(L4.~5P*UC @ՓlVfoM dm `iv`ySwAX[_8 6Ro`'Ϟ={|zɓ/c;xkgfn9,2nJrݼ@!/;/6s]Y@d ,):9唁2rU*nvuu>co] z))DE  `ç>bk&iAk އP:(s !)6#60W &Y P"n{I]IqsY驿 Y/!3 ;Nhb#畻Z?,(gĞdw̖ `˩6,] ƼEFTNfΉ%X<[*,#IX^8ey<\$@RK㱥9 @]_?HaV'hDB%~LLZbT,yOڍI$% (P`Xcz_(NJ &1]A 3&*o*kQ܌ Egi=r *w.`U 7[X HRI`rnRmy1x בN6h(0X t\N??ʊۑ#G4yD F!ݸ=<9F! $D@!X*yL[qTU .:2)a iXɒ?{hyHI'JLL:PLY:pN sg} A}(mr 9 HPt`[t40 @7!7OK4\a, %ԲߔR4'/6)}S)B#'o F'9N S*կ50IkZ%CBʤc  d ^W`f@ ߟmO묣M-{n)bfq`==ZYN(H l^zeqC|KeX[ܤZn-cFٵ>*N>={ѩxCfsV/E*#y<~.$ﲏ~u۹v3Hdk3C۩FA Xn`;1 !0bXkCk0!% řQI’ Ŋ۰KI_~ ,$z1&y(GM;hLM"gk=1E@%5,0&$3I>`. An0wSpG:yLpkyL WkkkI d0Kcp~P3g>d h9V) Q@7gIM 3:*eֲP:ae<VWT8rV΂k7gc2XGm.!]_bLƼz5z ֐]B_k[\{{ɓ'O?{ӧOˣ>ZYԙNyvfwdF}@ ly|>Gx|'Yh _N9ȝ.SN9͘2ГRgù'xVk&HYt۶3j0pCY!L7օv3 * nqt 2Aj I* xk>CBC?tB@ςNXw@u @#Ba'XiK-MG=}y{C B1!=M"'fǬ)hTUʼ&}S|gJT{}H|KLeSs `”XCX4XLH:&$1So xoj#+coK%}3܊ 57 er$k>_`Ԧ2j"$x\@{y0# Af*۶іOm6MJ1y|0[xg#G [+~\Xch55(e'^RcVP >mLe׸04`62=%sbVU@l ś ]Ekh#gk/6 4/!2`63鹒^X+ ̛'Pm^<~]]]=5Ǐϝw6GV~%=lMw}7v=b.`N9-C9-9SN9-8e'y.> sӿpĉ vaf F8jZ Ƌ1Oݤc hKxh !h8+)Iւ= UH d "]ܶ۝K=$0-7 )HޒP{vƇΟ?;zTߖ<[#4xfUO9aP &֒܈1V֬%yb;I'aF*Hb;+i&W-1% K>;di1Y _,Io9b.|͏i^?җO^'h]J%A㵯1 K`ts=pt@>2|20T@1< кCg Xco0@c/zri~F/ Q*m| _($R#<"pkqn3˕#20r|wYgAY5Yr.)0e'bڍ{OOsνĉơԛ՗dztN̈+8H tǠtAWH Ֆweϊ,%~GYkA\xu@.`>vo{s׳ $;gŎ{,vꝛ0sa6?sضz~'HŠ[~Q#S ZI1̃}gm[1Aj0>`IoB@oaC3(r~,mYW#ݘu { h @C\A՟}|" s5eE0Yu4zr~ҟ=3arϲ|aߩ޼<Rv ƛ6Q+}Gz$4ŞQH' 茍22Ȍw3NLHtL*.x8e}(x > d9usScwb;q%ZІb,#k8 {iҐmb~N)Pץ?|'ȣA9c9Vki@ "B2>) I ȬT_jfX ǟ]P'` )%aXD!P7H9_$q`bLC0QY٘cѣ~>CS>` ,##0#)cl1g Hel:Θ-eH2R6>^֡, ,k[yP׺`,4WZ= $Ө|0~&+Xqxi)euTH=7ӕZj!F,.!FS}Oĵ?ydso0vD $F 7f,'SvifB)S>U5ԾQ_jk+<:FF*HRla|aK]-Lʮ@ ͙Ώ"a54\2"pfyϐ!$ي}y\/:SEswZm٧ڭ>b9o 3x<߱Pƣg_撱S? p=${D`Ri7f1^E@mc)-PWtrT5ɑE/;ţ=YE2PU"7$v{(YFo|r `*iDq5hZ/(X me~kdv*g)B0m'zEҸ>O~~+s_&`|'@ȲQ>idj|2s:5| b*Sdw $Ynۉf$Eиv*A{Ծ8 *w%}@z&v@$2I)值g76גR`P热FrH緅 lGt25zF˜pNm|"=dy 8Βg ^dSTCԝaߩuÊ!$cSm9NAF} 3WY 1g2{鵺~/gEB55bDkmT^r|OlgI<Gp`$ zkuu,C`7ԲAM3UŘD}dou*!߱Dxx{k]݂w=]RhtF<`+ǏI{9 ??{.6/Pg^><;7/Ye3O hEݳs۹v ȳY?EM('=*ڹgU[Ұ!4فآt`3)2\x N03/.2G8IU|GJ:y/0L3܈&wP_DGi?򑏬^s5Q 9i`3)XKzH'k$} L% J?(zqPbc֊#%bT5)H 6s=,T-HCQ5 (K_/4ZB9|syO P<žC)ED{k>tB5e966՛ yrɜy1,;il!& ֎'xB@,#3 JK`K`){muּ'h^PĎ|r1Eu>c%yz@v,y1{>C<$u&(!v3Tc ]'=}_8{l}<|(;<.Y`B"}?n2$sngDY20zvZn"00#m yI^Dή@ Af X" (OOF/:oMmReR@υ ł:[iYms&zec,'4zn'@Y=<~ɿ_|ͥvwd>r)r)JUng/wۿmo{ߨj<g@;oFp btXPNK&c<%?qw4K P9Zعjo!2,pɠP8 {ˁW|28p:DXe v~,Z yGA3nsgm-WVV=skE3zI\Ō!gT[ }=.y21qI{qO^S̾H= *m YDl@ܲ $,XB` G2i%`ko34#)6#˿a`נ@Emc6ٞ$舍Tژf@tdG"{fas<_Ւs'G2nrNyܵ4 c˥'.qkұ+GRc%- b>$4'?\uU }l6!O)}@t\`Y<1f[욇&$جJJxHPR8Oie ڐ\hX3^x E{󓘃 ϪLֲ3õK=h.G7[IEAA9nwکS+qi{_![p}]}WGry,m"<}nv"E޿6y,zNcRY_t>SN9SN˙2Г垦?яo'O|sBK뮻N9>uV&w O SXE 9ǐTKlE - * -O0+ %S8&gĮcyO =H& Aيk*v1guc<`$VU `ʘ`8Ƿt%{̈IՃ@4ڤ/$>oʞ5hT McRZ (ãVcC 1/cH#䉄&se0,m(0? VX;By:R$X!́vx>l9OXic9r1-XǢ/I`⪫ZӵÀڞh dJE @yv={M{7hA2p&1%WIgӀv ^|1PZO<"b8Ȼ1 ؛ Zx#G3-Sa_uT?kN:uy[;F o|Y=te19vzO|v;֙νyʚ1g]Fd$59SN9ӾKY-E… I`ƿ < wDI={POx9b~ !kcgewѨp}"!{Au L\$ ΅}n V1AkKLU.`p$˧[X&Υ>툧v"֯Q L@Z Q!Wkf$x=*3X'c֓ݧ?L41*$V0ֱ^@IhsVGOy&(;>=p3>PUמ\JQ D6c#F4#HP%00 H4 !) _N|df02N ؉y5G`n2PF˦yUH7Mڱ/ c'Y cEy` l;HQ =&gy[WTz`-|v|Z/q\L$-u. noMÃbf865kg"7gnYA3ɶyPNN9SN9tY,ݖ~Isyޞ;w?a%H"b)@:ۅwA8sH*Iʓ_+PrP|(cRKm<֮<}װ9g!vlgN(@ECHA?(mA@P :ImJJU[*piJD1\@ C88Mb;ϸz~o=;Ιϱ>{5|뛖|1JzkT4JvNhW# ,{yPȹ?Ih^vQ}%&m*~gcJϣ|55̸%4Tisӵ^۱H .ɿ|'d0)8%Ba;=tyܩW5coƘ}\'xdl Spp-oys>M6FNv{uzW#=:58}+PO^߈XS݁?ԯ1`Wiӥo~'뮫⊂njwYulә z"YMow쳝=zZ%ضJ]_Yqr^HvB"DO@OOvtͣL'NhsiNMZ|{t. hIsXVLI>=þ `]Xr~)*z q0#`/ 0["⤆HL''VǞ qdJዒXX/6v}H/rτйZ@`K)F8a_7Ϲ:,eG$eee u,ǎO;fms#LaIؔxL%q DH *NNJh+X?zA !K0ˤI`CΰkLf SJQ&4Vͅyەccwn#A1!6O9 SaLfLxu\o _?Ϙֶ1~` |Rz~"c6 3WFSPψPCI72d*1¼oc):_}t1yh|| e6eY6/fRn@,'uǤDـ||V)6-.cZ|?1@ENZОu#U}ݗ(nUlݮze R?ɓ'6m7ȅ ٿgm'kǮ|m=u7v8Džt!B"DmA-k//C5*ݦlG255D̟{b[ҖX{j[ GNL$ېLH qS{F+zu~(tR^iy!c`Iw1'?#!o$ 2rxsj( /h6gB`>@;,5WAcc$ ̐ĎMm{-p^Jss?9yNΝc 3ŁK6 ͪ.98_1i@,sP#&2x@MO1Ac /$&cc~@چԇ(pnik7+*"+`l*伭tt;58d xPlG1>>އ AE`tL}:" _}UpQ n_mx[gYn-ԛڥVr@-}ͯ "D!B6?T{9uIxΠђl#jm(8-LU_cuiu(,|3' ,J$4Z?c`6HoC( '@ 5ܣW]GZaxdQnPIA weTSƒʀy.1빨K-ϜIoϪ¤=K_jC]7%!sf?Mܹc+gTJ;TK/MͧHF$7›g%o7F;"ǯg%J*~'zPg9Y YX[˳5"D!B! qŚ TM5裏OC IO6ow8\3/c? Jj+m)*HTV; %P+14!)ip^(R.4&&HGf@`)!wTG!%us 2t]Oe]c3YBVfcv8`8?&Nn"ioŖ%03H S'FY+ƾh+}&w), 0L4`:4L0Y4Ye#ET7@t@^ a3ȸ/#$0F FmIcu=(l$@:rY0W9ǬFu$c2Щ< a$ձҌ~ v1 }=Ls?ssW\qy\@^+b~by1aJcIe& bQZb  ;OBu 4j9EMg dLPg2d&VT70I3.1"kR@0=~9߭gYF}6'c7zX їg@!B"D "Dx+,ٳgߩm9yd-ۖƉ\$xSa8lxi q6},w&B~w Vׁ%0 &IVB씀*3yc8%o]"JzQD+" Gz& Xq@ +DAhTe$qC`' 0bF +Ұp SO5Qu !GKKd&{'=XAuP)};ܟ8Nf-=@s:[ %`y * Hͱ@‘ۈxdԿYՓ;.ߜ!j, {tcѼ~H07XhSm4%yvt*CD 1c!ԅ4Ǣ~oq@2Dr4HeN  |sc`7Jx?#?_̤ UPOoS?Za}݁&VKW'1[3dЖ}6y9#5gIS17"}8;~]U3c>)CgǬhyslzΪ.vF$7ɳQrl6j@#Gd{y g=>kn9߯&"D!BqD &G<׻Ç<ܣG*sCSْ!X]:HZvCOͬ&t<߷}rE bHE^;e =W{Ҁ3~H.$GQfu7')@k+i 0KLHe[֡3icNK9yW?k0gffuY\\\یiUDjj3KDKC@; MnƤ:ǘ[V͆y?em3Wo `L'h H# gv2 3P)dhLB&혘昈b 2(ڠ06 02mb:&ó ֥c[j;^USߛI)$HV_hllLWȃ>}衇TsH|蘝cʪUUegO}ẹG+r/`z9g<+bn+ȳX/`klu7+"D!Bq@OOg={T6{Q6tunb`KBI,cŸD?H#en av3dDdv |M|t@"D^#CB7DD1$؀MHRL%cJA[`eSf?,Ozkv:ꫯnyRWx Z mj{4ݐ,?%Ji3 ^ǖ6c%g6HEDp zDu~;2[ 9G/=k%q<=)#tMBY;vm=} VKByVixMB0ߐJd6Ώ9,>RRۗ4EH',%:oSnz~@NLHԁ,nIߏz̒w29 Jk7=O6ܹ39pyuϪcM-ι CcWTχH<<9ғ$GlH +bbEidrhWV=9 |t" cFc^Xd}o\Zoz}Jz@~/{dC:(2wJ 9vz`6>w UT9&ԚSmqkW~u=_o @H5&-~#<+wYɾO5Pg=Arjz ƅ("D!Bsɲ~j6Mtۙ3gTT|_W_}&(͉:(UxmT>$!$]JIi ˠ9q"Z-$:  Xt3dKb99wKBT#IQ%q{.֮cJmD?OS8 &|D> 91RjyE=Li@H&H!\~ VID'ŀR h6`ْXDsd@fNbONev擜}dv)M'e6bobtma&Y Uk}EuzQɵERGd`[duz'j?o߾brrRyz%\yK_:~^z]iN@ڼ[2΀Ę~Ip fO ڴ|&WOstCsOa̟M/1X&K1nnq$5) gN9AS^lKFP"-7y  qMljr+ ;o*^=` 1X}l*hWǾ/e7KoL'im~Q*Qo} B䚀2\*611V5ON:W#xu=%/xomkV?5˧;g??޽3ZlNh=wὤ߫|uZ}r[[>ɶcVYVy9nyd߭Ji5۬mپV)yB"D!k>|}w:}B%۪9!Õ;v?L9rd|44 ޮ4"2 ,%!RcH! _;09GIz~*|HPi qBsމ$sX-󘱾ʞhίɀ.```CllRouJD p.[R ҝƸ:/#c)\Xd̢^a׬{tc e`w mjeV.30<3ߞרܝwyzMWZo#aN6.uGN^I]TCl,賒c]ͱ Z˾.+@S R!B"ēDzB\lI}$sMs_ˣ+o}t:ӚDW{%8% o: 1@<fتgI;! ϗ鐗JIHTdqyG0GbXN@Kj g'mC('Ɣ܏MS3n4+xI=4̂/fڜD _'BNI?q:/Kq0}|}Ӆ} ['f OoH:>Jfdl$$JrIԷej0rtc !aߡl4 MјǸtrxIb42:p#i5N@P^>ÒƟˊ.y,5[\\,ig}`9y%%6{R@˩s1MQ@z]!P5)n&(횐t͟)Ic-5^$FaitB 0"{uO lT.R{j66*C xrZ7nI@.<|:c*&&)w]wݵ+xVfXgaq1o`k zbvIm%΅Y hV@kH#y-,gy6wӅ'D!BbKx`nnX'h4n*BMkCeMرcK_eǏ5Y.QE,ȓqr5f1.g:I"^*C%J]"NkXR Rj2= w@11,*i(HfR;l$"H6@ʬST '" &rK'HI+$wB&`dIEro]"ԍXxlB?g1Gprj< f%$_Z_ }$+'{Ffy6gU06Q"m-ak@c/Gqn030OAmy  @p.7$p" tfd-⃣KcnZ2S(껣[ϱؘ2,T_b̍/* 0]ǎ=:3;;;T+:uTy7^\HNlc0lwk:C30fq@\n ru b mI<'h7>!DD#mnu6~j߀`+դܷ+02d(s45/>g4Jb,ۇ YY 9rnm3_l_ggRLzZcu37S2Uߑhɽgw,͖͢o[w+qDG8Fk7s[ o "D!BEzB\q>ϊ~(dY6n0zL~G8,j락{>ZR1=N?D4%EH. 2ugK|B'G@UsD8Y3^OdАr'!ц1{ؗ'! Y#'뮻nαcRۇAQi W9M\ ,..~#j*?;;ۯ^JTU/}WG]8$5' Yٌ!aJ8`iɑ0e(̎/ll ;%c ЗXl,5fI.XU~1 + ž5f02LCΗ+d/CO)9SH[Κd@a^ޠ1?l >C߰`N齻AmgKIxs+krrD4X_3H9@f$XY`>:2!y nC،1i&$9Xӑ ZMzl#CI5 eT*ѱzplR@UGq{キw^'WHq!ƈ/;SFOPOV t@]uk\Oտr"z5WQ=Co*V'5qu~ZšK(2Z`T1#, ? |aH*O9gxڠ?MR ֧`yrim'%ؗސF: ،0}v*,+wƣ|"=(u"`i,E 旒El, G;E[JF 0qPg߱c =ӧ󩩩[oرk (S#$'Kin/H-2& ؖ`$Iysv?lV)J:D`PEem~dOV;&ldW=.|` 4 rp7}alc'p۪_SNHڱ O¬IMI?Hs(3i2bm߶oezi]( "D!B`#=!.ĈpH 7] F<Ҥ *C<[;y__+س[V.0dRW d DrQu⊒H4wXRKڨ;Ȝ%Ti#ĺG.K,rjB̙IW] I2cCVyIà{'67mZf>+޹ŲbL;7\4ARV}ڮK~D b ̨x{mHYQY=>ի[Y Pg+`kf<<X+ҝ' xw`V󁑆P1D4`Py%inynoL94HT1WaQìb 1 I3){u|_XؓI1bIpUg<: $|O'9E7 N-Ԑ3p$൵]H),"KKBu,cX-W]'N(ٓկ>o>ejԲa ,P݈Ǟ]3@ P  0n0YlHH' M?$KHLfA3VbCyo$<YOd/V!<[@S}p==yϥgΜmLњg6nHkxP?VSfIA.oʧum ,+Ygy.VV}ވccvyVOɶ!B"D+X͏h#[Ŕ ‚&|,)ԟew_o}+SiX5bh86fLjjLsµC6YCp)izx +3X-IF @S"K ݙ=n.L(Ģ@K~ʽ>%ݜd}dBa9N @mǍ!: ONQRl9:% @vs<Oj`Qf7qZBg>`dSvVv 8uMb=5X&WC +LKڵR*=oh\>bB~E;:$rˮmuQM + _2,IVH>1U'Wm5$gAs5|Zs.xsUDmQϙש t0'aީ[͐4$6eAvZ~.MSmٳǏ9?ݓZ^h!(O׎W|w1 RDuI!?=|ʺ?N> ȓ{n%׺r-8Y6u!B"Di[F9_D|]$\s#Q숳gj%T_C3[36667;;;w'ꪮIt,md-@z3RȐ=dy+? `(%׊]N: qi7w]ּ2 +H,4]$;a#vSRk0SNLLo|ѣGw'''~ٱ:YIʪ/6&34JOzŚ HZ-ȴZ@M|tS3R_HL}܃YDu> A; 3s, ̡6X?Gwh8.-v2i{>'D7һ6꼠qcBlaׇܛ}rx@P;xfI|^F2dWO}]ҹ pQYbI[+k_;k׮Ԥz;_G?eY @fyrf7%%3Qi9S2g>摂7 &E CMP6C6f)I=p~5xM9yGr?rVjـkqx;}t4Ut?utjg=JaAnsNZ%^et:\?uI.N$U)7r Q^e}6߬g%4y ]7D!B₎ 3mu7'P-eH]@3i=+vvz'O.#GTmؐrf( ,;87)?H k (-:`{bg(w+B v`8yJhIߟ@%~JjDX=ȁ%X2}X ׎X4ޘe 7f4!3 Oe`8 $c0ɩGv+J)ja,7xs\T{y^ `fHҽL=0 6:fFN.y ?W?uutn.$֏#й1p(t"&Ԉ,a~5-H@)KS:V|"u0D=P]zW oxtdl1a  Ԙ"m/D;;_Dr[2'?Sd'|0(2SZ߆U ,`?@ts@sОjs%NJA9 Tx?Ї>c=6166Qdd,=! V¼Wo1kC]evRŒ9S!>2εBmrV{sV2ׯ6g#oߧ$B"D!b= `2+-Ɇːs+ir|#!C#ƃE(SBV,@ .&1Zr?4h:x 5kb*{"m*ع)%CV^c,ʋIB %,ʧ?'Nho s刓s>$\&9V<-@rb=E܆v\nJ4617֧2&sbV}x1&CU|,>O۳gԔ+`etdaFS}/HðԄ4,@#o'WO?POLLzG>r<3JM#ݶQ<'dA‰}PtX_~"%?j08ĀBRJ| [ D!@ 0jvioޥswxXyxUB r×|x(gSOǸ0K{ᣓ[g::^EJ1+hNϭ=/ ExlOyo{|;r饗fRX$Ҁ;߼=k`S,c|೺}dl9Vl>=Ndc;+پrY;"D!Bb " Ub(ɡ͈ney>2UmWj2L؜^Q&01&y8wp:111x+_޽ɨYbcZLi%Ńd'V4/IЊߗ,?H,C$t7I%tKl@!Z4f,^ZH{+%Pd*-YVOO'Ki+.{k_?'~3G#Mӂ($7[$@)4`j؇Ūv$sxǶzC3R33X: W𤸘9:~6H٭16ԫ%Hct!"5ߝ}_b{IIzP2[ e-+"#$ci )̐*D>ϢQr0 s$Y(_4"(S K1͘S83ΟH[-5t`[nI sMFEb` IJS)hM ,R 21p6cP@k}ěs iT{d1Y"Ϲ'=ZhV?9VҢܞʒ:uǟ>O۹sg4NrdV.Hᙦ7|2q:4k?G:kj:W_83=Gl[Iۿ*CQ^<*uN:wż.Vfݬm}-߭t\:ﻚ!B"Dk / 4D@|4 d vz$\>0>3:QTu.⮻*/e2RI<MBc3%y@=pP.8So! U$y5$KϟUd@ V@'Muy.U5}I>("ıX\ eA9b"ݖ%zֳ~mo{wRھ31ɂ;Lj%]BdDi21rP/+|&o,Ƥ>2(/.a0$t]@j}/!WhjCt.AʹL=1$SD+ $m,G^CNOACr"uDI{Pb8$1a*gNy*#F̥O΅׳}7kZAk9f g3'@ "D!BqAG`Q~8tjCyeLXb87PG>sil̒~4\WIu'wyӛ޴WصkW>.ȉ)as,VZ6s#|X"ocX%)t'UòIB2o1l%sE3PW 7%WJO$ g50Rv|+_U?ַu~vѥ1GIi$2{ \zp_lu!yxg>?>{Zqe :Xg2BP,H|z* PX/b orOFƌ;0MwǴ0*q4*aBes){ۯѾň>&5Ac(XatWKc\Su-Ʋ|͑Bo߾ոoᆸ{ʴjAY楃Ę&1(|sx^[ 2gv/ 68 <'p|b4AO 5}j좜,i@ ={d|ffWSwyӞkdjϘ7K鸴Y};fAUv~eF6빔תż8ѳd2Cךp^Oj>Z>[]ڶeJY;l "D!Bܡ Bll 8˲G#ܚUfz=VC}M&y_Nj۷8too,^{ r ,%JFGT$wO_}83%Flb%pi'!Җr\,+يҤ|y&"ds=cbA9&3%9d{w^K#+%(&^WZ_ͨKilpA> cI=ܾ/cuKB _dϐ%נ) H#U#eJ0J;$9]_.y4J,6ͨ }{in E:H+xA;+gLjxPd@.z[#yL*԰~3lѡv,;-c 8p`RW\4H! E7oTlj16 >%^4" `̀KiNu 79%D5, ۱ vMU/)_fƼ5?Fxcsg?.rlj7Lq .1uy.1m΄Ťqр3 Wu(s=ZU;}je$W-߶zFznq}VZوs\"D!B"=!k8&D|ĉ5u$# pIVi||vQ"d5Y">ѱ <)9[ [)^$2(YK[M,&0>,V'2~.|5\3&~dh6$09lyX.My,FdO9+0Z ^F$_H,1~NB@P=fTc0(6Y| !FT&7 ն_Eڠc &=9>7'RwryTJm?DF"רN_<9e5Ь›i\4~H v 1f\ ڼʡ2\昆= 2c:**e 0'my$KLA 1 f9ٱK~iy3EiٳgϹΦW_}5@{1x'~ A"}+ ok@:jYVk]/5_qkǞKx.*Sv 2ZVk5 Βk[sOzJvflwj]1q !B"D]9qС4ϥ x=64$&̟GǍӵ.qK-̍zIfhnΝgϻЇ̙3*, fO%M"5H:@,l.خ&o@!0#MRqPΈ$k{c|4) {<",&&|?@;f8=@Gn|qbpM7]-o[Ka=K_f,dp$}F'K:F?dԏk=riyv0[ Y Bdc${v\`O}ceb VaqFJL9)2fe ,=}8i{%x씞sgIr(~U^"'<@L۱Wn`HBA,){LLL̜%6jCRv߅1Mj`hb})>(, >8wBl5ΟpS`&*3q<- 4@ȏP}j Sﻊ__ʏ|#+pR=+aB3270IE$٘}yeT 3f~mP9S٪3TggG-Ň߾lw}nŶ쳞:wm;_=oOy.k"D!B\=!Bdԧ/Wte14sD.ܚG !<ѕª IKq恠ɣXW߿~[2W^y-..km}+ۖ[~!2|mȪ]h2m}z}VZ "D!BqG4*"9;Lme7yz]r}8t|h+{4T;Vp嶍+M-{nFM8MӧOۿw??=F+5b H;$:HW,%%s%%\ Onu{Cy1NeT~%3Dm~΄<\I>ُ~_~{s*vQ5q0:۸fHX0dcƻ?񀝖lp=.>xߗܷ|eh3' > %I2DT +q]%Yn\Q ,>w8P(=GhԉDǔua\L4URnҽYJL qt 7_ű{SɥtC.`۸boxeV؀3""8g+s[?0@a4tʶkRkR>_2{(L~ڷO/T.El)ֈM|@JJ8sy`:~q"g1(2@ =,`bY]ȅ"hVkQsDz]NVcc0*VU7#w{gg?[ˠckGFu?*ckg.K 9=9Z?hy 3+Ɓd (ׂq‰وaٰX(1. HB=1e|o<CȝUh<-%^N@&0YA~;"3i7/ %K;7t^tcMĠc S^r5Kr5as-._E fNs$ܽ5F ۡv8XR1 Xp]''H,9'%=S=NV|p‚:Y>@Y:7+쩞>sssʢ>Xˣudv-l1(s:zNr'm~WOUWT/C?v%KVY> wBgsų}VJ]1y k+|ȟ"D!i':= M]{j߾}Ç&-{l?tRee(StA"xGw q&j]|^GkONNv4%M7>Ìf|N8QI6{|ٵ,Z`GSe#ϑsIyN:?g@9X;!b ^7M?O|?|]-޽{c[\v|NGb91o f˰WOy";'3b>XROt U22J[ Oguc~/h&Q6hDJڬ/}7XIJhԩcc7yr\P("9]YjBd)H>I[o*i:>S׋Tz?bAX@_BjvbPՑ7Dh K HRj9uQ M wbgɻ*1SxR C.m5qa3 Sz'~N:|TK 7wRN2nE}fggV(Hc`^f;nl'9zcҘ_qW*IT^Bg~_)'z֚0d}a>6KҊ%$ppX VsVW?~؞ÿ$r #mVs:iy􌸯 +bf4l煄1#CfQ ƔB2dICf7= ۋyx2l!N:G1 e4+u\Nvt[>ʼUfgg CwOϞ8Q1409==-=ȉ'n <+m[my5pvx*>k=ࣳEm-my."D!B\=!Vaу(gO _ǎ?~׿u9s挘W@_xLzu¥13X}]m[rk6웁M eX}C6}0 |X Ϟ=y _INjϾڣ1Svg>ܙYRJϭm.pgh ?4(GdaaaqvvVPgZ1J;isXYߏeT$q<'Ʉ[ۡڷoLMM=q{q=,ell3 H^o5\=U޶u6M6☵쳒VzQP~7'!B"Dm s=׾+u 1Y[hEuK1 w,&3@, 1OQz;3BA_{礮VjV Ԑ-.i*;Cu`/u`bLu#M"PxTtMf/T̗+}FsI-4ĘvQYb<086ILF+H^򗏿/K,ΞI+:*qrY4Hܘkb.s+3@0jj`1;ͶE؛3"7NfP4m^qKB`NX} َ;#7'Wb :y~EW@?'N:}ᩅ zʴUc:'*p Yk{`*}}/gJU)T6Oe[ٔq[;* U={_h6Ps\LnOfn1knc0< "D!B@Ouz4 :|;GRG}%fɩ#y/:F u=0VKXo^:&GKPq5GNjcgϞ^ <5)yIk_i"#iA|ܭxID <0f0)䓱kgŒ6;GG;XQ%gE# S`xeK̔=O}S?ءCW"gX`R-THJWydl~9!sz@RRlc֧&&T9V5V`i`Nt`Siu98>5H=JLϭfDfL%܀Ҁ1]I8'p#GN?VO cά|cC|g֘4j@Q`.P^$z̫ Ѻm2|g~~-5̞8C% )9,2mIU 'v}uNZ߬;#CٶrkI&iDdV \2mko:υ^? "D!Bl@OuhlsGwsࡇrQz)Cz X7D[0i"e'Op 9jGo8ɓ'/ꪫu_z=5du"Kb"]Di'ͥ 0S}~Z4%V_Ǘ㲍 BŻh3lbF wfÀP}̈)VBPEXc~;8/su#sXu1jC$k97m)5KF\<%۹OMI3qc+ڲ[Z>9)vw 8+EᲳKAr/ijc{^iVd:lW*36 ։tL4dҰ:xBkI.볐3~yM7P+hNodsRbAևK$ofc4&1{YX&&3S$e}{>N5lG~2b~"חM y&rvɁnVəE )N N%T/uӧw[=; nv<%c q[`1i>>CʲUlΡz-..*koޛHgb/"22{f bMY$ FʰkX^ږiA R"MK#s(iH3=wwG^~x]UU~@ xŋ}fhc4+>܁ }&qz>)=};6~ A6`~^=mvF^ ϶ؾDY{{A!"D!B@Ouz{ O~?tʕ_[)PpE$0Pf:-ʱ)۶NgI1$`DxH«AH8 n]|=O?t,IIڑ{y,ÿ#5S\fG;|~(]̑'y'/S,أST9~UKl4u>hFJ4n\JkgUR /~/ӟǏ~ٳg b.N+lȔG=d 꾀yH7{$<,o''mCsGODA PփI&aFpGр)3fbȫ9` se}{]5j'ěa)fM<~h骚j/_7e"%)I~ T_y灀Ucŧ&'@PC7}2t`CVP2' 7 R?1% =or?4q*e /Q}9UfLQpDr|7Zk׮Mzz= |_uyOv` @#HI硍1a >D[~fzA--ete$zL$n~>''N>H"vˮ=5da"Q%1ɸ#_ZZ:nOx Y:q+IlU"$ɹiքQ [)x*KT(hg&@8t9!蓓s{!}"rdM>sgH~8^PN@y^g^y啥/VWWc=Vk4SCH(} [&|0(U$.5K܎?ٺDLSI*ǒ>ۤ۴ٻ{cA7Q+Vfe ۩|+YK3jwrȈ `AJ6 u0 ,s*sզllXuXՆB?>oI;^kFɥjIɶr}SP,ѠǏTX>"M#xd& s,'&'㖴}(]#]jfÔ7F|m'G')"3's> `$:PF`N?3N)97킺+'^YYBGyt\~Tu2iEʭG8J6aX?o^xo[GF&ft!ֶ'w3goe>cvlp簀>z8A w3q]a\Xȟ"D!4bwf^=H. '?/OgȲl '$Pd{I;~ zLC-0veүOFO`rk@駟?ř"Bܫ6(ϫvI͗颤O~Eu0H RK2Tx e># %MoOCRwxzzZQK#{?iF0w0IS%m?'깓N\䵝Ғo{$Iw%{&V}x k`s)iGb?U__~}w[{!h4dȊD@hз (+d< 'Ooo}ꩧ7QoDI1g4r>~l*#J3fH-or f)ppO=dg9'Ces,)6FQ.J#&c"yr^՘(kH0Tn3u_~ c"{wd0 l~@-uX0c PZǀbǾﺶijcpl1sِ0k߮<{n;eq FZF&O d$wl>uT}nn?Ͼ`\ޏt^&|Viq/c/1}~sB$D!B10GA.?ԩSAЧ d[lfԑoF2OݚLItp*sxΈ\`ii??]{]kZΛ( DfQ6VR^6ERSI݉|&?$<, ǂm%A%NKs^J$b hQ[%i9"@:^_]]x^g>>hΞ=kfgg{/$,=j6^ۧm P6fӋH"K)@c!oh)Ԡ]rS}`[[[kڿ32Iy6Lг'NׯϿk)߲]ͽy:0x1~_<\=P*LkftYg$ʹA^p^>2lr|N0(BY/|~f~ӧPUeBU$|m^9f;;H+iIm>:-Aj*]+_$3 73cd@Vi('Dg@FCXZ?A2@f(&l\sv˝͛7<Ο?8yd]r }Jk3c>!Cn*|3PQTYeO}K_cLLLLw(idpkxNB ̔aha8pV97t:P)w>DV̿UTVFE?>1}r'E?09x)Ыƌ*mCLdd(gkk˱&)wU4N?sf$gVO3/3 fff7M'|roctQcU!J*ű?BX'VQ$M&c\q(lusc4ʜ9I0 }Xq/>@u,<٦vx@?@7R}#Tuȝ-//3e)',{|D[Mջgq=:[ 6lMMPzs5Ⱦ};i>xgeee~rmP{g~ԩSg?~^5e`e'g'gБ(qViô~ٯmw\ ƔpX=E^Ɇ;H6fnP>#!nb !ޢl.m3 ±2rͿO~>9H2?VM_ֹ`v3k @ DlF?}R=n1Qu9n76y}鶜U62{#<Έ':wO*{Խ׾+B2à %؜@@룥`+ɓO>|6i3gG?r()7bGQ&JyZVW(EgѤLɔ|l+<}ȯFX?uz,|}xN7&˔/NO 8"xfXҌ%,d@$S56' RX^^X__LRLnx%,GTnF+3:0c#̬a#h r~FelC1e &4PRt$wRʶ9Ԏ/ {]Sy m>vأNOgFllnx*v ~^А? "D!Bi':={LŋgC&v ,.A!CG:%tL|[ хARf kddJgodzz:,Ű)8>S>ԅ̛blbaWa/?. ._S:V3o|G u)0G {_ri̅ⱎhyd% 7GuO_[,<˾Dq=鿸}>4d)a<u^}}&᯳5x| N yZ̈}vm{>H;mwi{9樀5Gg簲x؃(`.6OB"Dc s8=H.3ΝЇ>=\4M79o[,u ٴĔQr8Hx(0$-c2{r&찾NVLےTlcc}_HDz`$=KO""W+4YW{3/#iȔӶ;^$d*3e9;瑡fk 7oײ`|bF*"Su`RcmwMVW^Ygv7~7:333Nwra"v`4_XB.f'!s*)!lD?# M<9;]Z"P-oq@ |G7eӞ=rRl>ʴ_˳<)#yD_qۂ@Ox%R^,"3P[M!e>kPP Ѳn9Wa+苨Wvd_1f#L?{ubmϞ=8<@].\h7J2H{<_lʠfT<P3)ӎ6l+|wT40S0ZX)Kd kү D0 ZNNL@#1g\߷Klpf6$C# eoPL8>rp `|Dmų gl{>l܍c"^;xڿ'Qׁ9 EX-)PL@ S6_:'!n2mt }MY׷t㛛x9@dْcǎʯdUSfT1z㝒{ u$8 m_>h{?qUQ]tȟ"D!4bwp=>{]N81}7_x" &]6 ww4xغ<^)HSiu`?Wg0 {;gH!\}6z3jz}}"R$#!) XK'F{bW$d$o |IWR1yCX'^B ,.|{ /S IJ]fkӗ^zi.k_WW_|ŕ}kKO H֔HF7w~'oqv<hY_"&Q㋴ȨO? a IXU3%I1/)C'C菛nw̖\vF> Y:5]pr3`8y4z>Cx11S0Ha>U=?ڦ($d(9QKTnW]EۧH$k])5ymwew󝳒tG{׺/:7on0p]>G0{(mat=oă 󁷂݄";uȶMNN:|THA<xY:}>sssg6gܹs{ >:ɓ'"vJor$I\bd)Y<{{yD M4(sY`ȧ h$`<;{cAW n}x_'LM}8ڻ $#C}r?Y Lmqpp EIѧe[`K57,9sǬwW\rZ|Vb@ +g`3E6O]|dJ~7.ȷZ4}pm c6v{"@.ѻ {g ;׭/B ly2w%fqc82r{ٯw=J]B"D!BDYhwaG"cdy`~~߿+6}|]c37wHA:i.-&6,kR )h4Hl"a ft8KÄ>vzO>9jj4UKn$T4o+x 3R'vbxK~7xOMzRaL8 6\@mLXEдFɻi>[}>{Al"@'FˮQKĪu(깶ֿ|2p׻k6???b?XC% 맙@mzshnnݷ7oFyxpٶ3ZɓvrgΜiLMMų`(EuU# @v@HU_,32e&0rJ`LJHMSp}9|~k" 7ozl98FYj0"b<'Rqq &)&}Y"#'~4J@7$ ^7XYYCdWkv|o}g܍H 祃ky (<騶q>t{ikJ >nٺӵN.xaBIk`"MF {A睡Tv.RmhL~h]+;^d}oݺU 5[/bqr<;??__5l,f4:aW{,ϱ+mYث}瘽8 < uyp'!B"D1]9@b`.^= vٟlllNދg_4WͶ!{s 0ɸJ/$ B oC9rR6$RAB?q,ܬ jr9` SO=-oFϜ$Ȍ.&훓 Ej rjwσPg1)^ BƟoF7\T5|1omm婨ytb*MefhEQ +$q1K/xHkUbyy9vږb(Ƚ@DfS{GZvc)}HR!bԺԽ_QDjbigo)oTȕK1DnQʒE#cEd,D5f"24%`C*6[4(U">Bj de<'P <J. fxRa ¶"cvu,u3xl"$xdEf$'2}+.zdmÎ}L7\{&s(a:wͭ;VP/m# 9Wt:KKK}wY KۺLٜW;;|Ts{6~$5}._؃|v9f/Z}\CI!BbL#=!va?Ѓ-s.K{zz4M )$?+H!Adit)]D6jHHr>ޣ!PIO<-sOf qadFX;xI'v/I>%Pg'pdێr= kC;1taDRk98Y3l<`roV#rmgTIoP0ivs2UȻ&pgTj|CI媿djߧE_ 'M2!^DDAꘘ2%W-M~C"oV/Qr⯢C` Şrs"&@y(iI_\E%1+rcSf Uv(R@*K0r>U;jD-R uZ(:&,CXyׯZjS`wЀyK'P}u&sNQTSP-QS{ X ˋ)q*'7^@r ı kFX[[R_YYvu<`Lј\XXzW31B$D!B10GAVd܎}7|w2ew8SyɩUNwK "U"3`jm:dD\5sIgE+5<9jLPBhB6^AR=2tCH/IIqc`rtABI@ziߞm?vS%vJSyS|g9>&Jɠ;A^?3%}i`œ} ?=4&aR糢StJu2D$YN(^F1\ QL3#J<$J2hRbE"ޔU4ΝpobB2=H'#Q92 /Fɠ)(W|(bż{WI1^#uz:K12}Dja>oFlT`F>”j6r'OZ~pLX3k]ջLmD[܃2ǝsFaxe{=f/?2b|I!BbL#=!vaЃ V$y~~~z`$Z fwδD Zm:XcH4ۜ&@NgI!fIY9@@D%Zy1LXF=J5;ֺpΝ;>;;6&>9{'Y;B͔"U)8t&ZQ_"e=?`9r-ƔIZNmsygjoE6_M%ǻ7)'Zq#\Sb^ $u*c$c%H$g jQ $fEJ2LZɁ2dp+Z1V -,R̨*):mGl*uSSh4'JdrrQ=(Iy9 ?JϖC-m#ݾ ȨIЃq7nk6HFn ;`5nY=i?"{M8wN ?e0yw,>ElXcx~^侹#X INf~?wu0e6۹Ͽ~߳qleec̬Zϙ!m4:vW_Oϛȳº,I݋l}={HQ샸/vL{^Pe:GÄI!BbL#=!vaЃ[˓vieG=ݿhdVGra:^a5X{J**w˨ʧ?̼L7̑wУ)1Te}[d߼|fs܇<y@cFL?Y4E9 H݃c7=yr#& mJM\IiBSlSH!◙&U 4`F TT pC""} !) \[DbǧZrΌ@$ps.-Zbyj?6M_!*5#_LHb<'%]'b Id<$SzNL) 8jte|j!cHb'0رj/^"'P;£Kd!h38=_݌m)}w|Dä d4Y:ڿ[dmd0<BXc LVL*mbWQisA-?xA ujFrhv-VVV mυԚk˿~+f\ˢ1vT%B<Ώ$Fpla8 XfYIxIN8Ϝ>}xՊmS1>3GJzi(УCN[助t%ȔmnXىcFdJe TH)FfJe>ï)Rx(Ez3bg$CDX<s&S%Wxx% `ܓ?s H$g<3!Ϙb8 2fAɾvC5HOx!sT3yvbslLh@ϸ:wϸ=,ay2m{UAy5> '!B"D1]9@nydi^xY$>HvX0}v#g0[ %fPC@GX>nk[vq<=ޫ%6E͌=H9f|!h{>E/udOLOOoy|_'g'u6#p k% ǯCUy1H1s4":J~=꼅,>¼p1$ڄ)1IJ,Sy@' I23bl <(ö]3-̏bF0dbJ1Km Gc%f4Ȍ%>YcĪɵ2D0@3l=ۤ)C $dX%Gט| 1cFf*PKڤN'<)5HeNꫯX]]= ,h傿V7:ú`qΓw~C)F6' FX51sio(crpa2zMpL.J>@hL)#LP@KN$:|hc_`̺QV_1ylNܸq#r! e&/{53y</3K|~ :ǘl`"$B$D!B10G葸s.3Zv''g=#ᅤ,ftxbbbI$I$Kـ <}"HF6$PR%/1Df$/V tIFP:}$v 0gY2'nϟ}ϟ Hr QYLK̊EϝL$v}}LA)agw%o *i/R]@ko 3b4Ptz_M$ƺ kE1Ԓw. <( .bȟi/ym&ck)FM䵉Cc]c $** 4y\:ۨQuJ2/}D˗]6s՞`t8 !Ё4u׊qk)1'Nʩ Խ8]%SH> $ "h!הL%D($ַIMߜ0L@3@/'^ٶD0g}?>s72C1yvZ2SٶM1w૳^W?R6B$D!B10Ѓcg G~G~gl;G|ҷg FuH`FstJ=mʺ+H0PmȮ)݆ZF|e%)6ẙ0DrCdyx$3('0{}>o9qǎFOO2nK?JN)U<J4]qy,ɤU.mZՏhFR@z4{R&eƘ,! 8f\Dʓd@ ]_/R5x }x> .L Q% ^,;XT5"S%%!ܜ=1#sR̝\]sWdo9hcifQo1x8J<@I{%$T=sZ̻ڣ)ScPNu}3)3op~(8uKKK7\ؘ+e8L&`t>HbbRu^;Y'y䑓^/UKy9:?*VN>JΗ@ӀC z\;?+œZmqTyN~1INTyuC]I+ n m൝MD,cƈKFHsJ1h4K jXȤR !ёk0i! $e"2O,V>u32 SuPJE#TxԾnT~Zix2 ɨH){JwfXyׯ1C <'k!{K8 QPI bCw({fmJsn בejcc#qFLHv[@n6s#e3d4Lїb̽ɶoRudap,A}6'!B"Dq]y$F d}{[&J2nHVh4zvUg|30aSdsfOi¼hp(o7 Da­&M&b31}=`ͲLT 0Z7;ṿ>n }X=+jS%Ͷӏ7Sx*~iL{iosd/VeNA hHSF3|Rrr[W/ϲK˜i+3 14|WhTIjFgr-D 0/}fՃ9sJS oI4͓u+Iy`"CdV})7 Eѣ$laŊqR7ɶɚi.//_~zo}}ݶHy-vL^Ȅ ;`{}25Uu9yN'SƾSq_ud0.CmRNFnKL\ȶg¾j TWc>&Mv⳸!&&AguއzP2wviպuV|^9 _Vknmm'>Ju <;yS-f<~ >{훇 r\? "D!Bk': HL]l'|P/Z ^dt&eumj1SRK)ɆdxΘ`fL$sp7H6 ʘH*f#fG78)1d ǣ >}xݮ+ }4ӏ21U VPG_6lcK2grOXa܈̒%Gn(LK 30xcF uc{D1Reb%&2g^"wytc><">0$4U,Q>?Ir}owL!uHOrNa$u 芕T|eSm??Ӟ8̥Κ IiϤ:G{Q}((83lˉW ȪV/_|܎3s2bd`b98 ȡ7C20Encf 90gqlxdw0#GF|X;~="53NdZ0:q;@$cziC[)]ԡF>{dY[,(4[k׮5ݮ{?rf/fABL'ٯ}93+<{^ >u;4'!B"Dq]yN`@=vp¿Ϙ}fhlԐL$̺n[nSd2(~=?˪% qiM|cH#3#J"P'eAGQ-}S"  &ro )-Sx%[\*"N :8O6d \k&\J׶޶mn޼9a 2qmvպ}x饗[|]ˋc݃;cn~%^}F^? Qgs$sպ!B"ĸFzB=H>yܹs`I !P1hZ x) مY7#> HlJ@3- =&ݐJhN5 ~dEjZL\m&6j֘uh ӇwfpL@v=v;-{VSvݛO9sfbaaAfK*%ew+qڶw'5Fgh&EUUBnۼ}(Vߗ셜@)[w43GɾKHq`!20UR\ E|g8ВdE($DLݣRğEzu̼bA{LENosb"'bퟣK#ʹJJ+1#~@wPBnZ-ܗye^TwyF}^r15\z[n8m^%q3tdÁmE͎i!-Q,"xTgڿ*h,5/uJnDz-&pPFqic$P.)I8A` A~̿0f(&1q\=H.rz u 9!\ƍ6xY1 $ LOO?yĉɓ'}8bDRncMwe*y666@E/+|CTWaL՛4~4 4,G$=!airRLW zp#HV-){2@K;0X2uf;E.hZ.ZB͟*+_) KgJQ !iNI^MKyglTcʲwsZ0{L=sҧ &هY0~ݺy7g oP13ik& Su   IwZi-n~fL863*X:8<7X lDӯZgiiɝ۾7e͛gy͌X<+v[Sg1N~'^e{t:ދjُ2dKUI!Bb\#=!vaGz5Cg ='})I-=zZy-Iyb?dL 0AG<":d Q;cdb̢uTCrsSMDHP"8&0k ! Y|t*9ǀ`0'wfHnH s(fݝw&4I%$-AbȖƅgϞ회o1*9:wCN3*XS߶ t Ji5}x}N]KyRm [|"SGb_D^ۈ2ڷEi?\yh`h ::g$b>;k0VcB(0.2e(3b"+ǶwPTq=S<_F4}q/ ]4ZMٌj# )I3b9,_?iHI k[x(郑n-Zǜxom0y.Ij4SABm IEz.lݑDÄnsOMt5Xs({tr^EH"q'?8{lE=4v>}Oډ T)fFLm=rn7z{h#J".ז.d)]?% )IE[s@ ]{׋,--7gt"mL'`Fd06i3kq +TX6 A Lc3C#KnNyB8Îi IC|xαvBx [B=ы 3xm9>TC0& 2 ;ec,207=qŋ)6jrroo߷nX b0{j&nx{׃`Th{?y@}~ϲDKmI!Bb\#=!vasЀMoI3L!u[[oאa"hI{Nu$z?Qìk+[M>@f Ӏ;:"=Od~P#b~_U>=VKO<]!K$r{IDqo EK`O$zLF֙)ɓAn(Ֆl&Ώ`R?HJ'-q>H а'0pͨ&|v116=(u4vNfNmwH2x׀ #xsĹqFrʕxmm$.{~g~u c2{vɳm,~ s{@Z>ƘpHEȟ"D!5bw&= I"@&kΞ7oF`=]0IRJ d )J0EIx"٦q M$VE6 &ADHm C2D1u^"hYLˑDٔ|CpF$yCr7b3{bG .zLcy,"C%*LQ'q( 34hS1{ö#%2q-&0uϐȴoifKxx @4kJX:95$lnkIXdl07d%,Qaok1&K Q{D] Gh=S507 Pc'XL4X?hOQ٥&^ڹx[^^gvyNWw'g7mwAAGgֲ~C_>9ƺ!B"ĸFzB^ؘ!s ܐj~]333ߧ̭355U0{gY9Ȁakǖ$D2|ҡڛ~Prߘ1} Rkf\[fIeY9)8QL$OҌI\ŵ!͛Lc ^8j8#A$'$}I[N=ŵ.Hz^3HTv!q(5s`vlnBc>nf$q&ו(MX?Y``kTML{. TT }|DPJNԕ 1X^yH b ~%@Ҟrxb,cfAC666/^\%+cӋcScMҗ06'OfV il 6 6\ xfm+ݿVK+ 61X`6.a$Csڇ J"U=O2uEm }"|ߗ/ъ IbvT H$ 6WVV޶p 0MVaTK֤8Am@/'y2;KON5Q:]$k99 b:,!,;/ƮzaUw@9brJ%={sJƸI9SY2{\`kdc2򒕉@#sǜ:]{?#A眧cAƸ@asO? +2h}7]{Zj bQ8c_J&72܁"i5:qsf$sdEǧ$,J ́i7_ GRh}2-{^_Ji 5 Liq@\<10~Ӓ>ԿOcu&O8&Ʃ)߳|9ЧnۺvZs΄q_7(j˦~/PYכK,*s'˲s;_{OGʟH"E)6G%ũ&Lznf`x=`<[KKK?e =kkkѷ|{{Lh Iv'<=X@\HT0fݒ\ܚH< IάLDi9-՛bB1}xV;#yr|dp]~-*!{1_~+ ؑ$D,]6Εͺ[_^nͳj7_%ln>cSx!+ÃNK<Ωђe,yԪ}r2$U(XR$E)RH0 S?^ZV=kz~|3 0^[[[A2^$@ʺ׋bꐌ{o1Q '\N"a$x{<9 D1!Gt0dk "HErD-o$ sV>aF~(IVUIB6eH*@K"i,#qm%JGINUHPS5hν!ac}[hgCaI]__o=UL²3xX; q8|4\d9l^c2bƘuÓ@gdǞ6^ieNdRK$^Oze~;#<~$4+0}flm9?RttĘ߿gNhS&ȳJe⎛$8"ЙT$3ByTyߌ wd*;N1זYݚ#m}7% c"96Zwn0u|: @N^ﻟg&9)yQ0W~H J&!ʣIKI}gf7 ÆjG6 0@z( ;@<bkP cIб' SY-I2X!#k {Wwv.џ^~ڻ^_Ǔd^/yHTF-=cwj]w9x3˹U-J]sH)RH"ŢFzRn$ mVz~(IK; NH܌r`O&24`t%QF65dH8yp uHuKI:'8LHԍCERnS(1!ԑOL̋ӈ饤`r?n#ٻIAAJBmՔ\S^=%ABd$HV-))H|Ǧb'r[%< m]ӪՑu!b]  υϏ>{lk{{{. d@toO(s+3 8Ejͽ=^*fM5ƠGG ةW~^¼ǹʎ[/;y#v NNFVNc K& gYc gnj$Q;&1k^IJ{%ɳa?//# Wd  [nU*:b5) DQ]RaԃHJGQ>`+e7UHH .\sl}/0xlNv @%)7 {TlNl|p9+nDdD2zFRm;bRfjSX@wJߑCo`raO97}99[/^ܿ|_>w׮]ī ͫU7 J'c7}Կ1ܹUe.R}kH)RH"&8ՄI@ϭY)f=5lX}Gw"nGwI>lAȼQo߮H5A; B%6 wCzx2|O@slUO%8wMI, H'2A  Fmx\DRP  e4<"!P+#H &:V)<<>:t( D. bdHjd^>$ N|J^<%ȴ#dd!{W ܱz)О7gb+pz… ώ+Q.k96T)ʛq W1ٜ9_1uܚ&97' m$29td L{Y93'cIW6d +3s )kIhR5Ĵ]s?c1Pf)._<q5KF.0d >K'#s.0$jwUOR͘== ]{>5 3 h"Eظ'Й>bݵSscP^i)yOW@001K8oL{ı.\5^=p{ s(OzQZ{ ֹ1sdYWt]anІ$(#C%.*+VmT=}1`V~b%8Y#O9IX >ا\?+_5q†y! lsBc/SGskDźU>~f+T !w'^Ѩtҥ+W`+,t|xmHvLFOIן6hI; Oܹrt]yI)RHbQ#=)N7as̞GËON; ?Gy?'ѷ_ȷ'>1o@8\X,$4qǭ=<- "vD0Ia3 o7HhGKO#v6|\" >D[ c#?5Wo˾DydղdZnVի0FXC6=%`lD~ $+Yg$ɮrKK5fɵa;`qX[q\gHCrZV2vңmr0ګ0)0x1&!fҀ=l l"'34܁$7N8(fKRC++,cdq:mKқ^o /rLY֯گfqQ ɸ =Q>LRj$r{$dbD;z2>3Gr?z.b$\ L~cCbg ad}.&,A-$j۲e)Q;Z-$2j3U@xΚb$}[qCԁ^#f@"Æ/O pfj^)t{6%4$Yd@c"^Z_>u DD ؏1AϤ5RYU&H_$^REo$/\>ijG6s0v Dzyό23q^=7Ϡ9!beW+̳0@ȳj&;2uk?sS;)3Q~klN/s=7vU7<3a`xY 3,L@1%IFQ?@U0R*.rbadgeɭ.G"|eĞv `0O?;Po6 Fu#.VW=G"ˈ@nr(F$HS}"y:mɺ-{<"'}zfS (ƃg=&WV6!7 c{3&O_՟~ֿ-0xi[ɶοQYytvQdW@i_9u]:SSa)RH"ŢEzRn$Ntde%֋~yk6˝N';sL3@=+DzCy"f T2d> S+Jڂi5z:)seH G>+LMQnIFڤqC{bݔ۲f3;e3;3$6~_ d 20ȷAĴ_C5d cdxr˓7gQvf,4tY\JC7u1L(AjݼSH*Q:hcH 1GYLX7 %[iS_miI'[Hog $WIqJ6VYHD}Iq6hr~$YMb˱@lSʮ  JKsVכˎY?D8V ԅYv*f+iMLo* c,IR-2xR, /t8>}5שּׂȜ 7$0E~S@F THyN{c2mԱ{HǎHˉ2hGcv=eIj*c} h+&qKF9H7~MlL",0v6.'Raȵ}1i Da~#=[ !Z>A;&V"ۚv˒i)4U̕MIu'Gp^tM]$ݪ aEn[vZѳ>GWh,#<u-1WJVvynvR*SZ24U"ՙofʟH"E)4Гt&=w:H!;|H&D#?fѕA vFyL΍>=4kvUOS k<  XB j{H 0e$54\O%%.wH<H‰ER YOlm%!OdxIr9l#G?k4 #Q,F ަU/ @mόźX;$^`1PV˽H,[Vĺ=c=D $Dz}%aq!eՑ_ h \C;c|bWOx*&BWuKbqս7 ,0@C_ ֜X?% hv sNT<1#6{*LM7UTf3ХIxIjY\.ue Ru RM']a #dPr6Z~7Nm6wc XQh2zʙ||nX9M5jwVV=}KS<&bsn5}u}#!co*PwubENV L`ɟEY8!~-cc4k=0MnOPKr aG@TLVnc/&&`N!c-äw Ndxx 0foM x ^9<b2n;YoCW)"E)RXH@OM,B<y(Ji:;|#GyY^^W*=> IF/y  g̼@ID ]Jvξ=}| `u( ` WPC#`p<ÐQyI*;@Sȳ$VIR$߶DoC}i)>D"DlOImunO@܃pm4HO裘 +1<]$IZ#P&\4?379{1x:{@+}{$yաW) =;G}M{C8`Y]ubR_9 b:p\xdX=v$) Q cRx@?{)3j5Zi 12?٘U-g븲f  On8wžjZ*"ΰF(XɆ nKzZ b+[Cz0 L QMUʒ[(orzS֎؋c@Ǝ%y$G |9g()ffTqd͊)d4@瑀es<s7{xgggW .__>^v^Og%^އFǣ4/Wݖ0/rŶ܊K݇/wsS$E)RH0 Y Pv vKW/\?looϱ{p=aY<,ב>^0tJNeAd6 |{,i<)SǼ'ܕ ~ Il\7_ Iep%)i,N}S%yx<&Kb3d/˚;Id2%@QJ(F{瘪U/U]K`Dly^&3HH#YI @k Jp/S=' ֎dJGFN/ ܚEBwE>"޺b&}'@Zeb?)so)r蘬:+f-糝S=1c@,F<#3pKD>>PeXPMcW5Udf|obl`~hY2+ՙk/` /Cuf,?1#Xu3`=) `jO>JMw{Sox_Ѹ'S+ȮFz-EHn[qZNgI?r={ZWB3 V7AqʕOc><_}5&9_YZ<7ZL-fE*綍wq̇U"םorʟH"E)4Гt&=06{$ɼmfdwy,k ЬL#A1#h46nʧ I%Vž=1FbJ*VLC!XRDIm @x$^Iv|dϡ|Ź摼k,ݓ` I|FKz(?\'@2Q'CBjYX_>< II@E#zJcc+er77Ϙ7TOl08+^1ȋ-"Ӓ6X+O~1<% m^ɪvC#{F~]Y J> ,,Iۑ-D;zJ1eG@UOBUKN]ƾ1ܮ&c]2+xg;::Z >33Jbs9rmא,Nۍ<Z CcnYsݩWMPG')RH"EE8݄I@ϢYV$JݖtDz׻>Fէoq$Wo_%U|):@(@@=үHnC4+N΄s};BH '#3Fr\$]y|KO(y֒+ȐG􋄩$`$NGڡ<{6Y ׮ \!AܬHb i5|q还HMې% ;{J^0u` V;rj}N$o%7@Ƴ9&O]E/ d(jJk[x'YUƒ8&eT7#+=SxFe'%D$TɷKr@1Ďy3[U&K?K9izIyɣYSL/I{I>;/s65v;3*Ӊjk4T%,j#;9em3aٺYXC6Syv4̀KH"ٯiB'bF&b܌7H:,oߴ)y"`*&i꥽V@_GS-}ImIŽ=5\ ȵ.LWUJg{^3}YD I X^WEX[};KMӑd?a> Ccq2L½ 탃r=sS }lS&'Ioms3#E? k׵}əq=̥!v%[@q2K){@JhŃ(#]}i-yVS+"!]JDO%w,${ŸwCuv1|k@j1Lfk(/UՁa|[>39$reڬK֊& 629h :}d>0Tw݁!-%'? ( g$Hfţ:sc'+wgxpFdfmm%ʬoTY8Teּ,Zuo sR LQr5&SQa :2s۩x>yWW΍b4j! U%7vH0zU҉l?1` ܔa&o@9 b}kVc[N<^N&iǶ|21}\d@\~Y0 Ίt0z^`_j(;φSY (Z4m<)9[sʽ˼]vq7!'ŭ )"E)RXH@OM܍A"IJnemm-ԧ>=>!90Q"JreVer=3$0 S3#%lIlPnZ Fq%6䭳 vY%HyҾ $_yB)!(oƀ?Eo(wSHD#xtdEc%92/ Ajed}s0fd0%hSmē1ا'BI,XDzN#I̫#v1HHp9KHMy81I4c0&Ue>280Edϣ4irfk&fs/f6~{y7B5E/c33@0_F#UkK2l,<@ CulM|!D0M00~@L`VMMT(8L&jB fg"/,ï~;]{Vg:=z ؇sd 2 bƵ(@Ѕzؖ` ŋ&׿oc(yL'@sgՎzvg ??v:Mc$fjۢjO>+lD5r=+$g@V(!}G8vYOӯ |Bʀ-Gzzތ֩μ>` ?V`:!yJҖgJBw[`@̡r3+@BqT9@bz%,! $2{ q̛y4yϨV ) Pa1bx@ʀ/g`U 2Uodzj*S֘+ p_ս8 c>ygJ97^ OZnWj;]O*]ЎԯI)RHbA#=)N7asIP|{0ކݳ˺n.//'g~rn>afKCT|F"y ' 5ʿ$ AR)]Z 5%4`{0: eO^2J؆~%[G q-4HFE<]OVf^WŲo!CցMCY'? &`#Q6RP-yPƞdɸ%b2qOd6I5P5RȇP9L&dB#mD_$(1F@1gK:3.5Σ*37670ž;@ɪǔY5 𠦿ί_%FKRrY0jL΁N[VT #Nk=6>yǏet]M7v*@kb*I6|k5K?`eY2p8kIHy'Z^W[~]Mq T{u VCNx 2l#]+@u6Uu{}^^V/]|=|K_Cxu^oW75lIV|3o{=U{Sߦ$h:-^eߊgi:5PNϻEmG_ř4)"E)RXH@OM@x7IO|G?d=DK`{BEXum%kݔP {6uC$d} \_T>P@uIF61"ي$@I&y(;E($Y ۦ/S($3t(1M,P$,'Ǝ2{b)ȃ@ /fs(PFUy*ɪUM4 48:w"))viވ7L˒ p@H@  L̀$2yw;6'1"x̱cUL_;cx4=I+Kc6(eu)L@@·9& z9*Ρn߈@ߌ-<3ES7*6lZ\"o%i-uzF~@[M0Ȋ&$ފ1} !{$Zo[ѸSFKRZY} L#/\zO>|+O?SiL^υ0xn+1rN ﱺniK_ś@)"E)RXH@OMA2-&+Ҧ%f '-!\=#c(O2I t6bߐL}_QF7\%HϗE=Ϟ,e],!XF⍤kԧx D/=I Po ٗRM> ޑ|Vh1-zI4mXKb4&Y]ǰ9"H2Tj߁%mkdxxmJFInEu!%LCYu%*@36a~O2cxd#058'{_BUo5*c3pAN={xyvgYv=f ==P 2oTͱm,|1ę%c3,qc\ӵ5J1$!PFXHf0rFȤ3Xq?a\lbĴ Pğ ֡{HъdMRw30\QEjK>q,h(yÉ|r` $7YϴY~O~իyg5ڑ^|vz['ny-o BE箬njOcŝH)"E)RXH@OMOAI7X>,  o'?}{߯%=s)=ůd(?5$j|8=I ! w(?Ni~$qap]%p$1 ׎dj7@`MKrh$miGd^%)]xUα@ΣhXmй_/*0Vx#&S@dĎ_h,>/gA /5ATZN]fM^7e6l.h^"p$ɯ9nMԮ$C,.0fG.uR`S о޴5ހj5h"F>$ȾtO} /o7^׾v&k7+<Ϸ&r^F˽}c~wg^P)"E)RXH@OM܏A zy@[򖋿?op`c&xsvwReNd} 2d^$2H |;6fy>sU$jVoKd&'D| !A Au#;02 $iв%d>Cc1`ZR6ڎA8g[L$ܮ~ s|Bh/ 2gpI0_2q,}/uI5 `1@Z +@(¸4ÀtÏpUyx9c&#wMͼe p!=nՓ}5\{䚗i5Ъzm˜nvK옵˯&pU~L$r2xFT8^\?wZ_Nܹ5R S-^)@mb0MQvrZvT"70x:@.eFX>(|a9i)tQLCb^cIr.@Ӿr[H0xGk6sg8n{O>~K_]|c8w`0X/qm0\<88|/}߸OOb1fӱ]oJqO~ZN^~.]kR?lSI')RH"E8݄I@O2`,H27M{[VrN2РXvI"$e<o}VJ-a~FO#k~VzuN2}r]˓MyqTárϛxz?|`y[v>#9$H֮G8c5=Rm(`syK|H>nـCRf@zH@v(D 9' 8a5#, ] lb\a\wų,yo /O&P*wl9L|u cV 5 'mW`56UexyFcj'IyࣺU=gx9~Ag6 2?8~rcW{Ǥʄ2 *m}O X7($Rk0bY>=CC;#oDH̴~?:}Cj `-Pc;-`SGL# Io@@""XijU,,q8.6!$PJ-dV)kp'RmvNƭ,c86HB3) ̦g&Іd>1U aǬZxRYD, Fz 1i3>k6Z'][_4h+*l8kc$Oo /RUz΃eu}5/ƪ&5$J)B/^9GẶ i޷%6ҜoKnI똽:ru|z5 ߻ (ߞkj`l"u5[QmŽjam!&& \ sn{ /]?>7 KJVJ= t\N@e ZۙUR݋:w&^ʟH"E)4Гt&=)N$_g˽ /6 :_=w*o}>N`=b'q\5$]MJZ>192P}%zV&_RoD8O*z|^Su`L,eIОb5KI9.!&V@ jŲX|$IGaEҽ#AGr1P1^>^M 8˪c!꽵ٱ ѽ1;1fqOc2eUCsU4aaʊRp:vM< P$xa(P(OB[() Kj!ցõj?OKrOC}68L0e4/G$apDud-j/aLiMrmU0ڡ%}0$b3L @c<3O}$7pqo?[[L/f \1VS\q+*]X2fQ0>{ YT5~d PV'Ǯ1֐g5og<Ǜ1zt!;̢рYͧv(:਎n\7jk!^=;+Brn}(d^׷C9:Ma4+͝^'׵n๔;ѼGxnEv:`gQy i<mLNqoOƔ?I"E)R,h$'&LzRx$ س7'ʩ܍?{{V#NƊDA1cTHݑ<0wH6C]+JN`9G9a1P\7a]^ y_HuJL3g߁ XW_0j_WhsH$;@*932Z(眤 /|XWz2ǿٷE;gEzmy,t2j4L\?JDC%͹Uq"h DUh4C bs=ij>{ _tgg瑬Ay7<٫_%݄ۖ/ҽHmY{u73=1)S$E)RH0 I"JeRn{G$Q}3̙w?NoNXɖǜXiafɳ3F$a0(a<ԮEHtn!ђ́|xIͤ,a={1Er;N7H2a'"yɎxf>Y l1{ *W1`Q19Z8 vg(4;Wxޟ3YyĊc@;+r|^b gx!_EVEuosu}?aƱbm8& )L`ニvИ~%I&F ~ (v~߸|zk?{(+%خ׳u1+A[)r}9nmkoI)RHbA#=)N7aГ}gl}CfmmmwwW~gݛ?'i * d2ӌс|t:m[2ZuO>Q2>WV;OS!2VLQkas`<5qZ$03C^?·'#A̱>Pf<Ej.\ۑo :gѣPĈu>"mI$cx ϯ0Rrv)Ԇ\n8`Lo*MsI<X!< gʎ#wW SG$Htk`b,#7cjR ,z̀a{ÃlfrjFm/|W&{cs"lUA,cT9Уs/虜0B89Y!G86>ar@Q1OY߼dg1(;pl)'77'ws.O]`*<n ?Xl ɶ֤1XgCECgb }Zy*|>  X3Eg"DIa|`bDi) I+S 3d5\szC1`#wdZzi^ڬ*Λ&7&ν琓+^PGМ5c!UY^|E0KੜίSm 5l`ZF d"z_c (Z˿z|8&4,}b{l;l=}>̎e'謁m[cw=Om۞"EW)"E)RXH@OM<6d%cOdd%OTc؛\YYeZdČ#@ ^,oO70(+l|/%}v!By]I֬+iݓm Hz}E2R0wc*f31S] 7 e`¯'ԍ|^̓Qnf^y xR8RIkWSU2P3SٸTrpVۗkiLR8N)RH"łFzRn$'ŝ /I]lMG|lۿe Ac u%I*2Dv$5A8le{6s?Dn{!wxtt/:88='x`vMΓ(vK-x~IVM?I"E)R,h$'&LzR,f%& ȳ?3;I\{GЇ o񵵵 y29(G:&ZD%x +71@ ^tɎy=rQbLLm)SeόEc;O('o6VBOx(U NJmV>k2~<(==:Xj}7U2I .;}YD2 DFpB3V:Ƚ"nY8o\ۨ=|(bG@,O$>zEgwMȸkcb&f )#~<Dž&OȝWGrS5)mRD]x7}-..^yQ*^W7ExEXXڶwB($tL|Nv= WmHg5abI^Sv'5|hIXZURYDO+x葡hM?mqYaQ=nlKl3铘1ёFr/-4th8$@qNn-:Nq 5||[.eW}Yҗ>&Q䥖sutmimGgcT'P֫nV +M+O Pa:fyʺ0ϣ}׋9I,uvjf*Jauu?~QJ6E,}{x>G{6%z)`:o 聝]08iv%u Qz4?\׿ (JHi<hիk}˗_?qU)JǕ>O͇EH$Cy%xS;֙z6LTkQznHGapiU^B՞tWYyrҾ$|$֍X8%c5X*Fdn?}[^Obe!FukrlCr"V.FQ12ɛ\Xb87>>0,".sKͅΨ9.gɞѵ؉~jVNZ0[:ʶ.iZmH"vZt!ýVm6?²~]C;+L&r8H m4e\,em#F GIJ+ Xv$qޯ6%S3Q4NL&LEY__WԄN>Vfyjff_Jϭ[~ꫯIˮn?3Y2yuzel9VA ) ?J&ѣI Y$ZӶ "nG~|ҥKN>}^t:Rt,|k6uX򑯱]KvBKt"K U+b_nUKhJH|t]mCB(Z?]K!e@zB׷`v )./|#4nYT/&ݎ JGiKK.kr([2q66PkFY:R(_Ǐi4ʦngF=-\|s=3,lemS})Z-OIꬭi)LjZyvvv!o7 ]7޽{|SDu$+:%ۼlj6jp܃'0 z`g =CdOQ?=M" ݳHlB4)jm^p'|sΝ;677wltVŢΧiE;|gkFX6oC/z-TW?˞DȥEtLd\)Wӵ|4W,kbYˉe&2߶E-GQA.YZI"HK1rw$4ysb' "8ZEVj4)u3]Cy*t)`Ai(Iüjnyc}jN{uuU~5Ul6 ZLxU?޻wןo揤#_&s~CI=cq`<& )x w$}t$H%gfvr)z=PZ\\\裏+W4gϞ=6;;l4siRhUFG-gw~Eq}O5fQ8D)V*ɏ1):Da\4k_N_g&e͗wRD%DLԤ&[f=r(O DaHd|ޗi}{9o%%}o)|\~k7aZ;Jh//(Ek IRE%rVWWlґ R_5@.u-سg~sν7o޻u֟IO/]8ydԩSGN>}VUOAU;N-̛ay\.+[5~HEOqDdEx}dQ䉋XZdQL2%׶TjJ.WtyNlҾ\|~Ҵms30(Rz<;1iZ#{oeK?6-> Ȩy0"o4}Lbgss3\2ǤOn$t$$jT fsee۷['vKh>z"u`}vc pXn<聝]0Ҭ?鸵ʟjg%?sRH/_-..VΝ;7l… e6F"pJy5M9[.aP2r:@&BҸNNT#Sݐt|]OqfɥK2&vI,IGIl:.|އC6q"?.m吺b⊞T"(&T)L IδC[X*zM%Wț<ߵN[NIIQ9Q::EL(FaBJhLus¼ a8>˖ZZZƍ\vMbRi8k6:LCNyl c 0m7"O`LA.D^2ZP>MzTG? {mY=Zz]z]N\4% :PT*m:u0uϟ?_|kssszKA%Y# QDTɢ}*BM jӅY$Kbi$uXh(DT9z3iJ]IlǼGhq4}3(ј}!C:GaK\Oay˓p|#i~EATE)Y깼K0lnnM$r-ZF S^"r z.>]j>w%fffJuZJ)[h7?HzQ:5b%md?0xcDA=iK@KHT$PgsLBDЃz$"ȣJ~=V'ZțgNz/^,,,$W{= E 5=*i={uɄN#ɠ(zkHHHqᓋ)z] K&)Sh3H$ɏh_|I/\nRExlK%2yC#+X/EY3~&s(bW HHʣtyeƮGSqoZU0_jG?~>o߾}П/'o\&2'IH?+m_]~W_Ƈݤe{OͦEJf$|ƪ40wkZɓ'߿ߺyҒ媝W8z Rlby{Nh;<޴: ӮwVͅmxMڟƱ4sE(ڳlºy}$Q7Hd-I=ȧx\ ǒ=g1 i^m G/v/i4dSu$8Fs5d m㈣qicEA֒Tze"ÅR>{ ˙' "#>6ږ$hQ֣Huoi[ ( ξrI9F\^q.؀ 0 z`!#Y`˝<L48WrspAs0 z`Aˀ@|L $ L8, Dc 0 z`@n%Ԃi 0 %7|@ [m>G "=CmA;#8A:li';LǑex;=OH!8LdA.BpȦ|/ `zIwlL0 z`/.G z`Ip[H8 z`RPedDHy0 z& d[CIENDB`HandBrake-0.10.2/gfx/icons/0000775000175200017520000000000012535641636015751 5ustar handbrakehandbrakeHandBrake-0.10.2/gfx/icons/problem.png0000664000175200017520000000354212051732765020120 0ustar handbrakehandbrakePNG  IHDR szzgAMA a pHYsktEXtSoftwarePaint.NET v3.5.100rIDATXG{LuD#r0kJa gi ePΦ̿ti59kWv񲠲D+ @D s=A}{s~{]xD$VGcػH4%.%NOOLII9nRRW1rȽ&_M\RS㊏/NHH8ϼYZzҎm[vՎl|!j"܀ڳ./~b#ܶmPD~5kDErrGLs:|v Zw4J <>ٸQd ,G 1r՚檪>8cj0P^>*Vdd$%K1KB'O6O~1sH.5Z \p޶ y Sn+y2$˖g #>>b4A.5V9JJBp=cRRBlRwj͢d3 hAFGMQQ@-pG#jlӾ>?0+pX!{{5oPc& NјiCɸ.=#{D%|4 >bL N5qUh͑c@ɜu 2v#'Tp⇦6_A,^ ҳaL̰gll̀1r&j'8 HqqqeB53Ki|C}|w]c8k6hfw!7WRRdDk@I%؁Y 1rf t}>_08{ҥ$85D^|ш  9vTbbb>zC7cc>W^?+Ȱz"ȟ @ I gf8D#{|Yqu:#:V]I~&5vge.3{ܮh>pKn)x~[T~guPB-4#+n=$Z%F).>4]?QksG4B3@VV7;#-2{~e]~=#GF-4# SSRS2/֮kj#pQ-**z<Ҁ`~lsH,Tl5_X8fk: t60`[ezyQ Ѭ 'cmwիk\ۆn48ຝrפ'G-K),]9Æ 3ʝy-:UUUΝ[ߚP}8g#N鑶Vikmj#F>s@КwN 6-LIihvC_k^a./aPGJcc6>bp F.8F p) WzW6"}-?ߊzസc^"oF?7{y%//rB…C'划xA|!j?]$Z>cYmm[/tg_z1^~6>bK 8Xۿ'LphS<}ԩ۷ooi=GrjሾνvqaE #\j? |:MAIENDB`HandBrake-0.10.2/gfx/icons/showqueue@2x.png0000664000175200017520000000745412047445774021073 0ustar handbrakehandbrakePNG  IHDR@@iqsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100rIDATx^[Mkd`h C`@VnIݭZR5*0 d *0U+a``SUƆCխ:uky9>>?}/ 5\1m+]EuԮ{㑃Y[Yj^]Ϭ5W1 _~kAG"c›r|[z9|,4WrBS7cc?}@!&Pe-OP|a\zӧ'%wjq}/v~h\#&x|% 7X,e wao?܏Q(~֬G󏔹>Wkć?|;h5/y}yۇa>s^1,#gsFBi}s%e係}6Ũ]T EG 5 #OE5>=|6Jv7r쭹~">yl5".Crᰀq`r4yo5 7*} kOOCp~CoÎE Ё-ߢws5VZc9oyݷjM=hɫ`GG9?>|Xsa=7 jx{]AH1_9u͏f×zp<(sԢpo|Q?kpΦ̼&9O/h+=1SΔ39(s~Nd7 Wk}byw_v'8oKVg) )'0D'"V;A"5~=׾oOx>MG8=ɷM'c:q9D> <:xP`g?l,sRgOzfBrkGS=nݻO{4[1"rk{]뜽]~O]!NzAV1c{ڶ7{ioն Ğf#9;hz]=ߢ5m0uVK>uS}+ -Զx[Vmo/|>t꽘πZʳˣ_@~<{S޻#w wwͭjssst}!&8FMy}a= hb0>j}->f۵B3xCnޥ}ͣ=ߓ66P6YwFBN\B '`\ג>׀p95LGoj9ys#n7g߯WhF ӻ&Rp^xMcgxG׀o3 j_Ïx^M \Fﳯx}3@+!>'.5iؽM]AkOw9vb׺xW~5U4|-^^z8k|^Kz'P촙S*wOݞ7nn1~cZMu>?4ĻO7y68Fnt: |cѰnB2w>k\^p\F3' mGw?py? oy ~Z]]a*ک0;X?r뇎6pNa?YzźYztZG:|#oPoi r+^YqZa&>>oc;Կ߫'.'ix}8/T˃dt?u<\-Ԗ;p2cQdeiz7ɽ/nO{Mc8𔽹GyqOQYO6 yC[vܮPCl75]\j}S-G}eP9ԋ>߂ψ0.5h,%up@~O߁S }KnkK_q3!1+ޮW쌞2yWh`aZ?ޏQСٻa`,{QCGuQsLk\bEQՁud5rntc P#fczฎYz+bhxnj 9% h33zaG|}8gZ@VP>܏GRD7Ϛ8|(3GGzG1<3ѿqoނ6:G;'}?v}J*IENDB`HandBrake-0.10.2/gfx/icons/Preview.png0000664000175200017520000000175512047445774020113 0ustar handbrakehandbrakePNG  IHDR szzgAMA a pHYs(JtEXtSoftwarePaint.NET v3.5.100riIDATXGWNPDBE5(P!ı4q}`|Ram ]C[hn\_;o^.@Ν3s'v)O=S;T6 >;LfQ{< `q.>ӹFȓԂY̤2LZ`2|!=^IKp?rxqu]uFȹDAJ9^-I\uPG&p,Svlff.ɤ}wEN4mDo%s{#-pNG~ml˾lY:вK^{ >Ԧe}Jm,3۶u;nYm}3oZUPy3tmG\G5MGh 7 uwJK>4Yƍa#0ƕMMxmSѻLeM:_Z^P0JoBໝ[Z:2"o,_ZVA@cڬzz>uc%2)JU!*\Rk7e2[WoW}KeT*wiS,$yyXgnXxJ-x4EdMZP(_ӷӤ7 Q 33ɾДD9 0Y~)R0 IENDB`HandBrake-0.10.2/gfx/icons/EncodeWorking.pdf0000664000175200017520000000524312431605213021170 0ustar handbrakehandbrake%PDF-1.3 % 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream xmRn@ lXru` H+ʖ$77B+R(DS*x%"Vs)t)bXci/gcT=Rd9|>X}^;GrVtKnOg9DPLٽ6jk0א3 endstream endobj 5 0 obj 391 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 32 32] >> endobj 6 0 obj << /ProcSet [ /PDF ] /ColorSpace << /Cs1 7 0 R >> >> endobj 8 0 obj << /Length 9 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xUoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqx endstream endobj 9 0 obj 1047 endobj 7 0 obj [ /ICCBased 8 0 R ] endobj 3 0 obj << /Type /Pages /MediaBox [0 0 32 32] /Count 1 /Kids [ 2 0 R ] >> endobj 10 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 11 0 obj (EncodeWorking) endobj 12 0 obj (Mac OS X 10.10.1 Quartz PDFContext) endobj 13 0 obj (iDraw) endobj 14 0 obj (D:20141115080622Z00'00') endobj 1 0 obj << /Title 11 0 R /Producer 12 0 R /Creator 13 0 R /CreationDate 14 0 R /ModDate 14 0 R >> endobj xref 0 15 0000000000 65535 f 0000002161 00000 n 0000000506 00000 n 0000001879 00000 n 0000000022 00000 n 0000000487 00000 n 0000000608 00000 n 0000001844 00000 n 0000000676 00000 n 0000001824 00000 n 0000001960 00000 n 0000002010 00000 n 0000002042 00000 n 0000002095 00000 n 0000002119 00000 n trailer << /Size 15 /Root 10 0 R /Info 1 0 R /ID [ <6790a53d0116b179d0c723da2d2fbb60> <6790a53d0116b179d0c723da2d2fbb60> ] >> startxref 2266 %%EOF HandBrake-0.10.2/gfx/icons/disc@2x.png0000664000175200017520000001063012047445774017756 0ustar handbrakehandbrakePNG  IHDR@@iqsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100rIDATx^md{oBRSMd{gdg'{'Mv|RpC)Pa8t% PB)PB)%{~Zgf gezֵֽIşQrxGz{(b>Y0NZی;X)WW.~tf\6]A&$6_kCQ UGȎ7A y)gH|Yj#ƣf'h?Rc?{-_UjFoa05`?VþzW88߮5QPyot7bx ~z?;޾-KaqaCd>FL⾖3>iEb6ªFcm+v~CzKk8VƸbHE~#oR3l7M!#n{ >HsJvqݮXH-oFrQ.Η_Pw» x>5G,hC|~|Sh_Pb@ԼOA~n>Lqxc| c>^,-wWYKxS6+O_q8-KW&i+>#]q=x`x `6y ϰV0H}}%km3,[g:[q@v>5V &sdn3s{0`/.۽ 6ڽߋ3{0i$הL30i=ϩuޣeC_@9ǂ6ѩ¼r4\!ć ^ ݥA@g;fO{a+=ƭ9Yf=%v ;"-oy4k8qỻ/no~_5G }krׁj(C-1oy3-õ;!Cy:: uˎCV9'h7S=˯z2fg8nΜ<w9.^Q{5lD470.xFCex_+CjšZ8ӈ}ա}.*Fu 5WCk4.~yJ~Fcf7>ߍƇW ]e]lv^iF}xҐm_F:6}m:qr?oѭ#gwlj^t,'rE}uCإqUzm~?Oy 1N-Kĝk?GzbGV';4#bNy[3CQNT`浵kjX#`kkkɚrQXZWԍ`G}%npM}>o%>;p LwM&`ޚnSyյUkj9Vʩv%WWuՇ+q_+w˸WY~u ;pMk :| ӱݬW|WWb%Klsu#0r&%ttKsh>oecd3s+XH# }{U)gzSMU(uC.a7,,'#Šc_9O=9C8OcŅ}M1wWUC}/̋BGmݬKKKKK./-ӥ奔er5\)nøKb3Qos~a|ZmSZW{Gl$KZBN5.pFL򙍵oV} NIyXۃgD3;7:wrK5lͩuUd+bbm ?-BX;&`^$&cym>|{j g`ji_[|GHqnR(Eڥ h!YXXr"P|BX{;|jeݏ𘿝kfzߓfzq80A6 X^CoxWsNxk|ϳo4w?;.U`~I焹9R8v|nn&^} { |SšXԐE=y>34I307ͨ<@";#,=:33shgΌXm'K0\ì<#a6ṳFΌͶ~MShG!lFa*qidpqEMbZw8q;@ x{+6cZs4 ]w E ljM#x0 x;J͎{c$=f*2q\k88Qh(Kj<XB CLh)}\#̗&̘ C>RK&Xv ijy. qJSjS8)yDfd|6x5q9|q_qRw#&N=eω/~a+'[31eW~;IENDB`HandBrake-0.10.2/gfx/icons/file.png0000664000175200017520000000033112235207355017364 0ustar handbrakehandbrakePNG  IHDRasRGBgAMA a pHYsodtEXtSoftwarepaint.net 4.0;iLIDAT8OA }S%!Csrzfn)ѣ# vQ_<8 TO4ԗuːB$IENDB`HandBrake-0.10.2/gfx/icons/addqueue@2x.png0000664000175200017520000000763612047445774020645 0ustar handbrakehandbrakePNG  IHDR@@iqsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100r IDATx^[]g~B˰W!,JUVWWwWwWwTOUXaYBث²j0,2 K!!Ր0 !4÷s'?L.9;9y[};2>?/2/Ey႑VEYGBqjbVk}M_}fTCtn/qD\rr^YhD\%pxc?=~(~>aǜ{7sOs|9j֫11^8GzM78srnX̧) < QMbGQX珔>WkCz;s4ך<y~ x{1sVE= z?i8>+Dͣ6"oyu{k# 8g\p)#?p3h7Սjx"7jfRGT.>m ^ k +=ea^>ףnw4c33=CqY?Pug߄k_JPuV{}tv1`~1XԀ8rXג"/{s5'FZαr5z3&t㖆'h @88A:#P&ffj hcozR0yң,L=ޣ瑋qDLv \>g=m8zg59y|==k3=)NO9z (|p7 5¼s4S{d?^ }Q7r^y|ӿÛNNOӓNljS'''#3 |o܂_^!<;;];аx/="?d+/yV>8D敹k}Cޟ}3Ư}G7[pOBRGC$Sx1{unĹ#;xb'=w*"s> 8'|3<yh4]KgSf8y^2=d7e%7A7mNGG$PD@q Gt0cu9/6ulzZ&-(~܀xWxܫ{8:T;WscNA~h3:fb>kuzS-ø+{kHCŞQg,{{6">y?y}Ě)uj`P?@/fg9}㹸qp\ `bˇǫ}\l7"=y =ګh="΀=xzƕfw}E7r6O1ryΔ;uoW'v4[{{. 쑳G8ϾG\任>oϚ>isXsv{y7`Eq }ra|Hjrn!zW =q#>@.=Q;ٹ'ءh@<ֽ)|ޞBk*{,{l?%m|olGgKM:cڹVh;z؍Ggk7vu+<μ3Â*tD+tϔI- ȫ+xȼ#{S 树+fl׳q~76tU|y ~[j#wow֫ytw!v: =5y=$<lu5mF0l_oar_^Q/vc/˥A,iqŴ"q+Y㛚yӊ mz0wr!]vg7>|MGYwлi܁BZX87QG~ N*/D.3)‚,zԷvL̊o^Oqa}^=GcM|g)<ƷVCjB|Okz>_d} #G|IV -k ȫiU:yS!=0y(=7{lMpgui~~^wk5yjWBZd=hfݴ[!ޓGǦm[=CI+^}ģ~5tu;<7ozO\w0#xT/rrĝBb`>7Wuy}q9_b?;얂>HY\_ `>d>H.d7taԤ p*RRշPnnW9?oK2<&If>O䩆 4 \>5㑼Cl3@2 I2 [f^=vCsx9N2Nd25D~:DqgnA'>eGop,2e4qxD};'|f5\]]F1"N7#]/Pu=v_ n.Xd0`00 RC!Xg{ yn  n߃_}e}՟p_o7aABs-ryyI&^iVs=wߒNtM5n`0hʺn-p7-#nviۄiڦjvP ZsyWá4D4MVCoY֙ztc~|\Af"80q FA̻ݽFvnx.T<;|!CDqlbpG(G1fR'Csp>/rqtފ s”#{@G.v8Da]Q䐸~{H_9$a7Wt7o;u % C6as3h` Bq4;vN2{|@-ZP{[ vjt2Y^CfK=7 ȉjU*ι>(g~ xD*q @>|o?ϘʿUh%8IIENDB`HandBrake-0.10.2/gfx/icons/folder.png0000664000175200017520000000035312235207355017724 0ustar handbrakehandbrakePNG  IHDRasRGBgAMA a pHYsodtEXtSoftwarepaint.net 4.0;i^IDAT8OcO* 0H /.**i kQhm̐_l B @PC̀l&db n)$ 4IENDB`HandBrake-0.10.2/gfx/icons/removePreset@2x.png0000664000175200017520000000526112051732765021512 0ustar handbrakehandbrakePNG  IHDR((mgAMA a pHYsj։ tEXtSoftwarePaint.NET v3.5.100r -IDATXGk!R-ɲV7 ٖvj^t[eS?RqB>ZHRB7b0 8~p`oТÙ9s.9sf~3:t(^vÇwϢG:"t EAm-FZd=;RO}Z[[ݱcӧݩSܹsٳg8qt/ߟ8` `[ɀrX,ӑ#G,K\gg~sb E788h\ndz\WW|`̧|"o)[[ .kg@w nqi-̌rccnxx؋/ǎ477sϻx<@͸5'pZ\bVpggg@]vHK#j+jeYĴ9ː42~\ܲ,&IF\⎟8T:_s;AMmMP[ScFb_Q3tHK9 _1 v)Cn2飋܀![0!;\ȷ2i*I[9sK&n\LA|>1 F`h} 6OMN)eELK+[ awkV# ΆAhd&&,ɆH'iR3kvF,9 O>X^t`Rѵ}k5| Ҷ_cnO(>, EM6h-Kp5Sú\hK?NOOf L 657Í@=8\/X-JGllZ ue$m0Z]^DgXdAO\Q,S<:Y'&C(c9x>X uˆ:ĸųOYaъ>[[G1.= hk8 @vJFF!+fm20lMcp>pގ+܊}N !8(j߮!6^ЁU˼JLK؂/9KРL$b Xlؽܘ;:~hQG4T.8*鈁9]Lr@7Q43Œe".757mf1H#H%71aWR\Ħ'^t)iy 6BX6KLml߾};B&]_B &Ho( 0 xG1 WRyO$o+P1ȞW$Tj j62Z6:Y{u0q}lͼ پg\s"v82Op5w3 Y +aA.}f]QuP,(N>E)sjh8&5\XAv1 $10yr*\ߖ-x"aƃ8}7[|ߏý }oNڳ:˲+++a 8$i3gϹV=y2iu--a{n#=* -I}1Ņk/|&D6 ڜQ|rx>^ Ȁ^i<&)!bؙL&l *x8} \?P9GDy/NqXl&t/yr/#6~_^^7Ço\_J2"Nѿ3Gdk"f{ !їt<,Lo, /{?ӓLq!C~q^{ҲcYƳv#(v!`HԷ> jƍ]X |>+= GflJXd,߽{tʖr-Q*CH7PqCb3OM6 )[{~.?&V{2 ,Q{mSÙݻy!ֶw@O!؊L^/n/ݝ0@~\gm,Dǻd\_3G Vmۿ]NlB0jǿ_ḡDSd>:3n0|oX 2yt|s?/£B!n 8AE>Ax'uhIENDB`HandBrake-0.10.2/gfx/icons/Source@2x.png0000664000175200017520000000656712047445774020312 0ustar handbrakehandbrakePNG  IHDR@@iqsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100r IDATx^[In\>&Y"YEVE}0^€>!@+^bEðH~Gx~#~7|{}}5Lj9Ye31o>Sj^7ԮEr (kzaٷ^5b ',bZCѸWq9/Rg=|^jGnz/חCcy "rqgun]C,7ln9?-Crb u 0R߂K:Ҕȕƫiyc?QďfqU/~,?8.9Mi7q_>z>OsxX+uߞt O=C o䡞|ךoOwv\DL|Y.\l4Cތ\X*pVh=hz uaV՜[z Q<6KZ#GG H#|._gi>`{Gw 0S3*v?+q`{Φt:K!εWLOM̑ q{>Ʃ;!o-˚4g:Q8 +}ӯ}qE|ozc#ӏ ~KwrM_ |yYܿU5 ~`PbX~ޖuVj'-?VMa8 a&˿qB^!Vp|W,.|ݴf‹)8;4L&h'O2Wu /"6]'&'~ism32ml:Sw]x1-pǓ~x2'}yaqyj0Gm9w.|p}7e{/ޖ'Ve^}xb[3|jTyR3fZX.3^!0>7~n{0kpw# <yU^EsR]w͵>px- nuwwwzg[[“K/Muo=͹KǴYԠ&{.iƇ(D('azj-hP#}΋]J4[Zn:7v{Co`+pLե1u[kn;@r/oo]vO>Mu8=ߨ7;:Vs]` y5__w޷sVonw|ٵ wi׶w8ܖwJ/]]<7*q/Ǧx]{݋ЉK:*{,leؼ`|مc~!2g軎BteU3 +~Q 8?;3W휽̱@Y\|A;СnYsW˛bdz8==O?;C|wvzڡ&zz5S ;47=5k^;]nqݩ9O䤧t"ߡIXF/sLƎ999EXGe#} #|ۇ؅}xo-Ǵvܝg:02cur~rY#ִ+_~x^f㓼}#`GG;:>G. ӸQ8?8\MG{le|=CF3쁛pttãFtxao!0tCCnc-V}Έ/hx?QpxlK[pV6GC, G=)b~^rK$=/`s߂mMy&b1ߴ:sa6 gY5e>rӈGNi/w=nN>hsԏx8m4mnS6u%C_8g|덼Y:zYtsSv|݄[FDѿ &W_^0?P3rK;#[_0 F/m#xr7%&=їQ}rgG6dCIl3~G`oůc ?Rua^`ŭV˒GIENDB`HandBrake-0.10.2/gfx/icons/addPreset.png0000664000175200017520000000172312051732765020372 0ustar handbrakehandbrakePNG  IHDR gAMA a pHYsktEXtSoftwarePaint.NET v3.5.100rOIDAT8O;OA/6;aK< w>  2MR FJ%B)P<"4`HXZzw3166tn繹뽙95c|G` ȱ'EQ(JQ^(P2h4J1C].[w*TVnvR۶h`ϤD"A׾+zQ&%˲nݶPCfSY <499; D9 aTTD$"i #8n_3%?_YYE-ۭ-2 d&ujfhuuY+JZMXaqtMPaMA|ДYR0Z"-6 ?aRn0R, %adxk4P ē:U0 S<CkE\"^KXr6K*|,}tdh&[4LJbGǀ83T*i(+\\\P#8hzzZyQ hy$mmm}`8eUʲL}B( L=2%fI~SihiJӔNð@ΰ6 nooT864JjIa#벐GqV;;;5(+o#~"ivv'9Kmmn<c:=??\G dN{ ]hyyYym0ؖ}^ \?(*DWn—D#ą@{`_H|C}NXT*z):Io6}9{僰;IENDB`HandBrake-0.10.2/gfx/icons/add@2x.png0000664000175200017520000000615312047445774017571 0ustar handbrakehandbrakePNG  IHDR00WsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100r IDAThCݚylUYw7LHcŊ $UK Q8Hi#axIDJRFBې蒖*4"VTU[U{z~sgg,bә{|gfy!"3neiON37SVD*bqd:-W*.í %34MhC/)k ,;w.++r} UtiiAqqKaa:~)n F >/кEEƷ𔗗)ݽĶIMhC0%4qglذ+)R1 $?Up鸲A:>R\P`=X1.>EX51m}Ђ&qtJ%agqdc.ҀU* dժϗ= rt\Gm=sh:!6N o%{]"{A=WvKϕ=[Q^h)L' EmF=d5uḆK MVr*=g= iaVPn{|?v!b-ӘĮZS4 7Y*/ҒK=,Yk~T!*Mm[VH^A4)EMgJ^/~; @fsy}4 7-gFd,^l ТZKbiS`[=Y|P^%S+T)7.}zDtZ13̐eKM+ndѢEc4'#2JZNKrڻnr%#y~jp:IN9K̋ cv ͗qniB~Sq*ǠZw0|w=>@t x a_bLUfMII%N g hVPVc>X&!јFшVϷ8 ,uX,b[I_flFf;G=$J|B~`9^z VԽ&c{162J"P8ΗgQQz0O3OA1-pGz[rС$D( I0veŻo<~XOX$::Z$7?~W^9Oע Zm<'X;~a71ELb+ŸP9f(>gϞ9z- ֟cܝ}?' 1x4LMA믿^q'Na/tռ1&;Μ1z8p'$m?K`۶m3nS Z/^ -{͂7/p 71Ӄ>8×>z^ [o?vKCCC>'gV௺%D{<uL"8Ư>ׯ_֠_}?wܾ7xk:xˈӖ=Ge:_8=TLQ̼tRʍ7Uvh Кn?yA@{<u-)jOF !IENDB`HandBrake-0.10.2/gfx/icons/Pause.png0000664000175200017520000000152112014507023017512 0ustar handbrakehandbrakePNG  IHDR szzIDATx^nES )/yR` Ož[$HAaꜪÑ@->䞯S֌;2Wv+w߾wpx?"p7??{4;v8:}ʝO~裧N><7N~ w>~v7{mwl8 /W ̠RSƋEÍ5a p\c?gF+,eˬ\?9CfRmha2c\ 60d όj˴v4W $g1n@-Ӻ/XA a9"h ;p[UqEC&=Z-@^h!OC3z]iCi,ǻJ(~V5hqu$WzߦH5\}X+;$i/i\DB So$^v׭"Hm MBЂ",7J ჲ1.6|Q ,ʖhZċA X[An(9P{G* 9C) H)[&`] DbJ p"sR| g 7g d22Y/^ duHqXQ <&Ѐ -0xK 8ݏ>phx=T e{V=̀bG'[6mYtv$n.*D/ھ};T:/$> nmP-KUCk @zv6Ag G1FD2Snv0_CHߢIA3^ R,:S88|x).D`|y|:QK+fIF@H3,:N .sAA@"xS4\~jomSzMQі¥ʛ7µgL:8(NLA<˪ 팞R:zVj:7bPD?'wHj[o䅗W1QKMiQu|DGq!NsԴ$y ];+&ZkCz" qm58F q"n 9C 4:ϹpE3\zR ՃDž}kXŏ*q-5qCh'=0፮-Mz,uEt9 gk8x}=9܈Ȃ8}BSv$@1&8n-ŗV8Fp'A; "1p<*ײMZHEWWK+~9W3A9@>$&[Z oqP\d=pefNGbu/3!m t0GJ \thϲW˖eC֑ҋ+Xpafv\p@zYn[>}߶F44̸n?g Yt=g9&*2dG=ZVsjzڔ޴nifw_46FZM%C2朒_O(~)9`wy:t~޸V#mi٧=9@7ÌJzFd",d4 d]dR@N~M9p^4n5Mf%SX>U4oP})9'Chȃb82u|r\y{qG6FZ-ci:L܅Zɀ!З{FmAlY3D})an#v" ۣ3q is`>bkV#Em.뗛m$qr!䞊d]8`,9 :SF".&bΌ¥PuM4 )`4 gM{$G]8 "2eUY耬fB8;8̒^OPQ4 we%q,& nζEinNHo3$\"es>җT %X՛ u)p(T8fQx/&2QS[i[fpImiUt&:@"׶9`.An{P{#*7hb$|h(]-뛆-ۓ|;) g.`  A5+TmHkt Q8Dc( &2gz{Jm֩QjUљM C`Og s& B< gX?f`ԋ-ӅEid 5fJz62R͐5 _݊؂LKEl*V2ֹV#3á;7CT3b >.2wqǀ _!|HmӜLіwjΤOf#u56B"JvNs*t(F")M -Ϣ3lE|hZ_۞ !}#6R[fm":ŧ4Aڔ,:S';ĝe;:b;qm1Ń0jڃT|-q.$m:Β~kޛDwɰV b)ķ~'!M'"S#͘E-}?"d[}gibn{oݽdޗmI&`O.ؚm ڃ{r `=Hn"Al:V^ێυm\@p E4='BuBm!Fa~d7xQ\Q(dF8]A5vb_O<7oBbWlIENDB`HandBrake-0.10.2/gfx/icons/information.png0000664000175200017520000000452112051732765021003 0ustar handbrakehandbrakePNG  IHDR szzgAMA a pHYsktEXtSoftwarePaint.NET v3.5.100rIDATXGW{XMkO)Td$)]T&HňD.BrK D 6ie7䜙=﷾.[ZDwO&Ʉcsݸw}Dk Z}mJ?}ސ?uI7 %m xzu1E@q`zS r%1υӹýf3ǿ ļ{~9 ~i1|}xnRx}%ƞU9^5(D_}ϔ*!& ZWr8-aa\Q%p]~QO;j#L9Dt3%kQCk9ʢ{sS5#p=K0!VբE=<"#>wڅ^e%B! 0^O>QK; .vfJ0|j^]mSI?fC| 0)D[3o}wFWP W"Wzz ĠYWP[[+_].uU)ߦq؝!U~ngxA*6,m˯40/P( !@jV%lf]ˊK8iP=m+ut\*&, g\ |UX8QV_F< M * #͡)ɬS'ZT癐w 2.cYcKQCρ٤#=]S\ *Ӳ6{pR--i#cWxP^_]b=ZbLNjaA!,Oc@6F GaY.bep\r-~CBŞ c6Ը.Òbt_tpUUiL<@Wi (x\\x|v 3R&=|ȋ=[@=j^K]H 2t 9V \F㾥~i 큂V S@CC#gXlb.:pQV<+nyTE,AB:O&P`؋=[8jt{ΣJx_'0iWz4H*nMo5ߞ^^Mzo: LMLL-3!$vh7q? WT֣BT k" wcv^2;cjq9g`0 0v!CcPP*k!@ReVq`04H`AX6{[Ф[/D1!71h~ǧdjKaB~\^]ba8@i[K{=v.XA9D >G~Ըx7w\ԗgM7LgĥɑٞܫyD/Û}nciڇpYvt6T0~,~^X// tǸ`PkՈScC èfr~ +fB;}bu%x\ ;d7c5U<Ŕ0iNJ'm>Itp mw"4qWD4>)?<)oPӕsGyZigyEm_5ŋyG oh˺]BS/aX9 >0C<aߣN˪Gp^q{-zWIzgA=GjCDZ}u3-wfΪb|+w@7wύ;o3ڲG>g)^gzo=cF 5iKⶼa+.!E 57jYƵtpΛ1'K#[%!ڢǼ_}uS<gg<5ҩ80>;`>=s2.>⨦_5Q,熆SdNwPS66p-cgc[qs'bq]/'HFߕcx ;ɊsX֖.c٬asn9<-|j>;Y/Kϐ06v\BFgz/>ZClu]o=k#``[0F!Tw\7sxp8; cTWPAJ#{abau@S$h  ` AxJNöN;\OobVcQs/;ߗ?qo6KEOOopa 3_?Ii|$o1156Y_r'm5%Go>߿ûkj%71}wmi}H~CrV9ĕ;j=Fr/~=KJ|#?vLPl#?C]S>C#=6s؂;~{؟w(l_>zXu_gBhܑj^EoW ^77Ask[  3nUsB<~մ!/`wf 6bȓ+nv^}v.g .@8nyq(֬4A96懶G9-F;j ;;7rv;_ vowʜJ|Ж}I7}{&47vٹ B=PlocY.<x.scFV5O5۬Iuє)yZv ~ԣ8Hc#MǾCbE>Si/;!tj}nnwvwF`!-b&|+@8{bi)r(/ss,k+4Nŷ1KnUݭV [ F979)XLkԪ7rXo8OyZ uw5}'tjC9ZJ6-]ؐ[hFlmzzlz\vǘפ޷BM Wn367q` g_q Ee~4;^u?8ro@=.:.>5p<g؛)b5odrcgS+5k/=XebZ]]]^յ E/b1[[|>j#}{G1syL.o~? ++ɮO VWVVoWVWJ"Yc9o؟u1zh\Œ~c8EXNj9od`r~+noqEmgyGYuiyEBa`Ȯ:D!EV{C' W}<@H~DrW9W+Ghbb:RL Aioŧ.//IK1ė|9Xƌq H ~,`O|=w-4La&v bڇ@:b$|ťg|QKkqRz;JM3PF'5,ICd{q_!\P"p\z$G.-8(?9~LzڰdD: ŅABM\HX#qFl8c@NoϣNVK["{5یuvB;-~{./`am@ m &qm #޻ͱ#hi qn{@$4xHP 2CoZEeP-΍:fq9=fGoW +ƻ0o,fF>MD*ƾf4#oQc<=Lv{؇;~@#8B}w{L>~/-Ժ p^HժZ5ߪßo1kd5 suժ>C|JyAHKTX>5f]Kk~Z֚~ik;2_/㭹A)+x?h M e3;G~I BM] qI3 x5&U68'U'`ip,qaI |} MHz4{~2nHCbggsh ?YΑ[~ #fgY6Klfq[.f<o55l8loqZ\պ.9ʸ4cr@y#>'3{ٓ $P9+w^cV5K^?󙸪n|\H>=noGKD#Έx1Y(NwR y)C?>[no5Hly4ܓELv];V3}hTW9G)i6?`pIT`XٟA~IBLr2x`1/vahp:e͑gshe❶x0bb1Å8#ؗ4F|Ιb}0c;]pp3妞Y:ě-{q.g=y 5K/qجf?ncf3lgֳzRϵ1~ qəKN /3zbynfLntxxC=,+:{`Mlӌ}0΍b_ZEM7-/䦦EG8eN1Zz_ "az/{M.WkѨU#XΈ^MR7=Ok`2 @qr?֘h l@/pXNGչ򫆞E#i5hqp$N:aNA^nr'wYI/wwx>9x.f\K#LK>I;{6^>q?m%QΈ[!Kgw_y$t_5kz<u\kE=ّ=.{cz/%7qoѻ`#oc&:'I-[uFLyO4M7k+/:\uƵq}dwඁh8ھrh\›j6]zLq.xt8/tOy0=1W2V+N̶`u'sWL'e|̾Qju}_<zx`xGIF(f3 e4oF GM9N>?+/LzeɃ"qW(Njb( p4$2Xѳ>{#`-{| !|34Nr),Z!3 >dn9D<+f=X58^U+,y/8oÝ:'~Vi,:MzAiAwDC|V`p5 ^ "Zy:37d;sf-נQ$-}xsvpy9183Ч0Ἅs@ƃi cdgmz~ {˳LqKgzWz \\^DLqq/.pWRjraGeg7L6OCÛp=&|^/X:?gؙj/  5G3%8Ѭ~|1|<Gsm#Vy${,%ZU|! jųj;[)YEW:K8;; G0ouܷ'g/tu',|ﯹGw?@4 `O%"P"kvbS S u~F|\yǞVMܸc_ӓpc4و$A Dz '9}a_w:ox5s7];8C8Ψy>3{6w 2{+s۵02sNF<=d}zZ>lGI{V.AgG83ؓ}{_0-)tWp ]0wР҃# !GQk͇0c|BqY9>9{NzGerfʣ:N~E+>@0ƃÃxxp 2dlkCr_{pjY8bs8v\=?g>5Y/~wv~Yi]_(|$>ا{k=Wޱ{${{qO ѓ^gr& ulxg2=m"Km/>n{{{{=zGQO엻 iXsXTW9߁w9X^xuaiofż7z̹w?n@DF^KiSa$ .\++/כ=&\O 3n7挚grKu.KpR>\nz߲}%nwvv?v;Ӎ^c&b'Q;  ھmG׍}b%|Z+wo^X"vR3w&;fϒSW:M|Ky:v: FBexO0异#dK%F0;{,[4;v' nv;NlK !-o8w;G؎ros Np+sɊQ )6ĻuHC)&W[~  ޅwyV+Zvnїӭ6jYq)Xrϰ6|즆r]J5c>7xĻ ]ޭ??z5Sҿx{{//q2e WIENDB`HandBrake-0.10.2/gfx/icons/Encode@2x.png0000664000175200017520000000622712051732765020232 0ustar handbrakehandbrakePNG  IHDR@@iqsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100r IDATx^[STWOS5GR58N2RfRIL$j7D%nYҀl( .W,݀ qQ@`B4:f|sy=n7wLn߽s罴xaC-?bÜ#[`@BB£hBN >j÷ M$\s#Uy,1 DEQ]͖mp߃p~]{kn+xL7|өSRPt`ɀcx,sif<ę̯U ɤ$j+qS:)? umӷ UK}5_fcZc>n(5#rx6 $Zv-vU vgcշ`SxU[dzwr_Zܼ;ŒC`"ٹZmjp#%`,oBݵdݣxMesZ( wL`뎍q_t W?DQ (ҞD. *ndsZ5ymesy0z ⫪K叭\{/cko=D4G]EnHhӳ5ym栙0@ =F WZ[(K3mmO"4`gq`T:*g.X 4̂WP+w{蝟-& ฑ+ s cO<HX7ux-ھD[%[8m%…917eBa*t4>?'.{ 8szW$"y==S817uDg~}M&bܭC۷ra4G]]A>] 7樲÷2?i=.ީAA.A6ŗ]p7Q1GLDDrcST~C,DZ3s 4}?DUtyBl?[,?;jpdʀ!>RRm8}s/ڏU BGb~ǩJA4Yzl&X8YӭBK Dp9"X-&f&ʜAgMp'wméPs6Tz%bf8EzX2]tc&:MgJcp# {?K岯-h\!D͉ѠbFaY, B!̙+yVa"vag;"@,{,NPt^Ό+X2j!PӑP|JnV.ڃœc'I**H W h$q]<;2 Њ | yNѦlj#seʀ,YoaS[u+El1H}'QV #m88 & -Uo s@c5ض:QD{hNVkhS|6̍92We@Uȏ F~ 轃[mu2NE^|!Gߜ&Zp?!1Y"z tio<,|}TzȜs O&ynli-*FZmt.̉r&CVeDhd,&Y&"h̅9)g/=A}O[?H*ZcQ U+TEh;cs 釧[3%6s Jhg[RԣӐl`vdam _O/ak3%9_!]F1!`RPJk$ֵPt2AEUzZT4GZ&ϑ3*grkVV3I-))H'n{JoI|Zm$iNs~^T3u&HaLXKFl~ "HlBp?{ٱ'N1qh? =3ͼ-=nokt_yG|˗HH) e_?lwZv{Q_ PUؐTDjmb[y)ϕ, /?,Kw#B^:cYiű=ҪI5ZZӃ8pFtBk -xIGBpBg7䜿p.TZDž[^*V}[=h<4,癫{C6d=HTOt:ߗ;I{h/DG1(DqkGw Eq%n/}>^4ګym[0@i|O86 ?ѓ^w¡kZP_k\Pxz,BC | C [|4l6 Nt d2';'4lo0`}|`;{[skπcψ7'`ĘrJXF5G\.7g>& i;IENDB`HandBrake-0.10.2/gfx/icons/information@2x.png0000664000175200017520000001517012051732765021357 0ustar handbrakehandbrakePNG  IHDR@@iqgAMA a pHYs(JtEXtSoftwarePaint.NET v3.5.100rIDATx^[TWEiR{.v**bG^cWT슽w^c7)j&EE>3s[9s);3)\O< c (π@:19kG|u]Ôs.w]X[LB -91h{jRɨc)LQ L)L݀3-GOIĶ;]}2㮼{%uc8sR1+2WZ1}o> lయfQ_l>` N9(iH|U4YD-~zUϥ7c8sFb.c0c a_Xq1jlmK}A3gC]–D<4"-V>{RwR{9g1qKR mu1KEs&#5 xmmVہNsD?}H%pC9TŲ>#FGR]3Z(SJjŜPX `(mOJeΦrVIx+w_}wŷ9eL^1;q c̻DOM&ox4#pS˾`ѣ> ݸ6\q4Jm}ZW qZ O+ m, &63jQB 9=#@cu`fmrp+YR{O#|4fm)3?0UFc璫 5CSH Zc_p%gjxHӀՕwV2Hh;7s飼!}p $ٯP's]*N*^OIۿ5'\WﺞGaky ?W 9JY7%ّc10Pi?•Tb,+hc{ҘJx~IZno&JܫWsy' Yob)yĽ`|r1֦J @J䮛@-3` Q^zCWL#K\iQ A]'iX} ?x:,E94Iɑe1RHsbW?*2HJʫ~5f<><[]-@fF9VбR/J ߪ8FΤZT^sQձ U@M7#>uX/9\~dm m+R~7QlqP"Oߗo*W%ſyF~+ZPcˏFʏ 'E5nL@- Ԧ4W3ȅ7.C/,kqg{Yʏ Ra./m+KU8N8)IG I*VW@y٦xU a6ƣ e'9MT,! SF{R˿̈Hj)S\oaDXY+F =(n"%kRzj{%)Q7x% &~/Ԙ OO_]c^wMe1tTr#ni#G);)e53tχg[ %F^Q n}cm} +XB;+Emv6mXPmwCap//"9<|O彔%hBmS3{#'SWb_~*ףcc zbmM )#^ʣ?gdx<Ԙ--[a5W0xml͸f ?f?E(HhU[R_u<Գk&l/f^ǧ=p?;h_u^qFyʜwZ*B|S8ҟe`2~{σ_|K /@^856X+ε"w$uߦΉj|a[j%xVAոvHϏH~0?\6~:gzyw5`+ >+&1j8yF㒳~qTws;Ѷ\ S+" N#X<}-`Gr=$y>;oqgy~'7nu>j\^\\\!7?vnܟqDk,gd}Ȝ3.7~ Y0Br1grלuun@ WuiW-2`1'̇%w#Y[18Ǽn6&e 2[Q 3^Җո,J%G0 xNp:dA"Qw|}G${ \ _;O!E:{HɆ`nģq@ʴ^D wUbǐ[Q:V^ N5I.%[>FL򡹦܊9*t~F;ez*P[z/BU\E_q?!YuYA.'=J$+ ,ˤq/pp(cjxwڄ;a"}@qa\ZhKZfHm 3W_{^({xnY6c{^KlÒ/4 P\WP<8B&+ xػ^9(>9hSđ}^=Pnoj9 *xBH VOkg+Y8t-]vk8[cIcKUs6W<ۭώ1Ɉ ,:u+ Ԗ簝WPq9o<)@T7tv}lc?ѕb7Co x굺 z=T,GZ{,3enT/x.x^QqԦkL'ܹs㠂u:_*-wIS"Mkgi>t)}iTqT3u<ʄ]=LukXu֊WYWa=:qZġX/9B.pf2B7&7>m`H\QbmT}̸)yvr;UmB-dz-WEO>]{;~UL68X%U+HX. FXU} ڀb Yw{|OD褸%zar@ "0@ 8cS)Lۏ9R ZDI2ǍחA2eW؇NW*$ƊP):]|"4}ܗw5Ym(Ϟk?E]'cIS$s! ~#Ź,2LP9r-*_)Yɕɝ|7`c+'d@@rr>XזXph\,HfA IAW%d8/Klq?& LIfLuٗhNd>L4X *W#8[[bR~4_ЎWAիny|+Kũ2+ :Bխf t8x?/8ľ Tb> ǏS?cba Y O 1W8,9j/y`G,O7<@J*m``לŎ.c7%Ag U_:ڃ3S7YVg$5SZ[qj3~ai-[qU<(ȅȍɕu{@;?A!م,M+A@k׮uZF&pz(qm/?b3 " ܄+`85W^ W$qPc z& 00b-ccQʻPȡX%#3&>-WdJ]6mFd HBN$M6p0w|To"@Զ 40Јt37 Br#G%-l; wq?pC`?=UnؗUp G\A'pqjUp8BcLE (34aL|!h&C#ş'Um 'r9;lH[Y/vA)z ]v~Μ9w5PV\);9bWd v3݉h ѨFW:s\19x̑W mW8hxOhK/<- s$ 3]jOZS5^w:fcL/EWOX,9[@.!07.8 bPZx tݻw ^>zyyS$JZw ]$yگǿ ZzV [c|9fь\zN&G+?3vyh:&ݴk>A'pO ڛe˖-[ܒ?~usŜmo1CɦsnvBsh  kg#w`3gVL4if??e˖ݙҐRJdp,pya.')#)! ?-W_`̀x?I|O_uc8s81W#{=-= 0N 3IENDB`HandBrake-0.10.2/gfx/icons/Activity.png0000664000175200017520000000143612047445774020262 0ustar handbrakehandbrakePNG  IHDR szzgAMA a pHYs(JtEXtSoftwarePaint.NET v3.5.100rIDATXGIrP#HC!$B 9ULYL7/~!ԲL,pW=rau{ #C)/'߉w=}MOuzv{B5ȳ ,1R'Ahy9)ʞbVS=f> {c^RH*%1ae0wɳ,yGUzQAExž1̣Q{ҿ)0 O\<<͚0tvy"W=C=>լ\uu\p0uB=r]>;m l1^%zm-«vɽ~eY`Y})ZW%ByFH~9S ހi4M{.{%z4}SIS^at]xMNah(6&.r0s˹QڇG~@}hw<hZ 䀜Wҗ~޴o'55*DiIENDB`HandBrake-0.10.2/gfx/icons/stopEncode.png0000664000175200017520000000266112051733344020556 0ustar handbrakehandbrakePNG  IHDR szzgAMA|Q cHRMz%u0`:o_F pHYs  ~tEXtSoftwarePaint.NET v3.5.100rIDATXGViO[GUEo,`XTگMt{MDM 5@m ^%nΙ7c!O)ѽssΛ76n*hpK%;q⳿c't$V:>ǷnrF|\Áq2Q71)##r000b[یCݘr~5 p`6>@zLfA.S 1KHe|òG~= :75AGӘp4;7&{CC쳐엊JP>-OkwlLM G?⪱2J)5{uLOnoD=RW~dLjbD@9im2Py'$]])V)|UuPW݉ )BR#Q/s?`l_pB.|gp llqqk9R x[ۤIyWEr]IB >܁.K i0V $Ͷ4K9 9`Oeu-{Pw0kפQԱx5 6A6[k+f qMSxj87λF!#uwKCᆆQ7Nj8&)@@6z=Y3>= pY͢R@[Ze k ]TUQԞV5 zOP dOF[V|ĵ\=S7[Vqp4EGyvY@rDSzt>mKo\e =2RkGG^5gYƧwAzp# ֐7kY[y75Nw6..ti@|Ğd8?HhS|Xlr" ,uKTΨoB &~kѯvg ZZ$ yyCj\Z5ia<09nBMgIW>ksAlijU[ &fa3%4Q^[߸Y4jr5 ;wa  C*05O&^o|  h6D"q= gq;@IK/kW N19rwo r B˱IENDB`HandBrake-0.10.2/gfx/icons/Start@2x.png0000664000175200017520000001070312014507023020106 0ustar handbrakehandbrakePNG  IHDR@@iqIDATx^{eeyZ[};9gf*mVmEEFiEHmEoTTE֊z-Q **E(ȝ2Ügu|411f&ͷsIgZgz_[غO;@_~ЖoPJQV}P/W0NA`4Ņmft`'BxNBHӲ.Ö*Up<8L:=!DDzM[_Xl۾C#@|flOKu:AÂEePU%h{1Jh=yλs1DKKn0\ARЦ(H ;,\%|"c\7ſC2eӠ)E4EYQ krհX'㗁a`Ziێc*w!GVnJJdEBڲZiGJ [0JLjnt:Yn .mHI#@JJk֓eE$F fbֱR`&YJVU]Xd2fm4bD0IY֊LՌӜqyRa$AR /<iY/n8<P v ihF1χ{hCa0ssqA\t:= yډ!B˲^]SY!uݶ;B#Ղl9Ը+B# \)b`t{aEjM5 =ub70Q\JSY\3;7`GU+@ <#MW)+1-~{8G5&aaQ4Z/9두MS" sa hKAZ҄iQ-h UŴr Ka5JhlǥDbx~@} ?S<鑐if~T f,cLPaߣjvQhVЭYE DL*Q"-KrZBM4iY>>GboYR~`:60eGU7<%@34!lʺb&h[0M0;^ hC?q]!u)jh*5u(fϹůGxLzߛJqvoZ6҈qW% `ĄM\4hl&~B5qA+ǖ-QDE2 va&IO!q iY>k%v']\# m(CB)%jr K&fRb!ʊ,/pL T4 ' \ ""c۸EEXsT ͍R,?h6U]f\ 퀄(КIcQA"k^%bƭ׳0 *V.Q ȪVضGb6i^Q%v,)۱O{`Ӥmm*nۦsK m,H$-3)ueB(CӨ "gG@^U̅A;9lahtCYմڨUӐ$}cݫdyI4XW}цo h|GU>hc t#ho0*2L!ˢ#)eeUh0h0MI+ p=j$M];"fIO{?jSBKi`i@V!fek[DZYkZ(E禀R`-LvƱLRh(sD:[P&s'\&QU(%$T4*B? bh#9NRR Femʪ: mQDzFTa4"k2)S|۠J]u1]B{6p k <f" `8ΐB~X&G]+Lhhj&e^YYSU5q1NpL]yF2+@R[?{x~S_[nM'B4mRTyE"3*meP5]Q [B8Y0Θus+[jɨ0r1aIGc;Fhd']4~S6M䴆㄁ea hjB0mO@4sϢLj$KlmE EZy,\i63yuM~L"'>}z/-XM%`8J c41|a8qX4J#qLϡR I;6& z.B)TVba5F`ve:!t%پԷ^Mo-[qW߼z)}zS (°gxol2l&iIU$IEYT, b*vW3%tl U5}7n=/>};gffv{Plt tBaRz6ng2IE4u9ָM,.ӑT5M6)O :D]9&Yc)0n'A)G=;>O| Ox* !ԯh4Oaa1s] )j},-RPBERLVcl[3M64[p=EJC,,M~黀[dԠOZIAgf185$ D3&ZxҞVӢ)O]H`Ÿ]y-N:_^v7t `(7tP"BZhL1- ucy ^Ъbu):iJ˵Ӝ:+}-ET9}W\+mp7(#8U*/<,Fd}g1 4E"ə -V& 3Ȓ1 Ɲ0Z8ܷ]/9} vQDu0,X3BC0&'H!Zc5 ʈPjN`"Q՗\`_=ӿkS[q |:5]xR׺$kWeʚk:BVEKd0)-b ˒?|cFOO@~`?8նlDb2"(|a}}\&I!簲wD70݁q7 ;vw{>;Ѷ $)ieC=I̠bJA#&=8-#ۉ~Nfg^]ַ<غ;<Lj*znv*BID8llGRobhiw׵p C 7~~mZl-9hC$O zӬevoӓ$hLpmw,#tץ\?wX[% !PZku]$-&܉7ѫزeCg5zg`Ɔ=DA]% 04uD5r]M5O6 -~Yh9; I.Y:t(bZ?¸Z*裏۷oB!E@ܻ gU>ZsGځyLۯj|Hhy;)r}ijۇ-i`f0X9S; nq ;O9ca>w}#}eB!EZpcmBh9L0j!` [!E -!_r!DTIENDB`HandBrake-0.10.2/gfx/icons/Presets@2x.png0000664000175200017520000000621312047445774020463 0ustar handbrakehandbrakePNG  IHDR@@iqsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100r IDATx^TndG|Y/ͻy4dZ[Wk XnS®̌3kAx$,`+Bjp,{| `=ڣڰB,ھ/AZ;n;cXoxce>Np̶q Y's#~192t8,t-|@C1,aF9-^,).kt,j oxK<-lkq\^G?.,y%owI ~|jzUx^>cg[6@9,Ř&:CK3,xOإa ۾#ȧ!#hzK;+yOϟeĶuO'Z-X(J43!jC!0T!Z0$rx8fwoc/8Q>C; .pq9Ǹ+ _Fv%39#n.[hs>uCWp?s/:R+1.Xs: _(ksx?> cx4q<ʇ%#3QS`/\u`۹vμ [C-Z׏wYE?q0m[,Z0EMϱ|wyf~ps3onnn|]5mɟ$h 7|rK>Z8V̙5>֋V溹n [~Ļ68|%Xz.8kQ9⺶3ՁXW\Qꪹ`Z1ɉWKu)P Ug273f(z{j5gġo&lz\qf.cznE\1GhX.kcgc#:B3ǴM/EGGBe[4r!$M1A{yo^ղޭno m3>{?;ph?<`wdd|E'hgz|xǜ֧9v fάͶ|wsmhq𝄸,4VT 4cꨁX;loo7!|PG'Ƕ;ۉ7mo~5Wo8ϓt:l8ńo_ M= (̭ѐf>z_1s>д~^Wu #M&[~N~k_U{T`kk!0p3ٚ4[-X^g\\)m%v| j8Zczu،7-76MO[n\ZV?` Ў54}jG?:U0fv9[̕|X]`CB fb?EacsЂ>>hFln/sh!s q㓫XłXǘfc,7xa131rc K&b腘T1ŭa]Y4?GZQqz~mhv.{cNG0P#h1-Q6c9qƱ\hE-9YQ0Z?"= A(G  4z v8=?,xEpw1@On6 1C^ԌKrq[|,:q cnh/PEb!_x?Z֩>` 8 z+Vb0ȱ"nG׳oi+1L0G3%dGl Ȧ9褍,/B:fqq_V`TnʖQ:+2*NEv#e<.(/90 WAA0 d6aTm~yu9f/9ALiǵOb88XkyeЯA. vyޚ3+͇w M9 *Ẏ.+#j~n3s-qh8J`p8,e"0pksg0m'b[; ,î}Yfٶ}>}h9#ؕl XyjYm[X҇?Geݱ,e[GfLfV8m=3Mc/b4gmԞ·@" viEn`7atۨ97alA;3 c,_<@SR^uiCp{sMӇR[| TM=VUbZ/b lkzuvHwiy Ks1OՊN!`hKhzŏaR+}~]}.T_G$+(?ȲY (Yu|s}^y?dy>)2ThJTj6$ILjJ)5sc^asuila]TV%i.5AcW9`:l1(Nnf r+EQ7u,E,L#QF>z=UFHCDF#  x T~U A'&\5 C7u"",IENDB`HandBrake-0.10.2/gfx/icons/remove.png0000664000175200017520000000221712047445774017761 0ustar handbrakehandbrakePNG  IHDRw=gAMA a pHYs(JtEXtSoftwarePaint.NET v3.5.100r IDATHKTKH\g޹3w&3d&`2>D v%n]". IEt9MBMZ2T"-E[ dDCMh$_Mp}qϏm xJ+r<;P5 9fu?D$~`JӴ?АauG>uE 0 ~[29BAuȷt$u>G~zMQ$xCtIss]T̳NQo;z2)?.î̳NS:;"~ oyaтrL2!Y2p?,}z$V%| j?rMo+h^YF<)|PΟ!rNS#_頇Mm藡.Dx 5] r$ SSNG>uгxa]4~lwu]c” Bd܄a9ꠟWhb4;wUAĎU+V;-z򩛘`7,wHR$?WXWOfgg썙X!ʬg<ɼ0R3:Nf```#m1r<RrVbmmJ&by;5[ NfIENDB`HandBrake-0.10.2/gfx/icons/problem@2x.png0000664000175200017520000001210112051732765020461 0ustar handbrakehandbrakePNG  IHDR@@iqgAMA a pHYs(JtEXtSoftwarePaint.NET v3.5.100rIDATx^ tVdzEvBBH`:PS+`)xbX-NgўNgZأ.UGQZJ57 l !||ߗ/?zobI K<7U&IdH29+J(bmjW Eޒ?G' I/IY~N:tņˉH~zD /#F|С?߿|W^+%:tP"٥Yb&|;)ݖ˧O6mٽ{~ ;;{Ē[}]ۯ_EPF:b->O|'pM)=zywzz򌌌YYYY5K暽Kol…ջ,8r睭f"pMu蠋 >M 3cVF=޼K6c 6[o5댙6͘K/5_7_5f̘@:tЕ >M b(vto0}:H,oߕ3g_w4s׌9Ҙ!C4i9&tTkʨC]lŇ|pI(vare ߻GOϞ]{1#h5krXZi|":/ccm/|wd?6?=Rba̜I.x-&#AhSzar-o}eӺo1VDpo{>M :6~L-#$83$#=%[73ǘI@Zۓi M#Y:mQ= bʵ~ w;I%!U=OrGj7W)SI,a22:^| km|BŲ1- {oAVFvz$`OjMnc71]f̰adgMda\{AZœ=vOt2Z<b0-zk#㊮eWΙ`&N cf% 2L%dt$"L` %_0lG;da<`ӂ !;Z[[M,iS.$sx8Ċi `u=q= X/QvF^Ur|HF/y_n듇b 3%1 Eo8yeO.M>=".uI']<|^Y\;(`v \N;' ?EEM:w,אuNx{|EXf'F EltIK $ ge81uj),4woӤh3)GyV;[Ph=::t!->|OJ&6 f%N= h}6R3a1f'\x'ZUHӻ hl|A]c8 f%N'@c%6<0L Bҵb'tiyl/A2q$Tk'=f8c==xz\@-xҵbs%tiyl} ?f4>0pq 7(f빻 `艌 @h1ZBR[]5-س9jKc?$tē8$p5J + ܓ.K)S#]f v@\0+Ql_e8Nps i `lh:i[I7b~(k?$JYcuu: g'E~bC"*odT8sjgxs4<+RY5zLSȟA@>\iPҥvD"&R1u#ukom }/hǒfNJ&M\T1fl'V(n*(02@>qwM $r򩧂FL%]뮃] SW8F+mM@ Z'%( ꢯQ&{Hvr{-ژB:^[Vhю ' XBv* }8{!Po}&|`M)-+\KjkOٺ#A>>JZ<>pѣ ; 3&rLoJ( xg?Bf65TK82AW ;}D1ml/'!8y޸x]$;$[%{.l;[:b_cĊgZp[en0n'7(~c{%%9_TV\3**=YlI@,a a\ј?I=K@̍ [XZ|`ܹVh=3uȞ^mڥRe%1\7Hpw))㨜2p:D#L~D%1ƣGMUU)/?))ojC[Nǐ4. n.p=-mGl"(+lfӊl(f>} sqs {]!u蠋 >Ðj90©>} [TvX _ݟcG럙 p\SF:$V|D&)ɔ["q]>z}6}lH/AyizӅ!H~ɻ?^0Y2_.6#:$>zp8???# Yf A);-myو@Ό.ŗh}F鶴=IUC7?>:6؂.?H PH2dHag^pUXfYobܣ.6Ɗ]S=Г ׁ}8<:z:CJ17T(CWeþҼ5eSFsPyCT6$ϛgNXaZ4"uMuzJ"be6yؗ7SFuw]>PVOփ 65<#iVkknm6};]ۃTWWY]l1}8%' H_p3eʔ'H 7 ^M%sY͙w $O-[ʞ={LɓigOx!KָDFyԩO.ѭL|W\qs8z6fҖcyTq:jwpٲiYφ >b:t}_B<}60:ىw5D-srA%+kV"Ph `B{Pr?gd о0`PՑF3=bb05'9c/ |Fny饗`缼mk Sʾ"aCy7]lc} _`cVGڟh#s#G;wxNO#mX$1 >ɿ8?sP:v/|[c]y~@l0a[R&YDKs 3KJJ~=}WGS|5}uyyuŒ)'ɏp}ߜ`|/LjA,ba &7MJ]%\`,gſꪫ8hРgs Z²F~(?/%OS= t 6O|X.& 6v9I"st#}K,yQO`K wIl/| 1PtU{T@ޭC'ܽxW͛Lľ1yf'N\A:b/obeQK $bdb5T ci$92Al/|]] v7Qko>|ַ,fDx$.L`kuyv֪IENDB`HandBrake-0.10.2/gfx/icons/stop2@2x.png0000664000175200017520000001052312047524523020072 0ustar handbrakehandbrakePNG  IHDRC@A^sRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100rIDATx^\ipNda !$C "PeXk_:#fGp EԖ_q6PV+ RVimyr}A3s=9|yߛB9h+@jQg PT@)P'S|15@ 0T֘@:Rm[7bhFuvbџ}p 04K?5ZITۻ?6lc@m|]uit| }^4io۶=_>,Y$xs^7CEgOڳs􄞞  +u߮p߮]aԩaʔa 59 Y#~01Xģe=/h,[|]|꫺:UV .n[ns@ VZj* aJ:쩟'C:m.8U8nsZV\.X"\rɥaӦMaѢz:~* s-+욐ȳc8+ z_5y=N&AP\|ki3@L0A@:7Hc{&Ng~˗#,[Xm=A3ƍƍ(ub_y9Gq[ f܍oAĨѣh` K KFũK:ۗn'y^sK<\G?\YT ?+ȃ91jT,\(,\@.2m>Nw BA,\@6cRO tSh!rG.OOj2(a hii --0d DdlEȜY>o>b̟,u7o~mse~^pIkboE_?1cI\H3v86'ՠ'Fkxuuu@8 0C$4fX`L$.H[Zކ|4$7\L:_j~!, o!&f]̘cXl!=)uI=(?ɷ~{ "`Q0uiXɥ $#߱k!}M_lo"6s7M!&pĮ 8v[ ,@^I-H6cB '[4x`A/}Ip-c0<&NnWZA4~ 0=J2yB5~keᵲk&<}9& 08 qgh 7dי^-|7o6o- 8Ya >0sv&4͗/}ތCh!yK )L~ @gac^F<6 yEoҔ0qvqBV BifA /H/Plw!o#qؽW 4$Pmz/09nt|_<ݡ{S 55v1"o47CG"Eg afiF}q/W*ï*>IyqBryAќZ6^9p}MV7ͯ-IftP]SavXv \ yy8b3iAGP#'O߾ | UTo_%7EyZ=vE'H6c_}k CUu$1Hs#6`X0Hg0?!uxsO0 C(EY RJiG 1fx^I=hO}7LɓhVTnsub`y^Û7 6pO8FqQ?l,v<?3h`yxT)l8xtM^3*Mg@yI5D~]j w+흛n)%^V+@麏/s9do d\5|%67n hD9U">(%U49+}5Ҍ 6-2|*܁!6<(l6j@gt~&ː>KRb@U}ʘ / _ivŹ5C DD7v78oDfb6BIKyT(eY_gcy`,1=6rRhN9.nWQ!"9}'uw@K}/xRqΛ[Ш_܈gjK\xr2ǯ] \왊IeUl8g"+|ѱwDP˗ ټ䭳5evT\j?[횻0)vrwu"XɔBԉ8ɧz6K7?ھJN,>~[{ڰB5#AIɉ^96df3:?u"}iylJb_D7rtmC>3@'Y$kdk/94Su(0[4`]p^l~.c,ɯ`NNbIw?y~┓Mį ,bL|_1g5-ɹo!u84cp >tuRf:~t>C cfx| E7x3o?ò~rx;{/▽{~ٳ}/8p`˫zÇ7=ztE]Ŀu--:rȦ}رǏzĉ[>O<o[|7!@}vؿs]D֋ynfIENDB`HandBrake-0.10.2/gfx/icons/disc2@2x.png0000664000175200017520000001014112047445774020035 0ustar handbrakehandbrakePNG  IHDR@@iqsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100rIDATx^U]diEhfheheT~*I*UIUSIثeaaXZf۽!4 Mh4͐|<ޯ꫶۳;^^' p|8[c';O7QR6h}rQv<6 Rx#47lrCQEGǛA'''_? >> 9描&k>Y3w}rh8Msq#%^KBd<5x4ĨIY[Ȭ12sJ,*[0gODp%ޏGygbϱyK#dh(CR|X3y4^)juG ho[c@a_WyySbέ(<M@4(ҁ2C]J xy!6Gבzb#̢x^A"tH0!c׸-~?hK'gwx_  {&x{{ٽ|q8||Րz{iA[|{ΣEo3>#v/\6^ƒ Y1yYC PÐи{`tѹ2q :צr  , uI5c x3,XBggJ'kopa;w G /`A 7c|k\FMFN(7KW\+KJVsK|AAz7+LS AOެK^fSpeA5ء]+>ģ,3&UKN`_v{)l47#91oAρ7c 5jbۭžS5Wxc'e n71tݴ nְŰ߻̫?Lڨ:j+yl]9Ԃvr |,$*dz<6Ɔ,v: tN |0gnzAӼ: ;:lD5B_P#s𫈝Mՙ;0? wڝ)mon%|h]9g EGh3pm3\;"1.C[vcmvx X}0nwȉ{=È_]vvv΀hm?ncſVyK~ {-hKۖ5Rno#}`yo<Ξq5>Cw,ll';wmӊG}[ uZ7qD5Ct=>/>g?a}bz6?9ef#Q ![ۭj(%1)lז͖;>zC+<%l8bH|=^g:%ӽM{(n\Gb sPH4x#95R^M{e@ RոϜcs-Iדz~0+~N@C:Q[iqF!3' } p~B4 ˱|k% fcOXon̯Mx\^+5/A\h[X?#&9<qZazD3ڬZޜ-wдV{\caMjEo9 Q= %pujπS[fIo/3Ƙr2[Ljtcb# 䍼.Gnh|C}h݁^G}gFTq3Nߠ3y?koAj܍;Ml,_C.%x9Ji¿?>cg9c =w;`}}=@؈|X,0 i^`ݿ75,Gpm}-Y[[^)n:뚗73>9Ssx]g5M'ўy>%s'~PVjjZu-;USP}2(Ƹ j8 ^rux=DQMƳ5= w}-XG3.]$a$+ ^O᧫+#(Vr ]oabnٱ~X\L*d, `"ׅ7ȁ+ Gx6_xKKa?Ӕ Z:2?@o,NEQ)`cb VZXXx* n҅E]0₾C-^2uRׇ^ϴ1B 1;a{>.0F3c0??";=?ova./a>E>/E^ yyykLjU}S7OmY"9.`<KPA< 67Gl+a ϗUq<,T2f*c6e H ,v\C%/ͥV.KUCuMu^E1cu{`l'ݭO@($fhn X@A+%CΏc\d5R:Rkr^I{~y僅6/<,޳| wssl1 JY\c)璽ͩoi6=}/>PEyǖ#67;+.erㆣx)lN.g3|g| ™ ™x&|r4ШbK78K<%Zj@K9);6s.ag3 !9,烨u^οS=1SpShz}K)b#Iv/'s|Z |'7#ƿs/p_;|l}Z:inOѸIENDB`HandBrake-0.10.2/gfx/icons/new.png0000664000175200017520000000161212047445774017253 0ustar handbrakehandbrakePNG  IHDR szzgAMA a pHYs(JtEXtSoftwarePaint.NET v3.5.100rIDATXG͗rP N\,V]ـ㫧+NΙ?Y+Ɋ?3o?oж- >=v~nV=W_?߀[mm jO4ipm,MCjnnvjfxh~5u@]2x P]Ì}[۞ɛ.z7`*CUeX0UETRP}LU`Pa^t kYҵÞd65}@Y^R "/ N5O:3#{|:,_x$t87tفhwx?Avu l[ NnCn0!;mjEW5POPr}Oü˷%-x\ v>nNvu+/4ysC Y?d??`6t:t*'׃z T>deYY6l& djMd\)c;xJoZzY`amI?rD<%lI($Nl{.}(i4C<@2k'R{8AQQ F:3x92 /@GaȪUEJ6rGVX P,x`*897Ok:GIENDB`HandBrake-0.10.2/gfx/icons/pause2@2x.png0000664000175200017520000001053112047445774020233 0ustar handbrakehandbrakePNG  IHDR@@iqsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100rIDATx^[Ylmw1f7KR %yOPxE'L^~eg{dצ>^Zk酞|HL7 >'./g}m[a۰ n@^(ummί\ ׄuz'ZZU3<^|%xySWPY:t{֭cNo3J>-[ݦM5kZA!]`j@:!\:[pXYq9^Kkk֭[-zgzUg{-nʕ*jjYAI`c?ܪU =ћJ.A&7m/_ᖯXVx`@YʷBsW@=K_F9wjI}MZpP˗M7?.#?=dWruuuIb&k-bX-̡O>}rtc÷+owa>gZͧLٞ!ec[.t\2]1iY=amK׉\/{xZUٕmRJoQL6-Kt%KwttN^pR/|>yۃk`vŖ{>4 D#J.dnvݵS$%"u `[8_շ{>IR>A<@+E7 ,mX$ᄚ$tb~LV՞rO/c -r h)R0YkW]^p.Uo:^Y}…nE!sM/56!{?/cf`\ nIpo/w7ϔg733` _)<<֒&qAaD#AFm?hZ$zc?hiiq moF]ssK"pڷ.௏n.1&|h,m A[$<*$&[[fZ4779@OHÐFܐsQ .@2R?Yz1$'7w<7o^XσDd\?`]clECcp!<LJr9ވpMzSy`DĘ\yb67ו6a"~3c>8GXY\ s8A2DI-sF|4/WA(ٮ @{?*]]]m<<&i3֬u>c4ր+spUg7bF7 58A/ A->x<[.ogԺmmg AA<70P=q.cB DB8S/2PUU\%~ƒ <0ƒ" L>Z) kg bnXM2#i9o.&X'̥Y%7blsDqr¼|V26'W 8=Y;7w>://p H {N>8G" Ʉ\ƒt \ hn[1)P$H!YѯOUN(8e,5Bm  'ep<~3RXT(ZbѪ'1"WQfZ>8/-[1l< yB^*_ XTLl )HC<]@ qd&#݀| WqqJDtZ ƻ|ϗZ=Pk\^^<,Rn {M8EAAJ65ksrsxɑyc'GaZ ODnnɅ1E^i uZ#q:g\21֓7nƄg@+yyh-Mg.(~)D q'- Xb1ZG&40 : |E&KRcǎuqB ;Nkt^1f̘c~<}[Cdr <֥脞!qц/Ib?9bfLK7PgsBb&dKm*eG{ z!D_597qYYj4 G㜯ZpEN9# ;K~ҷMCz _G~7_IdFFlf_[ hhXZ۸fyި§ܲWں0]ћ=|Y:I}Qv5 sc6%ܶW#3m>_}ߏE | ?_*\C1iW?&Rd޾}^{ثzW^y@MFԺk׮CNBOChXc|mׯ_͵k~矿gz˗/uҥ=ʠFjfjz'zalB ˉ}oW~߿$;nݺQ5R+5S;= ={}{WҪ`IENDB`HandBrake-0.10.2/gfx/icons/Preview@2x.png0000664000175200017520000000726212047445774020464 0ustar handbrakehandbrakePNG  IHDR@@iqsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100r!IDATx^T݇ew=4Ԅ< 2n}WݏuoUb~ S?!)~shNqpvƳ3D!@CTK~q;D5u~is4״b?4N ^==tHC#g&Z̉QPϿ߬sܱIqzrR͞|jT\U9ExP;ou k<1[y;L'ަ!V1.kprR &,~}.1'~]ͶY,וJsV64`G?0f8aMnܱGGQi8vkcƎ#goTj)Z׽Dݷ#g a-r |9%}i4-ߚmc~Gqr6@H~u͸YFcI-L0VcoXaLx[p'-jcރ1p&Q18 /į4hpsV5oVӍ7CW/`$; v@]BĐπ\`88!TZCyU˹G7ի546wA/`rc{=a0.6a=fn&8O pyWoQiFo:8Xl{Xu0xk<4mcl&?D]Mx(Mcmb{k-kA٭~mfJකg֨׬lnn@UV>SoZcŚ٭,s6[7cfV~}Wf_e}8rW} W 6旛raއ<{Γt~&xm9#Krvq @A; -nag986hb\l~Swk.|uC:>cxol+ηխ o%GcޞG?5ol9U"FLCujlXκ++P++h3gfzw+%'z emi}f1sVλ}YsfȅC; XY^)e4Ӯ2-rYTϷǖC:Y^YlNEy[^G;Ի2^jq9ΟT/?XZ‥ K}qKQ#;xO}ha=kqŔuHx?Oȧs9w i'|X`q\], Ts6M-7gۼz̎މ vŅrqa\]7-rʛUUo Smw̴uk޾z Ěba]#|syx&V.»^Yg,⮳&v0n$Qr#8;j z+:qyzSs8uHKO3>i~mN?v{oA8_ϷK.ȹUL5jeMrV>9ʑ9 >a91-q LiND8 f\r s!^Vj8zC^ssvk.GZ.@f<; }o͕xΖelV=4>1f|U\ulb_tCX¶xw&\bMp%FnuDG |mVx3`9@kh 5zD~V+ZȳFC71f>~ONgpg& u5_@ (>iZ_ lZ-N0ղŁ␲owdc9< v 'b3 1,z_y??@">>-/{T_IENDB`HandBrake-0.10.2/gfx/icons/EncodeComplete.pdf0000664000175200017520000000471712431605213021325 0ustar handbrakehandbrake%PDF-1.3 % 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream xMA0y/^`}A=DTQeN+tƐn٤T NBo,>\f)"Bmz `l7~9ZIy6a+uV3ܣk"U-b8pFe9ZY"1PQ> endstream endobj 5 0 obj 178 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 32 32] >> endobj 6 0 obj << /ProcSet [ /PDF ] /ColorSpace << /Cs1 7 0 R >> >> endobj 8 0 obj << /Length 9 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xUoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqx endstream endobj 9 0 obj 1047 endobj 7 0 obj [ /ICCBased 8 0 R ] endobj 3 0 obj << /Type /Pages /MediaBox [0 0 32 32] /Count 1 /Kids [ 2 0 R ] >> endobj 10 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 11 0 obj (EncodeComplete) endobj 12 0 obj (Mac OS X 10.10.1 Quartz PDFContext) endobj 13 0 obj (iDraw) endobj 14 0 obj (D:20141115080612Z00'00') endobj 1 0 obj << /Title 11 0 R /Producer 12 0 R /Creator 13 0 R /CreationDate 14 0 R /ModDate 14 0 R >> endobj xref 0 15 0000000000 65535 f 0000001949 00000 n 0000000293 00000 n 0000001666 00000 n 0000000022 00000 n 0000000274 00000 n 0000000395 00000 n 0000001631 00000 n 0000000463 00000 n 0000001611 00000 n 0000001747 00000 n 0000001797 00000 n 0000001830 00000 n 0000001883 00000 n 0000001907 00000 n trailer << /Size 15 /Root 10 0 R /Info 1 0 R /ID [ ] >> startxref 2054 %%EOF HandBrake-0.10.2/gfx/icons/picturesettings@2x.png0000664000175200017520000000753112047445774022276 0ustar handbrakehandbrakePNG  IHDR@@iqsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100rIDATx^[͇w~ MЄ&d5T׽qV14aBj lCBhf5fhaz(yV'E,y99mo:euI\]}7ܕrr'.||xY/Z\q-嬬%a:!`rXp|˲_]/kcxZ?ԎKOƈ紸^=xyT>k~4J BXoT!/~$QQ[=Oq8JC=1A#3CC|b7Z ct|Z<#4l3kha^?@"/̘ހġk v9#^|.Z޿gO}kR붹YlGZ,s0E/ v5-_h:cE}s&z5>rПp~"\cx30Yχk8xv =gxmB{%Fy(Fmh.$AF?ӱ#Ԝ]˚S:ޢ3 \db.,c45MzAkeQ5zor滾D}m覎bA?iuzJ V##ZboüY^\DXoosOϾ]}\gp5H49)>Y}7'XpXO8]cviȚ ]'ߠc>NTh6i39X۵) .o~퍞wsY/MA>>fo@դ7rcT@ A=gyfrsô>!rͬC<:>"D9$Ž&qؑ-Unjy6j౾ԐzĜbV.qC+wnGX,c3ў7` Gá7#)E, {gX/~[约4Йr>c#v-~ ؐ!cCv93<!ϋx"ݪ.Fϸr`}6ԇ3m>@M5OeŹ'{_@ׯzh'z:='=-$~ެ71s{Կ6ԟl\r\@q]ҽ^׭jL頶 oڥvP =G+ϓz)opp.7y~ǟ9˴q8w;;Nwi[#Qw[!'tk@<aƼ&ڽ}[w`}yܝK|/c'vީwjbg{`w`Q|o+7_1bKK7Ϭq9 wkj^i5S1?޷I _:`ˆ[[V9m0'v̳86;꘷ZWӼ5\jƀ o/F\v7 `7&ޛ>f-μs߬וfD\a=3-: fK[r/&ln`& 33yp7Mmn86ue}#tOmfp~66 pc.Xx? 1=8?t kGnjc}&;6`j}C o|'_=,gh&R-kTWkY J=^wA_ڏsת5$jں[0ıN^b֬X zZq{XLc=>f$~>л=cC| <ߍq ]V @X{lkX/ Ɣ#oU: c;sk\ĆNh^j7]b'EkP~+\^Q T'W4:')|yLؿQmmĉOtPt+ON_@xAvX^^zM b+,f.uȵZXh9 L<=ȁ.jbKR,, %@͗F"K|欧 ^cslj Z[kiy88M\$,Q_C5ofLEY3xsR/.TfKr[/p(3ygܟ.Sw~KuFl?}X!`aqZXX@p_/"b VαS5wbzGDFZ/aX|"\haadڢi'*y$y$kC6K.ȟwX|~oO~ڲ>8O9Peuw57=cߡ>/o_9BǸp/{ 6,&x)jtɝN4eN_NSA,ЙM>vyEx{;x˙@MIchcorcy@C:UԨŧx6X?kiΎBiT33otSOWlFәgZŽFo[\+;G?lx團ZB!c>wr8̯d--:B&ͺFޗmȜr~w MB>v8`ZKM3Mq},ǘ+x<s4S4/fY?p=+4| H i)2^,5l~$y|:ulGO>6#fGxOx܅;;v;/Pیzy~ݽIENDB`HandBrake-0.10.2/gfx/icons/advanced.png0000664000175200017520000000215512047445774020232 0ustar handbrakehandbrakePNG  IHDR szzgAMA a pHYs(JtEXtSoftwarePaint.NET v3.5.100rIDATXG՗nVc- $/{śHɤdLdN@ϠiSW1[m8#fHp3x/?=!~>KKbx(48@#horqGa0EQB|I*5) 4$9A腯 V APo0`SBAi5B' Ypd= | Os_>DdT* ?fwEݗ=ǚIc7㳷\Ƀ]]]π)SᕘNtB42Enr4x,*=,\b&/'9ǚDgx#^\\PO=߂j΅'j!2ޖK11{ޓwXz:랹 #wqG# \ZOdVnxgPJé{ /!XIU? y^qb-H~P)~zRdz}ۥCz^WKo4{߂g n8vޕ݂NO}qOރ?Yv} (wq5Թ'gs0{ǶmڎCN"d#~i[iyq5oc?٩%5lVZMj$6wЈzSuRҧLIJV"Dyj&ǤlL?fqd;4?l_HFcc냺qz7Htu(Jxba>qa㚈kA]aTfZMӵi.̎QY zDL4,{6>̽_hJYr1Qq(RQRU!ιV6l}E釺|+Ozg{ iIENDB`HandBrake-0.10.2/gfx/icons/settings@2x.png0000664000175200017520000000635012047445774020700 0ustar handbrakehandbrakePNG  IHDR@@iqsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100r WIDATx^[InIЀ~$,* ys}j@oI3sȬ(9A:<3nJ93^??__?/x~60nD_7#'f5op~j^g> WӗƟ~ўn J"{<3"rF<|7kQˏS~qcC78ba sPQ#tKokV5Cy8>>?}fh}߳K3(f42g|:{-ݙN+4,o}fY><A?xϩ8`&A\ >ioOӧL<&3OO uLOя=fU+u;3Ǚ1]88m|ǁTNoH $9x8!ߏީ=ƼocF9 BC30(O9_'iwM 陖kgǵ"Y_y<# s O<1k Ǵ?4Sy^9~y̿ :LxFn&8 \zXO/pV3z*<|O},L&Xd2Ɉ5@BHoϿmVO4>M'~rqs龇{ܙw=̳5A6̉_uуjNRJ_͗ý} C. NHXG-{`:fgA.BoEM^:}ιBݟCNnHm 2︽ݭ= ""s89ΰj\ƫ8%n̋ϋ_ڞ}Kc?ۛt{sas2?uﻱ^j77lypL̳|D&\c \{pmcw52_IsסSGГ\x-#O~]__kʀ+쐣jWWs^1ϝ^n>U}VKz ? |C uVs\^^& C #ns^5}6i>EȈ`TM\r{ Xt!8zso\tEkS]=pvv\p3s9-󽾊+O],0]ۡj8==M@>==JLxL]1O^<s BPϩ#u-捻CZFG%jG>|t|ry5NIUY<'_z-zJoh{ly=O{<$8(%- +A^0\Ǻz{NA7;W9ۣ/0yãtTj\Gs3ȳj-Tg>]>(NO3eR O{{{`/1a?ۜ!%3rvV?:K= `ww7"s^݋Vr'ݽzi={:{y\4Q41Wrz|'vi~;;i;;;;'îwݪgsND֭׺U=jX: }y畻Mm9l$Fp#{Q,.p:׬N-Dӳ~ݻЇ'-f f̛ .5`k{+mmmEl¼o\ԷPC/絰7c8s(lEr,*=Ӈr,9y(>x8~M nͭMJ#n&#uYpyԃϜ<2ZC]`'r#fXn766Fml0bΡ3f7Q?k2 ˃{CWԖ醞| rϡAs/`}c=C԰ֵցrx4ПOezVG,IF##Sn)򯈯!2F6WywDCqsutZjJ}kG#.xFF0A#>hKX^Ӭx7{zTjT3Z=vWf޻=c'0ZѿGWG++B70@bʭXg +aN|E}\|pSqVs (n|7b=0-Scy,E˥uϕEn|Yh҈G_# '@_5v `~g{~#Ks_ϴtY_ ;LIENDB`HandBrake-0.10.2/gfx/icons/audio@2x.png0000664000175200017520000000673712047445774020152 0ustar handbrakehandbrakePNG  IHDR@@iqsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100r NIDATx^[_dY0S3, K3IRIU*?WIURI*IU²OK40 4 iXaiiiaX< Ð s_խ<>s}ܪJ?޻ی[ڈє8ȟh# 7|Gҧ*F_.}F=.?a@V_e>_x#Ì{9 Od8:xw'rVC$-'3Z /EyO4D[!'?܍;N}: Okx_%)w>R2W;9>=9; @%xP'UGItO;pd/st)pt$J#ggKY"ْRS] huK ~8f07":@q6yf~t4!%7гkG<A~&i֟Rs}, Xd," i=3=Nn+0g8G>\C_{F䅺ִ#(jXb8hm0^[}jurKxĹ^wK軞b< iOit:6D5؎+Sky~W?52C?c3p)嶶|;kRT3[t1% }19j9p66="31'׈ŻzK 7O!ze0蠛M H38rNyUԭ.^XNz&YJ9Fী7FoHј3/*j;s6ky/GpmmXgv)ڳvں[[ :]_Љ ~uԿ]<'3Y+=@3n}%seV۵Z-nvqg͵orۭ6{fbxgϟQ;pZD?XcΤ6g)fS[^MyfObr대r ?Z)G{/Rh5[kV[-G| 05fW]jZ"kOw=ˣ<7ۦP5䚯g5_ Z,.wHz /(UWsgԐs r 6V "a,8y5fpTѿD~Y7+ j!,G=3|b /e`euŭ@\YYLy_P_]9"1Sozw+W7R蚾+s<+:rMa''/2LhD,Y{w};gȁgy K#䉏r_k)WAef}/嚫aYEM_Ԗ] #QZ #|zk-?i 'PЙ Y̫&=AM ^jV`MP!vk*f5ͽsS/({HB VXѬQΚÀYT#: Yx/l{]8O`iꖪK~i `T5.-:9@;rU#ArUG\ZG>1\7;+};=P&gYef@<@>Յ7ʂ_@becE/cLtyrtT[!!'ye< -`wzghnq̻Y\ѼĒhMϳi^QNЎ}>a@բ/5 w{ w4%\a=tyG3!PR]vL}KNMjYñPZo$ ;jRŸoJC (K+[F5t4:Æ ni`4x=,0$U19d "ICe|Yˀ~)tKVVϲdg9r2Z<4:HR!5IMWǹRӳQLFF H᧲!Or Z0o!PG "삢>s vjzTQ$jX9.J3TyyO5>j˲^fwY,8cpUYLCqReUed@NZ(DQ vUJz:IOLOI IQ*tWO=")JMeSuGPfi%]0\G?0 XV *xב(aa^9hpLymGp#^p|La(|j#&;8_5a@OYJmYLG`WRe߮=SzUQJ ,*NT2±V5 j+.Nz=4#*:QB~8 AN T>!r(ؐ|%sVaS*<8NKkG 8q%֩*]<΁JZ_#\;:8_n%X*M>Ay=>%l\^vh eM.+'b2NIFNfJoswH8Σ|/*GLOq!+Ӻ0 zS8JJ%w|RA K82$Y-4:b$4WN@e# 'D9NKb谍OKsx<'&F.RO֑FHQ&=0:''chپ =@F],3r*F H( hYFOb=cA4`aav$F0kfa4i 1cf'cʍ)F0J 2cʼ͎Hҁ!<\18Z1 ^_cn;uT)'WiꆒAy"6 ?Sʣ =( /0pDLIFvAEQ>|Zڐi-]| pF22qbe,AJeoKNw_ *Kp04%ʲR#UqyKb8N:Z>Cwr5(2+lH+p3ځ@W+THyGMu 8]d`^D4\rlxk )G1ײӗX'Yˈ[@s9ZKH|LLLlL|ll|bǸ;wޣ[ݺH`ԱcN]$%$$$Aqx$Z$1%dbN4E$Z>ƵjZ'Tg"Fv5.Ӻkݵ/d,X;Mgݨf_}~8c~\b4{~_|$O3>VwvI+-}K:I~zl\ +ܲ>x_޲?*ҫwؾkߌEs|x׀Pt\ͤloV=㘧ve/]m~:=\kߗ~zצmB6ZmWY;n\M<ۯg=k)CRG7c/~0c7^nyec|Xf3c>c/ K7n3}ǐV<| ;@9#F;wNo!;~ #]b珰~֖c{ٸ;_rO\sߊY}s!1+3vMMw6<2NW=H]ʝgG?H`mć{?nҶouj!kY<ۮZοxԄwm֡Eo_pk.^zQ+/vkLJ ~e6\VƞY_Mvk့~Egu窻KFmYV^䠩ޭWG?4?7[g֚pH|附oLsgoܰNx! y]Blnk `ƫ9{X??а7yC&d-z`oՃ^Fo߲h[{yࠧy1ol#4{[?6Ș kugxpMlI_S>^O7pk_׵~OySܸa¨n\s{/ұ xݥ+-L]6o_Z[Y#~Mwo}m_o_`ݬ:-L* q 8xLQ#[5 Fs 5$ 0RѦ$MO:HW*SO%=tT Q:gʠ$8M"L~G.2# ?ɐ-'F$ni˗YqےE_|d)Y틩/f,Ôae+Ad뼄^My}~ۦy-sA.m!MqS 8ι ݈ 5ڋ OcFN{AjQ0(_ Wr,/dqnSD&EJ #3c1z)Y_]jDɅ=~DZj.q暮Xc{h &oz UFJ~H8A3WMfy\q,CGMK 0AKґ9%5:zl&pKtSŔۅ 9i PJTUn{>s:G"Fb'$"܎ RM ˅ATs;lE4 j]Hf̎܉c䯗}ow1VR!ˈ%EʀC>\bW㘊wI`N6AnDQy\27= U-Jdv.贋 -0t'MxhRzYwIEQlx|ҦقE\ JpZyīNyGñ3@0߮HZWb{8E,E*]q)ӭ]4ysmWAo" ˜w 06cRYX-7STt4SF-A/Xp pZ⢿bKsؐ*+槺-MI굛nRyﯖh#xZkgDJ^*ا2\|}S~KeV7p~ʫ !*RclfEuz`7&nb-9/9 @%u;&ܺN`$h%BdBT^ etB1, _P.Rۉ,E-vk% J^/|<B%$fuRjo#5Z,{^ W%%GzY7QJ5tKtkm5Αܢ.OMyQz4.%SDГԄ=^&D&wC.yrsnq5Anz(ɾ$SdSp[:SmbJV1uVdm쀇XT.57m.R$n_5kZ{J |u^vO@zVi&O(-}5"Hq'(.v$rC-o-(efbDA2LFDW֟YV]{r%7E_@߸ 5QYc%=f姞Gxw:zʤ[Q2enlElE=g=Jmo]O=jl.tU31:`ߑ+%L"S4m%emW0^_akOa&a-7l7l\? mSj1RdߖqZh#?ADD!w yQ"M!SKQ>2^@ <}5R[!r^0BlW3[Iv|젶rN$pmx8Ұó= ;ٰ ;9lǰ ;a'va'vra'v>x#Oْ|h" (XyQd'\'ADxnN^^=J(?Z T)7p(JT(ZݘҢJAn=^SZT)J=0EGk?nad?9>oπ'YI+x=FɆf,q|2µ;vRk-b71~'Ӧ67:7#Z(oo6߿t+iq(λi_P<l@Y0 dAkڝ*)D{2y ֹke vIXyQFcP|{䪖X2d@?K䴛I4]%q9i:^F KGZ*I]NW, `I-m!ėr×? hnKyAvH\D})/喖f,Rބ(I/#\_JK1(& ?G1(adžB1(~\ÏrQ"{~ٗ(b )κ,ɏ2k;h%#Q`DADeWe ֏2nȃ>Dɏ2AD=g=W)Qf]<(Q`DAD\@D3(3D ~<(CyQas=<@,BD=g՟*y1f8<(Qb`DADz1!ɜuW1.#P*\wKFd)8B.B ?\ R%O4SΜf}ɜ@_L"%D2sh"G1=A 2_72mfQ]+_"KjJ~#4F)ЂXZo "[ ֙X<}v"ȜϩlOds>M d\qL3[cql_(t:+dte}7HӸRO3OҌ:P)DIzs fY3 b  7B_2B_ ~/qџ uh'b6v}QA3ڤ Bx/h}cJد6}w`[(I`=GУD;kk=$LJi84 SZT)Q7ReX=_ĔU r,u(RPnѣ)-a[0.n(_pE rQkLiQ 5  uefLz:GY4RP>WNC5I(rLD9 u)-a:PG"(s{=)-a[Gr(~SZT)ȭG/ (E@iZ[)=d[2e{zTҢJAn=(SGQR[m{R(x]'uDL'H ?䞼4-e1E=:zga׃zGSZT)!3=?xX?=;S,(A'!w-X?ZQ^FüAp.OC[#Z(CkBa??i9.狘;?/\QբhcJF FDi2ԨElg< 5bJFl"ޑc@z)=~0LJy+=e!QV]G:w)z^=;/'=%-l%ś6_AK0c[>8G\NL^GY{=a`(N(RpĨ͙kt8ѣD^>R@¼sZ}p?+="]zf>[<-$p}Pjc( Sx>;{gWE^jVZhi- h("b.%Jp22-=+[%RlOת-ٮVܙ7 S]?̹̙;w͝yF2SزџTb'?OFS+{fԞ'=4`lꦥr d 9d&*MC67Oɰl"[Tʜ~MRLS@T=2u'+lyܴj0IcКahkt+"U"A)\8& ljs8ӿ[4hK)@/f'rAG "C`nrE^@/&*39KHd%}DkES/Rԏ, mB1 XmV\9d^蟫,PWp\vh"D#1 r*|Bb{@6c/LFڀ>@l3b@X#= "]ڀ+6#DХ5 S`erfo+Bo*pqz؉p"zlg79`wh#\-"!gtDCjZF3–UfJ oF7[-j5KS8v?C\Կ(˚&qcSyF72&u8"3_Оr2.!QƑ96_ٛq+ӆJ& R|e1(E.(\+sJ3*JLA϶<9ws8Oy_zH rm}wKۜ<ͩ>H'I/Sj`G8rߦ9Dߧv<GGI==~-C㔖~Oiĸ`z1VtxTK[2w6֫=9}:o}r<~Y1~ ӪǢl*ҩߢo.{<۲jcSy~ڇ9\ )=x)‹6+QcgMv{4ƗMOy+$jSc)^ԏqۇq6cp,OpC/ǘC}FR`Cu+6̴_TǾC}6驠2m@Ā6,@ q9zMhVжnf =[Z[24dZ[ٍh] smճf故]ZF [0ѵ*5Pet-k]+ZPת;V uuQr_̦$a~Py.~1Ԟ'ސ`Iђ<+,7DK*ϚC` :=<%3iy/}^X 7[ʳdvO{MCX3׉7Ny{ *pIMn" ֈ z'^K)$乒w`IђA+1HjJ a ͐m$y%qO` ͐Li{f =m$y%q}Xn=v3d m$-J*̋N߃h7CV${WXn=v3dx@I $q0Xn=c{S7Fˢ#3E/Cߵ귭^)@V?U;@ݱj9#} |̜<=haKJF{4chG=j[=FXrM隼%]އ5KPܴ0X5G{VG4lك5y9:skdsҩ`DE`kZ0hp\0H3I)Bg'<9/AI y%<Òz=gvHZP2 z'ۭdA$eS6Yk4(:ĸCt!:D4 }dd&z챷kW2G:?.c4> hF Qb4A hFDKL Ο84m)i<3 ukJiiBEyAdsMT<+Ӻ&7-}MTdkёINIյ{-^)iIR${-ӚIڵ5yKR${-S=Kd%CVF\Xn= Zf!Z-DKZG,AdzCVdzPR${-"I`Id%CV#I=8$z`Iђ!+${l"K= z&f58'|t*Korg[y+WY"T9R1hщضtw>PN|*${-h#$كhɠw̎ѣkSS Q\D 9<&n|ĀТ;.D6gߐ9~@9D 9<ܦ "Xܹ,h)7wnlЬ/Ԭ;l6OϝGY?Ț{vcꉖ9լ-:JjЅ F[4ѯ+5P~mk_:׵P>8uLmщ,`lMi?oxuVepj?ߘذT1N,v SߧPcO4sOך yOd)5='ΤuM}{ 5>s3u4~aܡ>!JRy}=qeZ);|vIygQ E^jǜʳN+jq1-MM_FM?+3O*߼N gghjjڦ @x͍sǩa1Wvumh zZ}WOyMV㑍p >+^4y3FҺ',A=KRgx"I}lÒd%XA:,I7$֞ >"6c,~Jqil+gzĢ sxoA `\/rԿX]1(WJ- S9-4Rfz&Ŭ>Z=, dʃڄlnZ`9ac!D6^6 A4|_߷=X(i!P=66šg8d'ÑMk^,"J' !{qKD?z1зonZ =ƘK^УnK=BI6X!bKJqK= OޜN-Hj}TL$ՕTg"<%hl1 ZR{Z@RaIUp??dhq1]'LOGJQ^wLm@ĀmZ 1 rbcW~ksZ8cݫGx TzB'R6Fmϲ6= =Yg7w9զQP]ҧ۫NW>otI7!7cp{Qٓć4/^:I:g5EņSkZujՇzU]Epi*RZ&9^r熯'V#2J]FH Tݬ$F(DUjd ۏWj ק4rտT=JT\i(/Ux\5o/T~fEa^҆7o/}$QO0oMvRgNgqϗ[RTP:I%AO&k/tLé>Zs|ۆ1~YtZґ:ߓw:uRVuU:(kZY{r\:YTlo3yy#ZRs-=9rO|5ϑW|֧KV;1z *o"Ƃz&?ڀm~_3 1 r1<|gS^A砖%~i8 9UX[2GqP^ׂYڛν*\ <Č#~]HṄbňbĞzmS O7~zœsկ)9HeRxFym,ay kJ*to@/=1 FY0_7Cϵ-q%r#\/wna[fna{V3_7|GꞙkԽo/|]3_S|DL5ue\8ݕ1jzmh%MvEH, a1y`>z#̴F3ʢ!P2M|P Maz5> w.{a{tiu׍nϼ.?Ly"X"iߦEGIXTI1XchtcT-h& '[3޿]厢<,LQ",wҩ0Gu"YЌ̑-X[0ˌnƎZ1vcB3.~_-: VfFm_eFWq}W!IXTQ)zV 94:襓* Ր-cP*hE.A:}޸U;{i\qN to#P>mlES!rWI5l]tg4hlQ{vI4,w(\rMwQjF`LP+JRG G~FFA j%_W߇̳E5uPKl_<*J2j֋V׊sUet=?[ KY*rN~=='Uur0񝖐e϶\=vKWܵ$3dž26z n3f0wc%X[`}&~;\?Þ8܆ $@lqlT59㽹ډ}J-wFfޯ,,4hߟDɻsþRIW̚*JV缃ǠbT1/l:fڨ `K8ptvTGb43ET::ua\le6[w)rif ȯuZ֬^_$z?>M)s`0_KNL๰}}UW)ׂ]T ct.sSmuvzsay.l؆<<éS4/wVzimξ4[^\X fh-\Hx8r.l\΅F^繰>vυ2g}e*vt3QJQq)ǠbT1uP1)4y¹{lTPfVgx֙z:xi#sZ4yd3fWZyn*M X.$5S㈜)HdӇ!qQ(f~as? ,osf ]!^.C1Dyd=wcu˔ ԹH*e~O^23{~䍵= Kӄgx6` " {:Q}0N-%z$ٻa[*w7Ek1XchtL+*@1]ɳJ/d u`ȥ밗P8o;G_ѓv-n:j^!^Tfwc!z>Z7EaG&Mt~C09KҰ)W{~⻋,`~5n)n8[AeB Smuv~M0AXfB#KkXQK7Q8igr(y ;Th{-¿.k k-)kZ|2Z\UA-l0iw"l}FUewC7*JG㖪d2n)*K6{w*J^qRU`mUiRU`+Uh,*Qh3VAP<8*z.g;t_Wq y/FՒiאqdì`17dVY YwfdVYAm#oYLXdVo;0+ht1+٣,f4youVpŤ'| v+^!}T2Φ"* f 1j >zGkCٝس6k9Vښꃇ.YI\6VgX 2/a[ә }GĝyA]d^п0|1c3uQ^ZyLUEIN~<(έ9媊'WUYgg:U쌞lۮc xA1쿝 f嘼]x0; Zmycmu`b | Ae1;xAg9Żq3I*w5,rz\&J欄u `U,[MbY:g1FhMJ%J-rQ=twĭ&3=|Y#|Q},@+68ÿW.s_ngx2Rim8ㆌ9c dM`j珺|^=+b̴.i.|fCQ8`vE_f.j s`׏QU|S(YY,ڪlݳT%_PUnJ%X[U\0M7$lR%9<}=Jm>gb0j0. #l}2#g2Q2H0I53r1#іcvS3#Œ3r1#gV~䍵= 3rȱ͌Ō\dcFΤ:#W>| iYu|ʒ5UȕDPWTP% Q1TY"./݌U2r%[ڜ Σ*#35z~vC}nB@>ѫ _{c0(.}rN֘<}"x $Clg|UOెOY˺}= CWЫwS0d E].}zܯr6MQcҜXLʫ2;>fRI0 ]&c:h)XqX-YYe[lE*2E.s"!9sdxwfxq9'ywfgig>lo6Hſ|l"eG-s>%<-[̻^%6fc7o_ߡV_}z#p1Q^;z_=y>vc!חRzpS{t[?^|K;MuSeS_7׿|l?wӱ7O>}%=m,D{cio7;A82ЏFkZmpS>x{wqlߐnh恿Q@ޓwyz;wyw6fmm_<|o~766EIφܳسٶٶfs?Ex,7l_l<x,t~XeOş̟k<XXXXwX7oXůXWůX/X/XXOX'OXŏXGŏXXE_6XϿX|ݿX|{lY9 7g,ܜO学 v6q{>j &1-WWϖ`U|25)yuG S,3օ/^ыQfltν80:Z_bmQdMя ^[^7\ք-CK%eKԪhYF:η|7cKO#_~&xZCo<ժz>>}8>Nս}Ïo-gh+hىci5;0dJ(M/Ysl6gƞm]l9[=S8{d8;7;77kl2WMgΩ)Qi'É ;i'dj<&)wڝ*>#=U`hl|ll< Iw4'ғ##G# #;zXr+5v.- }H^2.{\‹N.pnSNYVgf`W۶mݺ::D&t5vz-Fw&"n aZeez2:1 gfgWg K[~vLd6N 3iN[t_|*8}ʢjN1.3VݖcODύ묕l P~3g߁~kH[FfۢӮp]]Ѕ@t#TboZݩ.k]]{nqʲ܌j װzl݆Fftgutbrȋ:f3E&Mx8͎v`l2mOipJjxn_ 'NT& {2mLG |w rA7 Yj~tra];IStw7edžGu;umN:AP9z U~+MAپ`?:|s_kCq?XW4>+qYXPC涗\FnX߷^mg?ROVh5fن vPjnQ{j1!WY5Cd,M\]yfsXU(s(9juXiZkroɤ:.ɩMFq咰Εva{j9yOZlP[ˁ=ꎭ 6 @Ukn8ЯwڰЮvtw`!aCq6S U_rћ[5whhGm'w8\ٳBg1,7/tCGlPw'6yWb_3_Vffivr6M7ZLM]mA/S.۪\zɀ2AݍsEb.P8Hқ!/{8Jxۇ3u#8&#qw/Ȥd-uXCnS'($$e]lc ̽O};TA+ﬗ|'s %/l4}.θY7ӢO;vʠIvґ'фV`w3^#YwL9LɎT]SÍ6!.?8c 1w}ӀEsoYZoeUEnd0(8wE2Cw>q~Qo3~'},} {B(L\63+")ko K\b&L0; dHZ:Ea=0JR\S,Ҏb1VrK6/fF\֨T0ͪCn"ŚvSV~;*@|<*aZ'י/D)*kZioQ2;%86l5l%,>  m;3sɬ26`dji+k' ^QiGH@((#M-mvĀqnhy4@׆yEK/ai1@ _RNI`p SB]cPKIapN5j/6^3~ȖP~7(ЈV4-SVUZ%CZ;$lr }óJW6,<Ve0bWFGXxv'\Ouݙ[&|au@_`^t[ogkμ/3[~BŝVy5MoX-c :ӊ  Lk~߱1atl|Jٵ~ocԅOa*廻ssv}]1&={Ɋ a8h븸ǘ=V|U|J~ mm_w9_r#..^[ %\I87܊ 0~[wg|ES?ɑFr 45Byms5Ӂkh:ڹK\eMP^?*큫(]]#^( s? *PŰgB]K:Gpf.;c3ܙ9#7[*(mΚl6#̥\ZFs:mf*pf)3Y®2{:ȪgW xS=V7cf4P_ 'OBMOQ*8~'kD4fxldȱVUbUECCðw 64ēl TU)P[^pmӲu'={zP{XA-qGLw *,(>biTKPKJ>tgzb(<|=P|Z=Bel[G=*@u<_ڷ߾eP(B^ֽpܷwy5(Fm;14E-T}iuiOEfگcvp ryEU:je6g9̝y+XS0#f,(<#C((Mt3v0dGf4!S_^eς7Y} k6M2lyq \z-c?!ڐ';MHŤCR FgJuqnS#%f\He&4٨ XNrj4sIγreyn+HϬ:Y1#sҾˎѡ0=33ª93S&.'Z? SSЉĬEdoͫbޅSZ.ԒSݍI qHQ)FOmѓMa C˖|9Ցg▖:: p fTK,WTP (.iKݭ}^EV.YO_r=#ݙ w N pYy`kigCv4.ۺ}!X+&NBuZY`8ĺcS:ܔt(;\lq WCm[iױ cˍ>O|k?977.]v?9 y^o.&z/%.$s1A[u * gR3KNnl/3[;9wg|:xs<(vrslj~uq\/-/>܇Oz{ } i1ЗRw_kAIPޓ|^/InxBZ?ՇZ4Zj5' oz\cMd~[P so>a)}W%uB?(+xG#f;r$w㋑+圹Ɵ'<赊k}w94{1n:fP;d. ?6Nj7ӽiRw%Ow ;`:./j wA Κ`2iqtͬMzOgފF6U99q9v:h-,k = ݨUVAȂ T\t ȱeVd~Ӯ: 'I`w@b B!APv˾5룣uRmu;k'ɮJD)nKak"BzZLV!r#HK)4]4U 6fդ싨h EYJGWȹpn!ۤLr}+ .MC@9R?`cLWOL-ۛ1X/: _!AeP|0KFP c{Sb&KDB]Jsfh hT;_z~sy_ɰ4${fBGjH3|sZP 5|K Q_Ԭ3)낉EaLXzo.7)в21=:lZƵ GּY+Kh6HY䞯Rk$à `+AP܀m^g&cB3jbAʦRn& VV#8l(%(l-Rt&0Jo~&ITmř,2=2( ;0MK/zMj7P (Hב6Pz;fJ5mAs`tSv\t1Sn(f!gbv ]{G}jHÒ9hR.hP3Oݣ!U ~@I@*UǙ"^f2=;Ə#0Q᠁n Pf|m/E.KK#BJ4մukq1'cC60d4CC.tvapvZJATM641@H3EFtoXaOk0m%HU(rrziX-qOCm1gZ#U%0x&$4jR-+}05$dR\øk0QdBk עnK3n%h0Ա[0G1LOszsK ͸DV?4y 'LMŔ"pӿCA@*/cWNP|B#H&SMuuA*U?|Aw%Of"*.PXRl\Pn nyִ̘!w1l3=104i1v';?fƇWu5=#~|"H2 [ }3(@=}t.8Kgs?E,\w2A96̧~>׹sEuVkZu:kZwOg^?5-OLt6v˳izPؙ3ܤ)BupC3,pMI+鑓rbBeY\M,u|!b"KWNI"IFm@ttJ/d@ d ,m2,ԱЇkAi3nP-Xi7j{=gB#)ƴ~; ~ta|Al &34Wٕ^rkR4(,"}*Hi]H 5޸'ꅂutӒ N m*]9R<!dvRӻxZzN(^4D8,t5AQvX[xwR葸S2{L |!@NBQ vJh0^|-M[5ةYmVc_Š~FݟboJ,: UxfkMSɴ12ɶx@> !F=#zhU2Ƽ|"Da[By=Gak,R B80J`hYiS5RN(ȷr9ƴr`K(q*t)EF&qW@Šb-^=iJfT)/կQћ צ?kz)vҐ_evI8ԜP vpVͪ7T]㧻PŚ\\Nzռ`0!?iFJmeHpA)?Z *f ۓɮXZPV?c,9 _b-I`Zzv0} a /(-մ6߱@:ۯ8:8iu"%O|3(K8O_NO-xj%ZĻZzwJ,υ0"0OWq"7aBԍ-Rz% g O@ܳm%8@l_) j#EKp.%Y- T[!s*7wz[{^z<~ N[+Xݗ/cu_>A cAS@8h}`cO D$"\h^Bu1rJ "j)NpXݗreDE7Hm- +ʫrg. Qõw_KH2 ȕk]Whc6:XǼIRIvM!^CjXflžԇl䆹G%"FA(ɷo\[~k^4g~wgl*/@@f/OT){XدT$v:#F05J^SU.+ M{Qrn`&3x,0ї"dKrF{O 㡔WU 4-)4Ky;놕SMQOdɥP+z呔KyDxSA#ۂҁTD,NpA d&59Z$QN ,M'SVi.dJE(yӂ8<܅hjR rOK5lԀpV%) N b͡ԩE\HYKpmS/ ' CÌW peb 3.T9 K1 e*v)yA$Ȩ"AW$FQ{l-=aS#I>1q<ʹkU":65IBTnәЅDjFr Ap#Ztهz74XH*9{.&hOq|O5ףQq*7אؽ 9 L-'5 \jȡ,:zJƓJNζ,#\>>ST|0`̐ <&3lRt/oDwtB^WpLMnFEdmj,xˢ= '':7m '>/V{4K`PPSS'JITƐ~NYqgy.ӭ˃aJ4tZ*KC} @IZ,Vw$*z,Q()`yEq1 A:A:f=TT9T'((7of1 5)؄uHE6dL ظ1e5I~D=7~M9<,ygsSn)=C[ZOYx k~=ʪl ~"YJP/z`z$ϊvik#dRo`f5FKm$]3Y7%B MT>'3JIJzI*LS[&+Gb@j3%`J(o<jN((~b4Qo|fD >QvGcGqh0 paʘ/@+*_)L h̜&u&jTLAblJH6 {+ וFn8_V@Ö/P X43,(T[#i+/B]->HsÇV- 4r818+6M)CeW  B;\1ORz#j+YZW:$\ʻ2k"Sӏ?k%_?+Z1ql C a^kqft'G|4oV$W{"ZM^FDJ6%O)\we0cJJn( 4Ϟ(悝Ku28MOv:f ùŰ|׋Dq&DqAxC8Y6$Tlt;w +-e#kqvCv8P&gi3?nMThA}e$zGc4Ktaw PM鈇ٺ5paՕ3|QʹK\]|!O|LgUw1%-,B:.4+.@3?R'bMicv_z]s!u&RTMbPJ.Jؔ}" d=!1L6MNu]RSJSCj% seC VxZՔ|4N{@AO%~s۠1Ø1J[?vm/oɃ[)> ƥҍr WU|U0K=)wyXf7zo߃D}\ SMNѽkϞw7gG4MK HJjp ==;qwT礍_&o\^~#C_U_ʿszFіݞrE1ս1o gx:Z%2&,VXnTd$r6|M hO=hSz;ESkywByw&DkP6AG<G=w!A$=^NNR&ːfmB딲F=~1 竄NdkMkP*_վE@jNilrI˂*T'1fW֢^P)ܡV%!Sv~a(@p0 @%rPpXĻ??@brgVAmiՃ1:2 \ŀaa ;[>&~HkU^0\&+y)S3/ಲ: )!of ޟNM).^ҿDҹ3VIJMA$e`)b3uK6DbւQoIU1*؛ d.jEQ͉UF.U%@]2N6dfb76/6 qz\ro$#FyEhOѠԨcxR@ɨцi]r ԧS/ 64Hb`A!@=(!n E|C{S=b,FUNIȔ/u_"bC=FG9JQq\_Lՙ^CVdpɘV}Xy%cpWC 6x4kq=??L;>3h;G? Q=|u}W~w%׍{虨"«TqC%2$՝2*ܸڛ.Z!>Dw O_B6NrEHz E!or)տbCoܬ*\e&- فzbQP#JylQ?i0xB^уStG-U՜>S//>:gq+',sv*Enrgsm)ru 2֭7eeQN0Ϥ:# [ i̓6oeV_‚=\*d@{7, ;j 2%Xz);XmR,@Ō̜_E F3+51&<~p%O(9}ra|طў}`("+d-7t-J X my}vEG/w]29^oPOPf+O h}/ jᛞ~r|+qp0I-ŨՂ2|Mϙb>z</E±jU\p{AI#ˆ1+xzbv`uo516a`UAE$4# ֙z|wQR4L>mڒCـIC?Psآ|>2:0W-sb,<[kRX*7¾l!Lf *(mfE#d 5ij"tWz`4#%b"yLT*D7E0SUQ̟xtu,G o0C% %Zj/RQ3AX$b>ӽ*[~`†9PtxpQ א/Ӑ({t&rJyn 7:a!l`QkH :DFdd񥋲1xu17H=h|1 dES޸`,%VAnD&kXe֠*ڌEhw뎧 hz"L~Z%^L,mV.DT ≽0ZާcݬLGbi:WjE|:x+m* !TJN)y(SP+mH}RhU{b0U-@t%x-beկ_U~YQ_Lb|ئ ׈)h@2@o()pxYu_ET.\m9]K}y wzXyu7@~olLd7>IRbGe:rEݖdN;lI-pIl(q ؁?|{IqH5fBNWɲ~w-fޡy3tzovwfwvGdM)7Ѻ5L4 SCE[2;[Z[z}7ycۿd Kq6暱ȤoÔ6ص2^3މvȈh.t`R<)܇ .M+Tc5a7"l%Tw{'xͽ^qA 1&i8@ͱh ,L謡$ӀCl[ XNOSsSg:mMzOhk@NF [Jev>wD{O#ohb01إwC,y>Nuc&-V1fTup|`W.Jv:RG 1၆F ğ5d'h-fgH:841#[iR.ӊmg1@1 ؛e y"5XG@̰h.Lk!{]`ewph d.`! 28_0Ԙ7Í.D iE!bM%> lv̑gej cHYgJXg>Y!='j >̊ג̊;CyA/4iH43&X3<ߣN3=PT̈F $rnӀ-5ek{ 8Tpo-xSZ sj܁.X;OMDҠt_i LܗS8|:{[?)?xvS9~VhX9nn gn:>,ts' ޙkyg\`?3?9c6V1Eๆ'dwfqb;忻b@6;Y*o.fs |~(%Z$^i.ߔ?jDlW@({i⥦b8߳O`Ə|#H8gj1'.NY"(W.:sk<ڕfnՏF],z4,[EQ[=C! "-# `D!<瑪V<8ya,_j_Kk, @3puuK3v C5p㞤dKMMTxg,tRݓ?.Ţ6HXzDyK^T!KnTAR]tբthf)&@HxQ Na@0da`P()b0{dN\( Js9D~Kq~h>dۗSH6}(QXLE7[>SLdUݭ)z۽tեnݕBݩ dLӀvvlOKLGvʎD{G{GGBGjunO#Mn5mUik@Ŵ*L yҬ[LjͲE l&լ f)l53&&N6)D\PN:UMT)/JT:JJiDK22/vyLsX.63奂@E|NǪ0A}]i:WWj+UW999W:{|JWw%>{^G5'-=={*=f"cq{\q {W'W\~E/.s.L]0ĥ2}٥] J\*RY>+T<+H~VLI~yxf y gh q%n.M޵{wKֻ@ڭݝv]fkN+s𮝻NK?]OW;d +..zA]l.;]sQiqyD\44qAC9Ohi.J(>&p/Z"ʞRv\&0Ol~cM*20o=j8Qs9x@T [̏Gff_P?' p{ww';3ߖ-ַ+Ԁ*@}CM|=(@kk:e%~Y}Q|a ~_\}gggԍS'>y!!O|DD#x q y8uc:1 lӀיH@>1 k>p[7g^e4S.L` ̃2C :-4LD=SPl_7SgY\2c9d39_7glGПyw[=99ySS88'ss󈀅 FUi"/蒒Jhwy{Eǎo PgUNTNc. Oqu&hB3yJ6LM)1-3Lsl4v*D?RUS3`IP&r%LGL5]1/h"}AzlRz~@~Nm13nq1^=LMSfթӓ =LD59/wʂS5D:##PVZrhS_=8ɒyWY6yt:I$DD\*C,JLI#CEQMSQԠcXLhbmР$HT^A3#uR4^ r\DJUH B9+9ŊG-o=)uL1-sJ3O{*Ad/ c Z^V (A&PJLriS{ٲnL@nnV)pQ*fBuuvvL.@Yt.%4 0 ;t{ԡio{MvaEdk[k[[k1 iiiE]*h@ffs BԒhnE֜hjf}2V2e&RESvEXDL")e$|@*}:%bXawZ#%^\z%P#Ķ4mHm\CNZٛBk=Z@Tjq3i܃=!LݳwOG@Lm=.I}NBI S@0W/rP1P6ԬvB}e_FreeuJe.ιKͳ/xg>S4k3gV@e\z%<7KGb S06rC +wQ-wzڙ `SJ!&ma%@3-@ V4fjA;v%LCoOݶ}[ղu6Dju۶5 މfD8ۡ]^tG k"%);*I*Y4ʁT|$wum@vg.[L˘؉3yGJ؎MmSX$m[ފx0Nwj:_Zyj8FS7)u L7| p>yCu5b2f9~=i^1蕊sj{G ?22wk=|V u0N `:J~wGcu)4cq;n=Vp?oCh|+MzMŘ@γYo#Cu+YoQ5IP[p'q_+Do'|8״jdL?^9DU"{'bG; 2;!pU1 "\u.w>-]La&I6BaD $#(Y:2*0B0 LaB`P U*f!u0z̛[ʧL̢0;#LJJZ*8LNLr&['NAMZMȓ8yOBǤ(['sN-ϡ!M>QA \OI m!Jʥ>ՠ1FP{.ͺ)5 RXN(J @k/]P: Ըeͅ2jUԢSAU =/f_sggf䬙1=.kS*j*vQOMM:&) 0'PQ}A.PqF1@Μ!PqݏHޔdvl2!'B;Z: M( ЏQD(!4@&H4W!VHp!WzCuf=bp֙54hĩ9 n$3@uB0!/pNM.j/G꓆k'^D-:c`A(3c>3X~eBYLA lpZO~&3 &j" $ &"6XOz9"h#Eϱ֘;~}BK[j|OMpHSJ/.,"ȧ|5g5CAPct mL+nK" "jOSiQ9j*Ɉi3ig434ߕpLv'!\ DHAP]W {N,;$U$41V 0/ܫ4e[])n"@/R "@.R ;)JAMAw0"xAE D웤

\@ +uPF-oyވj9u {)_%"8ND" 8|y_-&|SOӋ%) O#~uvJcy0t'D,@1$W)7zU`v]Aamxg̥֮[^3׉գWի=j1:9IX#bEuplף)P*czMp7ۛ(tW'.X;> Wd7餛H&d)<u=R0$iN~TsƎgǯ. 5jQ\ݔ`xD=V !zssyg87@=|.J.ըӣU#rXDd Vߨ$,,<4jko<5ꬓ#kj٦x[4)9{TI $:,L'\]q|+*($IQMf '/#8xv6m7p:iAz. C~!;}M!ɥ6׭GhbxddxdXxH`_%R#JJ{#*H2?%@1ieQ2YtR'\NƱ3M:]N_[O!VB (LО;b ɍ3Vȃt=pD uz"]^F.`>(لV2\H9YO׽j`ҍlPIqJN;@+Cp=q D}{ ݏ_;;;;;*op7̽jUܵwYhL(oxafu5i&Ͷ)WA}]zXv-Oe{ʕsŐ{hH&&M.OH):Z/bmS8ѝڞ\T!hX_[d1͏)nLqdx]oFwjHyוyN$#Kop+:.hBC&4qî?#묝iGûZbPVn0bvmSa'>(e4R jni:lijV<7q͉;i; ~PwM#.QN-f:qkk)1>{!Y܌6%XP {cvRz!| gűG% X%# QIcרY zHT&Ϋ}pŬs^gbT ]~=D5*YpYD 2*eU*&D]N 75SLRCԹrȘ'Q]2iNtT*Ϻ+0rq]u]rs1wUHo2!ln9cst7:\aZfv+bJwPڣI@a䯎9mF7s6Ľ+E_+RsI2 $Q@҆GAЈO@IͤIYQVOFƒWExjI wl:~F3*?EIjJF YNƻ0+lE¦_8yOWJә^Z.B>C,udM3chv`if|za2 #<8Z6F8En ]`Cx)IN{SOawxvF9=g /5#z}Og==׬{rqO 8Z#b%#+GrB/+r+4a%6$,Aq,o`_~%eaO,3=Fe==P4X픐;RتC#ӎkjo舢D Ui 1#LIP*L{3~훢/b4um2Ƒ 2R>{k:Md^̛7ɹ ohf3vrs H#P[T;Ã"SIpɧzpީP]@"Fy& >|MHp"u@'|@3#G-Dp ®Nbve]ӈLVfĬtFV* ="FW@+(Xp"[V*m9ȼz#"^R.ȣ -!(ҩ2VV)Ve  : )[i"" ASx ,|\NyH[^"rwѱdi$(@\-X]<{ _m6[БDu|S^PS ם> 9nk䰓.<`{̜id9E1ȅN[*טf^Xvڵ ֘Q 9*VkVěWD<o0oÖ!1!hL}Ir+\=w:*y7N#xs5zs"l [t l=jt5B@I ~7jr6ڟ l)4 `ŶT,-L2?C:DQJo pww‚>ߩw#pˑ#G#BGcMc.1u5ɱ1xd(ȼX"b,LO Ndu=ڟk]l]Z']k]c ·v5`>o8}ØQj}FWp( -G*!ֈõX{qw 9Cr:$&H f"*|ā[4 =^H*(%nyaS$QAo'NJ /؈ \xOsq!YHy6ytfNB9%I`SP' :ÉLPXt5cFY i:c_xUd @. 5Ԍ(>YTSzƐQ]i)2ִsbU 3͈OJ1(@r f4 =0IȘ4:4cXj0 !q{4#-!"m*a 6Ryȱ *fYVrZA˷FHEb6hPs*%#,Ne+&is)@Ě !Jڱ (|[Cە^Xb}K+Q^#[#X_B=̂?hiMTJ3; eIhf!2b^)MY; A P 5@c#Y[IQ.S,w7s.ish@{عZ1,@'G8Ngm =G5qOK^OPfk} `mcEb@JY 䰩Yh+lH5hXzQ|  #f"爜T7ɮbkTh%7{]MxȊ*QFQ1u0X-uYB(+tQνԢ ~{!khڰɉL*y#`RC5)LFrD"ĺ0G}+VND9F.3gJt=s zWM)L=ҸjРgօe5p1y ōV(*:$wD\1h*$ob] Kl =w@9|) `ЀJkS._AꞢPA_cz>u:M4eKnVpX #l=˳YAL6#k\X⎉RR`TsT"0 {MXx ~޺~8{O7KbބsΫXmv!$-CE1x3,C|:jPUϖְ+Betǂ´Q>FzjC듘iT`CS n><kx_% 9jgu"sI IUOkh60ɑIG&.(٠OlaU)V1dDl'b[ 8< yya.jn| +j>?OǶuetv x*8vv-V &Bi(p-v/{wX4)'5oZߨCo!y?ϰ,$rQ2gυp)?!>/5%@~V3P慨}ӛߤ1U}_5*{yLdJ,Is\zh? t###0PP .^l?/66V06sT,:ctz4E6+lG #k@&>TrĞ%5@ucXk!Y@+iYg某"b+mLDlBom\U3GP!:&4/}D1v\~cO u_!qÍ7x pD|ȏp%V20 e%u!;7Vݥؑlp0`Vȅ, >,O3}YųVc Ln4I^&e m@DMP:7N~40`VQyqĸx@C'U%ÿ*dj0~MFa'sdX_Nj61$)O`/li$b޻E^^>Gms?WxN/PDG!)B,Νw}{ڵkt}vt9ޱnu^;~ȜĂGϸO%\O3)dlw. ~$By}Z^( L;ʔry$7U #LXd{fb,>gyl9Лug\ߒlO!?-V f=H>S.JBܕl/=paE;.d#R)ebPOΎdW R*tfr@Uå\PWpO1]t^}L^b.]Ї;]^*,+劫sB4ߔ-Ъliiq^^LvP^Ǔf30̬ȍ|&ۗV3o[/2Pvh0ٗeP1L7_*te`(.gY,ezb>v{P90]^]2`@~E!?W(2̪R_1;\ŷJ;SVu2p1_uf_VGRwӗ̀9\_ |:0V67׳2W{]|( 8k'_Ώ:޾R]FS)2yl_q lɍ3`Hq$ώ.vVd3r ǗB G2yl>_/ˠV}˹Uba)5^Hne?&ƺR1_o]ۑh\m؋W3Jʝ<<~Esdo<)t'Gr_gKY&a<,ꮟsQ?eoZ`P,u3r)? |^ZQ*2`LOjuo){ϞB'M-ý;VM;Ug: =SOB)۟ˌYC=9~btcVVtNG+͠n#p;_=Igj'X ͖ɟS+2w-!8KU31KϱSRkt,2Ff8hg̍i wHC0cx,1n`OևZ;A >F}f; Gk8;kqZ kLRZЭ3z34H'!`MR 1K+H'Flf1Yv)}s?ke$,1 KIpBe 6x/Zcjvb0q,1u}l~07ŖU1l)mDb6Dxiqg `S2t},n'!'?cYa3.h]0ꔌV*(('X} w;SʆM{n.bS6^u=E.w!;cr)ǝ@zЮKh|Nĭ%lX/W S"2FvX]]HDqw,sW\B$U6xN)c{`zqB:fb$&,=BF;vY~ANx8pu`,9n=b/SˉdG0u9J(!䘴}]c8ipӆ'3?l:N%.   "3>s 7ϠE@Z#`^^8~`@ %5&%h{)Z!`X9k{]ɿ%%KOH(\kaY}+,.'Ln"MkgWΡ$= nmөde37ٰ )gčMgb։rCܳGcc~Cp %; Ee{ H6"ts:n#\vq}Z܎ a&)l9v4KYFLfiHxN8@OE LGcOW2X 阀hi, 6 Q;ruj .%F&.qH1JLyĠb/zٚ ^&(~HHrzE jZcYc@ gj+;V:8)Sf8\A3 j0R1Oقs`u˵±A_xID(jG I 5Eqmpo!3ML^nlj0p$fD}'xpa1]!Dz5]L”5*f8-»ˢbZQE]KI|0r>,aSڹZ' &,6LU')0!QWN,WҒKLPR+ bM'/?q?+O2瓷K~0~ѷ$S -\sdEfH7V 1&{L1aLfKrCcED+"ϣ #3@r,8~W.qW>ޝO](xA7 QX1oN=L7L{t'_W03P>0l!>D肽\,8S,^eP ~]Fj[OL$lc爇2va/q2ل>Ff83?5Aڳ(`Ukz+ A'8˞Ũ$K"G=1x :W7!~S:#ޒdƢB]TYA *v6 &4ÂB!^D܊x. H"3?3&l gD f d)M HS6 D]f>,/12fɱƅ,M a|+N8U1p~ّrΈaGUIY*ϝ7G [0?`rntBǩ.fOy(Fvku7gg XMˎCy9n2-0JAI%+v 0]CKk)O>Z ĪlEBIcaKYxHLs#[o ka [_8P@t=’R :2bЋ\BQ-²&xt}%^BYS+Tq(@'(*} 5;q5azΚ 0#b8)(}r1 E9i~{~t# )Y> +;n:-& @iؾ]4~DQ(4 ~wt|Z AI@҇UZN!)xOPYxSCX*B$J真DaYfb%}aA!m-^Dbk 5@&_1Cw-r.BS NJ!-ʭ6 ü/]@QI0 ߇f IuUp`&AښI|1_b@hE'sx).,F,j(@XxbOJDoAEv%~0԰Zu}As6 \* ~qF.}KKcUԭ's_hx@(~j8e$nҺ5S!< 4TY%Y%S3o)X04:!J?PyFO)_t'>'Qs#RL$SA~@C_(, x7*#LP( a?^c ;G6S! n7)=I-D{7vbTFSr*%Yrn)yH#cey;ժt쥪A9:A7vr(h1N!,%}:&kQ<4ߋI=Hfx? s?./*PJ6V$ Q#Ѓ#p`L53  ;&IEJU,O Ԣd Wigq1k*MXF /"2;W4tuDP#~u< 6,j cas`XDٸHêo aWǰy) G$_xJ>!.Olb%!s&XKڠ/%:`M 쐧*KK*@)fyIw̟K@!~&pK9)ADh#JOJ78\4rwlh*ib*͠Ci*G0oe,Z~LUl^FvW7B,hqT PG.0ɾ`%QQL6,%1=e Q@K搕m3KfAQ``>ufX^`-QhgҺ)QuЈnEXe}H($2?m;! cWb#H6loz|,KC>1d-YÛoO%QWNafn )(#R[;UI?qs2\t6"8SkVo = Fٙ?] {L!X BĀ~(!CN/@͆XrE4&vb[_nVƏTCdDJ }1&UB6&35UL,|m $XrcL(5x1ܹ~tHA8df \{$>\"a^pc$;Θ'vd( `&HzzJML0iўV[QPR:{Y\nٹZ!4NlrWXQ@Q+{G6?'7O4G9-Yӎnw=0yR+) z/jCP #$iڷQ2y q W3/8ZuOYbcP|TVfKn~5uI(t̅YswI /}B 3Aʣ;b0˯b&;/vAKIkc-7QT /t #OYi<^:@ t)v;E[(!VͤO kzP0`њ4P,3fm`l'ȎG&٤trH e?'Bq-Jed}2Qm+Tp%+QRaL6 )=i3CR WvlPHop 5jnȋO#dvYPsX|.FK:־(3=Ejgs$ L\/`ri$~KOgsRK (&x:!u&E֒gSx׈ޗ vݸ3!n u"㑡hmEXٹ RqϯT\,¶䗉h|}[Z{Q`?k6b`-Og:qv / [.ӗR {@:;Zw@u)e}Qyp-@Ln;u?B;50zf/ JgG%ҁժ[:Z#=H'ׇb>U]X w@1fȅ#,nQ2woX%hXcOF_rcp{ۘԒIY潍LLc֩d{??pv GkK Mml<:'4ܿ(I\)URAΰ^4nW!6I+"1 voԴOU)oFsf4m=/nuZi݃mK:v)y@LdxL2F癆B)iGw|"j%jHk!J{`To9 i sw6lK3C|@L\Efhr4vn5R/X[cR] 5( }+C&_SP>3irPI-Xp,& &tڛE^t\gݢ ZP|JP <1|bO!I}wxtERs3iR6=iaa:gzg]~x vlޅi ,qc,&.F.Cu"c5Z,`+zZ5ZUڞWox]#Ph17liD|P?0N[-w8}٩|XD ʨmOJP탧a%r DDXHUT_ _@nj*e.|oMnyEG1 f:ԕB)qK$D3s/"-̾-D QGocg|rk$dRX@TLLw+Y\e]D#Ҁŭg㱖 Bdr. -lX{[(ɸ3̮7@GB8y4w,#*g!"MGgTŴ!5m";K?jm$QP]]- |1_ӨyB^Io`#q=, M:ѐ I`UY;_UOdqL&TT>08if5ժVE!A"F` Za`*n3D4!˔ɣN'O9-rk)k!t<8 VO_q"D#VcרWL}糷 dx:a-O}*OTRzd(X2H5vBZ0-E1 UmJȀ|ؖLmU[c0V5O%Ic@($MQ!r(!!_;Fe;AMڠE~-ksdz> w~&1H&%Vb_{FdT1(}ӒW.]X:E)6NC?mzP}v+O$irzmH;yk-Ħu3{JՖWF[d)ّN#ehzL{DLhܑO`T@Տ,xa$uEn 9 s9`քHU ӦDSYb4-U,t&Kg^s^9eH=zOa BVTW|b*dau#ҨR`o & ;fśiD^>xB|214e l!¨L5…|ZlVeS p8WuK(4s ?/jM c8q NjOL p4v!G'@쾛!́ab^LY*Jm:{'bV.` l݃5Oho6m+\ٿ-Z7l`tա >އ ~)I;!yw6QjdCh֠]7??>N=P0NEUQOeuى= Bץ&Q,w+ca(P aPs BαŤ/%gE-%&=f\xGZ yyQh{5DC;=E;cTV]2~+Bg$7#=W5}}UNjx|xwbPēNr&#g/RDUHt|R%Ce[YUTĥL*oT]NcnS$#z8O:bYryJ]e{pe5(.BEKW ABTERgVDw4 *{;e 6BނI w$Xx=W3.d,?&4QB88[l|.Om[2nHVN"^;mX ɎgҨeAg+q'fإF=(!թQSLdcz6jpmhڹw1֕>) PrQf zC9x}'csFhaf-c}:&Y E)k"-V,f};JHD3t Ǩ}8*2e($ ŐL@1SO<&68۟gSB6cm+rXOw ?1bZ$ SԮæUp})PAC!sf2mSb@%dzI. hۊ,I'l Ag^IeQ1sza# :+3} ?6OĎ~W/(Pԋu`[e՚eZm7V9r츦O@ցF>sC[a0lR f᮸_S}J3PNS[e.*x*{Uڡ5Pϙ*~I:όǻLM~:ӕpyՃHC)3 &j'<"-X^IrP+': D cylF0^f"D^2,d}cr!1F:HKZ9P3քFϠS1w ܬ6t7- qR)o |$N SeK qP q@<ڈ")):@OX*0 ;5zL f^'P<+-*SEe H^TmIG?(v(bt, z8;(I bMHࣕ{b Orڕ \n%dL,pipԘU];G\Ro E{_d(zDWlA#6vPXW m"Arӈώ Kͬ1yNQ[LGU8YDƶE\9,L۪9mL:vWE.F#G%bgkc;ۡ<CWzITZ~ 69cԢKhd,a@$H[hM 0{qδ 1]KLmɿN]1#:ߙmB2蔈8C:VHTdD(fP~&o:Z8FGHW?^DiȣpghUOkE9_BZBKmFa@]>ayI vn2~ 8=쟳K2ig QV{]ڰNm .cф|5#SERg܊&MwruPM1U9^s!D^a?qe2p }J,$=gqsESM>C{$5խSg˩OSP퍩cYth*v >T΂$V߾dޙuCQP2 P 'C5QM: 5U`{ԈTL>jhKUbi EQ{!dUk$ؙy9n4|{;=U \l{szZ:q)= '3O* dai?;B١1 BYN^lSwTp):<Fž+E&n;0I'zǗ-$or(4q͊b2%<:Ú]rVIW(G*J:!QVu9)m[A©{T6aĵ&SW'OBTLøib(F.D]4`sKVwc & :7 li z#DNTu+e1\$9y_ EZwcjB?#CXE!yNsq&pUc, <4(@;pNʫ &Μp̬NZ#2 '>y2힢ه/jS+ӹyS~݆ TsE83e)u$Ɓm= zm' Ii:dCKz(mvLx6Eğ][I%Sڴ@(l$lI6Q,N4 ]J9 p"95k1uSR;iJXK g`~YRԂ5@-nVQ t O Q4jڑ H¼>)<  -biIc&҈nK #IF7vx DmCUP=eZJ&@Gޒv)3np #'炤714ldlM : S*9Tqz)=ؚ%^{!whWh; ϭ(u FO&tDi"J9!r^2jVbcZE3f ΃`N4-M=jZ&=l z9k'=Wti܍!wHQ388K!`d1'h(Gb><O|mNNģK]#րԳ/AjRbۓI0 XbmHStz-ֆkΞubf:QK#aԕ U4?5co:-Ŏށmql+^ /oz۸8rC X>tqr,xO <;5OK t* CK?.Bkާ\"8)h)!ñjI|>*Yk*V9Pmފx'9ĩ&~v)A!NP y-h(nzw=⟤FZgW3 sIMf!eJhR+(Zf`jeY{UgwZ{>|/%E !gB|^$g-s=`2fP_YFU7-`'F5<!>՞M _AqT\NS#$ABʳL d9mnTx?p 8HX'QCބӠt-!GBY8N h-eN'"9P2.BRzL@=zdAErUz$\k1cm vx%I֨ި#[JzVb fľI=g%Էo+1y*gzV?r:ѭ8F;^xOZGQ"a3nE$o lJN@7ƨF" 1 Q] 8?od%Plbs$QrP]R[I BȯDTMX{c"AQ`Y=̘%̛Czֿ&t"bғ[y28Cen&-ɜR ?TuV*Ede_ wlDžgk߲ ߧ傰Ξ=p+STY,cmcsFf~t?⇓ :jT]rS b Wj@EƫJD es0ߙY#`K2fUCVq35 6 D UvwIU5ELjPiS^STʴc>b* A}mcXn6XBBe܍eDeׇMyG;02H5utWJT7s8z4tpO}'@}&poaoĭ;uz<4M Nnс7H<ZXF#t7lY'?6]90.6+1{aY:FJ'ssq[36YڣE`~`@*`ȮdM'#`:j3D / 4_'h!D+-RM\}ۈ%Z=OxxV]A( RTalԩ'+a3PA7`6~2]>zUQ b%DS_ c[Ax+e {`O2 WՓ q?tqg=FOTn;Rƈ2KY./<ik? :0V5OgEA[)^E`P}$@݆F?\yH-)}xAk~+ܫ*4KaMӍL J*Q䐁*bdTVkzF 5^r[/xT!'ʘ#7gZD <wmŊp9+:Ϯ82#zęp%f~qj!xAߌυ4H &M53yE7<%=rm&Il2Dxa/.5lql 6}S'%]gIq_`* ^0 emr9Aƶv )r:\θz B/'|:M*bt$_b@k ][$MWO-k07C[h+ZU3dG$~Lp).Ü\cEM6"ec,&#A=b'rl{ LDG^lXn#ЙuksQTԱl{ndخ??Y:%YyȶU_U IE p1#ߐyZ1«<%%V2g/m@|J*|A뭝ޭ+ ]z `LiRdX< { c\A`: I$VK$cI/o)qosNljbkPzUYm Ԫ2\N$`XD}7ĖaUBjU[CmWm/i9*8H^eHۆBX_5DRe`bDL峪ϸHC*-:s;i\R3}5!\"a\:V׀) :&);J6bNU I,`bJv&5GMdg/"ۖ2Xnb)<4u:uR$ːr)wt,^Lד1IjKWV>2,܍1 CӮT )^c͓-bL @F'sDB1d)q+(7O&5X9[6(7"od萘~KCώ:c Z:aSaJUFƖWY% RΩoֻ_.o:YAd.VKHu'ZQ:+:7mN5}BS{spğdS>smwv9"_Jxtu;ha۶~,N¦ֶ) S3|ͳ"nWvC4#YrB9@ ]W"1ֈzs?:5c礠 f@ !=:*zsL)KamƖ|Tm#IVY,,WnFs8ܻbdwHZrǖ| cmۯ/'nl[]/veV7v5a˯6,kQfˢKuH];:|8R+wإW;A^+;;@^ǎ>Q 2e @W/j_/D3p@EE%w_㇂.S2 q%7l,RRΊ,\o ck$]y&֦q_ EeLuC+s^_Y<{찲dȸ,EQ!TgcܤE--!(I%n}!&HW/+tS0*]L3i7ݻ]ZSɣ=ctv`1+):}Iw ˁ}8g%KcP0CUy/k#/ T tHpI L]ĤbAixxJ bB7&ӝ}-ذ`-K[z,Yly Er"-$ ыd>Dm QBNGZb^LM0LG[!`GV d{en@\+p3?1*(U鹖r>DٷJ#UM>[:mzD QS#=3oӳvvgHrzIGzT  6c_UBIR@@Di| 84eaN?{nZu1Լ8i,p}m}w!,` }mj(H`jQ`@1T*Ff<c/Xrrq3V &!t&RJMݍо+u}£u ܯInYln>נ86>%y2cq${}1]/m93sjˍ I],{j6=W斲S!`QyBCt,f\ ov%s^x6ټGT"]Sa֧vupTu~%ɶ|I5Fqk⾕xn~ {WR;%S$ a*;XZ!Ћ,Z!kt2B2jUe/۞&cN%"AgzOVt|Y-'ҩ})M)/;1SPݔ Dԗ D ŏwAٻE}1[N;۝qrh`AI C*C'DO+_[xˋs'YG<G%=zf8ة$)Tq; #u:)jRUЮW0WY fI^EБHuP+̋\h6tx'+r=X}(km@|<&%h%7Qe8vr+ZQ]G(QT3 dt%ߤ"UT-4ƎbyuZkH7(9Yi@@?T3 ;IM,L^r(S2]|%Zh/Kf:$X: \OL:*8N#(ɔ[f0g-.>Xp \Zi$ydP i]ތ#VZi|Uv"W~v"n[<_7I[Ddľ"g]6}DlnMQ;ysUsby2G=/$}wA鸁!#ؾ-:#qD!$Xx(t$BԞ09*av͇C+$N lԼ$)d ./TLD ²v-dTdFv}vr)ݞ|"AfcLɦH0?M:}㉠; ūڑ"7 {H z["gdrst(&7( bDnB=? PF{uydV_M 8n+>MTg 4bgk7o`+"^=^eV"M>/U G:gG᫈-D낗 UPh,v{R`t;)ԞoPbճwRb^v*j 9m,cб~>@hJ6E.>l/7pR$8j&#T^W"b&7GLE0 Fdj+E 6⼵CVS,PB0u|2%&5͏6~q^AN%>EgÃ1/5<0GV{@ K ]dWtre ]J/iC-M]`*?"m-:y\Mq:+&XU!,&LC'KRa/,kaU.hgʜ& ݠDGe>N}n/7-Z(m _Z.U$) BOsU]Ø~es:fs$)[cE(Ԟ& F1UBX]ktۇpqw0+HO=ˇb+NJb'QBY SǾR;:J@=ȵ'%mJ <hoXӳ yLxk_0Ytds^ =]pX+rހ$8݌хѱ38uN8E.w3,2P%n__FVJ+K.=0h'UTCc9@{/9jD6Ic;#@ba'O CO5 3GWåNļъ45?֗͒QeX?Uv` 2'+ +eiaˠݾ*$Sȿ?;Y\}/ZIzI*u._Ҍg 29"E$;:=B'`']HZ)Pbe'⯝S<6ؿQpщz,f=q[~! LЫp-l}`ѥ l $@`q3 BEeM#+:DkhG8Zk4 %Xs)4OE/$M->?qAWSpDcedHX0܀H{D@ e:CXcVq`j# x [/} EFfQxc5̨DΓ)6^bXf\߮^U:Odt^: qD\qGO̩{`8ӎ[T*gg^uOe.Lo׋8N+l!flxT-&ת*sOrn4+hp!joUW2IҍE ">b". x9y̩m%O ݡ૷$L7 ;/Ub?"21,tZ!9R3'F+^!y?E<{LP7ъH ckm d@ΐ(q)_ }V,86k"9m2w'IQڠEڿTNk&BZDwVc*;E!ھ|;Y+4xԷ!)]E8G4#jUE$5{w%, beVuﶗ+KIKM|]NI$JfQ1Cv._ wˉ011o~Kg/\W۱iz$ fE,VR}8؎ B*HuFU[!v0a2"moX`;!\}ĝ#UQ68e}y3BJfL݃ZD2;%_9 Zlɰ WT\[).+@'f-Ȍ-a]9 u;vR!9 3 \Kvyn!5,ЁGEG=qxzSi9' =o),lh N<AU*}gKIW::+~ Fs,6 HPkgT<:#Ąe%A7 7A nq9w6 QFnD9Ֆ9Ϥ"bɳqxǣ6E P(ÎO; ˤ q`֧ } ,RtoW[B,MO.=y> Ac7x~RQ<뛶qEi1mXz=&/rrc.%敐/X.E\"5FW–.ѾmJTf[3q:L H+@vQV`c(otZ߶e ȿ/8i6<λfA`;aǗ 8q0fgrPg Hewq@XPqӿȴ:|=@%6Eai; 1|X$Z;J1t|,0,U~Ҭx9~`jE}MdpԀ%]lcFODW[a[}L'|EߟknT;-{{a*b>~k<|i]~漏_ET·$y+ָ .~0Õ _2`jXm5>3]Չf)X!| Ƌm?Y?!ҽhzr:|rb֝c?P 2F/%nz9ڀn_Xy]>r铠O 녠a&*Vx[=I*i馌#\qw!z?\pS٤ȼ'ߊ}ܱ誛!+{Ɖ3\mL\xzjz$Bds'LYr3*JMlؽ<,/?[rՕկHtҮ8ŨR#H(A4g" ]d[RX_u^vH=K:r7 W*V'PNj?g?!rJ1N:?`XLGʞ'Of[rš@̖~{E#Gɲvy>,CqʒN"1EY^)MC }qe;'ۓȝ]&|ڃN:\cѱ t|oҿc 5) Gzgz~éu:57ט EXNRY-)U)ٖZ8'X͉|:I\o6H]F1,餆KsKa& hZ.UMH Iv[o$Rԩ T?}ҫi0)Ga +0Q]@LȤfdU)C*1?Ѫ9"h5ڨ1ȱ6@C4Nvora3NXuL˾f-I8ήb|E#ijh&XY)W錈pZ<^cOL\*L{v f#LmWs!ψ:v:'22C'|{s#]:N@. ۓ MG q 6 y J(+ lc?+RL+샰A )ӵh\Ӣf]g@UxW~X@ΌJqY  6cP\U+PCy1UNu> +2㳉RLA{ưx w͞T֭@3b]I{׆G`'T>49%~بAD%7 Hdj&ةYTZwjb~DdlƪS.=+)7W7ξF>HPxVh KoG:9icQP.lMb @18E"/"31:U${qGXf|(LMbMv t#'ui0og;~,xũ? s]~~cci|6k`2fWа" xb>"*o528mFH\\yXnplm&B^NQ!Em}ȸ`\4,Wv(;E9շU\Bw-(̶B3G}LJx0~vsל5; 7|mg,OYfco%b M@3HA0s?gEY+yټw(G:5%PJ,CFGدD g%p&aZ&tNe%u !˯7AʬR0f>R{M3hɸ^zUv-Xq쐝Yrө V_u>sT h$U{ovXLk G} SF|[*7Hľ}s묄KK1'SD~Wc/2ݺw:XO{yQY6VKR 8xEj2 XYu Ǥ0y2G7XRtsV\Wv3F ԰ GÙJo"iX!N%첿9]or$ >A%=e-[f,3Bp08ɁTJS"3@Ys\ R= @T 1|۫Lg_!\}&d0ɝ#+iwx4gTiF΁ ֝Naqfed5K6hNp}jt?{ȶKV!P'7%bhRw'CB[TdJGz,Jn+dŢrSҿkO8W=N6:T!ԚDuR9h9-@UŠK454~'W*C߭V:I1z/3˭,eSh|,3gT dC2*thR.bF4M`~FB,ɷ5JQS߲Q=;֚ܭrIQhad3@k+{4ةZNi}FVG.A'/ʛ `_[ܦ1y$ l @CˑeViT08%:tEu $b %v03;J4l5Ev\ SXv ?L.|+aGPg;JgRik!xBAʺTf pl U5DlXNd"ۃ=? ]tTݤᷱeyEIQ;cNZhtXmd r܀uayrb,i_ǃM-fH"s/>FzA5@vږ|V0A÷;5P>T!rv$HԽZ–Yj~^lCۢ%f aVѫY# R7MuPgųB@ 4-ebR'%PAB0H&b\鎽pO"Σ@q\H9C[H8EN%I&GEPE UN8=`yK)!zB[]GH i:oNujbۚ:57>YkؼvU]m.ad=镁zۿoXdIJHH`ڪѦr#1L_T8⪨LF!ט?E\!gDD-DV!aQa(&.y  2Ϟ h#z-:{{ ,oݴ"^WW"1~2ިf$g 4N}KDWƚlozg^Y?.k[aVCjƒU c'RF|^84{2NQ Bg=pp*^oFu[Z*鐌|$ƭ-[bUw1B2{J\W[>I)gB8##zˈbk d3iIᰵJ )eȀMZg2[uCbS (t`LhC/ՋE>"~FvRQCgî U*;'7{w'ch2?z,䞿?8̕( ډkJ{g F963;7Gbom{B XY4ZgrqX2k5Sp[k$c33'CpfW`Ds~lA.y%hڤ֌۸q;`DZ=w3!y 76F[%UJ@o _ʣ '# B!ݛ1SV(0Γ᝞3$9 ^QG=&@$V ꍌ8?$+u@!Sc[G.6LP f)|٭Z v<#oS}hH)|x-g |Tk0-4BkQᮮ\q FP^p@ZM}> YdJ$XlK?v`#݁5QZldٱ޼ {4voRYMP(~*TxV Y` Kqii(&KQPY]jS':`b`cUCZ/G+scPg 8^<(㢩OApo\XWM&Wދ8ĥVw5]،-J9r|V>0I}eNs Iß 1c'aL!V|$%=tRR`M gVH^zK%_fX]@IN![Glٹ־MqEŘkȂ8XsytꗕPʕ;Ǐ IDTc.AVHN0FT s:Kbh:>:I?uUD1アa= Jl(ힴT`no>: cݎB%wUsDsUQ'h&h2jt||`cUH!7tSiٓ{%3r2/u']~G2 &u&X- a@&2`S.1TdbFks %]ryRg%lY,yag1X>MqPY ־t[t*B51EvP0,` :ΨDJ< IGd;*FS]_W{uvA' !̳ T t%2E; ζbCWfP] EkS7h vWutܞ1l{cY2]XAhb=0֎2q]5@y G{Rj |$,( ioNz: $5%v @Bn#hgH%\AY]YMH- HJ^Zf)p(!EKCl̠W Vhz1ao*t&*+>!b)^{#mUX6Sq?UL2!Tߛ\Me%*IS@KQ٨m k/v0/p-^Ni³Fe%dC/[U_'X=b]W*ԵEx {pF0RNʯA[(Q (abhߢ2uQ5w.(eV6g aq+LcBʶ=6 Dg>x;$:$!5~w-tȆ09J2] ClHA3'R"p. f箌c{GEBǟ<*7+C%s}t)Yo 3Mk/^Xq߷CJV|+%ȑׁL%N6P;.uǽXqRw[ :q 1ȑ͂({hn?T `i=hz?:>کU[x>AG.DiZ]&yrF6p bn*I?T<1Ud,C9:dvi>(*yȻ{ַxlcMC\v  yˊ up*aq)YLZwo]z znIGD6M"أ,&y,N%Oת  N5;)H7}`Ѵɟ;FmJPnp8I[g[SoZZ7kf?Zx | E[ű, |hs0j @E3$i(ٞNȑ ~?;65{j[F@̌KV73 m93j]e=Ux3 Qٕ%Աufè㗧qI+^;"QMFHbӢP?JQV%>k-t~R%zvMubJw"Zz&9#EY͏Jу 2k3)M%\ Yם({{ȬvlZx8GE 3K?*eaFb 9Y 5SӞYy\+s!@$-]`9(-Sq8td@+.e##K](ƱD= z{ٓS$LQ!ձShM:dMб{\k5 t,aptH+vZJJa"WL)e70AS@\^̪%q|R+5k!'O`]Yۛs3PbwAfOD;n [*Dd.2Ǵ#yaf?[ݽ7{0~[fr'@Q>T\éi^ V>W Tu`د1ijxM6GvX.뵗Tz z.{FP1 0Lo ߙ$ޣo d9鰼/9-ҷ<CZ2 "Of{e=}|4ޠ{*rSWӓё&}(PrePS"x~BF1<PQuȀ^]˱Э176I:htG'aU:(I 3\G"fq`3}$O\>CT@}ѬOO.X0X4TY&JQ7­4 ߈iGZ$O  Mm=J2NwЩ)Kv`*ĵss*0D%, lyҒAR}U[J.80_t(KA-V~$5K)Ϣ%PQ1SӦCMH#EnyV'l"<ĭxe0*9=+ xHu|RXYuYC^S[,vqEzKt6#j G%$ ]fQ#|f5Z ]?Ͷ`R{`hގV*sr  ywG"uynJ**Nje_[2|k91Up6gS!}T!0H~;+=_ڞ #g^ _6MWh7"|O|uӉ.XXĽiXk "JݦU;3ojg*HDJ!Z ۭzmGF v#+qfVƒ3u]? O" /WN\A,H+aSꊊ:Cƕa'L0;uǪ^ c79y> _UgV!R18YXZPݚl@^mIkNDI7cv/: :Ol#gYǻO+xqHҊsL!NB}cѐ3F)faw74 ޱ^yJP \$^CѲ9{~& (] Hs=h T,Z pРԇs2AC9'W}YtI0$о~:Xp}v9vGԼ[ߒ$zd0۟y"Z댚ُY q- elx~ĆQujٜvYcNja%!3z{5"~0<KYh$ML[Tm`:ӨFF"6ZD́7bH%r|A*c/|NHJ/HoBH̝ma!O',Z k1N pq3???G%+`feox܃U"Mc-7Z)lNKWLZ˨T]$(IQK9%qɞ'WcxNo_R%k2'w2(' /}EvU4"&fYvhݪ(J$6 +%<*?=I_p*fOX[7}VvDY9Bj5x J"+g<@qp |Sw dX OGTwDbN~_Wl!u6ZFXw٫LW-5 o4.!HC@3( ՃP4@?Ө"u)kAu?˟gdQ/)&aʯ%Ù?Fy*"8HMJ f.+@ZyRF2' ?L'V샅 SZ*{ wP8Wdݢᒐeh w\tSgK&΀ۉ{ޣ[[BL>K [Ġ]vAIQ&aYZxMPګ :1HLCՋ m̕TCT?5YaK0 ;5k6i|!mΒR"Iɰe$% p{t\Z qq~X[,Mv8Z"(ϙlboW&Ͳ(~hINψ!BM[3̀JpIyVw; pR $ߖV"-I߽*b(Urb碚CΟ AR/Ln7 fƂu4T'pOK:N-Mm ^"a KbFWğڎ3Nf5U`s*c/y=z6Khy Nc'Wt-/0q~w|~uWTAXZ sM.e4_5"wlUgz.3Oo{IlJ#UX!LfTc]u7HϽzyuabtmZ!ZN~dnlAERtD &YEy GHYQRjԛ=0uuQ~e /V6M/>.`A)uծ 8Էp1x>{U %GË=AՓbOQ&Racgo#u7b{b[,'H~޷ȧHWJ.+Εd I8tX%y0T2.^Ptr7sfb`(n@:flxBE7nc.x.ؿ`<$jVT#W^pz*i(^"Qb<<)PRsס@=Vю·QC6#Jyu)ڦQBhߠETi M%h]DM:+F (a!vk:\xQѿ`Y{$C$YB?BW\ p0ۮ"ˑ"ir B/yVtd@6'Ă1<_;7؀"HŃ h#n`Uv_@.g$M 885Q+?\ -d*'?,q#y]p,T$#SFcn("ku 6H!<\b8fq*'d>N`|wFh;&w΄`#` Z|j SK7&;[AV+(L+!'MoEdu{vVMImKy`*s~37e+e;%u&q&vV/[ճJYyL'ikޯ nX|&P*+$'G1*QKT%lKsSoIr ^BWUQ!c- TO>C=03Qa (L-9@Ry修.5b"QE%ӌ=%c!R}ɏUTfbMm֋uK/8`+ʸh^dV'2zbň𫠆ox?1ʳ ! =)Kq,A`6<S7&NI Ԅ.kO(uB{D#5BFm{D]xbdgGw&U-8d{™$[|!V%-;>ʘ)A@FybBJڔ~kuuGw痱L՘+||=- bj Y?LnIM3ad,z}uOt2ϠzSY #璥a ,a yJz$/8kdW0\)NWv( ˯ ';& @?ϖ]~FQ(42}ȹ[+gU=h9m|5 HP9=[\K)D6ED} Ic<֘wF{i_b&V/"Zs[Lwb[Hr!AfȄ+v]9g;qHE30>:x4n*PJ6V$ Q[gg9z]yVWxf M@uw:-G70 $y*cHl_l Y$(KOm8꾺§l|!?WǤj H[>A$]@|2,"ȇ;k2 CN߶{Q[`ݢv2=L7FWޘ?{,cgƞqwJpj tzP_Ulur}~9z DOUa=*d}R5CիXoXKCJae?9oU`.#2o #!9ͦ[̓r85?=Sm.&?3# -5m9CexO*T #uOwc|_ӨyB^Io`'q=TeiZZ vI ~av`;Hwmz"ӕi3qQ** 4ʚ^>T*-^+78.b)VϤZg&o2yTuiz\#ҿkl){! , @\t2m-ӕ̚βJ1[>pGVbHsUS?yA}b|acVOc*kkc U(4nBLxșJWbr!m rr_U}B+U˕Zy!5@'P\%0s zJq3Yf(u$>\ 2\>UVߒ_n cl~wkD]ĚJ"尯n[r~B460tjN^3b^yjd$A 71bm@ >[:-&m V, *U[B\NcX)ّN3_eZBȶJGTnwARw?cZ?`L> $1eʜP=! LiahKd8 ^ݿ<Ӎu9eH=i\FOa}t! 3⍰nX pH4vd#bFR~\a򭉾e6I~.`wC̅x"̈́m*W{}&o3<[/pb1/4 ̚^J0OWNRH,Q0# 4+"v:Ma+' j%d-K 5 `(E?&k9ly!Do&5yˇ2^ʩ^8;GY^!⃷EkeCMEbpMQr2I:+׊XuĿɗ*r-AfB-aۭ9 '&=p ܉T71;g:i8UҖlaqvnJjJʋ럐Qfn2/O/1x_֎8arD|+cydZ} NN-J?U K.3.ruF% q|iL?/jM ;9I7FړLj&r"/NptIJ=@lY:J]:gLLEy"fbN ܗ^5Z EAWKC˞e 2 0!NiT2|;VUmOCw52,*տ Pܟe>^ t""a0xϪ KIR=`]$ ߭脡o[Ϲsg@w{Lv?P,<#mj.478d>d oN sY E!=dž˟ aV~R:])߿rLj" ooczy^+}Xኲ:}^3n `@¶ F/0MfY mf²3 \,9IٖS>~J?vj]aag%Zt2̜ CKzTGAT7`ީR(d2 E:N8lauso8iHI8tR[?|y0g|AjM 2ULD e,yX],Y+XQ$uM҉9b]/ Q-*i Ig lK3oiw|+ QB 8HTNqf%W^r!G-M>3v}1V^;C yVmә?KFm7ܼ٨OdTْc)SVSSթvURKA`Y HM9 @n7 @abtV9𣑄OTp$?; qu7z+LsM8[&*So:d({v 91R|]a"GjHh(<۷g?n99]f=uY32%1+gL9 Xݲ Oa?1(~#yc/Ue(xŀJ?b$ <>DPMZ%Tv׮ My59fElTPh N,:a̻KU6h.[YY?vB+> x-[ūu%}R"{tela]᮸˿6~3Tԭ2A?zUl:`Z*SEǧLE~-& 5\%Wq|i F@c$?υx W@+GT{]V!o %汙dE_8&Y>es!1ljO`6uIi ,!8Wn:4~%|ujvj"Q =|V 5F^_;k$&'q.d#(R~KqnQYLtFӼt|0&uojP#iS| S-nXg6R$#_k, ?-+L8T'u +LWA3pAZBD߸ma9 m#W:L+ꂜhC5[hBxx(׹fx] v ||j.zk؁D|. KO„R͊En54M^TkFXGG%oN/pw&r6z脣҃\N`?TELUu{5z 5;C2'!$'i7l c/`/J׳W\̀fol8]l{^.x *F\9 .V0>޾_$6y"T .NGްAJ.l7bQ6tE*&`, ص!$S.'v5ObdHl79!s.@ɶD N=QMyh.P-j/Tܨ?jxjg1 Z&a~;5&ro$#|Fuۚ욞t0$MQ+y-.*NhfO0r]kOꤚ1.s30m@2]hYOjUɵ :_0PP &C kWE O՗l yX-:@ѭ 8M֭)7.C;0rAR٘.gw٫+s3b;E3r!ȷ;O:>_ $|W|`RTZ ^ LnW4:K XcPEP:[{ސi9KIvb= ӯdGLsXg|zo܀ &\)JD`Ms4(rL) b2Kebgv'CI'+)kVXkg!rGYCY>Mk$긢пMb įfd;;>A]/e~6T ؖBxƢjVm/>wϋb+ `C5;&\Iu"#Xx쑗֋'*|Y%vm'_ (>*Q⎃YM*xv4 $l1EcdTmE@'*z]2&U" O߄s<$I k*@H""}kObR&KV#aos֒#F'F1%*ȂuqZp\D *f"#K%RN}/_` Is I4)坲)J[% [sjzyۣˏEO_M/8?huX[KŜnV/e&$l $(EQJS'zz]R2(pҚb;XLDE]~"TW]ͦ_~X.eI-ie˷мhu0@ÐyU`l@½X?|Z!9].y3*Dيq4U-Y i CV ,t^[ᅀ/õnt8UG09(SKD"j-_a«vO/bP5Zww3Vו- S*9 #^13$^[T 4MRiϯŃ( g!\rw+DI?ᢱfs;mjZQf\n3*;8ΜJK`O4-zZ($5؆ nS @O\8 &6ܱ;B:h%l7F2 l^4#]GP^'t]eDx!5d/xNf yB!TDW8flc-%:rܥ)|rK?%~~Nf^ +ïe 5jJ~ވc7BXh>H_ަ J~N|&сos< Dyׂ.eV&*;@wczRqdadـmj;ϷoUe JS5{M(!y䏗ﳄ7/uFq2PfY6JrN}H1VD/DhVc"b ,4jyɫ:$ӑML.T QSa /P%k:оT]#g^FE[w Xy թ.K3QiCv&=ZfLtC~®z5`4e(2u7KC':(&7P9sPD/BwhTHpm!,?nB{ޗ\"8R[o%kOE"ڮ@b0'^CMwCjgwYvr:#B)Xqɫ18P0F#⟤άFZк+㷆ĵ.'eHԊ:ǟe?^L` \[Pe qvUfo.gkJPB΄XIekYω+TRylU<+!`l^¾ YU6CH*0`V篛a+ %wbD$:tObEy"s {7j}P`Q (7FN\`Xnbѽ^ 4gRβulԿxeKK*gFku(@0 GC&4/k\s̋ȊjK?TBo3ޞ(Ox-*.Zc΄-@T9-K Fo!љ[2\H\&h,)mg ʒ&-krݾ !_aQx3IS#,dBy U8XU)4>DqyvLK ƑɁࣳ* LC}'V&iHЮ!jJ/"Y9u x Qv&DQEczX$ ~"nByU‚/s,M<#hdDTn׻>x Ԅ~jwbd F 9Gfɼ-GtJJ=9% a$ $P^&i?# ) dHoB=?+F[|ҡX>/D)mV;QehΐQn8}8u&?P0T|~J!L ͉aAg!qrn-qaA^v0]z\ Ǫ{ٍ% rQwo}pV<Ҁ6"n$箕O4\Xѥ;Lu (4w-ɇ!k'`j0_ /`* !_hN% O:%/NJIte7*a%L5Ό\{J2E6%\;385nI,TOv }kvz+oR)"n7c+=t?n<[w%cn Yl+ޙen6R*c?;GNod0vDIJ\RQvN).^)q*!@;؞JaefyXAf0[@5Q0lT%b{dLo"pl)bQdB%:=EL;7Bj("(iOI:GOCYE*iTo.*A.kCPJkl;ROqn#–b|k<{u8B0HqSUhP[OgG. |2\fu`7`6_~2S>z$0*^b{m(q$z 0 S}ƹГcp)pU; kKwꙏy qkHߑ"6F,Y )LOHApF@c>_Ć&VAj1مSNߑ[±7*̔/5MJ|ZcW"䷔u#b_XP2Ṃc@p8-TGiGg_3$ϣKU)pTCHSIf>0)6ڵ{2ՖQPw1 jWbBŞ"cL!`i N!3f7 Uiq[Hu70L渍<2? B~klj[}1<4*qFUt`]/;.γ#8CYua+miK)N7]O43b}yz˿&Zbbu{HVMéER@ORẅ́ !1Š; QT5On-RؙV)))T=oN8tw])t ?jq<1"}.cwIx5k]\Pq2̨2u„dWo̭ WutN~3ũ{9*bmjmxujS^w=K2*J'$s' m]SQp³U͓~YB#`fʱw,2Wh3f=^PıU5N^I>NvuTܕHj%?|~ C[$M}ץSs;Y}N3̍.t(JuLHPЏ_C 2UKcr`5 A_F y-$u$h܃MdFٜ<729zÃ:˹\vBgAM׉Rܮe;r#v_sSl{Ud^@1!jo-CޓˆZ%%V6gfƷضrRzkct mgA(cY #Ͼ!HWP*X*+AlmlEE=6wD~\ >j\]5ڨ*ibR^R%N(_CL@ǀ'Idr^"yNոBUm:V{M3]9?tV8#~TBqH|8AX_5DR3d`BL嫪ϸHG*-:k?iR35!ܤ"a:V׀)M:&w՝% 1$ހ V$xNg < kz;OEEk&DȚKȶL,X ,O3oR'S5:ɣ2jLYe6V6 SvR}"?8te3sE01^shڭ a{nyQߌ HdHh @;֋,%8%>~nXƥKv:ʤGPfGA:cz:1Aۯ EMt5<"!A K8gc6(™aZL uIwfп^idGo/3'N*L @*t]J]ƞtKbDTwb*A[8^eڸޱcq%H H?ɦ\skwvkFv*2'[-v4 XʵMmm )K Ӥ3|˳{~ 3iT,j9g!W  kl ͵H"y&y}iu=\FAuyTATZ =Xg?o Щr+~j>t0MT՞ؓj ~8dE09>s7ۨu:,F*I·KGCΑoa=TT|Y~m5ko6aY XNhua[b띺u~e,T i`VTu]cm/FPU@f.j'[~&cgȋT;jAlhjwt#[hhc𒢕E%}0jgEsAFf ۨ33ۤ m`E67ױ_$]y&֦76%tЃݙ~^6U Z<[WvXYSF2e\ڣ*?ICn"FҖiNHU$14xd+NV)zlMU!&e7=]ZSţ=f0k*˽P*:}Iά'^œ49 T("b $.0#"D$pP(Q"ٺ9rjT& tGR"vӖ(9ڎoJ5jM1ƉQ8G0l يNYaN''SDUf=!!ܕ\#7Wډ~" q ;JRsW Ui^d$xY!~b GʇKjNNjI)bxWTT(T ٭ j`/u0NsJWOƶapl`ǯWFDώ|JnbR1t< o ,t=XCҹ^b w,^h.{J$yzZ:._{+j\A-BDVsO(lM\;3b%^f”87e?T57B1l0E3`sG[.!=͇va&Q^g(pی}5 %}J&C :n(T^1( ;v1z`k#יPt|cٝ:?+)6O,JFGhC$1Ewq}?mh'=o5!hʳyн9yӬs?kob bRrjI _ "ȫ=aߦ R-SoЏ3.[t<.KiWhRqkÐi|XOFfq%㹿I|!\;.\ [~fbx5ia{:pP@N`Š`-ˡʽXu 6RM*]J)54B* :s )}Aqmf}ZKp,? dW$HAt=d/ș!s0Zn,LHb`Iئsq<?٣Ju牧!8+UbUQȠANh眞:=i 縁kg$Hň*҅ݹ6^n]N7Lr*Лl˷Te7,O_OBjDq3a0 MV@V"!,VL#>GAmq:@qP(J-Y!J.X0/ZQX۷rߒvBS;)M`CD}MTKP,Z dșdך~ >rB]7 74@ bMmU\ Y+V0F ){xˋs'YG<G%3zV<]ة$] }z C^\ ܝAR<~;ދ<:NZy-F=;BnV¾"+׏B"q~h/'GdoiTx1-G#352ST~R:`-4umIrU`2sQdo֦ezv5H Qt$OAxE#<:#qCCH:=iE3m0=2vPHb[403arXc;piU4ۡ*6-A}ݱ^>PApPQ&a٨u#s%)d sފ̩Z#ϻ1R߄ǪJk`yPxJ.k+ˤK*9Js.su+IqTSܬS|jT>:/ѹ^YHl:ې7p*&ama9 ;l*|P29wcvr)Ùg{kNԓC`ry\<{y'^jjG.ϛ&]s녉/X"S_+ݽCq;aζh] R%[<x6 BZ ܠ{2 };?@q8+wnAfU%!FeB */:K2Xӯc|T-Y8LOK4ilb9}AV8Rbk= Dhmw%XDWe=LEcs>2;I~xh+¿AsBMZޱ ZaŸ@'hJ6EWAІħrxP=楆&'!) kbE%aܢ~\ =p2$g~5vV ka:b#j@/Hf^v8ۻ-r]<_RNHBW'+ H \_-5!)VJQ4tEwpfEU P)%n8+XU!r^W%zI*3eM:*SY_dp'HqY_C%گcP2M 5AU[˥*#\$e\HV˨܀$½ˤ" lC:SOa;~iNes:fs$)[cE(Vh[jb*_hqgzهpLâʮ =It/ͮ 8@fUWP:4Op#I㬽 3# NLZ] 0TyB&t E O933XO2\pf=1gPtgy2˖$A9瓫,5/(ӵ!DE_دuWm+/ n$זv0]tDH jM5DЂ[3՜g<|V T9{>D(B,ϋ%c_T|wWj/rI,md*5:;Vp,dK%ڷ8qA3n2\hC~ſn8-r>$85ѱ38u.8E.O3,1zUI~*jUf[y1*IXr \A;}Nb+v ͬ[t퀠4+R DK-vQ4ۨ0%fRst:XZѹɀ7ZC҈fK(ѲTT*'hkS? ;VRNǖAesY)=P IU?~N"~}_c2x'7U\ (ĺEti×D6!ȫ8{ 0 VE+]Hd$S<6F1±BfNtAvw-}˷CWZ g~O`kV%@( \d{aL031I$pGpQzyA3## bi̥<%EfZM[mM->?qAw:pDeU2$@F n@u$kD@ e:CXkUqʯ9G@2_}OEFfQxc5̨D΋Y)>bSg1,b:W%9xtƤҾqE\qGOix`8ӮTb (ο- pdyQu^A5_1ȆHK`mЮ:$FlD*Z&ɽRF]zAGLenK.,sr8S-7aFIya|xܟU`W) J̸|oO{D lJNTČ-EeيWuH_Y"*?{auيH skm d@ΐ(QW[+Wsyf L&JN۾ JCɤ*}"ߪO`kBZDwUc);u@ҵ}"VWAL!#)z{Z3uňziIN^] KjU?@kXIxc鳉_u+ sT̐[sV9gu<&O*( ՁC5Ʌn>{MjyJHcDb˿k_ ޯp~\WjӄK{+ech¬qMӰ3,~(=Po'Ҟ,ˮQZ'DfM&%ϳX62VY!Qt(l=NA5y'XUGǗ:y du@W- >Fs,6 HP[gT<Rbޘ\{ {Kfo!ڛEa("a*b9Dx9~`jE}MdpԀ%] cFODW[7}L'|EߟYTe׳0x pk<>{c"&7[$y^;ָ n~0Ý@z?e^wYcr<W'v5L[`= /b ^rUlo'W 7+o=ClgJay]]fwdڃY^04W={Y='R6}QE"4rŴ o'ɺJn|Z%e;nSaȦavc:?u.xЩlRd}GIķATuՋ?Ӈzײ:-8sjkmw n"s%;I`_%WXJ7\ɽMQZmb^ۃtcU(WC>AꁍaSM'>M*{1i .#{Ih*̔ΌtpwfpNYYx$`P HW^ɦv?=^H#7Qa?W?R=y1~'+.Eney$yW$nXj:V<_%K߃f4OFx*iĪ _>By F)TqצqomOk7|= QȎ3j2D Ԍʼb3eJE6<'Z/_pNݠF59(|潫"1G6Q2ޔiU_|{*e? ~h$-$ #+CjRN:3. =ɚ[iώ_ӭkI1t;\3]GI ;_^aW٥wZ  vK੃OnOb$4]QG714`*ˢn_nrh#)XfJ^š&UN d&H[NףYr9['gOwiJ_}Z{*'rfT +ⳉR/LAgưx w,Q1!-0 kMk)_0o{M>(0+*MFY`bv5E xI'oqiR=Wk?O=v,,W7~F>HPyVh hg־8cQP.lt,b @1pEo#31:!H^َPX 5,>_n28m`4YzSJ"F@@г~kc6k`2TtWmwX 72o\GR37~38}FH\ynplm&B^N{S!>d\]RYA.+X ;2*Пqn]cp:S *uP(s&6񨏐Ri&s.rfolߏVdy2([\ҤQi~yEY+yټO/(G(OwEeKE%nl%p&aZ&tAe4$;!Y'oA+oピ٤dq//=a6z%7q_AN*׫tjcr|UJŝ˿J D$كf~9`@32+0VT -P;n$!0}r.!R"У᷻W` 󋮺064 0 a r"tƈL p1j!/mjm(C͓d w^_Bߒ =Yq?( # M?H/TLE,AH4XGrS ]>eO]"y# R&BԜdj}PV0C5QsNe.p/G?cHqUr WCJb:Gń7EV 8;/I ndl el{+{]RuO{gz#RJ_AѼ2xV W;]"R燛:g:I=`gio_79V5]AHyFR.LVuG]N:P-\!YV~؍Rje(Hc݀n&%[}ݠ~SD~Wc/2ݺW:h[\^dKMtQ~FJ_[Sž9T; ֶ^|: ֞0},^jo@X7V7(@$ KZQ^[hmH{ nЇOԶ~RxRas7YGrw`!*g~ X JRM-ogTgjk:P{&a_;'V+먾v>A?]>u%ng i2/*ƪtIji/o_ݠ %ޚ5lp~L :'st%10(J>`ŵ}l7H?86+&5YHF'9|P)vJD P֗0TdDd4HvL/_*WW k0:rrJDYtOALsvNk6bwUdWWtzX}:f{,ڐ9 'a .=RX[B |ܰ#KQ\߽Jx[ )̇(X7ݒWȐE妤ְ00q$`{jm1_^!ԚDuR9h9-@UŠK454~'W*C_V:I1z-3˭,eSh|,3gh%Q4r)L Q5 A+9;K-E͆DTh/7T̝Nr+8c\R3Z>L{3c;\˷Ӊ3_:\ys{tB:;pa=Rޟhh98F~GW2^Hn{!T,~?La̎ []So7E0UU(a Ų/x' rzƣ{&Eo':h8N 4[2CgK*}@%gBv -&,1EGM'~ZW5rRCtm:F-O$*3nh$n1 dJ&Yò +}} UuZr܈ qerb,i_AzX6E?BUvchk[RZ\ XCS-A:B9`H#URj [:kgCM^#)ʛǏE).Z7f^UFofYȠqۏ)S$XGU{V+Nr@rQ--zҚ.)PMr:TF6w{"?E7\v7q狒-H8tXj!8P pz:q 4Sc5lT ]x i~`AujbNۚ57 >EkزU]m+.cdDwCp2EYJHH`ڪz#3ЄT8ҪMA![0/֘?tG2<ϼZYh42ªP.]L'sj2v \q=yN[cݷ\nZt/k]oT 3Sujw4No^3TXm ],3~"Kǡ{͖ `K8 jHXM|rja DʄoU]v)jV(Wn!QXx_ͨn`˕_WTT"_qk:XnkS]JѪWwRYC5~2KRAK!-#֙CI)[!1`~RLTf`Hj`*TG6Ћrw l_Ux [$5^*JrlSlصjb-^CX*#Mŏ se+ FB 3 B.Kfڱfۄ#zk >vf撸! ^{t^ &U/=ȁ{1QN]]=!L`ʹ=6RlH3!bo&ܨsJ41TBypD5vSI<9{3f.Fy 3x%Gas+ v0$tjAP1':d%(d7{jlOȡŦxJa,w| ֣gO('@Ty!GR o:\B>T>>KPPwLK"+ͺR~+W\e$W0'=VsAac}V) :%=ROFw&lҽ,\}XC"db ]5L YSpӌ:ir)Nrg~Y ;sILm|$BȳG䍊Ϥ>׹9Cȉ(oN.sx2awЫ;b3ݗM2"$%C`q@,0n$=ta#lSyQ[R?5u#D5=S[2fIHa9*r4m+ (`zD%4¹\5<(mAx=)~ `B3%dc 1hd) sW^ML MFgI&ЊCŸ  !c>x(hc fu?s2KEӄa(cKj&7 \Zbd4?Gٲbf!@ zjY@RcL<)FF25pD֋m(:(Ug`$PpczFgBme]]H#lcf͟ 2b~2l6F>ڝ.~#juB>0SYn0d9wXvbTURZ>с|,]@7Oa&̫\_T$GܷMd}^Ƃל DKfŠ"F7j_Hg~clj3 dZb&N8)3tJKjV7;- &5 ('b0N-~7}OF |UUO`*{G rN4C$4%},lVg&!w_ -ȧםx+Ⱥ8#ijZ$mArcã..˩Ni'L` 'u! M^ @Hr,NzQMcӢm~~]Q{QdQ$dQ w5~Zϵڒ5|aS<#fr[񂀮VovGz_d3Rsf RU47M)eDdnҊܰ~GE C2sVV ;^>-COQ)7pxf\F5duDxM} b.{º7^ebZ{va oXXh*yH+UY uͱ1GSʗH!7tSiٳ{HTo%c@c:C~,RIЄ0 )ƔPuy2\5qc} #+ e.9wUqԿmWo4A p3PQ2Е΄6qKj㉡bCWP]!ES.38%VqSm{S]:_XAb0> d$i_p]5@ G%;;}40w*9B0|<,DyrpQ?@*Z8_ܷ7,#_v+yiMě.m3 D_y3(c 3 z0KkƈJd@FWi8sWJEZ+gdŮ<= x>NyVNҟ"۬bHhR|HJ{k`G|MxҙTfƼ'ښUVJPl!C: *_Wk,9 i}Nj+ oVeHB\Fѹcv{PPEOoÿl4Erʊoί)[ ;&Q&vUpcT HJZ ^eCb8f3brRX#9gT'k% VڕR|bC Ÿ:>av$fT=tXGy YF:\m+a!Ĵ\ӏ:"b>smgO̪VN4 >_a7 1-y(2}x>+T'MSo?><':P#Lƕ ,t^P;KݚnR>)M; I@`"RfQۓ{oEBf@ ;HX5=Bi}:6bŒdb۞JpPviĂ7co+NnsQ:Hlh!r{ ) +gkۇO+,*X`l/CeKݪO׶MrпÑ+Qn2.#/c~rF6 pLbnK?T<1Ud,C9:dvT>Wp%'{g~lmpɯCllO<`s!V.3]B=#I[0AhS8`!_ОŇY-AxnB7v:`m &嗐8B٤_)mMm}?PO{,b@{7;+D?q~n}zi!)y\jb!<6Bz&eg<  /Jv~ãk U]d̽q£0XZN,R=Y=ʃ%Op":g1[ 9/|H&YofSI2Ab _Z~unu jӧeK%]Zs?unH״,)"D(;{ m3!P廬@ߊXOê[5M"v4vO(3G4ly풺دZ\2XpmySia-lU7J% Tja=1xJ[ƃJNQ9e0][t/eQ CL>Z&fK XJ^Y@h06gc[5=,N86 N: L{SY@,=)ALJ] #}ckLT;><4|?y :.cB( Q zϽJ^$Y42]#1"vDC~dEҌq:MJd`f0EV |G_x[eia ]ͶK/`ܐ@o 9R= qz7cq*a~'V}gR~q!Yg؎Ew' g7ڟenTBDu+I:UlNMk:j9`;hQ_ET$}8;]T4lDz27OsOD`GoUk*!I;'0D-t&ER{pl Ԁ^13lngm3]u=Uy QٕԱ3cT{I;и` Ɖ&x#]hiQ aR^wt@b~0=hw:I|@;jSd{-j=t3HteQuiS%R`%Z EJ{FC gFʟ^61[ nQi wRŏFYy}V#ad+ft:2 "\+q91IgvR*h`ĸXFIjas Y҇2ʑQK](ƱD# z{?djX)&'0Y-1;^UPz?oAx7KXk#>(RU p7 }=La?Y"o[*scDg@B? $2VweR]b1p1">V0ja HX* W: m "\p$,dVu>F[eD6Cm 7&7G"q+nd2kǻ>ߏWt \oVdŀm_?DKg@Q>T\éi^ V>+:$WYSwX.ڵ\z mOs;toрs qf'1dLG4Ye>,?ܜ[U!-cj f{c=Corز? *U>:(P`/뀚*xT5jȁPtQ ϟepT; Wr-h̍M:IiX"orS |B,H*oV7\cqg, 3# 搙*äW<*:F{`ډnEɓ!#md`w.E~J4]09 jR]3XJ"oy)^4c"fja(0Vѵijr= M _Oq <AٰFNO*y"77DgΫ55Z`찋~LVU@-cٍ+.%%pDwEU|֛(v%+ jXE^3, #)宸ʜ5&Sã۞:|ٛٛQ\jMa&ZOb9Y,j*I{SO8>fuOJZ:. fGױjKnK b&x-SQ3atτi:_''!V>MIPES)5- ZgcM>'꽏k ksV 1e" ΈYGʛ AXI \x9\yރBys}&==* ?wkR[BY TgFNMT38|U+PaUBohÈ?td rwDx}ªU@f1{ ߗ v' _vCw^uh땱lvmuUtL!TJd&XǿǪ^ c7Oh^ˆ/*JVWV!Rbl{`^:T&PV`{қ(Q2؈]=m!>tIhXȑl{uxqҊK!B}cѐ3F9fa0uN=oIjb8BD ".ȹ}5ߗȗ ? *|.GhcwM U-K uw=OQ⟭0CnQ3%:#ϰC1Ɨk*Ilh^'yY`T?Xˆ{R˔ + A#;+_.BF,nRMO6.;Zjd$bS*%x#LTWo Z@awV@R}D~'lFl_gl e=aВMXQp:T>0!9ÉM# O/^3~DyJ$Mesj3XXǍbZF-IrS[yr5Vd%g^f{wx' ~KWdW[C3\obn3wѮ[_VEWiR$oe\vFՀ/8I7k|Kx~3gl+@R#VNǴ$r7uBՀDxDs;K:`p`3y3ꨮeL!~7Ae ޾?]F"Tq :t~ t܈*r^'a |ː)y|`,$D3I8S?TP)FZlVP-6uYA¦ʓ6X`O`:,X3,?ˀbY Gy(+;sâܞRjVbɪE{m?M=`5^*91# r3UBN`T#J~fnƸ0[EDE#W!9Tl ``[ab2*HFWf@I}\BvTIwԵ\B䗭&%*8ihS1jVkګJ$ rb?\ [4Lƪ(bThJ/!O 7c~6' 6H&`B,-?/1Q1A__`}O* 4] :e.1Q%S8] HS('+YfDd Uh3q ٻ-ϺMWxv.ZbUt}ҷP%,8TDkhAͬۺ.jvQzWioc&ȋLi"Vs͑vpl%md$(-ުd _ Zm|9S 8,nGN`Y X |UDz;APfleR_pFՂ*KtG~KBe8 \֚Z~O,MP+:MTSPPN\X}F!̓Oߺ2ךWFuLy[r{3<kK9щYІ2s,9Kt IBImY 2,~p0"uInCa@-޿. Akr_nf*o\%N~%4곔X@ :xޥnqV5L|m^Mԉ.  1O^dXn`zʤlImY¸ [qLHᮅE_,K0.5[H(r [\Z`ic.s Vk!N>/V AI1JǾpSS6Mߎ*>d/b0)6I<)DrJ(ԤjU= (gezǮ'ZanPIme"rݳpQ[0&ynt0F[&R'dIvAb TOU-R\+3D eRCfW;H;C8126}FHRLHr&'&3U-:A&x\ZYʶ? o#CYN?$#]Gf|w~1,M"hkH{_v^UB׃A-7 ]W2I4So/׌X6 3u۩f׎l(ճ>mQA}t%q$P!R=ǟ:j+3ǥ?fw'ke]u威VWWyixa5 مHxObSZ%KQpޕ˚w}k_l's-V>?s #B  Hj ؀l#b][_)"jXH Ʀ_2s, GHHc.Qge-}|%ve7:)2l:͐-q Vl\}կyGN4)&F#A~~ 꾉5j-%džYS jX kinQVY.4c>ngV*;)d(2)>+Ͽ 65-kfZ}2 L8T[1gCQQa(s{=sQ!wOBR/Ln7 MP3cED>z6TBqs% /j%1W_ڏ3Nf5WU`㗷*q`/yz6I*ҙN.#{ctP]k c1Z#,Y+f0̡lυT6kGHmalzHɈ%4KBI^+v/0ql[St;WRAXZ sͰPd4ߠ5"wzer6۪3\b 87Ƚ6J#UX!̌]uH-Q=꼺0W6C-=D} 7fEfmӤ")b[/`A)uͮ 4ǰp1x+!{U %GË=AճboQ&Qacۑ;J+b[#Y@0E-O]\W3rwUqH]`&:e9]'-9>T#|&q07V?z ~3AeFr <!tȢi?x<_1w|ZVT-WQpz*i?(^"Qb<<)PR~sס@=Vю⏕C6#Jyu)IBhߠETi M%h]DM+F (avk:\xQѿ`YA7HQ~<@yn#տE]b9 C$rHnb![duO'OvhKF,+5/ Ս a͍> X<nԛ?Ѫ?)0 쾀\DE5fW/HHB022dT:F.*6' j O܈5S9іd?:KJ Nȱ0sw ܍C OO{M! fO>S ngF:5uC0LgL'_W13P.NW-BO:89lߋfT%>?WMIUͧ>1ajqr_ތu%Wn%a zV8+o,m /(PϙMEnOңL@)KioKz97F$l9UjvJ~ږP@S03Qa (-@RYy S~[1GXv:ܪfVDyTL r?3)9RTTf`m֋yKﯫ8`+ʸh\dV'2zfՈ𣨆&o'[1*^MXoν+.U f$B̗Q^$}z\)D6E" [*&5ـ l;IXSikyM aXIFDص"A{錌$V `Hf2!ʕztbg6_u&Ǫghr+ H-5}gc1̛M!c*.ͶT ?"t?Cဴ p.FRGܮj?sf6Pz9h% [Όփʢ7jt0Ldc0ꆼ+r!iEJO|=)h3Eo_ QLak#TL%r)yΒcmYyc쥪A&A1#~Z),fX:u+ qLR@$)s;ɟU 'yn[H w{q>GQDzM}9J O% Vmczl*PJ6V% QGۼ3+^s=0|VWb{KW9毉XЅr넡2;)f;6"*Pm)YE;E9:hff[HjwF6H$)8YE5B9r|Uⷬ޻qRAOnZގ& y[ `In26N :tmポRj58?%svHX <V.ZKܹ-ĬJxI"ijb\bmw~k#ݺ\޴r4FTƙkabŠ]S}ȌO lZ~cZLW"l^•1: {Q=t|E L+Fs4ӫ!ZG1'5̃D,$&v]ؑ?[*y!Q)qn<%#ó/"ty#l(MfhTHl|f|Y㇙0TW*mk( ^O!E'1p.I|&¼!@,:ch:0Ps/f_mke TKw}0o{~'L: XZgz-֑ PΖ)djUvR ƆMn~]"B2 0lP~ufؖ6Ӂ6~.hQ̤Te&=zP?ySNҟ{WS4mÍ?(tڸ#o!.]j N6z\Av5;-(^GM6׫rZrQc1H|rp-BV681 +`PB\Vy`ubf# ;[X%LOpvMj*QX'= Vb8{ݰVfk+eQgX)B($3?cIov;bK;G~mvaᲃb=$''S <UcKj큭uBZqO`$'?8I  /W=2}lUZ5:znKI1X0u2ȹk2I:0k󤎱 İ3;Mj Czǜf?`1=š»V r+f rrPI 3r3x"s='L"C}?ޘu֪k agƂ(HGJ`:" w]#.s&=ěT=6EfO˯7|ps7ia_`?)[ ëvuc1 t93UVľTZ'ߐ5TO^*m8xkN@.ܰNfp z;Ɯ˃ݿDfɌ- `3TM=\('8Hꬺ_;ADZb:GfZ_\tlrLCvQu#tNPOu+?r㮾l}::a'Amώ[?sY6&T-c?kvD$6驽VWG@c,"ȇ;1,LsISbHâv2=7FWߘQ럍# /,Jp tPTlucy'9ʚTSiOGbi &`-)q䠿]Z8HWXƸFi@GB{sMǷk'Nqxx5j~J-m&?SgTŌ)5m9c1~4m/#mzL y%ؖYjkɓhțkJ(ՏىméFүkW3ΏшRQY9I ˨4`C>}"?*VX;^B 9Fn#xiH쩴{E i)M"*5<:d䪿P[]#@>τ^Z8Iܽ\yi=\twEPB C0l ߨW>u_hPA"õXo+ ?[-]ӾMm"/ֿaȶXsTIļ5Lw˭;OfD  ]G3'y`^jd$I ^ x`mD >[˃yZ@۟%XU4 5:"R#a*ҹ1ۃerBx+ ڃf1)Ξ|1U!Ib.AuO90݃5apOt(tH Fq. 75B\MY"'@<{M-^鉩 ݑ$J"OEJI (>,ySp2"4@}"$ZsoMGy0ښ}=+KPܞj!fLU ja%gRb-,yPPk6mTM-Ov2gϖ x,|&V,Ms;d,L V)oVy@T%uZQOSo-#93NXxd}[ 6ۛZM$oA~.'H-z F% }i jM ;6,[CSɀ &5Y%\ vnh0c KN2FιgP&ޒy\ry5Zã;/.h EQGkG!/{GLϒ/3H4?էmse4i~^X%7Ih{R7aY 2֘}Ap z3# BG`7*"Sp}gn|sҿTm !߷Dcb:AH:Rgp7?b2#fd,ZW(:>& 4&~^U*K`Q,LOG>(A Du6J*AVTGӮ{m06?s4AwJ{1Sw9wRmWkjDea"R(㷏8ebu>Nd@a`fuI'E ,wGP)N:cdY]~m=~LG[>z*S:WuFܻC.3qݬgnVn 29t,K-3PCBG}#.1ˡ5Aqg㘣3cLL<'y'UbG.\+_H$Q IRl*A o'~.HV-%%KgvjFӛ,8ۀJHD3t _òD !? TL+IB1p>DTͼȒ+~fyl59ELU XYd N,:6aƟK~ 2^] Z ?B+!G x-Ku5+ [ 7W"wґiز9v*8/mN)5Tԭ2A?z*L6bAW-c!HUL "Vk_4~#[b SJd;˫ QZޮW~C Ã6hՄGdrH?rk>ȯ?enmCj[]~:ϐ$OxfJ-(TS3D{(I~v_-{z=Tof$I&q- AcY˨,?Lt&ӂt޼0 &uojP#ӦR9UVpZX۰Tm IGԿT~n[zPa#vOXW䡯ꓴ,&=I>ZafWA.2Tf\5fWǣxB:"`' Pn>x] v ||j.kXDB:<>XcQqh?0b}ֿg^Vd;su4o8pS96"@7bL- #4oe,kcU:osr!a`S F-Fu ma4m3n^M֏"r;oVI#'5ze2IB|9nH_"ܒk _:֋Tp:Utm X[nV C~TeBR E` 91{mV&vR-F9&̾`@&[%XK-=R}z˕~2K%d[b%۞ƫb'"y~it+\*P Я;zLJ ҃ZE>c DITFN~sC,&PRH%?Wd h zbpîu1˜J'8x{s1;CBgߡuɧ J5p604n.o3դV|ԁDs lIkIF Ɓ1ZϿXwYYZ ۙ{ "b C~8#8Ge5Y'H`:Д `%%ũL澓.Roc&mVHx,>YLL9YӳïZ^ 9C & *aD9FHP%bKxt uxfIikViGT?KTjs(on識A7_opv$Q"Ɛgp9#@'^(tvj|y#g:F/ȅ>eLI~Zgk\YӯRT3Z^AN^h+zΠ3n8b&ġtf!?ꗒlY{]R_Ɏ/|}\=VQtt̯o܀&\)JD`M]jqif\T^1%:2֡r`gegT퓙>":}{\bɰtPW3XO%Ƿ sV]W"e9I-y_wσR+ `0C5;&\Iw*#XX O=5YJdy Cn'y& r6;NZcg6LN^k@&a)cW'n:Qj1? xTvW ]0ER*j $>\oSLj<j$mV r#0Ǚd:8{8f."WrET)'@ߤqͬp$E)x算W=鸾ZEyh VPTfFR= 'ce'DFm>[vhtJ9|xAU52{1uS]uR'Z~ cǂ_vAgfG+ɒZi460d@FEp!yjڑ H¼0 aElMSKB m7KC:>vx! DZJm'+OZ؜mv)%!Co0rpՆ61νU;uecy*Mo8?S>7Ap&wKO2=^kۖ]``˫F`${"YVe&`4{/?R&bLL4/WsHħ#`]i%S4B35-@̠1@:sv <{-^E4j;_Ӷ}ݓ~WF'}R"kI}uV쁡RnxȏUؕPMJI_%NcRRDBwDj$6vL?ÑoBkޗRks*)ZgJHp쭅  cյ*mW 1&O&;!NU5.9 Z8U<9P0h"+i_Vܿ7$5%d.CʔФV Q8~f1`F{2ɪ pmrCA5ub3GkNV^0F:!B5i/ۛQZVԵJb;3T^mj`&dLey;$8kzU3YYe12iP?1_r'.LD ALNQ V(v=4 &', XwVA7r[Ċp똪E4Z9rUc ;C.[~QRw)1on7TB: _l |$6랱]jq/e"Fkly*@&lCBc5@I9s3IC-_̤_5/nhz)f+#2PkѺ -+duT8qU{LfjEs8wh ĸC"6"9Ώ/(B I*euٛ׋9&`=tO,9 ]"AB[=V{j|)8h XRn2l:_Ϸ Q}fT9bu(ɐKY[l3s ^r42L󮆰ow"N%X Hfafšp`)_̈Υ N6m*vCO$ ұůzĕOq0 b*}B<^Y )7 )q;34lyX8 x&`M"@߾zy&ԟX6<΀:Z~͹N| [!XT }j y'=o^QqT3ڧ$ABW]V dk b|?p 8JX M(AV!oiP~Ϋz!,Pp+&n1fi ES'&6J&$T;KT$'YG.5*Ho-Sn$$I֨ި#[k:Y ȭ!zފID9A!MW6ClSM%'zt LlBwV"ϪDvX3n{QX Zo l>žJNH7] ƨF" 1co Wm۬07bŔĩ!XU)4Տ>DiyvK Qȁॳ) LC}V&HЮ!jJ/!Y9u v@M*"&:>Rjѓ;=)v*̋,mTWݯD|- P{p#ë55)uImh\TXSߑc.pf[{ ϯ2+Lv L꛲Ѥ4zG.+|2^fub7`6l\~2S>IaTN% x1|v z 1 z%_=ƸD9+0fD$U,;ZH\ qMw6q'ŔӝnTiflKz7 ` W~i=`i8)Y5gI=]K'ħMhD.&Y&:OXyq#hhɗJJ LIOa6(~wipp&`a2Sg%VGCybB\ƒNj0֖׻NM5dQ}EG}:ԭ b]1>[\՝ʂ8 `ƃ TY𛈵=Nvч,ʨ+iaI ]Gp³!DU͓~ EB#`fʱw1 CPq_<>=Ksrz&Gs^IAIŹ*>2s_E:8#jxC ,QfOܖ!UDBe,3Ul}EZep#0G6\΁CދdcBUΌJ=L3GZ^ .`-Ao54ɾryY^ئBhvڒڞCݫrz[Ƿ ^uYś|10ح'~ii&qpWxL8udA;=X,)O_yQZvTs8&@tZ.'(b#CIY+ BV٬iӤҮ,FGRu ~t6"N ol4 "C^GxpjZ23xoAAwwg)]x-I4ɕp#*Wg f#zGe*vd.hr)y4:H_ Wg:N\#I{A]wi BxiRdX<0A$RҸ'xhc+-W踷Kmgn܊M6 &qDq0t7Q,#IL.H9Ϲ7hVjٳ߱+6痴*]zuQ%O;D\ը&G!X>F=eV,_U}ER~l!_Gϫih~ ~\M*v|[NJ>t0BޤDudS^!&dP50w39Gک1SQz`E"dPd;R&,'of?9ɣ2jLd6V6LS{?>1te "^94J<9obd @z a+K$4EZGwHFi~\R{RuXs?erL£Y(ER 1wR "&Z::Ӑ@c5O3˛fm0σIsfҿ޵ɯ/>8SOb0%_UF֟WYRyEk_.o:YAd_SꂗN P?"W6.]ѱVHNRs Nvlsi;鑯]'<:zV8l? @VrmeS{BNǒt Fpz7?f"Z҉ p^[aQ$,'ԛ˨kR3v;_*J[}ed[l:tj0]gno&jOlQ5Q?5MvbdO2xs8OW:s$>[~՟p;qCMfo6aE XOhP`[b띺u~e,TG7nd[QuMe_JT CUP;A+;@^ǎ>Q 2u@SP{pDb}Q]pAR^_xߣ(F-;_|TthHو,auff6;Ʊ:IW5 |M)"2hw?yMµ˓g+kH\G?B%~ OҐ[4<'epZ|`2^A$[Ӂ7u% [/ T sS|JYtb? F4K0Qk?ۃ1: ;i暫r0սN`_3쉗{d8Nuj yPU]Wa.Kٶ 05J+b^"fYc&ލ|6h@h_(~¾VbiL>t>$8 \L{x( lz;5*s@TK#U)LrFhKS{6#%eZZKJif>'u},adT]^|>Lpp}Ϋ+5y <$rhx7.^RsEa^''D@.k9 (a/\B_J͝3T埧IxW`em8m.).9/&Q3?xCڧH8>SJLP~8נqR#@Uz65ek$=~D(4X&&s^I¼6!ǔ%R݆ ֲe'bUc ڝ:+6Pfl$I^jg&a_ QBWޣ ̳Fh*Tz/}E𲱟C2){dGsHc5feu4xY)9Fv _ UW{"~!J#0;g\) y\ҮyV(5S"sT D8e0_YŭLSlcWЦpcn~0}LJv/1|-|%|4DP+ްeQzO;3&X 3tamR1 ぜ7ClT0 UPRjiU^qW t~-vs9ZeأU/Ȍw"HAtdo3C^`X]OO_w}M;sũx.OG"OC(pwnyAN_:=k|"qf[DU slܺiWgOUgJЛbT{vsso/+~+5%΄m:U XZ!Ћ,Z!;C!J|tE5*ڲ̋ uӤQTjR9)+BV|Z[љ_tF!iLjW1~^ˆTȢ#ᩂꄻcOTFVOTu?2unLHc piL^5{Һ-x +GnUjۇ D B:xt Koop!, %9n)]LHLK)TgV,E#n?Β*hHw4lg}{D=ojNRQ'[Ӎ`UNRNw==~g!1?T)ƬӓV4˾cdUH:^! &5VAcz=bԧ90+D^s9;$¾ n~ <4"H?ϯZʜ:E)%+Y#]YvUw,O /ު$⊶bL>S4">{;؊A2:ǤFc6XϨsND0-GeעKk͕ja4ܣq,q}AfcBɡH0_\4Y1,nAՎR_7 0t ȭ&/RLLN}bN%D- :ۏwJn&3Vo,u A9ۃ܄֟Y7ߌ.w^UnAfU%!FcB o:KU2X3a`| [x|%Z46B_nԦ[,|#t?Yb]M3/FY/(驻h,֗3+s0:W0сF(d3Uz UPr+e*(nO˳wP 9yTVG` ̺V**(x0\Pc#Ɔ#!҈e_*' SɘOUcZe : @BMɦܮJ H+޻L*{9*|>o6Q=+`L+@RE!%BXhOnC}E]Az//ݮ 8q󪮞wh=XصMge@W}wbFX*S,K sӁK\A;}Nb; 90YmA7ُ+R DK-vQؤ0)fJr:XZѹɀ7Zf(Ѳ6nT'hkS;!xAes;O㜟$&F^?'Y!/ZII*u._Lg bp_.o⣩JdcɎ*N>T/tX<hllv%VDUQԓONL\J`#+d_ɉ#nÜ2Oܖ}_:{H} *\ thr2ؚI"" :#ّ)USػP L'&3 }qϫjWG~V ] Q̣5}V,:6>y%2$9m2*1O?'Qt~>1Mo`zL׺dK݄U+~&r{➂ͭ' ѨF4NHrjJXHV˘z//,qIc1M%I"a)w6tKrN }wxǒ{%3 RnHNlfq٪#;UfllG!d߾vE k.k '|!MƸ`2"m oXl'!\}ŝUI68Xȇ )ܘ}2i%ܑ(:FZ--,Xbp~7}S^Rq`l{0kAfl4OQ|vYsKbHrB jdmC.E@,9zd |J];J1f^*b9D>x9~`jEMdpq¾]cFLW[7L'|%4, g58a*R>a{#|e/>RDd*a*0Ivq e\`;"3~&Sj0xFN2k- kxO4`5'"D~M5N0@nV zܦ:>>"azwR2 zk,2@ {> ?"AfjRjzwt%d[%7^Rpm?l>fW'O:M,[$ ?Vjzg&b8q N!:\6Z2H\%g-WXJ7\ɽ]QZmfۓrcU(WS>CꉍaSM'>%%ȴEw\{H3*̔Όupw͆tԳرaA ]E?xղ.aJ x##s "G))~ I{b*W\ yW}h(Y֞wjiTMPt(*hj(+5#7Gdf>zl:p 0 ]h{-;%أ[pdaZ?lNQU'cPd8Ώ$ {b49YĪnN׹@wmA6 LfQnn] s6)J`FshwFj5s춿I6HQ:SuLfBDo~Y+)Ge ;ϼ)&drV3*r9W4d[~9wmTXXrX\XB$wOLGgU_)C)/pUz~@R|6/矓tT+b, EH]j:d*0Rr0caxOL*Lvo-_ LTs!H:v:g22l{yv_Ffi1(-M=t%p[\f>n.Cy'ׁm*,h AX ìI=i~t#%ur6x.u*׫uXLLJu[ ݉6cP\U+~)]N> <>P_:ګֲNyU<' =p\5q.Dфh{ej+h#/ q'KT|HJ!A2R\ヨ";o+>1*Dt p' |EW[8 4#pX;Ř5PVJ> e>;lzi}E_9"Elv*fzSԔW9t?;.uWj1yT?b2 c]>U4+)f,UǍKx|#{n" sJ m/xsƔA6+%yUpA#NevD7/!!鐋7gd5ˊť_zH̲;f3݆I y#<:%K$=_iy4z_܆0k84Wm wX 72ğ%pIT2,#=٨8 !=~P#ADpc;l3[r{B{C%E;a C)-sRS}[Sq8-k rjAYeM e&+Ts\˨ln?Bm ,>-8[wȾZ us6鿞PTırPi3_MS2lt JpVw/`veBWTzsle}m9jM~"ev)Y+HOfXԞCd\Laq*@:v.*9O\%Uy$((O7 9a _cm)ɈoK@_F7 w*:+;r.gr{# |l峏kn<hPaG{«+:>H'î1bf,S\ZHKbi; e 4A! ]BOO!{ yXt" g%}X[%HcxqȅܧD8o P#SVT(sLI  fRr67ɟtIE~Xf#OB fG* tFz vMbn/ OxE@m4wڟ xDa=̬>VC~YD5&ߗH; F&JZå6a$B/UW>y-4U33ŜV-Wɐʊx5.願yGHcժ㉱ Sz\_dAJR_e}fOw˪.yWHXo0s}UWa>)7Yp}9jK$!xI#f&GFϐ*Rwxݥ' UIW#Q)8$a6 d=-a0)(ϴ)X ̭qϥ64Wf#Wl(w}]F꼿mb %A/ĭ){FSaD, guG%ǼxG|md=.L_b<˺ײZ~RͧՍg (ңV{[t6^m$dq~ߪa-$~dž~JYq0=qU'@bkYՙzXjݤ:`U#a*v>EԱ>u%Q&i[gUq$'X %ޚ5l_tFuX&KcbQ}knLdr3p':+ VLjyLbmԮxVHSIj-ooN08 1 yV/e![HF'9|P)vJDP8TdDd4H L\.T32atm4RՇogz4}jmpZqHZEFuNgPۋu8BOlJ wY;r4A>5t= dWTJvKO14I`d{PDQ|Жґf[ rܔ&ΕtS 7˝){ӑx=TN"ZU!3E82u=>H^}꤃VMr_uShB,ў3M*TO6 )Or)LM\i k"&W>!KU͆DThbjA'[W.m|vEՙjñ=\˷Ydadv5Bm!MV@mo`4Y2u9N:Q/ᕌׁG,ҭ^ q'KnҰ_ CV=|4MbU9j5z0(dwl ,^?2ӶJMB u?kdAPD9Akv-a8agSlOevQucߦ%Xv1!~k$yෑ*q#f.~6)IȉTE|6>m+#DϝBm1qjb펵|)a .owRkrL ]!CFI0*{-!&~+)ʇǷE)KL7f^UFofYȠq_SI̭V(~; 䜿 b#Z][l[*5]%PMr:bK#Qq;=Mq ~"Yf搊}oa8EYN,IfGEPE UN8=`K1zB6Z]y.|Z4?LC:5xVqUz^mɚLIdz mlYPŪC612zeϻ!r2EYJHH`ڪz#3ЄT8ҪMA![0/?kE\# gDD-D]h42ªP.]L'sj2v \q=yoO[cݷ\nZt/k]oT 3Sujw4No^3TXm ](3~"Kǡ{͖ `K8 jHXM|rja DʄoU]v)jV(Wn!QXx=F;כQ+=ʩD:$#)?qk:XnkS]JѪWO:?_Aǡo(ֿ {I[̡ڤs L0?u&*UwLIpA0$50IG*# mRqGЯH`Tn]/%y9|6ZX_arsf!Ius,ͦG@~2EtqMil(:l#0c3~#zd~+_v$:MK#F!%{X3mBwYrI Iy ;f3sI\Ez=:/~*ȗ(i@.&f S6Hy ɏBrcmnԹUVT t@e*UQ3^ry#){.!S`ۥBD%f]ak)I?Օ+.HʫrHǰA>LPkm'l;PԿu6^M yzR.>,_d2j1T>4N-p "OKag.7OWWQhyQQԧ:b2{@9IeO&,zuGlIFpL3Sr:49K! I]X{T^ԖOM]3Q BԖ̨Y0RXNkF M[) 1-^Q pn ky:PڂU'{4S~; gJ4:V4b,;do(@M^^{62t0n 6| g4*&@+9g| '@c{]eY$͐dIwq, vl'q<%OyHdve@Zv,-eZc<ޫ3MGצi;|e??uw|>% ͌ vJFy"E*=%̅'#]^@ 9&b !GW~; [`8 2t5P+dS`FH^ 'M& R40vw)2<<Z!BuՠFa_{`t=-js->&!)g*EVf"tæp^i i1%#r1zoe0 NHd5LXP#i^t!\E* MB Z|%Qᙖ!ca:+kpiҮU +O|-o#]5w^—0eW*VkHE/]N34q %o̓k<lP#UBx3(R%+\yJX c%0lΈ\`4(@D4eF̄CR>GU ҤҥO93vM'`+~^ GQЮM # .Fu%OD-i%KHH@LҔt{Sʑ$5"OeRh^+-%2w2 @>I4^No1.C\Quԗӈ8 &*^vB>pX-fr/$Xq{t/ah!ø ~U?l@ AC"z"->Q$5,3'oTe\SAP+G! Zp"G H 7ܕXsMiY,SGC֗i1c9/WSZ$Xƹ>$WUd ,"& R!Rќ(bȬVl5X?,G:( <,b/8 U1xi-,|%VgfтRL+bQbO{a] $LqĉCxi;i2 V꫰0A"")*ǁa\eQ)he38WD 25G;shaBfIK&JI&IdV0*F7ghJVĸ31) XՅ 6Ŭ.G%wJ $U&SWJ @p qc!yx0/ Fbw`.hW<-GEB%RIQ&, *fA4DB!!* d6«N3t& @kI?,Z#U yVX`<@(a`r|6*Yk/ifNwO)^Cɝ*͕bUVd[яb*=R=$Gl}N,dr42W&5_!g(2SFe:-(2 K֟ ~i0@&Mr!E@QGSщU`d )(YH@C5JQ*VlfIp*DOU;ϔ:hF"c>-MAZBrFbYϕIUfMJ`YLZG*bNbAel@8s HIȶ,I\ IhV8益و_ @ LMy (Tw9@^XE;0N i ^l+@ a+6 ^$vKO[^CJs$:` Kc qbOC4̆-!C.-XpR )&Yǻ aHGz! Za@RyD ]!(n3 "zEZ~ ,CB"/ J$H%+ 4 e2zx[F. pa*St!0[0H@1n慧47DY ov:h yQ w8JPEl\ʊH8 fZË0)H3uj氒^GXd4ς@dǂ Aw] ëh+r(P .^o~K"jq$9 ` EMVe:?a䐐0 2= CH@l+e'XJD 9գ ҕ*lˮh#|L"y?g|+O**ҨrGV +CfUSlSh2G͑G4^(rG&4NĨ[:SuC⠴T`bJCTr`h߈*{a*5V"&!\UlkQ@,ZA T Ć L)0!w Ƀ:JanE.P , U\+=|+2 ȒLqiRH|%#nt1bg"0QTZ_ 3$I4T~AGlU.ABs`DS\CTX!( \6qJ1>+Xq[0}<bn'giIEdl6c0C/p(FF\~+>eL+uA"NV\n 7dAķqm˺Dlv4VB~4-iR@F*$bhnVp2 ! vZ@H+Z_:+M!fU*(h*"(`޴+Aͥ8VD[`0V,qG1>vB$AqZeB*z~ l}da-,iWYKĆ(UG{B8 fd};Y> 7LxQ2b^BQ4djY-oh:Tʣյ -@ 2Ljf*. Z-CؑA1!si1gԓRuˢ}RtKλdAXZ=<HFNdFeOe Qhde9aHyIQז#i N$jLw[A ֝T5{&4>ɠ{DZ NCVPu-n9Ƹ<к@/"-)r"E3ıXdn >?>+Y[_\bZ+ ,C pz|RPT=a1ziH(gg*pI-+bz:VpZﭠH7cV :Lƣ50*"I.шo3%HT+B94Qp+c)X)sY G(Jv >`hS4 6D5s^8HzؼqJ P8levZzȕMGxɗTDW=0cAPW҈^ `٣WTE?+lUn҈⇆bi+;y`HUdJtZtjA/yMC_VD =LcUTZ)X)Ѐ EW "0C Ur8;g)eɭ zy%HWQCdAj@ҊK07Sɔ*bẼ\Lo5ce[)(Mګc8^'ݖPUUEXAEG?]hI8 T(2Cvp3 ecd(m92=*,/J2U]AWm@P#hI9HБl$ҁNw0q} *,XTdtÃNᓪRZU-i*to"W ӖM_EGgdHhl߂䩢х-C`'TDx~3jE8.GOt9`ݍNV1J"  Dwqh"T:/I%'XDp;]GD4 B+IU[<)xih%Cy-ڤ]AQid )d#r %ĭd}*.AAА% M9y ى`H IԆS^᝚eP%UHİ:enDBlw" !r^NU'zXԇPX^B f?UX_ˆHJp)304$pŧ-XRe˸a {!|<փVK|>63-EI!! T,0FÉP>+:pQvTrY(8 8H?Փ$-yb&u,UTiW ^;IB+`~.'O}p6ʌpXiG&qMYJe, Kz`d%#hp Ch_[$ mOH QSCV=j͓ HkfXӨlyw?bdsX{i~19x0WB~'1,E,c=fR(0IAF#45D!@V !#dvt#ثQj&Nv`8CKjHMQy1ZIO LAOI$m/ݑ.(Q&(fV [ v4$6 "N7%ہ 38bk`~ape 7N^np મ98_ͳ6ﲱx#.]Ư߂O'/6~d|WFzcE53ݙ31b{w_`L_+b|#zt\ɨ95>>?OG}?8C}쏛xkl-g\&?zs5:x{ݪ?~yny}}{w^oTω=n?~}ܧoT*UU`i~쵳sX7%Cg o{=grvlܿ)/_o=kZٷ/,n>?tg, ~jspOҦ#774r6-ٟ/&g*Lmʲtsw@@B=ƛWזMg'Oxs9/3˦j+2ʊ*t=[̯infywYye%Fy%//?lXZ.xId21-|ז,zXk@Ogsm݋O._M;{z{Bݽxrow ~Y6x˿Yϯ~S}7p0?<7LF#6W/.}U4ʪ*EQ9N򲭥W\P_(7hyeiWH|rbɓddAե%xo3KmeOWP#/X>O w•|;MLG~]0-/|cц+c}7csw|]>XWנZ]8_O$?lbo)'⣁ߚ7b7&{ojl ݡX2jo|™@w/0{iޛlh ><08paxp}~+v B[& {_Լ<6kf{]/Hd4MJ$x$O͛KXf=Ҕi66ՏeM񥾎yğG{2q2;7M}+y=CC/o4_w=1c+>݁_@t,K$c|G;߿4m'D'Nԟxk`k}ǯ|p i.h*3hwsӵ梦؅MK/kMƭ&Ssq×V|ࣝ5gE|qگ1&Db,GhO":rmK$2s \8ׇ{ksڜq`k}.?1aOL h$C  uC+s{Ҋҹ᳟iz9Sz|026nǩvh^g"~Sfn۽G^3~fnϙӵm1$`W3ObӫX"9}sbd46;}\u\ݶ7plE]3ƻkkuyum u fl}^egm;[sf}£7vm7_==6^+8s ,Y f ;綟i~qG劲)D;d_y$L'Bv6w1-œ{+x"zzO#xp+-$i';v:^N>cΦw tLtW;;[^$qXF86v?ڱ(f(<6YO #~,6NƋ& <6$#GjӋW;?X8;~`~ GwϟϤ iCS&;oLic0ѵw-|y Vҹ\zWS)N/~Ϥj[K|kh,7Oܹ.xƝ+76ܿHE?GzdOҦx2:øӦW뛺c/ML{_}5 ;x6[;wwq&j)bC[۟=_64\<]DW_}l/ۃ/ÿ֚[_/}vJtm0zeh{y%n~zz]Jv!Lnu-+.|ac5yt>Z؍y(0=[xpv/<:6GcƢ@l07;ʖ#GGru;NԖajv6;kjGGёpdxtdt0;j ߘwx4zZLΦBhla 96v?(,'ch,9M'ICS`{vvl^OlX<eضHl4xl^gcpdύټYc0X-|_>5b؜{a6x_8YS3kNFNfR%~s/&S+> '̣ۢX|#f>܄"&ل)9ۈ> Bo}PGVg >k9p``C8N{} 9,>k :ŁG} ՆQ~lq8ܺtԍumـ3;j}U*M6G^Gv{CCP`$62lz=U>wG,6ܹՃ5xߙ߽pl42IKD"(OLjFuS`H4{KK{h4 h-HS\z)7S#7^T,\7>RyԾ`j]}wխ=Hxoį>\;qzro1EcXo?)wj=#TaX< xrp8N nF:͝-oFވF\KcTWU{r:[SX]S^'S_]eZ4=Wm2ܓIlg?f(/LJs?fت3ܳ>SM; OUaJJ;»>mU U]aZM rkVM˗ܿT}ee8Ǻ.Nwr~uOl?ެ>i}b/etz >>qep(:Q]}|ֹ5;m|)cH >99=8<8 GG£0l4ubf&;S>0tjmfg*}#CCpHE?)v2mpt0:ڹ>cb{|qw#b:#(2m$w?5cꘙ|$7h:6 |\:Jp:n_ lܠg Q]ә5tx3Fl_ fg5x^}eyL^{̾F"0҅puexs3^玝ι;Z_>c=<3:<ϵ`Ǻ:Sxpl8 ff h<imX :FH 6n %3Kox#ʹp*rg ]%_Qf h}>wEJ^\AUq-sukꚡ$Ŋmb;6mġ0 lp(Դs7TgLhK靟:=58?9m\8= ZGNjc:[6ƗV3|m߻mǿ|ث:{`z3Ծ> [F}zz|L:ry+/mNXo } cӦxP$ =֣}wHHfОAˢ/CϿ[|GɭSܜ^Rɩݜֵ ު!`JVy=YsH ͺ5>}qژ2[24nL[SSSC-SSͽC=7~|}vnۘwbq<ۆ>2թ?QwQg|w`c1ƾ=u;<1i* B!YMґ6M3Jڤ <|8@R ;K#t~dI f pVvu.'tzaXKuxjuĻ~pe$|<8҅vLѮL骪t+6iy&KYeib[]_.lW~fyþxWҾF1idV$Ij|:nVa|6٬lnᬙȹWɧo!3gم]`k::3o`"C]M.Bau@O0P;k8%'/z$ge ccl5<AeX*"R?"StVV#{DP4w`̅Gr<)J#[6C^`X,UT4PwwH!"ܮ?UT`Xhʪ שׁrV/D8 8bRYptǗ`HrP*9*e&i,igAtH6zMF0#~]D:DK폰0/a3$d!U;)4Ca6#ɦ(zQfw:vx]. .\{%3U!ӖU0\p( EoBD@1JJk%f,EpgGN9q ɟ1(@g"o\G_BB_EݙU0ys(yG *DȧS1LO?ly/§rЧ=OSg FTC{/\8O^9558D>qt…ދCxrs8B QϧLîܔuOMDhl]x,GHQΩGcT.<`ṫnHS$[CPZ'-xXԆ2Uva҄d`zü}@$+be~k$q$yѰ@pnޥV9 UFVJ0AĿ,\3N69esh8Sre.] X-Y\8(dca qYn7xp\W9\fF{_o"6Ȍh!g&+Q d>lS gj.z_rGAZe\Va 6۬8e%mtNm/.yfkjZ$¼*eo yyܼiS+NIEhLsOe"yu8 X702̟HS_Eϛ1| &#Œ&ɟ"oӎ܉]bO˟5Fȳc{Ͽ/sLsg *aJL%rl2'9EiM&s)+~Vv`觶9Tv VCQUP$_2YE򅅅,28S i˔о=BPhʤ i0X1ގ4f,Za/3 >X )BPU~>$ }UB\nymg⫉k5YhHT=Z !f ,0p(n؜oT % }riX6*0 ,*,l/Xe|XW[V^ㄮ[/Z*XT (E"3G d"+H/Q3mj>]y[M<~1ECTYnd6,ߦm*3jY| v90XaeeK!lKrlV#Pґ5l(};ʥr3&Irn@85harCw>dMԜD:BcstB"{ A^yn*stȔ)|9SIW )C|}oNc>豯Ns4z11*'O8 &gK2NJ(R eb/6@)2'M$'q%A+ӟ9Hg)/Ybd0eo0bԿp?aB6/8~Gĉ-N^Oh v  3%N Q/Q)q =my 쬬۲n#2{޾U12>G~> k|j0v(iH7P;@qz$h5`ޢ!i߇%?p)Lww dvov6U@fRc'Z@=j? | I"7WKb$lK$p]ýa2~dlf f5Jj78+z/8jzŋ p %r/ݡR։M&nуXPBe$m#t I EYj"[/Qgh* pM(Q@tkGtخ}+@j-GEb*#6_1(rdv)hcJ RG9یVA2UV`DZ; ,DInQ2/=3 RY؉"wH9;i v"Mlzì(2>@w2qiV#ef]} ơZck۶9Nk0ڷ'_N  {>qp馦G 8} b;}kd55HmLΈ%\Y?QQ>TiK*.YTEb0̛gy/8x WV_~߿HK^+ ]IU ?[i5 wy ;w/RCYQ?Gz6 ֘g4X-6QuQZ~g)e lRV >c됟)M:j1,ly2BҟC? :pJ,gIegϒI`'~?]bq?ۧSklb 6eZ V?;d5I3"'''s+:վ1r>?ދW93ml,=sR@r꘱cǦT^qT:sI 7\ճ>TPE ETʤZhp}+ʄ ZPmJCy>+WMjOaV|-3 [z}@S- 75E[Q-H5q"V,&bXLEg[,F3nM:%kr1g'Q fҚj1iϜ9GAC X 1dҙ-|,8 ggV>$ |='}\O{{Ǘ|3ܼ>\c|}}/c?+q>Vћ8^TAl]l^ǍQBid7})7:AK[5u͢"ӴD|?PLAfE4F1ţLeqػw=4X"cąΟGƌaD %|$%' DZ#O;;og< J9tGte?vaR%cƎ!e_楍C夦 %SƦ-bC`QYoؠ3c\{*͇z3I/*x;YfVxkĸAFi+Ǐc?zsu(zpqmBZUzU%^N^D'y{enwBf3ɤ549-;-;0*LQ:H($3I~ˑj^o8ެ-:ͧV/Gޤ#4j zYBi̋ Fڼgz='`eĤ7cbwuqz. "f$^bH"EE^TeJl[ FLiFvHKR7cߣz 7-wBk07rhjHOhQ0BI\DP DKؖ}^+hc(h4.7995Zv#1$BMK!^+t!#Hj*HZ꘴1+^^ xl@$^N%8e<^Nq7ݽ{~=hχ}'7?{y畓SRTJϿO,b9=QY*5h|/p!Gy }z#KQϸ0 %]%rq%jӗz/!=l^Jw('[ U\-/?kYR0 zD`Ъ1Ґ 3i! lF=%xԊ GHDQhC Ib,plx?'!+S_s}NNXḫKC~h)1$1"j&/\9A#*'a: U)0J12amC$J%Sx)@LPhJjH*w3755%H$ |R P"%2doCSbINdd۠8r}qiDp6h[zBdXyucpC}>i0 nqX~H*l/5[*'q-NM{8õaY7*6θQ^4䞬Y0w /?-Q==jɰ{\Z\1h5OZM9bKnŲ3jJblAAV-aX* "Jccgwv^j=WzXa*z}7 u=lv>a2AL&tPxǰnpsju#,pSV-Dh^|΍J⨛U*&hױ^7K(w >7%it\}:fXIC*4:df1A[>N~ӥŵ \V}6TqW+ Ъ~74U|BqQhD~LGNχ%Cc% CHX#P2涷~X#h >nkw_L0q>7ԑoG~y#_|/If!_ ]vS N g;Хg:PZ\+%01DZP,t#vuWcHs<L^Ȇ[S,imOvjHhtW::3‚:Xi↱zL3Tjx;7Q ~j_,ys#sp\]3ގWhew?xฅL"q." rܻ8gJ AqG8\.88\m~lm~oslwpꮽ{;/ 94rs{{K[hҜ$gN}5N^s(;ϻ8۞C_igKK.'Q,\D[= |}w: J9v^`?R??-?qI4FQ6g’Ma=d#3`Lx,iw?D<6 ٽ_N틜n2Uy$rg8|˕0ΤQ8nM޳mI]T4.J/PTB m}Gik./hk[$S=0ƽ_"J8.] I[ٖL-SJRaRRpH"8D5kΞ#Ι#)Hg.tlh?Τ rLMb|5 6b8ĥ+`eזY_'E;#=F֪JEpX b+mcJL ?J ar% j0ڷh)!L+o B8RB]",-U\l\:莭47K.-o^jWS\X}6֮$ A,B5L/G`ɢP ff<b`a=hL&F^ c}#m^;6='.v>wc]%^"(\H-3.n&'] m񯇺\ *A uI)*#Uj>h`fSK@Ͷ&FRM KR5ZEjP&5M"Rb\"VJ$QJĚísGYCP.Ԩ0@k}ݭZJ@{k{[\I ɾޣ-]0c9K:ƽr%FhUw< ?%Z9h mˢ|ѵ]k;<$z9ih.0=E VRzXpM3-3GX§Z7TRú~K[dJ[XރhkwPe w [-jIرJ'`vkI=q=!xGmG_ZZ;B^7=֧sM:ZF0~ԉ΅Ĩ80o] hN:t@h/:ko5,p>|;>ݜ|5X3'ϻZ(d1/my(Hg[z8y]cFcڠJ k_潛~qСoA{6u>u5gLCG;z;ȘXmi۾ۻyɻ;Z{6vt ^;t'N>ټ7w];ڵyөB}+'/dދnT*oP[PY5#srѢ|Fݝ`՗uFup⑅":ZNi5ۮߞh>ti=8>A8߿^_sP_[֍ B} SP 5YY,.aT߽˯ֶ-^=F wz^ׇ%\cU< A JWTJ_0v@:2}Y2_#ا/~f~͂}rQ=șKOK/}:/n%ݯYttY[_mE^$^JHmE_"ݶiV^J6_ Q泆Ҽ̼hVF_߼u;|7?36s?}晟>%~UoFߡM_Tۗ7&33M - -!Mٴ Uk@TWߔ;?w:`AJā ҉ZNa:\ˁY;3庫'ߖW6oō;,$<&3g7Xi̦Kߚ! "5l%4Q{,747N͕58 UO>5z0yu+Wfe *Xe尾*6r 56pecI.@05 \8v^3賏q9&D@]C,`jP]aõ %pu\FUMM nH + 4A (gڐLZ1 L2&iEc% ZkW4AWbD:&5$aZz%aF0BKasכ37axǟ~r,MGi ҷMO4M%h/MtkS54d&@WI&xe7l9 #F"P 5'PKJh&Ί88., @"چ'LWp˫6? ` 2Yq#S*TRQ$і8meflQ%[*pIPwIg7F N@W{/)^|ϽI某O/T{|'OheM4M h@? @$}D㬯o%k@j\ ,s/JV::n_hiΦz%ӊ&hDV&XQ@Cw#Ka X $,o:}Qsy%4+ꮢ$ 2Jf@M#J@PMoB W%(D*$4K@dJ%$yq*NbJK`K$ a"T0 W0(A;?Br@RO;qzt=߭7qQ8޿Q1elT@FJa|5DL- ts.wL 7't^tMp9555 $5.H3|P\ד Y5\P`/И /\r _ _KZJԺzHA4Օ1|UE̵8 .`kll jyMkkf@/m+r1 ^Q@_>VXSȗԹd @ҥTij"JYZBJJc%KK l2<`Xz2PR Ov~(2Ƣ#<~2~?KPJbRAʀ\-`A8 C%V€߲NvVV/o,+Q@SBmlރ'ZnB>€ a^+ O% ,c:0`0Ueuyˈʊj2g C[mc1XC?JFjY4&c21ME &k&?kf@ k%BCeSE#ku54X kh_VoE X5V 55{eW?9~#u @m>B.A%"g̅}iKNjOQD-A!a,^RT\T@&l`7ؽ7;v# .BCESq{ER*wt1W\ hfcw-,pi5 a`n޴顺^<;Z]Q;ˡ)@l67krګWq|v2pKemUppg](\@+@* 0p*,U VH[ p* .Ps.PUSݟb fߧA*5 /\u.`JU2\&@? H %x(^Qj5xSMuE}EV^xjxexTY Z:83uxM ^ # (syW_?~o?Z1ab<7q1bW4B~L^p>oa(Go4bO#$RQ ǂn-`ZV;ߌ0h \F jh}Z׍9-`p9+_A Thtsme]IhZV/kz5h'H@uUu-pGllSC;^Y] r[1 kX ,s?諮 Xo ;o|j>wޜBdy藧6ay/Kwdd\lэIs iNp@vg&Ec="@ IdsncӅ, z2Y@΅  , B*6nX~ݏVݾ{׮_82La90zޏj<?yl#HqW'`. Ox0*:v-OV^  @ ^]zn O@4h<}'`ڤy5uCyf'Pr*OճpL<M<UtkopJ~VᜑٜgfϞCL3eesBfHt&dƬk o ̟I|aA9 Ml,!؂xW[n޳fM/p׮~ ZǪAP#Ҝ~? TbXMk8my'}pحp sUUVVWU|g5ThUy-pp$Ym\8;ˌLh@:8e8+UeqUFh8u8%Qelr FqN% 8 x8K,35G M{A,@ 0k)Aeg0@Mrf#32mhbP$' ;s )LƃpTl, a8 `f, ŒT֐SkwF~5k5'3o bkP(9?aE{ So@L U@CDu"Dc Q D@A [CؘUNRgndeNUXmr:]6(C ?7=?Z.rBoMz*OC8tytL_~#dJ (Ntt'|vw 0[܉\3 ?^➑{2.Mêsoyɽ^rͱs&wfKTZkOm^RDX((-CiUUQa7Ld‚}g7|3aw}`Ql._A~!̦i61-`0i,I h#- J pi~1-w]$-AB9i RZnEZ?`i1iq1-giS cC>H p!i IXaa1, &IX@(X.bX@) 0nrڪ`iX h=6P~xԇ"a7\YgnP:, 1F$ fo/}'۠tpPD`lk8A~IUYimH l6d?PQ̮=N>R֑1423 $-@ퟠ>&'zMue.l)>.m |;wdђK/gؠ@ޠfOHXyٲRWL Ҁ0_@@ݐ<`]I.fh`#-pҀH,,յ!Pрswș( #Y1}>5.p"#K*3=rF?TH^1#  ,Jg\3Y>1# Jg$e#aޱi歏ui!HҌE"t, z (aѭV -.cC<@aU&`D9cOιO03UU$@=SXDJ0u3GwK,oK.u(|"=.K. 6(7(dq[RV;k.I]b,Gbz 8_P33cL`Xʱ~59z_dB,]#Lc,@>Rs#pXG}XN *&Hv4kprē{>?\ñn_jX%of*u:;P-o3SQl,TV$nڼpre28ޠ2Z~)+%kkO0'uq-ؿګ\xũe]x+q.^_dyt;Md:c?` ٳV`P7tiNs!|D2>>` L#96)" f"rD1C0hB@ȮHфI0ĀW$ msh@@cG䀀!O˓0]۟*UoMWMG>F єBUB$ E$To4`jLJZ XXr r:=fG Z#4_37]yV=oAba~щXk ΟFy4bY)_ct(9 0pa; @DPBF1#4ClP `̄p  = yH@ (<0#\h P@82 P E(@`Pl PgΤ\8T4:- t$XZ.gZx\k'˒εTvL `[T5\?_*jʆ c}M(q!? aXLQZQ׬Hb6 \9(+@1cFUIꃖ1DkZJv9/W5w,m׼H?Ճs {pt1 ˭ #lGM5Ŗ*/05$ >Jא 0$Mrr|It9O؋HXP 0 ׮:P`@ p\C *{})\{ DO=Yځ5aJ!!%0/,S`ᜄKdT&\y VVb҉_s6sI/]zq~݁ۺu$; Ӫ-֎TmIb, wǑk8H~z (/ l$b{f3\;w~*q`13Ye/9  kO%MJ_v s =? V/ߏ;ٿn0}wF*JO~j 셢d!uEbDS\$<࿄|OOtZKk?_s9ժkiqU08/hv5ҸjPkF V ȚRkSus&J&G@> fXN%sASCio?WsZ̔v TzXB#7z7rZ?ݕCqq.IԿ#咩 ԟuY(>SXXH`Dj ȪDA/RF kAA(|? *54|yw1_F$,4dx1u6˛cEo(kq犮mɸ~+u9,quuδwJ,9=,ٜJysiMYo T~#T9,W7 CTj̅h/\!Q~q|0 Qp'?O ipM תtZI,q $pҒ_H8-~MyѽC; F[^C H DMM%x !=\N_mEb`yv㈛K i⟐IfiqWc?$Fmoű 6_'zc&[$%wফvAH%w(ϒp&/g7D{ .8ud_H_Gi bu.*AT Onp@58;c|%IF[Y#@>ϥlRtKB_w1'q3$Y%*uzOX~xm,y%P 52g7}X_!8xN D ߦZJb;CkMP_J)P~É ]N<~b_@R~\昝9(R~g]%W†"ܞ"e%B^]-!"7į% ^_7)?7 +MNBP 2o<&'?OO ±J5V^KiW#jxZD3Aï&oss!qHS 1!^ X+|}tvČT\=#*|S1/2־ۧG{//אCGx~)~,d(~Hoox7{|ri;0\>i:M_4ş6#!i) ;cKUc>li/ݣ~d*qxVkxṃGп`a)Ÿ"|WG A)1}QZ'#D.FiP3ZF [m}~[:t_t`^\ rz$/vd0c;[3~u^ggMֻ㝝ݒnD?mw⒭wv#myl=rd+Dm}G{!2gS[om#3ܚ΁jm8$eRӟm;{]R^7UM2Ij'I cMXYǶUe"zZa;ݴelD/<=/887_c#5kyg_?a\B.|'xGB]EDN@B0O?ęW-ʊCT> C(zr"J},աn(@ߣ^D"ߝIA{DF_iA?vo"+G o 9~>a5PF?2!+\ݝts}ϼ~cA?G߳ ɧLF;JΛS0 I}&v\lƳ9 @#_豶g.wէ}8?G~egJO_S~ߋG q{^=t\w xkWX}:A/V7ŁZdDXZ "Q_d456C LC:wme5uA'vS7tubrpr~jUz^UO|_u].YE-j |w_Mn~'ڻ}掸~~6qZ0~z.jz-6"t^CVaWWVcU d t>ttfk +/鼫?wIt؛_}IA8ĺի7}:ӯ~tԫ«|ı'})uJv ] 88ߚ8'Dz99p>աΏ͏C0q3p9Sryl_Tr~*s|gk'<>\9o{"#ݝmaNmmR9?~;e9z?&YϴH9WfJ6̚=8/5[;k6Ɠ JA"~әʊҊJf4,L|h6;V;ηʶOK?=/fo7o|򍝇^9Rl^^.xm[myɗd4UxSx}ϷUs~MloRsJQ*x:v)C,|wXN[,',XY?bL1?(0?p7%/7<f%D<ϵv5wtIvOVK'[%XiNZQѦ2"iQ?0?I[/17-9f)ϓY}/%yCo/hno::0XQ됊ISSuդ*LLbyV}S6b83n0kIZ 3yvZ}QjMFrOX ^~嵃vo['{m'S'8x‹E<; F%?g6f@UgsM<~},?~#R{>~xoF+ȼ~ʟ J@%onl] Y>$r F ['M8ᫌǖ)0[=ݺ I~V E]?9F>&L;ձL)1`Ѿ[ӪƍKW\Γ1@XKKa)Cw*v<*1jK(sY]i|ѩΫK>[wOC<=# '-z< 4&`HT3RqVjO|Gw9+obNculo(l>.w{N׽|~}xV~G4߰?NѰ0# /XdyElAc Ϝ:^Ѥ˙R_ŏQ0491pVO4|j돡wqQ\(Y.4/O!ySN)31}~645#Oww -;Xrwѓ{?6?3[1%JL3c7[~OɇL7,O S2fG?*1yzZGߏG"O5G?(;$&=g|Tb7i&[ [ y|TgWG(D?L|(]Q2y* Ǝ4\5ȑQQ0AFP>JD' GL[yqr>Xr>Q?nqݹk={2E;?GwlܑإX;?|rw~\ y#yi*vY;/#@v;{<(A@SfOED8F ~ܩ^ѝd6%;|O^ d=y}}sGGuaX&nחaȧ E2JbUp:c&6LLzL)ȼ*OL`odL>@dSy?oGcou?ccܛL(y 'gWm|lF-A&Z :;1>,Uʇq&M 1ͳ$#icK=Ey[F{ȻK!1p| FNw޻QaM9_c4ԩFM";28~Ɩ6@&ʍYlsA/8<__?VMƷP 8H7um(ޯY~r=~ r~uki? 8ޞ'>~9 N(6,U* )-ee8XR |y*%oz2:Jᨦ.U3C0 dk<~R[ٶ9h/7b-k~y Uy^ Ǘxn%0ޟ X ȿ`6sɱگҋoYX|*\+H$>Jxo/aXME<!d ?h*_=:+W[HrCCG^j#6n-W *g Gl xQ6oZP ?CzW5 uQas{ OC,T<_ϪZשT|W/:Ԏ SүF.k—HP"i4ZCag{KTC1uXKQȹ8Ti߯R'p}6y :I?E5éE~}]wO>w}ת7.ziU{Y~WIGsIț ]BDBd?[coHqyb8rrs-R[|܉μB^/ !ճkLF̆o nFQw!}YOϺ,yȼC"9f׷ȼ֕riϘ82ʘe15?4AVt= =KCtvT׳#񴷳*:dWDQln5(5it&N& yPF6OLjjΎ9 s%R~S\ )#c9n>C[)nӴTńDH@0 )FB F#!HHh$`4R0 )FB F#!HHh$`4R0 )FB F#!HHh$`4R0 )FB F#!HHh$`4R0 )FB F#!HHh$70x-:xOA0|GW8L4+ #/@u5<xn9ByzG#ln<\lLa(9~H(!'$b% x)KW-aԣ . vnEoמFM3O%3JU2n6f$=s5U"W srhi89=ȡ尖2Y>#^j:-XֳYeJqt-7JSeUVp䴄T LКrjV+򎬋wtJq\*YcH(>pYS%삦\^檪:\LWNd8tUIiMLp\L'(^T>((W!+Ӻ0 &VV8BJ%w1O)I%i܏2$Y-4 qlf҇4WJ@e'd_QӏD9xDQOKsxl#(1 H0qѩY0Y%cQFaQF`4(cL.D¥xc hUaUchF 5b4iĨ ݂]->Q)^ hF11m 2'bDQDc3? )v\X:W V ~n7Yoqvfi0K'վ>s7|lWzW7miPᘪknպzNa i¡wHx e? ~M_gD`"Љ%;G ̗JA -0fFveVmR,x}wl3KǏ޽k{?]}Mgoy󕷹o)huʇ\)>ܕ>>k:nxf?*Oɮ1+Wf>m[#?zgݿ>cզ>o]0W:=gx:}?8 jWmak7ם:+|l/{+]/n|yt׿+s2Z~y^K~zl.OVtxϊ+==jWpݷ߽'/{mٱOM=\ƤWBޛt풥˶|\į̾dĴ8nћu[G.l?tx{͇2wu=t%>8iiͯo\>v2v?<s>4yK.Z=+޳9ٿ2(oo~}y;ri-`fY^p ^w%Ҭʟn[fG=;rs㮥q/4]׬^seQwv{-<8i}m{eݪL?aC/ںYc />nr\ntG?r9}&=~}ɣ^uHym_y_ܳg3yߩ7:+:{ݤgt0_ٷ0q}0ԷC}=qƻ;;ˏw9[sK~c㒍Ϭs욺N:3g̍>܁7\;?}_9{S&}fa/K~mvc.n}'-ܶvðt>` ݬ-L_;X>LQ#{5,4)]RRBΦRn$MH:HW $%}T Q2oJx8nE¥DW>]e:8U8 .%4R\g*")mt :@c\'W躻KF~T3 6Q֛:t0(tڐ ^2YÆUYr9H1V ~/)r %؋H8u@LE*!^2; ^*} uQ.2YF$W( <|V9Ĭ>\'eEts-`6hF+Jc2.@LrXCA9gu98"/u+L*-(/-[B1nUTVP&[pٞ<o N ~ȣJqZ$ 鏆@]IᴖOudr^mŹҵZKL{hdw%A=1Î WTFg،1J2o?}:#Z~'`%AjHOMҨ]z!5Ng}J)߁,לZ8{$rd[ZRIG.7 "{C+~MeV^ʫ Az yM46cҌAO8-K!os`õZBg zjyec%pB~z"?r|=[N*p-qxpͫbQraƭ,дS O` +|T`RrO,*aTH4 J^9Zyվej_ղP5gb{7/ФUKЂHB@䀻tܛ?7]7e呶~$46+B2I,YϻٰKDR @(W /c35OG[6e< J/ī@X=Uz J /mi@}wyA2B<<nc袀" A E΋Rz/Q P.%B.Rۉ,E-~k% J^/|>z!,HPI{Kh]zE|!+KVKz-v{|G?Z4dʕSUז[#6; ~JTuVB+Oct)xyEE}g&pOoMXҰ+iN *BfgSh#kVKe.}_YbڙZhPAtYʥڵ`U1ʧ_@ծ H Q٤ oM(>9TVxrYC -,\' :ȃHeh8yQ"^[fzu ݫG,}ЎT=evVlY(L{(mXG@fGLGG=$=jltUmlSZ۟;D|.]_Dmmo!/c\ajOa&aQ-7l7l,?mSj=ƒgP\'w܇&"ueHgAD=h~rL=J#b7`J*d+6V~a+h+f+QПʥV~'_Q^Fr<iɡٞlɆcɆ|T Ű ;ٰ ;9˰A;okx+Mqn`NyHyFd<(Co3 DBk'z(#P"P6~IKAđR~(XnDDbDAD-dVKG2b2{EFc^&M~Q;H'4TiNF K>@T0U7k}W3|;ryoH m!ȗK}ѡAP aoT|Q-XQ^F/0(l~ÏrT ~ÏbQ ?1G Q^{/xՂs%?UJFD G:ȃ2$?""@<eݑ}$eEv(^JGj#ȃ2E? ADDqD?4?"PeҀ"HE? ADD!Qf+@DxD/ƴ""z(%/G#b;;CbL9xyQ:ȃ2bxN%VR_@)L M2. 5AdBBGhVL}:Ӓf6i hE1BK{-GJΜqdJf"G'A;}s\ Ges+~L-*+S(^57LLy܂=8rDq<<"nz(Xg!P͈ol%*i#>(ٰ"*ȮLfy@ZiV2 !N{c"uq`P??@А"7l`1`>ͻt{OO`K3AeV3>3ҟl P/d P2BGP 2B_( ~+_C>3Bſw&Ty\Ɣq\m*FƾQ=G\[cӣD=ws=$LJ? 4 SZT)آeoV%>s%Jzh)-c=X9Q 4GSZT)ȭ#`Lt&sE rQ5R[K_8S%y^b͉[e(g}|DOEcOVI~?q(Slѣ”U r=oףLy=`J*ض'U ؅(yR'T<)bLE T^1 J"P SZT)آ֛8RGyv=GQҢJA uy @aWoN@l3b@X#= "]ڀ+6#DХPzHPz,t?CKLm8X1OҽH*y];\C=oyr'<2wW2ߌb7hoF3[AmuWL/HCΎsxՒhHzhHP.g'sߵkcSyƞ>&5t8:PОRϡRnCl*7)Wf:&LV*(kx XeuRQ1&n~ZHMYrfç^}<϶cڃjT_ר=}5J~g\ bYo͎b)S1}ҡc;a{ >6'/7u<Ћ1}<8.&PjJ̆q q GC72m@Ā6-@ Kdʣqխm$'{䕄tJ#IhAOd+=ZR{WXђT\8*R t Cx6P8KOkF|C^XC4dpuW?h}7 GKA FhF0 j/gf(u|Qi{:eeϠ`&q*ᦥArG!{F7Q,{tkf7i#l"0'~ĕHjOׁI}߃hɐt2IĮz=$كhɐt\Ү--IXAd /yR](K= Yygcez` Z2hEhI-iYJ YzP=J估{-"I`Id%CV#I=8$z`Iђ!+${l"K= z&f5%^?Gt<Ko$u]Т5,vt->PN|.${-h#$كhɠw.uѣΫRcR>Q|D 9<&qbĀТ, "Ŧ3uoƜ8|Ӄh"neYf84W;waЬ7jιGiWkqQyX.G}%sY7T(w̡ [6ѯ~,ik_hF6uѯAz:ױCFtZ<ӡ~^8u4T4XZT1)ɟӠ'R#&zߋtǑG=~ډ9AWwmY_oiF [V3_k4PjW5ڸk{:{l7VR,795j;$$'[\bI_Kaw3sܒ cS{@ҺM\kE/fߦ4ݸ.ʃMNC67> @ͦ4f`9\\ Wx!{P'Gة_x X+; x s@PUJr=+Anbf7`Igìf)3w~foaSy|f'ϑ1FRyzż\RynUGY-<9"I<'I}9YPZ~bWR|_@sS*)RztX oZI˿?+3Oo^30~x 6_0^y1jv_oݳ@]ڸkCE PPESy"1n:'zeJjOܛ}=a :9:,7fIc${-d`I!T{! ia˟glRIMf41J,ڀ9.ĀEm)O&@RTAOj-VBc2d^W!#\-F WW4@e4Ph hF5h@A :j1Ao[-FM=.zWҚ?& W2Lu赬bIk,ӵ{ϔ< 6K 6 <(X8hXͦ4?Mr87FP/emjOJZj,T*y-lyIك=&I/G`dsӚaa(0҉BoH%h{$y='7я^/-ƛVm$n1&%y=u7WA)zdPҪ dȹAzd%jS6fRUZ=g$/ %gntN3;qI}ƖCc$Ջ $-edP#l)ų뤋;ϧ)(Pt6 b@6-h9@1DZ_իt?Ë뇮wEǮZ+@գÏ^<*zԻ~e))w+HZ>M@4J*]{>iNyEU+9ֳLJ۳) %(]Di7Բԋۖ|^3=^`OVqhͱ訿z Oߵ;i#sMG(9&OQYnqKJA%}zr#V#oroٹŇQ[>/I:I:o%E'SkZuӋժZ@to #]J^TVgHEji+rIB+rPv#D}O_T|I9baIB׳u.Nc Kk4F*~[^Ow42W_ks|셒vz-kVk~QS-UQ\Py'@#}2ė>%_xhv[Wom4/+I-*vQUuqƸ`9WVbſ tRhW޶ԃDw͡cՒ.ԍ(=*+ze5R-ZvAzUhPZS_yAOu3*|`z{~0$?יTxJDVl≬x뺿xS=iBoX O 7/(TF0ʜߑuReRQV[Ԡ*0W mg}PRti9y^b C3BʭBa@N((_rM/TYQUSo7R _(E;>9=s[oqZ{rn]:տL[mz <=jέ{`_VRV1}nݓTs՟-ꞹ̿^<Ë۬9 A}ڀm~_5 1 r{1'=P }(ϥ*] /J"R^TXKUz<1qN,wtILJ#\د^P ]}͹aZ^=/$oP /rYJ]X5UU|@՞Jkk32%9a~gE'k #x)̈Od|ᅔڽRϧz+kc^8 P:?Vݞp=UmF`CxR`( ^&/`T('^ۮE^/;kQMw^V4{F(F̹9{AI[&mll掸 ʃ6;9DPyF,@1 r#^6'lߡ5rxKUK)-cn}ٶruU3m5;7m~[G{γR[P1ϿvjL[D;miSyn9o΃6wGl'70w4^n1#.i{p޺(1ʶ(fm~żؿ'ǑzezFu<盉<߼|}K<:0|G3޳yufoMF绻^ks|셒vz-kVk~QS3eX3wȰw&QQ~;$QcJ!xJKR !1H96I. HPR bETp%  g)<@v֝α;6;=mA qf'o:e{bVG'k}*A>w3fPE-mPцs|m4| [nKT._nQkt[n(6ϑ=8eZWsf|4>=d}17򯯖Klof11AK1|ߗu:,Ll/|#^)BvV%ŮCfbPYͧjpK:r"9'|۠ 6dRTl6b{gsP xW}]83ya8Y/uwN@3-u+A挫/6w鲫myAˮ둏5GV'puz\.Ae5ssP *aޅmPj;Q*`3ռT+~G#55ϩ;mT]e6i]%34_oRy-3J{],c|ͭrzicݩlC}1Y{+g 5euU_]- bvX ƻc!G_W򐦾Z۽56dSeZC1֕5F uWkNLi - 1Dkh h &\Cg5Z {ZCk5#.5| -'h} &b\ѽ] cE1Wx*AE6~=mpO̝njc*V;Sf]ֈuV!<fݤ}kEX<fN[Xִl&M'ִ|PL]NO:WB ϱ5,D$ִR7wFgن|A{U31f0eYI]r ARW|2h]K_I\dma'.2\~"B\$qޝ1훩͗9b*8PT!+(6*g_TYEGU9*|ƯhWVV3/s _VF_QYm<@wFc:1s]V8款{< DDyoxG: Keه;.peM`$1z&oD41H#iVXDI4#FI:HdjJŦ:+b$ 1z/1:?#F\c$m7O:GQ51kM*j>&&9kas2zEoe5w%)\&j֨hͣ[yzcКJ|G,y{bbǿv / iLpS[#OQSMˇzfD1cShU#C؀y*КmO;Qkz63oP#'rϾo,zLߝ9.PnW4@vPEUOճ*\K_Ifa} F3BhׅM+26G>ukq)jT_YE]%IE g,U|ٿJV>X`kY|mOtGvHb!%xN>H ]ƺsJp|S>rVtV/ɍ x%PǾD\$稂N5 h$NXxy H,-& hNX@b)0O,E,`ψ$PcޢmU\kTԔ~Z^,`i IwŇ[CM_W>?W$'տP_gUjW](V׿R_'_2AZZK;ROpR;Rߑ۶m~pEmvv?W)ݻֻZ)o}>ʇ?yʿ3p}#Oϧ䃟{'>?*U~ܟΫ?iynϳ\/yΨΨΨΨΨΨΨΨΨΨΨΨX6)#|_>gQQQQQQQQQQQQQ|QF/_w]+:::::::::::::c v-oުzުzުoe X}a Wz?[շkxoZ}yOO[7kxOO[kxo\}߸Wo_=?o_[kxol-ޟ'o[h-5?\}p V?o[{kxמּsW^շ~s-5VǮϯkx叮[kxޯ忶o[+kx/忴o/[ oϯ[soCϬ?y?wz5o5f T3jR@H5f 9F5f V3jR@H5f T35Ǩf ˼GܗKd=m_)WVl ֭^.#chc>Dd"rlplCO%bĻLdʉl8VcDr7}W^rxatlFt덠_al"#cw1]cPC6OcS3`}zs`4Xn(g҂ @<~ %Zi,,EZ `Tro围G}SM>=7g֟N,-r~Ъ_?~,dԗKޠwz߀׻t{ʻuޭ} {^bx}L`nC>>nq]Rx?p}IZz ~'#^|}65;TW-_[\}t(<̈́̏nx:? Z.ˤ^,w*r[d;aM([8^G].* }uf )K G!QY򯛺.d@f'&'=ҤLNLLNd$7sL&>2n&71`A 8.g I*3cv<3fL X|D vlˑ#aWA9hrĎf=bFxt=Nx#h20:2:(hD v4eԌ`dxd$OG̰ddpzbpFm!Ç2Cpv  > r8hgbGz=7Vكawqfzz{{zMOm/= |WT=f$*5=wh{Dw۽nw-nw; nevs]N#);"7 t$`1.BO#Q -7I0: X N@˨0 @,f8֠cPF̐ Caáa9 Y7!|rPCDfU!p N;<Jd/vbH)?,Pօ};"\I C H;"řO1f@^o}[@)BҘcc,Iz-U+5 DHb*zL4jlO5p EbF#Jx#ɨ`xQ%CPRSFd12Pp/@˸LRs␥A0"@PP2xˡe u! Rb_x{G\  tD\ҟd 8 fHX%6{9'&* F 5=% LDc#ۓK#C*I;bI-]uzLC] s~$!Ji0ޑt& RB4@M A㈅HGah]01m!cRlmB mK3bHqG6aC0VDb-T|4Ef f`x@bH9͕5aDG` U?B0K=nTaFE#+S>_.|N^䜼)򑸱 8Ji 5cwu]n\Z1Ys.9s+*W W Wթ ЩUsZB6e8^-lTRr5,Dtŗ&W٬Yr^[.KIb)uW,1?Ԍp^џenh*qxa8ON)5`bM*C/92U\WN 9' `I*8 3D{N)ZifX<";9PđO .G$ `]NA%F} >LG SN0ORK =ͧ +MP n %Mp_=/@za9 h+턃ۗkKA޽yNIGYl/(&|БliF{DZ^֥ TV'2Szv[ygwgJ\g)tRHnE79d"AȬ+4SWI{R@GI|{ 9{ҚQ"P_BhH+al*t_M& -Yx}P^9V0 e*ׄsQ{DTVRr\) O$6Y} ,mT% +`ߊyw{\}EsA5/4攚.#̼RJvқdf6$Y&sJ0kAnQQ",1ܘ*Q "MGʼR2m%xcQ&M-׈aTɤ$43d5mĀq%5,GR6Ѡ%MAg %!S42kb 2ss\J\#A vP(9"È٥rh"JVGd7!%`<1TdWDDYF=::۪:^Q؎Vv> )Q@K0VHHS&'U[rifQoXJHdE#` q{Kd|k'RT۱svM&2)1F3JejpĎBIv&JH >TH,6U^G{BU9껃4ixGT@[%4Zx&.>*m%jД*l\4VJKwms΍g4 E*Jr g. Oh|li "wbٍ|ŗM@HFF嚋:+>r<3WJl΢x:(7XP ؔbmQ1L DaNl |pfYS#?/Txƥ1Uجl #WފGIooj]{:*匆#>,Hh%Z mz:JC]#X S@Sғr! qfny,*!K+ T~" ‘Z! ,B(,G \:R e9Dъ$8F N! +pD;֡i!BSBmIV; Pru7GGuHg8gKfrn0]vg2;K s(, JRjKKnJoGl$r nvZnIv) 1.;::e d\`5sr%C(İ[֒i!Ǖc1㍪X%*xICu x5?V0HȺr_+$r,tuVyW]Cs8sבMҲ^.VzmWJO+ט{Mיj;@PmTvo;䶊>Qn'WʵrWݮK6GNdWxe7zp3 UB8ǨPHڠ}řY\LID22)UAZ E<# `G4Aaڄ6hZY*!d&tJM"Zk xa4_rx`Pٌĕ4{Zzʸ.hR=R֣6;'SxtD5+P#:tWcj-AIFbhɦ(sjRfEWjS1)fXM){xV8ȅJQ?p5q9`oP3P6ʝѰ( #a֫Lo!u 725K^ -9& e~gŶD*FE\Q2`F.Ns`CQ@ܭ*C8Owl8dCJșEĝ]@,q"N ƚG[L){Ӷ¸[<{I2r~nwⴕ%X{;x캻?g׈y{owe|_zh =Kkå ?\^^ol6h?c+"^WAǭ\ȏsR ͏oɫ ^O(l|2>xwsh^?ϝ;wp3\[|^CkxƑ'7-pƣob Q9KOr4KN+r&i;aE*M!1'wR >xx6/zrl+gY.(+Pp_Z@dn9BDQKF̯ GLciIӫ޾{>Aeѐ8)rO}"o yFdbhoO!: zAVIdqUu\՚y*<՞ڴ}II"I etj!>P-3ѭ{-@"KVSWH+]Az鶐% ukҗQwu2](B'* cҕ Kd3]$zdgܵk'T$릝vgvN!Z!=)mG9C3PJrem߱r@=qZL&(tn_-I(nZ!WűFuH8NDr=*R1%qtj*6 YPfh4F.-^8֨` $vnV[J7!<>jч7' H%s1_ƨ m׌ ͮ%%BN4SЉ4/:4q^B]X&Mj-Ԗ=͡u[1bm:nrgd>ziH;rka3šY5.Ҋݠsr{+/^-0Vimz̽\g;ʵxW4e)JSV ^ID]bx` AT;hU7YiSOhXP= *DZqvDĎ.ڈV/E v=ca{*F3E@a=C bҏnc"՝Q[܁,Wn D3@k  =$^ AI/ PPؗ:"OQV/X E'2i5hDF?W"^^"aq8:,!חB>ázXp"зG7!&(ً+8)7lT Oӡ,/7,MT3(teBG{">eAnùCwm.6Ym7F3_atuo@ȡY#RPەM*4TAD88C~@Qp;ϓ&pa!urCEzzpP#Ѻ~H\s8I @ܙuj⅔$#Vm;@g£++up(@)N:ANc٢ 1-KP6 XAd#fڇe Dd.lftD:ႶU{JˣB Ab`oQtCkHz%6b+U#PF9  7E !"mElhÇ/LakTO7}G;qM6K"F^z[){}٧$w8r>Ң Rr\gۺaIhr̼ %!USX(_Qԉ;0JJVbBڛamQ`:gq}R2I>į;KjD.+wo2:V;=~ \e/I?nT$Q>(-NƁ# d$ڇUX,4D9eВ-!)Еx;6YphK}H/kBlZM؏!"^Xtyu)rj"ej+,M  W)dtsZFVJT+I0! סq5G +Lb&h/ XAt,zꓐbX,C${9+{:x )*&z `D6iI -c]' LKJjhGtR%]K qwd 0F]3:,bdЅ`f|_Eτx7AH8"B ("B p]GFD +|MQEтniW[cv@hPeV\mycGWVx= bܲ$bRLhd8gw;CeH9`jƉ:F<2|S %9( Y7Ŏh 1C*"#˶%RDž# NEdSl[-V˶ղg֗K]'jb .v0ɵ\5!md-n;rL;3CEQJ8 m=~5CO?U.WuxK"* 7pY^%!@yg[`X9"Vo Ή}ˢofD77p< EY`npV=%\Wô$LatF&LͶ%{O,g+uՃS^z%hw6sx'*j/25eAYyүַr47+f:dD0kd݉kL%6b2/Z[T4]'GfJKB|~/R71)L<{eˈ]9C5^Sn˃hgA䱦H1i\r]NrPżZkjߍiw1i3cfG#i¤4W.4<읳9kKx|/H#~/aCnI`  v5\]1o+^4R9Lz_eoͦ#m̋Np^< 饳A/ҭ}ai6ڗ_# 'FfE^΋r1\ ' afYs;'y?A @|+wy/D^l.|;Sz72'`OC=E D˾BxfS6隁ǽtjG6=dBKIVAGJn r'̸|(m,Mrw]ZtoifXŗ{?C;G(RvEˏܲx 8y.F*S(i$8k8LۋLWnI=\:QuLJzcwioe3Rt7"SCipBΏê#}]HێiIplٮ'YrV#c E*);jq2vv4=CCPt)(gHa<i>]o{P${RV acI,Ce7.舽Zw Ӟ"WT=m5dbcPWP.R}tjIawK4ti:r@43C3*C#l%b!TU >4oSE@ryl(R/ GsxHW`Ԕ˄FO»f$XfAaȊ!oJ\/tME\V^UNOoQaX,*j- "0~zYwg P)\]poi;ދfE})Wcº ʆoĥ QZt7(s F=I«"xzwgꌖg{sn\YHj1nݴF7ɺ݉[jƢB7O E1c 6qLNO"6w@+l*r$_˺F#F6i2zsktU>8Wr@@,(>.e.6%z ۥE@J#ƉFZLwC#ћu6#::]RDsV3蚏vPQ{ԒZԬIG6%)f"_: k˺HiAH-у ,F˄ B|0x0a-uaiG-FXjqԕۍZgCeR;JFOD+w"_w]Eچ$JN$n=aBu$- vt`rlI%'ñIq]Y5[kY\ii{6ƶ#xGF1]1u* c3.U!Ɯ笂R`9syv&cwϨ:-ruBZ:h3̇Ia+3uFF4AeDD ZC 'D;fHo2w-ʵP)n0]gVڗm V_[X Nd6;)QTT1h̢HX[i5r˪)HPd%ԠHvujGuEBPIF[b6+c (Xt heJkH ]i1+3Ӄ m4bgRJnHm7((hrFkHHuffvlhU]3~,6##".hP1tu]͵tЂ^%KD:(BM5E4*+E!CcGlh,qA5ژdYHThQ;JpjE\{*pw1uD.FX]9*̢ gh\^(xrRl1 1ڧdQi4+JK1TbH :WMd׳ 5zi1сwsI~+(ZL ov*5ER9bȃZAt7,ņJ3w׾,] EdI,溴gyWAqꁮ #eĴa]+"ݨ(e "A9v\*܍٠--WN\z@ۇq!Zmm6:.\3B!;u~S0zM]i2hc(D·)P`ak}Pw`d-23L7BK!żkS)ڄż5T_1nf@z3B0vHDg1a< {꠻ZP7X<#ŠrPcd2n @[ݍ!'&aB 6岡CWe61_+p[uuzRoN {\!K+w7QKi6EH3 [w?!`3*sq֭-*DD[]7PdQGZ WomYF%yi݇2 (MVn,iʦ-z;nNO{^Gjs.FtJMƉjMF'lځFdNdy`dS@6*{T\[> {|/EM {qvzܽK6}r hAv"*| S<Axq]MG |cmDvDՔoŐ@{?R\2aBVOBw(e(> O@iכ+1ȈA ||'h6 ሉGM1-jS\신} eb:;ݻ*N Zqj_ N P@k$7*EaA&TV<"dﺂZh8s9K,Q@?2XeR e2SᇹʜsHa榓۵V{9Gu}靻# 4&Ҝ=`PLAN&ץbRF]#e(5@Mx_B:A4eh`ݓ]'pOȉ)jGŔɡ5D,tuQZ$@63%RS%ա1E=Wi QB{}U@ DeOA(@"hjH"0>H?tD11Vy)VTZQnmXQYE<gI@]S2)5U E} m#qJSZ0SLh  IBԕN@#2N Ȕ>o@ &pxT MwX\J qpCI1;Z!\c~DhB\%}& 13͜wF3ّ7vae׃u7!T_HڭpȦ1kI6ЦnUn)u0g*;uTjtI6 8 BS= E) m]*p-:YNrÜetuі x<۳J ϶rtWB,R!CT=8hJGqn\!=@[1Ci9=TSe2̺#6ӨWAhȏH)!wr#5HX: Ǩ`0H]lqg:(5rݲ_WW{rr]Rt%%n`T4cd&% h'ziPwUԻ{ɗ@)wz, cj<Etj\K;8mLY#u( i" է~ՖYڨp{Vg.)V\h&R'RPh%o7&A^iʌluð\Ҕr Wn zz?Hnnw7T V̬vƛjfyv馽x+Hgqל!yk3c+:V#xrNcZ&>k1L,b_/,Cnx}E幕: IBS+hQZEN&xI'v^_v"b1!^\= A][]V3ڌUJ9k[2 熣|muّxe ar`< h&*m=E@L̅~XF%$iE܄2wnMRZe=  9OXt-5)# s" K#px=U*2,C&^N^N f;7~=w.@1ʮM,HJ.mc`x6wyolV-( 6ڨWpS|y:ÖP6waC|[B'jun[bҭ|OεZg@ބs-V6[Biwp|v-;0J8pQ ZIN^[RqCBe1U=dNxkx7]"fW xIlmTʺ]Ϸn^T~kj[s|9]hy0fӔKԿ„x:6|[ulbQN {Pؔ/1n%,wqQ!z DpOJrA"o|5ɈRoܘVI_7_~RץdjE :Fw Ѝn+SO*zRo0kafuҍOSi7ħ ټ̣';@ɴ+d,,JfuPhcYyY Y]4d(I+ɲO>Q;51Xʪp 7+0v8OWRL,pkeb}h%G\/=6 ӦO[2S[33$)*(<Una0a3s-hWv3>9d5tts*!P2׺>9>Wꁍ>#v7̰jTxb&MT Ѓ&e]p7C| BZȤ"4Qr T1:ST۠l*QTur,R uRTB[9T Ӥ>taLǨt;^􏲊˫7Ȁ%rFѡ.Qq.99%&6jπyE\W/-I 4tmU4z` ;rc[mӥ{@y#ЯɅYX3)3)8]0ĝ)1EA~G>[Tp4qCb*\v4ؒp I]_Cj);aQ:f4SY7uM7;9|ǚRVYr}y(~M#/# J#u2j`RtL<]Msw<(;gN}׮Uz;p-Fz 6p CR]ϧmJzXC%?Qtjn@p<*36 n@,!tDe@rl5ȵrK-mgn!t1JԵ TQݱ EXT*HԒ8 PcszwJH--ChGЮu.Tz}rG F PW~nf߾-wpSxpMT+>ja7 0j,Rm99$!T[KMC.`hu? ##.69uB)0޿P}Qmoa/rm+طq-#ݟuO l*~7Wv\kQ5#W7/Zw6ďB%eumsNLa;b-J"v[TY:Zd6% ")F5Ց<V]Vp5Uj:Ihde%LQ?mR3r{ ܸ3DLYXmK@`{YEa|#I <@s9kW@0-@7i x@Ё0nQXEreϟ3ZB\@Q.(O@Ţ kd(>J%(U^|_ %~Hv|.U3o|ox`<.x >}{0;`p^v"k¶9j :ĴʪWY/C0cz<=>n ^J.0{o(k!Φ5!( -mQHg㎌g&A9|ū 9_x n|Ҧatm6NK)B36e58XQR{XU)xoզW;˾kqHon#wtzǔҘ[a2߀>Įv_qNO|`se.(w5:KW _sM.ܭ+Z~W5zZo1ܹ֥7bhO\,whqxzEk[_2gBto\4Ϳk_u1FZNz6KnùLJجVkl7Xn}~*_Bc&94 [;=,z'BCw};w*N?۹(y_,ѽvd1;;Vo$Wbs2ft~랎O~B 6֭L~7ZoBtĺu+]K?p{n{SxuV|"/@*׾ԹSb,o*~lޡp*mm?c# qzZ ה.6ns) ->zbzs5^tZO]q\gr's7Ƿ\Z!:8rڣIIW1M.+7sjN!MY[7FQ=mtw%/U qa<Ӑ;L#ݖ;< ?'/y( [dg|L'#ę/%Sn$҂Zvex] y8c#?QىHA,%i;kԒ2IJqěWXL`U0~n)K#SX.?).k .<ugtgÉ):99TbR{?,g8ǜS7/(j\r (Uf4a-i+6_I*>> 1$J<6.TՀ.ȚXUYX!EțrA).8̱=0WC<< ,< .@El?fd;/C B TKRZXo" #ܭ}V>QPBfB[q~J~3"n3fj UQ1DnxT?p$3[_mLN 9yN*΂ح] +XqdZm.ȎPr[#r^p29.(gXh;|ʌ 2<6mUxD:K}Mv̓̚%^%+Xb~Wq4s[jkDy`E`=Zpc`{׊V+f >=bPmpMFu v6M׍U6ROwlAE#lmm8%?i8c5ʼn)20J=ݬDfF$X{]p&{Uj-pۜtu7,muq]R Ft*wY,$1N6"6. i<Ǫ9)pn. boь:ZUi-Ȝqnh -ȋJ / {E]{ѶYh5lJl`A4ZT"5C -lmal pK m%ݗ7ќ;mJ[[,e,eSAfY_Q,2Mi"2 ?؞!!!!ke :[[Z'?qjox1}CDo`h o<{`.8ܹ сv8sq<+y{6\AF+?ifJ%9EՅCnq( bq- I&o^A^T5K '2:tkE[l?8( \ډTeDI&L o3ݻm;VbTcFZf=$ m*{Hq3tc37WuTiN P^P2[PVA[C)+9@ŊC cYlRow +3Q4&B@2JԂjDW4gLQQ-d]x,GNu ]ܴM dm`!F]o<5r =tgau[M0k';7zCT@%e&1:\h2ʙN썝&Xiz#m-̝@c]{@J;\@bI@Vu6q؎ G@'˕f5[ =Ɇx1qi+Ƣ OlI#n"z\p6X>l?TNy @SĸT'A8@&K󧴧Yv,y)umyRx nt㦐,˧E "#HH+B:{ILOxinf}ptufu1զ_^b]z\ Ȕ!:Yvu9yemI0>=.xaw9;{|l~C O(X|!ׅ`M-n#Cm}lͻ{/ZϹ_z ٿ{/K}|-:[_{͵_c?_7=ioйLm=S~r|YvpwUnw {<ĭbf]FxoY7C~[pwN-6ɼkY4z+ ̾ª9 ?> t#vy3_Y8M/Z,* m]׈Sk:DNUEWk] ґy3/'s^B*DEC-A dwÙN:z #4Di1H  Y':'%(Ob#`X85l5i|C<2_?nOju:=`w) Fߩ;>ī\Fe2!ʬPCq`2ʱpAu! D ]r`ȅmXnuguƭ2ҽ crRC9u5l;5.GK R-M[yb*!>k}.~r-Fy&㗛e˒lkE^~(D:/PޛZ2rCSoN{1,0rD~ Jx~%Nnk YwhAXZ?Ư0Z~K@4C5 "نϨKUh] ;BBTmPuLAC Cpƚ񓘻RoOoPd;`-1t[M[zHAXv*U`/I]dW]uab/E++@fH" .ꦾ=YdsVՁ55fwnEX"v{ӼژsT.>d&Y8I}Ki\-2TF dzTŸsp ѰqtZKc8CChe_{H[W֏)c.1NShq+I!KhbǦ=1Vg(($IY =.RaSԇ$7 ́F+d])g P],\ì3HDk- }PV6vmϭQq$9u'6̴VNB,x[ } im 5)D 3I'>-jE_Ӥ7u^TٙW95<2:U7d]} Dͭ OhPk"LP"?͒Ň6-!t(TSk0H=+68HU^qPAs@G\Z$/ oE@GBgPTP%yaWwU* tߧ%aD65jwS PZy0dx%&Rc֖zj:Ĥс75n;7PHBy+&*!BU(Arʕ5%ю k M)e%q uT* 3,x4it90P֤uAyWc2qRB}8kQo=e8+%E3.C~'ۮGݍ6릌Ǡʼn`pڄ6RW q+`C]6R!~!Mh]+/!+~JIl`r0ytуzk,#ݮ4 .x 3\6P*vhQe3~I@i(o>h#Jd llbodeaY1 iy]rUA0myL\gw'`T-(gwTMۤ6wPʭ@7 J9l7:UY<0w]nJ ?ZiO%tufZ\:6MLP'=4R:[4 P-'oj ZA>'zUߜ' , &@A#d@)1tvJV%mR9薦I(wz 'FPJkнյCnDt$o\Úx[VxBϝKPVC(:%53xzWBGwW=d 4ìRSeژzL-N|Aq њ{sƑf@ӺR<"WRk 4+.=*HG tᑴRfW G ɡ%]R0N:`qzT@X^E .:yWA1laWh qA )F;oiT9LI"9Rb})r#p(+' R57nux,LkBXlbQ~7+mNׁ:?K9 ]*nR:I˨ +Aつڅ䱻sht[FR\c*kӁ_R 4 CG܄.8@ @{6W"!;*x=3mav]6GDlWw KOJ̝WZuK<ώԧTBI[;kx~JzH[9[ajGMKi`.=<ЯEe#]$SpsT8S)/Riyms`ê.{&N\*ƅ1+\)`)y{Z<:KD49W1,}z tJټt@4'-oFru7ϔQi:x%:'02Bkqy(Wha P43[Kg %z-a!XMHSETEXUs='W\aZ!t%Ǜb]bRMM&KьiN5KɸMHʍS6I9..(篪[Rϻq5j,qGzLoMt?az+ι)] = <~Υ`FsCY~(9R0Fv)iR R>?0\?o9ehHz&%?:-e>2?>/=_r ׆GZnfݭԌƮ ї;B(θnC+Xi+M [J wʧT!Qّ+AP=8~8=:E %'J8Zzp h/A>6`9=V-01xH"â2np Ԇ>8Rbh%rK5hXK'@}){ut, MPE{bJg ЧU+#լʵt"Mȇ= vD&lC :!r\\Y],G Jx7 Ix1k;0&Q2 ̲\nR<ٺjd`Ʈw#ww-يkTItOw;|֘_X]ypr;\$a(VrtHs@=%V#)=;l3*цۻvL^̀k"RkvMg3&;& 'ON|CCA /qTJr]f8n/{;i pl--߽dv j[8n$F'q/{^L#:}6@Gm %**܎_%20: 4 75o0s33+xO@mM\*ܺC6-!1fǦA ZcԽ!ᷕnu uE׮ :`LPk GG[tݷ@zrz;mlBj4٥Ht NriVASFQ _n]{MAEiw)%/"4CWt3Z7D$zŮۺ;.xS7ron[nU7b5=>9nTFwc̣a=foYøwJ Cs QU@L{W%dMY{!EsN%R4 ;:ia%7K(L늱䨢/&{9mzm`$&| H"|" L &ƛf'8KFi}nݪfvn\rUmGksymNG? H yKkjz@leVChQ5JH>|Ok90!6e0JUXc+RxiO*0Y%BB,@?Kи60pR=e+ϊ1DT߇o1T\h 5|ǢoL:DynZ ƫ~u kȬXy`-.`;;M<扊(6fyVAתWhGڜX!`;_9kH~X%}W+ Jd_ʇp*go~6R":axrbXANb8FofrȓJqBait!hvk"ۜnV쏌/5%84jW&})+!z+#\liVm_b-Me54h:ntL/oey:nڬ4մ4kjIM'첵?˪r?uξjߚ?;UϽ~՝;R3ϝE=f*s Rf5-4 Z4=fSk umCřǡؤ6!"{xTƔt)ݽl0${K1#ȜƗ30c&}?|`)֥SKe/)AJEyKr:<6]P41`R)9uKUfMz4vD[fG-N@KpP*\8*tiaRuu|SfEZf4䠃"O[km\jI+A'sؐc8+>4 HɴlUA-E Kkx!,[C i,:Q'VqRUO&lL Om 1Pg>7)c!4rσ[&oKT}d00&B.Qm,hFzw8-,H8T Ô,KF2Mdr7ť>hG?CtIv2p.[NTQUU;MB3' ڤJ9r6JuakyodYdC$&A9SJ(Ϗc%{w]3U& TLO.v0gIBYL0 pSS HաbCPNE'E܊Nx6opXw}zb\2¡0"ih~$? %ÌKVcӢ=5]2OwxB'AZaQ\I.q`"h 97AN!1gbqYcma8P-p&$'jxW g¾eeh]3 {VWg<˲oD'Xɩ4 \" ̀1j,ءw&"@Z:~| T&UGp*Nժ8u[\rL,[nA'C3ɶ.hcap$v?UŁv⣀|{1 +T;3r(y q&Ĺn7A;xπ m]P˽=G=9x H鋀 6+ PmdK:͸ 1/S cx H_C .GueNH;mP?mP '4:bXu#uK{rGSvaYxfz%n?Q^o>i`-O~786]'1w\yTԭ?p^1u׋M[,->Afo?>ݿ]|%|gXi>vŋH1Ϙ^Oc[r/͡NrΙ+g _9v1} ^fl~v|֏^;x֋Sl{Թ׃Kdg2"͚_/0 k.x~륭 `of%OSw{nw7wǻxx˖NL <97۾(M{l~69q\}}_|'u'7>Z^~}owM_n79=]q懲?{W6~MUOmNg B'T94qxSz:+lIxgpokurp#dBE?Mc7%_jZ>Y&_im|1jw4Un9i|.tM?z1+ Q^C smX 9]u3w9qTSgEa-h"Pq0xkvԞ N}ɂJ1`uh0L !g0Th#ϵjBK0Fw.p_^ xuCtW}T@᪵+p{UfH:#\2ֈOLX ^yϜ %Z`N-2 /Xcc KAjs9k (QM96XLUA/3'A$5YS2CEj:G7jN?bhG-e̟W)cIZAϯ="3GV8n+xĹ4#+#QDoFg+Fnc"B#A1:#E'bNc+B䪣D+dAN8V2@` z4W%QK0*@b:q*;v2\9|E+qBPO&4NfX[-wI8u`@ l,ɇ?)Jmɶ(m95=Yy{JZSxPos7̨& p $t`D@i1dO@x iنƬ)0&,g5Fjr}y ^Sۼ6ov@z=Vd?/YwWѸ %;Ͱ!@%*ْO@zF)S[%,925Dc*$uUuBփwį1F g/: *Ff5j:귢je1ݦ qY`Ԇ+HE粭GފwZA@(:]NRҽĶ A֐gZn`!:Xұu$OmyNQSt*+imgq]bE%ՕW!6~n zj:iRXז] RBCНvvv;`k];@oµ}]\̙!V>v=\ ~ʮ3@o;ݪ?DJG]m+nR:r}J?9k-}Jq-%xZXrצpX}X许Rn [W]xw]xzOi.ǐ{DES{OKeKZ=G{X_*sۂ?gQ*/} 1k\\t8;`,*~]ϿՏSu}/_ͯ녟~;u:W\:#%Q&/¸!;o-6޽*T}׉?\ls+{vUɽr޿b#|js8v0SUù)87 {C{R)|KpŖcʁxP܃7p6i=G_X[7p #s˯]s/O poY-e6;^u[Ô6`҆=Gv)m͋旻˔6U*>^\'?DKe|A\߰w-?y¾YSOñ3{׿?MYq BYs oqoS+Ω/Nj?驽jm< FyOj=.j w.[ L/Vx7nnW\2͖Z Y!=-mjIqkvֶg<ǸBfƌev@eF돐c D`BWIHQaȳ ia3K0aT*/fƉœ)X+ZeZ!`tP Tn֋Jl5׬p?P恭]h0zIHZMZ,dKB` ,0<#U+jc^13Ze67+cbX7,[KUf%xLUj =\fMP]EyBsAHT-/C+Yx<ac R3dZ 8ثƃ:Ae'`m0e/#ba}2qJL^U<џYզ4on5Խ2Ui۹}ػw}n=7+o )L6~%i8]9_);[84T֤c$wpV8.X_UԷFΦ۴s].N@FcW㣤&Q"I;em2]G?3BhżJcłԝypLvOprru4dK` ̀hpȧ#_* m#dmj 2+razk0`ZmX6j5vc?/:2ZOZBXMbMl8"ՓO* QRbtI 4ozS^#,E/OƳp9X? _0҄FzK%>p|3~$ >Ǚ%/5iAX(S}`9H}FW;4@e#wjʽ,A*5PI ,\Ї#g a! t[Hն멟Wgm!c-E'Ͳ NAݙ]V1#1bC@V[i'*^K\"pczwTEyH)?uLu&JQt{iE xL o] Օ[ l5h tB} *m%*NF (0Jelpke.R񴄊S`++2J;T*K MLsZ ]FviW EF@ [sfڡFQPB9eS|5Fye.Rnȹܥ+ ܠKzO_/3}&mQg@jDkZ7d o!&3=#Ym<FkkTе@s>7#AwJs}jy9Pf:OxA ]0"Gkd*bMaa +wͰKy"RWtئhTw=E8ˬ /-u6<ٌ3O. '@Qբu P4fZ9`"_ yL6 7%Fjԡ RVئϱOjk"Vmi&Ӡ. ~T_ʏŢ!> -3FdOq"VlgJFZE 9)FU$TzT#l9u@EE=<}[I(F`-*̹1ƨq"Ֆb=Շ$ğn›Wux$5=dLNͼYiLb궀$[r=6QB]JZ]%f`4ڌ:ƅuKu1O"<9=. @PzL;Uxj<-I[%&65hj)PΡləI.tPONܯ}t kMDL[kԘt o1-A2ڀ l"!MEH ^a#nnAplXX[Zz~,CkaRX4oBsmy` 1̔5D)5C逡0=TTd'%Zł0kK*RpZv`}N@RDlĬk+PZj+@ufC[8S4ZږIpdO M j5 ط](nȒcʹZLDٴO y<~4Rap8cqnkĉ{zHi}K쳔O=*L/+:(ZK̽ԺB8 į,Eѵ'fI;=% EeH+LU e@lvCC2-k%2' >W) EB s1urT4x3YM(ϖ޼kˬYpj;17j&lHk |}}};ڲ7I7^-lͻv44AFZc|v,=-OBV+1֘Wǧx/gtjgvg3q<O#|˓Po{N.e0ۏ]3/K; rVq*|wCŝ[䚵׆?SY n"uhxXgp՞ ^xc:qgw]Ƴ|qpC;˜)3aXK2P P$kjTnc()#mLUOFc2i0(`j,;lLQ>Jc@{JKIj{UͯbLZ\1rK5Q`wOy&x *%)!V~c%5Gxe@\=%-G1'smዀ(!fV9Kr %ʤ]| *q~-xT92@fD?jL5c](u_”ն `sЉ;@@HI?Y䐳gf̥YlM(ʎMʊ%W@x\t cԼg6 T␉?<\C & 0w&lc ָ@F.׈XQ['cI'Y6tGvKN:礖ؔH$Z` Dp`aXǷQkV% xWB&en"#"l x̷}$~fpn}>6OFQ3)qҸߖiŪ5JW1c N_=pG cԘyD>o,q= '[N/Vup`k&:{U'TBGt}+McUgp]L;r(165v!׻B5ԊB5PG.ޕ6XzU.bbK3i;'w&S!rnR&&#0]#9LCPf9&C;\+\L^*6()OP/Yi8mX(Jw-Lc*z8?ݎ]ݭ mwAބkqw|۫vwAme ټTb^y wc5Jޅ#%xRD$Zb)H*kU#9 x?v@ibH9Kc` Wv K L:[vrclf {Z#A'p䊺R[ `j)1˅LŴWjUunA-$S.@[t%sc[c< y)zs[pOHx}8o|nk6=qQ+J]|8W +X8'?&:{/%:)ѫ#bJKPZ>\7h}xGIwܥTb.-^u7O[2(=TJ@|ru) T,a#yVbI~be%hKHB&_R2b RӋc|A!d5rq.G>}ڬD9 ;\|<]Y) VjWܒҀ6vkoZR=;}4Rۊ}xk\qpHMSzk qVx:<~ OPc= v? ~vW2#`q* !+7%WӞ[r1*{^Kh'^dp/ o\% IOm $kmJ__^q,ס{Dx+ǃ*IɢObTTHyodz)9̺-W'fMuږ0@N@m* ](vKπ C[pt+v<ݱR &L_*yP?::w?Vβxr]\7t7ƅ,_v:(pbA-%MA+rA SAؾ8 I\ R%q=\#TM'4s~.vVb6p9pYh o`{^['#k\fr6k~~չmյWfXY!S]^Wk_ezO[{fLP_y?#^;6,#e?:w;NE@$PJÿ=^_|%-E+oSVɽqTS}J?^@ \} ϼjt6x*e}j+Mَ%[lf DI|T}WUEBލ ^MMws㭱_^8]+ytq>H+Cc:Rpw,sJn[1 GW'wؿ2/!?w~~!Α~W;39}s?uQG9犯8NEPEMT|ݏ\Rgnp?t *\g%\vfynt|ߎ]a)nM -p-绻= l2?VwWu?aI=^_9Xe?^X%hk75VղzZ"붬V_"iyd@TcP.8`.e# ڍz\RV#NUQk4Z*z2 𴛫@.R#yrX|[bTPOL"5U-s 4mY8"dEiöyBeMJly&(h q.P,g4D+'ىxl"XchAšLD|޻u*OlH 1MbXUPb m^CTPpj^=9$Z,oc8?ז-eErXH0~r.(Aʌn33&T}e@v Ds q2fؾ@ qc42PHKʍF UAxc^$pUS5ǁU*ZIMƎd<*0cLmKډ _$ǪE!{ Eu@J\}وۢ&GLr2z*F7JEf*5l7m<9j-HU1-((fX䂚fAT%-6j%cJeXW I;mkn;ahŖ6֌D D@̑5y:&tL阀11Nu}p-.//~N~6[Ӣ-u`>`d†w^/>?~3vh3Y _ 3З<}qvs)8ňc2] u/Ս+IWyU4ݺ aXZW|Ytwp+omGމɽCoTL ڢD{X4>W*{PF 3>U׽՗a{w>{D@$$!HB]/@h${ %OPwξݝ}w| U $3oݳcލg3"geTe_a~N!Ejb[7>$WFP^ c9Yv{Bu5)6d,b1sɧ޿]{>[zK9y:W6m,EGr6> ¾"rm4dMwqY=lI6(խOGyc [d-t/bDj ӿPyP4>:4ϰ4xM^/'[0,x HwcBA͒(tDN /eYI\lv:upIU2VdȨ A'!9d+ p,Ub98("d8|*]7hS ljSW6E&st%T+KŴt=/YAZ yuks:\G-e2cJ-BH҈={:Tډt˕e83`j-&urN~1`W˰TM2W^:ƢeQ[dfm\dTY^gjJTYKg\v DlpKY%c']J24CzC3APhih C%$0V65A[xI^νZm6 >EțѧT[UqԚ΃CW*h))A#Gq-.~YNt~͓55q,3lRƘ ͊$"I5k怍;Ƴkt\Ij:.휢3iuN5Sf$?Sf-z:"XBL]Oe7߸6!5S[W͓ Ϋ:Zd(cHIwqo(r%hzzbZGӢVVNn""mZ:eT̃"BlY`;da㱚4siqCYէ9 dBmț TCPFW^σ߁=|~dCj{_I^^&#[8TVۮ14uWKc*Υ *nP#i&ydo!wٴ0Qbdsn0רft Xsj5}szn?e 8Z6Z)9e52ZYH7)i)Qz,◦nDV:v/pkƆn;HL Ns;z3u$zObw'au7Ϻ%IGT7 [_X=/-"=*d?Xݓy3ƛc"GU'j[{Dk#!/ZDEGbKМ z@j6h j/TS>sz=E$`Y4R=wLCs#%I~6n"Dn_@藑}]'}YdmT\ڑ~,=ߵ`EN2qΏ28_W'Jۃ^e\]FJߕctyufoF.G$CRD ר;.]>vB @*8zS΁U=nZG|Ot9f!|Ңz,F.V. >P`C4vIB+y%Dz;:'Ni(8%mЮP#8T:\)E>] *<>]`% <0*uɥ#_$k?u24Y;14(N)t2Z @#Z&3NQ!di) Yb(tҭq5Mlf-< }i_sA']A:V*(γTsIk%Ɛt!۸I}U(DrPM?]chN>^txԄD+d@w8]E*ڭ#3]D,r/҈b(r,C:thftzsg2V(ЎN>C4hDGu=/ţpJ Aԝz98_c'nAߧrD=J4t-N#.͸LeS#Rp׼=zcT)Zɤ[ i84n*Q#z1̬,\:DԶsѬPmS.g,+ecgdY.&zRPJ{SrKB0a:AK 66:tO٥6ĞO.7EgIյjK#>_fBE,FxSoO:tBDrHV"R'ġIM:1Zm]g[f)`.ONGgORv!.X ]טz3O̖xy_z\:&Ẓ7x8h BJ6b@z߷h'vHaǕY7ȡq:+@ϰrŸRmX/rj\q uҊm]V{j_tMZ;dGd\&E!xbk.^ exǑn>4hCBp"qaZ.в mup+?"Z'$Խ\T_ )?:HϚq'hwF^$XUVeL LdQN%laSÉݚ\)G^ վ \ON{<WP](K (qY۳tb_\ߐ'NdLY"]”5kg,h/G}dg6V7e(ӿw/\:Qٯjw Z#^{H6ң1@y$NOJKX{3/ 0SƭnߍҮ+}mBtm[_IJ2vT=4s2nO J41=?(=z:t"< BaNohM|ķE[[, {\Z#o]͔<y]IlIkl#O1&]ʩNr՜it5q~jnG!ݣjw!ySsQ~&$7tWv*$i&/=SdoƵsSdO(͛yzsa1qN?Zty5$7ʡW]^ދ&/po7 9^A}˓>Ǜڀ}&(m(o(uP~'hWƹ 6pTdK Iht;2pcr͘!;Kn hwdgin$3!{c-LoFW vf;vsY;Dv;a PhOO]UŎ0;=v{G-3y`UÎl۽n{c=HD%M Z!մbrvP6o˶o۶mKKppmmV-lmJ[6[m*SSĹTɶ-{--[-n)CblIB9ݸ́hoin޲enK؜ll ҪN9QjYlg6MMvsm͛6M͢lzi7M%MfskM6Voka8IbT@h7 `t@{6ņ1Rec R f^#֯߰Arڄߐ߰Ŷ!pS.Y,ZujYN{f]Ģ.^g _uZvN8Y.jnrYcfk֮].6֮YƯ5]vk*{]^Q[YH*-$\bu0jzիVUՃ(bU#êުlU+ NrUXJWC~*WU% ʕ骕:h#+V\!E2+ŧ<>AMhnYQ0+geXbr_V_qV8b ˗1 [-kr;rXRTr?'\'Q?c5j R5PR4/QLt cRt̍eQFƲeFD2̌ejH͈[VY62̏R,,8}ex\" ,rE2D;a?RAY jn$ Æ0R =h$с  QUxp&ch Aa ,B'b8X%Cnpn(U©5J r~Y֚ͦ dA)f7 S3fi4׀@sD@ՠ#kudkJ#1 E1%Ȯ"Z>‚Ɂ/j7ՙfCw`o.^kn.3,h 2ewҥ+v>N׌9z~&k ˎ=y^86>\ء9;ķ,@(ޡࡃPC(!spC`"A5!Ѐ!d+:L|A+zlw='B#c3~ 2Zb,<ÐS Y0mZu<.t n&dAVgYKl%x` ݔUQaR@( nÆ?\tRK2t\ᇃDj~F0K,.:P\AU/4.~.tKAvn 0u8 \dOҋ7\r7$o\47h. bq!xᢻ`/F.^p^Q,BzBF{^0| {P~sޜ?=?wp>9w>DqΜY{<Ϲ鹳gU:w61g,gp69s!w6=s@b|8Cg* voj@z߳HA}_~|ׁ`L~~;)kc뿳=˧w 0sz"hoo2o}C[4(#7M M D΁Bzu ~=|#||,_UWrK?b/~ S_4_X/// [ 3>ϫ|փ)>}slO])>k>&lӟg ͣ9EӟO]AS2tw|r擗>>n>1AO|ǚ?ӺA|R 8EܩN}ԩSFQJ>v,;۞Jii=}ԩS)4QsT;|t#G?G>J!9"|de务I? ~#En'‡}C.?D@zgЇN"I!x܇ē J z ,(O&'N'O'p";y9T9Epq'/O?T>@Dr;?8p?(O?xxA>H;6IЂ^)8`??>x\+=jI!t?@@zOv˲~l'\52W-1i g߿q9ˏ&ݼzc9֛sac֖~Gʼn]{8o +^/8^FgƉwFE7BRuEh߸?ڇh N{wCR>]#?z)Rs7ܪc_xnSyA+HAcY-=b yRR_FM~WfW^uWΕp5CE\⯚%tǯ^U?^u܎EPN4'(RwɉɉI;lbCvL0NNMi;MOMOS̴1Ӆ>3;;33kg, 7ktnv6<-V\1,g@Hm%bnڭv^h [=jY8Y\hcEwM z^nv/{.7EKuy=/R!QHˢLtփPu͜GMD1$&c{J H)ȁ&H#[o&̄x1`@ӌU-hpW4DWF4Շ"ZSc諸Ɣ!S\MsJ `Qo#_Mp 0a& ?&R|2vjrMf7ejM0fe9gY-cfs(~.'s*̜iUoq䭴jݚmVHIC"I;NjN&n[,]=CB/Eqrn^ꋲEY̖%5eEQl[8 @f"J:5|RSrg]wA;w RcV;Ǩɧ0wHZ5"[}Hv,q܎D5;sɋ;wԕtEwo+AVh1'a涧aS&v{@ַ.GNm/5K#[~a{M}D$ǒ~z>Ya[QQINoMrh9(7'=~a:-%–L,bs"ϡ{$TAeX-pCRM u$z :p0D~$R9~MJ,5"*tn0SjԾLlq7ޚ꿆7ľtㆍsH!ٸ+Eτ7ioQi|W@k`K-"*O]q*;["z#d:~}niԅ-fu&ݑHH3kkk7&k:慵3JKyhk:]^m4P! Iu!̽˄41<[ex!)9/un:T=joe&]VTrWB"aOy~eS|RK8}ͥ t\eO[ ;׀U_gy&~,DpXH/@Hwx i"iX%9zXEZbl5G.JK'm؉P: wJЛZp,[VQ(*zx ^J;l(*hgHhG2 Av%,97h!VbIPnG՜ j].>&weB+NˀD s)./0AėdT(;5 Nżp(hQTD37t/xzV&7` )nW@ƘLrc[ڵӻy=\|ݜ)[Tz F$ vݽ5xt_L0?<>/˧U)F%*#\hAVWsb5G#/ h 0E;w֝ft6Yg3uaf cPڔp23 )oPx_J `p~.̿x}\ O~x 񠬾@W|WX#K>[ G[r_Û#7kOi}=EAx%%Q%S5%)@s2|@H+?ڀ'U)>>*O{CT!":|򤇥'T4V}ŇRyBW4=MtJC^$ļIz5JGd? 3/+J`Խ "2$Jž[Jl+>X7# xs0ɎQDR2KkHY)qJ]@!)j陑//.ti?WϪ7Br&L?7{)n&$s7)~u |"j^0?5|Y؝r=*1;r~w/y2EHz7u=+5o`Ntu (lcxnmVLAIt0M<ƣ~B79w3oljĘI˯F\ʡgx'^C)7e&=Vp?'G&[`("6\ g R7q!)W6)j.|~VNVۯFmqm,GE6)5-~L@_oBYCN1!f׮A-\> (Y8Q@rǨ Ԥ>(z#^T6 \"ҀLlˋ)R+]\X̚d8gM1+=٬]i]K$̄QCFQp\ TʟGW"CWuk!!jM!B=pw 2(BnS™EUQt@vxyXڂCm눒,g[')E|\fۍvKim[*QdB󡕢8Wе2< ̇S#ZT!mZIzNIzۉf̬(7SQnV&gK8ٞ63vLyՕ k;% N)?i@rȎbL +!WY(gTeډd|BXj+m2m긔qle?dMJ"ZW+ppÕn`Q+Ji^ħYmI\9`lEӮ#.l2㓽ASs7_{K)9>MvR.͛&DчyA=\S$:6q9ʄ8C,=XW[ a@,l9,O5Z)2/S5M9|\Z\L żyy{pIOD/~p$ ^spB /Lk5hRm'w HyN@m]tPP˅ >FdI{rT´Qj)UYҎ^RlSKt&ykf774;/vDi<3PLꓜ6S$v:_\4~jH>%0=H$W | A (^&{8gzZ2>{T,%/ YRĵu땽Oo!WOhjBj$ބI)|B}ި6c8Gqy1,Gs/,=6D`֗;WxMejSUV-”qE" TS{鵁Q8X鶞P !j. ? k^VOmA m/Q̇"`.Q}O ^>y^){V2ּ3nn@XJdՄ-ns粙Y|B}ŨnfL3VӢE'ȁ S? -c8$S^>'arY4"JJ&kjM G2H8hCȍ|t*Oꞇ'Wǝ>˽g|5 Z3pV|809 Vd'=m9||ro} W=D7LtpM4V<֦hiی$^Q_?Atk;6CtQ9B3L3`{|=7趹^ $z+#,"FԜ4# f_ꃝWk+Ӯ}۝ꔺM`Î3xW^7\K`)SsĘp4 C%fo`᰺1Ʒ)p5FcS{cb<]m]%(Xƾ ޺a&^9bR6D)6_LhUsc4[O'z냎 Pc}6kI JȽ ~>Z}r{);'-e˒ʸ895RP ;cNFRƺ*)"saө 6*Ȕ<2 [OoYPɳ,#p?tCN 31Tu]fpgo$)tk ;ņ=Qδ>D<(2Au2~!a&4DwYZ+=NYJQU,T3lkg<  $qo#d/0OFt'Kg "@Ji]E䴶``b PC -Os}&WbʶkXZK-uQ4*{1+Y|^Mϛ2ԧ  \׉hyPME!.B\=ehZ}mXOI|q Or !+_TfI$]vt5fIv%i]vKɕ (Gj0^ @}:зB.2Amt-:Q( ֯)ctw ڍYv5h;f vנt9f ]YǬAw~Xc?1k÷Aݶ={k7UMR7o$+3ʰtH]hs]b@—XAgdb 5H(uCzÂ"ʫ0!*t2IUtLaJEB)>O]H @0̏@!Tz׫q~\50Bc:; L^eA@gT)ehQȬ T@F4Dy #[̸t!ӳu3f3eGʛ-# Wn05B߼&aF'Z16à#RCr ܖCLH#M@P +"1VAu'2 JW$.^jC{.\DBBLi.>p8Z5ss^sn-?w|kϙssΝ=;ϕgs¶ΘtgϜΔ͙o<8y;i =ĉtN8p'wo忩ǎs1Ȏ' ":vt9X 9j~}u Zͯ:~___~i~uC0t_D?E?w5翰?~`N*~nfyAg(hOEqHIpevP@LtF U,"m._?1?WR v6? ΨT]"Q1qجR s==r= Qs$unD3eT?JUAOljsq.SzD)܄V$woG2 >R-u2LoҠԐz ~(5周I^Fɓk%Aw!OUД[GTW!^_8~K;s;s?►rg˝՗ ) "h>}RrbhU7Mh>JeA9 \X i`(|4~=%2 |ld0ըpʫ +h¾Iiu!M'j\Up,q3bU'U֒)ļOpw<ݾЁ>xl4Ͽus.{\=ynӜKh48›O6Xz IOȻvOʻȿ>~0y0߅0W` ӁɅGZOw|w_rW}>on[ׄ3ӿ{5'ay_1uAt[=̽:mܒo?oc+Eq{~asd"=f6}7T%Ph(gяfwmwŻwr]~w-7w";wt;;~G*юVUn[ns6h6lmmInٺ5l7(A{6f[77(~.ukͺX ||nuk*ڬ*@ګxW˪鲥$X-[ꖙË͢]-Z ,r4=tfwR1rhF8Ŵws=Kr7_ZLxLxde~V6(g,9 9!r*w nRvӼʀhJ92VSIrA|e\+RhIS|-Uz4_F7s"9/ǷPUY}dKñyM$XӶqZuḹJoEyu.k7\6FU F8%>N8N\4Iyd6hEYE/9T8WygU#ߍr^i~yO+CptpqvÇa(>|p%!tA[jj%A`t~???RJIw/׈t}Ad?c ݻN{^jo{׈tS:ߕI5ݖ~YL;ʝfVtm۷=-PRm[Z=݄F?2qlfc)()b=__3]vϘ:RuT34LV6ҬȠ4l 5p*Y$c*uIIW G{iݹ9U;,=ti1̡yzd?l>~xF ]+8x&l}}x}Vr/̽ɾ(`Kgݒ6gnW3޵sW;{vvnv@ȶر}[Ͷ[͛f)oڼkm7lXm0b6b6bjn4ufm+V55x5^JT{JZiWde!+Zf@x[l~%cJ,p  Qʅ"$"t t{8RIAv^VVG'Q 鞆áA ,ϩr.Ŭ^DI:TjIAŠ*(Gw^Uo,g7GS(&p/Vt`rPWʗGq+[' `t@v=  .FsHعkӃ 0\:M$nJ X@qjL'i ҷҘ+9cK$]]t}\?|LyݷapaPf%451f"7Ru:.Y!_$PY0I`ô4:Iv̀!|3*^ati>v0ABf'_|]b;\'mt+[3S: lBߜ32l&Iq6nH$%&aA 2=2 Yv_֙պlud3Jy1])h6Yv|YS֭k'YF3UBQB5 $8wQs"p]J)g1 T}J. D#.]t,u9YBSXrc^CL~^{k՗͗_.}2(WGc8ߕ]m^yϥ7wߜozӛ7י^yWWWx_ R r K/RA2fqݾ>U/ӵ{)'Q@?><=(y`xRp\n_wtRD{`wUmcH&0'M[ʠ}&>Z\gxb˃T2U Y>, P.+ŋ-\tm~z3F Dnzk=0bxlg9]@a!] 7۸FU'}U1D+ZKojj<}0da JX 0lcT/FxInBV;+^GSJy>E 7q_xa)ԭ(Ǵꛍ s,iFj7|&ASB2rM| :at#/s-En|wтe稷&/ty.E3m&jBzJVoW?C@_64,+`bSĝ]fL8I=Թ J(u3TO]n;,>~rjT \Ԙ]HPNƑ{u>0Qw mX4JP0LvUt;{ԳHmoQa(vZq3mq4d Ioe_n6=HЃq#ir07lqH^Np.S4ZjGYQϷWT @V.K\G[еq;#Tyٱ~ {oQ\Цܕ'AȖ(k}rP]oy#;b-+7}"6p]H7}cK  ('%J&SGmW㙐T9l7S$W84#xY(UR6`EMeixw:+|p*T:M"O2q Zi1z &!MԪ^[hm|T&!~f'ggsgSgɚSmC6K^YL5RV` >,0>!ԢK>MƸZ呣5" oM%)o &|.|g? _ ߆M%<D'-/4u1a)P<NWN (O~8{éSmEi:1[O7 R3n:~4I\X_ӀmWXP+kp@Gڦ#2B#+\sdz9ģ^E?~v[܀ַKX7]pDU+W헒/K_v__%/|_;.BsC ڟq!A\P~|}4惏|8; p\M`#A[)2Sl'RIH2O] =MH(eшI8yk2.F"Tzj!%4Ey5F1f$ TCIZDPۄaT#'NOIB)&qOg))Kˬ R̤#"T@*5QV^jͲ2kk45wcրi2D1R6NBpưj1bCo0i@'#M9[&k90$4Ma+!ZF,~ .ʛ喅8]];”p P`XяJ"D4!ivi _|xR WXY-7D, pe] Ovo%oy5ySXԷ=wT60|-Pw40PE޼cx Ϥ[Aצ[!̓JI$>q[F~w4鄻;_~2L$HF'I{Fo; ,df Y B^eIOWY ZlrWN;㗢㓜¡pAu*4FYYDG}uHh%oĬRxs8h6IdFVeJM8L%Ph. h/w6| UK8Z':v@,b[ۺShy\_ȁR Su7-vciTm4 ƿm71ĢAop1g) M{f {PbMKmtCKXƯ KBH^=l@EcVfuD#7Akth}Y޻lr,2aZ ꀦ%KJ\j\& /2fQ j^T,l,1\'[p{¢pոn{[uQ]ߧ&$B/> >5OJ G(1 '<{νٜ{O"B{g5"F۸=9b>'HnN_W~mg{nf"35հƎJJuMV@yba>NIJ5 :D@#jˈN/槎 )5 iO1A\nR]΀rrV^MnnRDu]޵ނ'IVn_tShBK/D qmƕ@hA/!䭾+u5'ʁ>OtsxrF Bk|I5EB5b\WH p`(yI]=ܯtɀRQ28v> ɡHuLv$sP4'neAw ʙ̦P n(J ]|`VEn:mӹbjjQ7_JZWw =k3^M5ضH!h'4qy!- $ eFr&%am5uyL]5:-[lᑋK`%,Jծ^u`9YMCxJIe& JI, $bT‰*FB%Y)[ VLpb(c-X>J}ʚ%mߋ`Pg蔉XClKk^ DZ:Ę:L S܃ŭo՜1W$iHݱnfw}fϾE!b<*(%R !^(gfg$Z`B !DzGGeDd}x?K~6+ߟPzyߙkgߛ}-[y{߫١w=ѻޝ`}www7wޕ }׻w%0Jwӽ+~yKE.Nxy;ox}Gv6vmfߞo{}[ַ[˷o}[-~K淼7r#o=t 7Moz㛊7Ɣ#o o|F7Tz# {{C7P o^O~{}דn>~_?:8Pμν6zk,^׽hZ5}k^35^_k_c^[ƾֿƃ3zykLO63Z#tӪNEgϠI>j'~}Nwd)S!FS&M2y2 E$HMɠ;b8E a;H@Z1ZTd{9pR:QsWU+M5U:=ূmW!7Ÿ?|sʙ)94"QF4]JSit2 +ES7Vh4[pKCSRZiQDZ#^uCTH9(ZRK1ѐ4W hL=3-TxٴS@r.ҴamESUAqg%5]OJqq/0ntJ8Q/57?FG0HFG/C omoݓaŒwǼ᝿m"Sҽf?_,Ԙ'cblq|@x<0O5O6"?2KhHsLs?ufgюg|%Tk-TQCvvyjAX*^p#҆ Qzs8; _ *=5'rXX,pNzU B喝ohIW^`2D ঞs2Xm9چ<8\hstM)M TX rRm2f4.A"BG8d˅s4URhrw&mBXD*q4O xx]W?ꀣ`\ht;9d ?EA4Cvv@JGK+xڔ_qTKt"@#ɫJ@hNm!)EhK(TLDTeR%lLYJM6'914Fw!09av˯`-놥Z=_1P@+фd@F_IUSBSДȬ 55W%+ӈ,1B8ƣOFhv}!/1_bb4/HV$Y [jprk[֌=`YJt`pMB+^ (]Xv}bf; %$<ɃW0"{4*iR QifWd1F0a MhY^9Y(3R\FW&hPL-k"F˗U9ERu~1ajnni+痀W[AJ`AVC2i#":H\·EUU!M4 nzzJD>=|7.T'U#s*h-D #M{Y7ffw4F{jxZ20lj߻RK#Į2VI%Qw m.Z)PH^ d[}j^LY͵p8tܠJhD}*/!=7UMM4f.?hGIA5.s Tj*Сqnl?ݳSaV ,6p bxy9/_v17E(ݬЩpfnX5fy =A"gഉN" .p]' ^Tb5,O%*£LH)(FnNOD%Ma$;}m=Mz xԘj;*h jBlv:s{h+uSIjd4IC%- 6#e3ӚbR$=P7ʲ)9(Fۜ(!+Kx ]?ZT֙Cd, ",ᐉZ(Rqڸ D4%S4\JC|D4"9O1aRkJL@#VT@Cbӹqy.n u}T*hJ[i]L!, ~Zm5 6$9`s=uKD;NUQ{'ixuzh$JPC2m0\ZϴZ[hcdl&%3L&{{ "~PE({EE{-dv-_D,[rs „\ uPfA dޒL æf=v_zuM5tknJis=mՎUDA<'\B ؐn5-q%Mwjz\"tj+MB(1Q;cD_t"Մêp4@o۩:^]lthr /QN-b6*h ?xJcQe<[OB~#`A~͍q>x2[&Ǣ? á5nfnEogn_iMp%ꚤɾ,5 I|}մ!oo 8g4[X'/fƛK+ڳJLk]|V+h&vpxW j^lX(A'jgܴtf>VhZ( ;YhbΔ,KTiSW7]<;m__5Z2_S#Ij] C}'][ 1t*,tC5ʙ8O>=5̤l`; I !ЕXG˦kqԯull[%e}-*oߓh Isrw0t=k5P6*쪻[mٗ*#5Ɩ4咽5> tuZ)uY]Hc7ZhƑ~ vAig*Dڮ6D bƂ2h/q5uRyb\|Ctpˆ(Qu]Ϳpe\SHlwݓl!gqde#ĪxibϽY}vZmƫ`kFm@=7]-oRJ\M#/AJ,ͫCظ:јk@kqtCHRoCl]>{ 6=$}KJ]57訍{sQmR&7+=T|@T=S[fm˾jIδ%Hq+g%n\r>U2M|=+J6QX`A@[S\-է ).ttC?3|H:vWLǸUĈP_*ey(2T.\ை;ljʿl{tio_@ Λ+5 PKl@uJ![obB5JO&s6f)nɤ<5_61f{E.><yv\t!v@ PZ;l8S %C3VzV8{zfXN:$BYBrbS$\ZY={8[B LN`_eȬ ܡ{U,PGDcW%z#:(p$Ho:X|XI4wԽV!*@Eh ]AZҥ̥~ d敏%5t*tpD:V~E\ hujPI+ZV3X|@|eɂtuxDm_fa@5[6jE1Nk>l!1^ *ɨL`c`nS/ODT ֦@\ꤏ Y:R/.x!a!#hC:'D\&kuJ(1]jtO0/Vkj[浛"Wz*Nb\zHK.L7&:N1^?TAtkՀn+RM5%3E/[2'X۫oDXI)_>Uu_(Q`T!v L- ՚ޒ#O*4Sڀ@NgJH.1kGT#0EMm=Ԉ?ΨQ8]5P {5N DBWt][BtllZءWh ~qsGkhޒr q,vZA&wA-4E!J&m8aj! CR]? D_!Bw=Y ZAjvݭ:Gn:h, k^\p7:]6Eehg ]Օo1^Dh,*kb%>Ё 4 2\DX휣L+ vKAQb<`:7Bx2 IclOd{q?;O5n nngxqK}?*DU[YBRDgr竜*5:umg ;]u~埠[m'隁D}\kއ0Zv{##z &m%n_p:B.q! KpWmŖj7/u}Zχo)*-է dx~ᰍ?V8|觷2T^:@W#1'QWiemT{]UWא粌=U@_}y}fu;/KO7LJ4%pƭnYjֹnuUbX='ա?׈J?.@]>Vg)Lfg%;Zf~KT߼|͏çŠ!> {{YdWL{tMu_MY.S9 IRтlɱqk&Q{{2e; UģWYg$4g.ړHB溪16ׁ .CjokZ9JNdW<:im+m*uH4+Ѕ1p(Lu[SbW4t=1wG3t6Ls.)Bfhj4yNxUɳ&l>4c9upDX'uqd״0/2DSJ:DrpQkU5ez>튤̺zNB>2ON!g~e1B5GjN>LI$[{Kh^|SufI*UB'I ۉDn,%ttf?*pѓE}<.@uK[-8#;β匙L$).QJD-.|!4-%?&_;.C=߷ø+˵~hjW1cQ#Se6$ [Ҩ.![/ɥe*ͤ{ћ2Z2G)뙎ey`h^A$lU**Y ] $UBuXNTUdktd\tnћӠ^ogW.IoTАT:3,C7q!+J%VJմљǐ/. a2QG9fKNvj`|J6Om!(q j9k o -v$(]}CWuh5aRjVG}Mh|Gojk `&~HQc@ХAG=WzP i_Zr[1O/Aݮ?{!W8v^󗝜ӯHqGGYRε1$~AWm9+Ё,Ԇ_…T'k4\T# 4N<|tk~~RM Dm>O6w^'fVg*x:],˫4$';xC&ݺT_ڍ-Q E'˓ 6b%nΓ\=漏Q_i?t{ǧHԧMV;@}Yª ]CI |Kb+9-/)uvOuՐ/Ǥuʍ:Uبcn$ jDJNnTAK5$ ::ѥ\BuS}k2 !ߢ㿍q6jү)en.1߸WYI_8;sg4k_rW6?o,C?;\]i:?~E|M~St߅sC;~l|VKcc M}*<+Js7K=)WOAf)'t5Bڶ'!5d. 3 ?jGmd׍ m?/Q~H%%ꊍ)I(XT3'v=ukSC ~/̷.˙?nCY[Uj]}-1nӕALuc|1n`ACkRH,VzpsuJW] Ngm޼ζ@8\B7;7SSߴJtLgN 4W׹ Ύ9sblq#z 4Vg=, 7| Z.u]峣78h_ef#JF)!c_[׻lחu$tb 10i-N]C4mC%Lj{,(0خ) | ħ+8lPylLec8 Oƒ~i'l՟?5/A͟Ѹp x{wto:o/756\@uXF?XO?z?JC Kwﬠ :Ʒ.Bo6ߺ׾կ|_[~CTk~+lV}62X~bq{_[~T}Jg~ꓟTIS%$?h惝}>}!1ľx_}_vV|T|UwG6v㝫wz;w_wn-[ooy=/{{ǺKA*Vj8JzCOoxC`U5_wj5KV^xe-f+WjWo TC*5+++\zy/{ʗ6/z^&JEl6/n^yK^,_dpՋ//0ol,| ]$A:5D!!|ͮݓ> wۻ >oG,^ƽVЕ{U4qtgy{C@sύߣݻqwݖwVunwe e\SSʿDvާl:syۆyj[`&+k53p{ӄ~XCG7mN`Vp"XrN|6Ň? X nH _.:,b^2-l~>[nE/?;SLuhgq!0ڢ)S H ~qPԜtN7XjHN:WmY.tKdJ^4`yXBvH˺YZ|ɹnd+2cmQ]ѥ75F)s,OY 59гB;JTR/V׷F2h)ЎV ]j.[4/UyX j\J^m.Wau=<:<~$qT_i.o][}$$A WԻ S}aOj_b٥hrWg n{Acª~d ~V o>o_kD8Z"B WeK_t_`ӽ7_h>3ͧg>}Χ?''_Û?,?l|pヿ@}}[zPz׻U*zm@z;w4<0@TmgK̯ܼx/􋓏{]]pL[6ZD@\ԒQo\QC;@FhN&4Y^]$5.??.j1xvIw$R"oT0Ru*e[ܶ=* 3-\` '_*Iɾup0k$Co.7 _Zm^S?RĶE\WJ?\+jqޖ5VXRߙf+U Př#TXK:15 ߩr%dպkH@EqB>~D*--cG ڢ<-m] (ֆx:WvWr[#4j0@Z5RG *-WvVf%GVݥn[ȿ]l`+l6R v)%:^YB~zl]R¥T{8b>](tU~_lu*J>ֆu+hX E;-N)C"`x_%X?nt;^{3n~q x؍n^.}8mdlܫ}:1Y`},u]3p ?3ڑ44#r;s,+='dz E`Iۮsin$<*N'nDqyI'I&QFm? 3l83kE$q3w<`;0$޵<׊8\d,F6M'K4l`3ޑ&،y{;i"gM"4O(Lp'QYi 3;v4 (r\'ZmfكsC;̂wyCf`١D^ckGi'N͓dQ/_ 19(8tI'0\yq!gEa:܌^c7I1G1L牽qd>IP.WB yf7p&J= (Lmٹ{S8͋1C;r4Y7Kqi^'Imˍ$䪁kNhr1'fy6*IN)Zn4NdضP2d^,p8{:H]fة3wIy~0HRj5/\w/5~byM KyϻXn0Y9 u2]* p:^:T}̓@Ų"}I[̧I?uFvyTQ:C;aw5pOr,;=K5^gn4 g=IFp6O HJ쑴Ot_<vj B/m]eQ< өK1,Ηی3{u2OFQbFIjviŐ+RIEk[v2sR:VąM eS'I9{fqf 9bp83sh"iɊR삼,YnO'[" Fĺ-;Kp{>FqD9@@qigS?2\z¶BM)(zYIx,;rd>6 0Hi׶xs/GIM $L|sYi6gqGޢO^ey4UNLE:1m/vq4IA@vJŠU~>6s:rs.uYύm,c}jG0E>G*Ip"vK6%mŽAAN}Nqj4[Aԟ -:yUfF2GcsILA@/@?h{`7]˖^bX |<9Q˷SA4XI73O=_l2$YtvLwAt23'I0?!Ջ:[dth͕Ҥ\t0A8L\9l8>V&ayI< RpϝaCzݝ! "438aܢO),̦!N^;LGv<) jxՒLoI~C L07 /Nmkn<JyGv68֓Yj٦ԾΛKia"NI4XMJMQ ͱ Ӱ4ut 'Ln9eΛ$R]=?,%8  bÉx4 `t;/N4pn }z`{إ@ҪOs%MEւ3gI./ё rQYF!%E.$Eww\)أ- 32^m8dY _8̜̲ADxSx? +KHEܤStB/9)|7 pĵ0sz$3ac.H4=ww{?I<9VJwRD̓pgpj>R@7<*<^b2"i RNȴ~ߊj9p80{Np|כ4qfo6pNF.=fӏqG2NGzxdQS0YO@OXYD!##ta7CE2':w\Sv\lOfgǾ! 0yZ6T«~F@z:vχ33Nt <έOqOGSjj4dəi#0rht%L Y.^ w(ǩe"Y1 4b2L4d`}{ Ù j.b EB尩BтvDsMyqj=4۠;KB*2*P0(fXDf Q%a yL`<f%}s44#ߞrb !AIp`hs;F&ZDaΝxi7"x#07Q'QLD&-ޥ(\D'EUt;0kQmRްr7*ouTSLs:/|kgumJCP& #΄1.gGD@]dlN{3b^//x>^bN5LaJqsއ71qh0雴Mӿ lOMP! |ItHa? D;Rh;9/O5cDhng@[!yݟZBM(fԨJ],![?һGM,v`m{SQhe4 :BiO:knPʙ.4ѳ,ċkOFdg.34 &OӦ R ix!;~.E]@ QRx5O]X}Pъ0g磼= Og 0S'kA'Y""Y ZH@[29̔ 8rKAUt{;ކYMQȲT4.C 2A6}bܡQF^w?ȴi{ 60tZ6߉19-dmJP -h3^$>v&qR&L4AYDaPEXGlSS))Hed7@%\6e LcCD)ԃ۴"GʓzCXiF~\VLoAMMGqB_RŸ'JX#jryWbEvHжL{󨖐 'DBG "X{ ΔP,R 0b:8 鶴q=0Ev2fX4 {#P|JSzqf)X[0 *޾Z|<S f.}͓lEL9kGJbN7E&K(㑙x:ܜ|BԌAG;vi6=4m^)={/A4GF Gig|ըƈT)Ʌ_N@ĵSLvK(4h& OOx5=ОYЃ3-pߡ>9p{frN"Mls.x=R/%hH>zpDS[v zt$Y}@ӥ[*&# x񨼢.̩t=EҦ]?Mb0G0ʮ:w Sn6›xh@ TZcOSuiϹ&hz)`*'`Xc{ K FDo/"P?MP> hF*=6laư a"d>MER 1St9p&g\PPR<lj`!~N<ȅ':id/.:E AyŎ' ٸ*Mܣ3%|͢šP>ŎF<"uʣ&9YQiStd +Op"h}Byu28AAz Gc[3`CN:4r/]piيy\t@\g qfb><ԟ;[q}a8ӣ<ؙFr8?(p ^<3GWo =I ޥaK"cd *2R3FTI=)Ƅb`jx~s>#30E˥T7O׊۹(4PU3xD9G=0 gu<Tfx2c*^7%08VTF_ X.,(/G.c P8W()x c IOl @g+~"TF4B lk2Zg.\TFƴ)' օ7QI梬Ƹ[ C^\r:+yMb*% AVB|SdZ݁C-n:[B0,7هcf!b T('& ʓRO`]t@2 p1QPIh?pnj XJEbPqX?!B 7w~1 {HEAUP=j,Quxkf!}./6:kӿnmzaL)Z;F˱ n;- jiIwX`y\|<,hT:"'W/&4-j$i k<se`IXԕ62X P  <D98@B3ntLg/QYMݦӻH;af:} `(7H8!)wOrx Y ΏHL>uv'Pyލ)!x??pft- oJaQEPh wa(&K^ "<>IiE(5@2jNѫ2">[oa14h,#ԁ Dy&C%# dtm?GL t e }1M N~"r/u3h"~(aX4rV ǵ JM.Ks}Ƞ0 &=$ {Sx/=$e*ᆨ' LP#{[4F ފ>Y/|p?)Ip޳SN:Q)`LONDhR5%ۡC4Z@l HqLe k>C>t*x|z1c䂔͇d@k ʆtjS=S@X HVLd<`83l3 5cE{dChv= FU Fp /Č! vQD@Th 4xBbHs#F= ~BT b-(c>ZuL`3D8A! R#=o`GԞYAW?IR\R g*=y`_GP@dQljH<KCڥ "vAQGe;na8&k;{.CH,;=9~"2y QɨLÑ P lXq"HHZ ^Ƥ0uet`eȈ#lB,OHC)]^ye~i:Grͭ@cRé;D]45Sn],"#ޥ#fuwPa F!FFK@ma2%2 yjM Kh6#͟&H`3ހl4}+ ф<\Aѕ0^@%cR ^8;S89{* O^)ڬ {ĊHw9,a,l Lii4OHv(ZBLF^ܧ! p!*CmDE KSkibm!`S2ĘIJ&'nMޢ"T RQ&~( -|fV0T.q‹=LN3\1 s~J xr#@Gtk.%Dn #Abh0k1H180dydHb=2f}nfu #JSG[P%2{p ə""z%#Ռb݃ 1dM,ӱP->/MxZ'l&FDsT<0#[wTZPo(}v"㠪3N ;6čegNiQ=`a6A>`R3.e 'cQ6CRr&1E5NEE"`a83Z! hG3RJdO&ޣ1T!<wt4ZFn. BB8aӤECa/ 1.,VOH.#Bj[|p573Zʓ:`d)==#6A/ZخnRMgȀbO'9׷` F1I4>k"ڸT#ڌMwHр h~X-6j> ] h \* qP{QgSa*( L+\Xk)Ͽ!OLĖi+0,kB0wm:g0^4=r;xs1UIx >Cur1cMT0 /9AzHՠKaebGq~l<tYOM=Sps1=@S0 P=K jab34vo.Mռ¾cG' XSb6 apOE+q;! <`FG8qZt v9$r"]%2@\U |WGPk2U^Sh< 6=3S|;4(쌍ҹ )(B(\,EJ+IA1Q(5$֌U|$~#,`gc6Gҁ)V6p%p*,FƔLpB hGEC ! 2ꍹv*D5Å3t`.g5Q+@`cUey'MWpW3Lx:{|`B-a_%)PCZ|m+%w*a/ZV \x'E*XpA|nYPsellJHxHl/$]jYDB.\~J~up-jR ms*JY-L05bF{Z0zYXSࣂ_a`*VPkb=?)ƋɓVw)O,`B_8R+46^d)D ʋ{2fp8G c`2aPTQ5E ̬-{?H`V&`sZ/Ҽ KhLޜ2?D'Å0* gYiݯH$C(=G#'$E@l ]va].KSjG~ )EmIGGȡW_ iU\BP(D> F>smikDal-2G$>-qn˝{ly,K+3V-O\s 4 ŗaDwLJ^plOxy,"e]}_SP>ydr('bNNHD%g yf<&dU^OxE$BMO$@qNhKWS< !7Fy"z_k ?;nN`|3W 4MbKņ#1c`AV>0cxX!5jE{=|ӣnDkD~}['D P'\<:O`{Um!֪'8/;\ HB0&eR|ru}BB9xiț&3Yo"j9fX=zVE}^ymrEŻ Kr8p0$:g>qn>omlj8()c=UîP]m1X<:(K`f%C %BPv_cTO'&au:)C7 HQ>Q76e5h4ƈ̐a:ʕӺקдx@v<ӦH@D'd6db*syBZ 7sZQrsMd k{5omNUx:Z#rEݳ''M,uʳ7E@Z>$oh;=Kq{1=J&Ļ,4Dp$ɱ@ny8ĩO_Ъ†% j'!i9[ RKuUWG2µ7cZ`x-.#IOTXR84x^ @QgO18WDKVR"0 oEિN!`'fpu\g֒I3@kNCVr !C_3Fu?bn &6Q0 "hO? 9%wY-tܞ%&rtHquOĖ4/&8, $ co9裤į8K$p԰$8GbBlPňط2'OMT-u= h UB0F8XHWP'6|{E[p(NEpg'r՞˂` ղQMlQe$!d= ijj3lOvdۂr[NAyP!B ė4"@XTX {p+!!K م=G P 㩅D >>}R{d)tBXlFȸIjOOGh]%P,;/@vQ6ƾ M'{'ZCy^ gc7v2[ ψ^ kj[6 >fy p,0r`ue"׌!E{JdQg:ii;nʏOG6 @L{F*=,( t.ڂҪ2pNLIȩ܈A}r**W-]W{ lH"DdrL5:B#,^BEzo?p f,'""ҿG)Vga(Q"KDUǮ:}TVHgI ċ:$L%_pFϲ9W'ViWCDyc aQB犁<MV(w&3۴T+9# K@uk?}69ݰ>O$hNA [wlXm1`329y~F~)w!&#>ZdGJs :k@ yKS#''*5{ܽ!F[?gO"Ֆz?QvyGQ jhQ jdQ[T V<0 bZ$ Ƿ/02Y͈}VDX:dj s% \R=y능_f3r{*@}k`߃(@%V)}IȆs[&2+?ڳ)! N x fH?2kw:&UT@_0fxu`dMQcŨ^ :`yU7PJgb??b2JH#/]ྼe ^Mzk&&_7&F-c=!r]zL F!>DN(2h$wZU*7 R&n^H{*g sMJ|- :7 ޚP!U'9cz.\ !=҄ Y0|P':L *}_Y-Uf(2 yޒʄxӁ_/BLLvLº,DFrCg]=kJ 7x/+s*Ӈ;uHx jlJcF4l=8aF` lCs)jYm16)!JB&Č[h֞Kmkq}%(_~j>b@jyʼn30",c/` HW5xJ6|) zt(#Ytq 8oUf[va:{_y8|#}>T΂q  6y~#Od-B(R\W8dh\ڸEIgr8CQod=`=jXx*nwojh].{B %ֲHH@?<5r&$o^^rlk/< Ir4 hC|B-U'8iJ~ӈp/Ҋ سרNtvRi meac1 2#L3@_,Zaߓ~lۤt3!Un,"V' 66cKW L\"[N"?:i45 R +#hdu}$QQIgv= AY_" ~fYtBQOԩ: ¸t"?u|zzpD/X~E\KLH^*J50>_V넳#&rY6`qȝ7I|uR*gEla+Fˤ]=q>8"c`̇5`1WGXO2r\̕}T;wll`@du"?S>Gs'p;MkOR+& /+lM|sy"H(1251׹PP4 9r^2F8V`aLʣVHĊ9X%O~seM0zNzPD#{륕ir #'xCX` zBRԐVVe'4AVA"4t/PՔx4Tt< {s#PTEѵ':t1dxz\6l&>[kdNi#CX ל=6.~Gm։r'ZHTpbNw_tgByR%`#qL\(npْ6^;[d8. IxXȐ>pOC &Ё߶a\Bʻ؏wFPI7jLƟp(JC ۩ڲu"jk|Shļ !6 xPcZ)t ڙ>LFN Y`\d6*=&jq["0Y`(_!o$u֫AU܀Nќ8 i X tffa;%(;lY(l8>:ʶ׊q}]i ], OEnC浇PAhȷFR\z~lgrr)'N4cM9yZ߀5)thd\zJp+F^ 1(}YxJ̵CT9 !F"I% f-5T=;OАge3[oSY WBbc#E>ـ$*(X?3J?p'jyaS sE,Y˩6@pRݚ/l!]8 ԝ'/1SAa%@bA,{t3GkgTQ(ӲI_f2w"튉*EfaOL@Mc+k$Uy_oDsefѼu4d [3:P,r$!˿ZF88.H^ s5DDI$=`>CAx⯨q|v !P `= @3Un |f Uz~Ns4iN&#y&dcuP'mfR*@}@ qvzm _l@f<ß@_껂_D<æ lHW V(; |rɦ#fRW:B?Tmfjo P&h~O >%V³-oG |4ʞ|=n7鹬~E}roʸsFhT\ܒ%8ll8]msCxT$sX08߶4agr5Qɔuؒta&c2(6|RvyUz q\9|)kb=sћ}%/9(x4p9('>]YT&o<>l {7fMq`JOj8;%*b am[U&h?qDq 6-FSFcI:$,3:>)FXi?k)<3$ҏ}=dUdƋ|w@IóWlUU`<&,jdtہ7\x w!6<E5H sH! G|7+b\ @.]%itBPYx `(]qd=mjTx?rRc/` ĕXD&9 ȏi7RL[9و7SqϘϋMfh5˕ PZ~bb=ĭ51h{,(KSr3@ gw/?'AK:Vx"T㳨% 34*.g_OY !MW2m:OZq׿tDDǣ2Uqp+Bޓ`[ɑa"rW /BU=tb g~<i;Md Fy}$ 8O^|$ק'92_f7O!Dxrn@y~aTdF SeKF g]qH5(FR[Aػ}HnҫYfOaqҡu.M'ѠUvBXZv⦌SZ0&CD -o3(DXRY!'R?{:gÈ rML(Sh©G2+A_ `Iό=h,Ўt "qᦞY[&F8#6)[bnE@@ fAQBT%/><)P rE,YmӣSCTЈn+w NrKXKWn,=yH@R9eLR&Qu @P29+-9>W4y 5CZ09aJ=x %(&!v+* /=,8a!b;,UH#|`RƤQ˦R'sR1wv27~g[jd'`Ԫ3*ȔW2x(L0'N3/>;iq # iQ 4M=##B 0S AHnc#ª͈6)Oy,X -FKXt5}OXb8&Cu+𯉛#j+2O}=1"[ 2daM O>L~S ao(@1TĀRI'Xz/Ja;S25d! )._+(۪N3U2Twz(5&:#dgb6}2RٛIzbɱ*J@ا|"¶̔e_a`T6DQf n(ƈbR-oz&HHqOXQ&u;Vjn5%H0nS~h"0D$+i.o@4D{a\=mILHBQ߃ѓbIzc^`$4w}3?cm&4o:Jm%% Z ޣ\d# 2lGy@Wm @;^IP= 2s)Do*ci[F 0*OĐDЂ? '~KyqDbZdWb+n"կd#R p'DF3_^FBtݾ~]U4OKKKb'x,ǡCP~H[i'ZX>wȤN5DR tPR'͌lX?1 C6bӔ>HѡH 0kHw0\,N̓<"vn/nv012%뇕*iE 4"]#_?@6|s\(8&F^ IQb [LMˌ?CE^Pҩ%BRp7"‰o;Оq<*0\ż%I[SQ=tTCweNxayR9"nQ>IDEDOF,F]+<Ї| ^0G2QϽ*BC1gd?GpQb$Tcz2Ԋ<ų %TiRJq_iLks]aty nPjQ+:웓UJN\_HW?qUA#G ZbM\2+H WW.OkR)2S6{'nbqm , oŊ5eU^:Cbff&$m:@Z2D; RD >/d) dsi%LҲir] Xͬکͤ,&Ǚ0\`Ipj9vtѰC!lol4 ,3~] v@'8>熷sc u⧾#!\8tK8¢4w*;Bo|^0DXQaԕ<=>vCdAg~!ud,"\.>GGFs<`.C'*]Y@7l;n4.|_. +oɶUew)-sb Š#_xZ«@՞;KWb[ٶS=ON%/z VAzL10W!@?^0{?m|M"W+N.ѺlXk#I/}]Ƚ+y  ^ڵ$b| @ MC"0D=bO谪H̑RJ_'i5OV|ޱo_ʕ\Y&a1_}Ä s޳! ֓j 22[e£,&42`1A~h.T؏kpEj?|甠k@zK:M?/'N)` TPj ϖ7W|3dX5,K_stfd:u d)ާ_pʘ^?2@h(nO<2G@f_gÇ)%g&Ӫg>7"'T F`8}ǎ s_AS}넬O)Et2o ̾S; NxM *1+w4a x S7d2G6ZRUqdqH51=e:S;_5WUZ쫷(8c¦V)D$Tg&\88p3GE92wJ}c(gZЛcPÈ}}>yk}RF;5Ũ\զω`: ?+⏽jBD$X̣,wtq7.wIg;'0788Ep9RT[-%Ue"X& }'O2dķӫśbU7LE_@]YQ5,Io{85Up Y':AZ & rvyjFvZ"sW Xch:x Q&W0A{Q}< `Ԃ% (||x&q}i^.nc X l8* + X)'noLگf@X`:r{QgIQG\.ZVRtd52u }D^AԤӣ 0t |Ł< .کsym~MI(9Rzȋ)*0J^HGEca^>Y$%~򵡜J7*0nj(J O4pAŎ %@\h8M/j02'|S8GJQdW*1>"8!exwկ0M2UMWpќ +CUpȷQM¼6&@[WdXa-KZ Yly-5+7/;6>?IȌĹ{]ș!|bRn,MW7ܘ[L8czN!>2 bP|U3t"N_:=õѦxdc2/>X#)fқdP{_}}O&M/?^ ;wӂ9S Fk+ۖ|ɐ pH*˂`Q0!2 8&8hK^hLNt~>I a9MFz>dd9Έ4OKoi-$vqY~ĬŮ/0@ (IV0d>#ݸJr|*l>THqGW5jH,k{҆>Zsm4Am-yˋrҧ3h0Ub Nf*q @š#!*VRU.Wc|wRǷwQvB%QH&M_+ _-k6_A i2;Z}]$s170(e!~Umijz|E Bɍjz.7 *RD?W5+ZTX*sT p [X5% ӀNxC)HG}C |r% (Y01>,٥R&`kWȤCpGs]5fI[fIXB\Za$1SR"^Av>&$)kGj-kztCߖ4JHmVΰxT_wH&0?DQ@!d$+f 6Fx#ވ`&n/LYufU<[2i^p!QFW'6dw/zӘ`CEsJdڠGmo].k Bc,gыBe'[vOvmVt@̇v{"%y=if"4< cWj@VETI[&&6\Z5]}P6-@G&;i8o OKبBɎD>l)s?@ ca=ޘY.̮*c ̋*I%I[!O+2)Gu\"KD䣙:}W.%{{ͤ8aX[̮yKsR3hchQJBTMI>gI{آ1*zcTO=Ijybz7 Zڑlja.nyDO_WB,ŽCqfu\gk;F|L/P/{6cZۺw' 2q 㸘?ӷAIL (Q=]po8x]GxUPNk\P,m`^ slxŗZ46yE_!cKY_ bKxƺ%bכUtau FWpQ|k%Z؁EY/VAerʜeemg.QŢ8Ȍ*|ru{SSw8?bvş0p^C8KE[dd*ȿЊ!&H?1@ Lax\ld/?'I.V: J%(cuͅyT"{W|DXd~-ΦR!)K%&-pTyR䆲*O^POa=Ѩ9yPf.FIQ] YY8<RbCA`mtQWDsED.Y'Gص6D| EjIqaBu!EU 7SaZG)W2[l% gs{FVCmy4;OC'O }ZXEN%0ޠjDz1k $F'dȟ%V*K;rOeF7.쨘>߽W{aR_+hW=<6&Q|򼊖QporKw11 "r%A .yL9/9彭it]~"_!קBGvW`jkT=eP2*5`1,ߵ`= Pjʤʫ: gR[3ό)TϖMD%}͉g.6Z! _ި4*x|!{j]0yqM_JS0?lK.'z-RR}:n'6ߦw)Ɛ4L"n15 U@e ӥt`ꍤu<ޣAc]YbU;:;0!jU$)w9,be\: WgM%ryH+\ \¿Wb`=\1oѰ~ gD0Te\icAnQ(2;.z3[5.P}8FW|Rvb+\ @$8[܆k(ikvBU^<7B-8Yzx3:V .^ fbj<&.%;߃Oo~LkXiD'nK кY+dFVM+[NߣGϊX$\\ S){bDdmd3buDz(ꔎ ; PUZ͒n+q)&$V$OZTNÏ,Μ:]:AH܆ 0#FR6fOq<(yzxr$ˎ2Y+~cCr^!\O~zDfes㗲3-CE.ժ9oȮǃ65=W7ltz{T0ry3qӷ;!zG*hQXH^5nA<8]xr81^1.hgGη Sm/wY1;D[S2XM e_X9eE@H㋀J$1@_Pq=-]2b\VRXToX35,;qIM~ i}4E90wx䱫s>|n1Jȿ/8aΆ"D[`Q%rY9`̳L'f9re3c$xt:ŋc5=c{-}UuOb-Џ\j z Yy̑LFZ l16ŠNyR(k߂NP{\Zg!-cḑ{\>ʏ^Ozy$f2T{ s(AhV X3ƧLvpˌb)J ߳ylH#V*%f"K`XǠV[NL bJ|oyD!a_-ovUc"ިV;W ZCY#RHާnB2ά ~JۣvfZg,lu8=Γ@NCY]vƧL E\׻"3dc-̐0Z 6E.ɫ" #˺A:gz 36\B ܊zv l'z!HZ1w(t9a)0{+ldeyZb@L'!G f"II*嫽ߏ+0]ԩL]O2)V`7fן޵B߫Mg]WglZ}L+G^5[aSc:޽ sa% ȑjqm#N{  VU8d(֯|o` Y֖ 24_uɨbz ֔3tc*E v|=KSAK:aQ:>7ߢziz*( vQԍWPg,LϊFr(.GtZ`ec-p KwVds ŏDzT,:M^3'B#j<ڥJ10\>?`o0N"@jost zxvB!Srv˜ mOz=()0nG?* T3\oJ21hxro"me*[ +(ܫڍ}FVS)ffkYկuDZۄyOT>HҎGQ7"Fw$TP4A d6@PBco@a)%C~: ;l<>W!L׀y̗ yr>w̸%Wbe΁#g7_{=RE&ȶU]m+_'v&qDoǾ];U3/";RTAMJi{jB+!ʼlfG ȡY9շU/bǡ8:BE?UfO#Sr>|KMyb cP9{\[rS5 Qpxs˄b $3K5Mt*-^{2mJ.?b FDyA%Zn=Ԕr[3VOhbV+b&!Z&peV yrP zAFTy̝FU4H>#`@2o _v#š4QpX߾ oW+uvh60 Ơ7+:x0x@Ŀ7Vf2ՀQ+h+(2mkS.Cb0 śY_Bߢ =Imdlw48( Z+(Hr?/!'Ri&|?%v?tPK[ZrF71u?uLhL.YQ@hΎ3El BEf "⛴I}Ne >1-,Zb|Azbs+ʶdKP2W+Ӯ"X)N6F%Ƃ0N-80>[H+"LƲ.݉3s~G\^FЯ*뛑5?%Cxfj]ᦿRU4uθ3)G,ij+O5,Œx%%)}'.(j#!O(o֚dyLBъ r4'76XHm3D/xf=P%D ovc oSOHҫl(;_.:w%dʙ@QP*ߥD`q^BB}- ^֞-q Y-`x^wg H2疙{aޞ tSd,_ JaRWKz6j[ăe[x@7,Nj^ՙrHh]:l *e@=blq~ѭzʫA.eId9TŽ/YA-шD!D"ąS?a2ѝts+'E_JeF@Gg&y#+5lhp&{&j 9g ja[G]7U9 >JN-t8DQ0c;3W<^S,68y Dv BR_bD3 W0:"b+i7o1ԋ H^hFYt\cV`=̢w"T;V3b9B.r?XBqWH^.XZW!yEZ^J `xe׷Y%j#|nж2 3TuX/+xɂPܿ i>*3  K5+H-B9hg "XS )2W!(1TNշCL:{ŸYUpJȫL{3'% 8x3˟T6:}w]pPe^ _y9?tX'7g ȊО W[uqF^$36Ӏ{lUO;8ɾv8XP#~__^McMHG=Ɋ`S;%m S}dvi/F933ޑXE"ǰn 7\z1df3l5E;S`Uڳ4zk?XlKXnԦ:۞pŞ络BEÚ~?!~e)D!`sf|>|`awgƷ+TWRc($E$;EO&RϹab_'|T)G̕5d91 AqVnV3vTI_f:B0`W%ߖ@@ %mO=kBxY:vS(2Ҵ`p#YPw-Y!$"RxP&If0}v01~`UaBsq+V( iZL՝=ɹwt2RtPoS-xm"|IءS,%fp%>ʹ"4Gy*+ YI) q+n߇>ORApj<#|c lc{ZqPkYL&ä N឴|&EM`.DW 譎[wPou~(CtQHUď`ڪƼ=2L^TQDdK9Pig7) 7Q QV<|00FnՇq:ʼEޟظTψ9pjgWq?.7L5c"^WAE$Q6l0Q#6fQC.M ˫O26l\Ze&':﮼wXM4e5 ?xWo`1c)t+K^wr dΫFY`EEVHE8YE_5u3刯iA>HM2ґ_+spy 1CMڥT/|%nVK0oDS-Wf\LbdFyV1i!oZ+P0 x$f-Џ,߈`g85~_+*J?Mٰ##Db;32k)dQIeDH DŽ[AaHQ0rMal4 'd/59&wlE-}P=Hh\N-T,5r5_Pc5dMqFyL^Sa,\NlLFRq-.)+xEykzlNL>@w*:brnD+ 5UTT44$i=&y"VQ@Ux>JMΓ;gohp􂣐UϕFQNa?$jzLK߯Pڃ]f1w~S]6'RG{B= 3>>4nvj.h騏7>P!T w?#ZY6YU͛X%xvlY s`O^'Cd%cT"AZ͂g[r=n;hk\g=k2ۛnN@JK<Ya> 5kaϰi=6PNq şا}FP %AUZ3?T~,)JHRV2>,'<^H`^!x07X~; WFC'Vۓ=6*/%ծ3)2j- 2]l[Аz-X*HG[ջrB&:5aid$_PL)[!Ko_2E"\&no QYpQ6:&=!c4ʗL<;aSN#BCr! (ڈ0NY6c4 D\{BbB~Q&wf_RVGZMݣ!7HGE K!ya'p3}O1tS~ 'z'2;@2'yqOD> w.DS o3)Jv7?o̚?d1e#v lzdKA~\W:~ά SLt&T5,[,VK Pia &SQ +qXj('8 0͢bm-/JG, s⊸E'R 5EaHA36A bPBګli/.o_^PF|Qb5L`o 6%4 7J + uĢڏ^vJdY^lՁ' O׍& .E=EֈYQXe5_aʻW bwg)W|k7wݗsNakfc 3 J+H'ڣ}9>k.ptBg۱8ź`mo@Y¹GUvy>[{y/ӂn~樞L2ҡQxQ%9:Ģ[\ v-)FYd1-V`XMD&KH&6w'܄cd#boTs(2ZHT4R | +nF 0i5Zf( cUCqpI/f}>h/BM#je?1 `JNL\9!Z P],6*y$KY } f(=PU9e&013&tɾ`hjXY0"z u누O5ghQ%y3xa> F,(8UFlĊCۤ ,_ "'(QOO^Mqcaʊ#jY NsH:K\QG$"&w xB2j Xq,\s 1 H.ݓ"vυQЏe4^xUVf,!CvZ;nwa t={w6V0fp0VکD>-+:g nz\$7@5`G3f^ b&:%%'ի)lHjwl+,,E܅K22Hx;ϳW~5;wwN (EdXd+,8dl ttrn2L4dwdԾRuÈj3uҙI8*O50d7viR,8iP]!=ź('ۏ"bAŐV)6 /P$!潳bb^,bw5Zj@zn7YF<\.kX\zojҊխ" hHz#<a[/`Q̋%5('G*+~@03qxrZ@_ǥ:T^/(+PHH B#/fPۚ#N m/Ya>Ÿ:dugbC07\Mm%ҘqP+X 2̸bjz4m{%@A)PB1ZoEw XX<[TH]P19J1AI!ȼE0X]WږVcΏAkAF@|~ e[:GYL$DE*/[qsx3b+]ِ#WZHY1vRH|l[rэ5 ?gaeCn)$Sˣ3ź+vy-d\)2S(uSUvG%ʹ0Ny~כœDfG\$B`۽R0:Y8PO h/}IƘÇ"3a$h V-)Ymsz [_!ϳ 1NlR}\K.p†& -߫%RZl^~Wv&\B[;-LOaTN(#vrsuX.NcFFfpe/#W_mգ*~,kQD ;)fG "#f{mB=V =VR=ZWYM\%Bʇ-9ܥYi 6؍_mM]bF L[#Jm 1ZVXHCd mz"#t܄!a(ȖchnN C~sHX!B̔8!l+fL<8ў NHp m{;֣6)YDR|Ҟl0'3 -XLlg]Iթ9gȘcp ]*E}}mD9UdqH<ܑp^mH<9Ew:!ڭW B7lrF#_:2&'od&$`2 YN뢖?GknA/_­a.Hl \pNXYi1[sēҫ}P0?7i%c0\s+|fϙ#zԬV#2eALuY 7dkcQ=#&ph\N4g7h3L֗!pX' >b-cOBy#ҪS@Oa2goai* *(v{&RNkC̗LcF =] v% D|߳,ìfvE+_ 4 Vfhb^\8!s'H]YijtXi"6@CJ {4b品>W *{(: X?=dlEw &t2(sTJli|@q:/w_U;FA@ v?sGmY4mpUgnJ4aLg1'C'Klp8Ad)t*oEӯXZk{"5h/*b>F)aŗsv`f@TwoDX:Iq#o~k@Iҝ]/]Ţ%7T(EbVQ.ⴣuHwd14b/X6%Z' L9$3:ΡV?WMʔ@1"T[g6aՊg ְolZ)La6jmO1CWC>~&|3Df<zh삞K6e{Or t@t,{w=.`CV<܂x 7Rُyc1s >+.i,]:OY-z@F*)@#ƯoG[D`t^R({׽G)np(39 -zqy=2k&Q)* j,քs9r[a̬ٙ6(~p$B蹰D1W|yZrJ WaLꦈD%7a#+4RǪEq6GZn` U],8'`Vy50Hg+VaXuI BuRÍÀo>E#'EEf9YюUPFA(,[~܌v֕0l#J̫o:xߢI FX,2xS2~JT1p W¥nnMq ]|I;Na _[(6|8e/Ed^z7gNJ^ !L +>,>Sf't:qC%JrZ= !{guD Xڲcۢ(31))#4Y-?WOxx-iH!3gwPNnBŖٕXQkY)&9=,惷Q;kHM0|#/-9%GU/ޞzu!CM9B&q5Q`қ賉 i-*dTOTcp/WW5a'e$X4c~Y&W9qfp+LE3¥دChH}!20K,0OĉJ>I`KȵF.iAZsC3E*D91H,d6~PiI빪 % !zՔМYV| : )Ϥ%rSb&ҦA# Y&<:A }?[E#a0N|:իZ`=V"y2/ogNCj "_*%ìT!)Rp&U9֋Hv%X&l`=:)xmIIWF-Pf*nDÎȓC\NඅQ{1F%0f ?}=w`XNRa}> jVTl$J;9f'-SB,8V4j MLݓI^jێ %&HE:xR%ARKt7LϕSU/-Y%[Svn0$xh[d`O@·3Ũ %ᝏƳ{uoeX-%JݪU;(" D?18|U+ E*5J1("S8vq Nj% 2YtG]w :2vUP#"PH:ԶYt!qųA`q@1(dDHQ=rt6~K+؀Ym/b[ХPm_1 K-@ƞTtlJB}c8#9>m±v$*EY6&> +NDbU6~M8#"1n6^v5B\٬;nbAylY%vh aRt6kh[9֤ u3/V2JAz#'xػWJ·n;\ªiP.jol#+xf;c[\p9vihj^+^a@y<Rڰ _"UK;l`@:U6cw+!aW4x|ﯥ{L-29L ݣEUm󆷠/&EF̮ҍT8'D-:8_ )הQY$)痏J @P{+k) >ԑ]ȅ#$h;ʄy♩鬣(i!X7T ;[Qu .%X{ .!qRvfQOpp^ ]G@+z^9WXݷh/ /0t=Sg$J@b\&ZMLzУǸ/͹:x o}=[p@fJ*v`YIgE%v>Nz+`PDYQ՞6}n7~!ؤ$ic񯱨-@c@"[Bx~B|b(F!`%*%.\io ?Sl7Idӵb/`k9bCZLQ9d[DYCm!S ƴ#w.^.Ηʼnju58_@K,֒fk-ՋT+v@PxiӁG .B Oe3ʖg]wi +Uu,5F$J 6bL,G_;C/YbnWWa%X{K g =uLEک22V @v=F^ YZ.#تlǸCݠ*œJtg PUMQ51R.7&(8Zy}p\4tQq#|ߢ1ڜu R $5@.Jn̼qp.0J '֚~kdҲT[Qy6*w2 $(Rxe%4l H)o 1%~[16f#wC0~iJCYdlUSc\$ bZ$xk DB+%V,tT F7A< \؀QЬYÄW"2QpJoA/k^)S5p:_ ($)9 7zrPYzu6Mf/R S"R٠(GnA䞚؄G)d|2V'uTx^ƕ֖5 eZI]#/R&E@@i> "IJ3١8:RQDckF,fc)n}!N2g,fW j_: bj[ >9[bbFIpY`i|̸cBtHH&tHҰ|;13 #H4l 'VÓёhKJpck2fdkVu=f Q6J$A (VR`Sy@,< 2u%r1,謷OBqK/j“"_$١9Ox}~֠H%h~g$kȁ\2q 7vi%F-b])lW}۪^yT$f#'z 8Zc[EM 63lQ%dGv5Tj.MNwW!SMX!VZ 2iH=yQ,1KitbVL`ԾdK.f~1hˁTi<; ;,RU}^gkkUh)kX#'.QGxUg1ѯAM \W+3XhӢ+rljjHĶS͆u9rYԽЋ{>pnRAPB{ޔm1T@TѧMgk$p~y |\+8('QzRՈ d~So y|mYU@Nk`+j مt3 p wf3RmIjMdΌ"xW.k[F")(O,m kz0~88SȎϽ ԯgTC(bFў8Sp8ְQn@:{E_8C]'LaA[b;_OgKys }[np2~"q/e#9LoՌA =jÀL=:Sgo0ɥ5, a7J̅u6Ca\mvqbRgΟdKh'P E 6qE{VH;k"h`1{5lVa~bj2izZ< 7m[qIzjEUD>A8 ˪l eRtN|L[& tM1+ZĽqp*~&daBG-KIX(Ϙ+WjbDϪf j*a/dyg4 2kq6f8 g\B8'_]bEK:*.aNQ\[=;դD@{1g!c-|A[m~og=e[!B7[bp@BhMy_ rQ.W4Z9IBh: [7OGu8|x86Z:!B QF}.\=)6&kl4wgD2ǽY.@bPkhcMT}_Y MS MG:(lû?pT#$%#W菑$Z^=s;uߙKa[_1xRx0G?ϗKEQ9d I/kj&aujŸIfF]̡(/jE`y5`D]B VP=Hm[' PhU7L yvKg&D`ILQH=&x[f,#W03Ȟ(*S.#l<'`x|yoe/5,S6"$w7wCh-!. CJ{ 3\-Bp"fo"2Y#y>SMIQz"Z+٢.݌ӡr/oAw&1z@O/Hj%_$O@ʨ3"/XO{|UH}-[K`Ue2&ޢJF4qm"(v6*P+ff<25\6 H"+1_[@d۩!5T>1e*&OtB|SsnJ**30G`sP*e4.`itH{[}z?dFݔ2nU"|-R=\6IgyqtInKɪ7nOQ~`5Fxhr\cƎ'JV [d/{j4=kUxG3 `*7:fj(8q]_ 6˔ ۉ?wU.H8;Ettx7g{Tx"{29ZlHc_PzgCN dzA_ Ǿ~"1ٵ:R.wZD ,HɅP^XP5ſ^y BY^eҬX 'kLzި’\1Jg;IeŌEyɱ'#OEbknch7boEGR|[ UFA&1>*r+OϠ"fB4eaLM#\PU \h} xv^IŃ1(\&TٲlSlj6˨*5ҾJ.m ҴPJ)"z߭^Mb-/N*`I@ЇK/'ɪ./orX*43ujga 6KR,lȻZ:SO,^,.x8v0=jL;+t祸d{6) \/ i,Tp[J5YƆH,un,KS&{`>6ۄEVɿ77ض+AIښIq* HYeoMta]pJҘ 5C h-& rLsK}x9^6r? S e7W`yb?ʱBr9n p/P. /*M)Iԉ,t|8g̾j60vΤuR|%d5sI[wQ9[& ueltlJ%$jZDVJbtua}r si' oAQ%ټih鈓Z+oqSH&]U^&"L%VQgIJdcq'mT>/nA8,vȗ`!3I:)r$\Fpi39حW8}\mTBRځnsjvQ~LCE0 ^̕5Zct7:!2ؑmH1="@]F,N /l?yˑSf1PB "<&sR=C),*tt˦bۖG>.*S,)kJsj">Mä%ͫt:<ɇArȃ5vK,1#&se {_>x4av hؖ}󄬄 J{PĔuVbf#ģ\gR5P7n@I_m$kع["4F``X7r>̼Fq W:3sDC0F(T6TV{|5~=No;T@5kZLx|e^eQ1X|cno>Q|#"> C !BoMݩq45?'+>4eIQ{ qu @潻>@N%>Acdlm=]Ds`sZ/ҼtGhLޜ2?D'Å0* gYiݯH$C(=G VUILnBok_"SaZOc]ܪ(co+`|#V|. 6x!yaI'n"CL'Mr{8 ߦʡhh3ڀ;TO +jBY Oٶ5RXɼ:(K5N!QO ƒ?)1*ZqW`vq;[:Ruh2:zD_l5A'*g3Cr6ea:ʕӺChTɳ=`։$JMA&`C;KU9Ju+ҐkjG%ATUì)`S޲'wT=rOFgzONXFyMQOܐԿ$oh'=Kq{^,4<;;I}rls1Pm[s2N0cLqR߹@˚5ϐ4m-wa~rGf]ՑLpͪdQ`’cxf]F*ϓX9BA:5]={Pxxy%zTؽ,qEdŌKa=̿Q[Uk B}?^Zr؜Ty:3xOR*sfc "qxGI ġfRgkA@/_ټJdWJ_p%POn0]ȩ2dqO,\Vky"*x~GA"`hRD-tSx39-7ӥ\Lj_#a FeFG̼P6dOMsW8|&2`_͔볨f;1P}x[cAhOkx AȰsT3jjJqmdh:Ѵ,s_IgzY=HA]#O}rp3` K+Hų,=oZA9:$ꋸ'bKKY/`Lswx1܏q}QRW%qT8jI+4cVF yÚ DR[Ƈ:-YcY((?Ec:6xU#-Oz ƑoOu ũ$YړwY,AֳZփ5*"J# {+k^"ׄ$Ր$~^L6AK*?fLl͞.־c 󌩵Sy,% z$OMC8cB M671lύ{׶>ȲmUj@|aLBjحdF畹c HT|]"b:֕b$`ԧ`_ZDڈ5NCfUq3ͤxGZy8U1IЖ ftWtR_ۻ|+T!IF֣X窦SyVm*S]A,&ч5-A <9ekwJHr˹0pMj}ىxkk&ϸ FYT^TzEFngc1q(ϢG?l @zH3\JElXST 0XuCRiJпx#ǺlTAf@yd>BvMD pBEq"oiO j7I_1hwiBInN#SnDxPjZ)XXE2gD-*\m?*ju=j_9KE;_"6v6ƾ(Gu UQ.Ʌx{?ʬc288|*F}J_b5AXoL;+ ?g=!u(v)qc.S%p'񮦹%}X` JAiJd7ـ(3{ICNAc~ZUqӡ Ĵo䏟k0I70?V,:QF3ff4O=`<4 žeV&Uch#=m6?}sK q#;Srh ׽9ӥw\mj3jke<!@PE.;$+WyʷhAGs4^ԞM YpZ@[k0Gٝ®QT1S-S~(gn3 ( !Fi. wij[uriw3YQRp\;2eYvSP?6sȓś-•Few VY 9NHgeQpeDֲT9MY3t38Lt]¦[f䮨VPkp[qtV ʳt*W!"SDLC1@Qt`iŶ0%yX?-n䏴 ĸkߓ0: D] d&ZUF4EAސ7&A>I>$VÑ5gezk+Fԏp$ NxfX+ۯyщ x%N ak:Έqr ƒάyn;}-/P]?TqUWFNy eU- ܋ Nyl+.?lZg+/Uydڇόc`gp,=W[>ΌȤY+v<\ڸEIgrP[Y/Xk` FMVPu/`^XZ VBpH!#MG=q&:NfKd)SBTq1{G uVDY^a/(TlB y^ZGգO⬫T4*ǯC/76?ٽ"im#Q2X2ʸbz00#|sXLTQj D91{Wˌs,)x/1lBܪ9:Ձ"aQ c=@37 E䏈L+5)`:J ET/Zç-d+컰^4r#hߊeV B1EX{b3ھ])#B$iEx!m~NH*t1m5OtpѲXYk?a#%T"yP9%(cKjg1uQ jTg3͂)yE>U.%Q6|{ ߁T.Fs5tjPR!\Uwghj#KaJ& Kf9Eɚp#ⶸ+U@:f*@Hb 1qmc)Vebo @&@|8/l9% ݹC`c3 QH1R>GWLn_D5<~Qy 7B r$ߠپǞeKAB M…"Y %hX> EbW0bDew$K: ?H. !]K㼟<7쨻k'GRg=ZE+VSekb,&Z༺&ojS+嗑b_;'B)aaEy Hlcb%b1 Z^:YMXg}T xtEd@bxEϳbh;8жJ®gC-}ؔW5_5mXGbXV~-rA"jz;C}!?VfW@ &RN0*'h*lVaGBӱtzT0U>"F#@˫˞xZ@Iya`g*2x619J㯑H0J_rV@*(Tl 46 .ϮxF +U*VEeG hҜ QUɔM!DejO&Lj)T P0!F C#N $!Ȭb C'Dq_DI`CrA6+ ]LD#p鈙[)h⩎ z蹪V/X\?Q2e#>QQmO2:7vъFsqnb,N qcLFrߙ=d +}ϔ1$NWlCHJ3M2爀uF!~Є!gj_2%VεVkI6%)t3.5AE/ 4ѫ@q\9|)kb=sћ} E4pZf.D,dy*{A7jFsĬ)L9(\r! ֆ^n A6M۴tN&g.Ju|.&Ii?k)<3$ҏ}=dUdƋ|w~ M灁 |gqy?1yΏms ہ7\T;R΄}QbR}B\/z>R7~ &4|yx@L5z:M'5<\fQhxGeHdj%r1MrӢoER9AH"UԩgL"4wre#'&ۨbfd~` ʒԬ#v`_{~f}"c,I[ D`ڸ T7|dsϢV @.P778]93ξ⯳@XÛ Y\^"%Z/3H,F{z/Wl>AB#/ۉ ȴʛ\l_ `Iό=h,Ўt "qY[&F8T"dԭV17"I=q"9߫A,D-.I_IUYoE_H yRx3@<XΣ6ٵ4HK70DA^Amq> QΘvkgP+ <B(&@r[JOEP[mXUMW썹*V{jS{#{/SgɄqxh(Pz ؑ UGsܕv 2gD.R]3>3(^[T*YXe!wj䙰VE՜+Fق4{•7(Y2k0Ks.Ze@e}?͵g|A3 ڎ0U^T)CE.VRKz-4Z=1ۖfr-A1}0G?b[T2Wx.bmB9ޣ:-.rU4 #&imgGˀ\&q 0ƩYm^zZEcLDoTQ_)P3n ^v40ȾJ$ DݢMZNs5x.*d0S AH}OjV5 `GS B'ƼY-a;HcohPJbY$lFJʌ㶚>JG߫y#CDzV1E|}T^d:(X? ԑ/Bc#Ą+ZS#,uG> \A*X̌nm8OVdl#xLCٙ*;卌h= \ s?2 ?r󙘃 ko̱Ts%V'.$& "5J@ا|"¶¯C'/G@*>(pYv,X]3-Mwц\sk"*{~"lfVjn5%H0nS~h"0Ԡl^rͷ Q P,eP+t(ؑ]$q+>(*f + YXUG[Mn[mRUJ͏(V'%<6ڊ=?w|Q{ F_.5x Qʘgq" bH&m3(u]9}yGv,!;J;;4$Ma CM{.8`ȹG}rm)V|z3kEvj3)ˁauppyUDUv4eWVFSx5*hXf&/"E4x9ɴ=ꛘ2o=FCp0pEi(U"kYWO {>b/tcououb47DV fUY6Fw=mū#Jpgx.Tt/8[k"BxHأsg)9:Cld0m=cT"6zl 6VBsp\IF{C8R&K0pȗIz!*N(7aM'{R dEE H)}ժZcq߾ҕ+%,GL.=bAÄ s޳^jx$,`e죷\nGYLPi Ȉcm!N5(/\ڻυa._j+s5 J&=Sl'؄<Ǝ*%Z <_;2v "萉$ vRY $Og ou^H_Gepz9P)gs;ue8vGz6ԤEx IvR,rc4cJ  eȤG[NSe;d@ f]?R/G;1@DEvG=@|RUqdq4B j&4YDә*y\z ZYwCl{-l }Aw\J[3!OB5m~v;[…ƾQE9X r 9@X uv0)\`zs *7O ϝ7u(>AtX 9,Fq ML@P^Tp_RDAZpw<_2h@gBחv1y X%+# o4k0@Tkcyʦ$%to7ߎ}W3pgkJH *-:GP};O8if f18Rem/65;5#Y,>׸]+8Ǽ"P8 ndҐl5h瀽C]Z4=aUlϦ G-{&o Kp94odpxd% 1ԖUyIGNe._qΧ?vyN`(#0V8GXOy1%TF 5\<=*r ] r7)łA\kC9)X1) PO751T執 3a b@{b5: 9-QE_}&} Pd>ULv23!xZj TVF6@XVA;7]MDKEsJE1I\0oIЖVXvbV'[p2_eǪV9w^aJl{U^=\sӶQʂG@x)[Yk``-ĒjYftd] ugcױ1\J_mF&oRY %< (\gTɓ>5T57lI!1q/X}:.=mņ6x<Ԯ;ҫ=Vu23SB*?%@QUCQMGvCN6ƘusGoYnx1Y9 #"YWHĹS(X m> u[ Y=fxe}~d`ۡc5zn{Ԛ=8/AهA$W @=vY!þ-aSQSFW~)*+<r|Yň1bKVq;%c6vE(w;O c'NAC{T^e?j|Qܠ[=s?ۡ< _Rrut0BpB1*~ :ͯ,>jƪ#^u.Q U)AѾ+4`E$Z~MvclSy ?>W>NBf$ DxO F \rcɄn?5w٦bŹG v 7AM(wr=!P2` /UE1~Qx5w'u|{o'4Z}`E2iZy`^jY Q+j JH ށ&&-lUQ4xG)jKPC+jJnT3$t9ݸlP"_¤‚pPlVJ>g[pfQ.7-a@^tzЉwt^o(p5E|(OydV!? f:6WÇ~?1xY,r Ԓ;?Tpy B,R{ B2:{\KK+$9fJJ_īb (DŽ6 eBұebCn}r Fj#3|Jێ\?(?(db_l3rۦ; ĭ)0ά*gZ&Mp\ ?!9($ކe[}lhΒ_)LeAh 9zQ(Đ|)خꑀn/Y$'L$fgaC ۪H*i$ؤ4ֆK`Fʦ?x$jBKSv2ɍd_ +^N,HKfKSZ! |g(̚v9evUkld^UI,iLڊp x %X9I\l%v8ʮ3@WqX" ,9ﳼr)ۓUoVTDDama3E,I l|).>˅|"(EcTd%_z@:Րxo_jjGʯKU w>?~-^2 ѳmqh2jNm.;ucmOd"gw9@q1,7oP|z2^q4^a I V-k̟r^XVܒ%C(W]!Q2Uz,ET ste_DF }.%LDG^|?zҊ+iVA@vzŤ0y6pP&L珑C'֫dٮ xT.N9DkCЕ~>͉VAkWr 2Ԧd<0W PᯍD4EOakyiV_l.Dy+o@$>vIXّ~EGLkKAA|_b:cCV X=&/Geh?c>`CV6i(k+Bp 79Qkur$eiK(jHAثU_ Y֠,u/k%9Cze{' +L"xjt3#?:eNT^14 &N,Qs;k&h G.¸B`҃pf@أexHD!(ŔiXbGVQUh1Gq2[/9~Yz"P[#SB6ccc,{3>S`+"xVX6&}{qcAĈDSvJc)xGTw[jtxŽz{լ/!,U v+.c#o̷*ϫh)wtgH "p]pǔBٙS F!,RY~}txjl7q% FuC\ *c[eB[mC OPƪL*9~&E<B5;lD$QלipV Jx_k;/':0JW( oWL؇ɋk"8m(U")i[rqf"ܧi&ů$Lbdn_+n1{^.6a- i=Rq%~FYWDl|$C눌aDp- asz W"Ym،/jo}!T};JCx#ԂÞl7s\pɼF`&(ʓiQkY Ć9P4 qُh¬2d-zza 9K,?"'ɮk!s*E|OLȚLS㵘 xCXE\qУtc SJˑ,yAJkLk"ObERDM4YW=OW'əې8fOƌ4i2#G0܍>7,A"W&qy,`Hn2[0;EZ$@LdK)XfMY@?}/8PxtUO,Dk֤Bq “CŤ{3EY9!4Nэoן g5" {"DBnĄaoJN`71\ }T~cE-"o*@}A\t+ؗ԰¢ʬaa?&e6E]&KᕒL ؂Ÿs+!) l(*B % U X"ןQ0>Gy6?D|bk.7:3AR ,װK ?V36xZ5_$ȥNܐkǑ1o1_G,LAUV*c VtȻ-GDDQ_`~t¾'h}c10F|*+~*εcjXך'ڎMBiS̥{XC E) Q%򰩋oߢzi z*( vYԍwPg,LφFr(n8j۰Ya[Z/bU7P#x ŏEzU,: A3'B#j<ڥJ>p._Fa hZ xm1yѴ op|z 3v2LikP Sc+2/aaǬv3f-ƟQN- KjC?hT%;ۘwAfZ:ayՓg"yɥIJ̽q)@QL"*cadY(Bb tUaQfXX/DvU^qޛg@5;!f ݊ȅc EeTy!<҃HS'nYhgv.xQuqkSoW{4bLpI["e[+(ܫڍ:|F6S)fe{_YuDZǂxOT1H҉GQ7"Fw%TP4A~d@RBc_U{%p ̲|D21ߢ @:0ゞr\E{eskG{#RE&ƳmʻlQ~CTpƩ2K7Lb~CFpߩEdA V?F1(IWq9m^NZ|>$T]P֬92#+ E8GG^Rтl{52e:A-'T<(&[@& 7EP ❳m }&lve"Xzmeڔ\~|+9&m *w ?DzFW_ '0 92+dՍ&ZƐuXА%ڱ~Z)>p JV OpSluҡZw<.^Y\ Pтe[wYevWB%G6EAx3wqsV- \d*JjFw5+92]#}󽋅_^c3lh60Ơ+:|1x@Ŀoޭ̌ejV$%Md: \(?` ŻU_B_ =ImdjO49( Z+(0x3 /!'R&|?Kh~:蠕LU1b~*r(Q\&BМgتvDž̊ZA E 7is7dfϿ,W^p(L{h״( w-;+wC|zḾ'- y )`Fxx}V=(b1bIcүv;n\TA-CQJFALX6aKvCU-ɂm1ESf r;;vd[P2W+"X)XA6F-Ƃ0N#81>[H&LƲ.33sWܐ^FЯ*뇑5 ^ԡRY<3.pӟl:g<N#Z`׍ފEKX$PE 2"$D_r@$#d^L b)#b z̐f7(Xj5Q޲&eX l˒79$ʆ],E\7\Hxr%PjGA.ߥD`s^BB_}y,0 ^֞},S$bY=(ĹeD}EIثunED[U),S*ɰy8Yφ]`*%uz؆Q5%?I :SXP#tVU=><7[]#&F?ݪ;tVTM,b,})eU~h#r lߗyͪ~2jNtv*Dwbұ\}O(}mξ0::3Y1E33ً74X&VHͩ8Q.kr*'/> tx2h2 f`gK3rE 8.d(='P1`4QJ'LFf 9c]PDl% P[7bCD3W@1Yk6 w򘯠d+XO*h/vE|Ob9B.v?XBqwH^.XZW!yGZJ 'axe׏U%j#|ж2 3T}o+xłPܿ ?J5SXEԆy8F:RDtIGb,3ˣ Kyi֤DvcVSOUAM4 1d .J2k4A+o租& YdCA"b*g·4ՖN52+dqF3G`b/?xNrC'εt;)|e_]7^McMHG=Ɋ`S;-o}i |dv,F9733ޙXG" \z1ef3l5E**=1(H2 wlKXԧ:ۙp֌*|Bd4Q"UgjK]0fŠ/AXӥ+ەM*;B"ؓѕsn-Ϸ'I #:o|2KZD`cY7\'W"슧ZmIY; P_FTS'IWC4*#)/]/7uRUBI‰O}l"gy|dMcPNK81p;w^՞v;+cğ ŤZ]]l%>at:-HPRҡv;B},4ⵉL~%Etb2Z#OH}:P`(zV4 SNXQԢ*d%01}L?QzK]j@ND 2ijAiA!v"f1 Rg8gگ66-PS6[~Ŗ QHUď`ڪ|32L^TQDd[9PigsXKHDhV<|00FnՇque^"Ol\jt TgƜ}JΫ«8MOd(t]yﴓeE]jH~pjĘkRnV2 A6{fY`EEVHEޚ~Mrr J&H;sp]y&V pI)'8#eXv,z O T _e`,r21&1uGbbǤj@#h6Vk]1H̖[_Y{ k85wTc_Mٰ+#"љFKYk+dSIeDHǂKAaHQ0Nw6cǷ YD&m j4.UK#E$eF!'Kf͏bw3EXh AS?ii ;T;rE=tMɈ\zY#}%eUo4omPo1MB:ɧHNEQ3[3_wL<Hŵ6F-*=50"M IZmI>HM| AkLF$Εś:(dnsQr5XjzLK_K,Kx!6W nmu1OڜHUJ掯.h=3HTȾ1#o@sKfxc/BB:"ZY6CU!Vr %urHVٖ΃G $MZD̎fXS{R~bҍ0N󰯰)7T$tPmFwߘe"(c|2Ă* -=M*J6% )kgjlP^@tx4uxMB$ /f|R@h,?k\_o э.'{T^DKj]S׈1T#gjKd,zLd!t(#JY[&U`+oiU 4oL\f[' ҅bdKaܚe YxvbZ!./uȭ|ʆvv` }PeM/rJFZCdwx: QHFf̲qߧ0KE'$"D jA`Nv/euda(zԽzr%}TdHWiJv=#N=GPg¬'[AtQ$=Y=0 OHS͋{"`h#O!rn6r|L/cxß7f_22lfo&axCEм?0•Ο+9ɔS_c*<Uea*-d2a'nKEqp&BY5̽o%``銅{~V\0w`DJb6Au LI2B^UVf,%CvZ'w'aTsWz*is4aVԚ|X4n7c|"䥻DN7Qf>?*>#A?:Â'E`n ?" D)m 8P#exo ddp-<^-&6"vwY//TVs}2rA]ÆfV[~ms]ZT!IW>£:7w0 e]̾p:ZͼhʪQ ܿۃb}v" TP 3!W/us\F)WCY12DBd:qx1|7ڶԎ-uT՗hh;xu1sm\]Tv>f uB ZSM@њ3sFx#{7&sm\eМ ϋzbh†#cUƱxJҍ8~I:@~'N-Ȕ=Z7Bjf_$ʼnJ[ؙ;zZ9 Ɋ qm<8Q(ØUIY(m,OѭV {]>DivOK"B̞VbB԰2Xc6+h*5Za^\ YN쐚AyFڪ8z2* {A/GJcf: ,+56gG_r[tEىgx-&&ZQ^̫FמNi%T~yOrA4SU J5b<#vF,BtKit11.OۣVڞ"-0S}`mǐ:,Bb$>3Ua>Ÿdu gb:C07L.&ҘqP+X 297̸bjz鱵4m{-@A)PB1Z&oEw Xr<[TH]jh{ d".;FH1Xפ d#s L> e_:GYL$DE*/+Eg"|r{A&JQ!jl~G -}`[:XI|;ZE/{'cbv&5[f_hdl}ClKg$bfTlR} Knp<Ɔ& -߻%RZUŊ֙p oHX7g3N\ VrcܦQ=ɂ&Eīphvp(he(eCnU0.~Gh\;.89ĝ{*}WޒX-]:٘E1.+`hԸpZh [}oDZi- :f\ˊxC iH!=MRO1pΒ["D8-r,?ézo2y.+]>?'m\SrW٪%?>E') jøfdVjQDT0_{VG?'x&;8; :AC1yW~@nwj(ZXd̽JQqc_;QNY@@HLw)RhRݺG"g&gd9|a6ёI09x#g7!!p?sݿwrQ -[**{Y ?մ"xRYz@Aj%d KnU5.wD>4%" $ ~bW&EUTt :-]O[ÒB sE0hEc6'lm,׽?2*mopDSaR Sx\"n}0$.Ge,QI(qDZUt[)2 9}/3,KI/ͧNPyDQk;tlrZBM 1F.>vj3a`q䇵+1X :f9V5+AW*FOKPL&F a[< =VGb˚L\MGqU mg#I>YLU!Cnkx@SͶ /0T`֖nQ-";<*t _hay kw0'gѴMɿrW gT~S'Klp8Ad):[з7,P-#Qxa1|zuR0JK9 wS;mFO3!7!JdUK`8#*rEald9RF>af V@y>B/33*Yі"rofX9jF#wbɟ]YH[Y#3Bu$yVvxf;Yg ˍHʦV#vK 2Ta~ȇڄQ1y]s/25B&lECNAbr'mz*$ZX P[!4 _ZEgLn|Ꮥq[P/^*Q(+?v{hԌYtN{fR{"!&v`URFl_7zQ>7YZ1P@{;+rR>LRHdrAd?z\F{deqMSTAbY r 3YYm>Qz%\ÑjK9a\3k)y3n\1]P7E<tPrCN >jA#uZ4X}/;z(RՅZobAE9Ctk[YeÄ# X!PIsa&0 fr֩cOKw~lJPP) tWW?2J$lGa Z0n:YWư<(1һo/|׷v):JO`,"%sDSzar%J@d@ŗT K8@IGQK5|){,"PJ?sW^ aِ_1g6aB,W1k+s6ֲA ,ƌE Qf"_cRޣSGixZW9.xx-iJ!ו3;(j[!* ~`U9F+j`-+P%8^|6 p? iX] o%;A.cHU 4$;Vz}6!"㲰EEʛy+X$=$1}]g@ &_IS"tlrn7tE M~1d 4{_V~" qһ$%_L'RRH84 -⹡G`(fj$2[?S\օ ggjJdh*@V?D{E9ɏ)1iS{dQ΄}[Չoڄ~[Eg:`T|:իd=V"y2/ 9bո2);E2QdU@-4!jr(RpU9֛Hvo%XM6#ɫ 9R{tS0(8֒ZXT܈z'ٓm F%fS/2V'J7Ø),+rXl A09"@Mt**D%G8:A,b^% ʀv_>A-3k1b {1{ٜvZ^b anoȊPDy+A,uSTQ(uڪ^9dJ\~m$z|z*ْ,pEΰQ#C3"+u~=ϼmԝ nFm(\(<. АYnڎՊnߌ%(WBvUZ"^.J@D9Z!_("U (ǤN-9VF67KdƳ*/1<:*u Ѹe ?/+"PH:ԶYt!qųD`q@1(dDHQ=rr1~_K;؁Y_*K0۾9bV:[ H=i$ pG9 r~لctI%*EYNwsb'?pqT׫Z? YoL/; l7@6;iZg)| 6kh{9֤ u3/V2JAzp"'xػ7N·n;ªiP.|VFWxOD/TƢ,svԼr1W€8!? &+28@D~kw."&UU6cO+!W4x~{--29L Q"yy[P"#3 fWF*ᓉcf"/JDٔkJ(hb,W# b{p5i_H:nBH6F>Z+&He?!SiG܎a%M;~>o} fGEp-Drlt*$Ύ?YE-q{FќhA#ID J)X "+a(? jhZE5ĘJ].Aozj@FfAOxջD Ң#VNs$uZozj"B&Q~@<u~_ @Wl$L2MZu_lX[U">% SDyX"(th=*8qp/-q:P òvrrW]Ϸs6A&96לșjkP ΑJ{ p(,rT4cOEe;mҤ``O!Ȕ.(j5^X5O?8H~ O(23wrᄁ]~AB~^(3KثVy*i Twjb0ԻDu=s1픮FlS}gك`[͒.UЇv)*!J<]}$Vf=EmoB_ƍ+&%AL*\<tU 񽊡4/qd+`r+|^%SboJd(`[9bCZLQ9^dGDYCm!S ƴ3}՟:]U]??$kp0X91[KOz:(RXۭ3G C).-ԺG0J(eu5Taױ\1+(1؈Ŋ3PZu*_e؇S_`M|-7T=bAi˘z%5(F\4GUzAU&9!j/ΪԊZ[;WTp ->8.8uѴmrBdI }#P};+3/tp>yūZU˯-,VTqMUNFA>BlF;eSCCL?C dڌof@0^'ތK!J,h;VX|6 HBI4;5N4wk/.'"=oQM\;d] б"# պ2k0*U/!""i*~cM~| |Hiߤ|܄%C2>kgRTMt?ʮKL[2_YQj &ZY5lBԗl*hBڑ%R;F,̱ vÌ(T3 2FFɫt7b>l| 0!^ϤֲF,;yMʐEQPn cBxH00A̾mv(΋aԹ;ؖ#Bʬ[En vsb!JќjW۫#X*i>%N zYC3J(%h:KcO:ƺC|uDB2搥Cℐrb{:-Y Bhh/8ġF[Ry{3i9wp&;^S1CPU:)w" AÃbU0/aTwEg}d ^Z}P$ 7Sg$\EE*A#䮕˿FXCuk EѸqų+6}SQxX>^# {:'qK |z& "vU +z Rz]X!VZ 2iH;y1Kiotb̥%j0jx/V%t3O>ht]gf|+*~QgkkUh)k".'.c,p]ybKN1~FtƲ#1N7ZԵe]R(niEK SFS:־PR=GR4"ttůI噃qat/el#딪Ng zcU\ ĥ]^QK.,ȗSP5#i'jKRo"KU3^8]RoxJUԘYLm2";$P_QdĊpg[qnwq*voa܁t0Q.u0MAE?QS,M{+ leÍՆ>;YMydzpZ0kV=MJ=s:EO~lد[ ,z?pF~QgSFӱy7&Umt$3BhX(q;r?jWٺAr޻c# 3 S$Ajd2dz-  FU3XKU9rAˢ ܝ*j2J11 2cD}6쵈{TB(LȨ XP5y1WW Jqchǂ>뿝-KPS{VY]85!*+:PtCz AXX 3 $4֔"E}ɓ5܃zz.|"wQJ#X!fAntxT׋Χ׈Sk##"ؓfmƨH FswF$s<;M- eތ6D5E%heӔBוs;"m(&>ok\wUH e9IA#k&W܎vlfc?ln0 JrZ1xRx08 ϗKEQ9dE(,Ԅr?1P2K ! ;P __̇v6eP< }qnal{% (&hbczyAsOjzoV7a*Ly J nˬ&RGT' G#KsjxAk'n=`Gcԙa[έ)ڦM@Q"آ<*\_ESUXtAԩ\A|= ,$=.G /*,Ktxq`UgŐgfK͠ӮcqH 7CѦmz>ꥏתJf`TAH9qϗ0|ttiH{_[}F?eAݪ ncU"oGZ:S6IgyqtIn[ɪ7nOQ~`5Fxh W91cA+-2ʗ=55qaa3p`(njʍZ0 !ic;G\7M߿&˔ ۉ?wW.H8~Ettxw C*<=YP- 2/I^;Kܳ!/D'dzAY7}%({dnH8k- g̅űjbk*E\f9I3oFˤ̬*,Ʌ_t&頾J*#(f.K=y(ӭ D(ccZB4 2ϊ}jOl. 1^Ҕ)ˇƲ76pAV;r%|gN,X#eO#۱-6͖hzRwlwiޥVB)@D{ԫB@U{ʹQ#" P9~ l7PUx9]hl+2iptS uW-*f_c TT\Jg忷* ^5̈'$ =y+.ّMj˳B ܖVMVj!K~G:ҍI0!*/6aQU$e1{u vJNnQ\%F[V|}i4Ve;sqSZn#n=H+X{áb(MvE$~QPxY,WrAuj _:Owv(Cn=gԭ'.7qrt*+K55!@ FoboޘՈz(2 |<7plk'JP"Z+Uz\ X>qS7fL UOL(J*7*Rg"vǵY+9g\T[IkI:[Q$6F|{&7!&%59.s/q߸t^ˁygȍ8>@抩8JR nLeҪvǀ1c^t.ƣ.'I88®W6*B;U;OJYֿw>VXHNӰ@)؜ZqJY*UXgz+gX Q| ;# K)q#WZQx Wo#sQiJv r(e@Ռ :vwY2{8R6-4*8 s,Dոqêo)&Bd0YB6?sJk]QT*0p #K3-[ؠr%~}6eF[$O>/ӥb@@!L(& y ᬟ}rp7U`IfH UT#"ԌE$n :ܹDl{;#0]+?oJ]ȕbtua}r9R]F(l244cE1mPe'w=I8! ):]#!FcrgcMH(J$Ԇ*z`0Hik&8/&4LVjF퓎8iwB2^e*̨6aJ=yڱ_=šUJwEƃYT-ryF؝+߂M '_rW_/"Sk7IH㣍wmpi+9ؽW87mǸkmTBR:+k 9l( I/L 0wD{1WhёްWjtaGFBVl!k2!e4`bX&6U\,O^2lx1AmH"`1VC6%cm߶8uAts[ K-1#~6&se {1x4av hܗs󂬄 j tZPĔ}Ubf#ģ\7fR=P7@I m$k<["4F`Xwq>)oy à @tfz!r!8۬u=%%J'cOq/d`l(`ɿ@RE:"U͐Ar.bkhiL(Lzz:X1T^16+++~.bނ?2=Qx_BN*jQ31:賯32Q7 P'@r$sY`_aCedX$ׯ̫OZV>oD;;O(t[Бsn8K ͥiRBx&8+?J5yag2Vbʂ4l Z{wjW:hF᳏mMfsMs"I@ҢXf3=8eh|,tοGo+~Nf߆=Hp_(gؕc0KȊ0U묃k)+l&T}Ċ`;<F|c%ض?l&H"?je| ]06ү¤[Y#80kqʘ;gC+=jj=oLq+ nGǫ}!n:'yU0گSCcy}AJ5cꬵ(46 <#PGyvVz?<+3s^svnJ52G!v^ߩI$6*CwbP˽lT39  i%t/d8:: Ex:N}f7@"_wy3øqq Ƣ&֔ˡzLÜv^"Wl ʩJ`Jy>6ټ/ mLosT@ xO\iM>îJ#yG &aEBq =`B.rHTŁBUY`S}O,e+,wH)mjK?B@e XLK:-YfE A|׻5V:Dal=2rdƞ [c )7ˤM1S^, pl/d1L<"RnI(TąWb(XV=:` P*b> H`YyZ/%t}<&dU^Ol$nn0Ztp۷NYrc7z*2~ {D~jG lhǁ7"NVW-|&H<E*{}UOp(\4OB0&/q;[E%ef>k4=e6Zh[ՆUMYd:ʍӻдʳ{H(ZNWWφwn֫q0''p;뾲-ҐkZG%ATì)`W޲'l6/FgdFONXFyMQO<yER/вOG!v?J&ķ,4R]>9(^69'cQ1܊Je͂gHgĖ0#3L^aUyMo2(0a1VQrr_5/۩fUT(,S U%'_*/5Bk^N]VrU!LŪQw?X"[hbjE,{N| 1V_-)-r٦jeo!6i*LM ֹLFKz3"IPkR}vG+vh'i`Di \[<-Ma#K`WZV0,T[BOJjt!m>PÁcՁerBAxVPL}EtDL'Sjފ~Lⓔ/9t`#$ [T SSԲmb3Y5"tlKd[uwJ`%@=qXStI ʀ6ŽpXQ텨 c Ҋ@,2Iƶ=Om>_‗{$>\,Op-r#~',$Q6dOۚ-p6 SmsHaʟ6(Ҭ1P}XcAhO{x AȰsrUjZ4vRGۼgZGةB>aD_ӷ_:\-d,Lo>Ȳ_>OY1u JF#6#xvHOP]#PϷk vv&GW zMy XxkIAEzQښ]p= 6ׇj &WZR1jHMivB2$dhN9gL4Zu BV<`-rD84`DO&ȃS4"~ OǠ4sdH6fѲӉ%!Ys@NR2s|y(u !^݃kn+47b hcZs@o[<)1d^iۘb&jTdm !i{E;U( 3x=5FHh#g|Fd0EC`>(5'򞠲2/ I[$ULGvS :LGX^Dp1hFE7,Zy`_u'5ƩqO1ü+{ʡxWr:][eI2°_=W5}kN/ۄ!T +}U]A4[*KD'1 PDLRǒ\ TBK_Ƙ'UYd'.K|g㣅۬1<8eYRyQ.#; 0w9ӌnhDŽc0NPM_m1 o%:"U3fniٰA)a^5놤07vF|u٨ڃ0<݄jɌ} Յ:r, Db,_j[3n?k҈ZfS7U#SDxPjZ)XXE2gD=*c?*juj=j_D-  ]_Dp w\P$hG6Yɫx̔%!#>\[d5 TEVu+VpP>7qGn@/Ҏ> {E y"jdokɍp K3(8,)ISƲ"+mk|ϭ[YQ ro 3*2( <#6 _ιO%lĈ̯e*S)*~l0R?R1,; 㕚5BcWL T#vuU6:Rik&&qހN)F-,!rU#a;"ZMP4'*)7v)`OEYp@+lYGwQ߬~q] e&O#x{ӹFX҂o>K[F4_ caK`;iPИC,H7_,ƗzKZ({cQ.!q]dAn̋. uXjiFrCg]YfucPgyÅ2'3RcؗYpG'%@7`059U :D!0UA[byOB=JHDؤP+]#c }X|bi4b^_vI?_XST?א=KDF4 30V1_W_Q8#ئ* ʭ=ѲhZ!8q{$h%`r%r1}`7AH!FDD-@Kj3IL|:⋎yvW]#q DVWUˢΒGZ&$PU?vȼzqL5^MĮEؗ2O+ dÜ'bQP K I$=HQ M0?12Ag UHn!x1Y Ɩm#&y=e:!P(=WkQc$~HrZ 0kNTy;i}m>{Nm~z'#mPA2(LX,g>3z3#y&H5VCݜ[)t90;jl6`]k(>,{+vx{1NGtSUw$~Ll^Jp n!P5G$e(lc9^E.k٧k[Zg@7J9xl[.PWN4 eS/.(FZe>_wՈqr ƒjV[F ܽGu ~ #qg俣Kֺ_g#c:^&?YUi3x x-1\ Y!O|_JjGleo=./ v$+|pHEjcc%w^" *@71JDѹ摕9fag[Vvt z}23uOYK-n2K%R-Zid9єygª2[oI2E-N`Q(C9'S!KRgEU,_ F]Bɋ&ȐeQ9xQ toκZAE}orsݫ(f`;95./=sH6P3\qE<ht53c]0yRWi'||Đ79^ .+α⽜KŅk:T{횣S(*-qixQii:dC[tQr0&joTU V"|jc=-!kfiwX4GsC 藢<ؖ?vNmgzͱ6IQE@[ջ\E9yRk?"2M(1Q}i5fTWwahrG f!*y6crA'gbJ|QHK,hl[Ԡb;Pe7[(b*OVŇIYqikھH@͈2غA q"|눈lRSRs@Nyd֐ް̕IŬAd ^)}_F~5sĿ3j.IRkb /hv}F%擤g;AXׄ"N73:p8Y }L]2]gPhz:A唠r!nf@TԕXD5RSHc^6@GQP;P=KP@S k ՠp_ BzhUcJ3nG,-@>Vi[SEZp#x*U@:`~@Hb pm:c)Vebo @&@[]|]Tݎ]]BН'66O/y0#sqWL{RsӝO=V) ~fjc3{_g .C %F?': n+a s0-ΊEED".Y70_0ު{^Y&5Ȇ#YRV/0zڃeW`n58=IQ3Bͥ`6-v4dާJۓ(/г@}P3uxtd`Z&v= [=ԃvct}a$÷GeV⃧E'0$p^ڸvK#/fM-Q G,IҀ)Vg!uq O 62ܯ^u2qra@x|Qn|V{d9@7D]"P,da_Cڧb ?G> tH. !qcjq/NxHv=-#F3"(ikUm:ڳw;18!DIJmՁ`"`D(R99,Vw)נּd۱*#&9Zqx"0Y`(X:Xq+ J@Lϕok;}]ŠZ^x.a> |&+(vv&>O]ʆPG VcA\2O%':FPA_;>q EA Y}U\ 0g\D3!\|AzAƃjn 6ԁ.hZ&9lA:'X4*²* +*c|}s]j1KQ\YYs4o_e@1WPXVCv ?NI1bC{?Wi%A9͹uy.? WwTx=cM ,X Eοgz=';a>J\zߪ=-Hr\]Z5ʴ3Y X,TDQIm"+z1Dq舳cĦ~2+%†(Tp$lz@8U@ńH10 '1r ?;ͳ(WxcBzA?(s'JFT,s'Q1W`TICQ]<^jg"|E h魴SDe_StN>> ܗ,aǡ2;骔mq{&Eר#_4aʿ YH3lWkO+zhRkI76-)t3./k,^|pm=)ʆ߯.t|yy@L5v:M'5% '{SxGeHIZD[AQTD+ju*,>d]ܹelE65Yl$&85+-ٺYߺ8^dl5 a Atʗ YsG6UԊ~ =gٷSMO{@r R wGJGDHS-yO"m%G"ez܎E@4hAI$3wQs҉9{|u4bjri_j ;Md Fv;Mï ɫq̗ف- !Jlѕ i,{*V1 ˰0U𩲥hKg{.R8w<f3NH*jpΗ&eWv'VyC ZO aM%r|7 EJ*hf~;XRY!:&z/Wl>AB#/ۉ ȴ˛\l`I_?{X$&6DびY&F8T"dV17"I=q"9߫A,D-._I_EUYoE_H yQx3@< X0½>0)ܪnJwѯA\r)m̒yk[ZP1rJJ =Ly'㏗yu3L8QϼCy!?# iS sj#8Vq ޚXDlPY{7LY p"ƈF@xqY2am}|e,Lq6y^n `ddG%lelW T@Č[B+W8Wi`ش \7i_ 5 z<i V-ݮ7HycZh1z2V}"eYi, ʁpYsP,䔓SֈUIY1<aN,3ѯ]<!"}VD|}T/d6(X CUvH !4[ {T[ۑZxZǣW!+Hѭiԋri('S%CuUȈֳA0 * lX,d7}bUvJnq-ڢt}ʯgDv`^\vXUٌ GZ'V=^Q;u>1ncW m-&N!V @m'IFAc*/M:ͫ]nD'R7ʵݶ-x.Fc><`;+~QDu .ΧʾYq *cwÂ{DQZ,yKMnGmRUJ(V'%HMmg>w"'T&^B^!hbjobwRԟ*#nJ'Kˠ W d@ f݂?/G;1@DEW=@|Tqfq4Bchm?TS;ۗ[£Vd5Q C}s]*f6,YPM=jCp=<c([AN!84.b&EZ[@oI#2vu&Գ5^H%HŨܳH`: Åm^5^!VQ$6*h' ]ܝ8 ?=BQ|8GPcʷrS$h6_;}(̅KRgzY(Ǝ~e,PGNo40O^U],%KũuWx9'xgiOO0E䅫;j}P|@`叩sHk(h|uJ}0j6~"Ģ)y J$ \_}B{*!ع~YpJ3D6ٔ[yljƣZj~vX^SB2&TQK9: E30-GݩYA?Ȃd)6 hN] |)?ֽqd Yp#cԗdA}M1d59,+o4 ٴEr*@g֩-;ɳ8ΪPDl͐H_Dxw%_0c!ɫKzی }!3-Iձ÷^dV H3'61WƉH{D5¥i@3=~2>@x`ykQyҝ(3Jf ۉ -MVJϡr뷝DMO:Qx|9⮶.#x!CV|.>cR9Rvȓ)*0JHrhy,v-˭#/.6 ~!_ kŤ0oTB?`P*M+M,v wl.FQC@but_6K>q~*2E*&QHy-T57ْ#S1UM_t\jt3ׄ3yi]wWE{f1ee(1|5%~KJFF (cU'6pcr #uGhґ{(X m> u۵L7̘fxi}^}2`nzTySC\ CΧU֠CHΤZ{@^ WapZƞ0)ͨlUq_ 35Na*_9',fćiSV)+ڎ]1Ia"ʬ۵}q%0rq|S8zY{:Gr7x^x[\vܬ_/&[N(FR`rf:Ub*]J)4LU^i.'yԦ0{YMe'f֧_G8 8${r6)@&O!|bQn,C/`n1\BDseR 9+֙Ō\nDD#+.>u2{.1>^m7kMed'…d6^j]ܺ#)fқdP{vc}jO&M ~kܯ[6 t s׮l+# +$!, VHfJ@7TH<ޫ-y9K\'fT"|6y9Ar!}r$i,V[oi/$uqY~O1@ (IV0e>#üKr|+>RHqGw5jH,k{҆1ZkE6گGO\)#dE92YgJ\ȩ ]>r=!P1` OUE1~Qxvw'u|{o'4Z}`E2Vy`^jY Qģլd_ށ&7&X=* ,{YDCV7lL ]N/P6H\LܯhaRaAf(6$3-laq8^fmQ.w-a@j#k< ;:I7tQ>̀'ܲA+KQ_ 3F] ,eu,j]*8yB,R{ X[t_=.%F3%OU1lcA]HY/=bPtY0[l]HQrEn _vƣzb/wDe_CuI@AAoam3\Lk?֋XUgV̳E=n/_YI`iqCxoCfxvlhhΖ)LjmAh TjObHegHس:>e/w,RQݓVi&}C3γ1vmUHtd?lRA[åUyp3x=ex$jBKx1d}d_ ;^*Ov$J{ϖ2()mB^|g(̚~2-X62/Ϊ$]K"\SSʢ|.6;eX@*%D*G t*\Jͤ8aX[̮yKsR3hhQJBTMI>jI{آ1zrsTO;Ijkxo]jG[]  |RxER[[8w`{unw%W3v٩okKxx 92c1}ԟs-#}GY~qLw\ hhekvo5NϘ3>- =(+M YɑȒo4og Z+&yUEMάXJk`tj 7[+.\m Z b-\svǖ՗f4*EQqؑ7Un"/Οp~?0&*vVCSE[dd*ȿЊ!&Hֿ1@ Lax\ld/?'E.U: J'(cu͍yT&|W|DXd-ΦR!)K#&m*iMT}jaѨ_jN$k87)";!u篇5T*Rr;ln: Vs-e6xeW(`[ ,Va SĀ(N0iX{d yaYqp[^flVtTmE[yQa/WN[-$0J}+dJL$H?؏ѓV\I WL#^UHPxFm6ZQYxbԬQP.mRQbu`ǯ>yJDbT΀HQ̀Ћm&#?p h8î<_P*w'@_9[(D#4t'T)ZHٓ[5ɪN )#00q8?QbQd\[j ̃Gl&ml ǫźXw*(J:ZZݍZe R'X""1tQD}_l )7@ (JgmV%K-/36skM]yV^8<`jX "H.J9\`=TRi]'L#ZdCMqi9!0Cs;tQ&턆ߞFnq gmL$O((}~ ]g'ET}r (?b IYS- =N mAfnu]`f":CN/Lbӝ/@]6\\9{4wiqb/IOYmsv+-J/;,9X.R/̓>QňjCGG/H/͹~+];3C%]T~ Y_߳&yJ+x@TV|A31 bP,r?e'@-]?/Rѱ#;,4RٍYڼd6T-"׆*(sOph|eUy^E(7,yХ'D yM7%A .yL9/V4.`.e-ٺꩱ+a0-=mP2.=`1,ߵ`# Pjʢʫ: ﯤ4eL]l"(*oU R&^ƻhK/|D1zPiT Ŵ#{_jCYxqMJS0?lO.'{-RV}:_w)Ɛ4,"n=LU@eӥtaꍤuBѠ9ds]uEziIf]Kj7+>@NYeVqxCӴ}Uft W1Cv2/ קKb<șIh4aʸY?L#,#Pdv\Ujj>]Сl 5"v\AmKmhX7^Hpַ Qv_X>ϣrguYyoZ6qke- \2/{55$Bh@cD<"˿ ҾLl8x]?0UobP~l-@f%5 l& ɝ]¿g!a08Iv\ S){bDdmd3buDz(; PUZdI{ RZ8`Zy+'|n*}Gg^j< ^_v$gnC?)3ħ8DPp7~gް\bɲgU&qy,`Hn2[0;EZ$@LdK)XfP1^1 .*tr"s'H sv. [eFu5$2߰fjXv؏IM~ i}4E90wx䱻s>|n1yJrq8D5\ EED KsgcL'frc3c$| ۾ěc5=c{ncZUHb-Џ\ v YyiĘ (ey `#œo~O1-`AamݨI.i5t=%}s6Ks |z)\`n AArU$`4X])w´G4w.hi'#lL\! ԣ>h8Wa'$Jm&-5OBŏR@5Cv\?Qa5\)}CW`=B/D$JIIhs84#.aBP2%gsEO@lz5RG K -Ə%l/3zHvXYDQG^n0p7LbN5+ ~#:\kXM>_3$xus+btWBEH,tk=1%$8|1";lt/YdS`./H(=a-z!H^p3.,gɕXs[:1vK8+U4k@8] h3N40ߕYzqm2{|fb1X)ƠH&_Ut !F}H2o)[Yr(e(FVNmq(PQO'ejdtVZnӇOxx(QL6Z?vwM*g ׷ An!jj-;gL ٚVEHmʴ)|+9&m]`h))fŬWlp %LBLFea'xtl1dV84d Ov߼V~\f{ԅ+F3Ft込f*׫T`qv]uVUxrP zAF4y̝FU4H>#`. ʷRhZ]daMQpخ߾ ow/{tfPSQNcP+:|1x@Ŀ7Vf2Q+&2m[S.KbVݪ/t!poQ~2Q e'WD#MJ ֩ xLKHT O O֡*]L>yO]6SnvPE=#DVT(sL[nPY1@+&m&}Rly r Rt{M¡xyߢr7ėѮxEab1;^*Xޭ,j54^rU!p~ }b$bzWy.(% &|,%m!d)3_KFX~(vWU#T !#V .fmM|Qlv㰱5)y =O̧DVF )Ӈlęyx+}nH/|AW ȚWI/AP}),ZpTE? E3L  Q V-Gso[";YqO0Vjow]i=]lBq7˙`{aއ{!WlB 6=K0 >m[v,h=f}'.(Z #GHO(n9I<2Վ`,9܅j%8ݺ2ZUk`%Dw11aEK넢a'"V1Y1ōd/ In#ũ8Q:ّuI'`plxƞt6qN9`g㊛ 7S,ⰹ%lıaaȶktZNߤ%uƢ*Z6I&#(:$iEVU/ؐ9ь(>Ƭtd|JbJIS~"YY\a4h#1k9BN:?#,c !$b0*$Hˈ1뭦J xmm,P:C<pKI.30lDk#b̓V'DV'/qZqUNG(P&- R eX{A#(ƩfYQQ&u< "G4j$qZ/%WIEUPK | ?(LR ҒrMaLhNgZX-3!kK,S~_gnP=H8I54ZDRRqdƼ籦5SKcmOcH#4u g43r 0P') 1nFq,#x@sM$Q!:.M`rla+MLDŵ4FyneU)UWÈ4hH6 4$_'AЪFFDMV B‘(jSO5Xs@_5Zzcҿ<d[J YƜ `C͛biJsp)xĽ*Z5qx]ɞEŽ x_Rͳ-Yv4j+W׶m:p#y'CH+Piu|=σ F6hkVC4ܪSiFQ+ML#P*#̦@@ pWQSNHЊ@h {/Aq)T bUWQQ^\#>j*Jw}G !e3lP^@QεMZHC P]k'϶k9Ct# Fېu[T^DKWMu#@5zְ! @w*j ٥b J^Q[0&-W=k11iw2hE|niGMW~I6؃*'c0n2YlgZ#" E%ۊHT|\40h^LǺ\Lyv9宣rJ0xD[hqykm($F[`;xB/垐[BCÑ~Qh]zrRVGZMnܬW$N ^1MiJvw=~?B#z :(bt 3=X0ahjNCL96+ZߠA^'d㮭޲E&RkMEe@TXq٦H/[:PZXЂjB/4fiPZ&A)5qEqԠ%+FT$8 `|Kn"v+RuZԨ+W]KԂ]o"aT%X 0)jMEMґz鈑jAEPM0c2A[$Q jY0"z#8B'LC7y[Aj0DG5^bx_ÀTėuQ0hwZ̋457J᱑@>L,QWO@%̘8>rZE_}CTRe A(Ëdj7OGxb-U%n&smȻܪMh%dFEy VX0e-oa8QBsҀn3C~:#cUƱ-oI+Iw[<P&`"ؠuUX_Ft4N"O8qWi ;zz!Yq[Dc' J5Vy'1ɮ E@tSWG fiV’1`:'@r{AẄ#Gp5l1֘sN 5Jr▥B먈u*s8"tթѳ@^GJCǎ"w7FĂsźۥ#ÐY}+;ѫɰcmhŸ`A憡Q;.,*Eؠ\ۢͥ4i(,}̸犁F׸Vkl- QV~E f,E3wi  JvcA q)KPkː9JAI!y`zQ=ĨqݚƂے2] LBnlOLy_H%SzVfZxlYD=tʆd7 C^Hq I-/0W}K#3drkL'-ʓ ߨkKIj=~tԪ0 -,_×`4@q `v|U"ģR̯D\IdD(cdSW9ץ}9R}܇RIQukoX})Rn !NU6ja @DZX; r%)1R]Ur$p,UAJ>Ĥ̡a,+/#`2oV<ur>2#txmw)%2$Uۊ,1"=3&Z?8_~Z -Z=B(8jcO&J`a6$mx9=Q0jZSCĢz,SSՙ3xΜX?'HāK>֢Z܅6oHG6w5 CTqtH!s5I@UY8N)AJ%Szt-rэ0&1@]j62U$i$DѧG)z(ѠX7.Id)Rg B4mInr2 'Ik\Ru$2Kp DR]RpRqc1TSvˆ6+2F˜ nULJ 94?a!Cmڕ1m؂Ken7k8$s.h]GU|&4;t($ZLq+ YB[{$["gĥ@nS+[Q,GFؽ+M*ېnZZ(ps_tLTˉc(G[ŒAbu "ꙚABTۄz^Pʺq^g!:^ēk;6C+㒓[t2gYblz>JZx춒 Ō6se5Mi%_CtcB$A&Қ0@6%*EA DnܖֵxL;āP'KaqB[K_g1SDQ"ړ "uS8yӪN;ZYi $rUGK-_eI=`r+[:'AgN#hh0ıg$ᅞ7c{ep (IT7ג(,P4" n#1AQI1/kBvIeBkbu &'om;;qAxB,YF-dJ]8ha.HeJ]ZsWu 7:R5D2obHٓW; a((`gV'.s+L5hRcmŚA(wD>l*@Ӟ1}nĵX W ~Jٖ,?hdÜE0hE!lPOpb׵ucZ*mucS¾ ):I3auT-ݖ孡>WAC65ɰ!zb`#4ߥ-VI(qDZU9+>jOa̹_oSi4h=*NgEms5P:K9ڞB8smILcZ?048vZ mKl <S 2 G,b@gZ̮("d7 LSYJS11MbȱqN a*y$d|qͳ& 8LKwJd-@CvJjuGL@PTT5 U!CfL[i]˘v&t2py2[- X)uL<* U%L V}Ǒ@$PCV;) eàis%Ku槑Z.d .',M=[зDϱ@-s@S%Bk%ZٴRp=3 [괮qf:UKДUUqK\qPD ! lY<nz@t ykME#DP^({1&e'yEPeHZq[P#a T#e\<\;☽4 jF# 0tZXJbDBW\G*RFl_wkD="HGIH^8RUu`-+PXv\5oɧCX] 4ߑj-^;"ՁVĩ),ȐIAh`kbɐfqYآDʛq} +hH8zWC->nDH#Ģ6/C"t'0 m"\*M~$e&_aHl. NTz7 l |1DP - qݠTbVFK (R$d虚JKzJںPr9)q-JhRVlWJROH@,ʚ365w#Ě7;'˄6v[Yǘ 17 N*M^5Zb1!ƀ)D:&6xaZrI."/*i& m Q7Zʣ%Xީ5T尙I\N  |;ȍnh$ӊlA /Ip2Sq#ꭵy=yzxpۄV77X%Qr>3Պ1#p2XL E]Aܼj'RYK|9BD8Ş891Kk8T.޵x⣋+>xy+|?TA+n*VUʷs+^PE^Ž}o0 >~Ź,ӚcPk˸ -؏qph=>`˃I -|`H)_O߶)&7g\Y1*޺xNyNl> . >he-ap885wYswqZ?[85O_ ޝ{orř_sN+hw/^>wspS+ W>VGx̉S' Ͽ{Ɖo_sʷpsXaڅ;dKOX[; r-^ſijN~w:w/ҷ>ܳm۞s{Grb7Οx/>{ldӧ8n]GGӋȆ= .[<|ˏ-,,^s?o?q{oz{{k_;/ϷKcxSww@Ҁ4}SVk?O=vOֻnt~z^8p{ w;nk۾x|4wua=o;-~!t=t=g,V8i-.\鳟؇ z׿u㧏_ϻ?4W{?u}vE֩_Է?oﻏ~CG>Q=w饵_0GW c+|_+G04:i©/1@ ^;,}j/hE;wbrUGl;I;|UǾTeptGF5mG#ध/]gط|ً^\:zθb{:ПM?qh8|8'`fWpGŦ;,bi#;f/=yʥO&81\~K^]͵d5ദ=2ͤ{ʥ>?7߷b7ysLJ㱇;o?߹g}íGإg,p/}*UX7K[XYZYZ}Yzoݺ?%_o_#~7۶;=q]?ݥ/?/AGz׏!9}Ahlzڦx M ӧ?SssSGn{~š+OgfwГ|KW.nF|Fy[+7>?c/jG$;>H?Wڗ;M. W\xNNNxll1}w\d&ES3|p[8o=Ku9ݑY񧎾xgMKŵ.p k{Çxɱŕ?n}fEk_`W_M'Z^]{wm=}:{` ?e[߲zm>>;ta[|tmo$lYdML5Cϑ4p|e}xOTv]|=v♗?eWPmkNv?//lF`V4 _ρrOLS.@|OsᇟZbM[ou'7~?K>M-nJ{yy[gўM1GfgSN΃: \e_M=K[ꣿ<~7,doW./G}~vŋŇo,+$rYXmyx[>:sqT7uC7Æpuţ kGA?Oc)d>V0%訜gI){#،#Sss>#џϝ937a#ǔ\Pv٩ف#+,;-a;|D8N9{XÅ C>\{o^_ҷ>mӞ_|?7052wiqgK7Y[;3y=YF43hv,|satwB=|fggfn{~7wߠa۞t<fëQ҅u[WB4:t"o۹>z>52e-7ΚSֹn|tq ӁC:-k6o-~ptaƒ¡,z܁kg.޵{\?՟]sxr~{<, jd?}rڃ{893rjcuv '6HcnSk.3 ffpyx3g;Xٗwd7qoZVvə;6yѭn + o:o;._v M~Ad'}Bɇlw_0z~]vMޒmĉ 0‚`BN!3y*ؾYUT[asΉ\An'V7ywtϻEywNkvpn#|p=}iɵ:|ɵwbOl{#˴m_2_nvۋ]xv|ynLi≝[F#oomvo>uӞ9;sןZh&fq`SGf`'Κū2N~96>1(!8j2__n3s(76;u?<Ѽv:ٵq {\gWx9lȹc[MEWw_}''g8vf~C̚ɩ;^;389g?s~ݥ{OOǝGJ]V>b ~x7?~GuuU'˒xe'Mlo{=e7%IG(-PJJ)Ii)--R(tJ$,'^i$8J޺{ߺ?9>=x8F}FE.<"zXF*e~R~82m9!1" [sw[qsC5-6-pqYs3G1ub5:")d#>ؘ~2TԼˊ6xN~yu,˘KڙdNRÌ4+?H]kz^;ܛ'(+?$'pDcJ=iݖk?h{rX?*71&^z/yo4]$/%K%9UYy~zll]MP56656wn0eeVa}慨0 {L14OGUag{й2|sV"~A-CMe%Ey]{G'G?iJʴ&3GpU Ȉ;Ȉ ;0v5q'ES,A8*r'#8e+qȅ 4s쌚 >[T.kE]{g+?Y1>q&&J'|xޏ Xڬ2FS;#B̑Ԉw>]u#O J gCziûɪ,<.b* 7p VQ6^^Q #mQf4,Wy]%.{_=6s||t.0 ;>hCmɨնaIP a@{v)*EՇV GWƪyZ-8{͍x3J23 xV\~ ]-Vu?ND5, :Cu{һ{;{>uGy |ZqLE>~?=_h$_Ň[3&%9.Q(K hጽQ-v4%RV9gMR7(KVj# Yyz`ŎdNEѬr,M}G1u{C7HhZ2h5QFaͦ,h`1Z&ňd6J vCM%Xt"ޫf9z(ZK7ks7}ֲFﰵ,70Lʬ8}ܴ\ 58\T.oHT3|7g~D*8$Gp[`Q !URq8w!efd1h{FWzr]zǒ e|P` ^},6y>@' ΨGH !e-QaF$T6.1*cUرW5%beNhs#>gG-碧`O35ux.%_ٗo܆ttY2ї9ٲ>h!v;X݃fvwL; v\z|Pבݣ,d;=;IA UPPVgd4ssQcSH۲|KG2❩$TG#%vv>ʝ2*YbrP(KJ2Kڙ\=&R2jzэЁ7_yǾ`-[Ǟx7‰-p4i{O&NKPE \T\Z uTtO;?ikkhk_JS`jVnkhj^I3?DAKٴ%Y]A亴*#Y-"&fAd#!q4y $#0rօaLFTVXmy7G"wY9X#Z0"+pajcE.F}Y{ vs}= [xT<;Z`%e-a#J"껪Cvԝ]0^Bi%"A(Ӱk,<_ëk>iX?}wd?^^pc׃ #KHFSg#wD#EŵI+֖PZ\]ZRREY\D`o=@a_q삅%%^*l(|#Cexd NJH+s?nxE71XƏW} ,ra(;I ^Cut:WF|X4U#xa.*GVat8P1xP4Q"Jm)mmVEfRdK-6BVi kZ0DspyZe"*,XPJ2#?mY;jQϾ-EȪۂ\Fd\@rڂUF6m ldD~yJ_oCa)r俖(6xW/-_Z ȠK..^"v۳58;u-+E Rù_:u}]]Ģ{G%pV_ƪm[+Gz Hn2za0Wzk{ "(S< |oKmsr.Q]g`->[`j`.{.9$A,x`;ѫnW%x=>S,QuK.Ρ܄9[M,Ϡ5Ot&boJ6ni{n"8hvpc<2"l4 U-sܡ^ȐG*#2 1-*(w-Ö aրm,2 űU6/g9Y7u-yYOq,yK{%UqI,npߚM5%xQ #{z:#3Ta!n(h!F2qA+qrw= <2PCMC͛V߈0a &1 /2 >>2+Gf#i?.;KOMi|2cm @>ث7K Uh$''{`Xv }"oO(фYmq`LJgW0;tM~e{Xc{UL$?GQTTKYjJ˦nLQKĦsȺUl7/By2Eu|؈29"{RdWM r]Ѵaw#;`1uU\x=y#WMQ,Hhd;If /,^_XZSRnna6sCU_S[tC"FS0n#IJdS>"WU4ty\βԑjvuɊ+e91_o֐_#ufBBn=,WUZVZ=@" %u,B8vFC`ÖC0%t,C+eؕjJeV.[h`lM$_[RõfB"pe9XXc9T8 znk/l+* KDKEIVRcmlS"T%E;_<`t(*9Q,@z=nRiĥycI@ƙ8r ~ҳB܈8!~ho I[>?XGpꡣz'˓"w^M3 Cazfga9pM+ܽa3Wy&x<湰ʂt:O#{4̅Vܗh8a5,7, l,s47;-8MF`êb-\βxxkYK[ͱpځGgkyᒯ:0 ?|-gh9p*@kNZ!ʈK@?|ֈh>0Vp6X4aRBbD i.v /;(nҳ呔a (wvx%jAT}PțaғV^4$?L AϾ|nD 0K a,E[Z*ehKK ̈́7@ aGl'J ՇSSIT1 Ba}TF TSÔYRUZxw`ZF]xZN1m; M0ٚnP\ |zKMصw2~cN7`+g A\5#z{)L9PmΉ ; C7Vxi*CF(na㆗yAIY"2]Z'dT鰳Zx;bA{ w}OCQuR@5R:`ƞo>|ߠRm'B~Pb(aAO޴xMYI*%~!$k)82ydSɔX9l)8I* (xWQoܴ6Mq}WW9`0cLJrAnm,n{ra՞9,,/] G'C&@6"-,Bwpb(?8:jCX-h|4481tcFsi Zwq8yshKبEK;60]PC i(75l$ Vsn5w=rЭMc*#zG%ZDє"i45b2Qmxn%-0G"XlAűłʼnQeL&'GQ W0G5$kt LMrpGcaz/xsJࣁ! ӣ^ WiZJcS/v >7?zv4^W:ޢddttP0o̎Qѳ#'9ny#XߜUO{~D_{j81zuY/_6xR֒sH:U EѬןٕk>?USԕ?{ńL?UpQFZ_J[\;[LbE|hhhگۺ5UJ6Lh$RS=K9)=QΦ RU)R.^G1;>^SV.URUdM_?b` T0.W\ Ɍ{06 I*`xhlBH'(uA_BĄ&M[xa^aѣ$5BY( Fn$ZDj,!+ъ>Mʝ=_9CZ]A;9v\n`:E& #q#qj7XΎG 6c"hб_< Ѕ4l,ƹ'|2/C5c#ѧB Oa O4'5?g7X7"J.<0L9`ynkލ,~THibX|H)L )zs!$ x:0xD~~)2Q 4iNj QiދwՉ˻SKg/ΆԷZ#ӡ9QXNv2 ೶~~6{K=@! ONRBkH5XH0[yb,UP]N!U^<$%pWOiRDžyŝH)0h7>~FSS!WK/:i>KQ5R:>Q6|(N IyRH e|`RkP4-M)&&)ZUMQSSSdKDG[ -|_\MQ4kr5wOQW7RJF%ѳ ⽽[4dp&퇲tzܙpr "?ln.T]WW[IF13(/ؕj7-yF&1q9H zH՜U|&m|‚t;{tB*$SWgCP %bnB9g ̇*`L"Y졊T;|ie{á!(ȞF%a9#Q U2a*+:+;bEiq4+%EER+d8}K ṳ4XKvgBIvd&(4HYNfh'mi%sl\'^:eEMMq)hԈ塡fts>|$tdd< {* b+z3-]1<"(X`n%N XVV O`B`8C\Uܙ~891 Pgѵ Vd8^H 8E*5_G9nQlWE%bU1'D]}r;@KGCIH5TmFZQIQ.wIdVNA|QŖD *I0R7ȵdhTVAN"jEB@} |O2z,/hcgOo"'syM`iA%ތvB,KmiNIbigY4rq92T+*=24d]'\UNd}s!0s%whT fB}9J*[3@#ðTy7^FI\Ó*7TǬܟ`@Gq}wޠ0H[ A6:ltf7b3 b0Tw9]4pRӓ|n;Wic\!T#F&C O4(4 %H%6ovߛaZIFYz)2l ;hYci!3 PîP *uOnd IPqJ9S a?,T;JvFb3,da!U;!#Ŧani/O`v: 7]n8s06 jp ztDd6|EN2 z7<"yw!KH dBȞx=p|8%bsvRlapVw'Ws+r:[e.p^p@CK7T "A+˯> zo0xmWߗĉ;P ❟8Q*Jݸ7YR ` MwTn>\K+ډGzཅ%^?z . h;ֳx ԅo+㺭5;~ [ p1m=np&B\/V]Aՠې<f*Uz)%}4G jߎ>@$ `nTq c3'ٓg_bb>U_j&F*𭥋%늋s2^XjT 𙶦ݖ]K (AɌxu=-b`٦:7'nLٰqQY5N$N#r x+ onPgDs-x9x!'[xe~ӳU@Һ.Ղ@ ё5.k~ۅ''s>RXӥZ}݂AD~ q1m!'1$[G)ivHTx_QGNl% ;yD@?97CD ng=-G\RYQ)'2Xo;z|3Z\7=৷* 5 uK*()-Hj>qAO0?/- Uu* 1Orz5) Y$~#%Ѫ_sJ07|.̙{U ץ(ў[Qb@eaU_hHWYHBixe"D xNtpA~i'4}Of)ow>O 5{G믈lnˁ3;e]Z54y{hί[ +ی;޽Aq¡DL `3^5t!A`dH >N{'M|ЌشOݴ?LBŴ[mM1_/jՐ A@5k!` fd#0.(Ο8G @9rTkK?e+Eo_ ]HQ394m6@-B~RPRTc@R?Oy-t =קf5g~UT/ikS?O!~:zc҅xS9o8Y3K.H(.,k{j>T/n/۱s+mTۙ3oCƀ *-[2&>ݿNǮ0=,V-jk"V 5Y`G_f 뚛P0llBfp ~WbnW kQQ)? /_1p݃~<9ztHnyxBv+nG5iw~q":ON~tDgrI$:@tDtɟ>cO+/=rѴI*}abl޿zl6d e㍏tίeܺyv\DU_ŖOZs-6k=_blM}xpI8AcqN~<'DlBb(݂. {5UO<),ıQ699i\e9NOAJ=;`xo9*[t67^"}w?bs ./5;r`?&,A2L%i`ؔTkk9Po~ 7pllX`ɯy @%͝k^4 !jj jё!@;(jE:E$#ߖ/h*Xa`17wY8v칹9*eej ,~i$QQefjbY!3IՒsCg+LJ7QӊjI0*>$SYdYFp_;0ϼ,*S388>+Yگ⩌'2R Dw> _Ϟ;;;55-ŗƂ;%ΞO(?z<5Hy)yb^۷mßJȎ~!'.-ȼ Id|8zhd}f۝KCwլ.@ JɒSWm?t1=cPa jpPaAO>qCq1!;H\3'YVq$y;rȂ4~[ЖA7m.-)@i) h{'$q@$KNĎıqbKNGzwtPg=tvžTʅ6L_yU("+ub\Mcmԫ)?E9mGրeh )T/K@g:##)xXa5ޮK_NBʀw:TDJma7YeRk|5gRRO!:¢dArA· _ ȩi6pJ $}RNM kL8Te|Ӗ?|ۇɇڷ] H;#r#.4%]}εt~<2t=yB4Vjnjl(\Wum6;.pwݷ}2VWgPVnS3={ff:Ϟ&*]Ov))%b4y]ӀRWSE8ŐFg=$32%v?q \=$J.[Gfϱ<é/__}4sxvѳ3ׯ'M:}~4~Իҙyyi8;~~B?>T÷Bjk[m}-pbE n ~9!e%Vs?Dc8ήpVVdw8q _wK=E.fջrMs|3g Ô~o̙UW;O7+p?G9%qE R聀T>J(ZY)_hd*SLUk,hZλx36*(Um?2UgN{z }M[B>?>:.o;B02\N8u,"riQm%ԕ F]iUˋsr6^Sbez#U*]TL_#jdRuL$5dXIHrbNCI5)ڗk*-iHr@(N&ݒ6ٓIܙ @Ws$ Ud:itxXRx@RBj؀c>B!r ?,mv_V+ 6CV+GS7`id} ]':+1l;Ɏ?I;HH<axfx)݄JXN${ ϜT ®X㵴&(vx*j  =0ᐓ/kɷ[.Sh:$f[ 5 G&b6H:tPP$L'u'NEjp܆%uO%u)g"gfTxo42/?oJwz"Y͉ȥC ɷ…B> ~kwJEB(Aebg|AS!‘,N ?8;Üuk2ekn0E4yq, AQJ(7* upNk +qw1[4MJzZ6՘Ci2m9/8q2;tCh07$6 W(!y`##HYr!$GɌ's"ʏեͪL+JjD7 rO&54 b2 dpu%`Ztj@+nBfq-Bd }ЕL·C̹n}޺vflg o{Ͽ04%.ȣ0%(a7^HOyl=5Um`ܶ/-a? }.Em un#a%]Dn`Q&B3ULaxpA Bd>.p)@P̎@C,gv7*C6%:9A{su\KI~v۹sV1vKY I f<-3%t$W=PSfBTޛd_'rɽ.fi!r(>xa O\Thg{@01[$)I$5)A149ΰ0;"J%S0uM' [ > JH טpfK>0Wz/OèSx@n.w=c'}I^Cw_ChCGȀ"T3 ;H :OiPeuf9av5>B1+maxbIcVңgs䥌Q2csO W̌s#gNS 8E:ۜ+V(pAd¤g>45~T5 mgtajjVUw j@FzvMhΉ9CIkv-#8C^οpX=m[BP==. |.ֶI8CY118{'#x<ٓa=h/ [ؗ{AZg{4}/kp@koCP+==77lп+hVQI_ {{h'r 4y7܄i GDxO 1b $A_rGr3!* Po:cGEuQ.7 = m_aXO~݌,[}! z`~ m=:7ܹ$0  N3ѣީcgԐ1ҳ::ufa ZAı=Wds}=Y2&+!:ctH8P=?B(1Za=nqHv15YQ !9z>#] #`rZKFq>5cV-ra~R5 ^(ܱ~ 844X9H=wO`YVdj}N-! LJc9d_BF5g;dRA*=g3Q#Tf;ۡR4,Q{߳{V./Qʧ eG=v!*P fU>whᡣGΩ 箨ѣ'ϼ?MNCJgY(?,~nW Αrrlyђ9h1ޝLԇ˖!& &zz 6*&k"br!5]I`/ +[9vgH %{l&Ad{3 ) B厐D,bЙH89wcD:a06L' .k# LBLw:$=1AbPM${ၞD !ʄ+A f8"љuΐSNL: xxO ̇48_*5Pш+*D]VApL$(; 9*Rֹ}/ı.KA^DτaC:et^__}XBgtIp&'j2WeYȒ⯛@q!GTVs8s*v0A I%XQ=HbK"僜 H hD (J90!7NhJ:E |P9oolBxP{5.YgV9q1X2ۮV[a󹛾Zrx)ZNuq O$ĝ7z1M{E+\@˝Q DʠBʀe8E =(B)Ad]OJKm% W|Fi=U@)7RZN_r>.ϣ <|p?`sQӔj9|x轑gRxLyz2eIfD^Fa3,YܐLFI l'r,3pj:3~Xx2Ǵ& ڎV :TaD E"PTʁO몫3 ÷7uC ۧ(@I`n w\+ٱ²j,E֬XYYb%;xeΛGpEUb2nL>BQKH9RC6̊򘙏1BرdB\{pB0¡80H |O)"jꉦI96%M`[vWl\ athŎx -6|5+|>$N/#l8.hIf9NN3Vׁpxa'q$960E+SCˊP=ɸq5^=_6w  &rPHFo1& Nj:AvPǬ"Ua}k1ᯒJ8&؛Nu /8,f;l[d2e6>ׇ TOXo!ݷ,ⷹ>׿`Q@i0EX) ow@>-6t *W plvHxčK:!2Hbe0GҶx'K ~I¿y\,mEp`njnmKn+iq(m2yy}r(wW/YBQ}{;;;%>@8$ Iy!}c] @kL)E[6yGPu٠ G?28ΏRij.4Rd@qu CDcwvr!Ջ/, +pb@ȡ:5.R.թu`XH.H"%s]K%^L ѸSQ8^!2&79^. D90pT=j8:LwlSqӧZ**++*J"SG+:'_41>\F+)}2ib˩(QU2LvB4e*I,G8ESbp%c\_Y`tp1oV}YS>gFGZ Dywr>{5T)QB@ UUX~}ñHYd*HBQ5R$<TbB!=+s'/B2C)Uy_TC*SzĒoɱ[n)+(ǻbPQHZU1s'4S,++3dr`UXmP \&bĔxf;,k]}ZJ6tlV57ubWor"z>?? W -3Q-r,_=^[cMJ|YXxY ,K/W84-pi̫m_ܸ#t45 / hRfOg\@)9*8'7; do@xFbb:panU-xͭxla d|v35Cw1zkovw7E뭞_,WRjgᥧPA$i\LBAP%WfEvQFz~RWe՚(}=}t$Ӑh Ws~I tV?Z%M|#gaWn8Q-ݭyirwɷj.56G/_;~Lqz4'4me^B7u0}6Fwerw63,Úwlu5.^<ٝ3-u:SLȡnwVmqFZ'( Z'xgimvfw£o8",ig(aUҥrw #@j, zdw-M6? INwݥ'|AhCϣ̤*XΌ:;xPC?L>{HqeS $ iT|:x; aSX.[gH=:Mlx{8ݿiD[멉//(<7̸ׯ&&Hsp`aeA|j;w'Z;Zm\f9c1YyC;9,p~lV,6<+UPVȡ/]jk5ߝ;% ΰ[뱟è(U lfv;x#G^ہXMR9_e6nߞ70PVBwK/D6c[Ql4v%5nh޴3aev31o wݵ<s{ۢQ31K:J' I*Q>51*Qc\Z(-0뚛׵ylx9CS\t.wdp=xd.Jox#y27^YXx-x}}[c@$qT >CQ u~ROzG#r~h%>I!?5Aj0#: LGh%ÅOš>a׆Z`2\{&{y:Z{0}#hǷ'wq~}|q o`(~ȍ6fHeݛ1]:}mmqnc(+P(#5@F'CZ\CK~tEyob$jLlg7zӬk#!<,1-Jβ~{и|؃?~!Mܫ:ϗvGm55ќ73PXԸ) T0.J:ݶ--fKq3Q]FNzP8F"J u672]̚|W_z/.]Q%2'.=EN%\, |UYyF4ΝC\L 0!]Oz aTAaQ1QK~}r;5 E}7^}͝;4`vΝo]wTQq>!b;N ;;rkͣNv%Ar\ԍZZ֍%tI1yeJT]<]N{tƂBgԢD5CEJ>k"2-,.U kh/Z Xa>lT?y>Q}5u}Ъo#G辻, ܱo2g)Nas\hMH&x 䴠2fMa4l ASG>uhܜ(oX͹(upnPCZ[+X/ ZM?2GE m]Wq&E}8V3M= MF/3/\n)*2e YVL4 DL~@u՞Qmeů2\wܪv .Oa(oۃc<>/2G8"h{UҰ[kÆiU[}D]422`זKaVؼØ"}DW0~p2\%)J!־Kڗ^|IyW@知CEM.*4)qUPMX!!0Qe ʐ&?R!Pɏy!;/,,*(( Tf*e((Pi_T>. ~SϽsO>ClT}K'*Ա#{ꚛ{[sO~@==RDZQ*-.)*.ώt(RbN.6 :ŀIQw67LcX G{#t+c}[`yZf#y}1a]U4r~CF:p8r+qjk9)`pA1,#%GxV.GJZѦwk;Hr$~pD<Ɉг,볭[zk>b}Vh%6ª,12)Y8t\`8,9-D3,V"LPg#5=HXwڥ_(ilumÚ '7*P]FOG Zu|.Dt> ~vAS>qAj9~ wK=%Gr޴nt:6շ]|}G"o> c.B̝Qnnذn^plcE:5Ks(6”L (y:xaGޥR" %ĩxM%D37d1v31`rJd{R#4( iE ]=vweR<*_Cpjڎ.G4u4_SSyyvqRTw|;@pEe{=hLL3j-Q,a$liDZ;=|򙙝y{e޾{oE֛L7'>H[tzp`hph@œ١nsb_6/QWX'A_!#zAWp⤏&'to ӇuP:QG%/, |V{t2Q3ϩcOdN$Y?4d|ўJ-;4 ܮ ^ z¯.jm: sFI~+^cŒ9: V:3WCPI韖W-_BX%&,bprlLO{lK!$4Rkt|mMRCWYܡ/,Z-PSk)(5Pkʪ=)kLjpd !6x8_ vZ`j|e«H` +}E6V gk "\ Jy.`jF_ PI ON<4qZ`JS0WiA%EKh,+Fb+I_U׸AǞ c:GZ)A7))5=E{%@qJ1U٦ )bVj,@ j0J}<+T J,jޓIMj޹+Rαq̢SgH<&CMLPՂR˪y߶x!-_ 5U{r"'/ L2**VK0ޣ{$EWx6=u^$u)MWu|֞1#BT.vesf~okJ?7LvJExrwϝS^e)D*FS W #)c[/qzFR XsTQB5Z5#gFMAͤB VBQ"*~Q@WpθRZwrY"+y!IAxQZw9_QzQ7'!fb.~QIKR ^x&Xr?Ų*cKdsDd"A@LQ-u8E@PBv={^SS!GU<'AuBvi2'=2'c^[]NhB1&L `2z|k4TiEYKYMqIGoJiM8"ڱ)SΧw5 ֽλ:8>R=jkOMHLd1"L}Ï7:tz`j!m9:tkr(s:SV'zk{4r-4-.cӨ'u)43dj~(a«R_m 3,xK*`R?̀~^VÔ1A `95 z̯/ݸt)dr"Jd$=OdSN)-Ů/ih)Fr'Of'暾;,Ms$ՙ4{CR0p~yyZ>f{ syd.9E,kpanhbYj(B@/oQg2QKB#桤Imy7Q Jk ҴbIP7-1DO "@ذq]y_ꁖvj?Q޲څom?ahoxFt`h3ԉ*+Z Oi 3&yZK h"k͍˩c $;eM@"oz`]<Ւ|lE o'vU<^1ȬX9nѵh]@7$]S] D*k<~4Qx z.zo1eg'pnŊ #L`NȳJ`!*cAJRMA˔ҩTɉrMY"lнO>nB`E U1+ԃхC‰d UG4#";KO#?1L\ 9^psmC$B<58X |h++#:F#1uI]*Xʮ!Y, 4 ɐ -lHծn燢8+N#hG 0f/ rYg,|NJ|V ;'=PFp4HwJ2;AHpyۯ'*з0PpN'X306Yv3@ KI +8y^%|mH6d,sۗ,Z@5kYC` <"уxՊY< &yҕ#x. ~̪sW2BN},+qHE极;aAH J >г_o~jX?YL R+1Sּϒm>iAbƐ_5UlGIr-PL_)p!Fz0Q; lԑj 2BDQHs@6oyGF2eYkز!.-uY͢TalhdZm3M+:Q| ٜK.Ld֟:onƇ?q8A<^  F'2\Jp3kMF 6T ¦`34p"wܡ9IH|R"ޭ}}[ҙ}/,_!ؘJƒG'2lӼg5Zr$Cd<Hi9`Nt&h ]_%Bv8ڱ/V\Aa:piys%/`U dd8yjP|;gΝ=y _~U2p4@#!HFϓ XQarDI. 9HR4D*(;%告e Yl@@ <H3N%Pde H[sku^(ˋ<*}U~5S$DE"w~4A :O&tCb@o'@oEPw,*;a]Y׼?1 Ρ 7D2|Ydud8NP9N;m sW`H~q;Z:;&!fx] WߞIR48PPy+[^̝h cΠiu {_htaLj/xu ic+B>"(.EֿT=E U5 eRd 3sc>NLk|O"T Tسd).<}HqMt㡄˭404ccR)c, @F,s|IW50YuO9ZN5e3NWq`8m;׬ S]4+)-.%/Q4\雝?5LfD3l"S͞`';G3\?2D$"ΉqHV\QR/:G9<SS_-oji%}Hwnt뜬 u+L^89?7uaKM婎AQAhJ!-2Lǃ6 *}RCq#SQ~b MdFE[^HK)4"B׫tΠ d`;N~p1 =+^ߢո%z [=6Mo>W]|2=f}mgOMQ 6X,G^˶iHؓOE %fNñZ[FȹL+I&`CC;F`$:mZR8>^R:SptN7p*~ <"dJa|AM@I|>1&0H}k^ex+~6yR2RU6[}u$N_&o]Xe4(qo~zcKf/~8nz~YRpiwo\xv~XNp*Vf\#X$;*w  j+n~`=rkpJ0kwqOK>Iru宾G{t?AN0^UÄD~*8:M6aL^x,6ZGxu,BQl#DÍ/&4~ںTGc8g񩩮:.i oDX7ݭjJ5>5jD#u 8~ؕBD&qDM\.8iG7bʚ UF@7_Jp0+ 24VuQԢ,idg$ESЇ2':9Ӵ4_QF"a8k{>jRyպv!Q6mfI^-.+[Ɗ[4%2}񴲪* X&FyU~=gZN'K'Y͘=o^׀0L@}jh%x\(1l5q+1b]Piuw|Η姑ϛc.cUGbUє|YWisX*NY<^j˵ka-OCR*l9F @1C|}fMo}є用ex75!2Ȋgmv޻j$Vʋ#vTVU:lJr0TU8*cQ'VTUlӇv Uo7 YNL2Ŋ[p2f_ϫbUñ v(Cּr{%v fIE}痟m]U>yo@dR8@ F1BVZQ@] 徘Vn?6ocz>#zA`@XtyuudUtDH%ړ($j^^OAI:y*ϾUԘi8esSDrJ،h$//K1ѓD3$O|qinS}~ûh.=>Bt|]nte0f--Fq2%% @ ?sW,[4˜9e9ֲR|ޢ.0&*ͺ6^0dc*/̓xWĪگ#N*jKF{,~.7ËiAPe.i?33Y{%Jp=`v7C87[~d"ʁF.&bl-o^.h&_*lPlO a7{vi ꡔ+|ޫGb_4'pXv _Tqb~6@Bx^%WrrA'gY/ qeg|lFS\ +0l2OXS @ ٤^6 fsݺH̺5   |W P1n|€7|YSwd1դZ͟ ^#F[s 9m澝Ĵe{a0iY=D7^ْ cDg;I4hn#T^aK] k`վ4n[H{׮\_y{ۍ͉ `ı@7Zͼ`hnj^[qJE ܀ss biH' +W8U+`iuV(XaÚn)͎c73Tzw{l8\p:_G'<<|\m2\! ]D3ђG1EE> [| _7Yap+:(ЅDx9մݢV?*>>_eYb;\)SJ*G| q*[9UX&˙/z0(gıG,M68VYe" Jm67tx1eMO|8z~;1?о @At궁΁;9yg[i*ò=**v&<Ҝ9pMp^w'ÒӸHa{pUګPHNU ¹FY8hu,ZiCT} G߹|k9-+ յ]C&+=9aFAEG~x/2@lviہ. 7W%={5^v9O] b 7~L<\Fo HߜDr=*)i%*\C<{q4zl⒅n-,-YzU#ђ| V7zX4:Na<8 mE\zH(~؀xtO$$¡|QA4~Tٜ/A҆>ԃܣ}yWd|͙5+>{={* ķG yE@E"5 xE翌hu^~loE2P'4 l ,65S.BRhH" 9V9= irPd&&Z{ر}5q]֡|띷xoݮ#Gv߷7~79 &"9Jg 9ڈ2uGg VwV5FU?izJ}Shgς\!'E^^l:ҩS*y#]SjhQi@S%z}^!%7Xmx x'G^tYTUbѿ_K7}Y΅\ggÞ72:^6q\l0uhc#3>$)<4=㹾UmQ%Rm%A@w5E(A3'FN:0#A׽fLFxa](nkd&QG^ `5{#Fk_|͑Hю^\^(cO#OG&[_민O5鮇c~{+-ݑq5shp兴g9LhjG/yqD<*KT΀? u. ~/X-t=Q }/(-^܋l=p qn0FY,DBRNFvŵ+X+A1$& 5QX[#VL>ս#%-",RT=~< i`yaAŸ9%ha F4UG3?xßz7y"RÖ@yNDֽ\F9<Gτ;H!IՌUtq\e!|J/+;{B= i}ݣ#*w_Y6JLLʷ6T'_Oݟ##gǛ{f ֭[c׿ogּS`93vԪS)3ct &tծ?_Vt5Ѯ~Mw\`$xOo0y6|nWð.hį (af/967WbiYC巻*ù "eMO_s7yck/xϼur-p6\'>>(4cӢmo;<o+'Wy&6u&}xIʀ;uZp*z?6v㬺'w?w<#^?5˷''štgk3}(Ç5$1Gx ψ6֫/л$9~Z/گ\SB*zWa<˻Io6ieށAv& [>o4~2lm;HuXW߹~G{LН׸1u;ڒ)Bba錩gSP*Jrk9=cvb|?^ٰAjX%`|; aiGno&zi{ےͧ8nc]w|瑮\77k4p p,9o̸(gΙsn\ݷs3 65zW@2F=-:0TgjC|# ]}W|cu:5Pol%mqYh@[ݶhhtk7vؽ;/o 8=E7X~dxpol1}'~q][d.7ڽ}`\A Oj~_<ƮKj8h=4o=ù=;bμ{`ێ]v+{,wڱm5/{=?`sT3tw㑆q^;17 h7{ɶu~yΊ4x2J&.iOMrRq\|9rϼu+yOg^dlQ̋n<"oμ'3/2(>"g^ҽg^tϙy$}E_μȻ'3/%yb.cvP}e.e;|E%{`=t]FvZTV糀qn_}w. 1LзǗ<wLVԯ}  ^_?Uy+~h;Բ WDq _ضҥK,*F|ZxNcֱcv]?gTF/*O&mx EIJ 4I4$3rx++G) ת뮻 (@ݴ\8RJ23{&o޼}̛{+yoz%QpB:[X=$MF~22v3)OMά;7Z|s;neYkwG. aN|䢃ݣﭿn^ʤI)Y ~!-<)en$qN|>oQ|Pw{ORinɞYY‡y_">"}hasP"yhOH費a_cC%ǻWv}EٌtϠTB3BD%ؔ_:߅y^;}KDY~띮]v4;V\y`Jlǻb/3S*xU2}֌)oƁz猔t%W)Sߙ 5zoH/w&g鵷 w%/Yήr/>WN93uvި*XB/[/yӿiKz_O* C?'ڻC#џIbIr*"LU}=]}JE4ogxH6!U ٮsdaYt.N@ɮhƪ+IFQ+?K<.ɤkF#g_< v[+gom7y4'n?L9-5#_SUwtvx s*봺fvmy&#uI]c?󊒉X>dş?:xw]q?պnG]t<^\\~sgFI%lTq?S_TU\xGk R7~mOmټi-Omox]ukS0o!# HD\IJa+ʴz* lm.RPkmlk3Vg Oy̸g((_Z8u28@T`ld $  5Frr0(? >r1rʔeoJ0~jwڜNN HJl'RgʹCv2FiKݞ'ffAoI36f̌J{½&9(r߫9峜W h>߭+.[H={b(yE ˊuym[6)~mϼgsUZHV;XH'x"#*Rr(^Tʓ lrj0[(CTE,f nEh,DĂvaklU(˽}d] pxiǃflNtx˒cS|6x:vh)Hrgj \ׁ)*H10x,IPAI^9Pv\Ic:FDRP;2"~VPE)g>y3sߝ"]닁+g:qiz{f`eN'GgI(cZ5>-qC~lva!nyl4:|Bp#OǍu;s=X\j{G\ThzC"KiBXc]:oSo>OmjǶn(`\ vW>z"#o\yS;.l 70wB^ͳ)D|'|f-ғ:m!Q_&ذRTa()V;yXACaujhYlgp^`lx3FgDuD2N2J3"]l"燡bȅV5ϴCyS_YSӲkae~憜izNE١=Z)5 6#񕛱#CA"T L46hkz;e2U^!bc*F=5bZg?FPYCB;0YH:0H#V{uV^l ÿ/;~<%lSg`-)WqScݝLͭ3A5y=53} fVYO}w%}䩇QV֐MMCTM6JFMxԑj$) > ڨHgR">H4bW5JuuM FE?rtIHC#<|g7+VOONI;SIӫT/3 oj+s`xܱז A[{o6/=Kqjy#c9CQpV`f6%r.5R\$F1,<ئiIW@⢝tD,PSjs2 clvJs6kRwud%e)qGJHN 2\;:\BX"=R>p,0p`i(͘jJ̩n4A8`a4V+.ZZ2J 6fմp ߊjp3aMt9(sV6F,bw6fF,K 3>4X,LVK%i71RbGL( T(oBF iXԾd3t KW|` Am}@+aa2T5XJِ#1RP ws"@7uM-ꪫQ8WVUW_5T5kV59: (0 U^-i&R$ݑW4/- #'\TQFD$M E\A$luGe^J4O c1N Ska*V;!)^|/`൧le+ֵ͆훶<ߨsQMYn2߽}cZM-߃9`/ڈrj N9k#wk `1l 4Kkj@g6 ۔ @Q([ v2yZz`70ަ&Pv}o4NVdZ@EiwޙVd&' ^^YUQY)<1CU B%wfj,VHE Jm*@U1e{ {Q++ $ZDzP it$v~?}>8_U@qr5:A5*F"7./0bÇܾ*ۚ;_@F]M~Ѕ6-.OP( fJGڈыvY('6(#@&+pCU2~̠ >Lc6O_:=ELW*+#jLt72`lUL/{>KH2Ef[W/k i\͡Cs8Iao@v8Լ_s~5:l6Fs|ZBLbFª8bp*JJ为%[6)WP ))Joz0 el18Un'ڌh 3We]`|W}6xU=BokO旬"s .8cP,LVÑ|w0|2^1"EBcgRX>TcuCq4:] Җ^/R"JGG履?To'~7^v=Wg_u/_ZTaʼEv bHm+\vKh"^־m +Lm/ڷv0&i(Eo|z",^\bĶ6gFb*: e{l,26S弑ݐ )RzwHU?.q40T3o}ZwVkZYM]!FW妕0C%MsW9`d4IUj;SNHV9N9)YUoGhItŒՕ~JۣzF'kDVTFiW(wuV@FmM[cG9prr G"8,eZNv$0@Ԛ`Ra݁˪ /sJ S}eR _b0b׌#$*u8ZuZӖ^ǹ[[{{oIQ`ۖیY>(W! f`3ż^Ddcn=t9U \[U9CO.N*GMDݮ*A ۻQs_NI 9}g)JS$ %/)Ǔ[W|7 ~X GɣE;KjvMa.\)/*wCm6ɸ*tyk~hbΓ>٩w \TK.O^;l#qޱ qCw''Pd:ϗp6kSUji,0&6Y! (i =Ef}mwGy/J_OifYR!ODqf9fkW(vVLs6UF7_A=Y[Vu CLY蓹">`a1#MQ,ƒ!cšg('ke7K3m=-,Y2Gnf9Js8 2 ۼ/nm榤 kR\* s}qfva'HMgOJt#,hXJÆ荼O!;-vhIvf߈a5zbw{ofٲ/J&pxĨGתߥJA,hd9)gbvfe7G3&sRTb^.vE_ɾ~o'KTT$Q|`@Ig ĂOaapxɓ …Q8&B!i'G [t "0 v|#pH ,i_0hEx3UOy!BWuϺ>ך"cxeJr>/v6i>vok۰a 4. oa1 ڶaLj!zj~Xoyb!ql74ItrvCVS}J6S Y?cU(NaĄ<%e-V6![W9Z8AL&ɷ"DfabJo}6[-InFfjBtҳ@-LtFHUCԫ;',a=F^9 {„D4|:bl|P̿vާ` @p&8[i'%,{t8VUWW̩(o|Z-@AKF@ϏPFޑ́ SlP3!XRSMjT&&+uI?‚/$0t/Jy/ e}o}lQ"`J>ˊJQ@[ˈG0snPx7ڽx2b oDNn2n/νhZfƀ)c )=c!j܋и`#X_w9Z]9cջ)J ˏ|r}5&<>_̓#0\;wND*y!Rt^nnXR 8"[NJ$e84}T "g|S џ_-ߺu#$?D7F#OpQ}qr@r"$'$d=mukˋ^Lξnum;zucׯw/Dqu@ᝥ!k@;Zm,mLWgUȁnTϫcnK7Аث^Y^[*@[Bāc7A\б:cZg8 -Bùz-X,`)l4g67 y{?ʟPcb!71a4 Șuy#9\.B25K^J-pmh0NĿ3d} T Q I\rx8bNpHt7 ^c,V#w/otgXjgNr{;r\59lP0f}ݝqj #NwB^+6I ޸w.~ղf+95JΡ'zupSAfS,6rvx-UXTyz}Jt}^eWC{5U Uh]aCo1G N޺Gq>བ#g +!a؛p`nHy8 ~MpH8gA1afnKxGuvŽX !/ŀ$R߀zX7l#nY=?Ȁ7Оm_\a~|E]6%+G*Ww1*74;@ضn TwPe>g$-};:|`kPԅU/1$n[0zm߁.7 ShG¿0xoOM Q PMNY+9̳VKWʰ}x7Npu^oo__o4M^IS0pkh ZbBc^Z_޼7IñC)7PvsVS缷ֶf^:'r4'ͳ@Ur{AToOkOFEq# v)=.:pPKF&[x@8ri.5Sh mmzEZX%J8 %Uj_ u[^޴.VfVw@' bVwj_fu^rCkk[f'9|I>$m^zOITG +l)~/AّͣRsp3Mfq>"1P3Gz6<;*4q07v@CGX${LFZ_~ȓHcAM S7 yƮ(o2 0j1-uj5o5[ay[,fف`+Vy5Mnkxlj_RmX D.55 zX;cevJA i]0ftY{3SӔ|TPh_K= U&fXƂ@؛3Hi9O<ΞΐGAaO!x<;<{>IݠG>*"zƾjF~574FX3v֦KCB'MOO;N 'x|ATr>c&LsHF .Z|iqtιvXxfrFjХ+Xvޝ4_9!`frMd#vGYҽ'NN/10 ލrý 6 PSK= LX9̸ EL eitz 'K@ T \ct֝P9s(9u *J$r l۷OM H%Bt^y8Oܱ?s0E}=~ZefeeR#EqNNnpICc\GՅUZ"w|(n܊rz*Z_\I?1ɉP.V49..gS?ݲ 3,/F7ęI] V zGc%" HWp@ atCWZWm4zUWUxF[zpFZ%+Ǧ:nwc5fS[F`(Xci 5k1`M=uaung)ywY/x6_v 0`6M=iGঁ`U ii̟UEhAn-/? ,ՙlk^p'Gg^<@,QPPjqRjyT1A_vpIc<{`aQxE*z3 (-)||^Q!`aWVƮZbQyz-ym7ԯel8$eW\rKtʕƯ\j/ܪ5>7eXAU1?4p%Tq¤`.LذVR)5Ь*x)Sj;`꒩qd]r`;P]1Wr[ su85D%UjkMZ,M $0rYNjarK[ fwzƟt1!dg0MfsKf Jbܣ l}WqϠ+sk*~#& !fA+󼀷? K>s@B b¼;2zp w늟 <]:Y9kf"f-U>z?*dt.s.i5!s.Q=4Dq('>URՑqZ;K8&i dGϚHemsIQ_ͻdVcX5zJ˱wΫ3д.wUEf}53t;7L N[.q \^knA-.%lmoC>tbXQ\ۓ)pvWONoJ\X!܈S$-]ƜA¡̒)Ef0VHxse!1{Q5uq=Yמ8r\bK:O]ODcqs/BQgge YPv4gΞ8wR#qX:g_&0']BBHG4La6e[_.9(b90y_{{&]7_w(ULB/ߺ޿)M K" ʵ/MAb/[AЌjИx'X 좫6bo:wgup*"E:GgY.44MRkbap1X}HFYUGf ͘z6cnx=mGj,c"Z/Fb6ۼ|yލɤ-&T*"8B)yWTϋf Y`]@H`l[eZ2%@xbh3o` 0j K}CqgLI%X|%XbL˿f9ҢoɄόZi GERL)5C.jeטj+u^rSY6q3diaz`DžbJ_bj椧guQWDQj5 mz88$qIqv\q`WJ 0&!Bnwv|{3~fvofb~WUNjȕ'(նr~w0dE.īϡ 'Ƌϟ|t6箞;et]Z]; `C/En$U$^k͇O>l nd\@ :w.pL25V?]B3l^ٗ~:!o& frI5'P?]r׾6y-GaxLNwL/y |+HT/jqDTՄ4w C6:aRn52p7_yͽ9*:p!Lj! 8†^`!k k5Էϳ Xղj"3-v{K,nԶǪI mȃ۶=MW1v{蝹lIa~W J z{xFU+c.N~xQ&P4] 9z,|Bb:P<2h2v$L, {9zpUBA4M ^h'H%~ׯ^pWSg.\>Vp7mj~œ= վe3FX'uאY`-`M& 0Dg@`I`4~à0~O8&Ot^2Ss៉1߄^7_rg/^.}΄mɄh@L?vcN'ZgbI$6ܚkif֌1-c2!igO}F·)^//kD*!!@Fp>b2-v3t% ed-ĸX˘J.^-\Mߴ`[ۉ]cry5ӯZX\+K]䟉i?_6AnWשϦ!3Qo|sg7׋4=G+Α{ap1m~b:}MNYln뙽خ߀ Ep Щ&N$^|I3]W{ 7jI=Dt?x^$!)y%u dz3s_PovBBI'O%ҎW^ٱjI$Z͍ȑ=O YY[[7k6~JRm)rVڕPGr$r5QqGty;td@- kP\9k_>*WA )N3MKhׄ&ХQnМzTXЬ|x,M <J*J6 <ifM{YN?qy@ C/w1:~'{rL,z=nW/|^z]^Dz^S5s=qc{sWC^4ldЊq~̣`Ȱd|1`ts^anL\CK+9}t,wXʣ3`+l#d_x;1ūq giG YLɄ`@![ɇT^J zɸ RUv>&A\f4[[G{<((?++*N"Y0<#\1M@BSVC4 ;f3~|U?_N]os_^6e y~^{rnOB?{jY4jՋoXG97޸Ȋk8T7?!([c-o$Bh A7`V">5Dؕ\+d[}FBlL$2_UUAQ/o#<;rK! V5i|$R1%]j  S/$K_M@睢;g/(!y§DK{bP~y[6[c7EKWY~úsEН+z-J^۞ߺ=[ŭqdU^Ւy7NhŭY?SqqEI+qEljY }8cK@p`/qwMXt:MMq nGy_Q}@&NۮmK^yD+r23v) rp [g7`Jី'~`-8e7#͜fX<~盓nrYx {k\aF>./E樂Wq1}ѳi5~nz!?z~c۸LY:"ɐKL8yA qٶ-x^tgYy|,tGy܉3c$|#rqa=ƎL},[X1-qgW.϶g2LX[s^>4^N8*F1xi긦+ZAIl4ZIf| gWZ!T**Ȅgn {_X4e2qH剞 ; χ#߆F@?̝11&1LBc2M~ʘYUocy4\wڿLaf VktLa<^2xKTLq7>vrD7] nr1RXebɘ|cMcr+c9v/3^|5N-vz1K9ˡ/+˭Q[ ㇂z,A;vsī%;1f#I[Lv#4BLtHynH=DĈڀ$_ɏM^܈)k¹T U|X٧ΟGUXSgZ1R" Tyi| pܪ­OȒH7YD++ӈPG~!&(!Uph^z}׻P</$U! ѴDzŸi12T-OQ3"oBu52ZrMҷ? _U 8d4>LY_،+I|M*eT8Z.1Y1)P00~A+C=33lH7cҨ~T^21ӣF/e xP~:8偟a 8I8>:AsB42,2IvrT6{jmݽ㬯&T5g$&?nm9b%Q /xޛ=LȺ ƃte&kh -{\>7c.Ź~r(KqWΜ2؂ c,[ RFB>.C/084!!Vc\3m\{]zg<|N!3,A/S :'>~b  YqfȴvTzary=w]dc87_yb,]_^F94[p#@]5c$-ҵGs 3!~2>:U=_E2ﮆAi s%:F3%ﶵ p{(VRvT4s3;/N_qCj &>C+zP:f烃g !N3/XSkTa!*-./Q 0t <0<X{tQ;B E׉AZ y< &t$D]rBSڊ:j;zoA?y.`f#XĪM"[pZo|W3m&c ތ֘Guo33q!,Ӻ}dTe߾P̍=Y|YPл /yصzLS-ԐyzC2Q>0 [UBC֞sXqjwA^I\ʬ[8J0R[ڕW@ҠL&%[Ү["8!:p=AX߳#VjiYfZ׷?߉n.]f-$)֯[NnZ湴.A k^u?h4hT VHhCWDQ 5{ob8F[|fo#v8C0lm.-miWJ Ks"4Q0kzN#wρS,ǜE4C;j"S$nI?zϻ 2YRIȀTci/] @_d r_GZXVxSE3_Qұ8&yjqt5۠ݛfvNЌ5ZŚ ,B¢ƿ7^5zuH + pu!'.촱v am}+,1D*,\.0[d:&7yАՙsB$^[2f@-Ү FCdHq=K1 9W|Xf`D8ʕ4CsF {8 q _( 59{rRE',7nǰn΁ۉH̙rXq69&V_PRLtL>,ȐT>6y&#IڍDa98]fXiфjMß7FҙA`9@w0.)鬠1sVڡyNnt9jih}[XetgH`Fg8m 3ԧYWXFjo+ccqƎk1E#)*nR|ǟ a^ ǎ!2޿KUa=rt2^?\AQ#GCc1 PWo}&d3$8{{)q_)3ʏG{XqdR,M$tQ~~#"qO'vBNQnRtiHF)*h49qS)"uX|EIS``چNyeu B TA˜Zͧ@Mcp͹KTF^]ZbKFhnӲ54##>д2tKjAچd8;$@q Aj!/wmhj^ĭr+k"Y*:H ::tb>ufL_}%IA 68]]w&,NG"rJ7Tz.JR4Ù]aISYȺ#n>.אvTGz`ORE9Zd,B9sC+%d/nG#xw`Fz5.7HgrP9^Ad>nZ[M0ԚkJnʦac2t)rG3v,Y"[tʁ"f c@!-3h9xx efheJѳ./YR}4Hf0ph 7Okw8`Nx) wp4N3 K6ޭv:ܹ 2g'g@Å 6gî&kX"p3e0w0 h`fdi&Vb%"_R'KR+2[$">S(#}pC8.ߣ(0:IG $EH]rJ#"EFd|ψ' F`{ϛKo% hy[&dtg E}3(AE}IS'~pb7) bpFl4!Ũ#r:e| ˦ XVH_QYśt_S2Ii-Z$zU wbmݛ=fk-zVZ~w_DΙE~+B9z /]P_PחGW֧Q_OE¶\OFw1!3CԙTŭ%8>0 4zuJu.˦ t`C͇a,e[x3$=|3R|(igBRx*$?n3ڎP:I>9y|>ѡIWHɔgV s}-S-,̣M[ dhu+N| |e*X5zu0Lj X "Xl,J? p;\etd z+5౜k;u8cYM0v&ـ>F _#FY6zm,bD8]vfCQD(D5|HkD^y|G \TA*Ĺאp2S:5u*8' Xa|&+ q#ZfHJӍjFӡ kn`q2$*8 (iC\x'Z=5`8 D;hh^|WPw=$sN9m\Nt# T. x벺Vpɖ> OdY4evFo> {59`LB|cBB:zӿ>ajw=G G(YH|T̕(|rFGC@nDM>5(kdNBYG i‰#Aw/ܽ0XT@BL^ځDY-荥=8I|C!9|P{()A݇٢*C# ^-WTpf(GT:$VH8B<ʲnoYrʕ 9٬Y)[nu?/JVH3ς%˄Y5!2)P\U jlrCP+{\nv՚/[2z]PO,is&bԗ*RcH|L(*+ecG9 qtFwpCv؈ڟ8wҹx:$yt(k4L4qRo: 5FB!K csX\N;7CKJ+선AFG󔆦5>0['ZY;C\O>ФZpRz 4,эf7h.Nֶ"a"aɸeطSRw9<킹umeg ?T͢]"B=+̊Зq u\.-Y \9_D/,q1'~byW_, 0kMMa,M˜PRtzJ8\O:j:vTiKDR!Qc sYs7C_O|4yP38aҽ<ɐm;ݻһC|٧ )Rsd6'dP*b[IhS@ɏFI];v ;vBx>]w? 8zIstQÄ"%ށSpD.A#BeA6%6VdjʨlD, W($SJ]ݛPLϭeԊ3)]7o&o3.\g|ٚxjy t'ES\4۽`"AΉ'K VW뺂xPP_Wqɜ. eOx@f_D}!(26T*f3(lA\uMuNT jjCG0GC;I/:U68GN8jLM4$: e SA~P*+e95ze+%V5\;/AYҶ^\}7Z=R2A:]&v5oKAfWwŠJ?aO6̸GKC NрiM:8i< :4i6yh.4m.:/=!?]J0ZP87$jzW0o7Oѓ> /Y1't\n`4SdQ`s Q1|xQG \e/nNqH] ^ <=2O@&ڃG?[E y)oqM~mEȗgBơ2_$(Sx+z'+ 񯃲uge+WHg^Z7{뺥xt} /!K!-YT\2h<39Q wHz.Ϭ`azi.X;$B] ^a3L*)6Dġ JX*8c+JMirlً$x",6U 680a#(v ZRVPi&yDNNL_v”_&Wf5:hmk\9n\YZSov.?(:T4ClLk6fa}Z(ZZV\Z\&Z6Ն\J -4eE_8KOfphL3P4v,}bW~ 8=5;̏ hf,mH3VMDQ\d.Ͻ` 2J)5ӏ&Pz2-CkQbz_"y@6dtc9| #Z?{zDSqx,T= -9i8YDrDrC`q?<#:kMJJalwKr$$D;}mH9f(%Rl+7ItfSn_\Oz9@|bA{a1 Ұ:tx}ǎ7ٵ}P7L _az?W.ï3*JG${*R`F P fd"ٹ_عG  DBHD ;`Q{E_*覤vwS@a?ekܡC xJ:yZ[ԤQY aȂ,g~MIU|[Xrgo *^ib~ KF.^dbݴ[iUz3n=,ZDXL#ۼ(gob3",H[h{ ,v­OD3+foTN(ԨLck{膮9Btj`&{?1hè e_E˂. U"鴘Sa=qBmNPSSN;)kxfqjDꄢm̉Yuj8qf3K6uŹI3|(+WP1 $4}I 88)vn'Nl(B @!, nw 83o7sor9ĽzDGޟ֍8!`}ǰ~#pc%Bh!O6i&͝&߀c1=X,U_dž"==Q0KOAGIH|_ uw]HzI|;8z8"9O}4?E #`]~i0Sc&*/bcL#$=w>roGG~<|X99_ n1`-.[@-^ʑbIbѨڣ=_:6.5M|j$`)gߛeŢX xI"Tɾ1J` ;KvV;_c˷f`UE/Oo޴iO?x\1Ma@^NPk3ﻯPEP+f.( HSe%EJv[[vjb5H<ч~t3I]]-(-Ad4~Sf54sM*? zڴi55Ӧؖ9OYԭQ1Uя25c q4H7lq{ci')Cǽ|?Ž ۴m֍ g ;!o' @2R פ7ݵ^pQA(Ws) Pȕt%Z.箾u\vmq rϽ%=:zp;ё?F%r~J|>rdx@~K9˗繅XߑPs>c%!?^Qֵt^)s.IĈC[{7{7j4C`73x>7bBΐ:垉`)ߵKC_"w p ड़aȑqyay'rwO$*96U=|;Fl{ 8{ɽяwiO dEBw=#QUvPl"!Œ9{1(n,>F0ͥ>|7sFQ'kض?ֶCFhvɏYщ^ &¨#يO~jdO6g)!,Ƴf7XR"t(ּY˚ۂPaY 1q"Q} #$0q$#<duȒ 0V ѵ!d.tcmN3^QϙmS>!;k{L +h5cOٰΉa)5E bRj\(SX="nGju۰uu {}EߋWU^V70 lt,.ڽlu, ~@Ee,+A0"2?&UBfA ,x'A!WCfV` 8C|2!C*QXr/?q'nl">ǿ=M.ojcO`='T?T3>-нjVYW'ipInw+ bPj$}5EX%@*08!iO>裏OiTm zb֧"`3,)]d-)AIR~mlRB[uȋieIkҁ0] ș2LIH/v-#Eabk9Ȱ"<;N#Gİ_h6e6kiP253쮘i"P-v`(C{WTC-SD1I8`Q-:a {Og Ҡ۩f {|vh2C{y5Y)gͲ5_ YpcJ2_V%f|ŀ#ܤ 6$x&2fL2=u`XzGnIl7%mvʺ?)}omwϠW%Qĺ XEAWV}KT\U @F@yEExԌNF 7!%m}j'Z\_bCQ2;yԤly bGQ=XF-,f+(-c+40Zl`-Y,tO):olwy][z^o QNdf==9? {P==S*%K`L\+Apn9!C=2/ɬ$ /Nm#3th0ݲ)Aȼ,'WYfh`v' Kmhs;(KAʘ0/+W+ ]j2TR ;(Th_ HX#V#2R%,+ $,2}ԑVh<$3Ԏ6} " #IғK)6J"d2-P8TM ұ2DSog \Y)3ccoOSY.&(Mׯ; hp_KI P7^>8냋.Ke9i' z~ |ˁEWmYl>)~B/w#jJm!,B/Jt.\KW, ohCԄOm˘6`u !FM^YCfKX_{̫筿?~ug4mㅌȁLV16yi 1I `Ոa>KNB~$k.HA In1JK~#F 3CU%A7KBgtH%i-]&HxHB$\eXZ)ϩy"9t1 :4 IӴ͉wj֬o"ϴ|텧ڂUˬ| AJ#2p,p  \J=>6 > JyI~B Nq(sX D҃d9Ba![K> Ab#7Oill^G, C{PCJzuI*/"" |U>q 0٭<"JOg춠YL_{^ .ղ2y˓[aVD)͗)7|f{Y '(M (Դ/(m}Rݪ<Bɭ[ O>SH\Iw:mI_{vǀIOuyJ7\b&|l볯W4/nM䇅LWdSo.[ٷzVKRǹ˵17@)?q4hcк-ܻf-Iz;*-z2> %ڥ M¶Hs6=dJ8LOtJ2#dSo-LPI"]𝱔~OL2QJj0Π,">v,d;@M]J*2;y!(>w8E?4sQItx1x$XA4Y4FcEI@{y7JaRWFtNΦ̙}+B2)6Rn_tJهMAn!e؊\!d$!Glٗy+ոoNQ H9.W %pоC}CCJX@$r"}TDcwȪNG3wRW7 je2h nTБ_%Z)XRk_3}jYTb=oQ0teȉ vӫjJ}/< WW5Z+1V=VjU-:TC$=/T,jTbX ʬ7|r[pTWRLYwQ!4l^~=K䟱^Zbھ,p,e}uD11.ucxlo_f-\(ծ-zd ;n\frF 5n_]Lf">j$NYrAhZuFZ)\gZDLj0I9I $ddtRk*OY $,.MM0iؚWWQ% 7ϾԥFJ3|6(Ne#Uhٰ_]ɤyƭ$IdcGUkp^lvM#F=L0(=":xA@lR df%^{,ipsH69h؈+$GP9vRPJc)*I~GդP?W"rUbМ%"C 9?;DLڗvvZTJU%#%a2ijJ&`yFpO.T%5;Qja\/c'Jcŝ}"y&iI Ev%LZfWS'5ЩSXVDĨ=z3Yr k+y췣CYyM E9p,D 3 6S_ƼtS&DΛV&Kz;n.ѹ{k̫VpI`ɧ U5wQhՓ?Lv trr<|d&n.+`jmtYB:j|78TQ'\@ JVx,,ڞOtS;#Ǽ٭ =艹QC."i@#df F bAұ㋐.hB֠Y$MhbP-#XTQ@c'֒ ˑэ$+XPL #'L/߲3I nlY۰9*ф(NJ=G}E`}ACY.`]$)YCL)blIk d0xe2_5{7 7Ϙ.kakTy(`Gbw˿߱>dԆ{9A^$&u ey?1 sp71vpp>,uߣs_N'͈|VծzaܹΝsUU}AzGe\'&L;-Yߓ &.s A;` y#)̻5.1er;j S zN$bCoj DI.в 5[ņ^{8G kLF5Qb ;ҷ"_4j4zU (s1lGL|p1P̢P{A ~оIX;9dTWwUͬf8 ¯qz͌n̏D9E(أ8kE!wLjFGZ:tv:4K.^`fȰok{e:/@ l% H` CgqÎe]S l`q걄UiY@\{5m~A 1o36ۺ]<|*_y᪫HM/>7I}-v3fY a2x# ^hD\_[#:,1ſD8O9H$ FyGk3(̙~ u <7^xE==\܈㑑y+nZ:8H6jqSU!;rj\/SJA@r ȣ}odo~?eG_)u}{+?ŋӺb)SyldZo&{*f4Q5EcJOqm=Z³7d)D>@SiuEt ЈJ0?&Z#zK5~|=Ϙ'XЏ< x磹wwWFdDtajj<"q^%$c#+>9ylϴjK_;R0!F=T_(:4~#hfv;phx&}$n뿜0/qz7}] uQ++a%ݕIї;4?7?FNga2^9{Wqj6:r;'eEGzC*/>r\)wD#rGSk E!ˠ-hc7??6Mj*qbw*N섢Qfsg)9rzCCC2w:ΉU;,ї-Ilv~3hkp򜳱^dxN_gTyX~gzʙYge| Ja狜~ 1!lߌk]hwuC}Ca|iၟXfEPPMM?3|hpx= m`(~ϴ3}cooW36GgUphs X>/vQFq)\/U9AڷHq7y}.աjWaiwt:+㳻M6(șsef${ڃ*ZkwmE~jmV9 @B r$6 ~fg{{Ǽyμf/ドlwE\]7dF ?QT^Z=t&񸶫)'><#Osd)Ƿia%5N!2OwGNI?`˜^Trp-rJp|0pzdI \ɮ yHKQk_9㯗F8H6Ne$**x)>@u/43mi*&oC0zޣsr<2Y lUTey2evx|R[QZn[Z-QXڒr~_ר@" W%!*X,)PFWA=9O}?K9T*^4O:% JŬ:BMBN}2*5DE4H>G  ' Q.4*d"h0HUh= :R%/YV?|l:od}i,eQg^ FB;v_08E(DU Td5BN܁PXa 2 nᏜ95BFZfĩ1{X#Oq|Ms sr3]y'z;G=̛;;6;eH 0(W嫕x.(8UOAڀSjq}'0K`ETh>NͫbY(UjV_ '~ya.HJBR;g #yJV QF`ܹ0 Š aTVgM;'+;M*ƴ?\n[ۻ+U+YjR*JI:Z1cEEn%Zd$*Rs`Ɔ9vyZk=`EieqqejP[VU Z[UJ+(.,-*^k=%`,D@e) ( r觨6/T*=)Cs]=80aE`@ev:u[/ck7l+Kv9'K Т |~RDX~~轷y^1E=OI*y[j'Lz3uk\c/h6 ]T3Ud?vq=h)%zD!RVZc5|}u)%0D^/Ѫ4%5Oy;΁>yc㎓Wyy;U^8pǵΏ;0:}aBHvvʡ5BM΁qwܯ=T01~knqWj]!8Îضu^^f__ڳ/;w¶3qpc_dC=MT|$[rsvΑ[e1֖y99EZ]):pڊ<td9!<>] u;sU񦭻D⃑kW;Oy.tz=>> shnlB`{G:><<W Bm&ӗPN8;>s#QCmB[YeHݎg;mt9Rv=pwࡶَY{=. K==fgn}7/v>|N;G|Nzwc8<{r|} 嵲Nxv=A~M,33#csǎ` xݮ\n/ggn}7?zEWRTWNܻmHmAু2wgJVKDd,M,dWZ 39`?4?Pp3Tɡ[U\5vzwj-ߘdI֧ ,ݘ$yci H,8>W#mޏ^/Ī͏<(`ElWqEGڲ='X}ޞ-7" nUJK3L^Fep2Q\$#3=mЅ/ eCd?6{'= hvK+VGk{ 06-[b2֬Zb5s*W%X,.4D Cuȁ?⋻_|iO~Ɵ9bP8 K/phwηƤGZkSVNY[GM{-O( vez/}iˎ08FBφ$g+,Y-D4) 9Yhag|gC>uFa,TCvûfuM Nuqaw>iw !74-Pդk8/]/,|}%߆KKg"7KϾ*b{<#~3cEJfVkN <+3~~Sȍ l,Ӕ2SVȾI~7\(. ow%$;j;>ތuz)tW-*}ƊEH?}b- eWS<{qP(;269Sc0%G3I72IW '7Ie32RW)ǂqϮnVtV,SjZ)lBҭVKSg%#Jk3<tWd(($Bf323 xH$`J[~-Hqd0 u?dPJ R+'k-V-\܎L0X*Y^quAojvhRggN} ~,uu&& YV\Zbf׳#Shٯ?6A ZgpO0n_Byky!k %w&ܺY .@|W#`74821_y8&c|gA*i 6n)D[C(!86¦&ҵlXo=3H~:^RW]ZՔ6bɓṰl\\>x.č~o: Ub[JlP7JɦسԡVG/AQ> oe*|MBbpu]PbzږbX6Y{EzxwK I*92eb$XU{!QgE HhAdA<3hHԞ8<\U닪LCϾ104vnxold .M̢*|bGZ-'Ν|s_AGxġS:۽n0c2VM} r[\{$rPBѸ2}T޷(LIEE[~& ÁE#GaP |,&&Gͨ`@3P#*"l&8#b 7R/#)@JijT <\k <mӖf@x ζ,4"[Zl-6x,jZlpIqmRL$ÓTdoDh8 FX;kXidmk$,WlAiVŷؠH6[o | O ,fOjH6p: Zri6Bjޠ G}l@rɫPnd4l #,Q(Fp&B)@р84 1$5\' RW*? 1GBa'q//W[rzk᭒*y抇=@噘642 _<Q qw w}m9qwxx]pD b.{qWSY<\ F;wF2b6*N.,dsDG/)7+R,OQYJ*LwcӃJŗ\X0irv0"O3:;;=+4vSS @dnMZLwDbFB$[Mٝ?l,Ӣו6e@+ThǎX\=Z-";m.=tٟg_4 uf{NL=G4hk -V]_VA]pXi\\6S{iyN|y͊2qi}Uvʊ/.'%G]9E wHj!&(?6㭋#Ȯ(u $zԲJLBW{ )@:FMIKGG,<ds@tFG-yn:d9,i<а+Ͷ/=wnS*Som)@l69fGqx0@$.ֆ$v? ax;?Ҍre|9_Fs8ˤXgR&µ?S &2,Yp?[&NeW\ie#5' }Z܏i,%١(dfGHPHSC~9E\r^5Jhl{sZŖSgQ~Gw*ta:̇~4tCy...?upWccoNq5`~.No} *lЭtn/*j[*8-Q-pkKL-"~Y)zpv/Xnh\FJ^CDh/.d䱓"KAhαZ2ONb3D}>s"AwjdM*e9MVi5j3VWfU3?B 6 ړfo,xZZ!ֈzfʿuHkފmβCR1G\k,miԒThȸwjHJ4qP;0"q%qf,`DQR̒)=#+~tf@zla,zz.kDGMvդg_zSAf)-)r U.lI<ݻf )ҩ!\i~DDxǶں (ś]n}ZNZ,H6H&)BRFR6=Ϟ>/4?Ԓ8F0ׯ~,ε*0ǯ YF>r?8:?x1)2B?:%Zt.gŃ%YܙwaZKb!Q[_\Jz}v M_!.vJzN{)kY@7pMśiM%a2bΐA@DV<0)5cmiCiJR"<90$IKWkvejYqH7d+l4Z6lV6oϺJźr=m ~q]]$y5uSj Siٖ0;D[ĭ-}i픟ruwͷ H}Dbp UWW<%jtv/9qiG^Q6XO|?k_hpUbDLlGw4[h~vbFkR/]CJF& .ۛ)Wʛ\O(YnSר5KkZxmlVK~丬bQ״|P[8h‡3cԚů9FB yWćds5א$Gx==]lwv{̌w(MQbpgQDJ!QE %m.LqE>ұF/dxz:_agƎv]e$׹ L|%}W߁^gfX?~POP$9tӌv8;tnl6cQHIHψfZ:~_o8|"f2-κWrl %ߵ_<>~wُFIhB?rq0K`!%o_ 3ڎ!c' %83 v݂nmo 2а5̅1[#Bil{,E\G$zIZMjqSsK+ȦU\W= f87u9$540+u#+AyғeIJ&#)IVa#nbF t]^) b wȔ[5CGjWjj#_Tcxӫ0rEݳZMSUtVj6>4ezH:c^ g&M CF xƿPoO ebGyf>|6kQb3#v뵖-0O -l`q qmj[^wmo6*R*LbjCa=񬵹b5$׀.$bÁр'|DWXg(]FsӲd[Z]@^P)Fҳu*XAgcit󈹻gUiYE+Iz֞L|/STg,┱); J!J 1шDJaOlWSr'rWp`\9p EYE`EWRxsiǙ^wqǻ7k2!CߓQ͟]>73 #\_P3 oΓX5siab΄٣mxĨo"Og5^t=\_@mP~ <3} '٘KG6ڐt3 5>Ό]3;MVsod}2mlA'-c0C-ZAߠ⣼$4&f4P}2 rRWo3g:jktMnFz~=m]`Ȣ(jwߎ%0n"v9譹)z + e[0/1jin*]VP?#mZZ_ kwLn4-鳸׈-Fؿ&0Zk͂/eVSۘq/E=.HdtYZZz|L݄ˀfHeq-ƀ:DZQ榢P˖Z'"j\DV{m]@FZk}yx](o_q_t6Y#Ν~͸=\^Ýļ6+[AYh0PfWmXr/Q ׊QIX8HuFVESccE$j'=n*, (x9_ˣ %%NXz!"%gA.[%߽_oʹBD1R7SȂW/Wx _),B~$| x\PIil~%oW@Ns|ȅBby$I?qoo)N"yoO/t۸NޔX|\@k'I;y!o#:r]a>⡃nf+:%?#zs3T)7^d=7㻑'1s6SseI4 .ҕeIIU%&-UUNjJUDOjW\MI_:ʼnPVm2NLJZ}r`ʗ34-F e0Xh Z4fYo^Su7vҤD6If/ͨԛMX\4} jpqR"$NLZ)=j՛Sd55c' WsSNom"7Ej+l<F`@#sWFCb֗J,`"/5}rbXىKbfE_\=K{ތ[m ?pqo64 _&i1 H hj5AJ&,'@CV|:Qm[(cDA(YdLjWs_r>a,*:s8*Υ=g~/ykMSyk< ͛o9?^pYBJ6~^xygj #}з쿳_W(b9A˔Do񣌛>y',/b9ptqno'{^=yß\<́[q5nUx 0h֮KH[_@tEv]Fj%x\ iK ;'L{dw"]K[b5 =5l83t>CS%t V_.*l #GxYO)z 7/yy%Bd~U`5p&&QQB85-yy^E/U n`;E1hIf5@0`nʝtTq""LrW&vxlh(|hN[Ӏ=hEifczQ/ .x+-zyij\ФJټuS^qϩ`BdH_1Xf-[rdd|,Cq>`b*/qm>h)LyCot_ ޻7~;|X;]WWOC34öB .t!m7`CWy}^I_Ú*SY;e%@>7!%\OZC/=T H|HCzMJz~s\a^nr̡zEݵ>汹g%lH_n4 ~Q,EJ>/y94R^N6ܨ'  IlBޅWo7o92 Jعy9E-{][>aLg?ȋ* pi9} H /=?θ].h8GW`㣮:;aϗқ X\@Tρ0_.]xgw#/zބJ}Є;طMVim&F⨷9SƢaXtg=ɹRp9v&*0~:=Qj*/楩/yz\jnqHT´\e1KSR+J٪`in2a$J=QZli/yc0;95Gs2,A=Po> f(A^4# hPRL\>sIPᘈ1@àLM ͘+f X/ =- :zQ@f;|&Yys\3MCef4e$D2RREn+[T–3({T2d 6k5bX(+ʱ9띧Szƺ.-$m1YG?]3LG,SDz+#Fϓ>Ĥ"t:_/mhJW8]6‹)9r^yRPS }&&nR3x-F"7pX31YXcz BgsB(K;BBc $ ͡p$Bfg$LRܫ9|v4aE-ocnb6D_Dwslhjv[ueÝ;?l~}@`c?TT4~[z[ D)adT˷~]UE5@y]ܙm;tdfϟۑ~*I'.\N]_w )` V bT;6#(@&=@"<~Fi htb4O]BJ= A?8Efs%ϬG=SS%Mz$2nj2pUF#clj6O6[7&cBoڴ({<*}34OXݢ $'L`&? ijRԢS_[y< _ORJ|#>or B~iW_u?0)l*rfI9a08l*a8w> !7r'HN$$ff$V֫VZZk*V7\G.BJEbLBHvgjcf|f="i-[c2K~$!"JߛGd/! 1"_9UsiG<u[:cױ㳿tf\:&[иbm|H-/MW46%"kgX'%OOD=ɮp31;{XTOo >o}z8 1!.X,k%F9B..)CnD0Gc hf߮F`fdV `TW''*љnyOVy VzߘL|sIYI[rnP>1lb[`5$T2dT* ~3d`q^4%1\‘xZaы/=yL'^x1Ł[[d qtW1B6Y6cG;$\V %%VR/\8[?Nzn/g* "Am>Ko`h KRII Y#I JgQV3Dmz.u{^k]ג=Im-?}JRϵl>LQQ-cEE+'+o$iLn2y)YZ_SE'#FBYf2_%sюEi]բSScb]ճZ;T|;K4eg/V6sQ&CI^TqXpO/9+bH}n8 9` =gML~KML4M@ V4嫤Pm#.ѲgZ!# Y#bk W c_9Me‡_'z7ڙ/~ k.bliϰ<ړG0^ !.5*{"q2֤⟕6aw_L&5-44i|^[oakJj_ FӢEۄZA^1U1'zZcΟzt|+~u|׹Ug-W3(PU mkUKZRԄEZOpfp7 EFYp 7$Idj2C_)F?9wG%)!jY  ^R=s^U ))\sgԔTwfɚ-ⱳrfͽIgS-X!M2x8J&|E>"d,[^ԘpqˆygK)JsOh{7slU<ܓ=X {SnLH{Px).+/+%XfBbxJ;# ?M9B0u臏"SWw&ꅫH BjH%d;qkRK`pu\b< pmּE&}Y`:os"/g[|3V>tXjObԫh &(XڏSޯuߣ(oJlc=ݕLSRֽz1eV c@PDpkq,)"am%2ΓiPX w.Zu6;Гj"F& <_*z NW+ =~d2BzUB@3i℉"Ph?qʤLŠJdR9xROG4 Χsm"8]y +3'=d;a<.7 %Zn?K]v?U"W~2n/rzwhyo9Uf8 -cH "+$RƕJ _ Sm^O'r G|,QO/|HO@<@30؛os*AOǠ^H_]} [\oM /-p5_BPR7,-pXk [,ReWdmJ멓$^ZW*V02W^8xN]g`=DaփZnT߇/FE?F^.vM'ǜ>xǐW\4NmۥĘZߚaYc,` mq*wL\ 8x"{#?"sYi ǺY'v=q gOV`fWA/x$Xa6`g_\Y#gU!CUU ωM竄!i/$iIbe29]S8skIh< -(RI T|7 ok +h^*Ia1;VN4ov'&}1xWL $ m#3GNHܠCZ3J85bVQY3U ?#ái&Ǧ{ ]lwo-?ظYH ˄r!ik׷B|Hqp?ڸ(lmݾ`Ѿr@r\f@ ~Y3,{04 ̘E1RՖϙ9sĉ ƀLXX Ě^k]o^C11͘/^x B&@\^K` ^ .^S1z^fpxs9` lj6;ObOeyxLb8zג9K:J~Ǻ/3 `FM G?7s|(iq J;QM@7|~ͿReP-eM3'ıg.tÈ^0[\KL끸iQЇSo\#G;?ذXͱq Z%@}3.ZZ.>jLP3fp33ffJp4+A.!<)4|M^6ҧ`vxo\^jzv'-~L|wv::By Vm^#a|+]]GV;J-s{;9xy>x =Z\2ǀyAR[lxL\2.bnK: 02D.:=p<|+gzV<쁯OìY(TPfP xtph_9n[=³´~ȉ!ߘɔ ȫ)Cj|N, 6&LJcO,EUe%evrjxU9{rX:# @FqAFeCE%s7!/>KZ]eVq{ u ^I8yxertxҮ7O'd9.].å닏1"H Hi9?8:.#f46 4yCr=P`d+\P<?jp9>byX3ɡW=Fn/Zt+`ϧ`Ai}bc/tA%o7>iRRlhol`0d| qq1EŧoiI eaq  @<3;:lޗdY vd_\zoWΧǜgU93}c^qe1LccF/k`.D0X.x$@{ }ƀalhVe}d~O6]3$sLS^^JQVL Ԍi@Ϸ',P ByEVR^F@HB"Hl"TVߚ|x Ler]!Sa8d7HXb8!BR$BrUX0I=񉱣qZVr XJT]tUo(Gce7D:$6J)IgX2e-]"+qƆZ{k?x6|}gMkw CCׇkwȎ;1Gd;ܔ^Xnݖnbwş̇^5dj/z{٫4WTqPOM_lai.y\G51bN]ibX;\NtŤ'.r.CY)`a0pb82T,oJKC1ى ÜQx0Ժ=76JK2ͷ*cV nRA0(DSg2pJr K\eB.&1.ݕؤ]/?`kd<>K=\M;?n*$>M<甗P٬ǚ |,?5rbS #X(J,4‹؈ģpL|C+'pA>f,Ȃr(|1Ao1+$BKJ|92$iYTeQ>ZuP)@VTUaw,jK#be7MURE$)7,^4yf̒) iCJLү)WT:UUr :4~㍷j>?,?r8ƽ~;<|N6|PG{cC",P~0*ӓaYEEj{ٖEZKm}Q0d\VE hTvC'QwS2Ÿh**V ϊ3qnm0Nju T _u׉h%T6Ͱvò;]@(BQ l.?|;%7yvb5q>)asΨLEҡv|Jq!].]OtʳrJXqn'GG79r"E`ލ|5Ο=k>D:Q!*2Q6A_I9^XJ Meѩ7G-z/*֮&(L\F%˘N?4 8ꡨ€a#K=KhWT#4CL9(Ҹ^Drq^1KFAD-Ԡ;IbgƆ҃:GF6 [ʗQR^iO/PW絷G(R<ėKFoPg@Jl+QTZ,l(@NQ&*/Ke@?m8Gљsb@KKK?D 7i*/$UʹCy|q2YV|AyWjˡ-jD*P& pd&1'WU y_Jo/rI|!=&V?4U9pk) `TNEUoDE0 v8eu݂[Đ&sK0k#N!8V?[:YVh

F&֢4!6`75!ݭh&$uhKT\d VH}yMm 6Yn[ȤwBU;8AD[7t;y.ap)#HMviwD0hq{Hwi72bw-Xew W B>tJ=NWwI.rDa9$D 4Pڻ"b4Ӛv,֮`x]2Ձʒz3-`,VUox r|1_4 Uߖ/S'r/]VߠɧZv9ŸI\1U4T,/Ks% \)ɻyEIg%&ȌoiI'ݱmM9eqÉTJWLlɈo-DE< d y%"ߚ ]/Gx40 kAeH>~D ]?ۆـvbO~ËH]MOOo}zPv~onHFb.JovXࠄk„JKL}7\z4Xš/67HDĈت6 ~1˥-ʗHֆ>մf흑1zuu}|PQZl0J?óǢcg?d5>vq):û1h}"#ϺI%w]'e8"ih8+* Dj+NKviL:ha`u!bYuZR,-I LjTMJϘQr+7EPQt`P,;K;%fL+8QlVEq1LzظXeFuvp?2Q1׭iK-=q({#ELaQfRpfPFzD49U`#"#.StEJ3Br!Bad蓾+% l+ʚ Dr4.b p},yFy( W$ΣuKjxz_Xq1rEP§8{$Q~*σ n ᑈPyciHJFt^)/PUOR+|bYǃ/)CF$4x/x֬7 <!WD_Q w J*ի0Id'#99â"{BEAWCwH8|=p9E6+ڀ߳ΰL  R-PSP DdT=, AQr''(M32~ Me@X4^iUoRqLZ4޴Xq2!4cDJ ԤͽHFaK(t%jƎk6;XXQhI("Ip\[׏1Hisx و fij#Ip܌iFp+jjY.`Ak.ꢒBUWXшC:!("3Š +JTÊʒ2"6jX=r|>_ [+͟?<}˚J=ߘʪ˗;_-&O  Gd2u %t5硳xv)<8;l,>2/I'<&-Q?7wDz(&惗2ҍ`7iF6/'" /A] uy" T:%yI6?D @Dtq>j/G{جδ0ꛯu.$/7F߾Qn|}3uׯNv޽Q7E{ñ5}!JCi8`H.DF2}IN?ZHvpQAv"Ĝf=QB+V;ڎ|!IؤZeHĘOBS!yiv#1}x(?4)BY*hyӵTewBңC{-DmVZqǣ7T#ZOC.1v p ԜI@mwa]u(0BPH5[x&45@k BQ+t`њ>Dp4!!$kRM>.6}ZzPӄM QJłU;5jlCb֘i',84 iqIkzUB?,(S!I3˨.`gK4lb;1,4.\/¨!9j֡GUPFכm<ˠh߭`HICzmaq iZ{%|m(rHŠ ꮐBK3pB Q X1O`734P_)"ʫ!uObsyܱDlB:q܅@,NfΛ78_xR?A~X!)i&nFaGr( Me![0jInTYOD?*%S: 4be]]ҝ@ q oy啷7l b75jǯnμ[5?k`kn޶cgz(?(Wn*SSgM^VOhFSORܨ.vT~(M/֐4A{Rf4`ɵY̨bEAa&堨bvbьy_͍C(sNOWpL49f{ױS"tOwHLfjPws9BfYIQ@b~ezCX74Gǎs-΢(\.xHei)%2fBigs(Hg kAF!pCn/Ǐ)l!n7B.$dI黩soz^Aa:pd,B&#o)8(꬙<-((TvKPEJ+AD|1'O+DZVgJu;Ԏk;;v?:ࣶθn膷1`a9i2#MW&]IۤIӴ!d& i& {% $w3` $mSB2NR t}˽z/]QKlzk̻w$CX \bQv?%onߕ,IoGgsg A`z@llXGBrGLsC0w|AOgCsмnfC hLsΘEh[$}tm$ pwdQD4I("K9$cϣ-sk{ZZ϶>4[h֡g,`jFG(@BsS(`B}³h8PMFB!R+]mTh/vX.!,yZ EZ fIqi|I$3$ PMu!I@7?! noii<"j| )E[Z䧲"Glq b(p$nDc=.j-;/€o<P MꖰBIoy_Qõf4 k癥 W`:}`$ėmި(~3_< CZ.8 ;QEmbv"Fq~QͅH0; 瑣6s; J- ԕvYL}7`S&*uHQQv{Iޢ7/`1H:3BfXrœ 3z4EA!ZHuP|Țƍ4-GR#siNMSQzDo(. l /KoIBUGG]ZzY{YCBWA_uͻ=b ( {j^y~onl?oݵcӒ?jچm~*@:@U3oӃ`(>A3a>Oҽi/-˺"3:IF7+GJP 7]0x ,*] K,`ѣ'L$`QhHp?gŏ"r$*{rhi`dyzDFB\Е3%9z|N Id'Eї.ND FE9?q!m|o!1X|ql8j3 cu5j_4YbS) dA,,,2Vc M^WE*F;fqcui5mPXލr*`ҨÍpoT{Yu =6Ŵ5ThtLFL/}͗Ϥ墇W|y6kF $xA ~1*0IwT\v 5p=hLDo|pD0e$03C8o}-f TmKXm1a" 8(t`3?iDG0K|_h6EMe8 #Qs?DD|6BP#L8bZt仲StikyG E©ՈA,8 B$t${?Z[}#:C6! pEG<81O'{6}s='Lv> ObUPe|]'fB̜! C (3B[$%\3jpϰz0[g0f+2afgֈ 35l$HatH`P:(b~Fhݨ1R &F*/'-UWЪ 0 Q>\Pаazf Q`B(I1QhL>߉)hm wC+B&1"gJLSG mR*RGJAJt).dҝmg9b5i0i—e$"4! x*>GYڥ_QfX-. O'woS;ok;2oݱgOjWϟ;__z?unɧ;# _~&_'Q5jџ%> |>N4h_LTI`Ԁ/82=7ռ{4}bKr4Tۻ>8IpǷq0t{j~;u4uݜ HBOW aҞi=RPF$npeP$\c=y86nY E3,jw|`(ƒyp+޸|hQ c]8y)!znFpۤ]y}D k5煠y^I>9#C?F=uIÒ]k/sqR8DG ƴGDnH X^t=|V1|Ur@K:0m7W |@=ֈxΠH)v #MĕNZ0qaw{r"v&!$@ RD0̚B-C1O  Ո.l{/!S2N9gf y^K B4ʼno8W!>9CA2p4$D7 {Cy鞮;BPS_h}VFU  %O0UW*l&w5׈.ϖT R+Qr2l2=P@auZN"9xd^oJQ0DUĭZví:Z nBԑbawVMeÆ7?gkTf)#e4u~;3ua6S߭T5nfD@ kId*ug? #Ƶ醴)۩T햷ok~ߧe]EO/~e76oy뭡mTdxk (]:0-k6l!Hޙ}" hw3R]_Ȉn1{Lj'7I%S5lOR#{_ `!NpRn&ޓIO:u⠚ҽb(e$;7)+FN%A 0K-gN8'LpJYۏq"r!eܳv!q+_)F#e* ť$l ߙrm|d9$ G b5)fRF̖! ^ QS®2BQzL _\g@-/ςH~1eJ*?SB@Kʌ-.)Δ17MI]+()w0 3 րjd|>2O)>s^MT{nzuUݦm5mz)⧟μE_^j[n{kYn;dC@ۨ-[ܸl*MQ,_=lP}KI: {@{ e8=5 jaͦꞭ7M oav4qg*rfGti5'T%`$oOq%)nf+Q8AQ*Sv==oY<"QI7UwuO~^5 M鰜L<04~at`_BjX ˜]Po7*#^` &1tN=[Y 1^aӛ[ܹm)ۼaڵO< FxsK K$a,LB+B} ~` נ$'35 ^ux!#SӳFX9zaPOf+ɤ'1W.T"N;(aRA֭$3I%I:D׿S(W_dQPά彡_RJR9'A-H\I=҂y "NZC,A"$f'$ hG''s9 ;U3ތ|js0ぷ7N?%u* sd[5mlR~j/ɇI'S{P||[v" 6[PpI#RH^JkAܽ}#_}D_>}/i kRkPb%q8yAD|Sj̘Ȉ!Q\8Yt~J8|54p$DFi"ó9Y^X")(Q!h=Ċ>Sw=uwq yAB~wdze2e =8*GFg.*R4;-e&h=7TFBPJKqD5kl-X@ ^Rp`6.ư]/<sH~(Xf6()-gt0AP"ͅ)kQro*/ȧ(("? ?J rnWduQK!yTh҄@PMyfrһb$ O%4~KS II]6]~Ӵyg빬.=s/r6oDO76~m/e2O?u6_!w)ىNjR=tyh!oM VNYy'8S]97Ռ=q1uW׺eIޙRNM?;{^^g'ߞif3ɤSu@kӝ,ju wH&tg%@9TeIMA3z%gyE2=lY N'ZNjH-Y0R@,TbT8*SKU)+?NZ-$?QBΔ0ǎ2a%h&YI ^/P6T"QHZpITnL:r%iX/+I^Fz/=5B vNr4cUU}3v" Iv% [n$mjRN,' beg1̇IhW=/'_\L A݇nyxP8<(p$*,s-HL9,j?8OEDsCE?fуs2(r"D8VGDHOa>k?D25X2s:׻d,Z9c` :tKaYKF@Ez _dK àPdKiVXJJ ްY5b}p SJ-@ KldJbr'd| EQqQ1 ,ɁpS- nyUtyyV,jFe4ʘg*yFſEd A+XAgǫYX,2Ys{{+˔Judk1B{6ƚ _;%e_f}ck_dn2oڸa+-Ʌ?§}&H6PL.^%?trJ'PO;ڿj+߫i%C o,U~bd4-3~k9$^n xv&\" `hRmCwĻtg_rR1K< `;v-zM%j ɜ718\,vv, QcE$sصu'h#ЪಮZ]x%apa8Qi2 CINo8FxC:. Z`cX4T/J6+kMMH CgTi5a8t$z!ɖIзR\gEJQg]uZCW⸒T\K G%=hGv@o´ɏ((KV>bQBxͧm[|d9+@aUA Y!GN& c /N V~|4t d0H;LPk52nswV?LQ92WK oh]L&sM(T EΖ!;%SBa_8kw"-BëQb!WZ+%2բG8gCtb.퓄)yntgn=ٗ܎C #X,6tjɂϿlxc`~s)n(ڥ <{+^Jt&mBGHrH=-dhsFkKiEYj PM:/?r%;o͢ u ^RkG"DLr9kS`1`DID3us'ߞ9'xNͳ|BF x$N>o8ENg,0Ƽؗ<z4r;oI@m-c+ei"-oMTP:G v, uSRDzAn0)SSH"2 [.|r&9F;D`YB0Br 4LE XdWT ?p!C`Oȗ2yMuÅhÙ\3b0ζ9l|st6;ͪMN O {3x|t ^\~^ 2N5fgxB7fT=Q8sC1 o]V:xGWIמQ3 Y D&|ʠO|~JW%L9HftEJZ[Y;J yyjZ)TV3) " ,qPϜnd2@8k5*}@Eg)'[W>bbYnK1`Qxo?# kg9x7s9յ!iq B()P=.#Vg5tD`nM<hpzPXר D|%owޤzƝUu&~&7z܍mL6q9(922]_z85"fN?lYf:uaZ׬Ze~]/b5bY(ӚU[Lk׬xZ&[e3၇V:=Sd9rm:F[<>w2/?2`%nvBuNy7? ~x禺s7{SX?x?x UBN۵uǩfL&\UMPw|| P @4}A-pgk|\:gĵP3FhԶy0n%8ukԵ83uLE=WgNf絟!QB?5/͉- wN43_887&'z&W͈F!Ȋ&^p/ ~q?2.i5ڝ 玮D48'p a~LDwlf WcM\G/$>w$P7̺| 2?/..M03 r>h2)T`!yJfCmdzbI6 VRjܜ+!S:='%?fCT+~FO7~ø39Q5Ġ>97٪s.X iP 0Ͷ7njmuU+/y辧:__[*kV^z*Xjs/Ě?d*?]wiX00xȹQf%8;y>?a  #nx٭<6WX164\PF;-v/jhB {ڬ[.>m|c#0xCc3ι3LR5if7p,:)]7nCjbbQ4=#9>諤 }0cFh&TOPkU9}l6EuXJ)BF":qMMԱ&7A7yF ';vBT~z N%c'?ЃG#8πXޞC>v} ~!n>N\C`cGd1 uHӈ#R t'C˻Nz8O1%KL_:7]Vci<*s{Hį!DBru߉][t tBkUOa [˳^ˣobihAjAZ)j& #zL@F8#_[X x uffā'h8S|oaF^5 w{ zkAԏl^p+,RuWuڭfQb܂m_bJua媕ԕ+>y?K<  -7JG+~FhZKҲ+xIy!`rc;|Lʠ;j*ƍJ9zJ4v+71)JSzMRLv,Si[,}l<@p]>8},+L+uLtlţc=2Ff(KyÄǶGI 1_EàL{o&X/V?4Nc~ǐ*fXqs31-ϣ ?dƬs13B,`T.`;mzc7`G3u*=ԡ1v=>"n@B?[uk1K뭵_p1hgъD@à8u'DظxWh~/nOk=]<" a[.1<./,"}W ~p' `KUV:yrd@"W5c2Fbeg,VB1\\ фsw$ȒKƊB-(|3߼F ],5 7L6Qn'41]վ3'9yNݎOs}mcyDZ|˗_v᳧Wd>}Z{3/#rS]ute?H-|+?yP ch!wF2 1F{,NSqP80l(z聱2-%Pc?jq>O $Fp^?edM p ,A6c53 ` ?8Jp9{=~dn˕~ 5v8vo#ߌ2_$V^,GXs@u̓@Fh笆/Ĭs88me$ذ&yTxs(\^ +CܨpTFY63tvwwMhp 8q v>fI np뾿,P5~Xllx(H 5fY`N!ȿ n;>;{5'P_qXtv,| wH,f\b$O  /0 L̸{c&@Y3j~I.Ϙ'0šyD/k>mʡ;c\!Ix׶}4\T .-`)TC/1cI!J?pzXz'67:3C̽aŒCʖ.{m钻r>^|mL]-3/] ',X'/}^xeҥ }|gXbZ57:V[bzg[\imKJ7Wk5>]Qkznt' %7`'gӜN.0)=ѰV]_pp֭X+,v Ƈc=zݪkUT9qՕh)qp(+0yPb7t~7uMg^}Ux*5X9lw9GJ`Hq:'W,\ ExD£uR 85l?w95;o00BQP]XE1gG1MNgIJEqN֞K|I2Σ)h͒UԶuK^}bH6;CC,.z ,ˣ g Gqj?P9P  ։U$Ed'г34]!~:D{uSem)^=&\ snQ\Tm{SSMGTt5zwIlDӏTs&7|hM*ژ-Oz')Xy)bM4)xt!^G}cx8NŅ!+hf舾zDZL4pNi(%Xh`##Ã9DӼ@wKǤVqj{ 瞱%[ˏH귍,lZ)h1:Jom*oCyx[[wCҔ,BR*_`~5ʪxTc' p N$! o]DV~>? O):*M7;?zw[p7bu!Hm/:X8;k++jqP1IÍrܔ%9%s }t?qX:}SV>;/g qY ktDPCbXFFH$@F$7 w„8=ZOe-}pbobLSur}t\;^ "*U,|7A]p7fo$C˲xpI^1Q="B·.h%$~6h-<ؑ/-6dмXI"&$M숆vIW5.3/$@q[D)HhGECGp 4-IIH&Sq"Z8.wCG;>m[qI+`rp3Vյ}zu5.">A!QHXlG1 &FB1,.iD㭂̙ϛI7 ,.0ly`7K#*;:FbޢOxL)]LV$ThRO]Tn0b7zRYwK?HʽKq9! ڳlR$EXP"gdILYFɌH*[<^itJzvIcjRLmUժ(Kem8>;,\>wS]eht&U&BIae5$uBZ7r) DWזA%D*yrK:Y yˆܜ-N4pBaA^NVS^Yצed9&5i7>A"r%Mt ZC >^4L"o:ep*ES)~1~-ɤkdcd/J!2VkH)0D{qAO|efi~GɄVmEY_:gʌ L`B/?>LJ[#@V8K [j]&DuY KACҪ A,jR`e>`ppk@Ô;ͼ% )&p& \z W D|WCy 8%:fJL>qrld'\;%Sc0+y {gC3QU`a}`$&m9X2j2CXyw-SQ1\1DZSyݵŋnϊ[>y*EgRC1s Yrx)Vz|[q;PfZe m=ZY߄Av/L8Μ~DAO*,s>d8ć|n>1 YCKyB_ce)Y}fޛNqd"@,\gs9Eo[n{q`_91nXJJz/d+mbh)`F'`@jJ߄؋x/{({j=ʀBRP*W)AބԲB35/]XL(iP;P8y@TVn;~]_vRl9"bmE+PwHƙhM83rrs֮\C?5ٹy2 ?p\{\!k}ں efgGTlv X@|s0"*p±RN #OyaE1Gc2ΌGzdTuk*[{! xk,%ᮆQ1.1n+d4DDm D ,<Zt gWUcZV8Dn"_1 -o(Փb/3q3LT+ fnNV`aX@o!{ÑД|cۇt-pێoJhQwֵ}0 Q]S}*fVpLØ_//}}fVfU /X&-#+s}z{%5D,+cB RhFܙπc d.{r!f6|0Q.8dg.pD"jZ_Z-IFOIU-/Aw&",谚0$+SC(\K‡:}aF6spm9وN5gFrϜ{\]iVi!1C3c2!va|̙ pa!)'u)jC̸̡;u48T?C,Y1#L*PUxftl@?9A;∁oϡ㮙:;a/SO0)yC L }j5!y5x׎;S,c5 EBkIٹ{ǮR"csX@ӑpz=PvQT*"c0gxkc׶f+_]|Eʪ5kצf;Kv4Y{3.X5dW-^K~~Q|SIﵟm+1Zv-CA,`&bSmOA8lrԶzKNq $UxjZ,eb d׷%^d9New dϳ\?{N֭(oB=~Wx>]q;k_OYK+^R;Y'Rg C 嶍֬][ `WU6U-ۇ{]#ɷ?Um< O+=* 1?QU[ㅊ?dk=^gM0f޾piڗR+6ǜCQ>xC%r7,~.,šҔ(Q4jU4T!>0dO+0;q;3pxA*%{s[o?t8С>W}k ;D8l/9Wxܮ$da߫ꪜ>6-ɰX% %ڒ/I)Z<<'z%WY<,+Ǒ8j¡8 ovυCp(\8󿰻.w/_;OPK}qAbE5 StartIcons1.psd`$yd6ec*d!I #-J(q$ˆQ,(@ @ؔwdNAۄ;{N;+QHt2-BiQC&#_uCT1 @>I[Ƹt*$9}NM?=KtM`C՚8Ey`^۶NV={$b݆t iΫС:Z54`4m[GM҅~uT$%mXG/UXIjѢO00JHS8X>gAFssr5 ~CՉ~Ĝ!vhI$$$ډg+6`MHTEPkF,hƯPyԪ-pE.Z' Y@>B[r(H Z}?g+ܿO@`hP )+ 붿Vl$V3uF `h5 x:1Ѡרq)~FwhG7~JitN~Jsa::sBI P@C#fڨEAs^ںuN-:w o90 Sp-Zt  iW8il4IlmHp`N[v %SX-ZwvdJZ4۞&=>jßޑMoKmSH"-0Io'MfnuM/6z4C2~4ж/dBV/wO ?PCmY9cN&hKڤs+mi?]`V'hҿu?逰!-BZv : u(Ł\6HBgpфui:sxxP @r  lZ}VhtWgi ިMH1; ۖKV6kX;EWNUhX! N]m^!~u^hAܻk'UD簠fp6E@2~·P9w!%rBJ܅ȹ )sR".D]H9w!%rBJ܅ȹ )sR".D]H9w!%rBJ܅ȹ )sR".D]H9w!%rBJ܅ȹ )sR".D]H9w!%rBJ܅ȹ )sR".D?_` Ná2MJ$KbE2["V#hI0 IHPނӸԙUtT[s[ ԡ\:$h1lbLNwKc2t2̻1d'9N\YwqV aBkU*mBRTẚEd 2FK ժ^%m6XPCNWNߊ~ˌAK84.$$HA2i:{3BAHLzri?FJNR>}Lj91j"Z%6!4L#wARbWԤd3x)6 $N>$|'_$ļb7ݐ_t4h *D$Y6SH!tHZ#tX뢎yпBGB<7Z;&I PUL{  0 -r8j8z8xp A8h8芃n8δA1b-k$!5"J#`T&q@Ψsɤڢ2.1`iqe.\h6Xp2%lH0`q;Y%^+ND/UMau澆frD'JD[cJHJ, R$﹥yȧs$Nԓ5N$tu&}5?X"{L13-$=1`风) n%ܭOi!.364Ahwsqju$2/{&LWDE{GŐ5nY1AN&1$$׬p^h"R 4h7tql몑fD Cw1`?T)OD |5]a9L%,x1\lnT*T2WVh@6nO!Œ0M0m ]1"&#079QQ<&'tzb^9<@:F[tXqc *|tT>C>Q54VOR :Y/XMZ]DEWwXZe1[|Uv0bmpɢ#3[ SΧ/~rF4Wpks6*\&U4p%TfXT͜&|u=쭋: RQ7@wzX\BkʯQQSFNjq;g_~q7Hs1$]X>*M)nrXFPXOEP3X]Fo3ze4ݱ()h$&i 4-@KJhڇG?t݀;t:B'3z'=<}~Bg3f*1fL0d>d3c3sQ>OZC^z}ULoo:!yx-oO9&>a>$YsOfRPRR3K+uRy}Fj}G.{7g;Pzxy>Ui2 ʄQUfa\,]|ـ=Ɨ^۲?}XNYA&[QH;uˇה_~e߫аBDUXd*طЊ +xSAJJWZB%5)oq*w<+۪tW*\*ڤ{US.Cէ*T{j[Zޤz#~zv5H1ƑOkV١sjVZjkͩucUEUGA*vj'^Vdm{uޯ3Φ:z ]wNu3ժףzZ߳~p_?V?ALjÆF4p}ëJ5jhH5V4nƧMZ5mɩtӠ4M{Sfț7yO͒ovyݛky_=;7P.q{^6 . <עT.-FE˦-u-Ԫ|&:܊ jd J zԺ^A\!W!N!CXCB-[C쭸}amtmVӶN[uemoSnikW_vVwxбq7tLiGܰаO3]ç\tޥN.dtmuDCe5"4"2no}ws{{z=1wk|wWO3z55zo{{XdȁF>Wo~qu}s~|?WC}?\aG?ѽ[}< 4d'? :0` 7Az:;*"jQT&L扶vnAtYc̎y>v^S}~n ΍&N03l1x4fЄĦo 2wHj3eļRS'%}t+]伔)ۆjzbXaS=eG9vO;~l5*juGO}oL1kz8qe7~τLYOhxq[LMO>9ŔSک?O6o;]33!̌yŅ/vVYgݙc99Sd8y--뫤n~b> vnZT}єEh9%5L[b[_ziYe;7X>obE+<*xպUWO[ͭ1ڣZ[mo?_OOZhNo ߸;YM6Mی6'm~eЖ [zx[/Q~ԝa;3v|}-_qL8 ~0P⡧|W|p#:;x}??G?vhub/~q2SO>rzOZgڟlEwϧ]x¥_yI{e&j2keͻ^olts['nG޾rGs]]ބ{P={tⳲ7Jd{az!Y%sMVˬٽsU[k =Jk1* y\ އƙo^nplҝ. lܣf{}'[ sZw25]|[4VFT[*(' S^8 NhQ/iܬȊsJxC  ]*"YG}fEo\oVfE>oU[i6=iйkwn޲w5GgY~̠AÎ_:iNjSv6m~;`Iݩ޳Oyk53Y9\SZWxzan7skM|sCSt}xuT}{^?G1X_Vn]d[lH])OV)8hpy/;E<'P#>8cu6='Igjt}96לJ1[|҄~=~6>OÈ~~J0˗[iqf_3pݠ"/Miao6vXG^muP}+:kn/-TW!*I]+Ik2%Mv=5f%J[Ѻo u/y=RW.D-XnuU!T ::;=Lk["\cqV͠wypE 7[wy仼-9 I&g|w:G%htĕpq6Mm@#=":v2u7wS/#nF>dqju] Jt CȧX][Yd|w`߻R8kӹOg1ѝ:8վ2>~hr];s8%d^*l2GG۱SVBaL_S~ixu{ߔK5L 1FyGrmfK2ZT/\GC-=uV=5$j%S!.zC뵐OCźl^dtⒼbSwúY'3ZEGA}K ;8;@G[ ֦p_cf7:c̖A<ˑKȷHFZ͋61HEq`ZuL%|9\*|+:kߞ(-ьٻC ψo}:nBGv嗒̈诟CnjlP]XG[72 8fb$% /^%ȷwIˬ,__3ZB/ ,@/1D:?i"4麨(qIU(2zbc =^/5rxkWڔ"a$JbunZ6d1%>5# bw:j]m:UէqT.P|1Qƹhѫ z!o^v~dEƪu^ &HFlSRX<(?ΠtNfb&0Ʋ# W~%SQWxjlXdxqɑӡhՋ+R;HݿCqSzEjּ*nWTqZj8yv xcQha膊k?A`E)(O.w%wЮ'x::kICM8!GKU!fci&VZnuْ՗x?ޭ\'n{ֽ[ǩw־1wkݻMuFxnt诿)ƴ]o%]X[vkLߔՋ6f7 לпyBqo Ew#ѽX{{7 CTr'weWvM,%MG4{إ ?A04#[ cL. N]%͡";-;M l:9 k@d|\٤&aUOv6W٬^ҝMY$`7rl);BżkJ»0zͦ*W]]κJ]ݎw6;͒;krlw6_S~ixu{ߔKtlw6e{goBqlEw#ѽY{g{w6;MΦ{g?v68>Qa,if?j_f[ʏ]Fv}H}"ɮRb]]v;tvN[p?U xo.% %(NQ:yJ 9\i|E91v `lb&C{L.29$s%sd P@ 7&"O̊Yp8:D#9#.Od@.Xr{\#GAG`HBfrA!4%Kz% YCbԂS Dͩ@%P:%ҔJU!<VڀV *Ex^Hafek'.*zaX%U_ *WW,MWR:jJn+4x\JюӶmnڊdnmp[[Цh oi {3 M,M@jcu#O6 eS7nRRS7m+]-oG@V 쁕#mk횵kȱv Į׮Zx-FXˬ!~5jUy*+ V)VZuUhՕ+m9Z/b3+/_-VYt鲥 `TF(Rvtlŋ/Q m Z/}#_ E72B_d]p  .x `/|*m.?ͺ `m}_2_|% Kዜ/ 3@sX`>ctj4Ta0OCSӸh7L>2‰H>'+&N4q⤉$a0Ixv"7 9; 7L MAM m84%@8fqq1c1RcƲc"eX/EF;J>z(8Ԩ6@,H#O̧#G~$HؑSHa0B6|# FшԈᶡaC1l(P8* ZQsd!JN%yŒdI-J[$6 m6y"L&,df(Qʼn!B"7I8KD<"dƄcoqp 7*DMHG[m3yaßqaM=5X f! 0y}N,sX&&:&&h>|9cbcPC:y4::^[FՒC0EVkXYVb4Q EQƎeQ4syjeWˣQjuT.flAid~O|KH<@"̝ UT!H' ʀa.VL\ W'K?!vcW>D].kB)RH (pm9,;"gy;mx;0gm ݖ2dmflPV %+ R\c˳ZYT:*7';WsQ mKd(frE$Q)Pf̬Lkqeyb*0|ʴg1/3_f^ % K!C"#E >Cx!dP/2x@ 9 "f^tws1=g0˧υt =<>=AOs2x'OG yz(ao}2BqݷCsww{8}[osm-s-v-9rtzyMpvݴߠo޸RyuȻ~D^p]X/ l̥˗/qaE r.*.\$TEbss# S{03gqB ;#eϠ<p|}:- X8짅4ti`4`$:e?)?uy'i'S֓ԩ eǎ;eXSǎ??ʏ=*#(:b=U9zp+X! :zH8!1@i`m@oٻo^n޻om78~ߍ=軬=nݻ;n];w݉vNKh9(cD<;~;"nچϷ[mۄ Wz떭6B #ش&Q7)5TapMhCF(l؍y@؀s` Ya=!xz z~[-" ؾ"4mZîEk^<@sY<kV ^"Oc32[QNM5p/Մ8Z x4(]i=44 X%Rl7_R]/8bÇ 9҃nVr bÁ 8,o@ahr%<  0^4J<x,X1$|&0/@hY&eJ1A2q@6 Ajedv=y {;s< QK[%ZȲd@f7ѺjfcJHALXBD8ѼedvP` iPWBy!ȅm- t=!bF0f/ăc2)89 zk$xkl X{\jUgWV J0j%JXɀpZămaq (|rl8j&`'fJnbxXXD/\оH EEZh[$_h<1 |/X0b?[ x dDa 0 Ԍ`nA͜a̘9hf3Ӧ)L)8l9hD 5qӉhR@D4>Lax46?axЄcqX417=-e̍F󄶍t$xlł^ Fh:*| 7ZP$?ؓdItrR2j͖< 93e1 CP"J Q&~HbW,Y 4$d xhoYP l`oP8X}z>4_ "mR`xBD`i[8 `"0Oy%( i2HfՃ80R ^A܁^( Lbh ?@(` =@6pxn5pY`#+aM+/+R06I:L 'Xxt2OU1@SY4Wa˰BdxN`ᏅY< AȉKH,= Jvng!Ğ "8{g^{W;.MCVzK[sl,[yKR8ȼUpF5k%uWt:+_sK/wQ.˰"uBytxra;߰g=ΜH0H=M:}i :u򗓿",H_ؓ/NC3^?^8xr'G?=@"~ GG`6G< ut0\{<8xA˃̾s±oa?}sݳg/GA{ {,sowN.A.m'cμh۞o嚴vK 6'{Wǒ$xRFlTEJ$,6E(e%%![E=%e˘;kۗ0s9ιw7}}wЧ$WFPAh2K " F WMLH$NBMă $1.4"N%A 1zDb%:ʤ0}F_`~Ph3 # -ԙC8HdQ1H CDH$0D pFb #a4猏6>1F? z26<0T9=6WŌG`# 0$<;50ف 0 @ &ʪC&*#l==$z;%'KN7M$_U0N%bvJ`i;S|<N8XB@HH;Y1S?ĈZhB-EVs B3RD,+#fQ-@QjRa~d.@L}L~u/d:j,`LOCjGǐ+a%BBp$ ЗZ :dA?@z(@tLPH􌰑Ns9ր9a"F"=+r1}b$Dǁ:J Ap`x[$aǓ5@ j1!BR~aA1"3 # $0fD4Q&KLS%s `T1)"2YDAEXp2IHKU_?N1a)dL32H#H h|0 (L"Vq $dbEfA$=HOa scǁB0QS3dx̘W EWAJWFUXxi&kJfX/e"$5ψDd Ȉ A'<+5 D0OsFKG&ȓLbhLԋ9Yz8Gvj3 2" !4*: df #<@gUP0@XR MB2\g|ȁhHC 02id4#5!C2;9/2 DLV#I#P" 8` MHcD Q0:9: 2ˀ\@e s!eʌXdj1Ep [4xS3`Ai#!" H0E`+`D` !XLB@ L;rX"}6]>,X:,눙)_5M1jZ磴1lv/S|b7#WSYcƇJȬ[-%oTꈱjS@[O:\ rq3zXפ?=5\PrOg,u"] * Rjqr6#NqܞTqW;(bef-VJn!nm/JvlܗfB(8yvжcoq(UsƜzTMGܹxܿsŚx< _ͮyG"tʂ]L3MYwiç$wM?o]Wˏ}SMIxXT V='|bd'qL^:~-۷~)‡OG[m\U1׾1U/AbOYT^#>ҪĬpst $YNRxTcmeV00]!F]G{;\V 3;L]%=Wם),3n"JY޼u%f…mM{^;wsNe3le ղwY˷T^A͜>~)v.7'n_dQN=7I.W z儹^VWoéc)|Zm{g(sxz} ͆ xd덟t_-H+<1qʫ]~,_yGyp/zFw{^7NiwwnrċFVe+K&q(ĊX8W ;%07:;v]g^a|'yd#Nno(]z{=clJDl>4_-q8ʈcdz?饈]h]ۣ$۬q%;~l}>?}\hz^˨W&ukUp^KثN'/cؒ?3Z,.pgxhƑֳ'/4UO%HY]_b<0YJ XKFbChh\jcVg~GwzO5ؓs8Vq$E+לtl몆2H)*]SAzM7 4r,h4\rbkE1M+Y]U5cj/keXf]c8RbdŰ_jA8ۛ{*]1u>NWL$QLYh`.1y,6XL\+nVg7]&Yqc-O4l=Mהr<8c**hzDh3 m{ ltKD0-oKu,K`w7&8mP]}uWKEo^֦;)'eq>wb,7K G%sd[6~&l_.}q]LۤDXن*u6cu9Ǥ4Xn]tkN 7< 05QYx\m^ k3=v&)9߸n{҇sȾN3FlԺLs]\PטÎ%\a6z&"B,֙ ,G3.dcSxf_M/B!f }}/Q<_3Z2SK^]л)û-~r>ZQ(Zzբ]) | ;oW. |X#ݜeqL@%7c_Z x9hMpRrtptMxk' NX&UY\xWKL]o}+"%dUE[{$QlxC\>m4 ҫ{®XLKQ}([ޘ ;k3v'ryܼ'ǯ&ߞ;1XPuYmeG9wDWisM^AtV̞ܐ\AM<:?e[Z\V ΜC΍>'(ٙ"tn6#ZgDx_-J~W@贔zKWX;vbq{¢KF)+뒱9rN|q;mdí)DݍofՙbݮLE\I8Ue}ZõWBxϮRገGD L 5jM˥5(-_o 6 ݾP\ٛG\:|m2vqK?9f˚Pf|Ν ͋ dz)}.BV˾[g6V'gEǫU88|B[m=&^~*:i}gʎ'>֖.4*q\,cWlQy! 9q홿 _+["uʷvچr/RlX}SbwFwچoWYRB\@tqn-k/8+Z͵As nxMVz;$^%@wyod-)Iݐ֣ו?QHhW5SlN毶X,>Y)r窡,M  :[J0(8hq픴9y*^ŖK^"wy.kt=|F |\~{=u]:P<<](30zcb@u؍ԳKL~&<Wx}!F阑ßžpU&2BRsoܳnf[1@M[~G SL==d#^*av2Mhpgs~]Dnj&33tnooʻ(Ԇ&o03I;G*>1^䇆H_=DeQUe#.'?ɐJzn.G[[&k]J:ɻfٶ>\[jREC{DsI/ڗʚi]e#޿ZÃ2#.>B>P1͊Od.{d8o5d3^,:${>)YsYpIrk_}nf_ⰗO'{͵.] mAʑڋW7Fp=./%g-JDY Q̸:HY$_&q[dR?'h~?ocM,o^1B/:P?W0DuLv;g2ԭcc{ۏme]9/uh8dhnTբ;C ~*!diʣv;A7$'(NzotGwݢ|r+?uSxvAφ$v?֝/$wW?Pa%%.|էeY1h-ݴuS978 TΆF\ @tDﰩþ'y!t7C{ԩdČ lQ0}JJ>&Ζ䢉QQ/T`7޴3a0/LRf@L fϏFy ~M-Ϡʹը,xJӪP '=-kvB6}>\3CfJߩC[>ZgP{bNKMDwE[fG^f/~#ج\(iE^N8޸ªWhET7(/ ?vGnE2޹TEaxXFeDV2 hT4!)FTj%JaA%E-$05bP"f!&^Iks9rk/P([Mq Z:N).@ot7ʮFIn@Q2*e/``7$G8T OMg8|)vJ(mЫ&*.$י"H$NsԜ'^B]OI˧WypbE6:6nE'`*"'Iڞ`$ IVFΆ"Ǔ-|dE< Z[:YO /Gny,L%YK"l \ 3id BLk!!vHy t]U;ů  ɔd{ʌ֌Y9{3ɪiרz~p _PA@S#S3t8zh,"׿yk~VG|J5U׆__2N K~ NL5'6 ~֏b/pT<( *HKC(L*!)x-Pbyzx]=k tk}wO.z3ɹJؒ"?#"@=.kMՏ$1tnϲVI"OTu)?GtG=QnU&ѻ#"#99+~&{~چG;-޳Smtϸ]M;==09x7i`-mo}ZckV^Tw ctl^þj ol?-׻Ӟ!/X?DeBY0Sә4X,ˠ9dB(z4'A`t 9 -ċ @ D N4 }-:4>hȄ1',4?D$(ÄM I,`2yiKo~Eg>զvڐ62eFXR\ )V WFw_ZhR@Dڛ'^ҢDQb*Ek{ ,8巰`-ή] R?88[}ai™ƃa7rrj:߯ }ޗჍ [Uyq%R/ :v~mU7:2Rl dKo#>^_UPVz@5~ROHF#(f#A)pk8_֝/*3Ҝ&W=R[$1 ݝ[_]Ka?Vb KKETn]" A.,bC;xjâZ4A) QaǢK}}>|q}y18A=vI:84+aZY} %PRAs8fįG)ˉSՇ'ÒAf?I٫>zD6;*nM' t*?݂)THe2v&oU IbI%UJ=40M.~m_{UfU&D `3f3ٍHBM!@__")RvP "Ll$/0hrg*2 QH(Dy.WyhQSy`ޖI*o`߶Zּg$D VWlV۰&l7^_Hb#L\6B@}[Zp/(bwbPM,_,uy]ٓ2NiG#R17DfQ0DNhvl"]vU?#zTCH8t!<~P4y Ϩ3a1'᷼ CA~QwF\N?8L"JN"~,~ F-[(EĩƩ jMtM =BGneƝ |yzgI ި7U{Uewd@VZaSm 2 J.?ck!Obyn]w,j1ua5J#crju/ iKweKsatҘtix?bAS+ixMǠ׉,'u ζ?Z.W;dGQwCZC 8`Ń{y[ؼ+$OAs6IrUUf3A.ץl'`3sxO=d)I-Hmwn ^m.[ߕ?{mJX껹/mY3f'N+WP 6 5^v%6~=ZfCn\xSSecMx0}mTxnp2Jf&F™ιlڗ/r巇g2m;7Iv?I>ўS3Ӿ5o5o_{oFЛ@]jMZqN O%(/3^ D٢H2`,%R P (EM Db!,hh7H8Dg=6Eo{y^SZ0M+$a߇wVC N#L lE[ ˫8zU(. D9$Ĝ#SvT?Qr>X^ۙr"$ދ1T (@p`i[?yU`XH>g]e H 1+xDA#gMroTXM!B;Ӣ"eӾag$caA-{ O}_W<,]rցHE2/[WJ gZVvF7Nn,*f>",2y5@zYrڗ>Iʢ rq4V!+bʾޙe{^6M~ܝ:}stUtZsO] Qv?<~ԕss/[^Y16]:_2s$km}|RrҞm6%7-oecvTc"y {לU}%~c }.q6$품qƍ>񸃼Cުֶq0ɹOә 31ΆResf1yM^}{hPIGAA .eğU_b'qOs+J!T!vwpv-],O˓1mDߐ̞$GgGc0 8F=/ K_Qь< ^à BD~H&Ҋe?<= :񁐱$yA$!^@U^K\ϸ|.AqހbtR"`T-=|3"Y_9vł%$( h! Dje%"ZDH8.BgEh h1TDRj;"V-&EDpAj a̹ĝr=s#z,rp53*K{r֩I)rʺI[=؍C(!0$KPR7I,/掠{@q@.I5Z7J5ȦWF~أ2b+M}P>=y ӆ\U~՗zs[=C';#"< hKPo^8F^>v,XWD)hdb)~'v!AŔL+|H+|Yz<,H3R`M.XѺcbHl-Zۇoޕ%a s2Ѱ\ -փԋ"1OSJe=S^ vϣm;N}. lgSC__oww2FyI;E\Zee_1q;ӓ|zWKZ?e~U~M+}C[fGNf:ʣo/uק0}sȝZ?toqBzԡ+6:kWOAˣwI" ֖۱/3ѩtH*yђ2L9 ss{=jU@y@_yOy NYC*Cc:, f W>Ȝ;mz55W6sYDȆ"/d'~#àD>?AYfu[^@[ĤR8Ϥ ӕ^inBaDLp !T:sx1w xd6RS|2qy<')<C{9%Ecʏ Ҍ4bs0K{H>$g{' MХgǷ s 2k)QҨ,,H-IB3D1"i">H BM+!-5|`eTb "Qʂ2M%I4h?>{r9{uο (8Ӄ"G:N`r@L.0BˊNO;}n L\uɫ'}]Dg˧YQ7S~Xj{&+6Y5FlwkϏ>58ʔǕO<8v,xԜ;_\"ȚZw< B)c;nQT[-(P(ą&8H[*JI6_<:NqyʰeHPQ~ v>BVO#!Txq*_`˶aS& ja"e,M̖t ^eT"<6;ylωRmNrW Aw*-\ߊ! bЃ]/LB䈖'IFR$ {_e"Ǖ-|,U\ Z:{mtf=!nXXX$!X-AãuPzljbI B7б2DXSpA`3)Lmw5U8{L4k=Li ?$Ww:_A@ۧS&|q٘ʶΫuւHKDԑd۹M[j{_pE]M?4K~ NL6' ~O|-ŢV%UUQH76/T I:d=62XRf>cV … #Hŏ)'r^ҭlO]FZ43?3h&gs8I[f7 psHlI KXm}D AuTRiG4A#t5!U2!5]ؘamC*.߿*OrMR>nyz j+: Kw/^x(98{f֩i߁2Ɗ GuʙH,N϶y$%*ӕL3Ә??-bPP 9f}Ϡ? ˯Xzp7:@HW)f7qXH„1u9Yi~H( lb5tfʝYh`Xa&K@ʥWE-uiS ?Y Pɕ1(9$e KR#MaԌ>yiKo~|[AhC!Ij ׺BRHqR`224hF'O= %DEq|DXDIU H¢ZeVF!!"hS Fm*AA!!%CfA? VE- yw=?@ys9_>)SdZß6zgKmNԾ=6orz`[+={la҃w}s}J7[k{kkN-0jgJ=3']=¦'Lm}be]'T#كzGB&wddH~gpMJx$Sf&=dg4bdA=iIhp0-I_}3ԁS\ x(Ax_/_V{W=~ԋCcVc{˞N~7Vxw**XhM'҄TB^|-'H DLUo5$$yqh|Oo Xh VYV5=gfr39C!ח8EblRj'aĸmdw^8Ure у($ dUZPzY1(&AkfjZ+֊gTD%醂'[lGQ6~~>5ڵʹ_?>s~~\ԥ!.N2X`.h#|1(qR9+{2^F)V=.,i$Gԉ{uEvt~I{O Бx: АƏ3jRvL$W{)Cu$b D/^Fv!vtId)Yjv JScZ`KC:!`lr󮽬^|i.'J[&B0&+xcǠu̺e۟X?qcvYq??d"?C .`nc۞hO G$-&ddypԥuK{q0[{ڳXQG% )]ApQ4*8%BcABԸ DyX71}{gݨN&?5@ f3SKf<ԂOxi׿zEPU_k4J7^! 9TzBz7[׽'N_B' /_jY&[6;sc=tX]4a?D-|.;4*7T q;k,Kj-sמڱ =L,]?6ƨi]=[NIxr̺I͏M [N{;tkm RU@nFqUkSvnTB}:|jhi3`+Y!pp~G E|>?Q:(8 qёSv.W?s>_ۘrNIC뗡%jg8R~;~'"|lKRTlc)pBpx,<"3BO&yXr,&ǐii_AjӾaHI54<}4Am3V?NF%{Js՞]sY։MN:jgjW:x) ܹ#"6(asB=ǜaF`)~[@ 1%(UW/H{ތ%}Gnp~xV7iϳvrJ2KWXKx⁦^2WCU}}-nV(r )DhD A66+5Ko-='h~+;.Rm~o؏xZ2%xqE[|@s\{ ID$FΏcx܁zoճYbtkU8O?/Ng>@4hä~6+K3KmGD}|M"? D+Wզx5Ϣ/a ~Q Ѹ'9NUp]q]I@i{@*!twpv-]+,O˓1Dߐ,?3[eZhr0ji6yqB`^+^OҊ <`?E^|SuާBW9Z%ۇo$ޕ%a s2Ѱ\ lzZR/[SJe=S^ vm;N}.6 ٔ&..B(OA"iHkQsVt]&yV2䪯r@'3ȐAxІnL'= ZBDki#nstz6B( ApvA0'GɅ0{P&|-yu$J`:&rLx<0*1!AZm ϗԝ<'%f1 R^d« x}81'lbPaھ[CR_֑e~U~M+ۂ-GSbS[K|7ɯ{>L\y;r'V*~+[g^{3y c\v|>f<fu[^`$2*JL>&OWL;wyc_SDҙûLepě'MOu6҈|#]HTQ_K! R$4#K#^ğS$bJE ,G+Pɠ,,(Iz{9{0=gﵾιη m4F~)-n x/KIoJoIs9^ćhԜD !445<cϓ7 ۰ Dq81@/iK/dDJ0)9u:Ճ9, Vgr'QYras촛oeBOѹIW]݃ݢ2ڿz]&{:ZVM%Ud6EkkpH(;Y;2g!k룋w_8j*#aK7T- 5vz<D"O}`v؉e+Vwt*=)ByRm6:_EW#xt>:(a! 2BE p) zĠ_;ťbJhT3ԪJ(ci`DU&H.cΜ($('zIt`WrW `[<"$0..=*:i_T"'hyTa$%A8U6,r\ANJJV \B!vJo[@3z@Ēt%qql X v4`!J+:,W;Eo*.;S.SfgIVf0M8J.^ 2h(?bjT/N2_^cmTPc.1ELQ|6Gצ Dיdsb5Vг~Lj)2 [Uȭ@n_]HC˜k !L/٪OkUp! H偃0s]EU}[_>;zih-̒烷H+fgd8l>*Gh ܘa9-2)a钾$3#J?=+MPplͨ);qExisQlsPBOg]ؼwfGЎ7ϒ-i;[-s^w6 }̼|?ʥ MQ%ffݚwݵa8{J̶ɬ1r[@I7XߝDeLY0|Ԟ3No4LϹ4{M=I]zMCz f z"؃^@޼uc#Fpd~s. (slb5YuM~:ȝYh Ha"K[G*ݢ]VuPs?Y PJ`\^0>TD""9j AXT(yA& = hpIH Y""V Hz 3=Yk sk}}J dJ2\У(Q'Dx;\lFpCeɴ5AT< *)G+#3o- 1yv=iIpʜ$%joqՋ=KnٺgɞfNn9Xz3|x{l[u㕎Wn\޾9~˕jk2F[Za;}ϔ;;&N;zxͅMgK-O d7:ߗu@X} V% &wԩ4l3&8᚜8I+^}i`dS&{RG"ΠaV4/e 1za=iYxx4-Iiut*h4ׂ3(#=J2/W2/VW@aC~PS 52{P[;AE X+;, ViB.!Z/' ΌHJ`DǙjVɐ$KUUsyyOXh $FeE`ML"Ds@Kjz"SkGCA]50E 1sѻ@~ zijl\;})L!SHnWhbI-1$`HPta?5֊"J$Tpq@6tQN??lh9;zib{RW}'Mp`K0 XaP J|biҜוF&(9:e 'J7DgQ0?uNtv}E?R?Qo=J8ꇓȑ8u'!A4y7R;&q˫X-;3BwP@ DUD5~ցJR~_IȯLtH:nBPG=%zDt&[-wMUZ5tcݨ&*5w]!{WHb BY? ^3^g"CfRMU'd kS3Fu/@:0]Y={i.?/Ic?O .A1&kExsPuuѓ} c]oLQD by@`RE!̐NLȐmӔJMERJ5t,/D]H-/Aޙ{=|}oA 4ocvS~ $)*i Pץ֨N&=5@f3[;ʧ OjA+lѧj/4 ,ܵJel3kel՛ r8MNRB' O/Ңv)}w'z>I0~e fo۸$wӦ!8L].rGoMJRŋ^l,KyjwsGm֓1giByk{/(ȭo*._$0߉ڦƨfЮlWZѫV=e P5aJVH0[`$nz=^P ^@|YK$RfQTHr4!3{;7GsMG:t|P+Y!+O8s궯og;R/_ˣߪ#Ѽѓb',v= 殹cy-ckWܩȘ{`j֯nuo}vg^E돆b*"wZh,~G4qR{+ՖQo~p/%\%t$ l3lY}Kپ[,u̎tؼwл[D{ }2# ^/#)_:H`9?Ym IDwIpYĉ<7qި'3֢qeo$|hъIti,5R/gnm L"= &DJsx%Ϭ7a K"'cHv*뢍(& 2R.`Ϙ.kq'ɡ6-tN!3?3([G{ƑEW]B!K~Sc;Nۉ{; czi޻^E  Jwre?f+5^10ִ?_]^3K{J?̹~ g/go cpV?_o׿Wp^Xdm\W $,Ngo N>tK+ٛYYV??FyhRS*K[?#3"O]_bO^>¯$js[-gO3xO2$b%I?MҮDe߰bsl yF,~ҏYI/2oQ>'Ǻge=V_jFG2VF<;T~]iL< 0wބĴ`7l^tƿI@;@O|_J 9?yJaHl1uo2D&RC4M y ي|\@.#r 9@!!r9\As"b vnC7s#'^ySyI;^F!ۑ/CaDG|:wDe m6OC(y1FN𛇐v/G|tDm: ܙ!~ aYb ]@Ó3\~(/Ə~0{puydYKVKwOxku;ޱ_ j-@S.{Eɞ{Gy7LJ=sfwIycfF<x<ގyڼu ?yN"#Y 2#l=kW )/U Ȗ{MZA'BmWc uϯ6ʫSTQКQ4T(*\ -m)FxH}8>hrjzffvV*|4>pddx-=U\^akUa$2{VMơ*+,"QSp,EpxDwtl4!OM<J-f,%MUW?n.D C0JLJBgE:$GuQZ;zɆ#cMMNLLON=h\@)2XJ|V~q{"CRLED=CHSd2Ap6J3 z02pģ#uu6-樑؊l-bG}S" CAI%m=}<CQ1QURܸъL5:굗W$od .#feclt6.TTSUd5:az+Q^@2΁UT%-]=侁AÇc҇aԍz:[$ڪEh<bk]3ԧ57AS8V) (~0`ddtdD:loi*_86P&<5&CQ]C^K/x.]H w nK+j;t{݇~jx{=Mե%K]$:PP3t^{%],a!T`Ο9⊒zIk ?83$dlkWxRUqyђAf(t%FSJj:6ި9kfA |==A70{'`tu55+ B%+tMm6Sh+k\_h'f֗7Eu_VT^ iattv`}NpI}mEaYb%x uue"EIY Xկd7n8dnI~iMhitzd]ƺ%V'STUIVd d*CLUq:~fP h"iwi))7Q[;=}}=fI8bpEUT0 e&xl|յ`GJ&DIJd2QJ"{,مwk%Jͭ흠 E/[֩j=Ԕ-Z,*M&U [Cjf ՔW_ihWHǮ8C"I9ddU+45j:;Ai7%d,445.35x\&=`-X*Λ) !΂0OaAYz=OF^V~IUu=A.us;;Z$ ]Kp UX4 K"jr\MU?:~3$#c݃i9EU MM-ഝ F0QeYaN% ˢ+Q*k8t U9|>GU[j*<0N;yr $,ždIN.(fPNf;dmVFCC]m#+%+y>W񨚊4EG[i Yu euOYAKURT2! _Rt6@`4Cs2%edUkP IeVT;܌;Kz@U(X(@ Qbrt4WQ\:IQ"XJT:JQP LEQqdTTE 2&-ejws JUu0QEkK%%.V&JWajkte@UTT㐸\UMEPTQnB% p`7Rfz,Q\Z\jV^q)V\߈oMn”TU^RėU)[A,EWSYU%Ԡ\EMVE<3IQd9YэN.f)yfNA4ݬ܂2Tbq-涥Ņyٙw"|w#-*OJɥ UuZ*  M[Q<{< MIH+,.)VU/υ'E>.^ ɩٹyE%%Eyw3ӒEKA:K#0tՔ .Q͑T4aR Ighg@ߜCsFE'INʾOև;Z{/FW֤UB˧ VoZ"LhI7_FyZٻzDF%$gddfg&%FxZx.Fgr*  [SNe`"X6D Eή&^ ;;Ą`bTņB^Nh\St* n[;xxGFx99Xߺ`)UJ\o_TQՀ_3bC{ I[ͬtm]=}|E"WXY-hJ0*iluǃC b2f{/4wYZ\[7.^0B3+-6[RS 9FGB;Du)eY@zxx{y{{y8|8*i`Z!Y3 O1۷ nM-mlUlM oܸipˈ[kb |-j9!5BHZ[P)ƅ*BPjY>yt2XTS Foz*A% :( Ϲ2ѱR<ίX0 x8$̆j>]{ cG.o46R+=Fely'u)K|%T zK u1Aɩ|ֺw=_ joK> jЯ>υ\ب;gu˹ mJHXDtL4C波bbcb"BTI~*'1*#|'[79  +>XUU{ 4ΐ@[[Ј%wprPP)s1u8j8Y9z&$dd%.Y>VvnCB]<<]|qȀJ4b 7w3;;/W[M`@nvn..NA8P`+D'!ڣpM WEq1SPHDH|B\۟̄6嶹?&1䶳# (j\WFqg\5UCblow(2>*M>YO nd`lahCf`gmndd'a*)1eL q&HN,ut3wu LK_x m9 lX'V2}V,#P"VF%|M oRjDD%~VnN^0IJzlr,ǛFf~"k;[[KSvrY %o@ U_r }6=#,^cb. JE%\{ߎ2TKM'$_?k &`8R05鶋wLCDĸ.vnhfe(#SFfKG'WXNUlg P}[_ebigooM=1̖agmitS7'q@L"gS#/0W s s>MX>e$ccZ[ژݸba$(cJP >Q.7%u5r    U r8q,c˫Гlm/ =۸0BB:F_1M,3 ,&,6$0TBd7*Mc m?n HqYD2t|>2L9K-7:8,!^$ cyxߔVͺms9UlPkX޺vr`,Uo@goxtDdX@xd?+0}],mC MCQm&[[ܺz0)G,%?Md :Y9{*@hn\𸱶66VW/ܾ.G\X:" c0)BHhyܽBc"B##5}D~vnnN&淬L=. 0/^54DèC@cu@Vo_M0&DC `!Ms QB"Giz{9;Y[Yݾh`kdz> 08Wo_j`b ㉝}`gF1~M9qIӤq %q𸐸OP/˖VfW_?{Vg(Mbp:Ԏ4O!+së\D!` rd2lh$ N/$Rtpq0qGp_P%H^^qaaA1Z!bek*?ȑCm&y-!`<?,MM ]>{\'E54-$UFK.э`Xd\PLDDl\oLpK:uba^9yfVV6@ 112пxSx"3|wO_w|hBrPl|xF{m'w^_Ǿ|Jx 13516qCi;,}9y"&0/YnfNn>1!!\휜M 7(?wud'/9}QܔoN|}\,v 9 KHmՎ^Wm|#c}B9>&.7._8uT  :y $@ o/#< r3>$wL, E,%(K\bmkkFN>ގQP[UoO۷MΞlxg/o&ā6鯏sA./;yx߉iPRpKW<=݃C#<C-T`qi=cCSob> N?r⬞7 \ps8;]~Uuޣ,h~A]F*fKb[-L BDvnʖ7 o\u5l}xYO5''JW._ڇwG, EKs6^L#s/~ f9Ď {{\֗.^8wѯ~zp\TEsTH1*HIc`Fz--l}\V4г42: ˲8Lbڽ_}Sgϝx0?w3ڿg׾ݸ h ɢ=Esj/_260t473տxaivV]t>ق<}曳f8'O?v~d,]qM#@*\Ba޺x5+םMn_6dL30DW(Ͼ?ym#JO=r}?޶gstR!Hņ=wk|s s7nQ[<`]{}{ׇ9vu:x_|c';\4pD/k\{Bn\749w}+ư϶}_}}+W6ɾ`|]67:[7xR-={'/8~#FnK{l_`Kx/r޽-؊Z(Ї Oa4>mW9~C9vsҋcIm֖`Ov3gNݟڱw~(F3ޓ!SaV^:Ͽᓗ^uq,M?ز}Ǯw}r8tȮ;?hnzwq `N<%.sҡ}S֢K<'7[}G_1޿mooMxĆIHSi0`# Á9]BF9{w~'9[J{[;w}~';w@Co|oȱ@;8ͺ|#Sro?p˶5~>xP_%d)'K&;~ey^2?F@[Bѧp۬/` #T@|#v # x!W%-a'v3Fmo/d#I [1m BdB aۍ'䄀@6NR)×NЭIPmA`>!UֵJ:{~.'чvOOM ZecJ}h(Z-mjgu7zK xOCcӓGGF*>~pbjzbtH&r$ f ™) q}CO{1GdraO5-<_(a% >!\c:4]XSUU^_;<𠧧@ w`htC>~s81:4m8d7̓!ORpMbʦ{C:FHjP[;{zq^?<6^ogWQ5 ;T~؃I] \-ΩnwΖWD:`oWW';082z G^@Q)KœUbq ʋk:[w u447#|kEnli ;qnh𽾎F\5ؔ?z=Jϣy5%E`njGBQ(5 `Iۺz;[;pZ[;{aw[N.,E 4C4"N\+$ w4u-]AZpnx\X+$tcĥ y55ڦFoGK:sZP:6޷60MNx.`$8\"켒&IMDڠ*i*?݆U4uuI 'inln6TVf4Hƒ4x2+*jݍzfKcEB֊v uKqƦ8TWYS;0_CoG;b12::[z{$͝m]fsec QT u\Ɩ8S[VU:N,?GNL)zP\]%)HڻTRCsJKMCEsee,P{nLʒvx "W54^Ҋ\92S=KfUT5JjZuumʆªڒ:YEUMm5 u8INXI]eqywqHG3@A,L)J+oh5׵56pj`h}E^AYmyi/GPT2wtGm eEesS);$1%9eI][{g[GC]['n,˫*M/I٧+W_Z]/iC&U9qUM]$$W M̵  FY,DaAylnH|=mzq}QMMyvJVΝR&pS>*ݢJqc(B\KS][Q]SPWSQ[HcL~=Eojb_J)/-Mֺ:֊FqQe)ό(̱xE(3UxTTTUÆd1lC.+.̔g F N2,BO-,//i47׵hU_6TV&F%$$g\#φggV9ʪҊZ@_~$ 44SH)ͨ,W֦z{Ш))/.JMȺ藔t79}a&Dz^zN 9TUBTTWUW !;,.dPHX{]WUk[:Z[kagVe-ɜ"R *n tuQe9.TyeQIYlDJN#Y B,|; KTsV knI*ZNAN\7?zFLl8Q\^QQZRXYKSZQ┗'g&} DNc`&i꺆FD]efeuUIuYQJRj^" ŘIOJ),-++)///GgM8^r6hM+[QSQW\.>"Ԩ)e%z;zx)5| i )47%*R,^S mK. 9;5/=b[T\ 2B|vxm_q1w2s >/,_PmI#穰+3uZjL8Q Nc \52/ !*,3ȋB]XMNH+*.((LD LH$DK16%9.%NcTT2S$yP7=I(*e+LτiI1axJ)G4J< <:9" FX\CEN;uYL`#wd|hLbJVvVVFZA!&b3^"cb `0]r=E;q)ws+K+eUʙ Q!~6 { 5<&(2>)]AIKˑ@MNScC@z"MT/,G)$dIeg-*/Uyy); AAF0M~';iI9ٸ.$%&EEICPm;bD=iqLLLE㾠,'?6Q1:4&,Cwؔ68B3eIɠ`|J2$< :UZ._Q\~- %F\dzt&Müaw>OHaOYd׺ Rub=aN/]j IY%i9iwi Jl ;#RpԈ8@!paS 3s]rfJPTphoBE2FDæ;8"JXDddpCTl;8>NC?(ҧPD]tс`p}5ưUOo`XDxXhHR" Zx)=촁< .   w4c+oophHpP`B ! ((8S,_ nty9FD~Aq1AA"kHּIV⧇HGƝ;Jg|]l-=ZUM}-Z@4v pu tڥ+x`K8vGWwW7Wװ0 ˜~>%(/ q!O QnXJ ?0?vAYÎ{ys!Li;ev|EpYK-:a9 =QŗwYI[!]6=&,شMF^f/|"sٞFmr=6vßS mv}d]+4Y7*CT¦F%Hfg;~c N\D@FvT eAs/U?,EX/ZM]2t#/_#|`ݏ9Up4ЊGj/jn &Mm'(j1w]9ӮCФ,C_E߃ЪGI w2Z^R <98N2o0g0 A[0ӈw|WD=sdᔀ@SsPK{qAҏIbBK StartIcons2.psd@v{F^ AAf/! IxYg/@S ر" 33󦾙}My3W߈(F CN i QoHZ'1 ].m<Ӓi$L`q_(^փz%1Y|fTƀhzFݍaG3=a i4+pO1xqLwvTDe#"~<>dˆ˹ޱKOC/^vl]{ƺ=xH@8N RSSmSmy;{777s4Dǟ)YBK~ZO$i/ -DZK%1Ajw?>&y&CKNf4X] A4c]\!~~|^<# 9ۻ9;84}&<}.@"zNAtz2.)tf@ `rDpB lY,O/77_@gGǀ{{{'g7'GW'?WO;Ŭ"wZheY 糀׊ f IFZ796`Pk'&8tБbx5 J6hY?5X /^J3}.?1p2PYZ+C'6E8&~K=9w =:&Y\aH,: c\G\0+d8Haɾ8p b6DxXi0/Ke1hfth"~퐫IMrZq|'2A&sdP c  'G5e}I qcDe 7JeDq}y|a|Ft W! [AZsGFJuRtp]!&q/h^g>-L3f0" IQvFq2,\ϑW_/O .=(Ţ1>FøK18^Hz |ƨ&cwN%/!%cb]z w0fb3!k,8 {(%!Nȶ ;g qĻaGFbP z!78ppK/qb2`iI ل@Њ D:x 8 pݐ(#l7%}W~!x /WEXvb+GTyB caP'JQ :G()N*0AL.ǡڧ'K <1 A@'˿?VeM&H`ʅhhZ\ 1ӄ!P\Ւ%N3|ج C24)L3capq l|hbmAinfBtRP.It'SΤH[3~=#Lr.cq@ss,Q#~[6u([FyvS3(WV[v,{ >Ȑ@KJ@"t?-*!fY=XWw\0/l$H8`MfbsERlwv+.aW[X9{bWO2%YHH=H$WRR)IBMJ qI"Oɤ5\v>R &>'L!mɝvdW?9<@K O!&F^KBM>B.&_%k(EbNiOR(ᔡx 22MYKɧ짜\S)4Lըj6jjj1jtjf-Uۤ[ej/ۨPwWQ>U=[}z jhhktp1Fc,5J4jhjjZj פi 5j.ܢYYH󽖮VP-$l|5ږm[d2eiw-Z0-fXX\ZX&YγcyZJmՉVխZ{zG66D&͹65m۵ jvIcmۙnL2cŲZhUdUEmAQS_o>bc=ɺN;,pˎV:qsDzNڝ\;%vZTwtyZ=t%.w:ڮWitsmEKݝ'v_br+zT֓sm6z6>6)6mۚۆNcܮPyv{9bZ떽}?It@wXpQ11q=z3{})iQ:ggssKG.]Fr=wgw6IyOy*C3>ikЗwC n>c|<-}7??w7 r t8X=!mC!!/xQ=?/lAp C߉16@ |iS(ӨQQyQo}Dߊ#9k;<67 A<~pVCXC;tКa {4iFt6V##2EsAFjBǽӟ222*^̊xO$T&z'f'VXKYY5]Rx$1{5&qx%=&uhK~I0BWh&SD]EIY>56ugq7\zǩ;S~xk&&M8!sJ棉A7s'4қɃ&v) eTקyL[گ_/Lwdz ƌ3{̞i},߲ijg_-M\zuﲂmO_ncEʾ+W]5sUjk^ymvFNJuNw]Ն6r7ot<%77Mޜ͕͢[o|53aD۪~mGGwiBIwܓ|%;co-9sh!qQFQG<<:c]9>O>x)SEN~̾g;>t v_tۥ%%JK\|Jȕ\v77d|URg1}ݵ+(w.?xQn=?|O<]aUU=zcgϖ?|׋/ν++^[55wr~|7M\?T[1_[њL:A~=xM'V 8ƈ>_,0>5/ cȇ1&|[`NOKKK[KO[[XWG׸a SzeC7720023000A >kʯ11bL/#>NT'vqCHd5u M-m]b$ #S$FID!55)z Ҙ^g zflбE8I%~G.p2_crgь9B疥1r. k+Y_x꣚vKРi79u1F&ڪ:ij;*7Q5km<3X#%/;OՂ.fg9-1Bi\.*0= *j*+pC2I՘2 ӹvO7sN;>_=!Ɩo]Uߣ>yHB[+F.NY4$Y uzǷy`8s^/dYkc梢ngؒ7 ]}2_z퀏6.ڤ0CSsn16y{[z]6[u_0knQdHlauУ)w,1z: 8`}rhNV-3/w:v|BR5 k8JC m-GDtqMygLA1 c[so?Ÿ y)c=ߒݷKR-z$k oG6|:,|0ݻ6:7vIǫ&/w1jjS63ccw^(X=n9K<)YT?Ӫs3OD).F^z^xdѵҊ/[iT~S]>CܶSfRne}#sقZbt[bL#0>hj]G&ݹW7Wԋaon{磊UҝoBrZW[ͅYjUGf+=ajI#bz8k*Eც+Y#^Pj۬lW^G|3tk~M*uq:_p}Va Ş-{d\)=5>41rk;l˭r8)%u);>zLb瑑ŶϚcJ>vYb'*g; :I'dw>:#Cݜ0ws]6V]XzG^ۮ^£?MS#|8嘔`Ŧk7-'blMB}vG)x3EezN[-WKW9aؒiVzw-fn޽cf-L jyX`o߲Ŷgs.>OVfK*ZUӮ^X(;erfJ{Scybklf:4Ӵ}UasL9JZc12sD JyO}|jyz:blR*c69yu]~l5 WvW:Ŋ 6F{_ݾ/(øucwQr1s5 ϳĘajԧ[߯2GPgOy a`c1.ܛ8te"F6[VT+ ˊGd1wv4|3=:t/Ӳu47Mه4;aأUC^pϩݰff;y݂vYS\&?w"XЮvUV]8q-N+хo<Quiŏ#{-})V~2^i욋n^0WF]py_ϦjH}vÁgZ;o&v]O{2>~`h}͢aE/f={jl٩ϷcN[xV?z=x̰MM >qJi=^1v}Z҄ܭKș6Y?yfƢz#+{|Ջ.8΃YqL%hSd׾یlŸ Y~iqhhY).|jyiuvի yBR'ulڎZơoK_Ok ˨7Ήz o|hpDPՅY+n`s]xQxu˯9y2aKX?gK[D8u1<6M(|qWƤί ɕ[-n=r߭Y#>V!JK*2{H頿Q2Cؑz&y0m}hsѻfiuZUBƵ95=gͽ>i5,ZY63>4Q׶mQWmܻ!//cݑS>XWwZƔZZmqs ڵ|~YNٟ~ne}y 3ϴIBѧLTKt.˜c44yyhv]uwDw]]S&kF2/tcCn-߲nyh͍>yVtqiM⳹7V& XcĀM 18p 6][r% ۯa~X~ /-)l°&hZa+ZCJ!XImX~x.a:z~{/WwTɗѲh5IVd ~ XUnO H=HdA=$)[CBFZ+!FHXd ӉHO>-p!<ݠLN! ɹr D!9N( EфB&:L=/+I2xA6K.CaL7Gl6ji\.!>H2442H"<_rr&aD6|a$T#Q=OCHW|͐z`t 0 lo-lp1Ɵ%Ld} <>pq$hθAKpMjM5C4p4J i(xLeiED3S⃓~ M#r$C8fM(w~,FD1>iRY;W ;.a0|Bϊ >З .Qy _JGO.5@M DR@W_XBP|t&rؒxxpW(4A$Т hk(%r741\^ za\E]}Y0]B:Yh̀jI)~0 Oc%e`ЖЍf 1%F3hm6_B _jы1 a@KxR: ⅲRM !LaPsswL,qa1 6s]RC|^,2@3OPp:ˀߠ6Xr8"v&io5 Í~  2L0OD| "Nf]pi~ C̄GVt.Mh (+-_K:0 !_ZF@sH& (%8o{Mc"C $\#xQVD 846*}yQ,Nu;X@n IGȩNܪ4Aa94.+$20as_FɝpFlDC?@"j.5J Rȕ-K4GRDM+:lÉ ]3M [no{bvX(htt?*mdOÖ`YBKᔥ ?E@iӚtKh0J\|`)Ap$GbK"Dv&'=yAA &plm^ Ep_ ҌB6T34&#%=q^;QNM{Z{[Xx^ f&C[ƽh{+~sЭȽ ˎOsh _ÐMRYBz"e%B$8ɥI>^?nD{ K 1Xfce%)\g^pTHZRr)a7@%" 6<-Y$h/h|>%~ obC#?^JJ'8m%~oHF1\Q]ըQ]%ML@ /qrPv4P>8;{oE#\\>5EUR_CR%`~Wa p~z-@4 $U9h0 $qDEy$ݪ!2MHEoK`%ejCbEa,cP Ĕiʉr Bq@l¡ل10oj6dvlB&!Vmjϐ k);0C" q`s rɷ??7[%5c/sN7\x6oJg{jׇ)50IUc+S}[$fW`"X@Lx|{lBpےDDJ 8 cبwQ@fF1:[`cH$ y%(,j&C[^RԔz"/͙c2&*ʘ@qOq3 lQL"$bD1p b܊LSD R%<"ùGxou jj/ xv4==("`6ڒs{Tz7/i"~¸7r8Iv7H9k $ ! 4}BB諽w=`9 ?.ߨ30RGW%fFhМY'I]4@ɞA#az?@b<7/$J/͗_Ӿ% yyz]"ڗ0\B5+±HAh_p) !¡~I@;7mJNX}/*_Ҿ vTӾX Hh_+1fB}!ҔyqE!7k_ZX3v?s<*?iUi_c"1UhCb+_KA747<7p~>fk_ uSrlNh_nB m݄De%(s뱈GKy/1ъڗXofb`[>"m2۟l2UwKdl߀GJcj#Q %<">ԥlid/+>9PB#4{F,9Ohd.J52%fbb!YAՋI(piC 0\A^z4C(&T$Z>i4K0 0Rq\!qZ\#=vg2h=]x߄Pz(hmDmDmJS(_!fM&~>'lq52^rWo{UHIPڭ ~>3̼+V\9BO'8H(-|!q/–!A,Y ޵@GqVv!vk4F((ږcj*AhcE#` rEhb6<ȋ*mZ 5KBBC^@ l{gwfgvhRJp2g2I zR&K6?uS{e^YG~-d+-:KVy>JKd8%[\['| ~8R9I|z o:1Tc~Ch‡F&ՒdKk;:̲m}pllf>/{Tc> oL{NOWvޘfpC=wPNzM1\'7B7"nuDȤKzMw/|̓"&)2Oˊ )fY '%Em}<Pd>+K u}k)2OhȼU!MB")2 L9 >-+2%u帨ȠEEyLYy\܆YEfMxM`E䵒"qM^MJ1vXr 8 ̘b -pC6h5'l>x, ̑A7!AA;U᧎}jR|OJ^?S']-teZR 'ЇxNU]z̳>qDR=TA.h\lf6hȪGz0hgT=.:Mq#=wPD+K߹*)o+ӑ$KSJOy5XP~X3/?gHHHpm,ctο1zKtg&mn.(!~اxh W24hx, Ń5őZXM?>dNi>8\gL܆Y;(o\H\Wm/-忪-![l)GTS<}x}%cu鷋bCaɊ'^ABw)_YX''m'uΉ٘e`?xiAdP\elw z&ԱOyfyF̀$[-.g`9JK\0'Y<zAY cZ?;4GŧegHY᧎}jj<՘&O}Ӟ'aI>ۿ3˽gvWWue0:3m,Z12TfY S mS CRbfq欝̭ļuAd_ĤIJ̷5%FSb^j Mє}0X< ڎo^[LeAJ SfIyL܆Ycs:1X2Jȫ$%[ss j6ţxmTK1Z’ Iv|z]rn]u]9wzwټzwn|4Au'vzW۫勾\`7@ vȋ9oWNRϛ|&';;slnW'-ts#}vGċ`RcI&ޱS*E摄O\_W_?\O9׹En2d jؙI9k/|3;m/+p~,s7ru cyeBno<^JX귪s'ac_ϊO d|g'?*ݦG/g'n/?o%ww-'a^V *r{?U^f&h-9[~+as}|oaRh)֐[9+x*@K!5hy֜;mBR]bv<] 2dxH: +~nc$3 ö㳻}n۞ywrK/s6ŵ,"/ٲ8|5-AH>L"V{7ޚѥ>vԿv[~IvrVXNuF?b?!Bߏ~u5?^oz+D}FkNo \YуH~(Q@YWުS9rl4稜r-)׬āE'$E'9jR4D_++/~$%NJ`-eDj)-hImNiWe/VmKnStT=5)IS=ʓ~bgْT=_T9CMXqaq- Te#.UM^UJfIy !>UYi3ffgB;Y^'e`031gasJ*'7 ~)ӬY#7k,(b=nvO{˪nxK&XX=cEU }R_u?}'csF;I}u˚>֔ Yd֬qD]cd c(@RvI#])l|q|]Icuf61#C|8,ϓFFyS#'6ԃ㪞M٨R+wcҨy};m9c[6uB[!$QJ#H}E=!P8{ #5]a6clD ՛ƣfsFc ,)~Ц}*~чYf--xwRYlyImfmGcثPKrX1ŭZ$9 \[_>K1QEO4I g/dq%\ x@1ޛOū Mf.vM٫}>% %'1?Q!= ߉^vP]aҧd]P-mNyqURVMLg_%W7)W :w8}ZHq9^P"$PʰZӿZP)HV{5Q&`McM(Y-s]UoYY%}ܹ%+}%maEm YQ*5+dDj_)+C*3"_AUG*Ix/zKL pVsKKr7ofY V ^f~L[@ XsV<ބ![PťӰi/}B0Q2ZנߏRUOY;EoD 2Q2 "@\RKoGI'Y V7Coxoz{3gz;1gdyQh򣇪eqyUU٥rЪ1:uz/x>*#7̭3uƬϫC$f3l4 sɴҿ)YyFst٘bD]8UY.`7[^`yk<Ds3€/n: 6,3"{Q܃ebѡ]z)j,txvVꩿ,c h JE!rp&Qβ2Ifi- N A 'o , N~qW1:[R[Pho<+ƠҀEM^SHzZȞK`7RH[P;BPH;VMmPIC"β&jE>9M-NVor'"ˡzD3݂^$eueed_R2+6CU2al|fl_iԺH;-ȶBlbfH>V[~u4qT*  6RJ c)sAbY. _AԠRB`VRa% &{J|`X}P7$+5+~Ί[>T' p=rdߏ }?&EN @\؇>1'RB*j. @߄ N;gggK+ 2kvz%>QBg>QjC}e,QVU9rt匔T\ VbJ^CrD] goJHn1ގmɮZ.-.z#I--9 W-Sik1;>.CX D`⳷2-*Ue={f0aVJG!SSĒ))Zh57{U{$uoܗ{:u/C@cyxY7J=Ƴ.vCƌigNϕe%W{[N>ܖԨ8^XUflUXOo=xڀ8⸷ GmEd~M1,ѻaY`6XceY5f8fsqwn26ac"±z|/[3Z;⫦%8>)8[29W\=|W_5? vs#cBGzL8[Lؚl4}-֖6{v\-K1-<㰕~[ZBv?": s-r`fA3͖m͓6;5Gn4]Dh 6]F4kB4iAt9dCt |[Sc-g<jht4F$96ƫuy+93J/"r4Yf/K(dm4) 쀣 34l6 N  ;B > 6  A O7Q Ù醘 q8R?!@p~RF/`_E`OUŽ0`g]³Qz z^pv0]O2#4؁B2jpd㯣27.K쪛AlVblmu6Y:z)3z0h܁ ; L@Pf>`qb2㨏8dz1,- 3fZcuDk2suL L_o# Ɍ. V[]Z*1,.Z!Nc58qr`+@j8q ˌ@gMHfj PHѢZ=-3B8`B.`B2᫽2 ȌV^k-ZT&Zc&VepH]f5`U.U2 ˌ&*$351`lU2fjTV[iFf"v`&]MNUSUAGf<~`|&8CxLAU 8ML#UU֪)M*{tLPoL'3*?0ʠ8BqUdfc)ԔZ9%i-i\I+25y*Xjq|*20WDef 23m㪘1JGCF+%eRᗱQy"$cO AJ/ʌ2L8#0&(`)b8[c2Q42Ya3bb1F"b2alT[X*.Wv Y+cF,Q#-%?"Nc),͠7 v<.7Ύ;zCSW6 b )LZTZ(y9tdy%!|I1<܅ysw|y/#;a||_9O?yz ~lYD9;X9X"9o3ևu.96~NdFd=G͎1 X=/ph.) {]3vGmv mt)1a#136`B#>9e&<L`k$u;>`$12Eql xVf"`aG}2 &g2 =9h  ؈8YEFu "f;!5Tl˟*6`:LpӞm%p3n4 "El rꌩpL]^Nw &p0p@g 9Y XQXH=mz vqs pPFsvlXdc:t8p9N`"+7 vN8_'\T8t;'; 3IlpS.8N gJ:t̞'0юS&1q唐:Nx]/w60mUQ?UEQ,Ybbb1Ybbdq ɒ%&%$KH fKLHHQE):5/NQ nU( s:{zΎ&C)e**n9_C%N)3QY`xJbyzLQռ=݁ml1_lw(e{=rʖ{=a3hzB=є{F)f)9==a=K>;tuu")k XZE+瓼LRg}< d_)WF>oT'狓_*ʸ׏8o׊VRE ٸwbb޴wbb^OLŌsSw+*&XU\1l?wb}kguVW*~}Gm"ECR>bmǑd=d;Oi֏}H>k;Ǿd Wn8&1YN# uy)j'J؆lak8f%m&mkx6m[cA۬gMm^+Y[汞@-F?"MVlnk?Ҹ-b2i[ 9n9yK2nZdˈ9@g|0,8cF N+en"gSlа+sV9ce5' _Lu:eXNg kaU?Nb6,' cE4q0aX syYÌaF$6KēÄ~xܰhGG ˆa}xȰj8O596N金 `8z0}~(4EE! #H D!}.eb>Z܇O>q'˥RO>" R79W:tF?zNzO~I?ZCAwG}L* !}OJe0}QR(ĈVN~ pgqhhY$(6V4枋KZ򾋿KZ򎋿Xr17SO)N<,deEMir^jySb-U> Uj(?7&w[IQDz=S e74e44H;<<3y=3^>}#̓toŌWwo:ؚ3"hU2zH;23vti.nS0SCJ3(V .6(>.kd>Hڌ<&x$c@U~SCj@z{Kےwɬs0@mDeH| 4kmOR_$]YVR2֓͏2h^PqCE54nQ*D"mW3O&mm(J_LP%|?`ljik뽴m5Ϡ4M*asp͊Se<[Qyjw- wRj`xӺ4>)O~yʷaX~ޙOE9ox3+j?Ŗyk]Nl7e2~yn3lv="Z7V(g94ugsi)G=Qz+`z4MIk i;-}%M?d`&Mh_LOe/isiL#'22ާҎ"ݓvy2n,X` ,X` ,X` oƢ"(r!` ,X` ,X` ,X` ,X߷EQDx˅ ,X` ,X` ,X` ,X`NQD-2 ,X` ,X` ,X` ,X` gHp l,p56DȸZVOenT~~ӾsR3\F=,2ŧ̛?Hڎq&ujhA0@񋬛Ãt Im e afЂwXUqؔ>jF38"2^ب5q4mѴ96|.yhJ#sClٮ@xm^5ҧ47tռAsJ-3|ٖog" 3~{VG3ZݤjN3`όU43VfLKw'ݣyQ=8AsG;cgѼ6Hs~{jw~WZ43ߠuy5@H@:'Xl]k"'_R+ ZǿW>=xܒ\Yt=+'*uU_ɷ|ugV];;lGsrUּrU[9_Һ=Nx{%>m΃1ٟB+\+xW3/#7m38Gq90lėoNdq2݀yWE8b/~&P/MMlbݿT_v]KᰜTXFl2W. v`u_3?imj=U$C^p3x}¥ywM 07ܝģ[+!4l,޲Ҹ+>Ĵ]u歡hk]LݮM7XYŭ [)t$GQ4G24QYiaxk CXȱ"9?&ws 5S'ƖF郓%&JN:>n4Vm%\MbG,UۇlImgvp7\C>ޯYmƆl>l극۪Z˃UWIٝo t fc Mǐwƙ4z ZI[|sGo [Ik܉&o+i{;آyJ~ 3)Ww^᮸]=t7W ϻ+IξVZP";m _m\試U4Эܗ^@ g80~/zwbȊ;YCCЂ<XP,wXP*7 )K!Cp! Y" Yi6R4w1důG_O!봞BVanH Y*rCP{tS*/^G'X/*t~nH~ u8ɃVq ޲ A S!nH!KVrܐ9/e,66 UhY: (l{;P*Χ .] Nb'tqnh: lAǹf =^b/waxmD!!,H5D5HFNN$ CD)RR< k{:!uj(u_I6ʭUHԨzlFURۡTYiUd4FV5Z:Hrz'T/^(;w!&17:c|ǠHGQ?L܀HHԦg"Q ܀D >T2 h) l%Y5H _yڍŞ<)Ca ?6JKj>t4ʷQix\Lѧ@ixwާSwc/i!DB2d2xI*LN'<%%"A*$^)$M_EI9^W8M4%┨L58]c4Ypv(c>禦oN]r>M˂NZt$F6 '3 т#T x䁜 ƂǓ\7CGrI-˥p.-ܸre%hե/>ǴkAFa ;0V5[ fefGak>_Ođ.Rkb(|^.R}oJlbo8I.%>jfJ#_~<$U߭pJ[ })t8zN9tpT*/ҬB8HfP;#L_B[s$b-˨D(g4(Ӵ 9UvI*#t~˟2~8?iY!,4rg@ w~R@)8}el9Gw*ڕ8F;T0Aw [(qCiRo,o Լ΂ j&Hx-0QKq ]K X-r:_eɡE+l;(n7hQ"2e3g#naR_d\q bEqnH" [l.f UgQD ϡ jS< )fS f"Z,- z lk\ []\wK W,`Bx~WƆfz_3W(^gxU_ëg?=;;-Sj]$dElNnx/0y^66wmY[a7M[t]?d~NaOp}vyTod@'\{?q|#e0$k]X:'_Z.B*|~QCK-yO{n)ӹ@tr:=͇/ә@t~h?ONɻiX3*3-˓'e`qrxZ&)0?7MKTb*3\JO%'בS-O`*N ) SH2R &ҘS:$n"87O&'%#)D]C%#W&yʸbҤYYQ`2K0R7BʍcODzHDf"9NFM!kci#W'xȵg4~W2M_#ULS}i 2O1M_IS0G93efBP`Z(:+BG}lrFIC0ro4FL )`^bTHfz f}^XFVR}^R_/+W;\+fʐ 5M F A9#'4ȕ wr)X2K{z`jqo.(YMLo"Y:!\BN0oLػ䍔{7G0q Y|K!=2bjf-f|OHQ0ߓ5R9fz F*dOH`.xS6R6zFJKݞJ0֣0(7zJp'ԃ=i0hO ' +W%KP(J#8]R$!׫sLmXnHL,#?3؂vciH8R??O~[)줶/;E`)+N pa \8SbNh;83[`R󐔭YL6Iښ$n *[ mRik -CV1UHuLm )E7XI,Y1C7\##Wcda3arLo\ɐ9#b*& Q4F?ʁ7*Ѷ80dV UohۙXyKx[ ]?4LaKG ,̬or`ia`i_/))eӕ6têM]zP}W~HmN(+>LFڨ)KfTüjpn&of9 gx3L81#YG3"d>̇gd3BxgF5s&,fa9>3qX pF:LH{a2I$I1Sn2G\\  Nv Kyc_~:?w'xW<3KJ#Uc 6>.uǻ+ݩT`nw;.t^] Zw{;+Dk2߭u~{%M+4PRg 9?yP>dK@@:%8[@R`׿b}X@ l6J`OkXJ/]9$ݿ8dſJȒwU+#KB.Ld]~5] @G?ֵ[]Է WU:_}[w@oI|{+J~;D8r+ѕK]鮤/u庎}\u+"J]2rU$`iKtjvtHR'\\C}R;q1dwu"W|I_Ȝ/;L"|o3 Q㣐 SG:w:HZ~'ߡyԙ8jg3C.w;\qr<8ovmݎ$rҹALN c:T ռ ^-a.{+{Ed$㰣U$u;^HTG{¥lGܻu:ȹbǞ9Q&Վ1u 9L$ôA %Ի+FR^]2=#ބ7ӮI9qry3ޒw=VWn,Ik+`jmG].h4cD2%4kd YFFY#H h5 FX##u1k$I,F,@=F)̱FxH h5"̰F*FD{ O]ֈֈp MϜGN'{/o$l5WO1~rҳKIgEYɈpD8y7řV'ssf`qJ#dhdœyaV!ke~69ZY5o YȱF'+H<0f4/(g6`;[a&C#q]dĒ"mcR=ڟv1+LL~Ēa&?kq==;&YrL^/19e(q֯c%X7F-eW`2cɇϘ-A\¤`-tMM-qMש:Ɇ Àfs|Imp(u`Ä$SJB &} oGzrקhX fG ˦H?lXa%x kQaV1yɒ"wO ~̑awuK֠j8d%5j =pua  !KG>[ͫW9Mn}UX gd(v,ge.c^F:JuBnj')0w }p_lf\5Atd䧖>KlO 1EB?y"|nn&H2E4 *M6G.3G.h{hӖcƿF $)˱ _9ܲH<ͳ䓦U $f_̟PMlmᓫL#;KƆ-6 l#eE!6ϻ3 ۘ>h2۠{~6~@%a=d]TfLC`}m`]es?  ݠ*%/QOPmlampi3xm?_#/%)IK?0ezd{޿oQ,1?v6{O駸#^=6xwMMUvWl~7_/m}l6P>e{da}Xo;;;;;;oNqS=qvavavavavavavava3};8)N-uavavavavavavavav ;)NqS{swavavavavavavavav??i"giSygHSH;:EԈwuQuRF\gY+Ң`N(!ҦFw~}0M]9w>jj٪ o=_gr|XY=뗭8#==㛁wnkH+/s3͸2ؕyle&LZ^b+%y(\Pi<BOIEJ2Q]Wauh3k)HH\RU֠~&^аװZT6l~N7X3-9|tSyQT_T锄6E&I2M/j@>Nmxa a҇ J#hFi](;O1xL! iFCڒGzI i+GGTT=rq̱1gWن]mlP;61i(cE7)&o ݖ ^:}HH*AC'7F_>9Yl4tF-Cu0z.JkQsKqyڗgyޔH^GGgxXRۼEߞ%iw{}9){dI BUy}^a/YTKg@\xɇU4J()]+`U;;Jc]c$zqlHEcbXsW9,lVwPuAuPc+F2ux(ubpT>]JJA ګT>q+pE(([q&-F] u#FԻPBDD-`?SHt$<]z"h2JN 6Hbٔ!!WX/̦;(ݡxbT<‹^N$KSU:WaNR/);C`;rR_c)00>#ksڙ;*/"7v`(7ʐÆ=ŤkrٔugRG]+cWPUgasjA#ﻠnA\+a}\e#x]\]tP$PC,r#pԎ0/DJ1KA&~C;Qw@=::ZG{ڷyu?+x: Dҏ9O͇mJcӘ)NFJo9s)wKyzFyz'u tt,4,()ՏRi8U`伐O/y$2:׵Xu5}>j-"MuvvXGݪylf۝3Lo3wvU|XK-V97_1s^vB|9̜mv |6r~^w{}nBxZ!noe!nlByxDn`OB~ 0y`P(&19Xm&'<M&prLsTS&XH]ovb:NH+ gƎ)$'?8)YI=sL?lf#Z5fA3A |x ,Zeq ,wp6{mb`0f~{l%rmրڬog[>Y6ڬ>ƞeY6!QzIg%`M6k}0fY6+b.JٽN셽{/s-f"C <g=f0-Č^Y.n\<OMi&% wwkl8aX?M^se^%εsEH3t.oϒngu:Ez>۵:Xש.2.ZZVwj-۵ZVsVk"Ri55jFH[cH]VVՆն Vk5fpkչxȩ]dX1HH5ϙjEvu^:i"ft'iz%2&yNcꆹ)|}zBs/B. LE!C io {,zS9vCtᵱkz~qA?_‚=Ư@  RVC51 s҇iq" :9;+6+c 83 Ҝ}ҪQ:'8%C{^Pzq{ R݀t No C :"Otx3]/S?u{98;=Owq3K?xOx_xxdRr&A*jI E0ROx 2 fRoi00×3}3̀ Ң]h_$C}/Bz)!M 6@|A*~_xihc(r_E#1I*T`5í n%ԏ zò8#[`,{2D 68+L:|rB-Z "e K_dQ-ZB;$FD_UݨFf]PEBBAA-6 āҋy:S觚V ?wy礟< [݊nepS܆tIRLF@]X]zujL/֘ޢr Ie_xk:ט!rJ57 Cƚs,QOTPK`KTN[XQVٹonk C| O#_=~aُOh=0w=]c|gQגH쬾DYSA'拈/R0a.m>%a:n!XP|e} :ԯ\^4)TlJ $YCM|j0VeE($(HtA\=c ]?pI+8ZԺx72i"z79".śr"$!: [g@FlӪ! }7Wqyo9P044,7w1lz*hxkA~}ioR1l^>ޠ7pB U  ꧡak89f~UR1Զ1%ԍ/X*ɯ0me B #%"k!e2:+Qע~&;PEDDFF-ڼskO? Ӌ+?~Q:'O~rp#e(Q`+V=X~൤kIJu׭;XʨN)Wd ACC*U4j4fk54,K`OCkoX[e]wSsosXX5R;vQ3nlûc y _X[xʅ)Jz?jyl //$-B%qe rkLI?C m!xvz] 68 ,D3Do7-= 3=Ow-ZF5"ë8vUQa-V4 gUqU7>w/IMts^M7-x+AUQޢ:C{G7h1l(xݧxo7 fzVٍiG<14|7@kA.oPmmw15EXhq,c oҩ'+ֽPQ>oW:#'Bʿe{㷷luu"hQ:5&]u%QߍzݨQD=z5O _'F)~='-S7{R$r$?~϶`fY\k2R" t¯*~ VPeތ+4C#e4b*STcǗNE4%lfW֏`3r7¥wuhk0բU%6+O$m¦ ىףNY<_|ݿwL>>@h$'IgSۇ]l%4Ӯ(ۖvLx!{ءxXݡ{8 `ٚ~=4a5\l+ۮ]'&V)Yeëf 0U5W˽ -v-F`j`xEq+čWXdge_QCyqe;ĵ!s/+;sϽߡx<-`YM jd 7n*2qE#W^_TCjϽ%nnC\ !۪^C~ qh3[/6Y!n?Bx^ 0y=[6< 9`VFL:Lo6LivLnqYpד=49f6XkRI %ahШqCi+CP&Cr釔S qup!Sl[Ur.!w6Og=XT熨Wbc)N-;{9l5`,oϲ0˦k7g٬,ТᗖzmxH),-ih03gٌ>gMo|֯ ٌoY6iߪh3gٴaM%BqMOY6%ϲ8Y6sei5g쯘wJ+_~W̲ٽ,N%[ȝFkXP`Չqy/X}7Jb@Z5zW{x2=,nC\+Ve,qqãq#qp\N↋qE/'zpUBQ|LBwҵK'J9r]eC?t3MoH;!~~~~>M:>C:^&=ݮȺ]'㗺ۙۏ~PwvݤiuYeTMיws|7dpnEu3.Ys:EZZͭrM͹hr-RH޴`jI*| i4< 7n`3 VrDv67Ĺ \qwΦ\ rnzΟ2)Οȵ۔iat3 Ra`:ΛV}N_R)}K)M)Wq2DmL弒Ϝv#ws{295#ET .zM2Nɓ{]H ~6H%=rdʻOG@5ږpߟa ˁA*n*2=ȡg'_zٻ]_xHR^|Js6uY|[V[Iϛ}d68Fb892  BSpL{0LiO"Shh sj_2Ϫq >gY{R7q 7?Ny0}(`^u}cp|\WB^h>ۣg6"߅`"YE|C@uoEPmH|)5ɷ)8i8,oLˑߙS'!\ WwOaN̥ Wp+\]Q \jp+\ W̥ Wp+\]V\l+.ۊmŶsNQREDIۊmŶb[qm'ݴ/8V~%oV\l+.ۊmŶb[qV\l+.ۊmŶb[qV\l+.ۊ^8$gnڬO|.H=\{~I#Բg&2#t&%@e(LCe%,o,N#fRb_A0IfM@d<1`!81@L?@llH2_J rUvf-!ѷ\@#G륹|W*<L>EÕ%$MP#Px(cT F_323]$OA'9淜>91)(!򯗹,?S؇\C.!yEueqл "MDI"`{c;*:3 Cb$dSwSV̼nI}풗y ̛w~s=;cϹs9{=[,s9{=s_uɨ{X~Ϲs9{=ܓQ{osϹs9{=Y_Of˿{=s;2Sf3#`V3kXf9gv]A0$0IL(& c8±.#f~ndVX|;my3pt|&h`FuxV3UL%S͔2xTZKȁJ C1b0Bx^wU;ݕ8qFo5x@z2zs>z? RIcőq0,cX@[7c23g|3 f3c99*C[ a.3"f 3θ16c1ڟ#x1sJrT:JN)^`9\\Cubrm-MM uu5UUee%%.S73Z ިAߣKQ߾&23i_}P'wPr4Fƛtzh64265o9`uM_kA,[6襧xoB>ަYwprqusy89 켃L[}VAֶn>GFlokMaGʴY.WYMKWYA:.>~#G?q3eϜ>ucGtwqبw/]-5e\ƐUQ#׬0{ΈQc'L9{EK\-[3pK-;{ cG<-gڷ7i SV0R955 5]\=p׀Й5wk?ظeG;w߻{G۶l`% Κ1uʲI_C}]m 55i3R9ʪ6co`gW1'M1wUkup :+s 9thDe Θ:i؀a>Ύm3c8 1|aDSfس_O䥦g rrebAVz*/1>*ڽcu]3~6n`?zJ)aqt ڹ 1|u߹@PHDL$^jP,),),+)DTtDHЁ=C48]4nf}tՕNtP405|܌+nظ%pGҳEᒢ'Ommp…ֳOh()sy 1QAM֮X8cp/G+ZhՖSڼ>42)U5 ϶_rƭwݻ޽;oݸvR{Yj*+ Ǐ޿sSGu2LT29.M=c+q/ݰ3(". VT58zWݼs'O?O?O?䓏>y׮^x̉ƺJd"v~qV&Ca*$6lU'N+7n߻?}˯oo曯⏟ɳ'ܿ{ƕKΝni*/-*gn[=oH~} tUysJId1kja9g̥>YXNj9u򵛷{> 3@gO۷_xɖʂ?\{CZФ\C3QQc:0yCw=ʛZܸ־Ϟ}ɧ}9G_r+Η_u|)BG˟Aw3Twy3&3 Tlc;`,_#~[W/k/Eۺrx?+c]Ԥ4Of^ WRVҵ[w`woSɗ/y ӦƆzYVVSr3$D_|:(n n}b\a/5& 4T"|a8,EYU@,,,k:uJgxBp6 F_Ǿi_[SUYQ^VZRR\\TT\\RRZV^QYUSKpa3xCyœ]L22e89J*t5tTIu ʞɟ?JsgN47xTWUH\8//_RPXT\Z^YU]Sr̹$Dܹ-=n_ttS$5|3<ȍ|s2y#502`5v֪ δ_="gOajPKgϜj9 o@\QNP(99\1 js.vwid~~lѽ;n\m?]Wuh۪Ycjp)U+Umbip?o펠tI͙K7އ?}1=$vj#hIBX_r+R( tUeU64`! tt Hc\7oIomUJԇd 6 I*jiy!HXvM|BS-M u55e%EN$TVQYC̪ LaS$ģ={ +1tGx0饦 2T4o=j"SrkN]C+pÖ8Ô*aJu6(&zydC[egJ"r(jYy|8a)OpܼNeOK$l>9$J,͕,_NaZHlXv=\{Bׯj(ٶb(+#mU%0W%T\x4!ҵ0>%%'+ç/򞋿ϓ;SKN3Ο%۾rf#=-5 *#x(a@Qq,)[w! b ދAUYV\YQG4W/sYt\dDN~AqYeUm#z7Iݻ}L8Ur67,)EpFl72h9j֚I܁m]`7ΞD'T"ka2+Wyr"_EaA(UNwӤݫ;i?H_KKI0F}Ƃ#$YT~#Y'\LS<⩯r,IE, C sDyhDj{c ,=~B>tUO|G5ջByeMW=Ay@XV)Kߒq|鷌_r'!Bw=@]!߅ ~W4xHn^%"V i~n\ub&6_5Z"HC_x)pSl/>ʍ;#9\|Ti_g 1_63S}nw܂US+澄WC+I8CqHݥ{V‡c<1~  pDr$տs$-jw84LB*U?3D64nxqmxsDjx# 68Bmf0G&\ 3/k~$ς3'1̣&(LdIbS^] ddwI:Ȥ 3*Ӟșvj^]VgZˋ Q$d._ؓ@-  ax lM/xǪ#l,0cQvʵ2o1rǒD-îq/uEc9v#-C6Ju0 _)z3:f4 [XZYp{ƝwTp4pٴvE3:&n#.]\wu2B`]bBJ!)#pi/ȷDBn'$.')&|7>|ł-Kp6QJEś N]~t(O)wsQ'Ǐ"p ]YVjEnuT] Цœaw`ƶ>|x VPZS[O5!.+)"S/D\m:xG'4tnRÁ5|2VJ=Y/*+0[9 S!nAD|":βA'GWt dr,3j9c<+א񈱧/.(i%bM%4D*8a 4Bdk>s֍n߼v8#bY.i*VIUð+#!Ĕu%tGB&DF [Uշ=aWo_1} UN)1%0:iɖŠo˩G7V XL6K/sΈT,.Kkaj{߾|Bx˒IC-+wJ@Cޛadzaݹk7wI<E,i{>^$㧜oABk.,A7A$u7o+g acf[v ʻu 2$K+џxUH1# 1 )n.Mݽv3S$4z[ =sGjNvk\ vyNi(99c;H_PREĻϟ)N<ʙ[ 38}GN_5(33޸A$DB>Ѣ\@x*~hl3n%Fw™˦tJ9MZP\VaUө+PB%tnTq_`)\|]ۇWfڴhc^JO9RpXE'h/PܷC0R+tx( 8w C58\IjZaŶSM5Ir4(AGD/d9jHz'<_B(i 5heZ 9R#\:Xj8 zwdVYӹvE&xd^.C?RNir"2ڗBu@.BILcVIe]˙u5eEY=c%z˜+i9@K@oo?8/K!CB&TL; d9f952C\+^.(K䡪59XkNAQ.|T(͵zQֵp3o㑤@^(+, b dP)^/b W3hvL+RksIzSbnr8`2iw5gAyK(cKZ d#929B Mqrl\\|<,: ?NdAt}13D@j٫O?72{VB2*LM14AqF2CЛDp|4ȑcAa|DƊ`)Iqˎ7! _e1<:裵x{0eڏA^VQ^Z(J9Ѻ|]<`7`Dr&i|%˓6^'4!9U_Ҽ.8t9?:Wmu{#3 V=ebgb{ Pu8p՜Nn^Cx9;:8y*ptJNaj#fwww$Ao!Xj&YK S'QA,& \>}煂[`!9{9R) ,_euAcAfq~nk?oܬGBGƅ vOwll:ť$g#H;#j\ pH}5/($ES&@ ĂL0پfCk燭;nCeN ۆSɅh/Wg ƹ9;x{ه2s&e.؄",7`\z΄!6}5R(?-MX3,rieI~.4Ugfc^'LAYph_/OJ>|==|GO[n!.(EIBCj6dY6>}n^~'kK +v}h)*PB (mj!uȭD0. 6wE>rp?_o!c/Z!viq>vFWL5ˇ=/wwf-;)% eiA^r[SQ$ڶl0Gs]7iYfG`yCXB W#D~j /^&f跪$7`T-vr8\c0hKPbneIkP" l=|t$EXLܱ=a@6"H^|2i +4D{1 R%qX#) S(/2ә ϙJ 7&b?xp{QبTPxHrN+,: C^C&]uh|j21w Y^6It{&*ajiKqQk兄DFBݲ78!>-1w&S3PT(nC$- I{VƽUqm`j3q#9HO6T!]cȉsVl{1Gj "A517.DFaf=њ:N^h-UyXՠ4Bppg͋W븲b(:Nv.m-Ύ;y񤡃ꢬUJأ@J%j! !4S&wQ1^q,@ MLe +뚚Ju?XLB{Mݫp,YOЌaPL%H?-Z"IxZ'SXQ\WJgK|,җhjon^C gxGˢcR5Wgٶ|Hw~Fz݄,l=fa~t^S/HOSTMS3!$N/$DG"5u1p;b;߈()}yz%AhU_UGUe TM)IG WBr1ie#xY(%Ֆ+9[]1ye }A)Kf@ɦd) Ks8OLLJ/p.%SO;2;1Jf^7b bY)RO)\>''b R-'Ѳn̼ZF~gv8:-" *E|ʒ$KM塃w|.p¥Qܸd^2p;_E֐)su8=Hʙo[C?Vvr|@#U tBKrW) K`lI2K ,Kf{kظK ]]u? ]\US]}xۊށ%ے$P@*r_CǗ$h}% yɔe/APX/ vK~z1.IU

]SՅR#S֨3"3)s)8$5$kɥ!#\2G(cmB%dFF*hVTKgX1gIuV}6C'**dQ:3ҩ"jE6gA16LX_.sNe(?W : -ĩ,_4s oy[rLXy *2G<T/&IةI* S lAZ܋ʑ.#N-* Ne\|,n<%yY9o9Xw}^&NEt t ybC 2g8}p{.N 9i'tCޛfU6> ~5jVM% .S,kJV>-K!w2R-.8~WT i].&pUv?a#l^gw˕ @7CsB32rr)yMsly,A88 _Gk<ٻ:or1qt{n㣂!$!#kx?ĞtqIENDB`HandBrake-0.10.2/COPYING0000664000175200017520000004325311577654541015120 0ustar handbrakehandbrake GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.HandBrake-0.10.2/pkg/0000775000175200017520000000000012535641635014632 5ustar handbrakehandbrakeHandBrake-0.10.2/pkg/darwin/0000775000175200017520000000000012535641635016116 5ustar handbrakehandbrakeHandBrake-0.10.2/pkg/darwin/module.defs0000664000175200017520000000130211701117432020225 0ustar handbrakehandbrakePKG.platname = MacOSX$(suffix $(GCC.minver)) PKG.cli.dmg = $(PKG.out/)$(HB.name)-$(HB.version)-$(PKG.platname)_CLI_$(BUILD.arch).dmg PKG.gui.dmg = $(PKG.out/)$(HB.name)-$(HB.version)-$(PKG.platname)_GUI_$(BUILD.arch).dmg PKG.cli.tmp.dmg = $(PKG.out/)$(HB.name)-$(HB.version)-$(PKG.platname)_CLI_$(BUILD.arch).tmp.dmg PKG.gui.tmp.dmg = $(PKG.out/)$(HB.name)-$(HB.version)-$(PKG.platname)_GUI_$(BUILD.arch).tmp.dmg STAGE.out.cli/ = $(STAGE.out/)cli/ STAGE.out.gui/ = $(STAGE.out/)gui/ PKG.appcast = $(PKG.out/)$(notdir $(HB.url.appcast)) ############################################################################### BUILD.out += $(PKG.cli.dmg) BUILD.out += $(PKG.gui.dmg) BUILD.out += $(PKG.appcast) HandBrake-0.10.2/pkg/darwin/module.rules0000664000175200017520000000360611701106301020440 0ustar handbrakehandbrakepkg.create:: $(PKG.appcast) $(PKG.cli.dmg) $(PKG.gui.dmg) $(PKG.appcast): | $(dir $(PKG.appcast)) $(PKG.appcast): $(PKG.gui.dmg) $(PKG.appcast): $(BUILD/)project/handbrake.m4 $(PKG.appcast): $(PKG.in/)appcast.xml.m4 $(M4.exe) -I$(BUILD/)project \ -D__APPCAST_dmg="$(notdir $(PKG.gui.dmg))" \ -D__APPCAST_dmg_size="$(shell stat -f '%z' $(PKG.gui.dmg))" \ $(PKG.in/)appcast.xml.m4 > $@ $(PKG.cli.dmg): | $(dir $(PKG.cli.dmg)) $(PKG.cli.dmg): | $(STAGE.out.cli/) hdiutil create -srcfolder $(STAGE.out.cli/) -format UDRO -mode 755 \ -volname $(basename $(notdir $@)) \ -ov $(PKG.cli.tmp.dmg) hdiutil convert -format UDBZ -o $@ $(PKG.cli.tmp.dmg) $(RM.exe) $(PKG.cli.tmp.dmg) $(PKG.gui.dmg): | $(dir $(PKG.gui.dmg)) $(PKG.gui.dmg): | $(STAGE.out.gui/) ifeq (1-darwin,$(FEATURE.xcode)-$(BUILD.system)) hdiutil create -srcfolder $(STAGE.out.gui/) -format UDRO -mode 755 \ -volname $(basename $(notdir $@)) \ -ov $(PKG.gui.tmp.dmg) hdiutil convert -format UDBZ -o $@ $(PKG.gui.tmp.dmg) $(RM.exe) $(PKG.gui.tmp.dmg) else $(TOUCH.exe) $@ endif $(STAGE.out.cli/): $(MKDIR.exe) -p $@ ifeq (1-darwin,$(FEATURE.xcode)-$(BUILD.system)) $(CP.exe) $(XCODE.symroot)/HandBrakeCLI $(STAGE.out.cli/) $(call STAGE.doc,$(STAGE.out.cli/)) else $(CP.exe) $(TEST.exe) $(STAGE.cli/) $(call STAGE.doc,$(STAGE.out.cli/)) endif $(STAGE.out.gui/): $(MKDIR.exe) -p $@ ifeq (1-darwin,$(FEATURE.xcode)-$(BUILD.system)) $(CP.exe) -R $(XCODE.symroot)/HandBrake.app $(STAGE.out.gui/) $(LIPO.exe) -thin $(BUILD.arch) \ $(SRC/)macosx/Growl.framework/Versions/A/Growl \ -output $(STAGE.out.gui/)HandBrake.app/Contents/Frameworks/Growl.framework/Versions/A/Growl $(LIPO.exe) -thin $(BUILD.arch) \ $(SRC/)macosx/Sparkle.framework/Versions/A/Sparkle \ -output $(STAGE.out.gui/)HandBrake.app/Contents/Frameworks/Sparkle.framework/Versions/A/Sparkle $(call STAGE.doc,$(STAGE.out.gui/)) endif HandBrake-0.10.2/pkg/linux/0000775000175200017520000000000012535641635015771 5ustar handbrakehandbrakeHandBrake-0.10.2/pkg/linux/debian/0000775000175200017520000000000012535641635017213 5ustar handbrakehandbrakeHandBrake-0.10.2/pkg/linux/debian/control.utopic0000664000175200017520000000335212422261036022107 0ustar handbrakehandbrakeSource: handbrake Section: graphics Priority: optional Maintainer: John Stebbins Build-Depends: debhelper (>= 6), autotools-dev, libtool, libgudev-1.0-dev, intltool, autoconf, yasm (>= 1.2.0), cmake, libbz2-dev, zlib1g-dev, libgtk-3-dev, libwebkitgtk-3.0-dev, libnotify-dev, libgstreamer1.0-dev, libgstreamer-plugins-base1.0-dev, wget, python (>= 2.6), libappindicator3-dev, libfribidi-dev (>= 0.19.0), libxml2-dev, libogg-dev, libtheora-dev, libvorbis-dev, libsamplerate0-dev, libfreetype6-dev, libfontconfig1-dev, libass-dev, libmp3lame-dev, libx264-dev Standards-Version: 3.8.4 Homepage: http://www.handbrake.fr/ Package: handbrake-cli Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends} Conflicts: handbrake Description: versatile DVD ripper and video transcoder - command line HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the command-line variant, HandBrakeCLI Package: handbrake-gtk Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends}, gstreamer1.0-libav, gstreamer1.0-plugins-base, gstreamer1.0-plugins-good, gstreamer1.0-plugins-bad, gstreamer1.0-plugins-ugly, gstreamer1.0-pulseaudio Conflicts: handbrake Description: versatile DVD ripper and video transcoder - GTK GUI HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the graphical variant, ghb. HandBrake-0.10.2/pkg/linux/debian/rules.natty0000775000175200017520000000435512077536453021442 0ustar handbrakehandbrake#!/usr/bin/make -f # -*- makefile -*- # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) ifneq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE)) CROSS= --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE) else CROSS= --build $(DEB_BUILD_GNU_TYPE) endif PKGDESTDIR = ../ CONFIGURE = ./configure BUILDDIR = build FORCEVERSION = $(BUILDDIR)/GNUmakefile: $(CONFIGURE) dh_testdir unset CFLAGS; unset CPPLAGS; unset CXXLAGS; $(CONFIGURE) --enable-local-yasm --disable-gtk-update-checks --prefix=/usr build: $(BUILDDIR)/GNUmakefile dh_testdir #We must build both the main project and the GTK project unset CFLAGS ; unset CPPLAGS ; unset CXXLAGS; $(MAKE) -C $(BUILDDIR) clean: dh_testdir dh_testroot dh_clean install: build dh_testdir dh_testroot dh_installdirs $(MAKE) -C $(BUILDDIR) DESTDIR=$(CURDIR)/debian/tmp install # Build architecture-independent files here. binary-indep: build install dh_testdir dh_testroot dh_installchangelogs -i dh_installdocs -i dh_install --sourcedir=debian/tmp -i dh_icons -i dh_link -i dh_compress -i dh_fixperms -i dh_installdeb -i dh_gencontrol -i $(FORCEVERSION) dh_md5sums -i dh_builddeb --destdir=$(PKGDESTDIR) -i # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_install --sourcedir=debian/tmp -a dh_installchangelogs -a dh_installdocs -a dh_installexamples -a dh_installman -a dh_link -a dh_icons -a dh_strip -a dh_compress -a dh_fixperms -a dh_installdeb -a dh_shlibdeps -a dh_gencontrol -a $(FORCEVERSION) dh_md5sums -a dh_builddeb -a --destdir=$(PKGDESTDIR) -- -Zbzip2 binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install HandBrake-0.10.2/pkg/linux/debian/rules.lucid0000664000175200017520000000435611651547200021366 0ustar handbrakehandbrake#!/usr/bin/make -f # -*- makefile -*- # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) ifneq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE)) CROSS= --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE) else CROSS= --build $(DEB_BUILD_GNU_TYPE) endif PKGDESTDIR = ../ CONFIGURE = ./configure BUILDDIR = build FORCEVERSION = $(BUILDDIR)/GNUmakefile: $(CONFIGURE) dh_testdir unset CFLAGS; unset CPPLAGS; unset CXXLAGS; $(CONFIGURE) --enable-local-yasm --disable-gtk-update-checks --prefix=/usr build: $(BUILDDIR)/GNUmakefile dh_testdir #We must build both the main project and the GTK project unset CFLAGS ; unset CPPLAGS ; unset CXXLAGS; $(MAKE) -C $(BUILDDIR) clean: dh_testdir dh_testroot dh_clean install: build dh_testdir dh_testroot dh_installdirs $(MAKE) -C $(BUILDDIR) DESTDIR=$(CURDIR)/debian/tmp install # Build architecture-independent files here. binary-indep: build install dh_testdir dh_testroot dh_installchangelogs -i dh_installdocs -i dh_install --sourcedir=debian/tmp -i dh_icons -i dh_link -i dh_compress -i dh_fixperms -i dh_installdeb -i dh_gencontrol -i $(FORCEVERSION) dh_md5sums -i dh_builddeb --destdir=$(PKGDESTDIR) -i # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_install --sourcedir=debian/tmp -a dh_installchangelogs -a dh_installdocs -a dh_installexamples -a dh_installman -a dh_link -a dh_icons -a dh_strip -a dh_compress -a dh_fixperms -a dh_installdeb -a dh_shlibdeps -a dh_gencontrol -a $(FORCEVERSION) dh_md5sums -a dh_builddeb -a --destdir=$(PKGDESTDIR) -- -Zbzip2 binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install HandBrake-0.10.2/pkg/linux/debian/changelog0000664000175200017520000000727611216757404021076 0ustar handbrakehandbrakehandbrake (svn2573) jaunty; urgency=low * CORE - Allows sources with no audio to be encoded - TrueHD and DTS-HD demuxing - Transport stream improvements - Better reading of audio source parameters - Support for 8-bit audio sources - Fixed 5.1 Vorbis channel maps - Better DTS handling - Better VC1 handling - Better frame synchronization validation - Better identification of valid tracks in streams - Better handle of New Zealand FreeView broadcasts - Avoids a crash when a user tries to encode the same audio multiple times with file inputs, a big problem with the Universal and AppleTV presets - Preserves metadata when encoding MP4 sources - Avoids libavcodec threading issue by better locking - Fixes aspect ratio rounding bug - ffmpeg libraries update, libmp4v2 update, libx264 update - Other assorted improvements * MAC - Live preview encoding - Picture settings split off to its own window instead of a sheet, with a button in the toolbar - Variable number of preview images, default is now 30 not 10 - Logging level preference - On launch preference for whether or not to show Open Source window - Autocrop re-enabled for AppleTV Legacy preset - Support dropped for user presets generated in 0.9.2 or earlier - Correct dates for single-serving activity logs - Fixes issue where changing format reset video codec selection - Other assorted interface enhancements and bug fixes * WIN - XML-based queue system - Removed RAM limitation - Better memory alignment in Cygwin - Other assorted interface enhancements and bug fixes * LIN - Live preview encoding - Picture settings split off to its own window with a button in the toolbar - Variable number of preview images, default is now 30 not 10 - Interface improvements - Logging level preference - Other assorted interface enhancements and bug fixes * CLI - Added a --previews option to control how many preview images are scanned and whether or not they're written to disk (now defaults to not storing them) -- John Stebbins Sun, 21 Dec 2008 9:51:07 -0800 handbrake (0.9.3+repack1-0ubuntu0~8.04jdong4) hardy; urgency=low * Incorporate fixes suggested by John Stebbins: - Don't have the buildsys override HB's optimized CFLAGS - Do an official build (not a snapshot) - dh_icons/dh_desktop to correctly install menu icon -- John Dong Wed, 17 Dec 2008 21:15:07 -0500 handbrake (0.9.3+repack1-0ubuntu0~8.04jdong3) hardy; urgency=low * Switch to bzip2 compression -- John Dong Mon, 01 Dec 2008 14:19:06 -0500 handbrake (0.9.3+repack1-0ubuntu0~8.04jdong2) hardy; urgency=low * Split the GUI from the CLI: - handbrake-gtk: GUI frontend - handbrake-cli: CLI frontend - handbrake-common: README files and documentations common to both. -- John Dong Mon, 01 Dec 2008 12:39:22 -0500 handbrake (0.9.3+repack1-0ubuntu0~8.10jdong1) intrepid; urgency=low * Repacked orig.tar.gz: - Bundled contribs/ libraries so they don't get fetched as a part of the build process. - Documented this process in README.Debian * TODO: - Document debian/copyright for contrib libraries - Split handbrake-cli and handbrake-gtk -- John Dong Sat, 29 Nov 2008 00:31:50 -0500 handbrake (0.9.3-0ubuntu0~8.10jdong2) jaunty; urgency=low * UNRELEASED: - Work-in-progress documenting debian/copyright -- John Dong Sat, 29 Nov 2008 00:26:56 -0500 handbrake (0.9.3-0ubuntu0~8.10jdong1) jaunty; urgency=low * Initial attempt at packaging -- John Dong Fri, 28 Nov 2008 14:17:16 -0500 HandBrake-0.10.2/pkg/linux/debian/rules.quantal0000775000175200017520000000441612247175136021742 0ustar handbrakehandbrake#!/usr/bin/make -f # -*- makefile -*- # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) ifneq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE)) CROSS= --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE) else CROSS= --build $(DEB_BUILD_GNU_TYPE) endif PKGDESTDIR = ../ CONFIGURE = ./configure BUILDDIR = build FORCEVERSION = $(BUILDDIR)/GNUmakefile: $(CONFIGURE) dh_testdir unset CFLAGS; unset CPPLAGS; unset CXXLAGS; $(CONFIGURE) --enable-local-yasm --disable-gtk-update-checks --disable-libmkv --disable-mp4v2 --prefix=/usr build: $(BUILDDIR)/GNUmakefile dh_testdir #We must build both the main project and the GTK project unset CFLAGS ; unset CPPLAGS ; unset CXXLAGS; $(MAKE) -C $(BUILDDIR) clean: dh_testdir dh_testroot dh_clean install: build dh_testdir dh_testroot dh_installdirs $(MAKE) -C $(BUILDDIR) DESTDIR=$(CURDIR)/debian/tmp install # Build architecture-independent files here. binary-indep: build install dh_testdir dh_testroot dh_installchangelogs -i dh_installdocs -i dh_install --sourcedir=debian/tmp -i dh_icons -i dh_link -i dh_compress -i dh_fixperms -i dh_installdeb -i dh_gencontrol -i $(FORCEVERSION) dh_md5sums -i dh_builddeb --destdir=$(PKGDESTDIR) -i # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_install --sourcedir=debian/tmp -a dh_installchangelogs -a dh_installdocs -a dh_installexamples -a dh_installman -a dh_link -a dh_icons -a dh_strip -a dh_compress -a dh_fixperms -a dh_installdeb -a dh_shlibdeps -a dh_gencontrol -a $(FORCEVERSION) dh_md5sums -a dh_builddeb -a --destdir=$(PKGDESTDIR) -- -Zbzip2 binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install HandBrake-0.10.2/pkg/linux/debian/control.quantal0000664000175200017520000000330612271564163022261 0ustar handbrakehandbrakeSource: handbrake Section: graphics Priority: optional Maintainer: John Stebbins Build-Depends: debhelper (>= 6), autotools-dev, libtool, libgudev-1.0-dev, intltool, autoconf, yasm (>= 1.1.0), libbz2-dev, zlib1g-dev, libgtk-3-dev, libwebkitgtk-3.0-dev, libnotify-dev, libgstreamer1.0-dev, libgstreamer-plugins-base1.0-dev, wget, python (>= 2.6), libappindicator3-dev, libfribidi-dev (>= 0.19.0), libxml2-dev, libogg-dev, libtheora-dev, libvorbis-dev, libsamplerate0-dev, libfreetype6-dev, libfontconfig1-dev, libass-dev Standards-Version: 3.8.4 Homepage: http://www.handbrake.fr/ Package: handbrake-cli Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends} Conflicts: handbrake Description: versatile DVD ripper and video transcoder - command line HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the command-line variant, HandBrakeCLI Package: handbrake-gtk Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends}, gstreamer1.0-libav, gstreamer1.0-plugins-base, gstreamer1.0-plugins-good, gstreamer1.0-plugins-bad, gstreamer1.0-plugins-ugly, gstreamer1.0-pulseaudio Conflicts: handbrake Description: versatile DVD ripper and video transcoder - GTK GUI HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the graphical variant, ghb. HandBrake-0.10.2/pkg/linux/debian/control.raring0000664000175200017520000000330612271564163022076 0ustar handbrakehandbrakeSource: handbrake Section: graphics Priority: optional Maintainer: John Stebbins Build-Depends: debhelper (>= 6), autotools-dev, libtool, libgudev-1.0-dev, intltool, autoconf, yasm (>= 1.1.0), libbz2-dev, zlib1g-dev, libgtk-3-dev, libwebkitgtk-3.0-dev, libnotify-dev, libgstreamer1.0-dev, libgstreamer-plugins-base1.0-dev, wget, python (>= 2.6), libappindicator3-dev, libfribidi-dev (>= 0.19.0), libxml2-dev, libogg-dev, libtheora-dev, libvorbis-dev, libsamplerate0-dev, libfreetype6-dev, libfontconfig1-dev, libass-dev Standards-Version: 3.8.4 Homepage: http://www.handbrake.fr/ Package: handbrake-cli Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends} Conflicts: handbrake Description: versatile DVD ripper and video transcoder - command line HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the command-line variant, HandBrakeCLI Package: handbrake-gtk Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends}, gstreamer1.0-libav, gstreamer1.0-plugins-base, gstreamer1.0-plugins-good, gstreamer1.0-plugins-bad, gstreamer1.0-plugins-ugly, gstreamer1.0-pulseaudio Conflicts: handbrake Description: versatile DVD ripper and video transcoder - GTK GUI HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the graphical variant, ghb. HandBrake-0.10.2/pkg/linux/debian/control.precise0000664000175200017520000000311712271564163022246 0ustar handbrakehandbrakeSource: handbrake Section: graphics Priority: optional Maintainer: John Stebbins Build-Depends: debhelper (>= 6), autotools-dev, libtool, libgudev-1.0-dev, intltool, autoconf, yasm (>= 1.1.0), libbz2-dev, zlib1g-dev, libgtk-3-dev, libwebkitgtk-3.0-dev, libnotify-dev, libgstreamer0.10-dev, libgstreamer-plugins-base0.10-dev, wget, python (>= 2.6), libappindicator-dev, libfribidi-dev (>= 0.19.0), libxml2-dev, libogg-dev, libtheora-dev, libvorbis-dev, libsamplerate0-dev, libfreetype6-dev, libfontconfig1-dev, libass-dev Standards-Version: 3.8.4 Homepage: http://www.handbrake.fr/ Package: handbrake-cli Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends} Conflicts: handbrake Description: versatile DVD ripper and video transcoder - command line HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the command-line variant, HandBrakeCLI Package: handbrake-gtk Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends}, libwebkitgtk-3.0-0, libnotify4 Conflicts: handbrake Description: versatile DVD ripper and video transcoder - GTK GUI HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the graphical variant, ghb. HandBrake-0.10.2/pkg/linux/debian/handbrake-cli.install0000664000175200017520000000002511216757404023261 0ustar handbrakehandbrakeusr/bin/HandBrakeCLI HandBrake-0.10.2/pkg/linux/debian/README.Debian0000664000175200017520000000353411216757404021256 0ustar handbrakehandbrakehandbrake for Debian -------------------- HandBrake bundles its own copies of ffmpeg and related media libraries. This is an upstream decision that the Ubuntu maintainers will respect. This is done by running contrib/getcontrib.sh which wgets each library from HandBrake's website. Upstream has asked us to do this because they have modified their libraries to address the finickiness of the platforms that they support, along with prerelease patches that add support for advanced HandBrake functionality such as surround-sound downmixing. HandBrake then statically links against these libraries, and they are not installed to the system so it doesn't interfere with other parts of the system. Different or older versions of these packages are included in the Ubuntu distribution already, and have passed our guidelines for Multiverse inclusion. === Detailed Breakdown of Bundled Libraries and Reasons === a52dec - 0.7.4 patch to allow downmix to dolby prologic ii faad2 2.6.1 patch to configure.ac so it will build with libtool 2.2.x ffmpeg svn 15462 patch for building on beos patch that adds aac-latm codec patch fixes memory leak provoked by h264 streams with lots of errors patch for cygwin patch for solaris libdca svn 81 newer than released version libdvdread 0.9.7 patch for os x, changes path to dvdcss patch for cygwin, configure fixes faac patch for cygwin configure lame version 3.98 libmp4v2 svn 45 project was stagnant. using a fork that has picked up development libmkv 0.6.3 mpeg2dec 0.5.1 libogg 1.1.3 libsamplerate 0.1.4 libvorbis aotuv fork b5 libtheora 1.0 libx264 git 1028 patch for cygwin configure patch for solaris build scripts patch to allow forcing an IDR frame xvidcore 1.1.3 patch for os x configure patch for cygwin configure patch configure to recognize nasm 2.0 -- John Dong Fri, 28 Nov 2008 14:17:16 -0500 HandBrake-0.10.2/pkg/linux/debian/handbrake-cli.dirs0000664000175200017520000000001111216757404022547 0ustar handbrakehandbrake/usr/bin HandBrake-0.10.2/pkg/linux/debian/control.trusty0000664000175200017520000000335212374664323022172 0ustar handbrakehandbrakeSource: handbrake Section: graphics Priority: optional Maintainer: John Stebbins Build-Depends: debhelper (>= 6), autotools-dev, libtool, libgudev-1.0-dev, intltool, autoconf, yasm (>= 1.2.0), cmake, libbz2-dev, zlib1g-dev, libgtk-3-dev, libwebkitgtk-3.0-dev, libnotify-dev, libgstreamer1.0-dev, libgstreamer-plugins-base1.0-dev, wget, python (>= 2.6), libappindicator3-dev, libfribidi-dev (>= 0.19.0), libxml2-dev, libogg-dev, libtheora-dev, libvorbis-dev, libsamplerate0-dev, libfreetype6-dev, libfontconfig1-dev, libass-dev, libmp3lame-dev, libx264-dev Standards-Version: 3.8.4 Homepage: http://www.handbrake.fr/ Package: handbrake-cli Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends} Conflicts: handbrake Description: versatile DVD ripper and video transcoder - command line HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the command-line variant, HandBrakeCLI Package: handbrake-gtk Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends}, gstreamer1.0-libav, gstreamer1.0-plugins-base, gstreamer1.0-plugins-good, gstreamer1.0-plugins-bad, gstreamer1.0-plugins-ugly, gstreamer1.0-pulseaudio Conflicts: handbrake Description: versatile DVD ripper and video transcoder - GTK GUI HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the graphical variant, ghb. HandBrake-0.10.2/pkg/linux/debian/compat0000664000175200017520000000000211255735623020410 0ustar handbrakehandbrake6 HandBrake-0.10.2/pkg/linux/debian/control.natty0000664000175200017520000000311112033660703021736 0ustar handbrakehandbrakeSource: handbrake Section: graphics Priority: optional Maintainer: John Stebbins Build-Depends: debhelper (>= 6), autotools-dev, libtool, libgudev-1.0-dev, intltool, autoconf, yasm (>= 1.1.0), libbz2-dev, zlib1g-dev, libwebkitgtk-dev, libnotify-dev, libgstreamer0.10-dev, libgstreamer-plugins-base0.10-dev, wget, subversion, python (>= 2.6), libappindicator-dev, libfribidi-dev (>= 0.19.0), libxml2-dev, libogg-dev, libtheora-dev, libvorbis-dev, libsamplerate0-dev, libfreetype6-dev, libfontconfig1-dev, libass-dev Standards-Version: 3.8.4 Homepage: http://www.handbrake.fr/ Package: handbrake-cli Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends} Conflicts: handbrake Description: versatile DVD ripper and video transcoder - command line HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the command-line variant, HandBrakeCLI Package: handbrake-gtk Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends}, libwebkitgtk-1.0-0, libnotify1 Conflicts: handbrake Description: versatile DVD ripper and video transcoder - GTK GUI HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the graphical variant, ghb. HandBrake-0.10.2/pkg/linux/debian/docs0000664000175200017520000000003411223500674020051 0ustar handbrakehandbrakeNEWS AUTHORS CREDITS THANKS HandBrake-0.10.2/pkg/linux/debian/control.lucid0000664000175200017520000000264011651416147021713 0ustar handbrakehandbrakeSource: handbrake Section: graphics Priority: optional Maintainer: John Stebbins Build-Depends: debhelper (>= 6), autotools-dev, libtool, libgudev-1.0-dev, intltool, autoconf, libbz2-dev, zlib1g-dev, libwebkit-dev, libnotify-dev, libgstreamer0.10-dev, libgstreamer-plugins-base0.10-dev, wget, subversion, python (>= 2.6), libfribidi-dev (>= 0.19.0) Standards-Version: 3.8.4 Homepage: http://www.handbrake.fr/ Package: handbrake-cli Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends} Conflicts: handbrake Description: versatile DVD ripper and video transcoder - command line HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the command-line variant, HandBrakeCLI Package: handbrake-gtk Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends}, libwebkit-1.0-2, libnotify1 Conflicts: handbrake Description: versatile DVD ripper and video transcoder - GTK GUI HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the graphical variant, ghb. HandBrake-0.10.2/pkg/linux/debian/control0000664000175200017520000000266011650570327020616 0ustar handbrakehandbrakeSource: handbrake Section: graphics Priority: optional Maintainer: John Stebbins Build-Depends: debhelper (>= 6), autotools-dev, libtool, libgudev-1.0-dev, intltool, autoconf, yasm (>= 0.7.0), libbz2-dev, zlib1g-dev, libwebkit-dev, libnotify-dev, libgstreamer0.10-dev, libgstreamer-plugins-base0.10-dev, wget, subversion, python (>= 2.6), libfribidi-dev (>=0.19.0) Standards-Version: 3.8.4 Homepage: http://www.handbrake.fr/ Package: handbrake-cli Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends} Conflicts: handbrake Description: versatile DVD ripper and video transcoder - command line HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the command-line variant, HandBrakeCLI Package: handbrake-gtk Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends}, libwebkit-1.0-2, libnotify1 Conflicts: handbrake Description: versatile DVD ripper and video transcoder - GTK GUI HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the graphical variant, ghb. HandBrake-0.10.2/pkg/linux/debian/rules.precise0000775000175200017520000000441612247175136021727 0ustar handbrakehandbrake#!/usr/bin/make -f # -*- makefile -*- # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) ifneq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE)) CROSS= --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE) else CROSS= --build $(DEB_BUILD_GNU_TYPE) endif PKGDESTDIR = ../ CONFIGURE = ./configure BUILDDIR = build FORCEVERSION = $(BUILDDIR)/GNUmakefile: $(CONFIGURE) dh_testdir unset CFLAGS; unset CPPLAGS; unset CXXLAGS; $(CONFIGURE) --enable-local-yasm --disable-gtk-update-checks --disable-libmkv --disable-mp4v2 --prefix=/usr build: $(BUILDDIR)/GNUmakefile dh_testdir #We must build both the main project and the GTK project unset CFLAGS ; unset CPPLAGS ; unset CXXLAGS; $(MAKE) -C $(BUILDDIR) clean: dh_testdir dh_testroot dh_clean install: build dh_testdir dh_testroot dh_installdirs $(MAKE) -C $(BUILDDIR) DESTDIR=$(CURDIR)/debian/tmp install # Build architecture-independent files here. binary-indep: build install dh_testdir dh_testroot dh_installchangelogs -i dh_installdocs -i dh_install --sourcedir=debian/tmp -i dh_icons -i dh_link -i dh_compress -i dh_fixperms -i dh_installdeb -i dh_gencontrol -i $(FORCEVERSION) dh_md5sums -i dh_builddeb --destdir=$(PKGDESTDIR) -i # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_install --sourcedir=debian/tmp -a dh_installchangelogs -a dh_installdocs -a dh_installexamples -a dh_installman -a dh_link -a dh_icons -a dh_strip -a dh_compress -a dh_fixperms -a dh_installdeb -a dh_shlibdeps -a dh_gencontrol -a $(FORCEVERSION) dh_md5sums -a dh_builddeb -a --destdir=$(PKGDESTDIR) -- -Zbzip2 binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install HandBrake-0.10.2/pkg/linux/debian/handbrake-gtk.docs0000664000175200017520000000000711216757404022561 0ustar handbrakehandbrake#DOCS# HandBrake-0.10.2/pkg/linux/debian/control.karmic0000664000175200017520000000266111650570327022064 0ustar handbrakehandbrakeSource: handbrake Section: graphics Priority: optional Maintainer: John Stebbins Build-Depends: debhelper (>= 6), autotools-dev, libtool, libgudev-1.0-dev, intltool, autoconf, yasm (>= 0.7.0), libbz2-dev, zlib1g-dev, libwebkit-dev, libnotify-dev, libgstreamer0.10-dev, libgstreamer-plugins-base0.10-dev, wget, subversion, python (>= 2.6), libfribidi-dev (>= 0.19.0) Standards-Version: 3.8.4 Homepage: http://www.handbrake.fr/ Package: handbrake-cli Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends} Conflicts: handbrake Description: versatile DVD ripper and video transcoder - command line HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the command-line variant, HandBrakeCLI Package: handbrake-gtk Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends}, libwebkit-1.0-2, libnotify1 Conflicts: handbrake Description: versatile DVD ripper and video transcoder - GTK GUI HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the graphical variant, ghb. HandBrake-0.10.2/pkg/linux/debian/rules0000775000175200017520000000433111376541775020302 0ustar handbrakehandbrake#!/usr/bin/make -f # -*- makefile -*- # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) ifneq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE)) CROSS= --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE) else CROSS= --build $(DEB_BUILD_GNU_TYPE) endif PKGDESTDIR = ../ CONFIGURE = ./configure BUILDDIR = build FORCEVERSION = $(BUILDDIR)/GNUmakefile: $(CONFIGURE) dh_testdir unset CFLAGS; unset CPPLAGS; unset CXXLAGS; $(CONFIGURE) --disable-gtk-update-checks --prefix=/usr build: $(BUILDDIR)/GNUmakefile dh_testdir #We must build both the main project and the GTK project unset CFLAGS ; unset CPPLAGS ; unset CXXLAGS; $(MAKE) -C $(BUILDDIR) clean: dh_testdir dh_testroot dh_clean install: build dh_testdir dh_testroot dh_installdirs $(MAKE) -C $(BUILDDIR) DESTDIR=$(CURDIR)/debian/tmp install # Build architecture-independent files here. binary-indep: build install dh_testdir dh_testroot dh_installchangelogs -i dh_installdocs -i dh_install --sourcedir=debian/tmp -i dh_icons -i dh_link -i dh_compress -i dh_fixperms -i dh_installdeb -i dh_gencontrol -i $(FORCEVERSION) dh_md5sums -i dh_builddeb --destdir=$(PKGDESTDIR) -i # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_install --sourcedir=debian/tmp -a dh_installchangelogs -a dh_installdocs -a dh_installexamples -a dh_installman -a dh_link -a dh_icons -a dh_strip -a dh_compress -a dh_fixperms -a dh_installdeb -a dh_shlibdeps -a dh_gencontrol -a $(FORCEVERSION) dh_md5sums -a dh_builddeb -a --destdir=$(PKGDESTDIR) -- -Zbzip2 binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install HandBrake-0.10.2/pkg/linux/debian/control.maverick0000664000175200017520000000305612033660703022410 0ustar handbrakehandbrakeSource: handbrake Section: graphics Priority: optional Maintainer: John Stebbins Build-Depends: debhelper (>= 6), autotools-dev, libtool, libgudev-1.0-dev, intltool, autoconf, yasm (>= 1.1.0), libbz2-dev, zlib1g-dev, libwebkit-dev, libnotify-dev, libgstreamer0.10-dev, libgstreamer-plugins-base0.10-dev, wget, subversion, python (>= 2.6), libfribidi-dev (>= 0.19.0), libxml2-dev, libogg-dev, libtheora-dev, libvorbis-dev, libsamplerate0-dev, libfreetype6-dev, libfontconfig1-dev, libass-dev Standards-Version: 3.8.4 Homepage: http://www.handbrake.fr/ Package: handbrake-cli Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends} Conflicts: handbrake Description: versatile DVD ripper and video transcoder - command line HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the command-line variant, HandBrakeCLI Package: handbrake-gtk Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends}, libwebkit-1.0-2, libnotify1 Conflicts: handbrake Description: versatile DVD ripper and video transcoder - GTK GUI HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the graphical variant, ghb. HandBrake-0.10.2/pkg/linux/debian/rules.utopic0000775000175200017520000000433112422261036021562 0ustar handbrakehandbrake#!/usr/bin/make -f # -*- makefile -*- # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) ifneq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE)) CROSS= --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE) else CROSS= --build $(DEB_BUILD_GNU_TYPE) endif PKGDESTDIR = ../ CONFIGURE = ./configure BUILDDIR = build FORCEVERSION = $(BUILDDIR)/GNUmakefile: $(CONFIGURE) dh_testdir unset CFLAGS; unset CPPLAGS; unset CXXLAGS; $(CONFIGURE) --disable-gtk-update-checks --prefix=/usr build: $(BUILDDIR)/GNUmakefile dh_testdir #We must build both the main project and the GTK project unset CFLAGS ; unset CPPLAGS ; unset CXXLAGS; $(MAKE) -C $(BUILDDIR) clean: dh_testdir dh_testroot dh_clean install: build dh_testdir dh_testroot dh_installdirs $(MAKE) -C $(BUILDDIR) DESTDIR=$(CURDIR)/debian/tmp install # Build architecture-independent files here. binary-indep: build install dh_testdir dh_testroot dh_installchangelogs -i dh_installdocs -i dh_install --sourcedir=debian/tmp -i dh_icons -i dh_link -i dh_compress -i dh_fixperms -i dh_installdeb -i dh_gencontrol -i $(FORCEVERSION) dh_md5sums -i dh_builddeb --destdir=$(PKGDESTDIR) -i # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_install --sourcedir=debian/tmp -a dh_installchangelogs -a dh_installdocs -a dh_installexamples -a dh_installman -a dh_link -a dh_icons -a dh_strip -a dh_compress -a dh_fixperms -a dh_installdeb -a dh_shlibdeps -a dh_gencontrol -a $(FORCEVERSION) dh_md5sums -a dh_builddeb -a --destdir=$(PKGDESTDIR) -- -Zbzip2 binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install HandBrake-0.10.2/pkg/linux/debian/copyright0000664000175200017520000010073711216757404021153 0ustar handbrakehandbrakeThis package was debianized by John Dong on Fri, 28 Nov 2008 14:17:16 -0500. It was downloaded from http://handbrake.fr/ Upstream Author(s): See AUTHORS file for details Eric Petit Laurent Aimar John Allen (?) (johnallen) Joe Crain (dynaflash) Damiano Galassi (ritsuka) Edward Groenendaal (eddyg) Rodney Hester (rhester) Andrew Kimpton (awk) Chris Lee Chris Long (chrislong) Brian Mario (brianmario) Maurj (maurj) Mirkwood (mirkwood) Nyx (Nyx) Philippe Rigaux (prigaux) Jonathon Rubin (jbrjake) Scott (sr55) Chris Thoman (huevos_rancheros) Mark Krenek (travistex) Van Jacobson (van) Copyright: Copyright (C) 2008 by the HandBrake team (See AUTHORS) License: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA The full text of this license is at `/usr/share/common-licenses/GPL'. --- The Debian packaging is (C) 2008, John Dong and is licensed under the GPL, see `/usr/share/common-licenses/GPL'. ==== Contrib Libraries ==== HandBrake also includes and statically links against several open source supporting libraries. The source code for these libraries may be found in the `contribs/' directory. The projects and authors are listed in the CREDITS file of the source. A copy is installed to `/usr/share/doc/handbrake-common/CREDITS' when HandBrake is installed. The source for each library is found in a .txt in the contribs directory. For example, `contribs/a52dec.tar.gz' is downloaded from the URL in `contribs/version_a52dec.txt'. --- a52dec --- License: Copyright (C) 2000-2002 Michel Lespinasse Copyright (C) 1999-2000 Aaron Holtzman This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL file. --- faad2 --- Upstream Authors: menno Alexander Kurpiers M. Bakker Ahead Software AG Nero AG Copyright: 2002-2005 M. Bakker 2003-2005 Ahead Software AG 2003-2005 Nero AG Other Authors: John Edwards Bill May Quinnware VideoLAN Copyright: 2002 John Edwards 2003 Bill May 1997-2002 Quinnware 2004 VideoLAN License Declaration: The GPL may be found in the file /usr/share/common-licenses/GPL. The upstream source has the follwing license declaration: --- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Any non-GPL usage of this software or parts of this software is strictly forbidden. The "appropriate copyright message" mentioned in section 2c of the GPLv2 must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com" --- The aacDECdrop module has the following license declaration: --- This program is distributed under the GNU General Public License, version 2. A copy of this license is included with this source. --- Some files (extra components and plugins) and the Debian packaging have the following license declaration: --- This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. --- --- ffmpeg --- Upstream Authors: Fabrice Bellard Alex Beregszaszi BERO Mario Brito Ronald Bultje Tim Ferguson Brian Foley Arpad Gereoffy Philip Gladstone Vladimir Gneushev Wolfgang Hesseler Falk Hueffner Zdenek Kabelac Robin Kay Todd Kirby Nick Kurshev Mike Melanson Michael Niedermayer François Revol Roman Shaposhnik Dieter Shirley Juan J. Sierralta Ewald Snel Leon van Stuivenberg Roberto Togni Lionel Ulmer Copyright (c) 2000-2004 Fabrice Bellard et al. The following files are licensed under the GNU GPL, as clarified below: * ffmpeg.c * libavcodec: + dtsdec.c + i386/idct_mmx.c + liba52/*.[ch] * libavformat: + x11grab.c + gxfenc.c * libpostproc: + postprocess_internal.h + postprocess_altivec_template.c + postprocess.h + postprocess_template.c + postprocess.c + mangle.h * libswscale: + swscale.c + swscale-example.c + yuv2rgb_template.c + swscale_altivec_template.c + yuv2rgb_altivec.c + swscale_template.c + rgb2rgb_template.c + rgb2rgb.c + cs_test.c + yuv2rgb_mlib.c + yuv2rgb.c | This library is free software; you can redistribute it and/or | modify it under the terms of the GNU General Public License as | published by the Free Software Foundation; either version 2 of | the License, or (at your option) any later version. | | This library is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | Lesser General Public License for more details. | | You should have received a copy of the GNU General Public License | along with this program; if not, write to the Free Software | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA The debian/libfaad directory contains header from the libfaad-dev package. | Upstream Authors: menno | Alexander Kurpiers | M. Bakker | Ahead Software AG | Nero AG | Copyright: 2002-2005 M. Bakker | 2003-2005 Ahead Software AG | 2003-2005 Nero AG | | Other Authors: John Edwards | Bill May | Quinnware | VideoLAN | Copyright: 2002 John Edwards | 2003 Bill May | 1997-2002 Quinnware | 2004 VideoLAN | | License Declaration: | | The GPL may be found in the file /usr/share/common-licenses/GPL. | | The upstream source has the follwing license declaration: | --- | This program is free software; you can redistribute it and/or modify it under | the terms of the GNU General Public License as published by the Free Software | Foundation; either version 2 of the License, or (at your option) any later | version. | | This program is distributed in the hope that it will be useful, but WITHOUT ANY | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A | PARTICULAR PURPOSE. See the GNU General Public License for more details. | | You should have received a copy of the GNU General Public License along with | this program; if not, write to the Free Software Foundation, Inc., 59 Temple | Place - Suite 330, Boston, MA 02111-1307, USA. The debian/liba52 directory contains a copy of the header file from the liba52-0.7.4-dev package. | License: | | Copyright (C) 2000-2002 Michel Lespinasse | Copyright (C) 1999-2000 Aaron Holtzman | | This program is free software; you can redistribute it and/or modify | it under the terms of the GNU General Public License as published by | the Free Software Foundation; either version 2 of the License, or | (at your option) any later version. | | This program is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General Public License for more details. | | You should have received a copy of the GNU General Public License | along with this program; if not, write to the Free Software | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA The rest of the code is licensed under the GNU LGPL: | This library is free software; you can redistribute it and/or | modify it under the terms of the GNU Lesser General Public License as | published by the Free Software Foundation; either version 2.1 of | the License, or (at your option) any later version. | | This library is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | Lesser General Public License for more details. | | You should have received a copy of the GNU General Public License | along with this program; if not, write to the Free Software | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA On Debian GNU/Linux systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL' and the text of the GNU Lesser General Public License is in `/usr/share/common-licenses/LGPL'. --- libdca --- Authors: Gildas Bazin implementation of the DTS Coherent Acoustics decoder and current maintainer of libdts. Other libdts contributors include: Sam Hocevar - build system improvements and debian package While the libdts part of the source code was mostly written from scratch, this project was started from the a52dec code base so the original credits for a52dec are preserved: Aaron Holtzman started the project and made the initial working implementation. Michel Lespinasse did major changes for speed and conformance and is the current maintainer. Other contributors include: Gildas Bazin - mingw32 port Billy Biggs - most of liba52.txt Jeroen Dobbelaere - fixed point version Eduard Hasenleithner - gcc 3.0 fixes H�kan Hjort - Solaris output, mlib code Charles M. Hannum - fixes Chris Hodges - made the library reentrant Michael Holzt - OSS output.c and misc errata Angelos Keromytis - OpenBSD fixes David I. Lehn - API cleanup suggestion Don Mahurin - stdin support for extract_a52 Jim Miller - IRIX output.c Takefumi Sayo - FreeBSD tweak Shoji Tokunaga - aif file output Copyright: License: You should have received a copy of the GNU General Public License with the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. --- libdvdread --- Author: Bjorn Englund Copyright: (C) 2000-2001 Bjorn Englund (C) 2000-2001 Hakan Hjort (C) 2000-2001 Billy Biggs (C) 2000-2001 Christian Wolff License: GPL-2+ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. . On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-2 file. --- faac --- Authors: (see AUTHORS in contrib/faac.tar.gz for more information) lenox (Tony Lenox) menno oxygene2000/oxygene (?) thebard (wmilas@rarcoa.com) prkoat (?) xfhobbes (?) flyingfox (?) eraser (?) knik (Krzysztof Nikiel) stux (Stuart Espey) ca5e (Janne Hyvärinen) danchr (Dan Christiansen) corrados (Volker Fischer) Copyright: 2001 M. Bakker You should have received a copy of the GNU General Public License with the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. --- LAME --- Authors: The following people contributed to the LAME development: Lauri Ahonen Sakari Ailus Tero Auvinen Michal Bacik Alex Ballegooy Wilfried Behne Bob Bell Simon Blandford Segher Boessenkool Florian Bome Lionel Bonnet Gabriel Bouvigne Rogério Theodoro de Brito Erik de Castro Lopo David Chandler Michael Cheng John Dahlstrom Goran Dokic James Droppo Sergey Dubov Dominique Duvivier John Edwards Alvaro Martinez Echevarria Joakim Enerstam Albert Faber Nils Faerber Sami Farin Mikhail M. Fedotov Joseph Fourier Jani Frilander Richard Gorton Casper Gripenberg Steffan Haeuser Jeremy Hall Paul Hartman John Hayward-Warburton Peder Hedlund Robert Hegemann Mathew Hendry Magnus Holmgren Vitaly Ivanov Ben "Jacobs" Tamito Kajiyama Ti Kan Mo Katz Iwasa Kazmi Ralf Kempkens Frank Klemm Aleksander Korzynski Joachim Kuebart Leonid Kulakov Dmitry Kutsanov Jarmo Laakkonen An van Lam Dennis Lambe Jr Juha Laukala Greg Lehey Felix von Leitner Rafael Luebbert Macik Lars Magne Ingebrigtsen Scott Manley Vladimir Marek Goran Markovic Sergey A. Maslyakov Chris Matrakidis Greg Maxwell Chris Miller Scott Miller Darin Morrison Tomasz Motylewski Kimmo Mustonen Dan Nelson Nyaochi Anton Oleynikov Mike Oliphant André Osterhues Johannes Overmann Gian-Carlo Pascutto Jan Peman Jan Rafaj Gertjan van Ratingen Miguel Revilla Rodriguez Shawn Riley Tim Ruddick Ingo Saitz Conrad Sanderson Sergey Sapelin William Schelter Justin Schoeman Anton Sergunov Naoki Shibata Sigbjørn Skjæret Nathan Slingerland Patrick De Smet Acy Stapp Mark Stephens Jonathan Stott Alexander Stumpf Stephane Tavenard Mark Taylor Mikhail Teterin Brad Threatt Takehiro Tominaga Warren Toomey Atro Tossavainen Roel Van Den Berghe Kyle VanderBeek Linus Walleij Martin Weghofer William Welch Gerhard Wesp Alfred Weyers Christopher Wise Ethan Yeo Chuck Zenkus Original ISO contributors: Bill Aspromonte Shaun Astarabadi R. Bittner Karlheinz Brandenburg W. Joseph Carter Jack Chang Mike Coleman Johnathan Devine Ernst Eberlein Dan Ellis Peter Farrett Jean-Georges Fritsch Vlad Fruchter Hendrik Fuchs Bernhard Grill Amit Gulati Munsi Haque Chuck Hsiao Toshiyuki Ishino Masahiro Iwadare Earl Jennings James Johnston Leon v.d. Kerkhof Don Lee Mike Li Yu-Tang Lin Soren Neilsen Simao F. Campos Neto Mark Paley Davis Pan Tan Ah Peng Kevin Peterson Juan Pineda Ernst F. Schroeder Peter Siebert Jens Spille John Stewart Sam Stewart Al Tabayoyon Kathy Wang Franz-Otto Witte Douglas Wong Copyright: 1999-2008 by LAME authors LAME is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. LAME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. On Debian systems, the complete text of the GNU Library General Public License can be found in `/usr/share/common-licenses/LGPL'. --- libmp4v2 (mpeg4ip) --- libmp4v2 is a subset of the mpeg4ip project. Upstream Authors: menno (menno@audiocoding.com) Alexander Kurpiers (a.kurpiers@nt.tu-darmstadt.de) The mpeg4ip subset in libmp4v2.tar.gz is original code by menno and Alexander Kurpiers. It is licensed under the terms of the Mozilla Public License (MPL) version 1.1. The complete text of the license is reproduced at the end of this section. MOZILLA PUBLIC LICENSE Version 1.1 --------------- 1. Definitions. 1.0.1. "Commercial Use" means distribution or otherwise making the Covered Code available to a third party. 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications. 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor. 1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data. 1.5. "Executable" means Covered Code in any form other than Source Code. 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A. 1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. 1.8. "License" means this document. 1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is: A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications. B. Any new file that contains any part of the Original Code or previous Modifications. 1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License. 1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor. 1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. 1.12. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. Source Code License. 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof). (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License. (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices. 2.2. Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination). (c) the licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first makes Commercial Use of the Covered Code. (d) Notwithstanding Section 2.2(b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor. 3. Distribution Obligations. 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5. 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to. --- libmkv --- Authors: Most of this code was taken from Mike Mastnev's matroska muxer included in x264. Glue provided by saintdev@gmail.com Copyright: Copyright (C) saintdev@gmail.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA --- mpeg2dec --- Upstream Authors: Aaron Holtzman Michel Lespinasse Bruno Barreyra - build fixes Gildas Bazin - mingw32 port Alexander W. Chin - progressive_seq fix Stephen Crowley - build fixes Didier Gautheron - bug fixes Ryan C. Gordon - SDL support Peter Gubanov - MMX IDCT scheduling Håkan Hjort - Solaris fixes, mlib code Nicolas Joly - assorted bug fixes Gerd Knorr - Xv support David I. Lehn - motion_comp mmx code Olie Lho - MMX yuv2rgb routine David S. Miller - sparc VIS optimizations Rick Niles - build fixes Real Ouellet - g200 fixes Bajusz Peter - motion comp fixes Franck Sicard - x11 fixes Brion Vibber - x11 fixes Martin Vogt - reentrancy fixes Fredrik Vraalsen - general hackage and stuff mpeg2dec is Copyright (C) 1999-2000 Aaron Holtzman Copyright (C) 2000-2003 Michel Lespinasse Copyright (C) 2000-2003 Silicon Integrated System Corp. Copyright (C) 2000-2003 Ryan C. Gordon Copyright (C) 2000-2003 Dominik Schnitzer Copyright (C) 2003 Regis Duchesne Copyright (C) 2003 David S. Miller Copyright (C) 2003 Peter Gubanov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA On Debian systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL'. --- libogg --- Upstream Author: Christopher Montgomery Copyright: Copyright (c) 2002-2004, Xiph.org Foundation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the Xiph.Org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. HandBrake-0.10.2/pkg/linux/debian/rules.trusty0000775000175200017520000000433112374666127021651 0ustar handbrakehandbrake#!/usr/bin/make -f # -*- makefile -*- # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) ifneq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE)) CROSS= --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE) else CROSS= --build $(DEB_BUILD_GNU_TYPE) endif PKGDESTDIR = ../ CONFIGURE = ./configure BUILDDIR = build FORCEVERSION = $(BUILDDIR)/GNUmakefile: $(CONFIGURE) dh_testdir unset CFLAGS; unset CPPLAGS; unset CXXLAGS; $(CONFIGURE) --disable-gtk-update-checks --prefix=/usr build: $(BUILDDIR)/GNUmakefile dh_testdir #We must build both the main project and the GTK project unset CFLAGS ; unset CPPLAGS ; unset CXXLAGS; $(MAKE) -C $(BUILDDIR) clean: dh_testdir dh_testroot dh_clean install: build dh_testdir dh_testroot dh_installdirs $(MAKE) -C $(BUILDDIR) DESTDIR=$(CURDIR)/debian/tmp install # Build architecture-independent files here. binary-indep: build install dh_testdir dh_testroot dh_installchangelogs -i dh_installdocs -i dh_install --sourcedir=debian/tmp -i dh_icons -i dh_link -i dh_compress -i dh_fixperms -i dh_installdeb -i dh_gencontrol -i $(FORCEVERSION) dh_md5sums -i dh_builddeb --destdir=$(PKGDESTDIR) -i # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_install --sourcedir=debian/tmp -a dh_installchangelogs -a dh_installdocs -a dh_installexamples -a dh_installman -a dh_link -a dh_icons -a dh_strip -a dh_compress -a dh_fixperms -a dh_installdeb -a dh_shlibdeps -a dh_gencontrol -a $(FORCEVERSION) dh_md5sums -a dh_builddeb -a --destdir=$(PKGDESTDIR) -- -Zbzip2 binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install HandBrake-0.10.2/pkg/linux/debian/handbrake-gtk.dirs0000664000175200017520000000006411217051215022561 0ustar handbrakehandbrake/usr/share/icons /usr/bin/ /usr/share/applications HandBrake-0.10.2/pkg/linux/debian/rules.oneiric0000775000175200017520000000435512077536453021733 0ustar handbrakehandbrake#!/usr/bin/make -f # -*- makefile -*- # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) ifneq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE)) CROSS= --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE) else CROSS= --build $(DEB_BUILD_GNU_TYPE) endif PKGDESTDIR = ../ CONFIGURE = ./configure BUILDDIR = build FORCEVERSION = $(BUILDDIR)/GNUmakefile: $(CONFIGURE) dh_testdir unset CFLAGS; unset CPPLAGS; unset CXXLAGS; $(CONFIGURE) --enable-local-yasm --disable-gtk-update-checks --prefix=/usr build: $(BUILDDIR)/GNUmakefile dh_testdir #We must build both the main project and the GTK project unset CFLAGS ; unset CPPLAGS ; unset CXXLAGS; $(MAKE) -C $(BUILDDIR) clean: dh_testdir dh_testroot dh_clean install: build dh_testdir dh_testroot dh_installdirs $(MAKE) -C $(BUILDDIR) DESTDIR=$(CURDIR)/debian/tmp install # Build architecture-independent files here. binary-indep: build install dh_testdir dh_testroot dh_installchangelogs -i dh_installdocs -i dh_install --sourcedir=debian/tmp -i dh_icons -i dh_link -i dh_compress -i dh_fixperms -i dh_installdeb -i dh_gencontrol -i $(FORCEVERSION) dh_md5sums -i dh_builddeb --destdir=$(PKGDESTDIR) -i # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_install --sourcedir=debian/tmp -a dh_installchangelogs -a dh_installdocs -a dh_installexamples -a dh_installman -a dh_link -a dh_icons -a dh_strip -a dh_compress -a dh_fixperms -a dh_installdeb -a dh_shlibdeps -a dh_gencontrol -a $(FORCEVERSION) dh_md5sums -a dh_builddeb -a --destdir=$(PKGDESTDIR) -- -Zbzip2 binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install HandBrake-0.10.2/pkg/linux/debian/rules.raring0000775000175200017520000000441612247175136021557 0ustar handbrakehandbrake#!/usr/bin/make -f # -*- makefile -*- # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) ifneq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE)) CROSS= --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE) else CROSS= --build $(DEB_BUILD_GNU_TYPE) endif PKGDESTDIR = ../ CONFIGURE = ./configure BUILDDIR = build FORCEVERSION = $(BUILDDIR)/GNUmakefile: $(CONFIGURE) dh_testdir unset CFLAGS; unset CPPLAGS; unset CXXLAGS; $(CONFIGURE) --enable-local-yasm --disable-gtk-update-checks --disable-libmkv --disable-mp4v2 --prefix=/usr build: $(BUILDDIR)/GNUmakefile dh_testdir #We must build both the main project and the GTK project unset CFLAGS ; unset CPPLAGS ; unset CXXLAGS; $(MAKE) -C $(BUILDDIR) clean: dh_testdir dh_testroot dh_clean install: build dh_testdir dh_testroot dh_installdirs $(MAKE) -C $(BUILDDIR) DESTDIR=$(CURDIR)/debian/tmp install # Build architecture-independent files here. binary-indep: build install dh_testdir dh_testroot dh_installchangelogs -i dh_installdocs -i dh_install --sourcedir=debian/tmp -i dh_icons -i dh_link -i dh_compress -i dh_fixperms -i dh_installdeb -i dh_gencontrol -i $(FORCEVERSION) dh_md5sums -i dh_builddeb --destdir=$(PKGDESTDIR) -i # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_install --sourcedir=debian/tmp -a dh_installchangelogs -a dh_installdocs -a dh_installexamples -a dh_installman -a dh_link -a dh_icons -a dh_strip -a dh_compress -a dh_fixperms -a dh_installdeb -a dh_shlibdeps -a dh_gencontrol -a $(FORCEVERSION) dh_md5sums -a dh_builddeb -a --destdir=$(PKGDESTDIR) -- -Zbzip2 binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install HandBrake-0.10.2/pkg/linux/debian/rules.saucy0000775000175200017520000000441612247175136021421 0ustar handbrakehandbrake#!/usr/bin/make -f # -*- makefile -*- # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) ifneq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE)) CROSS= --build $(DEB_BUILD_GNU_TYPE) --host $(DEB_HOST_GNU_TYPE) else CROSS= --build $(DEB_BUILD_GNU_TYPE) endif PKGDESTDIR = ../ CONFIGURE = ./configure BUILDDIR = build FORCEVERSION = $(BUILDDIR)/GNUmakefile: $(CONFIGURE) dh_testdir unset CFLAGS; unset CPPLAGS; unset CXXLAGS; $(CONFIGURE) --enable-local-yasm --disable-gtk-update-checks --disable-libmkv --disable-mp4v2 --prefix=/usr build: $(BUILDDIR)/GNUmakefile dh_testdir #We must build both the main project and the GTK project unset CFLAGS ; unset CPPLAGS ; unset CXXLAGS; $(MAKE) -C $(BUILDDIR) clean: dh_testdir dh_testroot dh_clean install: build dh_testdir dh_testroot dh_installdirs $(MAKE) -C $(BUILDDIR) DESTDIR=$(CURDIR)/debian/tmp install # Build architecture-independent files here. binary-indep: build install dh_testdir dh_testroot dh_installchangelogs -i dh_installdocs -i dh_install --sourcedir=debian/tmp -i dh_icons -i dh_link -i dh_compress -i dh_fixperms -i dh_installdeb -i dh_gencontrol -i $(FORCEVERSION) dh_md5sums -i dh_builddeb --destdir=$(PKGDESTDIR) -i # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_install --sourcedir=debian/tmp -a dh_installchangelogs -a dh_installdocs -a dh_installexamples -a dh_installman -a dh_link -a dh_icons -a dh_strip -a dh_compress -a dh_fixperms -a dh_installdeb -a dh_shlibdeps -a dh_gencontrol -a $(FORCEVERSION) dh_md5sums -a dh_builddeb -a --destdir=$(PKGDESTDIR) -- -Zbzip2 binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install HandBrake-0.10.2/pkg/linux/debian/control.oneiric0000664000175200017520000000311112033660703022227 0ustar handbrakehandbrakeSource: handbrake Section: graphics Priority: optional Maintainer: John Stebbins Build-Depends: debhelper (>= 6), autotools-dev, libtool, libgudev-1.0-dev, intltool, autoconf, yasm (>= 1.1.0), libbz2-dev, zlib1g-dev, libwebkitgtk-dev, libnotify-dev, libgstreamer0.10-dev, libgstreamer-plugins-base0.10-dev, wget, subversion, python (>= 2.6), libappindicator-dev, libfribidi-dev (>= 0.19.0), libxml2-dev, libogg-dev, libtheora-dev, libvorbis-dev, libsamplerate0-dev, libfreetype6-dev, libfontconfig1-dev, libass-dev Standards-Version: 3.8.4 Homepage: http://www.handbrake.fr/ Package: handbrake-cli Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends} Conflicts: handbrake Description: versatile DVD ripper and video transcoder - command line HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the command-line variant, HandBrakeCLI Package: handbrake-gtk Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends}, libwebkitgtk-1.0-0, libnotify4 Conflicts: handbrake Description: versatile DVD ripper and video transcoder - GTK GUI HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the graphical variant, ghb. HandBrake-0.10.2/pkg/linux/debian/control.saucy0000664000175200017520000000330612271564163021740 0ustar handbrakehandbrakeSource: handbrake Section: graphics Priority: optional Maintainer: John Stebbins Build-Depends: debhelper (>= 6), autotools-dev, libtool, libgudev-1.0-dev, intltool, autoconf, yasm (>= 1.1.0), libbz2-dev, zlib1g-dev, libgtk-3-dev, libwebkitgtk-3.0-dev, libnotify-dev, libgstreamer1.0-dev, libgstreamer-plugins-base1.0-dev, wget, python (>= 2.6), libappindicator3-dev, libfribidi-dev (>= 0.19.0), libxml2-dev, libogg-dev, libtheora-dev, libvorbis-dev, libsamplerate0-dev, libfreetype6-dev, libfontconfig1-dev, libass-dev Standards-Version: 3.8.4 Homepage: http://www.handbrake.fr/ Package: handbrake-cli Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends} Conflicts: handbrake Description: versatile DVD ripper and video transcoder - command line HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the command-line variant, HandBrakeCLI Package: handbrake-gtk Architecture: i386 amd64 Depends: ${shlibs:Depends}, ${misc:Depends}, gstreamer1.0-libav, gstreamer1.0-plugins-base, gstreamer1.0-plugins-good, gstreamer1.0-plugins-bad, gstreamer1.0-plugins-ugly, gstreamer1.0-pulseaudio Conflicts: handbrake Description: versatile DVD ripper and video transcoder - GTK GUI HandBrake is a versatile, easy-to-use tool for converting DVDs and other videos into H.264, MPEG-4, or OGG formatted media. It's particularly useful for making videos that are compatible with portable video devices such as the Apple iPod/iPhone. This package contains the graphical variant, ghb. HandBrake-0.10.2/pkg/linux/debian/handbrake-gtk.install0000664000175200017520000000012112202224615023261 0ustar handbrakehandbrakeusr/bin/ghb usr/share/applications/* usr/share/icons/* usr/share/locale/* #DOCS# HandBrake-0.10.2/pkg/linux/module.defs0000664000175200017520000000366012121127354020112 0ustar handbrakehandbrakePKG.deb.machine = `dpkg --print-architecture` PKG.rpm.machine = `rpm -E "%_target_cpu"` PKG.release = 1 PKG.rpm.dist = `rpm -E "%dist"` ############################################################################### PKG.cli.tar = $(PKG.out/)$(HB.name)-$(HB.version)-$(BUILD.machine)_CLI.tar.gz STAGE.out.cli/ = $(STAGE.out/)cli/ ############################################################################### PKG.rpm.src.tar.bz2 = $(STAGE.out.src/)rpm/$(PKG.basename).tar.bz2 STAGE.out.rpm.src/ = $(STAGE.out.src/)rpm/ PKG.debian = $(PKG.in/)linux/debian PKG.cli.deb = $(PKG.out/)$(HB.name)-$(HB.version)-Ubuntu_CLI_$(BUILD.machine).deb PKG.gui.deb = $(PKG.out/)$(HB.name)-$(HB.version)-Ubuntu_GUI_$(BUILD.machine).deb PKG.deb.basename = handbrake-$(HB.version) PKG.src.deb.tar = handbrake_$(HB.version).tar.gz PKG.src.deb.stamp = $(STAGE.out.src/).debsrc.stamp PKG.src.deb = $(PKG.out/)handbrake_$(HB.version).deb PKG.cli.tmp.deb = $(PKG.out/)handbrake-cli_$(HB.version)_$(PKG.deb.machine).deb PKG.gui.tmp.deb = $(PKG.out/)handbrake-gtk_$(HB.version)_$(PKG.deb.machine).deb PKG.native.rpm.stamp = $(RPM.out/).rpm.stamp PKG.gui.native.rpm = $(RPM.out/)RPMS/$(PKG.rpm.machine)/$(HB.name)-gui-$(HB.version)-$(PKG.release)$(PKG.rpm.dist).$(PKG.rpm.machine).rpm PKG.cli.native.rpm = $(RPM.out/)RPMS/$(PKG.rpm.machine)/$(HB.name)-cli-$(HB.version)-$(PKG.release)$(PKG.rpm.dist).$(PKG.rpm.machine).rpm PKG.cli.rpm = $(PKG.out/)$(HB.name)-$(HB.version)-Fedora_CLI_$(BUILD.machine).rpm PKG.gui.rpm = $(PKG.out/)$(HB.name)-$(HB.version)-Fedora_GUI_$(BUILD.machine).rpm RPM.out = $(STAGE.out/)rpm RPM.out/ = $(STAGE.out/)rpm/ RPMROOT.out = $(PWD)/$(STAGE.out/)rpmroot RPMROOT.out/ = $(PWD)/$(STAGE.out/)rpmroot/ ############################################################################### BUILD.out += $(PKG.src.tar) BUILD.out += $(PKG.cli.deb) BUILD.out += $(PKG.gui.deb) BUILD.out += $(PKG.cli.rpm) BUILD.out += $(PKG.gui.rpm) BUILD.out += $(PKG.cli.tar) HandBrake-0.10.2/pkg/linux/module.rules0000664000175200017520000001324012422501225020313 0ustar handbrakehandbrakepkg.create.deb:: $(PKG.gui.deb) $(PKG.cli.deb) pkg.create.rpm:: $(PKG.gui.rpm) pkg.create.src.deb:: $(PKG.src.deb.stamp) pkg.create.tar:: pkg.create $(PKG.cli.tar) # # CLI Tar Package # $(PKG.cli.tar): | $(dir $(PKG.cli.tar)) $(PKG.cli.tar): | $(STAGE.out.cli/) cd $(STAGE.out.cli/) && $(TAR.exe) cjf $(call fn.ABSOLUTE,$(PKG.cli.tar)) . $(STAGE.out.cli/): -$(MKDIR.exe) -p $@ $(CP.exe) HandBrakeCLI $(STAGE.out.cli/) $(call STAGE.doc,$(STAGE.out.cli/)) # # RPM binary package rules # $(PKG.rpm.src.tar.bz2): $(STAGE.out.rpm.src/) $(TAR.exe) cjf $@ -C $(STAGE.out.rpm.src/) $(PKG.basename) $(STAGE.out.rpm.src/): GNUmakefile # -$(MKDIR.exe) -p $(STAGE.out.rpm.src/)$(PKG.basename) # tar c --exclude build ../* | tar x -C $(STAGE.out.rpm.src/)$(PKG.basename) -$(RM.exe) -rf $(STAGE.out.src/) make contrib.fetch svn export -r$(HB.repo.rev) $(HB.repo.url) $@/$(PKG.basename) svn info $(HB.repo.url)@$(HB.repo.rev) > $@/$(PKG.basename)/version.txt -$(MKDIR.exe) $(STAGE.out.rpm.src/)$(PKG.basename)/download $(CP.exe) $(SRC/)download/*.tar.* $(STAGE.out.rpm.src/)$(PKG.basename)/download $(PKG.native.rpm.stamp): $(PKG.rpm.src.tar.bz2) -$(MKDIR.exe) -p $(RPM.out/)SOURCES -$(MKDIR.exe) -p $(RPM.out/)SPECS -$(MKDIR.exe) -p $(RPM.out/)BUILD -$(MKDIR.exe) -p $(RPM.out/)RPMS -$(MKDIR.exe) -p $(RPM.out/)SRPMS -$(MKDIR.exe) -p $(RPMROOT.out/) echo "%define name $(HB.name)" > $(RPM.out/)SPECS/ghb.spec echo "%define release 1" >> $(RPM.out/)SPECS/ghb.spec echo "%define version $(HB.version)" >> $(RPM.out/)SPECS/ghb.spec cat $(GTK.src/)ghb.spec >> $(RPM.out/)SPECS/ghb.spec $(CP.exe) $(PKG.rpm.src.tar.bz2) $(RPM.out/)SOURCES rpmbuild --define="_topdir $(PWD)/$(RPM.out)" --buildroot $(RPMROOT.out) -ba $(RPM.out/)SPECS/ghb.spec $(TOUCH.exe) $(PKG.native.rpm.stamp) $(PKG.gui.rpm): | $(dir $(PKG.gui.rpm)) $(PKG.gui.rpm): $(PKG.native.rpm.stamp) $(CP.exe) $(PKG.gui.native.rpm) $(PKG.gui.rpm) $(CP.exe) $(PKG.cli.native.rpm) $(PKG.cli.rpm) # # Debian binary package rules # $(PKG.gui.tmp.deb): | $(dir $(PKG.gui.tmp.deb)) $(PKG.gui.tmp.deb): GNUmakefile -$(RM.exe) $(SRC/)debian ln -s $(PWD)/$(PKG.debian) $(SRC/)debian fakeroot $(MAKE) -C $(SRC/) -f debian/rules clean $(MAKE) BUILDDIR=$(PWD)/$(BUILD) CONFIGURE=configure -C $(SRC/) -f debian/rules build echo $(PKG.out/) fakeroot $(MAKE) FORCEVERSION="-- -v$(HB.version)" BUILDDIR=$(PWD)/$(BUILD) CONFIGURE=configure PKGDESTDIR=$(PWD)/$(PKG.out/) -C $(SRC/) -f debian/rules binary $(PKG.gui.deb): | $(dir $(PKG.gui.deb)) $(PKG.gui.deb): $(PKG.gui.tmp.deb) $(MV.exe) $(PKG.gui.tmp.deb) $(PKG.gui.deb) $(PKG.cli.deb): | $(dir $(PKG.cli.deb)) $(PKG.cli.deb): $(PKG.gui.tmp.deb) $(MV.exe) $(PKG.cli.tmp.deb) $(PKG.cli.deb) # # Debian source package rules # pkg.push.src.deb:: $(PKG.src.deb.stamp) (cd $(STAGE.out.src/)utopic && dput handbrake-snapshots handbrake_$(HB.version)ppa1~utopic1_source.changes ) (cd $(STAGE.out.src/)trusty && dput handbrake-snapshots handbrake_$(HB.version)ppa1~trusty1_source.changes ) $(PKG.src.deb.stamp): GNUmakefile -$(RM.exe) -rf $(STAGE.out.src/) make contrib.fetch svn export -r$(HB.repo.rev) $(HB.repo.url) $(STAGE.out.src/)utopic/$(PKG.deb.basename) svn info $(HB.repo.url)@$(HB.repo.rev) > $(STAGE.out.src/)utopic/$(PKG.deb.basename)/version.txt $(CP.exe) -a $(SRC/)download $(STAGE.out.src/)utopic/$(PKG.deb.basename) cp -a $(PWD)/$(PKG.debian) $(STAGE.out.src/)utopic/$(PKG.deb.basename) $(CP.exe) $(STAGE.out.src/)utopic/$(PKG.deb.basename)/debian/control.utopic $(STAGE.out.src/)utopic/$(PKG.deb.basename)/debian/control $(CP.exe) $(STAGE.out.src/)utopic/$(PKG.deb.basename)/debian/rules.utopic $(STAGE.out.src/)utopic/$(PKG.deb.basename)/debian/rules echo "handbrake ($(HB.version)ppa1~utopic1) utopic; urgency=low" > $(STAGE.out.src/)utopic/$(PKG.deb.basename)/debian/changelog echo " * Snapshot" >> $(STAGE.out.src/)utopic/$(PKG.deb.basename)/debian/changelog echo " - See timeline at http://trac.handbrake.fr/timeline" >> $(STAGE.out.src/)utopic/$(PKG.deb.basename)/debian/changelog echo "" >> $(STAGE.out.src/)utopic/$(PKG.deb.basename)/debian/changelog echo " -- John Stebbins Sun, 11 Apr 2010 9:51:07 -0800" >> $(STAGE.out.src/)utopic/$(PKG.deb.basename)/debian/changelog $(TAR.exe) czf $(STAGE.out.src/)utopic/$(PKG.src.deb.tar) -C $(STAGE.out.src/)utopic $(PKG.deb.basename) (cd $(STAGE.out.src/)utopic/$(PKG.deb.basename) && debuild -S -kjstebbins.hb) svn export -r$(HB.repo.rev) $(HB.repo.url) $(STAGE.out.src/)trusty/$(PKG.deb.basename) svn info $(HB.repo.url)@$(HB.repo.rev) > $(STAGE.out.src/)trusty/$(PKG.deb.basename)/version.txt $(CP.exe) -a $(SRC/)download $(STAGE.out.src/)trusty/$(PKG.deb.basename) cp -a $(PWD)/$(PKG.debian) $(STAGE.out.src/)trusty/$(PKG.deb.basename) $(CP.exe) $(STAGE.out.src/)trusty/$(PKG.deb.basename)/debian/control.trusty $(STAGE.out.src/)trusty/$(PKG.deb.basename)/debian/control $(CP.exe) $(STAGE.out.src/)trusty/$(PKG.deb.basename)/debian/rules.trusty $(STAGE.out.src/)trusty/$(PKG.deb.basename)/debian/rules echo "handbrake ($(HB.version)ppa1~trusty1) trusty; urgency=low" > $(STAGE.out.src/)trusty/$(PKG.deb.basename)/debian/changelog echo " * Snapshot" >> $(STAGE.out.src/)trusty/$(PKG.deb.basename)/debian/changelog echo " - See timeline at http://trac.handbrake.fr/timeline" >> $(STAGE.out.src/)trusty/$(PKG.deb.basename)/debian/changelog echo "" >> $(STAGE.out.src/)trusty/$(PKG.deb.basename)/debian/changelog echo " -- John Stebbins Sun, 11 Apr 2010 9:51:07 -0800" >> $(STAGE.out.src/)trusty/$(PKG.deb.basename)/debian/changelog $(TAR.exe) czf $(STAGE.out.src/)trusty/$(PKG.src.deb.tar) -C $(STAGE.out.src/)trusty $(PKG.deb.basename) (cd $(STAGE.out.src/)trusty/$(PKG.deb.basename) && debuild -S -kjstebbins.hb) HandBrake-0.10.2/pkg/appcast.xml.m40000664000175200017520000000233612116732730017322 0ustar handbrakehandbrakednl dnl This file is a template used to generate various appcast.xml files. dnl changequote(<<, >>)dnl include(<>)dnl changequote([, ])dnl dnl dnl dnl __HB_name __BUILD_arch Appcast __HB_url_appcast en __BUILD_date __BUILD_date __HB_name __HB_version Released __HB_build "__HB_version __BUILD_arch" __HB_url_appnote __BUILD_date 10.6.0 HandBrake-0.10.2/pkg/module.defs0000664000175200017520000000170112117454244016753 0ustar handbrakehandbrake$(eval $(call import.MODULE.defs,PKG,pkg)) PKG.in/ = $(SRC/)pkg/ PKG.out/ = $(BUILD/)pkg/ PKG.basename = $(HB.name)-$(HB.version) PKG.src.tar.bz2 = $(PKG.out/)$(PKG.basename).tar.bz2 PKG.src-contrib.tar.bz2 = $(PKG.out/)$(PKG.basename)-contrib.tar.bz2 STAGE.out/ = $(BUILD/)stage/ STAGE.out.src/ = $(STAGE.out/)src/ ############################################################################### define STAGE.doc $(MKDIR.exe) -p $(1)doc $(CP.exe) $(SRC/)AUTHORS $(1)doc $(CP.exe) $(SRC/)COPYING $(1)doc $(CP.exe) $(SRC/)CREDITS $(1)doc $(CP.exe) $(SRC/)NEWS $(1)doc $(CP.exe) $(SRC/)THANKS $(1)doc $(CP.exe) $(SRC/)TRANSLATIONS $(1)doc endef ############################################################################### BUILD.out += $(PKG.src.tar.bz2) ############################################################################### ## include optional platform packaging -include $(SRC/)pkg/$(BUILD.system)/module.defs HandBrake-0.10.2/pkg/mingw/0000775000175200017520000000000012535641635015753 5ustar handbrakehandbrakeHandBrake-0.10.2/pkg/mingw/module.defs0000664000175200017520000000041411610574500020067 0ustar handbrakehandbrakePKG.cli.zip = $(PKG.out/)$(HB.name)-$(HB.version)-$(BUILD.machine)-Win_CLI.zip PKG.mingw.lib = $(SRC/)libraries STAGE.out.cli/ = $(STAGE.out/)cli/ ############################################################################### BUILD.out += $(PKG.cli.zip) HandBrake-0.10.2/pkg/mingw/module.rules0000664000175200017520000000061211712566271020311 0ustar handbrakehandbrakepkg.create.zip:: pkg.create $(PKG.cli.zip) $(PKG.cli.zip): | $(dir $(PKG.cli.zip)) $(PKG.cli.zip): | $(STAGE.out.cli/) cd $(STAGE.out.cli/) && $(ZIP.exe) -r $(call fn.ABSOLUTE,$(PKG.cli.zip)) . $(STAGE.out.cli/): -$(MKDIR.exe) -p $@ $(CP.exe) HandBrakeCLI.exe $(STAGE.out.cli/) $(CP.exe) -R contrib/etc/fonts $(STAGE.out.cli/)/fonts/ $(call STAGE.doc,$(STAGE.out.cli/)) HandBrake-0.10.2/pkg/module.rules0000664000175200017520000000160512271564163017172 0ustar handbrakehandbrake$(eval $(call import.MODULE.rules,PKG)) ############################################################################### .PHONY: pkg.create pkg.create:: $(PKG.src.tar.bz2) pkg.createx:: pkg.create $(PKG.src-contrib.tar.bz2) pkg.clean: $(RM.exe) -fr $(STAGE.out/) $(RM.exe) -fr $(PKG.out/) $(PKG.src.tar.bz2): | $(dir $(PKG.src.tar.bz2)) $(PKG.src.tar.bz2): | $(STAGE.out.src/) $(TAR.exe) cjf $@ -C $(STAGE.out.src/) $(PKG.basename) $(STAGE.out.src/): svn export -r$(HB.repo.rev) $(HB.repo.url) $@/$(PKG.basename) svn info $(HB.repo.url)@$(HB.repo.rev) > $@/$(PKG.basename)/version.txt $(PKG.src-contrib.tar.bz2): $(TAR.exe) cjf $@ -C $(CONTRIB.download/) \ $(foreach f,$(MODULES.NAMES),$(notdir $($f.FETCH.tar))) ############################################################################### ## include optional platform packaging -include $(SRC/)pkg/$(BUILD.system)/module.rules

PE=_J^cLI!916࿚K|k," [TX^Q&N ٳaD_'nwc#8tu;gVV#NlY)$װS1$}&&'%UGJœ̄8| d3x0eiEEi'3/3K#%.b{<ߌ.fE1Qu1'$/#؎sBvɬS1VmI$Q'96I51!!>>.EG.&w!3Muh+]"GоE HnDdDeŹi1A= N_Ext-<-&{ŗQ QK౱l44:՞7]]^t/B>p "EPӎ'%Et#"f}ziq5둩B惑|QV:/x2q(wL (c~Dɔ<&{"y؄DJUвH&4!l #nTT `'æ,޸/ Īj):R(6p"go#OAv1 I_FåӅ"Iv S906@51C'_'$1[RVR"5E' :cɄziw3{s"fcmJ:-*2$pheBtȁO_NHD44Ɔ#䑦QO`V.>c͜ uJfk`ZѸ 8Ih2NDi_P kM9'el#),wR,wDuwFD|h ˢi? @O[f۾ ÔBC0psb=chFɪ) C,u+)aͰn,+H$oLy?!.-.LfmQB:'bO 6G+J}HD N>YVJcjZR؁-g 4&GT4zp%Śv.cg r~rFn]`ژ|]1 c ?:y(Nn:)!{7.: \ņcUV TN4N-<*[MjO}B_g''m6'ggPOrEdyu$~p2H)3HIiKSEzIR8HsjoCa1P=o{=*,=ht`ѽ[V+uOycɌ9wyGw^Kx`8 `X8x٘(s8/G-%-c!ť46E%8peiXD4Q)ª{‘&X|UhG!נl,Q;>؞OneBq4yFpT\K"1"|Bz';[jp^Zj`Ƶt~TRvS  Ɛ^sټj6B9:`+ٹ-n>ܴ.tMG5M@.'dC/H p[l}c ԅƒv}R=mmWVqaR0B7wmX2}r'c/%=said<<հ /҅xd7` +vrrl~t"HB.trgG{AxGnP%x-doHwR'Wn_,<34"JQw vTD,{/blFMD1Yc{^*a潷߽3wF`y{ιwÉGYXT(PSLLL͘5k4 t[Y&Gp@LG[4^w@4@?@ksLPHUuLZ $ޗ@hfFfFzFZ:&}.өRqY9[us#3 )㔩Ql5ʕtiVZbD I6h2pɳtF^lSX"3\@@^&5J f4cڨuX̀y1#3}Hɑy Ñw:v9>&|+'qvj!&~x 뙾zbS2NJ?{g .μ+ܶ c'C~[WoEFo J!;Cow[7OcF<{1IG qdMg M/#++`xhr+V^4xoic_p W:K++˕xLFz]tr&ege 2x˜ A -=1X)} @aydDmݷ %S?HjzK!`d(!f|LadC#_3 #ZFD\ g ,=#==Dt}"O !zP*ɿIpPh#/Fw RmLRZjrRB\tTD8& 煱aQѱxFLLL`e## S ٗ*cll*kx`;y#Id#nḱ(x,+!D j2q3`PDlBrZ"* #)<e!|&W?)od䄘C{Y2p@Ykټ8c/(,*>\Ne;p1pFb;OK ڇ'j) j Fr K#?0y)t c(E 76_d'p,~FʣDzO"/e"$ALgBSt\dn?FGl\l/(<.E& @] LhO&*Z:B!HO ;zLrMVJVFAUc<ߒͻ 'd}aDصiSOUA?h<9DNN-Gi4GeA*/v$jcbmd$NJJƠE^CfZB=[V3P6HSa@'4-ty 4N:"WALRJJa=.mrl8*b0H(O ƌ74_bydBJ1R¯z@=AH_iT7Z/1'li]UKV[L0>z#R64!a8~8'^(C*!O8D!",%RMd@CVal KH1CrDA?lRXڠ£Kxd.D:Qj)145Ƞ\܈~"lR<GoY̝j,B3W3~4hmFŁ`:PI,$/'0f'F M<~B3/j<+P8qTDc@3YxϘvm!4 %nvݷD%R[=Q8a|;~&/Mƒ*i&D &# ݍDn K&TPe%KUT1#IFTq|+ 2o*(`o{(*!)T.qq ]s *<=43mWAI +% 8ٱq86& D:V7L !1M'Z!p9-=ix02cpŧTcK'i @9o!ڈ0۱ J`}Df{$>6oZ#cAO!,)X/)T:޸~4\5v=L6R 3eٶXM;ьv<:Zqҕ%/Al\|cX8Ydbb^.!A|1v_crr'hf{yEFX,Ơcq8Cjv'x+3'"7TQmxչW8, rYq<hY(?:.=B8+hsjYJU#2I3,rY߽ Gf~dɃѱ,SL D[ V4HEYA^V/JH"dzymݱo?cfX%GEEFQС(~D$^V, [ N"]a=pJypx8 EL(&Rsa\Ȅ`{wضٓv,IHNpd9 [;ylھo0cyRl4?&X2iG#Ck nVP=Q{FyL+&&3-Yqz-`|H&غX^ pP!۽ ZM+X._2o ' ̛!KM4֊Yml۹:t-ADra2q,bCNPHfMnːe4lS #yЄ;]|5V6lھEjO<Ï:=.Qdd8 \lZ4;fS izy7ȁ#umRx9`g}zh? L6@?;: eDoݵ˝0[;nܰ ~/ MʑVȠ!`p<pAO98qܸimdu }w3> mM==89I]1~ :&g/ UZZLk=nlڼEuhx`MF},u%V )fO2I% Hd7a)Gjemk_95ucEwhqqqvr"+dS&N"7ޔ%A+WYZ^me{cϳcm mV\r%b{:?@o& R$4PQS7ꛚM1j9@q…`l\Y>b2Bm͝=|hN1<@FF ˀ24|8J2~´)v7W4gĞ43D m#JCUc4<&N40>&SrScdz̘ )VF)sdaOf54T5Pul&rx] 8b(:edx8pV`beI3*%7V@Oiqc%wNiճS"' HX FM4QSvr5.'7#U*rJCʜ(U,K2@~@dc"sr<\Bo|M)q|UbYFN$ȉ-#@[F0,#|:/oK~e yV? rXZc= 0$rx:4M-HtF!֖&R#=˵.A>ZK4tޥjgF5- m=vhکsxH?߼} =T%E::_sk{zgG[K3i^u߽7 !H.bSH8t4*~z@@[EGOn]$>,L>+n2b0 1#EˆpD8<<4 1̐ ]" !CYo~19v+:L8yV8tx$8rŠO( 7s}=={+j8%6o6_-}ܕHgEumPkVAͯnN`6%ƶs 1K? z^W}s&k !Tmoxn>*(;kj|`BD O^ Z[ZfidfEpvڣnÁ~Ne=ISEAVi ڈ<ϽGxzK,|Ma Eh쭩m`:uM0V`jko.)@} Tܹ轞6L *1<>Vq>^xX&Ì $ik`f&:akY NP)L>.?~)x]>hmU.[&[|V',L ]TB$H#[TխDe&j=j@¾gp'nuYE(4!6U|)ξ !Wo?)mؾGp|cPsSP@C $ġ&G%3i V҅o$~~Pͼ%N ͥrCmk`PB9q4x^Vw{c郟:#S"*zL$!?ۍ |}a!u-aMڳuv&8iixx6.nǠɳ. #%O lp7N4I W@^E־O!['n|;"o>H `RGCNia\%  2X|˳X6W=P3X;T7Xf\m]7}PjLy se5*وS[)3&J-p 7g`x#}pb19RwZ*?(tD& M-/ ~ỏZ`'oYd*2Y TM0soɳܠj["=bi,y\#0jRpw]ὟϞiGZ$d'N_r\ɫjpk(;5R3BUO =-Uy9WϼqY cy--WWs^7 sbK@\X^3T_#fjLUdJA\xEկދDQHgOXBu.'ȗWr*[;z{0" F{]4R3p6B-yW<n ]eIϣ%.V7^al@Ol!N\X\: fL !O.T]{ 4 jkc'ζt0 9Em)/'ZE|VPBp'"(FҶ d={XEٷ`dha¯HNWRN\Db…6AAZT(kɢOox:㰷&R)3DA#RIsPYpjJ)XQ5c4farM_$5)fSPE?˦(϶E5+"Ԋw Tnjm%TSAmf+{@ KS>Q'*}PvPW) *QRTV AL &3 yJD.2u+uDBC5Y,h&Ad%~r~y d|i( Je^c*u+O$­g+P9Lp XL:ˑy'>MDR\ߔHcrϯ>i!lbBv/o)*+j02 ̦0 Xh6n[YԢ"f,+%󈐫& q=5O~"-p 3<{>*nP?Ǥ@;vy8!HR\!L!D,.+GQ Z%XP!Qj/cUJ:%%]dQ h!Z-+_8vjbT˟76GL-3 V0EValmGf@(Tp\W>XAvbkSl꓀7Lbm+ #mv6Ga&C-25Ư!ﷳm?@n>EUm.O]t2(B SBd!KZ\mTBsąl,p@٣Hpԣu5~dʼ) u'ʹ L&xם([*g`B(2fP>r<J%~}A{ʾ qaA'm__ws] gM"RXd lnk*^'[  EElp-PUU*B%ea`oGL\jƅ׭y?wŌIqWVHN<]ԉ ?zQ DDh *_*݁FeDWB?J?w29]5!eX K+jeޏwwQa*ǸY@ʖ䧘+2$)1ӂ^U?çiOɅ 37w"Cj*̕uW* Qtwl]b/.1?q*H\RͺSKQe7W//tU䎻x^M (AeyS+,f)\O!׽k nۿǿSnž˷6lb\V `IҼGwn\,IHmٗgƝGyM6j+KF3BZ\y\wշ_W@QJX@|qo@JEU6u~^W> ߅u&)3ƴ{W~~~eSgF"e" xg97.=}(J^cث2e=y9Xo ~Y/f!y:#!/w^ E--gL_<N*xͶM>s?HM};]iK%/K:1̶77[$:~zccq;"4*)\zcU6=~r\c1qq1aCpg.a(,(`< |)LE^vZ;[޼G{OwAQs{'Vҫ e:=pLqݡO_EZFU<xE=3zOZ5Q,OxYnFtƋ1󢤪l5V7l-ަܫgN&lr_熝۱m&ओgִ"h.ȹՉ7n_}OPjbyP@E= ?z׃]]UYӜk> e]JhO]/o('Z0JB+X^";o ĺ:ٯuť5Z\ț ]\쬭l=}3N'%U5IG p{ӿT*C_ˬН.Kh8!7aA_i0#[%#e\© ]Э-qp. wk^vtVӴ;酫흜VZ,[av?񰨲')rĹlwѳZNo_ϝs_1mbr?B׿Ƃa1`qWYW!E|@Z[,Y][GG[<&l!~ įu?|jlRWŋZl *.QcɊ3\qC/`XSSY'{Z($xJsvD?{~-ՃVCLI 46Bv^7VvNN䔥Ų6vۣ}qOvŒ6 ٟsJIAGET |Cq9yT{]C[ʤNO~wgPĢdteE%C%L*VX Y+V_;4󳋷=]-G\r֮PջϋKK ?~#99?+(AFl8g釶Ν"p'fhSXׂ!=BJ(X,]^]XY{ˊrƕkqj.~81sG Q@$΋5_IlǁO/7 ov< "qP@^\sSC%y4V7uYs+ݷ")6,+ڻ+đ{4[nX5|66"ܗ܇O0T`V4n_Yk1u *< AeTuWo<6 1Id_"E|eOop:3 Qn.\Z^2ȕŏrc ]59ד3qGtIekG' /́LhXT$ʺ1bO}gem͵%k@nTa q"}uwj;9 KV>gUL-hQ1ycgo<~I@O ţ1Qk\,:*o>-m~ wXo?IA_vݕ@c:<4_ܾp*y&4o)k-rkf~_ڏjf^ky?ɇLw1s`"$| %\+wc-D>/>eu&^V{N%n?}R8YM Gݎ'}l5гu }m؍]Gg| ɡ>x1[չ1>6~Đm>}"T_؊XV (/s8y9Ymǝ (K=yL74zɯm8ƥ[7oO_u\ϞC/ff2yPm%F(&% G }T/O>zN욿a2Glza>a~ Bt$V*= OpљrHu"T#CiyX Ώ?|vfl,@`o|WH~JH5"3ص@GthYT2H>?'**;x#P^]mnAZ[fH֜{{Ÿ4{#;3BxGFR>y} c|O􄝿4!xh_;JҮ̌P JU PdC~Vɔxh?V<%gF߷t6+bm}k3BE \2b1V>}to?<4z!Bq Ճ2Dc}˷]-FMMmwh)7Y 77ʍmބOy$ >{=RLo{r$}y'w ?{p_Az~ːfFyh:{"}_v=P7w^Zz̘s͛p^ǶFa38+Zj~դҌP?UBݿq .wE<2=g%D{ 2 Xzg>'ONRb u7RŃ.|Z}~;wئZу07]/wo }٤{oSi}V+!Z;}/0CgB-˵ݹ]6z{)K[ #=Ȟ{nQhI_?-33S23̿A":U^~!t, wB][yh!%ܸd`s";7[:;^545sM؟"tdžoۥ3bס-Ϻas&:e!ܷn"L=/#r:g޿_5lT8Ĭb5+tz!+Ф[?<> 5i nkmm{4wɡ.lN08ϛ~[%?8e*oe!ʖwn5u532k8+LήUs'3*̔Ǜ>%0^?Ӈ55N?C XqXvuZ^m8RV/qS3lhEo%̘ʖO ņI '\_An֍H0[/@?xG;IFQgϟS&U>TO+ f)~0_l'D]C w<~]C;֯}߭ - @!ԋۀZJ _10^RUēVuF孧`A,۵@oGQ@#;^~g1/8nTD~"!z.A> x#DOwp;Z6\& M_Թ QuW݋nM&'!|P00 {et >I)i}UhvYș~ǀtq/gn z߸uZFSWϔ&݇k~k1{M ^ԀmjQBPEPȤ&"ښ!o̎bajЋq/ꍙk7?¦>ZcXԊ`< _)ku{!䶛7[j!5\Z&T.,2xkMkӕ5J؂~jxk5.S(c@ <zYεKn ] } 9A(B 5cza泱$5ԮU Jؼďis^1{O? PA(ƯlIE^Vw076'0~u~uOPclݪ/JqtA5l5|ڊ]=rj Z]mXrFkcY6AM ݆M6[Mͷ ٥ 7>xp7@FVfwLZtUYx mo$:Cmuvk4 EYw}K)y:aƖ6Ӌxx/ V>+Ws?F,檄C`L2?A ӂ!*+ R_[7?}e Dkx/=/^S3v KP~MO`k*'߁dB WCT_cO{. 0Ɨnu_ k0c-Yq8Fwog8 657tU}Y\vM4|xH)U621g{-eK`*+p5{rm:- 4]8lh5E_]-Cr+:&L؜talʪ?U7l]If6%ު5渃 k6' 6,*̾-kV,[r2<LqY-mmM5]_?ʐbBΩ,;{eFT틷K2Xcp?PeWw-Vr_UV溒h?wvo4C-1-ne]t'b2{g9ڽ .~pSMO9d֦jU U#k`nbJشx0MnG1CQr2PA`-N?p2Ŧ /Z>ф.ÊN.]tV +XEaAAP1rHX8ih%C[_ M-mَHRfLu5_%TPC7Zxu4ڒ+1J L_vQx$/ 0xpܹy=W y6]my03MSW7qѦÎIZtƷ#zߋݱv<btyK' l(Kң.%LL0AK8#靟ҪMQ ^TU]ߔ+F0 2C\X7`T4{+U1]ߓ -+C{[ߜ =T5EvwB\ehMZm}]6,Ƶ\$OR%)|+,*)++_DŽTA|R4UQVYCjiI _,)I2nSQr{xDv0Ud0~:3I57o ukq)et / \An+ B^ޯDЄbRZ~ J;KG K 3ǰz{xqڹ thB~j`MqUVҊU/rhN 8`^a 0]%?Z_*[ZRSHج67V]>qɔڌA={5(V545#$Gu&4/Kth8J9l ӄVV`"]9{6ʞjWx/G%ߗ3l#*P4Fyy{՜1۾4O]|nYMmOW@h9T-)k TX-TtiC"a;J(6b]cuD몊p>ut}N^^()4n jPP[JPe*A^9VM[nilqo CEukG΢}N fRD/\=+Ʃ1](Kɔ˪:( $BGA' 7 |!AKdYinTAJıZCXaWRUf,|~RXG+|{&-5҂Po̗L? &``A ;E0=`yחCBֿAcȢuPЎ KOd=qI+vJć:n;aVPY c`^@J~)[|ExX V)q7 q9H^`ay;<1 yd(d<ʗP 8 $~0Lβܤo-LbG-j3W!N_a;NBW/s*3O,4I4|WSQtjvXWԔp*_NoZ/ISz|&Oy/^wa I,&@ctVEI6x dr&pfI)`W1Q\Egx{F5I F7ISܬebx5!-  A٫<R))$3, ./.J}[6k毙iF&t',&)= 5^1]/.kY[H ]PVDZΗ`XVsMM&' 2@dwr ^F r0vL+x~n:AT%4  X0% OS7.NV(l0sL7X\MʒW0a6SEP I'ry\!{=23!o z'LVȳ|7' T4Pdu5 drP 8mJ 3R0y?=Y^ d]nO֠sQƋ֢$ ix )&35(G1$:4 rm\h(=mFT"j i<v&Q) pɱ{`\x暸IyB|Ge9Y сևwY`:UzU_U$WVo;xOEbU,S|%&nKqU{J$& 否\rVj9TN*REMi6(dst҅s3ějHfb#mD_KHNM'gDVFjzM?Ys3R'b0mOٻb%fϘ:iS!$R&m Dً$9GwpoSr r5:B%M !>UiȽe%! x^ /e<1| k{ 8 >fʜ%6:pJz'6z2|G1x4nAQñI 80q c(=u`UKL1>T\|,i?'3^35tV1m.w=c,w\'ArFfVV6ԗs+a_VrƁ ݽyҹ mHѤYVnܹHyan^> ֈB2'''%%ƣ訫a!~>^nm.|d΍+͚dd_QUi cXqfƇ U.BO Ήq ?ц#G 7G{iDlRR3H`5P2r`F1|0=-2kPj@(z`JQFʈA1^?`%y-Rp,]}$SD6jQ:{HOPչED n"QT'fhzeMo*ʟT_C޿d +JKVrh ߹i_pI(>|%Ç~낧$*Nߟ٦-~$ lQT!5g C(=KY%)f_A<( @eTC>>r'D&PՒ:|H%g׊:F(Z_h@K}0l 幄V$'@' tAja~l 0g?ߎ7Ict7F#k8Z3[N34d@'L($yTkT EpJ[{ F1`)=fTv4d^(h鈆+U=4?\ Q )t*:]a FFa24R\u3|pn0\WKG2VeЂ /LHܰh"ˍc4|0*%r[<]=VD jn0]: zDG̯9p#Q>Ɛ&h:Ӟ1k .Lc\Ę=Ȑ!Bk" J9 ExqM(=oa鉓Z[ņx/"lo s`$ӌ1#;AG2Vz鳁Φ~WПc}?z;&zA0[nķ69#Nh8\0Ѐ4(]52 .i6D t@NMjs DXp~{fQΒ@ZND-@]'M3AO[wZw=:e[`PH*L ad#'XJEшMxR=4@{1nE+Z\w{uqWWix3&L{n5e ,܅Kb֭\4x.4 3?mͻHOS%~<"5K7 EolH騊9'\hiSRW丰((㞆8F*޸jנ( ,JaG{CaA>ؖhpÙb3x249Ŏǭ.x_K.%(ΎD>ģJ<{pҊxm YoB` VX11`b$n }!)3dr=;%gfaO@v:-&N}p8tjfF&()CPF0?7l3_ })F F H)xK>͘o c۴mѳna' !)&u9bKY^IY-b+TYmUv3*?/;->2٣{m!1@)0oa];k<9)A`y5X'W![YrO+{eé4_<0/!>gؽkՋgSz{@?ت@ҵ[>q-ĖH#2ҴS'vB܂kedd)s*,b1G8X9qxuH~G3@LLw-_=DMf'ӑC>^n..c/{zyӞ@gL h#R#E;Op: 7yD>,/6~Rn۸E4 r4_cb Iu\L^n/8:I95R;;:_r식uX'QV@~G8 NMf{bՋgdvB$(c~wnЉlTU(wJp]Ib+C@z:ϝ[ U1^u? "Ёؘa) QAvo_o:(JIU HБ?DG`THxaRDv`?@p `Ԭ(n]:i'o{tj*,ަ#89!-U))z@<';)o+.i;^iJ0+[^:B4rPxޜ Wco[,1@w DSgPCk`LGO#h}JO,x{ ޲qXq+Fl_TaaV&0aذo`hdlRzV[pie18V܀{m &(- *9AD P_ Î}DE/)T##Z gkC+TRj'tӷ[*yLpo aDgnRa4TH DUgYcE æf0=b;҂,sk/*s- X/('P'1*h7{Z8ciy(.I1cSV̐4 Ka';!4܊?7~)%ccV*Ay1Ac8>nPJ(c↜0G]"pLE_N]4 E{KL;'@K)&A/_I&Siapq-fk͘@JAh#֗"KJkŃ=\.snC5l ܼY3Ex\W>fM#;@tOT:mm =zV*ɨ7 ؑQ^\B Kb#JAabqP4^TED(@: 6NYi W.ݿd"菝|Ӿ3|#iKb"/3g FY ),r`+IQ~xkP&o6n KxDKdVP;j鬔)-cplᕒzF=/C(sHz,( `!q-(&-@Lyb 4O~" SQa.4h)%롈U<8ub f7NPtVĘ=MUvYOz(H|_[oY5e? !+֓bNS XO]i- ޖ." ?dvx[ .AKNzL`m7,=u;EGH$ɐLrF_CB_ HQ1;4%LQфA X nek Kn,X}|KEl쐌 c\&cegƫzעՁCLӎ^)9X7ap#K)49Wl%dK}Dͫ@Y7t`7j Qh'lHym8"ȡQQh!7o0!*XBOkՔQp6nWQ\,#[xAql' %̘=!pAIih%+l\p(]uQh됄"L`Xoɚzusv?K8l'|(v4<* X (P6a)ŌYEP._+ P˗҃oTI HR p(8qlcǢ=, x?MԁSGt@|!%]w|T>5$B+B  ԻPr:u zmHbؿ8IlƱcA*w7N$qO~In33kiGG2g, b4+?8p^;-f&sKw ]F`[|?iD@iа(8 qXq&m]")m+[3#ܦFVA9U `G{tLuͺC(X%upq F8DztAnR{BēO>s |c='j"9ްǟx0V.X6lѥ`m,MD ,6-Qcy: XQXV7*j R۲sR=#{:q>0y am~zY-8ɋz4dtjl<:U89 xx<17[IIF2HLڷyysn/9[Bʔ4,xn1=@R`#݋ښ!HQ iX:* 3'qFy|ME PAD'|kŇxۛO${*:՛@i!z#Of;N $&ƈцz}TJj[Jی1wTD JTWR@jasL_ݦF%_Զ{);{g{6.mͯEnĎE=;>~xYABYKKd20^f\\*z ͔҇bLIIq1>ȨDR9& Dlt=:e.4bSf5d;Ӕ ;8:SOeg??ysFI6omjl))^J9Yp3I^QĞ/T0Q65[P,UfBr_T脊|@wV?6k8̍60@s kpw,cV-ùCUb;v?HNB ěR.z1,/35)!!>!KJoQqWzjF8M 5V5)frk%#{ 8D)mflxA맟 ,lkS' Bx(I2EmStP7`^zR 2JO\MM5 ZϚ*kA4l=iA>tn4{+!bvBlAAb[$Ch)+H q`0L㰉 A!aQIe5MbNd&EQA~y™reU]4\_+xVWUVVW#FkÇ!_F `rq@pcۙEQU ;=>Ӧx{ 3B"dU`fAww ^X^V0W1WQYPWH+ ~{o.H`ÖIJn:&6tB]-~ gkwAHܶe s"@)ʈ4^ krwC]I@NK-[@af= Gj[Z`|b4 riEMCRR?;7t! .bYJkSv7 q0:0΢=KݵenNpG\; g7h0 (AN2J-jҨhUP+ Qҍ vDOꊠp$$&K޼t=3?(QV-^o`Ӵ1GFPp j1wa:70́pqe}c<WjX!ˤrwܼo>h/oSnL>tupxzuqd,Tv!nKoL`w-mB?휀Bܦ¤ ߊ:AWYWbX1.--S4.\{wܺ~EGlAD0p7{+p޷2|el ß{ /w7eKCp u& x kpq'Т[ptnÇ|5Je}-Hb`ꆎ%w>[Vv!=jT dl[yn~غgYgM2Ú'>W}}P/MX;V%aFލ鸺ʫW1pW)ۺ_o۱sVw5eQɆڼæ: ?;=Z+<4O"^Bω$ 8/U(?)Ƨ0+7w/_`M,(`9@ʗJaoY boۺq܄]NDclHC{^be)[v/f&yf  xoWl۳CL뻴GC K:1u(JĆ"5f>u7mݾm%t0d*f 5JM.UؽAQoVqnY&" |4 )x:,ujUyP7aw[/kC`=\4Gb`s "&Lr K) 8JfRL&*" ! 5 -m*d9)1sCps8TU\/W o ضqE{u>yXq6pUPHjm]MN>a^^>/ǜ1IQ=7ۡ́('Jܲ}M?,"3L:5/۸k~zh\!Q~/X_-XY fe YRThsrG!S6::a֧8M*EI H7IB@Ux|@قtEdub 1Fr\@nvҌℜ86$C8_)TR(.[uaOKϠ@IuZe^k@ r_]ۭ,NG S&at!lܓ7܃NWe3Vd0q2ke폆Q8+*ٲkl~jXs{~h:?"qstN y0.P`64@"L^!;U=t%ibr Rfta]O@w@y`|O M!p? SZ5;"MS$:8͑<0 A=M8 aZM ն֦Zvk+E82I+j)IزW5wn*ˈ '0Y2x>n!!%4JaW%oA\8S7JnppvM;& /)<LdtE(R7oZ)=wVP˝b*ΕwfF3% fXm {݊Fe}=ZU7^cF&@\YU ??qъ 5 Uh*!65bk=:ZDQAPJnֈ3 GhKQ00AZvN.֋h9,d4;yu[wܺv^o[ؽf+dx5 4d:XeCLyo>#4b0]*\;XōeߎTፈBeٲ^k.=S/Úyn]=lvcxvQ%5ҧ!W)C(hʝ53knq<}8\.A9f<}Jv 4,Zi ;ِ]QP+QɏA?T~uHcЖozprxE-\&!W l:U5-WdZYvbhVR3sq{O-i_~-߂tRn,x8 po moGmQ|F&ܪۖ۲uˆUc R $$/'dM.#5<w&NB29T,]y[O(p<)ɾ O"z_@u %g`mKV߈XGU̱>Yۺ"7{~E5؛TU  N ]OAW@O)!"W1TA6lX~]Xظ̘hZܳqu+ǰn7YJW8QHbc=`-=0:.>` ώW:p)o^aFtց&-W7bgHfyŊj(Jbqx5!5guv;fzcʔ+AhJÍS0Ϙ ȇ*1?~W\^ |hwYDYNq= \p%uat)|/H[ ;֕\+*kں^z RgxTsLIVƺn]x& zfh/]zew+Ez#\})&-OUW]۳ɥi ~Ma1(Q*ɡ6@ټbΦ|fz# TvNnٵr B[1/t`W1clO%*KxSFWi{ e\)I9,_ta&œFwtI=j~ay 6+IILT֓pe8 &u(xw@!W!:]tgA8\%Y])IbmfDNhCS )#oX|5kHhD j%wlj4,7-~~Lq sa㪅؅4ta&ua `-7r å2uP-ruaҬ9PPRq{ &S}e".uI 8ƌT'|]=\B zdfz@;3h,D0v/YRS]1㐀z'; eے+aqSN56eeX%D\k)4ohwt-^(-zd R8HW6{I  8- q(Ք87d2stP 5"[d8zFT6u.]~ԀE.6xr6ז$"ս!3`6@hXdŝ#I*cMrd'EΗv:2,vjcuqV6f(>LllY; <#oZ%Kp^}a)*?럀r jRرc)8E{k]!{,F^6,n  1J9xDZ ;:O y{APJ13\PP={ ~3ad E1 qqQVhs'2 j㦄IV WlEYG|~֝G71 Rt S$v # qi/4>ctMRdAd vFYh] +؇J1Jl4J >=G(/j=Xia"5P#oH6 :rщi9%&k@(+NՃ( 2Zp*+iq:!&b۫mv+;dEy9Y k5)9Y`f 鋑4.Pf:I|NhY_ J)S9ă(2Y,B2 sӇIǥV ›E*IZ5KH9et27ӱb GJ dT}GqI$P"a<(Fàli 6JrA,8$i.8KkZ@pSJKA6:ZrhM@ I9&b.kP!>Hۨ_h-@"qD_=@#K_ؤa^08 +DEEWgA}(Ĥk{ZFBW^|ġ*g<'Ú>MI|gW)d WJ؈D9%DZ;V\N; CPgNcJTA)yE SO.+N(w/uG"BB@iR :56z@b9) Q<m,GA;kf8EƧfO̯IZQ`Aj P?ט<̩ 1o Yh[d3cO.N^8E'AZ@_;|h}FKʺ,))'Cny!~ζ,NH[z G:Jcm\)PU )zu)%(Z1oB舵` ȋ 4tv+z +berӓ. YŰEkt,ysƬyqD)+PuVYg?Ó*Gw :( X 8oC*vi2Pm&a4y ĆJ.3i,c-⿫:䢂ܜTިlD("/+M d,:Z5D"H2S1)b8`)Ī 7RW }%*Ucb "ff#C|1F\f rn1:!;*U bFCy}ŋa&d7)&jk1HehEC'Shηԝc@< *3@G[79Oy$l==>dc2'GXz\8c!"L//($ ͣ@y򇀚Ì[=\1Gf'׵ﲔ2n> ,R**ՐB| [@Efq@ORigOʷ̣sD=A60!iTD\[J y`9ԓ 8).0PLsblV<㓐3(jTW*AsX! csK=xF=7%ʒ/W:yEG)Xɨ.؏)Iah){BL. (#ϓ^S rr@sdgGBoZxNOHFkb 3{$p*j232kì/n 9̅*B&{U; (y9=04"6gT- !ۊ*ۊR3$%FNĎD"%6A&$2Djm? o1W|i ?oN )T}H8IU[K֬$aP3s U'FGΛ;+[>˾H2l;(CB" jf Ob@ur54ʬzCɲB6$h 8y# #jjI wspjM܈ӽAAJbRaf KFdLVsFc9gcWOAHs14P4"Enc#klRFG7/~%&rY-T9jVrIKIN8Q;#: k[a4BL'SNi31ѱ&]P`vX/2ghm1&Wq j#9;'qrE@@G|&3uAEB6Æ}Qgr\CZjj iٞ @̀( Ҏ!]Il\<2р$2,CB. )<>.YJr9";s-z@Zuf~QCj j -==CuUǰ&0 ,ZxhRP3W),؀xF^ N >`R  lKwrreIF,PB}B'/ lKk[ho\=}gR:8 Wk5$DXpgm$%-BE|=\3h4+&3< 9\(AM #؟ q'tK{ӡyEL)i\j6}#$*fh,s\s` 4q'/D BJΙ=;*ә,6! 6||(4KUz.6\н`M( g"iB;Ȳdoc@]#aM̐PQNԊUdRd|R?CCy >REDj)TJ͔,hsmS(SR,JƻS@!oK)x8.D@TgX,d4JuZbv(5Ϝ{ZS )Dj<;N^"X ,1D=^W(9\̰ L -Hzɼx[Fe)D1I|`vƝ_AOI4gjMXB=;'_ð=㱬\pfx9Cs\34uFplj\y?/ꛧ㿟j~݁aeW3<z՗_|g~~i:Q8g̘`en$Vo`2u'Ϻ|+:['0뺘Tϩio]|/}{?r'XhWM ާYН8Y #^*5.^ϿƻO>;^{ఞJW3+?}hō"=S4hkWTj^iuS]{g]x֝wUj 3yNA3CwݹEm]|sg걣wl.K f@iek`f*9r%4l{_{/ 0̫x58[?EoAہ_~k/>5䣎|d28+9]RZ=("U\͟O}rFqSS#,HRDQǨ< NLÀpdiC?>wWIuʹӯ޷uu X)_8֒!N7Tٽq'O¯XQS2y;AC׈OI6?WO:!~U7><}䫿;C0k:7"f ͬXc~-cUz׏="{,M!'=y {ܰ˂*qxTN˃:+s F |n CC*#M&H]y{/|o]\t55({Artc#& uR*ZVxN~?'|O“;VTP&KL2\\Hkv?u1we~Ɨyk:\g$;55UnCd[Gd@V ӑ<~qۄ%1HVh;ڰ+p(c 1Y?^?{O;Wϟ}_>}uGEV dW`8rB,Eݏ_uOMڪݺ|?8lZ# Z 9u<ϯdک^ٷ]*COa2ǭ#8Wݏ?U:kT׾}ʼd5sztIEj;xg]yq G LΝ=3wT`!ٲ'>'E!=~ꓯo T7o^T AX[d@="3+=G~wF` ܺw<(+壜'{%l-qorր`u_>zhǪVybu/SÒeMv:7? ‡o衝dx_h|~_3ah\;~}xP?w;3^ZȒdWuny˷ C@7|S?Y. ZOi 1ccAF93WCj O5!3'_9zR, (~=k L=c d8V0 )KA}(BAѡNfn1ٕk?S~}o(V@ߍ?=g̎ t6J: Snv/_>{_ad^˿ܻ+bAϞ)e=z_{g={Oi-K=Y3S&Z^V|7'|uW$ ݽՙy|犆hP(b'G]Pэ8~` Wϝ:X0P|f?K'?xH}B=G'_zY^I'kWű7|;0pڞp@g8+jܭ'lYY c@Fe#^?s{-1Kϟyؑݕo3#tzxsLj_=kǞکH .5g:xӊ椔o87~q5)Fsgݡ )sDc-笙 &e瀸ƞϞ8sʝ“Н+Ϝx@Oca\39StkiYt/wr=|ص:3ޒ S쾹{PbiǦ/٥w4)ݛ>{Û:JAOAUD#r/]cOqu^/޿J1ņBSz,҇ϜT]C{+\KvEGǞҧ4:^q~{^ED& D8$rV>_>2|\0iʇyjevSV\uzhrY#/:QS/U:b9]30캞ϝb?+ˎs7et/Aֶco}ze[Ǟ&K@T<3efVx3|ܵ{T޵s݋+}4(eno^;nIϼG{b5_!)Eێnհj޸/ٶH147" jroT^uiȍ𵝀\fT,;w -t?=kqE,3N׼w>xo`H@ߍs+΄TdN|;T+ɩݹՇ'~{IyRgOnXwػFAEo_=<8{mҌؒE;~˷e`؂o]էw,*qBjV>۷ rڒX˷ʚΗ Yжgi"7VRDjs[A$ET.XGŌ4}|v.K1o޿zA+.7s)\{ӯ~tg뎋CQl+ *vH1uh5^#J/ҧЫ޻T[%(̼;wufo8S޹{!vKV[ܣ >\ϮD[@o?ez}x= Jk g+=+I?Eiٝ&{`(ԝKq[=Vz\W⺗:}NrZշyz01Б;kyF`ϺLugwcExr_l{U)DJ~B~JK{P/A3C[~r83MM627e[_6`,VǷIoMz7}2_?[[sz?! r<~g\Fʰ4%XуMalMOH(MyTw_g6HďRdD_؁@!ʒpϣ;,B7JZQ讶GHb.D>Rdb,D3()6 C%d Y3n\{`lF1,dfuTC+Ɍ w{9W<{ מ Tkt A7\0c m@D-Vะ0k}QiEMbb]/pIٓGU1R7e 둓["qpz 7D~{;4![w=x N߼z믝K s:yQU:[h`&e$cn)d3}zx$%j5?lIwIDbfaE7oݹK.gρ@Jxp;oި9_-:}%2Q ʨз1U"-hGpOKL@2‡?Hm]=;B!\#f5|-:!0&>=KUqYxwW,8;uWk+/f&FA9kzIc)I=(3Zc2x*!1IX[ڢ~ _Zg$aYy%e+kn޾{Sa*T>n^Ru\QANzBݛ_J(L0{('ޅZAAL0q  $S mێ\)a,P`D )vM;sKBMQWo{ 2C4||6XSy8?;#%l=gl[( 5>l~\߮hH(uyDžX&揃lCܾSgV2iCD9=8k6lGRw_ipxt\ mAJMo#K/iϸ]W[}D % ][ס,B)(")j@;`Q~-孵-7hgZJ:IAIϦos_fZX*Vì_>_Z$t=}lƵ5Ŵ'G2H_ 6#1ߧ# }ŭFSZnqߡhP ڗkEĽUZJj//+LI3'ܳ}V#y[ʒJo5]>w02kP \Ȃ#bS2r vr͕uuun+5./+.HMBd^|nX @$g-#B]a-MF P%nrczW @L:F<mA,(L;<*&.)5=3;'/+[X6:",8ݜ0oZvjQ]$C5)^Aw+av:+չg w'ȽA}TUf)Qßx'wmC#"Qa!A2wW@>r oZݪoQ` ἥK&П8&:4 azk6ۧOo2V78e rA/߭Xv({PБ'' 9wM>~!`E6e\ˌ)Q!`Vua!_BOwnx4k΂v`";@}Af@#AȦ&CHSQa jcڨu@w'a[ǝ;Qu+SchEi,WVo(,G 4mǀvLCqP3M `m5iB'6*}\ɢFBŬ x:SeAI4/R,kʭA K.[%KP[%3#⓪ ~6.$@4O2bexmF1mƲ6'NM$Mg6j#5g ?yRF# Z16t050 bˏi ##)IMUi!y-~0N̆=J %9H9ī`dpKw t#\,{?@²ꮉ uDe4\ָlӿ|?jsun|| g! Y4jf$-Y(P`>|.KVĕکwG 1,&-brD;hG{悙\aijo& f9\ЇGjz_+]fLR hP -'KRXݩ걈.aJu h#{j0io"K&kE/'Bf8s|omfx S: ;H6 82ɈYeutXz|O5oM &|7:TQdZ/SD K`#Շ ޝHS!Y'%6DH'3,\Wo ;C]Azr&^4ȔRG֔D5.ttnt`(;1ti7S|gx(UyIsH!nҾcGw D|M\kWcywMCz>1yۋ3`:D4|u[ I! @BKMJpplD LJ; Ё;(Uэ~ם3 xFrRIn{ 61u4t4AVъ(F:+ 䆂!nWK@Pe, փMG] `,9 6Y/5|KMűx=E[!<é9}(p3bpDj'&"JbN4/\$LԜaHu!t1VN RtwxENPSl8|p-\Mqa!\ųMyA[(ttzD޻ ZBQ/Wo!؝*{`j8vS\K(t=.%o͵Z*`|)k&+e+V mto4~J}[ $ PaJC@ y(kKy+Zk yq~A0504?jhA_[7ػ8Fs = ZZߚ)p-3HU==?E"$oŨ \Hd:U$mxډv௽xG~<\]?=^u5#]CLSnO|$% LGD1&{~nmu,p=K <6D[-glÕ3sE(a* _+m}/ڂϬ7-_ nR⌑Z/H范tVڐmLQ^cXꋪTrFYY810R̞;"%T{v +8 "ljaΟ;{4kgN\VAa1 n;l:B߄Z uBAX7Cܻ2xq\DuKq۶۹GGٱ漹s,@hF=a`faV)1m-v7heH!nν3ġ}fvܽww\86a[pNtQ&ɨa3܂ցNP{1&d3dH롨KbܥS{|CT|/@ܥ+q 8'󌗗Ow7gS'=rl۲3_uf4=cV.$3j$0#E1fPyP?:wӵ)U߾^N\#c?;8k{zyA!a]AF@) e^y!XDoZM 6UF7nc]:$1A7 tfbX/~dwpvOJIḾ,3#=5%)>6:23=ku+}1nt3 aa !P :x}]I=?rƏF$$se/\TYYUUYyeJs3aK {:؟8r`϶Y08>(e0` %f`B' ӿZf#\<|ŲĔRǞ Lo߼u;oߺYz+.](/-.L2&̪.޼&8*u{G)sέׯ]\yDXWCeqPg|HtF$jb~!EYR XPMd&8Cb@*k^}g_ͦR0|~;ÉU/VI~vfZJBLC6mH_;Z`GJb#F*Ƙ?5.^ IWr/啔eVC }]=>@Pxx?﷼/wv}=+U.]{#/-.\vjB98V1AkVmt~`og!-AZ#8߸c1'oixlJVa*e+W" Z[ju:kp\M.d&džK}﵃gprobLRմ鈑GpN+ P7%;Ob5w9pC OJmm _+j\/ BWW)T eFVqEEibO*Cq-;n%Al ױ'Ych׆1rpC0M/ZUJY޲/ BP$e6$m!l/^ v1g\O?`~b*:(KZZ@1>]:"`f=ǝ 2K+.bm݀RSQ YYVZ\T*/77;+#=--5KY)iiYٹyEd x_Ug$F{90lFOLMkҶ:v\p۾4<&>%1&X9!iW*Ŷ@bDGEEFF@I9[\7;D p)mfȰNB Zk,&t`)ONg +.-+fz"Q N{6Z  a~M[\VnQBګ1]UEYiq>GvmXpژXE.}\8 z0aag`DlL[jO]U`H7o1|L FoPL*HbD*wx$;>19-3+'/A}a3~UsEYa~.Bic9Cƾv<9)Z+ܶ8p}ã责ڀ2rWnu]]PZ~2>MB8FJĿ>A@?m[BKaLB%0v9gaSG2b\ VA=ueIdg) VnهxӸ̂s!,#eg&'Dz- "XSNBr*1dw ۲j*8d:wz:Dq:z8ia9f5:xg_kx bgZR|) sXLR 2'!5 &EݸK=DJGQߒ=@Mcjkv *TC3ʠbbD$8/`@ lD2AtpIF 3`֣\6圏حq6T7Wj:YW+tx Z e&R,Zcw?,>b͵udA=4弅,i N*)dB< 8[.%CPm0h0x'&TQp"o޿-v{", ȤsP8 2!̓Ґ5H2Ǜfue>(x2(UxpۚE3?fnԩ-|rTb !aJO/QӰpDɱ ([yX > )^6bPhZ3w2uJ`ߝ6ʢiyhfmAmH/GwPzʂUۨzUJ~)XVDnF*V,c9E D@j ,[pp@S#CTgR :,dCq T¸JLza' jo:bU$ &2,7h7&P (Hs>AI V<}p#؂t7ASVm;[^E*$ "ÀC&b5 X;,aRhxb x"Q'-"T AV͛ K l̷mFj6p '6J;埖o9 ZrbB*-y>XWr\V2l+l3x@_O Sc6h3iPuM{O#o$ EE ޡQ;[x?>%+RY~r齛9v`+Ų8j`j1㨉F:gPlV 1j1Aو@)d0϶W7/fӷRP3Qpl,`.f r' ?඘d6 cpVSڤJ7$b&A$(b8󔖖E8#%(/r7HvqL|XT wAeY/_ LpʝX![)e͂Q'֬&6tBD y1f?%S 4p]=Zzz;D%p گ??eI7-5ziNTB4ӂ7`~<x6Dh?B63nN|/D<ӀW~>ww?` 1}i~Z0ɪO!μU[>lp:J*BA$!H)<= x'*>-S)v=c5% 3ԫEpcsgzvZ !]c0iG} 6w?fx IM/6ާћ2#p&e@0~X2z!(TtvpGslGBO  v,_t6[:M 9_cFނ i lno rS sRcCdmur{v9f\pBj3 rο |Σ_qM;1|R~жGX-y?W93ƒsP=݃(' I-(peaW 3},v P%<`UΞ?{ڴs.[n៑'ƶzWRxOCA. 4 Á:?'=)&?e`;b}d9"(q!2 m_)M={5>p/JTrXp\xgI2!щ99)2O=?,b<*&s]8#^Xw" ;< +,XuU gN=jNSf/X-;:HkDo5Ɓb9t 2J3A r3ƏidPXi'WO&IhfVޥљ8hDO;zczR32ҡxG<g k?q=0&)@Y{Fh˓7Typj:Xw9x\sL0a֖qo36 S#;NR\T ˾@MtΔf-)΀36И‚~9ڱvclmG4 ZY[@C&YO-Ȱ$87. οu> ),EoDeΜkzԋ!HCcS.> .7 \svtU55Qw9|v]&ϟLKPCNlpX~si+' Z\ѐB&OӜ=>Xupd"@Cqn/$qCvBlρn6GK1aGh; 2[HP'o;(3,H#9z4F;ay0c N;8q"w(ܼ 8AYpC:[Mj1hiQP l?jD=om SfQցq\zSHtF>&I'KïwD׊0,%>:jϦesƏs:|`͏? Ohʞ2([ȔS}E{so~ز1 Ps`ye!ʋ:JL#֨73,H\PcpP#,;XQj>šGt)oVEVN`]#g5'LJ9.ܚ.ILs#FM['XJȆQ+,F_cr(|؞>#4P|dpG VPkqKnONw;9;U?8hCbφx CTRUF8:_!)تX#kk |föXi'p2wp}q8EEr\oX:{<7?;%dgYTs(! <}pK9Xdh ])ފgamasݹg߁Cr>͚pjt!QH*C6<<ԪSb<ۭY0}qޏhr|ߏ+KK )w:>+AZ<$@ H %xpwwwqWH{(RZZv[])PE $~ϝ PY?H2s{ι(~%H61:=mCp0w10mDqWbg̑)c$!{K9'69Ni?PëU'22^è}ˎ$o?x`ߊYc:vwAqS^9|=O,,+8o Xx!ؽBXpTN-Yq8mKZ=x#D`t^uv70r8m6yIɿsፓGbJcb^IVr`lb@)"l)T ):hY,]v-H #Bfs 3ƣjO` D?{gO=)4J]@9{7ACa2#WO[PPTY1 =ZTCp!HxD"-DC"㞠)O4aw7g}qSf-Xv #~x3lL3٩0CaJE\&/=~qOsߥ:XA6`1/YQ\ԧӑmintuXӸsLmDۧ+^`>a}<{,:qBC/X,?($\9z|v{Zl o>h:CX5Azam_~@S}[EFVKs|R^+{ԥRirGqبSX~#UBGάM SOsS!`ooixd& Ta2*ihOoߨ+2_e+:G?HsԤݗDX=Af^0jso=N${BCXK4hdW8k'ǪkH'+ c/2$nDf+oy{6#Ri^;/qh \8 é^gUۻGLkp 9R]Ri`s޺Br.EqlTz1ʷ9%$+!:h/K6RM8zH^? ,I6KK0`OW?.W!XY97g8w֊ !ΐ4gdީUfyV'(c)JsOC,QRGRK0#Vj@ϊ3b8_vwة3e Ѿ6g8{l#~ FN*ǩFN"t]efF2oM"*8n;VIK ì)k {e}4j96Ú;:R[Nrp]wGnt{u@)]M]4Ϧ]wwY! ,;2|%0;q,m^`woлX'_?m..G).?gN=vjNgl}e%+ kJ;zT\ZV!s\/ ?*{053]6ض'Ti:{R<5z"5i*E`y[YXؘ=^K.܎쉁xtqsW?v3Z9޽OX[S:i~js9ZkUv=0Y}+Rksl;@)ZK<_آ&BP_._gF *?SXv#Smi-<0M@ΊQu`bKiO6(<3DUX2NKV, Jز9V>GF-${LHMX]<]9^uׁc9Aڐ"V'y[߭g== 竕lIbM"#~qƕaٹ:AgއGEGFj/<>6oŐ@'V[r2d xM*t>n.UL[_ܭ=ZP 5m_GUkGx_}tTy')ggoVYegcɊ1q F(-j%levz \1sWn Y}(*|TZXlτ~up@߮+ǰGz}x+Ԝ ゙kǬި 8kиltNȠȜ(h cö?zH]wV2icC,cJ"U.}O-U*,`#=ze#:\͠ぱ*v1)XAu͂iccy&f)j-*0D<2NW@@P(*Nb '7❭a A ȨH-8 2mA%c`}Ejkyک:%,|ey΄8obzDMsvCkqazpE{~1 )^ȢH',_4sAV}9 $Ӣ#K"##ʞ ] g Yd^`jk5ց, smĕd+s uKL3k9W4Eȯusu Mu {=ԡj5Zy(n8ޘȆ2=ez):ѱq1Q1Pu,%]Y) h";%PgF|6z򗦎УCK(\΍Zt>p6cވ0[DPM̛9m k=-[+!X!>22" =6 16<`E3ǡM8UXrF׮g-Ym A/IE-V,yi\ΙCuH_EF*ezD2fA%ԟ]l`4ԪhUQ׵Yע_X,gi~4܈(/#MEFn2%Nu$lG" # %\x-ڼ5GnBQIe&% VmGSVTn +G5""‚n@De"%51;E|DFd#5] ܵKM޷S&rV]Nlb%GsoߓX*5vcFx尐 ->f-[4PsIeaz.Pec8ٌժTKX [t#).6ayZ a`SP-3U]j(yO&Aqd/ϙ4O r#0%΋ZU3q%h!4,y_1hTlQ @+ $C'I# *:rpKXAhqA%>voCK’BavȔ7DJg濁C޽_Q.a,7՜A_j-4Y2ĈbDYh@H A14xojH=U')ժZ*cj^g.\_im|/|̉#PyF+8f岛aQq_4|Tʁ*j<#8-iUCKr ܼe />֮|y)!`L"Ph hթFV %LP  KX@#H <ᦵ̲fMfN8ׇOQU>CEXKa9 W29[řGL>ϺѪ#c_2ݿȯL|oFf B~ݼGjx4*UBMi̡2Jͤs76U {+b,LH ׮WݰI&t j-?M7jAzwP]TW7ՔiKa87a{%Ip߂PtJz"Fe[ĩ5q0\ D0R)#OW &ƾ.<"7O1RWZ-2y8D-q%́E͵0,{y93& >=Rɣ5 )OytMD92ډB uvARa*LNL{(eKXS'@9^mZtoJ=5Rbxj%Cso%%kM M$IlՉOٷ"rx//H!tgͅ!U3Yz^55XU[7y(z)Fb*񁍛t2جm갑.V,#ϝ͢E:VrU5cQߋ٫^D\ZyJbƌppJ}:{Z[d[hSJ LQS^ Ûd5[^HGP. !SϘ9.X/4,1/6i [-xi3gL*$ á$*ufXIϚUEz*sao޳ecxX7'M:m1~̙,ci>4(6'7vaaJ{ʰxUƛЪ;k)7݄?[C1ƍL(h0~882U[Bе3%D)i eq)@cwZS/ 8V C xô0!C냚YNBܦyʢ~ȧb"rt |l=oz?} 13_o׫ݐ̝0-zBkWm;Lw;-״YH.~B ev>Gi7c"*O1iܧ@&UNۃ s"-m4X1Bt㧑]˳(a)OfH+A)t|Ȉgy# Y"9T*Ww+mvL3MMLM^LMLLMUA9Z]  E3̱&O>umٕbkȦRO옮P9P#{};O<$wI9*V-#e0f}XbOE&SHEfPiJMPQl-p?C 3#l]d-KudMא;!xTNʩ>cY غF }wE1F-[Uuݣ@&N%dF]TEܔŕCHg̚/8E;jrra sߐrQ aBQaEg K*K#MɃk4;!7n4۴N1y{aYzyjuBsq W )ZB}֯^"ֈՔwSN?RxڴMdb{+7`]!zuJ԰A M}_}@r ۿ~ }8LiɆ gpN"GkF(o3UAn!7SгGz&-`Jr*'"b'_ç5M毗5ŵ8,i(:q od~:tz[yt9@ijZV'aR:6;<Bq{) >OB*i}MzzF;vXp[ƾ3UgDʰu~.s4M[%tLOc=ڶ0sO6-?)(-v;w33-ֱ|`b~c9BIS, FGqmiƾ?7_&*!=n 1~#ɵPe=|_=BҲr(7ʌQ%ECao3V*+& @ՋdߎPE 3iI̔ _~s=޾07O`ѡlz\^^VV"XrfݻS%_Q/.lN6.mUBNDh&O~=!2`V 請Un]mK5xTL3o?_qa~a^jJ2=W())OqV\;!+]z=oK=J Hvx3er㪥 L+jP&j:ِ:yكh '/`zpnȜ=O]/.~~vnA(ŕ BQkyvyZ]^,]bģ 7.ɇDz`<L"?XVxalkfβs$DE)sr UX$-/ɾRrnyo YUn/p] #97lbyϾIʸw?+Q~!qRbR` ԡJVH=C v;wWIy6%ŅH_?Bœ#!ÔS Pۺԫ'WOv:0J!]7h!庹bUZww+)[zizqˍ8/xa}`ծ m AڕLKTYc?ڵl V=|5]]`coև:vW=*,KhR\^ZjC(ffRN%rH@5ߒ|Ħ]G---72Aq{IW?tخ |-\ȮuPДh,LXX=>a˛`vϯ\.5G<T bw1}{D`T+0Abq6W!4(}Tq?Gx& 'vA*MCgIu7s?+۞S?4Jl,ILIATѧ~H7f^P4p/#|+q[L2կ'S( s#Ycy {-H}>e g>kyqU[Gg Dݲq! XV@mI |yN3H5 פֿ 7g@TlkqlEPszs]4Xx6!M]1$np-ջkPkPXPxΡlL]ퟯgdfKA ,JK/"xbJ#%3ֱ]Rf߻oWO$,s H}'/w1^0T_dJ8EiJ<ݹk@xۇ ݽ/d`\Aӱ\ (3zv>Ġ*K=\eנ0)waoqG؈{]v7;,>T.=8ՒZڧ MbV&,eyYwS_="P؁]4m( QH0JT_-6l=:luXrω]yރ|f/0pRJ1iVcʍ+^xu[<6b'y =٩s%sWo? aD J{-~e~nSnIJG?ɺte)C{vP B6$@b*j43FC&.$0O˯}d?PqET--!')]C|~LJ(/7^?sbwĦE!):bVc|gEūź}jA UHIRR$?Ư<[9]5%&ONjĕMXǮ準:tC\&{t;"JҽKHq>4Ŧ}\Ǭ D83d@XfxԾ & ;8DuKK[ՙP#[#p_[{ ڐ'/kR"q1G&w$D#4%?ϩSU,KʉBYOY+4fF7w59gݬY~нc L8~̎3=x>t&/)Fj4Q튘6K/py6sPi&DNق^[yxmՂުd\ҍ"RezPk>M zqǏR~{עU[Vjh2;б}v/H {|Rogj|'Vi:uBIl_ S/k,*v^): deyRoܔ^{LH byl=9_I-'O"[zoq^wbWfz5HLUAfx,|>6= eS6OLqr2B! &a!ַ]xu'>ޛ#5v@fU: w AGr9>$*"ECZ(}S[`إ_4͸#ѥ(AOM Z=5At$֨8KLҽc̥7>[9j`^HOd d&d$!.y`ٷ[ĊaFd8)cם~oc LF +0)Fvr⼎kˮ&[wG_^_&J_T>H$䢤䌤d?K\bkWaU-pPזusَsxUڰu\)y?PY\'9934ٔR\ěIeuࡸڃ׿ҙQkVWpI-ERMa=wMȮ~]-$IQAi4ͤ}T35Ordrxu+/=+xѽ=ִI1,zM:'\Ms_Ϫ-"KOEIt[L^[3y]GFqHj-IgЇY{Jɛc6/пCS;Ѹ+.AN,O8~o1f ^LnиTt2tl:,I*Ig8G%3Jl"zO_6uP'$BR[\8آBvw-ë<4D<"_BwM$ϩԢPR 4VH&ӻCVƒlj1T"45jcuL*eX.di2ZKe_Ā[ɄՐ񖬤R$h6p{@yzu8!,oھϸ>GմdbS\䢃7MIXq TW ~iy䮺F>HmY%=ȼ_~F'D[ UG~'X?^Zl=7ʁf^!v zqih˚tm 3wڭ<%﫱X,Op\:ڷ7νu //ͤA4Xo݀znzAc˦ԢAMrVh#>1ڇ0,`_ 8C2B3v1` #^;vxx>,ORV/niVXJfwWvptX_hQ|~~AtYj~87XÎl_:uMkiմuAϦ,z8"=|W?>y`Ox2.֭ZֽBᵇ5o >tpϛJ3̗5R+4_09`HLܼw2k-/4h+Rn?u3gWPbN.@Xvuosfu¹s/v>s;Bg4,W(fT̢d @f9O;=՛R,[|1kuF[kKY2_yw`nۂqb9v?})޺(|g/\~HuC1̐*D 21.>UL9}^2=Z#E>v3wWnÙ|Ed0eE_mU[z:BLWM@'(hc: 8FOss枂_˶ͫNEںHެӾɋ\u_2,' 6l%~Qp)̌8h!=nƂ[@_2C v2\Wy!+sLz9v.WoI.TO^={ۧBծ{=?b_b޵(nS ׿pSi ;bEw=x 8(sO睻+sY*l^P^8`ϟ;93j9lSY'+B+7Pͫhо .Y7zv.Ԕ*hno xw9 [6@#~MxpG1|jjY6칗 ׵gBQꨵPB6=P# .kO'kEڲdA2b4 `_zCNp*0ĥ!.Ve3dfA~o71tn)X>qFO8on& T%Ðϵ[p [Wٲo@X\j'n(/俘E%[AgfdWnj+fDپ|p'apR~̼x6j4iBzTc+8'Miٺ\P( b!tܟs~.Q[frO*4I{˰dDaMMW_}9\g!|xfAFQ\' ZHܒG=n+7w4ni_N)ɱ,H&[%1߬pJAN纹t>Ѻ3tr g?ӷ܋l6` }`>!n{Xqed:sVϜ:}J576YřX$]#5 L";=kK8|77%@G05/W4kx ō..BEwR8sefgk9\=G6d1Ȣ˅k[(㙫7.K;\[v_ v(j)Iy<ڂd qsv8\)޸+6ǧPD%$TK,zԩܜ,<,y _seggDٱ|HERع_K~* 7QU|mt1oɚM[weBҭ9,Rr$;.ahR{y;PDʕ(T\6MnZ1E+L̙SKLMg(9B*=W6o}* HQA^9YYe>{%;7 sNY3g2qODf?H*.i SiOsυ!% 4![j㎽ND%B "- 8s/t@/p@wo^:௪ֿɋ̺x@KbgdRo 8<<<g]o.Yy19Lgy{YY9%RLVs uO]/a-sg #X}>x2Ndchc_w>}bPj}ׁwQ]LW9GV@9ۼ#ɲ5s6L#z_kl8\"PUge0QݱK898n\Y/4!Sg/\aLuIH($!W1eɃJt:BP}}9Gw F?g-3 q $')0<;}ŗc:9A1de0w'>^ p;jY9+|:קgK ˜i&¿hml D߂]E޹H =uу\Zj:#/o}ʁC9j3Вi˞'B9B<&-K˩t Wc7Y6e@O߹C,x7qٗN'@5js>v}Y444͚%Kݒ,ΰ|ճ|>|x:RSqڄjSmQoV'N^t}/o[4DSMQ8ZCq$3J$lY ut8 M&U؝9[8Ln]|Nc I]PEH[8m'L٥'n02~1݄H d| ~LQV՜/ ĽMm-M&W7\H9PuRQ-v=HRpڕLAb6 `zt @\|iŹ{!/ 'J:xl3WL8jxso!gh)f.p&L%.IP@:uX?/d:$(zVM9 ZYȏ ,#f9,⏁T+L>/΂.$UakCW?OEʥs1'8^/@=ԛJR{1J³`M`/$26E"X "!3o-~k)gҙLA"pS$Sst%R%yKlaܰPVPQ钍޷ Q!fVsuUr#D 戜̠qRԼ Zaa/UX)X, H ~#7KwXⅼtu^&.DaC\(qk= CҬ~({eʻvD˅(PɅ?4GE2)E‚3iGwHu(߹jnk 6ekz4ފi1&n03^zOa0cJAhNBFNs:_VD|khM(_b`I,MC:9 {,^^G/g\jQ,=&;˔Qt&gS]mGP:@\%Ӏ"eΟ=Xe^=nupCFOdX%T_A{5/%% VH|#ρe8S@j ²8*dO,n{ p>3x3HlO^b;%5l74%\ 66 pfN+0`Bh ]j,$;+)ػ#K\[@]\84CNt}8:>s9߽If|Xi`+/1m &>UDӀPMp%]R<9F1g > [zf&/,J9/xΕ3IAӵIĪMw~@OJ*[ jf)]/߂̵pt:̶dJj 3t+\)Ɨi۷Q~`^Ao˦~FFVE?h#]}[wݺᅲUB E/'M`o~.编/Cse!XN=vʳ`@[Z-]'_(ë<oܹ{5Ԏ r.ϛ^>Hϋ- Pq'8C@dm^8ZqYJkLK2($!ëy4ޚL醴^$sy6FUu.puu~b]e@4xڙә f)3˃wK̇ 1|K(;{:AY 6%zgO)0QS#QuUp޺"\_+`§NMH҂11Η]diV8[ɲѧ7AYO2eF3.Q\{Ea)iЌ_B}]4RFhW3kߨ 7u VIڪVsbB4,t 5V(Ro׆V5c#7QϕVT 3:,Q1.`oٹZӮw_g@:91PvBKا)eI}fH-B6WϥK6TRu%[gcTX,!="ΩC;~ uzDRU`GIS:_ǩO`@ݼ*#!à6 M;wUBaA#/wb̖K}@] ܡIHT2xЯаxtY27 пv] ْT;V4Z n瞌9+,E-U2~ χScVߺLJx:''7!i,q«׮^M Z1Os:h R֕JL̘_/dRJSؘܾ2c_֬6}oC\@~ KWsE=>8P!z'4U67X'rJYsvู&]} n[e@P j)z_A0̥"N#&؋wF͊6{ڸmCHHS`ϲDt2!+'!JT9k)Ĝr@43t U ?nK<NT1u`c>V0| #,S)TcJT lx̘5x*n\p;I]KT@U|QM7!+slu"rޭk7=]pHXCg!Q--YOjWpr!{Gw9J{Wv1sn\/"J{XSMپM>l vڽ'n^y8|"d18䔔Ԓ%/d ^pbeGڣWx {|By[ՒX}W̙3mn@`!L ߺ T HILSX$ O;.hOX~0,kqSTUyxtk=3ݻ1mh ڸ}P[k)$.2'z Svf;Irq6I}5 4U#9J@2WD!Գ맥hzSպw2bFzfeOiĔx9\ShNjʙ#]dc.cnN (\2s*5 SCrJ ѥ]i=lK ۲s; pNTQN˙IE'~H⥬Cjkx/߫Ө.\z@%zGHISpd)8˿?ıǧKudXOG7 aZ#A~}:ɜf*~M1$j 8.#!g˒/uaɿt҅|WHpV 4,S֫W_at*^\j:{y b¢`'"3 =Cl 8jREDdЄ̔hԧK./ \jǾ#Qz9ޜh'hƄĢM ' ɤcX{ZJpOhj.MبHLHG.wՃ5φd?P*ѯɻ'$%>uJ\mgp^kvgqrB51 sN=* (7ĥO^5@([.'Om}L8hiRS+))!Y|QBQ x͂"SDZb?%sTv06ؖzŝ͇Mqň(q1),ꨅjRl؉0w^Uiھk?׽~lSc,XN$ H\BNΘ! ^u+Yq⟣V?iEA^ :I'v& ֮Nç-v06gS&~}gug+#=)oҙiFj:J,<+@lQ܏uGݷlnN5<`jdSies`۪;nҌRz$D Q!}$h5k)G~( <.%G؟ccQJbdAKЊ+7yph%,\,v`V[S[Sc& TVj\ڶu#ᴫTϡ~Bpxݑ~_`P9)!*ޭk-pxj_V/%^BxcƉ7'$ƙL(؇g`Ӻ8;]^6z֊-Kř\fŠ"F.␄?<{(@6l "cLsl8ORã~pYϛY 6P^vߝHψM2&&JޓWzUi0E B$&[ѣ%nj4 O&=;SK,U\jǡ@vE0by;# r+-ިDj-d=ś؏A[|=#,J3"<G6F~QKN :Ř[b0vp0Zv۝ǃ#hQ0>"#"GEDFGxbF*(bD[Tf@sU-,}JI͊–cjmBFm:vwłup@D2~#"1G59HK 7[v:2[șQdFǛFux8VVZO0sx6sSքڹvNKcz L,ZF[[?|F&/#I j:(2hCtc)нwhYfuz|}ը6*A:}ԪNc=PJIb aB !OBBCK?RC5pL| x4;-2ܥ{֤I5ʋ;XI8Ǟaoڶc7Wm{g7C&N#VN<eCb+"~'5h 3 Q!'۾nn aad^e2&SYq08=h*׬߼XJu؟e[%,žЧ ?";Kć#xm_hH41TT'te00UQ;&7EޟmB=/!?1!Pc)38`N =ǺEGۊ U週 0HqU2 H{^xOb0 =b~lEjB%~mzlX5)*,^V]V(A/ _K֨Ө$b*V%qpQHq0 nF*,i 1!fDqlR 7aYSk$\½!|L#}x}|` h'c myc' "xnwKVCV UzrZYݞf߲|e K6~8xAEfyPIP?8Ȅfg& $/lR,(2$MKf؛Db2 kh'P%tмkESC>3p̌%MS Fp *`; נ⠟Q i aP%ĺѡGz@Mn`;_<`4[]xgӆ"T  AZmi6z~[ES(Gm"Lj0|b2hnKSiLbl[XBk&m-L5ATRH@h*QT3N޼LJ㼚B#D s<4T~1P*EU+>&-3hۼA-E&r1,EH 9 ZO_ypJD,3hHI`1C-ET5(8XP(i(;Dܼjz [:5ٖ,[F[cg垡"mqg&zTq/~qJ*σ>B FWexIB Ãzm-=:Ek$ʒYJנ |$:QM9"XOba.c28aF V^W޿J ?u#M{]9TR ~o^S$YWЉs!E@GV2|r'A XeTգD8v{s& jV\A8u) lO?#^f)Ʉh$:c2:c)yT񅶸Vxh1z޾anN ۰VV1=2|_fVy~b̻(kʔye lF. 0gc*]bGK$CCc>{wm۸zl u@6n31NIV]REimreũI֮T|~d 4i $f#rIiXr欩a!y !ŀz2+U[+Qqbu,o۾Kc9A}M҆˾~TJZ|Y!@S_Y5ی6$﨏'5@ ݫaa h~YؚCg![ԫ|\bO×>g4قLr;| bjJCx8o}}E(5B*&U6pE t[.9q aGt3+Y7Ͷi)V(iGB i Vnܶ{ C/YTIWs"ZRhD e,Xc@LcYPP֕ AwҳPB`G}Rx!#pGȎ]hl\۪ ^ȥ {%ʲ!Jhf4 ?~/մIql)lņ6Tmu>५~CQCݰg03B'WxF_V:9ނ:rݜdKjL1sF$dωmeq'Cv_xI?ܧWPy7_u`m0 K:K^ūua[2vldZC}O(^_pLqp_x􉣆^v-ׇ[O2m0DZ%!kڲm~~Ϙb:{Ps2`{q#2u𪃥胛$gy3`w3lݲih!u2*4VucthXer:ppL@xʊALxBmoue' rVMAS3uX(~dj2 Wcmd0SGzԐ>GW.[y;l,eup),_x[ > bY⎪u/¶%+׬辅={Cav_u{?TLwjqKϞ9u 4 ~!-xK; 0XNV+V[~&[ء4{vN6۶0ؓV,] 5Ǐ1tl6Pa*SNN [^CVw.Zq*QE+| ,Њt>L\Jy|A9|aI/]ۿQgaʁτL&Os>X9g1]m1s{tܩb ehӀ7!if-nҲMbv^j=; Sd摦QH}xχ `ǟ>!oxjSq+>]U0[Nw!O:žkOb'ַyޜ5޵˧?Î $-[4kb>/"zL5$͡jb|cGj4}_x,ۿv;,!Ʉs TILvϒ*'V4ڵ ԧhIf[ꭊ[[i-hN1IF Ake YAԴůPATJ^Q, a'@Ҫ%yHr-TZXJ1ur ;V +U\E+Vj ee"djO5$m^B_B+Y\\TLe$y vh'[ )dz%ApE+2ސxIW`˟4p!TLX&_>~y,߯nFS?xu| ˢ7?qq ̇;`b<873||{a.S i#[Ο-]qa'O>O .[!3kL p,m̋K==&~f{?"=yt je-S!q?| _Sy"5:o r/hĖZh VU&,sE[H]/@%t›TR7R?@")7vUG|N~2 ضoEa}=( Esv7*.ݣ'>x0w\h %F3-[4m\F}fBMpUΧ(1ܑAseƗsU֬%ݰQw ׭]j%S'O$}gB ׳[!!coT?ټ:l7~jP[6.$yoPL^V=ԩ\EK%i3~{{hx{ W\p~̚6yHOݜzjRP[LS+T_B%Bd kF,!ྕo%9MC'5ɷ(Q #%}ú+,=}ı# 'hߦY70|튈ʍ@Sr*5nk'щۜE}$E80ZFꄀ좓b~K|5kڤу{|~;ҳU*ժ K~m3uH%P+m;uwxuvݦmI_l8WW+8*]jZ )1@?Ivo۴n53>{'v_ (u3!5il<GߤtƖΥazŪzIsҧUЗꎦ(kq=cǖkͥ8Bڼ KJe&8*ֈVbGovwwLS9VoI]ć rGB"Rsjq.z0=;W-3uˆ?߂_o性B Lÿ T %dְ"-Or8bڸ}a)ZPYB%[ 0إ2]dkj˰\g!ek W|{ݾa⯦O1й5sc ޾oDz6+ލƤm(1}M.Ȏ?} U{ &.%==尞!C)Ld/ 4)u8r`Mk(1stЮmõr TID {\ھG]џg4Hܼ 5Uy:5?]@`@ v;SLiXHq)m^1áwڶnP 'oAU*H.H> [y; V=}_j}|9|ӽ{u/>ޔl=vZ1UE6g_Us.۷no2l`7uسu]{<xung!silӻce9uչcMXyn2"p7R\ğOr*s[|bc%V ֬f;egWՖK`Hw tww+ v`ūDo7?y%ˑ)ڑɲ`ylTG0HCДw;ؾAzdks~}+3ج# X NçG9\C*Qo1yi7]:slWC<@ZS']tc6sNTdj1ʦ'/\4jU]P}gz [ށĵ,y敳%ݭ?WEA @Ԧ#W̘PUH){7?,_swD0o.gCkڴ98WAE }u 2qrÕݛn"kNQ醕Q~!d ޙ9)ؔόj#¹q3Q8}׷Oe4z큧w]het=;wڥ` OCzr};hԍ+#`:Ͷn1H9.a~ k;q.yCmo~+١=VZ]Z `|=<|j4"w n\:F#\Ł3iތK,Ďod\p;wtuvh7w;٥wuX:MlCR.,キc+7߾rr$3i5׺ Gc p0 ̕9rM~[N*o~ՋAJh㸵j-Fnth#i(m֘6v:ʛ߮.YڷH s۳Ō2GOs]0MFǭٚ]޲un݁d*wŬ~.R-GcӀkNJ |KPgHyM*+w. "Ec+Ζ$RKEY%jkvEKAzΓ>nb=!QF;{:N'< /[e'0ZVBt[{Q,.o}2p7. k*2k,3ODMT%nm(x3\AGVaJ@BqPܱ=yurxmcTZf tz][k, W\'ܸtp\` s6 &if<"ʘ"E84e5>$U*/]x1DŮMZ=_+ -0(ٍGqҝK18?y[3w7emer|B6:\N kΡ3fw\s3D  ՀפlV Hή#]0Z)]L.xJ/E~J|U2Q@Ӟp鋝m&wSMGo{nx8$ bd1Jx{RNfL+LTS% l][0]ξTHQ V 23M mZ[83cf'a3Wӻ+rIsUkAL(my;o}R;r.b^Rg{{IHC ;GL8wzs| i4/cD#&wpgSiƦAދTb4 D^J`Xz^ڐZ;z۾T.?}~L$B=eBq %3Wǒ 49[om OR]Ųl{4 ̀9sπ3,nHpepC]+ϰb`<m TZW(/1{y$nu7 1elE!b8] . 3ȾKs J:deWAu-.' Ɍ=BLIKQ{] ;WΞ@\D xxwq|3!)&LIB4Em9[. NCDN3  s(B>z ]s'Fzm*2sWؔ߿ M{Zѥ kk!;ZC{^q'x~Y[Gu 3ԡa![B 0%HIR !o%t'A?RIH-jHB.XʷpI <u9+SxQj=kgk'6</JۀM &*E} A_*`a -D58 Nkp0b7f 8 AuyVeG8:@HPX̪Mol qL}bGI GQBu,.C:v9 AV73(FNɆ E _18.CgpxQ 92:ASkfZBSՠL¼oG?0uc| YQ|Д/>ԁnQ+۔][CxΕ=l%@ 5 *;Emo{ʙ#{ۖ yOn먼m2Ew_I,mޅ{o2QXȽ>T3EomH]eݠ[~ԡ2"(xFJ(=pK{˽@bn@JIO"Y+(2 %I<\ aζ]Ðw%rx2gJs篟QSNl?Qϫ{#;jMlGb ])q9wM!$.cz}vɢtE%FuYs)!^ oo>B|w-Jq7wgre^tﮞM|1> @Ղ튦ghM:O~vM"o˪_8ߎ^C=8 YT Gݖ_=pA o66]HB#q\i@ |1bdZ"+wn\FjQ3.OJm|$Iil53OJEiBuY^ ZXqJas-oqx2Cn'w\3+;wnK015j{kG0p4\xQ*nCAZ`n!LDWW1ؕ"a[> +oy"(YoX°o y#u=o.7. bK0|S y2 wBنmb:b{76Fѡ?#XwDAL wPHRƌ dR%Z%=H0B_rXіf)H@ֲ^ t&aBiV8͢X会ν q1-sB:|*\f vWeT?ggfцNG43TLA1m6GM/s/r š=TfȒ]n#(o?}]m5>m0<*vŪ'v@d&c =SEO|KƪF+XRJZx˗/-|~ KNSrEdy\t&H] Σr4ea. eӋz9_sSGTZ#m3T)VF*"ědݡknT~Ui&ԍ :JSCn)؞,ԛ>u^~Ҫ+:\x ;; p-_rMB"u%ﮪi C6udbWu ţ.q'I|m8V8k)Gт<el֦DU4WӔ ~ )t;/J&e@@q]3"䖄88myȑs)$LNdeb,2;?$LD3<#T0y/ۘQֺK`"O b_&C!>aTRG7oMyŨ ih?Tʵ2عSBCϜ:6Pxys,!hzKm7DZ ~{gN>Qv!zX8< 4QKƂf5,rVF/+`+Nx<}h؜UI|OVd+0ڨ,yÒ06(8$DJҸ6oݾ#@/*MpcVgNJeǦtJ r&U ZL OxJ@Pˬ/c-+/*'A`8wȞ6isin0Xl۟MkUGg [L:?XHe"RDV9Vq՘ػ9>w1 sGн6-rރ._J8@ݢm $ Čpsg{0$3KJt"TaK}abW.jrK7D ]&9n'׸<6Ȣ`ӃFk}6Uhz,ĄS3:HA!ᰉ:fI[lS9jZP: 7Fm}yɫc憿XPO" ]Pnۤbm\@lSKfaQ ӪuX=2΁}Y_W  2){ZJ tTHuH@ꢦ(HKX@chYp.LxvԤ `Xs6%|ҵ) XS=n[ *( oLvxCe @9>h"~ zRg~Ҫ);,@N,DkZeQjدq +DM`:e@߷w(E1Ivx~xj T$ 6BWec W__J!$z;sA3}h/TϪ.7G@ԕoT{{*)ə F]Vܑ9*n w:Kp30Gjp5:H:296 0 7uਛU[U"8VMwp8Xw>wYCC8C3Uc2(8xz|-  ^ s?WHoUr 2Й3B DR {N9]מvSR젝b#]P (*AVdPP~(;q Vӧ` f-D?}NO@)QgLwV@U(J> / ktA ,=Sd O*6"T?l ?oA ɔ:h |R'Ҟ㉚@yCGfSD ,k vHNhwoV kJFUc^',J@ rFr(i9 .wF$%lنG^M4m#$P,ĊXL"@RK( j;v>DCnjGWs1)h->CDn(\*,W\fq%vMp =VϤ`u˯tzr`ኂk \ӕx`_ THn0 9¦J bg\-mMIg`TTd3p}{z[ZKLOs Y-*pBujjsS#B$ tԵl#0o (tUHf25[> S^ja`J$DF(Z|O$VE۶%A+r)M]6'HMf؞ C +d*c B鉫^͏ٴ-k5m5x?*+Ib۵XĞG1p*$ё[^ Zu+dyE>d ޒYZ.ic9Arڂ~ZiTHڥ ؀h7A01D .,T(8[<>x󌦆I}1o:9`Cnk@]"i^`#Ŭ (14 Տ%C BbCV]:d ؋ZUhhShzCjM:- U=4DU8'>?45qJ)2i\d̜dEVPO{`ܕfg mw$8̳]I _#s&C7c9rąy\ߔ[VKc.@j+9 HQ6)lˣC܃i,D70&—f;%N -ByWǒim,9bņ9ڜ\+YdI(TCʒD38PjةuzJ *&Q.m 3QXJ+F^6ԆwŊ6_6ahch͇ Hc Q ԮQ#pQ*jiIkVd;$f\+ Elhpѓbגgمp_Wd*!Ωƛ%_,~=d%4?3eS|jfx̞b[2 "YQRr6XSUhg!REEA\,cY$0:{0p'8:v% YX<&cj9\tYHbnX_RV\LNv5ؗ?ԦzX'SŒMmQa#ip̐|\"򽴸 ;=yӺU+cQj,9p~MARhKhXnCMd)^(ߡozYL}o$M\_)=]S6_kaP86`"OkHΓpbNΧF=Jj4lpZN\$'6"gffIWFoNA`ƶĥUهEzH+3l*̥!>5)DPtm;˪&HA( EÅ!?GyޑFKX:եq]bdP+C ssL#IDG~^8\d)mhG!DKmDIaF[ʅJ7Hz,|;Qq?LyZ(a Å55Hnjm㗆x- N9H|;{I6)-81 e5jEp+f:~8WJLs4A4cJXwz:-Zl_Ôv-#z Qt**f޻q/~g[̈i:|ރU |N `8>%m9ТM4 UfP6 ;u3G]c8}"~Ӷ|Tͨ5+B^k #n Âa~`"j9@甖 eP XB[ҭv 4rarX QE:` r0C01Q:  MkM?L"xἇc|eVQ(,q݊P -!lG66:+N:%O!;D9rmka+DyG^ێIyR`̓xotfH'N4ߌaOUv7r3SԖPm'ӁgiҦOY$< >A9.EOgǍmpmlg} (Iu5 li|QyPLC?(X̱azyBQn$?̪͜[7+^n TdںKt'!)K)rf$9l >6\:reǜ),-M4f inN4 %8ePؑI{=p=D9Jnɛ1)ŦsT [̐ivN4wfPeq$HED~F[9) qr#(9VPҙz;bIgh&/UuUVmd-۱RapXwQ4-58ǷP~9qch!Ueۆ<ȑu2U̓FDܪ [=r\ l)|zdHݲa[*#7TQ̓gO<sgjh8RtH MlX˰ 8ȸ8Gϗqu; jyF󹉯{0 B:'v*I;{;j-9VA܈t9u}?rs2Ҷ''mZR7efΈɬX(*wwb Wܼ\y:0w J9(_v6x x֮trs`|IS# 촙rJ1+@ sf'ko#aJcptF$Y`gdFNlcБ`[ӸtSa'Bh#́F[zRu*'3ц|ؔސb,T2dꇞepSsrs!0b/geeã,F<c`D=CI8,5O&}E/[D\8-YzQ3ܑ-)avҖOF}r# *BKoxss~\Z'7cZ mj"9Zr42=75&*vP$D,P TsorAOxI@gZl {I:jBJvӳ,H}ԱsF "v2WH;J0?'0ɒpKet9M9ۀ֫|#ےef-CC| E mm tfdȔ5ߋN2<P==P֤D ΃@#xOd/$`0q9N1 $ɔ$m̈́ʃV.H ؕxdRPj-FJ45I3}1GK{_%x:6d7Y"κ2j#6Ja>YuvW~`IM#sҧȁZ(f^mG$=ܢԑM2tDxX8ӿgeƒrԞ6Kfjd#M޴a v :c"晬x,eIY9v0,֑`b#$DMk5!`[?jl*=bcbc.xA,O 33!2= `{Tb1z;g,b_F.ij$1[Vyzrփ]."\x)|L}% 0[瞬b {l|1FBgRd Hb E>ތcbE21]JM< 3w%1p7<0,Pޣ>P OcᾘÓi*Lj1PvLD_4Z0%y. p5OCwupn,4$}f8(|9tPZ7}$G4{z6:: &6ن`+NϘf1oS/ʙ>fc4ŧMxɲC92ZLc =Lttww0tlݏ>e| c4>Pu5Laeݣ3<Hf_fbtlQ~chaOOո!d_&+{zk2M{WKDatyumLL|LdmM]MdLLmLd|uq$vUV0FrU2+g5EyL>tbGYOYےG>M{ۣwW~׷|<{>ᔷުGx7ϝNx'@^>{_-ɝ.|O΂ %=;//{XJSCWV8;3 O }{[kxZ;~x- \-$7Dɾ˟dze?>~뾳Cѡv ^ޗGp=L9-wfu_3ov/<~hOwf9淴_mϟZVM<|6~?>kf-}k%_M'zO=rV?rug 'ĻV_]t#lB:)FzB $H!B BBHB ܋dUe;ߝϽWYe+n>Y'wm}Pv}Kw>ē󝽏=Տ0&X|6eֵy_3ǧ߮lwS3kn[讏X 9c7Fn{t#}C/KC{>sv3wzӋ{sFO*l9# Yk#;~'oG/VQ9p4크YZG~}o¿nk|[^jz=~on۷~鳥E491Ƨg>Ly/1]cFMHğɶ&ԏ.x߹ˍ>G}}±~#'6gsi}=닟]lMx`ɉɏ{رkͷ?tu~ݛ_bN%Gnog?a~7np+mn:4+OXc }{S@شW?4:O4ul }꧿_xyd ?|?f>yG?7s=Kq/3wάo}_{QK~?^xdz}oj:_ޞTO>]|ha[nL欑kny?u7rdmE~˙5>мP\Éռ׼i&\9s?f\D=G6QzvǮro;P[YvJH|g>tW:"񝻓Zo}8ŷzB׸n""uѐo #?~oıU\eoL;D3[911]VnFӈqxca[AaM1 6UMZ+0Oxhhb {OjL2U1J';~~߳{1w>iZ%r {0<$C2 (c҅)a:w3*nӍTTȷs7>?`w⺇=߸php'F\_%MPVBq]ye$c(#{l6?{'nfއ9nP JG]@l;k+}S-jaP(Ip+4 %zxnO=f=оNڄ2݂wn`-翰IBW3jjjg#k8WBOG4x< FLԌKXHvCO@-e}׃~9M#s\q1u߇8 XquS,R~9r$2ۧ )Jn)u7rq?V WYPZܥ7O/]nWvy^[W* TW*hY47u8w !ù2:){pmfEeB+?Xrb; * n#ч{%)M#&)2:PVe<N?fqZ6^wZر:VpDr yK 'Ɂ 8wI<"ΰ*Peթ.s֩vR*XpX722$fitw،f8 Ae@-)m 7s+O9vӎEƸWcN "8c @SU%e76;xUAcܡ(pɹ;2x52AdnyD5ܶl6,5@* ڔ.^1PoI]IcUq>"2pI(ajt< Ke"9.,e_ٮUR<5m5OѩOlMi]W&,sv4u{#erT` `C@eYyhr=ЅɈ̋r+ve?Fm ühO\>"6LnS='/--UPͻ5Z;)R(=/zIZ Dޮ@fyUnզ=`oϏZm*L7݉@Mbl 2^9C /ӓ#r?wR4.Q"QK`fSOtl5{L &]6j~s`dYjA^+jm @ԨNن ӗV}2JdB:ØY3vjvod6۸FyǠ|G.* 1K<tP+6I e,Kb@$FO`$ÝZeǒjo4gTm]s%CHb];UV5?TyA8U6J%b1W-_'SQ0cy Op fkI{ȋ]Dt/ xȠi(yYBT1:o$qw(2֪ʲ`Cdp?8IZFB$ޥG+C]V6_ŴIrz{<N0஀0£3/ʗv*bX#RK<Ŋʗ3yfV y@[2H ab&7*jKYAhV_0YT-g@|6qp>D oyH;h%qOTZ@:#勪Zʹe& OP7Al%n[X ÓrRƳrҽ2n7OkRm95^&+)8{cp1-OMJ 0U_GT)ܙ(B'4FZ\5vo[,fO&iI|b! m>,zNC:й iXPy"`!唳GG6UiX>9Z@dxo9!:?:d 9,,,bI45, %NΜёm>OÒ|Nu:XW5V]IWJ3G})^InEP'a V`J{ZvLxܶ^LlcgbP &k jl44g>7 Nǻ^ 1d|kL`uB;74"[9>JP7}(aVҎ&ƠL 5:d4 n?{ i1K8DQ`%ԟn*h0u>Fv@i<*7 i \ jը!-.➔O& ^ZխkEvkQ- zGCHʮyf 6M7YiUKv٬% Eߪm}iIE=!ߜ|ymo7  Q(t ABRlz`+Q6 wۓݼ$# F#UFSrKFlrdm4*ܞI@(԰Ih%ـ6G6K%Q0U7jKR H2׉T?ϕO\a, h"dZ>~1Gcqị39D֛߫5ܯD6D4thZqC͹l訜.6Nh2Z/E09AM#mAt{\]崆vXDކBpa Q.cFI,95foBrez.ǠĦ$H{z^UUZbA\l'4[~[ j]E3,q7{KV)q LMAm !:bx1EI!@Tm i;`!xU&4Nz4^mH c<0';fLM t6r̄+1(G-5b'Ah, H}YOP(@ % %h")b Ze66Mp(B1[jrC,N3@HR*t4|EBR$Eg\,8gP-"jBO q,`1t[,\hro rvlwW/+bQ6ro]HH< UPBot1gwOqm 0Z@gv.? Q(L} M֋I.$MIs'bi$*d$7 ~q's?I/6$^Fw4^Q pnDd#cw,ψ \\Z(L쳁Ǹ 6.@l?9$l rnyDzmism 5wA` :(kkPYI tFH,Aw)]"? qj D٬\JW+(AS@YΦ6\Qi.F=&w(p6r !{ Dc6'k~-$Ku\uFPf zDl-8XEy4BЭ!\0]jŢs.?,߈AFs,8S_b>PG*/jTQqleYeX ƒ32{[a10\SB``2HdR:g%jcBDR #a cT@Z`3'CQh%ìQb|c8X$l8ȅ 6 xubnI^cycjnPg\. a4t jP\2GX-4 2cP`xz#iqt Zԧ}ǒ1^o4Gm`\! Ja [ƼٰW!2Im 0nñ8QeBev]:\D]\^ v?r(3hL" L|x`lg-ZC]_+bu Q@ h.h^0YÆuM!KX4T.7 x0 9H7X!rr-nUn 6?4\\[|0a@͑&3-B39$`P)Lc4 h4nڨ` “SjRԺ /Ɍ7Iժ԰6 #e,0 I k`?@3d^/m wz Ilm8^,WMД 5FPFԕ! 0S:PB$TIl. tcmzFv(hAٖ2J0PpeR~<.7l! Ƃ:&9E>ڶ 't\s?B{haxX{xS5APzU)} "^N0*{UHŬ@lX'GB6G*FaQO t\F[ {YƕMr-{ ՚1\.\Fh"f#qdnKA`Bl D86 ؟Efe JwQbH؞ʹ {%K,sRN+p˜?jZ_^Z\ N /mq,;mFa7]F gr<25aDD b/vd;n>XоOþq b ђ0AxM:?ZɅC7րA{D.60-&.`̺QLB}RWLd `$!a$yאaX"ӱQy T,vݘW~ysk7(]\aA[+D `CF`*dYML͍oF!Hb/esPb)ƨՍfSQ6Fԍ$K;vV@ a8>9ZE9\ 2l2ef17!mSThP"daKL XLG .2>CtkP# a>T|/("1>IQxcH b}fUeGuFtb:ZBb2th+Ia6g [uTE!Ƕ$MR,r?ہb:Jf1*E~)hbynFřC g+"XL&;膗xa \UpA[GNL L yg%n-|GE:0y Ib5SБLQ1ZFb$ į%59 aCV4BR1k+ d j$ ,%VU%#z DKx}$搬`&h05R&v="%Ն>!輎DnLE̴H.3כg%;_ 0d6y}tDSIՂ rnF։Žib$9爌e3HraU`T.*j2_WV^:6FBej#d"3x w<'A 5=/<"#Dvi9Z~#ƌQP1KqtQh/ -h\XZ4:vVd1%r8n}rJT=!>c"(.hY%EzpbZې 5C)s8|!vZK<εPE^(r@*5t,#F=$h\U;-DIx%N aS*l!4S77dBCr-q](x &{Rx UN8(qꐛKU5/t"~Z>a.f aSRT b|8nvVK|By Әh}Q6tkw<܅ ڐm'`&%υs_^QSQU"jk>ul_3|xMvY}|T7k]톛ERȷ~CԹeRxe* WE X._;.LzQN.iCW#WF" ;Ѧ| tHa]N( E<+4uii2q]KeħM(#kƍ MNBxFF |psC$Y+Ė77z|x;f=aW}QFf?M9~A{ϻ ~B[Ŧ#r(`_21s\ڱ}͑,Ч72?9s LRǬ/T9 H d, 3w2Kl'M&%Cl֌6LcHUWqNᅕცGt1X`֜|nr)'/KN6+kG_[]rW|B жP@g$d2A1JL෌0$6lg؎Ֆ'' 60rvy)=W^8aU/X!-E Pw 7G s `)k/S8Q{ޞ[|Y5a5aU^{qrt0 BKxQ_N|B%Lli1qθXvr^ys=[?Nbbȵ+".b9mczNN QA o%6u\K]BFIAL,K=ۛ@l5L^o,{.eEou셭84[VsatgeeD*]]""ۂ-z?utD|:H,,G|-3]ėp9lM`B}Z+=o@5%pH8wX@EԞ ZN"0"(!e2lpGdBFYq11#a0 eL_&™ ( bѦa 1EJ0^<X2R&HjzL( A[IڦV-. E({O7MgXf>1 p)Cl!3/tIp=ä1e&|C[X։dyh:Mg#-&o7Kg76EULiH@jݵrHQB &9 -& K"' M*}}nn.| tMn.φB$-OKleڜ}i %ڤ? z %sPо԰Ֆ4qqV?O3%f9 #6P6uCt\؊5!~\$hǷ.;A$R%eB|]I8`8jDUz!)T}ͦ} źvÁq,؆H)N#ǑFzTM7IJEd;i-utH}iW7O{d-xQp-B~,(;/^mbQ=|V25 H-?ɔNik"D[c払FjqFsQSat#4Tź:>V4c;X1 BawY &wh+J&5`L(>͠Ԡ#։0ZE*=7Z6lP`I+F 6RI/-o Hԥ Yrd #mhq'Qp'l,.$%" #Q{(h7uUTрĵ'*Hˈ'Zf@j<v8~78b";X^-ꁢ~8v4 #_KVcj 1j͝%њtMC}}M).jWTcV-W K BڰN'#HbK 9GHX1.:!F=a 1^8<΅9Paڎ̕<(k䂄&kkpLJ,~Jjc5UR8eRfX o6RD-W׶6顽 *WN[ Zkj IZ1OWUE#eQ+Ya&S&nΪ<4a=myAW叆{C#uYs%/,|"L+J~BEsZX[Ӫ$'0%Wa^-L Xy#m8+yjeϩ\/=d2-y ydhDZ˜JQPOhNol14lcg3O6Qua~AbV>R:WWXm^%' HYRj82=vP`XȊ*ۢޕtXOhΛPW[:r itʚ8E")`G0JKM4A!e؀Y$`TXdXȃըpVBzvs7;0dan" hW[oNʀ 2o5:!p_ A#!KJ!NTYLg7 ˸: =M}4]'Sq3}m.7ks!{ P۰NK[__W ZHʏ _l;Q{Lh+׋ =v$DfVr !؟*ؙ^^()U"A׭ Pp^rD~2 Zpi e~{K[gg7z.uwvaI^V*J(/1aNRx~~L#l .ovt\1UĦpB~X~ ٥OFF?:m4+wmoV ]эvc~&*+Q?P~(fNhhq89)Y X։M٠Kj6\Ryhm~*U޿Uދ^Vh):AݑKT|P*ŷƪrKC{GW7^]̘aZI "J)|]@E-@0t >S!Ma-( T~.J3s؋۬bc"Oc݈H|7"NL_#t7[MK(xwWH-j%VH/6DUk%.x"Uۋ $u +vs;(wğ?xq삱,?A>>uo(&{L('kߓ1D =<Yp '.niq]`NCԐ/OlEV+% 'WRS@ET=$vX_DvE[ |X,VV׭jL/c;4K7\߻A%@g{DD5rbxV/N'}Ӹ:H颓el(>lY[oT_$դ~_m #;RPk'g{X}.l8PZ߉uQzmu}e)Ƹ>N(90rߙ sb*a.IG'}:p:҇C6ؗ!6bQ0S%onk!;"ߗF\D%1ȇG߮9@J噇]FN)zzwC'(db%".RޓQ!t̛q' R`CBk4J*{|qڧ$ $:pD>|sj́Yޜ ~Kې#4nm@^O,91wo@H h7׻S$m gg>z]d]|R`uV:ҎQzv̮YyՎ"O0ԁ,^:ҙvc"fi/J,`"xQ(B:/P7n1)kDH{K@b3odzSvN4:T X'XV0nCs"ٛEQVĶY|qBG/ozOu9;伷-YjRzz pogWCN{%elˢ3 p=t=y\VjC쳪hRf)X[Z1^^Q\=׀l-1.=N1iсٜMh:z@v`6mP,:.Fn93M'-\B0v`f,II ȫV{ޓ] 8 jOjEƛ@|vݐ^:I;0RϾ l67ߒ{ܓImy|E&mr)$\K~=#=u5GbUŞE?gׂγdy g)=gּӚѕ{@\k cIJ= k ~jihޝah^ ]ׯm,x/r6<٪yq4ۙᄍ.uW:w\koE^G_`0F Zub>7ݑi2\X%-y3mD4>BvOt0=*5OM!Bw+j{Uj;Ӧnԩ׸QjI/|glE=39鷅|7X/rP썸VKï꟪o-yuE2 3};][r wӿ˿+Zt3_z~K1cдߚiELԯxl)_S-/e%-#{ /-HqlWIϧ?H)Sl"Zj7ӖӖuCEԲQNQUxލ*izKL{h.? R4T7/7Ǐ98 ?3Qc5_'(KFA-T#ڸ)3!dF;Lc!:*3̩-3hp<2eFeF.eeQgz}ߟ[|1!6ȕMʵˏN@`'z-Ȯ26 BEHKQ#j4Hk"h(!BKȡ#+y#s(udl:Cpl:+SC7B&R^6Hw-@I aAZtHo{ƞJntH6)Zr8_! ᬑcO8n\ j>aug) P#c'jŃ!#IôuUI2T):??>:3"F {|tʈ UIasnV+acW% YFbq#+I>P^/U6eҎ\\{H>Ҵ{t.hqݕ)t#cvUwzFدbضa7P(L\6i?]䉶$#hyWYԉ3gtI ^3;42ڡzH5qvq'bJoS'{Ki@"$\S&EX@o4HVmj-0;3547@޵[kC%JI.)q&"9r]{q1 ߛ"N9,2a#N8fscyV*Aߦmx}ЌM Jod4:Rw"H*:HMعe:qJ$jYDJIjiBǂNPd3ܧcm TLjҎ\jT=ȑXͨHeZH1#2ύdj_̗g\d# n7^ +6&_IkG-rvEJN5J|$nYJFi("7 _ hX5,>ἕI0H4Ļ˧ۍJfBPD 2HwW vopJ}}w?R%m96zMR'2IّVqv&vзȹV_<5@],رw(`@]OlvdPTLKK>;W}sʼnLF`nGVKsvR6P`kƹk,`m(Js{vnVbS jt z4TP]1=߂)vČ3t T}#³r6\kee :w]s+Y̴bתL{ jy%|NP$^R3uN#x[aV5{ppB\\ -ϡrR`+ - ^jvoS:lJŴ*jz?ك݈NOHԆ9`lXMǍ;mn|'~qU`!#&%Mm`TKӻ.'4J_Pd/g@BD˲6aU+*kOYD%z.DX<Be 9ƪ j@@#.=pyeu;MD ki :mXfҚzgc-|W7aѫTe7 elTpD#; AI:lmH #c &kʪLAzOGYc!4.0A<":*9y`H 6vTEecMZ^e|EUuDEjZʼYf14/X2 mh7X4~YpOwڀ!ji)`kn يn(AVՌdAZXc2vc bCJ#Xk KȳucC]5V'Ȫ᝝ [g,Ǭ_ϠZEqb`b]9ķKD%ODa''ي0j)'*Xwj+ T))?r Ys6\OGWL,mʼn[q,Xqi?+/򏒠 2Yh|e<ʸnJv9O]}+E4L ?Db mHL렾nOs/4Jcx eV'ޒ=h& V az7K,ȭc LV'6-bPD§kݜru6h$Bl])ӷ$"HOҭ\z[c;"0VV4󋢈WDeӠ(:2ˠ[Z5,gYH|[Hը(Sȵ:mmsžD[b:Cyv~Q~u`Z ~Q%HgTSkak!sޝKƲ0Ed_+saa<&a:E^;P%-äŬ];,xD;&:b,yTLNvq=CHyiVL%jG|,sW]\"Oq5pVƎʾ٤A\ugFqݡ5;֤*1E'!cdjlA:Zhi+vUuXd1G~*6)Z'A 0mL5,XlW䔺c,pofH"$!/@4V5^ *X;;GU:"-/@o'(loBI 9 |LX^:akemGBy?h&+i}ׂ0Evh1VpVqؠfG i$֓+k d*@A:.a6*S+t#̃lm .febΕKS֚:^nme7U 5,`TxSRyഌo1Юkb+9a`jT~D~f&"s D,brfk̦A,2ƵOA0b02rsQæZ*>Zx,ϳ$[VaU2=kS'M9" 5I .^q)4jx.M0)omXiM=TiظU^N(_\F;_NI Ǭ8 sִj^YoLFf6sPϒiQP,.T,5+ILU Sgn64̣FMjslH܊VfSHu% L\p5UoLk Ϡ!NP? 9Y]giQ%([}KBydvaU,wkv+ʆ΅PvB~Rxr$%>aZ-0rC3>+$.l \fY_ Fz( ᤝr{kvhNjٮMHJ!τ 奴U.gqGL5 ߙ7 p/!sff*q/nh^eexb@ͯHCgiOhkk*sJŒ`L67~k݅5#`J"2ΰdJ 7D:ļHIJuA.ӧ%Yi;NQafgGӜKVWpA'9 D[0nj3h&#y{&L3%i%p्fvy;YbM+:8a%%|8k1U!}>ÂIƌdo`jsvR4Ec \"Գ|8K]gֳBu6ZA}Zx)?t!al ߯G ~x-gl_&20ЫlQ9+75,M.svb%ġ߯?|  )!^ UQ|v4r_q" kJtk 3c^">pؽ ֞4=D*7$'p}k&e;K5D5I;f.cudQo2S;ʨY03%mA:%GEP #~zЪ4;c4͵r?DTn4155ną2;B/pE2 i]Kx۹;e0no`X &q<1>tidtt'i>aa(PtyfVDc xRtrazHN;SFWw7ӭnս GQeA)X\ώ&jX(DM jʔ=:݌audvDo1㻗)s~ȯH֣H D3VR%/_j/̱i})Ui<[xNON$Wi?[dTFyZ*[.ÿ>q_gwD<ʍIqc+G#I{<82YL3V?!Ȼ,QPBy H3}&;" Qfvgl2Wʼ2! 9p(K%*>6հ)22܅"b?{C9œIkWrEZ0̷'KAd*S.8PO&o?1:|lVFF;cfoWP7fP-tM4/>t2LX$Väcvyc6+cPތ5K` E0 4ʡĀ el&vA_)rbڻwa"^6t&i4 LWy]Wmgɗw#n)^ %*GcM2MOf`K ր݅A>,L4{LsQ͢ nދ"E'y]%q1 ekPNo˓/gQBˎѸ+<W&Ъv1ݞ}vfUHp\f/ 9~v6]s;T!>Z>%j]I}HV}D]OҥUa>J҄N4aj[DȁXbR C/$ctiP "qmcEr>oJ?A ճt6 &献b{2B*9zϦ,p}MxY 3jP:<ywQ 홇ML]T"b'l62{PӊQPQAo gL[E/b!]5x$߮gP p ,%' xI(;ZUY!v^@ Ý׊806LzZG8e#>XTѯ͆/R\jIdNP$1).?2d\" pU\K YT `$a蓶Z*(~ Ul?s鸶Q*ᔀ)0;fN4raZJie^T6)ts@ :û~q@"ƋT%J%T%Zhi:HNl M1S%?JnUYPZѵg|Ѩ 6iKx<$105WꧣvP1&XY`5bdPǨrqtXm U.+xp(f&>>ؖpB wԱ-0nø"63YO+1P9KHAGld=Z 6^-b;Дp/}硕ibxM`!ڛ<06=2[ojSui}_ߔ7r*o,n8à bzopm OQnW~;.XL}z؃y0ٓ2 oUXl \fGjZxB:q15qU^aPŬTEm m]Xk#%zF*RɨenEredoif0]:q.Uxҗ76-mnl-=i NNNaxWUwN! &Rx!mi^eU{T"i+.qF\Q݌nhXn)YA0Fo8-k ?n5߆g xN-EK<խ,Wm47*rRB :E85Ob?t0cs D`l T/ mF"t%tMC6|VS5|SG!$zg [[h+y (xgJ[P1Y]Εr9h%x󂨜]9V75:,8mύK+-T eA*aQoCB&A+6mo󪮻Byn´v`bq;.=|3YiԴV!X}G]eR8oܨbIǵMRT,6\/>aѷ}ж/Phn]?nvZe)⫎*~+p'DTg5*p&^?SxH\c;CK%}8c;ĬޮWsK}߳8Ccc='ꨣTØyQE"W*khų$< g6L=wb-2\~G s 1+k žIT7w>rsBuf",^ -zY)|}mme)!6ָ]w>͚# ѓj;t5h}[ې*etıM9Q2 1e'^ԩ`CSKAtT0SǚF'WS<^G]OjiRRm36Z"ÆQU( tb= P Qn +׀+ I6t`yW51UT>8^R|o;Fчi=| [ Kr}JuH c,jm/<%04U(7h5bFΫt⤟rܕNRG<͙?Uqh5M͛Tӊ!Y, &xrTQalQYbN|N/@,G2Ƨ/oJ0_M7>:442u.l1W*WL y=!"69B Ravd*cMh#M4w8x_B}D_"E9,"N""t(" PJ M wZ~9?q<oȥ_:!k2ߗٌJׂG3Y+#lCL "C4fn>TMϽ\?ת6K 9^VLUmxZ&*QŜ͂4[:P&g`P?ת%NY|ŋ/Ag6P.7#[ц5ewZMjoe)\?۪|}^y^d.Qc,&vLaީapWI͗$5dŵ1tiAsŪ~UE8:+0~(="q<܎#+h;='2^[ompWyW'\H19"}Flh'S(hCǃO&oZ /~{eɓ9=d++Ϊ5<5pԡ+᮲s\sB$[̸Jnb# f~NqJc ʩ=$ (1F$FrܟHm,mG:;8wfNā=CuR0>!ә9G>&ͱ;t+jcxۀvu3 F"tQeSeY-|ZiV𷑌P gג>LF%!3 PT~&Nѿ"i'NMpvi~7 |~ 3HUtk!ьPFJۘ9i$D] +Q У@ִ i$`C!D''YQ iרT|,bN!5Q%ېvja`J&CY#ij|Q hWDDnƹEӈ:p pv%k(55"4ѷL5k'4ëh'07v,k] mHXJñ<4itp <ݾkb6|<bH|t UɢH ʡs1I<'\<>BX ];P lJ?."֓=owlgl%RHWHMW s6]5PlmLz:`l0[x rW^3sF)`f췾 s)j6^WQQ k3'8b~r}\E 6DpDR^OKqD_x)ү G(4,-'%S+t)W0QQ_;dž}蚃ؖSyH껕$tnp}Ƴ4eN bBFJwrᾆX:g}d ջ9LMOl vOko:=/Ĺ1l5VjlaM/?x9q,&HjvL;ZX]:K4 5-;jd Rxi ?Gpd)i8_JQۇ/7 Ӝ9unc浲 GqTSlr顳3M'DlLwi84~fQ8UpChfٴM'] ӟ)h1MT2R W65 M'ǽأYMӦZ/N%5  P) zCcZ;m׶符qCw9J\QC-M3;ڢ9N[n67o9֢9e {۲E)= M--ˆ7o-[T"P^tWKW5òw5r7iU ~YE۰}E,b[$*o7$4Woǥ#W׹ek5iU3gɚM4Όi>{Ӛ%sy]C0ZhusJ kku۱ `μvawXz]W*q"O_Gc69*hoܾ}CjI;g:FFS}c-}o$??R|[Msyup>k\{vSsY\ju? pkjkj\SkjjWo;$0_E=}IYvjKo?-bsU]|5͙~ש1ʄ6Ejw=W"WCu̿G|ͥ]Rp~5 J_P,tu85]4RW!%q40_i'5iϨ/=8# 1LL |Xcuq;Bf9Tӵ4 :i{;F{jz uu¹kΟ[/o$!f -˙fsqƤH[eߡ#R 9ے~ПɈ㤿4Kg3:QhAy $8ewn~Tr 4@B{^o5-F)~ryԷ! )tts}tY*:sNCcs ൽW={*mofo=`VX3y5fg#]eu;3+Լ7\uK{a}R͛YV)ݍc%|H~UE[\f*+^i~SsJ7,ޛo3Kwm(6 Ňe ޣ21QznԼ{Bᛷ%U{tRhaڄY I}Ix4"l^0 MM7Zo`rZ fS*v/5e>{RĖ0р-&# oxS{#%[A˚5[Wq׬UmҶZ2755jW^wY*DV 3Iǡ A&]׵bTi]7w_xdBt*'%k#NC<`Ċb ԶSw훗k."v­C5߬d5$y!Pr_uǬc EJ(YcTiN;O _.gTuurYR ڢp6Y'}hg ^Wԉqm<ׄem%s\OyW^* 8"h>Ypmقt{?bm*t,;vޫKD3fK_-;9Kt=8fi?Y3#֊4-XYHp7Voe^,J}ksY+5+pv\Q;<ūjL*< ᛖA(rHvB\ҟjei6cra_mv1?nujJt{E86OVVRnWux^3xm\Oh٫Y.Kdn@\y"0f<绢9d5|L[8>+yQSyxlM8/g`> XHf,Ó^栘̧u:ovb,̩;./kZLsSrhW Ҏ(9+HNTvR͌/CNNԦOd{-0[xrŵtTȎUʒyQ[J]}C3u[y쥛 ;o)"i'S.J6oƘZ8sȂ焝"YGxO:w0r+Z?ru;;}a>[V2kt^z<ǎdZf(?V%\Kvg>EQ/D"s^4NCXxȵ9^zT C6"N>f2U&lx4 iϭQ[W_9{aVɯycxikԞ–!w)|4%8d&o`bn)H R_楬k. {]O.04kĽ1u82A>i4dҙmo wrp΋8 )u'ts!DDV1'<ËV/6}5ۈNk$/f")9ax@rCS-ͅ?]gM Ճx2/ΏXoH'|AˠT:kLq5D8(ZD8(, BS]|6!~z)[d0^zThLs:+QKLa9ӾTa`1:.j =ŧ1L8wB~ϮUsx^)%1ZULNLH]EĮrDGbܽ/ѩj+ϓ-tayV6ӚVP)(Niz0 E$C'|׽f/t&ӚDuu$; <9J/k Ζ3b#vFs5OJӭdAiVj:"qMBŸ 1d,U;z9.'AA9T+ypX+B!g:ͧhrI6W0a@?Ţ5b3x aiěn x&)oY)1: sc]M4ESiB=IV{u H5e̻<2~ĪF=Y2Ŗ -L] 1W>jNT^!Y'?R-k?+w")^y86\T^[FQZ.RcEBϡ3(3hutvFeRsMz)uLoYEyUssMbA3?1h$ffe= RĕBCS (w&l^y{tt*r´G,#f -1As#ɃE~Qpٹ1 K99B(tF!&QSU~ei?[3Cdd%bJɵœdk#lzR8^v(GՑYXqX^֏MFR*͂wLYMLaGYStw'C%]J('UTcPIZȨJ3mEE9^vaA#Ɏ͌S)NEqcRtV8v2&x?wxx:)M[s P)@GBG&$Cj̷>?EC&6eQcgʼn/J&wx&@R%OqP/ )x5bppjb0jlzw # 'Xz+%pID5n.E $'Vfy$J(= η1fi R-CGn)(1ii)g^&I)s J1Fe(6!e#`9bpcxY$LLQ,?| l6e&Unx1 /wD<є:y|AdJ.Rg$O^GTBdB0u10(Y駏?ȋaߋb}V`68 /'<#$h]EVCc &$΀q]*4kswfI{ֽ+ 1WXʒ8qd'# !;)d;'>*^ ڇxOaAd<́u#ێES5* <#+}LWmǵDpau0Vf>rִ»ɻe0=U,ΰ̓^,12Qi;Q\$2cԠUK//T!@g3<ތA5KΌ\p}63hDAJ2)B)CML"^ e 9`W>,C/| HvUIߵ)¾+Ńd])#2"z֕:9bG/{3|,oeG 3/"z2E *`~JI> A3xKp5®NNj}E#\ أp5ʒ*[Br}QOr+x L\fRzɒGh1USSہIR ЫM.Ͱ:!'f2= HQ]w C_Ycx-j&.MBޛ?>L>hQ3Ub2gU =޿IjVCnsp᫓"6L/Q;pjm%e6n[Γ; /3ë'FDrg%w6>ReߟcR]l^:UQ܌p^kw:}9kDZEȞVf^;i_f낃}=la֍LVW'hRM ?JsU[5F)~ndA>֋K(@Yn T)1du< NFn=1Dd,I8KJ c例`2~>OX>;e˰yIi2Q~>j(|U!!w;8֓BVôAP.{6B= zBw*&nV'T9»I0Ƥt1B:ǪuTw 0e[es~8W<΋ږЭNu*+/b0K9 $چ4PЎ ľܩX.Dx׭& ]z2 l]+MLZN#1r 뱅YWZUdЉ#*NVmV)Ei7 u(P20ob_Ue 66t]^!˒F;-+2I$ΚT}%Pwa뇣X_LɅS$E0հ/s8n]˒?[I&MD8"OoIah8c(w]44d_C7p˦v*>KłO",l_d̚ԭL C,,!>76rJ$]02}Qxm%dNF0|{u%Utʡk3'dMk'eMk&9C6*9>>[$V$xP8^τqBjVc1 /A!1i91KKY3'~L%sĭnFFx"U 9LTVz"]GO9W=X쩲*]$["lBem 5K?؆ؙXdްJ7y;1+MNt|f&ܨ|Au .ô3(~挄N^Ntr4/[Q7{ļ~-E)<,谷نOչƒ'3e9P=-];=)ĦU-RVRV%^V)þҮPſdvE/OW[I,Qp- zORG',[]Fmy:&`(yP`_m.%rj &X:lsJ~r%JK`O^k"kPx* 1#rLVX˔c@a(:/,CbI-b1&%*B>KPG|x9Z%0,(-aX׎N`R3mRђ~cQ̎ W쵤6\^+uuibfLKG?R!2[ [/1kCK;°əP7X%˵df2ϊ! :OKLIHw =ʸZ-yxMF-jEsY{ GYGcˬJVǶ|D±a3)@:*!^e*Rgrb8}*dST>^JUƯ$}k&9H^I>;S N[~D֟ݥ@r&o9yy`SغeUC"`:)Kuc5S:)cv6ᷭx# .i*!%S'u֑aV&q H%,Ģ Q?BwYx{Mh:5;92qBtÅ\#XFo] K /$O"^.3wa9,X8Iԙמ?xw|ZjCi]x)FWN3mtNDԱ~4v(LOeK9 -g,KU^t.3 [ sw잛sRu]/$:FKghf[Ca" ;Gdv#sx¡L%;V;p-=_͌d0|O[c eJۺe9X!ac܊-[\5 dzs|zd9O XE4У?K2ʭBxe[~4?09jJ7-x=vf u=ɼ1z;aBD'J>KpFIYNL{֋i>4,zxP%O?t(/zj ϸ<+o\M35ͫVKixcDӲ-V5$3 'UfdƉMkSSZd6w#-8.# ĿMG72e׬/Ʊ[nD;L"(RgWהYtE~N=T Ѥ9j^UI6{z'TRݣ7'I`reLjRViȜ>j1,VI[_"ߦg?F>j. |y<$bL+j<4KcqgBjܠ5L)ܝ-*GX-ɠA$eg+MRk2Vw*0=pn ס6{֢K-ry;pM3*Q\Tgec5$9Kaal*ȽMAi fu LQ2tA$gLwl h#*Ƶ c5`0i¢UѨ~4 8eKj. 󿉘숁!Tdy쥳OS(_j\-ۨ!N+l;aje~8y2gQ,~)i2YqV^II&f͢Ž $ s(JbsQ4 _X5J&䘂3Iqv[NJn+eƙ6~X67#cI7 .u43o𲏶Sٯ{=tǘTc٥-9]_XizD#nbw wdžλXG&#yb7C_ןxA*k G0t(# Q*8mJ}k'6`GąJ&yB;M~3Lq K ~,/OJ3ȂT)R$G[L#5m )'8B^5qy'~)+!di6=:6Jyzjg;'e!5ڳIٜڡژ*uX"ɮεhGNA<6I IEq,, NvǏ,& V=9Cl^x@,Vɛ ]E +oqTȢtaYȑoc8S2RQx#aed#Voe'W2j/cB23؃0Kz49Hej*b|Stvq LgJMSg$ ]bɏP*W>\^ +0XaeO&?0=75 +Vk%3bT0J²Pn5=52\A {R1Ћe} fد`2OH.dq_ɴPqPdN&= 2^0I`Jϣ`҃!lE$ۆ\[ áBr"/bk,Wȯbj oZhbr;|8ZЃ !w1ZaT<2ĞE@"^>c c8VP :@=&<* ld"){F$J@$-t0dxnxLbJ$n@bUyA}-41[ #ƻ D) X cl1<w+BN yD-8e^`>jvt` Wt#_PJ`1ܧSCW1®+EsjTBv<+J)2S0l|h L!xJղ}p8BPz8>̦vzeZo2a z<}yNi0 V$_Ev;y$_ q-}`]g]-W& L~/fuٽ)~a.`Ȕ*D7L lbeCCGA`{2\m*a"SE|#϶uw)zh>)~3%KDE|Q/p#!λhkz׈t/p#n0un.[D3܋'od, 1Q%xaOp_p n: M1K0Y2˅Rq 6~`ݺq_oM"L\hx/nk Iǿ;ee>v捧?jX9_T-MuHI%G`r#DL&74Gd3x)Y$hp+Xd\D#¸+vB1S!9'Љx~rhtGs߬= DZOQ>M!T)Oɔ18Ͳx i\܏*>&7ă4&GptA9?@}q|]>3_ O{1~\Jq"}x"ͮ4HrM*B~I¾7$bfDq/ C$[:}ړrqů6"h#}m"|\aHoݡOo5&b4v$2Y(=9LpWmϰ4%b'h: ` f=v|9ovTBrQX-?ݍM_҂=+6~%{]n|~k|{!gkÃ,pB%/!PeOs Cq-b6YICB^r=5~B"v_A-q!NŇg^t+'aojA{fF3De%]7{sb"vc 4C72t ^r wq4vth(g:BG.b= z!|.]{[W^P>>rZ EjICJfۇΐ,;JF]c=?"8Ol'z:DOxxnSe/B(YEf`]R%1On⠏bqߊD W2ze/B^GKɺ;~Jc>֋mTEۉ2Xq市WX+~ԓ}/v{Fs W԰{,܈8W^xFq1dWQrQC>_p+GD|B1dl0z7'kjN}1nz 7x"Y-z812И{xZG*kX?jNCnJ =I ͰESn9;i{1 Fxp) /G&bilU(N#OYX|Cjm GP["ƆG5Pm!%m,|4a#_Gu?둯{K ^gp C g46BW ̎}+LBP0o1o1<(Mr|Ι>g }#m"FD,_ۻP(SJ)RjRJ)OJ)IyRJy:N)\NAH_r0 6 6̙ 0̅aַwxoٳl}A {K/QYm/= ǫs+;`܋vȒ|#n7pٞ@ +FE%1eK ]ӉiF`ucÏa$bQAŲ41Mb=A_841b 6 \\bDC0N;l|0OaE,*bT2Plr0I[cgA$Vt)kALxNC.'MTl"b=h"a(+N qyq*,w=pչg1|whq:<:-g!Z&5WY(.P‰|ઽWE[yxγּ"~wp8>X4O19b(kRo+X#frP$鞗81/kધHQC\jԮBU=GLU&9 xs^d ):ܵu:NxSNrzJ:rXK$_QDBu򮴀WZ'ƕ,.1hgQ`#hwQ`#ybDhpQ`#g4'F؈3ڟ'F؈2:'F؈6eFK I,)q1yQ(J -|͹*C[֝(stM3{彙}uM'%X&>~܅GǏ̻U0V.ǕIJ''u265{NM+28OOܛi`MO9O}Ɋ(c )???>x9;xN&A 70^ `$8f`~Sޯg`C!$60mvW?k޳4g؄ R@)$< Nmh $Pa P>xga n2fR6=,4 /6-g`Si ]}La`C_IC\@d0Cr99LpPP$\M9pY* *f*Re_د(T0PagPa0Ro(*؋$s9/lI*  Jer@H&ӯ!  ax"6o…0 FxF"!i t@-!ՂTȋLd gœFsSA\K(\P%Pf Hm.CL F 9NRPhXp-j:abC:X׌bUVAtrrN$BHm2R+<(D++ gHwt(H`&1L>8] !L = "kL2 Jb:dNCp ռPϷ/2AZ dt§0ha ף%#@*_(@800:kֹTo"8 :`$o8fmc`0-tsWR! 0)Q"B 95c# DqPl!v]]|| ( 3a iqdBt2sۧ56j L?EvgϳZf/_h/B"ۄj )(uc1O4'w\F 0JMd،a s{ F{z2Q3&\ ,jm I  A{i<6WC2e`_2H"^h 8ݰ餚h,^I1 :LãNb¡g!I_n-ϚY[^ӫA`& } Q 2gY d/Bj(ADF1i=sG$kiN2wGtMo=s m".A^e a' zbIfa7xQ]!^p ٿm#h+7D!"{}$!CeY+p^x9'6+q@n7NcHHMүJ < զw`۠o x|##^R OqAnBMp@gn7=WQA8vW>>g>]Ѽï/~'?9@~»s> ||7j| = /x ^q8eIENDB`HandBrake-0.10.2/gfx/icons/disc.png0000664000175200017520000000241112047445774017402 0ustar handbrakehandbrakePNG  IHDR szzgAMA a pHYs(JtEXtSoftwarePaint.NET v3.5.100rIDATXGKod{i:ݖjTXP_\/[bDZB4 4_H5*eBĦ=#v\S{f_ @[`( gsNa8OD X8C8Z!C8,O~pA؛< À6x #5DoQ?3kW̠4n5T/;}߿;}Oz4h?k˶*| F\Qcۿێm`/:Ss{R̲,)o#Ӳj}[[=@ ^i}^F7 uiF#n98aש|zLurCݶz{UoXk^j};Sfyj5YJЌjLL[% kM244MMw^DSeUU]"M:%P 0EVR/CYV:X(HO(&+]Ҥ +2iH^o4 ބ,w Vd!GJh(2!'ozz0kj5V/b%|Q.UU 0\?Djb5IJ~ =_ӊTHҳJ~l$I }tR)oeKwuj8qHfrD1IENDB`HandBrake-0.10.2/gfx/icons/addqueue.png0000664000175200017520000000757312047445774020273 0ustar handbrakehandbrakePNG  IHDR szzgAMA|Q cHRMR@}y<s,٩<9SbL!GĈ 3,F0+7T3IlpX"61"H _qW,d ėrIKst.ښAdp&+g]RәY2EE44432PuoJEzg`̉j- -b8o׿M]9La.+-%Mȧg3YះuAxEK i<:ŹPcu*@~(  ]o0 ~y*s7g%9%(3H*@C`-pn VH@ A1 jPA3hA'8΃Kn`Lg` a!2D!H҇ dAP B Byf*z: @]h ~L CUp΅ p%;56< ?" GxG iE>&2 oQEGlQP UFFuzQ7QcYG4G۠t]nB/o'Я1 xb"1I>Lf3bX} *QYvGĩp( &q x)&gsF|7:~@&h!$&B%pH$D.q#xx8F|K!\H$!i.%L";r3EHK-AFCbH$^RSIrdd 3Rx)-))zR#RsiSiT#Wd2Z2n2l2d)EBaQ6S))T UEMSPgeedɆfȞ!4--VJ;N g%K-sɵݖ{'OwO%)P_RRۥEK/+))U<د8䡔TtAiF쨜\|FyZbU)W9.Kw+YUEUOUjꂚZZZCu:C=^\G}VCEO#OE&^WOs^K[+\kV֔vv[]n>z^^u}XROm`m3h01$:fь|:kG23hbabhrT4ߴw3=3Y-s.q_vǂbgբ⃥%߲rJ*֪jAe0JOY6rvvtXLǎl&I']$NϝM.6.\ι"En2nnn[g=,=t٪E2}4\j5loDŽǞ~q=''Z^utv&vvEv >mяN9-{ LOgsΝK?7s>xOL n\x }N}g/]>uɫ,u[dS@u]7ot.<30tKn]p;;SwSyoEVx[5Wg~_Be}s_>rqg.n7'^Wz]p`ōl] ;n@ήD.//Bfs'z;y:Nn;us^tNfCдnkt 7|ԙ7ojB.R Ŭ\!ZzGȾ]\l@ $=KDi&r{X _`,;3wǻlItnGNxwOnNIENDB`HandBrake-0.10.2/gfx/icons/audio.png0000664000175200017520000000163612047445774017571 0ustar handbrakehandbrakePNG  IHDR szzgAMA a pHYs(JtEXtSoftwarePaint.NET v3.5.100rIDATXG햻oP"xX#MRq$G֖PƄ}H HUBS+ uAH]'NuA=9[cc=sS筁]@{sTq1ľ/yzd6 y=yb<qPgZ-8i 4gN?lF҆M۾bz۶8ò-bY].`2oz4~Ʀi-UacgALƾ 8o!6S/Gy Љh00^$ >Ώ3zCG/]MP"?T^' 4MVns9@s"~{V?ͼq3Eja0Z>=p[`f%I"Rv+ WO [{h7CD̋$..Q CSa;13fff#gds9fI$m8j]wd2m@\Y~zzzCw%XDt'Ҁ=,|IG;C[>ᐆ p|<~z~xD`Ч`@~_\Z<y WwYN\!U=,pG# W6-=2 wtU!ɾRn=}=0Q3t:u"Z"F"ɕם(FoY_oT3=oms ,uOn1ݷ*300lxb% ~`-¯ =8A~.93H6e٠ Z -hρ߁E~Z"Cayd/f9@}?) o o8?ߔan7=ߛ{{O |Y7zrsy<*m`s3̍KlmmF5 wt\7w!0א>/uQq*wEsF.tl[VGV[h\myQօO޼r۶em- VqCGF鵬GU￧-ˆ/mRƚIENDB`HandBrake-0.10.2/gfx/icons/question@2x.png0000664000175200017520000001620512051732765020701 0ustar handbrakehandbrakePNG  IHDR@@iqgAMA a pHYs(JtEXtSoftwarePaint.NET v3.5.100rIDATx^[tǻ ]H)nAwS]RO[ŋK H !Hwnys31>֪ݔ%rP30ltpxuXmG2`E1_֎!(ך5nɶACS9|Lۙ:U|J?ԭT<+=h|l !s:6؅ ׿u§`"|Ab{-Ow[/<,~H" V%J'Rm# FϏp퍪uنm*:&A8{\2db+?KWzReC ~WO-&ߔoH ץk 'PxuX*ڄE>W1?b26sfja7@g %$cml+}ſkM8<끔rKL.e^7J̀RX;gMYeʳK%/ b3GsbNrJQ`#5PQ}=o%T~GaML C8 Cz*NK'%c^x4<=[\=Iڗ&PC>bN6 lKaoAMr쁖VyH寤{JNЄE?uI^ﰤwTAtq|TO<뙛Ngب*^i*fܓ:_J"m殹i29mFӌo.I>{"v^y|b԰.]V4Sҽe7d'rZ<|WoOe$9ݬdzVɸ4씿ڼG|H+G9& bc.븤/I!qɸy錸9(m\ZKj!'r#GrGB=w_a@YXZ bpȘ??` &~i;墼{gϫ;?rG1\Y7!(%##є57J7| 9{\Ӓ6EjL2ߏ P()δݑnC({8O߳W2'ǧN1"~^ p u9UWk1O#Z#/ N 'r$fL }hso@a6urn9&!x) wGBHV`EIJ Y[=|)\ٴa ] IEE1Jəm]c_`z 1jR/DtG.@j~;xN&أƶ}4EP\¢%)YNܡ!ƻT;8R`oRS|tyk{84`P glcXp`U M Ur;#ΡRCă;7/J{U5P5jB7w [/_ErqMZ;"O .lgI QI@-&EjT[j# ls&ovBh^y12i%q(ZCJ&ג^U'SUfC~wXr:nP'gʀ7oh uX7Wy*E-Ԥ2@ Љ{lj|asP(xFG%g}'CR!uľH _4%U7[zk 6HN[$G(ud Ǜs޴oRm_G+5PK? >欐b{x7񘣣J a"9:"9zQ ֋C -lx^7čx=wJΈ=#b3v67,cnZTv@P&%5@MԦ@)n~*Rhy09"}<Ο}OH>Gį>%Z\Xem[-(={?A#lZu_70omKK~'<@h #Ԧ><xoDj?bɷ$3ߩ$ľO0Q8vO-_vVg˥nkĻ&x_ ѽca8V]'keJkb;s#%%gtRmPL灏 Q,uߓ//Tྂv_AG|:oxk[}o m[|`_9xţvJ@|f`@BBy]U%lt+8I\ (cd{Kuch@gA^NsL|zO9Z:nCnώ[ūSxuVX߼xv;%&X%=gIz$$KB<{BI`xcD1? N_CgR#ZSɋf܈@wGJTNhԻQ"0{~Wλij }q@LmWM咫fsZ[[< >]CVvYlA=qU|MA>s׃xd ]M`Wp%yVߧ9T_)%JhdCCρgL?_|>z)7[ZQGfdhC2tܭ &2t9-@?K >C==z+C'|x$zN{$}h,>K̑%Y36mَK@);S\K%}uvx.vgܦ<%[Rb| Pn ~2! X1PzḶ:ynұvuYϩ&zL}Oc%6Vا[Rͪeљb[nԙ#n"%]Uj#L?Fr5[玒M.R8orq'L K/A;oN3at$mًEɦהyUo~ܑ %b?E ŭrqkVҵ,`;b-\惶Z Ō+ kae; 8qθJj8ob$Hy)woZ_K z]ZmB=pջnTKM0Pk8՛/N sӕ0`؄[r|_x=tR}6stt tS˭ O B^ǨJiEw/&yIy<|H5=X)͎|OqlIg!̀qQõMt85^Iuŧ y,A{U#rUqs0#1NM .23ΟZ,l$?iF`Z0{5^6Ǧŀq^! Vcx#ų4YyBž#仚fQp؇ šRql 1ez䀙sFΦ\xݮ:,vJަPQ' 8Tmܲ;Fkž^j erN]ßuuUh5,bWQbg(0L+r_+- jo_w7XhOS݈dsRFhc+S^^^(h2j6[ŶJ!S#Gsss|5ֵ#1&X>yMrcWg1C$Mdîy>r/E^:%mA0;1^xZNK[R5XG\əܝ7ٳ8RZ׊5՟Oc1 :thGI|{`8HlĖ#Yc_QnE&goIW #B[gUϦ\Y!KkC;A\əܩZ"2aùO.Z*U@Vn;[,NEІ$MK[mX D(oߑ7nȵk[X hȾGz6?F[1JӼANɕ+W<3K (pVPa:dεM65e<  6i 6|؄*]6M/_N[0_1N@йböIҘY p9QE{!Mj-j3G>#@fF…W2}[-a%:Q @N1>S,4ū;wȵU?m:ub9<>06Lf\kUpW%`@.DnHs79Yy{_fn%G Z(QbX:Ego:KY V55zº0SNyzINYu;FֹS$^1OO{I_k60K2] Yǿ;oAX!SSь\zN&Թ^xSPMFh;mCbxe*k׮]lٲyCt_O!jeQ_h X x|1bqC!tuuIυt:rg!BaB EEEX#d/Sġ0*<,X51XL ߏ~999w?v|jQ^nAgW7%gQ-zuuD}= UxV YNj0@]@\' z {:6].U}555aQ-in] 9FT+'w 91/XM3HL:C=5_vK%=7.l)'b_4!xC+:RdO6#ym0鐟2X+*PinӚ2{*TP [+:v@bFp5b+0UP4.--\gX#b J3cii X}#zĎ*>m({qq56I =@v*cC|U$vD>qĄ\CPPP@QHs[zG P!U,c3$h5ZD}ʳ6LbHkדkڼVkG}ɨeQC'?Jzz9`Jh2ҜueTz 6@ד?÷:7W'<7<÷h,suP])Bmz.z\U|&|> @+_糠%4Zrkd$xFqq/䩱Z'9jFcڻgϞchJV7߁٦񹹹w_| @1#"I $5:`IENDB`HandBrake-0.10.2/gfx/icons/PauseEncode.png0000664000175200017520000000244612051732765020655 0ustar handbrakehandbrakePNG  IHDR szzsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100rIDATXGOTWyj>Դ1ѦsƴZV @e` @1j8ERBbҚ1mu}3ФiR/s/ol$Jj*p8։4ј/C|֭3b($&q!gqkdmX%!X%Q]X~s~mGhу=]bݘ9Z^z) nla0=lJO4SRýת /.h*(:-S%%;:i;s^zЋ !1g|g *@BѮyPp]~9,MԡS9h3u7gg 0=7NM/s]VhmwtރwdF7=?rV;Sf1n~cWpdd|6_ƍ_:Ao2L/rZaF{kkx m ZkjȆ @o2b5zidi|sk7da{bXքtyh񣱏Aצk:Oo$jf3eo`dkdikz]HG.ЛXxP'l2)4͒1JWKGȫefv.@Xdzl3#p3[7* zP*j6;^ߕm2WA)vtHiWhBΧTmh'" 'DzlF`:f+G:1CEq82So \m蠬 Wjb㽆K\8q#Oqf#˳%:fz'c!bwYu:lս3LWw hԄzR w 5]'l**/wt7 kLUHgB# _}LTJ3=3S=K3SOKA?FBRҸΑ4xsJ0՟ 4c#u%+4$fRʺ"SKx2k)jJf6lKBqhʴ8< g_2\XvܣRjV1"qa/ {Qx.,cSkE\˗2b^t(u%sȘ[U{ ?>R0IENDB`HandBrake-0.10.2/gfx/icons/Stop@2x.png0000664000175200017520000000401312014507023017733 0ustar handbrakehandbrakePNG  IHDR@@iqIDATx^Oe;k[j(\?u8 8:lIrP n! ) ebc8/ĂjY7Dyւ ЃyocA{o[,r p H_ )}z>Y KZ& aWeS4#8m킝@(Ŭ7/PN=/& 12qB˷EX:/i&&&<t V0KqU 6ؽj ~ zR3Ť/ +T60/+'7ڈLt肂/!#`PzOE:W [5XZc#vWᰞl1eřStU"HGjr\Y! FCtdf^rPMXA´l  'N wogH'!4(f-*&X{`ڵ\xN2C*X}eܬANr\٦2cF%';@}JԊ'{ۺ# ȸKKKLlI Bh *iW~kц)1l^ziItG<(\!nG*)67YצCeA0 {`TwlrUD9Uo8-3S!8 ?ۻ_E5z\}+HS*ODtO= -H`:Q@,ok3dVpg^DW f Dј h c.:"bQ22 ꀠXئ(,؞Sc?;CBGĐ $p Zi׶͋q,e) p*vr.Sp!8# o@6% LeA+%贓~L}A<'3Af n̋ugc-a /Cٔ$(;e1aPnBZOaSl_xV˯}],~} z{'O< x_?^*IENDB`HandBrake-0.10.2/gfx/icons/add.png0000664000175200017520000000214412047445774017213 0ustar handbrakehandbrakePNG  IHDRw=gAMA a pHYs(JtEXtSoftwarePaint.NET v3.5.100rIDATHKUOgJ/%-؎2[K"IP#A2.[#]68, X<r !&>˗oZ\<<>2Ij]`k|&L Y9~p#݂`F𤮮X LF1srAAcc#P,0== SӷP,Tru 69EFJ1;FFFD 39tMtqT ߏ# ڿ#f;*e~|+[O8'ft_OcM 3݃LO.5Ct$<B2I$]|hcq[9JC-=t/zDDSSwA ?:AA̱h2O/=周 GZ7CxVo9 r*Md)-FJz 1ŵ+ W.jR`|MrUn}宦ihrzŇwqz2tiC.5jn ntBpȥCnW`lF).5~y$1R]] OC:"6l /K٢bjkwvCix i,~Rgu?eO1s9ץtϔ^h2*0XM!0hSc/R+xo6y%x~9U1s~Eoo]p(p8|LP:Xs@{kӚl~޲}}?ԛP-c;Y|(Bv;(P''axX^P7O?z1?lf x؆v+ 7!vS@/z6eYW^+l )Ccox;,ךoq,WZd\ Xkj}4³ZbN&$j1% qaz|l y͛g8b^9ޢ<>>'\=ٹG5#}B!;?x:r=2s4;^ #߳7]h}ne|ܳ}zxBx.[{=kf\<4t01c]?l 5L0E5D50g{z'2!c|/9q<`8#0wfsPF 4+f0W]C;`0A ~@?9πcb E=gk!KsTB/IKuD ygODin !Ќ\ܡ(&'~'n<3{e;zx@e!RTu,;eY:K/W1caf<X.G=tSwqHowE;s^`o| xx H%nŏF_$⪓$$HI=ٴVo1M;;`ؕ=IENDB`HandBrake-0.10.2/gfx/icons/stop2.png0000664000175200017520000000266512047524523017530 0ustar handbrakehandbrakePNG  IHDR VόgAMA a pHYs(JtEXtSoftwarePaint.NET v3.5.100r1IDATXG͗]HWOL4&Ĩ?bvmuNVvS aAւ`/OEJ.ea-֭alpct6u1;aoQ <!pRdc 6`:A>X7I]]]']Jv} UpU{WT_VmGpkh``,˶q*.)sNt1Z)#1^io/]*P ,mǣU.oSW-V-Ϣ3g2Ң%،SfU vSR֛ΨqD6ú&D0q/MԨ*\IIIhWIDyv>d*Qo,o/.RVVJmm3-/FxMg?:fߩSAbaaF&P0 UWҬBN'׾#V+..}59hV%1??Otj /5N땕T^QAii4- @uF? RyyԈfUTT!177'`5&~aPgYg7ԟNJQVVф]ZZF$fggQ@IjErh?Ŀ7~_d2(? EW,*"u$fffQA >yľCxh'Ccq|څ<Ē]$rssmr8tc!| W!0! /[ INSSS~|0YzWo Au#D0.}bs؏:0">Wﻦɥ$''%&''w2!*7pA!| .{ӽ2M5~ĄO5 |@SRRjfLw ~nsm7^4lWfd7HVK8s`u|nj:>&lNtI E?,+Q)p2C {q!A]||%Qno)~o4msXVMz}VX-r8f|OO ->O0Gcccﯬ,B'zhcccysssakkMv;;;/1 HIENDB`HandBrake-0.10.2/gfx/icons/remove@2x.png0000664000175200017520000000643412047445774020340 0ustar handbrakehandbrakePNG  IHDR00WsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100r IDAThCݚ{lmO{N/Jzpa"( n nq bd0 qӠ@dydbE( Ujeٖlٞ}?Jk6}}יu2it+z$* }BP*TUBuц \p衧}- !hap0E2ua-> \p8@r[#(9XQ$,~*N Lm|'|"mLD a0UxVU+/p 71E+4?eB0LǟNzѩ6tP5k6نYg ۸d NA,b^qJ4҅R`ȿ+JJJ{ƍFMmu>%' 1p$zD" ڿ;w6tcي+ Ճi۴m ڃ+V8[\A"pX$ /]-0; 5x`kiyUVقؔf)vg<.*[|.8FXdBZ.Z%g'`11|nzkyŚ&ZzzCeeM 4MQR}2tl>%ϊ=0+B ̺vi>G$2))U?z*SG6:|pX'(-hBCÖU,xv`GH/XA>&̶svNyo|s,77ϕkc]=$' m@ ІƤD#6 yw5c1ґ|}:3%dҩ=+tJX缹%/:%3)9j N1r(Ŝb MhCcrR!x eሑ#,/?Lٙ/TR"d% P#3 z2ugK틤qϐ %;N]g5ʖ.]@ژF)`nqWg\WgS6۰aXgۅ ės ;| NS4뀫_hCc8Qvt# fCnb_?믷:%կ_[³)MSSsZ \8-ҷ=Ӂ/pYWWbkȐpL#4M4F%ũ2khZ뫭(&HԧI>pp2V]_8b8<);'@czc qCC#Fpm-k dUUUGOP}"$Gz~gwjnڱ(*qVWWX5AlFz13QjĻ]N7H$xTJV^^a$6W;(cJğD6'b-OEEGu&3HUmЄ64,Z )/*z"p)PfX4H'%L8P->G58:q׊%0L 2%C *ns\6 ^PJC}DRSc?ҕ xcɸ%r%)1 +@{k*))uOQ...qaIvTK{:ڰ|ဋSB b>" Um477]¸BB$c;\^f%`j8T;tQ;P!v( |(*(!1-";7gpb{/݅,OO矸;.!5Hd j;|=IM b~V-64&<ȸJzK聜\D.ߨy|Xw+m_$l%\'_4w6-m%\IB.q_0~hCc«9^#0>& i~yޞ3ސ% {S1ЮT'<zWvaa N Ι@ dJ4L#w0frYȰJqwZ^ׄ_ h{AZ{O`ZzwpYzMULb3>^e^ Ӳܻ0s`J;S=c١ϧ%򢄬)}h[%"4ӖQkMhxDRw&{!šÞ{%SP*#!^< #=csM4\~}bRh)Є6bߓYf-Ԏp֓E?p.CAVJ2z/4?Mcpgzz j fU_(p+Mtŋ_T'7_|\֎Q9o׋]1MlǟUHcRykk3g|@.ޞT%"ܢU}XS=E8WH%s|j[FL}[WHZ쯏clԌ3bI0?C+ s;Ew~2ϐwO(꣧tI`" (w׮]nݺY/oh 36#q1B>\|pX$ >霾t}ݾ}S* /T^"8\pM b3' '/wD[[۶oNP&{nr;o.xpW_l\&&?"zGFOsG>;z|.8&xoĉ'O|Dٳg;ܴi6}^9?5:ALm`p'bǾ:::Ϟ=RArȑG߿W^yA7o~qg׮]Lm`p'܉{LjD;so 9s!ۊ6@:ڰ|ါ½55=3'0YIENDB`HandBrake-0.10.2/gfx/icons/delete@2x.png0000664000175200017520000000576012051732765020300 0ustar handbrakehandbrakePNG  IHDR((msRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100r _IDATXGoTW_ O.;mx*O.UcG&hZ]D)?-uK ;Xr+Ev ""F"w_ e`q3|swc&)((p"EEE?GDϿ󮰰_ to؟@_\޽۔ sa'X9x𠕲2gSRZjsa0%;'9[Iqo㦾޴ӧOP(dzzzM__5 ޽{Miio>wZ_RRh߾}6;u eM@EfjjDc133;k&'&n| ia+++; >[JaneLwD"30&&3#ט@wO)Ą@՝2=Qe3Y\\ |61U?6+|\2e^TYŻ+Wn1zŢTBijj!݀&@ȁL 666n A._ٳ=Z83{&77occcI 3I١3?? >[jL"DͦPԼVPomuCLraqqe}}VIÇΝ;' n#sudGט-,,[|\tieLe`O,&bkX%//{ť%TLxע l ggg F4`6hL'j -@..,`CWcCP8dO4v&//AlСCFs2 @$3gFFF5Fr6am*-ǔg?xLI@ԯU`xhH#-jUC}vq_˒ײD$㪹Mp&BY$.55'ȿ m!5A;8w^2G9Jd*qDjzpyNl w 69CN͑**hBk(KKkf`I>I"X2pꥴ6%f!a'kjjU9Zj?ؑKkXk.EKrNѺ/yK& lԽ}}a F"aFM̍8Ν;W%wgdn ]} Æڵ]?X OѰ jQD";hQ`u! \Dݗ,W(8ݦ]ݡ%JwO:8 OTD0}*ֈ}xOoݜz $89ZIr}ou}~REyF󭒢Ʋ Zf$au/o3&,++ݭ>(s6suu6x7d'fŒb FpM/ G=J1`<`C4ͼ3 `6 ߩ@}(:YQ"SNHP8[/ʴj$~e'$'&99ٛ2.6FRV$ƀ18Tԧ.>~f;o{m(?@x.$-UG_38ԕFgW)`M?E2Goc.n*8E|] GY񬑽h^} Eg&ÐZ pYHV,e5]_qjCuB^Xzzsڵv1^6R#Au6daҋO_Ԭ'.`0^09O>=5ozL61'e,2&ɤvqbun X=Fw^p)Rw5rɓ'aoFXϏіB '[MR(5`_P|oyyyN'/Pd`phjkk9A{tjpQsT?Vvܿ'{u W^T]ME`Krv]AI9Quž[@nW:lN=Mc{޻woe՞tXcǎٌ( 3![A 64I{ʣR??.dF]Fv]׎fjjj4mĨ{aCogW_}wަz6+#* 322K|yyUXBeG& |=S3e->JKdWiK-=@WM }ӼxnFM FJUU!ʼoQSN kTmmqnxVa| ܵ15s@"In. l3.M Mcph/%u픶|cF9{5^wWiG})R8,qw2I@7+4isZ]]m]9ln3kw NL;{GBJ vrRsm֖V[۶wEL%n+@I8vu%iTfZZuUgGa 4'b鐽lxF ~zH*ܛ.Ǐu6=gw>o':4KIt{s\3G2ѵ#`JFpJW@ݮw%[kM;G:q>K@0|>f/m nf%G$ф饥ڑ=}<2|C]._n| .CBgIENDB`HandBrake-0.10.2/gfx/icons/stopEncode.pdf0000664000175200017520000002272212431605213020536 0ustar handbrakehandbrake%PDF-1.3 % 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream xTˎ1+n_ r3"JM@>e=3f'#Mݶ~HVHqDNKjO!RFڄQO5Ĵci7V NtbzxE_i+ 6Dܺ+"/lϴ^;T9B ${Xg ^c!"|lQ\CX)Gy5'3KkKB!˪rY\|BZq%yjZl.b b}yq!Yݓ6  ܜ4bR5Ȗ">)10ĥbeҜ7huay@T=ǝ=4R=40 qf[5J{~`8 endstream endobj 5 0 obj 541 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 64 64] >> endobj 6 0 obj << /ProcSet [ /PDF ] /ColorSpace << /Cs1 10 0 R >> /ExtGState << /Gs1 15 0 R >> /XObject << /Fm1 7 0 R >> /Shading << /Sh4 14 0 R /Sh1 11 0 R /Sh2 12 0 R /Sh3 13 0 R >> >> endobj 7 0 obj << /Length 8 0 R /Filter /FlateDecode /Type /XObject /Subtype /Form /FormType 1 /BBox [6 6 58 58] /Resources 9 0 R /Group << /S /Transparency /CS 16 0 R /I true /K false >> >> stream xݏ0D|ŜM,Х|W !bF HՃ_඗y1heʢ&*XZ0WFؒb.ak"K:\Z?FebYp 5-A~CyD"?,y4 ݄4X,C(˜ Kg endstream endobj 8 0 obj 163 endobj 9 0 obj << /ProcSet [ /PDF ] /ExtGState << /Gs1 15 0 R >> /Shading << /Sh6 18 0 R /Sh5 17 0 R >> >> endobj 14 0 obj << /ColorSpace 10 0 R /ShadingType 2 /Coords [ 21.00001 31 20.99999 0 ] /Domain [ 0 1 ] /Extend [ true true ] /Function 19 0 R >> endobj 11 0 obj << /ColorSpace 10 0 R /ShadingType 2 /Coords [ 500.0001 1000 499.9999 0 ] /Domain [ 0 1 ] /Extend [ true true ] /Function 20 0 R >> endobj 18 0 obj << /ColorSpace 10 0 R /ShadingType 2 /Coords [ 21.125 47.625 33.125 1.622559 ] /Domain [ 0 1 ] /Extend [ true true ] /Function 21 0 R >> endobj 12 0 obj << /ColorSpace 10 0 R /ShadingType 2 /Coords [ 22.00001 44 21.99999 0 ] /Domain [ 0 1 ] /Extend [ true true ] /Function 20 0 R >> endobj 17 0 obj << /ColorSpace 10 0 R /ShadingType 2 /Coords [ 406.25 915.8654 637.0192 31.20305 ] /Domain [ 0 1 ] /Extend [ true true ] /Function 21 0 R >> endobj 13 0 obj << /ColorSpace 10 0 R /ShadingType 2 /Coords [ 500.0001 1000 499.9999 0.000004953106 ] /Domain [ 0 1 ] /Extend [ true true ] /Function 19 0 R >> endobj 15 0 obj << /Type /ExtGState /ca 0 >> endobj 25 0 obj << /Length 26 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xwTSϽ7" %z ;HQIP&vDF)VdTG"cE b PQDE݌k 5ޚYg}׺PtX4X\XffGD=HƳ.d,P&s"7C$ E6<~&S2)212 "įl+ɘ&Y4Pޚ%ᣌ\%g|eTI(L0_&l2E9r9hxgIbטifSb1+MxL 0oE%YmhYh~S=zU&ϞAYl/$ZUm@O ޜl^ ' lsk.+7oʿ9V;?#I3eE妧KD d9i,UQ h A1vjpԁzN6p\W p G@ K0ށiABZyCAP8C@&*CP=#t] 4}a ٰ;GDxJ>,_“@FXDBX$!k"EHqaYbVabJ0՘cVL6f3bձX'?v 6-V``[a;p~\2n5׌ &x*sb|! ߏƿ' Zk! $l$T4QOt"y\b)AI&NI$R$)TIj"]&=&!:dGrY@^O$ _%?P(&OJEBN9J@y@yCR nXZOD}J}/G3ɭk{%Oחw_.'_!JQ@SVF=IEbbbb5Q%O@%!BӥyҸM:e0G7ӓ e%e[(R0`3R46i^)*n*|"fLUo՝mO0j&jajj.ϧwϝ_4갺zj=U45nɚ4ǴhZ ZZ^0Tf%9->ݫ=cXgN].[7A\SwBOK/X/_Q>QG[ `Aaac#*Z;8cq>[&IIMST`ϴ kh&45ǢYYF֠9<|y+ =X_,,S-,Y)YXmĚk]c}džjcΦ浭-v};]N"&1=xtv(}'{'IߝY) Σ -rqr.d._xpUەZM׍vm=+KGǔ ^WWbj>:>>>v}/avO8 FV> 2 u/_$\BCv< 5 ]s.,4&yUx~xw-bEDCĻHGKwFGEGME{EEKX,YFZ ={$vrK .3\rϮ_Yq*©L_wד+]eD]cIIIOAu_䩔)3ѩiB%a+]3='/40CiU@ёL(sYfLH$%Y jgGeQn~5f5wugv5k֮\۹Nw]m mHFˍenQQ`hBBQ-[lllfjۗ"^bO%ܒY}WwvwXbY^Ю]WVa[q`id2JjGէ{׿m>PkAma꺿g_DHGGu;776ƱqoC{P38!9 ҝˁ^r۽Ug9];}}_~imp㭎}]/}.{^=}^?z8hc' O*?f`ϳgC/Oϩ+FFGGόzˌㅿ)ѫ~wgbk?Jި9mdwi獵ޫ?cǑOO?w| x&mf endstream endobj 26 0 obj 2612 endobj 10 0 obj [ /ICCBased 25 0 R ] endobj 27 0 obj << /Length 28 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xUoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqx endstream endobj 28 0 obj 1047 endobj 16 0 obj [ /ICCBased 27 0 R ] endobj 21 0 obj << /Length 29 0 R /FunctionType 0 /BitsPerSample 8 /Size [ 1365 ] /Domain [ 0 1 ] /Range [ 0 1 0 1 0 1 ] /Filter /FlateDecode >> stream xm…Zi]{mTQ  $E@@)ER$$D$ D)s393>_ܿ7< ޲yx˦y+U&qanpK\yݶK7NN.%ݒ{GHczbI=^~~)z@?~@J?9(qSㄞ8$;N:|]dS.2&;{Mu=~r#sGcrS %`c癁rqE oq('CN(BN/*gteYe; g_S甗t`22: TFs@vDĈ1D`˾Bp/y.E"E %C0q2>L'^"dbyU1>}bDg]Fzp_wS_S~f3 0LAs47ayxA ёApt<: }aFQy=9ffZ&,ؗtՄ¿~ zmLLOY3SVu9;mXgs3Vt:;k ߿[?mlymAp^\qmEwɶqF\h[^//? bb__W̵5;wݾƻnG7 /Wow??} endstream endobj 29 0 obj 1150 endobj 19 0 obj << /Length 30 0 R /FunctionType 0 /BitsPerSample 8 /Size [ 1365 ] /Domain [ 0 1 ] /Range [ 0 1 0 1 0 1 ] /Filter /FlateDecode >> stream xWRBAmLߞL0`€u ~YTyѯY-3nTgqK:u xbq C/AKX< B8œ +OПE?b" 8Jq 7 Ό + T`kuY۷߫TY~^߯N-~wJi_ endstream endobj 30 0 obj 226 endobj 20 0 obj << /Length 31 0 R /FunctionType 0 /BitsPerSample 8 /Size [ 1365 ] /Domain [ 0 1 ] /Range [ 0 1 0 1 0 1 ] /Filter /FlateDecode >> stream xI0}oAEAfbm)Lf 6ې:H+S8t24%F,EW:'/I)1=ROg`rVL,X~桖R2fQ qY~).}R.r+Y*ZkU{kaLn5{7f[c[ӎv]p.'1 endstream endobj 31 0 obj 204 endobj 3 0 obj << /Type /Pages /MediaBox [0 0 64 64] /Count 1 /Kids [ 2 0 R ] >> endobj 32 0 obj << /Type /Catalog /Pages 3 0 R /Version /1.4 >> endobj 33 0 obj (Mac OS X 10.10.1 Quartz PDFContext) endobj 34 0 obj (D:20141115080656Z00'00') endobj 1 0 obj << /Producer 33 0 R /CreationDate 34 0 R /ModDate 34 0 R >> endobj xref 0 35 0000000000 65535 f 0000008750 00000 n 0000000656 00000 n 0000008510 00000 n 0000000022 00000 n 0000000637 00000 n 0000000758 00000 n 0000000945 00000 n 0000001317 00000 n 0000001336 00000 n 0000005135 00000 n 0000001589 00000 n 0000001890 00000 n 0000002193 00000 n 0000001443 00000 n 0000002354 00000 n 0000006343 00000 n 0000002036 00000 n 0000001737 00000 n 0000007714 00000 n 0000008123 00000 n 0000006380 00000 n 0000000000 00000 n 0000000000 00000 n 0000000000 00000 n 0000002399 00000 n 0000005114 00000 n 0000005172 00000 n 0000006322 00000 n 0000007693 00000 n 0000008103 00000 n 0000008490 00000 n 0000008591 00000 n 0000008655 00000 n 0000008708 00000 n trailer << /Size 35 /Root 32 0 R /Info 1 0 R /ID [ <707223496cc420ab078c12530d92d993> <707223496cc420ab078c12530d92d993> ] >> startxref 8825 %%EOF HandBrake-0.10.2/gfx/icons/removePreset.png0000664000175200017520000000173112051732765021136 0ustar handbrakehandbrakePNG  IHDR gAMA a pHYsktEXtSoftwarePaint.NET v3.5.100rUIDAT8OT;OA$R @F`cug|64PR4I?D%B)P<"4ob4;};3Ie``pattt\NMM]NLLm3Oa ǽsss)JQP$])R2@ @^g#t\~yJKih4jSݦ"-mJfρKzCȩl|@zǡzNNG;-M!y v7pddPd2i6ETvKDuesl BTU2 &e,8% 2tjY,-jUȶkW췅8L} BG<j BjMNfnRR!j> AX[|)p$!.) n./J|5! T+Be{|mI(lK?QQ,B#Բ,,KDW)ɀQoo2cADB47 Ey6!?_(pv $'9Q~لESTXZ0L6C:GorWBCxo4zzf(y,暈OoPїkkk_&L4ǀhQ ۪A*'eJNfեo귈kiyyh BxBDְgKKLﱓ+++.5 x>ޖ.osxzzooShb8GPT(p{;;;aVT9;;wMvGp8̭\v78oN?\hyPIENDB`HandBrake-0.10.2/gfx/icons/Encode.png0000664000175200017520000000256212051732765017656 0ustar handbrakehandbrakePNG  IHDR szzsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100rIDATXGWO[e' f&ĸ%KԸF&" AC1Q Bq\6ȸ_da -0(t`c1:DB,ŘE R?h'=9{w9=o>JVvBA!|׶o ~sÃE?~_=^=7@pBYX~ciɅmKGw%'&i^Xkd#g]oxT3Ic `6 mrX` M2@u+"U$$ Xʘ;R2s1gHk^t?L4( } ZR5>0mew`=d)EUAUe:Rqa.^0's38#Q,NX !|/MwU49y7 QvWn-wsՙ"N5[ʴIeA_{0N|HfͨܬDph1b=zOeo_-S)8kIBBbȌxx;ܬ΀Q ./e)JKx{) x!ZWGWoÚ ,(3K+ERRIʏFـIuHpل$07U`!\(W$[A"Ʃch}U_'>K!*PFp+`n2$l)ZtQ!Luf3LIsɯ~Pd*KH\G2ya~Yk&3qz4Q md?@tNh1 1wD SA=e=pVY0HE®^s2w"N~}τg$y ]Z)oN{p*Bk$q#4$0'sG.ؕWQQ4BQfqbΈ^FZ< TRf*E^#^X3 $u鰏5Q&,3~5̱fB<(eDYe*Ei(XR "cH&PZk-'[ArcaO#QW0pcDJHvFٰ֤ ˄܋UtO>2D&}ڎM1V7 tt[ԽIENDB`HandBrake-0.10.2/gfx/icons/Start.png0000664000175200017520000000263012014507023017534 0ustar handbrakehandbrakePNG  IHDR szz_IDATx^kUy綳.-3a*Mj&D)&K$1IҴBJ6֠h*5&U#j4MPnww潝sg'u3=̇_KuQ}C䷕jm#㭮Zqh=j ^PT䮱>ٺR^P9y(%Q7t<_YT;@; #g#*22JaVvlY>EĴRGk:!l C_x_v#cEՔO,-oiO{_ܷ/ܚPY('8{R,.${ ߄o4M =̦0>|1*Јw|'~#4%H-XAHj!u$)t"6A [О%>s?W1=mΝaO@rorA!I?F < <ۺeCCC/iOD"CH<GE6Ҏk|i'W??n4'34`ED{>HCU40EGv~w~ޟ=5kI ~SFp` -*ʹ;1#8==,%7lڰ/)`E_OĀW$PE4SoP~ngY;jI5`ZD%h 4NPgFJ|t+_vpO^r߁瀤9X$jC#%/O k1_?GoRt hћu@#db?)Q>_oV]ݭ;F1J'G}deˎtu\.w0qbUKc,tټ{Ӧ蜿ѽVw^wcK?i6Ue v?.9~ HW){#6< ]<տ:BIENDB`HandBrake-0.10.2/gfx/icons/Presets.png0000664000175200017520000000137212047445774020112 0ustar handbrakehandbrakePNG  IHDR szzgAMA a pHYs(JtEXtSoftwarePaint.NET v3.5.100rvIDATXGVK@eenC`_r u(4HSn`iN,驪{]~t>S۫iOy$xw=C}u݂ z9n!ꪂPWj]70T%eT[\OeQ>eqWVOŹ,3 bp߄?˲x<"f[%ii`$@3kC ` )MԾnX`[ ힽ_ c1RErXo[5X[=Yg00N$|E_p[ȁZikV=F~ ,K RT?t{X,@ћ|Svf~ -WAB7 0|-W1kf3 ` 9)MԾ><#:9O'_q>jd )1pl(k \?iOlhK0dFF\={~n@egj8~QN<_ & RIIENDB`HandBrake-0.10.2/gfx/icons/Stop.png0000664000175200017520000000141012014507023017357 0ustar handbrakehandbrakePNG  IHDR szzIDATx^A,E?߼JPi\<\>kbЅ/O""HdR &_~⑫E\!?ۯ]Exer:1ILٮD 6m0|}~\M 5;S&gTؠa@邩[cZf8jkEpʀEPk0H-:Mfm jϸVM0ّd\t ]fCl@Vc=aK؅-w€5 0 1R3]M4BMSr@ MJ[ٱn<;v~{žBNsa߀vL%d_ Õ[P9pm`Sr;fS& ͠^X焴H.T :x&"@d^Jݦ$ lvX+ DaLv J7`59ۑHi7C4l" 9zĂ#*8P," TXK$6r-Xm* ̂EbfH| {gܜ^?r X}#`&}\HVËͳOI;?}6ψ'lsnf9oY g;{`kL=(O"1M.#6ɋ@AVbu ~^|fJ3&4&m+CpȜ1liFSIENDB`HandBrake-0.10.2/gfx/icons/delete.png0000664000175200017520000000226212051732765017720 0ustar handbrakehandbrakePNG  IHDR sRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100r!IDAT8O]UKO[Wv?xa$ UUH]] !EXY$ B] ""M;yo33g,XfdXtZ@>l644mpp0wttsIehb%4' _L&df'VMp:H333娐/e6;;CPrw#(e9X,^ yZZZ B_^߅B2 Nh23s]W0?Oo߼)ަ6O*_($v2ldC0loo1l Y$,nnn Jv# >[[[l&CKlO)eZupYVWCbyX<&lѵHFQ^]] MlL722r HS%O2EC.q`: [sta3ni34==}*)1GGL&ЀknnWGsJTFP\J%r-n0Bh(mllgpGnMSSXa B P2b,UH8BNlvCNCpF+9Nt#w ,Q?eKX^QccL466F]K<|ueeC  pc=}@H:En|YQ^y5IxÌe9; >ă)Xa{&~`oYqWO~G  _AW=|`|eo5VWWR'$V8IENDB`HandBrake-0.10.2/gfx/icons/addPreset@2x.png0000664000175200017520000000526012051732765020744 0ustar handbrakehandbrakePNG  IHDR((mgAMA a pHYsj։ tEXtSoftwarePaint.NET v3.5.100r ,IDATXGkY"M3̀1&Q1OoMN*TRWU|ahf;if103\Ffz'Ath|?ŊV8=s-9%HE_;DkuuA~-[0پ'9G*J$R\UUUUUzw wwy&/~N8[S]Se$G*+wR;lAһ/JI~ occ;{kruww!H$\2tSSS&TʍZ_OO|;c.` 6Ĭ|$Dkkk_:y]ta7==]niɭu6(նj} 391FFF\_oa:uJaPR oەT?.^)7;;떗ښ[3R+F([t"\J1K"ʘ&:ет57^z\877gȩD5As" 5&̎SY:/AJ'&&d[UJkDB%Yln)ns`&@Wt«(+**?$ڥ/\\&sUn$fnqQM[y-,,[rqK"c)Yl&cqN][b ,]}}}n8$(+6{z{\:݇E,dieXs?7 \YvOϤg\wO;}&\+.V"ML*ev.J gll57xo^I䤕'UG[Z<#Xp!d;["cIfSV|[`Rb`kppзdIa;c$_*$9 9JHR~\%xm-:MPGל Ԅ2Tꋲ*]5%uJ>0X.ȊMNM UWWdk777=hZ`1kHΤ@ߵYA2,_L##öS!NSj.5?M;Xc+FGp$[I& aDdl-0YX%$KJm51(ataZ\:::I୬KbozzLĽ Ɇߴs8v%Hgp>sP~;xa,KĨuSdHH=z@pxnvv 2浵}2f@8BLK)7g6 2|Z}}Fp@\0&Lwڵ[NF٨3Dԕ2$GHR%Gd^^Vs`+_:ЇQd9buq߿kTIq:t$QD*T6Z]R+ ɍBvLaPM/bu釛f5n R,dz(Pҵ!ήN xW1 IRʼ Hb*^Z>|C8 &!(rƅx- a448$CM~Рn1,=0N 37o"_mwMYY\%vSpR:;;-,bRƀȵa=b;Ȯ= #DrPhVtR5rn1Vr!Ac1SU 766p-oϋe=o3[[[zZ Cq㼄,Vioȇ+GBx*c 6۷["Wϙm^) 677FLZbrJ-\jnG.ob0ā 6rEEE&~:1,Ðp^Bj@$!b %c!1UboC8IZ!BrA onl, ;0f$7 D =[xPܻw]?$AK^zuɷQY1T]%B!a-˾ ȡ{CEV&I{oݺ// I,./Q2~yYDžr((IL,k=;wnn?Lsu0ƍ!BX'ҿIENDB`HandBrake-0.10.2/gfx/icons/play2@2x.png0000664000175200017520000001173312047445774020070 0ustar handbrakehandbrakePNG  IHDR@@iqsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100rJIDATx^[kevfa~c; s 3pbƔAPQw7YV]EVX.&*5l#UO9WPy=9yC0zƨ(EhB,a,aaB/`loo?]AҜy}g${/Ť;fX,ZXNʗsJ^wns|~5kzA#$Uc{Cf̺1|thC!qipW|ݼ\c@uKGӃ#﷎`+{N#`0k0ntwt[AXpP~ KJKRGţ506<,0sG].*njڙWW = ^/,HKlyt8YOrx:iwR|:cr8/Ņc(n_ ގAGg'eMCw}HNNKUm:\qǍ W^258vp{ vM bQ0ʶ ymo) x&,\ ) V44M~Jl4Er4DWy 47OôLBZJČ($[ԜҜ%xcP,Ř1"DLL srȿU:'AL*":qˮiޚGIJ'`js3xߢ1EC_>`EXbwwO`6wVaF= ǏGQQʫ5pV\̺ϙ7|͉㙽&[бdN%!4"Zb#p # ݈? .߄o]xʓx),{NY!&Le1\ͩ4hɕkxjB;ک DL&TCP%r4`B㦦hljBcc#Xpi!nB'̻/݂>En„ V{*7e 1v8x5jm&SQ h$!&wDH]y's0Y09ŭoנ~Lj|=paQ)aH4kk| ggajף^@E669A1W()dya>~<3abֹ27K?VǃǗy^= Q qsx&S3O2h+@- T%7bĪ{ _]S٘~fTG1杛~Xy-wS&;7DQr@xiNr|h|͵XjkTIJ5ݭ ""E;]«QUUmsbG~ x=4}yz ,=~.]Oބ\|WQVV&is7bժj PA >PǼ;.>u) >6sNax?Яy#ۅ /'>x2g܏ `G1KFI3W_q&TIPYYJTR+)'!A0.[Ec^˾Jc 0vfSA3]=ľӉ1^{<ً%a +݇CFFTk.Ur |G]rq , \4o>e)1 fof+|oU#mѸl[mON> ²n'CXwQ|e(is>l79eƙO毬EQc+(# BVʇ?򖔔EI0PO1t-[3|e8hA!FBGV¼ºnMH+dODBxhΆPUDžQOȲ*(,,x3y1v'Bڌ_Nh"k`Ⱦ>כ$BoNxb k>U=8 ?y- Ve=FqqtaDq[:ҞBzLb+/;1L?܎ &xAᠲ^u6OB^C-ǼH5YT *D`9by΂}U,ddf\PAǵ R о1YBtn-g(R= =WDs1cƀE}zF:c3jɜ 5⌭V7SESR>~'= {@vk[:C3yh'tlGOe޿| "_=Qz$Dǫ9;v,}HAIHMMEjZ* DHöu}&kէ>w~XI'b"BB$ aN/5,oDFU[+gppQ8R`M$ğ d+@` 3ȁwK6go˦lx΢׹w~3}="`HF !5S$y]\uX$*p&NL}ɖ9=sL[&|[3x> Y2ۚo'b8H=1Q'%1&p<6|TX~Ȝ47܏kL '$)A`Q$sA&ʤ~l϶4*>M{$g 1P~?ٱM!ȊFD'qUC8^*A:$VHuF:$]2q+M Cj5:odp>Zg$$$Xp,w%H<-G ҄Èy01 [&¿%]?JyFGH|w箛#`1"c(W{ $LH"E>2Ov8Q5T%O3yG>6^7: 9Oɼ Gh5ofnKB%"ȩy~󧱺;H\F|hᷰF!5P8ܚKB$+q_//-&8GDDF7m6+X[KXňY7AºkiNM9A8{cE)9[3uWXg?IM)6&&V[ukG+Tϧ@!F[}>r/6w欸\]q|hŖx!-Dۂx^}Dxl(AP:=֏kl8z~hbԜ"q<{x~'HSO=Xp-u2V?ٹswy'ow"hjÇ?o߾u[lMa͚5?|wߖ{gsΟ?ԩS?~C=v5Xw{p-0=|ʕۿ.]ŋ^pa ܹs+E18JXLkW pM\Ͽگγ ؍Qu$5IENDB`HandBrake-0.10.2/gfx/icons/minimise.png0000664000175200017520000000200612047524070020255 0ustar handbrakehandbrakePNG  IHDR szzgAMA a pHYsktEXtSoftwarePaint.NET v3.5.100rIDATXGWnAS>(.` 7Ƙ]R|g\IUw0 R"C w  @u\UC|CM<v9{4G7|,Pv:6<z@Y|Y2<_Vd,+黆®;9eYz~'4=l3>2y[ۗ{=`۹]Qb@]A.ͳZGkz}!rv[-B+Z+:5u<>v¿6eZِS;ڛ 3r3 A_#-g=ɭOL5@NO[Ž[wֳ 9`Zr˥_-Zp4Hoz:o!9!CXn|yn[}^#u< !ZΦNHz6 Xbfa693{00ۍ'¯ zW19I&ȘNo|2 /y._crtV<9iyZpwwGxC9`4~4n`xŻ.1G% E齚6賙a9nnnW ONZu: Yv<<O^ G>}kg7>z{Afa.V]9IzjlZҽ9rZe,<0ɳ /5Q[ DK-Y]f;nd Kt\9,y>o՚ 0ShghbU} eIj;6w~޻oqok_9QkPIENDB`HandBrake-0.10.2/gfx/icons/old/0000775000175200017520000000000012535641636016527 5ustar handbrakehandbrakeHandBrake-0.10.2/gfx/icons/old/RevealHighlight.png0000664000175200017520000000034512047524456022303 0ustar handbrakehandbrakePNG  IHDRatEXtSoftwareGraphicConverter5]HIDATx Dr 'RF!^A4\$H"B'ydIwpC1xPsAƱ՜@:؝bKST@!^.a d{w_/a;om`+IENDB`HandBrake-0.10.2/gfx/icons/old/Info.png0000664000175200017520000000342612047524456020133 0ustar handbrakehandbrakePNG  IHDR szztEXtSoftwareGraphicConverter5]HIDATxyPSW[EDQVJ- $QQQ QA[v8c۩t:Z%@BV@@ R[Lo޹9Wr@pQء{kBi@p #%iyD+7#}&7&Do{;_65Y0s.:X4!!WU~ &+r},2KC/IOɳq>Q+5/=z[|d$ U#MğjٳXaĤ5[8^HR(]ò֜K4pŒ%giDpK{ea56g[aI>O qQp;,jeğl@ ŭiLL[ڎI.Nk;WC\ &v2`m5S)|s X_5'=Pw xJ5m\;[нZ ={sk:E$ҝOŃ38 U{jDł=5]ͱ0i))x'=S#|I3韖\#WXB2Ĥwn1tp"a[@6s"4N:RvQC`zEۈ,M &hg-q;v4aJy 34mҨÚPY8#uVFST(W7u=TUAL3O GQƨ3OhC}u9oݽLEH@ZU~IG 6!K ^#dBF H4)3F(}˨BJ&rU[.wWvDЎ :95#^i)[id@kEn-"%Տ('g/݃d۝13 "S Pk1&umBZEkw2@9 x΃hF/tg_R2#qcҽOn#x fl,Ü HS!xg Ga=Ԛ/>MMgQ[TY_ As|}.QX~)_ߙbB-љ&dA5n4zoRcLMq]+}^Bo U}LdU@D xso)᭸ "~;>IJ/CR Da[[A#͜5gnAHqܵObIt}"dE0)2%fWqK 0"ìldK,^W`tطYWNN1SD߽*L}ny/QB "#FMs–p&p[dk۱CaWvJ*[1u:=<]\\b,1 `6'GBx#ڎKsp GlHA@ XKgl#01P>!ВyO&^ό?2TIENDB`HandBrake-0.10.2/gfx/icons/old/Delete.png0000664000175200017520000000034412047524456020436 0ustar handbrakehandbrakePNG  IHDRatEXtSoftwareGraphicConverter5]HIDATx 'pWQCA@W7zP[XeЌfpR-ʊ+n7TsP$y йphAyAE N'$z 9N 논/&< P^`"~7t~s9\+|:}$x Dw"bqKc{?3  gϰ HV6$;'͍yy<|X~NRZFFFVVN^^NAAQQYQEYUEMM]}*M-m-<g`hhhdx9`A]gooh? `F"100((8D PțTj F%$$&lْum))ii3wdܙk99{ݛo~8665>>1Sԩ/_~m.qnn~~aaqwz2䏠 Q,ll(.6N p`x1X^>^~>Aa1qqq II )'- ) #/ ) 9ZO__@hҚဝ:;e\!= }||! 6l`Xt $$4$,4<"@d4L,Lô >>1 RoOOر*?xX`ybA[taeV ـ Xl/a$)8$.6?v-e=qe{ ( L v-7@J<̓rIeȲ3ru[(|Wl¥ҢGA]yU&fmaun>L#5=kMvi6B6K[+ FBMvxٟwH"N#\]֋raFϾ~ kM27:yOI!{²Ó"b7EFR6F-)ը21|4$m6v(=>L-I䭎RR&R;'g٩վfwAvb\żKe'p:Q-=wV#EG],FO.;93N./,l9xVյ5+k/])z7^݂y׶!ƻM:7g=jk}#؎.^u+Eޅ uMDK2fcV]ݚ(V X 373pik0KI\?@=H F݅}ƒ8bbϒ)BRb4V6uqv)vWk(j7+G0G'5gg=6WI4.ax4xx[X8_*A`@`PpHQ)q[YRI_)TTngYܔUdE+ %2DU;5qIu?Nh$jkhct:'t36)5FEMY;o2m:n6b>L`jzƆnZo/ho}[Kćï¿-`~<50%?j5rXbTpIf*)6мE )hku2"=,Qհ[:;-g0:2/iۂêEE\GS#N 9yԱiK՟k9)_~sv]tùFI">?k(|1w?!ᅑq3u_ggY 1Yg{Ugf_"~ w o2w?pS~&=wMG0ȟqaXa9ށIѬw;ЙegП @w {rapx|aAe1e)A"^V{llWuPGY\\$48$!vl FUhRy'Z#!.!#1/L#M5SVPUWU~A*Ҕv҉篮503]nmzÌNpBY\[7 wrq&v-]3po#h4F2 :nQ)T5NpӶG' nKjzCO>T2D`=*iP߹-!(&!2 ߸~UՕɯ},ztֲ<{H8BVT5H]@k A-o.!"y^xC{k&0r*=)+l Q"Gݡ'0jqHn 4gy)?>0l*/ÍlN) 0C]iOPglR! px|2ݙg'L 60>B uz|;)EQ >tS6k@|4dLMy|S<"Tm &pRyP% /"eNᰂ($GD!iMrdg2A,+ϫ/1r F0$3F?3]5L(lK@%S+ J9"L0"Cu U<U` t' 4ysP%E*MĽIZwӖw k)&TI=nOTqp&j4 F:KqQHv95D q?).zkr AD27"QUqз ǵkakdQ0W Y 闅=ml/Մ8YQO뫅w)]wXaV`(7*nLN~A̋WOްk| n䈼!$7[Q2z*jcq>%bx2يÄk=paބǰ#atAySB&x̆tIENDB`HandBrake-0.10.2/gfx/icons/old/JobPassFirstSmall.png0000664000175200017520000000036312047524456022577 0ustar handbrakehandbrakePNG  IHDR ObtEXtSoftwareGraphicConverter5]HIDATx˱ ao @"t444X%ճI%">(s8=AP˰wuL)ce 72|>J7t_! o e8^hJ)_ᯩ4;Bn4&*IENDB`HandBrake-0.10.2/gfx/icons/old/JobPassSmall.png0000664000175200017520000000113412047524456021564 0ustar handbrakehandbrakePNG  IHDRatEXtSoftwareGraphicConverter5]HIDATxQHSQғ! 6ͥhkՃ& kC82$—Kn aAEBiBhX(4J"H z _~spxM:qڛL&UV^=A2tkܙZ%{n=##z7Tug g"EM9`5[!- _ڙ SshK5LF1.aq=нDdX_<^?T)ع C? >A"Y}ӯY;T)9"fGX zW ph®t}(+a(ƠK[W[U3%%t^a԰! =q>F{Z%@^a3)+|=- n~9ӔWw? X5n2wcQL)QFgD&P52[z?C`\ع%<IENDB`HandBrake-0.10.2/gfx/icons/old/Pause.png0000664000175200017520000000326312047524456020314 0ustar handbrakehandbrakePNG  IHDR szz pHYs   cHRMz%u0`:o_F9IDATxĖ[lT}fc`18"mhҨ6j*Q ^H HQ*JV-M6yQz !}AMB 65!:393sl1,3_Z_ۨ*w ϾcT/ ٮFjsQsJTOc1m6fKOf5wKޞٶUM4Zmby& cy؄c߃?}RSSYBW9BknVsWI19ymέ3>kwm?svE<3UۊJ:7VsWF9Á{~|| wxS jYM\S35ut2~"<?w&>o};1ŹL.N|*"h'`2XGwkR>#{زgF(F3hHv_qV@dl>9?Wt|=7geo@n6d*$/q%eGTkva:$}9ge/OFܷgmY^!ؾaz4 Q/TT +/< .vR*.; -Nѽ^VzTDE3I+^"D@DKeux?Rr۱֭?ƥ$V O/x8]fb4OSs> 8D(j2%OkB~6dҪ`HdA@9.|G59_%iI4#,)k^]RX.b!@$ɫl4L@6K5z*yJ63h)%QLT.4ddާqfT"rA!\qd~Z=53SD0",- ^>-DMմUH՞]&ze gT]e+'Ώc%,X&jwHZ|p)%@0rʉ%e2ř7ҭؗ_?riT+,T#M~tbLKA6x&NƑ>7UJvgg~wx6&ZY^UA(4(^=Wf9CoLv/ƇVr>?yo,$j1 :Z^42oUV][ڿ<Ρu[fUg3UvS2qz'\vG'̾ t*hVtt~|A>&4#Nc+gmhߛ6a|(6mҽhH;E,)͕'*,S YA̒eBY6s!.̰XW@r<IENDB`HandBrake-0.10.2/gfx/icons/old/JobPassLarge.png0000664000175200017520000000254312047524456021553 0ustar handbrakehandbrakePNG  IHDR@@iqtEXtSoftwareGraphicConverter5]HIDATxLu'<88@B䇝rrw(2$ihm& LF?khf-(۲Z[2Gݽ=yp7^cn<=yvp6l82x=Ǥ;S?PL|,_`4&'%b;6܂u{Ge'/-ݪ&(KgΗvZk |${hʎ -^󻼚qlR#j!9(TH*h<eccD,Gσ񏡠,7"~k^`8V/Mf*< BPiaB9 s|FFƈXڦKfspra MAv;5IX|7o y*큀-J,ׇXH?[X4Wn'+J\g4_ZS/"] [Ƚp@ZAĖsA΂+J/׉DžSsU"5l\Qb`2ޣ'AnQ n^œC.@ jmpo䥔&j9j@7tO?\&9F-Je5ϏC =AE LylEwm@Ρꥎ[J[}m P5h9F >bA8 H)`u,PAP}@,,R)B7NIMrxV s˷^7@XZ!! bX@ ` O_ -sGv> m@H M+>c좔mrmC_@!0 %wH0$B4dھ>,YP'(5CÁA}[Uu~c;,QBlN0m@g;')j"̠VC̊zOpp0`o]5pbPF.nHmmS((X*VSk)#⏃f ()IUv["j@,DS* 4Z G@4@t|(:N<($|uT/Z-J¹# EnMw {9wUΑSG^w X4 5H)7,tCI$ܽ',b IIENDB`HandBrake-0.10.2/gfx/icons/old/Drawer.tiff0000664000175200017520000000416212047524456020626 0ustar handbrakehandbrakeMM* P8$ BaPd6DbQ8V- D]#O pZ9a/VES85NCt<41{YxO*U8:>Gjx_0J$@5KeQoN nCuU(d6]M=CGD1kKr /hl2LO Su)hl,ȖW?I7m|\2rJHLE4c[V]ܵu5چB0dA@+ZF3}axH8O[(_%#Bf;j\K&wag_8V7f]-KK0!w|4]vI/u!`O5"qgQo4P/+ب 9u*Htv(GZ瞦G\.}g(4l,tGl^igzsVᨯ;G{a_z`22 AHnD7]:&O.t@ CBv; *A+ : cA7 A   =RS P8$ BaPd6 D☠]#tOpZ)!'VCRR>\GYx7AJ|$@2%':}g0T'`PFlYI!٫g`R?Cպw @E~Ѫ .bC6e{p]!{H-W%@ƖB%v;re2ۜ h=&I-zvC]uJGMpS!N'8?"H@ҩuB 7@}|8N#J8JX0Ab+B*Pd0 /AL;H(7 `Ioæ>2WAld,C-9=2$DI4ɭ0Q3`@Y27 RBKLɐ02?RRO=T5Ͱ8@˓/32JUD < \Hm4ZJ0 s{8KeWFJuGa7Qu# lKVq:5ԟ 0rcP%@3PHpoͫ 4SH{> 4-eq jHaqGwUp[(,.}w67(8@|adcՙFvl0lrתXwQfZg`2" !Fpvn [~ gʁF>[Zp:]ȁ~H^Ăm; l[2qO bU=RSjHandBrake-0.10.2/gfx/icons/old/EncodeWorking2.png0000664000175200017520000000053312047524456022054 0ustar handbrakehandbrakePNG  IHDRatEXtSoftwareGraphicConverter5]HIDATx?A 'P뜀#8B"*n::%:ʭ[nٟ1k0V¾%7yH %jh*l5&7&F w54\ @bTI5Ii"Lwa B{1T.؀.71I ]y3 BOl"JZw 'FL&E؜n)Q>4SH7D|3RHs~|FwܥaX&hp}@T*~+ K:IENDB`HandBrake-0.10.2/gfx/icons/old/JobSmall.png0000664000175200017520000006573212047524456020753 0ustar handbrakehandbrakePNG  IHDRasBIT|d pHYsNtEXtCreation Time9/13/07=Ftd tEXtSoftwareMacromedia Fireworks MX*$prVWxm0 EQIa͔uL(K74 {@H{h_?^y1#*>f-[Q,=^*-UT *g.Ҥf4h4j3nXP]wU|[tE/KJޖl՛[L7R?,^=R/E- /%aHmkBF)3mkTSx][sƒɉcKoVa_TO@vDQ$P e+J,3  IJ{@׭Eܝ/4`lҰIY[yvZhkYUkZGsՅ87~ʆ2Ql2Tf9͕3w`%Pr%Ε?3lN;knl PlmY{tա2GVN^f?`C.c思EFpĞ>X%?fl=uI9kejR vfGC L[ \Mo2|p' Pz\9<[$SC}@*$XQQ_D5}sӥtA[3 10[Vsڕa\ vEo Eq/A ŸrџpH҄fL\*_*nH!8<MG[E '@tXg)Ӂef4E6Dyαs8p|6B&/pw5:TU u>I[4tLEF"ԧ pݑS^'+t9^ר2qw/NVvZ@-;QIHj!-E  &5Wd;.kuAgLֈKK[1{KJA C K&k-__19!>2$ ^>pq a! %sb;^742M~ƴ1 ӧ~+L8 qcjR`&i('#N>N`*R^[nݴ*t(9!e!J %͉1tފ32@~x; X&Q3Rc!1#,3Bi#r`qP'M sr<7Kr+7)fr.)J< {f26Hڞʑ]sḅMaËp@R^QJY1,cz5X w(|15UK5QӐ ;>ÇT/TDMBd#8pWoQY[=㊑]qbJlJ̣\-geijd ʣ ?VD%Ŕ5-UsL %/Y"#a0~.;J)+2'Ң-pfDiNi› E,?ZR^ulZN[U"GFhD߻p,2. 2gʤoi˜M0UmkDM&M 0J+޹$1Q8I¦xg*w\Ѻ~Ρƒ MKEkvU鍠ToRNj⽵[)jbD}}_Al Wb4FM:@iîF ~igZM*̙Ni\iº}-HYZkӥpײ>;ڶkf]$63`V$#?X[>(6Q@eK DjIH6Ԇ*YdƂ.)xcIBUiῌ𠛿nC`hkKۓy1[N.^l衶}T/hrN~Mu\m&Xix $/҂r-/VesYs޽Nf MV}ZgOys^3}r E{wes0yw)_K.HpnOq]K#wmj~ vJX+jE}:Zp߆o9i΄Z;J?BSiMGUt'ط )}PW+[hj߂^A# +((z)wN?3,t}Z6uY%Y%Ӥ.}rMIҧ"J?K_&H^T:1JJB@S־BDڞFЈ#FrD& ։8-'k3 =bbZNeHY)?ϊ;_1z9yBy&E|!vgMNA~!;B ܏wJ+lg|U~yݫ{"$/c 5 mi3y8(zhčX~Iɛlrir| #ސd5ߦ^yp}}E7 eN}AMfq?rm^ƞ+[>ퟚ˺_)=0b|gM*=KKncSh\}Ƭq|2qFھJX沾o΢u:@[OO*5h˼p4ӯ+koDJY2+Qf%|V'~aK>װs;OM_;/oG>FOYOmnKm϶|f\+9_*/;PI!~5486_?ŰWǫ//9N?G⍷bBjUoj WK Lc9ͦnx2_ގDWNBOa=}Iq9XΟ=gbZl"uV*hZ5^mz\[!#559[ѳ+iW$ [zisN2Yɶ6i̷ECE@@w4bo[fϏ>r)_)fb拭 a=ntUbZ fdgq$5mU(ZMޫa<:ڠyf2W׼ZL ֛dQq<ފU V('3фS&֓ RAq w*{Ԉ;@%pgth QaG{Bjp곜{(lNP˨5^6`h4_hnl%zqG'X99^, O拆vdaF~{F6&;:ѓܽ͢3fW.l4jF v cjL tw& 8쓀¾ j!ࣹ;>Xe6m,x l ]|2jǭfPv;kưc#|/VqVnlӷH#o5}B)zO0ͨN[Jź'jɀ:2v'nN;97 5_\8l*l`p.mG;}*@ - 8wނG&gI[ >o/}x`0Afqrءl]@ƤqcҸ15¶4յiْ{?f5Bvkp3`1yKjZMou"kolr-m,8}˶46pMuۯ{pԃ*chS,b+luJx 2|+G83$70qI?RmXp4nhO犭ff~&7-CkLxw{mkBSx]N 0҂WkQi# e*{ٙeT9&-Y>\yنytc u/$р_ ݈vvvͅUJ@4ZaE6HxV:F6Q>KSy.&{<}Ad:KN?2 n37,OMEN#HF`?- j!j {r+%Aqh $S@%%|9`ƅ}6.?jU 7KBC_{-׮8`Se[ lhF-*,?nt WTe\ÚMOsf^|ŝ}Em ;GFn^Kb[dAݜҥy_g:=h5=#*|3z¡F#$̣[^Y EO)ـ~Iȑ8  Ľ&C}24 L >bXXFkhFWI. -h-yU`F m<}hRvTf}|dT@OP\I' 0%U-b2֓yxd^ƣcAēwP)Xͣ KZQX!+}~CX@- >9Fwx+$@>,#Xٵ1vQ5%tᑄJ#5̖3S=R e[OjRgfM߉ZpE9%辴tc⏥R1. p8Ywh;w@^tug.XHT^?2!+tU#_:{*!nygn:@ Zgpwp٧P栢=ԇ|Utl7~䙔@0 +h%\'̊!Lu`tp/߹÷0!:lH- gA ^Z1KO_<*•G}|;n'e}*#(4hB9Th玡NŶ@%fĠpo9,vrdԬ,qcD-7= 7V >v`f1B+?`ؕ?ջ+yKV"o6TpŚUk}"bɍ7!H*3(Tu|jGyŪEe18-BYYdf[L>='4"}\x乢2EOcvxV|MڡBs%ȧԜ?U$g{075պ[=h`Iu7ޡ1t(f\%}R@uPA(@0Mz>n /:{7l6u]~m'3u/OOX-?Jk3pMNVnuS>q$t>NSgِ,yfzkf6{4!ڵ Y̳X?%ţG|j}~H)'FCy6)$.[xgߍJ<MC+QgPX 7~PT֜~5=4WaH]0/qJ!Ѧ?q?+kpB7ޠ">G}W=\&fH0e kƲ]N0Gg45*O xFD ~Pv_u9%|?vǾal*.p,S?5jPt~ۢ4$FMm3\9\fVdٿIuKk+s]h?:iĺ-^pYljkeA ֣;$3~:LH Ip#W,%ebbad{O`eE}9!mB ,ƈyAUkb]Nø ˌD_ˁj5j%u^׸ `O g9cE @=BHMlk@$ ءۖ#iBOOA\zXCR%ˆ6 _loNVL?7\iB#~_l5zq#wRf|{jSKlcԁt*=LfLQJ_Ke[LE(EU0"R4hbR |;i@fT,}X?GBP7A#Hk2dwըU¢ {3eKxŨ!tϼѝ;ZهMK+2S:¾m:ʋT,شt|H>(;:HfT+_oG=3fLál,0K@I rHmD-Kɵ""7@8bRz=Dmc CF_ŀT-DM2_=Wl@F䴦"%b||+h Un+z`Dv~_ClO$>Y Bcyg |rp]o:8khۘM埁/W^ֆE^ls wfY~(cUSM6:-=j0y-AgЀNN2g$>+y| GA]cپ6m n5v{y4y:l<5y7/CBqP!L||R۝I׷Iw|X\Zꑽܳuu}1c%(Hh!!&>{Ny! BC~{ Uz-b i>hTN./hY}_sPp͠ '.D1bԿoK'mCn0H]_ _&kdx3{G!~KdlfPej9dm5Zq[nJ;#:u|CC-y Oo!N獼7;tYW[kcd [@P {q7wΎd'>{*QVؿMAN: Zl]ѓ䯹c[}: pz$1 U(rtH):=)_]2{xlu:0'Y0v~p %Nj #o^'X՜T¯-d'dT{voƧ߇&9ގlOz|lxq!leP>ޓ 4lBޞ}yơ$9Eg/>_Y\j*SCş9Pdo/g_J7wG`fע4>*04yWĉ &oD93mϻL}5Čގv$OEA0h61s_ qhL`Iw^%M2g 0=f_v fM7R;IEJQ04Ys N[4HWR99s_;o@O7 @s21FQyhm`:o_ π7:zAE^,έx!{>!7r#{77;"{&/0WßﳠA[yυmk}ݲC,NZ M9@`CtD"<p& ^K_J}GR}/ +-khO.ЙZ"W 83;&_ EKًH|!GCVyhKj-үP~Z̚lpyN7;aACNh }p*}FZM8oMGeh ŕW&;0ܕ +$Dz sD %0x/{gݷB#cpD˵#e"K/) !Y#̓Dlg4b>EM3T (9&p~-'GɑlY[?E.`ހM Evn4s<{nҟ4z3ƿM(p4x։m |&x%Z@({~N1qy8FPjDo6SXyhr :JgǾfN&b7Ԋ{dK<ڜw#sںN}xU~Mf_ 244yYhtvш̪>Qc8{Y oߛ|u߄6W(-ߺȍ# n֚MPj] Mp# y}XK sSU>q݁(yug5s3q !{;ߓ"4dG _4>7IH4(.#B ؜cz (#}%şnMC- }|[ϸ%6Pe>4RQoZ ]r;^@UofJ_nnaL3RlP?yרfchRr$R΄"rKG,-cJ铞\ੂJq{} y+wܥMYaq$xH{h2؂H!XG-Cz+Ng÷M#Y#dnrjE!ٯ.Th";vv ׏I42'ZJFBѳ$.2n;ӹtw=xt劉f>W:m+zl<Q CsBL@G1RB9zBG/u9߱\!p"Mj} ӆr)nST&t`&YfpKOm5oI~6;6h'!aG^drE% v <R1%/LPskK\wIΌ\ӀxI7Jb(i(H<'ߟ-Ԥ)ycNŚ:V J>̛jATg /d^YOCrG:ic;CʀW4' hJ7_sQL3!Mۯ6Ky{⼲q 7x˼`[(sk349bx7|q T zըH?-m䙄3ΐ{K@lg)ݺ@ RtiMd\zK.(\rZȞqp~M`d6 /wOd`.HyEϼ zd&<ʉdd9}hR^օ=W"3D_=,!l,ŏq?E?BІ^^c] J*סIq+\bjō 0}6?t6Gл̂:?s%V/Df= ~4_ M=? uA? L`R.[ B:RZ^A)D(ϼRSJu!lꟆ&/Λ cM}[cXaDe(_Lz"oHqcTxwt]8a S!BɄL}XJ" Y[8—>;r xDv؅7Q/b .=_U$?&Y ]X>e F€"UE,hi#z@7Hzc/x27m0"f\0Ѓ>#au Mb5s$Tvb18#{c4+Ώѭ _'2Eeoߟe-2$i%  MrNMAN.9$Dayj~\l;]?B~^Ng~5/Bmu%hk^BiJʵ+w @F!M*yg_8,|:ֆmWàc 3FGu~2'uF=!"ct8/ZԸruڐGtXCg5ZbKZbb[^V ٤)E=+!KΔ\C50!xV~&,~"1>ύ^/kdCmBb4 rQ"x D!W'ܻ}QΏy"Bx|31H.Ͷ2!qb̬?M!?q9><-lW]M}m5NgO4Z{m݅o #CÇM] 2h2_AG@nn*I J<&ߟ Jk`:1Ϩ#{D+B2y\i1^_E1lAbBAnE FX0>>H1'H$K[~ l5n}*jV9/@>RL.$?m>EN$D'~.%8yFc`}{C;+@03Ft̋g; $O)O:f |"""yAБlDk#/{φ7$Ő; Žz ,^rp"~}5C|pKAyɼ"7zq%{SJ{.̥܅ؔNICC!nVzСÒ+e1f(G -/3h)I=қUbPxF?M;YͶTj^zWyr>&5zRnqO÷ My}vP/vFZ4Z#GqES:/]`<{f#y3lfT9FG Oy6uB%|@uf~3˙@}XSwx$"Q}S!T AHP!.Eev_\κ7+miy4qv0‰jdž?+o^+-Y>VH?uhR?wX[ЦKZ$'͡WF E c[`P[Џ q\_]R +zZQ>3jQÉ(cF̬BYB-yFvrQYc? PCt8|];_ svXV +P٭o)1[H>fo]HL@c9w ttW#0*-ZDx}n;,^y{Rn] *j^D;b0 7 63&7*}QxoɶR^dte3a(ԍʛ?ߚW♰\#zz>% T,ȦRkYp\V$PUઐuAXcg{ .uU69WDԶ,|=?_xb}N@;|P%})A?zR!@@P?t@Jy1P{mS̔eYZ^w^1*| ֊9L'd !P'hKh70b:lf'bYykxDm1mx*|u'8B@(čbJJc~0y7ŏz #9{khA׿Y&Y7NU6S@cFLRz}n@*&d^O?bF4k@~O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?YMwmkBT xOHeqk u:3:83Fq5beVb! Bk$tKfcVw"\tZ}}}} ^.xvm}|oinnNmmm0??m"韆ή7jff%s#7\Yy뽣ӯ 5*֘TQf)򒶷ɤٹ^­^PmWMv]hbrWթsX"Vzٹ:[V$:,K"a9r5fFRU5s#7"57厏?`}G*:*d}Ow3Oeꝕ?6s#WWTvO,0S)kSz~t=P1/z^N>Ril0`vn EXRUcT=jmե@%U-f`A<$ȍpeCPRE]Yǧ|[]׆Ux]YƆdxGaȍ.ݪ[q*lWA۴WqW13S*0EGdmr,4;7r"/;Szbwsisi\ƿSup:"gMϏogrz`y_mkBT}*x흍) q ĉ8D^>׻gI@XjjgiЃ`0 `0 ?ϟ|:seQ3|ӧO|:2|.};7eGFO6_Qv]T]^ˮg{>pjzkuo{yye?{-x/ D:3D&򈼹e^Hyi#/OGzϪ߯_~ :sMe#M3Y#=2 QЙ[\s=E8}E>GȩT ڲTg-}VfoSVwzV}./>~!?U1<#}=F[ ~QڋBN..+푹^edLo+[\-k dW(}6q$#?z6Bөi?L7!3O_Q}Пuo[=tkȋM!'}/Ƈdr2_Cﲨ: `0 :8o=+8-4}۞cĥXdq{bUq©ήm!ƶg*ΪU\z[GA=^+ru{LV U?)V>ғ)x|Yҁgi\yi^cUo*= !TY?rfgWsʽVn*VX#=Fϫ+[F~yH\L~[O҇h5ݵTow|Sfӟ+);F;:x )/OS yUo2e)Ve3'wgGg=J^`0  ľu kU,Ksؑ5nY,bXw{ w&3QהNQev ]ƷgcH˞i{A3I8hwduwUIWq8I>+@pQşGcZ\ƪUߝ]/:3d;ɫ:gB9R|GW~w2;fzt|+i5nΟgZY|<1NyŬ|E7k?z/k><=Α}N΅>uWydʬdz `0 *\?W8GY:Dgcg< 2+'W6qn؟{ru"wU쏘~c#T?+y{Q,,^qF/Xv8.֩g3}ȸOP ~n%hUG4(_sn|W}Tg&x^c,Fѭ+ <#+}/Uw8BRh_|33!mr\7U9m({ѝpvew[xG]߱?g;,nҽow8]וb?OV=Z_#ve?vN_WrYLo;1g9pV^G~>[_vNOS3 `0Q[ veO\k^8֔v<Zbz\Opbn$~}oz3ј mK vU]^iNWA#x딫jt q :E= z%օq)CcYEqyRG-+u (K\hP'*^ء^q=m=y|Kvūe\rȊ4={W1;=ݷxp;o@>ȘT\Ԏ+C=*ɫ|GJOCW]x1.ﵠ9_Eб Vq)v(ʑ}[GwǺ{-oSdו_˞׃2;iT&w*w:g׭SOsj%Z[~_˯d֮+w]7 `0]kIu+eL]ւoA^;=GR?v쯱;<y o$N1紈=:ߥPVu< <&3KyC/4r)i=*/|Ύ^]QNН1qGw>ù{ ?Kv:A}E:_n+{u=rq͓̳]>>d}+|L01`0 leg:׺񶊝`W,3O?]\9P~[kOWiGc~)-<w.3q}'vuw$Vnv(r52S;Wk_Kϔ8B/hEՠ'9w?K;x:x<|@cϽVyc@ۖSw8Bq]=2lBe6V}eR( VeZT4ade2ޒ+nYBTqSߔ<[&=f[|szP)G}{Zׅ3n7jpWwftEw[ǽ;`l? `0 `0 `{~i`oLy>uoi\qK|}7Svu9G쯿c¾#>,jow{ՆݲL=mW2u_8دjo?kD߱mw>#}E:OۡO;y`$jjT mkBT}(`x흍8 FSHI!)$FRHnw HYx3ꇤsaaaaxIǏ'U{o_ھgW9 o'GW {>~Jlo߾)*/N\ϱov[iZ_ձaJΝ/:6O- 92b?Tlk%?_21B sY5>:>c=1Ow y^- ڶ,XzusM#גU]>H_yYv!ۉ_mi Rus]Xm_g)YY)m]y,m z1aaaxEߓGקo/Y\k6xjgH|yu.\aæM&wk#ϐ$?]Mo\Ⱦ,/ڥQ@~6s?)}, l gX #vQg Bٙ^uのuhm?}{].~}v_J;xogJY]޳@.)oqC?}>@Xߘ'-(W? źvƔOʙRv[K?[A}?-wmՑ}g\=c}M ggg DŽ-B^k_g?F? v0||؎=ǧHPgs/hؑI t~{n^}ZyD5XWvO)"c0vY Z|~_%/,p\ɹyΰZ/;/xs_9?Pܯ5ݻ\[y|č8gʱL{? 0 0 _k3>z_\S |<)b|7aaaxn.ta?l^Cvkؽ#~e)3<3^kdlc&jK+o"e<.ʞ`^(3zu l+6v<ï k7]/lc[`On}򚄫 G뎱zt^v2)?;Wmr5ocIz?Ozx{&!ez."ѯ 1Gg{+ҏlw<=}GݽFƨ^)zIpG K֜{{e G12ۭqiumf>.}~a? 0 0 [u+7Svq֭y΅ ?ނ}XwŶv?ߩDZۓ-q/?߳=<~#>Fk"qzrQo 9r,nY[;o:)@-`ק-7({߯S@µK9֠ɸ>:n3 _[_*mtcmC>qSL=<6;ǫsaaa{xˌ\ފpx?0׋#5zяc]x^l򼠕(f:~٣^lin59W~\;?vn6erUbS~v^U O7O(|;+SG4|?f*?rW~2oNٟS9~daևmH6mX[J~s.ym4ٶO|Bd/b5ɿyU? 0 0 0 0 0 0 0.P~*1@G\⟿KrKXs2(ߥ纎J8'>X@▼QQbqwx b)_K|v 1M6kee-2Ǜ59?K^E~9ϱQﱮYF8N?~;:=J<-tĒyNAgC \NXKs)'^Kg\~2}6}Գ)n]Or^j~"{p29w6/.z-v:+M{WJYZ굢`% Ҥl9ힶկ#OUz+U?;sd~vND7*.Y+v:ye;8}~|+ÑޅN9}{Bƞ#txխsXɿkSV/uJ=o G<ջL'L:D]6jfgLz/+ؽ[{rCMYq~[{yy czA;w9zszWHVax3 fmkBT xڍm0aaAXAtNuө~"B~v(I$Ic i#Gl6p8޾s[ouv?Ru߯+fԍ^|}m43ϭu!Z9RUY׽B,z"|~>/|RתY̞EGߔ뙷L?:fV\꺯Pv#R>cNb=^Fz=yNI}.3?3̥*Ǫ7٧ڞƵ2~zWİg#g&2yK;wʞ2vm"Ggʼd~g<[şXlk=^k͞Gy\ׇ3M^&}V3=3ڣ<_(녾v9> G@Sǿ99Gy*"QYu*lz;{-/9v=Q_s>c),k:gQ%ԝ,SFwA2vWcWqlׯ[},g7F}.2˘ڳ8]m (N98& ; E $r|B |~e!Nx*SmH߉O>KJIENDB`HandBrake-0.10.2/gfx/icons/old/Movies.png0000664000175200017520000000432212047524456020476 0ustar handbrakehandbrakePNG  IHDR szz pHYs  gAMA|Q cHRMz%u0`:o_FHIDATxbdhhh=\}}}?L Xhiiss ]ݻ(w@1RٗZ߾}Çـ322۷a% B7@zfggw =з" wf[`3HII1pF 2 Hr@gg'?쀖ә_ׯ cæM֯_ɓ'3ܸqAK[ww Lb$—V@ܸ|0z1.Cll,(+Þ={zzz^0kLϟ0<6f\\g͚|b.D'WIII6555`|2$$$018q(ΰ`|}}##>|!> L{!suTСC wa aHKK.Q QQQ 7odXݻ ǀ!ӧϛ-^tt@hnjǰ~p\^p [nVAAڧpprFիW$!<޾{}qΠ`LMMM  ;>?cƌ̺`/ k^~>FF&v66VVVZ`JK߾}?0x` @M0{b9Hd^LTA0ӷּke"A[$2{@XJ $ JG1+Ί+Ud К"iz%a1//%Z;IU גq2Խ=1nDL#3*A}\R@ s15q3;e-O8īAȀᓠ6? P?_8=c`ovA% _d0Cb/#3Ov!hW6_c`9? ?00 Q@021|!;7 0s1}g`h/pE/6A/lh Pjx 79o ]C^ ?xt e/$1@|'߻54!9 P V So f6@2d`e; f _٧N @(! WN n\gA G> hqC5] X^xսd)pm 5OEc &`B6a52@`Ԃ7Y@r- JXC@~v჈m g b  0$o <﯁ gj J,9]LAj_z0p|zfuge"#@@(i?vif`qq,# _`Q\ G )r30@$?3XH勝۹j_ 7-_>p^e%&`V:0jY;0cfa`fKVP PC( fs@8/(h_P .ZdL@10~.3~p?`5A€V1 RA (յUh6!IaWt~bH8<0-( _.ُro/GHݑ.NXr¨*T9 M?7f7ZhF\^e׍yt Q)AOփ<θYYH(7C4o=|d13 (_ T=8>!v:(PAgd$1`atސ?f`a @ *4J3_5&x]YdX)056AaD Y0 3#a&(FS E7b$X2h }"IENDB`HandBrake-0.10.2/gfx/icons/old/EncodeWorking3.png0000664000175200017520000000053612047524456022060 0ustar handbrakehandbrakePNG  IHDRatEXtSoftwareGraphicConverter5]HIDATx1Q@1T#8A($TV: R%Jg{EN';3ogۊ? ^-m#[JO7>]9O3Q=? 6CevZIq#y;/}%qAJ =krd :Ql#fN>9jr6m l bԔGL"AE#K, "ERA]bcW\c!!ߺC*)3vCV5IENDB`HandBrake-0.10.2/gfx/icons/old/EncodeComplete.png0000664000175200017520000000047212047524456022124 0ustar handbrakehandbrakePNG  IHDRatEXtSoftwareGraphicConverter5]HIDATxb@ %Qp=1cшqWs9[_06}P g8:Uc QxBn@N/zm :XS; % Xl.؀鹏'@?g_`ͯ~>oy\f@pE/>_r#$%$\I*d *|IENDB`HandBrake-0.10.2/gfx/icons/old/Reveal.png0000664000175200017520000000040312047524456020446 0ustar handbrakehandbrakePNG  IHDRatEXtSoftwareGraphicConverter5]HIDATx EYE\[x$5\5q'6UЅxrD*IENDB`HandBrake-0.10.2/gfx/icons/old/EncodeWorking4.png0000664000175200017520000000054712047524456022063 0ustar handbrakehandbrakePNG  IHDRatEXtSoftwareGraphicConverter5]H IDATx10 E9B NGHH nЍC7;!Ǝ %m%+Mm==HZK4n7gu$Rmӷ)p^ gf%"mg> @w;l0v;cNDāI XidT$MT(0sEl:/ACy (r1Wmўk-c)`LwD# v${h|Zx9mQ?J! vuIENDB`HandBrake-0.10.2/gfx/icons/old/Play.png0000664000175200017520000000340012047524456020135 0ustar handbrakehandbrakePNG  IHDR szz pHYs   cHRMz%u0`:o_FIDATxĖMlT`c턄I*VD"U~ H*,DjՒtQcдـ % mbl`{sxoaRhtuߜ9sr??s/Ƙ'Ÿ٭F{:j33jߨ(_sJ6vs˗^CjpWG45Q[QOe ]$W1>B@);Μ<ǽmDtdד̆"~55ԕeu >~ϱ_=kv'/j n毱9 Ϻl[WX:os3'=^qm/kedq+Y]b2jPMT32~I_1iw ݑ}O=X`S&P*"h`"?'lhhfߓjU>:FӈhwWtAl.>?[6nϽġΎL0d~#O9[ ]ZvDu@L`0AgGg&rŔKtvo;P]Ye| bCI؈ aRXY3?ĨIT8^&%r$ o|vNԆUٸRt . Аm28~Yi>=[j x(2)Œ= )2ľbG62 'zk-ZYK.JbTxYQ0AH$W巯[+wwrtp )s IEO `,:AkAs8!|# j( ty,G9*j+ad/F@^P@jIԢa8ynʡ[%ɨI^S$`࢏@ @z+zi r}+`}+(ҋaU0 RT`1zo`"2@XHܢr'* zvS'ܠZT}6Z` `*} Vm~HAY`9"(G33;m yat‡Nxj~f XFpk@u ^(,j}‡w \{t9t[c齑Ov.!w@.Bzu4&;.bQN+KB Gai b s>AVmcYSaz gٶBBXg*kyȇFB 910L܁f e 0 AB6crBGiDw"0V 8 L A%!-0na%~k@Z!ZE^͙2 vcOc l"kCJ.QclFa qPUdBkFa wG d CH#0{$p#\U.83 g3E>@^Dͱ(q5` \ xJE- j2YLI^A'\9ptЛ3bilA&  =RS n P8$ BaPd6DbQ8V-FcQPgJa8\(de&#{b?J$Y1bEPD?@Ho#UG8P'hOsE/{+` ,c8tX>Wh.bJVp&iW f(>L6! A@$ST@ @LJ%RP\PV'{)PF[ iG#b*XdjB惮4 bAt`y T;jppRvh3BPxVgy a\t dr/`ja~%8 km:`jqtY`Xx@i1A@vP6!PK!w@*E!t``Js .l`e1p@(au!/P6BogWGj~pH -¥ j|%  IÇz~cXݲs=I w q:jpbfxDfP<RK'l``k*h'1@&uf.oD b`@)r^;x||HXl{FQh٠þ mVg 1@*tCsg -Q6ݔdn\ @!$ॆr tžGDܖ4y lTIENDB`HandBrake-0.10.2/gfx/icons/old/Preferences.png0000664000175200017520000000272212047524456021477 0ustar handbrakehandbrakePNG  IHDR szz pHYs  gAMA|Q cHRMz%u0`:o_FHIDATxb?@bb`@ b֬Yg1>f@,4>{lX2{JJJT\FFFX pHYYa ._+ 2d6\d@Ds6nC9@f m X ii`E ʢ s{{"b555rF0F &##"0B9$##@@a-M bAa5f1ZgbA1 !l6`D;xbA$ (b.]d;?H ¢ x9b C;y珟ial @(=aq}"/߿000S,W n =@,?$&?- _F+/_&0"8Pn 8` X҆8FUx8>dgϢ(+×xyyA͈C] f0| TL ƨ%K>} ",N035J"E5@KBP,o0‚  w1HJJ2@#D$ #@1Ȳ@JTAfa?dx%0[ς! K ;` X 8@YPs@6 3@@&Tp(A-T/A?Q"rgPQV3_e) ~IӧNk T 8sCs#yT <+AIENDB`HandBrake-0.10.2/gfx/icons/old/DeleteHighlightPressed.png0000664000175200017520000000032412047524456023612 0ustar handbrakehandbrakePNG  IHDRatEXtSoftwareGraphicConverter5]HvIDATx !M(FW! Q ;MDZE%UzxhDq8 XRcBv;_ wP~L[H88BZrvdIENDB`HandBrake-0.10.2/gfx/icons/old/EncodeWorking0.png0000664000175200017520000000053612047524456022055 0ustar handbrakehandbrakePNG  IHDRatEXtSoftwareGraphicConverter5]HIDATx!0 'rŽALL $pLN"'A">J+Xϟ4"AY}ƕ,md%ҵWKivN1O+B^'&f*R판-BN+p*~=9ާiّMKa Af#ڤ]6tsC J @8CjTsR4SgO00_IENDB`HandBrake-0.10.2/gfx/icons/old/picture.png0000664000175200017520000000127212047524456020710 0ustar handbrakehandbrakePNG  IHDR szz pHYs   cHRMz%u0`:o_F@IDATx=hSQ4ڴqM6N⦓Ht`AYW'E8(bq]țtHEҘMM*wι \1P,nOMOZ|Y[|3ح<|)Ǩ@8:SSV߸ٟ]sDQ$wSOuX+b@\ ;"Xms,i&rԦ o-SA֓ }Ij-OYIV֗11YN@`'oJt;tU\I#&ꞆMt;iIIh㼕26mez$<xó~nÇ.ED|yO4aJPy(_p{7x80unިϺkf&]ׇLڎ#ho 5#}BW'BH%[YA[:(@_A^x9TE2q Xyø)IENDB`HandBrake-0.10.2/gfx/icons/old/RevealHighlightPressed.png0000664000175200017520000000034512047524456023631 0ustar handbrakehandbrakePNG  IHDRatEXtSoftwareGraphicConverter5]HIDATx !D<مtzЄ&&yEO uQWwڨE`"˞(4`@2X(vB!qV`2ddقWEfN!K 6Q68IENDB`HandBrake-0.10.2/gfx/icons/old/Queue.png0000664000175200017520000000321412047524456020317 0ustar handbrakehandbrakePNG  IHDR szz pHYs  gAMA|Q cHRMz%u0`:o_FIDATxė]lWsfflk;k[IДR B@U "QU T \+ HMo "T PT*іRIӦIv㟵w̜Ů׻'7gϜsFc%eLLLH$>s+}z_ߨH$v!c BQMkPѣc; D*yFu#YOt]EM)%@ C@@P5DP' EE~n!(ꫳH.,6,6od)AjAAJ&R9nY-2ka,v]0ZbگYSS` 穉b8uС7!`z?hswojC0D_AkJqVA \x;{⿍ ? e`&3ʖ(È_,.T5g`ҥg>P˖䦞fҫ>t*'J-R耒 YHI%r?0W?2y dIgCc̾}IxlkXEo/BIk+_Ig<|e;;"/N73F$m1E`lHa$!!%ZPFOr_qJgbDatU weeǿGB{NN?s) +_hP0ZPtRz XYv=v=K k^''N2c./r_# 'pĉpPנ |]'|v}gt {> zƾJfS<2!%jW<˲s`#\džB؂rŔ1E+Nwr;~L n G$z隸pܙ;R$Zaڲ eB!u%12@K3n@d p]wL$n[T7:dW6iVT6 rܬݟ;SIENDB`HandBrake-0.10.2/gfx/icons/old/JobPassSecondSmall.png0000664000175200017520000000043512047524456022723 0ustar handbrakehandbrakePNG  IHDR ObtEXtSoftwareGraphicConverter5]HIDATx10 ̝I8GأCWn@t@c%k$c^ueq\4Mu27 fI,J6?/a>n۶mƫjoڶ8jdfZ7MX q9oq[c8ʵwy爬q+0Lziulx;r"kKƜe,=}VO&\X|a72it]˞Ic8ג_.q\D(7A3pƏ)ߠVOee_}ߎDv@鑐_`|;'8HmkBF)3mkTSx][sƒɉcKoVa_TO@vDQ$P e+J,3  IJ{@׭Eܝ/4`lҰIY[yvhkYB׌ѨԌkZGsՅ'97~ʆ2Ql2Tf9͕3w`%Pr%Ε?3lN;knm PlmY{tա2GVN^f?`C.c思EFpĞ>X%?fl=uI9kejR vfGC L[ \Mo2|p' Pz\9<[$SC}@*$XQQ_D5}sӥtA[3 10[Vsڕa\ vEo Eq/A ŸrџpH҄fL\*_*nH!8<MG[E '@tXg)Ӂef4E6Dyαs8p|6B&/pw5:TU u>I[4tLEF"ԧ pݑS^'+t9^ר2qw/NVvZ@-;QIHj!-E  &5Wd;.kuAgLֈKK[1{KJA C K&k-__19!>2$ ^>pq a! %sb;^742M~ƴ1 ӧ~+L8 qcjR`&i('#N>N`*R^[nݴ*t(9!e!J %͉1tފ32@~x; X&Q3Rc!1#,3Bi#r`qP'M sr<7Kr+7)fr.)J< {f26Hڞʑ]sḅMaËp@R^QJY1,cz5X w(|15UK5QӐ ;>ÇT/TDMBd#8pWoQY[=㊑]qbJlJ̣\-geijd ʣ ?VD%Ŕ5-UsL %/Y"#a0~.;J)+2'Ң-pfDiNi› E,?ZR^ulZN[U"GFhD߻p,2. 2gʤoi˜M0UmkDM&M 0J+޹$1Q8I¦xg*w\Ѻ~Ρƒ MKEkvU鍠ToRNj⽵[)jbD}}_Al Wb4FM:@iîF ~igZM*̙Ni\iº}-HYZkӥpײ>;ڶkf]$63`V$#?X[>(6Q@eK DjIH6Ԇ*YdƂ.)xcIBUiῌ𠛿nC`hkKۓy1[N.^l衶}T/hrN~Mu\m&Xix $/҂r-/VesYs޽Nf MV}ZgOys^3}r E{wes0yw)_K.HpnOq]K#wmj~ vJX+jE}:Zp߆o9i΄Z;J?BSiMGUt'ط )}PW+[hj߂^A# +((z)wN?3,t}Z6uY%Y%Ӥ.}rMIҧ"J?K_&H^T:1JJB@S־BDڞFЈ#FrD& ։8-'k3 =bbZNeHY)?ϊ;_1z9yBy&E|!vgMNA~!;B ܏wJ+lg|U~yݫ{"$/c 5 mi3y8(zhčX~Iɛlrir| #ސd5ߦ^yp}}E7 eN}AMfq?rm^ƞ+[>ퟚ˺_)=0b|gM*=KKncSh\}Ƭq|2qFھJX沾o΢u:@[OO*5h˼p4ӯ+koDJY2+Qf%|V'~aK>װs;OM_;/oG>FOYOmnKm϶|f\+9_*/;PI!~5486_?ŰWǫ//9N?G⍷bBjUoj WK Lc9ͦnx2_ގDWNBOa=}Iq9XΟ=gbZl"uV*hZ5^mz\[!#559[ѳ+iW$ [zisN2Yɶ6i̷ECE@@w4bo[fϏ>r)_)fb拭 a=ntUbZ fdgq$5mU(ZMޫa<:ڠyf2W׼ZL ֛dQq<ފU V('3фS&֓ RAq w*{Ԉ;@%pgth QaG{Bjp곜{(lNP˨5^6`h4_hnl%zqG'X99^, O拆vdaF~{F6&;:ѓܽ͢3fW.l4jF v cjL tw& 8쓀¾ j!ࣹ;>Xe6m,x l ]|2jǭfPv;kưc#|/VqVnlӷH#o5}B)zO0ͨN[Jź'jɀ:2v'nN;97 5_\8l*l`p.mG;}*@ - 8wނG&gI[ >o/}x`0Afqrءl]@Ƥscҹ15¶4kSْ{?f5Bvkp3`1yKjZMou"kolr-m,8`m)h*m n*_3UtXQѦXV '0eVpfHzo5='t('a<5ڰ?hWО[LoLZDIҿ3<ևY%mkBSx]N0 >*aV `o6U&fn 23;Qeb1Z9u\d1li/AR=g¥<X]g1) &-Y>\yنytc u/$р_ ݈vvvͅUJ@4ZaE6HxV:F6Q>KSy.&{<}Ad:KN?2 n37,OMEN#HF`?- j!j {r+%Aqh $S@%%|9`ƅ}6.?jU 7KBC_{-׮8`Se[ lhF-*,?nt WTe\ÚMOsf^|ŝ}Em ;GFn^Kb[dAݜҥy_g:=h5=#*|3z¡F#$̣[^Y EO)ـ~Iȑ8  Ľ&C}24 L >bXXFkhFWI. -h-yU`F m<}hRvTf}|dT@OP\I' 0%U-b2֓yxd^ƣcAēwP)Xͣ KZQX!+}~CX@- >9Fwx+$@>,#Xٵ1vQ5%tᑄJ#5̖3S=R e[OjRgfM߉ZpE9%辴tc⏥R1. p8Ywh;w@^tug.XHT^?2!+tU#_:{*!nygn:@ Zgpwp٧P栢=ԇ|Utl7~䙔@0 +h%\'̊!Lu`tp/߹÷0!:lH- gA ^Z1KO_<*•G}|;n'e}*#(4hB9Th玡NŶ@%fĠpo9,vrdԬ,qcD-7= 7V >v`f1B+?`ؕ?ջ+yKV"o6TpŚUk}"bɍ7!H*3(Tu|jGyŪEe18-BYYdf[L>='4"}\x乢2EOcvxV|MڡBs%ȧԜ?U$g{075պ[=h`Iu7ޡ1t(f\%}R@uPA(@0Mz>n /:{7l6u]~m'3u/OOX-?Jk3pMNVnuS>q$t>NSgِ,yfzkf6{4!ڵ Y̳X?%ţG|j}~H)'FCy6)$.[xgߍJ<MC+QgPX 7~PT֜~5=4WaH]0/qJ!Ѧ?q?+kpB7ޠ">G}W=\&fH0e kƲ]N0Gg45*O xFD ~Pv_u9%|?vǾal*.p,S?5jPt~ۢ4$FMm3\9\fVdٿIuKk+s]h?:iĺ-^pYljkeA ֣;$3~:LH Ip#W,%ebbad{O`eE}9!mB ,ƈyAUkb]Nø ˌD_ˁj5j%u^׸ `O g9cE @=BHMlk@$ ءۖ#iBOOA\zXCR%ˆ6 _loNVL?7\iB#~_l5zq#wRf|{jSKlcԁt*=LfLQJ_Ke[LE(EU0"R4hbR |;i@fT,}X?GBP7A#Hk2dwըU¢ {3eKxŨ!tϼѝ;ZهMK+2S:¾m:ʋT,شt|H>(;:HfT+_oG=3fLál,0K@I rHmD-Kɵ""7@8bRz=Dmc CF_ŀT-DM2_=Wl@F䴦"%b||+h Un+z`Dv~_ClO$>Y Bcyg |rp]o:8khۘM埁/W^ֆE^ls wfY~(cUSM6:-=j0y-AgЀNN2g$>+y| GA]cپ6m n5v{y4y:l<5y7/CBqP!L||R۝I׷Iw|X\Zꑽܳuu}1c%(Hh!!&>{Ny! BC~{ Uz-b i>hTN./hY}_sPp͠ '.D1bԿoK'mCn0H]_ _&kdx3{G!~KdlfPej9dm5Zq[nJ;#:u|CC-y Oo!N獼7;tYW[kcd [@P {q7wΎd'>{*QVؿMAN: Zl]ѓ䯹c[}: pz$1 U(rtH):=)_]2{xlu:0'Y0v~p %Nj #o^'X՜T¯-d'dT{voƧ߇&9ގlOz|lxq!leP>ޓ 4lBޞ}yơ$9Eg/>_Y\j*SCş9Pdo/g_J7wG`fע4>*04yWĉ &oD93mϻL}5Čގv$OEA0h61s_ qhL`Iw^%M2g 0=f_v fM7R;IEJQ04Ys N[4HWR99s_;o@O7 @s21FQyhm`:o_ π7:zAE^,έx!{>!7r#{77;"{&/0WßﳠA[yυmk}ݲC,NZ M9@`CtD"<p& ^K_J}GR}/ +-khO.ЙZ"W 83;&_ EKًH|!GCVyhKj-үP~Z̚lpyN7;aACNh }p*}FZM8oMGeh ŕW&;0ܕ +$Dz sD %0x/{gݷB#cpD˵#e"K/) !Y#̓Dlg4b>EM3T (9&p~-'GɑlY[?E.`ހM Evn4s<{nҟ4z3ƿM(p4x։m |&x%Z@({~N1qy8FPjDo6SXyhr :JgǾfN&b7Ԋ{dK<ڜw#sںN}xU~Mf_ 244yYhtvш̪>Qc8{Y oߛ|u߄6W(-ߺȍ# n֚MPj] Mp# y}XK sSU>q݁(yug5s3q !{;ߓ"4dG _4>7IH4(.#B ؜cz (#}%şnMC- }|[ϸ%6Pe>4RQoZ ]r;^@UofJ_nnaL3RlP?yרfchRr$R΄"rKG,-cJ铞\ੂJq{} y+wܥMYaq$xH{h2؂H!XG-Cz+Ng÷M#Y#dnrjE!ٯ.Th";vv ׏I42'ZJFBѳ$.2n;ӹtw=xt劉f>W:m+zl<Q CsBL@G1RB9zBG/u9߱\!p"Mj} ӆr)nST&t`&YfpKOm5oI~6;6h'!aG^drE% v <R1%/LPskK\wIΌ\ӀxI7Jb(i(H<'ߟ-Ԥ)ycNŚ:V J>̛jATg /d^YOCrG:ic;CʀW4' hJ7_sQL3!Mۯ6Ky{⼲q 7x˼`[(sk349bx7|q T zըH?-m䙄3ΐ{K@lg)ݺ@ RtiMd\zK.(\rZȞqp~M`d6 /wOd`.HyEϼ zd&<ʉdd9}hR^օ=W"3D_=,!l,ŏq?E?BІ^^c] J*סIq+\bjō 0}6?t6Gл̂:?s%V/Df= ~4_ M=? uA? L`R.[ B:RZ^A)D(ϼRSJu!lꟆ&/Λ cM}[cXaDe(_Lz"oHqcTxwt]8a S!BɄL}XJ" Y[8—>;r xDv؅7Q/b .=_U$?&Y ]X>e F€"UE,hi#z@7Hzc/x27m0"f\0Ѓ>#au Mb5s$Tvb18#{c4+Ώѭ _'2Eeoߟe-2$i%  MrNMAN.9$Dayj~\l;]?B~^Ng~5/Bmu%hk^BiJʵ+w @F!M*yg_8,|:ֆmWàc 3FGu~2'uF=!"ct8/ZԸruڐGtXCg5ZbKZbb[^V ٤)E=+!KΔ\C50!xV~&,~"1>ύ^/kdCmBb4 rQ"x D!W'ܻ}QΏy"Bx|31H.Ͷ2!qb̬?M!?q9><-lW]M}m5NgO4Z{m݅o #CÇM] 2h2_AG@nn*I J<&ߟ Jk`:1Ϩ#{D+B2y\i1^_E1lAbBAnE FX0>>H1'H$K[~ l5n}*jV9/@>RL.$?m>EN$D'~.%8yFc`}{C;+@03Ft̋g; $O)O:f |"""yAБlDk#/{φ7$Ő; Žz ,^rp"~}5C|pKAyɼ"7zq%{SJ{.̥܅ؔNICC!nVzСÒ+e1f(G -/3h)I=қUbPxF?M;YͶTj^zWyr>&5zRnqO÷ My}vP/vFZ4Z#GqES:/]`<{f#y3lfT9FG Oy6uB%|@uf~3˙@}XSwx$"Q}S!T AHP!.Eev_\κ7+miy4qv0‰jdž?+o^+-Y>VH?uhR?wX[ЦKZ$'͡WF E c[`P[Џ q\_]R +zZQ>3jQÉ(cF̬BYB-yFvrQYc? PCt8|];_ svXV +P٭o)1[H>fo]HL@c9w ttW#0*-ZDx}n;,^y{Rn] *j^D;b0 7 63&7*}QxoɶR^dte3a(ԍʛ?ߚW♰\#zz>% T,ȦRkYp\V$PUઐuAXcg{ .uU69WDԶ,|=?_xb}N@;|P%})A?zR!@@P?t@Jy1P{mS̔eYZ^w^1*| ֊9L'd !P'hKh70b:lf'bYykxDm1mx*|u'8B@(čbJJc~0y7ŏz #9{khA׿Y&Y7NU6S@cFLRz}n@*&d^O?bF4k@~O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?O~'?YMw`mkBT :mx[h𭲫*ݨsLf&d3Lc2Xr35Y5Id+kWQո-eq}X "io) ,AoL,Bu0g0|O[o8m+V`0`-7jfGq \| 7֭[wܲeˋcǎaxxsTy&Y322A|jժ/)N7p}DQ8>%0zzzf=n77ZlYb|166ӧOp8(**ݻwu׮]cnnΝݻowvu~6^iP(t[}Q~ٳxκ^g}<{ _kAw)״v?uj>.FGG_ϟ?[nҥK:gɄ/^˨`bb[{~Y,Ef7|bd@Ң*D{ ݎK:~s$FFF=+{ܵf͚77T '~}O>>y^rΛK*uퟚvy;0yB}Ab<0{p vxO1H0>>d峘AbgybT&#H^彪u8=MKϯe( 7\ oeC2 P_jj<2Z+ ci@ң+੊߁pF{u0wNٌsFFtjLͯѴm`i;?gDyM y(h?}EYu ڏI3 ؚ$p57b"@bL1!5[V&9c``iډMjvہ]`%:s~n+lsU kFlRkkFj< o$Q!v!$ Zz8۰8"z]zlj+ >꫱IGbs dA>Um2}E&6!:{em34NhAu)?7(^2 LGP)aԜCyY=/&١[;;8+c}b zGx1Km-'߲[?s%F, d^njܡUR7/׻gI@XjjgiЃ`0 `0 ?ϟ|:seQ3|ӧO|:2|.};7eGFO6_Qv]T]^ˮg{>pjzkuo{yye?{-x/ D:3D&򈼹e^Hyi#/OGzϪ߯_~ :sMe#M3Y#=2 QЙ[\s=E8}E>GȩT ڲTg-}VfoSVwzV}./>~!?U1<#}=F[ ~QڋBN..+푹^edLo+[\-k dW(}6q$#?z6Bөi?L7!3O_Q}Пuo[=tkȋM!'}/Ƈdr2_Cﲨ: `0 :8o=+8-4}۞cĥXdq{bUq©ήm!ƶg*ΪU\z[GA=^+ru{LV U?)V>ғ)x|Yҁgi\yi^cUo*= !TY?rfgWsʽVn*VX#=Fϫ+[F~yH\L~[O҇h5ݵTow|Sfӟ+);F;:x )/OS yUo2e)Ve3'wgGg=J^`0  ľu kU,Ksؑ5nY,bXw{ w&3QהNQev ]ƷgcH˞i{A3I8hwduwUIWq8I>+@pQşGcZ\ƪUߝ]/:3d;ɫ:gB9R|GW~w2;fzt|+i5nΟgZY|<1NyŬ|E7k?z/k><=Α}N΅>uWydʬdz `0 *\?W8GY:Dgcg< 2+'W6qn؟{ru"wU쏘~c#T?+y{Q,,^qF/Xv8.֩g3}ȸOP ~n%hUG4(_sn|W}Tg&x^c,Fѭ+ <#+}/Uw8BRh_|33!mr\7U9m({ѝpvew[xG]߱?g;,nҽow8]וb?OV=Z_#ve?vN_WrYLo;1g9pV^G~>[_vNOS3 `0Q[ veO\k^8֔v<Zbz\Opbn$~}oz3ј mK vU]^iNWA#x딫jt q :E= z%օq)CcYEqyRG-+u (K\hP'*^ء^q=m=y|Kvūe\rȊ4={W1;=ݷxp;o@>ȘT\Ԏ+C=*ɫ|GJOCW]x1.ﵠ9_Eб Vq)v(ʑ}[GwǺ{-oSdו_˞׃2;iT&w*w:g׭SOsj%Z[~_˯d֮+w]7 `0]kIu+eL]ւoA^;=GR?v쯱;<y o$N1紈=:ߥPVu< <&3KyC/4r)i=*/|Ύ^]QNН1qGw>ù{ ?Kv:A}E:_n+{u=rq͓̳]>>d}+|L01`0 leg:׺񶊝`W,3O?]\9P~[kOWiGc~)-<w.3q}'vuw$Vnv(r52S;Wk_Kϔ8B/hEՠ'9w?K;x:x<|@cϽVyc@ۖSw8Bq]=2lBe6V}eR( VeZT4ade2ޒ+nYBTqSߔ<[&=f[|szP)G}{Zׅ3n7jpWwftEw[ǽ;`l? `0 `0 `{~i`oLy>uoi\qK|}7Svu9G쯿c¾#>,jow{ՆݲL=mW2u_8دjo?kD߱mw>#}E:OۡO;y`$jm.2 mkBT}px흍8 FSHI!)$FRHnw HYx3ꇤsaaaaxIǏ'U{o_ھgW9 o'GW {>~Jlo߾)*/N\ϱov[iZ_ձaJΝ/:6O- 92b?Tlk%?_21B sY5>:>c=1Ow y^- ڶ,XzusM#גU]>H_yYv!ۉ_mi Rus]Xm_g)YY)m]y,m z1aaaxEߓGקo/Y\k6xjgH|yu.\aæM&wk#ϐ$?]Mo\Ⱦ,/ڥQ@~6s?)}, l gX #vQg Bٙ^uのuhm?}{].~}v_J;xogJY]޳@.)oqC?}>@Xߘ'-(W? źvƔOʙRv[K?[A}?-wmՑ}g\=c}M ggg DŽ-B^k_g?F? v0||؎=ǧHPgs/hؑI t~{n^}ZyD5XWvO)"c0vY Z|~_%/,p\ɹyΰZ/;/xs_9?Pܯ5ݻ\[y|č8gʱL{? 0 0 _k3>z_\S |<)b|7aaaxn.ta?l^Cvkؽ#~e)3<3^kdlc&jK+o"e<.ʞ`^(3zu l+6v<ï k7]/lc[`On}򚄫 G뎱zt^v2)?;Wmr5ocIz?Ozx{&!ez."ѯ 1Gg{+ҏlw<=}GݽFƨ^)zIpG K֜{{e G12ۭqiumf>.}~a? 0 0 [u+7Svq֭y΅ ?ނ}XwŶv?ߩDZۓ-q/?߳=<~#>Fk"qzrQo 9r,nY[;o:)@-`ק-7({߯S@µK9֠ɸ>:n3 _[_*mtcmC>qSL=<6;ǫsaaa{xˌ\ފpx?0׋#5zяc]x^l򼠕(f:~٣^lin59W~\;?vn6erUbS~v^U O7O(|;+SG4|?f*?rW~2oNٟS9~daևmH6mX[J~s.ym4ٶO|Bd/b5ɿyU? 0 0 0 0 0 0 0.P~*1@G\⟿KrKXs2(ߥ纎J8'>X@▼QQbqwx b)_K|v 1M6kee-2Ǜ59?K^E~9ϱQﱮYF8N?~;:=J<-tĒyNAgC \NXKs)'^Kg\~2}6}Գ)n]Or^j~"{p29w6/.z-v:+M{WJYZ굢`% Ҥl9ힶկ#OUz+U?;sd~vND7*.Y+v:ye;8}~|+ÑޅN9}{Bƞ#txխsXɿkSV/uJ=o G<ջL'L:D]6jfgLz/+ؽ[{rCMYq~[{yy czA;w9zszWHVax3 =j3fmkBT jPxڍm0aaAXAtNuө~"B~v(I$Ic i#Gl6p8޾s[ouv?Ru߯+fԍ^|}m43ϭu!Z9RUY׽B,z"|~>/|RתY̞EGߔ뙷L?:fV\꺯Pv#R>cNb=^Fz=yNI}.3?3̥*Ǫ7٧ڞƵ2~zWİg#g&2yK;wʞ2vm"Ggʼd~g<[şXlk=^k͞Gy\ׇ3M^&}V3=3ڣ<_(녾v9> G@Sǿ99Gy*"QYu*lz;{-/9v=Q_s>c),k:gQ%ԝ,SFwA2vWcWqlׯ[},g7F}.2˘ڳ|B4V;̙ob'ob}u޾odÆ \r0>>믅@<=CCC߻yf9x Ü?ߍf2ԗ&ؘ}lxx'dMMMm<CCC64e.cU!E`m&h?S> "P".6%Vki>,{|A wUD 3]̧WeeuwR h5Lw=WԦWLlQ:?~BC7-m~>wniuE^#f.('߰"m1_{%W-)EژMFM{̷Ҹř~0Hk6А@Dp i#mHu!s׆ h?4d'Y8I%4o(zm療#bl_`)U*@c* `#2Xhm*\h@Y RS#47?ʈ0h_Į,1 ̓ɧq(`~cE1vӄs׭l@iN5I;4bE =ё A-1ڣ (Ǝ[S;m#@@%_6&1Vm|)LP%l+Qv#L`EcUr@H!4|4\CTP2У]"XbbeK" 6`-@\2V!ME!Vu1(JtG*ybްz\P[Okt9 dV }s0 U3M |zu6UuGq!_;U9ufWͪ3]uWRgel_sף[IENDB`HandBrake-0.10.2/gfx/icons/old/JobPassSubtitleSmall.png0000664000175200017520000000043712047524456023305 0ustar handbrakehandbrakePNG  IHDR ObtEXtSoftwareGraphicConverter5]HIDATx= @Fwa!6{ϴMbeCِ`10a { RX3]!i u@Z0r9m"I}"$si"ch#C{Q%(8>9y~H" Ccn{a4ȲlnUU1vDh6YBaE; 2AIENDB`HandBrake-0.10.2/gfx/icons/old/EncodeWorking5.png0000664000175200017520000000054512047524456022062 0ustar handbrakehandbrakePNG  IHDRatEXtSoftwareGraphicConverter5]HIDATxS!@ hHxOA(0$uE!QYn{MM3[gC8#_j%>*"mRIZ6|@5~$1>6mSɷt2]`70w[y @ I(&9Jy]h@&Y3`P3N@ZL,[4`EDZD#Q( c"tjYZ~8NGIENDB`HandBrake-0.10.2/gfx/icons/old/EncodeWorking1.png0000664000175200017520000000055112047524456022053 0ustar handbrakehandbrakePNG  IHDRatEXtSoftwareGraphicConverter5]H IDATx @ F3BA0# 1)BIIT2e^d)wgl_Dڛ80ј08S@?>4֠m cТ l']2 {(>s$r^WuPiIENDB`HandBrake-0.10.2/gfx/icons/old/AddToQueue.png0000664000175200017520000000357112047524456021241 0ustar handbrakehandbrakePNG  IHDR szz pHYs   cHRMz%u0`:o_FIDATx]pUW|sM $!$@ P)J>Lg:-֗>(^2X+X )ΔvlK V Җ4hHH $79{olmy?sf==_k֚ oݟi'm۶~V˷n: xg~-eDFθ9# D^]p .Ck̓ODBia:ifSqg -q̺m2=Xo|MA\=[SaFx%,*1IhYnS9NR:!bvڈ1ʕ8~q<|ȀR% 3kX6ZX` $iA:Ex9^/o"o0[04ՑaTmxHHP(QWEGyZ@y=*#o5*^L*gjP#H߆W#4č]CJ\_$ KhS>a"_4]28ڇDRh%u?i^Ј'ӓ%J Fq(p+,1ϴiSQ&Z'訊_&]h Da$A2w)cC "c1qn\JlVz>^d޽b/R{|g-5 @>2IWұb9^Bjq|eҭ4u(dr aӶxY.%!3OK<9M,6H|Lx*|Kp $a#? p[8YrGDDqB4l#ɑ+tpך4s4 ]p db8 ȸL;ulۧEƷ1q#eٍ* ,R܄6ϮtN&|+.К_R9slE c-ɪ60];KdmuѦ=si10b f]ཾ?/غ~E F4,L.$đt (kZ0 z_[9{E=HfS$:ltLɘx? 57o~> <}OUDzyhC  Lkk˄w7<)ttt;w8U_ZޭTW/ŘD PZ.<~vtN}@hgLj ?Z{˛DQP8sJOB XY?/sW*k.0"bxKKA? ؗU1?r|#YqIENDB`HandBrake-0.10.2/gfx/icons/old/stop.png0000664000175200017520000000247512047524456020230 0ustar handbrakehandbrakePNG  IHDR szz pHYs  gAMA|Q cHRMz%u0`:o_FIDATxėoWgfvYc.`C2 R2eڴQ(mڔ)2R %< &ݝ=)sk+;3|;9*s8xL}践ˊ{vg1 i:l~V?߇8~7]ŋln3Dk+G(;sHQ4pX^cC5 u`}ey *4 K}-@p;یlp213ߟs`2% j"h&+?*,}gM:w,_o'XsxP\ک4]n^KRvw |T 8//#8X-y*FZ&CK8[TLEe`KRF-& ǘ$$Aܿ@O0&ѫVaJ4M88Bapbs@ SHۚϩ8A!ylJY6D(m@$x<ƄcȌT 5,RhHnf0`LZT f4",J/`u"@bDc_,4IL:aTϵ}mGAl`lT ("91X*p-NQ,4ҤR*9) r Ih8$MQ "Z\siYz312qeo\xuPR%` Y8 1iQ85 ڱn Jfq.+" Gt~twgECr&N#(CVܫToGH{sΟÉ:CCRo|6oq3/k&Vj9qN $sǏ4}w/%IL&&2E6Ï|dHr":Y`}r^)#u%~y># ]`yWW~ؔ%@j ׏޾}C ΛS0`gZǦk?RbԀe«uj@k@L Lj=ɢMkkۨL'SS ,p%IENDB`HandBrake-0.10.2/gfx/icons/old/ActivityWindow.tiff0000664000175200017520000000463012047524456022366 0ustar handbrakehandbrakeMM* P8$ BaPd6DbQ8V-FcQv=I Ja,IbiM/37m*N@ Y?T7 %KR@MJ +4RfU A M*WgW8ADQOG*I&N "7 [ `5d`ld@)Hsap.0q,@v%*9ٌ 9숭q<0`2TkEL&TPZmRҠtB*h1 x- ;|lt2r9# k\ e  h3An'I1 iG dJ+!*|&g+O%Ąau!ajIF+?:`}1$ ;1;JH :9 iwX@$>QG$ 'M < DHBU GUl“}o GT'8HJ; y`}eyghv"`\/ $XCOڛt 鞧B(F_#8(C5LN):NW.{ ڝ=hD$ʳ,1?$f sO5]\vr-r g}ƔF(;b,#8[n- (do :Bj $ " ։~$]= {0t0}H-=>P,. (}y%v*n=GU. HQk ID8")܌N$Aa+ Gq^xSb´ S@ {3RX@?v(KBI+t@[B0 xB,F"2&b^$V U X `p(aƿhE #S'C[7c#P& `t\% 6I!lnEAƔmFq-A" P:E"U ^dx9H2SKR?H^"  ^=RSf P8$ BaPd6DbQ8V-ItH@ Y"iS'3] &@ =0= ('y!*@VEWI|!Pp~Y80Ry O3kMx" Av3!D V|=t<!~D zh3-~ ^`{G$!|2w?ǰ`׬A(8h3ǼaXv Dg#gvg?P_dm )z`$7  < P@r @!iRbz_ A@ #\2(! '0/1b'ɚ'+S^̋,8J @5 0s;,JT?GU? ~g]I, t==)I& d JWRS%ra;]0; 6+Z2U\[\6-D@%~=Q-D;tl=Γky;!HR,QEn L']!sgtD,TB* FD`' p|esDX@:VVi(+h{Fo]:2/1FW~09\ `h' ǑG.Kx^K"I#T<Ypr `qy 8yaHTKrwTWS,$xfIqekR @{F* ʁ`"~h nUl=RS HandBrake-0.10.2/gfx/icons/stopEncode@2x.png0000664000175200017520000000364312051733344021131 0ustar handbrakehandbrakePNG  IHDR@@iqgAMA|Q cHRMz%u0`:o_F pHYs  ~tEXtSoftwarePaint.NET v3.5.100rIDATx^oUAb7B_RB["$J F?o"FHaIKB@RMՖ>0~ -1qJɝ{ι{Ι٥[ ؒfR}x~3inؼ ؒP6[N.d'I^g]iER\{+F6w5>ujC_}mJ=MyT?q2X9zl,/ I;a; /ueFX1^{tԭßïZ^޽zcQU& t :GH}ηwdqp~# HTf1M|}k wpKp`@t|<=Mr"qϷs{8jUd)ftS1s*Cc'+";8ΑR/Z[2{1*'q|PGGs?V`4'{1!?}Ha?w=%:GH}>)d3_2VSkA TrQ;um#J'Xss@t|i/  66q[ kʋ締#ζ@A[5nm<ΑROZrmƝ; MdH~zڡb7@(7O~캰/x %:GH}>;(Lds=9-'p]ozQ| ?DHO9U'7|^Lr#:#$n0PN!eu^~ # 0~eC̆?GH}J2:cG@K~B@կ!9#8G:Q:GL@m=m3>42tZ CGdb8aȉ,b/e!"C@qۅgcw@R%t}K]@jj.j$繌n1l"v9ϡ (Q[x%<{uuuƀP]m\B T\DU~% h8q[3 8u x 'vpSKY%NȯѾNXO>_@"IӀt[3 ~15!j;֟Z"GR{<@zzZ1.O/F9EriȯѾ3 {0ѳ #y٣{sRHT"N~Wh_šѽ)ez&(@Ỷ*<)]jˏSEJ?*l}},IENDB`HandBrake-0.10.2/gfx/icons/question.png0000664000175200017520000000466012051732765020331 0ustar handbrakehandbrakePNG  IHDR szzgAMA a pHYsktEXtSoftwarePaint.NET v3.5.100r ,IDATXGVyXڦB BE"Kƹj12|}!I%Bm438kHQ4F"Sv-[ڴ<,3^}y߻|O$?kw:!pPs@ 5!Z}_[L-F:ysc%GT\ec<9KwZk J\7/hY~y]y (Sj11R)sùγq-9:*~VyGW<ێ~, Bǫ}g\ɼ,+ !k"9H̯] 0ٹI kkƸ lohN!\_Ł/piޢN # .pmęf" ]u n|܀xIA{r5\D'jHUǡg p Ჹjn1' |d,[4!Fb`xuȧ~G۷`tnM-2_Q b8m &NCO g^ʉpX} #Vޠ4 o5nܯE?p w\w|]?d@53# [yv^'*o{^ &(wFHcAAaE,F^ "9=ӛ׍}_uv?^#k]emoOO"~Eo~\Uv([.K )ptV^' ֖.qJ ӲPWaU z>GeT׵a-,9=tg2$7-3 --B%a=`-Rp4˥"CHt"V `8zf@cК@,(nKNL$dhjnuw{ \C`-0WocEXvЛG,`4al$#S%̅ՌjDkk+=be.kL -i_mdEW,L#/!q&agazF!0Z E'x>  7#\ۊ㫄MiDfQ ]z%ga  FϩY\sAko Է-hSarj$Zqd΢~|?4Z Nq3v=\vo:H,dmhb@EE%q&}"B^<'_A-}.I',JS}tk~惵Xc7#+~C#S} s_&!#Ak֡4MA'П58b>Fkv077/3x\&ŎW`>HWI˗d5К >3 fp9ƗfDވIK pZm*֞}H@!dCa=EUu`]К!iөm$b:{N[_=42 M]1[6/L:DeCМ>{9볺1's# b-4=h@ HG}Ab ?B{=&?lmb =Ѓ4NȕC࠘^'Eu6HSڗdۅZ>Fdfz+/WQt1% IPGUxO*uIƠ8 v-`n`~ݷ S`4;Y= n۠2qTſ@mRG``ɳjMhasT&n[eZKr%f){9__<"i 3yNjbҰ&cz35G:cWŽ ܿwszXK WvM]#͛z;Nr=Rp ,(8:\5뽽Yߜ2kw ww@pw/|3 <}{;/jU4Y^kr?Y^\s[n߆[M$n%ƷV[VrzLu0%u$sxydCO\:>b7ƚon4o\wsz7|pw\1M@-0sWkY5WtNٰϼIzU0[`̀ɟw3Vr~2okծW=l6 @l<"̟#m<@cE}?/*>w:kN1^aL&a2uT;IL&o}3ZÚu.kCVkiVzq4jbb1b;}_u&*8xoO2^g1'_xXPgwj/9FwJ @\J{œؕ8+9Y ?k2Αc3#&@I}"^~` 8== @<=ɆSXce,qƅxsZFںk__`f''$#sJ<7xRǘqm[oV_(3sxĹD+\i/+uszptt8>b,=:b9q}zÃpxxZc5t |7gl>.MzUpppc _y,Ͼ X {akWkeM =`o jqU} @l&t%4j> aw:3Vr[h$˸ݩ}>xLlo#vۀba? j|1 Kz׿3=(=mh4Sh?F@lyÈ< w ^crfYuNEfbzpFaGqhgN|䟙.kpZjd+5K9U5n3MIQM3Z`k!#33bqkj A`\=4+q H8-%һ'yQ_g9'Go`0,R[ Txد~;r_䅓kT -eGXA0<2 3Nݪ+7lz=@Şl@,U /1m qq$h/Z׷A<"oZs.Gnf =XM.|wɑ%sXǏm 7}fbo|Z$xLmOS S/y֕:lɑ`s}s`>Xk~ƥO>#_됗z7]3.zX-/}x=[xOK~~?Oy9k2IENDB`HandBrake-0.10.2/gfx/icons/new@2x.png0000664000175200017520000000460112047445774017626 0ustar handbrakehandbrakePNG  IHDR@@iqsRGBgAMA a pHYsodtEXtSoftwarePaint.NET v3.5.100rIDATx^TnbKoca670;#]j""ΩiPfeFF.;zy;O#Z-ṏwx05QzvoF.܏|?D΅%#$ߏǨ>|Vo,_zևבA #b ]Oo0 T-4,o|Q՗{/rvq7"l74톃9a ր ꬠ{`E^od3lq1 (.k|jl279b_Lج6=|p}X/ChZjW6_WY5_+Ջ fZQÖYMQE9ֱ>װ-s \-1T\bGKpF/Wh}gGWK-9y,a|/xu3A_?觺BXb\v@lb-K gɜ C௤߳'+s|0sqp|n G7r "(fW-;/s[x{o>0x3`ycopu3r;7WϺ^<ȟl63> z^ř9XKngaOZf=s"a:p gi20WAl>to1CM9t%Vx5 @O}Mal^ֿ0hG>c=U)2}xHzn}2pa|{#Փd}\{zh[pw04pwwGpo]3?[ךj*}E0b0Ä!caYʝ-wz܈fRv0Da S0 ކ!ٻYxV V9l@L_r״Qtsރ@^{>Ћ},޴2w<4Զ<,EnEn+݀7솞\)zq\+qM8|Thvoow]rn1,,b6UNIyziPBsENtbs;hށX6 ̑W1[-rnuԺN}jzӚf9 N@VK1MǸ\ToƔgO e =ֵ+sq}}!tlkZxC4ʓgT>4Sw/ˇ+,wZ۫6"X O" Gs,>cᚹkq8bX2N!g}k_2dכTqs쮵>f&^gfٷx0 Ǡ&A/Ä0dqԢ&A/z \$_O6=EOz4gҥ/ЙzAo=%?9?A^Mq1cN4׶1]viѰ>Ћ{_ϝ{[&''ej괜>])v^sá6=EOzsȩ)(P4(O*X(L@$45[΄5PDxzV9n1qQHdu{.G5q5k[5aa%;%xW׬Ǿm{r]hS7gu!I.W rZco퇘]\a>8x\ĕb9]]hptd2BtPE3٬i#=̗yt; 0009)g9F&U]o@*tFҩ q-_*"s{z{rb3 `9}A0NA;zz؋[͘z19ǎSs09N4z1p+씮n5%wtt׸7bID#s.gC.`^1Q;:xD0Q=o|1yqD־ݤ 9rD]Y1C5]m{`e7akklM>|؈I/p?8"s֛#` ֩j3"7-ؤ'1gxDdɲ9R(g9n9`MMLmhQa؄ dmBl셨ۦ's?q-^1PZ+'(DH$U83此l\Qĸl!g=J$5kqAkzU v" ohP7q 9z`9wpХw_kBᰄFR u=!4Gdz]m=FZS>-{ jjk ƈHVu_C]JP:@IuuD]|l4ګ:smx iky Nk\{475 /j!:85G#'@oŋW^]oe|x}JO>KKK,..^YXXh~~~q/@-jRݻ鉭SkkkS{ jR%>i(` IENDB`HandBrake-0.10.2/gfx/old_logo.png0000664000175200017520000007450612030656713017146 0ustar handbrakehandbrakePNG  IHDR>agAMABO pHYs  $tEXtSoftwareQuickTime 6.5.2 (Mac OS X)tIME9&8?6 IDATx[ixU~< 7s@ $0((hE@A@Q V},C-*ZjJREJ*V@"@B[9giݽ^]khy M.c w 6+zYpPYźYx%nѨ=8zuEVѴ3>DG:10I4`X`lH4D&ҵtV (G \'nOm?\n )w &ʃ:jPX =` aY#Hڏa2cNCGXk@-I=хy~_SY V}NJuG 0Ӹ 0HL|!1o 2Uc{Qe5׼NP?$"x.>9 ot BB@σg ϗSrRҸ%;p_e, \˓AunL©S*zƋ%Ca1=YNJ>V,u[)E>ϴrFж>_dc:tnH Slc \KrE~al~mgn &czI976;1g6k4W\\`us{&~Da5\Nh;:W-{r K%|N(GE; !Bn1 "~".jEF:O[-KۻZs>7h"`~[1r224sQH&B4(+/E*d1fF5l+![{{Y.T&:!p'5m+@"^i|32+A*$WigaXrX|ǵbDVpsθ؎LXc Ba>!^1bj:^S #De+fdf}2N3؅&2d s3pL}MNo9A-02\p@AJO KSkȮw._OfAPk.C> {C\,Ҁk 9eP#Mi{Zv{$1 z6456W;To0;&&O'P?J1r5LuboDm )ĺ7^1ё+νF[TBxKhZk RY﹤y MxpNt۪qB~KB N/Y/n  _iqvQ9]OaHK16"# ő9IܶzM"|1=m RCW[Q} Ʒ8F~!!Dr>aR|b" ki.X|d Ä ͉FTa!p`eM՘C}nPeظ}V$ cE@lM\F};n񥥈m6P X2{(+ٳ`n*ES@ڌpLj2S?o-᧲t:p@\t#Ғ0up76w8V31KZ!({0G /j1e8H8xS9C9e7: C[G:zގlElukw<HpЇ2b7'(tc"zt8S .6c*{(Dd`Rzi3p7'`:Hq!a{M1rc{Hy@!\?)RF|8:x<;>$tP;QE ,'"q Nڳ^^YU>~+FEP%uТ!?h2 shTD9IZ.j 7d qэ$=V؅^{ d%9P^] oj `XZБip{pBY"mfIT Ipgއbh.tr︄ԽUd05~E!@ RM&qzƋ&zIcEy&9fuI[%Ptu76-5a4xY뿤DC?Q6Wm}>fB{ wSK*W$=y>Y#p ,a䚺F 080o n^ +1vzk?X3xq+`]>urY?߁ƪt5py~Ef OG:*=+GP$棭U:JD,]UA׷0 PQଂⴔ!'`ɒT>kkʙ$G̒h齥Y~F HE/B4n;>?,&4yUjٱ!-< _ 0ug*5]=(ggg_HœG ~SA #eR `'!y^O[@oU_TW% s+C.:zM7<</M5Z5{Xq@~/jy̴썫s3Q\Vm΍O&Z"v{`7`Te-d7SK08_Kܑؓ;, n' Ր9?Zmɾ[3~׌MΊ$K)`dJ "q &5 *ϏGw =oz^%8kN/s;SpͲ2@`qޱ3sK\7}_a;?J*]Lڜ+g*54=D*1a ڹ ޴D=tUEV:+CtƤsNK;vHk|pPL9?ϩ+m\&v\U4AW_Tk(@Nhn)$hh u\ *jXDSPU{t?@D`QkʵF(;sԟ-2p\-wՔ/Ƨ0X/umA`ب,Y5o(um>:/REȩ=[>ظQڼJB/RR3{Yj/1R 0ы7ۃMGX|uidTl),W oDsPHЖ:IoCXtc5ѶW~u2'pЅC<1 Z~P6HOHYxW–/,ThiK֎JV^L w0M=R[3 ;/(4pJ̽c*g \y/7krx;Rݨ4VܝI&d 3uK@MB3Y,B:G4B\ു\qKlTp7s<`2j#8nl-d1%]Iy._ ٥NwУw#udcjyb|8GWY]]fθkf2Lܕ@. hq(o-% f"n]Z M!?=0O]{j<O&B0LdSP&'}%XklG¯Ǭu 5CA{cАdQǰмȳ"} E%B@nJ=䯱Wqvz>PSc|0ԗBҗ1^r:1)]2\L#+BNB>䐛5 c=`xa9g;ւ o6;)y:Mw7OјZ>^n? s/9́\~9xl+qNyA,kLJr#1Y2!c)‚\RRyyG#pj8c:HH7 %LM,pNO^@\?Z5u2gYw/.΢{{ dpx)$ydEC%i%#F7 =M2Gh)a44 FQa0iʫuj0aB|W~,BZ2OBs=Qg;Aw}X<񣤑g[A7_?*zպ_0{JEF)K{F؈r FvdrBIg 9~88(xg% 8@ZSmؘJL "r;o0,ja"@ᯡ]]sT1Rdt_0q֯[`L[x Z-f̘:CD0W;PZJ1@#;~vh5o}9 4qs0 &ʕK 8%M-߬ˇk[iokwisȟxƍ)ShrFGw[-rF+A{MAd |~,OIC\$ؗ&_j1=jMQ8$/rH!q@]ȧDbHx-}Zd!(Ư< <5ߙrXK_e,M2,!h4u[Z:p{_f7bJ?6\p 27p+w^Rfu= n"廒ps*<۬T A9r3Gʋm<s&)a!g*\u#MCHO茉r dt6%crN:rqn%L v8H~ PO1X/Z}^ f=XfHR r/X̎Yt)Swֹ7', 6݂K.yr&͍fn# toJ†#]OrՆSڜYdA|X4z+2k3Pm Ao *E1h2h HjjOkewRDad`XBS>QFmAH:=e/k)YV;nr'3q9"~I S/ ؘ1ag|9{:^Ԁm⮻>pU/b֜IRyr8V6(;ش$S߉e}Ar5~b {nk; ȂK 4k7 rF!OBw0\#g7['mG2N'a$-D.[+$_`qڜ]\vN1k> >C]x N6+?H%''< T)z}3;BB7w6Km.w*4 B*ea cyKڗ[q9`RgϿ"Xh:[wɐ94a@'5p^[O8]K· )Xe0m#f.sk$3Nax?U/ϳZ9,XY+1q;^'VϱC@b?82,,zzztTd9RK!IMOOCCC>>ǁ.ReecE/(>n\<DV$ۣ"m{Jrj#Q{km., ,HbT%? vIGTbF, zȠG#%㤸~X)]Tn>ӝloNOy/":!0m[eTgf$%?8FFnAa!`H)XLa'?"n$哱x5k83n`0 33Btރs`SƐ Pȋ/~$ w'> /p ~Ld}~HpIBl觧@?Ft]N$?e'AuJ<g?96z}tD.+yy{ uũH0W9] ܩ,Ӓy?g_~x3/B%jUAa2Y, o|$ }ti~be zYHq1>f #$ɲ"m$!qJw~ܰ[Vkμ$K0# =_UY6>Zэ6x@DhVvBD2tP)yS4tu~1+C8TMP_!jVܶKfM'k⏿`d9ƫo^ G+%'NL a`k% v'\F{^cǎW" c ᓳvsi:Z.#Q|t!$R0 >cù1$? N gqcnP"#k$$Wv[v`+Yz#H:bxuM1p_]ON QOBc* @[YEf+Lnش!tE+ i)I0: )VVkJ{Lo}{|;֬!&VgۏIyK1yGJ ~/~Y|nɣr?FW `arD(.kĀ^$ CEc {#h5x:/|'kq #r5=Xߊ{iqǹ\80j{I(n9^^D*(3tPeb[ epʗ>/WW0o$&~Ss% h<3f}tlCm_-xgfNb|1=L5" l6W<ދ*{72HH{m bQ2rwJ(^I >7T]NV(?:+~S. *pQ0 =1z/uC{l'..3gyY?M$ FgN=mtyb"G4^-;شۆvH{g 7ue;xЛXA;ԆAX0Aոt{*<>)IURde95VBD s^c+Cp(^c.Fr BP3Ql)&RJ!zBC]j=]1wD9V o$i [.*W< B)̩q۝6@r/OtbX1ODMO{@@sÀ2N58dE<S?H4u~'($˗0yH4~kߩDs$\^)xGF3>Ty8`'y FHXJ6[|0^1![!)FהV!m]B{!Qp9C9\ +xSLr"Ə?+G$`Պؾ ~:Z ÊըdbɸTPYj0gfRI9YCmgމ0h]xxCmf|͉&'?`xm5Ջ+,%, c`.sOv?UP:#*vz!/)FUW iu2!Z7g}_WQ8֠F$h0##%6jrWk=t'S3 P9><3~OSNg1:>Jk,8^  6/$Rq: p*[/`ހj{L$?f$jUި7+S,ع$$`B~Bc-x?BC.ǎ"XZbw5nDI QI$=Eߺ[7 $j10xKV,OcaONE@0J %:Crٺ0j86.8(x[EpiP"NJèAcWLBjV₱IIB/>{?)BI){\1ip6 [X7 fřC͂}ahrLdX/Ą/\֌ ]WuKuģ}gDU cݶZp81dl!]bBǭ.+&C~$ded0"7D1B.n_\qCBް ~?a_cZBFKn^0y'yGZ6PWQDITI6YYIxq睟a &P WܶIIIXv|ņ{/&")I:쪵`a^6WB ,kʑFfi{۰tC~TA]g(< >IiȨCKڅL‚{1F7T(H҅i[-]4]y !H&]~1!XgNin,[ * XҐDKKJA !dfW p)AvA#Q"&iZ!9̓qd6"UJL&A"Wp ЭF`If/"ȡ`Μ19 / O? vKSf31a.̮Ջ9j[0H)ۛtUhp9&x!$J(*(Ju-uB2&ݴ8 sFYjtC ʫ )3oy2+ AEv`Ɇn8(J'Î9EpBÊ]Ίzނµp< eNq^ͮT{(!t\ ux;h-h8UrAK:j1.8=qˆh侇.O>w6 IIZ"Bs5?8 W^1/} r â!~fSnv y9I0JOɢq]U(:I R!X-5@i9HJ'LCM[LgڸAޏ.;~h-}rl~|LR*->&OJ<;ğy[F@&k'"=r%a0p.),,?~ %c|Ӄr(n\1 qD"a1?J*W@^3JFS9U^C\-#9KZb'<F&fىFQGAB,'$7bLiRٌ+`Ҋƣ]D+V(Tr5ǠyfHSeItr[b? 3d^N/I&BB!3]+qL_#x4 {<7x鵵*՟`#J, ^2n>.菭[Y0J]Ŵ+7bzja Nw%7[mXρWoX#*-/{xzD;9I&~I=ĎpO<_${&58ˋ>6_nśl&CV EE"$ZP$`IŨQ01v=VպUqFJE7R2 a؃f|]A6O o,cRtbFkvcwmx8‡/õp %a)KS.sAQCayn_3q,;ΝCuNmXyg %ȑNH0Jy=^\13`ȃE!Iu;%.щԄKy2-_)ÎxumXj $%^ {d=RԌ]SѦ^ f| nx\3?tKk q `7p:=T4eETc # r/p^B(DNĽ%F|Ob%?c3d"MV=3nܧlv.fƦݰ!Af '31px Ba*aXJ?fc#>oc<ǡ.4 .SVo6|Sa.8\!}t 2Bma F>7{JDs)P*xw5dPBі|^.= Q*Tbꃤpm2JJ %E{|JDNDKM886D] ^gTaL(N<>'^Kv[b yEs+dF#fnBf1a`^[jHD+C$^^3\$Q) s-hGjQ%N{Lp_1,gbGo [LϦÆ]V4e Nt_5ƽͣո ^߮iǢUͨkD%3KCvdO)OAH%ˈv@Z9nV"wd]J!UɱӃp:B}ND'ZuBP'Zw"W_9 E܃"#6,q3D]Gnax&B7 ~f˗mM9yt&EnrP|#yH4%Hl];* 9"v bΔ2dhxf: )cu\E^zJ^rG2Q]jaR"Q & v?ǟ8v~UCèn [BsJ^f /^TY@#s@e9tvH Ԝ&2&O#">U@U2##K\b ƜUVـZ#ZHsFCKiQ&[-y(}"YҤ@|7=ۚƄV׋`'x'~rqw)>LC^9~uZOaZ‚) 75Λ*bXSR<WE* -3#7<) phiƝWBcF;﷣Սnkijĩ(0~4;3-Xˆ6&RsdΝ Fa4ъ /5r+O 1jX2 pGN'1lP\X$,{ ƨtɍ3i0 %7B1ocI ,m%i$Unt=ƝQtpJ*S"WvYLݏ°t|Hȅ-HdđH%7;dd6LAAEe4 V\z]7g L"q|%R>,_~'MU~ JupFS1< +ك=˗UkX-V9Ceh~ 2R it_O-G[0Bsf5 3o^Iøk t UmJi(2QEaJ)X^W<%X$FY/\[u[IԈ,ЧD9ZS*WaBݮX8@’fW ]¡Tx3%#)6x XK. qQ5O X BX2:EfQ%@!BW}h-a%yDhGw[( 1jb6m(QlP6bfM4 .F\?U͇ͅ==քUxt'SMhjl]"Cog/[l͝ ݘHPHsaRID"Ƣx}gM*7Ǣ-nB}~ HԓhsW on&eaPS?Em ڱ QQkD)J3#M]heY?Ksf% 1ATKO(?DRComjJRbhªzh8NSFݮd(A:^F*# !֑WPp}O.X]kPy Fe9E(j>3/GtPf@{`o!p/qCT3sHqjnK5go.[9 )NT0_Fiݐ"nǦ2L CCR= }5O9Ÿyi^{aǝpL  ~FRurht pѢ8V׌ɃdZ0g\*44,ZW' K Du$~j O2#ܾFH2b[Q"! "PA#GxprgR"*bBF7%凢r(:N mGmI0 hPM6kFp\"CC~PUB%zŏ~) ůx%-IIw. w']qFpE%Z~wL?N67!m_׋$L#3'+'ޫ)^O )I:;_ġ:޾swHyBHKKŻ++{\زo!D@? F2`D$߀Qb#96 .i# *ѡPյP]ހm S IDAT iһw@@4y0d&+HP*aQ=dH*:)2J(K"' C$4*CXm9!D>(rp.!pOh 7$e-9E*M+8jȍ)+2b0_#l{ ⊧|8\y<2,P۝`,NTn@^J!EvljR mwB$ ?y^9ZYNg3ì`o[:}b?L~g1Ml% hiPQE^j~O3q Dg;jUᵅ-rp *6OCF-]~L-5AN?j?U!|s(j{ ?digDjڥ0>o'`; S֮ (˧9)$ PA!a9"tzy5)2yE*(dH CE獃|TR1$w .zFG`a'6mcΟaj^N  B፠0C-:dg(1jqkpEfi`,^#Q~)y! FV cx?+kƧ0e \I9g @GOAY&7[yAvG* uВ3H^Ď: 7rb0a*h+owbtXƧ@+#vy(a<uPgЪn跄T nr$u0Ix륤2]7=DWB~X#AJH m@/S%$/"OB4)( u~%dߋnp&Ot9l /~ŌIB,ګĂ/ah$x[R>B~t;\ı&]%~g5!ӬDU Q8UޅV'B,?ߺ[P҅/'Eq+'k*@%;fȕ儕 a߁Z>6@Ӈ* vr0&&DZ.5ka%X_}{zG~6qd+}>wܭP|Vxn&8QU0qvW~hl"/}`2ԣ@\ QIlr[GGQQ,ۮƍ= \8>K9j?@)ȊtԵX_QO .BdV2#po>vKk1 `|*WwgN}I8r^_ ɥBŬ5<}=\ťz' Bπp8,#JP؎e:]ׅڐBbA} Q?kG`ᣇDKWXy(Fe w13+;+kz Ə Rx"+xâx`MQG^J݆?8/(-+@Dl50P'>8&uBhEIV F<>\{X{ ^?'lFqŠq}T_~CN:P͋e ǎ{z$ųO hufix+7v3d KsQDiT6C}VJ{uH2bcS^\u0t4X"8}&;U8sXs'UظA/z)n'$tZRIt-Ów]X.ohK_s;)SX>GLE@]4b!%ʅ+V?&VMheP%e⑗QL3rE8@E]I_ZV&ݼy m(R -B(w$\}m6U⭣a~a? ZcN9ۜoO/bR-b1Ɩa(R$~K 8{)j\5MwDGpV.yPN.  pyXQd0-bpG KXsݶ0\l8Ʀ $pkK1uNu{D)R$BqU)tn; UŕtZdv2ặ;v?¬鑠7@גp5>_̩+ 4;YYY())9H߉ʌowGyg(oeVր HKREd(ȽXt KHG 7nS(R#k\z񗋳viЪ0Ȼ 5`_3x**p+vغjB <:!QxgnQ %8o=t1456{!'$axm)րoB~w?!_#rKuø:JRuiV6k­9JoiهN{m>DCA~|^z['q ytXCqL7*"[,-Mx<6-\8z{(F[lڴ .G/׹=S'd+IF3|^/[cx,3 g!pɍ=)b"PeiQh0e'@8 8pS_H'B/y\P5VV(cW&;& !`>޼:7 @$k9I  e"V1~|vO*~m{EaR ~/@;oF5]uyy=~;;;-P߀M[b%| F\h$nCa&-u(s_jzXطߊ 1-Ly()~߉$ 6k?=7`MnGχ;w\Uگ UUU"/& +~\˔*UY& te%!Hǐ"z &r*Eq Ͷ K(5˜l PX x(ba+ڛhsO+ٓ ^ _@2~@;D$׫DϥVR& 0o%b]{"iMಽd V"0ߤ%=蟥"~33FpǟJ'!^Z*\7# /bɈN7/X([wzD:Q7hA(Ѩu;Ɵ17(n) M ? MN-۩2bfLQÙ96ZSMQJRj=OyTm;(⬜&^C6sx,~ *!'L7&*AGܑ8K‰G9Q̻/J#TJ)0~*Xz)+Igu8\=A=0}†MJg=3 H}~-"o׉Y ~qB,$P1kK<_,dbp :Ƴt\SO>GjpA>| : a`3} HJh|.E僱d,^ۈEbmD mEl߯B(c'|b*L|ԍklx\"QįYߠb0c 7N7~kuj,\゚f{]Q{t'*Q^&[ |݌kvPtsE7*,[%n G?"*D%Ni 2Bhmc,Z iT"^Bl݇;kw.2x р͢=~*)'%8oV4~)Dz3l8b퇫/KL5aO0eF$NAat,»_”a#07=,iG~JBgᒌ ekj|U3/߶nނuِ:}8Su@UMQѨX%r767@e v $K`lrɖl6'ށ!J(٬F6q,[e͌׽.`Z9Oє|0f)8ĊU[C߰'CB_l&N5lKu݌+잹XP qNsSc>>Pp8P/˜(Ť4-:p+_lוf/ici+U˴uk*RK v bvuqQd=v6aZ{ {4Y^:h( >yB .]ZS7Pc8Cc a5Xk[/:݋aV;@^6`Ќv`C~{f"0ص'DBG$&nPcH* wsQws#}'}!=`RpF|)U' T#}8GY[_;¸WxyL431Ay65 | f6^igȑA%$C`TJG \8~\ϝ9s3X8z&ߑ ۝˸َ|2+`/w>FcN1 PQ L t0%u<)#~9 j2i`PaI%XR[xIDAT᪠&)LsiQE_bPƂŽ~|*Zk*6a{ysכ=;;Jf/DuSEѼXSLYW3<<؇lf 430ώ቗q=KC̸iiQh]}i9@O(`fè+ ḇf팃gcDA,^¬aVgyv+??ĭY/qZycD]?=~JTN;n1Q 1+Y6vp\L+h?چp$J"A$;aP0P۸]>#q7gtf$L"gTc*c 94谱e/DEUʘw_=ψ]f1wZtq:0Sj+ĽBћܖ!,2*&i{x!ic |/o97(wOӚbJo-}/o朌 o`-Yga=SȘ(Kըxf9+#8>Tw NM_dUrj%ض4x=| ц4~gs002 f4V觎 1Et`oG*,kBmWkrbEcQmEiƉ2qSFm;7[*Tr'͝…8VT"fg樂w4`ׄfwfTT;M8}T9h[Zv 㚥Vŋ[ob$D@:ؘh) `8숂Z$xlGy( j$c>#n<'7/䟸sk40U0j2/4ΰ6=^k<RT*̶NNw[ ~i&{2ޤe" Ƴ鵔 =u5 p5XhnY>ΐg0~c/!gbYjTyaOeUۿeR{1cyE]l@: + CF t]R j۹?gr ZOfC =ԃ㽟tzK0ӮuN@tAP)p&(~2LB'ISWW)I^ɨΤW2)tt:9iKAB-֑C"4 eFh4֨5E1/x(```׉ĬR'0XxqoAܵZ:b:>աEyĆ}(tNwS$X=/xOZŔG©8;LDwZs2$af"bb磹=?цL?f<َrj HId 'r#4Π  d;\ztB4#%o]מhE%57\ szǹ_qe:8 Q@_ vjXo ǐK=>7o>UˎNK4]E,v]SI[nk=o/G#ф?}7lX`Ey{H!C| Baq%L{cnL*UO$u1DcD}q(<,9/c_/laV`Uc.QqH'I,SLFzokk+w".qk޾~T\x㉶=zmAE?CUhO>:~rj;–gE8 Do{)w|" GcxztEn\3BˑV _5.3E4lznRҚP>3;])rr38*z_ kB2_ ݄I= ʪ\Ԥ59&Qnqq1Hk4"OD:1?޽@ i| P~A0T+|=ͅVAo8[2+)RÔ4]œ0ں}Wc +0k (LS7*f6[U%r~cv;-F-0?P63z/2ݝfBZACxy(;4T,x(u28Uac)'^\ ʊ 9>|S>TVVrS'm@֭[9,X!Qh'r7mڄ={pO+ôiӰ`Xه~b 58g]aI}&`k'GMugT\a o=^Ph8ΎՆƆ7B[3Kjf2EIzl5"hXL _cN/^ܜPB^mR b!EԸt-^ YwKZ9eC6y&];),,T($éǡCjEr0#+rLL\D($F(ꫯ܀D R%QTT0_Ž`r{gP{bUZ͏gGpK1z јBs8A}%fy:x< oV5NYRld< Lk ?sDs<2\7(^{B .+,QqZؑя~ȑlwY^jSN6( w q $nܺJPUBF5yh\RE-6F>ܵ~>La1=Ńk33N(1j'{$+՗(. P|qJ| E/4qeO(~Atp^@YrF(u0gάԪlcc(ܸD$I 4oƈ`""8B"Ah!>l۶5k4)Lٻw_y={'ETu9~ ;߾![;t*y߿LR͋8>Q;ã135x̬wF,Y^؉w9L >Wy9J*  |2mtSKx@$W}1}v7aRM|ui r9Gxxw4J 3cZLC=OY lc2:sW Ԣp5lEeB"(G5qBƻL7KcHH2#Q;htEeޮ6*jMs @IH /L{;,e3k橂)Ʋ,n;Wa0gm%kЪar7BA(7a[ -meQN%_g !3l%^>TAQ:RL bRrE