blob: dc4f0ffa9722032594ea52533d79ee48584c7448
1 | /* |
2 | * SSA/ASS encoder |
3 | * Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org> |
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 <string.h> |
23 | |
24 | #include "avcodec.h" |
25 | #include "ass.h" |
26 | #include "libavutil/avstring.h" |
27 | #include "libavutil/internal.h" |
28 | #include "libavutil/mem.h" |
29 | |
30 | typedef struct { |
31 | int id; ///< current event id, ReadOrder field |
32 | } ASSEncodeContext; |
33 | |
34 | static av_cold int ass_encode_init(AVCodecContext *avctx) |
35 | { |
36 | avctx->extradata = av_malloc(avctx->subtitle_header_size + 1); |
37 | if (!avctx->extradata) |
38 | return AVERROR(ENOMEM); |
39 | memcpy(avctx->extradata, avctx->subtitle_header, avctx->subtitle_header_size); |
40 | avctx->extradata_size = avctx->subtitle_header_size; |
41 | avctx->extradata[avctx->extradata_size] = 0; |
42 | return 0; |
43 | } |
44 | |
45 | static int ass_encode_frame(AVCodecContext *avctx, |
46 | unsigned char *buf, int bufsize, |
47 | const AVSubtitle *sub) |
48 | { |
49 | ASSEncodeContext *s = avctx->priv_data; |
50 | int i, len, total_len = 0; |
51 | |
52 | for (i=0; i<sub->num_rects; i++) { |
53 | char ass_line[2048]; |
54 | const char *ass = sub->rects[i]->ass; |
55 | long int layer; |
56 | char *p; |
57 | |
58 | if (sub->rects[i]->type != SUBTITLE_ASS) { |
59 | av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); |
60 | return -1; |
61 | } |
62 | |
63 | #if FF_API_ASS_TIMING |
64 | if (!strncmp(ass, "Dialogue: ", 10)) { |
65 | if (i > 0) { |
66 | av_log(avctx, AV_LOG_ERROR, "ASS encoder supports only one " |
67 | "ASS rectangle field.\n"); |
68 | return AVERROR_INVALIDDATA; |
69 | } |
70 | |
71 | ass += 10; // skip "Dialogue: " |
72 | /* parse Layer field. If it's a Marked field, the content |
73 | * will be "Marked=N" instead of the layer num, so we will |
74 | * have layer=0, which is fine. */ |
75 | layer = strtol(ass, &p, 10); |
76 | |
77 | #define SKIP_ENTRY(ptr) do { \ |
78 | char *sep = strchr(ptr, ','); \ |
79 | if (sep) \ |
80 | ptr = sep + 1; \ |
81 | } while (0) |
82 | |
83 | SKIP_ENTRY(p); // skip layer or marked |
84 | SKIP_ENTRY(p); // skip start timestamp |
85 | SKIP_ENTRY(p); // skip end timestamp |
86 | snprintf(ass_line, sizeof(ass_line), "%d,%ld,%s", ++s->id, layer, p); |
87 | ass_line[strcspn(ass_line, "\r\n")] = 0; |
88 | ass = ass_line; |
89 | } |
90 | #endif |
91 | |
92 | len = av_strlcpy(buf+total_len, ass, bufsize-total_len); |
93 | |
94 | if (len > bufsize-total_len-1) { |
95 | av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); |
96 | return -1; |
97 | } |
98 | |
99 | total_len += len; |
100 | } |
101 | |
102 | return total_len; |
103 | } |
104 | |
105 | #if CONFIG_SSA_ENCODER |
106 | AVCodec ff_ssa_encoder = { |
107 | .name = "ssa", |
108 | .long_name = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"), |
109 | .type = AVMEDIA_TYPE_SUBTITLE, |
110 | .id = AV_CODEC_ID_ASS, |
111 | .init = ass_encode_init, |
112 | .encode_sub = ass_encode_frame, |
113 | .priv_data_size = sizeof(ASSEncodeContext), |
114 | }; |
115 | #endif |
116 | |
117 | #if CONFIG_ASS_ENCODER |
118 | AVCodec ff_ass_encoder = { |
119 | .name = "ass", |
120 | .long_name = NULL_IF_CONFIG_SMALL("ASS (Advanced SubStation Alpha) subtitle"), |
121 | .type = AVMEDIA_TYPE_SUBTITLE, |
122 | .id = AV_CODEC_ID_ASS, |
123 | .init = ass_encode_init, |
124 | .encode_sub = ass_encode_frame, |
125 | .priv_data_size = sizeof(ASSEncodeContext), |
126 | }; |
127 | #endif |
128 |