[FFmpeg-soc] libavfilter - filter for .ass subtitle rendering using libass

Vitor Sessak vitor1001 at gmail.com
Thu Feb 26 18:03:49 CET 2009


Stefano Sabatini wrote:
> On date Tuesday 2009-02-24 20:28:53 +0300, Alexey Lebedeff encoded:
>> 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.
>>
> 
[...]

>>
>> #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 )                                                                      
> 
> Please avoid _foo var names, they are unreadable.
> 
> I wonder if we can do such transformation using some function, if
> that's not the case maybe we should implement it in the lib, otherwise
> filter writers will write this again and again (see for example VHOOK).

I agree. It will be nice to have a simple one-pixel colorspace transform 
in libavutil to simplify filters that accept a color as a user parameter.

>> 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;
>>   } 
>> }
> 
> It's always better to use the slice API, would be possible to
> implement this overlay algorithm per-slice (I bet yes)?
> 
>> 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;
>>     }
> 
> I don't think this is a good syntax, ':' is already used with a
> special meaning (to separate params) in most filters, if you want to
> support a list of key/val values (which looks like a good idea, at
> least better than a list of unnamed params) then I think it's better
> to use the syntax:
> param1=val1:param2=val2...:paramN=valN
> 
>>     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}},
>>   };
> 
> As already noted, the patch as it is currently designed maybe won't be
> accepted in SVN anyway, I think the "right" solution for the FFmpeg
> standards would be to use/implement/extend somehow the native ASS
> libavcodec decoder, then maybe we should also extend the lavfi API to
> also support subtitle frames.

Yes, in libavcodec a decoder can output either audio, video or 
subtitles. Ideally, libavfilter should handle all the three types of data...

> But maybe we could provide some place (multimedia wiki?) where to post
> "interesting/useful" filters which for some reason or another cannot
> be committed right into SVN.

I like this idea. Maybe adding a "filters" section to 
http://wiki.multimedia.cx/index.php?title=Interesting_Patches .

> Then we may design a system (a-la VHOOK, but implemented better) to
> dynamically load user-provided filters, so no need to manually
> integrate them each time in the native build system.

Hmmm, we do not accept to load user-provided codecs, I doubt if such 
system would be approved into SVN...

-Vitor



More information about the FFmpeg-soc mailing list