FFmpeg
Loading...
Searching...
No Matches
assenc.c
Go to the documentation of this file.
1/*
2 * SSA/ASS muxer
3 * Copyright (c) 2008 Michael Niedermayer
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#include "libavutil/avstring.h"
23#include "libavutil/mem.h"
24#include "avformat.h"
25#include "avio_internal.h"
26#include "internal.h"
27#include "mux.h"
28
29#include "libavutil/opt.h"
30
31typedef struct DialogueLine {
33 char *line;
36
37typedef struct ASSContext {
38 const AVClass *class;
45 uint8_t *trailer;
48
50{
51 ASSContext *ass = s->priv_data;
52 AVCodecParameters *par = s->streams[0]->codecpar;
53
54 avpriv_set_pts_info(s->streams[0], 64, 1, 100);
55 if (par->extradata_size > 0) {
56 size_t header_size = par->extradata_size;
57 uint8_t *trailer = strstr(par->extradata, "\n[Events]");
58
59 if (trailer)
60 trailer = strstr(trailer, "Format:");
61 if (trailer)
62 trailer = strstr(trailer, "\n");
63
64 if (trailer) {
65 header_size = (++trailer - par->extradata);
66 ass->trailer_size = par->extradata_size - header_size;
67 if (ass->trailer_size)
68 ass->trailer = trailer;
69 }
70
71 ffio_write_lines(s->pb, par->extradata, header_size, NULL);
72
73 ass->ssa_mode = !strstr(par->extradata, "\n[V4+ Styles]");
74 if (!strstr(par->extradata, "\n[Events]"))
75 avio_printf(s->pb, "[Events]\nFormat: %s, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n",
76 ass->ssa_mode ? "Marked" : "Layer");
77 }
78
79 return 0;
80}
81
82static void purge_dialogues(AVFormatContext *s, int force)
83{
84 int n = 0;
85 ASSContext *ass = s->priv_data;
86 DialogueLine *dialogue = ass->dialogue_cache;
87
88 while (dialogue && (dialogue->readorder == ass->expected_readorder || force)) {
89 DialogueLine *next = dialogue->next;
90 if (dialogue->readorder != ass->expected_readorder) {
91 av_log(s, AV_LOG_WARNING, "ReadOrder gap found between %d and %d\n",
92 ass->expected_readorder, dialogue->readorder);
93 ass->expected_readorder = dialogue->readorder;
94 }
95 avio_print(s->pb, "Dialogue: ", dialogue->line, "\n");
96 if (dialogue == ass->last_added_dialogue)
97 ass->last_added_dialogue = next;
98 av_freep(&dialogue->line);
99 av_free(dialogue);
100 if (next)
101 next->prev = NULL;
102 dialogue = ass->dialogue_cache = next;
103 ass->expected_readorder++;
104 n++;
105 }
106 ass->cache_size -= n;
107 if (n > 1)
108 av_log(s, AV_LOG_DEBUG, "wrote %d ASS lines, cached dialogues: %d, waiting for event id %d\n",
109 n, ass->cache_size, ass->expected_readorder);
110}
111
112static void insert_dialogue(ASSContext *ass, DialogueLine *dialogue)
113{
114 DialogueLine *cur, *next = NULL, *prev = NULL;
115
116 /* from the last added to the end of the list */
117 if (ass->last_added_dialogue) {
118 for (cur = ass->last_added_dialogue; cur; cur = cur->next) {
119 if (cur->readorder > dialogue->readorder)
120 break;
121 prev = cur;
122 next = cur->next;
123 }
124 }
125
126 /* from the beginning to the last one added */
127 if (!prev) {
128 next = ass->dialogue_cache;
129 for (cur = next; cur != ass->last_added_dialogue; cur = cur->next) {
130 if (cur->readorder > dialogue->readorder)
131 break;
132 prev = cur;
133 next = cur->next;
134 }
135 }
136
137 if (prev) {
138 prev->next = dialogue;
139 dialogue->prev = prev;
140 } else {
141 dialogue->prev = ass->dialogue_cache;
142 ass->dialogue_cache = dialogue;
143 }
144 if (next) {
145 next->prev = dialogue;
146 dialogue->next = next;
147 }
148 ass->cache_size++;
149 ass->last_added_dialogue = dialogue;
150}
151
153{
154 ASSContext *ass = s->priv_data;
155
156 long int layer;
157 int text_len;
158 char *p = pkt->data;
159 int64_t start = pkt->pts;
160 int64_t end = start + pkt->duration;
161 int hh1, mm1, ss1, ms1;
162 int hh2, mm2, ss2, ms2;
163 DialogueLine *dialogue = av_mallocz(sizeof(*dialogue));
164
165 if (!dialogue)
166 return AVERROR(ENOMEM);
167
168 dialogue->readorder = strtol(p, &p, 10);
169 if (dialogue->readorder < ass->expected_readorder)
170 av_log(s, AV_LOG_WARNING, "Unexpected ReadOrder %d\n",
171 dialogue->readorder);
172 if (*p == ',')
173 p++;
174
175 if (ass->ssa_mode && !strncmp(p, "Marked=", 7))
176 p += 7;
177
178 layer = strtol(p, &p, 10);
179 if (*p == ',')
180 p++;
181 hh1 = (int)(start / 360000); mm1 = (int)(start / 6000) % 60;
182 hh2 = (int)(end / 360000); mm2 = (int)(end / 6000) % 60;
183 ss1 = (int)(start / 100) % 60; ms1 = (int)(start % 100);
184 ss2 = (int)(end / 100) % 60; ms2 = (int)(end % 100);
185 if (hh1 > 9) hh1 = 9, mm1 = 59, ss1 = 59, ms1 = 99;
186 if (hh2 > 9) hh2 = 9, mm2 = 59, ss2 = 59, ms2 = 99;
187
188 text_len = strlen(p);
189 while (text_len > 0 && (p[text_len - 1] == '\r' || p[text_len - 1] == '\n'))
190 text_len--;
191
192 dialogue->line = av_asprintf("%s%ld,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%.*s",
193 ass->ssa_mode ? "Marked=" : "",
194 layer, hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, text_len, p);
195 if (!dialogue->line) {
196 av_free(dialogue);
197 return AVERROR(ENOMEM);
198 }
199 insert_dialogue(ass, dialogue);
201
202 return 0;
203}
204
206{
207 ASSContext *ass = s->priv_data;
208
209 purge_dialogues(s, 1);
210
211 if (ass->trailer) {
212 ffio_write_lines(s->pb, ass->trailer, ass->trailer_size, NULL);
213 }
214
215 return 0;
216}
217
218#define OFFSET(x) offsetof(ASSContext, x)
219#define E AV_OPT_FLAG_ENCODING_PARAM
220static const AVOption options[] = {
221 { "ignore_readorder", "write events immediately, even if they're out-of-order", OFFSET(ignore_readorder), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E },
222 { NULL },
223};
224
225static const AVClass ass_class = {
226 .class_name = "ass muxer",
227 .item_name = av_default_item_name,
228 .option = options,
229 .version = LIBAVUTIL_VERSION_INT,
230};
231
233 .p.name = "ass",
234 .p.long_name = NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"),
235 .p.mime_type = "text/x-ass",
236 .p.extensions = "ass,ssa",
237 .p.audio_codec = AV_CODEC_ID_NONE,
238 .p.video_codec = AV_CODEC_ID_NONE,
239 .p.subtitle_codec = AV_CODEC_ID_ASS,
241 .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH |
243 .p.priv_class = &ass_class,
244 .priv_data_size = sizeof(ASSContext),
248};
const FFOutputFormat ff_ass_muxer
Definition assenc.c:232
#define E
Definition avdct.c:34
void avpriv_set_pts_info(AVStream *st, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition avformat.c:834
Main libavformat public API header.
#define AVFMT_TS_NONSTRICT
Format does not require strictly increasing timestamps, but they must still be monotonic.
Definition avformat.h:507
#define AVFMT_GLOBALHEADER
Format wants global header.
Definition avformat.h:497
#define AVFMT_NOTIMESTAMPS
Format does not need / have any timestamps.
Definition avformat.h:498
#define avio_print(s,...)
Write strings (const char *) to the context.
Definition avio.h:537
int avio_printf(AVIOContext *s, const char *fmt,...) av_printf_format(2
Writes a formatted string to the context.
void ffio_write_lines(AVIOContext *s, const unsigned char *buf, int size, const unsigned char *ending)
Write a sequence of text lines, converting line endings.
Definition aviobuf.c:963
char * av_asprintf(const char *fmt,...)
Definition avstring.c:115
#define s(width, name)
Definition cbs_vp9.c:198
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
static AVPacket * pkt
static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
Definition ffmpeg_mux.c:204
static void write_header(FFV1Context *f)
Definition ffv1enc.c:384
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
Definition opt.h:326
@ AV_CODEC_ID_NONE
Definition codec_id.h:48
@ AV_CODEC_ID_ASS
Definition codec_id.h:588
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
const char * av_default_item_name(void *ptr)
Return the context name.
Definition log.c:241
#define LIBAVUTIL_VERSION_INT
Definition version.h:85
static void purge_dialogues(AVFormatContext *s, int force)
Definition assenc.c:82
static int write_packet(AVFormatContext *s, AVPacket *pkt)
Definition assenc.c:152
static void insert_dialogue(ASSContext *ass, DialogueLine *dialogue)
Definition assenc.c:112
static int write_header(AVFormatContext *s)
Definition assenc.c:49
static const AVClass ass_class
Definition assenc.c:225
static int write_trailer(AVFormatContext *s)
Definition assenc.c:205
#define OFFSET(x)
Definition assenc.c:218
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
Memory handling functions.
#define FF_OFMT_FLAG_MAX_ONE_OF_EACH
If this flag is set, it indicates that for each codec type whose corresponding default codec (i....
Definition mux.h:50
#define FF_OFMT_FLAG_ONLY_DEFAULT_CODECS
If this flag is set, then the only permitted audio/video/subtitle codec ids are AVOutputFormat....
Definition mux.h:59
AVOptions.
int expected_readorder
Definition assenc.c:39
int cache_size
Definition assenc.c:42
DialogueLine * last_added_dialogue
Definition assenc.c:41
uint8_t * trailer
Definition assenc.c:45
int ssa_mode
Definition assenc.c:43
size_t trailer_size
Definition assenc.c:46
int ignore_readorder
Definition assenc.c:44
DialogueLine * dialogue_cache
Definition assenc.c:40
Describe the class of an AVClass context structure.
Definition log.h:76
This struct describes the properties of an encoded stream.
Definition codec_par.h:49
int extradata_size
Size of the extradata content in bytes.
Definition codec_par.h:75
uint8_t * extradata
Extra binary data needed for initializing the decoder, codec-dependent.
Definition codec_par.h:71
Format I/O context.
Definition avformat.h:1333
AVOption.
Definition opt.h:428
This structure stores compressed data.
Definition packet.h:580
struct DialogueLine * next
Definition assenc.c:34
struct DialogueLine * prev
Definition assenc.c:34
char * line
Definition assenc.c:33
int readorder
Definition assenc.c:32
#define av_free(p)
#define av_mallocz(s)
#define av_freep(p)
#define av_log(a,...)
static int write_trailer(AVFormatContext *s1)
Definition v4l2enc.c:101