EventManager.hpp 48.7 KB
Newer Older
1
/*****************************************************************************
2
 * EventManager.hpp: Exposes libvlc events
3
 *****************************************************************************
4
 * Copyright © 2015 libvlcpp authors & VideoLAN
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifndef LIBVLC_EVENTMANAGER_HPP
#define LIBVLC_EVENTMANAGER_HPP

#include <string>
27

28
#include "common.hpp"
29
#include "Internal.hpp"
30
#include "Media.hpp"
31

32
#include <algorithm>
33
#include <functional>
34
#include <vector>
35 36
#include <memory>

37 38
#define EXPECT_SIGNATURE(sig) static_assert(signature_match<decltype(f), sig>::value, "Expected a function with prototype " #sig)

39 40 41
namespace VLC
{

42 43 44
/**
 * @brief This class serves as a base for all event managers.
 *
45 46 47
 * All events can be handled by providing a std::function.
 * libvlcpp will take ownership (ie. the function will be moved inside an internal list)
 * If the provided std::function is a lambda, it may capture anything you desire
48 49
 */
class EventManager : public Internal<libvlc_event_manager_t>
50
{
51 52 53 54
protected:
    template <typename T>
    using DecayPtr = typename std::add_pointer<typename std::decay<T>::type>::type;

55 56 57 58 59
private:
    // Polymorphic base to hold a templated implementation
    struct EventHandlerBase
    {
        using Wrapper = std::add_pointer<void(const libvlc_event_t*, void*)>::type;
60 61 62 63 64 65
        virtual ~EventHandlerBase() = default;
        /**
         * @brief unregister Unregister this event handler.
         *
         * Calling this method makes the instance invalid.
         */
66
        virtual void unregister() = 0;
67 68
    };

69
    template <typename Func>
70 71
    class EventHandler : public EventHandlerBase
    {
72
    public:
73 74 75 76 77 78 79
        // We have to re-declare a template type, otherwise userCallback wouldn't be a forwarding reference
        // Not doing so works since EventHandler is instantiated with a perfect forwarded type, so Func type
        // will be a reference-less type if the callback was an lvalue, and an lvalue if the callback was an lvalue
        // As described here: http://stackoverflow.com/questions/24497311/is-this-a-universal-reference-does-stdforward-make-sense-here
        // this would still make sense, in a perverted kind of way.
        template <typename FuncTpl>
        EventHandler(EventManager& em, libvlc_event_e eventType, FuncTpl&& userCallback, Wrapper wrapper)
80
            : m_userCallback( std::forward<Func>(userCallback) )
81 82 83 84
            , m_eventManager(&em)
            , m_wrapper(wrapper)
            , m_eventType( eventType )
        {
85 86
            static_assert(std::is_same<typename std::decay<Func>::type,
                                        typename std::decay<FuncTpl>::type>::value, "");
87 88 89 90 91 92
            if (libvlc_event_attach( *m_eventManager, m_eventType, m_wrapper, &m_userCallback ) != 0)
                throw std::bad_alloc();
        }

        ~EventHandler()
        {
93 94
            // We unregister only when the object actually goes out of scope, IE. when it is
            // removed from EventManager's event handler vector
95 96 97
            libvlc_event_detach( *m_eventManager, m_eventType, m_wrapper, &m_userCallback );
        }

98 99
        virtual void unregister() override
        {
100
            m_eventManager->unregister(this);
101 102
        }

103
        EventHandler(const EventHandler&) = delete;
104
        EventHandler& operator=( const EventHandler& ) = delete;
105 106

    private:
107
        // Deduced type is Func& in case of lvalue; Func in case of rvalue.
108 109 110
        // We decay the type to ensure we either copy or take ownership.
        // Taking a reference would quite likely lead to unexpected behavior
        typename std::decay<Func>::type m_userCallback;
111 112 113 114 115 116
        // EventManager always outlive EventHandler, no need for smart pointer
        EventManager* m_eventManager;
        Wrapper m_wrapper;
        libvlc_event_e m_eventType;
    };

117 118 119 120
private:
    // variadic template recursion termination
    void unregister(){}

121
public:
122
    template <typename T, typename... Args>
123
    void unregister(const T e, const Args... args)
124
    {
125
        static_assert(std::is_convertible<decltype(e), const EventHandlerBase*>::value, "Expected const RegisteredEvent");
126 127 128

        {
            auto it = std::find_if(begin(m_lambdas), end(m_lambdas), [&e](decltype(m_lambdas)::value_type &value) {
129
                return e == value.get();
130 131 132 133 134 135
            });
            if (it != end(m_lambdas))
                m_lambdas.erase( it );
        }

        unregister(args...);
136 137
    }

138 139 140 141 142 143
protected:
    EventManager(InternalPtr ptr)
        : Internal{ ptr, [](InternalPtr){ /* No-op; EventManager's are handled by their respective objects */ } }
    {
    }

144
public:
145 146 147 148 149 150 151 152
    /**
     * @brief EventManager Wraps the same EventManager
     *
     * This will copy the underlying libvlc_event_manager_t, but will not copy
     * the already registered events.
     * This allows you to copy an event manager, register a few events for a
     * punctual task, and unregister those events by destroying the new instance.
     */
153 154 155
    EventManager(const EventManager& em)
        : Internal( em )
    {
156 157
        // Don't rely on the default implementation, as we don't want to copy the
        // current list of events.
158 159
    }

160 161 162 163 164 165 166 167
    /**
     * @brief operator= Wraps the same EventManager
     *
     * This will copy the underlying libvlc_event_manager_t, but will not copy
     * the already registered events.
     * This allows you to copy an event manager, register a few events for a
     * punctual task, and unregister those events by destroying the new instance.
     */
168 169
    EventManager& operator=(const EventManager& em)
    {
170
        // See above notes, this isn't the same as the default assignment operator
171 172
        if (this == &em)
            return *this;
173 174 175 176
        Internal::operator=(em);
        return *this;
    }

177
#if !defined(_MSC_VER) || _MSC_VER >= 1900
178 179
    EventManager(EventManager&&) = default;
    EventManager& operator=(EventManager&&) = default;
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
#else
    EventManager(EventManager&& em)
        : Internal( std::move( em ) )
        , m_lambdas(std::move( em.m_lambdas ) )
    {
    }

    EventManager& operator=(EventManager&& em)
    {
        if ( this == &em )
            return *this;
        Internal::operator=( std::move( em ) );
        m_lambdas = std::move( em.m_lambdas );
    }
#endif
195

196
    using RegisteredEvent = EventHandlerBase*;
197

198 199
protected:

200 201 202 203 204 205 206 207
    /**
     * @brief handle        Provides the common behavior for all event handlers
     * @param eventType     The libvlc type of event
     * @param f             The user provided std::function. This has to either match the expected parameter list
     *                      exactly, or it can take no arguments.
     * @param wrapper       Our implementation defined wrapper around the user's callback. It is expected to be able
     *                      able to decay to a regular C-style function pointer. It is currently implemented as a
     *                      captureless lambda (§5.1.2)
208
     * @return              A pointer to an abstract EventHandler type. It is assumed that the EventManager will
209 210 211
     *                      outlive this reference. When EventManager::~EventManager is called, it will destroy all
     *                      the registered event handler, this making this reference a dangling reference, which is
     *                      undefined behavior.
212
     *                      When calling unregister() on this object, the pointer should immediatly be considered invalid.
213
     */
214
    template <typename Func>
215
    RegisteredEvent handle(libvlc_event_e eventType, Func&& f, EventHandlerBase::Wrapper wrapper)
216
    {
217 218 219 220
        auto ptr = std::unique_ptr<EventHandlerBase>( new EventHandler<Func>(
                                *this, eventType, std::forward<Func>( f ), wrapper ) );
        auto raw = ptr.get();
        m_lambdas.push_back( std::move( ptr ) );
221
        return raw;
222 223
    }

224
    template <typename Func>
225
    RegisteredEvent handle(libvlc_event_e eventType, Func&& f)
226
    {
227 228
        EXPECT_SIGNATURE(void());
        return handle(eventType, std::forward<Func>( f ), [](const libvlc_event_t*, void* data)
229
        {
230
            auto callback = static_cast<DecayPtr<Func>>( data );
231 232 233 234 235 236 237
            (*callback)();
        });
    }

protected:
    // We store the EventHandlerBase's as unique_ptr in order for the function stored within
    // EventHandler<T> not to move to another memory location
238
    std::vector<std::unique_ptr<EventHandlerBase>> m_lambdas;
239 240
};

