vout_subpictures.c 53.3 KB
Newer Older
Sam Hocevar's avatar
 
Sam Hocevar committed
1 2 3
/*****************************************************************************
 * vout_subpictures.c : subpicture management functions
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2000-2007 VLC authors and VideoLAN
5
 * $Id$
Sam Hocevar's avatar
 
Sam Hocevar committed
6 7 8
 *
 * Authors: Vincent Seguin <seguin@via.ecp.fr>
 *          Samuel Hocevar <sam@zoy.org>
9
 *          Gildas Bazin <gbazin@videolan.org>
Sam Hocevar's avatar
 
Sam Hocevar committed
10
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
11 12 13
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
Sam Hocevar's avatar
 
Sam Hocevar committed
14 15 16 17
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
18 19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
Sam Hocevar's avatar
 
Sam Hocevar committed
20
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
21 22 23
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Sam Hocevar's avatar
 
Sam Hocevar committed
24 25 26 27 28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
29 30 31 32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33 34 35
#include <assert.h>
#include <limits.h>

36
#include <vlc_common.h>
37 38
#include <vlc_modules.h>
#include <vlc_input.h>
Clément Stenac's avatar
Clément Stenac committed
39 40
#include <vlc_vout.h>
#include <vlc_filter.h>
41
#include <vlc_spu.h>
42

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
43
#include "../libvlc.h"
Laurent Aimar's avatar
Laurent Aimar committed
44
#include "vout_internal.h"
45
#include "../misc/subpicture.h"
46

47 48 49
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
50

51
/* Number of simultaneous subpictures */
52
#define VOUT_MAX_SUBPICTURES (__MAX(VOUT_MAX_PICTURES, SPU_MAX_PREPARE_TIME/5000))
53

Laurent Aimar's avatar
Laurent Aimar committed
54
/* */
Laurent Aimar's avatar
Laurent Aimar committed
55 56 57
typedef struct {
    subpicture_t *subpicture;
    bool          reject;
58 59
} spu_heap_entry_t;

Laurent Aimar's avatar
Laurent Aimar committed
60 61
typedef struct {
    spu_heap_entry_t entry[VOUT_MAX_SUBPICTURES];
62 63
} spu_heap_t;

Laurent Aimar's avatar
Laurent Aimar committed
64 65 66
struct spu_private_t {
    vlc_mutex_t  lock;            /* lock to protect all followings fields */
    vlc_object_t *input;
67

Laurent Aimar's avatar
Laurent Aimar committed
68
    spu_heap_t   heap;
69

Laurent Aimar's avatar
Laurent Aimar committed
70 71 72 73 74 75 76 77 78 79 80
    int channel;             /**< number of subpicture channels registered */
    filter_t *text;                              /**< text renderer module */
    filter_t *scale_yuvp;                     /**< scaling module for YUVP */
    filter_t *scale;                    /**< scaling module (all but YUVP) */
    bool force_crop;                     /**< force cropping of subpicture */
    struct {
        int x;
        int y;
        int width;
        int height;
    } crop;                                                  /**< cropping */
81

Laurent Aimar's avatar
Laurent Aimar committed
82 83 84
    int     margin;                    /**< force position of a subpicture */
    bool    force_palette;                /**< force palette of subpicture */
    uint8_t palette[4][4];                             /**< forced palette */
85

86
    /* Subpiture filters */
Laurent Aimar's avatar
Laurent Aimar committed
87
    char           *source_chain_update;
88
    vlc_mutex_t    source_chain_lock;
Laurent Aimar's avatar
Laurent Aimar committed
89 90
    filter_chain_t *source_chain;
    char           *filter_chain_update;
91
    vlc_mutex_t    filter_chain_lock;
Laurent Aimar's avatar
Laurent Aimar committed
92
    filter_chain_t *filter_chain;
93 94

    /* */
Laurent Aimar's avatar
Laurent Aimar committed
95
    mtime_t last_sort_date;
96 97
};

