vlm.c 42 KB
Newer Older
1
/*****************************************************************************
2
 * vlm.c: VLM interface plugin
3
 *****************************************************************************
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2000-2005 VLC authors and VideoLAN
5
 * $Id$
6 7 8
 *
 * Authors: Simon Latapie <garf@videolan.org>
 *          Laurent Aimar <fenrir@videolan.org>
Gildas Bazin's avatar
Gildas Bazin committed
9
 *          Gildas Bazin <gbazin@videolan.org>
10
 *
Jean-Baptiste Kempf's avatar
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
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
Jean-Baptiste Kempf committed
18 19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
20
 *
Jean-Baptiste Kempf's avatar
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.
24 25 26 27 28
 *****************************************************************************/

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

33
#include <vlc_common.h>
34 35

#include <stdio.h>
36
#include <ctype.h>                                              /* tolower() */
37 38
#include <time.h>                                                 /* ctime() */
#include <limits.h>
39
#include <assert.h>
40

41
#include <vlc_vlm.h>
42
#include <vlc_modules.h>
43

44 45
#include <vlc_input.h>
#include <vlc_stream.h>
46
#include "vlm_internal.h"
Laurent Aimar's avatar
Laurent Aimar committed
47
#include "vlm_event.h"
48
#include <vlc_vod.h>
49
#include <vlc_sout.h>
Pierre Ynard's avatar
Pierre Ynard committed
50
#include <vlc_url.h>
51
#include "../stream_output/stream_output.h"
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
52
#include "../libvlc.h"
53

54 55 56
/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
57

58
static void* Manage( void * );
59
static int vlm_MediaVodControl( void *, vod_media_t *, const char *, int, va_list );
60

61 62 63 64 65 66
typedef struct preparse_data_t
{
    vlc_sem_t *p_sem;
    bool b_mux;
} preparse_data_t;

67 68 69 70
static int InputEventPreparse( vlc_object_t *p_this, char const *psz_cmd,
                               vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
71
    preparse_data_t *p_pre = p_data;
72

73
    if( newval.i_int == INPUT_EVENT_DEAD ||
74 75
        ( p_pre->b_mux && newval.i_int == INPUT_EVENT_ITEM_META ) )
        vlc_sem_post( p_pre->p_sem );
76 77 78 79

    return VLC_SUCCESS;
}

80 81 82 83
static int InputEvent( vlc_object_t *p_this, char const *psz_cmd,
                       vlc_value_t oldval, vlc_value_t newval,
                       void *p_data )
{
84 85
    VLC_UNUSED(psz_cmd);
    VLC_UNUSED(oldval);
86
    input_thread_t *p_input = (input_thread_t *)p_this;
87
    vlm_t *p_vlm = libvlc_priv( p_input->obj.libvlc )->p_vlm;
88
    assert( p_vlm );
89 90 91 92 93 94 95 96 97 98 99 100 101 102
    vlm_media_sys_t *p_media = p_data;
    const char *psz_instance_name = NULL;

    if( newval.i_int == INPUT_EVENT_STATE )
    {
        for( int i = 0; i < p_media->i_instance; i++ )
        {
            if( p_media->instance[i]->p_input == p_input )
            {
                psz_instance_name = p_media->instance[i]->psz_name;
                break;
            }
        }
        vlm_SendEventMediaInstanceState( p_vlm, p_media->cfg.id, p_media->cfg.psz_name, psz_instance_name, var_GetInteger( p_input, "state" ) );
103

104 105 106 107
        vlc_mutex_lock( &p_vlm->lock_manage );
        p_vlm->input_state_changed = true;
        vlc_cond_signal( &p_vlm->wait_manage );
        vlc_mutex_unlock( &p_vlm->lock_manage );
108 109 110 111
    }
    return VLC_SUCCESS;
}

112 113 114
static vlc_mutex_t vlm_mutex = VLC_STATIC_MUTEX;

#undef vlm_New
115 116 117
/*****************************************************************************
 * vlm_New:
 *****************************************************************************/
