vout_subpictures.c 51.7 KB
Newer Older
Sam Hocevar's avatar
 
Sam Hocevar committed
1 2 3
/*****************************************************************************
 * vout_subpictures.c : subpicture management functions
 *****************************************************************************
4
 * Copyright (C) 2000-2007 the VideoLAN team
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 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (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
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
23
 * 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 168 169
    if (filter->p_module)
        module_unneed(filter, filter->p_module);
    if (filter->p_owner)
        free(filter->p_owner);
170

Laurent Aimar's avatar
Laurent Aimar committed
171
    vlc_object_release(filter);
Laurent Aimar's avatar
Laurent Aimar committed
172
}
173

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

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

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

    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
199
}
200

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

Laurent Aimar's avatar
Laurent Aimar committed
207 208
    text->p_owner = xmalloc(sizeof(*text->p_owner));
    text->p_owner->spu = spu;
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 241
    es_format_Init(&scale->fmt_in, VIDEO_ES, 0);
    scale->fmt_in.video.i_chroma = src_chroma;
    scale->fmt_in.video.i_width =
    scale->fmt_in.video.i_height = 32;
242

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

Laurent Aimar's avatar
Laurent Aimar committed
248 249
    scale->pf_video_buffer_new = spu_new_video_buffer;
    scale->pf_video_buffer_del = spu_del_video_buffer;
250

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

Laurent Aimar's avatar
Laurent Aimar committed
253
    return scale;
Laurent Aimar's avatar
Laurent Aimar committed
254
}
255

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

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

Laurent Aimar's avatar
Laurent Aimar committed
265
    if (!text || !text->p_module)
266
        return;
267

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

    if (text->pf_render_html && region->psz_html)
291
        text->pf_render_html(text, region, region, chroma_list);
Laurent Aimar's avatar
Laurent Aimar committed
292
    else if (text->pf_render_text)
293
        text->pf_render_text(text, region, region, chroma_list);
Laurent Aimar's avatar
Laurent Aimar committed
294
    *rerender_text = var_GetBool(text, "text-rerender");
295 296
}

297
/**
Laurent Aimar's avatar
Laurent Aimar committed
298
 * A few scale functions helpers.
299 300
 */

Laurent Aimar's avatar
Laurent Aimar committed
301
#define SCALE_UNIT (1000)
Laurent Aimar's avatar
Laurent Aimar committed
302
typedef struct {
Laurent Aimar's avatar
Laurent Aimar committed
303 304 305
    int w;
    int h;
} spu_scale_t;
306

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

342
/**
Laurent Aimar's avatar
Laurent Aimar committed
343
 * A few area functions helpers
344
 */
Laurent Aimar's avatar
Laurent Aimar committed
345 346 347 348 349
typedef struct {
    int x;
    int y;
    int width;
    int height;
350

Laurent Aimar's avatar
Laurent Aimar committed
351 352
    spu_scale_t scale;
} spu_area_t;
353

Laurent Aimar's avatar
Laurent Aimar committed
354
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
355
{
Laurent Aimar's avatar
Laurent Aimar committed
356
    spu_area_t a = { .x = x, .y = y, .width = w, .height = h, .scale = s };
Laurent Aimar's avatar
Laurent Aimar committed
357
    return a;
Sam Hocevar's avatar
 
Sam Hocevar committed
358
}
Laurent Aimar's avatar
Laurent Aimar committed
359
static spu_area_t spu_area_scaled(spu_area_t a)
360
{
Laurent Aimar's avatar
Laurent Aimar committed
361
    if (a.scale.w == SCALE_UNIT && a.scale.h == SCALE_UNIT)
Laurent Aimar's avatar
Laurent Aimar committed
362
        return a;
363

Laurent Aimar's avatar
Laurent Aimar committed
364 365 366 367
    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);
368

Laurent Aimar's avatar
Laurent Aimar committed
369 370 371
    a.scale = spu_scale_unit();
    return a;
}
Laurent Aimar's avatar
Laurent Aimar committed
372
static spu_area_t spu_area_unscaled(spu_area_t a, spu_scale_t s)
Laurent Aimar's avatar
Laurent Aimar committed
373
{
Laurent Aimar's avatar
Laurent Aimar committed
374
    if (a.scale.w == s.w && a.scale.h == s.h)
Laurent Aimar's avatar
Laurent Aimar committed
375
        return a;
376

Laurent Aimar's avatar
Laurent Aimar committed
377
    a = spu_area_scaled(a);
378

Laurent Aimar's avatar
Laurent Aimar committed
379 380 381 382
    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);
