* modules/codec/cvdsub.c: ported cvd subtitles decoder to the new subpicture architecture.

* modules/codec/svcdsub.c: ported svcd subtitles decoder to the new subpicture architecture.
This commit is contained in:
Gildas Bazin 2004-12-19 05:05:10 +00:00
parent 805a60e6e9
commit 2cb7e0165b
20 changed files with 1123 additions and 5163 deletions

View File

@ -2464,8 +2464,6 @@ AC_CHECK_HEADERS(png.h, [
AC_CHECK_LIB(png, png_set_rows, [
VLC_ADD_LDFLAGS([png],[-lpng -lz])
VLC_ADD_PLUGINS([png])
VLC_ADD_LDFLAGS([svcdsub],[-lpng -lz])
VLC_ADD_LDFLAGS([cvdsub],[-lpng -lz -lm])
AC_DEFINE(HAVE_LIBPNG, [], [Define if you have the PNG library: libpng])],
[],[-lz])
LDFLAGS="${LDFLAGS_save}"
@ -4169,7 +4167,6 @@ AC_CONFIG_FILES([
modules/codec/cmml/Makefile
modules/codec/dmo/Makefile
modules/codec/ffmpeg/Makefile
modules/codec/ogt/Makefile
modules/codec/spudec/Makefile
modules/control/Makefile
modules/control/corba/Makefile

View File

@ -23,3 +23,5 @@ SOURCES_x264 = x264.c
SOURCES_toolame = toolame.c
SOURCES_dirac = dirac.c
SOURCES_png = png.c
SOURCES_svcdsub = svcdsub.c
SOURCES_cvdsub = cvdsub.c

611
modules/codec/cvdsub.c Normal file
View File

@ -0,0 +1,611 @@
/*****************************************************************************
* cvd.c : CVD Subtitle decoder
*****************************************************************************
* Copyright (C) 2003, 2004 VideoLAN
* $Id$
*
* Authors: Rocky Bernstein
* Gildas Bazin <gbazin@videolan.org>
* Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
* Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/decoder.h>
#include "vlc_bits.h"
#define DEBUG_CVDSUB 1
/*****************************************************************************
* Module descriptor.
*****************************************************************************/
static int DecoderOpen ( vlc_object_t * );
static int PacketizerOpen( vlc_object_t * );
static void DecoderClose ( vlc_object_t * );
vlc_module_begin();
set_description( _("CVD subtitle decoder") );
set_capability( "decoder", 50 );
set_callbacks( DecoderOpen, DecoderClose );
add_submodule();
set_description( _("Chaoji VCD subtitle packetizer") );
set_capability( "packetizer", 50 );
set_callbacks( PacketizerOpen, DecoderClose );
vlc_module_end();
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static subpicture_t *Decode( decoder_t *, block_t ** );
static block_t *Packetize ( decoder_t *, block_t ** );
static block_t *Reassemble ( decoder_t *, block_t * );
static void ParseMetaInfo ( decoder_t *, block_t * );
static void ParseHeader ( decoder_t *, block_t * );
static subpicture_t *DecodePacket( decoder_t *, block_t * );
static void RenderImage( decoder_t *, block_t *, subpicture_region_t * );
#define SUBTITLE_BLOCK_EMPTY 0
#define SUBTITLE_BLOCK_PARTIAL 1
#define SUBTITLE_BLOCK_COMPLETE 2
struct decoder_sys_t
{
int b_packetizer;
int i_state; /* data-gathering state for this subtitle */
block_t *p_spu; /* Bytes of the packet. */
int i_spu_size; /* goal for subtitle_data_pos while gathering,
size of used subtitle_data later */
uint16_t i_image_offset; /* offset from subtitle_data to compressed
image data */
int i_image_length; /* size of the compressed image data */
int first_field_offset; /* offset of even raster lines */
int second_field_offset; /* offset of odd raster lines */
int metadata_offset; /* offset to data describing the image */
int metadata_length; /* length of metadata */
mtime_t i_duration; /* how long to display the image, 0 stands
for "until next subtitle" */
uint16_t i_x_start, i_y_start; /* position of top leftmost pixel of
image when displayed */
uint16_t i_width, i_height; /* dimensions in pixels of image */
uint8_t p_palette[4][4]; /* Palette of colors used in subtitle */
uint8_t p_palette_highlight[4][4];
};
/*****************************************************************************
* DecoderOpen: open/initialize the cvdsub decoder.
*****************************************************************************/
static int DecoderOpen( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
decoder_sys_t *p_sys;
if( p_dec->fmt_in.i_codec != VLC_FOURCC( 'c','v','d',' ' ) )
{
return VLC_EGENERIC;
}
p_dec->p_sys = p_sys = malloc( sizeof( decoder_sys_t ) );
p_sys->b_packetizer = VLC_FALSE;
p_sys->i_state = SUBTITLE_BLOCK_EMPTY;
p_sys->p_spu = NULL;
es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 'c','v','d',' ' ) );
p_dec->pf_decode_sub = Decode;
p_dec->pf_packetize = Packetize;
return VLC_SUCCESS;
}
/*****************************************************************************
* PacketizerOpen: open/initialize the cvdsub packetizer.
*****************************************************************************/
static int PacketizerOpen( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
if( DecoderOpen( p_this ) != VLC_SUCCESS ) return VLC_EGENERIC;
p_dec->p_sys->b_packetizer = VLC_TRUE;
return VLC_SUCCESS;
}
/*****************************************************************************
* DecoderClose: closes the cvdsub decoder/packetizer.
*****************************************************************************/
void DecoderClose( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
decoder_sys_t *p_sys = p_dec->p_sys;
if( p_sys->p_spu ) block_ChainRelease( p_sys->p_spu );
free( p_sys );
}
/*****************************************************************************
* Decode:
*****************************************************************************/
static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
{
block_t *p_block, *p_spu;
if( pp_block == NULL || *pp_block == NULL ) return NULL;
p_block = *pp_block;
*pp_block = NULL;
if( !(p_spu = Reassemble( p_dec, p_block )) ) return NULL;
/* Parse and decode */
return DecodePacket( p_dec, p_spu );
}
/*****************************************************************************
* Packetize:
*****************************************************************************/
static block_t *Packetize( decoder_t *p_dec, block_t **pp_block )
{
block_t *p_block, *p_spu;
if( pp_block == NULL || *pp_block == NULL ) return NULL;
p_block = *pp_block;
*pp_block = NULL;
if( !(p_spu = Reassemble( p_dec, p_block )) ) return NULL;
p_spu->i_dts = p_spu->i_pts;
p_spu->i_length = 0;
return p_spu;
}
/*****************************************************************************
Reassemble:
Data for single screen subtitle may come in several non-contiguous
packets of a stream. This routine is called when the next packet in
the stream comes in. The job of this routine is to parse the header,
if this is the beginning, and combine the packets into one complete
subtitle unit.
If everything is complete, we will return a block. Otherwise return
NULL.
*****************************************************************************/
#define SPU_HEADER_LEN 1
static block_t *Reassemble( decoder_t *p_dec, block_t *p_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *p_buffer;
if( p_block->i_buffer < SPU_HEADER_LEN )
{
msg_Dbg( p_dec, "invalid packet header (size %d < %d)" ,
p_block->i_buffer, SPU_HEADER_LEN );
block_Release( p_block );
return NULL;
}
p_buffer = p_block->p_buffer;
/* From the scant data on the format, there is only only way known
* to detect the first packet in a subtitle. The first packet
* seems to have a valid PTS while later packets for the same
* image don't. */
if( p_sys->i_state == SUBTITLE_BLOCK_EMPTY && p_block->i_pts == 0 )
{
msg_Warn( p_dec, "first packet expected but no PTS present");
return NULL;
}
p_block->p_buffer += SPU_HEADER_LEN;
p_block->i_buffer -= SPU_HEADER_LEN;
/* First packet in the subtitle block */
if( p_sys->i_state == SUBTITLE_BLOCK_EMPTY ) ParseHeader( p_dec, p_block );
block_ChainAppend( &p_sys->p_spu, p_block );
p_sys->p_spu = block_ChainGather( p_sys->p_spu );
if( p_sys->p_spu->i_buffer >= p_sys->i_spu_size )
{
block_t *p_spu = p_sys->p_spu;
if( p_spu->i_buffer != p_sys->i_spu_size )
{
msg_Warn( p_dec, "SPU packets size=%d should be %d",
p_spu->i_buffer, p_sys->i_spu_size );
}
msg_Dbg( p_dec, "subtitle packet complete, size=%d", p_spu->i_buffer);
ParseMetaInfo( p_dec, p_spu );
p_sys->i_state = SUBTITLE_BLOCK_EMPTY;
p_sys->p_spu = 0;
return p_spu;
}
else
{
/* Not last block in subtitle, so wait for another. */
p_sys->i_state = SUBTITLE_BLOCK_PARTIAL;
}
return NULL;
}
/*
We do not have information on the subtitle format used on CVD's
except the submux sample code and a couple of samples of dubious
origin. Thus, this is the result of reading some code whose
correctness is not known and some experimentation.
CVD subtitles are different in several ways from SVCD OGT subtitles.
Image comes first and metadata is at the end. So that the metadata
can be found easily, the subtitle packet starts with two bytes
(everything is big-endian again) that give the total size of the
subtitle data and the offset to the metadata - i.e. size of the
image data plus the four bytes at the beginning.
Image data comes interlaced is run-length encoded. Each field is a
four-bit nibble. Each nibble contains a two-bit repeat count and a
two-bit color number so that up to three pixels can be described in
four bits. The function of a 0 repeat count is unknown; it might be
used for RLE extension. However when the full nibble is zero, the
rest of the line is filled with the color value in the next nibble.
It is unknown what happens if the color value is greater than three.
The rest seems to use a 4-entries palette. It is not impossible
that the fill-line complete case above is not as described and the
zero repeat count means fill line. The sample code never produces
this, so it may be untested.
*/
static void ParseHeader( decoder_t *p_dec, block_t *p_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *p = p_block->p_buffer;
p_sys->i_spu_size = (p[0] << 8) + p[1] + 4; p += 2;
/* FIXME: check data sanity */
p_sys->metadata_offset = (p[0] << 8) + p[1]; p +=2;
p_sys->metadata_length = p_sys->i_spu_size - p_sys->metadata_offset;
p_sys->i_image_offset = 4;
p_sys->i_image_length = p_sys->metadata_offset - p_sys->i_image_offset;
#ifdef DEBUG_CVDSUB
msg_Dbg( p_dec, "total size: %d image size: %d",
p_sys->i_spu_size, p_sys->i_image_length );
#endif
}
/*
We parse the metadata information here.
Although metadata information does not have to come in a fixed field
order, every metadata field consists of a tag byte followed by
parameters. In all cases known, the size including tag byte is
exactly four bytes in length.
*/
#define ExtractXY(x, y) x = ((p[1]&0x0f)<<6) + (p[2]>>2); \
y = ((p[2]&0x03)<<8) + p[3];
static void ParseMetaInfo( decoder_t *p_dec, block_t *p_spu )
{
/* Last packet in subtitle block. */
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *p = p_spu->p_buffer + p_sys->metadata_offset;
uint8_t *p_end = p + p_sys->metadata_length;
for( ; p < p_end; p += 4 )
{
switch( p[0] )
{
case 0x04: /* subtitle duration in 1/90000ths of a second */
p_sys->i_duration = (p[1]<<16) + (p[2]<<8) + p[3];
#ifdef DEBUG_CVDSUB
msg_Dbg( p_dec, "subtitle display duration %lu secs",
(long unsigned int)(p_sys->i_duration / 90000) );
#endif
p_sys->i_duration *= 100 / 9;
break;
case 0x0c: /* unknown */
#ifdef DEBUG_CVDSUB
msg_Dbg( p_dec, "subtitle command unknown 0x%0x 0x%0x 0x%0x 0x%0x",
(int)p[0], (int)p[1], (int)p[2], (int)p[3] );
#endif
break;
case 0x17: /* coordinates of subtitle upper left x, y position */
ExtractXY(p_sys->i_x_start, p_sys->i_y_start);
#ifdef DEBUG_CVDSUB
msg_Dbg( p_dec, "start position (%d,%d)",
p_sys->i_x_start, p_sys->i_y_start );
#endif
break;
case 0x1f: /* coordinates of subtitle bottom right x, y position */
{
int lastx;
int lasty;
ExtractXY(lastx, lasty);
p_sys->i_width = lastx - p_sys->i_x_start + 1;
p_sys->i_height = lasty - p_sys->i_y_start + 1;
#ifdef DEBUG_CVDSUB
msg_Dbg( p_dec, "end position (%d,%d), w x h: %dx%d",
lastx, lasty, p_sys->i_width, p_sys->i_height );
#endif
break;
}
case 0x24:
case 0x25:
case 0x26:
case 0x27:
{
uint8_t v = p[0] - 0x24;
#ifdef DEBUG_CVDSUB
/* Primary Palette */
msg_Dbg( p_dec, "primary palette %d (y,u,v): (0x%0x,0x%0x,0x%0x)",
(int)v, (int)p[1], (int)p[2], (int)p[3] );
#endif
p_sys->p_palette[v][0] = p[1];
p_sys->p_palette[v][1] = p[2];
p_sys->p_palette[v][2] = p[3];
break;
}
case 0x2c:
case 0x2d:
case 0x2e:
case 0x2f:
{
uint8_t v = p[0] - 0x2c;
#ifdef DEBUG_CVDSUB
msg_Dbg( p_dec,"highlight palette %d (y,u,v): (0x%0x,0x%0x,0x%0x)",
(int)v, (int)p[1], (int)p[2], (int)p[3] );
#endif
/* Highlight Palette */
p_sys->p_palette_highlight[v][0] = p[1];
p_sys->p_palette_highlight[v][1] = p[2];
p_sys->p_palette_highlight[v][2] = p[3];
break;
}
case 0x37:
/* transparency for primary palette */
p_sys->p_palette[0][3] = (p[3] & 0x0f) << 4;
p_sys->p_palette[1][3] = (p[3] >> 4) << 4;
p_sys->p_palette[2][3] = (p[2] & 0x0f) << 4;
p_sys->p_palette[3][3] = (p[2] >> 4) << 4;
#ifdef DEBUG_CVDSUB
msg_Dbg( p_dec, "transparency for primary palette 0..3: "
"0x%0x 0x%0x 0x%0x 0x%0x",
(int)p_sys->p_palette[0][3], (int)p_sys->p_palette[1][3],
(int)p_sys->p_palette[2][3], (int)p_sys->p_palette[3][3]);
#endif
break;
case 0x3f:
/* transparency for highlight palette */
p_sys->p_palette_highlight[0][3] = (p[2] & 0x0f) << 4;
p_sys->p_palette_highlight[1][3] = (p[2] >> 4) << 4;
p_sys->p_palette_highlight[2][3] = (p[1] & 0x0f) << 4;
p_sys->p_palette_highlight[3][3] = (p[1] >> 4) << 4;
#ifdef DEBUG_CVDSUB
msg_Dbg( p_dec, "transparency for highlight palette 0..3: "
"0x%0x 0x%0x 0x%0x 0x%0x",
(int)p_sys->p_palette_highlight[0][3],
(int)p_sys->p_palette_highlight[1][3],
(int)p_sys->p_palette_highlight[2][3],
(int)p_sys->p_palette_highlight[3][3] );
#endif
break;
case 0x47:
/* offset to start of even rows of interlaced image, we correct
* to make it relative to i_image_offset (usually 4) */
p_sys->first_field_offset =
(p[2] << 8) + p[3] - p_sys->i_image_offset;
#ifdef DEBUG_CVDSUB
msg_Dbg( p_dec, "1st_field_offset %d", p_sys->first_field_offset );
#endif
break;
case 0x4f:
/* offset to start of odd rows of interlaced image, we correct
* to make it relative to i_image_offset (usually 4) */
p_sys->second_field_offset =
(p[2] << 8) + p[3] - p_sys->i_image_offset;
#ifdef DEBUG_CVDSUB
msg_Dbg( p_dec, "2nd_field_offset %d", p_sys->second_field_offset);
#endif
break;
default:
#ifdef DEBUG_CVDSUB
msg_Warn( p_dec, "unknown sequence in control header "
"0x%0x 0x%0x 0x%0x 0x%0x", p[0], p[1], p[2], p[3]);
#endif
}
}
}
/*****************************************************************************
* DecodePacket: parse and decode an SPU packet
*****************************************************************************
* This function parses and decodes an SPU packet and, if valid, returns a
* subpicture.
*****************************************************************************/
static subpicture_t *DecodePacket( decoder_t *p_dec, block_t *p_data )
{
decoder_sys_t *p_sys = p_dec->p_sys;
subpicture_t *p_spu;
subpicture_region_t *p_region;
video_format_t fmt;
int i;
/* Allocate the subpicture internal data. */
p_spu = p_dec->pf_spu_buffer_new( p_dec );
if( !p_spu ) return NULL;
p_spu->i_x = p_sys->i_x_start;
p_spu->i_x = p_spu->i_x * 3 / 4; /* FIXME: use aspect ratio for x? */
p_spu->i_y = p_sys->i_y_start;
p_spu->i_start = p_data->i_pts;
p_spu->i_stop = p_data->i_pts + p_sys->i_duration;
p_spu->b_ephemer = VLC_TRUE;
/* Create new SPU region */
memset( &fmt, 0, sizeof(video_format_t) );
fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
fmt.i_aspect = VOUT_ASPECT_FACTOR;
fmt.i_width = fmt.i_visible_width = p_sys->i_width;
fmt.i_height = fmt.i_visible_height = p_sys->i_height;
fmt.i_x_offset = fmt.i_y_offset = 0;
p_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt );
if( !p_region )
{
msg_Err( p_dec, "cannot allocate SPU region" );
//goto error;
}
p_spu->p_region = p_region;
p_region->i_x = p_region->i_y = 0;
/* Build palette */
fmt.p_palette->i_entries = 4;
for( i = 0; i < fmt.p_palette->i_entries; i++ )
{
fmt.p_palette->palette[i][0] = p_sys->p_palette[i][0];
fmt.p_palette->palette[i][1] = p_sys->p_palette[i][1];
fmt.p_palette->palette[i][2] = p_sys->p_palette[i][2];
fmt.p_palette->palette[i][3] = p_sys->p_palette[i][3];
}
RenderImage( p_dec, p_data, p_region );
return p_spu;
}
/*****************************************************************************
* ParseImage: parse and render the image part of the subtitle
*****************************************************************************
This part parses the subtitle graphical data and renders it.
Image data comes interlaced and is run-length encoded (RLE). Each
field is a four-bit nibbles that is further subdivided in a two-bit
repeat count and a two-bit color number - up to three pixels can be
described in four bits. What a 0 repeat count means is unknown. It
might be used for RLE extension. There is a special case of a 0
repeat count though. When the full nibble is zero, the rest of the
line is filled with the color value in the next nibble. It is
unknown what happens if the color value is greater than three. The
rest seems to use a 4-entries palette. It is not impossible that the
fill-line complete case above is not as described and the zero repeat
count means fill line. The sample code never produces this, so it
may be untested.
However we'll transform this so that that the RLE is expanded and
interlacing will also be removed. On output each pixel entry will by
a 4-bit alpha (filling 8 bits), and 8-bit y, u, and v entry.
*****************************************************************************/
static void RenderImage( decoder_t *p_dec, block_t *p_data,
subpicture_region_t *p_region )
{
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *p_dest = p_region->picture.Y_PIXELS;
int i_field; /* The subtitles are interlaced */
int i_row, i_column; /* scanline row/column number */
uint8_t i_color, i_count;
bs_t bs;
bs_init( &bs, p_data->p_buffer + p_sys->i_image_offset,
p_data->i_buffer - p_sys->i_image_offset );
for( i_field = 0; i_field < 2; i_field++ )
{
for( i_row = i_field; i_row < p_sys->i_height; i_row += 2 )
{
for( i_column = 0; i_column < p_sys->i_width; i_column++ )
{
uint8_t i_val = bs_read( &bs, 4 );
if( i_val == 0 )
{
/* Fill the rest of the line with next color */
i_color = bs_read( &bs, 4 );
memset( &p_dest[i_row * p_region->picture.Y_PITCH +
i_column], i_color,
p_sys->i_width - i_column );
i_column = p_sys->i_width;
continue;
}
else
{
/* Normal case: get color and repeat count */
i_count = (i_val >> 2);
i_color = i_val & 0x3;
i_count = __MIN( i_count, p_sys->i_width - i_column );
memset( &p_dest[i_row * p_region->picture.Y_PITCH +
i_column], i_color, i_count );
i_column += i_count - 1;
continue;
}
}
bs_align( &bs );
}
}
}

View File

@ -1,29 +0,0 @@
SOURCES_svcdsub = \
common.c \
common.h \
subtitle.h \
ogt.c \
ogt.h \
ogt_parse.c \
pixmap.c \
pixmap.h \
render.c \
render.h \
write_png.c \
write_png.h \
$(NULL)
SOURCES_cvdsub = \
common.c \
common.h \
cvd.c \
cvd.h \
subtitle.h \
cvd_parse.c \
pixmap.c \
pixmap.h \
render.c \
render.h \
write_png.c \
write_png.h \
$(NULL)

View File

@ -1,540 +0,0 @@
/*****************************************************************************
* Common SVCD and CVD subtitle routines.
*****************************************************************************
* Copyright (C) 2003, 2004 VideoLAN
* $Id$
*
* Author: Rocky Bernstein <rocky@panix.com>
* based on code from:
* Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
* Samuel Hocevar <sam@zoy.org>
* Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/decoder.h>
#include "subtitle.h"
#include "pixmap.h"
#include "common.h"
#ifdef HAVE_LIBPNG
#include "write_png.h"
#endif
/*****************************************************************************
Free Resources associated with subtitle packet.
*****************************************************************************/
void VCDSubClose( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
decoder_sys_t *p_sys = p_dec->p_sys;
dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
if( !p_sys->b_packetizer && p_sys->p_vout )
{
/* FIXME check if it's ok to not lock vout */
spu_Control( p_sys->p_vout->p_spu, SPU_CHANNEL_CLEAR,
p_sys->i_subpic_channel );
}
if( p_sys->p_block )
{
block_ChainRelease( p_sys->p_block );
}
free(p_sys->subtitle_data);
free( p_sys );
}
/*****************************************************************************
Initialize so the next packet will start off a new one.
*****************************************************************************/
void
VCDSubInitSubtitleBlock( decoder_sys_t * p_sys )
{
p_sys->i_spu_size = 0;
p_sys->state = SUBTITLE_BLOCK_EMPTY;
p_sys->i_spu = 0;
p_sys->p_block = NULL;
p_sys->subtitle_data_pos = 0;
}
void
VCDSubInitSubtitleData(decoder_sys_t *p_sys)
{
if ( p_sys->subtitle_data ) {
if ( p_sys->subtitle_data_size < p_sys->i_spu_size ) {
p_sys->subtitle_data = realloc(p_sys->subtitle_data,
p_sys->i_spu_size);
p_sys->subtitle_data_size = p_sys->i_spu_size;
}
} else {
p_sys->subtitle_data = malloc(p_sys->i_spu_size);
p_sys->subtitle_data_size = p_sys->i_spu_size;
/* FIXME: wrong place to get p_sys */
p_sys->i_image = 0;
}
p_sys->subtitle_data_pos = 0;
}
void
VCDSubAppendData ( decoder_t *p_dec, uint8_t *buffer, uint32_t buf_len )
{
decoder_sys_t *p_sys = p_dec->p_sys;
int chunk_length = buf_len;
if ( chunk_length > p_sys->i_spu_size - p_sys->subtitle_data_pos ) {
msg_Warn( p_dec, "too much data (%d) expecting at most %u",
chunk_length, p_sys->i_spu_size - p_sys->subtitle_data_pos );
chunk_length = p_sys->i_spu_size - p_sys->subtitle_data_pos;
}
if ( chunk_length > 0 ) {
#if 0
int i;
int8_t *b=buffer;
for (i=0; i<chunk_length; i++)
printf ("%02x", b[i]);
printf("\n");
#endif
memcpy(p_sys->subtitle_data + p_sys->subtitle_data_pos,
buffer, chunk_length);
p_sys->subtitle_data_pos += chunk_length;
dbg_print(DECODE_DBG_PACKET, "%d bytes appended, pointer now %d",
chunk_length, p_sys->subtitle_data_pos);
}
}
/*****************************************************************************
* FindVout: Find a vout or wait for one to be created.
*****************************************************************************/
vout_thread_t *VCDSubFindVout( decoder_t *p_dec )
{
vout_thread_t *p_vout = NULL;
/* Find an available video output */
do
{
if( p_dec->b_die || p_dec->b_error )
{
break;
}
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( p_vout )
{
break;
}
msleep( VOUT_OUTMEM_SLEEP );
}
while( 1 );
return p_vout;
}
/**
Remove color palette by expanding pixel entries to contain the
palette values. We work from the free space at the end to the
beginning so we can expand inline.
*/
static void
InlinePalette ( /*inout*/ uint8_t *p_dest, decoder_sys_t *p_sys )
{
const unsigned int i_width = p_sys->i_width;
const unsigned int i_height = p_sys->i_height;
int n = (i_height * i_width) - 1;
uint8_t *p_from = p_dest;
ogt_yuvt_t *p_to = (ogt_yuvt_t *) p_dest;
for ( ; n >= 0 ; n-- ) {
p_to[n] = p_sys->p_palette[p_from[n]];
/*p_to[n] = p_sys->p_palette[p_from[3]];*/
}
}
/**
Check to see if user has overridden subtitle aspect ratio.
0 is returned for no override which means just counteract any
scaling effects.
*/
unsigned int
VCDSubGetAROverride(vlc_object_t * p_input, vout_thread_t *p_vout)
{
char *psz_string = config_GetPsz( p_input, MODULE_STRING "-aspect-ratio" );
/* Check whether the user tried to override aspect ratio */
if( !psz_string ) return 0;
{
unsigned int i_new_aspect = 0;
char *psz_parser = strchr( psz_string, ':' );
if( psz_parser )
{
*psz_parser++ = '\0';
i_new_aspect = atoi( psz_string ) * VOUT_ASPECT_FACTOR
/ atoi( psz_parser );
}
else
{
i_new_aspect = p_vout->output.i_width * VOUT_ASPECT_FACTOR
* atof( psz_string )
/ p_vout->output.i_height;
}
return i_new_aspect;
}
}
/**
Scales down (reduces size) of p_dest in the x direction as
determined through aspect ratio x_scale by y_scale. Scaling
is done in place. p_spu->i_width, is updated to new width
The aspect ratio is assumed to be between 1/2 and 1.
Note: the scaling truncates the new width rather than rounds it.
Perhaps something one might want to address.
*/
void
VCDSubScaleX( decoder_t *p_dec, subpicture_t *p_spu,
unsigned int i_scale_x, unsigned int i_scale_y )
{
int i_row, i_col;
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *p_src1 = p_spu->p_sys->p_data;
uint8_t *p_src2 = p_src1 + PIXEL_SIZE;
uint8_t *p_dst = p_src1;
unsigned int i_new_width = (p_spu->i_width * i_scale_x) / i_scale_y ;
unsigned int i_used=0; /* Number of bytes used up in p_src1. */
dbg_print( (DECODE_DBG_CALL|DECODE_DBG_TRANSFORM) ,
"aspect ratio %i:%i, Old width: %d, new width: %d",
i_scale_x, i_scale_y, p_spu->i_width, i_new_width);
if (! (i_scale_x < i_scale_y && i_scale_y < i_scale_x+i_scale_x) )
{
msg_Warn( p_dec, "Need x < y < 2x. x: %i, y: %i", i_scale_x, i_scale_y );
return;
}
for ( i_row=0; i_row <= p_spu->i_height - 1; i_row++ ) {
if (i_used != 0) {
/* Discard the remaining piece of the column of the previous line*/
i_used=0;
p_src1 = p_src2;
p_src2 += PIXEL_SIZE;
}
for ( i_col=0; i_col <= p_spu->i_width - 2; i_col++ ) {
unsigned int i;
unsigned int w1= i_scale_x - i_used;
unsigned int w2;
if ( i_scale_y - w1 <= i_scale_x ) {
/* Average spans 2 pixels. */
w2 = i_scale_y - w1;
for (i = 0; i < PIXEL_SIZE; i++ ) {
*p_dst = ( (*p_src1 * w1) + (*p_src2 * w2) ) / i_scale_y;
p_src1++; p_src2++; p_dst++;
}
} else {
/* Average spans 3 pixels. */
unsigned int w0 = w1;
unsigned int w1 = i_scale_x;
uint8_t *p_src0 = p_src1;
w2 = i_scale_y - w0 - w1;
p_src1 = p_src2;
p_src2 += PIXEL_SIZE;
for (i = 0; i < PIXEL_SIZE; i++ ) {
*p_dst = ( (*p_src0 * w0) + (*p_src1 * w1) + (*p_src2 * w2) )
/ i_scale_y;
p_src0++; p_src1++; p_src2++; p_dst++;
}
i_col++;
}
i_used = w2;
if (i_scale_x == i_used) {
/* End of last pixel was end of p_src2. */
p_src1 = p_src2;
p_src2 += PIXEL_SIZE;
i_col++;
i_used = 0;
}
}
}
p_spu->i_width = i_new_width;
if ( p_sys && p_sys->i_debug & DECODE_DBG_TRANSFORM )
{
ogt_yuvt_t *p_source = (ogt_yuvt_t *) p_spu->p_sys->p_data;
for ( i_row=0; i_row < p_spu->i_height; i_row++ ) {
for ( i_col=0; i_col < p_spu->i_width; i_col++ ) {
printf("%1x", p_source->s.t);
p_source++;
}
printf("\n");
}
}
}
/**
The video may be scaled. However subtitle bitmaps assume an 1:1
aspect ratio. So unless the user has specified otherwise, we
need to scale to compensate for or undo the effects of video
output scaling.
Perhaps this should go in the Render routine? The advantage would
be that it will deal with a dynamically changing aspect ratio.
The downside is having to scale many times for each render call.
We also expand palette entries here, unless we are dealing with a
palettized chroma (e.g. RGB2).
*/
void
VCDSubHandleScaling( subpicture_t *p_spu, decoder_t *p_dec )
{
vlc_object_t * p_input = p_spu->p_sys->p_input;
vout_thread_t *p_vout = vlc_object_find( p_input, VLC_OBJECT_VOUT,
FIND_CHILD );
int i_aspect_x, i_aspect_y;
uint8_t *p_dest = (uint8_t *)p_spu->p_sys->p_data;
if (p_vout) {
/* Check for user-configuration override. */
unsigned int i_new_aspect;
if ( p_vout->output.i_chroma == VLC_FOURCC('R','G','B','2') ) {
/* This is an unscaled palettized format. We don't allow
user scaling here. And to make the render process faster,
we don't expand the palette entries into a color value.
*/
return;
}
InlinePalette( p_dest, p_dec->p_sys );
i_new_aspect = VCDSubGetAROverride( p_input, p_vout );
if (i_new_aspect == VOUT_ASPECT_FACTOR) {
/* For scaling 1:1, nothing needs to be done. Note this means
subtitles will get scaled the same way the video does.
*/
;
} else {
if (0 == i_new_aspect) {
/* Counteract the effects of background video scaling when
there is scaling. That's why x and y are reversed from
the else branch in the call below.
*/
switch( p_vout->output.i_chroma )
{
/* chromas in which scaling is done outside of our
blending routine, so we need to compensate for those
effects before blending gets called: */
case VLC_FOURCC('I','4','2','0'):
case VLC_FOURCC('I','Y','U','V'):
case VLC_FOURCC('Y','V','1','2'):
case VLC_FOURCC('Y','U','Y','2'):
break;
/* chromas in which scaling is done in our blending
routine and thus we don't do it here: */
case VLC_FOURCC('R','V','1','6'):
case VLC_FOURCC('R','V','2','4'):
case VLC_FOURCC('R','V','3','2'):
case VLC_FOURCC('R','G','B','2'):
return;
break;
default:
msg_Err( p_vout, "unknown chroma %x",
p_vout->output.i_chroma );
return;
break;
}
/* We get here only for scaled chromas. */
vlc_reduce( &i_aspect_x, &i_aspect_y, p_vout->render.i_aspect,
VOUT_ASPECT_FACTOR, 0 );
} else {
/* User knows best? */
vlc_reduce( &i_aspect_x, &i_aspect_y, p_vout->render.i_aspect,
VOUT_ASPECT_FACTOR, 0 );
}
VCDSubScaleX( p_dec, p_spu, i_aspect_x, i_aspect_y );
}
}
}
/**
* DestroySPU: subpicture destructor
*/
void VCDSubDestroySPU( subpicture_t *p_spu )
{
if( p_spu->p_sys->p_input )
{
/* Detach from our input thread */
vlc_object_release( p_spu->p_sys->p_input );
}
vlc_mutex_destroy( &p_spu->p_sys->lock );
free( p_spu->p_sys );
}
/*****************************************************************************
This callback is called from the input thread when we need cropping
*****************************************************************************/
int VCDSubCropCallback( vlc_object_t *p_object, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
VCDSubUpdateSPU( (subpicture_t *)p_data, p_object );
return VLC_SUCCESS;
}
/*****************************************************************************
update subpicture settings
*****************************************************************************
This function is called from CropCallback and at initialization time, to
retrieve crop information from the input.
*****************************************************************************/
void VCDSubUpdateSPU( subpicture_t *p_spu, vlc_object_t *p_object )
{
vlc_value_t val;
p_spu->p_sys->b_crop = val.b_bool;
if( !p_spu->p_sys->b_crop )
{
return;
}
if ( VLC_SUCCESS == var_Get( p_object, "x-start", &val ) )
p_spu->p_sys->i_x_start = val.i_int;
if ( VLC_SUCCESS == var_Get( p_object, "y-start", &val ) )
p_spu->p_sys->i_y_start = val.i_int;
if ( VLC_SUCCESS == var_Get( p_object, "x-end", &val ) )
p_spu->p_sys->i_x_end = val.i_int;
if ( VLC_SUCCESS == var_Get( p_object, "y-end", &val ) )
p_spu->p_sys->i_y_end = val.i_int;
}
/*
Dump an a subtitle image to standard output - for debugging.
*/
void VCDSubDumpImage( uint8_t *p_image, uint32_t i_height, uint32_t i_width )
{
uint8_t *p = p_image;
unsigned int i_row; /* scanline row number */
unsigned int i_column; /* scanline column number */
printf("-------------------------------------\n++");
for ( i_row=0; i_row < i_height; i_row ++ ) {
for ( i_column=0; i_column<i_width; i_column++ ) {
printf("%1d", *p++ & 0x03);
}
printf("\n++");
}
printf("\n-------------------------------------\n");
}
#ifdef HAVE_LIBPNG
#define PALETTE_SIZE 4
/* Note the below assumes the above is a power of 2 */
#define PALETTE_SIZE_MASK (PALETTE_SIZE-1)
/*
Dump an a subtitle image to a Portable Network Graphics (PNG) file.
All we do here is convert YUV palette entries to RGB, expand
the image into a linear RGB pixel array, and call the routine
that does the PNG writing.
*/
void
VCDSubDumpPNG( uint8_t *p_image, decoder_t *p_dec,
uint32_t i_height, uint32_t i_width, const char *filename,
png_text *text_ptr, int i_text_count )
{
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *p = p_image;
uint8_t *image_data = malloc(RGB_SIZE * i_height * i_width );
uint8_t *q = image_data;
unsigned int i_row; /* scanline row number */
unsigned int i_column; /* scanline column number */
uint8_t rgb_palette[PALETTE_SIZE * RGB_SIZE];
int i;
dbg_print( (DECODE_DBG_CALL), "%s", filename);
if (NULL == image_data) return;
/* Convert palette YUV into RGB. */
for (i=0; i<PALETTE_SIZE; i++) {
ogt_yuvt_t *p_yuv = &(p_sys->p_palette[i]);
uint8_t *p_rgb_out = &(rgb_palette[i*RGB_SIZE]);
yuv2rgb( p_yuv, p_rgb_out );
}
/* Convert palette entries into linear RGB array. */
for ( i_row=0; i_row < i_height; i_row ++ ) {
for ( i_column=0; i_column<i_width; i_column++ ) {
uint8_t *p_rgb = &rgb_palette[ ((*p)&PALETTE_SIZE_MASK)*RGB_SIZE ];
*q++ = p_rgb[0];
*q++ = p_rgb[1];
*q++ = p_rgb[2];
p++;
}
}
write_png( filename, i_height, i_width, image_data, text_ptr, i_text_count );
free(image_data);
}
#endif /*HAVE_LIBPNG*/
/*
* Local variables:
* c-file-style: "gnu"
* tab-width: 8
* indent-tabs-mode: nil
* End:
*/

View File

@ -1,60 +0,0 @@
/*****************************************************************************
* Header for Common SVCD and VCD subtitle routines.
*****************************************************************************
* Copyright (C) 2003, 2004 VideoLAN
* $Id: common.h,v 1.6 2004/01/16 13:32:37 rocky Exp $
*
* Author: Rocky Bernstein
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
void VCDSubClose ( vlc_object_t * );
void VCDSubInitSubtitleBlock( decoder_sys_t * p_sys );
void VCDSubInitSubtitleData(decoder_sys_t *p_sys);
void VCDSubAppendData( decoder_t *p_dec, uint8_t *buffer,
uint32_t buf_len );
vout_thread_t *VCDSubFindVout( decoder_t *p_dec );
void VCDSubHandleScaling( subpicture_t *p_spu, decoder_t *p_dec ) ;
void VCDSubScaleX( decoder_t *p_dec, subpicture_t *p_spu,
unsigned int i_scale_x, unsigned int i_scale_y );
void VCDSubDestroySPU( subpicture_t *p_spu );
int VCDSubCropCallback( vlc_object_t *p_object, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval,
void *p_data );
void VCDSubUpdateSPU( subpicture_t *p_spu, vlc_object_t *p_object );
void VCDSubDumpImage( uint8_t *p_image, uint32_t i_height,
uint32_t i_width );
unsigned int VCDSubGetAROverride(vlc_object_t * p_input,
vout_thread_t *p_vout);
#ifdef HAVE_LIBPNG
#include <png.h>
void VCDSubDumpPNG( uint8_t *p_image, decoder_t *p_dec,
uint32_t i_height, uint32_t i_width,
const char *filename, /*in*/ png_text *text_ptr,
int i_text_count );
#endif /*HAVE_LIBPNG*/

