npolibvlc.cpp 52.1 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 145
    "addEventListener",
    "removeEventListener",
Rafaël Carré's avatar
Rafaël Carré committed
146
};
147
COUNTNAMES(LibvlcRootNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
148 149 150 151

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

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

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

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

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

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

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

189
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
190

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

/*
** implementation of libvlc audio object
*/

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

enum LibvlcAudioNPObjectPropertyIds
{
    ID_audio_mute,
    ID_audio_volume,
    ID_audio_track,
223
    ID_audio_count,
Rafaël Carré's avatar
Rafaël Carré committed
224 225 226
    ID_audio_channel,
};

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

235 236
        auto& mp = p_plugin->getMD();
        if( !mp )
237
            RETURN_ON_ERROR;
Rémi Duraffort's avatar
Rémi Duraffort committed
238

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

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

281 282
        auto& mp = p_plugin->getMD();
        if( !mp )
283
            RETURN_ON_ERROR;
Rémi Duraffort's avatar
Rémi Duraffort committed
284

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

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

enum LibvlcAudioNPObjectMethodIds
{
    ID_audio_togglemute,
336
    ID_audio_description,
Rafaël Carré's avatar
Rafaël Carré committed
337 338
};

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

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

/*
** implementation of libvlc input object
*/

387
const NPUTF8 * const LibvlcInputNPObject::propertyNames[] =
Rafaël Carré's avatar
Rafaël Carré committed
388 389 390 391 392 393 394 395 396
{
    "length",
    "position",
    "time",
    "state",
    "rate",
    "fps",
    "hasVout",
};
397
COUNTNAMES(LibvlcInputNPObject,propertyCount,propertyNames);
Rafaël Carré's avatar
Rafaël Carré committed
398 399 400 401 402 403 404 405 406 407 408 409

enum LibvlcInputNPObjectPropertyIds
{
    ID_input_length,
    ID_input_position,
    ID_input_time,
    ID_input_state,
    ID_input_rate,
    ID_input_fps,
    ID_input_hasvout,
};

410
RuntimeNPObject::InvokeResult
411
LibvlcInputNPObject::getProperty(int index, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
412 413
{
    /* is plugin still running */
414
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
415
    {
416
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
417 418
        auto& mp = p_plugin->getMD();
        if( !mp )
Rafaël Carré's avatar
Rafaël Carré committed
419 420
        {
            if( index != ID_input_state )
421
                RETURN_ON_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
422 423 424
            else
            {
                /* for input state, return CLOSED rather than an exception */
425
                result = 0;
Rafaël Carré's avatar
Rafaël Carré committed
426 427 428 429 430 431 432 433
                return INVOKERESULT_NO_ERROR;
            }
        }

        switch( index )
        {
            case ID_input_length:
            {
434
                result = mp.length();
Rafaël Carré's avatar
Rafaël Carré committed
435 436 437 438
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_position:
            {
439
                result = mp.position();
Rafaël Carré's avatar
Rafaël Carré committed
440 441 442 443
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_time:
            {
444
                result = mp.time();
Rafaël Carré's avatar
Rafaël Carré committed
445 446 447 448
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_state:
            {
449
                result = (int)mp.state();
Rafaël Carré's avatar
Rafaël Carré committed
450 451 452 453
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_rate:
            {
454
                result = mp.rate();
Rafaël Carré's avatar
Rafaël Carré committed
455 456 457 458
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_fps:
            {
459
                result = mp.fps();
Rafaël Carré's avatar
Rafaël Carré committed
460 461 462 463
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_hasvout:
            {
464
                result = p_plugin->player().get_mp().hasVout() != 0;
Rafaël Carré's avatar
Rafaël Carré committed
465 466 467 468 469 470 471 472 473
                return INVOKERESULT_NO_ERROR;
            }
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

474 475
RuntimeNPObject::InvokeResult
LibvlcInputNPObject::setProperty(int index, const NPVariant &value)
Rafaël Carré's avatar
Rafaël Carré committed
476 477
{
    /* is plugin still running */
478
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
479
    {
480
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
481 482
        auto& mp = p_plugin->getMD();
        if( !mp )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
483
            RETURN_ON_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
484

485
        auto v = npapi::Variant( value );
Rafaël Carré's avatar
Rafaël Carré committed
486 487 488 489
        switch( index )
        {
            case ID_input_position:
            {
490
                if( !v.is<double>() )
Rafaël Carré's avatar
Rafaël Carré committed
491 492 493 494
                {
                    return INVOKERESULT_INVALID_VALUE;
                }

495
                mp.setPosition( v );
Rafaël Carré's avatar
Rafaël Carré committed
496 497 498 499
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_time:
            {
500
                if( !v.is<int>() )
Rafaël Carré's avatar
Rafaël Carré committed
501 502 503 504
                {
                    return INVOKERESULT_INVALID_VALUE;
                }

505
                mp.setTime( v );
Rafaël Carré's avatar
Rafaël Carré committed
506 507 508 509
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_rate:
            {
510
                if( !v.is<double>() )
Rafaël Carré's avatar
Rafaël Carré committed
511 512 513 514
                {
                    return INVOKERESULT_INVALID_VALUE;
                }

515
                mp.setRate( v );
Rafaël Carré's avatar
Rafaël Carré committed
516 517 518 519 520 521 522 523 524 525 526 527
                return INVOKERESULT_NO_ERROR;
            }
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcInputNPObject::methodNames[] =
{
    /* no methods */
528
    "none",
Rafaël Carré's avatar
Rafaël Carré committed
529
};
530
COUNTNAMES(LibvlcInputNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
531

532 533 534 535 536 537
enum LibvlcInputNPObjectMethodIds
{
    ID_none,
};

RuntimeNPObject::InvokeResult
538 539
LibvlcInputNPObject::invoke(int index, const NPVariant *,
                            uint32_t, NPVariant &)
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
        switch( index )
        {
            case ID_none:
                return INVOKERESULT_NO_SUCH_METHOD;
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
/*
** 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
602
LibvlcMediaDescriptionNPObject::getProperty(int index, npapi::OutVariant &result)
603 604 605 606 607
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
        VlcPlugin* p_plugin = getPrivate<VlcPlugin>();
608 609
        auto& mp = p_plugin->getMD();
        if( !mp )
610
            RETURN_ON_ERROR;
611 612
        auto media = mp.media();
        if( !media )
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
            RETURN_ON_ERROR;
        const char *info;
        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:
634 635
            {
                auto m = media->meta( (libvlc_meta_t)index );
636 637
                result = m;
                return INVOKERESULT_NO_ERROR;
638
            }
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
            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
658 659 660 661
/*
** implementation of libvlc playlist items object
*/

662
const NPUTF8 * const LibvlcPlaylistItemsNPObject::propertyNames[] =
Rafaël Carré's avatar
Rafaël Carré committed
663 664 665
{
    "count",
};
666
COUNTNAMES(LibvlcPlaylistItemsNPObject,propertyCount,propertyNames);
Rafaël Carré's avatar
Rafaël Carré committed
667 668 669 670 671 672

enum LibvlcPlaylistItemsNPObjectPropertyIds
{
    ID_playlistitems_count,
};

673
RuntimeNPObject::InvokeResult
674
LibvlcPlaylistItemsNPObject::getProperty(int index, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
675 676
{
    /* is plugin still running */
677
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
678
    {
679
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
680 681 682 683 684

        switch( index )
        {
            case ID_playlistitems_count:
            {
685
                result = p_plugin->player().items_count();
Rafaël Carré's avatar
Rafaël Carré committed
686 687 688 689 690 691 692 693 694 695 696 697 698 699
                return INVOKERESULT_NO_ERROR;
            }
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcPlaylistItemsNPObject::methodNames[] =
{
    "clear",
    "remove",
};
700
COUNTNAMES(LibvlcPlaylistItemsNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
701 702 703 704 705 706 707

enum LibvlcPlaylistItemsNPObjectMethodIds
{
    ID_playlistitems_clear,
    ID_playlistitems_remove,
};

708 709
RuntimeNPObject::InvokeResult
LibvlcPlaylistItemsNPObject::invoke(int index, const NPVariant *args,
710
                                    uint32_t argCount, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
711 712
{
    /* is plugin still running */
713
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
714
    {
715
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
716 717 718 719 720 721

        switch( index )
        {
            case ID_playlistitems_clear:
                if( argCount == 0 )
                {
722
                    p_plugin->player().clear_items();
723
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
724 725 726
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlistitems_remove:
727 728 729 730 731
            {
                if ( argCount < 1 )
                    INVOKERESULT_INVALID_ARGS;
                auto v = npapi::Variant( args[0] );
                if( v.is<int>() )
Rafaël Carré's avatar
Rafaël Carré committed
732
                {
733
                    if( !p_plugin->player().delete_item( v ) )
734
                        return INVOKERESULT_GENERIC_ERROR;
735
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
736 737
                }
                return INVOKERESULT_NO_SUCH_METHOD;
738
            }
Rafaël Carré's avatar
Rafaël Carré committed
739 740 741 742 743 744 745 746 747 748 749 750 751
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

/*
** implementation of libvlc playlist object
*/

LibvlcPlaylistNPObject::~LibvlcPlaylistNPObject()
{
752 753 754
    // Why the isValid()?
    if( isValid() && playlistItemsObj )
        NPN_ReleaseObject(playlistItemsObj);
Rafaël Carré's avatar
Rafaël Carré committed
755 756
};

757
const NPUTF8 * const LibvlcPlaylistNPObject::propertyNames[] =
Rafaël Carré's avatar
Rafaël Carré committed
758 759 760
{
    "itemCount", /* deprecated */
    "isPlaying",
761
    "currentItem",
Rafaël Carré's avatar
Rafaël Carré committed
762 763
    "items",
};
764
COUNTNAMES(LibvlcPlaylistNPObject,propertyCount,propertyNames);
Rafaël Carré's avatar
Rafaël Carré committed
765 766 767 768 769

enum LibvlcPlaylistNPObjectPropertyIds
{
    ID_playlist_itemcount,
    ID_playlist_isplaying,
770
    ID_playlist_currentitem,
Rafaël Carré's avatar
Rafaël Carré committed
771 772 773
    ID_playlist_items,
};

774
RuntimeNPObject::InvokeResult
775
LibvlcPlaylistNPObject::getProperty(int index, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
776 777
{
    /* is plugin still running */
778
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
779
    {
780
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
781 782 783 784 785

        switch( index )
        {
            case ID_playlist_itemcount: /* deprecated */
            {
786
                result = p_plugin->player().items_count();
Rafaël Carré's avatar
Rafaël Carré committed
787 788 789 790
                return INVOKERESULT_NO_ERROR;
            }
            case ID_playlist_isplaying:
            {
791
                result = p_plugin->player().mlp().isPlaying();
Rafaël Carré's avatar
Rafaël Carré committed
792 793
                return INVOKERESULT_NO_ERROR;
            }
794 795
            case ID_playlist_currentitem:
            {
796
                result = p_plugin->player().current_item();
797 798
                return INVOKERESULT_NO_ERROR;
            }
Rafaël Carré's avatar
Rafaël Carré committed
799 800
            case ID_playlist_items:
            {
801
                InstantObj<LibvlcPlaylistItemsNPObject>( playlistItemsObj );
802
                result = playlistItemsObj;
Rafaël Carré's avatar
Rafaël Carré committed
803 804 805 806 807 808 809 810 811 812 813 814 815 816
                return INVOKERESULT_NO_ERROR;
            }
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcPlaylistNPObject::methodNames[] =
{
    "add",
    "play",
    "playItem",
817
    "pause",
Rafaël Carré's avatar
Rafaël Carré committed
818 819 820 821 822 823 824
    "togglePause",
    "stop",
    "next",
    "prev",
    "clear", /* deprecated */
    "removeItem", /* deprecated */
};
825
COUNTNAMES(LibvlcPlaylistNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
826 827 828 829 830 831

enum LibvlcPlaylistNPObjectMethodIds
{
    ID_playlist_add,
    ID_playlist_play,
    ID_playlist_playItem,
832
    ID_playlist_pause,
Rafaël Carré's avatar
Rafaël Carré committed
833 834 835 836 837 838 839 840
    ID_playlist_togglepause,
    ID_playlist_stop,
    ID_playlist_next,
    ID_playlist_prev,
    ID_playlist_clear,
    ID_playlist_removeitem
};

841 842
RuntimeNPObject::InvokeResult
LibvlcPlaylistNPObject::invoke(int index, const NPVariant *args,
843
                               uint32_t argCount, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
844 845
{
    /* is plugin still running */
846
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
847
    {
848
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
849 850 851

        switch( index )
        {
852
            // XXX FIXME this needs squashing into something much smaller
Rafaël Carré's avatar
Rafaël Carré committed
853 854 855 856
            case ID_playlist_add:
            {
                if( (argCount < 1) || (argCount > 3) )
                    return INVOKERESULT_NO_SUCH_METHOD;
857
                if( !npapi::is_string( args[0] ) )
JP Dinger's avatar
JP Dinger committed
858
                    return INVOKERESULT_NO_SUCH_METHOD;
Rafaël Carré's avatar
Rafaël Carré committed
859 860

                // grab URL
861 862 863
                if( NPVARIANT_IS_NULL(args[0]) )
                    return INVOKERESULT_NO_SUCH_METHOD;

864 865 866 867
                // 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
868

869
                //FIXME: This is never used
870
                auto name = npapi::NPStringPtr( nullptr, NPN_MemFree );
Rafaël Carré's avatar
Rafaël Carré committed
871 872 873 874 875 876 877 878 879
                // grab name if available
                if( argCount > 1 )
                {
                    if( NPVARIANT_IS_NULL(args[1]) )
                    {
                        // do nothing
                    }
                    else if( NPVARIANT_IS_STRING(args[1]) )
                    {
880
                        name = npapi::to_string( args[1] );
Rafaël Carré's avatar
Rafaël Carré committed
881 882 883 884 885 886 887 888 889 890 891 892 893
                    }
                    else
                    {
                        return INVOKERESULT_INVALID_VALUE;
                    }
                }

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

                // grab options if available
                if( argCount > 2 )
                {
894 895
                    npapi::Variant v = args[2];
                    if( v.is<std::nullptr_t>() )
Rafaël Carré's avatar
Rafaël Carré committed
896 897 898
                    {
                        // do nothing
                    }
899
                    else if( v.is<NPString>() )
Rafaël Carré's avatar
Rafaël Carré committed
900
                    {
901
                        parseOptions((NPString)v, &i_options, &ppsz_options);
Rafaël Carré's avatar
Rafaël Carré committed
902 903

                    }
904
                    else if( v.is<NPObject>() )
Rafaël Carré's avatar
Rafaël Carré committed
905
                    {
906
                        parseOptions((NPObject*)v, &i_options, &ppsz_options);
Rafaël Carré's avatar
Rafaël Carré committed
907 908 909 910 911 912 913
                    }
                    else
                    {
                        return INVOKERESULT_INVALID_VALUE;
                    }
                }

914
                int item = p_plugin->player().add_item( url ? url.get() : s.get(), i_options, const_cast<const char **>(ppsz_options));
915 916 917
                if( item == -1 )
                    RETURN_ON_ERROR;

Rafaël Carré's avatar
Rafaël Carré committed
918 919
                for( int i=0; i< i_options; ++i )
                {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
920
                    free(ppsz_options[i]);
Rafaël Carré's avatar
Rafaël Carré committed
921
                }
922
                free(ppsz_options);
Rafaël Carré's avatar
Rafaël Carré committed
923

924
                result = item;
925
                return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
926 927 928 929
            }
            case ID_playlist_play:
                if( argCount == 0 )
                {
930
                    p_plugin->player().play();
931
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
932 933 934
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_playItem:
935 936 937 938 939
            {
                if ( argCount < 1 )
                    return INVOKERESULT_INVALID_ARGS;
                npapi::Variant v = args[0];
                if ( v.is<int>() )
Rafaël Carré's avatar
Rafaël Carré committed
940
                {
941
                    p_plugin->player().mlp().playItemAtIndex( v );
942
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
943 944
                }
                return INVOKERESULT_NO_SUCH_METHOD;
945
            }
946
            case ID_playlist_pause:
Rafaël Carré's avatar
Rafaël Carré committed
947 948
                if( argCount == 0 )
                {
949
                    p_plugin->player().get_mp().setPause( true );
950
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
951 952
                }
                return INVOKERESULT_NO_SUCH_METHOD;
953 954 955
            case ID_playlist_togglepause:
                if( argCount == 0 )
                {
956
                    p_plugin->player().mlp().pause();
957 958 959
                    return INVOKERESULT_NO_ERROR;
                }
                return INVOKERESULT_NO_SUCH_METHOD;
Rafaël Carré's avatar
Rafaël Carré committed
960 961 962
            case ID_playlist_stop:
                if( argCount == 0 )
                {
963
                    p_plugin->player().mlp().stop();
964
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
965 966 967 968 969
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_next:
                if( argCount == 0 )
                {
970
                    p_plugin->player().mlp().next();
971
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
972 973 974 975 976
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_prev:
                if( argCount == 0 )
                {
977
                    p_plugin->player().mlp().previous();
978
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
979 980 981 982 983
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_clear: /* deprecated */
                if( argCount == 0 )
                {
984
                    p_plugin->player().clear_items();
985
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
986 987 988
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_removeitem: /* deprecated */
989 990 991 992 993
            {
                if ( argCount < 1 )
                    return INVOKERESULT_INVALID_ARGS;
                npapi::Variant v = args[0];
                if ( v.is<int>() )
Rafaël Carré's avatar
Rafaël Carré committed
994
                {
995
                    if( !p_plugin->player().delete_item( v ) )
996
                        return INVOKERESULT_GENERIC_ERROR;
997
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
998 999
                }
                return INVOKERESULT_NO_SUCH_METHOD;
1000
            }
Rafaël Carré's avatar
Rafaël Carré committed
1001 1002 1003 1004 1005 1006 1007
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

1008 1009 1010 1011 1012 1013
// 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.

1014
void LibvlcPlaylistNPObject::parseOptions(const NPString& nps,
1015
                                         int *i_options, char*** ppsz_options)
Rafaël Carré's avatar
Rafaël Carré committed
1016
{
1017
    if( nps.UTF8Length )
Rafaël Carré's avatar
Rafaël Carré committed
1018
    {
1019 1020
        auto s = CStr( strdup( nps.UTF8Characters ), free );
        char *val = s.get();
Rafaël Carré's avatar
Rafaël Carré committed
1021 1022 1023 1024 1025 1026 1027 1028
        if( val )
        {
            long capacity = 16;
            char **options = (char **)malloc(capacity*sizeof(char *));
            if( options )
            {
                int nOptions = 0;

1029
                char *end = val + nps.UTF8Length;
Rafaël Carré's avatar
Rafaël Carré committed
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
                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;
1056
                            char **moreOptions = (char **)realloc(options, capacity*sizeof(char*));
Rafaël Carré's avatar
Rafaël Carré committed
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
                            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;
            }
        }
    }
}

1081
// XXX FIXME See comment at the other parseOptions variant.
1082 1083
void LibvlcPlaylistNPObject::parseOptions(NPObject *obj, int *i_options,
                                          char*** ppsz_options)
Rafaël Carré's avatar
Rafaël Carré committed
1084 1085 1086
{
    /* WARNING: Safari does not implement NPN_HasProperty/NPN_HasMethod */

1087
    npapi::Variant value;
Rafaël Carré's avatar
Rafaël Carré committed
1088 1089 1090

    /* we are expecting to have a Javascript Array object */
    NPIdentifier propId = NPN_GetStringIdentifier("length");
1091
    if( NPN_GetProperty(_instance, obj, propId, value) )
Rafaël Carré's avatar
Rafaël Carré committed
1092
    {
1093
        int count = value;
Rafaël Carré's avatar
Rafaël Carré committed
1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104

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

                while( nOptions < count )
                {
1105
                    npapi::Variant value;
Rafaël Carré's avatar
Rafaël Carré committed
1106
                    propId = NPN_GetIntIdentifier(nOptions);
1107
                    if( ! NPN_GetProperty(_instance, obj, propId, value) )
Rafaël Carré's avatar
Rafaël Carré committed
1108 1109 1110
                        /* return what we got so far */
                        break;

1111
                    if( !value.is<NPString>() )
Rafaël Carré's avatar
Rafaël Carré committed
1112 1113 1114 1115 1116 1117 1118 1119
                    {
                        /* return what we got so far */
                        break;
                    }

                    if( nOptions == capacity )
                    {
                        capacity += 16;
1120
                        char **moreOptions = (char **)realloc(options, capacity*sizeof(char*));
Rafaël Carré's avatar
Rafaël Carré committed
1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
                        if( ! moreOptions )
                        {
                            /* failed to allocate more memory */
                            /* return what we got so far */
                            *i_options = nOptions;
                            *ppsz_options = options;
                            break;
                        }
                        options = moreOptions;
                    }

1132
                    options[nOptions++] = strdup(value);
Rafaël Carré's avatar
Rafaël Carré committed
1133 1134 1135 1136 1137 1138 1139 1140
                }
                *i_options = nOptions;
                *ppsz_options = options;
            }
        }
    }
}

1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158
/*
** 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
1159
LibvlcSubtitleNPObject::getProperty(int index, npapi::OutVariant& result)
1160 1161 1162 1163
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
1164
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
1165 1166
        auto& mp = p_plugin->getMD();
        if( !mp )
1167
            RETURN_ON_ERROR;
1168 1169 1170 1171 1172

        switch( index )
        {
            case ID_subtitle_track:
            {
1173
                /* get the current internal subtitles track ID */
1174
                result = p_plugin->player().currentSubtitleTrack();
1175 1176 1177 1178 1179
                return INVOKERESULT_NO_ERROR;
            }
            case ID_subtitle_count:
            {
                /* get the number of subtitles available */
1180
                result = mp.spuCount();
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
                /* return it */
                return INVOKERESULT_NO_ERROR;
            }
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

RuntimeNPObject::InvokeResult
LibvlcSubtitleNPObject::setProperty(int index, const NPVariant &value)
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
1195
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
1196 1197
        auto& mp = p_plugin->getMD();
        if( !mp )
1198
            RETURN_ON_ERROR;
1199 1200 1201 1202 1203

        switch( index )
        {
            case ID_subtitle_track:
            {
1204 1205
                auto v = npapi::Variant( value );
                if( v.is<int>() )
1206
                {
1207 1208 1209 1210
                    auto tracks = mp.spuDescription();
                    if ( v >= tracks.size() )
                        return INVOKERESULT_INVALID_ARGS;
                    if ( mp.setSpu( tracks[ v ].id() ) )
1211
                        return INVOKERESULT_NO_ERROR;
1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232
                }
                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,
1233
                            uint32_t argCount, npapi::OutVariant& result)
1234 1235 1236 1237
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
1238
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
1239 1240
        auto& mp = p_plugin->getMD();
        if( !mp )
1241
            RETURN_ON_ERROR;
1242 1243 1244 1245 1246

        switch( index )
        {
            case ID_subtitle_description:
            {
1247 1248 1249 1250
                if ( argCount < 1 )
                    return INVOKERESULT_INVALID_ARGS;
                auto v = npapi::Variant( args[0] );
                if ( v.is<int>() )
1251
                {
1252
                    auto tracks = mp.spuDescription();
1253
                    if ( v >= tracks.size() )
1254
                        return INVOKERESULT_INVALID_VALUE;
1255
                    /* display the name of the track chosen */
1256 1257
                    result = tracks[v].name();
                    return INVOKERESULT_NO_ERROR;
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            }
            default:
                return INVOKERESULT_NO_SUCH_METHOD;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

Rafaël Carré's avatar
Rafaël Carré committed
1268 1269 1270 1271
/*
** implementation of libvlc video object
*/

1272 1273 1274 1275 1276 1277 1278 1279 1280 1281
LibvlcVideoNPObject::~LibvlcVideoNPObject()
{
    if( isValid() )
    {
        if( marqueeObj ) NPN_ReleaseObject(marqueeObj);
        if( logoObj    ) NPN_ReleaseObject(logoObj);
        if( deintObj   ) NPN_ReleaseObject(deintObj);
    }
}

1282
const NPUTF8 * const LibvlcVideoNPObject::propertyNames[] =
Rafaël Carré's avatar
Rafaël Carré committed
1283 1284 1285 1286 1287 1288 1289
{
    "fullscreen",
    "height",
    "width",
    "aspectRatio",
    "subtitle",
    "crop",
1290
    "teletext",
1291
    "marquee",
1292 1293
    "logo",
    "deinterlace",
Rafaël Carré's avatar
Rafaël Carré committed
1294 1295 1296 1297 1298 1299 1300 1301 1302 1303
};

enum LibvlcVideoNPObjectPropertyIds
{
    ID_video_fullscreen,
    ID_video_height,
    ID_video_width,
    ID_video_aspectratio,
    ID_video_subtitle,
    ID_video_crop,
1304
    ID_video_teletext,
1305
    ID_video_marquee,
1306 1307
    ID_video_logo,
    ID_video_deinterlace,
Rafaël Carré's avatar
Rafaël Carré committed
1308
};
1309
COUNTNAMES(LibvlcVideoNPObject,propertyCount,propertyNames);
Rafaël Carré's avatar
Rafaël Carré committed
1310

1311
RuntimeNPObject::InvokeResult
1312
LibvlcVideoNPObject::getProperty(int index, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
1313 1314
{
    /* is plugin still running */
1315
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
1316
    {
1317
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
1318 1319
        auto& mp = p_plugin->getMD();
        if( !mp )
1320
            RETURN_ON_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1321 1322 1323 1324 1325

        switch( index )
        {
            case ID_video_fullscreen:
            {
1326
                result = p_plugin->get_fullscreen();
Rafaël Carré's avatar
Rafaël Carré committed
1327 1328 1329 1330
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_height:
            {
1331 1332
                unsigned width, height;
                mp.size( 0, &width, &height );
1333
                result = height;
Rafaël Carré's avatar
Rafaël Carré committed
1334 1335 1336 1337
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_width:
            {
1338 1339
                unsigned width, height;
                mp.size( 0, &width, &height );
1340
                result = width;
Rafaël Carré's avatar
Rafaël Carré committed
1341 1342 1343 1344
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_aspectratio:
            {
1345 1346
                auto ar = mp.aspectRatio();
                if( ar.empty() )
Rafaël Carré's avatar
Rafaël Carré committed
1347 1348
                    return INVOKERESULT_GENERIC_ERROR;

1349
                result = ar;
Rafaël Carré's avatar
Rafaël Carré committed
1350 1351 1352 1353
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_subtitle:
            {
1354
                result = mp.spu();
Rafaël Carré's avatar
Rafaël Carré committed
1355 1356 1357 1358
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_crop:
            {
1359 1360
                auto geo = mp.cropGeometry();
                if( geo.empty() )
Rafaël Carré's avatar
Rafaël Carré committed
1361 1362
                    return INVOKERESULT_GENERIC_ERROR;

1363
                result = geo;
Rafaël Carré's avatar
Rafaël Carré committed
1364 1365 1366 1367
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_teletext:
            {
1368
                int i_page = mp.teletext();
1369 1370
                if( i_page < 0 )
                    return INVOKERESULT_GENERIC_ERROR;
1371
                result = i_page;
Rafaël Carré's avatar
Rafaël Carré committed
1372 1373
                return INVOKERESULT_NO_ERROR;
            }
1374 1375
            case ID_video_marquee:
            {
1376
                InstantObj<LibvlcMarqueeNPObject>( marqueeObj );
1377
                result = marqueeObj;
1378 1379
                return INVOKERESULT_NO_ERROR;
            }
1380 1381 1382
            case ID_video_logo:
            {
                InstantObj<LibvlcLogoNPObject>( logoObj );
1383
                result = logoObj;
1384 1385
                return INVOKERESULT_NO_ERROR;
            }
1386 1387 1388
            case ID_video_deinterlace:
            {
                InstantObj<LibvlcDeinterlaceNPObject>( deintObj );
1389
                result = deintObj;
1390 1391
                return INVOKERESULT_NO_ERROR;
            }
Rafaël Carré's avatar
Rafaël Carré committed
1392 1393 1394 1395 1396
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

1397 1398
RuntimeNPObject::InvokeResult
LibvlcVideoNPObject::setProperty(int index, const NPVariant &value)
Rafaël Carré's avatar
Rafaël Carré committed
1399 1400
{
    /* is plugin still running */
1401
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
1402
    {
1403
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
1404 1405
        auto& mp = p_plugin->getMD();
        if( !mp )