Commit 506b43d0 authored by François Cartegnie's avatar François Cartegnie 🤞

codec: webvtt: add CSS support

parent 649b3d02
......@@ -238,6 +238,8 @@ libwebvtt_plugin_la_SOURCES += codec/webvtt/CSSGrammar.y \
codec/webvtt/CSSLexer.l \
codec/webvtt/css_parser.c \
codec/webvtt/css_parser.h \
codec/webvtt/css_style.c \
codec/webvtt/css_style.h \
codec/webvtt/css_bridge.h
endif
......
/*****************************************************************************
* css_style.c : CSS styles conversions
*****************************************************************************
* Copyright (C) 2017 VideoLabs, VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_text_style.h>
#include "css_parser.h"
#include "css_style.h"
static void Color( vlc_css_term_t term,
int *color, uint8_t *alpha,
uint16_t *feat, int cflag, int aflag )
{
if( term.type == TYPE_FUNCTION )
{
if( term.function ) /* func( expr ) */
{
if( ( !strcmp( term.psz, "rgb" ) && term.function->i_count == 3 ) ||
( !strcmp( term.psz, "rgba" ) && term.function->i_count == 4 ) )
{
*color = (((int)term.function->seq[0].term.val) << 16) |
(((int)term.function->seq[1].term.val) << 8) |
((int)term.function->seq[2].term.val);
*feat |= cflag;
if( term.psz[3] != 0 ) /* rgba */
{
*alpha = term.function->seq[3].term.val * STYLE_ALPHA_OPAQUE;
*feat |= aflag;
}
}
}
}
else if( term.type == TYPE_STRING ||
term.type == TYPE_HEXCOLOR ||
term.type == TYPE_IDENTIFIER )
{
bool b_valid = false;
unsigned i_color = vlc_html_color( term.psz, &b_valid );
if( b_valid )
{
*alpha = (i_color & 0xFF000000) >> 24;
*color = i_color & 0x00FFFFFF;
*feat |= cflag|aflag;
}
}
}
static void OutlineWidth( vlc_css_term_t term, text_style_t *p_style )
{
if( term.type >= TYPE_PIXELS )
{
p_style->i_outline_width = term.val;
p_style->i_style_flags |= STYLE_OUTLINE;
p_style->i_features |= STYLE_HAS_FLAGS;
}
}
static void OutlineColor( vlc_css_term_t term, text_style_t *p_style )
{
Color( term, &p_style->i_outline_color, &p_style->i_outline_alpha,
&p_style->i_features, STYLE_HAS_OUTLINE_COLOR, STYLE_HAS_OUTLINE_ALPHA );
}
static void ShadowDrop( vlc_css_term_t term, text_style_t *p_style )
{
if( term.type >= TYPE_PIXELS )
{
p_style->i_shadow_width = term.val;
p_style->i_style_flags |= STYLE_SHADOW;
p_style->i_features |= STYLE_HAS_FLAGS;
}
}
static void ShadowColor( vlc_css_term_t term, text_style_t *p_style )
{
Color( term, &p_style->i_shadow_color, &p_style->i_shadow_alpha,
&p_style->i_features, STYLE_HAS_SHADOW_COLOR, STYLE_HAS_SHADOW_ALPHA );
}
void webvtt_FillStyleFromCssDeclaration( const vlc_css_declaration_t *p_decl, text_style_t *p_style )
{
if( !p_decl->psz_property || !p_style )
return;
/* Only support simple expressions for now */
if( p_decl->expr->i_count < 1 )
return;
vlc_css_term_t term0 = p_decl->expr->seq[0].term;
if( !strcmp( p_decl->psz_property, "color" ) )
{
Color( term0, &p_style->i_font_color, &p_style->i_font_alpha,
&p_style->i_features, STYLE_HAS_FONT_COLOR, STYLE_HAS_FONT_ALPHA );
}
else if( !strcmp( p_decl->psz_property, "text-decoration" ) )
{
if( term0.type == TYPE_STRING )
{
if( !strcmp( term0.psz, "none" ) )
{
p_style->i_style_flags &= ~(STYLE_STRIKEOUT|STYLE_UNDERLINE);
p_style->i_features |= STYLE_HAS_FLAGS;
}
else if( !strcmp( term0.psz, "line-through" ) )
{
p_style->i_style_flags |= STYLE_STRIKEOUT;
p_style->i_features |= STYLE_HAS_FLAGS;
}
else if( !strcmp( term0.psz, "underline" ) )
{
p_style->i_style_flags |= STYLE_UNDERLINE;
p_style->i_features |= STYLE_HAS_FLAGS;
}
}
}
else if( !strcmp( p_decl->psz_property, "text-shadow" ) )
{
ShadowDrop( term0, p_style );
if( p_decl->expr->i_count == 3 )
ShadowColor( p_decl->expr->seq[2].term, p_style );
}
else if( !strcmp( p_decl->psz_property, "background-color" ) )
{
Color( term0, &p_style->i_background_color, &p_style->i_background_alpha,
&p_style->i_features, STYLE_HAS_BACKGROUND_COLOR, STYLE_HAS_BACKGROUND_ALPHA );
p_style->i_style_flags |= STYLE_BACKGROUND;
p_style->i_features |= STYLE_HAS_FLAGS;
}
else if( !strcmp( p_decl->psz_property, "outline-color" ) )
{
OutlineColor( term0, p_style );
}
else if( !strcmp( p_decl->psz_property, "outline-width" ) )
{
OutlineWidth( term0, p_style );
}
else if( !strcmp( p_decl->psz_property, "outline" ) )
{
OutlineWidth( term0, p_style );
if( p_decl->expr->i_count == 3 )
OutlineColor( p_decl->expr->seq[2].term, p_style );
}
else if( !strcmp( p_decl->psz_property, "font-family" ) )
{
if( term0.type >= TYPE_STRING )
{
char *psz_font = NULL;
const char *psz = strchr( term0.psz, ',' );
if( psz )
psz_font = strndup( term0.psz, psz - term0.psz + 1 );
else
psz_font = strdup( term0.psz );
free( p_style->psz_fontname );
p_style->psz_fontname = vlc_css_unquoted( psz_font );
free( psz_font );
}
}
else if( !strcmp( p_decl->psz_property, "font-style" ) )
{
if( term0.type >= TYPE_STRING )
{
if( !strcmp(term0.psz, "normal") )
{
p_style->i_style_flags &= ~STYLE_ITALIC;
p_style->i_features |= STYLE_HAS_FLAGS;
}
else if( !strcmp(term0.psz, "italic") )
{
p_style->i_style_flags |= STYLE_ITALIC;
p_style->i_features |= STYLE_HAS_FLAGS;
}
}
}
else if( !strcmp( p_decl->psz_property, "font-weight" ) )
{
if( term0.type >= TYPE_STRING )
{
if( !strcmp(term0.psz, "normal") )
{
p_style->i_style_flags &= ~STYLE_BOLD;
p_style->i_features |= STYLE_HAS_FLAGS;
}
if( !strcmp(term0.psz, "bold") )
{
p_style->i_style_flags |= STYLE_BOLD;
p_style->i_features |= STYLE_HAS_FLAGS;
}
}
else if( term0.type == TYPE_NONE )
{
if( term0.val >= 700.0 )
p_style->i_style_flags |= STYLE_BOLD;
else
p_style->i_style_flags &= ~STYLE_BOLD;
p_style->i_features |= STYLE_HAS_FLAGS;
}
}
else if( !strcmp( p_decl->psz_property, "font-size" ) )
{
if( term0.type == TYPE_PIXELS )
p_style->i_font_size = term0.val;
else if( term0.type == TYPE_EMS )
p_style->f_font_relsize = term0.val * 5.33 / 1.06;
else if( term0.type == TYPE_PERCENT )
p_style->f_font_relsize = term0.val * 5.33 / 100;
}
else if( !strcmp( p_decl->psz_property, "font" ) )
{
/* what to do ? */
}
else if( !strcmp( p_decl->psz_property, "white-space" ) )
{
if( term0.type >= TYPE_STRING )
{
if( !strcmp(term0.psz, "normal" ) )
p_style->e_wrapinfo = STYLE_WRAP_DEFAULT;
if( !strcmp(term0.psz, "nowrap" ) )
p_style->e_wrapinfo = STYLE_WRAP_NONE;
}
}
}
/*****************************************************************************
* css_style.h : CSS styles conversions
*****************************************************************************
* Copyright (C) 2017 VideoLabs, VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
void webvtt_FillStyleFromCssDeclaration( const vlc_css_declaration_t *p_decl,
text_style_t *p_style );
......@@ -38,6 +38,12 @@
#include "../demux/mp4/minibox.h"
#include "webvtt.h"
#ifdef HAVE_CSS
# include "css_parser.h"
# include "css_style.h"
#endif
#include <ctype.h>
//#define SUBSVTT_DEBUG
......@@ -53,6 +59,7 @@ typedef struct webvtt_dom_cue_t webvtt_dom_cue_t;
#define WEBVTT_REGION_LINES_COUNT 18
#define WEBVTT_DEFAULT_LINE_HEIGHT_VH 5.33
#define WEBVTT_LINE_TO_HEIGHT_RATIO 1.06
#define WEBVTT_MAX_DEPTH 20 /* recursion prevention for now */
enum webvtt_align_e
{
......@@ -83,7 +90,6 @@ enum webvtt_node_type_e
NODE_TEXT,
NODE_CUE,
NODE_REGION,
NODE_VIDEO,
};
#define WEBVTT_NODE_BASE_MEMBERS \
......@@ -102,6 +108,7 @@ struct webvtt_region_t
float viewport_anchor_x;
float viewport_anchor_y;
bool b_scroll_up;
text_style_t *p_cssstyle;
webvtt_dom_node_t *p_child;
};
......@@ -113,6 +120,7 @@ struct webvtt_dom_cue_t
mtime_t i_stop;
webvtt_cue_settings_t settings;
unsigned i_lines;
text_style_t *p_cssstyle;
webvtt_dom_node_t *p_child;
};
......@@ -128,15 +136,10 @@ typedef struct
mtime_t i_start;
char *psz_tag;
char *psz_attrs;
text_style_t *p_cssstyle;
webvtt_dom_node_t *p_child;
} webvtt_dom_tag_t;
typedef struct
{
WEBVTT_NODE_BASE_MEMBERS
webvtt_dom_node_t *p_child;
} webvtt_dom_video_t;
struct webvtt_dom_node_t
{
WEBVTT_NODE_BASE_MEMBERS
......@@ -144,7 +147,11 @@ struct webvtt_dom_node_t
struct decoder_sys_t
{
webvtt_dom_video_t *p_root;
webvtt_dom_tag_t *p_root;
#ifdef HAVE_CSS
/* CSS */
vlc_css_rule_t *p_css_rules;
#endif
};
#define ATOM_iden VLC_FOURCC('i', 'd', 'e', 'n')
......@@ -334,18 +341,13 @@ static void webvtt_domnode_Debug( webvtt_dom_node_t *p_node, int i_depth )
}
}
}
#define webvtt_domnode_Debug(a,b) webvtt_domnode_Debug((webvtt_dom_node_t *)a,b)
#endif
static void webvtt_domnode_ChainDelete( webvtt_dom_node_t *p_node );
static void webvtt_dom_cue_Delete( webvtt_dom_cue_t *p_cue );
static void webvtt_region_Delete( webvtt_region_t *p_region );
static void webvtt_dom_video_Delete( webvtt_dom_video_t *p_node )
{
webvtt_domnode_ChainDelete( p_node->p_child );
free( p_node );
}
static void webvtt_dom_text_Delete( webvtt_dom_text_t *p_node )
{
free( p_node->psz_text );
......@@ -354,6 +356,7 @@ static void webvtt_dom_text_Delete( webvtt_dom_text_t *p_node )
static void webvtt_dom_tag_Delete( webvtt_dom_tag_t *p_node )
{
text_style_Delete( p_node->p_cssstyle );
free( p_node->psz_attrs );
free( p_node->psz_tag );
webvtt_domnode_ChainDelete( p_node->p_child );
......@@ -385,21 +388,11 @@ static void webvtt_domnode_ChainDelete( webvtt_dom_node_t *p_node )
webvtt_dom_cue_Delete( (webvtt_dom_cue_t *) p_node );
else if( p_node->type == NODE_REGION )
webvtt_region_Delete( (webvtt_region_t *) p_node );
else if( p_node->type == NODE_VIDEO )
webvtt_dom_video_Delete( (webvtt_dom_video_t *) p_node );
p_node = p_next;
}
}
static webvtt_dom_video_t * webvtt_dom_video_New( void )
{
webvtt_dom_video_t *p_node = calloc( 1, sizeof(*p_node) );
if( p_node )
p_node->type = NODE_VIDEO;
return p_node;
}
static webvtt_dom_text_t * webvtt_dom_text_New( webvtt_dom_node_t *p_parent )
{
webvtt_dom_text_t *p_node = calloc( 1, sizeof(*p_node) );
......@@ -438,7 +431,7 @@ static webvtt_dom_node_t * webvtt_domnode_getParentByTag( webvtt_dom_node_t *p_p
return p_parent;
}
static const webvtt_dom_node_t * webvtt_domnode_getFirstChild( const webvtt_dom_node_t *p_node )
static webvtt_dom_node_t * webvtt_domnode_getFirstChild( webvtt_dom_node_t *p_node )
{
webvtt_dom_node_t *p_child = NULL;
switch( p_node->type )
......@@ -457,6 +450,336 @@ static const webvtt_dom_node_t * webvtt_domnode_getFirstChild( const webvtt_dom_
}
return p_child;
}
#define webvtt_domnode_getFirstChild(a) webvtt_domnode_getFirstChild((webvtt_dom_node_t *)a)
static mtime_t webvtt_domnode_GetPlaybackTime( const webvtt_dom_node_t *p_node, bool b_end )
{
for( ; p_node; p_node = p_node->p_parent )
{
if( p_node->type == NODE_TAG )
{
mtime_t i_start = ((const webvtt_dom_tag_t *) p_node)->i_start;
if( i_start > -1 && !b_end )
return i_start;
}
else if( p_node->type == NODE_CUE )
{
break;
}
}
if( p_node )
return b_end ? ((const webvtt_dom_cue_t *) p_node)->i_stop:
((const webvtt_dom_cue_t *) p_node)->i_start;
return VLC_TS_INVALID;
}
#ifdef HAVE_CSS
static bool webvtt_domnode_Match_Class( const webvtt_dom_node_t *p_node, const char *psz )
{
const size_t i_len = strlen( psz );
if( p_node->type == NODE_TAG )
{
const webvtt_dom_tag_t *p_tagnode = (webvtt_dom_tag_t *) p_node;
while( p_tagnode->psz_attrs && psz )
{
const char *p = strstr( p_tagnode->psz_attrs, psz );
if( !p )
return false;
if( p > psz && p[-1] == '.' && !isalnum(p[i_len]) )
return true;
psz = p + 1;
}
}
return false;
}
static bool webvtt_domnode_Match_Id( const webvtt_dom_node_t *p_node, const char *psz_id )
{
if( !psz_id )
return false;
if( *psz_id == '#' )
psz_id++;
if( p_node->type == NODE_REGION )
return ((webvtt_region_t *)p_node)->psz_id &&
!strcmp( ((webvtt_region_t *)p_node)->psz_id, psz_id );
else if( p_node->type == NODE_CUE )
return ((webvtt_dom_cue_t *)p_node)->psz_id &&
!strcmp( ((webvtt_dom_cue_t *)p_node)->psz_id, psz_id );
return false;
}
static bool webvtt_domnode_Match_Tag( const webvtt_dom_node_t *p_node, const char *psz_tag )
{
if( p_node->type == NODE_TAG && psz_tag )
{
/* special case, not allowed to match anywhere but root */
if( !strcmp(psz_tag, "video") && p_node->p_parent )
return false;
return ((webvtt_dom_tag_t *)p_node)->psz_tag &&
!strcmp( ((webvtt_dom_tag_t *)p_node)->psz_tag, psz_tag );
}
else return false;
}
static bool webvtt_domnode_Match_PseudoClass( const webvtt_dom_node_t *p_node, const char *psz,
mtime_t i_playbacktime )
{
if( !strcmp(psz, "past") || !strcmp(psz, "future") )
{
mtime_t i_start = webvtt_domnode_GetPlaybackTime( p_node, false );
return ( *psz == 'p' ) ? i_start < i_playbacktime : i_start > i_playbacktime;
}
return false;
}
static bool webvtt_domnode_Match_PseudoElement( const webvtt_dom_node_t *p_node, const char *psz )
{
if( !strcmp(psz, "cue") )
return p_node->type == NODE_CUE;
else if( !strcmp(psz, "cue-region") )
return p_node->type == NODE_REGION;
return false;
}
static bool MatchAttribute( const char *psz_attr, const char *psz_lookup, enum vlc_css_match_e match )
{
switch( match )
{
case MATCH_EQUALS:
return !strcmp( psz_attr, psz_lookup );
case MATCH_INCLUDES:
{
const char *p = strstr( psz_attr, psz_lookup );
if( p && ( p == psz_attr || isspace(p[-1]) ) )
{
const char *end = p + strlen( psz_lookup );
return (*end == 0 || isspace(*end));
}
break;
}
case MATCH_DASHMATCH:
{
size_t i_len = strlen(psz_lookup);
if( !strncmp( psz_attr, psz_lookup, i_len ) )
{
const char *end = psz_attr + i_len;
return (*end == 0 || !isalnum(*end) );
}
break;
}
case MATCH_BEGINSWITH:
return !strncmp( psz_attr, psz_lookup, strlen(psz_lookup) );
case MATCH_ENDSWITH:
{
const char *p = strstr( psz_attr, psz_lookup );
return (p && *p && p[1] == 0);
}
case MATCH_CONTAINS:
return !!strstr( psz_attr, psz_lookup );
default:
break;
}
return false;
}
static bool webvtt_domnode_Match_Attribute( const webvtt_dom_node_t *p_node,
const char *psz, const vlc_css_selector_t *p_matchsel )
{
if( p_node->type == NODE_TAG && p_matchsel )
{
const webvtt_dom_tag_t *p_tagnode = (webvtt_dom_tag_t *) p_node;
if( ( !strcmp( p_tagnode->psz_tag, "v" ) && !strcmp( psz, "voice" ) ) || /* v = only voice */
( !strcmp( p_tagnode->psz_tag, "lang" ) && !strcmp( psz, "lang" ) ) )
{
const char *psz_start = NULL;
/* skip classes decl */
for( const char *p = p_tagnode->psz_attrs; *p; p++ )
{
if( isspace(*p) )
{
psz_start = p + 1;
}
else if( psz_start != NULL )
{
break;
}
}
if( psz_start == NULL || *psz_start == 0 )
psz_start = p_tagnode->psz_attrs;
if( !p_matchsel ) /* attribute check only */
return strlen( psz_start ) > 0;
return MatchAttribute( psz_start, p_matchsel->psz_name, p_matchsel->match );
}
}
return false;
}
static bool webvtt_domnode_MatchType( decoder_t *p_dec, const webvtt_dom_node_t *p_node,
const vlc_css_selector_t *p_sel, mtime_t i_playbacktime )
{
VLC_UNUSED(p_dec);
switch( p_sel->type )
{
case SELECTOR_SIMPLE:
return webvtt_domnode_Match_Tag( p_node, p_sel->psz_name );
case SELECTOR_PSEUDOCLASS:
return webvtt_domnode_Match_PseudoClass( p_node, p_sel->psz_name,
i_playbacktime );
case SELECTOR_PSEUDOELEMENT:
return webvtt_domnode_Match_PseudoElement( p_node, p_sel->psz_name );
case SPECIFIER_ID:
return webvtt_domnode_Match_Id( p_node, p_sel->psz_name );
case SPECIFIER_CLASS:
return webvtt_domnode_Match_Class( p_node, p_sel->psz_name );
case SPECIFIER_ATTRIB:
return webvtt_domnode_Match_Attribute( p_node, p_sel->psz_name, p_sel->p_matchsel );
}
return false;
}
#endif
static text_style_t ** get_ppCSSStyle( webvtt_dom_node_t *p_node )
{
switch( p_node->type )
{
case NODE_CUE:
return &((webvtt_dom_cue_t *)p_node)->p_cssstyle;
case NODE_REGION:
return &((webvtt_region_t *)p_node)->p_cssstyle;
case NODE_TAG:
return &((webvtt_dom_tag_t *)p_node)->p_cssstyle;
default:
return NULL;
}
}
static text_style_t * webvtt_domnode_getCSSStyle( webvtt_dom_node_t *p_node )
{
text_style_t **pp_style = get_ppCSSStyle( p_node );
if( pp_style )
return *pp_style;
return NULL;
}
#define webvtt_domnode_getCSSStyle(a) webvtt_domnode_getCSSStyle((webvtt_dom_node_t *)a)
static bool webvtt_domnode_supportsCSSStyle( webvtt_dom_node_t *p_node )
{
return get_ppCSSStyle( p_node ) != NULL;
}
static void webvtt_domnode_setCSSStyle( webvtt_dom_node_t *p_node, text_style_t *p_style )
{
text_style_t **pp_style = get_ppCSSStyle( p_node );
if( !pp_style )
{
assert( pp_style );
if( p_style )
text_style_Delete( p_style );
return;
}
if( *pp_style )
text_style_Delete( *pp_style );
*pp_style = p_style;
}
#ifdef HAVE_CSS
static void webvtt_domnode_SelectNodesInTree( decoder_t *p_dec, const vlc_css_selector_t *p_sel,
const webvtt_dom_node_t *p_tree, int i_max_depth,
mtime_t i_playbacktime, vlc_array_t *p_results );
static void webvtt_domnode_SelectChildNodesInTree( decoder_t *p_dec, const vlc_css_selector_t *p_sel,
const webvtt_dom_node_t *p_root, int i_max_depth,
mtime_t i_playbacktime, vlc_array_t *p_results )
{
const webvtt_dom_node_t *p_child = webvtt_domnode_getFirstChild( p_root );
if( i_max_depth > 0 )
{
for( ; p_child; p_child = p_child->p_next )
webvtt_domnode_SelectNodesInTree( p_dec, p_sel, p_child, i_max_depth - 1,
i_playbacktime, p_results );
}
}
static void webvtt_domnode_SelectNodesBySpeficier( decoder_t *p_dec, const vlc_css_selector_t *p_spec,
const webvtt_dom_node_t *p_node,
mtime_t i_playbacktime, vlc_array_t *p_results )
{
if( p_spec == NULL )
return;
switch( p_spec->combinator )
{
case RELATION_DESCENDENT:
webvtt_domnode_SelectChildNodesInTree( p_dec, p_spec, p_node, WEBVTT_MAX_DEPTH,
i_playbacktime, p_results );
break;
case RELATION_DIRECTADJACENT:
for( const webvtt_dom_node_t *p_adj = p_node->p_next; p_adj; p_adj = p_adj->p_next )
webvtt_domnode_SelectChildNodesInTree( p_dec, p_spec, p_adj, 1,
i_playbacktime, p_results );
break;
case RELATION_INDIRECTADJACENT:
for( const webvtt_dom_node_t *p_adj = webvtt_domnode_getFirstChild( p_node->p_parent );
p_adj && p_adj != p_node; p_adj = p_adj->p_next )
webvtt_domnode_SelectChildNodesInTree( p_dec, p_spec, p_adj, 1,
i_playbacktime, p_results );
break;
case RELATION_CHILD:
webvtt_domnode_SelectChildNodesInTree( p_dec, p_spec, p_node, 1,
i_playbacktime, p_results );
break;
case RELATION_SELF:
webvtt_domnode_SelectNodesInTree( p_dec, p_spec, p_node, WEBVTT_MAX_DEPTH,
i_playbacktime, p_results );
}
}
static void webvtt_domnode_SelectNodesInTree( decoder_t *p_dec, const vlc_css_selector_t *p_sel,
const webvtt_dom_node_t *p_root, int i_max_depth,
mtime_t i_playbacktime, vlc_array_t *p_results )
{
if( p_root == NULL )
return;
if( webvtt_domnode_MatchType( p_dec, p_root, p_sel, i_playbacktime ) )
{
if( p_sel->specifiers.p_first == NULL )
{
/* End of matching, this node is part of results */
(void) vlc_array_append( p_results, (void *) p_root );
}
else webvtt_domnode_SelectNodesBySpeficier( p_dec, p_sel->specifiers.p_first, p_root,
i_playbacktime, p_results );
}
/* lookup other subnodes */
webvtt_domnode_SelectChildNodesInTree( p_dec, p_sel, p_root, i_max_depth - 1,
i_playbacktime, p_results );
}
static void webvtt_domnode_SelectRuleNodes( decoder_t *p_dec, const vlc_css_rule_t *p_rule,
mtime_t i_playbacktime, vlc_array_t *p_results )
{
const webvtt_dom_node_t *p_cues = p_dec->p_sys->p_root->p_child;
for( const vlc_css_selector_t *p_sel = p_rule->p_selectors; p_sel; p_sel = p_sel->p_next )
{
vlc_array_t tempresults;