npolibvlc.cpp 55.9 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 98
RuntimeNPObject::InvokeResult
LibvlcRootNPObject::getProperty(int index, NPVariant &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 );
Rafaël Carré's avatar
Rafaël Carré committed
107 108 109
                OBJECT_TO_NPVARIANT(NPN_RetainObject(audioObj), result);
                return INVOKERESULT_NO_ERROR;
            case ID_root_input:
110
                InstantObj<LibvlcInputNPObject>( inputObj );
Rafaël Carré's avatar
Rafaël Carré committed
111 112 113
                OBJECT_TO_NPVARIANT(NPN_RetainObject(inputObj), result);
                return INVOKERESULT_NO_ERROR;
            case ID_root_playlist:
114
                InstantObj<LibvlcPlaylistNPObject>( playlistObj );
Rafaël Carré's avatar
Rafaël Carré committed
115 116
                OBJECT_TO_NPVARIANT(NPN_RetainObject(playlistObj), result);
                return INVOKERESULT_NO_ERROR;
117
            case ID_root_subtitle:
118
                InstantObj<LibvlcSubtitleNPObject>( subtitleObj );
119 120
                OBJECT_TO_NPVARIANT(NPN_RetainObject(subtitleObj), result);
                return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
121
            case ID_root_video:
122
                InstantObj<LibvlcVideoNPObject>( videoObj );
Rafaël Carré's avatar
Rafaël Carré committed
123 124 125
                OBJECT_TO_NPVARIANT(NPN_RetainObject(videoObj), result);
                return INVOKERESULT_NO_ERROR;
            case ID_root_VersionInfo:
126
                return invokeResultString(libvlc_get_version(),result);
127 128 129 130 131 132
            case ID_root_MediaDescription:
            {
                InstantObj<LibvlcMediaDescriptionNPObject>( mediaDescriptionObj );
                OBJECT_TO_NPVARIANT(NPN_RetainObject(mediaDescriptionObj), result);
                return INVOKERESULT_NO_ERROR;
            }
Rafaël Carré's avatar
Rafaël Carré committed
133 134 135 136 137 138 139 140 141 142
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

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

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

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

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

    case ID_root_addeventlistener:
    case ID_root_removeeventlistener:
171
        if( (2 < argCount) ||
172
            !NPVARIANT_IS_STRING(args[0]) ||
173
            !NPVARIANT_IS_OBJECT(args[1]) )
174
            break;
Rafaël Carré's avatar
Rafaël Carré committed
175

176
        if( !VlcPluginBase::canUseEventListener() )
Rafaël Carré's avatar
Rafaël Carré committed
177
        {
178 179
            NPN_SetException(this, ERROR_API_VERSION);
            return INVOKERESULT_GENERIC_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
180
        }
181

182
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
183

Jean-Paul Saman's avatar
Jean-Paul Saman committed
184 185
        if( ID_root_addeventlistener == index )
        {
186 187
            p_plugin->subscribe( NPVARIANT_TO_STRING(args[0]).UTF8Characters,
                                         NPVARIANT_TO_OBJECT(args[1] ) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
188
        }
189
        else
Jean-Paul Saman's avatar
Jean-Paul Saman committed
190
        {
191 192
            p_plugin->unsubscribe( NPVARIANT_TO_STRING(args[1]).UTF8Characters,
                    NPVARIANT_TO_OBJECT( args[1] ) );
Jean-Paul Saman's avatar
Jean-Paul Saman committed
193
        }
194 195
        VOID_TO_NPVARIANT(result);

196
        return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
197
    }
198
    return INVOKERESULT_NO_SUCH_METHOD;
Rafaël Carré's avatar
Rafaël Carré committed
199 200 201 202 203 204
}

/*
** implementation of libvlc audio object
*/

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

enum LibvlcAudioNPObjectPropertyIds
{
    ID_audio_mute,
    ID_audio_volume,
    ID_audio_track,
220
    ID_audio_count,
Rafaël Carré's avatar
Rafaël Carré committed
221 222 223
    ID_audio_channel,
};

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

232 233
        auto& mp = p_plugin->getMD();
        if( !mp )
234
            RETURN_ON_ERROR;
Rémi Duraffort's avatar
Rémi Duraffort committed
235

