Skip to content

Commit 63dde6f

Browse files
authored
feat(mp4): Add VOBSUB subtitle extraction with OCR for MP4 files
2 parents 173db88 + 8f64eeb commit 63dde6f

File tree

8 files changed

+884
-7
lines changed

8 files changed

+884
-7
lines changed

linux/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ ccextractor_SOURCES = \
151151
../src/lib_ccx/list.h \
152152
../src/lib_ccx/matroska.c \
153153
../src/lib_ccx/matroska.h \
154+
../src/lib_ccx/vobsub_decoder.c \
155+
../src/lib_ccx/vobsub_decoder.h \
154156
../src/lib_ccx/mp4.c \
155157
../src/lib_ccx/myth.c \
156158
../src/lib_ccx/networking.c \

mac/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ ccextractor_SOURCES = \
123123
../src/lib_ccx/list.h \
124124
../src/lib_ccx/matroska.c \
125125
../src/lib_ccx/matroska.h \
126+
../src/lib_ccx/vobsub_decoder.c \
127+
../src/lib_ccx/vobsub_decoder.h \
126128
../src/lib_ccx/mp4.c \
127129
../src/lib_ccx/myth.c \
128130
../src/lib_ccx/networking.c \

src/lib_ccx/matroska.c

Lines changed: 129 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <limits.h>
77
#include <assert.h>
88
#include "dvb_subtitle_decoder.h"
9+
#include "vobsub_decoder.h"
910

1011
void skip_bytes(FILE *file, ULLONG n)
1112
{
@@ -1426,6 +1427,114 @@ static void generate_vobsub_timestamp(char *buf, size_t bufsize, ULLONG millisec
14261427
hours, minutes, seconds, ms);
14271428
}
14281429