383

Laurent Aimar's avatar
Laurent Aimar committed
384 385 386
    a.scale = s;
    return a;
}
Laurent Aimar's avatar
Laurent Aimar committed
387
static bool spu_area_overlap(spu_area_t a, spu_area_t b)
Laurent Aimar's avatar
Laurent Aimar committed
388
{
Laurent Aimar's avatar
Laurent Aimar committed
389 390
    const int dx = 0;
    const int dy = 0;
391

Laurent Aimar's avatar
Laurent Aimar committed
392 393
    a = spu_area_scaled(a);
    b = spu_area_scaled(b);
394

Laurent Aimar's avatar
Laurent Aimar committed
395 396
    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
397
}
Laurent Aimar's avatar
Laurent Aimar committed
398

Laurent Aimar's avatar
Laurent Aimar committed
399 400 401
/**
 * Avoid area overlapping
 */
Laurent Aimar's avatar
Laurent Aimar committed
402 403
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
404
{
Laurent Aimar's avatar
Laurent Aimar committed
405 406 407
    spu_area_t a = spu_area_scaled(*dst);
    bool is_moved = false;
    bool is_ok;
408

Laurent Aimar's avatar
Laurent Aimar committed
409 410
    /* 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
411 412 413 414 415 416
    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
417
                continue;
418

Laurent Aimar's avatar
Laurent Aimar committed
419
            if (align & SUBPICTURE_ALIGN_TOP) {
Laurent Aimar's avatar
Laurent Aimar committed
420
                /* We go down */
Laurent Aimar's avatar
Laurent Aimar committed
421 422 423 424
                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
425
                /* We go up */
Laurent Aimar's avatar
Laurent Aimar committed
426 427 428 429
                int i_y = sub.y - a.height;
                a.y = i_y;
                is_moved = true;
            } else {
Laurent Aimar's avatar
Laurent Aimar committed
430
                /* TODO what to do in this case? */
Laurent Aimar's avatar
Laurent Aimar committed
431
                //fprintf(stderr, "Overlap with unsupported alignment\n");
Laurent Aimar's avatar
Laurent Aimar committed
432 433
                break;
            }
434

Laurent Aimar's avatar
Laurent Aimar committed
435
            is_ok = false;
Laurent Aimar's avatar
Laurent Aimar committed
436
            break;
Laurent Aimar's avatar
Laurent Aimar committed
437
        }
Laurent Aimar's avatar
Laurent Aimar committed
438
    } while (!is_ok);
439

Laurent Aimar's avatar
Laurent Aimar committed
440 441
    if (is_moved)
        *dst = spu_area_unscaled(a, dst->scale);
Laurent Aimar's avatar
Laurent Aimar committed
442
}
443

444

Laurent Aimar's avatar
Laurent Aimar committed
445
static void SpuAreaFitInside(spu_area_t *area, const spu_area_t *boundary)
Laurent Aimar's avatar
Laurent Aimar committed
446
{
Laurent Aimar's avatar
Laurent Aimar committed
447
  spu_area_t a = spu_area_scaled(*area);
448

Laurent Aimar's avatar
Laurent Aimar committed
449 450 451 452 453
  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;
454

Laurent Aimar's avatar
Laurent Aimar committed
455 456 457 458 459
  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;
460

Laurent Aimar's avatar
Laurent Aimar committed
461
  *area = spu_area_unscaled(a, area->scale);
Laurent Aimar's avatar
Laurent Aimar committed
462
}
463

Laurent Aimar's avatar
Laurent Aimar committed
464 465 466
/**
 * Place a region
 */
Laurent Aimar's avatar
Laurent Aimar committed
467 468 469
static void SpuRegionPlace(int *x, int *y,
                           const subpicture_t *subpic,
                           const subpicture_region_t *region)
Laurent Aimar's avatar
Laurent Aimar committed
470
{
Laurent Aimar's avatar
Laurent Aimar committed
471 472 473 474 475 476 477 478 479
    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)
            *y = subpic->i_original_picture_height - region->fmt.i_height - region->i_y;
