mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
lavc: add MicroDVD decoder.
Based on my MicroDVD->ASS conversion code from MPlayer (sub/subassconvert.c).
This commit is contained in:
parent
e40981b8c4
commit
423b827668
@ -27,6 +27,7 @@ version next:
|
|||||||
- ffmpeg -benchmark_all option
|
- ffmpeg -benchmark_all option
|
||||||
- super2xsai filter ported from libmpcodecs
|
- super2xsai filter ported from libmpcodecs
|
||||||
- add libavresample audio conversion library for compatibility
|
- add libavresample audio conversion library for compatibility
|
||||||
|
- MicroDVD decoder
|
||||||
|
|
||||||
|
|
||||||
version 0.10:
|
version 0.10:
|
||||||
|
@ -837,7 +837,7 @@ performance on systems without hardware floating point support).
|
|||||||
@item SSA/ASS @tab X @tab X @tab X @tab X
|
@item SSA/ASS @tab X @tab X @tab X @tab X
|
||||||
@item DVB @tab X @tab X @tab X @tab X
|
@item DVB @tab X @tab X @tab X @tab X
|
||||||
@item DVD @tab X @tab X @tab X @tab X
|
@item DVD @tab X @tab X @tab X @tab X
|
||||||
@item MicroDVD @tab X @tab X @tab @tab
|
@item MicroDVD @tab X @tab X @tab @tab X
|
||||||
@item PGS @tab @tab @tab @tab X
|
@item PGS @tab @tab @tab @tab X
|
||||||
@item SubRip (SRT) @tab X @tab X @tab X @tab X
|
@item SubRip (SRT) @tab X @tab X @tab X @tab X
|
||||||
@item XSUB @tab @tab @tab X @tab X
|
@item XSUB @tab @tab @tab X @tab X
|
||||||
|
@ -239,6 +239,7 @@ OBJS-$(CONFIG_MACE3_DECODER) += mace.o
|
|||||||
OBJS-$(CONFIG_MACE6_DECODER) += mace.o
|
OBJS-$(CONFIG_MACE6_DECODER) += mace.o
|
||||||
OBJS-$(CONFIG_MDEC_DECODER) += mdec.o mpeg12.o mpeg12data.o \
|
OBJS-$(CONFIG_MDEC_DECODER) += mdec.o mpeg12.o mpeg12data.o \
|
||||||
mpegvideo.o error_resilience.o
|
mpegvideo.o error_resilience.o
|
||||||
|
OBJS-$(CONFIG_MICRODVD_DECODER) += microdvddec.o ass.o
|
||||||
OBJS-$(CONFIG_MIMIC_DECODER) += mimic.o
|
OBJS-$(CONFIG_MIMIC_DECODER) += mimic.o
|
||||||
OBJS-$(CONFIG_MJPEG_DECODER) += mjpegdec.o mjpeg.o
|
OBJS-$(CONFIG_MJPEG_DECODER) += mjpegdec.o mjpeg.o
|
||||||
OBJS-$(CONFIG_MJPEG_ENCODER) += mjpegenc.o mjpeg.o \
|
OBJS-$(CONFIG_MJPEG_ENCODER) += mjpegenc.o mjpeg.o \
|
||||||
|
@ -397,6 +397,7 @@ void avcodec_register_all(void)
|
|||||||
REGISTER_ENCDEC (ASS, ass);
|
REGISTER_ENCDEC (ASS, ass);
|
||||||
REGISTER_ENCDEC (DVBSUB, dvbsub);
|
REGISTER_ENCDEC (DVBSUB, dvbsub);
|
||||||
REGISTER_ENCDEC (DVDSUB, dvdsub);
|
REGISTER_ENCDEC (DVDSUB, dvdsub);
|
||||||
|
REGISTER_DECODER (MICRODVD, microdvd);
|
||||||
REGISTER_DECODER (PGSSUB, pgssub);
|
REGISTER_DECODER (PGSSUB, pgssub);
|
||||||
REGISTER_ENCDEC (SRT, srt);
|
REGISTER_ENCDEC (SRT, srt);
|
||||||
REGISTER_ENCDEC (XSUB, xsub);
|
REGISTER_ENCDEC (XSUB, xsub);
|
||||||
|
317
libavcodec/microdvddec.c
Normal file
317
libavcodec/microdvddec.c
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Clément Bœsch
|
||||||
|
*
|
||||||
|
* This file is part of FFmpeg.
|
||||||
|
*
|
||||||
|
* FFmpeg 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.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* FFmpeg 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 FFmpeg; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* MicroDVD subtitle decoder
|
||||||
|
*
|
||||||
|
* Based on the specifications found here:
|
||||||
|
* https://trac.videolan.org/vlc/ticket/1825#comment:6
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libavutil/avstring.h"
|
||||||
|
#include "libavutil/parseutils.h"
|
||||||
|
#include "libavutil/bprint.h"
|
||||||
|
#include "avcodec.h"
|
||||||
|
#include "ass.h"
|
||||||
|
|
||||||
|
static int indexof(const char *s, int c)
|
||||||
|
{
|
||||||
|
char *f = strchr(s, c);
|
||||||
|
return f ? (f - s) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct microdvd_tag {
|
||||||
|
char key;
|
||||||
|
int persistent;
|
||||||
|
uint32_t data1;
|
||||||
|
uint32_t data2;
|
||||||
|
char *data_string;
|
||||||
|
int data_string_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
#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 = 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 = 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's text */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag.key == 0)
|
||||||
|
return start;
|
||||||
|
|
||||||
|
microdvd_set_tag(tags, tag);
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void microdvd_open_tags(AVBPrint *new_line, struct microdvd_tag *tags)
|
||||||
|
{
|
||||||
|
int i, sidx;
|
||||||
|
for (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 (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++)
|
||||||
|
if (tags[i].data1 & (1 << sidx))
|
||||||
|
av_bprintf(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
av_bprintf(new_line, "{\\c&H%06X&}", tags[i].data1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
av_bprintf(new_line, "{\\fn%.*s}",
|
||||||
|
tags[i].data_string_len, tags[i].data_string);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
av_bprintf(new_line, "{\\fs%d}", tags[i].data1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'p':
|
||||||
|
if (tags[i].data1 == 0)
|
||||||
|
av_bprintf(new_line, "{\\an8}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'o':
|
||||||
|
av_bprintf(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(AVBPrint *new_line,
|
||||||
|
struct microdvd_tag *tags)
|
||||||
|
{
|
||||||
|
int i, sidx;
|
||||||
|
|
||||||
|
for (i = sizeof(MICRODVD_TAGS) - 2; i; i--) {
|
||||||
|
if (tags[i].persistent != MICRODVD_PERSISTENT_OFF)
|
||||||
|
continue;
|
||||||
|
switch (tags[i].key) {
|
||||||
|
|
||||||
|
case 'y':
|
||||||
|
for (sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--)
|
||||||
|
if (tags[i].data1 & (1 << sidx))
|
||||||
|
av_bprintf(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
av_bprintf(new_line, "{\\c}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
av_bprintf(new_line, "{\\fn}");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
av_bprintf(new_line, "{\\fs}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tags[i].key = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int microdvd_decode_frame(AVCodecContext *avctx,
|
||||||
|
void *data, int *got_sub_ptr, AVPacket *avpkt)
|
||||||
|
{
|
||||||
|
AVSubtitle *sub = data;
|
||||||
|
AVBPrint new_line;
|
||||||
|
char *decoded_sub;
|
||||||
|
char *line = avpkt->data;
|
||||||
|
char *end = avpkt->data + avpkt->size;
|
||||||
|
int64_t frame_start = avpkt->pts;
|
||||||
|
int64_t frame_end = avpkt->pts + avpkt->duration;
|
||||||
|
int ts_start = av_rescale_q(frame_start, avctx->time_base, (AVRational){1,100});
|
||||||
|
int ts_end = av_rescale_q(frame_end, avctx->time_base, (AVRational){1,100});
|
||||||
|
struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
|
||||||
|
|
||||||
|
if (avpkt->size <= 0)
|
||||||
|
return avpkt->size;
|
||||||
|
|
||||||
|
av_bprint_init(&new_line, 0, 2048);
|
||||||
|
|
||||||
|
// skip {frame_start}{frame_end}
|
||||||
|
line = strchr(line, '}'); if (!line) goto end; line++;
|
||||||
|
line = strchr(line, '}'); if (!line) goto end; line++;
|
||||||
|
|
||||||
|
// subtitle content
|
||||||
|
while (line < end && *line) {
|
||||||
|
|
||||||
|
// parse MicroDVD tags, and open them in ASS
|
||||||
|
line = microdvd_load_tags(tags, line);
|
||||||
|
microdvd_open_tags(&new_line, tags);
|
||||||
|
|
||||||
|
// simple copy until EOL or forced carriage return
|
||||||
|
while (line < end && *line && *line != '|') {
|
||||||
|
av_bprint_chars(&new_line, *line, 1);
|
||||||
|
line++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// line split
|
||||||
|
if (line < end && *line == '|') {
|
||||||
|
microdvd_close_no_persistent_tags(&new_line, tags);
|
||||||
|
av_bprintf(&new_line, "\\N");
|
||||||
|
line++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
av_bprint_finalize(&new_line, &decoded_sub);
|
||||||
|
if (*decoded_sub)
|
||||||
|
ff_ass_add_rect(sub, decoded_sub, ts_start, ts_end, 0);
|
||||||
|
av_free(decoded_sub);
|
||||||
|
|
||||||
|
*got_sub_ptr = sub->num_rects > 0;
|
||||||
|
return avpkt->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
AVCodec ff_microdvd_decoder = {
|
||||||
|
.name = "microdvd",
|
||||||
|
.long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle"),
|
||||||
|
.type = AVMEDIA_TYPE_SUBTITLE,
|
||||||
|
.id = CODEC_ID_MICRODVD,
|
||||||
|
.init = ff_ass_subtitle_header_default,
|
||||||
|
.decode = microdvd_decode_frame,
|
||||||
|
};
|
@ -27,7 +27,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define LIBAVCODEC_VERSION_MAJOR 54
|
#define LIBAVCODEC_VERSION_MAJOR 54
|
||||||
#define LIBAVCODEC_VERSION_MINOR 15
|
#define LIBAVCODEC_VERSION_MINOR 16
|
||||||
#define LIBAVCODEC_VERSION_MICRO 100
|
#define LIBAVCODEC_VERSION_MICRO 100
|
||||||
|
|
||||||
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
|
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
|
||||||
|
Loading…
Reference in New Issue
Block a user