root/src/media_tools/vobsub.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vobsub_lang_name
  2. vobsub_lang_id
  3. strltrim
  4. strrtrim
  5. strtrim
  6. vobsub_read_idx
  7. vobsub_free
  8. vobsub_get_subpic_duration
  9. vobsub_packetize_subpicture

/*
 *          GPAC - Multimedia Framework C SDK
 *
 *          Copyright (c) by  Falco (Ivan Vecera) 2006
 *                  All rights reserved
 *
 *  This file is part of GPAC / Media Tools sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#include <gpac/list.h>
#include <gpac/internal/vobsub.h>

typedef struct _tag_lang_type
{
        char id[3];
        char lang[4];
} lang_type;

static lang_type lang_table[] =
{
        {"--", "und" },
        {"aa", "aar" },
        {"ab", "abk" },
        {"af", "afr" },
        {"am", "amh" },
        {"ar", "ara" },
        {"as", "ast" },
        {"ay", "aym" },
        {"az", "aze" },
        {"ba", "bak" },
        {"be", "bel" },
        {"bg", "bul" },
        {"bh", "bih" },
        {"bi", "bis" },
        {"bn", "ben" },
        {"bo", "bod" }, // was "tib" (Tibetan)
        {"br", "bre" },
        {"ca", "cat" },
        {"cc", "und" },
        {"co", "cos" },
        {"cs", "ces" }, // was "cze" (Czech)
        {"cy", "cym" }, // was "wel" (Welsh)
        {"da", "dan" },
        {"de", "deu" }, // was "ger" (German)
        {"dz", "dzo" },
        {"el", "ell" }, // was "gre" (Greek, Modern (1453-))
        {"en", "eng" },
        {"eo", "epo" },
        {"es", "spa" },
        {"et", "est" },
        {"eu", "eus" }, // was "baq" (Basque)
        {"fa", "fas" }, // was "per" (Persian)
        {"fi", "fin" },
        {"fj", "fij" },
        {"fo", "fao" },
        {"fr", "fra" }, // was "fre" (French)
        {"fy", "fry" },
        {"ga", "gle" },
        {"gl", "glg" },
        {"gn", "grn" },
        {"gu", "guj" },
        {"ha", "hau" },
        {"he", "heb" },
        {"hi", "hin" },
        {"hr", "scr" },
        {"hu", "hun" },
        {"hy", "hye" }, // was "arm" (Armenian)
        {"ia", "ina" },
        {"id", "ind" },
        {"ik", "ipk" },
        {"is", "isl" }, // was "ice" (Icelandic)
        {"it", "ita" },
        {"iu", "iku" },
        {"ja", "jpn" },
        {"jv", "jav" },
        {"ka", "kat" }, // was "geo" (Georgian)
        {"kk", "kaz" },
        {"kl", "kal" },
        {"km", "khm" },
        {"kn", "kan" },
        {"ko", "kor" },
        {"ks", "kas" },
        {"ku", "kur" },
        {"ky", "kir" },
        {"la", "lat" },
        {"ln", "lin" },
        {"lo", "lao" },
        {"lt", "lit" },
        {"lv", "lav" },
        {"mg", "mlg" },
        {"mi", "mri" }, // was "mao" (Maori)
        {"mk", "mkd" }, // was "mac" (Macedonian)
        {"ml", "mlt" },
        {"mn", "mon" },
        {"mo", "mol" },
        {"mr", "mar" },
        {"ms", "msa" }, // was "may" (Malay)
        {"my", "mya" }, // was "bur" (Burmese)
        {"na", "nau" },
        {"ne", "nep" },
        {"nl", "nld" }, // was "dut" (Dutch; Flemish)
        {"no", "nor" },
        {"oc", "oci" },
        {"om", "orm" },
        {"or", "ori" },
        {"pa", "pan" },
        {"pl", "pol" },
        {"ps", "pus" },
        {"pt", "por" },
        {"qu", "que" },
        {"rm", "roh" },
        {"rn", "run" },
        {"ro", "ron" }, // was "rum" (Romanian; Moldavian; Moldovan)
        {"ru", "rus" },
        {"rw", "kin" },
        {"sa", "san" },
        {"sd", "snd" },
        {"sg", "sag" },
        {"sh", "scr" },
        {"si", "sin" },
        {"sk", "slk" }, // was "slo" (Slovak)
        {"sl", "slv" },
        {"sm", "smo" },
        {"sn", "sna" },
        {"so", "som" },
        {"sq", "sqi" }, // was "alb" (Albanian)
        {"sr", "srp" },
        {"ss", "ssw" },
        {"st", "sot" },
        {"su", "sun" },
        {"sv", "swe" },
        {"sw", "swa" },
        {"ta", "tam" },
        {"te", "tel" },
        {"tg", "tgk" },
        {"th", "tha" },
        {"ti", "tir" },
        {"tk", "tuk" },
        {"tl", "tgl" },
        {"tn", "tsn" },
        {"to", "tog" },
        {"tr", "tur" },
        {"ts", "tso" },
        {"tt", "tat" },
        {"tw", "twi" },
        {"ug", "uig" },
        {"uk", "ukr" },
        {"ur", "urd" },
        {"uz", "uzb" },
        {"vi", "vie" },
        {"vo", "vol" },
        {"wo", "wol" },
        {"xh", "xho" },
        {"yi", "yid" },
        {"yo", "yor" },
        {"za", "zha" },
        {"zh", "zho" }, // was "chi" (Chinese)
        {"zu", "zul" }
};



s32 vobsub_lang_name(u16 id)
{
        u16 lang_id;
        s32 i, count;

        count = (sizeof(lang_table) / sizeof(lang_table[0]));

        for (i = 0; i < count; i++) {
                lang_id = (lang_table[i].id[0]<<8) | lang_table[i].id[1];

                if (id == lang_id) {
                        return i;
                }
        }

        return 0; /* Undefined - und */
}

