~ubuntu-branches/ubuntu/quantal/gst-plugins-bad-multiverse0.10/quantal

« back to all changes in this revision

Viewing changes to ext/assrender/gstassrender.c

  • Committer: Bazaar Package Importer
  • Author(s): Onkar Shinde
  • Date: 2010-03-13 22:48:10 UTC
  • mfrom: (1.1.16 upstream)
  • Revision ID: james.westby@ubuntu.com-20100313224810-8l25xl017imj7z4l
Tags: 0.10.18-0ubuntu1
* New upstream bugfix release.
* Relevant upstream fixes
  - 598350 : qtmux with AAC streams (from faac) generate invalid files
  - 607105 : faac doesn't negotiate channel positions correctly
  - 606726 : FAAC bitrate setting has no effect

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
2
 * Copyright (c) 2008 Benjamin Schmitz <vortex@wolpzone.de>
 
3
 * Copyright (c) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
3
4
 *
4
5
 * This library is free software; you can redistribute it and/or
5
6
 * modify it under the terms of the GNU Library General Public
17
18
 * Boston, MA 02111-1307, USA.
18
19
 */
19
20
 
 
21
/**
 
22
 * SECTION:element-assrender
 
23
 *
 
24
 * Renders timestamped SSA/ASS subtitles on top of a video stream.
 
25
 *
 
26
 * <refsect2>
 
27
 * <title>Example launch line</title>
 
28
 * |[
 
29
 * gst-launch -v filesrc location=/path/to/mkv ! matroskademux name=d ! queue ! mp3parse ! mad ! audioconvert ! autoaudiosink  d. ! queue ! ffdec_h264 ! ffmpegcolorspace ! r.   d. ! queue ! "application/x-ass" ! assrender name=r ! ffmpegcolorspace ! autovideosink
 
30
 * ]| This pipeline demuxes a Matroska file with h.264 video, MP3 audio and embedded ASS subtitles and renders the subtitles on top of the video.
 
31
 * </refsect2>
 
32
 */
 
33
 
 
34
 
20
35
#ifdef HAVE_CONFIG_H
21
36
#  include <config.h>
22
37
#endif
23
38
 
24
39
#include "gstassrender.h"
25
40
 
26
 
#include <gst/video/video.h>
 
41
#include <string.h>
27
42
 
28
 
GST_DEBUG_CATEGORY_STATIC (gst_assrender_debug);
29
 
#define GST_CAT_DEFAULT gst_assrender_debug
 
43
GST_DEBUG_CATEGORY_STATIC (gst_ass_render_debug);
 
44
GST_DEBUG_CATEGORY_STATIC (gst_ass_render_lib_debug);
 
45
#define GST_CAT_DEFAULT gst_ass_render_debug
30
46
 
31
47
/* Filter signals and props */
32
48
enum
36
52
 
37
53
enum
38
54
{
39
 
  ARG_0,
40
 
  ARG_ENABLE,
41
 
  ARG_EMBEDDEDFONTS
 
55
  PROP_0,
 
56
  PROP_ENABLE,
 
57
  PROP_EMBEDDEDFONTS
42
58
};
43
59
 
44
60
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
45
61
    GST_PAD_SRC,
46
62
    GST_PAD_ALWAYS,
47
 
    GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB)
 
63
    GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
 
64
        GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";"
 
65
        GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx ";"
 
66
        GST_VIDEO_CAPS_YUV ("I420"))
48
67
    );
49
68
 
50
69
static GstStaticPadTemplate video_sink_factory =
51
 
GST_STATIC_PAD_TEMPLATE ("video_sink",
 
70
    GST_STATIC_PAD_TEMPLATE ("video_sink",
52
71
    GST_PAD_SINK,
53
72
    GST_PAD_ALWAYS,
54
 
    GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB)
 
73
    GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB ";" GST_VIDEO_CAPS_BGR ";"
 
74
        GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR ";"
 
75
        GST_VIDEO_CAPS_RGBx ";" GST_VIDEO_CAPS_BGRx ";"
 
76
        GST_VIDEO_CAPS_YUV ("I420"))
55
77
    );
56
78
 
57
79
static GstStaticPadTemplate text_sink_factory =
61
83
    GST_STATIC_CAPS ("application/x-ass; application/x-ssa")
62
84
    );
63
85
 
64
 