Laurent Aimar's avatar
Laurent Aimar committed
98 99 100
/*****************************************************************************
 * heap managment
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
101
static void SpuHeapInit(spu_heap_t *heap)
Laurent Aimar's avatar
Laurent Aimar committed
102
{
Laurent Aimar's avatar
Laurent Aimar committed
103 104
    for (int i = 0; i < VOUT_MAX_SUBPICTURES; i++) {
        spu_heap_entry_t *e = &heap->entry[i];
Laurent Aimar's avatar
Laurent Aimar committed
105

Laurent Aimar's avatar
Laurent Aimar committed
106 107
        e->subpicture = NULL;
        e->reject     = false;
Laurent Aimar's avatar
Laurent Aimar committed
108 109 110
    }
}

Laurent Aimar's avatar
Laurent Aimar committed
111
static int SpuHeapPush(spu_heap_t *heap, subpicture_t *subpic)
Laurent Aimar's avatar
Laurent Aimar committed
112
{
Laurent Aimar's avatar
Laurent Aimar committed
113 114
    for (int i = 0; i < VOUT_MAX_SUBPICTURES; i++) {
        spu_heap_entry_t *e = &heap->entry[i];
Laurent Aimar's avatar
Laurent Aimar committed
115

Laurent Aimar's avatar
Laurent Aimar committed
116
        if (e->subpicture)
Laurent Aimar's avatar
Laurent Aimar committed
117
            continue;
Laurent Aimar's avatar
Laurent Aimar committed
118

Laurent Aimar's avatar
Laurent Aimar committed
119 120
        e->subpicture = subpic;
        e->reject     = false;
Laurent Aimar's avatar
Laurent Aimar committed
121 122 123 124
        return VLC_SUCCESS;
    }
    return VLC_EGENERIC;
}
Laurent Aimar's avatar
Laurent Aimar committed
125

Laurent Aimar's avatar
Laurent Aimar committed
126
static void SpuHeapDeleteAt(spu_heap_t *heap, int index)
Laurent Aimar's avatar
Laurent Aimar committed
127
{
Laurent Aimar's avatar
Laurent Aimar committed
128
    spu_heap_entry_t *e = &heap->entry[index];
129

Laurent Aimar's avatar
Laurent Aimar committed
130 131
    if (e->subpicture)
        subpicture_Delete(e->subpicture);
132

Laurent Aimar's avatar
Laurent Aimar committed
133
    e->subpicture = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
134
}
Laurent Aimar's avatar
Laurent Aimar committed
135

Laurent Aimar's avatar
Laurent Aimar committed
136
static int SpuHeapDeleteSubpicture(spu_heap_t *heap, subpicture_t *subpic)
Laurent Aimar's avatar
Laurent Aimar committed
137
{
Laurent Aimar's avatar
Laurent Aimar committed
138 139
    for (int i = 0; i < VOUT_MAX_SUBPICTURES; i++) {
        spu_heap_entry_t *e = &heap->entry[i];
Laurent Aimar's avatar
Laurent Aimar committed
140

Laurent Aimar's avatar
Laurent Aimar committed
141
        if (e->subpicture != subpic)
Laurent Aimar's avatar
Laurent Aimar committed
142 143
            continue;

Laurent Aimar's avatar
Laurent Aimar committed
144
        SpuHeapDeleteAt(heap, i);
Laurent Aimar's avatar
Laurent Aimar committed
145 146 147 148
        return VLC_SUCCESS;
    }
    return VLC_EGENERIC;
}
149

Laurent Aimar's avatar
Laurent Aimar committed
150
static void SpuHeapClean(spu_heap_t *heap)
Laurent Aimar's avatar
Laurent Aimar committed
151
{
Laurent Aimar's avatar
Laurent Aimar committed
152 153 154 155
    for (int i = 0; i < VOUT_MAX_SUBPICTURES; i++) {
        spu_heap_entry_t *e = &heap->entry[i];
        if (e->subpicture)
            subpicture_Delete(e->subpicture);
Laurent Aimar's avatar
Laurent Aimar committed
156 157
    }
}
158

Laurent Aimar's avatar
Laurent Aimar committed
159 160 161
struct filter_owner_sys_t {
    spu_t *spu;
    int   channel;
162
};
163

Laurent Aimar's avatar
Laurent Aimar committed
164
static void FilterRelease(filter_t *filter)
Laurent Aimar's avatar
Laurent Aimar committed
165
{
Laurent Aimar's avatar
Laurent Aimar committed
166 167
    if (filter->p_module)
        module_unneed(filter, filter->p_module);
168
    free(filter->owner.sys);
Laurent Aimar's avatar
Laurent Aimar committed
169
    vlc_object_release(filter);
Laurent Aimar's avatar
Laurent Aimar committed
170
}
171

Laurent Aimar's avatar
Laurent Aimar committed
172
static picture_t *spu_new_video_buffer(filter_t *filter)
Laurent Aimar's avatar
Laurent Aimar committed
173
{
Laurent Aimar's avatar
Laurent Aimar committed
174
    const video_format_t *fmt = &filter->fmt_out.video;
175

Laurent Aimar's avatar
Laurent Aimar committed
176
    return picture_NewFromFormat(fmt);
Laurent Aimar's avatar
Laurent Aimar committed
177
}
178

Laurent Aimar's avatar
Laurent Aimar committed
179
static void spu_del_video_buffer(filter_t *filter, picture_t *picture)
180
{
Laurent Aimar's avatar
Laurent Aimar committed
181 182
    VLC_UNUSED(filter);
    picture_Release(picture);
Laurent Aimar's avatar
Laurent Aimar committed
183
}
184

Laurent Aimar's avatar
Laurent Aimar committed
185 186 187
static int spu_get_attachments(filter_t *filter,
                               input_attachment_t ***attachment_ptr,
                               int *attachment_count)
Laurent Aimar's avatar
Laurent Aimar committed
188
{
189 190
    filter_owner_sys_t *sys = filter->owner.sys;
    spu_t *spu = sys->spu;
Laurent Aimar's avatar
Laurent Aimar committed
191 192 193 194 195 196 197

    int ret = VLC_EGENERIC;
    if (spu->p->input)
        ret = input_Control((input_thread_t*)spu->p->input,
                            INPUT_GET_ATTACHMENTS,
                            attachment_ptr, attachment_count);
    return ret;
Laurent Aimar's avatar
Laurent Aimar committed
198
}
199

Laurent Aimar's avatar
Laurent Aimar committed
200
static filter_t *SpuRenderCreateAndLoadText(spu_t *spu)
Laurent Aimar's avatar
Laurent Aimar committed
201
{
202
    filter_t *text = vlc_custom_create(spu, sizeof(*text), "spu text");
Laurent Aimar's avatar
Laurent Aimar committed
203
    if (!text)
204
        return NULL;
205

206 207 208
    filter_owner_sys_t *sys = xmalloc(sizeof(*sys));
    sys->spu = spu;
    text->owner.sys = sys;
209

Laurent Aimar's avatar
Laurent Aimar committed
210
    es_format_Init(&text->fmt_in, VIDEO_ES, 0);
211

Laurent Aimar's avatar
Laurent Aimar committed
212 213 214 215 216
    es_format_Init(&text->fmt_out, VIDEO_ES, 0);
    text->fmt_out.video.i_width          =
    text->fmt_out.video.i_visible_width  = 32;
    text->fmt_out.video.i_height         =
    text->fmt_out.video.i_visible_height = 32;
217

Laurent Aimar's avatar
Laurent Aimar committed
218
    text->pf_get_attachments = spu_get_attachments;
219

Laurent Aimar's avatar
Laurent Aimar committed
220
    text->p_module = module_need(text, "text renderer", "$text-renderer", false);
Laurent Aimar's avatar
Laurent Aimar committed
221 222

    /* Create a few variables used for enhanced text rendering */