1430+
/* Check if output format is text-based (requires OCR for bitmap subtitles) */
1431+
static int is_text_output_format(enum ccx_output_format format)
1432+
{
1433+
return (format == CCX_OF_SRT || format == CCX_OF_SSA ||
1434+
format == CCX_OF_WEBVTT || format == CCX_OF_TRANSCRIPT ||
1435+
format == CCX_OF_SAMI || format == CCX_OF_SMPTETT);
1436+
}
1437+
1438+
/* VOBSUB support: Process VOBSUB track with OCR and output text format */
1439+
static void process_vobsub_track_ocr(struct matroska_ctx *mkv_ctx, struct matroska_sub_track *track)
1440+
{
1441+
if (track->sentence_count == 0)
1442+
{
1443+
mprint("\nNo VOBSUB subtitles to process");
1444+
return;
1445+
}
1446+
1447+
/* Check if OCR is available */
1448+
if (!vobsub_ocr_available())
1449+
{
1450+
fatal(EXIT_NOT_CLASSIFIED,
1451+
"VOBSUB to text conversion requires OCR support.\n"
1452+
"Please rebuild CCExtractor with -DWITH_OCR=ON or use raw output (--out=idx)");
1453+
}
1454+
1455+
/* Initialize VOBSUB decoder */
1456+
struct vobsub_ctx *vob_ctx = init_vobsub_decoder();
1457+
if (!vob_ctx)
1458+
{
1459+
fatal(EXIT_NOT_CLASSIFIED,
1460+
"VOBSUB to text conversion requires OCR, but initialization failed.\n"
1461+
"Please ensure Tesseract is installed with language data.");
1462+
}
1463+
1464+
/* Parse palette from track header (CodecPrivate) */
1465+
if (track->header)
1466+
{
1467+
vobsub_parse_palette(vob_ctx, track->header);
1468+
}
1469+
1470+
mprint("\nProcessing VOBSUB track with OCR (%d subtitles)", track->sentence_count);
1471+
1472+
/* Get encoder context for output */
1473+
struct encoder_ctx *enc_ctx = update_encoder_list(mkv_ctx->ctx);
1474+
1475+
/* Process each subtitle */
1476+
for (int i = 0; i < track->sentence_count; i++)
1477+
{
1478+
struct matroska_sub_sentence *sentence = track->sentences[i];
1479+
mkv_ctx->sentence_count++;
1480+
1481+
/* Calculate end time (use next subtitle start if not specified) */
1482+
ULLONG end_time = sentence->time_end;
1483+
if (end_time == 0 && i + 1 < track->sentence_count)
1484+
{
1485+
end_time = track->sentences[i + 1]->time_start - 1;
1486+
}
1487+
else if (end_time == 0)
1488+
{
1489+
end_time = sentence->time_start + 5000; /* Default 5 second duration */
1490+
}
1491+
1492+
/* Decode SPU and run OCR */
1493+
struct cc_subtitle sub;
1494+
memset(&sub, 0, sizeof(sub));
1495+
1496+
int ret = vobsub_decode_spu(vob_ctx,
1497+
(unsigned char *)sentence->text,
1498+
sentence->text_size,
1499+
sentence->time_start,
1500+
end_time,
1501+
&sub);
1502+
1503+
if (ret == 0 && sub.got_output)
1504+
{
1505+
/* Encode the subtitle to output format */
1506+
encode_sub(enc_ctx, &sub);
1507+
1508+
/* Free subtitle data */
1509+
if (sub.data)
1510+
{
1511+
struct cc_bitmap *rect = (struct cc_bitmap *)sub.data;
1512+
for (int j = 0; j < sub.nb_data; j++)
1513+
{
1514+
if (rect[j].data0)
1515+
free(rect[j].data0);
1516+
if (rect[j].data1)
1517+
free(rect[j].data1);
1518+
#ifdef ENABLE_OCR
1519+
if (rect[j].ocr_text)
1520+
free(rect[j].ocr_text);
1521+
#endif
1522+
}
1523+
free(sub.data);
1524+
}
1525+
}
1526+
1527+
/* Progress indicator */
1528+
if ((i + 1) % 50 == 0 || i + 1 == track->sentence_count)
1529+
{
1530+
mprint("\rProcessing VOBSUB: %d/%d subtitles", i + 1, track->sentence_count);
1531+
}
1532+
}
1533+
1534+
delete_vobsub_decoder(&vob_ctx);
1535+
mprint("\nVOBSUB OCR processing complete");
1536+
}
1537+
14291538
/* VOBSUB support: Save VOBSUB track to .idx and .sub files */
14301539
#define VOBSUB_BLOCK_SIZE 2048
14311540
static void save_vobsub_track(struct matroska_ctx *mkv_ctx, struct matroska_sub_track *track)
@@ -1564,10 +1673,21 @@ void save_sub_track(struct matroska_ctx *mkv_ctx, struct matroska_sub_track *tra
15641673
char *filename;
15651674
int desc;
15661675

1567-
// VOBSUB tracks need special handling - separate .idx and .sub files
1676+
// VOBSUB tracks need special handling
15681677
if (track->codec_id == MATROSKA_TRACK_SUBTITLE_CODEC_ID_VOBSUB)
15691678
{
1570-
save_vobsub_track(mkv_ctx, track);
1679+
// Check if user wants text output (SRT, SSA, WebVTT, etc.)
1680+
if (ccx_options.write_format_rewritten &&
1681+
is_text_output_format(ccx_options.enc_cfg.write_format))
1682+
{
1683+
// Use OCR to convert VOBSUB to text
1684+
process_vobsub_track_ocr(mkv_ctx, track);
1685+
}
1686+
else
1687+
{
1688+
// Output raw idx/sub files
1689+
save_vobsub_track(mkv_ctx, track);
1690+
}
15711691
return;
15721692
}
15731693

@@ -1846,8 +1966,13 @@ int matroska_loop(struct lib_ccx_ctx *ctx)
18461966
{
18471967
if (ccx_options.write_format_rewritten)
18481968
{
1849-
mprint(MATROSKA_WARNING "You are using --out=<format>, but Matroska parser extract subtitles in a recorded format\n");
1850-
mprint("--out=<format> will be ignored\n");
1969+
/* Note: For VOBSUB tracks, text output formats (SRT, SSA, etc.) are
1970+
* supported via OCR. For other subtitle types, the native format is used. */
1971+
if (!is_text_output_format(ccx_options.enc_cfg.write_format))
1972+
{
1973+
mprint(MATROSKA_WARNING "You are using --out=<format>, but Matroska parser extracts subtitles in their recorded format\n");
1974+
mprint("--out=<format> will be ignored for non-VOBSUB tracks\n");
1975+
}
18511976
}
18521977

18531978
// Don't need generated input file

0 commit comments

Comments
 (0)