static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture) { vout_display_sys_t *sys = vd->sys; LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev; if (sys->lost_not_ready) { picture_Release(picture); if (subpicture) subpicture_Delete(subpicture); return; } // Present the back buffer contents to the display // No stretching should happen here ! const RECT src = sys->rect_dest_clipped; const RECT dst = sys->rect_dest_clipped; HRESULT hr = IDirect3DDevice9_Present(d3ddev, &src, &dst, NULL, NULL); if (FAILED(hr)) { msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); } #if 0 VLC_UNUSED(picture); VLC_UNUSED(subpicture); #else /* XXX See Prepare() */ Direct3DLockSurface(picture); picture_Release(picture); #endif if (subpicture) subpicture_Delete(subpicture); CommonDisplay(vd); }
static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture) { VLC_UNUSED(vd); VLC_UNUSED(subpicture); picture_sys_t *p_picsys = picture->p_sys; vout_display_sys_t *sys = vd->sys; void (*display_callback)(picture_sys_t*) = p_picsys->pf_display_callback; if (display_callback) display_callback(p_picsys); if (subpicture) sys->b_has_subpictures = true; /* As long as no subpicture was received, do not call DisplaySubpicture since JNI calls and clearing the subtitles surface are expensive operations. */ if (sys->b_has_subpictures) { DisplaySubpicture(vd, subpicture); if (!subpicture) { /* The surface has been cleared and there is no new subpicture to upload, do not clear again until a new subpicture is received. */ sys->b_has_subpictures = false; } } /* refcount lowers to 0, and pool_cfg.unlock is called */ picture_Release(picture); if (subpicture) subpicture_Delete(subpicture); }
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; }
/***************************************************************************** * SubsdelayFilter: Filter new subpicture *****************************************************************************/ static subpicture_t * SubsdelayFilter( filter_t *p_filter, subpicture_t* p_subpic ) { subsdelay_heap_t *p_heap; subsdelay_heap_entry_t *p_entry; if( !p_subpic->b_subtitle ) { return p_subpic; } if( SubpicIsEmpty( p_subpic ) ) { /* empty subtitles usually helps terminate ephemer subtitles, but this filter calculates the stop value anyway, so this subtitle can be dropped */ subpicture_Delete( p_subpic ); return NULL; } p_heap = &p_filter->p_sys->heap; /* add subpicture to the heap */ SubsdelayHeapLock( p_heap ); p_entry = SubsdelayHeapPush( p_heap, p_subpic, p_filter ); if( !p_entry ) { SubsdelayHeapUnlock( p_heap ); msg_Err(p_filter, "Can't add subpicture to the heap"); return p_subpic; } p_subpic = p_entry->p_subpic; /* get the local subpic */ if( p_subpic->b_ephemer ) { /* set a relativly long delay in hope that the next subtitle will arrive in this time and the real delay could be determined */ p_subpic->i_stop = p_subpic->i_start + 20000000; /* start + 20 sec */ p_subpic->b_ephemer = false; } SubsdelayEnforceDelayRules( p_filter ); SubsdelayHeapUnlock( p_heap ); return p_subpic; }
static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture) { vout_display_sys_t *sys = vd->sys; vout_display_opengl_Display(sys->vgl, &vd->source); picture_Release(picture); if (subpicture) subpicture_Delete(subpicture); CommonDisplay(vd); }
static void vd_display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture) { vout_display_sys_t *sys = vd->sys; picture_sys_t *pic_sys = picture->p_sys; MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer; MMAL_STATUS_T status; if (picture->format.i_frame_rate != sys->i_frame_rate || picture->format.i_frame_rate_base != sys->i_frame_rate_base || picture->b_progressive != sys->b_progressive || picture->b_top_field_first != sys->b_top_field_first) { sys->b_top_field_first = picture->b_top_field_first; sys->b_progressive = picture->b_progressive; sys->i_frame_rate = picture->format.i_frame_rate; sys->i_frame_rate_base = picture->format.i_frame_rate_base; configure_display(vd, NULL, &picture->format); } if (!pic_sys->displayed || !sys->opaque) { buffer->cmd = 0; buffer->length = sys->input->buffer_size; vlc_mutex_lock(&sys->buffer_mutex); while (sys->buffers_in_transit >= MAX_BUFFERS_IN_TRANSIT) vlc_cond_wait(&sys->buffer_cond, &sys->buffer_mutex); status = mmal_port_send_buffer(sys->input, buffer); if (status == MMAL_SUCCESS) ++sys->buffers_in_transit; vlc_mutex_unlock(&sys->buffer_mutex); if (status != MMAL_SUCCESS) { msg_Err(vd, "Failed to send buffer to input port. Frame dropped"); picture_Release(picture); } pic_sys->displayed = true; } else { picture_Release(picture); } display_subpicture(vd, subpicture); if (subpicture) subpicture_Delete(subpicture); if (sys->next_phase_check == 0 && sys->adjust_refresh_rate) maintain_phase_sync(vd); sys->next_phase_check = (sys->next_phase_check + 1) % PHASE_CHECK_INTERVAL; }
static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture) { vout_display_sys_t *sys = vd->sys; /* refcount lowers to 0, and pool_cfg.unlock is called */ UnlockPicture(picture, true); picture_Release(picture); if (sys->b_sub_pic_locked) { sys->b_sub_pic_locked = false; AndroidWindow_UnlockPicture(sys, sys->p_sub_window, sys->p_sub_pic, true); } if (subpicture) subpicture_Delete(subpicture); }
/** * \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; }
int transcode_osd_process( sout_stream_t *p_stream, sout_stream_id_sys_t *id, block_t *in, block_t **out ) { sout_stream_sys_t *p_sys = p_stream->p_sys; subpicture_t *p_subpic = NULL; /* Check if we have a subpicture to send */ if( p_sys->p_spu && in->i_dts > VLC_TS_INVALID ) { video_format_t fmt; video_format_Init( &fmt, 0 ); video_format_Setup( &fmt, 0, 720, 576, 720, 576, 1, 1 ); p_subpic = spu_Render( p_sys->p_spu, NULL, &fmt, &fmt, in->i_dts, in->i_dts, false ); } else { msg_Warn( p_stream, "spu channel not initialized, doing it now" ); if( !p_sys->p_spu ) p_sys->p_spu = spu_Create( p_stream ); } if( p_subpic ) { block_t *p_block = NULL; if( p_sys->b_master_sync && p_sys->i_master_drift ) { p_subpic->i_start -= p_sys->i_master_drift; if( p_subpic->i_stop ) p_subpic->i_stop -= p_sys->i_master_drift; } p_block = id->p_encoder->pf_encode_sub( id->p_encoder, p_subpic ); subpicture_Delete( p_subpic ); if( p_block ) { p_block->i_dts = p_block->i_pts = in->i_dts; block_ChainAppend( out, p_block ); return VLC_SUCCESS; } } return VLC_EGENERIC; }
int transcode_spu_process( sout_stream_t *p_stream, sout_stream_id_sys_t *id, block_t *in, block_t **out ) { sout_stream_sys_t *p_sys = p_stream->p_sys; subpicture_t *p_subpic; *out = NULL; p_subpic = id->p_decoder->pf_decode_sub( id->p_decoder, &in ); if( !p_subpic ) { /* We just don't have anything to handle now, go own*/ return VLC_SUCCESS; } if( p_sys->b_master_sync && p_sys->i_master_drift ) { p_subpic->i_start -= p_sys->i_master_drift; if( p_subpic->i_stop ) p_subpic->i_stop -= p_sys->i_master_drift; } if( p_sys->b_soverlay ) { spu_PutSubpicture( p_sys->p_spu, p_subpic ); return VLC_SUCCESS; } else { block_t *p_block; p_block = id->p_encoder->pf_encode_sub( id->p_encoder, p_subpic ); subpicture_Delete( p_subpic ); if( p_block ) { block_ChainAppend( out, p_block ); return VLC_SUCCESS; } } return VLC_EGENERIC; }
/**************************************************************************** * DecodeBlock: ****************************************************************************/ static subpicture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = p_dec->p_sys; subpicture_t *p_spu = NULL; block_t *p_block; if( !pp_block || *pp_block == NULL ) return NULL; p_block = *pp_block; *pp_block = NULL; if( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) { Flush( p_dec ); block_Release( p_block ); return NULL; } if( p_block->i_buffer == 0 || p_block->p_buffer[0] == '\0' ) { block_Release( p_block ); return NULL; } subpicture_updater_sys_t *p_spu_sys = malloc( sizeof(*p_spu_sys) ); if( !p_spu_sys ) { block_Release( p_block ); return NULL; } subpicture_updater_t updater = { .pf_validate = SubpictureValidate, .pf_update = SubpictureUpdate, .pf_destroy = SubpictureDestroy, .p_sys = p_spu_sys, }; p_spu = decoder_NewSubpicture( p_dec, &updater ); if( !p_spu ) { msg_Warn( p_dec, "can't get spu buffer" ); free( p_spu_sys ); block_Release( p_block ); return NULL; } p_spu_sys->p_img = NULL; p_spu_sys->p_dec_sys = p_sys; p_spu_sys->i_subs_len = p_block->i_buffer; p_spu_sys->p_subs_data = malloc( p_block->i_buffer ); p_spu_sys->i_pts = p_block->i_pts; if( !p_spu_sys->p_subs_data ) { subpicture_Delete( p_spu ); block_Release( p_block ); return NULL; } memcpy( p_spu_sys->p_subs_data, p_block->p_buffer, p_block->i_buffer ); p_spu->i_start = p_block->i_pts; p_spu->i_stop = __MAX( p_sys->i_max_stop, p_block->i_pts + p_block->i_length ); p_spu->b_ephemer = true; p_spu->b_absolute = true; p_sys->i_max_stop = p_spu->i_stop; vlc_mutex_lock( &p_sys->lock ); if( p_sys->p_track ) { ass_process_chunk( p_sys->p_track, p_spu_sys->p_subs_data, p_spu_sys->i_subs_len, p_block->i_pts / 1000, p_block->i_length / 1000 ); } vlc_mutex_unlock( &p_sys->lock ); DecSysHold( p_sys ); /* Keep a reference for the returned subpicture */ block_Release( p_block ); return p_spu; }
int transcode_video_process( sout_stream_t *p_stream, sout_stream_id_t *id, block_t *in, block_t **out ) { sout_stream_sys_t *p_sys = p_stream->p_sys; bool b_need_duplicate = false; picture_t *p_pic, *p_pic2 = NULL; *out = NULL; if( in == NULL ) { if( p_sys->i_threads == 0 ) { block_t *p_block; do { video_timer_start( id->p_encoder ); p_block = id->p_encoder->pf_encode_video(id->p_encoder, NULL ); video_timer_stop( id->p_encoder ); block_ChainAppend( out, p_block ); } while( p_block ); } else { /* * FIXME: we need EncoderThread() to flush buffers and signal us * when it's done so we can send the last frames to the chain */ } return VLC_SUCCESS; } while( (p_pic = id->p_decoder->pf_decode_video( id->p_decoder, &in )) ) { if( p_stream->p_sout->i_out_pace_nocontrol && p_sys->b_hurry_up ) { mtime_t current_date = mdate(); if( current_date + 50000 > p_pic->date ) { msg_Dbg( p_stream, "late picture skipped (%"PRId64")", current_date + 50000 - p_pic->date ); picture_Release( p_pic ); continue; } } if( p_sys->b_master_sync ) { mtime_t i_video_drift; mtime_t i_master_drift = p_sys->i_master_drift; mtime_t i_pts; i_pts = date_Get( &id->interpolated_pts ) + 1; if ( p_pic->date - i_pts > MASTER_SYNC_MAX_DRIFT || p_pic->date - i_pts < -MASTER_SYNC_MAX_DRIFT ) { msg_Dbg( p_stream, "drift is too high, resetting master sync" ); date_Set( &id->interpolated_pts, p_pic->date ); i_pts = p_pic->date + 1; } i_video_drift = p_pic->date - i_pts; b_need_duplicate = false; /* Set the pts of the frame being encoded */ p_pic->date = i_pts; if( i_video_drift < (i_master_drift - 50000) ) { #if 0 msg_Dbg( p_stream, "dropping frame (%i)", (int)(i_video_drift - i_master_drift) ); #endif picture_Release( p_pic ); continue; } else if( i_video_drift > (i_master_drift + 50000) ) { #if 0 msg_Dbg( p_stream, "adding frame (%i)", (int)(i_video_drift - i_master_drift) ); #endif b_need_duplicate = true; } } if( unlikely( !id->p_encoder->p_module ) ) { transcode_video_encoder_init( p_stream, id ); transcode_video_filter_init( p_stream, id ); if( transcode_video_encoder_open( p_stream, id ) != VLC_SUCCESS ) { picture_Release( p_pic ); transcode_video_close( p_stream, id ); id->b_transcode = false; return VLC_EGENERIC; } } /* Run filter chain */ if( id->p_f_chain ) p_pic = filter_chain_VideoFilter( id->p_f_chain, p_pic ); /* * Encoding */ /* Check if we have a subpicture to overlay */ if( p_sys->p_spu ) { video_format_t fmt; if( filter_chain_GetLength( id->p_f_chain ) > 0 ) fmt = filter_chain_GetFmtOut( id->p_f_chain )->video; else fmt = id->p_decoder->fmt_out.video; subpicture_t *p_subpic = spu_Render( p_sys->p_spu, NULL, &fmt, &fmt, p_pic->date, p_pic->date, false ); /* Overlay subpicture */ if( p_subpic ) { if( picture_IsReferenced( p_pic ) && !filter_chain_GetLength( id->p_f_chain ) ) { /* We can't modify the picture, we need to duplicate it */ picture_t *p_tmp = video_new_buffer_decoder( id->p_decoder ); if( p_tmp ) { picture_Copy( p_tmp, p_pic ); picture_Release( p_pic ); p_pic = p_tmp; } } if( !p_sys->p_spu_blend ) p_sys->p_spu_blend = filter_NewBlend( VLC_OBJECT( p_sys->p_spu ), &fmt ); if( p_sys->p_spu_blend ) picture_BlendSubpicture( p_pic, p_sys->p_spu_blend, p_subpic ); subpicture_Delete( p_subpic ); } } /* Run user specified filter chain */ if( id->p_uf_chain ) p_pic = filter_chain_VideoFilter( id->p_uf_chain, p_pic ); if( p_sys->i_threads == 0 ) { block_t *p_block; video_timer_start( id->p_encoder ); p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic ); video_timer_stop( id->p_encoder ); block_ChainAppend( out, p_block ); } if( p_sys->b_master_sync ) { mtime_t i_pts = date_Get( &id->interpolated_pts ) + 1; if ( p_pic->date - i_pts > MASTER_SYNC_MAX_DRIFT || p_pic->date - i_pts < -MASTER_SYNC_MAX_DRIFT ) { msg_Dbg( p_stream, "drift is too high, resetting master sync" ); date_Set( &id->interpolated_pts, p_pic->date ); i_pts = p_pic->date + 1; } date_Increment( &id->interpolated_pts, 1 ); if( unlikely( b_need_duplicate ) ) { if( p_sys->i_threads >= 1 ) { /* We can't modify the picture, we need to duplicate it */ p_pic2 = video_new_buffer_decoder( id->p_decoder ); if( p_pic2 != NULL ) { picture_Copy( p_pic2, p_pic ); p_pic2->date = i_pts; } } else { block_t *p_block; p_pic->date = i_pts; video_timer_start( id->p_encoder ); p_block = id->p_encoder->pf_encode_video(id->p_encoder, p_pic); video_timer_stop( id->p_encoder ); block_ChainAppend( out, p_block ); } } } if( p_sys->i_threads == 0 ) { picture_Release( p_pic ); } else { vlc_mutex_lock( &p_sys->lock_out ); p_sys->pp_pics[p_sys->i_last_pic++] = p_pic; p_sys->i_last_pic %= PICTURE_RING_SIZE; *out = p_sys->p_buffers; p_sys->p_buffers = NULL; if( p_pic2 != NULL ) { p_sys->pp_pics[p_sys->i_last_pic++] = p_pic2; p_sys->i_last_pic %= PICTURE_RING_SIZE; } vlc_cond_signal( &p_sys->cond ); vlc_mutex_unlock( &p_sys->lock_out ); } } return VLC_SUCCESS; }
/**************************************************************************** * 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; }
/**************************************************************************** * 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; }
/***************************************************************************** * 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; int 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. */ subpicture_t *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; p_spu->i_original_picture_width = p_sys->i_width; p_spu->i_original_picture_height = p_sys->i_height; 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( int 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 */ for( int 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( int 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; 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 ; video_format_Init( &fmt_in, 0 ); video_format_Init( &fmt_out, 0 ); 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" ); video_format_Clean( &fmt_in ); video_format_Clean( &fmt_out ); 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 ) { video_format_Clean( &fmt_in ); video_format_Clean( &fmt_out ); msg_Err( p_filter, "cannot allocate SPU region" ); subpicture_Delete( p_spu ); vlc_global_unlock( VLC_MOSAIC_MUTEX ); vlc_mutex_unlock( &p_sys->lock ); return NULL; } 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; } video_format_Clean( &fmt_in ); video_format_Clean( &fmt_out ); p_region_prev = p_region; } vlc_global_unlock( VLC_MOSAIC_MUTEX ); vlc_mutex_unlock( &p_sys->lock ); return p_spu; }
/***************************************************************************** * SubpicDestroyClone: destroy cloned subpicture (shared references will not * be destroyed) *****************************************************************************/ static void SubpicDestroyClone( subpicture_t *p_subpic ) { p_subpic->p_region = NULL; /* don't destroy region */ subpicture_Delete( p_subpic ); }
int transcode_spu_process( sout_stream_t *p_stream, sout_stream_id_sys_t *id, block_t *in, block_t **out ) { VLC_UNUSED(p_stream); *out = NULL; bool b_error = false; int ret = id->p_decoder->pf_decode( id->p_decoder, in ); if( ret != VLCDEC_SUCCESS ) return VLC_EGENERIC; subpicture_t *p_subpics = transcode_dequeue_all_subs( id ); do { subpicture_t *p_subpic = p_subpics; if( p_subpic == NULL ) break; p_subpics = p_subpic->p_next; p_subpic->p_next = NULL; if( b_error ) { subpicture_Delete( p_subpic ); continue; } vlc_tick_t drift; if( id->pf_get_master_drift && (drift = id->pf_get_master_drift( id->callback_data )) ) { p_subpic->i_start -= drift; if( p_subpic->i_stop ) p_subpic->i_stop -= drift; } if( id->p_enccfg->i_codec == 0 /* overlay */ ) { if( !id->pf_send_subpicture ) { subpicture_Delete( p_subpic ); b_error = true; } else id->pf_send_subpicture( id->callback_data, p_subpic ); } else { block_t *p_block; es_format_t fmt; es_format_Init( &fmt, VIDEO_ES, VLC_CODEC_TEXT ); unsigned w, h; if( id->pf_get_output_dimensions == NULL || id->pf_get_output_dimensions( id->callback_data, &w, &h ) != VLC_SUCCESS ) { w = id->p_enccfg->spu.i_width; h = id->p_enccfg->spu.i_height; } fmt.video.i_sar_num = fmt.video.i_visible_width = fmt.video.i_width = w; fmt.video.i_sar_den = fmt.video.i_visible_height = fmt.video.i_height = h; subpicture_Update( p_subpic, &fmt.video, &fmt.video, p_subpic->i_start ); es_format_Clean( &fmt ); p_block = transcode_encoder_encode( id->encoder, p_subpic ); subpicture_Delete( p_subpic ); if( p_block ) block_ChainAppend( out, p_block ); else b_error = true; } } while( p_subpics ); return b_error ? VLC_EGENERIC : VLC_SUCCESS; }
static void OutputFrame( sout_stream_sys_t *p_sys, picture_t *p_pic, sout_stream_t *p_stream, sout_stream_id_t *id, block_t **out ) { picture_t *p_pic2 = NULL; bool b_need_duplicate=false; /* If input pts + input_frame_interval is lower than next_output_pts - output_frame_interval * Then the future input frame should fit better and we can drop this one * * Duplication need is checked in OutputFrame */ if( ( p_pic->date + (mtime_t)id->i_input_frame_interval ) < ( date_Get( &id->next_output_pts ) ) ) { #if 0 msg_Dbg( p_stream, "dropping frame (%"PRId64" + %"PRId64" vs %"PRId64")", p_pic->date, id->i_input_frame_interval, date_Get(&id->next_output_pts) ); #endif picture_Release( p_pic ); return; } /* * Encoding */ /* Check if we have a subpicture to overlay */ if( p_sys->p_spu ) { video_format_t fmt = id->p_encoder->fmt_in.video; if( fmt.i_visible_width <= 0 || fmt.i_visible_height <= 0 ) { fmt.i_visible_width = fmt.i_width; fmt.i_visible_height = fmt.i_height; fmt.i_x_offset = 0; fmt.i_y_offset = 0; } subpicture_t *p_subpic = spu_Render( p_sys->p_spu, NULL, &fmt, &fmt, p_pic->date, p_pic->date, false ); /* Overlay subpicture */ if( p_subpic ) { if( picture_IsReferenced( p_pic ) && !filter_chain_GetLength( id->p_f_chain ) ) { /* We can't modify the picture, we need to duplicate it, * in this point the picture is already p_encoder->fmt.in format*/ picture_t *p_tmp = video_new_buffer_encoder( id->p_encoder ); if( likely( p_tmp ) ) { picture_Copy( p_tmp, p_pic ); picture_Release( p_pic ); p_pic = p_tmp; } } if( unlikely( !p_sys->p_spu_blend ) ) p_sys->p_spu_blend = filter_NewBlend( VLC_OBJECT( p_sys->p_spu ), &fmt ); if( likely( p_sys->p_spu_blend ) ) picture_BlendSubpicture( p_pic, p_sys->p_spu_blend, p_subpic ); subpicture_Delete( p_subpic ); } } /*This pts is handled, increase clock to next one*/ date_Increment( &id->next_output_pts, id->p_encoder->fmt_in.video.i_frame_rate_base ); if( p_sys->i_threads == 0 ) { block_t *p_block; p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic ); block_ChainAppend( out, p_block ); } /* we need to duplicate while next_output_pts + output_frame_interval < input_pts (next input pts)*/ b_need_duplicate = ( date_Get( &id->next_output_pts ) + id->i_output_frame_interval ) < ( date_Get( &id->interpolated_pts ) ); if( p_sys->i_threads ) { if( p_sys->b_master_sync ) { p_pic2 = video_new_buffer_encoder( id->p_encoder ); if( likely( p_pic2 != NULL ) ) picture_Copy( p_pic2, p_pic ); } vlc_mutex_lock( &p_sys->lock_out ); picture_fifo_Push( p_sys->pp_pics, p_pic ); vlc_cond_signal( &p_sys->cond ); vlc_mutex_unlock( &p_sys->lock_out ); } while( (p_sys->b_master_sync && b_need_duplicate )) { if( p_sys->i_threads >= 1 ) { picture_t *p_tmp = NULL; /* We can't modify the picture, we need to duplicate it */ p_tmp = video_new_buffer_encoder( id->p_encoder ); if( likely( p_tmp != NULL ) ) { picture_Copy( p_tmp, p_pic2 ); p_tmp->date = date_Get( &id->next_output_pts ); vlc_mutex_lock( &p_sys->lock_out ); picture_fifo_Push( p_sys->pp_pics, p_tmp ); vlc_cond_signal( &p_sys->cond ); vlc_mutex_unlock( &p_sys->lock_out ); } } else { block_t *p_block; p_pic->date = date_Get( &id->next_output_pts ); p_block = id->p_encoder->pf_encode_video(id->p_encoder, p_pic); block_ChainAppend( out, p_block ); } #if 0 msg_Dbg( p_stream, "duplicated frame"); #endif date_Increment( &id->next_output_pts, id->p_encoder->fmt_in.video.i_frame_rate_base ); b_need_duplicate = ( date_Get( &id->next_output_pts ) + id->i_output_frame_interval ) < ( date_Get( &id->interpolated_pts ) ); } if( p_sys->i_threads && p_pic2 ) picture_Release( p_pic2 ); else if ( p_sys->i_threads == 0 ) picture_Release( p_pic ); }
/** * Sub source */ static subpicture_t *FilterSub(filter_t *p_filter, vlc_tick_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 NULL; } 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 */ video_format_Init(&fmt, 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); 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; }
static void spu_del_buffer( decoder_t *p_dec, subpicture_t *p_subpic ) { VLC_UNUSED( p_dec ); subpicture_Delete( p_subpic ); }
/** * 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" ); subpicture_Delete( 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; }
/**************************************************************************** * 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 ) { subpicture_Delete( 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 ); } 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 ); } 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 ); 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 ); } 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 ); } /* 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; }
static void OutputFrame( sout_stream_sys_t *p_sys, picture_t *p_pic, bool b_need_duplicate, sout_stream_t *p_stream, sout_stream_id_t *id, block_t **out ) { picture_t *p_pic2 = NULL; /* * Encoding */ /* Check if we have a subpicture to overlay */ if( p_sys->p_spu ) { video_format_t fmt = id->p_encoder->fmt_in.video; if( fmt.i_visible_width <= 0 || fmt.i_visible_height <= 0 ) { fmt.i_visible_width = fmt.i_width; fmt.i_visible_height = fmt.i_height; fmt.i_x_offset = 0; fmt.i_y_offset = 0; } subpicture_t *p_subpic = spu_Render( p_sys->p_spu, NULL, &fmt, &fmt, p_pic->date, p_pic->date, false ); /* Overlay subpicture */ if( p_subpic ) { if( picture_IsReferenced( p_pic ) && !filter_chain_GetLength( id->p_f_chain ) ) { /* We can't modify the picture, we need to duplicate it, * in this point the picture is already p_encoder->fmt.in format*/ picture_t *p_tmp = video_new_buffer_encoder( id->p_encoder ); if( likely( p_tmp ) ) { picture_Copy( p_tmp, p_pic ); picture_Release( p_pic ); p_pic = p_tmp; } } if( unlikely( !p_sys->p_spu_blend ) ) p_sys->p_spu_blend = filter_NewBlend( VLC_OBJECT( p_sys->p_spu ), &fmt ); if( likely( p_sys->p_spu_blend ) ) picture_BlendSubpicture( p_pic, p_sys->p_spu_blend, p_subpic ); subpicture_Delete( p_subpic ); } } if( p_sys->i_threads == 0 ) { block_t *p_block; p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic ); block_ChainAppend( out, p_block ); } if( p_sys->b_master_sync ) { mtime_t i_pts = date_Get( &id->interpolated_pts ) + 1; mtime_t i_video_drift = p_pic->date - i_pts; if (unlikely ( i_video_drift > MASTER_SYNC_MAX_DRIFT || i_video_drift < -MASTER_SYNC_MAX_DRIFT ) ) { msg_Dbg( p_stream, "drift is too high (%"PRId64"), resetting master sync", i_video_drift ); date_Set( &id->interpolated_pts, p_pic->date ); i_pts = p_pic->date + 1; } date_Increment( &id->interpolated_pts, 1 ); if( unlikely( b_need_duplicate ) ) { if( p_sys->i_threads >= 1 ) { /* We can't modify the picture, we need to duplicate it */ p_pic2 = video_new_buffer_encoder( id->p_encoder ); if( likely( p_pic2 != NULL ) ) { picture_Copy( p_pic2, p_pic ); p_pic2->date = i_pts; } } else { block_t *p_block; p_pic->date = i_pts; p_block = id->p_encoder->pf_encode_video(id->p_encoder, p_pic); block_ChainAppend( out, p_block ); } } } if( p_sys->i_threads == 0 ) { picture_Release( p_pic ); } else { vlc_mutex_lock( &p_sys->lock_out ); picture_fifo_Push( p_sys->pp_pics, p_pic ); if( p_pic2 != NULL ) { picture_fifo_Push( p_sys->pp_pics, p_pic2 ); } vlc_cond_signal( &p_sys->cond ); vlc_mutex_unlock( &p_sys->lock_out ); } }
/***************************************************************************** * 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" ); subpicture_Delete( 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; }