]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
* src/video_output/vout_subpictures.c : New OSD channels
[vlc] / src / video_output / vout_subpictures.c
1 /*****************************************************************************
2  * vout_subpictures.c : subpicture management functions
3  *****************************************************************************
4  * Copyright (C) 2000-2004 VideoLAN
5  * $Id$
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 <stdlib.h>                                                /* free() */
29 #include <stdio.h>                                              /* sprintf() */
30 #include <string.h>                                            /* strerror() */
31
32 #include <vlc/vlc.h>
33
34 #include "vlc_video.h"
35 #include "video_output.h"
36
37 /**
38  * Display a subpicture unit
39  *
40  * Remove the reservation flag of a subpicture, which will cause it to be
41  * ready for display.
42  * \param p_vout the video output this subpicture should be displayed on
43  * \param p_subpic the subpicture to display
44  */
45 void vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
46 {
47     int         i_margin;
48
49     /* Check if status is valid */
50     if( p_subpic->i_status != RESERVED_SUBPICTURE )
51     {
52         msg_Err( p_vout, "subpicture %p has invalid status #%d",
53                          p_subpic, p_subpic->i_status );
54     }
55
56     /* If the user requested an SPU margin, we force the position after
57      * having checked that it was a valid value. */
58     i_margin = config_GetInt( p_vout, "spumargin" );
59
60     if( i_margin >= 0 )
61     {
62         if( p_subpic->i_height + (unsigned int)i_margin
63                                                  <= p_vout->output.i_height )
64         {
65             p_subpic->i_y = p_vout->output.i_height
66                              - i_margin - p_subpic->i_height;
67         }
68     }
69
70     /* Remove reservation flag */
71     p_subpic->i_status = READY_SUBPICTURE;
72 }
73
74 /**
75  * Allocate a subpicture in the video output heap.
76  *
77  * This function create a reserved subpicture in the video output heap.
78  * A null pointer is returned if the function fails. This method provides an
79  * already allocated zone of memory in the spu data fields. It needs locking
80  * since several pictures can be created by several producers threads.
81  * \param p_vout the vout in which to create the subpicture
82  * \param i_type the type of the subpicture
83  * \return NULL on error, a reserved subpicture otherwise
84  */
85 subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_channel,
86                                      int i_type )
87 {
88     int                 i_subpic;                        /* subpicture index */
89     subpicture_t *      p_subpic = NULL;            /* first free subpicture */
90
91     /* Clear the default channel before writing into it */
92     if( i_channel == DEFAULT_CHAN )
93     {
94         vout_ClearOSDChannel( p_vout, DEFAULT_CHAN );
95     }
96
97     /* Get lock */
98     vlc_mutex_lock( &p_vout->subpicture_lock );
99
100     /*
101      * Look for an empty place
102      */
103     p_subpic = NULL;
104     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
105     {
106         if( p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
107         {
108             /* Subpicture is empty and ready for allocation */
109             p_subpic = &p_vout->p_subpicture[i_subpic];
110             p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
111             break;
112         }
113     }
114
115     /* If no free subpicture could be found */
116     if( p_subpic == NULL )
117     {
118         msg_Err( p_vout, "subpicture heap is full" );
119         vlc_mutex_unlock( &p_vout->subpicture_lock );
120         return NULL;
121     }
122
123     /* Copy subpicture information, set some default values */
124     p_subpic->i_channel = i_channel;
125     p_subpic->i_type    = i_type;
126     p_subpic->i_status  = RESERVED_SUBPICTURE;
127
128     p_subpic->i_start   = 0;
129     p_subpic->i_stop    = 0;
130     p_subpic->b_ephemer = VLC_FALSE;
131
132     p_subpic->i_x       = 0;
133     p_subpic->i_y       = 0;
134     p_subpic->i_width   = 0;
135     p_subpic->i_height  = 0;
136
137     /* Remain last subpicture displayed in DEFAULT_CHAN */
138     if( i_channel == DEFAULT_CHAN )
139     {
140         p_vout->p_default_channel = p_subpic;
141     }
142
143     vlc_mutex_unlock( &p_vout->subpicture_lock );
144
145     return p_subpic;
146 }
147
148 /**
149  * Remove a subpicture from the heap
150  *
151  * This function frees a previously reserved subpicture.
152  * It is meant to be used when the construction of a picture aborted.
153  * This function does not need locking since reserved subpictures are ignored
154  * by the output thread.
155  */
156 void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
157 {
158     /* Get lock */
159     vlc_mutex_lock( &p_vout->subpicture_lock );
160
161     /* There can be race conditions so we need to check the status */
162     if( p_subpic->i_status == FREE_SUBPICTURE )
163     {
164         vlc_mutex_unlock( &p_vout->subpicture_lock );
165         return;
166     }
167
168     /* Check if status is valid */
169     if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
170            && ( p_subpic->i_status != READY_SUBPICTURE ) )
171     {
172         msg_Err( p_vout, "subpicture %p has invalid status %d",
173                          p_subpic, p_subpic->i_status );
174     }
175
176     if( p_subpic->pf_destroy )
177     {
178         p_subpic->pf_destroy( p_subpic );
179     }
180
181     p_subpic->i_status = FREE_SUBPICTURE;
182
183     vlc_mutex_unlock( &p_vout->subpicture_lock );
184 }
185
186 /*****************************************************************************
187  * vout_RenderSubPictures: render a subpicture list
188  *****************************************************************************
189  * This function renders all sub picture units in the list.
190  *****************************************************************************/
191 void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic,
192                              subpicture_t *p_subpic )
193 {
194     /* Get lock */
195     vlc_mutex_lock( &p_vout->subpicture_lock );
196
197     /* Check i_status again to make sure spudec hasn't destroyed the subpic */
198     while( p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE )
199     {
200         p_subpic->pf_render( p_vout, p_pic, p_subpic );
201         p_subpic = p_subpic->p_next;
202     }
203
204     vlc_mutex_unlock( &p_vout->subpicture_lock );
205 }
206
207 /*****************************************************************************
208  * vout_SortSubPictures: find the subpictures to display
209  *****************************************************************************
210  * This function parses all subpictures and decides which ones need to be
211  * displayed. This operation does not need lock, since only READY_SUBPICTURE
212  * are handled. If no picture has been selected, display_date will depend on
213  * the subpicture.
214  * We also check for ephemer DVD subpictures (subpictures that have
215  * to be removed if a newer one is available), which makes it a lot
216  * more difficult to guess if a subpicture has to be rendered or not.
217  *****************************************************************************/
218 subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout,
219                                     mtime_t display_date )
220 {
221     int i_index;
222     subpicture_t *p_subpic     = NULL;
223     subpicture_t *p_ephemer    = NULL;
224     mtime_t       ephemer_date = 0;
225
226     /* We get an easily parsable chained list of subpictures which
227      * ends with NULL since p_subpic was initialized to NULL. */
228     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
229     {
230         if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
231         {
232             /* If it is a DVD subpicture, check its date */
233             if( p_vout->p_subpicture[i_index].i_type == MEMORY_SUBPICTURE )
234             {
235                 if( !p_vout->p_subpicture[i_index].b_ephemer
236                      && display_date > p_vout->p_subpicture[i_index].i_stop )
237                 {
238                     /* Too late, destroy the subpic */
239                     vout_DestroySubPicture( p_vout,
240                                     &p_vout->p_subpicture[i_index] );
241                     continue;
242                 }
243
244                 if( display_date
245                      && display_date < p_vout->p_subpicture[i_index].i_start )
246                 {
247                     /* Too early, come back next monday */
248                     continue;
249                 }
250
251                 /* If this is an ephemer subpic, see if it's the
252                  * youngest we have */
253                 if( p_vout->p_subpicture[i_index].b_ephemer )
254                 {
255                     if( p_ephemer == NULL )
256                     {
257                         p_ephemer = &p_vout->p_subpicture[i_index];
258                         continue;
259                     }
260
261                     if( p_vout->p_subpicture[i_index].i_start
262                                                      < p_ephemer->i_start )
263                     {
264                         /* Link the previous ephemer subpicture and
265                          * replace it with the current one */
266                         p_ephemer->p_next = p_subpic;
267                         p_subpic = p_ephemer;
268                         p_ephemer = &p_vout->p_subpicture[i_index];
269
270                         /* If it's the 2nd youngest subpicture,
271                          * register its date */
272                         if( !ephemer_date
273                               || ephemer_date > p_subpic->i_start )
274                         {
275                             ephemer_date = p_subpic->i_start;
276                         }
277
278                         continue;
279                     }
280                 }
281
282                 p_vout->p_subpicture[i_index].p_next = p_subpic;
283                 p_subpic = &p_vout->p_subpicture[i_index];
284
285                 /* If it's the 2nd youngest subpicture, register its date */
286                 if( !ephemer_date || ephemer_date > p_subpic->i_start )
287                 {
288                     ephemer_date = p_subpic->i_start;
289                 }
290             }
291             /* If it's not a DVD subpicture, just register it */
292             else
293             {
294                 p_vout->p_subpicture[i_index].p_next = p_subpic;
295                 p_subpic = &p_vout->p_subpicture[i_index];
296             }
297         }
298     }
299
300     /* If we found an ephemer subpicture, check if it has to be
301      * displayed */
302     if( p_ephemer != NULL )
303     {
304         if( p_ephemer->i_start < ephemer_date )
305         {
306             /* Ephemer subpicture has lived too long */
307             vout_DestroySubPicture( p_vout, p_ephemer );
308         }
309         else
310         {
311             /* Ephemer subpicture can still live a bit */
312             p_ephemer->p_next = p_subpic;
313             return p_ephemer;
314         }
315     }
316
317     return p_subpic;
318 }
319
320 /*****************************************************************************
321  * vout_RegisterOSDChannel: register an OSD channel
322  *****************************************************************************
323  * This function affects an ID to an OSD channel
324  *****************************************************************************/
325 int vout_RegisterOSDChannel( vout_thread_t *p_vout )
326 {
327     msg_Dbg( p_vout, "Registering OSD channel, ID: %i", p_vout->i_channel_count + 1 );
328     return ++p_vout->i_channel_count;
329 }
330
331 /*****************************************************************************
332  * vout_ClearOSDChannel: clear an OSD channel
333  *****************************************************************************
334  * This function destroys the subpictures which belong to the OSD channel
335  * corresponding to i_channel_id.
336  *****************************************************************************/
337 void vout_ClearOSDChannel( vout_thread_t *p_vout, int i_channel )
338 {
339     int                 i_subpic;                        /* subpicture index */
340     subpicture_t *      p_subpic = NULL;            /* first free subpicture */
341
342     if( i_channel == DEFAULT_CHAN )
343     {
344         if( p_vout->p_default_channel != NULL )
345         {
346             vout_DestroySubPicture( p_vout, p_vout->p_default_channel );
347         }
348         p_vout->p_default_channel = NULL;
349         return;
350     }
351
352     vlc_mutex_lock( &p_vout->subpicture_lock );
353
354     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
355     {
356         p_subpic = &p_vout->p_subpicture[i_subpic];
357         if( p_subpic->i_status == FREE_SUBPICTURE
358             || ( p_subpic->i_status != RESERVED_SUBPICTURE
359                  && p_subpic->i_status != READY_SUBPICTURE ) )
360         {
361             continue;
362         }
363         if( p_subpic->i_channel == i_channel )
364         {
365             if( p_subpic->pf_destroy )
366             {
367                 p_subpic->pf_destroy( p_subpic );
368             }
369             p_subpic->i_status = FREE_SUBPICTURE;
370         }
371     }
372
373     vlc_mutex_unlock( &p_vout->subpicture_lock );
374 }