241 242 243
/**
 * @brief The MediaEventManager class allows one to register Media related events
 */
244
class MediaEventManager : public EventManager
245 246
{
    public:
247
        MediaEventManager(InternalPtr ptr) : EventManager( ptr ) {}
248

249
        /**
250 251
         * @brief onMetaChanged Registers an event called when a Media meta changes
         * @param f A std::function<void(libvlc_meta_t)> (or an equivalent Callable type)
252 253
         */
        template <typename Func>
254
        RegisteredEvent onMetaChanged( Func&& f)
255
        {
256 257
            EXPECT_SIGNATURE(void(libvlc_meta_t));
            return handle( libvlc_MediaMetaChanged, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
258
            {
259
                auto callback = static_cast<DecayPtr<Func>>(data);
260 261 262
                (*callback)( e->u.media_meta_changed.meta_type );
            });
        }
263

264
        /**
265 266
         * @brief onSubItemAdded Registers an event called when a Media gets a subitem added
         * @param f A std::function<void(MediaPtr)> (or an equivalent Callable type)
267 268
         */
        template <typename Func>
269
        RegisteredEvent onSubItemAdded( Func&& f )
270
        {
271 272
            EXPECT_SIGNATURE(void(MediaPtr));
            return handle(libvlc_MediaSubItemAdded, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
273
            {
274
                auto callback = static_cast<DecayPtr<Func>>(data);
275 276
                auto media = e->u.media_subitem_added.new_child;
                (*callback)( media != nullptr ? std::make_shared<Media>( media, true ) : nullptr );
277 278 279
            });
        }

280 281 282 283
        /**
         * \brief onDurationChanged Registers an event called when a media duration changes
         * \param f A std::function<void(int64_t)> (or an equivalent Callable type)
         */
284
        template <typename Func>
285
        RegisteredEvent onDurationChanged( Func&& f )
286
        {
287 288
            EXPECT_SIGNATURE(void(int64_t));
            return handle(libvlc_MediaDurationChanged, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
289
            {
290
                auto callback = static_cast<DecayPtr<Func>>(data);
291 292 293 294
                (*callback)( e->u.media_duration_changed.new_duration );
            });
        }

295 296 297 298 299
        /**
         * \brief onParsedChanged Registers an event called when the preparsed state changes
         * \param f A std::function<void(bool)> (or an equivalent Callable type)
         *          The provided boolean will be true if the media has been parsed, false otherwise.
         */
300
        template <typename Func>
301
        RegisteredEvent onParsedChanged( Func&& f )
302
        {
303 304
            EXPECT_SIGNATURE(void(bool));
            return handle( libvlc_MediaParsedChanged, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
305
            {
306
                auto callback = static_cast<DecayPtr<Func>>(data);
307 308 309 310
                (*callback)( e->u.media_parsed_changed.new_status );
            });
        }

311 312 313 314 315 316 317 318 319 320 321
        template <typename Func>
        RegisteredEvent onParsedStatus( Func&& f )
        {
            EXPECT_SIGNATURE( void(Media::ParseStatus) );
            return handle(libvlc_MediaParsedStatus, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
            {
                auto callback = static_cast<DecayPtr<Func>>(data);
                (*callback)( static_cast<Media::ParseStatus>( e->u.media_parsed_status.new_status ) );
            });
        }

322 323 324 325 326 327 328 329
        /**
         * \brief onFreed Registers an event called when the media reaches a refcount of 0
         * \param f A std::function<void(MediaPtr)> (or an equivalent Callable type)
         *          The media is being destroyed by libvlc when this event gets called.
         *          Any Media instance that would live past this call would wrap
         *          a dangling pointer.
         *
         */
330
        template <typename Func>
331
        RegisteredEvent onFreed( Func&& f)
332
        {
333
            //FIXME: Provide a read-only Media wrapper, to avoid wild dangling references to appear.
334 335
            EXPECT_SIGNATURE(void(MediaPtr));
            return handle(libvlc_MediaFreed, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
336
            {
337
                auto callback = static_cast<DecayPtr<Func>>(data);
338 339
                auto media = e->u.media_freed.md;
                (*callback)( media != nullptr ? std::make_shared<Media>( media, true ) : nullptr );
340 341 342
            });
        }

343 344 345 346
        /**
         * \brief onStateChanged Registers an event called when the Media state changes
         * \param f A std::function<void(libvlc_state_t)> (or an equivalent Callable type)
         */
347
        template <typename Func>
348
        RegisteredEvent onStateChanged( Func&& f)
349
        {
350
            //FIXME: Wrap libvlc_state_t in a class enum
351 352
            EXPECT_SIGNATURE(void(libvlc_state_t));
            return handle(libvlc_MediaStateChanged, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
353
            {
354
                auto callback = static_cast<DecayPtr<Func>>(data);
355 356 357 358
                (*callback)( e->u.media_state_changed.new_state );
            });
        }

359 360 361 362 363 364
        /**
         * \brief onSubItemTreeAdded Registers an event called when all subitem have been added.
         * \param f A std::function<void(MediaPtr)> (or an equivalent Callable type)
         *          The provided Media is the media for which this event has been registered,
         *          not a potential child media
         */
365
        template <typename Func>
366
        RegisteredEvent onSubItemTreeAdded( Func&& f)
367
        {
368
            EXPECT_SIGNATURE(void(MediaPtr));
369
            return handle(libvlc_MediaSubItemTreeAdded, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
370
            {
371
                auto callback = static_cast<DecayPtr<Func>>(data);
372 373
                auto media = e->u.media_subitemtree_added.item;
                (*callback)( media != nullptr ? std::make_shared<Media>( media, true ) : nullptr );
374 375
            });
        }
376 377
};

