--- /dev/null
+/*****************************************************************************
+ * Common SVCD and VCD subtitle routines.
+ *****************************************************************************
+ * Copyright (C) 2003 VideoLAN
+ * $Id: common.c,v 1.1 2003/12/28 04:51:52 rocky Exp $
+ *
+ * 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 "common.h"
+
+/*****************************************************************************
+ 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 )
+ {
+ /* FIXME check if it's ok to not lock vout */
+ if( p_sys->p_vout != NULL && p_sys->p_vout->p_subpicture != NULL )
+ {
+ subpicture_t * p_subpic;
+ int i_subpic;
+
+ for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
+ {
+ p_subpic = &p_sys->p_vout->p_subpicture[i_subpic];
+
+ if( p_subpic != NULL &&
+ ( ( p_subpic->i_status == RESERVED_SUBPICTURE ) ||
+ ( p_subpic->i_status == READY_SUBPICTURE ) ) )
+ {
+ vout_DestroySubPicture( p_sys->p_vout, p_subpic );
+ }
+ }
+ }
+ }
+
+ if( p_sys->p_block )
+ {
+ block_ChainRelease( p_sys->p_block );
+ }
+
+ 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 ) {
+ 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;
+}
+
+
+/* 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.
+*/
+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 used=0; /* Number of bytes used up in p_src1. */
+
+ dbg_print( (DECODE_DBG_CALL|DECODE_DBG_TRANSFORM) ,
+ "Old width: %d, new width: %d",
+ p_spu->i_width, i_new_width);
+
+ for ( i_row=0; i_row <= p_spu->i_height - 1; i_row++ ) {
+
+ if (used != 0) {
+ /* Discard the remaining piece of the column of the previous line*/
+ 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 - used;
+ unsigned int w2= i_scale_y - w1;
+
+ used = w2;
+ for (i = 0; i < PIXEL_SIZE; i++ ) {
+ *p_dst = ( (*p_src1 * w1) + (*p_src2 * w2) ) / i_scale_y;
+ p_src1++; p_src2++; p_dst++;
+ }
+
+ if (i_scale_x == used) {
+ /* End of last pixel was end of p_src2. */
+ p_src1 = p_src2;
+ p_src2 += PIXEL_SIZE;
+ i_col++;
+ 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 - 1; i_row++ ) {
+ for ( i_col=0; i_col < p_spu->i_width - 1; i_col++ ) {
+ printf("%1x", p_source->s.t);
+ p_source++;
+ }
+ printf("\n");
+ }
+ }
+
+}
+
+/*****************************************************************************
+ * 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;
+ }
+
+ 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;
+
+}
+
--- /dev/null
+/*****************************************************************************
+ * Header for Common SVCD and VCD subtitle routines.
+ *****************************************************************************
+ * Copyright (C) 2003 VideoLAN
+ * $Id: common.h,v 1.1 2003/12/28 04:51:52 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 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 );
+
+
--- /dev/null
+/*****************************************************************************
+ * cvd.c : CVD Subtitle decoder thread
+ *****************************************************************************
+ * Copyright (C) 2003 VideoLAN
+ * $Id: cvd.c,v 1.1 2003/12/28 04:51:52 rocky Exp $
+ *
+ * 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"
+
+#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" \
+ "misc info 32\n" )
+
+/*****************************************************************************
+ * Module descriptor.
+ *****************************************************************************/
+static int DecoderOpen ( vlc_object_t * );
+static int PacketizerOpen( vlc_object_t * );
+
+vlc_module_begin();
+ set_description( _("CVD subtitle decoder") );
+ set_capability( "decoder", 50 );
+ set_callbacks( DecoderOpen, VCDSubClose );
+
+ add_integer ( MODULE_STRING "-debug", 0, NULL,
+ N_("set debug mask for additional debugging."),
+ N_(DEBUG_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 void Decode ( decoder_t *, block_t ** );
+static block_t *Packetize( decoder_t *, block_t ** );
+
+
+/*****************************************************************************
+ * DecoderOpen
+ *****************************************************************************
+ * Tries to launch a decoder and return score so that the interface is able
+ * to chose.
+ *****************************************************************************/
+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->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( DecoderOpen( p_this ) )
+ {
+ return VLC_EGENERIC;
+ }
+ p_dec->p_sys->b_packetizer = VLC_TRUE;
+
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Decode:
+ *****************************************************************************/
+static void
+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 );
+
+ 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 ) ) )
+ {
+ /* Parse and decode */
+ E_(ParsePacket)( p_dec );
+
+ vlc_object_release( p_sys->p_vout );
+ }
+
+ VCDSubInitSubtitleBlock ( p_sys );
+ }
+
+}
+
+/*****************************************************************************
+ * 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 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);
+
+ if( config_GetInt( p_dec, "spu-channel" ) != p_buffer[1] )
+ 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 - 5 );
+
+ 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;
+}
--- /dev/null
+/*****************************************************************************
+ * cvd.h : CVD subtitles decoder thread interface
+ *****************************************************************************
+ * Copyright (C) 2003 VideoLAN
+ * $Id: cvd.h,v 1.1 2003/12/28 04:51:52 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 * );
+
--- /dev/null
+/*****************************************************************************
+ * parse.c: Philips OGT (SVCD subtitle) packet parser
+ *****************************************************************************
+ * Copyright (C) 2003 VideoLAN
+ * $Id: cvd_parse.c,v 1.1 2003/12/28 04:51:52 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 "subtitle.h"
+#include "render.h"
+#include "cvd.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 * );
+
+/*
+ * 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 present several differences compared to SVCD OGT
+ * subtitles. Firstly, the image comes first and the metadata is at
+ * the end. So that the metadata can be found easily, the subtitle
+ * begins with two two-byte (everything is big-endian again) that
+ * describe, the total size of the subtitle data and the offset to the
+ * metadata (size of the image data plus the four bytes at the
+ * beginning.
+ *
+ * Image data comes interlaced and uses RLE. Coding is based in
+ * four-bit nibbles that are further subdivided in a two-bit repeat
+ * count and a two-bit color number so that up to three pixels can be
+ * describe with a total of four bits. The function of a 0 repeat
+ * count is unknown. It might be used for RLE extension. There is a
+ * special case, 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.
+ *
+ * The metadata section does not follow a fixed pattern, every
+ * metadata item consists of a tag byte followed by parameters. In all
+ * cases known, the block (including the tag byte) is exactly four
+ * bytes in length. Read the code for the rest.
+ */
+
+/* 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;
+
+ dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
+
+ /* To be finished...*/
+ return;
+}
+
+
+/*****************************************************************************
+ * 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;
+
+ dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
+
+ /* To be completed... */
+ return;
+
+}
+
+/* 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 );
+}
+
+/*****************************************************************************
+ * 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;
+
+ dbg_print( (DECODE_DBG_CALL) , "");
+ /* To be finished...*/
+ return VLC_EGENERIC;
+
+}
+
* ogt.c : Overlay Graphics Text (SVCD subtitles) decoder thread
*****************************************************************************
* Copyright (C) 2003 VideoLAN
- * $Id: ogt.c,v 1.5 2003/12/28 02:01:11 rocky Exp $
+ * $Id: ogt.c,v 1.6 2003/12/28 04:51:52 rocky Exp $
*
* Authors: Rocky Bernstein
* based on code from:
#include "subtitle.h"
#include "ogt.h"
+#include "common.h"
#define DEBUG_LONGTEXT N_( \
"This integer when viewed in binary is a debugging mask\n" \
static int DecoderOpen ( vlc_object_t * );
static int PacketizerOpen( vlc_object_t * );
-static void Close ( vlc_object_t * );
-
vlc_module_begin();
set_description( _("Philips OGT (SVCD subtitle) decoder") );
set_capability( "decoder", 50 );
- set_callbacks( DecoderOpen, Close );
+ set_callbacks( DecoderOpen, VCDSubClose );
add_integer ( MODULE_STRING "-debug", 0, NULL,
N_("set debug mask for additional debugging."),
add_submodule();
set_description( _("Philips OGT (SVCD subtitle) packetizer") );
set_capability( "packetizer", 50 );
- set_callbacks( PacketizerOpen, Close );
+ set_callbacks( PacketizerOpen, VCDSubClose );
vlc_module_end();
/*****************************************************************************
* Local prototypes
*****************************************************************************/
-static void InitSubtitleBlock( decoder_sys_t * p_sys );
-static vout_thread_t *FindVout( decoder_t *);
static block_t *Reassemble( decoder_t *, block_t ** );
-
static void Decode ( decoder_t *, block_t ** );
static block_t *Packetize( decoder_t *, block_t ** );
p_sys->i_image = -1;
p_sys->subtitle_data = NULL;
- InitSubtitleBlock( p_sys );
+ VCDSubInitSubtitleBlock( p_sys );
es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 'o','g','t',' ' ) );
return VLC_SUCCESS;
}
-/*****************************************************************************
- * Close:
- *****************************************************************************/
-static void Close( 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 )
- {
- /* FIXME check if it's ok to not lock vout */
- if( p_sys->p_vout != NULL && p_sys->p_vout->p_subpicture != NULL )
- {
- subpicture_t * p_subpic;
- int i_subpic;
-
- for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
- {
- p_subpic = &p_sys->p_vout->p_subpicture[i_subpic];
-
- if( p_subpic != NULL &&
- ( ( p_subpic->i_status == RESERVED_SUBPICTURE ) ||
- ( p_subpic->i_status == READY_SUBPICTURE ) ) )
- {
- vout_DestroySubPicture( p_sys->p_vout, p_subpic );
- }
- }
- }
- }
-
- if( p_sys->p_block )
- {
- block_ChainRelease( p_sys->p_block );
- }
-
- free( p_sys );
-}
-
/*****************************************************************************
* Decode:
*****************************************************************************/
p_sys->i_pts = p_spu->i_pts;
block_ChainRelease( p_spu );
- if( ( p_sys->p_vout = FindVout( p_dec ) ) )
+ if( ( p_sys->p_vout = VCDSubFindVout( p_dec ) ) )
{
/* Parse and decode */
E_(ParsePacket)( p_dec );
vlc_object_release( p_sys->p_vout );
}
- InitSubtitleBlock ( p_sys );
+ VCDSubInitSubtitleBlock ( p_sys );
}
}
p_spu->i_dts = p_spu->i_pts;
p_spu->i_length = 0;
- InitSubtitleBlock( p_sys );
+ VCDSubInitSubtitleBlock( p_sys );
return block_ChainGather( p_spu );
}
return NULL;
}
-/* following functions are local */
-
-static void
-InitSubtitleData(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;
-}
-
-static void
-AppendData ( 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 ) {
- 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);
- }
-}
-
-/*****************************************************************************
- InitSubtitleBlock:
-
-Initialize so the next packet will start off a new one.
-
- *****************************************************************************/
-static void
-InitSubtitleBlock( 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;
-
-}
-
#define SPU_HEADER_LEN 5
/*****************************************************************************
if ( p_sys->i_packet == 0 ) {
/* First packet in the subtitle block */
E_(ParseHeader)( p_dec, p_buffer, p_block );
- InitSubtitleData(p_sys);
+ VCDSubInitSubtitleData(p_sys);
}
/* FIXME - remove append_data and use chainappend */
- AppendData( p_dec, p_buffer, p_block->i_buffer - 5 );
+ VCDSubAppendData( p_dec, p_buffer, p_block->i_buffer - 5 );
block_ChainAppend( &p_sys->p_block, p_block );
}
return NULL;
}
-
-/*****************************************************************************
- * FindVout: Find a vout or wait for one to be created.
- *****************************************************************************/
-static vout_thread_t *FindVout( 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;
-}
-
/*****************************************************************************
- * parse.c: Philips OGT (SVCD subtitle) packet parser
+ * Philips OGT (SVCD subtitle) packet parser
*****************************************************************************
* Copyright (C) 2003 VideoLAN
- * $Id: parse.c,v 1.3 2003/12/28 02:01:11 rocky Exp $
+ * $Id: ogt_parse.c,v 1.1 2003/12/28 04:51:52 rocky Exp $
*
- * Authors: Rocky Bernstein
+ * Author: Rocky Bernstein
* based on code from:
* Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
* Sam Hocevar <sam@zoy.org>
#include <vlc/decoder.h>
#include "subtitle.h"
+#include "common.h"
#include "render.h"
#include "ogt.h"
*****************************************************************************/
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):
/* Fill the p_spu structure */
vlc_mutex_init( p_dec, &p_spu->p_sys->lock );
- p_spu->pf_render = VCDRenderSPU;
- p_spu->pf_destroy = DestroySPU;
+ p_spu->pf_render = VCDSubRender;
+ 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;
return ( ( *p >> 2*(i_remaining-1) ) & 0x3 );
}
-/* 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.
-*/
-static void
-ScaleX( 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 used=0; /* Number of bytes used up in p_src1. */
-
- dbg_print( (DECODE_DBG_CALL|DECODE_DBG_TRANSFORM) ,
- "Old width: %d, new width: %d",
- p_spu->i_width, i_new_width);
-
- for ( i_row=0; i_row <= p_spu->i_height - 1; i_row++ ) {
-
- if (used != 0) {
- /* Discard the remaining piece of the column of the previous line*/
- 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 - used;
- unsigned int w2= i_scale_y - w1;
-
- used = w2;
- for (i = 0; i < PIXEL_SIZE; i++ ) {
- *p_dst = ( (*p_src1 * w1) + (*p_src2 * w2) ) / i_scale_y;
- p_src1++; p_src2++; p_dst++;
- }
-
- if (i_scale_x == used) {
- /* End of last pixel was end of p_src2. */
- p_src1 = p_src2;
- p_src2 += PIXEL_SIZE;
- i_col++;
- 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 - 1; i_row++ ) {
- for ( i_col=0; i_col < p_spu->i_width - 1; i_col++ ) {
- printf("%1x", p_source->s.t);
- p_source++;
- }
- printf("\n");
- }
- }
-
-}
-
/*****************************************************************************
* ParseImage: parse the image part of the subtitle
*****************************************************************************
or undo the effects of video output scaling.
*/
/* FIXME do the right scaling depending on vout. It may not be 4:3 */
- ScaleX( p_dec, p_spu, 3, 4 );
-
- 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 );
+ VCDSubScaleX( p_dec, p_spu, 3, 4 );
return VLC_SUCCESS;
}
* render.c : Philips OGT (SVCD Subtitle) renderer
*****************************************************************************
* Copyright (C) 2003 VideoLAN
- * $Id: render.c,v 1.3 2003/12/28 02:01:11 rocky Exp $
+ * $Id: render.c,v 1.4 2003/12/28 04:51:52 rocky Exp $
*
* Author: Rocky Bernstein
* based on code from:
routine can be as fast as possible.
*****************************************************************************/
-void VCDRenderSPU( vout_thread_t *p_vout, picture_t *p_pic,
+void VCDSubRender( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu )
{
struct subpicture_sys_t *p_sys = p_spu->p_sys;
* render.h : Common SVCD and CVD rendering routine(s).
*****************************************************************************
* Copyright (C) 2003 VideoLAN
- * $Id: render.h,v 1.1 2003/12/28 02:01:11 rocky Exp $
+ * $Id: render.h,v 1.2 2003/12/28 04:51:52 rocky Exp $
*
* Author: Rocky Bernstein
*
/*****************************************************************************
* Prototypes
*****************************************************************************/
-void VCDRenderSPU ( vout_thread_t *, picture_t *, const subpicture_t * );
+void VCDSubRender ( vout_thread_t *, picture_t *, const subpicture_t * );