/*****************************************************************************
|
* MediaList.java
|
*****************************************************************************
|
* Copyright © 2013 VLC authors and VideoLAN
|
* Copyright © 2013 Edward Wang
|
*
|
* 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;
|
|
import java.util.ArrayList;
|
|
import android.os.Bundle;
|
|
/**
|
* Java/JNI wrapper for the libvlc_media_list_t structure.
|
*/
|
public class MediaList {
|
private static final String TAG = "VLC/LibVLC/MediaList";
|
|
/* Since the libvlc_media_t is not created until the media plays, we have
|
* to cache them here. */
|
private class MediaHolder {
|
Media m;
|
boolean noVideo; // default false
|
boolean noHardwareAcceleration; // default false
|
|
public MediaHolder(Media media) {
|
m = media; noVideo = false; noHardwareAcceleration = false;
|
}
|
public MediaHolder(Media m_, boolean noVideo_, boolean noHardwareAcceleration_) {
|
m = m_; noVideo = noVideo_; noHardwareAcceleration = noHardwareAcceleration_;
|
}
|
}
|
|
/* TODO: add locking */
|
private ArrayList<MediaHolder> mInternalList;
|
private LibVLC mLibVLC; // Used to create new objects that require a libvlc instance
|
private EventHandler mEventHandler;
|
|
public MediaList(LibVLC libVLC) {
|
mEventHandler = new EventHandler(); // used in init() below to fire events at the correct targets
|
mInternalList = new ArrayList<MediaHolder>();
|
mLibVLC = libVLC;
|
}
|
|
/**
|
* Adds a media URI to the media list.
|
*
|
* @param mrl
|
* The MRL to add. Must be a location and not a path.
|
* {@link LibVLC#PathToURI(String)} can be used to convert a path
|
* to a MRL.
|
*/
|
public void add(String mrl) {
|
add(new Media(mLibVLC, mrl));
|
}
|
public void add(Media media) {
|
add(media, false, false);
|
}
|
public void add(Media media, boolean noVideo) {
|
add(media, noVideo, false);
|
}
|
public void add(Media media, boolean noVideo, boolean noHardwareAcceleration) {
|
mInternalList.add(new MediaHolder(media, noVideo, noHardwareAcceleration));
|
signal_list_event(EventHandler.CustomMediaListItemAdded, mInternalList.size() - 1, media.getLocation());
|
}
|
|
/**
|
* Clear the media list. (remove all media)
|
*/
|
public void clear() {
|
// Signal to observers of media being deleted.
|
for(int i = 0; i < mInternalList.size(); i++) {
|
signal_list_event(EventHandler.CustomMediaListItemDeleted, i, mInternalList.get(i).m.getLocation());
|
}
|
mInternalList.clear();
|
}
|
|
private boolean isValid(int position) {
|
return position >= 0 && position < mInternalList.size();
|
}
|
|
/**
|
* This function checks the currently playing media for subitems at the given
|
* position, and if any exist, it will expand them at the same position
|
* and replace the current media.
|
*
|
* @param position The position to expand
|
* @return -1 if no subitems were found, 0 if subitems were expanded
|
*/
|
public int expandMedia(int position) {
|
ArrayList<String> children = new ArrayList<String>();
|
int ret = expandMedia(mLibVLC, position, children);
|
if(ret == 0) {
|
mEventHandler.callback(EventHandler.CustomMediaListExpanding, new Bundle());
|
this.remove(position);
|
for(String mrl : children) {
|
this.insert(position, mrl);
|
}
|
mEventHandler.callback(EventHandler.CustomMediaListExpandingEnd, new Bundle());
|
}
|
return ret;
|
}
|
private native int expandMedia(LibVLC libvlc_instance, int position, ArrayList<String> children);
|
|
public void loadPlaylist(String mrl) {
|
ArrayList<String> items = new ArrayList<String>();
|
loadPlaylist(mLibVLC, mrl, items);
|
this.clear();
|
for(String item : items) {
|
this.add(item);
|
}
|
}
|
private native void loadPlaylist(LibVLC libvlc_instance, String mrl, ArrayList<String> items);
|
|
public void insert(int position, String mrl) {
|
insert(position, new Media(mLibVLC, mrl));
|
}
|
public void insert(int position, Media media) {
|
mInternalList.add(position, new MediaHolder(media));
|
signal_list_event(EventHandler.CustomMediaListItemAdded, position, media.getLocation());
|
}
|
|
/**
|
* Move a media from one position to another
|
*
|
* @param startPosition start position
|
* @param endPosition end position
|
* @throws IndexOutOfBoundsException
|
*/
|
public void move(int startPosition, int endPosition) {
|
if (!(isValid(startPosition)
|
&& endPosition >= 0 && endPosition <= mInternalList.size()))
|
throw new IndexOutOfBoundsException("Indexes out of range");
|
|
MediaHolder toMove = mInternalList.get(startPosition);
|
mInternalList.remove(startPosition);
|
if (startPosition >= endPosition)
|
mInternalList.add(endPosition, toMove);
|
else
|
mInternalList.add(endPosition - 1, toMove);
|
Bundle b = new Bundle();
|
b.putInt("index_before", startPosition);
|
b.putInt("index_after", endPosition);
|
mEventHandler.callback(EventHandler.CustomMediaListItemMoved, b);
|
}
|
|
public void remove(int position) {
|
if (!isValid(position))
|
return;
|
String uri = mInternalList.get(position).m.getLocation();
|
mInternalList.remove(position);
|
signal_list_event(EventHandler.CustomMediaListItemDeleted, position, uri);
|
}
|
|
public int size() {
|
return mInternalList.size();
|
}
|
|
public Media getMedia(int position) {
|
if (!isValid(position))
|
return null;
|
return mInternalList.get(position).m;
|
}
|
|
/**
|
* @param position The index of the media in the list
|
* @return null if not found
|
*/
|
public String getMRL(int position) {
|
if (!isValid(position))
|
return null;
|
return mInternalList.get(position).m.getLocation();
|
}
|
|
public String[] getMediaOptions(int position) {
|
boolean noHardwareAcceleration = mLibVLC.getHardwareAcceleration() == 0;
|
boolean noVideo = false;
|
if (isValid(position))
|
{
|
if (!noHardwareAcceleration)
|
noHardwareAcceleration = mInternalList.get(position).noHardwareAcceleration;
|
noVideo = mInternalList.get(position).noVideo;
|
}
|
ArrayList<String> options = new ArrayList<String>();
|
|
if (!noHardwareAcceleration) {
|
/*
|
* Set higher caching values if using iomx decoding, since some omx
|
* decoders have a very high latency, and if the preroll data isn't
|
* enough to make the decoder output a frame, the playback timing gets
|
* started too soon, and every decoded frame appears to be too late.
|
* On Nexus One, the decoder latency seems to be 25 input packets
|
* for 320x170 H.264, a few packets less on higher resolutions.
|
* On Nexus S, the decoder latency seems to be about 7 packets.
|
*/
|
options.add(":file-caching=1500");
|
options.add(":network-caching=1500");
|
options.add(":codec=mediacodec,iomx,all");
|
}
|
if (noVideo)
|
options.add(":no-video");
|
|
return options.toArray(new String[options.size()]);
|
}
|
|
public EventHandler getEventHandler() {
|
return mEventHandler;
|
}
|
|
@Override
|
public String toString() {
|
StringBuilder sb = new StringBuilder();
|
sb.append("LibVLC Media List: {");
|
for(int i = 0; i < size(); i++) {
|
sb.append(((Integer)i).toString());
|
sb.append(": ");
|
sb.append(getMRL(i));
|
sb.append(", ");
|
}
|
sb.append("}");
|
return sb.toString();
|
}
|
|
private void signal_list_event(int event, int position, String uri) {
|
Bundle b = new Bundle();
|
b.putString("item_uri", uri);
|
b.putInt("item_index", position);
|
mEventHandler.callback(event, b);
|
}
|
}
|