Laurent Aimar's avatar
Laurent Aimar committed
223 224
    var_Create(text, "spu-elapsed",   VLC_VAR_TIME);
    var_Create(text, "text-rerender", VLC_VAR_BOOL);
225

Laurent Aimar's avatar
Laurent Aimar committed
226
    return text;
227 228
}

Laurent Aimar's avatar
Laurent Aimar committed
229 230 231 232
static filter_t *SpuRenderCreateAndLoadScale(vlc_object_t *object,
                                             vlc_fourcc_t src_chroma,
                                             vlc_fourcc_t dst_chroma,
                                             bool require_resize)
233
{
234
    filter_t *scale = vlc_custom_create(object, sizeof(*scale), "scale");
Laurent Aimar's avatar
Laurent Aimar committed
235
    if (!scale)
Laurent Aimar's avatar
Laurent Aimar committed
236
        return NULL;
237

Laurent Aimar's avatar
Laurent Aimar committed
238 239 240
    es_format_Init(&scale->fmt_in, VIDEO_ES, 0);
    scale->fmt_in.video.i_chroma = src_chroma;
    scale->fmt_in.video.i_width =
241 242 243
    scale->fmt_in.video.i_visible_width =
    scale->fmt_in.video.i_height =
    scale->fmt_in.video.i_visible_height = 32;
244

Laurent Aimar's avatar
Laurent Aimar committed
245 246 247
    es_format_Init(&scale->fmt_out, VIDEO_ES, 0);
    scale->fmt_out.video.i_chroma = dst_chroma;
    scale->fmt_out.video.i_width =
248 249 250
    scale->fmt_out.video.i_visible_width =
    scale->fmt_out.video.i_height =
    scale->fmt_out.video.i_visible_height = require_resize ? 16 : 32;
251

252 253
    scale->owner.video.buffer_new = spu_new_video_buffer;
    scale->owner.video.buffer_del = spu_del_video_buffer;
254

Laurent Aimar's avatar
Laurent Aimar committed
255
    scale->p_module = module_need(scale, "video filter2", NULL, false);
256

Laurent Aimar's avatar
Laurent Aimar committed
257
    return scale;
Laurent Aimar's avatar
Laurent Aimar committed
258
}
259

Laurent Aimar's avatar
Laurent Aimar committed
260 261
static void SpuRenderText(spu_t *spu, bool *rerender_text,
                          subpicture_region_t *region,
262
                          const vlc_fourcc_t *chroma_list,
263
                          mtime_t elapsed_time)
264
{
Laurent Aimar's avatar
Laurent Aimar committed
265
    filter_t *text = spu->p->text;
266

Laurent Aimar's avatar
Laurent Aimar committed
267
    assert(region->fmt.i_chroma == VLC_CODEC_TEXT);
268

Laurent Aimar's avatar
Laurent Aimar committed
269
    if (!text || !text->p_module)
270
        return;
271

Laurent Aimar's avatar
Laurent Aimar committed
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
    /* 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.
     */
291
    var_SetTime(text, "spu-elapsed", elapsed_time);
Laurent Aimar's avatar
Laurent Aimar committed
292 293 294
    var_SetBool(text, "text-rerender", false);

    if (text->pf_render_html && region->psz_html)
295
        text->pf_render_html(text, region, region, chroma_list);
Laurent Aimar's avatar
Laurent Aimar committed
296
    else if (text->pf_render_text)
297
        text->pf_render_text(text, region, region, chroma_list);
Laurent Aimar's avatar
Laurent Aimar committed
298
    *rerender_text = var_GetBool(text, "text-rerender");
299 300
}

301
/**
Laurent Aimar's avatar
Laurent Aimar committed
302
 * A few scale functions helpers.
303 304
 */

Laurent Aimar's avatar
Laurent Aimar committed
305
#define SCALE_UNIT (1000)
Laurent Aimar's avatar
Laurent Aimar committed
306
typedef struct {
Laurent Aimar's avatar
Laurent Aimar committed
307 308 309
    int w;
    int h;
} spu_scale_t;
310

Laurent Aimar's avatar
Laurent Aimar committed
311
static spu_scale_t spu_scale_create(int w, int h)
Laurent Aimar's avatar
Laurent Aimar committed
312 313
{
    spu_scale_t s = { .w = w, .h = h };
Laurent Aimar's avatar
Laurent Aimar committed
314
    if (s.w <= 0)
Laurent Aimar's avatar
Laurent Aimar committed
315
        s.w = SCALE_UNIT;
Laurent Aimar's avatar
Laurent Aimar committed
316
    if (s.h <= 0)
Laurent Aimar's avatar
Laurent Aimar committed
317 318 319
        s.h = SCALE_UNIT;
    return s;
}
Laurent Aimar's avatar
Laurent Aimar committed
320
static spu_scale_t spu_scale_unit(void)
Laurent Aimar's avatar
Laurent Aimar committed
321
{
Laurent Aimar's avatar
Laurent Aimar committed
322
    return spu_scale_create(SCALE_UNIT, SCALE_UNIT);
Laurent Aimar's avatar
Laurent Aimar committed
323
}
Laurent Aimar's avatar
Laurent Aimar committed
324
static spu_scale_t spu_scale_createq(int64_t wn, int64_t wd, int64_t hn, int64_t hd)
Laurent Aimar's avatar
Laurent Aimar committed
325
{
Laurent Aimar's avatar
Laurent Aimar committed
326 327
    return spu_scale_create(wn * SCALE_UNIT / wd,
                            hn * SCALE_UNIT / hd);
Laurent Aimar's avatar
Laurent Aimar committed
328
}
Laurent Aimar's avatar
Laurent Aimar committed
329
static int spu_scale_w(int v, const spu_scale_t s)
Laurent Aimar's avatar
Laurent Aimar committed
330 331 332
{
    return v * s.w / SCALE_UNIT;
}
Laurent Aimar's avatar
Laurent Aimar committed
333
static int spu_scale_h(int v, const spu_scale_t s)
Laurent Aimar's avatar
Laurent Aimar committed
334 335 336
{
    return v * s.h / SCALE_UNIT;
}
Laurent Aimar's avatar
Laurent Aimar committed
337
static int spu_invscale_w(int v, const spu_scale_t s)
Laurent Aimar's avatar
Laurent Aimar committed
338 339 340
{
    return v * SCALE_UNIT / s.w;
}
Laurent Aimar's avatar
Laurent Aimar committed
341
static int spu_invscale_h(int v, const spu_scale_t s)
Laurent Aimar's avatar
Laurent Aimar committed
342 343
{
    return v * SCALE_UNIT / s.h;
344 345
}

