0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 20:03:10 +02:00
mpv/sub/sd_microdvd.c
wm4 e19ffa02aa sub: turn subassconvert_ functions into sub converters
This means subassconvert.c is split in sd_srt.c and sd_microdvd.c. Now
this code is involved in the sub conversion chain like sd_movtext is.
The invocation of the converter in sd_ass.c is removed.

This requires some other changes to make the new sub converter code work
with loading external subtitles. Until now, subtitles loaded via
subreader.c was assumed to be in plaintext, or for some formats, in ASS
(except in -no-ass mode). Then these were added to an ASS_Track. Change
this so that subtitles are always in their original format (as far as
decoders/converters for them are available), and turn every sub event
read by subreader.c as packet to the dec_sub.c subtitle chain.

This removes differences between external/demuxed and -ass/-no-ass code
paths further.
2013-06-03 22:40:02 +02:00

338 lines
8.7 KiB
C

/*
* Subtitles converter to SSA/ASS in order to allow special formatting
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdbool.h>
#include <ctype.h>
#include <libavutil/common.h>
#include "core/mp_msg.h"
#include "core/bstr.h"
#include "sd.h"
struct line {
char *buf;
int bufsize;
int len;
};
#ifdef __GNUC__
static void append_text(struct line *dst, char *fmt, ...) __attribute__ ((format(printf, 2, 3)));
#endif
static void append_text(struct line *dst, char *fmt, ...)
{
va_list va;
va_start(va, fmt);
int ret = vsnprintf(dst->buf + dst->len, dst->bufsize - dst->len, fmt, va);
if (ret < 0)
goto out;
dst->len += ret;
if (dst->len > dst->bufsize)
dst->len = dst->bufsize;
out:
va_end(va);
}
static int indexof(const char *s, int c)
{
char *f = strchr(s, c);
return f ? (f - s) : -1;
}
/*
* MicroDVD
*
* Based on the specifications found here:
* https://trac.videolan.org/vlc/ticket/1825#comment:6
*/
struct microdvd_tag {
char key;
int persistent;
uint32_t data1;
uint32_t data2;
struct bstr data_string;
};
#define MICRODVD_PERSISTENT_OFF 0
#define MICRODVD_PERSISTENT_ON 1
#define MICRODVD_PERSISTENT_OPENED 2
// Color, Font, Size, cHarset, stYle, Position, cOordinate
#define MICRODVD_TAGS "cfshyYpo"
static void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag)
{
int tag_index = indexof(MICRODVD_TAGS, tag.key);
if (tag_index < 0)
return;
memcpy(&tags[tag_index], &tag, sizeof(tag));
}
// italic, bold, underline, strike-through
#define MICRODVD_STYLES "ibus"
static char *microdvd_load_tags(struct microdvd_tag *tags, char *s)
{
while (*s == '{') {
char *start = s;
char tag_char = *(s + 1);
struct microdvd_tag tag = {0};
if (!tag_char || *(s + 2) != ':')
break;
s += 3;
switch (tag_char) {
/* Style */
case 'Y':
tag.persistent = MICRODVD_PERSISTENT_ON;
case 'y':
while (*s && *s != '}') {
int style_index = indexof(MICRODVD_STYLES, *s);
if (style_index >= 0)
tag.data1 |= (1 << style_index);
s++;
}
if (*s != '}')
break;
/* We must distinguish persistent and non-persistent styles
* to handle this kind of style tags: {y:ib}{Y:us} */
tag.key = tag_char;
break;
/* Color */
case 'C':
tag.persistent = MICRODVD_PERSISTENT_ON;
case 'c':
tag.data1 = strtol(s, &s, 16) & 0x00ffffff;
if (*s != '}')
break;
tag.key = 'c';
break;
/* Font name */
case 'F':
tag.persistent = MICRODVD_PERSISTENT_ON;
case 'f':
{
int len = indexof(s, '}');
if (len < 0)
break;
tag.data_string.start = s;
tag.data_string.len = len;
s += len;
tag.key = 'f';
break;
}
/* Font size */
case 'S':
tag.persistent = MICRODVD_PERSISTENT_ON;
case 's':
tag.data1 = strtol(s, &s, 10);
if (*s != '}')
break;
tag.key = 's';
break;
/* Charset */
case 'H':
{
//TODO: not yet handled, just parsed.
int len = indexof(s, '}');
if (len < 0)
break;
tag.data_string.start = s;
tag.data_string.len = len;
s += len;
tag.key = 'h';
break;
}
/* Position */
case 'P':
tag.persistent = MICRODVD_PERSISTENT_ON;
tag.data1 = (*s++ == '1');
if (*s != '}')
break;
tag.key = 'p';
break;
/* Coordinates */
case 'o':
tag.persistent = MICRODVD_PERSISTENT_ON;
tag.data1 = strtol(s, &s, 10);
if (*s != ',')
break;
s++;
tag.data2 = strtol(s, &s, 10);
if (*s != '}')
break;
tag.key = 'o';
break;
default: /* Unknown tag, we consider it to be text */
break;
}
if (tag.key == 0)
return start;
microdvd_set_tag(tags, tag);
s++;
}
return s;
}
static void microdvd_open_tags(struct line *new_line, struct microdvd_tag *tags)
{
for (int i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED)
continue;
switch (tags[i].key) {
case 'Y':
case 'y':
for (int sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++)
if (tags[i].data1 & (1 << sidx))
append_text(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]);
break;
case 'c':
append_text(new_line, "{\\c&H%06X&}", tags[i].data1);
break;
case 'f':
append_text(new_line, "{\\fn%.*s}", BSTR_P(tags[i].data_string));
break;
case 's':
append_text(new_line, "{\\fs%d}", tags[i].data1);
break;
case 'p':
if (tags[i].data1 == 0)
append_text(new_line, "{\\an8}");
break;
case 'o':
append_text(new_line, "{\\pos(%d,%d)}",
tags[i].data1, tags[i].data2);
break;
}
if (tags[i].persistent == MICRODVD_PERSISTENT_ON)
tags[i].persistent = MICRODVD_PERSISTENT_OPENED;
}
}
static void microdvd_close_no_persistent_tags(struct line *new_line,
struct microdvd_tag *tags)
{
int i;
for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) {
if (tags[i].persistent != MICRODVD_PERSISTENT_OFF)
continue;
switch (tags[i].key) {
case 'y':
for (int sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--)
if (tags[i].data1 & (1 << sidx))
append_text(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]);
break;
case 'c':
append_text(new_line, "{\\c}");
break;
case 'f':
append_text(new_line, "{\\fn}");
break;
case 's':
append_text(new_line, "{\\fs}");
break;
}
tags[i].key = 0;
}
}
static void convert_microdvd(const char *orig, char *dest, int dest_buffer_size)
{
/* line is not const to avoid warnings with strtol, etc.
* orig content won't be changed */
char *line = (char *)orig;
struct line new_line = {
.buf = dest,
.bufsize = dest_buffer_size,
};
struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
while (*line) {
line = microdvd_load_tags(tags, line);
microdvd_open_tags(&new_line, tags);
while (*line && *line != '|')
new_line.buf[new_line.len++] = *line++;
if (*line == '|') {
microdvd_close_no_persistent_tags(&new_line, tags);
append_text(&new_line, "\\N");
line++;
}
}
new_line.buf[new_line.len] = 0;
}
static bool supports_format(const char *format)
{
return format && strcmp(format, "microdvd") == 0;
}
static int init(struct sd *sd)
{
sd->output_codec = "ass-text";
return 0;
}
static void decode(struct sd *sd, struct demux_packet *packet)
{
char dest[SD_MAX_LINE_LEN];
// Assume input buffer is padded with 0
convert_microdvd(packet->buffer, dest, sizeof(dest));
sd_conv_add_packet(sd, dest, strlen(dest), packet->pts, packet->duration);
}
const struct sd_functions sd_microdvd = {
.supports_format = supports_format,
.init = init,
.decode = decode,
.get_converted = sd_conv_def_get_converted,
.reset = sd_conv_def_reset,
};