On Tue, 24 Feb 2009 13:54:08 +0300, Alexey Lebedeff wrote:

 AL> If there is any interest in it, I'll do some cleanup to code,
 AL> properly integrate it into build process, and share the results.

Here is the very primitive patch to libavfilter Makefile, and source
of filter itself. No proper build process integration yet.

--- Makefile.orig	2009-02-23 10:41:03.000000000 +0000
+++ Makefile	2009-02-24 16:57:36.000000000 +0000
@@ -13,6 +13,7 @@
        graphparser.o \
 
 
+OBJS-$(CONFIG_ASS_FILTER)       += vf_ass.o
 OBJS-$(CONFIG_CROP_FILTER)       += vf_crop.o
 OBJS-$(CONFIG_DRAWBOX_FILTER)    += vf_drawbox.o
 OBJS-$(CONFIG_FPS_FILTER)        += vf_fps.o
@@ -34,4 +35,8 @@
 
 HEADERS = avfilter.h
 
+CFLAGS += -I/usr/include/ass
+EXTRALIBS += -lass
+
 include $(SUBDIR)../subdir.mak
+
--- allfilters.c.orig	2009-02-23 10:41:03.000000000 +0000
+++ allfilters.c	2009-02-23 11:26:18.000000000 +0000
@@ -34,6 +34,7 @@
         return;
     initialized = 1;
 
+    REGISTER_FILTER(ASS,ass,vf);
     REGISTER_FILTER(CROP,crop,vf);
     REGISTER_FILTER(DRAWBOX,drawbox,vf);
     REGISTER_FILTER(FIFO,fifo,vf);
/*
 * SSA/ASS subtitles rendering filter, using libssa.
 * Based on vf_drawbox.c from libavfilter and vf_ass.c from mplayer.
 *
 * Copyright (c) 2006 Evgeniy Stepanov <eugeni.stepa...@gmail.com>
 * Copyright (c) 2008 Affine Systems, Inc (Michael Sullivan, Bobby Impollonia)
 * Copyright (c) 2009 Alexey Lebedeff <bina...@binarin.ru>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */

/*
 * Usage: '-vfilters ass=filename:somefile.ass|margin:50|encoding:utf-8'
 * Only 'filename' param is mandatory.
 */

#include <string.h>
#include <stdio.h>
#include <ctype.h>

#include <ass.h>

#include "avfilter.h"

typedef struct
{
  ass_library_t *ass_library;
  ass_renderer_t *ass_renderer;
  ass_track_t *ass_track;

  int margin;
  char *filename;
  char *encoding;

  int frame_width, frame_height;
  int vsub,hsub;   //< chroma subsampling

} AssContext;

static int parse_args(AVFilterContext *ctx, AssContext *context, const char* args);
static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
{
  AssContext *context= ctx->priv;

  /* defaults */
  context->margin = 10;
  context->encoding = "utf-8";

  if ( parse_args(ctx, context, args) )
    return 1;

  return 0;
}

static int query_formats(AVFilterContext *ctx)
{
  avfilter_set_common_formats
    (ctx,
     avfilter_make_format_list(10,
			       PIX_FMT_YUV444P,  PIX_FMT_YUV422P,  PIX_FMT_YUV420P,
			       PIX_FMT_YUV411P,  PIX_FMT_YUV410P,
			       PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ420P,
			       PIX_FMT_YUV440P,  PIX_FMT_YUVJ440P));
  return 0;
}

static int config_input(AVFilterLink *link)
{
  AssContext *context = link->dst->priv;

  context->frame_width = link->w;
  context->frame_height = link->h;

  context->ass_library = ass_library_init();

  if ( !context->ass_library ) {
    av_log(0, AV_LOG_ERROR, "ass_library_init() failed!\n");
    return 1;
  }

  ass_set_fonts_dir(context->ass_library, "");
  ass_set_extract_fonts(context->ass_library, 1);
  ass_set_style_overrides(context->ass_library, NULL);

  context->ass_renderer = ass_renderer_init(context->ass_library);
  if ( ! context->ass_renderer ) {
    av_log(0, AV_LOG_ERROR, "ass_renderer_init() failed!\n");
    return 1;
  }

  ass_set_frame_size(context->ass_renderer, link->w, link->h);
  ass_set_margins(context->ass_renderer, context->margin, context->margin, context->margin, context->margin);
  ass_set_use_margins(context->ass_renderer, 1);
  ass_set_font_scale(context->ass_renderer, 1.);
  ass_set_fonts(context->ass_renderer, NULL, "Sans");

  context->ass_track = ass_read_file(context->ass_library, context->filename, context->encoding);
  if ( !context->ass_track ) {
    av_log(0, AV_LOG_ERROR, "Failed to read subtitle file with ass_read_file()!\n");
    return 1;
  }

  avcodec_get_chroma_sub_sample(link->format,
				&context->hsub, &context->vsub);

  return 0;
}

static void start_frame(AVFilterLink *link, AVFilterPicRef *picref)
{
  avfilter_start_frame(link->dst->outputs[0], picref);
}

