npolibvlc.cpp 61 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
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

Rafaël Carré's avatar
Rafaël Carré committed
30 31 32 33 34 35 36
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

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

37
#include "../../common/position.h"
38

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

49 50 51 52
#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
53 54 55 56 57 58 59
/*
** implementation of libvlc root object
*/

LibvlcRootNPObject::~LibvlcRootNPObject()
{
    /*
60 61 62 63 64 65
    ** 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
66 67 68 69 70 71
    */
    if( isValid() )
    {
        if( audioObj    ) NPN_ReleaseObject(audioObj);
        if( inputObj    ) NPN_ReleaseObject(inputObj);
        if( playlistObj ) NPN_ReleaseObject(playlistObj);
72
        if( subtitleObj ) NPN_ReleaseObject(subtitleObj);
Rafaël Carré's avatar
Rafaël Carré committed
73
        if( videoObj    ) NPN_ReleaseObject(videoObj);
74
        if( mediaDescriptionObj ) NPN_ReleaseObject(mediaDescriptionObj);
Rafaël Carré's avatar
Rafaël Carré committed
75 76 77
    }
}

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

enum LibvlcRootNPObjectPropertyIds
{
    ID_root_audio = 0,
    ID_root_input,
    ID_root_playlist,
95
    ID_root_subtitle,
Rafaël Carré's avatar
Rafaël Carré committed
96 97
    ID_root_video,
    ID_root_VersionInfo,
98
    ID_root_MediaDescription,
Rafaël Carré's avatar
Rafaël Carré committed
99 100
};

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

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

enum LibvlcRootNPObjectMethodIds
{
    ID_root_versionInfo,
157
    ID_root_getVersionInfo,
158 159
    ID_root_addeventlistener,
    ID_root_removeeventlistener,
Rafaël Carré's avatar
Rafaël Carré committed
160 161
};

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

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

    case ID_root_addeventlistener:
    case ID_root_removeeventlistener:
180 181 182 183
        if ( argCount < 2 )
            return INVOKERESULT_INVALID_ARGS;

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

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

190
        if( !VlcPluginBase::canUseEventListener() )
Rafaël Carré's avatar
Rafaël Carré committed
191
        {
192 193
            NPN_SetException(this, ERROR_API_VERSION);
            return INVOKERESULT_GENERIC_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
194
        }
195

196
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
197

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

/*
** implementation of libvlc audio object
*/

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

enum LibvlcAudioNPObjectPropertyIds
{
    ID_audio_mute,
    ID_audio_volume,
    ID_audio_track,
230
    ID_audio_count,
Rafaël Carré's avatar
Rafaël Carré committed
231 232 233
    ID_audio_channel,
};

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

242 243
        auto& mp = p_plugin->getMD();
        if( !mp )
244
            RETURN_ON_ERROR;
Rémi Duraffort's avatar
Rémi Duraffort committed
245

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

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

288 289
        auto& mp = p_plugin->getMD();
        if( !mp )
290
            RETURN_ON_ERROR;
Rémi Duraffort's avatar
Rémi Duraffort committed
291

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

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

enum LibvlcAudioNPObjectMethodIds
{
    ID_audio_togglemute,
343
    ID_audio_description,
Rafaël Carré's avatar
Rafaël Carré committed
344 345
};

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

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

/*
** implementation of libvlc input object
*/

394 395 396 397 398 399 400 401 402
LibvlcInputNPObject::~LibvlcInputNPObject()
{
    if( isValid() )
    {
        if( titleObj   ) NPN_ReleaseObject(titleObj);
        if( chapterObj ) NPN_ReleaseObject(chapterObj);
    }
}

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

