[vlc-commits] [Git][videolan/vlc][master] dvbsub: handle HDR subtitles

Steve Lhomme (@robUx4) gitlab at videolan.org
Thu May 22 07:29:49 UTC 2025



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
920f0add by Steve Lhomme at 2025-05-22T06:48:00+00:00
dvbsub: handle HDR subtitles

>From chapter 7.2.8 in ETSI EN 300 743 [^1].
We don't support palette color values with more than 8 bits
so we downscale the 10-bit values for now.

[^1]: https://www.etsi.org/deliver/etsi_en/300700_300799/300743/01.06.01_60/en_300743v010601p.pdf

- - - - -


1 changed file:

- modules/codec/dvbsub.c


Changes:

=====================================
modules/codec/dvbsub.c
=====================================
@@ -210,7 +210,9 @@ typedef struct dvbsub_clut_s
     dvbsub_color_t          c_2b[4];
     dvbsub_color_t          c_4b[16];
     dvbsub_color_t          c_8b[256];
+    int                     c_8b_entries;
     video_color_range_t     color_range;
+    int                     dynamic_range_and_colour_gamut;
 
     struct dvbsub_clut_s    *p_next;
 
@@ -291,6 +293,7 @@ typedef struct
 #define DVBSUB_ST_CLUT_DEFINITION       0x12
 #define DVBSUB_ST_OBJECT_DATA           0x13
 #define DVBSUB_ST_DISPLAY_DEFINITION    0x14
+#define DVBSUB_ST_ALTERNATE_CLUT        0x16
 #define DVBSUB_ST_ENDOFDISPLAY          0x80
 #define DVBSUB_ST_STUFFING              0xff
 /* List of different OBJECT TYPES */
@@ -311,6 +314,15 @@ typedef struct
 /* According to EN 300-743, 7.2.1 table 3 */
 #define DVBSUB_PCS_STATE_ACQUISITION    0x01
 #define DVBSUB_PCS_STATE_CHANGE         0x02
+/* According to EN 300-743, 7.2.8 table 33 */
+#define DVBSUB_ST_BITDEPTH_8BIT         0x00
+#define DVBSUB_ST_BITDEPTH_10BIT        0x01
+/* According to EN 300-743, 7.2.8 table 34 */
+#define DVBSUB_ST_COLORIMETRY_CDS       -1
+#define DVBSUB_ST_COLORIMETRY_SDR_709   0x00
+#define DVBSUB_ST_COLORIMETRY_SDR_2020  0x01
+#define DVBSUB_ST_COLORIMETRY_HDR_PQ    0x02
+#define DVBSUB_ST_COLORIMETRY_HDR_HLG   0x03
 
 /*****************************************************************************
  * Local prototypes
@@ -320,6 +332,7 @@ static void decode_page_composition( decoder_t *, bs_t *, uint16_t );
 static void decode_region_composition( decoder_t *, bs_t *, uint16_t );
 static void decode_object( decoder_t *, bs_t *, uint16_t );
 static void decode_display_definition( decoder_t *, bs_t *, uint16_t );
+static void alternative_CLUT( decoder_t *, bs_t *, uint16_t );
 static void decode_clut( decoder_t *, bs_t *, uint16_t );
 static void free_all( decoder_t * );
 
@@ -551,7 +564,9 @@ static void default_clut_init( decoder_t *p_dec )
 
     /* 256 entries CLUT */
     memset( p_sys->default_clut.c_8b, 0xFF, 256 * sizeof(dvbsub_color_t) );
+    p_sys->default_clut.c_8b_entries = 256;
     p_sys->default_clut.color_range = COLOR_RANGE_LIMITED;
+    p_sys->default_clut.dynamic_range_and_colour_gamut = DVBSUB_ST_COLORIMETRY_CDS;
 }
 
 static void decode_segment( decoder_t *p_dec, bs_t *s )
@@ -639,6 +654,13 @@ static void decode_segment( decoder_t *p_dec, bs_t *s )
         decode_display_definition( p_dec, s, i_size );
         break;
 
