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 "codec_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  int *got_sub_ptr, const AVPacket *avpkt)
167 {
168  int ret;
169  const char *ptr = avpkt->data;
170  FFASSDecoderContext *s = avctx->priv_data;
171 
172  if (avpkt->size <= 0)
173  goto end;
174 
175  if (*ptr) {
176  AVBPrint buffer;
177 
178  // skip timers
179  ptr = jss_skip_whitespace(ptr);
180  ptr = strchr(ptr, ' '); if (!ptr) goto end; ptr++;
181  ptr = strchr(ptr, ' '); if (!ptr) goto end; ptr++;
182 
184  jacosub_to_ass(avctx, &buffer, ptr);
185  ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
187  if (ret < 0)
188  return ret;
189  }
190 
191 end:
192  *got_sub_ptr = sub->num_rects > 0;
193  return avpkt->size;
194 }
195 
197  .p.name = "jacosub",
198  .p.long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle"),
199  .p.type = AVMEDIA_TYPE_SUBTITLE,
200  .p.id = AV_CODEC_ID_JACOSUB,
203  .flush = ff_ass_decoder_flush,
204  .priv_data_size = sizeof(FFASSDecoderContext),
205  .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
206 };
AVSubtitle
Definition: avcodec.h:2305
func
int(* func)(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:68
ALIGN_VT
@ ALIGN_VT
Definition: jacosubdec.c:89
AVMEDIA_TYPE_SUBTITLE
@ AVMEDIA_TYPE_SUBTITLE
Definition: avutil.h:204
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
av_bprint_finalize
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:235
av_bprint_init
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:69
sub
static float sub(float src0, float src1)
Definition: dnn_backend_native_layer_mathbinary.c:31
ALIGN_VM
@ ALIGN_VM
Definition: jacosubdec.c:88
ass_codes_map
static const struct @79 ass_codes_map[]
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:157
AVPacket::data
uint8_t * data
Definition: packet.h:374
ALIGN_VB
@ ALIGN_VB
Definition: jacosubdec.c:87
insert_datetime
static int insert_datetime(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:43
FFCodec
Definition: codec_internal.h:112
ALIGN_JL
@ ALIGN_JL
Definition: jacosubdec.c:91
jacosub_to_ass
static void jacosub_to_ass(AVCodecContext *avctx, AVBPrint *dst, const char *src)
Definition: jacosubdec.c:95
FFCodec::p
AVCodec p
The public AVCodec.
Definition: codec_internal.h:116
JSS_MAX_LINESIZE
#define JSS_MAX_LINESIZE
Definition: jacosub.h:31
AV_CODEC_ID_JACOSUB
@ AV_CODEC_ID_JACOSUB
Definition: codec_id.h:538
ass.h
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
s
#define s(width, name)
Definition: cbs_vp9.c:256
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
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
time.h
ALIGN_JR
@ ALIGN_JR
Definition: jacosubdec.c:92
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:375
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
codec_internal.h
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
ALIGN_JC
@ ALIGN_JC
Definition: jacosubdec.c:90
jacosub_decode_frame
static int jacosub_decode_frame(AVCodecContext *avctx, AVSubtitle *sub, int *got_sub_ptr, const AVPacket *avpkt)
Definition: jacosubdec.c:165
ff_jacosub_decoder
const FFCodec ff_jacosub_decoder
Definition: jacosubdec.c:196
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:164
bprint.h
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:269
av_toupper
static av_const int av_toupper(int c)
Locale-independent conversion of ASCII characters to uppercase.
Definition: avstring.h:236
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: codec_internal.h:31
AVCodec::name
const char * name
Name of the codec implementation.
Definition: codec.h:203
ret
ret
Definition: filter_design.txt:187
av_bprintf
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:94
AVCodecContext
main external API structure.
Definition: avcodec.h:389
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
FF_CODEC_DECODE_SUB_CB
#define FF_CODEC_DECODE_SUB_CB(func)
Definition: codec_internal.h:257
AVCodecContext::priv_data
void * priv_data
Definition: avcodec.h:416
AVPacket
This structure stores compressed data.
Definition: packet.h:351
src
INIT_CLIP pixel * src
Definition: h264pred_template.c:418
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