118
vlm_t *vlm_New ( vlc_object_t *p_this )
119
{
120
    vlm_t *p_vlm = NULL, **pp_vlm = &(libvlc_priv (p_this->obj.libvlc)->p_vlm);
121
    char *psz_vlmconf;
Gildas Bazin's avatar
Gildas Bazin committed
122

123
    /* Avoid multiple creation */
124
    vlc_mutex_lock( &vlm_mutex );
125

126 127 128
    p_vlm = *pp_vlm;
    if( p_vlm )
    {   /* VLM already exists */
129 130 131 132
        if( likely( p_vlm->users < UINT_MAX ) )
            p_vlm->users++;
        else
            p_vlm = NULL;
133
        vlc_mutex_unlock( &vlm_mutex );
134
        return p_vlm;
135
    }
136

137
    msg_Dbg( p_this, "creating VLM" );
138

139
    p_vlm = vlc_custom_create( p_this->obj.libvlc, sizeof( *p_vlm ),
140
                               "vlm daemon" );
141 142
    if( !p_vlm )
    {
143
        vlc_mutex_unlock( &vlm_mutex );
144 145
        return NULL;
    }
146

147
    vlc_mutex_init( &p_vlm->lock );
148 149
    vlc_mutex_init( &p_vlm->lock_manage );
    vlc_cond_init_daytime( &p_vlm->wait_manage );
150
    p_vlm->users = 1;
151
    p_vlm->input_state_changed = false;
152 153 154 155
    p_vlm->i_id = 1;
    TAB_INIT( p_vlm->i_media, p_vlm->media );
    TAB_INIT( p_vlm->i_schedule, p_vlm->schedule );
    p_vlm->p_vod = NULL;
Laurent Aimar's avatar
Laurent Aimar committed
156
    var_Create( p_vlm, "intf-event", VLC_VAR_ADDRESS );
Gildas Bazin's avatar
Gildas Bazin committed
157

158
    if( vlc_clone( &p_vlm->thread, Manage, p_vlm, VLC_THREAD_PRIORITY_LOW ) )
159
    {
160
        vlc_cond_destroy( &p_vlm->wait_manage );
161
        vlc_mutex_destroy( &p_vlm->lock );
162
        vlc_mutex_destroy( &p_vlm->lock_manage );
163
        vlc_object_release( p_vlm );
164
        vlc_mutex_unlock( &vlm_mutex );
165
        return NULL;
166 167
    }

168 169
    *pp_vlm = p_vlm; /* for future reference */

170 171 172
    /* Load our configuration file */
    psz_vlmconf = var_CreateGetString( p_vlm, "vlm-conf" );
    if( psz_vlmconf && *psz_vlmconf )
Clément Stenac's avatar
Clément Stenac committed
173
    {
174 175
        vlm_message_t *p_message = NULL;
        char *psz_buffer = NULL;
176

177
        msg_Dbg( p_this, "loading VLM configuration" );
178
        if( asprintf(&psz_buffer, "load %s", psz_vlmconf ) != -1 )
179
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
180
            msg_Dbg( p_this, "%s", psz_buffer );
181 182
            if( vlm_ExecuteCommand( p_vlm, psz_buffer, &p_message ) )
                msg_Warn( p_this, "error while loading the configuration file" );
183

184 185
            vlm_MessageDelete( p_message );
            free( psz_buffer );
186
        }
Gildas Bazin's avatar
Gildas Bazin committed
187
    }
188
    free( psz_vlmconf );
189

190
    vlc_mutex_unlock( &vlm_mutex );
191

192
    return p_vlm;
193
}
Gildas Bazin's avatar
Gildas Bazin committed
194 195

/*****************************************************************************
196
 * vlm_Delete:
Gildas Bazin's avatar
Gildas Bazin committed
197
 *****************************************************************************/
198
void vlm_Delete( vlm_t *p_vlm )
Gildas Bazin's avatar
Gildas Bazin committed
199
{
200
    /* vlm_Delete() is serialized against itself, and against vlm_New().
201
     * This mutex protects libvlc_priv->p_vlm and p_vlm->users. */
202
    vlc_mutex_lock( &vlm_mutex );
203 204
    assert( p_vlm->users > 0 );
    if( --p_vlm->users == 0 )
205
        assert( libvlc_priv(p_vlm->obj.libvlc)->p_vlm == p_vlm );
206 207
    else
        p_vlm = NULL;
Gildas Bazin's avatar
Gildas Bazin committed
208

209
    if( p_vlm == NULL )
210 211
    {
        vlc_mutex_unlock( &vlm_mutex );
212
        return;
213
    }
214 215

    /* Destroy and release VLM */
216
    vlc_mutex_lock( &p_vlm->lock );
217 218
    vlm_ControlInternal( p_vlm, VLM_CLEAR_MEDIAS );
    TAB_CLEAN( p_vlm->i_media, p_vlm->media );
Gildas Bazin's avatar
Gildas Bazin committed
219

220
    vlm_ControlInternal( p_vlm, VLM_CLEAR_SCHEDULES );
221
    TAB_CLEAN( p_vlm->i_schedule, p_vlm->schedule );
222
    vlc_mutex_unlock( &p_vlm->lock );
223

224
    vlc_cancel( p_vlm->thread );
225

226 227 228 229 230 231
    if( p_vlm->p_vod )
    {
        module_unneed( p_vlm->p_vod, p_vlm->p_vod->p_module );
        vlc_object_release( p_vlm->p_vod );
    }

232
    libvlc_priv(p_vlm->obj.libvlc)->p_vlm = NULL;
233 234
    vlc_mutex_unlock( &vlm_mutex );

235
    vlc_join( p_vlm->thread, NULL );
236

237
    vlc_cond_destroy( &p_vlm->wait_manage );
238
    vlc_mutex_destroy( &p_vlm->lock );
239
    vlc_mutex_destroy( &p_vlm->lock_manage );
240
    vlc_object_release( p_vlm );
241
}
242

243 244 245 246 247 248 249
/*****************************************************************************
 * vlm_ExecuteCommand:
 *****************************************************************************/
int vlm_ExecuteCommand( vlm_t *p_vlm, const char *psz_command,
                        vlm_message_t **pp_message)
{
    int i_result;
250

251 252 253
    vlc_mutex_lock( &p_vlm->lock );
    i_result = ExecuteCommand( p_vlm, psz_command, pp_message );
    vlc_mutex_unlock( &p_vlm->lock );
254

255 256
    return i_result;
}
257 258 259 260 261 262 263 264 265 266 267 268 269 270

/*****************************************************************************
 *
 *****************************************************************************/
