Commit f0b52e34 authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen Committed by Jean-Baptiste Kempf

bluray: Add support for overlay

Signed-off-by: default avatarJean-Baptiste Kempf <jb@videolan.org>
(cherry picked from commit 70d36ebbdcfd0c787d605e676699632442365f51)
Signed-off-by: default avatarJean-Baptiste Kempf <jb@videolan.org>
parent 3d7ba9ae
......@@ -36,9 +36,11 @@
#include <vlc_demux.h> /* demux_t */
#include <vlc_input.h> /* Seekpoints, chapters */
#include <vlc_dialog.h> /* BD+/AACS warnings */
#include <vlc_vout.h> /* vout_PutSubpicture / subpicture_t */
#include <libbluray/bluray.h>
#include <libbluray/meta_data.h>
#include <libbluray/overlay.h>
/*****************************************************************************
* Module descriptor
......@@ -66,21 +68,51 @@ vlc_module_begin ()
set_callbacks( blurayOpen, blurayClose )
vlc_module_end ()
/* libbluray's overlay.h defines 2 types of overlay (bd_overlay_plane_e). */
#define MAX_OVERLAY 2
struct demux_sys_t
typedef enum OverlayStatus {
Closed = 0,
ToDisplay, //Used to mark the overlay to be displayed the first time.
Displayed,
Outdated //used to update the overlay after it has been sent to the vout
} OverlayStatus;
typedef struct bluray_overlay_t
{
BLURAY *bluray;
VLC_GC_MEMBERS
vlc_mutex_t lock;
subpicture_t *p_pic;
OverlayStatus status;
subpicture_region_t *p_regions;
} bluray_overlay_t;
struct demux_sys_t
{
BLURAY *bluray;
/* Titles */
unsigned int i_title;
unsigned int i_longest_title;
input_title_t **pp_title;
unsigned int i_title;
unsigned int i_longest_title;
input_title_t **pp_title;
/* Menus */
bool b_menu;
bluray_overlay_t *p_overlays[MAX_OVERLAY];
int current_overlay; // -1 if no current overlay;
bool b_menu;
/* */
input_thread_t *p_input;
vout_thread_t *p_vout;
/* TS stream */
stream_t *p_parser;
stream_t *p_parser;
};
struct subpicture_updater_sys_t
{
bluray_overlay_t *p_overlay;
};
/*****************************************************************************
......@@ -92,6 +124,8 @@ static int blurayDemux(demux_t *);
static int blurayInitTitles(demux_t *p_demux );
static int bluraySetTitle(demux_t *p_demux, int i_title);
static void blurayOverlayProc(void *ptr, const BD_OVERLAY * const overlay);
#define FROM_TICKS(a) (a*CLOCK_FREQ / INT64_C(90000))
#define TO_TICKS(a) (a*INT64_C(90000)/CLOCK_FREQ)
#define CUR_LENGTH p_sys->pp_title[p_demux->info.i_title]->i_length
......@@ -115,11 +149,11 @@ static int blurayOpen( vlc_object_t *object )
}
/* */
p_demux->p_sys = p_sys = malloc(sizeof(*p_sys));
p_demux->p_sys = p_sys = calloc(1, sizeof(*p_sys));
if (unlikely(!p_sys)) {
return VLC_ENOMEM;
}
p_sys->p_parser = NULL;
p_sys->current_overlay = -1;
/* init demux info fields */
p_demux->info.i_update = 0;
......@@ -209,11 +243,15 @@ static int blurayOpen( vlc_object_t *object )
p_sys->b_menu = var_InheritBool( p_demux, "bluray-menu" );
if ( p_sys->b_menu )
{
/*
* libbluray will start playback from "First-Title" title
* Therefore, We don't have to select any title.
*/
bd_play( p_sys->bluray );
p_sys->p_input = demux_GetParentInput(p_demux);
if (unlikely(!p_sys->p_input))
goto error;
/* libbluray will start playback from "First-Title" title */
bd_play(p_sys->bluray);
/* Registering overlay event handler */
bd_register_overlay_proc(p_sys->bluray, p_demux, blurayOverlayProc);
}
else
{
......@@ -258,6 +296,18 @@ static void blurayClose( vlc_object_t *object )
demux_t *p_demux = (demux_t*)object;
demux_sys_t *p_sys = p_demux->p_sys;
/*
* Close libbluray first.
* This will close all the overlays before we release p_vout
* bd_close( NULL ) can crash
*/
assert(p_sys->bluray);
bd_close(p_sys->bluray);
if (p_sys->p_vout != NULL)
vlc_object_release(p_sys->p_vout);
if (p_sys->p_input != NULL)
vlc_object_release(p_sys->p_input);
if (p_sys->p_parser)
stream_Delete(p_sys->p_parser);
......@@ -266,12 +316,348 @@ static void blurayClose( vlc_object_t *object )
vlc_input_title_Delete(p_sys->pp_title[i]);
TAB_CLEAN( p_sys->i_title, p_sys->pp_title );
/* bd_close( NULL ) can crash */
assert(p_sys->bluray);
bd_close(p_sys->bluray);
free(p_sys);
}
/*****************************************************************************
* subpicture_updater_t functions:
*****************************************************************************/
static int subpictureUpdaterValidate( subpicture_t *p_subpic,
bool b_fmt_src, const video_format_t *p_fmt_src,
bool b_fmt_dst, const video_format_t *p_fmt_dst,
mtime_t i_ts )
{
VLC_UNUSED( b_fmt_src );
VLC_UNUSED( b_fmt_dst );
VLC_UNUSED( p_fmt_src );
VLC_UNUSED( p_fmt_dst );
VLC_UNUSED( i_ts );
subpicture_updater_sys_t *p_upd_sys = p_subpic->updater.p_sys;
bluray_overlay_t *p_overlay = p_upd_sys->p_overlay;
vlc_mutex_lock(&p_overlay->lock);
int res = p_overlay->status == Outdated;
vlc_mutex_unlock(&p_overlay->lock);
return res;
}
/* This should probably be moved to subpictures.c afterward */
static subpicture_region_t* subpicture_region_Clone(subpicture_region_t *p_region_src)
{
if (!p_region_src)
return NULL;
subpicture_region_t *p_region_dst = subpicture_region_New(&p_region_src->fmt);
if (unlikely(!p_region_dst))
return NULL;
p_region_dst->i_x = p_region_src->i_x;
p_region_dst->i_y = p_region_src->i_y;
p_region_dst->i_align = p_region_src->i_align;
p_region_dst->i_alpha = p_region_src->i_alpha;
p_region_dst->psz_text = p_region_src->psz_text ? strdup(p_region_src->psz_text) : NULL;
p_region_dst->psz_html = p_region_src->psz_html ? strdup(p_region_src->psz_html) : NULL;
if (p_region_src->p_style != NULL) {
p_region_dst->p_style = malloc(sizeof(*p_region_dst->p_style));
p_region_dst->p_style = text_style_Copy(p_region_dst->p_style,
p_region_src->p_style);
}
//Palette is already copied by subpicture_region_New, we just have to duplicate p_pixels
for (int i = 0; i < p_region_src->p_picture->i_planes; i++)
memcpy(p_region_dst->p_picture->p[i].p_pixels,
p_region_src->p_picture->p[i].p_pixels,
p_region_src->p_picture->p[i].i_lines * p_region_src->p_picture->p[i].i_pitch);
return p_region_dst;
}
static void subpictureUpdaterUpdate(subpicture_t *p_subpic,
const video_format_t *p_fmt_src,
const video_format_t *p_fmt_dst,
mtime_t i_ts)
{
VLC_UNUSED(p_fmt_src);
VLC_UNUSED(p_fmt_dst);
VLC_UNUSED(i_ts);
subpicture_updater_sys_t *p_upd_sys = p_subpic->updater.p_sys;
bluray_overlay_t *p_overlay = p_upd_sys->p_overlay;
/*
* When this function is called, all p_subpic regions are gone.
* We need to duplicate our regions (stored internaly) to this subpic.
*/
vlc_mutex_lock(&p_overlay->lock);
subpicture_region_t *p_src = p_overlay->p_regions;
if (!p_src)
return;
subpicture_region_t **p_dst = &(p_subpic->p_region);
while (p_src != NULL) {
*p_dst = subpicture_region_Clone(p_src);
if (*p_dst == NULL)
break ;
p_dst = &((*p_dst)->p_next);
p_src = p_src->p_next;
}
if (*p_dst != NULL)
(*p_dst)->p_next = NULL;
p_overlay->status = Displayed;
vlc_mutex_unlock(&p_overlay->lock);
}
static void subpictureUpdaterDestroy(subpicture_t *p_subpic)
{
vlc_gc_decref(p_subpic->updater.p_sys->p_overlay);
}
/*****************************************************************************
* libbluray overlay handling:
*****************************************************************************/
static void blurayCleanOverayStruct(gc_object_t *p_gc)
{
bluray_overlay_t *p_overlay = vlc_priv(p_gc, bluray_overlay_t);
/*
* This will be called when destroying the picture.
* Don't delete it again from here!
*/
vlc_mutex_destroy(&p_overlay->lock);
subpicture_region_Delete(p_overlay->p_regions);
free(p_overlay);
}
static void blurayCloseAllOverlays(demux_t *p_demux)
{
demux_sys_t *p_sys = p_demux->p_sys;
p_demux->p_sys->current_overlay = -1;
if (p_sys->p_vout != NULL) {
for (int i = 0; i < 0; i++) {
if (p_sys->p_overlays[i] != NULL) {
vout_FlushSubpictureChannel(p_sys->p_vout,
p_sys->p_overlays[i]->p_pic->i_channel);
vlc_gc_decref(p_sys->p_overlays[i]);
p_sys->p_overlays[i] = NULL;
}
}
vlc_object_release(p_sys->p_vout);
p_sys->p_vout = NULL;
}
}
/*
* Mark the overlay as "ToDisplay" status.
* This will not send the overlay to the vout instantly, as the vout
* may not be acquired (not acquirable) yet.
* If is has already been acquired, the overlay has already been sent to it,
* therefore, we only flag the overlay as "Outdated"
*/
static void blurayActivateOverlay(demux_t *p_demux, const BD_OVERLAY* const ov)
{
demux_sys_t *p_sys = p_demux->p_sys;
/*
* If the overlay is already displayed, mark the picture as outdated.
* We must NOT use vout_PutSubpicture if a picture is already displayed.
*/
vlc_mutex_lock(&p_sys->p_overlays[ov->plane]->lock);
if ((p_sys->p_overlays[ov->plane]->status == Displayed ||
p_sys->p_overlays[ov->plane]->status == Outdated)
&& p_sys->p_vout) {
p_sys->p_overlays[ov->plane]->status = Outdated;
vlc_mutex_unlock(&p_sys->p_overlays[ov->plane]->lock);
return ;
}
/*
* Mark the overlay as available, but don't display it right now.
* the blurayDemuxMenu will send it to vout, as it may be unavailable when
* the overlay is computed
*/
p_sys->current_overlay = ov->plane;
p_sys->p_overlays[ov->plane]->status = ToDisplay;
vlc_mutex_unlock(&p_sys->p_overlays[ov->plane]->lock);
}
static void blurayInitOverlay(demux_t *p_demux, const BD_OVERLAY* const ov)
{
demux_sys_t *p_sys = p_demux->p_sys;
assert(p_sys->p_overlays[ov->plane] == NULL);
p_sys->p_overlays[ov->plane] = calloc(1, sizeof(**p_sys->p_overlays));
if (unlikely(!p_sys->p_overlays[ov->plane]))
return;
subpicture_updater_sys_t *p_upd_sys = malloc(sizeof(*p_upd_sys));
if (unlikely(!p_upd_sys)) {
free(p_sys->p_overlays[ov->plane]);
p_sys->p_overlays[ov->plane] = NULL;
return;
}
vlc_gc_init(p_sys->p_overlays[ov->plane], blurayCleanOverayStruct);
/* Incrementing refcounter: vout + demux */
vlc_gc_incref(p_sys->p_overlays[ov->plane]);
p_upd_sys->p_overlay = p_sys->p_overlays[ov->plane];
subpicture_updater_t updater = {
.pf_validate = subpictureUpdaterValidate,
.pf_update = subpictureUpdaterUpdate,
.pf_destroy = subpictureUpdaterDestroy,
.p_sys = p_upd_sys,
};
p_sys->p_overlays[ov->plane]->p_pic = subpicture_New(&updater);
p_sys->p_overlays[ov->plane]->p_pic->i_original_picture_width = ov->w;
p_sys->p_overlays[ov->plane]->p_pic->i_original_picture_height = ov->h;
p_sys->p_overlays[ov->plane]->p_pic->b_ephemer = true;
p_sys->p_overlays[ov->plane]->p_pic->b_absolute = true;
}
/**
* Destroy every regions in the subpicture.
* This is done in two steps:
* - Wiping our private regions list
* - Flagging the overlay as outdated, so the changes are replicated from
* the subpicture_updater_t::pf_update
* This doesn't destroy the subpicture, as the overlay may be used again by libbluray.
*/
static void blurayClearOverlay(demux_t *p_demux, const BD_OVERLAY* const ov)
{
demux_sys_t *p_sys = p_demux->p_sys;
vlc_mutex_lock(&p_sys->p_overlays[ov->plane]->lock);
subpicture_region_ChainDelete(p_sys->p_overlays[ov->plane]->p_regions);
p_sys->p_overlays[ov->plane]->p_regions = NULL;
p_sys->p_overlays[ov->plane]->status = Outdated;
vlc_mutex_unlock(&p_sys->p_overlays[ov->plane]->lock);
}
/*
* This will draw to the overlay by adding a region to our region list
* This will have to be copied to the subpicture used to render the overlay.
*/
static void blurayDrawOverlay(demux_t *p_demux, const BD_OVERLAY* const ov)
{
demux_sys_t *p_sys = p_demux->p_sys;
/*
* Compute a subpicture_region_t.
* It will be copied and sent to the vout later.
*/
if (!ov->img)
return;
vlc_mutex_lock(&p_sys->p_overlays[ov->plane]->lock);
/* Find a region to update */
subpicture_region_t *p_reg = p_sys->p_overlays[ov->plane]->p_regions;
subpicture_region_t *p_last = NULL;
while (p_reg != NULL) {
p_last = p_reg;
if (p_reg->i_x == ov->x && p_reg->i_y == ov->y &&
p_reg->fmt.i_width == ov->w && p_reg->fmt.i_height == ov->h)
break;
p_reg = p_reg->p_next;
}
/* If there is no region to update, create a new one. */
if (!p_reg) {
video_format_t fmt;
video_format_Init(&fmt, 0);
video_format_Setup(&fmt, VLC_CODEC_YUVP, ov->w, ov->h, 1, 1);
p_reg = subpicture_region_New(&fmt);
p_reg->i_x = ov->x;
p_reg->i_y = ov->y;
/* Append it to our list. */
if (p_last != NULL)
p_last->p_next = p_reg;
else /* If we don't have a last region, then our list empty */
p_sys->p_overlays[ov->plane]->p_regions = p_reg;
}
/* Now we can update the region, regardless it's an update or an insert */
const BD_PG_RLE_ELEM *img = ov->img;
for (int y = 0; y < ov->h; y++) {
for (int x = 0; x < ov->w;) {
memset(p_reg->p_picture->p[0].p_pixels +
y * p_reg->p_picture->p[0].i_pitch + x,
img->color, img->len);
x += img->len;
img++;
}
}
if (ov->palette) {
p_reg->fmt.p_palette->i_entries = 256;
for (int i = 0; i < 256; ++i) {
p_reg->fmt.p_palette->palette[i][0] = ov->palette[i].Y;
p_reg->fmt.p_palette->palette[i][1] = ov->palette[i].Cb;
p_reg->fmt.p_palette->palette[i][2] = ov->palette[i].Cr;
p_reg->fmt.p_palette->palette[i][3] = ov->palette[i].T;
}
}
vlc_mutex_unlock(&p_sys->p_overlays[ov->plane]->lock);
/*
* /!\ The region is now stored in our internal list, but not in the subpicture /!\
*/
}
static void blurayOverlayProc(void *ptr, const BD_OVERLAY *const overlay)
{
demux_t *p_demux = (demux_t*)ptr;
if (!overlay) {
msg_Info(p_demux, "Closing overlay.");
blurayCloseAllOverlays(p_demux);
return;
}
switch (overlay->cmd) {
case BD_OVERLAY_INIT:
msg_Info(p_demux, "Initializing overlay");
blurayInitOverlay(p_demux, overlay);
break;
case BD_OVERLAY_CLEAR:
blurayClearOverlay(p_demux, overlay);
break;
case BD_OVERLAY_FLUSH:
blurayActivateOverlay(p_demux, overlay);
break;
case BD_OVERLAY_DRAW:
blurayDrawOverlay(p_demux, overlay);
break;
default:
msg_Warn(p_demux, "Unknown BD overlay command: %u", overlay->cmd);
break;
}
}
static void bluraySendOverlayToVout(demux_t *p_demux)
{
demux_sys_t *p_sys = p_demux->p_sys;
assert(p_sys->current_overlay >= 0 &&
p_sys->p_overlays[p_sys->current_overlay] != NULL &&
p_sys->p_overlays[p_sys->current_overlay]->p_pic != NULL);
p_sys->p_overlays[p_sys->current_overlay]->p_pic->i_start =
p_sys->p_overlays[p_sys->current_overlay]->p_pic->i_stop = mdate();
p_sys->p_overlays[p_sys->current_overlay]->p_pic->i_channel =
vout_RegisterSubpictureChannel(p_sys->p_vout);
/*
* After this point, the picture should not be accessed from the demux thread,
* as it's hold by the vout thread.
* This must be done only once per subpicture, ie. only once between each
* blurayInitOverlay & blurayCloseOverlay call.
*/
vout_PutSubpicture(p_sys->p_vout, p_sys->p_overlays[p_sys->current_overlay]->p_pic);
/*
* Mark the picture as Outdated, as it contains no region for now.
* This will make the subpicture_updater_t call pf_update
*/
p_sys->p_overlays[p_sys->current_overlay]->status = Outdated;
}
static int blurayInitTitles(demux_t *p_demux )
{
......@@ -566,6 +952,19 @@ static int blurayDemux(demux_t *p_demux)
block_Release(p_block);
return 1;
}
if (p_sys->current_overlay != -1)
{
vlc_mutex_lock(&p_sys->p_overlays[p_sys->current_overlay]->lock);
if (p_sys->p_overlays[p_sys->current_overlay]->status == ToDisplay) {
vlc_mutex_unlock(&p_sys->p_overlays[p_sys->current_overlay]->lock);
if (p_sys->p_vout == NULL)
p_sys->p_vout = input_GetVout(p_sys->p_input);
if (p_sys->p_vout != NULL) {
bluraySendOverlayToVout(p_demux);
}
} else
vlc_mutex_unlock(&p_sys->p_overlays[p_sys->current_overlay]->lock);
}
}
p_block->i_buffer = nread;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment