| /* |
| * 3GPP TS 26.245 Timed Text decoder |
| * Copyright (c) 2012 Philip Langdale <philipl@overt.org> |
| * |
| * This file is part of FFmpeg. |
| * |
| * FFmpeg is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * FFmpeg is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with FFmpeg; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "avcodec.h" |
| #include "ass.h" |
| #include "libavutil/avstring.h" |
| #include "libavutil/common.h" |
| #include "libavutil/bprint.h" |
| #include "libavutil/intreadwrite.h" |
| |
| static int text_to_ass(AVBPrint *buf, const char *text, const char *text_end) |
| { |
| while (text < text_end) { |
| switch (*text) { |
| case '\r': |
| break; |
| case '\n': |
| av_bprintf(buf, "\\N"); |
| break; |
| default: |
| av_bprint_chars(buf, *text, 1); |
| break; |
| } |
| text++; |
| } |
| |
| av_bprintf(buf, "\r\n"); |
| return 0; |
| } |
| |
| static int mov_text_init(AVCodecContext *avctx) { |
| /* |
| * TODO: Handle the default text style. |
| * NB: Most players ignore styles completely, with the result that |
| * it's very common to find files where the default style is broken |
| * and respecting it results in a worse experience than ignoring it. |
| */ |
| return ff_ass_subtitle_header_default(avctx); |
| } |
| |
| static int mov_text_decode_frame(AVCodecContext *avctx, |
| void *data, int *got_sub_ptr, AVPacket *avpkt) |
| { |
| AVSubtitle *sub = data; |
| int ts_start, ts_end; |
| AVBPrint buf; |
| const char *ptr = avpkt->data; |
| const char *end; |
| |
| if (!ptr || avpkt->size < 2) |
| return AVERROR_INVALIDDATA; |
| |
| /* |
| * A packet of size two with value zero is an empty subtitle |
| * used to mark the end of the previous non-empty subtitle. |
| * We can just drop them here as we have duration information |
| * already. If the value is non-zero, then it's technically a |
| * bad packet. |
| */ |
| if (avpkt->size == 2) |
| return AV_RB16(ptr) == 0 ? 0 : AVERROR_INVALIDDATA; |
| |
| /* |
| * The first two bytes of the packet are the length of the text string |
| * In complex cases, there are style descriptors appended to the string |
| * so we can't just assume the packet size is the string size. |
| */ |
| end = ptr + FFMIN(2 + AV_RB16(ptr), avpkt->size); |
| ptr += 2; |
| |
| ts_start = av_rescale_q(avpkt->pts, |
| avctx->time_base, |
| (AVRational){1,100}); |
| ts_end = av_rescale_q(avpkt->pts + avpkt->duration, |
| avctx->time_base, |
| (AVRational){1,100}); |
| |
| // Note that the spec recommends lines be no longer than 2048 characters. |
| av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); |
| text_to_ass(&buf, ptr, end); |
| |
| if (!av_bprint_is_complete(&buf)) |
| return AVERROR(ENOMEM); |
| |
| ff_ass_add_rect(sub, buf.str, ts_start, ts_end-ts_start, 0); |
| *got_sub_ptr = sub->num_rects > 0; |
| av_bprint_finalize(&buf, NULL); |
| return avpkt->size; |
| } |
| |
| AVCodec ff_movtext_decoder = { |
| .name = "mov_text", |
| .long_name = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"), |
| .type = AVMEDIA_TYPE_SUBTITLE, |
| .id = AV_CODEC_ID_MOV_TEXT, |
| .init = mov_text_init, |
| .decode = mov_text_decode_frame, |
| }; |