mirror of
https://github.com/mpv-player/mpv
synced 2025-04-17 12:54:40 +00:00
Support for SSA text subs in Matroska. Patch partially by Tristan <z80@oceanfree.net>.
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@10846 b3059339-0415-0410-9bf9-f77b7e298cf2
This commit is contained in:
parent
2982f7b1d4
commit
c80808b5a3
@ -69,6 +69,9 @@ extern char *audio_lang;
|
|||||||
// default values for Matroska elements
|
// default values for Matroska elements
|
||||||
#define MKVD_TIMECODESCALE 1000000 // 1000000 = 1ms
|
#define MKVD_TIMECODESCALE 1000000 // 1000000 = 1ms
|
||||||
|
|
||||||
|
#define MKV_SUBTYPE_TEXT 1
|
||||||
|
#define MKV_SUBTYPE_SSA 2
|
||||||
|
|
||||||
class mpstream_io_callback: public IOCallback {
|
class mpstream_io_callback: public IOCallback {
|
||||||
private:
|
private:
|
||||||
stream_t *s;
|
stream_t *s;
|
||||||
@ -187,9 +190,10 @@ typedef struct mkv_demuxer {
|
|||||||
|
|
||||||
mpstream_io_callback *in;
|
mpstream_io_callback *in;
|
||||||
|
|
||||||
uint64_t clear_subs_at;
|
uint64_t clear_subs_at[SUB_MAX_TEXT];
|
||||||
|
|
||||||
subtitle subs;
|
subtitle subs;
|
||||||
|
int subtitle_type;
|
||||||
|
|
||||||
EbmlStream *es;
|
EbmlStream *es;
|
||||||
EbmlElement *saved_l1, *saved_l2;
|
EbmlElement *saved_l1, *saved_l2;
|
||||||
@ -397,7 +401,7 @@ unsigned int read_dword(unsigned char *p, int &pos, int size) {
|
|||||||
|
|
||||||
static void handle_subtitles(demuxer_t *d, KaxBlock *block, int64_t duration) {
|
static void handle_subtitles(demuxer_t *d, KaxBlock *block, int64_t duration) {
|
||||||
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *)d->priv;
|
mkv_demuxer_t *mkv_d = (mkv_demuxer_t *)d->priv;
|
||||||
int len, line, state;
|
int len, line, state, i;
|
||||||
char *s1, *s2, *buffer;
|
char *s1, *s2, *buffer;
|
||||||
|
|
||||||
if (duration == -1) {
|
if (duration == -1) {
|
||||||
@ -417,34 +421,83 @@ static void handle_subtitles(demuxer_t *d, KaxBlock *block, int64_t duration) {
|
|||||||
s1++;
|
s1++;
|
||||||
|
|
||||||
line = 0;
|
line = 0;
|
||||||
s2 = mkv_d->subs.text[0];
|
mkv_d->subs.lines++;
|
||||||
mkv_d->subs.lines = 1;
|
s2 = mkv_d->subs.text[mkv_d->subs.lines - 1];
|
||||||
state = 0;
|
state = 0;
|
||||||
while ((unsigned int)(s1 - buffer) != data.Size()) {
|
|
||||||
if ((*s1 == '\n') || (*s1 == '\r')) {
|
if (mkv_d->subtitle_type == MKV_SUBTYPE_SSA) {
|
||||||
if (state == 0) { // normal char --> newline
|
/* Matroska's SSA format does not have timecodes embedded into
|
||||||
if (mkv_d->subs.lines == SUB_MAX_TEXT)
|
the SAA line. Timescodes are encoded into the blocks timecode
|
||||||
break;
|
and duration. */
|
||||||
*s2 = 0;
|
|
||||||
s2 = mkv_d->subs.text[mkv_d->subs.lines];
|
/* Find text section. */
|
||||||
mkv_d->subs.lines++;
|
for (i = 0; (i < 8) && (*s1 != 0); s1++)
|
||||||
|
if (*s1 == ',')
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (*s1 == 0) { // Broken line?
|
||||||
|
mkv_d->subs.lines--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load text. */
|
||||||
|
while ((unsigned int)(s1 - buffer) < data.Size()) {
|
||||||
|
if ((*s1 == '{') && ((unsigned int)(s1 + 2 - buffer) < data.Size())) {
|
||||||
|
/* Newline */
|
||||||
|
if (*(s1 + 1) == '\\' && (*(s1 + 2) == 'N' || *(s1 + 2) == 'n')) {
|
||||||
|
mkv_d->clear_subs_at[mkv_d->subs.lines - 1] =
|
||||||
|
block->GlobalTimecode() / 1000000 - mkv_d->first_tc + duration;
|
||||||
|
|
||||||
|
mkv_d->subs.lines++;
|
||||||
|
*s2 = 0;
|
||||||
|
s2 = mkv_d->subs.text[mkv_d->subs.lines - 1];
|
||||||
|
}
|
||||||
state = 1;
|
state = 1;
|
||||||
}
|
} else if (*s1 == '}' && state == 1)
|
||||||
} else if (*s1 == '<') // skip HTML tags
|
state = 2;
|
||||||
state = 2;
|
|
||||||
else if (*s1 == '>')
|
if (state == 0) {
|
||||||
state = 0;
|
|
||||||
else if (state != 2) { // normal character
|
|
||||||
state = 0;
|
|
||||||
if ((s2 - mkv_d->subs.text[mkv_d->subs.lines - 1]) < 255) {
|
|
||||||
*s2 = *s1;
|
*s2 = *s1;
|
||||||
s2++;
|
s2++;
|
||||||
|
if ((s2 - mkv_d->subs.text[mkv_d->subs.lines - 1]) >= 255)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
s1++;
|
||||||
s1++;
|
|
||||||
}
|
|
||||||
|
|
||||||
*s2 = 0;
|
if (state == 2)
|
||||||
|
state = 0;
|
||||||
|
}
|
||||||
|
*s2 = 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
while ((unsigned int)(s1 - buffer) != data.Size()) {
|
||||||
|
if ((*s1 == '\n') || (*s1 == '\r')) {
|
||||||
|
if (state == 0) { // normal char --> newline
|
||||||
|
if (mkv_d->subs.lines == SUB_MAX_TEXT)
|
||||||
|
break;
|
||||||
|
*s2 = 0;
|
||||||
|
mkv_d->clear_subs_at[mkv_d->subs.lines - 1]=
|
||||||
|
block->GlobalTimecode() / 1000000 - mkv_d->first_tc + duration;
|
||||||
|
s2 = mkv_d->subs.text[mkv_d->subs.lines];
|
||||||
|
mkv_d->subs.lines++;
|
||||||
|
state = 1;
|
||||||
|
}
|
||||||
|
} else if (*s1 == '<') // skip HTML tags
|
||||||
|
state = 2;
|
||||||
|
else if (*s1 == '>')
|
||||||
|
state = 0;
|
||||||
|
else if (state != 2) { // normal character
|
||||||
|
state = 0;
|
||||||
|
if ((s2 - mkv_d->subs.text[mkv_d->subs.lines - 1]) < 255) {
|
||||||
|
*s2 = *s1;
|
||||||
|
s2++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s1++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*s2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_ICONV
|
#ifdef USE_ICONV
|
||||||
subcp_recode1(&mkv_d->subs);
|
subcp_recode1(&mkv_d->subs);
|
||||||
@ -453,8 +506,8 @@ static void handle_subtitles(demuxer_t *d, KaxBlock *block, int64_t duration) {
|
|||||||
vo_sub = &mkv_d->subs;
|
vo_sub = &mkv_d->subs;
|
||||||
vo_osd_changed(OSDTYPE_SUBTITLE);
|
vo_osd_changed(OSDTYPE_SUBTITLE);
|
||||||
|
|
||||||
mkv_d->clear_subs_at = block->GlobalTimecode() / 1000000 - mkv_d->first_tc +
|
mkv_d->clear_subs_at[mkv_d->subs.lines - 1] =
|
||||||
duration;
|
block->GlobalTimecode() / 1000000 - mkv_d->first_tc + duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
static mkv_track_t *new_mkv_track(mkv_demuxer_t *d) {
|
static mkv_track_t *new_mkv_track(mkv_demuxer_t *d) {
|
||||||
@ -1837,7 +1890,8 @@ extern "C" int demux_mkv_open(demuxer_t *demuxer) {
|
|||||||
track = find_track_by_language(mkv_d, dvdsub_lang, NULL);
|
track = find_track_by_language(mkv_d, dvdsub_lang, NULL);
|
||||||
if (track) {
|
if (track) {
|
||||||
if (strcmp(track->codec_id, MKV_S_TEXTASCII) &&
|
if (strcmp(track->codec_id, MKV_S_TEXTASCII) &&
|
||||||
strcmp(track->codec_id, MKV_S_TEXTUTF8))
|
strcmp(track->codec_id, MKV_S_TEXTUTF8) &&
|
||||||
|
strcmp(track->codec_id, MKV_S_TEXTSSA))
|
||||||
mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Subtitle type '%s' is not "
|
mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Subtitle type '%s' is not "
|
||||||
"supported. Track will not be displayed.\n", track->codec_id);
|
"supported. Track will not be displayed.\n", track->codec_id);
|
||||||
else {
|
else {
|
||||||
@ -1850,6 +1904,10 @@ extern "C" int demux_mkv_open(demuxer_t *demuxer) {
|
|||||||
|
|
||||||
if (!strcmp(track->codec_id, MKV_S_TEXTUTF8))
|
if (!strcmp(track->codec_id, MKV_S_TEXTUTF8))
|
||||||
sub_utf8 = 1; // Force UTF-8 conversion.
|
sub_utf8 = 1; // Force UTF-8 conversion.
|
||||||
|
if (!strcmp(track->codec_id, MKV_S_TEXTSSA))
|
||||||
|
mkv_d->subtitle_type = MKV_SUBTYPE_SSA;
|
||||||
|
else
|
||||||
|
mkv_d->subtitle_type = MKV_SUBTYPE_TEXT;
|
||||||
} else
|
} else
|
||||||
mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] File does not contain a "
|
mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] File does not contain a "
|
||||||
"subtitle track with the id %u.\n", demuxer->sub->id);
|
"subtitle track with the id %u.\n", demuxer->sub->id);
|
||||||
@ -1992,13 +2050,14 @@ extern "C" int demux_mkv_fill_buffer(demuxer_t *d) {
|
|||||||
demux_packet_t *dp;
|
demux_packet_t *dp;
|
||||||
demux_stream_t *ds;
|
demux_stream_t *ds;
|
||||||
mkv_demuxer_t *mkv_d;
|
mkv_demuxer_t *mkv_d;
|
||||||
int upper_lvl_el, exit_loop, found_data, i;
|
int upper_lvl_el, exit_loop, found_data, i, linei, sl;
|
||||||
|
char *texttmp;
|
||||||
// Elements for different levels
|
// Elements for different levels
|
||||||
EbmlElement *l0 = NULL, *l1 = NULL, *l2 = NULL, *l3 = NULL;
|
EbmlElement *l0 = NULL, *l1 = NULL, *l2 = NULL, *l3 = NULL;
|
||||||
EbmlStream *es;
|
EbmlStream *es;
|
||||||
KaxBlock *block;
|
KaxBlock *block;
|
||||||
int64_t block_duration, block_bref, block_fref;
|
int64_t block_duration, block_bref, block_fref;
|
||||||
bool use_this_block;
|
bool use_this_block, lines_cut;
|
||||||
float current_pts;
|
float current_pts;
|
||||||
|
|
||||||
mkv_d = (mkv_demuxer_t *)d->priv;
|
mkv_d = (mkv_demuxer_t *)d->priv;
|
||||||
@ -2087,13 +2146,26 @@ extern "C" int demux_mkv_fill_buffer(demuxer_t *d) {
|
|||||||
|
|
||||||
if (kblock != NULL) {
|
if (kblock != NULL) {
|
||||||
// Clear the subtitles if they're obsolete now.
|
// Clear the subtitles if they're obsolete now.
|
||||||
if ((mkv_d->clear_subs_at > 0) &&
|
lines_cut = false;
|
||||||
(mkv_d->clear_subs_at <=
|
for (linei = 0; linei < mkv_d->subs.lines; linei++) {
|
||||||
(kblock->GlobalTimecode() / 1000000 - mkv_d->first_tc))) {
|
if (mkv_d->clear_subs_at[linei] <=
|
||||||
mkv_d->subs.lines = 0;
|
(kblock->GlobalTimecode() / 1000000 - mkv_d->first_tc)) {
|
||||||
vo_sub = &mkv_d->subs;
|
sl = linei;
|
||||||
vo_osd_changed(OSDTYPE_SUBTITLE);
|
texttmp = mkv_d->subs.text[sl];
|
||||||
mkv_d->clear_subs_at = 0;
|
while (sl < mkv_d->subs.lines) {
|
||||||
|
mkv_d->subs.text[sl] = mkv_d->subs.text[sl + 1];
|
||||||
|
mkv_d->clear_subs_at[sl] = mkv_d->clear_subs_at[sl + 1];
|
||||||
|
sl++;
|
||||||
|
}
|
||||||
|
mkv_d->subs.text[sl] = texttmp;
|
||||||
|
mkv_d->subs.lines--;
|
||||||
|
linei--;
|
||||||
|
lines_cut = true;
|
||||||
|
}
|
||||||
|
if (lines_cut) {
|
||||||
|
vo_sub = &mkv_d->subs;
|
||||||
|
vo_osd_changed(OSDTYPE_SUBTITLE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ds = NULL;
|
ds = NULL;
|
||||||
@ -2424,7 +2496,6 @@ extern "C" void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs,
|
|||||||
mkv_d->subs.lines = 0;
|
mkv_d->subs.lines = 0;
|
||||||
vo_sub = &mkv_d->subs;
|
vo_sub = &mkv_d->subs;
|
||||||
vo_osd_changed(OSDTYPE_SUBTITLE);
|
vo_osd_changed(OSDTYPE_SUBTITLE);
|
||||||
mkv_d->clear_subs_at = 0;
|
|
||||||
|
|
||||||
if(demuxer->audio->sh != NULL)
|
if(demuxer->audio->sh != NULL)
|
||||||
resync_audio_stream((sh_audio_t *)demuxer->audio->sh);
|
resync_audio_stream((sh_audio_t *)demuxer->audio->sh);
|
||||||
|
@ -27,8 +27,8 @@
|
|||||||
#define MKV_A_REALCOOK "A_REAL/COOK"
|
#define MKV_A_REALCOOK "A_REAL/COOK"
|
||||||
#define MKV_A_REALDNET "A_REAL/DNET"
|
#define MKV_A_REALDNET "A_REAL/DNET"
|
||||||
#define MKV_A_REALSIPR "A_REAL/SIPR"
|
#define MKV_A_REALSIPR "A_REAL/SIPR"
|
||||||
#define MKV_A_QDMC "A_QDESIGNMUSIC"
|
#define MKV_A_QDMC "A_QUICKTIME/QDMC"
|
||||||
#define MKV_A_QDMC2 "A_QDESIGNMUSIC/V2"
|
#define MKV_A_QDMC2 "A_QUICKTIME/QDM2"
|
||||||
|
|
||||||
#define MKV_V_MSCOMP "V_MS/VFW/FOURCC"
|
#define MKV_V_MSCOMP "V_MS/VFW/FOURCC"
|
||||||
#define MKV_V_REALV10 "V_REAL/RV10"
|
#define MKV_V_REALV10 "V_REAL/RV10"
|
||||||
@ -43,5 +43,6 @@
|
|||||||
|
|
||||||
#define MKV_S_TEXTASCII "S_TEXT/ASCII"
|
#define MKV_S_TEXTASCII "S_TEXT/ASCII"
|
||||||
#define MKV_S_TEXTUTF8 "S_TEXT/UTF8"
|
#define MKV_S_TEXTUTF8 "S_TEXT/UTF8"
|
||||||
|
#define MKV_S_TEXTSSA "S_TEXT/SSA"
|
||||||
|
|
||||||
#endif /* __MATROSKA_H */
|
#endif /* __MATROSKA_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user