summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
Diffstat (limited to 'libhb')
-rw-r--r--libhb/common.c30
-rw-r--r--libhb/common.h7
-rw-r--r--libhb/decssasub.c284
-rw-r--r--libhb/hb_json.c57
-rw-r--r--libhb/muxavformat.c3
-rw-r--r--libhb/reader.c4
-rw-r--r--libhb/rendersub.c9
-rw-r--r--libhb/stream.c3
-rw-r--r--libhb/work.c8
9 files changed, 358 insertions, 47 deletions
diff --git a/libhb/common.c b/libhb/common.c
index 525bf3ae4..f6b4ff7f1 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -4857,9 +4857,9 @@ int hb_subtitle_add(const hb_job_t * job, const hb_subtitle_config_t * subtitlec
return 1;
}
-int hb_srt_add( const hb_job_t * job,
+int hb_import_subtitle_add( const hb_job_t * job,
const hb_subtitle_config_t * subtitlecfg,
- const char *lang_code )
+ const char *lang_code, int source )
{
hb_subtitle_t *subtitle;
iso639_lang_t *lang = NULL;
@@ -4873,8 +4873,8 @@ 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;
+ subtitle->source = source;
+ subtitle->codec = source == IMPORTSRT ? WORK_DECSRTSUB : WORK_DECSSASUB;
subtitle->timebase.num = 1;
subtitle->timebase.den = 90000;
@@ -4895,6 +4895,13 @@ int hb_srt_add( const hb_job_t * job,
return 1;
}
+int hb_srt_add( const hb_job_t * job,
+ const hb_subtitle_config_t * subtitlecfg,
+ const char *lang_code )
+{
+ return hb_import_subtitle_add(job, subtitlecfg, lang_code, IMPORTSRT);
+}
+
int hb_subtitle_can_force( int source )
{
return source == VOBSUB || source == PGSSUB;
@@ -4902,9 +4909,9 @@ int hb_subtitle_can_force( int source )
int hb_subtitle_can_burn( int source )
{
- return source == VOBSUB || source == PGSSUB || source == SSASUB ||
- source == SRTSUB || source == CC608SUB || source == UTF8SUB ||
- source == TX3GSUB;
+ return source == VOBSUB || source == PGSSUB || source == SSASUB ||
+ source == CC608SUB || source == UTF8SUB || source == TX3GSUB ||
+ source == IMPORTSRT || source == IMPORTSSA;
}
int hb_subtitle_can_pass( int source, int mux )
@@ -4917,11 +4924,12 @@ int hb_subtitle_can_pass( int source, int mux )
case PGSSUB:
case VOBSUB:
case SSASUB:
- case SRTSUB:
case UTF8SUB:
case TX3GSUB:
case CC608SUB:
case CC708SUB:
+ case IMPORTSRT:
+ case IMPORTSSA:
return 1;
default:
@@ -4933,11 +4941,12 @@ int hb_subtitle_can_pass( int source, int mux )
{
case VOBSUB:
case SSASUB:
- case SRTSUB:
case UTF8SUB:
case TX3GSUB:
case CC608SUB:
case CC708SUB:
+ case IMPORTSRT:
+ case IMPORTSSA:
return 1;
default:
@@ -5446,7 +5455,7 @@ const char * hb_subsource_name( int source )
{
case VOBSUB:
return "VOBSUB";
- case SRTSUB:
+ case IMPORTSRT:
return "SRT";
case CC608SUB:
return "CC608";
@@ -5456,6 +5465,7 @@ const char * hb_subsource_name( int source )
return "UTF-8";
case TX3GSUB:
return "TX3G";
+ case IMPORTSSA:
case SSASUB:
return "SSA";
case PGSSUB:
diff --git a/libhb/common.h b/libhb/common.h
index cfdaced17..1cdee684e 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -164,6 +164,9 @@ hb_subtitle_t *hb_subtitle_copy(const hb_subtitle_t *src);
hb_list_t *hb_subtitle_list_copy(const hb_list_t *src);
void hb_subtitle_close( hb_subtitle_t **sub );
int hb_subtitle_add(const hb_job_t * job, const hb_subtitle_config_t * subtitlecfg, int track);
+int hb_import_subtitle_add( const hb_job_t * job,
+ const hb_subtitle_config_t * subtitlecfg,
+ const char *lang_code, int source );
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 );
@@ -926,7 +929,9 @@ struct hb_subtitle_s
hb_subtitle_config_t config;
enum subtype { PICTURESUB, TEXTSUB } format;
- enum subsource { VOBSUB, SRTSUB, CC608SUB, /*unused*/CC708SUB, UTF8SUB, TX3GSUB, SSASUB, PGSSUB } source;
+ enum subsource { VOBSUB, CC608SUB, /*unused*/CC708SUB,
+ UTF8SUB, TX3GSUB, SSASUB, PGSSUB,
+ IMPORTSRT, IMPORTSSA, SRTSUB = IMPORTSRT } source;
char lang[1024];
char iso639_2[4];
uint32_t attributes; /* Closed Caption, Childrens, Directors etc */
diff --git a/libhb/decssasub.c b/libhb/decssasub.c
index 612c7c1c2..4bf9c79d5 100644
--- a/libhb/decssasub.c
+++ b/libhb/decssasub.c
@@ -34,10 +34,12 @@
struct hb_work_private_s
{
- // If decoding to PICTURESUB format:
- int readOrder;
+ hb_job_t * job;
+ hb_subtitle_t * subtitle;
- hb_job_t *job;
+ // SSA Import
+ FILE * file;
+ int readOrder;
};
#define SSA_VERBOSE_PACKETS 0
@@ -220,27 +222,287 @@ void hb_ssa_style_init(hb_subtitle_style_t *style)
style->bg_alpha = 0xFF;
}
+static int extradataInit( hb_work_private_t * pv )
+{
+ int events = 0;
+ char * events_tag = "[Events]";
+ char * format_tag = "Format:";
+ int events_len = strlen(events_tag);;
+ int format_len = strlen(format_tag);;
+ char * header = NULL;
+
+ while (1)
+ {
+ char * line = NULL;
+ size_t len, size = 0;
+
+ len = getline(&line, &size, pv->file);
+ if (len < 0)
+ {
+ // Incomplete SSA header
+ free(header);
+ return 1;
+ }
+ if (len > 0)
+ {
+ if (header != NULL)
+ {
+ char * tmp = header;
+ header = hb_strdup_printf("%s%s", header, line);
+ free(tmp);
+ }
+ else
+ {
+ header = strdup(line);
+ }
+ }
+ if (!events)
+ {
+ if (len >= events_len && !strncasecmp(line, events_tag, events_len))
+ {
+ events = 1;
+ }
+ }
+ else
+ {
+ if (len >= format_len && !strncasecmp(line, format_tag, format_len))
+ {
+ free(line);
+ break;
+ }
+ if (len > 0)
+ {
+ // Improperly formatted SSA header
+ free(header);
+ return 1;
+ }
+ }
+ free(line);
+ }
+ pv->subtitle->extradata = (uint8_t*)header;
+ pv->subtitle->extradata_size = strlen(header) + 1;
+
+ return 0;
+}
+
static int decssaInit( hb_work_object_t * w, hb_job_t * job )
{
hb_work_private_t * pv;
pv = calloc( 1, sizeof( hb_work_private_t ) );
w->private_data = pv;
- pv->job = job;
+ pv->job = job;
+ pv->subtitle = w->subtitle;
+
+ if (w->fifo_in == NULL && pv->subtitle->config.src_filename != NULL)
+ {
+ pv->file = hb_fopen(pv->subtitle->config.src_filename, "r");
+ if(pv->file == NULL)
+ {
+ hb_error("Could not open the SSA subtitle file '%s'\n",
+ pv->subtitle->config.src_filename);
+ goto fail;
+ }
+
+ // Read SSA header and store in subtitle extradata
+ if (extradataInit(pv))
+ {
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ if (pv != NULL)
+ {
+ if (pv->file != NULL)
+ {
+ fclose(pv->file);
+ }
+ free(pv);
+ w->private_data = NULL;
+ }
+ return 1;
+}
+
+#define SSA_2_HB_TIME(hr,min,sec,centi) \
+ ( 90LL * ( hr * 1000LL * 60 * 60 +\
+ min * 1000LL * 60 +\
+ sec * 1000LL +\
+ centi * 10LL ) )
+
+/*
+ * Parses the start and stop time from the specified SSA packet.
+ *
+ * Returns true if parsing failed; false otherwise.
+ */
+static int parse_timing( char *line, int64_t *start, int64_t *stop )
+{
+ /*
+ * Parse Start and End fields for timing information
+ */
+ int start_hr, start_min, start_sec, start_centi;
+ int end_hr, end_min, end_sec, end_centi;
+ // SSA subtitles have an empty layer field (bare ','). The scanf
+ // format specifier "%*128[^,]" will not match on a bare ','. There
+ // must be at least one non ',' character in the match. So the format
+ // specifier is placed directly next to the ':' so that the next
+ // expected ' ' after the ':' will be the character it matches on
+ // when there is no layer field.
+ int numPartsRead = sscanf( (char *) line, "Dialogue:%*128[^,],"
+ "%d:%d:%d.%d," // Start
+ "%d:%d:%d.%d,", // End
+ &start_hr, &start_min, &start_sec, &start_centi,
+ &end_hr, &end_min, &end_sec, &end_centi );
+ if ( numPartsRead != 8 )
+ return 1;
+
+ *start = SSA_2_HB_TIME(start_hr, start_min, start_sec, start_centi);
+ *stop = SSA_2_HB_TIME( end_hr, end_min, end_sec, end_centi);
return 0;
}
+static char * find_field( char * pos, char * end, int fieldNum )
+{
+ int curFieldID = 1;
+ while (pos < end)
+ {
+ if ( *pos++ == ',' )
+ {
+ curFieldID++;
+ if ( curFieldID == fieldNum )
+ return pos;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * SSA line format:
+ * Dialogue: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0'
+ * 1 2 3 4 5 6 7 8 9 10
+ *
+ * MKV-SSA packet format:
+ * ReadOrder,Marked, Style,Name,MarginL,MarginR,MarginV,Effect,Text '\0'
+ * 1 2 3 4 5 6 7 8 9
+ */
+static hb_buffer_t *
+decode_line_to_mkv_ssa( hb_work_private_t * pv, char * line, int size )
+{
+ hb_buffer_t * out;
+
+ int64_t start, stop;
+ if (parse_timing(line, &start, &stop))
+ {
+ goto fail;
+ }
+
+ // Convert the SSA packet to MKV-SSA format, which is what libass expects
+ char * mkvSSA;
+ int numPartsRead;
+ char * styleToTextFields;
+ char * layerField = malloc(size);
+
+ // SSA subtitles have an empty layer field (bare ','). The scanf
+ // format specifier "%*128[^,]" will not match on a bare ','. There
+ // must be at least one non ',' character in the match. So the format
+ // specifier is placed directly next to the ':' so that the next
+ // expected ' ' after the ':' will be the character it matches on
+ // when there is no layer field.
+ numPartsRead = sscanf( (char *)line, "Dialogue:%128[^,],", layerField );
+ if ( numPartsRead != 1 )
+ {
+ free(layerField);
+ goto fail;
+ }
+
+ styleToTextFields = find_field( line, line + size, 4 );
+ if ( styleToTextFields == NULL ) {
+ free( layerField );
+ goto fail;
+ }
+
+ // The sscanf conversion above will result in an extra space
+ // before the layerField. Strip the space.
+ char *stripLayerField = layerField;
+ for(; *stripLayerField == ' '; stripLayerField++);
+
+ out = hb_buffer_init( size + 1 );
+ mkvSSA = (char*)out->data;
+
+ mkvSSA[0] = '\0';
+ sprintf(mkvSSA, "%d", pv->readOrder++);
+ strcat( mkvSSA, "," );
+ strcat( mkvSSA, stripLayerField );
+ strcat( mkvSSA, "," );
+ strcat( mkvSSA, (char *)styleToTextFields );
+
+ out->size = strlen(mkvSSA) + 1;
+ out->s.frametype = HB_FRAME_SUBTITLE;
+ out->s.start = start;
+ out->s.duration = stop - start;
+ out->s.stop = stop;
+
+ if( out->size == 0 )
+ {
+ hb_buffer_close(&out);
+ }
+
+ free( layerField );
+
+ return out;
+
+fail:
+ hb_log( "decssasub: malformed SSA subtitle packet: %.*s\n", size, line );
+ return NULL;
+}
+
+/*
+ * Read the SSA file and put the entries into the subtitle fifo for all to read
+ */
+static hb_buffer_t * ssa_read( hb_work_private_t * pv )
+{
+ hb_buffer_t * out;
+
+ while (!feof(pv->file))
+ {
+ char * line = NULL;
+ size_t len, size = 0;
+
+ len = getline(&line, &size, pv->file);
+ if (len > 0)
+ {
+ out = decode_line_to_mkv_ssa(pv, line, len);
+ if (out != NULL)
+ {
+ return out;
+ }
+ }
+ if (len < 0)
+ {
+ // Error or EOF
+ out = hb_buffer_eof_init();
+ return out;
+ }
+ }
+ out = hb_buffer_eof_init();
+
+ return out;
+}
+
static int decssaWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_t ** buf_out )
{
- hb_buffer_t * in = *buf_in;
-
-#if SSA_VERBOSE_PACKETS
- printf("\nPACKET(%"PRId64",%"PRId64"): %.*s\n", in->s.start/90, in->s.stop/90, in->size, in->data);
-#endif
+ hb_work_private_t * pv = w->private_data;
+ hb_buffer_t * in = *buf_in;
*buf_in = NULL;
+ if (in == NULL && pv->file != NULL)
+ {
+ in = ssa_read(pv);
+ }
*buf_out = in;
if (in->s.flags & HB_BUF_FLAG_EOF)
{
@@ -254,6 +516,10 @@ static int decssaWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_realloc(in, ++in->size);
in->data[in->size - 1] = '\0';
+#if SSA_VERBOSE_PACKETS
+ printf("\nPACKET(%"PRId64",%"PRId64"): %.*s\n", in->s.start/90, in->s.stop/90, in->size, in->data);
+#endif
+
return HB_WORK_OK;
}
diff --git a/libhb/hb_json.c b/libhb/hb_json.c
index 27df2fd71..2399adf45 100644
--- a/libhb/hb_json.c
+++ b/libhb/hb_json.c
@@ -823,17 +823,25 @@ hb_dict_t* hb_job_to_dict( const hb_job_t * job )
hb_dict_t *subtitle_dict;
hb_subtitle_t *subtitle = hb_list_item(job->list_subtitle, ii);
- if (subtitle->source == SRTSUB)
+ if (subtitle->source == IMPORTSRT ||
+ subtitle->source == IMPORTSSA)
{
subtitle_dict = json_pack_ex(&error, 0,
"{s:o, s:o, s:o, s:{s:o, s:o, s:o}}",
"Default", hb_value_bool(subtitle->config.default_track),
"Burn", hb_value_bool(subtitle->config.dest == RENDERSUB),
"Offset", hb_value_int(subtitle->config.offset),
- "SRT",
+ "Import",
+ "Format", hb_value_string(subtitle->source == IMPORTSRT ?
+ "SRT" : "SSA"),
"Filename", hb_value_string(subtitle->config.src_filename),
- "Language", hb_value_string(subtitle->iso639_2),
- "Codeset", hb_value_string(subtitle->config.src_codeset));
+ "Language", hb_value_string(subtitle->iso639_2));
+ if (subtitle->source == IMPORTSRT)
+ {
+ hb_dict_t *import_dict = hb_dict_get(subtitle_dict, "Import");
+ hb_dict_set(import_dict, "Codeset",
+ hb_value_string(subtitle->config.src_codeset));
+ }
}
else
{
@@ -1509,14 +1517,17 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict )
hb_subtitle_config_t sub_config;
int track = -1;
int burn = 0;
- const char *srtfile = NULL;
+ const char *importfile = NULL;
json_int_t offset = 0;
result = json_unpack_ex(subtitle_dict, &error, 0,
- "{s?i, s?{s:s}}",
+ "{s?i, s?{s:s}, s?{s:s}}",
"Track", unpack_i(&track),
+ // Support legacy "SRT" import
"SRT",
- "Filename", unpack_s(&srtfile));
+ "Filename", unpack_s(&importfile),
+ "Import",
+ "Filename", unpack_s(&importfile));
if (result < 0)
{
hb_error("json unpack failure: %s", error.text);
@@ -1524,7 +1535,7 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict )
return NULL;
}
// Embedded subtitle track
- if (track >= 0 && srtfile == NULL)
+ if (track >= 0 && importfile == NULL)
{
hb_subtitle_t *subtitle;
subtitle = hb_list_item(job->title->list_subtitle, track);
@@ -1548,22 +1559,30 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict )
hb_subtitle_add(job, &sub_config, track);
}
}
- else if (srtfile != NULL)
+ else if (importfile != NULL)
{
- strncpy(sub_config.src_filename, srtfile, 255);
+ strncpy(sub_config.src_filename, importfile, 255);
sub_config.src_filename[255] = 0;
- const char *srtlang = "und";
- const char *srtcodeset = "UTF-8";
+ const char * lang = "und";
+ const char * srtcodeset = "UTF-8";
+ const char * format = "SRT";
+ int source = IMPORTSRT;
result = json_unpack_ex(subtitle_dict, &error, 0,
- "{s?b, s?b, s?I, " // Common
- "s?{s?s, s?s, s?s}}", // SRT
+ "{s?b, s?b, s?I, " // Common
+ "s?{s?s, s?s, s?s}," // Legacy SRT settings
+ "s?{s?s, s?s, s?s, s?s}}", // Import settings
"Default", unpack_b(&sub_config.default_track),
"Burn", unpack_b(&burn),
"Offset", unpack_I(&offset),
"SRT",
- "Filename", unpack_s(&srtfile),
- "Language", unpack_s(&srtlang),
+ "Filename", unpack_s(&importfile),
+ "Language", unpack_s(&lang),
+ "Codeset", unpack_s(&srtcodeset),
+ "Import",
+ "Format", unpack_s(&format),
+ "Filename", unpack_s(&importfile),
+ "Language", unpack_s(&lang),
"Codeset", unpack_s(&srtcodeset));
if (result < 0)
{
@@ -1575,7 +1594,11 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict )
sub_config.dest = burn ? RENDERSUB : PASSTHRUSUB;
strncpy(sub_config.src_codeset, srtcodeset, 39);
sub_config.src_codeset[39] = 0;
- hb_srt_add(job, &sub_config, srtlang);
+ if (!strcasecmp(format, "SSA"))
+ {
+ source = IMPORTSSA;
+ }
+ hb_import_subtitle_add(job, &sub_config, lang, source);
}
}
}
diff --git a/libhb/muxavformat.c b/libhb/muxavformat.c
index 806620b69..60eba066f 100644
--- a/libhb/muxavformat.c
+++ b/libhb/muxavformat.c
@@ -842,9 +842,10 @@ static int avformatInit( hb_mux_object_t * m )
case CC608SUB:
case CC708SUB:
case TX3GSUB:
- case SRTSUB:
case UTF8SUB:
case SSASUB:
+ case IMPORTSRT:
+ case IMPORTSSA:
{
if (job->mux == HB_MUX_AV_MP4)
{
diff --git a/libhb/reader.c b/libhb/reader.c
index 4099a54f6..f200152bb 100644
--- a/libhb/reader.c
+++ b/libhb/reader.c
@@ -432,8 +432,10 @@ static void reader_send_eof( hb_work_private_t * r )
hb_subtitle_t *subtitle;
for (ii = 0; (subtitle = hb_list_item(r->job->list_subtitle, ii)); ++ii)
{
- if (subtitle->fifo_in && subtitle->source != SRTSUB)
+ if (subtitle->fifo_in)
+ {
push_buf(r, subtitle->fifo_in, hb_buffer_eof_init());
+ }
}
hb_log("reader: done. %d scr changes", r->demux.scr_changes);
}
diff --git a/libhb/rendersub.c b/libhb/rendersub.c
index 88b4bfdf1..866e277f4 100644
--- a/libhb/rendersub.c
+++ b/libhb/rendersub.c
@@ -981,7 +981,8 @@ static int hb_rendersub_post_init( hb_filter_object_t * filter, hb_job_t *job )
return ssa_post_init( filter, job );
} break;
- case SRTSUB:
+ case IMPORTSRT:
+ case IMPORTSSA:
case UTF8SUB:
case TX3GSUB:
{
@@ -1024,7 +1025,8 @@ static int hb_rendersub_work( hb_filter_object_t * filter,
return ssa_work( filter, buf_in, buf_out );
} break;
- case SRTSUB:
+ case IMPORTSRT:
+ case IMPORTSSA:
case CC608SUB:
case UTF8SUB:
case TX3GSUB:
@@ -1065,7 +1067,8 @@ static void hb_rendersub_close( hb_filter_object_t * filter )
ssa_close( filter );
} break;
- case SRTSUB:
+ case IMPORTSRT:
+ case IMPORTSSA:
case CC608SUB:
case UTF8SUB:
case TX3GSUB:
diff --git a/libhb/stream.c b/libhb/stream.c
index 427e10209..86aab1b39 100644
--- a/libhb/stream.c
+++ b/libhb/stream.c
@@ -5372,7 +5372,8 @@ static void add_ffmpeg_subtitle( hb_title_t *title, hb_stream_t *stream, int id
snprintf(subtitle->lang, sizeof( subtitle->lang ), "%s [%s]",
strlen(lang->native_name) ? lang->native_name : lang->eng_name,
hb_subsource_name(subtitle->source));
- strncpy(subtitle->iso639_2, lang->iso639_2, 4);
+ strncpy(subtitle->iso639_2, lang->iso639_2, 3);
+ subtitle->iso639_2[3] = 0;
// Copy the extradata for the subtitle track
if (codecpar->extradata != NULL)
diff --git a/libhb/work.c b/libhb/work.c
index ac5ae5533..5c7efb0b7 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -600,7 +600,7 @@ void hb_display_job_info(hb_job_t *job)
subtitle->lang, subtitle->track, subtitle->id,
subtitle->format == PICTURESUB ? "Picture" : "Text");
}
- else if( subtitle->source == SRTSUB )
+ else if (subtitle->source == IMPORTSRT)
{
/* For SRT, print offset and charset too */
hb_log(" * subtitle track %d, %s (track %d, id 0x%x, Text) -> "
@@ -1619,10 +1619,10 @@ static void do_job(hb_job_t *job)
// Since that number is unbounded, the FIFO must be made
// (effectively) unbounded in capacity.
subtitle->fifo_raw = hb_fifo_init( FIFO_UNBOUNDED, FIFO_UNBOUNDED_WAKE );
- if (w->id != WORK_DECSRTSUB)
+ // Check if input comes from a file.
+ if (subtitle->source != IMPORTSRT &&
+ subtitle->source != IMPORTSSA)
{
- // decsrtsub is a buffer source like reader. It's input comes
- // from a file.
subtitle->fifo_in = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
}
if (!job->indepth_scan)