char *vobsub_lang_id(char *name)
{
        s32 i, count;

        count = (sizeof(lang_table) / sizeof(lang_table[0]));

        for (i = 0; i < count; i++) {
                if (!stricmp(lang_table[i].lang, name)) {
                        return lang_table[i].id;
                }
        }

        return "--"; /* Undefined */
}

static char *strltrim(char *str)
{
        if (str == NULL) {
                return NULL;
        }

        while (*str) {
                if (!isspace(*str)) {
                        return str;
                }
                str++;
        }

        return str;
}

static char *strrtrim(char *str)
{
        char *end;

        if (str == NULL) {
                return NULL;
        }

        end = str + strlen(str);

        while (end-- > str) {
                if (!isspace(*end)) {
                        return str;
                }
                *end = '\0';
        }

        return str;
}

static char *strtrim(char *str)
{
        return strltrim(strrtrim(str));
}

GF_Err vobsub_read_idx(FILE *file, vobsub_file *vobsub, s32 *version)
{
        char  strbuf[256];
        char *str, *pos, *entry;
        s32   line, id =-1, delay = 0;
        Bool  error = 0;

        for (line = 0; !error && fgets(strbuf, sizeof(strbuf), file); line++)
        {
                str = strtrim(strbuf);

                if (line == 0)
                {
                        char *buf = "VobSub index file, v";

                        pos = strstr(str, buf);
                        if (pos == NULL || sscanf(pos + strlen(buf), "%d", version) != 1 || *version > VOBSUBIDXVER)
                        {
                                error = 1;
                                continue;
                        }
                }
                else if (strlen(str) == 0)
                {
                        continue;
                }
                else if (str[0] == '#')
                {
                        continue;
                }

                pos = strchr(str, ':');
                if (pos == NULL || pos == str)
                {
                        continue;
                }

                entry = str;
                *pos  = '\0';

                str = strtrim(pos + 1);
                if (strlen(str) == 0)
                {
                        continue;
                }

                if (stricmp(entry, "size") == 0)
                {
                        s32 w, h;
                        if (sscanf(str, "%dx%d", &w, &h) != 2)
                        {
                                error = 1;
                        }
                        vobsub->width  = w;
                        vobsub->height = h;
                }
                else if (stricmp(entry, "palette") == 0)
                {
                        s32 c;
                        u8  palette[16][4];

                        if (sscanf(str, "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x",
                                   (u32 *) &palette[0], (u32 *) &palette[1], (u32 *) &palette[2], (u32 *) &palette[3],
                                   (u32 *) &palette[4], (u32 *) &palette[5], (u32 *) &palette[6], (u32 *) &palette[7],
                                   (u32 *) &palette[8], (u32 *) &palette[9], (u32 *) &palette[10], (u32 *) &palette[11],
                                   (u32 *) &palette[12],(u32 *) &palette[13],(u32 *) &palette[14], (u32 *) &palette[15]) != 16)
                        {
                                error = 1;
                                continue;
                        }

                        for (c = 0; c < 16; c++)
                        {
                                u8 r, g, b;

                                r = palette[c][2];
                                g = palette[c][1];
                                b = palette[c][0];
                                vobsub->palette[c][0] = 0;
                                vobsub->palette[c][1] = (( 66 * r + 129 * g +  25 * b + 128 +  4096) >> 8) & 0xff;
                                vobsub->palette[c][2] = ((112 * r -  94 * g -  18 * b + 128 + 32768) >> 8) & 0xff;
                                vobsub->palette[c][3] = ((-38 * r -  74 * g + 112 * b + 128 + 32768) >> 8) & 0xff;
                        }
                }
                else if (stricmp(entry, "id") == 0)
                {
                        char *buf = "index:";
                        s32   lang_id;

                        strlwr(str);
                        lang_id = ((str[0] & 0xff) << 8) | (str[1] & 0xff);

                        pos = strstr(str, buf);
                        if (pos == NULL)
                        {
                                error = 1;
                                continue;
                        }

                        if (sscanf(pos + strlen(buf), "%d", &id) != 1 || id < 0 || id >= 32)
                        {
                                error = 1;
                                continue;
                        }

                        vobsub->langs[id].id   = lang_id;
                        vobsub->langs[id].name = lang_table[vobsub_lang_name((u16)lang_id)].lang;

                        vobsub->langs[id].subpos = gf_list_new();
                        if (vobsub->langs[id].subpos == NULL)
                        {
                                error = 1;
                                continue;
                        }

                        delay = 0;
                        vobsub->num_langs++;
                }
                else if (id >= 0 && stricmp(entry, "delay") == 0)
                {
                        s32  hh, mm, ss, ms;
                        char c;
                        s32  sign = (str[0] == '-') ? -1 : 1;

                        pos = str;
                        while (*pos == '-' || *pos == '+') pos++;

                        if (sscanf(pos, "%d%c%d%c%d%c%d", &hh, &c, &mm, &c, &ss, &c, &ms) != 7)
                        {
                                error = 1;
                                continue;
                        }

                        delay += (hh*60*60*1000 + mm*60*1000 + ss*1000 + ms) * sign;
                }
                else if (id >= 0 && stricmp(entry, "timestamp") == 0)
                {
                        vobsub_pos *vspos;
                        s32         sign;
                        char        c;
                        s32         hh, mm, ss, ms;
                        char       *buf = "filepos:";

                        vspos = (vobsub_pos*)gf_calloc(1, sizeof(vobsub_pos));
                        if (vspos == NULL) {
                                error = 1;
                                continue;
                        }

                        sign = (str[0] == '-') ? -1 : 1;
                        while (*str == '-' || *str == '+') str++;

                        if (sscanf(str, "%d%c%d%c%d%c%d", &hh, &c, &mm, &c, &ss, &c, &ms) != 7)
                        {
                                gf_free(vspos);
                                error = 1;
                                continue;
                        }

                        vspos->start = (((hh*60 + mm)*60 + ss)*1000 + ms) * sign + delay;

                        pos = strstr(str, buf);
                        if (pos == NULL)
                        {
                                gf_free(vspos);
                                error = 1;
                                continue;
                        }

                        if (sscanf(pos + strlen(buf), LLX, &vspos->filepos) != 1)
                        {
                                gf_free(vspos);
                                error = 1;
                                continue;
                        }

                        if (delay < 0 && gf_list_count(vobsub->langs[id].subpos) > 0)
                        {
                                vobsub_pos *pos;

                                pos = (vobsub_pos*)gf_list_get(vobsub->langs[id].subpos, gf_list_count(vobsub->langs[id].subpos) - 1);
                                if (vspos->start < pos->start)
                                {
                                        delay += (s32)(pos->start - vspos->start);
                                        vspos->start = pos->start;
                                }
                        }

                        if (gf_list_add(vobsub->langs[id].subpos, vspos) != GF_OK)
                        {
                                gf_free(vspos);
                                error = 1;
                                continue;
                        }
                }
        }

        return error ? GF_CORRUPTED_DATA : GF_OK;
}

