/src/vlc/modules/codec/dvbsub.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * dvbsub.c : DVB subtitles decoder |
3 | | * DVB subtitles encoder (developed for Anevia, www.anevia.com) |
4 | | ***************************************************************************** |
5 | | * Copyright (C) 2003 ANEVIA |
6 | | * Copyright (C) 2003-2009 VLC authors and VideoLAN |
7 | | * |
8 | | * Authors: Gildas Bazin <gbazin@videolan.org> |
9 | | * Damien LUCAS <damien.lucas@anevia.com> |
10 | | * Laurent Aimar <fenrir@via.ecp.fr> |
11 | | * Jean-Paul Saman <jpsaman #_at_# m2x dot nl> |
12 | | * Derk-Jan Hartman <hartman #at# videolan dot org> |
13 | | * Simon Hailes <simon _a_ screen.subtitling.com> |
14 | | * |
15 | | * This program is free software; you can redistribute it and/or modify it |
16 | | * under the terms of the GNU Lesser General Public License as published by |
17 | | * the Free Software Foundation; either version 2.1 of the License, or |
18 | | * (at your option) any later version. |
19 | | * |
20 | | * This program is distributed in the hope that it will be useful, |
21 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
23 | | * GNU Lesser General Public License for more details. |
24 | | * |
25 | | * You should have received a copy of the GNU Lesser General Public License |
26 | | * along with this program; if not, write to the Free Software Foundation, Inc., |
27 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
28 | | *****************************************************************************/ |
29 | | |
30 | | /***************************************************************************** |
31 | | * Preamble |
32 | | * |
33 | | * FIXME: |
34 | | * DVB subtitles coded as strings of characters are not handled correctly. |
35 | | * The character codes in the string should actually be indexes referring to a |
36 | | * character table identified in the subtitle descriptor. |
37 | | * |
38 | | * The spec is quite vague in this area, but what is meant is perhaps that it |
39 | | * refers to the character index in the codepage belonging to the language |
40 | | * specified in the subtitle descriptor. Potentially it's designed for widechar |
41 | | * (but not for UTF-*) codepages. |
42 | | ***************************************************************************** |
43 | | * |
44 | | ***************************************************************************** |
45 | | * Notes on DDS (Display Definition Segment) |
46 | | * ----------------------------------------- |
47 | | * DDS (Display Definition Segment) tells the decoder how the subtitle image |
48 | | * relates to the video image. |
49 | | * For SD, the subtitle image is always considered to be for display at |
50 | | * 720x576 (although it's assumed that for NTSC, this is 720x480, this |
51 | | * is not documented well) Also, for SD, the subtitle image is drawn 'on |
52 | | * the glass' (i.e. after video scaling, letterbox, etc.) |
53 | | * For 'HD' (subs marked type 0x14/0x24 in PSI), a DDS must be present, |
54 | | * and the subs area is drawn onto the video area (scales if necessary). |
55 | | * The DDS tells the decoder what resolution the subtitle images were |
56 | | * intended for, and hence how to scale the subtitle images for a |
57 | | * particular video size |
58 | | * i.e. if HD video is presented as letterbox, the subs will be in the |
59 | | * same place on the video as if the video was presented on an HD set |
60 | | * indeed, if the HD video was pillarboxed by the decoder, the subs may |
61 | | * be cut off as well as the video. The intent here is that the subs can |
62 | | * be placed accurately on the video - something which was missed in the |
63 | | * original spec. |
64 | | * |
65 | | * A DDS may also specify a window - this is where the subs images are moved so that the (0,0) |
66 | | * origin of decode is offset. |
67 | | ********************************************************************************************/ |
68 | | |
69 | | #ifdef HAVE_CONFIG_H |
70 | | # include "config.h" |
71 | | #endif |
72 | | |
73 | | #include <vlc_common.h> |
74 | | #include <vlc_configuration.h> |
75 | | #include <vlc_plugin.h> |
76 | | #include <vlc_codec.h> |
77 | | #include <vlc_sout.h> |
78 | | |
79 | | #include <vlc_bits.h> |
80 | | |
81 | | #include <limits.h> |
82 | | |
83 | | /* #define DEBUG_DVBSUB 1 */ |
84 | | |
85 | | #define POSX_TEXT N_("Decoding X coordinate") |
86 | | #define POSX_LONGTEXT N_("X coordinate of the rendered subtitle") |
87 | | |
88 | | #define POSY_TEXT N_("Decoding Y coordinate") |
89 | | #define POSY_LONGTEXT N_("Y coordinate of the rendered subtitle") |
90 | | |
91 | | #define POS_TEXT N_("Subpicture position") |
92 | | #define POS_LONGTEXT N_( \ |
93 | | "You can enforce the subpicture position on the video " \ |
94 | | "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \ |
95 | | "also use combinations of these values, e.g. 6=top-right).") |
96 | | |
97 | | #define ENC_POSX_TEXT N_("Encoding X coordinate") |
98 | | #define ENC_POSX_LONGTEXT N_("X coordinate of the encoded subtitle" ) |
99 | | #define ENC_POSY_TEXT N_("Encoding Y coordinate") |
100 | | #define ENC_POSY_LONGTEXT N_("Y coordinate of the encoded subtitle" ) |
101 | | |
102 | | static const int pi_pos_values[] = { |
103 | | 0, |
104 | | SUBPICTURE_ALIGN_LEFT, |
105 | | SUBPICTURE_ALIGN_RIGHT, |
106 | | SUBPICTURE_ALIGN_TOP, |
107 | | SUBPICTURE_ALIGN_BOTTOM, |
108 | | SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT, |
109 | | SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_RIGHT, |
110 | | SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_LEFT, |
111 | | SUBPICTURE_ALIGN_BOTTOM | SUBPICTURE_ALIGN_RIGHT, |
112 | | }; |
113 | | static const char *const ppsz_pos_descriptions[] = |
114 | | { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"), |
115 | | N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") }; |
116 | | |
117 | | /***************************************************************************** |
118 | | * Module descriptor. |
119 | | *****************************************************************************/ |
120 | | static int Open ( vlc_object_t * ); |
121 | | static void Close( vlc_object_t * ); |
122 | | static int Decode( decoder_t *, block_t * ); |
123 | | static void Flush( decoder_t * ); |
124 | | |
125 | | #ifdef ENABLE_SOUT |
126 | | static int OpenEncoder ( vlc_object_t * ); |
127 | | static void CloseEncoder( encoder_t * ); |
128 | | static block_t *Encode ( encoder_t *, subpicture_t * ); |
129 | | #endif |
130 | | |
131 | 104 | vlc_module_begin () |
132 | 52 | # define DVBSUB_CFG_PREFIX "dvbsub-" |
133 | 52 | set_description( N_("DVB subtitles decoder") ) |
134 | 52 | set_shortname( N_("DVB subtitles") ) |
135 | 52 | set_capability( "spu decoder", 80 ) |
136 | 52 | set_subcategory( SUBCAT_INPUT_SCODEC ) |
137 | 52 | set_callbacks( Open, Close ) |
138 | | |
139 | 52 | add_integer( DVBSUB_CFG_PREFIX "position", 8, POS_TEXT, POS_LONGTEXT ) |
140 | 52 | change_integer_list( pi_pos_values, ppsz_pos_descriptions ) |
141 | 52 | add_integer( DVBSUB_CFG_PREFIX "x", -1, POSX_TEXT, POSX_LONGTEXT ) |
142 | 52 | add_integer( DVBSUB_CFG_PREFIX "y", -1, POSY_TEXT, POSY_LONGTEXT ) |
143 | | |
144 | 52 | #ifdef ENABLE_SOUT |
145 | 52 | # define ENC_CFG_PREFIX "sout-dvbsub-" |
146 | 52 | add_submodule () |
147 | 52 | set_description( N_("DVB subtitles encoder") ) |
148 | 52 | set_capability( "spu encoder", 100 ) |
149 | 52 | set_callback( OpenEncoder ) |
150 | | |
151 | 52 | add_integer( ENC_CFG_PREFIX "x", -1, ENC_POSX_TEXT, ENC_POSX_LONGTEXT ) |
152 | 52 | add_integer( ENC_CFG_PREFIX "y", -1, ENC_POSY_TEXT, ENC_POSY_LONGTEXT ) |
153 | 52 | #endif |
154 | 52 | vlc_module_end () |
155 | | |
156 | | static const char *const ppsz_enc_options[] = { "x", "y", NULL }; |
157 | | |
158 | | /**************************************************************************** |
159 | | * Local structures |
160 | | **************************************************************************** |
161 | | * Those structures refer closely to the ETSI 300 743 Object model |
162 | | ****************************************************************************/ |
163 | | |
164 | | /* The object definition gives the position of the object in a region [7.2.5] */ |
165 | | typedef struct dvbsub_objectdef_s |
166 | | { |
167 | | int i_id; |
168 | | int i_type; |
169 | | int i_x; |
170 | | int i_y; |
171 | | int i_fg_pc; |
172 | | int i_bg_pc; |
173 | | char *psz_text; /* for string of characters objects */ |
174 | | |
175 | | } dvbsub_objectdef_t; |
176 | | |
177 | | /* The entry in the palette CLUT */ |
178 | | typedef struct |
179 | | { |
180 | | uint8_t Y; |
181 | | uint8_t Cr; |
182 | | uint8_t Cb; |
183 | | uint8_t T; |
184 | | |
185 | | } dvbsub_color_t; |
186 | | |
187 | | /* The displays dimensions [7.2.1] */ |
188 | | typedef struct dvbsub_display_s |
189 | | { |
190 | | uint8_t i_id; |
191 | | uint8_t i_version; |
192 | | |
193 | | uint16_t i_width_minus1; |
194 | | uint16_t i_height_minus1; |
195 | | |
196 | | bool b_windowed; |
197 | | /* these values are only relevant if windowed */ |
198 | | int i_x; |
199 | | int i_y; |
200 | | int i_max_x; |
201 | | int i_max_y; |
202 | | |
203 | | } dvbsub_display_t; |
204 | | |
205 | | /* [7.2.4] */ |
206 | | typedef struct dvbsub_clut_s |
207 | | { |
208 | | uint8_t i_id; |
209 | | uint8_t i_version; |
210 | | dvbsub_color_t c_2b[4]; |
211 | | dvbsub_color_t c_4b[16]; |
212 | | dvbsub_color_t c_8b[256]; |
213 | | int c_8b_entries; |
214 | | video_color_range_t color_range; |
215 | | int dynamic_range_and_colour_gamut; |
216 | | |
217 | | struct dvbsub_clut_s *p_next; |
218 | | |
219 | | } dvbsub_clut_t; |
220 | | |
221 | | /* The Region is an area on the image [7.2.3] |
222 | | * with a list of the object definitions associated and a CLUT */ |
223 | | typedef struct dvbsub_region_s |
224 | | { |
225 | | int i_id; |
226 | | int i_version; |
227 | | int i_x; |
228 | | int i_y; |
229 | | int i_width; |
230 | | int i_height; |
231 | | int i_level_comp; |
232 | | int i_depth; |
233 | | int i_clut; |
234 | | |
235 | | uint8_t *p_pixbuf; |
236 | | |
237 | | int i_object_defs; |
238 | | dvbsub_objectdef_t *p_object_defs; |
239 | | |
240 | | struct dvbsub_region_s *p_next; |
241 | | |
242 | | } dvbsub_region_t; |
243 | | |
244 | | /* The object definition gives the position of the object in a region */ |
245 | | typedef struct dvbsub_regiondef_s |
246 | | { |
247 | | int i_id; |
248 | | int i_x; |
249 | | int i_y; |
250 | | |
251 | | } dvbsub_regiondef_t; |
252 | | |
253 | | /* The page defines the list of regions [7.2.2] */ |
254 | | typedef struct |
255 | | { |
256 | | int i_id; |
257 | | int i_timeout; /* in seconds */ |
258 | | int i_state; |
259 | | int i_version; |
260 | | |
261 | | int i_region_defs; |
262 | | dvbsub_regiondef_t *p_region_defs; |
263 | | |
264 | | } dvbsub_page_t; |
265 | | |
266 | | typedef struct |
267 | | { |
268 | | bs_t bs; |
269 | | |
270 | | /* Decoder internal data */ |
271 | | int i_id; |
272 | | int i_ancillary_id; |
273 | | vlc_tick_t i_pts; |
274 | | |
275 | | int i_spu_position; |
276 | | int i_spu_x; |
277 | | int i_spu_y; |
278 | | |
279 | | bool b_page; |
280 | | dvbsub_page_t *p_page; |
281 | | dvbsub_region_t *p_regions; |
282 | | dvbsub_clut_t *p_cluts; |
283 | | /* this is very small, so keep forever */ |
284 | | dvbsub_display_t display; |
285 | | dvbsub_clut_t default_clut; |
286 | | } decoder_sys_t; |
287 | | |
288 | | |
289 | | /* List of different SEGMENT TYPES */ |
290 | | /* According to EN 300-743, table 2 */ |
291 | 0 | #define DVBSUB_ST_PAGE_COMPOSITION 0x10 |
292 | 0 | #define DVBSUB_ST_REGION_COMPOSITION 0x11 |
293 | 0 | #define DVBSUB_ST_CLUT_DEFINITION 0x12 |
294 | 0 | #define DVBSUB_ST_OBJECT_DATA 0x13 |
295 | 0 | #define DVBSUB_ST_DISPLAY_DEFINITION 0x14 |
296 | 0 | #define DVBSUB_ST_ALTERNATE_CLUT 0x16 |
297 | 0 | #define DVBSUB_ST_ENDOFDISPLAY 0x80 |
298 | 0 | #define DVBSUB_ST_STUFFING 0xff |
299 | | /* List of different OBJECT TYPES */ |
300 | | /* According to EN 300-743, table 6 */ |
301 | 0 | #define DVBSUB_OT_BASIC_BITMAP 0x00 |
302 | 0 | #define DVBSUB_OT_BASIC_CHAR 0x01 |
303 | 0 | #define DVBSUB_OT_COMPOSITE_STRING 0x02 |
304 | | /* Pixel DATA TYPES */ |
305 | | /* According to EN 300-743, table 9 */ |
306 | | #define DVBSUB_DT_2BP_CODE_STRING 0x10 |
307 | | #define DVBSUB_DT_4BP_CODE_STRING 0x11 |
308 | | #define DVBSUB_DT_8BP_CODE_STRING 0x12 |
309 | | #define DVBSUB_DT_24_TABLE_DATA 0x20 |
310 | | #define DVBSUB_DT_28_TABLE_DATA 0x21 |
311 | | #define DVBSUB_DT_48_TABLE_DATA 0x22 |
312 | | #define DVBSUB_DT_END_LINE 0xf0 |
313 | | /* List of different Page Composition Segment state */ |
314 | | /* According to EN 300-743, 7.2.1 table 3 */ |
315 | 0 | #define DVBSUB_PCS_STATE_ACQUISITION 0x01 |
316 | 0 | #define DVBSUB_PCS_STATE_CHANGE 0x02 |
317 | | /* According to EN 300-743, 7.2.8 table 33 */ |
318 | 0 | #define DVBSUB_ST_BITDEPTH_8BIT 0x00 |
319 | 0 | #define DVBSUB_ST_BITDEPTH_10BIT 0x01 |
320 | | /* According to EN 300-743, 7.2.8 table 34 */ |
321 | 1 | #define DVBSUB_ST_COLORIMETRY_CDS -1 |
322 | 0 | #define DVBSUB_ST_COLORIMETRY_SDR_709 0x00 |
323 | 0 | #define DVBSUB_ST_COLORIMETRY_SDR_2020 0x01 |
324 | 0 | #define DVBSUB_ST_COLORIMETRY_HDR_PQ 0x02 |
325 | 0 | #define DVBSUB_ST_COLORIMETRY_HDR_HLG 0x03 |
326 | | |
327 | | /***************************************************************************** |
328 | | * Local prototypes |
329 | | *****************************************************************************/ |
330 | | static void decode_segment( decoder_t *, bs_t * ); |
331 | | static void decode_page_composition( decoder_t *, bs_t *, uint16_t ); |
332 | | static void decode_region_composition( decoder_t *, bs_t *, uint16_t ); |
333 | | static void decode_object( decoder_t *, bs_t *, uint16_t ); |
334 | | static void decode_display_definition( decoder_t *, bs_t *, uint16_t ); |
335 | | static void alternative_CLUT( decoder_t *, bs_t *, uint16_t ); |
336 | | static void decode_clut( decoder_t *, bs_t *, uint16_t ); |
337 | | static void free_all( decoder_t * ); |
338 | | |
339 | | static void default_clut_init( decoder_t * ); |
340 | | static void default_dds_init( decoder_t * ); |
341 | | |
342 | | static subpicture_t *render( decoder_t * ); |
343 | | |
344 | | /***************************************************************************** |
345 | | * Open: probe the decoder and return score |
346 | | ***************************************************************************** |
347 | | * Tries to launch a decoder and return score so that the interface is able |
348 | | * to chose. |
349 | | *****************************************************************************/ |
350 | | static int Open( vlc_object_t *p_this ) |
351 | 16.2k | { |
352 | 16.2k | decoder_t *p_dec = (decoder_t *) p_this; |
353 | 16.2k | decoder_sys_t *p_sys; |
354 | 16.2k | int i_posx, i_posy; |
355 | | |
356 | 16.2k | if( p_dec->fmt_in->i_codec != VLC_CODEC_DVBS ) |
357 | 16.2k | { |
358 | 16.2k | return VLC_EGENERIC; |
359 | 16.2k | } |
360 | | |
361 | 1 | p_dec->pf_decode = Decode; |
362 | 1 | p_dec->pf_flush = Flush; |
363 | 1 | p_sys = p_dec->p_sys = calloc( 1, sizeof(decoder_sys_t) ); |
364 | 1 | if( !p_sys ) |
365 | 0 | return VLC_ENOMEM; |
366 | | |
367 | 1 | p_sys->i_pts = VLC_TICK_INVALID; |
368 | 1 | p_sys->i_id = p_dec->fmt_in->subs.dvb.i_id & 0xFFFF; |
369 | 1 | p_sys->i_ancillary_id = p_dec->fmt_in->subs.dvb.i_id >> 16; |
370 | | |
371 | 1 | p_sys->p_regions = NULL; |
372 | 1 | p_sys->p_cluts = NULL; |
373 | 1 | p_sys->p_page = NULL; |
374 | | |
375 | | /* configure for SD res in case DDS is not present */ |
376 | 1 | default_dds_init( p_dec ); |
377 | | |
378 | 1 | p_sys->i_spu_position = var_CreateGetInteger( p_this, |
379 | 1 | DVBSUB_CFG_PREFIX "position" ); |
380 | 1 | i_posx = var_CreateGetInteger( p_this, DVBSUB_CFG_PREFIX "x" ); |
381 | 1 | i_posy = var_CreateGetInteger( p_this, DVBSUB_CFG_PREFIX "y" ); |
382 | | |
383 | | /* Check if subpicture position was overridden */ |
384 | 1 | p_sys->i_spu_x = p_sys->i_spu_y = 0; |
385 | | |
386 | 1 | if( ( i_posx >= 0 ) && ( i_posy >= 0 ) ) |
387 | 0 | { |
388 | 0 | p_sys->i_spu_x = i_posx; |
389 | 0 | p_sys->i_spu_y = i_posy; |
390 | 0 | } |
391 | | |
392 | 1 | p_dec->fmt_out.i_codec = 0; |
393 | | |
394 | 1 | default_clut_init( p_dec ); |
395 | | |
396 | 1 | return VLC_SUCCESS; |
397 | 1 | } |
398 | | |
399 | | /***************************************************************************** |
400 | | * Close: |
401 | | *****************************************************************************/ |
402 | | static void Close( vlc_object_t *p_this ) |
403 | 1 | { |
404 | 1 | decoder_t *p_dec = (decoder_t*) p_this; |
405 | 1 | decoder_sys_t *p_sys = p_dec->p_sys; |
406 | | |
407 | 1 | var_Destroy( p_this, DVBSUB_CFG_PREFIX "x" ); |
408 | 1 | var_Destroy( p_this, DVBSUB_CFG_PREFIX "y" ); |
409 | 1 | var_Destroy( p_this, DVBSUB_CFG_PREFIX "position" ); |
410 | | |
411 | 1 | free_all( p_dec ); |
412 | 1 | free( p_sys ); |
413 | 1 | } |
414 | | |
415 | | /***************************************************************************** |
416 | | * Flush: |
417 | | *****************************************************************************/ |
418 | | static void Flush( decoder_t *p_dec ) |
419 | 0 | { |
420 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
421 | |
|
422 | 0 | p_sys->i_pts = VLC_TICK_INVALID; |
423 | 0 | } |
424 | | |
425 | | /***************************************************************************** |
426 | | * Decode: |
427 | | *****************************************************************************/ |
428 | | static int Decode( decoder_t *p_dec, block_t *p_block ) |
429 | 15.2k | { |
430 | 15.2k | decoder_sys_t *p_sys = p_dec->p_sys; |
431 | | |
432 | 15.2k | if( p_block == NULL ) /* No Drain */ |
433 | 7.62k | return VLCDEC_SUCCESS; |
434 | | |
435 | 7.62k | if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED) ) |
436 | 0 | { |
437 | 0 | Flush( p_dec ); |
438 | 0 | if( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) |
439 | 0 | goto end; |
440 | 0 | } |
441 | | |
442 | | /* configure for SD res in case DDS is not present */ |
443 | | /* a change of PTS is a good indication we must get a new DDS */ |
444 | 7.62k | if( p_sys->i_pts != p_block->i_pts ) |
445 | 7.62k | default_dds_init( p_dec ); |
446 | | |
447 | 7.62k | p_sys->i_pts = p_block->i_pts; |
448 | 7.62k | if( p_sys->i_pts == VLC_TICK_INVALID ) |
449 | 0 | { |
450 | | #ifdef DEBUG_DVBSUB |
451 | | /* Some DVB channels send stuffing segments in non-dated packets so |
452 | | * don't complain too loudly. */ |
453 | | msg_Warn( p_dec, "non dated subtitle" ); |
454 | | #endif |
455 | 0 | goto end; |
456 | 0 | } |
457 | | |
458 | 7.62k | bs_init( &p_sys->bs, p_block->p_buffer, p_block->i_buffer ); |
459 | | |
460 | 7.62k | if( bs_read( &p_sys->bs, 8 ) != 0x20 ) /* Data identifier */ |
461 | 7.62k | { |
462 | 7.62k | msg_Dbg( p_dec, "invalid data identifier" ); |
463 | 7.62k | goto end; |
464 | 7.62k | } |
465 | | |
466 | 0 | if( bs_read( &p_sys->bs, 8 ) ) /* Subtitle stream id */ |
467 | 0 | { |
468 | 0 | msg_Dbg( p_dec, "invalid subtitle stream id" ); |
469 | 0 | goto end; |
470 | 0 | } |
471 | | |
472 | | #ifdef DEBUG_DVBSUB |
473 | | msg_Dbg( p_dec, "subtitle packet received: %"PRId64, p_sys->i_pts ); |
474 | | #endif |
475 | | |
476 | 0 | p_sys->b_page = false; |
477 | |
|
478 | 0 | uint8_t i_sync_byte = bs_read( &p_sys->bs, 8 ); |
479 | 0 | while( i_sync_byte == 0x0f ) /* Sync byte */ |
480 | 0 | { |
481 | 0 | decode_segment( p_dec, &p_sys->bs ); |
482 | 0 | i_sync_byte = bs_read( &p_sys->bs, 8 ); |
483 | 0 | } |
484 | |
|
485 | 0 | if( ( i_sync_byte & 0x3f ) != 0x3f ) /* End marker */ |
486 | 0 | { |
487 | 0 | msg_Warn( p_dec, "end marker not found (corrupted subtitle ?)" ); |
488 | 0 | goto end; |
489 | 0 | } |
490 | | |
491 | | /* Check if the page is to be displayed */ |
492 | 0 | if( p_sys->p_page && p_sys->b_page ) |
493 | 0 | { |
494 | 0 | subpicture_t *p_spu = render( p_dec ); |
495 | 0 | if( p_spu != NULL ) |
496 | 0 | decoder_QueueSub( p_dec, p_spu ); |
497 | 0 | } |
498 | |
|
499 | 7.62k | end: |
500 | 7.62k | block_Release(p_block); |
501 | 7.62k | return VLCDEC_SUCCESS; |
502 | 0 | } |
503 | | |
504 | | /* following functions are local */ |
505 | | |
506 | | /***************************************************************************** |
507 | | * default_clut_init: default clut as defined in EN 300-743 section 10 |
508 | | *****************************************************************************/ |
509 | | static void default_clut_init( decoder_t *p_dec ) |
510 | 1 | { |
511 | 1 | decoder_sys_t *p_sys = p_dec->p_sys; |
512 | 1 | uint8_t i; |
513 | | |
514 | 20 | #define RGB_TO_Y(r, g, b) ((int16_t) 77 * r + 150 * g + 29 * b) / 256; |
515 | 20 | #define RGB_TO_U(r, g, b) ((int16_t) -44 * r - 87 * g + 131 * b) / 256; |
516 | 20 | #define RGB_TO_V(r, g, b) ((int16_t) 131 * r - 110 * g - 21 * b) / 256; |
517 | | |
518 | | /* 4 entries CLUT */ |
519 | 5 | for( i = 0; i < 4; i++ ) |
520 | 4 | { |
521 | 4 | uint8_t R = 0, G = 0, B = 0, T = 0; |
522 | | |
523 | 4 | if( !(i & 0x2) && !(i & 0x1) ) T = 0xFF; |
524 | 3 | else if( !(i & 0x2) && (i & 0x1) ) R = G = B = 0xFF; |
525 | 2 | else if( (i & 0x2) && !(i & 0x1) ) R = G = B = 0; |
526 | 1 | else R = G = B = 0x7F; |
527 | | |
528 | 4 | p_sys->default_clut.c_2b[i].Y = RGB_TO_Y(R,G,B); |
529 | 4 | p_sys->default_clut.c_2b[i].Cb = RGB_TO_V(R,G,B); |
530 | 4 | p_sys->default_clut.c_2b[i].Cr = RGB_TO_U(R,G,B); |
531 | 4 | p_sys->default_clut.c_2b[i].T = T; |
532 | 4 | } |
533 | | |
534 | | /* 16 entries CLUT */ |
535 | 17 | for( i = 0; i < 16; i++ ) |
536 | 16 | { |
537 | 16 | uint8_t R = 0, G = 0, B = 0, T = 0; |
538 | | |
539 | 16 | if( !(i & 0x8) ) |
540 | 8 | { |
541 | 8 | if( !(i & 0x4) && !(i & 0x2) && !(i & 0x1) ) |
542 | 1 | { |
543 | 1 | T = 0xFF; |
544 | 1 | } |
545 | 7 | else |
546 | 7 | { |
547 | 7 | R = (i & 0x1) ? 0xFF : 0; |
548 | 7 | G = (i & 0x2) ? 0xFF : 0; |
549 | 7 | B = (i & 0x4) ? 0xFF : 0; |
550 | 7 | } |
551 | 8 | } |
552 | 8 | else |
553 | 8 | { |
554 | 8 | R = (i & 0x1) ? 0x7F : 0; |
555 | 8 | G = (i & 0x2) ? 0x7F : 0; |
556 | 8 | B = (i & 0x4) ? 0x7F : 0; |
557 | 8 | } |
558 | | |
559 | 16 | p_sys->default_clut.c_4b[i].Y = RGB_TO_Y(R,G,B); |
560 | 16 | p_sys->default_clut.c_4b[i].Cr = RGB_TO_V(R,G,B); |
561 | 16 | p_sys->default_clut.c_4b[i].Cb = RGB_TO_U(R,G,B); |
562 | 16 | p_sys->default_clut.c_4b[i].T = T; |
563 | 16 | } |
564 | | |
565 | | /* 256 entries CLUT */ |
566 | 1 | memset( p_sys->default_clut.c_8b, 0xFF, 256 * sizeof(dvbsub_color_t) ); |
567 | 1 | p_sys->default_clut.c_8b_entries = 256; |
568 | 1 | p_sys->default_clut.color_range = COLOR_RANGE_LIMITED; |
569 | 1 | p_sys->default_clut.dynamic_range_and_colour_gamut = DVBSUB_ST_COLORIMETRY_CDS; |
570 | 1 | } |
571 | | |
572 | | static void decode_segment( decoder_t *p_dec, bs_t *s ) |
573 | 0 | { |
574 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
575 | 0 | int i_type; |
576 | 0 | int i_page_id; |
577 | 0 | int i_size; |
578 | | |
579 | | /* sync_byte (already checked) */ |
580 | | //bs_skip( s, 8 ); |
581 | | |
582 | | /* segment type */ |
583 | 0 | i_type = bs_read( s, 8 ); |
584 | | |
585 | | /* page id */ |
586 | 0 | i_page_id = bs_read( s, 16 ); |
587 | | |
588 | | /* segment size */ |
589 | 0 | i_size = bs_read( s, 16 ); |
590 | |
|
591 | 0 | if( ( i_page_id != p_sys->i_id ) && |
592 | 0 | ( i_page_id != p_sys->i_ancillary_id ) ) |
593 | 0 | { |
594 | | #ifdef DEBUG_DVBSUB |
595 | | msg_Dbg( p_dec, "subtitle skipped (page id: %i, %i)", |
596 | | i_page_id, p_sys->i_id ); |
597 | | #endif |
598 | 0 | bs_skip( s, 8 * i_size ); |
599 | 0 | return; |
600 | 0 | } |
601 | | |
602 | 0 | if( ( p_sys->i_ancillary_id != p_sys->i_id ) && |
603 | 0 | ( i_type == DVBSUB_ST_PAGE_COMPOSITION ) && |
604 | 0 | ( i_page_id == p_sys->i_ancillary_id ) ) |
605 | 0 | { |
606 | | #ifdef DEBUG_DVBSUB |
607 | | msg_Dbg( p_dec, "skipped invalid ancillary subtitle packet" ); |
608 | | #endif |
609 | 0 | bs_skip( s, 8 * i_size ); |
610 | 0 | return; |
611 | 0 | } |
612 | | |
613 | | #ifdef DEBUG_DVBSUB |
614 | | if( i_page_id == p_sys->i_id ) |
615 | | msg_Dbg( p_dec, "segment (id: %i)", i_page_id ); |
616 | | else |
617 | | msg_Dbg( p_dec, "ancillary segment (id: %i)", i_page_id ); |
618 | | #endif |
619 | | |
620 | 0 | switch( i_type ) |
621 | 0 | { |
622 | 0 | case DVBSUB_ST_PAGE_COMPOSITION: |
623 | | #ifdef DEBUG_DVBSUB |
624 | | msg_Dbg( p_dec, "decode_page_composition" ); |
625 | | #endif |
626 | 0 | decode_page_composition( p_dec, s, i_size ); |
627 | 0 | break; |
628 | | |
629 | 0 | case DVBSUB_ST_REGION_COMPOSITION: |
630 | | #ifdef DEBUG_DVBSUB |
631 | | msg_Dbg( p_dec, "decode_region_composition" ); |
632 | | #endif |
633 | 0 | decode_region_composition( p_dec, s, i_size ); |
634 | 0 | break; |
635 | | |
636 | 0 | case DVBSUB_ST_CLUT_DEFINITION: |
637 | | #ifdef DEBUG_DVBSUB |
638 | | msg_Dbg( p_dec, "decode_clut" ); |
639 | | #endif |
640 | 0 | decode_clut( p_dec, s, i_size ); |
641 | 0 | break; |
642 | | |
643 | 0 | case DVBSUB_ST_OBJECT_DATA: |
644 | | #ifdef DEBUG_DVBSUB |
645 | | msg_Dbg( p_dec, "decode_object" ); |
646 | | #endif |
647 | 0 | decode_object( p_dec, s, i_size ); |
648 | 0 | break; |
649 | | |
650 | 0 | case DVBSUB_ST_DISPLAY_DEFINITION: |
651 | | #ifdef DEBUG_DVBSUB |
652 | | msg_Dbg( p_dec, "decode_display_definition" ); |
653 | | #endif |
654 | 0 | decode_display_definition( p_dec, s, i_size ); |
655 | 0 | break; |
656 | | |
657 | 0 | case DVBSUB_ST_ALTERNATE_CLUT: |
658 | | #ifdef DEBUG_DVBSUB |
659 | | msg_Dbg( p_dec, "alternative_CLUT" ); |
660 | | #endif |
661 | 0 | alternative_CLUT( p_dec, s, i_size ); |
662 | 0 | break; |
663 | | |
664 | 0 | case DVBSUB_ST_ENDOFDISPLAY: |
665 | | #ifdef DEBUG_DVBSUB |
666 | | msg_Dbg( p_dec, "end of display" ); |
667 | | #endif |
668 | 0 | bs_skip( s, 8 * i_size ); |
669 | 0 | break; |
670 | | |
671 | 0 | case DVBSUB_ST_STUFFING: |
672 | | #ifdef DEBUG_DVBSUB |
673 | | msg_Dbg( p_dec, "skip stuffing" ); |
674 | | #endif |
675 | 0 | bs_skip( s, 8 * i_size ); |
676 | 0 | break; |
677 | | |
678 | 0 | default: |
679 | 0 | msg_Warn( p_dec, "unsupported segment type: (%04x)", i_type ); |
680 | 0 | bs_skip( s, 8 * i_size ); |
681 | 0 | break; |
682 | 0 | } |
683 | 0 | } |
684 | | |
685 | | static void decode_clut( decoder_t *p_dec, bs_t *s, uint16_t i_segment_length ) |
686 | 0 | { |
687 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
688 | 0 | uint16_t i_processed_length; |
689 | 0 | dvbsub_clut_t *p_clut, *p_next; |
690 | 0 | int i_id, i_version; |
691 | |
|
692 | 0 | i_id = bs_read( s, 8 ); |
693 | 0 | i_version = bs_read( s, 4 ); |
694 | | |
695 | | /* Check if we already have this clut */ |
696 | 0 | for( p_clut = p_sys->p_cluts; p_clut != NULL; p_clut = p_clut->p_next ) |
697 | 0 | { |
698 | 0 | if( p_clut->i_id == i_id ) break; |
699 | 0 | } |
700 | | |
701 | | /* Check version number */ |
702 | 0 | if( p_clut && ( p_clut->i_version == i_version ) ) |
703 | 0 | { |
704 | | /* Nothing to do */ |
705 | 0 | bs_skip( s, 8 * i_segment_length - 12 ); |
706 | 0 | return; |
707 | 0 | } |
708 | | |
709 | 0 | if( !p_clut ) |
710 | 0 | { |
711 | | #ifdef DEBUG_DVBSUB |
712 | | msg_Dbg( p_dec, "new clut: %i", i_id ); |
713 | | #endif |
714 | 0 | p_clut = malloc( sizeof( dvbsub_clut_t ) ); |
715 | 0 | if( !p_clut ) |
716 | 0 | return; |
717 | 0 | p_clut->p_next = p_sys->p_cluts; |
718 | 0 | p_sys->p_cluts = p_clut; |
719 | 0 | } |
720 | | |
721 | | /* Initialize to default clut */ |
722 | 0 | p_next = p_clut->p_next; |
723 | 0 | *p_clut = p_sys->default_clut; |
724 | 0 | p_clut->p_next = p_next; |
725 | | |
726 | | /* We don't have this version of the CLUT: Parse it */ |
727 | 0 | p_clut->i_version = i_version; |
728 | 0 | p_clut->i_id = i_id; |
729 | 0 | bs_skip( s, 4 ); /* Reserved bits */ |
730 | 0 | i_processed_length = 2; |
731 | 0 | while( i_processed_length < i_segment_length ) |
732 | 0 | { |
733 | 0 | uint8_t y, cb, cr, t; |
734 | 0 | uint_fast8_t cid = bs_read( s, 8 ); |
735 | 0 | uint_fast8_t type = bs_read( s, 3 ); |
736 | |
|
737 | 0 | bs_skip( s, 4 ); |
738 | |
|
739 | 0 | p_clut->color_range = bs_read( s, 1 ) ? COLOR_RANGE_FULL : COLOR_RANGE_LIMITED; |
740 | 0 | if( p_clut->color_range == COLOR_RANGE_FULL ) |
741 | 0 | { |
742 | 0 | y = bs_read( s, 8 ); |
743 | 0 | cr = bs_read( s, 8 ); |
744 | 0 | cb = bs_read( s, 8 ); |
745 | 0 | t = bs_read( s, 8 ); |
746 | 0 | i_processed_length += 6; |
747 | 0 | } |
748 | 0 | else |
749 | 0 | { |
750 | 0 | y = bs_read( s, 6 ) << 2; |
751 | 0 | cr = bs_read( s, 4 ) << 4; |
752 | 0 | cb = bs_read( s, 4 ) << 4; |
753 | 0 | t = bs_read( s, 2 ) << 6; |
754 | 0 | i_processed_length += 4; |
755 | 0 | } |
756 | | |
757 | | /* We are not entirely compliant here as full transparency is indicated |
758 | | * with a luma value of zero, not a transparency value of 0xff |
759 | | * (full transparency would actually be 0xff + 1). */ |
760 | 0 | if( y == 0 ) |
761 | 0 | { |
762 | 0 | cr = cb = 0; |
763 | 0 | t = 0xff; |
764 | 0 | } |
765 | | |
766 | | /* According to EN 300-743 section 7.2.3 note 1, type should |
767 | | * not have more than 1 bit set to one, but some streams don't |
768 | | * respect this note. */ |
769 | 0 | if( ( type & 0x04 ) && ( cid < 4 ) ) |
770 | 0 | { |
771 | 0 | p_clut->c_2b[cid].Y = y; |
772 | 0 | p_clut->c_2b[cid].Cr = cr; |
773 | 0 | p_clut->c_2b[cid].Cb = cb; |
774 | 0 | p_clut->c_2b[cid].T = t; |
775 | 0 | } |
776 | 0 | if( ( type & 0x02 ) && ( cid < 16 ) ) |
777 | 0 | { |
778 | 0 | p_clut->c_4b[cid].Y = y; |
779 | 0 | p_clut->c_4b[cid].Cr = cr; |
780 | 0 | p_clut->c_4b[cid].Cb = cb; |
781 | 0 | p_clut->c_4b[cid].T = t; |
782 | 0 | } |
783 | 0 | if( type & 0x01 ) |
784 | 0 | { |
785 | 0 | p_clut->c_8b[cid].Y = y; |
786 | 0 | p_clut->c_8b[cid].Cr = cr; |
787 | 0 | p_clut->c_8b[cid].Cb = cb; |
788 | 0 | p_clut->c_8b[cid].T = t; |
789 | 0 | } |
790 | 0 | } |
791 | 0 | } |
792 | | |
793 | | static void decode_page_composition( decoder_t *p_dec, bs_t *s, uint16_t i_segment_length ) |
794 | 0 | { |
795 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
796 | 0 | int i_version, i_state, i_timeout, i; |
797 | | |
798 | | /* A page is composed by 0 or more region */ |
799 | 0 | i_timeout = bs_read( s, 8 ); |
800 | 0 | i_version = bs_read( s, 4 ); |
801 | 0 | i_state = bs_read( s, 2 ); |
802 | 0 | bs_skip( s, 2 ); /* Reserved */ |
803 | |
|
804 | 0 | if( i_state == DVBSUB_PCS_STATE_CHANGE ) |
805 | 0 | { |
806 | | /* End of an epoch, reset decoder buffer */ |
807 | | #ifdef DEBUG_DVBSUB |
808 | | msg_Dbg( p_dec, "page composition mode change" ); |
809 | | #endif |
810 | 0 | free_all( p_dec ); |
811 | 0 | } |
812 | 0 | else if( !p_sys->p_page && ( i_state != DVBSUB_PCS_STATE_ACQUISITION ) && |
813 | 0 | ( i_state != DVBSUB_PCS_STATE_CHANGE ) ) |
814 | 0 | { |
815 | | /* Not a full PCS, we need to wait for one */ |
816 | 0 | msg_Dbg( p_dec, "didn't receive an acquisition page yet" ); |
817 | |
|
818 | | #if 0 |
819 | | /* Try to start decoding even without an acquisition page */ |
820 | | bs_skip( s, 8 * (i_segment_length - 2) ); |
821 | | return; |
822 | | #endif |
823 | 0 | } |
824 | |
|
825 | | #ifdef DEBUG_DVBSUB |
826 | | if( i_state == DVBSUB_PCS_STATE_ACQUISITION ) |
827 | | msg_Dbg( p_dec, "acquisition page composition" ); |
828 | | #endif |
829 | | |
830 | | /* Check version number */ |
831 | 0 | if( p_sys->p_page && ( p_sys->p_page->i_version == i_version ) ) |
832 | 0 | { |
833 | 0 | bs_skip( s, 8 * (i_segment_length - 2) ); |
834 | 0 | return; |
835 | 0 | } |
836 | 0 | else if( p_sys->p_page ) |
837 | 0 | { |
838 | 0 | if( p_sys->p_page->i_region_defs ) |
839 | 0 | free( p_sys->p_page->p_region_defs ); |
840 | 0 | p_sys->p_page->p_region_defs = NULL; |
841 | 0 | p_sys->p_page->i_region_defs = 0; |
842 | 0 | } |
843 | | |
844 | 0 | if( !p_sys->p_page ) |
845 | 0 | { |
846 | | #ifdef DEBUG_DVBSUB |
847 | | msg_Dbg( p_dec, "new page" ); |
848 | | #endif |
849 | | /* Allocate a new page */ |
850 | 0 | p_sys->p_page = malloc( sizeof(dvbsub_page_t) ); |
851 | 0 | if( !p_sys->p_page ) |
852 | 0 | return; |
853 | 0 | } |
854 | | |
855 | 0 | p_sys->p_page->i_version = i_version; |
856 | 0 | p_sys->p_page->i_timeout = i_timeout; |
857 | 0 | p_sys->b_page = true; |
858 | | |
859 | | /* Number of regions */ |
860 | 0 | p_sys->p_page->i_region_defs = (i_segment_length - 2) / 6; |
861 | |
|
862 | 0 | if( p_sys->p_page->i_region_defs == 0 ) return; |
863 | | |
864 | 0 | p_sys->p_page->p_region_defs = |
865 | 0 | vlc_alloc( p_sys->p_page->i_region_defs, sizeof(dvbsub_regiondef_t) ); |
866 | 0 | if( p_sys->p_page->p_region_defs ) |
867 | 0 | { |
868 | 0 | for( i = 0; i < p_sys->p_page->i_region_defs; i++ ) |
869 | 0 | { |
870 | 0 | p_sys->p_page->p_region_defs[i].i_id = bs_read( s, 8 ); |
871 | 0 | bs_skip( s, 8 ); /* Reserved */ |
872 | 0 | p_sys->p_page->p_region_defs[i].i_x = bs_read( s, 16 ); |
873 | 0 | p_sys->p_page->p_region_defs[i].i_y = bs_read( s, 16 ); |
874 | |
|
875 | | #ifdef DEBUG_DVBSUB |
876 | | msg_Dbg( p_dec, "page_composition, region %i (%i,%i)", |
877 | | i, p_sys->p_page->p_region_defs[i].i_x, |
878 | | p_sys->p_page->p_region_defs[i].i_y ); |
879 | | #endif |
880 | 0 | } |
881 | 0 | } |
882 | 0 | } |
883 | | |
884 | | static void decode_region_composition( decoder_t *p_dec, bs_t *s, uint16_t i_segment_length ) |
885 | 0 | { |
886 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
887 | 0 | dvbsub_region_t *p_region, **pp_region = &p_sys->p_regions; |
888 | 0 | int i_processed_length, i_id, i_version; |
889 | 0 | int i_width, i_height, i_level_comp, i_depth, i_clut; |
890 | 0 | int i_8_bg, i_4_bg, i_2_bg; |
891 | 0 | bool b_fill; |
892 | |
|
893 | 0 | i_id = bs_read( s, 8 ); |
894 | 0 | i_version = bs_read( s, 4 ); |
895 | | |
896 | | /* Check if we already have this region */ |
897 | 0 | for( p_region = p_sys->p_regions; p_region != NULL; |
898 | 0 | p_region = p_region->p_next ) |
899 | 0 | { |
900 | 0 | pp_region = &p_region->p_next; |
901 | 0 | if( p_region->i_id == i_id ) break; |
902 | 0 | } |
903 | | |
904 | | /* Check version number */ |
905 | 0 | if( p_region && ( p_region->i_version == i_version ) ) |
906 | 0 | { |
907 | 0 | bs_skip( s, 8 * (i_segment_length - 1) - 4 ); |
908 | 0 | return; |
909 | 0 | } |
910 | | |
911 | 0 | if( !p_region ) |
912 | 0 | { |
913 | | #ifdef DEBUG_DVBSUB |
914 | | msg_Dbg( p_dec, "new region: %i", i_id ); |
915 | | #endif |
916 | 0 | p_region = *pp_region = calloc( 1, sizeof(dvbsub_region_t) ); |
917 | 0 | if( !p_region ) |
918 | 0 | return; |
919 | 0 | p_region->p_object_defs = NULL; |
920 | 0 | p_region->p_pixbuf = NULL; |
921 | 0 | p_region->p_next = NULL; |
922 | 0 | } |
923 | | |
924 | | /* Region attributes */ |
925 | 0 | p_region->i_id = i_id; |
926 | 0 | p_region->i_version = i_version; |
927 | 0 | b_fill = bs_read( s, 1 ); |
928 | 0 | bs_skip( s, 3 ); /* Reserved */ |
929 | |
|
930 | 0 | i_width = bs_read( s, 16 ); |
931 | 0 | i_height = bs_read( s, 16 ); |
932 | | #ifdef DEBUG_DVBSUB |
933 | | msg_Dbg( p_dec, " width=%d height=%d", i_width, i_height ); |
934 | | #endif |
935 | 0 | i_level_comp = bs_read( s, 3 ); |
936 | 0 | i_depth = bs_read( s, 3 ); |
937 | 0 | bs_skip( s, 2 ); /* Reserved */ |
938 | 0 | i_clut = bs_read( s, 8 ); |
939 | |
|
940 | 0 | i_8_bg = bs_read( s, 8 ); |
941 | 0 | i_4_bg = bs_read( s, 4 ); |
942 | 0 | i_2_bg = bs_read( s, 2 ); |
943 | 0 | bs_skip( s, 2 ); /* Reserved */ |
944 | | |
945 | | /* Free old object defs */ |
946 | 0 | while( p_region->i_object_defs ) |
947 | 0 | free( p_region->p_object_defs[--p_region->i_object_defs].psz_text ); |
948 | |
|
949 | 0 | free( p_region->p_object_defs ); |
950 | 0 | p_region->p_object_defs = NULL; |
951 | | |
952 | | /* Extra sanity checks */ |
953 | 0 | if( ( p_region->i_width != i_width ) || |
954 | 0 | ( p_region->i_height != i_height ) ) |
955 | 0 | { |
956 | 0 | if( p_region->p_pixbuf ) |
957 | 0 | { |
958 | 0 | msg_Dbg( p_dec, "region size changed (%dx%d->%dx%d)", |
959 | 0 | p_region->i_width, p_region->i_height, i_width, i_height ); |
960 | 0 | free( p_region->p_pixbuf ); |
961 | 0 | } |
962 | |
|
963 | 0 | p_region->p_pixbuf = xmalloc( i_height * i_width ); |
964 | 0 | p_region->i_depth = 0; |
965 | 0 | b_fill = true; |
966 | 0 | } |
967 | 0 | if( p_region->i_depth && |
968 | 0 | ( ( p_region->i_depth != i_depth ) || |
969 | 0 | ( p_region->i_level_comp != i_level_comp ) || |
970 | 0 | ( p_region->i_clut != i_clut) ) ) |
971 | 0 | { |
972 | 0 | msg_Dbg( p_dec, "region parameters changed (not allowed)" ); |
973 | 0 | } |
974 | | |
975 | | /* Erase background of region */ |
976 | 0 | if( b_fill ) |
977 | 0 | { |
978 | 0 | int i_background = ( i_depth == 1 ) ? i_2_bg : |
979 | 0 | ( ( i_depth == 2 ) ? i_4_bg : i_8_bg ); |
980 | 0 | memset( p_region->p_pixbuf, i_background, i_width * i_height ); |
981 | 0 | } |
982 | |
|
983 | 0 | p_region->i_width = i_width; |
984 | 0 | p_region->i_height = i_height; |
985 | 0 | p_region->i_level_comp = i_level_comp; |
986 | 0 | p_region->i_depth = i_depth; |
987 | 0 | p_region->i_clut = i_clut; |
988 | | |
989 | | /* List of objects in the region */ |
990 | 0 | i_processed_length = 10; |
991 | 0 | while( i_processed_length < i_segment_length ) |
992 | 0 | { |
993 | 0 | dvbsub_objectdef_t *p_obj; |
994 | | |
995 | | /* We create a new object */ |
996 | 0 | p_region->i_object_defs++; |
997 | 0 | p_region->p_object_defs = xrealloc( p_region->p_object_defs, |
998 | 0 | sizeof(dvbsub_objectdef_t) * p_region->i_object_defs ); |
999 | | |
1000 | | /* We parse object properties */ |
1001 | 0 | p_obj = &p_region->p_object_defs[p_region->i_object_defs - 1]; |
1002 | 0 | p_obj->i_id = bs_read( s, 16 ); |
1003 | 0 | p_obj->i_type = bs_read( s, 2 ); |
1004 | 0 | bs_skip( s, 2 ); /* Provider */ |
1005 | 0 | p_obj->i_x = bs_read( s, 12 ); |
1006 | 0 | bs_skip( s, 4 ); /* Reserved */ |
1007 | 0 | p_obj->i_y = bs_read( s, 12 ); |
1008 | 0 | p_obj->psz_text = NULL; |
1009 | |
|
1010 | 0 | i_processed_length += 6; |
1011 | |
|
1012 | 0 | if( ( p_obj->i_type == DVBSUB_OT_BASIC_CHAR ) || |
1013 | 0 | ( p_obj->i_type == DVBSUB_OT_COMPOSITE_STRING ) ) |
1014 | 0 | { |
1015 | 0 | p_obj->i_fg_pc = bs_read( s, 8 ); |
1016 | 0 | p_obj->i_bg_pc = bs_read( s, 8 ); |
1017 | 0 | i_processed_length += 2; |
1018 | 0 | } |
1019 | 0 | } |
1020 | 0 | } |
1021 | | |
1022 | | /* ETSI 300 743 [7.2.8] */ |
1023 | | static void alternative_CLUT( decoder_t *p_dec, bs_t *s, uint16_t i_segment_length ) |
1024 | 0 | { |
1025 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
1026 | 0 | uint16_t i_processed_length; |
1027 | 0 | int i_id, i_version; |
1028 | 0 | dvbsub_clut_t *p_clut; |
1029 | |
|
1030 | 0 | i_id = bs_read( s, 8 ); |
1031 | 0 | i_version = bs_read( s, 4 ); |
1032 | | |
1033 | | /* Check if we already have this clut */ |
1034 | 0 | for( p_clut = p_sys->p_cluts; p_clut != NULL; p_clut = p_clut->p_next ) |
1035 | 0 | { |
1036 | 0 | if( p_clut->i_id == i_id ) |
1037 | 0 | { |
1038 | | /* Check version number */ |
1039 | 0 | if( p_clut->i_version == i_version ) |
1040 | 0 | { |
1041 | | /* Nothing to do */ |
1042 | 0 | bs_skip( s, 8 * i_segment_length - 12 ); |
1043 | 0 | return; |
1044 | 0 | } |
1045 | | |
1046 | 0 | break; |
1047 | 0 | } |
1048 | 0 | } |
1049 | | |
1050 | 0 | if( !p_clut ) |
1051 | 0 | { |
1052 | | #ifdef DEBUG_DVBSUB |
1053 | | msg_Dbg( p_dec, "new alternative clut: %i", i_id ); |
1054 | | #endif |
1055 | 0 | p_clut = malloc( sizeof( dvbsub_clut_t ) ); |
1056 | 0 | if( !p_clut ) |
1057 | 0 | return; |
1058 | 0 | p_clut->p_next = p_sys->p_cluts; |
1059 | 0 | p_sys->p_cluts = p_clut; |
1060 | 0 | } |
1061 | | |
1062 | | /* We don't have this version of the CLUT: Parse it */ |
1063 | 0 | p_clut->i_version = i_version; |
1064 | 0 | p_clut->i_id = i_id; |
1065 | |
|
1066 | 0 | bs_skip( s, 4 ); // reserved_zero_future_use |
1067 | | |
1068 | | // CLUT_parameters |
1069 | 0 | int CLUT_entry_max_number = bs_read( s, 2 ); |
1070 | 0 | int colour_component_type = bs_read( s, 2 ); |
1071 | 0 | int output_bit_depth = bs_read( s, 3 ); |
1072 | 0 | bs_skip( s, 1 ); // reserved_zero_future_use |
1073 | 0 | int dynamic_range_and_colour_gamut = bs_read( s, 8 ); |
1074 | |
|
1075 | 0 | bool error = false; |
1076 | 0 | i_processed_length = 4; |
1077 | 0 | if (output_bit_depth != DVBSUB_ST_BITDEPTH_8BIT && |
1078 | 0 | output_bit_depth != DVBSUB_ST_BITDEPTH_10BIT) |
1079 | 0 | { |
1080 | 0 | msg_Err( p_dec, "unsupported alternative clut bitdepth: %i", output_bit_depth ); |
1081 | 0 | error = true; |
1082 | 0 | } |
1083 | 0 | if (CLUT_entry_max_number != 0) // 0: 256 entries |
1084 | 0 | { |
1085 | 0 | msg_Err( p_dec, "unsupported alternative clut max entries: %i", CLUT_entry_max_number ); |
1086 | 0 | error = true; |
1087 | 0 | } |
1088 | 0 | if (colour_component_type != 0) // 0: YCbCr |
1089 | 0 | { |
1090 | 0 | msg_Err( p_dec, "unsupported alternative clut component type: %i", colour_component_type ); |
1091 | 0 | error = true; |
1092 | 0 | } |
1093 | 0 | if (dynamic_range_and_colour_gamut > DVBSUB_ST_COLORIMETRY_HDR_HLG) |
1094 | 0 | { |
1095 | 0 | msg_Err( p_dec, "unsupported alternative clut color range: %i", dynamic_range_and_colour_gamut ); |
1096 | 0 | error = true; |
1097 | 0 | } |
1098 | 0 | if (error) |
1099 | 0 | goto done; |
1100 | | |
1101 | 0 | p_clut->c_8b_entries = 0; |
1102 | 0 | while( i_processed_length < i_segment_length ) |
1103 | 0 | { |
1104 | 0 | uint8_t y, cb, cr, t; |
1105 | 0 | if (output_bit_depth == DVBSUB_ST_BITDEPTH_10BIT) |
1106 | 0 | { |
1107 | | // TODO: apply the palette locally to keep 10-bit values |
1108 | 0 | y = bs_read( s, 10 ) >> 2; |
1109 | 0 | cr = bs_read( s, 10 ) >> 2; |
1110 | 0 | cb = bs_read( s, 10 ) >> 2; |
1111 | 0 | t = bs_read( s, 10 ) >> 2; |
1112 | 0 | i_processed_length += 5; |
1113 | 0 | } |
1114 | 0 | else |
1115 | 0 | { |
1116 | 0 | y = bs_read( s, 8 ); |
1117 | 0 | cr = bs_read( s, 8 ); |
1118 | 0 | cb = bs_read( s, 8 ); |
1119 | 0 | t = bs_read( s, 8 ); |
1120 | 0 | i_processed_length += 4; |
1121 | 0 | } |
1122 | | |
1123 | | /* We are not entirely compliant here as full transparency is indicated |
1124 | | * with a luma value of zero, not a transparency value of 0xff |
1125 | | * (full transparency would actually be 0xff + 1). */ |
1126 | 0 | if( y == 0 ) |
1127 | 0 | { |
1128 | 0 | cr = cb = 0; |
1129 | 0 | t = 0xff; |
1130 | 0 | } |
1131 | |
|
1132 | 0 | p_clut->c_8b[p_clut->c_8b_entries].Y = y; |
1133 | 0 | p_clut->c_8b[p_clut->c_8b_entries].Cr = cr; |
1134 | 0 | p_clut->c_8b[p_clut->c_8b_entries].Cb = cb; |
1135 | 0 | p_clut->c_8b[p_clut->c_8b_entries].T = t; |
1136 | 0 | p_clut->c_8b_entries++; |
1137 | 0 | if (p_clut->c_8b_entries >= (int)ARRAY_SIZE(p_clut->c_8b)) |
1138 | 0 | break; |
1139 | 0 | } |
1140 | 0 | p_clut->dynamic_range_and_colour_gamut = dynamic_range_and_colour_gamut; |
1141 | 0 | p_clut->color_range = COLOR_RANGE_FULL; |
1142 | 0 | done: |
1143 | 0 | bs_skip( s, 8 * (i_segment_length - i_processed_length) ); |
1144 | 0 | } |
1145 | | |
1146 | | /* ETSI 300 743 [7.2.1] */ |
1147 | | static void decode_display_definition( decoder_t *p_dec, bs_t *s, uint16_t i_segment_length ) |
1148 | 0 | { |
1149 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
1150 | 0 | uint16_t i_processed_length = 40; |
1151 | 0 | int i_version; |
1152 | |
|
1153 | 0 | i_version = bs_read( s, 4 ); |
1154 | | |
1155 | | /* Check version number */ |
1156 | 0 | if( p_sys->display.i_version == i_version ) |
1157 | 0 | { |
1158 | | /* The definition did not change */ |
1159 | 0 | bs_skip( s, 8*i_segment_length - 4 ); |
1160 | 0 | return; |
1161 | 0 | } |
1162 | | |
1163 | | #ifdef DEBUG_DVBSUB |
1164 | | msg_Dbg( p_dec, "new display definition: %i", i_version ); |
1165 | | #endif |
1166 | | |
1167 | | /* We don't have this version of the display definition: Parse it */ |
1168 | 0 | p_sys->display.i_version = i_version; |
1169 | 0 | p_sys->display.b_windowed = bs_read( s, 1 ); |
1170 | 0 | bs_skip( s, 3 ); /* Reserved bits */ |
1171 | 0 | p_sys->display.i_width_minus1 = bs_read( s, 16 ); |
1172 | 0 | p_sys->display.i_height_minus1 = bs_read( s, 16 ); |
1173 | |
|
1174 | 0 | if( p_sys->display.b_windowed ) |
1175 | 0 | { |
1176 | | #ifdef DEBUG_DVBSUB |
1177 | | msg_Dbg( p_dec, "display definition with offsets (windowed)" ); |
1178 | | #endif |
1179 | | /* Coordinates are measured from the top left corner */ |
1180 | 0 | p_sys->display.i_x = bs_read( s, 16 ); |
1181 | 0 | p_sys->display.i_max_x = bs_read( s, 16 ); |
1182 | 0 | p_sys->display.i_y = bs_read( s, 16 ); |
1183 | 0 | p_sys->display.i_max_y = bs_read( s, 16 ); |
1184 | 0 | i_processed_length += 64; |
1185 | 0 | } |
1186 | 0 | else |
1187 | 0 | { |
1188 | | /* if not windowed, setup the window variables to good defaults */ |
1189 | | /* not necessary, but to avoid future confusion.. */ |
1190 | 0 | p_sys->display.i_x = 0; |
1191 | 0 | p_sys->display.i_max_x = p_sys->display.i_width_minus1; |
1192 | 0 | p_sys->display.i_y = 0; |
1193 | 0 | p_sys->display.i_max_y = p_sys->display.i_height_minus1; |
1194 | 0 | } |
1195 | |
|
1196 | 0 | if( i_processed_length != i_segment_length*8 ) |
1197 | 0 | { |
1198 | 0 | msg_Err( p_dec, "processed length %d bytes != segment length %d bytes", |
1199 | 0 | i_processed_length / 8 , i_segment_length ); |
1200 | 0 | } |
1201 | |
|
1202 | | #ifdef DEBUG_DVBSUB |
1203 | | msg_Dbg( p_dec, "version: %d, width: %d, height: %d", |
1204 | | p_sys->display.i_version, (int)p_sys->display.i_width_minus1+1, (int)p_sys->display.i_height_minus1+1 ); |
1205 | | if( p_sys->display.b_windowed ) |
1206 | | msg_Dbg( p_dec, "xmin: %d, xmax: %d, ymin: %d, ymax: %d", |
1207 | | p_sys->display.i_x, p_sys->display.i_max_x, p_sys->display.i_y, p_sys->display.i_max_y ); |
1208 | | #endif |
1209 | 0 | } |
1210 | | |
1211 | | static void dvbsub_render_pdata( decoder_t *, dvbsub_region_t *, int, int, |
1212 | | uint8_t *, int ); |
1213 | | static void dvbsub_pdata2bpp( bs_t *, uint8_t *, int, int * ); |
1214 | | static void dvbsub_pdata4bpp( bs_t *, uint8_t *, int, int * ); |
1215 | | static void dvbsub_pdata8bpp( bs_t *, uint8_t *, int, int * ); |
1216 | | |
1217 | | static void decode_object( decoder_t *p_dec, bs_t *s, uint16_t i_segment_length ) |
1218 | 0 | { |
1219 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
1220 | 0 | dvbsub_region_t *p_region; |
1221 | 0 | int i_coding_method, i_id, i; |
1222 | | |
1223 | | /* ETSI 300-743 v1.5.1 section 7.2.5 'Object data segment' |
1224 | | * sync_byte, segment_type, page_id and i_segment_length have already been processed. |
1225 | | */ |
1226 | 0 | i_id = bs_read( s, 16 ); |
1227 | 0 | bs_skip( s, 4 ); /* version */ |
1228 | 0 | i_coding_method = bs_read( s, 2 ); |
1229 | |
|
1230 | 0 | if( i_coding_method > 1 ) |
1231 | 0 | { |
1232 | 0 | msg_Dbg( p_dec, "unknown DVB subtitling coding %d is not handled!", i_coding_method ); |
1233 | 0 | bs_skip( s, 8 * (i_segment_length - 2) - 6 ); |
1234 | 0 | return; |
1235 | 0 | } |
1236 | | |
1237 | | /* Check if the object needs to be rendered in at least one |
1238 | | * of the regions */ |
1239 | 0 | for( p_region = p_sys->p_regions; p_region != NULL; |
1240 | 0 | p_region = p_region->p_next ) |
1241 | 0 | { |
1242 | 0 | for( i = 0; i < p_region->i_object_defs; i++ ) |
1243 | 0 | if( p_region->p_object_defs[i].i_id == i_id ) break; |
1244 | |
|
1245 | 0 | if( i != p_region->i_object_defs ) break; |
1246 | 0 | } |
1247 | 0 | if( !p_region ) |
1248 | 0 | { |
1249 | 0 | bs_skip( s, 8 * (i_segment_length - 2) - 6 ); |
1250 | 0 | return; |
1251 | 0 | } |
1252 | | |
1253 | | #ifdef DEBUG_DVBSUB |
1254 | | msg_Dbg( p_dec, "new object: %i", i_id ); |
1255 | | #endif |
1256 | | |
1257 | 0 | bs_skip( s, 1 ); /* non_modify_color */ |
1258 | 0 | bs_skip( s, 1 ); /* Reserved */ |
1259 | |
|
1260 | 0 | if( i_coding_method == 0x00 ) |
1261 | 0 | { |
1262 | 0 | int i_topfield, i_bottomfield; |
1263 | 0 | uint8_t *p_topfield, *p_bottomfield; |
1264 | |
|
1265 | 0 | i_topfield = bs_read( s, 16 ); |
1266 | 0 | i_bottomfield = bs_read( s, 16 ); |
1267 | 0 | p_topfield = s->p_start + bs_pos( s ) / 8; |
1268 | 0 | p_bottomfield = p_topfield + i_topfield; |
1269 | |
|
1270 | 0 | bs_skip( s, 8 * (i_segment_length - 7) ); |
1271 | | |
1272 | | /* Sanity check */ |
1273 | 0 | if( ( i_segment_length < ( i_topfield + i_bottomfield + 7 ) ) || |
1274 | 0 | ( ( p_topfield + i_topfield + i_bottomfield ) > s->p_end ) ) |
1275 | 0 | { |
1276 | 0 | msg_Dbg( p_dec, "corrupted object data" ); |
1277 | 0 | return; |
1278 | 0 | } |
1279 | | |
1280 | 0 | for( p_region = p_sys->p_regions; p_region != NULL; |
1281 | 0 | p_region = p_region->p_next ) |
1282 | 0 | { |
1283 | 0 | for( i = 0; i < p_region->i_object_defs; i++ ) |
1284 | 0 | { |
1285 | 0 | if( p_region->p_object_defs[i].i_id != i_id ) continue; |
1286 | | |
1287 | 0 | dvbsub_render_pdata( p_dec, p_region, |
1288 | 0 | p_region->p_object_defs[i].i_x, |
1289 | 0 | p_region->p_object_defs[i].i_y, |
1290 | 0 | p_topfield, i_topfield ); |
1291 | |
|
1292 | 0 | if( i_bottomfield ) |
1293 | 0 | { |
1294 | 0 | dvbsub_render_pdata( p_dec, p_region, |
1295 | 0 | p_region->p_object_defs[i].i_x, |
1296 | 0 | p_region->p_object_defs[i].i_y + 1, |
1297 | 0 | p_bottomfield, i_bottomfield ); |
1298 | 0 | } |
1299 | 0 | else |
1300 | 0 | { |
1301 | | /* Duplicate the top field */ |
1302 | 0 | dvbsub_render_pdata( p_dec, p_region, |
1303 | 0 | p_region->p_object_defs[i].i_x, |
1304 | 0 | p_region->p_object_defs[i].i_y + 1, |
1305 | 0 | p_topfield, i_topfield ); |
1306 | 0 | } |
1307 | 0 | } |
1308 | 0 | } |
1309 | 0 | } |
1310 | 0 | else |
1311 | 0 | { |
1312 | | /* DVB subtitling as characters */ |
1313 | 0 | int i_number_of_codes = bs_read( s, 8 ); |
1314 | 0 | uint8_t* p_start = s->p_start + bs_pos( s ) / 8; |
1315 | | |
1316 | | /* Sanity check */ |
1317 | 0 | if( ( i_segment_length < ( i_number_of_codes*2 + 4 ) ) || |
1318 | 0 | ( ( p_start + i_number_of_codes*2 ) > s->p_end ) ) |
1319 | 0 | { |
1320 | 0 | msg_Dbg( p_dec, "corrupted object data" ); |
1321 | 0 | return; |
1322 | 0 | } |
1323 | | |
1324 | 0 | for( p_region = p_sys->p_regions; p_region != NULL; |
1325 | 0 | p_region = p_region->p_next ) |
1326 | 0 | { |
1327 | 0 | for( i = 0; i < p_region->i_object_defs; i++ ) |
1328 | 0 | { |
1329 | 0 | int j; |
1330 | |
|
1331 | 0 | if( p_region->p_object_defs[i].i_id != i_id ) continue; |
1332 | | |
1333 | 0 | p_region->p_object_defs[i].psz_text = |
1334 | 0 | xrealloc( p_region->p_object_defs[i].psz_text, |
1335 | 0 | i_number_of_codes + 1 ); |
1336 | | |
1337 | | /* FIXME 16bits -> char ??? See Preamble */ |
1338 | 0 | for( j = 0; j < i_number_of_codes; j++ ) |
1339 | 0 | { |
1340 | 0 | p_region->p_object_defs[i].psz_text[j] = (char)(bs_read( s, 16 ) & 0xFF); |
1341 | 0 | } |
1342 | | /* Null terminate the string */ |
1343 | 0 | p_region->p_object_defs[i].psz_text[j] = 0; |
1344 | 0 | } |
1345 | 0 | } |
1346 | 0 | } |
1347 | |
|
1348 | | #ifdef DEBUG_DVBSUB |
1349 | | msg_Dbg( p_dec, "end object: %i", i_id ); |
1350 | | #endif |
1351 | 0 | } |
1352 | | |
1353 | | static void dvbsub_render_pdata( decoder_t *p_dec, dvbsub_region_t *p_region, |
1354 | | int i_x, int i_y, |
1355 | | uint8_t *p_field, int i_field ) |
1356 | 0 | { |
1357 | 0 | uint8_t *p_pixbuf; |
1358 | 0 | int i_offset = 0; |
1359 | 0 | bs_t bs; |
1360 | | |
1361 | | /* Sanity check */ |
1362 | 0 | if( !p_region->p_pixbuf ) |
1363 | 0 | { |
1364 | 0 | msg_Err( p_dec, "region %i has no pixel buffer!", p_region->i_id ); |
1365 | 0 | return; |
1366 | 0 | } |
1367 | 0 | if( i_y < 0 || i_x < 0 || i_y >= p_region->i_height || |
1368 | 0 | i_x >= p_region->i_width ) |
1369 | 0 | { |
1370 | 0 | msg_Dbg( p_dec, "invalid offset (%i,%i)", i_x, i_y ); |
1371 | 0 | return; |
1372 | 0 | } |
1373 | | |
1374 | 0 | p_pixbuf = p_region->p_pixbuf + i_y * p_region->i_width; |
1375 | 0 | bs_init( &bs, p_field, i_field ); |
1376 | |
|
1377 | 0 | while( !bs_eof( &bs ) ) |
1378 | 0 | { |
1379 | | /* Sanity check */ |
1380 | 0 | if( i_y >= p_region->i_height ) return; |
1381 | | |
1382 | 0 | switch( bs_read( &bs, 8 ) ) |
1383 | 0 | { |
1384 | 0 | case 0x10: |
1385 | 0 | dvbsub_pdata2bpp( &bs, p_pixbuf + i_x, p_region->i_width - i_x, |
1386 | 0 | &i_offset ); |
1387 | 0 | break; |
1388 | | |
1389 | 0 | case 0x11: |
1390 | 0 | dvbsub_pdata4bpp( &bs, p_pixbuf + i_x, p_region->i_width - i_x, |
1391 | 0 | &i_offset ); |
1392 | 0 | break; |
1393 | | |
1394 | 0 | case 0x12: |
1395 | 0 | dvbsub_pdata8bpp( &bs, p_pixbuf + i_x, p_region->i_width - i_x, |
1396 | 0 | &i_offset ); |
1397 | 0 | break; |
1398 | | |
1399 | 0 | case 0x20: |
1400 | 0 | case 0x21: |
1401 | 0 | case 0x22: |
1402 | | /* We don't use map tables */ |
1403 | 0 | break; |
1404 | | |
1405 | 0 | case 0xf0: /* End of line code */ |
1406 | 0 | p_pixbuf += 2*p_region->i_width; |
1407 | 0 | i_offset = 0; i_y += 2; |
1408 | 0 | break; |
1409 | 0 | } |
1410 | 0 | } |
1411 | 0 | } |
1412 | | |
1413 | | static void dvbsub_pdata2bpp( bs_t *s, uint8_t *p, int i_width, int *pi_off ) |
1414 | 0 | { |
1415 | 0 | bool b_stop = false; |
1416 | |
|
1417 | 0 | while( !b_stop && !bs_eof( s ) ) |
1418 | 0 | { |
1419 | 0 | int i_count = 0, i_color = 0; |
1420 | |
|
1421 | 0 | i_color = bs_read( s, 2 ); |
1422 | 0 | if( i_color != 0x00 ) |
1423 | 0 | { |
1424 | 0 | i_count = 1; |
1425 | 0 | } |
1426 | 0 | else |
1427 | 0 | { |
1428 | 0 | if( bs_read( s, 1 ) == 0x01 ) // Switch1 |
1429 | 0 | { |
1430 | 0 | i_count = 3 + bs_read( s, 3 ); |
1431 | 0 | i_color = bs_read( s, 2 ); |
1432 | 0 | } |
1433 | 0 | else |
1434 | 0 | { |
1435 | 0 | if( bs_read( s, 1 ) == 0x00 ) //Switch2 |
1436 | 0 | { |
1437 | 0 | switch( bs_read( s, 2 ) ) //Switch3 |
1438 | 0 | { |
1439 | 0 | case 0x00: |
1440 | 0 | b_stop = true; |
1441 | 0 | break; |
1442 | 0 | case 0x01: |
1443 | 0 | i_count = 2; |
1444 | 0 | break; |
1445 | 0 | case 0x02: |
1446 | 0 | i_count = 12 + bs_read( s, 4 ); |
1447 | 0 | i_color = bs_read( s, 2 ); |
1448 | 0 | break; |
1449 | 0 | case 0x03: |
1450 | 0 | i_count = 29 + bs_read( s, 8 ); |
1451 | 0 | i_color = bs_read( s, 2 ); |
1452 | 0 | break; |
1453 | 0 | default: |
1454 | 0 | break; |
1455 | 0 | } |
1456 | 0 | } |
1457 | 0 | else |
1458 | 0 | { |
1459 | | /* 1 pixel color 0 */ |
1460 | 0 | i_count = 1; |
1461 | 0 | } |
1462 | 0 | } |
1463 | 0 | } |
1464 | | |
1465 | 0 | if( !i_count ) continue; |
1466 | | |
1467 | | /* Sanity check */ |
1468 | 0 | if( ( i_count + *pi_off ) > i_width ) break; |
1469 | | |
1470 | 0 | if( i_count == 1 ) p[*pi_off] = i_color; |
1471 | 0 | else memset( ( p + *pi_off ), i_color, i_count ); |
1472 | |
|
1473 | 0 | (*pi_off) += i_count; |
1474 | 0 | } |
1475 | | |
1476 | 0 | bs_align( s ); |
1477 | 0 | } |
1478 | | |
1479 | | static void dvbsub_pdata4bpp( bs_t *s, uint8_t *p, int i_width, int *pi_off ) |
1480 | 0 | { |
1481 | 0 | bool b_stop = false; |
1482 | |
|
1483 | 0 | while( !b_stop && !bs_eof( s ) ) |
1484 | 0 | { |
1485 | 0 | int i_count = 0, i_color = 0; |
1486 | |
|
1487 | 0 | i_color = bs_read( s, 4 ); |
1488 | 0 | if( i_color != 0x00 ) |
1489 | 0 | { |
1490 | | /* Add 1 pixel */ |
1491 | 0 | i_count = 1; |
1492 | 0 | } |
1493 | 0 | else |
1494 | 0 | { |
1495 | 0 | if( bs_read( s, 1 ) == 0x00 ) // Switch1 |
1496 | 0 | { |
1497 | 0 | i_count = bs_read( s, 3 ); |
1498 | 0 | if( i_count != 0x00 ) |
1499 | 0 | { |
1500 | 0 | i_count += 2; |
1501 | 0 | } |
1502 | 0 | else b_stop = true; |
1503 | 0 | } |
1504 | 0 | else |
1505 | 0 | { |
1506 | 0 | if( bs_read( s, 1 ) == 0x00) //Switch2 |
1507 | 0 | { |
1508 | 0 | i_count = 4 + bs_read( s, 2 ); |
1509 | 0 | i_color = bs_read( s, 4 ); |
1510 | 0 | } |
1511 | 0 | else |
1512 | 0 | { |
1513 | 0 | switch ( bs_read( s, 2 ) ) //Switch3 |
1514 | 0 | { |
1515 | 0 | case 0x0: |
1516 | 0 | i_count = 1; |
1517 | 0 | break; |
1518 | 0 | case 0x1: |
1519 | 0 | i_count = 2; |
1520 | 0 | break; |
1521 | 0 | case 0x2: |
1522 | 0 | i_count = 9 + bs_read( s, 4 ); |
1523 | 0 | i_color = bs_read( s, 4 ); |
1524 | 0 | break; |
1525 | 0 | case 0x3: |
1526 | 0 | i_count= 25 + bs_read( s, 8 ); |
1527 | 0 | i_color = bs_read( s, 4 ); |
1528 | 0 | break; |
1529 | 0 | } |
1530 | 0 | } |
1531 | 0 | } |
1532 | 0 | } |
1533 | | |
1534 | 0 | if( !i_count ) continue; |
1535 | | |
1536 | | /* Sanity check */ |
1537 | 0 | if( ( i_count + *pi_off ) > i_width ) break; |
1538 | | |
1539 | 0 | if( i_count == 1 ) p[*pi_off] = i_color; |
1540 | 0 | else memset( ( p + *pi_off ), i_color, i_count ); |
1541 | |
|
1542 | 0 | (*pi_off) += i_count; |
1543 | 0 | } |
1544 | | |
1545 | 0 | bs_align( s ); |
1546 | 0 | } |
1547 | | |
1548 | | static void dvbsub_pdata8bpp( bs_t *s, uint8_t *p, int i_width, int *pi_off ) |
1549 | 0 | { |
1550 | 0 | bool b_stop = false; |
1551 | |
|
1552 | 0 | while( !b_stop && !bs_eof( s ) ) |
1553 | 0 | { |
1554 | 0 | int i_count = 0, i_color = 0; |
1555 | |
|
1556 | 0 | i_color = bs_read( s, 8 ); |
1557 | 0 | if( i_color != 0x00 ) |
1558 | 0 | { |
1559 | | /* Add 1 pixel */ |
1560 | 0 | i_count = 1; |
1561 | 0 | } |
1562 | 0 | else |
1563 | 0 | { |
1564 | 0 | if( bs_read( s, 1 ) == 0x00 ) // Switch1 |
1565 | 0 | { |
1566 | 0 | i_count = bs_read( s, 7 ); |
1567 | 0 | if( i_count == 0x00 ) |
1568 | 0 | b_stop = true; |
1569 | 0 | } |
1570 | 0 | else |
1571 | 0 | { |
1572 | 0 | i_count = bs_read( s, 7 ); |
1573 | 0 | i_color = bs_read( s, 8 ); |
1574 | 0 | } |
1575 | 0 | } |
1576 | |
|
1577 | 0 | if( !i_count ) continue; |
1578 | | |
1579 | | /* Sanity check */ |
1580 | 0 | if( ( i_count + *pi_off ) > i_width ) break; |
1581 | | |
1582 | 0 | if( i_count == 1 ) p[*pi_off] = i_color; |
1583 | 0 | else memset( ( p + *pi_off ), i_color, i_count ); |
1584 | |
|
1585 | 0 | (*pi_off) += i_count; |
1586 | 0 | } |
1587 | |
|
1588 | 0 | bs_align( s ); |
1589 | 0 | } |
1590 | | |
1591 | | static void free_all( decoder_t *p_dec ) |
1592 | 1 | { |
1593 | 1 | decoder_sys_t *p_sys = p_dec->p_sys; |
1594 | 1 | dvbsub_region_t *p_reg, *p_reg_next; |
1595 | 1 | dvbsub_clut_t *p_clut, *p_clut_next; |
1596 | | |
1597 | | /*free( p_sys->p_display ); No longer malloced */ |
1598 | | |
1599 | 1 | for( p_clut = p_sys->p_cluts; p_clut != NULL; p_clut = p_clut_next ) |
1600 | 0 | { |
1601 | 0 | p_clut_next = p_clut->p_next; |
1602 | 0 | free( p_clut ); |
1603 | 0 | } |
1604 | 1 | p_sys->p_cluts = NULL; |
1605 | | |
1606 | 1 | for( p_reg = p_sys->p_regions; p_reg != NULL; p_reg = p_reg_next ) |
1607 | 0 | { |
1608 | 0 | p_reg_next = p_reg->p_next; |
1609 | 0 | for( int i = 0; i < p_reg->i_object_defs; i++ ) |
1610 | 0 | free( p_reg->p_object_defs[i].psz_text ); |
1611 | 0 | if( p_reg->i_object_defs ) free( p_reg->p_object_defs ); |
1612 | 0 | free( p_reg->p_pixbuf ); |
1613 | 0 | free( p_reg ); |
1614 | 0 | } |
1615 | 1 | p_sys->p_regions = NULL; |
1616 | | |
1617 | 1 | if( p_sys->p_page ) |
1618 | 0 | { |
1619 | 0 | if( p_sys->p_page->i_region_defs ) |
1620 | 0 | free( p_sys->p_page->p_region_defs ); |
1621 | 0 | free( p_sys->p_page ); |
1622 | 0 | } |
1623 | 1 | p_sys->p_page = NULL; |
1624 | 1 | } |
1625 | | |
1626 | | static subpicture_t *render( decoder_t *p_dec ) |
1627 | 0 | { |
1628 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
1629 | 0 | subpicture_t *p_spu; |
1630 | 0 | int i, j; |
1631 | 0 | int i_base_x; |
1632 | 0 | int i_base_y; |
1633 | |
|
1634 | 0 | if ( p_sys->p_page == NULL) |
1635 | 0 | { |
1636 | 0 | return NULL; |
1637 | 0 | } |
1638 | | |
1639 | | /* Allocate the subpicture internal data. */ |
1640 | 0 | p_spu = decoder_NewSubpicture( p_dec, NULL ); |
1641 | 0 | if( !p_spu ) |
1642 | 0 | return NULL; |
1643 | | |
1644 | | /* Set the pf_render callback */ |
1645 | 0 | p_spu->i_start = p_sys->i_pts; |
1646 | | //p_spu->i_stop = (vlc_tick_t) 0; |
1647 | 0 | p_spu->b_ephemer = true; |
1648 | | //p_spu->b_fade = true; |
1649 | | //p_spu->i_stop = p_spu->i_start + (vlc_tick_t) (i_timeout * 1000000); |
1650 | 0 | p_spu->b_subtitle = true; |
1651 | | |
1652 | | /* Correct positioning of SPU */ |
1653 | 0 | i_base_x = p_sys->i_spu_x; |
1654 | 0 | i_base_y = p_sys->i_spu_y; |
1655 | |
|
1656 | 0 | p_spu->i_original_picture_width = (unsigned)p_sys->display.i_width_minus1 + 1; |
1657 | 0 | p_spu->i_original_picture_height = (unsigned)p_sys->display.i_height_minus1 + 1; |
1658 | |
|
1659 | 0 | if( p_sys->display.b_windowed ) |
1660 | 0 | { |
1661 | | /* From en_300743v01 - */ |
1662 | | /* the DDS is there to indicate intended size/position of text */ |
1663 | | /* the intended video area is ->i_width/height */ |
1664 | | /* the window is within this... SO... we should leave i_original_picture_width etc. as is */ |
1665 | | /* and ONLY change i_base_x. effectively i_max_x/y are only there to limit memory requirements*/ |
1666 | | /* we COULD use the upper limits to limit rendering to within these? */ |
1667 | | |
1668 | | /* see notes on DDS at the top of the file */ |
1669 | 0 | i_base_x += p_sys->display.i_x; |
1670 | 0 | i_base_y += p_sys->display.i_y; |
1671 | 0 | } |
1672 | | |
1673 | | /* Loop on region definitions */ |
1674 | | #ifdef DEBUG_DVBSUB |
1675 | | msg_Dbg( p_dec, "rendering %i regions", p_sys->p_page->i_region_defs ); |
1676 | | #endif |
1677 | |
|
1678 | 0 | for( i = 0; i < p_sys->p_page->i_region_defs; i++ ) |
1679 | 0 | { |
1680 | 0 | dvbsub_region_t *p_region; |
1681 | 0 | dvbsub_regiondef_t *p_regiondef; |
1682 | 0 | dvbsub_clut_t *p_clut; |
1683 | 0 | dvbsub_color_t *p_color; |
1684 | 0 | subpicture_region_t *p_spu_region; |
1685 | 0 | uint8_t *p_src, *p_dst; |
1686 | 0 | video_format_t fmt; |
1687 | 0 | video_palette_t palette; |
1688 | 0 | int i_pitch; |
1689 | |
|
1690 | 0 | p_regiondef = &p_sys->p_page->p_region_defs[i]; |
1691 | | |
1692 | | /* Find associated region */ |
1693 | 0 | for( p_region = p_sys->p_regions; p_region != NULL; |
1694 | 0 | p_region = p_region->p_next ) |
1695 | 0 | { |
1696 | 0 | if( p_regiondef->i_id == p_region->i_id ) break; |
1697 | 0 | } |
1698 | |
|
1699 | | #ifdef DEBUG_DVBSUB |
1700 | | /* if a region exists, then print it's size */ |
1701 | | if (p_region) |
1702 | | { |
1703 | | msg_Dbg( p_dec, "rendering region %i (%i,%i) to (%i,%i)", i, |
1704 | | p_regiondef->i_x, p_regiondef->i_y, |
1705 | | p_regiondef->i_x + p_region->i_width, |
1706 | | p_regiondef->i_y + p_region->i_height ); |
1707 | | } |
1708 | | else |
1709 | | { |
1710 | | msg_Dbg( p_dec, "rendering region %i (%i,%i) (no region matched to render)", i, |
1711 | | p_regiondef->i_x, p_regiondef->i_y ); |
1712 | | } |
1713 | | #endif |
1714 | |
|
1715 | 0 | if( !p_region ) |
1716 | 0 | { |
1717 | 0 | msg_Dbg( p_dec, "region %i not found", p_regiondef->i_id ); |
1718 | 0 | continue; |
1719 | 0 | } |
1720 | | |
1721 | | /* Find associated CLUT */ |
1722 | 0 | for( p_clut = p_sys->p_cluts; p_clut != NULL; p_clut = p_clut->p_next ) |
1723 | 0 | { |
1724 | 0 | if( p_region->i_clut == p_clut->i_id ) break; |
1725 | 0 | } |
1726 | 0 | if( !p_clut ) |
1727 | 0 | { |
1728 | 0 | msg_Dbg( p_dec, "clut %i not found", p_region->i_clut ); |
1729 | 0 | continue; |
1730 | 0 | } |
1731 | | |
1732 | | /* FIXME: don't create a subpicture region with VLC CODEC YUVP |
1733 | | * when it actually is a TEXT region */ |
1734 | | |
1735 | | /* Create new SPU region */ |
1736 | 0 | video_format_Init( &fmt, VLC_CODEC_YUVP ); |
1737 | 0 | fmt.i_sar_num = 0; /* 0 means use aspect ratio of background video */ |
1738 | 0 | fmt.i_sar_den = 1; |
1739 | 0 | fmt.i_width = fmt.i_visible_width = p_region->i_width; |
1740 | 0 | fmt.i_height = fmt.i_visible_height = p_region->i_height; |
1741 | 0 | fmt.i_x_offset = fmt.i_y_offset = 0; |
1742 | 0 | fmt.p_palette = &palette; |
1743 | 0 | fmt.p_palette->i_entries = ( p_region->i_depth == 1 ) ? 4 : |
1744 | 0 | ( ( p_region->i_depth == 2 ) ? 16 : p_clut->c_8b_entries ); |
1745 | 0 | p_color = ( p_region->i_depth == 1 ) ? p_clut->c_2b : |
1746 | 0 | ( ( p_region->i_depth == 2 ) ? p_clut->c_4b : p_clut->c_8b ); |
1747 | 0 | for( j = 0; j < fmt.p_palette->i_entries; j++ ) |
1748 | 0 | { |
1749 | 0 | fmt.p_palette->palette[j][0] = p_color[j].Y; |
1750 | 0 | fmt.p_palette->palette[j][1] = p_color[j].Cb; /* U == Cb */ |
1751 | 0 | fmt.p_palette->palette[j][2] = p_color[j].Cr; /* V == Cr */ |
1752 | 0 | fmt.p_palette->palette[j][3] = 0xff - p_color[j].T; |
1753 | 0 | } |
1754 | 0 | switch (p_clut->dynamic_range_and_colour_gamut) |
1755 | 0 | { |
1756 | 0 | case DVBSUB_ST_COLORIMETRY_CDS: |
1757 | 0 | fmt.space = COLOR_SPACE_BT601; |
1758 | 0 | fmt.primaries = COLOR_PRIMARIES_BT601_525; |
1759 | 0 | fmt.transfer = TRANSFER_FUNC_BT709; |
1760 | 0 | break; |
1761 | 0 | case DVBSUB_ST_COLORIMETRY_SDR_709: |
1762 | 0 | fmt.space = COLOR_SPACE_BT709; |
1763 | 0 | fmt.primaries = COLOR_PRIMARIES_BT709; |
1764 | 0 | fmt.transfer = TRANSFER_FUNC_BT709; |
1765 | 0 | break; |
1766 | 0 | case DVBSUB_ST_COLORIMETRY_SDR_2020: |
1767 | 0 | fmt.space = COLOR_SPACE_BT2020; |
1768 | 0 | fmt.primaries = COLOR_PRIMARIES_BT2020; |
1769 | 0 | fmt.transfer = TRANSFER_FUNC_BT709; |
1770 | 0 | break; |
1771 | 0 | case DVBSUB_ST_COLORIMETRY_HDR_PQ: |
1772 | 0 | fmt.space = COLOR_SPACE_BT2020; |
1773 | 0 | fmt.primaries = COLOR_PRIMARIES_BT2020; |
1774 | 0 | fmt.transfer = TRANSFER_FUNC_SMPTE_ST2084; |
1775 | 0 | break; |
1776 | 0 | case DVBSUB_ST_COLORIMETRY_HDR_HLG: |
1777 | 0 | fmt.space = COLOR_SPACE_BT2020; |
1778 | 0 | fmt.primaries = COLOR_PRIMARIES_BT2020; |
1779 | 0 | fmt.transfer = TRANSFER_FUNC_HLG; |
1780 | 0 | break; |
1781 | 0 | } |
1782 | 0 | fmt.color_range = p_clut->color_range; |
1783 | |
|
1784 | 0 | p_spu_region = subpicture_region_New( &fmt ); |
1785 | 0 | fmt.p_palette = NULL; /* was stack var */ |
1786 | 0 | video_format_Clean( &fmt ); |
1787 | 0 | if( !p_spu_region ) |
1788 | 0 | { |
1789 | 0 | msg_Err( p_dec, "cannot allocate SPU region" ); |
1790 | 0 | continue; |
1791 | 0 | } |
1792 | 0 | p_spu_region->b_absolute = true; p_spu_region->b_in_window = false; |
1793 | 0 | p_spu_region->i_x = i_base_x + p_regiondef->i_x; |
1794 | 0 | p_spu_region->i_y = i_base_y + p_regiondef->i_y; |
1795 | 0 | p_spu_region->i_align = p_sys->i_spu_position; |
1796 | 0 | vlc_spu_regions_push(&p_spu->regions, p_spu_region); |
1797 | |
|
1798 | 0 | p_src = p_region->p_pixbuf; |
1799 | 0 | p_dst = p_spu_region->p_picture->Y_PIXELS; |
1800 | 0 | i_pitch = p_spu_region->p_picture->Y_PITCH; |
1801 | | |
1802 | | /* Copy pixel buffer */ |
1803 | 0 | for( j = 0; j < p_region->i_height; j++ ) |
1804 | 0 | { |
1805 | 0 | memcpy( p_dst, p_src, p_region->i_width ); |
1806 | 0 | p_src += p_region->i_width; |
1807 | 0 | p_dst += i_pitch; |
1808 | 0 | } |
1809 | | |
1810 | | /* Check subtitles encoded as strings of characters |
1811 | | * (since there are not rendered in the pixbuffer) */ |
1812 | 0 | for( j = 0; j < p_region->i_object_defs; j++ ) |
1813 | 0 | { |
1814 | 0 | dvbsub_objectdef_t *p_object_def = &p_region->p_object_defs[j]; |
1815 | |
|
1816 | 0 | if( ( p_object_def->i_type != 1 ) || !p_object_def->psz_text ) |
1817 | 0 | continue; |
1818 | | |
1819 | | /* Create new SPU region */ |
1820 | 0 | p_spu_region = subpicture_region_NewText(); |
1821 | 0 | if( !p_spu_region ) |
1822 | 0 | { |
1823 | 0 | msg_Err( p_dec, "cannot allocate SPU region" ); |
1824 | 0 | continue; |
1825 | 0 | } |
1826 | | |
1827 | 0 | p_spu_region->fmt.i_sar_num = 1; |
1828 | 0 | p_spu_region->fmt.i_sar_den = 1; |
1829 | 0 | p_spu_region->fmt.i_width = p_spu_region->fmt.i_visible_width = p_region->i_width; |
1830 | 0 | p_spu_region->fmt.i_height = p_spu_region->fmt.i_visible_height = p_region->i_height; |
1831 | |
|
1832 | 0 | p_spu_region->p_text = text_segment_New( p_object_def->psz_text ); |
1833 | 0 | p_spu_region->b_absolute = true; p_spu_region->b_in_window = false; |
1834 | 0 | p_spu_region->i_x = i_base_x + p_regiondef->i_x + p_object_def->i_x; |
1835 | 0 | p_spu_region->i_y = i_base_y + p_regiondef->i_y + p_object_def->i_y; |
1836 | 0 | p_spu_region->i_align = p_sys->i_spu_position; |
1837 | 0 | vlc_spu_regions_push(&p_spu->regions, p_spu_region); |
1838 | 0 | } |
1839 | 0 | } |
1840 | | |
1841 | 0 | return p_spu; |
1842 | 0 | } |
1843 | | |
1844 | | /***************************************************************************** |
1845 | | * encoder_sys_t : encoder descriptor |
1846 | | *****************************************************************************/ |
1847 | | typedef struct encoder_region_t |
1848 | | { |
1849 | | int i_width; |
1850 | | int i_height; |
1851 | | |
1852 | | } encoder_region_t; |
1853 | | |
1854 | | typedef struct |
1855 | | { |
1856 | | unsigned int i_page_ver; |
1857 | | unsigned int i_region_ver; |
1858 | | unsigned int i_clut_ver; |
1859 | | |
1860 | | size_t i_regions; |
1861 | | encoder_region_t *p_regions; |
1862 | | |
1863 | | vlc_tick_t i_pts; |
1864 | | |
1865 | | /* subpicture positioning */ |
1866 | | int i_offset_x; |
1867 | | int i_offset_y; |
1868 | | } encoder_sys_t; |
1869 | | |
1870 | | #ifdef ENABLE_SOUT |
1871 | | static void encode_page_composition( encoder_t *, bs_t *, const subpicture_t * ); |
1872 | | static void encode_clut( encoder_t *, bs_t *, subpicture_region_t * ); |
1873 | | static void encode_region_composition( encoder_t *, bs_t *, const subpicture_t * ); |
1874 | | static void encode_object( encoder_t *, bs_t *, const subpicture_t * ); |
1875 | | |
1876 | | /***************************************************************************** |
1877 | | * OpenEncoder: probe the encoder and return score |
1878 | | *****************************************************************************/ |
1879 | | static int OpenEncoder( vlc_object_t *p_this ) |
1880 | 0 | { |
1881 | 0 | encoder_t *p_enc = (encoder_t *)p_this; |
1882 | 0 | encoder_sys_t *p_sys; |
1883 | |
|
1884 | 0 | if( ( p_enc->fmt_out.i_codec != VLC_CODEC_DVBS ) && |
1885 | 0 | !p_enc->obj.force ) |
1886 | 0 | { |
1887 | 0 | return VLC_EGENERIC; |
1888 | 0 | } |
1889 | | |
1890 | | /* Allocate the memory needed to store the decoder's structure */ |
1891 | 0 | p_sys = malloc(sizeof(encoder_sys_t)); |
1892 | 0 | if (p_sys == NULL) |
1893 | 0 | return VLC_ENOMEM; |
1894 | 0 | p_enc->p_sys = p_sys; |
1895 | |
|
1896 | 0 | p_enc->fmt_out.i_codec = VLC_CODEC_DVBS; |
1897 | 0 | p_enc->fmt_out.subs.dvb.i_id = 1 << 16 | 1; |
1898 | |
|
1899 | 0 | config_ChainParse( p_enc, ENC_CFG_PREFIX, ppsz_enc_options, p_enc->p_cfg ); |
1900 | |
|
1901 | 0 | p_sys->i_page_ver = 0; |
1902 | 0 | p_sys->i_region_ver = 0; |
1903 | 0 | p_sys->i_clut_ver = 0; |
1904 | 0 | p_sys->i_regions = 0; |
1905 | 0 | p_sys->p_regions = 0; |
1906 | |
|
1907 | 0 | p_sys->i_offset_x = var_CreateGetInteger( p_this, ENC_CFG_PREFIX "x" ); |
1908 | 0 | p_sys->i_offset_y = var_CreateGetInteger( p_this, ENC_CFG_PREFIX "y" ); |
1909 | |
|
1910 | 0 | static const struct vlc_encoder_operations ops = |
1911 | 0 | { |
1912 | 0 | .close = CloseEncoder, |
1913 | 0 | .encode_sub = Encode, |
1914 | 0 | }; |
1915 | 0 | p_enc->ops = &ops; |
1916 | |
|
1917 | 0 | return VLC_SUCCESS; |
1918 | 0 | } |
1919 | | |
1920 | | /* FIXME: this routine is a hack to convert VLC_CODEC_YUVA |
1921 | | * into VLC_CODEC_YUVP |
1922 | | */ |
1923 | | static void YuvaYuvp( subpicture_t *p_subpic ) |
1924 | 0 | { |
1925 | 0 | const subpicture_region_t *p_region; |
1926 | |
|
1927 | 0 | vlc_spu_regions_foreach_const(p_region, &p_subpic->regions) |
1928 | 0 | { |
1929 | 0 | video_format_t *p_fmt = &p_region->p_picture->format; |
1930 | 0 | int i = 0, j = 0, n = 0, p = 0; |
1931 | 0 | int i_max_entries = 256; |
1932 | |
|
1933 | | #ifdef RANDOM_DITHERING |
1934 | | int i_seed = 0xdeadbeef; /* random seed */ |
1935 | | #else |
1936 | 0 | int *pi_delta; |
1937 | 0 | #endif |
1938 | 0 | int i_pixels = p_region->p_picture->p[0].i_visible_lines |
1939 | 0 | * p_region->p_picture->Y_PITCH; |
1940 | 0 | int i_iterator = p_region->p_picture->p[0].i_visible_lines * 3 / 4 |
1941 | 0 | * p_region->p_picture->Y_PITCH |
1942 | 0 | + p_region->p_picture->Y_PITCH * 1 / 3; |
1943 | 0 | int i_tolerance = 0; |
1944 | |
|
1945 | | #ifdef DEBUG_DVBSUB1 |
1946 | | /* p_enc not valid here */ |
1947 | | msg_Dbg( p_enc, "YuvaYuvp: i_pixels=%d, i_iterator=%d", i_pixels, i_iterator ); |
1948 | | #endif |
1949 | 0 | p_fmt->i_chroma = VLC_CODEC_YUVP; |
1950 | 0 | p_fmt->p_palette = (video_palette_t *) malloc( sizeof( video_palette_t ) ); |
1951 | 0 | if( !p_fmt->p_palette ) break; |
1952 | 0 | p_fmt->p_palette->i_entries = 0; |
1953 | | |
1954 | | /* Find best iterator using Euclide’s algorithm */ |
1955 | 0 | for( ; i_iterator > 1 ; i_iterator-- ) |
1956 | 0 | { |
1957 | 0 | int a = i_pixels; |
1958 | 0 | int b = i_iterator; |
1959 | 0 | int c; |
1960 | |
|
1961 | 0 | while( b ) |
1962 | 0 | { |
1963 | 0 | c = a % b; |
1964 | 0 | a = b; |
1965 | 0 | b = c; |
1966 | 0 | } |
1967 | |
|
1968 | 0 | if( a == 1 ) |
1969 | 0 | { |
1970 | 0 | break; |
1971 | 0 | } |
1972 | 0 | } |
1973 | | |
1974 | | /* Count colors, build best palette */ |
1975 | 0 | for( i_tolerance = 0; i_tolerance < 128; i_tolerance++ ) |
1976 | 0 | { |
1977 | 0 | bool b_success = true; |
1978 | 0 | p_fmt->p_palette->i_entries = 0; |
1979 | |
|
1980 | 0 | for( i = 0; i < i_pixels ; ) |
1981 | 0 | { |
1982 | 0 | uint8_t y, u, v, a; |
1983 | 0 | y = p_region->p_picture->Y_PIXELS[i]; |
1984 | 0 | u = p_region->p_picture->U_PIXELS[i]; |
1985 | 0 | v = p_region->p_picture->V_PIXELS[i]; |
1986 | 0 | a = p_region->p_picture->A_PIXELS[i]; |
1987 | 0 | for( j = 0; j < p_fmt->p_palette->i_entries; j++ ) |
1988 | 0 | { |
1989 | 0 | if( abs((int)p_fmt->p_palette->palette[j][0] - (int)y) <= i_tolerance && |
1990 | 0 | abs((int)p_fmt->p_palette->palette[j][1] - (int)u) <= i_tolerance && |
1991 | 0 | abs((int)p_fmt->p_palette->palette[j][2] - (int)v) <= i_tolerance && |
1992 | 0 | abs((int)p_fmt->p_palette->palette[j][3] - (int)a) <= i_tolerance / 2 ) |
1993 | 0 | { |
1994 | 0 | break; |
1995 | 0 | } |
1996 | 0 | } |
1997 | 0 | if( j == p_fmt->p_palette->i_entries ) |
1998 | 0 | { |
1999 | 0 | p_fmt->p_palette->palette[j][0] = y; |
2000 | 0 | p_fmt->p_palette->palette[j][1] = u; |
2001 | 0 | p_fmt->p_palette->palette[j][2] = v; |
2002 | 0 | p_fmt->p_palette->palette[j][3] = a; |
2003 | 0 | p_fmt->p_palette->i_entries++; |
2004 | 0 | } |
2005 | 0 | if( p_fmt->p_palette->i_entries >= i_max_entries ) |
2006 | 0 | { |
2007 | 0 | b_success = false; |
2008 | 0 | break; |
2009 | 0 | } |
2010 | 0 | i += i_iterator; |
2011 | 0 | if( i > i_pixels ) |
2012 | 0 | { |
2013 | 0 | i -= i_pixels; |
2014 | 0 | } |
2015 | 0 | } |
2016 | |
|
2017 | 0 | if( b_success ) |
2018 | 0 | { |
2019 | 0 | break; |
2020 | 0 | } |
2021 | 0 | } |
2022 | |
|
2023 | | #ifdef DEBUG_DVBSUB1 |
2024 | | /* p_enc not valid here */ |
2025 | | msg_Dbg( p_enc, "best palette has %d colors", p_fmt->p_palette->i_entries ); |
2026 | | #endif |
2027 | |
|
2028 | 0 | #ifndef RANDOM_DITHERING |
2029 | 0 | pi_delta = calloc( ( p_region->p_picture->Y_PITCH + 1 ) * 4, sizeof(int) ); |
2030 | 0 | if (unlikely(pi_delta == NULL)) |
2031 | 0 | return; |
2032 | 0 | #endif |
2033 | | |
2034 | | /* Fill image with our new colours */ |
2035 | 0 | for( p = 0; p < p_region->p_picture->p[0].i_visible_lines ; p++ ) |
2036 | 0 | { |
2037 | 0 | int i_ydelta = 0, i_udelta = 0, i_vdelta = 0, i_adelta = 0; |
2038 | |
|
2039 | 0 | for( n = 0; n < p_region->p_picture->Y_PITCH ; n++ ) |
2040 | 0 | { |
2041 | 0 | int i_offset = p * p_region->p_picture->Y_PITCH + n; |
2042 | 0 | int y, u, v, a; |
2043 | 0 | int i_mindist, i_best; |
2044 | |
|
2045 | 0 | y = p_region->p_picture->Y_PIXELS[i_offset]; |
2046 | 0 | u = p_region->p_picture->U_PIXELS[i_offset]; |
2047 | 0 | v = p_region->p_picture->V_PIXELS[i_offset]; |
2048 | 0 | a = p_region->p_picture->A_PIXELS[i_offset]; |
2049 | | |
2050 | | /* Add dithering compensation */ |
2051 | | #ifdef RANDOM_DITHERING |
2052 | | y += ((i_seed & 0xff) - 0x80) * i_tolerance / 0x80; |
2053 | | u += (((i_seed >> 8) & 0xff) - 0x80) * i_tolerance / 0x80; |
2054 | | v += (((i_seed >> 16) & 0xff) - 0x80) * i_tolerance / 0x80; |
2055 | | a += (((i_seed >> 24) & 0xff) - 0x80) * i_tolerance / 0x80; |
2056 | | #else |
2057 | 0 | y += i_ydelta + pi_delta[ n * 4 + 0 ]; |
2058 | 0 | u += i_udelta + pi_delta[ n * 4 + 1 ]; |
2059 | 0 | v += i_vdelta + pi_delta[ n * 4 + 2 ]; |
2060 | 0 | a += i_adelta + pi_delta[ n * 4 + 3 ]; |
2061 | 0 | #endif |
2062 | | |
2063 | | /* Find best colour in palette */ |
2064 | 0 | for( i_mindist = INT_MAX, i_best = 0, j = 0; j < p_fmt->p_palette->i_entries; j++ ) |
2065 | 0 | { |
2066 | 0 | int i_dist = 0; |
2067 | |
|
2068 | 0 | i_dist += abs((int)p_fmt->p_palette->palette[j][0] - y); |
2069 | 0 | i_dist += abs((int)p_fmt->p_palette->palette[j][1] - u); |
2070 | 0 | i_dist += abs((int)p_fmt->p_palette->palette[j][2] - v); |
2071 | 0 | i_dist += 2 * abs((int)p_fmt->p_palette->palette[j][3] - a); |
2072 | |
|
2073 | 0 | if( i_dist < i_mindist ) |
2074 | 0 | { |
2075 | 0 | i_mindist = i_dist; |
2076 | 0 | i_best = j; |
2077 | 0 | } |
2078 | 0 | } |
2079 | | |
2080 | | /* Set pixel to best color */ |
2081 | 0 | p_region->p_picture->Y_PIXELS[i_offset] = i_best; |
2082 | | |
2083 | | /* Update dithering state */ |
2084 | | #ifdef RANDOM_DITHERING |
2085 | | i_seed = (i_seed * 0x1283837) ^ 0x789479 ^ (i_seed >> 13); |
2086 | | #else |
2087 | 0 | i_ydelta = y - (int)p_fmt->p_palette->palette[i_best][0]; |
2088 | 0 | i_udelta = u - (int)p_fmt->p_palette->palette[i_best][1]; |
2089 | 0 | i_vdelta = v - (int)p_fmt->p_palette->palette[i_best][2]; |
2090 | 0 | i_adelta = a - (int)p_fmt->p_palette->palette[i_best][3]; |
2091 | 0 | pi_delta[ n * 4 + 0 ] = i_ydelta * 3 / 8; |
2092 | 0 | pi_delta[ n * 4 + 1 ] = i_udelta * 3 / 8; |
2093 | 0 | pi_delta[ n * 4 + 2 ] = i_vdelta * 3 / 8; |
2094 | 0 | pi_delta[ n * 4 + 3 ] = i_adelta * 3 / 8; |
2095 | 0 | i_ydelta = i_ydelta * 5 / 8; |
2096 | 0 | i_udelta = i_udelta * 5 / 8; |
2097 | 0 | i_vdelta = i_vdelta * 5 / 8; |
2098 | 0 | i_adelta = i_adelta * 5 / 8; |
2099 | 0 | #endif |
2100 | 0 | } |
2101 | 0 | } |
2102 | 0 | #ifndef RANDOM_DITHERING |
2103 | 0 | free( pi_delta ); |
2104 | 0 | #endif |
2105 | | |
2106 | | /* pad palette */ |
2107 | 0 | for( i = p_fmt->p_palette->i_entries; i < i_max_entries; i++ ) |
2108 | 0 | memset(p_fmt->p_palette->palette[i], 0, sizeof(p_fmt->p_palette->palette[i])); |
2109 | 0 | p_fmt->p_palette->i_entries = i_max_entries; |
2110 | | #ifdef DEBUG_DVBSUB1 |
2111 | | /* p_enc not valid here */ |
2112 | | msg_Dbg( p_enc, "best palette has %d colors", p_fmt->p_palette->i_entries ); |
2113 | | #endif |
2114 | 0 | } |
2115 | 0 | } /* End of hack */ |
2116 | | |
2117 | | /**************************************************************************** |
2118 | | * Encode: the whole thing |
2119 | | ****************************************************************************/ |
2120 | | static block_t *Encode( encoder_t *p_enc, subpicture_t *p_subpic ) |
2121 | 0 | { |
2122 | 0 | subpicture_region_t *p_region = NULL; |
2123 | 0 | bs_t bits, *s = &bits; |
2124 | 0 | block_t *p_block; |
2125 | |
|
2126 | 0 | if( !p_subpic || vlc_spu_regions_is_empty(&p_subpic->regions) ) return NULL; |
2127 | | |
2128 | | /* FIXME: this is a hack to convert VLC_CODEC_YUVA into |
2129 | | * VLC_CODEC_YUVP |
2130 | | */ |
2131 | 0 | p_region = vlc_spu_regions_first_or_null(&p_subpic->regions); |
2132 | | /* Sanity check */ |
2133 | 0 | if( !p_region ) return NULL; |
2134 | | |
2135 | 0 | if( !subpicture_region_IsText( p_region ) ) |
2136 | 0 | { |
2137 | 0 | if( p_region->p_picture->format.i_chroma == VLC_CODEC_YUVA ) |
2138 | 0 | { |
2139 | 0 | YuvaYuvp( p_subpic ); |
2140 | 0 | } |
2141 | |
|
2142 | 0 | if( p_region->p_picture->format.i_chroma != VLC_CODEC_YUVP ) |
2143 | 0 | { |
2144 | 0 | msg_Err( p_enc, "chroma %4.4s not supported", (char *)&p_region->p_picture->format.i_chroma ); |
2145 | 0 | return NULL; |
2146 | 0 | } |
2147 | | |
2148 | 0 | if( p_region->p_picture->format.p_palette ) |
2149 | 0 | { |
2150 | 0 | switch( p_region->p_picture->format.p_palette->i_entries ) |
2151 | 0 | { |
2152 | 0 | case 0: |
2153 | 0 | case 4: |
2154 | 0 | case 16: |
2155 | 0 | case 256: |
2156 | 0 | break; |
2157 | 0 | default: |
2158 | 0 | msg_Err( p_enc, "subpicture palette (%d) not handled", |
2159 | 0 | p_region->p_picture->format.p_palette->i_entries ); |
2160 | 0 | return NULL; |
2161 | 0 | } |
2162 | 0 | } |
2163 | 0 | } |
2164 | | /* End of hack */ |
2165 | | |
2166 | | #ifdef DEBUG_DVBSUB |
2167 | | msg_Dbg( p_enc, "encoding subpicture" ); |
2168 | | #endif |
2169 | 0 | p_block = block_Alloc( 64000 ); |
2170 | 0 | if( unlikely(p_block == NULL) ) |
2171 | 0 | return NULL; |
2172 | | |
2173 | 0 | bs_init( s, p_block->p_buffer, p_block->i_buffer ); |
2174 | |
|
2175 | 0 | bs_write( s, 8, 0x20 ); /* Data identifier */ |
2176 | 0 | bs_write( s, 8, 0x0 ); /* Subtitle stream id */ |
2177 | |
|
2178 | 0 | encode_page_composition( p_enc, s, p_subpic ); |
2179 | 0 | encode_region_composition( p_enc, s, p_subpic ); |
2180 | 0 | encode_clut( p_enc, s, p_region ); |
2181 | 0 | encode_object( p_enc, s, p_subpic ); |
2182 | | |
2183 | | /* End of display */ |
2184 | 0 | bs_write( s, 8, 0x0f ); /* Sync byte */ |
2185 | 0 | bs_write( s, 8, DVBSUB_ST_ENDOFDISPLAY ); /* Segment type */ |
2186 | 0 | bs_write( s, 16, 1 ); /* Page id */ |
2187 | 0 | bs_write( s, 16, 0 ); /* Segment length */ |
2188 | |
|
2189 | 0 | bs_write( s, 8, 0xff );/* End marker */ |
2190 | 0 | p_block->i_buffer = bs_pos( s ) / 8; |
2191 | 0 | p_block->i_pts = p_block->i_dts = p_subpic->i_start; |
2192 | 0 | if( !p_subpic->b_ephemer && |
2193 | 0 | ( p_subpic->i_stop != VLC_TICK_INVALID && p_subpic->i_stop > p_subpic->i_start ) ) |
2194 | 0 | { |
2195 | 0 | block_t *p_block_stop; |
2196 | |
|
2197 | 0 | p_block->i_length = p_subpic->i_stop - p_subpic->i_start; |
2198 | | |
2199 | | /* Send another (empty) subtitle to signal the end of display */ |
2200 | 0 | p_block_stop = block_Alloc( 64000 ); |
2201 | 0 | if( unlikely(p_block_stop == NULL) ) |
2202 | 0 | { |
2203 | 0 | block_Release(p_block); |
2204 | 0 | return NULL; |
2205 | 0 | } |
2206 | | |
2207 | 0 | bs_init( s, p_block_stop->p_buffer, p_block_stop->i_buffer ); |
2208 | 0 | bs_write( s, 8, 0x20 ); /* Data identifier */ |
2209 | 0 | bs_write( s, 8, 0x0 ); /* Subtitle stream id */ |
2210 | 0 | encode_page_composition( p_enc, s, 0 ); |
2211 | 0 | bs_write( s, 8, 0x0f ); /* Sync byte */ |
2212 | 0 | bs_write( s, 8, DVBSUB_ST_ENDOFDISPLAY ); /* Segment type */ |
2213 | 0 | bs_write( s, 16, 1 ); /* Page id */ |
2214 | 0 | bs_write( s, 16, 0 ); /* Segment length */ |
2215 | 0 | bs_write( s, 8, 0xff );/* End marker */ |
2216 | 0 | p_block_stop->i_buffer = bs_pos( s ) / 8; |
2217 | 0 | p_block_stop->i_pts = p_block_stop->i_dts = p_subpic->i_stop; |
2218 | 0 | block_ChainAppend( &p_block, p_block_stop ); |
2219 | 0 | p_block_stop->i_length = VLC_TICK_FROM_MS(100); /* p_subpic->i_stop - p_subpic->i_start; */ |
2220 | 0 | } |
2221 | | #ifdef DEBUG_DVBSUB |
2222 | | msg_Dbg( p_enc, "subpicture encoded properly" ); |
2223 | | #endif |
2224 | 0 | return p_block; |
2225 | 0 | } |
2226 | | |
2227 | | /***************************************************************************** |
2228 | | * CloseEncoder: encoder destruction |
2229 | | *****************************************************************************/ |
2230 | | static void CloseEncoder( encoder_t *p_enc ) |
2231 | 0 | { |
2232 | 0 | encoder_sys_t *p_sys = p_enc->p_sys; |
2233 | |
|
2234 | 0 | var_Destroy( p_enc , ENC_CFG_PREFIX "x" ); |
2235 | 0 | var_Destroy( p_enc , ENC_CFG_PREFIX "y" ); |
2236 | |
|
2237 | 0 | if( p_sys->i_regions ) free( p_sys->p_regions ); |
2238 | 0 | free( p_sys ); |
2239 | 0 | } |
2240 | | |
2241 | | static void encode_page_composition( encoder_t *p_enc, bs_t *s, |
2242 | | const subpicture_t *p_subpic ) |
2243 | 0 | { |
2244 | 0 | encoder_sys_t *p_sys = p_enc->p_sys; |
2245 | 0 | const subpicture_region_t *p_region; |
2246 | 0 | bool b_mode_change = false; |
2247 | 0 | unsigned int i_regions; |
2248 | 0 | int i_timeout; |
2249 | |
|
2250 | 0 | bs_write( s, 8, 0x0f ); /* Sync byte */ |
2251 | 0 | bs_write( s, 8, DVBSUB_ST_PAGE_COMPOSITION ); /* Segment type */ |
2252 | 0 | bs_write( s, 16, 1 ); /* Page id */ |
2253 | |
|
2254 | 0 | i_regions = 0; |
2255 | 0 | if (p_subpic) |
2256 | 0 | vlc_spu_regions_foreach_const(p_region, &p_subpic->regions) |
2257 | 0 | { |
2258 | 0 | if( i_regions >= p_sys->i_regions ) |
2259 | 0 | { |
2260 | 0 | encoder_region_t region; |
2261 | 0 | region.i_width = region.i_height = 0; |
2262 | 0 | p_sys->p_regions = xrealloc( p_sys->p_regions, |
2263 | 0 | sizeof(encoder_region_t) * (p_sys->i_regions + 1) ); |
2264 | 0 | p_sys->p_regions[p_sys->i_regions++] = region; |
2265 | 0 | } |
2266 | |
|
2267 | 0 | if( ( p_sys->p_regions[i_regions].i_width < |
2268 | 0 | (int)p_region->fmt.i_visible_width ) || |
2269 | 0 | ( p_sys->p_regions[i_regions].i_width > |
2270 | 0 | (int)p_region->fmt.i_visible_width ) ) |
2271 | 0 | { |
2272 | 0 | b_mode_change = true; |
2273 | 0 | msg_Dbg( p_enc, "region %u width change: %i -> %i", |
2274 | 0 | i_regions, p_sys->p_regions[i_regions].i_width, |
2275 | 0 | p_region->fmt.i_visible_width ); |
2276 | 0 | p_sys->p_regions[i_regions].i_width = |
2277 | 0 | p_region->fmt.i_visible_width; |
2278 | 0 | } |
2279 | 0 | if( p_sys->p_regions[i_regions].i_height < |
2280 | 0 | (int)p_region->fmt.i_visible_height ) |
2281 | 0 | { |
2282 | 0 | b_mode_change = true; |
2283 | 0 | msg_Dbg( p_enc, "region %u height change: %i -> %i", |
2284 | 0 | i_regions, p_sys->p_regions[i_regions].i_height, |
2285 | 0 | p_region->fmt.i_visible_height ); |
2286 | 0 | p_sys->p_regions[i_regions].i_height = |
2287 | 0 | p_region->fmt.i_visible_height; |
2288 | 0 | } |
2289 | 0 | i_regions++; |
2290 | 0 | } |
2291 | |
|
2292 | 0 | bs_write( s, 16, i_regions * 6 + 2 ); /* Segment length */ |
2293 | |
|
2294 | 0 | i_timeout = 0; |
2295 | 0 | if( p_subpic && !p_subpic->b_ephemer && |
2296 | 0 | ( p_subpic->i_stop != VLC_TICK_INVALID && p_subpic->i_stop > p_subpic->i_start ) ) |
2297 | 0 | { |
2298 | 0 | i_timeout = SEC_FROM_VLC_TICK(p_subpic->i_stop - p_subpic->i_start); |
2299 | 0 | } |
2300 | |
|
2301 | 0 | bs_write( s, 8, i_timeout ); /* Timeout */ |
2302 | 0 | bs_write( s, 4, p_sys->i_page_ver++ ); |
2303 | 0 | bs_write( s, 2, b_mode_change ? |
2304 | 0 | DVBSUB_PCS_STATE_CHANGE : DVBSUB_PCS_STATE_ACQUISITION ); |
2305 | 0 | bs_write( s, 2, 0 ); /* Reserved */ |
2306 | |
|
2307 | 0 | i_regions = 0; |
2308 | 0 | if (p_subpic) |
2309 | 0 | vlc_spu_regions_foreach_const(p_region, &p_subpic->regions) |
2310 | 0 | { |
2311 | 0 | bs_write( s, 8, i_regions ); |
2312 | 0 | bs_write( s, 8, 0 ); /* Reserved */ |
2313 | 0 | if( (p_sys->i_offset_x > 0) && (p_sys->i_offset_y > 0) ) |
2314 | 0 | { |
2315 | 0 | bs_write( s, 16, p_sys->i_offset_x ); /* override x position */ |
2316 | 0 | bs_write( s, 16, p_sys->i_offset_y ); /* override y position */ |
2317 | 0 | } |
2318 | 0 | else |
2319 | 0 | { |
2320 | 0 | bs_write( s, 16, p_region->i_x ); |
2321 | 0 | bs_write( s, 16, p_region->i_y ); |
2322 | 0 | } |
2323 | 0 | i_regions++; |
2324 | 0 | } |
2325 | 0 | } |
2326 | | |
2327 | | static void encode_clut( encoder_t *p_enc, bs_t *s, subpicture_region_t *p_region ) |
2328 | 0 | { |
2329 | 0 | encoder_sys_t *p_sys = p_enc->p_sys; |
2330 | 0 | video_palette_t *p_pal, empty_palette = { .i_entries = 4 }; |
2331 | | |
2332 | | /* Sanity check */ |
2333 | 0 | if( !p_region ) return; |
2334 | | |
2335 | 0 | if( !subpicture_region_IsText( p_region ) && p_region->p_picture->format.i_chroma == VLC_CODEC_YUVP ) |
2336 | 0 | { |
2337 | 0 | p_pal = p_region->p_picture->format.p_palette; |
2338 | 0 | } |
2339 | 0 | else |
2340 | 0 | p_pal = &empty_palette; |
2341 | |
|
2342 | 0 | bs_write( s, 8, 0x0f ); /* Sync byte */ |
2343 | 0 | bs_write( s, 8, DVBSUB_ST_CLUT_DEFINITION ); /* Segment type */ |
2344 | 0 | bs_write( s, 16, 1 ); /* Page id */ |
2345 | |
|
2346 | 0 | bs_write( s, 16, p_pal->i_entries * 6 + 2 ); /* Segment length */ |
2347 | 0 | bs_write( s, 8, 1 ); /* Clut id */ |
2348 | 0 | bs_write( s, 4, p_sys->i_clut_ver++ ); |
2349 | 0 | bs_write( s, 4, 0 ); /* Reserved */ |
2350 | |
|
2351 | 0 | for( int i = 0; i < p_pal->i_entries; i++ ) |
2352 | 0 | { |
2353 | 0 | bs_write( s, 8, i ); /* Clut entry id */ |
2354 | 0 | bs_write( s, 1, p_pal->i_entries == 4 ); /* 2bit/entry flag */ |
2355 | 0 | bs_write( s, 1, p_pal->i_entries == 16 ); /* 4bit/entry flag */ |
2356 | 0 | bs_write( s, 1, p_pal->i_entries == 256 ); /* 8bit/entry flag */ |
2357 | 0 | bs_write( s, 4, 0 ); /* Reserved */ |
2358 | 0 | bs_write( s, 1, 1 ); /* Full range flag */ |
2359 | 0 | bs_write( s, 8, p_pal->palette[i][3] ? /* Y value */ |
2360 | 0 | (p_pal->palette[i][0] ? p_pal->palette[i][0] : 16) : 0 ); |
2361 | 0 | bs_write( s, 8, p_pal->palette[i][1] ); /* Cr value */ |
2362 | 0 | bs_write( s, 8, p_pal->palette[i][2] ); /* Cb value */ |
2363 | 0 | bs_write( s, 8, 0xff - p_pal->palette[i][3] ); /* T value */ |
2364 | 0 | } |
2365 | 0 | } |
2366 | | |
2367 | | static void encode_region_composition( encoder_t *p_enc, bs_t *s, |
2368 | | const subpicture_t *p_subpic ) |
2369 | 0 | { |
2370 | 0 | encoder_sys_t *p_sys = p_enc->p_sys; |
2371 | 0 | const subpicture_region_t *p_region; |
2372 | 0 | unsigned int i_region; |
2373 | |
|
2374 | 0 | i_region = 0; |
2375 | 0 | vlc_spu_regions_foreach_const(p_region, &p_subpic->regions) |
2376 | 0 | { |
2377 | 0 | int i_entries = 4, i_depth = 0x1, i_bg = 0; |
2378 | 0 | bool b_text = subpicture_region_IsText( p_region ); |
2379 | |
|
2380 | 0 | if( !b_text ) |
2381 | 0 | { |
2382 | 0 | video_palette_t *p_pal = p_region->p_picture->format.p_palette; |
2383 | |
|
2384 | 0 | if( !p_pal ) |
2385 | 0 | { |
2386 | 0 | msg_Err( p_enc, "subpicture has no palette - ignoring it" ); |
2387 | 0 | break; |
2388 | 0 | } |
2389 | | |
2390 | 0 | i_entries = p_pal->i_entries; |
2391 | 0 | i_depth = i_entries == 4 ? 0x1 : i_entries == 16 ? 0x2 : 0x3; |
2392 | |
|
2393 | 0 | for( i_bg = 0; i_bg < p_pal->i_entries; i_bg++ ) |
2394 | 0 | { |
2395 | 0 | if( !p_pal->palette[i_bg][3] ) break; |
2396 | 0 | } |
2397 | 0 | } |
2398 | | |
2399 | 0 | bs_write( s, 8, 0x0f ); /* Sync byte */ |
2400 | 0 | bs_write( s, 8, DVBSUB_ST_REGION_COMPOSITION ); /* Segment type */ |
2401 | 0 | bs_write( s, 16, 1 ); /* Page id */ |
2402 | |
|
2403 | 0 | bs_write( s, 16, 10 + 6 + (b_text ? 2 : 0) ); /* Segment length */ |
2404 | 0 | bs_write( s, 8, i_region ); |
2405 | 0 | bs_write( s, 4, p_sys->i_region_ver++ ); |
2406 | | |
2407 | | /* Region attributes */ |
2408 | 0 | bs_write( s, 1, i_bg < i_entries ); /* Fill */ |
2409 | 0 | bs_write( s, 3, 0 ); /* Reserved */ |
2410 | 0 | bs_write( s, 16, p_sys->p_regions[i_region].i_width ); |
2411 | 0 | bs_write( s, 16, p_sys->p_regions[i_region].i_height ); |
2412 | 0 | bs_write( s, 3, i_depth ); /* Region level of compatibility */ |
2413 | 0 | bs_write( s, 3, i_depth ); /* Region depth */ |
2414 | 0 | bs_write( s, 2, 0 ); /* Reserved */ |
2415 | 0 | bs_write( s, 8, 1 ); /* Clut id */ |
2416 | 0 | bs_write( s, 8, i_bg ); /* region 8bit pixel code */ |
2417 | 0 | bs_write( s, 4, i_bg ); /* region 4bit pixel code */ |
2418 | 0 | bs_write( s, 2, i_bg ); /* region 2bit pixel code */ |
2419 | 0 | bs_write( s, 2, 0 ); /* Reserved */ |
2420 | | |
2421 | | /* In our implementation we only have 1 object per region */ |
2422 | 0 | bs_write( s, 16, i_region ); |
2423 | 0 | bs_write( s, 2, b_text ? DVBSUB_OT_BASIC_CHAR:DVBSUB_OT_BASIC_BITMAP ); |
2424 | 0 | bs_write( s, 2, 0 ); /* object provider flag */ |
2425 | 0 | bs_write( s, 12, 0 );/* object horizontal position */ |
2426 | 0 | bs_write( s, 4, 0 ); /* Reserved */ |
2427 | 0 | bs_write( s, 12, 0 );/* object vertical position */ |
2428 | |
|
2429 | 0 | if( b_text ) |
2430 | 0 | { |
2431 | 0 | bs_write( s, 8, 1 ); /* foreground pixel code */ |
2432 | 0 | bs_write( s, 8, 0 ); /* background pixel code */ |
2433 | 0 | } |
2434 | 0 | i_region++; |
2435 | 0 | } |
2436 | 0 | } |
2437 | | |
2438 | | static void encode_pixel_data( encoder_t *p_enc, bs_t *s, |
2439 | | const subpicture_region_t *p_region, |
2440 | | bool b_top ); |
2441 | | |
2442 | | static void encode_object( encoder_t *p_enc, bs_t *s, const subpicture_t *p_subpic ) |
2443 | 0 | { |
2444 | 0 | encoder_sys_t *p_sys = p_enc->p_sys; |
2445 | 0 | const subpicture_region_t *p_region; |
2446 | 0 | int i_region; |
2447 | |
|
2448 | 0 | int i_length_pos, i_update_pos, i_pixel_data_pos; |
2449 | |
|
2450 | 0 | i_region = 0; |
2451 | 0 | vlc_spu_regions_foreach_const(p_region, &p_subpic->regions) |
2452 | 0 | { |
2453 | 0 | bs_write( s, 8, 0x0f ); /* Sync byte */ |
2454 | 0 | bs_write( s, 8, DVBSUB_ST_OBJECT_DATA ); /* Segment type */ |
2455 | 0 | bs_write( s, 16, 1 ); /* Page id */ |
2456 | |
|
2457 | 0 | i_length_pos = bs_pos( s ); |
2458 | 0 | bs_write( s, 16, 0 ); /* Segment length */ |
2459 | 0 | bs_write( s, 16, i_region ); /* Object id */ |
2460 | 0 | bs_write( s, 4, p_sys->i_region_ver++ ); |
2461 | | |
2462 | | /* object coding method */ |
2463 | 0 | if (subpicture_region_IsText( p_region )) |
2464 | 0 | bs_write( s, 2, 1 ); |
2465 | 0 | else if ( p_region->p_picture->format.i_chroma == VLC_CODEC_YUVP ) |
2466 | 0 | bs_write( s, 2, 0 ); |
2467 | 0 | else |
2468 | 0 | { |
2469 | 0 | msg_Err( p_enc, "FOURCC %4.4s not supported by encoder.", |
2470 | 0 | (const char*)&p_region->p_picture->format.i_chroma ); |
2471 | 0 | i_region++; |
2472 | 0 | continue; |
2473 | 0 | } |
2474 | | |
2475 | 0 | bs_write( s, 1, 0 ); /* non modifying color flag */ |
2476 | 0 | bs_write( s, 1, 0 ); /* Reserved */ |
2477 | |
|
2478 | 0 | if(subpicture_region_IsText( p_region )) |
2479 | 0 | { |
2480 | 0 | int i_size, i; |
2481 | |
|
2482 | 0 | if( !p_region->p_text ) |
2483 | 0 | { |
2484 | 0 | i_region++; |
2485 | 0 | continue; |
2486 | 0 | } |
2487 | | |
2488 | 0 | i_size = __MIN( strlen( p_region->p_text->psz_text ), 256 ); |
2489 | |
|
2490 | 0 | bs_write( s, 8, i_size ); /* number of characters in string */ |
2491 | 0 | for( i = 0; i < i_size; i++ ) |
2492 | 0 | { |
2493 | 0 | bs_write( s, 16, p_region->p_text->psz_text[i] ); |
2494 | 0 | } |
2495 | | |
2496 | | /* Update segment length */ |
2497 | 0 | SetWBE( &s->p_start[i_length_pos/8], |
2498 | 0 | (bs_pos(s) - i_length_pos)/8 -2 ); |
2499 | 0 | i_region++; |
2500 | 0 | continue; |
2501 | 0 | } |
2502 | | |
2503 | | /* Coding of a bitmap object */ |
2504 | 0 | i_update_pos = bs_pos( s ); |
2505 | 0 | bs_write( s, 16, 0 ); /* topfield data block length */ |
2506 | 0 | bs_write( s, 16, 0 ); /* bottomfield data block length */ |
2507 | | |
2508 | | /* Top field */ |
2509 | 0 | i_pixel_data_pos = bs_pos( s ); |
2510 | 0 | encode_pixel_data( p_enc, s, p_region, true ); |
2511 | 0 | i_pixel_data_pos = ( bs_pos( s ) - i_pixel_data_pos ) / 8; |
2512 | 0 | SetWBE( &s->p_start[i_update_pos/8], i_pixel_data_pos ); |
2513 | | |
2514 | | /* Bottom field */ |
2515 | 0 | i_pixel_data_pos = bs_pos( s ); |
2516 | 0 | encode_pixel_data( p_enc, s, p_region, false ); |
2517 | 0 | i_pixel_data_pos = ( bs_pos( s ) - i_pixel_data_pos ) / 8; |
2518 | 0 | SetWBE( &s->p_start[i_update_pos/8+2], i_pixel_data_pos ); |
2519 | | |
2520 | | /* Stuffing for word alignment */ |
2521 | 0 | bs_align_0( s ); |
2522 | 0 | if( bs_pos( s ) % 16 ) bs_write( s, 8, 0 ); |
2523 | | |
2524 | | /* Update segment length */ |
2525 | 0 | SetWBE( &s->p_start[i_length_pos/8], (bs_pos(s) - i_length_pos)/8 -2 ); |
2526 | 0 | i_region++; |
2527 | 0 | } |
2528 | 0 | } |
2529 | | |
2530 | | static void encode_pixel_line_2bp( bs_t *s, const subpicture_region_t *p_region, |
2531 | | int i_line ); |
2532 | | static void encode_pixel_line_4bp( bs_t *s, const subpicture_region_t *p_region, |
2533 | | int i_line ); |
2534 | | static void encode_pixel_line_8bp( bs_t *s, const subpicture_region_t *p_region, |
2535 | | int i_line ); |
2536 | | static void encode_pixel_data( encoder_t *p_enc, bs_t *s, |
2537 | | const subpicture_region_t *p_region, |
2538 | | bool b_top ) |
2539 | 0 | { |
2540 | 0 | unsigned int i_line; |
2541 | | |
2542 | | /* Encode line by line */ |
2543 | 0 | for( i_line = !b_top; i_line < p_region->fmt.i_visible_height; |
2544 | 0 | i_line += 2 ) |
2545 | 0 | { |
2546 | 0 | switch( p_region->p_picture->format.p_palette->i_entries ) |
2547 | 0 | { |
2548 | 0 | case 0: |
2549 | 0 | break; |
2550 | | |
2551 | 0 | case 4: |
2552 | 0 | bs_write( s, 8, 0x10 ); /* 2 bit/pixel code string */ |
2553 | 0 | encode_pixel_line_2bp( s, p_region, i_line ); |
2554 | 0 | break; |
2555 | | |
2556 | 0 | case 16: |
2557 | 0 | bs_write( s, 8, 0x11 ); /* 4 bit/pixel code string */ |
2558 | 0 | encode_pixel_line_4bp( s, p_region, i_line ); |
2559 | 0 | break; |
2560 | | |
2561 | 0 | case 256: |
2562 | 0 | bs_write( s, 8, 0x12 ); /* 8 bit/pixel code string */ |
2563 | 0 | encode_pixel_line_8bp( s, p_region, i_line ); |
2564 | 0 | break; |
2565 | | |
2566 | 0 | default: |
2567 | 0 | msg_Err( p_enc, "subpicture palette (%i) not handled", |
2568 | 0 | p_region->p_picture->format.p_palette->i_entries ); |
2569 | 0 | break; |
2570 | 0 | } |
2571 | | |
2572 | 0 | bs_write( s, 8, 0xf0 ); /* End of object line code */ |
2573 | 0 | } |
2574 | 0 | } |
2575 | | |
2576 | | static void encode_pixel_line_2bp( bs_t *s, const subpicture_region_t *p_region, |
2577 | | int i_line ) |
2578 | 0 | { |
2579 | 0 | unsigned int i, i_length = 0; |
2580 | 0 | int i_pitch = p_region->p_picture->p->i_pitch; |
2581 | 0 | uint8_t *p_data = &p_region->p_picture->p->p_pixels[ i_pitch * i_line ]; |
2582 | 0 | int i_last_pixel = p_data[0]; |
2583 | |
|
2584 | 0 | for( i = 0; i <= p_region->fmt.i_visible_width; i++ ) |
2585 | 0 | { |
2586 | 0 | if( ( i != p_region->fmt.i_visible_width ) && |
2587 | 0 | ( p_data[i] == i_last_pixel ) && ( i_length != 284 ) ) |
2588 | 0 | { |
2589 | 0 | i_length++; |
2590 | 0 | continue; |
2591 | 0 | } |
2592 | | |
2593 | 0 | if( ( i_length == 1 ) || ( i_length == 11 ) || ( i_length == 28 ) ) |
2594 | 0 | { |
2595 | | /* 2bit/pixel code */ |
2596 | 0 | if( i_last_pixel ) |
2597 | 0 | bs_write( s, 2, i_last_pixel ); |
2598 | 0 | else |
2599 | 0 | { |
2600 | 0 | bs_write( s, 2, 0 ); |
2601 | 0 | bs_write( s, 1, 0 ); |
2602 | 0 | bs_write( s, 1, 1 ); /* pseudo color 0 */ |
2603 | 0 | } |
2604 | 0 | i_length--; |
2605 | 0 | } |
2606 | |
|
2607 | 0 | if( i_length == 2 ) |
2608 | 0 | { |
2609 | 0 | if( i_last_pixel ) |
2610 | 0 | { |
2611 | 0 | bs_write( s, 2, i_last_pixel ); |
2612 | 0 | bs_write( s, 2, i_last_pixel ); |
2613 | 0 | } |
2614 | 0 | else |
2615 | 0 | { |
2616 | 0 | bs_write( s, 2, 0 ); |
2617 | 0 | bs_write( s, 1, 0 ); |
2618 | 0 | bs_write( s, 1, 0 ); |
2619 | 0 | bs_write( s, 2, 1 ); /* 2 * pseudo color 0 */ |
2620 | 0 | } |
2621 | 0 | } |
2622 | 0 | else if( i_length > 2 ) |
2623 | 0 | { |
2624 | 0 | bs_write( s, 2, 0 ); |
2625 | 0 | if( i_length <= 10 ) |
2626 | 0 | { |
2627 | 0 | bs_write( s, 1, 1 ); |
2628 | 0 | bs_write( s, 3, i_length - 3 ); |
2629 | 0 | bs_write( s, 2, i_last_pixel ); |
2630 | 0 | } |
2631 | 0 | else |
2632 | 0 | { |
2633 | 0 | bs_write( s, 1, 0 ); |
2634 | 0 | bs_write( s, 1, 0 ); |
2635 | |
|
2636 | 0 | if( i_length <= 27 ) |
2637 | 0 | { |
2638 | 0 | bs_write( s, 2, 2 ); |
2639 | 0 | bs_write( s, 4, i_length - 12 ); |
2640 | 0 | bs_write( s, 2, i_last_pixel ); |
2641 | 0 | } |
2642 | 0 | else |
2643 | 0 | { |
2644 | 0 | bs_write( s, 2, 3 ); |
2645 | 0 | bs_write( s, 8, i_length - 29 ); |
2646 | 0 | bs_write( s, 2, i_last_pixel ); |
2647 | 0 | } |
2648 | 0 | } |
2649 | 0 | } |
2650 | |
|
2651 | 0 | if( i == p_region->fmt.i_visible_width ) break; |
2652 | | |
2653 | 0 | i_last_pixel = p_data[i]; |
2654 | 0 | i_length = 1; |
2655 | 0 | } |
2656 | | |
2657 | | /* Stop */ |
2658 | 0 | bs_write( s, 2, 0 ); |
2659 | 0 | bs_write( s, 1, 0 ); |
2660 | 0 | bs_write( s, 1, 0 ); |
2661 | 0 | bs_write( s, 2, 0 ); |
2662 | | |
2663 | | /* Stuffing */ |
2664 | 0 | bs_align_0( s ); |
2665 | 0 | } |
2666 | | |
2667 | | static void encode_pixel_line_4bp( bs_t *s, const subpicture_region_t *p_region, |
2668 | | int i_line ) |
2669 | 0 | { |
2670 | 0 | unsigned int i, i_length = 0; |
2671 | 0 | int i_pitch = p_region->p_picture->p->i_pitch; |
2672 | 0 | uint8_t *p_data = &p_region->p_picture->p->p_pixels[ i_pitch * i_line ]; |
2673 | 0 | int i_last_pixel = p_data[0]; |
2674 | |
|
2675 | 0 | for( i = 0; i <= p_region->fmt.i_visible_width; i++ ) |
2676 | 0 | { |
2677 | 0 | if( i != p_region->fmt.i_visible_width && |
2678 | 0 | p_data[i] == i_last_pixel && i_length != 280 ) |
2679 | 0 | { |
2680 | 0 | i_length++; |
2681 | 0 | continue; |
2682 | 0 | } |
2683 | | |
2684 | 0 | if( ( i_length == 1 ) || |
2685 | 0 | ( ( i_length == 3 ) && i_last_pixel ) || |
2686 | 0 | ( i_length == 8 ) ) |
2687 | 0 | { |
2688 | | /* 4bit/pixel code */ |
2689 | 0 | if( i_last_pixel ) |
2690 | 0 | bs_write( s, 4, i_last_pixel ); |
2691 | 0 | else |
2692 | 0 | { |
2693 | 0 | bs_write( s, 4, 0 ); |
2694 | 0 | bs_write( s, 1, 1 ); |
2695 | 0 | bs_write( s, 1, 1 ); |
2696 | 0 | bs_write( s, 2, 0 ); /* pseudo color 0 */ |
2697 | 0 | } |
2698 | 0 | i_length--; |
2699 | 0 | } |
2700 | |
|
2701 | 0 | if( i_length == 2 ) |
2702 | 0 | { |
2703 | 0 | if( i_last_pixel ) |
2704 | 0 | { |
2705 | 0 | bs_write( s, 4, i_last_pixel ); |
2706 | 0 | bs_write( s, 4, i_last_pixel ); |
2707 | 0 | } |
2708 | 0 | else |
2709 | 0 | { |
2710 | 0 | bs_write( s, 4, 0 ); |
2711 | 0 | bs_write( s, 1, 1 ); |
2712 | 0 | bs_write( s, 1, 1 ); |
2713 | 0 | bs_write( s, 2, 1 ); /* 2 * pseudo color 0 */ |
2714 | 0 | } |
2715 | 0 | } |
2716 | 0 | else if( !i_last_pixel && ( i_length >= 3 ) && ( i_length <= 9 ) ) |
2717 | 0 | { |
2718 | 0 | bs_write( s, 4, 0 ); |
2719 | 0 | bs_write( s, 1, 0 ); |
2720 | 0 | bs_write( s, 3, i_length - 2 ); /* (i_length - 2) * color 0 */ |
2721 | 0 | } |
2722 | 0 | else if( i_length > 2 ) |
2723 | 0 | { |
2724 | 0 | bs_write( s, 4, 0 ); |
2725 | 0 | bs_write( s, 1, 1 ); |
2726 | |
|
2727 | 0 | if( i_length <= 7 ) |
2728 | 0 | { |
2729 | 0 | bs_write( s, 1, 0 ); |
2730 | 0 | bs_write( s, 2, i_length - 4 ); |
2731 | 0 | bs_write( s, 4, i_last_pixel ); |
2732 | 0 | } |
2733 | 0 | else |
2734 | 0 | { |
2735 | 0 | bs_write( s, 1, 1 ); |
2736 | |
|
2737 | 0 | if( i_length <= 24 ) |
2738 | 0 | { |
2739 | 0 | bs_write( s, 2, 2 ); |
2740 | 0 | bs_write( s, 4, i_length - 9 ); |
2741 | 0 | bs_write( s, 4, i_last_pixel ); |
2742 | 0 | } |
2743 | 0 | else |
2744 | 0 | { |
2745 | 0 | bs_write( s, 2, 3 ); |
2746 | 0 | bs_write( s, 8, i_length - 25 ); |
2747 | 0 | bs_write( s, 4, i_last_pixel ); |
2748 | 0 | } |
2749 | 0 | } |
2750 | 0 | } |
2751 | |
|
2752 | 0 | if( i == p_region->fmt.i_visible_width ) break; |
2753 | | |
2754 | 0 | i_last_pixel = p_data[i]; |
2755 | 0 | i_length = 1; |
2756 | 0 | } |
2757 | | |
2758 | | /* Stop */ |
2759 | 0 | bs_write( s, 8, 0 ); |
2760 | | |
2761 | | /* Stuffing */ |
2762 | 0 | bs_align_0( s ); |
2763 | 0 | } |
2764 | | |
2765 | | static void encode_pixel_line_8bp( bs_t *s, const subpicture_region_t *p_region, |
2766 | | int i_line ) |
2767 | 0 | { |
2768 | 0 | unsigned int i, i_length = 0; |
2769 | 0 | int i_pitch = p_region->p_picture->p->i_pitch; |
2770 | 0 | uint8_t *p_data = &p_region->p_picture->p->p_pixels[ i_pitch * i_line ]; |
2771 | 0 | int i_last_pixel = p_data[0]; |
2772 | |
|
2773 | 0 | for( i = 0; i <= p_region->fmt.i_visible_width; i++ ) |
2774 | 0 | { |
2775 | 0 | if( ( i != p_region->fmt.i_visible_width ) && |
2776 | 0 | ( p_data[i] == i_last_pixel ) && ( i_length != 127 ) ) |
2777 | 0 | { |
2778 | 0 | i_length++; |
2779 | 0 | continue; |
2780 | 0 | } |
2781 | | |
2782 | 0 | if( ( i_length == 1 ) && i_last_pixel ) |
2783 | 0 | { |
2784 | | /* 8bit/pixel code */ |
2785 | 0 | bs_write( s, 8, i_last_pixel ); |
2786 | 0 | } |
2787 | 0 | else if( ( i_length == 2 ) && i_last_pixel ) |
2788 | 0 | { |
2789 | | /* 8bit/pixel code */ |
2790 | 0 | bs_write( s, 8, i_last_pixel ); |
2791 | 0 | bs_write( s, 8, i_last_pixel ); |
2792 | 0 | } |
2793 | 0 | else if( i_length <= 127 ) |
2794 | 0 | { |
2795 | 0 | bs_write( s, 8, 0 ); |
2796 | |
|
2797 | 0 | if( !i_last_pixel ) |
2798 | 0 | { |
2799 | 0 | bs_write( s, 1, 0 ); |
2800 | 0 | bs_write( s, 7, i_length ); /* pseudo color 0 */ |
2801 | 0 | } |
2802 | 0 | else |
2803 | 0 | { |
2804 | 0 | bs_write( s, 1, 1 ); |
2805 | 0 | bs_write( s, 7, i_length ); |
2806 | 0 | bs_write( s, 8, i_last_pixel ); |
2807 | 0 | } |
2808 | 0 | } |
2809 | |
|
2810 | 0 | if( i == p_region->fmt.i_visible_width ) break; |
2811 | | |
2812 | 0 | i_last_pixel = p_data[i]; |
2813 | 0 | i_length = 1; |
2814 | 0 | } |
2815 | | |
2816 | | /* Stop */ |
2817 | 0 | bs_write( s, 8, 0 ); |
2818 | 0 | bs_write( s, 8, 0 ); |
2819 | | |
2820 | | /* Stuffing */ |
2821 | 0 | bs_align_0( s ); |
2822 | 0 | } |
2823 | | |
2824 | | #endif |
2825 | | |
2826 | | static void default_dds_init( decoder_t * p_dec ) |
2827 | 7.62k | { |
2828 | 7.62k | decoder_sys_t *p_sys = p_dec->p_sys; |
2829 | | |
2830 | | /* see notes on DDS at the top of the file */ |
2831 | | |
2832 | | /* configure for SD res in case DDS is not present */ |
2833 | 7.62k | p_sys->display.i_version = 0xff; /* an invalid version so it's always different */ |
2834 | 7.62k | p_sys->display.i_width_minus1 = 720-1; |
2835 | 7.62k | p_sys->display.i_height_minus1 = 576-1; |
2836 | | p_sys->display.b_windowed = false; |
2837 | 7.62k | } |