--- /dev/null
+/*****************************************************************************
+ * 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;
+}
+
--- /dev/null
+/*****************************************************************************
+ * 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
+ pixel with subtitle pixel. */
+
+ p_destptr = p_dest - i_x - i_y;
+
+ i_colprecomp = (uint16_t) ( p_source->y * 15 );
+ *p_destptr = i_colprecomp >> 4;
+
+ break;
+
+ default:
+ /* Blend in underlying pixel subtitle pixel. */
+
+ /* To be able to divide by 16 (>>4) we add 1 to the alpha.
+ * This means Alpha 0 won't be completely transparent, but
+ * that's handled in a special case above anyway. */
+
+ p_destptr = p_dest - i_x - i_y;
+
+ i_colprecomp = (uint16_t) ( (p_source->y
+ * (uint16_t)(p_source->t + 1) ) );
+ i_destalpha = 15 - p_source->t;
+
+ *p_destptr = ( i_colprecomp +
+ (uint16_t)*p_destptr * i_destalpha ) >> 4;
+ break;
+ }
+ }
+ }
+}
+