00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00030 #include <ass/ass.h>
00031
00032 #include "config.h"
00033 #if CONFIG_SUBTITLES_FILTER
00034 # include "libavcodec/avcodec.h"
00035 # include "libavformat/avformat.h"
00036 #endif
00037 #include "libavutil/avstring.h"
00038 #include "libavutil/imgutils.h"
00039 #include "libavutil/opt.h"
00040 #include "libavutil/parseutils.h"
00041 #include "drawutils.h"
00042 #include "avfilter.h"
00043 #include "internal.h"
00044 #include "formats.h"
00045 #include "video.h"
00046
00047 typedef struct {
00048 const AVClass *class;
00049 ASS_Library *library;
00050 ASS_Renderer *renderer;
00051 ASS_Track *track;
00052 char *filename;
00053 uint8_t rgba_map[4];
00054 int pix_step[4];
00055 int original_w, original_h;
00056 FFDrawContext draw;
00057 } AssContext;
00058
00059 #define OFFSET(x) offsetof(AssContext, x)
00060 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
00061
00062 static const AVOption options[] = {
00063 {"filename", "set the filename of file to read", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
00064 {"f", "set the filename of file to read", OFFSET(filename), AV_OPT_TYPE_STRING, {.str = NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
00065 {"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 },
00066 {NULL},
00067 };
00068
00069
00070 static const int ass_libavfilter_log_level_map[] = {
00071 AV_LOG_QUIET,
00072 AV_LOG_PANIC,
00073 AV_LOG_FATAL,
00074 AV_LOG_ERROR,
00075 AV_LOG_WARNING,
00076 AV_LOG_INFO,
00077 AV_LOG_VERBOSE,
00078 AV_LOG_DEBUG,
00079 };
00080
00081 static void ass_log(int ass_level, const char *fmt, va_list args, void *ctx)
00082 {
00083 int level = ass_libavfilter_log_level_map[ass_level];
00084
00085 av_vlog(ctx, level, fmt, args);
00086 av_log(ctx, level, "\n");
00087 }
00088
00089 static av_cold int init(AVFilterContext *ctx, const char *args, const AVClass *class)
00090 {
00091 AssContext *ass = ctx->priv;
00092 static const char *shorthand[] = { "filename", NULL };
00093 int ret;
00094
00095 ass->class = class;
00096 av_opt_set_defaults(ass);
00097
00098 if ((ret = av_opt_set_from_string(ass, args, shorthand, "=", ":")) < 0)
00099 return ret;
00100
00101 if (!ass->filename) {
00102 av_log(ctx, AV_LOG_ERROR, "No filename provided!\n");
00103 return AVERROR(EINVAL);
00104 }
00105
00106 ass->library = ass_library_init();
00107 if (!ass->library) {
00108 av_log(ctx, AV_LOG_ERROR, "Could not initialize libass.\n");
00109 return AVERROR(EINVAL);
00110 }
00111 ass_set_message_cb(ass->library, ass_log, ctx);
00112
00113 ass->renderer = ass_renderer_init(ass->library);
00114 if (!ass->renderer) {
00115 av_log(ctx, AV_LOG_ERROR, "Could not initialize libass renderer.\n");
00116 return AVERROR(EINVAL);
00117 }
00118
00119 ass_set_fonts(ass->renderer, NULL, NULL, 1, NULL, 1);
00120 return 0;
00121 }
00122
00123 static av_cold void uninit(AVFilterContext *ctx)
00124 {
00125 AssContext *ass = ctx->priv;
00126
00127 av_opt_free(ass);
00128 if (ass->track)
00129 ass_free_track(ass->track);
00130 if (ass->renderer)
00131 ass_renderer_done(ass->renderer);
00132 if (ass->library)
00133 ass_library_done(ass->library);
00134 }
00135
00136 static int query_formats(AVFilterContext *ctx)
00137 {
00138 ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0));
00139 return 0;
00140 }
00141
00142 static int config_input(AVFilterLink *inlink)
00143 {
00144 AssContext *ass = inlink->dst->priv;
00145
00146 ff_draw_init(&ass->draw, inlink->format, 0);
00147
00148 ass_set_frame_size (ass->renderer, inlink->w, inlink->h);
00149 if (ass->original_w && ass->original_h)
00150 ass_set_aspect_ratio(ass->renderer, (double)inlink->w / inlink->h,
00151 (double)ass->original_w / ass->original_h);
00152
00153 return 0;
00154 }
00155
00156
00157 #define AR(c) ( (c)>>24)
00158 #define AG(c) (((c)>>16)&0xFF)
00159 #define AB(c) (((c)>>8) &0xFF)
00160 #define AA(c) ((0xFF-c) &0xFF)
00161
00162 static void overlay_ass_image(AssContext *ass, AVFilterBufferRef *picref,
00163 const ASS_Image *image)
00164 {
00165 for (; image; image = image->next) {
00166 uint8_t rgba_color[] = {AR(image->color), AG(image->color), AB(image->color), AA(image->color)};
00167 FFDrawColor color;
00168 ff_draw_color(&ass->draw, &color, rgba_color);
00169 ff_blend_mask(&ass->draw, &color,
00170 picref->data, picref->linesize,
00171 picref->video->w, picref->video->h,
00172 image->bitmap, image->stride, image->w, image->h,
00173 3, 0, image->dst_x, image->dst_y);
00174 }
00175 }
00176
00177 static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *picref)
00178 {
00179 AVFilterContext *ctx = inlink->dst;
00180 AVFilterLink *outlink = ctx->outputs[0];
00181 AssContext *ass = ctx->priv;
00182 int detect_change = 0;
00183 double time_ms = picref->pts * av_q2d(inlink->time_base) * 1000;
00184 ASS_Image *image = ass_render_frame(ass->renderer, ass->track,
00185 time_ms, &detect_change);
00186
00187 if (detect_change)
00188 av_log(ctx, AV_LOG_DEBUG, "Change happened at time ms:%f\n", time_ms);
00189
00190 overlay_ass_image(ass, picref, image);
00191
00192 return ff_filter_frame(outlink, picref);
00193 }
00194
00195 static const AVFilterPad ass_inputs[] = {
00196 {
00197 .name = "default",
00198 .type = AVMEDIA_TYPE_VIDEO,
00199 .filter_frame = filter_frame,
00200 .config_props = config_input,
00201 .min_perms = AV_PERM_READ | AV_PERM_WRITE,
00202 },
00203 { NULL }
00204 };
00205
00206 static const AVFilterPad ass_outputs[] = {
00207 {
00208 .name = "default",
00209 .type = AVMEDIA_TYPE_VIDEO,
00210 },
00211 { NULL }
00212 };
00213
00214 #if CONFIG_ASS_FILTER
00215
00216 #define ass_options options
00217 AVFILTER_DEFINE_CLASS(ass);
00218
00219 static av_cold int init_ass(AVFilterContext *ctx, const char *args)
00220 {
00221 AssContext *ass = ctx->priv;
00222 int ret = init(ctx, args, &ass_class);
00223
00224 if (ret < 0)
00225 return ret;
00226
00227 ass->track = ass_read_file(ass->library, ass->filename, NULL);
00228 if (!ass->track) {
00229 av_log(ctx, AV_LOG_ERROR,
00230 "Could not create a libass track when reading file '%s'\n",
00231 ass->filename);
00232 return AVERROR(EINVAL);
00233 }
00234 return 0;
00235 }
00236
00237 AVFilter avfilter_vf_ass = {
00238 .name = "ass",
00239 .description = NULL_IF_CONFIG_SMALL("Render subtitles onto input video using the libass library."),
00240 .priv_size = sizeof(AssContext),
00241 .init = init_ass,
00242 .uninit = uninit,
00243 .query_formats = query_formats,
00244 .inputs = ass_inputs,
00245 .outputs = ass_outputs,
00246 .priv_class = &ass_class,
00247 };
00248 #endif
00249
00250 #if CONFIG_SUBTITLES_FILTER
00251
00252 #define subtitles_options options
00253 AVFILTER_DEFINE_CLASS(subtitles);
00254
00255 static av_cold int init_subtitles(AVFilterContext *ctx, const char *args)
00256 {
00257 int ret, sid;
00258 AVFormatContext *fmt = NULL;
00259 AVCodecContext *dec_ctx = NULL;
00260 AVCodec *dec = NULL;
00261 AVStream *st;
00262 AVPacket pkt;
00263 AssContext *ass = ctx->priv;
00264
00265
00266 ret = init(ctx, args, &subtitles_class);
00267 if (ret < 0)
00268 return ret;
00269 ass->track = ass_new_track(ass->library);
00270 if (!ass->track) {
00271 av_log(ctx, AV_LOG_ERROR, "Could not create a libass track\n");
00272 return AVERROR(EINVAL);
00273 }
00274
00275
00276 ret = avformat_open_input(&fmt, ass->filename, NULL, NULL);
00277 if (ret < 0) {
00278 av_log(ctx, AV_LOG_ERROR, "Unable to open %s\n", ass->filename);
00279 goto end;
00280 }
00281 ret = avformat_find_stream_info(fmt, NULL);
00282 if (ret < 0)
00283 goto end;
00284
00285
00286 ret = av_find_best_stream(fmt, AVMEDIA_TYPE_SUBTITLE, -1, -1, NULL, 0);
00287 if (ret < 0) {
00288 av_log(ctx, AV_LOG_ERROR, "Unable to locate subtitle stream in %s\n",
00289 ass->filename);
00290 goto end;
00291 }
00292 sid = ret;
00293 st = fmt->streams[sid];
00294
00295
00296 dec_ctx = st->codec;
00297 dec = avcodec_find_decoder(dec_ctx->codec_id);
00298 if (!dec) {
00299 av_log(ctx, AV_LOG_ERROR, "Failed to find subtitle codec %s\n",
00300 avcodec_get_name(dec_ctx->codec_id));
00301 return AVERROR(EINVAL);
00302 }
00303 ret = avcodec_open2(dec_ctx, dec, NULL);
00304 if (ret < 0)
00305 goto end;
00306
00307
00308 if (dec_ctx->subtitle_header)
00309 ass_process_codec_private(ass->track,
00310 dec_ctx->subtitle_header,
00311 dec_ctx->subtitle_header_size);
00312 av_init_packet(&pkt);
00313 pkt.data = NULL;
00314 pkt.size = 0;
00315 while (av_read_frame(fmt, &pkt) >= 0) {
00316 int i, got_subtitle;
00317 AVSubtitle sub;
00318
00319 if (pkt.stream_index == sid) {
00320 ret = avcodec_decode_subtitle2(dec_ctx, &sub, &got_subtitle, &pkt);
00321 if (ret < 0 || !got_subtitle)
00322 break;
00323 for (i = 0; i < sub.num_rects; i++) {
00324 char *ass_line = sub.rects[i]->ass;
00325 if (!ass_line)
00326 break;
00327 ass_process_data(ass->track, ass_line, strlen(ass_line));
00328 }
00329 }
00330 av_free_packet(&pkt);
00331 avsubtitle_free(&sub);
00332 }
00333
00334 end:
00335 if (dec_ctx)
00336 avcodec_close(dec_ctx);
00337 if (fmt)
00338 avformat_close_input(&fmt);
00339 return ret;
00340 }
00341
00342 AVFilter avfilter_vf_subtitles = {
00343 .name = "subtitles",
00344 .description = NULL_IF_CONFIG_SMALL("Render subtitles onto input video using the libass library."),
00345 .priv_size = sizeof(AssContext),
00346 .init = init_subtitles,
00347 .uninit = uninit,
00348 .query_formats = query_formats,
00349 .inputs = ass_inputs,
00350 .outputs = ass_outputs,
00351 .priv_class = &subtitles_class,
00352 };
00353 #endif