static int vlm_MediaVodControl( void *p_private, vod_media_t *p_vod_media,
                                const char *psz_id, int i_query, va_list args )
{
    vlm_t *vlm = (vlm_t *)p_private;
    int i, i_ret;
    const char *psz;
    int64_t id;

    if( !p_private || !p_vod_media )
        return VLC_EGENERIC;
271

272
    vlc_mutex_lock( &vlm->lock );
273

274 275 276 277
    /* Find media id */
    for( i = 0, id = -1; i < vlm->i_media; i++ )
    {
        if( p_vod_media == vlm->media[i]->vod.p_media )
278
        {
279 280
            id = vlm->media[i]->cfg.id;
            break;
281
        }
282 283 284 285 286
    }
    if( id == -1 )
    {
        vlc_mutex_unlock( &vlm->lock );
        return VLC_EGENERIC;
287
    }
Gildas Bazin's avatar
Gildas Bazin committed
288

289
    switch( i_query )
290
    {
291
    case VOD_MEDIA_PLAY:
292
    {
293
        psz = (const char *)va_arg( args, const char * );
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
        int64_t *i_time = (int64_t *)va_arg( args, int64_t *);
        bool b_retry = false;
        if (*i_time < 0)
        {
            /* No start time requested: return the current NPT */
            i_ret = vlm_ControlInternal( vlm, VLM_GET_MEDIA_INSTANCE_TIME, id, psz_id, i_time );
            /* The instance is not running yet, it will start at 0 */
            if (i_ret)
                *i_time = 0;
        }
        else
        {
            /* We want to seek before unpausing, but it won't
             * work if the instance is not running yet. */
            b_retry = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_TIME, id, psz_id, *i_time );
        }

311
        i_ret = vlm_ControlInternal( vlm, VLM_START_MEDIA_VOD_INSTANCE, id, psz_id, 0, psz );
312 313 314

        if (!i_ret && b_retry)
            i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_TIME, id, psz_id, *i_time );
315
        break;
316
    }
317

318
    case VOD_MEDIA_PAUSE:
319 320
    {
        int64_t *i_time = (int64_t *)va_arg( args, int64_t *);
321
        i_ret = vlm_ControlInternal( vlm, VLM_PAUSE_MEDIA_INSTANCE, id, psz_id );
322 323
        if (!i_ret)
            i_ret = vlm_ControlInternal( vlm, VLM_GET_MEDIA_INSTANCE_TIME, id, psz_id, i_time );
324
        break;
325
    }
326

327 328 329
    case VOD_MEDIA_STOP:
        i_ret = vlm_ControlInternal( vlm, VLM_STOP_MEDIA_INSTANCE, id, psz_id );
        break;
330

331 332
    case VOD_MEDIA_SEEK:
    {
333 334
        int64_t i_time = (int64_t)va_arg( args, int64_t );
        i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_TIME, id, psz_id, i_time );
335 336
        break;
    }
337

338 339 340 341
    case VOD_MEDIA_REWIND:
    {
        double d_scale = (double)va_arg( args, double );
        double d_position;
342

343 344 345 346 347 348 349
        vlm_ControlInternal( vlm, VLM_GET_MEDIA_INSTANCE_POSITION, id, psz_id, &d_position );
        d_position -= (d_scale / 1000.0);
        if( d_position < 0.0 )
            d_position = 0.0;
        i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, psz_id, d_position );
        break;
    }
350

351 352 353 354
    case VOD_MEDIA_FORWARD:
    {
        double d_scale = (double)va_arg( args, double );
        double d_position;
355

356 357 358 359 360 361 362
        vlm_ControlInternal( vlm, VLM_GET_MEDIA_INSTANCE_POSITION, id, psz_id, &d_position );
        d_position += (d_scale / 1000.0);
        if( d_position > 1.0 )
            d_position = 1.0;
        i_ret = vlm_ControlInternal( vlm, VLM_SET_MEDIA_INSTANCE_POSITION, id, psz_id, d_position );
        break;
    }
Gildas Bazin's avatar
Gildas Bazin committed
363

364 365 366 367
    default:
        i_ret = VLC_EGENERIC;
        break;
    }
368

369
    vlc_mutex_unlock( &vlm->lock );
Gildas Bazin's avatar
Gildas Bazin committed
370

371 372
    return i_ret;
}
373

Gildas Bazin's avatar
Gildas Bazin committed
374

375 376 377
/*****************************************************************************
 * Manage:
 *****************************************************************************/
