Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright (c) 2012 Clément Bœsch
  3.  *
  4.  * This file is part of FFmpeg.
  5.  *
  6.  * FFmpeg is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU Lesser General Public
  8.  * License as published by the Free Software Foundation; either
  9.  * version 2.1 of the License, or (at your option) any later version.
  10.  *
  11.  * FFmpeg is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.  * Lesser General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU Lesser General Public
  17.  * License along with FFmpeg; if not, write to the Free Software
  18.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19.  */
  20.  
  21. /**
  22.  * @file
  23.  * RealText subtitle demuxer
  24.  * @see http://service.real.com/help/library/guides/ProductionGuide/prodguide/htmfiles/realtext.htm
  25.  */
  26.  
  27. #include "avformat.h"
  28. #include "internal.h"
  29. #include "subtitles.h"
  30. #include "libavutil/avstring.h"
  31. #include "libavutil/bprint.h"
  32. #include "libavutil/intreadwrite.h"
  33.  
  34. typedef struct {
  35.     FFDemuxSubtitlesQueue q;
  36. } RealTextContext;
  37.  
  38. static int realtext_probe(AVProbeData *p)
  39. {
  40.     char buf[7];
  41.     FFTextReader tr;
  42.     ff_text_init_buf(&tr, p->buf, p->buf_size);
  43.     ff_text_read(&tr, buf, sizeof(buf));
  44.  
  45.     return !av_strncasecmp(buf, "<window", 7) ? AVPROBE_SCORE_EXTENSION : 0;
  46. }
  47.  
  48. static int read_ts(const char *s)
  49. {
  50.     int hh, mm, ss, ms;
  51.  
  52.     if (sscanf(s, "%u:%u:%u.%u", &hh, &mm, &ss, &ms) == 4) return (hh*3600 + mm*60 + ss) * 100 + ms;
  53.     if (sscanf(s, "%u:%u:%u"   , &hh, &mm, &ss     ) == 3) return (hh*3600 + mm*60 + ss) * 100;
  54.     if (sscanf(s,    "%u:%u.%u",      &mm, &ss, &ms) == 3) return (          mm*60 + ss) * 100 + ms;
  55.     if (sscanf(s,    "%u:%u"   ,      &mm, &ss     ) == 2) return (          mm*60 + ss) * 100;
  56.     if (sscanf(s,       "%u.%u",           &ss, &ms) == 2) return (                  ss) * 100 + ms;
  57.     return strtol(s, NULL, 10) * 100;
  58. }
  59.  
  60. static int realtext_read_header(AVFormatContext *s)
  61. {
  62.     RealTextContext *rt = s->priv_data;
  63.     AVStream *st = avformat_new_stream(s, NULL);
  64.     AVBPrint buf;
  65.     char c = 0;
  66.     int res = 0, duration = read_ts("60"); // default duration is 60 seconds
  67.     FFTextReader tr;
  68.     ff_text_init_avio(s, &tr, s->pb);
  69.  
  70.     if (!st)
  71.         return AVERROR(ENOMEM);
  72.     avpriv_set_pts_info(st, 64, 1, 100);
  73.     st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
  74.     st->codec->codec_id   = AV_CODEC_ID_REALTEXT;
  75.  
  76.     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
  77.  
  78.     while (!ff_text_eof(&tr)) {
  79.         AVPacket *sub;
  80.         const int64_t pos = ff_text_pos(&tr) - (c != 0);
  81.         int n = ff_smil_extract_next_text_chunk(&tr, &buf, &c);
  82.  
  83.         if (n == 0)
  84.             break;
  85.  
  86.         if (!av_strncasecmp(buf.str, "<window", 7)) {
  87.             /* save header to extradata */
  88.             const char *p = ff_smil_get_attr_ptr(buf.str, "duration");
  89.  
  90.             if (p)
  91.                 duration = read_ts(p);
  92.             st->codec->extradata = av_strdup(buf.str);
  93.             if (!st->codec->extradata) {
  94.                 res = AVERROR(ENOMEM);
  95.                 goto end;
  96.             }
  97.             st->codec->extradata_size = buf.len + 1;
  98.         } else {
  99.             /* if we just read a <time> tag, introduce a new event, otherwise merge
  100.              * with the previous one */
  101.             int merge = !av_strncasecmp(buf.str, "<time", 5) ? 0 : 1;
  102.             sub = ff_subtitles_queue_insert(&rt->q, buf.str, buf.len, merge);
  103.             if (!sub) {
  104.                 res = AVERROR(ENOMEM);
  105.                 goto end;
  106.             }
  107.             if (!merge) {
  108.                 const char *begin = ff_smil_get_attr_ptr(buf.str, "begin");
  109.                 const char *end   = ff_smil_get_attr_ptr(buf.str, "end");
  110.  
  111.                 sub->pos      = pos;
  112.                 sub->pts      = begin ? read_ts(begin) : 0;
  113.                 sub->duration = end ? (read_ts(end) - sub->pts) : duration;
  114.             }
  115.         }
  116.         av_bprint_clear(&buf);
  117.     }
  118.     ff_subtitles_queue_finalize(&rt->q);
  119.  
  120. end:
  121.     av_bprint_finalize(&buf, NULL);
  122.     return res;
  123. }
  124.  
  125. static int realtext_read_packet(AVFormatContext *s, AVPacket *pkt)
  126. {
  127.     RealTextContext *rt = s->priv_data;
  128.     return ff_subtitles_queue_read_packet(&rt->q, pkt);
  129. }
  130.  
  131. static int realtext_read_seek(AVFormatContext *s, int stream_index,
  132.                              int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
  133. {
  134.     RealTextContext *rt = s->priv_data;
  135.     return ff_subtitles_queue_seek(&rt->q, s, stream_index,
  136.                                    min_ts, ts, max_ts, flags);
  137. }
  138.  
  139. static int realtext_read_close(AVFormatContext *s)
  140. {
  141.     RealTextContext *rt = s->priv_data;
  142.     ff_subtitles_queue_clean(&rt->q);
  143.     return 0;
  144. }
  145.  
  146. AVInputFormat ff_realtext_demuxer = {
  147.     .name           = "realtext",
  148.     .long_name      = NULL_IF_CONFIG_SMALL("RealText subtitle format"),
  149.     .priv_data_size = sizeof(RealTextContext),
  150.     .read_probe     = realtext_probe,
  151.     .read_header    = realtext_read_header,
  152.     .read_packet    = realtext_read_packet,
  153.     .read_seek2     = realtext_read_seek,
  154.     .read_close     = realtext_read_close,
  155.     .extensions     = "rt",
  156. };
  157.