346
/**
Laurent Aimar's avatar
Laurent Aimar committed
347
 * A few area functions helpers
348
 */
Laurent Aimar's avatar
Laurent Aimar committed
349 350 351 352 353
typedef struct {
    int x;
    int y;
    int width;
    int height;
354

Laurent Aimar's avatar
Laurent Aimar committed
355 356
    spu_scale_t scale;
} spu_area_t;
357

Laurent Aimar's avatar
Laurent Aimar committed
358
static spu_area_t spu_area_create(int x, int y, int w, int h, spu_scale_t s)
Laurent Aimar's avatar
Laurent Aimar committed
359
{
Laurent Aimar's avatar
Laurent Aimar committed
360
    spu_area_t a = { .x = x, .y = y, .width = w, .height = h, .scale = s };
Laurent Aimar's avatar
Laurent Aimar committed
361
    return a;
Sam Hocevar's avatar
 
Sam Hocevar committed
362
}
Laurent Aimar's avatar
Laurent Aimar committed
363
static spu_area_t spu_area_scaled(spu_area_t a)
364
{
Laurent Aimar's avatar
Laurent Aimar committed
365
    if (a.scale.w == SCALE_UNIT && a.scale.h == SCALE_UNIT)
Laurent Aimar's avatar
Laurent Aimar committed
366
        return a;
367

Laurent Aimar's avatar
Laurent Aimar committed
368 369 370 371
    a.x      = spu_scale_w(a.x,      a.scale);
    a.y      = spu_scale_h(a.y,      a.scale);
    a.width  = spu_scale_w(a.width,  a.scale);
    a.height = spu_scale_h(a.height, a.scale);
372

Laurent Aimar's avatar
Laurent Aimar committed
373 374 375
    a.scale = spu_scale_unit();
    return a;
}
Laurent Aimar's avatar
Laurent Aimar committed
376
static spu_area_t spu_area_unscaled(spu_area_t a, spu_scale_t s)
Laurent Aimar's avatar
Laurent Aimar committed
377
{
Laurent Aimar's avatar
Laurent Aimar committed
378
    if (a.scale.w == s.w && a.scale.h == s.h)
Laurent Aimar's avatar
Laurent Aimar committed
379
        return a;
380

Laurent Aimar's avatar
Laurent Aimar committed
381
    a = spu_area_scaled(a);
382

Laurent Aimar's avatar
Laurent Aimar committed
383 384 385 386
    a.x      = spu_invscale_w(a.x,      s);
    a.y      = spu_invscale_h(a.y,      s);
    a.width  = spu_invscale_w(a.width,  s);
    a.height = spu_invscale_h(a.height, s);
387

Laurent Aimar's avatar
Laurent Aimar committed
388 389 390
    a.scale = s;
    return a;
}
Laurent Aimar's avatar
Laurent Aimar committed
391
static bool spu_area_overlap(spu_area_t a, spu_area_t b)
Laurent Aimar's avatar
Laurent Aimar committed
392
{
Laurent Aimar's avatar
Laurent Aimar committed
393 394
    const int dx = 0;
    const int dy = 0;
395

Laurent Aimar's avatar
Laurent Aimar committed
396 397
    a = spu_area_scaled(a);
    b = spu_area_scaled(b);
398

Laurent Aimar's avatar
Laurent Aimar committed
399 400
    return __MAX(a.x - dx, b.x) < __MIN(a.x + a.width  + dx, b.x + b.width ) &&
           __MAX(a.y - dy, b.y) < __MIN(a.y + a.height + dy, b.y + b.height);
Laurent Aimar's avatar
Laurent Aimar committed
401
}
Laurent Aimar's avatar
Laurent Aimar committed
402

Laurent Aimar's avatar
Laurent Aimar committed
403 404 405
/**
 * Avoid area overlapping
 */
Laurent Aimar's avatar
Laurent Aimar committed
406 407
static void SpuAreaFixOverlap(spu_area_t *dst,
                              const spu_area_t *sub_array, int sub_count, int align)
