diff options
author | wm4 <wm4@nowhere> | 2014-03-30 07:21:25 +0200 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2014-03-30 07:21:25 +0200 |
commit | 8487bb2c549283d340ee73310b1a58b13f746864 (patch) | |
tree | 7db8e84c28aa9a1b72e207137a126d029037c64d | |
parent | bdc936af059a9136d907885a3b7de189f4cde3d5 (diff) | |
parent | fb47f2f9402567eb7ecda7e5ec83e0c1915632c1 (diff) | |
download | mpv-8487bb2c549283d340ee73310b1a58b13f746864.tar.bz2 mpv-8487bb2c549283d340ee73310b1a58b13f746864.tar.xz |
Merge remote-tracking branch 'mpv/pr/676'
-rw-r--r-- | etc/input.conf | 24 | ||||
-rw-r--r-- | input/cmd_list.c | 3 | ||||
-rw-r--r-- | input/cmd_list.h | 2 | ||||
-rw-r--r-- | old-makefile | 2 | ||||
-rw-r--r-- | player/command.c | 2 | ||||
-rw-r--r-- | player/core.h | 2 | ||||
-rw-r--r-- | player/discnav.c (renamed from player/dvdnav.c) | 62 | ||||
-rw-r--r-- | stream/discnav.h (renamed from stream/stream_dvdnav.h) | 6 | ||||
-rw-r--r-- | stream/stream.c | 2 | ||||
-rw-r--r-- | stream/stream_bluray.c | 639 | ||||
-rw-r--r-- | stream/stream_dvdnav.c | 2 | ||||
-rw-r--r-- | wscript_build.py | 2 |
12 files changed, 566 insertions, 182 deletions
diff --git a/etc/input.conf b/etc/input.conf index 943050eb75..035ec783bf 100644 --- a/etc/input.conf +++ b/etc/input.conf @@ -192,19 +192,19 @@ #JOY_BTN2 add volume 1 #JOY_BTN3 add volume -1 -# For dvdnav:// +# For dvdnav:// and bdnav:// -# dvdnav controls during playback -#ENTER {dvdnav} dvdnav menu # DVDNav MENU -# BS {dvdnav} dvdnav prev # DVDNav PREVIOUS menu (in the order chapter->title->root) -# dvdnav controls when showing menu (additionally to the controls above) -#UP {dvdnav-menu} dvdnav up # DVDNav UP -#DOWN {dvdnav-menu} dvdnav down # DVDNav DOWN -#LEFT {dvdnav-menu} dvdnav left # DVDNav LEFT -#RIGHT {dvdnav-menu} dvdnav right # DVDNav RIGHT -#ENTER {dvdnav-menu} dvdnav select # DVDNav SELECT (ok) -#MOUSE_BTN0 {dvdnav-menu} dvdnav mouse -#MOUSE_MOVE {dvdnav-menu} dvdnav mouse_move +# navigation controls during playback +#ENTER {discnav} discnav menu # DISCNAV MENU +# BS {discnav} discnav prev # DISCNAV PREVIOUS menu (in the order chapter->title->root) +# navigation controls when showing menu (additionally to the controls above) +#UP {discnav-menu} discnav up # DISCNAV UP +#DOWN {discnav-menu} discnav down # DISCNAV DOWN +#LEFT {discnav-menu} discnav left # DISCNAV LEFT +#RIGHT {discnav-menu} discnav right # DISCNAV RIGHT +#ENTER {discnav-menu} discnav select # DISCNAV SELECT (ok) +#MOUSE_BTN0 {discnav-menu} discnav mouse +#MOUSE_MOVE {discnav-menu} discnav mouse_move # # Not assigned by default diff --git a/input/cmd_list.c b/input/cmd_list.c index 76d97bd4df..35761cdbea 100644 --- a/input/cmd_list.c +++ b/input/cmd_list.c @@ -159,7 +159,7 @@ const struct mp_cmd_def mp_cmds[] = { }}, { MP_CMD_DISABLE_INPUT_SECTION, "disable_section", { ARG_STRING } }, - { MP_CMD_DVDNAV, "dvdnav", { ARG_STRING } }, + { MP_CMD_DISCNAV, "discnav", { ARG_STRING } }, { MP_CMD_AF, "af", { ARG_STRING, ARG_STRING } }, @@ -234,6 +234,7 @@ static const struct legacy_cmd legacy_cmds[] = { {"show_tracks", "show_text ${track-list}"}, {"show_playlist", "show_text ${playlist}"}, {"speed_mult", "multiply speed"}, + {"dvdnav", "discnav"}, // Approximate (can fail if user added additional whitespace) {"pt_step 1", "playlist_next"}, diff --git a/input/cmd_list.h b/input/cmd_list.h index 8768345de1..0ed5b1d90b 100644 --- a/input/cmd_list.h +++ b/input/cmd_list.h @@ -84,7 +84,7 @@ enum mp_command_type { MP_CMD_ENABLE_INPUT_SECTION, MP_CMD_DISABLE_INPUT_SECTION, - MP_CMD_DVDNAV, + MP_CMD_DISCNAV, /// DVB commands MP_CMD_DVB_SET_CHANNEL, diff --git a/old-makefile b/old-makefile index 5d5e72410b..4b1fe7f36a 100644 --- a/old-makefile +++ b/old-makefile @@ -222,7 +222,7 @@ SOURCES = audio/audio.c \ player/client.c \ player/configfiles.c \ player/command.c \ - player/dvdnav.c \ + player/discnav.c \ player/loadfile.c \ player/main.c \ player/misc.c \ diff --git a/player/command.c b/player/command.c index 8cc7e8120b..d22382c7fb 100644 --- a/player/command.c +++ b/player/command.c @@ -3333,7 +3333,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) mp_input_disable_section(mpctx->input, cmd->args[0].v.s); break; - case MP_CMD_DVDNAV: + case MP_CMD_DISCNAV: mp_nav_user_input(mpctx, cmd->args[0].v.s); break; diff --git a/player/core.h b/player/core.h index 55c526b4cc..1d16a0e005 100644 --- a/player/core.h +++ b/player/core.h @@ -371,7 +371,7 @@ void mp_write_watch_later_conf(struct MPContext *mpctx); struct playlist_entry *mp_check_playlist_resume(struct MPContext *mpctx, struct playlist *playlist); -// dvdnav.c +// discnav.c void mp_nav_init(struct MPContext *mpctx); void mp_nav_reset(struct MPContext *mpctx); void mp_nav_destroy(struct MPContext *mpctx); diff --git a/player/dvdnav.c b/player/discnav.c index 12ce5b621b..990f882d87 100644 --- a/player/dvdnav.c +++ b/player/discnav.c @@ -24,7 +24,7 @@ #include "common/common.h" #include "input/input.h" -#include "stream/stream_dvdnav.h" +#include "stream/discnav.h" #include "sub/dec_sub.h" #include "sub/osd.h" @@ -46,6 +46,8 @@ struct mp_nav_state { int vidsize[2]; int subsize[2]; struct sub_bitmap *hi_elem; + struct sub_bitmap *overlays[2]; + struct sub_bitmap outputs[3]; }; static inline bool is_valid_size(int size[2]) { @@ -84,12 +86,12 @@ void mp_nav_init(struct MPContext *mpctx) return; mpctx->nav_state = talloc_zero(NULL, struct mp_nav_state); - mpctx->nav_state->log = mp_log_new(mpctx->nav_state, mpctx->log, "dvdnav"); + mpctx->nav_state->log = mp_log_new(mpctx->nav_state, mpctx->log, "discnav"); MP_VERBOSE(mpctx->nav_state, "enabling\n"); - mp_input_enable_section(mpctx->input, "dvdnav", 0); - mp_input_set_section_mouse_area(mpctx->input, "dvdnav-menu", + mp_input_enable_section(mpctx->input, "discnav", 0); + mp_input_set_section_mouse_area(mpctx->input, "discnav-menu", INT_MIN, INT_MIN, INT_MAX, INT_MAX); } @@ -105,7 +107,7 @@ void mp_nav_reset(struct MPContext *mpctx) nav->nav_menu = false; nav->nav_draining = false; nav->nav_still_frame = 0; - mp_input_disable_section(mpctx->input, "dvdnav-menu"); + mp_input_disable_section(mpctx->input, "discnav-menu"); // Prevent demuxer init code to seek to the "start" mpctx->stream->start_pos = stream_tell(mpctx->stream); stream_control(mpctx->stream, STREAM_CTRL_RESUME_CACHE, NULL); @@ -116,8 +118,8 @@ void mp_nav_destroy(struct MPContext *mpctx) osd_set_nav_highlight(mpctx->osd, NULL); if (!mpctx->nav_state) return; - mp_input_disable_section(mpctx->input, "dvdnav"); - mp_input_disable_section(mpctx->input, "dvdnav-menu"); + mp_input_disable_section(mpctx->input, "discnav"); + mp_input_disable_section(mpctx->input, "discnav-menu"); talloc_free(mpctx->nav_state); mpctx->nav_state = NULL; } @@ -186,10 +188,10 @@ void mp_handle_nav(struct MPContext *mpctx) case MP_NAV_EVENT_MENU_MODE: nav->nav_menu = ev->u.menu_mode.enable; if (nav->nav_menu) { - mp_input_enable_section(mpctx->input, "dvdnav-menu", + mp_input_enable_section(mpctx->input, "discnav-menu", MP_INPUT_ON_TOP); } else { - mp_input_disable_section(mpctx->input, "dvdnav-menu"); + mp_input_disable_section(mpctx->input, "discnav-menu"); } break; case MP_NAV_EVENT_HIGHLIGHT: { @@ -207,6 +209,17 @@ void mp_handle_nav(struct MPContext *mpctx) osd_set_nav_highlight(mpctx->osd, mpctx); break; } + case MP_NAV_EVENT_OVERLAY: { + osd_set_nav_highlight(mpctx->osd, NULL); + 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]); + } + update_resolution(mpctx); + osd_set_nav_highlight(mpctx->osd, mpctx); + break; + } default: ; // ignore } talloc_free(ev); @@ -265,13 +278,26 @@ void mp_nav_get_highlight(void *priv, struct mp_osd_res res, nav->subsize[1] = sizes[1]; } - 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; - out_imgs->format = SUBBITMAP_RGBA; - out_imgs->parts = sub; - out_imgs->num_parts = sub->w > 0 && sub->h > 0 && nav->hi_visible; - osd_rescale_bitmaps(out_imgs, sizes[0], sizes[1], res, -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); + } } diff --git a/stream/stream_dvdnav.h b/stream/discnav.h index e27af6eb1e..9accf0c87a 100644 --- a/stream/stream_dvdnav.h +++ b/stream/discnav.h @@ -35,8 +35,11 @@ enum mp_nav_event_type { MP_NAV_EVENT_HIGHLIGHT, // highlight changed MP_NAV_EVENT_MENU_MODE, // menu mode on/off MP_NAV_EVENT_EOF, // it's over + MP_NAV_EVENT_OVERLAY, // overlay changed }; +struct sub_bitmap; + struct mp_nav_event { enum mp_nav_event_type event; union { @@ -51,6 +54,9 @@ struct mp_nav_event { struct { bool enable; } menu_mode; + struct { + struct sub_bitmap *images[2]; + } overlay; } u; }; diff --git a/stream/stream.c b/stream/stream.c index 1caf6e718c..a7990595d2 100644 --- a/stream/stream.c +++ b/stream/stream.c @@ -77,6 +77,7 @@ extern const stream_info_t stream_info_ifo; extern const stream_info_t stream_info_dvd; extern const stream_info_t stream_info_dvdnav; extern const stream_info_t stream_info_bluray; +extern const stream_info_t stream_info_bdnav; extern const stream_info_t stream_info_rar_filter; extern const stream_info_t stream_info_rar_entry; extern const stream_info_t stream_info_edl; @@ -114,6 +115,7 @@ static const stream_info_t *const stream_list[] = { #endif #if HAVE_LIBBLURAY &stream_info_bluray, + &stream_info_bdnav, #endif &stream_info_memory, diff --git a/stream/stream_bluray.c b/stream/stream_bluray.c index a30cdd603b..e7ecd08d64 100644 --- a/stream/stream_bluray.c +++ b/stream/stream_bluray.c @@ -23,7 +23,6 @@ * Use 'git clone git://git.videolan.org/libbluray' to get it. * * TODO: - * - Add libbdnav support for menus navigation * - Add AACS/BD+ protection detection * - Add descrambled keys database support (KEYDB.cfg) * @@ -34,6 +33,9 @@ #include <libbluray/bluray.h> #include <libbluray/meta_data.h> +#include <libbluray/overlay.h> +#include <libbluray/keys.h> +#include <libbluray/bluray-version.h> #include <libavutil/common.h> #include "config.h" @@ -43,12 +45,18 @@ #include "options/m_option.h" #include "stream.h" #include "osdep/timer.h" +#include "discnav.h" +#include "sub/osd.h" +#include "sub/img_convert.h" +#include "video/mp_image.h" +#include "video/mp_image_pool.h" #define BLURAY_SECTOR_SIZE 6144 #define BLURAY_DEFAULT_ANGLE 0 #define BLURAY_DEFAULT_CHAPTER 0 -#define BLURAY_DEFAULT_TITLE -1 +#define BLURAY_DEFAULT_TITLE -2 +#define BLURAY_MENU_TITLE -1 // 90khz ticks #define BD_TIMEBASE (90000) @@ -58,6 +66,12 @@ char *bluray_device = NULL; int bluray_angle = 0; +struct bluray_overlay { + struct sub_bitmap *image; + bool clean, hidden; + int x, y, w, h; +}; + struct bluray_priv_s { BLURAY *bd; BLURAY_TITLE_INFO *title_info; @@ -68,10 +82,25 @@ struct bluray_priv_s { int cfg_title; char *cfg_device; + + // overlay stuffs + struct bluray_overlay overlays[2], ol_flushed[2]; + struct mp_image_pool *pool; + + // navigation stuffs + uint64_t next_event; + uint32_t still_length; + int mousex, mousey; + bool in_menu, use_nav, nav_enabled, popup_enabled; }; static struct bluray_priv_s bluray_stream_priv_dflts = { - .cfg_title = BLURAY_DEFAULT_TITLE + .cfg_title = BLURAY_DEFAULT_TITLE, +}; + +static struct bluray_priv_s bdnav_stream_priv_dflts = { + .cfg_title = BLURAY_DEFAULT_TITLE, + .use_nav = true, }; #define OPT_BASE_STRUCT struct bluray_priv_s @@ -82,42 +111,254 @@ static const m_option_t bluray_stream_opts_fields[] = { {0} }; -static void bluray_stream_close(stream_t *s) +static const m_option_t bdnav_stream_opts_fields[] = { + OPT_CHOICE_OR_INT("title", cfg_title, 0, 0, 99999, + ({"menu", BLURAY_MENU_TITLE}, + {"first", BLURAY_DEFAULT_TITLE})), + OPT_STRING("device", cfg_device, 0), + {0} +}; + +static void destruct(struct bluray_priv_s *priv) { - struct bluray_priv_s *b = s->priv; + if (priv->title_info) + bd_free_title_info(priv->title_info); + bd_close(priv->bd); + talloc_free(priv->pool); +} - if (b->title_info) - bd_free_title_info(b->title_info); - bd_close(b->bd); +inline static int play_title(struct bluray_priv_s *priv, int title) +{ + if (priv->use_nav) { + if (title == priv->num_titles - 1) + title = BLURAY_TITLE_FIRST_PLAY; + return bd_play_title(priv->bd, title); + } else + return bd_select_title(priv->bd, title); } -static int bluray_stream_seek(stream_t *s, int64_t pos) +static void overlay_release(struct bluray_overlay *overlay) { - struct bluray_priv_s *b = s->priv; - int64_t p; + if (overlay->image) + talloc_free(overlay->image); + *overlay = (struct bluray_overlay) { .clean = true }; +} + +static void overlay_alloc(struct bluray_priv_s *priv, + struct bluray_overlay *overlay, + int x, int y, int w, int h) +{ + assert(overlay->image == NULL); + struct sub_bitmap *image = talloc_zero(NULL, struct sub_bitmap); + overlay->w = image->w = image->dw = w; + overlay->h = image->h = image->dh = h; + overlay->x = image->x = x; + overlay->y = image->y = y; + struct mp_image *mpi = mp_image_pool_get(priv->pool, IMGFMT_RGBA, w, h); + mpi = talloc_steal(image, mpi); + assert(image->w > 0 && image->h > 0 && mpi != NULL); + image->stride = mpi->stride[0]; + image->bitmap = mpi->planes[0]; + overlay->image = image; + overlay->clean = true; + overlay->hidden = false; +} + +static void overlay_close_all(struct bluray_priv_s *priv) +{ + for (int i = 0; i < 2; i++) + overlay_release(&priv->overlays[i]); +} + +static void overlay_close(struct bluray_priv_s *priv, + const BD_OVERLAY *const bo) +{ + overlay_release(&priv->overlays[bo->plane]); +} - p = bd_seek(b->bd, pos); - if (p == -1) - return 0; +static inline uint32_t conv_rgba(const BD_PG_PALETTE_ENTRY *p) +{ + uint32_t rgba; + uint8_t *out = (uint8_t*)&rgba; + const int y = p->Y, cb = (int)p->Cb - 128, cr = (int)p->Cr - 128; + // CAUTION: inaccurate but fast, broken in big endian +#define CONV(a) (MPCLAMP((a), 0, 255)*p->T >> 8) + out[0] = CONV(y + cb + (cb >> 1) + (cb >> 2) + (cb >> 6)); + out[1] = CONV(y - ((cb >> 2) + (cb >> 4) + (cb >> 5)) + - ((cr >> 3) + (cr >> 4) + (cr >> 5))); + out[2] = CONV(y + cr + (cr >> 2) + (cr >> 3) + (cr >> 5)); + out[3] = p->T; +#undef CONV + return rgba; +} - return 1; +static void overlay_process(void *data, const BD_OVERLAY *const bo) +{ + stream_t *s = data; + struct bluray_priv_s *priv = s->priv; + if (!bo) { + overlay_close_all(priv); + return; + } + struct bluray_overlay *overlay = &priv->overlays[bo->plane]; + switch (bo->cmd) { + case BD_OVERLAY_INIT: + overlay_alloc(priv, overlay, bo->x, bo->y, bo->w, bo->h); + break; + case BD_OVERLAY_CLOSE: + overlay_close(priv, bo); + break; + case BD_OVERLAY_CLEAR: + if (!overlay->clean) { + memset(overlay->image->bitmap, 0, + overlay->image->stride*overlay->h); + overlay->clean = true; + } + break; + case BD_OVERLAY_DRAW: { + if (!bo->img) + break; + overlay->hidden = false; + overlay->clean = false; + struct sub_bitmap *img = overlay->image; + uint32_t *const origin = img->bitmap; + const BD_PG_RLE_ELEM *in = bo->img; + for (int y = 0; y < bo->h; y++) { + uint32_t *out = origin + (img->stride/4) * (y + bo->y) + bo->x; + for (int x = 0; x < bo->w; ) { + uint32_t c = 0; + if (bo->palette[in->color].T) { + c = conv_rgba(&bo->palette[in->color]); + for (int i = 0; i < in->len; i++) + *out++ = c; + } else { + memset(out, 0, in->len*4); + out += in->len; + } + x += in->len; + ++in; + } + } + break; + } + case BD_OVERLAY_WIPE: { + uint32_t *const origin = overlay->image->bitmap; + for (int y = 0; y < bo->h; y++) + memset(origin + overlay->w * (y + bo->y) + bo->x, 0, 4 * bo->w); + break; + } + case BD_OVERLAY_HIDE: + priv->overlays[bo->plane].hidden = true; + break; + case BD_OVERLAY_FLUSH: { + struct bluray_overlay *in = overlay; + struct bluray_overlay *out = &priv->ol_flushed[bo->plane]; + if (out->image && (out->image->stride != in->image->stride || + out->image->h != in->image->h)) + overlay_release(out); + if (!out->image) + overlay_alloc(priv, out, in->x, in->y, in->w, in->h); + const int len = in->image->stride*in->image->h; + memcpy(out->image->bitmap, in->image->bitmap, len); + out->clean = in->clean; + out->hidden = in->hidden; + priv->next_event |= 1 << MP_NAV_EVENT_OVERLAY; + break; + } default: + break; + } +} + +static inline bool set_event_type(struct bluray_priv_s *priv, int type, + struct mp_nav_event *event) +{ + if (!(priv->next_event & (1 << type))) + return false; + priv->next_event &= ~(1 << type); + event->event = type; + return true; +} + +static void fill_next_event(stream_t *s, struct mp_nav_event **ret) +{ + struct bluray_priv_s *priv = s->priv; + struct mp_nav_event e = {0}; + // this should be checked before any other events + if (!set_event_type(priv, MP_NAV_EVENT_RESET_ALL, &e)) + for (int n = 0; n < 30 && !set_event_type(priv, n, &e); n++) ; + switch (e.event) { + case MP_NAV_EVENT_NONE: + return; + case MP_NAV_EVENT_OVERLAY: { + for (int i = 0; i < 2; i++) { + struct bluray_overlay *o = &priv->ol_flushed[i]; + e.u.overlay.images[i] = NULL; + if (!o->clean && !o->hidden) { + e.u.overlay.images[i] = o->image; + o->image = NULL; + } + } + break; + } + case MP_NAV_EVENT_MENU_MODE: + e.u.menu_mode.enable = priv->in_menu; + break; + case MP_NAV_EVENT_STILL_FRAME: + e.u.still_frame.seconds = priv->still_length; + break; + } + *ret = talloc(NULL, struct mp_nav_event); + **ret = e; +} + +static void bluray_stream_close(stream_t *s) +{ + destruct(s->priv); } static void handle_event(stream_t *s, const BD_EVENT *ev) { + static const int reset_flags = (1 << MP_NAV_EVENT_RESET_ALL) + | (1 << MP_NAV_EVENT_RESET); struct bluray_priv_s *b = s->priv; switch (ev->event) { + case BD_EVENT_MENU: + b->in_menu = ev->param; + b->next_event |= 1 << MP_NAV_EVENT_MENU_MODE; + break; + case BD_EVENT_STILL: + b->still_length = ev->param ? -1 : 0; + if (b->nav_enabled) + b->next_event |= 1 << MP_NAV_EVENT_STILL_FRAME; + break; + case BD_EVENT_STILL_TIME: + b->still_length = ev->param ? -1 : ev->param*1000; + if (b->nav_enabled) + b->next_event |= 1 << MP_NAV_EVENT_STILL_FRAME; + else + bd_read_skip_still(b->bd); + break; + case BD_EVENT_END_OF_TITLE: + overlay_close_all(b); + break; case BD_EVENT_PLAYLIST: + b->next_event = reset_flags; b->current_playlist = ev->param; + if (!b->use_nav) + b->current_title = bd_get_current_title(b->bd); if (b->title_info) bd_free_title_info(b->title_info); b->title_info = bd_get_playlist_info(b->bd, b->current_playlist, b->current_angle); break; case BD_EVENT_TITLE: - if (ev->param == BLURAY_TITLE_FIRST_PLAY) - b->current_title = bd_get_current_title(b->bd); - else + b->next_event = reset_flags; + if (ev->param == BLURAY_TITLE_FIRST_PLAY) { + if (b->use_nav) + b->current_title = b->num_titles - 1; + else + b->current_title = bd_get_current_title(b->bd); + } else b->current_title = ev->param; if (b->title_info) { bd_free_title_info(b->title_info); @@ -132,6 +373,14 @@ static void handle_event(stream_t *s, const BD_EVENT *ev) b->current_angle); } break; + case BD_EVENT_POPUP: + b->popup_enabled = ev->param; + break; +#if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 5, 0) + case BD_EVENT_DISCONTINUITY: + b->next_event = reset_flags; + break; +#endif default: MP_TRACE(s, "Unhandled event: %d %d\n", ev->event, ev->param); break; @@ -141,126 +390,167 @@ static void handle_event(stream_t *s, const BD_EVENT *ev) static int bluray_stream_fill_buffer(stream_t *s, char *buf, int len) { struct bluray_priv_s *b = s->priv; - + assert(!b->use_nav); BD_EVENT event; while (bd_get_event(b->bd, &event)) handle_event(s, &event); return bd_read(b->bd, buf, len); } +static int bdnav_stream_fill_buffer(stream_t *s, char *buf, int len) +{ + struct bluray_priv_s *b = s->priv; + assert(b->use_nav); + BD_EVENT event; + int read = -1; + for (;;) { + read = bd_read_ext(b->bd, buf, len, &event); + if (read < 0) + return read; + if (read == 0) { + if (event.event == BD_EVENT_NONE) + return 0; // end of stream + handle_event(s, &event); + } else + break; + } + return read; +} + +static bd_vk_key_e translate_nav_menu_action(const char *cmd) +{ + if (strcmp(cmd, "mouse") == 0) + return BD_VK_MOUSE_ACTIVATE; + if (strcmp(cmd, "up") == 0) + return BD_VK_UP; + if (strcmp(cmd, "down") == 0) + return BD_VK_DOWN; + if (strcmp(cmd, "left") == 0) + return BD_VK_LEFT; + if (strcmp(cmd, "right") == 0) + return BD_VK_RIGHT; + if (strcmp(cmd, "select") == 0) + return BD_VK_ENTER; + return BD_VK_NONE; +} + +static void handle_nav_command(stream_t *s, struct mp_nav_cmd *ev) +{ + struct bluray_priv_s *priv = s->priv; + switch (ev->event) { + case MP_NAV_CMD_ENABLE: + priv->nav_enabled = true; + break; + case MP_NAV_CMD_MENU: { + const int64_t pts = mp_time_us(); + const char *action = ev->u.menu.action; + bd_vk_key_e key = translate_nav_menu_action(action); + if (key != BD_VK_NONE) { + if (key == BD_VK_MOUSE_ACTIVATE) + bd_mouse_select(priv->bd, pts, priv->mousex, priv->mousey); + bd_user_input(priv->bd, pts, key); + } else if (strcmp(action, "menu") == 0) { + if (priv->popup_enabled) + bd_user_input(priv->bd, pts, BD_VK_POPUP); + else + bd_menu_call(priv->bd, pts); + } + break; + } case MP_NAV_CMD_MOUSE_POS: + priv->mousex = ev->u.mouse_pos.x; + priv->mousey = ev->u.mouse_pos.y; + bd_mouse_select(priv->bd, mp_time_us(), priv->mousex, priv->mousey); + break; + case MP_NAV_CMD_SKIP_STILL: + bd_read_skip_still(priv->bd); + break; + } +} + static int bluray_stream_control(stream_t *s, int cmd, void *arg) { struct bluray_priv_s *b = s->priv; switch (cmd) { - case STREAM_CTRL_GET_NUM_CHAPTERS: { const BLURAY_TITLE_INFO *ti = b->title_info; - if (!ti) return STREAM_UNSUPPORTED; - *((unsigned int *) arg) = ti->chapter_count; - - return 1; + return STREAM_OK; } - case STREAM_CTRL_GET_CHAPTER_TIME: { const BLURAY_TITLE_INFO *ti = b->title_info; - int chapter = *(double *)arg; - double time = MP_NOPTS_VALUE; - if (!ti) return STREAM_UNSUPPORTED; - - if (chapter >= 0 || chapter < ti->chapter_count) { + int chapter = *(double *)arg; + double time = MP_NOPTS_VALUE; + if (chapter >= 0 || chapter < ti->chapter_count) time = BD_TIME_TO_MP(ti->chapters[chapter].start); - } - - if (time != MP_NOPTS_VALUE) { - *(double *)arg = time; - return STREAM_OK; - } - return STREAM_ERROR; + if (time == MP_NOPTS_VALUE) + return STREAM_ERROR; + *(double *)arg = time; + return STREAM_OK; } case STREAM_CTRL_SET_CURRENT_TITLE: { const uint32_t title = *((unsigned int*)arg); - if (title < b->num_titles && bd_select_title(b->bd, title)) { - b->current_title = title; - return STREAM_OK; - } - return STREAM_ERROR; + if (title >= b->num_titles || !play_title(b, title)) + return STREAM_UNSUPPORTED; + b->current_title = title; + return STREAM_OK; } case STREAM_CTRL_GET_CURRENT_TITLE: { *((unsigned int *) arg) = b->current_title; - return 1; + return STREAM_OK; } case STREAM_CTRL_GET_NUM_TITLES: { *((unsigned int *)arg) = b->num_titles; - return 1; + return STREAM_OK; } - case STREAM_CTRL_GET_TIME_LENGTH: { const BLURAY_TITLE_INFO *ti = b->title_info; - if (!ti) return STREAM_UNSUPPORTED; - *((double *) arg) = BD_TIME_TO_MP(ti->duration); return STREAM_OK; } - case STREAM_CTRL_GET_CURRENT_TIME: { *((double *) arg) = BD_TIME_TO_MP(bd_tell_time(b->bd)); return STREAM_OK; } - case STREAM_CTRL_SEEK_TO_TIME: { double pts = *((double *) arg); bd_seek_time(b->bd, BD_TIME_FROM_MP(pts)); - // Reset mpv internal stream position. - stream_seek(s, bd_tell(b->bd)); + stream_drop_buffers(s); // API makes it hard to determine seeking success return STREAM_OK; } - case STREAM_CTRL_GET_NUM_ANGLES: { const BLURAY_TITLE_INFO *ti = b->title_info; - if (!ti) return STREAM_UNSUPPORTED; - *((int *) arg) = ti->angle_count; - - return 1; + return STREAM_OK; } - case STREAM_CTRL_GET_ANGLE: { *((int *) arg) = b->current_angle; - return 1; + return STREAM_OK; } - case STREAM_CTRL_SET_ANGLE: { const BLURAY_TITLE_INFO *ti = b->title_info; - int angle = *((int *) arg); - if (!ti) return STREAM_UNSUPPORTED; - - if (angle < 0 || angle > ti->angle_count) { + int angle = *((int *) arg); + if (angle < 0 || angle > ti->angle_count) return STREAM_UNSUPPORTED; - } - b->current_angle = angle; bd_seamless_angle_change(b->bd, angle); - - return 1; + return STREAM_OK; } - case STREAM_CTRL_GET_LANG: { - struct stream_lang_req *req = arg; const BLURAY_TITLE_INFO *ti = b->title_info; if (ti && ti->clip_count) { + struct stream_lang_req *req = arg; BLURAY_STREAM_INFO *si = NULL; int count = 0; switch (req->type) { @@ -283,21 +573,30 @@ static int bluray_stream_control(stream_t *s, int cmd, void *arg) } return STREAM_ERROR; } - case STREAM_CTRL_GET_START_TIME: - { + case STREAM_CTRL_GET_START_TIME: { *((double *)arg) = 0; return STREAM_OK; } case STREAM_CTRL_MANAGES_TIMELINE: return STREAM_OK; - case STREAM_CTRL_GET_DISC_NAME: - { + case STREAM_CTRL_GET_DISC_NAME: { const struct meta_dl *meta = bd_get_meta(b->bd); if (!meta || !meta->di_name || !meta->di_name[0]) break; *(char**)arg = talloc_strdup(NULL, meta->di_name); return STREAM_OK; } + case STREAM_CTRL_NAV_CMD: + if (!b->use_nav) + return STREAM_UNSUPPORTED; + handle_nav_command(s, arg); + return STREAM_OK; + case STREAM_CTRL_GET_NAV_EVENT: { + struct mp_nav_event **ev = arg; + if (ev) + fill_next_event(s, ev); + return STREAM_OK; + } default: break; } @@ -305,22 +604,57 @@ static int bluray_stream_control(stream_t *s, int cmd, void *arg) return STREAM_UNSUPPORTED; } -static int bluray_stream_open(stream_t *s, int mode) -{ +static void select_initial_title(stream_t *s, |