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.

178 lines
4.6KB

  1. /*
  2. * Metadata demuxer
  3. * Copyright (c) 2010 Anton Khirnov
  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 "libavutil/mathematics.h"
  22. #include "avformat.h"
  23. #include "ffmeta.h"
  24. #include "internal.h"
  25. #include "libavutil/dict.h"
  26. static int probe(AVProbeData *p)
  27. {
  28. if(!memcmp(p->buf, ID_STRING, strlen(ID_STRING)))
  29. return AVPROBE_SCORE_MAX;
  30. return 0;
  31. }
  32. static void get_line(AVIOContext *s, uint8_t *buf, int size)
  33. {
  34. do {
  35. uint8_t c;
  36. int i = 0;
  37. while ((c = avio_r8(s))) {
  38. if (c == '\\') {
  39. if (i < size - 1)
  40. buf[i++] = c;
  41. c = avio_r8(s);
  42. } else if (c == '\n')
  43. break;
  44. if (i < size - 1)
  45. buf[i++] = c;
  46. }
  47. buf[i] = 0;
  48. } while (!avio_feof(s) && (buf[0] == ';' || buf[0] == '#' || buf[0] == 0));
  49. }
  50. static AVChapter *read_chapter(AVFormatContext *s)
  51. {
  52. uint8_t line[256];
  53. int64_t start, end;
  54. AVRational tb = {1, 1e9};
  55. get_line(s->pb, line, sizeof(line));
  56. if (sscanf(line, "TIMEBASE=%d/%d", &tb.num, &tb.den))
  57. get_line(s->pb, line, sizeof(line));
  58. if (!sscanf(line, "START=%"SCNd64, &start)) {
  59. av_log(s, AV_LOG_ERROR, "Expected chapter start timestamp, found %s.\n", line);
  60. start = (s->nb_chapters && s->chapters[s->nb_chapters - 1]->end != AV_NOPTS_VALUE) ?
  61. s->chapters[s->nb_chapters - 1]->end : 0;
  62. } else
  63. get_line(s->pb, line, sizeof(line));
  64. if (!sscanf(line, "END=%"SCNd64, &end)) {
  65. av_log(s, AV_LOG_ERROR, "Expected chapter end timestamp, found %s.\n", line);
  66. end = AV_NOPTS_VALUE;
  67. }
  68. return avpriv_new_chapter(s, s->nb_chapters, tb, start, end, NULL);
  69. }
  70. static uint8_t *unescape(const uint8_t *buf, int size)
  71. {
  72. uint8_t *ret = av_malloc(size + 1);
  73. uint8_t *p1 = ret;
  74. const uint8_t *p2 = buf;
  75. if (!ret)
  76. return NULL;
  77. while (p2 < buf + size) {
  78. if (*p2 == '\\')
  79. p2++;
  80. *p1++ = *p2++;
  81. }
  82. *p1 = 0;
  83. return ret;
  84. }
  85. static int read_tag(const uint8_t *line, AVDictionary **m)
  86. {
  87. uint8_t *key, *value;
  88. const uint8_t *p = line;
  89. /* find first not escaped '=' */
  90. while (1) {
  91. if (*p == '=')
  92. break;
  93. else if (*p == '\\')
  94. p++;
  95. if (*p++)
  96. continue;
  97. return 0;
  98. }
  99. if (!(key = unescape(line, p - line)))
  100. return AVERROR(ENOMEM);
  101. if (!(value = unescape(p + 1, strlen(p + 1)))) {
  102. av_free(key);
  103. return AVERROR(ENOMEM);
  104. }
  105. av_dict_set(m, key, value, AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
  106. return 0;
  107. }
  108. static int read_header(AVFormatContext *s)
  109. {
  110. AVDictionary **m = &s->metadata;
  111. uint8_t line[1024];
  112. while(!avio_feof(s->pb)) {
  113. get_line(s->pb, line, sizeof(line));
  114. if (!memcmp(line, ID_STREAM, strlen(ID_STREAM))) {
  115. AVStream *st = avformat_new_stream(s, NULL);
  116. if (!st)
  117. return AVERROR(ENOMEM);
  118. st->codec->codec_type = AVMEDIA_TYPE_DATA;
  119. st->codec->codec_id = AV_CODEC_ID_FFMETADATA;
  120. m = &st->metadata;
  121. } else if (!memcmp(line, ID_CHAPTER, strlen(ID_CHAPTER))) {
  122. AVChapter *ch = read_chapter(s);
  123. if (!ch)
  124. return AVERROR(ENOMEM);
  125. m = &ch->metadata;
  126. } else
  127. read_tag(line, m);
  128. }
  129. s->start_time = 0;
  130. if (s->nb_chapters)
  131. s->duration = av_rescale_q(s->chapters[s->nb_chapters - 1]->end,
  132. s->chapters[s->nb_chapters - 1]->time_base,
  133. AV_TIME_BASE_Q);
  134. return 0;
  135. }
  136. static int read_packet(AVFormatContext *s, AVPacket *pkt)
  137. {
  138. return AVERROR_EOF;
  139. }
  140. AVInputFormat ff_ffmetadata_demuxer = {
  141. .name = "ffmetadata",
  142. .long_name = NULL_IF_CONFIG_SMALL("FFmpeg metadata in text"),
  143. .read_probe = probe,
  144. .read_header = read_header,
  145. .read_packet = read_packet,
  146. };