Laurent Aimar's avatar
Laurent Aimar committed
408
{
Laurent Aimar's avatar
Laurent Aimar committed
409 410 411
    spu_area_t a = spu_area_scaled(*dst);
    bool is_moved = false;
    bool is_ok;
412

Laurent Aimar's avatar
Laurent Aimar committed
413 414
    /* Check for overlap
     * XXX It is not fast O(n^2) but we should not have a lot of region */
Laurent Aimar's avatar
Laurent Aimar committed
415 416 417 418 419 420
    do {
        is_ok = true;
        for (int i = 0; i < sub_count; i++) {
            spu_area_t sub = spu_area_scaled(sub_array[i]);

            if (!spu_area_overlap(a, sub))
Laurent Aimar's avatar
Laurent Aimar committed
421
                continue;
422

Laurent Aimar's avatar
Laurent Aimar committed
423
            if (align & SUBPICTURE_ALIGN_TOP) {
Laurent Aimar's avatar
Laurent Aimar committed
424
                /* We go down */
Laurent Aimar's avatar
Laurent Aimar committed
425 426 427 428
                int i_y = sub.y + sub.height;
                a.y = i_y;
                is_moved = true;
            } else if (align & SUBPICTURE_ALIGN_BOTTOM) {
Laurent Aimar's avatar
Laurent Aimar committed
429
                /* We go up */
Laurent Aimar's avatar
Laurent Aimar committed
430 431 432 433
                int i_y = sub.y - a.height;
                a.y = i_y;
                is_moved = true;
            } else {
Laurent Aimar's avatar
Laurent Aimar committed
434
                /* TODO what to do in this case? */
Laurent Aimar's avatar
Laurent Aimar committed
435
                //fprintf(stderr, "Overlap with unsupported alignment\n");
Laurent Aimar's avatar
Laurent Aimar committed
436 437
                break;
            }
438

Laurent Aimar's avatar
Laurent Aimar committed
439
            is_ok = false;
Laurent Aimar's avatar
Laurent Aimar committed
440
            break;
Laurent Aimar's avatar
Laurent Aimar committed
441
        }
Laurent Aimar's avatar
Laurent Aimar committed
442
    } while (!is_ok);
443

Laurent Aimar's avatar
Laurent Aimar committed
444 445
    if (is_moved)
        *dst = spu_area_unscaled(a, dst->scale);
Laurent Aimar's avatar
Laurent Aimar committed
446
}
447

448

Laurent Aimar's avatar
Laurent Aimar committed
449
static void SpuAreaFitInside(spu_area_t *area, const spu_area_t *boundary)
Laurent Aimar's avatar
Laurent Aimar committed
450
{
451
    spu_area_t a = spu_area_scaled(*area);
452

453 454 455 456 457
    const int i_error_x = (a.x + a.width) - boundary->width;
    if (i_error_x > 0)
        a.x -= i_error_x;
    if (a.x < 0)
        a.x = 0;
458

459 460 461 462 463
    const int i_error_y = (a.y + a.height) - boundary->height;
    if (i_error_y > 0)
        a.y -= i_error_y;
    if (a.y < 0)
        a.y = 0;
464

465
    *area = spu_area_unscaled(a, area->scale);
Laurent Aimar's avatar
Laurent Aimar committed
466
}
467

Laurent Aimar's avatar
Laurent Aimar committed
468 469 470
/**
 * Place a region
 */
Laurent Aimar's avatar
Laurent Aimar committed
471 472 473
static void SpuRegionPlace(int *x, int *y,
                           const subpicture_t *subpic,
                           const subpicture_region_t *region)
Laurent Aimar's avatar
Laurent Aimar committed
474
{
Laurent Aimar's avatar
Laurent Aimar committed
475 476 477 478 479 480 481 482
    assert(region->i_x != INT_MAX && region->i_y != INT_MAX);
    if (subpic->b_absolute) {
        *x = region->i_x;
        *y = region->i_y;
    } else {
        if (region->i_align & SUBPICTURE_ALIGN_TOP)
            *y = region->i_y;
        else if (region->i_align & SUBPICTURE_ALIGN_BOTTOM)
483
            *y = subpic->i_original_picture_height - region->fmt.i_visible_height - region->i_y;
484
        else
485
            *y = subpic->i_original_picture_height / 2 - region->fmt.i_visible_height / 2;
486

Laurent Aimar's avatar
Laurent Aimar committed
487 488 489
        if (region->i_align & SUBPICTURE_ALIGN_LEFT)
            *x = region->i_x;
        else if (region->i_align & SUBPICTURE_ALIGN_RIGHT)
490
            *x = subpic->i_original_picture_width - region->fmt.i_visible_width - region->i_x;
491
        else
492
            *x = subpic->i_original_picture_width / 2 - region->fmt.i_visible_width / 2;
Laurent Aimar's avatar
Laurent Aimar committed
493 494 495 496 497 498 499
    }
}

/**
 * This function compares two 64 bits integers.
 * It can be used by qsort.
 */
