Package uk.co.caprica.vlcj.player.list

Source Code of uk.co.caprica.vlcj.player.list.DefaultMediaListPlayer

/*
* This file is part of VLCJ.
*
* VLCJ 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 3 of the License, or
* (at your option) any later version.
*
* VLCJ 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.
*
* You should have received a copy of the GNU General Public License
* along with VLCJ.  If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2009, 2010, 2011, 2012, 2013, 2014 Caprica Software Limited.
*/

package uk.co.caprica.vlcj.player.list;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

import uk.co.caprica.vlcj.binding.LibVlc;
import uk.co.caprica.vlcj.binding.internal.libvlc_callback_t;
import uk.co.caprica.vlcj.binding.internal.libvlc_event_e;
import uk.co.caprica.vlcj.binding.internal.libvlc_event_manager_t;
import uk.co.caprica.vlcj.binding.internal.libvlc_event_t;
import uk.co.caprica.vlcj.binding.internal.libvlc_instance_t;
import uk.co.caprica.vlcj.binding.internal.libvlc_media_list_player_t;
import uk.co.caprica.vlcj.binding.internal.libvlc_media_t;
import uk.co.caprica.vlcj.binding.internal.libvlc_playback_mode_e;
import uk.co.caprica.vlcj.logger.Logger;
import uk.co.caprica.vlcj.medialist.MediaList;
import uk.co.caprica.vlcj.player.AbstractMediaPlayer;
import uk.co.caprica.vlcj.player.MediaPlayer;
import uk.co.caprica.vlcj.player.NativeString;
import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer;
import uk.co.caprica.vlcj.player.list.events.MediaListPlayerEvent;
import uk.co.caprica.vlcj.player.list.events.MediaListPlayerEventFactory;
import uk.co.caprica.vlcj.player.list.events.MediaListPlayerEventType;

import com.sun.jna.Pointer;

/**
* Implementation of a media list player.
*/
public class DefaultMediaListPlayer extends AbstractMediaPlayer implements MediaListPlayer {

    /**
     * Collection of event listeners.
     */
    private final List<MediaListPlayerEventListener> eventListenerList = new ArrayList<MediaListPlayerEventListener>();

    /**
     * Factory to create media player events from native events.
     */
    private final MediaListPlayerEventFactory eventFactory = new MediaListPlayerEventFactory(this);

    /**
     * Background service to notify event listeners.
     */
    private final ExecutorService listenersService = Executors.newSingleThreadExecutor();

    /**
     * Native media player instance.
     */
    private libvlc_media_list_player_t mediaListPlayerInstance;

    /**
     * Native event manager instance.
     */
    private libvlc_event_manager_t mediaListPlayerEventManager;

    /**
     * Native event call-back.
     */
    private libvlc_callback_t callback;

    /**
     * Associated native media player instance.
     * <p>
     * This may be <code>null</code>.
     */
    private MediaPlayer mediaPlayer;

    /**
     * Mask of the native events that will cause notifications to be sent to listeners.
     */
    private int eventMask = MediaListPlayerEventType.ALL.value();

    /**
     * Media list.
     */
    private MediaList mediaList;

    /**
     * Opaque reference to user/application-specific data associated with this media player.
     */
    private Object userData;

    /**
     * Set to true when the player has been released.
     */
    private final AtomicBoolean released = new AtomicBoolean();

    /**
     * Create a new media list player.
     *
     * @param libvlc native library interface
     * @param instance libvlc instance
     */
    public DefaultMediaListPlayer(LibVlc libvlc, libvlc_instance_t instance) {
        super(libvlc, instance);
        Logger.debug("DefaultMediaListPlayer(libvlc={}, instance={})", libvlc, instance);
        createInstance();
    }

    @Override
    public void addMediaListPlayerEventListener(MediaListPlayerEventListener listener) {
        Logger.debug("addMediaPlayerEventListener(listener={})", listener);
        eventListenerList.add(listener);
    }

    @Override
    public void removeMediaListPlayerEventListener(MediaListPlayerEventListener listener) {
        Logger.debug("removeMediaPlayerEventListener(listener={})", listener);
        eventListenerList.remove(listener);
    }

    @Override
    public void enableEvents(int eventMask) {
        Logger.debug("enableEvents(eventMask={})", eventMask);
        this.eventMask = eventMask;
    }

    @Override
    public void setMediaPlayer(MediaPlayer mediaPlayer) {
        Logger.debug("setMediaPlayer(mediaPlayer={})", mediaPlayer);
        this.mediaPlayer = mediaPlayer;
        libvlc.libvlc_media_list_player_set_media_player(mediaListPlayerInstance, mediaPlayer.mediaPlayerInstance());
    }

    @Override
    public void setMediaList(MediaList mediaList) {
        Logger.debug("setMediaList(mediaList={})", mediaList);
        libvlc.libvlc_media_list_player_set_media_list(mediaListPlayerInstance, mediaList.mediaListInstance());
        this.mediaList = mediaList;
    }

    @Override
    public MediaList getMediaList() {
        Logger.debug("getMediaList()");
        return mediaList;
    }