void vobsub_free(vobsub_file *vobsub)
{
        s32 c;

        if (vobsub == NULL)
        {
                return;
        }

        for (c = 0; c < 32; c++)
        {
                if (vobsub->langs[c].subpos)
                {
                        GF_List    *list = vobsub->langs[c].subpos;
                        vobsub_pos *vspos;
                        u32         pos = 0;

                        do
                        {
                                vspos = (vobsub_pos*)gf_list_enum(list, &pos);
                                gf_free(vspos);
                        }
                        while (vspos != NULL);
                }
        }
}

GF_Err vobsub_get_subpic_duration(char *_data, u32 psize, u32 dsize, u32 *duration)
{
        u32 i, dcsq_stm, nxt_dcsq, start_stm, stop_stm;
        unsigned char *data = (unsigned char *)_data;
        start_stm = 0;
        stop_stm  = 0;
        nxt_dcsq  = dsize;

        do {
                i = nxt_dcsq;
                dcsq_stm = (data[i+0] << 8) | data[i+1];
                nxt_dcsq = (data[i+2] << 8) | data[i+3];
                i += 4;

                if (nxt_dcsq > psize || nxt_dcsq < dsize) {
                        return GF_CORRUPTED_DATA;
                }

                while (1) {
                        u8  cmd;
                        int len;

                        cmd = data[i++];
                        switch (cmd)
                        {
                        case 0x00:
                                len = 0;
                                break;
                        case 0x01:
                                len = 0;
                                break;
                        case 0x02:
                                len = 0;
                                break;
                        case 0x03:
                                len = 2;
                                break;
                        case 0x04:
                                len = 2;
                                break;
                        case 0x05:
                                len = 6;
                                break;
                        case 0x06:
                                len = 4;
                                break;
                        default:
                                len = 0;
                                break;
                        }

                        if (i + len > psize) {
                                return GF_CORRUPTED_DATA;
                        }

                        i += len;

                        if (cmd == 0x00 || cmd == 0x01) {
                                /* start normal or forced displaying */
                                start_stm = dcsq_stm * 1024;
                        } else if (cmd == 0x02) {
                                /* stop displaying */
                                stop_stm = dcsq_stm * 1024;
                        } else if (cmd > 0x06) {
                                /* unknown command or end of control block */
                                break;
                        }
                }
        } while (i <= nxt_dcsq && i < psize);

        *duration = stop_stm - start_stm;

        return GF_OK;
}

