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.     const unsigned char *ptr = p->buf;
  41.  
  42.     if (AV_RB24(ptr) == 0xEFBBBF)
  43.         ptr += 3;  /* skip UTF-8 BOM */
  44.     return !av_strncasecmp(ptr, "<window", 7) ? AVPROBE_SCORE_EXTENSION : 0;
  45. }
  46.  
  47. static int read_ts(const char *s)
  48. {
  49.     int hh, mm, ss, ms;
  50.  
  51.     if (sscanf(s, "%u:%u:%u.%u", &hh, &mm, &ss, &ms) == 4) return (hh*3600 + mm*60 + ss) * 100 + ms;
  52.     if (sscanf(s, "%u:%u:%u"   , &hh, &mm, &ss     ) == 3) return (hh*3600 + mm*60 + ss) * 100;
  53.     if (sscanf(s,    "%u:%u.%u",      &mm, &ss, &ms) == 3) return (          mm*60 + ss) * 100 + ms;
  54.     if (sscanf(s,    "%u:%u"   ,      &mm, &ss     ) == 2) return (          mm*60 + ss) * 100;
  55.     if (sscanf(s,       "%u.%u",           &ss, &ms) == 2) return (                  ss) * 100 + ms;
  56.     return strtol(s, NULL, 10) * 100;
  57. }
  58.  
  59. static int realtext_read_header(AVFormatContext *s)
  60. {
  61.     RealTextContext *rt = s->priv_data;
  62.     AVStream *st = avformat_new_stream(s, NULL);
  63.     AVBPrint buf;
  64.     char c = 0;
  65.     int res = 0, duration = read_ts("60"); // default duration is 60 seconds
  66.  
  67.     if (!st)
  68.         return AVERROR(ENOMEM);
  69.     avpriv_set_pts_info(st, 64, 1, 100);
  70.     st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
  71.     st->codec->codec_id   = AV_CODEC_ID_REALTEXT;
  72.  
  73.     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
  74.  
  75.     while (!url_feof(s->pb)) {
  76.         AVPacket *sub;
  77.         const int64_t pos = avio_tell(s->pb) - (c != 0);
  78.         int n = ff_smil_extract_next_chunk(s->pb, &buf, &c);
  79.  
  80.         if (n == 0)
  81.             break;
  82.  
  83.         if (!av_strncasecmp(buf.str, "<window", 7)) {
  84.             /* save header to extradata */
  85.             const char *p = ff_smil_get_attr_ptr(buf.str, "duration");
  86.  
  87.             if (p)
  88.                 duration = read_ts(p);
  89.             st->codec->extradata = av_strdup(buf.str);
  90.             if (!st->codec->extradata) {
  91.                 res = AVERROR(ENOMEM);
  92.                 goto end;
  93.             }
  94.             st->codec->extradata_size = buf.len + 1;
  95.         } else {
  96.             /* if we just read a <time> tag, introduce a new event, otherwise merge
  97.              * with the previous one */
  98.             int merge = !av_strncasecmp(buf.str, "<time", 5) ? 0 : 1;
  99.             sub = ff_subtitles_queue_insert(&rt->q, buf.str, buf.len, merge);
  100.             if (!sub) {
  101.                 res = AVERROR(ENOMEM);
  102.                 goto end;
  103.             }
  104.             if (!merge) {
  105.                 const char *begin = ff_smil_get_attr_ptr(buf.str, "begin");
  106.                 const char *end   = ff_smil_get_attr_ptr(buf.str, "end");
  107.  
  108.                 sub->pos      = pos;
  109.                 sub->pts      = begin ? read_ts(begin) : 0;
  110.                 sub->duration = end ? (read_ts(end) - sub->pts) : duration;
  111.             }
  112.         }
  113.         av_bprint_clear(&buf);
  114.     }
  115.     ff_subtitles_queue_finalize(&rt->q);
  116.  
  117. end:
  118.     av_bprint_finalize(&buf, NULL);
  119.     return res;
  120. }
  121.  
  122. static int realtext_read_packet(AVFormatContext *s, AVPacket *pkt)
  123. {
  124.     RealTextContext *rt = s->priv_data;
  125.     return ff_subtitles_queue_read_packet(&rt->q, pkt);
  126. }
  127.  
  128. static int realtext_read_seek(AVFormatContext *s, int stream_index,
  129.                              int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
  130. {
  131.     RealTextContext *rt = s->priv_data;
  132.     return ff_subtitles_queue_seek(&rt->q, s, stream_index,
  133.                                    min_ts, ts, max_ts, flags);
  134. }
  135.  
  136. static int realtext_read_close(AVFormatContext *s)
  137. {
  138.     RealTextContext *rt = s->priv_data;
  139.     ff_subtitles_queue_clean(&rt->q);
  140.     return 0;
  141. }
  142.  
  143. AVInputFormat ff_realtext_demuxer = {
  144.     .name           = "realtext",
  145.     .long_name      = NULL_IF_CONFIG_SMALL("RealText subtitle format"),
  146.     .priv_data_size = sizeof(RealTextContext),
  147.     .read_probe     = realtext_probe,
  148.     .read_header    = realtext_read_header,
  149.     .read_packet    = realtext_read_packet,
  150.     .read_seek2     = realtext_read_seek,
  151.     .read_close     = realtext_read_close,
  152.     .extensions     = "rt",
  153. };
  154.