npolibvlc.cpp 60.6 KB
Newer Older
Rafaël Carré's avatar
Rafaël Carré committed
1 2 3
/*****************************************************************************
 * npolibvlc.cpp: official Javascript APIs
 *****************************************************************************
4
 * Copyright (C) 2002-2014 VLC authors and VideoLAN
5
 * Copyright (C) 2010 M2X BV
Rafaël Carré's avatar
Rafaël Carré committed
6 7
 *
 * Authors: Damien Fouilleul <Damien.Fouilleul@laposte.net>
8
 *          JP Dinger <jpd@videolan.org>
9
 *          Felix Paul Kühne <fkuehne # videolan.org>
Rafaël Carré's avatar
Rafaël Carré committed
10 11 12 13 14 15 16 17 18 19 20
 *
 * 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.
 *
21 22 23
 * You should have received a copy of the GNU 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.
Rafaël Carré's avatar
Rafaël Carré committed
24 25 26 27 28 29 30 31 32
 *****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "vlcplugin.h"
#include "npolibvlc.h"

33
#include "../../common/position.h"
34

35 36 37
/*
** Local helper macros and function
*/
38
#define COUNTNAMES(a,b,c) const int a::b = sizeof(a::c)/sizeof(NPUTF8 *)
39 40 41 42 43
#define RETURN_ON_ERROR                             \
    do {                                            \
        NPN_SetException(this, libvlc_errmsg());    \
        return INVOKERESULT_GENERIC_ERROR;          \
    }while(0)
44

45 46 47 48
#define ERROR_EVENT_NOT_FOUND "ERROR: One or more events could not be found."
#define ERROR_API_VERSION "ERROR: NPAPI version not high enough. (Gecko >= 1.9 needed)"


Rafaël Carré's avatar
Rafaël Carré committed
49 50 51 52 53 54 55
/*
** implementation of libvlc root object
*/

LibvlcRootNPObject::~LibvlcRootNPObject()
{
    /*
56 57 58 59 60 61
    ** When the plugin is destroyed, firefox takes it upon itself to
    ** destroy all 'live' script objects and ignores refcounting.
    ** Therefore we cannot safely assume that refcounting will control
    ** lifespan of objects. Hence they are only lazily created on
    ** request, so that firefox can take ownership, and are not released
    ** when the plugin is destroyed.
Rafaël Carré's avatar
Rafaël Carré committed
62 63 64 65 66 67
    */
    if( isValid() )
    {
        if( audioObj    ) NPN_ReleaseObject(audioObj);
        if( inputObj    ) NPN_ReleaseObject(inputObj);
        if( playlistObj ) NPN_ReleaseObject(playlistObj);
68
        if( subtitleObj ) NPN_ReleaseObject(subtitleObj);
Rafaël Carré's avatar
Rafaël Carré committed
69
        if( videoObj    ) NPN_ReleaseObject(videoObj);
70
        if( mediaDescriptionObj ) NPN_ReleaseObject(mediaDescriptionObj);
Rafaël Carré's avatar
Rafaël Carré committed
71 72 73
    }
}

74
const NPUTF8 * const LibvlcRootNPObject::propertyNames[] =
Rafaël Carré's avatar
Rafaël Carré committed
75 76 77 78
{
    "audio",
    "input",
    "playlist",
79
    "subtitle",
Rafaël Carré's avatar
Rafaël Carré committed
80 81
    "video",
    "VersionInfo",
82
    "mediaDescription"
Rafaël Carré's avatar
Rafaël Carré committed
83
};
84
COUNTNAMES(LibvlcRootNPObject,propertyCount,propertyNames);
Rafaël Carré's avatar
Rafaël Carré committed
85 86 87 88 89 90

enum LibvlcRootNPObjectPropertyIds
{
    ID_root_audio = 0,
    ID_root_input,
    ID_root_playlist,
91
    ID_root_subtitle,
Rafaël Carré's avatar
Rafaël Carré committed
92 93
    ID_root_video,
    ID_root_VersionInfo,
94
    ID_root_MediaDescription,
Rafaël Carré's avatar
Rafaël Carré committed
95 96
};

