diff --git a/gtk/src/hb-backend.c b/gtk/src/hb-backend.c index 6fd929c3acf8..e86c3a76f7ae 100644 --- a/gtk/src/hb-backend.c +++ b/gtk/src/hb-backend.c @@ -3553,9 +3553,12 @@ void ghb_backend_scan_stop() } void -ghb_backend_scan(const gchar *path, gint titleindex, gint preview_count, uint64_t min_duration) +ghb_backend_scan(const gchar *path, gint titleindex, gint image_sequence, + const gchar *sequence_framerate, gint preview_count, + uint64_t min_duration) { - hb_scan( h_scan, path, titleindex, preview_count, 1, min_duration ); + hb_scan( h_scan, path, titleindex, image_sequence, sequence_framerate, + 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 @@ -3570,7 +3573,7 @@ void ghb_backend_queue_scan(const gchar *path, gint titlenum) { g_debug("ghb_backend_queue_scan()"); - hb_scan( h_queue, path, titlenum, -1, 0, 0 ); + hb_scan( h_queue, path, titlenum, 0, 0.0f, -1, 0, 0 ); hb_status.queue.state |= GHB_STATE_SCANNING; } diff --git a/libhb/common.c b/libhb/common.c index ceff2817970e..49a9874497b5 100644 --- a/libhb/common.c +++ b/libhb/common.c @@ -4248,7 +4248,15 @@ void hb_job_set_file(hb_job_t *job, const char *file) } } -hb_filter_object_t * hb_filter_copy( hb_filter_object_t * filter ) +void hb_job_set_sequence_framerate(hb_job_t *job, const char *sequence_framerate) +{ + if (job != NULL) + { + hb_update_str(&job->sequence_framerate, sequence_framerate); + } +} + +hb_filter_object_t *hb_filter_copy(hb_filter_object_t *filter) { if( filter == NULL ) return NULL; diff --git a/libhb/handbrake/common.h b/libhb/handbrake/common.h index f103af1068cb..cc72bd6acaf1 100644 --- a/libhb/handbrake/common.h +++ b/libhb/handbrake/common.h @@ -149,6 +149,7 @@ 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); +void hb_job_set_sequence_framerate(hb_job_t *job, const char *sequence_framerate); hb_audio_t *hb_audio_copy(const hb_audio_t *src); hb_list_t *hb_audio_list_copy(const hb_list_t *src); @@ -537,6 +538,10 @@ struct hb_job_s /* Include chapter marker track in mp4? */ int chapter_markers; + /* Should be opened as an image sequence, Boolean */ + int image_sequence; + const char * sequence_framerate; + // Video filters int grayscale; // Black and white encoding hb_list_t * list_filter; diff --git a/libhb/handbrake/handbrake.h b/libhb/handbrake/handbrake.h index f65f4de9fd9c..cf7e07c2d0b0 100644 --- a/libhb/handbrake/handbrake.h +++ b/libhb/handbrake/handbrake.h @@ -45,9 +45,12 @@ 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. */ + a VOB file. If title_index is 0, scan all titles. + If image_sequence is non-0, try opening as image sequence with framerate + set in sequence_framerate. */ void hb_scan( hb_handle_t *, const char * path, - int title_index, int preview_count, + int title_index, int image_sequence, + const char *sequence_framerate, int preview_count, int store_previews, uint64_t min_duration ); void hb_scan_stop( hb_handle_t * ); void hb_force_rescan( hb_handle_t * ); diff --git a/libhb/handbrake/internal.h b/libhb/handbrake/internal.h index ce67e4779454..084b8e9f7db9 100644 --- a/libhb/handbrake/internal.h +++ b/libhb/handbrake/internal.h @@ -272,10 +272,11 @@ static inline hb_buffer_t * hb_video_buffer_init( int width, int height ) /*********************************************************************** * Threads: scan.c, work.c, reader.c, muxcommon.c **********************************************************************/ -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_scan_init(hb_handle_t *, volatile int *die, + const char *path, int title_index, + hb_title_set_t *title_set, int image_sequence, + const char *sequence_framerate, 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 ); @@ -360,6 +361,8 @@ hb_stream_t * hb_bd_stream_open( hb_handle_t *h, hb_title_t *title ); void hb_ts_stream_reset(hb_stream_t *stream); hb_stream_t * hb_stream_open(hb_handle_t *h, const char * path, hb_title_t *title, int scan); +hb_stream_t * hb_sequence_open(hb_handle_t *h, const char * path, + const char * framerate); 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 * ); diff --git a/libhb/hb.c b/libhb/hb.c index b9272d6229d5..15e51af50c1f 100644 --- a/libhb/hb.c +++ b/libhb/hb.c @@ -359,11 +359,13 @@ void hb_remove_previews( hb_handle_t * h ) * @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 image_sequence Whether to try reading an image sequence from the starting image. * @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 ) +void hb_scan(hb_handle_t *h, const char *path, int title_index, + int image_sequence, const char *sequence_framerate, + int preview_count, int store_previews, uint64_t min_duration) { hb_title_t * title; @@ -434,7 +436,8 @@ void hb_scan( hb_handle_t * h, const char * path, int title_index, 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, + &h->title_set, image_sequence, + sequence_framerate, preview_count, store_previews, min_duration ); } diff --git a/libhb/hb_json.c b/libhb/hb_json.c index 47c0cbe5f1af..ae6e9d1a8d71 100644 --- a/libhb/hb_json.c +++ b/libhb/hb_json.c @@ -503,6 +503,8 @@ hb_dict_t* hb_job_to_dict( const hb_job_t * job ) "s:{s:o, s:o, s:o, s:o, s:[]}," // Source {Path, Title, Angle} "s:{s:o, s:o, s:o,}," + // ImageSequence, SequenceFramerate + "s:o, s:o," // PAR {Num, Den} "s:{s:o, s:o}," // Video {Encoder, QSV {Decode, AsyncDepth, AdapterIndex}} @@ -527,6 +529,8 @@ hb_dict_t* hb_job_to_dict( const hb_job_t * job ) "Path", hb_value_string(job->title->path), "Title", hb_value_int(job->title->index), "Angle", hb_value_int(job->angle), + "ImageSequence", hb_value_bool(job->image_sequence), + "SequenceFramerate", hb_value_string(job->sequence_framerate), "PAR", "Num", hb_value_int(job->par.num), "Den", hb_value_int(job->par.den), @@ -920,7 +924,7 @@ void hb_json_job_scan( hb_handle_t * h, const char * json_job ) // If the job wants to use Hardware decode, it must also be // enabled during scan. So enable it here. - hb_scan(h, path, title_index, -1, 0, 0); + hb_scan(h, path, title_index, 0, NULL, -1, 0, 0); // Wait for scan to complete hb_state_t state; @@ -993,6 +997,7 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) hb_value_t * acodec_copy_mask = NULL, * acodec_fallback = NULL; const char * destfile = NULL; const char * range_type = NULL; + const char * sequence_framerate = NULL; const char * video_preset = NULL, * video_tune = NULL; const char * video_profile = NULL, * video_level = NULL; const char * video_options = NULL; @@ -1013,6 +1018,8 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) "s:{s?s, s:o, s?b, s?b, s:b, s?o s?{s?b, s?b}}," // Source {Angle, Range {Type, Start, End, SeekPoints}} "s:{s?i, s?{s:s, s?I, s?I, s?I}}," + // ImageSequence, SequenceFramerate + "s:b, s:s," // PAR {Num, Den} "s?{s:i, s:i}," // Video {Codec, Quality, Bitrate, Preset, Tune, Profile, Level, Options @@ -1058,6 +1065,8 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) "Start", unpack_I(&range_start), "End", unpack_I(&range_end), "SeekPoints", unpack_I(&range_seek_points), + "ImageSequence", unpack_b(&job->image_sequence), + "SequenceFramerate", unpack_s(&sequence_framerate), "PAR", "Num", unpack_i(&job->par.num), "Den", unpack_i(&job->par.den), @@ -1175,6 +1184,10 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) { hb_job_set_file(job, destfile); } + if(sequence_framerate != NULL && sequence_framerate[0] != 0) + { + hb_job_set_sequence_framerate(job, sequence_framerate); + } hb_job_set_encoder_preset(job, video_preset); hb_job_set_encoder_tune(job, video_tune); diff --git a/libhb/preset.c b/libhb/preset.c index 25d5fb67ff82..1b6f74260d7f 100644 --- a/libhb/preset.c +++ b/libhb/preset.c @@ -2223,6 +2223,13 @@ int hb_preset_apply_title(hb_handle_t *h, int title_index, if (hb_list_count(title->list_chapter) <= 1) chapters = 0; + // Apply image sequence settings if present + if (hb_dict_get_bool(preset, "ImageSequence")) + { + hb_dict_set_bool(job_dict, "ImageSequence", 1); + hb_dict_set(job_dict, "SequenceFramerate", hb_dict_get(preset, "SequenceFramerate")); + } + // Set "Destination" settings in job hb_dict_t *dest_dict = hb_dict_get(job_dict, "Destination"); hb_dict_set(dest_dict, "ChapterMarkers", hb_value_bool(chapters)); diff --git a/libhb/reader.c b/libhb/reader.c index 90afbe3e2f3d..290015117e0e 100644 --- a/libhb/reader.c +++ b/libhb/reader.c @@ -155,8 +155,16 @@ static int hb_reader_open( hb_work_private_t * r ) else if (r->title->type == HB_STREAM_TYPE || r->title->type == HB_FF_STREAM_TYPE) { - if (!(r->stream = hb_stream_open(r->h, r->title->path, r->title, 0))) - return 1; + if (r->job->image_sequence == 0) + { + if (!(r->stream = hb_stream_open(r->h, r->title->path, r->title, 0))) + return 1; + } + else + { + if (!(r->stream = hb_sequence_open(r->h, r->title->path, r->job->sequence_framerate))) + return 1; + } if (r->job->start_at_preview) { // First try seeking to PTS title duration / (seek_points + 1) diff --git a/libhb/scan.c b/libhb/scan.c index bec7ee0c1550..f28f56ce5932 100644 --- a/libhb/scan.c +++ b/libhb/scan.c @@ -24,6 +24,9 @@ typedef struct hb_stream_t * stream; hb_batch_t * batch; + int image_sequence; + char * sequence_framerate; + int preview_count; int store_previews; @@ -56,10 +59,11 @@ static const char *aspect_to_string(hb_rational_t *dar) 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_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 image_sequence, + const char *sequence_framerate, int preview_count, + int store_previews, uint64_t min_duration) { hb_scan_t * data = calloc( sizeof( hb_scan_t ), 1 ); @@ -69,7 +73,9 @@ hb_thread_t * hb_scan_init( hb_handle_t * handle, volatile int * die, data->title_index = title_index; data->title_set = title_set; - data->preview_count = preview_count; + data->image_sequence = image_sequence; + data->sequence_framerate = strdup(sequence_framerate); + data->preview_count = preview_count; data->store_previews = store_previews; data->min_title_duration = min_duration; @@ -188,13 +194,35 @@ static void ScanFunc( void * _data ) // mode. if (data->title_index == 0) data->title_index = 1; - hb_title_t * title = hb_title_init( data->path, data->title_index ); - data->stream = hb_stream_open(data->h, data->path, title, 1); + + hb_title_t *title = hb_title_init(data->path, data->title_index); + if (data->image_sequence == 1) + { + hb_log( "trying to load image sequence" ); + data->stream = hb_sequence_open(data->h, data->path, data->sequence_framerate); + } + else + { + data->stream = hb_stream_open(data->h, data->path, title, 1); + } if (data->stream != NULL) { title = hb_stream_title_scan( data->stream, title ); if ( title ) - hb_list_add( data->title_set->list_title, title ); + { + if (data->image_sequence == 1) + { + char *name = data->path; + char *sep = hb_strr_dir_sep(data->path); + if (sep) + name = sep + 1; + title->name = strdup(name); + char *dot_term = strrchr(title->name, '.'); + if (dot_term) + *dot_term = '\0'; + } + hb_list_add(data->title_set->list_title, title); + } } else { @@ -322,6 +350,7 @@ static void ScanFunc( void * _data ) hb_batch_close( &data->batch ); } free( data->path ); + free( data->sequence_framerate ); free( data ); _data = NULL; hb_buffer_pool_free(); @@ -586,7 +615,14 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title, int flush ) } else if (data->stream) { - stream = hb_stream_open(data->h, data->path, title, 0); + if(data->image_sequence == 0) + { + stream = hb_stream_open(data->h, data->path, title, 0); + } + else + { + stream = hb_sequence_open(data->h, data->path, data->sequence_framerate); + } } if (title->video_codec == WORK_NONE) diff --git a/libhb/stream.c b/libhb/stream.c index 8b4cf69cb833..047b98e5c5cb 100644 --- a/libhb/stream.c +++ b/libhb/stream.c @@ -166,6 +166,8 @@ struct hb_stream_s hb_handle_t * h; int scan; + int image_sequence; /* 1 if we're reading an image sequence */ + char *sequence_framerate; /* framerate when reading an image sequence */ int frames; /* video frames so far */ int errors; /* total errors so far */ int last_error_frame; /* frame # at last error message */ @@ -273,7 +275,8 @@ static int update_ts_streams( hb_stream_t * stream, int pid, int stream_id_ext, 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 int ffmpeg_open_sequence(hb_stream_t *stream ); +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 ); @@ -833,17 +836,17 @@ hb_stream_open(hb_handle_t *h, const char *path, hb_title_t *title, int scan) } FILE *f = hb_fopen(path, "rb"); - if ( f == NULL ) + if (f == NULL) { - hb_log( "hb_stream_open: open %s failed", path ); + hb_log("hb_stream_open: open %s failed", path); return NULL; } - hb_stream_t *d = calloc( sizeof( hb_stream_t ), 1 ); - if ( d == 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 ); + fclose(f); + hb_log("hb_stream_open: can't allocate space for %s stream state", path); return NULL; } @@ -860,41 +863,109 @@ hb_stream_open(hb_handle_t *h, const char *path, hb_title_t *title, int scan) d->file_handle = f; d->title = title; d->scan = scan; - d->path = strdup( path ); - if (d->path != NULL ) + d->path = strdup(path); + if (d->path != NULL) { - if (hb_stream_get_type( d ) != 0) + if (hb_stream_get_type(d) != 0) { - if( !scan ) + if (!scan) { - prune_streams( d ); + prune_streams(d); } // reset to beginning of file and reset some stream // state information - hb_stream_seek( d, 0. ); + hb_stream_seek(d, 0.); return d; } - fclose( d->file_handle ); + fclose(d->file_handle); d->file_handle = NULL; - if ( ffmpeg_open( d, title, scan ) ) + if (ffmpeg_open(d, title, scan)) { return d; } } - if ( d->file_handle ) + if (d->file_handle) { - fclose( d->file_handle ); + fclose(d->file_handle); } if (d->path) { - free( d->path ); + free(d->path); } - hb_log( "hb_stream_open: open %s failed", path ); - free( d ); + hb_log("hb_stream_open: open %s failed", path); + free(d); return NULL; } -static int new_pid( hb_stream_t * stream ) +hb_stream_t * +hb_sequence_open(hb_handle_t *h, const char *firstPath, const char *framerate) +{ + char *dot = strrchr(firstPath, '.'); + size_t extLength = firstPath + strlen(firstPath) - dot; + const char *extension = firstPath + strlen(firstPath) - extLength; + if (dot == NULL) + { + hb_log("hb_sequence_open: no file extension found in %s", firstPath); + return NULL; + } + dot--; + if(*dot > '9' || *dot < '0') + { + hb_log("hb_sequence_open: no number before file extension in %s", firstPath); + return NULL; + } + + char *end = dot; + while (*dot >= '0' && *dot <= '9') + dot--; + unsigned int numbers = end - dot; + + if (numbers > 99) + { + hb_log("hb_sequence_open: more than 99 index numbers found " + "in sequence file name %s", firstPath); + return NULL; + } + + unsigned int baseLength = dot - firstPath + 1; + char *path = calloc(baseLength + extLength + 5, 1); + strncpy(path, firstPath, baseLength); + sprintf(path + baseLength, "%%0%ud%s", numbers, extension); + + hb_log("hb_sequence_open: trying %s...", path); + hb_stream_t *d = calloc(sizeof(hb_stream_t), 1); + if (d == NULL) + { + hb_log("hb_sequence_open: can't allocate space for %s stream state", path); + return NULL; + } + + /* + * If it's something we can deal with (MPEG2 PS or TS) return a stream + * reference structure & null otherwise. + */ + d->h = h; + d->image_sequence = 1; + d->sequence_framerate = strdup(framerate); + d->path = strdup(path); + if (d->path != NULL && ffmpeg_open_sequence(d)) + { + return d; + } + if (d->path) + { + free(d->path); + } + if (d->sequence_framerate) + { + free(d->sequence_framerate); + } + hb_log("hb_sequence_open: open %s failed", path); + free(d); + return NULL; +} + +static int new_pid(hb_stream_t *stream) { int num = stream->ts.alloc; @@ -5282,6 +5353,77 @@ static int ffmpeg_open( hb_stream_t *stream, hb_title_t *title, int scan ) return 0; } +static int ffmpeg_open_sequence(hb_stream_t *stream) +{ + hb_log("ffmpeg_open_sequence: trying %s...", stream->path); + + 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 ); + + av_dict_set( &av_opts, "framerate", strdup(stream->sequence_framerate), 0 ); + av_dict_set( &av_opts, "pattern_type", "sequence", 0 ); + + hb_log("trying to open input with pattern_type sequence"); + // See FFMpeg has issues with seeking comment in ffmpeg_open(). + if ( avformat_open_input( &info_ic, stream->path, NULL, &av_opts ) < 0 ) + { + av_dict_free( &av_opts ); + 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; + + stream->ffmpeg_ic = info_ic; + stream->hb_stream_type = ffmpeg; + stream->chapter_end = INT64_MAX; + stream->ffmpeg_pkt = av_packet_alloc(); + + if (stream->ffmpeg_pkt == NULL) + { + hb_error("stream: av_packet_alloc failed"); + goto fail; + } + + // we're opening as sequence. let ffmpeg put some info into the + // log about what we've got. + //TODO 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]->codecpar->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 ); diff --git a/libhb/work.c b/libhb/work.c index c94253960ab4..133ae85b09d6 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -401,6 +401,11 @@ void hb_display_job_info(hb_job_t *job) if( title->container_name != NULL ) hb_log( " + container: %s", title->container_name); + + if( job->image_sequence ) + { + hb_log(" + image sequence, framerate %s", job->sequence_framerate); + } if( title->data_rate ) { diff --git a/macosx/Base.lproj/MainMenu.xib b/macosx/Base.lproj/MainMenu.xib index b21f52f9cb48..3e1ceb98189b 100644 --- a/macosx/Base.lproj/MainMenu.xib +++ b/macosx/Base.lproj/MainMenu.xib @@ -1,8 +1,8 @@ - + - + diff --git a/macosx/Base.lproj/MainWindow.xib b/macosx/Base.lproj/MainWindow.xib index 54b07b64520b..017798567c73 100644 --- a/macosx/Base.lproj/MainWindow.xib +++ b/macosx/Base.lproj/MainWindow.xib @@ -41,7 +41,7 @@ - + @@ -860,11 +860,11 @@ Blu-ray and DVD sources often have multiple titles, the longest of which is typi - + - + + + + + + + + + + + + + + + NSAllRomanInputSourcesLocaleIdentifier + + + + + + + + + + + + + - + - + @@ -900,26 +927,42 @@ Blu-ray and DVD sources often have multiple titles, the longest of which is typi + + + + + + + + - + - - + diff --git a/macosx/HBController.m b/macosx/HBController.m index d4bd79ee1d8e..a1cd9b57c3a9 100644 --- a/macosx/HBController.m +++ b/macosx/HBController.m @@ -102,6 +102,8 @@ @interface HBController () *titles))completionHandler +- (void)scanURL:(NSURL *)fileURL titleIndex:(NSUInteger)index imageSequence:(BOOL)imageSequence sequenceFramerate:(NSString *)sequenceFramerate completionHandler:(void(^)(NSArray *titles))completionHandler { // Save the current settings [self updateCurrentPreset]; @@ -716,8 +720,8 @@ - (void)scanURL:(NSURL *)fileURL titleIndex:(NSUInteger)index completionHandler: NSUInteger hb_num_previews = [NSUserDefaults.standardUserDefaults integerForKey:HBPreviewsNumber]; NSUInteger min_title_duration_seconds = [NSUserDefaults.standardUserDefaults integerForKey:HBMinTitleScanSeconds]; - [self.core scanURL:mediaURL - titleIndex:index + [self.core scanURL:mediaURL titleIndex:index + imageSequence:imageSequence sequenceFramerate:sequenceFramerate previews:hb_num_previews minDuration:min_title_duration_seconds keepPreviews:YES progressHandler:^(HBState state, HBProgress progress, NSString *info) { @@ -774,11 +778,11 @@ - (void)scanURL:(NSURL *)fileURL titleIndex:(NSUInteger)index completionHandler: } } -- (void)openURL:(NSURL *)fileURL titleIndex:(NSUInteger)index +- (void)openURL:(NSURL *)fileURL titleIndex:(NSUInteger)index imageSequence:(BOOL)imageSequence sequenceFramerate:(NSString *)sequenceFramerate { [self showWindow:self]; - [self scanURL:fileURL titleIndex:index completionHandler:^(NSArray *titles) + [self scanURL:fileURL titleIndex:index imageSequence:imageSequence sequenceFramerate:sequenceFramerate completionHandler:^(NSArray *titles) { if (titles.count) { @@ -812,7 +816,7 @@ - (void)openURL:(NSURL *)fileURL { if (self.core.state != HBStateScanning) { - [self openURL:fileURL titleIndex:0]; + [self openURL:fileURL titleIndex:0 imageSequence:self.scanImageSequence sequenceFramerate:self.sequenceFramerate]; } } @@ -824,7 +828,7 @@ - (void)openJob:(HBJob *)job completionHandler:(void (^)(BOOL result))handler if (self.core.state != HBStateScanning) { [job refreshSecurityScopedResources]; - [self scanURL:job.fileURL titleIndex:job.titleIdx completionHandler:^(NSArray *titles) + [self scanURL:job.fileURL titleIndex:job.titleIdx imageSequence:job.imageSequence sequenceFramerate:job.sequenceFramerate completionHandler:^(NSArray *titles) { if (titles.count) { @@ -872,8 +876,8 @@ - (HBJob *)jobFromTitle:(HBTitle *)title // If there is already a title load, save the current settings to a preset // Save the current settings [self updateCurrentPreset]; - - HBJob *job = [[HBJob alloc] initWithTitle:title preset:self.currentPreset]; + + HBJob *job = [[HBJob alloc] initWithTitle:title imageSequence:self.scanImageSequence sequenceFramerate:self.sequenceFramerate preset:self.currentPreset]; if (job) { job.destinationFolderURL = self.destinationFolderURL; @@ -1016,7 +1020,7 @@ - (IBAction)browseSources:(id)sender if (result == NSModalResponseOK) { NSInteger titleIdx = self.scanSpecificTitle ? self.scanSpecificTitleIdx : 0; - [self openURL:panel.URL titleIndex:titleIdx]; + [self openURL:panel.URL titleIndex:titleIdx imageSequence:self.scanImageSequence sequenceFramerate:self.sequenceFramerate]; } }]; } @@ -1325,7 +1329,7 @@ - (void)doAddTitlesToQueue:(NSArray *)titles for (HBTitle *title in titles) { - HBJob *job = [[HBJob alloc] initWithTitle:title preset:preset]; + HBJob *job = [[HBJob alloc] initWithTitle:title imageSequence:self.scanImageSequence sequenceFramerate:self.sequenceFramerate preset:preset]; job.destinationFolderURL = self.destinationFolderURL; job.destinationFileName = job.defaultName; job.title = nil; diff --git a/macosx/HBCore.h b/macosx/HBCore.h index c96f8cd553e0..9b4ae262c20b 100644 --- a/macosx/HBCore.h +++ b/macosx/HBCore.h @@ -162,14 +162,16 @@ typedef void (^HBCoreCompletionHandler)(HBCoreResult result); * Initiates an asynchronous scan operation and returns immediately. * * @param url the URL of the input file. - * @param index the index of the desired title. Use 0 to scan every title. + * @param index the index of the desired title. Use 0 to scan every title. + * @param imageSequence whether to try opening an image sequence. + * @param sequenceFramerate the framerate when openning an image sequence. * @param previewsNum the number of previews image to generate. * @param seconds the minimum duration of the wanted titles in seconds. * @param keepPreviews whether the previews images are kept on disk or discarded. * @param progressHandler a block called periodically with the progress information. * @param completionHandler a block called with the scan result. */ -- (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds keepPreviews:(BOOL)keepPreviews progressHandler:(HBCoreProgressHandler)progressHandler completionHandler:(HBCoreCompletionHandler)completionHandler; +- (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index imageSequence:(BOOL)imageSequence sequenceFramerate:(NSString *)sequenceFramerate previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds keepPreviews:(BOOL)keepPreviews progressHandler:(HBCoreProgressHandler)progressHandler completionHandler:(HBCoreCompletionHandler)completionHandler; /** * Cancels the scan execution. diff --git a/macosx/HBCore.m b/macosx/HBCore.m index 8ebfd36f2363..ea55deeaff93 100644 --- a/macosx/HBCore.m +++ b/macosx/HBCore.m @@ -256,7 +256,7 @@ - (BOOL)canScan:(NSURL *)url error:(NSError * __autoreleasing *)error return YES; } -- (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds keepPreviews:(BOOL)keepPreviews progressHandler:(HBCoreProgressHandler)progressHandler completionHandler:(HBCoreCompletionHandler)completionHandler +- (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index imageSequence:(BOOL)imageSequence sequenceFramerate:(NSString *)sequenceFramerate previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds keepPreviews:(BOOL)keepPreviews progressHandler:(HBCoreProgressHandler)progressHandler completionHandler:(HBCoreCompletionHandler)completionHandler { NSAssert(self.state == HBStateIdle, @"[HBCore scanURL:] called while another scan or encode already in progress"); NSAssert(url, @"[HBCore scanURL:] called with nil url."); @@ -292,8 +292,8 @@ - (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index previews:(NSUInteger)p [self preventAutoSleep]; hb_scan(_hb_handle, url.fileSystemRepresentation, - (int)index, (int)previewsNum, - keepPreviews, min_title_duration_ticks); + (int)index, (int)imageSequence, sequenceFramerate.UTF8String, + (int)previewsNum, keepPreviews, min_title_duration_ticks); // Start the timer to handle libhb state changes [self startUpdateTimerWithInterval:0.2]; diff --git a/macosx/HBJob+HBJobConversion.m b/macosx/HBJob+HBJobConversion.m index f7abc17e2eca..41016cc85883 100644 --- a/macosx/HBJob+HBJobConversion.m +++ b/macosx/HBJob+HBJobConversion.m @@ -87,6 +87,9 @@ - (hb_job_t *)hb_job job->seek_points = self.range.previewsCount; job->pts_to_stop = self.range.ptsToStop; } + + job->image_sequence = self.imageSequence; + hb_job_set_sequence_framerate(job, self.sequenceFramerate.UTF8String); // Format (Muxer) and Video Encoder job->mux = self.container; diff --git a/macosx/HBJob.h b/macosx/HBJob.h index a49f3d3524c8..b6993ab3ab12 100644 --- a/macosx/HBJob.h +++ b/macosx/HBJob.h @@ -29,7 +29,7 @@ extern NSString *HBChaptersChangedNotification; */ @interface HBJob : NSObject -- (nullable instancetype)initWithTitle:(HBTitle *)title preset:(HBPreset *)preset; +- (nullable instancetype)initWithTitle:(HBTitle *)title imageSequence:(BOOL)imageSequence sequenceFramerate:(NSString *)sequenceFramerate preset:(HBPreset *)preset; @property (nonatomic, readwrite, weak, nullable) HBTitle *title; @property (nonatomic, readonly) int titleIdx; @@ -37,6 +37,10 @@ extern NSString *HBChaptersChangedNotification; // Whether the source is a single file or a DVD-Video/Blu-ray @property (nonatomic, readonly, getter=isStream) BOOL stream; +// Whether the input is an image sequence +@property (nonatomic, readonly) BOOL imageSequence; +@property (nonatomic, readonly) NSString *sequenceFramerate; + @property (nonatomic, readwrite, copy) NSString *presetName; /// The file URL of the source. diff --git a/macosx/HBJob.m b/macosx/HBJob.m index 803a9d51e34c..b36d32a33e76 100644 --- a/macosx/HBJob.m +++ b/macosx/HBJob.m @@ -45,7 +45,7 @@ @interface HBJob () @implementation HBJob -- (nullable instancetype)initWithTitle:(HBTitle *)title preset:(HBPreset *)preset +- (nullable instancetype)initWithTitle:(HBTitle *)title imageSequence:(BOOL)imageSequence sequenceFramerate:(NSString *)sequenceFramerate preset:(HBPreset *)preset { self = [super init]; if (self) { @@ -55,6 +55,9 @@ - (nullable instancetype)initWithTitle:(HBTitle *)title preset:(HBPreset *)prese _title = title; _titleIdx = title.index; _stream = title.isStream; + + _imageSequence = imageSequence; + _sequenceFramerate = sequenceFramerate; _name = [title.name copy]; _fileURL = title.url; @@ -383,6 +386,9 @@ - (instancetype)copyWithZone:(NSZone *)zone copy->_presetName = [_presetName copy]; copy->_titleIdx = _titleIdx; copy->_stream = _stream; + + copy->_imageSequence = _imageSequence; + copy->_sequenceFramerate = [_sequenceFramerate copy]; copy->_fileURLBookmark = [_fileURLBookmark copy]; copy->_destinationFolderURLBookmark = [_destinationFolderURLBookmark copy]; @@ -433,6 +439,10 @@ - (void)encodeWithCoder:(NSCoder *)coder encodeObject(_presetName); encodeInt(_titleIdx); encodeBool(_stream); + + encodeBool(_imageSequence); + encodeObject(_sequenceFramerate); + #ifdef __SANDBOX_ENABLED__ if (!_fileURLBookmark) @@ -489,6 +499,9 @@ - (instancetype)initWithCoder:(NSCoder *)decoder decodeObjectOrFail(_presetName, NSString); decodeInt(_titleIdx); if (_titleIdx < 0) { goto fail; } decodeBool(_stream); + + decodeBool(_imageSequence); + decodeObjectOrFail(_sequenceFramerate, NSString); #ifdef __SANDBOX_ENABLED__ decodeObject(_fileURLBookmark, NSData) diff --git a/macosx/HBQueueWorker.m b/macosx/HBQueueWorker.m index 32ee4c4f1606..52eff191059d 100644 --- a/macosx/HBQueueWorker.m +++ b/macosx/HBQueueWorker.m @@ -195,6 +195,8 @@ - (void)encodeItem:(HBQueueJobItem *)item // only useful for autocrop and static previews, which are already taken care of at this point [self.core scanURL:item.fileURL titleIndex:item.job.titleIdx + imageSequence:item.job.imageSequence + sequenceFramerate:item.job.sequenceFramerate previews:10 minDuration:0 keepPreviews:NO diff --git a/macosx/HBRemoteCore.h b/macosx/HBRemoteCore.h index 723f4c211b32..26f3cebdc00d 100644 --- a/macosx/HBRemoteCore.h +++ b/macosx/HBRemoteCore.h @@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)preventSleep; - (void)allowSleep; -- (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds keepPreviews:(BOOL)keepPreviews progressHandler:(nonnull HBCoreProgressHandler)progressHandler completionHandler:(nonnull HBCoreCompletionHandler)completionHandler; +- (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index imageSequence:(BOOL)imageSequence sequenceFramerate:(NSString *)sequenceFramerate previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds keepPreviews:(BOOL)keepPreviews progressHandler:(HBCoreProgressHandler)progressHandler completionHandler:(HBCoreCompletionHandler)completionHandler; - (void)cancelScan; diff --git a/macosx/HBRemoteCore.m b/macosx/HBRemoteCore.m index 95e205c73dcc..2f1b0a06ce95 100644 --- a/macosx/HBRemoteCore.m +++ b/macosx/HBRemoteCore.m @@ -149,7 +149,7 @@ - (void)preventSleep [_proxy preventSleep]; } -- (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds keepPreviews:(BOOL)keepPreviews progressHandler:(nonnull HBCoreProgressHandler)progressHandler completionHandler:(nonnull HBCoreCompletionHandler)completionHandler +- (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index imageSequence:(BOOL)imageSequence sequenceFramerate:(NSString *)sequenceFramerate previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds keepPreviews:(BOOL)keepPreviews progressHandler:(HBCoreProgressHandler)progressHandler completionHandler:(HBCoreCompletionHandler)completionHandler { if (!_connection) { @@ -173,7 +173,7 @@ - (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index previews:(NSUInteger)p __weak HBRemoteCore *weakSelf = self; - [_proxy scanURL:url titleIndex:index previews:previewsNum minDuration:seconds keepPreviews:keepPreviews withReply:^(HBCoreResult result) { + [_proxy scanURL:url titleIndex:index imageSequence:imageSequence sequenceFramerate:sequenceFramerate previews:previewsNum minDuration:seconds keepPreviews:keepPreviews withReply:^(HBCoreResult result) { dispatch_sync(dispatch_get_main_queue(), ^{ HBCoreCompletionHandler handler = weakSelf.completionHandler; weakSelf.completionHandler = nil; diff --git a/macosx/HandBrakeKitTests/HBJobTests.m b/macosx/HandBrakeKitTests/HBJobTests.m index 4db5d4897961..8581be71beb1 100644 --- a/macosx/HandBrakeKitTests/HBJobTests.m +++ b/macosx/HandBrakeKitTests/HBJobTests.m @@ -39,7 +39,7 @@ - (void)setUp dispatch_semaphore_t sem = dispatch_semaphore_create(0); self.core = [[HBCore alloc] initWithLogLevel:1 queue:self.queue]; - [self.core scanURL:sampleURL titleIndex:0 previews:1 minDuration:0 keepPreviews:NO progressHandler:^(HBState state, HBProgress progress, NSString * _Nonnull info) { + [self.core scanURL:sampleURL titleIndex:0 imageSequence:false sequenceFramerate:@"0" previews:1 minDuration:0 keepPreviews:NO progressHandler:^(HBState state, HBProgress progress, NSString * _Nonnull info) { } completionHandler:^(HBCoreResult result) { dispatch_semaphore_signal(sem); diff --git a/macosx/HandBrakeKitTests/HBJobUndoTests.m b/macosx/HandBrakeKitTests/HBJobUndoTests.m index ba2ea97f725f..545faf2c5652 100644 --- a/macosx/HandBrakeKitTests/HBJobUndoTests.m +++ b/macosx/HandBrakeKitTests/HBJobUndoTests.m @@ -39,7 +39,7 @@ - (void)setUp dispatch_semaphore_t sem = dispatch_semaphore_create(0); self.core = [[HBCore alloc] initWithLogLevel:1 queue:self.queue]; - [self.core scanURL:sampleURL titleIndex:0 previews:1 minDuration:0 keepPreviews:NO progressHandler:^(HBState state, HBProgress progress, NSString * _Nonnull info) { + [self.core scanURL:sampleURL titleIndex:0 imageSequence:false sequenceFramerate:@"0" previews:1 minDuration:0 keepPreviews:NO progressHandler:^(HBState state, HBProgress progress, NSString * _Nonnull info) { } completionHandler:^(HBCoreResult result) { dispatch_semaphore_signal(sem); diff --git a/macosx/HandBrakeXPCService/HBRemoteCoreProtocol.h b/macosx/HandBrakeXPCService/HBRemoteCoreProtocol.h index 97c8870ef888..e859b55944d9 100644 --- a/macosx/HandBrakeXPCService/HBRemoteCoreProtocol.h +++ b/macosx/HandBrakeXPCService/HBRemoteCoreProtocol.h @@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)preventSleep; - (void)allowSleep; -- (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds keepPreviews:(BOOL)keepPreviews withReply:(void (^)(HBCoreResult))reply; +- (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index imageSequence:(BOOL)imageSequence sequenceFramerate:(NSString *)sequenceFramerate previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds keepPreviews:(BOOL)keepPreviews withReply:(void (^)(HBCoreResult))reply; - (void)cancelScan; - (void)encodeJob:(HBJob *)job withReply:(void (^)(HBCoreResult))reply; diff --git a/macosx/HandBrakeXPCService/HandBrakeXPCService.m b/macosx/HandBrakeXPCService/HandBrakeXPCService.m index 552d4f92c7d0..0f86f8d3cace 100644 --- a/macosx/HandBrakeXPCService/HandBrakeXPCService.m +++ b/macosx/HandBrakeXPCService/HandBrakeXPCService.m @@ -135,7 +135,7 @@ - (void)allowSleep }); } -- (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds keepPreviews:(BOOL)keepPreviews withReply:(void (^)(HBCoreResult))reply +- (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index imageSequence:(BOOL)imageSequence sequenceFramerate:(NSString *)sequenceFramerate previews:(NSUInteger)previewsNum minDuration:(NSUInteger)seconds keepPreviews:(BOOL)keepPreviews withReply:(void (^)(HBCoreResult))reply { dispatch_sync(_queue, ^{ void (^progressHandler)(HBState state, HBProgress progress, NSString *info) = ^(HBState state, HBProgress progress, NSString *info) @@ -145,7 +145,7 @@ - (void)scanURL:(NSURL *)url titleIndex:(NSUInteger)index previews:(NSUInteger)p self->_progressHandler = progressHandler; self.reply = reply; - [self.core scanURL:url titleIndex:index previews:previewsNum minDuration:seconds keepPreviews:keepPreviews + [self.core scanURL:url titleIndex:index imageSequence:imageSequence sequenceFramerate:sequenceFramerate previews:previewsNum minDuration:seconds keepPreviews:keepPreviews progressHandler:self.progressHandler completionHandler:self.completionHandler]; }); diff --git a/preset/preset_builtin.json b/preset/preset_builtin.json index fd02fbe9ef24..cf4394d49a39 100644 --- a/preset/preset_builtin.json +++ b/preset/preset_builtin.json @@ -27,6 +27,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -137,6 +139,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -247,6 +251,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -357,6 +363,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -467,6 +475,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": true, @@ -577,6 +587,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -687,6 +699,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -797,6 +811,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -921,6 +937,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -1045,6 +1063,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -1169,6 +1189,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -1293,6 +1315,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -1417,6 +1441,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -1541,6 +1567,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -1665,6 +1693,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -1789,6 +1819,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -1906,6 +1938,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -2016,6 +2050,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -2126,6 +2162,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -2236,6 +2274,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -2346,6 +2386,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -2456,6 +2498,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -2566,6 +2610,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -2676,6 +2722,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -2786,6 +2834,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -2896,6 +2946,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -3006,6 +3058,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -3116,6 +3170,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -3226,6 +3282,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -3357,6 +3415,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -3481,6 +3541,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -3591,6 +3653,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -3701,6 +3765,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -3811,6 +3877,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -3921,6 +3989,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -4031,6 +4101,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -4155,6 +4227,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -4279,6 +4353,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -4403,6 +4479,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -4527,6 +4605,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -4651,6 +4731,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -4775,6 +4857,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -4899,6 +4983,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -5023,6 +5109,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -5146,6 +5234,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [], "Default": false, "FileFormat": "mp4", @@ -5269,6 +5359,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -5379,6 +5471,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -5489,6 +5583,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -5616,6 +5712,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -5740,6 +5838,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -5864,6 +5964,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -5974,6 +6076,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -6084,6 +6188,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -6208,6 +6314,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -6325,6 +6433,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -6435,6 +6545,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -6545,6 +6657,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -6655,6 +6769,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -6765,6 +6881,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -6875,6 +6993,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -6985,6 +7105,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -7095,6 +7217,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -7205,6 +7329,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -7315,6 +7441,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -7424,6 +7552,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -7533,6 +7663,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -7642,6 +7774,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -7751,6 +7885,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -7860,6 +7996,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -7969,6 +8107,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -8078,6 +8218,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -8187,6 +8329,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -8296,6 +8440,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -8413,6 +8559,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -8522,6 +8670,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -8631,6 +8781,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -8740,6 +8892,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -8849,6 +9003,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -8958,6 +9114,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -9067,6 +9225,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -9176,6 +9336,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -9292,6 +9454,8 @@ "AudioSecondaryEncoderMode": false, "AudioTrackSelectionBehavior": "all", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -9402,6 +9566,8 @@ "AudioSecondaryEncoderMode": false, "AudioTrackSelectionBehavior": "all", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -9512,6 +9678,8 @@ "AudioSecondaryEncoderMode": false, "AudioTrackSelectionBehavior": "all", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, @@ -9622,6 +9790,8 @@ "AudioSecondaryEncoderMode": false, "AudioTrackSelectionBehavior": "all", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, diff --git a/preset/preset_cli_default.json b/preset/preset_cli_default.json index f015d6062e45..9a952cb382e0 100644 --- a/preset/preset_cli_default.json +++ b/preset/preset_cli_default.json @@ -33,6 +33,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": true, diff --git a/preset/preset_template.json b/preset/preset_template.json index 4db4d8e59a81..b79961046551 100644 --- a/preset/preset_template.json +++ b/preset/preset_template.json @@ -32,6 +32,8 @@ "AudioSecondaryEncoderMode": true, "AudioTrackSelectionBehavior": "first", "ChapterMarkers": true, + "ImageSequence": false, + "SequenceFramerate": "0", "ChildrenArray": [ ], "Default": false, diff --git a/scripts/manicure.rb b/scripts/manicure.rb index fb4afa9ecb34..ef0f2e671db2 100755 --- a/scripts/manicure.rb +++ b/scripts/manicure.rb @@ -612,6 +612,7 @@ def generateCLIString(hash, depth) # Makes a full CLI equivalent of a preset #Booleans if hash["ChapterMarkers"] == 1 then commandString << " -m" end + if hash["ImageSequence"] == 1 then commandString << " --image-sequence" 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 @@ -1011,6 +1012,7 @@ def generateCLIParse(hash, depth) # Makes a CLI equivalent of all user presets, #Booleans if hash["ChapterMarkers"] == 1 then commandString << " -m" end + if hash["ImageSequence"] == 1 then commandString << " --image-sequence" 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 @@ -1472,6 +1474,10 @@ def generateAPIcalls(hash) # Makes a C version of the preset ready for coding in commandString << "job->chapter_markers = 1;\n " end + if hash["ImageSequence"] == 1 + commandString << "job->image_sequence = 1;\n " + end + if hash["VideoGrayScale"] == 1 commandString << "job->grayscale = 1;\n " end @@ -1863,6 +1869,7 @@ def generateAPIList(hash, depth) # Makes a list of the CLI options a built-in CL #Booleans if hash["ChapterMarkers"] == 1 then commandString << " -m" end + if hash["ImageSequence"] == 1 then commandString << " --image-sequence" 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 diff --git a/test/test.c b/test/test.c index 943f289b4f6c..9762cf39676b 100644 --- a/test/test.c +++ b/test/test.c @@ -70,6 +70,8 @@ static char * output = NULL; static char * format = NULL; static int titleindex = 1; static int titlescan = 0; +static int image_sequence = 0; +static char * sequence_framerate = NULL; static int main_feature = 0; static char * native_language = NULL; static int native_dub = 0; @@ -587,7 +589,7 @@ int main( int argc, char ** argv ) hb_system_sleep_prevent(h); - hb_scan(h, input, titleindex, preview_count, store_previews, + hb_scan(h, input, titleindex, image_sequence, sequence_framerate, preview_count, store_previews, min_title_duration * 90000LL); EventLoop(h, preset_dict); @@ -968,7 +970,9 @@ static int HandleEvents(hb_handle_t * h, hb_dict_t *preset_dict) return -1; } - char * json_job; +fprintf(stderr, "+++++ Framerate Test: %s\n", hb_value_get_string(hb_dict_get(job_dict, "SequenceFramerate"))); + + char *json_job; json_job = hb_value_get_json(job_dict); hb_value_free(&job_dict); if (json_job == NULL) @@ -1292,78 +1296,81 @@ static void ShowHelp() const hb_container_t *container; FILE* const out = stdout; - fprintf( out, -"Usage: HandBrakeCLI [options] -i -o \n" -"\n" -"General Options --------------------------------------------------------------\n" -"\n" -" -h, --help Print help\n" -" --version Print version\n" -" --json Log title, progress, and version info in\n" -" JSON format\n" -" -v, --verbose[=number] Be verbose (optional argument: logging level)\n" -" -Z. --preset Select preset by name (case-sensitive)\n" -" Enclose names containing spaces in double quotation\n" -" marks (e.g. \"Preset Name\")\n" -" -z, --preset-list List available presets\n" -" --preset-import-file \n" -" Import presets from a json preset file.\n" -" 'filespec' may be a list of files separated\n" -" by spaces, or it may use shell wildcards.\n" -" --preset-import-gui Import presets from GUI config preset file.\n" -" --preset-export \n" -" Create a new preset from command line options and\n" -" write a json representation of the preset to the\n" -" console or a file if '--preset-export-file' is\n" -" specified. The required argument will be the name\n" -" of the new preset.\n" -" --preset-export-file \n" -" Write new preset generated by '--preset-export'\n" -" to file 'filename'.\n" -" --preset-export-description \n" -" Add a description to the new preset created with\n" -" '--preset-export'\n" -" --queue-import-file \n" -" Import an encode queue file created by the GUI\n" -" --no-dvdnav Do not use dvdnav for reading DVDs\n" -"\n" -"\n" -"Source Options ---------------------------------------------------------------\n" -"\n" -" -i, --input Set input file or device (\"source\")\n" -" -t, --title Select a title to encode (0 to scan all titles\n" -" only, default: 1)\n" -" --min-duration Set the minimum title duration (in seconds).\n" -" Shorter titles will be ignored (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 \n" -" Select how many preview images are generated,\n" -" and whether to store to disk (0 or 1).\n" -" (default: 10:0)\n" -" --start-at-preview \n" -" Start encoding at a given preview.\n" -" --start-at \n" -" Start encoding at a given offset in seconds,\n" -" frames, or pts (on a 90kHz clock)\n" -" (e.g. seconds:10, frames:300, pts:900000).\n" -" Units must match --stop-at units, if specified.\n" -" --stop-at \n" -" Stop encoding after a given duration in seconds,\n" -" frames, or pts (on a 90kHz clock) has passed\n" -" (e.g. seconds:10, frames:300, pts:900000).\n" -" Duration is relative to --start-at, if specified.\n" -" Units must match --start-at units, if specified.\n" -"\n" -"\n" -"Destination Options ----------------------------------------------------------\n" -"\n" -" -o, --output Set destination file name\n" -" -f, --format Select container format:\n"); + fprintf(out, + "Usage: HandBrakeCLI [options] -i -o \n" + "\n" + "General Options --------------------------------------------------------------\n" + "\n" + " -h, --help Print help\n" + " --version Print version\n" + " --json Log title, progress, and version info in\n" + " JSON format\n" + " -v, --verbose[=number] Be verbose (optional argument: logging level)\n" + " -Z. --preset Select preset by name (case-sensitive)\n" + " Enclose names containing spaces in double quotation\n" + " marks (e.g. \"Preset Name\")\n" + " -z, --preset-list List available presets\n" + " --preset-import-file \n" + " Import presets from a json preset file.\n" + " 'filespec' may be a list of files separated\n" + " by spaces, or it may use shell wildcards.\n" + " --preset-import-gui Import presets from GUI config preset file.\n" + " --preset-export \n" + " Create a new preset from command line options and\n" + " write a json representation of the preset to the\n" + " console or a file if '--preset-export-file' is\n" + " specified. The required argument will be the name\n" + " of the new preset.\n" + " --preset-export-file \n" + " Write new preset generated by '--preset-export'\n" + " to file 'filename'.\n" + " --preset-export-description \n" + " Add a description to the new preset created with\n" + " '--preset-export'\n" + " --queue-import-file \n" + " Import an encode queue file created by the GUI\n" + " --no-dvdnav Do not use dvdnav for reading DVDs\n" + "\n" + "\n" + "Source Options ---------------------------------------------------------------\n" + "\n" + " -i, --input Set input file or device (\"source\")\n" + " -t, --title Select a title to encode (0 to scan all titles\n" + " only, default: 1)\n" + " --min-duration Set the minimum title duration (in seconds).\n" + " Shorter titles will be ignored (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 \n" + " Select how many preview images are generated,\n" + " and whether to store to disk (0 or 1).\n" + " (default: 10:0)\n" + " --image-sequence \n" + " Input file is the first of a numbered image\n" + " sequence, with supplied framerate (default:29.97).\n" + " --start-at-preview \n" + " Start encoding at a given preview.\n" + " --start-at \n" + " Start encoding at a given offset in seconds,\n" + " frames, or pts (on a 90kHz clock)\n" + " (e.g. seconds:10, frames:300, pts:900000).\n" + " Units must match --stop-at units, if specified.\n" + " --stop-at \n" + " Stop encoding after a given duration in seconds,\n" + " frames, or pts (on a 90kHz clock) has passed\n" + " (e.g. seconds:10, frames:300, pts:900000).\n" + " Duration is relative to --start-at, if specified.\n" + " Units must match --start-at units, if specified.\n" + "\n" + "\n" + "Destination Options ----------------------------------------------------------\n" + "\n" + " -o, --output Set destination file name\n" + " -f, --format Select container format:\n"); container = NULL; while ((container = hb_container_get_next(container)) != NULL) { @@ -2184,6 +2191,7 @@ static int ParseOptions( int argc, char ** argv ) #define FILTER_CHROMA_SMOOTH_TUNE 324 #define FILTER_DEBLOCK_TUNE 325 #define FILTER_COLORSPACE 326 + #define IMAGE_SEQUENCE 327 for( ;; ) { static struct option long_options[] = @@ -2210,6 +2218,7 @@ static int ParseOptions( int argc, char ** argv ) { "no-optimize", no_argument, &mp4_optimize, 0 }, { "ipod-atom", no_argument, NULL, 'I' }, { "no-ipod-atom",no_argument, &ipod_atom, 0 }, + { "image-sequence", required_argument, NULL, IMAGE_SEQUENCE }, { "title", required_argument, NULL, 't' }, { "min-duration",required_argument, NULL, MIN_DURATION }, @@ -2527,6 +2536,10 @@ static int ParseOptions( int argc, char ** argv ) } chapter_markers = 1; break; + case IMAGE_SEQUENCE: + image_sequence = 1; + sequence_framerate = strdup( optarg ); + break; case AUDIO_LANG_LIST: audio_lang_list = hb_str_vsplit(optarg, ','); break; @@ -3613,6 +3626,12 @@ static hb_dict_t * PreparePreset(const char *preset_name) { hb_dict_set(preset, "ChapterMarkers", hb_value_bool(chapter_markers)); } + if (image_sequence != -1) + { + hb_dict_set(preset, "ImageSequence", hb_value_bool(image_sequence)); + hb_dict_set(preset, "SequenceFramerate", + hb_value_string(sequence_framerate)); + } if (align_av_start != -1) { hb_dict_set(preset, "AlignAVStart", hb_value_bool(align_av_start)); diff --git a/win/CS/HandBrake.Interop/Interop/HandBrakeInstance.cs b/win/CS/HandBrake.Interop/Interop/HandBrakeInstance.cs index 7b04a036f089..3891394042f1 100644 --- a/win/CS/HandBrake.Interop/Interop/HandBrakeInstance.cs +++ b/win/CS/HandBrake.Interop/Interop/HandBrakeInstance.cs @@ -133,18 +133,25 @@ public void Initialize(int verbosity, bool noHardware) /// /// The number of previews to make on each title. /// + /// + /// Whether to scan for an image sequence. + /// + /// + /// The framerate to use if using an image sequence. + /// /// /// The minimum duration of a title to show up on the scan. /// /// /// The title index to scan (1-based, 0 for all titles). /// - public void StartScan(string path, int previewCount, TimeSpan minDuration, int titleIndex) + public void StartScan(string path, int previewCount, bool imageSequence, string sequenceFramerate, TimeSpan minDuration, int titleIndex) { this.PreviewCount = previewCount; IntPtr pathPtr = InteropUtilities.ToUtf8PtrFromString(path); - HBFunctions.hb_scan(this.Handle, pathPtr, titleIndex, previewCount, 1, (ulong)(minDuration.TotalSeconds * 90000)); + IntPtr sRatePtr = InteropUtilities.ToUtf8PtrFromString(sequenceFramerate); + HBFunctions.hb_scan(this.Handle, pathPtr, titleIndex, (imageSequence ? 1 : 0), sRatePtr, previewCount, 1, (ulong)(minDuration.TotalSeconds * 90000)); Marshal.FreeHGlobal(pathPtr); this.scanPollTimer = new Timer(); diff --git a/win/CS/HandBrake.Interop/Interop/HbLib/HbFunctions.cs b/win/CS/HandBrake.Interop/Interop/HbLib/HbFunctions.cs index 911b09b526c7..2d9d396c0699 100644 --- a/win/CS/HandBrake.Interop/Interop/HbLib/HbFunctions.cs +++ b/win/CS/HandBrake.Interop/Interop/HbLib/HbFunctions.cs @@ -49,7 +49,7 @@ internal static class HBFunctions public static extern void hb_dvd_set_dvdnav(int enable); [DllImport("hb", EntryPoint = "hb_scan", CallingConvention = CallingConvention.Cdecl)] - public static extern void hb_scan(IntPtr hbHandle, IntPtr path, int title_index, int preview_count, int store_previews, ulong min_duration); + public static extern void hb_scan(IntPtr hbHandle, IntPtr path, int title_index, int image_sequence, IntPtr sequence_framerate, int preview_count, int store_previews, ulong min_duration); [DllImport("hb", EntryPoint = "hb_scan_stop", CallingConvention = CallingConvention.Cdecl)] public static extern void hb_scan_stop(IntPtr hbHandle); @@ -148,13 +148,13 @@ internal static class HBFunctions public static extern void hb_video_quality_get_limits(uint codec, ref float low, ref float high, ref float granularity, ref int direction); [DllImport("hb", EntryPoint = "hb_video_quality_get_name", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr hb_video_quality_get_name(uint codec); - - [DllImport("hb", EntryPoint = "hb_video_twopass_is_supported", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr hb_video_quality_get_name(uint codec); + + [DllImport("hb", EntryPoint = "hb_video_twopass_is_supported", CallingConvention = CallingConvention.Cdecl)] public static extern int hb_video_twopass_is_supported(uint codec); - - [DllImport("hb", EntryPoint = "hb_video_encoder_is_supported", CallingConvention = CallingConvention.Cdecl)] - public static extern int hb_video_encoder_is_supported(int encoder); + + [DllImport("hb", EntryPoint = "hb_video_encoder_is_supported", CallingConvention = CallingConvention.Cdecl)] + public static extern int hb_video_encoder_is_supported(int encoder); [DllImport("hb", EntryPoint = "hb_audio_quality_get_limits", CallingConvention = CallingConvention.Cdecl)] public static extern void hb_audio_quality_get_limits(uint codec, ref float low, ref float high, ref float granularity, ref int direction); @@ -193,16 +193,16 @@ internal static class HBFunctions public static extern IntPtr hb_mixdown_get_next(IntPtr last); [DllImport("hb", EntryPoint = "hb_video_encoder_get_next", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr hb_video_encoder_get_next(IntPtr last); - + public static extern IntPtr hb_video_encoder_get_next(IntPtr last); + [DllImport("hb", EntryPoint = "hb_video_encoder_get_default", CallingConvention = CallingConvention.Cdecl)] - public static extern int hb_video_encoder_get_default(int muxer); + public static extern int hb_video_encoder_get_default(int muxer); [DllImport("hb", EntryPoint = "hb_audio_encoder_get_next", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr hb_audio_encoder_get_next(IntPtr last); - + [DllImport("hb", EntryPoint = "hb_audio_encoder_get_default", CallingConvention = CallingConvention.Cdecl)] - public static extern int hb_audio_encoder_get_default(int muxer); + public static extern int hb_audio_encoder_get_default(int muxer); [DllImport("hb", EntryPoint = "hb_container_get_next", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr hb_container_get_next(IntPtr last); diff --git a/win/CS/HandBrake.Interop/Interop/Interfaces/IHandBrakeInstance.cs b/win/CS/HandBrake.Interop/Interop/Interfaces/IHandBrakeInstance.cs index eefa011913fa..487b6a0ec789 100644 --- a/win/CS/HandBrake.Interop/Interop/Interfaces/IHandBrakeInstance.cs +++ b/win/CS/HandBrake.Interop/Interop/Interfaces/IHandBrakeInstance.cs @@ -93,7 +93,7 @@ public interface IHandBrakeInstance : IEncodeInstance /// /// The title Index. /// - void StartScan(string path, int previewCount, TimeSpan minDuration, int titleIndex); + void StartScan(string path, int previewCount, bool imageSequence, string sequenceFramerate, TimeSpan minDuration, int titleIndex); /// /// Stop any running scans diff --git a/win/CS/HandBrake.Interop/Interop/Json/Encode/Source.cs b/win/CS/HandBrake.Interop/Interop/Json/Encode/Source.cs index 7727b99dc6d9..a1410d6b5243 100644 --- a/win/CS/HandBrake.Interop/Interop/Json/Encode/Source.cs +++ b/win/CS/HandBrake.Interop/Interop/Json/Encode/Source.cs @@ -33,5 +33,15 @@ public class Source /// Gets or sets the path. /// public string Path { get; set; } + + /// + /// Gets or sets whether input is an image sequence. + /// + public bool ImageSequence { get; set; } + + /// + /// Gets or sets the framerate for an image sequence. + /// + public string SequenceFramerate { get; set; } } } \ No newline at end of file diff --git a/win/CS/HandBrake.Interop/Interop/Json/Presets/HBPreset.cs b/win/CS/HandBrake.Interop/Interop/Json/Presets/HBPreset.cs index 3e72391cb167..0ac62d5f6e38 100644 --- a/win/CS/HandBrake.Interop/Interop/Json/Presets/HBPreset.cs +++ b/win/CS/HandBrake.Interop/Interop/Json/Presets/HBPreset.cs @@ -53,6 +53,11 @@ public class HBPreset /// public bool ChapterMarkers { get; set; } + /// + /// Gets or sets a value indicating whether input is an image sequence. + /// + public bool ImageSequence { get; set; } + /// /// Gets or sets the children array. /// diff --git a/win/CS/HandBrake.sln b/win/CS/HandBrake.sln index 3b3b4b189fc7..d499a405a905 100644 --- a/win/CS/HandBrake.sln +++ b/win/CS/HandBrake.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28803.352 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32210.308 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5CB7BC74-449C-4E95-98AB-E1E4387E514B}" ProjectSection(SolutionItems) = preProject diff --git a/win/CS/HandBrakeWPF/App.xaml.cs b/win/CS/HandBrakeWPF/App.xaml.cs index 03aeadc8e9e3..e1c52fc6a324 100644 --- a/win/CS/HandBrakeWPF/App.xaml.cs +++ b/win/CS/HandBrakeWPF/App.xaml.cs @@ -213,7 +213,7 @@ protected override void OnStartup(StartupEventArgs e) if (args.Any() && (File.Exists(args[0]) || Directory.Exists(args[0]))) { IMainViewModel mvm = IoC.Get(); - mvm.StartScan(args[0], 0); + mvm.StartScan(args[0], 0, false, "0"); } } diff --git a/win/CS/HandBrakeWPF/Controls/SourceSelection.xaml b/win/CS/HandBrakeWPF/Controls/SourceSelection.xaml index 61ccb5507dac..a6b4877f1186 100644 --- a/win/CS/HandBrakeWPF/Controls/SourceSelection.xaml +++ b/win/CS/HandBrakeWPF/Controls/SourceSelection.xaml @@ -25,6 +25,8 @@ + + @@ -90,7 +92,30 @@ - + + + + +