+    case DVBSUB_ST_ALTERNATE_CLUT:
+#ifdef DEBUG_DVBSUB
+        msg_Dbg( p_dec, "alternative_CLUT" );
+#endif
+        alternative_CLUT( p_dec, s, i_size );
+        break;
+
     case DVBSUB_ST_ENDOFDISPLAY:
 #ifdef DEBUG_DVBSUB
         msg_Dbg( p_dec, "end of display" );
@@ -997,6 +1019,130 @@ static void decode_region_composition( decoder_t *p_dec, bs_t *s, uint16_t i_seg
     }
 }
 
+/* ETSI 300 743 [7.2.8] */
+static void alternative_CLUT( decoder_t *p_dec, bs_t *s, uint16_t i_segment_length )
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    uint16_t      i_processed_length;
+    int           i_id, i_version;
+    dvbsub_clut_t *p_clut, *p_next;
+
+    i_id = bs_read( s, 8 );
+    i_version = bs_read( s, 4 );
+
+    /* Check if we already have this clut */
+    for( p_clut = p_sys->p_cluts; p_clut != NULL; p_clut = p_clut->p_next )
+    {
+        if( p_clut->i_id == i_id )
+        {
+            /* Check version number */
+            if( p_clut->i_version == i_version )
+            {
+                /* Nothing to do */
+                bs_skip( s, 8 * i_segment_length - 12 );
+                return;
+            }
+
+            break;
+        }
+    }
+
+    if( !p_clut )
+    {
+#ifdef DEBUG_DVBSUB
+        msg_Dbg( p_dec, "new alternative clut: %i", i_id );
+#endif
+        p_clut = malloc( sizeof( dvbsub_clut_t ) );
+        if( !p_clut )
+            return;
+        p_clut->p_next = p_sys->p_cluts;
+        p_sys->p_cluts = p_clut;
+    }
+
+    /* We don't have this version of the CLUT: Parse it */
+    p_clut->i_version = i_version;
+    p_clut->i_id = i_id;
+
+    bs_skip( s, 4 ); // reserved_zero_future_use
+
+    // CLUT_parameters
+    int CLUT_entry_max_number = bs_read( s, 2 );
+    int colour_component_type = bs_read( s, 2 );
+    int output_bit_depth = bs_read( s, 3 );
+    bs_skip( s, 1 ); // reserved_zero_future_use
+    int dynamic_range_and_colour_gamut = bs_read( s, 8 );
+
+    bool error = false;
+    i_processed_length = 4;
+    if (output_bit_depth != DVBSUB_ST_BITDEPTH_8BIT &&
+        output_bit_depth == DVBSUB_ST_BITDEPTH_10BIT)
+    {
+        msg_Err( p_dec, "unsupported alternative clut bitdepth: %i", output_bit_depth );
+        error = true;
+    }
+    if (CLUT_entry_max_number != 0) // 0: 256 entries
+    {
+        msg_Err( p_dec, "unsupported alternative clut max entries: %i", CLUT_entry_max_number );
+        error = true;
+    }
+    if (colour_component_type != 0) // 0: YCbCr
+    {
+        msg_Err( p_dec, "unsupported alternative clut component type: %i", colour_component_type );
+        error = true;
+    }
+    if (dynamic_range_and_colour_gamut > DVBSUB_ST_COLORIMETRY_HDR_HLG)
+    {
+        msg_Err( p_dec, "unsupported alternative clut color range: %i", dynamic_range_and_colour_gamut );
+        error = true;
+    }
+    if (error)
+        goto done;
+
+    p_clut->c_8b_entries = 0;
+    while( i_processed_length < i_segment_length )
+    {
+        uint8_t y, cb, cr, t;
+        if (output_bit_depth == DVBSUB_ST_BITDEPTH_10BIT)
+        {
+            // TODO: apply the palette locally to keep 10-bit values
+            y  = bs_read( s, 10 ) >> 2;
+            cr = bs_read( s, 10 ) >> 2;
+            cb = bs_read( s, 10 ) >> 2;
+            t  = bs_read( s, 10 ) >> 2;
+            i_processed_length += 5;
+        }
+        else
+        {
+            y  = bs_read( s, 8 );
+            cr = bs_read( s, 8 );
+            cb = bs_read( s, 8 );
+            t  = bs_read( s, 8 );
+            i_processed_length += 4;
+        }
+
+        /* We are not entirely compliant here as full transparency is indicated
+         * with a luma value of zero, not a transparency value of 0xff
+         * (full transparency would actually be 0xff + 1). */
+        if( y == 0 )
+        {
+            cr = cb = 0;
+            t  = 0xff;
+        }
+
+        p_clut->c_8b[p_clut->c_8b_entries].Y = y;
+        p_clut->c_8b[p_clut->c_8b_entries].Cr = cr;
+        p_clut->c_8b[p_clut->c_8b_entries].Cb = cb;
+        p_clut->c_8b[p_clut->c_8b_entries].T = t;
+        p_clut->c_8b_entries++;
+        if (p_clut->c_8b_entries >= (int)ARRAY_SIZE(p_clut->c_8b))
+            break;
+    }
+    p_clut->dynamic_range_and_colour_gamut = dynamic_range_and_colour_gamut;
+    p_clut->color_range = COLOR_RANGE_FULL;
+done:
+    bs_skip( s, 8 * (i_segment_length - i_processed_length) );
+}
+
 /* ETSI 300 743 [7.2.1] */
 static void decode_display_definition( decoder_t *p_dec, bs_t *s, uint16_t i_segment_length )
 {
@@ -1595,7 +1741,7 @@ static subpicture_t *render( decoder_t *p_dec )
         fmt.i_x_offset = fmt.i_y_offset = 0;
         fmt.p_palette = &palette;
         fmt.p_palette->i_entries = ( p_region->i_depth == 1 ) ? 4 :
-            ( ( p_region->i_depth == 2 ) ? 16 : 256 );
+            ( ( p_region->i_depth == 2 ) ? 16 : p_clut->c_8b_entries );
         p_color = ( p_region->i_depth == 1 ) ? p_clut->c_2b :
             ( ( p_region->i_depth == 2 ) ? p_clut->c_4b : p_clut->c_8b );
         for( j = 0; j < fmt.p_palette->i_entries; j++ )
@@ -1605,6 +1751,34 @@ static subpicture_t *render( decoder_t *p_dec )
             fmt.p_palette->palette[j][2] = p_color[j].Cr; /* V == Cr */
             fmt.p_palette->palette[j][3] = 0xff - p_color[j].T;
         }
