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.

159 lines
4.8KB

  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. * @file
  22. * SAMI subtitle decoder
  23. * @see http://msdn.microsoft.com/en-us/library/ms971327.aspx
  24. */
  25. #include "ass.h"
  26. #include "libavutil/avstring.h"
  27. #include "libavutil/bprint.h"
  28. typedef struct {
  29. AVBPrint source;
  30. AVBPrint content;
  31. AVBPrint full;
  32. } SAMIContext;
  33. static int sami_paragraph_to_ass(AVCodecContext *avctx, const char *src)
  34. {
  35. SAMIContext *sami = avctx->priv_data;
  36. int ret = 0;
  37. char *tag = NULL;
  38. char *dupsrc = av_strdup(src);
  39. char *p = dupsrc;
  40. av_bprint_clear(&sami->content);
  41. for (;;) {
  42. char *saveptr = NULL;
  43. int prev_chr_is_space = 0;
  44. AVBPrint *dst = &sami->content;
  45. /* parse & extract paragraph tag */
  46. p = av_stristr(p, "<P");
  47. if (!p)
  48. break;
  49. if (p[2] != '>' && !isspace(p[2])) { // avoid confusion with tags such as <PRE>
  50. p++;
  51. continue;
  52. }
  53. if (dst->len) // add a separator with the previous paragraph if there was one
  54. av_bprintf(dst, "\\N");
  55. tag = av_strtok(p, ">", &saveptr);
  56. if (!tag || !saveptr)
  57. break;
  58. p = saveptr;
  59. /* check if the current paragraph is the "source" (speaker name) */
  60. if (av_stristr(tag, "ID=Source") || av_stristr(tag, "ID=\"Source\"")) {
  61. dst = &sami->source;
  62. av_bprint_clear(dst);
  63. }
  64. /* if empty event -> skip subtitle */
  65. while (isspace(*p))
  66. p++;
  67. if (!strncmp(p, "&nbsp;", 6)) {
  68. ret = -1;
  69. goto end;
  70. }
  71. /* extract the text, stripping most of the tags */
  72. while (*p) {
  73. if (*p == '<') {
  74. if (!av_strncasecmp(p, "<P", 2) && (p[2] == '>' || isspace(p[2])))
  75. break;
  76. if (!av_strncasecmp(p, "<BR", 3))
  77. av_bprintf(dst, "\\N");
  78. p++;
  79. while (*p && *p != '>')
  80. p++;
  81. if (!*p)
  82. break;
  83. if (*p == '>')
  84. p++;
  85. }
  86. if (!isspace(*p))
  87. av_bprint_chars(dst, *p, 1);
  88. else if (!prev_chr_is_space)
  89. av_bprint_chars(dst, ' ', 1);
  90. prev_chr_is_space = isspace(*p);
  91. p++;
  92. }
  93. }
  94. av_bprint_clear(&sami->full);
  95. if (sami->source.len)
  96. av_bprintf(&sami->full, "{\\i1}%s{\\i0}\\N", sami->source.str);
  97. av_bprintf(&sami->full, "%s\r\n", sami->content.str);
  98. end:
  99. av_free(dupsrc);
  100. return ret;
  101. }
  102. static int sami_decode_frame(AVCodecContext *avctx,
  103. void *data, int *got_sub_ptr, AVPacket *avpkt)
  104. {
  105. AVSubtitle *sub = data;
  106. const char *ptr = avpkt->data;
  107. SAMIContext *sami = avctx->priv_data;
  108. if (ptr && avpkt->size > 0 && !sami_paragraph_to_ass(avctx, ptr)) {
  109. int ts_start = av_rescale_q(avpkt->pts, avctx->time_base, (AVRational){1,100});
  110. int ts_duration = avpkt->duration != -1 ?
  111. av_rescale_q(avpkt->duration, avctx->time_base, (AVRational){1,100}) : -1;
  112. ff_ass_add_rect(sub, sami->full.str, ts_start, ts_duration, 0);
  113. }
  114. *got_sub_ptr = sub->num_rects > 0;
  115. return avpkt->size;
  116. }
  117. static av_cold int sami_init(AVCodecContext *avctx)
  118. {
  119. SAMIContext *sami = avctx->priv_data;
  120. av_bprint_init(&sami->source, 0, 2048);
  121. av_bprint_init(&sami->content, 0, 2048);
  122. av_bprint_init(&sami->full, 0, 2048);
  123. return ff_ass_subtitle_header_default(avctx);
  124. }
  125. static av_cold int sami_close(AVCodecContext *avctx)
  126. {
  127. SAMIContext *sami = avctx->priv_data;
  128. av_bprint_finalize(&sami->source, NULL);
  129. av_bprint_finalize(&sami->content, NULL);
  130. av_bprint_finalize(&sami->full, NULL);
  131. return 0;
  132. }
  133. AVCodec ff_sami_decoder = {
  134. .name = "sami",
  135. .long_name = NULL_IF_CONFIG_SMALL("SAMI subtitle"),
  136. .type = AVMEDIA_TYPE_SUBTITLE,
  137. .id = AV_CODEC_ID_SAMI,
  138. .priv_data_size = sizeof(SAMIContext),
  139. .init = sami_init,
  140. .close = sami_close,
  141. .decode = sami_decode_frame,
  142. };