Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * 3GPP TS 26.245 Timed Text encoder | |
3 | * Copyright (c) 2012 Philip Langdale <philipl@overt.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 <stdarg.h> | |
23 | #include "avcodec.h" | |
24 | #include "libavutil/avassert.h" | |
25 | #include "libavutil/avstring.h" | |
26 | #include "libavutil/intreadwrite.h" | |
27 | #include "ass_split.h" | |
28 | #include "ass.h" | |
29 | ||
30 | typedef struct { | |
31 | ASSSplitContext *ass_ctx; | |
32 | char buffer[2048]; | |
33 | char *ptr; | |
34 | char *end; | |
35 | } MovTextContext; | |
36 | ||
37 | ||
38 | static av_cold int mov_text_encode_init(AVCodecContext *avctx) | |
39 | { | |
40 | /* | |
41 | * For now, we'll use a fixed default style. When we add styling | |
42 | * support, this will be generated from the ASS style. | |
43 | */ | |
44 | static const uint8_t text_sample_entry[] = { | |
45 | 0x00, 0x00, 0x00, 0x00, // uint32_t displayFlags | |
46 | 0x01, // int8_t horizontal-justification | |
47 | 0xFF, // int8_t vertical-justification | |
48 | 0x00, 0x00, 0x00, 0x00, // uint8_t background-color-rgba[4] | |
49 | // BoxRecord { | |
50 | 0x00, 0x00, // int16_t top | |
51 | 0x00, 0x00, // int16_t left | |
52 | 0x00, 0x00, // int16_t bottom | |
53 | 0x00, 0x00, // int16_t right | |
54 | // }; | |
55 | // StyleRecord { | |
56 | 0x00, 0x00, // uint16_t startChar | |
57 | 0x00, 0x00, // uint16_t endChar | |
58 | 0x00, 0x01, // uint16_t font-ID | |
59 | 0x00, // uint8_t face-style-flags | |
60 | 0x12, // uint8_t font-size | |
61 | 0xFF, 0xFF, 0xFF, 0xFF, // uint8_t text-color-rgba[4] | |
62 | // }; | |
63 | // FontTableBox { | |
64 | 0x00, 0x00, 0x00, 0x12, // uint32_t size | |
65 | 'f', 't', 'a', 'b', // uint8_t name[4] | |
66 | 0x00, 0x01, // uint16_t entry-count | |
67 | // FontRecord { | |
68 | 0x00, 0x01, // uint16_t font-ID | |
69 | 0x05, // uint8_t font-name-length | |
70 | 'S', 'e', 'r', 'i', 'f',// uint8_t font[font-name-length] | |
71 | // }; | |
72 | // }; | |
73 | }; | |
74 | ||
75 | MovTextContext *s = avctx->priv_data; | |
76 | ||
77 | avctx->extradata_size = sizeof text_sample_entry; | |
78 | avctx->extradata = av_mallocz(avctx->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); | |
79 | if (!avctx->extradata) | |
80 | return AVERROR(ENOMEM); | |
81 | ||
82 | memcpy(avctx->extradata, text_sample_entry, avctx->extradata_size); | |
83 | ||
84 | s->ass_ctx = ff_ass_split(avctx->subtitle_header); | |
85 | return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; | |
86 | } | |
87 | ||
88 | static void mov_text_text_cb(void *priv, const char *text, int len) | |
89 | { | |
90 | MovTextContext *s = priv; | |
91 | av_assert0(s->end >= s->ptr); | |
92 | av_strlcpy(s->ptr, text, FFMIN(s->end - s->ptr, len + 1)); | |
93 | s->ptr += FFMIN(s->end - s->ptr, len); | |
94 | } | |
95 | ||
96 | static void mov_text_new_line_cb(void *priv, int forced) | |
97 | { | |
98 | MovTextContext *s = priv; | |
99 | av_assert0(s->end >= s->ptr); | |
100 | av_strlcpy(s->ptr, "\n", FFMIN(s->end - s->ptr, 2)); | |
101 | if (s->end > s->ptr) | |
102 | s->ptr++; | |
103 | } | |
104 | ||
105 | static const ASSCodesCallbacks mov_text_callbacks = { | |
106 | .text = mov_text_text_cb, | |
107 | .new_line = mov_text_new_line_cb, | |
108 | }; | |
109 | ||
110 | static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, | |
111 | int bufsize, const AVSubtitle *sub) | |
112 | { | |
113 | MovTextContext *s = avctx->priv_data; | |
114 | ASSDialog *dialog; | |
115 | int i, len, num; | |
116 | ||
117 | s->ptr = s->buffer; | |
118 | s->end = s->ptr + sizeof(s->buffer); | |
119 | ||
120 | for (i = 0; i < sub->num_rects; i++) { | |
121 | ||
122 | if (sub->rects[i]->type != SUBTITLE_ASS) { | |
123 | av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); | |
124 | return AVERROR(ENOSYS); | |
125 | } | |
126 | ||
127 | dialog = ff_ass_split_dialog(s->ass_ctx, sub->rects[i]->ass, 0, &num); | |
128 | for (; dialog && num--; dialog++) { | |
129 | ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); | |
130 | } | |
131 | } | |
132 | ||
133 | if (s->ptr == s->buffer) | |
134 | return 0; | |
135 | ||
136 | AV_WB16(buf, strlen(s->buffer)); | |
137 | buf += 2; | |
138 | ||
139 | len = av_strlcpy(buf, s->buffer, bufsize - 2); | |
140 | ||
141 | if (len > bufsize-3) { | |
142 | av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); | |
143 | return AVERROR(EINVAL); | |
144 | } | |
145 | ||
146 | return len + 2; | |
147 | } | |
148 | ||
149 | static int mov_text_encode_close(AVCodecContext *avctx) | |
150 | { | |
151 | MovTextContext *s = avctx->priv_data; | |
152 | ff_ass_split_free(s->ass_ctx); | |
153 | return 0; | |
154 | } | |
155 | ||
156 | AVCodec ff_movtext_encoder = { | |
157 | .name = "mov_text", | |
158 | .long_name = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"), | |
159 | .type = AVMEDIA_TYPE_SUBTITLE, | |
160 | .id = AV_CODEC_ID_MOV_TEXT, | |
161 | .priv_data_size = sizeof(MovTextContext), | |
162 | .init = mov_text_encode_init, | |
163 | .encode_sub = mov_text_encode_frame, | |
164 | .close = mov_text_encode_close, | |
165 | }; |