#define _r(c)  ((c)>>24)
#define _g(c)  (((c)>>16)&0xFF)
#define _b(c)  (((c)>>8)&0xFF)
#define _a(c)  ((c)&0xFF)
#define rgba2y(c)  ( (( 263*_r(c)  + 516*_g(c) + 100*_b(c)) >> 10) + 16  )                                                                     
#define rgba2u(c)  ( ((-152*_r(c) - 298*_g(c) + 450*_b(c)) >> 10) + 128 )                                                                      
#define rgba2v(c)  ( (( 450*_r(c) - 376*_g(c) -  73*_b(c)) >> 10) + 128 )                                                                      

static void draw_ass_image(AVFilterPicRef *pic, ass_image_t *img, AssContext *context)
{
  unsigned char *row[4];
  unsigned char c_y = rgba2y(img->color);
  unsigned char c_u = rgba2u(img->color);
  unsigned char c_v = rgba2v(img->color);
  unsigned char opacity = 255 - _a(img->color);
  unsigned char *src;
  int i, j;

  unsigned char *bitmap = img->bitmap;
  int bitmap_w = img->w;
  int bitmap_h = img->h;
  int dst_x = img->dst_x;
  int dst_y = img->dst_y;

  int channel;
  int x,y;

  src = bitmap;

  for (i = 0; i < bitmap_h; ++i) {
    y = dst_y + i;
    if ( y >= pic->h )
      break;

    row[0] = pic->data[0] + y * pic->linesize[0];

    for (channel = 1; channel < 3; channel++)
      row[channel] = pic->data[channel] +
	pic->linesize[channel] * (y>> context->vsub);

    for (j = 0; j < bitmap_w; ++j) {
      unsigned k = ((unsigned)src[j]) * opacity / 255;

      x = dst_x + j;
      if ( y >= pic->w )
	break;

      row[0][x] = (k*c_y + (255-k)*row[0][x]) / 255;
      row[1][x >> context->hsub] = (k*c_u + (255-k)*row[1][x >> context->hsub]) / 255;
      row[2][x >> context->hsub] = (k*c_v + (255-k)*row[2][x >> context->hsub]) / 255;
    }

    src += img->stride;
  } 
}

static void end_frame(AVFilterLink *link)
{
  AssContext *context = link->dst->priv;
  AVFilterLink* output = link->dst->outputs[0];
  AVFilterPicRef *pic = link->cur_pic;

  ass_image_t* img = ass_render_frame(context->ass_renderer,
				      context->ass_track,
				      pic->pts * 1000 / AV_TIME_BASE,
				      NULL);

  while ( img ) {
    draw_ass_image(pic, img, context);
    img = img->next;
  }

  avfilter_draw_slice(output, 0, pic->h);
  avfilter_end_frame(output);
}

static int parse_args(AVFilterContext *ctx, AssContext *context, const char* args)
{
  char *arg_copy = av_strdup(args);
  char *strtok_arg = arg_copy;
  char *param;

  while ( param = strtok(strtok_arg, "|") ) {
    char *tmp = param;
    char *param_name;
    char *param_value;

    strtok_arg = NULL;

    while ( *tmp && *tmp != ':' ) {
      tmp++;
    }

    if ( param == tmp || ! *tmp ) {
      av_log(ctx, AV_LOG_ERROR, "Error while parsing arguments - must be like 'param1:value1|param2:value2'\n");
      return 1;
    }

    param_name = av_malloc(tmp - param + 1);
    memset(param_name, 0, tmp - param + 1);
    strncpy(param_name, param, tmp-param);

    tmp++;

    if ( ! *tmp ) {
      av_log(ctx, AV_LOG_ERROR, "Error while parsing arguments - parameter value cannot be empty\n");
      return 1;
    }

    param_value = av_strdup(tmp);

    if ( !strcmp("margin", param_name ) ) {
      context->margin = atoi(param_value);
    } else if ( !strcmp("filename", param_name ) ) {
      context->filename = av_strdup(param_value);
    } else if ( !strcmp("encoding", param_name ) ) {
      context->encoding = av_strdup(param_value);
    } else {
      av_log(ctx, AV_LOG_ERROR, "Error while parsing arguments - unsupported parameter '%s'\n", param_name);
      return 1;
    }
    av_free(param_name);
    av_free(param_value);
  }

  if ( ! context->filename ) {
    av_log(ctx, AV_LOG_ERROR, "Error while parsing arguments - mandatory parameter 'filename' missing\n");
    return 1;
  }
  return 0;
}

AVFilter avfilter_vf_ass=
  {
    .name      = "ass",
    .priv_size = sizeof(AssContext),
    .init      = init,

    .query_formats   = query_formats,
    .inputs    = (AVFilterPad[]) {{ .name            = "default",
                                    .type            = CODEC_TYPE_VIDEO,
                                    .start_frame     = start_frame,
                                    .end_frame       = end_frame,
                                    .config_props    = config_input,
                                    .min_perms       = AV_PERM_WRITE |
				    AV_PERM_READ,
                                    .rej_perms       = AV_PERM_REUSE |
				    AV_PERM_REUSE2},
                                  { .name = NULL}},
    .outputs   = (AVFilterPad[]) {{ .name            = "default",
                                    .type            = CODEC_TYPE_VIDEO, },
                                  { .name = NULL}},
  };

Attachment: pgp4ObvTomaD3.pgp
Description: PGP signature

_______________________________________________
FFmpeg-soc mailing list
FFmpeg-soc@mplayerhq.hu
https://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-soc

Reply via email to