View File

@ -1,309 +0,0 @@
/*****************************************************************************
* cvd.c : CVD Subtitle decoder thread
*****************************************************************************
* Copyright (C) 2003, 2004 VideoLAN
* $Id$
*
* Authors: Rocky Bernstein
* based on code from:
* Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
* Samuel Hocevar <sam@zoy.org>
* Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/decoder.h>
#include "subtitle.h"
#include "cvd.h"
#include "common.h"
/*****************************************************************************
* Module descriptor.
*****************************************************************************/
static int VCDSubOpen ( vlc_object_t * );
static int PacketizerOpen( vlc_object_t * );
vlc_module_begin();
set_description( _("CVD subtitle decoder") );
set_capability( "decoder", 50 );
set_callbacks( VCDSubOpen, VCDSubClose );
set_category( CAT_INPUT );
set_subcategory( SUBCAT_INPUT_SCODEC );
add_integer ( MODULE_STRING "-debug", 0, NULL,
DEBUG_TEXT, DEBUG_LONGTEXT, VLC_TRUE );
add_integer ( MODULE_STRING "-horizontal-correct", 0, NULL,
HORIZONTAL_CORRECT, HORIZONTAL_CORRECT_LONGTEXT, VLC_FALSE );
add_integer ( MODULE_STRING "-vertical-correct", 0, NULL,
VERTICAL_CORRECT, VERTICAL_CORRECT_LONGTEXT, VLC_FALSE );
add_string( MODULE_STRING "-aspect-ratio", "", NULL,
SUB_ASPECT_RATIO_TEXT, SUB_ASPECT_RATIO_LONGTEXT,
VLC_TRUE );
add_integer( MODULE_STRING "-duration-scaling", 3, NULL,
DURATION_SCALE_TEXT, DURATION_SCALE_LONGTEXT,
VLC_TRUE );
add_submodule();
set_description( _("Chaoji VCD subtitle packetizer") );
set_capability( "packetizer", 50 );
set_callbacks( PacketizerOpen, VCDSubClose );
vlc_module_end();
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static block_t *Reassemble( decoder_t *, block_t ** );
static subpicture_t *Decode( decoder_t *, block_t ** );
static block_t *Packetize( decoder_t *, block_t ** );
/*****************************************************************************
* VCDSubOpen
*****************************************************************************
* Tries to launch a decoder and return score so that the interface is able
* to chose.
*****************************************************************************/
static int
VCDSubOpen( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
decoder_sys_t *p_sys;
if( p_dec->fmt_in.i_codec != VLC_FOURCC( 'c','v','d',' ' ) )
{
return VLC_EGENERIC;
}
p_dec->p_sys = p_sys = malloc( sizeof( decoder_sys_t ) );
p_sys->i_debug = config_GetInt( p_this, MODULE_STRING "-debug" );
p_sys->b_packetizer = VLC_FALSE;
p_sys->p_vout = NULL;
p_sys->i_image = -1;
p_sys->subtitle_data = NULL;
VCDSubInitSubtitleBlock( p_sys );
es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 'c','v','d',' ' ) );
p_dec->pf_decode_sub = Decode;
p_dec->pf_packetize = Packetize;
dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
return VLC_SUCCESS;
}
/*****************************************************************************
* PacketizerOpen
*****************************************************************************
* Tries to launch a decoder and return score so that the interface is able
* to chose.
*****************************************************************************/
static int PacketizerOpen( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
if( VCDSubOpen( p_this ) )
{
return VLC_EGENERIC;
}
p_dec->p_sys->b_packetizer = VLC_TRUE;
return VLC_SUCCESS;
}
/*****************************************************************************
* Decode:
*****************************************************************************/
static subpicture_t *
Decode ( decoder_t *p_dec, block_t **pp_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_spu = Reassemble( p_dec, pp_block );
vout_thread_t *p_last_vout = p_dec->p_sys->p_vout;
dbg_print( (DECODE_DBG_CALL) , "");
if( p_spu )
{
p_sys->i_spu = block_ChainExtract( p_spu, p_sys->buffer, 65536 );
p_sys->i_pts = p_spu->i_pts;
block_ChainRelease( p_spu );
if( ( p_sys->p_vout = VCDSubFindVout( p_dec ) ) )
{
if( p_last_vout != p_sys->p_vout )
{
spu_Control( p_sys->p_vout->p_spu, SPU_CHANNEL_REGISTER,
&p_sys->i_subpic_channel );
}
/* Parse and decode */
E_(ParsePacket)( p_dec );
vlc_object_release( p_sys->p_vout );
}
VCDSubInitSubtitleBlock ( p_sys );
}
return NULL;
}
/*****************************************************************************
* Packetize:
*****************************************************************************/
static block_t *
Packetize( decoder_t *p_dec, block_t **pp_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_spu = Reassemble( p_dec, pp_block );
if( p_spu )
{
p_spu->i_dts = p_spu->i_pts;
p_spu->i_length = 0;
VCDSubInitSubtitleBlock( p_sys );
return block_ChainGather( p_spu );
}
return NULL;
}
/* following functions are local */
#define SPU_HEADER_LEN 1
/*****************************************************************************
Reassemble:
Data for single screen subtitle may come in several non-contiguous
packets of a stream. This routine is called when the next packet in
the stream comes in. The job of this routine is to parse the header,
if this is the beginning, and combine the packets into one complete
subtitle unit.
If everything is complete, we will return a block. Otherwise return
NULL.
*****************************************************************************/
static block_t *
Reassemble( decoder_t *p_dec, block_t **pp_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_block;
uint8_t *p_buffer;
if( pp_block == NULL || *pp_block == NULL )
{
return NULL;
}
p_block = *pp_block;
*pp_block = NULL;
if( p_block->i_buffer < SPU_HEADER_LEN )
{
msg_Dbg( p_dec, "invalid packet header (size %d < %d)" ,
p_block->i_buffer, SPU_HEADER_LEN );
block_Release( p_block );
return NULL;
}
p_buffer = p_block->p_buffer;
dbg_print( (DECODE_DBG_CALL|DECODE_DBG_PACKET),
"header: 0x%02x 0x%02x 0x%02x 0x%02x, 0x%02x, 0x%02x, size: %i",
p_buffer[1], p_buffer[2], p_buffer[3], p_buffer[4],
p_buffer[5], p_buffer[6],
p_block->i_buffer);
/* Attach to our input thread and see if subtitle is selected. */
{
vlc_object_t * p_input;
vlc_value_t val;
p_input = vlc_object_find( p_dec, VLC_OBJECT_INPUT, FIND_PARENT );
if( !p_input ) return NULL;
if( var_Get( p_input, "spu-channel", &val ) )
{
vlc_object_release( p_input );
return NULL;
}
vlc_object_release( p_input );
/* Number could be 0bd, 1bd, 2bd, 3bd for 0..3. If so
reduce it to 0..3.
*/
if ( (val.i_int & 0xff) == 0xbd ) val.i_int >>= 8;
if( val.i_int == -1 || val.i_int != p_buffer[0] )
return NULL;
}
/* From the scant data on the format, there is only only way known
to detect the first packet in a subtitle. The first packet
seems to have a valid PTS while later packets for the same
image don't. */
if ( p_sys->state == SUBTITLE_BLOCK_EMPTY && p_block->i_pts == 0 ) {
msg_Warn( p_dec,
"first packet expected but no PTS present -- skipped\n");
return NULL;
}
if ( p_sys->subtitle_data_pos == 0 ) {
/* First packet in the subtitle block */
E_(ParseHeader)( p_dec, p_buffer, p_block );
VCDSubInitSubtitleData(p_sys);
}
/* FIXME - remove append_data and use chainappend */
VCDSubAppendData( p_dec, p_buffer + SPU_HEADER_LEN,
p_block->i_buffer - SPU_HEADER_LEN );
block_ChainAppend( &p_sys->p_block, p_block );
p_sys->i_spu += p_block->i_buffer - SPU_HEADER_LEN;
if ( p_sys->subtitle_data_pos == p_sys->i_spu_size ) {
E_(ParseMetaInfo)( p_dec );
return p_sys->p_block;
} else {
/* Not last block in subtitle, so wait for another. */
p_sys->state = SUBTITLE_BLOCK_PARTIAL;
}
return NULL;
}