480
        else
Laurent Aimar's avatar
Laurent Aimar committed
481
            *y = subpic->i_original_picture_height / 2 - region->fmt.i_height / 2;
482

Laurent Aimar's avatar
Laurent Aimar committed
483 484 485 486
        if (region->i_align & SUBPICTURE_ALIGN_LEFT)
            *x = region->i_x;
        else if (region->i_align & SUBPICTURE_ALIGN_RIGHT)
            *x = subpic->i_original_picture_width - region->fmt.i_width - region->i_x;
487
        else
Laurent Aimar's avatar
Laurent Aimar committed
488
            *x = subpic->i_original_picture_width / 2 - region->fmt.i_width / 2;
Laurent Aimar's avatar
Laurent Aimar committed
489 490 491 492 493 494 495
    }
}

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

Laurent Aimar's avatar
Laurent Aimar committed
517 518 519 520 521 522 523
    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
524 525 526
    return r;
}

Laurent Aimar's avatar
Laurent Aimar committed
527
/*****************************************************************************
528
 * SpuSelectSubpictures: find the subpictures to display
Laurent Aimar's avatar
Laurent Aimar committed
529 530 531 532 533 534 535 536
 *****************************************************************************
 * 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
537 538 539 540 541 542
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
543
{
Laurent Aimar's avatar
Laurent Aimar committed
544
    spu_private_t *sys = spu->p;
545 546

    /* */
Laurent Aimar's avatar
Laurent Aimar committed
547
    *subpicture_count = 0;
Laurent Aimar's avatar
Laurent Aimar committed
548

Laurent Aimar's avatar
Laurent Aimar committed
549
    /* Create a list of channels */
Laurent Aimar's avatar
Laurent Aimar committed
550 551
    int channel[VOUT_MAX_SUBPICTURES];
    int channel_count = 0;
Laurent Aimar's avatar
Laurent Aimar committed
552

Laurent Aimar's avatar
Laurent Aimar committed
553 554 555
    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
556
            continue;
Laurent Aimar's avatar
Laurent Aimar committed
557
        const int i_channel = entry->subpicture->i_channel;
Laurent Aimar's avatar
Laurent Aimar committed
558
        int i;
Laurent Aimar's avatar
Laurent Aimar committed
559 560
        for (i = 0; i < channel_count; i++) {
            if (channel[i] == i_channel)
Laurent Aimar's avatar
Laurent Aimar committed
561 562
                break;
        }
Laurent Aimar's avatar
Laurent Aimar committed
563 564
        if (channel_count <= i)
            channel[channel_count++] = i_channel;
Laurent Aimar's avatar
Laurent Aimar committed
565 566
    }

Laurent Aimar's avatar
Laurent Aimar committed
567 568 569 570 571
    /* 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;
572

573
        mtime_t      start_date = render_subtitle_date;
574
        mtime_t      ephemer_subtitle_date = 0;
575
        mtime_t      ephemer_osd_date = 0;
Laurent Aimar's avatar
Laurent Aimar committed
576 577
        int64_t      ephemer_subtitle_order = INT64_MIN;
        int64_t      ephemer_system_order = INT64_MIN;
Laurent Aimar's avatar
Laurent Aimar committed
578

579
        /* Select available pictures */
Laurent Aimar's avatar
Laurent Aimar committed
580 581 582 583 584 585 586 587 588
        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
589 590 591
                continue;
            }

Laurent Aimar's avatar
Laurent Aimar committed
592 593
            if (current->i_channel != channel[i] ||
               (ignore_osd && !current->b_subtitle))
Laurent Aimar's avatar
Laurent Aimar committed
594
                continue;
Laurent Aimar's avatar
Laurent Aimar committed
595 596 597 598

            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
599 600 601 602
                /* Too early, come back next monday */
                continue;
            }

Laurent Aimar's avatar
Laurent Aimar committed
603 604 605 606 607 608
            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;
609
            }
Laurent Aimar's avatar
Laurent Aimar committed
610

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

Laurent Aimar's avatar
Laurent Aimar committed
613
            is_late = is_stop_valid && current->i_stop <= render_date;
614 615

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

            /* */
