/*
* TTML subtitle muxer
* Copyright (c) 2020 24i
*
* 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
*/
/**
* @file
* TTML subtitle muxer
* @see https://www.w3.org/TR/ttml1/
* @see https://www.w3.org/TR/ttml2/
* @see https://www.w3.org/TR/ttml-imsc/rec
*/
#include "libavutil/avstring.h"
#include "avformat.h"
#include "internal.h"
#include "mux.h"
#include "ttmlenc.h"
#include "libavcodec/ttmlenc.h"
#include "libavutil/internal.h"
enum TTMLPacketType {
PACKET_TYPE_PARAGRAPH,
PACKET_TYPE_DOCUMENT,
};
struct TTMLHeaderParameters {
const char *tt_element_params;
const char *pre_body_elements;
};
typedef struct TTMLMuxContext {
enum TTMLPacketType input_type;
unsigned int document_written;
} TTMLMuxContext;
static const char ttml_header_text[] =
"\n"
"\n"
"%s"
" \n"
"
pts); avio_w8(pb, '\n'); ttml_write_time(pb, " end", pkt->pts + pkt->duration); avio_printf(pb, ">"); avio_write(pb, pkt->data, pkt->size); avio_printf(pb, "
\n"); break; case PACKET_TYPE_DOCUMENT: // dump the given document out as-is. if (ttml_ctx->document_written) { av_log(ctx, AV_LOG_ERROR, "Attempting to write multiple TTML documents into a " "single document! The XML specification forbids this " "as there has to be a single root tag.\n"); return AVERROR(EINVAL); } avio_write(pb, pkt->data, pkt->size); ttml_ctx->document_written = 1; break; default: av_log(ctx, AV_LOG_ERROR, "Internal error: invalid TTML input packet type: %d!\n", ttml_ctx->input_type); return AVERROR_BUG; } return 0; } static int ttml_write_trailer(AVFormatContext *ctx) { TTMLMuxContext *ttml_ctx = ctx->priv_data; AVIOContext *pb = ctx->pb; if (ttml_ctx->input_type == PACKET_TYPE_PARAGRAPH) avio_printf(pb, ttml_footer_text); return 0; } const FFOutputFormat ff_ttml_muxer = { .p.name = "ttml", .p.long_name = NULL_IF_CONFIG_SMALL("TTML subtitle"), .p.extensions = "ttml", .p.mime_type = "text/ttml", .priv_data_size = sizeof(TTMLMuxContext), .p.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT, .p.video_codec = AV_CODEC_ID_NONE, .p.audio_codec = AV_CODEC_ID_NONE, .p.subtitle_codec = AV_CODEC_ID_TTML, .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_header = ttml_write_header, .write_packet = ttml_write_packet, .write_trailer = ttml_write_trailer, };