View File

@ -1,30 +0,0 @@
/*****************************************************************************
* cvd.h : CVD subtitles decoder thread interface
*****************************************************************************
* Copyright (C) 2003 VideoLAN
* $Id: cvd.h,v 1.2 2004/01/04 04:56:21 rocky Exp $
*
* Author: Rocky Bernstein
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Prototypes
*****************************************************************************/
void E_(ParseHeader)( decoder_t *, uint8_t *, block_t * );
void E_(ParsePacket)( decoder_t * );
void E_(ParseMetaInfo)( decoder_t *p_dec );

View File

@ -1,547 +0,0 @@
/*****************************************************************************
* parse.c: Philips OGT (SVCD subtitle) packet parser
*****************************************************************************
* Copyright (C) 2003, 2004 VideoLAN
* $Id$
*
* Authors: Rocky Bernstein
* based on code from:
* Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
* Sam Hocevar <sam@zoy.org>
* Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/decoder.h>
#include "subtitle.h"
#include "render.h"
#include "cvd.h"
#include "common.h"
#ifdef HAVE_LIBPNG
#include <png.h>
#endif
/* An image color is a two-bit palette entry: 0..3 */
typedef uint8_t ogt_color_t;
/*****************************************************************************
* Local prototypes.
*****************************************************************************/
static int ParseImage ( decoder_t *, subpicture_t * );
/*
We do not have information on the subtitle format used on CVD's
except the submux sample code and a couple of samples of dubious
origin. Thus, this is the result of reading some code whose
correctness is not known and some experimentation.
CVD subtitles are different in several ways from SVCD OGT subtitles.
Image comes first and metadata is at the end. So that the metadata
can be found easily, the subtitle packet starts with two bytes
(everything is big-endian again) that give the total size of the
subtitle data and the offset to the metadata - i.e. size of the
image data plus the four bytes at the beginning.
Image data comes interlaced is run-length encoded. Each field is a
four-bit nibble. Each nibble contains a two-bit repeat count and a
two-bit color number so that up to three pixels can be described in
four bits. The function of a 0 repeat count is unknown; it might be
used for RLE extension. However when the full nibble is zero, the
rest of the line is filled with the color value in the next nibble.
It is unknown what happens if the color value is greater than three.
The rest seems to use a 4-entries palette. It is not impossible
that the fill-line complete case above is not as described and the
zero repeat count means fill line. The sample code never produces
this, so it may be untested.
*/
void E_(ParseHeader)( decoder_t *p_dec, uint8_t *p_buffer, block_t *p_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *p = p_buffer+1;
dbg_print( (DECODE_DBG_CALL|DECODE_DBG_PACKET),
"header: 0x%02x 0x%02x 0x%02x 0x%02x, 0x%02x, 0x%02x, size: %i",
p_buffer[0], p_buffer[1], p_buffer[2], p_buffer[3],
p_buffer[4], p_buffer[5],
p_block->i_buffer);
dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
p_sys->i_pts = p_block->i_pts;
p_sys->i_spu_size = (p[0] << 8) + p[1] + 4; p += 2;
/* FIXME: check data sanity */
p_sys->metadata_offset = GETINT16(p);
p_sys->metadata_length = p_sys->i_spu_size - p_sys->metadata_offset;
p_sys->i_image_offset = 4;
p_sys->i_image_length = p_sys->metadata_offset - p_sys->i_image_offset;
dbg_print(DECODE_DBG_PACKET, "total size: %d image size: %d\n",
p_sys->i_spu_size, p_sys->i_image_length);
}
#define ExtractXY(x, y) \
x = ((p[1]&0x0f)<<6) + (p[2]>>2); \
y = ((p[2]&0x03)<<8) + p[3];
/*
We parse the metadata information here.
Although metadata information does not have to come in a fixed field
order, every metadata field consists of a tag byte followed by
parameters. In all cases known, the size including tag byte is
exactly four bytes in length.
*/
void E_(ParseMetaInfo)( decoder_t *p_dec )
{
/* last packet in subtitle block. */
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *p = p_sys->subtitle_data + p_sys->metadata_offset;
uint8_t *p_end = p + p_sys->metadata_length;
dbg_print( (DECODE_DBG_PACKET),
"subtitle packet complete, size=%d", p_sys->i_spu );
p_sys->state = SUBTITLE_BLOCK_COMPLETE;
p_sys->i_image++;
for ( ; p < p_end; p += 4 ) {
switch ( p[0] ) {
case 0x04: /* subtitle duration in 1/90000ths of a second */
{
mtime_t i_duration = (p[1]<<16) + (p[2]<<8) + p[3];
mtime_t i_duration_scale = config_GetInt( p_dec, MODULE_STRING
"-duration-scaling" );
dbg_print( DECODE_DBG_PACKET,
"subtitle display duration %lu secs (scaled %lu secs)",
(long unsigned int) (i_duration / 90000),
(long unsigned int) (i_duration * i_duration_scale / 90000)
);
p_sys->i_duration = i_duration * i_duration_scale ;
break;
}
case 0x0c: /* unknown */
dbg_print( DECODE_DBG_PACKET,
"subtitle command unknown 0x%0x 0x%0x 0x%0x 0x%0x\n",
p[0], p[1], p[2], p[3]);
break;
case 0x17: /* coordinates of subtitle upper left x, y position */
ExtractXY(p_sys->i_x_start, p_sys->i_y_start);
break;
case 0x1f: /* coordinates of subtitle bottom right x, y position */
{
int lastx;
int lasty;
ExtractXY(lastx, lasty);
p_sys->i_width = lastx - p_sys->i_x_start + 1;
p_sys->i_height = lasty - p_sys->i_y_start + 1;
dbg_print( DECODE_DBG_PACKET,
"end position: (%d,%d): %.2x %.2x %.2x, w x h: %dx%d",
lastx, lasty, p[1], p[2], p[3],
p_sys->i_width, p_sys->i_height );
break;
}
case 0x24:
case 0x25:
case 0x26:
case 0x27:
{
uint8_t v = p[0]-0x24;
/* Primary Palette */
dbg_print( DECODE_DBG_PACKET,
"primary palette %d (y,u,v): (0x%0x,0x%0x,0x%0x)",
v, p[1], p[2], p[3]);
p_sys->p_palette[v].s.y = p[1];
p_sys->p_palette[v].s.u = p[2];
p_sys->p_palette[v].s.v = p[3];
break;
}
case 0x2c:
case 0x2d:
case 0x2e:
case 0x2f:
{
uint8_t v = p[0]-0x2c;
dbg_print( DECODE_DBG_PACKET,
"highlight palette %d (y,u,v): (0x%0x,0x%0x,0x%0x)",
v, p[1], p[2], p[3]);
/* Highlight Palette */
p_sys->p_palette_highlight[v].s.y = p[1];
p_sys->p_palette_highlight[v].s.u = p[2];
p_sys->p_palette_highlight[v].s.v = p[3];
break;
}
case 0x37:
/* transparency for primary palette */
p_sys->p_palette[0].s.t = p[3] & 0x0f;
p_sys->p_palette[1].s.t = p[3] >> 4;
p_sys->p_palette[2].s.t = p[2] & 0x0f;
p_sys->p_palette[3].s.t = p[2] >> 4;
dbg_print( DECODE_DBG_PACKET,
"transparency for primary palette 0..3: "
"0x%0x 0x%0x 0x%0x 0x%0x",
p_sys->p_palette[0].s.t,
p_sys->p_palette[1].s.t,
p_sys->p_palette[2].s.t,
p_sys->p_palette[3].s.t );
break;
case 0x3f:
/* transparency for highlight palette */
p_sys->p_palette_highlight[0].s.t = p[2] & 0x0f;
p_sys->p_palette_highlight[1].s.t = p[2] >> 4;
p_sys->p_palette_highlight[2].s.t = p[1] & 0x0f;
p_sys->p_palette_highlight[3].s.t = p[1] >> 4;
dbg_print( DECODE_DBG_PACKET,
"transparency for primary palette 0..3: "
"0x%0x 0x%0x 0x%0x 0x%0x",
p_sys->p_palette_highlight[0].s.t,
p_sys->p_palette_highlight[1].s.t,
p_sys->p_palette_highlight[2].s.t,
p_sys->p_palette_highlight[3].s.t );
break;
case 0x47:
/* offset to start of even rows of interlaced image, we correct
to make it relative to i_image_offset (usually 4) */
p_sys->first_field_offset =
(p[2] << 8) + p[3] - p_sys->i_image_offset;
dbg_print( DECODE_DBG_PACKET,
"first_field_offset %d", p_sys->first_field_offset);
break;
case 0x4f:
/* offset to start of odd rows of interlaced image, we correct
to make it relative to i_image_offset (usually 4) */
p_sys->second_field_offset =
(p[2] << 8) + p[3] - p_sys->i_image_offset;
dbg_print( DECODE_DBG_PACKET,
"second_field_offset %d", p_sys->second_field_offset);
break;
default:
msg_Warn( p_dec,
"unknown sequence in control header "
"0x%0x 0x%0x 0x%0x 0x%0x",
p[0], p[1], p[2], p[3]);
p_sys->subtitle_data_pos = 0;
}
}
}
/*****************************************************************************
* ParsePacket: parse an SPU packet and send it to the video output
*****************************************************************************
* This function parses the SPU packet and, if valid, sends it to the
* video output.
*****************************************************************************/
void
E_(ParsePacket)( decoder_t *p_dec)
{
decoder_sys_t *p_sys = p_dec->p_sys;
subpicture_t *p_spu;
dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
/* Allocate the subpicture internal data. */
p_spu = spu_CreateSubpicture( p_sys->p_vout->p_spu );
if( p_spu == NULL ) return;
p_spu->i_channel = p_sys->i_subpic_channel;
/* In ParseImage we expand the run-length encoded color 0's; also
we expand pixels and remove the color palette. This should
facilitate scaling and antialiasing and speed up rendering.
*/
p_spu->p_sys = malloc( sizeof( subpicture_sys_t )
+ PIXEL_SIZE * (p_sys->i_width * p_sys->i_height) );
/* Fill the p_spu structure */
vlc_mutex_init( p_dec, &p_spu->p_sys->lock );
p_spu->pf_render = VCDSubBlend;
p_spu->pf_destroy = VCDSubDestroySPU;
p_spu->p_sys->p_data = (uint8_t*)p_spu->p_sys + sizeof( subpicture_sys_t );
p_spu->p_sys->i_x_end = p_sys->i_x_start + p_sys->i_width - 1;
p_spu->p_sys->i_y_end = p_sys->i_y_start + p_sys->i_height - 1;
p_spu->i_x = p_sys->i_x_start
+ config_GetInt( p_dec, MODULE_STRING "-horizontal-correct" );
p_spu->p_sys->p_palette[0] = p_sys->p_palette[0];
p_spu->p_sys->p_palette[1] = p_sys->p_palette[1];
p_spu->p_sys->p_palette[2] = p_sys->p_palette[2];
p_spu->p_sys->p_palette[3] = p_sys->p_palette[3];
/* FIXME: use aspect ratio for x? */
p_spu->i_x = (p_spu->i_x * 3) / 4;
p_spu->i_y = p_sys->i_y_start
+ config_GetInt( p_dec, MODULE_STRING "-vertical-correct" );
p_spu->i_width = p_sys->i_width;
p_spu->i_height = p_sys->i_height;
p_spu->i_start = p_sys->i_pts;
p_spu->i_stop = p_sys->i_pts + (p_sys->i_duration);
p_spu->p_sys->b_crop = VLC_FALSE;
p_spu->p_sys->i_debug = p_sys->i_debug;
/* Get display time now. If we do it later, we may miss the PTS. */
p_spu->p_sys->i_pts = p_sys->i_pts;
/* Attach to our input thread */
p_spu->p_sys->p_input = vlc_object_find( p_dec,
VLC_OBJECT_INPUT, FIND_PARENT );
/* We try to display it */
if( ParseImage( p_dec, p_spu ) )
{
/* There was a parse error, delete the subpicture */
spu_DestroySubpicture( p_sys->p_vout->p_spu, p_spu );
return;
}
/* SPU is finished - we can ask the video output to display it */
spu_DisplaySubpicture( p_sys->p_vout->p_spu, p_spu );
}
#define advance_color_byte_pointer \
p++; \
i_nibble_field = 2; \
/* \
* This is wrong, it may exceed maxp if it is the last, check \
* should be moved to use location or the algorithm changed to \
* that in vob2sub \
*/ \
if (p >= maxp) { \
msg_Warn( p_dec, \
"broken subtitle - overflow while decoding " \
" padding (%d,%d,%d)\n", \
i_field, i_row, i_column ); \
return VLC_EGENERIC; \
}
#define CVD_FIELD_BITS (4)
#define CVD_FIELD_MASK ((1<<CVD_FIELD_BITS) - 1)
/* Get the next field - a 2-bit palette index and a run count. To do
this we use byte image pointer p, and i_nibble_field which
indicates where we are in the byte.
*/
static inline uint8_t
ExtractField(uint8_t *p, uint8_t i_nibble_field)
{
return ( ( *p >> (CVD_FIELD_BITS*(i_nibble_field-1)) ) & CVD_FIELD_MASK );
}
/*****************************************************************************
* ParseImage: parse the image part of the subtitle
*****************************************************************************
This part parses the subtitle graphical data and stores it in a more
convenient structure for later rendering.
Image data comes interlaced and is run-length encoded (RLE). Each
field is a four-bit nibbles that is further subdivided in a two-bit
repeat count and a two-bit color number - up to three pixels can be
described in four bits. What a 0 repeat count means is unknown. It
might be used for RLE extension. There is a special case of a 0
repeat count though. When the full nibble is zero, the rest of the
line is filled with the color value in the next nibble. It is
unknown what happens if the color value is greater than three. The
rest seems to use a 4-entries palette. It is not impossible that the
fill-line complete case above is not as described and the zero repeat
count means fill line. The sample code never produces this, so it
may be untested.
However we'll transform this so that that the RLE is expanded and
interlacing will also be removed. On output each pixel entry will by
a 4-bit alpha (filling 8 bits), and 8-bit y, u, and v entry.
*****************************************************************************/
static int
ParseImage( decoder_t *p_dec, subpicture_t * p_spu )
{
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t i_field; /* The subtitles are interlaced, are we on an
even or odd scanline? */
unsigned int i_row; /* scanline row number */
unsigned int i_column; /* scanline column number */
unsigned int i_width = p_sys->i_width;
unsigned int i_height = p_sys->i_height;
uint8_t *p_dest = (uint8_t *)p_spu->p_sys->p_data;
uint8_t i_nibble_field; /* The 2-bit pixels remaining in byte of *p.
Has value 0..2. */
vlc_bool_t b_filling; /* Filling i_color to the of the line. */
uint8_t i_pending = 0; /* number of pixels to fill with
color zero 0..3 */
ogt_color_t i_color=0; /* current pixel color: 0..3 */
uint8_t *p = p_sys->subtitle_data + p_sys->i_image_offset;
uint8_t *maxp = p + p_sys->i_image_length;
dbg_print( (DECODE_DBG_CALL) , "width x height: %dx%d",
i_width, i_height);
if (p_sys && p_sys->i_debug & DECODE_DBG_IMAGE)
printf("\n");
i_pending = 0;
for ( i_field=0; i_field < 2; i_field++ ) {
i_nibble_field = 2; /* 4-bit pieces available in *p */
#if 0
unsigned int i;
int8_t *b=p;
for (i=0; i< i_width * i_height; i++)
printf ("%02x", b[i]);
printf("\n");
#endif
for ( i_row=i_field; i_row < i_height; i_row += 2 ) {
b_filling = VLC_FALSE;
for ( i_column=0; i_column<i_width; i_column++ ) {
if ( i_pending ) {
/* We are in the middle of a RLE expansion, just decrement and
fall through with current color value */
i_pending--;
} else if ( b_filling ) {
/* We are just filling to the end of line with one color, just
reuse current color value */
} else {
uint8_t i_val = ExtractField(p, i_nibble_field--);
if ( i_nibble_field == 0 ) {
advance_color_byte_pointer;
}
if ( i_val == 0 ) {
/* fill the rest of the line with next color */
i_color = ExtractField( p, i_nibble_field-- );
if ( i_nibble_field == 0 ) {
p++;
i_nibble_field=2;
/*
This is wrong, it may exceed maxp if it is the
last, check should be moved to use location or the
algorithm changed to that in vob2sub
*/
if (p >= maxp) {
msg_Warn( p_dec,
"broken subtitle - overflow while decoding "
" filling (%d,%d,%d)",
i_field, i_row, i_column);
/* return VLC_EGENERIC; */
}
}
b_filling = VLC_TRUE;
} else {
/* Normal case: get color and repeat count,
this iteration will output the first (or only)
instance */
i_pending = (i_val >> 2);
i_color = i_val & 0x3;
/* This time counts against the total */
i_pending--;
}
}
/* Color is 0-3. */
p_dest[i_row*i_width+i_column] = i_color;
if (p_sys && p_sys->i_debug & DECODE_DBG_IMAGE)
printf("%1d", i_color);
}
if ( i_nibble_field == 1 ) {
advance_color_byte_pointer;
}
if (p_sys && p_sys->i_debug & DECODE_DBG_IMAGE)
printf("\n");
}
}
if (p_sys && (p_sys->i_debug & DECODE_DBG_IMAGE)) {
/* Dump out image not interlaced... */
VCDSubDumpImage( p_dest, i_height, i_width );
}
#ifdef HAVE_LIBPNG
if (p_sys && (p_sys->i_debug & DECODE_DBG_PNG)) {
#define TEXT_COUNT 2
/* Dump image to a file in PNG format. */
char filename[300];
png_text text_ptr[TEXT_COUNT];
text_ptr[0].key = "Preparer";
text_ptr[0].text = "VLC";
text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
text_ptr[1].key = "Description";
text_ptr[1].text = "CVD Subtitle";
text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
snprintf(filename, 300, "%s%d.png", "/tmp/vlc-cvd-sub", p_sys->i_image);
VCDSubDumpPNG( p_dest, p_dec, i_height, i_width, filename,
text_ptr, TEXT_COUNT );
}
#endif /*HAVE_LIBPNG*/
VCDSubHandleScaling( p_spu, p_dec );
return VLC_SUCCESS;
}

