diff options
Diffstat (limited to 'libhb')
-rw-r--r-- | libhb/common.h | 58 | ||||
-rw-r--r-- | libhb/hb.c | 382 | ||||
-rw-r--r-- | libhb/hb.h | 3 |
3 files changed, 302 insertions, 141 deletions
diff --git a/libhb/common.h b/libhb/common.h index 236f96895..a5ba78730 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -78,6 +78,9 @@ 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_job_s hb_job_t; typedef struct hb_title_set_s hb_title_set_t; typedef struct hb_title_s hb_title_t; @@ -133,8 +136,9 @@ 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 0 -#define HB_KEEP_HEIGHT 1 +#define HB_KEEP_WIDTH 0x01 +#define HB_KEEP_HEIGHT 0x02 +#define HB_KEEP_DISPLAY_ASPECT 0x04 void hb_fix_aspect( hb_job_t * job, int keep ); void hb_job_set_encoder_preset (hb_job_t *job, const char *preset); @@ -232,6 +236,34 @@ struct hb_container_s 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 +}; + // Update win/CS/HandBrake.Interop/HandBrakeInterop/HbLib/hb_subtitle_config_s.cs when changing this struct struct hb_subtitle_config_s { @@ -379,6 +411,14 @@ struct hb_title_set_s 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 @@ -422,13 +462,13 @@ struct hb_job_s struct { - int 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; + 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; diff --git a/libhb/hb.c b/libhb/hb.c index 303781cc5..d1e494195 100644 --- a/libhb/hb.c +++ b/libhb/hb.c @@ -913,77 +913,78 @@ int hb_detect_comb( hb_buffer_t * buf, int color_equal, int color_diff, int thre } /** - * Calculates job width and height for anamorphic content, + * Calculates destination 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 + * Returns calculated geometry + * @param source_geometry - Pointer to source geometry info + * @param ui_geometry - Pointer to requested destination parameters */ -void hb_set_anamorphic_size( hb_job_t * job, - int *output_width, int *output_height, - int *output_par_width, int *output_par_height ) +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. */ - hb_title_t * title = job->title; - int cropped_width = title->width - job->crop[2] - job->crop[3] ; - int cropped_height = title->height - job->crop[0] - job->crop[1] ; - double storage_aspect = (double)cropped_width / (double)cropped_height; - int mod = job->modulus ? job->modulus : 16; - double aspect = title->aspect; - - int64_t pixel_aspect_width = job->anamorphic.par_width; - int64_t pixel_aspect_height = job->anamorphic.par_height; + 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) : 16; + + // 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( title->width == 720 && job->anamorphic.itu_par ) + 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 = aspect * 9.; + int iaspect = src_geo->width * src_par.num * 9. / + (src_geo->height * src_par.den); /* Handle ITU PARs */ - if (title->height == 480) + if (src_geo->height == 480) { /* It's NTSC */ if (iaspect == 16) { /* It's widescreen */ - pixel_aspect_width = 40; - pixel_aspect_height = 33; + dst_par_num = 40; + dst_par_den = 33; } else if (iaspect == 12) { /* It's 4:3 */ - pixel_aspect_width = 10; - pixel_aspect_height = 11; + dst_par_num = 10; + dst_par_den = 11; } } - else if (title->height == 576) + else if (src_geo->height == 576) { /* It's PAL */ - if(iaspect == 16) + if (iaspect == 16) { /* It's widescreen */ - pixel_aspect_width = 16; - pixel_aspect_height = 11; + dst_par_num = 16; + dst_par_den = 11; } else if (iaspect == 12) { /* It's 4:3 */ - pixel_aspect_width = 12; - pixel_aspect_height = 11; + dst_par_num = 12; + dst_par_den = 11; } } } - /* Figure out what width the source would display at. */ - int source_display_width = cropped_width * (double)pixel_aspect_width / - (double)pixel_aspect_height ; - /* 3 different ways of deciding output dimensions: - 1: Strict anamorphic, preserve source dimensions @@ -993,82 +994,135 @@ void hb_set_anamorphic_size( hb_job_t * job, int width, height; int maxWidth, maxHeight; - maxWidth = MULTIPLE_MOD_DOWN( job->maxWidth, mod ); - maxHeight = MULTIPLE_MOD_DOWN( job->maxHeight, mod ); + 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( job->anamorphic.mode ) + switch (ui_geo->mode) { - case 1: - /* Strict anamorphic */ - *output_width = MULTIPLE_MOD( cropped_width, 2 ); - *output_height = MULTIPLE_MOD( cropped_height, 2 ); - // adjust the source PAR for new width/height - // new PAR = source PAR * ( old width / new_width ) * ( new_height / old_height ) - pixel_aspect_width = (int64_t)title->pixel_aspect_width * cropped_width * (*output_height); - pixel_aspect_height = (int64_t)title->pixel_aspect_height * (*output_width) * cropped_height; - break; - - case 2: + 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 = ui_geo->width; + height = MULTIPLE_MOD((int)(width / dar), mod); + } + else + { + height = ui_geo->height; + width = MULTIPLE_MOD((int)(height * dar), mod); + } + } + else + { + width = ui_geo->width; + height = ui_geo->height; + } + if (maxWidth && (width > maxWidth)) + { + width = maxWidth; + height = MULTIPLE_MOD((int)(width / dar), mod); + } + if (maxHeight && (height > maxHeight)) + { + height = maxHeight; + width = MULTIPLE_MOD((int)(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(cropped_width, 2); + height = MULTIPLE_MOD(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 mod16-compliant dimensions, - - Allows users to set the width - */ - width = job->width; - // height: Gets set later, ignore user job->height value - - /* Gotta handle bounding dimensions. - If the width is too big, just reset it with no rescaling. - Instead of using the aspect-scaled job height, - we need to see if the job width divided by the storage aspect - is bigger than the max. If so, set it to the max (this is sloppy). - If not, set job height to job width divided by storage aspect. - */ - - /* Time to get picture width that divide cleanly.*/ - width = MULTIPLE_MOD( width, mod); - - if ( maxWidth && (maxWidth < job->width) ) - width = maxWidth; + * - Uses mod-compliant dimensions, set by user + * - Allows users to set the either width *or* height + */ + if (!keep_height) + { + width = MULTIPLE_MOD(ui_geo->width, mod); + height = MULTIPLE_MOD((int)(width / storage_aspect + 0.5), mod); + } + else + { + height = MULTIPLE_MOD(ui_geo->height, mod); + width = MULTIPLE_MOD((int)(height * storage_aspect + 0.5), mod); + } - /* Verify these new dimensions don't violate max height and width settings */ - height = ((double)width / storage_aspect) + 0.5; + if (maxWidth && (maxWidth < width)) + { + width = maxWidth; + height = MULTIPLE_MOD((int)(width / storage_aspect + 0.5), mod); + } - /* Time to get picture height that divide cleanly.*/ - height = MULTIPLE_MOD( height, mod); - - if ( maxHeight && (maxHeight < height) ) + if (maxHeight && (maxHeight < height)) { height = maxHeight; - width = ((double)height * storage_aspect) + 0.5; - width = MULTIPLE_MOD( width, mod); + width = MULTIPLE_MOD((int)(height * storage_aspect + 0.5), mod); } - /* The film AR is the source's display width / cropped source height. - The output display width is the output height * film AR. - The output PAR is the output display width / output storage width. */ - pixel_aspect_width = (int64_t)height * cropped_width * pixel_aspect_width; - pixel_aspect_height = (int64_t)width * cropped_height * pixel_aspect_height; - - /* Pass the results back to the caller */ - *output_width = width; - *output_height = height; - break; - - case 3: + /* 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)job->width / (double)job->height; - width = job->width; - height = job->height; - + storage_aspect = (double)ui_geo->width / ui_geo->height; + width = ui_geo->width; + height = ui_geo->height; + /* Time to get picture dimensions that divide cleanly.*/ - width = MULTIPLE_MOD( width, mod); - height = MULTIPLE_MOD( height, mod); - + width = MULTIPLE_MOD(width, mod); + height = MULTIPLE_MOD(height, mod); + /* Bind to max dimensions */ - if( maxWidth && width > maxWidth ) + if (maxWidth && width > maxWidth) { width = maxWidth; // If we are keeping the display aspect, then we are going @@ -1079,64 +1133,128 @@ void hb_set_anamorphic_size( hb_job_t * job, // But otherwise, PAR and DAR will change the least // if we stay as close as possible to the requested // storage aspect. - if ( !job->anamorphic.keep_display_aspect ) + if (!keep_display_aspect) { - height = ((double)width / storage_aspect) + 0.5; - height = MULTIPLE_MOD( height, mod); + height = width / storage_aspect + 0.5; + height = MULTIPLE_MOD(height, mod); } } - if( maxHeight && height > maxHeight ) + if (maxHeight && height > maxHeight) { height = maxHeight; // Ditto, see comment above - if ( !job->anamorphic.keep_display_aspect ) + if (!keep_display_aspect) { - width = ((double)height * storage_aspect) + 0.5; - width = MULTIPLE_MOD( width, mod); + width = height * storage_aspect + 0.5; + width = MULTIPLE_MOD(width, mod); } } - - /* That finishes the storage dimensions. On to display. */ - if( job->anamorphic.dar_width && job->anamorphic.dar_height ) + + /* 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. */ - pixel_aspect_width = (int64_t)height * job->anamorphic.dar_width / job->anamorphic.dar_height; - pixel_aspect_height = width; + dst_par_num = (int64_t)height * ui_geo->dar.num / + ui_geo->dar.den; + dst_par_den = width; } else { - /* If we're doing ana 3 and not specifying a DAR, care needs to be taken. - This indicates a PAR is potentially being set by the interface. But - this is an output PAR, to correct a source, and it should not be assumed - that it properly creates a display aspect ratio when applied to the source, - which could easily be stored in a different resolution. */ - if( job->anamorphic.keep_display_aspect ) + if (keep_display_aspect) { - /* We can ignore the possibility of a PAR change */ - pixel_aspect_width = (int64_t)height * ( (double)source_display_width / (double)cropped_height ); - pixel_aspect_height = width; + /* 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 { - int output_display_width = width * (double)pixel_aspect_width / - (double)pixel_aspect_height; - pixel_aspect_width = output_display_width; - pixel_aspect_height = width; + /* 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; } } - - /* Back to caller */ - *output_width = width; - *output_height = height; - break; + } 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( &pixel_aspect_width, &pixel_aspect_height, - pixel_aspect_width, pixel_aspect_height, 65535 ); - hb_reduce( output_par_width, output_par_height, - pixel_aspect_width, pixel_aspect_height ); + 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; } /** diff --git a/libhb/hb.h b/libhb/hb.h index 6939d8082..e958d68d9 100644 --- a/libhb/hb.h +++ b/libhb/hb.h @@ -72,6 +72,9 @@ hb_buffer_t * hb_read_preview( hb_handle_t * h, int title_idx, int preview ); void hb_get_preview( hb_handle_t *, hb_job_t *, int, uint8_t * ); void hb_set_size( hb_job_t *, double ratio, int pixels ); +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 ); |