mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-26 17:18:15 +00:00
katedec: create SPU format images from suitable background images
Make katedec fabricate dvd-style subpictures for subtitle overlay bitmaps, for easier playbin2 integration (#588638).
This commit is contained in:
parent
265b75ca38
commit
7ae5022cee
6 changed files with 990 additions and 584 deletions
|
@ -3,7 +3,7 @@
|
|||
plugin_LTLIBRARIES = libgstkate.la
|
||||
|
||||
# sources used to compile this plug-in
|
||||
libgstkate_la_SOURCES = gstkate.c gstkatedec.c gstkateenc.c gstkateparse.c gstkatetag.c gstkateutil.c
|
||||
libgstkate_la_SOURCES = gstkate.c gstkatedec.c gstkateenc.c gstkateparse.c gstkatetag.c gstkateutil.c gstkatespu.c
|
||||
if USE_TIGER
|
||||
libgstkate_la_SOURCES += gstkatetiger.c
|
||||
endif
|
||||
|
@ -15,6 +15,6 @@ libgstkate_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
|||
libgstkate_la_LIBTOOLFLAGS = --tag=disable-static
|
||||
|
||||
# headers we need but don't want installed
|
||||
noinst_HEADERS = gstkate.h gstkatedec.h gstkateenc.h gstkateparse.h gstkatetag.h gstkateutil.h gstkatetiger.h
|
||||
noinst_HEADERS = gstkate.h gstkatedec.h gstkateenc.h gstkateparse.h gstkatetag.h gstkateutil.h gstkatespu.h gstkatetiger.h
|
||||
|
||||
EXTRA_DIST=README
|
||||
|
|
|
@ -9,7 +9,7 @@ http://libtiger.googlecode.com/
|
|||
|
||||
The Kate plugin contains various elements to manipulate Kate streams:
|
||||
|
||||
- katedec: decodes Kate streams to text
|
||||
- katedec: decodes Kate streams to text and SPU images
|
||||
- kateenc: encodes Kate streams from text and SPU images
|
||||
- kateparse: parses Kate streams
|
||||
- katetag: allows changing metadata in Kate streams
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* GStreamer
|
||||
* Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
||||
* Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||
* Copyright 2008 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
|
||||
* Copyright 2008, 2009 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
|
@ -85,6 +85,7 @@
|
|||
#include <gst/gst.h>
|
||||
|
||||
#include "gstkate.h"
|
||||
#include "gstkatespu.h"
|
||||
#include "gstkatedec.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (gst_katedec_debug);
|
||||
|
@ -111,7 +112,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
|
|||
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("text/plain; text/x-pango-markup")
|
||||
GST_STATIC_CAPS ("text/plain; text/x-pango-markup; " GST_KATE_SPU_MIME_TYPE)
|
||||
);
|
||||
|
||||
GST_BOILERPLATE (GstKateDec, gst_kate_dec, GstElement, GST_TYPE_ELEMENT);
|
||||
|
@ -250,6 +251,7 @@ gst_kate_dec_chain (GstPad * pad, GstBuffer * buf)
|
|||
gchar *escaped;
|
||||
GstBuffer *buffer;
|
||||
size_t len;
|
||||
gboolean plain = TRUE;
|
||||
|
||||
if (kd->remove_markup && ev->text_markup_type != kate_markup_none) {
|
||||
size_t len0 = ev->len + 1;
|
||||
|
@ -257,26 +259,64 @@ gst_kate_dec_chain (GstPad * pad, GstBuffer * buf)
|
|||
if (escaped) {
|
||||
kate_text_remove_markup (ev->text_encoding, escaped, &len0);
|
||||
}
|
||||
plain = TRUE;
|
||||
} else if (ev->text_markup_type == kate_markup_none) {
|
||||
/* no pango markup yet, escape text */
|
||||
/* TODO: actually do the pango thing */
|
||||
escaped = g_markup_printf_escaped ("%s", ev->text);
|
||||
plain = TRUE;
|
||||
} else {
|
||||
escaped = g_strdup (ev->text);
|
||||
plain = FALSE;
|
||||
}
|
||||
|
||||
if (G_LIKELY (escaped)) {
|
||||
len = strlen (escaped);
|
||||
GST_DEBUG_OBJECT (kd, "kate event: %s, escaped %s", ev->text, escaped);
|
||||
buffer = gst_buffer_new_and_alloc (len + 1);
|
||||
if (G_LIKELY (buffer)) {
|
||||
/* allocate and copy the NULs, but don't include them in passed size */
|
||||
memcpy (GST_BUFFER_DATA (buffer), escaped, len + 1);
|
||||
GST_BUFFER_SIZE (buffer) = len;
|
||||
if (len > 0) {
|
||||
GST_DEBUG_OBJECT (kd, "kate event: %s, escaped %s", ev->text, escaped);
|
||||
buffer = gst_buffer_new_and_alloc (len + 1);
|
||||
if (G_LIKELY (buffer)) {
|
||||
const char *mime = plain ? "text/plain" : "text/x-pango-markup";
|
||||
GstCaps *caps = gst_caps_new_simple (mime, NULL);
|
||||
gst_buffer_set_caps (buffer, caps);
|
||||
gst_caps_unref (caps);
|
||||
/* allocate and copy the NULs, but don't include them in passed size */
|
||||
memcpy (GST_BUFFER_DATA (buffer), escaped, len + 1);
|
||||
GST_BUFFER_SIZE (buffer) = len;
|
||||
GST_BUFFER_TIMESTAMP (buffer) = ev->start_time * GST_SECOND;
|
||||
GST_BUFFER_DURATION (buffer) =
|
||||
(ev->end_time - ev->start_time) * GST_SECOND;
|
||||
rflow = gst_pad_push (kd->srcpad, buffer);
|
||||
if (rflow == GST_FLOW_NOT_LINKED) {
|
||||
GST_DEBUG_OBJECT (kd, "source pad not linked, ignored");
|
||||
} else if (rflow != GST_FLOW_OK) {
|
||||
GST_WARNING_OBJECT (kd, "failed to push buffer: %s",
|
||||
gst_flow_get_name (rflow));
|
||||
}
|
||||
} else {
|
||||
GST_WARNING_OBJECT (kd, "failed to create buffer");
|
||||
rflow = GST_FLOW_ERROR;
|
||||
}
|
||||
} else {
|
||||
GST_WARNING_OBJECT (kd, "Empty string, nothing to do");
|
||||
rflow = GST_FLOW_OK;
|
||||
}
|
||||
g_free (escaped);
|
||||
} else {
|
||||
GST_WARNING_OBJECT (kd, "failed to allocate string");
|
||||
rflow = GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
// if there's a background paletted bitmap, construct a DVD SPU for it
|
||||
if (ev->bitmap && ev->palette) {
|
||||
GstBuffer *buffer = gst_kate_spu_encode_spu (kd, ev);
|
||||
if (buffer) {
|
||||
GstCaps *caps = gst_caps_new_simple (GST_KATE_SPU_MIME_TYPE, NULL);
|
||||
gst_buffer_set_caps (buffer, caps);
|
||||
gst_caps_unref (caps);
|
||||
GST_BUFFER_TIMESTAMP (buffer) = ev->start_time * GST_SECOND;
|
||||
GST_BUFFER_DURATION (buffer) =
|
||||
(ev->end_time - ev->start_time) * GST_SECOND;
|
||||
gst_buffer_set_caps (buffer, GST_PAD_CAPS (kd->srcpad));
|
||||
rflow = gst_pad_push (kd->srcpad, buffer);
|
||||
if (rflow == GST_FLOW_NOT_LINKED) {
|
||||
GST_DEBUG_OBJECT (kd, "source pad not linked, ignored");
|
||||
|
@ -285,13 +325,9 @@ gst_kate_dec_chain (GstPad * pad, GstBuffer * buf)
|
|||
gst_flow_get_name (rflow));
|
||||
}
|
||||
} else {
|
||||
GST_WARNING_OBJECT (kd, "failed to create buffer");
|
||||
GST_WARNING_OBJECT (kd, "failed to create SPU from paletted bitmap");
|
||||
rflow = GST_FLOW_ERROR;
|
||||
}
|
||||
g_free (escaped);
|
||||
} else {
|
||||
GST_WARNING_OBJECT (kd, "failed to allocate string");
|
||||
rflow = GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
||||
* Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||
* Copyright (C) 2007 Fluendo S.A. <info@fluendo.com>
|
||||
* Copyright 2008 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
|
||||
* Copyright 2008, 2009 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
|
@ -82,6 +82,7 @@
|
|||
|
||||
#include "gstkate.h"
|
||||
#include "gstkateutil.h"
|
||||
#include "gstkatespu.h"
|
||||
#include "gstkateenc.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (gst_kateenc_debug);
|
||||
|
@ -108,24 +109,9 @@ enum
|
|||
ARG_DEFAULT_SPU_DURATION,
|
||||
};
|
||||
|
||||
/* taken off the dvdsubdec element */
|
||||
static const guint32 gst_kate_enc_default_clut[16] = {
|
||||
0xb48080, 0x248080, 0x628080, 0xd78080,
|
||||
0x808080, 0x808080, 0x808080, 0x808080,
|
||||
0x808080, 0x808080, 0x808080, 0x808080,
|
||||
0x808080, 0x808080, 0x808080, 0x808080
|
||||
};
|
||||
|
||||
#define GST_KATE_UINT16_BE(ptr) ( ( ((guint16)((ptr)[0])) <<8) | ((ptr)[1]) )
|
||||
|
||||
/* taken off the DVD SPU decoder - now is time for today's WTF ???? */
|
||||
#define GST_KATE_STM_TO_GST(stm) ((GST_MSECOND * 1024 * (stm)) / 90)
|
||||
|
||||
#define DEFAULT_KEEPALIVE_MIN_TIME 2.5f
|
||||
#define DEFAULT_DEFAULT_SPU_DURATION 1.5f
|
||||
|
||||
#define GST_KATE_SPU_MIME_TYPE "video/x-dvd-subpicture"
|
||||
|
||||
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
|
@ -280,8 +266,8 @@ gst_kate_enc_init (GstKateEnc * ke, GstKateEncClass * gclass)
|
|||
ke->original_canvas_height = 0;
|
||||
ke->keepalive_min_time = DEFAULT_KEEPALIVE_MIN_TIME;
|
||||
ke->default_spu_duration = DEFAULT_DEFAULT_SPU_DURATION;
|
||||
memcpy (ke->spu_clut, gst_kate_enc_default_clut,
|
||||
sizeof (gst_kate_enc_default_clut));
|
||||
memcpy (ke->spu_clut, gst_kate_spu_default_clut,
|
||||
sizeof (gst_kate_spu_default_clut));
|
||||
ke->delayed_spu = FALSE;
|
||||
ke->delayed_bitmap = NULL;
|
||||
ke->delayed_palette = NULL;
|
||||
|
@ -531,6 +517,8 @@ gst_kate_enc_is_simple_subtitle_category (GstKateEnc * ke, const char *category)
|
|||
static const char *const simple[] = {
|
||||
"subtitles",
|
||||
"SUB",
|
||||
"spu-subtitles",
|
||||
"K-SPU",
|
||||
};
|
||||
int n;
|
||||
|
||||
|
@ -634,554 +622,6 @@ gst_kate_enc_flush_headers (GstKateEnc * ke)
|
|||
return rflow;
|
||||
}
|
||||
|
||||
enum SpuCmd
|
||||
{
|
||||
SPU_CMD_FSTA_DSP = 0x00, /* Forced Display */
|
||||
SPU_CMD_DSP = 0x01, /* Display Start */
|
||||
SPU_CMD_STP_DSP = 0x02, /* Display Off */
|
||||
SPU_CMD_SET_COLOR = 0x03, /* Set the color indexes for the palette */
|
||||
SPU_CMD_SET_ALPHA = 0x04, /* Set the alpha indexes for the palette */
|
||||
SPU_CMD_SET_DAREA = 0x05, /* Set the display area for the SPU */
|
||||
SPU_CMD_DSPXA = 0x06, /* Pixel data addresses */
|
||||
SPU_CMD_CHG_COLCON = 0x07, /* Change Color & Contrast */
|
||||
SPU_CMD_END = 0xff
|
||||
};
|
||||
|
||||
static void
|
||||
gst_kate_enc_decode_colormap (GstKateEnc * ke, const guint8 * ptr)
|
||||
{
|
||||
ke->spu_colormap[3] = ptr[0] >> 4;
|
||||
ke->spu_colormap[2] = ptr[0] & 0x0f;
|
||||
ke->spu_colormap[1] = ptr[1] >> 4;
|
||||
ke->spu_colormap[0] = ptr[1] & 0x0f;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_kate_enc_decode_alpha (GstKateEnc * ke, const guint8 * ptr)
|
||||
{
|
||||
ke->spu_alpha[3] = ptr[0] >> 4;
|
||||
ke->spu_alpha[2] = ptr[0] & 0x0f;
|
||||
ke->spu_alpha[1] = ptr[1] >> 4;
|
||||
ke->spu_alpha[0] = ptr[1] & 0x0f;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_kate_enc_decode_area (GstKateEnc * ke, const guint8 * ptr)
|
||||
{
|
||||
ke->spu_left = ((((guint16) ptr[0]) & 0x3f) << 4) | (ptr[1] >> 4);
|
||||
ke->spu_top = ((((guint16) ptr[3]) & 0x3f) << 4) | (ptr[4] >> 4);
|
||||
ke->spu_right = ((((guint16) ptr[1]) & 0x03) << 8) | ptr[2];
|
||||
ke->spu_bottom = ((((guint16) ptr[4]) & 0x03) << 8) | ptr[5];
|
||||
GST_DEBUG_OBJECT (ke, "SPU area %u %u -> %u %d", ke->spu_left, ke->spu_top,
|
||||
ke->spu_right, ke->spu_bottom);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_kate_enc_decode_pixaddr (GstKateEnc * ke, const guint8 * ptr)
|
||||
{
|
||||
ke->spu_pix_data[0] = GST_KATE_UINT16_BE (ptr + 0);
|
||||
ke->spu_pix_data[1] = GST_KATE_UINT16_BE (ptr + 2);
|
||||
}
|
||||
|
||||
/* heavily inspired from dvdspudec */
|
||||
static guint16
|
||||
gst_kate_enc_decode_colcon (GstKateEnc * ke, const guint8 * ptr)
|
||||
{
|
||||
guint16 nbytes = GST_KATE_UINT16_BE (ptr + 0);
|
||||
guint16 nbytes_left = nbytes;
|
||||
|
||||
GST_LOG_OBJECT (ke, "Number of bytes in color/contrast change command is %u",
|
||||
nbytes);
|
||||
if (G_UNLIKELY (nbytes < 2)) {
|
||||
GST_WARNING_OBJECT (ke,
|
||||
"Number of bytes in color/contrast change command is %u, should be at least 2",
|
||||
nbytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ptr += 2;
|
||||
nbytes_left -= 2;
|
||||
|
||||
/* we will just skip that data for now */
|
||||
while (nbytes_left > 0) {
|
||||
guint32 entry, nchanges, sz;
|
||||
GST_LOG_OBJECT (ke, "Reading a color/contrast change entry, %u bytes left",
|
||||
nbytes_left);
|
||||
if (G_UNLIKELY (nbytes_left < 4)) {
|
||||
GST_WARNING_OBJECT (ke,
|
||||
"Not enough bytes to read a full color/contrast entry header");
|
||||
break;
|
||||
}
|
||||
entry = GST_READ_UINT32_BE (ptr);
|
||||
GST_LOG_OBJECT (ke, "Color/contrast change entry header is %08x", entry);
|
||||
nchanges = CLAMP ((ptr[2] >> 4), 1, 8);
|
||||
ptr += 4;
|
||||
nbytes_left -= 4;
|
||||
if (entry == 0x0fffffff) {
|
||||
GST_LOG_OBJECT (ke,
|
||||
"Encountered color/contrast change termination code, breaking, %u bytes left",
|
||||
nbytes_left);
|
||||
break;
|
||||
}
|
||||
GST_LOG_OBJECT (ke, "Color/contrast change entry has %u changes", nchanges);
|
||||
sz = 6 * nchanges;
|
||||
if (G_UNLIKELY (sz > nbytes_left)) {
|
||||
GST_WARNING_OBJECT (ke,
|
||||
"Not enough bytes to read a full color/contrast entry");
|
||||
break;
|
||||
}
|
||||
ptr += sz;
|
||||
nbytes_left -= sz;
|
||||
}
|
||||
return nbytes - nbytes_left;
|
||||
}
|
||||
|
||||
static inline guint8
|
||||
gst_kate_enc_get_nybble (const guint8 * nybbles, size_t * nybble_offset)
|
||||
{
|
||||
guint8 ret;
|
||||
|
||||
ret = nybbles[(*nybble_offset) / 2];
|
||||
|
||||
/* If the offset is even, we shift the answer down 4 bits, otherwise not */
|
||||
if ((*nybble_offset) & 0x01)
|
||||
ret &= 0x0f;
|
||||
else
|
||||
ret = ret >> 4;
|
||||
|
||||
(*nybble_offset)++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static guint16
|
||||
gst_kate_enc_get_rle_code (const guint8 * nybbles, size_t * nybble_offset)
|
||||
{
|
||||
guint16 code;
|
||||
|
||||
code = gst_kate_enc_get_nybble (nybbles, nybble_offset);
|
||||
if (code < 0x4) { /* 4 .. f */
|
||||
code = (code << 4) | gst_kate_enc_get_nybble (nybbles, nybble_offset);
|
||||
if (code < 0x10) { /* 1x .. 3x */
|
||||
code = (code << 4) | gst_kate_enc_get_nybble (nybbles, nybble_offset);
|
||||
if (code < 0x40) { /* 04x .. 0fx */
|
||||
code = (code << 4) | gst_kate_enc_get_nybble (nybbles, nybble_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_kate_enc_crop_bitmap (GstKateEnc * ke, kate_bitmap * kb, guint16 * dx,
|
||||
guint16 * dy)
|
||||
{
|
||||
int top, bottom, left, right;
|
||||
guint8 zero = 0;
|
||||
size_t n, x, y, w, h;
|
||||
|
||||
#if 0
|
||||
/* find the zero */
|
||||
zero = kb->pixels[0];
|
||||
for (x = 0; x < kb->width; ++x) {
|
||||
if (kb->pixels[x] != zero) {
|
||||
GST_LOG_OBJECT (ke, "top line at %u is not zero: %u", x, kb->pixels[x]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* top */
|
||||
for (top = 0; top < kb->height; ++top) {
|
||||
int empty = 1;
|
||||
for (x = 0; x < kb->width; ++x) {
|
||||
if (G_UNLIKELY (kb->pixels[x + top * kb->width] != zero)) {
|
||||
empty = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!empty)
|
||||
break;
|
||||
}
|
||||
|
||||
/* bottom */
|
||||
for (bottom = kb->height - 1; bottom >= top; --bottom) {
|
||||
int empty = 1;
|
||||
for (x = 0; x < kb->width; ++x) {
|
||||
if (G_UNLIKELY (kb->pixels[x + bottom * kb->width] != zero)) {
|
||||
empty = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!empty)
|
||||
break;
|
||||
}
|
||||
|
||||
/* left */
|
||||
for (left = 0; left < kb->width; ++left) {
|
||||
int empty = 1;
|
||||
for (y = top; y <= bottom; ++y) {
|
||||
if (G_UNLIKELY (kb->pixels[left + y * kb->width] != zero)) {
|
||||
empty = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!empty)
|
||||
break;
|
||||
}
|
||||
|
||||
/* right */
|
||||
for (right = kb->width - 1; right >= left; --right) {
|
||||
int empty = 1;
|
||||
for (y = top; y <= bottom; ++y) {
|
||||
if (G_UNLIKELY (kb->pixels[right + y * kb->width] != zero)) {
|
||||
empty = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!empty)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
w = right - left + 1;
|
||||
h = bottom - top + 1;
|
||||
GST_LOG_OBJECT (ke, "cropped from %zu %zu to %zu %zu", kb->width, kb->height,
|
||||
w, h);
|
||||
*dx += left;
|
||||
*dy += top;
|
||||
n = 0;
|
||||
for (y = 0; y < h; ++y) {
|
||||
memmove (kb->pixels + n, kb->pixels + kb->width * (y + top) + left, w);
|
||||
n += w;
|
||||
}
|
||||
kb->width = w;
|
||||
kb->height = h;
|
||||
}
|
||||
|
||||
#define CHECK(x) do { guint16 _ = (x); if (G_UNLIKELY((_) > sz)) { GST_WARNING_OBJECT (ke, "SPU overflow"); return GST_FLOW_ERROR; } } while (0)
|
||||
#define ADVANCE(x) do { guint16 _ = (x); ptr += (_); sz -= (_); } while (0)
|
||||
#define IGNORE(x) do { guint16 __ = (x); CHECK (__); ADVANCE (__); } while (0)
|
||||
|
||||
static GstFlowReturn
|
||||
gst_kate_enc_decode_command_sequence (GstKateEnc * ke, GstBuffer * buf,
|
||||
guint16 command_sequence_offset)
|
||||
{
|
||||
guint16 date;
|
||||
guint16 next_command_sequence;
|
||||
const guint8 *ptr;
|
||||
guint16 sz;
|
||||
|
||||
if (command_sequence_offset >= GST_BUFFER_SIZE (buf)) {
|
||||
GST_WARNING_OBJECT (ke, "Command sequence offset %u is out of range %u",
|
||||
command_sequence_offset, GST_BUFFER_SIZE (buf));
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
ptr = GST_BUFFER_DATA (buf) + command_sequence_offset;
|
||||
sz = GST_BUFFER_SIZE (buf) - command_sequence_offset;
|
||||
|
||||
GST_DEBUG_OBJECT (ke, "Decoding command sequence at %u (%u bytes)",
|
||||
command_sequence_offset, sz);
|
||||
|
||||
CHECK (2);
|
||||
date = GST_KATE_UINT16_BE (ptr);
|
||||
ADVANCE (2);
|
||||
GST_DEBUG_OBJECT (ke, "date %u", date);
|
||||
|
||||
CHECK (2);
|
||||
next_command_sequence = GST_KATE_UINT16_BE (ptr);
|
||||
ADVANCE (2);
|
||||
GST_DEBUG_OBJECT (ke, "next command sequence at %u", next_command_sequence);
|
||||
|
||||
while (sz) {
|
||||
guint8 cmd = *ptr++;
|
||||
switch (cmd) {
|
||||
case SPU_CMD_FSTA_DSP: /* 0x00 */
|
||||
GST_DEBUG_OBJECT (ke, "[0] DISPLAY");
|
||||
break;
|
||||
case SPU_CMD_DSP: /* 0x01 */
|
||||
GST_DEBUG_OBJECT (ke, "[1] SHOW");
|
||||
ke->show_time = date;
|
||||
break;
|
||||
case SPU_CMD_STP_DSP: /* 0x02 */
|
||||
GST_DEBUG_OBJECT (ke, "[2] HIDE");
|
||||
ke->hide_time = date;
|
||||
break;
|
||||
case SPU_CMD_SET_COLOR: /* 0x03 */
|
||||
GST_DEBUG_OBJECT (ke, "[3] SET COLOR");
|
||||
CHECK (2);
|
||||
gst_kate_enc_decode_colormap (ke, ptr);
|
||||
ADVANCE (2);
|
||||
break;
|
||||
case SPU_CMD_SET_ALPHA: /* 0x04 */
|
||||
GST_DEBUG_OBJECT (ke, "[4] SET ALPHA");
|
||||
CHECK (2);
|
||||
gst_kate_enc_decode_alpha (ke, ptr);
|
||||
ADVANCE (2);
|
||||
break;
|
||||
case SPU_CMD_SET_DAREA: /* 0x05 */
|
||||
GST_DEBUG_OBJECT (ke, "[5] SET DISPLAY AREA");
|
||||
CHECK (6);
|
||||
gst_kate_enc_decode_area (ke, ptr);
|
||||
ADVANCE (6);
|
||||
break;
|
||||
case SPU_CMD_DSPXA: /* 0x06 */
|
||||
GST_DEBUG_OBJECT (ke, "[6] SET PIXEL ADDRESSES");
|
||||
CHECK (4);
|
||||
gst_kate_enc_decode_pixaddr (ke, ptr);
|
||||
GST_DEBUG_OBJECT (ke, " -> first pixel address %u",
|
||||
ke->spu_pix_data[0]);
|
||||
GST_DEBUG_OBJECT (ke, " -> second pixel address %u",
|
||||
ke->spu_pix_data[1]);
|
||||
ADVANCE (4);
|
||||
break;
|
||||
case SPU_CMD_CHG_COLCON: /* 0x07 */
|
||||
GST_DEBUG_OBJECT (ke, "[7] CHANGE COLOR/CONTRAST");
|
||||
CHECK (2);
|
||||
ADVANCE (gst_kate_enc_decode_colcon (ke, ptr));
|
||||
break;
|
||||
case SPU_CMD_END: /* 0xff */
|
||||
GST_DEBUG_OBJECT (ke, "[0xff] END");
|
||||
if (next_command_sequence != command_sequence_offset) {
|
||||
GST_DEBUG_OBJECT (ke, "Jumping to next sequence at offset %u",
|
||||
next_command_sequence);
|
||||
return gst_kate_enc_decode_command_sequence (ke, buf,
|
||||
next_command_sequence);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (ke, "No more sequences to decode");
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
GST_WARNING_OBJECT (ke, "invalid SPU command: %u", cmd);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
static inline int
|
||||
gst_kate_enc_clamp (int value)
|
||||
{
|
||||
if (value < 0)
|
||||
return 0;
|
||||
if (value > 255)
|
||||
return 255;
|
||||
return value;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_kate_enc_yuv2rgb (int y, int u, int v, int *r, int *g, int *b)
|
||||
{
|
||||
#if 0
|
||||
*r = gst_kate_enc_clamp (y + 1.371 * v);
|
||||
*g = gst_kate_enc_clamp (y - 0.698 * v - 0.336 * u);
|
||||
*b = gst_kate_enc_clamp (y + 1.732 * u);
|
||||
#elif 0
|
||||
*r = gst_kate_enc_clamp (y + u);
|
||||
*g = gst_kate_enc_clamp (y - (76 * u - 26 * v) / 256);
|
||||
*b = gst_kate_enc_clamp (y + v);
|
||||
#else
|
||||
y = (y - 16) * 255 / 219;
|
||||
u -= 128;
|
||||
v -= 128;
|
||||
|
||||
*r = gst_kate_enc_clamp (y + 1.402 * 255 / 224 * v);
|
||||
*g = gst_kate_enc_clamp (y + 0.34414 * 255 / 224 * v -
|
||||
0.71414 * 255 / 224 * u);
|
||||
*b = gst_kate_enc_clamp (y + 1.772 * 244 / 224 * u);
|
||||
#endif
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_kate_enc_create_spu_palette (GstKateEnc * ke, kate_palette * kp)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
kate_palette_init (kp);
|
||||
kp->ncolors = 4;
|
||||
kp->colors = (kate_color *) g_malloc (kp->ncolors * sizeof (kate_color));
|
||||
if (G_UNLIKELY (!kp->colors))
|
||||
return GST_FLOW_ERROR;
|
||||
|
||||
#if 1
|
||||
for (n = 0; n < kp->ncolors; ++n) {
|
||||
int idx = ke->spu_colormap[n];
|
||||
guint32 color = ke->spu_clut[idx];
|
||||
int y = (color >> 16) & 0xff;
|
||||
int v = (color >> 8) & 0xff;
|
||||
int u = color & 0xff;
|
||||
int r, g, b;
|
||||
gst_kate_enc_yuv2rgb (y, u, v, &r, &g, &b);
|
||||
kp->colors[n].r = r;
|
||||
kp->colors[n].g = g;
|
||||
kp->colors[n].b = b;
|
||||
kp->colors[n].a = ke->spu_alpha[n] * 17;
|
||||
}
|
||||
#else
|
||||
/* just make a ramp from 0 to 255 for those non transparent colors */
|
||||
for (n = 0; n < kp->ncolors; ++n)
|
||||
if (ke->spu_alpha[n] == 0)
|
||||
++ntrans;
|
||||
|
||||
for (n = 0; n < kp->ncolors; ++n) {
|
||||
kp->colors[n].r = luma;
|
||||
kp->colors[n].g = luma;
|
||||
kp->colors[n].b = luma;
|
||||
kp->colors[n].a = ke->spu_alpha[n] * 17;
|
||||
if (ke->spu_alpha[n])
|
||||
luma /= 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_kate_enc_decode_spu (GstKateEnc * ke, GstBuffer * buf, kate_region * kr,
|
||||
kate_bitmap * kb, kate_palette * kp)
|
||||
{
|
||||
const guint8 *ptr = GST_BUFFER_DATA (buf);
|
||||
size_t sz = GST_BUFFER_SIZE (buf);
|
||||
guint16 packet_size;
|
||||
guint16 x, y;
|
||||
size_t n;
|
||||
guint8 *pixptr[2];
|
||||
size_t nybble_offset[2];
|
||||
size_t max_nybbles[2];
|
||||
GstFlowReturn rflow;
|
||||
guint16 next_command_sequence;
|
||||
guint16 code;
|
||||
|
||||
/* before decoding anything, initialize to sensible defaults */
|
||||
memset (ke->spu_colormap, 0, sizeof (ke->spu_colormap));
|
||||
memset (ke->spu_alpha, 0, sizeof (ke->spu_alpha));
|
||||
ke->spu_top = ke->spu_left = 1;
|
||||
ke->spu_bottom = ke->spu_right = 0;
|
||||
ke->spu_pix_data[0] = ke->spu_pix_data[1] = 0;
|
||||
ke->show_time = ke->hide_time = 0;
|
||||
|
||||
/* read sizes and get to the start of the data */
|
||||
CHECK (2);
|
||||
packet_size = GST_KATE_UINT16_BE (ptr);
|
||||
ADVANCE (2);
|
||||
GST_DEBUG_OBJECT (ke, "packet size %u (GstBuffer size %u)", packet_size,
|
||||
GST_BUFFER_SIZE (buf));
|
||||
|
||||
CHECK (2);
|
||||
next_command_sequence = GST_KATE_UINT16_BE (ptr);
|
||||
ADVANCE (2);
|
||||
ptr = GST_BUFFER_DATA (buf) + next_command_sequence;
|
||||
sz = GST_BUFFER_SIZE (buf) - next_command_sequence;
|
||||
GST_DEBUG_OBJECT (ke, "next command sequence at %u for %u",
|
||||
next_command_sequence, sz);
|
||||
|
||||
rflow = gst_kate_enc_decode_command_sequence (ke, buf, next_command_sequence);
|
||||
if (G_UNLIKELY (rflow != GST_FLOW_OK))
|
||||
return rflow;
|
||||
|
||||
/* if no addresses or sizes were given, or if they define an empty SPU, nothing more to do */
|
||||
if (G_UNLIKELY (ke->spu_right - ke->spu_left < 0
|
||||
|| ke->spu_bottom - ke->spu_top < 0 || ke->spu_pix_data[0] == 0
|
||||
|| ke->spu_pix_data[1] == 0)) {
|
||||
GST_WARNING_OBJECT (ke, "SPU area is empty, nothing to encode");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
/* create the palette */
|
||||
rflow = gst_kate_enc_create_spu_palette (ke, kp);
|
||||
if (G_UNLIKELY (rflow != GST_FLOW_OK))
|
||||
return rflow;
|
||||
|
||||
/* create the bitmap */
|
||||
kate_bitmap_init (kb);
|
||||
kb->width = ke->spu_right - ke->spu_left + 1;
|
||||
kb->height = ke->spu_bottom - ke->spu_top + 1;
|
||||
kb->bpp = 2;
|
||||
kb->type = kate_bitmap_type_paletted;
|
||||
kb->pixels = (unsigned char *) g_malloc (kb->width * kb->height);
|
||||
if (G_UNLIKELY (!kb->pixels)) {
|
||||
GST_WARNING_OBJECT (ke, "Failed to allocate memory for pixel data");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
n = 0;
|
||||
pixptr[0] = GST_BUFFER_DATA (buf) + ke->spu_pix_data[0];
|
||||
pixptr[1] = GST_BUFFER_DATA (buf) + ke->spu_pix_data[1];
|
||||
nybble_offset[0] = 0;
|
||||
nybble_offset[1] = 0;
|
||||
max_nybbles[0] = 2 * (packet_size - ke->spu_pix_data[0]);
|
||||
max_nybbles[1] = 2 * (packet_size - ke->spu_pix_data[1]);
|
||||
for (y = 0; y < kb->height; ++y) {
|
||||
nybble_offset[y & 1] = GST_ROUND_UP_2 (nybble_offset[y & 1]);
|
||||
for (x = 0; x < kb->width;) {
|
||||
if (G_UNLIKELY (nybble_offset[y & 1] >= max_nybbles[y & 1])) {
|
||||
GST_DEBUG_OBJECT (ke, "RLE overflow, clearing the remainder");
|
||||
memset (kb->pixels + n, 0, kb->width - x);
|
||||
n += kb->width - x;
|
||||
break;
|
||||
}
|
||||
code = gst_kate_enc_get_rle_code (pixptr[y & 1], &nybble_offset[y & 1]);
|
||||
if (code == 0) {
|
||||
memset (kb->pixels + n, 0, kb->width - x);
|
||||
n += kb->width - x;
|
||||
break;
|
||||
} else {
|
||||
guint16 npixels = code >> 2;
|
||||
guint16 pixel = code & 3;
|
||||
if (npixels > kb->width - x) {
|
||||
npixels = kb->width - x;
|
||||
}
|
||||
memset (kb->pixels + n, pixel, npixels);
|
||||
n += npixels;
|
||||
x += npixels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (ke, "%u/%u bytes left in the data packet",
|
||||
max_nybbles[0] - nybble_offset[0], max_nybbles[1] - nybble_offset[1]);
|
||||
|
||||
/* some streams seem to have huge uncropped SPUs, fix those up */
|
||||
x = ke->spu_left;
|
||||
y = ke->spu_top;
|
||||
gst_kate_enc_crop_bitmap (ke, kb, &x, &y);
|
||||
|
||||
/* create the region */
|
||||
kate_region_init (kr);
|
||||
if (ke->original_canvas_width > 0 && ke->original_canvas_height > 0) {
|
||||
/* prefer relative sizes in case we're encoding for a different resolution
|
||||
that what the SPU was created for */
|
||||
kr->metric = kate_millionths;
|
||||
kr->x = 1000000 * x / ke->original_canvas_width;
|
||||
kr->y = 1000000 * y / ke->original_canvas_height;
|
||||
kr->w = 1000000 * kb->width / ke->original_canvas_width;
|
||||
kr->h = 1000000 * kb->height / ke->original_canvas_height;
|
||||
} else {
|
||||
kr->metric = kate_pixel;
|
||||
kr->x = x;
|
||||
kr->y = y;
|
||||
kr->w = kb->width;
|
||||
kr->h = kb->height;
|
||||
}
|
||||
|
||||
/* some SPUs have no hide time */
|
||||
if (ke->hide_time == 0) {
|
||||
GST_INFO_OBJECT (ke, "SPU has no hide time");
|
||||
/* now, we don't know when the next SPU is scheduled to go, since we probably
|
||||
haven't received it yet, so we'll just make it a 1 second delay, which is
|
||||
probably going to end before the next one while being readable */
|
||||
//ke->hide_time = ke->show_time + (1000*90/1024);
|
||||
}
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
#undef IGNORE
|
||||
#undef ADVANCE
|
||||
#undef CHECK
|
||||
|
||||
static GstFlowReturn
|
||||
gst_kate_enc_chain_push_packet (GstKateEnc * ke, kate_packet * kp,
|
||||
GstClockTime start, GstClockTime duration)
|
||||
|
@ -1308,7 +748,7 @@ gst_kate_enc_chain_spu (GstKateEnc * ke, GstBuffer * buf)
|
|||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
rflow = gst_kate_enc_decode_spu (ke, buf, kregion, kbitmap, kpalette);
|
||||
rflow = gst_kate_spu_decode_spu (ke, buf, kregion, kbitmap, kpalette);
|
||||
if (G_UNLIKELY (rflow != GST_FLOW_OK)) {
|
||||
GST_ERROR_OBJECT (ke, "Failed to decode incoming SPU");
|
||||
#if 0
|
||||
|
|
865
ext/kate/gstkatespu.c
Normal file
865
ext/kate/gstkatespu.c
Normal file
|
@ -0,0 +1,865 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2009 Vincent Penquerc'h <ogg.k.ogg.k@googlemail.com>
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <kate/kate.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/gstpad.h>
|
||||
#include "gstkatespu.h"
|
||||
|
||||
#define MAX_SPU_SIZE 53220
|
||||
|
||||
GST_DEBUG_CATEGORY_EXTERN (gst_kateenc_debug);
|
||||
GST_DEBUG_CATEGORY_EXTERN (gst_katedec_debug);
|
||||
|
||||
/* taken off the dvdsubdec element */
|
||||
const guint32 gst_kate_spu_default_clut[16] = {
|
||||
0xb48080, 0x248080, 0x628080, 0xd78080,
|
||||
0x808080, 0x808080, 0x808080, 0x808080,
|
||||
0x808080, 0x808080, 0x808080, 0x808080,
|
||||
0x808080, 0x808080, 0x808080, 0x808080
|
||||
};
|
||||
|
||||
#define GST_CAT_DEFAULT gst_kateenc_debug
|
||||
|
||||
static void
|
||||
gst_kate_spu_decode_colormap (GstKateEnc * ke, const guint8 * ptr)
|
||||
{
|
||||
ke->spu_colormap[3] = ptr[0] >> 4;
|
||||
ke->spu_colormap[2] = ptr[0] & 0x0f;
|
||||
ke->spu_colormap[1] = ptr[1] >> 4;
|
||||
ke->spu_colormap[0] = ptr[1] & 0x0f;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_kate_spu_decode_alpha (GstKateEnc * ke, const guint8 * ptr)
|
||||
{
|
||||
ke->spu_alpha[3] = ptr[0] >> 4;
|
||||
ke->spu_alpha[2] = ptr[0] & 0x0f;
|
||||
ke->spu_alpha[1] = ptr[1] >> 4;
|
||||
ke->spu_alpha[0] = ptr[1] & 0x0f;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_kate_spu_decode_area (GstKateEnc * ke, const guint8 * ptr)
|
||||
{
|
||||
ke->spu_left = ((((guint16) ptr[0]) & 0x3f) << 4) | (ptr[1] >> 4);
|
||||
ke->spu_top = ((((guint16) ptr[3]) & 0x3f) << 4) | (ptr[4] >> 4);
|
||||
ke->spu_right = ((((guint16) ptr[1]) & 0x03) << 8) | ptr[2];
|
||||
ke->spu_bottom = ((((guint16) ptr[4]) & 0x03) << 8) | ptr[5];
|
||||
GST_DEBUG_OBJECT (ke, "SPU area %u %u -> %u %d", ke->spu_left, ke->spu_top,
|
||||
ke->spu_right, ke->spu_bottom);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_kate_spu_decode_pixaddr (GstKateEnc * ke, const guint8 * ptr)
|
||||
{
|
||||
ke->spu_pix_data[0] = GST_KATE_UINT16_BE (ptr + 0);
|
||||
ke->spu_pix_data[1] = GST_KATE_UINT16_BE (ptr + 2);
|
||||
}
|
||||
|
||||
/* heavily inspired from dvdspudec */
|
||||
static guint16
|
||||
gst_kate_spu_decode_colcon (GstKateEnc * ke, const guint8 * ptr)
|
||||
{
|
||||
guint16 nbytes = GST_KATE_UINT16_BE (ptr + 0);
|
||||
guint16 nbytes_left = nbytes;
|
||||
|
||||
GST_LOG_OBJECT (ke, "Number of bytes in color/contrast change command is %u",
|
||||
nbytes);
|
||||
if (G_UNLIKELY (nbytes < 2)) {
|
||||
GST_WARNING_OBJECT (ke,
|
||||
"Number of bytes in color/contrast change command is %u, should be at least 2",
|
||||
nbytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ptr += 2;
|
||||
nbytes_left -= 2;
|
||||
|
||||
/* we will just skip that data for now */
|
||||
while (nbytes_left > 0) {
|
||||
guint32 entry, nchanges, sz;
|
||||
GST_LOG_OBJECT (ke, "Reading a color/contrast change entry, %u bytes left",
|
||||
nbytes_left);
|
||||
if (G_UNLIKELY (nbytes_left < 4)) {
|
||||
GST_WARNING_OBJECT (ke,
|
||||
"Not enough bytes to read a full color/contrast entry header");
|
||||
break;
|
||||
}
|
||||
entry = GST_READ_UINT32_BE (ptr);
|
||||
GST_LOG_OBJECT (ke, "Color/contrast change entry header is %08x", entry);
|
||||
nchanges = CLAMP ((ptr[2] >> 4), 1, 8);
|
||||
ptr += 4;
|
||||
nbytes_left -= 4;
|
||||
if (entry == 0x0fffffff) {
|
||||
GST_LOG_OBJECT (ke,
|
||||
"Encountered color/contrast change termination code, breaking, %u bytes left",
|
||||
nbytes_left);
|
||||
break;
|
||||
}
|
||||
GST_LOG_OBJECT (ke, "Color/contrast change entry has %u changes", nchanges);
|
||||
sz = 6 * nchanges;
|
||||
if (G_UNLIKELY (sz > nbytes_left)) {
|
||||
GST_WARNING_OBJECT (ke,
|
||||
"Not enough bytes to read a full color/contrast entry");
|
||||
break;
|
||||
}
|
||||
ptr += sz;
|
||||
nbytes_left -= sz;
|
||||
}
|
||||
return nbytes - nbytes_left;
|
||||
}
|
||||
|
||||
static inline guint8
|
||||
gst_kate_spu_get_nybble (const guint8 * nybbles, size_t * nybble_offset)
|
||||
{
|
||||
guint8 ret;
|
||||
|
||||
ret = nybbles[(*nybble_offset) / 2];
|
||||
|
||||
/* If the offset is even, we shift the answer down 4 bits, otherwise not */
|
||||
if ((*nybble_offset) & 0x01)
|
||||
ret &= 0x0f;
|
||||
else
|
||||
ret = ret >> 4;
|
||||
|
||||
(*nybble_offset)++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static guint16
|
||||
gst_kate_spu_get_rle_code (const guint8 * nybbles, size_t * nybble_offset)
|
||||
{
|
||||
guint16 code;
|
||||
|
||||
code = gst_kate_spu_get_nybble (nybbles, nybble_offset);
|
||||
if (code < 0x4) { /* 4 .. f */
|
||||
code = (code << 4) | gst_kate_spu_get_nybble (nybbles, nybble_offset);
|
||||
if (code < 0x10) { /* 1x .. 3x */
|
||||
code = (code << 4) | gst_kate_spu_get_nybble (nybbles, nybble_offset);
|
||||
if (code < 0x40) { /* 04x .. 0fx */
|
||||
code = (code << 4) | gst_kate_spu_get_nybble (nybbles, nybble_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_kate_spu_crop_bitmap (GstKateEnc * ke, kate_bitmap * kb, guint16 * dx,
|
||||
guint16 * dy)
|
||||
{
|
||||
int top, bottom, left, right;
|
||||
guint8 zero = 0;
|
||||
size_t n, x, y, w, h;
|
||||
|
||||
#if 0
|
||||
/* find the zero */
|
||||
zero = kb->pixels[0];
|
||||
for (x = 0; x < kb->width; ++x) {
|
||||
if (kb->pixels[x] != zero) {
|
||||
GST_LOG_OBJECT (ke, "top line at %u is not zero: %u", x, kb->pixels[x]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* top */
|
||||
for (top = 0; top < kb->height; ++top) {
|
||||
int empty = 1;
|
||||
for (x = 0; x < kb->width; ++x) {
|
||||
if (G_UNLIKELY (kb->pixels[x + top * kb->width] != zero)) {
|
||||
empty = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!empty)
|
||||
break;
|
||||
}
|
||||
|
||||
/* bottom */
|
||||
for (bottom = kb->height - 1; bottom >= top; --bottom) {
|
||||
int empty = 1;
|
||||
for (x = 0; x < kb->width; ++x) {
|
||||
if (G_UNLIKELY (kb->pixels[x + bottom * kb->width] != zero)) {
|
||||
empty = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!empty)
|
||||
break;
|
||||
}
|
||||
|
||||
/* left */
|
||||
for (left = 0; left < kb->width; ++left) {
|
||||
int empty = 1;
|
||||
for (y = top; y <= bottom; ++y) {
|
||||
if (G_UNLIKELY (kb->pixels[left + y * kb->width] != zero)) {
|
||||
empty = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!empty)
|
||||
break;
|
||||
}
|
||||
|
||||
/* right */
|
||||
for (right = kb->width - 1; right >= left; --right) {
|
||||
int empty = 1;
|
||||
for (y = top; y <= bottom; ++y) {
|
||||
if (G_UNLIKELY (kb->pixels[right + y * kb->width] != zero)) {
|
||||
empty = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!empty)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
w = right - left + 1;
|
||||
h = bottom - top + 1;
|
||||
GST_LOG_OBJECT (ke, "cropped from %zu %zu to %zu %zu", kb->width, kb->height,
|
||||
w, h);
|
||||
*dx += left;
|
||||
*dy += top;
|
||||
n = 0;
|
||||
for (y = 0; y < h; ++y) {
|
||||
memmove (kb->pixels + n, kb->pixels + kb->width * (y + top) + left, w);
|
||||
n += w;
|
||||
}
|
||||
kb->width = w;
|
||||
kb->height = h;
|
||||
}
|
||||
|
||||
#define CHECK(x) do { guint16 _ = (x); if (G_UNLIKELY((_) > sz)) { GST_WARNING_OBJECT (ke, "SPU overflow"); return GST_FLOW_ERROR; } } while (0)
|
||||
#define ADVANCE(x) do { guint16 _ = (x); ptr += (_); sz -= (_); } while (0)
|
||||
#define IGNORE(x) do { guint16 __ = (x); CHECK (__); ADVANCE (__); } while (0)
|
||||
|
||||
static GstFlowReturn
|
||||
gst_kate_spu_decode_command_sequence (GstKateEnc * ke, GstBuffer * buf,
|
||||
guint16 command_sequence_offset)
|
||||
{
|
||||
guint16 date;
|
||||
guint16 next_command_sequence;
|
||||
const guint8 *ptr;
|
||||
guint16 sz;
|
||||
|
||||
if (command_sequence_offset >= GST_BUFFER_SIZE (buf)) {
|
||||
GST_WARNING_OBJECT (ke, "Command sequence offset %u is out of range %u",
|
||||
command_sequence_offset, GST_BUFFER_SIZE (buf));
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
ptr = GST_BUFFER_DATA (buf) + command_sequence_offset;
|
||||
sz = GST_BUFFER_SIZE (buf) - command_sequence_offset;
|
||||
|
||||
GST_DEBUG_OBJECT (ke, "Decoding command sequence at %u (%u bytes)",
|
||||
command_sequence_offset, sz);
|
||||
|
||||
CHECK (2);
|
||||
date = GST_KATE_UINT16_BE (ptr);
|
||||
ADVANCE (2);
|
||||
GST_DEBUG_OBJECT (ke, "date %u", date);
|
||||
|
||||
CHECK (2);
|
||||
next_command_sequence = GST_KATE_UINT16_BE (ptr);
|
||||
ADVANCE (2);
|
||||
GST_DEBUG_OBJECT (ke, "next command sequence at %u", next_command_sequence);
|
||||
|
||||
while (sz) {
|
||||
guint8 cmd = *ptr++;
|
||||
switch (cmd) {
|
||||
case SPU_CMD_FSTA_DSP: /* 0x00 */
|
||||
GST_DEBUG_OBJECT (ke, "[0] DISPLAY");
|
||||
break;
|
||||
case SPU_CMD_DSP: /* 0x01 */
|
||||
GST_DEBUG_OBJECT (ke, "[1] SHOW");
|
||||
ke->show_time = date;
|
||||
break;
|
||||
case SPU_CMD_STP_DSP: /* 0x02 */
|
||||
GST_DEBUG_OBJECT (ke, "[2] HIDE");
|
||||
ke->hide_time = date;
|
||||
break;
|
||||
case SPU_CMD_SET_COLOR: /* 0x03 */
|
||||
GST_DEBUG_OBJECT (ke, "[3] SET COLOR");
|
||||
CHECK (2);
|
||||
gst_kate_spu_decode_colormap (ke, ptr);
|
||||
ADVANCE (2);
|
||||
break;
|
||||
case SPU_CMD_SET_ALPHA: /* 0x04 */
|
||||
GST_DEBUG_OBJECT (ke, "[4] SET ALPHA");
|
||||
CHECK (2);
|
||||
gst_kate_spu_decode_alpha (ke, ptr);
|
||||
ADVANCE (2);
|
||||
break;
|
||||
case SPU_CMD_SET_DAREA: /* 0x05 */
|
||||
GST_DEBUG_OBJECT (ke, "[5] SET DISPLAY AREA");
|
||||
CHECK (6);
|
||||
gst_kate_spu_decode_area (ke, ptr);
|
||||
ADVANCE (6);
|
||||
break;
|
||||
case SPU_CMD_DSPXA: /* 0x06 */
|
||||
GST_DEBUG_OBJECT (ke, "[6] SET PIXEL ADDRESSES");
|
||||
CHECK (4);
|
||||
gst_kate_spu_decode_pixaddr (ke, ptr);
|
||||
GST_DEBUG_OBJECT (ke, " -> first pixel address %u",
|
||||
ke->spu_pix_data[0]);
|
||||
GST_DEBUG_OBJECT (ke, " -> second pixel address %u",
|
||||
ke->spu_pix_data[1]);
|
||||
ADVANCE (4);
|
||||
break;
|
||||
case SPU_CMD_CHG_COLCON: /* 0x07 */
|
||||
GST_DEBUG_OBJECT (ke, "[7] CHANGE COLOR/CONTRAST");
|
||||
CHECK (2);
|
||||
ADVANCE (gst_kate_spu_decode_colcon (ke, ptr));
|
||||
break;
|
||||
case SPU_CMD_END: /* 0xff */
|
||||
GST_DEBUG_OBJECT (ke, "[0xff] END");
|
||||
if (next_command_sequence != command_sequence_offset) {
|
||||
GST_DEBUG_OBJECT (ke, "Jumping to next sequence at offset %u",
|
||||
next_command_sequence);
|
||||
return gst_kate_spu_decode_command_sequence (ke, buf,
|
||||
next_command_sequence);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (ke, "No more sequences to decode");
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
GST_WARNING_OBJECT (ke, "invalid SPU command: %u", cmd);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
static inline int
|
||||
gst_kate_spu_clamp (int value)
|
||||
{
|
||||
if (value < 0)
|
||||
return 0;
|
||||
if (value > 255)
|
||||
return 255;
|
||||
return value;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_kate_spu_yuv2rgb (int y, int u, int v, int *r, int *g, int *b)
|
||||
{
|
||||
#if 0
|
||||
*r = gst_kate_spu_clamp (y + 1.371 * v);
|
||||
*g = gst_kate_spu_clamp (y - 0.698 * v - 0.336 * u);
|
||||
*b = gst_kate_spu_clamp (y + 1.732 * u);
|
||||
#elif 0
|
||||
*r = gst_kate_spu_clamp (y + u);
|
||||
*g = gst_kate_spu_clamp (y - (76 * u - 26 * v) / 256);
|
||||
*b = gst_kate_spu_clamp (y + v);
|
||||
#else
|
||||
y = (y - 16) * 255 / 219;
|
||||
u = (u - 128) * 255 / 224;
|
||||
v = (v - 128) * 255 / 224;
|
||||
|
||||
*r = gst_kate_spu_clamp (y + 1.402 * v);
|
||||
*g = gst_kate_spu_clamp (y - 0.34414 * u - 0.71414 * v);
|
||||
*b = gst_kate_spu_clamp (y + 1.772 * u);
|
||||
#endif
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_kate_spu_create_spu_palette (GstKateEnc * ke, kate_palette * kp)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
kate_palette_init (kp);
|
||||
kp->ncolors = 4;
|
||||
kp->colors = (kate_color *) g_malloc (kp->ncolors * sizeof (kate_color));
|
||||
if (G_UNLIKELY (!kp->colors))
|
||||
return GST_FLOW_ERROR;
|
||||
|
||||
#if 1
|
||||
for (n = 0; n < kp->ncolors; ++n) {
|
||||
int idx = ke->spu_colormap[n];
|
||||
guint32 color = ke->spu_clut[idx];
|
||||
int y = (color >> 16) & 0xff;
|
||||
int v = (color >> 8) & 0xff;
|
||||
int u = color & 0xff;
|
||||
int r, g, b;
|
||||
gst_kate_spu_yuv2rgb (y, u, v, &r, &g, &b);
|
||||
kp->colors[n].r = r;
|
||||
kp->colors[n].g = g;
|
||||
kp->colors[n].b = b;
|
||||
kp->colors[n].a = ke->spu_alpha[n] * 17;
|
||||
}
|
||||
#else
|
||||
/* just make a ramp from 0 to 255 for those non transparent colors */
|
||||
for (n = 0; n < kp->ncolors; ++n)
|
||||
if (ke->spu_alpha[n] == 0)
|
||||
++ntrans;
|
||||
|
||||
for (n = 0; n < kp->ncolors; ++n) {
|
||||
kp->colors[n].r = luma;
|
||||
kp->colors[n].g = luma;
|
||||
kp->colors[n].b = luma;
|
||||
kp->colors[n].a = ke->spu_alpha[n] * 17;
|
||||
if (ke->spu_alpha[n])
|
||||
luma /= 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
GstFlowReturn
|
||||
gst_kate_spu_decode_spu (GstKateEnc * ke, GstBuffer * buf, kate_region * kr,
|
||||
kate_bitmap * kb, kate_palette * kp)
|
||||
{
|
||||
const guint8 *ptr = GST_BUFFER_DATA (buf);
|
||||
size_t sz = GST_BUFFER_SIZE (buf);
|
||||
guint16 packet_size;
|
||||
guint16 x, y;
|
||||
size_t n;
|
||||
guint8 *pixptr[2];
|
||||
size_t nybble_offset[2];
|
||||
size_t max_nybbles[2];
|
||||
GstFlowReturn rflow;
|
||||
guint16 next_command_sequence;
|
||||
guint16 code;
|
||||
|
||||
/* before decoding anything, initialize to sensible defaults */
|
||||
memset (ke->spu_colormap, 0, sizeof (ke->spu_colormap));
|
||||
memset (ke->spu_alpha, 0, sizeof (ke->spu_alpha));
|
||||
ke->spu_top = ke->spu_left = 1;
|
||||
ke->spu_bottom = ke->spu_right = 0;
|
||||
ke->spu_pix_data[0] = ke->spu_pix_data[1] = 0;
|
||||
ke->show_time = ke->hide_time = 0;
|
||||
|
||||
/* read sizes and get to the start of the data */
|
||||
CHECK (2);
|
||||
packet_size = GST_KATE_UINT16_BE (ptr);
|
||||
ADVANCE (2);
|
||||
GST_DEBUG_OBJECT (ke, "packet size %u (GstBuffer size %u)", packet_size,
|
||||
GST_BUFFER_SIZE (buf));
|
||||
|
||||
CHECK (2);
|
||||
next_command_sequence = GST_KATE_UINT16_BE (ptr);
|
||||
ADVANCE (2);
|
||||
ptr = GST_BUFFER_DATA (buf) + next_command_sequence;
|
||||
sz = GST_BUFFER_SIZE (buf) - next_command_sequence;
|
||||
GST_DEBUG_OBJECT (ke, "next command sequence at %u for %u",
|
||||
next_command_sequence, sz);
|
||||
|
||||
rflow = gst_kate_spu_decode_command_sequence (ke, buf, next_command_sequence);
|
||||
if (G_UNLIKELY (rflow != GST_FLOW_OK))
|
||||
return rflow;
|
||||
|
||||
/* if no addresses or sizes were given, or if they define an empty SPU, nothing more to do */
|
||||
if (G_UNLIKELY (ke->spu_right - ke->spu_left < 0
|
||||
|| ke->spu_bottom - ke->spu_top < 0 || ke->spu_pix_data[0] == 0
|
||||
|| ke->spu_pix_data[1] == 0)) {
|
||||
GST_WARNING_OBJECT (ke, "SPU area is empty, nothing to encode");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
/* create the palette */
|
||||
rflow = gst_kate_spu_create_spu_palette (ke, kp);
|
||||
if (G_UNLIKELY (rflow != GST_FLOW_OK))
|
||||
return rflow;
|
||||
|
||||
/* create the bitmap */
|
||||
kate_bitmap_init (kb);
|
||||
kb->width = ke->spu_right - ke->spu_left + 1;
|
||||
kb->height = ke->spu_bottom - ke->spu_top + 1;
|
||||
kb->bpp = 2;
|
||||
kb->type = kate_bitmap_type_paletted;
|
||||
kb->pixels = (unsigned char *) g_malloc (kb->width * kb->height);
|
||||
if (G_UNLIKELY (!kb->pixels)) {
|
||||
GST_WARNING_OBJECT (ke, "Failed to allocate memory for pixel data");
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
n = 0;
|
||||
pixptr[0] = GST_BUFFER_DATA (buf) + ke->spu_pix_data[0];
|
||||
pixptr[1] = GST_BUFFER_DATA (buf) + ke->spu_pix_data[1];
|
||||
nybble_offset[0] = 0;
|
||||
nybble_offset[1] = 0;
|
||||
max_nybbles[0] = 2 * (packet_size - ke->spu_pix_data[0]);
|
||||
max_nybbles[1] = 2 * (packet_size - ke->spu_pix_data[1]);
|
||||
for (y = 0; y < kb->height; ++y) {
|
||||
nybble_offset[y & 1] = GST_ROUND_UP_2 (nybble_offset[y & 1]);
|
||||
for (x = 0; x < kb->width;) {
|
||||
if (G_UNLIKELY (nybble_offset[y & 1] >= max_nybbles[y & 1])) {
|
||||
GST_DEBUG_OBJECT (ke, "RLE overflow, clearing the remainder");
|
||||
memset (kb->pixels + n, 0, kb->width - x);
|
||||
n += kb->width - x;
|
||||
break;
|
||||
}
|
||||
code = gst_kate_spu_get_rle_code (pixptr[y & 1], &nybble_offset[y & 1]);
|
||||
if (code == 0) {
|
||||
memset (kb->pixels + n, 0, kb->width - x);
|
||||
n += kb->width - x;
|
||||
break;
|
||||
} else {
|
||||
guint16 npixels = code >> 2;
|
||||
guint16 pixel = code & 3;
|
||||
if (npixels > kb->width - x) {
|
||||
npixels = kb->width - x;
|
||||
}
|
||||
memset (kb->pixels + n, pixel, npixels);
|
||||
n += npixels;
|
||||
x += npixels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (ke, "%u/%u bytes left in the data packet",
|
||||
max_nybbles[0] - nybble_offset[0], max_nybbles[1] - nybble_offset[1]);
|
||||
|
||||
/* some streams seem to have huge uncropped SPUs, fix those up */
|
||||
x = ke->spu_left;
|
||||
y = ke->spu_top;
|
||||
gst_kate_spu_crop_bitmap (ke, kb, &x, &y);
|
||||
|
||||
/* create the region */
|
||||
kate_region_init (kr);
|
||||
if (ke->original_canvas_width > 0 && ke->original_canvas_height > 0) {
|
||||
/* prefer relative sizes in case we're encoding for a different resolution
|
||||
that what the SPU was created for */
|
||||
kr->metric = kate_millionths;
|
||||
kr->x = 1000000 * x / ke->original_canvas_width;
|
||||
kr->y = 1000000 * y / ke->original_canvas_height;
|
||||
kr->w = 1000000 * kb->width / ke->original_canvas_width;
|
||||
kr->h = 1000000 * kb->height / ke->original_canvas_height;
|
||||
} else {
|
||||
kr->metric = kate_pixel;
|
||||
kr->x = x;
|
||||
kr->y = y;
|
||||
kr->w = kb->width;
|
||||
kr->h = kb->height;
|
||||
}
|
||||
|
||||
/* some SPUs have no hide time */
|
||||
if (ke->hide_time == 0) {
|
||||
GST_INFO_OBJECT (ke, "SPU has no hide time");
|
||||
/* now, we don't know when the next SPU is scheduled to go, since we probably
|
||||
haven't received it yet, so we'll just make it a 1 second delay, which is
|
||||
probably going to end before the next one while being readable */
|
||||
//ke->hide_time = ke->show_time + (1000 * 90 / 1024);
|
||||
}
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
#undef IGNORE
|
||||
#undef ADVANCE
|
||||
#undef CHECK
|
||||
|
||||
#undef GST_CAT_DEFAULT
|
||||
#define GST_CAT_DEFAULT gst_katedec_debug
|
||||
|
||||
static void
|
||||
gst_kate_spu_add_nybble (unsigned char *bytes, size_t nbytes, int nybble_offset,
|
||||
unsigned char nybble)
|
||||
{
|
||||
unsigned char *ptr = bytes + nbytes + nybble_offset / 2;
|
||||
if (!(nybble_offset & 1)) {
|
||||
*ptr = nybble << 4;
|
||||
} else {
|
||||
*ptr |= nybble;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_kate_spu_rgb2yuv (int r, int g, int b, int *y, int *u, int *v)
|
||||
{
|
||||
*y = gst_kate_spu_clamp (r * 0.299 * 219 / 255 + g * 0.587 * 219 / 255 +
|
||||
b * 0.114 * 219 / 255 + 16);
|
||||
*u = gst_kate_spu_clamp (-r * 0.16874 * 224 / 255 - g * 0.33126 * 224 / 255 +
|
||||
b * 0.5 * 224 / 255 + 128);
|
||||
*v = gst_kate_spu_clamp (r * 0.5 * 224 / 255 - g * 0.41869 * 224 / 255 -
|
||||
b * 0.08131 * 224 / 255 + 128);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_kate_spu_make_palette (GstKateDec * kd, int palette[4],
|
||||
const kate_palette * kp)
|
||||
{
|
||||
int n;
|
||||
GstStructure *structure;
|
||||
GstEvent *event;
|
||||
char name[16];
|
||||
int y, u, v;
|
||||
|
||||
palette[0] = 0;
|
||||
palette[1] = 1;
|
||||
palette[2] = 2;
|
||||
palette[3] = 3;
|
||||
|
||||
structure = gst_structure_new ("application/x-gst-dvd",
|
||||
"event", G_TYPE_STRING, "dvd-spu-clut-change", NULL);
|
||||
|
||||
/* Create a separate field for each value in the table. */
|
||||
for (n = 0; n < 16; n++) {
|
||||
guint32 color = 0;
|
||||
if (n < 4) {
|
||||
gst_kate_spu_rgb2yuv (kp->colors[n].r, kp->colors[n].g, kp->colors[n].b,
|
||||
&y, &u, &v);
|
||||
color = (y << 16) | (v << 8) | u;
|
||||
}
|
||||
g_snprintf (name, sizeof (name), "clut%02d", n);
|
||||
gst_structure_set (structure, name, G_TYPE_INT, (int) color, NULL);
|
||||
}
|
||||
|
||||
/* Create the DVD event and put the structure into it. */
|
||||
event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, structure);
|
||||
|
||||
GST_LOG_OBJECT (kd, "preparing clut change event %" GST_PTR_FORMAT, event);
|
||||
gst_pad_push_event (kd->srcpad, event);
|
||||
}
|
||||
|
||||
GstBuffer *
|
||||
gst_kate_spu_encode_spu (GstKateDec * kd, const kate_event * ev)
|
||||
{
|
||||
kate_tracker kin;
|
||||
unsigned char *bytes = NULL;
|
||||
size_t nbytes = 0;
|
||||
GstBuffer *buffer = NULL;
|
||||
int ret;
|
||||
int ocw, och;
|
||||
int top, left, right, bottom;
|
||||
int pass, line, row;
|
||||
int lines_offset[2];
|
||||
int first_commands_offset, second_commands_offset;
|
||||
int nybble_count;
|
||||
const kate_bitmap *kb;
|
||||
const kate_palette *kp;
|
||||
int palette[4];
|
||||
int delay;
|
||||
|
||||
/* we need a region, a bitmap, and a palette */
|
||||
if (!ev || !ev->region || !ev->bitmap || !ev->palette)
|
||||
return NULL;
|
||||
|
||||
kb = ev->bitmap;
|
||||
kp = ev->palette;
|
||||
|
||||
/* these need particular properties */
|
||||
if (kb->type != kate_bitmap_type_paletted || kb->bpp != 2)
|
||||
return NULL;
|
||||
if (kp->ncolors != 4)
|
||||
return NULL;
|
||||
|
||||
ret = kate_tracker_init (&kin, ev->ki, ev);
|
||||
if (ret < 0) {
|
||||
GST_WARNING_OBJECT (kd, "Failed to initialize kate tracker");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ocw = ev->ki->original_canvas_width;
|
||||
och = ev->ki->original_canvas_height;
|
||||
ret = kate_tracker_update (&kin, (kate_float) 0, ocw, och, 0, 0, ocw, och);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
if (kin.has.region) {
|
||||
top = (int) (kin.region_y + (kate_float) 0.5);
|
||||
left = (int) (kin.region_x + (kate_float) 0.5);
|
||||
} else {
|
||||
GST_WARNING_OBJECT (kd,
|
||||
"No region information to place SPU, placing at 0 0");
|
||||
top = left = 0;
|
||||
}
|
||||
right = left + kb->width - 1;
|
||||
bottom = top + kb->height - 1;
|
||||
|
||||
/* Allocate space to build the SPU */
|
||||
bytes = g_malloc (MAX_SPU_SIZE);
|
||||
if (G_UNLIKELY (!bytes)) {
|
||||
GST_WARNING_OBJECT (kd, "Failed to allocate %zu byte buffer", nbytes);
|
||||
goto error;
|
||||
}
|
||||
nbytes = 4;
|
||||
nybble_count = 0;
|
||||
|
||||
#define CHKBUFSPC(nybbles) \
|
||||
do { \
|
||||
if ((nbytes + (nybbles + nybble_count + 1) / 2) > MAX_SPU_SIZE) { \
|
||||
GST_WARNING_OBJECT (kd, "Not enough space in SPU buffer"); \
|
||||
goto error; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* encode lines */
|
||||
for (pass = 0; pass <= 1; ++pass) {
|
||||
lines_offset[pass] = nbytes;
|
||||
for (line = pass; line < bottom - top + 1; line += 2) {
|
||||
const unsigned char *ptr = kb->pixels + line * kb->width;
|
||||
for (row = 0; row < kb->width;) {
|
||||
int run = 1;
|
||||
while (row + run < kb->width && run < 255 && ptr[row + run] == ptr[row])
|
||||
++run;
|
||||
if (run >= 63 && row + run == kb->width) {
|
||||
/* special end of line marker */
|
||||
CHKBUFSPC (4);
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, 0);
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, 0);
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, 0);
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, ptr[row]);
|
||||
} else if (run >= 1 && run <= 3) {
|
||||
CHKBUFSPC (1);
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++,
|
||||
(run << 2) | ptr[row]);
|
||||
} else if (run <= 15) {
|
||||
CHKBUFSPC (2);
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, run >> 2);
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++,
|
||||
((run & 3) << 2) | ptr[row]);
|
||||
} else if (run <= 63) {
|
||||
CHKBUFSPC (3);
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, 0);
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, run >> 2);
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++,
|
||||
((run & 3) << 2) | ptr[row]);
|
||||
} else {
|
||||
CHKBUFSPC (4);
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, 0);
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, (run >> 6));
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++,
|
||||
(run >> 2) & 0xf);
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++,
|
||||
((run & 3) << 2) | ptr[row]);
|
||||
}
|
||||
row += run;
|
||||
}
|
||||
if (nybble_count & 1) {
|
||||
CHKBUFSPC (1);
|
||||
gst_kate_spu_add_nybble (bytes, nbytes, nybble_count++, 0);
|
||||
}
|
||||
nbytes += nybble_count / 2;
|
||||
nybble_count = 0;
|
||||
}
|
||||
}
|
||||
first_commands_offset = nbytes;
|
||||
|
||||
gst_kate_spu_make_palette (kd, palette, kp);
|
||||
|
||||
/* Commands header */
|
||||
CHKBUFSPC (4 * 2);
|
||||
bytes[nbytes++] = 0;
|
||||
bytes[nbytes++] = 0;
|
||||
/* link to next command chunk will be filled later, when we know where it is */
|
||||
bytes[nbytes++] = 0;
|
||||
bytes[nbytes++] = 0;
|
||||
|
||||
CHKBUFSPC (3 * 2);
|
||||
bytes[nbytes++] = SPU_CMD_SET_COLOR;
|
||||
bytes[nbytes++] = (palette[3] << 4) | palette[2];
|
||||
bytes[nbytes++] = (palette[1] << 4) | palette[0];
|
||||
|
||||
CHKBUFSPC (3 * 2);
|
||||
bytes[nbytes++] = SPU_CMD_SET_ALPHA;
|
||||
bytes[nbytes++] =
|
||||
((kp->colors[palette[3]].a / 17) << 4) | (kp->colors[palette[2]].a / 17);
|
||||
bytes[nbytes++] =
|
||||
((kp->colors[palette[1]].a / 17) << 4) | (kp->colors[palette[0]].a / 17);
|
||||
|
||||
#if 0
|
||||
// move to top left - avoids a crash in dvdspu when overlaying on a small video :/
|
||||
right -= left;
|
||||
bottom -= top;
|
||||
left = 0;
|
||||
top = 0;
|
||||
#endif
|
||||
|
||||
CHKBUFSPC (7 * 2);
|
||||
bytes[nbytes++] = SPU_CMD_SET_DAREA;
|
||||
bytes[nbytes++] = left >> 4;
|
||||
bytes[nbytes++] = ((left & 0xf) << 4) | (right >> 8);
|
||||
bytes[nbytes++] = right & 0xff;
|
||||
bytes[nbytes++] = top >> 4;
|
||||
bytes[nbytes++] = ((top & 0xf) << 4) | (bottom >> 8);
|
||||
bytes[nbytes++] = bottom & 0xff;
|
||||
|
||||
CHKBUFSPC (5 * 2);
|
||||
bytes[nbytes++] = SPU_CMD_DSPXA;
|
||||
bytes[nbytes++] = (lines_offset[0] >> 8) & 0xff;
|
||||
bytes[nbytes++] = lines_offset[0] & 0xff;
|
||||
bytes[nbytes++] = (lines_offset[1] >> 8) & 0xff;
|
||||
bytes[nbytes++] = lines_offset[1] & 0xff;
|
||||
|
||||
CHKBUFSPC (1 * 2);
|
||||
bytes[nbytes++] = SPU_CMD_DSP;
|
||||
|
||||
CHKBUFSPC (1 * 2);
|
||||
bytes[nbytes++] = SPU_CMD_END;
|
||||
|
||||
/* stop display chunk */
|
||||
CHKBUFSPC (4 * 2);
|
||||
second_commands_offset = nbytes;
|
||||
bytes[first_commands_offset + 2] = (second_commands_offset >> 8) & 0xff;
|
||||
bytes[first_commands_offset + 3] = second_commands_offset & 0xff;
|
||||
delay = GST_KATE_GST_TO_STM (ev->end_time - ev->start_time);
|
||||
bytes[nbytes++] = (delay >> 8) & 0xff;
|
||||
bytes[nbytes++] = delay & 0xff;
|
||||
/* close the loop by linking back to self */
|
||||
bytes[nbytes++] = (second_commands_offset >> 8) & 0xff;
|
||||
bytes[nbytes++] = second_commands_offset & 0xff;
|
||||
|
||||
CHKBUFSPC (1 * 2);
|
||||
bytes[nbytes++] = SPU_CMD_STP_DSP;
|
||||
|
||||
CHKBUFSPC (1 * 2);
|
||||
bytes[nbytes++] = SPU_CMD_END;
|
||||
|
||||
/* Now that we know the size of the SPU, update the size and pointers */
|
||||
bytes[0] = (nbytes >> 8) & 0xff;
|
||||
bytes[1] = nbytes & 0xff;
|
||||
bytes[2] = (first_commands_offset >> 8) & 0xff;
|
||||
bytes[3] = first_commands_offset & 0xff;
|
||||
|
||||
/* Create a buffer with those values */
|
||||
buffer = gst_buffer_new ();
|
||||
if (G_UNLIKELY (!buffer)) {
|
||||
GST_WARNING_OBJECT (kd, "Failed to allocate %zu byte buffer", nbytes);
|
||||
goto error;
|
||||
}
|
||||
GST_BUFFER_DATA (buffer) = bytes;
|
||||
GST_BUFFER_MALLOCDATA (buffer) = bytes;
|
||||
GST_BUFFER_SIZE (buffer) = nbytes;
|
||||
GST_BUFFER_OFFSET_END (buffer) = GST_SECOND * (ev->end_time);
|
||||
GST_BUFFER_OFFSET (buffer) = GST_SECOND * (ev->start_time);
|
||||
GST_BUFFER_TIMESTAMP (buffer) = GST_SECOND * (ev->start_time);
|
||||
GST_BUFFER_DURATION (buffer) = GST_SECOND * (ev->end_time - ev->start_time);
|
||||
|
||||
GST_DEBUG_OBJECT (kd, "SPU uses %zu bytes", nbytes);
|
||||
|
||||
kate_tracker_clear (&kin);
|
||||
return buffer;
|
||||
|
||||
error:
|
||||
kate_tracker_clear (&kin);
|
||||
if (bytes)
|
||||
g_free (bytes);
|
||||
return NULL;
|
||||
}
|
65
ext/kate/gstkatespu.h
Normal file
65
ext/kate/gstkatespu.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/* -*- c-basic-offset: 2 -*-
|
||||
* GStreamer
|
||||
* Copyright (C) <2009> ogg.k.ogg.k <ogg.k.ogg.k at googlemail dot com>
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GST_KATE_SPU_H__
|
||||
#define __GST_KATE_SPU_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <kate/kate.h>
|
||||
#include "gstkateenc.h"
|
||||
#include "gstkatedec.h"
|
||||
|
||||
#define GST_KATE_UINT16_BE(ptr) ( ( ((guint16)((ptr)[0])) <<8) | ((ptr)[1]) )
|
||||
|
||||
/* taken off the DVD SPU decoder - now is time for today's WTF ???? */
|
||||
#define GST_KATE_STM_TO_GST(stm) ((1024 * (stm)) / 90000)
|
||||
#define GST_KATE_GST_TO_STM(gst) ((int)(((gst) * 90000 ) / 1024))
|
||||
|
||||
#define GST_KATE_SPU_MIME_TYPE "video/x-dvd-subpicture"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
enum GstKateSpuCmd
|
||||
{
|
||||
SPU_CMD_FSTA_DSP = 0x00, /* Forced Display */
|
||||
SPU_CMD_DSP = 0x01, /* Display Start */
|
||||
SPU_CMD_STP_DSP = 0x02, /* Display Off */
|
||||
SPU_CMD_SET_COLOR = 0x03, /* Set the color indexes for the palette */
|
||||
SPU_CMD_SET_ALPHA = 0x04, /* Set the alpha indexes for the palette */
|
||||
SPU_CMD_SET_DAREA = 0x05, /* Set the display area for the SPU */
|
||||
SPU_CMD_DSPXA = 0x06, /* Pixel data addresses */
|
||||
SPU_CMD_CHG_COLCON = 0x07, /* Change Color & Contrast */
|
||||
SPU_CMD_END = 0xff
|
||||
};
|
||||
|
||||
|
||||
extern const guint32 gst_kate_spu_default_clut[16];
|
||||
|
||||
extern GstFlowReturn
|
||||
gst_kate_spu_decode_spu (GstKateEnc * ke, GstBuffer * buf, kate_region * kr,
|
||||
kate_bitmap * kb, kate_palette * kp);
|
||||
|
||||
extern GstBuffer*
|
||||
gst_kate_spu_encode_spu (GstKateDec * kd, const kate_event * ev);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_KATE_SPU_H__ */
|
Loading…
Reference in a new issue