378 379 380
/**
 * @brief The MediaPlayerEventManager class allows one to register MediaPlayer related events
 */
381
class MediaPlayerEventManager : public EventManager
382 383
{
    public:
384 385
        MediaPlayerEventManager(InternalPtr ptr) : EventManager( ptr ) {}

386 387 388 389
        /**
         * \brief onMediaChanged Registers an event called when the played media changes
         * \param f A std::function<void(MediaPtr)> (or an equivalent Callable type)
         */
390
        template <typename Func>
391
        RegisteredEvent onMediaChanged( Func&& f )
392
        {
393 394
            EXPECT_SIGNATURE(void(MediaPtr));
            return handle(libvlc_MediaPlayerMediaChanged, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
395
            {
396
                auto callback = static_cast<DecayPtr<Func>>( data );
397 398
                auto media = e->u.media_player_media_changed.new_media;
                (*callback)( media != nullptr ? std::make_shared<Media>( media, true ) : nullptr );
399 400
            });
        }
401 402

        template <typename Func>
403
        RegisteredEvent onNothingSpecial( Func&& f )
404
        {
405
            return handle(libvlc_MediaPlayerNothingSpecial, std::forward<Func>( f ));
406 407
        }

408 409 410 411
        /**
         * \brief onOpening Registers an event called when the MediaPlayer starts initializing
         * \param f A std::function<void(void)> (or an equivalent Callable type)
         */
412
        template <typename Func>
413
        RegisteredEvent onOpening( Func&& f )
414
        {
415
            return handle(libvlc_MediaPlayerOpening, std::forward<Func>( f ) );
416 417
        }

418 419 420 421 422
        /**
         * \brief onBuffering Registers an event called when the buffering state changes
         * \param f A std::function<void(float)> (or an equivalent Callable type)
         *          The provided float is the current media buffering percentage
         */
423
        template <typename Func>
424
        RegisteredEvent onBuffering( Func&& f )
425
        {
426 427 428 429 430 431
            EXPECT_SIGNATURE(void(float));
            return handle(libvlc_MediaPlayerBuffering, std::forward<Func>(f), [](const libvlc_event_t* e, void* data)
            {
                auto callback = static_cast<DecayPtr<Func>>( data );
                (*callback)( e->u.media_player_buffering.new_cache );
            });
432 433
        }

434 435 436 437
        /**
         * \brief onPlaying Registers an event called when the media player reaches playing state
         * \param f A std::function<void(void)> (or an equivalent Callable type)
         */
438
        template <typename Func>
439
        RegisteredEvent onPlaying( Func&& f )
440
        {
441
            return handle( libvlc_MediaPlayerPlaying, std::forward<Func>( f ) );
442 443
        }

444 445 446 447
        /**
         * \brief onPaused Registers an event called when the media player gets paused
         * \param f A std::function<void(void)> (or an equivalent Callable type)
         */
448
        template <typename Func>
449
        RegisteredEvent onPaused(Func&& f)
450
        {
451
            return handle( libvlc_MediaPlayerPaused, std::forward<Func>( f ) );
452 453
        }

454 455 456 457
        /**
         * \brief onStopped Registers an event called when the media player gets stopped
         * \param f A std::function<void(void)> (or an equivalent Callable type)
         */
458
        template <typename Func>
459
        RegisteredEvent onStopped(Func&& f)
460
        {
461
            return handle( libvlc_MediaPlayerStopped, std::forward<Func>( f ) );
462 463
        }

464
        // This never gets invoked
465
        template <typename Func>
466
        RegisteredEvent onForward(Func&& f)
467
        {
468
            return handle( libvlc_MediaPlayerForward, std::forward<Func>( f ) );
469 470
        }

471
        // This never gets invoked.
472
        template <typename Func>
473
        RegisteredEvent onBackward(Func&& f)
474
        {
475
            return handle( libvlc_MediaPlayerBackward, std::forward<Func>( f ) );
476 477
        }

478 479 480 481
        /**
         * \brief onEndReached Registers an event called when the media player reaches the end of a media
         * \param f A std::function<void(void)> (or an equivalent Callable type)
         */
482
        template <typename Func>
483
        RegisteredEvent onEndReached(Func&& f)
484
        {
485
            return handle( libvlc_MediaPlayerEndReached, std::forward<Func>( f ) );
486 487
        }

488 489 490 491
        /**
         * \brief onEncounteredError Registers an event called when the media player reaches encounters an error
         * \param f A std::function<void(void)> (or an equivalent Callable type)
         */
492
        template <typename Func>
493
        RegisteredEvent onEncounteredError(Func&& f)
494
        {
495
            return handle( libvlc_MediaPlayerEncounteredError, std::forward<Func>( f ) );
496 497
        }

498 499 500 501 502
        /**
         * \brief onTimeChanged Registers an event called periodically to indicate the current playback time
         * \param f A std::function<void(int64_t)> (or an equivalent Callable type)
         *          Time is in ms.
         */
503
        template <typename Func>
504
        RegisteredEvent onTimeChanged( Func&& f )
505
        {
506 507
            EXPECT_SIGNATURE(void(libvlc_time_t));
            return handle( libvlc_MediaPlayerTimeChanged, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
508
            {
509
                auto callback = static_cast<DecayPtr<Func>>( data );
510 511 512 513
                (*callback)( e->u.media_player_time_changed.new_time );
            });
        }

514 515 516 517 518
        /**
         * \brief onPositionChanged Registers an event called periodically to indicate the current playback position.
         * \param f A std::function<void(float)> (or an equivalent Callable type)
         *          Provided float in a number between 0 (beginning of the media) and 1 (end of the media)
         */
519
        template <typename Func>
520
        RegisteredEvent onPositionChanged( Func&& f )
521
        {
522 523
            EXPECT_SIGNATURE(void(float));
            return handle( libvlc_MediaPlayerPositionChanged, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
524
            {
525
                auto callback = static_cast<DecayPtr<Func>>( data );
526 527 528 529
                (*callback)( e->u.media_player_position_changed.new_position );
            });
        }

530 531 532 533 534
        /**
         * \brief onSeekableChanged Registers an event called when the seekable state changes
         * \param f A std::function<void(bool)> (or an equivalent Callable type)
         *          The provided boolean will be true if the media is seekable, false otherwise.
         */
535
        template <typename Func>
536
        RegisteredEvent onSeekableChanged( Func&& f )
537
        {
538 539
            EXPECT_SIGNATURE(void(bool));
            return handle( libvlc_MediaPlayerSeekableChanged, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
540
            {
541
                auto callback = static_cast<DecayPtr<Func>>( data );
542
                (*callback)( e->u.media_player_seekable_changed.new_seekable != 0 );
543 544 545
            });
        }

546 547 548 549 550
        /**
         * \brief onPausableChanged Registers an event called when the pausable state changes
         * \param f A std::function<void(bool)> (or an equivalent Callable type)
         *          The provided boolean will be true if the playback can be paused, false otherwise.
         */
551
        template <typename Func>
552
        RegisteredEvent onPausableChanged( Func&& f )
553
        {
554 555
            EXPECT_SIGNATURE(void(bool));
            return handle( libvlc_MediaPlayerSeekableChanged, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
556
            {
557
                auto callback = static_cast<DecayPtr<Func>>( data );
558
                (*callback)( e->u.media_player_seekable_changed.new_seekable != 0 );
559 560 561
            });
        }

562 563 564 565 566 567 568
        /**
         * \brief onTitleChanged Registers an event called when the current title changes
         * \param f A std::function<void(int)> (or an equivalent Callable type)
         *          The provided integer is the new current title identifier.
         *          Please note that this has nothing to do with a text title (the name of
         *          the video being played, for instance)
         */
569
        template <typename Func>
570
        RegisteredEvent onTitleChanged( Func&& f )
571
        {
572 573
            EXPECT_SIGNATURE(void(int));
            return handle( libvlc_MediaPlayerTitleChanged, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
574
            {
575
                auto callback = static_cast<DecayPtr<Func>>( data );
576 577 578 579
                (*callback)( e->u.media_player_title_changed.new_title );
            });
        }

580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
#if LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)
        /**
         * \brief onChapterChanged Registers an event called when the current chapter changes
         * \param f A std::function<void(int)> (or an equivalent Callable type)
         *          The provided integer is the new current chapter identifier.
         *          Please note that this has nothing to do with a text chapter (the name of
         *          the video being played, for instance)
         */
        template <typename Func>
        RegisteredEvent onChapterChanged( Func&& f )
        {
            EXPECT_SIGNATURE(void(int));
            return handle( libvlc_MediaPlayerChapterChanged, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
            {
                auto callback = static_cast<DecayPtr<Func>>( data );
                (*callback)( e->u.media_player_chapter_changed.new_chapter );
            });
        }
#endif

600 601 602 603 604
        /**
         * \brief onSnapshotTaken Registers an event called when a snapshot gets taken
         * \param f A std::function<void(std::string)> (or an equivalent Callable type)
         *          The provided string is the path of the created snapshot
         */
605
        template <typename Func>
606
        RegisteredEvent onSnapshotTaken( Func&& f )
607
        {
608 609
            EXPECT_SIGNATURE(void(std::string));
            return handle( libvlc_MediaPlayerSnapshotTaken, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
610
            {
611
                auto callback = static_cast<DecayPtr<Func>>( data );
612 613 614 615
                (*callback)( e->u.media_player_snapshot_taken.psz_filename );
            });
        }

616 617 618 619
        /**
         * \brief onLengthChanged Registers an event called when the length gets updated.
         * \param f A std::function<void(int64_t)> (or an equivalent Callable type)
         */
620
        template <typename Func>
621
        RegisteredEvent onLengthChanged( Func&& f )
622
        {
623 624
            EXPECT_SIGNATURE(void(libvlc_time_t));
            return handle( libvlc_MediaPlayerLengthChanged, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
625
            {
626
                auto callback = static_cast<DecayPtr<Func>>( data );
627 628 629 630
                (*callback)( e->u.media_player_length_changed.new_length );
            });
        }

631 632 633 634 635
        /**
         * \brief onVout Registers an event called when the number of vout changes
         * \param f A std::function<void(int)> (or an equivalent Callable type)
         *          The provided int represents the number of currently available vout.
         */
636
        template <typename Func>
637
        RegisteredEvent onVout( Func&& f )
638
        {
639 640
            EXPECT_SIGNATURE(void(int));
            return handle( libvlc_MediaPlayerVout, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
641
            {
642
                auto callback = static_cast<DecayPtr<Func>>( data );
643 644 645 646
                (*callback)( e->u.media_player_vout.new_count );
            });
        }

647 648 649 650 651
        /**
         * \brief onScrambledChanged Registers an event called when the scrambled state changes
         * \param f A std::function<void(bool)> (or an equivalent Callable type)
         *          The provided boolean will be true if the media is scrambled, false otherwise.
         */
652
        template <typename Func>
653
        RegisteredEvent onScrambledChanged( Func&& f )
654
        {
655
            EXPECT_SIGNATURE(void(bool));
656
            return handle( libvlc_MediaPlayerScrambledChanged, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
657
            {
658
                auto callback = static_cast<DecayPtr<Func>>( data );
659
                (*callback)( e->u.media_player_scrambled_changed.new_scrambled != 0 );
660 661 662
            });
        }

663
#if LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)
664 665 666 667 668 669
        /**
         * \brief onESAdded Registers an event called when an elementary stream get added
         * \param f A std::function<void(libvlc_track_type_t, int)> (or an equivalent Callable type)
         *          libvlc_track_type_t: The new track type
         *          int: the new track index
         */
670
        template <typename Func>
671
        RegisteredEvent onESAdded( Func&& f )
672
        {
673
            //FIXME: Expose libvlc_track_type_t as an enum class
674 675
            EXPECT_SIGNATURE(void(libvlc_track_type_t, int));
            return handle( libvlc_MediaPlayerESAdded, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
676
            {
677
                auto callback = static_cast<DecayPtr<Func>>( data );
678 679 680 681
                (*callback)( e->u.media_player_es_changed.i_type, e->u.media_player_es_changed.i_id );
            });
        }

682 683 684 685 686 687
        /**
         * \brief onESDeleted Registers an event called when an elementary stream get deleted
         * \param f A std::function<void(libvlc_track_type_t, int)> (or an equivalent Callable type)
         *          libvlc_track_type_t: The track type
         *          int: the track index
         */
688
        template <typename Func>
689
        RegisteredEvent onESDeleted( Func&& f )
690
        {
691 692
            EXPECT_SIGNATURE(void(libvlc_track_type_t, int));
            return handle( libvlc_MediaPlayerESDeleted, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
693
            {
694
                auto callback = static_cast<DecayPtr<Func>>( data );
695 696 697 698
                (*callback)( e->u.media_player_es_changed.i_type, e->u.media_player_es_changed.i_id );
            });
        }

699 700 701 702 703 704
        /**
         * \brief onESSelected Registers an event called when an elementary stream get selected
         * \param f A std::function<void(libvlc_track_type_t, int)> (or an equivalent Callable type)
         *          libvlc_track_type_t: The track type
         *          int: the track index
         */
705
        template <typename Func>
706
        RegisteredEvent onESSelected( Func&& f )
707
        {
708 709
            EXPECT_SIGNATURE(void(libvlc_track_type_t, int));
            return handle( libvlc_MediaPlayerESSelected, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
710
            {
711
                auto callback = static_cast<DecayPtr<Func>>( data );
712 713 714
                (*callback)( e->u.media_player_es_changed.i_type, e->u.media_player_es_changed.i_id );
            });
        }
715 716 717 718

        /**
         * \brief onAudioDevice Registers an event called when the current audio output device changes
         * \param f A std::function<void(std::string)> (or an equivalent Callable type)
719
         *          The provided string is the new current audio device.
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
         */
        template <typename Func>
        RegisteredEvent onAudioDevice( Func&& f )
        {
            EXPECT_SIGNATURE(void(std::string));
            return handle( libvlc_MediaPlayerAudioDevice, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
            {
                auto callback = static_cast<DecayPtr<Func>>( data );
                (*callback)( e->u.media_player_audio_device.device );
            });
        }
#endif

#if LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 2, 0)
        /**
         * \brief onCorked Registers an event called when the playback is paused automatically for a higher priority audio stream
         * \param f A std::function<void(void)> (or an equivalent Callable type)
         */
        template <typename Func>
        RegisteredEvent onCorked( Func&& f )
        {
            return handle( libvlc_MediaPlayerCorked, std::forward<Func>( f ) );
        }

        /**
         * \brief onUncorked Registers an event called when the playback is unpaused automatically after a higher priority audio stream ends
         * \param f A std::function<void(void)> (or an equivalent Callable type)
         */
        template <typename Func>
        RegisteredEvent onUncorked( Func&& f )
        {
            return handle( libvlc_MediaPlayerUncorked, std::forward<Func>( f ) );
        }

        /**
         * \brief onMuted Registers an event called when the audio is muted
         * \param f A std::function<void(void)> (or an equivalent Callable type)
         */
        template <typename Func>
        RegisteredEvent onMuted( Func&& f )
        {
            return handle( libvlc_MediaPlayerMuted, std::forward<Func>( f ) );
        }

        /**
         * \brief onUnmuted Registers an event called when the audio is unmuted
         * \param f A std::function<void(void)> (or an equivalent Callable type)
         */
        template <typename Func>
        RegisteredEvent onUnmuted( Func&& f )
        {
            return handle( libvlc_MediaPlayerUnmuted, std::forward<Func>( f ) );
        }

        /**
         * \brief onAudioVolume Registers an event called when the current audio volume changes
         * \param f A std::function<void(float)> (or an equivalent Callable type)
         *          The provided float is the new current audio volume percentage.
         */
        template <typename Func>
        RegisteredEvent onAudioVolume( Func&& f )
        {
            EXPECT_SIGNATURE(void(float));
            return handle( libvlc_MediaPlayerAudioVolume, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
            {
                auto callback = static_cast<DecayPtr<Func>>( data );
                (*callback)( e->u.media_player_audio_volume.volume );
            });
        }
789
#endif
790 791
};

