]> git.sesse.net Git - vlc/blobdiff - modules/codec/spudec/parse.c
Use var_InheritString for --decklink-video-connection.
[vlc] / modules / codec / spudec / parse.c
index 5a2ee513301f521cc765557759a4591df2240940..99a8c4702da49bf03729cdd0f816849a52be61c3 100644 (file)
@@ -31,7 +31,6 @@
 #endif
 
 #include <vlc_common.h>
-#include <vlc_vout.h>
 #include <vlc_codec.h>
 #include <vlc_input.h>
 
@@ -48,8 +47,25 @@ typedef struct
     int i_y;
 } spu_properties_t;
 
+typedef struct
+{
+    int   pi_offset[2];                              /* byte offsets to data */
+    uint16_t *p_data;
+
+    /* Color information */
+    bool b_palette;
+    uint8_t    pi_alpha[4];
+    uint8_t    pi_yuv[4][3];
+
+    /* Auto crop fullscreen subtitles */
+    bool b_auto_crop;
+    int i_y_top_offset;
+    int i_y_bottom_offset;
+
+} subpicture_data_t;
+
 static int  ParseControlSeq( decoder_t *, subpicture_t *, subpicture_data_t *,
-                             spu_properties_t * );
+                             spu_properties_t *, mtime_t i_pts );
 static int  ParseRLE       ( decoder_t *, subpicture_data_t *,
                              const spu_properties_t * );
 static void Render         ( decoder_t *, subpicture_t *, subpicture_data_t *,
@@ -59,7 +75,7 @@ static void Render         ( decoder_t *, subpicture_t *, subpicture_data_t *,
  * AddNibble: read a nibble from a source packet and add it to our integer.
  *****************************************************************************/
 static inline unsigned int AddNibble( unsigned int i_code,
-                                      uint8_t *p_src, unsigned int *pi_index )
+                                      const uint8_t *p_src, unsigned int *pi_index )
 {
     if( *pi_index & 0x1 )
     {
@@ -80,64 +96,54 @@ static inline unsigned int AddNibble( unsigned int i_code,
 subpicture_t * ParsePacket( decoder_t *p_dec )
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
-    subpicture_data_t *p_spu_data;
     subpicture_t *p_spu;
+    subpicture_data_t spu_data;
     spu_properties_t spu_properties;
 
     /* Allocate the subpicture internal data. */
-    p_spu = p_dec->pf_spu_buffer_new( p_dec );
+    p_spu = decoder_NewSubpicture( p_dec, NULL );
     if( !p_spu ) return NULL;
 
-    /* Rationale for the "p_spudec->i_rle_size * 4": we are going to
-     * expand the RLE stuff so that we won't need to read nibbles later
-     * on. This will speed things up a lot. Plus, we'll only need to do
-     * this stupid interlacing stuff once. */
-    p_spu_data = malloc( sizeof(subpicture_data_t) + 4 * p_sys->i_rle_size );
-    p_spu_data->p_data = (uint8_t *)p_spu_data + sizeof(subpicture_data_t);
-    p_spu_data->b_palette = false;
-    p_spu_data->b_auto_crop = false;
-    p_spu_data->i_y_top_offset = 0;
-    p_spu_data->i_y_bottom_offset = 0;
-
-    p_spu_data->pi_alpha[0] = 0x00;
-    p_spu_data->pi_alpha[1] = 0x0f;
-    p_spu_data->pi_alpha[2] = 0x0f;
-    p_spu_data->pi_alpha[3] = 0x0f;
-
-    /* Get display time now. If we do it later, we may miss the PTS. */
-    p_spu_data->i_pts = p_sys->i_pts;
-
     p_spu->i_original_picture_width =
         p_dec->fmt_in.subs.spu.i_original_frame_width;
     p_spu->i_original_picture_height =
         p_dec->fmt_in.subs.spu.i_original_frame_height;
 
-    memset( &spu_properties, 0, sizeof(spu_properties) );
-
     /* Getting the control part */
-    if( ParseControlSeq( p_dec, p_spu, p_spu_data, &spu_properties ) )
+    if( ParseControlSeq( p_dec, p_spu, &spu_data, &spu_properties, p_sys->i_pts ) )
     {
         /* There was a parse error, delete the subpicture */
-        p_dec->pf_spu_buffer_del( p_dec, p_spu );
+        decoder_DeleteSubpicture( p_dec, p_spu );
         return NULL;
     }
 
+    /* we are going to expand the RLE stuff so that we won't need to read
+     * nibbles later on. This will speed things up a lot. Plus, we'll only
+     * need to do this stupid interlacing stuff once.
+     *
+     * Rationale for the "p_spudec->i_rle_size * 4*sizeof(*spu_data.p_data)":
+     *  one byte gaves two nibbles and may be used twice (once per field)
+     * generating 4 codes.
+     */
+    spu_data.p_data = malloc( sizeof(*spu_data.p_data) * 2 * 2 * p_sys->i_rle_size );
+
     /* We try to display it */
-    if( ParseRLE( p_dec, p_spu_data, &spu_properties ) )
+    if( ParseRLE( p_dec, &spu_data, &spu_properties ) )
     {
         /* There was a parse error, delete the subpicture */
-        p_dec->pf_spu_buffer_del( p_dec, p_spu );
+        decoder_DeleteSubpicture( p_dec, p_spu );
+        free( spu_data.p_data );
         return NULL;
     }
 
 #ifdef DEBUG_SPUDEC
     msg_Dbg( p_dec, "total size: 0x%x, RLE offsets: 0x%x 0x%x",
              p_sys->i_spu_size,
-             p_spu_data->pi_offset[0], p_spu_data->pi_offset[1] );
+             spu_data.pi_offset[0], spu_data.pi_offset[1] );
 #endif
 
-    Render( p_dec, p_spu, p_spu_data, &spu_properties );
-    free( p_spu_data );
+    Render( p_dec, p_spu, &spu_data, &spu_properties );
+    free( spu_data.p_data );
 
     return p_spu;
 }
@@ -150,12 +156,12 @@ subpicture_t * ParsePacket( decoder_t *p_dec )
  * subtitles format, see http://sam.zoy.org/doc/dvd/subtitles/index.html
  *****************************************************************************/
 static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu,
-                            subpicture_data_t *p_spu_data, spu_properties_t *p_spu_properties )
+                            subpicture_data_t *p_spu_data, spu_properties_t *p_spu_properties, mtime_t i_pts )
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
 
     /* Our current index in the SPU packet */
