X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fvideo_output%2Fvideo_output.c;h=bb1eeb0fabe0f3a1bda597577fc20cb365b3e37d;hb=4d85871d1b29265a6cc5744677f0f0bb9bfac20f;hp=d827b41db8816593d6fbed133a6c463cdfc116fd;hpb=4b9c63a36eded246ad9ef1ea0de0cf7b38d29ad9;p=vlc diff --git a/src/video_output/video_output.c b/src/video_output/video_output.c index d827b41db8..bb1eeb0fab 100644 --- a/src/video_output/video_output.c +++ b/src/video_output/video_output.c @@ -112,6 +112,7 @@ static vout_thread_t *VoutCreate(vlc_object_t *object, vout->p = (vout_thread_sys_t*)&vout[1]; vout->p->original = original; + vout->p->dpb_size = cfg->dpb_size; vout_control_Init(&vout->p->control); vout_control_PushVoid(&vout->p->control, VOUT_CONTROL_INIT); @@ -125,6 +126,7 @@ static vout_thread_t *VoutCreate(vlc_object_t *object, /* Initialize locks */ vlc_mutex_init(&vout->p->picture_lock); vlc_mutex_init(&vout->p->vfilter_lock); + vlc_mutex_init(&vout->p->spu_lock); /* Attach the new object now so we can use var inheritance below */ vlc_object_attach(vout, object); @@ -154,6 +156,7 @@ static vout_thread_t *VoutCreate(vlc_object_t *object, /* */ if (vlc_clone(&vout->p->thread, Thread, vout, VLC_THREAD_PRIORITY_OUTPUT)) { + spu_Destroy(vout->p->p_spu); vlc_object_release(vout); return NULL; } @@ -177,7 +180,7 @@ vout_thread_t *(vout_Request)(vlc_object_t *object, const vout_configuration_t *cfg) { vout_thread_t *vout = cfg->vout; - if (!cfg->fmt) { + if (cfg->change_fmt && !cfg->fmt) { if (vout) vout_CloseAndRelease(vout); return NULL; @@ -185,9 +188,6 @@ vout_thread_t *(vout_Request)(vlc_object_t *object, /* If a vout is provided, try reusing it */ if (vout) { - vlc_object_detach(vout); - vlc_object_attach(vout, object); - if (vout->p->input != cfg->input) { if (vout->p->input) spu_Attach(vout->p->p_spu, vout->p->input, false); @@ -196,12 +196,15 @@ vout_thread_t *(vout_Request)(vlc_object_t *object, spu_Attach(vout->p->p_spu, vout->p->input, true); } - vout_control_cmd_t cmd; - vout_control_cmd_Init(&cmd, VOUT_CONTROL_REINIT); - cmd.u.cfg = cfg; + if (cfg->change_fmt) { + vout_control_cmd_t cmd; + vout_control_cmd_Init(&cmd, VOUT_CONTROL_REINIT); + cmd.u.cfg = cfg; + + vout_control_Push(&vout->p->control, &cmd); + vout_control_WaitEmpty(&vout->p->control); + } - vout_control_Push(&vout->p->control, &cmd); - vout_control_WaitEmpty(&vout->p->control); if (!vout->p->dead) { msg_Dbg(object, "reusing provided vout"); return vout; @@ -213,26 +216,22 @@ vout_thread_t *(vout_Request)(vlc_object_t *object, return VoutCreate(object, cfg); } -/***************************************************************************** - * vout_Close: Close a vout created by VoutCreate. - ***************************************************************************** - * You HAVE to call it on vout created by VoutCreate before vlc_object_release. - * You should NEVER call it on vout not obtained through VoutCreate - * (like with vout_Request or vlc_object_find.) - * You can use vout_CloseAndRelease() as a convenience method. - *****************************************************************************/ void vout_Close(vout_thread_t *vout) { assert(vout); if (vout->p->input) spu_Attach(vout->p->p_spu, vout->p->input, false); - vlc_object_detach(vout->p->p_spu); vout_snapshot_End(&vout->p->snapshot); vout_control_PushVoid(&vout->p->control, VOUT_CONTROL_CLEAN); vlc_join(vout->p->thread, NULL); + + vlc_mutex_lock(&vout->p->spu_lock); + spu_Destroy(vout->p->p_spu); + vout->p->p_spu = NULL; + vlc_mutex_unlock(&vout->p->spu_lock); } /* */ @@ -245,10 +244,8 @@ static void VoutDestructor(vlc_object_t *object) free(vout->p->splitter_name); - /* */ - spu_Destroy(vout->p->p_spu); - /* Destroy the locks */ + vlc_mutex_destroy(&vout->p->spu_lock); vlc_mutex_destroy(&vout->p->picture_lock); vlc_mutex_destroy(&vout->p->vfilter_lock); vout_control_Clean(&vout->p->control); @@ -348,15 +345,27 @@ void vout_DisplayTitle(vout_thread_t *vout, const char *title) void vout_PutSubpicture( vout_thread_t *vout, subpicture_t *subpic ) { - spu_DisplaySubpicture(vout->p->p_spu, subpic); + vout_control_cmd_t cmd; + vout_control_cmd_Init(&cmd, VOUT_CONTROL_SUBPICTURE); + cmd.u.subpicture = subpic; + + vout_control_Push(&vout->p->control, &cmd); } int vout_RegisterSubpictureChannel( vout_thread_t *vout ) { - return spu_RegisterChannel(vout->p->p_spu); + int channel = SPU_DEFAULT_CHANNEL; + + vlc_mutex_lock(&vout->p->spu_lock); + if (vout->p->p_spu) + channel = spu_RegisterChannel(vout->p->p_spu); + vlc_mutex_unlock(&vout->p->spu_lock); + + return channel; } void vout_FlushSubpictureChannel( vout_thread_t *vout, int channel ) { - spu_ClearChannel(vout->p->p_spu, channel); + vout_control_PushInteger(&vout->p->control, VOUT_CONTROL_FLUSH_SUBPICTURE, + channel); } /* vout_Control* are usable by anyone at anytime */ @@ -426,6 +435,11 @@ void vout_ControlChangeSubFilters(vout_thread_t *vout, const char *filters) vout_control_PushString(&vout->p->control, VOUT_CONTROL_CHANGE_SUB_FILTERS, filters); } +void vout_ControlChangeSubMargin(vout_thread_t *vout, int margin) +{ + vout_control_PushInteger(&vout->p->control, VOUT_CONTROL_CHANGE_SUB_MARGIN, + margin); +} /* */ static void VoutGetDisplayCfg(vout_thread_t *vout, vout_display_cfg_t *cfg, const char *title) @@ -453,9 +467,9 @@ static void VoutGetDisplayCfg(vout_thread_t *vout, vout_display_cfg_t *cfg, cons else if (align_mask & 0x2) cfg->align.horizontal = VOUT_DISPLAY_ALIGN_RIGHT; if (align_mask & 0x4) - cfg->align.horizontal = VOUT_DISPLAY_ALIGN_TOP; + cfg->align.vertical = VOUT_DISPLAY_ALIGN_TOP; else if (align_mask & 0x8) - cfg->align.horizontal = VOUT_DISPLAY_ALIGN_BOTTOM; + cfg->align.vertical = VOUT_DISPLAY_ALIGN_BOTTOM; } vout_window_t * vout_NewDisplayWindow(vout_thread_t *vout, vout_display_t *vd, @@ -488,7 +502,7 @@ vout_window_t * vout_NewDisplayWindow(vout_thread_t *vout, vout_display_t *vd, vout->p->window.object = NULL; } - vout_window_t *window = vout_window_New(VLC_OBJECT(vout), NULL, + vout_window_t *window = vout_window_New(VLC_OBJECT(vout), "$window", &cfg_override); if (!window) return NULL; @@ -535,88 +549,109 @@ static int VoutVideoFilterAllocationSetup(filter_t *filter, void *data) } /* */ -static int ThreadDisplayPicture(vout_thread_t *vout, - bool now, mtime_t *deadline) +static picture_t *ThreadDisplayGetDecodedPicture(vout_thread_t *vout, + int *lost_count, bool *is_forced, + bool now, mtime_t *deadline) { vout_display_t *vd = vout->p->display.vd; - int displayed_count = 0; - int lost_count = 0; - - for (;;) { - const mtime_t date = mdate(); - const bool is_paused = vout->p->pause.is_on; - bool redisplay = is_paused && !now && vout->p->displayed.decoded; - bool is_forced; - /* FIXME/XXX we must redisplay the last decoded picture (because - * of potential vout updated, or filters update or SPU update) - * For now a high update period is needed but it coulmd be removed - * if and only if: - * - vout module emits events from theselves. - * - *and* SPU is modified to emit an event or a deadline when needed. - * - * So it will be done latter. - */ - if (!redisplay) { - picture_t *peek = picture_fifo_Peek(vout->p->decoder_fifo); - if (peek) { - is_forced = peek->b_force || is_paused || now; - *deadline = (is_forced ? date : peek->date) - vout_chrono_GetHigh(&vout->p->render); - picture_Release(peek); - } else { - redisplay = true; - } + const mtime_t date = mdate(); + const bool is_paused = vout->p->pause.is_on; + bool redisplay = is_paused && !now && vout->p->displayed.decoded; + + mtime_t vfilter_delay = 0; + for (int i = 0; i < VOUT_FILTER_DELAYS; i++) + vfilter_delay = __MAX(vfilter_delay, vout->p->vfilter_delay[i]); + + /* FIXME/XXX we must redisplay the last decoded picture (because + * of potential vout updated, or filters update or SPU update) + * For now a high update period is needed but it coulmd be removed + * if and only if: + * - vout module emits events from theselves. + * - *and* SPU is modified to emit an event or a deadline when needed. + * + * So it will be done latter. + */ + if (!redisplay) { + picture_t *peek = picture_fifo_Peek(vout->p->decoder_fifo); + if (peek) { + *is_forced = peek->b_force || is_paused || now; + *deadline = (*is_forced ? date : peek->date) - + vout_chrono_GetHigh(&vout->p->render) - + vfilter_delay; + picture_Release(peek); + } else { + redisplay = true; } - if (redisplay) { - /* FIXME a better way for this delay is needed */ - const mtime_t date_update = vout->p->displayed.date + VOUT_REDISPLAY_DELAY; - if (date_update > date || !vout->p->displayed.decoded) { - *deadline = vout->p->displayed.decoded ? date_update : VLC_TS_INVALID; - break; - } - /* */ - is_forced = true; - *deadline = date - vout_chrono_GetHigh(&vout->p->render); + } + if (redisplay) { + /* FIXME a better way for this delay is needed */ + const mtime_t date_update = vout->p->displayed.date + VOUT_REDISPLAY_DELAY; + if (date_update > date || !vout->p->displayed.decoded) { + *deadline = vout->p->displayed.decoded ? date_update : VLC_TS_INVALID; + return NULL; } - if (*deadline > VOUT_MWAIT_TOLERANCE) - *deadline -= VOUT_MWAIT_TOLERANCE; + /* */ + *is_forced = true; + *deadline = date - vout_chrono_GetHigh(&vout->p->render) - vfilter_delay; + } + if (*deadline > VOUT_MWAIT_TOLERANCE) + *deadline -= VOUT_MWAIT_TOLERANCE; - /* If we are too early and can wait, do it */ - if (date < *deadline && !now) - break; + /* If we are too early and can wait, do it */ + if (date < *deadline && !now) + return NULL; - picture_t *decoded; - if (redisplay) { - decoded = vout->p->displayed.decoded; - vout->p->displayed.decoded = NULL; - } else { - decoded = picture_fifo_Pop(vout->p->decoder_fifo); - assert(decoded); - if (!is_forced && !vout->p->is_late_dropped) { - const mtime_t predicted = date + vout_chrono_GetLow(&vout->p->render); - const mtime_t late = predicted - decoded->date; - if (late > 0) { - msg_Dbg(vout, "picture might be displayed late (missing %d ms)", (int)(late/1000)); - if (late > VOUT_DISPLAY_LATE_THRESHOLD) { - msg_Warn(vout, "rejected picture because of render time"); - /* TODO */ - picture_Release(decoded); - lost_count++; - break; - } + picture_t *decoded; + if (redisplay) { + decoded = vout->p->displayed.decoded; + vout->p->displayed.decoded = NULL; + } else { + decoded = picture_fifo_Pop(vout->p->decoder_fifo); + assert(decoded); + if (!*is_forced && !vout->p->is_late_dropped) { + const mtime_t predicted = date + vout_chrono_GetLow(&vout->p->render); + const mtime_t late = predicted - decoded->date; + if (late > 0) { + msg_Dbg(vout, "picture might be displayed late (missing %d ms)", (int)(late/1000)); + if (late > VOUT_DISPLAY_LATE_THRESHOLD) { + msg_Warn(vout, "rejected picture because of render time"); + /* TODO */ + picture_Release(decoded); + (*lost_count)++; + return NULL; } } - - vout->p->displayed.is_interlaced = !decoded->b_progressive; - vout->p->displayed.qtype = decoded->i_qtype; } - vout->p->displayed.timestamp = decoded->date; - /* */ - if (vout->p->displayed.decoded) - picture_Release(vout->p->displayed.decoded); - picture_Hold(decoded); - vout->p->displayed.decoded = decoded; + vout->p->displayed.is_interlaced = !decoded->b_progressive; + vout->p->displayed.qtype = decoded->i_qtype; + } + vout->p->displayed.timestamp = decoded->date; + + /* */ + if (vout->p->displayed.decoded) + picture_Release(vout->p->displayed.decoded); + picture_Hold(decoded); + vout->p->displayed.decoded = decoded; + + return decoded; +} + +static int ThreadDisplayPicture(vout_thread_t *vout, + bool now, mtime_t *deadline) +{ + vout_display_t *vd = vout->p->display.vd; + int displayed_count = 0; + int lost_count = 0; + + for (;;) { + bool is_forced; + picture_t *decoded = ThreadDisplayGetDecodedPicture(vout, + &lost_count, &is_forced, + now, deadline); + if (!decoded) + break; /* */ vout_chrono_Start(&vout->p->render); @@ -629,6 +664,8 @@ static int ThreadDisplayPicture(vout_thread_t *vout, vlc_mutex_unlock(&vout->p->vfilter_lock); if (!filtered) continue; + vout->p->vfilter_delay[vout->p->vfilter_delay_index] = decoded->date - filtered->date; + vout->p->vfilter_delay_index = (vout->p->vfilter_delay_index + 1) % VOUT_FILTER_DELAYS; } /* @@ -707,7 +744,7 @@ static int ThreadDisplayPicture(vout_thread_t *vout, /* Wait the real date (for rendering jitter) */ if (!is_forced) - mwait(decoded->date); + mwait(direct->date); /* Display the direct buffer returned by vout_RenderPicture */ vout->p->displayed.date = mdate(); @@ -748,6 +785,17 @@ static void ThreadManage(vout_thread_t *vout, vout_ManageWrapper(vout); } +static void ThreadDisplaySubpicture(vout_thread_t *vout, + subpicture_t *subpicture) +{ + spu_DisplaySubpicture(vout->p->p_spu, subpicture); +} + +static void ThreadFlushSubpicture(vout_thread_t *vout, int channel) +{ + spu_ClearChannel(vout->p->p_spu, channel); +} + static void ThreadDisplayOsdTitle(vout_thread_t *vout, const char *string) { if (!vout->p->title.show) @@ -772,12 +820,27 @@ static void ThreadChangeFilters(vout_thread_t *vout, const char *filters) msg_Err(vout, "Video filter chain creation failed"); vlc_mutex_unlock(&vout->p->vfilter_lock); + + vout->p->vfilter_delay_index = 0; + for (int i = 0; i < VOUT_FILTER_DELAYS; i++) + vout->p->vfilter_delay[i] = 0; } static void ThreadChangeSubFilters(vout_thread_t *vout, const char *filters) { spu_ChangeFilters(vout->p->p_spu, filters); } +static void ThreadChangeSubMargin(vout_thread_t *vout, int margin) +{ + spu_ChangeMargin(vout->p->p_spu, margin); +} + +static void ThreadFilterFlush(vout_thread_t *vout) +{ + vlc_mutex_lock(&vout->p->vfilter_lock); + filter_chain_VideoFlush(vout->p->vfilter_chain); + vlc_mutex_unlock(&vout->p->vfilter_lock); +} static void ThreadChangePause(vout_thread_t *vout, bool is_paused, mtime_t date) { @@ -795,6 +858,8 @@ static void ThreadChangePause(vout_thread_t *vout, bool is_paused, mtime_t date) vout->p->displayed.decoded->date += duration; spu_OffsetSubtitleDate(vout->p->p_spu, duration); + + ThreadFilterFlush(vout); } else { vout->p->step.timestamp = VLC_TS_INVALID; vout->p->step.last = VLC_TS_INVALID; @@ -819,6 +884,8 @@ static void ThreadFlush(vout_thread_t *vout, bool below, mtime_t date) vout->p->displayed.timestamp = VLC_TS_INVALID; } } + ThreadFilterFlush(vout); + picture_fifo_Flush(vout->p->decoder_fifo, date, below); } @@ -944,6 +1011,9 @@ static int ThreadStart(vout_thread_t *vout, const vout_display_state_t *state) vout->p->vfilter_chain = filter_chain_New( vout, "video filter2", false, VoutVideoFilterAllocationSetup, NULL, vout); + vout->p->vfilter_delay_index = 0; + for (int i = 0; i < VOUT_FILTER_DELAYS; i++) + vout->p->vfilter_delay[i] = 0; vout_display_state_t state_default; if (!state) { @@ -1027,8 +1097,11 @@ static int ThreadReinit(vout_thread_t *vout, ThreadClean(vout); return VLC_EGENERIC; } - if (video_format_IsSimilar(&original, &vout->p->original)) - return VLC_SUCCESS; + if (video_format_IsSimilar(&original, &vout->p->original)) { + if (cfg->dpb_size <= vout->p->dpb_size) + return VLC_SUCCESS; + msg_Warn(vout, "DPB need to be increased"); + } vout_display_state_t state; memset(&state, 0, sizeof(state)); @@ -1045,6 +1118,7 @@ static int ThreadReinit(vout_thread_t *vout, * and I am not sure what to do */ vout->p->original = original; + vout->p->dpb_size = cfg->dpb_size; if (ThreadStart(vout, &state)) { ThreadClean(vout); return VLC_EGENERIC; @@ -1095,6 +1169,13 @@ static void *Thread(void *object) if (ThreadReinit(vout, cmd.u.cfg)) return NULL; break; + case VOUT_CONTROL_SUBPICTURE: + ThreadDisplaySubpicture(vout, cmd.u.subpicture); + cmd.u.subpicture = NULL; + break; + case VOUT_CONTROL_FLUSH_SUBPICTURE: + ThreadFlushSubpicture(vout, cmd.u.integer); + break; case VOUT_CONTROL_OSD_TITLE: ThreadDisplayOsdTitle(vout, cmd.u.string); break; @@ -1104,6 +1185,9 @@ static void *Thread(void *object) case VOUT_CONTROL_CHANGE_SUB_FILTERS: ThreadChangeSubFilters(vout, cmd.u.string); break; + case VOUT_CONTROL_CHANGE_SUB_MARGIN: + ThreadChangeSubMargin(vout, cmd.u.integer); + break; case VOUT_CONTROL_PAUSE: ThreadChangePause(vout, cmd.u.pause.is_on, cmd.u.pause.date); break;