792 793 794
/**
 * @brief The MediaListEventManager class allows one to register MediaList related events
 */
795
class MediaListEventManager : public EventManager
796 797
{
    public:
798 799
        MediaListEventManager(InternalPtr ptr) : EventManager( ptr ) {}

800 801 802 803 804 805
        /**
         * \brief onItemAdded Registers an event called when an item gets added to the media list
         * \param f A std::function<void(MediaPtr, int)> (or an equivalent Callable type)
         *          MediaPtr: The added media
         *          int: The media index in the list
         */
806
        template <typename Func>
807
        RegisteredEvent onItemAdded( Func&& f )
808
        {
809 810
            EXPECT_SIGNATURE(void(MediaPtr, int));
            return handle(libvlc_MediaListItemAdded, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
811
            {
812
                auto callback = static_cast<DecayPtr<Func>>( data );
813 814 815
                auto media = e->u.media_list_item_added.item;
                (*callback)( media != nullptr ? std::make_shared<Media>( media, true ) : nullptr,
                             e->u.media_list_item_added.index );
816 817 818
            });
        }

819 820 821 822 823 824
        /**
         * \brief onWillAddItem Registers an event called when an item is about to be added to the media list
         * \param f A std::function<void(MediaPtr, int)> (or an equivalent Callable type)
         *          MediaPtr: The added media
         *          int: The media index in the list
         */
825
        template <typename Func>
826
        RegisteredEvent onWillAddItem( Func&& f )
827
        {
828 829
            EXPECT_SIGNATURE(void(MediaPtr, int));
            return handle( libvlc_MediaListWillAddItem, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
830
            {
831
                auto callback = static_cast<DecayPtr<Func>>( data );
832
                auto media = e->u.media_list_will_add_item.item;
833
                (*callback)( media != nullptr ? std::make_shared<Media>( media, true ) : nullptr,
834
                            e->u.media_list_will_add_item.index );
835 836 837
            });
        }

838 839 840 841 842 843
        /**
         * \brief onItemDeleted Registers an event called when an item gets deleted to the media list
         * \param f A std::function<void(MediaPtr, int)> (or an equivalent Callable type)
         *          MediaPtr: The added media
         *          int: The media index in the list
         */
844
        template <typename Func>
845
        RegisteredEvent onItemDeleted( Func&& f )
846
        {
847 848
            EXPECT_SIGNATURE(void(MediaPtr, int));
            return handle(libvlc_MediaListItemDeleted, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
849
            {
850
                auto callback = static_cast<DecayPtr<Func>>( data );
851 852 853
                auto media = e->u.media_list_item_deleted.item;
                (*callback)( media != nullptr ? std::make_shared<Media>( media, true ) : nullptr,
                             e->u.media_list_item_deleted.index );
854 855 856
            });
        }

857 858 859 860 861 862
        /**
         * \brief onWillDeleteItem Registers an event called when an item is about to be deleted
         * \param f A std::function<void(MediaPtr, int)> (or an equivalent Callable type)
         *          MediaPtr: The added media
         *          int: The media index in the list
         */
863
        template <typename Func>
864
        RegisteredEvent onWillDeleteItem( Func&& f )
865
        {
866 867
            EXPECT_SIGNATURE(void(MediaPtr, int));
            return handle(libvlc_MediaListWillDeleteItem, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
868
            {
869
                auto callback = static_cast<DecayPtr<Func>>( data );
870 871 872
                auto media = e->u.media_list_will_delete_item.item;
                (*callback)( media != nullptr ? std::make_shared<Media>( media, true ) : nullptr,
                             e->u.media_list_will_delete_item.index );
873 874
            });
        }
875 876 877 878 879 880

        template <typename Func>
        RegisteredEvent onEndReached( Func&& f )
        {
            return handle( libvlc_MediaListEndReached, std::forward<Func>( f ) );
        }
881 882
};

