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