enum LibvlcInputNPObjectPropertyIds
{
    ID_input_length,
    ID_input_position,
    ID_input_time,
    ID_input_state,
    ID_input_rate,
    ID_input_fps,
    ID_input_hasvout,
426 427
    ID_input_title,
    ID_input_chapter,
Rafaël Carré's avatar
Rafaël Carré committed
428 429
};

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

        switch( index )
        {
            case ID_input_length:
            {
454
                result = mp.length();
Rafaël Carré's avatar
Rafaël Carré committed
455 456 457 458
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_position:
            {
459
                result = mp.position();
Rafaël Carré's avatar
Rafaël Carré committed
460 461 462 463
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_time:
            {
464
                result = mp.time();
Rafaël Carré's avatar
Rafaël Carré committed
465 466 467 468
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_state:
            {
469
                result = (int)mp.state();
Rafaël Carré's avatar
Rafaël Carré committed
470 471 472 473
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_rate:
            {
474
                result = mp.rate();
Rafaël Carré's avatar
Rafaël Carré committed
475 476 477 478
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_fps:
            {
479 480 481 482 483 484 485 486 487
                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
488
                        return INVOKERESULT_NO_ERROR;
489 490 491
                    }
                }
                result = 0.0f;
Rafaël Carré's avatar
Rafaël Carré committed
492 493 494 495
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_hasvout:
            {
496
                result = p_plugin->player().get_mp().hasVout() != 0;
Rafaël Carré's avatar
Rafaël Carré committed
497 498
                return INVOKERESULT_NO_ERROR;
            }
499 500 501 502 503 504 505 506 507 508 509 510
            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
511 512 513 514 515 516 517
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

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

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

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

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

559
                mp.setRate( v );
Rafaël Carré's avatar
Rafaël Carré committed
560 561 562 563 564 565 566 567 568 569 570 571
                return INVOKERESULT_NO_ERROR;
            }
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcInputNPObject::methodNames[] =
{
    /* no methods */
572
    "none",
Rafaël Carré's avatar
Rafaël Carré committed
573
};
574
COUNTNAMES(LibvlcInputNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
575

576 577 578 579 580 581
enum LibvlcInputNPObjectMethodIds
{
    ID_none,
};

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

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 642 643 644 645
/*
** 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
646
LibvlcMediaDescriptionNPObject::getProperty(int index, npapi::OutVariant &result)
647 648 649 650 651
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
        VlcPlugin* p_plugin = getPrivate<VlcPlugin>();
652 653
        auto& mp = p_plugin->getMD();
        if( !mp )
654
            RETURN_ON_ERROR;
655 656
        auto media = mp.media();
        if( !media )
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
            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:
677 678
            {
                auto m = media->meta( (libvlc_meta_t)index );
679 680
                result = m;
                return INVOKERESULT_NO_ERROR;
681
            }
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
            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
701 702 703 704
/*
** implementation of libvlc playlist items object
*/

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

enum LibvlcPlaylistItemsNPObjectPropertyIds
{
    ID_playlistitems_count,
};

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

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

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

enum LibvlcPlaylistItemsNPObjectMethodIds
{
    ID_playlistitems_clear,
    ID_playlistitems_remove,
};

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

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

/*
** implementation of libvlc playlist object
*/

LibvlcPlaylistNPObject::~LibvlcPlaylistNPObject()
{
795 796 797
    // Why the isValid()?
    if( isValid() && playlistItemsObj )
        NPN_ReleaseObject(playlistItemsObj);
Rafaël Carré's avatar
Rafaël Carré committed
798 799
};

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

enum LibvlcPlaylistNPObjectPropertyIds
{
    ID_playlist_itemcount,
    ID_playlist_isplaying,
813
    ID_playlist_currentitem,
Rafaël Carré's avatar
Rafaël Carré committed
814 815 816
    ID_playlist_items,
};

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

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

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

enum LibvlcPlaylistNPObjectMethodIds
{
    ID_playlist_add,
    ID_playlist_play,
    ID_playlist_playItem,
875
    ID_playlist_pause,
Rafaël Carré's avatar
Rafaël Carré committed
876 877 878 879 880 881 882 883
    ID_playlist_togglepause,
    ID_playlist_stop,
    ID_playlist_next,
    ID_playlist_prev,
    ID_playlist_clear,
    ID_playlist_removeitem
};

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

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

                // grab URL
904 905 906
                if( NPVARIANT_IS_NULL(args[0]) )
                    return INVOKERESULT_NO_SUCH_METHOD;

907 908 909 910
                // 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
911

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

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

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

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

957
                int item = p_plugin->player().add_item( url ? url.get() : s.get(), i_options, const_cast<const char **>(ppsz_options));
958 959 960
                if( item == -1 )
                    RETURN_ON_ERROR;

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

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

1051 1052 1053 1054 1055 1056
// 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.

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

1072
                char *end = val + nps.UTF8Length;
Rafaël Carré's avatar
Rafaël Carré committed
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098
                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;
1099
                            char **moreOptions = (char **)realloc(options, capacity*sizeof(char*));
Rafaël Carré's avatar
Rafaël Carré committed
1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
                            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;
            }
        }
    }
}

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

1130
    npapi::Variant value;
Rafaël Carré's avatar
Rafaël Carré committed
1131 1132 1133

    /* we are expecting to have a Javascript Array object */
    NPIdentifier propId = NPN_GetStringIdentifier("length");
1134
    if( NPN_GetProperty(_instance, obj, propId, value) )
Rafaël Carré's avatar
Rafaël Carré committed
1135
    {
Daniel Amm's avatar
Daniel Amm committed
1136 1137 1138 1139
        /* Check if result is valid (because we don't use NPN_HasProperty, the result can be void) */
        if( !value.is<int>() )
            return;

1140
        int count = value;
Rafaël Carré's avatar
Rafaël Carré committed
1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151

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

                while( nOptions < count )
                {
1152
                    npapi::Variant value;
Rafaël Carré's avatar
Rafaël Carré committed
1153
                    propId = NPN_GetIntIdentifier(nOptions);
1154
                    if( ! NPN_GetProperty(_instance, obj, propId, value) )
Rafaël Carré's avatar
Rafaël Carré committed
1155 1156 1157
                        /* return what we got so far */
                        break;

1158
                    if( !value.is<NPString>() )
Rafaël Carré's avatar
Rafaël Carré committed
1159 1160 1161 1162 1163 1164 1165 1166
                    {
                        /* return what we got so far */
                        break;
                    }

                    if( nOptions == capacity )
                    {
                        capacity += 16;
1167
                        char **moreOptions = (char **)realloc(options, capacity*sizeof(char*));
Rafaël Carré's avatar
Rafaël Carré committed
1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178
                        if( ! moreOptions )
                        {
                            /* failed to allocate more memory */
                            /* return what we got so far */
                            *i_options = nOptions;
                            *ppsz_options = options;
                            break;
                        }
                        options = moreOptions;
                    }

1179
                    options[nOptions++] = strdup(value);
Rafaël Carré's avatar
Rafaël Carré committed
1180 1181 1182 1183 1184 1185 1186 1187
                }
                *i_options = nOptions;
                *ppsz_options = options;
            }
        }
    }
}

