Browse code

lavc: add a librsvg rasterization library wrapper

Enables rendering of SVG images. This is possible since SVG images
still contain and specify the dimensions in pixels to which they've
been drawn to and thus enable browsers to display them without any
external data. Users can still override and generate images with
arbitrary resolutions.

Signed-off-by: Rostislav Pehlivanov <atomnuker@gmail.com>

Rostislav Pehlivanov authored on 2017/05/08 13:02:19
Showing 5 changed files
... ...
@@ -11,6 +11,7 @@ version <next>:
11 11
 - update cuvid/nvenc headers to Video Codec SDK 8.0.14
12 12
 - afir audio filter
13 13
 - scale_cuda CUDA based video scale filter
14
+- librsvg support for svg rasterization
14 15
 
15 16
 version 3.3:
16 17
 - CrystalHD decoder moved to new decode API
... ...
@@ -245,6 +245,7 @@ External library support:
245 245
   --enable-libopenmpt      enable decoding tracked files via libopenmpt [no]
246 246
   --enable-libopus         enable Opus de/encoding via libopus [no]
247 247
   --enable-libpulse        enable Pulseaudio input via libpulse [no]
248
+  --enable-librsvg         enable SVG rasterization via librsvg [no]
248 249
   --enable-librubberband   enable rubberband needed for rubberband filter [no]
249 250
   --enable-librtmp         enable RTMP[E] support via librtmp [no]
250 251
   --enable-libschroedinger enable Dirac de/encoding via libschroedinger [no]
... ...
@@ -1560,6 +1561,7 @@ EXTERNAL_LIBRARY_LIST="
1560 1560
     libopenmpt
1561 1561
     libopus
1562 1562
     libpulse
1563
+    librsvg
1563 1564
     librtmp
1564 1565
     libschroedinger
1565 1566
     libshine
... ...
@@ -2875,6 +2877,7 @@ libopenmpt_demuxer_deps="libopenmpt"
2875 2875
 libopus_decoder_deps="libopus"
2876 2876
 libopus_encoder_deps="libopus"
2877 2877
 libopus_encoder_select="audio_frame_queue"
2878
+librsvg_decoder_deps="librsvg"
2878 2879
 libschroedinger_decoder_deps="libschroedinger"
2879 2880
 libschroedinger_encoder_deps="libschroedinger"
2880 2881
 libshine_encoder_deps="libshine"
... ...
@@ -5837,6 +5840,7 @@ enabled libopus           && {
5837 5837
     }
5838 5838
 }
5839 5839
 enabled libpulse          && require_pkg_config libpulse pulse/pulseaudio.h pa_context_new
5840
+enabled librsvg           && require_pkg_config librsvg-2.0 librsvg-2.0/librsvg/rsvg.h rsvg_handle_render_cairo
5840 5841
 enabled librtmp           && require_pkg_config librtmp librtmp/rtmp.h RTMP_Socket
5841 5842
 enabled librubberband     && require_pkg_config "rubberband >= 1.8.1" rubberband/rubberband-c.h rubberband_new
5842 5843
 enabled libschroedinger   && require_pkg_config schroedinger-1.0 schroedinger/schro.h schro_init
... ...
@@ -551,6 +551,7 @@ OBJS-$(CONFIG_SUBVIEWER1_DECODER)      += textdec.o ass.o
551 551
 OBJS-$(CONFIG_SUBVIEWER_DECODER)       += subviewerdec.o ass.o
552 552
 OBJS-$(CONFIG_SUNRAST_DECODER)         += sunrast.o
553 553
 OBJS-$(CONFIG_SUNRAST_ENCODER)         += sunrastenc.o
554
+OBJS-$(CONFIG_LIBRSVG_DECODER)         += librsvgdec.o
554 555
 OBJS-$(CONFIG_SVQ1_DECODER)            += svq1dec.o svq1.o svq13.o h263data.o
555 556
 OBJS-$(CONFIG_SVQ1_ENCODER)            += svq1enc.o svq1.o  h263data.o  \
556 557
                                           h263.o ituh263enc.o
... ...
@@ -319,6 +319,7 @@ static void register_all(void)
319 319
     REGISTER_DECODER(SPEEDHQ,           speedhq);
320 320
     REGISTER_DECODER(SRGC,              srgc);
321 321
     REGISTER_ENCDEC (SUNRAST,           sunrast);
322
+    REGISTER_DECODER(LIBRSVG,           librsvg);
322 323
     REGISTER_ENCDEC (SVQ1,              svq1);
323 324
     REGISTER_DECODER(SVQ3,              svq3);
324 325
     REGISTER_ENCDEC (TARGA,             targa);
