Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * LRC lyrics file format decoder
  3.  * Copyright (c) 2014 StarBrilliant <m13253@hotmail.com>
  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 <inttypes.h>
  23. #include <stdint.h>
  24. #include <string.h>
  25.  
  26. #include "avformat.h"
  27. #include "internal.h"
  28. #include "lrc.h"
  29. #include "metadata.h"
  30. #include "subtitles.h"
  31. #include "version.h"
  32. #include "libavutil/bprint.h"
  33. #include "libavutil/dict.h"
  34. #include "libavutil/log.h"
  35. #include "libavutil/macros.h"
  36.  
  37. static int lrc_write_header(AVFormatContext *s)
  38. {
  39.     const AVDictionaryEntry *metadata_item;
  40.  
  41.     if(s->nb_streams != 1 ||
  42.        s->streams[0]->codec->codec_type != AVMEDIA_TYPE_SUBTITLE) {
  43.         av_log(s, AV_LOG_ERROR,
  44.                "LRC supports only a single subtitle stream.\n");
  45.         return AVERROR(EINVAL);
  46.     }
  47.     if(s->streams[0]->codec->codec_id != AV_CODEC_ID_SUBRIP &&
  48.        s->streams[0]->codec->codec_id != AV_CODEC_ID_TEXT) {
  49.         av_log(s, AV_LOG_ERROR, "Unsupported subtitle codec: %s\n",
  50.                avcodec_get_name(s->streams[0]->codec->codec_id));
  51.         return AVERROR(EINVAL);
  52.     }
  53.     avpriv_set_pts_info(s->streams[0], 64, 1, 100);
  54.  
  55.     ff_metadata_conv_ctx(s, ff_lrc_metadata_conv, NULL);
  56.     if(!(s->flags & AVFMT_FLAG_BITEXACT)) { // avoid breaking regression tests
  57.         /* LRC provides a metadata slot for specifying encoder version
  58.          * in addition to encoder name. We will store LIBAVFORMAT_VERSION
  59.          * to it.
  60.          */
  61.         av_dict_set(&s->metadata, "ve", AV_STRINGIFY(LIBAVFORMAT_VERSION), 0);
  62.     } else {
  63.         av_dict_set(&s->metadata, "ve", NULL, 0);
  64.     }
  65.     for(metadata_item = NULL;
  66.        (metadata_item = av_dict_get(s->metadata, "", metadata_item,
  67.                                     AV_DICT_IGNORE_SUFFIX));) {
  68.         char *delim;
  69.         if(!metadata_item->value[0]) {
  70.             continue;
  71.         }
  72.         while((delim = strchr(metadata_item->value, '\n'))) {
  73.             *delim = ' ';
  74.         }
  75.         while((delim = strchr(metadata_item->value, '\r'))) {
  76.             *delim = ' ';
  77.         }
  78.         avio_printf(s->pb, "[%s:%s]\n",
  79.                     metadata_item->key, metadata_item->value);
  80.     }
  81.     avio_printf(s->pb, "\n");
  82.     return 0;
  83. }
  84.  
  85. static int lrc_write_packet(AVFormatContext *s, AVPacket *pkt)
  86. {
  87.     if(pkt->pts != AV_NOPTS_VALUE) {
  88.         char *data = av_malloc(pkt->size + 1);
  89.         char *line;
  90.         char *delim;
  91.  
  92.         if(!data) {
  93.             return AVERROR(ENOMEM);
  94.         }
  95.         memcpy(data, pkt->data, pkt->size);
  96.         data[pkt->size] = '\0';
  97.  
  98.         for(delim = data + pkt->size - 1;
  99.             delim >= data && (delim[0] == '\n' || delim[0] == '\r'); delim--) {
  100.             delim[0] = '\0'; // Strip last empty lines
  101.         }
  102.         line = data;
  103.         while(line[0] == '\n' || line[0] == '\r') {
  104.             line++; // Skip first empty lines
  105.         }
  106.  
  107.         while(line) {
  108.             delim = strchr(line, '\n');
  109.             if(delim) {
  110.                 if(delim > line && delim[-1] == '\r') {
  111.                     delim[-1] = '\0';
  112.                 }
  113.                 delim[0] = '\0';
  114.                 delim++;
  115.             }
  116.             if(line[0] == '[') {
  117.                 av_log(s, AV_LOG_WARNING,
  118.                        "Subtitle starts with '[', may cause problems with LRC format.\n");
  119.             }
  120.  
  121.             if(pkt->pts >= 0) {
  122.                 avio_printf(s->pb, "[%02"PRId64":%02"PRId64".%02"PRId64"]",
  123.                             (pkt->pts / 6000),
  124.                             ((pkt->pts / 100) % 60),
  125.                             (pkt->pts % 100));
  126.             } else {
  127.                 /* Offset feature of LRC can easily make pts negative,
  128.                  * we just output it directly and let the player drop it. */
  129.                 avio_printf(s->pb, "[-%02"PRId64":%02"PRId64".%02"PRId64"]",
  130.                             (-pkt->pts) / 6000,
  131.                             ((-pkt->pts) / 100) % 60,
  132.                             (-pkt->pts) % 100);
  133.             }
  134.             avio_printf(s->pb, "%s\n", line);
  135.             line = delim;
  136.         }
  137.         av_free(data);
  138.     }
  139.     return 0;
  140. }
  141.  
  142. AVOutputFormat ff_lrc_muxer = {
  143.     .name           = "lrc",
  144.     .long_name      = NULL_IF_CONFIG_SMALL("LRC lyrics"),
  145.     .extensions     = "lrc",
  146.     .priv_data_size = 0,
  147.     .write_header   = lrc_write_header,
  148.     .write_packet   = lrc_write_packet,
  149.     .flags          = AVFMT_VARIABLE_FPS | AVFMT_GLOBALHEADER |
  150.                       AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT,
  151.     .subtitle_codec = AV_CODEC_ID_SUBRIP
  152. };
  153.