378
static void* Manage( void* p_object )
379 380
{
    vlm_t *vlm = (vlm_t*)p_object;
381
    time_t lastcheck, now, nextschedule = 0;
382

383
    time(&lastcheck);
384

385
    for( ;; )
386
    {
387 388
        char **ppsz_scheduled_commands = NULL;
        int    i_scheduled_commands = 0;
389
        bool scheduled_command = false;
390

391
        vlc_mutex_lock( &vlm->lock_manage );
392
        mutex_cleanup_push( &vlm->lock_manage );
393 394
        while( !vlm->input_state_changed && !scheduled_command )
        {
395
            if( nextschedule != 0 )
396
                scheduled_command = vlc_cond_timedwait_daytime( &vlm->wait_manage, &vlm->lock_manage, nextschedule ) != 0;
397 398 399 400
            else
                vlc_cond_wait( &vlm->wait_manage, &vlm->lock_manage );
        }
        vlm->input_state_changed = false;
401 402
        vlc_cleanup_pop( );
        vlc_mutex_unlock( &vlm->lock_manage );
403 404

        int canc = vlc_savecancel ();
405
        /* destroy the inputs that wants to die, and launch the next input */
406
        vlc_mutex_lock( &vlm->lock );
407
        for( int i = 0; i < vlm->i_media; i++ )
408
        {
409
            vlm_media_sys_t *p_media = vlm->media[i];
410

411
            for( int j = 0; j < p_media->i_instance; )
412 413
            {
                vlm_media_instance_sys_t *p_instance = p_media->instance[j];
414
                int state = INIT_S;
415

416 417 418
                if( p_instance->p_input != NULL )
                    state = var_GetInteger( p_instance->p_input, "state" );
                if( state == END_S || state == ERROR_S )
419 420
                {
                    int i_new_input_index;
421

422 423 424 425 426 427 428 429 430
                    /* */
                    i_new_input_index = p_instance->i_index + 1;
                    if( !p_media->cfg.b_vod && p_media->cfg.broadcast.b_loop && i_new_input_index >= p_media->cfg.i_input )
                        i_new_input_index = 0;

                    /* FIXME implement multiple input with VOD */
                    if( p_media->cfg.b_vod || i_new_input_index >= p_media->cfg.i_input )
                        vlm_ControlInternal( vlm, VLM_STOP_MEDIA_INSTANCE, p_media->cfg.id, p_instance->psz_name );
                    else
431
                        vlm_ControlInternal( vlm, VLM_START_MEDIA_BROADCAST_INSTANCE, p_media->cfg.id, p_instance->psz_name, i_new_input_index );
432 433 434 435

                    j = 0;
                }
                else
Gildas Bazin's avatar
Gildas Bazin committed
436 437
                {
                    j++;
438
                }
Gildas Bazin's avatar
Gildas Bazin committed
439
            }
440
        }
Gildas Bazin's avatar
Gildas Bazin committed
441

442
        /* scheduling */
443 444
        time(&now);
        nextschedule = 0;
445

446
        for( int i = 0; i < vlm->i_schedule; i++ )
447
        {
448
            time_t real_date = vlm->schedule[i]->date;
449

450
            if( vlm->schedule[i]->b_enabled )
Gildas Bazin's avatar
Gildas Bazin committed
451
            {
452
                if( vlm->schedule[i]->date == 0 ) // now !
453
                {
454 455
                    vlm->schedule[i]->date = now;
                    real_date = now;
456
                }
457
                else if( vlm->schedule[i]->period != 0 )
458 459
                {
                    int j = 0;
460 461
                    while( ((vlm->schedule[i]->date + j *
                             vlm->schedule[i]->period) <= lastcheck) &&
462
                           ( vlm->schedule[i]->i_repeat > j ||
463
                             vlm->schedule[i]->i_repeat < 0 ) )
464 465 466
                    {
                        j++;
                    }
467

468 469
                    real_date = vlm->schedule[i]->date + j *
                        vlm->schedule[i]->period;
470
                }
471

472
                if( real_date <= now )
473
                {
474
                    if( real_date > lastcheck )
475
                    {
476
                        for( int j = 0; j < vlm->schedule[i]->i_command; j++ )
477 478 479 480 481
                        {
                            TAB_APPEND( i_scheduled_commands,
                                        ppsz_scheduled_commands,
                                        strdup(vlm->schedule[i]->command[j] ) );
                        }
482 483
                    }
                }
484
                else if( nextschedule == 0 || real_date < nextschedule )
485
                {
486
                    nextschedule = real_date;
487
                }
488 489
            }
        }
490

491 492 493 494 495 496 497 498 499 500 501 502 503
        while( i_scheduled_commands )
        {
            vlm_message_t *message = NULL;
            char *psz_command = ppsz_scheduled_commands[0];
            ExecuteCommand( vlm, psz_command,&message );

            /* for now, drop the message */
            vlm_MessageDelete( message );
            TAB_REMOVE( i_scheduled_commands,
                        ppsz_scheduled_commands,
                        psz_command );
            free( psz_command );
        }
504

505
        lastcheck = now;
506
        vlc_mutex_unlock( &vlm->lock );
507
        vlc_restorecancel (canc);
508
    }
Gildas Bazin's avatar
Gildas Bazin committed
509

510
    return NULL;
511 512 513 514 515 516 517 518
}

/* New API
 */
/*
typedef struct
{
    struct
519
    {
520 521 522 523 524 525
        int i_connection_count;
        int i_connection_active;
    } vod;
    struct
    {
        int        i_count;
526
        bool b_playing;
527 528
        int        i_playing_index;
    } broadcast;
529

530 531
} vlm_media_status_t;
*/
532