Laurent Aimar's avatar
Laurent Aimar committed
622 623 624
            available_subpic[available_count] = current;
            is_available_late[available_count] = is_late;
            available_count++;
Laurent Aimar's avatar
Laurent Aimar committed
625 626
        }

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

        /* Select pictures to be displayed */
Laurent Aimar's avatar
Laurent Aimar committed
634 635 636
        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
637

Laurent Aimar's avatar
Laurent Aimar committed
638 639 640
            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;
641

642
            /* Destroy late and obsolete ephemer subpictures */
Laurent Aimar's avatar
Laurent Aimar committed
643 644 645 646 647 648 649
            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
650
            }
651

Laurent Aimar's avatar
Laurent Aimar committed
652 653
            if (is_rejeted)
                SpuHeapDeleteSubpicture(&sys->heap, current);
Laurent Aimar's avatar
Laurent Aimar committed
654
            else
Laurent Aimar's avatar
Laurent Aimar committed
655
                subpicture_array[(*subpicture_count)++] = current;
Laurent Aimar's avatar
Laurent Aimar committed
656
        }
Laurent Aimar's avatar
Laurent Aimar committed
657
    }
658

Laurent Aimar's avatar
Laurent Aimar committed
659
    sys->last_sort_date = render_subtitle_date;
Laurent Aimar's avatar
Laurent Aimar committed
660 661
}

Laurent Aimar's avatar
Laurent Aimar committed
662 663 664 665 666


/**
 * It will transform the provided region into another region suitable for rendering.
 */
Laurent Aimar's avatar
Laurent Aimar committed
667 668 669 670 671 672 673 674
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)
675
{
Laurent Aimar's avatar
Laurent Aimar committed
676
    spu_private_t *sys = spu->p;
677

Laurent Aimar's avatar
Laurent Aimar committed
678 679 680 681
    video_format_t fmt_original = region->fmt;
    bool restore_text = false;
    int x_offset;
    int y_offset;
682

Laurent Aimar's avatar
Laurent Aimar committed
683
    video_format_t region_fmt;
Laurent Aimar's avatar
Laurent Aimar committed
684
    picture_t *region_picture;
685

Laurent Aimar's avatar
Laurent Aimar committed
686
    /* Invalidate area by default */
Laurent Aimar's avatar
Laurent Aimar committed
687 688
    *dst_area = spu_area_create(0,0, 0,0, scale_size);
    *dst_ptr  = NULL;
689

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

        /* Check if the rendering has failed ... */
Laurent Aimar's avatar
Laurent Aimar committed
697
        if (region->fmt.i_chroma == VLC_CODEC_TEXT)
Laurent Aimar's avatar
Laurent Aimar committed
698
            goto exit;
699 700
    }

Laurent Aimar's avatar
Laurent Aimar committed
701
    /* Force palette if requested
Laurent Aimar's avatar
Laurent Aimar committed
702
     * FIXME b_force_palette and force_crop are applied to all subpictures using palette
Laurent Aimar's avatar
Laurent Aimar committed
703 704
     * instead of only the right one (being the dvd spu).
     */
Laurent Aimar's avatar
Laurent Aimar committed
705 706 707 708
    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;
709

Laurent Aimar's avatar
Laurent Aimar committed
710 711 712
    /* 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
713 714 715
    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
716 717 718

    /* Place the picture
     * We compute the position in the rendered size */
Laurent Aimar's avatar
Laurent Aimar committed
719 720
    SpuRegionPlace(&x_offset, &y_offset,
                   subpic, region);
Laurent Aimar's avatar
Laurent Aimar committed
721 722 723

    /* 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
724 725 726
    *dst_area = spu_area_create(x_offset, y_offset,
                                region->fmt.i_width, region->fmt.i_height,
                                scale_size);
Laurent Aimar's avatar
Laurent Aimar committed
727 728

    /* Handle overlapping subtitles when possible */
Laurent Aimar's avatar
Laurent Aimar committed
729 730 731
    if (subpic->b_subtitle && !subpic->b_absolute)
        SpuAreaFixOverlap(dst_area, subtitle_area, subtitle_area_count,
                          region->i_align);
732

