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;
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 */
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);
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 };
AVSubtitle
Definition: avcodec.h:2694
func
int(* func)(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:67
AVCodec
AVCodec.
Definition: codec.h:190
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:97
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
AVSubtitle::num_rects
unsigned num_rects
Definition: avcodec.h:2698
end
static av_cold int end(AVCodecContext *avctx)
Definition: avrndec.c:92
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:118
AVPacket::data
uint8_t * data
Definition: packet.h:355
data
const char data[16]
Definition: mxf.c:91
insert_datetime
static int insert_datetime(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:42
jacosub_to_ass
static void jacosub_to_ass(AVCodecContext *avctx, AVBPrint *dst, const char *src)
Definition: jacosubdec.c:94
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:164
AV_CODEC_ID_JACOSUB
@ AV_CODEC_ID_JACOSUB
Definition: codec_id.h:520
ass.h
ALIGN_VB
@ ALIGN_VB
Definition: jacosubdec.c:86
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:36
from
const char * from
Definition: jacosubdec.c:65
ALIGN_JL
@ ALIGN_JL
Definition: jacosubdec.c:90
arg
const char * arg
Definition: jacosubdec.c:66
time_internal.h
ALIGN_JR
@ ALIGN_JR
Definition: jacosubdec.c:91
NULL
#define NULL
Definition: coverity.c:32
insert_font
static int insert_font(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:59
src
#define src
Definition: vp8dsp.c:254
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
ALIGN_VM
@ ALIGN_VM
Definition: jacosubdec.c:87
AVPacket::size
int size
Definition: packet.h:356
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:188
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:89
ass_codes_map
static const struct @82 ass_codes_map[]
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:141
in
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
Definition: audio_convert.c:326
bprint.h
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:269
ff_jacosub_decoder
AVCodec ff_jacosub_decoder
Definition: jacosubdec.c:196
av_toupper
static av_const int av_toupper(int c)
Locale-independent conversion of ASCII characters to uppercase.
Definition: avstring.h:231
AVCodec::name
const char * name
Name of the codec implementation.
Definition: codec.h:197
ret
ret
Definition: filter_design.txt:187
av_bprintf
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:94
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen_template.c:38
AVCodecContext
main external API structure.
Definition: avcodec.h:526
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:553
AVPacket
This structure stores compressed data.
Definition: packet.h:332
insert_color
static int insert_color(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:54
FFASSDecoderContext
Definition: ass.h:46
avstring.h
ALIGN_VT
@ ALIGN_VT
Definition: jacosubdec.c:88
int
int
Definition: ffmpeg_filter.c:192