libavformat/assdec.c
Go to the documentation of this file.
00001 /*
00002  * SSA/ASS demuxer
00003  * Copyright (c) 2008 Michael Niedermayer
00004  *
00005  * This file is part of FFmpeg.
00006  *
00007  * FFmpeg is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * FFmpeg is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with FFmpeg; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00022 #include "libavutil/mathematics.h"
00023 #include "avformat.h"
00024 #include "internal.h"
00025 
00026 #define MAX_LINESIZE 2000
00027 
00028 typedef struct ASSContext{
00029     uint8_t *event_buffer;
00030     uint8_t **event;
00031     unsigned int event_count;
00032     unsigned int event_index;
00033 }ASSContext;
00034 
00035 static int probe(AVProbeData *p)
00036 {
00037     const char *header= "[Script Info]";
00038 
00039     if(   !memcmp(p->buf  , header, strlen(header))
00040        || !memcmp(p->buf+3, header, strlen(header)))
00041         return AVPROBE_SCORE_MAX;
00042 
00043     return 0;
00044 }
00045 
00046 static int read_close(AVFormatContext *s)
00047 {
00048     ASSContext *ass = s->priv_data;
00049 
00050     av_freep(&ass->event_buffer);
00051     av_freep(&ass->event);
00052 
00053     return 0;
00054 }
00055 
00056 static int64_t get_pts(const uint8_t *p)
00057 {
00058     int hour, min, sec, hsec;
00059 
00060     if(sscanf(p, "%*[^,],%d:%d:%d%*c%d", &hour, &min, &sec, &hsec) != 4)
00061         return AV_NOPTS_VALUE;
00062 
00063 //    av_log(NULL, AV_LOG_ERROR, "%d %d %d %d %d [%s]\n", i, hour, min, sec, hsec, p);
00064 
00065     min+= 60*hour;
00066     sec+= 60*min;
00067 
00068     return sec*100+hsec;
00069 }
00070 
00071 static int event_cmp(uint8_t **a, uint8_t **b)
00072 {
00073     return get_pts(*a) - get_pts(*b);
00074 }
00075 
00076 static int read_header(AVFormatContext *s, AVFormatParameters *ap)
00077 {
00078     int i, len, header_remaining;
00079     ASSContext *ass = s->priv_data;
00080     AVIOContext *pb = s->pb;
00081     AVStream *st;
00082     int allocated[2]={0};
00083     uint8_t *p, **dst[2]={0};
00084     int pos[2]={0};
00085 
00086     st = avformat_new_stream(s, NULL);
00087     if (!st)
00088         return -1;
00089     avpriv_set_pts_info(st, 64, 1, 100);
00090     st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
00091     st->codec->codec_id= CODEC_ID_SSA;
00092 
00093     header_remaining= INT_MAX;
00094     dst[0] = &st->codec->extradata;
00095     dst[1] = &ass->event_buffer;
00096     while(!url_feof(pb)){
00097         uint8_t line[MAX_LINESIZE];
00098 
00099         len = ff_get_line(pb, line, sizeof(line));
00100 
00101         if(!memcmp(line, "[Events]", 8))
00102             header_remaining= 2;
00103         else if(line[0]=='[')
00104             header_remaining= INT_MAX;
00105 
00106         i= header_remaining==0;
00107 
00108         if(i && get_pts(line) == AV_NOPTS_VALUE)
00109             continue;
00110 
00111         p = av_fast_realloc(*(dst[i]), &allocated[i], pos[i]+MAX_LINESIZE);
00112         if(!p)
00113             goto fail;
00114         *(dst[i])= p;
00115         memcpy(p + pos[i], line, len+1);
00116         pos[i] += len;
00117         if(i) ass->event_count++;
00118         else  header_remaining--;
00119     }
00120     st->codec->extradata_size= pos[0];
00121 
00122     if(ass->event_count >= UINT_MAX / sizeof(*ass->event))
00123         goto fail;
00124 
00125     ass->event= av_malloc(ass->event_count * sizeof(*ass->event));
00126     p= ass->event_buffer;
00127     for(i=0; i<ass->event_count; i++){
00128         ass->event[i]= p;
00129         while(*p && *p != '\n')
00130             p++;
00131         p++;
00132     }
00133 
00134     qsort(ass->event, ass->event_count, sizeof(*ass->event), (void*)event_cmp);
00135 
00136     return 0;
00137 
00138 fail:
00139     read_close(s);
00140 
00141     return -1;
00142 }
00143 
00144 static int read_packet(AVFormatContext *s, AVPacket *pkt)
00145 {
00146     ASSContext *ass = s->priv_data;
00147     uint8_t *p, *end;
00148 
00149     if(ass->event_index >= ass->event_count)
00150         return AVERROR(EIO);
00151 
00152     p= ass->event[ ass->event_index ];
00153 
00154     end= strchr(p, '\n');
00155     av_new_packet(pkt, end ? end-p+1 : strlen(p));
00156     pkt->flags |= AV_PKT_FLAG_KEY;
00157     pkt->pos= p - ass->event_buffer + s->streams[0]->codec->extradata_size;
00158     pkt->pts= pkt->dts= get_pts(p);
00159     memcpy(pkt->data, p, pkt->size);
00160 
00161     ass->event_index++;
00162 
00163     return 0;
00164 }
00165 
00166 static int read_seek2(AVFormatContext *s, int stream_index,
00167                       int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
00168 {
00169     ASSContext *ass = s->priv_data;
00170 
00171     if (flags & AVSEEK_FLAG_BYTE) {
00172         return AVERROR(ENOSYS);
00173     } else if (flags & AVSEEK_FLAG_FRAME) {
00174         if (ts < 0 || ts >= ass->event_count)
00175             return AVERROR(ERANGE);
00176         ass->event_index = ts;
00177     } else {
00178         int i, idx = -1;
00179         int64_t min_ts_diff = INT64_MAX;
00180         if (stream_index == -1) {
00181             AVRational time_base = s->streams[0]->time_base;
00182             ts = av_rescale_q(ts, AV_TIME_BASE_Q, time_base);
00183             min_ts = av_rescale_rnd(min_ts, time_base.den,
00184                                     time_base.num * (int64_t)AV_TIME_BASE,
00185                                     AV_ROUND_UP);
00186             max_ts = av_rescale_rnd(max_ts, time_base.den,
00187                                     time_base.num * (int64_t)AV_TIME_BASE,
00188                                     AV_ROUND_DOWN);
00189         }
00190         /* TODO: ass->event[] is sorted by pts so we could do a binary search */
00191         for (i=0; i<ass->event_count; i++) {
00192             int64_t pts = get_pts(ass->event[i]);
00193             int64_t ts_diff = FFABS(pts - ts);
00194             if (pts >= min_ts && pts <= max_ts && ts_diff < min_ts_diff) {
00195                 min_ts_diff = ts_diff;
00196                 idx = i;
00197             }
00198         }
00199         if (idx < 0)
00200             return AVERROR(ERANGE);
00201         ass->event_index = idx;
00202     }
00203     return 0;
00204 }
00205 
00206 AVInputFormat ff_ass_demuxer = {
00207     .name           = "ass",
00208     .long_name      = NULL_IF_CONFIG_SMALL("Advanced SubStation Alpha subtitle format"),
00209     .priv_data_size = sizeof(ASSContext),
00210     .read_probe     = probe,
00211     .read_header    = read_header,
00212     .read_packet    = read_packet,
00213     .read_close     = read_close,
00214     .read_seek2     = read_seek2,
00215 };