# HG changeset patch # User mosu # Date 1066338618 0 # Node ID 0e9ea2a3a2207eea405ffbf8d07f2469789dadff # Parent cb82b062188f7eb6b10130166a5f43fbd8052f0c Support for uncompressed VobSubs. diff -r cb82b062188f -r 0e9ea2a3a220 libmpdemux/demux_mkv.cpp --- a/libmpdemux/demux_mkv.cpp Thu Oct 16 21:06:43 2003 +0000 +++ b/libmpdemux/demux_mkv.cpp Thu Oct 16 21:10:18 2003 +0000 @@ -39,6 +39,7 @@ #include extern "C" { +#include #include #include #include @@ -75,8 +76,12 @@ // default values for Matroska elements #define MKVD_TIMECODESCALE 1000000 // 1000000 = 1ms -#define MKV_SUBTYPE_TEXT 1 -#define MKV_SUBTYPE_SSA 2 +#define MKV_SUBTYPE_TEXT 1 +#define MKV_SUBTYPE_SSA 2 +#define MKV_SUBTYPE_VOBSUB 3 + +#define MKV_SUBCOMPRESSION_NONE 0 +#define MKV_SUBCOMPRESSION_ZLIB 1 class mpstream_io_callback: public IOCallback { private: @@ -182,6 +187,10 @@ // Stuff for QuickTime bool fix_i_bps; float qt_last_a_pts; + + // Stuff for VobSubs + mkv_sh_sub_t sh_sub; + int vobsub_compression; } mkv_track_t; typedef struct mkv_demuxer { @@ -580,6 +589,74 @@ return NULL; } +static bool mkv_parse_idx(mkv_track_t *t) { + uint32_t i, p, things_found; + int idx; + string line, s1, s2; + char *src; + + if ((t->private_data == NULL) || (t->private_size < 1)) + return false; + + things_found = 0; + i = 0; + src = (char *)t->private_data; + do { + line = ""; + while ((i < t->private_size) && (src[i] != '\n') && (src[i] != '\r')) { + if (!isspace(src[i])) + line += src[i]; + i++; + } + while ((i < t->private_size) && ((src[i] == '\n') || (src[i] == '\r'))) + i++; + + if (!strncasecmp(line.c_str(), "size:", 5)) { + s1 = line.substr(5); + idx = s1.find('x'); + if (idx >= 0) { + s2 = s1.substr(idx + 1); + s1.erase(idx); + t->sh_sub.width = strtol(s1.c_str(), NULL, 10); + t->sh_sub.height = strtol(s2.c_str(), NULL, 10); + things_found |= 1; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] VobSub IDX parser: size: %d x %d\n", + t->sh_sub.width, t->sh_sub.height); + } + + } else if (!strncasecmp(line.c_str(), "palette:", 8)) { + s1 = line.substr(8); + for (p = 0; p < 15; p++) { + idx = s1.find(','); + if (idx < 0) + break; + s2 = s1.substr(0, idx); + s1.erase(0, idx + 1); + t->sh_sub.palette[p] = (unsigned int)strtol(s2.c_str(), NULL, 16); + } + if (idx >= 0) { + t->sh_sub.palette[15] = (unsigned int)strtol(s1.c_str(), NULL, 16); + things_found |= 2; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] VobSub IDX parser: palette: 0x%06x " + "0x%06x 0x%06x 0x%06x 0x%06x 0x%06x 0x%06x 0x%06x 0x%06x " + "0x%06x 0x%06x 0x%06x 0x%06x 0x%06x 0x%06x 0x%06x\n", + t->sh_sub.palette[0], t->sh_sub.palette[1], + t->sh_sub.palette[2], t->sh_sub.palette[3], + t->sh_sub.palette[4], t->sh_sub.palette[5], + t->sh_sub.palette[6], t->sh_sub.palette[7], + t->sh_sub.palette[8], t->sh_sub.palette[9], + t->sh_sub.palette[10], t->sh_sub.palette[11], + t->sh_sub.palette[12], t->sh_sub.palette[13], + t->sh_sub.palette[14], t->sh_sub.palette[15]); + } + } + + } while ((i != t->private_size) && (things_found != 3)); + t->sh_sub.type = 'v'; + + return (things_found == 3); +} + static int check_track_information(mkv_demuxer_t *d) { int i, track_num; unsigned char *c; @@ -809,12 +886,23 @@ t->language != NULL ? t->language : ""); break; - case 's': // Text subtitles do not need any data - t->ok = 1; // except the CodecID. - mp_msg(MSGT_DEMUX, MSGL_INFO, "[mkv] Track ID %u: subtitles (%s), " - "-sid %u%s%s\n", t->tnum, t->codec_id, t->xid, - t->language != NULL ? ", -slang " : "", - t->language != NULL ? t->language : ""); + case 's': + if (!strncmp(t->codec_id, MKV_S_VOBSUB, strlen(MKV_S_VOBSUB))) { + if (mkv_parse_idx(t)) { + t->ok = 1; + mp_msg(MSGT_DEMUX, MSGL_INFO, "[mkv] Track ID %u: subtitles (%s), " + "-sid %u%s%s\n", t->tnum, t->codec_id, + t->xid, t->language != NULL ? ", -slang " : "", + t->language != NULL ? t->language : ""); + } + + } else { // Text subtitles do not need any data + t->ok = 1; // except the CodecID. + mp_msg(MSGT_DEMUX, MSGL_INFO, "[mkv] Track ID %u: subtitles (%s), " + "-sid %u%s%s\n", t->tnum, t->codec_id, t->xid, + t->language != NULL ? ", -slang " : "", + t->language != NULL ? t->language : ""); + } break; default: // unknown track type!? error in demuxer... @@ -1898,10 +1986,19 @@ else if (dvdsub_lang != NULL) track = find_track_by_language(mkv_d, dvdsub_lang, NULL); if (track) { - if (strcmp(track->codec_id, MKV_S_TEXTASCII) && - strcmp(track->codec_id, MKV_S_TEXTUTF8) && - strcmp(track->codec_id, MKV_S_TEXTSSA) && - strcmp(track->codec_id, "S_SSA")) + if (!strncmp(track->codec_id, MKV_S_VOBSUB, strlen(MKV_S_VOBSUB))) { + mp_msg(MSGT_DEMUX, MSGL_INFO, "[mkv] Will display subtitle track %u\n", + track->tnum); + mkv_d->subs_track = track; + mkv_d->subtitle_type = MKV_SUBTYPE_VOBSUB; + demuxer->sub->sh = (mkv_sh_sub_t *)malloc(sizeof(mkv_sh_sub_t)); + demuxer->sub->id = track->xid; + memcpy(demuxer->sub->sh, &track->sh_sub, sizeof(mkv_sh_sub_t)); + + } else if (strcmp(track->codec_id, MKV_S_TEXTASCII) && + strcmp(track->codec_id, MKV_S_TEXTUTF8) && + strcmp(track->codec_id, MKV_S_TEXTSSA) && + strcmp(track->codec_id, "S_SSA")) mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] Subtitle type '%s' is not " "supported. Track will not be displayed.\n", track->codec_id); else { @@ -1923,6 +2020,7 @@ } else mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] File does not contain a " "subtitle track with the id %u.\n", demuxer->sub->id); + demuxer->sub->sh = NULL; } } @@ -2058,6 +2156,214 @@ found_data++; } +#define mpeg_read(buf, num) _mpeg_read(srcbuf, size, srcpos, buf, num) +static uint32_t _mpeg_read(unsigned char *srcbuf, uint32_t size, + uint32_t &srcpos, unsigned char *dstbuf, + uint32_t num) { + uint32_t real_num; + + if ((srcpos + num) >= size) + real_num = size - srcpos; + else + real_num = num; + memcpy(dstbuf, &srcbuf[srcpos], real_num); + srcpos += real_num; + + return real_num; +} + +#define mpeg_getc() _mpeg_getch(srcbuf, size, srcpos) +static int _mpeg_getch(unsigned char *srcbuf, uint32_t size, + uint32_t &srcpos) { + unsigned char c; + + if (mpeg_read(&c, 1) != 1) + return -1; + return (int)c; +} + +#define mpeg_seek(b, w) _mpeg_seek(size, srcpos, b, w) +static int _mpeg_seek(uint32_t size, uint32_t &srcpos, uint32_t num, + int whence) { + uint32_t new_pos; + + if (whence == SEEK_SET) + new_pos = num; + else if (whence == SEEK_CUR) + new_pos = srcpos + num; + else + abort(); + + if (new_pos >= size) { + srcpos = size; + return 1; + } + + srcpos = new_pos; + return 0; +} + +#define mpeg_tell() srcpos + +// Adopted from vobsub.c +static int mpeg_run(demuxer_t *demuxer, unsigned char *srcbuf, uint32_t size) { + uint32_t len, idx, version, srcpos, packet_size; + int c, aid; + float pts; + /* Goto start of a packet, it starts with 0x000001?? */ + const unsigned char wanted[] = { 0, 0, 1 }; + unsigned char buf[5]; + demux_packet_t *dp; + demux_stream_t *ds; + mkv_demuxer_t *mkv_d; + + mkv_d = (mkv_demuxer_t *)demuxer->priv; + ds = demuxer->sub; + + srcpos = 0; + packet_size = 0; + while (1) { + if (mpeg_read(buf, 4) != 4) + return -1; + while (memcmp(buf, wanted, sizeof(wanted)) != 0) { + c = mpeg_getc(); + if (c < 0) + return -1; + memmove(buf, buf + 1, 3); + buf[3] = c; + } + switch (buf[3]) { + case 0xb9: /* System End Code */ + return 0; + break; + + case 0xba: /* Packet start code */ + c = mpeg_getc(); + if (c < 0) + return -1; + if ((c & 0xc0) == 0x40) + version = 4; + else if ((c & 0xf0) == 0x20) + version = 2; + else { + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] VobSub: Unsupported MPEG " + "version: 0x%02x\n", c); + return -1; + } + + if (version == 4) { + if (mpeg_seek(9, SEEK_CUR)) + return -1; + } else if (version == 2) { + if (mpeg_seek(7, SEEK_CUR)) + return -1; + } else + abort(); + break; + + case 0xbd: /* packet */ + if (mpeg_read(buf, 2) != 2) + return -1; + len = buf[0] << 8 | buf[1]; + idx = mpeg_tell(); + c = mpeg_getc(); + if (c < 0) + return -1; + if ((c & 0xC0) == 0x40) { /* skip STD scale & size */ + if (mpeg_getc() < 0) + return -1; + c = mpeg_getc(); + if (c < 0) + return -1; + } + if ((c & 0xf0) == 0x20) { /* System-1 stream timestamp */ + /* Do we need this? */ + abort(); + } else if ((c & 0xf0) == 0x30) { + /* Do we need this? */ + abort(); + } else if ((c & 0xc0) == 0x80) { /* System-2 (.VOB) stream */ + uint32_t pts_flags, hdrlen, dataidx; + c = mpeg_getc(); + if (c < 0) + return -1; + pts_flags = c; + c = mpeg_getc(); + if (c < 0) + return -1; + hdrlen = c; + dataidx = mpeg_tell() + hdrlen; + if (dataidx > idx + len) { + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] VobSub: Invalid header " + "length: %d (total length: %d, idx: %d, dataidx: %d)\n", + hdrlen, len, idx, dataidx); + return -1; + } + if ((pts_flags & 0xc0) == 0x80) { + if (mpeg_read(buf, 5) != 5) + return -1; + if (!(((buf[0] & 0xf0) == 0x20) && (buf[0] & 1) && (buf[2] & 1) && + (buf[4] & 1))) { + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] VobSub PTS error: 0x%02x " + "%02x%02x %02x%02x \n", + buf[0], buf[1], buf[2], buf[3], buf[4]); + pts = 0; + } else + pts = ((buf[0] & 0x0e) << 29 | buf[1] << 22 | + (buf[2] & 0xfe) << 14 | buf[3] << 7 | (buf[4] >> 1)); + } else /* if ((pts_flags & 0xc0) == 0xc0) */ { + /* what's this? */ + /* abort(); */ + } + mpeg_seek(dataidx, SEEK_SET); + aid = mpeg_getc(); + if (aid < 0) { + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] VobSub: Bogus aid %d\n", aid); + return -1; + } + packet_size = len - ((unsigned int)mpeg_tell() - idx); + + dp = new_demux_packet(packet_size); + dp->flags = 1; + dp->pts = mkv_d->last_pts; + if (mpeg_read(dp->buffer, packet_size) != packet_size) { + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] VobSub: mpeg_read failure"); + packet_size = 0; + return -1; + } + ds_add_packet(ds, dp); + idx = len; + } + break; + + case 0xbe: /* Padding */ + if (mpeg_read(buf, 2) != 2) + return -1; + len = buf[0] << 8 | buf[1]; + if ((len > 0) && mpeg_seek(len, SEEK_CUR)) + return -1; + break; + + default: + if ((0xc0 <= buf[3]) && (buf[3] < 0xf0)) { + /* MPEG audio or video */ + if (mpeg_read(buf, 2) != 2) + return -1; + len = (buf[0] << 8) | buf[1]; + if ((len > 0) && mpeg_seek(len, SEEK_CUR)) + return -1; + + } + else { + mp_msg(MSGT_DEMUX, MSGL_ERR, "[mkv] VobSub: unknown header " + "0x%02X%02X%02X%02X\n", buf[0], buf[1], buf[2], buf[3]); + return -1; + } + } + } + return 0; +} + extern "C" int demux_mkv_fill_buffer(demuxer_t *d) { demux_packet_t *dp; demux_stream_t *ds; @@ -2187,6 +2493,9 @@ else if ((mkv_d->audio != NULL) && (mkv_d->audio->tnum == kblock->TrackNum())) ds = d->audio; + else if ((mkv_d->subs_track != NULL) && + (mkv_d->subs_track->tnum == kblock->TrackNum())) + ds = d->sub; use_this_block = true; @@ -2238,9 +2547,11 @@ } else if ((mkv_d->subs_track != NULL) && (mkv_d->subs_track->tnum == kblock->TrackNum())) { - if (!mkv_d->v_skip_to_keyframe) - handle_subtitles(d, kblock, block_duration); - use_this_block = false; + if (mkv_d->subtitle_type != MKV_SUBTYPE_VOBSUB) { + if (!mkv_d->v_skip_to_keyframe) + handle_subtitles(d, kblock, block_duration); + use_this_block = false; + } } else use_this_block = false; @@ -2261,6 +2572,10 @@ handle_realaudio(d, data, block_bref == 0, found_data); + else if ((ds == d->sub) && + (mkv_d->subtitle_type == MKV_SUBTYPE_VOBSUB)) + mpeg_run(d, (unsigned char *)data.Buffer(), data.Size()); + else { dp = new_demux_packet(data.Size()); memcpy(dp->buffer, data.Buffer(), data.Size()); @@ -2273,8 +2588,7 @@ if (ds == d->video) { mkv_d->v_skip_to_keyframe = false; mkv_d->skip_to_timecode = 0; - } - if (ds == d->audio) + } else if (ds == d->audio) mkv_d->a_skip_to_keyframe = false; }