View File

@ -1,348 +0,0 @@
/*****************************************************************************
* ogt.c : Overlay Graphics Text (SVCD subtitles) decoder thread
*****************************************************************************
* Copyright (C) 2003, 2004 VideoLAN
* $Id$
*
* Author: Rocky Bernstein
* based on code from:
* Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
* Samuel Hocevar <sam@zoy.org>
* Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/decoder.h>
#include "subtitle.h"
#include "ogt.h"
#include "common.h"
/*****************************************************************************
* Module descriptor.
*****************************************************************************/
static int VCDSubOpen ( vlc_object_t * );
static int PacketizerOpen( vlc_object_t * );
vlc_module_begin();
set_description( _("Philips OGT (SVCD subtitle) decoder") );
set_capability( "decoder", 50 );
set_callbacks( VCDSubOpen, VCDSubClose );
add_integer ( MODULE_STRING "-debug", 0, NULL,
DEBUG_TEXT, DEBUG_LONGTEXT, VLC_TRUE );
add_integer ( MODULE_STRING "-horizontal-correct", 0, NULL,
HORIZONTAL_CORRECT, HORIZONTAL_CORRECT_LONGTEXT, VLC_FALSE );
add_integer ( MODULE_STRING "-vertical-correct", 0, NULL,
VERTICAL_CORRECT, VERTICAL_CORRECT_LONGTEXT, VLC_FALSE );
add_string( MODULE_STRING "-aspect-ratio", "", NULL,
SUB_ASPECT_RATIO_TEXT, SUB_ASPECT_RATIO_LONGTEXT, VLC_TRUE );
add_integer( MODULE_STRING "-duration-scaling", 9, NULL,
DURATION_SCALE_TEXT, DURATION_SCALE_LONGTEXT, VLC_TRUE );
add_submodule();
set_description( _("Philips OGT (SVCD subtitle) packetizer") );
set_capability( "packetizer", 50 );
set_callbacks( PacketizerOpen, VCDSubClose );
vlc_module_end();
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static block_t *Reassemble ( decoder_t *, block_t ** );
static subpicture_t *Decode( decoder_t *, block_t ** );
static block_t *Packetize ( decoder_t *, block_t ** );
/*****************************************************************************
* VCDSubOpen
*****************************************************************************
* Tries to launch a decoder and return score so that the interface is able
* to chose.
*****************************************************************************/
static int
VCDSubOpen( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
decoder_sys_t *p_sys;
if( p_dec->fmt_in.i_codec != VLC_FOURCC( 'o','g','t',' ' ) )
{
return VLC_EGENERIC;
}
p_dec->p_sys = p_sys = malloc( sizeof( decoder_sys_t ) );
p_sys->i_debug = config_GetInt( p_this, MODULE_STRING "-debug" );
p_sys->b_packetizer = VLC_FALSE;
p_sys->p_vout = NULL;
p_sys->i_image = -1;
p_sys->subtitle_data = NULL;
VCDSubInitSubtitleBlock( p_sys );
es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 'o','g','t',' ' ) );
p_dec->pf_decode_sub = Decode;
p_dec->pf_packetize = Packetize;
dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
return VLC_SUCCESS;
}
/*****************************************************************************
* PacketizerOpen
*****************************************************************************
* Tries to launch a decoder and return score so that the interface is able
* to chose.
*****************************************************************************/
static int PacketizerOpen( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
if( VCDSubOpen( p_this ) )
{
return VLC_EGENERIC;
}
p_dec->p_sys->b_packetizer = VLC_TRUE;
return VLC_SUCCESS;
}
/*****************************************************************************
* Decode:
*****************************************************************************/
static subpicture_t *
Decode ( decoder_t *p_dec, block_t **pp_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_spu = Reassemble( p_dec, pp_block );
vout_thread_t *p_last_vout = p_dec->p_sys->p_vout;
dbg_print( (DECODE_DBG_CALL) , "");
if( p_spu )
{
p_sys->i_spu = block_ChainExtract( p_spu, p_sys->buffer, 65536 );
p_sys->i_pts = p_spu->i_pts;
block_ChainRelease( p_spu );
if( ( p_sys->p_vout = VCDSubFindVout( p_dec ) ) )
{
if( p_last_vout != p_sys->p_vout )
{
spu_Control( p_sys->p_vout->p_spu, SPU_CHANNEL_REGISTER,
&p_sys->i_subpic_channel );
}
/* Parse and decode */
E_(ParsePacket)( p_dec );
vlc_object_release( p_sys->p_vout );
}
VCDSubInitSubtitleBlock ( p_sys );
}
return NULL;
}
/*****************************************************************************
* Packetize:
*****************************************************************************/
static block_t *
Packetize( decoder_t *p_dec, block_t **pp_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_spu = Reassemble( p_dec, pp_block );
if( p_spu )
{
p_spu->i_dts = p_spu->i_pts;
p_spu->i_length = 0;
VCDSubInitSubtitleBlock( p_sys );
return block_ChainGather( p_spu );
}
return NULL;
}
#define SPU_HEADER_LEN 5
/*****************************************************************************
Reassemble:
The data for single screen subtitle may come in one of many
non-contiguous packets of a stream. This routine is called when the
next packet in the stream comes in. The job of this routine is to
parse the header, if this is the beginning, and combine the packets
into one complete subtitle unit.
If everything is complete, we will return a block. Otherwise return
NULL.
The format of the beginning of the subtitle packet that is used here.
size description
-------------------------------------------
byte subtitle channel (0..7) in bits 0-3
byte subtitle packet number of this subtitle image 0-N,
if the subtitle packet is complete, the top bit of the byte is 1.
uint16 subtitle image number
*****************************************************************************/
static block_t *
Reassemble( decoder_t *p_dec, block_t **pp_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_block;
uint8_t *p_buffer;
uint16_t i_expected_image;
uint8_t i_packet, i_expected_packet;
if( pp_block == NULL || *pp_block == NULL )
{
return NULL;
}
p_block = *pp_block;
*pp_block = NULL;
if( p_block->i_buffer < SPU_HEADER_LEN )
{
msg_Dbg( p_dec, "invalid packet header (size %d < %d)" ,
p_block->i_buffer, SPU_HEADER_LEN );
block_Release( p_block );
return NULL;
}
p_buffer = p_block->p_buffer;
dbg_print( (DECODE_DBG_CALL|DECODE_DBG_PACKET),
"header: 0x%02x 0x%02x 0x%02x 0x%02x, size: %i",
p_buffer[1], p_buffer[2], p_buffer[3], p_buffer[4],
p_block->i_buffer);
/* Attach to our input thread and see if subtitle is selected. */
{
vlc_object_t * p_input;
vlc_value_t val;
p_input = vlc_object_find( p_dec, VLC_OBJECT_INPUT, FIND_PARENT );
if( !p_input ) return NULL;
if( var_Get( p_input, "spu-channel", &val ) )
{
vlc_object_release( p_input );
return NULL;
}
vlc_object_release( p_input );
dbg_print( (DECODE_DBG_PACKET),
"val.i_int %x p_buffer[i] %x", val.i_int, p_buffer[1]);
/* The dummy ES that the menu selection uses has an 0x70 at
the head which we need to strip off. */
if( val.i_int == -1 || (val.i_int & 0x03) != p_buffer[1] )
{
dbg_print( DECODE_DBG_PACKET, "subtitle not for us.\n");
return NULL;
}
}
if ( p_sys->state == SUBTITLE_BLOCK_EMPTY ) {
i_expected_image = p_sys->i_image+1;
i_expected_packet = 0;
} else {
i_expected_image = p_sys->i_image;
i_expected_packet = p_sys->i_packet+1;
}
p_buffer += 2;
if ( *p_buffer & 0x80 ) {
p_sys->state = SUBTITLE_BLOCK_COMPLETE;
i_packet = ( *p_buffer++ & 0x7F );
} else {
p_sys->state = SUBTITLE_BLOCK_PARTIAL;
i_packet = *p_buffer++;
}
p_sys->i_image = GETINT16(p_buffer);
if ( p_sys->i_image != i_expected_image ) {
msg_Warn( p_dec, "expecting subtitle image %u but found %u",
i_expected_image, p_sys->i_image );
}
if ( i_packet != i_expected_packet ) {
msg_Warn( p_dec, "expecting subtitle image packet %u but found %u",
i_expected_packet, i_packet);
}
p_sys->i_packet = i_packet;
if ( p_sys->i_packet == 0 ) {
/* First packet in the subtitle block */
E_(ParseHeader)( p_dec, p_buffer, p_block );
VCDSubInitSubtitleData(p_sys);
}
/* FIXME - remove append_data and use chainappend */
VCDSubAppendData( p_dec, p_buffer, p_block->i_buffer - SPU_HEADER_LEN );
block_ChainAppend( &p_sys->p_block, p_block );
p_sys->i_spu += p_block->i_buffer - SPU_HEADER_LEN;
if (p_sys->state == SUBTITLE_BLOCK_COMPLETE)
{
if( p_sys->i_spu != p_sys->i_spu_size )
{
msg_Warn( p_dec, "SPU packets size=%d should be %d",
p_sys->i_spu, p_sys->i_spu_size );
}
dbg_print( (DECODE_DBG_PACKET),
"subtitle packet complete, size=%d", p_sys->i_spu );
return p_sys->p_block;
}
return NULL;
}
/*
* Local variables:
* c-file-style: "gnu"
* tab-width: 8
* indent-tabs-mode: nil
* End:
*/