1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
/*
** 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
1206
LibvlcSubtitleNPObject::getProperty(int index, npapi::OutVariant& result)
1207 1208 1209 1210
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
1211
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
1212 1213
        auto& mp = p_plugin->getMD();
        if( !mp )
1214
            RETURN_ON_ERROR;
1215 1216 1217 1218 1219

        switch( index )
        {
            case ID_subtitle_track:
            {
1220
                result = p_plugin->player().currentSubtitleTrack();
1221 1222 1223 1224
                return INVOKERESULT_NO_ERROR;
            }
            case ID_subtitle_count:
            {
1225
                result = mp.spuCount();
1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238
                return INVOKERESULT_NO_ERROR;
            }
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

RuntimeNPObject::InvokeResult
LibvlcSubtitleNPObject::setProperty(int index, const NPVariant &value)
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
1239
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
1240 1241
        auto& mp = p_plugin->getMD();
        if( !mp )
1242
            RETURN_ON_ERROR;
1243 1244 1245 1246 1247

        switch( index )
        {
            case ID_subtitle_track:
            {
1248
                const npapi::Variant v( value );
1249
                if( v.is<int>() )
1250
                {
1251 1252 1253
                    auto tracks = mp.spuDescription();
                    if ( v >= tracks.size() )
                        return INVOKERESULT_INVALID_ARGS;
1254 1255
                    mp.setSpu( tracks[ v ].id() );
                    return INVOKERESULT_NO_ERROR;
1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276
                }
                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,
1277
                            uint32_t argCount, npapi::OutVariant& result)
1278 1279 1280 1281
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
1282
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
1283 1284
        auto& mp = p_plugin->getMD();
        if( !mp )
1285
            RETURN_ON_ERROR;
1286 1287 1288 1289 1290

        switch( index )
        {
            case ID_subtitle_description:
            {
1291 1292
                if ( argCount < 1 )
                    return INVOKERESULT_INVALID_ARGS;
1293
                const npapi::Variant v( args[0] );
1294
                if ( v.is<int>() )
1295
                {
1296
                    auto tracks = mp.spuDescription();
1297
                    if ( v >= tracks.size() )
1298
                        return INVOKERESULT_INVALID_VALUE;
1299
                    /* display the name of the track chosen */
