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
Show 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
++
)
{
spu_area_t
sub
=
spu_area_scaled
(
p_sub
[
i
]
);
if
(
!
p_subpic
->
p_region
)
if
(
!
spu_area_overlap
(
a
,
sub
)
)
continue
;
continue
;
/* FIXME when possible use a better rendering size than source size
if
(
i_align
&
SUBPICTURE_ALIGN_TOP
)
* (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
)
/* We go down */
msg_Err
(
p_spu
,
"unsupported original picture size %dx%d"
,
int
i_y
=
sub
.
i_y
+
sub
.
i_height
;
i_render_width
,
i_render_height
);
a
.
i_y
=
i_y
;
b_moved
=
true
;
p_subpic
->
i_original_picture_width
=
i_render_width
=
i_source_video_width
;
p_subpic
->
i_original_picture_height
=
i_render_height
=
i_source_video_height
;
}
}
else
if
(
i_align
&
SUBPICTURE_ALIGN_BOTTOM
)
if
(
p_sys
->
p_text
)
{
{
p_sys
->
p_text
->
fmt_out
.
video
.
i_width
=
/* We go up */
p_sys
->
p_text
->
fmt_out
.
video
.
i_visible_width
=
i_render_width
;
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
;
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
);
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_TOP
)
{
{
subpicture_region_t
**
pp_last
=
&
p_output
->
p_region
;
i_y
=
i_delta_y
;
while
(
*
pp_last
)
pp_last
=
&
(
*
pp_last
)
->
p_next
;
*
pp_last
=
p_render
;
}
}
else
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_BOTTOM
)
if
(
p_subpic
->
b_subtitle
)
{
{
area
=
spu_area_unscaled
(
area
,
scale
);
i_y
=
p_subpic
->
i_original_picture_height
-
p_region
->
fmt
.
i_height
-
i_delta_y
;
if
(
!
p_subpic
->
b_absolute
&&
area
.
i_width
>
0
&&
area
.
i_height
>
0
)
}
else
{
{
p_region
->
i_x
=
area
.
i_x
;
i_y
=
p_subpic
->
i_original_picture_height
/
2
-
p_region
->
fmt
.
i_height
/
2
;
p_region
->
i_y
=
area
.
i_y
;
}
}
if
(
p_subtitle_area
)
p_subtitle_area
[
i_subtitle_area
++
]
=
area
;
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_LEFT
)
{
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
;
}
}
if
(
p_subpic
->
b_subtitle
)
else
p_subpic
->
b_absolute
=
true
;
{
i_x
=
p_subpic
->
i_original_picture_width
/
2
-
p_region
->
fmt
.
i_width
/
2
;
}
}
/* */
if
(
p_subpic
->
b_absolute
)
if
(
p_subtitle_area
!=
p_subtitle_area_buffer
)
{
free
(
p_subtitle_area
);
i_x
=
i_delta_x
;
i_y
=
i_delta_y
;
}
return
p_output
;
*
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
,
render_osd_date
,
b_subtitle_only
);
if
(
!
p_list
)
{
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
NULL
;
}
/* Render the current list of subpictures */
/* Compute the margin which is expressed in destination pixel unit
subpicture_t
*
p_render
=
SpuRenderSubpictures
(
p_spu
,
* The margin is applied only to subtitle and when no forced crop is
p_fmt_dst
,
* requested (dvd menu) */
p_list
,
int
i_margin_y
=
0
;
p_fmt_src
,
if
(
!
b_force_crop
&&
p_subpic
->
b_subtitle
)
render_subtitle_date
,
i_margin_y
=
spu_invscale_h
(
p_sys
->
i_margin
,
scale_size
);
render_osd_date
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
return
p_render
;
}
void
spu_OffsetSubtitleDate
(
spu_t
*
p_spu
,
mtime_t
i_duration
)
/* Place the picture
{
* We compute the position in the rendered size */
spu_private_t
*
p_sys
=
p_spu
->
p
;
SpuRegionPlace
(
&
i_x_offset
,
&
i_y_offset
,
p_subpic
,
p_region
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
/* Save this position for subtitle overlap support
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
* it is really important that there are given without scale_size applied */
{
*
p_dst_area
=
spu_area_create
(
i_x_offset
,
i_y_offset
,
spu_heap_entry_t
*
p_entry
=
&
p_sys
->
heap
.
p_entry
[
i
];
p_region
->
fmt
.
i_width
,
p_region
->
fmt
.
i_height
,
subpicture_t
*
p_current
=
p_entry
->
p_subpicture
;
scale_size
)
;
if
(
p_current
&&
p_current
->
b_subtitle
)
/* Handle overlapping subtitles when possible */
if
(
p_subpic
->
b_subtitle
&&
!
p_subpic
->
b_absolute
)
{
{
if
(
p_current
->
i_start
>
0
)
SpuAreaFixOverlap
(
p_dst_area
,
p_subtitle_area
,
i_subtitle_area
,
p_current
->
i_start
+=
i_duration
;
p_region
->
i_align
);
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
)
{
spu_private_t
*
p_sys
=
p_spu
->
p
;
vlc_mutex_lock
(
&
p_sys
->
lock
);
/* we copy the area: for the subtitle overlap support we want
int
i_channel
=
p_sys
->
i_channel
++
;
* to only save the area without margin applied */
vlc_mutex_unlock
(
&
p_sys
->
lock
)
;
spu_area_t
restrained
=
*
p_dst_area
;
return
i_channel
;
/* 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_ClearChannel
(
spu_t
*
p_spu
,
int
i_channel
)
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 */
i_x_offset
=
spu_scale_w
(
restrained
.
i_x
,
restrained
.
scale
);
i_y_offset
=
spu_scale_h
(
restrained
.
i_y
,
restrained
.
scale
);
for
(
int
i_subpic
=
0
;
i_subpic
<
VOUT_MAX_SUBPICTURES
;
i_subpic
++
)
/* */
if
(
b_force_palette
)
{
{
spu_heap_entry_t
*
p_entry
=
&
p_sys
->
heap
.
p_entry
[
i_subpic
]
;
video_palette_t
*
p_palette
=
p_region
->
fmt
.
p_palette
;
subpicture_t
*
p_subpic
=
p_entry
->
p_subpictur
e
;
video_palette_t
palett
e
;
if
(
!
p_subpic
)
/* We suppose DVD palette here */
continue
;
palette
.
i_entries
=
4
;
if
(
p_subpic
->
i_channel
!=
i_channel
&&
(
i_channel
!=
-
1
||
p_subpic
->
i_channel
==
SPU_DEFAULT_CHANNEL
)
)
for
(
int
i
=
0
;
i
<
4
;
i
++
)
continue
;
for
(
int
j
=
0
;
j
<
4
;
j
++
)
palette
.
palette
[
i
][
j
]
=
p_sys
->
palette
[
i
][
j
];
/* You cannot delete subpicture outside of spu_SortSubpictures */
if
(
p_palette
->
i_entries
==
palette
.
i_entries
)
p_entry
->
b_reject
=
true
;
{
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
;
}
}
vlc_mutex_unlock
(
&
p_sys
->
lock
);
/* */
}
region_fmt
=
p_region
->
fmt
;
p_region_picture
=
p_region
->
p_picture
;
void
spu_ChangeFilters
(
spu_t
*
p_spu
,
const
char
*
psz_filters
)
{
spu_private_t
*
p_sys
=
p_spu
->
p
;
vlc_mutex_lock
(
&
p_sys
->
lock
);
free
(
p_sys
->
psz_chain_update
);
p_sys
->
psz_chain_update
=
strdup
(
psz_filters
);
vlc_mutex_unlock
(
&
p_sys
->
lock
);
}
void
spu_ChangeMargin
(
spu_t
*
p_spu
,
int
i_margin
)
/* Scale from rendered size to destination size */
{
if
(
p_sys
->
p_scale
&&
p_sys
->
p_scale
->
p_module
&&
spu_private_t
*
p_sys
=
p_spu
->
p
;
(
!
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
);
vlc_mutex_lock
(
&
p_sys
->
lock
);
/* Destroy the cache if unusable */
p_sys
->
i_margin
=
i_margin
;
if
(
p_region
->
p_private
)
vlc_mutex_unlock
(
&
p_sys
->
lock
);
{
}
subpicture_region_private_t
*
p_private
=
p_region
->
p_private
;
bool
b_changed
=
false
;
/*
*/
/* Check resize changes
*/
static
void
SubpictureChain
(
subpicture_t
**
pp_head
,
subpicture_t
*
p_subpic
)
if
(
i_dst_width
!=
p_private
->
fmt
.
i_width
||
{
i_dst_height
!=
p_private
->
fmt
.
i_height
)
p_subpic
->
p_next
=
*
pp_head
;
b_changed
=
true
;
*
pp_head
=
p_subpic
;
/* Check forced palette changes */
}
if
(
b_changed_palette
)
b_changed
=
true
;
/*****************************************************************************
if
(
b_changed
)
* heap managment
*****************************************************************************/
static
void
SpuHeapInit
(
spu_heap_t
*
p_heap
)
{
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
{
{
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i
];
subpicture_region_private_Delete
(
p_private
);
p_region
->
p_private
=
NULL
;
e
->
p_subpicture
=
NULL
;
}
e
->
b_reject
=
false
;
}
}
}
static
int
SpuHeapPush
(
spu_heap_t
*
p_heap
,
subpicture_t
*
p_subpic
)
/* Scale if needed into cache */
{
if
(
!
p_region
->
p_private
&&
i_dst_width
>
0
&&
i_dst_height
>
0
)
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
{
{
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i
]
;
filter_t
*
p_scale
=
p_sys
->
p_scale
;
if
(
e
->
p_subpicture
)
picture_t
*
p_picture
=
p_region
->
p_picture
;
continue
;
picture_Hold
(
p_picture
)
;
e
->
p_subpicture
=
p_subpic
;
/* Convert YUVP to YUVA/RGBA first for better scaling quality */
e
->
b_reject
=
false
;
if
(
b_using_palette
)
return
VLC_SUCCESS
;
{
}
filter_t
*
p_scale_yuvp
=
p_sys
->
p_scale_yuvp
;
return
VLC_EGENERIC
;
}
static
void
SpuHeapDeleteAt
(
spu_heap_t
*
p_heap
,
int
i_index
)
p_scale_yuvp
->
fmt_in
.
video
=
p_region
->
fmt
;
{
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i_index
];
if
(
e
->
p_subpicture
)
/* TODO converting to RGBA for RGB video output is better */
subpicture_Delete
(
e
->
p_subpicture
);
p_scale_yuvp
->
fmt_out
.
video
=
p_region
->
fmt
;
p_scale_yuvp
->
fmt_out
.
video
.
i_chroma
=
VLC_CODEC_YUVA
;
e
->
p_subpicture
=
NULL
;
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
);
}
}
static
int
SpuHeapDeleteSubpicture
(
spu_heap_t
*
p_heap
,
subpicture_t
*
p_subpic
)
/* Conversion(except from YUVP)/Scaling */
{
if
(
p_picture
&&
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
(
p_picture
->
format
.
i_width
!=
i_dst_width
||
p_picture
->
format
.
i_height
!=
i_dst_height
)
)
{
{
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i
];
p_scale
->
fmt_in
.
video
=
p_picture
->
format
;
p_scale
->
fmt_out
.
video
=
p_picture
->
format
;
if
(
e
->
p_subpicture
!=
p_subpic
)
p_scale
->
fmt_out
.
video
.
i_width
=
i_dst_width
;
continue
;
p_scale
->
fmt_out
.
video
.
i_height
=
i_dst_height
;
SpuHeapDeleteAt
(
p_heap
,
i
);
p_scale
->
fmt_out
.
video
.
i_visible_width
=
return
VLC_SUCCESS
;
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"
);
}
}
return
VLC_EGENERIC
;
}
static
void
SpuHeapClean
(
spu_heap_t
*
p_heap
)
/* */
{
if
(
p_picture
)
for
(
int
i
=
0
;
i
<
VOUT_MAX_SUBPICTURES
;
i
++
)
{
{
spu_heap_entry_t
*
e
=
&
p_heap
->
p_entry
[
i
];
p_region
->
p_private
=
subpicture_region_private_New
(
&
p_picture
->
format
);
if
(
e
->
p_subpicture
)
if
(
p_region
->
p_private
)
subpicture_Delete
(
e
->
p_subpicture
);
{
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
);
}
}
}
}
}
static
void
FilterRelease
(
filter_t
*
p_filter
)
/* And use the scaled picture */
{
if
(
p_region
->
p_private
)
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
*
p_spu
)
{
filter_t
*
p_text
;
assert
(
!
p_spu
->
p
->
p_text
);
p_spu
->
p
->
p_text
=
p_text
=
vlc_custom_create
(
p_spu
,
sizeof
(
filter_t
),
VLC_OBJECT_GENERIC
,
"spu text"
);
if
(
!
p_text
)
return
;
p_text
->
p_owner
=
xmalloc
(
sizeof
(
*
p_text
->
p_owner
)
);
p_text
->
p_owner
->
p_spu
=
p_spu
;
es_format_Init
(
&
p_text
->
fmt_in
,
VIDEO_ES
,
0
);
es_format_Init
(
&
p_text
->
fmt_out
,
VIDEO_ES
,
0
);
p_text
->
fmt_out
.
video
.
i_width
=
p_text
->
fmt_out
.
video
.
i_visible_width
=
32
;
p_text
->
fmt_out
.
video
.
i_height
=
p_text
->
fmt_out
.
video
.
i_visible_height
=
32
;
p_text
->
pf_get_attachments
=
spu_get_attachments
;
vlc_object_attach
(
p_text
,
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"
,
region_fmt
=
p_region
->
p_private
->
fmt
;
psz_modulename
,
true
);
p_region_picture
=
p_region
->
p_private
->
p_picture
;
}
}
}
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
,
vlc_fourcc_t
i_src_chroma
,
vlc_fourcc_t
i_dst_chroma
,
bool
b_resize
)
{
filter_t
*
p_scale
;
p_scale
=
vlc_custom_create
(
p_obj
,
sizeof
(
filter_t
),
VLC_OBJECT_GENERIC
,
"scale"
);
if
(
!
p_scale
)
return
NULL
;
es_format_Init
(
&
p_scale
->
fmt_in
,
VIDEO_ES
,
0
);
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
;
es_format_Init
(
&
p_scale
->
fmt_out
,
VIDEO_ES
,
0
);
p_scale
->
fmt_out
.
video
.
i_chroma
=
i_dst_chroma
;
p_scale
->
fmt_out
.
video
.
i_width
=
p_scale
->
fmt_out
.
video
.
i_height
=
b_resize
?
16
:
32
;
p_scale
->
pf_video_buffer_new
=
spu_new_video_buffer
;
/* Force cropping if requested */
p_scale
->
pf_video_buffer_del
=
spu_del_video_buffer
;
if
(
b_force_crop
)
{
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
);
vlc_object_attach
(
p_scale
,
p_obj
);
/* Find the intersection */
p_scale
->
p_module
=
module_need
(
p_scale
,
"video filter2"
,
NULL
,
false
);
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
);
return
p_scale
;
region_fmt
.
i_x_offset
=
i_x
-
i_x_offset
;
}
region_fmt
.
i_y_offset
=
i_y
-
i_y_offset
;
static
void
SpuRenderCreateAndLoadScale
(
spu_t
*
p_spu
)
region_fmt
.
i_visible_width
=
i_x_end
-
i_x
;
{
region_fmt
.
i_visible_height
=
i_y_end
-
i_y
;
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
,
i_x_offset
=
__MAX
(
i_x
,
0
);
subpicture_t
*
p_subpic
,
subpicture_region_t
*
p_region
,
i_y_offset
=
__MAX
(
i_y
,
0
);
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
);
subpicture_region_t
*
p_dst
=
*
pp_dst
=
subpicture_region_New
(
&
region_fmt
);
if
(
p_dst
)
{
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
(
!
p_text
||
!
p_text
->
p_module
)
if
(
fade_start
<=
render_date
&&
fade_start
<
p_subpic
->
i_stop
)
goto
exit
;
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
;
}
/* Setup 3 variables which can be used to render
exit:
* time-dependent text (and effects). The first indicates
if
(
b_rerender_text
)
* the total amount of time the text will be on screen,
{
* the second the amount of time it has already been on
/* Some forms of subtitles need to be re-rendered more than
* screen (can be a negative value as text is layed out
* once, eg. karaoke. We therefore restore the region to its
* before it is rendered) and the third is a feedback
* pre-rendered state, so the next time through everything is
* variable from the renderer - if the renderer sets it
* calculated again.
* 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
);
if
(
p_region
->
p_picture
)
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
);
picture_Release
(
p_region
->
p_picture
);
p_region
->
p_picture
=
NULL
;
}
}
else
if
(
p_text
->
pf_render_text
)
if
(
p_region
->
p_private
)
{
{
p_text
->
pf_render_text
(
p_text
,
p_region
,
p_region
);
subpicture_region_private_Delete
(
p_region
->
p_private
);
p_region
->
p_private
=
NULL
;
}
}
*
pb_rerender_text
=
var_GetBool
(
p_text
,
"text-rerender"
);
p_region
->
i_align
&=
~
SUBPICTURE_RENDERED
;
}
exit:
if
(
b_restore_format
)
p_region
->
i_align
|=
SUBPICTURE_RENDERED
;
p_region
->
fmt
=
fmt_original
;
}
/**
* A few scale functions helpers.
*/
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
;
}
}
/**
/**
*
A few area functions helpers
*
This function renders all sub picture units in the list.
*/
*/
static
spu_area_t
spu_area_create
(
int
x
,
int
y
,
int
w
,
int
h
,
spu_scale_t
s
)
static
subpicture_t
*
SpuRenderSubpictures
(
spu_t
*
p_spu
,
{
const
video_format_t
*
p_fmt_dst
,
spu_area_t
a
=
{
.
i_x
=
x
,
.
i_y
=
y
,
.
i_width
=
w
,
.
i_height
=
h
,
.
scale
=
s
};
subpicture_t
*
p_subpic_list
,
return
a
;
const
video_format_t
*
p_fmt_src
,
}
mtime_t
render_subtitle_date
,
static
spu_area_t
spu_area_scaled
(
spu_area_t
a
)
mtime_t
render_osd_date
)
{
if
(
a
.
scale
.
w
==
SCALE_UNIT
&&
a
.
scale
.
h
==
SCALE_UNIT
)
return
a
;
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
);
a
.
i_height
=
spu_scale_h
(
a
.
i_height
,
a
.
scale
);
a
.
scale
=
spu_scale_unit
();
return
a
;
}
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
);
a
.
i_x
=
spu_invscale_w
(
a
.
i_x
,
s
);
a
.
i_y
=
spu_invscale_h
(
a
.
i_y
,
s
);
a
.
i_width
=
spu_invscale_w
(
a
.
i_width
,
s
);
a
.
i_height
=
spu_invscale_h
(
a
.
i_height
,
s
);
a
.
scale
=
s
;
return
a
;
}
static
bool
spu_area_overlap
(
spu_area_t
a
,
spu_area_t
b
)
{
{
const
int
i_dx
=
0
;
spu_private_t
*
p_sys
=
p_spu
->
p
;
const
int
i_dy
=
0
;
a
=
spu_area_scaled
(
a
)
;
const
int
i_source_video_width
=
p_fmt_src
->
i_width
;
b
=
spu_area_scaled
(
b
)
;
const
int
i_source_video_height
=
p_fmt_src
->
i_height
;
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
)
&&
unsigned
int
i_subpicture
;
__MAX
(
a
.
i_y
-
i_dy
,
b
.
i_y
)
<
__MIN
(
a
.
i_y
+
a
.
i_height
+
i_dy
,
b
.
i_y
+
b
.
i_height
);
subpicture_t
*
pp_subpicture
[
VOUT_MAX_SUBPICTURES
];
}
/**
unsigned
int
i_subtitle_region_count
;
* Avoid area overlapping
spu_area_t
p_subtitle_area_buffer
[
VOUT_MAX_SUBPICTURES
];
*/
spu_area_t
*
p_subtitle_area
;
static
void
SpuAreaFixOverlap
(
spu_area_t
*
p_dst
,
int
i_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
;
/*
Check for overlap
/*
Preprocess subpictures */
* XXX It is not fast O(n^2) but we should not have a lot of region */
i_subpicture
=
0
;
do
i_subtitle_region_count
=
0
;
{
for
(
subpicture_t
*
p_subpic
=
p_subpic_list
;
b_ok
=
true
;
p_subpic
!=
NULL
;
for
(
int
i
=
0
;
i
<
i_sub
;
i
++
)
p_subpic
=
p_subpic
->
p_next
)
{
{
spu_area_t
sub
=
spu_area_scaled
(
p_sub
[
i
]
);
subpicture_Update
(
p_subpic
,
p_fmt_src
,
p_fmt_dst
,
if
(
!
spu_area_overlap
(
a
,
sub
)
)
p_subpic
->
b_subtitle
?
render_subtitle_date
:
render_osd_date
);
continue
;
if
(
i_align
&
SUBPICTURE_ALIGN_TOP
)
/* */
{
if
(
p_subpic
->
b_subtitle
)
/* We go down */
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? */
for
(
subpicture_region_t
*
r
=
p_subpic
->
p_region
;
r
!=
NULL
;
r
=
r
->
p_next
)
//fprintf( stderr, "Overlap with unsupported alignment\n" );
i_subtitle_region_count
++
;
break
;
}
}
b_ok
=
false
;
/* */
break
;
pp_subpicture
[
i_subpicture
++
]
=
p_subpic
;
}
}
}
while
(
!
b_ok
);
if
(
b_moved
)
*
p_dst
=
spu_area_unscaled
(
a
,
p_dst
->
scale
);
}
/* Be sure we have at least 1 picture to process */
if
(
i_subpicture
<=
0
)
return
NULL
;
static
void
SpuAreaFitInside
(
spu_area_t
*
p_area
,
const
spu_area_t
*
p_boundary
)
subpicture_t
*
p_output
=
subpicture_New
(
NULL
);
{
spu_area_t
a
=
spu_area_scaled
(
*
p_area
);
const
int
i_error_x
=
(
a
.
i_x
+
a
.
i_width
)
-
p_boundary
->
i_width
;
/* Now order subpicture array
if
(
i_error_x
>
0
)
* XXX The order is *really* important for overlap subtitles positionning */
a
.
i_x
-=
i_error_x
;
qsort
(
pp_subpicture
,
i_subpicture
,
sizeof
(
*
pp_subpicture
),
SubpictureCmp
);
if
(
a
.
i_x
<
0
)
a
.
i_x
=
0
;
const
int
i_error_y
=
(
a
.
i_y
+
a
.
i_height
)
-
p_boundary
->
i_height
;
/* Allocate area array for subtitle overlap */
if
(
i_error_y
>
0
)
i_subtitle_area
=
0
;
a
.
i_y
-=
i_error_y
;
p_subtitle_area
=
p_subtitle_area_buffer
;
if
(
a
.
i_y
<
0
)
if
(
i_subtitle_region_count
>
sizeof
(
p_subtitle_area_buffer
)
/
sizeof
(
*
p_subtitle_area_buffer
)
)
a
.
i_y
=
0
;
p_subtitle_area
=
calloc
(
i_subtitle_region_count
,
sizeof
(
*
p_subtitle_area
)
)
;
*
p_area
=
spu_area_unscaled
(
a
,
p_area
->
scale
);
/* Process all subpictures and regions (in the right order) */
}
for
(
unsigned
int
i_index
=
0
;
i_index
<
i_subpicture
;
i_index
++
)
{
subpicture_t
*
p_subpic
=
pp_subpicture
[
i_index
];
subpicture_region_t
*
p_region
;
/**
if
(
!
p_subpic
->
p_region
)
* Place a region
continue
;
*/
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
);
/* FIXME when possible use a better rendering size than source size
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_TOP
)
* (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
)
{
{
i_y
=
i_delta_y
;
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
);
p_subpic
->
i_original_picture_width
=
i_render_width
=
i_source_video_width
;
p_subpic
->
i_original_picture_height
=
i_render_height
=
i_source_video_height
;
}
}
else
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_BOTTOM
)
if
(
p_sys
->
p_text
)
{
{
i_y
=
p_subpic
->
i_original_picture_height
-
p_region
->
fmt
.
i_height
-
i_delta_y
;
p_sys
->
p_text
->
fmt_out
.
video
.
i_width
=
p_sys
->
p_text
->
fmt_out
.
video
.
i_visible_width
=
i_render_width
;
p_sys
->
p_text
->
fmt_out
.
video
.
i_height
=
p_sys
->
p_text
->
fmt_out
.
video
.
i_visible_height
=
i_render_height
;
}
}
else
/* Compute scaling from picture to source size */
spu_scale_t
scale
=
spu_scale_createq
(
i_source_video_width
,
i_render_width
,
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
* 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
)
{
{
i_y
=
p_subpic
->
i_original_picture_height
/
2
-
p_region
->
fmt
.
i_height
/
2
;
p_region
->
fmt
.
i_sar_den
=
p_fmt_dst
->
i_sar_den
;
p_region
->
fmt
.
i_sar_num
=
p_fmt_dst
->
i_sar_num
;
}
}
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_LEFT
)
/* Take care of the aspect ratio */
if
(
p_region
->
fmt
.
i_sar_num
*
p_fmt_dst
->
i_sar_den
!=
p_region
->
fmt
.
i_sar_den
*
p_fmt_dst
->
i_sar_num
)
{
{
i_x
=
i_delta_x
;
/* FIXME FIXME what about region->i_x/i_y ? */
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
;
}
}
else
if
(
p_region
->
i_align
&
SUBPICTURE_ALIGN_RIGHT
)
/* Render all regions
* We always transform non absolute subtitle into absolute one on the
* first rendering to allow good subtitle overlap support.
*/
for
(
p_region
=
p_subpic
->
p_region
;
p_region
!=
NULL
;
p_region
=
p_region
->
p_next
)
{
{
i_x
=
p_subpic
->
i_original_picture_width
-
p_region
->
fmt
.
i_width
-
i_delta_x
;
spu_area_t
area
;
}
else
/* 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
)
{
{
i_x
=
p_subpic
->
i_original_picture_width
/
2
-
p_region
->
fmt
.
i_width
/
2
;
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_absolut
e
)
if
(
p_subpic
->
b_subtitl
e
)
{
{
i_x
=
i_delta_x
;
area
=
spu_area_unscaled
(
area
,
scale
);
i_y
=
i_delta_y
;
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
;
}
}
*
pi_x
=
i_x
;
/* */
*
pi_y
=
i_y
;
if
(
p_subtitle_area
!=
p_subtitle_area_buffer
)
free
(
p_subtitle_area
);
return
p_output
;
}
}
/**
/**
***************************************************************************
*
It will transform the provided region into another region suitable for rendering.
*
Object variables callbacks
*/
*
****************************************************************************
/
static
void
SpuRenderRegion
(
spu_t
*
p_spu
,
/*****************************************************************************
subpicture_region_t
**
pp_dst
,
spu_area_t
*
p_dst_area
,
* UpdateSPU: update subpicture settings
subpicture_t
*
p_subpic
,
subpicture_region_t
*
p_region
,
*****************************************************************************
const
spu_scale_t
scale_size
,
* This function is called from CropCallback and at initialization time, to
const
video_format_t
*
p_fmt
,
* retrieve crop information from the input.
const
spu_area_t
*
p_subtitle_area
,
int
i_subtitle_area
,
*****************************************************************************/
mtime_t
render_date
)
static
void
UpdateSPU
(
spu_t
*
p_spu
,
vlc_object_t
*
p_object
)
{
{
spu_private_t
*
p_sys
=
p_spu
->
p
;
spu_private_t
*
p_sys
=
p_spu
->
p
;
vlc_value_t
val
;
video_format_t
fmt_original
=
p_region
->
fmt
;
vlc_mutex_lock
(
&
p_sys
->
lock
);
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_sys
->
b_force_palette
=
false
;
*
p_dst_area
=
spu_area_create
(
0
,
0
,
0
,
0
,
scale_size
);
p_sys
->
b_force_crop
=
false
;
*
pp_dst
=
NULL
;
/* Render text region */
if
(
var_Get
(
p_object
,
"highlight"
,
&
val
)
||
!
val
.
b_bool
)
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) */
vlc_mutex_unlock
(
&
p_sys
->
lock
);
SpuRenderText
(
p_spu
,
&
b_rerender_text
,
p_subpic
,
p_region
,
return
;
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
p_sys
->
b_force_crop
=
true
;
* FIXME b_force_palette and b_force_crop are applied to all subpictures using palette
p_sys
->
i_crop_x
=
var_GetInteger
(
p_object
,
"x-start"
);
* instead of only the right one (being the dvd spu).
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
;
const
bool
b_using_palette
=
p_region
->
fmt
.
i_chroma
==
VLC_CODEC_YUVP
;
p_sys
->
i_crop_height
=
var_GetInteger
(
p_object
,
"y-end"
)
-
p_sys
->
i_crop_y
;
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
(
var_Get
(
p_object
,
"menu-palette"
,
&
val
)
==
VLC_SUCCESS
)
if
(
p_subpic
->
b_subtitle
&&
!
p_subpic
->
b_absolute
)
{
{
SpuAreaFixOverlap
(
p_dst_area
,
p_subtitle_area
,
i_subtitle_area
,
memcpy
(
p_sys
->
palette
,
val
.
p_address
,
16
);
p_region
->
i_align
)
;
p_sys
->
b_force_palette
=
true
;
}
}
vlc_mutex_unlock
(
&
p_sys
->
lock
);
/* we copy the area: for the subtitle overlap support we want
msg_Dbg
(
p_object
,
"crop: %i,%i,%i,%i, palette forced: %i"
,
* to only save the area without margin applied */
p_sys
->
i_crop_x
,
p_sys
->
i_crop_y
,
spu_area_t
restrained
=
*
p_dst_area
;
p_sys
->
i_crop_width
,
p_sys
->
i_crop_height
,
p_sys
->
b_force_palette
);
}
/* apply margin to subtitles and correct if they go over the picture edge */
/*****************************************************************************
if
(
p_subpic
->
b_subtitle
)
* CropCallback: called when the highlight properties are changed
restrained
.
i_y
-=
i_margin_y
;
*****************************************************************************
* 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_area_t
display
=
spu_area_create
(
0
,
0
,
p_fmt
->
i_width
,
p_fmt
->
i_height
,
UpdateSPU
(
(
spu_t
*
)
p_data
,
p_object
);
spu_scale_unit
()
)
;
return
VLC_SUCCESS
;
SpuAreaFitInside
(
&
restrained
,
&
display
);
}
/* Fix the position for the current scale_size */
/*****************************************************************************
i_x_offset
=
spu_scale_w
(
restrained
.
i_x
,
restrained
.
scale
);
* Buffers allocation callbacks for the filters
i_y_offset
=
spu_scale_h
(
restrained
.
i_y
,
restrained
.
scale
);
*****************************************************************************/
/* */
static
subpicture_t
*
sub_new_buffer
(
filter_t
*
p_filter
)
if
(
b_force_palette
)
{
{
filter_owner_sys_t
*
p_sys
=
p_filter
->
p_owner
;
video_palette_t
*
p_palette
=
p_region
->
fmt
.
p_palette
;
video_palette_t
palette
;
/* We suppose DVD palette here */
subpicture_t
*
p_subpicture
=
subpicture_New
(
NULL
);
palette
.
i_entries
=
4
;
if
(
p_subpicture
)
for
(
int
i
=
0
;
i
<
4
;
i
++
)
p_subpicture
->
i_channel
=
p_sys
->
i_channel
;
for
(
int
j
=
0
;
j
<
4
;
j
++
)
return
p_subpicture
;
palette
.
palette
[
i
][
j
]
=
p_sys
->
palette
[
i
][
j
];
}
static
void
sub_del_buffer
(
filter_t
*
p_filter
,
subpicture_t
*
p_subpic
)
{
VLC_UNUSED
(
p_filter
);
subpicture_Delete
(
p_subpic
);
}
if
(
p_palette
->
i_entries
==
palette
.
i_entries
)
static
int
SubFilterAllocationInit
(
filter_t
*
p_filter
,
void
*
p_data
)
{
{
for
(
int
i
=
0
;
i
<
p_palette
->
i_entries
;
i
++
)
spu_t
*
p_spu
=
p_data
;
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
;
}
/* */
filter_owner_sys_t
*
p_sys
=
malloc
(
sizeof
(
filter_owner_sys_t
)
);
region_fmt
=
p_region
->
fmt
;
if
(
!
p_sys
)
p_region_picture
=
p_region
->
p_picture
;
return
VLC_EGENERIC
;
p_filter
->
pf_sub_buffer_new
=
sub_new_buffer
;
p_filter
->
pf_sub_buffer_del
=
sub_del_buffer
;
/* Scale from rendered size to destination size */
p_filter
->
p_owner
=
p_sys
;
if
(
p_sys
->
p_scale
&&
p_sys
->
p_scale
->
p_module
&&
p_sys
->
i_channel
=
spu_RegisterChannel
(
p_spu
);
(
!
b_using_palette
||
(
p_sys
->
p_scale_yuvp
&&
p_sys
->
p_scale_yuvp
->
p_module
)
)
&&
p_sys
->
p_spu
=
p_spu
;
(
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 */
return
VLC_SUCCESS
;
if
(
p_region
->
p_private
)
}
{
subpicture_region_private_t
*
p_private
=
p_region
->
p_private
;
bool
b_changed
=
false
;
/* Check resize changes */
static
void
SubFilterAllocationClean
(
filter_t
*
p_filter
)
if
(
i_dst_width
!=
p_private
->
fmt
.
i_width
||
{
i_dst_height
!=
p_private
->
fmt
.
i_height
)
filter_owner_sys_t
*
p_sys
=
p_filter
->
p_owner
;
b_changed
=
true
;
/* Check forced palette changes */
spu_ClearChannel
(
p_sys
->
p_spu
,
p_sys
->
i_channel
);
if
(
b_changed_palette
)
free
(
p_filter
->
p_owner
);
b_changed
=
true
;
}
if
(
b_changed
)
/*****************************************************************************
{
* Public API
subpicture_region_private_Delete
(
p_private
);
*****************************************************************************/
p_region
->
p_private
=
NULL
;
}
}
/* Scale if needed into cache */
#undef spu_Create
if
(
!
p_region
->
p_private
&&
i_dst_width
>
0
&&
i_dst_height
>
0
)
/**
{
* Creates the subpicture unit
filter_t
*
p_scale
=
p_sys
->
p_scale
;
*
* \param p_this the parent object which creates the subpicture unit
*/
spu_t
*
spu_Create
(
vlc_object_t
*
p_this
)
{
spu_t
*
p_spu
;
spu_private_t
*
p_sys
;
picture_t
*
p_picture
=
p_region
->
p_picture
;
p_spu
=
vlc_custom_create
(
p_this
,
sizeof
(
spu_t
)
+
sizeof
(
spu_private_t
),
picture_Hold
(
p_picture
);
VLC_OBJECT_GENERIC
,
"subpicture"
);
if
(
!
p_spu
)
return
NULL
;
vlc_object_attach
(
p_spu
,
p_this
);
/* Convert YUVP to YUVA/RGBA first for better scaling quality */
/* Initialize spu fields */
if
(
b_using_palette
)
p_spu
->
p
=
p_sys
=
(
spu_private_t
*
)
&
p_spu
[
1
];
{
filter_t
*
p_scale_yuvp
=
p_sys
->
p_scale_yuvp
;
p_scale_yuvp
->
fmt_in
.
video
=
p_region
->
fmt
;
/* Initialize private fields */
vlc_mutex_init
(
&
p_sys
->
lock
);
/* TODO converting to RGBA for RGB video output is better */
SpuHeapInit
(
&
p_sys
->
heap
);
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
);
p_sys
->
p_text
=
NULL
;
if
(
!
p_picture
)
p_sys
->
p_scale
=
NULL
;
{
p_sys
->
p_scale_yuvp
=
NULL
;
/* 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 */
p_sys
->
i_margin
=
var_InheritInteger
(
p_spu
,
"sub-margin"
);
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
;
/* Register the default subpicture channel */
p_scale
->
fmt_out
.
video
.
i_height
=
i_dst_height
;
p_sys
->
i_channel
=
SPU_DEFAULT_CHANNEL
+
1
;
p_scale
->
fmt_out
.
video
.
i_visible_width
=
p_sys
->
psz_chain_update
=
NULL
;
spu_scale_w
(
p_region
->
fmt
.
i_visible_width
,
scale_size
);
vlc_mutex_init
(
&
p_sys
->
chain_lock
);
p_scale
->
fmt_out
.
video
.
i_visible_height
=
p_sys
->
p_chain
=
filter_chain_New
(
p_spu
,
"sub filter"
,
false
,
spu_scale_h
(
p_region
->
fmt
.
i_visible_height
,
scale_size
);
SubFilterAllocationInit
,
SubFilterAllocationClean
,
p_spu
);
p_picture
=
p_scale
->
pf_video_filter
(
p_scale
,
p_picture
);
/* Load text and scale module */
if
(
!
p_picture
)
SpuRenderCreateAndLoadText
(
p_spu
);
msg_Err
(
p_spu
,
"scaling failed"
);
SpuRenderCreateAndLoadScale
(
p_spu
);
}
/* */
/* */
if
(
p_picture
)
p_sys
->
i_last_sort_date
=
-
1
;
{
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 */
return
p_spu
;
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
)
* Destroy the subpicture unit
{
*
int
i_crop_x
=
spu_scale_w
(
p_sys
->
i_crop_x
,
scale_size
);
* \param p_this the parent object which destroys the subpicture unit
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
);
void
spu_Destroy
(
spu_t
*
p_spu
)
int
i_crop_height
=
spu_scale_h
(
p_sys
->
i_crop_height
,
scale_size
);
{
spu_private_t
*
p_sys
=
p_spu
->
p
;
/* Find the intersection */
if
(
p_sys
->
p_text
)
if
(
i_crop_x
+
i_crop_width
<=
i_x_offset
||
FilterRelease
(
p_sys
->
p_text
);
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
;
if
(
p_sys
->
p_scale_yuvp
)
region_fmt
.
i_y_offset
=
i_y
-
i_y_offset
;
FilterRelease
(
p_sys
->
p_scale_yuvp
);
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
);
if
(
p_sys
->
p_scale
)
i_y_offset
=
__MAX
(
i_y
,
0
);
FilterRelease
(
p_sys
->
p_scale
);
}
}
subpicture_region_t
*
p_dst
=
*
pp_dst
=
subpicture_region_New
(
&
region_fmt
);
filter_chain_Delete
(
p_sys
->
p_chain
);
if
(
p_dst
)
vlc_mutex_destroy
(
&
p_sys
->
chain_lock
);
{
free
(
p_sys
->
psz_chain_update
);
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
)
/* Destroy all remaining subpictures */
i_fade_alpha
=
255
*
(
p_subpic
->
i_stop
-
render_date
)
/
SpuHeapClean
(
&
p_sys
->
heap
);
(
p_subpic
->
i_stop
-
fade_start
);
}
p_dst
->
i_alpha
=
i_fade_alpha
*
p_subpic
->
i_alpha
*
p_region
->
i_alpha
/
65025
;
}
exit:
vlc_mutex_destroy
(
&
p_sys
->
lock
);
if
(
b_rerender_text
)
{
vlc_object_release
(
p_spu
);
/* Some forms of subtitles need to be re-rendered more than
}
* once, eg. karaoke. We therefore restore the region to its
* pre-rendered state, so the next time through everything is
/**
* calculated again.
* 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
*/
*/
if
(
p_region
->
p_picture
)
void
spu_Attach
(
spu_t
*
p_spu
,
vlc_object_t
*
p_input
,
bool
b_attach
)
{
{
picture_Release
(
p_region
->
p_picture
);
if
(
b_attach
)
p_region
->
p_picture
=
NULL
;
}
if
(
p_region
->
p_private
)
{
{
subpicture_region_private_Delete
(
p_region
->
p_private
);
UpdateSPU
(
p_spu
,
p_input
);
p_region
->
p_private
=
NULL
;
var_Create
(
p_input
,
"highlight"
,
VLC_VAR_BOOL
);
var_AddCallback
(
p_input
,
"highlight"
,
CropCallback
,
p_spu
);
vlc_mutex_lock
(
&
p_spu
->
p
->
lock
);
p_spu
->
p
->
p_input
=
p_input
;
FilterRelease
(
p_spu
->
p
->
p_text
);
p_spu
->
p
->
p_text
=
NULL
;
SpuRenderCreateAndLoadText
(
p_spu
);
vlc_mutex_unlock
(
&
p_spu
->
p
->
lock
);
}
}
p_region
->
i_align
&=
~
SUBPICTURE_RENDERED
;
else
{
vlc_mutex_lock
(
&
p_spu
->
p
->
lock
);
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"
);
}
}
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