-    unsigned int i_index = p_sys->i_rle_size + 4;
+    unsigned int i_index;
 
     /* The next start-of-control-sequence index and the previous one */
     unsigned int i_next_seq = 0, i_cur_seq = 0;
@@ -163,14 +169,36 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu,
     /* Command and date */
     uint8_t i_command = SPU_CMD_END;
     mtime_t date = 0;
+    bool b_cmd_offset = false;
+    bool b_cmd_alpha = false;
+    subpicture_data_t spu_data_cmd;
 
     if( !p_spu || !p_spu_data )
         return VLC_EGENERIC;
 
+    /* Create working space for spu data */
+    memset( &spu_data_cmd, 0, sizeof(spu_data_cmd) );
+    spu_data_cmd.pi_offset[0] = -1;
+    spu_data_cmd.pi_offset[1] = -1;
+    spu_data_cmd.p_data = NULL;
+    spu_data_cmd.b_palette = false;
+    spu_data_cmd.b_auto_crop = false;
+    spu_data_cmd.i_y_top_offset = 0;
+    spu_data_cmd.i_y_bottom_offset = 0;
+    spu_data_cmd.pi_alpha[0] = 0x00;
+    spu_data_cmd.pi_alpha[1] = 0x0f;
+    spu_data_cmd.pi_alpha[2] = 0x0f;
+    spu_data_cmd.pi_alpha[3] = 0x0f;
+
     /* Initialize the structure */
     p_spu->i_start = p_spu->i_stop = 0;
     p_spu->b_ephemer = false;
 
