3 * Copyright (c) 2008 Michael Niedermayer
5 * This file is part of FFmpeg.
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.
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.
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
22 #include "libavutil/avstring.h"
23 #include "libavutil/mem.h"
25 #include "avio_internal.h"
29 #include "libavutil/opt.h"
31 typedef struct DialogueLine
{
34 struct DialogueLine
*prev
, *next
;
37 typedef struct ASSContext
{
39 int expected_readorder
;
40 DialogueLine
*dialogue_cache
;
41 DialogueLine
*last_added_dialogue
;
49 static int write_header(AVFormatContext
*s
)
51 ASSContext
*ass
= s
->priv_data
;
52 AVCodecParameters
*par
= s
->streams
[0]->codecpar
;
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]");
60 trailer
= strstr(trailer
, "Format:");
62 trailer
= strstr(trailer
, "\n");
65 header_size
= (++trailer
- par
->extradata
);
66 ass
->trailer_size
= par
->extradata_size
- header_size
;
67 if (ass
->trailer_size
)
68 ass
->trailer
= trailer
;
71 ffio_write_lines(s
->pb
, par
->extradata
, header_size
, NULL
);
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");
82 static void purge_dialogues(AVFormatContext
*s
, int force
)
85 ASSContext
*ass
= s
->priv_data
;
86 DialogueLine
*dialogue
= ass
->dialogue_cache
;
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
;
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
);
102 dialogue
= ass
->dialogue_cache
= next
;
103 ass
->expected_readorder
++;
106 ass
->cache_size
-= n
;
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
);
112 static void insert_dialogue(ASSContext
*ass
, DialogueLine
*dialogue
)
114 DialogueLine
*cur
, *next
= NULL
, *prev
= NULL
;
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
)
126 /* from the beginning to the last one added */
128 next
= ass
->dialogue_cache
;
129 for (cur
= next
; cur
!= ass
->last_added_dialogue
; cur
= cur
->next
) {
130 if (cur
->readorder
> dialogue
->readorder
)
138 prev
->next
= dialogue
;
139 dialogue
->prev
= prev
;
141 dialogue
->prev
= ass
->dialogue_cache
;
142 ass
->dialogue_cache
= dialogue
;
145 next
->prev
= dialogue
;
146 dialogue
->next
= next
;
149 ass
->last_added_dialogue
= dialogue
;
152 static int write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
154 ASSContext
*ass
= s
->priv_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
));
166 return AVERROR(ENOMEM
);
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
);
175 if (ass
->ssa_mode
&& !strncmp(p
, "Marked=", 7))
178 layer
= strtol(p
, &p
, 10);
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;
188 text_len
= strlen(p
);
189 while (text_len
> 0 && p
[text_len
- 1] == '\r' || p
[text_len
- 1] == '\n')
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
) {
197 return AVERROR(ENOMEM
);
199 insert_dialogue(ass
, dialogue
);
200 purge_dialogues(s
, ass
->ignore_readorder
);
205 static int write_trailer(AVFormatContext
*s
)
207 ASSContext
*ass
= s
->priv_data
;
209 purge_dialogues(s
, 1);
212 ffio_write_lines(s
->pb
, ass
->trailer
, ass
->trailer_size
, NULL
);
218 #define OFFSET(x) offsetof(ASSContext, x)
219 #define E AV_OPT_FLAG_ENCODING_PARAM
220 static 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
},
225 static const AVClass ass_class
= {
226 .class_name
= "ass muxer",
227 .item_name
= av_default_item_name
,
229 .version
= LIBAVUTIL_VERSION_INT
,
232 const FFOutputFormat ff_ass_muxer
= {
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
,
240 .p
.flags
= AVFMT_GLOBALHEADER
| AVFMT_NOTIMESTAMPS
| AVFMT_TS_NONSTRICT
,
241 .flags_internal
= FF_OFMT_FLAG_MAX_ONE_OF_EACH
|
242 FF_OFMT_FLAG_ONLY_DEFAULT_CODECS
,
243 .p
.priv_class
= &ass_class
,
244 .priv_data_size
= sizeof(ASSContext
),
245 .write_header
= write_header
,
246 .write_packet
= write_packet
,
247 .write_trailer
= write_trailer
,