1/* 2 * SubRip subtitle demuxer 3 * Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.org> 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22#include "avformat.h" 23#include "internal.h" 24#include "subtitles.h" 25#include "libavutil/bprint.h" 26#include "libavutil/intreadwrite.h" 27 28typedef struct { 29 FFDemuxSubtitlesQueue q; 30} SRTContext; 31 32static int srt_probe(AVProbeData *p) 33{ 34 const unsigned char *ptr = p->buf; 35 int i, v, num = 0; 36 37 if (AV_RB24(ptr) == 0xEFBBBF) 38 ptr += 3; /* skip UTF-8 BOM */ 39 40 while (*ptr == '\r' || *ptr == '\n') 41 ptr++; 42 for (i=0; i<2; i++) { 43 if ((num == i || num + 1 == i) 44 && sscanf(ptr, "%*d:%*2d:%*2d%*1[,.]%*3d --> %*d:%*2d:%*2d%*1[,.]%3d", &v) == 1) 45 return AVPROBE_SCORE_MAX; 46 num = atoi(ptr); 47 ptr += ff_subtitles_next_line(ptr); 48 } 49 return 0; 50} 51 52static int64_t get_pts(const char **buf, int *duration, 53 int32_t *x1, int32_t *y1, int32_t *x2, int32_t *y2) 54{ 55 int i; 56 57 for (i=0; i<2; i++) { 58 int hh1, mm1, ss1, ms1; 59 int hh2, mm2, ss2, ms2; 60 if (sscanf(*buf, "%d:%2d:%2d%*1[,.]%3d --> %d:%2d:%2d%*1[,.]%3d" 61 "%*[ ]X1:%u X2:%u Y1:%u Y2:%u", 62 &hh1, &mm1, &ss1, &ms1, 63 &hh2, &mm2, &ss2, &ms2, 64 x1, x2, y1, y2) >= 8) { 65 int64_t start = (hh1*3600LL + mm1*60LL + ss1) * 1000LL + ms1; 66 int64_t end = (hh2*3600LL + mm2*60LL + ss2) * 1000LL + ms2; 67 *duration = end - start; 68 *buf += ff_subtitles_next_line(*buf); 69 return start; 70 } 71 *buf += ff_subtitles_next_line(*buf); 72 } 73 return AV_NOPTS_VALUE; 74} 75 76static int srt_read_header(AVFormatContext *s) 77{ 78 SRTContext *srt = s->priv_data; 79 AVBPrint buf; 80 AVStream *st = avformat_new_stream(s, NULL); 81 int res = 0; 82 83 if (!st) 84 return AVERROR(ENOMEM); 85 avpriv_set_pts_info(st, 64, 1, 1000); 86 st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; 87 st->codec->codec_id = AV_CODEC_ID_SUBRIP; 88 89 av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); 90 91 while (!url_feof(s->pb)) { 92 ff_subtitles_read_chunk(s->pb, &buf); 93 94 if (buf.len) { 95 int64_t pos = avio_tell(s->pb); 96 int64_t pts; 97 int duration; 98 const char *ptr = buf.str; 99 int32_t x1 = -1, y1 = -1, x2 = -1, y2 = -1; 100 AVPacket *sub; 101 102 pts = get_pts(&ptr, &duration, &x1, &y1, &x2, &y2); 103 if (pts != AV_NOPTS_VALUE) { 104 int len = buf.len - (ptr - buf.str); 105 if (len <= 0) 106 continue; 107 sub = ff_subtitles_queue_insert(&srt->q, ptr, len, 0); 108 if (!sub) { 109 res = AVERROR(ENOMEM); 110 goto end; 111 } 112 sub->pos = pos; 113 sub->pts = pts; 114 sub->duration = duration; 115 if (x1 != -1) { 116 uint8_t *p = av_packet_new_side_data(sub, AV_PKT_DATA_SUBTITLE_POSITION, 16); 117 if (p) { 118 AV_WL32(p, x1); 119 AV_WL32(p + 4, y1); 120 AV_WL32(p + 8, x2); 121 AV_WL32(p + 12, y2); 122 } 123 } 124 } 125 } 126 } 127 128 ff_subtitles_queue_finalize(&srt->q); 129 130end: 131 av_bprint_finalize(&buf, NULL); 132 return res; 133} 134 135static int srt_read_packet(AVFormatContext *s, AVPacket *pkt) 136{ 137 SRTContext *srt = s->priv_data; 138 return ff_subtitles_queue_read_packet(&srt->q, pkt); 139} 140 141static int srt_read_seek(AVFormatContext *s, int stream_index, 142 int64_t min_ts, int64_t ts, int64_t max_ts, int flags) 143{ 144 SRTContext *srt = s->priv_data; 145 return ff_subtitles_queue_seek(&srt->q, s, stream_index, 146 min_ts, ts, max_ts, flags); 147} 148 149static int srt_read_close(AVFormatContext *s) 150{ 151 SRTContext *srt = s->priv_data; 152 ff_subtitles_queue_clean(&srt->q); 153 return 0; 154} 155 156AVInputFormat ff_srt_demuxer = { 157 .name = "srt", 158 .long_name = NULL_IF_CONFIG_SMALL("SubRip subtitle"), 159 .priv_data_size = sizeof(SRTContext), 160 .read_probe = srt_probe, 161 .read_header = srt_read_header, 162 .read_packet = srt_read_packet, 163 .read_seek2 = srt_read_seek, 164 .read_close = srt_read_close, 165}; 166