+    memset( p_spu_properties, 0, sizeof(*p_spu_properties) );
+
+    /* */
+    *p_spu_data = spu_data_cmd;
+
     for( i_index = 4 + p_sys->i_rle_size; i_index < p_sys->i_spu_size ; )
     {
         /* If we just read a command sequence, read the next one;
@@ -183,6 +211,9 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu,
                 return VLC_EGENERIC;
             }
 
+            /* */
+            b_cmd_offset = false;
+            b_cmd_alpha = false;
             /* Get the control sequence date */
             date = (mtime_t)GetWBE( &p_sys->buffer[i_index] ) * 11000;
 
@@ -205,19 +236,19 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu,
         switch( i_command )
         {
         case SPU_CMD_FORCE_DISPLAY: /* 00 (force displaying) */
-            p_spu->i_start = p_spu_data->i_pts + date;
+            p_spu->i_start = i_pts + date;
             p_spu->b_ephemer = true;
             i_index += 1;
             break;
 
         /* Convert the dates in seconds to PTS values */
         case SPU_CMD_START_DISPLAY: /* 01 (start displaying) */
-            p_spu->i_start = p_spu_data->i_pts + date;
+            p_spu->i_start = i_pts + date;
             i_index += 1;
             break;
 
         case SPU_CMD_STOP_DISPLAY: /* 02 (stop displaying) */
-            p_spu->i_stop = p_spu_data->i_pts + date;
+            p_spu->i_stop = i_pts + date;
             i_index += 1;
             break;
 
@@ -234,7 +265,7 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu,
                 unsigned int idx[4];
                 int i;
 
-                p_spu_data->b_palette = true;
+                spu_data_cmd.b_palette = true;
 
                 idx[0] = (p_sys->buffer[i_index+1]>>4)&0x0f;
                 idx[1] = (p_sys->buffer[i_index+1])&0x0f;
@@ -246,9 +277,9 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu,
                     uint32_t i_color = p_dec->fmt_in.subs.spu.palette[1+idx[i]];
 
                     /* FIXME: this job should be done sooner */
-                    p_spu_data->pi_yuv[3-i][0] = (i_color>>16) & 0xff;
-                    p_spu_data->pi_yuv[3-i][1] = (i_color>>0) & 0xff;
-                    p_spu_data->pi_yuv[3-i][2] = (i_color>>8) & 0xff;
+                    spu_data_cmd.pi_yuv[3-i][0] = (i_color>>16) & 0xff;
+                    spu_data_cmd.pi_yuv[3-i][1] = (i_color>>0) & 0xff;
+                    spu_data_cmd.pi_yuv[3-i][2] = (i_color>>8) & 0xff;
                 }
             }
 
@@ -262,10 +293,14 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu,
                 return VLC_EGENERIC;
             }
 
-            p_spu_data->pi_alpha[3] = (p_sys->buffer[i_index+1]>>4)&0x0f;
-            p_spu_data->pi_alpha[2] = (p_sys->buffer[i_index+1])&0x0f;
-            p_spu_data->pi_alpha[1] = (p_sys->buffer[i_index+2]>>4)&0x0f;
-            p_spu_data->pi_alpha[0] = (p_sys->buffer[i_index+2])&0x0f;
+            if(!p_sys->b_disabletrans)
+            { /* If we want to use original transparency values */
+                b_cmd_alpha = true;
+                spu_data_cmd.pi_alpha[3] = (p_sys->buffer[i_index+1]>>4)&0x0f;
+                spu_data_cmd.pi_alpha[2] = (p_sys->buffer[i_index+1])&0x0f;
+                spu_data_cmd.pi_alpha[1] = (p_sys->buffer[i_index+2]>>4)&0x0f;
+                spu_data_cmd.pi_alpha[0] = (p_sys->buffer[i_index+2])&0x0f;
+            }
 
             i_index += 3;
             break;
