/***************************************************************************** * Render: displays previously rendered output ***************************************************************************** * This function send the currently rendered image to the internal opencv * filter for processing. *****************************************************************************/ static void Render( vout_thread_t *p_vout, picture_t *p_pic ) { picture_t *p_outpic = NULL; clock_t start, finish; double duration; while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) ) == NULL ) { if( !vlc_object_alive (p_vout) || p_vout->b_error ) { return; } msleep( VOUT_OUTMEM_SLEEP ); } vout_LinkPicture( p_vout->p_sys->p_vout, p_outpic ); start = clock(); if (p_vout->p_sys->i_wrapper_output == VINPUT) //output = input video { //This copy is a bit unfortunate but image_Convert can't write into an existing image so it is better to copy the //(say) 16bit YUV image here than a 32bit RGB image somehwere else. //It is also not that expensive in time. picture_Copy( p_outpic, p_pic ); VlcPictureToIplImage( p_vout, p_pic); //pass the image to the internal opencv filter for processing if ((p_vout->p_sys->p_opencv) && (p_vout->p_sys->p_opencv->p_module)) p_vout->p_sys->p_opencv->pf_video_filter( p_vout->p_sys->p_opencv, &(p_vout->p_sys->hacked_pic)); } else //output = processed video (NONE option not working yet) { VlcPictureToIplImage( p_vout, p_pic); //pass the image to the internal opencv filter for processing if ((p_vout->p_sys->p_opencv) && (p_vout->p_sys->p_opencv->p_module)) p_vout->p_sys->p_opencv->pf_video_filter( p_vout->p_sys->p_opencv, &(p_vout->p_sys->hacked_pic)); //copy the processed image into the output image if ((p_vout->p_sys->p_proc_image) && (p_vout->p_sys->p_proc_image->i_planes > 0)) picture_Copy( p_outpic, p_vout->p_sys->p_proc_image ); } //calculate duration finish = clock(); duration = (double)(finish - start) / CLOCKS_PER_SEC; if (p_vout->p_sys->i_verbosity > VERB_WARN) msg_Dbg( p_vout, "Render took %2.4f seconds", duration ); ReleaseImages(p_vout); p_outpic->date = p_pic->date; vout_UnlinkPicture( p_vout->p_sys->p_vout, p_outpic ); vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic ); }
static void Display(vout_display_t *vd, picture_t *picture) { vout_display_sys_t *sys = vd->sys; IDirectFBSurface *primary = sys->primary; void *pixels; int pitch; if (primary->Lock(primary, DSLF_WRITE, &pixels, &pitch) == DFB_OK) { picture_resource_t rsc; memset(&rsc, 0, sizeof(rsc)); rsc.p[0].p_pixels = pixels; rsc.p[0].i_lines = sys->height; rsc.p[0].i_pitch = pitch; picture_t *direct = picture_NewFromResource(&vd->fmt, &rsc); if (direct) { picture_Copy(direct, picture); picture_Release(direct); } if (primary->Unlock(primary) == DFB_OK) primary->Flip(primary, NULL, 0); } picture_Release(picture); }
static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture) { vout_display_sys_t *sys = vd->sys; /* swap the two Y offsets if the drivers supports panning */ if (sys->has_pan) { sys->var_info.yoffset = 0; /*vd->sys->var_info.yoffset = vd->sys->var_info.yres; */ /* the X offset should be 0, but who knows ... * some other app might have played with the framebuffer */ sys->var_info.xoffset = 0; /* FIXME 'static' is damn wrong and it's dead code ... */ static int panned = 0; if (panned < 0) { ioctl(sys->fd, FBIOPAN_DISPLAY, &sys->var_info); panned++; } } if (!sys->is_hw_accel) picture_Copy(sys->picture, picture); picture_Release(picture); VLC_UNUSED(subpicture); }
/**************************************************************************** * DecodeBlock: the whole thing ****************************************************************************/ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { decoder_sys_t *p_sys = (decoder_sys_t*) p_dec->p_sys; picture_t *p_pic; if( pp_block == NULL || !*pp_block ) return NULL; p_pic = decoder_NewPicture( p_dec ); if( p_pic == NULL ) { msg_Err( p_dec, "cannot get picture" ); goto error; } if( p_sys->b_reload && (mdate() >= p_sys->i_next) ) { var_TriggerCallback( p_dec, "fake-file" ); /* next period */ p_sys->i_next = (mtime_t)(p_sys->i_reload + mdate()); } vlc_mutex_lock( &p_dec->p_sys->lock ); picture_Copy( p_pic, p_dec->p_sys->p_image ); vlc_mutex_unlock( &p_dec->p_sys->lock ); p_pic->date = (*pp_block)->i_pts; error: block_Release( *pp_block ); *pp_block = NULL; return p_pic; }
/** * Video filter */ static picture_t *FilterVideo(filter_t *p_filter, picture_t *p_src) { filter_sys_t *p_sys = p_filter->p_sys; BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph); picture_t *p_dst = filter_NewPicture(p_filter); if (!p_dst) { picture_Release(p_src); return NULL; } picture_Copy(p_dst, p_src); /* */ vlc_mutex_lock(&p_sys->lock); /* */ const picture_t *p_pic = p_BarGraph->p_pic; if (!p_pic) goto out; const video_format_t *p_fmt = &p_pic->format; const int i_dst_w = p_filter->fmt_out.video.i_visible_width; const int i_dst_h = p_filter->fmt_out.video.i_visible_height; if (p_sys->i_pos) { if (p_sys->i_pos & SUBPICTURE_ALIGN_BOTTOM) p_sys->i_pos_y = i_dst_h - p_fmt->i_visible_height; else if (!(p_sys->i_pos & SUBPICTURE_ALIGN_TOP)) p_sys->i_pos_y = (i_dst_h - p_fmt->i_visible_height) / 2; else p_sys->i_pos_y = 0; if (p_sys->i_pos & SUBPICTURE_ALIGN_RIGHT) p_sys->i_pos_x = i_dst_w - p_fmt->i_visible_width; else if (!(p_sys->i_pos & SUBPICTURE_ALIGN_LEFT)) p_sys->i_pos_x = (i_dst_w - p_fmt->i_visible_width) / 2; else p_sys->i_pos_x = 0; } /* */ const int i_alpha = p_BarGraph->i_alpha; if (filter_ConfigureBlend(p_sys->p_blend, i_dst_w, i_dst_h, p_fmt) || filter_Blend(p_sys->p_blend, p_dst, p_sys->i_pos_x, p_sys->i_pos_y, p_pic, i_alpha)) msg_Err(p_filter, "failed to blend a picture"); out: vlc_mutex_unlock(&p_sys->lock); picture_Release(p_src); return p_dst; }
/** * This function filter a picture */ static int Filter( video_splitter_t *p_splitter, picture_t *pp_dst[], picture_t *p_src ) { if( video_splitter_NewPicture( p_splitter, pp_dst ) ) { picture_Release( p_src ); return VLC_EGENERIC; } for( int i = 0; i < p_splitter->i_output; i++ ) picture_Copy( pp_dst[i], p_src ); picture_Release( p_src ); return VLC_SUCCESS; }
static void CopyPad( picture_t *p_dst, const picture_t *p_src ) { picture_Copy( p_dst, p_src ); for( int n = 0; n < p_dst->i_planes; n++ ) { const plane_t *s = &p_src->p[n]; plane_t *d = &p_dst->p[n]; for( int y = 0; y < s->i_lines; y++ ) { for( int x = s->i_visible_pitch; x < d->i_visible_pitch; x += s->i_pixel_pitch ) memcpy( &d->p_pixels[y*d->i_pitch + x], &d->p_pixels[y*d->i_pitch + s->i_visible_pitch - s->i_pixel_pitch], s->i_pixel_pitch ); } } }
void vout_snapshot_Set(vout_snapshot_t *snap, const video_format_t *fmt, const picture_t *picture) { if (!fmt) fmt = &picture->format; vlc_mutex_lock(&snap->lock); while (snap->request_count > 0) { picture_t *dup = picture_NewFromFormat(fmt); if (!dup) break; picture_Copy(dup, picture); dup->p_next = snap->picture; snap->picture = dup; snap->request_count--; } vlc_cond_broadcast(&snap->wait); vlc_mutex_unlock(&snap->lock); }
static int Filter( video_splitter_t *p_splitter, picture_t *pp_dst[], picture_t *p_src ) { video_splitter_sys_t *p_sys = p_splitter->p_sys; if( video_splitter_NewPicture( p_splitter, pp_dst ) ) { picture_Release( p_src ); return VLC_EGENERIC; } for( int y = 0; y < p_sys->i_row; y++ ) { for( int x = 0; x < p_sys->i_col; x++ ) { wall_output_t *p_output = &p_sys->pp_output[x][y]; if( !p_output->b_active ) continue; picture_t *p_dst = pp_dst[p_output->i_output]; /* */ picture_t tmp = *p_src; for( int i = 0; i < tmp.i_planes; i++ ) { plane_t *p0 = &tmp.p[0]; plane_t *p = &tmp.p[i]; const int i_y = p_output->i_top * p->i_visible_pitch / p0->i_visible_pitch; const int i_x = p_output->i_left * p->i_visible_lines / p0->i_visible_lines; p->p_pixels += i_y * p->i_pitch + ( i_x - (i_x % p->i_pixel_pitch)); } picture_Copy( p_dst, &tmp ); } } picture_Release( p_src ); return VLC_SUCCESS; }
static void SnapshotRatio( filter_t *p_filter, picture_t *p_pic ) { filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys; if( !p_pic ) return; if( p_sys->i_frames % p_sys->i_ratio != 0 ) { p_sys->i_frames++; return; } p_sys->i_frames++; if( p_sys->scene.p_pic ) picture_Release( p_sys->scene.p_pic ); if( (p_sys->i_width <= 0) && (p_sys->i_height > 0) ) { p_sys->i_width = (p_pic->format.i_width * p_sys->i_height) / p_pic->format.i_height; } else if( (p_sys->i_height <= 0) && (p_sys->i_width > 0) ) { p_sys->i_height = (p_pic->format.i_height * p_sys->i_width) / p_pic->format.i_width; } else if( (p_sys->i_width <= 0) && (p_sys->i_height <= 0) ) { p_sys->i_width = p_pic->format.i_width; p_sys->i_height = p_pic->format.i_height; } p_sys->scene.p_pic = picture_NewFromFormat( &p_pic->format ); if( p_sys->scene.p_pic ) { picture_Copy( p_sys->scene.p_pic, p_pic ); SavePicture( p_filter, p_sys->scene.p_pic ); } }
/***************************************************************************** * 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; }
/** * 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; }
/** * Video filter */ static picture_t *FilterVideo( filter_t *p_filter, picture_t *p_src ) { filter_sys_t *p_sys = p_filter->p_sys; logo_list_t *p_list = &p_sys->list; picture_t *p_dst = filter_NewPicture( p_filter ); if( !p_dst ) goto exit; picture_Copy( p_dst, p_src ); /* */ vlc_mutex_lock( &p_sys->lock ); logo_t *p_logo; if( p_list->i_next_pic < p_src->date ) p_logo = LogoListNext( p_list, p_src->date ); else p_logo = LogoListCurrent( p_list ); /* */ const picture_t *p_pic = p_logo->p_pic; if( p_pic ) { const video_format_t *p_fmt = &p_pic->format; const int i_dst_w = p_filter->fmt_out.video.i_visible_width; const int i_dst_h = p_filter->fmt_out.video.i_visible_height; if( p_sys->i_pos ) { if( p_sys->i_pos & SUBPICTURE_ALIGN_BOTTOM ) { p_sys->i_pos_y = i_dst_h - p_fmt->i_visible_height; } else if ( !(p_sys->i_pos & SUBPICTURE_ALIGN_TOP) ) { p_sys->i_pos_y = ( i_dst_h - p_fmt->i_visible_height ) / 2; } else { p_sys->i_pos_y = 0; } if( p_sys->i_pos & SUBPICTURE_ALIGN_RIGHT ) { p_sys->i_pos_x = i_dst_w - p_fmt->i_visible_width; } else if ( !(p_sys->i_pos & SUBPICTURE_ALIGN_LEFT) ) { p_sys->i_pos_x = ( i_dst_w - p_fmt->i_visible_width ) / 2; } else { p_sys->i_pos_x = 0; } } /* */ const int i_alpha = p_logo->i_alpha != -1 ? p_logo->i_alpha : p_list->i_alpha; if( filter_ConfigureBlend( p_sys->p_blend, i_dst_w, i_dst_h, p_fmt ) || filter_Blend( p_sys->p_blend, p_dst, p_sys->i_pos_x, p_sys->i_pos_y, p_pic, i_alpha ) ) { msg_Err( p_filter, "failed to blend a picture" ); } } vlc_mutex_unlock( &p_sys->lock ); exit: picture_Release( p_src ); return p_dst; }
/***************************************************************************** * 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; }
static picture_t *ImageConvert( image_handler_t *p_image, picture_t *p_pic, video_format_t *p_fmt_in, video_format_t *p_fmt_out ) { picture_t *p_pif; if( !p_fmt_out->i_width && !p_fmt_out->i_height && p_fmt_out->i_sar_num && p_fmt_out->i_sar_den && p_fmt_out->i_sar_num * p_fmt_in->i_sar_den != p_fmt_out->i_sar_den * p_fmt_in->i_sar_num ) { p_fmt_out->i_width = p_fmt_in->i_sar_num * (int64_t)p_fmt_out->i_sar_den * p_fmt_in->i_width / p_fmt_in->i_sar_den / p_fmt_out->i_sar_num; p_fmt_out->i_visible_width = p_fmt_in->i_sar_num * (int64_t)p_fmt_out->i_sar_den * p_fmt_in->i_visible_width / p_fmt_in->i_sar_den / p_fmt_out->i_sar_num; } if( !p_fmt_out->i_chroma ) p_fmt_out->i_chroma = p_fmt_in->i_chroma; if( !p_fmt_out->i_width ) p_fmt_out->i_width = p_fmt_out->i_visible_width = p_fmt_in->i_width; if( !p_fmt_out->i_height ) p_fmt_out->i_height = p_fmt_out->i_visible_height = p_fmt_in->i_height; if( !p_fmt_out->i_sar_num ) p_fmt_out->i_sar_num = p_fmt_in->i_sar_num; if( !p_fmt_out->i_sar_den ) p_fmt_out->i_sar_den = p_fmt_in->i_sar_den; if( p_image->p_filter ) if( p_image->p_filter->fmt_in.video.i_chroma != p_fmt_in->i_chroma || p_image->p_filter->fmt_out.video.i_chroma != p_fmt_out->i_chroma ) { /* We need to restart a new filter */ DeleteFilter( p_image->p_filter ); p_image->p_filter = NULL; } /* Start a filter */ if( !p_image->p_filter ) { es_format_t fmt_in; es_format_Init( &fmt_in, VIDEO_ES, p_fmt_in->i_chroma ); fmt_in.video = *p_fmt_in; p_image->p_filter = CreateFilter( p_image->p_parent, &fmt_in, p_fmt_out ); if( !p_image->p_filter ) { return NULL; } } else { /* Filters should handle on-the-fly size changes */ p_image->p_filter->fmt_in.video = *p_fmt_in; p_image->p_filter->fmt_out.video = *p_fmt_out; } picture_Hold( p_pic ); p_pif = p_image->p_filter->pf_video_filter( p_image->p_filter, p_pic ); if( p_fmt_in->i_chroma == p_fmt_out->i_chroma && p_fmt_in->i_width == p_fmt_out->i_width && p_fmt_in->i_height == p_fmt_out->i_height ) { /* Duplicate image */ picture_Release( p_pif ); /* XXX: Better fix must be possible */ p_pif = filter_NewPicture( p_image->p_filter ); if( p_pif ) picture_Copy( p_pif, p_pic ); } return p_pif; }
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 ); }
/** * @brief VLC filter callback * @return picture(s) containing the filtered frames */ static picture_t* y4m_filter(filter_t* intf, picture_t* srcPic) { filter_sys_t* sys = intf->p_sys; //msg_Info(intf, ">>>> filter"); if (!srcPic) { //msg_Info(intf, ">>> filter: NULL INPUT"); return NULL; } // will be stored to sys->lastDate on return mtime_t currDate = srcPic->date; // if there was a problem with the subprocess then send back // the picture unmodified, at least we won't freeze up vlc if (sys->startFailed || sys->threadExit) goto ECHO_RETURN; // start subprocess and write the y4m header // fixme: this can go in open() if fmt_in matches the srcPic if (!sys->startFailed && !sys->childPid) { sys->startFailed = !startProcess(intf); if (sys->startFailed) goto ECHO_RETURN; if (0 >= writeY4mHeader(intf, srcPic, sys->stdin)) { msg_Err(intf, "writeY4mHeader failed: errno=%d %s", errno, strerror(errno)); stopProcess(intf); sys->startFailed = true; goto ECHO_RETURN; } } // // control the buffering level by monitoring the input/output fifos // // the input/output fifos are emptied/filled by input/output threads // in response to the subprocess reading/writing frames // // if the input fifo is empty, then probably the subprocess wants // more input, so we should buffer more input // // if the output fifo is empty, and the input fifo is not empty, then // probably the subprocess is about to write out a frame and we // can wait for it to arrive. in practice, most of the time there // is no waiting needed, unless the filter is too slow to keep up. // bool inputEmpty = true; bool outputEmpty = true; picture_t* tmp = picture_fifo_Peek(sys->inputFifo); if (tmp) { picture_Release(tmp); inputEmpty = false; } tmp = picture_fifo_Peek(sys->outputFifo); if (tmp) { picture_Release(tmp); outputEmpty = false; } // copy picture to input fifo, we can't use picture_Hold or else // the decoder or vout would run out of pictures in its pool picture_t* inPic = picture_NewFromFormat(&srcPic->format); picture_Copy(inPic, srcPic); picture_fifo_Push(sys->inputFifo, inPic); // signal input thread to wake up and write some more data out vlc_mutex_lock(&sys->inputMutex); sys->bufferedIn++; vlc_cond_signal(&sys->inputCond); vlc_mutex_unlock(&sys->inputMutex); // if echo is enabled, we're done // todo: there should be a limiter on the input buffering in case // the subprocess can't keep up if (sys->echo) goto ECHO_RETURN; // keeps track of the number of buffered output pictures, assumes // an integer ratio // fixme: needs modification to support non-integer ratios // and ratios < 1 sys->bufferedOut += sys->bufferRatio; // handle buffering if (outputEmpty && inputEmpty) { // we haven't supplied enough input, raise the minimum // level of buffer to keep and return sys->minBuffered += sys->bufferRatio; msg_Info(intf, "buffer more input: buffers:%d:%d:%d", sys->bufferedIn, sys->bufferedOut, sys->minBuffered); goto NULL_RETURN; } if (outputEmpty) waitForOutput(intf); // if we don't know what the frame interval is, make it 0 which // probably causes the next frames out to drop // note: this happens at least every time y4m_flush() is called // for example when seeking if (currDate <= sys->lastDate || sys->lastDate == 0) { //msg_Err(intf, "currDate <= lastDate"); //goto ECHO_RETURN; sys->lastDate = currDate; } // reference to first and last picture we are returning picture_t* first = NULL; picture_t* last = NULL; picture_t* pic; while( (pic = picture_fifo_Pop(sys->outputFifo)) ) { // do output setup when we see the first frame out from the filter, // it could have a different frame rate, chroma, size, etc than // the frame going in if (!sys->gotFirstOutput) { sys->gotFirstOutput = true; // get the in/out frame ratio by comparing frame rates float speed = ((float)srcPic->format.i_frame_rate_base * (float)pic->format.i_frame_rate) / ((float)srcPic->format.i_frame_rate * (float)pic->format.i_frame_rate_base); if (speed < 1.0) { msg_Err(intf, "frame rate reduction isn't supported yet"); } else if (speed > 1.0) { if (ceil(speed) != speed) msg_Err(intf, "frame rate change must be integer ratio"); sys->bufferRatio = speed; // initial ratio was 1.0, need to correct the number of buffered frames // now that we know what it is sys->bufferedOut *= sys->bufferRatio; sys->minBuffered *= sys->bufferRatio; } intf->fmt_out.video.i_frame_rate = pic->format.i_frame_rate; intf->fmt_out.video.i_frame_rate_base = pic->format.i_frame_rate_base; if (intf->fmt_out.video.i_chroma != pic->format.i_chroma) msg_Err(intf, "filter changed the chroma, expect corruption"); // this can't be changed after open, crashes the GLX vout //intf->fmt_out.i_codec = pic->format.i_chroma; //intf->fmt_out.video.i_chroma = pic->format.i_chroma; msg_Info(intf, "first output: buffers=%d:%d", sys->bufferedOut, sys->minBuffered); } sys->numFrames++; sys->bufferedOut--; // it seems filter_NewPicture is required now. however, // sometimes it returns null in which case it seems like // the best thing to do is dump frames picture_t* copy = first == NULL ? srcPic : filter_NewPicture(intf); if (!copy) { picture_Release(pic); // throw away frames // vlc already prints warning for this //msg_Err(intf, "filter_NewPicture returns null"); if (sys->bufferedOut < sys->minBuffered) break; else continue; } else { picture_CopyPixels(copy, pic); picture_Release(pic); pic = copy; } // the time per output frame interval is a fraction of the input frame time int frameTime = (currDate - sys->lastDate) / sys->bufferRatio; // the pts is whatever the current pts is minus any buffering // introduced by the filter pic->date = currDate - sys->bufferedOut*frameTime; // msg_Info(intf, "frame=%d buffered=%d:%d frameTime=%d ratio:%d:1 fin=%d:%d fout=%d:%d pts=%u", // sys->numFrames, sys->bufferedOut, sys->minBuffered, // frameTime, sys->bufferRatio, // srcPic->format.i_frame_rate, srcPic->format.i_frame_rate_base, // pic->format.i_frame_rate, pic->format.i_frame_rate_base, // (unsigned int)pic->date); if (last) last->p_next = pic; else first = pic; last = pic; // if we read too many frames on this iteration, on the next // one we might not have any frames available which would be // bad as vlc would think our intent was to drop frames // // if we stop reading before the output is completely empty, // there will always be some frames for the next iteration, // assuming the filter is fast enough to keep up if (sys->bufferedOut < sys->minBuffered) break; // if there is still some input buffer left, but the fifo is // empty, wait for next frame to arrive. otherwise we can // build too much input buffering if (sys->bufferedIn > 1) waitForOutput(intf); } if (!first) { // the buffer checks should prevent from getting here, but // just in case prevent leaking the input picture picture_Release(srcPic); sys->minBuffered++; } sys->lastDate = currDate; return first; ECHO_RETURN: sys->lastDate = currDate; //msg_Info(intf, "<<<< filter: ECHO"); return srcPic; NULL_RETURN: sys->lastDate = currDate; picture_Release(srcPic); //msg_Info(intf, "<<<< filter: NULL"); return NULL; }
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; }
/** * 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; }
/* This is the filter function. See Open(). */ picture_t *Deinterlace( filter_t *p_filter, picture_t *p_pic ) { filter_sys_t *p_sys = p_filter->p_sys; picture_t *p_dst[DEINTERLACE_DST_SIZE]; /* Request output picture */ p_dst[0] = filter_NewPicture( p_filter ); if( p_dst[0] == NULL ) { picture_Release( p_pic ); return NULL; } picture_CopyProperties( p_dst[0], p_pic ); /* Any unused p_dst pointers must be NULL, because they are used to check how many output frames we have. */ for( int i = 1; i < DEINTERLACE_DST_SIZE; ++i ) p_dst[i] = NULL; /* Update the input frame history, if the currently active algorithm needs it. */ if( p_sys->b_use_frame_history ) { /* Duplicate the picture * TODO when the vout rework is finished, picture_Hold() might be enough * but becarefull, the pitches must match */ picture_t *p_dup = picture_NewFromFormat( &p_pic->format ); if( p_dup ) picture_Copy( p_dup, p_pic ); /* Slide the history */ if( p_sys->pp_history[0] ) picture_Release( p_sys->pp_history[0] ); for( int i = 1; i < HISTORY_SIZE; i++ ) p_sys->pp_history[i-1] = p_sys->pp_history[i]; p_sys->pp_history[HISTORY_SIZE-1] = p_dup; } /* Slide the metadata history. */ for( int i = 1; i < METADATA_SIZE; i++ ) { p_sys->meta.pi_date[i-1] = p_sys->meta.pi_date[i]; p_sys->meta.pi_nb_fields[i-1] = p_sys->meta.pi_nb_fields[i]; p_sys->meta.pb_top_field_first[i-1] = p_sys->meta.pb_top_field_first[i]; } /* The last element corresponds to the current input frame. */ p_sys->meta.pi_date[METADATA_SIZE-1] = p_pic->date; p_sys->meta.pi_nb_fields[METADATA_SIZE-1] = p_pic->i_nb_fields; p_sys->meta.pb_top_field_first[METADATA_SIZE-1] = p_pic->b_top_field_first; /* Remember the frame offset that we should use for this frame. The value in p_sys will be updated to reflect the correct value for the *next* frame when we call the renderer. */ int i_frame_offset = p_sys->i_frame_offset; int i_meta_idx = (METADATA_SIZE-1) - i_frame_offset; /* These correspond to the current *outgoing* frame. */ bool b_top_field_first; int i_nb_fields; if( i_frame_offset != CUSTOM_PTS ) { /* Pick the correct values from the history. */ b_top_field_first = p_sys->meta.pb_top_field_first[i_meta_idx]; i_nb_fields = p_sys->meta.pi_nb_fields[i_meta_idx]; } else { /* Framerate doublers must not request CUSTOM_PTS, as they need the original field timings, and need Deinterlace() to allocate the correct number of output frames. */ assert( !p_sys->b_double_rate ); /* NOTE: i_nb_fields is only used for framerate doublers, so it is unused in this case. b_top_field_first is only passed to the algorithm. We assume that algorithms that request CUSTOM_PTS will, if necessary, extract the TFF/BFF information themselves. */ b_top_field_first = p_pic->b_top_field_first; /* this is not guaranteed to be meaningful */ i_nb_fields = p_pic->i_nb_fields; /* unused */ } /* For framerate doublers, determine field duration and allocate output frames. */ mtime_t i_field_dur = 0; int i_double_rate_alloc_end = 0; /* One past last for allocated output frames in p_dst[]. Used only for framerate doublers. Will be inited below. Declared here because the PTS logic needs the result. */ if( p_sys->b_double_rate ) { /* Calculate one field duration. */ int i = 0; int iend = METADATA_SIZE-1; /* Find oldest valid logged date. The current input frame doesn't count. */ for( ; i < iend; i++ ) if( p_sys->meta.pi_date[i] > VLC_TS_INVALID ) break; if( i < iend ) { /* Count how many fields the valid history entries (except the new frame) represent. */ int i_fields_total = 0; for( int j = i ; j < iend; j++ ) i_fields_total += p_sys->meta.pi_nb_fields[j]; /* One field took this long. */ i_field_dur = (p_pic->date - p_sys->meta.pi_date[i]) / i_fields_total; } /* Note that we default to field duration 0 if it could not be determined. This behaves the same as the old code - leaving the extra output frame dates the same as p_pic->date if the last cached date was not valid. */ i_double_rate_alloc_end = i_nb_fields; if( i_nb_fields > DEINTERLACE_DST_SIZE ) { /* Note that the effective buffer size depends also on the constant private_picture in vout_wrapper.c, since that determines the maximum number of output pictures filter_NewPicture() will successfully allocate for one input frame. */ msg_Err( p_filter, "Framerate doubler: output buffer too small; "\ "fields = %d, buffer size = %d. Dropping the "\ "remaining fields.", i_nb_fields, DEINTERLACE_DST_SIZE ); i_double_rate_alloc_end = DEINTERLACE_DST_SIZE; } /* Allocate output frames. */ for( int i = 1; i < i_double_rate_alloc_end ; ++i ) { p_dst[i-1]->p_next = p_dst[i] = filter_NewPicture( p_filter ); if( p_dst[i] ) { picture_CopyProperties( p_dst[i], p_pic ); } else { msg_Err( p_filter, "Framerate doubler: could not allocate "\ "output frame %d", i+1 ); i_double_rate_alloc_end = i; /* Inform the PTS logic about the correct end position. */ break; /* If this happens, the rest of the allocations aren't likely to work, either... */ } } /* Now we have allocated *up to* the correct number of frames; normally, exactly the correct number. Upon alloc failure, we may have succeeded in allocating *some* output frames, but fewer than were desired. In such a case, as many will be rendered as were successfully allocated. Note that now p_dst[i] != NULL for 0 <= i < i_double_rate_alloc_end. */ } assert( p_sys->b_double_rate || p_dst[1] == NULL ); assert( i_nb_fields > 2 || p_dst[2] == NULL ); /* Render */ switch( p_sys->i_mode ) { case DEINTERLACE_DISCARD: RenderDiscard( p_dst[0], p_pic, 0 ); break; case DEINTERLACE_BOB: RenderBob( p_dst[0], p_pic, !b_top_field_first ); if( p_dst[1] ) RenderBob( p_dst[1], p_pic, b_top_field_first ); if( p_dst[2] ) RenderBob( p_dst[2], p_pic, !b_top_field_first ); break;; case DEINTERLACE_LINEAR: RenderLinear( p_filter, p_dst[0], p_pic, !b_top_field_first ); if( p_dst[1] ) RenderLinear( p_filter, p_dst[1], p_pic, b_top_field_first ); if( p_dst[2] ) RenderLinear( p_filter, p_dst[2], p_pic, !b_top_field_first ); break; case DEINTERLACE_MEAN: RenderMean( p_filter, p_dst[0], p_pic ); break; case DEINTERLACE_BLEND: RenderBlend( p_filter, p_dst[0], p_pic ); break; case DEINTERLACE_X: RenderX( p_dst[0], p_pic ); break; case DEINTERLACE_YADIF: if( RenderYadif( p_filter, p_dst[0], p_pic, 0, 0 ) ) goto drop; break; case DEINTERLACE_YADIF2X: if( RenderYadif( p_filter, p_dst[0], p_pic, 0, !b_top_field_first ) ) goto drop; if( p_dst[1] ) RenderYadif( p_filter, p_dst[1], p_pic, 1, b_top_field_first ); if( p_dst[2] ) RenderYadif( p_filter, p_dst[2], p_pic, 2, !b_top_field_first ); break; case DEINTERLACE_PHOSPHOR: if( RenderPhosphor( p_filter, p_dst[0], 0, !b_top_field_first ) ) goto drop; if( p_dst[1] ) RenderPhosphor( p_filter, p_dst[1], 1, b_top_field_first ); if( p_dst[2] ) RenderPhosphor( p_filter, p_dst[2], 2, !b_top_field_first ); break; case DEINTERLACE_IVTC: /* Note: RenderIVTC will automatically drop the duplicate frames produced by IVTC. This is part of normal operation. */ if( RenderIVTC( p_filter, p_dst[0] ) ) goto drop; break; } /* Set output timestamps, if the algorithm didn't request CUSTOM_PTS for this frame. */ assert( i_frame_offset <= METADATA_SIZE || i_frame_offset == CUSTOM_PTS ); if( i_frame_offset != CUSTOM_PTS ) { mtime_t i_base_pts = p_sys->meta.pi_date[i_meta_idx]; /* Note: in the usual case (i_frame_offset = 0 and b_double_rate = false), this effectively does nothing. This is needed to correct the timestamp when i_frame_offset > 0. */ p_dst[0]->date = i_base_pts; if( p_sys->b_double_rate ) { /* Processing all actually allocated output frames. */ for( int i = 1; i < i_double_rate_alloc_end; ++i ) { /* XXX it's not really good especially for the first picture, but * I don't think that delaying by one frame is worth it */ if( i_base_pts > VLC_TS_INVALID ) p_dst[i]->date = i_base_pts + i * i_field_dur; else p_dst[i]->date = VLC_TS_INVALID; } } } for( int i = 0; i < DEINTERLACE_DST_SIZE; ++i ) { if( p_dst[i] ) { p_dst[i]->b_progressive = true; p_dst[i]->i_nb_fields = 2; } } picture_Release( p_pic ); return p_dst[0]; drop: picture_Release( p_dst[0] ); for( int i = 1; i < DEINTERLACE_DST_SIZE; ++i ) { if( p_dst[i] ) picture_Release( p_dst[i] ); } picture_Release( p_pic ); return NULL; }
/**************************************************************************** * 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; }
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 ); } }
/***************************************************************************** * Filter: displays previously rendered output ***************************************************************************** * This function send the currently rendered image to the internal opencv * filter for processing. *****************************************************************************/ static picture_t* Filter( filter_t* p_filter, picture_t* p_pic ) { picture_t* p_outpic = filter_NewPicture( p_filter ); if( p_outpic == NULL ) { msg_Err( p_filter, "couldn't get a p_outpic!" ); picture_Release( p_pic ); return NULL; } video_format_t fmt_out; // Make a copy if we want to show the original input if (p_filter->p_sys->i_wrapper_output == VINPUT) picture_Copy( p_outpic, p_pic ); VlcPictureToIplImage( p_filter, p_pic ); // Pass the image (as a pointer to the first IplImage*) to the // internal OpenCV filter for processing. p_filter->p_sys->p_opencv->pf_video_filter( p_filter->p_sys->p_opencv, (picture_t*)&(p_filter->p_sys->p_cv_image[0]) ); if(p_filter->p_sys->i_wrapper_output == PROCESSED) { // Processed video if( (p_filter->p_sys->p_proc_image) && (p_filter->p_sys->p_proc_image->i_planes > 0) && (p_filter->p_sys->i_internal_chroma != CINPUT) ) { //p_filter->p_sys->p_proc_image->format.i_chroma = VLC_CODEC_RGB24; memset( &fmt_out, 0, sizeof(video_format_t) ); fmt_out = p_pic->format; //picture_Release( p_outpic ); /* * We have to copy out the image from image_Convert(), otherwise * you leak pictures for some reason: * main video output error: pictures leaked, trying to workaround */ picture_t* p_outpic_tmp = image_Convert( p_filter->p_sys->p_image, p_filter->p_sys->p_proc_image, &(p_filter->p_sys->p_proc_image->format), &fmt_out ); picture_CopyPixels( p_outpic, p_outpic_tmp ); CopyInfoAndRelease( p_outpic, p_outpic_tmp ); } else if( p_filter->p_sys->i_internal_chroma == CINPUT ) { picture_CopyPixels( p_outpic, p_filter->p_sys->p_proc_image ); picture_CopyProperties( p_outpic, p_filter->p_sys->p_proc_image ); } } ReleaseImages( p_filter ); picture_Release( p_pic ); #ifndef NDEBUG msg_Dbg( p_filter, "Filter() done" ); #endif if( p_filter->p_sys->i_wrapper_output != NONE ) { return p_outpic; } else { // NONE picture_Release( p_outpic ); return NULL; } }
/***************************************************************************** * VlcPictureToIplImage: Convert picture_t to IplImage ***************************************************************************** * Converts given picture_t into IplImage(s) according to module config. * IplImage(s) are stored in vout_sys_t. *****************************************************************************/ static void VlcPictureToIplImage( filter_t* p_filter, picture_t* p_in ) { int planes = p_in->i_planes; //num input video planes // input video size CvSize sz = cvSize(abs(p_in->format.i_width), abs(p_in->format.i_height)); video_format_t fmt_out; filter_sys_t* p_sys = p_filter->p_sys; memset( &fmt_out, 0, sizeof(video_format_t) ); //do scale / color conversion according to p_sys config if ((p_sys->f_scale != 1) || (p_sys->i_internal_chroma != CINPUT)) { fmt_out = p_in->format; //calc the scaled video size fmt_out.i_width = p_in->format.i_width * p_sys->f_scale; fmt_out.i_height = p_in->format.i_height * p_sys->f_scale; if (p_sys->i_internal_chroma == RGB) { //rgb2 gives 3 separate planes, this gives 1 interleaved plane //rv24 gives is about 20% faster but gives r&b the wrong way round //and I can't think of an easy way to fix this fmt_out.i_chroma = VLC_CODEC_RGB24; } else if (p_sys->i_internal_chroma == GREY) { //take the I (gray) plane (video seems to commonly be in this fmt so usually the //conversion does nothing) fmt_out.i_chroma = VLC_CODEC_I420; } //convert from the input image p_sys->p_proc_image = image_Convert( p_sys->p_image, p_in, &(p_in->format), &fmt_out ); if (!p_sys->p_proc_image) { msg_Err(p_filter, "can't convert (unsupported formats?), aborting..."); return; } p_sys->p_to_be_freed = p_sys->p_proc_image; //remember this so we can free it later } else //((p_sys->f_scale != 1) || (p_sys->i_internal_chroma != CINPUT)) { // In theory, you could use the input image without conversion, // but it seems to cause weird picture effects (like repeated // image filtering) and picture leaking. p_sys->p_proc_image = filter_NewPicture( p_filter ); //p_in picture_Copy( p_sys->p_proc_image, p_in ); p_sys->p_to_be_freed = p_sys->p_proc_image; } //Convert to the IplImage array that is to be processed. //If there are multiple planes in p_sys->p_proc_image, then 1 IplImage //is created for each plane. planes = p_sys->p_proc_image->i_planes; p_sys->i_cv_image_size = planes; for( int i = 0; i < planes; i++ ) { sz = cvSize(abs(p_sys->p_proc_image->p[i].i_visible_pitch / p_sys->p_proc_image->p[i].i_pixel_pitch), abs(p_sys->p_proc_image->p[i].i_visible_lines)); p_sys->p_cv_image[i] = cvCreateImageHeader(sz, IPL_DEPTH_8U, p_sys->p_proc_image->p[i].i_pixel_pitch); cvSetData( p_sys->p_cv_image[i], (char*)(p_sys->p_proc_image->p[i].p_pixels), p_sys->p_proc_image->p[i].i_pitch ); } //Hack the above opencv image array into a picture_t so that it can be sent to //another video filter p_sys->hacked_pic.i_planes = planes; p_sys->hacked_pic.format.i_chroma = fmt_out.i_chroma; #ifndef NDEBUG msg_Dbg( p_filter, "VlcPictureToIplImageRgb() completed" ); #endif }
/**************************************************************************** * 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 int Send( sout_stream_t *p_stream, sout_stream_id_t *id, block_t *p_buffer ) { sout_stream_sys_t *p_sys = p_stream->p_sys; picture_t *p_pic; if ( (sout_stream_sys_t *)id != p_sys ) { block_ChainRelease( p_buffer ); return VLC_SUCCESS; } while ( (p_pic = p_sys->p_decoder->pf_decode_video( p_sys->p_decoder, &p_buffer )) ) { picture_t *p_new_pic; if( p_sys->i_height || p_sys->i_width ) { video_format_t fmt_out, fmt_in; memset( &fmt_in, 0, sizeof(video_format_t) ); memset( &fmt_out, 0, sizeof(video_format_t) ); fmt_in = p_sys->p_decoder->fmt_out.video; if( p_sys->i_chroma ) fmt_out.i_chroma = p_sys->i_chroma; else fmt_out.i_chroma = VLC_CODEC_I420; const unsigned i_fmt_in_aspect = (int64_t)VOUT_ASPECT_FACTOR * fmt_in.i_sar_num * fmt_in.i_width / (fmt_in.i_sar_den * fmt_in.i_height); if ( !p_sys->i_height ) { fmt_out.i_width = p_sys->i_width; fmt_out.i_height = (p_sys->i_width * VOUT_ASPECT_FACTOR * p_sys->i_sar_num / p_sys->i_sar_den / i_fmt_in_aspect) & ~0x1; } else if ( !p_sys->i_width ) { fmt_out.i_height = p_sys->i_height; fmt_out.i_width = (p_sys->i_height * i_fmt_in_aspect * p_sys->i_sar_den / p_sys->i_sar_num / VOUT_ASPECT_FACTOR) & ~0x1; } else { fmt_out.i_width = p_sys->i_width; fmt_out.i_height = p_sys->i_height; } fmt_out.i_visible_width = fmt_out.i_width; fmt_out.i_visible_height = fmt_out.i_height; p_new_pic = image_Convert( p_sys->p_image, p_pic, &fmt_in, &fmt_out ); if( p_new_pic == NULL ) { msg_Err( p_stream, "image conversion failed" ); picture_Release( p_pic ); continue; } } else { /* TODO: chroma conversion if needed */ p_new_pic = picture_New( p_pic->format.i_chroma, p_pic->format.i_width, p_pic->format.i_height, p_sys->p_decoder->fmt_out.video.i_sar_num, p_sys->p_decoder->fmt_out.video.i_sar_den ); if( !p_new_pic ) { picture_Release( p_pic ); msg_Err( p_stream, "image allocation failed" ); continue; } picture_Copy( p_new_pic, p_pic ); } picture_Release( p_pic ); if( p_sys->p_vf2 ) p_new_pic = filter_chain_VideoFilter( p_sys->p_vf2, p_new_pic ); PushPicture( p_stream, p_new_pic ); } return VLC_SUCCESS; }