View File

@ -1,29 +0,0 @@
/*****************************************************************************
* ogt.h : Overlay Graphics Text (SVCD subtitles) decoder thread interface
*****************************************************************************
* Copyright (C) 2003 VideoLAN
* $Id: ogt.h,v 1.6 2003/12/28 02:01:11 rocky Exp $
*
* Author: Rocky Bernstein
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Prototypes
*****************************************************************************/
void E_(ParseHeader)( decoder_t *, uint8_t *, block_t * );
void E_(ParsePacket)( decoder_t * );

View File

@ -1,390 +0,0 @@
/*****************************************************************************
* Philips OGT (SVCD subtitle) packet parser
*****************************************************************************
* Copyright (C) 2003, 2004 VideoLAN
* $Id$
*
* Author: Rocky Bernstein
* based on code from:
* Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
* Sam Hocevar <sam@zoy.org>
* Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/decoder.h>
#include "subtitle.h"
#include "common.h"
#include "render.h"
#include "ogt.h"
#ifdef HAVE_LIBPNG
#include <png.h>
#endif
/* An image color is a two-bit palette entry: 0..3 */
typedef uint8_t ogt_color_t;
/*****************************************************************************
* Local prototypes.
*****************************************************************************/
static int ParseImage ( decoder_t *, subpicture_t * );
/*
The format is roughly as follows (everything is big-endian):
size description
-------------------------------------------
byte subtitle channel (0..7) in bits 0-3
byte subtitle packet number of this subtitle image 0-N,
if the subtitle packet is complete, the top bit of the byte is 1.
u_int16 subtitle image number
u_int16 length in bytes of the rest
byte option flags, unknown meaning except bit 3 (0x08) indicates
presence of the duration field
byte unknown
u_int32 duration in 1/90000ths of a second (optional), start time
is as indicated by the PTS in the PES header
u_int32 xpos
u_int32 ypos
u_int32 width (must be even)
u_int32 height (must be even)
byte[16] palette, 4 palette entries, each contains values for
Y, U, V and transparency, 0 standing for transparent
byte command,
cmd>>6==1 indicates shift
(cmd>>4)&3 is direction from, (0=top,1=left,2=right,3=bottom)
u_int32 shift duration in 1/90000ths of a second
u_int16 offset of odd-numbered scanlines - subtitle images are
given in interlace order
byte[] limited RLE image data in interlace order (0,2,4... 1,3,5) with
2-bits per palette number
*/
/* FIXME: do we really need p_buffer and p?
Can't all of thes _offset's and _lengths's get removed?
*/
void E_(ParseHeader)( decoder_t *p_dec, uint8_t *p_buffer, block_t *p_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *p = p_buffer;
int i;
dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
p_sys->i_pts = p_block->i_pts;
p_sys->i_spu_size = GETINT16(p);
p_sys->i_options = *p++;
p_sys->i_options2 = *p++;
if ( p_sys->i_options & 0x08 ) {
p_sys->i_duration = GETINT32(p);
p_sys->i_duration *= config_GetInt( p_dec, MODULE_STRING
"-duration-scaling" );
} else {
/* 0 means display until next subtitle comes in. */
p_sys->i_duration = 0;
}
p_sys->i_x_start= GETINT16(p);
p_sys->i_y_start= GETINT16(p);
p_sys->i_width = GETINT16(p);
p_sys->i_height = GETINT16(p);
for (i=0; i<4; i++) {
p_sys->p_palette[i].s.y = *p++;
p_sys->p_palette[i].s.u = *p++;
p_sys->p_palette[i].s.v = *p++;
/* OGT has 8-bit resolution for alpha, but DVD's and CVDS use 4-bits.
Since we want to use the same render routine, rather than scale up
CVD (and DVD) subtitles, we'll scale down ours.
*/
p_sys->p_palette[i].s.t = (*p++) >> 4;
}
p_sys->i_cmd = *p++;
/* We do not really know this, FIXME */
if ( p_sys->i_cmd ) {
p_sys->i_cmd_arg = GETINT32(p);
}
/* Actually, this is measured against a different origin, so we have to
adjust it */
p_sys->second_field_offset = GETINT16(p);
p_sys->i_image_offset = p - p_buffer;
p_sys->i_image_length = p_sys->i_spu_size - p_sys->i_image_offset;
p_sys->metadata_length = p_sys->i_image_offset;
if (p_sys && p_sys->i_debug & DECODE_DBG_PACKET) {
msg_Dbg( p_dec, "x-start: %d, y-start: %d, width: %d, height %d, "
"spu size: %d, duration: %lu (d:%d p:%d)",
p_sys->i_x_start, p_sys->i_y_start,
p_sys->i_width, p_sys->i_height,
p_sys->i_spu_size, (long unsigned int) p_sys->i_duration,
p_sys->i_image_length, p_sys->i_image_offset);
for (i=0; i<4; i++) {
msg_Dbg( p_dec, "palette[%d]= T: %2x, Y: %2x, u: %2x, v: %2x", i,
p_sys->p_palette[i].s.t, p_sys->p_palette[i].s.y,
p_sys->p_palette[i].s.u, p_sys->p_palette[i].s.v );
}
}
}
/*****************************************************************************
* ParsePacket: parse an SPU packet and send it to the video output
*****************************************************************************
* This function parses the SPU packet and, if valid, sends it to the
* video output.
*****************************************************************************/
void
E_(ParsePacket)( decoder_t *p_dec)
{
decoder_sys_t *p_sys = p_dec->p_sys;
subpicture_t *p_spu;
dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
/* Allocate the subpicture internal data. */
p_spu = spu_CreateSubpicture( p_sys->p_vout->p_spu );
if( p_spu == NULL ) return;
p_spu->i_channel = p_sys->i_subpic_channel;
/* In ParseImage we expand the run-length encoded color 0's; also
we expand pixels and remove the color palette. This should
facilitate scaling and antialiasing and speed up rendering.
*/
p_spu->p_sys = malloc( sizeof( subpicture_sys_t )
+ PIXEL_SIZE * (p_sys->i_width * p_sys->i_height) );
/* Fill the p_spu structure */
vlc_mutex_init( p_dec, &p_spu->p_sys->lock );
p_spu->pf_render = VCDSubBlend;
p_spu->pf_destroy = VCDSubDestroySPU;
p_spu->p_sys->p_data = (uint8_t*)p_spu->p_sys + sizeof( subpicture_sys_t );
p_spu->p_sys->i_x_end = p_sys->i_x_start + p_sys->i_width - 1;
p_spu->p_sys->i_y_end = p_sys->i_y_start + p_sys->i_height - 1;
p_spu->i_x = p_sys->i_x_start
+ config_GetInt( p_dec, MODULE_STRING "-horizontal-correct" );
p_spu->p_sys->p_palette[0] = p_sys->p_palette[0];
p_spu->p_sys->p_palette[1] = p_sys->p_palette[1];
p_spu->p_sys->p_palette[2] = p_sys->p_palette[2];
p_spu->p_sys->p_palette[3] = p_sys->p_palette[3];
/* FIXME: use aspect ratio for x? */
p_spu->i_x = (p_spu->i_x * 3) / 4;
p_spu->i_y = p_sys->i_y_start
+ config_GetInt( p_dec, MODULE_STRING "-vertical-correct" );
p_spu->i_width = p_sys->i_width;
p_spu->i_height = p_sys->i_height;
p_spu->i_start = p_sys->i_pts;
p_spu->i_stop = p_sys->i_pts + p_sys->i_duration;
p_spu->p_sys->b_crop = VLC_FALSE;
p_spu->p_sys->i_debug = p_sys->i_debug;
/* Get display time now. If we do it later, we may miss the PTS. */
p_spu->p_sys->i_pts = p_sys->i_pts;
/* Attach to our input thread */
p_spu->p_sys->p_input = vlc_object_find( p_dec,
VLC_OBJECT_INPUT, FIND_PARENT );
/* We try to display it */
if( ParseImage( p_dec, p_spu ) )
{
/* There was a parse error, delete the subpicture */
spu_DestroySubpicture( p_sys->p_vout->p_spu, p_spu );
return;
}
/* SPU is finished - we can ask the video output to display it */
spu_DisplaySubpicture( p_sys->p_vout->p_spu, p_spu );
}
/* Advance pointer to image pointer, update internal i_2bit_field counter
and check that we haven't goine too far in the image data. */
#define advance_color_pointer_byte \
p++; \
i_2bit_field=4; \
if (p >= maxp) { \
msg_Warn( p_dec, \
"broken subtitle - tried to access beyond end " \
"in image extraction"); \
return VLC_EGENERIC; \
} \
#define advance_color_pointer \
i_2bit_field--; \
if ( i_2bit_field == 0 ) { \
advance_color_pointer_byte; \
}
#define OGT_FIELD_BITS (2)
#define OGT_FIELD_MASK ((1<<OGT_FIELD_BITS) - 1)
/* Get the next field - either a palette index or a RLE count for
color 0. To do this we use byte image pointer p, and i_2bit_field
which indicates where we are in the byte.
*/
static inline ogt_color_t
ExtractField(uint8_t *p, unsigned int i_2bit_field)
{
return ( ( *p >> (OGT_FIELD_BITS*(i_2bit_field-1)) ) & OGT_FIELD_MASK );
}
/*****************************************************************************
* ParseImage: parse the image part of the subtitle
*****************************************************************************
This part parses the subtitle graphical data and stores it in a more
convenient structure for later rendering.
The image is encoded using two bits per pixel that select a palette
entry except that value 0 starts a limited run-length encoding for
color 0. When 0 is seen, the next two bits encode one less than the
number of pixels, so we can encode run lengths from 1 to 4. These get
filled with the color in palette entry 0.
The encoding of each line is padded to a whole number of bytes. The
first field is padded to an even byte length and the complete subtitle
is padded to a 4-byte multiple that always include one zero byte at
the end.
However we'll transform this so that that the RLE is expanded and
interlacing will also be removed. On output each pixel entry will by
an 4-bit alpha (filling 8 bits), and 8-bit y, u, and v entry.
*****************************************************************************/
static int
ParseImage( decoder_t *p_dec, subpicture_t * p_spu )
{
decoder_sys_t *p_sys = p_dec->p_sys;
unsigned int i_field; /* The subtitles are interlaced, are we on an
even or odd scanline? */
unsigned int i_row; /* scanline row number */
unsigned int i_column; /* scanline column number */
unsigned int i_width = p_sys->i_width;
unsigned int i_height = p_sys->i_height;
uint8_t *p_dest = (uint8_t *)p_spu->p_sys->p_data;
uint8_t i_2bit_field; /* The 2-bit field to sue in byte of *p.
Has value 0..4. */
uint8_t i_pending_zero = 0; /* number of pixels to fill with
color zero 0..3 */
ogt_color_t i_color; /* current pixel color: 0..3 */
uint8_t *p = p_sys->subtitle_data + p_sys->i_image_offset;
uint8_t *maxp = p + p_sys->i_image_length;
dbg_print( (DECODE_DBG_CALL) , "width x height: %dx%d ",
i_width, i_height);
if (p_sys && p_sys->i_debug & DECODE_DBG_IMAGE)
printf("\n");
for ( i_field=0; i_field < 2; i_field++ ) {
i_2bit_field = 4;
for ( i_row=i_field; i_row < i_height; i_row += 2 ) {
for ( i_column=0; i_column<i_width; i_column++ ) {
if ( i_pending_zero ) {
/* We are in the middle of a RLE expansion, just decrement and
fall through with current color value */
i_pending_zero--;
i_color = 0;
} else {
i_color = ExtractField( p, i_2bit_field );
advance_color_pointer;
if ( i_color == 0 ) {
i_pending_zero = ExtractField( p, i_2bit_field );
advance_color_pointer;
/* Fall through with i_color == 0 to output the first cell */
}
}
/* Color is 0-3. */
p_dest[i_row*i_width+i_column] = i_color;
if (p_sys && p_sys->i_debug & DECODE_DBG_IMAGE)
printf("%1d", i_color);
}
if (p_sys && (p_sys->i_debug & DECODE_DBG_IMAGE))
printf("\n");
if ( i_2bit_field != 4 ) {
/* Lines are padded to complete bytes, ignore padding */
advance_color_pointer_byte;
}
}
p = p_sys->subtitle_data + p_sys->i_image_offset
+ p_sys->second_field_offset;
}
if (p_sys && (p_sys->i_debug & DECODE_DBG_IMAGE)) {
/* Dump out image not interlaced... */
VCDSubDumpImage( p_dest, i_height, i_width );
}
#ifdef HAVE_LIBPNG
if (p_sys && (p_sys->i_debug & DECODE_DBG_PNG)) {
#define TEXT_COUNT 2
/* Dump image to a file in PNG format. */
char filename[300];
png_text text_ptr[TEXT_COUNT];
text_ptr[0].key = "Preparer";
text_ptr[0].text = "VLC";
text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
text_ptr[1].key = "Description";
text_ptr[1].text = "SVCD Subtitle";
text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
snprintf(filename, 300, "%s%d.png", "/tmp/vlc-svcd-sub", p_sys->i_image);
VCDSubDumpPNG( p_dest, p_dec, i_height, i_width, filename,
text_ptr, TEXT_COUNT );
}
#endif /*HAVE_LIBPNG*/
VCDSubHandleScaling( p_spu, p_dec );
return VLC_SUCCESS;
}
/*
* Local variables:
* c-file-style: "gnu"
* tab-width: 8
* indent-tabs-mode: nil
* End:
*/

