From de3ae185a4b168ffb79c8d966bc89a4f174e2d54 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer Date: Fri, 26 Sep 2008 02:19:21 +0000 Subject: [PATCH] 2 in 1, an ASS and SSA demuxer. Tested with -scodec copy on all ASS & SSA samples that i found on mphq (exactly 2). Originally committed as revision 15422 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/assdec.c | 186 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 libavformat/assdec.c diff --git a/libavformat/Makefile b/libavformat/Makefile index ad6b323f6b..c5ff173233 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -21,6 +21,7 @@ OBJS-$(CONFIG_APE_DEMUXER) += ape.o OBJS-$(CONFIG_ASF_DEMUXER) += asf.o asfcrypt.o riff.o OBJS-$(CONFIG_ASF_MUXER) += asf-enc.o riff.o OBJS-$(CONFIG_ASF_STREAM_MUXER) += asf-enc.o riff.o +OBJS-$(CONFIG_ASS_DEMUXER) += assdec.o OBJS-$(CONFIG_AU_DEMUXER) += au.o raw.o OBJS-$(CONFIG_AU_MUXER) += au.o OBJS-$(CONFIG_AVI_DEMUXER) += avidec.o riff.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 49d65f0372..d644879b77 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -59,6 +59,7 @@ void av_register_all(void) REGISTER_DEMUXER (APC, apc); REGISTER_DEMUXER (APE, ape); REGISTER_MUXDEMUX (ASF, asf); + REGISTER_DEMUXER (ASS, ass); REGISTER_MUXER (ASF_STREAM, asf_stream); REGISTER_MUXDEMUX (AU, au); REGISTER_MUXDEMUX (AVI, avi); diff --git a/libavformat/assdec.c b/libavformat/assdec.c new file mode 100644 index 0000000000..fa0ab793e0 --- /dev/null +++ b/libavformat/assdec.c @@ -0,0 +1,186 @@ +/* + * SSA/ASS demuxer + * Copyright (c) 2008 Michael Niedermayer + * + * 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 "avformat.h" + +#define MAX_LINESIZE 2000 + +typedef struct ASSContext{ + uint8_t *event_buffer; + uint8_t **event; + unsigned int event_count; + unsigned int event_index; +}ASSContext; + +static void get_line(ByteIOContext *s, char *buf, int maxlen) +{ + int i = 0; + char c; + + do{ + c = get_byte(s); + if (i < maxlen-1 && c != '\r') + buf[i++] = c; + }while(c != '\n' && c); + + buf[i] = 0; +} + +static int probe(AVProbeData *p) +{ + const char *header= "[Script Info]"; + + if( !memcmp(p->buf , header, strlen(header)) + || !memcmp(p->buf+3, header, strlen(header))) + return AVPROBE_SCORE_MAX; + + return 0; +} + +static int read_close(AVFormatContext *s) +{ + ASSContext *ass = s->priv_data; + + av_freep(&ass->event_buffer); + av_freep(&ass->event); + + return 0; +} + +static int64_t get_pts(const uint8_t *p) +{ + int hour, min, sec, hsec; + + if(sscanf(p, "%*[^,],%d:%d:%d%*c%d", &hour, &min, &sec, &hsec) != 4) + return AV_NOPTS_VALUE; + +// av_log(NULL, AV_LOG_ERROR, "%d %d %d %d %d [%s]\n", i, hour, min, sec, hsec, p); + + min+= 60*hour; + sec+= 60*min; + + return sec*100+hsec; +} + +static int event_cmp(uint8_t **a, uint8_t **b) +{ + return get_pts(*a) - get_pts(*b); +} + +static int read_header(AVFormatContext *s, AVFormatParameters *ap) +{ + int i, header_remaining; + ASSContext *ass = s->priv_data; + ByteIOContext *pb = s->pb; + AVStream *st; + int allocated[2]={0}; + uint8_t *p, **dst[2]={0}; + int pos[2]={0}; + + st = av_new_stream(s, 0); + if (!st) + return -1; + av_set_pts_info(st, 64, 1, 100); + st->codec->codec_type = CODEC_TYPE_SUBTITLE; + st->codec->codec_id= CODEC_ID_SSA; + + header_remaining= INT_MAX; + dst[0] = &st->codec->extradata; + dst[1] = &ass->event_buffer; + while(!url_feof(pb)){ + uint8_t line[MAX_LINESIZE]; + + get_line(pb, line, sizeof(line)); + + if(!memcmp(line, "[Events]", 8)) + header_remaining= 2; + else if(line[0]=='[') + header_remaining= INT_MAX; + + i= header_remaining==0; + + if(i && get_pts(line) == AV_NOPTS_VALUE) + continue; + + p = av_fast_realloc(*(dst[i]), &allocated[i], pos[i]+MAX_LINESIZE); + if(!p) + goto fail; + *(dst[i])= p; + memcpy(p + pos[i], line, strlen(line)+1); + pos[i] += strlen(line); + if(i) ass->event_count++; + else header_remaining--; + } + st->codec->extradata_size= pos[0]; + + if(ass->event_count >= UINT_MAX / sizeof(*ass->event)) + goto fail; + + ass->event= av_malloc(ass->event_count * sizeof(*ass->event)); + p= ass->event_buffer; + for(i=0; ievent_count; i++){ + ass->event[i]= p; + while(*p && *p != '\n') + p++; + *p++ = 0; + } + + qsort(ass->event, ass->event_count, sizeof(*ass->event), event_cmp); + + return 0; + +fail: + read_close(s); + + return -1; +} + +static int read_packet(AVFormatContext *s, AVPacket *pkt) +{ + ASSContext *ass = s->priv_data; + uint8_t *p; + + if(ass->event_index >= ass->event_count) + return AVERROR(EIO); + + p= ass->event[ ass->event_index ]; + + av_new_packet(pkt, strlen(p)); + pkt->flags |= PKT_FLAG_KEY; + pkt->pos= p - ass->event_buffer + s->streams[0]->codec->extradata_size; + pkt->pts= pkt->dts= get_pts(p); + memcpy(pkt->data, p, pkt->size); + + ass->event_index++; + + return 0; +} + +AVInputFormat ass_demuxer = { + "ass", + NULL_IF_CONFIG_SMALL("SSA/ASS format"), + sizeof(ASSContext), + probe, + read_header, + read_packet, + read_close, +// read_seek, +};