mirror of
https://github.com/videolan/vlc.git
synced 2025-01-10 09:48:21 +08:00
* 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:
parent
805a60e6e9
commit
2cb7e0165b
@ -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
|
||||
|
@ -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
611
modules/codec/cvdsub.c
Normal 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 );
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
@ -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:
|
||||
*/
|
@ -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*/
|
@ -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;
|
||||
}
|
@ -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 );
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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:
|
||||
*/
|
@ -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 * );
|
||||
|
@ -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:
|
||||
*/
|
@ -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:
|
||||
*/
|
@ -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
@ -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 * );
|
||||
|
@ -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 */
|
||||
|
||||
};
|
@ -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*/
|
@ -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
510
modules/codec/svcdsub.c
Normal 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 );
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user