View File

@ -1,241 +0,0 @@
/*****************************************************************************
* Common pixel/chroma manipulation routines.
*****************************************************************************
* Copyright (C) 2003, 2004 VideoLAN
* $Id: pixmap.c,v 1.3 2004/01/31 05:53:35 rocky Exp $
*
* Author: Rocky Bernstein
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include "pixmap.h"
/* FIXME: This is copied from modules/video_chroma/i420_rgb.h.
Include from a more common location.
*/
/*****************************************************************************
* chroma_sys_t: chroma method descriptor
*****************************************************************************
* This structure is part of the chroma transformation descriptor, it
* describes the yuv2rgb specific properties.
*****************************************************************************/
struct chroma_sys_t
{
uint8_t *p_buffer;
int *p_offset;
/* Pre-calculated conversion tables */
void *p_base; /* base for all conversion tables */
uint8_t *p_rgb8; /* RGB 8 bits table */
uint16_t *p_rgb16; /* RGB 16 bits table */
uint32_t *p_rgb32; /* RGB 32 bits table */
/* To get RGB value for palette entry i, use (p_rgb_r[i], p_rgb_g[i],
p_rgb_b[i])
*/
uint16_t p_rgb_r[CMAP_RGB2_SIZE]; /* Red values of palette */
uint16_t p_rgb_g[CMAP_RGB2_SIZE]; /* Green values of palette */
uint16_t p_rgb_b[CMAP_RGB2_SIZE]; /* Blue values of palette */
};
/*
From
http://www.inforamp.net/~poynton/notes/colour_and_gamma/ColorFAQ.html#RTFToC11
http://people.ee.ethz.ch/~buc/brechbuehler/mirror/color/ColorFAQ.html#RTFToC1
11. What is "luma"?
It is useful in a video system to convey a component representative of
luminance and two other components representative of colour. It is
important to convey the component representative of luminance in such
a way that noise (or quantization) introduced in transmission,
processing and storage has a perceptually similar effect across the
entire tone scale from black to white. The ideal way to accomplish
these goals would be to form a luminance signal by matrixing RGB, then
subjecting luminance to a nonlinear transfer function similar to the
L* function.
There are practical reasons in video to perform these operations
in the opposite order. First a nonlinear transfer function - gamma
correction - is applied to each of the linear R, G and B. Then a
weighted sum of the nonlinear components is computed to form a
signal representative of luminance. The resulting component is
related to brightness but is not CIE luminance. Many video
engineers call it luma and give it the symbol Y'. It is often
carelessly called luminance and given the symbol Y. You must be
careful to determine whether a particular author assigns a linear
or nonlinear interpretation to the term luminance and the symbol
Y.
The coefficients that correspond to the "NTSC" red, green and blue
CRT phosphors of 1953 are standardized in ITU-R Recommendation BT.
601-2 (formerly CCIR Rec. 601-2). I call it Rec. 601. To compute
nonlinear video luma from nonlinear red, green and blue:
Y'601 = 0.299R' 0.587G' + 0.114B'
We will use the integer scaled versions of these numbers below
as RED_COEF, GREEN_COEF and BLUE_COEF.
*/
/* 19 = round(0.299 * 64) */
#define RED_COEF ((int32_t) 19)
/* 38 = round(0.587 * 64) */
#define GREEN_COEF ((int32_t) 37)
/* 7 = round(0.114 * 64) */
#define BLUE_COEF ((int32_t) 7)
/**
Find the nearest colormap entry in p_vout (assumed to have RGB2
chroma, i.e. 256 RGB 8bpp entries) that is closest in color to p_rgb. Set
out_rgb to the color found and return the colormap index.
INVALID_CMAP_ENTRY is returned if there is some error.
The closest match is determined by the the Euclidean distance
using integer-scaled 601-2 coefficients described above.
Actually, we use the square of the Euclidean distance; but in
comparisons it amounts to the same thing.
*/
cmap_t
find_cmap_rgb8_nearest(const vout_thread_t *p_vout, const uint8_t *rgb,
uint8_t *out_rgb)
{
uint16_t *p_cmap_r;
uint16_t *p_cmap_g;
uint16_t *p_cmap_b;
int i;
cmap_t i_bestmatch = INVALID_CMAP_ENTRY;
uint32_t i_mindist = 0xFFFFFFFF; /* The largest number here. */
/* Check that we really have RGB2. */
if ( !p_vout && p_vout->output.i_chroma != VLC_FOURCC('R','G','B','2') )
return INVALID_CMAP_ENTRY;
p_cmap_r=p_vout->chroma.p_sys->p_rgb_r;
p_cmap_g=p_vout->chroma.p_sys->p_rgb_g;
p_cmap_b=p_vout->chroma.p_sys->p_rgb_b;
for (i = 0; i < CMAP_RGB2_SIZE; i++) {
/* Interval range calculations to show that we don't overflow the
word sizes below. pixels component values start out 8
bits. When we subtract two components we get 9 bits, then
square to 10 bits. Next we scale by 6 to give 16
bits. XXX_COEF all fit into 5 bits, so when we multiply we
should have 21 bits maximum. So computations can be done using
32-bit precision. However before storing back distance
components we scale back down by 12 bits making the precision 9
bits. (This checks out since it is basically the range of the
square of the initial 8-bit value.)
The squared distance is the sum of three of the 9-bit components
described above. This then uses 27-bits and also fits in a
32-bit word.
*/
/* We use in integer fixed-point fractions rather than floating
point for speed. We multiply by 64 (= 1 << 6) before computing
the product, and divide the result by 64*64 (= 1 >> (6*2)).
*/
#define SCALEBITS 6
#define int32_sqr(x) ( ((int32_t) (x)) * ((int32_t) x) )
/* colormap entires are scaled to 16 bits, so we need to shift
them back down to 8. */
#define CMAP8_RED(i) (p_cmap_r[i]>>8)
#define CMAP8_GREEN(i) (p_cmap_g[i]>>8)
#define CMAP8_BLUE(i) (p_cmap_b[i]>>8)
uint32_t dr = ( RED_COEF * ( int32_sqr(rgb[RED_PIXEL] - CMAP8_RED(i))
<< SCALEBITS ) ) >> (SCALEBITS*2);
uint32_t dg = ( GREEN_COEF * ( int32_sqr(rgb[GREEN_PIXEL] - CMAP8_GREEN(i))
<< SCALEBITS ) ) >> (SCALEBITS*2);
uint32_t db = ( BLUE_COEF * ( int32_sqr(rgb[BLUE_PIXEL] - CMAP8_BLUE(i))
<< SCALEBITS ) ) >> (SCALEBITS*2);
uint32_t i_dist = dr + dg + db;
if (i_dist < i_mindist) {
i_bestmatch = i;
i_mindist = i_dist;
#if 0
printf("+++Change dist to %d RGB cmap %d (%0x, %0x, %0x)\n",
i_dist, i, p_cmap_r[ i ], p_cmap_g[ i ], p_cmap_b[ i ]);
#endif
}
}
if (out_rgb)
{
out_rgb[RED_PIXEL] = CMAP8_RED(i_bestmatch);
out_rgb[GREEN_PIXEL] = CMAP8_GREEN(i_bestmatch);
out_rgb[BLUE_PIXEL] = CMAP8_BLUE(i_bestmatch);
}
return i_bestmatch;
}
/**
Get the the rgb value for a given colormap entry for p_vout (which is'
assumed to have RGB2 chroma).
VLC_FALSE is returned if there was some error.
*/
vlc_bool_t
query_color(const vout_thread_t *p_vout, cmap_t i_cmap,
/*out*/ uint8_t *out_rgb)
{
uint16_t *p_cmap_r;
uint16_t *p_cmap_g;
uint16_t *p_cmap_b;
/* Check that we really have RGB2. */
if ( !p_vout && p_vout->output.i_chroma != VLC_FOURCC('R','G','B','2') )
return VLC_FALSE;
if ( !out_rgb )
return VLC_FALSE;
p_cmap_r=p_vout->chroma.p_sys->p_rgb_r;
p_cmap_g=p_vout->chroma.p_sys->p_rgb_g;
p_cmap_b=p_vout->chroma.p_sys->p_rgb_b;
out_rgb[RED_PIXEL] = CMAP8_RED(i_cmap);
out_rgb[GREEN_PIXEL] = CMAP8_GREEN(i_cmap);
out_rgb[BLUE_PIXEL] = CMAP8_BLUE(i_cmap);
return VLC_TRUE;
}
/*
* Local variables:
* c-file-style: "gnu"
* tab-width: 8
* indent-tabs-mode: nil
* End:
*/

View File

