FFmpeg
jacosubdec.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012 Clément Bœsch
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /**
22  * @file
23  * JACOsub subtitle decoder
24  * @see http://unicorn.us.com/jacosub/jscripts.html
25  */
26 
27 #include <time.h>
28 #include "ass.h"
29 #include "internal.h"
30 #include "jacosub.h"
31 #include "libavutil/avstring.h"
32 #include "libavutil/bprint.h"
34 
35 #undef time
36 
37 static int insert_text(AVBPrint *dst, const char *in, const char *arg)
38 {
39  av_bprintf(dst, "%s", arg);
40  return 0;
41 }
42 
43 static int insert_datetime(AVBPrint *dst, const char *in, const char *arg)
44 {
45  char buf[16] = {0};
46  time_t now = time(0);
47  struct tm ltime;
48 
49  localtime_r(&now, &ltime);
50  if (strftime(buf, sizeof(buf), arg, &ltime))
51  av_bprintf(dst, "%s", buf);
52  return 0;
53 }
54 
55 static int insert_color(AVBPrint *dst, const char *in, const char *arg)
56 {
57  return 1; // skip id
58 }
59 
60 static int insert_font(AVBPrint *dst, const char *in, const char *arg)
61 {
62  return 1; // skip id
63 }
64 
65 static const struct {
66  const char *from;
67  const char *arg;
68  int (*func)(AVBPrint *dst, const char *in, const char *arg);
69 } ass_codes_map[] = {
70  {"\\~", "~", insert_text}, // tilde doesn't need escaping
71  {"~", "{\\h}", insert_text}, // hard space
72  {"\\n", "\\N", insert_text}, // newline
73  {"\\D", "%d %b %Y", insert_datetime}, // current date
74  {"\\T", "%H:%M", insert_datetime}, // current time
75  {"\\N", "{\\r}", insert_text}, // reset to default style
76  {"\\I", "{\\i1}", insert_text}, // italic on
77  {"\\i", "{\\i0}", insert_text}, // italic off
78  {"\\B", "{\\b1}", insert_text}, // bold on
79  {"\\b", "{\\b0}", insert_text}, // bold off
80  {"\\U", "{\\u1}", insert_text}, // underline on
81  {"\\u", "{\\u0}", insert_text}, // underline off
82  {"\\C", "", insert_color}, // TODO: color
83  {"\\F", "", insert_font}, // TODO: font
84 };
85 
86 enum {
87  ALIGN_VB = 1<<0, // vertical bottom, default
88  ALIGN_VM = 1<<1, // vertical middle
89  ALIGN_VT = 1<<2, // vertical top
90  ALIGN_JC = 1<<3, // justify center, default
91  ALIGN_JL = 1<<4, // justify left
92  ALIGN_JR = 1<<5, // justify right
93 };
94 
95 static void jacosub_to_ass(AVCodecContext *avctx, AVBPrint *dst, const char *src)
96 {
97  int i, valign = 0, halign = 0;
98  char c = av_toupper(*src);
99  char directives[128] = {0};
100 
101  /* extract the optional directives */
102  if ((c >= 'A' && c <= 'Z') || c == '[') {
103  char *p = directives;
104  char *pend = directives + sizeof(directives) - 1;
105 
106  do *p++ = av_toupper(*src++);
107  while (*src && !jss_whitespace(*src) && p < pend);
108  *p = 0;
110  }
111 
112  /* handle directives (TODO: handle more of them, and more reliably) */
113  if (strstr(directives, "VB")) valign = ALIGN_VB;
114  else if (strstr(directives, "VM")) valign = ALIGN_VM;
115  else if (strstr(directives, "VT")) valign = ALIGN_VT;
116  if (strstr(directives, "JC")) halign = ALIGN_JC;
117  else if (strstr(directives, "JL")) halign = ALIGN_JL;
118  else if (strstr(directives, "JR")) halign = ALIGN_JR;
119  if (valign || halign) {
120  if (!valign) valign = ALIGN_VB;
121  if (!halign) halign = ALIGN_JC;
122  switch (valign | halign) {
123  case ALIGN_VB | ALIGN_JL: av_bprintf(dst, "{\\an1}"); break; // bottom left
124  case ALIGN_VB | ALIGN_JC: av_bprintf(dst, "{\\an2}"); break; // bottom center
125  case ALIGN_VB | ALIGN_JR: av_bprintf(dst, "{\\an3}"); break; // bottom right
126  case ALIGN_VM | ALIGN_JL: av_bprintf(dst, "{\\an4}"); break; // middle left
127  case ALIGN_VM | ALIGN_JC: av_bprintf(dst, "{\\an5}"); break; // middle center
128  case ALIGN_VM | ALIGN_JR: av_bprintf(dst, "{\\an6}"); break; // middle right
129  case ALIGN_VT | ALIGN_JL: av_bprintf(dst, "{\\an7}"); break; // top left
130  case ALIGN_VT | ALIGN_JC: av_bprintf(dst, "{\\an8}"); break; // top center
131  case ALIGN_VT | ALIGN_JR: av_bprintf(dst, "{\\an9}"); break; // top right
132  }
133  }
134 
135  /* process timed line */
136  while (*src && *src != '\n') {
137 
138  /* text continue on the next line */
139  if (src[0] == '\\' && src[1] == '\n') {
140  src += 2;
141  while (jss_whitespace(*src))
142  src++;
143  continue;
144  }
145 
146  /* special character codes */
147  for (i = 0; i < FF_ARRAY_ELEMS(ass_codes_map); i++) {
148  const char *from = ass_codes_map[i].from;
149  const char *arg = ass_codes_map[i].arg;
150  size_t codemap_len = strlen(from);
151 
152  if (!strncmp(src, from, codemap_len)) {
153  src += codemap_len;
154  src += ass_codes_map[i].func(dst, src, arg);
155  break;
156  }
157  }
158 
159  /* simple char copy */
161  av_bprintf(dst, "%c", *src++);
162  }
163 }
164 
166  void *data, int *got_sub_ptr, AVPacket *avpkt)
167 {
168  int ret;
169  AVSubtitle *sub = data;
170  const char *ptr = avpkt->data;
171  FFASSDecoderContext *s = avctx->priv_data;
172 
173  if (avpkt->size <= 0)
174  goto end;
175 
176  if (*ptr) {
177  AVBPrint buffer;
178 
179  // skip timers
180  ptr = jss_skip_whitespace(ptr);
181  ptr = strchr(ptr, ' '); if (!ptr) goto end; ptr++;
182  ptr = strchr(ptr, ' '); if (!ptr) goto end; ptr++;
183 
185  jacosub_to_ass(avctx, &buffer, ptr);
186  ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
188  if (ret < 0)
189  return ret;
190  }
191 
192 end:
193  *got_sub_ptr = sub->num_rects > 0;
194  return avpkt->size;
195 }
196 
198  .name = "jacosub",
199  .long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle"),
200  .type = AVMEDIA_TYPE_SUBTITLE,
201  .id = AV_CODEC_ID_JACOSUB,
203  .decode = jacosub_decode_frame,
204  .flush = ff_ass_decoder_flush,
205  .priv_data_size = sizeof(FFASSDecoderContext),
206  .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
207 };
AVSubtitle
Definition: avcodec.h:2289
func
int(* func)(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:68
ass_codes_map
static const struct @77 ass_codes_map[]
AVCodec
AVCodec.
Definition: codec.h:202
AVMEDIA_TYPE_SUBTITLE
@ AVMEDIA_TYPE_SUBTITLE
Definition: avutil.h:204
FF_CODEC_CAP_INIT_THREADSAFE
#define FF_CODEC_CAP_INIT_THREADSAFE
The codec does not modify any global variables in the init function, allowing to call the init functi...
Definition: internal.h:42
ff_ass_subtitle_header_default
int ff_ass_subtitle_header_default(AVCodecContext *avctx)
Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS with default style.
Definition: ass.c:96
ff_jacosub_decoder
const AVCodec ff_jacosub_decoder
Definition: jacosubdec.c:197
av_bprint_finalize
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:234
av_bprint_init
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:68
sub
static float sub(float src0, float src1)
Definition: dnn_backend_native_layer_mathbinary.c:31
ff_ass_add_rect
int ff_ass_add_rect(AVSubtitle *sub, const char *dialog, int readorder, int layer, const char *style, const char *speaker)
Add an ASS dialog to a subtitle.
Definition: ass.c:117
internal.h
AVPacket::data
uint8_t * data
Definition: packet.h:373
ALIGN_JL
@ ALIGN_JL
Definition: jacosubdec.c:91
data
const char data[16]
Definition: mxf.c:143
insert_datetime
static int insert_datetime(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:43
jacosub_to_ass
static void jacosub_to_ass(AVCodecContext *avctx, AVBPrint *dst, const char *src)
Definition: jacosubdec.c:95
JSS_MAX_LINESIZE
#define JSS_MAX_LINESIZE
Definition: jacosub.h:31
jacosub_decode_frame
static int jacosub_decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr, AVPacket *avpkt)
Definition: jacosubdec.c:165
AV_CODEC_ID_JACOSUB
@ AV_CODEC_ID_JACOSUB
Definition: codec_id.h:533
ALIGN_VT
@ ALIGN_VT
Definition: jacosubdec.c:89
ALIGN_JR
@ ALIGN_JR
Definition: jacosubdec.c:92
ass.h
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
s
#define s(width, name)
Definition: cbs_vp9.c:257
insert_text
static int insert_text(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:37
from
const char * from
Definition: jacosubdec.c:66
ALIGN_VB
@ ALIGN_VB
Definition: jacosubdec.c:87
arg
const char * arg
Definition: jacosubdec.c:67
time_internal.h
NULL
#define NULL
Definition: coverity.c:32
insert_font
static int insert_font(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:60
src
#define src
Definition: vp8dsp.c:255
time.h
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
jacosub.h
AVPacket::size
int size
Definition: packet.h:374
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:117
ALIGN_JC
@ ALIGN_JC
Definition: jacosubdec.c:90
localtime_r
#define localtime_r
Definition: time_internal.h:46
jss_whitespace
static av_always_inline int jss_whitespace(char c)
Definition: jacosub.h:33
jss_skip_whitespace
static const av_always_inline char * jss_skip_whitespace(const char *p)
Definition: jacosub.h:38
ff_ass_decoder_flush
void ff_ass_decoder_flush(AVCodecContext *avctx)
Helper to flush a text subtitles decoder making use of the FFASSDecoderContext.
Definition: ass.c:140
bprint.h
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:271
ALIGN_VM
@ ALIGN_VM
Definition: jacosubdec.c:88
av_toupper
static av_const int av_toupper(int c)
Locale-independent conversion of ASCII characters to uppercase.
Definition: avstring.h:236
AVCodec::name
const char * name
Name of the codec implementation.
Definition: codec.h:209
ret
ret
Definition: filter_design.txt:187
av_bprintf
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:93
AVCodecContext
main external API structure.
Definition: avcodec.h:383
buffer
the frame and frame reference mechanism is intended to as much as expensive copies of that data while still allowing the filters to produce correct results The data is stored in buffers represented by AVFrame structures Several references can point to the same frame buffer
Definition: filter_design.txt:49
AVCodecContext::priv_data
void * priv_data
Definition: avcodec.h:410
AVPacket
This structure stores compressed data.
Definition: packet.h:350
insert_color
static int insert_color(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:55
FFASSDecoderContext
Definition: ass.h:46
avstring.h
int
int
Definition: ffmpeg_filter.c:153