// Render "fake" highlights, because using actual dvd sub highlight elements // is too hard, and would require extra libavcodec to begin with. // Note: a proper solution would introduce something like // SD_CTRL_APPLY_DVDNAV, which would crop the vobsub frame, // and apply the current CLUT. void mp_nav_get_highlight(void *priv, struct mp_osd_res res, struct sub_bitmaps *out_imgs) { struct MPContext *mpctx = priv; struct mp_nav_state *nav = mpctx ? mpctx->nav_state : NULL; if (!nav) return; struct sub_bitmap *sub = nav->hi_elem; if (!sub) sub = talloc_zero(nav, struct sub_bitmap); nav->hi_elem = sub; if (!is_valid_size(nav->vidsize)) { update_resolution(mpctx); if (!is_valid_size(nav->vidsize)) return; } int sizes[2] = {nav->vidsize[0], nav->vidsize[1]}; if (sizes[0] != nav->subsize[0] || sizes[1] != nav->subsize[1]) { talloc_free(sub->bitmap); sub->bitmap = talloc_array(sub, uint32_t, sizes[0] * sizes[1]); memset(sub->bitmap, 0x80, talloc_get_size(sub->bitmap)); nav->subsize[0] = sizes[0]; nav->subsize[1] = sizes[1]; } out_imgs->num_parts = 0; if (nav->hi_visible) { sub->x = nav->highlight[0]; sub->y = nav->highlight[1]; sub->w = MPCLAMP(nav->highlight[2] - sub->x, 0, sizes[0]); sub->h = MPCLAMP(nav->highlight[3] - sub->y, 0, sizes[1]); sub->stride = sub->w * 4; if (sub->w > 0 && sub->h > 0) nav->outputs[out_imgs->num_parts++] = *sub; } if (nav->overlays[0]) nav->outputs[out_imgs->num_parts++] = *nav->overlays[0]; if (nav->overlays[1]) nav->outputs[out_imgs->num_parts++] = *nav->overlays[1]; if (out_imgs->num_parts) { out_imgs->parts = nav->outputs; out_imgs->format = SUBBITMAP_RGBA; osd_rescale_bitmaps(out_imgs, sizes[0], sizes[1], res, -1); } }
void mp_handle_nav(struct MPContext *mpctx) { struct mp_nav_state *nav = mpctx->nav_state; if (!nav) return; mpctx->sleeptime = MPMIN(mpctx->sleeptime, 0.5); while (1) { if (!mpctx->demuxer) break; struct mp_nav_event *ev = NULL; demux_control(mpctx->demuxer, DEMUXER_CTRL_GET_NAV_EVENT, &ev); if (!ev) break; switch (ev->event) { case MP_NAV_EVENT_DRAIN: { nav->nav_draining = true; MP_VERBOSE(nav, "drain requested\n"); break; } case MP_NAV_EVENT_RESET_ALL: { mpctx->stop_play = PT_RELOAD_DEMUXER; MP_VERBOSE(nav, "reload\n"); // return immediately. // other events should be handled after reloaded. talloc_free(ev); return; } case MP_NAV_EVENT_RESET: { nav->nav_still_frame = 0; break; } case MP_NAV_EVENT_EOF: nav->nav_eof = true; break; case MP_NAV_EVENT_STILL_FRAME: { int len = ev->u.still_frame.seconds; MP_VERBOSE(nav, "wait for %d seconds\n", len); if (len > 0 && nav->nav_still_frame == 0) nav->nav_still_frame = len; break; } case MP_NAV_EVENT_MENU_MODE: nav->nav_menu = ev->u.menu_mode.enable; if (nav->nav_menu) { mp_input_enable_section(mpctx->input, "discnav-menu", MP_INPUT_ON_TOP); } else { mp_input_disable_section(mpctx->input, "discnav-menu"); } update_state(mpctx); break; case MP_NAV_EVENT_HIGHLIGHT: { pthread_mutex_lock(&nav->osd_lock); MP_VERBOSE(nav, "highlight: %d %d %d - %d %d\n", ev->u.highlight.display, ev->u.highlight.sx, ev->u.highlight.sy, ev->u.highlight.ex, ev->u.highlight.ey); nav->highlight[0] = ev->u.highlight.sx; nav->highlight[1] = ev->u.highlight.sy; nav->highlight[2] = ev->u.highlight.ex; nav->highlight[3] = ev->u.highlight.ey; nav->hi_visible = ev->u.highlight.display; pthread_mutex_unlock(&nav->osd_lock); update_resolution(mpctx); osd_set_nav_highlight(mpctx->osd, mpctx); break; } case MP_NAV_EVENT_OVERLAY: { pthread_mutex_lock(&nav->osd_lock); for (int i = 0; i < 2; i++) { if (nav->overlays[i]) talloc_free(nav->overlays[i]); nav->overlays[i] = talloc_steal(nav, ev->u.overlay.images[i]); } pthread_mutex_unlock(&nav->osd_lock); update_resolution(mpctx); osd_set_nav_highlight(mpctx->osd, mpctx); break; } default: ; // ignore } talloc_free(ev); } update_resolution(mpctx); if (mpctx->stop_play == AT_END_OF_FILE) { if (nav->nav_still_frame > 0) { // gross hack mpctx->time_frame += nav->nav_still_frame; nav->nav_still_frame = -2; } else if (nav->nav_still_frame == -2) { struct mp_nav_cmd inp = {MP_NAV_CMD_SKIP_STILL}; run_stream_control(mpctx, STREAM_CTRL_NAV_CMD, &inp); } } if (nav->nav_draining && mpctx->stop_play == AT_END_OF_FILE) { MP_VERBOSE(nav, "execute drain\n"); struct mp_nav_cmd inp = {MP_NAV_CMD_DRAIN_OK}; run_stream_control(mpctx, STREAM_CTRL_NAV_CMD, &inp); nav->nav_draining = false; run_stream_control(mpctx, STREAM_CTRL_RESUME_CACHE, NULL); } // E.g. keep displaying still frames if (mpctx->stop_play == AT_END_OF_FILE && !nav->nav_eof) mpctx->stop_play = KEEP_PLAYING; }