@ -1,164 +0,0 @@
/*****************************************************************************
* Common pixel/chroma manipulation routines.
*****************************************************************************
* Copyright (C) 2003, 2004 VideoLAN
* $Id: pixmap.h,v 1.6 2004/01/31 23:33:02 rocky Exp $
*
* Author: Rocky Bernstein
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#ifndef PIXMAP_H
#define PIXMAP_H
/** Color and transparency of a pixel or a palette (CLUT) entry
*/
typedef union {
uint8_t plane[4];
struct {
uint8_t y;
uint8_t v;
uint8_t u;
uint8_t t;
} s;
} ogt_yuvt_t;
/** An undefined or invalid colormap index. */
#define INVALID_CMAP_ENTRY -1
/** Type of a palette/colormap index*/
typedef int16_t cmap_t;
/** Number of entries in RGB palette/colormap*/
#define CMAP_RGB2_SIZE 256
/**
Force v in the range 0..255. In video_chroma/i420_rgb.c, this
is macro is called CLIP. FIXME: Combine with that.
*/
#define clip_8_bit(v) \
((v < 0) ? 0 : (v > 255) ? 255 : v)
/**
Color conversion from
http://www.inforamp.net/~poynton/notes/colour_and_gamma/ColorFAQ.html#RTFToC30
http://people.ee.ethz.ch/~buc/brechbuehler/mirror/color/ColorFAQ.html
Thanks to Billy Biggs <vektor@dumbterm.net> for the pointer and
the following conversion.
R' = [ 1.1644 0 1.5960 ] ([ Y' ] [ 16 ])
G' = [ 1.1644 -0.3918 -0.8130 ] * ([ Cb ] - [ 128 ])
B' = [ 1.1644 2.0172 0 ] ([ Cr ] [ 128 ])
See also vlc/modules/video_chroma/i420_rgb.h and
vlc/modules/video_chroma/i420_rgb_c.h for a way to do this in a way
more optimized for integer arithmetic. Would be nice to merge the
two routines.
*/
/**
Convert a YUV pixel into an RGB pixel.
*/
static inline void
yuv2rgb(const ogt_yuvt_t *p_yuv, uint8_t *p_rgb_out )
{
int i_Y = p_yuv->s.y - 16;
int i_Cb = p_yuv->s.v - 128;
int i_Cr = p_yuv->s.u - 128;
int i_red = (1.1644 * i_Y) + (1.5960 * i_Cr);
int i_green = (1.1644 * i_Y) - (0.3918 * i_Cb) - (0.8130 * i_Cr);
int i_blue = (1.1644 * i_Y) + (2.0172 * i_Cb);
i_red = clip_8_bit( i_red );
i_green = clip_8_bit( i_green );
i_blue = clip_8_bit( i_blue );
#ifdef WORDS_BIGENDIAN
*p_rgb_out++ = i_red;
*p_rgb_out++ = i_green;
*p_rgb_out++ = i_blue;
#else
*p_rgb_out++ = i_blue;
*p_rgb_out++ = i_green;
*p_rgb_out++ = i_red;
#endif
}
/* The byte storage of an RGB pixel. */
#define RGB_SIZE 3
#define GREEN_PIXEL 1
#ifdef WORDS_BIGENDIAN
#define RED_PIXEL 0
#define BLUE_PIXEL 2
#else
#define RED_PIXEL 2
#define BLUE_PIXEL 0
#endif
/**
Store an RGB pixel into the location of p_pixel, taking into
account the "Endian"-ness of the underlying machine.
(N.B. Not sure if I've got this right or this is the right thing
to do.)
*/
static inline void
put_rgb24_pixel(const uint8_t *rgb, /*out*/ uint8_t *p_pixel)
{
#ifdef WORDS_BIGENDIAN
*p_pixel++;
#endif
*p_pixel++ = rgb[RED_PIXEL];
*p_pixel++ = rgb[GREEN_PIXEL];
*p_pixel++ = rgb[BLUE_PIXEL];
}
/**
Find the nearest colormap entry in p_vout (assumed to have RGB2
chroma, i.e. 256 RGB 8bpp entries) that is closest in color to p_rgb. Set
out_rgb to the color found and return the colormap index.
INVALID_CMAP_ENTRY is returned if there is some error.
*/
cmap_t
find_cmap_rgb8_nearest(const vout_thread_t *p_vout, const uint8_t *p_rgb,
/*out*/ uint8_t *out_rgb);
/**
Get the the rgb value for a given colormap entry for p_vout (which is'
assumed to have RGB2 chroma).
VLC_FALSE is returned if there was some error.
*/
vlc_bool_t
query_color(const vout_thread_t *p_vout, cmap_t i_cmap,
/*out*/ uint8_t *rgb);
#endif /* PIXMAP_H */
/*
* Local variables:
* c-file-style: "gnu"
* tab-width: 8
* indent-tabs-mode: nil
* End:
*/

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +0,0 @@
/*****************************************************************************
* render.h : Common SVCD and CVD rendering routine(s).
*****************************************************************************
* Copyright (C) 2003 VideoLAN
* $Id: render.h,v 1.3 2004/01/21 04:45:47 rocky Exp $
*
* Author: Rocky Bernstein
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Prototypes
*****************************************************************************/
void VCDSubBlend ( vout_thread_t *, picture_t *, const subpicture_t * );

View File

@ -1,209 +0,0 @@
/*****************************************************************************
* subtitle.h : Common SVCD and CVD subtitles header
*****************************************************************************
* Copyright (C) 2003,2004 VideoLAN
* $Id$
*
* Author: Rocky Bernstein
* based on code from:
* Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
* Sam Hocevar <sam@zoy.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#include "pixmap.h"
#define DECODE_DBG_EXT 1 /* Calls from external routines */
#define DECODE_DBG_CALL 2 /* all calls */
#define DECODE_DBG_PACKET 4 /* packet assembly info */
#define DECODE_DBG_IMAGE 8 /* image bitmaps */
#define DECODE_DBG_TRANSFORM 16 /* bitmap transformations */
#define DECODE_DBG_RENDER 32 /* rendering information */
#define DECODE_DBG_PNG 64 /* Extract subtitles to PNG files. */
#define DECODE_DBG_INFO 128
#define DEBUG_TEXT N_( \
"If nonzero, this gives additional debug information." \
)
#define DEBUG_LONGTEXT N_( \
"This integer when viewed in binary is a debugging mask\n" \
"external call 1\n" \
"all calls 2\n" \
"packet assembly info 4\n" \
"image bitmaps 8\n" \
"image transformations 16\n" \
"rendering information 32\n" \
"extract subtitles 64\n" \
"misc info 128\n" )
#define SUB_ASPECT_RATIO_TEXT N_("Subtitle aspect-ratio correction")
#define SUB_ASPECT_RATIO_LONGTEXT N_( \
"Use this to force the subtitle aspect ratio. If you give a null string " \
"the right value will be determined automatically. Usually this is what " \
"you want. For OGT and CVD subtitles this undoes the effect " \
"of the underlying video scaling. And using a value of 1 will cause " \
"no correction; subtitles will be scaled with the same aspect " \
"ratio as as the underlying video (which not correct for OGT or " \
"CVD subtitles). You can also force another ratio by giving a pair of " \
"integers x:y where y should between x and twice x. For example 4:3, or " \
"16:9. Alternatively, you can give a float value expressing pixel " \
"squareness. For example 1.25 or 1.3333 which mean the same thing as " \
"4:3 and 16:9 respectively." \
)
#define DURATION_SCALE_TEXT N_("Factor to increase subtitle display interval")
#define DURATION_SCALE_LONGTEXT N_( \
"If you find you need extra time for reading subtitles, " \
"you can set this higher and it will multiply the display " \
"time by that amount. Use 0 to mean until the next " \
"subtitle.")
#define HORIZONTAL_CORRECT \
N_("Add this to starting horizontal position of subtitle.")
#define HORIZONTAL_CORRECT_LONGTEXT N_( \
"If you need to adjust the subtitle starting position horizontally, " \
"set this. Negative values shift left and positive values right. 0 would " \
"be no deviation from where the position specified in the subtitle." \
)
#define VERTICAL_CORRECT \
N_("Add this to starting vertical position of subtitle.")
#define VERTICAL_CORRECT_LONGTEXT N_( \
"If you need to adjust the subtitle starting position vertically, " \
"set this. Negative values shift up, positive values down. 0 would " \
"be no deviation from where the position specified in the subtitle." \
)
#define DECODE_DEBUG 1
#if DECODE_DEBUG
#define dbg_print(mask, s, args...) \
if (p_sys && p_sys->i_debug & mask) \
msg_Dbg(p_dec, "%s: "s, __func__ , ##args)
#else
#define dbg_print(mask, s, args...)
#endif
#define LOG_ERR(args...) msg_Err( p_input, args )
#define LOG_WARN(args...) msg_Warn( p_input, args )
#define GETINT16(p) ( (p[0] << 8) + p[1] ) ; p +=2;
#define GETINT32(p) ( (p[0] << 24) + (p[1] << 16) + \
(p[2] << 8) + (p[3]) ) ; p += 4;
/* The number of color palette entries allowed in a subtitle. */
#define NUM_SUBTITLE_COLORS 4
typedef enum {
SUBTITLE_BLOCK_EMPTY,
SUBTITLE_BLOCK_PARTIAL,
SUBTITLE_BLOCK_COMPLETE
} packet_state_t;
/* The byte storage used by one pixel */
#define PIXEL_SIZE 4
/* Size in bytes of YUV portion above. */
#define YUV_SIZE 3
/* Transparency plane. NOTE: see vlc_video.h for V_PLANE */
#define T_PLANE V_PLANE+1
struct decoder_sys_t
{
int i_debug; /* debugging mask */
mtime_t i_pts; /* Start PTS of subtitle block */
int i_spu;
packet_state_t state; /* data-gathering state for this subtitle */
uint16_t i_image; /* image number in the subtitle stream; 0 is the
first one. */
uint8_t i_packet;/* packet number for above image number; 0 is the
first one. */
block_t *p_block;/* Bytes of the packet. */
uint8_t buffer[65536 + 20 ]; /* we will never overflow more than 11
bytes if I'm right */
int b_packetizer;
int i_spu_size; /* goal for subtitle_data_pos while gathering,
size of used subtitle_data later */
vout_thread_t *p_vout;
int i_subpic_channel; /* Subpicture channel in which subtitles will
be written */
/* FIXME: Remove this? */
uint8_t *subtitle_data; /* buffer used to accumulate data from
successive packets in the same subtitle */
int subtitle_data_size; /* size of the allocated subtitle_data */
/* Move into subpicture_sys_t? */
uint16_t i_image_offset; /* offset from subtitle_data to compressed
image data */
int i_image_length; /* size of the compressed image data */
int first_field_offset; /* offset of even raster lines. Used
only for CVD. */
int second_field_offset; /* offset of odd raster lines */
int metadata_offset; /* offset to data describing the image */
int metadata_length; /* length of metadata */
int subtitle_data_pos; /* where to write next chunk */
mtime_t i_duration; /* how long to display the image, 0 stands
for "until next subtitle" */
uint16_t i_x_start, i_y_start; /* position of top leftmost pixel of
image when displayed */
uint16_t i_width, i_height; /* dimensions in pixels of image */
ogt_yuvt_t p_palette[NUM_SUBTITLE_COLORS]; /* Palette of colors used
in subtitle */
ogt_yuvt_t p_palette_highlight[NUM_SUBTITLE_COLORS]; /* Only used
for CVD */
uint8_t i_options;
uint8_t i_options2;
uint8_t i_cmd;
uint32_t i_cmd_arg;
};
struct subpicture_sys_t
{
int i_debug; /* debugging mask */
mtime_t i_pts; /* presentation timestamp */
uint8_t *p_data; /* Image data one byte T, Y, U, V */
/* Link to our input */
vlc_object_t * p_input;
/* Cropping properties */
vlc_mutex_t lock;
vlc_bool_t b_crop;
unsigned int i_x_start, i_y_start, i_x_end, i_y_end;
/* This is only used for color palette Chromas like RGB2. */
ogt_yuvt_t p_palette[NUM_SUBTITLE_COLORS]; /* Palette of colors used
in subtitle */
};

View File

@ -1,238 +0,0 @@
/*****************************************************************************
* Dump an Image to a Portable Network Graphics (PNG) file
****************************************************************************
Copyright (C) 2004 VideoLAN
Author: Rocky Bernstein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#include "config.h"
#ifdef HAVE_LIBPNG
#include <stdio.h>
#include <stdlib.h>
#include "write_png.h"
#include <setjmp.h>
typedef void (*snapshot_messenger_t)(char *message);
#define _(x) x
/*
* Error functions for use as callbacks by the png libraries
*/
void error_msg(char *message)
{
printf("error: %s\n", message);
}
void warning_msg(char *message)
{
printf("warning: %s\n", message);
}
static snapshot_messenger_t error_msg_cb = error_msg;
static snapshot_messenger_t warning_msg_cb = warning_msg;
static void
user_error_fn(png_structp png_ptr, png_const_charp error_msg)
{
if(error_msg_cb) {
char uerror[4096];
memset(&uerror, 0, sizeof(uerror));
sprintf(uerror, _("Error: %s\n"), error_msg);
error_msg_cb(uerror);
}
}
static void
user_warning_fn(png_structp png_ptr, png_const_charp warning_msg)
{
if(error_msg_cb) {
char uerror[4096];
memset(&uerror, 0, sizeof(uerror));
sprintf(uerror, _("Error: %s\n"), warning_msg);
warning_msg_cb(uerror);
}
}
/*
Dump an image to a Portable Network Graphics (PNG) file. File_name
is where the file goes, i_height and i_width are the height and
width in pixels of the image. The data for the image is stored as a
linear array of one byte for each of red, green, and blue
components of an RGB pixel. Thus row[i] will begin at rgb_image +
i*(i_width*3) and the blue pixel at image[i][0] would be rgb_image +
i*(i_width*3) + 1.
*/
void
write_png(const char *file_name, png_uint_32 i_height, png_uint_32 i_width,
void *rgb_image, /*in*/ png_text *text_ptr, int i_text_count )
{
FILE *fp;
png_structp png_ptr;
png_infop info_ptr;
png_color_8 sig_bit;
png_bytep *row_pointers;
unsigned int i,j;
/* open the file */
fp = fopen(file_name, "wb");
if (fp == NULL)
return;
/* Create and initialize the png_struct with the desired error handler
* functions. If you want to use the default stderr and longjump method,
* you can supply NULL for the last three parameters. We also check that
* the library version is compatible with the one used at compile time,
* in case we are using dynamically linked libraries. REQUIRED.
*/
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp) NULL,
user_error_fn, user_warning_fn);
if (png_ptr == NULL)
{
fclose(fp);
return;
}
/* Allocate/initialize the image information data. REQUIRED */
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL)
{
fclose(fp);
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
return;
}
/* Set error handling. REQUIRED if you aren't supplying your own
* error handling functions in the png_create_write_struct() call.
*/
if (setjmp(png_ptr->jmpbuf))
{
/* If we get here, we had a problem writing the file */
fclose(fp);
png_destroy_write_struct(&png_ptr, (png_infopp) &info_ptr);
return;
}
/* Set up the output control using standard C streams. This
is required. */
png_init_io(png_ptr, fp);
/* Set the image information here. i_width and i_height are up to 2^31,
* bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
* the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
* PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
* or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
* PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
* currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
*/
png_set_IHDR(png_ptr, info_ptr, i_width, i_height, 8, PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_BASE);
/* For color images: */
sig_bit.red = 8;
sig_bit.green = 8;
sig_bit.blue = 8;
if (text_ptr)
png_set_text(png_ptr, info_ptr, text_ptr, i_text_count);
/* Write the file header information. REQUIRED */
png_write_info(png_ptr, info_ptr);
/* Once we write out the header, the compression type on the text
* chunks gets changed to PNG_TEXT_COMPRESSION_NONE_WR or
* PNG_TEXT_COMPRESSION_zTXt_WR, so it doesn't get written out again
* at the end.
*/
/* Shift the pixels up to a legal bit depth and fill in
* as appropriate to correctly scale the image.
*/
png_set_shift(png_ptr, &sig_bit);
/* pack pixels into bytes */
png_set_packing(png_ptr);
row_pointers = png_malloc(png_ptr, i_height*sizeof(png_bytep *));
for (i=0, j=0; i<i_height; i++, j+=i_width*3) {
row_pointers[i] = rgb_image + j;
}
png_set_rows (png_ptr, info_ptr, row_pointers);
png_write_image(png_ptr, row_pointers);
/* You can write optional chunks like tEXt, zTXt, and tIME at the end
* as well.
*/
/* It is REQUIRED to call this to finish writing the rest of the file */
png_write_end(png_ptr, info_ptr);
/* if you allocated any text comments, free them here */
/* free image data if allocated. */
/* clean up after the write, and free any memory allocated */
png_destroy_info_struct(png_ptr, &info_ptr);
/* clean up after the write, and free any memory allocated */
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
fclose(fp);
return;
}
#ifdef STANDALONE
int
main(int argc, char **argv)
{
char image_data[3*16 * 3*16 * 3];
int i,j,k,l,m;
char r,g,b,t, or,og,ob;
or=0x00; og=0xFF; ob=0x0;
m=0;
for (i=0; i<3; i++) {
t=or; or=og; og=ob; ob=t;
for (j=0; j<16; j++) {
r=or; g=og; b=ob;
for (k=0; k<3; k++) {
for (l=0; l<16; l++) {
image_data[m++]=r;
image_data[m++]=g;
image_data[m++]=b;
}
t=r; r=g; g=b; b=t;
}
}
}
write_png("/tmp/pngtest.png", 3*16, 3*16, (void *) image_data) ;
return 0;
}
#endif /*STANDALONE*/
#endif /*HAVE_LIBPNG*/

View File