883 884 885 886
/**
 * @brief The MediaListPlayerEventManager class
 * Those events aren't sent by VLC so far.
 */
887
class MediaListPlayerEventManager : public EventManager
888 889
{
    public:
890 891
        MediaListPlayerEventManager(InternalPtr ptr) : EventManager( ptr ) {}

892
        template <typename Func>
893
        RegisteredEvent onPlayed(Func&& f)
894
        {
895
            return handle(libvlc_MediaListPlayerPlayed, std::forward<Func>( f ) );
896 897
        }

898
        template <typename Func>
899
        RegisteredEvent onNextItemSet( Func&& f )
900
        {
901 902
            EXPECT_SIGNATURE(void(MediaPtr));
            return handle(libvlc_MediaListPlayerNextItemSet, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
903
            {
904
                auto callback = static_cast<DecayPtr<Func>>( data );
905 906
                auto media = e->u.media_list_player_next_item_set.item;
                (*callback)( media != nullptr ? std::make_shared<Media>( media, true ) : nullptr );
907
            });
908 909
        }

910
        template <typename Func>
911
        RegisteredEvent onStopped( Func&& f )
912
        {
913
            return handle(libvlc_MediaListPlayerStopped, std::forward<Func>( f ) );
914
        }
915
};
916

917 918 919
/**
 * @brief The MediaDiscovererEventManager class allows one to register MediaDiscoverer related events
 */
