Skip to content
Snippets Groups Projects
assdec.c 4.37 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * 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);
    
                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; i<ass->event_count; i++){
            ass->event[i]= p;
            while(*p && *p != '\n')
                p++;
    
        qsort(ass->event, ass->event_count, sizeof(*ass->event), (void*)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, *end;
    
    
        if(ass->event_index >= ass->event_count)
            return AVERROR(EIO);
    
        p= ass->event[ ass->event_index ];
    
    
        end= strchr(p, '\n');
        av_new_packet(pkt, end ? end-p+1 : 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,
    };