|
6 | 6 | #include <limits.h> |
7 | 7 | #include <assert.h> |
8 | 8 | #include "dvb_subtitle_decoder.h" |
| 9 | +#include "vobsub_decoder.h" |
9 | 10 |
|
10 | 11 | void skip_bytes(FILE *file, ULLONG n) |
11 | 12 | { |
@@ -1426,6 +1427,114 @@ static void generate_vobsub_timestamp(char *buf, size_t bufsize, ULLONG millisec |
1426 | 1427 | hours, minutes, seconds, ms); |
1427 | 1428 | } |
1428 | 1429 |
|
| 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 | + |
1429 | 1538 | /* VOBSUB support: Save VOBSUB track to .idx and .sub files */ |
1430 | 1539 | #define VOBSUB_BLOCK_SIZE 2048 |
1431 | 1540 | 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 |
1564 | 1673 | char *filename; |
1565 | 1674 | int desc; |
1566 | 1675 |
|
1567 | | - // VOBSUB tracks need special handling - separate .idx and .sub files |
| 1676 | + // VOBSUB tracks need special handling |
1568 | 1677 | if (track->codec_id == MATROSKA_TRACK_SUBTITLE_CODEC_ID_VOBSUB) |
1569 | 1678 | { |
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 | + } |
1571 | 1691 | return; |
1572 | 1692 | } |
1573 | 1693 |
|
@@ -1846,8 +1966,13 @@ int matroska_loop(struct lib_ccx_ctx *ctx) |
1846 | 1966 | { |
1847 | 1967 | if (ccx_options.write_format_rewritten) |
1848 | 1968 | { |
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 | + } |
1851 | 1976 | } |
1852 | 1977 |
|
1853 | 1978 | // Don't need generated input file |
|
0 commit comments