stream_bluray: implement HDMV menu navigation
Add HDMV menu support to stream_bluray, activated by opening with "bd://menu/..." or the global --disc-menu option. The IG overlay plane arrives through the libbluray YUV overlay callback. Decode the palettized RLE into a BGRA plane (premultiplied alpha) and hand it to the player via STREAM_CTRL_GET_NAV_OVERLAY. STREAM_CTRL_NAV_CMD maps arrow/select/menu/popup/mouse actions onto bd_user_input() and bd_mouse_select(). Events relevant to the menu state (title, playlist, menu on/off) are tracked so STREAM_CTRL_GET_NAV_STATE can report an active menu, and playlist changes bump discontinuity_id so the player flushes stale packets.
This commit is contained in:
1 parent
b17feb1f79
commit
3a8b6995e9
1 file changed
+423
-11
+423
-11
@@ -41,9 +41,11 @@
|
||||
#include "mpv_talloc.h"
|
||||
#include "common/common.h"
|
||||
#include "common/msg.h"
|
||||
#include "misc/thread_tools.h"
|
||||
#include "options/m_config.h"
|
||||
#include "options/options.h"
|
||||
#include "options/path.h"
|
||||
#include "osdep/threads.h"
|
||||
#include "stream.h"
|
||||
#include "osdep/io.h"
|
||||
#include "osdep/timer.h"
|
||||
@@ -96,14 +98,214 @@ struct bluray_priv_s {
|
||||
int current_title;
|
||||
int current_playlist;
|
||||
|
||||
// Cached map from filtered title index (0..num_titles-1) to mpls_id.
|
||||
uint32_t *title_to_playlist;
|
||||
|
||||
int cfg_title;
|
||||
int cfg_playlist;
|
||||
char *cfg_device;
|
||||
|
||||
struct mp_bluray_opts *opts;
|
||||
struct m_config_cache *opts_cache;
|
||||
|
||||
// HDMV menu support (enabled when cfg_title == BLURAY_MENU_TITLE).
|
||||
// libbluray delivers compressed YUV+RLE overlay primitives via
|
||||
// bd_register_overlay_proc for the HDMV graphics controller. We decompress
|
||||
// into ig_plane and snapshot to ig_publish on every FLUSH so the player
|
||||
// thread can read a coherent image under overlay_lock.
|
||||
//
|
||||
// libbluray docs seems to suggest we should use bd_register_argb_overlay_proc
|
||||
// if we can handle ARGB planes directly, but in practice that callback is
|
||||
// only used for BD-J menus, and HDMV menus goes through the RLE callback.
|
||||
// We support only the later, currently.
|
||||
bool hdmv_mode;
|
||||
uint32_t *ig_plane; // working BGRA plane, written from callback
|
||||
uint32_t *ig_publish; // last FLUSHed snapshot, read by player
|
||||
int plane_w, plane_h; // current allocation size (0 if unallocated)
|
||||
mp_mutex overlay_lock; // guards ig_publish + visibility flags
|
||||
|
||||
bool overlay_ig_visible; // IG plane was FLUSHED with non-empty content
|
||||
bool menu_event_active; // BD_EVENT_MENU == 1
|
||||
bool popup_supported; // BD_EVENT_POPUP == 1
|
||||
uint32_t nav_change_id; // bumped on FLUSH/HIDE/MENU/POPUP events
|
||||
uint32_t discontinuity_id; // bumped on actions that may hop (SELECT...)
|
||||
bool data_delivered; // any byte returned from fill_buffer yet
|
||||
};
|
||||
|
||||
// Lazy (re-)allocation for the IG-plane working/publish buffers.
|
||||
static bool bd_ensure_plane(struct bluray_priv_s *priv, int w, int h)
|
||||
{
|
||||
if (w <= 0 || h <= 0)
|
||||
return false;
|
||||
if (priv->ig_plane && w <= priv->plane_w && h <= priv->plane_h)
|
||||
return true;
|
||||
int nw = MPMAX(w, priv->plane_w);
|
||||
int nh = MPMAX(h, priv->plane_h);
|
||||
size_t bytes = (size_t)nw * nh * 4;
|
||||
uint32_t *plane = talloc_realloc(priv, priv->ig_plane, uint32_t, nw * nh);
|
||||
uint32_t *publish = talloc_realloc(priv, priv->ig_publish, uint32_t, nw * nh);
|
||||
if (!plane || !publish)
|
||||
return false;
|
||||
memset(plane, 0, bytes);
|
||||
memset(publish, 0, bytes);
|
||||
priv->ig_plane = plane;
|
||||
priv->ig_publish = publish;
|
||||
priv->plane_w = nw;
|
||||
priv->plane_h = nh;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bd_palette_to_bgra(const BD_PG_PALETTE_ENTRY *pg, uint32_t out[256])
|
||||
{
|
||||
for (int i = 0; i < 256; i++) {
|
||||
int Y = pg[i].Y, Cb = pg[i].Cb, Cr = pg[i].Cr, T = pg[i].T;
|
||||
// BT.709 limited->full.
|
||||
int y_ = (Y - 16) * 1192; // 1.164 << 10
|
||||
int cr = Cr - 128;
|
||||
int cb = Cb - 128;
|
||||
int r = (y_ + 1836 * cr + 512) >> 10; // 1.793
|
||||
int g = (y_ - 547 * cr - 218 * cb + 512) >> 10; // 0.534 / 0.213
|
||||
int b = (y_ + 2166 * cb + 512) >> 10; // 2.115
|
||||
r = MPCLAMP(r, 0, 255);
|
||||
g = MPCLAMP(g, 0, 255);
|
||||
b = MPCLAMP(b, 0, 255);
|
||||
// Pre-multiply RGB by alpha so the OSD layer can composite directly.
|
||||
r = r * T / 255;
|
||||
g = g * T / 255;
|
||||
b = b * T / 255;
|
||||
out[i] = ((uint32_t)T << 24) | ((uint32_t)r << 16) |
|
||||
((uint32_t)g << 8) | (uint32_t)b;
|
||||
}
|
||||
// palette index 0xFF is always transparent.
|
||||
out[0xFF] = 0;
|
||||
}
|
||||
|
||||
// Composite one RLE-encoded sub-bitmap into the IG plane at (ov->x, ov->y).
|
||||
static void bd_overlay_draw_rle(struct bluray_priv_s *priv, const BD_OVERLAY *ov)
|
||||
{
|
||||
if (!ov->img || !ov->palette || ov->w <= 0 || ov->h <= 0)
|
||||
return;
|
||||
uint32_t pal[256];
|
||||
bd_palette_to_bgra(ov->palette, pal);
|
||||
|
||||
const BD_PG_RLE_ELEM *rle = ov->img;
|
||||
for (int y = 0; y < ov->h; y++) {
|
||||
int dst_y = ov->y + y;
|
||||
bool in_plane = dst_y >= 0 && dst_y < priv->plane_h;
|
||||
uint32_t *dst_row = in_plane
|
||||
? priv->ig_plane + (size_t)dst_y * priv->plane_w : NULL;
|
||||
int x = 0;
|
||||
int cols = ov->w;
|
||||
while (x < ov->w && cols-- >= 0) {
|
||||
int len = rle->len;
|
||||
int color = rle->color;
|
||||
rle++;
|
||||
if (len == 0)
|
||||
continue; // stray EOL, skip
|
||||
int dst_x = ov->x + x;
|
||||
int run = len;
|
||||
if (dst_x < 0) {
|
||||
int skip = MPMIN(-dst_x, run);
|
||||
run -= skip;
|
||||
dst_x += skip;
|
||||
}
|
||||
if (dst_x + run > priv->plane_w)
|
||||
run = priv->plane_w - dst_x;
|
||||
if (dst_row && run > 0) {
|
||||
uint32_t c = pal[color & 0xFF];
|
||||
for (int i = 0; i < run; i++)
|
||||
dst_row[dst_x + i] = c;
|
||||
}
|
||||
x += len;
|
||||
}
|
||||
if (rle->len == 0)
|
||||
rle++;
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshot the working plane to ig_publish and recompute visibility.
|
||||
// Must be called with priv->overlay_lock held.
|
||||
static void bd_publish_overlay_flush(struct bluray_priv_s *priv)
|
||||
{
|
||||
if (!priv->ig_plane)
|
||||
return;
|
||||
bool any = false;
|
||||
size_t n = (size_t)priv->plane_w * priv->plane_h;
|
||||
memcpy(priv->ig_publish, priv->ig_plane, n * 4);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (priv->ig_publish[i] & 0xFF000000) {
|
||||
any = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
priv->overlay_ig_visible = any;
|
||||
priv->nav_change_id++;
|
||||
}
|
||||
|
||||
// Called by libbluray's HDMV graphics controller for every overlay primitive
|
||||
// on either plane. We only render the IG (menu) plane; PG (subtitles) flows
|
||||
// through the regular demuxer/sd_lavc pipeline.
|
||||
static void bd_yuv_overlay_cb(void *handle, const struct bd_overlay_s *ov)
|
||||
{
|
||||
struct bluray_priv_s *priv = handle;
|
||||
if (!ov)
|
||||
return;
|
||||
if (ov->plane != BD_OVERLAY_IG)
|
||||
return;
|
||||
|
||||
mp_mutex_lock(&priv->overlay_lock);
|
||||
switch (ov->cmd) {
|
||||
case BD_OVERLAY_INIT:
|
||||
bd_ensure_plane(priv, ov->w, ov->h);
|
||||
if (priv->ig_plane) {
|
||||
memset(priv->ig_plane, 0,
|
||||
(size_t)priv->plane_w * priv->plane_h * 4);
|
||||
}
|
||||
priv->overlay_ig_visible = false;
|
||||
priv->nav_change_id++;
|
||||
break;
|
||||
case BD_OVERLAY_CLOSE:
|
||||
priv->overlay_ig_visible = false;
|
||||
priv->nav_change_id++;
|
||||
break;
|
||||
case BD_OVERLAY_CLEAR:
|
||||
if (priv->ig_plane) {
|
||||
memset(priv->ig_plane, 0,
|
||||
(size_t)priv->plane_w * priv->plane_h * 4);
|
||||
}
|
||||
break;
|
||||
case BD_OVERLAY_WIPE:
|
||||
if (priv->ig_plane) {
|
||||
for (int y = 0; y < ov->h; y++) {
|
||||
int dy = ov->y + y;
|
||||
if (dy < 0 || dy >= priv->plane_h)
|
||||
continue;
|
||||
int dx = MPMAX(ov->x, 0);
|
||||
int run = MPMIN(ov->w, priv->plane_w - dx);
|
||||
if (run > 0) {
|
||||
memset(priv->ig_plane + (size_t)dy * priv->plane_w + dx,
|
||||
0, run * 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BD_OVERLAY_DRAW:
|
||||
if (priv->ig_plane)
|
||||
bd_overlay_draw_rle(priv, ov);
|
||||
break;
|
||||
case BD_OVERLAY_HIDE:
|
||||
priv->overlay_ig_visible = false;
|
||||
priv->nav_change_id++;
|
||||
break;
|
||||
case BD_OVERLAY_FLUSH:
|
||||
bd_publish_overlay_flush(priv);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mp_mutex_unlock(&priv->overlay_lock);
|
||||
}
|
||||
|
||||
inline static int play_playlist(struct bluray_priv_s *priv, int playlist)
|
||||
{
|
||||
return bd_select_playlist(priv->bd, playlist);
|
||||
@@ -122,17 +324,33 @@ static void bluray_stream_close(stream_t *s)
|
||||
|
||||
if (priv->title_info)
|
||||
bd_free_title_info(priv->title_info);
|
||||
if (priv->bd)
|
||||
if (priv->bd) {
|
||||
if (priv->hdmv_mode)
|
||||
bd_register_overlay_proc(priv->bd, NULL, NULL);
|
||||
bd_close(priv->bd);
|
||||
}
|
||||
if (priv->hdmv_mode)
|
||||
mp_mutex_destroy(&priv->overlay_lock);
|
||||
}
|
||||
|
||||
static void handle_event(stream_t *s, const BD_EVENT *ev)
|
||||
{
|
||||
struct bluray_priv_s *b = s->priv;
|
||||
if (b->hdmv_mode)
|
||||
MP_VERBOSE(s, "bdnav: event %d param %u\n", ev->event, ev->param);
|
||||
switch (ev->event) {
|
||||
case BD_EVENT_MENU:
|
||||
// ev->param: 1 if the disc is currently in an HDMV menu, 0 otherwise.
|
||||
if (b->hdmv_mode) {
|
||||
mp_mutex_lock(&b->overlay_lock);
|
||||
b->menu_event_active = ev->param != 0;
|
||||
b->nav_change_id++;
|
||||
mp_mutex_unlock(&b->overlay_lock);
|
||||
}
|
||||
break;
|
||||
case BD_EVENT_STILL:
|
||||
if (ev->param)
|
||||
bd_read_skip_still(b->bd);
|
||||
break;
|
||||
case BD_EVENT_STILL_TIME:
|
||||
bd_read_skip_still(b->bd);
|
||||
@@ -142,20 +360,35 @@ static void handle_event(stream_t *s, const BD_EVENT *ev)
|
||||
case BD_EVENT_PLAYLIST:
|
||||
b->current_playlist = ev->param;
|
||||
b->current_title = bd_get_current_title(b->bd);
|
||||
if (b->title_to_playlist) {
|
||||
for (int i = 0; i < b->num_titles; i++) {
|
||||
if (b->title_to_playlist[i] == (uint32_t)ev->param) {
|
||||
b->current_title = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
if (b->hdmv_mode) {
|
||||
mp_mutex_lock(&b->overlay_lock);
|
||||
b->discontinuity_id++;
|
||||
mp_mutex_unlock(&b->overlay_lock);
|
||||
}
|
||||
break;
|
||||
case BD_EVENT_TITLE:
|
||||
if (ev->param == BLURAY_TITLE_FIRST_PLAY) {
|
||||
b->current_title = bd_get_current_title(b->bd);
|
||||
} else
|
||||
b->current_title = ev->param;
|
||||
b->current_title = bd_get_current_title(b->bd);
|
||||
if (b->title_info) {
|
||||
bd_free_title_info(b->title_info);
|
||||
b->title_info = NULL;
|
||||
}
|
||||
if (b->hdmv_mode) {
|
||||
mp_mutex_lock(&b->overlay_lock);
|
||||
b->discontinuity_id++;
|
||||
mp_mutex_unlock(&b->overlay_lock);
|
||||
}
|
||||
break;
|
||||
case BD_EVENT_ANGLE:
|
||||
b->current_angle = ev->param;
|
||||
@@ -166,6 +399,13 @@ static void handle_event(stream_t *s, const BD_EVENT *ev)
|
||||
}
|
||||
break;
|
||||
case BD_EVENT_POPUP:
|
||||
// ev->param: 1 if popup menu is currently available, 0 otherwise.
|
||||
if (b->hdmv_mode) {
|
||||
mp_mutex_lock(&b->overlay_lock);
|
||||
b->popup_supported = ev->param != 0;
|
||||
b->nav_change_id++;
|
||||
mp_mutex_unlock(&b->overlay_lock);
|
||||
}
|
||||
break;
|
||||
#if BLURAY_VERSION >= BLURAY_VERSION_CODE(0, 5, 0)
|
||||
case BD_EVENT_DISCONTINUITY:
|
||||
@@ -181,6 +421,51 @@ static int bluray_stream_fill_buffer(stream_t *s, void *buf, int len)
|
||||
{
|
||||
struct bluray_priv_s *b = s->priv;
|
||||
BD_EVENT event;
|
||||
|
||||
if (b->hdmv_mode) {
|
||||
// bd_read() doesn't drive the HDMV VM, so the disc's first-play
|
||||
// bytecode would never run and we'd be stuck with "no valid title"
|
||||
// forever. bd_read_ext() runs the VM between event drains and also
|
||||
// delivers one event per call, which we hand off to handle_event.
|
||||
while (bd_get_event(b->bd, &event))
|
||||
handle_event(s, &event);
|
||||
int total = 0;
|
||||
int events_seen = 0;
|
||||
// Loop briefly to absorb event-only returns (where bd_read_ext
|
||||
// returns 0 with a freshly produced event) before reporting EOF.
|
||||
// If an event bumps discontinuity_id (PLAYLIST/TITLE) *after* we
|
||||
// have already delivered data to the slave demuxer, stop here even
|
||||
// if no data was read: the next bd_read_ext would deliver data from
|
||||
// the new playlist, but the slave must be reopened first so it
|
||||
// parses with the correct codec context.
|
||||
for (int i = 0; i < 200; i++) {
|
||||
uint32_t disc_before = b->discontinuity_id;
|
||||
int n = bd_read_ext(b->bd, (uint8_t *)buf + total, len - total, &event);
|
||||
if (n < 0) {
|
||||
MP_VERBOSE(s, "bdnav: bd_read_ext err iter=%d\n", i);
|
||||
return -1;
|
||||
}
|
||||
if (event.event != BD_EVENT_NONE) {
|
||||
handle_event(s, &event);
|
||||
events_seen++;
|
||||
}
|
||||
if (n > 0) {
|
||||
total += n;
|
||||
break;
|
||||
}
|
||||
if (b->data_delivered && b->discontinuity_id != disc_before)
|
||||
break;
|
||||
if (mp_cancel_test(s->cancel))
|
||||
return 0;
|
||||
mp_sleep_ns(MP_TIME_MS_TO_NS(5));
|
||||
}
|
||||
if (total > 0)
|
||||
b->data_delivered = true;
|
||||
if (total == 0)
|
||||
MP_VERBOSE(s, "bdnav: fill returned 0 (events=%d)\n", events_seen);
|
||||
return total;
|
||||
}
|
||||
|
||||
while (bd_get_event(b->bd, &event))
|
||||
handle_event(s, &event);
|
||||
return bd_read(b->bd, buf, len);
|
||||
@@ -213,6 +498,12 @@ static int bluray_stream_control(stream_t *s, int cmd, void *arg)
|
||||
}
|
||||
case STREAM_CTRL_SET_CURRENT_TITLE: {
|
||||
const uint32_t title = *((unsigned int*)arg);
|
||||
// demux_disc appends a synthetic "Disc Menu" edition at index num_titles.
|
||||
if (title == b->num_titles) {
|
||||
if (!b->hdmv_mode || !bd_menu_call(b->bd, -1))
|
||||
return STREAM_UNSUPPORTED;
|
||||
return STREAM_OK;
|
||||
}
|
||||
if (title >= b->num_titles || !play_title(b, title))
|
||||
return STREAM_UNSUPPORTED;
|
||||
b->current_title = title;
|
||||
@@ -321,6 +612,99 @@ static int bluray_stream_control(stream_t *s, int cmd, void *arg)
|
||||
*(char**)arg = talloc_strdup(NULL, meta->di_name);
|
||||
return STREAM_OK;
|
||||
}
|
||||
case STREAM_CTRL_NAV_CMD: {
|
||||
if (!b->hdmv_mode)
|
||||
return STREAM_UNSUPPORTED;
|
||||
struct stream_nav_cmd *nav = arg;
|
||||
uint32_t key = BD_VK_NONE;
|
||||
switch (nav->action) {
|
||||
case STREAM_NAV_UP:
|
||||
key = BD_VK_UP;
|
||||
break;
|
||||
case STREAM_NAV_DOWN:
|
||||
key = BD_VK_DOWN;
|
||||
break;
|
||||
case STREAM_NAV_LEFT:
|
||||
key = BD_VK_LEFT;
|
||||
break;
|
||||
case STREAM_NAV_RIGHT:
|
||||
key = BD_VK_RIGHT;
|
||||
break;
|
||||
case STREAM_NAV_SELECT:
|
||||
key = BD_VK_ENTER;
|
||||
break;
|
||||
case STREAM_NAV_MENU_ROOT:
|
||||
case STREAM_NAV_MENU_TITLE:
|
||||
// BD doesn't distinguish "title menu", both map to disc root.
|
||||
bd_menu_call(b->bd, -1);
|
||||
return STREAM_OK;
|
||||
case STREAM_NAV_MENU_POPUP:
|
||||
key = BD_VK_POPUP;
|
||||
break;
|
||||
case STREAM_NAV_PREV_MENU:
|
||||
// No dedicated "previous menu" key; popup-toggle is the closest
|
||||
// equivalent and behaves like "dismiss current menu" on most
|
||||
// discs when already in popup.
|
||||
key = BD_VK_POPUP;
|
||||
break;
|
||||
case STREAM_NAV_MOUSE_MOVE:
|
||||
bd_mouse_select(b->bd, -1, nav->x, nav->y);
|
||||
return STREAM_OK;
|
||||
case STREAM_NAV_MOUSE_CLICK:
|
||||
bd_mouse_select(b->bd, -1, nav->x, nav->y);
|
||||
key = BD_VK_MOUSE_ACTIVATE;
|
||||
break;
|
||||
}
|
||||
if (key != BD_VK_NONE)
|
||||
bd_user_input(b->bd, -1, key);
|
||||
return STREAM_OK;
|
||||
}
|
||||
case STREAM_CTRL_GET_NAV_STATE: {
|
||||
struct stream_nav_state *st = arg;
|
||||
if (!b->hdmv_mode) {
|
||||
*st = (struct stream_nav_state){0};
|
||||
return STREAM_OK;
|
||||
}
|
||||
mp_mutex_lock(&b->overlay_lock);
|
||||
bool visible = b->menu_event_active && b->overlay_ig_visible;
|
||||
*st = (struct stream_nav_state){
|
||||
.menu_active = visible,
|
||||
.has_popup = b->popup_supported,
|
||||
.src_w = b->plane_w,
|
||||
.src_h = b->plane_h,
|
||||
.change_id = b->nav_change_id,
|
||||
.discontinuity_id = b->discontinuity_id,
|
||||
};
|
||||
mp_mutex_unlock(&b->overlay_lock);
|
||||
return STREAM_OK;
|
||||
}
|
||||
case STREAM_CTRL_GET_NAV_OVERLAY: {
|
||||
if (!b->hdmv_mode)
|
||||
return STREAM_UNSUPPORTED;
|
||||
struct stream_nav_overlay_req *req = arg;
|
||||
if (!req->dst || req->w <= 0 || req->h <= 0)
|
||||
return STREAM_ERROR;
|
||||
mp_mutex_lock(&b->overlay_lock);
|
||||
int copy_w = MPMIN(req->w, b->plane_w);
|
||||
int copy_h = MPMIN(req->h, b->plane_h);
|
||||
if (b->ig_publish && b->overlay_ig_visible) {
|
||||
for (int y = 0; y < copy_h; y++) {
|
||||
memcpy(req->dst + y * req->stride,
|
||||
b->ig_publish + y * b->plane_w,
|
||||
copy_w * 4);
|
||||
}
|
||||
} else {
|
||||
// Plane is hidden / pre-init; clear the caller's buffer so a
|
||||
// stale image doesn't linger after the menu closes.
|
||||
for (int y = 0; y < copy_h; y++)
|
||||
memset(req->dst + y * req->stride, 0, copy_w * 4);
|
||||
}
|
||||
req->change_id = b->nav_change_id;
|
||||
req->w = copy_w;
|
||||
req->h = copy_h;
|
||||
mp_mutex_unlock(&b->overlay_lock);
|
||||
return STREAM_OK;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -463,13 +847,17 @@ static int bluray_stream_open_internal(stream_t *s)
|
||||
MP_INFO(s, "List of available titles:\n");
|
||||
|
||||
/* parse titles information */
|
||||
b->title_to_playlist = talloc_array(b, uint32_t, b->num_titles);
|
||||
for (int i = 0; i < b->num_titles; i++) {
|
||||
b->title_to_playlist[i] = (uint32_t)-1;
|
||||
/* the information we're accessing (duration, playlist, angle count)
|
||||
* doesn't depend on the angle */
|
||||
BLURAY_TITLE_INFO *ti = bd_get_title_info(bd, i, 0);
|
||||
if (!ti)
|
||||
continue;
|
||||
|
||||
b->title_to_playlist[i] = ti->playlist;
|
||||
|
||||
char *time = mp_format_time(ti->duration / 90000, false);
|
||||
MP_INFO(s, "idx: %3d duration: %s angles: %2d (playlist: %05d.mpls)\n",
|
||||
i, time, ti->angle_count, ti->playlist);
|
||||
@@ -485,10 +873,28 @@ static int bluray_stream_open_internal(stream_t *s)
|
||||
// initialize libbluray event queue
|
||||
bd_get_event(bd, NULL);
|
||||
|
||||
select_initial_title(s, bd_get_main_title(bd));
|
||||
b->hdmv_mode = b->cfg_title == BLURAY_MENU_TITLE;
|
||||
MP_VERBOSE(s, "bdnav: cfg_title=%d hdmv_mode=%d\n", b->cfg_title, b->hdmv_mode);
|
||||
if (b->hdmv_mode) {
|
||||
mp_mutex_init(&b->overlay_lock);
|
||||
bd_register_overlay_proc(bd, b, bd_yuv_overlay_cb);
|
||||
if (!bd_play(bd)) {
|
||||
MP_ERR(s, "Couldn't start Blu-ray HDMV playback.\n");
|
||||
ret = STREAM_UNSUPPORTED;
|
||||
goto err;
|
||||
}
|
||||
b->current_title = bd_get_current_title(bd);
|
||||
MP_VERBOSE(s, "bdnav: HDMV entered; current title=%d\n",
|
||||
b->current_title);
|
||||
} else {
|
||||
select_initial_title(s, bd_get_main_title(bd));
|
||||
}
|
||||
|
||||
if (!bd_select_angle(bd, b->opts->angle - 1))
|
||||
MP_WARN(s, "Couldn't select angle '%d'.\n", b->opts->angle - 1);
|
||||
// Angle selection is only valid once a playlist has been picked.
|
||||
if (!b->hdmv_mode) {
|
||||
if (!bd_select_angle(bd, b->opts->angle - 1))
|
||||
MP_WARN(s, "Couldn't select angle '%d'.\n", b->opts->angle - 1);
|
||||
}
|
||||
|
||||
b->current_angle = bd_get_current_angle(bd);
|
||||
|
||||
@@ -519,12 +925,15 @@ static int bluray_stream_open(stream_t *s)
|
||||
|
||||
struct MPOpts *opts = mp_get_config_group(s, s->global, &mp_opt_root);
|
||||
int edition_id = opts->edition_id;
|
||||
bool disc_menu = opts->disc_menu;
|
||||
talloc_free(opts);
|
||||
|
||||
if (edition_id >= 0) {
|
||||
b->cfg_title = edition_id;
|
||||
} else if (bstr_equals0(title, "longest") || bstr_equals0(title, "first")) {
|
||||
b->cfg_title = BLURAY_DEFAULT_TITLE;
|
||||
} else if (title.len == 0 || bstr_equals0(title, "longest") ||
|
||||
bstr_equals0(title, "first"))
|
||||
{
|
||||
b->cfg_title = disc_menu ? BLURAY_MENU_TITLE : BLURAY_DEFAULT_TITLE;
|
||||
} else if (bstr_equals0(title, "menu")) {
|
||||
b->cfg_title = BLURAY_MENU_TITLE;
|
||||
} else if (bstr_equals0(title, "mpls")) {
|
||||
@@ -609,8 +1018,11 @@ static int bdmv_dir_stream_open(stream_t *stream)
|
||||
struct bluray_priv_s *priv = talloc_ptrtype(stream, priv);
|
||||
stream->priv = priv;
|
||||
struct MPOpts *opts = mp_get_config_group(NULL, stream->global, &mp_opt_root);
|
||||
int default_title = opts->edition_id >= 0 ? opts->edition_id
|
||||
: opts->disc_menu ? BLURAY_MENU_TITLE
|
||||
: BLURAY_DEFAULT_TITLE;
|
||||
*priv = (struct bluray_priv_s){
|
||||
.cfg_title = opts->edition_id >= 0 ? opts->edition_id : BLURAY_DEFAULT_TITLE,
|
||||
.cfg_title = default_title,
|
||||
};
|
||||
talloc_free(opts);
|
||||
|
||||
|
||||
Reference in new issue
Block a user