    @Override
    public void play() {
        Logger.debug("play()");
        // If there is an associated media player then make sure the video surface
        // is attached
        if(mediaPlayer instanceof EmbeddedMediaPlayer) {
            ((EmbeddedMediaPlayer)mediaPlayer).attachVideoSurface();
        }
        libvlc.libvlc_media_list_player_play(mediaListPlayerInstance);
    }

    @Override
    public void pause() {
        Logger.debug("pause()");
        libvlc.libvlc_media_list_player_pause(mediaListPlayerInstance);
    }

    @Override
    public void stop() {
        Logger.debug("stop()");
        libvlc.libvlc_media_list_player_stop(mediaListPlayerInstance);
    }

    @Override
    public boolean playItem(int itemIndex) {
        Logger.debug("playItem(itemIndex={})", itemIndex);
        if(mediaList != null && itemIndex >= 0 && itemIndex < mediaList.size()) {
            return libvlc.libvlc_media_list_player_play_item_at_index(mediaListPlayerInstance, itemIndex) == 0;
        }
        else {
            throw new IllegalArgumentException("Item index is out of bounds");
        }
    }

    @Override
    public void playNext() {
        Logger.debug("playNext()");
        libvlc.libvlc_media_list_player_next(mediaListPlayerInstance);
    }

    @Override
    public void playPrevious() {
        Logger.debug("playPrevious()");
        libvlc.libvlc_media_list_player_previous(mediaListPlayerInstance);
    }

    @Override
    public boolean isPlaying() {
        Logger.debug("isPlaying()");
        return libvlc.libvlc_media_list_player_is_playing(mediaListPlayerInstance) != 0;
    }

    @Override
    public void setMode(MediaListPlayerMode mode) {
        Logger.debug("setMode(mode={})", mode);
        libvlc_playback_mode_e playbackMode;
        switch(mode) {
            case DEFAULT:
                playbackMode = libvlc_playback_mode_e.libvlc_playback_mode_default;
                break;

            case LOOP:
                playbackMode = libvlc_playback_mode_e.libvlc_playback_mode_loop;
                break;

            case REPEAT:
                playbackMode = libvlc_playback_mode_e.libvlc_playback_mode_repeat;
                break;

            default:
                throw new IllegalArgumentException("Invalid mode " + mode);
        }
        libvlc.libvlc_media_list_player_set_playback_mode(mediaListPlayerInstance, playbackMode.intValue());
    }

    @Override
    public String mrl(libvlc_media_t mediaInstance) {
        Logger.debug("mrl(mediaInstance={})", mediaInstance);
        return NativeString.getNativeString(libvlc, libvlc.libvlc_media_get_mrl(mediaInstance));
    }

    @Override
    public Object userData() {
        Logger.debug("userData()");
        return userData;
    }

    @Override
    public void userData(Object userData) {
        Logger.debug("userData(userData={})", userData);
        this.userData = userData;
    }

    @Override
    public final void release() {
        Logger.debug("release()");
        if(released.compareAndSet(false, true)) {
            destroyInstance();
        }
    }

    /**
     *
     */
    private void createInstance() {
        Logger.debug("createInstance()");

        mediaListPlayerInstance = libvlc.libvlc_media_list_player_new(instance);

        mediaListPlayerEventManager = libvlc.libvlc_media_list_player_event_manager(mediaListPlayerInstance);
        Logger.debug("mediaListPlayerEventManager={}", mediaListPlayerEventManager);

        registerEventListener();

        addMediaListPlayerEventListener(new NextItemHandler());
    }

    /**
     *
     */
    private void destroyInstance() {
        Logger.debug("destroyInstance()");

        Logger.debug("Detach events...");
        deregisterEventListener();
        Logger.debug("Events detached.");

        eventListenerList.clear();
        if(mediaListPlayerInstance != null) {
            Logger.debug("Release media list player...");
            libvlc.libvlc_media_list_player_release(mediaListPlayerInstance);
            Logger.debug("Media list player released");
        }

        Logger.debug("Shut down listeners...");
        listenersService.shutdown();
        Logger.debug("Listeners shut down.");
    }

    /**
     *
     */
    private void registerEventListener() {
        Logger.debug("registerEventListener()");
        callback = new VlcVideoPlayerCallback();
        for(libvlc_event_e event : libvlc_event_e.values()) {
            // The native event manager reports that it does not support
            // libvlc_MediaListPlayerPlayed
            if(event.intValue() >= libvlc_event_e.libvlc_MediaListPlayerNextItemSet.intValue() && event.intValue() <= libvlc_event_e.libvlc_MediaListPlayerStopped.intValue()) {
                Logger.debug("event={}", event);
                int result = libvlc.libvlc_event_attach(mediaListPlayerEventManager, event.intValue(), callback, null);
                Logger.debug("result={}", result);
            }
        }
    }