Rafaël Carré's avatar
Rafaël Carré committed
236 237 238 239
        switch( index )
        {
            case ID_audio_mute:
            {
240
                bool muted = mp.mute();
Rafaël Carré's avatar
Rafaël Carré committed
241 242 243 244 245
                BOOLEAN_TO_NPVARIANT(muted, result);
                return INVOKERESULT_NO_ERROR;
            }
            case ID_audio_volume:
            {
246
                int volume = mp.volume();
Rafaël Carré's avatar
Rafaël Carré committed
247 248 249 250 251
                INT32_TO_NPVARIANT(volume, result);
                return INVOKERESULT_NO_ERROR;
            }
            case ID_audio_track:
            {
252
                /* get the current internal audio track ID */
253
                int actualTrack = mp.audioTrack();
254

255
                int audioTrackCount = mp.audioTrackCount();
256 257 258 259 260
                if (audioTrackCount < 0) {
                    INT32_TO_NPVARIANT(actualTrack, result);
                    return INVOKERESULT_NO_ERROR;
                }

261 262 263 264 265 266 267 268
                auto tracks = mp.audioTrackDescription();
                auto t = std::find_if(begin(tracks), end(tracks), [actualTrack](const VLC::TrackDescription& td) {
                    return td.id() == actualTrack;
                });
                if ( t == end( tracks ) )
                    INT32_TO_NPVARIANT(tracks.size(), result);
                else
                    INT32_TO_NPVARIANT(actualTrack, result);
Rafaël Carré's avatar
Rafaël Carré committed
269 270
                return INVOKERESULT_NO_ERROR;
            }
271 272 273
            case ID_audio_count:
            {
                // get the number of audio track available
274
                int i_track = mp.audioTrackCount();
275 276 277 278
                // return it
                INT32_TO_NPVARIANT(i_track, result);
                return INVOKERESULT_NO_ERROR;
            }
Rafaël Carré's avatar
Rafaël Carré committed
279 280
            case ID_audio_channel:
            {
281
                int channel = mp.channel();
Rafaël Carré's avatar
Rafaël Carré committed
282 283 284 285 286 287 288 289 290 291
                INT32_TO_NPVARIANT(channel, result);
                return INVOKERESULT_NO_ERROR;
            }
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

292 293
RuntimeNPObject::InvokeResult
LibvlcAudioNPObject::setProperty(int index, const NPVariant &value)
Rafaël Carré's avatar
Rafaël Carré committed
294 295
{
    /* is plugin still running */
296
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
297
    {
298
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
299

300 301
        auto& mp = p_plugin->getMD();
        if( !mp )
302
            RETURN_ON_ERROR;
Rémi Duraffort's avatar
Rémi Duraffort committed
303

Rafaël Carré's avatar
Rafaël Carré committed
304 305 306
        switch( index )
        {
            case ID_audio_mute:
307
                if( isBoolValue(value) )
Rafaël Carré's avatar
Rafaël Carré committed
308
                {
309
                    mp.setMute( boolValue( value ) );
Rafaël Carré's avatar
Rafaël Carré committed
310 311 312 313 314 315
                    return INVOKERESULT_NO_ERROR;
                }
                return INVOKERESULT_INVALID_VALUE;
            case ID_audio_volume:
                if( isNumberValue(value) )
                {
316
                    mp.setVolume( intValue( value ) );
Rafaël Carré's avatar
Rafaël Carré committed
317 318 319 320 321 322
                    return INVOKERESULT_NO_ERROR;
                }
                return INVOKERESULT_INVALID_VALUE;
            case ID_audio_track:
                if( isNumberValue(value) )
                {
323 324 325
                    int trackIdx = intValue(value);
                    if ( mp.setAudioTrack( trackIdx ) )
                        return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
326 327 328 329 330
                }
                return INVOKERESULT_INVALID_VALUE;
            case ID_audio_channel:
                if( isNumberValue(value) )
                {
331 332
                    if ( mp.setChannel( intValue( value ) ) )
                        return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
333 334 335 336 337 338 339 340 341 342 343 344
                }
                return INVOKERESULT_INVALID_VALUE;
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcAudioNPObject::methodNames[] =
{
    "toggleMute",
345
    "description",
Rafaël Carré's avatar
Rafaël Carré committed
346
};
347
COUNTNAMES(LibvlcAudioNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
348 349 350 351

enum LibvlcAudioNPObjectMethodIds
{
    ID_audio_togglemute,
352
    ID_audio_description,
Rafaël Carré's avatar
Rafaël Carré committed
353 354
};

355 356 357
RuntimeNPObject::InvokeResult
LibvlcAudioNPObject::invoke(int index, const NPVariant *args,
                            uint32_t argCount, NPVariant &result)
Rafaël Carré's avatar
Rafaël Carré committed
358 359
{
    /* is plugin still running */
360
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
361
    {
362
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
363 364
        auto& mp = p_plugin->getMD();
        if( !mp )
365
            RETURN_ON_ERROR;
Rémi Duraffort's avatar
Rémi Duraffort committed
366

Rafaël Carré's avatar
Rafaël Carré committed
367 368 369 370 371
        switch( index )
        {
            case ID_audio_togglemute:
                if( argCount == 0 )
                {
372
                    mp.toggleMute();
373 374
                    VOID_TO_NPVARIANT(result);
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
375 376
                }
                return INVOKERESULT_NO_SUCH_METHOD;
377 378
            case ID_audio_description:
            {
379
                if( argCount == 1 && isNumberValue(args[0]))
380
                {
381
                    int fakeTrackIndex = intValue(args[0]);
382 383 384 385 386
                    auto tracks = mp.audioTrackDescription();
                    auto track = std::find_if( begin( tracks ), end( tracks ), [fakeTrackIndex](const VLC::TrackDescription& t) {
                        return t.id() == fakeTrackIndex;
                    });
                    if (track == end( tracks ) )
387 388 389
                        return INVOKERESULT_INVALID_VALUE;

                    /* display the name of the track chosen */
390
                    return invokeResultString( (*track).name().c_str(), result );
391 392 393
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            }
Rafaël Carré's avatar
Rafaël Carré committed
394 395 396 397 398 399 400 401 402 403 404
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

/*
** implementation of libvlc input object
*/

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

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

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

        switch( index )
        {
            case ID_input_length:
            {
452
                double val = mp.length();
Rafaël Carré's avatar
Rafaël Carré committed
453 454 455 456 457
                DOUBLE_TO_NPVARIANT(val, result);
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_position:
            {
458
                double val = mp.position();
Rafaël Carré's avatar
Rafaël Carré committed
459 460 461 462 463
                DOUBLE_TO_NPVARIANT(val, result);
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_time:
            {
464
                double val = (double)mp.time();
Rafaël Carré's avatar
Rafaël Carré committed
465 466 467 468 469
                DOUBLE_TO_NPVARIANT(val, result);
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_state:
            {
470
                int val = mp.state();
Rafaël Carré's avatar
Rafaël Carré committed
471 472 473 474 475
                INT32_TO_NPVARIANT(val, result);
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_rate:
            {
476
                float val = mp.rate();
Rafaël Carré's avatar
Rafaël Carré committed
477 478 479 480 481
                DOUBLE_TO_NPVARIANT(val, result);
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_fps:
            {
482
                double val = mp.fps();
Rafaël Carré's avatar
Rafaël Carré committed
483 484 485 486 487
                DOUBLE_TO_NPVARIANT(val, result);
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_hasvout:
            {
488
                bool val = p_plugin->player_has_vout();
Rafaël Carré's avatar
Rafaël Carré committed
489 490 491 492 493 494 495 496 497 498
                BOOLEAN_TO_NPVARIANT(val, result);
                return INVOKERESULT_NO_ERROR;
            }
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

499 500
RuntimeNPObject::InvokeResult
LibvlcInputNPObject::setProperty(int index, const NPVariant &value)
Rafaël Carré's avatar
Rafaël Carré committed
501 502
{
    /* is plugin still running */
503
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
504
    {
505
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
506 507
        auto& mp = p_plugin->getMD();
        if( !mp )
Jean-Paul Saman's avatar
Jean-Paul Saman committed
508
            RETURN_ON_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
509 510 511 512 513

        switch( index )
        {
            case ID_input_position:
            {
514
                if( ! isNumberValue(value) )
Rafaël Carré's avatar
Rafaël Carré committed
515 516 517 518
                {
                    return INVOKERESULT_INVALID_VALUE;
                }

519
                float val = (float)doubleValue(value);
520
                mp.setPosition( val );
Rafaël Carré's avatar
Rafaël Carré committed
521 522 523 524
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_time:
            {
525
                if( ! isNumberValue(value) )
Rafaël Carré's avatar
Rafaël Carré committed
526 527 528 529
                {
                    return INVOKERESULT_INVALID_VALUE;
                }

530
                int64_t val = (int64_t)intValue(value);
531
                mp.setTime( val );
Rafaël Carré's avatar
Rafaël Carré committed
532 533 534 535
                return INVOKERESULT_NO_ERROR;
            }
            case ID_input_rate:
            {
536
                if( ! isNumberValue(value) )
Rafaël Carré's avatar
Rafaël Carré committed
537 538 539 540
                {
                    return INVOKERESULT_INVALID_VALUE;
                }

541
                float val = (float)doubleValue(value);
542
                mp.setRate( val );
Rafaël Carré's avatar
Rafaël Carré committed
543 544 545 546 547 548 549 550 551 552 553 554
                return INVOKERESULT_NO_ERROR;
            }
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcInputNPObject::methodNames[] =
{
    /* no methods */
555
    "none",
Rafaël Carré's avatar
Rafaël Carré committed
556
};
557
COUNTNAMES(LibvlcInputNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
558

559 560 561 562 563 564
enum LibvlcInputNPObjectMethodIds
{
    ID_none,
};

RuntimeNPObject::InvokeResult
565 566
LibvlcInputNPObject::invoke(int index, const NPVariant *,
                            uint32_t, NPVariant &)
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
        switch( index )
        {
            case ID_none:
                return INVOKERESULT_NO_SUCH_METHOD;
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
/*
** 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
LibvlcMediaDescriptionNPObject::getProperty(int index, NPVariant &result)
{
    /* is plugin still running */
    if( isPluginRunning() )
    {
        VlcPlugin* p_plugin = getPrivate<VlcPlugin>();
635 636
        auto& mp = p_plugin->getMD();
        if( !mp )
637
            RETURN_ON_ERROR;
638 639
        auto media = mp.media();
        if( !media )
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
            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:
661 662 663 664
            {
                auto m = media->meta( (libvlc_meta_t)index );
                return invokeResultString(m.c_str(), result);
            }
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
            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
684 685 686 687
/*
** implementation of libvlc playlist items object
*/

688
const NPUTF8 * const LibvlcPlaylistItemsNPObject::propertyNames[] =
Rafaël Carré's avatar
Rafaël Carré committed
689 690 691
{
    "count",
};
692
COUNTNAMES(LibvlcPlaylistItemsNPObject,propertyCount,propertyNames);
Rafaël Carré's avatar
Rafaël Carré committed
693 694 695 696 697 698

enum LibvlcPlaylistItemsNPObjectPropertyIds
{
    ID_playlistitems_count,
};

699 700
RuntimeNPObject::InvokeResult
LibvlcPlaylistItemsNPObject::getProperty(int index, NPVariant &result)
Rafaël Carré's avatar
Rafaël Carré committed
701 702
{
    /* is plugin still running */
703
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
704
    {
705
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
706 707 708 709 710

        switch( index )
        {
            case ID_playlistitems_count:
            {
711
                int val = p_plugin->playlist_count();
Rafaël Carré's avatar
Rafaël Carré committed
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726
                INT32_TO_NPVARIANT(val, result);
                return INVOKERESULT_NO_ERROR;
            }
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcPlaylistItemsNPObject::methodNames[] =
{
    "clear",
    "remove",
};
727
COUNTNAMES(LibvlcPlaylistItemsNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
728 729 730 731 732 733 734

enum LibvlcPlaylistItemsNPObjectMethodIds
{
    ID_playlistitems_clear,
    ID_playlistitems_remove,
};

735 736 737
RuntimeNPObject::InvokeResult
LibvlcPlaylistItemsNPObject::invoke(int index, const NPVariant *args,
                                    uint32_t argCount, NPVariant &result)
Rafaël Carré's avatar
Rafaël Carré committed
738 739
{
    /* is plugin still running */
740
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
741
    {
742
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
743 744 745 746 747 748

        switch( index )
        {
            case ID_playlistitems_clear:
                if( argCount == 0 )
                {
749
                    p_plugin->playlist_clear();
750 751
                    VOID_TO_NPVARIANT(result);
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
752 753 754 755 756
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlistitems_remove:
                if( (argCount == 1) && isNumberValue(args[0]) )
                {
757
                    if( !p_plugin->playlist_delete_item(intValue(args[0])) )
758
                        return INVOKERESULT_GENERIC_ERROR;
759 760
                    VOID_TO_NPVARIANT(result);
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

/*
** implementation of libvlc playlist object
*/

LibvlcPlaylistNPObject::~LibvlcPlaylistNPObject()
{
776 777 778
    // Why the isValid()?
    if( isValid() && playlistItemsObj )
        NPN_ReleaseObject(playlistItemsObj);
Rafaël Carré's avatar
Rafaël Carré committed
779 780
};

781
const NPUTF8 * const LibvlcPlaylistNPObject::propertyNames[] =
Rafaël Carré's avatar
Rafaël Carré committed
782 783 784
{
    "itemCount", /* deprecated */
    "isPlaying",
785
    "currentItem",
Rafaël Carré's avatar
Rafaël Carré committed
786 787
    "items",
};
788
COUNTNAMES(LibvlcPlaylistNPObject,propertyCount,propertyNames);
Rafaël Carré's avatar
Rafaël Carré committed
789 790 791 792 793

enum LibvlcPlaylistNPObjectPropertyIds
{
    ID_playlist_itemcount,
    ID_playlist_isplaying,
794
    ID_playlist_currentitem,
Rafaël Carré's avatar
Rafaël Carré committed
795 796 797
    ID_playlist_items,
};

798 799
RuntimeNPObject::InvokeResult
LibvlcPlaylistNPObject::getProperty(int index, NPVariant &result)
Rafaël Carré's avatar
Rafaël Carré committed
800 801
{
    /* is plugin still running */
802
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
803
    {
804
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
805 806 807 808 809

        switch( index )
        {
            case ID_playlist_itemcount: /* deprecated */
            {
810
                int val = p_plugin->playlist_count();
Rafaël Carré's avatar
Rafaël Carré committed
811 812 813 814 815
                INT32_TO_NPVARIANT(val, result);
                return INVOKERESULT_NO_ERROR;
            }
            case ID_playlist_isplaying:
            {
816
                int val = p_plugin->playlist_isplaying();
Rafaël Carré's avatar
Rafaël Carré committed
817 818 819
                BOOLEAN_TO_NPVARIANT(val, result);
                return INVOKERESULT_NO_ERROR;
            }
820 821 822 823 824 825
            case ID_playlist_currentitem:
            {
                int val = p_plugin->playlist_currentitem();
                INT32_TO_NPVARIANT(val, result);
                return INVOKERESULT_NO_ERROR;
            }
Rafaël Carré's avatar
Rafaël Carré committed
826 827
            case ID_playlist_items:
            {
828
                InstantObj<LibvlcPlaylistItemsNPObject>( playlistItemsObj );
Rafaël Carré's avatar
Rafaël Carré committed
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
                OBJECT_TO_NPVARIANT(NPN_RetainObject(playlistItemsObj), result);
                return INVOKERESULT_NO_ERROR;
            }
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcPlaylistNPObject::methodNames[] =
{
    "add",
    "play",
    "playItem",
844
    "pause",
Rafaël Carré's avatar
Rafaël Carré committed
845 846 847 848 849 850 851
    "togglePause",
    "stop",
    "next",
    "prev",
    "clear", /* deprecated */
    "removeItem", /* deprecated */
};
852
COUNTNAMES(LibvlcPlaylistNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
853 854 855 856 857 858

enum LibvlcPlaylistNPObjectMethodIds
{
    ID_playlist_add,
    ID_playlist_play,
    ID_playlist_playItem,
859
    ID_playlist_pause,
Rafaël Carré's avatar
Rafaël Carré committed
860 861 862 863 864 865 866 867
    ID_playlist_togglepause,
    ID_playlist_stop,
    ID_playlist_next,
    ID_playlist_prev,
    ID_playlist_clear,
    ID_playlist_removeitem
};

868 869 870
RuntimeNPObject::InvokeResult
LibvlcPlaylistNPObject::invoke(int index, const NPVariant *args,
                               uint32_t argCount, NPVariant &result)
Rafaël Carré's avatar
Rafaël Carré committed
871 872
{
    /* is plugin still running */
873
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
874
    {
875
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
876 877 878

        switch( index )
        {
879
            // XXX FIXME this needs squashing into something much smaller
Rafaël Carré's avatar
Rafaël Carré committed
880 881 882 883
            case ID_playlist_add:
            {
                if( (argCount < 1) || (argCount > 3) )
                    return INVOKERESULT_NO_SUCH_METHOD;
JP Dinger's avatar
JP Dinger committed
884 885
                if( !NPVARIANT_IS_STRING(args[0]) )
                    return INVOKERESULT_NO_SUCH_METHOD;
Rafaël Carré's avatar
Rafaël Carré committed
886 887

                // grab URL
888 889 890
                if( NPVARIANT_IS_NULL(args[0]) )
                    return INVOKERESULT_NO_SUCH_METHOD;

JP Dinger's avatar
JP Dinger committed
891 892 893 894 895 896 897
                char *s = stringValue(NPVARIANT_TO_STRING(args[0]));
                if( !s )
                    return INVOKERESULT_OUT_OF_MEMORY;

                char *url = p_plugin->getAbsoluteURL(s);
                if( url )
                    free(s);
Rafaël Carré's avatar
Rafaël Carré committed
898
                else
JP Dinger's avatar
JP Dinger committed
899 900
                    // problem with combining url, use argument
                    url = s;
Rafaël Carré's avatar
Rafaël Carré committed
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916

                char *name = NULL;

                // grab name if available
                if( argCount > 1 )
                {
                    if( NPVARIANT_IS_NULL(args[1]) )
                    {
                        // do nothing
                    }
                    else if( NPVARIANT_IS_STRING(args[1]) )
                    {
                        name = stringValue(NPVARIANT_TO_STRING(args[1]));
                    }
                    else
                    {
917
                        free(url);
Rafaël Carré's avatar
Rafaël Carré committed
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
                        return INVOKERESULT_INVALID_VALUE;
                    }
                }

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

                // grab options if available
                if( argCount > 2 )
                {
                    if( NPVARIANT_IS_NULL(args[2]) )
                    {
                        // do nothing
                    }
                    else if( NPVARIANT_IS_STRING(args[2]) )
                    {
934 935
                        parseOptions(NPVARIANT_TO_STRING(args[2]),
                                     &i_options, &ppsz_options);
Rafaël Carré's avatar
Rafaël Carré committed
936 937 938 939

                    }
                    else if( NPVARIANT_IS_OBJECT(args[2]) )
                    {
940 941
                        parseOptions(NPVARIANT_TO_OBJECT(args[2]),
                                     &i_options, &ppsz_options);
Rafaël Carré's avatar
Rafaël Carré committed
942 943 944
                    }
                    else
                    {
Jean-Paul Saman's avatar
Jean-Paul Saman committed
945
                        free(url);
946
                        free(name);
Rafaël Carré's avatar
Rafaël Carré committed
947 948 949 950
                        return INVOKERESULT_INVALID_VALUE;
                    }
                }

951
                int item = p_plugin->playlist_add_extended_untrusted(url, name,
952
                      i_options, const_cast<const char **>(ppsz_options));
953 954
                free(url);
                free(name);
955 956 957
                if( item == -1 )
                    RETURN_ON_ERROR;

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

964 965
                INT32_TO_NPVARIANT(item, result);
                return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
966 967 968 969
            }
            case ID_playlist_play:
                if( argCount == 0 )
                {
970
                    p_plugin->playlist_play();
971 972
                    VOID_TO_NPVARIANT(result);
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
973 974 975 976 977
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_playItem:
                if( (argCount == 1) && isNumberValue(args[0]) )
                {
978
                    p_plugin->playlist_play_item(intValue(args[0]));
979 980
                    VOID_TO_NPVARIANT(result);
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
981 982
                }
                return INVOKERESULT_NO_SUCH_METHOD;
983
            case ID_playlist_pause:
Rafaël Carré's avatar
Rafaël Carré committed
984 985
                if( argCount == 0 )
                {
Rémi Duraffort's avatar
Rémi Duraffort committed
986
                    p_plugin->playlist_pause();
987 988
                    VOID_TO_NPVARIANT(result);
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
989 990
                }
                return INVOKERESULT_NO_SUCH_METHOD;
991 992 993 994 995 996 997 998
            case ID_playlist_togglepause:
                if( argCount == 0 )
                {
                    p_plugin->playlist_togglePause();
                    VOID_TO_NPVARIANT(result);
                    return INVOKERESULT_NO_ERROR;
                }
                return INVOKERESULT_NO_SUCH_METHOD;
Rafaël Carré's avatar
Rafaël Carré committed
999 1000 1001
            case ID_playlist_stop:
                if( argCount == 0 )
                {
1002
                    p_plugin->playlist_stop();
1003 1004
                    VOID_TO_NPVARIANT(result);
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1005 1006 1007 1008 1009
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_next:
                if( argCount == 0 )
                {
1010
                    p_plugin->playlist_next();
1011 1012
                    VOID_TO_NPVARIANT(result);
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1013 1014 1015 1016 1017
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_prev:
                if( argCount == 0 )
                {
1018
                    p_plugin->playlist_prev();
1019 1020
                    VOID_TO_NPVARIANT(result);
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1021 1022 1023 1024 1025
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_clear: /* deprecated */
                if( argCount == 0 )
                {
1026
                    p_plugin->playlist_clear();
1027 1028
                    VOID_TO_NPVARIANT(result);
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1029 1030 1031 1032 1033
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            case ID_playlist_removeitem: /* deprecated */
                if( (argCount == 1) && isNumberValue(args[0]) )
                {
1034
                    if( !p_plugin->playlist_delete_item(intValue(args[0])) )
1035
                        return INVOKERESULT_GENERIC_ERROR;
1036 1037
                    VOID_TO_NPVARIANT(result);
                    return INVOKERESULT_NO_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1038 1039 1040 1041 1042 1043 1044 1045 1046
                }
                return INVOKERESULT_NO_SUCH_METHOD;
            default:
                ;
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

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

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

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

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

                    if( val > start )
                    {
                        if( nOptions == capacity )
                        {
                            capacity += 16;
1095
                            char **moreOptions = (char **)realloc(options, capacity*sizeof(char*));
Rafaël Carré's avatar
Rafaël Carré committed
1096 1097 1098
                            if( ! moreOptions )
                            {
                                /* failed to allocate more memory */
1099
                                free(s);
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
                                /* 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;
            }
1117
            free(s);
Rafaël Carré's avatar
Rafaël Carré committed
1118 1119 1120 1121
        }
    }
}

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

    NPVariant value;

    /* we are expecting to have a Javascript Array object */
    NPIdentifier propId = NPN_GetStringIdentifier("length");
    if( NPN_GetProperty(_instance, obj, propId, &value) )
    {
1134
        int count = intValue(value);
Rafaël Carré's avatar
Rafaël Carré committed
1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161
        NPN_ReleaseVariantValue(&value);

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

                while( nOptions < count )
                {
                    propId = NPN_GetIntIdentifier(nOptions);
                    if( ! NPN_GetProperty(_instance, obj, propId, &value) )
                        /* return what we got so far */
                        break;

                    if( ! NPVARIANT_IS_STRING(value) )
                    {
                        /* return what we got so far */
                        NPN_ReleaseVariantValue(&value);
                        break;
                    }

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

                    options[nOptions++] = stringValue(value);
1176
                    NPN_ReleaseVariantValue(&value);
Rafaël Carré's avatar
Rafaël Carré committed
1177 1178 1179 1180 1181 1182 1183 1184
                }
                *i_options = nOptions;
                *ppsz_options = options;
            }
        }
    }
}

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

        switch( index )
        {
            case ID_subtitle_track:
            {
1217
                /* get the current internal subtitles track ID */
1218 1219
                int actualTrack = mp.spu();
                INT32_TO_NPVARIANT(actualTrack, result);
1220 1221 1222 1223 1224
                return INVOKERESULT_NO_ERROR;
            }
            case ID_subtitle_count:
            {
                /* get the number of subtitles available */
1225
                int i_spu = mp.spuCount();
1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240
                /* return it */
                INT32_TO_NPVARIANT(i_spu, result);
                return INVOKERESULT_NO_ERROR;
            }
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

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

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

        switch( index )
        {
            case ID_subtitle_description:
            {
1290
                if (argCount == 1 && isNumberValue(args[0]))
1291
                {
1292
                    int fakeTrackIndex = intValue(args[0]);
1293 1294 1295 1296 1297
                    auto tracks = mp.spuDescription();
                    auto track = std::find_if( begin( tracks ), end( tracks ), [fakeTrackIndex](const VLC::TrackDescription& t) {
                        return t.id() == fakeTrackIndex;
                    });
                    if (track == end( tracks ) )
1298 1299
                        return INVOKERESULT_INVALID_VALUE;

1300
                    /* display the name of the track chosen */
1301
                    return invokeResultString( (*track).name().c_str(), result );
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 1356
RuntimeNPObject::InvokeResult
LibvlcVideoNPObject::getProperty(int index, NPVariant &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
                int val = p_plugin->get_fullscreen();
Rafaël Carré's avatar
Rafaël Carré committed
1371 1372 1373 1374 1375
                BOOLEAN_TO_NPVARIANT(val, result);
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_height:
            {
1376 1377 1378
                unsigned width, height;
                mp.size( 0, &width, &height );
                INT32_TO_NPVARIANT(height, result);
Rafaël Carré's avatar
Rafaël Carré committed
1379 1380 1381 1382
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_width:
            {
1383 1384 1385
                unsigned width, height;
                mp.size( 0, &width, &height );
                INT32_TO_NPVARIANT(width, result);
Rafaël Carré's avatar
Rafaël Carré committed
1386 1387 1388 1389
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_aspectratio:
            {
1390 1391
                auto ar = mp.aspectRatio();
                if( ar.empty() )
Rafaël Carré's avatar
Rafaël Carré committed
1392 1393
                    return INVOKERESULT_GENERIC_ERROR;

1394
                STRINGZ_TO_NPVARIANT(ar.c_str(), result);
Rafaël Carré's avatar
Rafaël Carré committed
1395 1396 1397 1398
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_subtitle:
            {
1399
                int i_spu = mp.spu();
Rafaël Carré's avatar
Rafaël Carré committed
1400 1401 1402 1403 1404
                INT32_TO_NPVARIANT(i_spu, result);
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_crop:
            {
1405 1406
                auto geo = mp.cropGeometry();
                if( geo.empty() )
Rafaël Carré's avatar
Rafaël Carré committed
1407 1408
                    return INVOKERESULT_GENERIC_ERROR;

1409
                STRINGZ_TO_NPVARIANT(geo.c_str(), result);
Rafaël Carré's avatar
Rafaël Carré committed
1410 1411 1412 1413
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_teletext:
            {
1414
                int i_page = mp.teletext();
1415 1416
                if( i_page < 0 )
                    return INVOKERESULT_GENERIC_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1417 1418 1419
                INT32_TO_NPVARIANT(i_page, result);
                return INVOKERESULT_NO_ERROR;
            }
1420 1421
            case ID_video_marquee:
            {
1422 1423
                InstantObj<LibvlcMarqueeNPObject>( marqueeObj );
                OBJECT_TO_NPVARIANT(NPN_RetainObject(marqueeObj), result);
1424 1425
                return INVOKERESULT_NO_ERROR;
            }
1426 1427 1428 1429 1430 1431
            case ID_video_logo:
            {
                InstantObj<LibvlcLogoNPObject>( logoObj );
                OBJECT_TO_NPVARIANT(NPN_RetainObject(logoObj), result);
                return INVOKERESULT_NO_ERROR;
            }
1432 1433 1434 1435 1436 1437
            case ID_video_deinterlace:
            {
                InstantObj<LibvlcDeinterlaceNPObject>( deintObj );
                OBJECT_TO_NPVARIANT(NPN_RetainObject(deintObj), result);
                return INVOKERESULT_NO_ERROR;
            }
Rafaël Carré's avatar
Rafaël Carré committed
1438 1439 1440 1441 1442
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

1443 1444
RuntimeNPObject::InvokeResult
LibvlcVideoNPObject::setProperty(int index, const NPVariant &value)
Rafaël Carré's avatar
Rafaël Carré committed
1445 1446
{
    /* is plugin still running */
1447
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
1448
    {
1449
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
1450 1451
        auto& mp = p_plugin->getMD();
        if( !mp )
1452
            RETURN_ON_ERROR;
Rafaël Carré's avatar
Rafaël Carré committed
1453 1454 1455 1456 1457

        switch( index )
        {
            case ID_video_fullscreen:
            {
1458
                if( ! isBoolValue(value) )
Rafaël Carré's avatar
Rafaël Carré committed
1459 1460 1461 1462
                {
                    return INVOKERESULT_INVALID_VALUE;
                }

1463
                int val = boolValue(value);
1464
                p_plugin->set_fullscreen(val);
Rafaël Carré's avatar
Rafaël Carré committed
1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481
                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_aspectratio:
            {
                char *psz_aspect = NULL;

                if( ! NPVARIANT_IS_STRING(value) )
                {
                    return INVOKERESULT_INVALID_VALUE;
                }

                psz_aspect = stringValue(NPVARIANT_TO_STRING(value));
                if( !psz_aspect )
                {
                    return INVOKERESULT_GENERIC_ERROR;
                }

1482
                mp.setAspectRatio( psz_aspect );
1483
                free(psz_aspect);
Rafaël Carré's avatar
Rafaël Carré committed
1484 1485 1486 1487 1488 1489 1490

                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_subtitle:
            {
                if( isNumberValue(value) )
                {
1491
                    mp.setSpu( intValue( value ) );
Rafaël Carré's avatar
Rafaël Carré committed
1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509
                    return INVOKERESULT_NO_ERROR;
                }
                return INVOKERESULT_INVALID_VALUE;
            }
            case ID_video_crop:
            {
                char *psz_geometry = NULL;

                if( ! NPVARIANT_IS_STRING(value) )
                {
                    return INVOKERESULT_INVALID_VALUE;
                }

                psz_geometry = stringValue(NPVARIANT_TO_STRING(value));
                if( !psz_geometry )
                {
                    return INVOKERESULT_GENERIC_ERROR;
                }
1510
                mp.setCropGeometry( psz_geometry );
1511
                free(psz_geometry);
Rafaël Carré's avatar
Rafaël Carré committed
1512 1513 1514 1515 1516 1517 1518

                return INVOKERESULT_NO_ERROR;
            }
            case ID_video_teletext:
            {
                if( isNumberValue(value) )
                {
1519
                    mp.setTeletext( intValue( value ) );
Rafaël Carré's avatar
Rafaël Carré committed
1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531
                    return INVOKERESULT_NO_ERROR;
                }
                return INVOKERESULT_INVALID_VALUE;
            }
        }
    }
    return INVOKERESULT_GENERIC_ERROR;
}

const NPUTF8 * const LibvlcVideoNPObject::methodNames[] =
{
    "toggleFullscreen",
1532
    "toggleTeletext",
Rafaël Carré's avatar
Rafaël Carré committed
1533
};
1534
COUNTNAMES(LibvlcVideoNPObject,methodCount,methodNames);
Rafaël Carré's avatar
Rafaël Carré committed
1535 1536 1537 1538

enum LibvlcVideoNPObjectMethodIds
{
    ID_video_togglefullscreen,
1539
    ID_video_toggleteletext,
Rafaël Carré's avatar
Rafaël Carré committed
1540 1541
};

1542
RuntimeNPObject::InvokeResult
1543
LibvlcVideoNPObject::invoke(int index, const NPVariant *,
1544
                            uint32_t argCount, NPVariant &result)
Rafaël Carré's avatar
Rafaël Carré committed
1545 1546
{
    /* is plugin still running */
1547
    if( isPluginRunning() )
Rafaël Carré's avatar
Rafaël Carré committed
1548
    {
1549
        VlcPluginBase* p_plugin = getPrivate<VlcPluginBase>();
Rafaël Carré's avatar
Rafaël Carré committed
1550 1551 1552 1553

        switch( index )
        {
            case ID_video_togglefullscreen:
1554
            {