533 534 535
/* */
static vlm_media_sys_t *vlm_ControlMediaGetById( vlm_t *p_vlm, int64_t id )
{
536
    for( int i = 0; i < p_vlm->i_media; i++ )
537 538 539
    {
        if( p_vlm->media[i]->cfg.id == id )
            return p_vlm->media[i];
540
    }
541 542 543 544
    return NULL;
}
static vlm_media_sys_t *vlm_ControlMediaGetByName( vlm_t *p_vlm, const char *psz_name )
{
545
    for( int i = 0; i < p_vlm->i_media; i++ )
546
    {
547 548
        if( !strcmp( p_vlm->media[i]->cfg.psz_name, psz_name ) )
            return p_vlm->media[i];
549
    }
550
    return NULL;
551
}
552
static int vlm_MediaDescriptionCheck( vlm_t *p_vlm, vlm_media_t *p_cfg )
553
{
554 555 556
    if( !p_cfg || !p_cfg->psz_name ||
        !strcmp( p_cfg->psz_name, "all" ) || !strcmp( p_cfg->psz_name, "media" ) || !strcmp( p_cfg->psz_name, "schedule" ) )
        return VLC_EGENERIC;
557

558
    for( int i = 0; i < p_vlm->i_media; i++ )
559
    {
560 561 562 563 564 565 566
        if( p_vlm->media[i]->cfg.id == p_cfg->id )
            continue;
        if( !strcmp( p_vlm->media[i]->cfg.psz_name, p_cfg->psz_name ) )
            return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}
567 568


569 570 571 572 573
/* Called after a media description is changed/added */
static int vlm_OnMediaUpdate( vlm_t *p_vlm, vlm_media_sys_t *p_media )
{
    vlm_media_t *p_cfg = &p_media->cfg;
    /* Check if we need to create/delete a vod media */
574
    if( p_cfg->b_vod && p_vlm->p_vod )
575 576 577 578 579 580 581 582 583 584 585 586 587
    {
        if( !p_cfg->b_enabled && p_media->vod.p_media )
        {
            p_vlm->p_vod->pf_media_del( p_vlm->p_vod, p_media->vod.p_media );
            p_media->vod.p_media = NULL;
        }
        else if( p_cfg->b_enabled && !p_media->vod.p_media && p_cfg->i_input )
        {
            /* Pre-parse the input */
            input_thread_t *p_input;
            char *psz_output;
            char *psz_header;
            char *psz_dup;
588

589
            input_item_Release( p_media->vod.p_item );
Pierre Ynard's avatar
Pierre Ynard committed
590

591 592 593 594 595 596 597 598 599 600
            if( strstr( p_cfg->ppsz_input[0], "://" ) == NULL )
            {
                char *psz_uri = vlc_path2uri( p_cfg->ppsz_input[0], NULL );
                p_media->vod.p_item = input_item_New( psz_uri,
                                                      p_cfg->psz_name );
                free( psz_uri );
            }
            else
                p_media->vod.p_item = input_item_New( p_cfg->ppsz_input[0],
                                                      p_cfg->psz_name );
601 602

            if( p_cfg->psz_output )
603 604 605 606
            {
                if( asprintf( &psz_output, "%s:description", p_cfg->psz_output )  == -1 )
                    psz_output = NULL;
            }
607
            else
608
                psz_output = strdup( "#description" );
609

610
            if( psz_output && asprintf( &psz_dup, "sout=%s", psz_output ) != -1 )
611
            {
612
                input_item_AddOption( p_media->vod.p_item, psz_dup, VLC_INPUT_OPTION_TRUSTED );
613 614
                free( psz_dup );
            }
615 616
            free( psz_output );

617
            for( int i = 0; i < p_cfg->i_option; i++ )
618
                input_item_AddOption( p_media->vod.p_item,
619
                                      p_cfg->ppsz_option[i], VLC_INPUT_OPTION_TRUSTED );
620

621 622
            if( asprintf( &psz_header, _("Media: %s"), p_cfg->psz_name ) == -1 )
                psz_header = NULL;
623

624 625 626
            sout_description_data_t data;
            TAB_INIT(data.i_es, data.es);

627
            p_input = input_Create( p_vlm->p_vod, p_media->vod.p_item, psz_header, NULL, NULL );
628
            if( p_input )
629
            {
630 631
                vlc_sem_t sem_preparse;
                vlc_sem_init( &sem_preparse, 0 );
632 633 634 635 636 637

                preparse_data_t preparse = { .p_sem = &sem_preparse,
                                    .b_mux = (p_cfg->vod.psz_mux != NULL) };
                var_AddCallback( p_input, "intf-event", InputEventPreparse,
                                 &preparse );

638 639 640
                data.sem = &sem_preparse;
                var_Create( p_input, "sout-description-data", VLC_VAR_ADDRESS );
                var_SetAddress( p_input, "sout-description-data", &data );
641

642
                if( !input_Start( p_input ) )
643
                    vlc_sem_wait( &sem_preparse );
644

645 646
                var_DelCallback( p_input, "intf-event", InputEventPreparse,
                                 &preparse );
647

648
                input_Stop( p_input );
649
                input_Close( p_input );
650
                vlc_sem_destroy( &sem_preparse );
651 652
            }
            free( psz_header );
653

654 655
            /* XXX: Don't do it that way, but properly use a new input item ref. */
            input_item_t item = *p_media->vod.p_item;;
656
            es_format_t es, *p_es = &es;
657 658
            if( p_cfg->vod.psz_mux )
            {
659 660 661 662 663 664 665 666
                const char *psz_mux;
                if (!strcmp(p_cfg->vod.psz_mux, "ps"))
                    psz_mux = "mp2p";
                else if (!strcmp(p_cfg->vod.psz_mux, "ts"))
                    psz_mux = "mp2t";
                else
                    psz_mux = p_cfg->vod.psz_mux;

667 668 669 670 671
                union {
                    char text[5];
                    unsigned char utext[5];
                    uint32_t value;
                } fourcc;
672

673
                sprintf( fourcc.text, "%4.4s", psz_mux );
674 675
                for( int i = 0; i < 4; i++ )
                    fourcc.utext[i] = tolower(fourcc.utext[i]);
676 677 678

                item.i_es = 1;
                item.es = &p_es;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
679
                es_format_Init( &es, VIDEO_ES, fourcc.value );
680 681
            }
            else
682
            {
683 684
                item.i_es = data.i_es;
                item.es = data.es;
685
            }
686 687 688 689
            p_media->vod.p_media = p_vlm->p_vod->pf_media_new( p_vlm->p_vod,
                                                    p_cfg->psz_name, &item );

            TAB_CLEAN(data.i_es, data.es);
690
        }
691
    }
692 693
    else if ( p_cfg->b_vod )
        msg_Err( p_vlm, "vod server is not loaded" );
694 695 696
    else
    {
        /* TODO start media if needed */
697
    }
Gildas Bazin's avatar
Gildas Bazin committed
698

699
    /* TODO add support of var vlm_media_broadcast/vlm_media_vod */
700

701
    vlm_SendEventMediaChanged( p_vlm, p_cfg->id, p_cfg->psz_name );
702 703 704
    return VLC_SUCCESS;
}
static int vlm_ControlMediaChange( vlm_t *p_vlm, vlm_media_t *p_cfg )
705
{
706
    vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, p_cfg->id );
707

708 709 710 711 712 713 714
    /* */
    if( !p_media || vlm_MediaDescriptionCheck( p_vlm, p_cfg ) )
        return VLC_EGENERIC;
    if( ( p_media->cfg.b_vod && !p_cfg->b_vod ) || ( !p_media->cfg.b_vod && p_cfg->b_vod ) )
        return VLC_EGENERIC;

    if( 0 )
715
    {
716 717
        /* TODO check what are the changes being done (stop instance if needed) */
    }
718

719 720
    vlm_media_Clean( &p_media->cfg );
    vlm_media_Copy( &p_media->cfg, p_cfg );
721

722 723
    return vlm_OnMediaUpdate( p_vlm, p_media );
}
724

