You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
5.1KB

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