1 /*****************************************************************************
2 * vout_subpictures.c : subpicture management functions
3 *****************************************************************************
4 * Copyright (C) 2000 VideoLAN
5 * $Id: vout_subpictures.c,v 1.13 2002/04/25 21:52:42 sam Exp $
7 * Authors: Vincent Seguin <seguin@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
28 #include <errno.h> /* ENOMEM */
29 #include <stdlib.h> /* free() */
30 #include <stdio.h> /* sprintf() */
31 #include <string.h> /* strerror() */
33 #include <videolan/vlc.h>
36 #include "video_output.h"
38 /*****************************************************************************
39 * vout_DisplaySubPicture: display a subpicture unit
40 *****************************************************************************
41 * Remove the reservation flag of a subpicture, which will cause it to be
43 *****************************************************************************/
44 void vout_DisplaySubPicture( vout_thread_t
*p_vout
, subpicture_t
*p_subpic
)
48 /* Check if status is valid */
49 if( p_subpic
->i_status
!= RESERVED_SUBPICTURE
)
51 intf_ErrMsg( "vout error: subpicture %p has invalid status #%d",
52 p_subpic
, p_subpic
->i_status
);
55 /* If the user requested an SPU margin, we force the position after
56 * having checked that it was a valid value. */
57 i_margin
= config_GetIntVariable( "spumargin" );
61 if( p_subpic
->i_height
+ i_margin
<= p_vout
->output
.i_height
)
63 p_subpic
->i_y
= p_vout
->output
.i_height
64 - i_margin
- p_subpic
->i_height
;
68 /* Remove reservation flag */
69 p_subpic
->i_status
= READY_SUBPICTURE
;
72 /*****************************************************************************
73 * vout_CreateSubPicture: allocate a subpicture in the video output heap.
74 *****************************************************************************
75 * This function create a reserved subpicture in the video output heap.
76 * A null pointer is returned if the function fails. This method provides an
77 * already allocated zone of memory in the spu data fields. It needs locking
78 * since several pictures can be created by several producers threads.
79 *****************************************************************************/
80 subpicture_t
*vout_CreateSubPicture( vout_thread_t
*p_vout
, int i_type
,
83 int i_subpic
; /* subpicture index */
84 subpicture_t
* p_free_subpic
= NULL
; /* first free subpicture */
85 subpicture_t
* p_destroyed_subpic
= NULL
; /* first destroyed subpic */
88 vlc_mutex_lock( &p_vout
->subpicture_lock
);
91 * Look for an empty place
93 for( i_subpic
= 0; i_subpic
< VOUT_MAX_SUBPICTURES
; i_subpic
++ )
95 if( p_vout
->p_subpicture
[i_subpic
].i_status
== DESTROYED_SUBPICTURE
)
97 /* Subpicture is marked for destruction, but is still allocated */
98 if( (p_vout
->p_subpicture
[i_subpic
].i_type
== i_type
) &&
99 (p_vout
->p_subpicture
[i_subpic
].i_size
>= i_size
) )
101 /* Memory size do match or is smaller : memory will not be
102 * reallocated, and function can end immediately - this is
103 * the best possible case, since no memory allocation needs
105 p_vout
->p_subpicture
[i_subpic
].i_status
= RESERVED_SUBPICTURE
;
106 vlc_mutex_unlock( &p_vout
->subpicture_lock
);
107 return( &p_vout
->p_subpicture
[i_subpic
] );
109 else if( p_destroyed_subpic
== NULL
)
111 /* Memory size do not match, but subpicture index will be kept
112 * in case we find no other place */
113 p_destroyed_subpic
= &p_vout
->p_subpicture
[i_subpic
];
116 else if( (p_free_subpic
== NULL
) &&
117 (p_vout
->p_subpicture
[i_subpic
].i_status
== FREE_SUBPICTURE
))
119 /* Subpicture is empty and ready for allocation */
120 p_free_subpic
= &p_vout
->p_subpicture
[i_subpic
];
124 /* If no free subpictures are available, use a destroyed subpicture */
125 if( (p_free_subpic
== NULL
) && (p_destroyed_subpic
!= NULL
) )
127 /* No free subpicture or matching destroyed subpictures have been
128 * found, but a destroyed subpicture is still available */
129 free( p_destroyed_subpic
->p_sys_orig
);
130 p_free_subpic
= p_destroyed_subpic
;
133 /* If no free or destroyed subpicture could be found */
134 if( p_free_subpic
== NULL
)
136 intf_ErrMsg( "vout error: subpicture heap is full" );
137 vlc_mutex_unlock( &p_vout
->subpicture_lock
);
141 p_free_subpic
->p_sys
=
142 vlc_memalign( &p_free_subpic
->p_sys_orig
, 16, i_size
);
144 if( p_free_subpic
->p_sys
!= NULL
)
146 /* Copy subpicture information, set some default values */
147 p_free_subpic
->i_type
= i_type
;
148 p_free_subpic
->i_status
= RESERVED_SUBPICTURE
;
149 p_free_subpic
->i_size
= i_size
;
150 p_free_subpic
->i_x
= 0;
151 p_free_subpic
->i_y
= 0;
152 p_free_subpic
->i_width
= 0;
153 p_free_subpic
->i_height
= 0;
157 /* Memory allocation failed : set subpicture as empty */
158 p_free_subpic
->i_type
= EMPTY_SUBPICTURE
;
159 p_free_subpic
->i_status
= FREE_SUBPICTURE
;
160 p_free_subpic
= NULL
;
161 intf_ErrMsg( "vout error: spu allocation returned %s",
162 strerror( ENOMEM
) );
165 vlc_mutex_unlock( &p_vout
->subpicture_lock
);
167 return( p_free_subpic
);
170 /*****************************************************************************
171 * vout_DestroySubPicture: remove a subpicture from the heap
172 *****************************************************************************
173 * This function frees a previously reserved subpicture.
174 * It is meant to be used when the construction of a picture aborted.
175 * This function does not need locking since reserved subpictures are ignored
176 * by the output thread.
177 *****************************************************************************/
178 void vout_DestroySubPicture( vout_thread_t
*p_vout
, subpicture_t
*p_subpic
)
180 /* Check if status is valid */
181 if( ( p_subpic
->i_status
!= RESERVED_SUBPICTURE
)
182 && ( p_subpic
->i_status
!= READY_SUBPICTURE
) )
184 intf_ErrMsg( "vout error: subpicture %p has invalid status %d",
185 p_subpic
, p_subpic
->i_status
);
188 p_subpic
->i_status
= DESTROYED_SUBPICTURE
;
191 /*****************************************************************************
192 * vout_RenderSubPictures: render a subpicture list
193 *****************************************************************************
194 * This function renders all sub picture units in the list.
195 *****************************************************************************/
196 void vout_RenderSubPictures( vout_thread_t
*p_vout
, picture_t
*p_pic
,
197 subpicture_t
*p_subpic
)
199 while( p_subpic
!= NULL
)
201 p_subpic
->pf_render( p_vout
, p_pic
, p_subpic
);
202 p_subpic
= p_subpic
->p_next
;
206 /*****************************************************************************
207 * vout_SortSubPictures: find the subpictures to display
208 *****************************************************************************
209 * This function parses all subpictures and decides which ones need to be
210 * displayed. This operation does not need lock, since only READY_SUBPICTURE
211 * are handled. If no picture has been selected, display_date will depend on
213 * We also check for ephemer DVD subpictures (subpictures that have
214 * to be removed if a newer one is available), which makes it a lot
215 * more difficult to guess if a subpicture has to be rendered or not.
216 *****************************************************************************/
217 subpicture_t
*vout_SortSubPictures( vout_thread_t
*p_vout
,
218 mtime_t display_date
)
221 subpicture_t
*p_subpic
= NULL
;
222 subpicture_t
*p_ephemer
= NULL
;
223 mtime_t ephemer_date
= 0;
225 /* We get an easily parsable chained list of subpictures which
226 * ends with NULL since p_subpic was initialized to NULL. */
227 for( i_index
= 0; i_index
< VOUT_MAX_SUBPICTURES
; i_index
++ )
229 if( p_vout
->p_subpicture
[i_index
].i_status
== READY_SUBPICTURE
)
231 /* If it is a DVD subpicture, check its date */
232 if( p_vout
->p_subpicture
[i_index
].i_type
== MEMORY_SUBPICTURE
)
234 if( display_date
> p_vout
->p_subpicture
[i_index
].i_stop
)
236 /* Too late, destroy the subpic */
237 vout_DestroySubPicture( p_vout
,
238 &p_vout
->p_subpicture
[i_index
] );
242 if( display_date
< p_vout
->p_subpicture
[i_index
].i_start
)
244 /* Too early, come back next monday */
248 /* If this is an ephemer subpic, see if it's the
249 * youngest we have */
250 if( p_vout
->p_subpicture
[i_index
].b_ephemer
)
252 if( p_ephemer
== NULL
)
254 p_ephemer
= &p_vout
->p_subpicture
[i_index
];
258 if( p_vout
->p_subpicture
[i_index
].i_start
259 < p_ephemer
->i_start
)
261 /* Link the previous ephemer subpicture and
262 * replace it with the current one */
263 p_ephemer
->p_next
= p_subpic
;
264 p_subpic
= p_ephemer
;
265 p_ephemer
= &p_vout
->p_subpicture
[i_index
];
267 /* If it's the 2nd youngest subpicture,
268 * register its date */
270 || ephemer_date
> p_subpic
->i_start
)
272 ephemer_date
= p_subpic
->i_start
;
279 p_vout
->p_subpicture
[i_index
].p_next
= p_subpic
;
280 p_subpic
= &p_vout
->p_subpicture
[i_index
];
282 /* If it's the 2nd youngest subpicture, register its date */ if( !ephemer_date
|| ephemer_date
> p_subpic
->i_start
)
284 ephemer_date
= p_subpic
->i_start
;
287 /* If it's not a DVD subpicture, just register it */
290 p_vout
->p_subpicture
[i_index
].p_next
= p_subpic
;
291 p_subpic
= &p_vout
->p_subpicture
[i_index
];
296 /* If we found an ephemer subpicture, check if it has to be
298 if( p_ephemer
!= NULL
)
300 if( p_ephemer
->i_start
< ephemer_date
)
302 /* Ephemer subpicture has lived too long */
303 vout_DestroySubPicture( p_vout
, p_ephemer
);
307 /* Ephemer subpicture can still live a bit */
308 p_ephemer
->p_next
= p_subpic
;