Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vlc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
videolan
vlc
Commits
8d92c1ea
Commit
8d92c1ea
authored
Dec 12, 2010
by
Laurent Aimar
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
No functionnal changes (spu).
Reorder the function declarations to avoid forward declarations.
parent
96b23172
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
1126 additions
and
1172 deletions
+1126
-1172
src/video_output/vout_subpictures.c
src/video_output/vout_subpictures.c
+1126
-1172
No files found.
src/video_output/vout_subpictures.c
View file @
8d92c1ea
...
@@ -65,12 +65,6 @@ typedef struct
...
@@ -65,12 +65,6 @@ typedef struct
}
spu_heap_t
;
}
spu_heap_t
;
static
void
SpuHeapInit
(
spu_heap_t
*
);
static
int
SpuHeapPush
(
spu_heap_t
*
,
subpicture_t
*
);
static
void
SpuHeapDeleteAt
(
spu_heap_t
*
,
int
i_index
);
static
int
SpuHeapDeleteSubpicture
(
spu_heap_t
*
,
subpicture_t
*
);
static
void
SpuHeapClean
(
spu_heap_t
*
p_heap
);
struct
spu_private_t
struct
spu_private_t
{
{
vlc_mutex_t
lock
;
/* lock to protect all followings fields */
vlc_mutex_t
lock
;
/* lock to protect all followings fields */
...
@@ -98,416 +92,526 @@ struct spu_private_t
...
@@ -98,416 +92,526 @@ struct spu_private_t
mtime_t
i_last_sort_date
;
mtime_t
i_last_sort_date
;
};
};
/* */
/* Subpicture rendered flag
typedef
struct
* FIXME ? it could be moved to private ? */
#define SUBPICTURE_RENDERED (0x1000)
#if SUBPICTURE_RENDERED < SUBPICTURE_ALIGN_MASK
# error SUBPICTURE_RENDERED too low
#endif
/*****************************************************************************
* heap managment
*****************************************************************************/
static
void
SpuHeapInit
(
spu_heap_t
*
p_heap
)
{
{
int
w
;
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
int
h
;
{
}
spu_scale_t
;
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i
];
static
spu_scale_t
spu_scale_create
(
int
w
,
int
h
);
static
spu_scale_t
spu_scale_unit
(
void
);
static
spu_scale_t
spu_scale_createq
(
int
wn
,
int
wd
,
int
hn
,
int
hd
);
static
int
spu_scale_w
(
int
v
,
const
spu_scale_t
s
);
static
int
spu_scale_h
(
int
v
,
const
spu_scale_t
s
);
static
int
spu_invscale_w
(
int
v
,
const
spu_scale_t
s
);
static
int
spu_invscale_h
(
int
v
,
const
spu_scale_t
s
);
typedef
struct
e
->
p_subpicture
=
NULL
;
e
->
b_reject
=
false
;
}
}
static
int
SpuHeapPush
(
spu_heap_t
*
p_heap
,
subpicture_t
*
p_subpic
)
{
{
int
i_x
;
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
int
i_y
;
{
int
i_width
;
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i
];
int
i_height
;
spu_scale_t
scale
;
if
(
e
->
p_subpicture
)
}
spu_area_t
;
continue
;
static
spu_area_t
spu_area_create
(
int
x
,
int
y
,
int
w
,
int
h
,
spu_scale_t
);
e
->
p_subpicture
=
p_subpic
;
static
spu_area_t
spu_area_scaled
(
spu_area_t
);
e
->
b_reject
=
false
;
static
spu_area_t
spu_area_unscaled
(
spu_area_t
,
spu_scale_t
);
return
VLC_SUCCESS
;
static
bool
spu_area_overlap
(
spu_area_t
,
spu_area_t
);
}
return
VLC_EGENERIC
;
}
static
void
SpuHeapDeleteAt
(
spu_heap_t
*
p_heap
,
int
i_index
)
{
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i_index
];
/* Subpicture rendered flag
if
(
e
->
p_subpicture
)
* FIXME ? it could be moved to private ? */
subpicture_Delete
(
e
->
p_subpicture
);
#define SUBPICTURE_RENDERED (0x1000)
#if SUBPICTURE_RENDERED < SUBPICTURE_ALIGN_MASK
# error SUBPICTURE_RENDERED too low
#endif
#define SCALE_UNIT (1000)
e
->
p_subpicture
=
NULL
;
}
static
void
SubpictureChain
(
subpicture_t
**
pp_head
,
subpicture_t
*
p_subpic
);
static
int
SpuHeapDeleteSubpicture
(
spu_heap_t
*
p_heap
,
subpicture_t
*
p_subpic
)
static
int
SubpictureCmp
(
const
void
*
s0
,
const
void
*
s1
);
{
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
{
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i
];
static
void
SpuRenderRegion
(
spu_t
*
,
if
(
e
->
p_subpicture
!=
p_subpic
)
subpicture_region_t
**
,
spu_area_t
*
,
continue
;
subpicture_t
*
,
subpicture_region_t
*
,
const
spu_scale_t
scale_size
,
SpuHeapDeleteAt
(
p_heap
,
i
);
const
video_format_t
*
p_fmt
,
return
VLC_SUCCESS
;
const
spu_area_t
*
p_subtitle_area
,
int
i_subtitle_area
,
}
mtime_t
render_date
);
return
VLC_EGENERIC
;
}
static
void
UpdateSPU
(
spu_t
*
,
vlc_object_t
*
);
static
void
SpuHeapClean
(
spu_heap_t
*
p_heap
)
static
int
CropCallback
(
vlc_object_t
*
,
char
const
*
,
{
vlc_value_t
,
vlc_value_t
,
void
*
);
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
{
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i
];
if
(
e
->
p_subpicture
)
subpicture_Delete
(
e
->
p_subpicture
);
}
}
/* Buffer allocation for SPU filter (blend, scale, ...) */
struct
filter_owner_sys_t
struct
filter_owner_sys_t
{
{
spu_t
*
p_spu
;
spu_t
*
p_spu
;
int
i_channel
;
int
i_channel
;
};
};
static
int
spu_get_attachments
(
filter_t
*
,
input_attachment_t
***
,
int
*
);
static
picture_t
*
spu_new_video_buffer
(
filter_t
*
);
static
void
spu_del_video_buffer
(
filter_t
*
,
picture_t
*
);
/* Buffer aloccation fir SUB filter */
static
void
FilterRelease
(
filter_t
*
p_filter
)
static
int
SubFilterAllocationInit
(
filter_t
*
,
void
*
);
{
static
void
SubFilterAllocationClean
(
filter_t
*
);
if
(
p_filter
->
p_module
)
module_unneed
(
p_filter
,
p_filter
->
p_module
);
if
(
p_filter
->
p_owner
)
free
(
p_filter
->
p_owner
);
/* */
vlc_object_release
(
p_filter
);
static
void
SpuRenderCreateAndLoadText
(
spu_t
*
);
}
static
void
SpuRenderCreateAndLoadScale
(
spu_t
*
);
static
void
FilterRelease
(
filter_t
*
p_filter
);
/*****************************************************************************
static
picture_t
*
spu_new_video_buffer
(
filter_t
*
p_filter
)
* Public API
{
*****************************************************************************/
const
video_format_t
*
p_fmt
=
&
p_filter
->
fmt_out
.
video
;
#undef spu_Create
VLC_UNUSED
(
p_filter
);
/**
return
picture_NewFromFormat
(
p_fmt
);
* Creates the subpicture unit
}
*
static
void
spu_del_video_buffer
(
filter_t
*
p_filter
,
picture_t
*
p_picture
)
* \param p_this the parent object which creates the subpicture unit
*/
spu_t
*
spu_Create
(
vlc_object_t
*
p_this
)
{
{
spu_t
*
p_spu
;
VLC_UNUSED
(
p_filter
);
spu_private_t
*
p_sys
;
picture_Release
(
p_picture
);
}
p_spu
=
vlc_custom_create
(
p_this
,
sizeof
(
spu_t
)
+
sizeof
(
spu_private_t
)
,
static
int
spu_get_attachments
(
filter_t
*
p_filter
,
VLC_OBJECT_GENERIC
,
"subpicture"
);
input_attachment_t
***
ppp_attachment
,
if
(
!
p_spu
)
int
*
pi_attachment
)
return
NULL
;
{
vlc_object_attach
(
p_spu
,
p_this
)
;
spu_t
*
p_spu
=
p_filter
->
p_owner
->
p_spu
;
/* Initialize spu fields */
int
i_ret
=
VLC_EGENERIC
;
p_spu
->
p
=
p_sys
=
(
spu_private_t
*
)
&
p_spu
[
1
];
if
(
p_spu
->
p
->
p_input
)
i_ret
=
input_Control
(
(
input_thread_t
*
)
p_spu
->
p
->
p_input
,
INPUT_GET_ATTACHMENTS
,
ppp_attachment
,
pi_attachment
);
return
i_ret
;
}
/* Initialize private fields */
static
void
SpuRenderCreateAndLoadText
(
spu_t
*
p_spu
)
vlc_mutex_init
(
&
p_sys
->
lock
);
{
filter_t
*
p_text
;
SpuHeapInit
(
&
p_sys
->
heap
);
assert
(
!
p_spu
->
p
->
p_text
);
p_sys
->
p_text
=
NULL
;
p_spu
->
p
->
p_text
=
p_sys
->
p_scale
=
NULL
;
p_text
=
vlc_custom_create
(
p_spu
,
sizeof
(
filter_t
),
p_sys
->
p_scale_yuvp
=
NULL
;
VLC_OBJECT_GENERIC
,
"spu text"
);
if
(
!
p_text
)
return
;
p_sys
->
i_margin
=
var_InheritInteger
(
p_spu
,
"sub-margin"
);
p_text
->
p_owner
=
xmalloc
(
sizeof
(
*
p_text
->
p_owner
)
);
p_text
->
p_owner
->
p_spu
=
p_spu
;
/* Register the default subpicture channel */
es_format_Init
(
&
p_text
->
fmt_in
,
VIDEO_ES
,
0
);
p_sys
->
i_channel
=
SPU_DEFAULT_CHANNEL
+
1
;
p_sys
->
psz_chain_update
=
NULL
;
es_format_Init
(
&
p_text
->
fmt_out
,
VIDEO_ES
,
0
);
vlc_mutex_init
(
&
p_sys
->
chain_lock
);
p_text
->
fmt_out
.
video
.
i_width
=
p_sys
->
p_chain
=
filter_chain_New
(
p_spu
,
"sub filter"
,
false
,
p_text
->
fmt_out
.
video
.
i_visible_width
=
32
;
SubFilterAllocationInit
,
p_text
->
fmt_out
.
video
.
i_height
=
SubFilterAllocationClean
,
p_text
->
fmt_out
.
video
.
i_visible_height
=
32
;
p_spu
);
/* Load text and scale module */
p_text
->
pf_get_attachments
=
spu_get_attachments
;
SpuRenderCreateAndLoadText
(
p_spu
);
SpuRenderCreateAndLoadScale
(
p_spu
);
/* */
vlc_object_attach
(
p_text
,
p_spu
);
p_sys
->
i_last_sort_date
=
-
1
;
return
p_spu
;
/* FIXME TOCHECK shouldn't module_need( , , psz_modulename, false ) do the
* same than these 2 calls ? */
char
*
psz_modulename
=
var_CreateGetString
(
p_spu
,
"text-renderer"
);
if
(
psz_modulename
&&
*
psz_modulename
)
{
p_text
->
p_module
=
module_need
(
p_text
,
"text renderer"
,
psz_modulename
,
true
);
}
free
(
psz_modulename
);
if
(
!
p_text
->
p_module
)
p_text
->
p_module
=
module_need
(
p_text
,
"text renderer"
,
NULL
,
false
);
/* Create a few variables used for enhanced text rendering */
var_Create
(
p_text
,
"spu-duration"
,
VLC_VAR_TIME
);
var_Create
(
p_text
,
"spu-elapsed"
,
VLC_VAR_TIME
);
var_Create
(
p_text
,
"text-rerender"
,
VLC_VAR_BOOL
);
var_Create
(
p_text
,
"scale"
,
VLC_VAR_INTEGER
);
}
}
/**
static
filter_t
*
CreateAndLoadScale
(
vlc_object_t
*
p_obj
,
* Destroy the subpicture unit
vlc_fourcc_t
i_src_chroma
,
vlc_fourcc_t
i_dst_chroma
,
*
bool
b_resize
)
* \param p_this the parent object which destroys the subpicture unit
*/
void
spu_Destroy
(
spu_t
*
p_spu
)
{
{
spu_private_t
*
p_sys
=
p_spu
->
p
;
filter_t
*
p_scale
;
if
(
p_sys
->
p_text
)
FilterRelease
(
p_sys
->
p_text
);
if
(
p_sys
->
p_scale_yuvp
)
p_scale
=
vlc_custom_create
(
p_obj
,
sizeof
(
filter_t
),
FilterRelease
(
p_sys
->
p_scale_yuvp
);
VLC_OBJECT_GENERIC
,
"scale"
);
if
(
!
p_scale
)
return
NULL
;
if
(
p_sys
->
p_scale
)
es_format_Init
(
&
p_scale
->
fmt_in
,
VIDEO_ES
,
0
);
FilterRelease
(
p_sys
->
p_scale
);
p_scale
->
fmt_in
.
video
.
i_chroma
=
i_src_chroma
;
p_scale
->
fmt_in
.
video
.
i_width
=
p_scale
->
fmt_in
.
video
.
i_height
=
32
;
filter_chain_Delete
(
p_sys
->
p_chain
);
es_format_Init
(
&
p_scale
->
fmt_out
,
VIDEO_ES
,
0
);
vlc_mutex_destroy
(
&
p_sys
->
chain_lock
);
p_scale
->
fmt_out
.
video
.
i_chroma
=
i_dst_chroma
;
free
(
p_sys
->
psz_chain_update
);
p_scale
->
fmt_out
.
video
.
i_width
=
p_scale
->
fmt_out
.
video
.
i_height
=
b_resize
?
16
:
32
;
/* Destroy all remaining subpictures */
p_scale
->
pf_video_buffer_new
=
spu_new_video_buffer
;
SpuHeapClean
(
&
p_sys
->
heap
)
;
p_scale
->
pf_video_buffer_del
=
spu_del_video_buffer
;
vlc_mutex_destroy
(
&
p_sys
->
lock
);
vlc_object_attach
(
p_scale
,
p_obj
);
p_scale
->
p_module
=
module_need
(
p_scale
,
"video filter2"
,
NULL
,
false
);
vlc_object_release
(
p_spu
);
return
p_scale
;
}
static
void
SpuRenderCreateAndLoadScale
(
spu_t
*
p_spu
)
{
assert
(
!
p_spu
->
p
->
p_scale
);
assert
(
!
p_spu
->
p
->
p_scale_yuvp
);
/* XXX p_spu->p_scale is used for all conversion/scaling except yuvp to
* yuva/rgba */
p_spu
->
p
->
p_scale
=
CreateAndLoadScale
(
VLC_OBJECT
(
p_spu
),
VLC_CODEC_YUVA
,
VLC_CODEC_YUVA
,
true
);
/* This one is used for YUVP to YUVA/RGBA without scaling
* FIXME rename it */
p_spu
->
p
->
p_scale_yuvp
=
CreateAndLoadScale
(
VLC_OBJECT
(
p_spu
),
VLC_CODEC_YUVP
,
VLC_CODEC_YUVA
,
false
);
}
}
/**
static
void
SpuRenderText
(
spu_t
*
p_spu
,
bool
*
pb_rerender_text
,
* Attach/Detach the SPU from any input
subpicture_t
*
p_subpic
,
subpicture_region_t
*
p_region
,
*
int
i_min_scale_ratio
,
mtime_t
render_date
)
* \param p_this the object in which to destroy the subpicture unit
* \param b_attach to select attach or detach
*/
void
spu_Attach
(
spu_t
*
p_spu
,
vlc_object_t
*
p_input
,
bool
b_attach
)
{
{
if
(
b_attach
)
filter_t
*
p_text
=
p_spu
->
p
->
p_text
;
{
UpdateSPU
(
p_spu
,
p_input
);
var_Create
(
p_input
,
"highlight"
,
VLC_VAR_BOOL
);
var_AddCallback
(
p_input
,
"highlight"
,
CropCallback
,
p_spu
);
vlc_mutex_lock
(
&
p_spu
->
p
->
lock
);
assert
(
p_region
->
fmt
.
i_chroma
==
VLC_CODEC_TEXT
);
p_spu
->
p
->
p_input
=
p_input
;
FilterRelease
(
p_spu
->
p
->
p_text
);
if
(
!
p_text
||
!
p_text
->
p_module
)
p_spu
->
p
->
p_text
=
NULL
;
goto
exit
;
SpuRenderCreateAndLoadText
(
p_spu
);
vlc_mutex_unlock
(
&
p_spu
->
p
->
lock
);
/* Setup 3 variables which can be used to render
* time-dependent text (and effects). The first indicates
* the total amount of time the text will be on screen,
* the second the amount of time it has already been on
* screen (can be a negative value as text is layed out
* before it is rendered) and the third is a feedback
* variable from the renderer - if the renderer sets it
* then this particular text is time-dependent, eg. the
* visual progress bar inside the text in karaoke and the
* text needs to be rendered multiple times in order for
* the effect to work - we therefore need to return the
* region to its original state at the end of the loop,
* instead of leaving it in YUVA or YUVP.
* Any renderer which is unaware of how to render
* time-dependent text can happily ignore the variables
* and render the text the same as usual - it should at
* least show up on screen, but the effect won't change
* the text over time.
*/
var_SetTime
(
p_text
,
"spu-duration"
,
p_subpic
->
i_stop
-
p_subpic
->
i_start
);
var_SetTime
(
p_text
,
"spu-elapsed"
,
render_date
);
var_SetBool
(
p_text
,
"text-rerender"
,
false
);
var_SetInteger
(
p_text
,
"scale"
,
i_min_scale_ratio
);
if
(
p_text
->
pf_render_html
&&
p_region
->
psz_html
)
{
p_text
->
pf_render_html
(
p_text
,
p_region
,
p_region
);
}
}
else
else
if
(
p_text
->
pf_render_text
)
{
{
vlc_mutex_lock
(
&
p_spu
->
p
->
lock
);
p_text
->
pf_render_text
(
p_text
,
p_region
,
p_region
);
p_spu
->
p
->
p_input
=
NULL
;
vlc_mutex_unlock
(
&
p_spu
->
p
->
lock
);
/* Delete callbacks */
var_DelCallback
(
p_input
,
"highlight"
,
CropCallback
,
p_spu
);
var_Destroy
(
p_input
,
"highlight"
);
}
}
*
pb_rerender_text
=
var_GetBool
(
p_text
,
"text-rerender"
);
exit:
p_region
->
i_align
|=
SUBPICTURE_RENDERED
;
}
}
/**
/**
*
Inform the SPU filters of mouse event
*
A few scale functions helpers.
*/
*/
int
spu_ProcessMouse
(
spu_t
*
p_spu
,
const
vlc_mouse_t
*
p_mouse
,
const
video_format_t
*
p_fmt
)
{
spu_private_t
*
p_sys
=
p_spu
->
p
;
vlc_mutex_lock
(
&
p_sys
->
chain_lock
);
#define SCALE_UNIT (1000)
filter_chain_MouseEvent
(
p_sys
->
p_chain
,
p_mouse
,
p_fmt
);
typedef
struct
vlc_mutex_unlock
(
&
p_sys
->
chain_lock
);
{
int
w
;
int
h
;
}
spu_scale_t
;
return
VLC_SUCCESS
;
static
spu_scale_t
spu_scale_create
(
int
w
,
int
h
)
{
spu_scale_t
s
=
{
.
w
=
w
,
.
h
=
h
};
if
(
s
.
w
<=
0
)
s
.
w
=
SCALE_UNIT
;
if
(
s
.
h
<=
0
)
s
.
h
=
SCALE_UNIT
;
return
s
;
}
static
spu_scale_t
spu_scale_unit
(
void
)
{
return
spu_scale_create
(
SCALE_UNIT
,
SCALE_UNIT
);
}
static
spu_scale_t
spu_scale_createq
(
int
wn
,
int
wd
,
int
hn
,
int
hd
)
{
return
spu_scale_create
(
wn
*
SCALE_UNIT
/
wd
,
hn
*
SCALE_UNIT
/
hd
);
}
static
int
spu_scale_w
(
int
v
,
const
spu_scale_t
s
)
{
return
v
*
s
.
w
/
SCALE_UNIT
;
}
static
int
spu_scale_h
(
int
v
,
const
spu_scale_t
s
)
{
return
v
*
s
.
h
/
SCALE_UNIT
;
}
static
int
spu_invscale_w
(
int
v
,
const
spu_scale_t
s
)
{
return
v
*
SCALE_UNIT
/
s
.
w
;
}
static
int
spu_invscale_h
(
int
v
,
const
spu_scale_t
s
)
{
return
v
*
SCALE_UNIT
/
s
.
h
;
}
}
/**
/**
* Display a subpicture
* A few area functions helpers
*
* Remove the reservation flag of a subpicture, which will cause it to be
* ready for display.
* \param p_spu the subpicture unit object
* \param p_subpic the subpicture to display
*/
*/
void
spu_PutSubpicture
(
spu_t
*
p_spu
,
subpicture_t
*
p_subpic
)
typedef
struct
{
{
spu_private_t
*
p_sys
=
p_spu
->
p
;
int
i_x
;
int
i_y
;
/* SPU_DEFAULT_CHANNEL always reset itself */
int
i_width
;
if
(
p_subpic
->
i_channel
==
SPU_DEFAULT_CHANNEL
)
int
i_height
;
spu_ClearChannel
(
p_spu
,
SPU_DEFAULT_CHANNEL
);
/* p_private is for spu only and cannot be non NULL here */
spu_scale_t
scale
;
for
(
subpicture_region_t
*
r
=
p_subpic
->
p_region
;
r
!=
NULL
;
r
=
r
->
p_next
)
}
spu_area_t
;
assert
(
r
->
p_private
==
NULL
);
/* */
static
spu_area_t
spu_area_create
(
int
x
,
int
y
,
int
w
,
int
h
,
spu_scale_t
s
)
vlc_mutex_lock
(
&
p_sys
->
lock
);
{
if
(
SpuHeapPush
(
&
p_sys
->
heap
,
p_subpic
)
)
spu_area_t
a
=
{
.
i_x
=
x
,
.
i_y
=
y
,
.
i_width
=
w
,
.
i_height
=
h
,
.
scale
=
s
};
{
return
a
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
msg_Err
(
p_spu
,
"subpicture heap full"
);
subpicture_Delete
(
p_subpic
);
return
;
}
vlc_mutex_unlock
(
&
p_sys
->
lock
);
}
}
static
spu_area_t
spu_area_scaled
(
spu_area_t
a
)
/**
* This function renders all sub picture units in the list.
*/
static
subpicture_t
*
SpuRenderSubpictures
(
spu_t
*
p_spu
,
const
video_format_t
*
p_fmt_dst
,
subpicture_t
*
p_subpic_list
,
const
video_format_t
*
p_fmt_src
,
mtime_t
render_subtitle_date
,
mtime_t
render_osd_date
)
{
{
spu_private_t
*
p_sys
=
p_spu
->
p
;
if
(
a
.
scale
.
w
==
SCALE_UNIT
&&
a
.
scale
.
h
==
SCALE_UNIT
)
return
a
;
const
int
i_source_video_width
=
p_fmt_src
->
i_width
;
a
.
i_x
=
spu_scale_w
(
a
.
i_x
,
a
.
scale
)
;
const
int
i_source_video_height
=
p_fmt_src
->
i_height
;
a
.
i_y
=
spu_scale_h
(
a
.
i_y
,
a
.
scale
)
;
unsigned
int
i_subpicture
;
a
.
i_width
=
spu_scale_w
(
a
.
i_width
,
a
.
scale
)
;
subpicture_t
*
pp_subpicture
[
VOUT_MAX_SUBPICTURES
]
;
a
.
i_height
=
spu_scale_h
(
a
.
i_height
,
a
.
scale
)
;
unsigned
int
i_subtitle_region_count
;
a
.
scale
=
spu_scale_unit
();
spu_area_t
p_subtitle_area_buffer
[
VOUT_MAX_SUBPICTURES
];
return
a
;
spu_area_t
*
p_subtitle_area
;
}
int
i_subtitle_area
;
static
spu_area_t
spu_area_unscaled
(
spu_area_t
a
,
spu_scale_t
s
)
{
if
(
a
.
scale
.
w
==
s
.
w
&&
a
.
scale
.
h
==
s
.
h
)
return
a
;
/* Preprocess subpictures */
a
=
spu_area_scaled
(
a
);
i_subpicture
=
0
;
i_subtitle_region_count
=
0
;
for
(
subpicture_t
*
p_subpic
=
p_subpic_list
;
p_subpic
!=
NULL
;
p_subpic
=
p_subpic
->
p_next
)
{
subpicture_Update
(
p_subpic
,
p_fmt_src
,
p_fmt_dst
,
p_subpic
->
b_subtitle
?
render_subtitle_date
:
render_osd_date
);
/* */
a
.
i_x
=
spu_invscale_w
(
a
.
i_x
,
s
);
if
(
p_subpic
->
b_subtitle
)
a
.
i_y
=
spu_invscale_h
(
a
.
i_y
,
s
);
{
for
(
subpicture_region_t
*
r
=
p_subpic
->
p_region
;
r
!=
NULL
;
r
=
r
->
p_next
)
i_subtitle_region_count
++
;
}
/* */
a
.
i_width
=
spu_invscale_w
(
a
.
i_width
,
s
);
pp_subpicture
[
i_subpicture
++
]
=
p_subpic
;
a
.
i_height
=
spu_invscale_h
(
a
.
i_height
,
s
);
}
/* Be sure we have at least 1 picture to process */
a
.
scale
=
s
;
if
(
i_subpicture
<=
0
)
return
a
;
return
NULL
;
}
static
bool
spu_area_overlap
(
spu_area_t
a
,
spu_area_t
b
)
{
const
int
i_dx
=
0
;
const
int
i_dy
=
0
;
subpicture_t
*
p_output
=
subpicture_New
(
NULL
);
a
=
spu_area_scaled
(
a
);
b
=
spu_area_scaled
(
b
);
/* Now order subpicture array
return
__MAX
(
a
.
i_x
-
i_dx
,
b
.
i_x
)
<
__MIN
(
a
.
i_x
+
a
.
i_width
+
i_dx
,
b
.
i_x
+
b
.
i_width
)
&&
* XXX The order is *really* important for overlap subtitles positionning */
__MAX
(
a
.
i_y
-
i_dy
,
b
.
i_y
)
<
__MIN
(
a
.
i_y
+
a
.
i_height
+
i_dy
,
b
.
i_y
+
b
.
i_height
);
qsort
(
pp_subpicture
,
i_subpicture
,
sizeof
(
*
pp_subpicture
),
SubpictureCmp
);
}
/* Allocate area array for subtitle overlap */
/**
i_subtitle_area
=
0
;
* Avoid area overlapping
p_subtitle_area
=
p_subtitle_area_buffer
;
*/
if
(
i_subtitle_region_count
>
sizeof
(
p_subtitle_area_buffer
)
/
sizeof
(
*
p_subtitle_area_buffer
)
)
static
void
SpuAreaFixOverlap
(
spu_area_t
*
p_dst
,
p_subtitle_area
=
calloc
(
i_subtitle_region_count
,
sizeof
(
*
p_subtitle_area
)
);
const
spu_area_t
*
p_sub
,
int
i_sub
,
int
i_align
)
{
spu_area_t
a
=
spu_area_scaled
(
*
p_dst
);
bool
b_moved
=
false
;
bool
b_ok
;
/* Process all subpictures and regions (in the right order) */
/* Check for overlap
for
(
unsigned
int
i_index
=
0
;
i_index
<
i_subpicture
;
i_index
++
)
* XXX It is not fast O(n^2) but we should not have a lot of region */
do
{
{
subpicture_t
*
p_subpic
=
pp_subpicture
[
i_index
];
b_ok
=
true
;
subpicture_region_t
*
p_region
;
for
(
int
i
=
0
;
i
<
i_sub
;
i
++
)
if
(
!
p_subpic
->
p_region
)
continue
;
/* FIXME when possible use a better rendering size than source size
* (max of display size and source size for example) FIXME */
int
i_render_width
=
p_subpic
->
i_original_picture_width
;
int
i_render_height
=
p_subpic
->
i_original_picture_height
;
if
(
!
i_render_width
||
!
i_render_height
)
{
{
if
(
i_render_width
!=
0
||
i_render_height
!=
0
)
spu_area_t
sub
=
spu_area_scaled
(
p_sub
[
i
]
);
msg_Err
(
p_spu
,
"unsupported original picture size %dx%d"
,
i_render_width
,
i_render_height
);
p_subpic
->
i_original_picture_width
=
i_render_width
=
i_source_video_width
;
if
(
!
spu_area_overlap
(
a
,
sub
)
)
p_subpic
->
i_original_picture_height
=
i_render_height
=
i_source_video_height
;
continue
;
}
if
(
p_sys
->
p_text
)
if
(
i_align
&
SUBPICTURE_ALIGN_TOP
)
{
{
p_sys
->
p_text
->
fmt_out
.
video
.
i_width
=
/* We go down */
p_sys
->
p_text
->
fmt_out
.
video
.
i_visible_width
=
i_render_width
;
int
i_y
=
sub
.
i_y
+
sub
.
i_height
;
a
.
i_y
=
i_y
;
b_moved
=
true
;
}
else
if
(
i_align
&
SUBPICTURE_ALIGN_BOTTOM
)
{
/* We go up */
int
i_y
=
sub
.
i_y
-
a
.
i_height
;
a
.
i_y
=
i_y
;
b_moved
=
true
;
}
else
{
/* TODO what to do in this case? */
//fprintf( stderr, "Overlap with unsupported alignment\n" );
break
;
}
p_sys
->
p_text
->
fmt_out
.
video
.
i_height
=
b_ok
=
false
;
p_sys
->
p_text
->
fmt_out
.
video
.
i_visible_height
=
i_render_height
;
break
;
}
}
}
while
(
!
b_ok
);
/* Compute scaling from picture to source size */
if
(
b_moved
)
spu_scale_t
scale
=
spu_scale_createq
(
i_source_video_width
,
i_render_width
,
*
p_dst
=
spu_area_unscaled
(
a
,
p_dst
->
scale
);
i_source_video_height
,
i_render_height
);
}
/* Update scaling from source size to display size(p_fmt_dst) */
scale
.
w
=
scale
.
w
*
p_fmt_dst
->
i_width
/
i_source_video_width
;
scale
.
h
=
scale
.
h
*
p_fmt_dst
->
i_height
/
i_source_video_height
;
/* Set default subpicture aspect ratio
static
void
SpuAreaFitInside
(
spu_area_t
*
p_area
,
const
spu_area_t
*
p_boundary
)
* FIXME if we only handle 1 aspect ratio per picture, why is it set per
{
* region ? */
spu_area_t
a
=
spu_area_scaled
(
*
p_area
);
p_region
=
p_subpic
->
p_region
;
if
(
!
p_region
->
fmt
.
i_sar_num
||
!
p_region
->
fmt
.
i_sar_den
)
{
p_region
->
fmt
.
i_sar_den
=
p_fmt_dst
->
i_sar_den
;
p_region
->
fmt
.
i_sar_num
=
p_fmt_dst
->
i_sar_num
;
}
/* Take care of the aspect ratio */
const
int
i_error_x
=
(
a
.
i_x
+
a
.
i_width
)
-
p_boundary
->
i_width
;
if
(
p_region
->
fmt
.
i_sar_num
*
p_fmt_dst
->
i_sar_den
!=
if
(
i_error_x
>
0
)
p_region
->
fmt
.
i_sar_den
*
p_fmt_dst
->
i_sar_num
)
a
.
i_x
-=
i_error_x
;
{
if
(
a
.
i_x
<
0
)
/* FIXME FIXME what about region->i_x/i_y ? */
a
.
i_x
=
0
;
scale
.
w
=
scale
.
w
*
(
int64_t
)
p_region
->
fmt
.
i_sar_num
*
p_fmt_dst
->
i_sar_den
/
p_region
->
fmt
.
i_sar_den
/
p_fmt_dst
->
i_sar_num
;
}
/* Render all regions
const
int
i_error_y
=
(
a
.
i_y
+
a
.
i_height
)
-
p_boundary
->
i_height
;
* We always transform non absolute subtitle into absolute one on the
if
(
i_error_y
>
0
)
* first rendering to allow good subtitle overlap support.
a
.
i_y
-=
i_error_y
;
*/
if
(
a
.
i_y
<
0
)
for
(
p_region
=
p_subpic
->
p_region
;
p_region
!=
NULL
;
p_region
=
p_region
->
p_next
)
a
.
i_y
=
0
;
{
spu_area_t
area
;
/* Check scale validity */
*
p_area
=
spu_area_unscaled
(
a
,
p_area
->
scale
);
if
(
scale
.
w
<=
0
||
scale
.
h
<=
0
)
}
continue
;
/* */
/**
subpicture_region_t
*
p_render
;
* Place a region
SpuRenderRegion
(
p_spu
,
&
p_render
,
&
area
,
*/
p_subpic
,
p_region
,
scale
,
p_fmt_dst
,
static
void
SpuRegionPlace
(
int
*
pi_x
,
int
*
pi_y
,
p_subtitle_area
,
i_subtitle_area
,
const
subpicture_t
*
p_subpic
,
p_subpic
->
b_subtitle
?
render_subtitle_date
:
render_osd_date
);
const
subpicture_region_t
*
p_region
)
if
(
p_render
)
{
{
const
int
i_delta_x
=
p_region
->
i_x
;
subpicture_region_t
**
pp_last
=
&
p_output
->
p_region
;
const
int
i_delta_y
=
p_region
->
i_y
;
while
(
*
pp_last
)
int
i_x
,
i_y
;
pp_last
=
&
(
*
pp_last
)
->
p_next
;
*
pp_last
=
p_render
;
}
if
(
p_subpic
->
b_subtitle
)
assert
(
p_region
->
i_x
!=
INT_MAX
&&
p_region
->
i_y
!=
INT_MAX
);
{
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_TOP
)
area
=
spu_area_unscaled
(
area
,
scale
);
{
if
(
!
p_subpic
->
b_absolute
&&
area
.
i_width
>
0
&&
area
.
i_height
>
0
)
i_y
=
i_delta_y
;
{
}
p_region
->
i_x
=
area
.
i_x
;
else
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_BOTTOM
)
p_region
->
i_y
=
area
.
i_y
;
{
}
i_y
=
p_subpic
->
i_original_picture_height
-
p_region
->
fmt
.
i_height
-
i_delta_y
;
if
(
p_subtitle_area
)
}
p_subtitle_area
[
i_subtitle_area
++
]
=
area
;
else
}
{
}
i_y
=
p_subpic
->
i_original_picture_height
/
2
-
p_region
->
fmt
.
i_height
/
2
;
if
(
p_subpic
->
b_subtitle
)
p_subpic
->
b_absolute
=
true
;
}
}
/* */
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_LEFT
)
if
(
p_subtitle_area
!=
p_subtitle_area_buffer
)
{
free
(
p_subtitle_area
);
i_x
=
i_delta_x
;
}
else
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_RIGHT
)
{
i_x
=
p_subpic
->
i_original_picture_width
-
p_region
->
fmt
.
i_width
-
i_delta_x
;
}
else
{
i_x
=
p_subpic
->
i_original_picture_width
/
2
-
p_region
->
fmt
.
i_width
/
2
;
}
return
p_output
;
if
(
p_subpic
->
b_absolute
)
{
i_x
=
i_delta_x
;
i_y
=
i_delta_y
;
}
*
pi_x
=
i_x
;
*
pi_y
=
i_y
;
}
/**
* This function compares two 64 bits integers.
* It can be used by qsort.
*/
static
int
IntegerCmp
(
int64_t
i0
,
int64_t
i1
)
{
return
i0
<
i1
?
-
1
:
i0
>
i1
?
1
:
0
;
}
/**
* This function compares 2 subpictures using the following properties
* (ordered by priority)
* 1. absolute positionning
* 2. start time
* 3. creation order (per channel)
*
* It can be used by qsort.
*
* XXX spu_RenderSubpictures depends heavily on this order.
*/
static
int
SubpictureCmp
(
const
void
*
s0
,
const
void
*
s1
)
{
subpicture_t
*
p_subpic0
=
*
(
subpicture_t
**
)
s0
;
subpicture_t
*
p_subpic1
=
*
(
subpicture_t
**
)
s1
;
int
r
;
r
=
IntegerCmp
(
!
p_subpic0
->
b_absolute
,
!
p_subpic1
->
b_absolute
);
if
(
!
r
)
r
=
IntegerCmp
(
p_subpic0
->
i_start
,
p_subpic1
->
i_start
);
if
(
!
r
)
r
=
IntegerCmp
(
p_subpic0
->
i_channel
,
p_subpic1
->
i_channel
);
if
(
!
r
)
r
=
IntegerCmp
(
p_subpic0
->
i_order
,
p_subpic1
->
i_order
);
return
r
;
}
static
void
SubpictureChain
(
subpicture_t
**
pp_head
,
subpicture_t
*
p_subpic
)
{
p_subpic
->
p_next
=
*
pp_head
;
*
pp_head
=
p_subpic
;
}
}
/*****************************************************************************
/*****************************************************************************
...
@@ -656,1037 +760,887 @@ static subpicture_t *SpuSortSubpictures( spu_t *p_spu,
...
@@ -656,1037 +760,887 @@ static subpicture_t *SpuSortSubpictures( spu_t *p_spu,
return
p_subpic
;
return
p_subpic
;
}
}
subpicture_t
*
spu_Render
(
spu_t
*
p_spu
,
const
video_format_t
*
p_fmt_dst
,
const
video_format_t
*
p_fmt_src
,
/**
mtime_t
render_subtitle_date
,
* It will transform the provided region into another region suitable for rendering.
mtime_t
render_osd_date
,
*/
bool
b_subtitle_only
)
static
void
SpuRenderRegion
(
spu_t
*
p_spu
,
subpicture_region_t
**
pp_dst
,
spu_area_t
*
p_dst_area
,
subpicture_t
*
p_subpic
,
subpicture_region_t
*
p_region
,
const
spu_scale_t
scale_size
,
const
video_format_t
*
p_fmt
,
const
spu_area_t
*
p_subtitle_area
,
int
i_subtitle_area
,
mtime_t
render_date
)
{
{
spu_private_t
*
p_sys
=
p_spu
->
p
;
spu_private_t
*
p_sys
=
p_spu
->
p
;
/* Update sub-filter chain */
video_format_t
fmt_original
=
p_region
->
fmt
;
vlc_mutex_lock
(
&
p_sys
->
lock
)
;
bool
b_rerender_text
=
false
;
char
*
psz_chain_update
=
p_sys
->
psz_chain_updat
e
;
bool
b_restore_format
=
fals
e
;
p_sys
->
psz_chain_update
=
NULL
;
int
i_x_offset
;
vlc_mutex_unlock
(
&
p_sys
->
lock
)
;
int
i_y_offset
;
vlc_mutex_lock
(
&
p_sys
->
chain_lock
);
video_format_t
region_fmt
;
if
(
psz_chain_update
)
picture_t
*
p_region_picture
;
{
filter_chain_Reset
(
p_sys
->
p_chain
,
NULL
,
NULL
);
filter_chain_AppendFromString
(
p_spu
->
p
->
p_chain
,
psz_chain_update
);
/* Invalidate area by default */
*
p_dst_area
=
spu_area_create
(
0
,
0
,
0
,
0
,
scale_size
);
*
pp_dst
=
NULL
;
free
(
psz_chain_update
);
/* Render text region */
if
(
p_region
->
fmt
.
i_chroma
==
VLC_CODEC_TEXT
)
{
const
int
i_min_scale_ratio
=
SCALE_UNIT
;
/* FIXME what is the right value? (scale_size is not) */
SpuRenderText
(
p_spu
,
&
b_rerender_text
,
p_subpic
,
p_region
,
i_min_scale_ratio
,
render_date
);
b_restore_format
=
b_rerender_text
;
/* Check if the rendering has failed ... */
if
(
p_region
->
fmt
.
i_chroma
==
VLC_CODEC_TEXT
)
goto
exit
;
}
}
/* Run subpicture filters */
filter_chain_SubFilter
(
p_sys
->
p_chain
,
render_osd_date
);
vlc_mutex_unlock
(
&
p_sys
->
chain_lock
);
/* Get the sorted list of subpicture to render */
/* Force palette if requested
vlc_mutex_lock
(
&
p_sys
->
lock
);
* FIXME b_force_palette and b_force_crop are applied to all subpictures using palette
* instead of only the right one (being the dvd spu).
*/
const
bool
b_using_palette
=
p_region
->
fmt
.
i_chroma
==
VLC_CODEC_YUVP
;
const
bool
b_force_palette
=
b_using_palette
&&
p_sys
->
b_force_palette
;
const
bool
b_force_crop
=
b_force_palette
&&
p_sys
->
b_force_crop
;
bool
b_changed_palette
=
false
;
subpicture_t
*
p_list
=
SpuSortSubpictures
(
p_spu
,
render_subtitle_date
,
/* Compute the margin which is expressed in destination pixel unit
render_osd_date
,
* The margin is applied only to subtitle and when no forced crop is
b_subtitle_only
);
* requested (dvd menu) */
if
(
!
p_list
)
int
i_margin_y
=
0
;
if
(
!
b_force_crop
&&
p_subpic
->
b_subtitle
)
i_margin_y
=
spu_invscale_h
(
p_sys
->
i_margin
,
scale_size
);
/* Place the picture
* We compute the position in the rendered size */
SpuRegionPlace
(
&
i_x_offset
,
&
i_y_offset
,
p_subpic
,
p_region
);
/* Save this position for subtitle overlap support
* it is really important that there are given without scale_size applied */
*
p_dst_area
=
spu_area_create
(
i_x_offset
,
i_y_offset
,
p_region
->
fmt
.
i_width
,
p_region
->
fmt
.
i_height
,
scale_size
);
/* Handle overlapping subtitles when possible */
if
(
p_subpic
->
b_subtitle
&&
!
p_subpic
->
b_absolute
)
{
{
vlc_mutex_unlock
(
&
p_sys
->
lock
);
SpuAreaFixOverlap
(
p_dst_area
,
p_subtitle_area
,
i_subtitle_area
,
return
NULL
;
p_region
->
i_align
)
;
}
}
/* Render the current list of subpictures */
/* we copy the area: for the subtitle overlap support we want
subpicture_t
*
p_render
=
SpuRenderSubpictures
(
p_spu
,
* to only save the area without margin applied */
p_fmt_dst
,
spu_area_t
restrained
=
*
p_dst_area
;
p_list
,
p_fmt_src
,
render_subtitle_date
,
render_osd_date
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
p_render
;
/* apply margin to subtitles and correct if they go over the picture edge */
}
if
(
p_subpic
->
b_subtitle
)
restrained
.
i_y
-=
i_margin_y
;
void
spu_OffsetSubtitleDate
(
spu_t
*
p_spu
,
mtime_t
i_duration
)
spu_area_t
display
=
spu_area_create
(
0
,
0
,
p_fmt
->
i_width
,
p_fmt
->
i_height
,
{
spu_scale_unit
()
);
spu_private_t
*
p_sys
=
p_spu
->
p
;
SpuAreaFitInside
(
&
restrained
,
&
display
)
;
vlc_mutex_lock
(
&
p_sys
->
lock
);
/* Fix the position for the current scale_size */
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
i_x_offset
=
spu_scale_w
(
restrained
.
i_x
,
restrained
.
scale
);
i_y_offset
=
spu_scale_h
(
restrained
.
i_y
,
restrained
.
scale
);
/* */
if
(
b_force_palette
)
{
{
spu_heap_entry_t
*
p_entry
=
&
p_sys
->
heap
.
p_entry
[
i
]
;
video_palette_t
*
p_palette
=
p_region
->
fmt
.
p_palette
;
subpicture_t
*
p_current
=
p_entry
->
p_subpictur
e
;
video_palette_t
palett
e
;
if
(
p_current
&&
p_current
->
b_subtitle
)
/* We suppose DVD palette here */
palette
.
i_entries
=
4
;
for
(
int
i
=
0
;
i
<
4
;
i
++
)
for
(
int
j
=
0
;
j
<
4
;
j
++
)
palette
.
palette
[
i
][
j
]
=
p_sys
->
palette
[
i
][
j
];
if
(
p_palette
->
i_entries
==
palette
.
i_entries
)
{
{
if
(
p_current
->
i_start
>
0
)
for
(
int
i
=
0
;
i
<
p_palette
->
i_entries
;
i
++
)
p_current
->
i_start
+=
i_duration
;
for
(
int
j
=
0
;
j
<
4
;
j
++
)
if
(
p_current
->
i_stop
>
0
)
b_changed_palette
|=
p_palette
->
palette
[
i
][
j
]
!=
palette
.
palette
[
i
][
j
];
p_current
->
i_stop
+=
i_duration
;
}
else
{
b_changed_palette
=
true
;
}
}
*
p_palette
=
palette
;
}
}
vlc_mutex_unlock
(
&
p_sys
->
lock
);
}
int
spu_RegisterChannel
(
spu_t
*
p_spu
)
/* */
{
region_fmt
=
p_region
->
fmt
;
spu_private_t
*
p_sys
=
p_spu
->
p
;
p_region_picture
=
p_region
->
p_picture
;
vlc_mutex_lock
(
&
p_sys
->
lock
);
int
i_channel
=
p_sys
->
i_channel
++
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
i_channel
;
/* Scale from rendered size to destination size */
}
if
(
p_sys
->
p_scale
&&
p_sys
->
p_scale
->
p_module
&&
(
!
b_using_palette
||
(
p_sys
->
p_scale_yuvp
&&
p_sys
->
p_scale_yuvp
->
p_module
)
)
&&
(
scale_size
.
w
!=
SCALE_UNIT
||
scale_size
.
h
!=
SCALE_UNIT
||
b_using_palette
)
)
{
const
unsigned
i_dst_width
=
spu_scale_w
(
p_region
->
fmt
.
i_width
,
scale_size
);
const
unsigned
i_dst_height
=
spu_scale_h
(
p_region
->
fmt
.
i_height
,
scale_size
);
void
spu_ClearChannel
(
spu_t
*
p_spu
,
int
i_channel
)
/* Destroy the cache if unusable */
{
if
(
p_region
->
p_private
)
spu_private_t
*
p_sys
=
p_spu
->
p
;
{
subpicture_region_private_t
*
p_private
=
p_region
->
p_private
;
bool
b_changed
=
false
;
vlc_mutex_lock
(
&
p_sys
->
lock
);
/* Check resize changes */
if
(
i_dst_width
!=
p_private
->
fmt
.
i_width
||
i_dst_height
!=
p_private
->
fmt
.
i_height
)
b_changed
=
true
;
for
(
int
i_subpic
=
0
;
i_subpic
<
VOUT_MAX_SUBPICTURES
;
i_subpic
++
)
/* Check forced palette changes */
{
if
(
b_changed_palette
)
spu_heap_entry_t
*
p_entry
=
&
p_sys
->
heap
.
p_entry
[
i_subpic
];
b_changed
=
true
;
subpicture_t
*
p_subpic
=
p_entry
->
p_subpicture
;
if
(
!
p_subpic
)
if
(
b_changed
)
continue
;
{
if
(
p_subpic
->
i_channel
!=
i_channel
&&
(
i_channel
!=
-
1
||
p_subpic
->
i_channel
==
SPU_DEFAULT_CHANNEL
)
)
subpicture_region_private_Delete
(
p_private
);
continue
;
p_region
->
p_private
=
NULL
;
}
}
/* You cannot delete subpicture outside of spu_SortSubpictures */
/* Scale if needed into cache */
p_entry
->
b_reject
=
true
;
if
(
!
p_region
->
p_private
&&
i_dst_width
>
0
&&
i_dst_height
>
0
)
}
{
filter_t
*
p_scale
=
p_sys
->
p_scale
;
vlc_mutex_unlock
(
&
p_sys
->
lock
)
;
picture_t
*
p_picture
=
p_region
->
p_picture
;
}
picture_Hold
(
p_picture
);
void
spu_ChangeFilters
(
spu_t
*
p_spu
,
const
char
*
psz_filters
)
/* Convert YUVP to YUVA/RGBA first for better scaling quality */
{
if
(
b_using_palette
)
spu_private_t
*
p_sys
=
p_spu
->
p
;
{
filter_t
*
p_scale_yuvp
=
p_sys
->
p_scale_yuvp
;
vlc_mutex_lock
(
&
p_sys
->
lock
)
;
p_scale_yuvp
->
fmt_in
.
video
=
p_region
->
fmt
;
free
(
p_sys
->
psz_chain_update
);
/* TODO converting to RGBA for RGB video output is better */
p_sys
->
psz_chain_update
=
strdup
(
psz_filters
);
p_scale_yuvp
->
fmt_out
.
video
=
p_region
->
fmt
;
p_scale_yuvp
->
fmt_out
.
video
.
i_chroma
=
VLC_CODEC_YUVA
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
p_picture
=
p_scale_yuvp
->
pf_video_filter
(
p_scale_yuvp
,
p_picture
);
}
if
(
!
p_picture
)
{
/* Well we will try conversion+scaling */
msg_Warn
(
p_spu
,
"%4.4s to %4.4s conversion failed"
,
(
const
char
*
)
&
p_scale_yuvp
->
fmt_in
.
video
.
i_chroma
,
(
const
char
*
)
&
p_scale_yuvp
->
fmt_out
.
video
.
i_chroma
);
}
}
void
spu_ChangeMargin
(
spu_t
*
p_spu
,
int
i_margin
)
/* Conversion(except from YUVP)/Scaling */
{
if
(
p_picture
&&
spu_private_t
*
p_sys
=
p_spu
->
p
;
(
p_picture
->
format
.
i_width
!=
i_dst_width
||
p_picture
->
format
.
i_height
!=
i_dst_height
)
)
{
p_scale
->
fmt_in
.
video
=
p_picture
->
format
;
p_scale
->
fmt_out
.
video
=
p_picture
->
format
;
vlc_mutex_lock
(
&
p_sys
->
lock
);
p_scale
->
fmt_out
.
video
.
i_width
=
i_dst_width
;
p_sys
->
i_margin
=
i_margin
;
p_scale
->
fmt_out
.
video
.
i_height
=
i_dst_height
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
}
/* */
p_scale
->
fmt_out
.
video
.
i_visible_width
=
static
void
SubpictureChain
(
subpicture_t
**
pp_head
,
subpicture_t
*
p_subpic
)
spu_scale_w
(
p_region
->
fmt
.
i_visible_width
,
scale_size
);
{
p_scale
->
fmt_out
.
video
.
i_visible_height
=
p_subpic
->
p_next
=
*
pp_head
;
spu_scale_h
(
p_region
->
fmt
.
i_visible_height
,
scale_size
)
;
*
pp_head
=
p_subpic
;
p_picture
=
p_scale
->
pf_video_filter
(
p_scale
,
p_picture
);
}
if
(
!
p_picture
)
msg_Err
(
p_spu
,
"scaling failed"
);
}
/*****************************************************************************
/* */
* heap managment
if
(
p_picture
)
*****************************************************************************/
{
static
void
SpuHeapInit
(
spu_heap_t
*
p_heap
)
p_region
->
p_private
=
subpicture_region_private_New
(
&
p_picture
->
format
);
{
if
(
p_region
->
p_private
)
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
{
{
p_region
->
p_private
->
p_picture
=
p_picture
;
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i
];
if
(
!
p_region
->
p_private
->
p_picture
)
{
subpicture_region_private_Delete
(
p_region
->
p_private
);
p_region
->
p_private
=
NULL
;
}
}
else
{
picture_Release
(
p_picture
);
}
}
}
e
->
p_subpicture
=
NULL
;
/* And use the scaled picture */
e
->
b_reject
=
false
;
if
(
p_region
->
p_private
)
{
region_fmt
=
p_region
->
p_private
->
fmt
;
p_region_picture
=
p_region
->
p_private
->
p_picture
;
}
}
}
}
static
int
SpuHeapPush
(
spu_heap_t
*
p_heap
,
subpicture_t
*
p_subpic
)
/* Force cropping if requested */
{
if
(
b_force_crop
)
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
{
{
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i
];
int
i_crop_x
=
spu_scale_w
(
p_sys
->
i_crop_x
,
scale_size
);
int
i_crop_y
=
spu_scale_h
(
p_sys
->
i_crop_y
,
scale_size
);
int
i_crop_width
=
spu_scale_w
(
p_sys
->
i_crop_width
,
scale_size
);
int
i_crop_height
=
spu_scale_h
(
p_sys
->
i_crop_height
,
scale_size
);
if
(
e
->
p_subpicture
)
/* Find the intersection */
continue
;
if
(
i_crop_x
+
i_crop_width
<=
i_x_offset
||
i_x_offset
+
(
int
)
region_fmt
.
i_visible_width
<
i_crop_x
||
e
->
p_subpicture
=
p_subpic
;
i_crop_y
+
i_crop_height
<=
i_y_offset
||
e
->
b_reject
=
false
;
i_y_offset
+
(
int
)
region_fmt
.
i_visible_height
<
i_crop_y
)
return
VLC_SUCCESS
;
{
}
/* No intersection */
return
VLC_EGENERIC
;
region_fmt
.
i_visible_width
=
}
region_fmt
.
i_visible_height
=
0
;
}
static
void
SpuHeapDeleteAt
(
spu_heap_t
*
p_heap
,
int
i_index
)
else
{
{
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i_index
];
int
i_x
,
i_y
,
i_x_end
,
i_y_end
;
i_x
=
__MAX
(
i_crop_x
,
i_x_offset
);
i_y
=
__MAX
(
i_crop_y
,
i_y_offset
);
i_x_end
=
__MIN
(
i_crop_x
+
i_crop_width
,
i_x_offset
+
(
int
)
region_fmt
.
i_visible_width
);
i_y_end
=
__MIN
(
i_crop_y
+
i_crop_height
,
i_y_offset
+
(
int
)
region_fmt
.
i_visible_height
);
if
(
e
->
p_subpicture
)
region_fmt
.
i_x_offset
=
i_x
-
i_x_offset
;
subpicture_Delete
(
e
->
p_subpicture
);
region_fmt
.
i_y_offset
=
i_y
-
i_y_offset
;
region_fmt
.
i_visible_width
=
i_x_end
-
i_x
;
region_fmt
.
i_visible_height
=
i_y_end
-
i_y
;
e
->
p_subpicture
=
NULL
;
i_x_offset
=
__MAX
(
i_x
,
0
);
}
i_y_offset
=
__MAX
(
i_y
,
0
);
}
}
static
int
SpuHeapDeleteSubpicture
(
spu_heap_t
*
p_heap
,
subpicture_t
*
p_subpic
)
subpicture_region_t
*
p_dst
=
*
pp_dst
=
subpicture_region_New
(
&
region_fmt
);
{
if
(
p_dst
)
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
{
{
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i
];
p_dst
->
i_x
=
i_x_offset
;
p_dst
->
i_y
=
i_y_offset
;
if
(
e
->
p_subpicture
!=
p_subpic
)
p_dst
->
i_align
=
0
;
continue
;
p_dst
->
p_picture
=
picture_Hold
(
p_region_picture
);
int
i_fade_alpha
=
255
;
if
(
p_subpic
->
b_fade
)
{
mtime_t
fade_start
=
(
p_subpic
->
i_stop
+
p_subpic
->
i_start
)
/
2
;
SpuHeapDeleteAt
(
p_heap
,
i
);
if
(
fade_start
<=
render_date
&&
fade_start
<
p_subpic
->
i_stop
)
return
VLC_SUCCESS
;
i_fade_alpha
=
255
*
(
p_subpic
->
i_stop
-
render_date
)
/
(
p_subpic
->
i_stop
-
fade_start
);
}
p_dst
->
i_alpha
=
i_fade_alpha
*
p_subpic
->
i_alpha
*
p_region
->
i_alpha
/
65025
;
}
}
return
VLC_EGENERIC
;
}
static
void
SpuHeapClean
(
spu_heap_t
*
p_heap
)
exit:
{
if
(
b_rerender_text
)
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
{
{
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i
];
/* Some forms of subtitles need to be re-rendered more than
if
(
e
->
p_subpicture
)
* once, eg. karaoke. We therefore restore the region to its
subpicture_Delete
(
e
->
p_subpicture
);
* pre-rendered state, so the next time through everything is
* calculated again.
*/
if
(
p_region
->
p_picture
)
{
picture_Release
(
p_region
->
p_picture
);
p_region
->
p_picture
=
NULL
;
}
if
(
p_region
->
p_private
)
{
subpicture_region_private_Delete
(
p_region
->
p_private
);
p_region
->
p_private
=
NULL
;
}
p_region
->
i_align
&=
~
SUBPICTURE_RENDERED
;
}
}
if
(
b_restore_format
)
p_region
->
fmt
=
fmt_original
;
}
}
static
void
FilterRelease
(
filter_t
*
p_filter
)
/**
* This function renders all sub picture units in the list.
*/
static
subpicture_t
*
SpuRenderSubpictures
(
spu_t
*
p_spu
,
const
video_format_t
*
p_fmt_dst
,
subpicture_t
*
p_subpic_list
,
const
video_format_t
*
p_fmt_src
,
mtime_t
render_subtitle_date
,
mtime_t
render_osd_date
)
{
{
if
(
p_filter
->
p_module
)
spu_private_t
*
p_sys
=
p_spu
->
p
;
module_unneed
(
p_filter
,
p_filter
->
p_module
);
if
(
p_filter
->
p_owner
)
free
(
p_filter
->
p_owner
);
vlc_object_release
(
p_filter
)
;
const
int
i_source_video_width
=
p_fmt_src
->
i_width
;
}
const
int
i_source_video_height
=
p_fmt_src
->
i_height
;
static
void
SpuRenderCreateAndLoadText
(
spu_t
*
p_spu
)
unsigned
int
i_subpicture
;
{
subpicture_t
*
pp_subpicture
[
VOUT_MAX_SUBPICTURES
];
filter_t
*
p_text
;
assert
(
!
p_spu
->
p
->
p_text
);
unsigned
int
i_subtitle_region_count
;
spu_area_t
p_subtitle_area_buffer
[
VOUT_MAX_SUBPICTURES
];
spu_area_t
*
p_subtitle_area
;
int
i_subtitle_area
;
p_spu
->
p
->
p_text
=
/* Preprocess subpictures */
p_text
=
vlc_custom_create
(
p_spu
,
sizeof
(
filter_t
),
i_subpicture
=
0
;
VLC_OBJECT_GENERIC
,
"spu text"
);
i_subtitle_region_count
=
0
;
if
(
!
p_text
)
for
(
subpicture_t
*
p_subpic
=
p_subpic_list
;
return
;
p_subpic
!=
NULL
;
p_subpic
=
p_subpic
->
p_next
)
{
subpicture_Update
(
p_subpic
,
p_fmt_src
,
p_fmt_dst
,
p_subpic
->
b_subtitle
?
render_subtitle_date
:
render_osd_date
);
p_text
->
p_owner
=
xmalloc
(
sizeof
(
*
p_text
->
p_owner
)
);
/* */
p_text
->
p_owner
->
p_spu
=
p_spu
;
if
(
p_subpic
->
b_subtitle
)
{
for
(
subpicture_region_t
*
r
=
p_subpic
->
p_region
;
r
!=
NULL
;
r
=
r
->
p_next
)
i_subtitle_region_count
++
;
}
es_format_Init
(
&
p_text
->
fmt_in
,
VIDEO_ES
,
0
);
/* */
pp_subpicture
[
i_subpicture
++
]
=
p_subpic
;
}
es_format_Init
(
&
p_text
->
fmt_out
,
VIDEO_ES
,
0
);
/* Be sure we have at least 1 picture to process */
p_text
->
fmt_out
.
video
.
i_width
=
if
(
i_subpicture
<=
0
)
p_text
->
fmt_out
.
video
.
i_visible_width
=
32
;
return
NULL
;
p_text
->
fmt_out
.
video
.
i_height
=
p_text
->
fmt_out
.
video
.
i_visible_height
=
32
;
p_text
->
pf_get_attachments
=
spu_get_attachments
;
subpicture_t
*
p_output
=
subpicture_New
(
NULL
)
;
vlc_object_attach
(
p_text
,
p_spu
);
/* Now order subpicture array
* XXX The order is *really* important for overlap subtitles positionning */
qsort
(
pp_subpicture
,
i_subpicture
,
sizeof
(
*
pp_subpicture
),
SubpictureCmp
);
/* FIXME TOCHECK shouldn't module_need( , , psz_modulename, false ) do the
/* Allocate area array for subtitle overlap */
* same than these 2 calls ? */
i_subtitle_area
=
0
;
char
*
psz_modulename
=
var_CreateGetString
(
p_spu
,
"text-renderer"
);
p_subtitle_area
=
p_subtitle_area_buffer
;
if
(
psz_modulename
&&
*
psz_modulename
)
if
(
i_subtitle_region_count
>
sizeof
(
p_subtitle_area_buffer
)
/
sizeof
(
*
p_subtitle_area_buffer
)
)
p_subtitle_area
=
calloc
(
i_subtitle_region_count
,
sizeof
(
*
p_subtitle_area
)
);
/* Process all subpictures and regions (in the right order) */
for
(
unsigned
int
i_index
=
0
;
i_index
<
i_subpicture
;
i_index
++
)
{
{
p_text
->
p_module
=
module_need
(
p_text
,
"text renderer"
,
subpicture_t
*
p_subpic
=
pp_subpicture
[
i_index
];
psz_modulename
,
true
);
subpicture_region_t
*
p_region
;
}
free
(
psz_modulename
);
if
(
!
p_text
->
p_module
)
if
(
!
p_subpic
->
p_region
)
p_text
->
p_module
=
module_need
(
p_text
,
"text renderer"
,
NULL
,
false
)
;
continue
;
/* Create a few variables used for enhanced text rendering */
/* FIXME when possible use a better rendering size than source size
var_Create
(
p_text
,
"spu-duration"
,
VLC_VAR_TIME
);
* (max of display size and source size for example) FIXME */
var_Create
(
p_text
,
"spu-elapsed"
,
VLC_VAR_TIME
);
int
i_render_width
=
p_subpic
->
i_original_picture_width
;
var_Create
(
p_text
,
"text-rerender"
,
VLC_VAR_BOOL
);
int
i_render_height
=
p_subpic
->
i_original_picture_height
;
var_Create
(
p_text
,
"scale"
,
VLC_VAR_INTEGER
);
if
(
!
i_render_width
||
!
i_render_height
)
}
{
if
(
i_render_width
!=
0
||
i_render_height
!=
0
)
msg_Err
(
p_spu
,
"unsupported original picture size %dx%d"
,
i_render_width
,
i_render_height
);
static
filter_t
*
CreateAndLoadScale
(
vlc_object_t
*
p_obj
,
p_subpic
->
i_original_picture_width
=
i_render_width
=
i_source_video_width
;
vlc_fourcc_t
i_src_chroma
,
vlc_fourcc_t
i_dst_chroma
,
p_subpic
->
i_original_picture_height
=
i_render_height
=
i_source_video_height
;
bool
b_resize
)
}
{
filter_t
*
p_scale
;
p_scale
=
vlc_custom_create
(
p_obj
,
sizeof
(
filter_t
),
if
(
p_sys
->
p_text
)
VLC_OBJECT_GENERIC
,
"scale"
);
{
if
(
!
p_scale
)
p_sys
->
p_text
->
fmt_out
.
video
.
i_width
=
return
NULL
;
p_sys
->
p_text
->
fmt_out
.
video
.
i_visible_width
=
i_render_width
;
es_format_Init
(
&
p_scale
->
fmt_in
,
VIDEO_ES
,
0
);
p_sys
->
p_text
->
fmt_out
.
video
.
i_height
=
p_scale
->
fmt_in
.
video
.
i_chroma
=
i_src_chroma
;
p_sys
->
p_text
->
fmt_out
.
video
.
i_visible_height
=
i_render_height
;
p_scale
->
fmt_in
.
video
.
i_width
=
}
p_scale
->
fmt_in
.
video
.
i_height
=
32
;
es_format_Init
(
&
p_scale
->
fmt_out
,
VIDEO_ES
,
0
);
/* Compute scaling from picture to source size */
p_scale
->
fmt_out
.
video
.
i_chroma
=
i_dst_chroma
;
spu_scale_t
scale
=
spu_scale_createq
(
i_source_video_width
,
i_render_width
,
p_scale
->
fmt_out
.
video
.
i_width
=
i_source_video_height
,
i_render_height
);
p_scale
->
fmt_out
.
video
.
i_height
=
b_resize
?
16
:
32
;
p_scale
->
pf_video_buffer_new
=
spu_new_video_buffer
;
/* Update scaling from source size to display size(p_fmt_dst) */
p_scale
->
pf_video_buffer_del
=
spu_del_video_buffer
;
scale
.
w
=
scale
.
w
*
p_fmt_dst
->
i_width
/
i_source_video_width
;
scale
.
h
=
scale
.
h
*
p_fmt_dst
->
i_height
/
i_source_video_height
;
vlc_object_attach
(
p_scale
,
p_obj
);
/* Set default subpicture aspect ratio
p_scale
->
p_module
=
module_need
(
p_scale
,
"video filter2"
,
NULL
,
false
);
* FIXME if we only handle 1 aspect ratio per picture, why is it set per
* region ? */
p_region
=
p_subpic
->
p_region
;
if
(
!
p_region
->
fmt
.
i_sar_num
||
!
p_region
->
fmt
.
i_sar_den
)
{
p_region
->
fmt
.
i_sar_den
=
p_fmt_dst
->
i_sar_den
;
p_region
->
fmt
.
i_sar_num
=
p_fmt_dst
->
i_sar_num
;
}
return
p_scale
;
/* Take care of the aspect ratio */
}
if
(
p_region
->
fmt
.
i_sar_num
*
p_fmt_dst
->
i_sar_den
!=
static
void
SpuRenderCreateAndLoadScale
(
spu_t
*
p_spu
)
p_region
->
fmt
.
i_sar_den
*
p_fmt_dst
->
i_sar_num
)
{
{
assert
(
!
p_spu
->
p
->
p_scale
);
/* FIXME FIXME what about region->i_x/i_y ? */
assert
(
!
p_spu
->
p
->
p_scale_yuvp
);
scale
.
w
=
scale
.
w
*
/* XXX p_spu->p_scale is used for all conversion/scaling except yuvp to
(
int64_t
)
p_region
->
fmt
.
i_sar_num
*
p_fmt_dst
->
i_sar_den
/
* yuva/rgba */
p_region
->
fmt
.
i_sar_den
/
p_fmt_dst
->
i_sar_num
;
p_spu
->
p
->
p_scale
=
CreateAndLoadScale
(
VLC_OBJECT
(
p_spu
),
}
VLC_CODEC_YUVA
,
VLC_CODEC_YUVA
,
true
);
/* This one is used for YUVP to YUVA/RGBA without scaling
/* Render all regions
* FIXME rename it */
* We always transform non absolute subtitle into absolute one on the
p_spu
->
p
->
p_scale_yuvp
=
CreateAndLoadScale
(
VLC_OBJECT
(
p_spu
),
* first rendering to allow good subtitle overlap support.
VLC_CODEC_YUVP
,
VLC_CODEC_YUVA
,
false
);
*/
for
(
p_region
=
p_subpic
->
p_region
;
p_region
!=
NULL
;
p_region
=
p_region
->
p_next
)
{
spu_area_t
area
;
/* Check scale validity */
if
(
scale
.
w
<=
0
||
scale
.
h
<=
0
)
continue
;
/* */
subpicture_region_t
*
p_render
;
SpuRenderRegion
(
p_spu
,
&
p_render
,
&
area
,
p_subpic
,
p_region
,
scale
,
p_fmt_dst
,
p_subtitle_area
,
i_subtitle_area
,
p_subpic
->
b_subtitle
?
render_subtitle_date
:
render_osd_date
);
if
(
p_render
)
{
subpicture_region_t
**
pp_last
=
&
p_output
->
p_region
;
while
(
*
pp_last
)
pp_last
=
&
(
*
pp_last
)
->
p_next
;
*
pp_last
=
p_render
;
}
if
(
p_subpic
->
b_subtitle
)
{
area
=
spu_area_unscaled
(
area
,
scale
);
if
(
!
p_subpic
->
b_absolute
&&
area
.
i_width
>
0
&&
area
.
i_height
>
0
)
{
p_region
->
i_x
=
area
.
i_x
;
p_region
->
i_y
=
area
.
i_y
;
}
if
(
p_subtitle_area
)
p_subtitle_area
[
i_subtitle_area
++
]
=
area
;
}
}
if
(
p_subpic
->
b_subtitle
)
p_subpic
->
b_absolute
=
true
;
}
/* */
if
(
p_subtitle_area
!=
p_subtitle_area_buffer
)
free
(
p_subtitle_area
);
return
p_output
;
}
}
static
void
SpuRenderText
(
spu_t
*
p_spu
,
bool
*
pb_rerender_text
,
/*****************************************************************************
subpicture_t
*
p_subpic
,
subpicture_region_t
*
p_region
,
* Object variables callbacks
int
i_min_scale_ratio
,
mtime_t
render_date
)
*****************************************************************************/
{
filter_t
*
p_text
=
p_spu
->
p
->
p_text
;
assert
(
p_region
->
fmt
.
i_chroma
==
VLC_CODEC_TEXT
);
/*****************************************************************************
* UpdateSPU: update subpicture settings
*****************************************************************************
* This function is called from CropCallback and at initialization time, to
* retrieve crop information from the input.
*****************************************************************************/
static
void
UpdateSPU
(
spu_t
*
p_spu
,
vlc_object_t
*
p_object
)
{
spu_private_t
*
p_sys
=
p_spu
->
p
;
vlc_value_t
val
;
if
(
!
p_text
||
!
p_text
->
p_module
)
vlc_mutex_lock
(
&
p_sys
->
lock
);
goto
exit
;
/* Setup 3 variables which can be used to render
p_sys
->
b_force_palette
=
false
;
* time-dependent text (and effects). The first indicates
p_sys
->
b_force_crop
=
false
;
* the total amount of time the text will be on screen,
* the second the amount of time it has already been on
* screen (can be a negative value as text is layed out
* before it is rendered) and the third is a feedback
* variable from the renderer - if the renderer sets it
* then this particular text is time-dependent, eg. the
* visual progress bar inside the text in karaoke and the
* text needs to be rendered multiple times in order for
* the effect to work - we therefore need to return the
* region to its original state at the end of the loop,
* instead of leaving it in YUVA or YUVP.
* Any renderer which is unaware of how to render
* time-dependent text can happily ignore the variables
* and render the text the same as usual - it should at
* least show up on screen, but the effect won't change
* the text over time.
*/
var_SetTime
(
p_text
,
"spu-duration"
,
p_subpic
->
i_stop
-
p_subpic
->
i_start
);
var_SetTime
(
p_text
,
"spu-elapsed"
,
render_date
);
var_SetBool
(
p_text
,
"text-rerender"
,
false
);
var_SetInteger
(
p_text
,
"scale"
,
i_min_scale_ratio
);
if
(
p_text
->
pf_render_html
&&
p_region
->
psz_htm
l
)
if
(
var_Get
(
p_object
,
"highlight"
,
&
val
)
||
!
val
.
b_boo
l
)
{
{
p_text
->
pf_render_html
(
p_text
,
p_region
,
p_region
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
;
}
}
else
if
(
p_text
->
pf_render_text
)
p_sys
->
b_force_crop
=
true
;
p_sys
->
i_crop_x
=
var_GetInteger
(
p_object
,
"x-start"
);
p_sys
->
i_crop_y
=
var_GetInteger
(
p_object
,
"y-start"
);
p_sys
->
i_crop_width
=
var_GetInteger
(
p_object
,
"x-end"
)
-
p_sys
->
i_crop_x
;
p_sys
->
i_crop_height
=
var_GetInteger
(
p_object
,
"y-end"
)
-
p_sys
->
i_crop_y
;
if
(
var_Get
(
p_object
,
"menu-palette"
,
&
val
)
==
VLC_SUCCESS
)
{
{
p_text
->
pf_render_text
(
p_text
,
p_region
,
p_region
);
memcpy
(
p_sys
->
palette
,
val
.
p_address
,
16
);
p_sys
->
b_force_palette
=
true
;
}
}
*
pb_rerender_text
=
var_GetBool
(
p_text
,
"text-rerender"
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
exit:
msg_Dbg
(
p_object
,
"crop: %i,%i,%i,%i, palette forced: %i"
,
p_region
->
i_align
|=
SUBPICTURE_RENDERED
;
p_sys
->
i_crop_x
,
p_sys
->
i_crop_y
,
p_sys
->
i_crop_width
,
p_sys
->
i_crop_height
,
p_sys
->
b_force_palette
);
}
}
/**
/*****************************************************************************
* A few scale functions helpers.
* CropCallback: called when the highlight properties are changed
*/
*****************************************************************************
static
spu_scale_t
spu_scale_create
(
int
w
,
int
h
)
* This callback is called from the input thread when we need cropping
{
*****************************************************************************/
spu_scale_t
s
=
{
.
w
=
w
,
.
h
=
h
};
static
int
CropCallback
(
vlc_object_t
*
p_object
,
char
const
*
psz_var
,
if
(
s
.
w
<=
0
)
vlc_value_t
oldval
,
vlc_value_t
newval
,
void
*
p_data
)
s
.
w
=
SCALE_UNIT
;
if
(
s
.
h
<=
0
)
s
.
h
=
SCALE_UNIT
;
return
s
;
}
static
spu_scale_t
spu_scale_unit
(
void
)
{
return
spu_scale_create
(
SCALE_UNIT
,
SCALE_UNIT
);
}
static
spu_scale_t
spu_scale_createq
(
int
wn
,
int
wd
,
int
hn
,
int
hd
)
{
{
return
spu_scale_create
(
wn
*
SCALE_UNIT
/
wd
,
VLC_UNUSED
(
oldval
);
VLC_UNUSED
(
newval
);
VLC_UNUSED
(
psz_var
);
hn
*
SCALE_UNIT
/
hd
);
UpdateSPU
(
(
spu_t
*
)
p_data
,
p_object
);
return
VLC_SUCCESS
;
}
}
static
int
spu_scale_w
(
int
v
,
const
spu_scale_t
s
)
/*****************************************************************************
* Buffers allocation callbacks for the filters
*****************************************************************************/
static
subpicture_t
*
sub_new_buffer
(
filter_t
*
p_filter
)
{
{
return
v
*
s
.
w
/
SCALE_UNIT
;
filter_owner_sys_t
*
p_sys
=
p_filter
->
p_owner
;
subpicture_t
*
p_subpicture
=
subpicture_New
(
NULL
);
if
(
p_subpicture
)
p_subpicture
->
i_channel
=
p_sys
->
i_channel
;
return
p_subpicture
;
}
}
static
int
spu_scale_h
(
int
v
,
const
spu_scale_t
s
)
static
void
sub_del_buffer
(
filter_t
*
p_filter
,
subpicture_t
*
p_subpic
)
{
{
return
v
*
s
.
h
/
SCALE_UNIT
;
VLC_UNUSED
(
p_filter
);
subpicture_Delete
(
p_subpic
);
}
}
static
int
spu_invscale_w
(
int
v
,
const
spu_scale_t
s
)
static
int
SubFilterAllocationInit
(
filter_t
*
p_filter
,
void
*
p_data
)
{
{
return
v
*
SCALE_UNIT
/
s
.
w
;
spu_t
*
p_spu
=
p_data
;
filter_owner_sys_t
*
p_sys
=
malloc
(
sizeof
(
filter_owner_sys_t
)
);
if
(
!
p_sys
)
return
VLC_EGENERIC
;
p_filter
->
pf_sub_buffer_new
=
sub_new_buffer
;
p_filter
->
pf_sub_buffer_del
=
sub_del_buffer
;
p_filter
->
p_owner
=
p_sys
;
p_sys
->
i_channel
=
spu_RegisterChannel
(
p_spu
);
p_sys
->
p_spu
=
p_spu
;
return
VLC_SUCCESS
;
}
}
static
int
spu_invscale_h
(
int
v
,
const
spu_scale_t
s
)
static
void
SubFilterAllocationClean
(
filter_t
*
p_filter
)
{
{
return
v
*
SCALE_UNIT
/
s
.
h
;
filter_owner_sys_t
*
p_sys
=
p_filter
->
p_owner
;
spu_ClearChannel
(
p_sys
->
p_spu
,
p_sys
->
i_channel
);
free
(
p_filter
->
p_owner
);
}
}
/*****************************************************************************
* Public API
*****************************************************************************/
#undef spu_Create
/**
/**
* A few area functions helpers
* Creates the subpicture unit
*
* \param p_this the parent object which creates the subpicture unit
*/
*/
static
spu_area_t
spu_area_create
(
int
x
,
int
y
,
int
w
,
int
h
,
spu_scale_t
s
)
spu_t
*
spu_Create
(
vlc_object_t
*
p_this
)
{
spu_area_t
a
=
{
.
i_x
=
x
,
.
i_y
=
y
,
.
i_width
=
w
,
.
i_height
=
h
,
.
scale
=
s
};
return
a
;
}
static
spu_area_t
spu_area_scaled
(
spu_area_t
a
)
{
{
if
(
a
.
scale
.
w
==
SCALE_UNIT
&&
a
.
scale
.
h
==
SCALE_UNIT
)
spu_t
*
p_spu
;
return
a
;
spu_private_t
*
p_sys
;
a
.
i_x
=
spu_scale_w
(
a
.
i_x
,
a
.
scale
);
a
.
i_y
=
spu_scale_h
(
a
.
i_y
,
a
.
scale
);
a
.
i_width
=
spu_scale_w
(
a
.
i_width
,
a
.
scale
);
p_spu
=
vlc_custom_create
(
p_this
,
sizeof
(
spu_t
)
+
sizeof
(
spu_private_t
),
a
.
i_height
=
spu_scale_h
(
a
.
i_height
,
a
.
scale
);
VLC_OBJECT_GENERIC
,
"subpicture"
);
if
(
!
p_spu
)
return
NULL
;
vlc_object_attach
(
p_spu
,
p_this
);
a
.
scale
=
spu_scale_unit
();
/* Initialize spu fields */
return
a
;
p_spu
->
p
=
p_sys
=
(
spu_private_t
*
)
&
p_spu
[
1
];
}
static
spu_area_t
spu_area_unscaled
(
spu_area_t
a
,
spu_scale_t
s
)
{
if
(
a
.
scale
.
w
==
s
.
w
&&
a
.
scale
.
h
==
s
.
h
)
return
a
;
a
=
spu_area_scaled
(
a
);
/* Initialize private fields */
vlc_mutex_init
(
&
p_sys
->
lock
);
a
.
i_x
=
spu_invscale_w
(
a
.
i_x
,
s
);
SpuHeapInit
(
&
p_sys
->
heap
);
a
.
i_y
=
spu_invscale_h
(
a
.
i_y
,
s
);
a
.
i_width
=
spu_invscale_w
(
a
.
i_width
,
s
);
p_sys
->
p_text
=
NULL
;
a
.
i_height
=
spu_invscale_h
(
a
.
i_height
,
s
);
p_sys
->
p_scale
=
NULL
;
p_sys
->
p_scale_yuvp
=
NULL
;
a
.
scale
=
s
;
return
a
;
}
static
bool
spu_area_overlap
(
spu_area_t
a
,
spu_area_t
b
)
{
const
int
i_dx
=
0
;
const
int
i_dy
=
0
;
a
=
spu_area_scaled
(
a
);
b
=
spu_area_scaled
(
b
);
return
__MAX
(
a
.
i_x
-
i_dx
,
b
.
i_x
)
<
__MIN
(
a
.
i_x
+
a
.
i_width
+
i_dx
,
b
.
i_x
+
b
.
i_width
)
&&
__MAX
(
a
.
i_y
-
i_dy
,
b
.
i_y
)
<
__MIN
(
a
.
i_y
+
a
.
i_height
+
i_dy
,
b
.
i_y
+
b
.
i_height
);
}
/**
p_sys
->
i_margin
=
var_InheritInteger
(
p_spu
,
"sub-margin"
);
* Avoid area overlapping
*/
static
void
SpuAreaFixOverlap
(
spu_area_t
*
p_dst
,
const
spu_area_t
*
p_sub
,
int
i_sub
,
int
i_align
)
{
spu_area_t
a
=
spu_area_scaled
(
*
p_dst
);
bool
b_moved
=
false
;
bool
b_ok
;
/* Check for overlap
/* Register the default subpicture channel */
* XXX It is not fast O(n^2) but we should not have a lot of region */
p_sys
->
i_channel
=
SPU_DEFAULT_CHANNEL
+
1
;
do
{
b_ok
=
true
;
for
(
int
i
=
0
;
i
<
i_sub
;
i
++
)
{
spu_area_t
sub
=
spu_area_scaled
(
p_sub
[
i
]
);
if
(
!
spu_area_overlap
(
a
,
sub
)
)
p_sys
->
psz_chain_update
=
NULL
;
continue
;
vlc_mutex_init
(
&
p_sys
->
chain_lock
);
p_sys
->
p_chain
=
filter_chain_New
(
p_spu
,
"sub filter"
,
false
,
SubFilterAllocationInit
,
SubFilterAllocationClean
,
p_spu
);
if
(
i_align
&
SUBPICTURE_ALIGN_TOP
)
/* Load text and scale module */
{
SpuRenderCreateAndLoadText
(
p_spu
);
/* We go down */
SpuRenderCreateAndLoadScale
(
p_spu
);
int
i_y
=
sub
.
i_y
+
sub
.
i_height
;
a
.
i_y
=
i_y
;
b_moved
=
true
;
}
else
if
(
i_align
&
SUBPICTURE_ALIGN_BOTTOM
)
{
/* We go up */
int
i_y
=
sub
.
i_y
-
a
.
i_height
;
a
.
i_y
=
i_y
;
b_moved
=
true
;
}
else
{
/* TODO what to do in this case? */
//fprintf( stderr, "Overlap with unsupported alignment\n" );
break
;
}
b_ok
=
false
;
/* */
break
;
p_sys
->
i_last_sort_date
=
-
1
;
}
}
while
(
!
b_ok
);
if
(
b_moved
)
return
p_spu
;
*
p_dst
=
spu_area_unscaled
(
a
,
p_dst
->
scale
);
}
}
/**
static
void
SpuAreaFitInside
(
spu_area_t
*
p_area
,
const
spu_area_t
*
p_boundary
)
* Destroy the subpicture unit
*
* \param p_this the parent object which destroys the subpicture unit
*/
void
spu_Destroy
(
spu_t
*
p_spu
)
{
{
spu_area_t
a
=
spu_area_scaled
(
*
p_area
);
spu_private_t
*
p_sys
=
p_spu
->
p
;
const
int
i_error_x
=
(
a
.
i_x
+
a
.
i_width
)
-
p_boundary
->
i_width
;
if
(
i_error_x
>
0
)
a
.
i_x
-=
i_error_x
;
if
(
a
.
i_x
<
0
)
a
.
i_x
=
0
;
const
int
i_error_y
=
(
a
.
i_y
+
a
.
i_height
)
-
p_boundary
->
i_height
;
if
(
p_sys
->
p_text
)
if
(
i_error_y
>
0
)
FilterRelease
(
p_sys
->
p_text
);
a
.
i_y
-=
i_error_y
;
if
(
a
.
i_y
<
0
)
a
.
i_y
=
0
;
*
p_area
=
spu_area_unscaled
(
a
,
p_area
->
scale
);
if
(
p_sys
->
p_scale_yuvp
)
}
FilterRelease
(
p_sys
->
p_scale_yuvp
);
/**
if
(
p_sys
->
p_scale
)
* Place a region
FilterRelease
(
p_sys
->
p_scale
);
*/
static
void
SpuRegionPlace
(
int
*
pi_x
,
int
*
pi_y
,
const
subpicture_t
*
p_subpic
,
const
subpicture_region_t
*
p_region
)
{
const
int
i_delta_x
=
p_region
->
i_x
;
const
int
i_delta_y
=
p_region
->
i_y
;
int
i_x
,
i_y
;
assert
(
p_region
->
i_x
!=
INT_MAX
&&
p_region
->
i_y
!=
INT_MAX
);
filter_chain_Delete
(
p_sys
->
p_chain
);
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_TOP
)
vlc_mutex_destroy
(
&
p_sys
->
chain_lock
);
{
free
(
p_sys
->
psz_chain_update
);
i_y
=
i_delta_y
;
}
else
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_BOTTOM
)
{
i_y
=
p_subpic
->
i_original_picture_height
-
p_region
->
fmt
.
i_height
-
i_delta_y
;
}
else
{
i_y
=
p_subpic
->
i_original_picture_height
/
2
-
p_region
->
fmt
.
i_height
/
2
;
}
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_LEFT
)
/* Destroy all remaining subpictures */
{
SpuHeapClean
(
&
p_sys
->
heap
);
i_x
=
i_delta_x
;
}
else
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_RIGHT
)
{
i_x
=
p_subpic
->
i_original_picture_width
-
p_region
->
fmt
.
i_width
-
i_delta_x
;
}
else
{
i_x
=
p_subpic
->
i_original_picture_width
/
2
-
p_region
->
fmt
.
i_width
/
2
;
}
if
(
p_subpic
->
b_absolute
)
vlc_mutex_destroy
(
&
p_sys
->
lock
);
{
i_x
=
i_delta_x
;
i_y
=
i_delta_y
;
}
*
pi_x
=
i_x
;
vlc_object_release
(
p_spu
);
*
pi_y
=
i_y
;
}
}
/**
/**
* It will transform the provided region into another region suitable for rendering.
* Attach/Detach the SPU from any input
*
* \param p_this the object in which to destroy the subpicture unit
* \param b_attach to select attach or detach
*/
*/
void
spu_Attach
(
spu_t
*
p_spu
,
vlc_object_t
*
p_input
,
bool
b_attach
)
static
void
SpuRenderRegion
(
spu_t
*
p_spu
,
subpicture_region_t
**
pp_dst
,
spu_area_t
*
p_dst_area
,
subpicture_t
*
p_subpic
,
subpicture_region_t
*
p_region
,
const
spu_scale_t
scale_size
,
const
video_format_t
*
p_fmt
,
const
spu_area_t
*
p_subtitle_area
,
int
i_subtitle_area
,
mtime_t
render_date
)
{
{
spu_private_t
*
p_sys
=
p_spu
->
p
;
if
(
b_attach
)
video_format_t
fmt_original
=
p_region
->
fmt
;
bool
b_rerender_text
=
false
;
bool
b_restore_format
=
false
;
int
i_x_offset
;
int
i_y_offset
;
video_format_t
region_fmt
;
picture_t
*
p_region_picture
;
/* Invalidate area by default */
*
p_dst_area
=
spu_area_create
(
0
,
0
,
0
,
0
,
scale_size
);
*
pp_dst
=
NULL
;
/* Render text region */
if
(
p_region
->
fmt
.
i_chroma
==
VLC_CODEC_TEXT
)
{
const
int
i_min_scale_ratio
=
SCALE_UNIT
;
/* FIXME what is the right value? (scale_size is not) */
SpuRenderText
(
p_spu
,
&
b_rerender_text
,
p_subpic
,
p_region
,
i_min_scale_ratio
,
render_date
);
b_restore_format
=
b_rerender_text
;
/* Check if the rendering has failed ... */
if
(
p_region
->
fmt
.
i_chroma
==
VLC_CODEC_TEXT
)
goto
exit
;
}
/* Force palette if requested
* FIXME b_force_palette and b_force_crop are applied to all subpictures using palette
* instead of only the right one (being the dvd spu).
*/
const
bool
b_using_palette
=
p_region
->
fmt
.
i_chroma
==
VLC_CODEC_YUVP
;
const
bool
b_force_palette
=
b_using_palette
&&
p_sys
->
b_force_palette
;
const
bool
b_force_crop
=
b_force_palette
&&
p_sys
->
b_force_crop
;
bool
b_changed_palette
=
false
;
/* Compute the margin which is expressed in destination pixel unit
* The margin is applied only to subtitle and when no forced crop is
* requested (dvd menu) */
int
i_margin_y
=
0
;
if
(
!
b_force_crop
&&
p_subpic
->
b_subtitle
)
i_margin_y
=
spu_invscale_h
(
p_sys
->
i_margin
,
scale_size
);
/* Place the picture
* We compute the position in the rendered size */
SpuRegionPlace
(
&
i_x_offset
,
&
i_y_offset
,
p_subpic
,
p_region
);
/* Save this position for subtitle overlap support
* it is really important that there are given without scale_size applied */
*
p_dst_area
=
spu_area_create
(
i_x_offset
,
i_y_offset
,
p_region
->
fmt
.
i_width
,
p_region
->
fmt
.
i_height
,
scale_size
);
/* Handle overlapping subtitles when possible */
if
(
p_subpic
->
b_subtitle
&&
!
p_subpic
->
b_absolute
)
{
SpuAreaFixOverlap
(
p_dst_area
,
p_subtitle_area
,
i_subtitle_area
,
p_region
->
i_align
);
}
/* we copy the area: for the subtitle overlap support we want
* to only save the area without margin applied */
spu_area_t
restrained
=
*
p_dst_area
;
/* apply margin to subtitles and correct if they go over the picture edge */
if
(
p_subpic
->
b_subtitle
)
restrained
.
i_y
-=
i_margin_y
;
spu_area_t
display
=
spu_area_create
(
0
,
0
,
p_fmt
->
i_width
,
p_fmt
->
i_height
,
spu_scale_unit
()
);
SpuAreaFitInside
(
&
restrained
,
&
display
);
/* Fix the position for the current scale_size */
i_x_offset
=
spu_scale_w
(
restrained
.
i_x
,
restrained
.
scale
);
i_y_offset
=
spu_scale_h
(
restrained
.
i_y
,
restrained
.
scale
);
/* */
if
(
b_force_palette
)
{
video_palette_t
*
p_palette
=
p_region
->
fmt
.
p_palette
;
video_palette_t
palette
;
/* We suppose DVD palette here */
palette
.
i_entries
=
4
;
for
(
int
i
=
0
;
i
<
4
;
i
++
)
for
(
int
j
=
0
;
j
<
4
;
j
++
)
palette
.
palette
[
i
][
j
]
=
p_sys
->
palette
[
i
][
j
];
if
(
p_palette
->
i_entries
==
palette
.
i_entries
)
{
for
(
int
i
=
0
;
i
<
p_palette
->
i_entries
;
i
++
)
for
(
int
j
=
0
;
j
<
4
;
j
++
)
b_changed_palette
|=
p_palette
->
palette
[
i
][
j
]
!=
palette
.
palette
[
i
][
j
];
}
else
{
b_changed_palette
=
true
;
}
*
p_palette
=
palette
;
}
/* */
region_fmt
=
p_region
->
fmt
;
p_region_picture
=
p_region
->
p_picture
;
/* Scale from rendered size to destination size */
if
(
p_sys
->
p_scale
&&
p_sys
->
p_scale
->
p_module
&&
(
!
b_using_palette
||
(
p_sys
->
p_scale_yuvp
&&
p_sys
->
p_scale_yuvp
->
p_module
)
)
&&
(
scale_size
.
w
!=
SCALE_UNIT
||
scale_size
.
h
!=
SCALE_UNIT
||
b_using_palette
)
)
{
const
unsigned
i_dst_width
=
spu_scale_w
(
p_region
->
fmt
.
i_width
,
scale_size
);
const
unsigned
i_dst_height
=
spu_scale_h
(
p_region
->
fmt
.
i_height
,
scale_size
);
/* Destroy the cache if unusable */
if
(
p_region
->
p_private
)
{
subpicture_region_private_t
*
p_private
=
p_region
->
p_private
;
bool
b_changed
=
false
;
/* Check resize changes */
if
(
i_dst_width
!=
p_private
->
fmt
.
i_width
||
i_dst_height
!=
p_private
->
fmt
.
i_height
)
b_changed
=
true
;
/* Check forced palette changes */
if
(
b_changed_palette
)
b_changed
=
true
;
if
(
b_changed
)
{
subpicture_region_private_Delete
(
p_private
);
p_region
->
p_private
=
NULL
;
}
}
/* Scale if needed into cache */
if
(
!
p_region
->
p_private
&&
i_dst_width
>
0
&&
i_dst_height
>
0
)
{
filter_t
*
p_scale
=
p_sys
->
p_scale
;
picture_t
*
p_picture
=
p_region
->
p_picture
;
picture_Hold
(
p_picture
);
/* Convert YUVP to YUVA/RGBA first for better scaling quality */
if
(
b_using_palette
)
{
filter_t
*
p_scale_yuvp
=
p_sys
->
p_scale_yuvp
;
p_scale_yuvp
->
fmt_in
.
video
=
p_region
->
fmt
;
/* TODO converting to RGBA for RGB video output is better */
p_scale_yuvp
->
fmt_out
.
video
=
p_region
->
fmt
;
p_scale_yuvp
->
fmt_out
.
video
.
i_chroma
=
VLC_CODEC_YUVA
;
p_picture
=
p_scale_yuvp
->
pf_video_filter
(
p_scale_yuvp
,
p_picture
);
if
(
!
p_picture
)
{
/* Well we will try conversion+scaling */
msg_Warn
(
p_spu
,
"%4.4s to %4.4s conversion failed"
,
(
const
char
*
)
&
p_scale_yuvp
->
fmt_in
.
video
.
i_chroma
,
(
const
char
*
)
&
p_scale_yuvp
->
fmt_out
.
video
.
i_chroma
);
}
}
/* Conversion(except from YUVP)/Scaling */
if
(
p_picture
&&
(
p_picture
->
format
.
i_width
!=
i_dst_width
||
p_picture
->
format
.
i_height
!=
i_dst_height
)
)
{
p_scale
->
fmt_in
.
video
=
p_picture
->
format
;
p_scale
->
fmt_out
.
video
=
p_picture
->
format
;
p_scale
->
fmt_out
.
video
.
i_width
=
i_dst_width
;
p_scale
->
fmt_out
.
video
.
i_height
=
i_dst_height
;
p_scale
->
fmt_out
.
video
.
i_visible_width
=
spu_scale_w
(
p_region
->
fmt
.
i_visible_width
,
scale_size
);
p_scale
->
fmt_out
.
video
.
i_visible_height
=
spu_scale_h
(
p_region
->
fmt
.
i_visible_height
,
scale_size
);
p_picture
=
p_scale
->
pf_video_filter
(
p_scale
,
p_picture
);
if
(
!
p_picture
)
msg_Err
(
p_spu
,
"scaling failed"
);
}
/* */
if
(
p_picture
)
{
p_region
->
p_private
=
subpicture_region_private_New
(
&
p_picture
->
format
);
if
(
p_region
->
p_private
)
{
p_region
->
p_private
->
p_picture
=
p_picture
;
if
(
!
p_region
->
p_private
->
p_picture
)
{
subpicture_region_private_Delete
(
p_region
->
p_private
);
p_region
->
p_private
=
NULL
;
}
}
else
{
picture_Release
(
p_picture
);
}
}
}
/* And use the scaled picture */
if
(
p_region
->
p_private
)
{
region_fmt
=
p_region
->
p_private
->
fmt
;
p_region_picture
=
p_region
->
p_private
->
p_picture
;
}
}
/* Force cropping if requested */
if
(
b_force_crop
)
{
{
int
i_crop_x
=
spu_scale_w
(
p_sys
->
i_crop_x
,
scale_size
);
UpdateSPU
(
p_spu
,
p_input
);
int
i_crop_y
=
spu_scale_h
(
p_sys
->
i_crop_y
,
scale_size
);
var_Create
(
p_input
,
"highlight"
,
VLC_VAR_BOOL
);
int
i_crop_width
=
spu_scale_w
(
p_sys
->
i_crop_width
,
scale_size
);
var_AddCallback
(
p_input
,
"highlight"
,
CropCallback
,
p_spu
);
int
i_crop_height
=
spu_scale_h
(
p_sys
->
i_crop_height
,
scale_size
);
/* Find the intersection */
if
(
i_crop_x
+
i_crop_width
<=
i_x_offset
||
i_x_offset
+
(
int
)
region_fmt
.
i_visible_width
<
i_crop_x
||
i_crop_y
+
i_crop_height
<=
i_y_offset
||
i_y_offset
+
(
int
)
region_fmt
.
i_visible_height
<
i_crop_y
)
{
/* No intersection */
region_fmt
.
i_visible_width
=
region_fmt
.
i_visible_height
=
0
;
}
else
{
int
i_x
,
i_y
,
i_x_end
,
i_y_end
;
i_x
=
__MAX
(
i_crop_x
,
i_x_offset
);
i_y
=
__MAX
(
i_crop_y
,
i_y_offset
);
i_x_end
=
__MIN
(
i_crop_x
+
i_crop_width
,
i_x_offset
+
(
int
)
region_fmt
.
i_visible_width
);
i_y_end
=
__MIN
(
i_crop_y
+
i_crop_height
,
i_y_offset
+
(
int
)
region_fmt
.
i_visible_height
);
region_fmt
.
i_x_offset
=
i_x
-
i_x_offset
;
region_fmt
.
i_y_offset
=
i_y
-
i_y_offset
;
region_fmt
.
i_visible_width
=
i_x_end
-
i_x
;
region_fmt
.
i_visible_height
=
i_y_end
-
i_y
;
i_x_offset
=
__MAX
(
i_x
,
0
);
i_y_offset
=
__MAX
(
i_y
,
0
);
}
}
subpicture_region_t
*
p_dst
=
*
pp_dst
=
subpicture_region_New
(
&
region_fmt
);
vlc_mutex_lock
(
&
p_spu
->
p
->
lock
);
if
(
p_dst
)
p_spu
->
p
->
p_input
=
p_input
;
{
p_dst
->
i_x
=
i_x_offset
;
p_dst
->
i_y
=
i_y_offset
;
p_dst
->
i_align
=
0
;
p_dst
->
p_picture
=
picture_Hold
(
p_region_picture
);
int
i_fade_alpha
=
255
;
if
(
p_subpic
->
b_fade
)
{
mtime_t
fade_start
=
(
p_subpic
->
i_stop
+
p_subpic
->
i_start
)
/
2
;
if
(
fade_start
<=
render_date
&&
fade_start
<
p_subpic
->
i_stop
)
FilterRelease
(
p_spu
->
p
->
p_text
);
i_fade_alpha
=
255
*
(
p_subpic
->
i_stop
-
render_date
)
/
p_spu
->
p
->
p_text
=
NULL
;
(
p_subpic
->
i_stop
-
fade_start
);
SpuRenderCreateAndLoadText
(
p_spu
);
}
p_dst
->
i_alpha
=
i_fade_alpha
*
p_subpic
->
i_alpha
*
p_region
->
i_alpha
/
65025
;
}
exit:
vlc_mutex_unlock
(
&
p_spu
->
p
->
lock
);
if
(
b_rerender_text
)
}
{
else
/* Some forms of subtitles need to be re-rendered more than
{
* once, eg. karaoke. We therefore restore the region to its
vlc_mutex_lock
(
&
p_spu
->
p
->
lock
);
* pre-rendered state, so the next time through everything is
p_spu
->
p
->
p_input
=
NULL
;
* calculated again.
vlc_mutex_unlock
(
&
p_spu
->
p
->
lock
);
*/
if
(
p_region
->
p_picture
)
/* Delete callbacks */
{
var_DelCallback
(
p_input
,
"highlight"
,
CropCallback
,
p_spu
);
picture_Release
(
p_region
->
p_picture
);
var_Destroy
(
p_input
,
"highlight"
);
p_region
->
p_picture
=
NULL
;
}
if
(
p_region
->
p_private
)
{
subpicture_region_private_Delete
(
p_region
->
p_private
);
p_region
->
p_private
=
NULL
;
}
p_region
->
i_align
&=
~
SUBPICTURE_RENDERED
;
}
}
if
(
b_restore_format
)
p_region
->
fmt
=
fmt_original
;
}
}
/**
/**
* This function compares two 64 bits integers.
* Inform the SPU filters of mouse event
* It can be used by qsort.
*/
*/
static
int
IntegerCmp
(
int64_t
i0
,
int64_t
i1
)
int
spu_ProcessMouse
(
spu_t
*
p_spu
,
const
vlc_mouse_t
*
p_mouse
,
const
video_format_t
*
p_fmt
)
{
{
return
i0
<
i1
?
-
1
:
i0
>
i1
?
1
:
0
;
spu_private_t
*
p_sys
=
p_spu
->
p
;
vlc_mutex_lock
(
&
p_sys
->
chain_lock
);
filter_chain_MouseEvent
(
p_sys
->
p_chain
,
p_mouse
,
p_fmt
);
vlc_mutex_unlock
(
&
p_sys
->
chain_lock
);
return
VLC_SUCCESS
;
}
}
/**
/**
* This function compares 2 subpictures using the following properties
* Display a subpicture
* (ordered by priority)
* 1. absolute positionning
* 2. start time
* 3. creation order (per channel)
*
* It can be used by qsort.
*
*
* XXX spu_RenderSubpictures depends heavily on this order.
* Remove the reservation flag of a subpicture, which will cause it to be
* ready for display.
* \param p_spu the subpicture unit object
* \param p_subpic the subpicture to display
*/
*/
static
int
SubpictureCmp
(
const
void
*
s0
,
const
void
*
s1
)
void
spu_PutSubpicture
(
spu_t
*
p_spu
,
subpicture_t
*
p_subpic
)
{
{
subpicture_t
*
p_subpic0
=
*
(
subpicture_t
**
)
s0
;
spu_private_t
*
p_sys
=
p_spu
->
p
;
subpicture_t
*
p_subpic1
=
*
(
subpicture_t
**
)
s1
;
int
r
;
r
=
IntegerCmp
(
!
p_subpic0
->
b_absolute
,
!
p_subpic1
->
b_absolute
);
/* SPU_DEFAULT_CHANNEL always reset itself */
if
(
!
r
)
if
(
p_subpic
->
i_channel
==
SPU_DEFAULT_CHANNEL
)
r
=
IntegerCmp
(
p_subpic0
->
i_start
,
p_subpic1
->
i_start
);
spu_ClearChannel
(
p_spu
,
SPU_DEFAULT_CHANNEL
);
if
(
!
r
)
r
=
IntegerCmp
(
p_subpic0
->
i_channel
,
p_subpic1
->
i_channel
);
if
(
!
r
)
r
=
IntegerCmp
(
p_subpic0
->
i_order
,
p_subpic1
->
i_order
);
return
r
;
}
/*****************************************************************************
/* p_private is for spu only and cannot be non NULL here */
* Object variables callbacks
for
(
subpicture_region_t
*
r
=
p_subpic
->
p_region
;
r
!=
NULL
;
r
=
r
->
p_next
)
*****************************************************************************/
assert
(
r
->
p_private
==
NULL
);
/*****************************************************************************
/* */
* UpdateSPU: update subpicture settings
vlc_mutex_lock
(
&
p_sys
->
lock
);
*****************************************************************************
if
(
SpuHeapPush
(
&
p_sys
->
heap
,
p_subpic
)
)
* This function is called from CropCallback and at initialization time, to
{
* retrieve crop information from the input.
vlc_mutex_unlock
(
&
p_sys
->
lock
);
*****************************************************************************/
msg_Err
(
p_spu
,
"subpicture heap full"
);
static
void
UpdateSPU
(
spu_t
*
p_spu
,
vlc_object_t
*
p_object
)
subpicture_Delete
(
p_subpic
);
return
;
}
vlc_mutex_unlock
(
&
p_sys
->
lock
);
}
subpicture_t
*
spu_Render
(
spu_t
*
p_spu
,
const
video_format_t
*
p_fmt_dst
,
const
video_format_t
*
p_fmt_src
,
mtime_t
render_subtitle_date
,
mtime_t
render_osd_date
,
bool
b_subtitle_only
)
{
{
spu_private_t
*
p_sys
=
p_spu
->
p
;
spu_private_t
*
p_sys
=
p_spu
->
p
;
vlc_value_t
val
;
/* Update sub-filter chain */
vlc_mutex_lock
(
&
p_sys
->
lock
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
char
*
psz_chain_update
=
p_sys
->
psz_chain_update
;
p_sys
->
psz_chain_update
=
NULL
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
p_sys
->
b_force_palette
=
false
;
vlc_mutex_lock
(
&
p_sys
->
chain_lock
);
p_sys
->
b_force_crop
=
false
;
if
(
psz_chain_update
)
if
(
var_Get
(
p_object
,
"highlight"
,
&
val
)
||
!
val
.
b_bool
)
{
{
vlc_mutex_unlock
(
&
p_sys
->
lock
);
filter_chain_Reset
(
p_sys
->
p_chain
,
NULL
,
NULL
);
return
;
filter_chain_AppendFromString
(
p_spu
->
p
->
p_chain
,
psz_chain_update
);
free
(
psz_chain_update
);
}
}
/* Run subpicture filters */
filter_chain_SubFilter
(
p_sys
->
p_chain
,
render_osd_date
);
vlc_mutex_unlock
(
&
p_sys
->
chain_lock
);
p_sys
->
b_force_crop
=
true
;
/* Get the sorted list of subpicture to render */
p_sys
->
i_crop_x
=
var_GetInteger
(
p_object
,
"x-start"
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
p_sys
->
i_crop_y
=
var_GetInteger
(
p_object
,
"y-start"
);
p_sys
->
i_crop_width
=
var_GetInteger
(
p_object
,
"x-end"
)
-
p_sys
->
i_crop_x
;
p_sys
->
i_crop_height
=
var_GetInteger
(
p_object
,
"y-end"
)
-
p_sys
->
i_crop_y
;
if
(
var_Get
(
p_object
,
"menu-palette"
,
&
val
)
==
VLC_SUCCESS
)
subpicture_t
*
p_list
=
SpuSortSubpictures
(
p_spu
,
render_subtitle_date
,
render_osd_date
,
b_subtitle_only
);
if
(
!
p_list
)
{
{
memcpy
(
p_sys
->
palette
,
val
.
p_address
,
16
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
p_sys
->
b_force_palette
=
true
;
return
NULL
;
}
}
/* Render the current list of subpictures */
subpicture_t
*
p_render
=
SpuRenderSubpictures
(
p_spu
,
p_fmt_dst
,
p_list
,
p_fmt_src
,
render_subtitle_date
,
render_osd_date
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
msg_Dbg
(
p_object
,
"crop: %i,%i,%i,%i, palette forced: %i"
,
return
p_render
;
p_sys
->
i_crop_x
,
p_sys
->
i_crop_y
,
p_sys
->
i_crop_width
,
p_sys
->
i_crop_height
,
p_sys
->
b_force_palette
);
}
}
/*****************************************************************************
void
spu_OffsetSubtitleDate
(
spu_t
*
p_spu
,
mtime_t
i_duration
)
* CropCallback: called when the highlight properties are changed
*****************************************************************************
* This callback is called from the input thread when we need cropping
*****************************************************************************/
static
int
CropCallback
(
vlc_object_t
*
p_object
,
char
const
*
psz_var
,
vlc_value_t
oldval
,
vlc_value_t
newval
,
void
*
p_data
)
{
{
VLC_UNUSED
(
oldval
);
VLC_UNUSED
(
newval
);
VLC_UNUSED
(
psz_var
)
;
spu_private_t
*
p_sys
=
p_spu
->
p
;
UpdateSPU
(
(
spu_t
*
)
p_data
,
p_object
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
return
VLC_SUCCESS
;
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
{
spu_heap_entry_t
*
p_entry
=
&
p_sys
->
heap
.
p_entry
[
i
];
subpicture_t
*
p_current
=
p_entry
->
p_subpicture
;
if
(
p_current
&&
p_current
->
b_subtitle
)
{
if
(
p_current
->
i_start
>
0
)
p_current
->
i_start
+=
i_duration
;
if
(
p_current
->
i_stop
>
0
)
p_current
->
i_stop
+=
i_duration
;
}
}
vlc_mutex_unlock
(
&
p_sys
->
lock
);
}
}
/*****************************************************************************
int
spu_RegisterChannel
(
spu_t
*
p_spu
)
* Buffers allocation callbacks for the filters
*****************************************************************************/
static
int
spu_get_attachments
(
filter_t
*
p_filter
,
input_attachment_t
***
ppp_attachment
,
int
*
pi_attachment
)
{
{
spu_
t
*
p_spu
=
p_filter
->
p_owner
->
p_spu
;
spu_
private_t
*
p_sys
=
p_spu
->
p
;
int
i_ret
=
VLC_EGENERIC
;
vlc_mutex_lock
(
&
p_sys
->
lock
);
if
(
p_spu
->
p
->
p_input
)
int
i_channel
=
p_sys
->
i_channel
++
;
i_ret
=
input_Control
(
(
input_thread_t
*
)
p_spu
->
p
->
p_input
,
vlc_mutex_unlock
(
&
p_sys
->
lock
);
INPUT_GET_ATTACHMENTS
,
ppp_attachment
,
pi_attachment
);
return
i_channel
;
return
i_ret
;
}
}
static
subpicture_t
*
sub_new_buffer
(
filter_t
*
p_filter
)
void
spu_ClearChannel
(
spu_t
*
p_spu
,
int
i_channel
)
{
{
filter_owner_sys_t
*
p_sys
=
p_filter
->
p_owner
;
spu_private_t
*
p_sys
=
p_spu
->
p
;
subpicture_t
*
p_subpicture
=
subpicture_New
(
NULL
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
if
(
p_subpicture
)
p_subpicture
->
i_channel
=
p_sys
->
i_channel
;
return
p_subpicture
;
}
static
void
sub_del_buffer
(
filter_t
*
p_filter
,
subpicture_t
*
p_subpic
)
{
VLC_UNUSED
(
p_filter
);
subpicture_Delete
(
p_subpic
);
}
static
picture_t
*
spu_new_video_buffer
(
filter_t
*
p_filter
)
{
const
video_format_t
*
p_fmt
=
&
p_filter
->
fmt_out
.
video
;
VLC_UNUSED
(
p_filter
);
for
(
int
i_subpic
=
0
;
i_subpic
<
VOUT_MAX_SUBPICTURES
;
i_subpic
++
)
return
picture_NewFromFormat
(
p_fmt
);
{
}
spu_heap_entry_t
*
p_entry
=
&
p_sys
->
heap
.
p_entry
[
i_subpic
];
static
void
spu_del_video_buffer
(
filter_t
*
p_filter
,
picture_t
*
p_picture
)
subpicture_t
*
p_subpic
=
p_entry
->
p_subpicture
;
{
VLC_UNUSED
(
p_filter
);
if
(
!
p_subpic
)
picture_Release
(
p_picture
);
continue
;
if
(
p_subpic
->
i_channel
!=
i_channel
&&
(
i_channel
!=
-
1
||
p_subpic
->
i_channel
==
SPU_DEFAULT_CHANNEL
)
)
continue
;
/* You cannot delete subpicture outside of spu_SortSubpictures */
p_entry
->
b_reject
=
true
;
}
vlc_mutex_unlock
(
&
p_sys
->
lock
);
}
}
static
int
SubFilterAllocationInit
(
filter_t
*
p_filter
,
void
*
p_data
)
void
spu_ChangeFilters
(
spu_t
*
p_spu
,
const
char
*
psz_filters
)
{
{
spu_t
*
p_spu
=
p_data
;
spu_private_t
*
p_sys
=
p_spu
->
p
;
filter_owner_sys_t
*
p_sys
=
malloc
(
sizeof
(
filter_owner_sys_t
)
);
if
(
!
p_sys
)
return
VLC_EGENERIC
;
p_filter
->
pf_sub_buffer_new
=
sub_new_buffer
;
vlc_mutex_lock
(
&
p_sys
->
lock
);
p_filter
->
pf_sub_buffer_del
=
sub_del_buffer
;
p_filter
->
p_owner
=
p_sys
;
free
(
p_sys
->
psz_chain_update
);
p_sys
->
i_channel
=
spu_RegisterChannel
(
p_spu
);
p_sys
->
psz_chain_update
=
strdup
(
psz_filters
);
p_sys
->
p_spu
=
p_spu
;
return
VLC_SUCCESS
;
vlc_mutex_unlock
(
&
p_sys
->
lock
)
;
}
}
static
void
SubFilterAllocationClean
(
filter_t
*
p_filter
)
void
spu_ChangeMargin
(
spu_t
*
p_spu
,
int
i_margin
)
{
{
filter_owner_sys_t
*
p_sys
=
p_filter
->
p_owner
;
spu_private_t
*
p_sys
=
p_spu
->
p
;
spu_ClearChannel
(
p_sys
->
p_spu
,
p_sys
->
i_channel
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
free
(
p_filter
->
p_owner
);
p_sys
->
i_margin
=
i_margin
;
vlc_mutex_unlock
(
&
p_sys
->
lock
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment