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 "jacosub.h"
30 #include "libavutil/avstring.h"
31 #include "libavutil/bprint.h"
33 
34 #undef time
35 
36 static int insert_text(AVBPrint *dst, const char *in, const char *arg)
37 {
38  av_bprintf(dst, "%s", arg);
39  return 0;
40 }
41 
42 static int insert_datetime(AVBPrint *dst, const char *in, const char *arg)
43 {
44  char buf[16] = {0};
45  time_t now = time(0);
46  struct tm ltime;
47 
48  localtime_r(&now, &ltime);
49  if (strftime(buf, sizeof(buf), arg, &ltime))
50  av_bprintf(dst, "%s", buf);
51  return 0;
52 }
53 
54 static int insert_color(AVBPrint *dst, const char *in, const char *arg)
55 {
56  return 1; // skip id
57 }
58 
59 static int insert_font(AVBPrint *dst, const char *in, const char *arg)
60 {
61  return 1; // skip id
62 }
63 
64 static const struct {
65  const char *from;
66  const char *arg;
67  int (*func)(AVBPrint *dst, const char *in, const char *arg);
68 } ass_codes_map[] = {
69  {"\\~", "~", insert_text}, // tilde doesn't need escaping
70  {"~", "{\\h}", insert_text}, // hard space
71  {"\\n", "\\N", insert_text}, // newline
72  {"\\D", "%d %b %Y", insert_datetime}, // current date
73  {"\\T", "%H:%M", insert_datetime}, // current time
74  {"\\N", "{\\r}", insert_text}, // reset to default style
75  {"\\I", "{\\i1}", insert_text}, // italic on
76  {"\\i", "{\\i0}", insert_text}, // italic off
77  {"\\B", "{\\b1}", insert_text}, // bold on
78  {"\\b", "{\\b0}", insert_text}, // bold off
79  {"\\U", "{\\u1}", insert_text}, // underline on
80  {"\\u", "{\\u0}", insert_text}, // underline off
81  {"\\C", "", insert_color}, // TODO: color
82  {"\\F", "", insert_font}, // TODO: font
83 };
84 
85 enum {
86  ALIGN_VB = 1<<0, // vertical bottom, default
87  ALIGN_VM = 1<<1, // vertical middle
88  ALIGN_VT = 1<<2, // vertical top
89  ALIGN_JC = 1<<3, // justify center, default
90  ALIGN_JL = 1<<4, // justify left
91  ALIGN_JR = 1<<5, // justify right
92 };
93 
94 static void jacosub_to_ass(AVCodecContext *avctx, AVBPrint *dst, const char *src)
95 {
96  int i, valign = 0, halign = 0;
97  char c = av_toupper(*src);
98  char directives[128] = {0};
99 
100  /* extract the optional directives */
101  if ((c >= 'A' && c <= 'Z') || c == '[') {
102  char *p = directives;
103  char *pend = directives + sizeof(directives) - 1;
104 
105  do *p++ = av_toupper(*src++);
106  while (*src && !jss_whitespace(*src) && p < pend);
107  *p = 0;
108  src = jss_skip_whitespace(src);
109  }
110 
111  /* handle directives (TODO: handle more of them, and more reliably) */
112  if (strstr(directives, "VB")) valign = ALIGN_VB;
113  else if (strstr(directives, "VM")) valign = ALIGN_VM;
114  else if (strstr(directives, "VT")) valign = ALIGN_VT;
115  if (strstr(directives, "JC")) halign = ALIGN_JC;
116  else if (strstr(directives, "JL")) halign = ALIGN_JL;
117  else if (strstr(directives, "JR")) halign = ALIGN_JR;
118  if (valign || halign) {
119  if (!valign) valign = ALIGN_VB;
120  if (!halign) halign = ALIGN_JC;
121  switch (valign | halign) {
122  case ALIGN_VB | ALIGN_JL: av_bprintf(dst, "{\\an1}"); break; // bottom left
123  case ALIGN_VB | ALIGN_JC: av_bprintf(dst, "{\\an2}"); break; // bottom center
124  case ALIGN_VB | ALIGN_JR: av_bprintf(dst, "{\\an3}"); break; // bottom right
125  case ALIGN_VM | ALIGN_JL: av_bprintf(dst, "{\\an4}"); break; // middle left
126  case ALIGN_VM | ALIGN_JC: av_bprintf(dst, "{\\an5}"); break; // middle center
127  case ALIGN_VM | ALIGN_JR: av_bprintf(dst, "{\\an6}"); break; // middle right
128  case ALIGN_VT | ALIGN_JL: av_bprintf(dst, "{\\an7}"); break; // top left
129  case ALIGN_VT | ALIGN_JC: av_bprintf(dst, "{\\an8}"); break; // top center
130  case ALIGN_VT | ALIGN_JR: av_bprintf(dst, "{\\an9}"); break; // top right
131  }
132  }
133 
134  /* process timed line */
135  while (*src && *src != '\n') {
136 
137  /* text continue on the next line */
138  if (src[0] == '\\' && src[1] == '\n') {
139  src += 2;
140  while (jss_whitespace(*src))
141  src++;
142  continue;
143  }
144 
145  /* special character codes */
146  for (i = 0; i < FF_ARRAY_ELEMS(ass_codes_map); i++) {
147  const char *from = ass_codes_map[i].from;
148  const char *arg = ass_codes_map[i].arg;
149  size_t codemap_len = strlen(from);
150 
151  if (!strncmp(src, from, codemap_len)) {
152  src += codemap_len;
153  src += ass_codes_map[i].func(dst, src, arg);
154  break;
155  }
156  }
157 
158  /* simple char copy */
159  if (i == FF_ARRAY_ELEMS(ass_codes_map))
160  av_bprintf(dst, "%c", *src++);
161  }
162 }
163 
165  void *data, int *got_sub_ptr, AVPacket *avpkt)
166 {
167  int ret;
168  AVSubtitle *sub = data;
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);
186  av_bprint_finalize(&buffer, 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  .name = "jacosub",
198  .long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle"),
199  .type = AVMEDIA_TYPE_SUBTITLE,
200  .id = AV_CODEC_ID_JACOSUB,
202  .decode = jacosub_decode_frame,
203  .flush = ff_ass_decoder_flush,
204  .priv_data_size = sizeof(FFASSDecoderContext),
205 };
#define NULL
Definition: coverity.c:32
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:94
static int insert_datetime(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:42
ptrdiff_t const GLvoid * data
Definition: opengl_enc.c:100
int size
Definition: avcodec.h:1478
unsigned num_rects
Definition: avcodec.h:3937
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:101
JACOsub shared utils.
static const struct @103 ass_codes_map[]
#define src
Definition: vp8dsp.c:254
AVCodec.
Definition: avcodec.h:3481
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:235
static av_cold int end(AVCodecContext *avctx)
Definition: avrndec.c:90
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
int ff_ass_subtitle_header_default(AVCodecContext *avctx)
Generate a suitable AVCodecContext.subtitle_header for SUBTITLE_ASS with default style.
Definition: ass.c:80
const char * from
Definition: jacosubdec.c:65
uint8_t * data
Definition: avcodec.h:1477
static int insert_text(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:36
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:259
#define JSS_MAX_LINESIZE
Definition: jacosub.h:31
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:186
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:69
const char * arg
Definition: jacosubdec.c:66
const char * name
Name of the codec implementation.
Definition: avcodec.h:3488
static int jacosub_decode_frame(AVCodecContext *avctx, void *data, int *got_sub_ptr, AVPacket *avpkt)
Definition: jacosubdec.c:164
#define s(width, name)
Definition: cbs_vp9.c:257
#define FF_ARRAY_ELEMS(a)
static av_always_inline int jss_whitespace(char c)
Definition: jacosub.h:33
static int insert_color(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:54
main external API structure.
Definition: avcodec.h:1565
void * buf
Definition: avisynth_c.h:766
uint8_t pi<< 24) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi-0x80)*(1.0f/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi-0x80)*(1.0/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S16, int16_t,(*(const int16_t *) pi >> 8)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S16, int16_t,*(const int16_t *) pi *(1.0f/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S16, int16_t,*(const int16_t *) pi *(1.0/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S32, int32_t,(*(const int32_t *) pi >> 24)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S32, int32_t,*(const int32_t *) pi *(1.0f/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S32, int32_t,*(const int32_t *) pi *(1.0/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_FLT, float, av_clip_uint8(lrintf(*(const float *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, float, av_clip_int16(lrintf(*(const float *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, float, av_clipl_int32(llrintf(*(const float *) pi *(1U<< 31)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_DBL, double, av_clip_uint8(lrint(*(const double *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, double, av_clip_int16(lrint(*(const double *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, double, av_clipl_int32(llrint(*(const double *) pi *(1U<< 31))))#define SET_CONV_FUNC_GROUP(ofmt, ifmt) static void set_generic_function(AudioConvert *ac){}void ff_audio_convert_free(AudioConvert **ac){if(!*ac) return;ff_dither_free(&(*ac) ->dc);av_freep(ac);}AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, enum AVSampleFormat out_fmt, enum AVSampleFormat in_fmt, int channels, int sample_rate, int apply_map){AudioConvert *ac;int in_planar, out_planar;ac=av_mallocz(sizeof(*ac));if(!ac) return NULL;ac->avr=avr;ac->out_fmt=out_fmt;ac->in_fmt=in_fmt;ac->channels=channels;ac->apply_map=apply_map;if(avr->dither_method!=AV_RESAMPLE_DITHER_NONE &&av_get_packed_sample_fmt(out_fmt)==AV_SAMPLE_FMT_S16 &&av_get_bytes_per_sample(in_fmt) > 2){ac->dc=ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate, apply_map);if(!ac->dc){av_free(ac);return NULL;}return ac;}in_planar=ff_sample_fmt_is_planar(in_fmt, channels);out_planar=ff_sample_fmt_is_planar(out_fmt, channels);if(in_planar==out_planar){ac->func_type=CONV_FUNC_TYPE_FLAT;ac->planes=in_planar?ac->channels:1;}else if(in_planar) ac->func_type=CONV_FUNC_TYPE_INTERLEAVE;else ac->func_type=CONV_FUNC_TYPE_DEINTERLEAVE;set_generic_function(ac);if(ARCH_AARCH64) ff_audio_convert_init_aarch64(ac);if(ARCH_ARM) ff_audio_convert_init_arm(ac);if(ARCH_X86) ff_audio_convert_init_x86(ac);return ac;}int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in){int use_generic=1;int len=in->nb_samples;int p;if(ac->dc){av_log(ac->avr, AV_LOG_TRACE,"%d samples - audio_convert: %s to %s (dithered)\n", len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt));return ff_convert_dither(ac-> in
int(* func)(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:67
static av_const int av_toupper(int c)
Locale-independent conversion of ASCII characters to uppercase.
Definition: avstring.h:231
void ff_ass_decoder_flush(AVCodecContext *avctx)
Helper to flush a text subtitles decoder making use of the FFASSDecoderContext.
Definition: ass.c:124
int
static av_always_inline const char * jss_skip_whitespace(const char *p)
Definition: jacosub.h:38
#define localtime_r
Definition: time_internal.h:46
void * priv_data
Definition: avcodec.h:1592
static int insert_font(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:59
static void jacosub_to_ass(AVCodecContext *avctx, AVBPrint *dst, const char *src)
Definition: jacosubdec.c:94
AVCodec ff_jacosub_decoder
Definition: jacosubdec.c:196
This structure stores compressed data.
Definition: avcodec.h:1454
GLuint buffer
Definition: opengl_enc.c:101