325 326
new file mode 100644
... ...
@@ -0,0 +1,127 @@
0
+/*
1
+ * Librsvg rasterization wrapper
2
+ * Copyright (c) 2017 Rostislav Pehlivanov <atomnuker@gmail.com>
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
+#include "avcodec.h"
22
+#include "internal.h"
23
+#include "libavutil/opt.h"
24
+#include "librsvg-2.0/librsvg/rsvg.h"
25
+
26
+typedef struct LibRSVGContext {
27
+    AVClass *class;
28
+
29
+    int width;
30
+    int height;
31
+    int keep_ar;
32
+} LibRSVGContext;
33
+
34
+static int librsvg_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *pkt)
35
+{
36
+    int ret;
37
+    LibRSVGContext *s = avctx->priv_data;
38
+    AVFrame *frame = data;
39
+
40
+    RsvgHandle *handle;
41
+    RsvgDimensionData unscaled_dimensions, dimensions;
42
+    cairo_surface_t *image;
43
+    cairo_t *crender = NULL;
44
+    GError *error = NULL;
45
+
46
+    *got_frame = 0;
47
+
48
+    handle = rsvg_handle_new_from_data(pkt->data, pkt->size, &error);
49
+    if (error) {
50
+        av_log(avctx, AV_LOG_ERROR, "Error parsing svg!\n");
51
+        g_error_free(error);
52
+        return AVERROR_INVALIDDATA;
53
+    }
54
+
55
+    rsvg_handle_get_dimensions(handle, &dimensions);
56
+    rsvg_handle_get_dimensions(handle, &unscaled_dimensions);
57
+    dimensions.width  = s->width  ? s->width  : dimensions.width;
58
+    dimensions.height = s->height ? s->height : dimensions.height;
59
+    if (s->keep_ar && (s->width || s->height)) {
60
+        double default_ar = unscaled_dimensions.width/(double)unscaled_dimensions.height;
61
+        if (!s->width)
62
+            dimensions.width  = lrintf(dimensions.height * default_ar);
63
+        else
64
+            dimensions.height = lrintf(dimensions.width  / default_ar);
65
+    }
66
+
67
+    if ((ret = ff_set_dimensions(avctx, dimensions.width, dimensions.height)))
68
+        return ret;
69
+    avctx->pix_fmt = AV_PIX_FMT_BGRA;
70
+
71
+    if ((ret = ff_get_buffer(avctx, frame, 0)))
72
+        return ret;
73
+    frame->pict_type = AV_PICTURE_TYPE_I;
74
+    frame->key_frame = 1;
75
+
76
+    image = cairo_image_surface_create_for_data(frame->data[0], CAIRO_FORMAT_ARGB32,
77
+                                                frame->width, frame->height,
78
+                                                frame->linesize[0]);
79
+    if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS)
80
+        return AVERROR_INVALIDDATA;
81
+
82
+    crender = cairo_create(image);
83
+
84
+    cairo_set_source_rgba(crender, 0.0, 0.0, 0.0, 1.0f);
85
+    cairo_paint_with_alpha(crender, 0.0f);
86
+
87
+    cairo_scale(crender, dimensions.width / (double)unscaled_dimensions.width,
88
+                dimensions.height / (double)unscaled_dimensions.height);
89
+
90
+    rsvg_handle_render_cairo(handle, crender);
91
+
92
+    cairo_destroy(crender);
93
+    cairo_surface_destroy(image);
94
+    g_object_unref(handle);
95
+
96
+    *got_frame = 1;
97
+
98
+    return 0;
99
+}
100
+
101
+#define OFFSET(x) offsetof(LibRSVGContext, x)
102
+#define DEC (AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
103
+static const AVOption options[] = {
104
+    { "width", "Width to render to (0 for default)", OFFSET(width), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, DEC },
105
+    { "height", "Height to render to (0 for default)", OFFSET(height), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, DEC },
106
+    { "keep_ar", "Keep aspect ratio with custom width/height", OFFSET(keep_ar), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, DEC },
107
+    { NULL },
108
+};
109
+
110
+static const AVClass librsvg_decoder_class = {
111
+    .class_name = "Librsvg",
112
+    .item_name  = av_default_item_name,
113
+    .option     = options,
114
+    .version    = LIBAVUTIL_VERSION_INT,
115
+};
116
+
117
+AVCodec ff_librsvg_decoder = {
118
+    .name           = "librsvg",
119
+    .long_name      = NULL_IF_CONFIG_SMALL("Librsvg rasterizer"),
120
+    .priv_class     = &librsvg_decoder_class,
121
+    .type           = AVMEDIA_TYPE_VIDEO,
122
+    .id             = AV_CODEC_ID_SVG,
123
+    .decode         = librsvg_decode_frame,
124
+    .priv_data_size = sizeof(LibRSVGContext),
125
+    .capabilities   = AV_CODEC_CAP_LOSSLESS | AV_CODEC_CAP_DR1,
126
+};