725 726 727
static int vlm_ControlMediaAdd( vlm_t *p_vlm, vlm_media_t *p_cfg, int64_t *p_id )
{
    vlm_media_sys_t *p_media;
728

729 730 731 732 733 734
    if( vlm_MediaDescriptionCheck( p_vlm, p_cfg ) || vlm_ControlMediaGetByName( p_vlm, p_cfg->psz_name ) )
    {
        msg_Err( p_vlm, "invalid media description" );
        return VLC_EGENERIC;
    }
    /* Check if we need to load the VOD server */
735
    if( p_cfg->b_vod && !p_vlm->p_vod )
736
    {
737
        p_vlm->p_vod = vlc_custom_create( VLC_OBJECT(p_vlm), sizeof( vod_t ),
738
                                          "vod server" );
739
        p_vlm->p_vod->p_module = module_need( p_vlm->p_vod, "vod server", "$vod-server", false );
740
        if( !p_vlm->p_vod->p_module )
741
        {
742
            msg_Err( p_vlm, "cannot find vod server" );
743
            vlc_object_release( p_vlm->p_vod );
744
            p_vlm->p_vod = NULL;
745
            return VLC_EGENERIC;
746 747
        }

748 749 750
        p_vlm->p_vod->p_data = p_vlm;
        p_vlm->p_vod->pf_media_control = vlm_MediaVodControl;
    }
751

752
    p_media = calloc( 1, sizeof( vlm_media_sys_t ) );
753 754
    if( !p_media )
        return VLC_ENOMEM;
755

756 757 758
    vlm_media_Copy( &p_media->cfg, p_cfg );
    p_media->cfg.id = p_vlm->i_id++;
    /* FIXME do we do something here if enabled is true ? */
759

760
    p_media->vod.p_item = input_item_New( NULL, NULL );
761

762 763
    p_media->vod.p_media = NULL;
    TAB_INIT( p_media->i_instance, p_media->instance );
764

765 766
    /* */
    TAB_APPEND( p_vlm->i_media, p_vlm->media, p_media );
767

768 769
    if( p_id )
        *p_id = p_media->cfg.id;
770

Laurent Aimar's avatar
Laurent Aimar committed
771
    /* */
772
    vlm_SendEventMediaAdded( p_vlm, p_media->cfg.id, p_media->cfg.psz_name );
773 774
    return vlm_OnMediaUpdate( p_vlm, p_media );
}
775

776 777 778
static int vlm_ControlMediaDel( vlm_t *p_vlm, int64_t id )
{
    vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
779

780 781
    if( !p_media )
        return VLC_EGENERIC;
782

783 784
    while( p_media->i_instance > 0 )
        vlm_ControlInternal( p_vlm, VLM_STOP_MEDIA_INSTANCE, id, p_media->instance[0]->psz_name );
785

786 787
    if( p_media->cfg.b_vod )
    {
788
        p_media->cfg.b_enabled = false;
789
        vlm_OnMediaUpdate( p_vlm, p_media );
790 791
    }

792 793 794
    /* */
    vlm_SendEventMediaRemoved( p_vlm, id, p_media->cfg.psz_name );

795
    vlm_media_Clean( &p_media->cfg );
796

797
    input_item_Release( p_media->vod.p_item );
798

799 800
    if( p_media->vod.p_media )
        p_vlm->p_vod->pf_media_del( p_vlm->p_vod, p_media->vod.p_media );
801

802
    TAB_REMOVE( p_vlm->i_media, p_vlm->media, p_media );
803 804 805 806
    free( p_media );

    return VLC_SUCCESS;
}
807