Laurent Aimar's avatar
Laurent Aimar committed
733 734
    /* 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
735
    spu_area_t restrained = *dst_area;
736

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

Laurent Aimar's avatar
Laurent Aimar committed
741 742 743
    spu_area_t display = spu_area_create(0, 0, fmt->i_width, fmt->i_height,
                                         spu_scale_unit());
    SpuAreaFitInside(&restrained, &display);
Laurent Aimar's avatar
Laurent Aimar committed
744

Laurent Aimar's avatar
Laurent Aimar committed
745
    /* Fix the position for the current scale_size */
Laurent Aimar's avatar
Laurent Aimar committed
746 747
    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
748 749

    /* */
Laurent Aimar's avatar
Laurent Aimar committed
750 751 752
    if (force_palette) {
        video_palette_t *old_palette = region->fmt.p_palette;
        video_palette_t new_palette;
Laurent Aimar's avatar
Laurent Aimar committed
753

Laurent Aimar's avatar
Laurent Aimar committed
754
        /* We suppose DVD palette here */
Laurent Aimar's avatar
Laurent Aimar committed
755 756 757 758 759 760 761 762 763 764 765
        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
766
        }
Laurent Aimar's avatar
Laurent Aimar committed
767
        *old_palette = new_palette;
Laurent Aimar's avatar
Laurent Aimar committed
768 769
    }

Laurent Aimar's avatar
Laurent Aimar committed
770
    /* */
Laurent Aimar's avatar
Laurent Aimar committed
771 772 773 774 775 776 777
    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;
778
    }
Laurent Aimar's avatar
Laurent Aimar committed
779

Laurent Aimar's avatar
Laurent Aimar committed
780
    /* Scale from rendered size to destination size */
Laurent Aimar's avatar
Laurent Aimar committed
781 782 783 784 785 786
    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)) {
        const unsigned dst_width  = spu_scale_w(region->fmt.i_width,  scale_size);
        const unsigned dst_height = spu_scale_h(region->fmt.i_height, scale_size);
Laurent Aimar's avatar
Laurent Aimar committed
787

Laurent Aimar's avatar
Laurent Aimar committed
788
        /* Destroy the cache if unusable */
Laurent Aimar's avatar
Laurent Aimar committed
789 790 791
        if (region->p_private) {
            subpicture_region_private_t *private = region->p_private;
            bool is_changed = false;
Laurent Aimar's avatar
Laurent Aimar committed
792

Laurent Aimar's avatar
Laurent Aimar committed
793
            /* Check resize changes */
Laurent Aimar's avatar
Laurent Aimar committed
794 795 796
            if (dst_width  != private->fmt.i_width ||
                dst_height != private->fmt.i_height)
                is_changed = true;
Laurent Aimar's avatar
Laurent Aimar committed
797

Laurent Aimar's avatar
Laurent Aimar committed
798
            /* Check forced palette changes */
Laurent Aimar's avatar
Laurent Aimar committed
799 800
            if (changed_palette)
                is_changed = true;
Laurent Aimar's avatar
Laurent Aimar committed
801

Laurent Aimar's avatar
Laurent Aimar committed
802 803
            if (convert_chroma && private->fmt.i_chroma != chroma_list[0])
                is_changed = true;
804

Laurent Aimar's avatar
Laurent Aimar committed
805 806 807
            if (is_changed) {
                subpicture_region_private_Delete(private);
                region->p_private = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
808 809
            }
        }
Laurent Aimar's avatar
Laurent Aimar committed
810

Laurent Aimar's avatar
Laurent Aimar committed
811
        /* Scale if needed into cache */
Laurent Aimar's avatar
Laurent Aimar committed
812 813
        if (!region->p_private && dst_width > 0 && dst_height > 0) {
            filter_t *scale = sys->scale;
Laurent Aimar's avatar
Laurent Aimar committed
814

Laurent Aimar's avatar
Laurent Aimar committed
815 816
            picture_t *picture = region->p_picture;
            picture_Hold(picture);
Laurent Aimar's avatar
Laurent Aimar committed
817

Laurent Aimar's avatar
Laurent Aimar committed
818
            /* Convert YUVP to YUVA/RGBA first for better scaling quality */
Laurent Aimar's avatar
Laurent Aimar committed
819 820
            if (using_palette) {
                filter_t *scale_yuvp = sys->scale_yuvp