static void gst_assrender_set_property (GObject * object, guint prop_id,
 
86
static void gst_ass_render_set_property (GObject * object, guint prop_id,
65
87
    const GValue * value, GParamSpec * pspec);
66
 
static void gst_assrender_get_property (GObject * object, guint prop_id,
 
88
static void gst_ass_render_get_property (GObject * object, guint prop_id,
67
89
    GValue * value, GParamSpec * pspec);
68
90
 
69
 
static void gst_assrender_finalize (GObject * object);
 
91
static void gst_ass_render_finalize (GObject * object);
70
92
 
71
 
static GstStateChangeReturn gst_assrender_change_state (GstElement * element,
 
93
static GstStateChangeReturn gst_ass_render_change_state (GstElement * element,
72
94
    GstStateChange transition);
73
95
 
74
 
GST_BOILERPLATE (Gstassrender, gst_assrender, GstElement, GST_TYPE_ELEMENT);
75
 
 
76
 
static GstCaps *gst_assrender_getcaps (GstPad * pad);
77
 
 
78
 
static gboolean gst_assrender_setcaps_video (GstPad * pad, GstCaps * caps);
79
 
static gboolean gst_assrender_setcaps_text (GstPad * pad, GstCaps * caps);
80
 
 
81
 
static GstFlowReturn gst_assrender_chain_video (GstPad * pad, GstBuffer * buf);
82
 
static GstFlowReturn gst_assrender_chain_text (GstPad * pad, GstBuffer * buf);
83
 
 
84
 
static gboolean gst_assrender_event_video (GstPad * pad, GstEvent * event);
85
 
static gboolean gst_assrender_event_text (GstPad * pad, GstEvent * event);
 
96
GST_BOILERPLATE (GstAssRender, gst_ass_render, GstElement, GST_TYPE_ELEMENT);
 
97
 
 
98
static GstCaps *gst_ass_render_getcaps (GstPad * pad);
 
99
 
 
100
static gboolean gst_ass_render_setcaps_video (GstPad * pad, GstCaps * caps);
 
101
static gboolean gst_ass_render_setcaps_text (GstPad * pad, GstCaps * caps);
 
102
 
 
103
static GstFlowReturn gst_ass_render_chain_video (GstPad * pad, GstBuffer * buf);
 
104
static GstFlowReturn gst_ass_render_chain_text (GstPad * pad, GstBuffer * buf);
 
105
 
 
106
static gboolean gst_ass_render_event_video (GstPad * pad, GstEvent * event);
 
107
static gboolean gst_ass_render_event_text (GstPad * pad, GstEvent * event);
 
108
static gboolean gst_ass_render_event_src (GstPad * pad, GstEvent * event);
 
109
 
 
110
static GstFlowReturn gst_ass_render_bufferalloc_video (GstPad * pad,
 
111
    guint64 offset, guint size, GstCaps * caps, GstBuffer ** buffer);
 
112
 
 
113
static gboolean gst_ass_render_query_src (GstPad * pad, GstQuery * query);
86
114
 
87
115
static void
88
 
gst_assrender_base_init (gpointer gclass)
 
116
gst_ass_render_base_init (gpointer gclass)
89
117
{
90
 
  GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
 
118
  GstElementClass *element_class = (GstElementClass *) gclass;
91
119
 
92
120
  gst_element_class_add_pad_template (element_class,
93
121
      gst_static_pad_template_get (&src_factory));
99
127
  gst_element_class_set_details_simple (element_class, "ASS/SSA Render",
100
128
      "Mixer/Video/Overlay/Subtitle",
101
129
      "Renders ASS/SSA subtitles with libass",
102
 
      "Benjamin Schmitz <vortex@wolpzone.de>");
 
130
      "Benjamin Schmitz <vortex@wolpzone.de>, "
 
131
      "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
103
132
}
104
133
 
105
134
/* initialize the plugin's class */
106
135
static void
107
 
gst_assrender_class_init (GstassrenderClass * klass)
 
136
gst_ass_render_class_init (GstAssRenderClass * klass)
108
137
{
109
 
  GObjectClass *gobject_class;
110
 
  GstElementClass *gstelement_class;
111
 
 
112
 
  gobject_class = (GObjectClass *) klass;
113
 
  gstelement_class = (GstElementClass *) klass;
114
 
 
115
 
  gobject_class->set_property = gst_assrender_set_property;
116
 
  gobject_class->get_property = gst_assrender_get_property;
117
 
 
118
 
  gobject_class->finalize = gst_assrender_finalize;
119
 
 
120
 
  g_object_class_install_property (gobject_class, ARG_ENABLE,
121
 
      g_param_spec_boolean ("enable", "Toggle rendering",
 
138
  GObjectClass *gobject_class = (GObjectClass *) klass;
 
139
  GstElementClass *gstelement_class = (GstElementClass *) klass;
 
140
 
 
141
  gobject_class->set_property = gst_ass_render_set_property;
 
142
  gobject_class->get_property = gst_ass_render_get_property;
 
143
  gobject_class->finalize = gst_ass_render_finalize;
 
144
 
 
145
  g_object_class_install_property (gobject_class, PROP_ENABLE,
 
146
      g_param_spec_boolean ("enable", "Enable",
122
147
          "Enable rendering of subtitles", TRUE,
123
148
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
124
 
  g_object_class_install_property (gobject_class, ARG_EMBEDDEDFONTS,
125
 
      g_param_spec_boolean ("embeddedfonts", "Use embedded fonts",
 
149
  g_object_class_install_property (gobject_class, PROP_EMBEDDEDFONTS,
 
150
      g_param_spec_boolean ("embeddedfonts", "Embedded Fonts",
126
151
          "Extract and use fonts embedded in the stream", TRUE,
127
152
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
128
153
 
129
154
  gstelement_class->change_state =
130
 
      GST_DEBUG_FUNCPTR (gst_assrender_change_state);
131
 
}
132
 
 
133
 
static void
134
 
gst_assrender_init (Gstassrender * render, GstassrenderClass * gclass)
 
155
      GST_DEBUG_FUNCPTR (gst_ass_render_change_state);
 
156
}
 
157
 
 
158
#if defined(LIBASS_VERSION) && LIBASS_VERSION >= 0x00907000
 
159
static void
 
160
_libass_message_cb (gint level, const gchar * fmt, va_list args, gpointer data)
 
161
{
 
162
  GstAssRender *render = GST_ASS_RENDER (data);
 
163
  gchar *message = g_strdup_vprintf (fmt, args);
 
164
 
 
165
  if (level < 2)
 
166
    GST_CAT_ERROR_OBJECT (gst_ass_render_lib_debug, render, message);
 
167
  else if (level < 4)
 
168
    GST_CAT_WARNING_OBJECT (gst_ass_render_lib_debug, render, message);
 
169
  else if (level < 5)
 
170
    GST_CAT_INFO_OBJECT (gst_ass_render_lib_debug, render, message);
 
171
  else if (level < 6)
 
172
    GST_CAT_DEBUG_OBJECT (gst_ass_render_lib_debug, render, message);
 
173
  else
 
174
    GST_CAT_LOG_OBJECT (gst_ass_render_lib_debug, render, message);
 
175
 
 
176
  g_free (message);
 
177
}
 
178
#endif
 
179
 
 
180
static void
 
181
gst_ass_render_init (GstAssRender * render, GstAssRenderClass * gclass)
135
182
{
136
183
  GST_DEBUG_OBJECT (render, "init");
137
184
 
142
189
      gst_pad_new_from_static_template (&text_sink_factory, "text_sink");
143
190
 
144
191
  gst_pad_set_setcaps_function (render->video_sinkpad,
145
 
      GST_DEBUG_FUNCPTR (gst_assrender_setcaps_video));
 
192
      GST_DEBUG_FUNCPTR (gst_ass_render_setcaps_video));
146
193
  gst_pad_set_setcaps_function (render->text_sinkpad,
147
 
      GST_DEBUG_FUNCPTR (gst_assrender_setcaps_text));
 
194
      GST_DEBUG_FUNCPTR (gst_ass_render_setcaps_text));
148
195
 
149
196
  gst_pad_set_getcaps_function (render->srcpad,
150
 
      GST_DEBUG_FUNCPTR (gst_assrender_getcaps));
 
197
      GST_DEBUG_FUNCPTR (gst_ass_render_getcaps));
 
198
  gst_pad_set_getcaps_function (render->video_sinkpad,
 
199
      GST_DEBUG_FUNCPTR (gst_ass_render_getcaps));
151
200
 
152
201
  gst_pad_set_chain_function (render->video_sinkpad,
153
 
      GST_DEBUG_FUNCPTR (gst_assrender_chain_video));
 
202
      GST_DEBUG_FUNCPTR (gst_ass_render_chain_video));
154
203
  gst_pad_set_chain_function (render->text_sinkpad,
155
 
      GST_DEBUG_FUNCPTR (gst_assrender_chain_text));
 
204
      GST_DEBUG_FUNCPTR (gst_ass_render_chain_text));
156
205
 
157
206
  gst_pad_set_event_function (render->video_sinkpad,
158
 
      GST_DEBUG_FUNCPTR (gst_assrender_event_video));
 
207
      GST_DEBUG_FUNCPTR (gst_ass_render_event_video));
159
208
  gst_pad_set_event_function (render->text_sinkpad,
160
 
      GST_DEBUG_FUNCPTR (gst_assrender_event_text));
 
209
      GST_DEBUG_FUNCPTR (gst_ass_render_event_text));
 
210
  gst_pad_set_event_function (render->srcpad,
 
211
      GST_DEBUG_FUNCPTR (gst_ass_render_event_src));
 
212
 
 
213
  gst_pad_set_bufferalloc_function (render->video_sinkpad,
 
214
      GST_DEBUG_FUNCPTR (gst_ass_render_bufferalloc_video));
 
215
 
 
216
  gst_pad_set_query_function (render->srcpad,
 
217
      GST_DEBUG_FUNCPTR (gst_ass_render_query_src));
161
218
 
162
219
  gst_element_add_pad (GST_ELEMENT (render), render->srcpad);
163
220
  gst_element_add_pad (GST_ELEMENT (render), render->video_sinkpad);
177
234
  gst_segment_init (&render->video_segment, GST_FORMAT_TIME);
178
235
  gst_segment_init (&render->subtitle_segment, GST_FORMAT_TIME);
179
236
 
 
237
  render->ass_mutex = g_mutex_new ();
180
238
  render->ass_library = ass_library_init ();
181
 
  ass_set_fonts_dir (render->ass_library, "./");
182
 
  ass_set_extract_fonts (render->ass_library, 0);
 
239
#if defined(LIBASS_VERSION) && LIBASS_VERSION >= 0x00907000
 
240
  ass_set_message_cb (render->ass_library, _libass_message_cb, render);
 
241
#endif
 
242
  ass_set_extract_fonts (render->ass_library, 1);
183
243
 
184
244
  render->ass_renderer = ass_renderer_init (render->ass_library);
185
245
  if (!render->ass_renderer) {
193
253
}
194
254
 
195
255
static void
196
 
gst_assrender_finalize (GObject * object)
 
256
gst_ass_render_finalize (GObject * object)
197
257
{
198
 
  Gstassrender *render = GST_ASSRENDER (object);
 
258
  GstAssRender *render = GST_ASS_RENDER (object);
199
259
 
200
260
  if (render->subtitle_mutex)
201
261
    g_mutex_free (render->subtitle_mutex);
215
275
    ass_library_done (render->ass_library);
216
276
  }
217
277
 
 
278
  if (render->ass_mutex)
 
279
    g_mutex_free (render->ass_mutex);
 
280
 
218
281
  G_OBJECT_CLASS (parent_class)->finalize (object);
219
282
}
220
283
 
221
284
static void
222
 
gst_assrender_set_property (GObject * object, guint prop_id,
 
285
gst_ass_render_set_property (GObject * object, guint prop_id,
223
286
    const GValue * value, GParamSpec * pspec)
224
287
{
225
 
  Gstassrender *render = GST_ASSRENDER (object);
 
288
  GstAssRender *render = GST_ASS_RENDER (object);
226
289
 
227
290
  switch (prop_id) {
228
 
    case ARG_ENABLE:
 
291
    case PROP_ENABLE:
229
292
      render->enable = g_value_get_boolean (value);
230
293
      break;
231
 
    case ARG_EMBEDDEDFONTS:
 
294
    case PROP_EMBEDDEDFONTS:
232
295
      render->embeddedfonts = g_value_get_boolean (value);
 
296
      g_mutex_lock (render->ass_mutex);
 
297
      ass_set_extract_fonts (render->ass_library, render->embeddedfonts);
 
298
      g_mutex_unlock (render->ass_mutex);
233
299
      break;
234
300
    default:
235
301
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
238
304
}
239
305
 
240
306
static void
241
 
gst_assrender_get_property (GObject * object, guint prop_id,
 
307
gst_ass_render_get_property (GObject * object, guint prop_id,
242
308
    GValue * value, GParamSpec * pspec)
243
309
{
244
 
  Gstassrender *render = GST_ASSRENDER (object);
 
310
  GstAssRender *render = GST_ASS_RENDER (object);
245
311
 
246
312
  switch (prop_id) {
247
 
    case ARG_ENABLE:
 
313
    case PROP_ENABLE:
248
314
      g_value_set_boolean (value, render->enable);
249
315
      break;
250
 
    case ARG_EMBEDDEDFONTS:
 
316
    case PROP_EMBEDDEDFONTS:
251
317
      g_value_set_boolean (value, render->embeddedfonts);
252
318
      break;
253
319
    default:
257
323
}
258
324
 
259
325
static GstStateChangeReturn
260
 
gst_assrender_change_state (GstElement * element, GstStateChange transition)
 
326
gst_ass_render_change_state (GstElement * element, GstStateChange transition)
261
327
{
262
 
  Gstassrender *render = GST_ASSRENDER (element);
 
328
  GstAssRender *render = GST_ASS_RENDER (element);
263
329
  GstStateChangeReturn ret;
264
330
 
265
331
  switch (transition) {
288
354
 
289
355
  switch (transition) {
290
356
    case GST_STATE_CHANGE_PAUSED_TO_READY:
 
357
      g_mutex_lock (render->ass_mutex);
291
358
      if (render->ass_track)
292
359
        ass_free_track (render->ass_track);
293
360
      render->ass_track = NULL;
 
361
      g_mutex_unlock (render->ass_mutex);
294
362
      render->track_init_ok = FALSE;
295
363
      render->renderer_init_ok = FALSE;
296
364
      break;
304
372
  return ret;
305
373
}
306
374
 
 
375
static gboolean
 
376
gst_ass_render_query_src (GstPad * pad, GstQuery * query)
 
377
{
 
378
  GstAssRender *render = GST_ASS_RENDER (gst_pad_get_parent (pad));
 
379
  gboolean ret;
 
380
 
 
381
  ret = gst_pad_peer_query (render->video_sinkpad, query);
 
382
 
 
383
  gst_object_unref (render);
 
384
  return ret;
 
385
}
 
386
 
 
387
static gboolean
 
388
gst_ass_render_event_src (GstPad * pad, GstEvent * event)
 
389
{
 
390
  GstAssRender *render = GST_ASS_RENDER (gst_pad_get_parent (pad));
 
391
  gboolean ret = FALSE;
 
392
 
 
393
  switch (GST_EVENT_TYPE (event)) {
 
394
    case GST_EVENT_SEEK:{
 
395
      GstSeekFlags flags;
 
396
 
 
397
      GST_DEBUG_OBJECT (render, "seek received, driving from here");
 
398
 
 
399
      gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
 
400
 
 
401
      /* Flush downstream, only for flushing seek */
 
402
      if (flags & GST_SEEK_FLAG_FLUSH)
 
403
        gst_pad_push_event (render->srcpad, gst_event_new_flush_start ());
 
404
 
 
405
      /* Mark subtitle as flushing, unblocks chains */
 
406
      g_mutex_lock (render->subtitle_mutex);
 
407
      if (render->subtitle_pending)
 
408
        gst_buffer_unref (render->subtitle_pending);
 
409
      render->subtitle_pending = NULL;
 
410
      render->subtitle_flushing = TRUE;
 
411
      g_cond_signal (render->subtitle_cond);
 
412
      g_mutex_unlock (render->subtitle_mutex);
 
413
 
 
414
      /* Seek on each sink pad */
 
415
      gst_event_ref (event);
 
416
      ret = gst_pad_push_event (render->video_sinkpad, event);
 
417
      if (ret) {
 
418
        ret = gst_pad_push_event (render->text_sinkpad, event);
 
419
      } else {
 
420
        gst_event_unref (event);
 
421
      }
 
422
      break;
 
423
    }
 
424
    default:
 
425
      gst_event_ref (event);
 
426
      ret = gst_pad_push_event (render->video_sinkpad, event);
 
427
      gst_pad_push_event (render->text_sinkpad, event);
 
428
      break;
 
429
  }
 
430
 
 
431
  gst_object_unref (render);
 
432
 
 
433
  return ret;
 
434
}
 
435
 
307
436
static GstCaps *
308
 
gst_assrender_getcaps (GstPad * pad)
 
437
gst_ass_render_getcaps (GstPad * pad)
309
438
{
310
 
  Gstassrender *render;
 
439
  GstAssRender *render = GST_ASS_RENDER (gst_pad_get_parent (pad));
311
440
  GstPad *otherpad;
312
441
  GstCaps *caps;
313
442
 
314
 
  render = GST_ASSRENDER (gst_pad_get_parent (pad));
315
 
 
316
443
  if (pad == render->srcpad)
317
444
    otherpad = render->video_sinkpad;
318
445
  else
340
467
  return caps;
341
468
}
342
469
 
 
470
#define CREATE_RGB_BLIT_FUNCTION(name,bpp,R,G,B) \
 
471
static void \
 
472
blit_##name (GstAssRender * render, ASS_Image * ass_image, GstBuffer * buffer) \
 
473
{ \
 
474
  guint counter = 0; \
 
475
  gint alpha, r, g, b, k; \
 
476
  const guint8 *src; \
 
477
  guint8 *dst; \
 
478
  gint x, y, w, h; \
 
479
  gint width = render->width; \
 
480
  gint height = render->height; \
 
481
  gint dst_stride = GST_ROUND_UP_4 (width * bpp); \
 
482
  gint dst_skip; \
 
483
  gint src_stride, src_skip; \
 
484
  \
 
485
  while (ass_image) { \
 
486
    if (ass_image->dst_y > height || ass_image->dst_x > width) \
 
487
      goto next; \
 
488
    \
 
489
    /* blend subtitles onto the video frame */ \
 
490
    alpha = 255 - ((ass_image->color) & 0xff); \
 
491
    r = ((ass_image->color) >> 24) & 0xff; \
 
492
    g = ((ass_image->color) >> 16) & 0xff; \
 
493
    b = ((ass_image->color) >> 8) & 0xff; \
 
494
    src = ass_image->bitmap; \
 
495
    dst = buffer->data + ass_image->dst_y * dst_stride + ass_image->dst_x * bpp; \
 
496
    \
 
497
    w = MIN (ass_image->w, width - ass_image->dst_x); \
 
498
    h = MIN (ass_image->h, height - ass_image->dst_y); \
 
499
    src_stride = ass_image->stride; \
 
500
    src_skip = ass_image->stride - w; \
 
501
    dst_skip = dst_stride - w * bpp; \
 
502
    \
 
503
    for (y = 0; y < h; y++) { \
 
504
      for (x = 0; x < w; x++) { \
 
505
        k = src[0] * alpha / 255; \
 
506
        dst[R] = (k * r + (255 - k) * dst[R]) / 255; \
 
507
        dst[G] = (k * g + (255 - k) * dst[G]) / 255; \
 
508
        dst[B] = (k * b + (255 - k) * dst[B]) / 255; \
 
509
        src++; \
 
510
        dst += bpp; \
 
511
      } \
 
512
      src += src_skip; \
 
513
      dst += dst_skip; \
 
514
    } \
 
515
next: \
 
516
    counter++; \
 
517
    ass_image = ass_image->next; \
 
518
  } \
 
519
  GST_LOG_OBJECT (render, "amount of rendered ass_image: %u", counter); \
 
520
}
 
521
 
 
522
CREATE_RGB_BLIT_FUNCTION (rgb, 3, 0, 1, 2);
 
523
CREATE_RGB_BLIT_FUNCTION (bgr, 3, 2, 1, 0);
 
524
CREATE_RGB_BLIT_FUNCTION (xrgb, 4, 1, 2, 3);
 
525
CREATE_RGB_BLIT_FUNCTION (xbgr, 4, 3, 2, 1);
 
526
CREATE_RGB_BLIT_FUNCTION (rgbx, 4, 0, 1, 2);
 
527
CREATE_RGB_BLIT_FUNCTION (bgrx, 4, 2, 1, 0);
 
528
 
 
529
#undef CREATE_RGB_BLIT_FUNCTION
 
530
 
 
531
static inline gint
 
532
rgb_to_y (gint r, gint g, gint b)
 
533
{
 
534
  gint ret;
 
535
 
 
536
  ret = (gint) (((19595 * r) >> 16) + ((38470 * g) >> 16) + ((7471 * b) >> 16));
 
537
  ret = CLAMP (ret, 0, 255);
 
538
  return ret;
 
539
}
 
540
 
 
541
static inline gint
 
542
rgb_to_u (gint r, gint g, gint b)
 
543
{
 
544
  gint ret;
 
545
 
 
546
  ret =
 
547
      (gint) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + ((32768 * b) >> 16) +
 
548
      128);
 
549
  ret = CLAMP (ret, 0, 255);
 
550
  return ret;
 
551
}
 
552
 
 
553
static inline gint
 
554
rgb_to_v (gint r, gint g, gint b)
 
555
{
 
556
  gint ret;
 
557
 
 
558
  ret =
 
559
      (gint) (((32768 * r) >> 16) - ((27439 * g) >> 16) - ((5329 * b) >> 16) +
 
560
      128);
 
561
  ret = CLAMP (ret, 0, 255);
 
562
  return ret;
 
563
}
 
564
 
 
565
static void
 
566
blit_i420 (GstAssRender * render, ASS_Image * ass_image, GstBuffer * buffer)
 
567
{
 
568
  guint counter = 0;
 
569
  gint alpha, r, g, b, k, k2;
 
570
  gint Y, U, V;
 
571
  const guint8 *src;
 
572
  guint8 *dst_y, *dst_u, *dst_v;
 
573
  gint x, y, w, h;
 
574
  gint w2, h2;
 
575
  gint width = render->width;
 
576
  gint height = render->height;
 
577
  gint src_stride;
 
578
  gint y_offset, y_height, y_width, y_stride;
 
579
  gint u_offset, u_height, u_width, u_stride;
 
580
  gint v_offset, v_height, v_width, v_stride;
 
581
 
 
582
  y_offset =
 
583
      gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 0, width,
 
584
      height);
 
585
  u_offset =
 
586
      gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 1, width,
 
587
      height);
 
588
  v_offset =
 
589
      gst_video_format_get_component_offset (GST_VIDEO_FORMAT_I420, 2, width,
 
590
      height);
 
591
 
 
592
  y_height =
 
593
      gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 0, height);
 
594
  u_height =
 
595
      gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 1, height);
 
596
  v_height =
 
597
      gst_video_format_get_component_height (GST_VIDEO_FORMAT_I420, 2, height);
 
598
 
 
599
  y_width =
 
600
      gst_video_format_get_component_width (GST_VIDEO_FORMAT_I420, 0, width);
 
601
  u_width =
 
602
      gst_video_format_get_component_width (GST_VIDEO_FORMAT_I420, 1, width);
 
603
  v_width =
 
604
      gst_video_format_get_component_width (GST_VIDEO_FORMAT_I420, 2, width);
 
605
 
 
606
  y_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 0, width);
 
607
  u_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 1, width);
 
608
  v_stride = gst_video_format_get_row_stride (GST_VIDEO_FORMAT_I420, 2, width);
 
609
 
 
610
  while (ass_image) {
 
611
    if (ass_image->dst_y > height || ass_image->dst_x > width)
 
612
      goto next;
 
613
 
 
614
    /* blend subtitles onto the video frame */
 
615
    alpha = 255 - ((ass_image->color) & 0xff);
 
616
    r = ((ass_image->color) >> 24) & 0xff;
 
617
    g = ((ass_image->color) >> 16) & 0xff;
 
618
    b = ((ass_image->color) >> 8) & 0xff;
 
619
 
 
620
    Y = rgb_to_y (r, g, b);
 
621
    U = rgb_to_u (r, g, b);
 
622
    V = rgb_to_v (r, g, b);
 
623
 
 
624
    w = MIN (ass_image->w, width - ass_image->dst_x);
 
625
    h = MIN (ass_image->h, height - ass_image->dst_y);
 
626
 
 
627
    w2 = (w + 1) / 2;
 
628
    h2 = (h + 1) / 2;
 
629
 
 
630
    src_stride = ass_image->stride;
 
631
 
 
632
    src = ass_image->bitmap;
 
633
    dst_y =
 
634
        buffer->data + y_offset + ass_image->dst_y * y_stride +
 
635
        ass_image->dst_x;
 
636
    dst_u =
 
637
        buffer->data + u_offset + ((ass_image->dst_y + 1) / 2) * u_stride +
 
638
        (ass_image->dst_x + 1) / 2;
 
639
    dst_v =
 
640
        buffer->data + v_offset + ((ass_image->dst_y + 1) / 2) * v_stride +
 
641
        (ass_image->dst_x + 1) / 2;
 
642
 
 
643
    for (y = 0; y < h - 1; y += 2) {
 
644
      for (x = 0; x < w - 1; x += 2) {
 
645
        k = src[0] * alpha / 255;
 
646
        k2 = k;
 
647
        dst_y[0] = (k * Y + (255 - k) * dst_y[0]) / 255;
 
648
 
 
649
        k = src[1] * alpha / 255;
 
650
        k2 += k;
 
651
        dst_y[1] = (k * Y + (255 - k) * dst_y[1]) / 255;
 
652
 
 
653
        src += src_stride;
 
654
        dst_y += y_stride;
 
655
 
 
656
        k = src[0] * alpha / 255;
 
657
        k2 += k;
 
658
        dst_y[0] = (k * Y + (255 - k) * dst_y[0]) / 255;
 
659
 
 
660
        k = src[1] * alpha / 255;
 
661
        k2 += k;
 
662
        dst_y[1] = (k * Y + (255 - k) * dst_y[1]) / 255;
 
663
 
 
664
        k2 /= 4;
 
665
        dst_u[0] = (k2 * U + (255 - k2) * dst_u[0]) / 255;
 
666
        dst_v[0] = (k2 * V + (255 - k2) * dst_v[0]) / 255;
 
667
        dst_u++;
 
668
        dst_v++;
 
669
 
 
670
        src += -src_stride + 2;
 
671
        dst_y += -y_stride + 2;
 
672
      }
 
673
 
 
674
      if (x < w) {
 
675
        k = src[0] * alpha / 255;
 
676
        k2 = k;
 
677
        dst_y[0] = (k * Y + (255 - k) * dst_y[0]) / 255;
 
678
 
 
679
        src += src_stride;
 
680
        dst_y += y_stride;
 
681
 
 
682
        k = src[0] * alpha / 255;
 
683
        k2 += k;
 
684
        dst_y[0] = (k * Y + (255 - k) * dst_y[0]) / 255;
 
685
 
 
686
        k2 /= 2;
 
687
        dst_u[0] = (k2 * U + (255 - k2) * dst_u[0]) / 255;
 
688
        dst_v[0] = (k2 * V + (255 - k2) * dst_v[0]) / 255;
 
689
        dst_u++;
 
690
        dst_v++;
 
691
 
 
692
        src += -src_stride + 1;
 
693
        dst_y += -y_stride + 1;
 
694
      }
 
695
 
 
696
      src += src_stride + (src_stride - w);
 
697
      dst_y += y_stride + (y_stride - w);
 
698
      dst_u += u_stride - w2;
 
699
      dst_v += v_stride - w2;
 
700
    }
 
701
 
 
702
    if (y < h) {
 
703
      for (x = 0; x < w - 1; x += 2) {
 
704
        k = src[0] * alpha / 255;
 
705
        k2 = k;
 
706
        dst_y[0] = (k * Y + (255 - k) * dst_y[0]) / 255;
 
707
 
 
708
        k = src[1] * alpha / 255;
 
709
        k2 += k;
 
710
        dst_y[1] = (k * Y + (255 - k) * dst_y[1]) / 255;
 
711
 
 
712
        k2 /= 2;
 
713
        dst_u[0] = (k2 * U + (255 - k2) * dst_u[0]) / 255;
 
714
        dst_v[0] = (k2 * V + (255 - k2) * dst_v[0]) / 255;
 
715
        dst_u++;
 
716
        dst_v++;
 
717
 
 
718
        src += 2;
 
719
        dst_y += 2;
 
720
      }
 
721
 
 
722
      if (x < w) {
 
723
        k = src[0] * alpha / 255;
 
724
        k2 = k;
 
725
        dst_y[0] = (k * Y + (255 - k) * dst_y[0]) / 255;
 
726
 
 
727
        dst_u[0] = (k2 * U + (255 - k2) * dst_u[0]) / 255;
 
728
        dst_v[0] = (k2 * V + (255 - k2) * dst_v[0]) / 255;
 
729
      }
 
730
    }
 
731
 
 
732
  next:
 
733
    counter++;
 
734
    ass_image = ass_image->next;
 
735
  }
 
736
 
 
737
  GST_LOG_OBJECT (render, "amount of rendered ass_image: %u", counter);
 
738
}
 
739
 
343
740
static gboolean
344
 
gst_assrender_setcaps_video (GstPad * pad, GstCaps * caps)
 
741
gst_ass_render_setcaps_video (GstPad * pad, GstCaps * caps)
345
742
{
346
 
  Gstassrender *render;
347
 
  GstStructure *structure;
 
743
  GstAssRender *render = GST_ASS_RENDER (gst_pad_get_parent (pad));
348
744
  gboolean ret = FALSE;
349
745
  gint par_n = 1, par_d = 1;
350
 
 
351
 
  render = GST_ASSRENDER (gst_pad_get_parent (pad));
 
746
  gdouble dar;
352
747
 
353
748
  render->width = 0;
354
749
  render->height = 0;
355
750
 
356
 
  structure = gst_caps_get_structure (caps, 0);
357
 
 
358
 
  gst_structure_get_fraction (structure, "pixel-aspect-ratio", &par_n, &par_d);
359
 
 
360
 
  if (gst_structure_get_int (structure, "width", &render->width) &&
361
 
      gst_structure_get_int (structure, "height", &render->height)) {
362
 
    gdouble dar;
363
 
 
364
 
    ret = gst_pad_set_caps (render->srcpad, caps);
365
 
    ass_set_frame_size (render->ass_renderer, render->width, render->height);
366
 
 
367
 
    dar = (((gdouble) par_n) * ((gdouble) render->width));
368
 
    dar /= (((gdouble) par_d) * ((gdouble) render->height));
369
 
#if !defined(LIBASS_VERSION) || LIBASS_VERSION < 0x00907000
370
 
    ass_set_aspect_ratio (render->ass_renderer, dar);
371
 
#else
372
 
    ass_set_aspect_ratio (render->ass_renderer,
373
 
        dar, ((gdouble) render->width) / ((gdouble) render->height));
374
 
#endif
375
 
    ass_set_font_scale (render->ass_renderer, 1.0);
376
 
    ass_set_hinting (render->ass_renderer, ASS_HINTING_NATIVE);
377
 
 
378
 
#if !defined(LIBASS_VERSION) || LIBASS_VERSION < 0x00907000
379
 
    ass_set_fonts (render->ass_renderer, "Arial", "sans-serif");
380
 
    ass_set_fonts (render->ass_renderer, NULL, "Sans");
381
 
#else
382
 
    ass_set_fonts (render->ass_renderer, "Arial", "sans-serif", 1, NULL, 1);
383
 
    ass_set_fonts (render->ass_renderer, NULL, "Sans", 1, NULL, 1);
384
 
#endif
385
 
    ass_set_margins (render->ass_renderer, 0, 0, 0, 0);
386
 
    ass_set_use_margins (render->ass_renderer, 0);
387
 
 
388
 
    render->renderer_init_ok = TRUE;
389
 
 
390
 
    GST_DEBUG_OBJECT (render, "ass renderer setup complete");
391
 
  } else {
392
 
    GST_ERROR_OBJECT (render, "Invalid caps %" GST_PTR_FORMAT, caps);
 
751
  if (!gst_video_format_parse_caps (caps, &render->format, &render->width,
 
752
          &render->height) ||
 
753
      !gst_video_parse_caps_framerate (caps, &render->fps_n, &render->fps_d)) {
 
754
    GST_ERROR_OBJECT (render, "Can't parse caps: %" GST_PTR_FORMAT, caps);
393
755
    ret = FALSE;
394
 
  }
395
 
 
 
756
    goto out;
 
757
  }
 
758
 
 
759
  gst_video_parse_caps_pixel_aspect_ratio (caps, &par_n, &par_d);
 
760
 
 
761
  ret = gst_pad_set_caps (render->srcpad, caps);
 
762
  if (!ret)
 
763
    goto out;
 
764
 
 
765
  switch (render->format) {
 
766
    case GST_VIDEO_FORMAT_RGB:
 
767
      render->blit = blit_rgb;
 
768
      break;
 
769
    case GST_VIDEO_FORMAT_BGR:
 
770
      render->blit = blit_bgr;
 
771
      break;
 
772
    case GST_VIDEO_FORMAT_xRGB:
 
773
      render->blit = blit_xrgb;
 
774
      break;
 
775
    case GST_VIDEO_FORMAT_xBGR:
 
776
      render->blit = blit_xbgr;
 
777
      break;
 
778
    case GST_VIDEO_FORMAT_RGBx:
 
779
      render->blit = blit_rgbx;
 
780
      break;
 
781
    case GST_VIDEO_FORMAT_BGRx:
 
782
      render->blit = blit_bgrx;
 
783
      break;
 
784
    case GST_VIDEO_FORMAT_I420:
 
785
      render->blit = blit_i420;
 
786
      break;
 
787
    default:
 
788
      ret = FALSE;
 
789
      goto out;
 
790
  }
 
791
 
 
792
  g_mutex_lock (render->ass_mutex);
 
793
  ass_set_frame_size (render->ass_renderer, render->width, render->height);
 
794
 
 
795
  dar = (((gdouble) par_n) * ((gdouble) render->width))
 
796
      / (((gdouble) par_d) * ((gdouble) render->height));
 
797
#if !defined(LIBASS_VERSION) || LIBASS_VERSION < 0x00907000
 
798
  ass_set_aspect_ratio (render->ass_renderer, dar);
 
799
#else
 
800
  ass_set_aspect_ratio (render->ass_renderer,
 
801
      dar, ((gdouble) render->width) / ((gdouble) render->height));
 
802
#endif
 
803
  ass_set_font_scale (render->ass_renderer, 1.0);
 
804
  ass_set_hinting (render->ass_renderer, ASS_HINTING_LIGHT);
 
805
 
 
806
#if !defined(LIBASS_VERSION) || LIBASS_VERSION < 0x00907000
 
807
  ass_set_fonts (render->ass_renderer, "Arial", "sans-serif");
 
808
  ass_set_fonts (render->ass_renderer, NULL, "Sans");
 
809
#else
 
810
  ass_set_fonts (render->ass_renderer, "Arial", "sans-serif", 1, NULL, 1);
 
811
  ass_set_fonts (render->ass_renderer, NULL, "Sans", 1, NULL, 1);
 
812
#endif
 
813
  ass_set_margins (render->ass_renderer, 0, 0, 0, 0);
 
814
  ass_set_use_margins (render->ass_renderer, 0);
 
815
  g_mutex_unlock (render->ass_mutex);
 
816
 
 
817
  render->renderer_init_ok = TRUE;
 
818
 
 
819
  GST_DEBUG_OBJECT (render, "ass renderer setup complete");
 
820
 
 
821
out:
396
822
  gst_object_unref (render);
397
823
 
398
824
  return ret;
399
825
}
400
826
 
401
827
static gboolean
402
 
gst_assrender_setcaps_text (GstPad * pad, GstCaps * caps)
 
828
gst_ass_render_setcaps_text (GstPad * pad, GstCaps * caps)
403
829
{
404
 
  Gstassrender *render;
 
830
  GstAssRender *render = GST_ASS_RENDER (gst_pad_get_parent (pad));
405
831
  GstStructure *structure;
406
832
  const GValue *value;
407
833
  GstBuffer *priv;
409
835
  guint codec_private_size;
410
836
  gboolean ret = FALSE;
411
837
 
412
 
  render = GST_ASSRENDER (gst_pad_get_parent (pad));
413
 
 
414
838
  structure = gst_caps_get_structure (caps, 0);
415
839
 
416
840
  GST_DEBUG_OBJECT (render, "text pad linked with caps:  %" GST_PTR_FORMAT,
418
842
 
419
843
  value = gst_structure_get_value (structure, "codec_data");
420
844
 
 
845
  g_mutex_lock (render->ass_mutex);
421
846
  if (value != NULL) {
422
 
    priv = (GstBuffer *) gst_value_get_mini_object (value);
 
847
    priv = gst_value_get_buffer (value);
423
848
    g_return_val_if_fail (priv != NULL, FALSE);
424
849
 
425
 
    gst_buffer_ref (priv);
426
 
 
427
850
    codec_private = (gchar *) GST_BUFFER_DATA (priv);
428
851
    codec_private_size = GST_BUFFER_SIZE (priv);
429
852
 
430
 
    if (render->ass_track) {
431
 
      ass_free_track (render->ass_track);
432
 
    }
 
853
    if (!render->ass_track)
 
854
      render->ass_track = ass_new_track (render->ass_library);
433
855
 
434
 
    render->ass_track = ass_new_track (render->ass_library);
435
856
    ass_process_codec_private (render->ass_track,
436
857
        codec_private, codec_private_size);
437
858
 
439
860
 
440
861
    render->track_init_ok = TRUE;
441
862
 
442
 
    gst_buffer_unref (priv);
443
 
 
444
863
    ret = TRUE;
445
864
  } else if (!render->ass_track) {
446
865
    render->ass_track = ass_new_track (render->ass_library);
 
866
 
 
867
    render->track_init_ok = TRUE;
 
868
 
447
869
    ret = TRUE;
448
870
  }
 
871
  g_mutex_unlock (render->ass_mutex);
449
872
 
450
873
  gst_object_unref (render);
451
874
 
454
877
 
455
878
 
456
879
static void
457
 
gst_assrender_process_text (Gstassrender * render, GstBuffer * buffer,
 
880
gst_ass_render_process_text (GstAssRender * render, GstBuffer * buffer,
458
881
    GstClockTime running_time, GstClockTime duration)
459
882
{
460
 
  char *data = (gchar *) GST_BUFFER_DATA (buffer);
 
883
  gchar *data = (gchar *) GST_BUFFER_DATA (buffer);
461
884
  guint size = GST_BUFFER_SIZE (buffer);
462
 
  double pts_start, pts_end;
 
885
  gdouble pts_start, pts_end;
463
886
 
464
887
  pts_start = running_time;
465
888
  pts_start /= GST_MSECOND;
470
893
      "Processing subtitles with running time %" GST_TIME_FORMAT
471
894
      " and duration %" GST_TIME_FORMAT, GST_TIME_ARGS (running_time),
472
895
      GST_TIME_ARGS (duration));
 
896
  g_mutex_lock (render->ass_mutex);
473
897
  ass_process_chunk (render->ass_track, data, size, pts_start, pts_end);
 
898
  g_mutex_unlock (render->ass_mutex);
474
899
  gst_buffer_unref (buffer);
475
900
}
476
901
 
477
902
static GstFlowReturn
478
 
gst_assrender_chain_video (GstPad * pad, GstBuffer * buffer)
479
 
{
480
 
  Gstassrender *render;
 
903
gst_ass_render_bufferalloc_video (GstPad * pad, guint64 offset, guint size,
 
904
    GstCaps * caps, GstBuffer ** buffer)
 
905
{
 
906
  GstAssRender *render = GST_ASS_RENDER (gst_pad_get_parent (pad));
 
907
  GstFlowReturn ret = GST_FLOW_WRONG_STATE;
 
908
  GstPad *allocpad;
 
909
 
 
910
  GST_OBJECT_LOCK (render);
 
911
  allocpad = render->srcpad ? gst_object_ref (render->srcpad) : NULL;
 
912
  GST_OBJECT_UNLOCK (render);
 
913
 
 
914
  if (allocpad) {
 
915
    ret = gst_pad_alloc_buffer (allocpad, offset, size, caps, buffer);
 
916
    gst_object_unref (allocpad);
 
917
  }
 
918
 
 
919
  gst_object_unref (render);
 
920
 
 
921
  return ret;
 
922
}
 
923
 
 
924
static GstFlowReturn
 
925
gst_ass_render_chain_video (GstPad * pad, GstBuffer * buffer)
 
926
{
 
927
  GstAssRender *render = GST_ASS_RENDER (GST_PAD_PARENT (pad));
481
928
  GstFlowReturn ret = GST_FLOW_OK;
482
929
  gboolean in_seg = FALSE;
483
930
  gint64 start, stop, clip_start = 0, clip_stop = 0;
484
931
  ASS_Image *ass_image;
485
932
 
486
 
  render = GST_ASSRENDER (GST_PAD_PARENT (pad));
487
 
 
488
933
  if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
489
934
    GST_WARNING_OBJECT (render, "buffer without timestamp, discarding");
490
935
    gst_buffer_unref (buffer);
543
988
        gst_segment_to_running_time (&render->video_segment, GST_FORMAT_TIME,
544
989
        GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer));
545
990
 
546
 
    if (sub_running_time <= vid_running_time_end) {
547
 
      gst_assrender_process_text (render, render->subtitle_pending,
548
 
          sub_running_time, sub_running_time_end - sub_running_time);
549
 
      render->subtitle_pending = NULL;
550
 
      g_cond_signal (render->subtitle_cond);
551
 
    } else if (sub_running_time_end < vid_running_time) {
 
991
    if (sub_running_time_end < vid_running_time) {
552
992
      gst_buffer_unref (render->subtitle_pending);
553
993
      GST_DEBUG_OBJECT (render,
554
994
          "Too late text buffer, dropping (%" GST_TIME_FORMAT " < %"
556
996
          GST_TIME_ARGS (vid_running_time));
557
997
      render->subtitle_pending = NULL;
558
998
      g_cond_signal (render->subtitle_cond);
 
999
    } else if (sub_running_time <= vid_running_time_end + GST_SECOND / 2) {
 
1000
      gst_ass_render_process_text (render, render->subtitle_pending,
 
1001
          sub_running_time, sub_running_time_end - sub_running_time);
 
1002
      render->subtitle_pending = NULL;
 
1003
      g_cond_signal (render->subtitle_cond);
559
1004
    }
560
1005
  }
561
1006
  g_mutex_unlock (render->subtitle_mutex);
562
1007
 
563
1008
  /* now start rendering subtitles, if all conditions are met */
564
1009
  if (render->renderer_init_ok && render->track_init_ok && render->enable) {
565
 
    int counter;
566
1010
    GstClockTime running_time;
567
 
    double timestamp;
568
 
    double step;
 
1011
    gdouble timestamp;
 
1012
#ifndef GST_DISABLE_GST_DEBUG
 
1013
    gdouble step;
 
1014
#endif
569
1015
 
570
1016
    running_time =
571
1017
        gst_segment_to_running_time (&render->video_segment, GST_FORMAT_TIME,
576
1022
    /* libass needs timestamps in ms */
577
1023
    timestamp = running_time / GST_MSECOND;
578
1024
 
 
1025
    g_mutex_lock (render->ass_mutex);
 
1026
#ifndef GST_DISABLE_GST_DEBUG
579
1027
    /* only for testing right now. could possibly be used for optimizations? */
580
1028
    step = ass_step_sub (render->ass_track, timestamp, 1);
581
1029
    GST_DEBUG_OBJECT (render, "Current running time: %" GST_TIME_FORMAT
582
1030
        " // Next event: %" GST_TIME_FORMAT,
583
1031
        GST_TIME_ARGS (running_time), GST_TIME_ARGS (step * GST_MSECOND));
 
1032
#endif
584
1033
 
585
1034
    /* not sure what the last parameter to this call is for (detect_change) */
586
1035
    ass_image = ass_render_frame (render->ass_renderer, render->ass_track,
587
 
        timestamp, 0);
 
1036
        timestamp, NULL);
 
1037
    g_mutex_unlock (render->ass_mutex);
588
1038
 
589
 
    if (ass_image == NULL) {
 
1039
    if (ass_image != NULL) {
 
1040
      buffer = gst_buffer_make_writable (buffer);
 
1041
      render->blit (render, ass_image, buffer);
 
1042
    } else {
590
1043
      GST_LOG_OBJECT (render, "nothing to render right now");
591
 
      ret = gst_pad_push (render->srcpad, buffer);
592
 
      return ret;
593
 
    }
594
 
 
595
 
    counter = 0;
596
 
    while (ass_image) {
597
 
      /* blend subtitles onto the video frame */
598
 
      guint8 alpha = 255 - ((ass_image->color) & 0xff);
599
 
      guint8 r = ((ass_image->color) >> 24) & 0xff;
600
 
      guint8 g = ((ass_image->color) >> 16) & 0xff;
601
 
      guint8 b = ((ass_image->color) >> 8) & 0xff;
602
 
 
603
 
      guint8 *src = ass_image->bitmap;
604
 
      guint8 *dst =
605
 
          buffer->data + ass_image->dst_y * (render->width * 3) +
606
 
          ass_image->dst_x * 3;
607
 
 
608
 
      guint x = 0;
609
 
      guint y = 0;
610
 
 
611
 
      for (y = 0; y < ass_image->h; y++) {
612
 
        for (x = 0; x < ass_image->w; x++) {
613
 
          guint8 k = ((guint8) src[x]) * alpha / 255;
614
 
          dst[x * 3] = (k * r + (255 - k) * dst[x * 3]) / 255;
615
 
          dst[x * 3 + 1] = (k * g + (255 - k) * dst[x * 3 + 1]) / 255;
616
 
          dst[x * 3 + 2] = (k * b + (255 - k) * dst[x * 3 + 2]) / 255;
617
 
        }
618
 
        src += ass_image->stride;
619
 
        dst += render->width * 3;
620
 
      }
621
 
      counter++;
622
 
      ass_image = ass_image->next;
623
 
    }
624
 
    GST_LOG_OBJECT (render, "amount of rendered ass_image: %d", counter);
 
1044
    }
 
1045
  } else {
 
1046
    GST_LOG_OBJECT (render, "rendering disabled, doing buffer passthrough");
625
1047
  }
626
1048
 
627
1049
  ret = gst_pad_push (render->srcpad, buffer);
637
1059
}
638
1060
 
639
1061
static GstFlowReturn
640
 
gst_assrender_chain_text (GstPad * pad, GstBuffer * buffer)
 
1062
gst_ass_render_chain_text (GstPad * pad, GstBuffer * buffer)
641
1063
{
642
1064
  GstFlowReturn ret = GST_FLOW_OK;
643
 
  Gstassrender *render;
 
1065
  GstAssRender *render = GST_ASS_RENDER (GST_PAD_PARENT (pad));
644
1066
  GstClockTime timestamp, duration;
645
1067
  GstClockTime sub_running_time, vid_running_time;
646
1068
  GstClockTime sub_running_time_end;
647
 
 
648
 
  render = GST_ASSRENDER (GST_PAD_PARENT (pad));
649
 
 
650
 
  gst_segment_set_last_stop (&render->subtitle_segment, GST_FORMAT_TIME,
651
 
      GST_BUFFER_TIMESTAMP (buffer));
652
 
 
653
 
  if (render->subtitle_flushing)
 
1069
  gint64 cstart, cstop;
 
1070
  gboolean in_seg;
 
1071
 
 
1072
  if (render->subtitle_flushing) {
 
1073
    gst_buffer_unref (buffer);
654
1074
    return GST_FLOW_WRONG_STATE;
 
1075
  }
655
1076
 
656
1077
  timestamp = GST_BUFFER_TIMESTAMP (buffer);
657
1078
  duration = GST_BUFFER_DURATION (buffer);
664
1085
    return GST_FLOW_OK;
665
1086
  }
666
1087
 
 
1088
  in_seg =
 
1089
      gst_segment_clip (&render->subtitle_segment, GST_FORMAT_TIME, timestamp,
 
1090
      timestamp + duration, &cstart, &cstop);
 
1091
  if (!in_seg) {
 
1092
    GST_DEBUG_OBJECT (render,
 
1093
        "Text buffer before segment start (%" GST_TIME_FORMAT " < %"
 
1094
        GST_TIME_FORMAT ")", GST_TIME_ARGS (timestamp),
 
1095
        GST_TIME_ARGS (render->subtitle_segment.start));
 
1096
    gst_buffer_unref (buffer);
 
1097
    return GST_FLOW_OK;
 
1098
  }
 
1099
 
 
1100
  GST_BUFFER_TIMESTAMP (buffer) = timestamp = cstart;
 
1101
  GST_BUFFER_DURATION (buffer) = duration = cstop - cstart;
 
1102
 
 
1103
  gst_segment_set_last_stop (&render->subtitle_segment, GST_FORMAT_TIME,
 
1104
      GST_BUFFER_TIMESTAMP (buffer));
 
1105
 
667
1106
  sub_running_time =
668
1107
      gst_segment_to_running_time (&render->subtitle_segment, GST_FORMAT_TIME,
669
1108
      timestamp);
674
1113
      gst_segment_to_running_time (&render->video_segment, GST_FORMAT_TIME,
675
1114
      render->video_segment.last_stop);
676
1115
 
677
 
  if (sub_running_time > vid_running_time) {
 
1116
  if (render->fps_n && render->fps_d)
 
1117
    vid_running_time +=
 
1118
        gst_util_uint64_scale (GST_SECOND, render->fps_d, render->fps_n);
 
1119
 
 
1120
  if (sub_running_time > vid_running_time + GST_SECOND / 2) {
678
1121
    g_assert (render->subtitle_pending == NULL);
679
1122
    g_mutex_lock (render->subtitle_mutex);
680
1123
    if (G_UNLIKELY (render->subtitle_flushing)) {
698
1141
    gst_buffer_unref (buffer);
699
1142
    ret = GST_FLOW_OK;
700
1143
  } else {
701
 
    gst_assrender_process_text (render, buffer, sub_running_time,
 
1144
    gst_ass_render_process_text (render, buffer, sub_running_time,
702
1145
        sub_running_time_end - sub_running_time);
 
1146
    ret = GST_FLOW_OK;
703
1147
  }
704
1148
 
705
1149
  GST_DEBUG_OBJECT (render,
710
1154
  return ret;
711
1155
}
712
1156
 
 
1157
static void
 
1158
gst_ass_render_handle_tags (GstAssRender * render, GstTagList * taglist)
 
1159
{
 
1160
  static const gchar *mimetypes[] = {
 
1161
    "application/x-font-ttf",
 
1162
    "application/x-font-otf",
 
1163
    "application/x-truetype-font"
 
1164
  };
 
1165
  static const gchar *extensions[] = {
 
1166
    ".otf",
 
1167
    ".ttf"
 
1168
  };
 
1169
  guint tag_size;
 
1170
  guint index;
 
1171
 
 
1172
  if (!taglist)
 
1173
    return;
 
1174
 
 
1175
  tag_size = gst_tag_list_get_tag_size (taglist, GST_TAG_ATTACHMENT);
 
1176
  if (tag_size > 0 && render->embeddedfonts) {
 
1177
    const GValue *value;
 
1178
    GstBuffer *buf;
 
1179
    GstCaps *caps;
 
1180
    GstStructure *structure;
 
1181
    gboolean valid_mimetype, valid_extension;
 
1182
    guint j;
 
1183
    const gchar *filename;
 
1184
 
 
1185
    GST_DEBUG_OBJECT (render, "TAG event has attachments");
 
1186
 
 
1187
    for (index = 0; index < tag_size; index++) {
 
1188
      value = gst_tag_list_get_value_index (taglist, GST_TAG_ATTACHMENT, index);
 
1189
      buf = gst_value_get_buffer (value);
 
1190
      if (!buf || !GST_BUFFER_CAPS (buf))
 
1191
        continue;
 
1192
 
 
1193
      caps = GST_BUFFER_CAPS (buf);
 
1194
      structure = gst_caps_get_structure (caps, 0);
 
1195
 
 
1196
      valid_mimetype = FALSE;
 
1197
      valid_extension = FALSE;
 
1198
 
 
1199
      for (j = 0; j < G_N_ELEMENTS (mimetypes); j++) {
 
1200
        if (gst_structure_has_name (structure, mimetypes[j])) {
 
1201
          valid_mimetype = TRUE;
 
1202
          break;
 
1203
        }
 
1204
      }
 
1205
      filename = gst_structure_get_string (structure, "filename");
 
1206
      if (!filename)
 
1207
        continue;
 
1208
 
 
1209
      if (!valid_mimetype) {
 
1210
        guint len = strlen (filename);
 
1211
        const gchar *extension = filename + len - 4;
 
1212
        for (j = 0; j < G_N_ELEMENTS (extensions); j++) {
 
1213
          if (g_ascii_strcasecmp (extension, extensions[j]) == 0) {
 
1214
            valid_extension = TRUE;
 
1215
            break;
 
1216
          }
 
1217
        }
 
1218
      }
 
1219
 
 
1220
      if (valid_mimetype || valid_extension) {
 
1221
        g_mutex_lock (render->ass_mutex);
 
1222
        ass_add_font (render->ass_library, (gchar *) filename,
 
1223
            (gchar *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
 
1224
        GST_DEBUG_OBJECT (render, "registered new font %s", filename);
 
1225
        g_mutex_unlock (render->ass_mutex);
 
1226
      }
 
1227
    }
 
1228
  }
 
1229
}
 
1230
 
713
1231
static gboolean
714
 
gst_assrender_event_video (GstPad * pad, GstEvent * event)
 
1232
gst_ass_render_event_video (GstPad * pad, GstEvent * event)
715
1233
{
716
1234
  gboolean ret = FALSE;
717
 
  Gstassrender *render;
718
 
 
719
 
  render = GST_ASSRENDER (gst_pad_get_parent (pad));
 
1235
  GstAssRender *render = GST_ASS_RENDER (gst_pad_get_parent (pad));
720
1236
 
721
1237
  GST_DEBUG_OBJECT (pad, "received video event %s",
722
1238
      GST_EVENT_TYPE_NAME (event));
754
1270
    }
755
1271
    case GST_EVENT_TAG:
756
1272
    {
757
 
      GstTagList *taglist = gst_tag_list_new ();
758
 
      guint tag_size;
759
 
      guint index;
 
1273
      GstTagList *taglist = NULL;
760
1274
 
761
1275
      /* tag events may contain attachments which might be fonts */
762
1276
      GST_DEBUG_OBJECT (render, "got TAG event");
763
1277
 
764
1278
      gst_event_parse_tag (event, &taglist);
765
 
      tag_size = gst_tag_list_get_tag_size (taglist, GST_TAG_ATTACHMENT);
766
 
      if (tag_size > 0 && render->embeddedfonts) {
767
 
        const GValue *value;
768
 
        GstBuffer *buf;
769
 
        GstCaps *caps;
770
 
        GstStructure *structure;
771
 
 
772
 
        GST_DEBUG_OBJECT (render, "TAG event has attachments");
773
 
 
774
 
        for (index = 0; index < tag_size; index++) {
775
 
          value = gst_tag_list_get_value_index (taglist, GST_TAG_ATTACHMENT,
776
 
              index);
777
 
          buf = (GstBuffer *) gst_value_get_mini_object (value);
778
 
          if (!buf) {
779
 
            continue;
780
 
          }
781
 
          gst_buffer_ref (buf);
782
 
          caps = GST_BUFFER_CAPS (buf);
783
 
          structure = gst_caps_get_structure (caps, 0);
784
 
          if (gst_structure_has_name (structure, "application/x-truetype-font")
785
 
              && gst_structure_has_field (structure, "filename")) {
786
 
            const gchar *filename;
787
 
            filename = gst_structure_get_string (structure, "filename");
788
 
            ass_add_font (render->ass_library, (gchar *) filename,
789
 
                (gchar *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
790
 
            GST_DEBUG_OBJECT (render, "registered new font %s", filename);
791
 
          }
792
 
          gst_buffer_unref (buf);
793
 
        }
794
 
      }
795
 
      ret = gst_pad_event_default (pad, event);
 
1279
      gst_ass_render_handle_tags (render, taglist);
 
1280
      ret = gst_pad_push_event (render->srcpad, event);
796
1281
      break;
797
1282
    }
798
1283
    case GST_EVENT_FLUSH_STOP:
799
1284
      gst_segment_init (&render->video_segment, GST_FORMAT_TIME);
800
1285
    default:
801
 
      ret = gst_pad_event_default (pad, event);
 
1286
      ret = gst_pad_push_event (render->srcpad, event);
802
1287
      break;
803
1288
  }
804
1289
 
808
1293
}
809
1294
 
810
1295
static gboolean
811
 
gst_assrender_event_text (GstPad * pad, GstEvent * event)
 
1296
gst_ass_render_event_text (GstPad * pad, GstEvent * event)
812
1297
{
813
 
  int i;
 
1298
  gint i;
814
1299
  gboolean ret = FALSE;
815
 
  Gstassrender *render;
816
 
 
817
 
  render = GST_ASSRENDER (gst_pad_get_parent (pad));
 
1300
  GstAssRender *render = GST_ASS_RENDER (gst_pad_get_parent (pad));
818
1301
 
819
1302
  GST_DEBUG_OBJECT (pad, "received text event %s", GST_EVENT_TYPE_NAME (event));
820
1303
 
859
1342
      break;
860
1343
    case GST_EVENT_FLUSH_START:
861
1344
      GST_DEBUG_OBJECT (render, "begin flushing");
 
1345
      g_mutex_lock (render->ass_mutex);
862
1346
      if (render->ass_track) {
863
 
        GST_OBJECT_LOCK (render);
864
1347
        /* delete any events on the ass_track */
865
1348
        for (i = 0; i < render->ass_track->n_events; i++) {
866
1349
          GST_DEBUG_OBJECT (render, "deleted event with eid %i", i);
867
1350
          ass_free_event (render->ass_track, i);
868
1351
        }
869
1352
        render->ass_track->n_events = 0;
870
 
        GST_OBJECT_UNLOCK (render);
871
1353
        GST_DEBUG_OBJECT (render, "done flushing");
872
1354
      }
 
1355
      g_mutex_unlock (render->ass_mutex);
873
1356
      g_mutex_lock (render->subtitle_mutex);
874
1357
      if (render->subtitle_pending)
875
1358
        gst_buffer_unref (render->subtitle_pending);
889
1372
      break;
890
1373
    case GST_EVENT_TAG:
891
1374
    {
892
 
      GstTagList *taglist = gst_tag_list_new ();
893
 
      guint tag_size;
894
 
      guint index;
 
1375
      GstTagList *taglist = NULL;
895
1376
 
 
1377
      /* tag events may contain attachments which might be fonts */
896
1378
      GST_DEBUG_OBJECT (render, "got TAG event");
897
1379
 
898
1380
      gst_event_parse_tag (event, &taglist);
899
 
      tag_size = gst_tag_list_get_tag_size (taglist, GST_TAG_ATTACHMENT);
900
 
      if (tag_size > 0 && render->embeddedfonts) {
901
 
        const GValue *value;
902
 
        GstBuffer *buf;
903
 
        GstCaps *caps;
904
 
        GstStructure *structure;
905
 
 
906
 
        GST_DEBUG_OBJECT (render, "TAG event has attachments");
907
 
 
908
 
        for (index = 0; index < tag_size; index++) {
909
 
          value = gst_tag_list_get_value_index (taglist, GST_TAG_ATTACHMENT,
910
 
              index);
911
 
          buf = (GstBuffer *) gst_value_get_mini_object (value);
912
 
          if (!buf) {
913
 
            continue;
914
 
          }
915
 
          gst_buffer_ref (buf);
916
 
          caps = GST_BUFFER_CAPS (buf);
917
 
          structure = gst_caps_get_structure (caps, 0);
918
 
          if (gst_structure_has_name (structure, "application/x-truetype-font")
919
 
              && gst_structure_has_field (structure, "filename")) {
920
 
            const gchar *filename;
921
 
            filename = gst_structure_get_string (structure, "filename");
922
 
            ass_add_font (render->ass_library, (gchar *) filename,
923
 
                (gchar *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
924
 
            GST_DEBUG_OBJECT (render, "registered new font %s", filename);
925
 
          }
926
 
          gst_buffer_unref (buf);
927
 
        }
928
 
      }
929
 
      ret = gst_pad_event_default (pad, event);
 
1381
      gst_ass_render_handle_tags (render, taglist);
 
1382
      ret = gst_pad_push_event (render->srcpad, event);
930
1383
      break;
931
1384
    }
932
1385
    default:
933
 
      ret = gst_pad_event_default (pad, event);
 
1386
      ret = gst_pad_push_event (render->srcpad, event);
934
1387
      break;
935
1388
  }
936
1389
 
942
1395
static gboolean
943
1396
plugin_init (GstPlugin * plugin)
944
1397
{
945
 
  GST_DEBUG_CATEGORY_INIT (gst_assrender_debug, "assrender",
 
1398
  GST_DEBUG_CATEGORY_INIT (gst_ass_render_debug, "assrender",
946
1399
      0, "ASS/SSA subtitle renderer");
 
1400
  GST_DEBUG_CATEGORY_INIT (gst_ass_render_lib_debug, "assrender_library",
 
1401
      0, "ASS/SSA subtitle renderer library");
947
1402
 
948
1403
  return gst_element_register (plugin, "assrender",
949
 
      GST_RANK_PRIMARY, GST_TYPE_ASSRENDER);
 
1404
      GST_RANK_PRIMARY, GST_TYPE_ASS_RENDER);
950
1405
}
951
1406
 
952
1407
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,