yading@10: /* yading@10: * 3GPP TS 26.245 Timed Text encoder yading@10: * Copyright (c) 2012 Philip Langdale yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: #include yading@10: #include "avcodec.h" yading@10: #include "libavutil/avassert.h" yading@10: #include "libavutil/avstring.h" yading@10: #include "libavutil/intreadwrite.h" yading@10: #include "ass_split.h" yading@10: #include "ass.h" yading@10: yading@10: typedef struct { yading@10: ASSSplitContext *ass_ctx; yading@10: char buffer[2048]; yading@10: char *ptr; yading@10: char *end; yading@10: } MovTextContext; yading@10: yading@10: yading@10: static av_cold int mov_text_encode_init(AVCodecContext *avctx) yading@10: { yading@10: /* yading@10: * For now, we'll use a fixed default style. When we add styling yading@10: * support, this will be generated from the ASS style. yading@10: */ yading@10: static uint8_t text_sample_entry[] = { yading@10: 0x00, 0x00, 0x00, 0x00, // uint32_t displayFlags yading@10: 0x01, // int8_t horizontal-justification yading@10: 0xFF, // int8_t vertical-justification yading@10: 0x00, 0x00, 0x00, 0x00, // uint8_t background-color-rgba[4] yading@10: // BoxRecord { yading@10: 0x00, 0x00, // int16_t top yading@10: 0x00, 0x00, // int16_t left yading@10: 0x00, 0x00, // int16_t bottom yading@10: 0x00, 0x00, // int16_t right yading@10: // }; yading@10: // StyleRecord { yading@10: 0x00, 0x00, // uint16_t startChar yading@10: 0x00, 0x00, // uint16_t endChar yading@10: 0x00, 0x01, // uint16_t font-ID yading@10: 0x00, // uint8_t face-style-flags yading@10: 0x12, // uint8_t font-size yading@10: 0xFF, 0xFF, 0xFF, 0xFF, // uint8_t text-color-rgba[4] yading@10: // }; yading@10: // FontTableBox { yading@10: 0x00, 0x00, 0x00, 0x12, // uint32_t size yading@10: 'f', 't', 'a', 'b', // uint8_t name[4] yading@10: 0x00, 0x01, // uint16_t entry-count yading@10: // FontRecord { yading@10: 0x00, 0x01, // uint16_t font-ID yading@10: 0x05, // uint8_t font-name-length yading@10: 'S', 'e', 'r', 'i', 'f',// uint8_t font[font-name-length] yading@10: // }; yading@10: // }; yading@10: }; yading@10: yading@10: MovTextContext *s = avctx->priv_data; yading@10: yading@10: avctx->extradata_size = sizeof text_sample_entry; yading@10: avctx->extradata = av_mallocz(avctx->extradata_size); yading@10: if (!avctx->extradata) yading@10: return AVERROR(ENOMEM); yading@10: yading@10: memcpy(avctx->extradata, text_sample_entry, avctx->extradata_size); yading@10: yading@10: s->ass_ctx = ff_ass_split(avctx->subtitle_header); yading@10: return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: static void mov_text_text_cb(void *priv, const char *text, int len) yading@10: { yading@10: MovTextContext *s = priv; yading@10: av_assert0(s->end >= s->ptr); yading@10: av_strlcpy(s->ptr, text, FFMIN(s->end - s->ptr, len + 1)); yading@10: s->ptr += FFMIN(s->end - s->ptr, len); yading@10: } yading@10: yading@10: static void mov_text_new_line_cb(void *priv, int forced) yading@10: { yading@10: MovTextContext *s = priv; yading@10: av_assert0(s->end >= s->ptr); yading@10: av_strlcpy(s->ptr, "\n", FFMIN(s->end - s->ptr, 2)); yading@10: if (s->end > s->ptr) yading@10: s->ptr++; yading@10: } yading@10: yading@10: static const ASSCodesCallbacks mov_text_callbacks = { yading@10: .text = mov_text_text_cb, yading@10: .new_line = mov_text_new_line_cb, yading@10: }; yading@10: yading@10: static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, yading@10: int bufsize, const AVSubtitle *sub) yading@10: { yading@10: MovTextContext *s = avctx->priv_data; yading@10: ASSDialog *dialog; yading@10: int i, len, num; yading@10: yading@10: s->ptr = s->buffer; yading@10: s->end = s->ptr + sizeof(s->buffer); yading@10: yading@10: for (i = 0; i < sub->num_rects; i++) { yading@10: yading@10: if (sub->rects[i]->type != SUBTITLE_ASS) { yading@10: av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); yading@10: return AVERROR(ENOSYS); yading@10: } yading@10: yading@10: dialog = ff_ass_split_dialog(s->ass_ctx, sub->rects[i]->ass, 0, &num); yading@10: for (; dialog && num--; dialog++) { yading@10: ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text); yading@10: } yading@10: } yading@10: yading@10: if (s->ptr == s->buffer) yading@10: return 0; yading@10: yading@10: AV_WB16(buf, strlen(s->buffer)); yading@10: buf += 2; yading@10: yading@10: len = av_strlcpy(buf, s->buffer, bufsize - 2); yading@10: yading@10: if (len > bufsize-3) { yading@10: av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); yading@10: return AVERROR(EINVAL); yading@10: } yading@10: yading@10: return len + 2; yading@10: } yading@10: yading@10: static int mov_text_encode_close(AVCodecContext *avctx) yading@10: { yading@10: MovTextContext *s = avctx->priv_data; yading@10: ff_ass_split_free(s->ass_ctx); yading@10: return 0; yading@10: } yading@10: yading@10: AVCodec ff_movtext_encoder = { yading@10: .name = "mov_text", yading@10: .long_name = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"), yading@10: .type = AVMEDIA_TYPE_SUBTITLE, yading@10: .id = AV_CODEC_ID_MOV_TEXT, yading@10: .priv_data_size = sizeof(MovTextContext), yading@10: .init = mov_text_encode_init, yading@10: .encode_sub = mov_text_encode_frame, yading@10: .close = mov_text_encode_close, yading@10: };