@@ -301,12 +336,25 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu,
                 return VLC_EGENERIC;
             }
 
+            b_cmd_offset = true;
             p_spu_data->pi_offset[0] = GetWBE(&p_sys->buffer[i_index+1]) - 4;
             p_spu_data->pi_offset[1] = GetWBE(&p_sys->buffer[i_index+3]) - 4;
             i_index += 5;
             break;
 
         case SPU_CMD_END: /* ff (end) */
+            if( b_cmd_offset )
+            {
+                /* It seems that palette and alpha from the block having
+                 * the cmd offset have to be used
+                 * XXX is it all ? */
+                p_spu_data->b_palette = spu_data_cmd.b_palette;
+                if( spu_data_cmd.b_palette )
+                    memcpy( p_spu_data->pi_yuv, spu_data_cmd.pi_yuv, sizeof(spu_data_cmd.pi_yuv) );
+                if( b_cmd_alpha )
+                    memcpy( p_spu_data->pi_alpha, spu_data_cmd.pi_alpha, sizeof(spu_data_cmd.pi_alpha) );
+            }
+
             i_index += 1;
             break;
 
@@ -336,16 +384,9 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu,
             }
         }
 
-        /* We need to check for quit commands here */
-        if( !vlc_object_alive (p_dec) )
-        {
-            return VLC_EGENERIC;
-        }
-
+        /* */
         if( i_command == SPU_CMD_END && i_index != i_next_seq )
-        {
             break;
-        }
     }
 
     /* Check that the next sequence index matches the current one */
@@ -363,6 +404,14 @@ static int ParseControlSeq( decoder_t *p_dec, subpicture_t *p_spu,
         return VLC_EGENERIC;
     }
 
