static text_segment_t* NewTextSegmentPushStyle( text_segment_t* p_segment, style_stack_t** pp_stack ) { text_segment_t* p_new = text_segment_New( NULL ); if ( unlikely( p_new == NULL ) ) return NULL; text_style_t* p_style = DuplicateAndPushStyle( pp_stack ); p_new->style = p_style; p_segment->p_next = p_new; return p_new; }
static text_segment_t* NewTextSegmentPopStyle( text_segment_t* p_segment, style_stack_t** pp_stack ) { text_segment_t* p_new = text_segment_New( NULL ); if ( unlikely( p_new == NULL ) ) return NULL; // We shouldn't have an empty stack since this happens when closing a tag, // but better be safe than sorry if (/when) we encounter a broken subtitle file. PopStyle( pp_stack ); text_style_t* p_dup = ( *pp_stack ) ? text_style_Duplicate( (*pp_stack)->p_style ) : text_style_Create( STYLE_NO_DEFAULTS ); p_new->style = p_dup; p_segment->p_next = p_new; return p_new; }
static subpicture_t *Decode(decoder_t *dec, block_t **block) { if (block == NULL || *block == NULL) return NULL; subpicture_t *sub = NULL; block_t *b = *block; *block = NULL; if (b->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) goto exit; if (b->i_buffer < 128) goto exit; int payload_size = (b->i_buffer / 128) * 112; uint8_t *payload = malloc(payload_size); if (!payload) goto exit; for (unsigned i = 0; i < b->i_buffer / 128; i++) memcpy(&payload[112 * i], &b->p_buffer[128 * i + 16], 112); sub = decoder_NewSubpicture(dec, NULL); if (!sub) { free(payload); goto exit; } sub->i_start = b->i_pts; sub->i_stop = b->i_pts + b->i_length; sub->b_ephemer = b->i_length == 0; sub->b_absolute = false; //sub->i_original_picture_width = 0; //sub->i_original_picture_height = 0; video_format_t fmt; video_format_Init(&fmt, VLC_CODEC_TEXT); sub->p_region = subpicture_region_New(&fmt); video_format_Clean(&fmt); if (sub->p_region) { sub->p_region->p_text = text_segment_New( ParseText(payload, payload_size, cct_nums[dec->p_sys->cct - CCT_BEGIN].str) ); sub->p_region->i_align = SUBPICTURE_ALIGN_BOTTOM; } free(payload); exit: block_Release(b); return sub; }
text_segment_t *text_segment_NewInheritStyle( const text_style_t* p_style ) { if ( !p_style ) return NULL; //FIXME: Allow this, even if it is an alias to text_segment_New( NULL ) ? text_segment_t* p_segment = text_segment_New( NULL ); if ( unlikely( !p_segment ) ) return NULL; p_segment->style = text_style_Duplicate( p_style ); if ( unlikely( !p_segment->style ) ) { text_segment_Delete( p_segment ); return NULL; } return p_segment; }
static tx3g_segment_t * tx3g_segment_New( const char *psz_string ) { tx3g_segment_t *p_seg = malloc( sizeof(tx3g_segment_t) ); if( p_seg ) { p_seg->i_size = 0; p_seg->p_next3g = NULL; p_seg->s = text_segment_New( psz_string ); if( !p_seg->s ) { free( p_seg ); p_seg = NULL; } } return p_seg; }
text_segment_t *text_segment_Copy( text_segment_t *p_src ) { text_segment_t *p_dst = NULL, *p_dst0 = NULL; while( p_src ) { text_segment_t *p_new = text_segment_New( p_src->psz_text ); if( p_new ) p_new->style = text_style_Duplicate( p_src->style ); if( p_dst == NULL ) { p_dst = p_dst0 = p_new; } else { p_dst->p_next = p_new; p_dst = p_dst->p_next; } p_src = p_src->p_next; } return p_dst0; }
static text_segment_t* ParseSubtitles( int *pi_align, const char *psz_subtitle ) { text_segment_t* p_segment; text_segment_t* p_first_segment; style_stack_t* p_stack = NULL; tag_stack_t* p_tag_stack = NULL; //FIXME: Remove initial allocation? Might make the below code more complicated p_first_segment = p_segment = text_segment_New( "" ); bool b_has_align = false; /* */ while( *psz_subtitle ) { /* HTML extensions */ if( *psz_subtitle == '<' ) { char *psz_tagname = GetTag( &psz_subtitle, false ); if ( psz_tagname != NULL ) { if( !strcasecmp( psz_tagname, "br" ) ) { if ( !AppendCharacter( p_segment, '\n' ) ) { free( psz_tagname ); goto fail; } } else if( !strcasecmp( psz_tagname, "b" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_BOLD; p_segment->style->i_features |= STYLE_HAS_FLAGS; } else if( !strcasecmp( psz_tagname, "i" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_ITALIC; p_segment->style->i_features |= STYLE_HAS_FLAGS; } else if( !strcasecmp( psz_tagname, "u" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_UNDERLINE; p_segment->style->i_features |= STYLE_HAS_FLAGS; } else if( !strcasecmp( psz_tagname, "s" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_STRIKEOUT; p_segment->style->i_features |= STYLE_HAS_FLAGS; } else if( !strcasecmp( psz_tagname, "font" ) ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); char* psz_attribute_name; char* psz_attribute_value; while( ( psz_attribute_name = ConsumeAttribute( &psz_subtitle, &psz_attribute_value ) ) ) { if ( !strcasecmp( psz_attribute_name, "face" ) ) { p_segment->style->psz_fontname = psz_attribute_value; // We don't want to free the attribute value since it has become our fontname psz_attribute_value = NULL; } else if ( !strcasecmp( psz_attribute_name, "family" ) ) { p_segment->style->psz_monofontname = psz_attribute_value; psz_attribute_value = NULL; } else if ( !strcasecmp( psz_attribute_name, "size" ) ) { int size = atoi( psz_attribute_value ); if( size ) { p_segment->style->i_font_size = size; p_segment->style->f_font_relsize = STYLE_DEFAULT_REL_FONT_SIZE * STYLE_DEFAULT_FONT_SIZE / p_segment->style->i_font_size; } } else if ( !strcasecmp( psz_attribute_name, "color" ) ) { p_segment->style->i_font_color = vlc_html_color( psz_attribute_value, NULL ); p_segment->style->i_features |= STYLE_HAS_FONT_COLOR; } else if ( !strcasecmp( psz_attribute_name, "outline-color" ) ) { p_segment->style->i_outline_color = vlc_html_color( psz_attribute_value, NULL ); p_segment->style->i_features |= STYLE_HAS_OUTLINE_COLOR; } else if ( !strcasecmp( psz_attribute_name, "shadow-color" ) ) { p_segment->style->i_shadow_color = vlc_html_color( psz_attribute_value, NULL ); p_segment->style->i_features |= STYLE_HAS_SHADOW_COLOR; } else if ( !strcasecmp( psz_attribute_name, "outline-level" ) ) { p_segment->style->i_outline_width = atoi( psz_attribute_value ); } else if ( !strcasecmp( psz_attribute_name, "shadow-level" ) ) { p_segment->style->i_shadow_width = atoi( psz_attribute_value ); } else if ( !strcasecmp( psz_attribute_name, "back-color" ) ) { p_segment->style->i_background_color = vlc_html_color( psz_attribute_value, NULL ); p_segment->style->i_features |= STYLE_HAS_BACKGROUND_COLOR; } else if ( !strcasecmp( psz_attribute_name, "alpha" ) ) { p_segment->style->i_font_alpha = atoi( psz_attribute_value ); p_segment->style->i_features |= STYLE_HAS_FONT_ALPHA; } free( psz_attribute_name ); free( psz_attribute_value ); } } else { // This is an unknown tag. We need to hide it if it's properly closed, and display it otherwise if ( !IsClosed( psz_subtitle, psz_tagname ) ) { AppendCharacter( p_segment, '<' ); AppendString( p_segment, psz_tagname ); AppendCharacter( p_segment, '>' ); } else { AppendTag( &p_tag_stack, psz_tagname ); // We don't want to free the tagname now, it will be freed when the tag // gets poped from the stack. psz_tagname = NULL; } // In any case, fall through and skip to the closing tag. } // Skip potential spaces & end tag while ( *psz_subtitle && *psz_subtitle != '>' ) psz_subtitle++; if ( *psz_subtitle == '>' ) psz_subtitle++; free( psz_tagname ); } else if( !strncmp( psz_subtitle, "</", 2 )) { char* psz_tagname = GetTag( &psz_subtitle, true ); if ( psz_tagname != NULL ) { if ( !strcasecmp( psz_tagname, "b" ) || !strcasecmp( psz_tagname, "i" ) || !strcasecmp( psz_tagname, "u" ) || !strcasecmp( psz_tagname, "s" ) || !strcasecmp( psz_tagname, "font" ) ) { // A closing tag for one of the tags we handle, meaning // we pushed a style onto the stack earlier p_segment = NewTextSegmentPopStyle( p_segment, &p_stack ); } else { // Unknown closing tag. If it is closing an unknown tag, ignore it. Otherwise, display it if ( !HasTag( &p_tag_stack, psz_tagname ) ) { AppendString( p_segment, "</" ); AppendString( p_segment, psz_tagname ); AppendCharacter( p_segment, '>' ); } } while ( *psz_subtitle == ' ' ) psz_subtitle++; if ( *psz_subtitle == '>' ) psz_subtitle++; free( psz_tagname ); } } else { /* We have an unknown tag, just append it, and move on. * The rest of the string won't be recognized as a tag, and * we will ignore unknown closing tag */ AppendCharacter( p_segment, '<' ); psz_subtitle++; } } /* SSA extensions */ else if( psz_subtitle[0] == '{' && psz_subtitle[1] == '\\' && strchr( psz_subtitle, '}' ) ) { /* Check for forced alignment */ if( !b_has_align && !strncmp( psz_subtitle, "{\\an", 4 ) && psz_subtitle[4] >= '1' && psz_subtitle[4] <= '9' && psz_subtitle[5] == '}' ) { static const int pi_vertical[3] = { SUBPICTURE_ALIGN_BOTTOM, 0, SUBPICTURE_ALIGN_TOP }; static const int pi_horizontal[3] = { SUBPICTURE_ALIGN_LEFT, 0, SUBPICTURE_ALIGN_RIGHT }; const int i_id = psz_subtitle[4] - '1'; b_has_align = true; *pi_align = pi_vertical[i_id/3] | pi_horizontal[i_id%3]; } /* TODO fr -> rotation */ /* Hide {\stupidity} */ psz_subtitle = strchr( psz_subtitle, '}' ) + 1; } /* MicroDVD extensions */ /* FIXME: * - Currently, we don't do difference between X and x, and we should: * Capital Letters applies to the whole text and not one line * - We don't support Position and Coordinates * - We don't support the DEFAULT flag (HEADER) */ else if( psz_subtitle[0] == '{' && psz_subtitle[2] == ':' && strchr( &psz_subtitle[2], '}' ) ) { const char *psz_tag_end = strchr( &psz_subtitle[2], '}' ); size_t i_len = psz_tag_end - &psz_subtitle[3]; if( psz_subtitle[1] == 'Y' || psz_subtitle[1] == 'y' ) { if( psz_subtitle[3] == 'i' ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_ITALIC; p_segment->style->i_features |= STYLE_HAS_FLAGS; psz_subtitle++; } if( psz_subtitle[3] == 'b' ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_BOLD; p_segment->style->i_features |= STYLE_HAS_FLAGS; psz_subtitle++; } if( psz_subtitle[3] == 'u' ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_style_flags |= STYLE_UNDERLINE; p_segment->style->i_features |= STYLE_HAS_FLAGS; psz_subtitle++; } } else if( (psz_subtitle[1] == 'C' || psz_subtitle[1] == 'c' ) && psz_subtitle[3] == '$' && i_len >= 7 ) { /* Yes, they use BBGGRR, instead of RRGGBB */ char psz_color[7]; psz_color[0] = psz_subtitle[8]; psz_color[1] = psz_subtitle[9]; psz_color[2] = psz_subtitle[6]; psz_color[3] = psz_subtitle[7]; psz_color[4] = psz_subtitle[4]; psz_color[5] = psz_subtitle[5]; psz_color[6] = '\0'; p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_font_color = vlc_html_color( psz_color, NULL ); p_segment->style->i_features |= STYLE_HAS_FONT_COLOR; } else if( psz_subtitle[1] == 'F' || psz_subtitle[1] == 'f' ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->psz_fontname = strndup( &psz_subtitle[3], i_len ); } else if( psz_subtitle[1] == 'S' || psz_subtitle[1] == 's' ) { int size = atoi( &psz_subtitle[3] ); if( size ) { p_segment = NewTextSegmentPushStyle( p_segment, &p_stack ); p_segment->style->i_font_size = size; p_segment->style->f_font_relsize = STYLE_DEFAULT_REL_FONT_SIZE * STYLE_DEFAULT_FONT_SIZE / p_segment->style->i_font_size; } } /* Currently unsupported since we don't have access to the i_align flag here else if( psz_subtitle[1] == 'P' ) { if( psz_subtitle[3] == "1" ) i_align = SUBPICTURE_ALIGN_TOP; else if( psz_subtitle[3] == "0" ) i_align = SUBPICTURE_ALIGN_BOTTOM; } */ // Hide other {x:y} atrocities, notably {o:x} psz_subtitle = psz_tag_end + 1; } else { if( *psz_subtitle == '\n' || !strncasecmp( psz_subtitle, "\\n", 2 ) ) { if ( !AppendCharacter( p_segment, '\n' ) ) goto fail; if ( *psz_subtitle == '\n' ) psz_subtitle++; else psz_subtitle += 2; } else if( !strncasecmp( psz_subtitle, "\\h", 2 ) ) { if ( !AppendString( p_segment, "\xC2\xA0" ) ) goto fail; psz_subtitle += 2; } else { //FIXME: Highly inneficient AppendCharacter( p_segment, *psz_subtitle ); psz_subtitle++; } } } while ( p_stack ) PopStyle( &p_stack ); while ( p_tag_stack ) { tag_stack_t *p_tag = p_tag_stack; p_tag_stack = p_tag_stack->p_next; free( p_tag->psz_tagname ); free( p_tag ); } return p_first_segment; fail: text_segment_ChainDelete( p_first_segment ); return NULL; }
/***************************************************************************** * Decode: *****************************************************************************/ static subpicture_t *Decode( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; block_t *p_block; subpicture_t *p_spu = NULL; video_format_t fmt; bool b_cached = false; vbi_page p_page; if( (pp_block == NULL) || (*pp_block == NULL) ) return NULL; p_block = *pp_block; *pp_block = NULL; if( p_block->i_buffer > 0 && ( ( p_block->p_buffer[0] >= 0x10 && p_block->p_buffer[0] <= 0x1f ) || ( p_block->p_buffer[0] >= 0x99 && p_block->p_buffer[0] <= 0x9b ) ) ) { vbi_sliced *p_sliced = p_sys->p_vbi_sliced; unsigned int i_lines = 0; p_block->i_buffer--; p_block->p_buffer++; while( p_block->i_buffer >= 2 ) { int i_id = p_block->p_buffer[0]; unsigned i_size = p_block->p_buffer[1]; if( 2 + i_size > p_block->i_buffer ) break; if( ( i_id == 0x02 || i_id == 0x03 ) && i_size >= 44 && i_lines < MAX_SLICES ) { if(p_block->p_buffer[3] == 0xE4 ) /* framing_code */ { unsigned line_offset = p_block->p_buffer[2] & 0x1f; unsigned field_parity = p_block->p_buffer[2] & 0x20; p_sliced[i_lines].id = VBI_SLICED_TELETEXT_B; if( line_offset > 0 ) p_sliced[i_lines].line = line_offset + (field_parity ? 0 : 313); else p_sliced[i_lines].line = 0; for( int i = 0; i < 42; i++ ) p_sliced[i_lines].data[i] = vbi_rev8( p_block->p_buffer[4 + i] ); i_lines++; } } p_block->i_buffer -= 2 + i_size; p_block->p_buffer += 2 + i_size; } if( i_lines > 0 ) vbi_decode( p_sys->p_vbi_dec, p_sliced, i_lines, 0 ); } /* */ vlc_mutex_lock( &p_sys->lock ); const int i_align = p_sys->i_align; const unsigned int i_wanted_page = p_sys->i_wanted_page; const unsigned int i_wanted_subpage = p_sys->i_wanted_subpage; const bool b_opaque = p_sys->b_opaque; vlc_mutex_unlock( &p_sys->lock ); /* Try to see if the page we want is in the cache yet */ memset( &p_page, 0, sizeof(vbi_page) ); b_cached = vbi_fetch_vt_page( p_sys->p_vbi_dec, &p_page, vbi_dec2bcd( i_wanted_page ), i_wanted_subpage, VBI_WST_LEVEL_3p5, 25, true ); if( i_wanted_page == p_sys->i_last_page && !p_sys->b_update ) goto error; if( !b_cached ) { if( p_sys->b_text && p_sys->i_last_page != i_wanted_page ) { /* We need to reset the subtitle */ p_spu = Subpicture( p_dec, &fmt, true, p_page.columns, p_page.rows, i_align, p_block->i_pts ); if( !p_spu ) goto error; subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys; p_spu_sys->p_segments = text_segment_New(""); p_sys->b_update = true; p_sys->i_last_page = i_wanted_page; goto exit; } goto error; } p_sys->b_update = false; p_sys->i_last_page = i_wanted_page; #ifdef ZVBI_DEBUG msg_Dbg( p_dec, "we now have page: %d ready for display", i_wanted_page ); #endif /* Ignore transparent rows at the beginning and end */ int i_first_row = get_first_visible_row( p_page.text, p_page.rows, p_page.columns ); int i_num_rows; if ( i_first_row < 0 ) { i_first_row = p_page.rows - 1; i_num_rows = 0; } else { i_num_rows = get_last_visible_row( p_page.text, p_page.rows, p_page.columns ) - i_first_row + 1; } #ifdef ZVBI_DEBUG msg_Dbg( p_dec, "After top and tail of page we have rows %i-%i of %i", i_first_row + 1, i_first_row + i_num_rows, p_page.rows ); #endif /* If there is a page or sub to render, then we do that here */ /* Create the subpicture unit */ p_spu = Subpicture( p_dec, &fmt, p_sys->b_text, p_page.columns, i_num_rows, i_align, p_block->i_pts ); if( !p_spu ) goto error; if( p_sys->b_text ) { unsigned int i_textsize = 7000; int i_total,offset; char p_text[i_textsize+1]; i_total = vbi_print_page_region( &p_page, p_text, i_textsize, "UTF-8", 0, 0, 0, i_first_row, p_page.columns, i_num_rows ); for( offset=1; offset<i_total && isspace( p_text[i_total-offset ] ); offset++) p_text[i_total-offset] = '\0'; i_total -= offset; offset=0; while( offset < i_total && isspace( p_text[offset] ) ) offset++; subpicture_updater_sys_t *p_spu_sys = p_spu->updater.p_sys; p_spu_sys->p_segments = text_segment_New( &p_text[offset] ); if( p_spu_sys->p_segments && b_opaque ) { p_spu_sys->p_segments->style = text_style_Create( STYLE_NO_DEFAULTS ); if( p_spu_sys->p_segments->style ) { /* Set text background */ p_spu_sys->p_segments->style->i_style_flags = STYLE_BACKGROUND; p_spu_sys->p_segments->style->i_features |= STYLE_HAS_FLAGS; } } p_spu_sys->align = i_align; p_spu_sys->noregionbg = true; #ifdef ZVBI_DEBUG msg_Info( p_dec, "page %x-%x(%d)\n\"%s\"", p_page.pgno, p_page.subno, i_total, &p_text[offset] ); #endif } else { picture_t *p_pic = p_spu->p_region->p_picture; /* ZVBI is stupid enough to assume pitch == width */ p_pic->p->i_pitch = 4 * fmt.i_width; /* Maintain subtitle postion */ p_spu->p_region->i_y = i_first_row*10; p_spu->i_original_picture_width = p_page.columns*12; p_spu->i_original_picture_height = p_page.rows*10; vbi_draw_vt_page_region( &p_page, ZVBI_PIXFMT_RGBA32, p_spu->p_region->p_picture->p->p_pixels, -1, 0, i_first_row, p_page.columns, i_num_rows, 1, 1); vlc_mutex_lock( &p_sys->lock ); memcpy( p_sys->nav_link, &p_page.nav_link, sizeof( p_sys->nav_link )) ; vlc_mutex_unlock( &p_sys->lock ); OpaquePage( p_pic, &p_page, fmt, b_opaque, i_first_row * p_page.columns ); } exit: vbi_unref_page( &p_page ); block_Release( p_block ); return p_spu; error: vbi_unref_page( &p_page ); block_Release( p_block ); return NULL; }
static subpicture_region_t *CreateTextRegion( decoder_t *p_dec, char *psz_subtitle, int i_len, int i_sys_align ) { decoder_sys_t *p_sys = p_dec->p_sys; subpicture_region_t *p_text_region; video_format_t fmt; VLC_UNUSED( i_len ); /* Create a new subpicture region */ memset( &fmt, 0, sizeof(video_format_t) ); fmt.i_chroma = VLC_CODEC_TEXT; fmt.i_width = fmt.i_height = 0; fmt.i_x_offset = fmt.i_y_offset = 0; p_text_region = subpicture_region_New( &fmt ); if( p_text_region != NULL ) { ssa_style_t *p_ssa_style = NULL; p_ssa_style = ParseStyle( p_sys, psz_subtitle ); if( !p_ssa_style ) { int i; for( i = 0; i < p_sys->i_ssa_styles; i++ ) { if( !strcasecmp( p_sys->pp_ssa_styles[i]->psz_stylename, "Default" ) ) p_ssa_style = p_sys->pp_ssa_styles[i]; } } if( p_ssa_style ) { msg_Dbg( p_dec, "style is: %s", p_ssa_style->psz_stylename ); p_text_region->i_align = p_ssa_style->i_align; /* TODO: Setup % based offsets properly, without adversely affecting * everything else in vlc. Will address with separate patch, * to prevent this one being any more complicated. * p_ssa_style->i_margin_percent_h; * p_ssa_style->i_margin_percent_v; */ p_text_region->i_x = p_ssa_style->i_margin_h; p_text_region->i_y = p_ssa_style->i_margin_v; p_text_region->p_text = text_segment_NewInheritStyle( p_ssa_style->p_style ); } else { p_text_region->i_align = SUBPICTURE_ALIGN_BOTTOM | i_sys_align; p_text_region->i_x = i_sys_align ? 20 : 0; p_text_region->i_y = 10; p_text_region->p_text = text_segment_New( NULL ); } /* Look for position arguments which may override the style-based * defaults. */ SetupPositions( p_text_region, psz_subtitle ); p_text_region->p_next = NULL; } return p_text_region; }
/***************************************************************************** * Render: displays previously rendered output ***************************************************************************** * This function send the currently rendered image to adjust modified image, * waits until it is displayed and switch the two rendering buffers, preparing * next frame. *****************************************************************************/ static subpicture_t *Filter( filter_t *p_filter, vlc_tick_t date ) { filter_sys_t *p_sys = p_filter->p_sys; /* We might need to open these at any time. */ vlc_mutex_lock( &p_sys->lock ); if( p_sys->i_inputfd == -1 ) { p_sys->i_inputfd = vlc_open( p_sys->psz_inputfile, O_RDONLY | O_NONBLOCK ); if( p_sys->i_inputfd == -1 ) { msg_Warn( p_filter, "Failed to grab input file: %s (%s)", p_sys->psz_inputfile, vlc_strerror_c(errno) ); } else { msg_Info( p_filter, "Grabbed input file: %s", p_sys->psz_inputfile ); } } if( p_sys->i_outputfd == -1 ) { p_sys->i_outputfd = vlc_open( p_sys->psz_outputfile, O_WRONLY | O_NONBLOCK ); if( p_sys->i_outputfd == -1 ) { if( errno != ENXIO ) { msg_Warn( p_filter, "Failed to grab output file: %s (%s)", p_sys->psz_outputfile, vlc_strerror_c(errno) ); } } else { msg_Info( p_filter, "Grabbed output file: %s", p_sys->psz_outputfile ); } } vlc_mutex_unlock( &p_sys->lock ); /* Read any waiting commands */ if( p_sys->i_inputfd != -1 ) { char p_buffer[1024]; ssize_t i_len = read( p_sys->i_inputfd, p_buffer, 1024 ); if( i_len == -1 ) { /* We hit an error */ if( errno != EAGAIN ) { msg_Warn( p_filter, "Error on input file: %s", vlc_strerror_c(errno) ); vlc_close( p_sys->i_inputfd ); p_sys->i_inputfd = -1; } } else if( i_len == 0 ) { /* We hit the end-of-file */ } else { BufferAdd( &p_sys->input, p_buffer, i_len ); } } /* Parse any complete commands */ char *p_end, *p_cmd; while( ( p_end = memchr( p_sys->input.p_begin, '\n', p_sys->input.i_length ) ) ) { commanddesc_t *p_cur = NULL; bool b_found = false; size_t i_index = 0; *p_end = '\0'; p_cmd = BufferGetToken( &p_sys->input ); msg_Info( p_filter, "Search command: %s", p_cmd ); for( i_index = 0; i_index < p_sys->i_commands; i_index++ ) { p_cur = p_sys->pp_commands[i_index]; if( !strncmp( p_cur->psz_command, p_cmd, strlen(p_cur->psz_command) ) ) { p_cmd[strlen(p_cur->psz_command)] = '\0'; b_found = true; break; } } if( !b_found ) { /* No matching command */ msg_Err( p_filter, "Got invalid command: %s", p_cmd ); BufferPrintf( &p_sys->output, "FAILURE: %d Invalid Command\n", VLC_EGENERIC ); } else { msg_Info( p_filter, "Got valid command: %s", p_cmd ); command_t *p_cmddesc = malloc( sizeof( command_t ) ); if( !p_cmddesc ) return NULL; p_cmd = p_cmd + strlen(p_cur->psz_command) +1; p_cmddesc->p_command = p_cur; p_cmddesc->p_command->pf_parser( p_cmd, p_end, &p_cmddesc->params ); if( p_cmddesc->p_command->b_atomic && p_sys->b_atomic ) QueueEnqueue( &p_sys->atomic, p_cmddesc ); else QueueEnqueue( &p_sys->pending, p_cmddesc ); } BufferDel( &p_sys->input, p_end - p_sys->input.p_begin + 1 ); } /* Process any pending commands */ command_t *p_command = NULL; while( (p_command = QueueDequeue( &p_sys->pending )) ) { p_command->i_status = p_command->p_command->pf_execute( p_filter, &p_command->params, &p_command->results ); QueueEnqueue( &p_sys->processed, p_command ); } /* Output any processed commands */ while( (p_command = QueueDequeue( &p_sys->processed )) ) { if( p_command->i_status == VLC_SUCCESS ) { const char *psz_success = "SUCCESS:"; const char *psz_nl = "\n"; BufferAdd( &p_sys->output, psz_success, 8 ); p_command->p_command->pf_unparse( &p_command->results, &p_sys->output ); BufferAdd( &p_sys->output, psz_nl, 1 ); } else { BufferPrintf( &p_sys->output, "FAILURE: %d\n", p_command->i_status ); } } /* Try emptying the output buffer */ if( p_sys->i_outputfd != -1 ) { ssize_t i_len = vlc_write( p_sys->i_outputfd, p_sys->output.p_begin, p_sys->output.i_length ); if( i_len == -1 ) { /* We hit an error */ if( errno != EAGAIN ) { msg_Warn( p_filter, "Error on output file: %s", vlc_strerror_c(errno) ); vlc_close( p_sys->i_outputfd ); p_sys->i_outputfd = -1; } } else { BufferDel( &p_sys->output, i_len ); } } if( !p_sys->b_updated ) return NULL; subpicture_t *p_spu = NULL; overlay_t *p_overlay = NULL; p_spu = filter_NewSubpicture( p_filter ); if( !p_spu ) return NULL; p_spu->b_absolute = true; p_spu->i_start = date; p_spu->i_stop = 0; p_spu->b_ephemer = true; subpicture_region_t **pp_region = &p_spu->p_region; while( (p_overlay = ListWalk( &p_sys->overlays )) ) { subpicture_region_t *p_region; *pp_region = p_region = subpicture_region_New( &p_overlay->format ); if( !p_region ) break; msg_Dbg( p_filter, "Displaying overlay: %4.4s, %d, %d, %d", (char*)&p_overlay->format.i_chroma, p_overlay->i_x, p_overlay->i_y, p_overlay->i_alpha ); if( p_overlay->format.i_chroma == VLC_CODEC_TEXT ) { p_region->p_text = text_segment_New( p_overlay->data.p_text ); p_region->p_text->style = text_style_Duplicate( p_overlay->p_fontstyle ); } else { /* FIXME the copy is probably not needed anymore */ picture_Copy( p_region->p_picture, p_overlay->data.p_pic ); } p_region->i_x = p_overlay->i_x; p_region->i_y = p_overlay->i_y; p_region->i_align = SUBPICTURE_ALIGN_LEFT | SUBPICTURE_ALIGN_TOP; p_region->i_alpha = p_overlay->i_alpha; pp_region = &p_region->p_next; } p_sys->b_updated = false; return p_spu; }
/**************************************************************************** * Filter: the whole thing **************************************************************************** * This function outputs subpictures at regular time intervals. ****************************************************************************/ static subpicture_t *Filter( filter_t *p_filter, mtime_t date ) { filter_sys_t *p_sys = p_filter->p_sys; subpicture_t *p_spu = NULL; video_format_t fmt; vlc_mutex_lock( &p_sys->lock ); if( p_sys->last_time + p_sys->i_refresh > date ) goto out; if( p_sys->filepath != NULL ) { char *fmt = MarqueeReadFile( p_filter, p_sys->filepath ); if( fmt != NULL ) { free( p_sys->format ); p_sys->format = fmt; } } char *msg = vlc_strftime( p_sys->format ? p_sys->format : "" ); if( unlikely( msg == NULL ) ) goto out; if( p_sys->message != NULL && !strcmp( msg, p_sys->message ) ) { free( msg ); goto out; } free( p_sys->message ); p_sys->message = msg; p_spu = filter_NewSubpicture( p_filter ); if( !p_spu ) goto out; memset( &fmt, 0, sizeof(video_format_t) ); fmt.i_chroma = VLC_CODEC_TEXT; fmt.i_width = fmt.i_height = 0; fmt.i_x_offset = 0; fmt.i_y_offset = 0; fmt.i_sar_den = fmt.i_sar_num = 1; p_spu->p_region = subpicture_region_New( &fmt ); if( !p_spu->p_region ) { subpicture_Delete( p_spu ); p_spu = NULL; goto out; } p_sys->last_time = date; p_spu->p_region->p_text = text_segment_New( msg ); p_spu->i_start = date; p_spu->i_stop = p_sys->i_timeout == 0 ? 0 : date + p_sys->i_timeout * 1000; p_spu->b_ephemer = true; /* where to locate the string: */ if( p_sys->i_pos < 0 ) { /* set to an absolute xy */ p_spu->p_region->i_align = SUBPICTURE_ALIGN_LEFT | SUBPICTURE_ALIGN_TOP; p_spu->b_absolute = true; } else { /* set to one of the 9 relative locations */ p_spu->p_region->i_align = p_sys->i_pos; p_spu->b_absolute = false; } p_spu->p_region->i_x = p_sys->i_xoff; p_spu->p_region->i_y = p_sys->i_yoff; p_spu->p_region->p_text->style = text_style_Duplicate( p_sys->p_style ); out: vlc_mutex_unlock( &p_sys->lock ); return p_spu; }