+        switch (p_clut->dynamic_range_and_colour_gamut)
+        {
+            case DVBSUB_ST_COLORIMETRY_CDS:
+                fmt.space = COLOR_SPACE_BT601;
+                fmt.primaries = COLOR_PRIMARIES_BT601_525;
+                fmt.transfer = TRANSFER_FUNC_BT709;
+                break;
+            case DVBSUB_ST_COLORIMETRY_SDR_709:
+                fmt.space = COLOR_SPACE_BT709;
+                fmt.primaries = COLOR_PRIMARIES_BT709;
+                fmt.transfer = TRANSFER_FUNC_BT709;
+                break;
+            case DVBSUB_ST_COLORIMETRY_SDR_2020:
+                fmt.space = COLOR_SPACE_BT2020;
+                fmt.primaries = COLOR_PRIMARIES_BT2020;
+                fmt.transfer = TRANSFER_FUNC_BT709;
+                break;
+            case DVBSUB_ST_COLORIMETRY_HDR_PQ:
+                fmt.space = COLOR_SPACE_BT2020;
+                fmt.primaries = COLOR_PRIMARIES_BT2020;
+                fmt.transfer = TRANSFER_FUNC_SMPTE_ST2084;
+                break;
+            case DVBSUB_ST_COLORIMETRY_HDR_HLG:
+                fmt.space = COLOR_SPACE_BT2020;
+                fmt.primaries = COLOR_PRIMARIES_BT2020;
+                fmt.transfer = TRANSFER_FUNC_HLG;
+                break;
+        }
         fmt.color_range = p_clut->color_range;
 
         p_spu_region = subpicture_region_New( &fmt );



View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/920f0addc5909e21ad9fd51c2b7ddfc02ab3f428

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/commit/920f0addc5909e21ad9fd51c2b7ddfc02ab3f428
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list