+    const int i_spu_size = p_sys->i_spu - 4;
+    if( p_spu_data->pi_offset[0] < 0 || p_spu_data->pi_offset[0] >= i_spu_size ||
+        p_spu_data->pi_offset[1] < 0 || p_spu_data->pi_offset[1] >= i_spu_size )
+    {
+        msg_Err( p_dec, "invalid offset values" );
+        return VLC_EGENERIC;
+    }
+
     if( !p_spu->i_start )
     {
         msg_Err( p_dec, "no `start display' command" );
@@ -402,15 +451,12 @@ static int ParseRLE( decoder_t *p_dec,
                      const spu_properties_t *p_spu_properties )
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
-    uint8_t       *p_src = &p_sys->buffer[4];
-
-    unsigned int i_code;
 
-    unsigned int i_width = p_spu_properties->i_width;
-    unsigned int i_height = p_spu_properties->i_height;
+    const unsigned int i_width = p_spu_properties->i_width;
+    const unsigned int i_height = p_spu_properties->i_height;
     unsigned int i_x, i_y;
 
-    uint16_t *p_dest = (uint16_t *)p_spu_data->p_data;
+    uint16_t *p_dest = p_spu_data->p_data;
 
     /* The subtitles are interlaced, we need two offsets */
     unsigned int  i_id = 0;                   /* Start on the even SPU layer */
@@ -421,7 +467,7 @@ static int ParseRLE( decoder_t *p_dec,
     bool b_empty_top = true;
     unsigned int i_skipped_top = 0, i_skipped_bottom = 0;
     unsigned int i_transparent_code = 0;
+
     /* Colormap statistics */
     int i_border = -1;
     int stats[4]; stats[0] = stats[1] = stats[2] = stats[3] = 0;
@@ -431,42 +477,26 @@ static int ParseRLE( decoder_t *p_dec,
 
     for( i_y = 0 ; i_y < i_height ; i_y++ )
     {
+        unsigned int i_code;
         pi_offset = pi_table + i_id;
 
         for( i_x = 0 ; i_x < i_width ; i_x += i_code >> 2 )
         {
-            i_code = AddNibble( 0, p_src, pi_offset );
-
-            if( i_code < 0x04 )
+            i_code = 0;
+            for( unsigned int i_min = 1; i_min <= 0x40 && i_code < i_min; i_min <<= 2 )
             {
-                i_code = AddNibble( i_code, p_src, pi_offset );
-
-                if( i_code < 0x10 )
+                if( (*pi_offset >> 1) >= p_sys->i_spu_size )
                 {
-                    i_code = AddNibble( i_code, p_src, pi_offset );
-
-                    if( i_code < 0x040 )
-                    {
-                        i_code = AddNibble( i_code, p_src, pi_offset );
-
-                        if( i_code < 0x0100 )
-                        {
-                            /* If the 14 first bits are set to 0, then it's a
-                             * new line. We emulate it. */
-                            if( i_code < 0x0004 )
-                            {
-                                i_code |= ( i_width - i_x ) << 2;
-                            }
-                            else
-                            {
-                                /* We have a boo boo ! */
-                                msg_Err( p_dec, "unknown RLE code "
-                                         "0x%.4x", i_code );
-                                return VLC_EGENERIC;
-                            }
-                        }
-                    }
+                    msg_Err( p_dec, "out of bounds while reading rle" );
+                    return VLC_EGENERIC;
                 }
+                i_code = AddNibble( i_code, &p_sys->buffer[4], pi_offset );
+            }
+            if( i_code < 0x0004 )
+            {
+                /* If the 14 first bits are set to 0, then it's a
+                 * new line. We emulate it. */
+                i_code |= ( i_width - i_x ) << 2;
             }
 
             if( ( (i_code >> 2) + i_x + i_y * i_width ) > i_height * i_width )
@@ -586,7 +616,7 @@ static int ParseRLE( decoder_t *p_dec,
                  p_spu->i_width, i_height, p_spu->i_x, i_y );
 #endif
     }
+
     /* Handle color if no palette was found */
     if( !p_spu_data->b_palette )
     {
@@ -654,14 +684,15 @@ static void Render( decoder_t *p_dec, subpicture_t *p_spu,
 {
     uint8_t *p_p;
     int i_x, i_y, i_len, i_color, i_pitch;
-    uint16_t *p_source = (uint16_t *)p_spu_data->p_data;
+    const uint16_t *p_source = p_spu_data->p_data;
     video_format_t fmt;
     video_palette_t palette;
 
     /* Create a new subpicture region */
     memset( &fmt, 0, sizeof(video_format_t) );
-    fmt.i_chroma = VLC_FOURCC('Y','U','V','P');
-    fmt.i_aspect = 0; /* 0 means use aspect ratio of background video */
+    fmt.i_chroma = VLC_CODEC_YUVP;
+    fmt.i_sar_num = 0; /* 0 means use aspect ratio of background video */
+    fmt.i_sar_den = 1;
     fmt.i_width = fmt.i_visible_width = p_spu_properties->i_width;
     fmt.i_height = fmt.i_visible_height = p_spu_properties->i_height -
         p_spu_data->i_y_top_offset - p_spu_data->i_y_bottom_offset;
@@ -673,9 +704,7 @@ static void Render( decoder_t *p_dec, subpicture_t *p_spu,
         fmt.p_palette->palette[i_x][0] = p_spu_data->pi_yuv[i_x][0];
         fmt.p_palette->palette[i_x][1] = p_spu_data->pi_yuv[i_x][1];
         fmt.p_palette->palette[i_x][2] = p_spu_data->pi_yuv[i_x][2];
-        fmt.p_palette->palette[i_x][3] =
-            p_spu_data->pi_alpha[i_x] == 0xf ? 0xff :
-            p_spu_data->pi_alpha[i_x] << 4;
+        fmt.p_palette->palette[i_x][3] = p_spu_data->pi_alpha[i_x] * 0x11;
     }
 
     p_spu->p_region = subpicture_region_New( &fmt );