static subpicture_region_t * vout_OSDBackground(int x, int y, int width, int height, uint32_t i_argb) { /* Create a new subpicture region */ video_palette_t palette; spuregion_CreateVGradientPalette( &palette, GRADIENT_COLORS, i_argb, 0xFF000000 ); video_format_t fmt; video_format_Init(&fmt, VLC_CODEC_YUVP); fmt.i_width = fmt.i_visible_width = width; fmt.i_height = fmt.i_visible_height = height; fmt.i_sar_num = 1; fmt.i_sar_den = 1; fmt.p_palette = &palette; subpicture_region_t *region = subpicture_region_New(&fmt); if (!region) return NULL; region->i_align = SUBPICTURE_ALIGN_LEFT | SUBPICTURE_ALIGN_TOP; region->i_x = x; region->i_y = y; spuregion_CreateVGradientFill( region->p_picture->p, palette.i_entries ); return region; }
static subpicture_region_t * vout_OSDEpgText(const char *text, int x, int y, int size, uint32_t color) { video_format_t fmt; subpicture_region_t *region; if (!text) return NULL; /* Create a new subpicture region */ video_format_Init(&fmt, VLC_CODEC_TEXT); fmt.i_sar_num = 0; fmt.i_sar_den = 1; region = subpicture_region_New(&fmt); if (!region) return NULL; /* Set subpicture parameters */ region->psz_text = strdup(text); region->i_align = 0; region->i_x = x; region->i_y = y; /* Set text style */ region->p_style = text_style_New(); if (region->p_style) { region->p_style->i_font_size = size; region->p_style->i_font_color = color; region->p_style->i_font_alpha = 0; } return region; }
/** * Create a region with a white transparent picture. */ static subpicture_region_t *OSDRegion(int x, int y, int width, int height) { video_format_t fmt; video_format_Init(&fmt, VLC_CODEC_YUVA); fmt.i_width = fmt.i_visible_width = width; fmt.i_height = fmt.i_visible_height = height; fmt.i_sar_num = 0; fmt.i_sar_den = 1; subpicture_region_t *r = subpicture_region_New(&fmt); if (!r) return NULL; r->i_x = x; r->i_y = y; for (int i = 0; i < r->p_picture->i_planes; i++) { plane_t *p = &r->p_picture->p[i]; int colors[PICTURE_PLANE_MAX] = { 0xff, 0x80, 0x80, 0x00 }; memset(p->p_pixels, colors[i], p->i_pitch * height); } return r; }
static subpicture_t *Subpicture( decoder_t *p_dec, video_format_t *p_fmt, bool b_text, int i_columns, int i_rows, int i_align, mtime_t i_pts ) { video_format_t fmt; subpicture_t *p_spu; /* If there is a page or sub to render, then we do that here */ /* Create the subpicture unit */ p_spu = decoder_NewSubpicture( p_dec, NULL ); if( !p_spu ) { msg_Warn( p_dec, "can't get spu buffer" ); return NULL; } memset( &fmt, 0, sizeof(video_format_t) ); fmt.i_chroma = b_text ? VLC_CODEC_TEXT : VLC_CODEC_RGBA; fmt.i_sar_num = 0; fmt.i_sar_den = 1; if( b_text ) { fmt.i_bits_per_pixel = 0; } else { fmt.i_width = fmt.i_visible_width = i_columns * 12; fmt.i_height = fmt.i_visible_height = i_rows * 10; fmt.i_bits_per_pixel = 32; } fmt.i_x_offset = fmt.i_y_offset = 0; p_spu->p_region = subpicture_region_New( &fmt ); if( p_spu->p_region == NULL ) { msg_Err( p_dec, "cannot allocate SPU region" ); decoder_DeleteSubpicture( p_dec, p_spu ); return NULL; } p_spu->p_region->i_x = 0; p_spu->p_region->i_y = 0; p_spu->p_region->i_align = i_align; p_spu->i_start = i_pts; p_spu->i_stop = 0; p_spu->b_ephemer = true; p_spu->b_absolute = true; if( !b_text ) { p_spu->i_original_picture_width = fmt.i_width; p_spu->i_original_picture_height = fmt.i_height; } /* */ *p_fmt = fmt; return p_spu; }
/** * Create a region with a white transparent picture. */ static subpicture_region_t *OSDRegion(int x, int y, int width, int height) { video_palette_t palette = { // sunqueen modify start /*.i_entries =*/ 2, /*.palette =*/ { /*[0] =*/ { 0xff, 0x80, 0x80, 0x00 }, /*[1] =*/ { 0xff, 0x80, 0x80, 0xff }, }, // sunqueen modify end }; video_format_t fmt; video_format_Init(&fmt, VLC_CODEC_YUVP); fmt.i_width = fmt.i_visible_width = width; fmt.i_height = fmt.i_visible_height = height; fmt.i_sar_num = 1; fmt.i_sar_den = 1; fmt.p_palette = &palette; subpicture_region_t *r = subpicture_region_New(&fmt); if (!r) return NULL; r->i_x = x; r->i_y = y; memset(r->p_picture->p->p_pixels, 0, r->p_picture->p->i_pitch * height); return r; }
static subpicture_t *Subpicture( decoder_t *p_dec, video_format_t *p_fmt, bool b_text, int i_columns, int i_rows, int i_align, mtime_t i_pts ) { video_format_t fmt; subpicture_t *p_spu=NULL; /* If there is a page or sub to render, then we do that here */ /* Create the subpicture unit */ if( b_text ) p_spu = decoder_NewSubpictureText( p_dec ); else p_spu = decoder_NewSubpicture( p_dec, NULL ); if( !p_spu ) { msg_Warn( p_dec, "can't get spu buffer" ); return NULL; } video_format_Init(&fmt, b_text ? VLC_CODEC_TEXT : VLC_CODEC_RGBA); if( b_text ) { fmt.i_bits_per_pixel = 0; } else { fmt.i_width = fmt.i_visible_width = i_columns * 12; fmt.i_height = fmt.i_visible_height = i_rows * 10; fmt.i_bits_per_pixel = 32; fmt.i_sar_num = fmt.i_sar_den = 0; /* let the vout set the correct AR */ } fmt.i_x_offset = fmt.i_y_offset = 0; p_spu->p_region = subpicture_region_New( &fmt ); if( p_spu->p_region == NULL ) { msg_Err( p_dec, "cannot allocate SPU region" ); subpicture_Delete( p_spu ); return NULL; } p_spu->p_region->i_x = 0; p_spu->p_region->i_y = 0; p_spu->i_start = i_pts; p_spu->i_stop = b_text ? i_pts + (10*CLOCK_FREQ): 0; p_spu->b_ephemer = true; p_spu->b_absolute = b_text ? false : true; if( !b_text ) p_spu->p_region->i_align = i_align; p_spu->i_original_picture_width = fmt.i_width; p_spu->i_original_picture_height = fmt.i_height; /* */ *p_fmt = fmt; return p_spu; }
static subpicture_region_t * vout_OSDEpgSlider(int x, int y, int width, int height, float ratio) { /* Create a new subpicture region */ video_palette_t palette = { // sunqueen modify start /*.i_entries =*/ 4, /*.palette =*/ { /*[0] =*/ { 0xff, 0x80, 0x80, 0x00 }, /*[1] =*/ { 0x00, 0x80, 0x80, 0x00 }, /*[2] =*/ { 0xff, 0x80, 0x80, 0xff }, /*[3] =*/ { 0x00, 0x80, 0x80, 0xff }, }, // sunqueen modify end }; video_format_t fmt; video_format_Init(&fmt, VLC_CODEC_YUVP); fmt.i_width = fmt.i_visible_width = width; fmt.i_height = fmt.i_visible_height = height; fmt.i_sar_num = 1; fmt.i_sar_den = 1; fmt.p_palette = &palette; subpicture_region_t *region = subpicture_region_New(&fmt); if (!region) return NULL; region->i_x = x; region->i_y = y; picture_t *picture = region->p_picture; ratio = VLC_CLIP(ratio, 0, 1); int filled_part_width = ratio * width; for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { /* Slider border. */ bool is_outline = j == 0 || j == height - 1 || i == 0 || i == width - 1; /* We can see the video through the part of the slider which corresponds to the leaving time. */ bool is_border = j < 3 || j > height - 4 || i < 3 || i > width - 4 || i < filled_part_width; picture->p->p_pixels[picture->p->i_pitch * j + i] = 2 * is_border + is_outline; } } return region; }
static subpicture_region_t * vout_OSDEpgSlider( int i_x, int i_y, int i_width, int i_height, float f_ratio ) { video_format_t fmt; subpicture_region_t *p_region; /* Create a new subpicture region */ video_format_Init( &fmt, VLC_CODEC_YUVA ); fmt.i_width = fmt.i_visible_width = i_width; fmt.i_height = fmt.i_visible_height = i_height; fmt.i_sar_num = 0; fmt.i_sar_den = 1; p_region = subpicture_region_New( &fmt ); if( !p_region ) return NULL; p_region->i_x = i_x; p_region->i_y = i_y; picture_t *p_picture = p_region->p_picture; f_ratio = __MIN( __MAX( f_ratio, 0 ), 1 ); int i_filled_part_width = f_ratio * i_width; for( int j = 0; j < i_height; j++ ) { for( int i = 0; i < i_width; i++ ) { #define WRITE_COMP( plane, value ) \ p_picture->p[plane].p_pixels[p_picture->p[plane].i_pitch * j + i] = value /* Draw the slider. */ bool is_outline = j == 0 || j == i_height - 1 || i == 0 || i == i_width - 1; WRITE_COMP( 0, is_outline ? 0x00 : 0xff ); WRITE_COMP( 1, 0x80 ); WRITE_COMP( 2, 0x80 ); /* We can see the video through the part of the slider which corresponds to the leaving time. */ bool is_border = j < 3 || j > i_height - 4 || i < 3 || i > i_width - 4 || i < i_filled_part_width; WRITE_COMP( 3, is_border ? 0xff : 0x00 ); #undef WRITE_COMP } } return p_region; }
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->psz_text = ParseText(payload, payload_size, cct_nums[dec->p_sys->cct - CCT_BEGIN].str); sub->p_region->i_align = SUBPICTURE_ALIGN_BOTTOM; sub->p_region->psz_html = NULL; } free(payload); exit: block_Release(b); return sub; }
subpicture_t *subpicture_NewFromPicture( vlc_object_t *p_obj, picture_t *p_picture, vlc_fourcc_t i_chroma ) { /* */ video_format_t fmt_in = p_picture->format; /* */ video_format_t fmt_out; fmt_out = fmt_in; fmt_out.i_chroma = i_chroma; /* */ image_handler_t *p_image = image_HandlerCreate( p_obj ); if( !p_image ) return NULL; picture_t *p_pip = image_Convert( p_image, p_picture, &fmt_in, &fmt_out ); image_HandlerDelete( p_image ); if( !p_pip ) return NULL; subpicture_t *p_subpic = subpicture_New( NULL ); if( !p_subpic ) { picture_Release( p_pip ); return NULL; } p_subpic->i_original_picture_width = fmt_out.i_width; p_subpic->i_original_picture_height = fmt_out.i_height; fmt_out.i_sar_num = fmt_out.i_sar_den = 0; p_subpic->p_region = subpicture_region_New( &fmt_out ); if( p_subpic->p_region ) { picture_Release( p_subpic->p_region->p_picture ); p_subpic->p_region->p_picture = p_pip; } else { picture_Release( p_pip ); } return p_subpic; }
/** * \brief Show text on the video from a given start date to a given end date * \param p_spu pointer to the subpicture queue the text is to be showed on * \param i_channel Subpicture channel * \param psz_string The text to be shown * \param p_style Pointer to a struct with text style info (it is duplicated) * \param i_flags flags for alignment and such * \param i_hmargin horizontal margin in pixels * \param i_vmargin vertical margin in pixels * \param i_start the time when this string is to appear on the video * \param i_stop the time when this string should stop to be displayed * if this is 0 the string will be shown untill the next string * is about to be shown */ int osd_ShowTextAbsolute( spu_t *p_spu_channel, int i_channel, const char *psz_string, const text_style_t *p_style, int i_flags, int i_hmargin, int i_vmargin, mtime_t i_start, mtime_t i_stop ) { subpicture_t *p_spu; video_format_t fmt; (void)p_style; if( !psz_string ) return VLC_EGENERIC; p_spu = subpicture_New( NULL ); if( !p_spu ) return VLC_EGENERIC; p_spu->i_channel = i_channel; p_spu->i_start = i_start; p_spu->i_stop = i_stop; p_spu->b_ephemer = true; p_spu->b_absolute = false; /* 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_spu->p_region = subpicture_region_New( &fmt ); if( !p_spu->p_region ) { msg_Err( p_spu_channel, "cannot allocate SPU region" ); subpicture_Delete( p_spu ); return VLC_EGENERIC; } p_spu->p_region->psz_text = strdup( psz_string ); p_spu->p_region->i_align = i_flags & SUBPICTURE_ALIGN_MASK; p_spu->p_region->i_x = i_hmargin; p_spu->p_region->i_y = i_vmargin; spu_PutSubpicture( p_spu_channel, p_spu ); return VLC_SUCCESS; }
subpicture_region_t* subpicture_region_Copy( subpicture_region_t *p_region_src ) { if (!p_region_src) return NULL; subpicture_region_t *p_region_dst = subpicture_region_New(&p_region_src->fmt); if (unlikely(!p_region_dst)) return NULL; p_region_dst->i_x = p_region_src->i_x; p_region_dst->i_y = p_region_src->i_y; p_region_dst->i_align = p_region_src->i_align; p_region_dst->i_alpha = p_region_src->i_alpha; p_region_dst->p_text = text_segment_Copy( p_region_src->p_text ); //Palette is already copied by subpicture_region_New, we just have to duplicate p_pixels for (int i = 0; i < p_region_src->p_picture->i_planes; i++) memcpy(p_region_dst->p_picture->p[i].p_pixels, p_region_src->p_picture->p[i].p_pixels, p_region_src->p_picture->p[i].i_lines * p_region_src->p_picture->p[i].i_pitch); return p_region_dst; }
static subpicture_region_t * vout_OSDEpgText( const char *psz_string, int i_x, int i_y, int i_size, uint32_t i_color ) { video_format_t fmt; subpicture_region_t *p_region; if( !psz_string ) return NULL; /* Create a new subpicture region */ video_format_Init( &fmt, VLC_CODEC_TEXT ); fmt.i_sar_num = 0; fmt.i_sar_den = 1; p_region = subpicture_region_New( &fmt ); if( !p_region ) return NULL; /* Set subpicture parameters */ p_region->psz_text = strdup( psz_string ); p_region->i_align = 0; p_region->i_x = i_x; p_region->i_y = i_y; /* Set text style */ p_region->p_style = text_style_New(); if( p_region->p_style ) { p_region->p_style->i_font_size = i_size; p_region->p_style->i_font_color = i_color; p_region->p_style->i_font_alpha = 0; } return p_region; }
/**************************************************************************** * 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->b_need_update == false ) goto out; 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_aspect = 0; fmt.i_width = fmt.i_height = 0; fmt.i_x_offset = 0; fmt.i_y_offset = 0; p_spu->p_region = subpicture_region_New( &fmt ); if( !p_spu->p_region ) { p_filter->pf_sub_buffer_del( p_filter, p_spu ); p_spu = NULL; goto out; } p_sys->last_time = date; if( !strchr( p_sys->psz_marquee, '%' ) && !strchr( p_sys->psz_marquee, '$' ) ) p_sys->b_need_update = false; p_spu->p_region->psz_text = str_format( p_filter, p_sys->psz_marquee ); 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 = OSD_ALIGN_LEFT | OSD_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_style = text_style_Duplicate( p_sys->p_style ); out: vlc_mutex_unlock( &p_sys->lock ); 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; video_format_t fmt; subpicture_region_t *p_region; int i_feed, i_item; rss_feed_t *p_feed; memset( &fmt, 0, sizeof(video_format_t) ); vlc_mutex_lock( &p_sys->lock ); /* Check if the feeds have been fetched and that we have some feeds */ /* TODO: check that we have items for each feeds */ if( !p_sys->b_fetched && p_sys->i_feeds > 0 ) { vlc_mutex_unlock( &p_sys->lock ); return NULL; } if( p_sys->last_date + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == ( p_sys->i_title == scroll_title ? -1 : 0 ) ? 5 : 1 ) /* ( ... ? 5 : 1 ) means "wait 5 times more for the 1st char" */ * p_sys->i_speed > date ) { vlc_mutex_unlock( &p_sys->lock ); return NULL; } p_sys->last_date = date; p_sys->i_cur_char++; if( p_sys->i_cur_item == -1 ? p_sys->p_feeds[p_sys->i_cur_feed].psz_title[p_sys->i_cur_char] == 0 : p_sys->p_feeds[p_sys->i_cur_feed].p_items[p_sys->i_cur_item].psz_title[p_sys->i_cur_char] == 0 ) { p_sys->i_cur_char = 0; p_sys->i_cur_item++; if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items ) { if( p_sys->i_title == scroll_title ) p_sys->i_cur_item = -1; else p_sys->i_cur_item = 0; p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds; } } p_spu = filter_NewSubpicture( p_filter ); if( !p_spu ) { vlc_mutex_unlock( &p_sys->lock ); return NULL; } fmt.i_chroma = VLC_CODEC_TEXT; p_spu->p_region = subpicture_region_New( &fmt ); if( !p_spu->p_region ) { p_filter->pf_sub_buffer_del( p_filter, p_spu ); vlc_mutex_unlock( &p_sys->lock ); return NULL; } /* Generate the string that will be displayed. This string is supposed to be p_sys->i_length characters long. */ i_item = p_sys->i_cur_item; i_feed = p_sys->i_cur_feed; p_feed = &p_sys->p_feeds[i_feed]; if( ( p_feed->p_pic && p_sys->i_title == default_title ) || p_sys->i_title == hide_title ) { /* Don't display the feed's title if we have an image */ _snprintf( p_sys->psz_marquee, p_sys->i_length, "%s", p_sys->p_feeds[i_feed].p_items[i_item].psz_title +p_sys->i_cur_char ); // sunqueen modify } else if( ( !p_feed->p_pic && p_sys->i_title == default_title ) || p_sys->i_title == prepend_title ) { _snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s", p_sys->p_feeds[i_feed].psz_title, p_sys->p_feeds[i_feed].p_items[i_item].psz_title +p_sys->i_cur_char ); // sunqueen modify } else /* scrolling title */ { if( i_item == -1 ) _snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s", p_sys->p_feeds[i_feed].psz_title + p_sys->i_cur_char, p_sys->p_feeds[i_feed].p_items[i_item+1].psz_title ); // sunqueen modify else _snprintf( p_sys->psz_marquee, p_sys->i_length, "%s", p_sys->p_feeds[i_feed].p_items[i_item].psz_title +p_sys->i_cur_char ); // sunqueen modify } while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length ) { i_item++; if( i_item == p_sys->p_feeds[i_feed].i_items ) break; _snprintf( strchr( p_sys->psz_marquee, 0 ), p_sys->i_length - strlen( p_sys->psz_marquee ), " - %s", p_sys->p_feeds[i_feed].p_items[i_item].psz_title ); // sunqueen modify } /* Calls to snprintf might split multibyte UTF8 chars ... * which freetype doesn't like. */ { char *a = strdup( p_sys->psz_marquee ); char *a2 = a; char *b = p_sys->psz_marquee; EnsureUTF8( p_sys->psz_marquee ); /* we want to use ' ' instead of '?' for erroneous chars */ while( *b != '\0' ) { if( *b != *a ) *b = ' '; b++;a++; } free( a2 ); } p_spu->p_region->psz_text = strdup(p_sys->psz_marquee); if( p_sys->p_style->i_font_size > 0 ) p_spu->p_region->fmt.i_visible_height = p_sys->p_style->i_font_size; p_spu->i_start = date; p_spu->i_stop = 0; 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_style = text_style_Duplicate( p_sys->p_style ); if( p_feed->p_pic ) { /* Display the feed's image */ picture_t *p_pic = p_feed->p_pic; video_format_t fmt_out; memset( &fmt_out, 0, sizeof(video_format_t) ); fmt_out.i_chroma = VLC_CODEC_YUVA; fmt_out.i_sar_num = fmt_out.i_sar_den = 1; fmt_out.i_width = fmt_out.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch; fmt_out.i_height = fmt_out.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines; p_region = subpicture_region_New( &fmt_out ); if( !p_region ) { msg_Err( p_filter, "cannot allocate SPU region" ); } else { p_region->i_x = p_spu->p_region->i_x; p_region->i_y = p_spu->p_region->i_y; /* FIXME the copy is probably not needed anymore */ picture_Copy( p_region->p_picture, p_pic ); p_spu->p_region->p_next = p_region; /* Offset text to display right next to the image */ p_spu->p_region->i_x += fmt_out.i_visible_width; } } vlc_mutex_unlock( &p_sys->lock ); return p_spu; }
/** * Sub source */ static subpicture_t *FilterSub( filter_t *p_filter, mtime_t date ) { filter_sys_t *p_sys = p_filter->p_sys; logo_list_t *p_list = &p_sys->list; subpicture_t *p_spu; subpicture_region_t *p_region; video_format_t fmt; picture_t *p_pic; logo_t *p_logo; vlc_mutex_lock( &p_sys->lock ); /* Basic test: b_spu_update occurs on a dynamic change, & i_next_pic is the general timer, when to look at updating the logo image */ if( ( !p_sys->b_spu_update && p_list->i_next_pic > date ) || !p_list->i_repeat ) { vlc_mutex_unlock( &p_sys->lock ); return NULL; } /* adjust index to the next logo */ p_logo = LogoListNext( p_list, date ); p_sys->b_spu_update = false; p_pic = p_logo->p_pic; /* Allocate the subpicture internal data. */ p_spu = filter_NewSubpicture( p_filter ); if( !p_spu ) goto exit; p_spu->b_absolute = p_sys->b_absolute; p_spu->i_start = date; p_spu->i_stop = 0; p_spu->b_ephemer = true; /* Send an empty subpicture to clear the display when needed */ if( p_list->i_repeat != -1 && p_list->i_counter == 0 ) { p_list->i_repeat--; if( p_list->i_repeat == 0 ) goto exit; } if( !p_pic || !p_logo->i_alpha || ( p_logo->i_alpha == -1 && !p_list->i_alpha ) ) goto exit; /* Create new SPU region */ memset( &fmt, 0, sizeof(video_format_t) ); fmt.i_chroma = VLC_CODEC_YUVA; fmt.i_sar_num = fmt.i_sar_den = 1; fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch; fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines; fmt.i_x_offset = fmt.i_y_offset = 0; p_region = subpicture_region_New( &fmt ); if( !p_region ) { msg_Err( p_filter, "cannot allocate SPU region" ); p_filter->pf_sub_buffer_del( p_filter, p_spu ); p_spu = NULL; goto exit; } /* */ picture_Copy( p_region->p_picture, p_pic ); /* where to locate the logo: */ if( p_sys->i_pos < 0 ) { /* set to an absolute xy */ p_region->i_align = SUBPICTURE_ALIGN_RIGHT | SUBPICTURE_ALIGN_TOP; p_spu->b_absolute = true; } else { /* set to one of the 9 relative locations */ p_region->i_align = p_sys->i_pos; p_spu->b_absolute = false; } p_region->i_x = p_sys->i_pos_x; p_region->i_y = p_sys->i_pos_y; p_spu->p_region = p_region; p_spu->i_alpha = ( p_logo->i_alpha != -1 ? p_logo->i_alpha : p_list->i_alpha ); exit: vlc_mutex_unlock( &p_sys->lock ); return p_spu; }
static void SubpictureUpdate( subpicture_t *p_subpic, const video_format_t *p_fmt_src, const video_format_t *p_fmt_dst, mtime_t i_ts ) { VLC_UNUSED( p_fmt_src ); VLC_UNUSED( p_fmt_dst ); VLC_UNUSED( i_ts ); decoder_sys_t *p_sys = p_subpic->updater.p_sys->p_dec_sys; video_format_t fmt = p_sys->fmt; ASS_Image *p_img = p_subpic->updater.p_sys->p_img; /* */ p_subpic->i_original_picture_height = fmt.i_visible_height; p_subpic->i_original_picture_width = fmt.i_visible_width; /* XXX to improve efficiency we merge regions that are close minimizing * the lost surface. * libass tends to create a lot of small regions and thus spu engine * reinstanciate a lot the scaler, and as we do not support subpel blending * it looks ugly (text unaligned). */ const int i_max_region = 4; rectangle_t region[i_max_region]; const int i_region = BuildRegions( region, i_max_region, p_img, fmt.i_width, fmt.i_height ); if( i_region <= 0 ) { vlc_mutex_unlock( &p_sys->lock ); return; } /* Allocate the regions and draw them */ subpicture_region_t **pp_region_last = &p_subpic->p_region; for( int i = 0; i < i_region; i++ ) { subpicture_region_t *r; video_format_t fmt_region; /* */ fmt_region = fmt; fmt_region.i_width = fmt_region.i_visible_width = region[i].x1 - region[i].x0; fmt_region.i_height = fmt_region.i_visible_height = region[i].y1 - region[i].y0; r = subpicture_region_New( &fmt_region ); if( !r ) break; r->i_x = region[i].x0; r->i_y = region[i].y0; r->i_align = SUBPICTURE_ALIGN_TOP | SUBPICTURE_ALIGN_LEFT; /* */ RegionDraw( r, p_img ); /* */ *pp_region_last = r; pp_region_last = &r->p_next; } vlc_mutex_unlock( &p_sys->lock ); }
/***************************************************************************** * 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, mtime_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 (%m)", p_sys->psz_inputfile ); } 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 (%m)", p_sys->psz_outputfile ); } } 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: %m" ); 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 = 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: %m" ); 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 = p_filter->pf_sub_buffer_new( p_filter ); if( !p_spu ) { msg_Err( p_filter, "cannot allocate subpicture" ); 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->psz_text = strdup( p_overlay->data.p_text ); p_region->p_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; }
/** * Sub source */ static subpicture_t *FilterSub( filter_t *p_filter, mtime_t date ) { filter_sys_t *p_sys = p_filter->p_sys; BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph); subpicture_t *p_spu; subpicture_region_t *p_region; video_format_t fmt; picture_t *p_pic; vlc_mutex_lock( &p_sys->lock ); /* Basic test: b_spu_update occurs on a dynamic change */ if( !p_sys->b_spu_update ) { vlc_mutex_unlock( &p_sys->lock ); return 0; } p_pic = p_BarGraph->p_pic; /* Allocate the subpicture internal data. */ p_spu = filter_NewSubpicture( p_filter ); if( !p_spu ) goto exit; p_spu->b_absolute = p_sys->b_absolute; p_spu->i_start = date; p_spu->i_stop = 0; p_spu->b_ephemer = true; /* Send an empty subpicture to clear the display when needed */ if( !p_pic || !p_BarGraph->i_alpha ) goto exit; /* Create new SPU region */ memset( &fmt, 0, sizeof(video_format_t) ); fmt.i_chroma = VLC_CODEC_YUVA; fmt.i_sar_num = fmt.i_sar_den = 1; fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch; fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines; fmt.i_x_offset = fmt.i_y_offset = 0; p_region = subpicture_region_New( &fmt ); if( !p_region ) { msg_Err( p_filter, "cannot allocate SPU region" ); p_filter->pf_sub_buffer_del( p_filter, p_spu ); p_spu = NULL; goto exit; } /* */ picture_Copy( p_region->p_picture, p_pic ); /* where to locate the bar graph: */ if( p_sys->i_pos < 0 ) { /* set to an absolute xy */ p_region->i_align = SUBPICTURE_ALIGN_RIGHT | SUBPICTURE_ALIGN_TOP; p_spu->b_absolute = true; } else { /* set to one of the 9 relative locations */ p_region->i_align = p_sys->i_pos; p_spu->b_absolute = false; } p_region->i_x = p_sys->i_pos_x; p_region->i_y = p_sys->i_pos_y; p_spu->p_region = p_region; p_spu->i_alpha = p_BarGraph->i_alpha ; exit: vlc_mutex_unlock( &p_sys->lock ); return p_spu; }
/***************************************************************************** * Filter *****************************************************************************/ static subpicture_t *Filter( filter_t *p_filter, mtime_t date ) { filter_sys_t *p_sys = p_filter->p_sys; bridge_t *p_bridge; subpicture_t *p_spu; int i_index, i_real_index, i_row, i_col; int i_greatest_real_index_used = p_sys->i_order_length - 1; unsigned int col_inner_width, row_inner_height; subpicture_region_t *p_region; subpicture_region_t *p_region_prev = NULL; /* Allocate the subpicture internal data. */ p_spu = filter_NewSubpicture( p_filter ); if( !p_spu ) return NULL; /* Initialize subpicture */ p_spu->i_channel = 0; p_spu->i_start = date; p_spu->i_stop = 0; p_spu->b_ephemer = true; p_spu->i_alpha = p_sys->i_alpha; p_spu->b_absolute = false; vlc_mutex_lock( &p_sys->lock ); vlc_global_lock( VLC_MOSAIC_MUTEX ); p_bridge = GetBridge( p_filter ); if ( p_bridge == NULL ) { vlc_global_unlock( VLC_MOSAIC_MUTEX ); vlc_mutex_unlock( &p_sys->lock ); return p_spu; } if ( p_sys->i_position == position_offsets ) { /* If we have either too much or not enough offsets, fall-back * to automatic positioning. */ if ( p_sys->i_offsets_length != p_sys->i_order_length ) { msg_Err( p_filter, "Number of specified offsets (%d) does not match number " "of input substreams in mosaic-order (%d), falling back " "to mosaic-position=0", p_sys->i_offsets_length, p_sys->i_order_length ); p_sys->i_position = position_auto; } } if ( p_sys->i_position == position_auto ) { int i_numpics = p_sys->i_order_length; /* keep slots and all */ for ( i_index = 0; i_index < p_bridge->i_es_num; i_index++ ) { bridged_es_t *p_es = p_bridge->pp_es[i_index]; if ( !p_es->b_empty ) { i_numpics ++; if( p_sys->i_order_length && p_es->psz_id != NULL ) { /* We also want to leave slots for images given in * mosaic-order that are not available in p_vout_picture */ int i; for( i = 0; i < p_sys->i_order_length ; i++ ) { if( !strcmp( p_sys->ppsz_order[i], p_es->psz_id ) ) { i_numpics--; break; } } } } } p_sys->i_rows = ceil(sqrt( (double)i_numpics )); p_sys->i_cols = ( i_numpics % p_sys->i_rows == 0 ? i_numpics / p_sys->i_rows : i_numpics / p_sys->i_rows + 1 ); } col_inner_width = ( ( p_sys->i_width - ( p_sys->i_cols - 1 ) * p_sys->i_borderw ) / p_sys->i_cols ); row_inner_height = ( ( p_sys->i_height - ( p_sys->i_rows - 1 ) * p_sys->i_borderh ) / p_sys->i_rows ); i_real_index = 0; for ( i_index = 0; i_index < p_bridge->i_es_num; i_index++ ) { bridged_es_t *p_es = p_bridge->pp_es[i_index]; video_format_t fmt_in, fmt_out; picture_t *p_converted; memset( &fmt_in, 0, sizeof( video_format_t ) ); memset( &fmt_out, 0, sizeof( video_format_t ) ); if ( p_es->b_empty ) continue; while ( p_es->p_picture != NULL && p_es->p_picture->date + p_sys->i_delay < date ) { if ( p_es->p_picture->p_next != NULL ) { picture_t *p_next = p_es->p_picture->p_next; picture_Release( p_es->p_picture ); p_es->p_picture = p_next; } else if ( p_es->p_picture->date + p_sys->i_delay + BLANK_DELAY < date ) { /* Display blank */ picture_Release( p_es->p_picture ); p_es->p_picture = NULL; p_es->pp_last = &p_es->p_picture; break; } else { msg_Dbg( p_filter, "too late picture for %s (%"PRId64 ")", p_es->psz_id, date - p_es->p_picture->date - p_sys->i_delay ); break; } } if ( p_es->p_picture == NULL ) continue; if ( p_sys->i_order_length == 0 ) { i_real_index++; } else { int i; for ( i = 0; i <= p_sys->i_order_length; i++ ) { if ( i == p_sys->i_order_length ) break; if ( strcmp( p_es->psz_id, p_sys->ppsz_order[i] ) == 0 ) { i_real_index = i; break; } } if ( i == p_sys->i_order_length ) i_real_index = ++i_greatest_real_index_used; } i_row = ( i_real_index / p_sys->i_cols ) % p_sys->i_rows; i_col = i_real_index % p_sys->i_cols ; if ( !p_sys->b_keep ) { /* Convert the images */ fmt_in.i_chroma = p_es->p_picture->format.i_chroma; fmt_in.i_height = p_es->p_picture->format.i_height; fmt_in.i_width = p_es->p_picture->format.i_width; if( fmt_in.i_chroma == VLC_CODEC_YUVA || fmt_in.i_chroma == VLC_CODEC_RGBA ) fmt_out.i_chroma = VLC_CODEC_YUVA; else fmt_out.i_chroma = VLC_CODEC_I420; fmt_out.i_width = col_inner_width; fmt_out.i_height = row_inner_height; if( p_sys->b_ar ) /* keep aspect ratio */ { if( (float)fmt_out.i_width / (float)fmt_out.i_height > (float)fmt_in.i_width / (float)fmt_in.i_height ) { fmt_out.i_width = ( fmt_out.i_height * fmt_in.i_width ) / fmt_in.i_height; } else { fmt_out.i_height = ( fmt_out.i_width * fmt_in.i_height ) / fmt_in.i_width; } } fmt_out.i_visible_width = fmt_out.i_width; fmt_out.i_visible_height = fmt_out.i_height; p_converted = image_Convert( p_sys->p_image, p_es->p_picture, &fmt_in, &fmt_out ); if( !p_converted ) { msg_Warn( p_filter, "image resizing and chroma conversion failed" ); continue; } } else { p_converted = p_es->p_picture; fmt_in.i_width = fmt_out.i_width = p_converted->format.i_width; fmt_in.i_height = fmt_out.i_height = p_converted->format.i_height; fmt_in.i_chroma = fmt_out.i_chroma = p_converted->format.i_chroma; fmt_out.i_visible_width = fmt_out.i_width; fmt_out.i_visible_height = fmt_out.i_height; } p_region = subpicture_region_New( &fmt_out ); /* FIXME the copy is probably not needed anymore */ if( p_region ) picture_Copy( p_region->p_picture, p_converted ); if( !p_sys->b_keep ) picture_Release( p_converted ); if( !p_region ) { msg_Err( p_filter, "cannot allocate SPU region" ); p_filter->pf_sub_buffer_del( p_filter, p_spu ); vlc_global_unlock( VLC_MOSAIC_MUTEX ); vlc_mutex_unlock( &p_sys->lock ); return p_spu; } if( p_es->i_x >= 0 && p_es->i_y >= 0 ) { p_region->i_x = p_es->i_x; p_region->i_y = p_es->i_y; } else if( p_sys->i_position == position_offsets ) { p_region->i_x = p_sys->pi_x_offsets[i_real_index]; p_region->i_y = p_sys->pi_y_offsets[i_real_index]; } else { if( fmt_out.i_width > col_inner_width || p_sys->b_ar || p_sys->b_keep ) { /* we don't have to center the video since it takes the whole rectangle area or it's larger than the rectangle */ p_region->i_x = p_sys->i_xoffset + i_col * ( p_sys->i_width / p_sys->i_cols ) + ( i_col * p_sys->i_borderw ) / p_sys->i_cols; } else { /* center the video in the dedicated rectangle */ p_region->i_x = p_sys->i_xoffset + i_col * ( p_sys->i_width / p_sys->i_cols ) + ( i_col * p_sys->i_borderw ) / p_sys->i_cols + ( col_inner_width - fmt_out.i_width ) / 2; } if( fmt_out.i_height > row_inner_height || p_sys->b_ar || p_sys->b_keep ) { /* we don't have to center the video since it takes the whole rectangle area or it's taller than the rectangle */ p_region->i_y = p_sys->i_yoffset + i_row * ( p_sys->i_height / p_sys->i_rows ) + ( i_row * p_sys->i_borderh ) / p_sys->i_rows; } else { /* center the video in the dedicated rectangle */ p_region->i_y = p_sys->i_yoffset + i_row * ( p_sys->i_height / p_sys->i_rows ) + ( i_row * p_sys->i_borderh ) / p_sys->i_rows + ( row_inner_height - fmt_out.i_height ) / 2; } } p_region->i_align = p_sys->i_align; p_region->i_alpha = p_es->i_alpha; if( p_region_prev == NULL ) { p_spu->p_region = p_region; } else { p_region_prev->p_next = p_region; } p_region_prev = p_region; } vlc_global_unlock( VLC_MOSAIC_MUTEX ); vlc_mutex_unlock( &p_sys->lock ); 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; subpicture_region_t *p_region; video_format_t fmt; picture_t *p_pic = NULL; vlc_mutex_lock( &p_sys->lock ); if( p_sys->b_need_update ) p_pic = p_sys->p_pic; if( p_pic == NULL ) { vlc_mutex_unlock( &p_sys->lock ); return NULL; } /* Allocate the subpicture internal data. */ p_spu = filter_NewSubpicture( p_filter ); if( !p_spu ) { vlc_mutex_unlock( &p_sys->lock ); return NULL; } p_spu->b_absolute = false; p_spu->i_start = date; p_spu->i_stop = 0; p_spu->b_ephemer = true; /* Create new SPU region */ memset( &fmt, 0, sizeof(video_format_t) ); fmt.i_chroma = VLC_CODEC_YUVA; fmt.i_sar_num = fmt.i_sar_den = 1; fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch; fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines; fmt.i_x_offset = fmt.i_y_offset = 0; p_region = subpicture_region_New( &fmt ); if( !p_region ) { msg_Err( p_filter, "cannot allocate SPU region" ); subpicture_Delete( p_spu ); vlc_mutex_unlock( &p_sys->lock ); return NULL; } /* FIXME the copy is probably not needed anymore */ picture_Copy( p_region->p_picture, p_pic ); p_sys->b_need_update = false; vlc_mutex_unlock( &p_sys->lock ); /* set to one of the 9 relative locations */ p_region->i_align = 0; /* Center */ p_spu->b_absolute = false; p_spu->i_original_picture_width = 0; /*Let vout core do the horizontal scaling */ p_spu->i_original_picture_height = fmt.i_height; p_spu->p_region = p_region; p_spu->i_alpha = ( p_sys->i_alpha ); return p_spu; }
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; /* 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_text_region->psz_text = NULL; p_text_region->psz_html = strndup( psz_subtitle, i_len ); if( ! p_text_region->psz_html ) { subpicture_region_Delete( p_text_region ); return NULL; } p_ssa_style = ParseStyle( p_sys, p_text_region->psz_html ); 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->p_style = text_style_Duplicate( &p_ssa_style->font_style ); 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; } 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; } /* 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; }
/***************************************************************************** * ParseText: parse an text subtitle packet and send it to the video output *****************************************************************************/ static subpicture_t *ParseText( decoder_t *p_dec, block_t *p_block ) { decoder_sys_t *p_sys = p_dec->p_sys; subpicture_t *p_spu = NULL; char *psz_subtitle = NULL; video_format_t fmt; /* We cannot display a subpicture with no date */ if( p_block->i_pts <= VLC_TS_INVALID ) { msg_Warn( p_dec, "subtitle without a date" ); return NULL; } /* Check validity of packet data */ /* An "empty" line containing only \0 can be used to force and ephemer picture from the screen */ if( p_block->i_buffer < 1 ) { msg_Warn( p_dec, "no subtitle data" ); return NULL; } /* Should be resiliant against bad subtitles */ psz_subtitle = malloc( p_block->i_buffer + 1 ); if( psz_subtitle == NULL ) return NULL; memcpy( psz_subtitle, p_block->p_buffer, p_block->i_buffer ); psz_subtitle[p_block->i_buffer] = '\0'; if( p_sys->iconv_handle == (vlc_iconv_t)-1 ) { if (EnsureUTF8( psz_subtitle ) == NULL) { msg_Err( p_dec, "failed to convert subtitle encoding.\n" "Try manually setting a character-encoding " "before you open the file." ); } } else { if( p_sys->b_autodetect_utf8 ) { if( IsUTF8( psz_subtitle ) == NULL ) { msg_Dbg( p_dec, "invalid UTF-8 sequence: " "disabling UTF-8 subtitles autodetection" ); p_sys->b_autodetect_utf8 = false; } } if( !p_sys->b_autodetect_utf8 ) { size_t inbytes_left = strlen( psz_subtitle ); size_t outbytes_left = 6 * inbytes_left; char *psz_new_subtitle = xmalloc( outbytes_left + 1 ); char *psz_convert_buffer_out = psz_new_subtitle; const char *psz_convert_buffer_in = psz_subtitle; size_t ret = vlc_iconv( p_sys->iconv_handle, &psz_convert_buffer_in, &inbytes_left, &psz_convert_buffer_out, &outbytes_left ); *psz_convert_buffer_out++ = '\0'; free( psz_subtitle ); if( ( ret == (size_t)(-1) ) || inbytes_left ) { free( psz_new_subtitle ); msg_Err( p_dec, "failed to convert subtitle encoding.\n" "Try manually setting a character-encoding " "before you open the file." ); return NULL; } psz_subtitle = realloc( psz_new_subtitle, psz_convert_buffer_out - psz_new_subtitle ); if( !psz_subtitle ) psz_subtitle = psz_new_subtitle; } } /* Create the subpicture unit */ p_spu = decoder_NewSubpicture( p_dec, NULL ); if( !p_spu ) { msg_Warn( p_dec, "can't get spu buffer" ); free( psz_subtitle ); return NULL; } /* 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_spu->p_region = subpicture_region_New( &fmt ); if( !p_spu->p_region ) { msg_Err( p_dec, "cannot allocate SPU region" ); free( psz_subtitle ); decoder_DeleteSubpicture( p_dec, p_spu ); return NULL; } /* Decode and format the subpicture unit */ if( p_dec->fmt_in.i_codec != VLC_CODEC_SSA ) { /* Normal text subs, easy markup */ p_spu->p_region->i_align = SUBPICTURE_ALIGN_BOTTOM | p_sys->i_align; p_spu->p_region->i_x = p_sys->i_align ? 20 : 0; p_spu->p_region->i_y = 10; /* Remove formatting from string */ p_spu->p_region->psz_text = StripTags( psz_subtitle ); if( var_InheritBool( p_dec, "subsdec-formatted" ) ) { p_spu->p_region->psz_html = CreateHtmlSubtitle( &p_spu->p_region->i_align, psz_subtitle ); } p_spu->i_start = p_block->i_pts; p_spu->i_stop = p_block->i_pts + p_block->i_length; p_spu->b_ephemer = (p_block->i_length == 0); p_spu->b_absolute = false; } else { /* Decode SSA/USF strings */ ParseSSAString( p_dec, psz_subtitle, p_spu ); p_spu->i_start = p_block->i_pts; p_spu->i_stop = p_block->i_pts + p_block->i_length; p_spu->b_ephemer = (p_block->i_length == 0); p_spu->b_absolute = false; p_spu->i_original_picture_width = p_sys->i_original_width; p_spu->i_original_picture_height = p_sys->i_original_height; } free( psz_subtitle ); return p_spu; }
/***************************************************************************** * DecodePacket: parse and decode an subtitle packet ***************************************************************************** * This function parses and decodes an SPU packet and, if valid, returns a * subpicture. *****************************************************************************/ static subpicture_t *DecodePacket( decoder_t *p_dec, block_t *p_data ) { decoder_sys_t *p_sys = p_dec->p_sys; subpicture_t *p_spu; subpicture_region_t *p_region; video_format_t fmt; video_palette_t palette; int i; /* Allocate the subpicture internal data. */ p_spu = decoder_NewSubpicture( p_dec, NULL ); if( !p_spu ) return NULL; p_spu->i_start = p_data->i_pts; p_spu->i_stop = p_data->i_pts + p_sys->i_duration; p_spu->b_ephemer = true; /* Create new subtitle region */ memset( &fmt, 0, sizeof(video_format_t) ); fmt.i_chroma = VLC_CODEC_YUVP; /** The video on which the subtitle sits, is scaled, probably 4:3. However subtitle bitmaps assume an 1:1 aspect ratio. FIXME: We should get the video aspect ratio from somewhere. Two candidates are the video and the other possibility would be the access module. */ fmt.i_sar_num = p_sys->i_height; fmt.i_sar_den = p_sys->i_width; fmt.i_width = fmt.i_visible_width = p_sys->i_width; fmt.i_height = fmt.i_visible_height = p_sys->i_height; fmt.i_x_offset = fmt.i_y_offset = 0; fmt.p_palette = &palette; fmt.p_palette->i_entries = 4; for( i = 0; i < fmt.p_palette->i_entries; i++ ) { fmt.p_palette->palette[i][0] = p_sys->p_palette[i][0]; fmt.p_palette->palette[i][1] = p_sys->p_palette[i][1]; fmt.p_palette->palette[i][2] = p_sys->p_palette[i][2]; fmt.p_palette->palette[i][3] = p_sys->p_palette[i][3]; } p_region = subpicture_region_New( &fmt ); if( !p_region ) { msg_Err( p_dec, "cannot allocate SVCD subtitle region" ); decoder_DeleteSubpicture( p_dec, p_spu ); return NULL; } p_spu->p_region = p_region; p_region->i_x = p_sys->i_x_start; p_region->i_y = p_sys->i_y_start; SVCDSubRenderImage( p_dec, p_data, p_region ); 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 = str_format_time( 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; p_spu->p_region = subpicture_region_New( &fmt ); if( !p_spu->p_region ) { p_filter->pf_sub_buffer_del( p_filter, p_spu ); p_spu = NULL; goto out; } p_sys->last_time = date; p_spu->p_region->psz_text = strdup( 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_style = text_style_Duplicate( p_sys->p_style ); out: vlc_mutex_unlock( &p_sys->lock ); return p_spu; }
[0] = { 0xff, 0x80, 0x80, 0x00 }, [1] = { 0x00, 0x80, 0x80, 0x00 }, [2] = { 0xff, 0x80, 0x80, 0xff }, [3] = { 0x00, 0x80, 0x80, 0xff }, }, }; video_format_t fmt; video_format_Init(&fmt, VLC_CODEC_YUVP); fmt.i_width = fmt.i_visible_width = width; fmt.i_height = fmt.i_visible_height = height; fmt.i_sar_num = 1; fmt.i_sar_den = 1; fmt.p_palette = &palette; subpicture_region_t *region = subpicture_region_New(&fmt); if (!region) return NULL; region->i_x = x; region->i_y = y; picture_t *picture = region->p_picture; ratio = VLC_CLIP(ratio, 0, 1); int filled_part_width = ratio * width; for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { /* Slider border. */ bool is_outline = j == 0 || j == height - 1 ||
static subpicture_region_t *CreateTextRegion( decoder_t *p_dec, char *psz_subtitle, int i_sys_align ) { decoder_sys_t *p_sys = p_dec->p_sys; subpicture_region_t *p_text_region; video_format_t fmt; /* Create a new subpicture region */ video_format_Init( &fmt, 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 ); video_format_Clean( &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 ) { for( int 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]; } } /* Set default or user align/magin. * Style overriden if no user value. */ p_text_region->i_x = i_sys_align > 0 ? 20 : 0; p_text_region->i_y = 10; p_text_region->i_align = SUBPICTURE_ALIGN_BOTTOM | ((i_sys_align > 0) ? i_sys_align : 0); if( p_ssa_style ) { msg_Dbg( p_dec, "style is: %s", p_ssa_style->psz_stylename ); /* 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; */ if( i_sys_align == -1 ) { p_text_region->i_align = p_ssa_style->i_align; 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->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; }