Laurent Aimar's avatar
Laurent Aimar committed
500
static int IntegerCmp(int64_t i0, int64_t i1)
Laurent Aimar's avatar
Laurent Aimar committed
501 502 503 504 505 506 507 508 509 510 511 512 513 514
{
    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.
 */
Laurent Aimar's avatar
Laurent Aimar committed
515
static int SubpictureCmp(const void *s0, const void *s1)
Laurent Aimar's avatar
Laurent Aimar committed
516
{
Laurent Aimar's avatar
Laurent Aimar committed
517 518
    subpicture_t *subpic0 = *(subpicture_t**)s0;
    subpicture_t *subpic1 = *(subpicture_t**)s1;
Laurent Aimar's avatar
Laurent Aimar committed
519 520
    int r;

Laurent Aimar's avatar
Laurent Aimar committed
521 522 523 524 525 526 527
    r = IntegerCmp(!subpic0->b_absolute, !subpic1->b_absolute);
    if (!r)
        r = IntegerCmp(subpic0->i_start, subpic1->i_start);
    if (!r)
        r = IntegerCmp(subpic0->i_channel, subpic1->i_channel);
    if (!r)
        r = IntegerCmp(subpic0->i_order, subpic1->i_order);
Laurent Aimar's avatar
Laurent Aimar committed
528 529 530
    return r;
}

Laurent Aimar's avatar
Laurent Aimar committed
531
/*****************************************************************************
532
 * SpuSelectSubpictures: find the subpictures to display
Laurent Aimar's avatar
Laurent Aimar committed
533 534 535 536 537 538 539 540
 *****************************************************************************
 * This function parses all subpictures and decides which ones need to be
 * displayed. If no picture has been selected, display_date will depend on
 * the subpicture.
 * We also check for ephemer DVD subpictures (subpictures that have
 * to be removed if a newer one is available), which makes it a lot
 * more difficult to guess if a subpicture has to be rendered or not.
 *****************************************************************************/
Laurent Aimar's avatar
Laurent Aimar committed
541 542 543 544 545 546
static void SpuSelectSubpictures(spu_t *spu,
                                 unsigned int *subpicture_count,
                                 subpicture_t **subpicture_array,
                                 mtime_t render_subtitle_date,
                                 mtime_t render_osd_date,
                                 bool ignore_osd)
Laurent Aimar's avatar
Laurent Aimar committed
547
{
Laurent Aimar's avatar
Laurent Aimar committed
548
    spu_private_t *sys = spu->p;
549 550

    /* */
Laurent Aimar's avatar
Laurent Aimar committed
551
    *subpicture_count = 0;
Laurent Aimar's avatar
Laurent Aimar committed
552

Laurent Aimar's avatar
Laurent Aimar committed
553
    /* Create a list of channels */
Laurent Aimar's avatar
Laurent Aimar committed
554 555
    int channel[VOUT_MAX_SUBPICTURES];
    int channel_count = 0;
Laurent Aimar's avatar
Laurent Aimar committed
556

Laurent Aimar's avatar
Laurent Aimar committed
557 558 559
    for (int index = 0; index < VOUT_MAX_SUBPICTURES; index++) {
        spu_heap_entry_t *entry = &sys->heap.entry[index];
        if (!entry->subpicture || entry->reject)
Laurent Aimar's avatar
Laurent Aimar committed
560
            continue;
Laurent Aimar's avatar
Laurent Aimar committed
561
        const int i_channel = entry->subpicture->i_channel;
Laurent Aimar's avatar
Laurent Aimar committed
562
        int i;
Laurent Aimar's avatar
Laurent Aimar committed
563 564
        for (i = 0; i < channel_count; i++) {
            if (channel[i] == i_channel)
Laurent Aimar's avatar
Laurent Aimar committed
565 566
                break;
        }
Laurent Aimar's avatar
Laurent Aimar committed
567 568
        if (channel_count <= i)
            channel[channel_count++] = i_channel;
Laurent Aimar's avatar
Laurent Aimar committed
569 570
    }

Laurent Aimar's avatar
Laurent Aimar committed
571 572 573 574 575
    /* Fill up the subpicture_array arrays with relevent pictures */
    for (int i = 0; i < channel_count; i++) {
        subpicture_t *available_subpic[VOUT_MAX_SUBPICTURES];
        bool         is_available_late[VOUT_MAX_SUBPICTURES];
        int          available_count = 0;
576

577
        mtime_t      start_date = render_subtitle_date;
578
        mtime_t      ephemer_subtitle_date = 0;
579
        mtime_t      ephemer_osd_date = 0;
Laurent Aimar's avatar
Laurent Aimar committed
580 581
        int64_t      ephemer_subtitle_order = INT64_MIN;
        int64_t      ephemer_system_order = INT64_MIN;
Laurent Aimar's avatar
Laurent Aimar committed
582

583
        /* Select available pictures */
Laurent Aimar's avatar
Laurent Aimar committed
584 585 586 587 588 589 590 591 592
        for (int index = 0; index < VOUT_MAX_SUBPICTURES; index++) {
            spu_heap_entry_t *entry = &sys->heap.entry[index];
            subpicture_t *current = entry->subpicture;
            bool is_stop_valid;
            bool is_late;

            if (!current || entry->reject) {
                if (entry->reject)
                    SpuHeapDeleteAt(&sys->heap, index);
Laurent Aimar's avatar
Laurent Aimar committed
593 594 595
                continue;
            }

Laurent Aimar's avatar
Laurent Aimar committed
596 597
            if (current->i_channel != channel[i] ||
               (ignore_osd && !current->b_subtitle))
Laurent Aimar's avatar
Laurent Aimar committed
598
                continue;
Laurent Aimar's avatar
Laurent Aimar committed
599 600 601 602

            const mtime_t render_date = current->b_subtitle ? render_subtitle_date : render_osd_date;
            if (render_date &&
                render_date < current->i_start) {
Laurent Aimar's avatar
Laurent Aimar committed
603 604 605 606
                /* Too early, come back next monday */
                continue;
            }

Laurent Aimar's avatar
Laurent Aimar committed
607 608 609 610 611 612
            mtime_t *ephemer_date_ptr  = current->b_subtitle ? &ephemer_subtitle_date  : &ephemer_osd_date;
            int64_t *ephemer_order_ptr = current->b_subtitle ? &ephemer_subtitle_order : &ephemer_system_order;
            if (current->i_start >= *ephemer_date_ptr) {
                *ephemer_date_ptr = current->i_start;
                if (current->i_order > *ephemer_order_ptr)
                    *ephemer_order_ptr = current->i_order;
613
            }
Laurent Aimar's avatar
Laurent Aimar committed
614

Laurent Aimar's avatar
Laurent Aimar committed
615
            is_stop_valid = !current->b_ephemer || current->i_stop > current->i_start;
616

Laurent Aimar's avatar
Laurent Aimar committed
617
            is_late = is_stop_valid && current->i_stop <= render_date;
618 619

            /* start_date will be used for correct automatic overlap support
Laurent Aimar's avatar
Laurent Aimar committed
620
             * in case picture that should not be displayed anymore (display_time)
Laurent Aimar's avatar
Laurent Aimar committed
621 622 623
             * overlap with a picture to be displayed (current->i_start)  */
            if (current->b_subtitle && !is_late && !current->b_ephemer)
                start_date = current->i_start;
624 625

            /* */
Laurent Aimar's avatar
Laurent Aimar committed
626 627 628
            available_subpic[available_count] = current;
            is_available_late[available_count] = is_late;
            available_count++;
Laurent Aimar's avatar
Laurent Aimar committed
629 630
        }

631
        /* Only forced old picture display at the transition */
Laurent Aimar's avatar
Laurent Aimar committed
632 633 634
        if (start_date < sys->last_sort_date)
            start_date = sys->last_sort_date;
        if (start_date <= 0)
635 636 637
            start_date = INT64_MAX;

        /* Select pictures to be displayed */
Laurent Aimar's avatar
Laurent Aimar committed
638 639 640
        for (int index = 0; index < available_count; index++) {
            subpicture_t *current = available_subpic[index];
            bool is_late = is_available_late[index];
Laurent Aimar's avatar
Laurent Aimar committed
641

Laurent Aimar's avatar
Laurent Aimar committed
642 643 644
            const mtime_t stop_date = current->b_subtitle ? __MAX(start_date, sys->last_sort_date) : render_osd_date;
            const mtime_t ephemer_date  = current->b_subtitle ? ephemer_subtitle_date  : ephemer_osd_date;
            const int64_t ephemer_order = current->b_subtitle ? ephemer_subtitle_order : ephemer_system_order;
645

646
            /* Destroy late and obsolete ephemer subpictures */
Laurent Aimar's avatar
Laurent Aimar committed
647 648 649 650 651 652 653
            bool is_rejeted = is_late && current->i_stop <= stop_date;
            if (current->b_ephemer) {
                if (current->i_start < ephemer_date)
                    is_rejeted = true;
                else if (current->i_start == ephemer_date &&
                         current->i_order < ephemer_order)
                    is_rejeted = true;
Laurent Aimar's avatar
Laurent Aimar committed
654
            }
655

Laurent Aimar's avatar
Laurent Aimar committed
656 657
            if (is_rejeted)
                SpuHeapDeleteSubpicture(&sys->heap, current);
Laurent Aimar's avatar
Laurent Aimar committed
658
            else
Laurent Aimar's avatar
Laurent Aimar committed
659
                subpicture_array[(*subpicture_count)++] = current;
Laurent Aimar's avatar
Laurent Aimar committed
660
        }
Laurent Aimar's avatar
Laurent Aimar committed
661
    }
662

Laurent Aimar's avatar
Laurent Aimar committed
663
    sys->last_sort_date = render_subtitle_date;
Laurent Aimar's avatar
Laurent Aimar committed
664 665
}

Laurent Aimar's avatar
Laurent Aimar committed
666 667 668 669 670


/**
 * It will transform the provided region into another region suitable for rendering.
 */
Laurent Aimar's avatar
Laurent Aimar committed
671 672 673 674 675 676 677 678
static void SpuRenderRegion(spu_t *spu,
                            subpicture_region_t **dst_ptr, spu_area_t *dst_area,
                            subpicture_t *subpic, subpicture_region_t *region,
                            const spu_scale_t scale_size,
                            const vlc_fourcc_t *chroma_list,
                            const video_format_t *fmt,
                            const spu_area_t *subtitle_area, int subtitle_area_count,
                            mtime_t render_date)
679
{
Laurent Aimar's avatar
Laurent Aimar committed
680
    spu_private_t *sys = spu->p;
681

Laurent Aimar's avatar
Laurent Aimar committed
682 683 684 685
    video_format_t fmt_original = region->fmt;
    bool restore_text = false;
    int x_offset;
    int y_offset;
686

Laurent Aimar's avatar
Laurent Aimar committed
687
    video_format_t region_fmt;
Laurent Aimar's avatar
Laurent Aimar committed
688
    picture_t *region_picture;
689

Laurent Aimar's avatar
Laurent Aimar committed
690
    /* Invalidate area by default */
Laurent Aimar's avatar
Laurent Aimar committed
691 692
    *dst_area = spu_area_create(0,0, 0,0, scale_size);
    *dst_ptr  = NULL;
693

Laurent Aimar's avatar
Laurent Aimar committed
694
    /* Render text region */
Laurent Aimar's avatar
Laurent Aimar committed
695 696
    if (region->fmt.i_chroma == VLC_CODEC_TEXT) {
        SpuRenderText(spu, &restore_text, region,
697
                      chroma_list,
698
                      render_date - subpic->i_start);
Laurent Aimar's avatar
Laurent Aimar committed
699 700

        /* Check if the rendering has failed ... */
Laurent Aimar's avatar
Laurent Aimar committed
701
        if (region->fmt.i_chroma == VLC_CODEC_TEXT)
Laurent Aimar's avatar
Laurent Aimar committed
702
            goto exit;
703 704
    }

Laurent Aimar's avatar
Laurent Aimar committed
705
    /* Force palette if requested
Laurent Aimar's avatar
Laurent Aimar committed
706
     * FIXME b_force_palette and force_crop are applied to all subpictures using palette
Laurent Aimar's avatar
Laurent Aimar committed
707 708
     * instead of only the right one (being the dvd spu).
     */
Laurent Aimar's avatar
Laurent Aimar committed
709 710 711 712
    const bool using_palette = region->fmt.i_chroma == VLC_CODEC_YUVP;
    const bool force_palette = using_palette && sys->force_palette;
    const bool force_crop    = force_palette && sys->force_crop;
    bool changed_palette     = false;
713

Laurent Aimar's avatar
Laurent Aimar committed
714 715 716
    /* 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) */
Laurent Aimar's avatar
Laurent Aimar committed
717 718 719
    int y_margin = 0;
    if (!force_crop && subpic->b_subtitle)
        y_margin = spu_invscale_h(sys->margin, scale_size);
Laurent Aimar's avatar
Laurent Aimar committed
720 721 722

    /* Place the picture
     * We compute the position in the rendered size */
Laurent Aimar's avatar
Laurent Aimar committed
723 724
    SpuRegionPlace(&x_offset, &y_offset,
                   subpic, region);
Laurent Aimar's avatar
Laurent Aimar committed
725 726 727

    /* Save this position for subtitle overlap support
     * it is really important that there are given without scale_size applied */
Laurent Aimar's avatar
Laurent Aimar committed
728
    *dst_area = spu_area_create(x_offset, y_offset,
729 730
                                region->fmt.i_visible_width,
                                region->fmt.i_visible_height,
Laurent Aimar's avatar
Laurent Aimar committed
731
                                scale_size);
Laurent Aimar's avatar
Laurent Aimar committed
732 733

    /* Handle overlapping subtitles when possible */
Laurent Aimar's avatar
Laurent Aimar committed
734 735 736
    if (subpic->b_subtitle && !subpic->b_absolute)
        SpuAreaFixOverlap(dst_area, subtitle_area, subtitle_area_count,
                          region->i_align);
737

Laurent Aimar's avatar
Laurent Aimar committed
738 739
    /* we copy the area: for the subtitle overlap support we want
     * to only save the area without margin applied */
Laurent Aimar's avatar
Laurent Aimar committed
740
    spu_area_t restrained = *dst_area;
741

Laurent Aimar's avatar
Laurent Aimar committed
742
    /* apply margin to subtitles and correct if they go over the picture edge */
Laurent Aimar's avatar
Laurent Aimar committed
743 744
    if (subpic->b_subtitle)
        restrained.y -= y_margin;
745

746 747
    spu_area_t display = spu_area_create(0, 0, fmt->i_visible_width,
                                         fmt->i_visible_height,
Laurent Aimar's avatar
Laurent Aimar committed
748
                                         spu_scale_unit());
749
    //fprintf("
Laurent Aimar's avatar
Laurent Aimar committed
750
    SpuAreaFitInside(&restrained, &display);
Laurent Aimar's avatar
Laurent Aimar committed
751

Laurent Aimar's avatar
Laurent Aimar committed
752
    /* Fix the position for the current scale_size */
Laurent Aimar's avatar
Laurent Aimar committed
753 754
    x_offset = spu_scale_w(restrained.x, restrained.scale);
    y_offset = spu_scale_h(restrained.y, restrained.scale);
Laurent Aimar's avatar
Laurent Aimar committed
755 756

    /* */
Laurent Aimar's avatar
Laurent Aimar committed
757 758 759
    if (force_palette) {
        video_palette_t *old_palette = region->fmt.p_palette;
        video_palette_t new_palette;
Laurent Aimar's avatar
Laurent Aimar committed
760

Laurent Aimar's avatar
Laurent Aimar committed
761
        /* We suppose DVD palette here */
Laurent Aimar's avatar
Laurent Aimar committed
762 763 764 765 766 767 768 769 770 771 772
        new_palette.i_entries = 4;
        for (int i = 0; i < 4; i++)
            for (int j = 0; j < 4; j++)
                new_palette.palette[i][j] = sys->palette[i][j];

        if (old_palette->i_entries == new_palette.i_entries) {
            for (int i = 0; i < old_palette->i_entries; i++)
                for (int j = 0; j < 4; j++)
                    changed_palette |= old_palette->palette[i][j] != new_palette.palette[i][j];
        } else {
            changed_palette = true;
Laurent Aimar's avatar
Laurent Aimar committed
773
        }
Laurent Aimar's avatar
Laurent Aimar committed
774
        *old_palette = new_palette;
Laurent Aimar's avatar
Laurent Aimar committed
775 776
    }

Laurent Aimar's avatar
Laurent Aimar committed
777
    /* */
Laurent Aimar's avatar
Laurent Aimar committed
778 779 780 781 782 783 784
    region_fmt = region->fmt;
    region_picture = region->p_picture;

    bool convert_chroma = true;
    for (int i = 0; chroma_list[i] && convert_chroma; i++) {
        if (region_fmt.i_chroma == chroma_list[i])
            convert_chroma = false;
785
    }
Laurent Aimar's avatar
Laurent Aimar committed
786

Laurent Aimar's avatar
Laurent Aimar committed
787
    /* Scale from rendered size to destination size */
Laurent Aimar's avatar
Laurent Aimar committed
788 789 790 791
    if (sys->scale && sys->scale->p_module &&
        (!using_palette || (sys->scale_yuvp && sys->scale_yuvp->p_module)) &&
        (scale_size.w != SCALE_UNIT || scale_size.h != SCALE_UNIT ||
        using_palette || convert_chroma)) {
792 793
        const unsigned dst_width  = spu_scale_w(region->fmt.i_visible_width,  scale_size);
        const unsigned dst_height = spu_scale_h(region->fmt.i_visible_height, scale_size);
Laurent Aimar's avatar
Laurent Aimar committed
794

Laurent Aimar's avatar
Laurent Aimar committed
795
        /* Destroy the cache if unusable */
Laurent Aimar's avatar
Laurent Aimar committed
796 797 798
        if (region->p_private) {
            subpicture_region_private_t *private = region->p_private;
            bool is_changed = false;
Laurent Aimar's avatar
Laurent Aimar committed
799

Laurent Aimar's avatar
Laurent Aimar committed
800
            /* Check resize changes */
801 802
            if (dst_width  != private->fmt.i_visible_width ||
                dst_height != private->fmt.i_visible_height)
Laurent Aimar's avatar
Laurent Aimar committed
803
                is_changed = true;
Laurent Aimar's avatar
Laurent Aimar committed
804

Laurent Aimar's avatar
Laurent Aimar committed
805
            /* Check forced palette changes */