Commit ef4c3d63 authored by Damien Lucas's avatar Damien Lucas

oups, ... forgot the interesting file !

parent 1ddb23b6
/*****************************************************************************
* dvbsub.c : DVB subtitles decoder thread
*****************************************************************************
* Copyright (C) 2003 ANEVIA
* $Id: dvbsub.c,v 1.1 2003/11/06 16:37:19 nitrox Exp $
*
* Authors: Damien LUCAS <damien.lucas@anevia.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 <stdlib.h> /* malloc(), free() */
#include <string.h> /* memcpy(), memset() */
#include <vlc/vlc.h>
#include <vlc/vout.h>
#include <vlc/decoder.h>
#include "codecs.h"
// Wow, that's ugly but very usefull for a memory leak track
// so I just keep it
#if 0
static long long unsigned int trox_malloc_nb = 0;
static long long unsigned int trox_free_nb = 0;
static void* trox_malloc (size_t size)
{ ++trox_malloc_nb; return malloc (size); }
static void trox_free (void* ptr)
{ ++trox_free_nb; free(ptr); return; }
static void trox_call ()
{
fprintf(stderr, "dvbbsub -- Memory usage: %llu mallocs %llu frees (%llu)\n",
trox_malloc_nb,
trox_free_nb,
trox_malloc_nb - trox_free_nb);
return;
}
#else
# define trox_malloc malloc
# define trox_free free
# define trox_call()
#endif
/****************************************************************************
* Local structures
****************************************************************************
* Those structures refer closely to the ETSI 300 743 Object model
****************************************************************************/
/* Storage of a RLE entry */
typedef struct dvbsub_rle_s
{
uint16_t i_num;
uint8_t i_color_code;
uint8_t y;
uint8_t cr;
uint8_t cb;
uint8_t t;
struct dvbsub_rle_s* p_next;
} dvbsub_rle_t;
/* A subpicture image is a list of codes
* We need to store the length of each line since nothing specify in
* the standard that all lines shoudl have the same length
* WARNING: We assume here that a spu is less than 576 lines high */
typedef struct
{
uint16_t i_rows;
uint16_t i_cols[576];
dvbsub_rle_t* p_last;
dvbsub_rle_t* p_codes;
} dvbsub_image_t;
/* The object definition gives the position of the object in a region */
typedef struct dvbsub_objectdef_s
{
uint16_t i_id;
uint8_t i_type;
uint8_t i_provider;
uint16_t i_xoffset;
uint16_t i_yoffset;
uint8_t i_fg_pc;
uint8_t i_bg_pc;
struct dvbsub_objectdef_s* p_next;
} dvbsub_objectdef_t;
/* The Region is an aera on the image
* with a list of the object definitions associated
* and a CLUT */
typedef struct dvbsub_region_s
{
uint8_t i_id;
uint8_t i_version_number;
vlc_bool_t b_fill;
uint16_t i_x;
uint16_t i_y;
uint16_t i_width;
uint16_t i_height;
uint8_t i_level_comp;
uint8_t i_depth;
uint8_t i_clut;
uint8_t i_8bp_code;
uint8_t i_4bp_code;
uint8_t i_2bp_code;
dvbsub_objectdef_t* p_object;
} dvbsub_region_t;
/* The page defines the list of regions */
typedef struct
{
uint16_t i_id;
uint8_t i_timeout;
uint8_t i_state;
uint8_t i_version_number;
uint8_t i_regions_number;
dvbsub_region_t* regions;
} dvbsub_page_t;
/* An object is constituted of 2 images (for interleaving) */
typedef struct dvbsub_object_s
{
uint16_t i_id;
uint8_t i_version_number;
uint8_t i_coding_method;
vlc_bool_t b_non_modify_color;
dvbsub_image_t* topfield;
dvbsub_image_t* bottomfield;
struct dvbsub_object_s* p_next;
} dvbsub_object_t;
/* The entry in the palette CLUT */
typedef struct
{
uint8_t Y;
uint8_t Cr;
uint8_t Cb;
uint8_t T;
} dvbsub_color_t;
/* */
typedef struct
{
uint8_t i_id;
uint8_t i_version_number;
dvbsub_color_t c_2b[0xff];
dvbsub_color_t c_4b[0xff];
dvbsub_color_t c_8b[0xff];
} dvbsub_clut_t;
typedef struct
{
uint8_t i_x;
uint16_t i_y;
dvbsub_image_t* p_rle_top;
dvbsub_image_t* p_rle_bot;
} dvbsub_render_t;
typedef struct
{
dvbsub_clut_t* p_clut[0xff];
dvbsub_page_t* p_page;
dvbsub_object_t* p_objects;
subpicture_t* p_spu[16];
} dvbsub_all_t;
typedef struct
{
/* Thread properties and locks */
vlc_thread_t thread_id; /* Id for thread functions */
/* Input properties */
decoder_fifo_t* p_fifo; /* Stores the PES stream data */
bit_stream_t bit_stream; /* PES data at the bit level */
/* Output properties */
vout_thread_t* p_vout; /* Needed to create the subpictures */
} dvbsub_thread_t;
struct subpicture_sys_t
{
mtime_t i_pts;
void * p_data; /* rle datas are stored */
vlc_object_t* p_input; /* Link to the input */
vlc_bool_t b_obsolete;
};
// List of different SEGMENT TYPES
// According to EN 300-743, table 2
#define DVBSUB_ST_PAGE_COMPOSITION 0x10
#define DVBSUB_ST_REGION_COMPOSITION 0x11
#define DVBSUB_ST_CLUT_DEFINITION 0x12
#define DVBSUB_ST_OBJECT_DATA 0x13
#define DVBSUB_ST_ENDOFDISPLAY 0x80
#define DVBSUB_ST_STUFFING 0xff
// List of different OBJECT TYPES
// According to EN 300-743, table 6
#define DVBSUB_OT_BASIC_BITMAP 0x00
#define DVBSUB_OT_BASIC_CHAR 0x01
#define DVBSUB_OT_COMPOSITE_STRING 0x02
// Pixel DATA TYPES
// According to EN 300-743, table 9
#define DVBSUB_DT_2BP_CODE_STRING 0x10
#define DVBSUB_DT_4BP_CODE_STRING 0x11
#define DVBSUB_DT_8BP_CODE_STRING 0x12
#define DVBSUB_DT_24_TABLE_DATA 0x20
#define DVBSUB_DT_28_TABLE_DATA 0x21
#define DVBSUB_DT_48_TABLE_DATA 0x22
#define DVBSUB_DT_END_LINE 0xf0
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static int OpenDecoder ( vlc_object_t * );
static int RunDecoder ( decoder_fifo_t * );
static int InitThread ( dvbsub_thread_t * );
static void EndThread ( dvbsub_thread_t *i, dvbsub_all_t*);
static vout_thread_t *FindVout( dvbsub_thread_t * );
static void RenderI42x( vout_thread_t *, picture_t *, const subpicture_t *,
vlc_bool_t );
static void RenderYUY2( vout_thread_t *, picture_t *, const subpicture_t *,
vlc_bool_t );
static void dvbsub_clut_add_entry ( dvbsub_clut_t* clut, uint8_t type,
uint8_t id, uint8_t y, uint8_t cr,
uint8_t cb, uint8_t t);
static void dvbsub_add_objectdef_to_region ( dvbsub_objectdef_t* p_obj,
dvbsub_region_t* p_region );
static dvbsub_image_t* dvbsub_parse_pdata ( dvbsub_thread_t* ,uint16_t );
static uint16_t dvbsub_count0x11(dvbsub_thread_t* p_spudec,
uint16_t* p,
dvbsub_image_t* p_image);
static void dvbsub_decode_segment ( dvbsub_thread_t *, dvbsub_all_t* );
static void dvbsub_decode_page_composition ( dvbsub_thread_t *, dvbsub_all_t* );
static void dvbsub_decode_region_composition ( dvbsub_thread_t*, dvbsub_all_t*);
static void dvbsub_decode_object ( dvbsub_thread_t*, dvbsub_all_t* );
static vlc_bool_t dvbsub_check_page ( dvbsub_all_t* );
static void dvbsub_render ( dvbsub_thread_t *p_spudec,dvbsub_all_t* );
static int dvbsub_parse ( dvbsub_thread_t *p_spudec, dvbsub_all_t* dvbsub );
static void dvbsub_decode_clut ( dvbsub_thread_t*, dvbsub_all_t*);
static void dvbsub_stop_display ( dvbsub_thread_t* p_dec, dvbsub_all_t* dvbsub);
static void free_image (dvbsub_image_t* p_i);
static void free_object (dvbsub_object_t* p_o);
static void free_regions (dvbsub_region_t* p_r, uint8_t nb);
static void free_objects (dvbsub_object_t* p_o);
static void free_clut ( dvbsub_clut_t* p_c);
static void free_page (dvbsub_page_t* p_p);
static void free_all ( dvbsub_all_t* p_a );
/*****************************************************************************
* Module descriptor.
*****************************************************************************/
vlc_module_begin();
add_category_hint( N_("subtitles"), NULL, VLC_TRUE );
set_description( _("subtitles decoder") );
set_capability( "decoder", 50 );
set_callbacks( OpenDecoder, NULL );
vlc_module_end();
/*****************************************************************************
* OpenDecoder: probe the decoder and return score
*****************************************************************************
* Tries to launch a decoder and return score so that the interface is able
* to chose.
*****************************************************************************/
static int OpenDecoder( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*) p_this;
if( p_dec->p_fifo->i_fourcc != VLC_FOURCC('d','v','b','s') )
{
return VLC_EGENERIC;
}
p_dec->pf_run = RunDecoder;
return VLC_SUCCESS;
}
/*****************************************************************************
* RunDecoder: this function is called just after the thread is created
*****************************************************************************/
static int RunDecoder( decoder_fifo_t * p_fifo )
{
dvbsub_thread_t * p_dvbsubdec;
// vout_thread_t * p_vout_backup = NULL;
dvbsub_all_t dvbsub;
unsigned int k;
/* Allocate the memory needed to store the thread's structure */
p_dvbsubdec = (dvbsub_thread_t *)trox_malloc( sizeof(dvbsub_thread_t) );
if ( p_dvbsubdec == NULL )
{
msg_Err( p_fifo, "out of memory" );
DecoderError( p_fifo );
return( -1 );
}
/*
* Initialize the thread properties
*/
p_dvbsubdec->p_vout = NULL;
p_dvbsubdec->p_fifo = p_fifo;
/*
* Initialize thread and free configuration
*/
p_dvbsubdec->p_fifo->b_error = InitThread( p_dvbsubdec );
dvbsub.p_page=NULL;
dvbsub.p_objects=NULL;
for(k=0; k<0xff; k++) dvbsub.p_clut[k] = NULL;
for(k=0; k<16; k++) dvbsub.p_spu[k] = NULL;
/*
* Main loop - it is not executed if an error occured during
* initialization
*/
while( (!p_dvbsubdec->p_fifo->b_die) && (!p_dvbsubdec->p_fifo->b_error) )
{
dvbsub_parse( p_dvbsubdec, &dvbsub );
p_dvbsubdec->p_vout = FindVout( p_dvbsubdec );
if( p_dvbsubdec->p_vout )
{
// Check if the page is to be displayed
if(dvbsub_check_page(&dvbsub))
{
dvbsub_render(p_dvbsubdec, &dvbsub);
}
vlc_object_release( p_dvbsubdec->p_vout );
}
}
// Free all structures
//dvbsub.p_objects=NULL;
//for(k=0; k<16; k++)
// if(dvbsub.p_spu[k] != NULL)
// dvbsub.p_spu[k]->p_sys->b_obsolete = 1;
/*
* Error loop
*/
if( p_dvbsubdec->p_fifo->b_error )
{
DecoderError( p_dvbsubdec->p_fifo );
/* End of thread */
EndThread( p_dvbsubdec, &dvbsub );
return -1;
}
/* End of thread */
EndThread( p_dvbsubdec, &dvbsub );
free_all(&dvbsub);
return 0;
}
/* following functions are local */
/*****************************************************************************
* InitThread: initialize dvbsub decoder thread
*****************************************************************************
* This function is called from RunThread and performs the second step of the
* initialization. It returns 0 on success. Note that the thread's flag are not
* modified inside this function.
*****************************************************************************/
static int InitThread( dvbsub_thread_t *p_dvbsubdec )
{
int i_ret;
/* Call InitBitstream anyway so p_spudec->bit_stream is in a known
* state before calling CloseBitstream */
i_ret = InitBitstream( &p_dvbsubdec->bit_stream, p_dvbsubdec->p_fifo,
NULL, NULL );
/* Check for a video output */
p_dvbsubdec->p_vout = FindVout( p_dvbsubdec );
if( !p_dvbsubdec->p_vout )
{
return -1;
}
/* It was just a check */
vlc_object_release( p_dvbsubdec->p_vout );
p_dvbsubdec->p_vout = NULL;
return i_ret;
}
/*****************************************************************************
* FindVout: Find a vout or wait for one to be created.
*****************************************************************************/
static vout_thread_t *FindVout( dvbsub_thread_t *p_spudec )
{
vout_thread_t *p_vout = NULL;
/* Find an available video output */
do
{
if( p_spudec->p_fifo->b_die || p_spudec->p_fifo->b_error )
{
break;
}
p_vout = vlc_object_find( p_spudec->p_fifo, VLC_OBJECT_VOUT,
FIND_ANYWHERE );
if( p_vout )
{
break;
}
msleep( VOUT_OUTMEM_SLEEP );
}
while( 1 );
return p_vout;
}
/*****************************************************************************
* EndThread: thread destruction
*****************************************************************************
* This function is called when the thread ends after a sucessful
* initialization.
*****************************************************************************/
static void EndThread( dvbsub_thread_t *p_dvbsubdec, dvbsub_all_t* p_dvbsub )
{
if( p_dvbsubdec->p_vout != NULL
&& p_dvbsubdec->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_dvbsubdec->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_dvbsubdec->p_vout, p_subpic );
}
}
}
CloseBitstream( &p_dvbsubdec->bit_stream );
trox_free( p_dvbsubdec );
trox_call();
}
static int dvbsub_parse ( dvbsub_thread_t *p_spudec,
dvbsub_all_t* dvbsub )
{
unsigned int data_identifier;
unsigned int subtitle_stream_id;
unsigned int nextbits;
uint32_t end_data_marker;
/* Re-align the buffer on an 8-bit boundary */
RealignBits( &p_spudec->bit_stream );
data_identifier = GetBits( &p_spudec->bit_stream, 8 );
subtitle_stream_id = GetBits( &p_spudec->bit_stream, 8 );
nextbits = ShowBits( &p_spudec->bit_stream, 8 );
while(nextbits == 0x0f )
{
dvbsub_decode_segment( p_spudec, dvbsub );
nextbits = ShowBits( &p_spudec->bit_stream, 8 );
}
end_data_marker = GetBits( &p_spudec->bit_stream, 8 );
return 0;
}
static void dvbsub_decode_segment ( dvbsub_thread_t * p_spudec,
dvbsub_all_t * dvbsub )
{
unsigned int sync_byte;
unsigned int segment_type;
uint16_t page_id;
uint16_t segment_length;
int k;
sync_byte = GetBits( &p_spudec->bit_stream, 8 );
segment_type = GetBits( &p_spudec->bit_stream, 8 );
page_id = GetBits( &p_spudec->bit_stream, 16 );
segment_length = ShowBits( &p_spudec->bit_stream, 16 );
if( page_id != ((dvb_spuinfo_t*)p_spudec->p_fifo->p_spuinfo)->i_id )
{
//TODO should use GetChunk
for(k=0; k<segment_length+2; k++) GetBits( &p_spudec->bit_stream, 8 );
return;
}
switch( segment_type )
{
case DVBSUB_ST_CLUT_DEFINITION:
dvbsub_decode_clut ( p_spudec, dvbsub );
break;
case DVBSUB_ST_PAGE_COMPOSITION:
dvbsub_decode_page_composition ( p_spudec, dvbsub );
break;
case DVBSUB_ST_REGION_COMPOSITION:
dvbsub_decode_region_composition ( p_spudec, dvbsub );
break;
case DVBSUB_ST_OBJECT_DATA:
dvbsub_decode_object ( p_spudec, dvbsub );
break;
case DVBSUB_ST_ENDOFDISPLAY:
dvbsub_stop_display ( p_spudec, dvbsub);
break;
case DVBSUB_ST_STUFFING:
default:
fprintf(stderr, "*** DVBSUB - Unsupported segment type ! (%04x)\n",
segment_type );
GetBits( &p_spudec->bit_stream, 16 );
for(k=0; k<segment_length; k++)
GetBits( &p_spudec->bit_stream, 8 );
break;
}
return;
}
static void dvbsub_decode_page_composition (dvbsub_thread_t *p_spudec,
dvbsub_all_t *dvbsub)
{
unsigned int i_version_number;
unsigned int i_state;
unsigned int i_segment_length;
uint8_t i_timeout;
unsigned int k;
i_segment_length = GetBits( &p_spudec->bit_stream, 16 );
//A page is composed by one or more region:
i_timeout = GetBits( &p_spudec->bit_stream, 8 );
i_version_number = GetBits( &p_spudec->bit_stream, 4 );
i_state = GetBits( &p_spudec->bit_stream, 2 );
// TODO We assume it is a new page (i_state)
if (dvbsub->p_page) free_page(dvbsub->p_page);
GetBits( &p_spudec->bit_stream, 2 ); /* Reserved */
//Allocate a new page
dvbsub->p_page = trox_malloc (sizeof(dvbsub_page_t));
dvbsub->p_page->i_timeout = i_timeout;
// Number of regions:
dvbsub->p_page->i_regions_number = (i_segment_length-2) / 6;
/* Special workaround for CAVENA encoders
* a page with no regions is sent instead of a 0x80 packet (End Of Display) */
if( dvbsub->p_page->i_regions_number == 0 )
{
dvbsub_stop_display(p_spudec, dvbsub );
}
/* /Special workaround */
dvbsub->p_page->regions =
trox_malloc(dvbsub->p_page->i_regions_number*sizeof(dvbsub_region_t));
for(k=0; k<dvbsub->p_page->i_regions_number ; k++)
{
dvbsub->p_page->regions[k].i_id = GetBits( &p_spudec->bit_stream, 8 );
GetBits( &p_spudec->bit_stream, 8 ); /* Reserved */
dvbsub->p_page->regions[k].i_x = GetBits( &p_spudec->bit_stream, 16 );
dvbsub->p_page->regions[k].i_y = GetBits( &p_spudec->bit_stream, 16 );
dvbsub->p_page->regions[k].p_object = NULL;
}
}
static void dvbsub_decode_region_composition (dvbsub_thread_t *p_spudec,
dvbsub_all_t *dvbsub)
{
unsigned int i_segment_length;
unsigned int i_processed_length;
unsigned int i_region_id;
dvbsub_region_t* p_region;
unsigned int k;
p_region = NULL;
i_segment_length = GetBits( &p_spudec->bit_stream, 16 );
// Get region id:
i_region_id = GetBits( &p_spudec->bit_stream, 8 );
for(k=0; k<dvbsub->p_page->i_regions_number; k++)
{
if ( dvbsub->p_page->regions[k].i_id == i_region_id )
p_region = &(dvbsub->p_page->regions[k]);
}
if(p_region == NULL)
{
// TODO
// The region has never been declared before
// Internal error
fprintf (stderr, "Decoding of undeclared region N/A...\n");
return;
}
// Skip version number and fill flag
if (ShowBits( &p_spudec->bit_stream, 4 ) == p_region->i_version_number)
{
fprintf(stderr, "Skipping already known region N/A ...\n");
// TODO Skip the right number of bits
}
// Region attributes
p_region->i_version_number = GetBits( &p_spudec->bit_stream, 4 );
p_region->b_fill = GetBits( &p_spudec->bit_stream, 1 );
GetBits( &p_spudec->bit_stream, 3 ); /* Reserved */
p_region->i_width = GetBits( &p_spudec->bit_stream, 16 );
p_region->i_height = GetBits( &p_spudec->bit_stream, 16 );
p_region->i_level_comp = GetBits( &p_spudec->bit_stream, 3 );
p_region->i_depth = GetBits( &p_spudec->bit_stream, 3 );
GetBits( &p_spudec->bit_stream, 2 ); /* Reserved */
p_region->i_clut = GetBits( &p_spudec->bit_stream, 8 );
p_region->i_8bp_code = GetBits( &p_spudec->bit_stream, 8 );
p_region->i_4bp_code = GetBits( &p_spudec->bit_stream, 4 );
p_region->i_2bp_code = GetBits( &p_spudec->bit_stream, 2 );
GetBits( &p_spudec->bit_stream, 2 ); /* Reserved */
// List of objects in the region:
// We already skipped 10 bytes
i_processed_length = 10;
while ( i_processed_length < i_segment_length )
{
// We create a new object
dvbsub_objectdef_t* p_obj;
p_obj = trox_malloc(sizeof(dvbsub_objectdef_t));
// We parse object properties
p_obj->i_id = GetBits( &p_spudec->bit_stream, 16 );
p_obj->i_type = GetBits( &p_spudec->bit_stream, 2 );
p_obj->i_provider = GetBits( &p_spudec->bit_stream, 2 );
p_obj->i_xoffset = GetBits( &p_spudec->bit_stream, 12 );
GetBits( &p_spudec->bit_stream, 4 ); /* Reserved */
p_obj->i_yoffset = GetBits( &p_spudec->bit_stream, 12 );
i_processed_length += 6;
if ( p_obj->i_type == DVBSUB_OT_BASIC_CHAR
|| p_obj->i_type == DVBSUB_OT_COMPOSITE_STRING )
{
p_obj->i_fg_pc = GetBits( &p_spudec->bit_stream, 8 );
p_obj->i_bg_pc = GetBits( &p_spudec->bit_stream, 8 );
i_processed_length += 2;
}
p_obj->p_next = NULL;
dvbsub_add_objectdef_to_region(p_obj, p_region);
}
}
static void dvbsub_decode_object (dvbsub_thread_t* p_spudec,
dvbsub_all_t* dvbsub)
{
dvbsub_object_t* p_obj;
dvbsub_object_t* p_o;
uint16_t i_segment_length;
uint16_t i_topfield_length;
uint16_t i_bottomfield_length;
// Memory Allocation
p_obj = trox_malloc ( sizeof ( dvbsub_object_t ) );
p_obj->p_next=NULL;
i_segment_length = GetBits( &p_spudec->bit_stream, 16 );
p_obj->i_id = GetBits( &p_spudec->bit_stream, 16 );
p_obj->i_version_number = GetBits( &p_spudec->bit_stream, 4 );
// TODO Check we don't already have this object / this version
p_obj->i_coding_method = GetBits( &p_spudec->bit_stream, 2 );
p_obj->b_non_modify_color = GetBits( &p_spudec->bit_stream, 1 );
GetBits( &p_spudec->bit_stream, 1 ); /* Reserved */
if(p_obj->i_coding_method == 0x00)
{
i_topfield_length = GetBits( &p_spudec->bit_stream, 16 );
i_bottomfield_length = GetBits( &p_spudec->bit_stream, 16 );
p_obj->topfield = dvbsub_parse_pdata (p_spudec, i_topfield_length);
p_obj->bottomfield =
dvbsub_parse_pdata (p_spudec, i_bottomfield_length);
}
else
{
GetBits(&p_spudec->bit_stream, (i_segment_length -3) *8);
//TODO
// DVB subtitling as characters
}
// Add this object to the list of the page
p_o = dvbsub->p_objects;
dvbsub->p_objects = p_obj;
p_obj->p_next = p_o;
return;
}
static void dvbsub_stop_display ( dvbsub_thread_t* p_dec,
dvbsub_all_t* dvbsub)
{
unsigned int j;
for(j = 0; dvbsub->p_spu[j] != NULL; j++)
dvbsub->p_spu[j]->i_stop = p_dec->bit_stream.p_pes->i_pts;
return;
}
static void dvbsub_decode_clut ( dvbsub_thread_t* p_dec,
dvbsub_all_t* dvbsub)
{
uint16_t i_segment_length;
uint16_t i_processed_length;
uint8_t i_entry_id;
uint8_t i_entry_type;
dvbsub_clut_t* clut;
uint8_t i_clut_id;
uint8_t i_version_number;
uint8_t y;
uint8_t cr;
uint8_t cb;
uint8_t t;
i_segment_length = GetBits( &p_dec->bit_stream, 16 );
i_clut_id = GetBits( &p_dec->bit_stream, 8 );
i_version_number = GetBits( &p_dec->bit_stream, 4 );
// Check that this id doesn't not already exist
// with the same version number
// And allocate memory if necessary
if( dvbsub->p_clut[i_clut_id] != NULL)
{
if ( dvbsub->p_clut[i_clut_id]->i_version_number == i_version_number )
{
//TODO skip the right number of bits
return;
}
else
{
memset(dvbsub->p_clut[i_clut_id], 0, sizeof(dvbsub_clut_t));
}
}
else
{
dvbsub->p_clut[i_clut_id] = trox_malloc(sizeof(dvbsub_clut_t));
}
clut = dvbsub->p_clut[i_clut_id];
/* We don't have this version of the CLUT:
* Parse it */
clut->i_version_number = i_version_number;
GetBits( &p_dec->bit_stream, 4 ); /* Reserved bits */
i_processed_length=2;
while(i_processed_length<i_segment_length)
{
i_entry_id = GetBits( &p_dec->bit_stream, 8 );
i_entry_type = GetBits( &p_dec->bit_stream, 3 );
GetBits( &p_dec->bit_stream, 4 );
if ( GetBits( &p_dec->bit_stream, 1 )==0x01 )
{
y = GetBits( &p_dec->bit_stream, 8 );
cr = GetBits( &p_dec->bit_stream, 8 );
cb = GetBits( &p_dec->bit_stream, 8 );
t = GetBits( &p_dec->bit_stream, 8 );
i_processed_length += 6;
}
else
{
y = GetBits( &p_dec->bit_stream, 6 );
cr = GetBits( &p_dec->bit_stream, 4 );
cb = GetBits( &p_dec->bit_stream, 4 );
t = GetBits( &p_dec->bit_stream, 2 );
i_processed_length += 4;
}
dvbsub_clut_add_entry(clut, i_entry_type, i_entry_id, y, cr, cb, t);
}
}
static void dvbsub_clut_add_entry ( dvbsub_clut_t* clut, uint8_t type,
uint8_t id, uint8_t y, uint8_t cr,
uint8_t cb, uint8_t t)
{
/* According to EN 300-743 section 7.2.3 note 1, type should
* not have more than 1 bit set to one
But, some strams don't respect this note. */
if( type & 0x04)
{
clut->c_2b[id].Y = y;
clut->c_2b[id].Cr = cr;
clut->c_2b[id].Cb = cb;
clut->c_2b[id].T = t;
}
if( type & 0x02)
{
clut->c_4b[id].Y = y;
clut->c_4b[id].Cr = cr;
clut->c_4b[id].Cb = cb;
clut->c_4b[id].T = t;
}
if( type & 0x01)
{
clut->c_8b[id].Y = y;
clut->c_8b[id].Cr = cr;
clut->c_8b[id].Cb = cb;
clut->c_8b[id].T = t;
}
return;
}
static void dvbsub_add_objectdef_to_region ( dvbsub_objectdef_t* p_obj,
dvbsub_region_t* p_region )
{
dvbsub_objectdef_t* p_o = p_region->p_object;
// Seek to the last non null element
if(p_o!=NULL)
{
for(; p_o->p_next!=NULL; p_o=p_o->p_next);
p_o->p_next = p_obj;
p_o->p_next->p_next = NULL;
}
else
{
p_region->p_object = p_obj;
p_region->p_object->p_next = NULL;
}
return;
}
static dvbsub_image_t* dvbsub_parse_pdata ( dvbsub_thread_t* p_spudec,
uint16_t length )
{
dvbsub_image_t* p_image;
uint16_t i_processed_length=0;
uint16_t i_lines=0;
uint16_t i_cols_last=0;
p_image = trox_malloc ( sizeof ( dvbsub_image_t) );
p_image->p_last=NULL;
memset(p_image->i_cols, 0, 576*sizeof(uint16_t));
/* Let's parse it a first time to determine the size of the buffer */
while (i_processed_length < length)
{
switch(GetBits( &p_spudec->bit_stream, 8 ))
{
case 0x10:
fprintf(stderr, "0x10 N/A\n");
break;
case 0x11:
i_processed_length += 1 + dvbsub_count0x11(p_spudec,
&(p_image->i_cols[i_lines]),
p_image);
break;
case 0x12:
fprintf(stderr, "0x12 N/A\n");
break;
case 0x20:
fprintf(stderr, "0x20 N/A\n");
break;
case 0x21:
fprintf(stderr, "0x21 N/A\n");
break;
case 0x22:
fprintf(stderr, "0x22 N/A\n");
break;
case 0xf0:
i_processed_length++;
i_lines++;
break;
}
}
p_image->i_rows = i_lines;
p_image->i_cols[i_lines] = i_cols_last;
// Check word-aligned bits
if(ShowBits( &p_spudec->bit_stream, 8 )==0x00)
GetBits( &p_spudec->bit_stream, 8 );
return p_image;
}
static void add_rle_code (dvbsub_image_t* p, uint16_t num, uint8_t color)
{
if(p->p_last != NULL)
{
p->p_last->p_next = trox_malloc (sizeof (dvbsub_rle_t));
p->p_last = p->p_last->p_next;
}
else
{
p->p_codes = trox_malloc (sizeof (dvbsub_rle_t));
p->p_last = p->p_codes;
}
p->p_last->i_num = num;
p->p_last->i_color_code = color;
p->p_last->p_next = NULL;
return;
}
static uint16_t dvbsub_count0x11(dvbsub_thread_t* p_spudec, uint16_t* p, dvbsub_image_t* p_image)
{
uint16_t i_processed=0;
vlc_bool_t b_stop=0;
uint16_t i_count = 0;
uint8_t i_color =0;
while (!b_stop)
{
if ( (i_color = GetBits( &p_spudec->bit_stream, 4 )) != 0x00 )
{
(*p)++;
i_processed+=4;
// 1 pixel of color code '0000'
add_rle_code (p_image, 1, i_color );
}
else
{
if(GetBits( &p_spudec->bit_stream, 1 ) == 0x00) // Switch1
{
if( ShowBits( &p_spudec->bit_stream, 3 ) != 0x00 )
{
i_count = 2 + GetBits( &p_spudec->bit_stream, 3 );
(*p) += i_count ;
add_rle_code (p_image, i_count, 0x00);
}
else
{
GetBits( &p_spudec->bit_stream, 3);
b_stop=1;
}
i_processed += 8;
}
else
{
if(GetBits( &p_spudec->bit_stream, 1 ) == 0x00) //Switch2
{
i_count = 4 + GetBits( &p_spudec->bit_stream, 2 );
i_color = GetBits( &p_spudec->bit_stream, 4 );
(*p) += i_count;
i_processed += 12;
add_rle_code(p_image, i_count, i_color);
}
else
{
switch ( GetBits( &p_spudec->bit_stream, 2 ) ) //Switch3
{
case 0x0:
(*p)++;
i_processed += 8;
add_rle_code(p_image, 1, 0x00);
break;
case 0x1:
(*p)+=2;
i_processed += 8;
add_rle_code(p_image, 2, 0x00);
break;
case 0x2:
i_count = 9 + GetBits( &p_spudec->bit_stream, 4 );
i_color = GetBits( &p_spudec->bit_stream, 4 );
(*p)+= i_count;
i_processed += 16;
add_rle_code ( p_image, i_count, i_color );
break;
case 0x3:
i_count= 25 + GetBits( &p_spudec->bit_stream, 8 );
i_color = GetBits( &p_spudec->bit_stream, 4 );
(*p)+= i_count;
i_processed += 20;
add_rle_code ( p_image, i_count, i_color );
break;
}
}
}
}
}
RealignBits ( &p_spudec->bit_stream );
return (i_processed+7)/8 ;
}
static vlc_bool_t dvbsub_check_page(dvbsub_all_t* dvbsub)
{
if(dvbsub->p_page != NULL)
{
if(dvbsub->p_objects != NULL)
return VLC_TRUE;
}
return VLC_FALSE;
}
static void free_image (dvbsub_image_t* p_i)
{
dvbsub_rle_t* p1;
dvbsub_rle_t* p2=NULL;
for( p1 = p_i->p_codes; p1 != NULL; p1=p2)
{
p2=p1->p_next;
trox_free(p1);
p1=NULL;
}
trox_free(p_i);
}
static void free_object (dvbsub_object_t* p_o)
{
trox_free(p_o);
}
static void free_objectdefs ( dvbsub_objectdef_t* p_o)
{
dvbsub_objectdef_t* p1;
dvbsub_objectdef_t* p2=NULL;
for( p1 = p_o; p1 != NULL; p1=p2)
{
p2=p1->p_next;
trox_free(p1);
p1=NULL;
}
}
static void free_regions (dvbsub_region_t* p_r, uint8_t nb)
{
unsigned int i;
for (i = 0; i<nb; i++) free_objectdefs ( p_r[i].p_object );
trox_free (p_r);
p_r = NULL;
}
static void free_objects (dvbsub_object_t* p_o)
{
dvbsub_object_t* p1;
dvbsub_object_t* p2=NULL;
for( p1 = p_o; p1 != NULL; p1=p2)
{
p2=p1->p_next;
free_image (p1->topfield);
free_image (p1->bottomfield);
free_object(p1);
}
}
static void free_clut ( dvbsub_clut_t* p_c) { trox_free(p_c); }
static void free_page (dvbsub_page_t* p_p)
{
free_regions (p_p->regions, p_p->i_regions_number);
trox_free(p_p);
p_p = NULL;
}
static void free_spu( subpicture_t *p_spu )
{
if ( p_spu->p_sys )
{
free_image(((dvbsub_render_t *)p_spu->p_sys->p_data)->p_rle_top);
free_image(((dvbsub_render_t *)p_spu->p_sys->p_data)->p_rle_bot);
trox_free(p_spu->p_sys->p_data);
trox_free( p_spu->p_sys );
p_spu->p_sys = NULL;
}
}
static void free_all ( dvbsub_all_t* p_a )
{
unsigned int i;
for(i=0; i<0xff; i++) if (p_a->p_clut[i]) free_clut ( p_a->p_clut[i] );
for(i=0; i<16; i++) if (p_a->p_spu[i]) free_spu ( p_a->p_spu[i] );
if(p_a->p_page) free_page( p_a->p_page );
free_objects (p_a->p_objects);
}
static void dvbsub_RenderDVBSUB ( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu, vlc_bool_t b_crop )
{
// If we have changed the language on the fly,
if(!p_spu->p_sys) return;
if(p_spu->p_sys->b_obsolete) return;
switch (p_vout->output.i_chroma)
{
/* I420 target, no scaling */
case VLC_FOURCC('I','4','2','2'):
case VLC_FOURCC('I','4','2','0'):
case VLC_FOURCC('I','Y','U','V'):
case VLC_FOURCC('Y','V','1','2'):
// As long as we just use Y info, I422 and YV12 are just equivalent
// to I420. Remember to change it the day we'll take into account
// U and V info.
RenderI42x( p_vout, p_pic, p_spu, VLC_FALSE );
break;
/* RV16 target, scaling */
case VLC_FOURCC('R','V','1','6'):
fprintf(stderr, "Not implemented chroma ! RV16)\n");
//RenderRV16( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
break;
/* RV32 target, scaling */
case VLC_FOURCC('R','V','2','4'):
case VLC_FOURCC('R','V','3','2'):
fprintf(stderr, "Not implemented chroma ! RV32 \n");
//RenderRV32( p_vout, p_pic, p_spu, p_spu->p_sys->b_crop );
break;
/* NVidia overlay, no scaling */
case VLC_FOURCC('Y','U','Y','2'):
RenderYUY2( p_vout, p_pic, p_spu, VLC_FALSE );
break;
default:
msg_Err( p_vout, "unknown chroma, can't render SPU" );
break;
}
}
static void RenderYUY2 ( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu, vlc_bool_t b_crop )
{
/* Common variables */
uint8_t *p_desty;
uint16_t i,j;
uint16_t i_cnt;
uint16_t x, y;
dvbsub_rle_t* p_c;
dvbsub_render_t* p_r = ((dvbsub_render_t *)p_spu->p_sys->p_data);
dvbsub_image_t* p_im = p_r->p_rle_top;
i=0;
j=0;
p_desty = p_pic->Y_PIXELS;
//let's render the 1st frame
for(p_c = p_im->p_codes; p_c->p_next != NULL; p_c=p_c->p_next)
{
// if( p_c->y != 0 && p_c->t < 0x20)
if( p_c->y != 0 && p_c->t < 0x20)
{
x = j+ p_r->i_x;
y = 2*i+p_r->i_y;
//memset(p_desty+ y*p_pic->Y_PITCH + x, p_c->y, p_c->i_num);
// In YUY2 we have to set pixel per pixel
for( i_cnt = 0; i_cnt < p_c->i_num; i_cnt+=2 )
{
memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt, p_c->y, 1);
// memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+1, p_c->cr, 1);
// memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+2, p_c->y, 1);
// memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+3, p_c->cb, 1);
}
}
j += p_c->i_num;
if(j >= p_im->i_cols[i])
{
i++; j=0;
}
if( i>= p_im->i_rows) break;
}
//idem for the second frame
p_im = p_r->p_rle_bot; i=0; j=0;
for(p_c = p_im->p_codes; p_c->p_next != NULL; p_c=p_c->p_next)
{
if( p_c->y != 0 && p_c->t < 0x20)
{
x = j+ p_r->i_x;
y = 2*i+1+p_r->i_y;
//memset(p_desty+ y*p_pic->Y_PITCH + x, p_c->y, p_c->i_num);
// In YUY2 we have to set pixel per pixel
for( i_cnt = 0; i_cnt < p_c->i_num; i_cnt+=2 )
{
memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt, p_c->y, 1);
// memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+1, p_c->cr, 1);
// memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+2, p_c->y, 1);
// memset(p_desty+ y*p_pic->Y_PITCH + 2*x + i_cnt+3, p_c->cb, 1);
}
}
j += p_c->i_num;
if(j >= p_im->i_cols[i])
{
i++; j=0;
}
if( i>= p_im->i_rows) break;
}
}
static void RenderI42x ( vout_thread_t *p_vout, picture_t *p_pic,
const subpicture_t *p_spu, vlc_bool_t b_crop )
{
/* Common variables */
uint8_t *p_desty;
uint8_t *p_destu;
uint8_t *p_destv;
uint16_t i,j;
uint16_t x, y;
dvbsub_rle_t* p_c;
dvbsub_render_t* p_r = ((dvbsub_render_t *)p_spu->p_sys->p_data);
dvbsub_image_t* p_im = p_r->p_rle_top;
i=0;
j=0;
p_desty = p_pic->Y_PIXELS;
p_destu = p_pic->U_PIXELS;
p_destv = p_pic->V_PIXELS;
//let's render the 1st frame
for(p_c = p_im->p_codes; p_c->p_next != NULL; p_c=p_c->p_next)
{
if( p_c->y != 0 )
{
x = j+ p_r->i_x;
y = 2*i+p_r->i_y;
//memset(p_dest+ y*p_pic->U_PITCH*2 + x, p_c->cr, p_c->i_num);
// memset(p_desty+ (y)*p_pic->Y_PITCH + x, p_c->cr, p_c->i_num);
//memset(p_dest+ y*p_pic->V_PITCH*2 + x, p_c->cb, p_c->i_num);
//memset(p_destu+ (y)*p_pic->Y_PITCH + x, p_c->cb, p_c->i_num);
memset(p_desty+ y*p_pic->Y_PITCH + x, p_c->y, p_c->i_num);
// memset(p_desty+ 2*y*p_pic->U_PITCH + x, p_c->cr, p_c->i_num);
// memset(p_desty+ 2*y*p_pic->V_PITCH + x, p_c->cb, p_c->i_num);
}
j += p_c->i_num;
if(j >= p_im->i_cols[i])
{
i++; j=0;
}
if( i>= p_im->i_rows) break;
}
//idem for the second frame
p_im = p_r->p_rle_bot; i=0; j=0;
for(p_c = p_im->p_codes; p_c->p_next != NULL; p_c=p_c->p_next)
{
if( p_c->y != 0 && p_c->t < 0x20)
{
x = j+ p_r->i_x;
y = 2*i+1+p_r->i_y;
// memset(p_desty+ y*p_pic->U_PITCH*2 + x, p_c->cr, p_c->i_num);
// memset(p_desty+ y*p_pic->V_PITCH*2 + x, p_c->cb, p_c->i_num);
memset(p_desty+ y*p_pic->Y_PITCH + x, p_c->y, p_c->i_num);
// memset(p_desty+ 2*y*p_pic->U_PITCH + x, p_c->cr, p_c->i_num);
// memset(p_desty+ 2*y*p_pic->V_PITCH + x, p_c->cb, p_c->i_num);
}
j += p_c->i_num;
if(j >= p_im->i_cols[i])
{
i++; j=0;
}
if( i>= p_im->i_rows) break;
}
}
static void dvbsub_Destroy( subpicture_t *p_spu )
{
free_spu( p_spu );
}
static void dvbsub_render( dvbsub_thread_t *p_dec, dvbsub_all_t* dvbsub)
{
dvbsub_region_t* p_region;
dvbsub_objectdef_t* p_objectdef;
dvbsub_object_t* p_o;
dvbsub_object_t* p_object;
dvbsub_object_t* p_object_old;
dvbsub_render_t* p_render;
dvbsub_rle_t* p_c;
uint8_t i,j;
j=0;
/* loop on regions */
for(i=0; i< dvbsub->p_page->i_regions_number; i++)
{
p_region = &(dvbsub->p_page->regions[i]);
/* loop on objects */
for(p_objectdef = p_region->p_object;
p_objectdef != NULL;
p_objectdef = p_objectdef->p_next)
{
/* Look for the right object */
p_object = dvbsub->p_objects;
while((p_object!=NULL) && (p_object->i_id != p_objectdef->i_id))
{
p_object = p_object->p_next;
}
if(p_object==NULL)
{
fprintf(stderr, "Internal DvbSub decoder error\n");
return;
}
/* Allocate the render structure */
p_render = trox_malloc(sizeof(dvbsub_render_t));
p_render->i_x = p_region->i_x + p_objectdef->i_xoffset;
p_render->i_y = p_region->i_y + p_objectdef->i_yoffset;
p_render->p_rle_top = p_object->topfield;
p_render->p_rle_bot = p_object->bottomfield;
/* Compute the color datas according to the appropriate CLUT */
for(p_c=p_render->p_rle_top->p_codes;p_c->p_next!=NULL; p_c=p_c->p_next)
{
//TODO We assume here we are working in 4bp
p_c->y=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].Y;
p_c->cr=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].Cr;
p_c->cb=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].Cb;
p_c->t=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].T;
}
for(p_c=p_render->p_rle_bot->p_codes;p_c->p_next!=NULL; p_c=p_c->p_next)
{
//TODO We assume here we are working in 4bp
p_c->y=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].Y;
p_c->cr=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].Cr;
p_c->cb=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].Cb;
p_c->t=dvbsub->p_clut[p_region->i_clut]->c_4b[p_c->i_color_code].T;
}
/* Allocate the subpicture internal data. */
dvbsub->p_spu[j] = vout_CreateSubPicture( p_dec->p_vout,
MEMORY_SUBPICTURE );
if( dvbsub->p_spu[j] == NULL )
{
fprintf(stderr, "Unable to allocate memory ... skipping\n");
return;
}
/* Set the pf_render callback */
dvbsub->p_spu[j]->pf_render = dvbsub_RenderDVBSUB;
dvbsub->p_spu[j]->p_sys = trox_malloc( sizeof( subpicture_sys_t ));
dvbsub->p_spu[j]->p_sys->p_data = p_render;
dvbsub->p_spu[j]->p_sys->b_obsolete=0;
dvbsub->p_spu[j]->pf_destroy = dvbsub_Destroy;
dvbsub->p_spu[j]->i_start = p_dec->bit_stream.p_pes->i_pts;
dvbsub->p_spu[j]->i_stop = dvbsub->p_spu[j]->i_start + dvbsub->p_page->i_timeout*1000000;
dvbsub->p_spu[j]->b_ephemer = VLC_FALSE;
// At this stage, we have all we need in p_render
// We need to free the object
//Remove this object from the list
p_object_old = p_object;
if(p_object == dvbsub->p_objects)
dvbsub->p_objects = p_object->p_next;
else
{
for(p_o = dvbsub->p_objects; p_o->p_next != p_object; p_o=p_o->p_next);
p_o->p_next = p_object->p_next;
}
free_object(p_object_old);
vout_DisplaySubPicture (p_dec->p_vout, dvbsub->p_spu[j] );
j++;
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment