FFmpeg
ttmlenc.c
Go to the documentation of this file.
1 /*
2  * TTML subtitle encoder
3  * Copyright (c) 2020 24i
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 /**
23  * @file
24  * TTML subtitle encoder
25  * @see https://www.w3.org/TR/ttml1/
26  * @see https://www.w3.org/TR/ttml2/
27  * @see https://www.w3.org/TR/ttml-imsc/rec
28  */
29 
30 #include "avcodec.h"
31 #include "internal.h"
32 #include "libavutil/avstring.h"
33 #include "libavutil/bprint.h"
34 #include "libavutil/internal.h"
35 #include "ass_split.h"
36 #include "ass.h"
37 #include "ttmlenc.h"
38 
39 typedef struct {
42  AVBPrint buffer;
43 } TTMLContext;
44 
45 static void ttml_text_cb(void *priv, const char *text, int len)
46 {
47  TTMLContext *s = priv;
48  AVBPrint cur_line = { 0 };
49  AVBPrint *buffer = &s->buffer;
50 
52 
53  av_bprint_append_data(&cur_line, text, len);
54  if (!av_bprint_is_complete(&cur_line)) {
55  av_log(s->avctx, AV_LOG_ERROR,
56  "Failed to move the current subtitle dialog to AVBPrint!\n");
57  av_bprint_finalize(&cur_line, NULL);
58  return;
59  }
60 
61 
63  0);
64 
65  av_bprint_finalize(&cur_line, NULL);
66 }
67 
68 static void ttml_new_line_cb(void *priv, int forced)
69 {
70  TTMLContext *s = priv;
71 
72  av_bprintf(&s->buffer, "<br/>");
73 }
74 
76  .text = ttml_text_cb,
77  .new_line = ttml_new_line_cb,
78 };
79 
80 static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf,
81  int bufsize, const AVSubtitle *sub)
82 {
83  TTMLContext *s = avctx->priv_data;
84  ASSDialog *dialog;
85  int i;
86 
87  av_bprint_clear(&s->buffer);
88 
89  for (i=0; i<sub->num_rects; i++) {
90  const char *ass = sub->rects[i]->ass;
91 
92  if (sub->rects[i]->type != SUBTITLE_ASS) {
93  av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
94  return AVERROR(EINVAL);
95  }
96 
97 #if FF_API_ASS_TIMING
98  if (!strncmp(ass, "Dialogue: ", 10)) {
99  int num;
100  dialog = ff_ass_split_dialog(s->ass_ctx, ass, 0, &num);
101 
102  for (; dialog && num--; dialog++) {
104  dialog->text);
105  int log_level = (ret != AVERROR_INVALIDDATA ||
106  avctx->err_recognition & AV_EF_EXPLODE) ?
108 
109  if (ret < 0) {
110  av_log(avctx, log_level,
111  "Splitting received ASS dialog failed: %s\n",
112  av_err2str(ret));
113 
114  if (log_level == AV_LOG_ERROR)
115  return ret;
116  }
117  }
118  } else {
119 #endif
120  dialog = ff_ass_split_dialog2(s->ass_ctx, ass);
121  if (!dialog)
122  return AVERROR(ENOMEM);
123 
124  {
126  dialog->text);
127  int log_level = (ret != AVERROR_INVALIDDATA ||
128  avctx->err_recognition & AV_EF_EXPLODE) ?
130 
131  if (ret < 0) {
132  av_log(avctx, log_level,
133  "Splitting received ASS dialog text %s failed: %s\n",
134  dialog->text,
135  av_err2str(ret));
136 
137  if (log_level == AV_LOG_ERROR) {
138  ff_ass_free_dialog(&dialog);
139  return ret;
140  }
141  }
142 
143  ff_ass_free_dialog(&dialog);
144  }
145 #if FF_API_ASS_TIMING
146  }
147 #endif
148  }
149 
150  if (!av_bprint_is_complete(&s->buffer))
151  return AVERROR(ENOMEM);
152  if (!s->buffer.len)
153  return 0;
154 
155  // force null-termination, so in case our destination buffer is
156  // too small, the return value is larger than bufsize minus null.
157  if (av_strlcpy(buf, s->buffer.str, bufsize) > bufsize - 1) {
158  av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n");
160  }
161 
162  return s->buffer.len;
163 }
164 
166 {
167  TTMLContext *s = avctx->priv_data;
168 
169  ff_ass_split_free(s->ass_ctx);
170 
171  av_bprint_finalize(&s->buffer, NULL);
172 
173  return 0;
174 }
175 
177 {
178  TTMLContext *s = avctx->priv_data;
179 
180  s->avctx = avctx;
181 
182  if (!(s->ass_ctx = ff_ass_split(avctx->subtitle_header))) {
183  return AVERROR_INVALIDDATA;
184  }
185 
188  return AVERROR(ENOMEM);
189  }
190 
192  memcpy(avctx->extradata, TTMLENC_EXTRADATA_SIGNATURE,
194 
196 
197  return 0;
198 }
199 
201  .name = "ttml",
202  .long_name = NULL_IF_CONFIG_SMALL("TTML subtitle"),
203  .type = AVMEDIA_TYPE_SUBTITLE,
204  .id = AV_CODEC_ID_TTML,
205  .priv_data_size = sizeof(TTMLContext),
207  .encode_sub = ttml_encode_frame,
208  .close = ttml_encode_close,
209  .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
210 };
AVSubtitle
Definition: avcodec.h:2722
AVCodec
AVCodec.
Definition: codec.h:197
AVMEDIA_TYPE_SUBTITLE
@ AVMEDIA_TYPE_SUBTITLE
Definition: avutil.h:204
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:200
AV_BPRINT_SIZE_UNLIMITED
#define AV_BPRINT_SIZE_UNLIMITED
init
static av_cold int init(AVCodecContext *avctx)
Definition: avrndec.c:31
ASSCodesCallbacks
Set of callback functions corresponding to each override codes that can be encountered in a "Dialogue...
Definition: ass_split.h:154
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
ff_ass_split_dialog
ASSDialog * ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf, int cache, int *number)
Split one or several ASS "Dialogue" lines from a string buffer and store them in an already initializ...
Definition: ass_split.c:413
ff_ttml_encoder
AVCodec ff_ttml_encoder
Definition: ttmlenc.c:200
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:32
AVCodecContext::err_recognition
int err_recognition
Error recognition; may misdetect some more or less valid parts as errors.
Definition: avcodec.h:1645
internal.h
AVCodecContext::subtitle_header
uint8_t * subtitle_header
Header containing style information for text subtitles.
Definition: avcodec.h:2016
av_bprint_append_data
void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size)
Append data to a print buffer.
Definition: bprint.c:158
SUBTITLE_ASS
@ SUBTITLE_ASS
Formatted text, the ass field must be set by the decoder and is authoritative.
Definition: avcodec.h:2682
TTMLENC_EXTRADATA_SIGNATURE_SIZE
#define TTMLENC_EXTRADATA_SIGNATURE_SIZE
Definition: ttmlenc.h:26
ttml_text_cb
static void ttml_text_cb(void *priv, const char *text, int len)
Definition: ttmlenc.c:45
ass_split.h
AVERROR_BUFFER_TOO_SMALL
#define AVERROR_BUFFER_TOO_SMALL
Buffer too small.
Definition: error.h:51
TTMLContext::avctx
AVCodecContext * avctx
Definition: ttmlenc.c:40
ass.h
ff_ass_free_dialog
void ff_ass_free_dialog(ASSDialog **dialogp)
Free a dialogue obtained from ff_ass_split_dialog2().
Definition: ass_split.c:432
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:194
av_cold
#define av_cold
Definition: attributes.h:90
AVCodecContext::extradata_size
int extradata_size
Definition: avcodec.h:638
s
#define s(width, name)
Definition: cbs_vp9.c:257
ff_ass_split_dialog2
ASSDialog * ff_ass_split_dialog2(ASSSplitContext *ctx, const char *buf)
Split one ASS Dialogue line from a string buffer.
Definition: ass_split.c:444
ttml_callbacks
static const ASSCodesCallbacks ttml_callbacks
Definition: ttmlenc.c:75
NULL
#define NULL
Definition: coverity.c:32
ttml_new_line_cb
static void ttml_new_line_cb(void *priv, int forced)
Definition: ttmlenc.c:68
ttml_encode_frame
static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, int bufsize, const AVSubtitle *sub)
Definition: ttmlenc.c:80
TTMLENC_EXTRADATA_SIGNATURE
#define TTMLENC_EXTRADATA_SIGNATURE
Definition: ttmlenc.h:25
TTMLContext::ass_ctx
ASSSplitContext * ass_ctx
Definition: ttmlenc.c:41
ASSSplitContext
This struct can be casted to ASS to access to the split data.
Definition: ass_split.c:197
ff_ass_split
ASSSplitContext * ff_ass_split(const char *buf)
Split a full ASS file or a ASS header from a string buffer and store the split structure in a newly a...
Definition: ass_split.c:374
AV_EF_EXPLODE
#define AV_EF_EXPLODE
abort decoding on minor error detection
Definition: avcodec.h:1656
av_bprint_is_complete
static int av_bprint_is_complete(const AVBPrint *buf)
Test if the print buffer is complete (not truncated).
Definition: bprint.h:185
ff_ass_split_free
void ff_ass_split_free(ASSSplitContext *ctx)
Free all the memory allocated for an ASSSplitContext.
Definition: ass_split.c:481
TTMLContext
Definition: ttmlenc.c:39
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
ASSDialog::text
char * text
actual text which will be displayed as a subtitle, can include style override control codes (see ff_a...
Definition: ass_split.h:82
av_err2str
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition: error.h:119
AV_ESCAPE_MODE_XML
@ AV_ESCAPE_MODE_XML
Use XML non-markup character data escaping.
Definition: avstring.h:327
ttml_encode_init
static av_cold int ttml_encode_init(AVCodecContext *avctx)
Definition: ttmlenc.c:176
bprint.h
i
int i
Definition: input.c:407
AVCodecContext::extradata
uint8_t * extradata
some codecs need / can use extradata like Huffman tables.
Definition: avcodec.h:637
FF_CODEC_CAP_INIT_CLEANUP
#define FF_CODEC_CAP_INIT_CLEANUP
The codec allows calling the close function for deallocation even if the init function returned a fai...
Definition: internal.h:49
internal.h
ttmlenc.h
AV_CODEC_ID_TTML
@ AV_CODEC_ID_TTML
Definition: codec_id.h:548
uint8_t
uint8_t
Definition: audio_convert.c:194
av_mallocz
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:237
AVCodec::name
const char * name
Name of the codec implementation.
Definition: codec.h:204
len
int len
Definition: vorbis_enc_data.h:452
avcodec.h
ret
ret
Definition: filter_design.txt:187
av_bprintf
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:94
av_bprint_escape
void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars, enum AVEscapeMode mode, int flags)
Escape the content in src and append it to dstbuf.
Definition: bprint.c:265
AV_INPUT_BUFFER_PADDING_SIZE
#define AV_INPUT_BUFFER_PADDING_SIZE
Definition: avcodec.h:215
TTMLContext::buffer
AVBPrint buffer
Definition: ttmlenc.c:42
AVCodecContext
main external API structure.
Definition: avcodec.h:536
av_bprint_clear
void av_bprint_clear(AVBPrint *buf)
Reset the string to "" but keep internal allocated data.
Definition: bprint.c:227
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
ASSDialog
fields extracted from the [Events] section
Definition: ass_split.h:71
AVCodecContext::priv_data
void * priv_data
Definition: avcodec.h:563
ttml_encode_close
static av_cold int ttml_encode_close(AVCodecContext *avctx)
Definition: ttmlenc.c:165
av_strlcpy
size_t av_strlcpy(char *dst, const char *src, size_t size)
Copy the string src to dst, but no more than size - 1 bytes, and null-terminate dst.
Definition: avstring.c:83
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:28
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
ff_ass_split_override_codes
int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, const char *buf)
Split override codes out of a ASS "Dialogue" Text field.
Definition: ass_split.c:494
avstring.h
ASSCodesCallbacks::text
void(* text)(void *priv, const char *text, int len)
Definition: ass_split.h:159