1300 1301
                    result = tracks[v].name();
                    return INVOKERESULT_NO_ERROR;
1302 1303 1304 1305 1306 1307 1308 1309 1310 1311
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            }
            default:
                return INVOKERESULT_NO_SUCH_METHOD;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

Rafaël Carré's avatar
Rafaël Carré committed
1312 1313 1314 1315
/*
** implementation of libvlc video object
*/

1316 1317 1318 1319 1320 1321 1322 1323 1324 1325
LibvlcVideoNPObject::~LibvlcVideoNPObject()
{
    if( isValid() )
    {
        if( marqueeObj ) NPN_ReleaseObject(marqueeObj);
        if( logoObj    ) NPN_ReleaseObject(logoObj);
        if( deintObj   ) NPN_ReleaseObject(deintObj);
    }
}

1326
const NPUTF8 * const LibvlcVideoNPObject::propertyNames[] =
Rafaël Carré's avatar
Rafaël Carré committed
1327 1328 1329 1330 1331 1332 1333
{
    "fullscreen",
    "height",
    "width",
    "aspectRatio",
    "subtitle",
    "crop",
1334
    "teletext",
1335
    "marquee",
1336 1337
    "logo",
    "deinterlace",
Rafaël Carré's avatar
Rafaël Carré committed
1338 1339 1340 1341 1342 1343 1344 1345 1346 1347
};

enum LibvlcVideoNPObjectPropertyIds
{
    ID_video_fullscreen,
    ID_video_height,
    ID_video_width,
    ID_video_aspectratio,
    ID_video_subtitle,
    ID_video_crop,
1348
    ID_video_teletext,
1349
    ID_video_marquee,
1350 1351
    ID_video_logo,
    ID_video_deinterlace,
Rafaël Carré's avatar
Rafaël Carré committed
1352
};
1353
COUNTNAMES(LibvlcVideoNPObject,propertyCount,propertyNames);
Rafaël Carré's avatar
Rafaël Carré committed
1354

1355
RuntimeNPObject::InvokeResult
1356
LibvlcVideoNPObject::getProperty(int index, npapi::OutVariant& result)
Rafaël Carré's avatar
Rafaël Carré committed
1357 1358
{
    /* is plugin still running */
1359
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
1360
    {
1361
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
1362 1363
        auto& mp = p_plugin->getMD();
        if( !mp )
1364
            RETURN_ON_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1365 1366 1367 1368 1369

        switch( index )
        {
            case ID_video_fullscreen:
            {
1370
                result = p_plugin->get_fullscreen();
Rafaël Carré's avatar
Rafaël Carré committed
1371 1372 1373 1374
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_height:
            {
1375 1376
                unsigned width, height;
                mp.size( 0, &width, &height );
1377
                result = height;
Rafaël Carré's avatar
Rafaël Carré committed
1378 1379 1380 1381
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_width:
            {
1382 1383
                unsigned width, height;
                mp.size( 0, &width, &height );
1384
                result = width;
Rafaël Carré's avatar
Rafaël Carré committed
1385 1386 1387 1388
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_aspectratio:
            {
1389
                result = mp.aspectRatio();
Rafaël Carré's avatar
Rafaël Carré committed
1390 1391 1392 1393
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_subtitle:
            {
1394
                result = p_plugin->player().currentSubtitleTrack();
Rafaël Carré's avatar
Rafaël Carré committed
1395 1396 1397 1398
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_crop:
            {
1399
                result = mp.cropGeometry();
Rafaël Carré's avatar
Rafaël Carré committed
1400 1401 1402 1403
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_teletext:
            {
Hugo Beauzée-Luyssen's avatar