]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
0e9cebd32bba92e7767614cb778ee9c56f1a6cb6
[vlc] / src / video_output / vout_subpictures.c
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 $
6  *
7  * Authors: Vincent Seguin <seguin@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <errno.h>                                                 /* ENOMEM */
29 #include <stdlib.h>                                                /* free() */
30 #include <stdio.h>                                              /* sprintf() */
31 #include <string.h>                                            /* strerror() */
32
33 #include <videolan/vlc.h>
34
35 #include "video.h"
36 #include "video_output.h"
37
38 /*****************************************************************************
39  * vout_DisplaySubPicture: display a subpicture unit
40  *****************************************************************************
41  * Remove the reservation flag of a subpicture, which will cause it to be
42  * ready for display.
43  *****************************************************************************/
44 void  vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
45 {
46     int         i_margin;
47
48     /* Check if status is valid */
49     if( p_subpic->i_status != RESERVED_SUBPICTURE )
50     {
51         intf_ErrMsg( "vout error: subpicture %p has invalid status #%d",
52                      p_subpic, p_subpic->i_status );
53     }
54
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" );
58
59     if( i_margin >= 0 )
60     {
61         if( p_subpic->i_height + i_margin <= p_vout->output.i_height )
62         {
63             p_subpic->i_y = p_vout->output.i_height
64                              - i_margin - p_subpic->i_height;
65         }
66     }
67
68     /* Remove reservation flag */
69     p_subpic->i_status = READY_SUBPICTURE;
70 }
71
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,
81                                      int i_size )
82 {
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 */
86
87     /* Get lock */
88     vlc_mutex_lock( &p_vout->subpicture_lock );
89
90     /*
91      * Look for an empty place
92      */
93     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
94     {
95         if( p_vout->p_subpicture[i_subpic].i_status == DESTROYED_SUBPICTURE )
96         {
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) )
100             {
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
104                  * to be done */
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] );
108             }
109             else if( p_destroyed_subpic == NULL )
110             {
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];
114             }
115         }
116         else if( (p_free_subpic == NULL) &&
117                  (p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE ))
118         {
119             /* Subpicture is empty and ready for allocation */
120             p_free_subpic = &p_vout->p_subpicture[i_subpic];
121         }
122     }
123
124     /* If no free subpictures are available, use a destroyed subpicture */
125     if( (p_free_subpic == NULL) && (p_destroyed_subpic != NULL ) )
126     {
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;
131     }
132
133     /* If no free or destroyed subpicture could be found */
134     if( p_free_subpic == NULL )
135     {
136         intf_ErrMsg( "vout error: subpicture heap is full" );
137         vlc_mutex_unlock( &p_vout->subpicture_lock );
138         return( NULL );
139     }
140
141     p_free_subpic->p_sys =
142         vlc_memalign( &p_free_subpic->p_sys_orig, 16, i_size );
143
144     if( p_free_subpic->p_sys != NULL )
145     {
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;
154     }
155     else
156     {
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 ) );
163     }
164
165     vlc_mutex_unlock( &p_vout->subpicture_lock );
166
167     return( p_free_subpic );
168 }
169
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 )
179 {
180    /* Check if status is valid */
181    if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
182           && ( p_subpic->i_status != READY_SUBPICTURE ) )
183    {
184        intf_ErrMsg( "vout error: subpicture %p has invalid status %d",
185                    p_subpic, p_subpic->i_status );
186    }
187
188     p_subpic->i_status = DESTROYED_SUBPICTURE;
189 }
190
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 )
198 {
199     while( p_subpic != NULL )
200     {
201         p_subpic->pf_render( p_vout, p_pic, p_subpic );
202         p_subpic = p_subpic->p_next;
203     }
204 }
205
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
212  * the subpicture.
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 )
219 {
220     int i_index;
221     subpicture_t *p_subpic     = NULL;
222     subpicture_t *p_ephemer    = NULL;
223     mtime_t       ephemer_date = 0;
224
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++ )
228     {
229         if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
230         {
231             /* If it is a DVD subpicture, check its date */
232             if( p_vout->p_subpicture[i_index].i_type == MEMORY_SUBPICTURE )
233             {
234                 if( display_date > p_vout->p_subpicture[i_index].i_stop )
235                 {
236                     /* Too late, destroy the subpic */
237                     vout_DestroySubPicture( p_vout,
238                                     &p_vout->p_subpicture[i_index] );
239                     continue;
240                 }
241
242                 if( display_date < p_vout->p_subpicture[i_index].i_start )
243                 {
244                     /* Too early, come back next monday */
245                     continue;
246                 }
247
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 )
251                 {
252                     if( p_ephemer == NULL )
253                     {
254                         p_ephemer = &p_vout->p_subpicture[i_index];
255                         continue;
256                     }
257
258                     if( p_vout->p_subpicture[i_index].i_start
259                                                      < p_ephemer->i_start )
260                     {
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];
266
267                         /* If it's the 2nd youngest subpicture,
268                          * register its date */
269                         if( !ephemer_date
270                               || ephemer_date > p_subpic->i_start )
271                         {
272                             ephemer_date = p_subpic->i_start;
273                         }
274
275                         continue;
276                     }
277                 }
278
279                 p_vout->p_subpicture[i_index].p_next = p_subpic;
280                 p_subpic = &p_vout->p_subpicture[i_index];
281
282                 /* If it's the 2nd youngest subpicture, register its date */                    if( !ephemer_date || ephemer_date > p_subpic->i_start )
283                 {
284                     ephemer_date = p_subpic->i_start;
285                 }
286             }
287             /* If it's not a DVD subpicture, just register it */
288             else
289             {
290                 p_vout->p_subpicture[i_index].p_next = p_subpic;
291                 p_subpic = &p_vout->p_subpicture[i_index];
292             }
293         }
294     }
295
296     /* If we found an ephemer subpicture, check if it has to be
297      * displayed */
298     if( p_ephemer != NULL )
299     {
300         if( p_ephemer->i_start < ephemer_date )
301         {
302             /* Ephemer subpicture has lived too long */
303             vout_DestroySubPicture( p_vout, p_ephemer );
304         }
305         else
306         {
307             /* Ephemer subpicture can still live a bit */
308             p_ephemer->p_next = p_subpic;
309             return p_ephemer;
310         }
311     }
312
313     return p_subpic;
314 }
315