808 809 810 811
static int vlm_ControlMediaGets( vlm_t *p_vlm, vlm_media_t ***ppp_dsc, int *pi_dsc )
{
    vlm_media_t **pp_dsc;
    int                     i_dsc;
812

813
    TAB_INIT( i_dsc, pp_dsc );
814
    for( int i = 0; i < p_vlm->i_media; i++ )
815 816 817 818
    {
        vlm_media_t *p_dsc = vlm_media_Duplicate( &p_vlm->media[i]->cfg );
        TAB_APPEND( i_dsc, pp_dsc, p_dsc );
    }
819

820 821
    *ppp_dsc = pp_dsc;
    *pi_dsc = i_dsc;
822

823 824 825 826 827 828
    return VLC_SUCCESS;
}
static int vlm_ControlMediaClear( vlm_t *p_vlm )
{
    while( p_vlm->i_media > 0 )
        vlm_ControlMediaDel( p_vlm, p_vlm->media[0]->cfg.id );
829

830 831 832 833 834 835 836
    return VLC_SUCCESS;
}
static int vlm_ControlMediaGet( vlm_t *p_vlm, int64_t id, vlm_media_t **pp_dsc )
{
    vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
    if( !p_media )
        return VLC_EGENERIC;
837

838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
    *pp_dsc = vlm_media_Duplicate( &p_media->cfg );
    return VLC_SUCCESS;
}
static int vlm_ControlMediaGetId( vlm_t *p_vlm, const char *psz_name, int64_t *p_id )
{
    vlm_media_sys_t *p_media = vlm_ControlMediaGetByName( p_vlm, psz_name );
    if( !p_media )
        return VLC_EGENERIC;

    *p_id = p_media->cfg.id;
    return VLC_SUCCESS;
}

static vlm_media_instance_sys_t *vlm_ControlMediaInstanceGetByName( vlm_media_sys_t *p_media, const char *psz_id )
{
853
    for( int i = 0; i < p_media->i_instance; i++ )
854 855 856 857 858
    {
        const char *psz = p_media->instance[i]->psz_name;
        if( ( psz == NULL && psz_id == NULL ) ||
            ( psz && psz_id && !strcmp( psz, psz_id ) ) )
            return p_media->instance[i];
859
    }
860 861 862 863
    return NULL;
}
static vlm_media_instance_sys_t *vlm_MediaInstanceNew( vlm_t *p_vlm, const char *psz_name )
{
864
    vlm_media_instance_sys_t *p_instance = calloc( 1, sizeof(vlm_media_instance_sys_t) );
865 866
    if( !p_instance )
        return NULL;
867

868 869 870 871
    p_instance->psz_name = NULL;
    if( psz_name )
        p_instance->psz_name = strdup( psz_name );

872
    p_instance->p_item = input_item_New( NULL, NULL );
873 874

    p_instance->i_index = 0;
875
    p_instance->b_sout_keep = false;
876
    p_instance->p_parent = vlc_object_create( p_vlm, sizeof (vlc_object_t) );
877
    p_instance->p_input = NULL;
878
    p_instance->p_input_resource = input_resource_New( p_instance->p_parent );
879 880 881

    return p_instance;
}
882
static void vlm_MediaInstanceDelete( vlm_t *p_vlm, int64_t id, vlm_media_instance_sys_t *p_instance, vlm_media_sys_t *p_media )
883
{
884 885
    input_thread_t *p_input = p_instance->p_input;
    if( p_input )
886
    {
887
        input_Stop( p_input );
888
        input_Close( p_input );
Laurent Aimar's avatar
Laurent Aimar committed
889

890
        vlm_SendEventMediaInstanceStopped( p_vlm, id, p_media->cfg.psz_name );
891
    }
892 893
    input_resource_Terminate( p_instance->p_input_resource );
    input_resource_Release( p_instance->p_input_resource );
894
    vlc_object_release( p_instance->p_parent );
895

896
    TAB_REMOVE( p_media->i_instance, p_media->instance, p_instance );
897
    input_item_Release( p_instance->p_item );
898
    free( p_instance->psz_name );
899 900 901 902 903 904 905 906 907
    free( p_instance );
}


