Commit c523f5a5 authored by gbazin's avatar gbazin
Browse files

* modules/codec/dvbsub.c: rewrite of the DVB subtitles decoder.

   It can finally decode properly all the samples I have.
parent 859483c8
......@@ -7,6 +7,7 @@
*
* Authors: Damien LUCAS <damien.lucas@anevia.com>
* Laurent Aimar <fenrir@via.ecp.fr>
* Gildas Bazin <gbazin@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -31,6 +32,8 @@
#include "vlc_bits.h"
//#define DEBUG_DVBSUB 1
/*****************************************************************************
* Module descriptor.
*****************************************************************************/
......@@ -52,45 +55,67 @@ vlc_module_end();
/* Storage of a RLE entry */
typedef struct dvbsub_rle_s
{
uint16_t i_num;
uint8_t i_color_code;
uint16_t i_num;
int i_color_code;
int i_bpp;
uint8_t y;
uint8_t cr;
uint8_t cb;
uint8_t t;
struct dvbsub_rle_s* p_next;
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
* the standard that all lines should 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;
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;
uint16_t i_id;
uint8_t i_type;
uint8_t i_provider;
uint16_t i_x;
uint16_t i_y;
uint8_t i_fg_pc;
uint8_t i_bg_pc;
} dvbsub_objectdef_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 object definition gives the position of the object in a region */
typedef struct dvbsub_regiondef_s
{
uint16_t i_id;
uint16_t i_x;
uint16_t i_y;
} dvbsub_regiondef_t;
/* The Region is an aera on the image
* with a list of the object definitions associated
* and a CLUT */
* with a list of the object definitions associated and a CLUT */
typedef struct dvbsub_region_s
{
uint8_t i_id;
......@@ -106,7 +131,11 @@ typedef struct dvbsub_region_s
uint8_t i_8bp_code;
uint8_t i_4bp_code;
uint8_t i_2bp_code;
dvbsub_objectdef_t* p_object;
int i_object_defs;
dvbsub_objectdef_t *p_object_defs;
struct dvbsub_region_s *p_next;
} dvbsub_region_t;
......@@ -117,23 +146,11 @@ typedef struct
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;
uint8_t i_region_defs;
dvbsub_regiondef_t *p_region_defs;
} dvbsub_object_t;
} dvbsub_page_t;
/* The entry in the palette CLUT */
typedef struct
......@@ -150,50 +167,49 @@ 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_color_t c_2b[4];
dvbsub_color_t c_4b[16];
dvbsub_color_t c_8b[256];
} 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
typedef struct dvbsub_render_s
{
int i_id;
mtime_t i_pts;
uint16_t i_x;
uint16_t i_y;
dvbsub_image_t *p_rle_top;
dvbsub_image_t *p_rle_bot;
dvbsub_clut_t* p_clut[0xff];
dvbsub_page_t* p_page;
dvbsub_object_t* p_objects;
subpicture_t* p_spu[16];
int i_subpic_channel;
struct dvbsub_render_s *p_next;
} dvbsub_all_t;
} dvbsub_render_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;
dvbsub_render_t *p_objects; /* Linked list of objects to render */
};
struct decoder_sys_t
{
vout_thread_t *p_vout;
vout_thread_t *p_vout;
bs_t bs;
/* Decoder internal data */
int i_id;
int i_ancillary_id;
mtime_t i_pts;
bs_t bs;
dvbsub_page_t *p_page;
dvbsub_region_t *p_regions;
dvbsub_object_t *p_objects;
dvbsub_all_t dvbsub;
dvbsub_clut_t *p_clut[256];
dvbsub_clut_t default_clut;
subpicture_t *p_spu;
int i_subpic_channel;
};
......@@ -219,18 +235,27 @@ struct decoder_sys_t
#define DVBSUB_DT_28_TABLE_DATA 0x21
#define DVBSUB_DT_48_TABLE_DATA 0x22
#define DVBSUB_DT_END_LINE 0xf0
// List of different Page Composition Segment state
// According to EN 300-743, 7.2.1 table 3
#define DVBSUB_PCS_STATE_ACQUISITION 0x01
#define DVBSUB_PCS_STATE_CHANGE 0x10
/*****************************************************************************
* Local prototypes
*****************************************************************************/
static void Decode ( decoder_t *, block_t ** );
static void Decode( decoder_t *, block_t ** );
static void decode_segment( decoder_t *, bs_t * );
static void decode_page_composition( decoder_t *, bs_t * );
static void decode_region_composition( decoder_t *, bs_t * );
static void decode_object( decoder_t *, bs_t * );
static void decode_clut( decoder_t *, bs_t * );
static vout_thread_t *FindVout( decoder_t * );
static void free_objects( decoder_t * );
static void free_all( decoder_t * );
static int init( dvbsub_all_t *, int );
static void decode_segment( decoder_t *, dvbsub_all_t *, bs_t * );
static void render( dvbsub_all_t *, vout_thread_t * );
static void dvbsub( dvbsub_all_t * );
static void render( decoder_t *, vout_thread_t * );
static void default_clut_init( decoder_t * );
/*****************************************************************************
* Open: probe the decoder and return score
......@@ -240,8 +265,9 @@ static void dvbsub( dvbsub_all_t * );
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*) p_this;
decoder_t *p_dec = (decoder_t *) p_this;
decoder_sys_t *p_sys;
int i;
if( p_dec->fmt_in.i_codec != VLC_FOURCC('d','v','b','s') )
{
......@@ -251,12 +277,20 @@ static int Open( vlc_object_t *p_this )
p_dec->pf_decode_sub = Decode;
p_sys = p_dec->p_sys = malloc( sizeof(decoder_sys_t) );
p_sys->p_vout = NULL;
init( &p_sys->dvbsub, p_dec->fmt_in.subs.dvb.i_id );
p_sys->i_pts = 0;
p_sys->i_id = p_dec->fmt_in.subs.dvb.i_id & 0xFFFF;
p_sys->i_ancillary_id = p_dec->fmt_in.subs.dvb.i_id >> 16;
p_sys->p_page = NULL;
p_sys->p_regions = NULL;
p_sys->p_objects = NULL;
p_sys->p_vout = NULL;
p_sys->p_spu = NULL;
for( i = 0; i < 256; i++ ) p_sys->p_clut[i] = NULL;
es_format_Init( &p_dec->fmt_out, SPU_ES, VLC_FOURCC( 'd','v','b','s' ) );
default_clut_init( p_dec );
return VLC_SUCCESS;
}
......@@ -270,7 +304,7 @@ static void Close( vlc_object_t *p_this )
if( p_sys->p_vout && p_sys->p_vout->p_subpicture != NULL )
{
subpicture_t * p_subpic;
subpicture_t *p_subpic;
int i_subpic;
for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
{
......@@ -284,8 +318,7 @@ static void Close( vlc_object_t *p_this )
}
}
dvbsub( &p_sys->dvbsub );
free_all( p_dec );
free( p_sys );
}
......@@ -305,120 +338,137 @@ static void Decode( decoder_t *p_dec, block_t **pp_block )
p_block = *pp_block;
*pp_block = NULL;
p_sys->dvbsub.i_pts = p_block->i_pts;
if( p_sys->dvbsub.i_pts <= 0 )
p_sys->i_pts = p_block->i_pts;
if( p_sys->i_pts <= 0 )
{
msg_Warn( p_dec, "non dated subtitle" );
block_Release( p_block );
return;
}
p_last_vout = p_sys->p_vout;
if( ( p_sys->p_vout = FindVout( p_dec ) ) )
bs_init( &p_sys->bs, p_block->p_buffer, p_block->i_buffer );
if( bs_read( &p_sys->bs, 8 ) != 0x20 ) /* Data identifier */
{
int i_data_identifier;
int i_subtitle_stream_id;
int i_end_data_marker;
msg_Dbg( p_dec, "invalid data identifier" );
block_Release( p_block );
return;
}
bs_init( &p_sys->bs, p_block->p_buffer, p_block->i_buffer );
if( bs_read( &p_sys->bs, 8 ) != 0x20 && 0 ) /* Subtitle stream id */
{
msg_Dbg( p_dec, "invalid subtitle stream id" );
block_Release( p_block );
return;
}
i_data_identifier = bs_read( &p_sys->bs, 8 );
i_subtitle_stream_id = bs_read( &p_sys->bs, 8 );
while( bs_show( &p_sys->bs, 8 ) == 0x0f ) /* Sync byte */
{
decode_segment( p_dec, &p_sys->bs );
}
for( ;; )
{
if( bs_show( &p_sys->bs, 8 ) != 0x0f )
{
break;
}
decode_segment( p_dec, &p_sys->dvbsub, &p_sys->bs );
}
i_end_data_marker = bs_read( &p_sys->bs, 8 );
if( bs_read( &p_sys->bs, 8 ) != 0xff ) /* End marker */
{
msg_Warn( p_dec, "end marker not found (corrupted subtitle ?)" );
block_Release( p_block );
return;
}
p_last_vout = p_sys->p_vout;
if( ( p_sys->p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT,
FIND_ANYWHERE ) ) )
{
if( p_last_vout != p_sys->p_vout )
{
p_sys->dvbsub.i_subpic_channel =
p_sys->i_subpic_channel =
vout_RegisterOSDChannel( p_sys->p_vout );
}
/* Check if the page is to be displayed */
if( p_sys->dvbsub.p_page && p_sys->dvbsub.p_objects )
{
render( &p_sys->dvbsub, p_sys->p_vout );
}
if( p_sys->p_page ) render( p_dec, p_sys->p_vout );
vlc_object_release( p_sys->p_vout );
}
#ifdef DEBUG_DVBSUB
else if( p_sys->p_page ) render( p_dec, NULL );
#endif
block_Release( p_block );
}
/* following functions are local */
/*****************************************************************************
* FindVout: Find a vout or wait for one to be created.
* default_clut_init: default clut as defined in EN 300-743 section 10
*****************************************************************************/
static vout_thread_t *FindVout( decoder_t *p_dec )
static void default_clut_init( decoder_t *p_dec )
{
for( ;; )
{
vout_thread_t *p_vout;
decoder_sys_t *p_sys = p_dec->p_sys;
uint8_t i;
if( p_dec->b_die || p_dec->b_error )
{
return NULL;
}
p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
if( p_vout )
{
return p_vout;
}
msleep( VOUT_OUTMEM_SLEEP );
}
}
#define RGB_TO_Y(r, g, b) ((int16_t) 77 * r + 150 * g + 29 * b) / 256;
#define RGB_TO_U(r, g, b) ((int16_t) -44 * r - 87 * g + 131 * b) / 256;
#define RGB_TO_V(r, g, b) ((int16_t) 131 * r - 110 * g - 21 * b) / 256;
static int init( dvbsub_all_t *p_dvbsub, int i_id )
{
int i;
/* 4 entries CLUT */
for( i = 0; i < 4; i++ )
{
uint8_t R = 0, G = 0, B = 0, T = 0;
memset( p_dvbsub, 0, sizeof( dvbsub_all_t ) );
if( !(i & 0x2) && !(i & 0x1) ) T = 0xFF;
else if( !(i & 0x2) && (i & 0x1) ) R = G = B = 0xFF;
else if( (i & 0x2) && !(i & 0x1) ) R = G = B = 0;
else R = G = B = 0x7F;
p_dvbsub->i_pts = 0;
p_dvbsub->i_id = i_id;
p_dvbsub->p_page = NULL;
p_dvbsub->p_objects = NULL;
for( i = 0; i < 255; i++ )
{
p_dvbsub->p_clut[i] = NULL;
p_sys->default_clut.c_2b[i].Y = RGB_TO_Y(R,G,B);
p_sys->default_clut.c_2b[i].Cr = RGB_TO_U(R,G,B);
p_sys->default_clut.c_2b[i].Cb = RGB_TO_V(R,G,B);
p_sys->default_clut.c_2b[i].T = T;
}
/* 16 entries CLUT */
for( i = 0; i < 16; i++ )
{
p_dvbsub->p_spu[i] = NULL;
}
return 0;
}
static void free_all( dvbsub_all_t * );
uint8_t R = 0, G = 0, B = 0, T = 0;
static void dvbsub( dvbsub_all_t *p_dvbsub )
{
free_all( p_dvbsub );
}
if( !(i & 0x8) )
{
if( !(i & 0x4) && !(i & 0x2) && !(i & 0x1) )
{
T = 0xFF;
}
else
{
R = (i & 0x1) ? 0xFF : 0;
G = (i & 0x2) ? 0xFF : 0;
B = (i & 0x4) ? 0xFF : 0;
}
}
else
{
R = (i & 0x1) ? 0x7F : 0;
G = (i & 0x2) ? 0x7F : 0;
B = (i & 0x4) ? 0x7F : 0;
}
static void decode_clut( dvbsub_all_t *p_dvbsub, bs_t *s );
static void decode_page_composition( dvbsub_all_t *p_dvbsub, bs_t *s);
static void decode_region_composition( dvbsub_all_t *p_dvbsub, bs_t *s );
static void stop_display( dvbsub_all_t* p_dvbsub );
static void decode_object( dvbsub_all_t *p_dvbsub, bs_t *s );
p_sys->default_clut.c_4b[i].Y = RGB_TO_Y(R,G,B);
p_sys->default_clut.c_4b[i].Cr = RGB_TO_U(R,G,B);
p_sys->default_clut.c_4b[i].Cb = RGB_TO_V(R,G,B);
p_sys->default_clut.c_4b[i].T = T;
}
static void free_page( dvbsub_page_t* p_p );
/* 256 entries CLUT (TODO) */
memset( p_sys->default_clut.c_8b, 0xFF, 256 * sizeof(dvbsub_color_t) );
}
static void decode_segment( decoder_t *p_dec, dvbsub_all_t *p_dvbspu, bs_t *s )
static void decode_segment( decoder_t *p_dec, bs_t *s )
{
decoder_sys_t *p_sys = p_dec->p_sys;
int i_type;
int i_page_id;
int i_size;
/* sync_byte */
/* sync_byte (already checked) */
bs_skip( s, 8 );
/* segment type */
......@@ -430,45 +480,66 @@ static void decode_segment( decoder_t *p_dec, dvbsub_all_t *p_dvbspu, bs_t *s )
/* segment size */
i_size = bs_show( s, 16 );
if( i_page_id != p_dvbspu->i_id )
if( i_page_id != p_sys->i_id && i_page_id != p_sys->i_ancillary_id )
{
#ifdef DEBUG_DVBSUB
msg_Dbg( p_dec, "subtitle skipped (page id: %i)", i_page_id );
#endif
bs_skip( s, 8 * ( 2 + i_size ) );
return;
}
#ifdef DEBUG_DVBSUB
if( i_page_id == p_sys->i_id )
msg_Dbg( p_dec, "segment (id: %i)", i_page_id );
else
msg_Dbg( p_dec, "ancillary segment (id: %i)", i_page_id );
#endif
switch( i_type )
{
case DVBSUB_ST_CLUT_DEFINITION:
case DVBSUB_ST_PAGE_COMPOSITION:
#ifdef DEBUG_DVBSUB
msg_Dbg( p_dec, "subtitle dvbsub_decode_clut" );
msg_Dbg( p_dec, "decode_page_composition" );
#endif
decode_clut( p_dvbspu, s );
decode_page_composition( p_dec, s );
break;
case DVBSUB_ST_PAGE_COMPOSITION:
case DVBSUB_ST_REGION_COMPOSITION:
#ifdef DEBUG_DVBSUB
msg_Dbg( p_dec, "subtitle dvbsub_decode_page_composition" );
msg_Dbg( p_dec, "decode_region_composition" );
#endif
decode_page_composition( p_dvbspu, s );
decode_region_composition( p_dec, s );
break;
case DVBSUB_ST_REGION_COMPOSITION:
case DVBSUB_ST_CLUT_DEFINITION:
#ifdef DEBUG_DVBSUB
msg_Dbg( p_dec, "subtitle dvbsub_decode_region_composition" );
msg_Dbg( p_dec, "decode_clut" );
#endif
decode_region_composition( p_dvbspu, s );
decode_clut( p_dec, s );
break;
case DVBSUB_ST_OBJECT_DATA:
#ifdef DEBUG_DVBSUB
msg_Dbg( p_dec, "subtitle dvbsub_decode_object" );
msg_Dbg( p_dec, "decode_object" );
#endif
decode_object( p_dvbspu, s );
decode_object( p_dec, s );
break;
case DVBSUB_ST_ENDOFDISPLAY:
#ifdef DEBUG_DVBSUB
msg_Dbg( p_dec, "subtitle dvbsub_stop_display" );
msg_Dbg( p_dec, "end of display" );
#endif
stop_display( p_dvbspu );
bs_skip( s, 8 * ( 2 + i_size ) );
break;
case DVBSUB_ST_STUFFING:
#ifdef DEBUG_DVBSUB
msg_Dbg( p_dec, "skip stuffing" );
#endif
bs_skip( s, 8 * ( 2 + i_size ) );
break;
default:
msg_Warn( p_dec, "unsupported segment type: (%04x)", i_type );
bs_skip( s, 8 * ( 2 + i_size ) );
......@@ -476,51 +547,38 @@ static void decode_segment( decoder_t *p_dec, dvbsub_all_t *p_dvbspu, bs_t *s )
}
}
static void stop_display( dvbsub_all_t *p_dvbsub )
{
int i;
for( i = 0; p_dvbsub->p_spu[i] != NULL; i++ )
{
p_dvbsub->p_spu[i]->i_stop = p_dvbsub->i_pts;
}
}
static void decode_clut( dvbsub_all_t *p_dvbsub, bs_t *s )
static void decode_clut( decoder_t *p_dec, bs_t *s )
{
uint16_t i_segment_length;
uint16_t i_processed_length;
dvbsub_clut_t* clut;
uint8_t i_clut_id;
uint8_t i_version_number;
decoder_sys_t *p_sys = p_dec->p_sys;
uint16_t i_segment_length;
uint16_t i_processed_length;
dvbsub_clut_t *p_clut;
uint8_t i_clut_id;
uint8_t i_version_number;
i_segment_length = bs_read( s, 16 );
i_clut_id = bs_read( s, 8 );
i_version_number = bs_read( s, 4 );