@ -1,40 +0,0 @@
/*****************************************************************************
* Dump an Image to a Portable Network Graphics (PNG) file
****************************************************************************
Copyright (C) 2004 VideoLAN
Author: Rocky Bernstein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#include <png.h>
/*
Dump an image to a Portable Network Graphics (PNG) file. File_name
is where the file goes, i_height and i_width are the height and
width in pixels of the image. The data for the image is stored as a
linear array RGB pixel entries: one byte for each of red, green,
and blue component. Thus row[i] will begin at rgb_image +
i*(i_width*3) and the blue pixel at image[i][0] would be rgb_image
+ i*(i_width*3) + 1.
text_ptr contains comments that can be written to the image. It can
be null. i_text_count is the number of entries in text_ptr.
*/
void write_png(const char *file_name, png_uint_32 i_height,
png_uint_32 i_width, void *rgb_image,
/*in*/ png_text *text_ptr, int i_text_count );

510
modules/codec/svcdsub.c Normal file
View File

@ -0,0 +1,510 @@
/*****************************************************************************
* svcdsub.c : Overlay Graphics Text (SVCD subtitles) decoder
*****************************************************************************
* Copyright (C) 2003, 2004 VideoLAN
* $Id$
*
* Authors: Rocky Bernstein
* Gildas Bazin <gbazin@videolan.org>
* Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
* Laurent Aimar <fenrir@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
#define DEBUG_SVCD 1
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <vlc/vlc.h>
#include <vlc/decoder.h>
#include "vlc_bits.h"
/*****************************************************************************
* Module descriptor.
*****************************************************************************/
static int DecoderOpen ( vlc_object_t * );
static int PacketizerOpen( vlc_object_t * );
static void DecoderClose ( vlc_object_t * );
vlc_module_begin();
set_description( _("Philips OGT (SVCD subtitle) decoder") );
set_capability( "decoder", 50 );
set_callbacks( DecoderOpen, DecoderClose );
add_submodule();
set_description( _("Philips OGT (SVCD subtitle) packetizer") );
set_capability( "packetizer", 50 );
set_callbacks( PacketizerOpen, DecoderClose );
vlc_module_end();
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static subpicture_t *Decode( decoder_t *, block_t ** );
static block_t *Packetize ( decoder_t *, block_t ** );
static block_t *Reassemble ( decoder_t *, block_t * );
static void ParseHeader( decoder_t *, block_t * );
static subpicture_t *DecodePacket( decoder_t *, block_t * );
static void RenderImage( decoder_t *, block_t *, subpicture_region_t * );
#define GETINT16(p) ( (p[0] << 8) + p[1] ) ; p +=2;
#define GETINT32(p) ( (p[0] << 24) + (p[1] << 16) + \
(p[2] << 8) + (p[3]) ) ; p += 4;
#define SUBTITLE_BLOCK_EMPTY 0
#define SUBTITLE_BLOCK_PARTIAL 1
#define SUBTITLE_BLOCK_COMPLETE 2
struct decoder_sys_t
{
int b_packetizer;
int i_state; /* data-gathering state for this subtitle */
block_t *p_spu; /* Bytes of the packet. */
uint16_t i_image; /* image number in the subtitle stream */
uint8_t i_packet; /* packet number for above image number */
int i_spu_size; /* goal for subtitle_data_pos while gathering,
size of used subtitle_data later */
uint16_t i_image_offset; /* offset from subtitle_data to compressed
image data */
int i_image_length; /* size of the compressed image data */
int second_field_offset; /* offset of odd raster lines */
int metadata_offset; /* offset to data describing the image */
int metadata_length; /* length of metadata */
mtime_t i_duration; /* how long to display the image, 0 stands
for "until next subtitle" */
uint16_t i_x_start, i_y_start; /* position of top leftmost pixel of
image when displayed */
uint16_t i_width, i_height; /* dimensions in pixels of image */
uint8_t p_palette[4][4]; /* Palette of colors used in subtitle */
};
/*****************************************************************************
* DecoderOpen: open/initialize the svcdsub decoder.
*****************************************************************************/
static int DecoderOpen( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
decoder_sys_t *p_sys;
if( p_dec->fmt_in.i_codec != VLC_FOURCC( 'o','g','t',' ' ) )
{
return VLC_EGENERIC;
}
p_dec->p_sys = p_sys = malloc( sizeof( decoder_sys_t ) );
p_sys->b_packetizer = VLC_FALSE;
p_sys->i_image = -1;
p_sys->i_state = SUBTITLE_BLOCK_EMPTY;
p_sys->p_spu = NULL;
es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 'o','g','t',' ' ) );
p_dec->pf_decode_sub = Decode;
p_dec->pf_packetize = Packetize;
return VLC_SUCCESS;
}
/*****************************************************************************
* PacketizerOpen: open/initialize the svcdsub packetizer.
*****************************************************************************/
static int PacketizerOpen( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
if( DecoderOpen( p_this ) != VLC_SUCCESS ) return VLC_EGENERIC;
p_dec->p_sys->b_packetizer = VLC_TRUE;
return VLC_SUCCESS;
}
/*****************************************************************************
* DecoderClose: closes the svcdsub decoder/packetizer.
*****************************************************************************/
void DecoderClose( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
decoder_sys_t *p_sys = p_dec->p_sys;
if( p_sys->p_spu ) block_ChainRelease( p_sys->p_spu );
free( p_sys );
}
/*****************************************************************************
* Decode:
*****************************************************************************/
static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block )
{
block_t *p_block, *p_spu;
if( pp_block == NULL || *pp_block == NULL ) return NULL;
p_block = *pp_block;
*pp_block = NULL;
if( !(p_spu = Reassemble( p_dec, p_block )) ) return NULL;
/* Parse and decode */
return DecodePacket( p_dec, p_spu );
}
/*****************************************************************************
* Packetize:
*****************************************************************************/
static block_t *Packetize( decoder_t *p_dec, block_t **pp_block )
{
block_t *p_block, *p_spu;
if( pp_block == NULL || *pp_block == NULL ) return NULL;
p_block = *pp_block;
*pp_block = NULL;
if( !(p_spu = Reassemble( p_dec, p_block )) ) return NULL;
p_spu->i_dts = p_spu->i_pts;
p_spu->i_length = 0;
return p_spu;
}
/*****************************************************************************
Reassemble:
The data for single screen subtitle may come in one of many
non-contiguous packets of a stream. This routine is called when the
next packet in the stream comes in. The job of this routine is to
parse the header, if this is the beginning, and combine the packets
into one complete subtitle unit.
If everything is complete, we will return a block. Otherwise return
NULL.
The format of the beginning of the subtitle packet that is used here.
size description
-------------------------------------------
byte subtitle channel (0..7) in bits 0-3
byte subtitle packet number of this subtitle image 0-N,
if the subtitle packet is complete, the top bit of the byte is 1.
uint16 subtitle image number
*****************************************************************************/
#define SPU_HEADER_LEN 5
static block_t *Reassemble( decoder_t *p_dec, block_t *p_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *p_buffer;
uint16_t i_expected_image;
uint8_t i_packet, i_expected_packet;
if( p_block->i_buffer < SPU_HEADER_LEN )
{
msg_Dbg( p_dec, "invalid packet header (size %d < %d)" ,
p_block->i_buffer, SPU_HEADER_LEN );
block_Release( p_block );
return NULL;
}
p_buffer = p_block->p_buffer;
if( p_sys->i_state == SUBTITLE_BLOCK_EMPTY )
{
i_expected_image = p_sys->i_image + 1;
i_expected_packet = 0;
}
else
{
i_expected_image = p_sys->i_image;
i_expected_packet = p_sys->i_packet + 1;
}
p_buffer += 2;
if( *p_buffer & 0x80 )
{
p_sys->i_state = SUBTITLE_BLOCK_COMPLETE;
i_packet = *p_buffer++ & 0x7F;
}
else
{
p_sys->i_state = SUBTITLE_BLOCK_PARTIAL;
i_packet = *p_buffer++;
}
p_sys->i_image = GETINT16(p_buffer);
if( p_sys->i_image != i_expected_image )
{
msg_Warn( p_dec, "expected subtitle image %u but found %u",
i_expected_image, p_sys->i_image );
}
if( i_packet != i_expected_packet )
{
msg_Warn( p_dec, "expected subtitle image packet %u but found %u",
i_expected_packet, i_packet );
}
p_block->p_buffer += SPU_HEADER_LEN;
p_block->i_buffer -= SPU_HEADER_LEN;
p_sys->i_packet = i_packet;
/* First packet in the subtitle block */
if( !p_sys->i_packet ) ParseHeader( p_dec, p_block );
block_ChainAppend( &p_sys->p_spu, p_block );
if( p_sys->i_state == SUBTITLE_BLOCK_COMPLETE )
{
block_t *p_spu = block_ChainGather( p_sys->p_spu );
if( p_spu->i_buffer != p_sys->i_spu_size )
{
msg_Warn( p_dec, "SPU packets size=%d should be %d",
p_spu->i_buffer, p_sys->i_spu_size );
}
msg_Dbg( p_dec, "subtitle packet complete, size=%d", p_spu->i_buffer);
p_sys->i_state = SUBTITLE_BLOCK_EMPTY;
p_sys->p_spu = 0;
return p_spu;
}
return NULL;
}
/******************************************************************************
The format is roughly as follows (everything is big-endian):
size description
-------------------------------------------
byte subtitle channel (0..7) in bits 0-3
byte subtitle packet number of this subtitle image 0-N,
if the subtitle packet is complete, the top bit of the byte is 1.
u_int16 subtitle image number
u_int16 length in bytes of the rest
byte option flags, unknown meaning except bit 3 (0x08) indicates
presence of the duration field
byte unknown
u_int32 duration in 1/90000ths of a second (optional), start time
is as indicated by the PTS in the PES header
u_int32 xpos
u_int32 ypos
u_int32 width (must be even)
u_int32 height (must be even)
byte[16] palette, 4 palette entries, each contains values for
Y, U, V and transparency, 0 standing for transparent
byte command,
cmd>>6==1 indicates shift
(cmd>>4)&3 is direction from, (0=top,1=left,2=right,3=bottom)
u_int32 shift duration in 1/90000ths of a second
u_int16 offset of odd-numbered scanlines - subtitle images are
given in interlace order
byte[] limited RLE image data in interlace order (0,2,4... 1,3,5) with
2-bits per palette number
******************************************************************************/
static void ParseHeader( decoder_t *p_dec, block_t *p_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *p = p_block->p_buffer;
uint8_t i_options, i_options2, i_cmd, i_cmd_arg;
int i;
p_sys->i_spu_size = GETINT16(p);
i_options = *p++;
i_options2 = *p++;
if( i_options & 0x08 ) { p_sys->i_duration = GETINT32(p); }
else p_sys->i_duration = 0; /* Ephemer subtitle */
p_sys->i_duration *= 100 / 9;
p_sys->i_x_start = GETINT16(p);
p_sys->i_y_start = GETINT16(p);
p_sys->i_width = GETINT16(p);
p_sys->i_height = GETINT16(p);
for( i = 0; i < 4; i++ )
{
p_sys->p_palette[i][0] = *p++;
p_sys->p_palette[i][1] = *p++;
p_sys->p_palette[i][2] = *p++;
p_sys->p_palette[i][3] = *p++;
}
i_cmd = *p++;
/* We do not really know this, FIXME */
if( i_cmd ) {i_cmd_arg = GETINT32(p);}
/* Actually, this is measured against a different origin, so we have to
* adjust it */
p_sys->second_field_offset = GETINT16(p);
p_sys->i_image_offset = p - p_block->p_buffer;
p_sys->i_image_length = p_sys->i_spu_size - p_sys->i_image_offset;
p_sys->metadata_length = p_sys->i_image_offset;
#ifdef DEBUG_SVCD
msg_Dbg( p_dec, "x-start: %d, y-start: %d, width: %d, height %d, "
"spu size: %d, duration: %lu (d:%d p:%d)",
p_sys->i_x_start, p_sys->i_y_start,
p_sys->i_width, p_sys->i_height,
p_sys->i_spu_size, (long unsigned int) p_sys->i_duration,
p_sys->i_image_length, p_sys->i_image_offset);
for( i = 0; i < 4; i++ )
{
msg_Dbg( p_dec, "palette[%d]= T: %2x, Y: %2x, u: %2x, v: %2x", i,
p_sys->p_palette[i][3], p_sys->p_palette[i][0],
p_sys->p_palette[i][1], p_sys->p_palette[i][2] );
}
#endif
}
/*****************************************************************************
* DecodePacket: parse and decode an SPU packet
*****************************************************************************
* This function parses and decodes an SPU packet and, if valid, returns a
* subpicture.
*****************************************************************************/
static subpicture_t *DecodePacket( decoder_t *p_dec, block_t *p_data )
{
decoder_sys_t *p_sys = p_dec->p_sys;
subpicture_t *p_spu;
subpicture_region_t *p_region;
video_format_t fmt;
int i;
/* Allocate the subpicture internal data. */
p_spu = p_dec->pf_spu_buffer_new( p_dec );
if( !p_spu ) return NULL;
p_spu->i_x = p_sys->i_x_start;
p_spu->i_x = p_spu->i_x * 3 / 4; /* FIXME: use aspect ratio for x? */
p_spu->i_y = p_sys->i_y_start;
p_spu->i_start = p_data->i_pts;
p_spu->i_stop = p_data->i_pts + p_sys->i_duration;
p_spu->b_ephemer = VLC_TRUE;
/* Create new SPU region */
memset( &fmt, 0, sizeof(video_format_t) );
fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
fmt.i_aspect = VOUT_ASPECT_FACTOR;
fmt.i_width = fmt.i_visible_width = p_sys->i_width;
fmt.i_height = fmt.i_visible_height = p_sys->i_height;
fmt.i_x_offset = fmt.i_y_offset = 0;
p_region = p_spu->pf_create_region( VLC_OBJECT(p_dec), &fmt );
if( !p_region )
{
msg_Err( p_dec, "cannot allocate SPU region" );
//goto error;
}
p_spu->p_region = p_region;
p_region->i_x = p_region->i_y = 0;
/* Build palette */
fmt.p_palette->i_entries = 4;
for( i = 0; i < fmt.p_palette->i_entries; i++ )
{
fmt.p_palette->palette[i][0] = p_sys->p_palette[i][0];
fmt.p_palette->palette[i][1] = p_sys->p_palette[i][1];
fmt.p_palette->palette[i][2] = p_sys->p_palette[i][2];
fmt.p_palette->palette[i][3] = p_sys->p_palette[i][3];
}
RenderImage( p_dec, p_data, p_region );
return p_spu;
}
/*****************************************************************************
* ParseImage: parse the image part of the subtitle
*****************************************************************************
This part parses the subtitle graphical data and renders it.
The image is encoded using two bits per pixel that select a palette
entry except that value 0 starts a limited run-length encoding for
color 0. When 0 is seen, the next two bits encode one less than the
number of pixels, so we can encode run lengths from 1 to 4. These get
filled with the color in palette entry 0.
The encoding of each line is padded to a whole number of bytes. The
first field is padded to an even byte length and the complete subtitle
is padded to a 4-byte multiple that always include one zero byte at
the end.
However we'll transform this so that that the RLE is expanded and
interlacing will also be removed.
*****************************************************************************/
static void RenderImage( decoder_t *p_dec, block_t *p_data,
subpicture_region_t *p_region )
{
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t *p_dest = p_region->picture.Y_PIXELS;
int i_field; /* The subtitles are interlaced */
int i_row, i_column; /* scanline row/column number */
uint8_t i_color, i_count;
bs_t bs;
bs_init( &bs, p_data->p_buffer + p_sys->i_image_offset,
p_data->i_buffer - p_sys->i_image_offset );
for( i_field = 0; i_field < 2; i_field++ )
{
for( i_row = i_field; i_row < p_sys->i_height; i_row += 2 )
{
for( i_column = 0; i_column < p_sys->i_width; i_column++ )
{
i_color = bs_read( &bs, 2 );
if( i_color == 0 && (i_count = bs_read( &bs, 2 )) )
{
i_count = __MIN( i_count, p_sys->i_width - i_column );
memset( &p_dest[i_row * p_region->picture.Y_PITCH +
i_column], 0, i_count + 1 );
i_column += i_count;
continue;
}
p_dest[i_row * p_region->picture.Y_PITCH + i_column] = i_color;
}
bs_align( &bs );
}
/* odd field */
bs_init( &bs, p_data->p_buffer + p_sys->i_image_offset +
p_sys->second_field_offset,
p_data->i_buffer - p_sys->i_image_offset -
p_sys->second_field_offset );
}
}