static int vlm_ControlMediaInstanceStart( vlm_t *p_vlm, int64_t id, const char *psz_id, int i_input_index, const char *psz_vod_output )
{
    vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
    vlm_media_instance_sys_t *p_instance;
    char *psz_log;
908

909 910
    if( !p_media || !p_media->cfg.b_enabled || p_media->cfg.i_input <= 0 )
        return VLC_EGENERIC;
911

912
    /* TODO support multiple input for VOD with sout-keep ? */
913

914 915
    if( ( p_media->cfg.b_vod && !psz_vod_output ) || ( !p_media->cfg.b_vod && psz_vod_output ) )
        return VLC_EGENERIC;
916

917 918
    if( i_input_index < 0 || i_input_index >= p_media->cfg.i_input )
        return VLC_EGENERIC;
919

920 921 922 923
    p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
    if( !p_instance )
    {
        vlm_media_t *p_cfg = &p_media->cfg;
924

925 926 927
        p_instance = vlm_MediaInstanceNew( p_vlm, psz_id );
        if( !p_instance )
            return VLC_ENOMEM;
928

929 930 931 932 933 934 935 936 937
        if ( p_cfg->b_vod )
        {
            var_Create( p_instance->p_parent, "vod-media", VLC_VAR_ADDRESS );
            var_SetAddress( p_instance->p_parent, "vod-media",
                            p_media->vod.p_media );
            var_Create( p_instance->p_parent, "vod-session", VLC_VAR_STRING );
            var_SetString( p_instance->p_parent, "vod-session", psz_id );
        }

938 939 940
        if( p_cfg->psz_output != NULL || psz_vod_output != NULL )
        {
            char *psz_buffer;
941
            if( asprintf( &psz_buffer, "sout=%s%s%s",
942 943
                      p_cfg->psz_output ? p_cfg->psz_output : "",
                      (p_cfg->psz_output && psz_vod_output) ? ":" : psz_vod_output ? "#" : "",
944 945
                      psz_vod_output ? psz_vod_output : "" ) != -1 )
            {
946
                input_item_AddOption( p_instance->p_item, psz_buffer, VLC_INPUT_OPTION_TRUSTED );
947 948
                free( psz_buffer );
            }
949 950
        }

951
        for( int i = 0; i < p_cfg->i_option; i++ )
952
        {
953
            if( !strcmp( p_cfg->ppsz_option[i], "sout-keep" ) )
954
                p_instance->b_sout_keep = true;
955
            else if( !strcmp( p_cfg->ppsz_option[i], "nosout-keep" ) || !strcmp( p_cfg->ppsz_option[i], "no-sout-keep" ) )
956
                p_instance->b_sout_keep = false;
957
            else
958
                input_item_AddOption( p_instance->p_item, p_cfg->ppsz_option[i], VLC_INPUT_OPTION_TRUSTED );
959
        }
960 961 962 963
        TAB_APPEND( p_media->i_instance, p_media->instance, p_instance );
    }

    /* Stop old instance */
964 965
    input_thread_t *p_input = p_instance->p_input;
    if( p_input )
966
    {
967
        if( p_instance->i_index == i_input_index )
968
        {
969 970
            int state = var_GetInteger( p_input, "state" );
            if( state == PAUSE_S )
971
                var_SetInteger( p_input, "state",  PLAYING_S );
972
            return VLC_SUCCESS;
973 974
        }

975
        input_Stop( p_input );
976
        input_Close( p_input );
977

978
        if( !p_instance->b_sout_keep )
979 980
            input_resource_TerminateSout( p_instance->p_input_resource );
        input_resource_TerminateVout( p_instance->p_input_resource );
Laurent Aimar's avatar
Laurent Aimar committed
981

982
        vlm_SendEventMediaInstanceStopped( p_vlm, id, p_media->cfg.psz_name );
983 984 985 986
    }

    /* Start new one */
    p_instance->i_index = i_input_index;
987 988 989 990 991 992 993 994 995
    if( strstr( p_media->cfg.ppsz_input[p_instance->i_index], "://" ) == NULL )
    {
        char *psz_uri = vlc_path2uri(
                          p_media->cfg.ppsz_input[p_instance->i_index], NULL );
        input_item_SetURI( p_instance->p_item, psz_uri ) ;
        free( psz_uri );
    }
    else
        input_item_SetURI( p_instance->p_item, p_media->cfg.ppsz_input[p_instance->i_index] ) ;
996

997
    if( asprintf( &psz_log, _("Media: %s"), p_media->cfg.psz_name ) != -1 )
998
    {
999 1000
        p_instance->p_input = input_Create( p_instance->p_parent,
                                            p_instance->p_item, psz_log,
1001 1002
                                            p_instance->p_input_resource,
                                            NULL );
1003
        if( p_instance->p_input )
1004
        {
1005
            var_AddCallback( p_instance->p_input, "intf-event", InputEvent, p_media );
1006

1007 1008 1009
            if( input_Start( p_instance->p_input ) != VLC_SUCCESS )
            {
                var_DelCallback( p_instance->p_input, "intf-event", InputEvent, p_media );
1010
                input_Close( p_instance->p_input );
1011 1012
                p_instance->p_input = NULL;
            }
1013
        }
1014

1015 1016
        if( !p_instance->p_input )
        {
1017
            vlm_MediaInstanceDelete( p_vlm, id, p_instance, p_media );
Laurent Aimar's avatar
Laurent Aimar committed
1018 1019 1020
        }
        else
        {
1021
            vlm_SendEventMediaInstanceStarted( p_vlm, id, p_media->cfg.psz_name );
1022 1023
        }
        free( psz_log );
1024 1025
    }

1026
    return VLC_SUCCESS;
1027 1028
}

1029
static int vlm_ControlMediaInstanceStop( vlm_t *p_vlm, int64_t id, const char *psz_id )
1030
{
1031 1032
    vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
    vlm_media_instance_sys_t *p_instance;
1033

1034 1035
    if( !p_media )
        return VLC_EGENERIC;
1036

1037 1038
    p_instance = vlm_ControlMediaInstanceGetByName( p_media, psz_id );
    if( !p_instance )
1039 1040
        return VLC_EGENERIC;

1041
    vlm_MediaInstanceDelete( p_vlm, id, p_instance, p_media );
1042

1043 1044 1045 1046 1047 1048 1049
    return VLC_SUCCESS;
}
static int vlm_ControlMediaInstancePause( vlm_t *p_vlm, int64_t id, const char *psz_id )
{
    vlm_media_sys_t *p_media = vlm_ControlMediaGetById( p_vlm, id );
    vlm_media_instance_sys_t *p_instance;
    int i_state;
1050