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 static int insert_text(AVBPrint *dst, const char *in, const char *arg)
36 {
37  av_bprintf(dst, "%s", arg);
38  return 0;
39 }
40 
41 static int insert_datetime(AVBPrint *dst, const char *in, const char *arg)
42 {
43  char buf[16] = {0};
44  time_t now = time(0);
45  struct tm ltime;
46 
47  localtime_r(&now, &ltime);
48  if (strftime(buf, sizeof(buf), arg, &ltime))
49  av_bprintf(dst, "%s", buf);
50  return 0;
51 }
52 
53 static int insert_color(AVBPrint *dst, const char *in, const char *arg)
54 {
55  return 1; // skip id
56 }
57 
58 static int insert_font(AVBPrint *dst, const char *in, const char *arg)
59 {
60  return 1; // skip id
61 }
62 
63 static const struct {
64  const char *from;
65  const char *arg;
66  int (*func)(AVBPrint *dst, const char *in, const char *arg);
67 } ass_codes_map[] = {
68  {"\\~", "~", insert_text}, // tilde doesn't need escaping
69  {"~", "{\\h}", insert_text}, // hard space
70  {"\\n", "\\N", insert_text}, // newline
71  {"\\D", "%d %b %Y", insert_datetime}, // current date
72  {"\\T", "%H:%M", insert_datetime}, // current time
73  {"\\N", "{\\r}", insert_text}, // reset to default style
74  {"\\I", "{\\i1}", insert_text}, // italic on
75  {"\\i", "{\\i0}", insert_text}, // italic off
76  {"\\B", "{\\b1}", insert_text}, // bold on
77  {"\\b", "{\\b0}", insert_text}, // bold off
78  {"\\U", "{\\u1}", insert_text}, // underline on
79  {"\\u", "{\\u0}", insert_text}, // underline off
80  {"\\C", "", insert_color}, // TODO: color
81  {"\\F", "", insert_font}, // TODO: font
82 };
83 
84 enum {
85  ALIGN_VB = 1<<0, // vertical bottom, default
86  ALIGN_VM = 1<<1, // vertical middle
87  ALIGN_VT = 1<<2, // vertical top
88  ALIGN_JC = 1<<3, // justify center, default
89  ALIGN_JL = 1<<4, // justify left
90  ALIGN_JR = 1<<5, // justify right
91 };
92 
93 static void jacosub_to_ass(AVCodecContext *avctx, AVBPrint *dst, const char *src)
94 {
95  int i, valign = 0, halign = 0;
96  char c = av_toupper(*src);
97  char directives[128] = {0};
98 
99  /* extract the optional directives */
100  if ((c >= 'A' && c <= 'Z') || c == '[') {
101  char *p = directives;
102  char *pend = directives + sizeof(directives) - 1;
103 
104  do *p++ = av_toupper(*src++);
105  while (*src && !jss_whitespace(*src) && p < pend);
106  *p = 0;
108  }
109 
110  /* handle directives (TODO: handle more of them, and more reliably) */
111  if (strstr(directives, "VB")) valign = ALIGN_VB;
112  else if (strstr(directives, "VM")) valign = ALIGN_VM;
113  else if (strstr(directives, "VT")) valign = ALIGN_VT;
114  if (strstr(directives, "JC")) halign = ALIGN_JC;
115  else if (strstr(directives, "JL")) halign = ALIGN_JL;
116  else if (strstr(directives, "JR")) halign = ALIGN_JR;
117  if (valign || halign) {
118  if (!valign) valign = ALIGN_VB;
119  if (!halign) halign = ALIGN_JC;
120  switch (valign | halign) {
121  case ALIGN_VB | ALIGN_JL: av_bprintf(dst, "{\\an1}"); break; // bottom left
122  case ALIGN_VB | ALIGN_JC: av_bprintf(dst, "{\\an2}"); break; // bottom center
123  case ALIGN_VB | ALIGN_JR: av_bprintf(dst, "{\\an3}"); break; // bottom right
124  case ALIGN_VM | ALIGN_JL: av_bprintf(dst, "{\\an4}"); break; // middle left
125  case ALIGN_VM | ALIGN_JC: av_bprintf(dst, "{\\an5}"); break; // middle center
126  case ALIGN_VM | ALIGN_JR: av_bprintf(dst, "{\\an6}"); break; // middle right
127  case ALIGN_VT | ALIGN_JL: av_bprintf(dst, "{\\an7}"); break; // top left
128  case ALIGN_VT | ALIGN_JC: av_bprintf(dst, "{\\an8}"); break; // top center
129  case ALIGN_VT | ALIGN_JR: av_bprintf(dst, "{\\an9}"); break; // top right
130  }
131  }
132 
133  /* process timed line */
134  while (*src && *src != '\n') {
135 
136  /* text continue on the next line */
137  if (src[0] == '\\' && src[1] == '\n') {
138  src += 2;
139  while (jss_whitespace(*src))
140  src++;
141  continue;
142  }
143 
144  /* special character codes */
145  for (i = 0; i < FF_ARRAY_ELEMS(ass_codes_map); i++) {
146  const char *from = ass_codes_map[i].from;
147  const char *arg = ass_codes_map[i].arg;
148  size_t codemap_len = strlen(from);
149 
150  if (!strncmp(src, from, codemap_len)) {
151  src += codemap_len;
152  src += ass_codes_map[i].func(dst, src, arg);
153  break;
154  }
155  }
156 
157  /* simple char copy */
159  av_bprintf(dst, "%c", *src++);
160  }
161 }
162 
164  int *got_sub_ptr, const AVPacket *avpkt)
165 {
166  int ret;
167  const char *ptr = avpkt->data;
168  FFASSDecoderContext *s = avctx->priv_data;
169 
170  if (avpkt->size <= 0)
171  goto end;
172 
173  if (*ptr) {
174  AVBPrint buffer;
175 
176  // skip timers
177  ptr = jss_skip_whitespace(ptr);
178  ptr = strchr(ptr, ' '); if (!ptr) goto end; ptr++;
179  ptr = strchr(ptr, ' '); if (!ptr) goto end; ptr++;
180 
182  jacosub_to_ass(avctx, &buffer, ptr);
183  ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
185  if (ret < 0)
186  return ret;
187  }
188 
189 end:
190  *got_sub_ptr = sub->num_rects > 0;
191  return avpkt->size;
192 }
193 
195  .p.name = "jacosub",
196  CODEC_LONG_NAME("JACOsub subtitle"),
197  .p.type = AVMEDIA_TYPE_SUBTITLE,
198  .p.id = AV_CODEC_ID_JACOSUB,
201  .flush = ff_ass_decoder_flush,
202  .priv_data_size = sizeof(FFASSDecoderContext),
203 };
AVSubtitle
Definition: avcodec.h:2082
func
int(* func)(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:66
AVMEDIA_TYPE_SUBTITLE
@ AVMEDIA_TYPE_SUBTITLE
Definition: avutil.h:203
ALIGN_JL
@ ALIGN_JL
Definition: jacosubdec.c:89
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:98
av_bprint_init
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:69
AVSubtitle::num_rects
unsigned num_rects
Definition: avcodec.h:2086
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:159
AVPacket::data
uint8_t * data
Definition: packet.h:588
insert_datetime
static int insert_datetime(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:41
FFCodec
Definition: codec_internal.h:127
jacosub_to_ass
static void jacosub_to_ass(AVCodecContext *avctx, AVBPrint *dst, const char *src)
Definition: jacosubdec.c:93
FFCodec::p
AVCodec p
The public AVCodec.
Definition: codec_internal.h:131
JSS_MAX_LINESIZE
#define JSS_MAX_LINESIZE
Definition: jacosub.h:31
AV_CODEC_ID_JACOSUB
@ AV_CODEC_ID_JACOSUB
Definition: codec_id.h:582
ass.h
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
s
#define s(width, name)
Definition: cbs_vp9.c:198
insert_text
static int insert_text(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:35
from
const char * from
Definition: jacosubdec.c:64
ALIGN_VT
@ ALIGN_VT
Definition: jacosubdec.c:87
CODEC_LONG_NAME
#define CODEC_LONG_NAME(str)
Definition: codec_internal.h:332
arg
const char * arg
Definition: jacosubdec.c:65
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:58
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:589
av_bprint_finalize
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:235
codec_internal.h
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:87
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:88
jacosub_decode_frame
static int jacosub_decode_frame(AVCodecContext *avctx, AVSubtitle *sub, int *got_sub_ptr, const AVPacket *avpkt)
Definition: jacosubdec.c:163
ff_jacosub_decoder
const FFCodec ff_jacosub_decoder
Definition: jacosubdec.c:194
jss_skip_whitespace
static const av_always_inline char * jss_skip_whitespace(const char *p)
Definition: jacosub.h:38
bprint.h
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
ass_codes_map
static const struct @166 ass_codes_map[]
av_toupper
static av_const int av_toupper(int c)
Locale-independent conversion of ASCII characters to uppercase.
Definition: avstring.h:227
AVCodec::name
const char * name
Name of the codec implementation.
Definition: codec.h:179
ret
ret
Definition: filter_design.txt:187
av_bprintf
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:122
ALIGN_VB
@ ALIGN_VB
Definition: jacosubdec.c:85
ALIGN_JR
@ ALIGN_JR
Definition: jacosubdec.c:90
AVCodecContext
main external API structure.
Definition: avcodec.h:431
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
Windows::Graphics::DirectX::Direct3D11::p
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
Definition: vsrc_gfxcapture_winrt.hpp:53
FF_CODEC_DECODE_SUB_CB
#define FF_CODEC_DECODE_SUB_CB(func)
Definition: codec_internal.h:351
AVCodecContext::priv_data
void * priv_data
Definition: avcodec.h:458
AVPacket
This structure stores compressed data.
Definition: packet.h:565
insert_color
static int insert_color(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:53
FFASSDecoderContext
Definition: ass.h:46
ff_ass_decoder_flush
av_cold void ff_ass_decoder_flush(AVCodecContext *avctx)
Helper to flush a text subtitles decoder making use of the FFASSDecoderContext.
Definition: ass.c:166
avstring.h
src
#define src
Definition: vp8dsp.c:248
ALIGN_VM
@ ALIGN_VM
Definition: jacosubdec.c:86