/***************************************************************************** * MediaBrowser.java ***************************************************************************** * Copyright © 2015 VLC authors, VideoLAN and VideoLabs * * 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. *****************************************************************************/ package org.videolan.libvlc.util; import android.net.Uri; import java.util.ArrayList; import org.videolan.BuildConfig; import org.videolan.libvlc.LibVLC; import org.videolan.libvlc.Media; import org.videolan.libvlc.MediaDiscoverer; import org.videolan.libvlc.MediaList; public class MediaBrowser { private static final String TAG = "LibVLC/util/MediaBrowser"; private static final String[] DISCOVERER_LIST = BuildConfig.DEBUG ? new String[]{ "dsm", // Netbios discovery via libdsm "upnp", // "bonjour", // "mdns" } : new String[]{"upnp"} ; //Only UPnP for release private final LibVLC mLibVlc; private final ArrayList<MediaDiscoverer> mMediaDiscoverers = new ArrayList<MediaDiscoverer>(); private final ArrayList<Media> mDiscovererMediaArray = new ArrayList<Media>(); private MediaList mBrowserMediaList; private Media mMedia; private EventListener mEventListener; private boolean mAlive; private static final String IGNORE_LIST_OPTION = ":ignore-filetypes="; private String mIgnoreList = "db,nfo,ini,jpg,jpeg,ljpg,gif,png,pgm,pgmyuv,pbm,pam,tga,bmp,pnm,xpm,xcf,pcx,tif,tiff,lbm,sfv,txt,sub,idx,srt,cue,ssa"; /** * Listener called when medias are added or removed. */ public interface EventListener { /** * Received when a new media is added. * @param index * @param media */ public void onMediaAdded(int index, Media media); /** * Received when a media is removed (Happens only when you discover networks) * @param index * @param media Released media, but cached attributes are still * available (like media.getMrl()) */ public void onMediaRemoved(int index, Media media); /** * Called when browse ended. * It won't be called when you discover networks */ public void onBrowseEnd(); } public MediaBrowser(LibVLC libvlc, EventListener listener) { mLibVlc = libvlc; mLibVlc.retain(); mEventListener = listener; mAlive = true; } private synchronized void reset() { for (MediaDiscoverer md : mMediaDiscoverers) md.release(); mMediaDiscoverers.clear(); mDiscovererMediaArray.clear(); if (mMedia != null) { mMedia.release(); mMedia = null; } if (mBrowserMediaList != null) { mBrowserMediaList.release(); mBrowserMediaList = null; } } /** * Release the MediaBrowser. */ public synchronized void release() { reset(); if (!mAlive) throw new IllegalStateException("MediaBrowser released more than one time"); mLibVlc.release(); mAlive = false; } /** * Reset this media browser and register a new EventListener * @param eventListener new EventListener for this browser */ public synchronized void changeEventListener(EventListener eventListener){ reset(); mEventListener = eventListener; } private void startMediaDiscoverer(String discovererName) { MediaDiscoverer md = new MediaDiscoverer(mLibVlc, discovererName); mMediaDiscoverers.add(md); final MediaList ml = md.getMediaList(); ml.setEventListener(mDiscovererMediaListEventListener); ml.release(); md.start(); } /** * Discover networks shares using available MediaDiscoverers */ public synchronized void discoverNetworkShares() { reset(); for (String discovererName : DISCOVERER_LIST) startMediaDiscoverer(discovererName); } /** * Discover networks shares using specified MediaDiscoverer * @param discovererName */ public synchronized void discoverNetworkShares(String discovererName) { reset(); startMediaDiscoverer(discovererName); } /** * Browse to the specified local path starting with '/'. * * @param path */ public synchronized void browse(String path) { final Media media = new Media(mLibVlc, path); browse(media); media.release(); } /** * Browse to the specified uri. * * @param uri */ public synchronized void browse(Uri uri) { final Media media = new Media(mLibVlc, uri); browse(media); media.release(); } /** * Browse to the specified media. * * @param media Can be a media returned by MediaBrowser. */ public synchronized void browse(Media media) { /* media can be associated with a medialist, * so increment ref count in order to don't clean it with the medialist */ media.retain(); media.addOption(IGNORE_LIST_OPTION+mIgnoreList); reset(); mBrowserMediaList = media.subItems(); mBrowserMediaList.setEventListener(mBrowserMediaListEventListener); media.parseAsync(Media.Parse.ParseNetwork); mMedia = media; } /** * Get the number or media. */ public synchronized int getMediaCount() { return mBrowserMediaList != null ? mBrowserMediaList.getCount() : mDiscovererMediaArray.size(); } /** * Get a media at a specified index. Should be released with {@link #release()}. */ public synchronized Media getMediaAt(int index) { if (index < 0 || index >= getMediaCount()) throw new IndexOutOfBoundsException(); final Media media = mBrowserMediaList != null ? mBrowserMediaList.getMediaAt(index) : mDiscovererMediaArray.get(index); media.retain(); return media; } /** * Override the extensions list to be ignored in browsing * default is "db,nfo,ini,jpg,jpeg,ljpg,gif,png,pgm,pgmyuv,pbm,pam,tga,bmp,pnm,xpm,xcf,pcx,tif,tiff,lbm,sfv,txt,sub,idx,srt,cue,ssa" * * @param list files extensions to be ignored by browser */ public synchronized void setIgnoreFileTypes(String list) { mIgnoreList = list; } private final MediaList.EventListener mBrowserMediaListEventListener = new MediaList.EventListener() { @Override public void onEvent(MediaList.Event event) { if (mEventListener == null) return; final MediaList.Event mlEvent = (MediaList.Event) event; /* * We use an intermediate array here since more than one MediaDiscoverer can be used */ switch (mlEvent.type) { case MediaList.Event.ItemAdded: mEventListener.onMediaAdded(mlEvent.index, mlEvent.media); break; case MediaList.Event.ItemDeleted: mEventListener.onMediaRemoved(mlEvent.index, mlEvent.media); break; case MediaList.Event.EndReached: mEventListener.onBrowseEnd(); } } }; private final MediaList.EventListener mDiscovererMediaListEventListener = new MediaList.EventListener() { @Override public void onEvent(MediaList.Event event) { if (mEventListener == null) return; final MediaList.Event mlEvent = (MediaList.Event) event; int index = -1; /* * We use an intermediate array here since more than one MediaDiscoverer can be used */ switch (mlEvent.type) { case MediaList.Event.ItemAdded: synchronized (MediaBrowser.this) { /* one item can be found by severals discoverers */ boolean found = false; for (Media media : mDiscovererMediaArray) { if (media.getUri().equals(mlEvent.media.getUri())) { found = true; break; } } if (!found) { mDiscovererMediaArray.add(mlEvent.media); index = mDiscovererMediaArray.size() - 1; } } if (index != -1) mEventListener.onMediaAdded(index, mlEvent.media); break; case MediaList.Event.ItemDeleted: synchronized (MediaBrowser.this) { index = mDiscovererMediaArray.indexOf(mlEvent.media); if (index != -1) mDiscovererMediaArray.remove(index); } if (index != -1) mEventListener.onMediaRemoved(index, mlEvent.media); break; case MediaList.Event.EndReached: mEventListener.onBrowseEnd(); } } }; }