920 921 922 923 924
class MediaDiscovererEventManager : public EventManager
{
    public:
        MediaDiscovererEventManager(InternalPtr ptr) : EventManager( ptr ) {}

925 926 927 928
        /**
         * \brief onStarted Registers an event called when the discovery starts
         * \param f A std::function<void(void)> (or an equivalent Callable type)
         */
929
        template <typename Func>
930
        RegisteredEvent onStarted(Func&& f)
931
        {
932
            return handle(libvlc_MediaDiscovererStarted, std::forward<Func>( f ) );
933 934
        }

935 936 937 938
        /**
         * \brief onStopped Registers an event called when the discovery stops
         * \param f A std::function<void(void)> (or an equivalent Callable type)
         */
939
        template <typename Func>
940
        RegisteredEvent onStopped(Func&& f)
941
        {
942
            return handle(libvlc_MediaDiscovererEnded, std::forward<Func>( f ) );
943
        }
944
};
945

946 947 948
/**
 * @brief The VLMEventManager class allows one to register VLM related events
 */
949 950 951
class VLMEventManager : public EventManager
{
    public:
952
        VLMEventManager(InternalPtr ptr) : EventManager(ptr) {}
953

954 955 956 957 958
        /**
         * \brief onMediaAdded Registers an event called when a media gets added
         * \param f A std::function<void(std::string)> (or an equivalent Callable type)
         *          The given string is the name of the added media
         */
959
        template <typename Func>
960
        RegisteredEvent onMediaAdded( Func&& f )
961
        {
962 963
            EXPECT_SIGNATURE(void(std::string));
            return handle(libvlc_VlmMediaAdded, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
964
            {
965
                auto callback = static_cast<DecayPtr<Func>>( data );
966 967
                (*callback)( e->u.vlm_media_event.psz_media_name ? e->u.vlm_media_event.psz_media_name : "" );
            });
968 969
        }

970 971 972 973 974
        /**
         * \brief onMediaRemoved Registers an event called when a media gets removed
         * \param f A std::function<void(std::string)> (or an equivalent Callable type)
         *          The given string is the name of the removed media
         */
975
        template <typename Func>
976
        RegisteredEvent onMediaRemoved( Func&& f )
977
        {
978 979
            EXPECT_SIGNATURE(void(std::string));
            return handle(libvlc_VlmMediaRemoved, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
980
            {
981
                auto callback = static_cast<DecayPtr<Func>>( data );
982 983
                (*callback)( e->u.vlm_media_event.psz_media_name ? e->u.vlm_media_event.psz_media_name : "" );
            });
984 985
        }

986 987 988 989 990
        /**
         * \brief onMediaChanged Registers an event called when a media changes
         * \param f A std::function<void(std::string)> (or an equivalent Callable type)
         *          The given string is the name of the changed media
         */
991
        template <typename Func>
992
        RegisteredEvent onMediaChanged( Func&& f )
993
        {
994 995
            EXPECT_SIGNATURE(void(std::string));
            return handle(libvlc_VlmMediaChanged, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
996
            {
997
                auto callback = static_cast<DecayPtr<Func>>( data );
998 999 1000
                (*callback)( e->u.vlm_media_event.psz_media_name ? e->u.vlm_media_event.psz_media_name : "" );
            });
        }
1001

1002 1003 1004 1005 1006 1007 1008
        /**
         * \brief onMediaInstanceStarted Registers an event called when a media instance starts
         * \param f A std::function<void(std::string, std::string)> (or an equivalent Callable type)
         *          Parameters are:
         *              - The media name
         *              - The instance name
         */
1009
        template <typename Func>
1010
        RegisteredEvent onMediaInstanceStarted( Func&& f )
1011
        {
1012 1013
            EXPECT_SIGNATURE(void(std::string, std::string));
            return handle(libvlc_VlmMediaInstanceStarted, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
1014
            {
1015
                auto callback = static_cast<DecayPtr<Func>>( data );
1016 1017 1018
                (*callback)( e->u.vlm_media_event.psz_media_name ? e->u.vlm_media_event.psz_media_name : "",
                             e->u.vlm_media_event.psz_instance_name ? e->u.vlm_media_event.psz_instance_name : "" );
            });
1019 1020
        }

1021 1022 1023 1024 1025 1026 1027
        /**
         * \brief onMediaInstanceStopped Registers an event called when a media instance stops
         * \param f A std::function<void(std::string, std::string)> (or an equivalent Callable type)
         *          Parameters are:
         *              - The media name
         *              - The instance name
         */
1028
        template <typename Func>
1029
        RegisteredEvent onMediaInstanceStopped( Func&& f )
1030
        {
1031 1032
            EXPECT_SIGNATURE(void(std::string, std::string));
            return handle(libvlc_VlmMediaInstanceStopped, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
1033
            {
1034
                auto callback = static_cast<DecayPtr<Func>>( data );
1035 1036 1037
                (*callback)( e->u.vlm_media_event.psz_media_name ? e->u.vlm_media_event.psz_media_name : "",
                             e->u.vlm_media_event.psz_instance_name ? e->u.vlm_media_event.psz_instance_name : "" );
            });
1038 1039
        }

1040 1041 1042 1043 1044 1045 1046
        /**
         * \brief onMediaInstanceStatusInit Registers an event called when a media instance is initializing
         * \param f A std::function<void(std::string, std::string)> (or an equivalent Callable type)
         *          Parameters are:
         *              - The media name
         *              - The instance name
         */
1047
        template <typename Func>
1048
        RegisteredEvent onMediaInstanceStatusInit( Func&& f )
1049
        {
1050 1051
            EXPECT_SIGNATURE(void(std::string, std::string));
            return handle(libvlc_VlmMediaInstanceStatusInit, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
1052
            {
1053
                auto callback = static_cast<DecayPtr<Func>>( data );
1054 1055 1056
                (*callback)( e->u.vlm_media_event.psz_media_name ? e->u.vlm_media_event.psz_media_name : "",
                             e->u.vlm_media_event.psz_instance_name ? e->u.vlm_media_event.psz_instance_name : "" );
            });
1057 1058
        }

1059 1060 1061 1062 1063 1064 1065
        /**
         * \brief onMediaInstanceStatusOpening Registers an event called when a media instance is opening
         * \param f A std::function<void(std::string, std::string)> (or an equivalent Callable type)
         *          Parameters are:
         *              - The media name
         *              - The instance name
         */
1066
        template <typename Func>
1067
        RegisteredEvent onMediaInstanceStatusOpening( Func&& f )
1068
        {
1069 1070
            EXPECT_SIGNATURE(void(std::string, std::string));
            return handle(libvlc_VlmMediaInstanceStatusOpening, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
1071
            {
1072
                auto callback = static_cast<DecayPtr<Func>>( data );
1073 1074 1075
                (*callback)( e->u.vlm_media_event.psz_media_name ? e->u.vlm_media_event.psz_media_name : "",
                             e->u.vlm_media_event.psz_instance_name ? e->u.vlm_media_event.psz_instance_name : "" );
            });
1076 1077
        }

1078 1079 1080 1081 1082 1083 1084
        /**
         * \brief onMediaInstanceStatusPlaying Registers an event called when a media instance reaches playing state
         * \param f A std::function<void(std::string, std::string)> (or an equivalent Callable type)
         *          Parameters are:
         *              - The media name
         *              - The instance name
         */
1085
        template <typename Func>
1086
        RegisteredEvent onMediaInstanceStatusPlaying( Func&& f )
1087
        {
1088 1089
            EXPECT_SIGNATURE(void(std::string, std::string));
            return handle(libvlc_VlmMediaInstanceStatusPlaying, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
1090
            {
1091
                auto callback = static_cast<DecayPtr<Func>>( data );
1092 1093 1094
                (*callback)( e->u.vlm_media_event.psz_media_name ? e->u.vlm_media_event.psz_media_name : "",
                             e->u.vlm_media_event.psz_instance_name ? e->u.vlm_media_event.psz_instance_name : "" );
            });
1095 1096
        }

1097 1098 1099 1100 1101 1102 1103
        /**
         * \brief onMediaInstanceStatusPause Registers an event called when a media instance gets paused
         * \param f A std::function<void(std::string, std::string)> (or an equivalent Callable type)
         *          Parameters are:
         *              - The media name
         *              - The instance name
         */
1104
        template <typename Func>
1105
        RegisteredEvent onMediaInstanceStatusPause( Func&& f )
1106
        {
1107 1108
            EXPECT_SIGNATURE(void(std::string, std::string));
            return handle(libvlc_VlmMediaInstanceStatusPause, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
1109
            {
1110
                auto callback = static_cast<DecayPtr<Func>>( data );
1111 1112 1113
                (*callback)( e->u.vlm_media_event.psz_media_name ? e->u.vlm_media_event.psz_media_name : "",
                             e->u.vlm_media_event.psz_instance_name ? e->u.vlm_media_event.psz_instance_name : "" );
            });
1114
        }
1115

1116 1117 1118 1119 1120 1121 1122
        /**
         * \brief onMediaInstanceStatusEnd Registers an event called when a media instance ends
         * \param f A std::function<void(std::string, std::string)> (or an equivalent Callable type)
         *          Parameters are:
         *              - The media name
         *              - The instance name
         */
1123
        template <typename Func>
1124
        RegisteredEvent onMediaInstanceStatusEnd( Func&& f )
1125
        {
1126 1127
            EXPECT_SIGNATURE(void(std::string, std::string));
            return handle(libvlc_VlmMediaInstanceStatusEnd, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
1128
            {
1129
                auto callback = static_cast<DecayPtr<Func>>( data );
1130 1131 1132
                (*callback)( e->u.vlm_media_event.psz_media_name ? e->u.vlm_media_event.psz_media_name : "",
                             e->u.vlm_media_event.psz_instance_name ? e->u.vlm_media_event.psz_instance_name : "" );
            });
1133
        }
1134

1135 1136 1137 1138 1139 1140 1141
        /**
         * \brief onMediaInstanceStatusError Registers an event called when a media instance encouters an error
         * \param f A std::function<void(std::string, std::string)> (or an equivalent Callable type)
         *          Parameters are:
         *              - The media name
         *              - The instance name
         */
1142
        template <typename Func>
1143
        RegisteredEvent onMediaInstanceStatusError( Func&& f )
1144
        {
1145 1146
            EXPECT_SIGNATURE(void(std::string, std::string));
            return handle(libvlc_VlmMediaInstanceStatusError, std::forward<Func>( f ), [](const libvlc_event_t* e, void* data)
1147
            {
1148
                auto callback = static_cast<DecayPtr<Func>>( data );
1149 1150 1151 1152