/* test.c
Copyright (c) 2003-2019 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
#include
#ifdef SYS_SunOS
#include
#endif
#if defined( __MINGW32__ )
#include
#include
#endif
#if defined( PTW32_STATIC_LIB )
#include
#endif
#include "hb.h"
#include "lang.h"
#include "parsecsv.h"
#if HB_PROJECT_FEATURE_QSV
#include "qsv_common.h"
#endif
#if defined( __APPLE_CC__ )
#import
#include
#include
#include
#include
#endif
#define LAPSHARP_DEFAULT_PRESET "medium"
#define UNSHARP_DEFAULT_PRESET "medium"
#define CHROMA_SMOOTH_DEFAULT_PRESET "medium"
#define NLMEANS_DEFAULT_PRESET "medium"
#define DEINTERLACE_DEFAULT_PRESET "default"
#define DECOMB_DEFAULT_PRESET "default"
#define DETELECINE_DEFAULT_PRESET "default"
#define COMB_DETECT_DEFAULT_PRESET "default"
#define HQDN3D_DEFAULT_PRESET "medium"
#define ROTATE_DEFAULT "angle=180:hflip=0"
#define DEBLOCK_DEFAULT_PRESET "medium"
/* Options */
static int debug = HB_DEBUG_ALL;
static int json = 0;
static int inline_parameter_sets = -1;
static int align_av_start = -1;
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 = -1;
static int pad_disable = 0;
static char * pad = NULL;
static int deinterlace_disable = 0;
static int deinterlace_custom = 0;
static char * deinterlace = NULL;
static int deblock_disable = 0;
static int deblock_custom = 0;
static char * deblock = NULL;
static char * deblock_tune = NULL;
static int hqdn3d_disable = 0;
static int hqdn3d_custom = 0;
static char * hqdn3d = NULL;
static int nlmeans_disable = 0;
static int nlmeans_custom = 0;
static char * nlmeans = NULL;
static char * nlmeans_tune = NULL;
static int chroma_smooth_disable = 0;
static int chroma_smooth_custom = 0;
static char * chroma_smooth = NULL;
static char * chroma_smooth_tune = NULL;
static int unsharp_disable = 0;
static int unsharp_custom = 0;
static char * unsharp = NULL;
static char * unsharp_tune = NULL;
static int lapsharp_disable = 0;
static int lapsharp_custom = 0;
static char * lapsharp = NULL;
static char * lapsharp_tune = NULL;
static int detelecine_disable = 0;
static int detelecine_custom = 0;
static char * detelecine = NULL;
static int comb_detect_disable = 0;
static int comb_detect_custom = 0;
static char * comb_detect = NULL;
static int decomb_disable = 0;
static int decomb_custom = 0;
static char * decomb = NULL;
static char * rotate = NULL;
static int grayscale = -1;
static char * vcodec = NULL;
static int audio_all = -1;
static char ** audio_copy_list = NULL;
static char ** audio_lang_list = NULL;
static char ** atracks = NULL;
static char ** acodecs = NULL;
static char ** abitrates = NULL;
static char ** aqualities = NULL;
static char ** arates = NULL;
static char ** mixdowns = NULL;
static char ** normalize_mix_level = NULL;
static char ** audio_dither = NULL;
static char ** dynamic_range_compression = NULL;
static char ** audio_gain = NULL;
static char ** acompressions = NULL;
static char * acodec_fallback = NULL;
static char ** anames = NULL;
static char ** subtitle_lang_list = NULL;
static char ** subtracks = NULL;
static char ** subforce = NULL;
static int subtitle_all = -1;
static int subburn = -1;
static int subburn_native = -1;
static int subdefault = 0;
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 char ** ssafile = NULL;
static char ** ssaoffset = NULL;
static char ** ssalang = NULL;
static int ssadefault = -1;
static int ssaburn = -1;
static int width = 0;
static int height = 0;
static int crop[4] = { -1,-1,-1,-1 };
static int loose_crop = -1;
static char * vrate = NULL;
static float vquality = HB_INVALID_VIDEO_QUALITY;
static int vbitrate = 0;
static int mux = 0;
static int anamorphic_mode = -1;
static int modulus = 0;
static int par_height = -1;
static int par_width = -1;
static int display_width = -1;
static int keep_display_aspect = -1;
static int itu_par = -1;
static int angle = 0;
static int chapter_start = 0;
static int chapter_end = 0;
static int chapter_markers = -1;
static char * marker_file = NULL;
static char * encoder_preset = NULL;
static char * encoder_tune = NULL;
static char * encoder_profile = NULL;
static char * encoder_level = NULL;
static char * advanced_opts = NULL;
static int maxHeight = 0;
static int maxWidth = 0;
static int fastfirstpass = -1;
static char * preset_export_name = NULL;
static char * preset_export_desc = NULL;
static char * preset_export_file = NULL;
static char * preset_name = NULL;
static char * queue_import_name = NULL;
static int cfr = -1;
static int mp4_optimize = -1;
static int ipod_atom = -1;
static int color_matrix_code = -1;
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;
#if HB_PROJECT_FEATURE_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 volatile int work_done = 0;
static void SigHandler( int );
/* Utils */
static void ShowHelp(void);
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, hb_dict_t *preset_dict );
static hb_dict_t * PreparePreset( const char *preset_name );
static hb_dict_t * PrepareJob( hb_handle_t *h, hb_title_t *title,
hb_dict_t *preset_dict );
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;
/* Terminal detection */
static int stdout_tty = 0;
static int stderr_tty = 0;
static char * stdout_sep = "\r";
static char * stderr_sep = "\r";
static void test_tty()
{
#if defined(__MINGW32__)
HANDLE handle;
handle = (HANDLE) _get_osfhandle(_fileno(stdout));
if ((handle != INVALID_HANDLE_VALUE) && (GetFileType(handle) == FILE_TYPE_CHAR))
{
stdout_tty = 1;
}
handle = (HANDLE) _get_osfhandle(_fileno(stderr));
if ((handle != INVALID_HANDLE_VALUE) && (GetFileType(handle) == FILE_TYPE_CHAR))
{
stderr_tty = 1;
}
#else
if (isatty(1) == 1)
{
stdout_tty = 1;
}
if (isatty(2) == 1)
{
stderr_tty = 1;
}
#endif
/*
if (stdout_tty == 1) stdout_sep = "\r";
if (stderr_tty == 1) stderr_sep = "\r";
*/
}
/****************************************************************************
* 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 != NULL)
{
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
}
static volatile int job_running = 0;
void EventLoop(hb_handle_t *h, hb_dict_t *preset_dict)
{
/* Wait... */
work_done = 0;
while (!die && !work_done)
{
#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;
}
}
#else
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;
}
}
}
#endif
hb_snooze(200);
HandleEvents( h, preset_dict );
}
job_running = 0;
}
int RunQueueJob(hb_handle_t *h, hb_dict_t *job_dict)
{
if (job_dict == NULL)
{
return -1;
}
char * json_job;
json_job = hb_value_get_json(job_dict);
hb_value_free(&job_dict);
if (json_job == NULL)
{
fprintf(stderr, "Error in setting up job! Aborting.\n");
return -1;
}
hb_add_json(h, json_job);
free(json_job);
job_running = 1;
hb_start( h );
EventLoop(h, NULL);
return 0;
}
int RunQueue(hb_handle_t *h, const char *queue_import_name)
{
hb_value_t * queue = hb_value_read_json(queue_import_name);
if (hb_value_type(queue) == HB_VALUE_TYPE_DICT)
{
return RunQueueJob(h, hb_dict_get(queue, "Job"));
}
else if (hb_value_type(queue) == HB_VALUE_TYPE_ARRAY)
{
int ii, count, result = 0;
count = hb_value_array_len(queue);
for (ii = 0; ii < count; ii++)
{
hb_dict_t * entry = hb_value_array_get(queue, ii);
int ret = RunQueueJob(h, hb_dict_get(entry, "Job"));
if (ret < 0)
{
result = ret;
}
if (die)
{
break;
}
}
return result;
}
else
{
fprintf(stderr, "Error: Invalid queue file %s\n", queue_import_name);
return -1;
}
return 0;
}
int main( int argc, char ** argv )
{
hb_handle_t * h;
hb_global_init();
hb_presets_builtin_update();
hb_presets_cli_default_init();
/* Init libhb */
h = hb_init(4); // Show all logging until debug level is parsed
test_tty(); // Terminal detection
// Get utf8 command line if windows
get_argv_utf8(&argc, &argv);
/* Parse command line */
if( ParseOptions( argc, argv ) ||
CheckOptions( argc, argv ) )
{
hb_log_level_set(h, debug);
goto cleanup;
}
hb_log_level_set(h, debug);
/* Register our error handler */
hb_register_error_handler(&hb_cli_error_handler);
hb_dvd_set_dvdnav( dvdnav );
/* Show version */
fprintf( stderr, "%s - %s - %s\n",
HB_PROJECT_TITLE, HB_PROJECT_HOST_TITLE, HB_PROJECT_URL_WEBSITE );
/* Geeky */
fprintf( stderr, "%d CPU%s detected\n", hb_get_cpu_count(),
hb_get_cpu_count() > 1 ? "s" : "" );
/* Exit ASAP on Ctrl-C */
signal( SIGINT, SigHandler );
if (queue_import_name != NULL)
{
hb_system_sleep_prevent(h);
RunQueue(h, queue_import_name);
}
else
{
// Apply all command line overrides to the preset that are possible.
// Some command line options are applied later to the job
// (e.g. chapter names, explicit audio & subtitle tracks).
hb_dict_t *preset_dict = PreparePreset(preset_name);
if (preset_dict == NULL)
{
// An appropriate error message should have already
// been spilled by PreparePreset.
done_error = HB_ERROR_WRONG_INPUT;
goto cleanup;
}
if (preset_export_name != NULL)
{
hb_dict_set(preset_dict, "PresetName",
hb_value_string(preset_export_name));
if (preset_export_desc != NULL)
{
hb_dict_set(preset_dict, "PresetDescription",
hb_value_string(preset_export_desc));
}
if (preset_export_file != NULL)
{
hb_presets_write_json(preset_dict, preset_export_file);
}
else
{
char *json;
json = hb_presets_package_json(preset_dict);
fprintf(stdout, "%s\n", json);
}
// If the user requested to export a preset, but not to
// transcode or scan a file, exit here.
if (input == NULL ||
(!titlescan && titleindex != 0 && output == NULL))
{
hb_value_free(&preset_dict);
goto cleanup;
}
}
/* 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_scan(h, input, titleindex, preview_count, store_previews,
min_title_duration * 90000LL);
EventLoop(h, preset_dict);
hb_value_free(&preset_dict);
}
cleanup:
/* Clean up */
hb_close(&h);
hb_global_close();
hb_str_vfree(audio_copy_list);
hb_str_vfree(abitrates);
hb_str_vfree(acompressions);
hb_str_vfree(aqualities);
hb_str_vfree(audio_dither);
hb_str_vfree(acodecs);
hb_str_vfree(arates);
hb_str_vfree(atracks);
hb_str_vfree(audio_lang_list);
hb_str_vfree(audio_gain);
hb_str_vfree(dynamic_range_compression);
hb_str_vfree(mixdowns);
hb_str_vfree(subtitle_lang_list);
hb_str_vfree(subtracks);
free(acodec_fallback);
free(native_language);
free(format);
free(input);
free(output);
free(preset_name);
free(encoder_preset);
free(encoder_tune);
free(advanced_opts);
free(encoder_profile);
free(encoder_level);
free(rotate);
free(deblock);
free(deblock_tune);
free(detelecine);
free(deinterlace);
free(decomb);
free(hqdn3d);
free(nlmeans);
free(nlmeans_tune);
free(chroma_smooth);
free(chroma_smooth_tune);
free(unsharp);
free(unsharp_tune);
free(lapsharp);
free(lapsharp_tune);
free(preset_export_name);
free(preset_export_desc);
free(preset_export_file);
// 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 )
{
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, " + index %d\n", title->index);
}
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->geometry.width, title->geometry.height,
title->geometry.par.num, title->geometry.par.den,
(float)title->dar.num / title->dar.den,
(float)title->vrate.num / title->vrate.den );
fprintf( stderr, " + autocrop: %d/%d/%d/%d\n", title->crop[0],
title->crop[1], title->crop[2], title->crop[3] );
fprintf( stderr, " + chapters:\n" );
for( i = 0; i < hb_list_count( title->list_chapter ); i++ )
{
hb_chapter_t * chapter;
chapter = hb_list_item( title->list_chapter, i );
fprintf( stderr, " + %d: duration %02d:%02d:%02d\n",
chapter->index, chapter->hours, chapter->minutes,
chapter->seconds );
}
fprintf( stderr, " + audio tracks:\n" );
for( i = 0; i < hb_list_count( title->list_audio ); i++ )
{
hb_audio_config_t *audio;
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++ )
{
hb_subtitle_t *subtitle;
subtitle = hb_list_item( title->list_subtitle, i );
fprintf(stderr, " + %d, %s\n", i + 1, subtitle->lang);
}
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 )
{
if (json)
{
hb_dict_t * title_set_dict;
char * title_set_json;
title_set_dict = hb_title_set_to_dict(title_set);
title_set_json = hb_value_get_json(title_set_dict);
hb_value_free(&title_set_dict);
fprintf(stdout, "JSON Title Set: %s\n", title_set_json);
free(title_set_json);
}
else
{
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;
}
void write_chapter_names(hb_dict_t *job_dict, const char *marker_file)
{
if (marker_file == NULL)
return;
hb_csv_file_t * file = hb_open_csv_file(marker_file);
hb_csv_cell_t * cell;
int row = 0;
if (file == NULL)
{
fprintf(stderr, "Cannot open chapter marker file, using defaults\n");
return;
}
fprintf(stderr, "Reading chapter markers from file %s\n", marker_file);
hb_value_array_t *chapter_array;
chapter_array = hb_dict_get(hb_dict_get(job_dict, "Destination"),
"ChapterList");
if (chapter_array == NULL)
return;
/* 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;
}
/* We have a chapter name */
if (cell->cell_col == 1 && row == cell->cell_row)
{
/* If we have a valid chapter, add chapter entry */
hb_dict_t *chapter_dict = hb_value_array_get(chapter_array, row);
if (chapter_dict != NULL)
{
hb_dict_set(chapter_dict, "Name",
hb_value_string(cell->cell_text));
}
}
hb_dispose_cell( cell );
}
hb_close_csv_file( file );
}
static void lang_list_remove(hb_value_array_t *list, const char *lang)
{
int count = hb_value_array_len(list);
int ii;
for (ii = count - 1; ii >= 0; ii--)
{
const char *tmp = hb_value_get_string(hb_value_array_get(list, ii));
if (!strncmp(lang, tmp, 4))
hb_value_array_remove(list, ii);
}
}
static void show_progress_json(hb_state_t * state)
{
hb_dict_t * state_dict;
char * state_json;
state_dict = hb_state_to_dict(state);
state_json = hb_value_get_json(state_dict);
hb_value_free(&state_dict);
fprintf(stdout, "Progress: %s\n", state_json);
free(state_json);
fflush(stderr);
}
static int HandleEvents(hb_handle_t * h, hb_dict_t *preset_dict)
{
hb_state_t s;
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 (json)
{
show_progress_json(&s);
break;
}
if (p.preview_cur)
{
fprintf(stderr, "%sScanning title %d of %d, preview %d, %.2f %%",
stderr_sep, p.title_cur, p.title_count, p.preview_cur, 100 * p.progress);
}
else
{
fprintf(stderr, "%sScanning title %d of %d, %.2f %%",
stderr_sep, 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;
if (job_running)
{
// SCANDONE generated by a scan during execution of the job
break;
}
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_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 %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;
}
fprintf( stderr, "+ Using preset: %s\n",
hb_value_get_string(hb_dict_get(preset_dict, "PresetName")));
PrintTitleInfo(title, title_set->feature);
// All overrides to the preset are complete
// Initialize the job from preset + overrides
// and apply job specific command line overrides
hb_dict_t *job_dict = PrepareJob(h, title, preset_dict);
if (job_dict == NULL)
{
die = 1;
return -1;
}
char * json_job;
json_job = hb_value_get_json(job_dict);
hb_value_free(&job_dict);
if (json_job == NULL)
{
fprintf(stderr, "Error in setting up job! Aborting.\n");
die = 1;
return -1;
}
hb_add_json(h, json_job);
free(json_job);
job_running = 1;
hb_start( h );
break;
}
#define p s.param.working
case HB_STATE_SEARCHING:
if (json)
{
show_progress_json(&s);
break;
}
fprintf( stdout, "%sEncoding: task %d of %d, Searching for start time, %.2f %%",
stdout_sep, p.pass, p.pass_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:
if (json)
{
show_progress_json(&s);
break;
}
fprintf( stdout, "%sEncoding: task %d of %d, %.2f %%",
stdout_sep, p.pass, p.pass_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 (json)
{
show_progress_json(&s);
break;
}
if (show_mux_warning)
{
fprintf( stdout, "%sMuxing: this may take awhile...", stdout_sep );
fflush(stdout);
show_mux_warning = 0;
}
break;
}
#undef p
#define p s.param.working
case HB_STATE_WORKDONE:
/* Print error if any, then exit */
if (json)
{
show_progress_json(&s);
}
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;
work_done = 1;
job_running = 0;
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 showFilterPresets(FILE* const out, int filter_id)
{
char ** names = hb_filter_get_presets_short_name(filter_id);
int ii, count = 0;
// Count number of entries we want to display
for (ii = 0; names[ii] != NULL; ii++)
{
if (!strcasecmp(names[ii], "custom") || // skip custom
!strcasecmp(names[ii], "off") || // skip off
!strcasecmp(names[ii], "default")) // skip default
continue;
count++;
}
// If there are no entries, display nothing.
if (count == 0)
{
return;
}
fprintf(out, " Presets:\n");
for (ii = 0; names[ii] != NULL; ii++)
{
if (!strcasecmp(names[ii], "custom") || // skip custom
!strcasecmp(names[ii], "off") || // skip off
!strcasecmp(names[ii], "default")) // skip default
continue;
fprintf(out, " %s\n", names[ii]);
}
hb_str_vfree(names);
}
static void showFilterTunes(FILE* const out, int filter_id)
{
char ** tunes = hb_filter_get_tunes_short_name(filter_id);
int ii, count = 0;
// Count number of entries we want to display
for (ii = 0; tunes[ii] != NULL; ii++)
{
/*
if (!strcasecmp(tunes[ii], "custom") || // skip custom
!strcasecmp(tunes[ii], "off") || // skip off
!strcasecmp(tunes[ii], "default")) // skip default
continue;
*/
count++;
}
// If there are no entries, display nothing.
if (count == 0)
{
return;
}
fprintf(out, " Tunes:\n");
for (ii = 0; tunes[ii] != NULL; ii++)
{
/*
if (!strcasecmp(tunes[ii], "custom") || // skip custom
!strcasecmp(tunes[ii], "off") || // skip off
!strcasecmp(tunes[ii], "default")) // skip default
continue;
*/
fprintf(out, " %s\n", tunes[ii]);
}
hb_str_vfree(tunes);
}
static void showFilterKeys(FILE* const out, int filter_id)
{
char ** keys = hb_filter_get_keys(filter_id);
char * colon = "", * newline;
int ii, linelen = 0;
fprintf(out, " Custom Format:\n"
" ");
for (ii = 0; keys[ii] != NULL; ii++)
{
int c = tolower(keys[ii][0]);
int len = strlen(keys[ii]) + 3;
if (linelen + len > 48)
{
newline = "\n ";
linelen = 0;
}
else
{
newline = "";
}
fprintf(out, "%s%s%s=%c", colon, newline, keys[ii], c);
linelen += len;
colon = ":";
}
fprintf(out, "\n");
hb_str_vfree(keys);
}
static void showFilterDefault(FILE* const out, int filter_id)
{
const char * preset = "default";
fprintf(out, " Default:\n"
" ");
switch (filter_id)
{
case HB_FILTER_UNSHARP:
preset = UNSHARP_DEFAULT_PRESET;
break;
case HB_FILTER_LAPSHARP:
preset = LAPSHARP_DEFAULT_PRESET;
break;
case HB_FILTER_CHROMA_SMOOTH:
preset = CHROMA_SMOOTH_DEFAULT_PRESET;
break;
case HB_FILTER_NLMEANS:
preset = NLMEANS_DEFAULT_PRESET;
break;
case HB_FILTER_DEINTERLACE:
preset = DEINTERLACE_DEFAULT_PRESET;
break;
case HB_FILTER_DECOMB:
preset = DECOMB_DEFAULT_PRESET;
break;
case HB_FILTER_DETELECINE:
preset = DETELECINE_DEFAULT_PRESET;
break;
case HB_FILTER_HQDN3D:
preset = HQDN3D_DEFAULT_PRESET;
break;
case HB_FILTER_COMB_DETECT:
preset = COMB_DETECT_DEFAULT_PRESET;
break;
case HB_FILTER_DEBLOCK:
preset = DEBLOCK_DEFAULT_PRESET;
break;
default:
break;
}
switch (filter_id)
{
case HB_FILTER_DEINTERLACE:
case HB_FILTER_NLMEANS:
case HB_FILTER_CHROMA_SMOOTH:
case HB_FILTER_UNSHARP:
case HB_FILTER_LAPSHARP:
case HB_FILTER_DECOMB:
case HB_FILTER_DETELECINE:
case HB_FILTER_HQDN3D:
case HB_FILTER_COMB_DETECT:
case HB_FILTER_DEBLOCK:
{
hb_dict_t * settings;
settings = hb_generate_filter_settings(filter_id, preset,
NULL, NULL);
char * str = hb_filter_settings_string(filter_id, settings);
hb_value_free(&settings);
char ** split = hb_str_vsplit(str, ':');
char * colon = "", * newline;
int ii, linelen = 0;
for (ii = 0; split[ii] != NULL; ii++)
{
int len = strlen(split[ii]) + 1;
if (linelen + len > 48)
{
newline = "\n ";
linelen = 0;
}
else
{
newline = "";
}
fprintf(out, "%s%s%s", colon, newline, split[ii]);
linelen += len;
colon = ":";
}
hb_str_vfree(split);
free(str);
} break;
case HB_FILTER_ROTATE:
fprintf(out, "%s", ROTATE_DEFAULT);
break;
default:
break;
}
fprintf(out, "\n");
}
static void ShowHelp()
{
int i, clock_min, clock_max, clock;
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,
"Usage: HandBrakeCLI [options] -i