    /**
     *
     */
    private void deregisterEventListener() {
        Logger.debug("deregisterEventListener()");
        if(callback != null) {
            for(libvlc_event_e event : libvlc_event_e.values()) {
                // The native event manager reports that it does not support
                // libvlc_MediaListPlayerPlayed
                if(event.intValue() >= libvlc_event_e.libvlc_MediaListPlayerNextItemSet.intValue() && event.intValue() <= libvlc_event_e.libvlc_MediaListPlayerStopped.intValue()) {
                    Logger.debug("event={}", event);
                    libvlc.libvlc_event_detach(mediaListPlayerEventManager, event.intValue(), callback, null);
                }
            }
            callback = null;
        }
    }

    /**
     * A call-back to handle events from the native media player.
     * <p>
     * There are some important implementation details for this callback:
     * <ul>
     * <li>First, the event notifications are off-loaded to a different thread so as to prevent
     * application code re-entering libvlc in an event call-back which may lead to a deadlock in the
     * native code;</li>
     * <li>Second, the native event union structure refers to natively allocated memory which will
     * not be in the scope of the thread used to actually dispatch the event notifications.</li>
     * </ul>
     * Without copying the fields at this point from the native union structure, the native memory
     * referred to by the native event is likely to get deallocated and overwritten by the time the
     * notification thread runs. This would lead to unreliable data being sent with the
     * notification, or even a fatal JVM crash.
     */
    private final class VlcVideoPlayerCallback implements libvlc_callback_t {

        @Override
        public void callback(libvlc_event_t event, Pointer userData) {
            Logger.trace("callback(event={},userData={})", event, userData);
            if(!eventListenerList.isEmpty()) {
                // Create a new media player event for the native event
                MediaListPlayerEvent mediaListPlayerEvent = eventFactory.newMediaListPlayerEvent(event, eventMask);
                Logger.trace("mediaListPlayerEvent={}", mediaListPlayerEvent);
                if(mediaListPlayerEvent != null) {
                    listenersService.submit(new NotifyListenersRunnable(mediaListPlayerEvent));
                }
            }
        }
    }

    /**
     *
     */
    private final class NotifyListenersRunnable implements Runnable {

        /**
         *
         */
        private final MediaListPlayerEvent mediaListPlayerEvent;

        /**
         *
         *
         * @param mediaListPlayerEvent
         */
        private NotifyListenersRunnable(MediaListPlayerEvent mediaListPlayerEvent) {
            this.mediaListPlayerEvent = mediaListPlayerEvent;
        }

        @Override
        public void run() {
            Logger.trace("run()");
            for(int i = eventListenerList.size() - 1; i >= 0; i -- ) {
                MediaListPlayerEventListener listener = eventListenerList.get(i);
                try {
                    mediaListPlayerEvent.notify(listener);
                }
                catch(Exception e) {
                    Logger.warn("Event listener {} threw an exception", e, listener);
                    // Continue with the next listener...
                }
            }
            Logger.trace("runnable exits");
        }
    }

    /**
     *
     */
    private class NextItemHandler extends MediaListPlayerEventAdapter {

        /**
         * Current media instance.
         */
        private libvlc_media_t mediaInstance;

        @Override
        public void nextItem(MediaListPlayer mediaListPlayer, libvlc_media_t item, String itemMrl) {
            Logger.debug("nextItem(item={},itemMrl={})", item, itemMrl);
            deregisterMediaEventListener();
            this.mediaInstance = item;
            registerMediaEventListener();
        }

        /**
         *
         */
        private void registerMediaEventListener() {
            Logger.debug("registerMediaEventListener()");
            // If there is a media, register a new listener...
            if(mediaInstance != null) {
                libvlc_event_manager_t mediaEventManager = libvlc.libvlc_media_event_manager(mediaInstance);
                for(libvlc_event_e event : libvlc_event_e.values()) {
                    if(event.intValue() >= libvlc_event_e.libvlc_MediaMetaChanged.intValue() && event.intValue() <= libvlc_event_e.libvlc_MediaStateChanged.intValue()) {
                        Logger.debug("event={}", event);
                        int result = libvlc.libvlc_event_attach(mediaEventManager, event.intValue(), callback, null);
                        Logger.debug("result={}", result);
                    }
                }
            }
        }

        /**
         *
         */
        private void deregisterMediaEventListener() {
            Logger.debug("deregisterMediaEventListener()");
            // If there is a media, deregister the listener...
            if(mediaInstance != null) {
                libvlc_event_manager_t mediaEventManager = libvlc.libvlc_media_event_manager(mediaInstance);
                for(libvlc_event_e event : libvlc_event_e.values()) {
                    if(event.intValue() >= libvlc_event_e.libvlc_MediaMetaChanged.intValue() && event.intValue() <= libvlc_event_e.libvlc_MediaStateChanged.intValue()) {
                        Logger.debug("event={}", event);
                        libvlc.libvlc_event_detach(mediaEventManager, event.intValue(), callback, null);
                    }
                }
                mediaEventManager = null;
            }
        }
    }

    @Override
    protected void finalize() throws Throwable {
        Logger.debug("finalize()");
        Logger.debug("Media list player has been garbage collected");
        super.finalize();
    }
}
TOP

Related Classes of uk.co.caprica.vlcj.player.list.DefaultMediaListPlayer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.