97
RuntimeNPObject::InvokeResult
98
LibvlcRootNPObject::getProperty(int index, npapi::OutVariant &result)
Rafaël Carré's avatar
Rafaël Carré committed
99 100
{
    /* is plugin still running */
101
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
102 103 104 105
    {
        switch( index )
        {
            case ID_root_audio:
106
                InstantObj<LibvlcAudioNPObject>( audioObj );
107
                result = audioObj;
Rafaël Carré's avatar
Rafaël Carré committed
108 109
                return INVOKERESULT_NO_ERROR;
            case ID_root_input:
110
                InstantObj<LibvlcInputNPObject>( inputObj );
111
                result = inputObj;
Rafaël Carré's avatar
Rafaël Carré committed
112 113
                return INVOKERESULT_NO_ERROR;
            case ID_root_playlist:
114
                InstantObj<LibvlcPlaylistNPObject>( playlistObj );
115
                result = playlistObj;
Rafaël Carré's avatar
Rafaël Carré committed
116
                return INVOKERESULT_NO_ERROR;
117
            case ID_root_subtitle:
118
                InstantObj<LibvlcSubtitleNPObject>( subtitleObj );
119
                result = subtitleObj;
120
                return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
121
            case ID_root_video:
122
                InstantObj<LibvlcVideoNPObject>( videoObj );
123
                result = videoObj;
Rafaël Carré's avatar
Rafaël Carré committed
124 125
                return INVOKERESULT_NO_ERROR;
            case ID_root_VersionInfo:
126 127
                result = libvlc_get_version();
                return INVOKERESULT_NO_ERROR;
128 129 130
            case ID_root_MediaDescription:
            {
                InstantObj<LibvlcMediaDescriptionNPObject>( mediaDescriptionObj );
131
                result = mediaDescriptionObj;
132 133
                return INVOKERESULT_NO_ERROR;
            }
Rafaël Carré's avatar
Rafaël Carré committed
134 135 136 137 138 139 140 141 142 143
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcRootNPObject::methodNames[] =
{
    "versionInfo",
144
    "getVersionInfo",
145 146
    "addEventListener",
    "removeEventListener",
Rafaël Carré's avatar
Rafaël Carré committed
147
};
148
COUNTNAMES(LibvlcRootNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
149 150 151 152

enum LibvlcRootNPObjectMethodIds
{
    ID_root_versionInfo,
153
    ID_root_getVersionInfo,
154 155
    ID_root_addeventlistener,
    ID_root_removeeventlistener,
Rafaël Carré's avatar
Rafaël Carré committed
156 157
};

158
RuntimeNPObject::InvokeResult LibvlcRootNPObject::invoke(int index,
159
                  const NPVariant *args, uint32_t argCount, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
160 161
{
    /* is plugin still running */
162 163 164 165
    if( !isPluginRunning() )
        return INVOKERESULT_GENERIC_ERROR;

    switch( index )
Rafaël Carré's avatar
Rafaël Carré committed
166
    {
167
    case ID_root_versionInfo:
168
    case ID_root_getVersionInfo:
169 170
        if( 0 != argCount )
            return INVOKERESULT_NO_SUCH_METHOD;
171 172
        result = libvlc_get_version();
        return INVOKERESULT_NO_ERROR;
173 174 175

    case ID_root_addeventlistener:
    case ID_root_removeeventlistener:
176 177 178 179
        if ( argCount < 2 )
            return INVOKERESULT_INVALID_ARGS;

        // Don't wrap eventName as it would copy the string even though it's not required.
180
        const npapi::Variant listener( args[1] );
181 182 183

        if ( !npapi::is_string( args[0] ) ||
            !listener.is<NPObject>() )
184
            return INVOKERESULT_INVALID_ARGS;
Rafaël Carré's avatar
Rafaël Carré committed
185

186
        if( !VlcPluginBase::canUseEventListener() )
Rafaël Carré's avatar
Rafaël Carré committed
187
        {
188 189
            NPN_SetException(this, ERROR_API_VERSION);
            return INVOKERESULT_GENERIC_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
190
        }
191

192
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
193

Jean-Paul Saman's avatar
Jean-Paul Saman committed
194 195
        if( ID_root_addeventlistener == index )
        {
196
            p_plugin->subscribe( npapi::to_tmp_string( args[0] ), listener );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
197
        }
198
        else
Jean-Paul Saman's avatar
Jean-Paul Saman committed
199
        {
200
            p_plugin->unsubscribe( npapi::to_tmp_string( args[0] ), listener );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
201
        }
202
        return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
203
    }
204
    return INVOKERESULT_NO_SUCH_METHOD;
Rafaël Carré's avatar
Rafaël Carré committed
205 206 207 208 209 210
}

/*
** implementation of libvlc audio object
*/

211
const NPUTF8 * const LibvlcAudioNPObject::propertyNames[] =
Rafaël Carré's avatar
Rafaël Carré committed
212 213 214 215
{
    "mute",
    "volume",
    "track",
216
    "count",
Rafaël Carré's avatar
Rafaël Carré committed
217 218
    "channel",
};
219
COUNTNAMES(LibvlcAudioNPObject,propertyCount,propertyNames);
Rafaël Carré's avatar
Rafaël Carré committed
220 221 222 223 224 225

enum LibvlcAudioNPObjectPropertyIds
{
    ID_audio_mute,
    ID_audio_volume,
    ID_audio_track,
226
    ID_audio_count,
Rafaël Carré's avatar
Rafaël Carré committed
227 228 229
    ID_audio_channel,
};

230
RuntimeNPObject::InvokeResult
231
LibvlcAudioNPObject::getProperty(int index, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
232 233
{
    /* is plugin still running */
234
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
235
    {
236
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
237

238 239
        auto& mp = p_plugin->getMD();
        if( !mp )
240
            RETURN_ON_ERROR;
Rémi Duraffort's avatar
Rémi Duraffort committed
241

Rafaël Carré's avatar
Rafaël Carré committed
242 243 244 245
        switch( index )
        {
            case ID_audio_mute:
            {
246
                result = mp.mute();
Rafaël Carré's avatar
Rafaël Carré committed
247 248 249 250
                return INVOKERESULT_NO_ERROR;
            }
            case ID_audio_volume:
            {
251
                result = mp.volume();
Rafaël Carré's avatar
Rafaël Carré committed
252 253 254 255
                return INVOKERESULT_NO_ERROR;
            }
            case ID_audio_track:
            {
256
                result = p_plugin->player().currentAudioTrack();
Rafaël Carré's avatar
Rafaël Carré committed
257 258
                return INVOKERESULT_NO_ERROR;
            }
259 260
            case ID_audio_count:
            {
261
                result = mp.audioTrackCount();
262 263
                return INVOKERESULT_NO_ERROR;
            }
Rafaël Carré's avatar
Rafaël Carré committed
264 265
            case ID_audio_channel:
            {
266
                result = mp.channel();
Rafaël Carré's avatar
Rafaël Carré committed
267 268 269 270 271 272 273 274 275
                return INVOKERESULT_NO_ERROR;
            }
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

276 277
RuntimeNPObject::InvokeResult
LibvlcAudioNPObject::setProperty(int index, const NPVariant &value)
Rafaël Carré's avatar
Rafaël Carré committed
278 279
{
    /* is plugin still running */
280
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
281
    {
282
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
283

284 285
        auto& mp = p_plugin->getMD();
        if( !mp )
286
            RETURN_ON_ERROR;
Rémi Duraffort's avatar
Rémi Duraffort committed
287

288
        const npapi::Variant v( value );
Rafaël Carré's avatar
Rafaël Carré committed
289 290 291
        switch( index )
        {
            case ID_audio_mute:
292
                if( v.is<bool>() )
Rafaël Carré's avatar
Rafaël Carré committed
293
                {
294
                    mp.setMute( v );
Rafaël Carré's avatar
Rafaël Carré committed
295 296 297 298
                    return INVOKERESULT_NO_ERROR;
                }
                return INVOKERESULT_INVALID_VALUE;
            case ID_audio_volume:
299
                if( v.is<int>() )
Rafaël Carré's avatar
Rafaël Carré committed
300
                {
301
                    mp.setVolume( v );
Rafaël Carré's avatar
Rafaël Carré committed
302 303 304 305
                    return INVOKERESULT_NO_ERROR;
                }
                return INVOKERESULT_INVALID_VALUE;
            case ID_audio_track:
306
                if( v.is<int>() )
Rafaël Carré's avatar
Rafaël Carré committed
307
                {
308 309 310
                    auto tracks = mp.audioTrackDescription();
                    if ( v >= tracks.size() )
                        return INVOKERESULT_INVALID_VALUE;
311 312
                    mp.setAudioTrack( tracks[v].id() );
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
313 314 315
                }
                return INVOKERESULT_INVALID_VALUE;
            case ID_audio_channel:
316
                if( v.is<int>() )
Rafaël Carré's avatar
Rafaël Carré committed
317
                {
318
                    if ( mp.setChannel( v ) )
319
                        return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
320 321 322 323 324 325 326 327 328 329 330 331
                }
                return INVOKERESULT_INVALID_VALUE;
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcAudioNPObject::methodNames[] =
{
    "toggleMute",
332
    "description",
Rafaël Carré's avatar
Rafaël Carré committed
333
};
334
COUNTNAMES(LibvlcAudioNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
335 336 337 338

enum LibvlcAudioNPObjectMethodIds
{
    ID_audio_togglemute,
339
    ID_audio_description,
Rafaël Carré's avatar
Rafaël Carré committed
340 341
};

342 343
RuntimeNPObject::InvokeResult
LibvlcAudioNPObject::invoke(int index, const NPVariant *args,
344
                            uint32_t argCount, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
345 346
{
    /* is plugin still running */
347
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
348
    {
349
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
350 351
        auto& mp = p_plugin->getMD();
        if( !mp )
352
            RETURN_ON_ERROR;
Rémi Duraffort's avatar
Rémi Duraffort committed
353

Rafaël Carré's avatar
Rafaël Carré committed
354 355 356 357 358
        switch( index )
        {
            case ID_audio_togglemute:
                if( argCount == 0 )
                {
359
                    mp.toggleMute();
360
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
361 362
                }
                return INVOKERESULT_NO_SUCH_METHOD;
363 364
            case ID_audio_description:
            {
365 366
                if ( argCount < 1 )
                    return INVOKERESULT_INVALID_ARGS;
367
                const npapi::Variant v( args[0] );
368
                if( v.is<int>() )
369
                {
370
                    auto tracks = mp.audioTrackDescription();
371
                    if ( v >= tracks.size() )
372 373
                        return INVOKERESULT_INVALID_VALUE;
                    /* display the name of the track chosen */
374 375
                    result = tracks[v].name();
                    return INVOKERESULT_NO_ERROR;
376 377 378
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            }
Rafaël Carré's avatar
Rafaël Carré committed
379 380 381 382 383 384 385 386 387 388 389
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

/*
** implementation of libvlc input object
*/

390 391 392 393 394 395 396 397 398
LibvlcInputNPObject::~LibvlcInputNPObject()
{
    if( isValid() )
    {
        if( titleObj   ) NPN_ReleaseObject(titleObj);
        if( chapterObj ) NPN_ReleaseObject(chapterObj);
    }
}

399
const NPUTF8 * const LibvlcInputNPObject::propertyNames[] =
Rafaël Carré's avatar
Rafaël Carré committed
400 401 402 403 404 405 406 407
{
    "length",
    "position",
    "time",
    "state",
    "rate",
    "fps",
    "hasVout",
408 409
    "title",
    "chapter",
Rafaël Carré's avatar
Rafaël Carré committed
410
};
411
COUNTNAMES(LibvlcInputNPObject,propertyCount,propertyNames);
Rafaël Carré's avatar
Rafaël Carré committed
412 413 414 415 416 417 418 419 420 421

enum LibvlcInputNPObjectPropertyIds
{
    ID_input_length,
    ID_input_position,
    ID_input_time,
    ID_input_state,
    ID_input_rate,
    ID_input_fps,
    ID_input_hasvout,
422 423
    ID_input_title,
    ID_input_chapter,
Rafaël Carré's avatar
Rafaël Carré committed
424 425
};

426
RuntimeNPObject::InvokeResult
427
LibvlcInputNPObject::getProperty(int index, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
428 429
{
    /* is plugin still running */
430
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
431
    {
432
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
433 434
        auto& mp = p_plugin->getMD();
        if( !mp )
Rafaël Carré's avatar
Rafaël Carré committed
435 436
        {
            if( index != ID_input_state )
437
                RETURN_ON_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
438 439 440
            else
            {
                /* for input state, return CLOSED rather than an exception */
441
                result = 0;
Rafaël Carré's avatar
Rafaël Carré committed
442 443 444 445 446 447 448 449
                return INVOKERESULT_NO_ERROR;
            }
        }

        switch( index )
        {
            case ID_input_length:
            {
450
                result = mp.length();
Rafaël Carré's avatar
Rafaël Carré committed
451 452 453 454
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_position:
            {
455
                result = mp.position();
Rafaël Carré's avatar
Rafaël Carré committed
456 457 458 459
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_time:
            {
460
                result = mp.time();
Rafaël Carré's avatar
Rafaël Carré committed
461 462 463 464
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_state:
            {
465
                result = (int)mp.state();
Rafaël Carré's avatar
Rafaël Carré committed
466 467 468 469
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_rate:
            {
470
                result = mp.rate();
Rafaël Carré's avatar
Rafaël Carré committed
471 472 473 474
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_fps:
            {
475 476 477 478 479 480 481 482 483
                auto media = mp.media();
                if ( media == nullptr )
                    return INVOKERESULT_GENERIC_ERROR;
                auto tracks = media->tracks();
                for ( const auto& t : tracks )
                {
                    if ( t.type() == VLC::MediaTrack::Type::Video )
                    {
                        result = (float)( (float)t.fpsNum() / (float)t.fpsDen() );
Daniel Amm's avatar
Daniel Amm committed
484
                        return INVOKERESULT_NO_ERROR;
485 486 487
                    }
                }
                result = 0.0f;
Rafaël Carré's avatar
Rafaël Carré committed
488 489 490 491
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_hasvout:
            {
492
                result = p_plugin->player().get_mp().hasVout() != 0;
Rafaël Carré's avatar
Rafaël Carré committed
493 494
                return INVOKERESULT_NO_ERROR;
            }
495 496 497 498 499 500 501 502 503 504 505 506
            case ID_input_title:
            {
                InstantObj<LibvlcTitleNPObject>( titleObj );
                result = titleObj;
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_chapter:
            {
                InstantObj<LibvlcChapterNPObject>( chapterObj );
                result = chapterObj;
                return INVOKERESULT_NO_ERROR;
            }
Rafaël Carré's avatar
Rafaël Carré committed
507 508 509 510 511 512 513
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

514 515
RuntimeNPObject::InvokeResult
LibvlcInputNPObject::setProperty(int index, const NPVariant &value)
Rafaël Carré's avatar
Rafaël Carré committed
516 517
{
    /* is plugin still running */
518
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
519
    {
520
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
521 522
        auto& mp = p_plugin->getMD();
        if( !mp )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
523
            RETURN_ON_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
524

525
        const npapi::Variant v( value );
Rafaël Carré's avatar
Rafaël Carré committed
526 527 528 529
        switch( index )
        {
            case ID_input_position:
            {
530
                if( !v.is<double>() )
Rafaël Carré's avatar
Rafaël Carré committed
531 532 533 534
                {
                    return INVOKERESULT_INVALID_VALUE;
                }

535
                mp.setPosition( v );
Rafaël Carré's avatar
Rafaël Carré committed
536 537 538 539
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_time:
            {
540
                if( !v.is<int>() )
Rafaël Carré's avatar
Rafaël Carré committed
541 542 543 544
                {
                    return INVOKERESULT_INVALID_VALUE;
                }

545
                mp.setTime( v );
Rafaël Carré's avatar
Rafaël Carré committed
546 547 548 549
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_rate:
            {
550
                if( !v.is<double>() )
Rafaël Carré's avatar
Rafaël Carré committed
551 552 553 554
                {
                    return INVOKERESULT_INVALID_VALUE;
                }

555
                mp.setRate( v );
Rafaël Carré's avatar
Rafaël Carré committed
556 557 558 559 560 561 562 563 564 565 566 567
                return INVOKERESULT_NO_ERROR;
            }
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcInputNPObject::methodNames[] =
{
    /* no methods */
568
    "none",
Rafaël Carré's avatar
Rafaël Carré committed
569
};
570
COUNTNAMES(LibvlcInputNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
571

572 573 574 575 576 577
enum LibvlcInputNPObjectMethodIds
{
    ID_none,
};

RuntimeNPObject::InvokeResult
578 579
LibvlcInputNPObject::invoke(int index, const NPVariant *,
                            uint32_t, NPVariant &)
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
        switch( index )
        {
            case ID_none:
                return INVOKERESULT_NO_SUCH_METHOD;
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
/*
** implementation of libvlc MediaDescription object
*/

const NPUTF8 * const LibvlcMediaDescriptionNPObject::propertyNames[] =
{
    "title",
    "artist",
    "genre",
    "copyright",
    "album",
    "trackNumber",
    "description",
    "rating",
    "date",
    "setting",
    "URL",
    "language",
    "nowPlaying",
    "publisher",
    "encodedBy",
    "artworkURL",
    "trackID",
};
COUNTNAMES(LibvlcMediaDescriptionNPObject,propertyCount,propertyNames);

enum LibvlcMediaDescriptionNPObjectPropertyIds
{
    ID_meta_title,
    ID_meta_artist,
    ID_meta_genre,
    ID_meta_copyright,
    ID_meta_album,
    ID_meta_trackNumber,
    ID_meta_description,
    ID_meta_rating,
    ID_meta_date,
    ID_meta_setting,
    ID_meta_URL,
    ID_meta_language,
    ID_meta_nowPlaying,
    ID_meta_publisher,
    ID_meta_encodedBy,
    ID_meta_artworkURL,
    ID_meta_trackID,
};
RuntimeNPObject::InvokeResult
642
LibvlcMediaDescriptionNPObject::getProperty(int index, npapi::OutVariant &result)
643 644 645 646 647
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
        VlcPlugin* p_plugin = getPrivate<VlcPlugin>();
648 649
        auto& mp = p_plugin->getMD();
        if( !mp )
650
            RETURN_ON_ERROR;
651 652
        auto media = mp.media();
        if( !media )
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
            RETURN_ON_ERROR;
        switch( index )
        {
            case ID_meta_title:
            case ID_meta_artist:
            case ID_meta_genre:
            case ID_meta_copyright:
            case ID_meta_album:
            case ID_meta_trackNumber:
            case ID_meta_description:
            case ID_meta_rating:
            case ID_meta_date:
            case ID_meta_setting:
            case ID_meta_URL:
            case ID_meta_language:
            case ID_meta_nowPlaying:
            case ID_meta_publisher:
            case ID_meta_encodedBy:
            case ID_meta_artworkURL:
            case ID_meta_trackID:
673 674
            {
                auto m = media->meta( (libvlc_meta_t)index );
675 676
                result = m;
                return INVOKERESULT_NO_ERROR;
677
            }
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
            default:
            ;
        }
     }
     return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcMediaDescriptionNPObject::methodNames[] =
{
    "None"
};
COUNTNAMES(LibvlcMediaDescriptionNPObject,methodCount,methodNames);

enum LibvlcMediaDescriptionNPObjectMethodIds
{
    ID_mediadescription_method_none,
};


Rafaël Carré's avatar
Rafaël Carré committed
697 698 699 700
/*
** implementation of libvlc playlist items object
*/

701
const NPUTF8 * const LibvlcPlaylistItemsNPObject::propertyNames[] =
Rafaël Carré's avatar
Rafaël Carré committed
702 703 704
{
    "count",
};
705
COUNTNAMES(LibvlcPlaylistItemsNPObject,propertyCount,propertyNames);
Rafaël Carré's avatar
Rafaël Carré committed
706 707 708 709 710 711

enum LibvlcPlaylistItemsNPObjectPropertyIds
{
    ID_playlistitems_count,
};

712
RuntimeNPObject::InvokeResult
713
LibvlcPlaylistItemsNPObject::getProperty(int index, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
714 715
{
    /* is plugin still running */
716
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
717
    {
718
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
719 720 721 722 723

        switch( index )
        {
            case ID_playlistitems_count:
            {
724
                result = p_plugin->player().items_count();
Rafaël Carré's avatar
Rafaël Carré committed
725 726 727 728 729 730 731 732 733 734 735 736 737 738
                return INVOKERESULT_NO_ERROR;
            }
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcPlaylistItemsNPObject::methodNames[] =
{
    "clear",
    "remove",
};
739
COUNTNAMES(LibvlcPlaylistItemsNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
740 741 742 743 744 745 746

enum LibvlcPlaylistItemsNPObjectMethodIds
{
    ID_playlistitems_clear,
    ID_playlistitems_remove,
};

747 748
RuntimeNPObject::InvokeResult
LibvlcPlaylistItemsNPObject::invoke(int index, const NPVariant *args,
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
749
                                    uint32_t argCount, npapi::OutVariant& )
Rafaël Carré's avatar
Rafaël Carré committed
750 751
{
    /* is plugin still running */
752
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
753
    {
754
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
755 756 757 758 759 760

        switch( index )
        {
            case ID_playlistitems_clear:
                if( argCount == 0 )
                {
761
                    p_plugin->player().clear_items();
762
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
763 764 765
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlistitems_remove:
766 767
            {
                if ( argCount < 1 )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
768
                    return INVOKERESULT_INVALID_ARGS;
769
                const npapi::Variant v( args[0] );
770
                if( v.is<int>() )
Rafaël Carré's avatar
Rafaël Carré committed
771
                {
772
                    if( !p_plugin->player().delete_item( v ) )
773
                        return INVOKERESULT_GENERIC_ERROR;
774
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
775 776
                }
                return INVOKERESULT_NO_SUCH_METHOD;
777
            }
Rafaël Carré's avatar
Rafaël Carré committed
778 779 780 781 782 783 784 785 786 787 788 789 790
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

/*
** implementation of libvlc playlist object
*/

LibvlcPlaylistNPObject::~LibvlcPlaylistNPObject()
{
791 792 793
    // Why the isValid()?
    if( isValid() && playlistItemsObj )
        NPN_ReleaseObject(playlistItemsObj);
Rafaël Carré's avatar
Rafaël Carré committed
794 795
};

796
const NPUTF8 * const LibvlcPlaylistNPObject::propertyNames[] =
Rafaël Carré's avatar
Rafaël Carré committed
797 798 799
{
    "itemCount", /* deprecated */
    "isPlaying",
800
    "currentItem",
Rafaël Carré's avatar
Rafaël Carré committed
801 802
    "items",
};
803
COUNTNAMES(LibvlcPlaylistNPObject,propertyCount,propertyNames);
Rafaël Carré's avatar
Rafaël Carré committed
804 805 806 807 808

enum LibvlcPlaylistNPObjectPropertyIds
{
    ID_playlist_itemcount,
    ID_playlist_isplaying,
809
    ID_playlist_currentitem,
Rafaël Carré's avatar
Rafaël Carré committed
810 811 812
    ID_playlist_items,
};

813
RuntimeNPObject::InvokeResult
814
LibvlcPlaylistNPObject::getProperty(int index, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
815 816
{
    /* is plugin still running */
817
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
818
    {
819
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
820 821 822 823 824

        switch( index )
        {
            case ID_playlist_itemcount: /* deprecated */
            {
825
                result = p_plugin->player().items_count();
Rafaël Carré's avatar
Rafaël Carré committed
826 827 828 829
                return INVOKERESULT_NO_ERROR;
            }
            case ID_playlist_isplaying:
            {
830
                result = p_plugin->player().mlp().isPlaying();
Rafaël Carré's avatar
Rafaël Carré committed
831 832
                return INVOKERESULT_NO_ERROR;
            }
833 834
            case ID_playlist_currentitem:
            {
835
                result = p_plugin->player().current_item();
836 837
                return INVOKERESULT_NO_ERROR;
            }
Rafaël Carré's avatar
Rafaël Carré committed
838 839
            case ID_playlist_items:
            {
840
                InstantObj<LibvlcPlaylistItemsNPObject>( playlistItemsObj );
841
                result = playlistItemsObj;
Rafaël Carré's avatar
Rafaël Carré committed
842 843 844 845 846 847 848 849 850 851 852 853 854 855
                return INVOKERESULT_NO_ERROR;
            }
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcPlaylistNPObject::methodNames[] =
{
    "add",
    "play",
    "playItem",
856
    "pause",
Rafaël Carré's avatar
Rafaël Carré committed
857 858 859 860 861 862 863
    "togglePause",
    "stop",
    "next",
    "prev",
    "clear", /* deprecated */
    "removeItem", /* deprecated */
};
864
COUNTNAMES(LibvlcPlaylistNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
865 866 867 868 869 870

enum LibvlcPlaylistNPObjectMethodIds
{
    ID_playlist_add,
    ID_playlist_play,
    ID_playlist_playItem,
871
    ID_playlist_pause,
Rafaël Carré's avatar
Rafaël Carré committed
872 873 874 875 876 877 878 879
    ID_playlist_togglepause,
    ID_playlist_stop,
    ID_playlist_next,
    ID_playlist_prev,
    ID_playlist_clear,
    ID_playlist_removeitem
};

880 881
RuntimeNPObject::InvokeResult
LibvlcPlaylistNPObject::invoke(int index, const NPVariant *args,
882
                               uint32_t argCount, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
883 884
{
    /* is plugin still running */
885
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
886
    {
887
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
888 889 890

        switch( index )
        {
891
            // XXX FIXME this needs squashing into something much smaller
Rafaël Carré's avatar
Rafaël Carré committed
892 893 894 895
            case ID_playlist_add:
            {
                if( (argCount < 1) || (argCount > 3) )
                    return INVOKERESULT_NO_SUCH_METHOD;
896
                if( !npapi::is_string( args[0] ) )
JP Dinger's avatar
JP Dinger committed
897
                    return INVOKERESULT_NO_SUCH_METHOD;
Rafaël Carré's avatar
Rafaël Carré committed
898 899

                // grab URL
900 901 902
                if( NPVARIANT_IS_NULL(args[0]) )
                    return INVOKERESULT_NO_SUCH_METHOD;

903 904 905 906
                // Don't assign url pointer to s, they don't have the same
                // deleter function.
                auto s = npapi::to_string( args[0] );
                auto url = CStr( p_plugin->getAbsoluteURL( s.get() ), free );
Rafaël Carré's avatar
Rafaël Carré committed
907

908
                //FIXME: This is never used
909
                auto name = npapi::NPStringPtr( nullptr, NPN_MemFree );
Rafaël Carré's avatar
Rafaël Carré committed
910 911 912 913 914 915 916 917 918
                // grab name if available
                if( argCount > 1 )
                {
                    if( NPVARIANT_IS_NULL(args[1]) )
                    {
                        // do nothing
                    }
                    else if( NPVARIANT_IS_STRING(args[1]) )
                    {
919
                        name = npapi::to_string( args[1] );
Rafaël Carré's avatar
Rafaël Carré committed
920 921 922 923 924 925 926 927 928 929 930 931 932
                    }
                    else
                    {
                        return INVOKERESULT_INVALID_VALUE;
                    }
                }

                int i_options = 0;
                char** ppsz_options = NULL;

                // grab options if available
                if( argCount > 2 )
                {
933
                    const npapi::Variant v( args[2] );
934
                    if( v.is<std::nullptr_t>() )
Rafaël Carré's avatar
Rafaël Carré committed
935 936 937
                    {
                        // do nothing
                    }
938
                    else if( v.is<NPString>() )
Rafaël Carré's avatar
Rafaël Carré committed
939
                    {
940
                        parseOptions((NPString)v, &i_options, &ppsz_options);
Rafaël Carré's avatar
Rafaël Carré committed
941 942

                    }
943
                    else if( v.is<NPObject>() )
Rafaël Carré's avatar
Rafaël Carré committed
944
                    {
945
                        parseOptions((NPObject*)v, &i_options, &ppsz_options);
Rafaël Carré's avatar
Rafaël Carré committed
946 947 948 949 950 951 952
                    }
                    else
                    {
                        return INVOKERESULT_INVALID_VALUE;
                    }
                }

953
                int item = p_plugin->player().add_item( url ? url.get() : s.get(), i_options, const_cast<const char **>(ppsz_options));
954 955 956
                if( item == -1 )
                    RETURN_ON_ERROR;

Rafaël Carré's avatar
Rafaël Carré committed
957 958
                for( int i=0; i< i_options; ++i )
                {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
959
                    free(ppsz_options[i]);
Rafaël Carré's avatar
Rafaël Carré committed
960
                }
961
                free(ppsz_options);
Rafaël Carré's avatar
Rafaël Carré committed
962

963
                result = item;
964
                return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
965 966 967 968
            }
            case ID_playlist_play:
                if( argCount == 0 )
                {
969
                    p_plugin->player().play();
970
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
971 972 973
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_playItem:
974 975 976
            {
                if ( argCount < 1 )
                    return INVOKERESULT_INVALID_ARGS;
977
                const npapi::Variant v( args[0] );
978
                if ( v.is<int>() )
Rafaël Carré's avatar
Rafaël Carré committed
979
                {
980
                    p_plugin->player().mlp().playItemAtIndex( v );
981
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
982 983
                }
                return INVOKERESULT_NO_SUCH_METHOD;
984
            }
985
            case ID_playlist_pause:
Rafaël Carré's avatar
Rafaël Carré committed
986 987
                if( argCount == 0 )
                {
988
                    p_plugin->player().get_mp().setPause( true );
989
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
990 991
                }
                return INVOKERESULT_NO_SUCH_METHOD;
992 993 994
            case ID_playlist_togglepause:
                if( argCount == 0 )
                {
995
                    p_plugin->player().mlp().pause();
996 997 998
                    return INVOKERESULT_NO_ERROR;
                }
                return INVOKERESULT_NO_SUCH_METHOD;
Rafaël Carré's avatar
Rafaël Carré committed
999 1000 1001
            case ID_playlist_stop:
                if( argCount == 0 )
                {
1002
                    p_plugin->player().mlp().stop();
1003
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1004 1005 1006 1007 1008
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_next:
                if( argCount == 0 )
                {
1009
                    p_plugin->player().mlp().next();
1010
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1011 1012 1013 1014 1015
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_prev:
                if( argCount == 0 )
                {
1016
                    p_plugin->player().mlp().previous();
1017
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1018 1019 1020 1021 1022
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_clear: /* deprecated */
                if( argCount == 0 )
                {
1023
                    p_plugin->player().clear_items();
1024
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1025 1026 1027
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_removeitem: /* deprecated */
1028 1029 1030
            {
                if ( argCount < 1 )
                    return INVOKERESULT_INVALID_ARGS;
1031
                const npapi::Variant v( args[0] );
1032
                if ( v.is<int>() )
Rafaël Carré's avatar
Rafaël Carré committed
1033
                {
1034
                    if( !p_plugin->player().delete_item( v ) )
1035
                        return INVOKERESULT_GENERIC_ERROR;
1036
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1037 1038
                }
                return INVOKERESULT_NO_SUCH_METHOD;
1039
            }
Rafaël Carré's avatar
Rafaël Carré committed
1040 1041 1042 1043 1044 1045 1046
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

1047 1048 1049 1050 1051 1052
// XXX FIXME The new playlist_add creates a media instance and feeds it
// XXX FIXME these options one at a time, so this hunk of code does lots
// XXX FIXME of unnecessairy work. Break out something that can do one
// XXX FIXME option at a time and doesn't need to realloc().
// XXX FIXME Same for the other version of parseOptions.

1053
void LibvlcPlaylistNPObject::parseOptions(const NPString& nps,
1054
                                         int *i_options, char*** ppsz_options)
Rafaël Carré's avatar
Rafaël Carré committed
1055
{
1056
    if( nps.UTF8Length )
Rafaël Carré's avatar
Rafaël Carré committed
1057
    {
1058 1059
        auto s = CStr( strdup( nps.UTF8Characters ), free );
        char *val = s.get();
Rafaël Carré's avatar
Rafaël Carré committed
1060 1061 1062 1063 1064 1065 1066 1067
        if( val )
        {
            long capacity = 16;
            char **options = (char **)malloc(capacity*sizeof(char *));
            if( options )
            {
                int nOptions = 0;

1068
                char *end = val + nps.UTF8Length;
Rafaël Carré's avatar
Rafaël Carré committed
1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
                while( val < end )
                {
                    // skip leading blanks
                    while( (val < end)
                        && ((*val == ' ' ) || (*val == '\t')) )
                        ++val;

                    char *start = val;
                    // skip till we get a blank character
                    while( (val < end)
                        && (*val != ' ' )
                        && (*val != '\t') )
                    {
                        char c = *(val++);
                        if( ('\'' == c) || ('"' == c) )
                        {
                            // skip till end of string
                            while( (val < end) && (*(val++) != c ) );
                        }
                    }

                    if( val > start )
                    {
                        if( nOptions == capacity )
                        {
                            capacity += 16;
1095
                            char **moreOptions = (char **)realloc(options, capacity*sizeof(char*));
Rafaël Carré's avatar
Rafaël Carré committed
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
                            if( ! moreOptions )
                            {
                                /* failed to allocate more memory */
                                /* return what we got so far */
                                *i_options = nOptions;
                                *ppsz_options = options;
                                return;
                            }
                            options = moreOptions;
                        }
                        *(val++) = '\0';
                        options[nOptions++] = strdup(start);
                    }
                    else
                        // must be end of string
                        break;
                }
                *i_options = nOptions;
                *ppsz_options = options;
            }
        }
    }
}

1120
// XXX FIXME See comment at the other parseOptions variant.
1121 1122
void LibvlcPlaylistNPObject::parseOptions(NPObject *obj, int *i_options,
                                          char*** ppsz_options)
Rafaël Carré's avatar
Rafaël Carré committed
1123 1124 1125
{
    /* WARNING: Safari does not implement NPN_HasProperty/NPN_HasMethod */

1126
    npapi::Variant value;
Rafaël Carré's avatar
Rafaël Carré committed
1127 1128 1129

    /* we are expecting to have a Javascript Array object */
    NPIdentifier propId = NPN_GetStringIdentifier("length");
1130
    if( NPN_GetProperty(_instance, obj, propId, value) )
Rafaël Carré's avatar
Rafaël Carré committed
1131
    {
1132
        int count = value;
Rafaël Carré's avatar
Rafaël Carré committed
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143

        if( count )
        {
            long capacity = 16;
            char **options = (char **)malloc(capacity*sizeof(char *));
            if( options )
            {
                int nOptions = 0;

                while( nOptions < count )
                {
1144
                    npapi::Variant value;
Rafaël Carré's avatar
Rafaël Carré committed
1145
                    propId = NPN_GetIntIdentifier(nOptions);
1146
                    if( ! NPN_GetProperty(_instance, obj, propId, value) )
Rafaël Carré's avatar
Rafaël Carré committed
1147 1148 1149
                        /* return what we got so far */
                        break;

1150
                    if( !value.is<NPString>() )
Rafaël Carré's avatar
Rafaël Carré committed
1151 1152 1153 1154 1155 1156 1157 1158
                    {
                        /* return what we got so far */
                        break;
                    }

                    if( nOptions == capacity )
                    {
                        capacity += 16;
1159
                        char **moreOptions = (char **)realloc(options, capacity*sizeof(char*));
Rafaël Carré's avatar
Rafaël Carré committed
1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
                        if( ! moreOptions )
                        {
                            /* failed to allocate more memory */
                            /* return what we got so far */
                            *i_options = nOptions;
                            *ppsz_options = options;
                            break;
                        }
                        options = moreOptions;
                    }

1171
                    options[nOptions++] = strdup(value);
Rafaël Carré's avatar
Rafaël Carré committed
1172 1173 1174 1175 1176 1177 1178 1179
                }
                *i_options = nOptions;
                *ppsz_options = options;
            }
        }
    }
}

1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
/*
** implementation of libvlc subtitle object
*/

const NPUTF8 * const LibvlcSubtitleNPObject::propertyNames[] =
{
    "track",
    "count",
};

enum LibvlcSubtitleNPObjectPropertyIds
{
    ID_subtitle_track,
    ID_subtitle_count,
};
COUNTNAMES(LibvlcSubtitleNPObject,propertyCount,propertyNames);

RuntimeNPObject::InvokeResult
1198
LibvlcSubtitleNPObject::getProperty(int index, npapi::OutVariant& result)
1199 1200 1201 1202
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
1203
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
1204 1205
        auto& mp = p_plugin->getMD();
        if( !mp )
1206
            RETURN_ON_ERROR;
1207 1208 1209 1210 1211

        switch( index )
        {
            case ID_subtitle_track:
            {
1212
                result = p_plugin->player().currentSubtitleTrack();
1213 1214 1215 1216
                return INVOKERESULT_NO_ERROR;
            }
            case ID_subtitle_count:
            {
1217
                result = mp.spuCount();
1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230
                return INVOKERESULT_NO_ERROR;
            }
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

RuntimeNPObject::InvokeResult
LibvlcSubtitleNPObject::setProperty(int index, const NPVariant &value)
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
1231
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
1232 1233
        auto& mp = p_plugin->getMD();
        if( !mp )
1234
            RETURN_ON_ERROR;
1235 1236 1237 1238 1239

        switch( index )
        {
            case ID_subtitle_track:
            {
1240
                const npapi::Variant v( value );
1241
                if( v.is<int>() )
1242
                {
1243 1244 1245
                    auto tracks = mp.spuDescription();
                    if ( v >= tracks.size() )
                        return INVOKERESULT_INVALID_ARGS;
1246 1247
                    mp.setSpu( tracks[ v ].id() );
                    return INVOKERESULT_NO_ERROR;
1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268
                }
                return INVOKERESULT_INVALID_VALUE;
            }
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcSubtitleNPObject::methodNames[] =
{
    "description"
};
COUNTNAMES(LibvlcSubtitleNPObject,methodCount,methodNames);

enum LibvlcSubtitleNPObjectMethodIds
{
    ID_subtitle_description
};

RuntimeNPObject::InvokeResult
LibvlcSubtitleNPObject::invoke(int index, const NPVariant *args,
1269
                            uint32_t argCount, npapi::OutVariant& result)
1270 1271 1272 1273
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
1274
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
1275 1276
        auto& mp = p_plugin->getMD();
        if( !mp )
1277
            RETURN_ON_ERROR;
1278 1279 1280 1281 1282

        switch( index )
        {
            case ID_subtitle_description:
            {
1283 1284
                if ( argCount < 1 )
                    return INVOKERESULT_INVALID_ARGS;
1285
                const npapi::Variant v( args[0] );
1286
                if ( v.is<int>() )
1287
                {
1288
                    auto tracks = mp.spuDescription();
1289
                    if ( v >= tracks.size() )
1290
                        return INVOKERESULT_INVALID_VALUE;
1291
                    /* display the name of the track chosen */
1292 1293
                    result = tracks[v].name();
                    return INVOKERESULT_NO_ERROR;
1294 1295 1296 1297 1298 1299 1300 1301 1302 1303
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            }
            default:
                return INVOKERESULT_NO_SUCH_METHOD;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

Rafaël Carré's avatar
Rafaël Carré committed
1304 1305 1306 1307
/*
** implementation of libvlc video object
*/

1308 1309 1310 1311 1312 1313 1314 1315 1316 1317
LibvlcVideoNPObject::~LibvlcVideoNPObject()
{
    if( isValid() )
    {
        if( marqueeObj ) NPN_ReleaseObject(marqueeObj);
        if( logoObj    ) NPN_ReleaseObject(logoObj);
        if( deintObj   ) NPN_ReleaseObject(deintObj);
    }
}

1318
const NPUTF8 * const LibvlcVideoNPObject::propertyNames[] =
Rafaël Carré's avatar
Rafaël Carré committed
1319 1320 1321 1322 1323 1324 1325
{
    "fullscreen",
    "height",
    "width",
    "aspectRatio",
    "subtitle",
    "crop",
1326
    "teletext",
1327
    "marquee",
1328 1329
    "logo",
    "deinterlace",
Rafaël Carré's avatar
Rafaël Carré committed
1330 1331 1332 1333 1334 1335 1336 1337 1338 1339
};

enum LibvlcVideoNPObjectPropertyIds
{
    ID_video_fullscreen,
    ID_video_height,
    ID_video_width,
    ID_video_aspectratio,
    ID_video_subtitle,
    ID_video_crop,
1340
    ID_video_teletext,
1341
    ID_video_marquee,
1342 1343
    ID_video_logo,
    ID_video_deinterlace,
Rafaël Carré's avatar
Rafaël Carré committed
1344
};
1345
COUNTNAMES(LibvlcVideoNPObject,propertyCount,propertyNames);
Rafaël Carré's avatar
Rafaël Carré committed
1346

1347
RuntimeNPObject::InvokeResult
1348
LibvlcVideoNPObject::getProperty(int index, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
1349 1350
{
    /* is plugin still running */
1351
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
1352
    {
1353
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
1354 1355
        auto& mp = p_plugin->getMD();
        if( !mp )
1356
            RETURN_ON_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1357 1358 1359 1360 1361

        switch( index )
        {
            case ID_video_fullscreen:
            {
1362
                result = p_plugin->get_fullscreen();
Rafaël Carré's avatar
Rafaël Carré committed
1363 1364 1365 1366
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_height:
            {
1367 1368
                unsigned width, height;
                mp.size( 0, &width, &height );
1369
                result = height;
Rafaël Carré's avatar
Rafaël Carré committed
1370 1371 1372 1373
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_width:
            {
1374 1375
                unsigned width, height;
                mp.size( 0, &width, &height );
1376
                result = width;
Rafaël Carré's avatar
Rafaël Carré committed
1377 1378 1379 1380
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_aspectratio:
            {
1381
                result = mp.aspectRatio();
Rafaël Carré's avatar
Rafaël Carré committed
1382 1383 1384 1385
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_subtitle:
            {
1386
                result = p_plugin->player().currentSubtitleTrack();
Rafaël Carré's avatar
Rafaël Carré committed
1387 1388 1389 1390
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_crop:
            {
1391
                result = mp.cropGeometry();
Rafaël Carré's avatar
Rafaël Carré committed
1392 1393 1394 1395
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_teletext:
            {
1396
                int i_page = mp.teletext();
1397 1398
                if( i_page < 0 )
                    return INVOKERESULT_GENERIC_ERROR;
1399
                result = i_page;
Rafaël Carré's avatar
Rafaël Carré committed