diff options
author | jstebbins <[email protected]> | 2012-04-26 19:11:03 +0000 |
---|---|---|
committer | jstebbins <[email protected]> | 2012-04-26 19:11:03 +0000 |
commit | 143f723b12881a8ab3732386785a00840bb2c934 (patch) | |
tree | c1bfd505f424c241a34ed5f90e474efac637c4fc | |
parent | 3f9828657ded40e2eabfef8f01fa2182a4eccd36 (diff) |
PGS (bluray) subtitle support \o/
Thanks to patches supplied by David Mitchell and Rob McMullen
we finally have PGS support.
I added a fix for libav pgs timestamp processing and
detection of forced subtitles to their work, then
made foreign audio search work with PGS subs.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@4605 b64f7644-9d1e-0410-96f1-a4d463321fa5
38 files changed, 1236 insertions, 330 deletions
diff --git a/contrib/ffmpeg/A09-pgs-pts.patch b/contrib/ffmpeg/A09-pgs-pts.patch new file mode 100644 index 000000000..b03f5acce --- /dev/null +++ b/contrib/ffmpeg/A09-pgs-pts.patch @@ -0,0 +1,82 @@ +diff -Naur ffmpeg-v0.7-1696-gcae4f4b.orig/libavcodec/avcodec.h ffmpeg-v0.7-1696-gcae4f4b/libavcodec/avcodec.h +--- ffmpeg-v0.7-1696-gcae4f4b.orig/libavcodec/avcodec.h 2011-11-03 12:29:48.000000000 +0100 ++++ ffmpeg-v0.7-1696-gcae4f4b/libavcodec/avcodec.h 2012-04-22 10:59:41.122351884 +0200 +@@ -3193,6 +3193,7 @@ + unsigned num_rects; + AVSubtitleRect **rects; + int64_t pts; ///< Same as packet pts, in AV_TIME_BASE ++ uint8_t forced; + } AVSubtitle; + + /* packet functions */ +diff -Naur ffmpeg-v0.7-1696-gcae4f4b.orig/libavcodec/pgssubdec.c ffmpeg-v0.7-1696-gcae4f4b/libavcodec/pgssubdec.c +--- ffmpeg-v0.7-1696-gcae4f4b.orig/libavcodec/pgssubdec.c 2011-11-03 12:29:48.000000000 +0100 ++++ ffmpeg-v0.7-1696-gcae4f4b/libavcodec/pgssubdec.c 2012-04-22 13:09:29.499671541 +0200 +@@ -45,6 +45,8 @@ + int y; + int id_number; + int object_number; ++ uint8_t composition_flag; ++ int64_t pts; + } PGSSubPresentation; + + typedef struct PGSSubPicture { +@@ -271,7 +273,8 @@ + * @todo TODO: Implement forcing of subtitles + */ + static void parse_presentation_segment(AVCodecContext *avctx, +- const uint8_t *buf, int buf_size) ++ const uint8_t *buf, int buf_size, ++ int64_t pts) + { + PGSSubContext *ctx = avctx->priv_data; + +@@ -280,6 +283,8 @@ + int w = bytestream_get_be16(&buf); + int h = bytestream_get_be16(&buf); + ++ ctx->presentation.pts = pts; ++ + av_dlog(avctx, "Video Dimensions %dx%d\n", + w, h); + if (av_image_check_size(w, h, 0, avctx) >= 0) +@@ -299,16 +304,17 @@ + buf += 3; + + ctx->presentation.object_number = bytestream_get_byte(&buf); ++ ctx->presentation.composition_flag = 0; + if (!ctx->presentation.object_number) + return; + + /* +- * Skip 4 bytes of unknown: ++ * Skip 3 bytes of unknown: + * object_id_ref (2 bytes), + * window_id_ref, +- * composition_flag (0x80 - object cropped, 0x40 - object forced) + */ +- buf += 4; ++ buf += 3; ++ ctx->presentation.composition_flag = bytestream_get_byte(&buf); + + x = bytestream_get_be16(&buf); + y = bytestream_get_be16(&buf); +@@ -356,6 +362,9 @@ + */ + + memset(sub, 0, sizeof(*sub)); ++ sub->pts = ctx->presentation.pts; ++ sub->forced = (ctx->presentation.composition_flag & 0x40) != 0; ++ + // Blank if last object_number was 0. + // Note that this may be wrong for more complex subtitles. + if (!ctx->presentation.object_number) +@@ -441,7 +450,7 @@ + parse_picture_segment(avctx, buf, segment_length); + break; + case PRESENTATION_SEGMENT: +- parse_presentation_segment(avctx, buf, segment_length); ++ parse_presentation_segment(avctx, buf, segment_length, avpkt->pts); + break; + case WINDOW_SEGMENT: + /* diff --git a/gtk/src/callbacks.c b/gtk/src/callbacks.c index 94a932d67..ba0d569cc 100644 --- a/gtk/src/callbacks.c +++ b/gtk/src/callbacks.c @@ -1268,6 +1268,7 @@ container_changed_cb(GtkWidget *widget, signal_user_data_t *ud) ghb_clear_presets_selection(ud); ghb_live_reset(ud); ghb_subtitle_prune(ud); + ghb_subtitle_list_refresh_selected(ud); ghb_audio_list_refresh_selected(ud); } diff --git a/gtk/src/hb-backend.c b/gtk/src/hb-backend.c index 2b2b66348..af1b890e5 100644 --- a/gtk/src/hb-backend.c +++ b/gtk/src/hb-backend.c @@ -1423,37 +1423,6 @@ ghb_subtitle_track_source(GValue *settings, gint track) } const char* -ghb_subtitle_source_name(gint source) -{ - const gchar * name = "Unknown"; - switch (source) - { - case VOBSUB: - name = "VOBSUB"; - break; - case TX3GSUB: - name = "TX3G"; - break; - case UTF8SUB: - name = "UTF8"; - break; - case CC708SUB: - case CC608SUB: - name = "CC"; - break; - case SRTSUB: - name = "SRT"; - break; - case SSASUB: - name = "SSA"; - break; - default: - break; - } - return name; -} - -const char* ghb_subtitle_track_source_name(GValue *settings, gint track) { gint titleindex; @@ -1491,7 +1460,7 @@ ghb_subtitle_track_source_name(GValue *settings, gint track) sub = hb_list_item( title->list_subtitle, track); if (sub != NULL) { - name = ghb_subtitle_source_name(sub->source); + name = hb_subsource_name(sub->source); } done: @@ -2304,7 +2273,7 @@ subtitle_track_opts_set(GtkBuilder *builder, const gchar *name, gint titleindex) subtitle = (hb_subtitle_t *)hb_list_item(title->list_subtitle, ii); options[ii] = g_strdup_printf("%d - %s (%s)", ii+1, subtitle->lang, - ghb_subtitle_source_name(subtitle->source)); + hb_subsource_name(subtitle->source)); subtitle_opts.map[ii+1].option = options[ii]; subtitle_opts.map[ii+1].shortOpt = index_str[ii]; subtitle_opts.map[ii+1].ivalue = ii; @@ -2681,18 +2650,6 @@ ghb_find_cc_track(gint titleindex) return -2; } -static gboolean -canForce(int source) -{ - return (source == VOBSUB); -} - -static gboolean -canBurn(int source) -{ - return (source == VOBSUB || source == SSASUB); -} - gint ghb_find_subtitle_track( gint titleindex, @@ -2745,8 +2702,8 @@ ghb_find_subtitle_track( continue; subtitle = (hb_subtitle_t*)hb_list_item( title->list_subtitle, ii ); - if (((!force || (force && canForce(subtitle->source))) && - (!burn || (burn && canBurn(subtitle->source)))) && + if (((!force || (force && ghb_canForceSub(subtitle->source))) && + (!burn || (burn && ghb_canBurnSub(subtitle->source)))) && ((strcmp(lang, subtitle->iso639_2) == 0) || (strcmp(lang, "und") == 0))) { @@ -5106,7 +5063,7 @@ add_job(hb_handle_t *h, GValue *js, gint unique_id, gint titleindex) { sub_config.dest = PASSTHRUSUB; } - else if ( burned && canBurn(subt->source) ) + else if ( burned && ghb_canBurnSub(subt->source) ) { // Only allow one subtitle to be burned into the video if (one_burned) diff --git a/gtk/src/hb-backend.h b/gtk/src/hb-backend.h index 407dc7315..22f3f6770 100644 --- a/gtk/src/hb-backend.h +++ b/gtk/src/hb-backend.h @@ -163,7 +163,6 @@ gint ghb_get_title_number(gint titleindex); int ghb_get_title_count(); gint ghb_subtitle_track_source(GValue *settings, gint track); const char* ghb_subtitle_track_source_name(GValue *settings, gint track); -const char* ghb_subtitle_source_name(gint source); gchar* ghb_subtitle_track_lang(GValue *settings, gint track); gboolean ghb_validate_vquality(GValue *settings); diff --git a/gtk/src/subtitlehandler.c b/gtk/src/subtitlehandler.c index ffe38a0e6..5b5036520 100644 --- a/gtk/src/subtitlehandler.c +++ b/gtk/src/subtitlehandler.c @@ -42,16 +42,22 @@ free_subtitle_key(gpointer data) g_free(data); } -static gboolean -canBurn(int source) +gboolean +ghb_canPassSub(int source, int mux) { - return (source == VOBSUB || source == SSASUB); + return hb_subtitle_can_pass(source, mux); } -static gboolean -canForce(int source) +gboolean +ghb_canBurnSub(int source) { - return (source == VOBSUB); + return hb_subtitle_can_burn(source); +} + +gboolean +ghb_canForceSub(int source) +{ + return hb_subtitle_can_force(source); } gboolean @@ -86,7 +92,6 @@ ghb_subtitle_exclusive_burn_settings(GValue *settings, gint index) { if (ii != index) { -printf("settings burn %d also %d\n", index, ii); subsettings = ghb_array_get_nth(subtitle_list, ii); ghb_settings_set_boolean(subsettings, "SubtitleBurned", FALSE); } @@ -114,7 +119,6 @@ ghb_subtitle_exclusive_burn(signal_user_data_t *ud, gint index) gtk_tree_model_iter_nth_child(tm, &ti, NULL, ii); if (ii != index) { -printf("ui burn %d also %d\n", index, ii); subsettings = ghb_array_get_nth(subtitle_list, ii); ghb_settings_set_boolean(subsettings, "SubtitleBurned", FALSE); gtk_list_store_set(GTK_LIST_STORE(tm), &ti, 2, FALSE, -1); @@ -226,7 +230,8 @@ ghb_add_subtitle_to_settings(GValue *settings, GValue *subsettings) // Add the current subtitle settings to the list. GValue *subtitle_list; gint count; - gboolean burned; + gboolean burned, forced; + gint source, mux; const gchar *track; const gchar *lang; @@ -256,9 +261,29 @@ ghb_add_subtitle_to_settings(GValue *settings, GValue *subsettings) lang = ghb_settings_combo_string(subsettings, "SubtitleTrack"); ghb_settings_set_string(subsettings, "SubtitleLanguage", lang); + mux = ghb_settings_combo_int(settings, "FileFormat"); + source = ghb_settings_get_int(subsettings, "SubtitleSource"); + burned = ghb_settings_get_boolean(subsettings, "SubtitleBurned"); + if (burned && !ghb_canBurnSub(source)) + { + burned = FALSE; + ghb_settings_set_boolean(subsettings, "SubtitleBurned", burned); + } + if (!burned && !ghb_canPassSub(source, mux)) + { + burned = TRUE; + ghb_settings_set_boolean(subsettings, "SubtitleBurned", burned); + ghb_settings_set_boolean(subsettings, "SubtitleDefaultTrack", FALSE); + } + forced = ghb_settings_get_boolean(subsettings, "SubtitleForced"); + if (forced && !ghb_canForceSub(source)) + { + forced = FALSE; + ghb_settings_set_boolean(subsettings, "SubtitleForced", forced); + } + ghb_array_append(subtitle_list, subsettings); - burned = ghb_settings_get_boolean(subsettings, "SubtitleBurned"); if (burned) ghb_subtitle_exclusive_burn_settings(settings, count); return TRUE; @@ -300,6 +325,7 @@ add_all_pref_subtitles(signal_user_data_t *ud) for (ii = 0; ii < count; ii++) { subtitle = ghb_value_dup(ghb_array_get_nth(pref_subtitle, ii)); + gboolean force = ghb_settings_get_boolean(subtitle, "SubtitleForced"); lang = ghb_settings_get_string(subtitle, "SubtitleLanguage"); // If there are multiple subtitles using the same language, then // select sequential tracks for each. The hash keeps track @@ -417,7 +443,7 @@ ghb_set_pref_subtitle_settings(gint titleindex, GValue *settings) GValue *subsettings; gboolean burn; - track = ghb_find_subtitle_track(titleindex, pref_lang, FALSE, FALSE, VOBSUB, track_indices); + track = ghb_find_subtitle_track(titleindex, pref_lang, FALSE, FALSE, PGSSUB, track_indices); if (track >= -1) { int source; @@ -463,7 +489,6 @@ ghb_set_pref_subtitle_settings(gint titleindex, GValue *settings) if (track >= 0) { int source; - subsettings = ghb_dict_value_new(); ghb_settings_set_int(subsettings, "SubtitleTrack", track); source = ghb_subtitle_track_source(settings, track); @@ -606,8 +631,10 @@ subtitle_forced_toggled_cb( settings = ghb_array_get_nth(subtitle_list, row); source = ghb_settings_get_int(settings, "SubtitleSource"); - if (!canForce(source)) + if (!ghb_canForceSub(source)) + { return; + } ghb_settings_set_boolean(settings, "SubtitleForced", active); gtk_list_store_set(GTK_LIST_STORE(tm), &ti, 1, active, -1); @@ -630,7 +657,7 @@ subtitle_burned_toggled_cb( GValue *subtitle_list; gint count; GValue *settings; - gint source; + gint source, mux; g_debug("burned toggled"); tp = gtk_tree_path_new_from_string (path); @@ -654,7 +681,10 @@ subtitle_burned_toggled_cb( settings = ghb_array_get_nth(subtitle_list, row); source = ghb_settings_get_int(settings, "SubtitleSource"); - if (!canBurn(source)) + if (!ghb_canBurnSub(source)) + return; + mux = ghb_settings_combo_int(ud->settings, "FileFormat"); + if (!active && !hb_subtitle_can_pass(source, mux)) return; ghb_settings_set_boolean(settings, "SubtitleBurned", active); @@ -711,6 +741,12 @@ subtitle_default_toggled_cb( settings = ghb_array_get_nth(subtitle_list, row); + int source, mux; + source = ghb_settings_get_int(settings, "SubtitleSource"); + mux = ghb_settings_combo_int(ud->settings, "FileFormat"); + if (active && !hb_subtitle_can_pass(source, mux)) + return; + ghb_settings_set_boolean(settings, "SubtitleDefaultTrack", active); gtk_list_store_set(GTK_LIST_STORE(tm), &ti, 3, active, -1); @@ -724,8 +760,8 @@ subtitle_default_toggled_cb( ghb_live_reset(ud); } -static void -subtitle_list_refresh_selected(signal_user_data_t *ud) +void +ghb_subtitle_list_refresh_selected(signal_user_data_t *ud) { GtkTreeView *treeview; GtkTreePath *treepath; @@ -760,22 +796,34 @@ subtitle_list_refresh_selected(signal_user_data_t *ud) return; settings = ghb_array_get_nth(subtitle_list, row); - def = ghb_settings_get_boolean(settings, "SubtitleDefaultTrack"); + burned = ghb_settings_get_boolean(settings, "SubtitleBurned"); gint i_source; i_source = ghb_settings_get_int(settings, "SubtitleSource"); - if (!canBurn(i_source)) + if (!ghb_canBurnSub(i_source)) { burned = FALSE; ghb_settings_set_boolean(settings, "SubtitleBurned", burned); } - if (!canForce(i_source)) + + gint i_mux; + i_mux = ghb_settings_combo_int(ud->settings, "FileFormat"); + if (!burned && !ghb_canPassSub(i_source, i_mux)) + { + burned = TRUE; + ghb_settings_set_boolean(settings, "SubtitleBurned", burned); + ghb_settings_set_boolean(settings, "SubtitleDefaultTrack", FALSE); + } + + forced = ghb_settings_get_boolean(settings, "SubtitleForced"); + if (!ghb_canForceSub(i_source)) { - // Force only apply to VOBSUBS forced = FALSE; ghb_settings_set_boolean(settings, "SubtitleForced", forced); } + def = ghb_settings_get_boolean(settings, "SubtitleDefaultTrack"); + if (i_source == SRTSUB) { const gchar *lang; @@ -801,24 +849,18 @@ subtitle_list_refresh_selected(signal_user_data_t *ud) } g_free(code); offset = ghb_settings_get_int(settings, "SrtOffset"); - - forced = FALSE; - burned = FALSE; } else { track = g_strdup( ghb_settings_combo_option(settings, "SubtitleTrack")); - source = g_strdup(ghb_subtitle_source_name(i_source)); + source = g_strdup(hb_subsource_name(i_source)); s_track = ghb_settings_get_string(settings, "SubtitleTrack"); - - forced = ghb_settings_get_boolean(settings, "SubtitleForced"); - burned = ghb_settings_get_boolean(settings, "SubtitleBurned"); } - if (canBurn(i_source)) + if (ghb_canBurnSub(i_source)) allow_burn = TRUE; - if (canForce(i_source)) + if (ghb_canForceSub(i_source)) allow_force = TRUE; gtk_list_store_set(GTK_LIST_STORE(store), &iter, @@ -864,7 +906,7 @@ subtitle_track_changed_cb(GtkWidget *widget, signal_user_data_t *ud) ghb_settings_set_int(settings, "SubtitleSource", source); lang = ghb_settings_combo_string(settings, "SubtitleTrack"); ghb_settings_set_string(settings, "SubtitleLanguage", lang); - subtitle_list_refresh_selected(ud); + ghb_subtitle_list_refresh_selected(ud); ghb_live_reset(ud); } ghb_live_reset(ud); @@ -882,7 +924,7 @@ srt_changed_cb(GtkWidget *widget, signal_user_data_t *ud) if (settings != NULL) { ghb_widget_to_setting(settings, widget); - subtitle_list_refresh_selected(ud); + ghb_subtitle_list_refresh_selected(ud); ghb_live_reset(ud); } @@ -902,7 +944,7 @@ srt_file_changed_cb(GtkWidget *widget, signal_user_data_t *ud) gchar *filename, *dirname; ghb_widget_to_setting(settings, widget); - subtitle_list_refresh_selected(ud); + ghb_subtitle_list_refresh_selected(ud); ghb_live_reset(ud); @@ -936,7 +978,7 @@ srt_lang_changed_cb(GtkWidget *widget, signal_user_data_t *ud) const gchar *lang; ghb_widget_to_setting(settings, widget); - subtitle_list_refresh_selected(ud); + ghb_subtitle_list_refresh_selected(ud); ghb_live_reset(ud); @@ -982,7 +1024,7 @@ add_to_subtitle_list( GtkTreeIter iter; GtkListStore *store; GtkTreeSelection *selection; - const gchar *track, *source; + const gchar *track; gboolean forced, burned, def; gchar *s_track; gint i_source; @@ -1001,11 +1043,10 @@ add_to_subtitle_list( s_track = ghb_settings_get_string(settings, "SubtitleTrack"); i_source = ghb_settings_get_int(settings, "SubtitleSource"); - source = ghb_subtitle_source_name(i_source); - if (canBurn(i_source)) + if (ghb_canBurnSub(i_source)) allow_burn = TRUE; - if (canForce(i_source)) + if (ghb_canForceSub(i_source)) allow_force = TRUE; gtk_list_store_append(store, &iter); @@ -1251,7 +1292,7 @@ subtitle_add_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) // Add the current subtitle settings to the list. GValue *settings; gboolean burned = FALSE; - gint track, source; + gint track, source, mux; g_debug("subtitle_add_clicked_cb ()"); @@ -1260,6 +1301,11 @@ subtitle_add_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud) settings = ghb_dict_value_new(); ghb_settings_set_int(settings, "SubtitleTrack", track); source = ghb_subtitle_track_source(ud->settings, track); + + // Initialize to passthru if possible, else burn + mux = ghb_settings_combo_int(ud->settings, "FileFormat"); + burned = !hb_subtitle_can_pass(source, mux); + ghb_settings_set_int(settings, "SubtitleSource", source); ghb_settings_take_value(settings, "SubtitleForced", ghb_boolean_value_new(FALSE)); @@ -1331,7 +1377,6 @@ void ghb_subtitle_prune(signal_user_data_t *ud) { GtkTreeView *tv; - GtkTreeModel *tm; GValue *subtitle_list; gint count, ii; gint first_track = 0, one_burned = 0; @@ -1343,7 +1388,6 @@ ghb_subtitle_prune(signal_user_data_t *ud) tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "subtitle_list")); g_return_if_fail(tv != NULL); - tm = gtk_tree_view_get_model(tv); for (ii = count-1; ii >= 0; ii--) { gboolean burned; diff --git a/gtk/src/subtitlehandler.h b/gtk/src/subtitlehandler.h index ac662499f..267b79bc2 100644 --- a/gtk/src/subtitlehandler.h +++ b/gtk/src/subtitlehandler.h @@ -35,5 +35,9 @@ gint ghb_selected_subtitle_row(signal_user_data_t *ud); void ghb_reset_subtitles(signal_user_data_t *ud, GValue *settings); void ghb_subtitle_prune(signal_user_data_t *ud); gboolean ghb_soft_in_subtitle_list(GValue *subtitle_list); +gboolean ghb_canBurnSub(int source); +gboolean ghb_canForceSub(int source); +gboolean ghb_canPassSub(int source, int mux); +void ghb_subtitle_list_refresh_selected(signal_user_data_t *ud); #endif // _SUBTITLEHANDLER_H_ diff --git a/libhb/bd.c b/libhb/bd.c index 9ec4c078a..bb051115a 100644 --- a/libhb/bd.c +++ b/libhb/bd.c @@ -81,6 +81,44 @@ int hb_bd_title_count( hb_bd_t * d ) return d->title_count; } +static void add_subtitle(int track, hb_list_t *list_subtitle, BLURAY_STREAM_INFO *bdsub, uint32_t codec) +{ + hb_subtitle_t * subtitle; + iso639_lang_t * lang; + + subtitle = calloc( sizeof( hb_subtitle_t ), 1 ); + + subtitle->track = track; + subtitle->id = bdsub->pid; + lang = lang_for_code2( (char*)bdsub->lang ); + snprintf( subtitle->lang, sizeof( subtitle->lang ), "%s", + strlen(lang->native_name) ? lang->native_name : lang->eng_name); + snprintf( subtitle->iso639_2, sizeof( subtitle->iso639_2 ), "%s", + lang->iso639_2); + + switch ( bdsub->coding_type ) + { + case BLURAY_STREAM_TYPE_SUB_PG: + subtitle->source = PGSSUB; + subtitle->format = PICTURESUB; + subtitle->config.dest = RENDERSUB; + break; + default: + // Unrecognized, don't add to list + free( subtitle ); + return; + } + subtitle->reg_desc = STR4_TO_UINT32("HDMV"); + subtitle->stream_type = bdsub->coding_type; + subtitle->codec = codec; + + hb_log( "bd: subtitle id=0x%x, lang=%s, 3cc=%s", subtitle->id, + subtitle->lang, subtitle->iso639_2 ); + + hb_list_add( list_subtitle, subtitle ); + return; +} + static void add_audio(int track, hb_list_t *list_audio, BLURAY_STREAM_INFO *bdaudio, int substream_type, uint32_t codec, uint32_t codec_param) { hb_audio_t * audio; @@ -89,6 +127,7 @@ static void add_audio(int track, hb_list_t *list_audio, BLURAY_STREAM_INFO *bdau audio = calloc( sizeof( hb_audio_t ), 1 ); audio->id = (substream_type << 16) | bdaudio->pid; + audio->config.in.reg_desc = STR4_TO_UINT32("HDMV"); audio->config.in.stream_type = bdaudio->coding_type; audio->config.in.substream_type = substream_type; audio->config.in.codec = codec; @@ -393,6 +432,25 @@ hb_title_t * hb_bd_title_scan( hb_bd_t * d, int tt, uint64_t min_duration ) } } + // Add all the subtitles found in the above clip. + for ( ii = 0; ii < ti->clips[audio_clip_index].pg_stream_count; ii++ ) + { + BLURAY_STREAM_INFO * bdpgs; + + bdpgs = &ti->clips[audio_clip_index].pg_streams[ii]; + + switch( bdpgs->coding_type ) + { + case BLURAY_STREAM_TYPE_SUB_PG: + add_subtitle(ii, title->list_subtitle, bdpgs, WORK_DECPGSSUB); + break; + default: + hb_log( "scan: unknown subtitle pid 0x%x codec 0x%x", + bdpgs->pid, bdpgs->coding_type ); + break; + } + } + /* Chapters */ for ( ii = 0; ii < ti->chapter_count; ii++ ) { diff --git a/libhb/common.c b/libhb/common.c index 3f3097485..23c6ea21e 100644 --- a/libhb/common.c +++ b/libhb/common.c @@ -1751,6 +1751,7 @@ int hb_subtitle_add(const hb_job_t * job, const hb_subtitle_config_t * subtitlec return 0; } subtitle->config = *subtitlecfg; + subtitle->out_track = hb_list_count(job->list_subtitle) + 1; hb_list_add(job->list_subtitle, subtitle); return 1; } @@ -1768,6 +1769,7 @@ int hb_srt_add( const hb_job_t * job, subtitle->id = (hb_list_count(job->list_subtitle) << 8) | 0xFF; subtitle->format = TEXTSUB; subtitle->source = SRTSUB; + subtitle->codec = WORK_DECSRTSUB; language = lang_for_code2( lang ); @@ -1786,6 +1788,61 @@ int hb_srt_add( const hb_job_t * job, return retval; } +int hb_subtitle_can_force( int source ) +{ + return source == VOBSUB || source == PGSSUB; +} + +int hb_subtitle_can_burn( int source ) +{ + return source == VOBSUB || source == PGSSUB || source == SSASUB; +} + +int hb_subtitle_can_pass( int source, int mux ) +{ + if ( mux == HB_MUX_MKV ) + { + switch( source ) + { + case PGSSUB: + case VOBSUB: + case SSASUB: + case SRTSUB: + case UTF8SUB: + case TX3GSUB: + case CC608SUB: + case CC708SUB: + return 1; + + default: + return 0; + } + } + else if ( mux == HB_MUX_MP4 ) + { + switch( source ) + { + case VOBSUB: + case SSASUB: + case SRTSUB: + case UTF8SUB: + case TX3GSUB: + case CC608SUB: + case CC708SUB: + return 1; + + default: + return 0; + } + } + else + { + // Internal error. Should never get here. + hb_error("internel error. Bad mux %d\n", mux); + return 0; + } +} + char * hb_strdup_printf( const char * fmt, ... ) { int len; @@ -1964,6 +2021,8 @@ const char * hb_subsource_name( int source ) return "TX3G"; case SSASUB: return "SSA"; + case PGSSUB: + return "PGS"; default: return "Unknown"; } diff --git a/libhb/common.h b/libhb/common.h index d38f77a0b..741c32e03 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -122,6 +122,9 @@ hb_subtitle_t *hb_subtitle_copy(const hb_subtitle_t *src); int hb_subtitle_add(const hb_job_t * job, const hb_subtitle_config_t * subtitlecfg, int track); int hb_srt_add(const hb_job_t * job, const hb_subtitle_config_t * subtitlecfg, const char *lang); +int hb_subtitle_can_force( int source ); +int hb_subtitle_can_burn( int source ); +int hb_subtitle_can_pass( int source, int mux ); hb_attachment_t *hb_attachment_copy(const hb_attachment_t *src); @@ -610,11 +613,12 @@ struct hb_subtitle_s { int id; int track; + int out_track; hb_subtitle_config_t config; enum subtype { PICTURESUB, TEXTSUB } format; - enum subsource { VOBSUB, SRTSUB, CC608SUB, /*unused*/CC708SUB, UTF8SUB, TX3GSUB, SSASUB } source; + enum subsource { VOBSUB, SRTSUB, CC608SUB, /*unused*/CC708SUB, UTF8SUB, TX3GSUB, SSASUB, PGSSUB } source; char lang[1024]; char iso639_2[4]; uint8_t type; /* Closed Caption, Childrens, Directors etc */ @@ -622,6 +626,7 @@ struct hb_subtitle_s // Color lookup table for VOB subtitle tracks. Each entry is in YCbCr format. // Must be filled out by the demuxer for VOB subtitle tracks. uint32_t palette[16]; + uint8_t palette_set; int width; int height; @@ -634,6 +639,11 @@ struct hb_subtitle_s #ifdef __LIBHB__ /* Internal data */ + PRIVATE uint32_t codec; /* Input "codec" */ + PRIVATE uint32_t reg_desc; /* registration descriptor of source */ + PRIVATE uint32_t stream_type; /* stream type from source stream */ + PRIVATE uint32_t substream_type;/* substream for multiplexed streams */ + hb_fifo_t * fifo_in; /* SPU ES */ hb_fifo_t * fifo_raw; /* Decoded SPU */ hb_fifo_t * fifo_sync;/* Synced */ @@ -873,6 +883,7 @@ extern hb_work_object_t hb_decsrtsub; extern hb_work_object_t hb_decutf8sub; extern hb_work_object_t hb_dectx3gsub; extern hb_work_object_t hb_decssasub; +extern hb_work_object_t hb_decpgssub; extern hb_work_object_t hb_encavcodec; extern hb_work_object_t hb_encx264; extern hb_work_object_t hb_enctheora; @@ -920,6 +931,7 @@ struct hb_filter_object_s { int id; int enforce_order; + int init_index; char * name; char * settings; diff --git a/libhb/cropscale.c b/libhb/cropscale.c index 11a22a44e..c11e931ee 100644 --- a/libhb/cropscale.c +++ b/libhb/cropscale.c @@ -30,6 +30,7 @@ hb_filter_object_t hb_filter_crop_scale = { .id = HB_FILTER_CROP_SCALE, .enforce_order = 1, + .init_index = 0, .name = "Crop and Scale", .settings = NULL, .init = hb_crop_scale_init, diff --git a/libhb/deblock.c b/libhb/deblock.c index dab622abc..6d1f1aaec 100644 --- a/libhb/deblock.c +++ b/libhb/deblock.c @@ -63,6 +63,7 @@ hb_filter_object_t hb_filter_deblock = { .id = HB_FILTER_DEBLOCK, .enforce_order = 1, + .init_index = 0, .name = "Deblock (pp7)", .settings = NULL, .init = hb_deblock_init, diff --git a/libhb/deccc608sub.c b/libhb/deccc608sub.c index aaa3d020d..1d16410e4 100644 --- a/libhb/deccc608sub.c +++ b/libhb/deccc608sub.c @@ -1607,7 +1607,7 @@ int write_cc_buffer_as_srt (struct eia608_screen *data, struct s_write *wb) if (debug_608) { hb_log ("\n- - - SRT caption - - -\n"); - hb_log (timeline); + hb_log ("%s", timeline); } //fwrite (enc_buffer,enc_buffer_used,1,wb->fh); XMLRPC_APPEND(wb->enc_buffer,wb->enc_buffer_used); diff --git a/libhb/decdca.c b/libhb/decdca.c index 1e6e4192c..5bc118007 100644 --- a/libhb/decdca.c +++ b/libhb/decdca.c @@ -157,7 +157,7 @@ static hb_buffer_t * Decode( hb_work_object_t * w ) hb_buffer_t * buf; hb_audio_t * audio = w->audio; int i, j, k; - int64_t pts, pos; + int64_t pts; uint64_t upts, upos; int num_blocks; @@ -203,7 +203,6 @@ static hb_buffer_t * Decode( hb_work_object_t * w ) /* Get the whole frame */ hb_list_getbytes( pv->list, pv->frame, pv->size, &upts, &upos ); pts = (int64_t)upts; - pos = (int64_t)upos; if ( pts != pv->last_buf_pts ) { diff --git a/libhb/decmpeg2.c b/libhb/decmpeg2.c index 82ba78e31..95b189af0 100644 --- a/libhb/decmpeg2.c +++ b/libhb/decmpeg2.c @@ -654,6 +654,7 @@ static int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es, subtitle->format = TEXTSUB; subtitle->source = CC608SUB; subtitle->config.dest = PASSTHRUSUB; + subtitle->codec = WORK_DECCC608; subtitle->type = 5; snprintf( subtitle->lang, sizeof( subtitle->lang ), "Closed Captions"); /* @@ -664,7 +665,7 @@ static int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es, if( audio ) { snprintf( subtitle->iso639_2, sizeof( subtitle->iso639_2 ), - audio->config.lang.iso639_2); + "%s", audio->config.lang.iso639_2); } else { snprintf( subtitle->iso639_2, sizeof( subtitle->iso639_2 ), "und"); } diff --git a/libhb/decomb.c b/libhb/decomb.c index 7d911ff28..fae95caea 100644 --- a/libhb/decomb.c +++ b/libhb/decomb.c @@ -233,6 +233,7 @@ hb_filter_object_t hb_filter_decomb = { .id = HB_FILTER_DECOMB, .enforce_order = 1, + .init_index = 0, .name = "Decomb", .settings = NULL, .init = hb_decomb_init, diff --git a/libhb/decpgssub.c b/libhb/decpgssub.c new file mode 100644 index 000000000..1b0f7e4cd --- /dev/null +++ b/libhb/decpgssub.c @@ -0,0 +1,268 @@ +#include "hb.h" +#include "hbffmpeg.h" + +struct hb_work_private_s +{ + AVCodecContext * context; + hb_job_t * job; + // For PGS subs, when doing passthru, we don't know if we need a + // packet until we have processed several packets. So we cache + // all the packets we see until libav returns a subtitle with + // the information we need. + hb_buffer_t * list_pass_buffer; + hb_buffer_t * last_pass_buffer; + // It is possible for multiple subtitles to be enncapsulated in + // one packet. This won't happen for PGS subs, but may for other + // types of subtitles. Since I plan to generalize this code to handle + // other than PGS, we will need to keep a list of all subtitles seen + // while parsing an input packet. + hb_buffer_t * list_buffer; + hb_buffer_t * last_buffer; +}; + +static int decsubInit( hb_work_object_t * w, hb_job_t * job ) +{ + AVCodec *codec = avcodec_find_decoder( CODEC_ID_HDMV_PGS_SUBTITLE ); + AVCodecContext *context = avcodec_alloc_context3( codec ); + context->codec = codec; + + hb_work_private_t * pv; + pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + + pv->context = context; + pv->job = job; + + return 0; +} + +static int decsubWork( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_work_private_t * pv = w->private_data; + hb_buffer_t * in = *buf_in; + + if ( in->size <= 0 ) + { + /* EOF on input stream - send it downstream & say that we're done */ + if ( pv->list_buffer == NULL ) + { + pv->list_buffer = pv->last_buffer = in; + } + else + { + pv->last_buffer->next = in; + } + *buf_in = NULL; + *buf_out = pv->list_buffer; + pv->list_buffer = NULL; + + return HB_WORK_DONE; + } + + if ( !pv->job->indepth_scan && + w->subtitle->config.dest == PASSTHRUSUB && + hb_subtitle_can_pass( PGSSUB, pv->job->mux ) ) + { + // Append to buffer list. It will be sent to fifo after we determine + // if this is a packet we need. + if ( pv->list_pass_buffer == NULL ) + { + pv->list_pass_buffer = pv->last_pass_buffer = in; + } + else + { + pv->last_pass_buffer->next = in; + pv->last_pass_buffer = in; + } + // We are keeping the buffer, so prevent the filter loop from + // deleting it. + *buf_in = NULL; + } + + AVSubtitle subtitle; + memset( &subtitle, 0, sizeof(subtitle) ); + + AVPacket avp; + av_init_packet( &avp ); + avp.data = in->data; + avp.size = in->size; + // libav wants pkt pts in AV_TIME_BASE units + avp.pts = av_rescale(in->s.start, AV_TIME_BASE, 90000); + + int has_subtitle = 0; + + do + { + int usedBytes = avcodec_decode_subtitle2( pv->context, &subtitle, &has_subtitle, &avp ); + if (usedBytes < 0) + { + hb_log("unable to decode subtitle with %d bytes.", avp.size); + return HB_WORK_OK; + } + + if (usedBytes <= avp.size) + { + avp.data += usedBytes; + avp.size -= usedBytes; + } + else + { + avp.size = 0; + } + + // The sub is "usable" if: + // 1. libav returned a sub AND + // 2. we are scanning for foreign audio subs OR + // 3. we want all subs (e.g. not forced) OR + // 4. the sub is forced and we only want forced OR + // 5. subtitle clears the previous sub (these are never forced) + // subtitle.num_rects == 0 + uint8_t useable_sub = + pv->job->indepth_scan || + subtitle.num_rects == 0 || + !w->subtitle->config.force || + ( w->subtitle->config.force && subtitle.forced ); + + if (has_subtitle && useable_sub) + { + int64_t pts = av_rescale(subtitle.pts, 90000, AV_TIME_BASE); + hb_buffer_t * out = NULL; + + if( pv->job->indepth_scan ) + { + if (subtitle.forced) + { + w->subtitle->forced_hits++; + } + } + else if ( w->subtitle->config.dest == PASSTHRUSUB && + hb_subtitle_can_pass( PGSSUB, pv->job->mux ) ) + { + out = pv->list_pass_buffer; + pv->list_pass_buffer = NULL; + } + else + { + if (subtitle.num_rects != 0) + { + unsigned ii; + for (ii = 0; ii < subtitle.num_rects; ii++) + { + AVSubtitleRect *rect = subtitle.rects[ii]; + + out = hb_frame_buffer_init( + PIX_FMT_YUVA420P, rect->w, rect->h ); + + out->s.id = in->s.id; + out->sequence = in->sequence; + out->s.start = pts; + out->s.stop = 0; + out->f.x = rect->x; + out->f.y = rect->y; + + uint8_t *lum = out->plane[0].data; + uint8_t *chromaU = out->plane[1].data; + uint8_t *chromaV = out->plane[2].data; + uint8_t *alpha = out->plane[3].data; + + int xx, yy; + for (yy = 0; yy < rect->h; yy++) + { + for (xx = 0; xx < rect->w; xx++) + { + uint32_t argb, yuv; + int pixel; + uint8_t color; + + pixel = yy * rect->w + xx; + color = rect->pict.data[0][pixel]; + argb = ((uint32_t*)rect->pict.data[1])[color]; + yuv = hb_rgb2yuv(argb); + + lum[xx] = (yuv >> 16) & 0xff; + alpha[xx] = (argb >> 24) & 0xff; + if ((xx & 1) == 0 && (yy & 1) == 0) + { + chromaV[xx>>1] = (yuv >> 8) & 0xff; + chromaU[xx>>1] = yuv & 0xff; + } + } + lum += out->plane[0].stride; + if ((yy & 1) == 0) + { + chromaU += out->plane[1].stride; + chromaV += out->plane[2].stride; + } + alpha += out->plane[3].stride; + } + + if ( pv->list_buffer == NULL ) + { + pv->list_buffer = pv->last_buffer = out; + } + else + { + pv->last_buffer->next = out; + pv->last_buffer = out; + } + out = NULL; + } + } + else + { + out = hb_buffer_init( 1 ); + + out->s.id = in->s.id; + out->s.start = pts; + out->s.stop = 0; + out->f.x = 0; + out->f.y = 0; + out->f.width = 0; + out->f.height = 0; + } + } + if ( pv->list_buffer == NULL ) + { + pv->list_buffer = pv->last_buffer = out; + } + else + { + pv->last_buffer->next = out; + } + while (pv->last_buffer && pv->last_buffer->next) + { + pv->last_buffer = pv->last_buffer->next; + } + } + else if ( has_subtitle ) + { + hb_buffer_close( &pv->list_pass_buffer ); + pv->list_pass_buffer = NULL; + } + if ( has_subtitle ) + { + avsubtitle_free(&subtitle); + } + } while (avp.size > 0); + + *buf_out = pv->list_buffer; + pv->list_buffer = NULL; + return HB_WORK_OK; +} + +static void decsubClose( hb_work_object_t * w ) +{ + hb_work_private_t * pv = w->private_data; + avcodec_flush_buffers( pv->context ); + avcodec_close( pv->context ); +} + +hb_work_object_t hb_decpgssub = +{ + WORK_DECPGSSUB, + "PGS decoder", + decsubInit, + decsubWork, + decsubClose +}; diff --git a/libhb/decvobsub.c b/libhb/decvobsub.c index b633e7804..43f2aefd9 100644 --- a/libhb/decvobsub.c +++ b/libhb/decvobsub.c @@ -49,6 +49,7 @@ struct hb_work_private_s uint8_t chromaU[4]; uint8_t chromaV[4]; uint8_t alpha[4]; + uint8_t palette_set; }; static hb_buffer_t * Decode( hb_work_object_t * ); @@ -64,18 +65,23 @@ int decsubInit( hb_work_object_t * w, hb_job_t * job ) pv->pts = 0; // Warn if the input color palette is empty - int paletteEmpty = 1; - int i; - for (i=0; i<16; i++) + pv->palette_set = w->subtitle->palette_set; + if ( pv->palette_set ) { - if (w->subtitle->palette[i]) + // Make sure the entries in the palette are not all 0 + pv->palette_set = 0; + int i; + for (i=0; i<16; i++) { - paletteEmpty = 0; - break; + if (w->subtitle->palette[i]) + { + pv->palette_set = 1; + break; + } } } - if (paletteEmpty) { - hb_log( "decvobsub: input color palette is empty; not demuxed properly?" ); + if (!pv->palette_set) { + hb_log( "decvobsub: input color palette is empty!" ); } return 0; diff --git a/libhb/deinterlace.c b/libhb/deinterlace.c index 2ed6ef3e5..029207805 100644 --- a/libhb/deinterlace.c +++ b/libhb/deinterlace.c @@ -77,6 +77,7 @@ hb_filter_object_t hb_filter_deinterlace = { .id = HB_FILTER_DEINTERLACE, .enforce_order = 1, + .init_index = 0, .name = "Deinterlace (ffmpeg or yadif/mcdeint)", .settings = NULL, .init = hb_deinterlace_init, diff --git a/libhb/denoise.c b/libhb/denoise.c index dfc58863d..48addf8e9 100644 --- a/libhb/denoise.c +++ b/libhb/denoise.c @@ -46,6 +46,7 @@ hb_filter_object_t hb_filter_denoise = { .id = HB_FILTER_DENOISE, .enforce_order = 1, + .init_index = 0, .name = "Denoise (hqdn3d)", .settings = NULL, .init = hb_denoise_init, diff --git a/libhb/detelecine.c b/libhb/detelecine.c index 0437730f2..0ec02303a 100644 --- a/libhb/detelecine.c +++ b/libhb/detelecine.c @@ -98,6 +98,7 @@ hb_filter_object_t hb_filter_detelecine = { .id = HB_FILTER_DETELECINE, .enforce_order = 1, + .init_index = 0, .name = "Detelecine (pullup)", .settings = NULL, .init = hb_detelecine_init, diff --git a/libhb/dvd.c b/libhb/dvd.c index 338dd12d1..f2e0ffeb3 100644 --- a/libhb/dvd.c +++ b/libhb/dvd.c @@ -496,12 +496,16 @@ static hb_title_t * hb_dvdread_title_scan( hb_dvd_t * e, int t, uint64_t min_dur subtitle->format = PICTURESUB; subtitle->source = VOBSUB; subtitle->config.dest = RENDERSUB; // By default render (burn-in) the VOBSUB. + subtitle->stream_type = 0xbd; + subtitle->substream_type = 0x20 + position; + subtitle->codec = WORK_DECVOBSUB; subtitle->type = lang_extension; memcpy( subtitle->palette, vts->vts_pgcit->pgci_srp[pgc_id-1].pgc->palette, 16 * sizeof( uint32_t ) ); + subtitle->palette_set = 1; switch( lang_extension ) { diff --git a/libhb/dvdnav.c b/libhb/dvdnav.c index 52d062b06..8b356e188 100644 --- a/libhb/dvdnav.c +++ b/libhb/dvdnav.c @@ -646,12 +646,16 @@ static hb_title_t * hb_dvdnav_title_scan( hb_dvd_t * e, int t, uint64_t min_dura subtitle->format = PICTURESUB; subtitle->source = VOBSUB; subtitle->config.dest = RENDERSUB; // By default render (burn-in) the VOBSUB. + subtitle->stream_type = 0xbd; + subtitle->substream_type = 0x20 + position; + subtitle->codec = WORK_DECVOBSUB; subtitle->type = lang_extension; memcpy( subtitle->palette, ifo->vts_pgcit->pgci_srp[title_pgcn-1].pgc->palette, 16 * sizeof( uint32_t ) ); + subtitle->palette_set = 1; switch( lang_extension ) { diff --git a/libhb/encavcodec.c b/libhb/encavcodec.c index 4a44d4343..089639d6d 100644 --- a/libhb/encavcodec.c +++ b/libhb/encavcodec.c @@ -164,7 +164,6 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job ) lavc_opts = hb_encopts_to_dict( job->advanced_opts, job->vcodec ); } /* iterate through lavc_opts and have avutil parse the options for us */ - int ret; AVDictionary * av_opts = NULL; hb_dict_entry_t * entry = NULL; while( ( entry = hb_dict_next( lavc_opts, entry ) ) ) diff --git a/libhb/hb.c b/libhb/hb.c index 1dda37578..1b3b1c8b0 100644 --- a/libhb/hb.c +++ b/libhb/hb.c @@ -459,6 +459,7 @@ hb_handle_t * hb_init( int verbose, int update_check ) hb_register( &hb_decutf8sub ); hb_register( &hb_dectx3gsub ); hb_register( &hb_decssasub ); + hb_register( &hb_decpgssub ); hb_register( &hb_encavcodec ); hb_register( &hb_encx264 ); hb_register( &hb_enctheora ); @@ -1244,6 +1245,7 @@ void hb_add_filter( hb_job_t * job, hb_filter_object_t * filter, const char * se else if( f->id == filter->id ) { // Don't allow the same filter to be added twice + hb_filter_close( &filter ); return; } } @@ -1569,7 +1571,7 @@ void hb_add( hb_handle_t * h, hb_job_t * job ) { subtitle = hb_list_item( title->list_subtitle, i ); if( strcmp( subtitle->iso639_2, audio_lang ) == 0 && - subtitle->source == VOBSUB ) + hb_subtitle_can_force( subtitle->source ) ) { /* * Matched subtitle language with audio language, so diff --git a/libhb/hb_dict.c b/libhb/hb_dict.c index 31b5492ad..5912139d5 100644 --- a/libhb/hb_dict.c +++ b/libhb/hb_dict.c @@ -155,7 +155,8 @@ hb_dict_t * hb_encopts_to_dict( const char * encopts, int encoder ) hb_dict_t * dict = NULL; if( encopts && *encopts ) { - char *cur_opt, *opts_start, *name, *value; + char *cur_opt, *opts_start, *value; + const char *name; dict = hb_dict_init( 10 ); if( !dict ) return NULL; diff --git a/libhb/internal.h b/libhb/internal.h index 0ae63924a..0d6ee2776 100644 --- a/libhb/internal.h +++ b/libhb/internal.h @@ -395,7 +395,8 @@ enum WORK_ENC_CA_HAAC, WORK_ENCAVCODEC_AUDIO, WORK_MUX, - WORK_READER + WORK_READER, + WORK_DECPGSSUB }; extern hb_filter_object_t hb_filter_detelecine; diff --git a/libhb/muxmkv.c b/libhb/muxmkv.c index 1bed15c49..1140bc5b0 100644 --- a/libhb/muxmkv.c +++ b/libhb/muxmkv.c @@ -322,9 +322,9 @@ static int MKVInit( hb_mux_object_t * m ) continue; memset(track, 0, sizeof(mk_TrackConfig)); - switch (subtitle->format) + switch (subtitle->source) { - case PICTURESUB: + case VOBSUB: track->codecID = MK_SUBTITLE_VOBSUB; for (j = 0; j < 16; j++) rgb[j] = hb_yuv2rgb(subtitle->palette[j]); @@ -338,16 +338,20 @@ static int MKVInit( hb_mux_object_t * m ) track->codecPrivate = subidx; track->codecPrivateSize = len + 1; break; - case TEXTSUB: - if (subtitle->source == SSASUB) - { - track->codecID = MK_SUBTITLE_SSA; - need_fonts = 1; - track->codecPrivate = subtitle->extradata; - track->codecPrivateSize = subtitle->extradata_size; - } - else - track->codecID = MK_SUBTITLE_UTF8; + case PGSSUB: + track->codecID = "S_HDMV/PGS"; + break; + case SSASUB: + track->codecID = MK_SUBTITLE_SSA; + need_fonts = 1; + track->codecPrivate = subtitle->extradata; + track->codecPrivateSize = subtitle->extradata_size; + break; + case CC608SUB: + case CC708SUB: + case UTF8SUB: + case TX3GSUB: + track->codecID = MK_SUBTITLE_UTF8; break; default: continue; diff --git a/libhb/ports.c b/libhb/ports.c index bd3564be3..6ccbaede3 100644 --- a/libhb/ports.c +++ b/libhb/ports.c @@ -611,9 +611,7 @@ void hb_cond_wait( hb_cond_t * c, hb_lock_t * lock ) void hb_clock_gettime( struct timespec *tp ) { struct timeval tv; - time_t sec; - sec = time( NULL ); gettimeofday( &tv, NULL ); tp->tv_sec = tv.tv_sec; tp->tv_nsec = tv.tv_usec * 1000; diff --git a/libhb/rendersub.c b/libhb/rendersub.c index 8707bd3d2..40eaba5fe 100644 --- a/libhb/rendersub.c +++ b/libhb/rendersub.c @@ -38,6 +38,16 @@ static int ssa_work( hb_filter_object_t * filter, static void ssa_close( hb_filter_object_t * filter ); +// PGS +static int pgssub_init ( hb_filter_object_t * filter, hb_filter_init_t * init ); + +static int pgssub_work ( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); + +static void pgssub_close( hb_filter_object_t * filter ); + + // Entry points static int hb_rendersub_init( hb_filter_object_t * filter, hb_filter_init_t * init ); @@ -52,6 +62,7 @@ hb_filter_object_t hb_filter_render_sub = { .id = HB_FILTER_RENDER_SUB, .enforce_order = 1, + .init_index = 1, .name = "Subtitle renderer", .settings = NULL, .init = hb_rendersub_init, @@ -80,16 +91,15 @@ static void blend( hb_buffer_t *dst, hb_buffer_t *src, int left, int top ) } ww = src->f.width; - if( left + src->f.width > dst->f.width ) + if( src->f.width - x0 > dst->f.width - left ) { - ww = dst->f.width - ( left + src->f.width ); + ww = dst->f.width - left + x0; } hh = src->f.height; - if( top + src->f.height > dst->f.height ) + if( src->f.height - y0 > dst->f.height - top ) { - hh = dst->f.height - ( top + src->f.height ); + hh = dst->f.height - top + y0; } - // Blend luma for( yy = y0; yy < hh; yy++ ) { @@ -116,6 +126,7 @@ static void blend( hb_buffer_t *dst, hb_buffer_t *src, int left, int top ) hshift = 1; if( dst->plane[1].width < dst->plane[0].width ) wshift = 1; + for( yy = y0 >> hshift; yy < hh >> hshift; yy++ ) { u_in = src->plane[1].data + yy * src->plane[1].stride; @@ -224,13 +235,14 @@ static void ApplyVOBSubs( hb_filter_private_t * pv, hb_buffer_t * buf ) int ii; hb_buffer_t * sub; - for( ii = 0; ii < hb_list_count( pv->sub_list ); ii++ ) + for( ii = 0; ii < hb_list_count( pv->sub_list ); ) { sub = hb_list_item( pv->sub_list, ii ); if( sub->s.stop <= buf->s.start ) { // Subtitle stop is in the past, delete it hb_list_rem( pv->sub_list, sub ); + hb_buffer_close( &sub ); } else if( sub->s.start <= buf->s.start ) { @@ -241,6 +253,7 @@ static void ApplyVOBSubs( hb_filter_private_t * pv, hb_buffer_t * buf ) ApplySub( pv, buf, sub ); sub = sub->next; } + ii++; } else { @@ -530,6 +543,116 @@ static int ssa_work( hb_filter_object_t * filter, return HB_FILTER_OK; } +static void ApplyPGSSubs( hb_filter_private_t * pv, hb_buffer_t * buf ) +{ + int index; + hb_buffer_t * old_sub; + hb_buffer_t * sub; + + // Each PGS subtitle supersedes anything that preceded it. + // Find the active subtitle (if there is one), and delete + // everything before it. + for( index = hb_list_count( pv->sub_list ) - 1; index > 0; index-- ) + { + sub = hb_list_item( pv->sub_list, index); + if ( sub->s.start <= buf->s.start ) + { + while ( index > 0 ) + { + old_sub = hb_list_item( pv->sub_list, index - 1); + hb_list_rem( pv->sub_list, old_sub ); + hb_buffer_close( &old_sub ); + index--; + } + } + } + + // Some PGS subtitles have no content and only serve to clear + // the screen. If any of these are at the front of our list, + // we can now get rid of them. + while ( hb_list_count( pv->sub_list ) > 0 ) + { + sub = hb_list_item( pv->sub_list, 0 ); + if (sub->f.width != 0 && sub->f.height != 0) + break; + + hb_list_rem( pv->sub_list, sub ); + hb_buffer_close( &sub ); + } + + // Check to see if there's an active subtitle, and apply it. + if ( hb_list_count( pv->sub_list ) > 0) + { + sub = hb_list_item( pv->sub_list, 0 ); + if ( sub->s.start <= buf->s.start ) + { + while ( sub ) + { + ApplySub( pv, buf, sub ); + sub = sub->sub; + } + } + } +} + +static int pgssub_init( hb_filter_object_t * filter, + hb_filter_init_t * init ) +{ + hb_filter_private_t * pv = filter->private_data; + + // PGS render filter has no settings + memcpy( pv->crop, init->crop, sizeof( int[4] ) ); + + pv->sub_list = hb_list_init(); + + return 0; +} + +static void pgssub_close( hb_filter_object_t * filter ) +{ + hb_filter_private_t * pv = filter->private_data; + + if ( !pv ) + { + return; + } + + if ( pv->sub_list ) + hb_list_empty( &pv->sub_list ); + + free( pv ); + filter->private_data = NULL; +} + +static int pgssub_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out) +{ + hb_filter_private_t * pv = filter->private_data; + hb_buffer_t * in = *buf_in; + hb_buffer_t * sub; + + if ( in->size <= 0 ) + { + *buf_in = NULL; + *buf_out = in; + return HB_FILTER_DONE; + } + + // Get any pending subtitles and add them to the active + // subtitle list + while ( ( sub = hb_fifo_get( filter->subtitle->fifo_out ) ) ) + { + hb_list_add( pv->sub_list, sub ); + } + + ApplyPGSSubs( pv, in ); + *buf_in = NULL; + *buf_out = in; + + return HB_FILTER_OK; +} + static int hb_rendersub_init( hb_filter_object_t * filter, hb_filter_init_t * init ) { @@ -568,6 +691,11 @@ static int hb_rendersub_init( hb_filter_object_t * filter, return ssa_init( filter, init ); } break; + case PGSSUB: + { + return pgssub_init( filter, init ); + } break; + default: { hb_log("rendersub: unsupported subtitle format %d", pv->type ); @@ -593,6 +721,11 @@ static int hb_rendersub_work( hb_filter_object_t * filter, return ssa_work( filter, buf_in, buf_out ); } break; + case PGSSUB: + { + return pgssub_work( filter, buf_in, buf_out ); + } break; + default: { hb_error("rendersub: unsupported subtitle format %d", pv->type ); @@ -616,6 +749,11 @@ static void hb_rendersub_close( hb_filter_object_t * filter ) ssa_close( filter ); } break; + case PGSSUB: + { + pgssub_close( filter ); + } break; + default: { hb_error("rendersub: unsupported subtitle format %d", pv->type ); diff --git a/libhb/rotate.c b/libhb/rotate.c index 5f9558764..a96e6927d 100644 --- a/libhb/rotate.c +++ b/libhb/rotate.c @@ -46,6 +46,7 @@ hb_filter_object_t hb_filter_rotate = { .id = HB_FILTER_ROTATE, .enforce_order = 0, + .init_index = 2, .name = "Rotate (rotate & flip image axes)", .settings = NULL, .init = hb_rotate_init, diff --git a/libhb/scan.c b/libhb/scan.c index 1ad8659b7..a3e6e1832 100644 --- a/libhb/scan.c +++ b/libhb/scan.c @@ -237,7 +237,7 @@ static void ScanFunc( void * _data ) for( j = 0; j < hb_list_count( title->list_subtitle ); j++ ) { hb_subtitle_t *subtitle = hb_list_item( title->list_subtitle, j ); - if ( subtitle->source == VOBSUB ) + if ( subtitle->source == VOBSUB || subtitle->source == PGSSUB ) { subtitle->width = title->width; subtitle->height = title->height; diff --git a/libhb/stream.c b/libhb/stream.c index 2c7c4f9a5..7961eebfb 100644 --- a/libhb/stream.c +++ b/libhb/stream.c @@ -30,9 +30,10 @@ * U - Unknown (to be determined by further processing) * A - Audio * V - Video + * S - Subtitle * P - PCR */ -typedef enum { N, U, A, V, P } kind_t; +typedef enum { N, U, A, V, P, S } kind_t; typedef struct { kind_t kind; /* not handled / unknown / audio / video */ int codec; /* HB worker object id of codec */ @@ -82,7 +83,7 @@ static const stream2codec_t st2codec[256] = { st(0x8a, A, HB_ACODEC_DCA, 0, "DTS"), - st(0x90, N, 0, 0, "PGS Subtitle"), + st(0x90, S, WORK_DECPGSSUB, 0, "PGS Subtitle"), // 0x91 can be AC3 or BD Interactive Graphics Stream. st(0x91, U, 0, 0, "AC3/IGS"), st(0x92, N, 0, 0, "Subtitle"), @@ -253,6 +254,7 @@ static int64_t pes_timestamp( const uint8_t *pes ); static int hb_ts_stream_init(hb_stream_t *stream); static hb_buffer_t * hb_ts_stream_decode(hb_stream_t *stream); static void hb_init_audio_list(hb_stream_t *stream, hb_title_t *title); +static void hb_init_subtitle_list(hb_stream_t *stream, hb_title_t *title); static int hb_ts_stream_find_pids(hb_stream_t *stream); static void hb_ps_stream_init(hb_stream_t *stream); @@ -947,6 +949,15 @@ hb_stream_t * hb_bd_stream_open( hb_title_t *title ) update_ts_streams( d, pid, stream_id_ext, stream_type, A, NULL ); } + hb_subtitle_t * subtitle; + for ( ii = 0; ( subtitle = hb_list_item( title->list_subtitle, ii ) ); ++ii ) + { + pid = subtitle->id & 0xFFFF; + stream_type = subtitle->stream_type; + + update_ts_streams( d, pid, 0, stream_type, S, NULL ); + } + // When scanning, title->job == NULL. We don't need to wait for // a PCR when scanning. In fact, it trips us up on the first // preview of every title since we would have to read quite a @@ -1035,6 +1046,7 @@ hb_title_t * hb_stream_title_scan(hb_stream_t *stream, hb_title_t * title) // - For program streams read the first 4MB and take every unique // audio stream we find. hb_init_audio_list(stream, title); + hb_init_subtitle_list(stream, title); // set the video id, codec & muxer int idx = pes_index_of_video( stream ); @@ -2024,6 +2036,119 @@ static void set_audio_description( // Sort specifies the index in the audio list where you would // like sorted items to begin. +static void pes_add_subtitle_to_title( + hb_stream_t *stream, + int idx, + hb_title_t *title, + int sort) +{ + hb_pes_stream_t *pes = &stream->pes.list[idx]; + + // Sort by id when adding to the list + // This assures that they are always displayed in the same order + int id = get_id( pes ); + int i; + hb_subtitle_t *tmp = NULL; + + int count = hb_list_count( title->list_subtitle ); + + // Don't add the same audio twice. Search for audio. + for ( i = 0; i < count; i++ ) + { + tmp = hb_list_item( title->list_subtitle, i ); + if ( id == tmp->id ) + return; + } + + hb_subtitle_t *subtitle = calloc( sizeof( hb_subtitle_t ), 1 ); + iso639_lang_t * lang; + + subtitle->track = idx; + subtitle->id = id; + lang = lang_for_code( pes->lang_code ); + snprintf( subtitle->lang, sizeof( subtitle->lang ), "%s", + strlen(lang->native_name) ? lang->native_name : lang->eng_name); + snprintf( subtitle->iso639_2, sizeof( subtitle->iso639_2 ), "%s", + lang->iso639_2); + + switch ( pes->codec ) + { + case WORK_DECPGSSUB: + subtitle->source = PGSSUB; + subtitle->format = PICTURESUB; + subtitle->config.dest = RENDERSUB; + break; + case WORK_DECVOBSUB: + subtitle->source = VOBSUB; + subtitle->format = PICTURESUB; + subtitle->config.dest = RENDERSUB; + break; + default: + // Unrecognized, don't add to list + hb_log("unregonized subtitle!"); + free( subtitle ); + return; + } + subtitle->reg_desc = stream->reg_desc; + subtitle->stream_type = pes->stream_type; + subtitle->substream_type = pes->stream_id_ext; + subtitle->codec = pes->codec; + + // Create a default palette since vob files do not include the + // vobsub palette. + if ( subtitle->source == VOBSUB ) + { + subtitle->palette[0] = 0x108080; + subtitle->palette[1] = 0x108080; + subtitle->palette[2] = 0x108080; + subtitle->palette[3] = 0xbff000; + + subtitle->palette[4] = 0xbff000; + subtitle->palette[5] = 0x108080; + subtitle->palette[6] = 0x108080; + subtitle->palette[7] = 0x108080; + + subtitle->palette[8] = 0xbff000; + subtitle->palette[9] = 0x108080; + subtitle->palette[10] = 0x108080; + subtitle->palette[11] = 0x108080; + + subtitle->palette[12] = 0x108080; + subtitle->palette[13] = 0xbff000; + subtitle->palette[14] = 0x108080; + subtitle->palette[15] = 0x108080; + } + + hb_log("stream id 0x%x (type 0x%x substream 0x%x) subtitle 0x%x", + pes->stream_id, pes->stream_type, pes->stream_id_ext, subtitle->id); + + // Search for the sort position + if ( sort >= 0 ) + { + sort = sort < count ? sort : count; + for ( i = sort; i < count; i++ ) + { + tmp = hb_list_item( title->list_subtitle, i ); + int sid = tmp->id & 0xffff; + int ssid = tmp->id >> 16; + if ( pes->stream_id < sid ) + break; + else if ( pes->stream_id <= sid && + pes->stream_id_ext <= ssid ) + { + break; + } + } + hb_list_insert( title->list_subtitle, i, subtitle ); + } + else + { + hb_list_add( title->list_subtitle, subtitle ); + } +} + +// Sort specifies the index in the audio list where you would +// like sorted items to begin. static void pes_add_audio_to_title( hb_stream_t *stream, int idx, @@ -2090,6 +2215,42 @@ static void pes_add_audio_to_title( } } +static void hb_init_subtitle_list(hb_stream_t *stream, hb_title_t *title) +{ + int ii; + int map_idx; + int largest = -1; + + // First add all that were found in a map. + for ( map_idx = 0; 1; map_idx++ ) + { + for ( ii = 0; ii < stream->pes.count; ii++ ) + { + if ( stream->pes.list[ii].stream_kind == S ) + { + if ( stream->pes.list[ii].map_idx == map_idx ) + { + pes_add_subtitle_to_title( stream, ii, title, -1 ); + } + if ( stream->pes.list[ii].map_idx > largest ) + largest = stream->pes.list[ii].map_idx; + } + } + if ( map_idx > largest ) + break; + } + + int count = hb_list_count( title->list_audio ); + // Now add the reset. Sort them by stream id. + for ( ii = 0; ii < stream->pes.count; ii++ ) + { + if ( stream->pes.list[ii].stream_kind == S ) + { + pes_add_subtitle_to_title( stream, ii, title, count ); + } + } +} + static void hb_init_audio_list(hb_stream_t *stream, hb_title_t *title) { int ii; @@ -2206,6 +2367,19 @@ static int hb_ts_stream_init(hb_stream_t *stream) stream->ts.list[i].is_pcr ? " (PCR)" : ""); } } + hb_log(" Subtitle PIDS : "); + for (i = 0; i < stream->ts.count; i++) + { + if ( ts_stream_kind( stream, i ) == S ) + { + hb_log( " 0x%x type %s (0x%x)%s", + stream->ts.list[i].pid, + stream_type_name2(stream, + &stream->pes.list[stream->ts.list[i].pes_list]), + ts_stream_type( stream, i ), + stream->ts.list[i].is_pcr ? " (PCR)" : ""); + } + } hb_log(" Other PIDS : "); for (i = 0; i < stream->ts.count; i++) { @@ -2281,6 +2455,19 @@ static void hb_ps_stream_init(hb_stream_t *stream) stream->pes.list[i].stream_type ); } } + hb_log(" Subtitle Streams : "); + for (i = 0; i < stream->pes.count; i++) + { + if ( stream->pes.list[i].stream_kind == S ) + { + hb_log( " 0x%x-0x%x type %s (0x%x)", + stream->pes.list[i].stream_id, + stream->pes.list[i].stream_id_ext, + stream_type_name2(stream, + &stream->pes.list[i]), + stream->pes.list[i].stream_type ); + } + } hb_log(" Other Streams : "); for (i = 0; i < stream->pes.count; i++) { @@ -3728,10 +3915,10 @@ static void hb_ps_stream_find_streams(hb_stream_t *stream) // Check dvd substream id if ( ssid >= 0x20 && ssid <= 0x37 ) { - // Skip dvd subtitles int idx = update_ps_streams( stream, pes_info.stream_id, pes_info.bd_substream_id, 0, -1 ); - stream->pes.list[idx].stream_kind = N; + stream->pes.list[idx].stream_kind = S; + stream->pes.list[idx].codec = WORK_DECVOBSUB; strncpy(stream->pes.list[idx].codec_name, "DVD Subtitle", 80); continue; @@ -4122,6 +4309,7 @@ static void hb_ts_resolve_pid_types(hb_stream_t *stream) // stype == 0 indicates a type not in st2codec table if ( stype != 0 && ( ts_stream_kind( stream, ii ) == A || + ts_stream_kind( stream, ii ) == S || ts_stream_kind( stream, ii ) == V ) ) { // Assuming there are no substreams. @@ -4203,6 +4391,7 @@ static void hb_ps_resolve_stream_types(hb_stream_t *stream) // stype == 0 indicates a type not in st2codec table if ( stype != 0 && ( stream->pes.list[ii].stream_kind == A || + stream->pes.list[ii].stream_kind == S || stream->pes.list[ii].stream_kind == V ) ) { stream->pes.list[ii].codec = st2codec[stype].codec; @@ -5049,6 +5238,7 @@ static int ffmpeg_parse_vobsub_extradata_mkv( AVCodecContext *codec, hb_subtitle int i; for (i=0; i<16; i++) subtitle->palette[i] = hb_rgb2yuv(rgb[i]); + subtitle->palette_set = 1; return 0; } else @@ -5072,6 +5262,7 @@ static int ffmpeg_parse_vobsub_extradata_mp4( AVCodecContext *codec, hb_subtitle codec->extradata[j+1] << 16 | // Y codec->extradata[j+2] << 8 | // Cb codec->extradata[j+3] << 0; // Cr + subtitle->palette_set = 1; } if (codec->width <= 0 || codec->height <= 0) { @@ -5113,6 +5304,7 @@ static void add_ffmpeg_subtitle( hb_title_t *title, hb_stream_t *stream, int id subtitle->format = PICTURESUB; subtitle->source = VOBSUB; subtitle->config.dest = RENDERSUB; // By default render (burn-in) the VOBSUB. + subtitle->codec = WORK_DECVOBSUB; if ( ffmpeg_parse_vobsub_extradata( codec, subtitle ) ) hb_log( "add_ffmpeg_subtitle: malformed extradata for VOB subtitle track; " "subtitle colors likely to be wrong" ); @@ -5121,16 +5313,25 @@ static void add_ffmpeg_subtitle( hb_title_t *title, hb_stream_t *stream, int id subtitle->format = TEXTSUB; subtitle->source = UTF8SUB; subtitle->config.dest = PASSTHRUSUB; + subtitle->codec = WORK_DECUTF8SUB; break; case CODEC_ID_MOV_TEXT: // TX3G subtitle->format = TEXTSUB; subtitle->source = TX3GSUB; subtitle->config.dest = PASSTHRUSUB; + subtitle->codec = WORK_DECTX3GSUB; break; case CODEC_ID_SSA: subtitle->format = TEXTSUB; subtitle->source = SSASUB; subtitle->config.dest = PASSTHRUSUB; + subtitle->codec = WORK_DECSSASUB; + break; + case CODEC_ID_HDMV_PGS_SUBTITLE: + subtitle->format = PICTURESUB; + subtitle->source = PGSSUB; + subtitle->config.dest = RENDERSUB; + subtitle->codec = WORK_DECPGSSUB; break; default: hb_log( "add_ffmpeg_subtitle: unknown subtitle stream type: 0x%x", (int) codec->codec_id ); diff --git a/libhb/sync.c b/libhb/sync.c index c9a4eebe6..c76cc45d2 100644 --- a/libhb/sync.c +++ b/libhb/sync.c @@ -69,10 +69,6 @@ typedef struct uint64_t st_counts[4]; uint64_t st_dates[4]; uint64_t st_first; - - /* Subtitles */ - hb_buffer_t * sub_list; /* list of subtitles to be passed thru or rendered */ - hb_buffer_t * sub_tail; /* list of subtitles to be passed thru or rendered */ } hb_sync_video_t; struct hb_work_private_s diff --git a/libhb/vfr.c b/libhb/vfr.c index 9c3e2e2a8..7c70c21d4 100644 --- a/libhb/vfr.c +++ b/libhb/vfr.c @@ -46,6 +46,7 @@ hb_filter_object_t hb_filter_vfr = { .id = HB_FILTER_VFR, .enforce_order = 1, + .init_index = 0, .name = "Framerate Shaper", .settings = NULL, .init = hb_vfr_init, diff --git a/libhb/work.c b/libhb/work.c index f9d7bf656..eab4db821 100644 --- a/libhb/work.c +++ b/libhb/work.c @@ -390,17 +390,20 @@ void hb_display_job_info( hb_job_t * job ) { /* For SRT, print offset and charset too */ hb_log( " * subtitle track %i, %s (id 0x%x) %s [%s] -> %s%s, offset: %"PRId64", charset: %s", - subtitle->track, subtitle->lang, subtitle->id, "Text", "SRT", "Pass-Through", + subtitle->out_track, subtitle->lang, subtitle->id, + "Text", "SRT", "Pass-Through", subtitle->config.default_track ? ", Default" : "", subtitle->config.offset, subtitle->config.src_codeset ); } else { - hb_log( " * subtitle track %i, %s (id 0x%x) %s [%s] -> %s%s%s", subtitle->track, subtitle->lang, subtitle->id, + hb_log( " * subtitle track %i, %s (id 0x%x) %s [%s] -> %s%s%s", + subtitle->out_track, subtitle->lang, subtitle->id, subtitle->format == PICTURESUB ? "Picture" : "Text", hb_subsource_name( subtitle->source ), job->indepth_scan ? "Foreign Audio Search" : - subtitle->config.dest == RENDERSUB ? "Render/Burn in" : "Pass-Through", + subtitle->config.dest == RENDERSUB ? + "Render/Burn in" : "Pass-Through", subtitle->config.force ? ", Forced Only" : "", subtitle->config.default_track ? ", Default" : "" ); } @@ -489,6 +492,46 @@ void correct_framerate( hb_job_t * job ) interjob->vrate_base = job->vrate_base; } +void hb_filter_init_next( hb_list_t * list, int *score, int *ret_pos ) +{ + int count = hb_list_count( list ); + int pos = *ret_pos + 1; + int ii = 0; + if ( pos == 0 ) + { + hb_filter_object_t * filter = hb_list_item( list, pos ); + if ( filter->init_index == *score ) + { + *ret_pos = pos; + return; + } + pos++; + } + while (1) + { + if ( pos == count ) + { + pos = 0; + (*score)++; + } + hb_filter_object_t * filter = hb_list_item( list, pos ); + if ( filter->init_index == *score ) + { + *ret_pos = pos; + return; + } + pos++; + ii++; + if (ii > count) + { + // This is an error that should never happen + hb_error("internal error during filter initialization"); + *ret_pos = -1; + return; + } + } +} + /** * Job initialization rountine. * Initializes fifos. @@ -529,11 +572,139 @@ static void do_job( hb_job_t * job ) hb_log( "starting job" ); + /* + * Look for the scanned subtitle in the existing subtitle list + * select_subtitle implies that we did a scan. + */ + if ( !job->indepth_scan && interjob->select_subtitle && + ( job->pass == 0 || job->pass == 2 ) ) + { + /* + * Disable forced subtitles if we didn't find any in the scan + * so that we display normal subtitles instead. + */ + if( interjob->select_subtitle->config.force && + interjob->select_subtitle->forced_hits == 0 ) + { + interjob->select_subtitle->config.force = 0; + } + for( i=0; i < hb_list_count(title->list_subtitle); i++ ) + { + subtitle = hb_list_item( title->list_subtitle, i ); + + if( subtitle ) + { + /* + * Remove the scanned subtitle from the subtitle list if + * it would result in an identical duplicate subtitle track + * or an emty track (forced and no forced hits). + */ + if( ( interjob->select_subtitle->id == subtitle->id ) && + ( ( interjob->select_subtitle->forced_hits == 0 && + subtitle->config.force ) || + ( subtitle->config.force == interjob->select_subtitle->config.force ) ) ) + { + *subtitle = *(interjob->select_subtitle); + free( interjob->select_subtitle ); + interjob->select_subtitle = NULL; + break; + } + } + } + + if( interjob->select_subtitle ) + { + /* + * Its not in the existing list + * + * Must be second pass of a two pass with subtitle scan enabled, so + * add the subtitle that we found on the first pass for use in this + * pass. + */ + hb_list_add( title->list_subtitle, interjob->select_subtitle ); + interjob->select_subtitle = NULL; + } + } + + if ( !job->indepth_scan ) + { + // Sanitize subtitles + uint8_t one_burned = 0; + for( i=0; i < hb_list_count(title->list_subtitle); ) + { + subtitle = hb_list_item( title->list_subtitle, i ); + + if ( subtitle->config.dest == RENDERSUB ) + { + if ( one_burned ) + { + if ( !hb_subtitle_can_pass(subtitle->source, job->mux) ) + { + hb_log( "More than one subtitle burn-in requested, dropping track %d.", i ); + hb_list_rem( title->list_subtitle, subtitle ); + free( subtitle ); + continue; + } + else + { + hb_log( "More than one subtitle burn-in requested. Changing track %d to soft subtitle.", i ); + subtitle->config.dest = PASSTHRUSUB; + } + } + else if ( !hb_subtitle_can_burn(subtitle->source) ) + { + hb_log( "Subtitle burn-in requested and input track can not be rendered. Changing track %d to soft subtitle.", i ); + subtitle->config.dest = PASSTHRUSUB; + } + else + { + one_burned = 1; + } + } + + if ( subtitle->config.dest == PASSTHRUSUB && + !hb_subtitle_can_pass(subtitle->source, job->mux) ) + { + if ( !one_burned ) + { + hb_log( "Subtitle pass-thru requested and input track is not compatible with container. Changing track %d to burned-in subtitle.", i ); + subtitle->config.dest = RENDERSUB; + subtitle->config.default_track = 0; + one_burned = 1; + } + else + { + hb_log( "Subtitle pass-thru requested and input track is not compatible with container. One track already burned, dropping track %d.", i ); + hb_list_rem( title->list_subtitle, subtitle ); + free( subtitle ); + continue; + } + } + /* Adjust output track number, in case we removed one. + * Output tracks sadly still need to be in sequential order. + * Note: out.track starts at 1, i starts at 0 */ + subtitle->out_track = ++i; + } + if ( one_burned ) + { + hb_filter_object_t * filter; + + // Add subtitle rendering filter + // Note that if the filter is already in the filter chain, this + // has no effect. Note also that this means the front-end is + // not required to add the subtitle rendering filter since + // we will always try to do it here. + filter = hb_filter_init(HB_FILTER_RENDER_SUB); + hb_add_filter( job, filter, NULL ); + } + } + // Filters have an effect on settings. // So initialize the filters and update the job. if( job->list_filter && hb_list_count( job->list_filter ) ) { hb_filter_init_t init; + int pos = -1, score = 0; init.job = job; init.pix_fmt = PIX_FMT_YUV420P; @@ -547,7 +718,19 @@ static void do_job( hb_job_t * job ) init.cfr = 0; for( i = 0; i < hb_list_count( job->list_filter ); i++ ) { - hb_filter_object_t * filter = hb_list_item( job->list_filter, i ); + // Some filters need to be initialized in a different order + // than they are executed in the pipeline. e.g. rendersub + // needs to be initialized after cropscale so that it knows + // the crop settings, but it needs to be executed before + // cropscale. so hb_filter_init_next() finds returns the + // position of filters in initialization order. + hb_filter_init_next( job->list_filter, &score, &pos ); + if( pos == -1) + { + // This is an error that should never happen! + break; + } + hb_filter_object_t * filter = hb_list_item( job->list_filter, pos ); if( filter->init( filter, &init ) ) { hb_log( "Failure to initialise filter '%s', disabling", @@ -847,60 +1030,6 @@ static void do_job( hb_job_t * job ) w->fifo_in = job->fifo_mpeg2; w->fifo_out = job->fifo_raw; - /* - * Look for the scanned subtitle in the existing subtitle list - * select_subtitle implies that we did a scan. - */ - if ( !job->indepth_scan && interjob->select_subtitle && - ( job->pass == 0 || job->pass == 2 ) ) - { - /* - * Disable forced subtitles if we didn't find any in the scan - * so that we display normal subtitles instead. - */ - if( interjob->select_subtitle->config.force && - interjob->select_subtitle->forced_hits == 0 ) - { - interjob->select_subtitle->config.force = 0; - } - for( i=0; i < hb_list_count(title->list_subtitle); i++ ) - { - subtitle = hb_list_item( title->list_subtitle, i ); - - if( subtitle ) - { - /* - * Remove the scanned subtitle from the subtitle list if - * it would result in an identical duplicate subtitle track - * or an emty track (forced and no forced hits). - */ - if( ( interjob->select_subtitle->id == subtitle->id ) && - ( ( interjob->select_subtitle->forced_hits == 0 && - subtitle->config.force ) || - ( subtitle->config.force == interjob->select_subtitle->config.force ) ) ) - { - *subtitle = *(interjob->select_subtitle); - free( interjob->select_subtitle ); - interjob->select_subtitle = NULL; - break; - } - } - } - - if( interjob->select_subtitle ) - { - /* - * Its not in the existing list - * - * Must be second pass of a two pass with subtitle scan enabled, so - * add the subtitle that we found on the first pass for use in this - * pass. - */ - hb_list_add( title->list_subtitle, interjob->select_subtitle ); - interjob->select_subtitle = NULL; - } - } - for( i=0; i < hb_list_count(title->list_subtitle); i++ ) { subtitle = hb_list_item( title->list_subtitle, i ); @@ -920,60 +1049,17 @@ static void do_job( hb_job_t * job ) subtitle->fifo_sync = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); subtitle->fifo_out = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE ); - if( (!job->indepth_scan || job->select_subtitle_config.force) && - subtitle->source == VOBSUB ) { - /* - * Don't add threads for subtitles when we are scanning, unless - * looking for forced subtitles. - */ - w = hb_get_work( WORK_DECVOBSUB ); - w->fifo_in = subtitle->fifo_in; - w->fifo_out = subtitle->fifo_raw; - w->subtitle = subtitle; - hb_list_add( job->list_work, w ); - } - - if( !job->indepth_scan && subtitle->source == CC608SUB ) - { - w = hb_get_work( WORK_DECCC608 ); - w->fifo_in = subtitle->fifo_in; - w->fifo_out = subtitle->fifo_raw; - hb_list_add( job->list_work, w ); - } - - if( !job->indepth_scan && subtitle->source == SRTSUB ) - { - w = hb_get_work( WORK_DECSRTSUB ); - w->fifo_in = subtitle->fifo_in; - w->fifo_out = subtitle->fifo_raw; - w->subtitle = subtitle; - hb_list_add( job->list_work, w ); - } - - if( !job->indepth_scan && subtitle->source == UTF8SUB ) - { - w = hb_get_work( WORK_DECUTF8SUB ); - w->fifo_in = subtitle->fifo_in; - w->fifo_out = subtitle->fifo_raw; - hb_list_add( job->list_work, w ); - } - - if( !job->indepth_scan && subtitle->source == TX3GSUB ) + if( job->indepth_scan && + !( job->select_subtitle_config.force && + hb_subtitle_can_force( subtitle->source ) ) ) { - w = hb_get_work( WORK_DECTX3GSUB ); - w->fifo_in = subtitle->fifo_in; - w->fifo_out = subtitle->fifo_raw; - hb_list_add( job->list_work, w ); - } - - if( !job->indepth_scan && subtitle->source == SSASUB ) - { - w = hb_get_work( WORK_DECSSASUB ); - w->fifo_in = subtitle->fifo_in; - w->fifo_out = subtitle->fifo_raw; - w->subtitle = subtitle; - hb_list_add( job->list_work, w ); + continue; } + w = hb_get_work( subtitle->codec ); + w->fifo_in = subtitle->fifo_in; + w->fifo_out = subtitle->fifo_raw; + w->subtitle = subtitle; + hb_list_add( job->list_work, w ); } } diff --git a/macosx/Controller.m b/macosx/Controller.m index 53a64b9d6..88a146ae0 100644 --- a/macosx/Controller.m +++ b/macosx/Controller.m @@ -3185,28 +3185,6 @@ bool one_burned = FALSE; } else { - - /* for the actual source tracks, we must subtract the non source entries so - * that the menu index matches the source subtitle_list index for convenience */ - if (i == 0) - { - /* for the first track, the source tracks start at menu index 2 ( None is 0, - * Foreign Language Search is 1) so subtract 2 */ - subtitle = subtitle - 2; - } - else - { - /* for all other tracks, the source tracks start at menu index 1 (None is 0) - * so subtract 1. */ - - subtitle = subtitle - 1; - } - - /* We are setting a source subtitle so access the source subtitle info */ - hb_subtitle_t * subt; - - subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle); - /* if we are getting the subtitles from an external srt file */ if ([[tempObject objectForKey:@"subtitleSourceTrackType"] isEqualToString:@"SRT"]) { @@ -3225,35 +3203,48 @@ bool one_burned = FALSE; sub_config.default_track = def; hb_srt_add( job, &sub_config, [[tempObject objectForKey:@"subtitleTrackSrtLanguageIso3"] UTF8String]); + continue; } - if (subt != NULL) + /* for the actual source tracks, we must subtract the non source entries so + * that the menu index matches the source subtitle_list index for convenience */ + if( i == 0 ) + { + /* for the first track, the source tracks start at menu index 2 ( None is 0, + * Foreign Language Search is 1) so subtract 2 */ + subtitle = subtitle - 2; + } + else + { + /* for all other tracks, the source tracks start at menu index 1 (None is 0) + * so subtract 1. */ + subtitle = subtitle - 1; + } + + /* We are setting a source subtitle so access the source subtitle info */ + hb_subtitle_t * subt = (hb_subtitle_t *) hb_list_item( title->list_subtitle, subtitle ); + + if( subt != NULL ) { hb_subtitle_config_t sub_config = subt->config; - if ( !burned && subt->format == PICTURESUB ) + if( !burned && hb_subtitle_can_pass( subt->source, job->mux ) ) { sub_config.dest = PASSTHRUSUB; } - else if ( burned && subt->format == PICTURESUB ) + else if( hb_subtitle_can_burn( subt->source ) ) { // Only allow one subtitle to be burned into the video - if (one_burned) + if( one_burned ) continue; one_burned = TRUE; - } - - /* Besides VOBSUBS we can also burn in SSA text subs */ - if (subt->source == SSASUB && burned) - { sub_config.dest = RENDERSUB; } sub_config.force = force; sub_config.default_track = def; hb_subtitle_add( job, &sub_config, subtitle ); - } - + } } } i++; @@ -3724,28 +3715,6 @@ bool one_burned = FALSE; } else { - - /* for the actual source tracks, we must subtract the non source entries so - * that the menu index matches the source subtitle_list index for convenience */ - if (i == 0) - { - /* for the first track, the source tracks start at menu index 2 ( None is 0, - * Foreign Language Search is 1) so subtract 2 */ - subtitle = subtitle - 2; - } - else - { - /* for all other tracks, the source tracks start at menu index 1 (None is 0) - * so subtract 1. */ - - subtitle = subtitle - 1; - } - - /* We are setting a source subtitle so access the source subtitle info */ - hb_subtitle_t * subt; - - subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle); - /* if we are getting the subtitles from an external srt file */ if ([[tempObject objectForKey:@"subtitleSourceTrackType"] isEqualToString:@"SRT"]) { @@ -3764,36 +3733,48 @@ bool one_burned = FALSE; sub_config.default_track = def; hb_srt_add( job, &sub_config, [[tempObject objectForKey:@"subtitleTrackSrtLanguageIso3"] UTF8String]); + continue; + } + + /* for the actual source tracks, we must subtract the non source entries so + * that the menu index matches the source subtitle_list index for convenience */ + if( i == 0 ) + { + /* for the first track, the source tracks start at menu index 2 ( None is 0, + * Foreign Language Search is 1) so subtract 2 */ + subtitle = subtitle - 2; + } + else + { + /* for all other tracks, the source tracks start at menu index 1 (None is 0) + * so subtract 1. */ + subtitle = subtitle - 1; } + /* We are setting a source subtitle so access the source subtitle info */ + hb_subtitle_t * subt = (hb_subtitle_t *) hb_list_item( title->list_subtitle, subtitle ); - if (subt != NULL) + if( subt != NULL ) { hb_subtitle_config_t sub_config = subt->config; - if ( !burned && subt->format == PICTURESUB ) + if( !burned && hb_subtitle_can_pass( subt->source, job->mux ) ) { sub_config.dest = PASSTHRUSUB; } - else if ( burned ) + else if( hb_subtitle_can_burn( subt->source ) ) { // Only allow one subtitle to be burned into the video - if (one_burned) + if( one_burned ) continue; one_burned = TRUE; - } - - /* Besides VOBSUBS we can also burn in SSA text subs */ - if (subt->source == SSASUB && burned) - { sub_config.dest = RENDERSUB; } - + sub_config.force = force; sub_config.default_track = def; hb_subtitle_add( job, &sub_config, subtitle ); - } - + } } } i++; diff --git a/macosx/HBSubtitles.m b/macosx/HBSubtitles.m index 1d0c45ae6..f84749303 100644 --- a/macosx/HBSubtitles.m +++ b/macosx/HBSubtitles.m @@ -3,18 +3,10 @@ This file is part of the HandBrake source code. Homepage: <http://handbrake.fr/>. It may be used under the terms of the GNU General Public License. */ -// #import "HBSubtitles.h" #include "hb.h" - - - - - - - @implementation HBSubtitles - (id)init { @@ -285,8 +277,8 @@ subtitle = (hb_subtitle_t *) hb_list_item( fTitle->list_subtitle, i ); sub_config = subtitle->config; - int canBeBurnedIn = subtitle->source == VOBSUB || subtitle->source == SSASUB; - int supportsForcedFlags = subtitle->source == VOBSUB; + int canBeBurnedIn = hb_subtitle_can_burn( subtitle->source ); + int supportsForcedFlags = hb_subtitle_can_force( subtitle->source ); /* create a dictionary of source subtitle information to store in our array */ NSMutableDictionary *newSubtitleSourceTrack = [[NSMutableDictionary alloc] init]; @@ -294,7 +286,7 @@ [newSubtitleSourceTrack setObject:[NSNumber numberWithInt:i] forKey:@"sourceTrackNum"]; /* Human-readable representation of subtitle->source */ NSString *subSourceName = [NSString stringWithUTF8String:hb_subsource_name( subtitle->source )]; - NSString *bitmapOrText = subtitle->source == PICTURESUB ? @"Bitmap" : @"Text"; + NSString *bitmapOrText = subtitle->format == PICTURESUB ? @"Bitmap" : @"Text"; /* Subtitle Source track name */ NSString *popupName = [NSString stringWithFormat:@"%d - %@ - (%@) (%@)",i,[NSString stringWithUTF8String:subtitle->lang],bitmapOrText,subSourceName]; [newSubtitleSourceTrack setObject:popupName forKey:@"sourceTrackName"]; @@ -769,7 +761,9 @@ */ if (container == HB_MUX_MP4 && [anObject intValue] != 0) { - if ([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackType"] isEqualToString:[NSString stringWithUTF8String:hb_subsource_name( VOBSUB )]]) + NSString *subtitleSourceTrackType = [[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackType"]; + if ([subtitleSourceTrackType isEqualToString:[NSString stringWithUTF8String:hb_subsource_name( VOBSUB )]] || + [subtitleSourceTrackType isEqualToString:[NSString stringWithUTF8String:hb_subsource_name( PGSSUB )]]) { /* lets see if there are currently any burned in subs specified */ NSEnumerator *enumerator = [subtitleArray objectEnumerator]; @@ -783,7 +777,7 @@ } } /* if we have no current vobsub set to burn it in ... burn it in by default */ - if(!subtrackBurnedInFound) + if (!subtrackBurnedInFound) { [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:1] forKey:@"subtitleTrackBurned"]; /* Burned In and Default are mutually exclusive */ diff --git a/test/test.c b/test/test.c index 504a3507d..758dd0fa2 100644 --- a/test/test.c +++ b/test/test.c @@ -2229,11 +2229,10 @@ static int HandleEvents( hb_handle_t * h ) force = test_sub_list(subforce, i+1); - int supports_burn = - ( subtitle->source == VOBSUB ) || - ( subtitle->source == SSASUB ); + int supports_burn = hb_subtitle_can_burn( subtitle->source ); - if ( burn && supports_burn ) + if ( ( burn && supports_burn ) || + !hb_subtitle_can_pass( subtitle->source, mux ) ) { // Only allow one subtitle to be burned into video if ( sub_burned ) |