1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * DivX (XSUB) subtitle encoder
3cabdff1aSopenharmony_ci * Copyright (c) 2005 DivX, Inc.
4cabdff1aSopenharmony_ci * Copyright (c) 2009 Bjorn Axelsson
5cabdff1aSopenharmony_ci *
6cabdff1aSopenharmony_ci * This file is part of FFmpeg.
7cabdff1aSopenharmony_ci *
8cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
9cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
10cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
11cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
12cabdff1aSopenharmony_ci *
13cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
14cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
15cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16cabdff1aSopenharmony_ci * Lesser General Public License for more details.
17cabdff1aSopenharmony_ci *
18cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
19cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
20cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21cabdff1aSopenharmony_ci */
22cabdff1aSopenharmony_ci
23cabdff1aSopenharmony_ci#include "avcodec.h"
24cabdff1aSopenharmony_ci#include "bytestream.h"
25cabdff1aSopenharmony_ci#include "codec_internal.h"
26cabdff1aSopenharmony_ci#include "put_bits.h"
27cabdff1aSopenharmony_ci
28cabdff1aSopenharmony_ci/**
29cabdff1aSopenharmony_ci * Number of pixels to pad left and right.
30cabdff1aSopenharmony_ci *
31cabdff1aSopenharmony_ci * The official encoder pads the subtitles with two pixels on either side,
32cabdff1aSopenharmony_ci * but until we find out why, we won't do it (we will pad to have width
33cabdff1aSopenharmony_ci * divisible by 2 though).
34cabdff1aSopenharmony_ci */
35cabdff1aSopenharmony_ci#define PADDING 0
36cabdff1aSopenharmony_ci#define PADDING_COLOR 0
37cabdff1aSopenharmony_ci
38cabdff1aSopenharmony_ci/**
39cabdff1aSopenharmony_ci * Encode a single color run. At most 16 bits will be used.
40cabdff1aSopenharmony_ci * @param len   length of the run, values > 255 mean "until end of line", may not be < 0.
41cabdff1aSopenharmony_ci * @param color color to encode, only the lowest two bits are used and all others must be 0.
42cabdff1aSopenharmony_ci */
43cabdff1aSopenharmony_cistatic void put_xsub_rle(PutBitContext *pb, int len, int color)
44cabdff1aSopenharmony_ci{
45cabdff1aSopenharmony_ci    if (len <= 255)
46cabdff1aSopenharmony_ci        put_bits(pb, 2 + ((ff_log2_tab[len] >> 1) << 2), len);
47cabdff1aSopenharmony_ci    else
48cabdff1aSopenharmony_ci        put_bits(pb, 14, 0);
49cabdff1aSopenharmony_ci    put_bits(pb, 2, color);
50cabdff1aSopenharmony_ci}
51cabdff1aSopenharmony_ci
52cabdff1aSopenharmony_ci/**
53cabdff1aSopenharmony_ci * Encode a 4-color bitmap with XSUB rle.
54cabdff1aSopenharmony_ci *
55cabdff1aSopenharmony_ci * The encoded bitmap may be wider than the source bitmap due to padding.
56cabdff1aSopenharmony_ci */
57cabdff1aSopenharmony_cistatic int xsub_encode_rle(PutBitContext *pb, const uint8_t *bitmap,
58cabdff1aSopenharmony_ci                           int linesize, int w, int h)
59cabdff1aSopenharmony_ci{
60cabdff1aSopenharmony_ci    int x0, x1, y, len, color = PADDING_COLOR;
61cabdff1aSopenharmony_ci
62cabdff1aSopenharmony_ci    for (y = 0; y < h; y++) {
63cabdff1aSopenharmony_ci        x0 = 0;
64cabdff1aSopenharmony_ci        while (x0 < w) {
65cabdff1aSopenharmony_ci            // Make sure we have enough room for at least one run and padding
66cabdff1aSopenharmony_ci            if (put_bytes_left(pb, 1) < 7)
67cabdff1aSopenharmony_ci                return AVERROR_BUFFER_TOO_SMALL;
68cabdff1aSopenharmony_ci
69cabdff1aSopenharmony_ci            x1 = x0;
70cabdff1aSopenharmony_ci            color = bitmap[x1++] & 3;
71cabdff1aSopenharmony_ci            while (x1 < w && (bitmap[x1] & 3) == color)
72cabdff1aSopenharmony_ci                x1++;
73cabdff1aSopenharmony_ci            len = x1 - x0;
74cabdff1aSopenharmony_ci            if (PADDING && x0 == 0) {
75cabdff1aSopenharmony_ci                if (color == PADDING_COLOR) {
76cabdff1aSopenharmony_ci                    len += PADDING;
77cabdff1aSopenharmony_ci                    x0  -= PADDING;
78cabdff1aSopenharmony_ci                } else
79cabdff1aSopenharmony_ci                    put_xsub_rle(pb, PADDING, PADDING_COLOR);
80cabdff1aSopenharmony_ci            }
81cabdff1aSopenharmony_ci
82cabdff1aSopenharmony_ci            // Run can't be longer than 255, unless it is the rest of a row
83cabdff1aSopenharmony_ci            if (x1 == w && color == PADDING_COLOR) {
84cabdff1aSopenharmony_ci                len += PADDING + (w&1);
85cabdff1aSopenharmony_ci            } else
86cabdff1aSopenharmony_ci                len = FFMIN(len, 255);
87cabdff1aSopenharmony_ci            put_xsub_rle(pb, len, color);
88cabdff1aSopenharmony_ci
89cabdff1aSopenharmony_ci            x0 += len;
90cabdff1aSopenharmony_ci        }
91cabdff1aSopenharmony_ci        if (color != PADDING_COLOR && (PADDING + (w&1)))
92cabdff1aSopenharmony_ci            put_xsub_rle(pb, PADDING + (w&1), PADDING_COLOR);
93cabdff1aSopenharmony_ci
94cabdff1aSopenharmony_ci        align_put_bits(pb);
95cabdff1aSopenharmony_ci
96cabdff1aSopenharmony_ci        bitmap += linesize;
97cabdff1aSopenharmony_ci    }
98cabdff1aSopenharmony_ci
99cabdff1aSopenharmony_ci    return 0;
100cabdff1aSopenharmony_ci}
101cabdff1aSopenharmony_ci
102cabdff1aSopenharmony_cistatic int make_tc(uint64_t ms, int *tc)
103cabdff1aSopenharmony_ci{
104cabdff1aSopenharmony_ci    static const int tc_divs[3] = { 1000, 60, 60 };
105cabdff1aSopenharmony_ci    int i;
106cabdff1aSopenharmony_ci    for (i=0; i<3; i++) {
107cabdff1aSopenharmony_ci        tc[i] = ms % tc_divs[i];
108cabdff1aSopenharmony_ci        ms /= tc_divs[i];
109cabdff1aSopenharmony_ci    }
110cabdff1aSopenharmony_ci    tc[3] = ms;
111cabdff1aSopenharmony_ci    return ms > 99;
112cabdff1aSopenharmony_ci}
113cabdff1aSopenharmony_ci
114cabdff1aSopenharmony_cistatic int xsub_encode(AVCodecContext *avctx, unsigned char *buf,
115cabdff1aSopenharmony_ci                       int bufsize, const AVSubtitle *h)
116cabdff1aSopenharmony_ci{
117cabdff1aSopenharmony_ci    uint64_t startTime = h->pts / 1000; // FIXME: need better solution...
118cabdff1aSopenharmony_ci    uint64_t endTime = startTime + h->end_display_time - h->start_display_time;
119cabdff1aSopenharmony_ci    int start_tc[4], end_tc[4];
120cabdff1aSopenharmony_ci    uint8_t *hdr = buf + 27; // Point behind the timestamp
121cabdff1aSopenharmony_ci    uint8_t *rlelenptr;
122cabdff1aSopenharmony_ci    uint16_t width, height;
123cabdff1aSopenharmony_ci    int i;
124cabdff1aSopenharmony_ci    PutBitContext pb;
125cabdff1aSopenharmony_ci
126cabdff1aSopenharmony_ci    if (bufsize < 27 + 7*2 + 4*3) {
127cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "Buffer too small for XSUB header.\n");
128cabdff1aSopenharmony_ci        return AVERROR_BUFFER_TOO_SMALL;
129cabdff1aSopenharmony_ci    }
130cabdff1aSopenharmony_ci
131cabdff1aSopenharmony_ci    // TODO: support multiple rects
132cabdff1aSopenharmony_ci    if (h->num_rects != 1)
133cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_WARNING, "Only single rects supported (%d in subtitle.)\n", h->num_rects);
134cabdff1aSopenharmony_ci
135cabdff1aSopenharmony_ci    // TODO: render text-based subtitles into bitmaps
136cabdff1aSopenharmony_ci    if (!h->rects[0]->data[0] || !h->rects[0]->data[1]) {
137cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_WARNING, "No subtitle bitmap available.\n");
138cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
139cabdff1aSopenharmony_ci    }
140cabdff1aSopenharmony_ci
141cabdff1aSopenharmony_ci    // TODO: color reduction, similar to dvdsub encoder
142cabdff1aSopenharmony_ci    if (h->rects[0]->nb_colors > 4)
143cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_WARNING, "No more than 4 subtitle colors supported (%d found.)\n", h->rects[0]->nb_colors);
144cabdff1aSopenharmony_ci
145cabdff1aSopenharmony_ci    // TODO: Palette swapping if color zero is not transparent
146cabdff1aSopenharmony_ci    if (((uint32_t *)h->rects[0]->data[1])[0] & 0xff000000)
147cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_WARNING, "Color index 0 is not transparent. Transparency will be messed up.\n");
148cabdff1aSopenharmony_ci
149cabdff1aSopenharmony_ci    if (make_tc(startTime, start_tc) || make_tc(endTime, end_tc)) {
150cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_WARNING, "Time code >= 100 hours.\n");
151cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
152cabdff1aSopenharmony_ci    }
153cabdff1aSopenharmony_ci
154cabdff1aSopenharmony_ci    snprintf(buf, 28,
155cabdff1aSopenharmony_ci        "[%02d:%02d:%02d.%03d-%02d:%02d:%02d.%03d]",
156cabdff1aSopenharmony_ci        start_tc[3], start_tc[2], start_tc[1], start_tc[0],
157cabdff1aSopenharmony_ci        end_tc[3],   end_tc[2],   end_tc[1],   end_tc[0]);
158cabdff1aSopenharmony_ci
159cabdff1aSopenharmony_ci    // Width and height must probably be multiples of 2.
160cabdff1aSopenharmony_ci    // 2 pixels required on either side of subtitle.
161cabdff1aSopenharmony_ci    // Possibly due to limitations of hardware renderers.
162cabdff1aSopenharmony_ci    // TODO: check if the bitmap is already padded
163cabdff1aSopenharmony_ci    width  = FFALIGN(h->rects[0]->w, 2) + PADDING * 2;
164cabdff1aSopenharmony_ci    height = FFALIGN(h->rects[0]->h, 2);
165cabdff1aSopenharmony_ci
166cabdff1aSopenharmony_ci    bytestream_put_le16(&hdr, width);
167cabdff1aSopenharmony_ci    bytestream_put_le16(&hdr, height);
168cabdff1aSopenharmony_ci    bytestream_put_le16(&hdr, h->rects[0]->x);
169cabdff1aSopenharmony_ci    bytestream_put_le16(&hdr, h->rects[0]->y);
170cabdff1aSopenharmony_ci    bytestream_put_le16(&hdr, h->rects[0]->x + width -1);
171cabdff1aSopenharmony_ci    bytestream_put_le16(&hdr, h->rects[0]->y + height -1);
172cabdff1aSopenharmony_ci
173cabdff1aSopenharmony_ci    rlelenptr = hdr; // Will store length of first field here later.
174cabdff1aSopenharmony_ci    hdr+=2;
175cabdff1aSopenharmony_ci
176cabdff1aSopenharmony_ci    // Palette
177cabdff1aSopenharmony_ci    for (i=0; i<4; i++)
178cabdff1aSopenharmony_ci        bytestream_put_be24(&hdr, ((uint32_t *)h->rects[0]->data[1])[i]);
179cabdff1aSopenharmony_ci
180cabdff1aSopenharmony_ci    // Bitmap
181cabdff1aSopenharmony_ci    // RLE buffer. Reserve 2 bytes for possible padding after the last row.
182cabdff1aSopenharmony_ci    init_put_bits(&pb, hdr, bufsize - (hdr - buf) - 2);
183cabdff1aSopenharmony_ci    if (xsub_encode_rle(&pb, h->rects[0]->data[0],
184cabdff1aSopenharmony_ci                        h->rects[0]->linesize[0] * 2,
185cabdff1aSopenharmony_ci                        h->rects[0]->w, (h->rects[0]->h + 1) >> 1))
186cabdff1aSopenharmony_ci        return AVERROR_BUFFER_TOO_SMALL;
187cabdff1aSopenharmony_ci    bytestream_put_le16(&rlelenptr, put_bytes_count(&pb, 0)); // Length of first field
188cabdff1aSopenharmony_ci
189cabdff1aSopenharmony_ci    if (xsub_encode_rle(&pb, h->rects[0]->data[0] + h->rects[0]->linesize[0],
190cabdff1aSopenharmony_ci                        h->rects[0]->linesize[0] * 2,
191cabdff1aSopenharmony_ci                        h->rects[0]->w, h->rects[0]->h >> 1))
192cabdff1aSopenharmony_ci        return AVERROR_BUFFER_TOO_SMALL;
193cabdff1aSopenharmony_ci
194cabdff1aSopenharmony_ci    // Enforce total height to be a multiple of 2
195cabdff1aSopenharmony_ci    if (h->rects[0]->h & 1) {
196cabdff1aSopenharmony_ci        put_xsub_rle(&pb, h->rects[0]->w, PADDING_COLOR);
197cabdff1aSopenharmony_ci    }
198cabdff1aSopenharmony_ci
199cabdff1aSopenharmony_ci    flush_put_bits(&pb);
200cabdff1aSopenharmony_ci
201cabdff1aSopenharmony_ci    return hdr - buf + put_bytes_output(&pb);
202cabdff1aSopenharmony_ci}
203cabdff1aSopenharmony_ci
204cabdff1aSopenharmony_cistatic av_cold int xsub_encoder_init(AVCodecContext *avctx)
205cabdff1aSopenharmony_ci{
206cabdff1aSopenharmony_ci    if (!avctx->codec_tag)
207cabdff1aSopenharmony_ci        avctx->codec_tag = MKTAG('D','X','S','B');
208cabdff1aSopenharmony_ci
209cabdff1aSopenharmony_ci    avctx->bits_per_coded_sample = 4;
210cabdff1aSopenharmony_ci
211cabdff1aSopenharmony_ci    return 0;
212cabdff1aSopenharmony_ci}
213cabdff1aSopenharmony_ci
214cabdff1aSopenharmony_ciconst FFCodec ff_xsub_encoder = {
215cabdff1aSopenharmony_ci    .p.name     = "xsub",
216cabdff1aSopenharmony_ci    .p.long_name = NULL_IF_CONFIG_SMALL("DivX subtitles (XSUB)"),
217cabdff1aSopenharmony_ci    .p.type     = AVMEDIA_TYPE_SUBTITLE,
218cabdff1aSopenharmony_ci    .p.id       = AV_CODEC_ID_XSUB,
219cabdff1aSopenharmony_ci    .init       = xsub_encoder_init,
220cabdff1aSopenharmony_ci    FF_CODEC_ENCODE_SUB_CB(xsub_encode),
221cabdff1aSopenharmony_ci    .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
222cabdff1aSopenharmony_ci};
223