mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
dvdspu: render to AYUV overlay
Instead of only supporting writing SPU data directly to YUV frames, render the SPU data to an intermediate AYUV overlay buffer. The overlay data is then attached to the video frame if downstream supports overlay composition, otherwise the AYUV overlay is blended to the video frame. For the PGS format, the overlay buffer size is set to the size of the Composition Window, and its position in the overlay composition is set to the window position. The objects to render are now cropped when the cropping flag is set. For the Vobsub format, the overlay buffer size is set to the size of the Display Area. Once rendered, the overlay composition rectangle is now moved and scaled to fit the video output size, to avoid clipping. https://bugzilla.gnome.org/show_bug.cgi?id=663750
This commit is contained in:
parent
35e6d79d40
commit
5016a73190
10 changed files with 442 additions and 450 deletions
|
@ -1,7 +1,7 @@
|
|||
|
||||
plugin_LTLIBRARIES = libgstdvdspu.la
|
||||
|
||||
libgstdvdspu_la_SOURCES = gstdvdspu.c gstdvdspu-render.c gstspu-vobsub.c gstspu-vobsub-render.c gstspu-pgs.c
|
||||
libgstdvdspu_la_SOURCES = gstdvdspu.c gstspu-vobsub.c gstspu-vobsub-render.c gstspu-pgs.c
|
||||
|
||||
libgstdvdspu_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
|
||||
libgstdvdspu_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-$(GST_API_VERSION) \
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
/* GStreamer DVD Sub-Picture Unit
|
||||
* Copyright (C) 2007 Fluendo S.A. <info@fluendo.com>
|
||||
* Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "gstdvdspu.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (dvdspu_debug);
|
||||
#define GST_CAT_DEFAULT dvdspu_debug
|
||||
|
||||
void
|
||||
gstspu_clear_comp_buffers (SpuState * state)
|
||||
{
|
||||
/* The area to clear is the line inside the disp_rect, each entry 4 bytes,
|
||||
* of the sub-sampled UV planes. */
|
||||
gint16 left = state->comp_left / 2;
|
||||
gint16 right = state->comp_right / 2;
|
||||
gint16 uv_width = sizeof (guint32) * (right - left + 1);
|
||||
|
||||
memset (state->comp_bufs[0] + left, 0, uv_width);
|
||||
memset (state->comp_bufs[1] + left, 0, uv_width);
|
||||
memset (state->comp_bufs[2] + left, 0, uv_width);
|
||||
}
|
||||
|
||||
void
|
||||
gstspu_blend_comp_buffers (SpuState * state, guint8 * planes[3])
|
||||
{
|
||||
gint16 uv_end;
|
||||
gint16 left, x;
|
||||
guint8 *out_U;
|
||||
guint8 *out_V;
|
||||
guint32 *in_U;
|
||||
guint32 *in_V;
|
||||
guint32 *in_A;
|
||||
gint16 comp_last_x = state->comp_right;
|
||||
|
||||
if (comp_last_x < state->comp_left)
|
||||
return; /* Didn't draw in the comp buffers, nothing to do... */
|
||||
|
||||
#if 0
|
||||
GST_LOG ("Blending comp buffers from x=%d to x=%d",
|
||||
state->comp_left, state->comp_right);
|
||||
#endif
|
||||
|
||||
/* Set up the output pointers */
|
||||
out_U = planes[1]; /* U plane */
|
||||
out_V = planes[2]; /* V plane */
|
||||
|
||||
/* Input starts at the first pixel of the compositing buffer */
|
||||
in_U = state->comp_bufs[0]; /* U comp buffer */
|
||||
in_V = state->comp_bufs[1]; /* V comp buffer */
|
||||
in_A = state->comp_bufs[2]; /* A comp buffer */
|
||||
|
||||
/* Calculate how many pixels to blend based on the maximum X value that was
|
||||
* drawn in the render_line function, divided by 2 (rounding up) to account
|
||||
* for UV sub-sampling */
|
||||
uv_end = (comp_last_x + 1) / 2;
|
||||
left = state->comp_left / 2;
|
||||
|
||||
out_U += left * GST_VIDEO_INFO_COMP_PSTRIDE (&state->info, 1);
|
||||
out_V += left * GST_VIDEO_INFO_COMP_PSTRIDE (&state->info, 2);
|
||||
for (x = left; x < uv_end; x++) {
|
||||
guint32 tmp;
|
||||
/* Each entry in the compositing buffer is 4 summed pixels, so the
|
||||
* inverse alpha is (4 * 0xff) - in_A[x] */
|
||||
guint16 inv_A = (4 * 0xff) - in_A[x];
|
||||
|
||||
tmp = in_U[x] + inv_A * *out_U;
|
||||
*out_U = (guint8) (tmp / (4 * 0xff));
|
||||
|
||||
tmp = in_V[x] + inv_A * *out_V;
|
||||
*out_V = (guint8) (tmp / (4 * 0xff));
|
||||
|
||||
out_U += GST_VIDEO_INFO_COMP_PSTRIDE (&state->info, 1);
|
||||
out_V += GST_VIDEO_INFO_COMP_PSTRIDE (&state->info, 2);
|
||||
}
|
||||
}
|
|
@ -34,6 +34,9 @@
|
|||
|
||||
#include <gst/gst-i18n-plugin.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <gst/video/video-overlay-composition.h>
|
||||
#include <gst/video/gstvideometa.h>
|
||||
#include <gst/video/gstvideosink.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
@ -53,19 +56,19 @@ enum
|
|||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
#define VIDEO_FORMATS GST_VIDEO_OVERLAY_COMPOSITION_BLEND_FORMATS
|
||||
|
||||
static GstStaticPadTemplate video_sink_factory =
|
||||
GST_STATIC_PAD_TEMPLATE ("video",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("video/x-raw, " "format = (string) { I420, NV12, YV12 }, "
|
||||
"width = (int) [ 16, 4096 ], " "height = (int) [ 16, 4096 ]")
|
||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS))
|
||||
);
|
||||
|
||||
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("video/x-raw, " "format = (string) { I420, NV12, YV12 }, "
|
||||
"width = (int) [ 16, 4096 ], " "height = (int) [ 16, 4096 ]")
|
||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (VIDEO_FORMATS))
|
||||
);
|
||||
|
||||
static GstStaticPadTemplate subpic_sink_factory =
|
||||
|
@ -107,6 +110,7 @@ static gboolean gst_dvd_spu_subpic_event (GstPad * pad, GstObject * parent,
|
|||
GstEvent * event);
|
||||
static gboolean gst_dvd_spu_subpic_set_caps (GstDVDSpu * dvdspu, GstPad * pad,
|
||||
GstCaps * caps);
|
||||
static gboolean gst_dvd_spu_negotiate (GstDVDSpu * dvdspu);
|
||||
|
||||
static void gst_dvd_spu_clear (GstDVDSpu * dvdspu);
|
||||
static void gst_dvd_spu_flush_spu_info (GstDVDSpu * dvdspu,
|
||||
|
@ -209,14 +213,7 @@ static void
|
|||
gst_dvd_spu_finalize (GObject * object)
|
||||
{
|
||||
GstDVDSpu *dvdspu = GST_DVD_SPU (object);
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (dvdspu->spu_state.comp_bufs[i] != NULL) {
|
||||
g_free (dvdspu->spu_state.comp_bufs[i]);
|
||||
dvdspu->spu_state.comp_bufs[i] = NULL;
|
||||
}
|
||||
}
|
||||
g_queue_free (dvdspu->pending_spus);
|
||||
g_mutex_clear (&dvdspu->spu_lock);
|
||||
|
||||
|
@ -321,24 +318,20 @@ gst_dvd_spu_video_set_caps (GstDVDSpu * dvdspu, GstPad * pad, GstCaps * caps)
|
|||
{
|
||||
gboolean res = FALSE;
|
||||
GstVideoInfo info;
|
||||
gint i;
|
||||
SpuState *state;
|
||||
|
||||
if (!gst_video_info_from_caps (&info, caps))
|
||||
goto done;
|
||||
|
||||
DVD_SPU_LOCK (dvdspu);
|
||||
|
||||
state = &dvdspu->spu_state;
|
||||
|
||||
state->info = info;
|
||||
for (i = 0; i < 3; i++) {
|
||||
state->comp_bufs[i] = g_realloc (state->comp_bufs[i],
|
||||
sizeof (guint32) * info.width);
|
||||
res = gst_pad_set_caps (dvdspu->srcpad, caps);
|
||||
if (res) {
|
||||
DVD_SPU_LOCK (dvdspu);
|
||||
state = &dvdspu->spu_state;
|
||||
state->info = info;
|
||||
gst_dvd_spu_negotiate (dvdspu);
|
||||
DVD_SPU_UNLOCK (dvdspu);
|
||||
}
|
||||
DVD_SPU_UNLOCK (dvdspu);
|
||||
|
||||
res = TRUE;
|
||||
done:
|
||||
return res;
|
||||
}
|
||||
|
@ -555,6 +548,9 @@ gst_dvd_spu_video_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
|
|||
|
||||
g_return_val_if_fail (dvdspu != NULL, GST_FLOW_ERROR);
|
||||
|
||||
if (gst_pad_check_reconfigure (dvdspu->srcpad))
|
||||
gst_dvd_spu_negotiate (dvdspu);
|
||||
|
||||
GST_LOG_OBJECT (dvdspu, "video buffer %p with TS %" GST_TIME_FORMAT,
|
||||
buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
|
||||
|
||||
|
@ -659,13 +655,104 @@ no_ref_frame:
|
|||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gstspu_render (GstDVDSpu * dvdspu, GstBuffer * buf)
|
||||
static gboolean
|
||||
gstspu_fit_overlay_rectangle (GstDVDSpu * dvdspu, GstVideoRectangle * rect,
|
||||
gint spu_width, gint spu_height)
|
||||
{
|
||||
GstVideoFrame frame;
|
||||
gint video_width = GST_VIDEO_INFO_WIDTH (&dvdspu->spu_state.info);
|
||||
gint video_height = GST_VIDEO_INFO_HEIGHT (&dvdspu->spu_state.info);
|
||||
GstVideoRectangle r;
|
||||
|
||||
gst_video_frame_map (&frame, &dvdspu->spu_state.info, buf, GST_MAP_READWRITE);
|
||||
r = *rect;
|
||||
|
||||
if (spu_width != video_width || spu_height != video_height) {
|
||||
gdouble hscale, vscale;
|
||||
|
||||
hscale = (gdouble) video_width / (gdouble) spu_width;
|
||||
vscale = (gdouble) video_height / (gdouble) spu_height;
|
||||
|
||||
r.x *= hscale;
|
||||
r.y *= vscale;
|
||||
r.w *= hscale;
|
||||
r.h *= vscale;
|
||||
}
|
||||
|
||||
if (r.x + r.w > video_width)
|
||||
r.x = video_width - r.w;
|
||||
|
||||
if (r.x < 0) {
|
||||
r.x = 0;
|
||||
if (r.w > video_width)
|
||||
r.w = video_width;
|
||||
}
|
||||
|
||||
if (r.y + r.h > video_height)
|
||||
r.y = video_height - r.h;
|
||||
|
||||
if (r.y < 0) {
|
||||
r.y = 0;
|
||||
if (r.h > video_height)
|
||||
r.h = video_height;
|
||||
}
|
||||
|
||||
if (r.x != rect->x || r.y != rect->y || r.w != rect->w || r.h != rect->h) {
|
||||
*rect = r;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GstVideoOverlayComposition *
|
||||
gstspu_render_composition (GstDVDSpu * dvdspu)
|
||||
{
|
||||
GstBuffer *buffer;
|
||||
GstVideoInfo overlay_info;
|
||||
GstVideoFormat format;
|
||||
GstVideoFrame frame;
|
||||
GstVideoOverlayRectangle *rectangle;
|
||||
GstVideoOverlayComposition *composition;
|
||||
GstVideoRectangle win;
|
||||
gint spu_w, spu_h;
|
||||
gsize size;
|
||||
|
||||
format = GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_YUV;
|
||||
|
||||
switch (dvdspu->spu_input_type) {
|
||||
case SPU_INPUT_TYPE_PGS:
|
||||
gstspu_pgs_get_render_geometry (dvdspu, &spu_w, &spu_h, &win);
|
||||
break;
|
||||
case SPU_INPUT_TYPE_VOBSUB:
|
||||
gstspu_vobsub_get_render_geometry (dvdspu, &spu_w, &spu_h, &win);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (win.w <= 0 || win.h <= 0 || spu_w <= 0 || spu_h <= 0) {
|
||||
GST_DEBUG_OBJECT (dvdspu, "skip render of empty window");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gst_video_info_init (&overlay_info);
|
||||
gst_video_info_set_format (&overlay_info, format, win.w, win.h);
|
||||
size = GST_VIDEO_INFO_SIZE (&overlay_info);
|
||||
|
||||
buffer = gst_buffer_new_and_alloc (size);
|
||||
if (!buffer) {
|
||||
GST_WARNING_OBJECT (dvdspu, "failed to allocate overlay buffer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gst_buffer_add_video_meta (buffer, GST_VIDEO_FRAME_FLAG_NONE,
|
||||
format, win.w, win.h);
|
||||
|
||||
if (!gst_video_frame_map (&frame, &overlay_info, buffer, GST_MAP_READWRITE))
|
||||
goto map_failed;
|
||||
|
||||
memset (GST_VIDEO_FRAME_PLANE_DATA (&frame, 0), 0,
|
||||
GST_VIDEO_FRAME_PLANE_STRIDE (&frame, 0) *
|
||||
GST_VIDEO_FRAME_HEIGHT (&frame));
|
||||
|
||||
switch (dvdspu->spu_input_type) {
|
||||
case SPU_INPUT_TYPE_VOBSUB:
|
||||
|
@ -677,7 +764,61 @@ gstspu_render (GstDVDSpu * dvdspu, GstBuffer * buf)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
gst_video_frame_unmap (&frame);
|
||||
|
||||
GST_DEBUG_OBJECT (dvdspu, "Overlay rendered for video size %dx%d, "
|
||||
"spu display size %dx%d, window geometry %dx%d+%d%+d",
|
||||
GST_VIDEO_INFO_WIDTH (&dvdspu->spu_state.info),
|
||||
GST_VIDEO_INFO_HEIGHT (&dvdspu->spu_state.info),
|
||||
spu_w, spu_h, win.w, win.h, win.x, win.y);
|
||||
|
||||
if (gstspu_fit_overlay_rectangle (dvdspu, &win, spu_w, spu_h))
|
||||
GST_DEBUG_OBJECT (dvdspu, "Adjusted window to fit video: %dx%d%+d%+d",
|
||||
win.w, win.h, win.x, win.y);
|
||||
|
||||
rectangle = gst_video_overlay_rectangle_new_raw (buffer, win.x, win.y,
|
||||
win.w, win.h, GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA);
|
||||
|
||||
gst_buffer_unref (buffer);
|
||||
|
||||
composition = gst_video_overlay_composition_new (rectangle);
|
||||
gst_video_overlay_rectangle_unref (rectangle);
|
||||
|
||||
return composition;
|
||||
|
||||
map_failed:
|
||||
GST_ERROR_OBJECT (dvdspu, "failed to map buffer");
|
||||
gst_buffer_unref (buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gstspu_render (GstDVDSpu * dvdspu, GstBuffer * buf)
|
||||
{
|
||||
GstVideoOverlayComposition *composition;
|
||||
GstVideoFrame frame;
|
||||
|
||||
composition = gstspu_render_composition (dvdspu);
|
||||
if (!composition)
|
||||
return;
|
||||
|
||||
if (dvdspu->attach_compo_to_buffer) {
|
||||
gst_buffer_add_video_overlay_composition_meta (buf, composition);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!gst_video_frame_map (&frame, &dvdspu->spu_state.info, buf,
|
||||
GST_MAP_READWRITE)) {
|
||||
GST_WARNING_OBJECT (dvdspu, "failed to map video frame for blending");
|
||||
goto done;
|
||||
}
|
||||
|
||||
gst_video_overlay_composition_blend (composition, &frame);
|
||||
gst_video_frame_unmap (&frame);
|
||||
|
||||
done:
|
||||
gst_video_overlay_composition_unref (composition);
|
||||
}
|
||||
|
||||
/* With SPU LOCK */
|
||||
|
@ -903,6 +1044,41 @@ submit_new_spu_packet (GstDVDSpu * dvdspu, GstBuffer * buf)
|
|||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_dvd_spu_negotiate (GstDVDSpu * dvdspu)
|
||||
{
|
||||
GstQuery *query;
|
||||
GstCaps *caps;
|
||||
gboolean attach = FALSE;
|
||||
|
||||
GST_DEBUG_OBJECT (dvdspu, "performing negotiation");
|
||||
|
||||
caps = gst_pad_get_current_caps (dvdspu->srcpad);
|
||||
if (!caps || gst_caps_is_empty (caps))
|
||||
goto no_format;
|
||||
|
||||
query = gst_query_new_allocation (caps, FALSE);
|
||||
|
||||
if (!gst_pad_peer_query (dvdspu->srcpad, query))
|
||||
GST_DEBUG_OBJECT (dvdspu, "ALLOCATION query failed");
|
||||
|
||||
if (gst_query_find_allocation_meta (query,
|
||||
GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL))
|
||||
attach = TRUE;
|
||||
|
||||
gst_query_unref (query);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
dvdspu->attach_compo_to_buffer = attach;
|
||||
|
||||
return TRUE;
|
||||
|
||||
no_format:
|
||||
if (caps)
|
||||
gst_caps_unref (caps);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_dvd_spu_subpic_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
|
||||
{
|
||||
|
|
|
@ -71,10 +71,6 @@ struct SpuState {
|
|||
|
||||
GstVideoInfo info;
|
||||
|
||||
guint32 *comp_bufs[3]; /* Compositing buffers for U+V & A */
|
||||
guint16 comp_left;
|
||||
guint16 comp_right;
|
||||
|
||||
SpuVobsubState vobsub;
|
||||
SpuPgsState pgs;
|
||||
};
|
||||
|
@ -116,6 +112,9 @@ struct _GstDVDSpu {
|
|||
|
||||
/* Buffer to push after handling a DVD event, if any */
|
||||
GstBuffer *pending_frame;
|
||||
|
||||
/* Overlay composition */
|
||||
gboolean attach_compo_to_buffer;
|
||||
};
|
||||
|
||||
struct _GstDVDSpuClass {
|
||||
|
|
|
@ -39,19 +39,21 @@ struct SpuRect {
|
|||
gint16 bottom;
|
||||
};
|
||||
|
||||
/* Store a pre-multiplied colour value. The YUV fields hold the YUV values
|
||||
* multiplied by the 8-bit alpha, to save computing it while rendering */
|
||||
/* Store a pre-multiplied YUV colour value */
|
||||
struct SpuColour {
|
||||
guint16 Y;
|
||||
guint16 U;
|
||||
guint16 V;
|
||||
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
||||
guint8 A;
|
||||
guint8 Y;
|
||||
guint8 U;
|
||||
guint8 V;
|
||||
#else
|
||||
guint8 V;
|
||||
guint8 U;
|
||||
guint8 Y;
|
||||
guint8 A;
|
||||
#endif
|
||||
};
|
||||
|
||||
void gstspu_clear_comp_buffers (SpuState * state);
|
||||
void gstspu_blend_comp_buffers (SpuState * state, guint8 * planes[3]);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GSTSPU_COMMON_H__ */
|
||||
|
|
|
@ -171,15 +171,19 @@ dump_rle_data (GstDVDSpu * dvdspu, guint8 * data, guint32 len)
|
|||
|
||||
static void
|
||||
pgs_composition_object_render (PgsCompositionObject * obj, SpuState * state,
|
||||
GstVideoFrame * frame)
|
||||
GstVideoFrame * window)
|
||||
{
|
||||
SpuColour *colour;
|
||||
guint8 *planes[3]; /* YUV frame pointers */
|
||||
gint strides[3];
|
||||
guint8 *pixels, *p;
|
||||
gint stride;
|
||||
gint win_w;
|
||||
gint win_h;
|
||||
guint8 *data, *end;
|
||||
guint16 obj_w;
|
||||
guint16 obj_h G_GNUC_UNUSED;
|
||||
guint x, y, i, min_x, max_x;
|
||||
guint16 obj_w, obj_h;
|
||||
gint obj_x, obj_y;
|
||||
gint min_x, max_x;
|
||||
gint min_y, max_y;
|
||||
gint x, y, i;
|
||||
|
||||
if (G_UNLIKELY (obj->rle_data == NULL || obj->rle_data_size == 0
|
||||
|| obj->rle_data_used != obj->rle_data_size))
|
||||
|
@ -191,37 +195,47 @@ pgs_composition_object_render (PgsCompositionObject * obj, SpuState * state,
|
|||
if (data + 4 > end)
|
||||
return;
|
||||
|
||||
/* FIXME: Calculate and use the cropping window for the output, as the
|
||||
* intersection of the crop rectangle for this object (if any) and the
|
||||
* window specified by the object's window_id */
|
||||
pixels = GST_VIDEO_FRAME_PLANE_DATA (window, 0);
|
||||
stride = GST_VIDEO_FRAME_PLANE_STRIDE (window, 0);
|
||||
win_w = GST_VIDEO_FRAME_WIDTH (window);
|
||||
win_h = GST_VIDEO_FRAME_HEIGHT (window);
|
||||
|
||||
/* Store the start of each plane */
|
||||
planes[0] = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
|
||||
planes[1] = GST_VIDEO_FRAME_COMP_DATA (frame, 1);
|
||||
planes[2] = GST_VIDEO_FRAME_COMP_DATA (frame, 2);
|
||||
|
||||
strides[0] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
|
||||
strides[1] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1);
|
||||
strides[2] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2);
|
||||
|
||||
y = MIN (obj->y, state->info.height);
|
||||
|
||||
planes[0] += strides[0] * y;
|
||||
planes[1] += strides[1] * (y / 2);
|
||||
planes[2] += strides[2] * (y / 2);
|
||||
|
||||
/* RLE data: */
|
||||
obj_w = GST_READ_UINT16_BE (data);
|
||||
obj_h = GST_READ_UINT16_BE (data + 2);
|
||||
data += 4;
|
||||
|
||||
min_x = MIN (obj->x, strides[0]);
|
||||
max_x = MIN (obj->x + obj_w, strides[0]);
|
||||
/* Calculate object coordinates relative to the window */
|
||||
min_x = obj_x = (gint) obj->x - (gint) state->pgs.win_x;
|
||||
min_y = obj_y = (gint) obj->y - (gint) state->pgs.win_y;
|
||||
|
||||
state->comp_left = x = min_x;
|
||||
state->comp_right = max_x;
|
||||
if (obj->flags & PGS_COMPOSITION_OBJECT_FLAG_CROPPED) {
|
||||
obj_x -= obj->crop_x;
|
||||
obj_y -= obj->crop_y;
|
||||
obj_w = MIN (obj_w, obj->crop_w);
|
||||
obj_h = MIN (obj_h, obj->crop_h);
|
||||
}
|
||||
|
||||
gstspu_clear_comp_buffers (state);
|
||||
max_x = min_x + obj_w;
|
||||
max_y = min_y + obj_h;
|
||||
|
||||
/* Early out if object is out of the window */
|
||||
if (max_x <= 0 || max_y < 0 || min_x >= win_w || min_y >= win_h)
|
||||
return;
|
||||
|
||||
/* Crop inside window */
|
||||
if (min_x < 0)
|
||||
min_x = 0;
|
||||
if (max_x > win_w)
|
||||
max_x = win_w;
|
||||
if (min_y < 0)
|
||||
min_y = 0;
|
||||
if (max_y > win_h)
|
||||
max_y = win_h;
|
||||
|
||||
/* Write RLE data to the plane */
|
||||
x = obj_x;
|
||||
y = obj_y;
|
||||
p = pixels + y * stride;
|
||||
|
||||
while (data < end) {
|
||||
guint8 pal_id;
|
||||
|
@ -264,43 +278,56 @@ pgs_composition_object_render (PgsCompositionObject * obj, SpuState * state,
|
|||
}
|
||||
}
|
||||
|
||||
if (!run_len) {
|
||||
x = obj_x;
|
||||
y++;
|
||||
if (y >= max_y)
|
||||
break;
|
||||
|
||||
p = pixels + y * stride;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (y < min_y)
|
||||
continue;
|
||||
|
||||
if (x >= max_x)
|
||||
continue;
|
||||
|
||||
if (x < min_x) {
|
||||
if (x + run_len <= min_x) {
|
||||
x += run_len;
|
||||
continue;
|
||||
} else {
|
||||
run_len -= min_x - x;
|
||||
x = min_x;
|
||||
}
|
||||
}
|
||||
|
||||
colour = &state->pgs.palette[pal_id];
|
||||
if (colour->A) {
|
||||
guint32 inv_A = 0xff - colour->A;
|
||||
|
||||
if (colour->A > 0) {
|
||||
guint8 inv_A = 255 - colour->A;
|
||||
|
||||
if (G_UNLIKELY (x + run_len > max_x))
|
||||
run_len = (max_x - x);
|
||||
run_len = max_x - x;
|
||||
|
||||
for (i = 0; i < run_len; i++) {
|
||||
planes[0][x] = (inv_A * planes[0][x] + colour->Y) / 0xff;
|
||||
SpuColour *pix = &((SpuColour *) p)[x++];
|
||||
|
||||
state->comp_bufs[0][x / 2] += colour->U;
|
||||
state->comp_bufs[1][x / 2] += colour->V;
|
||||
state->comp_bufs[2][x / 2] += colour->A;
|
||||
x++;
|
||||
if (pix->A == 0) {
|
||||
memcpy (pix, colour, sizeof (*pix));
|
||||
} else {
|
||||
pix->A = colour->A;
|
||||
pix->Y = colour->Y + pix->Y * inv_A / 255;
|
||||
pix->U = colour->U + pix->U * inv_A / 255;
|
||||
pix->V = colour->V + pix->V * inv_A / 255;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
x += run_len;
|
||||
}
|
||||
|
||||
if (!run_len || x > max_x) {
|
||||
x = min_x;
|
||||
planes[0] += strides[0];
|
||||
|
||||
if (y % 2) {
|
||||
gstspu_blend_comp_buffers (state, planes);
|
||||
gstspu_clear_comp_buffers (state);
|
||||
|
||||
planes[1] += strides[1];
|
||||
planes[2] += strides[2];
|
||||
}
|
||||
y++;
|
||||
if (y >= state->info.height)
|
||||
return; /* Hit the bottom */
|
||||
}
|
||||
}
|
||||
|
||||
if (y % 2)
|
||||
gstspu_blend_comp_buffers (state, planes);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -427,8 +454,10 @@ parse_presentation_segment (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
|
|||
"x %u y %u\n", i, obj->id, obj->win_id, obj->flags, obj->x, obj->y);
|
||||
|
||||
if (obj->flags & PGS_COMPOSITION_OBJECT_FLAG_CROPPED) {
|
||||
if (payload + 8 > end)
|
||||
if (payload + 8 > end) {
|
||||
obj->flags &= ~PGS_COMPOSITION_OBJECT_FLAG_CROPPED;
|
||||
break;
|
||||
}
|
||||
|
||||
obj->crop_x = GST_READ_UINT16_BE (payload);
|
||||
obj->crop_y = GST_READ_UINT16_BE (payload + 2);
|
||||
|
@ -495,10 +524,10 @@ parse_set_palette (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
|
|||
#endif
|
||||
|
||||
/* Premultiply the palette entries by the alpha */
|
||||
state->pgs.palette[n].Y = Y * A;
|
||||
state->pgs.palette[n].U = U * A;
|
||||
state->pgs.palette[n].V = V * A;
|
||||
state->pgs.palette[n].A = A;
|
||||
state->pgs.palette[n].Y = Y * A / 255;
|
||||
state->pgs.palette[n].U = U * A / 255;
|
||||
state->pgs.palette[n].V = V * A / 255;
|
||||
|
||||
payload += PGS_PALETTE_ENTRY_SIZE;
|
||||
}
|
||||
|
@ -761,7 +790,7 @@ gstspu_pgs_execute_event (GstDVDSpu * dvdspu)
|
|||
}
|
||||
|
||||
void
|
||||
gstspu_pgs_render (GstDVDSpu * dvdspu, GstVideoFrame * frame)
|
||||
gstspu_pgs_render (GstDVDSpu * dvdspu, GstVideoFrame * window)
|
||||
{
|
||||
SpuState *state = &dvdspu->spu_state;
|
||||
PgsPresentationSegment *ps = &state->pgs.pres_seg;
|
||||
|
@ -773,7 +802,7 @@ gstspu_pgs_render (GstDVDSpu * dvdspu, GstVideoFrame * frame)
|
|||
for (i = 0; i < ps->objects->len; i++) {
|
||||
PgsCompositionObject *cur =
|
||||
&g_array_index (ps->objects, PgsCompositionObject, i);
|
||||
pgs_composition_object_render (cur, state, frame);
|
||||
pgs_composition_object_render (cur, state, window);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -784,6 +813,27 @@ gstspu_pgs_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
gstspu_pgs_get_render_geometry (GstDVDSpu * dvdspu,
|
||||
gint * display_width, gint * display_height,
|
||||
GstVideoRectangle * window_rect)
|
||||
{
|
||||
SpuPgsState *pgs_state = &dvdspu->spu_state.pgs;
|
||||
|
||||
if (window_rect) {
|
||||
window_rect->x = pgs_state->win_x;
|
||||
window_rect->y = pgs_state->win_y;
|
||||
window_rect->w = pgs_state->win_w;
|
||||
window_rect->h = pgs_state->win_h;
|
||||
}
|
||||
|
||||
if (display_width)
|
||||
*display_width = pgs_state->pres_seg.vid_w;
|
||||
|
||||
if (display_height)
|
||||
*display_height = pgs_state->pres_seg.vid_h;
|
||||
}
|
||||
|
||||
void
|
||||
gstspu_pgs_flush (GstDVDSpu * dvdspu)
|
||||
{
|
||||
|
|
|
@ -99,8 +99,11 @@ struct SpuPgsState {
|
|||
|
||||
void gstspu_pgs_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts, GstBuffer *buf);
|
||||
gboolean gstspu_pgs_execute_event (GstDVDSpu *dvdspu);
|
||||
void gstspu_pgs_render (GstDVDSpu *dvdspu, GstVideoFrame *frame);
|
||||
void gstspu_pgs_render (GstDVDSpu *dvdspu, GstVideoFrame *window);
|
||||
gboolean gstspu_pgs_handle_dvd_event (GstDVDSpu *dvdspu, GstEvent *event);
|
||||
void gstspu_pgs_get_render_geometry (GstDVDSpu *dvdspu,
|
||||
gint *display_width, gint *display_height,
|
||||
GstVideoRectangle *window_rect);
|
||||
void gstspu_pgs_flush (GstDVDSpu *dvdspu);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -43,10 +43,10 @@ gstspu_vobsub_recalc_palette (GstDVDSpu * dvdspu,
|
|||
|
||||
/* Convert incoming 4-bit alpha to 8 bit for blending */
|
||||
dest->A = (alpha[i] << 4) | alpha[i];
|
||||
dest->Y = ((guint16) ((col >> 16) & 0xff)) * dest->A;
|
||||
dest->Y = ((col >> 16) & 0xff) * dest->A / 255;
|
||||
/* U/V are stored as V/U in the clut words, so switch them */
|
||||
dest->V = ((guint16) ((col >> 8) & 0xff)) * dest->A;
|
||||
dest->U = ((guint16) (col & 0xff)) * dest->A;
|
||||
dest->V = ((col >> 8) & 0xff) * dest->A / 255;
|
||||
dest->U = (col & 0xff) * dest->A / 255;
|
||||
}
|
||||
} else {
|
||||
int y = 240;
|
||||
|
@ -56,13 +56,13 @@ gstspu_vobsub_recalc_palette (GstDVDSpu * dvdspu,
|
|||
for (i = 0; i < 4; i++, dest++) {
|
||||
dest->A = (alpha[i] << 4) | alpha[i];
|
||||
if (alpha[i] != 0) {
|
||||
dest[0].Y = y * dest[0].A;
|
||||
dest[0].Y = y * dest[0].A / 255;
|
||||
y -= 112;
|
||||
if (y < 0)
|
||||
y = 0;
|
||||
}
|
||||
dest[0].U = 128 * dest[0].A;
|
||||
dest[0].V = 128 * dest[0].A;
|
||||
dest[0].U = 128 * dest[0].A / 255;
|
||||
dest[0].V = 128 * dest[0].A / 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,28 +169,36 @@ gstspu_vobsub_get_rle_code (SpuState * state, guint16 * rle_offset)
|
|||
}
|
||||
|
||||
static inline gboolean
|
||||
gstspu_vobsub_draw_rle_run (SpuState * state, gint16 x, gint16 end,
|
||||
SpuColour * colour)
|
||||
gstspu_vobsub_draw_rle_run (SpuState * state, GstVideoFrame * frame,
|
||||
gint16 x, gint16 end, SpuColour * colour)
|
||||
{
|
||||
#if 0
|
||||
GST_LOG ("Y: %d x: %d end %d col %d %d %d %d",
|
||||
GST_TRACE ("Y: %d x: %d end %d %d %d %d %d",
|
||||
state->vobsub.cur_Y, x, end, colour->Y, colour->U, colour->V, colour->A);
|
||||
#endif
|
||||
|
||||
if (colour->A != 0) {
|
||||
guint32 inv_A = 0xff - colour->A;
|
||||
if (colour->A > 0) {
|
||||
gint i;
|
||||
guint8 *data;
|
||||
guint8 inv_A = 255 - colour->A;
|
||||
|
||||
/* FIXME: This could be more efficient */
|
||||
while (x < end) {
|
||||
state->vobsub.out_Y[x] =
|
||||
(inv_A * state->vobsub.out_Y[x] + colour->Y) / 0xff;
|
||||
state->vobsub.out_U[x / 2] += colour->U;
|
||||
state->vobsub.out_V[x / 2] += colour->V;
|
||||
state->vobsub.out_A[x / 2] += colour->A;
|
||||
x++;
|
||||
data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
|
||||
data += GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0) *
|
||||
(state->vobsub.cur_Y - state->vobsub.disp_rect.top);
|
||||
|
||||
x -= state->vobsub.disp_rect.left;
|
||||
end -= state->vobsub.disp_rect.left;
|
||||
|
||||
for (i = x; i < end; i++) {
|
||||
SpuColour *pix = &((SpuColour *) data)[x++];
|
||||
|
||||
if (pix->A == 0) {
|
||||
memcpy (pix, colour, sizeof (*pix));
|
||||
} else {
|
||||
pix->A = colour->A;
|
||||
pix->Y = colour->Y + pix->Y * inv_A / 255;
|
||||
pix->U = colour->U + pix->U * inv_A / 255;
|
||||
pix->V = colour->V + pix->V * inv_A / 255;
|
||||
}
|
||||
}
|
||||
/* Update the compositing buffer so we know how much to blend later */
|
||||
*(state->vobsub.comp_last_x_ptr) = end - 1; /* end is the start of the *next* run */
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -208,11 +216,11 @@ rle_end_x (guint16 rle_code, gint16 x, gint16 end)
|
|||
}
|
||||
|
||||
static gboolean gstspu_vobsub_render_line_with_chgcol (SpuState * state,
|
||||
guint8 * planes[3], guint16 * rle_offset);
|
||||
GstVideoFrame * frame, guint16 * rle_offset);
|
||||
static gboolean gstspu_vobsub_update_chgcol (SpuState * state);
|
||||
|
||||
static gboolean
|
||||
gstspu_vobsub_render_line (SpuState * state, guint8 * planes[3],
|
||||
gstspu_vobsub_render_line (SpuState * state, GstVideoFrame * frame,
|
||||
guint16 * rle_offset)
|
||||
{
|
||||
gint16 x, next_x, end, rle_code, next_draw_x;
|
||||
|
@ -226,19 +234,13 @@ gstspu_vobsub_render_line (SpuState * state, guint8 * planes[3],
|
|||
/* Check the top & bottom, because we might not be within the region yet */
|
||||
if (state->vobsub.cur_Y >= state->vobsub.cur_chg_col->top &&
|
||||
state->vobsub.cur_Y <= state->vobsub.cur_chg_col->bottom) {
|
||||
return gstspu_vobsub_render_line_with_chgcol (state, planes,
|
||||
rle_offset);
|
||||
return gstspu_vobsub_render_line_with_chgcol (state, frame, rle_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* No special case. Render as normal */
|
||||
|
||||
/* Set up our output pointers */
|
||||
state->vobsub.out_Y = planes[0];
|
||||
state->vobsub.out_U = state->comp_bufs[0];
|
||||
state->vobsub.out_V = state->comp_bufs[1];
|
||||
state->vobsub.out_A = state->comp_bufs[2];
|
||||
/* We always need to start our RLE decoding byte_aligned */
|
||||
*rle_offset = GST_ROUND_UP_2 (*rle_offset);
|
||||
|
||||
|
@ -249,12 +251,10 @@ gstspu_vobsub_render_line (SpuState * state, guint8 * planes[3],
|
|||
colour = &state->vobsub.main_pal[rle_code & 3];
|
||||
next_x = rle_end_x (rle_code, x, end);
|
||||
next_draw_x = next_x;
|
||||
if (next_draw_x > state->vobsub.clip_rect.right)
|
||||
next_draw_x = state->vobsub.clip_rect.right; /* ensure no overflow */
|
||||
if (next_draw_x > state->vobsub.disp_rect.right)
|
||||
next_draw_x = state->vobsub.disp_rect.right; /* ensure no overflow */
|
||||
/* Now draw the run between [x,next_x) */
|
||||
if (state->vobsub.cur_Y >= state->vobsub.clip_rect.top &&
|
||||
state->vobsub.cur_Y <= state->vobsub.clip_rect.bottom)
|
||||
visible |= gstspu_vobsub_draw_rle_run (state, x, next_draw_x, colour);
|
||||
visible |= gstspu_vobsub_draw_rle_run (state, frame, x, next_draw_x, colour);
|
||||
x = next_x;
|
||||
}
|
||||
|
||||
|
@ -289,7 +289,7 @@ gstspu_vobsub_update_chgcol (SpuState * state)
|
|||
}
|
||||
|
||||
static gboolean
|
||||
gstspu_vobsub_render_line_with_chgcol (SpuState * state, guint8 * planes[3],
|
||||
gstspu_vobsub_render_line_with_chgcol (SpuState * state, GstVideoFrame * frame,
|
||||
guint16 * rle_offset)
|
||||
{
|
||||
SpuVobsubLineCtrlI *chg_col = state->vobsub.cur_chg_col;
|
||||
|
@ -304,11 +304,6 @@ gstspu_vobsub_render_line_with_chgcol (SpuState * state, guint8 * planes[3],
|
|||
gint16 cur_reg_end;
|
||||
gint i;
|
||||
|
||||
state->vobsub.out_Y = planes[0];
|
||||
state->vobsub.out_U = state->comp_bufs[0];
|
||||
state->vobsub.out_V = state->comp_bufs[1];
|
||||
state->vobsub.out_A = state->comp_bufs[2];
|
||||
|
||||
/* We always need to start our RLE decoding byte_aligned */
|
||||
*rle_offset = GST_ROUND_UP_2 (*rle_offset);
|
||||
|
||||
|
@ -344,12 +339,13 @@ gstspu_vobsub_render_line_with_chgcol (SpuState * state, guint8 * planes[3],
|
|||
run_end = MIN (next_x, cur_reg_end);
|
||||
|
||||
run_draw_end = run_end;
|
||||
if (run_draw_end > state->vobsub.clip_rect.right)
|
||||
run_draw_end = state->vobsub.clip_rect.right; /* ensure no overflow */
|
||||
if (run_draw_end > state->vobsub.disp_rect.right)
|
||||
run_draw_end = state->vobsub.disp_rect.right; /* ensure no overflow */
|
||||
|
||||
if (G_LIKELY (x < run_end)) {
|
||||
colour = &cur_pix_ctrl->pal_cache[rle_code & 3];
|
||||
visible |= gstspu_vobsub_draw_rle_run (state, x, run_draw_end, colour);
|
||||
visible |= gstspu_vobsub_draw_rle_run (state, frame, x,
|
||||
run_draw_end, colour);
|
||||
x = run_end;
|
||||
}
|
||||
|
||||
|
@ -369,52 +365,37 @@ gstspu_vobsub_render_line_with_chgcol (SpuState * state, guint8 * planes[3],
|
|||
return visible;
|
||||
}
|
||||
|
||||
static void
|
||||
gstspu_vobsub_blend_comp_buffers (SpuState * state, guint8 * planes[3])
|
||||
{
|
||||
state->comp_left = state->vobsub.disp_rect.left;
|
||||
state->comp_right =
|
||||
MAX (state->vobsub.comp_last_x[0], state->vobsub.comp_last_x[1]);
|
||||
|
||||
state->comp_left = MAX (state->comp_left, state->vobsub.clip_rect.left);
|
||||
state->comp_right = MIN (state->comp_right, state->vobsub.clip_rect.right);
|
||||
|
||||
gstspu_blend_comp_buffers (state, planes);
|
||||
}
|
||||
|
||||
static void
|
||||
gstspu_vobsub_clear_comp_buffers (SpuState * state)
|
||||
{
|
||||
state->comp_left = state->vobsub.clip_rect.left;
|
||||
state->comp_right = state->vobsub.clip_rect.right;
|
||||
|
||||
gstspu_clear_comp_buffers (state);
|
||||
|
||||
state->vobsub.comp_last_x[0] = -1;
|
||||
state->vobsub.comp_last_x[1] = -1;
|
||||
}
|
||||
|
||||
static void
|
||||
gstspu_vobsub_draw_highlight (SpuState * state,
|
||||
GstVideoFrame * frame, SpuRect * rect)
|
||||
{
|
||||
guint8 *cur;
|
||||
SpuColour *cur;
|
||||
SpuRect r;
|
||||
guint8 *data;
|
||||
guint stride;
|
||||
gint16 pos;
|
||||
gint ystride;
|
||||
|
||||
ystride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
|
||||
r.left = rect->left - state->vobsub.disp_rect.left;
|
||||
r.right = rect->right - state->vobsub.disp_rect.left;
|
||||
r.top = rect->top - state->vobsub.disp_rect.top;
|
||||
r.bottom = rect->bottom - state->vobsub.disp_rect.top;
|
||||
rect = &r;
|
||||
|
||||
cur = GST_VIDEO_FRAME_COMP_DATA (frame, 0) + ystride * rect->top;
|
||||
for (pos = rect->left + 1; pos < rect->right; pos++)
|
||||
cur[pos] = (cur[pos] / 2) + 0x8;
|
||||
cur = GST_VIDEO_FRAME_COMP_DATA (frame, 0) + ystride * rect->bottom;
|
||||
for (pos = rect->left + 1; pos < rect->right; pos++)
|
||||
cur[pos] = (cur[pos] / 2) + 0x8;
|
||||
cur = GST_VIDEO_FRAME_COMP_DATA (frame, 0) + ystride * rect->top;
|
||||
for (pos = rect->top; pos <= rect->bottom; pos++) {
|
||||
cur[rect->left] = (cur[rect->left] / 2) + 0x8;
|
||||
cur[rect->right] = (cur[rect->right] / 2) + 0x8;
|
||||
cur += ystride;
|
||||
data = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
|
||||
stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
|
||||
|
||||
cur = (SpuColour *) (data + stride * rect->top);
|
||||
for (pos = rect->left; pos < rect->right; pos++)
|
||||
cur[pos].A = 0x80;
|
||||
|
||||
cur = (SpuColour *) (data + stride * (rect->bottom - 1));
|
||||
for (pos = rect->left; pos < rect->right; pos++)
|
||||
cur[pos].A = 0x80;
|
||||
|
||||
for (pos = rect->top; pos < rect->bottom; pos++) {
|
||||
cur = (SpuColour *) (data + stride * pos);
|
||||
cur[rect->left].A = 0x80;
|
||||
cur[rect->right - 1].A = 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -422,10 +403,8 @@ void
|
|||
gstspu_vobsub_render (GstDVDSpu * dvdspu, GstVideoFrame * frame)
|
||||
{
|
||||
SpuState *state = &dvdspu->spu_state;
|
||||
guint8 *planes[3]; /* YUV frame pointers */
|
||||
gint y, last_y;
|
||||
gint width, height;
|
||||
gint strides[3];
|
||||
guint16 cur_offsets[2];
|
||||
|
||||
/* Set up our initial state */
|
||||
if (G_UNLIKELY (state->vobsub.pix_buf == NULL))
|
||||
|
@ -435,18 +414,6 @@ gstspu_vobsub_render (GstDVDSpu * dvdspu, GstVideoFrame * frame)
|
|||
GST_MAP_READ))
|
||||
return;
|
||||
|
||||
/* Store the start of each plane */
|
||||
planes[0] = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
|
||||
planes[1] = GST_VIDEO_FRAME_COMP_DATA (frame, 1);
|
||||
planes[2] = GST_VIDEO_FRAME_COMP_DATA (frame, 2);
|
||||
|
||||
strides[0] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
|
||||
strides[1] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1);
|
||||
strides[2] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2);
|
||||
|
||||
width = GST_VIDEO_FRAME_WIDTH (frame);
|
||||
height = GST_VIDEO_FRAME_HEIGHT (frame);
|
||||
|
||||
GST_DEBUG_OBJECT (dvdspu,
|
||||
"Rendering SPU. disp_rect %d,%d to %d,%d. hl_rect %d,%d to %d,%d",
|
||||
state->vobsub.disp_rect.left, state->vobsub.disp_rect.top,
|
||||
|
@ -454,13 +421,6 @@ gstspu_vobsub_render (GstDVDSpu * dvdspu, GstVideoFrame * frame)
|
|||
state->vobsub.hl_rect.left, state->vobsub.hl_rect.top,
|
||||
state->vobsub.hl_rect.right, state->vobsub.hl_rect.bottom);
|
||||
|
||||
GST_DEBUG_OBJECT (dvdspu, "video size %d,%d", width, height);
|
||||
|
||||
/* When reading RLE data, we track the offset in nibbles... */
|
||||
state->vobsub.cur_offsets[0] = state->vobsub.pix_data[0] * 2;
|
||||
state->vobsub.cur_offsets[1] = state->vobsub.pix_data[1] * 2;
|
||||
state->vobsub.max_offset = state->vobsub.pix_buf_map.size * 2;
|
||||
|
||||
/* Update all the palette caches */
|
||||
gstspu_vobsub_update_palettes (dvdspu, state);
|
||||
|
||||
|
@ -475,140 +435,25 @@ gstspu_vobsub_render (GstDVDSpu * dvdspu, GstVideoFrame * frame)
|
|||
} else
|
||||
state->vobsub.cur_chg_col = NULL;
|
||||
|
||||
state->vobsub.clip_rect.left = state->vobsub.disp_rect.left;
|
||||
state->vobsub.clip_rect.right = state->vobsub.disp_rect.right;
|
||||
|
||||
/* center the image when display rectangle exceeds the video width */
|
||||
if (width <= state->vobsub.disp_rect.right) {
|
||||
gint left, disp_width;
|
||||
|
||||
disp_width = state->vobsub.disp_rect.right - state->vobsub.disp_rect.left
|
||||
+ 1;
|
||||
left = (width - disp_width) / 2;
|
||||
state->vobsub.disp_rect.left = left;
|
||||
state->vobsub.disp_rect.right = left + disp_width - 1;
|
||||
|
||||
/* if it clips to the right, shift it left, but only till zero */
|
||||
if (state->vobsub.disp_rect.right >= width) {
|
||||
gint shift = state->vobsub.disp_rect.right - width - 1;
|
||||
if (shift > state->vobsub.disp_rect.left)
|
||||
shift = state->vobsub.disp_rect.left;
|
||||
state->vobsub.disp_rect.left -= shift;
|
||||
state->vobsub.disp_rect.right -= shift;
|
||||
}
|
||||
|
||||
/* init clip to disp */
|
||||
state->vobsub.clip_rect.left = state->vobsub.disp_rect.left;
|
||||
state->vobsub.clip_rect.right = state->vobsub.disp_rect.right;
|
||||
|
||||
/* clip right after the shift */
|
||||
if (state->vobsub.clip_rect.right >= width)
|
||||
state->vobsub.clip_rect.right = width - 1;
|
||||
|
||||
GST_DEBUG_OBJECT (dvdspu,
|
||||
"clipping width to %d,%d", state->vobsub.clip_rect.left,
|
||||
state->vobsub.clip_rect.right);
|
||||
}
|
||||
|
||||
/* for the height, bring it up till it fits as well as it can. We
|
||||
* assume the picture is in the lower part. We should better check where it
|
||||
* is and do something more clever. */
|
||||
state->vobsub.clip_rect.top = state->vobsub.disp_rect.top;
|
||||
state->vobsub.clip_rect.bottom = state->vobsub.disp_rect.bottom;
|
||||
if (height <= state->vobsub.disp_rect.bottom) {
|
||||
|
||||
/* shift it up, but only till zero */
|
||||
gint shift = state->vobsub.disp_rect.bottom - height - 1;
|
||||
if (shift > state->vobsub.disp_rect.top)
|
||||
shift = state->vobsub.disp_rect.top;
|
||||
state->vobsub.disp_rect.top -= shift;
|
||||
state->vobsub.disp_rect.bottom -= shift;
|
||||
|
||||
/* start on even line */
|
||||
if (state->vobsub.disp_rect.top & 1) {
|
||||
state->vobsub.disp_rect.top--;
|
||||
state->vobsub.disp_rect.bottom--;
|
||||
}
|
||||
|
||||
/* init clip to disp */
|
||||
state->vobsub.clip_rect.top = state->vobsub.disp_rect.top;
|
||||
state->vobsub.clip_rect.bottom = state->vobsub.disp_rect.bottom;
|
||||
|
||||
/* clip right after the shift */
|
||||
if (state->vobsub.clip_rect.bottom >= height)
|
||||
state->vobsub.clip_rect.bottom = height - 1;
|
||||
|
||||
GST_DEBUG_OBJECT (dvdspu,
|
||||
"clipping height to %d,%d", state->vobsub.clip_rect.top,
|
||||
state->vobsub.clip_rect.bottom);
|
||||
}
|
||||
|
||||
/* We start rendering from the first line of the display rect */
|
||||
y = state->vobsub.disp_rect.top;
|
||||
/* start_y is always an even number and we render lines in pairs from there,
|
||||
* accumulating 2 lines of chroma then blending it. We might need to render a
|
||||
* single line at the end if the display rect ends on an even line too. */
|
||||
last_y = (state->vobsub.disp_rect.bottom - 1) & ~(0x01);
|
||||
last_y = state->vobsub.disp_rect.bottom;
|
||||
|
||||
/* Update our plane references to the first line of the disp_rect */
|
||||
planes[0] += strides[0] * y;
|
||||
planes[1] += strides[1] * (y / 2);
|
||||
planes[2] += strides[2] * (y / 2);
|
||||
|
||||
for (state->vobsub.cur_Y = y; state->vobsub.cur_Y <= last_y;
|
||||
state->vobsub.cur_Y++) {
|
||||
gboolean clip, visible = FALSE;
|
||||
|
||||
clip = (state->vobsub.cur_Y < state->vobsub.clip_rect.top
|
||||
|| state->vobsub.cur_Y > state->vobsub.clip_rect.bottom);
|
||||
|
||||
/* Reset the compositing buffer */
|
||||
gstspu_vobsub_clear_comp_buffers (state);
|
||||
/* Render even line */
|
||||
state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x;
|
||||
gstspu_vobsub_render_line (state, planes, &state->vobsub.cur_offsets[0]);
|
||||
|
||||
/* Advance the luminance output pointer */
|
||||
planes[0] += strides[0];
|
||||
|
||||
state->vobsub.cur_Y++;
|
||||
|
||||
/* Render odd line */
|
||||
state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x + 1;
|
||||
visible |=
|
||||
gstspu_vobsub_render_line (state, planes,
|
||||
&state->vobsub.cur_offsets[1]);
|
||||
|
||||
if (visible && !clip) {
|
||||
/* Blend the accumulated UV compositing buffers onto the output */
|
||||
gstspu_vobsub_blend_comp_buffers (state, planes);
|
||||
}
|
||||
|
||||
/* Update all the output pointers */
|
||||
planes[0] += strides[0];
|
||||
planes[1] += strides[1];
|
||||
planes[2] += strides[2];
|
||||
/* When reading RLE data, we track the offset in nibbles... */
|
||||
state->vobsub.max_offset = state->vobsub.pix_buf_map.size * 2;
|
||||
if (y & 1) {
|
||||
cur_offsets[1] = state->vobsub.pix_data[0] * 2;
|
||||
cur_offsets[0] = state->vobsub.pix_data[1] * 2;
|
||||
} else {
|
||||
cur_offsets[0] = state->vobsub.pix_data[0] * 2;
|
||||
cur_offsets[1] = state->vobsub.pix_data[1] * 2;
|
||||
}
|
||||
|
||||
if (state->vobsub.cur_Y == state->vobsub.disp_rect.bottom) {
|
||||
gboolean clip, visible = FALSE;
|
||||
|
||||
clip = (state->vobsub.cur_Y < state->vobsub.clip_rect.top
|
||||
|| state->vobsub.cur_Y > state->vobsub.clip_rect.bottom);
|
||||
|
||||
g_assert ((state->vobsub.disp_rect.bottom & 0x01) == 0);
|
||||
|
||||
if (!clip) {
|
||||
/* Render a remaining lone last even line. y already has the correct value
|
||||
* after the above loop exited. */
|
||||
gstspu_vobsub_clear_comp_buffers (state);
|
||||
state->vobsub.comp_last_x_ptr = state->vobsub.comp_last_x;
|
||||
visible |=
|
||||
gstspu_vobsub_render_line (state, planes,
|
||||
&state->vobsub.cur_offsets[0]);
|
||||
if (visible)
|
||||
gstspu_vobsub_blend_comp_buffers (state, planes);
|
||||
}
|
||||
/* Render line by line */
|
||||
for (state->vobsub.cur_Y = y; state->vobsub.cur_Y <= last_y;
|
||||
state->vobsub.cur_Y++) {
|
||||
gstspu_vobsub_render_line (state, frame,
|
||||
&cur_offsets[state->vobsub.cur_Y & 1]);
|
||||
}
|
||||
|
||||
/* for debugging purposes, draw a faint rectangle at the edges of the disp_rect */
|
||||
|
|
|
@ -492,6 +492,29 @@ gstspu_vobsub_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event)
|
|||
return hl_change;
|
||||
}
|
||||
|
||||
void
|
||||
gstspu_vobsub_get_render_geometry (GstDVDSpu * dvdspu,
|
||||
gint * display_width, gint * display_height,
|
||||
GstVideoRectangle * window_rect)
|
||||
{
|
||||
SpuState *state = &dvdspu->spu_state;
|
||||
|
||||
if (window_rect) {
|
||||
window_rect->x = state->vobsub.disp_rect.left;
|
||||
window_rect->y = state->vobsub.disp_rect.top;
|
||||
window_rect->w = state->vobsub.disp_rect.right -
|
||||
state->vobsub.disp_rect.left + 1;
|
||||
window_rect->h = state->vobsub.disp_rect.bottom -
|
||||
state->vobsub.disp_rect.top + 1;
|
||||
}
|
||||
|
||||
if (display_width)
|
||||
*display_width = state->info.width;
|
||||
|
||||
if (display_height)
|
||||
*display_height = state->info.height;
|
||||
}
|
||||
|
||||
void
|
||||
gstspu_vobsub_flush (GstDVDSpu * dvdspu)
|
||||
{
|
||||
|
|
|
@ -55,7 +55,6 @@ struct SpuVobsubState {
|
|||
GstMapInfo pix_buf_map; /* Mapped buffer info */
|
||||
|
||||
SpuRect disp_rect;
|
||||
SpuRect clip_rect;
|
||||
SpuRect hl_rect;
|
||||
|
||||
guint32 current_clut[16]; /* Colour lookup table from incoming events */
|
||||
|
@ -81,31 +80,25 @@ struct SpuVobsubState {
|
|||
* need recalculating */
|
||||
|
||||
/* Rendering state vars below */
|
||||
gint16 comp_last_x[2]; /* Maximum X values we rendered into the comp buffer (odd & even) */
|
||||
gint16 *comp_last_x_ptr; /* Ptr to the current comp_last_x value to be updated by the render */
|
||||
|
||||
/* Current Y Position */
|
||||
gint16 cur_Y;
|
||||
|
||||
/* Current offset in nibbles into the pix_data */
|
||||
guint16 cur_offsets[2];
|
||||
guint16 max_offset;
|
||||
|
||||
/* current ChgColCon Line Info */
|
||||
SpuVobsubLineCtrlI *cur_chg_col;
|
||||
SpuVobsubLineCtrlI *cur_chg_col_end;
|
||||
|
||||
/* Output position tracking */
|
||||
guint8 *out_Y;
|
||||
guint32 *out_U;
|
||||
guint32 *out_V;
|
||||
guint32 *out_A;
|
||||
};
|
||||
|
||||
void gstspu_vobsub_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts, GstBuffer *buf);
|
||||
gboolean gstspu_vobsub_execute_event (GstDVDSpu *dvdspu);
|
||||
void gstspu_vobsub_render (GstDVDSpu *dvdspu, GstVideoFrame *frame);
|
||||
gboolean gstspu_vobsub_handle_dvd_event (GstDVDSpu *dvdspu, GstEvent *event);
|
||||
void gstspu_vobsub_get_render_geometry (GstDVDSpu *dvdspu,
|
||||
gint *display_width, gint *display_height,
|
||||
GstVideoRectangle *window_rect);
|
||||
void gstspu_vobsub_flush (GstDVDSpu *dvdspu);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue