[vlc-devel] [PATCH 15/19] ttml demux: add timing on span support

Denis Charmet typx at dinauz.org
Mon Aug 29 19:41:36 CEST 2016


On 2016-08-29 16:01, Stanislas Plessia wrote:
> ---
>  modules/demux/ttml.c | 214 
> +++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 214 insertions(+)
> 
> diff --git a/modules/demux/ttml.c b/modules/demux/ttml.c
> index 4f57f67..79f93bb 100644
> --- a/modules/demux/ttml.c
> +++ b/modules/demux/ttml.c
> @@ -346,6 +346,218 @@ static int ReadAttrNode( xml_reader_t* reader,
> node_t* p_node, const char* psz_n
>      return VLC_SUCCESS;
>  }
> 
> +static inline bool isVisibleSpan( subtitle_t* p_sub, mtime_t time_ref 
> )
> +{
> +    return ( ( p_sub->i_start <= time_ref ) && ( p_sub->i_stop > 
> time_ref ) );
> +}
> +
> +static bool isInArray( vlc_array_t* p_array, mtime_t* p_elem )
> +{
> +    for( int i = 0; i < p_array->i_count ; i++ )
> +    {
> +        if( *(mtime_t*)p_array->pp_elems[i] == *p_elem )
> +            return true;
> +    }
> +    return false;
> +}
> +
> +static int addToArrayIfNotInside( vlc_array_t* p_array, mtime_t* 
> p_elem )
> +{
> +    if( !isInArray( p_array, p_elem ) )
> +    {
> +        vlc_array_append( p_array, (void*)p_elem );
> +        if( unlikely( p_array->pp_elems[p_array->i_count - 1] == NULL 
> ) )
> +            return VLC_ENOMEM;
> +    }
> +    return VLC_SUCCESS;
> +}
> +
> +static int timeCmp( const void* p_time1, const void* p_time2 )
> +{
> +    return ( *(int*)p_time1 - *(int*)p_time2 );
> +}
> +
> +/*
> +* To set the span opacity to zero, we add the opacity
> +* attribute after all the existing ones for him to be parsed last
> +* in order to always be effective.
> +*/
> +static char* setOpacitiyToZero( char* psz_text )
> +{
> +    const char* psz_begin = strstr( psz_text, "<span " );
> +    if( unlikely( psz_begin == NULL ) )
> +        return NULL;
> +
> +    const char* psz_end = strstr( psz_text, ">" );
> +    char* psz_cpy = malloc( strlen( psz_text ) );
> +    if( unlikely( psz_cpy == NULL ) )
> +        return NULL;
> +
> +    strncpy( psz_cpy, psz_text, psz_end - psz_begin );
> +    psz_cpy = Append( psz_cpy, " tts:opacity=\"0\">%s", psz_end + 1);
> +    return psz_cpy;
> +}
> +
> +static void CleanSubs( subtitle_t** tab )
> +{
> +    for( int i = 0; tab[i] != NULL; i++ )
> +    {
> +        free( tab[i]->psz_text );
> +        free( tab[i] );
> +    }
> +    free( tab );
> +}
> +
> +/*
> +* If the timing are set in the span tags, we will
> +* create a new p tag for each time space in the
> +* subtitle timeline in the function below.
> +*/
> +static int ParseTimeOnSpan( demux_sys_t* p_sys, char* psz_text )
> +{
> +    xml_reader_t* p_reader = p_sys->p_reader;
> +    subtitle_t** pp_subtitles = calloc( 1, sizeof( *pp_subtitles ) );
> +    if( unlikely( pp_subtitles == NULL ) )
> +        return VLC_ENOMEM;
> +
> +    vlc_array_t* p_times = vlc_array_new();
> +    if( unlikely( p_times == NULL ) )
> +        goto error;
> +
> +    const char* psz_node_name;
> +    int i_max_sub = 0;
> +    int i_nb_span = 0;
> +    int ret = VLC_ENOMEM;
> +
> +    int i_type = xml_ReaderNextNode( p_reader, &psz_node_name );
> +    /*
> +    * This loop will parse the current p tag and the
> +    * spans inside, and will store every span text and times
> +    * inside a subtitle_t structure.
> +    */
> +    do
> +    {
> +        if( i_type == XML_READER_STARTELEM && !strcasecmp(
> psz_node_name, "metadata" ) )
> +        {
> +            do
> +            {
> +                i_type = xml_ReaderNextNode( p_sys->p_reader, 
> &psz_node_name );
> +            }while ( i_type != XML_READER_ENDELEM || strcasecmp(
> psz_node_name, "metadata" ) );
> +            i_type = xml_ReaderNextNode( p_sys->p_reader, 
> &psz_node_name );
> +        }
> +        else if( i_type == XML_READER_STARTELEM )
> +        {
> +            node_t* p_node = calloc( 1, sizeof( *p_node ) );
> +            if( unlikely( p_node == NULL ) )
> +                goto error;
> +
> +            pp_subtitles[i_nb_span] = malloc( sizeof(
> *pp_subtitles[i_nb_span] ) );
> +            if( unlikely( pp_subtitles[i_nb_span] == NULL ) )
> +            {
> +                ClearNode( p_node );
> +                goto error;
> +            }
> +
> +            if( ReadAttrNode( p_sys->p_reader, p_node, psz_node_name
> ) != VLC_SUCCESS )
> +            {
> +                ClearNode( p_node );
> +                goto error;
> +            }
> +
> +            if ( asprintf( &pp_subtitles[i_nb_span]->psz_text, "%s",
> NodeToStr( p_node ) ) < 0 )
> +            {
> +                ClearNode( p_node );
> +                goto error;
> +            }
> +
> +            Convert_time( &pp_subtitles[i_nb_span]->i_start,
> p_node->psz_begin );
> +            Convert_time( &pp_subtitles[i_nb_span]->i_stop, 
> p_node->psz_end );
> +            ClearNode( p_node );
> +        }
> +        else if( i_type == XML_READER_TEXT )
> +        {
> +            pp_subtitles[i_nb_span]->psz_text = Append(
> pp_subtitles[i_nb_span]->psz_text, "%s", psz_node_name );
> +        }
> +        else if( i_type == XML_READER_ENDELEM )
> +        {
> +            pp_subtitles[i_nb_span]->psz_text = Append(
> pp_subtitles[i_nb_span]->psz_text, "</%s>", psz_node_name );
> +            i_nb_span++;
> +            pp_subtitles = realloc_or_free( pp_subtitles, (
> i_nb_span + 1 ) * sizeof( *pp_subtitles ) );
> +            if( unlikely( pp_subtitles == NULL ) )
> +                goto error;
> +        }
> +        i_type = xml_ReaderNextNode( p_reader, &psz_node_name );
> +
> +    } while( i_type != XML_READER_ENDELEM || ( strcasecmp(
> psz_node_name, "p" ) && strcasecmp( psz_node_name, "tt:p" ) ) );
> +
> +    /*
> +    * To split the timeline of the current subtitle, we take every
> +    * time of the structure array once and sort them from
> +    * earliest to last.
> +    */
> +    pp_subtitles[i_nb_span] = NULL;
> +    for( int j = 0; j < i_nb_span; j++ )
> +    {
> +        if( addToArrayIfNotInside( p_times,
> &pp_subtitles[j]->i_start ) != VLC_SUCCESS )
> +            goto error;
> +
> +        if( addToArrayIfNotInside( p_times, &pp_subtitles[j]->i_stop
> ) != VLC_SUCCESS )
> +            goto error;
> +    }
> +
> +    qsort( p_times->pp_elems, p_times->i_count, sizeof( mtime_t ), 
> timeCmp );
> +
> +    if( p_sys->i_subtitles >= i_max_sub )
> +    {
> +        i_max_sub += 500;

This is fishy... I might be getting it wrong but what if 
p_sys->i_subtitles > i_max_sub + 500?

> +        p_sys->subtitle = realloc_or_free( p_sys->subtitle, sizeof(
> *p_sys->subtitle ) * i_max_sub );
> +        if( unlikely( p_sys->subtitle == NULL ) )
> +            goto error;
> +    }
> +    /*
> +    * For each time space represented by the times inside the p_times 
> array
> +    * we create a p tag with all the spans inside.
> +    *
> +    * Then accroding to each span begin and end attributes,
> +    * we check if it should be displayed or not, and if not,
> +    * we set its opacity to zero and add the texts to the final
> +    * subtitle structure.
> +    */
> +    for( int j = 0; j < ( p_times->i_count - 1 ); j++)
> +    {
> +        char* psz_sub = strdup( psz_text );
> +        if( unlikely( psz_sub == NULL ) )
> +            goto error;
> +
> +        for( int k = 0; k < i_nb_span; k++ )
> +        {
> +            if( isVisibleSpan( pp_subtitles[k],
> *(mtime_t*)p_times->pp_elems[j] ) )
> +                psz_sub = Append( psz_sub, "%s", 
> pp_subtitles[k]->psz_text );
> +            else
> +            {
> +                char* psz_transparent= setOpacitiyToZero(
> pp_subtitles[k]->psz_text );
> +                if( unlikely( psz_transparent == NULL ) )
> +                    goto error;
> +
> +                psz_sub = Append( psz_sub, "%s",  psz_transparent );
> +                free( psz_transparent );
> +            }
> +        }
> +        psz_sub = Append( psz_sub, "</p>");
> +        subtitle_t *p_subtitle = 
> &p_sys->subtitle[p_sys->i_subtitles];
> +        p_subtitle->i_start = *(mtime_t*)p_times->pp_elems[j];
> +        p_subtitle->i_stop = *(mtime_t*)p_times->pp_elems[j+1];
> +        p_subtitle->psz_text = psz_sub;
> +        p_sys->i_subtitles++;
> +    }
> +    ret = VLC_SUCCESS;
> +
> +error:
> +    CleanSubs( pp_subtitles );
> +    vlc_array_destroy( p_times );
> +    return ret;
> +}
> +
>  static int ReadTTML( demux_t* p_demux )
>  {
>      demux_sys_t* p_sys = p_demux->p_sys;
> @@ -475,6 +687,8 @@ static int ReadTTML( demux_t* p_demux )
>                      p_subtitle->psz_text = psz_text;
>                      p_sys->i_subtitles++;
>                  }
> +                else if( ParseTimeOnSpan( p_sys , psz_text ) == 
> VLC_SUCCESS )
> +                    continue;
>                  else
>                      goto error;
>              }

Regards,
-- 
Denis Charmet - TypX
Le mauvais esprit est un art de vivre


More information about the vlc-devel mailing list