add recipes for GStreamer release 1.6.0 core + plugins
[opendreambox.git] / meta-opendreambox / recipes-multimedia / gstreamer / gstreamer1.0-plugins-bad / dvdspu-overlay-composition.patch
1 diff -u a/gst/dvdspu/Makefile.am b/gst/dvdspu/Makefile.am
2 --- a/gst/dvdspu/Makefile.am    2015-09-01 11:05:50.348528393 +0200
3 +++ b/gst/dvdspu/Makefile.am    2015-09-28 16:30:05.862437231 +0200
4 @@ -1,7 +1,7 @@
5  
6  plugin_LTLIBRARIES = libgstdvdspu.la
7  
8 -libgstdvdspu_la_SOURCES = gstdvdspu.c gstdvdspu-render.c gstspu-vobsub.c gstspu-vobsub-render.c gstspu-pgs.c
9 +libgstdvdspu_la_SOURCES = gstdvdspu.c gstspu-vobsub.c gstspu-vobsub-render.c gstspu-pgs.c
10  
11  libgstdvdspu_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
12  libgstdvdspu_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_API_VERSION) \
13 diff -u a/gst/dvdspu/gstdvdspu.c b/gst/dvdspu/gstdvdspu.c
14 --- a/gst/dvdspu/gstdvdspu.c    2015-09-24 13:57:24.000000000 +0200
15 +++ b/gst/dvdspu/gstdvdspu.c    2015-09-28 16:35:11.004443667 +0200
16 @@ -34,6 +34,9 @@
17  
18  #include <gst/gst-i18n-plugin.h>
19  #include <gst/video/video.h>
20 +#include <gst/video/video-overlay-composition.h>
21 +#include <gst/video/gstvideometa.h>
22 +#include <gst/video/gstvideosink.h>
23  
24  #include <string.h>
25  
26 @@ -53,19 +56,25 @@
27    LAST_SIGNAL
28  };
29  
30 +#define VIDEO_FORMATS GST_VIDEO_OVERLAY_COMPOSITION_BLEND_FORMATS
31 +
32 +#define DVDSPU_CAPS GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS)
33 +#define DVDSPU_ALL_CAPS DVDSPU_CAPS ";" \
34 +    GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("ANY", GST_VIDEO_FORMATS_ALL)
35 +
36 +static GstStaticCaps sw_template_caps = GST_STATIC_CAPS (DVDSPU_CAPS);
37 +
38  static GstStaticPadTemplate video_sink_factory =
39  GST_STATIC_PAD_TEMPLATE ("video",
40      GST_PAD_SINK,
41      GST_PAD_ALWAYS,
42 -    GST_STATIC_CAPS ("video/x-raw, " "format = (string) { I420, NV12, YV12 }, "
43 -        "width = (int) [ 16, 4096 ], " "height = (int) [ 16, 4096 ]")
44 +    GST_STATIC_CAPS (DVDSPU_ALL_CAPS "; video/mpeg; video/msmpeg; video/x-h264; video/x-divx; video/x-xvid; video/x-wmv")
45      );
46  
47  static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
48      GST_PAD_SRC,
49      GST_PAD_ALWAYS,
50 -    GST_STATIC_CAPS ("video/x-raw, " "format = (string) { I420, NV12, YV12 }, "
51 -        "width = (int) [ 16, 4096 ], " "height = (int) [ 16, 4096 ]")
52 +    GST_STATIC_CAPS (DVDSPU_ALL_CAPS "; video/mpeg; video/msmpeg; video/x-h264; video/x-divx; video/x-xvid; video/x-wmv")
53      );
54  
55  static GstStaticPadTemplate subpic_sink_factory =
56 @@ -87,10 +96,13 @@
57      GstEvent * event);
58  static gboolean gst_dvd_spu_src_query (GstPad * pad, GstObject * parent,
59      GstQuery * query);
60 +static GstCaps *gst_dvd_spu_src_get_caps (GstDVDSpu * dvdspu, GstPad * pad,
61 +    GstCaps * filter);
62  
63 -static GstCaps *gst_dvd_spu_video_proxy_getcaps (GstPad * pad,
64 +static GstCaps *gst_dvd_spu_video_get_caps (GstDVDSpu * dvdspu, GstPad * pad,
65      GstCaps * filter);
66 -static gboolean gst_dvd_spu_video_set_caps (GstPad * pad, GstCaps * caps);
67 +static gboolean gst_dvd_spu_video_set_caps (GstDVDSpu * dvdspu, GstPad * pad,
68 +    GstCaps * caps);
69  static GstFlowReturn gst_dvd_spu_video_chain (GstPad * pad, GstObject * parent,
70      GstBuffer * buf);
71  static gboolean gst_dvd_spu_video_event (GstPad * pad, GstObject * parent,
72 @@ -104,7 +116,9 @@
73      GstBuffer * buf);
74  static gboolean gst_dvd_spu_subpic_event (GstPad * pad, GstObject * parent,
75      GstEvent * event);
76 -static gboolean gst_dvd_spu_subpic_set_caps (GstPad * pad, GstCaps * caps);
77 +static gboolean gst_dvd_spu_subpic_set_caps (GstDVDSpu * dvdspu, GstPad * pad,
78 +    GstCaps * caps);
79 +static gboolean gst_dvd_spu_negotiate (GstDVDSpu * dvdspu, GstCaps * caps);
80  
81  static void gst_dvd_spu_clear (GstDVDSpu * dvdspu);
82  static void gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu,
83 @@ -174,6 +188,15 @@
84  }
85  
86  static void
87 +gst_dvd_spu_reset_composition (GstDVDSpu * dvdspu)
88 +{
89 +  if (dvdspu->composition) {
90 +    gst_video_overlay_composition_unref (dvdspu->composition);
91 +    dvdspu->composition = NULL;
92 +  }
93 +}
94 +
95 +static void
96  gst_dvd_spu_clear (GstDVDSpu * dvdspu)
97  {
98    gst_dvd_spu_flush_spu_info (dvdspu, FALSE);
99 @@ -207,14 +230,7 @@
100  gst_dvd_spu_finalize (GObject * object)
101  {
102    GstDVDSpu *dvdspu = GST_DVD_SPU (object);
103 -  gint i;
104  
105 -  for (i = 0; i < 3; i++) {
106 -    if (dvdspu->spu_state.comp_bufs[i] != NULL) {
107 -      g_free (dvdspu->spu_state.comp_bufs[i]);
108 -      dvdspu->spu_state.comp_bufs[i] = NULL;
109 -    }
110 -  }
111    g_queue_free (dvdspu->pending_spus);
112    g_mutex_clear (&dvdspu->spu_lock);
113  
114 @@ -270,6 +286,8 @@
115      default:
116        break;
117    }
118 +
119 +  gst_dvd_spu_reset_composition (dvdspu);
120  }
121  
122  static gboolean
123 @@ -292,6 +310,7 @@
124  static gboolean
125  gst_dvd_spu_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
126  {
127 +  GstDVDSpu *dvdspu = GST_DVD_SPU (parent);
128    gboolean res = FALSE;
129  
130    switch (GST_QUERY_TYPE (query)) {
131 @@ -300,7 +319,7 @@
132        GstCaps *filter, *caps;
133  
134        gst_query_parse_caps (query, &filter);
135 -      caps = gst_dvd_spu_video_proxy_getcaps (pad, filter);
136 +      caps = gst_dvd_spu_src_get_caps (dvdspu, pad, filter);
137        gst_query_set_caps_result (query, caps);
138        gst_caps_unref (caps);
139        res = TRUE;
140 @@ -315,59 +334,238 @@
141  }
142  
143  static gboolean
144 -gst_dvd_spu_video_set_caps (GstPad * pad, GstCaps * caps)
145 +gst_dvd_spu_can_handle_caps (GstCaps * caps)
146 +{
147 +  GstCaps *sw_caps;
148 +  gboolean ret;
149 +
150 +  sw_caps = gst_static_caps_get (&sw_template_caps);
151 +  ret = gst_caps_is_subset (caps, sw_caps);
152 +  gst_caps_unref (sw_caps);
153 +
154 +  return ret;
155 +}
156 +
157 +static gboolean
158 +gst_dvd_spu_video_set_caps (GstDVDSpu * dvdspu, GstPad * pad, GstCaps * caps)
159  {
160 -  GstDVDSpu *dvdspu = GST_DVD_SPU (gst_pad_get_parent (pad));
161 -  gboolean res = FALSE;
162    GstVideoInfo info;
163 -  gint i;
164 -  SpuState *state;
165 +  gboolean ret = FALSE;
166  
167    if (!gst_video_info_from_caps (&info, caps))
168 -    goto done;
169 +    goto invalid_caps;
170  
171 -  DVD_SPU_LOCK (dvdspu);
172 +  dvdspu->spu_state.info = info;
173  
174 -  state = &dvdspu->spu_state;
175 +  ret = gst_dvd_spu_negotiate (dvdspu, caps);
176  
177 -  state->info = info;
178 -  for (i = 0; i < 3; i++) {
179 -    state->comp_bufs[i] = g_realloc (state->comp_bufs[i],
180 -        sizeof (guint32) * info.width);
181 +  DVD_SPU_LOCK (dvdspu);
182 +  if (!dvdspu->attach_compo_to_buffer && !gst_dvd_spu_can_handle_caps (caps)) {
183 +    GST_DEBUG_OBJECT (dvdspu, "unsupported caps %" GST_PTR_FORMAT, caps);
184 +    ret = FALSE;
185    }
186 +
187    DVD_SPU_UNLOCK (dvdspu);
188  
189 -  res = TRUE;
190 -done:
191 -  gst_object_unref (dvdspu);
192 -  return res;
193 +  return ret;
194 +
195 +  /* ERRORS */
196 +invalid_caps:
197 +  {
198 +    GST_DEBUG_OBJECT (dvdspu, "could not parse caps");
199 +    return FALSE;
200 +  }
201 +}
202 +
203 +
204 +/**
205 + * gst_dvd_spu_add_feature_and_intersect:
206 + *
207 + * Creates a new #GstCaps containing the (given caps +
208 + * given caps feature) + (given caps intersected by the
209 + * given filter).
210 + *
211 + * Returns: the new #GstCaps
212 + */
213 +static GstCaps *
214 +gst_dvd_spu_add_feature_and_intersect (GstCaps * caps,
215 +    const gchar * feature, GstCaps * filter)
216 +{
217 +  int i, caps_size;
218 +  GstCaps *new_caps;
219 +
220 +  new_caps = gst_caps_copy (caps);
221 +
222 +  caps_size = gst_caps_get_size (new_caps);
223 +  for (i = 0; i < caps_size; i++) {
224 +    GstCapsFeatures *features = gst_caps_get_features (new_caps, i);
225 +    if (!gst_caps_features_is_any (features)) {
226 +      gst_caps_features_add (features, feature);
227 +    }
228 +  }
229 +
230 +  gst_caps_append (new_caps, gst_caps_intersect_full (caps,
231 +          filter, GST_CAPS_INTERSECT_FIRST));
232 +
233 +  return new_caps;
234  }
235  
236 +/**
237 + * gst_dvd_spu_intersect_by_feature:
238 + *
239 + * Creates a new #GstCaps based on the following filtering rule.
240 + *
241 + * For each individual caps contained in given caps, if the
242 + * caps uses the given caps feature, keep a version of the caps
243 + * with the feature and an another one without. Otherwise, intersect
244 + * the caps with the given filter.
245 + *
246 + * Returns: the new #GstCaps
247 + */
248  static GstCaps *
249 -gst_dvd_spu_video_proxy_getcaps (GstPad * pad, GstCaps * filter)
250 +gst_dvd_spu_intersect_by_feature (GstCaps * caps,
251 +    const gchar * feature, GstCaps * filter)
252  {
253 -  GstDVDSpu *dvdspu = GST_DVD_SPU (gst_pad_get_parent (pad));
254 -  GstCaps *caps;
255 -  GstPad *otherpad;
256 -
257 -  /* Proxy the getcaps between videosink and the srcpad, ignoring the 
258 -   * subpicture sink pad */
259 -  otherpad = (pad == dvdspu->srcpad) ? dvdspu->videosinkpad : dvdspu->srcpad;
260 -
261 -  caps = gst_pad_peer_query_caps (otherpad, filter);
262 -  if (caps) {
263 -    GstCaps *temp, *templ;
264 -
265 -    templ = gst_pad_get_pad_template_caps (otherpad);
266 -    temp = gst_caps_intersect (caps, templ);
267 -    gst_caps_unref (templ);
268 +  int i, caps_size;
269 +  GstCaps *new_caps;
270 +
271 +  new_caps = gst_caps_new_empty ();
272 +
273 +  caps_size = gst_caps_get_size (caps);
274 +  for (i = 0; i < caps_size; i++) {
275 +    GstStructure *caps_structure = gst_caps_get_structure (caps, i);
276 +    GstCapsFeatures *caps_features =
277 +        gst_caps_features_copy (gst_caps_get_features (caps, i));
278 +    GstCaps *filtered_caps;
279 +    GstCaps *simple_caps =
280 +        gst_caps_new_full (gst_structure_copy (caps_structure), NULL);
281 +    gst_caps_set_features (simple_caps, 0, caps_features);
282 +
283 +    if (gst_caps_features_contains (caps_features, feature)) {
284 +      gst_caps_append (new_caps, gst_caps_copy (simple_caps));
285 +
286 +      gst_caps_features_remove (caps_features, feature);
287 +      filtered_caps = gst_caps_ref (simple_caps);
288 +    } else {
289 +      filtered_caps = gst_caps_intersect_full (simple_caps, filter,
290 +          GST_CAPS_INTERSECT_FIRST);
291 +    }
292 +
293 +    gst_caps_unref (simple_caps);
294 +    gst_caps_append (new_caps, filtered_caps);
295 +  }
296 +
297 +  return new_caps;
298 +}
299 +
300 +static GstCaps *
301 +gst_dvd_spu_video_get_caps (GstDVDSpu * dvdspu, GstPad * pad, GstCaps * filter)
302 +{
303 +  GstPad *srcpad = dvdspu->srcpad;
304 +  GstCaps *peer_caps = NULL, *caps = NULL, *dvdspu_filter = NULL;
305 +
306 +  if (filter) {
307 +    /* filter caps + composition feature + filter caps
308 +     * filtered by the software caps. */
309 +    GstCaps *sw_caps = gst_static_caps_get (&sw_template_caps);
310 +    dvdspu_filter = gst_dvd_spu_add_feature_and_intersect (filter,
311 +        GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps);
312 +    gst_caps_unref (sw_caps);
313 +
314 +    GST_DEBUG_OBJECT (dvdspu, "dvdspu filter %" GST_PTR_FORMAT, dvdspu_filter);
315 +  }
316 +
317 +  peer_caps = gst_pad_peer_query_caps (srcpad, dvdspu_filter);
318 +
319 +  if (dvdspu_filter)
320 +    gst_caps_unref (dvdspu_filter);
321 +
322 +  if (peer_caps) {
323 +    GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, peer_caps);
324 +
325 +    if (gst_caps_is_any (peer_caps)) {
326 +      /* if peer returns ANY caps, return filtered src pad template caps */
327 +      caps = gst_caps_copy (gst_pad_get_pad_template_caps (srcpad));
328 +    } else {
329 +      /* duplicate caps which contains the composition into one version with
330 +       * the meta and one without. Filter the other caps by the software caps */
331 +      GstCaps *sw_caps = gst_static_caps_get (&sw_template_caps);
332 +      caps = gst_dvd_spu_intersect_by_feature (peer_caps,
333 +          GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps);
334 +      gst_caps_unref (sw_caps);
335 +    }
336 +
337 +    gst_caps_unref (peer_caps);
338 +
339 +  } else {
340 +    /* no peer, our padtemplate is enough then */
341 +    caps = gst_pad_get_pad_template_caps (pad);
342 +  }
343 +
344 +  if (filter) {
345 +    GstCaps *intersection = gst_caps_intersect_full (filter, caps,
346 +        GST_CAPS_INTERSECT_FIRST);
347      gst_caps_unref (caps);
348 -    caps = temp;
349 +    caps = intersection;
350 +  }
351 +
352 +  GST_DEBUG_OBJECT (dvdspu, "returning %" GST_PTR_FORMAT, caps);
353 +
354 +  return caps;
355 +}
356 +
357 +static GstCaps *
358 +gst_dvd_spu_src_get_caps (GstDVDSpu * dvdspu, GstPad * pad, GstCaps * filter)
359 +{
360 +  GstPad *sinkpad = dvdspu->videosinkpad;
361 +  GstCaps *peer_caps = NULL, *caps = NULL, *dvdspu_filter = NULL;
362 +
363 +  if (filter) {
364 +    /* duplicate filter caps which contains the composition into one version
365 +     * with the meta and one without. Filter the other caps by the software
366 +     * caps */
367 +    GstCaps *sw_caps = gst_static_caps_get (&sw_template_caps);
368 +    dvdspu_filter = gst_dvd_spu_intersect_by_feature (filter,
369 +        GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps);
370 +    gst_caps_unref (sw_caps);
371 +  }
372 +
373 +  peer_caps = gst_pad_peer_query_caps (sinkpad, dvdspu_filter);
374 +
375 +  if (dvdspu_filter)
376 +    gst_caps_unref (dvdspu_filter);
377 +
378 +  if (peer_caps) {
379 +    GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, peer_caps);
380 +
381 +    if (gst_caps_is_any (peer_caps)) {
382 +      /* if peer returns ANY caps, return filtered sink pad template caps */
383 +      caps = gst_caps_copy (gst_pad_get_pad_template_caps (sinkpad));
384 +    } else {
385 +      /* return upstream caps + composition feature + upstream caps
386 +       * filtered by the software caps. */
387 +      GstCaps *sw_caps = gst_static_caps_get (&sw_template_caps);
388 +      caps = gst_dvd_spu_add_feature_and_intersect (peer_caps,
389 +          GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, sw_caps);
390 +      gst_caps_unref (sw_caps);
391 +    }
392 +
393 +    gst_caps_unref (peer_caps);
394 +
395    } else {
396 +    /* no peer, our padtemplate is enough then */
397      caps = gst_pad_get_pad_template_caps (pad);
398    }
399  
400 -  gst_object_unref (dvdspu);
401 +  if (filter) {
402 +    GstCaps *intersection = gst_caps_intersect_full (filter, caps,
403 +        GST_CAPS_INTERSECT_FIRST);
404 +    gst_caps_unref (caps);
405 +    caps = intersection;
406 +  }
407 +
408 +  GST_DEBUG_OBJECT (dvdspu, "returning %" GST_PTR_FORMAT, caps);
409 +
410    return caps;
411  }
412  
413 @@ -405,7 +603,7 @@
414        GstCaps *caps;
415  
416        gst_event_parse_caps (event, &caps);
417 -      res = gst_dvd_spu_video_set_caps (pad, caps);
418 +      res = gst_dvd_spu_video_set_caps (dvdspu, pad, caps);
419        if (res)
420          res = gst_pad_push_event (dvdspu->srcpad, event);
421        else
422 @@ -496,12 +694,16 @@
423        break;
424      }
425      case GST_EVENT_FLUSH_START:
426 +      DVD_SPU_LOCK (dvdspu);
427 +      dvdspu->video_flushing = TRUE;
428 +      DVD_SPU_UNLOCK (dvdspu);
429        res = gst_pad_event_default (pad, parent, event);
430        goto done;
431      case GST_EVENT_FLUSH_STOP:
432        res = gst_pad_event_default (pad, parent, event);
433  
434        DVD_SPU_LOCK (dvdspu);
435 +      dvdspu->video_flushing = FALSE;
436        gst_segment_init (&dvdspu->video_seg, GST_FORMAT_UNDEFINED);
437        gst_buffer_replace (&dvdspu->ref_frame, NULL);
438        gst_buffer_replace (&dvdspu->pending_frame, NULL);
439 @@ -525,6 +727,7 @@
440  static gboolean
441  gst_dvd_spu_video_query (GstPad * pad, GstObject * parent, GstQuery * query)
442  {
443 +  GstDVDSpu *dvdspu = GST_DVD_SPU (parent);
444    gboolean res = FALSE;
445  
446    switch (GST_QUERY_TYPE (query)) {
447 @@ -533,7 +736,7 @@
448        GstCaps *filter, *caps;
449  
450        gst_query_parse_caps (query, &filter);
451 -      caps = gst_dvd_spu_video_proxy_getcaps (pad, filter);
452 +      caps = gst_dvd_spu_video_get_caps (dvdspu, pad, filter);
453        gst_query_set_caps_result (query, caps);
454        gst_caps_unref (caps);
455        res = TRUE;
456 @@ -555,6 +758,9 @@
457  
458    g_return_val_if_fail (dvdspu != NULL, GST_FLOW_ERROR);
459  
460 +  if (gst_pad_check_reconfigure (dvdspu->srcpad))
461 +    gst_dvd_spu_negotiate (dvdspu, NULL);
462 +
463    GST_LOG_OBJECT (dvdspu, "video buffer %p with TS %" GST_TIME_FORMAT,
464        buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
465  
466 @@ -659,13 +865,104 @@
467    return GST_FLOW_OK;
468  }
469  
470 +static gboolean
471 +gstspu_fit_overlay_rectangle (GstDVDSpu * dvdspu, GstVideoRectangle * rect,
472 +    gint spu_width, gint spu_height)
473 +{
474 +  gint video_width = GST_VIDEO_INFO_WIDTH (&dvdspu->spu_state.info);
475 +  gint video_height = GST_VIDEO_INFO_HEIGHT (&dvdspu->spu_state.info);
476 +  GstVideoRectangle r;
477 +
478 +  r = *rect;
479  
480 -static void
481 -gstspu_render (GstDVDSpu * dvdspu, GstBuffer * buf)
482 +  if (spu_width != video_width || spu_height != video_height) {
483 +    gdouble hscale, vscale;
484 +
485 +    hscale = (gdouble) video_width / (gdouble) spu_width;
486 +    vscale = (gdouble) video_height / (gdouble) spu_height;
487 +
488 +    r.x *= hscale;
489 +    r.y *= vscale;
490 +    r.w *= hscale;
491 +    r.h *= vscale;
492 +  }
493 +
494 +  if (r.x + r.w > video_width)
495 +    r.x = video_width - r.w;
496 +
497 +  if (r.x < 0) {
498 +    r.x = 0;
499 +    if (r.w > video_width)
500 +      r.w = video_width;
501 +  }
502 +
503 +  if (r.y + r.h > video_height)
504 +    r.y = video_height - r.h;
505 +
506 +  if (r.y < 0) {
507 +    r.y = 0;
508 +    if (r.h > video_height)
509 +      r.h = video_height;
510 +  }
511 +
512 +  if (r.x != rect->x || r.y != rect->y || r.w != rect->w || r.h != rect->h) {
513 +    *rect = r;
514 +    return TRUE;
515 +  }
516 +
517 +  return FALSE;
518 +}
519 +
520 +static GstVideoOverlayComposition *
521 +gstspu_render_composition (GstDVDSpu * dvdspu)
522  {
523 +  GstBuffer *buffer;
524 +  GstVideoInfo overlay_info;
525 +  GstVideoFormat format;
526    GstVideoFrame frame;
527 +  GstVideoOverlayRectangle *rectangle;
528 +  GstVideoOverlayComposition *composition;
529 +  GstVideoRectangle win;
530 +  gint spu_w, spu_h;
531 +  gsize size;
532 +
533 +  format = GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB;
534 +
535 +  switch (dvdspu->spu_input_type) {
536 +    case SPU_INPUT_TYPE_PGS:
537 +      gstspu_pgs_get_render_geometry (dvdspu, &spu_w, &spu_h, &win);
538 +      break;
539 +    case SPU_INPUT_TYPE_VOBSUB:
540 +      gstspu_vobsub_get_render_geometry (dvdspu, &spu_w, &spu_h, &win);
541 +      break;
542 +    default:
543 +      return NULL;
544 +  }
545 +
546 +  if (win.w <= 0 || win.h <= 0 || spu_w <= 0 || spu_h <= 0) {
547 +    GST_DEBUG_OBJECT (dvdspu, "skip render of empty window");
548 +    return NULL;
549 +  }
550 +
551 +  gst_video_info_init (&overlay_info);
552 +  gst_video_info_set_format (&overlay_info, format, win.w, win.h);
553 +  size = GST_VIDEO_INFO_SIZE (&overlay_info);
554 +
555 +  buffer = gst_buffer_new_and_alloc (size);
556 +  if (!buffer) {
557 +    GST_WARNING_OBJECT (dvdspu, "failed to allocate overlay buffer");
558 +    return NULL;
559 +  }
560  
561 -  gst_video_frame_map (&frame, &dvdspu->spu_state.info, buf, GST_MAP_READWRITE);
562 +  gst_buffer_add_video_meta (buffer, GST_VIDEO_FRAME_FLAG_NONE,
563 +      format, win.w, win.h);
564 +
565 +  if (!gst_video_frame_map (&frame, &overlay_info, buffer, GST_MAP_READWRITE))
566 +    goto map_failed;
567 +
568 +  memset (GST_VIDEO_FRAME_PLANE_DATA (&frame, 0), 0,
569 +      GST_VIDEO_FRAME_PLANE_STRIDE (&frame, 0) *
570 +      GST_VIDEO_FRAME_HEIGHT (&frame));
571  
572    switch (dvdspu->spu_input_type) {
573      case SPU_INPUT_TYPE_VOBSUB:
574 @@ -677,6 +974,61 @@
575      default:
576        break;
577    }
578 +
579 +  gst_video_frame_unmap (&frame);
580 +
581 +  GST_DEBUG_OBJECT (dvdspu, "Overlay rendered for video size %dx%d, "
582 +      "spu display size %dx%d, window geometry %dx%d+%d%+d",
583 +      GST_VIDEO_INFO_WIDTH (&dvdspu->spu_state.info),
584 +      GST_VIDEO_INFO_HEIGHT (&dvdspu->spu_state.info),
585 +      spu_w, spu_h, win.w, win.h, win.x, win.y);
586 +
587 +  if (gstspu_fit_overlay_rectangle (dvdspu, &win, spu_w, spu_h))
588 +    GST_DEBUG_OBJECT (dvdspu, "Adjusted window to fit video: %dx%d%+d%+d",
589 +        win.w, win.h, win.x, win.y);
590 +
591 +  rectangle = gst_video_overlay_rectangle_new_raw (buffer, win.x, win.y,
592 +      win.w, win.h, GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA);
593 +
594 +  gst_buffer_unref (buffer);
595 +
596 +  composition = gst_video_overlay_composition_new (rectangle);
597 +  gst_video_overlay_rectangle_unref (rectangle);
598 +
599 +  return composition;
600 +
601 +map_failed:
602 +  GST_ERROR_OBJECT (dvdspu, "failed to map buffer");
603 +  gst_buffer_unref (buffer);
604 +  return NULL;
605 +}
606 +
607 +static void
608 +gstspu_render (GstDVDSpu * dvdspu, GstBuffer * buf)
609 +{
610 +  GstVideoOverlayComposition *composition;
611 +  GstVideoFrame frame;
612 +
613 +  if (!dvdspu->composition) {
614 +    dvdspu->composition = gstspu_render_composition (dvdspu);
615 +    if (!dvdspu->composition)
616 +      return;
617 +  }
618 +
619 +  composition = dvdspu->composition;
620 +
621 +  if (dvdspu->attach_compo_to_buffer) {
622 +    gst_buffer_add_video_overlay_composition_meta (buf, composition);
623 +    return;
624 +  }
625 +
626 +  if (!gst_video_frame_map (&frame, &dvdspu->spu_state.info, buf,
627 +          GST_MAP_READWRITE)) {
628 +    GST_WARNING_OBJECT (dvdspu, "failed to map video frame for blending");
629 +    return;
630 +  }
631 +
632 +  gst_video_overlay_composition_blend (composition, &frame);
633    gst_video_frame_unmap (&frame);
634  }
635  
636 @@ -746,6 +1098,9 @@
637        break;
638    }
639  
640 +  if (hl_change)
641 +    gst_dvd_spu_reset_composition (dvdspu);
642 +
643    if (hl_change && (dvdspu->spu_state.flags & SPU_STATE_STILL_FRAME)) {
644      gst_dvd_spu_redraw_still (dvdspu, FALSE);
645    }
646 @@ -799,6 +1154,8 @@
647            GST_TIME_ARGS (dvdspu->video_seg.position),
648            packet->buf ? "buffer" : "event");
649  
650 +      gst_dvd_spu_reset_composition (dvdspu);
651 +
652        if (packet->buf) {
653          switch (dvdspu->spu_input_type) {
654            case SPU_INPUT_TYPE_VOBSUB:
655 @@ -903,6 +1260,137 @@
656    }
657  }
658  
659 +static gboolean
660 +gst_dvd_spu_negotiate (GstDVDSpu * dvdspu, GstCaps * caps)
661 +{
662 +  gboolean upstream_has_meta = FALSE;
663 +  gboolean caps_has_meta = FALSE;
664 +  gboolean alloc_has_meta = FALSE;
665 +  gboolean attach = FALSE;
666 +  gboolean ret = TRUE;
667 +  GstCapsFeatures *f;
668 +  GstCaps *overlay_caps;
669 +  GstQuery *query;
670 +
671 +  GST_DEBUG_OBJECT (dvdspu, "performing negotiation");
672 +
673 +  /* Clear the cached composition */
674 +  gst_dvd_spu_reset_composition (dvdspu);
675 +
676 +  /* Clear any pending reconfigure to avoid negotiating twice */
677 +  gst_pad_check_reconfigure (dvdspu->srcpad);
678 +
679 +  if (!caps)
680 +    caps = gst_pad_get_current_caps (dvdspu->videosinkpad);
681 +  else
682 +    gst_caps_ref (caps);
683 +
684 +  if (!caps || gst_caps_is_empty (caps))
685 +    goto no_format;
686 +
687 +  /* Check if upstream caps have meta */
688 +  if ((f = gst_caps_get_features (caps, 0))) {
689 +    upstream_has_meta = gst_caps_features_contains (f,
690 +        GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
691 +  }
692 +
693 +  if (upstream_has_meta) {
694 +    overlay_caps = gst_caps_ref (caps);
695 +  } else {
696 +    GstCaps *peercaps;
697 +
698 +    /* BaseTransform requires caps for the allocation query to work */
699 +    overlay_caps = gst_caps_copy (caps);
700 +    f = gst_caps_get_features (overlay_caps, 0);
701 +    gst_caps_features_add (f,
702 +        GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION);
703 +
704 +    /* Then check if downstream accept dvdspu composition in caps */
705 +    /* FIXME: We should probably check if downstream *prefers* the
706 +     * dvdspu meta, and only enforce usage of it if we can't handle
707 +     * the format ourselves and thus would have to drop the overlays.
708 +     * Otherwise we should prefer what downstream wants here.
709 +     */
710 +    peercaps = gst_pad_peer_query_caps (dvdspu->srcpad, NULL);
711 +    caps_has_meta = gst_caps_can_intersect (peercaps, overlay_caps);
712 +    gst_caps_unref (peercaps);
713 +
714 +    GST_DEBUG ("caps have dvdspu meta %d", caps_has_meta);
715 +  }
716 +
717 +  if (upstream_has_meta || caps_has_meta) {
718 +    /* Send caps immediatly, it's needed by GstBaseTransform to get a reply
719 +     * from allocation query */
720 +    ret = gst_pad_set_caps (dvdspu->srcpad, overlay_caps);
721 +
722 +    /* First check if the allocation meta has compositon */
723 +    query = gst_query_new_allocation (overlay_caps, FALSE);
724 +
725 +    if (!gst_pad_peer_query (dvdspu->srcpad, query)) {
726 +      /* no problem, we use the query defaults */
727 +      GST_DEBUG_OBJECT (dvdspu, "ALLOCATION query failed");
728 +
729 +      /* In case we were flushing, mark reconfigure and fail this method,
730 +       * will make it retry */
731 +      if (dvdspu->video_flushing)
732 +        ret = FALSE;
733 +    }
734 +
735 +    alloc_has_meta = gst_query_find_allocation_meta (query,
736 +        GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
737 +
738 +    GST_DEBUG ("sink alloc has dvdspu meta %d", alloc_has_meta);
739 +
740 +    gst_query_unref (query);
741 +  }
742 +
743 +  /* For backward compatbility, we will prefer bliting if downstream
744 +   * allocation does not support the meta. In other case we will prefer
745 +   * attaching, and will fail the negotiation in the unlikely case we are
746 +   * force to blit, but format isn't supported. */
747 +
748 +  if (upstream_has_meta) {
749 +    attach = TRUE;
750 +  } else if (caps_has_meta) {
751 +    if (alloc_has_meta) {
752 +      attach = TRUE;
753 +    } else {
754 +      /* Don't attach unless we cannot handle the format */
755 +      attach = !gst_dvd_spu_can_handle_caps (caps);
756 +    }
757 +  } else {
758 +    ret = gst_dvd_spu_can_handle_caps (caps);
759 +  }
760 +
761 +  /* If we attach, then pick the dvdspu caps */
762 +  if (attach) {
763 +    GST_DEBUG_OBJECT (dvdspu, "Using caps %" GST_PTR_FORMAT, overlay_caps);
764 +    /* Caps where already sent */
765 +  } else if (ret) {
766 +    GST_DEBUG_OBJECT (dvdspu, "Using caps %" GST_PTR_FORMAT, caps);
767 +    ret = gst_pad_set_caps (dvdspu->srcpad, caps);
768 +  }
769 +
770 +  dvdspu->attach_compo_to_buffer = attach;
771 +
772 +  if (!ret) {
773 +    GST_DEBUG_OBJECT (dvdspu, "negotiation failed, schedule reconfigure");
774 +    gst_pad_mark_reconfigure (dvdspu->srcpad);
775 +  }
776 +
777 +  gst_caps_unref (overlay_caps);
778 +  gst_caps_unref (caps);
779 +
780 +  return ret;
781 +
782 +no_format:
783 +  {
784 +    if (caps)
785 +      gst_caps_unref (caps);
786 +    return FALSE;
787 +  }
788 +}
789 +
790  static GstFlowReturn
791  gst_dvd_spu_subpic_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
792  {
793 @@ -1057,7 +1545,7 @@
794        GstCaps *caps;
795  
796        gst_event_parse_caps (event, &caps);
797 -      res = gst_dvd_spu_subpic_set_caps (pad, caps);
798 +      res = gst_dvd_spu_subpic_set_caps (dvdspu, pad, caps);
799        gst_event_unref (event);
800        break;
801      }
802 @@ -1066,9 +1554,10 @@
803      case GST_EVENT_CUSTOM_DOWNSTREAM_OOB:
804      {
805        const GstStructure *structure = gst_event_get_structure (event);
806 +      const gchar *name = gst_structure_get_name (structure);
807        gboolean need_push;
808  
809 -      if (!gst_structure_has_name (structure, "application/x-gst-dvd")) {
810 +      if (!g_str_has_prefix (name, "application/x-gst-dvd")) {
811          res = gst_pad_event_default (pad, parent, event);
812          break;
813        }
814 @@ -1184,9 +1673,8 @@
815  }
816  
817  static gboolean
818 -gst_dvd_spu_subpic_set_caps (GstPad * pad, GstCaps * caps)
819 +gst_dvd_spu_subpic_set_caps (GstDVDSpu * dvdspu, GstPad * pad, GstCaps * caps)
820  {
821 -  GstDVDSpu *dvdspu = GST_DVD_SPU (gst_pad_get_parent (pad));
822    gboolean res = FALSE;
823    GstStructure *s;
824    SpuInputType input_type;
825 @@ -1212,7 +1700,6 @@
826    DVD_SPU_UNLOCK (dvdspu);
827    res = TRUE;
828  done:
829 -  gst_object_unref (dvdspu);
830    return res;
831  }
832  
833 diff -u a/gst/dvdspu/gstdvdspu.h b/gst/dvdspu/gstdvdspu.h
834 --- a/gst/dvdspu/gstdvdspu.h    2015-09-24 13:57:24.272613939 +0200
835 +++ b/gst/dvdspu/gstdvdspu.h    2015-09-28 16:32:19.551442789 +0200
836 @@ -71,10 +71,6 @@
837  
838    GstVideoInfo info;
839  
840 -  guint32 *comp_bufs[3]; /* Compositing buffers for U+V & A */
841 -  guint16 comp_left;
842 -  guint16 comp_right;
843 -
844    SpuVobsubState vobsub;
845    SpuPgsState pgs;
846  };
847 @@ -98,6 +94,7 @@
848    /* Mutex to protect state we access from different chain funcs */
849    GMutex spu_lock;
850  
851 +  gboolean video_flushing;
852    GstSegment video_seg;
853    GstSegment subp_seg;
854  
855 @@ -116,6 +113,10 @@
856  
857    /* Buffer to push after handling a DVD event, if any */
858    GstBuffer *pending_frame;
859 +
860 +  /* Overlay composition */
861 +  gboolean attach_compo_to_buffer;
862 +  GstVideoOverlayComposition *composition;
863  };
864  
865  struct _GstDVDSpuClass {
866 diff -u a/gst/dvdspu/gstspu-common.h b/gst/dvdspu/gstspu-common.h
867 --- a/gst/dvdspu/gstspu-common.h        2015-03-20 11:50:14.000000000 +0100
868 +++ b/gst/dvdspu/gstspu-common.h        2015-09-28 16:30:56.646658928 +0200
869 @@ -39,19 +39,14 @@
870    gint16 bottom;
871  };
872  
873 -/* Store a pre-multiplied colour value. The YUV fields hold the YUV values
874 - * multiplied by the 8-bit alpha, to save computing it while rendering */
875 +/* Store a pre-multiplied ARGB colour value */
876  struct SpuColour {
877 -  guint16 Y;
878 -  guint16 U;
879 -  guint16 V;
880 +  guint8 B;
881 +  guint8 G;
882 +  guint8 R;
883    guint8 A;
884  };
885  
886 -void gstspu_clear_comp_buffers (SpuState * state);
887 -void gstspu_blend_comp_buffers (SpuState * state, guint8 * planes[3]);
888 -
889 -
890  G_END_DECLS
891  
892  #endif /* __GSTSPU_COMMON_H__ */
893 diff -u a/gst/dvdspu/gstspu-pgs.c b/gst/dvdspu/gstspu-pgs.c
894 --- a/gst/dvdspu/gstspu-pgs.c   2015-09-28 16:34:05.253292679 +0200
895 +++ b/gst/dvdspu/gstspu-pgs.c   2015-09-28 16:30:56.646658928 +0200
896 @@ -81,7 +81,8 @@
897  static void
898  dump_rle_data (GstDVDSpu * dvdspu, guint8 * data, guint32 len)
899  {
900 -  guint16 obj_h G_GNUC_UNUSED;
901 +#if DUMP_FULL_IMAGE
902 +  guint16 obj_h;
903    guint16 obj_w;
904    guint8 *end = data + len;
905    guint x = 0;
906 @@ -141,7 +142,6 @@
907        }
908      }
909  
910 -#if DUMP_FULL_IMAGE
911      {
912        gint i;
913  #if 1
914 @@ -159,7 +159,6 @@
915        PGS_DUMP ("Run x: %d pix: %d col: %d\n", x, run_len, pal_id);
916  #endif
917      }
918 -#endif
919  
920      x += run_len;
921      if (!run_len || x > obj_w)
922 @@ -167,19 +166,24 @@
923    };
924  
925    PGS_DUMP ("\n");
926 +#endif
927  }
928  
929  static void
930  pgs_composition_object_render (PgsCompositionObject * obj, SpuState * state,
931 -    GstVideoFrame * frame)
932 +    GstVideoFrame * window)
933  {
934    SpuColour *colour;
935 -  guint8 *planes[3];            /* YUV frame pointers */
936 -  gint strides[3];
937 +  guint8 *pixels, *p;
938 +  gint stride;
939 +  gint win_w;
940 +  gint win_h;
941    guint8 *data, *end;
942 -  guint16 obj_w;
943 -  guint16 obj_h G_GNUC_UNUSED;
944 -  guint x, y, i, min_x, max_x;
945 +  guint16 obj_w, obj_h;
946 +  gint obj_x, obj_y;
947 +  gint min_x, max_x;
948 +  gint min_y, max_y;
949 +  gint x, y, i;
950  
951    if (G_UNLIKELY (obj->rle_data == NULL || obj->rle_data_size == 0
952            || obj->rle_data_used != obj->rle_data_size))
953 @@ -191,37 +195,47 @@
954    if (data + 4 > end)
955      return;
956  
957 -  /* FIXME: Calculate and use the cropping window for the output, as the
958 -   * intersection of the crop rectangle for this object (if any) and the
959 -   * window specified by the object's window_id */
960 -
961 -  /* Store the start of each plane */
962 -  planes[0] = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
963 -  planes[1] = GST_VIDEO_FRAME_COMP_DATA (frame, 1);
964 -  planes[2] = GST_VIDEO_FRAME_COMP_DATA (frame, 2);
965 -
966 -  strides[0] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
967 -  strides[1] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1);
968 -  strides[2] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2);
969 -
970 -  y = MIN (obj->y, state->info.height);
971 -
972 -  planes[0] += strides[0] * y;
973 -  planes[1] += strides[1] * (y / 2);
974 -  planes[2] += strides[2] * (y / 2);
975 +  pixels = GST_VIDEO_FRAME_PLANE_DATA (window, 0);
976 +  stride = GST_VIDEO_FRAME_PLANE_STRIDE (window, 0);
977 +  win_w = GST_VIDEO_FRAME_WIDTH (window);
978 +  win_h = GST_VIDEO_FRAME_HEIGHT (window);
979  
980 -  /* RLE data: */
981    obj_w = GST_READ_UINT16_BE (data);
982    obj_h = GST_READ_UINT16_BE (data + 2);
983    data += 4;
984  
985 -  min_x = MIN (obj->x, strides[0]);
986 -  max_x = MIN (obj->x + obj_w, strides[0]);
987 +  /* Calculate object coordinates relative to the window */
988 +  min_x = obj_x = (gint) obj->x - (gint) state->pgs.win_x;
989 +  min_y = obj_y = (gint) obj->y - (gint) state->pgs.win_y;
990 +
991 +  if (obj->flags & PGS_COMPOSITION_OBJECT_FLAG_CROPPED) {
992 +    obj_x -= obj->crop_x;
993 +    obj_y -= obj->crop_y;
994 +    obj_w = MIN (obj_w, obj->crop_w);
995 +    obj_h = MIN (obj_h, obj->crop_h);
996 +  }
997 +
998 +  max_x = min_x + obj_w;
999 +  max_y = min_y + obj_h;
1000  
1001 -  state->comp_left = x = min_x;
1002 -  state->comp_right = max_x;
1003 +  /* Early out if object is out of the window */
1004 +  if (max_x <= 0 || max_y < 0 || min_x >= win_w || min_y >= win_h)
1005 +    return;
1006  
1007 -  gstspu_clear_comp_buffers (state);
1008 +  /* Crop inside window */
1009 +  if (min_x < 0)
1010 +    min_x = 0;
1011 +  if (max_x > win_w)
1012 +    max_x = win_w;
1013 +  if (min_y < 0)
1014 +    min_y = 0;
1015 +  if (max_y > win_h)
1016 +    max_y = win_h;
1017 +
1018 +  /* Write RLE data to the plane */
1019 +  x = obj_x;
1020 +  y = obj_y;
1021 +  p = pixels + y * stride;
1022  
1023    while (data < end) {
1024      guint8 pal_id;
1025 @@ -264,43 +278,56 @@
1026        }
1027      }
1028  
1029 +    if (!run_len) {
1030 +      x = obj_x;
1031 +      y++;
1032 +      if (y >= max_y)
1033 +        break;
1034 +
1035 +      p = pixels + y * stride;
1036 +      continue;
1037 +    }
1038 +
1039 +    if (y < min_y)
1040 +      continue;
1041 +
1042 +    if (x >= max_x)
1043 +      continue;
1044 +
1045 +    if (x < min_x) {
1046 +      if (x + run_len <= min_x) {
1047 +        x += run_len;
1048 +        continue;
1049 +      } else {
1050 +        run_len -= min_x - x;
1051 +        x = min_x;
1052 +      }
1053 +    }
1054 +
1055      colour = &state->pgs.palette[pal_id];
1056 -    if (colour->A) {
1057 -      guint32 inv_A = 0xff - colour->A;
1058 +
1059 +    if (colour->A > 0) {
1060 +      guint8 inv_A = 255 - colour->A;
1061 +
1062        if (G_UNLIKELY (x + run_len > max_x))
1063 -        run_len = (max_x - x);
1064 +        run_len = max_x - x;
1065  
1066        for (i = 0; i < run_len; i++) {
1067 -        planes[0][x] = (inv_A * planes[0][x] + colour->Y) / 0xff;
1068 +        SpuColour *pix = &((SpuColour *) p)[x++];
1069  
1070 -        state->comp_bufs[0][x / 2] += colour->U;
1071 -        state->comp_bufs[1][x / 2] += colour->V;
1072 -        state->comp_bufs[2][x / 2] += colour->A;
1073 -        x++;
1074 +        if (pix->A == 0) {
1075 +          memcpy (pix, colour, sizeof (*pix));
1076 +        } else {
1077 +          pix->A = colour->A;
1078 +          pix->R = colour->R + pix->R * inv_A / 255;
1079 +          pix->G = colour->G + pix->G * inv_A / 255;
1080 +          pix->B = colour->B + pix->B * inv_A / 255;
1081 +        }
1082        }
1083      } else {
1084        x += run_len;
1085      }
1086 -
1087 -    if (!run_len || x > max_x) {
1088 -      x = min_x;
1089 -      planes[0] += strides[0];
1090 -
1091 -      if (y % 2) {
1092 -        gstspu_blend_comp_buffers (state, planes);
1093 -        gstspu_clear_comp_buffers (state);
1094 -
1095 -        planes[1] += strides[1];
1096 -        planes[2] += strides[2];
1097 -      }
1098 -      y++;
1099 -      if (y >= state->info.height)
1100 -        return;                 /* Hit the bottom */
1101 -    }
1102    }
1103 -
1104 -  if (y % 2)
1105 -    gstspu_blend_comp_buffers (state, planes);
1106  }
1107  
1108  static void
1109 @@ -427,8 +454,10 @@
1110          "x %u y %u\n", i, obj->id, obj->win_id, obj->flags, obj->x, obj->y);
1111  
1112      if (obj->flags & PGS_COMPOSITION_OBJECT_FLAG_CROPPED) {
1113 -      if (payload + 8 > end)
1114 +      if (payload + 8 > end) {
1115 +        obj->flags &= ~PGS_COMPOSITION_OBJECT_FLAG_CROPPED;
1116          break;
1117 +      }
1118  
1119        obj->crop_x = GST_READ_UINT16_BE (payload);
1120        obj->crop_y = GST_READ_UINT16_BE (payload + 2);
1121 @@ -482,23 +511,35 @@
1122      state->pgs.palette[i].A = 0;
1123    for (i = 0; i < n_entries; i++) {
1124      guint8 n, Y, U, V, A;
1125 +    gint R, G, B;
1126      n = payload[0];
1127      Y = payload[1];
1128 -    U = payload[2];
1129 -    V = payload[3];
1130 +    V = payload[2];
1131 +    U = payload[3];
1132      A = payload[4];
1133  
1134  #if DUMP_FULL_PALETTE
1135      PGS_DUMP ("Entry %3d: Y %3d U %3d V %3d A %3d  ", n, Y, U, V, A);
1136 -    if (((i + 1) % 2) == 0)
1137 -      PGS_DUMP ("\n");
1138 +#endif
1139 +
1140 +    /* Convert to ARGB */
1141 +    R = (298 * Y + 459 * V - 63514) >> 8;
1142 +    G = (298 * Y - 55 * U - 136 * V + 19681) >> 8;
1143 +    B = (298 * Y + 541 * U - 73988) >> 8;
1144 +
1145 +    R = CLAMP (R, 0, 255);
1146 +    G = CLAMP (G, 0, 255);
1147 +    B = CLAMP (B, 0, 255);
1148 +
1149 +#if DUMP_FULL_PALETTE
1150 +    PGS_DUMP ("Entry %3d: A %3d R %3d G %3d B %3d\n", n, A, R, G, B);
1151  #endif
1152  
1153      /* Premultiply the palette entries by the alpha */
1154 -    state->pgs.palette[n].Y = Y * A;
1155 -    state->pgs.palette[n].U = U * A;
1156 -    state->pgs.palette[n].V = V * A;
1157      state->pgs.palette[n].A = A;
1158 +    state->pgs.palette[n].R = R * A / 255;
1159 +    state->pgs.palette[n].G = G * A / 255;
1160 +    state->pgs.palette[n].B = B * A / 255;
1161  
1162      payload += PGS_PALETTE_ENTRY_SIZE;
1163    }
1164 @@ -761,7 +802,7 @@
1165  }
1166  
1167  void
1168 -gstspu_pgs_render (GstDVDSpu * dvdspu, GstVideoFrame * frame)
1169 +gstspu_pgs_render (GstDVDSpu * dvdspu, GstVideoFrame * window)
1170  {
1171    SpuState *state = &dvdspu->spu_state;
1172    PgsPresentationSegment *ps = &state->pgs.pres_seg;
1173 @@ -773,7 +814,7 @@
1174    for (i = 0; i < ps->objects->len; i++) {
1175      PgsCompositionObject *cur =
1176          &g_array_index (ps->objects, PgsCompositionObject, i);
1177 -    pgs_composition_object_render (cur, state, frame);
1178 +    pgs_composition_object_render (cur, state, window);
1179    }
1180  }
1181  
1182 @@ -785,6 +826,27 @@
1183  }
1184  
1185  void
1186 +gstspu_pgs_get_render_geometry (GstDVDSpu * dvdspu,
1187 +    gint * display_width, gint * display_height,
1188 +    GstVideoRectangle * window_rect)
1189 +{
1190 +  SpuPgsState *pgs_state = &dvdspu->spu_state.pgs;
1191 +
1192 +  if (window_rect) {
1193 +    window_rect->x = pgs_state->win_x;
1194 +    window_rect->y = pgs_state->win_y;
1195 +    window_rect->w = pgs_state->win_w;
1196 +    window_rect->h = pgs_state->win_h;
1197 +  }
1198 +
1199 +  if (display_width)
1200 +    *display_width = pgs_state->pres_seg.vid_w;
1201 +
1202 +  if (display_height)
1203 +    *display_height = pgs_state->pres_seg.vid_h;
1204 +}
1205 +
1206 +void
1207  gstspu_pgs_flush (GstDVDSpu * dvdspu)
1208  {
1209    SpuPgsState *pgs_state = &dvdspu->spu_state.pgs;
1210 diff -u a/gst/dvdspu/gstspu-pgs.h b/gst/dvdspu/gstspu-pgs.h
1211 --- a/gst/dvdspu/gstspu-pgs.h   2015-03-20 11:50:14.721690223 +0100
1212 +++ b/gst/dvdspu/gstspu-pgs.h   2015-09-28 16:30:05.862437231 +0200
1213 @@ -99,8 +99,11 @@
1214  
1215  void gstspu_pgs_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts, GstBuffer *buf);
1216  gboolean gstspu_pgs_execute_event (GstDVDSpu *dvdspu);
1217 -void gstspu_pgs_render (GstDVDSpu *dvdspu, GstVideoFrame *frame);
1218 +void gstspu_pgs_render (GstDVDSpu *dvdspu, GstVideoFrame *window);
1219  gboolean gstspu_pgs_handle_dvd_event (GstDVDSpu *dvdspu, GstEvent *event);
1220 +void gstspu_pgs_get_render_geometry (GstDVDSpu *dvdspu,
1221 +    gint *display_width, gint *display_height,
1222 +    GstVideoRectangle *window_rect);
1223  void gstspu_pgs_flush (GstDVDSpu *dvdspu);
1224  
1225  #endif
1226 diff -u a/gst/dvdspu/gstspu-vobsub-render.c b/gst/dvdspu/gstspu-vobsub-render.c
1227 --- a/gst/dvdspu/gstspu-vobsub-render.c 2015-09-01 11:05:50.348528393 +0200
1228 +++ b/gst/dvdspu/gstspu-vobsub-render.c 2015-09-28 16:30:56.649992319 +0200
1229 @@ -40,29 +40,42 @@
1230    if (state->vobsub.current_clut[idx[0]] != 0) {
1231      for (i = 0; i < 4; i++, dest++) {
1232        guint32 col = state->vobsub.current_clut[idx[i]];
1233 +      gint A, Y, U, V;
1234 +      gint R, G, B;
1235  
1236        /* Convert incoming 4-bit alpha to 8 bit for blending */
1237 -      dest->A = (alpha[i] << 4) | alpha[i];
1238 -      dest->Y = ((guint16) ((col >> 16) & 0xff)) * dest->A;
1239 +      A = (alpha[i] << 4) | alpha[i];
1240 +      Y = ((col >> 16) & 0xff);
1241        /* U/V are stored as V/U in the clut words, so switch them */
1242 -      dest->V = ((guint16) ((col >> 8) & 0xff)) * dest->A;
1243 -      dest->U = ((guint16) (col & 0xff)) * dest->A;
1244 +      V = ((col >> 8) & 0xff);
1245 +      U = (col & 0xff);
1246 +
1247 +      R = (298 * Y + 459 * V - 63514) >> 8;
1248 +      G = (298 * Y - 55 * U - 136 * V + 19681) >> 8;
1249 +      B = (298 * Y + 541 * U - 73988) >> 8;
1250 +
1251 +      R = CLAMP (R, 0, 255);
1252 +      G = CLAMP (G, 0, 255);
1253 +      B = CLAMP (B, 0, 255);
1254 +
1255 +      dest->A = A;
1256 +      dest->R = R * A / 255;
1257 +      dest->G = G * A / 255;
1258 +      dest->B = B * A / 255;
1259      }
1260    } else {
1261 -    int y = 240;
1262 +    int c = 255;
1263  
1264      /* The CLUT presumably hasn't been set, so we'll just guess some
1265       * values for the non-transparent colors (white, grey, black) */
1266      for (i = 0; i < 4; i++, dest++) {
1267        dest->A = (alpha[i] << 4) | alpha[i];
1268        if (alpha[i] != 0) {
1269 -        dest[0].Y = y * dest[0].A;
1270 -        y -= 112;
1271 -        if (y < 0)
1272 -          y = 0;
1273 +        dest->R = dest->G = dest->B = c * dest->A / 255;
1274 +        c -= 128;
1275 +        if (c < 0)
1276 +          c = 0;
1277        }
1278 -      dest[0].U = 128 * dest[0].A;
1279 -      dest[0].V = 128 * dest[0].A;
1280      }
1281    }
1282  }
1283 @@ -169,28 +182,36 @@
1284  }
1285  
1286  static inline gboolean
1287 -gstspu_vobsub_draw_rle_run (SpuState * state, gint16 x, gint16 end,
1288 -    SpuColour * colour)
1289 +gstspu_vobsub_draw_rle_run (SpuState * state, GstVideoFrame * frame,
1290 +    gint16 x, gint16 end, SpuColour * colour)
1291  {
1292 -#if 0
1293 -  GST_LOG ("Y: %d x: %d end %d col %d %d %d %d",
1294 -      state->vobsub.cur_Y, x, end, colour->Y, colour->U, colour->V, colour->A);
1295 -#endif
1296 -
1297 -  if (colour->A != 0) {
1298 -    guint32 inv_A = 0xff - colour->A;
1299 +  GST_TRACE ("Y: %d x: %d end %d %d %d %d %d",
1300 +      state->vobsub.cur_Y, x, end, colour->R, colour->G, colour->B, colour->A);
1301  
1302 -    /* FIXME: This could be more efficient */
1303 -    while (x < end) {
1304 -      state->vobsub.out_Y[x] =
1305 -          (inv_A * state->vobsub.out_Y[x] + colour->Y) / 0xff;
1306 -      state->vobsub.out_U[x / 2] += colour->U;
1307 -      state->vobsub.out_V[x / 2] += colour->V;
1308 -      state->vobsub.out_A[x / 2] += colour->A;
1309 -      x++;
1310 +  if (colour->A > 0) {
1311 +    gint i;
1312 +    guint8 *data;
1313 +    guint8 inv_A = 255 - colour->A;
1314 +
1315 +    data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
1316 +    data += GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0) *
1317 +        (state->vobsub.cur_Y - state->vobsub.disp_rect.top);
1318 +
1319 +    x -= state->vobsub.disp_rect.left;
1320 +    end -= state->vobsub.disp_rect.left;
1321 +
1322 +    for (i = x; i < end; i++) {
1323 +      SpuColour *pix = &((SpuColour *) data)[x++];
1324 +
1325 +      if (pix->A == 0) {
1326 +        memcpy (pix, colour, sizeof (*pix));
1327 +      } else {
1328 +        pix->A = colour->A;
1329 +        pix->R = colour->R + pix->R * inv_A / 255;
1330 +        pix->G = colour->G + pix->G * inv_A / 255;
1331 +        pix->B = colour->B + pix->B * inv_A / 255;
1332 +      }
1333      }
1334 -    /* Update the compositing buffer so we know how much to blend later */
1335 -    *(state->vobsub.comp_last_x_ptr) = end - 1; /* end is the start of the *next* run */
1336  
1337      return TRUE;
1338    }
1339 @@ -208,11 +229,11 @@
1340  }
1341  
1342  static gboolean gstspu_vobsub_render_line_with_chgcol (SpuState * state,
1343 -    guint8 * planes[3], guint16 * rle_offset);
1344 +    GstVideoFrame * frame, guint16 * rle_offset);
1345  static gboolean gstspu_vobsub_update_chgcol (SpuState * state);
1346  
1347  static gboolean
1348 -gstspu_vobsub_render_line (SpuState * state, guint8 * planes[3],
1349 +gstspu_vobsub_render_line (SpuState * state, GstVideoFrame * frame,
1350      guint16 * rle_offset)
1351  {
1352    gint16 x, next_x, end, rle_code, next_draw_x;
1353 @@ -226,19 +247,13 @@
1354        /* Check the top & bottom, because we might not be within the region yet */
1355        if (state->vobsub.cur_Y >= state->vobsub.cur_chg_col->top &&
1356            state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom) {
1357 -        return gstspu_vobsub_render_line_with_chgcol (state, planes,
1358 -            rle_offset);
1359 +        return gstspu_vobsub_render_line_with_chgcol (state, frame, rle_offset);
1360        }
1361      }
1362    }
1363  
1364    /* No special case. Render as normal */
1365  
1366 -  /* Set up our output pointers */
1367 -  state->vobsub.out_Y = planes[0];
1368 -  state->vobsub.out_U = state->comp_bufs[0];
1369 -  state->vobsub.out_V = state->comp_bufs[1];
1370 -  state->vobsub.out_A = state->comp_bufs[2];
1371    /* We always need to start our RLE decoding byte_aligned */
1372    *rle_offset = GST_ROUND_UP_2 (*rle_offset);
1373  
1374 @@ -249,12 +264,10 @@
1375      colour = &state->vobsub.main_pal[rle_code & 3];
1376      next_x = rle_end_x (rle_code, x, end);
1377      next_draw_x = next_x;
1378 -    if (next_draw_x > state->vobsub.clip_rect.right)
1379 -      next_draw_x = state->vobsub.clip_rect.right;      /* ensure no overflow */
1380 +    if (next_draw_x > state->vobsub.disp_rect.right)
1381 +      next_draw_x = state->vobsub.disp_rect.right;      /* ensure no overflow */
1382      /* Now draw the run between [x,next_x) */
1383 -    if (state->vobsub.cur_Y >= state->vobsub.clip_rect.top &&
1384 -        state->vobsub.cur_Y <= state->vobsub.clip_rect.bottom)
1385 -      visible |= gstspu_vobsub_draw_rle_run (state, x, next_draw_x, colour);
1386 +    visible |= gstspu_vobsub_draw_rle_run (state, frame, x, next_draw_x, colour);
1387      x = next_x;
1388    }
1389  
1390 @@ -289,7 +302,7 @@
1391  }
1392  
1393  static gboolean
1394 -gstspu_vobsub_render_line_with_chgcol (SpuState * state, guint8 * planes[3],
1395 +gstspu_vobsub_render_line_with_chgcol (SpuState * state, GstVideoFrame * frame,
1396      guint16 * rle_offset)
1397  {
1398    SpuVobsubLineCtrlI *chg_col = state->vobsub.cur_chg_col;
1399 @@ -304,11 +317,6 @@
1400    gint16 cur_reg_end;
1401    gint i;
1402  
1403 -  state->vobsub.out_Y = planes[0];
1404 -  state->vobsub.out_U = state->comp_bufs[0];
1405 -  state->vobsub.out_V = state->comp_bufs[1];
1406 -  state->vobsub.out_A = state->comp_bufs[2];
1407 -
1408    /* We always need to start our RLE decoding byte_aligned */
1409    *rle_offset = GST_ROUND_UP_2 (*rle_offset);
1410  
1411 @@ -344,12 +352,13 @@
1412        run_end = MIN (next_x, cur_reg_end);
1413  
1414        run_draw_end = run_end;
1415 -      if (run_draw_end > state->vobsub.clip_rect.right)
1416 -        run_draw_end = state->vobsub.clip_rect.right;   /* ensure no overflow */
1417 +      if (run_draw_end > state->vobsub.disp_rect.right)
1418 +        run_draw_end = state->vobsub.disp_rect.right;   /* ensure no overflow */
1419  
1420        if (G_LIKELY (x < run_end)) {
1421          colour = &cur_pix_ctrl->pal_cache[rle_code & 3];
1422 -        visible |= gstspu_vobsub_draw_rle_run (state, x, run_draw_end, colour);
1423 +        visible |= gstspu_vobsub_draw_rle_run (state, frame, x,
1424 +            run_draw_end, colour);
1425          x = run_end;
1426        }
1427  
1428 @@ -370,51 +379,36 @@
1429  }
1430  
1431  static void
1432 -gstspu_vobsub_blend_comp_buffers (SpuState * state, guint8 * planes[3])
1433 -{
1434 -  state->comp_left = state->vobsub.disp_rect.left;
1435 -  state->comp_right =
1436 -      MAX (state->vobsub.comp_last_x[0], state->vobsub.comp_last_x[1]);
1437 -
1438 -  state->comp_left = MAX (state->comp_left, state->vobsub.clip_rect.left);
1439 -  state->comp_right = MIN (state->comp_right, state->vobsub.clip_rect.right);
1440 -
1441 -  gstspu_blend_comp_buffers (state, planes);
1442 -}
1443 -
1444 -static void
1445 -gstspu_vobsub_clear_comp_buffers (SpuState * state)
1446 -{
1447 -  state->comp_left = state->vobsub.clip_rect.left;
1448 -  state->comp_right = state->vobsub.clip_rect.right;
1449 -
1450 -  gstspu_clear_comp_buffers (state);
1451 -
1452 -  state->vobsub.comp_last_x[0] = -1;
1453 -  state->vobsub.comp_last_x[1] = -1;
1454 -}
1455 -
1456 -static void
1457  gstspu_vobsub_draw_highlight (SpuState * state,
1458      GstVideoFrame * frame, SpuRect * rect)
1459  {
1460 -  guint8 *cur;
1461 +  SpuColour *cur;
1462 +  SpuRect r;
1463 +  guint8 *data;
1464 +  guint stride;
1465    gint16 pos;
1466 -  gint ystride;
1467 -
1468 -  ystride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
1469  
1470 -  cur = GST_VIDEO_FRAME_COMP_DATA (frame, 0) + ystride * rect->top;
1471 -  for (pos = rect->left + 1; pos < rect->right; pos++)
1472 -    cur[pos] = (cur[pos] / 2) + 0x8;
1473 -  cur = GST_VIDEO_FRAME_COMP_DATA (frame, 0) + ystride * rect->bottom;
1474 -  for (pos = rect->left + 1; pos < rect->right; pos++)
1475 -    cur[pos] = (cur[pos] / 2) + 0x8;
1476 -  cur = GST_VIDEO_FRAME_COMP_DATA (frame, 0) + ystride * rect->top;
1477 -  for (pos = rect->top; pos <= rect->bottom; pos++) {
1478 -    cur[rect->left] = (cur[rect->left] / 2) + 0x8;
1479 -    cur[rect->right] = (cur[rect->right] / 2) + 0x8;
1480 -    cur += ystride;
1481 +  r.left = rect->left - state->vobsub.disp_rect.left;
1482 +  r.right = rect->right - state->vobsub.disp_rect.left;
1483 +  r.top = rect->top - state->vobsub.disp_rect.top;
1484 +  r.bottom = rect->bottom - state->vobsub.disp_rect.top;
1485 +  rect = &r;
1486 +
1487 +  data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
1488 +  stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
1489 +
1490 +  cur = (SpuColour *) (data + stride * rect->top);
1491 +  for (pos = rect->left; pos < rect->right; pos++)
1492 +    cur[pos].A = 0x80;
1493 +
1494 +  cur = (SpuColour *) (data + stride * (rect->bottom - 1));
1495 +  for (pos = rect->left; pos < rect->right; pos++)
1496 +    cur[pos].A = 0x80;
1497 +
1498 +  for (pos = rect->top; pos < rect->bottom; pos++) {
1499 +    cur = (SpuColour *) (data + stride * pos);
1500 +    cur[rect->left].A = 0x80;
1501 +    cur[rect->right - 1].A = 0x80;
1502    }
1503  }
1504  
1505 @@ -422,10 +416,8 @@
1506  gstspu_vobsub_render (GstDVDSpu * dvdspu, GstVideoFrame * frame)
1507  {
1508    SpuState *state = &dvdspu->spu_state;
1509 -  guint8 *planes[3];            /* YUV frame pointers */
1510    gint y, last_y;
1511 -  gint width, height;
1512 -  gint strides[3];
1513 +  guint16 cur_offsets[2];
1514  
1515    /* Set up our initial state */
1516    if (G_UNLIKELY (state->vobsub.pix_buf == NULL))
1517 @@ -435,18 +427,6 @@
1518            GST_MAP_READ))
1519      return;
1520  
1521 -  /* Store the start of each plane */
1522 -  planes[0] = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
1523 -  planes[1] = GST_VIDEO_FRAME_COMP_DATA (frame, 1);
1524 -  planes[2] = GST_VIDEO_FRAME_COMP_DATA (frame, 2);
1525 -
1526 -  strides[0] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
1527 -  strides[1] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1);
1528 -  strides[2] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2);
1529 -
1530 -  width = GST_VIDEO_FRAME_WIDTH (frame);
1531 -  height = GST_VIDEO_FRAME_HEIGHT (frame);
1532 -
1533    GST_DEBUG_OBJECT (dvdspu,
1534        "Rendering SPU. disp_rect %d,%d to %d,%d. hl_rect %d,%d to %d,%d",
1535        state->vobsub.disp_rect.left, state->vobsub.disp_rect.top,
1536 @@ -454,13 +434,6 @@
1537        state->vobsub.hl_rect.left, state->vobsub.hl_rect.top,
1538        state->vobsub.hl_rect.right, state->vobsub.hl_rect.bottom);
1539  
1540 -  GST_DEBUG_OBJECT (dvdspu, "video size %d,%d", width, height);
1541 -
1542 -  /* When reading RLE data, we track the offset in nibbles... */
1543 -  state->vobsub.cur_offsets[0] = state->vobsub.pix_data[0] * 2;
1544 -  state->vobsub.cur_offsets[1] = state->vobsub.pix_data[1] * 2;
1545 -  state->vobsub.max_offset = state->vobsub.pix_buf_map.size * 2;
1546 -
1547    /* Update all the palette caches */
1548    gstspu_vobsub_update_palettes (dvdspu, state);
1549  
1550 @@ -475,140 +448,25 @@
1551    } else
1552      state->vobsub.cur_chg_col = NULL;
1553  
1554 -  state->vobsub.clip_rect.left = state->vobsub.disp_rect.left;
1555 -  state->vobsub.clip_rect.right = state->vobsub.disp_rect.right;
1556 -
1557 -  /* center the image when display rectangle exceeds the video width */
1558 -  if (width <= state->vobsub.disp_rect.right) {
1559 -    gint left, disp_width;
1560 -
1561 -    disp_width = state->vobsub.disp_rect.right - state->vobsub.disp_rect.left
1562 -        + 1;
1563 -    left = (width - disp_width) / 2;
1564 -    state->vobsub.disp_rect.left = left;
1565 -    state->vobsub.disp_rect.right = left + disp_width - 1;
1566 -
1567 -    /* if it clips to the right, shift it left, but only till zero */
1568 -    if (state->vobsub.disp_rect.right >= width) {
1569 -      gint shift = state->vobsub.disp_rect.right - width - 1;
1570 -      if (shift > state->vobsub.disp_rect.left)
1571 -        shift = state->vobsub.disp_rect.left;
1572 -      state->vobsub.disp_rect.left -= shift;
1573 -      state->vobsub.disp_rect.right -= shift;
1574 -    }
1575 -
1576 -    /* init clip to disp */
1577 -    state->vobsub.clip_rect.left = state->vobsub.disp_rect.left;
1578 -    state->vobsub.clip_rect.right = state->vobsub.disp_rect.right;
1579 -
1580 -    /* clip right after the shift */
1581 -    if (state->vobsub.clip_rect.right >= width)
1582 -      state->vobsub.clip_rect.right = width - 1;
1583 -
1584 -    GST_DEBUG_OBJECT (dvdspu,
1585 -        "clipping width to %d,%d", state->vobsub.clip_rect.left,
1586 -        state->vobsub.clip_rect.right);
1587 -  }
1588 -
1589 -  /* for the height, bring it up till it fits as well as it can. We
1590 -   * assume the picture is in the lower part. We should better check where it
1591 -   * is and do something more clever. */
1592 -  state->vobsub.clip_rect.top = state->vobsub.disp_rect.top;
1593 -  state->vobsub.clip_rect.bottom = state->vobsub.disp_rect.bottom;
1594 -  if (height <= state->vobsub.disp_rect.bottom) {
1595 -
1596 -    /* shift it up, but only till zero */
1597 -    gint shift = state->vobsub.disp_rect.bottom - height - 1;
1598 -    if (shift > state->vobsub.disp_rect.top)
1599 -      shift = state->vobsub.disp_rect.top;
1600 -    state->vobsub.disp_rect.top -= shift;
1601 -    state->vobsub.disp_rect.bottom -= shift;
1602 -
1603 -    /* start on even line */
1604 -    if (state->vobsub.disp_rect.top & 1) {
1605 -      state->vobsub.disp_rect.top--;
1606 -      state->vobsub.disp_rect.bottom--;
1607 -    }
1608 -
1609 -    /* init clip to disp */
1610 -    state->vobsub.clip_rect.top = state->vobsub.disp_rect.top;
1611 -    state->vobsub.clip_rect.bottom = state->vobsub.disp_rect.bottom;
1612 -
1613 -    /* clip right after the shift */
1614 -    if (state->vobsub.clip_rect.bottom >= height)
1615 -      state->vobsub.clip_rect.bottom = height - 1;
1616 -
1617 -    GST_DEBUG_OBJECT (dvdspu,
1618 -        "clipping height to %d,%d", state->vobsub.clip_rect.top,
1619 -        state->vobsub.clip_rect.bottom);
1620 -  }
1621 -
1622    /* We start rendering from the first line of the display rect */
1623    y = state->vobsub.disp_rect.top;
1624 -  /* start_y is always an even number and we render lines in pairs from there,
1625 -   * accumulating 2 lines of chroma then blending it. We might need to render a
1626 -   * single line at the end if the display rect ends on an even line too. */
1627 -  last_y = (state->vobsub.disp_rect.bottom - 1) & ~(0x01);
1628 -
1629 -  /* Update our plane references to the first line of the disp_rect */
1630 -  planes[0] += strides[0] * y;
1631 -  planes[1] += strides[1] * (y / 2);
1632 -  planes[2] += strides[2] * (y / 2);
1633 +  last_y = state->vobsub.disp_rect.bottom;
1634  
1635 +  /* When reading RLE data, we track the offset in nibbles... */
1636 +  state->vobsub.max_offset = state->vobsub.pix_buf_map.size * 2;
1637 +  if (y & 1) {
1638 +    cur_offsets[1] = state->vobsub.pix_data[0] * 2;
1639 +    cur_offsets[0] = state->vobsub.pix_data[1] * 2;
1640 +  } else {
1641 +    cur_offsets[0] = state->vobsub.pix_data[0] * 2;
1642 +    cur_offsets[1] = state->vobsub.pix_data[1] * 2;
1643 +  }
1644 +
1645 +  /* Render line by line */
1646    for (state->vobsub.cur_Y = y; state->vobsub.cur_Y <= last_y;
1647        state->vobsub.cur_Y++) {
1648 -    gboolean clip, visible = FALSE;
1649 -
1650 -    clip = (state->vobsub.cur_Y < state->vobsub.clip_rect.top
1651 -        || state->vobsub.cur_Y > state->vobsub.clip_rect.bottom);
1652 -
1653 -    /* Reset the compositing buffer */
1654 -    gstspu_vobsub_clear_comp_buffers (state);
1655 -    /* Render even line */
1656 -    state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x;
1657 -    gstspu_vobsub_render_line (state, planes, &state->vobsub.cur_offsets[0]);
1658 -
1659 -    /* Advance the luminance output pointer */
1660 -    planes[0] += strides[0];
1661 -
1662 -    state->vobsub.cur_Y++;
1663 -
1664 -    /* Render odd line */
1665 -    state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x + 1;
1666 -    visible |=
1667 -        gstspu_vobsub_render_line (state, planes,
1668 -        &state->vobsub.cur_offsets[1]);
1669 -
1670 -    if (visible && !clip) {
1671 -      /* Blend the accumulated UV compositing buffers onto the output */
1672 -      gstspu_vobsub_blend_comp_buffers (state, planes);
1673 -    }
1674 -
1675 -    /* Update all the output pointers */
1676 -    planes[0] += strides[0];
1677 -    planes[1] += strides[1];
1678 -    planes[2] += strides[2];
1679 -  }
1680 -
1681 -  if (state->vobsub.cur_Y == state->vobsub.disp_rect.bottom) {
1682 -    gboolean clip, visible = FALSE;
1683 -
1684 -    clip = (state->vobsub.cur_Y < state->vobsub.clip_rect.top
1685 -        || state->vobsub.cur_Y > state->vobsub.clip_rect.bottom);
1686 -
1687 -    g_assert ((state->vobsub.disp_rect.bottom & 0x01) == 0);
1688 -
1689 -    if (!clip) {
1690 -      /* Render a remaining lone last even line. y already has the correct value
1691 -       * after the above loop exited. */
1692 -      gstspu_vobsub_clear_comp_buffers (state);
1693 -      state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x;
1694 -      visible |=
1695 -          gstspu_vobsub_render_line (state, planes,
1696 -          &state->vobsub.cur_offsets[0]);
1697 -      if (visible)
1698 -        gstspu_vobsub_blend_comp_buffers (state, planes);
1699 -    }
1700 +    gstspu_vobsub_render_line (state, frame,
1701 +        &cur_offsets[state->vobsub.cur_Y & 1]);
1702    }
1703  
1704    /* for debugging purposes, draw a faint rectangle at the edges of the disp_rect */
1705 diff -u a/gst/dvdspu/gstspu-vobsub.c b/gst/dvdspu/gstspu-vobsub.c
1706 --- a/gst/dvdspu/gstspu-vobsub.c        2015-03-20 11:50:14.721690223 +0100
1707 +++ b/gst/dvdspu/gstspu-vobsub.c        2015-09-28 16:31:14.380302505 +0200
1708 @@ -485,6 +485,11 @@
1709        if (was_forced != forced_only)
1710          hl_change = TRUE;
1711      }
1712 +  } else if (strcmp (event_type, "dvd-set-frame-size") == 0) {
1713 +    gst_structure_get_int (structure, "width", &state->vobsub.frame_w);
1714 +    gst_structure_get_int (structure, "height", &state->vobsub.frame_h);
1715 +    GST_INFO_OBJECT (dvdspu, "Frame size is now %dx%d",
1716 +        state->vobsub.frame_w, state->vobsub.frame_h);
1717    }
1718  
1719    gst_event_unref (event);
1720 @@ -493,6 +498,31 @@
1721  }
1722  
1723  void
1724 +gstspu_vobsub_get_render_geometry (GstDVDSpu * dvdspu,
1725 +    gint * display_width, gint * display_height,
1726 +    GstVideoRectangle * window_rect)
1727 +{
1728 +  SpuState *state = &dvdspu->spu_state;
1729 +
1730 +  if (window_rect) {
1731 +    window_rect->x = state->vobsub.disp_rect.left;
1732 +    window_rect->y = state->vobsub.disp_rect.top;
1733 +    window_rect->w = state->vobsub.disp_rect.right -
1734 +        state->vobsub.disp_rect.left + 1;
1735 +    window_rect->h = state->vobsub.disp_rect.bottom -
1736 +        state->vobsub.disp_rect.top + 1;
1737 +  }
1738 +
1739 +  if (display_width)
1740 +    *display_width = state->vobsub.frame_w > 0 ?
1741 +        state->vobsub.frame_w : state->info.width;
1742 +
1743 +  if (display_height)
1744 +    *display_height = state->vobsub.frame_h > 0 ?
1745 +        state->vobsub.frame_h : state->info.height;
1746 +}
1747 +
1748 +void
1749  gstspu_vobsub_flush (GstDVDSpu * dvdspu)
1750  {
1751    SpuState *state = &dvdspu->spu_state;
1752 diff -u a/gst/dvdspu/gstspu-vobsub.h b/gst/dvdspu/gstspu-vobsub.h
1753 --- a/gst/dvdspu/gstspu-vobsub.h        2015-09-01 11:05:50.348528393 +0200
1754 +++ b/gst/dvdspu/gstspu-vobsub.h        2015-09-28 16:31:14.380302505 +0200
1755 @@ -55,8 +55,8 @@
1756    GstMapInfo pix_buf_map; /* Mapped buffer info */
1757    
1758    SpuRect disp_rect;
1759 -  SpuRect clip_rect;
1760    SpuRect hl_rect;
1761 +  gint frame_w, frame_h;
1762  
1763    guint32 current_clut[16]; /* Colour lookup table from incoming events */
1764  
1765 @@ -81,31 +81,25 @@
1766                                     * need recalculating */
1767  
1768    /* Rendering state vars below */
1769 -  gint16 comp_last_x[2]; /* Maximum X values we rendered into the comp buffer (odd & even) */
1770 -  gint16 *comp_last_x_ptr; /* Ptr to the current comp_last_x value to be updated by the render */
1771  
1772    /* Current Y Position */
1773    gint16 cur_Y;
1774  
1775    /* Current offset in nibbles into the pix_data */
1776 -  guint16 cur_offsets[2];
1777    guint16 max_offset;
1778  
1779    /* current ChgColCon Line Info */
1780    SpuVobsubLineCtrlI *cur_chg_col;
1781    SpuVobsubLineCtrlI *cur_chg_col_end;
1782 -
1783 -  /* Output position tracking */
1784 -  guint8  *out_Y;
1785 -  guint32 *out_U;
1786 -  guint32 *out_V;
1787 -  guint32 *out_A;
1788  };
1789  
1790  void gstspu_vobsub_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts, GstBuffer *buf);
1791  gboolean gstspu_vobsub_execute_event (GstDVDSpu *dvdspu);
1792  void gstspu_vobsub_render (GstDVDSpu *dvdspu, GstVideoFrame *frame);
1793  gboolean gstspu_vobsub_handle_dvd_event (GstDVDSpu *dvdspu, GstEvent *event);
1794 +void gstspu_vobsub_get_render_geometry (GstDVDSpu *dvdspu,
1795 +    gint *display_width, gint *display_height,
1796 +    GstVideoRectangle *window_rect);
1797  void gstspu_vobsub_flush (GstDVDSpu *dvdspu);
1798  
1799  #endif