Subversion Repositories Kolibri OS

Rev

Go to most recent revision | 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.  * SubViewer subtitle demuxer
  24.  * @see https://en.wikipedia.org/wiki/SubViewer
  25.  */
  26.  
  27. #include "avformat.h"
  28. #include "internal.h"
  29. #include "subtitles.h"
  30. #include "libavcodec/internal.h"
  31. #include "libavutil/avstring.h"
  32. #include "libavutil/bprint.h"
  33. #include "libavutil/intreadwrite.h"
  34.  
  35. typedef struct {
  36.     FFDemuxSubtitlesQueue q;
  37. } SubViewerContext;
  38.  
  39. static int subviewer_probe(AVProbeData *p)
  40. {
  41.     char c;
  42.     const unsigned char *ptr = p->buf;
  43.  
  44.     if (AV_RB24(ptr) == 0xEFBBBF)
  45.         ptr += 3;  /* skip UTF-8 BOM */
  46.     if (sscanf(ptr, "%*u:%*u:%*u.%*u,%*u:%*u:%*u.%*u%c", &c) == 1)
  47.         return AVPROBE_SCORE_EXTENSION;
  48.     if (!strncmp(ptr, "[INFORMATION]", 13))
  49.         return AVPROBE_SCORE_MAX/3;
  50.     return 0;
  51. }
  52.  
  53. static int read_ts(const char *s, int64_t *start, int *duration)
  54. {
  55.     int64_t end;
  56.     int hh1, mm1, ss1, ms1;
  57.     int hh2, mm2, ss2, ms2;
  58.  
  59.     if (sscanf(s, "%u:%u:%u.%u,%u:%u:%u.%u",
  60.                &hh1, &mm1, &ss1, &ms1, &hh2, &mm2, &ss2, &ms2) == 8) {
  61.         end    = (hh2*3600LL + mm2*60LL + ss2) * 100LL + ms2;
  62.         *start = (hh1*3600LL + mm1*60LL + ss1) * 100LL + ms1;
  63.         *duration = end - *start;
  64.         return 0;
  65.     }
  66.     return -1;
  67. }
  68.  
  69. static int subviewer_read_header(AVFormatContext *s)
  70. {
  71.     SubViewerContext *subviewer = s->priv_data;
  72.     AVStream *st = avformat_new_stream(s, NULL);
  73.     AVBPrint header;
  74.     int res = 0, new_event = 1;
  75.     int64_t pts_start = AV_NOPTS_VALUE;
  76.     int duration = -1;
  77.     AVPacket *sub = NULL;
  78.  
  79.     if (!st)
  80.         return AVERROR(ENOMEM);
  81.     avpriv_set_pts_info(st, 64, 1, 100);
  82.     st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
  83.     st->codec->codec_id   = AV_CODEC_ID_SUBVIEWER;
  84.  
  85.     av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED);
  86.  
  87.     while (!url_feof(s->pb)) {
  88.         char line[2048];
  89.         int64_t pos = 0;
  90.         int len = ff_get_line(s->pb, line, sizeof(line));
  91.  
  92.         if (!len)
  93.             break;
  94.  
  95.         line[strcspn(line, "\r\n")] = 0;
  96.  
  97.         if (line[0] == '[' && strncmp(line, "[br]", 4)) {
  98.  
  99.             /* ignore event style, XXX: add to side_data? */
  100.             if (strstr(line, "[COLF]") || strstr(line, "[SIZE]") ||
  101.                 strstr(line, "[FONT]") || strstr(line, "[STYLE]"))
  102.                 continue;
  103.  
  104.             if (!st->codec->extradata) { // header not finalized yet
  105.                 av_bprintf(&header, "%s\n", line);
  106.                 if (!strncmp(line, "[END INFORMATION]", 17) || !strncmp(line, "[SUBTITLE]", 10)) {
  107.                     /* end of header */
  108.                     res = avpriv_bprint_to_extradata(st->codec, &header);
  109.                     if (res < 0)
  110.                         goto end;
  111.                 } else if (strncmp(line, "[INFORMATION]", 13)) {
  112.                     /* assume file metadata at this point */
  113.                     int i, j = 0;
  114.                     char key[32], value[128];
  115.  
  116.                     for (i = 1; i < sizeof(key) - 1 && line[i] && line[i] != ']'; i++)
  117.                         key[i - 1] = av_tolower(line[i]);
  118.                     key[i - 1] = 0;
  119.  
  120.                     if (line[i] == ']')
  121.                         i++;
  122.                     while (line[i] == ' ')
  123.                         i++;
  124.                     while (j < sizeof(value) - 1 && line[i] && line[i] != ']')
  125.                         value[j++] = line[i++];
  126.                     value[j] = 0;
  127.  
  128.                     av_dict_set(&s->metadata, key, value, 0);
  129.                 }
  130.             }
  131.         } else if (read_ts(line, &pts_start, &duration) >= 0) {
  132.             new_event = 1;
  133.             pos = avio_tell(s->pb);
  134.         } else if (*line) {
  135.             if (!new_event) {
  136.                 sub = ff_subtitles_queue_insert(&subviewer->q, "\n", 1, 1);
  137.                 if (!sub) {
  138.                     res = AVERROR(ENOMEM);
  139.                     goto end;
  140.                 }
  141.             }
  142.             sub = ff_subtitles_queue_insert(&subviewer->q, line, strlen(line), !new_event);
  143.             if (!sub) {
  144.                 res = AVERROR(ENOMEM);
  145.                 goto end;
  146.             }
  147.             if (new_event) {
  148.                 sub->pos = pos;
  149.                 sub->pts = pts_start;
  150.                 sub->duration = duration;
  151.             }
  152.             new_event = 0;
  153.         }
  154.     }
  155.  
  156.     ff_subtitles_queue_finalize(&subviewer->q);
  157.  
  158. end:
  159.     av_bprint_finalize(&header, NULL);
  160.     return res;
  161. }
  162.  
  163. static int subviewer_read_packet(AVFormatContext *s, AVPacket *pkt)
  164. {
  165.     SubViewerContext *subviewer = s->priv_data;
  166.     return ff_subtitles_queue_read_packet(&subviewer->q, pkt);
  167. }
  168.  
  169. static int subviewer_read_seek(AVFormatContext *s, int stream_index,
  170.                                int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
  171. {
  172.     SubViewerContext *subviewer = s->priv_data;
  173.     return ff_subtitles_queue_seek(&subviewer->q, s, stream_index,
  174.                                    min_ts, ts, max_ts, flags);
  175. }
  176.  
  177. static int subviewer_read_close(AVFormatContext *s)
  178. {
  179.     SubViewerContext *subviewer = s->priv_data;
  180.     ff_subtitles_queue_clean(&subviewer->q);
  181.     return 0;
  182. }
  183.  
  184. AVInputFormat ff_subviewer_demuxer = {
  185.     .name           = "subviewer",
  186.     .long_name      = NULL_IF_CONFIG_SMALL("SubViewer subtitle format"),
  187.     .priv_data_size = sizeof(SubViewerContext),
  188.     .read_probe     = subviewer_probe,
  189.     .read_header    = subviewer_read_header,
  190.     .read_packet    = subviewer_read_packet,
  191.     .read_seek2     = subviewer_read_seek,
  192.     .read_close     = subviewer_read_close,
  193.     .extensions     = "sub",
  194. };
  195.