Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright (c) 2011 Baptiste Coudurier
  3.  * Copyright (c) 2011 Stefano Sabatini
  4.  * Copyright (c) 2012 Clément Bœsch
  5.  *
  6.  * This file is part of FFmpeg.
  7.  *
  8.  * FFmpeg is free software; you can redistribute it and/or
  9.  * modify it under the terms of the GNU Lesser General Public
  10.  * License as published by the Free Software Foundation; either
  11.  * version 2.1 of the License, or (at your option) any later version.
  12.  *
  13.  * FFmpeg is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16.  * Lesser General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU Lesser General Public
  19.  * License along with FFmpeg; if not, write to the Free Software
  20.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21.  */
  22.  
  23. /**
  24.  * @file
  25.  * Libass subtitles burning filter.
  26.  *
  27.  * @see{http://www.matroska.org/technical/specs/subtitles/ssa.html}
  28.  */
  29.  
  30. #include <ass/ass.h>
  31.  
  32. #include "config.h"
  33. #if CONFIG_SUBTITLES_FILTER
  34. # include "libavcodec/avcodec.h"
  35. # include "libavformat/avformat.h"
  36. #endif
  37. #include "libavutil/avstring.h"
  38. #include "libavutil/imgutils.h"
  39. #include "libavutil/opt.h"
  40. #include "libavutil/parseutils.h"
  41. #include "drawutils.h"
  42. #include "avfilter.h"
  43. #include "internal.h"
  44. #include "formats.h"
  45. #include "video.h"
  46.  
  47. typedef struct {
  48.     const AVClass *class;
  49.     ASS_Library  *library;
  50.     ASS_Renderer *renderer;
  51.     ASS_Track    *track;
  52.     char *filename;
  53.     char *fontsdir;
  54.     char *charenc;
  55.     char *force_style;
  56.     int stream_index;
  57.     uint8_t rgba_map[4];
  58.     int     pix_step[4];       ///< steps per pixel for each plane of the main output
  59.     int original_w, original_h;
  60.     int shaping;
  61.     FFDrawContext draw;
  62. } AssContext;
  63.  
  64. #define OFFSET(x) offsetof(AssContext, x)
  65. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  66.  
  67. #define COMMON_OPTIONS \
  68.     {"filename",       "set the filename of file to read",                         OFFSET(filename),   AV_OPT_TYPE_STRING,     {.str = NULL},  CHAR_MIN, CHAR_MAX, FLAGS }, \
  69.     {"f",              "set the filename of file to read",                         OFFSET(filename),   AV_OPT_TYPE_STRING,     {.str = NULL},  CHAR_MIN, CHAR_MAX, FLAGS }, \
  70.     {"original_size",  "set the size of the original video (used to scale fonts)", OFFSET(original_w), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL},  CHAR_MIN, CHAR_MAX, FLAGS }, \
  71.     {"fontsdir",       "set the directory containing the fonts to read",           OFFSET(fontsdir),   AV_OPT_TYPE_STRING,     {.str = NULL},  CHAR_MIN, CHAR_MAX, FLAGS }, \
  72.  
  73. /* libass supports a log level ranging from 0 to 7 */
  74. static const int ass_libavfilter_log_level_map[] = {
  75.     [0] = AV_LOG_FATAL,     /* MSGL_FATAL */
  76.     [1] = AV_LOG_ERROR,     /* MSGL_ERR */
  77.     [2] = AV_LOG_WARNING,   /* MSGL_WARN */
  78.     [3] = AV_LOG_WARNING,   /* <undefined> */
  79.     [4] = AV_LOG_INFO,      /* MSGL_INFO */
  80.     [5] = AV_LOG_INFO,      /* <undefined> */
  81.     [6] = AV_LOG_VERBOSE,   /* MSGL_V */
  82.     [7] = AV_LOG_DEBUG,     /* MSGL_DBG2 */
  83. };
  84.  
  85. static void ass_log(int ass_level, const char *fmt, va_list args, void *ctx)
  86. {
  87.     const int ass_level_clip = av_clip(ass_level, 0,
  88.         FF_ARRAY_ELEMS(ass_libavfilter_log_level_map) - 1);
  89.     const int level = ass_libavfilter_log_level_map[ass_level_clip];
  90.  
  91.     av_vlog(ctx, level, fmt, args);
  92.     av_log(ctx, level, "\n");
  93. }
  94.  
  95. static av_cold int init(AVFilterContext *ctx)
  96. {
  97.     AssContext *ass = ctx->priv;
  98.  
  99.     if (!ass->filename) {
  100.         av_log(ctx, AV_LOG_ERROR, "No filename provided!\n");
  101.         return AVERROR(EINVAL);
  102.     }
  103.  
  104.     ass->library = ass_library_init();
  105.     if (!ass->library) {
  106.         av_log(ctx, AV_LOG_ERROR, "Could not initialize libass.\n");
  107.         return AVERROR(EINVAL);
  108.     }
  109.     ass_set_message_cb(ass->library, ass_log, ctx);
  110.  
  111.     ass_set_fonts_dir(ass->library, ass->fontsdir);
  112.  
  113.     ass->renderer = ass_renderer_init(ass->library);
  114.     if (!ass->renderer) {
  115.         av_log(ctx, AV_LOG_ERROR, "Could not initialize libass renderer.\n");
  116.         return AVERROR(EINVAL);
  117.     }
  118.  
  119.     return 0;
  120. }
  121.  
  122. static av_cold void uninit(AVFilterContext *ctx)
  123. {
  124.     AssContext *ass = ctx->priv;
  125.  
  126.     if (ass->track)
  127.         ass_free_track(ass->track);
  128.     if (ass->renderer)
  129.         ass_renderer_done(ass->renderer);
  130.     if (ass->library)
  131.         ass_library_done(ass->library);
  132. }
  133.  
  134. static int query_formats(AVFilterContext *ctx)
  135. {
  136.     return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0));
  137. }
  138.  
  139. static int config_input(AVFilterLink *inlink)
  140. {
  141.     AssContext *ass = inlink->dst->priv;
  142.  
  143.     ff_draw_init(&ass->draw, inlink->format, 0);
  144.  
  145.     ass_set_frame_size  (ass->renderer, inlink->w, inlink->h);
  146.     if (ass->original_w && ass->original_h)
  147.         ass_set_aspect_ratio(ass->renderer, (double)inlink->w / inlink->h,
  148.                              (double)ass->original_w / ass->original_h);
  149.     if (ass->shaping != -1)
  150.         ass_set_shaper(ass->renderer, ass->shaping);
  151.  
  152.     return 0;
  153. }
  154.  
  155. /* libass stores an RGBA color in the format RRGGBBTT, where TT is the transparency level */
  156. #define AR(c)  ( (c)>>24)
  157. #define AG(c)  (((c)>>16)&0xFF)
  158. #define AB(c)  (((c)>>8) &0xFF)
  159. #define AA(c)  ((0xFF-(c)) &0xFF)
  160.  
  161. static void overlay_ass_image(AssContext *ass, AVFrame *picref,
  162.                               const ASS_Image *image)
  163. {
  164.     for (; image; image = image->next) {
  165.         uint8_t rgba_color[] = {AR(image->color), AG(image->color), AB(image->color), AA(image->color)};
  166.         FFDrawColor color;
  167.         ff_draw_color(&ass->draw, &color, rgba_color);
  168.         ff_blend_mask(&ass->draw, &color,
  169.                       picref->data, picref->linesize,
  170.                       picref->width, picref->height,
  171.                       image->bitmap, image->stride, image->w, image->h,
  172.                       3, 0, image->dst_x, image->dst_y);
  173.     }
  174. }
  175.  
  176. static int filter_frame(AVFilterLink *inlink, AVFrame *picref)
  177. {
  178.     AVFilterContext *ctx = inlink->dst;
  179.     AVFilterLink *outlink = ctx->outputs[0];
  180.     AssContext *ass = ctx->priv;
  181.     int detect_change = 0;
  182.     double time_ms = picref->pts * av_q2d(inlink->time_base) * 1000;
  183.     ASS_Image *image = ass_render_frame(ass->renderer, ass->track,
  184.                                         time_ms, &detect_change);
  185.  
  186.     if (detect_change)
  187.         av_log(ctx, AV_LOG_DEBUG, "Change happened at time ms:%f\n", time_ms);
  188.  
  189.     overlay_ass_image(ass, picref, image);
  190.  
  191.     return ff_filter_frame(outlink, picref);
  192. }
  193.  
  194. static const AVFilterPad ass_inputs[] = {
  195.     {
  196.         .name             = "default",
  197.         .type             = AVMEDIA_TYPE_VIDEO,
  198.         .filter_frame     = filter_frame,
  199.         .config_props     = config_input,
  200.         .needs_writable   = 1,
  201.     },
  202.     { NULL }
  203. };
  204.  
  205. static const AVFilterPad ass_outputs[] = {
  206.     {
  207.         .name = "default",
  208.         .type = AVMEDIA_TYPE_VIDEO,
  209.     },
  210.     { NULL }
  211. };
  212.  
  213. #if CONFIG_ASS_FILTER
  214.  
  215. static const AVOption ass_options[] = {
  216.     COMMON_OPTIONS
  217.     {"shaping", "set shaping engine", OFFSET(shaping), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, FLAGS, "shaping_mode"},
  218.         {"auto", NULL,                 0, AV_OPT_TYPE_CONST, {.i64 = -1},                  INT_MIN, INT_MAX, FLAGS, "shaping_mode"},
  219.         {"simple",  "simple shaping",  0, AV_OPT_TYPE_CONST, {.i64 = ASS_SHAPING_SIMPLE},  INT_MIN, INT_MAX, FLAGS, "shaping_mode"},
  220.         {"complex", "complex shaping", 0, AV_OPT_TYPE_CONST, {.i64 = ASS_SHAPING_COMPLEX}, INT_MIN, INT_MAX, FLAGS, "shaping_mode"},
  221.     {NULL},
  222. };
  223.  
  224. AVFILTER_DEFINE_CLASS(ass);
  225.  
  226. static av_cold int init_ass(AVFilterContext *ctx)
  227. {
  228.     AssContext *ass = ctx->priv;
  229.     int ret = init(ctx);
  230.  
  231.     if (ret < 0)
  232.         return ret;
  233.  
  234.     /* Initialize fonts */
  235.     ass_set_fonts(ass->renderer, NULL, NULL, 1, NULL, 1);
  236.  
  237.     ass->track = ass_read_file(ass->library, ass->filename, NULL);
  238.     if (!ass->track) {
  239.         av_log(ctx, AV_LOG_ERROR,
  240.                "Could not create a libass track when reading file '%s'\n",
  241.                ass->filename);
  242.         return AVERROR(EINVAL);
  243.     }
  244.     return 0;
  245. }
  246.  
  247. AVFilter ff_vf_ass = {
  248.     .name          = "ass",
  249.     .description   = NULL_IF_CONFIG_SMALL("Render ASS subtitles onto input video using the libass library."),
  250.     .priv_size     = sizeof(AssContext),
  251.     .init          = init_ass,
  252.     .uninit        = uninit,
  253.     .query_formats = query_formats,
  254.     .inputs        = ass_inputs,
  255.     .outputs       = ass_outputs,
  256.     .priv_class    = &ass_class,
  257. };
  258. #endif
  259.  
  260. #if CONFIG_SUBTITLES_FILTER
  261.  
  262. static const AVOption subtitles_options[] = {
  263.     COMMON_OPTIONS
  264.     {"charenc",      "set input character encoding", OFFSET(charenc),      AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
  265.     {"stream_index", "set stream index",             OFFSET(stream_index), AV_OPT_TYPE_INT,    { .i64 = -1 }, -1,       INT_MAX,  FLAGS},
  266.     {"si",           "set stream index",             OFFSET(stream_index), AV_OPT_TYPE_INT,    { .i64 = -1 }, -1,       INT_MAX,  FLAGS},
  267.     {"force_style",  "force subtitle style",         OFFSET(force_style),  AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS},
  268.     {NULL},
  269. };
  270.  
  271. static const char * const font_mimetypes[] = {
  272.     "application/x-truetype-font",
  273.     "application/vnd.ms-opentype",
  274.     "application/x-font-ttf",
  275.     NULL
  276. };
  277.  
  278. static int attachment_is_font(AVStream * st)
  279. {
  280.     const AVDictionaryEntry *tag = NULL;
  281.     int n;
  282.  
  283.     tag = av_dict_get(st->metadata, "mimetype", NULL, AV_DICT_MATCH_CASE);
  284.  
  285.     if (tag) {
  286.         for (n = 0; font_mimetypes[n]; n++) {
  287.             if (av_strcasecmp(font_mimetypes[n], tag->value) == 0)
  288.                 return 1;
  289.         }
  290.     }
  291.     return 0;
  292. }
  293.  
  294. AVFILTER_DEFINE_CLASS(subtitles);
  295.  
  296. static av_cold int init_subtitles(AVFilterContext *ctx)
  297. {
  298.     int j, ret, sid;
  299.     int k = 0;
  300.     AVDictionary *codec_opts = NULL;
  301.     AVFormatContext *fmt = NULL;
  302.     AVCodecContext *dec_ctx = NULL;
  303.     AVCodec *dec = NULL;
  304.     const AVCodecDescriptor *dec_desc;
  305.     AVStream *st;
  306.     AVPacket pkt;
  307.     AssContext *ass = ctx->priv;
  308.  
  309.     /* Init libass */
  310.     ret = init(ctx);
  311.     if (ret < 0)
  312.         return ret;
  313.     ass->track = ass_new_track(ass->library);
  314.     if (!ass->track) {
  315.         av_log(ctx, AV_LOG_ERROR, "Could not create a libass track\n");
  316.         return AVERROR(EINVAL);
  317.     }
  318.  
  319.     /* Open subtitles file */
  320.     ret = avformat_open_input(&fmt, ass->filename, NULL, NULL);
  321.     if (ret < 0) {
  322.         av_log(ctx, AV_LOG_ERROR, "Unable to open %s\n", ass->filename);
  323.         goto end;
  324.     }
  325.     ret = avformat_find_stream_info(fmt, NULL);
  326.     if (ret < 0)
  327.         goto end;
  328.  
  329.     /* Locate subtitles stream */
  330.     if (ass->stream_index < 0)
  331.         ret = av_find_best_stream(fmt, AVMEDIA_TYPE_SUBTITLE, -1, -1, NULL, 0);
  332.     else {
  333.         ret = -1;
  334.         if (ass->stream_index < fmt->nb_streams) {
  335.             for (j = 0; j < fmt->nb_streams; j++) {
  336.                 if (fmt->streams[j]->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
  337.                     if (ass->stream_index == k) {
  338.                         ret = j;
  339.                         break;
  340.                     }
  341.                     k++;
  342.                 }
  343.             }
  344.         }
  345.     }
  346.  
  347.     if (ret < 0) {
  348.         av_log(ctx, AV_LOG_ERROR, "Unable to locate subtitle stream in %s\n",
  349.                ass->filename);
  350.         goto end;
  351.     }
  352.     sid = ret;
  353.     st = fmt->streams[sid];
  354.  
  355.     /* Load attached fonts */
  356.     for (j = 0; j < fmt->nb_streams; j++) {
  357.         AVStream *st = fmt->streams[j];
  358.         if (st->codec->codec_type == AVMEDIA_TYPE_ATTACHMENT &&
  359.             attachment_is_font(st)) {
  360.             const AVDictionaryEntry *tag = NULL;
  361.             tag = av_dict_get(st->metadata, "filename", NULL,
  362.                               AV_DICT_MATCH_CASE);
  363.  
  364.             if (tag) {
  365.                 av_log(ctx, AV_LOG_DEBUG, "Loading attached font: %s\n",
  366.                        tag->value);
  367.                 ass_add_font(ass->library, tag->value,
  368.                              st->codec->extradata,
  369.                              st->codec->extradata_size);
  370.             } else {
  371.                 av_log(ctx, AV_LOG_WARNING,
  372.                        "Font attachment has no filename, ignored.\n");
  373.             }
  374.         }
  375.     }
  376.  
  377.     /* Initialize fonts */
  378.     ass_set_fonts(ass->renderer, NULL, NULL, 1, NULL, 1);
  379.  
  380.     /* Open decoder */
  381.     dec_ctx = st->codec;
  382.     dec = avcodec_find_decoder(dec_ctx->codec_id);
  383.     if (!dec) {
  384.         av_log(ctx, AV_LOG_ERROR, "Failed to find subtitle codec %s\n",
  385.                avcodec_get_name(dec_ctx->codec_id));
  386.         return AVERROR(EINVAL);
  387.     }
  388.     dec_desc = avcodec_descriptor_get(dec_ctx->codec_id);
  389.     if (dec_desc && !(dec_desc->props & AV_CODEC_PROP_TEXT_SUB)) {
  390.         av_log(ctx, AV_LOG_ERROR,
  391.                "Only text based subtitles are currently supported\n");
  392.         return AVERROR_PATCHWELCOME;
  393.     }
  394.     if (ass->charenc)
  395.         av_dict_set(&codec_opts, "sub_charenc", ass->charenc, 0);
  396.     ret = avcodec_open2(dec_ctx, dec, &codec_opts);
  397.     if (ret < 0)
  398.         goto end;
  399.  
  400.     if (ass->force_style) {
  401.         char **list = NULL;
  402.         char *temp = NULL;
  403.         char *ptr = av_strtok(ass->force_style, ",", &temp);
  404.         int i = 0;
  405.         while (ptr) {
  406.             av_dynarray_add(&list, &i, ptr);
  407.             if (!list) {
  408.                 ret = AVERROR(ENOMEM);
  409.                 goto end;
  410.             }
  411.             ptr = av_strtok(NULL, ",", &temp);
  412.         }
  413.         av_dynarray_add(&list, &i, NULL);
  414.         if (!list) {
  415.             ret = AVERROR(ENOMEM);
  416.             goto end;
  417.         }
  418.         ass_set_style_overrides(ass->library, list);
  419.         av_free(list);
  420.     }
  421.     /* Decode subtitles and push them into the renderer (libass) */
  422.     if (dec_ctx->subtitle_header)
  423.         ass_process_codec_private(ass->track,
  424.                                   dec_ctx->subtitle_header,
  425.                                   dec_ctx->subtitle_header_size);
  426.     av_init_packet(&pkt);
  427.     pkt.data = NULL;
  428.     pkt.size = 0;
  429.     while (av_read_frame(fmt, &pkt) >= 0) {
  430.         int i, got_subtitle;
  431.         AVSubtitle sub = {0};
  432.  
  433.         if (pkt.stream_index == sid) {
  434.             ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_subtitle, &pkt);
  435.             if (ret < 0) {
  436.                 av_log(ctx, AV_LOG_WARNING, "Error decoding: %s (ignored)\n",
  437.                        av_err2str(ret));
  438.             } else if (got_subtitle) {
  439.                 for (i = 0; i < sub.num_rects; i++) {
  440.                     char *ass_line = sub.rects[i]->ass;
  441.                     if (!ass_line)
  442.                         break;
  443.                     ass_process_data(ass->track, ass_line, strlen(ass_line));
  444.                 }
  445.             }
  446.         }
  447.         av_free_packet(&pkt);
  448.         avsubtitle_free(&sub);
  449.     }
  450.  
  451. end:
  452.     av_dict_free(&codec_opts);
  453.     if (dec_ctx)
  454.         avcodec_close(dec_ctx);
  455.     if (fmt)
  456.         avformat_close_input(&fmt);
  457.     return ret;
  458. }
  459.  
  460. AVFilter ff_vf_subtitles = {
  461.     .name          = "subtitles",
  462.     .description   = NULL_IF_CONFIG_SMALL("Render text subtitles onto input video using the libass library."),
  463.     .priv_size     = sizeof(AssContext),
  464.     .init          = init_subtitles,
  465.     .uninit        = uninit,
  466.     .query_formats = query_formats,
  467.     .inputs        = ass_inputs,
  468.     .outputs       = ass_outputs,
  469.     .priv_class    = &subtitles_class,
  470. };
  471. #endif
  472.