GF_Err vobsub_packetize_subpicture(FILE *fsub, u64 pts, char *data, u32 dataSize)
{
        u8      buf[0x800], ptsbuf[5];
        u8      *p;
        int     put_pts = 1;

        /* Build PTS buffer */
        ptsbuf[0] = (u8)(((pts >> 29) & 0x0e) | 0x21);
        ptsbuf[1] = (u8)(((pts >> 22) & 0xff));
        ptsbuf[2] = (u8)(((pts >> 14) & 0xfe) | 0x01);
        ptsbuf[3] = (u8)(((pts >>  7) & 0xff));
        ptsbuf[4] = (u8)(((pts <<  1) & 0xfe) | 0x01);

        while (dataSize > 0)
        {
                u32 padLen = 0;
                u32 dataLen = sizeof(buf);
                u32 packLen;

                /* Zerofill packet */
                memset(buf, 0, sizeof(buf));
                p = buf;

                /* Put pack header */
                *p++ = 0x00;
                *p++ = 0x00;
                *p++ = 0x01;
                *p++ = 0xba;
                *p++ = 0x40;

                /* Jump to PES header */
                p += 9;

                /* Put PES header */
                *p++ = 0x00;
                *p++ = 0x00;
                *p++ = 0x01;
                *p++ = 0xbd;

                /* Compute max size of content */
                dataLen -= 14; /* Pack header */
                dataLen -=  4; /* Start code + Stream ID */
                dataLen -=  2; /* PES packet size */
                dataLen -=  3; /* PES header extension */
                dataLen -= put_pts ? 5 : 0; /* PTS */
                dataLen -=  1; /* Substream ID */

                /* Check if the subpicture data fits in packet */
                if (dataSize <= dataLen) {
                        padLen  = dataLen - dataSize;
                        dataLen = dataSize;
                }

                /* Compute and put packet size (PES header extension + PTS + Substream ID + data + padding) */
                packLen = 3 + (put_pts ? 5 : 0) + 1 + dataLen + ((padLen < 6) ? padLen : 0);
                *p++ = (packLen >> 8) & 0xff;
                *p++ = packLen & 0xff;

                /* Put PES header extension */
                *p++ = 0x80;
                *p++ = put_pts ? 0x80 : 0x00;
                *p++ = (put_pts ? 5 : 0) + ((padLen < 6) ? padLen : 0);

                /* Put PTS */
                if (put_pts) {
                        *p++ = ptsbuf[0];
                        *p++ = ptsbuf[1];
                        *p++ = ptsbuf[2];
                        *p++ = ptsbuf[3];
                        *p++ = ptsbuf[4];
                }

                /* Skip padding bytes */
                if (padLen < 6) {
                        p += padLen;
                }

                /* Put Substream ID */
                *p++ = 0x20;

                /* Copy data into packet buffer */
                memcpy(p, data, dataLen);
                p += dataLen;

                /* Put padding bytes if padding len >= 6 */
                if (padLen >= 6) {
                        padLen -= 6;
                        *p++ = 0x00;
                        *p++ = 0x00;
                        *p++ = 0x01;
                        *p++ = 0xbe;
                        *p++ = (padLen >> 8) & 0xff;
                        *p++ = padLen & 0xff;
                        memset(p, 0, padLen);
                }

                /* Write packet into file */
                if (gf_fwrite(buf, sizeof(buf), 1, fsub) != 1) {
                        return GF_IO_ERR;
                }

                /* Move data pointer... */
                data += dataLen;
                dataSize -= dataLen;

                /* Next packet (if any) will not contain PTS */
                put_pts = 0;
        }

        return GF_OK;
}

/* [<][>][^][v][top][bottom][index][help] */