static void SubpicturePrepare(vout_display_t *vd, subpicture_t *subpicture) { vout_display_sys_t *sys = vd->sys; ARect memset_bounds; SubtitleRegionToBounds(subpicture, &memset_bounds); if( subpicture ) { if( subpicture->i_order == sys->i_sub_last_order && memcmp( &memset_bounds, &sys->sub_last_region, sizeof(ARect) ) == 0 ) return; sys->i_sub_last_order = subpicture->i_order; sys->sub_last_region = memset_bounds; } if (AndroidWindow_LockPicture(sys, sys->p_sub_window, sys->p_sub_pic) != 0) return; /* Clear the subtitles surface. */ SubtitleGetDirtyBounds(vd, subpicture, &memset_bounds); const int x_pixels_offset = memset_bounds.left * sys->p_sub_pic->p[0].i_pixel_pitch; const int i_line_size = (memset_bounds.right - memset_bounds.left) * sys->p_sub_pic->p->i_pixel_pitch; for (int y = memset_bounds.top; y < memset_bounds.bottom; y++) memset(&sys->p_sub_pic->p[0].p_pixels[y * sys->p_sub_pic->p[0].i_pitch + x_pixels_offset], 0, i_line_size); if (subpicture) picture_BlendSubpicture(sys->p_sub_pic, sys->p_spu_blend, subpicture); }
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; }
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 ); }
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 ); } }
static void DisplaySubpicture(vout_display_t *vd, subpicture_t *subpicture) { vout_display_sys_t *sys = vd->sys; struct md5_s hash; InitMD5(&hash); if (subpicture) { for (subpicture_region_t *r = subpicture->p_region; r != NULL; r = r->p_next) { AddMD5(&hash, &r->i_x, sizeof(r->i_x)); AddMD5(&hash, &r->i_y, sizeof(r->i_y)); AddMD5(&hash, &r->fmt.i_visible_width, sizeof(r->fmt.i_visible_width)); AddMD5(&hash, &r->fmt.i_visible_height, sizeof(r->fmt.i_visible_height)); AddMD5(&hash, &r->fmt.i_x_offset, sizeof(r->fmt.i_x_offset)); AddMD5(&hash, &r->fmt.i_y_offset, sizeof(r->fmt.i_y_offset)); const int pixels_offset = r->fmt.i_y_offset * r->p_picture->p->i_pitch + r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch; for (int y = 0; y < r->fmt.i_visible_height; y++) AddMD5(&hash, &r->p_picture->p->p_pixels[pixels_offset + y*r->p_picture->p->i_pitch], r->fmt.i_visible_width); } } EndMD5(&hash); if (!memcmp(hash.buf, sys->hash, 16)) return; memcpy(sys->hash, hash.buf, 16); jobject jsurf = jni_LockAndGetSubtitlesSurface(); if (sys->window && jsurf != sys->jsurf) { sys->native_window.winRelease(sys->window); sys->window = NULL; } sys->jsurf = jsurf; if (!sys->window) { JNIEnv *p_env; jni_attach_thread(&p_env, THREAD_NAME); sys->window = sys->native_window.winFromSurface(p_env, jsurf); jni_detach_thread(); } ANativeWindow_Buffer buf = { 0 }; int32_t err = sys->native_window.winLock(sys->window, &buf, NULL); if (err) { jni_UnlockAndroidSurface(); return; } if (buf.width >= sys->fmt.i_width && buf.height >= sys->fmt.i_height) { /* Wrap the NativeWindow corresponding to the subtitles surface in a picture_t */ picture_t *picture = sys->subtitles_picture; picture->p[0].p_pixels = (uint8_t*)buf.bits; picture->p[0].i_lines = buf.height; picture->p[0].i_pitch = picture->p[0].i_pixel_pitch * buf.stride; /* Clear the subtitles surface. */ memset(picture->p[0].p_pixels, 0, picture->p[0].i_pitch * picture->p[0].i_lines); if (subpicture) { /* Allocate a blending filter if needed. */ if (unlikely(!sys->p_spu_blend)) sys->p_spu_blend = filter_NewBlend(VLC_OBJECT(vd), &picture->format); picture_BlendSubpicture(picture, sys->p_spu_blend, subpicture); } } sys->native_window.unlockAndPost(sys->window); jni_UnlockAndroidSurface(); }