Commit 1170be35 authored by Rocky Bernstein's avatar Rocky Bernstein
Browse files

Check in a stable copy for reference against future changes. Should

not affect or be seen in upcoming release.
parent c87f2a54
/*****************************************************************************
* parse.c: Philips OGT (SVCD subtitle) packet parser
*****************************************************************************
* Copyright (C) 2003 VideoLAN
* $Id: parse.c,v 1.1 2003/12/26 01:39:23 rocky Exp $
*
* 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 "ogt.h"
/* 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 * );
static void DestroySPU ( subpicture_t * );
static void UpdateSPU ( subpicture_t *, vlc_object_t * );
static int CropCallback ( vlc_object_t *, char const *,
vlc_value_t, vlc_value_t, void * );
/*
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;
u_int8_t *p = p_buffer;
int i;
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);
} 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->pi_palette[i].y = *p++;
p_sys->pi_palette[i].u = *p++;
p_sys->pi_palette[i].v = *p++;
/* We have just 4-bit resolution for alpha, but the value for SVCD
* has 8 bits so we scale down the values to the acceptable range */
p_sys->pi_palette[i].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->comp_image_offset = p - p_buffer;
p_sys->comp_image_length = p_sys->i_spu_size - p_sys->comp_image_offset;
p_sys->metadata_length = p_sys->comp_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: %u (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, p_sys->i_duration,
p_sys->comp_image_length, p_sys->comp_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->pi_palette[i].t, p_sys->pi_palette[i].y,
p_sys->pi_palette[i].u, p_sys->pi_palette[i].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 = vout_CreateSubPicture( p_sys->p_vout, MEMORY_SUBPICTURE );
if( p_spu == NULL )
{
return;
}
/* 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 = E_(RenderSPU);
p_spu->pf_destroy = DestroySPU;
p_spu->p_sys->p_data = (uint8_t*)p_spu->p_sys + sizeof( subpicture_sys_t );
p_spu->p_sys->b_palette = VLC_FALSE;
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 / 2;
p_spu->i_y = p_sys->i_y_start;
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 * 10);
p_spu->p_sys->b_crop = VLC_FALSE;
/* 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 */
vout_DestroySubPicture( p_sys->p_vout, p_spu );
return;
}
/* SPU is finished - we can ask the video output to display it */
vout_DisplaySubPicture( p_sys->p_vout, p_spu );
}
/* Advance pointer to image pointer, update internal i_remaining counter
and check that we haven't goine too far in the image data. */
#define advance_color_pointer_byte \
p++; \
i_remaining=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_remaining--; \
if ( i_remaining == 0 ) { \
advance_color_pointer_byte; \
}
/* 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_remaining
which indicates where we are in the byte.
*/
static inline ogt_color_t
ExtractField(uint8_t *p, unsigned int i_remaining)
{
return ( ( *p >> 2*(i_remaining-1) ) & 0x3 );
}
#ifdef FINISHED
/* 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. i_width, is updated to new ratio.
The aspect ratio is assumed to be between 1 and 2.
*/
static void
ScaleX( uint8_t *p_dest, /*in out */ u_int16_t *i_width, u_int16_t i_height,
unsigned int scale_x, unsigned int scale_y )
{
int i_row, i_col;
uint8_t *p1 = p_dest;
uint8_t *p2 = p_dest + PIXEL_SIZE;
unsigned int used=0; /* Number of bytes used up in p1. */
for ( i_row=0; i_row < i_height - 1; i_row++ ) {
for ( i_col=0; i_col <= (*i_width)-2; i_col++ ) {
unsigned int i;
unsigned int w1= scale_x - used;
unsigned int w2= scale_y - w1;
used = w2;
for (i = 0; i < PIXEL_SIZE; i++ ) {
*p1 = ( (*p1 * w1) + (*p2 * w2) ) / scale_y;
p1++; p2++;
}
if (scale_x == used) {
p1 = p2;
p2 += PIXEL_SIZE;
used = 0;
}
}
}
/* *i_width = ((*i_width) * scale_y) / scale_x; */
}
#endif
/*****************************************************************************
* 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 8-bit alpha, 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_remaining; /* number of 2-bit pixels remaining
in byte of *p */
uint8_t i_pending_zero = 0; /* number of pixels to fill with
color zero 0..4 */
ogt_color_t i_color; /* current pixel color: 0..3 */
uint8_t *p = p_sys->subtitle_data + p_sys->comp_image_offset;
uint8_t *maxp = p + p_sys->comp_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_remaining = 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 ) {
i_pending_zero--;
i_color = 0;
} else {
i_color = ExtractField( p, i_remaining);
advance_color_pointer;
if ( i_color == 0 ) {
i_pending_zero = ExtractField( p, i_remaining );
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_remaining != 4 ) {
/* Lines are padded to complete bytes, ignore padding */
advance_color_pointer_byte;
}
}
p = p_sys->subtitle_data + p_sys->comp_image_offset
+ p_sys->second_field_offset;
}
/* Dump out image not interlaced... */
if (p_sys && p_sys->i_debug & DECODE_DBG_IMAGE) {
uint8_t *p = p_dest;
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");
}
/* 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.
*/
{
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->pi_palette[p_from[n]];
}
}
#ifdef FINISHED
/* The video is automatically scaled. However subtitle bitmaps
assume a 1:1 aspect ratio. So we need to scale to compensate for
or undo the effects of video output scaling.
*/
/* FIXME do the right scaling depending on vout. It may not be 4:3 */
ScaleX( p_dest, &(p_sys->i_width), i_height, 3, 4 );
#endif
return VLC_SUCCESS;
}
/*****************************************************************************
* DestroySPU: subpicture destructor
*****************************************************************************/
static void DestroySPU( subpicture_t *p_spu )
{
if( p_spu->p_sys->p_input )
{
/* Detach from our input thread */
var_DelCallback( p_spu->p_sys->p_input, "highlight",
CropCallback, p_spu );
vlc_object_release( p_spu->p_sys->p_input );
}
vlc_mutex_destroy( &p_spu->p_sys->lock );
free( p_spu->p_sys );
}
/*****************************************************************************
* UpdateSPU: update subpicture settings
*****************************************************************************
* This function is called from CropCallback and at initialization time, to
* retrieve crop information from the input.
*****************************************************************************/
static void UpdateSPU( subpicture_t *p_spu, vlc_object_t *p_object )
{
vlc_value_t val;
if( var_Get( p_object, "highlight", &val ) )
{
return;
}
p_spu->p_sys->b_crop = val.b_bool;
if( !p_spu->p_sys->b_crop )
{
return;
}
var_Get( p_object, "x-start", &val );
p_spu->p_sys->i_x_start = val.i_int;
var_Get( p_object, "y-start", &val );
p_spu->p_sys->i_y_start = val.i_int;
var_Get( p_object, "x-end", &val );
p_spu->p_sys->i_x_end = val.i_int;
var_Get( p_object, "y-end", &val );
p_spu->p_sys->i_y_end = val.i_int;
}
/*****************************************************************************
* CropCallback: called when the highlight properties are changed
*****************************************************************************
* This callback is called from the input thread when we need cropping
*****************************************************************************/
static int CropCallback( vlc_object_t *p_object, char const *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
UpdateSPU( (subpicture_t *)p_data, p_object );
return VLC_SUCCESS;
}
/*****************************************************************************
* render.c : Philips OGT (SVCD Subtitle) renderer
*****************************************************************************
* Copyright (C) 2003 VideoLAN
* $Id: render.c,v 1.1 2003/12/26 01:39:35 rocky Exp $
*
* Author: Rocky Bernstein
* based on code from:
* Sam Hocevar <sam@zoy.org>
* Rudolf Cornelissen <rag.cornelissen@inter.nl.net>
* Roine Gustafsson <roine@popstar.com>
*
* 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 "ogt.h"
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static void RenderI420( vout_thread_t *, picture_t *, const subpicture_t *,
vlc_bool_t );
/*****************************************************************************
* RenderSPU: draw an SPU on a picture
*****************************************************************************
This is a fast implementation of the subpicture drawing code. The
data has been preprocessed. Each byte has a run-length 1 in the upper
nibble and a color in the lower nibble. The interleaving of rows has
been done. Most sanity checks are already done so that this
routine can be as fast as possible.
*****************************************************************************/
void E_(RenderSPU)( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu )
{
#ifndef FINISHED
printf("+++%x\n", p_vout->output.i_chroma);
#endif
switch( p_vout->output.i_chroma )
{
/* I420 target, no scaling */
case VLC_FOURCC('I','4','2','0'):
case VLC_FOURCC('I','Y','U','V'):
case VLC_FOURCC('Y','V','1','2'):
RenderI420( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
break;
/* RV16 target, scaling */
case VLC_FOURCC('R','V','1','6'):
msg_Err( p_vout, "RV16 not implimented yet" );
break;
/* RV32 target, scaling */
case VLC_FOURCC('R','V','2','4'):
case VLC_FOURCC('R','V','3','2'):
msg_Err( p_vout, "RV24/VF32 not implimented yet" );
break;
/* NVidia overlay, no scaling */
case VLC_FOURCC('Y','U','Y','2'):
msg_Err( p_vout, "YUV2 not implimented yet" );
break;
default:
msg_Err( p_vout, "unknown chroma, can't render SPU" );
break;
}
}
/* Following functions are local */
static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu, vlc_bool_t b_crop )
{
/* Common variables */
uint8_t *p_dest;
uint8_t *p_destptr;
ogt_yuvt_t *p_source;
int i_x, i_y;
uint16_t i_colprecomp, i_destalpha;
/* Crop-specific */
int i_x_start, i_y_start, i_x_end, i_y_end;
/* int i=0; */
p_dest = p_pic->Y_PIXELS + p_spu->i_x + p_spu->i_width
+ p_pic->Y_PITCH * ( p_spu->i_y + p_spu->i_height );
i_x_start = p_spu->i_width - p_spu->p_sys->i_x_end;
i_y_start = p_pic->Y_PITCH * (p_spu->i_height - p_spu->p_sys->i_y_end );
i_x_end = p_spu->i_width - p_spu->p_sys->i_x_start;
i_y_end = p_pic->Y_PITCH * (p_spu->i_height - p_spu->p_sys->i_y_start );
p_source = (ogt_yuvt_t *)p_spu->p_sys->p_data;
/* printf("+++spu width: %d, height %d\n", p_spu->i_width,
p_spu->i_height); */
/* Draw until we reach the bottom of the subtitle */
for( i_y = p_spu->i_height * p_pic->Y_PITCH ;
i_y ;
i_y -= p_pic->Y_PITCH )
{
/* printf("+++begin line: %d,\n", i++); */
/* Draw until we reach the end of the line */
for( i_x = p_spu->i_width ; i_x ; i_x--, p_source++ )
{
if( b_crop
&& ( i_x < i_x_start || i_x > i_x_end
|| i_y < i_y_start || i_y > i_y_end ) )
{
continue;
}
/* printf( "t: %x, y: %x, u: %x, v: %x\n",
p_source->t, p_source->y, p_source->u, p_source->v ); */
switch( p_source->t )
{
case 0x00:
/* Completely transparent. Don't change underlying pixel */
break;
case 0x0f:
/* Completely opaque. Completely overwrite underlying