/*
 * PS3 Media Server, for streaming any medias to your PS3.
 * Copyright (C) 2008  A.Brochard
 *
 * 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; version 2
 * of the License only.
 *
 * 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.
 *
 * 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.
 */
package net.pms.dlna;

import static net.pms.util.StringUtil.addAttribute;
import static net.pms.util.StringUtil.addXMLTagAndAttribute;
import static net.pms.util.StringUtil.closeTag;
import static net.pms.util.StringUtil.encodeXML;
import static net.pms.util.StringUtil.endTag;
import static net.pms.util.StringUtil.openTag;

import java.io.File;	//regza
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.text.Normalizer;

import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;

import net.pms.PMS;
import net.pms.configuration.FormatConfiguration;
import net.pms.configuration.RendererConfiguration;
import net.pms.dlna.virtual.TranscodeVirtualFolder;
import net.pms.dlna.virtual.VirtualFolder;
import net.pms.dlna.virtual.VirtualVideoAction;	//regzamod, add
import net.pms.dlna.MapFile;			//regzamod
import net.pms.dlna.rz_Resume;			//regzamod, add
import net.pms.dlna.rz_ResumeFolder;	//regzamod, add
import net.pms.dlna.rz_SessionInfo;		//regzamod, add
import net.pms.dlna.rz_ClearFolderCache;	//regzamod, add
import net.pms.dlna.rz_ScriptMenuFolder;	//regzamod, add
import net.pms.dlna.rz_ScriptExecutor;		//regzamod, add
import net.pms.dlna.rz_IavSelCtl;			//regzamod, add
import net.pms.dlna.rz_VirtualVideoActionF;	//regzamod, add
import net.pms.dlna.rz_SortMenuButton;	//regzamod, add
import net.pms.dlna.WebStream;	//regzamod, add

import net.pms.encoders.MEncoderVideo;
import net.pms.encoders.Player;
import net.pms.encoders.TSMuxerVideo;
import net.pms.encoders.VideoLanVideoStreaming;
import net.pms.encoders.RzAudioAsVideoTC;	//regzamod

import net.pms.external.AdditionalResourceFolderListener;
import net.pms.external.ExternalFactory;
import net.pms.external.ExternalListener;
import net.pms.external.StartStopListener;
import net.pms.formats.Format;
import net.pms.io.OutputParams;
import net.pms.io.ProcessWrapper;
import net.pms.io.ProcessWrapperImpl;
import net.pms.io.SizeLimitInputStream;
import net.pms.io.WaitBufferedInputStream; //regzamod
import net.pms.network.HTTPResource;
import net.pms.network.RequestV2;	//regzamod
import net.pms.util.FileUtil;
import net.pms.util.ImagesUtil;
import net.pms.util.Iso639;
import net.pms.util.MpegUtil;
//import net.pms.util.PMSUtil;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

//---- test for slowaction or inpatient renderer  
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.handler.codec.http.HttpResponse;  //regzamod, test2
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;	//regzamod
import org.jboss.netty.handler.codec.http.HttpResponseStatus;	//regzamod
import org.jboss.netty.handler.codec.http.HttpVersion;			//regzamod
import org.jboss.netty.handler.codec.http.HttpHeaders;


/**
 * Represents any item that can be browsed via the UPNP ContentDirectory service.
 *
 * TODO: Change all instance variables to private. For backwards compatibility
 * with external plugin code the variables have all been marked as deprecated
 * instead of changed to private, but this will surely change in the future.
 * When everything has been changed to private, the deprecated note can be
 * removed.
 */
public abstract class DLNAResource extends HTTPResource implements Cloneable, Runnable {
	private static final Logger logger = LoggerFactory.getLogger(DLNAResource.class);
	protected static final int MAX_ARCHIVE_ENTRY_SIZE = 10000000;
	protected static final int MAX_ARCHIVE_SIZE_SEEK = 800000000;
	protected static final String TRANSCODE_FOLDER = "#- TRANSCODE -#";
	protected static final String RESUME_TRANSCODE_FOLDER = "#- RESUME TRANSCODE -#";
	private final Map<String, Integer> requestIdToRefcount = new HashMap<String, Integer>();
	//public final Map<String, Integer> requestIdToRefcount = new HashMap<String, Integer>();	// regzamod, Test for refer from RequestV2.java
	private static final int STOP_PLAYING_DELAY = 4000;
	
	private int menu_type=PMS.getConfiguration().getRZ_menu_type();
	private int sort_menu_type=PMS.getConfiguration().getRZ_sort_menu_type();
	private double durationDelta=PMS.getConfiguration().getRZ_resume_duration_delta();

	//---- regzamod add start
	//---- MetaFile params 
	//---- TODO: Should be in sub-class for metafile
	public File  rz_Metafile;		// PlayMetafile
	public File  rz_Metafile2;		// linked-meta file
	public File  rz_Profile;		// PlayProfile
	public File  rz_Bodyfile;		// body file
	public String rz_Bodypath;		// path of body file
	public boolean rz_isPartPlay;		// partial_play (start/stop time) disignated
	public double rz_PartPlayDuration;	// partial_play duration (sec) 
	public double rz_PartPlaySttp;		// partial_play start time (sec)
	public double rz_PartPlayEndp;		// partial_play stop time (sec)
	public double rz_SeekAudioDelay;	// timeseek audio delay (sec)
	public double rz_SeekAudioMagnify;	// timeseek audio magnifying ratio
	public String rz_MetaAspect;		// force aspect_ratio
	public double[] rz_MetaShift=new double[2];	// force shift
	public int rz_MetaSoundVolume= -1;	// force volume (normal=256) 128=-6dBA512=+6dB,  -1:not setted
	public int rz_MetaSoundSync= -1;	// ffmpeg's -async value
	
	//[0]:for pxm(per file),[1]:for renderer_script(per renderer)
	public double rz_MetaZoom[]= {-1,-1};		// force zoom
	public double rz_SoundDelay[]={0,0};		// Sound Delay (sec) 
	public int rz_MetaTransType[]= {-1,-1};		// {ENC_DIRECT|ENC_REMUX|ENC_TRANSCODE|-1}, -1:not setted 
	public int rz_MetaTransFormat[]= {-1,-1};	// {TRS_M2TS|TRS_MPEGTS|TRS_MPEGPS}, -1:not setted 
	public int rz_MetaTransFormatWeb[]= {-1,-1};// {TRS_M2TS|TRS_MPEGTS|TRS_MPEGPS}, -1:not setted 
	public int rz_MetaRemuxFormat[]= {-1,-1};	// {TRS_M2TS|TRS_MPEGTS}, -1:not setted 
	public int rz_MetaTransProc[]= {-1,-1};		// {113|111|112|-1},1:mencoder,2:ffmpeg,3:tsmuxer, -1:not setted 
	public int rz_MetaRemuxProc[]= {-1,-1};		// {213|222|333|-1},1:mencoder,2:ffmpeg,3:tsmuxer, -1:not setted 
	
	public String rz_MetaTransProg[]= {null,null};		// trans_program path 
	public String rz_MetaRemuxProg[]= {null,null};		// remux program path
	public String rz_MetaMencoderOpt[]={null,null};		// mencoder options for trasco
	public String rz_MetaMencoderOptA[]={null,null};	// mencoder options for trasco (audio only)
	//public String rz_WebOpt[]={null,null};				// for web contents options
	public String rz_MetaFFmpegOpt[]={null,null};		// ffmpeg options for remux
	public String rz_MetaTsmuxerOptA[]={null,null};		// tsmuxer opt audio
	public String rz_MetaTsmuxerOptV[]={null,null};		// tsmuxer opt video
	//public String rz_MetaFFmpegPath[]={null,null};		// ffmpeg prog path
	public String rz_MetaMencoderPath[]={null,null};	// mencoder prog path
	
	public String rz_MetaDlnaProf[]={null,null};
	public String rz_MetaDlnaOp[]={null,null};
	public String rz_MetaMimeType[]={null,null};
	public String rz_MetaTitle[]={null,null};
	//public int rz_MetaFlags[]={0,0};
	
	//public String rz_ScriptMencoderOpt;	// mencoder options for trasco
	//public String rz_ScriptMencoderOptA;	// mencoder options for trasco (audio only)
	//public String rz_ScriptFFmpegOpt;		// ffmpeg options for remux
	
	public int rz_MetaChannel_v= -1;	// Video Channel No. -1:not setted
	public int rz_MetaChannel_a= -1;	// Audio Channel No. -1:not setted
	public int rz_MetaChannel_a_ffmpeg= -1;	// Audio Channel No. for ffmpeg when called from #TRANSCODE# virtual file
	public long rz_MetaDispDate;
	public int rz_MetaSortType =-1;
	public boolean rz_MetaSortRecursive;
	public String rz_MetaDVDchapter;
	public boolean rz_MetaBodyExists=true;
	public boolean rz_MetaNotExistSuffix=true;  //add (?) suffix to filename when file's body not found
	public boolean pxf_resolved;
	public long pxf_lastmodified;
	private int trans_type_org;
	private long ScriptLastModified;
	private String dlna_path;
	private DLNAResource prev_dlna;
	private long prev_tim;
	
	//--- mflags
	public long mflags;
	public static final long	MF_REMUX_JUDGED			=0x0001;
	public static final long	MF_UNDER_TRASCO_FOLDER	=0x0002;
	public static final long	MF_HIDE					=0x0004;  //hidden object (not displayed)
	public static final long	MF_USE_LINKNAME			=0x0008;  //for link obj, use link target name instead of it's own name
	public static final long	MF_KWM_MENU				=0x0010;  //for keyword menu top objects
	public static final long	MF_KWM_LIST				=0x0020;  //for keyword list top objects
	public static final long	MF_WEB_FOLDER			=0x0040;  //for web contents folder
	public static final long	MF_SORTMENU				=0x0080;  //sort_menu button
	public static final long	MF_BUTTON_ELEMENT		=0x0100;  //general menu button
	public static final long	MF_WEB_RESUME_FOLDER	=0x0200;  //web folder used for resume_per_folder)
	public static final long	MF_KWM_IN_KWL			=0x0400;  //keyword_edit_menu in keyword_select_list
	public static final long	MF_HIDE_SYS			    =0x0800;  //hidden object (don't un-hidden)
	
	//--- rz_MetaFlags[]
	//public static final long	META_DIRECT_SEEK		=0x0001;
	
	//---- from pxm , ugly params tobe refined
	public boolean isVfolder;
	public boolean isDVDISO=false;
	public int dvd_title_num;	//dvd title number(valid >=1)
	public String dvd_chapter_str;	//dvd chapter number string e.x. 1,2,1-5 
	//public boolean body_exists=true;
	//public String sname,sname0;
	//public String title; 			// metafile params
	//public String bodypath;
	//----
		
	public int	meta_type;	//PlayMetafile's sub type =0:not metafile,=1:general meta,=2:meta as resume_file
	
	//---- sub_type of DLNAResource object (for indicate special use)
	public int	sotype;		//sub_type of object
	//---- values for sotype (shouldn't be duplicated)
	//public static final int	SO_VFOLDER			=1;  //not used
	public static final int	SO_SORTFOLDER		=2;
	public static final int	SO_DVDDEV			=4;		//Real DVD
	public static final int	SO_ITUNE_SOLVED		=5;		//iTune folder resolved
	public static final int	SO_ITUNE_NOTSOLVED	=6;		//iTune folder not resolved
	public static final int	SO_PLAYLIST			=10;
	public static final int	SO_VIDEO_STREAM		=11;
	public static final int	SO_RESUME_FOLDER	=12;
	public static final int	SO_WATCHED_FOLDER	=13;
	public static final int	SO_RESUME			=14;
	public static final int	SO_KWM_KWD			=15;
	public static final int	SO_KWM_DIR			=16;
	public static final int	SO_KWM_CMD			=17;
	public static final int	SO_KWM_DSP			=18;
	public static final int	SO_WEB_AUDIO		=19;
	public static final int	SO_LOCK_BTN			=20;   	//lock button for avoid auto_search
	public static final int	SO_CONTROLLS		=21;   	//Controlls (menu base folder)
	//public static final int	SO_KWM_IN_KWL	=21; 	//conflicts with SO_KWM_XX: moved to MF_KWM_IN_KWL
	
	public boolean resolved;
	public boolean doSort=false;	//oneshot sort
	public int sort_immune;		//to be not sorted: (allways =1:top,=2:last)
	public boolean sort_enabled=true;	//regzamod
	public String rz_VisualClipPath;	//for RzAAVplayer
	public DLNAResource rz_linkobj;		//link to 
public DLNAResource rz_srcobj;		//dummy for srcobj (for disp multi-page)
	public long scanid;	// scan id, to avoid infinit loop, when scanning dlna tree

	//---- valiables for memory
	public String gMimeType_org;	//mimeType DLNA origin
	public String gMimeType_ren;	//mimeType modified per renderer
	public String gMimeType_cur;	//mimeType finally used
	//public String dlnaspec_org;		//current dlnaspec original(before modifications)
	//public String dlna_prof;			//current dlnaspec
	
	//---- simple mediainfo for web_contents
	//---- TODO: should be in sub-class
	public boolean wmed_valid;
	public String wmed_fmt;
	public String wmed_w;
	public String wmed_h;
	public String wmed_asp;
	public String wmed_fps;
	public String wmed_vc;
	public String wmed_ac;
	public float wmed_duration;
	public String wmed_opts;
	
	private long serialId;	//for serial_order under a Folder
	public DLNAResource sortMenu;
	public DLNAResource transFolder;
	public DLNAResource ctlFolder;
	public DLNAResource resumeFolder;
	public DLNAResource resumeFolder2;
	public DLNAResource ClearFolderCahceMenu;
	public DLNAResource ScriptMenuFolder;
	public DLNAResource KeyExtructMenu;
	public DLNAResource videoSettingFolder;
	
	public rz_IavSelCtl iavSelCtl;
	public String dname;	//2nd name forced by pxf/pxm
	public String dname2;	//3rd name used when under #- TRANSCODE -#
	public String subname;	//sub name
	public boolean allowCtlFolder=true;
	
	//---- for resume
	private long last_errtim;
	private double resumeTimeSec;			// sec
	private double timeseek_prev;
	private RendererConfiguration rend_cur;
	private boolean mpegTsMux;
	private int under_web_folder= -1;  //not checked yet
	public String resume_path;

	public int	 resStat; 
	public static int	RES_START=1;
	public static int	RES_STOP=2;
	public long resStartTime;		// millisec
	public long resStartTimeBias;	// millisec
	public long resStartTimeBias2;	// millisec
	public long resStartTimeForce;	// millisec
	public long resStartRealTime;	// millisec
	public long resStopRealTime;	// millisec
	public long pauseTime;			// pause pos in millisec, for ByteSeekPause
	public long resumeTime;			// resume pos in millisec
	public long bitrateForTimeSeek;	// kbps for ServerSide Timeseek
	public long bpsCalcCnt;		// counts of bitrateCalc
	public int seek_mode;
	public boolean isResumeFolderAll;
	public StringBuilder toString_sb;
	//public int onBDMV= -1;

	
	//menu order (==object index)
	public static final int MENU_ORD_TRANS		   	=1;
	public static final int MENU_ORD_SORT			=2;
	public static final int MENU_ORD_RESUME			=3;
	public static final int MENU_ORD_RESUME_ALL		=4;
	public static final int MENU_ORD_RESUME_DEL	    =5;
	public static final int MENU_ORD_WATCHED_DEL    =5;
	public static final int MENU_ORD_SCRIPT		    =6;
	public static final int MENU_ORD_CLEAR_CACHE    =7;
	public static final int MENU_ORD_KEY_EXT	    =8;
	public static final int MENU_ORD_BG_CLIP	    =9;
	public static final int MENU_ORD_VIDEO_SETTING  =10;
	
	//---- regzamod add end

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected int specificType;

	/**
	 * @deprecated Use standard getter and setter to access this variable. 
	 */
	@Deprecated
	protected String id;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected DLNAResource parent;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected Format ext;

	@Deprecated
	protected Format ext_aav;	//regzamod
	
	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected DLNAMediaInfo media;

	@Deprecated
	protected DLNAMediaInfo med_aav;	//regzamod, for audioAsVideo
	
	/**
	 * @deprecated Use {@link #getMediaAudio()} and {@link
	 * #setMediaAudio(DLNAMediaAudio)} to access this variable.
	 */
	@Deprecated
	protected DLNAMediaAudio media_audio;

	/**
	 * @deprecated Use {@link #getMediaSubtitle()} and {@link
	 * #setMediaSubtitle(DLNAMediaSubtitle)} to access this variable.
	 */
	@Deprecated
	protected DLNAMediaSubtitle media_subtitle;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected long lastmodified;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 * 
	 * Represents the transformation to be used to the file. If null, then 
	 * @see Player
	 */
	@Deprecated
	protected Player player;
	protected Player player2;	// regzamod for force-transcoding
	public Player player3;	// forced

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected boolean discovered = false;

	//private ProcessWrapper externalProcess;
	public ProcessWrapper externalProcess;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected boolean srtFile;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected int updateId = 1;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	public static int systemUpdateId = 1;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected boolean noName;

	private int nametruncate;
	private DLNAResource first;
	private DLNAResource second;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 * 
	 * The time range for the file containing the start and end time in seconds.
	 */
	@Deprecated
	protected Range.Time splitRange = new Range.Time();

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected int splitTrack;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected String fakeParentId;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	// Ditlew - needs this in one of the derived classes
	@Deprecated
	protected RendererConfiguration defaultRenderer;
	
	@Deprecated
	protected rz_SessionInfo currentSession;  //regzamod

	private String dlnaspec;
	

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected boolean avisynth;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected boolean skipTranscode = false;

	private boolean allChildrenAreFolders = true;
	private String flags;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 * 
	 * List of children objects associated with this DLNAResource. This is only valid when the DLNAResource is of the container type.
	 */
	@Deprecated
	protected List<DLNAResource> children;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 * 
	 * the id which the last child got, so the next child can get unique id with incrementing this value.
	 */
	@Deprecated
	//protected int lastChildrenId;
	protected int lastChildrenId=100;  //1-99 are reserveved for System menu

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 * 
	 * The last time when refresh is called.
	 */
	@Deprecated
	protected long lastRefreshTime;
	
	//---- regzamod start -----------------
	@Deprecated
	protected int encMode= -1;	//regzamod, EncodingMode 0:direct, 1:Remuxing, 2:Transcoding

	public void deleteMenus() {
		ctlFolder=null;
		sortMenu=null;
		transFolder=null;
		resumeFolder=null;
		resumeFolder2=null;
		ClearFolderCahceMenu=null;
		ScriptMenuFolder=null;
		iavSelCtl=null;
		videoSettingFolder=null;
	}
	//regzamod, EncodingMode 0:direct, 1:Remuxing, 2:Transcoding
	public void setEncMode(int enc) {	//regzamod
		encMode=enc;
	}
	public int getEncMode() {	//regzamod
		if(getPlayer()==null) return PMS.ENC_DIRECT;
		else if(encMode>=0) {  //strictly judged
			return encMode;
		}
		else if(getRemuxable()==1) {  //approximately judged
			return PMS.ENC_REMUX;
		}
		return PMS.ENC_TRANSCODE;
	}
	
	public boolean needReCreate() {	//regzamod add
		//if need change on subclasses, override me
		return false;
	}

	public String getFolderCachePath () {	//regzamod add
		return null;
	}
	public String getMediaCachePath (int[] rc) {	//regzamod add
		rc[0]=0;
		return null;
	}
	public void setResolved(boolean set) {	//regzamod add
		resolved=set;
	}
	public void setDelayedClear(boolean set) {	//regzamod
	}
	public boolean getDelayedClear() {	//regzamod
		return false;
	}

	public int activate(int type) {
		return -1;
	}
	
	public int getSortType () {	//regzamod add
		if(rz_MetaSortType>=0) {
			return rz_MetaSortType;
		}
		else {	//default
			DLNAResource pa=getParent();
			//search forced sort in parents, heavy process ?
			while (pa!=null) {
				if(pa.rz_MetaSortRecursive && pa.rz_MetaSortType>=0) {
					return pa.rz_MetaSortType;
				}
				pa=pa.getParent();
			}
			return PMS.getConfiguration().getSortMethod();
		}
	}
	
	public void setDispDate(long date) {
		lastmodified=date;
	}
	public long getDispDate() {
		return lastmodified;
	}
	
	public String[] getUrlParams() {
		return null;
	}
	public String getUrlParamsString() {
		return null;
	}
	
	public DLNAResource getMenuBaseFolder(DLNAResource dlna) {
		int ures=(isUnderResumeFolder(dlna)?1:0);// regzamod 
		if(dlna.menu_type>=2) {  // newType
			return dlna.getCtlFolder(true,ures);  //danger: assuming given dlna is me
		}
		else {
			return dlna.getTranscodeFolder(true,ures);//danger: assuming given dlna is me
		}
	}
	
	public void createVideoSettingMenu (DLNAResource dlna) {
		DLNAResource pa;
		pa=getMenuBaseFolder(dlna);
		
		if(PMS.rz_debug>1) PMS.dbg("createVideoSettingMenu: Start, dlna="+dlna.getName());
		if(pa==null) {
			if(PMS.rz_debug>1) PMS.dbg("createVideoSettingMenu: Noop: pa==null");
			return;
		}
		if(pa.videoSettingFolder!=null) {
			if(PMS.rz_debug>1) PMS.dbg("createVideoSettingMenu: Noop: Already created");
			return;
		}
		RootFolder root=dlna.getRootFolder();
		if(root==null) {
			if(PMS.rz_debug>1) PMS.dbg("createVideoSettingMenu: Noop: getRootFolder==null, dlna="+dlna.getName());
			return;
		}
		VirtualFolder vf=root.getVideoSettingssFolderVf(pa,"#- VIDEO SETTINGS -#");
		if(vf!=null) {
			vf.setSerialId(MENU_ORD_VIDEO_SETTING);
			pa.videoSettingFolder=vf;
			pa.addChildInternal(vf);
		}
		else {
			if(PMS.rz_debug>1) PMS.dbg("createVideoSettingMenu: Noop: getVideoSettingssFolderVf==null, dlna="+dlna.getName());
		}
	}
	
	public void createResumeFolder (DLNAResource dlna, int ures) {
		//PMS.dbg("createResumeFolder: start, name="+dlna.getName());
		DLNAResource pa;
		if(PMS.rz_resume_mode==0) {
			//resume disabled
			//PMS.dbg("createResumeFolder: Noop: rz_resume_mode==0");
			return;
		}
		if(isUnderResumeFolder(dlna)) {
			//PMS.dbg("createResumeFolder: Noop: isUnderResumeFolder, name="+dlna.getName());
			return;
		}
		if(dlna.sotype==SO_WATCHED_FOLDER) {
			//its watched folder: need not resume (cause recursive)
			return;
		}
		if(menu_type>=2) {  // newType
			pa=getCtlFolder(true,0);  //danger: assuming given dlna is me
		}
		else {
			pa=getTranscodeFolder(true,0);//danger: assuming given dlna is me
		}
		if(pa==null) {
			//PMS.dbg("createResumeFolder: Noop: pa==null");
			//already created, or something bad
			return;
		}
		else if(pa.resumeFolder!=null) {
			//PMS.dbg("createResumeFolder: Noop: seems already created (pa.resumeFolder!=null), pa="+pa.getName());
			//already created, or something bad
			return;
		}
		//PMS.dbg("createResumeFolder: found pa="+pa.getName());
		
		//---- per folder resumes
		if(dlna.getResumePath()!=null) {
			rz_ResumeFolder vf2=new rz_ResumeFolder(dlna,"#- RESUME -#",true);
			if(vf2!=null) {
				//vf2.sort_immune=1;
				//vf.sotype=SO_RESUME_FOLDER;
				vf2.setSerialId(MENU_ORD_RESUME);
				pa.resumeFolder2=(DLNAResource)vf2;
				pa.addChildInternal(vf2);
				//PMS.dbg("createResumeFolder: RESUME_FOLDER created, dlna.getResumePath()="+dlna.getResumePath()+", dlna="+dlna);
				
				//---- per folder watched folder
				rz_WatchedFolder vf3=new rz_WatchedFolder(dlna,"#- WATCHED -#",true);
				vf3.setSerialId(MENU_ORD_RESUME);
				vf2.addChildInternal(vf3);
			}
		}
		else {
			//PMS.dbg("createResumeFolder: RESUME_FOLDER Not created (dlna.getResumePath()==null)");
		}

		//all---- resumes
		VirtualFolder vf=PMS.getRootFolderByDlna(dlna).getResumeFolderVf(pa,"#- RESUME ALL -#");
		if(vf!=null) {
			//vf.sort_immune=1;
			vf.setSerialId(MENU_ORD_RESUME_ALL);
			//vf.sotype=SO_RESUME_FOLDER;
			pa.resumeFolder=vf;
			pa.addChildInternal(vf);
			//PMS.dbg("createResumeFolder: RESUME_ALL created, name="+dlna.getName());
		}
		else {
			//PMS.dbg("createResumeFolder:  RESUME_ALL failed create (PMS.getRootFolderByDlna(dlna).getResumeFolderVf==null), name="+dlna.getName());
		}
	}
	
	public void createSelClipPath(DLNAResource dlna, int ures) {
		DLNAResource pa;
		if(isUnderResumeFolder(dlna)) {
			return;
		}
		if(menu_type>=2) {  // newType
			pa=getCtlFolder(true,0);
		}
		else {
			pa=getTranscodeFolder(true,0);
		}
		if(pa==null || pa.iavSelCtl!=null) {
			//already created, or something bad
			return;
		}
		RootFolder root=PMS.getRootFolderByDlna(dlna);
		pa.iavSelCtl=root.getSelClipBase();
		//pa.iavSelCtl.setSerialId(10);  //danger!!
		
		//create root for clippath
		VirtualFolder vf= new VirtualFolder("#- BackGround Clips -#", null);
		vf.setSerialId(MENU_ORD_BG_CLIP);
		pa.addChildInternal(vf);
		
		//create select clippath_a/v
		root.getSelClipPathVf(vf, null,1);
		
		//create add clippath btns
		vf.addChild(pa.iavSelCtl.createClipAddBtn_file(this));
		vf.addChild(pa.iavSelCtl.createClipAddBtn_folder(this));
	}
	
	public void createScriptMenuFolder(DLNAResource dlna, int ures) {
		if(!PMS.rz_is_rend_script_enabled) return;
		DLNAResource pa;
		if(dlna.getDefaultRenderer()==null) 
			return;
		if(menu_type>=2) {  // newType
			pa=getCtlFolder(true,ures);
		}
		else {
			pa=getTranscodeFolder(true,ures);
		}
		if(pa==null || pa.ScriptMenuFolder!=null) {
			return;
		}
		
		if(PMS.rz_debug>2) {
			PMS.dbg("createScriptMenuFolder: dlna="+dlna.getName()+", renderer="+getDefaultRenderer());
			PMS.dbg("createScriptMenuFolder: parent="+dlna.getParent());
		}
		if(dlna.getParent()!=null) {
			if(PMS.rz_debug>2) PMS.dbg("createScriptMenuFolder: parent.renderer="+dlna.getParent().getDefaultRenderer());
		}
		
		rz_ScriptMenuFolder ch=new rz_ScriptMenuFolder("#- SCRIPT MENU -#",null,getDefaultRenderer());
		ch.setCurrentSession(pa.currentSession);
		ch.setSerialId(MENU_ORD_SCRIPT);
		pa.addChildInternal(ch);
		pa.ScriptMenuFolder=ch;
	}
	
	public void createKeyExtructMenu(DLNAResource dlna, int ures) {
		DLNAResource pa;
		if(dlna.getDefaultRenderer()==null) return;
		if(menu_type>=2) pa=getCtlFolder(true,ures);
		else pa=getTranscodeFolder(true,ures);
		if(pa==null || pa.KeyExtructMenu!=null) return;
		
		rz_KeywordMenu kwm=PMS.getRootFolderByDlna(dlna).getKwdMenuBase();
		rz_KeyExtructMenu ch=new rz_KeyExtructMenu("#- Keyword Extruct -#",null);
		ch.setOpt(dlna,kwm);
		pa.addChildInternal(ch);
		pa.KeyExtructMenu=ch;
		ch.setSerialId(MENU_ORD_KEY_EXT);
	}
	
	public void createClearFolderCahceMenu(DLNAResource dlna, int ures) {
		DLNAResource pa;
		String name;
		//if(dlna.getFolderCachePath()==null) {
		if(!dlna.isFolder()) {
			//havn't according media chache
			return;
		}
		if(ures==1 || (ures<0 && isUnderResumeFolder(dlna))) {
			name="#- RESUME CLEAR MEDIA CACHE -#";
			ures=1;
		}
		else {
			name="#- CLEAR MEDIA CACHE -#";
			ures=0;
		}
		if(menu_type>=2) {  // newType
			pa=getCtlFolder(true,ures);
		}
		else {
			pa=getTranscodeFolder(true,ures);
		}
		if(pa==null) {
			//PMS.dbg("createClearFolderCahceMenu: Noop: pa==null");
			return;
		}
		else if(pa.ClearFolderCahceMenu!=null) {
			//PMS.dbg("createClearFolderCahceMenu: Noop: pa.ClearFolderCahceMenu!=null, pa="+pa.getName());
			return;
		}
		VirtualFolder m1 = new VirtualFolder(name, null);
		m1.sort_enabled=false;
		//m1.sort_immune=1;
	    m1.setSerialId(MENU_ORD_CLEAR_CACHE);
		pa.addChildInternal(m1);
		pa.ClearFolderCahceMenu=m1;
		
		rz_ClearFolderCache m2;
		m2 = new rz_ClearFolderCache("Clear for Folder",1);
		m2.sort_enabled=false;
		m2.sort_immune=1;
		m2.targetDLNA=dlna;
		//m1.addChildInternal(m2); //light, but does not exec resolve, cause error on disp activate icon
		m1.addChild(m2); //heavy , but does exec resolve
		
		m2 = new rz_ClearFolderCache("Clear for Folder & SubFolders",2);
		m2.sort_enabled=false;
		m2.sort_immune=1;
		m2.targetDLNA=dlna;
		m1.addChild(m2);
	}
	
	public void createSortMenu (DLNAResource dlna, int ures) {
		//if(PMS.rz_debug>1) PMS.dbg("createSortMenu: name="+getName()+", parent="+(getParent()!=null?getParent().getName():"null"));
		DLNAResource pa;
		String name;
		int type;
		if(!PMS.getConfiguration().isRZ_disp_sort_menu()) {
			//if(PMS.rz_debug>1) PMS.dbg("createSortMenu: isRZ_disp_sort_menu() disabled, return");
			return;
		}
		if(dlna instanceof Feed) type=1;
		else if(dlna.sotype==SO_PLAYLIST) type=2;
		else if(dlna.getParent()==null) type=4;  //RootFolder
		else type=3;
		
		//if(PMS.rz_debug>1) PMS.dbg("createSortMenu: ures given="+ures+", dlna="+dlna);
		if(ures==1 || (ures<0 && isUnderResumeFolder(dlna))) {
			name="#- RESUME SORT -#";
			ures=1;
		}
		else {
			name="#- SORT -#";
			ures=0;
		}
		//if(PMS.rz_debug>1) PMS.dbg("createSortMenu: ures judged="+ures+", dlna="+dlna);
		
		if(menu_type>=2) {  // newType
			pa=getCtlFolder(true,ures);
		}
		else {
			pa=getTranscodeFolder(true,ures);
		}
		if(pa==null || pa.sortMenu!=null) {
			//if(PMS.rz_debug>1) PMS.dbg("createSortMenu: pa=null or sortMenu=null, return");
			return;
		}
		VirtualFolder m1 = new VirtualFolder(name, null);
		//m1.sort_immune=1;
		m1.setSerialId(MENU_ORD_SORT);
		m1.sotype=SO_SORTFOLDER;	//indicate SortMenu's parent
		m1.sort_enabled=false;
		pa.addChildInternal(m1);
		pa.sortMenu=m1;
		
		DLNAResource target=dlna;
		//VirtualFolder m2;
		//rz_VirtualVideoActionF m2;
		rz_SortMenuButton m2;
		
		if(type==1) {	//for Web Feed
			//m2 = new VirtualFolder("NAME", null);
			//m2 = new rz_VirtualVideoActionF("NAME", null);
			//m2.sotype=SO_SORTMENU;	//indicate SortMenu
			m2 = new rz_SortMenuButton("NAME", target);
			m2.mflags|=MF_SORTMENU;	//indicate SortMenu
			m2.sort_immune=1;
			m2.rz_MetaSortType=PMS.SORT_NAME;
			m1.addChild(m2);

			m2 = new rz_SortMenuButton("NAME_NUM", target);
			m2.mflags|=MF_SORTMENU;	//indicate SortMenu
			m2.sort_immune=1;
			m2.rz_MetaSortType=PMS.SORT_NAME_NUM;
			m1.addChild(m2);
			
			m2 = new rz_SortMenuButton("ORIGINAL", target);
			m2.mflags|=MF_SORTMENU;	//indicate SortMenu
			m2.sort_immune=1;
			m2.rz_MetaSortType=PMS.SORT_ORIGIN;
			m1.addChild(m2);

			m2 = new rz_SortMenuButton("ORIGINAL_R", target);
			m2.mflags|=MF_SORTMENU;	//indicate SortMenu
			m2.sort_immune=1;
			m2.rz_MetaSortType=PMS.SORT_ORIGIN_R;
			m1.addChild(m2);
		}
		else {	//for local contents
			//if(PMS.rz_debug>1) PMS.dbg("createSortMenu: create menu for local contents");
			m2 = new rz_SortMenuButton("NAME", target);
			m2.mflags|=MF_SORTMENU;	//indicate SortMenu
			m2.sort_immune=1;
			m2.rz_MetaSortType=PMS.SORT_NAME;
			m1.addChild(m2);
			
			m2 = new rz_SortMenuButton("NAME_NUM", target);
			m2.mflags|=MF_SORTMENU;	//indicate SortMenu
			m2.sort_immune=1;
			m2.rz_MetaSortType=PMS.SORT_NAME_NUM;
			m1.addChild(m2);

			m2 = new rz_SortMenuButton("NAME_ASC", target);
			m2.mflags|=MF_SORTMENU;	//indicate SortMenu
			m2.sort_immune=1;
			m2.rz_MetaSortType=PMS.SORT_NAME_ASC;
			m1.addChild(m2);

			m2 = new rz_SortMenuButton("DATE", target);
			m2.mflags|=MF_SORTMENU;	//indicate SortMenu
			m2.sort_immune=1;
			m2.rz_MetaSortType=PMS.SORT_DATE;
			m1.addChild(m2);

			m2 = new rz_SortMenuButton("DATE_R", target);
			m2.mflags|=MF_SORTMENU;	//indicate SortMenu
			m2.sort_immune=1;
			m2.rz_MetaSortType=PMS.SORT_DATE_R;
			m1.addChild(m2);

			//if(type==2 || type==4) {	//playlist
				m2 = new rz_SortMenuButton("ORIGINAL", target);
				m2.mflags|=MF_SORTMENU;	//indicate SortMenu
				m2.sort_immune=1;
				m2.rz_MetaSortType=PMS.SORT_ORIGIN;
				m1.addChild(m2);

				m2 = new rz_SortMenuButton("ORIGINAL_R", target);
				m2.mflags|=MF_SORTMENU;	//indicate SortMenu
				m2.sort_immune=1;
				m2.rz_MetaSortType=PMS.SORT_ORIGIN_R;
				m1.addChild(m2);
			//}
			//else {	//normal file list
				m2 = new rz_SortMenuButton("DEFAULT", target);
				m2.mflags|=MF_SORTMENU;	//indicate SortMenu
				m2.sort_immune=1;
				m2.rz_MetaSortType=PMS.SORT_DEFAULT;
				m1.addChild(m2);
			//}
		}
	}
	
	public boolean isUnderResumeFolder(DLNAResource dlna) {
		if(dlna.sotype==SO_RESUME_FOLDER)
			return true;
		return false;
		/*
		DLNAResource pa=dlna.getParent();
		if(pa!=null) {
			if(pa.sotype==SO_RESUME_FOLDER) {
				return true;
			}
			else {
				pa=pa.getParent();
				if(pa!=null) {
					if(pa.sotype==SO_RESUME_FOLDER) 
						return true;
				}
			}
			return false;
		}
		return false;
		*/
	}
	
	protected void setSerialId(long serialId) {	//regzamod, add
		this.serialId=serialId;
	}
	public long getSerialId() {	//regzamod, add
		return serialId;
	}

	//---- regzamod end -----------------

	/**
	 * Returns parent object, usually a folder type of resource. In the DLDI
	 * queries, the UPNP server needs to give out the parent container where
	 * the item is. The <i>parent</i> represents such a container.
	 * 
	 * @return Parent object.
	 */
	public DLNAResource getParent() {
		return parent;
	}

	/**
	 * Set the parent object, usually a folder type of resource. In the DLDI
	 * queries, the UPNP server needs to give out the parent container where
	 * the item is. The <i>parent</i> represents such a container.

	 * @param parent Sets the parent object.
	 */
	public void setParent(DLNAResource parent) {
		this.parent = parent;
	}

	/**
	 * Returns the id of this resource based on the index in its parent
	 * container. Its main purpose is to be unique in the parent container.
	 * 
	 * @return The id string.
	 * @since 1.50
	 */
	public String getId () {
		return id;
	}

	/**
	 * Set the id of this resource based on the index in its parent container.
	 * Its main purpose is to be unique in the parent container. The method is
	 * automatically called by addChildInternal, so most of the time it is not
	 * necessary to call it explicitly.
	 * 
	 * @param id
	 * @since 1.50
	 * @see #addChildInternal(DLNAResource)
	 */
	protected void setId (String id) {
		this.id = id;
		if(id==null) {  //used for clone
			toString_sb=null;  //clear cache
		}
	}

	/**
	 * String representing this resource ID. This string is used by the UPNP
	 * ContentDirectory service. There is no hard spec on the actual numbering
	 * except for the root container that always has to be "0". In PMS the
	 * format used is <i>number($number)+</i>. A common client that expects a
	 * different format than the one used here is the XBox360. PMS translates
	 * the XBox360 queries on the fly. For more info, check
	 * http://www.mperfect.net/whsUpnp360/ . 
	 * 
	 * @return The resource id.
	 * @since 1.50
	 */
	public String getResourceId () {
		DLNAResource tg=this;
		if(rz_srcobj!=null) {
			tg=rz_srcobj;
		}
		if (tg.getId() == null) {
			return null;
		}
		DLNAResource pa=tg.getParent();
		if (pa != null) {
			return pa.getResourceId() + '$' + tg.getId();
		} else {
			return tg.getId();
		}
	}

	/**
	 * @see #setId(String)
	 * @param id
	 */
	protected void setIndexId(int id) {
		setId(Integer.toString(id));
	}
	public void setIndexId_Subp(int id) {
		//special indexid for sub-page
		setId("@"+Integer.toString(id));
	}

	/**
	 * 
	 * @return the unique id which identifies the DLNAResource relative to it's parent. 
	 */
	public String getInternalId() {
		return getId();
	}

	/**
	 * 
	 * @return true, if this contain can have a transcode folder
	 */
	public boolean isTranscodeFolderAvailable() {
		return true;
	}

	/**Any {@link DLNAResource} needs to represent the container or item with a String.
	 * @return String to be showed in the UPNP client.
	 */
	//public abstract String getName();
	//public abstract String getDpName();	//regzamod
	public String getName () {
		//if(dname2!=null) return dname2;  //1st priority, used in case ex. auto-chapters entlies
		if(dname!=null) return dname; //2nd priority, setted expricitly ex. by pxm/pxf
		return "unknown";
	}
	public String getName0 () {
		//if(dname2!=null) return dname2;  //1st priority, used in case ex. auto-chapters entlies
		//return dname; //2nd priority, setted expricitly ex. by pxm/pxf
		return getName();	
	}
	public void setName (String s) {
		dname=s;
	}
	
	public void setSubName (String s) {
		subname=s;
	}
	public String getSubName () {
		return subname;	
	}
	
	public String getDpName () {	//regzamod
		//return dname;
		return getName();
	};	
	
	public String  getDlnaName (RendererConfiguration r) {	//regzamod
		String name=null;
		int type=2;
		int sfxf=0;
		int maxl= 12;
		String sfx=null;
		boolean delsfx=false;  
		
		/*
		type=0: {DispName(title)}
		type=1: {RealName}
		type=2: {FixedName}  //default
		sfxf=0: no add suffix
		sfxf=1: add suffix (for Some fool Android player that judge media type by suffix of dlna Media url)
		maxl={max name len}: <=0: means full length
		*/
		
		if(r!=null) {
			int np[]=null;
			np=r.getRZ_DlnaNameType();
		    type=np[0];
			sfxf=np[1];
			maxl=np[2];
		}
		if(type==0) {	//	name for display (Original)
			name=getName();
			delsfx=true;  //suffix may exists 
		}
		else if(type==1) {  //real file name
			name=getName0();
			delsfx=true;  //suffix may exists 
		}
		else {  //type==2: Fixed Short Name
			name="File";
		}
		if(sfxf==1) {  //add suffix
			if(getPlayer()==null) {  //direct
				if(getExt()!=null) {
					sfx="."+getExt().getMatchedId();
				}
				else {
					//will never come here
					sfx=".mpg";
				}
			}
			else if(is_aav()||is_iav()) {  // Audio/Image as Video
				sfx=".mpg";
			}
			else if(sotype==SO_VIDEO_STREAM) {
				sfx=".mpg";
			}
			else if(getExt()!=null) {
				if(getExt().isVideo()) {
					sfx=".mpg";
				}
				else if(getExt().isImage()) {
					sfx=".jpg";
				}
				else if(getExt().isAudio()) {
					sfx=".ac3";
				}
			}
			else {
				//will never come here
				sfx=".mpg";
			}
		}		
		if(sfx!=null) {
			if(delsfx) {  //suffix may exists in name
				//delete original suffix
				name=FileUtil.getFileNameWithoutExtension(name);
				name=FileUtil.getFileNameWithoutExtension(name);  // may be double suffixed ex. xxx.mp4.pxm
			}
			name=name+sfx;
		}
		if(maxl>0) {
			int len=name.length();
			if(len>maxl) {
				name=name.substring(len-maxl);
			}
		}
		return name;
	}
	
	public String  getSrcPath() {	//regzamod
		return (String)null;
		//return "Unknown://"+getName();
	}
	
	public boolean isUnderWebFolder() {	//regzamod, add
		if(under_web_folder<0) {  //not checked yet
			getResumePath();//for new check 
		}
		return (under_web_folder==0?false:true);
	}

	public String  getResumePath() {	//regzamod
		if(under_web_folder>=0) {  // checked
			if(PMS.rz_debug>1) PMS.dbg("DLNAResource.getResumePath: Already got, dlna="+getName0()+", ResumePath="+resume_path);
			return resume_path;
		}
		
		under_web_folder=0;
		boolean isWebStream=(this instanceof WebStream);
		String path=null;
		if(isWebStream || (mflags & DLNAResource.MF_WEB_FOLDER)!=0) { //webstream or webfolder
			DLNAResource pa=this;
			if(isWebStream) pa=getParent();
			while (pa!=null) {
				if((pa.mflags & DLNAResource.MF_WEB_RESUME_FOLDER)!=0) {
					under_web_folder=1;
					path=pa.getDlnaPath();
					break;
				}
				pa=pa.getParent();
			}
			if(path!=null) {  //web_resume_folder found
				if(isWebStream) path=path+"/dummy";
			}
			else {
				path=getSrcPath();
			}
		}
		else {
			path=getSrcPath();
		}
		resume_path=path;
		if(PMS.rz_debug>1) PMS.dbg("DLNAResource.getResumePath: New got, dlna="+getName0()+", ResumePath="+resume_path);
		return resume_path;
	}
	
	public String  getDlnaPath() {	//regzamod
		if(dlna_path!=null) return dlna_path;
		
		DLNAResource pa=getParent();
		String path=getName0();
		while (pa!=null) {
			path=pa.getName0()+"/"+path;
			pa=pa.getParent();
		}
		dlna_path="/"+path;
		if(PMS.rz_debug>1) PMS.dbg("DLNAResource.getDlnaPath: got path="+dlna_path);
		return dlna_path;
	}
	
	public File getBodysParentFolder() { //get target file's parent folder
		return (File)null;
	}
	public int  getTitleNum() {	//regzamod
		return 0;	//no title
	}

	public abstract String getSystemName();

	public abstract long length();

	// Ditlew
	public long length(RendererConfiguration mediaRenderer) {
		return length();
	}

	public abstract InputStream getInputStream() throws IOException;

	public abstract boolean isFolder();
	
	public File getFile() {
		return null;
	}

	public boolean isRealFile() { //regzam
		return false;
	}

	public String getDlnaContentFeatures() {
		return (dlnaspec != null ? (dlnaspec + ";") : "") + getFlags() + ";DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000";
	}
	public String getDlnaContentFeaturesCI1() {	//regzamod
		return (dlnaspec != null ? (dlnaspec + ";") : "") + getFlags() + ";DLNA.ORG_CI=1;DLNA.ORG_FLAGS=01700000000000000000000000000000";
	}

	public DLNAResource getPrimaryResource() {
		return first;
	}

	public DLNAResource getSecondaryResource() {
		return second;
	}

	public String getFakeParentId() {
		return fakeParentId;
	}

	public void setFakeParentId(String fakeParentId) {
		this.fakeParentId = fakeParentId;
	}

	/**
	 * @return the fake parent id if specified, or the real parent id
	 */
	public String getParentId() {
		if (getFakeParentId() != null) {
			return getFakeParentId();
		} else {
			if (getParent() != null) {
				return getParent().getResourceId();
			} else {
				return "-1";
			}
		}
	}

	public DLNAResource () {
		setSpecificType(Format.UNKNOWN);
		setChildren(new ArrayList<DLNAResource>());
		setUpdateId(1);
	}

	public DLNAResource (int specificType) {
		this();
		setSpecificType(specificType);
	}

	/** Recursive function that searchs through all of the children until it finds
	 * a {@link DLNAResource} that matches the name.<p> Only used by
	 * {@link net.pms.dlna.RootFolder#addWebFolder(File webConf)
	 * addWebFolder(File webConf)} while parsing the web.conf file.
	 * @param name String to be compared the name to.
	 * @return Returns a {@link DLNAResource} whose name matches the parameter name
	 * @see #getName()
	 */
	public DLNAResource searchByName(String name) {
		for (DLNAResource child : getChildren()) {
			if (child.getName().equals(name)) {
				return child;
			}
		}
		return null;
	}
	
		public DLNAResource searchByName0(String name) {
		for (DLNAResource child : getChildren()) {
			if (child.getName0().equals(name)) {
				return child;
			}
		}
		return null;
	}

	/**
	 * @param renderer Renderer for which to check if file is supported.
	 * @return true if the given {@link net.pms.configuration.RendererConfiguration
	 *		RendererConfiguration} can understand type of media. Also returns true
	 *		if this DLNAResource is a container.
	 */
	public boolean isCompatible(RendererConfiguration renderer) {
		return getExt() == null
			|| getExt().isUnknown()
			|| (getExt().isVideo() && renderer.isVideoSupported())
			|| (getExt().isAudio() && renderer.isAudioSupported())
			|| (getExt().isImage() && renderer.isImageSupported());
	}
	
	/**Adds a new DLNAResource to the child list. Only useful if this object is of the container type.<P>
	 * TODO: (botijo) check what happens with the child object. This function can and will transform the child
	 * object. If the transcode option is set, the child item is converted to a container with the real
	 * item and the transcode option folder. There is also a parser in order to get the right name and type,
	 * I suppose. Is this the right place to be doing things like these? 
	 * @param child DLNAResource to add to a container type.
	 */
	public void addChild (DLNAResource child) {
		// child may be null (spotted - via rootFolder.addChild() - in a misbehaving plugin
		if (child == null) {
			logger.error("Attempt to add a null child to " + getName(), new NullPointerException("Invalid DLNA resource"));
			return;
		}
		
		if(PMS.rz_debug>3) {
			PMS.dbg("==== DLNAResource.addChild Start: my name="+getName()+
				", Renderer="+getDefaultRenderer()+", player="+getPlayer());
			PMS.dbg("DLNAResource.addChild: child name="+child.getName()+
				", Renderer="+child.getDefaultRenderer()+", player="+child.getPlayer()+", media="+child.media);
		}
			
		child.setParent(this);
		
		if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-1: name="+child.getName());
		if(PMS.rz_debug>3) PMS.dbg("addChild: name="+child.getName()+", my currentSession="+currentSession);
		child.setCurrentSession(currentSession);
		child.setDefaultRenderer(getDefaultRenderer());
		
		if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-2: name="+child.getName());

		/* origin
		if (getParent() != null) {
			setDefaultRenderer(getParent().getDefaultRenderer());
		}
		*/
		if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-3: name="+child.getName());
		if (getParent() != null) {
			if(getParent().getDefaultRenderer()!=null) {
				setDefaultRenderer(getParent().getDefaultRenderer());
			}
			else {
				//don't set null-parent-renderer
				//for linked object, getParent() may not be real parent
				if(PMS.rz_debug>2) PMS.dbg("addChild: parent="+getParent().getName()+", parent.renderer==null");
			}
		}
		//child.setDefaultRenderer(getDefaultRenderer());

		if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-4: name="+child.getName());
		try {
			if (child.isValid()) {
				if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-41: name="+child.getName());
				//logger.trace("Adding " + child.getName() + " / class: " + child.getClass().getName());
				VirtualFolder vf = null;

				if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-42: name="+child.getName());
				if (allChildrenAreFolders && !child.isFolder()) {
					allChildrenAreFolders = false;
				}
				
				/*
				if(getParent()!=null && menu_type>=3 && child.isFolder()) {
					//disp menues, even if only folders
					createSortMenu(this);
					createClearFolderCahceMenu(this);
				}
				*/
				//moved after creation controll-folders, for default sort order(: objectId order)
				//addChildInternal(child);  

				if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-5: name="+child.getName());
				
				boolean forceTranscodeV2 = false;
				boolean parserV2 = child.getMedia() != null && getDefaultRenderer() != null 
					&& getDefaultRenderer().isMediaParserV2();
				
				if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-6: name="+child.getName());
				
				if (parserV2) {
					// We already have useful info, just need to layout folders
					String mimeType = getDefaultRenderer().getFormatConfiguration().match(child.getMedia());
					if (mimeType != null) {
						// This is streamable
						child.getMedia().setMimeType(mimeType.equals(FormatConfiguration.MIMETYPE_AUTO) ? child.getMedia().getMimeType() : mimeType);
					} else {
						// This is transcodable
						//if(PMS.rz_debug>1) PMS.dbg("==== WARNING!!  Test Set forceTranscodeV2=false");
						forceTranscodeV2 = true;
					}
				}
				
				if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-7: name="+child.getName());

				if (child.getExt() != null) {
					child.setSkipTranscode(child.getExt().skip(PMS.getConfiguration().getNoTranscode(), getDefaultRenderer() != null ? getDefaultRenderer().getStreamedExtensions() : null));
					//PMS.dbg("child.getSkipTranscode="+child.isSkipTranscode());
				}

				Player pl = null;	// regzamod moved upper 
				int ures=(isUnderResumeFolder(this)?1:0);// regzamod 
				
				if(PMS.rz_debug>1) {
					PMS.dbg("==== DLNAResource.addChild pos-8: name="+child.getName());
					PMS.dbg("DLNAResource.addChild: ch_name="+child.getName()+", is_aav="+child.is_aav()+", is_ia="+child.is_iav());
				}
				
				if(PMS.rz_debug>3) {
					PMS.dbg("==== Begin serach player for video");
					PMS.dbg("child.getExt()="+child.getExt());
					if(child.getExt()!=null) {
						PMS.dbg("child.getExt().transcodable()="+child.getExt().transcodable());
					}
					PMS.dbg("parserV2="+parserV2+", child.getMedia()="+child.getMedia());
				}
				
				if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-9: name="+child.getName());
				boolean rejudge_player=false;
				
				if(child.is_aav()) { //regzamod, for AudioAsVideo
					//audio specific title(:songname) must be fixed now! before list sorting in getDLNAResources()
					//PMS.dbg("addChild: seems aav, re-check after resolve");
					child.resolve();	//need before following checks
					rejudge_player=true;
				}
				
				if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-10: name="+child.getName());
				
				if(child.is_aav()) { //regzamod, for AudioAsVideo
					if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-10a(aav): name="+child.getName());
					//PMS.dbg("addChild: desided aav!");
					DLNAMediaAudio firstAudioTrack = child.getMedia() != null ? child.getMedia().getFirstAudioTrack() : null;
					if(firstAudioTrack != null && StringUtils.isNotBlank(firstAudioTrack.getSongname())) {
						String artist=firstAudioTrack.getArtist();
						if(artist!=null && artist.length()>0) artist=artist+" - ";
						else artist="";
						String name=artist + firstAudioTrack.getSongname();
						//PMS.dbg("AddChild: aav setName="+name);
						child.setName(name);
					}
					pl=PMS.get().getPlayer("rz_audio_as_video_tc");
					child.setPlayer(pl);
					if(isTranscodeFolderAvailable() && child.isTranscodeFolderAvailable()) {  
						//indicate normal folder (i.e. not controll menu folder )
						createSortMenu(this,ures);
						createResumeFolder(this,ures);
						createScriptMenuFolder(this,ures);
						createClearFolderCahceMenu(this,ures);
						createSelClipPath(this,ures);
						createVideoSettingMenu(this);
					}
					//if(PMS.rz_debug>1) PMS.dbg("addChild :name="+child.getName()+", doSort="+doSort+", sort_enabled="+sort_enabled);
				}
				else if(child.is_iav()) { //regzamod, for ImageAsVideo
					if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-10b(iav): name="+child.getName());
					pl=PMS.get().getPlayer("rz_image_as_video_tc");
					child.setPlayer(pl);
					if(isTranscodeFolderAvailable() && child.isTranscodeFolderAvailable()) {  
						//indicate normal folder (i.e. not controll menu folder )
						createSortMenu(this,ures);
						createResumeFolder(this,ures);
						createScriptMenuFolder(this,ures);
						createClearFolderCahceMenu(this,ures);
						createSelClipPath(this,ures);
						createVideoSettingMenu(this);
					}
				}
				else if (child.getExt() != null 
					&& (child.getExt().transcodable() || parserV2) 
					&& (child.getMedia() == null || rejudge_player || parserV2 || child instanceof WebStream)) {
					
					if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-10c (normal video/image/audio): name="+child.getName());
					if(PMS.rz_debug>3) PMS.dbg("begin serach player for video");
					//if (!parserV2) {
					if (child.getMedia() == null) {
						child.setMedia(new DLNAMediaInfo());
					}

					// Try to determine a player to use for transcoding. 
					//Player pl = null;	// regzamod moved upper

					if(PMS.rz_debug>3) {
						PMS.dbg("child.getExt().getProfiles()="+child.getExt().getProfiles());
						if(child.getExt().getProfiles()!=null) {
							PMS.dbg("child.getExt().getProfiles().size()="+child.getExt().getProfiles().size());
						}
					}
					if (child.getExt().getProfiles() != null && child.getExt().getProfiles().size() > 0) {
						// First try to match a player based on the format profiles.
						
						if(PMS.rz_debug>3) PMS.dbg("Begin serach-1 player for video");
						int i = 0;

						while (pl == null && i < child.getExt().getProfiles().size()) {
							pl = PMS.get().getPlayer(child.getExt().getProfiles().get(i), child.getExt());
							i++;
						}
						if(PMS.rz_debug>3) PMS.dbg("End serach-1 player ="+pl);

						// Next, try to match a player based on the name of the DLNAResource.
						// When a match is found it overrules the result of the first try.
						String name = getName();

						if(PMS.rz_debug>3) PMS.dbg("Begin serach-2 player for video");
						for (Class<? extends Player> clazz : child.getExt().getProfiles()) {
							for (Player p : PMS.get().getPlayers()) {
								if (p.getClass().equals(clazz)) {
									String end = "[" + p.id() + "]";

									if (name.endsWith(end)) {
										nametruncate = name.lastIndexOf(end);
										pl = p;
										break;
									} else if (getParent() != null && getParent().getName().endsWith(end)) {
										getParent().nametruncate = getParent().getName().lastIndexOf(end);
										pl = p;
										break;
									}
								}
							}
						}
						if(PMS.rz_debug>3) PMS.dbg("End serach-2 player="+pl);
					}
					if(PMS.rz_debug>3) PMS.dbg("Finally serach-end player="+pl);

					if (pl != null && !allChildrenAreFolders) {
						boolean forceTranscode = false;
						if (child.getExt() != null) {
							forceTranscode = child.getExt().skip(PMS.getConfiguration().getForceTranscode(), getDefaultRenderer() != null ? getDefaultRenderer().getTranscodedExtensions() : null);
						}

						boolean hasEmbeddedSubs = false;
						if (child.getMedia() != null) {
							for (DLNAMediaSubtitle s : child.getMedia().getSubtitlesCodes()) {
								hasEmbeddedSubs |= s.getSubType().equals("Embedded");
							}
						}

						// Force transcoding if
						// 1- MediaInfo support detected the file was not matched with supported codec configs and no SkipTranscode extension forced by user
						// or 2- ForceTranscode extension forced by user
						// or 3- FFmpeg support and the file is not ps3 compatible (need to remove this ?) and no SkipTranscode extension forced by user
						// or 4- There's some sub files or embedded subs to deal with and no SkipTranscode extension forced by user

						if(PMS.rz_debug>3) {
							PMS.dbg("Begin Child.setPlayer judge, player="+pl);
							PMS.dbg("forceTranscode="+forceTranscode
								+", isSkipTranscode()="+child.isSkipTranscode()
								+", forceTranscodeV2="+forceTranscodeV2
								+", parserV2="+parserV2
								+", child.isSrtFile()="+child.isSrtFile()
								+", hasEmbeddedSubs="+hasEmbeddedSubs
							);
						}
						//if (forceTranscode || !isSkipTranscode() 
						//	&& (forceTranscodeV2 || (!parserV2 && !child.getExt().ps3compatible()) 
						//	|| (PMS.getConfiguration().getUseSubtitles() && child.isSrtFile()) || hasEmbeddedSubs)) {
						if (!parserV2 && (forceTranscode ||!child.isSkipTranscode()) 
							|| forceTranscodeV2 
							|| (PMS.getConfiguration().getUseSubtitles() && child.isSrtFile()) 
							|| hasEmbeddedSubs
							) {
								
							if(PMS.rz_debug>3) PMS.dbg("set player-1 done");
							child.setPlayer(pl);
							logger.trace("Switching " + child.getName() + " to player: " + pl.toString());
						}
						if(PMS.rz_debug>3) PMS.dbg("End Child.setPlayer judge, child.getPlayer="+child.getPlayer());

						//regzamod, memory suitable player for later judge of force-transcoding
						child.player2=pl;
						if(child.getPlayer()==null) {
							child.trans_type_org=PMS.ENC_DIRECT; //memory judge results
						}
						
						//if (sotype!= SO_BUTTON_ELEMENT && child.getExt().isVideo()) {
						if ((mflags&MF_BUTTON_ELEMENT)==0) {
							if(child.getExt().isVideo()) {
								if(child.isTranscodeFolderAvailable()) {
									vf = getTranscodeFolder(true,-1);
								}
								if(isTranscodeFolderAvailable() && child.isTranscodeFolderAvailable()) {  
									//indicate normal folder (i.e. not controll menu folder )
									createSortMenu(this,ures);
									createResumeFolder(this,ures);
									createScriptMenuFolder(this,ures);
									createClearFolderCahceMenu(this,ures);
									createKeyExtructMenu(this,ures);
									createSelClipPath(this,ures);
									createVideoSettingMenu(this);
								}
								if (vf != null) {
									//Pif(PMS.rz_debug>1) MS.dbg("addChild: add transcode getName()="+child.getName());
									
									VirtualFolder fileFolder = new FileTranscodeVirtualFolder(child.getName(),child.getName0(), null,child);
									//VirtualFolder fileFolder = new FileTranscodeVirtualFolder(child.getDisplayName(), null);
									//make sort order(date/id) same as target child
									fileFolder.setLastmodified(child.getLastmodified());	//add, regzamod
									fileFolder.setSerialId(child.getSerialId());	//add, regzamod
									
									if (child.getExt().isVideo()) {
										/*
										DLNAResource newChild = (DLNAResource) child.clone();
										newChild.setId(null);
										newChild.setPlayer(pl);
										newChild.setMedia(child.getMedia());
										// newChild.original = child;
										fileFolder.addChildInternal(newChild);
										logger.trace("Duplicate " + child.getName() + " with player: " + pl.toString());										*/
										vf.addChild(fileFolder);
									}
								}
							}
						}

						for (ExternalListener listener : ExternalFactory.getExternalListeners()) {
							if (listener instanceof AdditionalResourceFolderListener) {
								try {
									((AdditionalResourceFolderListener) listener).addAdditionalFolder(this, child);
								} catch (Throwable t) {
									logger.error(String.format("Failed to add additional folder for listener of type=%s", listener.getClass()), t);
								}
							}
						}
					} else if (!child.is_aav()&&!child.is_iav()) {
						if(!child.getExt().ps3compatible() && !child.isFolder()) {
							getChildren().remove(child);
						}
					}
				}
				else {
					if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-10d (unknown media): name="+child.getName());
				}
				
				if(PMS.rz_debug>1) PMS.dbg("==== DLNAResource.addChild pos-12: name="+child.getName());
						
				addChildInternal(child);  //moved here, after createMenues

				
				/* moved upper
				//mediainfo may not resolved yet!! so can't judge force-transcoding now.
				//judge should be done by getDLNAResources() calld from RequestV2.answer()
				//that replys to the request from real renderer.
				//child.player2=pl;	
				//if(pl==null) child.player2=child.getPlayer();
				child.player2=child.getPlayer();
				if(child.player2==null) child.player2=pl;
				if(child.getPlayer()==null) {
					child.trans_type_org=PMS.ENC_DIRECT; //memory judge results
				}
				*/
				if(PMS.rz_debug>3) PMS.dbg("child.getPlayer 2="+child.getPlayer());

				//regzamod, memory suitable player for later judge of force-transcoding
				child.player2=pl;
				if(child.getPlayer()==null) {
					child.trans_type_org=PMS.ENC_DIRECT; //memory judge results
				}

				if (!child.is_aav()&&!child.is_iav()) {
					if(child.getExt() != null && child.getExt().getSecondaryFormat() != null && child.getMedia() != null && getDefaultRenderer() != null && getDefaultRenderer().supportsFormat(child.getExt().getSecondaryFormat())) {
						DLNAResource newChild = (DLNAResource) child.clone();
						newChild.setExt(newChild.getExt().getSecondaryFormat());
						newChild.first = child;
						child.second = newChild;
						if (!newChild.getExt().ps3compatible() && newChild.getExt().getProfiles().size() > 0) {
							newChild.setPlayer(PMS.get().getPlayer(newChild.getExt().getProfiles().get(0), newChild.getExt()));
						}
						if (child.getMedia() != null && child.getMedia().isSecondaryFormatValid()) {
							addChild(newChild);
						}
					}
				}
			}
		}catch (Throwable t) {
			logger.error(String.format("Failed to add child '%s'", child.getName()), t);

			child.setParent(null);
			getChildren().remove(child);
		}
		
		if(PMS.rz_debug>3) {	//regzamod2
			PMS.dbg("DLNAResource.addChild End: child name="+child.getName()+
				", Renderer="+child.getDefaultRenderer()+", player="+child.getPlayer()+", media="+child.media);
		}
	}

	public VirtualFolder getCtlFolder(boolean create, int ures) {
		//ures =1:isUnderResumeFolder
		if(!allowCtlFolder) return null;
		if(create && ctlFolder==null) {
			String name;
			if(ures==1 || (ures<0 && isUnderResumeFolder(this))) {
				name="#- RESUME_CONTROLLS -#";
			}
			else {
				name="#- CONTROLLS -#";
			}
			ctlFolder=new VirtualFolder(name, null);
			ctlFolder.sotype=SO_CONTROLLS;
			ctlFolder.sort_immune=1;
			ctlFolder.sort_enabled=true;
			ctlFolder.rz_MetaSortType=PMS.SORT_ORIGIN;
			addChildInternal(ctlFolder);
		}
		return (VirtualFolder)ctlFolder;
	}
	
	/**
	 * Return the transcode virtual folder if it's supported and allowed. If create set to true, it tries to create if not yet created.
	 * @param create
	 * @return
	 */
	TranscodeVirtualFolder getTranscodeFolder(boolean create,int ures) {
		DLNAResource dlna=this;
		String name;
		if(this instanceof rz_ResumeFolder) {
			DLNAResource pa=getParent();
			if(pa!=null) pa=pa.getParent();
			if(pa!=null && pa instanceof rz_ResumeFolder) {	
				//avoid recursive loop 
				return null;
			}
		}
		if (!isTranscodeFolderAvailable()) {
			return null;
		}
		if (PMS.getConfiguration().getHideTranscodeEnabled()) {
			return null;
		}
		
		// search for transcode folder
		if(transFolder!=null) {
			return (TranscodeVirtualFolder)transFolder;
		}
		if (create) {
			if(ures==1 || (ures<0 && isUnderResumeFolder(dlna))) {
				name=RESUME_TRANSCODE_FOLDER;
				ures=1;
			}
			else {
				name=TRANSCODE_FOLDER;
				ures=0;
			}
			TranscodeVirtualFolder vf = new TranscodeVirtualFolder(name,getName0(),null);
			vf.setSerialId(MENU_ORD_TRANS);
			transFolder=vf;	//regzamod, memory for quick accsess
			//vf.sotype=SO_VFOLDER;
			//vf.sort_immune=1;
			vf.sort_enabled=sort_enabled;
			vf.rz_MetaSortType=rz_MetaSortType;
			if(menu_type>=2) { // newType
				dlna=getCtlFolder(create,ures);
			}
			if(dlna==null) return null;
			dlna.addChildInternal(vf);
			return vf;
		}
		return null;
	}

	/**
	 * Add to the internal list of child nodes, and sets the parent to the
	 * current node.
	 * 
	 * @param res
	 */
	protected synchronized void addChildInternal (DLNAResource res) {
		addChildInternal(res, -1);
		
	}
	protected synchronized void addChildInternal (DLNAResource res, int pos) {
		if(pos== -1) {  //add last
			getChildren().add(res);
		}
		else {
			getChildren().add(pos,res);
		}
		res.setParent(this);
		
		if(PMS.rz_debug>3) PMS.dbg("addChildInternal: myName="+getName()+", child="+res.getName()+", currentSession="+currentSession);
		res.setCurrentSession(currentSession);
		if(getDefaultRenderer()!=null) {
			res.setDefaultRenderer(getDefaultRenderer());
		}
		
		if (res.getInternalId() == null) {
			setLastChildrenId(getLastChildrenId() + 1);
			res.setIndexId(getLastChildrenId());
			if(res.serialId<=0) res.setSerialId(getLastChildrenId());  //regzamod, default val for SORT_ORIGIN: not allways equals to IndexID
		}
	}

	/**
	 * First thing it does it searches for an item matching the given objectID.
	 * If children is false, then it returns the found object as the only object in the list.
	 * TODO: (botijo) This function does a lot more than this!
	 * @param objectId ID to search for.
	 * @param returnChildren State if you want all the children in the returned list.
	 * @param start
	 * @param count
	 * @param renderer Renderer for which to do the actions.
	 * @return List of DLNAResource items. 
	 * @throws IOException
	 */
	//String prev_objectId;
	
	//--- Really, getDLNAResources is called Only for RootFolder (see network/RequestV2.java)
	public synchronized List<DLNAResource> getDLNAResources (String objectId, boolean returnChildren, int start, 
		int count, RendererConfiguration renderer,rz_SessionInfo sess) throws IOException {

		long tim_start = System.currentTimeMillis();
		long tim0,tim1;
		long prev_tim_sv=prev_tim;
		prev_tim=tim_start;

		if(PMS.rz_trace>0) {
			PMS.trace("==== getDLNAResources: "
				+"objectId="+objectId+", returnChildren="+returnChildren+", start="+start+", count="+count);
			if(PMS.rz_debug>1) PMS.dbg("DLNAResource.getDLNAresourecs: sess="+sess);
		}
			
		ArrayList<DLNAResource> resources = new ArrayList<DLNAResource>();
		int stc[]={start,count};
		DLNAResource resource = search(objectId,start,renderer,returnChildren,stc);
		start=stc[0];
		//count=stc[1];
			
		if(PMS.rz_trace>0) {
			//must be after search(), to disp resource info
			PMS.trace("getDLNAResources: Folder="+(resource==null?"null":resource.getName()));
		}

		if (resource != null) {
			if(resource.rz_linkobj!=null) {
				resource=resource.rz_linkobj;
			}
			
			//---- regzamod, pre-process before getresource(children) end --------
			resource.setDefaultRenderer(renderer);
			resource.setCurrentSession(sess);  //regzamod
			
			if(PMS.rz_debug>2) PMS.dbg("getDLNAResources: target resource="+resource.getName()); 

			if (!returnChildren) {
				//if(PMS.rz_debug>1) PMS.dbg("getDLNAResources: resource.activate(0)="+resource); 
				resource.activate(0); //trigger specific action to
				resources.add(resource);
				if(PMS.rz_debug>1) PMS.dbg("getDLNAResources: End, returnChildren is false, so Skip refreshChildrenIfNeeded()!!");
				return resources;
				//resource.refreshChildrenIfNeeded();	//original
			} else {
				if(start==0 && prev_dlna!=resource || prev_tim_sv+1000>tim_start) {
					resource.activate(1); //trigger specific action to
					prev_dlna=resource;
				}
				//resource.discoverWithRenderer(renderer, count,returnChildren);	//regzamod

				if (count == 0) {
					count = resource.getChildren().size();
				}
				if (count > 0) {
					ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(count);
					int parallel_thread_number=PMS.rz_media_parse_thread;
					
					//if (resource instanceof DVDISOFile || resource.onBDMV==1) {
					if (resource instanceof DVDISOFile || resource.isOnSlowMedia()) {
						if(PMS.rz_debug>1) PMS.dbg("getDLNAResourecs: file seems on slow seek Media(DVD/BD), exec selial-parse instead of pararell");
						parallel_thread_number = 1; // my dvd drive is dying wih 3 parallel threads 
					}
					ThreadPoolExecutor tpe = new ThreadPoolExecutor(Math.min(count, parallel_thread_number), count, 20, TimeUnit.SECONDS, queue);

					if(resource.needSort() && resource.doSort) {
						if(PMS.rz_debug>2) PMS.dbg("getDLNAResources: name="+resource.getName()+", exec dlnaListSort");
						MapFile.dlnaListSort(resource,resource.getChildren(),resource.getSortType());
						resource.doSort=false;
					}
					
					//regzam, revised for the case hidden objects exist
					int size=resource.getChildren().size();
					int cnt=0;
					//int cnt_max=start+count;
					int cnt_max=start+stc[1];
					
					for (int i =0; i < size; i++) {
					    //i may not equal to index in reply list,
					    //if you have some hidden objects to reply
						final DLNAResource child = resource.getChildren().get(i);
						if(child == null || (child.mflags&MF_HIDE)!=0) {
							continue;
						}
						cnt++;  //real index in reply list, except hidden objs
						if(cnt>start && cnt<=cnt_max) {
							tpe.execute(child);  // call child's run() method (see DLNAResource.run())
							resources.add(child);
						}
					}
					try {
						tpe.shutdown();
						tpe.awaitTermination(20, TimeUnit.SECONDS);
					} catch (InterruptedException e) {
					}
					logger.trace("End of analysis");
				}
			}
		}

		//Now, mediainfo must be resolved already 
		int res_size=resources.size();
		if(PMS.rz_debug>2) {
			PMS.dbg("getDLNAResources: renderer="+renderer+", resources.size()="+res_size);
		}

		//---- strict judge remuxable ----------------------------------
		long RendererScriptLastModified=0;
		boolean scripts_exist=(PMS.rz_is_rend_script_enabled && (sess.scex!=null));
		if(scripts_exist) {
			RendererScriptLastModified=sess.scex.getRz_ScriptLastModified();
		}
		
		tim0 = System.currentTimeMillis();
		for(int i=0;i<res_size;i++) {
			DLNAResource dlna=resources.get(i);
			if(!dlna.isFolder()) {
				if(scripts_exist && dlna.ScriptLastModified>0 && RendererScriptLastModified > dlna.ScriptLastModified) {
					//script updated, need re-judge --> reset to origin before re-judge
					if((dlna.mflags&MF_UNDER_TRASCO_FOLDER)==0) { //not under trascofolder
						dlna.player=dlna.player2;
						if(dlna.trans_type_org==PMS.ENC_DIRECT) dlna.player=null;
					}
				}
				else {
					if((dlna.mflags&MF_REMUX_JUDGED)!=0) continue;
				}
				if(dlna.externalProcess!=null) {
					if(PMS.rz_debug>2) PMS.dbg("===>>> getDLNAResources: externalProcess Not NULL, --> set NULL");
					//dlna.externalProcess=null;	//danger!! proc may not stopped yet
				}
				if(!(dlna.is_aav()||dlna.is_iav())) {
					judgeTranscodePerRenderer(resources.get(i),renderer);
				}
				//if(dlna.player==null) dlna.trans_type_org=PMS.ENC_DIRECT;  //memory judge
				dlna.mflags |=MF_REMUX_JUDGED;
			}
		}
		tim1 = System.currentTimeMillis();
		if(PMS.rz_debug>1) PMS.dbg("getDLNAResources: judgeTranscodePerRenderer ElapsedTime(sec)="+(tim1-tim0)/1000.0);
		tim0=tim1;
		
		//---- exec scripts -----------------------------------
		if(scripts_exist) {  //test
			String opt=null; //options by renderer.conf
			long now_time=System.currentTimeMillis();
			int target_cnt=0;
			int exec_cnt=0;
			
			rz_ScriptExecutor.medAddOpts medAddOpts=new rz_ScriptExecutor.medAddOpts();
			for(int i=0;i<res_size;i++) {
				DLNAResource dlna=resources.get(i);
				
				if((dlna.mflags&MF_UNDER_TRASCO_FOLDER)!=0) {
					//Generally, spec of object defined under TrascoFolder shouldn't be changed by scripts
					//b/c conflicts with transcode folder's spec  
					
					//ToDo: but, overriding by scripts will be better, except player_types(Direct/Remux/Trasco)
					//b/c transcode folders spec is ONLY forceing specific player to play
					//continue;  
				}
				
				if(PMS.rz_debug>3) {
					PMS.dbg("getDLNAResources.exec_scripts: dlna="+dlna.getName()+", RendererScriptLastModified="+RendererScriptLastModified
					 +", dlna.ScriptLastModified="+dlna.ScriptLastModified);
				}
				if(RendererScriptLastModified <= dlna.ScriptLastModified) {
					if(PMS.rz_debug>3) PMS.dbg("getDLNAResources.exec_scripts: NOT Exec (Time indicates Already done)");
					continue; //already executed
				}
				if(PMS.rz_debug>3) PMS.dbg("getDLNAResources.exec_scripts: Exec");
				target_cnt++;
				exec_cnt++;
				
				/*
				execScripts(type,when,taget,dlna,..)
				public int type;  			//=1:load,=2:exec
				public int when;			//=1:list,=2:play,  =3:list|play,  =4:oneshot
				public int target;			//=1:file,=2:folder,=3:file|folder,=4:other
				*/
				//---- init script output values, before exec scripts
				resetMetaOpts(dlna,1);  //Bug1: has influence on TrascoFolders
				dlna.player3=null;  //Bug1: has influence on TrascoFolders
				
				if(dlna.player!=null) {
					medAddOpts.trans_type="trans";  // where is remux?
					medAddOpts.player=dlna.player.toString();
				}
				else {
					medAddOpts.trans_type="direct";
					medAddOpts.player="direct";
				}
				
				String name=dlna.getSrcPath();
				if(name==null) name=dlna.getName();
				
				//---- Now exec Scripts body
				sess.scex.execScripts(2,1,3,dlna,name,null,medAddOpts);  //Bug1: has influence on TrascoFolders
				
				if((dlna.mflags&MF_UNDER_TRASCO_FOLDER)==0) { //not under trascofolder
					if(dlna.player3!=null) {
						dlna.player=dlna.player3; //forced player  //Bug1: has influence on TrascoFolders
					}
				}
				if(PMS.rz_debug>3) PMS.dbg("getDLNAResources.exec_scripts: result rz_MetaTitle[1]="+dlna.rz_MetaTitle[1]);
				dlna.ScriptLastModified=now_time;  //Bug1: has influence on TrascoFolders
				dlna.toString_sb=null;   //set toString() not resolved
			}
			
			if(PMS.rz_debug>1) {
				tim1 = System.currentTimeMillis();
				PMS.dbg("getDLNAResources.exec_scripts: ElapsedTime(sec)="+(tim1-tim0)/1000.0
					+", total_cnt="+resources.size()
					+", target_cnt="+target_cnt
					+", executed_cnt="+exec_cnt
				);
			}
		}
		
		//---- judge forced here
		for(int i=0;i<res_size;i++) {
			DLNAResource dlna=resources.get(i);
			if(false && dlna.externalProcess!=null) {  //danger
				//PMS.dbg("getDLNAResources: externalProcess remained: purge process memories, dlna="+dlna.getSrcPath());
				dlna.externalProcess=null;
			}
			int ForcedTransType=dlna.getForcedTransType();
			if(ForcedTransType>-1 && dlna.getExt()!=null && dlna.getExt().isVideo()) {
				if(ForcedTransType==PMS.ENC_DIRECT) {	//forced direct
					dlna.setPlayer(null);
				}
				else if(ForcedTransType==PMS.ENC_REMUX){
					if(dlna.getPlayer()==null) dlna.setPlayer(dlna.player2);
				}
				else if(ForcedTransType==PMS.ENC_TRANSCODE){
					if(dlna.getPlayer()==null) dlna.setPlayer(dlna.player2);
				}
			}
		}

		if(PMS.rz_trace>0) {
			long tim2 = System.currentTimeMillis();
			PMS.trace("---- getDLNAResources: End, resources_cnt="+res_size+", elapsed="+(tim2-tim_start));
		}
		return resources;
	}
	
	public void setDoSortToSubfolders(DLNAResource pa) { //regzamod
		//set doSort=true to all subfolders of pa, recursively
		long scanid=System.currentTimeMillis();
		setDoSortToSubfolders_in(pa,scanid,0,pa.rz_MetaSortType);
	}
	
	private void setDoSortToSubfolders_in(DLNAResource dlnp,long scanid,int layer,int sort_type) { //regzamod
		if(layer>15) {
			logger.warn("tree_scan: reached too deep layer="+layer+", return to upper");
			return;
		}
		if(dlnp.scanid==scanid) {
			//already scaned? 
			logger.warn("tree_scan: found already scaned object="+dlnp);
			logger.warn("tree_scan: get back, to avoid infinite loop");
			return;
		}
		dlnp.scanid=scanid;
		for (DLNAResource child : dlnp.getChildren()) { 
			if(child.allowScan()) {  //equals to class MapFile -- may be real folder
			//if(child.isFolder()&& child.sotype!=SO_CONTROLLS) {
				//PMS.dbg("setDoSortToSubfolders_in: set doSort=true, child="+child.getName());
				child.doSort=true;
				child.rz_MetaSortType=sort_type;
			}
			if(child.isFolder()) {
				if (child.getChildren().size() <= 0) {
					continue;
				}
				setDoSortToSubfolders_in(child,scanid,++layer,sort_type);
			}
			else { //may be file
				//do something for files
			}
		}
	}

	protected void refreshChildrenIfNeeded() {
		if (isDiscovered()) {
			if (isRefreshNeeded()) {
				refreshChildren();
				notifyRefresh();
			}
		}
	}

	/**
	 * update the last refresh time.
	 */
	protected void notifyRefresh() {
		setLastRefreshTime(System.currentTimeMillis());
		setUpdateId(getUpdateId() + 1);
		setSystemUpdateId(getSystemUpdateId() + 1);
	}

	protected void discoverWithRenderer(RendererConfiguration renderer, int count) {	//original I/F
		discoverWithRenderer(renderer,count,false);
	}
	
	protected void discoverWithRenderer(RendererConfiguration renderer, int count,boolean returnChildren) {	//regzamod
		// Discovering if not already done.
		if(PMS.rz_debug>2) {
			PMS.dbg("discoverWithRenderer: called, renerer="+renderer+", myname="+getName()
				+", isDiscovered="+this.isDiscovered());
		}

		if (!isDiscovered()) {
			discoverChildren();
			boolean ready = true;
			if (renderer.isMediaParserV2() && renderer.isDLNATreeHack()) {
				ready = analyzeChildren(count);
			} else {
				ready = analyzeChildren(-1);
			}
			if (!renderer.isMediaParserV2() || ready) {
				setDiscovered(true);
			}
			if((needSort())) {	//test
				//PMS.dbg("discoverWithRenderer: name="+getName()+", Not Discovered yet --> Set doSrt=true, sort_type="+getSortType());
				//MapFile.dlnaListSort(getChildren(),getSortType());
				doSort=true;
			}
			//refreshChildren();	//regzamod for test
			notifyRefresh();
		//} else {	//original
		} else if(returnChildren) {	//regzamod
			if (isRefreshNeeded()) {
				//PMS.dbg("discoverWithRenderer: name="+getName()+", isRefreshNeeded()=true --> Set doSrt=true, sort_type="+getSortType());
				refreshChildren();
				notifyRefresh();
				doSort=true;
			}
		}
	}

	@Override
	public void run() {
		if (first == null) {
			resolve();
			if (second != null) {
				second.resolve();
			}
		}
	}

	/**Recursive function that searches for a given ID.
	 * @param searchId ID to search for.
	 * @param renderer 
	 * @param count 
	 * @return Item found, or null otherwise. 
	 * @see #getId()
	 * 
	 */
	public DLNAResource search (String searchId, int start_in
		,RendererConfiguration renderer
		,boolean returnChildren
		,int stc[]) {

		int start=start_in;
		int count=0;
		if(stc!=null) {
			start=stc[0];
			count=stc[1];
		}
		int listmax=renderer.getRZ_PageListMax();
		//PMS.dbg("Search: searchId="+searchId+", rendererName="+renderer.getRendererName()+", listmax="+listmax);
		
		if (getId() != null && searchId != null) {
			String[] indexPath = searchId.split("\\$", 3);
			if(indexPath[0].startsWith("@")) {  //subpage
				if(indexPath.length>1) {  //not tail
					//ignore
					//PMS.dbg("Search: --> specialIndex in between path, ignore it");
					indexPath=indexPath[1].split("\\$", 3);
				}
				else {  //tail
					//PMS.dbg("Search: --> Found specialIndex at tail, return null(not found)");
					return null;  //not found: already should be found
				}
			}
			if(getId().equals(indexPath[0])) {
				if(indexPath.length == 1 || indexPath[1].length() == 0) {
					DLNAResource tg=this; //me
					//last entry is me
					if(returnChildren) {
						if(rz_linkobj!=null) {
							tg=rz_linkobj;
						}
						tg.discoverWithRenderer(renderer, count,returnChildren);
						int ch_cnt=tg.getChildren().size();
						//PMS.dbg("Search: --> Found last entry is me, list_cnt="+ch_cnt+", list_max="+listmax);
						if(listmax>0 && ch_cnt>listmax) {
							if(PMS.rz_trace>0) PMS.trace("Search: --> Found list_cnt>max, return folder for multi-pages");
							//int np=(int)(ch_cnt/listmax+1);
							return renderer.getRootFolder().getMultiPageFolder(this,ch_cnt,listmax);
						}
						else {
							//PMS.dbg("Search: --> Found last entry is me, and list=<max, normally return me");
							return this;
						}
					}
					else {
						return this;
					}
				}
				else if(indexPath.length == 2 && indexPath[1].startsWith("@")) {
					//last-1 entry is me and last entry is subpage
					if(stc!=null) {
						DLNAResource tg=this; //me
						if(rz_linkobj!=null) {
							tg=rz_linkobj;
						}
						String s=indexPath[1].substring(1);
						int spn=Integer.parseInt(s);
						int ch_cnt=tg.getChildren().size();
						stc[0]=listmax*spn+start;
						//assert st[0]+st[1] within limit
						stc[1]=stc[0]+count;
						if(stc[1]>ch_cnt) stc[1]=ch_cnt;
						if(stc[1]>listmax*(spn+1)) stc[1]=listmax*(spn+1);
						//re-set value asserted
						stc[1]=stc[1]-stc[0];
						if(PMS.rz_trace>0) PMS.trace("Search: --> Found subpage index, return with sub-page start="+stc[0]+", count="+stc[1]);
					}
					return this;
				}
				else {
					//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.search: TEST mode force returnChildren=false!");
					//discoverWithRenderer(renderer, count, returnChildren);
					//for don't couse parents refresh when serach obj
					//PMS.dbg("Search: --> normal path, call search recursively");
					String idxp=indexPath[1];
					if(indexPath.length>2) {
						//recover 
						idxp=indexPath[1]+"$"+indexPath[2];
					}
					discoverWithRenderer(renderer, count, false);
					for (DLNAResource dlna : getChildren()) {
						DLNAResource found = dlna.search(idxp, start, renderer, returnChildren, stc);
						if (found != null) {
							return found;
						}
					}
				}
			} else {
				//PMS.dbg("Search: --> not match me, return null");
				return null;
			}
		}
		//PMS.dbg("Search: --> getId() or searchId is Null, return null");
		return null;
	}

	public DLNAResource search_org (String searchId, int count, RendererConfiguration renderer,boolean returnChildren) {
		if (getId() != null && searchId != null) {
			String[] indexPath = searchId.split("\\$", 2);
			if (getId().equals(indexPath[0])) {
				if (indexPath.length == 1 || indexPath[1].length() == 0) {
					return this;
				} else {
					//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.search: TEST mode force returnChildren=false!");
					//discoverWithRenderer(renderer, count, returnChildren);
					//for don't couse parents refresh when serach obj
					discoverWithRenderer(renderer, count, false);
					for (DLNAResource file : getChildren()) {
						DLNAResource found = file.search_org(indexPath[1], count, renderer,returnChildren);
						if (found != null) {
							return found;
						}
					}
				}
			} else {
				return null;
			}
		}
		return null;
	}

	/**
	 * TODO: (botijo) What is the intention of this function? Looks like a prototype to be overloaded.
	 */
	public void discoverChildren() {
	}

	/**
	 * TODO: (botijo) What is the intention of this function? Looks like a prototype to be overloaded.
	 * @param count
	 * @return Returns true
	 */
	public boolean analyzeChildren(int count) {
		return true;
	}

	/**
	 * Reload the list of children
	 */
	public void refreshChildren() {
	}

	/**
	 * 
	 * @return true, if the container is changed, so refresh is needed
	 */
	public boolean isRefreshNeeded() {
		return false;
	}

	protected void checktype() {
		if (getExt() == null) {
			setExt(PMS.get().getAssociatedExtension(getSystemName()));
		}
		if (getExt() != null) {
			if (getExt().isUnknown()) {
				getExt().setType(getSpecificType());
			}
		}
		else {
			if(PMS.rz_debug>1) PMS.dbg("DLNAResource.checktype: getExt()==null, Obj will be ignored, path="+getSystemName());
		}
	}

	/**
	 * Determine all properties for this DLNAResource that are relevant for playback
	 * or hierarchy traversal. This can be a costly operation, so when the method is
	 * finished the property <code>resolved</code> is set to <code>true</code>.
	 */
	public void resolve() { 
	}
	public void resolve2() { 
		//for metafile update
	}
	
	//---- regzamod2 add start
	void judgeTranscodePerRenderer (DLNAResource dlna,RendererConfiguration renderer) { //regzamod2
		//---- for fource-transcoding mpeg/mpeg2 that has sizes not supported by the renderer
		if(renderer==null) {
			return;	//Noop
		}
		boolean direct=false;
		if(renderer.getRZ_MediaJudgeType()==2) {  //judge by mediaInfo (supported=.... in renderer.conf)
			String mimeType = renderer.getFormatConfiguration().match(dlna.getMedia());
			//PMS.dbg("judgeTranscodePerRenderer: renderer.getFormatConfiguration().match returned mimeType="+mimeType
			//	+", mimeType_ORG="+(dlna.getMedia()==null?null:dlna.getMedia().getMimeType()));
			if(mimeType==null) {  //not streamable
				if(dlna.getPlayer()==null && dlna.player2!=null) {
					//PMS.dbg("judgeTranscodePerRenderer: mimeType==null: judged not streamable --> setPlayer");
					dlna.setPlayer(dlna.player2);	// force transcoding with memoried player
					return;
				}
			}
			else {  //streamable
				if(dlna.getMedia()!=null) {
					dlna.getMedia().setMimeType(mimeType.equals(FormatConfiguration.MIMETYPE_AUTO) ? dlna.getMedia().getMimeType() : mimeType);
					//PMS.dbg("judgeTranscodePerRenderer: set mimeType_V2="+dlna.getMedia().getMimeType());
				}
				if(dlna.getPlayer()!=null) {
					if(dlna.player2==null) {
						dlna.player2=dlna.getPlayer();
					}
					//PMS.dbg("MediaJudgeTypeV2: PreJudged as Trans, but Streamable --> Change to Direct, name="+dlna.getName());
					dlna.setPlayer(null);
					direct=true;
				}
			}
		}

		if(dlna.getExt()!=null && dlna.media!=null) {
			if(PMS.rz_debug>2) {
				PMS.dbg("====== judgeTranscodePerRenderer: ObjectId="+dlna.getId()+", getName()="+dlna.getName());
				PMS.dbg("vcodec="+dlna.media.getCodecV());
				PMS.dbg("isVideo()="+dlna.getExt().isVideo());
				PMS.dbg("getPlayer()="+dlna.getPlayer());
				PMS.dbg("suitable player="+dlna.player2);
			}
			//if(dlna.player2==null) {
			//	dlna.player2=dlna.getPlayer();
			//}
			
			
			//int ForcedTransType=getForcedTransType();
			if(dlna.getExt().isVideo()) {
				/* don't judge forced here!
				if(ForcedTransType==PMS.ENC_DIRECT) {	//forced direct
					dlna.setPlayer(null);
				}
				else if(ForcedTransType==PMS.ENC_REMUX) {	//forced remux
					dlna.setPlayer(dlna.player2);
				}
				else if(ForcedTransType==PMS.ENC_TRANSCODE) {	//forced transcode
					dlna.setPlayer(dlna.player2);
				}
				else if(dlna.getPlayer()== null) {	//direct play
				*/
				if(dlna.trans_type_org==PMS.ENC_DIRECT || direct) {	//direct play
					String vcodec=dlna.media.getCodecV();
					int height= dlna.media.getHeight();
					int width= dlna.media.getWidth();
					double aspect=dlna.media.getAspect_f();
					if(PMS.rz_debug>2) {
						PMS.dbg("type=Video & Player=null");
						PMS.dbg("media w="+width+", h="+height+", vcodec="+vcodec+", suitable player="+player2);
					}
					
					boolean forceTrans=!renderer.isRZ_NonTranscodeEx(width,height,aspect,dlna.getExt()==null?null:dlna.getExt().getMatchedId());
					if(PMS.rz_debug>2) PMS.dbg("====== judgeTranscodePerRenderer: isRZ_NonTranscodeEx="+!forceTrans);
					// why?
					/*
					if(!renderer.isMuxH264MpegTS()) {
						if(vcodec!=null && vcodec.startsWith("h264")) {
							forceTrans=true;
						}
					}
					*/
					
					//if(vcodec!=null && height<400 && (vcodec.startsWith("mpeg2")||vcodec.startsWith("mpeg1"))) {
					//if(vcodec!=null && (vcodec.startsWith("mpeg2")||vcodec.startsWith("mpeg1")||vcodec.startsWith("h264"))
					//	&& forceTrans && (dlna.mflags&MF_UNDER_TRASCO_FOLDER)==0) {
					
					if(forceTrans && (dlna.mflags&MF_UNDER_TRASCO_FOLDER)==0) {
						if(PMS.rz_debug>2) {
							PMS.dbg("judgeTranscodePerRenderer: defaultRenderer="+dlna.defaultRenderer);
							PMS.dbg("force transcoding, player="+dlna.player2+", fname="+dlna.getName());
						}
						// REGZA dosn't support the sizes , so force transcode
						if(dlna.player2!=null) {
							dlna.setPlayer(dlna.player2);	// force transcoding with memoried player
						}
					}
				}
			}
		}
	}
	//---- regzamod add end

	// Ditlew
	/**Returns the DisplayName for the default renderer.
	 * @return The display name.
	 * @see #getDisplayName(RendererConfiguration)
	 */
	public String getDisplayName () {
		return getDisplayName(null);
	}

	// Ditlew - org
	//public String getDisplayName() {
	// Ditlew
	/**Returns the DisplayName that is shown to the Renderer. Depending on the settings,
	 * extra info might be appended, like item duration.<p>
	 * This is based on {@link #getName()}.
	 * @param mediaRenderer Media Renderer for which to show information.
	 * @return String representing the item.
	 */
	public String getDisplayName (RendererConfiguration mediaRenderer) {
		String name=null;
		DLNAMediaAudio firstAudioTrack = getMedia() != null ? getMedia().getFirstAudioTrack() : null;
		//PMS.dbg("==== getDisplayName: start, name="+getName()+", firstAudioTrack="+firstAudioTrack);
		if(dname2!=null) {  //for auto_chapter names under transcode_folder 
			name=dname2;
			//PMS.dbg("getDisplayName: type1, name="+name);
		}
		else if(!isNoName() && dname!=null) {
			name=dname;   //for title defined by pxm/pxf
			//PMS.dbg("getDisplayName: type2, name="+name);
		}
		else {
			name = getName();
			//PMS.dbg("getDisplayName: type3, name="+name);
			if(this instanceof RealFile) {
				if (PMS.getConfiguration().isHideExtensions() && !isFolder()) {
					name = FileUtil.getFileNameWithoutExtension(name);
					//PMS.dbg("getDisplayName: type4, name="+name);
				}
			}
			if(getPlayer() != null) {
				if(isNoName()) {
					name = "[" + getPlayer().name() + "]";
					int ttype=getForcedTransType();
					//int ttype=rz_MetaTransType[0];
					if(ttype==PMS.ENC_TRANSCODE) {
						name+= " (Trans)";
					}
					else if(ttype==PMS.ENC_REMUX) {
						name+= " (Remux)";
					}
					else if(ttype==PMS.ENC_DIRECT) {
						name+= " (Direct)";
					}
				} else {
					// Ditlew - WDTV Live don't show durations otherwise, and this is useful for finding the main title
					if (mediaRenderer != null && mediaRenderer.isShowDVDTitleDuration() && getMedia().getDvdtrack() > 0) {
						name += " - " + getMedia().getDurationString();
					}

					if (!PMS.getConfiguration().isHideEngineNames()) {
						name += " [" + getPlayer().name() + "]";
					}
				}
			} else {
				//logger.debug("getDisplayName: getPlayer() ==null");	//regzamod
				if (isNoName()) {
					name = "[No encoding]";
				} else if (nametruncate > 0) {
					name = name.substring(0, nametruncate).trim();
				}
			}
			//if(isSrtFile() && (getMediaAudio() == null && getMediaSubtitle() == null)) {
			if(isSrtFile()) {
				if (getPlayer() == null || getPlayer().isExternalSubtitlesSupported()) {
					name += " {External Subtitles}";
				}
			}
			
			// list under transcode folder
			if(getMediaAudio() != null) {
				name = (getPlayer() != null ? ("[" + getPlayer().name() + "]") : "") + " {Audio: " + getMediaAudio().getAudioCodec() + "/" + getMediaAudio().getLangFullName() + ((getMediaAudio().getFlavor() != null && mediaRenderer != null && mediaRenderer.isShowAudioMetadata()) ? (" (" + getMediaAudio().getFlavor() + ")") : "") + "}";
			}
			if(getMediaSubtitle() != null && getMediaSubtitle().getId() != -1) {
				name += " {Sub: " + getMediaSubtitle().getSubType() + "/" + getMediaSubtitle().getLangFullName() + ((getMediaSubtitle().getFlavor() != null && mediaRenderer != null && mediaRenderer.isShowSubMetadata()) ? (" (" + getMediaSubtitle().getFlavor() + ")") : "") + "}";
			}
			if(isAvisynth()) {
				name = (getPlayer() != null ? ("[" + getPlayer().name()) : "") + " + AviSynth]";
			}

			// should moved to "ChapterFileTranscodeVirtualFolder" , by regzamod
			//if (getSplitRange().isEndLimitAvailable() && !rz_isPartPlay && isNoName()) {
			/*
			if (getSplitRange().isEndLimitAvailable() && isNoName()) {
				name = ">> " + DLNAMediaInfo.getDurationString(getSplitRange().getStart());
			}
			*/
		}
		return name;
		//name=Normalizer.normalize(name, Normalizer.Form.NFD);
		//return Normalizer.normalize(name, Normalizer.Form.NFC);  //good, covers narrow but smart grif
		//return Normalizer.normalize(name, Normalizer.Form.NFKC);  //good, covers wide but some ugly grif
	}

	/**Prototype for returning URLs.
	 * @return An empty URL
	 */
	protected String getFileURL(RendererConfiguration r) {
		return getURL("",r);
	}

	/**
	 * @return Returns an URL pointing to a image representing the item. It none is available, "thumbnail0000.png" is used.
	 * The idea is to use is in the UPNP ContentBrowser service.
	 */
	protected String getThumbnailURL() {
		StringBuilder sb = new StringBuilder();
		sb.append(PMS.get().getServer().getURL());
		sb.append("/images/");
		String id = null;
		if (getMediaAudio() != null) {
			id = getMediaAudio().getLang();
		}
		if (getMediaSubtitle() != null && getMediaSubtitle().getId() != -1) {
			id = getMediaSubtitle().getLang();
		}
		if ((getMediaSubtitle() != null || getMediaAudio() != null) && StringUtils.isBlank(id)) {
			id = DLNAMediaLang.UND;
		}
		if (id != null) {
			String code = Iso639.getISO639_2Code(id.toLowerCase());
			sb.append("codes/").append(code).append(".png");
			return sb.toString();
		}
		if (isAvisynth()) {
			sb.append("avisynth-logo-gears-mod.png");
			return sb.toString();
		}
		return getURL("thumbnail0000",null);
	}

	/**
	 * @param prefix
	 * @return Returns an URL for a given media item. Not used for container types.
	 */
	protected String getURL(String prefix, RendererConfiguration r) {
		StringBuilder sb = new StringBuilder();
		sb.append(PMS.get().getServer().getURL());
		sb.append("/get/");
		sb.append(getResourceId()); //id
		sb.append("/");
		sb.append(prefix);
		//1) deleted by regzamod, Real getName() is a redundant:not nesessary data in URL
		// b/c getResourceId() is unique and enough data to identify the object(file) on PMS
		// and, name data may exaust receive buffer on dumm renderer like REGZA, 
		// if many files have very long names, on BrowseContents
		//sb.append(encode(getName()));   //origin
		
		//2) but file suffix may be needed for some player :
		// ex. some mediaplayer on Android seems to judge media type not by DLNAProfile but by file-suffix
		String s=getDlnaName(r);
		//int len=s.length();
		//if(len>12) {
		//	s=s.substring(len-12);  //get name's tail 12 charas (enoufgh to contain normal(short) suffix)
		//}
		sb.append(encode(s));   
		return sb.toString();
	}

	/**Transforms a String to UTF-8.
	 * @param s
	 * @return Transformed string s in UTF-8 encoding.
	 */
	//private static String encode(String s) {
	public static String encode(String s) {
		if(s==null) return ""; //regzamod
		try {
			return URLEncoder.encode(s, "UTF-8");
		} catch (UnsupportedEncodingException e) {
		}
		return "";
	}

	/**
	 * @return Number of children objects. This might be used in the DLDI response, as some renderers might
	 * not have enough memory to hold the list for all children.
	 */
	public int childrenNumber() {
		if (getChildren() == null) {
			return 0;
		}
		return getChildren().size();
	}
	/* (non-Javadoc)
	 * @see java.lang.Object#clone()
	 */

	@Override
	protected Object clone() {
		Object o = null;
		try {
			o = super.clone();
		} catch (CloneNotSupportedException e) {
			logger.error(null, e);
		}
		//---- default clone is shallow copy : doesn't copy member type of array, object
		//---- so, let's  do partial deep copy
		int dum[]= {-1,-1};
		DLNAResource d=(DLNAResource)o;
		d.rz_MetaTransType=dum;
		d.rz_MetaTransType[0]=rz_MetaTransType[0];
		d.rz_MetaTransType[1]=rz_MetaTransType[1];
		
		return o;
	}

	public String getFlags() {
		return flags;
	}
		
	/**Returns a representation using DIDL response lines. It gives a complete representation of the item, with as many tags as available.
	 * Recommendations as per UPNP specification are followed where possible.
	 * @param mediaRenderer Media Renderer for which to represent this information. Useful for some hacks.
	 * @return String representing the item. An example would start like this: {@code <container id="0$1" childCount=1 parentID="0" restricted="true">}
	 */
	//public final String toString (RendererConfiguration mediaRenderer) {
	public String toString (RendererConfiguration mediaRenderer) {
		if(PMS.rz_dlna_prof_cache>0 &&toString_sb!=null && rend_cur==mediaRenderer) {
			//PMS.dbg("DLNAResource.toString: already resolved, return resolved results");
			//already resolved
			return toString_sb.toString();
		}
		
		StringBuilder sb = new StringBuilder();
		if (isFolder()) {
			openTag(sb, "container");
		} else {
			openTag(sb, "item");
		}
		//PMS.dbg("toString: name="+getName()+", rz_PartPlaySttp="+rz_PartPlaySttp);

		String dlnaspec_org=null;	//regzamod
		addAttribute(sb, "id", getResourceId());

		if (isFolder()) {
			if (!isDiscovered() && childrenNumber() == 0) {
				//  When a folder has not been scanned for resources, it will automatically have zero children.
				//  Some renderers like XBMC will assume a folder is empty when encountering childCount="0" and
				//  will not display the folder. By returning childCount="1" these renderers will still display
				//  the folder. When it is opened, its children will be discovered and childrenNumber() will be
				//  set to the right value.
				addAttribute(sb, "childCount", 1);
			} else {
				addAttribute(sb, "childCount", childrenNumber());
			}
		}
		addAttribute(sb, "parentID", getParentId());
		addAttribute(sb, "restricted", "true");
		endTag(sb);

		final DLNAMediaAudio firstAudioTrack = getMedia() != null ? getMedia().getFirstAudioTrack() : null;
		
		if(getForcedTitle()!=null && !isNoName()) {
			addXMLTagAndAttribute(sb, "dc:title", encodeXML(getForcedTitle() + (getPlayer() != null && !PMS.getConfiguration().isHideEngineNames() ? (" [" + getPlayer().name() + "]") : "")));
		}
		/* moved to AddChild aav
		else if (false && firstAudioTrack != null && StringUtils.isNotBlank(firstAudioTrack.getSongname())) {

			String artist=firstAudioTrack.getArtist();
			if(is_aav() && artist!=null && artist.length()>0) artist=artist+" - ";
			else artist="";
			
			addXMLTagAndAttribute(sb, "dc:title", encodeXML(artist + firstAudioTrack.getSongname() + (getPlayer() != null && !PMS.getConfiguration().isHideEngineNames() ? (" [" + getPlayer().name() + "]") : "")));
		} 
		*/
		else { // Ditlew - org
			//addXMLTagAndAttribute(sb, "dc:title", encodeXML((isFolder()||player==null)?getDisplayName():mediaRenderer.getUseSameExtension(getDisplayName())));
			// Ditlew
			addXMLTagAndAttribute(sb, "dc:title", encodeXML((isFolder() || getPlayer() == null) ? getDisplayName() : mediaRenderer.getUseSameExtension(getDisplayName(mediaRenderer))));
		}

		if (firstAudioTrack != null) {
			if (StringUtils.isNotBlank(firstAudioTrack.getAlbum())) {
				addXMLTagAndAttribute(sb, "upnp:album", encodeXML(firstAudioTrack.getAlbum()));
			}
			if (StringUtils.isNotBlank(firstAudioTrack.getArtist())) {
				addXMLTagAndAttribute(sb, "upnp:artist", encodeXML(firstAudioTrack.getArtist()));
				addXMLTagAndAttribute(sb, "dc:creator", encodeXML(firstAudioTrack.getArtist()));
			}
			if (StringUtils.isNotBlank(firstAudioTrack.getGenre())) {
				addXMLTagAndAttribute(sb, "upnp:genre", encodeXML(firstAudioTrack.getGenre()));
			}
			if (firstAudioTrack.getTrack() > 0) {
				addXMLTagAndAttribute(sb, "upnp:originalTrackNumber", "" + firstAudioTrack.getTrack());
			}
		}

		if (!isFolder()) {
			int indexCount = 1;
			if (mediaRenderer.isDLNALocalizationRequired()) {
				indexCount = getDLNALocalesCount();
			}
			//see HTTPResource.getRendererMimeType() --> rendererConfiguration.getMimeType()
			String mime = getRendererMimeType(mimeType(), mediaRenderer);
			//----------------------------------------------------------------------------------- 
			// You Should know ! 
			//----------------------------------------------------------------------------------- 
			// If target dlna is judged to be Transcoded/Remuxed (i.e getPlayer()!=null), 
			// it's original mimeType is ignored, and will be allways changed to "video/mpeg" or "video/x-ms-wmv"
			// by above getRendererMimeType().
			//-----------------------------------------------------------------------------------
			//PMS.dbg("DLNAResource.toString: neme="+getName()+", mime_org="+mimeType()+", mime_rend="+mime);
			//String mime_org=mime;	//regzamod
			if (mime == null) {
				mime = "video/mpeg";
			}
			//if(PMS.rz_debug>1) PMS.dbg("dlna="+this+", mime="+mime);
			gMimeType_org=mimeType();
			gMimeType_ren=mime;
			gMimeType_cur=mime;
			
			boolean isH264=(getMedia()==null?false:getMedia().isH264());
			boolean is_mpeg=mime.equals("video/mpeg");
			
			if(!is_aav()&&!is_iav()) {
				mpegTsMux=isRemuxable(mediaRenderer);
			}
			if(getForcedTransType()==PMS.ENC_REMUX) mpegTsMux=true ; //forced
			else if(getForcedTransType()==PMS.ENC_TRANSCODE) mpegTsMux=false ; //forced
			
			if(PMS.rz_debug>2) {
				PMS.dbg("mpegTsMux="+mpegTsMux+", getPlayer()="+getPlayer());//regzamod
				PMS.dbg("getMediaSubtitle()="+getMediaSubtitle()+", getMedia()="+getMedia());//regzamod
				if(getMedia()!=null) {
					PMS.dbg("2 getDvdtrack ="+getMedia().getDvdtrack());
					PMS.dbg("3 isMuxable="+getMedia().isMuxable(mediaRenderer));
					PMS.dbg("4 isMencoderMuxWhenCompatible="
						+PMS.getConfiguration().isMencoderMuxWhenCompatible());
					PMS.dbg("5 isMuxH264MpegTS="+mediaRenderer.isMuxH264MpegTS());
				}
			}
			
			//PMS.dbg1("getRendererMimeType mime="+mime);
			//---- localization loop
			for (int c = 0; c < indexCount; c++) {
				openTag(sb, "res");
				// DLNA.ORG_OP : 1er 10 = exemple: TimeSeekRange.dlna.org :npt=187.000-
				//                   01 = Range par octets
				//                   00 = pas de range, meme pas de pause possible
				flags = "DLNA.ORG_OP=01";
				if(mediaRenderer.isRZ_ForceSingleRangeType()) {	//regzamod, force 01 or 10, not 11
					if(mediaRenderer.isSeekByTime()) {	
						flags = "DLNA.ORG_OP=10";
					}
					else {
						flags = "DLNA.ORG_OP=01";
					}
				}
				else {
					if (getPlayer() != null) {	//Transcode or Remux
						if (getPlayer().isTimeSeekable() && mediaRenderer.isSeekByTime()) {
							if (mediaRenderer.isPS3()) // ps3 doesn't like OP=11
							{
								flags = "DLNA.ORG_OP=10";
							} else {
								flags = "DLNA.ORG_OP=11";
							}
						}
					} else {	//Direct play
						//if (mediaRenderer.isSeekByTime() && !mediaRenderer.isPS3()) {	//original
						if (mediaRenderer.isSeekByTime()) {	//regzamod
							if(!mediaRenderer.isPS3() && !PMS.rz_mod) {	// not PS3 && not REGZA
								flags = "DLNA.ORG_OP=11";	//REGZA Directplay also doesn't like 11
							}
							else {
								//i.e. use default:"DLNA.ORG_OP=01"----> REGZA works good
							}
						}
					}
				}
				if(PMS.rz_test>0) {
					if((PMS.rz_test&0x8)==0) addAttribute(sb, "xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0/");
					else logger.info(" don't send [xmlns:dlna]");
				}
				
				int vfmt=mediaRenderer.getRZ_TransVFormat();
				int rfmt=mediaRenderer.getRZ_RemuxVFormat();
				int wfmt=mediaRenderer.getRZ_TransVFormatWeb();
				int afmt=mediaRenderer.getRZ_TransVFormatAAV();
				if(getForcedTransFormatWeb()>0) wfmt=getForcedTransFormatWeb();
				if (mediaRenderer.isPS3()) { 
					// TO REMOVE, OR AT LEAST MAKE THIS GENERIC 
					// whole extensions/mime-types mess to rethink anyway
					if (mime.equals("video/x-divx")) {
						dlnaspec = "DLNA.ORG_PN=AVI";
					} else if (mime.equals("video/x-ms-wmv") && getMedia() != null && getMedia().getHeight() > 700) {
						dlnaspec = "DLNA.ORG_PN=WMVHIGH_PRO";
					}
				} 
				else if (c==(indexCount-1) && mediaRenderer.isRZ_UseGenericDLNAPN()) {	//regzamod, generate private DLNAPN
				//else if (mediaRenderer.isRZ_UseGenericDLNAPN()) {	//regzamod, generate private DLNAPN
					if(is_aav()||is_iav()) {	//audio as video
						//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString: is_aav: true -> fake audio as video");
						if(afmt==PMS.TRS_M2TS) {
							dlnaspec = "DLNA.ORG_PN=M2TS_MPEG";
						}
						else {
							dlnaspec = "DLNA.ORG_PN=PS_MPEG";
						}
						mime="video/mpeg";
					}
					else if(sotype==SO_VIDEO_STREAM) {	//web video
						//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString: is_aav: true -> fake audio as video");
						if(wfmt==PMS.TRS_M2TS) {
							dlnaspec = "DLNA.ORG_PN=M2TS_MPEG";
						}
						else {
							dlnaspec = "DLNA.ORG_PN=PS_MPEG";
						}
						mime="video/mpeg";
					}
					//else if (mime.equals("video/mpeg")) {
					//else if (true || is_mpeg) {
					else if (getExt()!=null && getExt().isVideo()) {
						if(getPlayer() != null) {
							//--------------------------------------------------------------------------
							// for TRANSCODE/REMUX Stream:
							// all formats judged to be TRANSCODE/REMUXed come here
							//--------------------------------------------------------------------------
							//if(PMS.rz_debug>1) PMS.dbg("getPlayer() != null, mpegTsMux="+mpegTsMux);
							//---- transcode/remux
							if(mpegTsMux) { //---- REMUXable
								//if(mediaRenderer.isRZ_RemuxToM2ts()) {	// remux to m2ts
								//if(mediaRenderer.getRZ_RemuxFormat().equals("m2ts")) {	// remux to m2ts
								int format=rfmt;
								if(format==PMS.TRS_M2TS) {
									if(isH264) dlnaspec = "DLNA.ORG_PN=M2TS_AVC";
									else  dlnaspec = "DLNA.ORG_PN=M2TS_MPEG";
								} else {	//remux to PMS.TRS_MPEGTS
									if(isH264) dlnaspec = "DLNA.ORG_PN=TS_AVC";
									else  dlnaspec = "DLNA.ORG_PN=TS_MPEG";
								}
								//---- forced ------------------
								format=getForcedRemuxFormat();  //forced
								if(format>=0) {
									if(format==PMS.TRS_M2TS) {
										dlnaspec = "DLNA.ORG_PN=M2TS_MPEG";
									} else if(format==PMS.TRS_MPEGTS) {
										dlnaspec = "DLNA.ORG_PN=TS_MPEG";
									} else if(format==PMS.TRS_MPEGTS2) {
										dlnaspec = "DLNA.ORG_PN=TS_MPEG";
									} else if(format==PMS.TRS_MPEGPS) {
										dlnaspec = "DLNA.ORG_PN=PS_MPEG";
									}
								}
							} else { //---- TRANSCODE
								//if(mediaRenderer.isRZ_TranscodeToM2ts() && MEncoderVideo.ID.equals(getPlayer().id())) {
								//if(PMS.rz_debug>1) PMS.dbg("getPlayer() != null, transcode format="+vfmt);
								int format=getForcedTransFormat();
								if(format<0) format=vfmt;
								if(format==PMS.TRS_M2TS) {
									dlnaspec = "DLNA.ORG_PN=M2TS_MPEG";
								} else if(format==PMS.TRS_MPEGTS) {
									dlnaspec = "DLNA.ORG_PN=TS_MPEG";
								} else if(format==PMS.TRS_MPEGTS2) {
									dlnaspec = "DLNA.ORG_PN=TS_MPEG";
								} else {
									dlnaspec = "DLNA.ORG_PN=PS_MPEG";
								}
							}
							//---- DVD specific
							if(sotype==SO_DVDDEV) {
								if(mediaRenderer.getRZ_TransFormatDVD().equals("mpegps")) {
									dlnaspec = "DLNA.ORG_PN=PS_MPEG";
								}
								else if(mediaRenderer.getRZ_TransFormatDVD().equals("mpegts")) {
									dlnaspec = "DLNA.ORG_PN=TS_MPEG";
								}
							}
							//if(PMS.rz_debug>1) PMS.dbg("getPlayer() != null, dlnaspec="+dlnaspec);
						} 
						else {
							//--------------------------------------------------------------------------
							// for DIRECT Stream
							// All formats judged as DIRECTable come here
							//--------------------------------------------------------------------------
							if (getMedia() != null) {	
								if (getMedia().isMpegTS()) {	//mpegts,m2ts
									if(isM2TS()) {	//m2ts
										if(getMedia().isH264()) dlnaspec ="DLNA.ORG_PN=M2TS_AVC";
										else dlnaspec ="DLNA.ORG_PN=M2TS_MPEG";
									} else {	//ts
										if(getMedia().isH264()) dlnaspec ="DLNA.ORG_PN=TS_AVC";
										else dlnaspec ="DLNA.ORG_PN=TS_MPEG";
									}
								} else if(getMedia().isMpegPS()) { //mpegps
									dlnaspec = "DLNA.ORG_PN=PS_MPEG";
								}
								else {
									//formats except above come here.
									//dlnaspec is null(unknown);
								}
							} else {
								//dlnaspec is null(unknown);
							}
						}
						if(true) {
							if(!rz_MetaBodyExists) { //test for slow error when body not exists
								//dlnaspec = "DLNA.ORG_PN=PS_MPEG";
								//PMS.dbg("dlna="+getName()+": rz_MetaBodyExists=Flase, --> set dlnaspec=null player="+getPlayer());
								dlnaspec = null;
								//setPlayer(null);
							}
						}
						if(false) {
							if (mime.equals("video/vnd.dlna.mpeg-tts")) {
								// patters - on Sony BDP m2ts clips aren't listed without this
								dlnaspec = "DLNA.ORG_PN=" + getMPEG_TS_SD_EULocalizedValue(c);
							}
						}
					}
					else if(getExt()!=null && getExt().isImage()) {
						if (mime.equals("image/jpeg")) {
							dlnaspec = "DLNA.ORG_PN=JPEG_LRG";
						}
					} 
					else if(getExt()!=null && getExt().isAudio()) {
						if (mime.equals("audio/mpeg")) {
							dlnaspec = "DLNA.ORG_PN=MP3";
						} else if (mime.substring(0, 9).equals("audio/L16") || mime.equals("audio/wav")) {
							dlnaspec = "DLNA.ORG_PN=LPCM";
						}
					}
				}
				else {	//original
					if(is_aav()||is_iav()) {	//audio as video
						//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString: is_aav: true -> fake audio as video");
						dlnaspec = "DLNA.ORG_PN="+getMPEG_PS_LocalizedValue(c);
						mime="video/mpeg";
					}
					else if(sotype==SO_VIDEO_STREAM) {	//web video
						//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString: is_aav: true -> fake audio as video");
						if(vfmt==PMS.TRS_M2TS) {
							dlnaspec = "DLNA.ORG_PN="+getMPEG_TT_LocalizedValue(c);
						}
						else {
							dlnaspec = "DLNA.ORG_PN="+getMPEG_PS_LocalizedValue(c);
						}
						mime="video/mpeg";
					}
					//else if (mime.equals("video/mpeg")) {
					//else if (true || is_mpeg) {
					else if (getExt()!=null && getExt().isVideo()) {
						if (getPlayer() != null) {
							//---- transcode/remux
							// do we have some mpegts to offer ?
							if(mpegTsMux) {
								//---- remux ----
								dlnaspec =getMedia()!=null && getMedia().isH264() 
									&& !VideoLanVideoStreaming.ID.equals(getPlayer().id())
									&& getMedia().isMuxable(mediaRenderer) ? 
									"DLNA.ORG_PN=AVC_TS_HD_24_AC3_ISO" : "DLNA.ORG_PN=" + getMPEG_TS_SD_EU_ISOLocalizedValue(c);
								//---- forced ------------------
								int format=getForcedRemuxFormat(); 
								if(format>=0) {
									if(format==PMS.TRS_M2TS) {
										dlnaspec = "DLNA.ORG_PN="+getMPEG_TT_LocalizedValue(c);
									} else if(format==PMS.TRS_MPEGTS) {
										dlnaspec = "DLNA.ORG_PN="+getMPEG_TS_LocalizedValue(c);
									} else if(format==PMS.TRS_MPEGTS2) {
										dlnaspec = "DLNA.ORG_PN="+getMPEG_TS_LocalizedValue(c);
									} else if(format==PMS.TRS_MPEGPS) {
										dlnaspec = "DLNA.ORG_PN="+getMPEG_PS_LocalizedValue(c);
									}
								}
							} else { //trans
								//if(mediaRenderer.isRZ_TranscodeToM2ts() && MEncoderVideo.ID.equals(getPlayer().id())) {	
								//if(mediaRenderer.getRZ_TransFormat().equals("m2ts") && MEncoderVideo.ID.equals(getPlayer().id())) {	
								if(rfmt==PMS.TRS_M2TS) {
									//mpeg-tts(m2ts) regzamod, trasco to m2ts by MencoderVideo
									dlnaspec = "DLNA.ORG_PN="+getMPEG_TS_LocalizedValue(c);
								}
								else {
									// mpeg-ps/mpeg-ts
									dlnaspec = "DLNA.ORG_PN="+getMPEG_PS_LocalizedValue(c);
								}
								//---- forced ------------------
								int format=getForcedTransFormat();
								if(format==PMS.TRS_M2TS) {
									dlnaspec = "DLNA.ORG_PN="+getMPEG_TT_LocalizedValue(c);
								} else if(format==PMS.TRS_MPEGTS) {
									dlnaspec = "DLNA.ORG_PN="+getMPEG_TS_LocalizedValue(c);
								} else if(format==PMS.TRS_MPEGTS2) {
									dlnaspec = "DLNA.ORG_PN="+getMPEG_TS_LocalizedValue(c);
								} else if(format==PMS.TRS_MPEGPS) {
									dlnaspec = "DLNA.ORG_PN="+getMPEG_PS_LocalizedValue(c);
								}
							}
						} 
						//---- getPlayer() == null i.e. direct play
						else if (getMedia() != null) {	
							if (getMedia().isMpegTS()) {
								dlnaspec = getMedia().isH264() ? "DLNA.ORG_PN=AVC_TS_HD_50_AC3" : "DLNA.ORG_PN=" + getMPEG_TS_LocalizedValue(c);
							} else if (getMedia().isMpegPS()) {
								dlnaspec = "DLNA.ORG_PN=" + getMPEG_PS_LocalizedValue(c);
							}
						} else {
							//dlnaspec = "DLNA.ORG_PN=" + getMPEG_PS_LocalizedValue(c);
						}
					} else if (mime.equals("video/vnd.dlna.mpeg-tts")) {
						// patters - on Sony BDP m2ts clips aren't listed without this
						dlnaspec = "DLNA.ORG_PN=" + getMPEG_TS_SD_EULocalizedValue(c);
					} else if (mime.equals("image/jpeg")) {
						dlnaspec = "DLNA.ORG_PN=JPEG_LRG";
					} else if (mime.equals("audio/mpeg")) {
						dlnaspec = "DLNA.ORG_PN=MP3";
					} else if (mime.substring(0, 9).equals("audio/L16") || mime.equals("audio/wav")) {
						dlnaspec = "DLNA.ORG_PN=LPCM";
					}
				}

				//---- DLNA profile change here --------
				//if(c==0) {
					dlnaspec_org=dlnaspec;	//remember before change
				//}
				if (dlnaspec != null) {
					//if(c==indexCount-1) dlnaspec = "DLNA.ORG_PN=" + mediaRenderer.getDLNAPN(dlnaspec.substring(12));
					//dlnaspec = "DLNA.ORG_PN=" + mediaRenderer.getDLNAPN(dlnaspec.substring(12));
					//if(PMS.rz_debug>1) PMS.dbg("==== toSTring: c="+c+", dlnaspec="+dlnaspec);
					//String pn[]=mediaRenderer.getDLNAPN_M(dlnaspec_org.substring(12));
					String pn[]=mediaRenderer.getDLNAPN_M(dlnaspec.substring(12));
					dlnaspec = "DLNA.ORG_PN=" + pn[0];
					if(pn[1].length()>0) {
						mime=pn[1];
					}
					if(getPlayer() == null) {
						//if(PMS.rz_debug>1) PMS.dbg("toSTring: pn.length="+pn.length+", pn[1]="+pn[1]);
						if(pn[2].length()>0) {
							flags="DLNA.ORG_OP="+pn[2];
						}
					}
					else if(mpegTsMux) {
						if(pn[3].length()>0) {
							flags="DLNA.ORG_OP="+pn[3];
						}
					}
					else {
						if(pn[4].length()>0) {
							flags="DLNA.ORG_OP="+pn[4];
						}
					}
				}
				//PMS.dbg("c="+c+", dlnaspec after DLNAProfileChange="+dlnaspec);	//regzamod

				if (!mediaRenderer.isDLNAOrgPNUsed()) {
					dlnaspec = null;
				}
				
				//---- regzamod, forced by Metafile
				if(getForcedMimeType()!=null) {
					mime=getForcedMimeType();
				}
				if(getForcedDlnaProf()!=null) {
					dlnaspec = "DLNA.ORG_PN="+getForcedDlnaProf();
				}
				if(getForcedDlnaOp()!=null) {
					flags="DLNA.ORG_OP="+getForcedDlnaOp();
				}
				if(flags.endsWith("01")) {
					seek_mode=1;
				}
				else {
					seek_mode=0; //unknown
				}
				//currentMimeType=mime;
						
				if(PMS.rz_debug>2) {
					PMS.dbg("DLNAResource.toString: locale="+c+", name="+getName()+", Org mime="+gMimeType_org+", dlnaspec="+dlnaspec_org+", flags="+flags);
					PMS.dbg("DLNAResource.toString: locale="+c+", -----> changed mime="+mime+", dlnaspec="+dlnaspec+", flags="+flags);
				}
				
				String dlna_prof="http-get:*:" + mime + ":" + (dlnaspec != null ? (dlnaspec + ";") : "") + flags;
				//if(PMS.rz_debug>1) PMS.dbg("toString: c="+c+", dlna_prof="+dlna_prof+", name="+getName());
				//addAttribute(sb, "protocolInfo", "http-get:*:" + mime + ":" + (dlnaspec != null ? (dlnaspec + ";") : "") + flags);
				addAttribute(sb, "protocolInfo", dlna_prof);

				if(sotype==SO_VIDEO_STREAM) {
					//web videostream come here
					if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString: seems to be Web contents, wmed_duration="+wmed_duration
						+", Media="+getMedia());
					addAttribute(sb, "size", DLNAMediaInfo.TRANS_SIZE);
					if(getMedia()!=null) {
						if (getSplitRange().isEndLimitAvailable()) {
							addAttribute(sb, "duration", DLNAMediaInfo.getDurationString(getSplitRange().getDuration()));
						} else {
							if(wmed_duration>0) {
								addAttribute(sb, "duration",DLNAMediaInfo.getDurationString(wmed_duration));
							}
							else {
								String d=getMedia().getDurationString();
								if(d==null) {
									addAttribute(sb, "duration", "09:59:59");
								}
								else {
									addAttribute(sb, "duration", d);
								}
							}
						}
					}
					else {
						if(wmed_duration>0) {
							addAttribute(sb, "duration",DLNAMediaInfo.getDurationString(wmed_duration));
						}
						else {
							addAttribute(sb, "duration", "09:59:59");
						}
					}
					addAttribute(sb, "bitrate", "4000000");
				}
				else if (getExt() != null && getExt().isVideo() && getMedia() != null && getMedia().isMediaparsed()) {
					//if (getPlayer() == null && getMedia() != null) {
					if (getPlayer() == null) {
						addAttribute(sb, "size", getMedia().getSize());
					} else {
						long transcoded_size = mediaRenderer.getTranscodedSize();
						if (transcoded_size != 0) {
							addAttribute(sb, "size", transcoded_size);
						}
					}
					if (getMedia().getDuration() != null && getMedia().getDuration()>0) {
						//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString: getMedia().getDuration()="+getMedia().getDuration());
						//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString: getSplitRange()="+getSplitRange());
						if (getSplitRange().isEndLimitAvailable()) {
							addAttribute(sb, "duration", DLNAMediaInfo.getDurationString(getSplitRange().getDuration()));
						} else {
							addAttribute(sb, "duration", getMedia().getDurationString());
							//addAttribute(sb, "duration", "09:33:44");
						}
					}
					else if(sotype==SO_VIDEO_STREAM) {
						if(wmed_duration>0) {
						}
						addAttribute(sb, "duration", "09:59:59");
					}
					if (getMedia().getResolution() != null) {
						addAttribute(sb, "resolution", getMedia().getResolution());
					}
					//if(PMS.rz_debug>1) PMS.dbg("TEST bitrate");
					addAttribute(sb, "bitrate", (getMedia().getRealVideoBitrate()));
					if (firstAudioTrack != null) {
						if (firstAudioTrack.getNrAudioChannels() > 0) {
							addAttribute(sb, "nrAudioChannels", firstAudioTrack.getNrAudioChannels());
						}
						if (firstAudioTrack.getSampleFrequency() != null) {
							addAttribute(sb, "sampleFrequency", firstAudioTrack.getSampleFrequency());
						}
					}
				}
				//else if(is_aav()||is_iav()) {	//regzamod, audio as video
				else if(is_aav()) {	//regzamod, audio as video
					//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString: is_aav=true, set fake video prof");
					if(getMedia() != null) {
						if (getPlayer() == null) {
							addAttribute(sb, "size", getMedia().getSize());
							//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString: size="+getMedia().getSize());
						} else {
							long transcoded_size = mediaRenderer.getTranscodedSize();
							if (transcoded_size != 0) {
								addAttribute(sb, "size", transcoded_size);
								//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString: size="+transcoded_size);
							}
						}
						if(getSession().getSessAutoPlay()!=0) {
							//auto-repeat for all entries: take long time
							addAttribute(sb, "duration", "09:59:59");
						}
						else if (getMedia().getDuration() != null) {
							if (getSplitRange()!=null && getSplitRange().isEndLimitAvailable()) {
								addAttribute(sb, "duration", DLNAMediaInfo.getDurationString(getSplitRange().getDuration()));
								//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString: duration="+DLNAMediaInfo.getDurationString(getSplitRange().getDuration()));
							} else {
								addAttribute(sb, "duration", getMedia().getDurationString());
								//addAttribute(sb, "duration", "00:03:00.00");
								//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString:duration="+getMedia().getDurationString());
							}
						}
						if (getMedia().getResolution() != null) {
							addAttribute(sb, "resolution", getMedia().getResolution());
							//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString: resolution="+getMedia().getResolution());
						}
						
						//addAttribute(sb, "bitrate", getMedia().getRealVideoBitrate());
						addAttribute(sb, "bitrate", getMedia().getRealVideoBitrate());
						//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString: bitrate="+getMedia().getRealVideoBitrate());
						
						if (firstAudioTrack != null) {
							if (firstAudioTrack.getNrAudioChannels() > 0) {
								addAttribute(sb, "nrAudioChannels", firstAudioTrack.getNrAudioChannels());
							}
							if (firstAudioTrack.getSampleFrequency() != null) {
								addAttribute(sb, "sampleFrequency", firstAudioTrack.getSampleFrequency());
							}
						}
					}
					else {	//no mediainfo (e.x. web contents)
						if(PMS.rz_test>0) {
							if(PMS.rz_debug>2) {
								PMS.dbg("DLNAResource.toString: rz_test>0 :TEST MODE of ReplyMediaInfo, OK?");
								PMS.dbg(" don't send if rz_test =0x1:[size], 0x2:[duration], 0x4:[bitrate]");
							}
							if((PMS.rz_test&0x1)==0) addAttribute(sb, "size", DLNAMediaInfo.TRANS_SIZE);
							else if(PMS.rz_debug>2) PMS.dbg(" don't send [size]");
							
							if((PMS.rz_test&0x2)==0) addAttribute(sb, "duration", "09:59:59");
							else if(PMS.rz_debug>2) PMS.dbg(" don't send [duration]");
							
							if((PMS.rz_test&0x4)==0) addAttribute(sb, "bitrate", "4000000");
							else if(PMS.rz_debug>2) PMS.dbg(" don't send [bitrate]");
						}
						else {
							addAttribute(sb, "size", DLNAMediaInfo.TRANS_SIZE);
							addAttribute(sb, "duration", "09:59:59");
							addAttribute(sb, "bitrate", "4000000");
						}
					}
				} 
				else if(is_iav()) {	//regzamod, ImageAsVideo
					if(getMedia() != null) {
						if (getPlayer() == null) {
							addAttribute(sb, "size", getMedia().getSize());
						} else {
							long transcoded_size = mediaRenderer.getTranscodedSize();
							if (transcoded_size != 0) {
								addAttribute(sb, "size", DLNAMediaInfo.TRANS_SIZE);
							}
						}
						addAttribute(sb, "duration", "09:59:59");
					}
					else {	//no mediainfo (e.x. web contents)
						addAttribute(sb, "size", DLNAMediaInfo.TRANS_SIZE);
						addAttribute(sb, "duration", "09:59:59");
					}
					addAttribute(sb, "bitrate", "4000000");
				} 
				else if (getExt() != null && getExt().isImage()) {
					//---- Image
					if (getMedia() != null && getMedia().isMediaparsed()) {
						addAttribute(sb, "size", getMedia().getSize());
						if (getMedia().getResolution() != null) {
							addAttribute(sb, "resolution", getMedia().getResolution());
						}
					} else {
						addAttribute(sb, "size", length());
					}
				} else if (getExt() != null && getExt().isAudio()) {
					//---- Audio
					if (getMedia() != null && getMedia().isMediaparsed()) {
						addAttribute(sb, "bitrate", getMedia().getBitrate());
						if (getMedia().getDuration() != null) {
							addAttribute(sb, "duration", getMedia().getDuration());
						}
						if (firstAudioTrack != null && firstAudioTrack.getSampleFrequency() != null) {
							addAttribute(sb, "sampleFrequency", firstAudioTrack.getSampleFrequency());
						}
						if (firstAudioTrack != null) {
							addAttribute(sb, "nrAudioChannels", firstAudioTrack.getNrAudioChannels());
						}

						if (getPlayer() == null) {
							addAttribute(sb, "size", getMedia().getSize());
						} else {
							// calcul taille wav
							if (firstAudioTrack != null) {
								int defaultFrequency = mediaRenderer.isTranscodeAudioTo441() ? 44100 : 48000;
								if (!PMS.getConfiguration().isAudioResample()) {
									try {
										defaultFrequency = firstAudioTrack.getSampleRate();
									} catch (Exception e) {
									}
								}
								int na = firstAudioTrack.getNrAudioChannels();
								if (na > 2) // no 5.1 dump in mplayer
								{
									na = 2;
								}
								int finalsize = (int) (getMedia().getDurationInSeconds() * defaultFrequency * 2 * na);
								logger.debug("Calculated size: " + finalsize);
								addAttribute(sb, "size", finalsize);
							}
						}
					} else {
						addAttribute(sb, "size", length());
					}
				} 
				
				endTag(sb);
				sb.append(getFileURL(mediaRenderer));
				closeTag(sb, "res");
				if(PMS.rz_debug>20) {
					PMS.dbg("dlna name="+getName()+", toString sb="+sb);
				}
			}	//---- localization loop
			gMimeType_cur=mime;
		}

		String thumbURL = getThumbnailURL();
		//PMS.dbg("DLNAResource.toString.getThumbnailURL="+thumbURL+", getExt()="+getExt());
						
		if (!isFolder() && (getExt() == null || (getExt() != null && thumbURL != null))) {
			openTag(sb, "upnp:albumArtURI");
			addAttribute(sb, "xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0/");

			if (getThumbnailContentType().equals(PNG_TYPEMIME) && !mediaRenderer.isForceJPGThumbnails()) {
				addAttribute(sb, "dlna:profileID", "PNG_TN");
				thumbURL=thumbURL+".png";  //regzam, add
			} else {
				addAttribute(sb, "dlna:profileID", "JPEG_TN");
				thumbURL=thumbURL+".jpg";	//regzam, add
			}
			endTag(sb);
			sb.append(thumbURL);
			closeTag(sb, "upnp:albumArtURI");
		}

		if ((isFolder() || mediaRenderer.isForceJPGThumbnails()) && thumbURL != null) {
			openTag(sb, "res");

			if (getThumbnailContentType().equals(PNG_TYPEMIME) && !mediaRenderer.isForceJPGThumbnails()) {
				addAttribute(sb, "protocolInfo", "http-get:*:image/png:DLNA.ORG_PN=PNG_TN");
				thumbURL=thumbURL+".png";  //regzam, add
			} else {
				addAttribute(sb, "protocolInfo", "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN");
				thumbURL=thumbURL+".jpg";	//regzam, add
			}
			endTag(sb);
			sb.append(thumbURL);
			closeTag(sb, "res");
		}

		/*
		if (getLastmodified() > 0) {
			//addXMLTagAndAttribute(sb, "dc:date", PMS.sdfDate.format(new Date(getLastmodified())));
		}*/
		
		if (getDispDate() > 0) {
			addXMLTagAndAttribute(sb, "dc:date", PMS.sdfDate.format(new Date(getDispDate())));	//regzamod
		}

		String uclass = null;
		if (first != null && getMedia() != null && !getMedia().isSecondaryFormatValid()) {
			uclass = "dummy";
		} else {
			if (isFolder()) {
				uclass = "object.container.storageFolder";
				boolean xbox = mediaRenderer.isXBOX();
				if (xbox && getFakeParentId() != null && getFakeParentId().equals("7")) {
					uclass = "object.container.album.musicAlbum";
				} else if (xbox && getFakeParentId() != null && getFakeParentId().equals("6")) {
					uclass = "object.container.person.musicArtist";
				} else if (xbox && getFakeParentId() != null && getFakeParentId().equals("5")) {
					uclass = "object.container.genre.musicGenre";
				} else if (xbox && getFakeParentId() != null && getFakeParentId().equals("F")) {
					uclass = "object.container.playlistContainer";
				}
			} else if (getExt() != null && getExt().isVideo()) {
				uclass = "object.item.videoItem";
			} else if (is_aav()||is_iav()) {
				//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.toString: is_aav=true, set object.item.videoItem");
				uclass = "object.item.videoItem";
			} else if (getExt() != null && getExt().isImage()) {
				uclass = "object.item.imageItem.photo";
			} else if (getExt() != null && getExt().isAudio()) {
				uclass = "object.item.audioItem.musicTrack";
			} else {
				uclass = "object.item.videoItem";
			}
		}
		if (uclass != null) {
			addXMLTagAndAttribute(sb, "upnp:class", uclass);
		}

		if (isFolder()) {
			closeTag(sb, "container");
		} else {
			closeTag(sb, "item");
		}
		toString_sb=sb;
		rend_cur=mediaRenderer;
		if(PMS.rz_debug>2) {
			PMS.dbg("DLNA toString: name="+getName()+", toString_sb="+toString_sb);
		}
		return sb.toString();
	}

	private String getRequestId(String rendererId) {
		return String.format("%s|%x|%s", rendererId, hashCode(), getSystemName());
	}

	/**
	 * Plugin implementation. When this item is going to play, it will notify all the StartStopListener objects available.
	 * @see StartStopListener
	 */
	static public int	playingCount;	//regzamod
	
	public void startPlaying(final String rendererId) {
		
		final String requestId = getRequestId(rendererId);
		
		if(PMS.rz_debug>2) PMS.dbg("===> startPlaying: rendererId="+requestId+", Already playingCount="+playingCount);	//regzamod
		
		//ToDo more smart way
		if(rz_VisualClipPath!=null) {
			if(PMS.rz_VisualClipPath==null || !rz_VisualClipPath.equals(PMS.rz_VisualClipPath)) {
				// move path1 to path2
				if(PMS.rz_VisualClipPath!=null) {
					PMS.get().getFrame().getRza().setClipPath2(PMS.rz_VisualClipPath,1);
				}
				// new set to path1
				PMS.rz_VisualClipPath=rz_VisualClipPath;
				PMS.get().getFrame().getRza().setClipPath1(rz_VisualClipPath,1);
				//if(PMS.rz_debug>1) PMS.dbg("===> startPlaying: imgClipPath changed to="+rz_VisualClipPath);
			}
		}
		
		synchronized (requestIdToRefcount) {
			Integer temp = requestIdToRefcount.get(requestId);
			if (temp == null) {
				temp = 0;
			}
			final Integer refCount = temp;
			requestIdToRefcount.put(requestId, refCount + 1);
			playingCount++;	//regzamod
			if (refCount == 0) {
				final DLNAResource self = this;
				Runnable r = new Runnable() {
					@Override
					public void run() {
						if(PMS.rz_debug>2) {
							PMS.dbg("StartStopListener: event:    start");
							PMS.dbg("StartStopListener: renderer: " + rendererId);
							PMS.dbg("StartStopListener: file:     " + getSystemName());
						}

						for (final ExternalListener listener : ExternalFactory.getExternalListeners()) {
							if (listener instanceof StartStopListener) {
								// run these asynchronously for slow handlers (e.g. logging, scrobbling) 
								Runnable fireStartStopEvent = new Runnable() {
									@Override
									public void run() {
										try {
											((StartStopListener) listener).nowPlaying(getMedia(), self);
										} catch (Throwable t) {
											logger.error(String.format("Failed to notify nowPlaying for StartStopListener of type=%s", listener.getClass()), t);
										}
									}
								};
								new Thread(fireStartStopEvent).start();
							}
						}
					}
				};

				new Thread(r).start();
			}
			//PMS.rz_resume.playStarted(this);	//regzamod
		}
	}

	/**
	 * Plugin implementation. When this item is going to stop playing, it will notify all the StartStopListener
	 * objects available.
	 * @see StartStopListener
	 */
	//public void stopPlaying(final String rendererId) {
	public void stopPlaying(final String rendererId, rz_SessionInfo sess,int mode) {
		//mode:=0:only for resume_memory,=1:really exec stop_procedure
		if(PMS.getServerStopping()) {
			logger.warn("stopPlaying: Server is now Stopping, so nothing can be done");
			return;
		}

		final DLNAResource self = this;
		//if Server Stopping, getRequestId will cause Java Fatal error(EXCEPTION_ACCESS_VIOLATION), 
		final String requestId = getRequestId(rendererId);
		
		if(PMS.rz_debug>2) PMS.dbg("===> stopPlaying: requestId="+requestId+", Already playingCount="+playingCount);	//regzamod
		
		PMS.rz_resume.playStopped(this,sess,1);	//regzamod
		//if(mode==0) return;
	
		Runnable defer = new Runnable() {
			@Override
			public void run() {
				try {
					//if(PMS.rz_debug>1) PMS.dbg("StartStopListener: stopPlaying Start (CAUTION! STOP_PLAYING_DELAY changed for TEST "+STOP_PLAYING_DELAY+" msec to 500 msec"); //regzamod
					Thread.sleep(STOP_PLAYING_DELAY);
					//Thread.sleep(500);	//regzamod
				} catch (InterruptedException e) {
					logger.error("stopPlaying sleep interrupted", e);
				}

				synchronized (requestIdToRefcount) {
					final Integer refCount = requestIdToRefcount.get(requestId);
					assert refCount != null;
					assert refCount > 0;
					requestIdToRefcount.put(requestId, refCount - 1);
					playingCount--;	//regzamod
					
					Runnable r = new Runnable() {
						@Override
						public void run() {
							if (refCount == 1) {
								if(PMS.rz_debug>2) {
									PMS.dbg("StartStopListener: event:    stop");
									PMS.dbg("StartStopListener: renderer: " + rendererId);
									PMS.dbg("StartStopListener: file:     " + getSystemName());
								}

								for (final ExternalListener listener : ExternalFactory.getExternalListeners()) {
									if (listener instanceof StartStopListener) {
										// run these asynchronously for slow handlers (e.g. logging, scrobbling) 
										Runnable fireStartStopEvent = new Runnable() {
											@Override
											public void run() {
												try {
													((StartStopListener) listener).donePlaying(getMedia(), self);
												} catch (Throwable t) {
													logger.error(String.format("Failed to notify with type=%s of donePlaying", listener.getClass()), t);
												}
											}
										};
										new Thread(fireStartStopEvent).start();
									}
								}
							}
						}
					};

					new Thread(r).start();
				}
			}
		};
		
		new Thread(defer).start();
		//if(PMS.rz_debug>1) PMS.dbg("DLNAResource: stopPlaying: refcnt="+requestIdToRefcount.get(requestId));
		//externalProcess=null;	//regzamod, for test --> when rewind , buffer will be cleared: Bad effect
	}
	
	public double convByteToTime(long bytes, RendererConfiguration mediarenderer,int mode) {	//regzamod
		long bps=0;
		double cbr_kbps=0;
		double rz_kbps=mediarenderer.getRZ_BitrateForTimeSeek()*1024;
		double timeseek;
		
		timeseek=(bytes * 8.0)/(rz_kbps *1024);  //defaults
		
		if(getMedia()!=null) {
			bps=getMedia().getRealVideoBitrate();
			//if(PMS.rz_debug>1) PMS.dbg("convByteToTime: media bps="+(bps/1048576.0)+" Mbps");
		}
		if(mode==1 && bps>0) {
			//use per_media bitrate
			if (bytes > bps) {
				timeseek= (bytes * 8.0) / bps ;
			}
		}
		else {
			//use assumed or constant bitrate
			if(bitrateForTimeSeek>0) {
				cbr_kbps=bitrateForTimeSeek;
			}
			else {
				cbr_kbps =rz_kbps;
			}
			if(cbr_kbps<=0) {
				cbr_kbps = mediarenderer.getCBRVideoBitrate();	//kbps
			}
			bps = (int) ((cbr_kbps + 256) * 1024 * 1.04); // bps, 1.04 = container overhead
			if (bytes > bps && bps>0) {
				timeseek=(bytes * 8.0) / bps ;
			}
		}
		if(PMS.rz_debug>2) PMS.dbg("convByteToTime: mode="+mode
			+", seekBytes="+bytes
			+", rz_kbps="+rz_kbps
			+", cbr_kbps="+cbr_kbps
			+", seekTime="+timeseek);
		return timeseek;
	}

	public void extProcEnded(ProcessWrapper pw) {	//regzamod, add
		if(PMS.rz_debug>2) PMS.dbg("DLNAResource: extProcEnded proc="+pw+", externalProcess="+externalProcess);
		if(externalProcess==pw) {
			//if(PMS.rz_debug>1) PMS.dbg("DLNAResource: name="+getName()+", extProcEnded: same proc --> set null to externalProcess");
			//externalProcess=null;
		}
	}
	
	/**Returns an InputStream of this DLNAResource that starts at a given time, if possible. Very useful if video chapters are being used.
	 * @param range
	 * @param mediarenderer
	 * @return The inputstream
	 * @throws IOException
	 */
	public InputStream getInputStream (long lowRange,long highRange, Range range, RendererConfiguration mediarenderer) throws IOException {
		return getInputStream(lowRange,highRange,range,mediarenderer,false,-1,null);
	}
	
	//for synchronize test, no good effects
	public InputStream getInputStream (long lowRange,long highRange, Range range, RendererConfiguration mediarenderer,
		boolean need_time_bias,int seekmode, rz_SessionInfo sess) throws IOException {
		InputStream inp;
		if(PMS.getServerStopping()) return null;
		synchronized (this) {
			inp=getInputStream_in(lowRange,highRange,range,mediarenderer,need_time_bias,seekmode,sess);
		}
		return inp;
	}
	
	//public InputStream getInputStream(Range range, RendererConfiguration mediarenderer,
	//	boolean need_time_bias,int seekmode) throws IOException {
	public InputStream getInputStream_in (long lowRange,long highRange,Range range, RendererConfiguration mediarenderer,
		boolean need_time_bias,int seekmode_in, rz_SessionInfo sess) throws IOException {
			
		//if(PMS.rz_debug>1) PMS.dbg("====>> getInputStream: Start CurrentSeekMode="+sess.CurrentSeekMode+", range="+range);
		//Type of param range isn't always match to real seekmode.
		//param seekmode: so, this indicate original seekMode from renderer, =0:Unknown,=1:ByteSeek,=2:TimeSeek
		//param sess.CurrentSeekMode: is more accurate for judge, than param seekmode(sometimes become Unknown per Request) 
		//but, fails when no FF/SKIP occured
		
		//foolNetty may not detect channel_close, during responce making
		//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.getInputStream: Channel.isWritable 1="+sess.ctx.getChannel().isWritable());
			
		//----------------------------------
		// CAUTION: given param range:
		// is already added bias(for patial/resume play) by RequestV2.java
		// and has double meaning values for byte_seek/time_seek : that's crazy!! difficult to detect witch is
		//----------------------------------
		
		//---- shagrath: small fix, regression on chapters
		boolean timeseek_auto = false;
		int seekmode;
		// Ditlew - WDTV Live
		// Ditlew - We convert byteoffset to timeoffset here. This needs the stream to be CBR!
		//int cbr_video_bitrate = (int)(mediarenderer.getRZ_BitrateForTimeSeek()*1024);	//regzamod
		long def_bps=(long)(mediarenderer.getRZ_BitrateForTimeSeek()*1024);
		long cbr_video_bitrate = def_bps;	//regzamod
		if(cbr_video_bitrate<0) {
			cbr_video_bitrate = mediarenderer.getCBRVideoBitrate();
		}
		
		//long low = range.isByteRange() && range.isStartOffsetAvailable() ? range.asByteRange().getStart() : 0;
		//long high = range.isByteRange() && range.isEndLimitAvailable() ? range.asByteRange().getEnd() : -1;
		long low = lowRange;
		long high = highRange;
		
		Range.Time timeRange = range.createTimeRange();
		double timeseek_sv=timeRange.getStartOrZero();
		long low_sv=low;
		
		if(PMS.rz_debug>1 || PMS.rz_trace>0) {
			PMS.dbg("==== dlna.getInputStream: Start, low="+low+", high="+high+", timeRange=" + timeRange 
				+", seekmode="+seekmode_in+", sess.CurrentSeekMode="+sess.CurrentSeekMode); //regzamod
			//PMS.dbg("     resStartTime="+(resStartTime/1000.0)); //regzamod
		}
		seekmode=sess.CurrentSeekMode;
		
		if(last_errtim > (System.currentTimeMillis()-1000)) {
			//PMS.dbg("DLNAResource.getInputSTream: errors in short time --> return Noop");
			last_errtim=System.currentTimeMillis();
			return null;
		}
		
		if(getSystemName()==null) {
			//target not exist?
			return null;
		}
		if(this instanceof RealFile) {
			File f=new File(getSystemName());
			if(!f.exists()) {
				logger.error("DLNAResource.getInputStream: file not exists name="+f.getName());
				last_errtim=System.currentTimeMillis();
				return null;
			}
		}

		if(meta_type>0 && !rz_MetaBodyExists) {
			//I'm PlayMetafile, and target File seems not exist.
			return null;
		}
			
		//TEST
		/*
		if(low==0 && high==1443839) {
			if(PMS.rz_debug>1) PMS.dbg("dlna.getInputStream: hi="+high+ ", sort play, let's go-on to end");
			high=0;
		}
		*/

		if (getPlayer() != null && low > 0 && cbr_video_bitrate > 0) {
		//if (low > 0 && cbr_video_bitrate > 0) {
			long used_byte_ps = (long) ((cbr_video_bitrate + 256) * 1024 / 8 * 1.04); // 1.04 = container overhead
			if (low > used_byte_ps) {
				Double time=0d;
				if(false) {
					//this is [less bad] than convByteToTime() orz ..
					timeRange.setStart(((double)low / used_byte_ps));
					time=timeRange.getStartOrZero();
				} 
				else {
					//convByteToTime is not correct in case of byteseek
					//what the heck logic REGA byteseek using??
					time=convByteToTime(low, mediarenderer,0)+timeRange.getStartOrZero();
					//if(PMS.rz_debug>1) PMS.dbg("byteseek="+low+", conv timeseek="+time);
				}
				timeRange.setStart(time);
				low = 0;

				// WDTV Live - if set to TS it asks multiple times and ends by
				// asking for an invalid offset which kills mencoder
				if (getMedia()!=null && timeRange.getStartOrZero() > getMedia().getDurationInSeconds()) {
					if(getMedia().getDurationInSeconds()>0) 	//regzamod, for not WEB contents
						return null;
				}

				// Should we rewind a little (in case our overhead isn't accurate enough)
				int rewind_secs = mediarenderer.getByteToTimeseekRewindSeconds();
				timeRange.rewindStart(rewind_secs);
				if(PMS.rz_debug>1) {
					PMS.dbg("getInputStream: default byteseek low="+low_sv+" ,bitrateForTimeSeek="+bitrateForTimeSeek
						+" ,converted timeseek="+time+", rewind_secs="+rewind_secs);
				}
				//shagrath:
				timeseek_auto = true;
			}
		}
		
		if(getPlayer() == null && timeseek_sv!=0 && !PMS.rz_direct_seek) {
			//PMS.dbg("getInputStream_in: timeseek="+timeseek_sv+" isn't Zero --> Let's Remux");
			setPlayer(player2);
		}

		if (getPlayer() == null) {
			//-----------------------------------------------------------------------
			// Direct Play
			//-----------------------------------------------------------------------
			double time_seek=0;
			if(PMS.rz_debug>1) PMS.dbg("getInputStream: type1 (getPlayer() == null)");	//regzamod
			if (this instanceof IPushOutput) {
				//--- read from File in resource data
				if(PMS.rz_debug>1) PMS.dbg("getInputStream: type1 IPushOutput");
				PipedOutputStream out = new PipedOutputStream();
				InputStream fis = new PipedInputStream(out);
				((IPushOutput) this).push(out);
				if (fis != null) {
					if (low > 0) {
						fis.skip(low);
					}
					fis = wrap(fis, high, low);
				}
				
				// http://www.ps3mediaserver.org/forum/viewtopic.php?f=11&t=12035
				if (high > low && fis != null) {
					long bytes = (high - (low < 0 ? 0 : low)) + 1;

					logger.trace("Using size-limiting stream (" + bytes + " bytes)");
					SizeLimitInputStream slis = new SizeLimitInputStream(fis, bytes);
					return slis;
				}
				//---- regzamod, test for wait buffered 
				//waitStreamReady(fis);  //regzamod, no good effects
				return fis;
			}

			if(PMS.rz_debug>1) PMS.dbg("getInputStream: type1 Not-IPushOutput, timeRange="+timeRange);
			InputStream fis = null;
			if (getExt() != null && getExt().isImage() && getMedia() != null && getMedia().getOrientation() > 1 && mediarenderer.isAutoRotateBasedOnExif()) {
				// seems it's a jpeg file with an orientation setting to take care of
				fis = ImagesUtil.getAutoRotateInputStreamImage(getInputStream(), getMedia().getOrientation());
				if (fis == null) // error, let's return the original one
				{
					fis = getInputStream();
				}
			} else {
				fis = getInputStream();
			}
			if (fis != null) {
				long seek_bytes=low;
				double bias_time=this.rz_PartPlaySttp;
				
				//video_stream segment_unit length
				int smod=192;  //m2ts
				if(low>0) {
					if(low%188==0) smod=188;  //mpegts
				}
				else {
					if(isTS()) smod=188;  //mpegts
				}
				
				if(PMS.rz_direct_seek){	//byteseek and PMS.rz_direct_seek
					long bias_bytes=0;
					if(bias_time>0) {
						if(false && this instanceof RealFile) {
							//getPossitionForTimeInMpeg doesn't work well
							File f=((RealFile) this).getFile();
							bias_bytes=MpegUtil.getPossitionForTimeInMpeg(f, (int)bias_time,(double)0);
							//PMS.dbg("DLNAReource.getInputStream: getPossitionForTimeInMpeg: bias_bytes="+bias_bytes);
							if(bias_bytes<=0) {
								bias_bytes=(long)(bias_time*this.getMedia().getSize()/getMedia().getDurationInSeconds());
							}
						}
						else {
							bias_bytes=(long)(bias_time*this.getMedia().getSize()/getMedia().getDurationInSeconds());
						}
					}
					seek_bytes +=bias_bytes;
					seek_bytes= seek_bytes/smod*smod;  //segment align
					fis.skip(seek_bytes);
					fis = wrap(fis, high, seek_bytes);
					
					double seek_time=getMedia().getDurationInSeconds()*(float)seek_bytes/getMedia().getSize();
					//double seek_time=convByteToTime(seek_bytes, mediarenderer,1);
					PMS.rz_resume.playStarted(this,seek_time);
					//if(PMS.rz_debug>=0) PMS.dbg("Direct_seek: low="+low+", bias_time="+bias_time+", seek_time="+seek_time+", seek_bytes="+seek_bytes);
				}
				else { // Not Direct_seek_mode
					if (seek_bytes > 0) {
						fis.skip(seek_bytes);
					}
					if (timeRange.getStartOrZero() > 0 && this instanceof RealFile) {  //for timeseek
						File f=((RealFile) this).getFile();
						seek_bytes=MpegUtil.getPossitionForTimeInMpeg(f, (int)timeseek_sv,(double)0);
						if(seek_bytes<=0) {  //getPossitionForTimeInMpeg() seems failed
							seek_bytes=(long)(timeRange.getStartOrZero()*getMedia().getSize()/(float)getMedia().getDurationInSeconds());
							seek_bytes= seek_bytes/smod*smod;  //segment align
							if(seek_bytes>getMedia().getSize()) {
								seek_bytes=getMedia().getSize();
							}
						}
						fis.skip(seek_bytes);
					}
					// http://www.ps3mediaserver.org/forum/viewtopic.php?f=11&t=12035
					fis = wrap(fis, high, seek_bytes);
					
					double seek_time=getMedia().getDurationInSeconds()*(float)seek_bytes/getMedia().getSize();
					PMS.rz_resume.playStarted(this,seek_time);
					//if(PMS.rz_debug>=0) PMS.dbg("Normal_seek: low="+low+", bias_time="+bias_time+", seek_time="+seek_time+", seek_bytes="+seek_bytes);
				}
			}
			return fis;
		} else {
			//-----------------------------------------------------------------------
			// Transcode/Remux Play
			//-----------------------------------------------------------------------
			OutputParams params = new OutputParams(PMS.getConfiguration());
			params.ctx=sess.ctx;	//regzamod
			params.sess=sess;	//regzamod
			params.aid = getMediaAudio();
			params.sid = getMediaSubtitle();
			params.mediaRenderer = mediarenderer;
			Double timeseek_add=timeRange.getStart(); // regzamod, memory before getSplitRange()
			timeRange.limit(getSplitRange());
			params.timeseek = timeRange.getStartOrZero();
			params.timeend = timeRange.getEndOrZero();
			params.shift_scr = timeseek_auto;
			Double bias=getSplitRange().getStart();//regzamod 
			double duration=10*3600; // max 10 hr

			if(getMedia()!=null && getMedia().getDurationInSeconds()>0) {
				duration=getMedia().getDurationInSeconds();
			}
			else if(wmed_duration>0) duration=wmed_duration;  //web video
			
			//PMS.dbg("rz_PartPlaySttp="+rz_PartPlaySttp+", timeseek="+params.timeseek);
			if(PMS.rz_debug>2) {
				PMS.dbg("getInputStream: type2 (getPlayer() != null) ");
				PMS.dbg("  seek_mode="+seek_mode+", seekmode="+seekmode+", sess.CurrentSeekMode="+sess.CurrentSeekMode
						+", timeseek="+params.timeseek
						+", startTime="+resStartTime/1000.0+", startTimeBias="+resStartTimeBias/1000.0
						+", pauseTime="+pauseTime+", resumeTime="+resumeTime/1000.0);
			}

			if(range.isTimeRange() && need_time_bias) {	
				//chaptered file && not 1st chapter
				params.timeseek +=timeseek_add;
			}
			//------------------------------------------------------------------------------------
			// for SeverSide TimeSeek/Pause
			//------------------------------------------------------------------------------------
			int BSH_ENABLE			=0x01;
			int BSH_REGZA_PAUSE_HACK=0x02;
			int BSH_CALIB_START		=0x04;
			int BSH_CALIB_ALWAYS	=0x08;
			int hack=mediarenderer.getRZ_ByteSeekHack();
			int vseek=0;   // >0: VirtualTimeseek executed
			
			//pauseTime means TotalPlayed-time from top to last pause/end 
			//name is somewhat misleading: really, it simply equals to "Total Played-time"
			double pauseTimeSec=pauseTime/1000.0;
			
			//part-time in TotalPlayed-time that not correspond to given byteseek
			//i.e. these are playedTime from top to last resume/restart, 
			//after every resume/restart, byteseek will be resetted to 0  
			double velapsed_bias=(resStartTimeBias+resStartTimeBias2)/1000+timeseek_sv; 
			
			//part-time in TotalPlayed-time that correspond to given byteseek
			double velapsed=pauseTimeSec-velapsed_bias; 
			
			//----------------------------------------------------------------------
			// VirualTimeSeek (SeverSideTimeSeek)
			//----------------------------------------------------------------------
			if((hack&BSH_ENABLE)!=0 && seekmode<=1 && pauseTime>0) {
				if(PMS.rz_debug>1) {
					PMS.dbg("==== VirualTimeSeek Start"
						+", Given ByteSeek="+low_sv
						+", Given params.timeseek="+timeseek_sv
						+", TotalPlayTime="+pauseTimeSec
						+", LastPlayTime="+velapsed
						+", Assumed Mbps="+bitrateForTimeSeek/1000.0
					);
					if(PMS.rz_debug>1) {
						PMS.dbg("  "
							+", velapsed_bias="+velapsed_bias
							+", resumeTime="+resumeTime
							+", resStartTimeBias="+resStartTimeBias
							+", resStartTimeBias2="+resStartTimeBias2
							+", sess.CurrentSeekMode="+sess.CurrentSeekMode
							+", resStat="+resStat
							+", seekmode="+seekmode
							//---- not valid before calc 
							//+", New Timeseek="+params.timeseek
							//+", Pre Timeseek="+pauseTime/1000.0
							//+", SkipSec(New-Pre)="+(params.timeseek-pauseTime/1000.0)
						);
					}
				}
				
				String vseek_msg=null;
				low = 0;  //change to timeseek
				if(PMS.rz_debug>1) PMS.dbg("VirualTimeSeek: given ByteSeek="+low_sv
					+", params.timeseek="+timeseek_sv);
				
				//---- set default when not-the-case of following hacks, by general ByteSeek logic
				long nowTime=System.currentTimeMillis();
				long pause_min=(long)(mediarenderer.getRZ_ByteSeekPauseMin()*1024);
				long pause_msec=nowTime-(resStopRealTime==0?nowTime:resStopRealTime);
				long min_bps=(long)(mediarenderer.getRZ_BitrateForTimeSeekMin()*1024);
				long max_bps=(long)(mediarenderer.getRZ_BitrateForTimeSeekMax()*1024);
				
				if(PMS.rz_debug>2) {
					PMS.dbg("VirualTimeSeek: CheckResult=True, Exec calculations for VirualTimeSeek");
					if(PMS.rz_debug>2 && low_sv>0 && getMedia()!=null ) {
						DLNAMediaInfo med=getMedia();
						double dur=med.getDurationInSeconds();
						long size=med.getSize();
						long br=med.getRealVideoBitrate();
						PMS.dbg("VirualTimeSeek: ByteSeek, SeekBytes="+low_sv+", bitrate="+br+", duration="+dur+",size="+size);
						if(dur>0 && size>0) {
							PMS.dbg("ByteSeek, Time_conv(sec) by Realsize="+(dur*low_sv/size/10));
							PMS.dbg("ByteSeek, Time_conv(sec) by Bitrate="+(low_sv/br*8.0));
							PMS.dbg("ByteSeek, Time_conv(sec) by DLNAsize="+(dur*low_sv/10000000000.0));
						}
					}
				}
				
				if(pause_msec<pause_min) {	
					//----------------------------------------------------------------
					// Stop-start in short time 
					//----------------------------------------------------------------
					if(PMS.rz_debug>2) {
						PMS.dbg("VirualTimeSeek Type-1: SHORT_TIME Stop and Restart");
						PMS.dbg(" pause_msec="+pause_msec+", pause_min="+pause_min);
					}
					if(params.timeseek==0 && low_sv==0) {
						//----------------------------------------------------------------
						// Preview stop or backward ChapterSkip
						//----------------------------------------------------------------
						if(PMS.rz_debug>1) {
							PMS.dbg("VirualTimeSeek Type-1-1: seems to be Preview stop or backward ChapterSkip");
							PMS.dbg(" --> set current resStartTime="+resStartTime+" to resStartTimeBias2");
						}
						//backward ChapterSkip:Really, PMS works not to top but to lastStart pos
						//resStartTime=resStartTime;
						resStartTimeBias2 =resStartTime;
					}
					else {
						//----------------------------------------------------------------
						// Skip/FF/RW (stop once & suddenly start from new pos)
						//----------------------------------------------------------------
						if(PMS.rz_debug>2) PMS.dbg("VirualTimeSeek Type-1-2: seems to be SKIP/FF/FR, hack="+hack+", low="+low_sv);
						if((hack&(BSH_CALIB_START|BSH_CALIB_ALWAYS))!=0) {
							if(PMS.rz_debug>2) PMS.dbg("VirualTimeSeek Type-1-2-1: stop once & suddenly start from new pos");
							
							double time_ratio=mediarenderer.getRZ_ByteSeekCalibTimeRatio();
							double time_lag=mediarenderer.getRZ_ByteSeekCalibTimeLag();
							double seek_prev=(pauseTimeSec)*time_ratio-time_lag;	//last stopped(pause) time
							double seek_now=params.timeseek;						//seektime calced by current assumed bps
							double skipsec_r=mediarenderer.getRZ_ByteSeekCalibSkip();
							double skipsec_p=seek_now-seek_prev;
							double skip_range=mediarenderer.getRZ_ByteSeekCalibSkipRange();
							double calib_mintime =mediarenderer.getRZ_ByteSeekCalibTimeMin();
							double calib_mintime2=mediarenderer.getRZ_ByteSeekCalibTimeMin2();
							long kbps,kbps1=0;
							
							if(skipsec_p>300) skipsec_p=300;
							if(skipsec_p<-300) skipsec_p=-300;
							if(bitrateForTimeSeek<=0) {
								bitrateForTimeSeek=def_bps;
							}
							
							if(PMS.rz_debug>2) {
								PMS.dbg("Byteseek="+low_sv+", timeseek="+timeseek_sv+", pauseTimeSec="+pauseTimeSec);
								PMS.dbg("seek_prev="+seek_prev+", seek_now="+seek_now+", skipsec_p(assumed)="+skipsec_p);
								PMS.dbg("skipsec_r="+skipsec_r+" ,skip_range="+skip_range);
								PMS.dbg("time_ratio="+time_ratio+", time_lag="+time_lag);
								PMS.dbg("resStartTimeBias="+resStartTimeBias+", resStartTimeBias2="+resStartTimeBias2);
								PMS.dbg("If(skipsec_p within skip_range) then Re-calc bps(=Byteseek/(seek_prev-timeseek+skipsec_r))");
								PMS.dbg("Note: in byteseek mode, timeseek is used seek_bias for chapter/resume play");
							}
							//if(low_sv>5000000 && seek_now>=0 && skipsec_p > -skip_range && skipsec_p < skip_range) {	
							if(low_sv>5000000 && seek_now>=0) {	
								//---- skip within skip_range
								if((hack&0x4)>0 || (hack&0x2)>0 && seek_prev<=time_lag) {
									if(PMS.rz_debug>2) {
										PMS.dbg("VirualTimeSeek Type-1-2-1-1 (skip within skip_range)");
									}
									
									//---- calc new bps & calc timeseek using the bps
									if(velapsed>calib_mintime) {
										//--------------------------------------------------------------
										// Type(B): Seek after LONG time played:
										// 1) calc bitrate=(given byteseek)/palytime+skipTime_const
										// 2) calc timeseek=(given byteseek)/bitrate
										// skipTime_const: 
										//    use given constant(default: 30sec)
										//    anyway, skipTime is negligible compared to Long playtime
										//--------------------------------------------------------------
										if(PMS.rz_debug>2) PMS.dbg("pauseTimeSec > calib_mintime="+calib_mintime+": use SimpleBpsCalc");
										boolean do_calc=false;
										//if(velapsed>calib_mintime2 && bpsCalcCnt<100) {  //re-calc
										if(velapsed>calib_mintime2 && bpsCalcCnt<1) { //calc-only once
											//re-calc once after LONG time played, 
											//even if already calced on short play
											//-> But re-calc may not be accurate,
											//b/c may include many not-acculate skipTimes
											do_calc=true;
											bpsCalcCnt+=100;
											vseek=2;   //calc after long play (typeB)
											if(PMS.rz_debug>1) {
												vseek_msg="calc after long play (typeB)";
											}
										}
										else if(bpsCalcCnt<1) {
											//calc sometime if playtime is SHORT
											do_calc=true;
											bpsCalcCnt+=1;
											vseek=1;	//calc bps after short play (typeB)
											if(PMS.rz_debug>1) {
												vseek_msg="calc bps after short play (typeB)";
											}
										}
										if(do_calc) {
											// calc bps from given byteseek & playtime
											kbps=(long)((low_sv*8.0/(velapsed+skipsec_r))/1000.0*time_ratio); // kbps
											bitrateForTimeSeek=kbps;
											
											if(PMS.rz_debug>1) {
												/* heavy load?
												String s_Mbps=String.format("%.2f",kbps/1000.0);
												String s_pauseTimeSec=String.format("%.2f",pauseTimeSec);
												String s_velapsed=String.format("%.2f",velapsed);
												String s_velapsed_bias=String.format("%.2f",velapsed_bias);
												String s_skipsec_r=String.format("%.2f",skipsec_r);
												String s_calib_mintime=String.format("%.2f",calib_mintime);
												String s_calib_mintime2=String.format("%.2f",calib_mintime2);
												*/
												
												PMS.dbg("BitrateCalc TypeB-"+vseek
													+": Mbps="+(kbps/1000.0)
													+", (Total PlayTime="+pauseTimeSec
													+", Last PlayTime="+velapsed
													+", ResumeTime="+velapsed_bias
													+", skip="+skipsec_r
													+", time_ratio="+time_ratio
													+", calib_mintime="+calib_mintime
													+", calib_mintime2="+calib_mintime2+")"
												);
												PMS.dbg("Mbps=(byteSeek*8.0)/(last_PlayTime+skip)/1000000.0*time_ratio");
											}
										}
										else { // use calced bps
											vseek=3;  //use calced bps (typeB)
											if(PMS.rz_debug>1) {
												vseek_msg="use calced bps (typeB)";
											}
											kbps=bitrateForTimeSeek;
										}
										params.timeseek=convByteToTime(low_sv, mediarenderer,0)+velapsed_bias;
									}
									else {
										//--------------------------------------------------------------
										// Type(A): Seek after SHORT time played
										// 1) calc bitrate=(given byteseek)/(playtime+skipTime_calced)
										// 2) calc timeseek=(given byteseek)/bitrate
										// skipTime_calced:
										//    calc skipTime from default bps 
										//    ->skipTime is not ignorable: not short comparing to short playtime 
										//    ->but calc skipTime from default bps is too roufgh, almost non-sence!
										//--------------------------------------------------------------
										if(PMS.rz_debug>2) PMS.dbg("pauseTimeSec < calib_mintime="+calib_mintime+": use AverageBpsCalc");
										//---- calc average bps
										kbps1=(long)(low_sv*8.0/(seek_prev-timeseek_sv+skipsec_r)/1000.0);  // kbps
										kbps=(long)((bitrateForTimeSeek*bpsCalcCnt+kbps1)/(double)(bpsCalcCnt+1));
										if(kbps < min_bps) {
											//kbps=min_bps;
											kbps=def_bps;
										}
										else if(kbps > max_bps){
											kbps=max_bps;
										}
										
										bitrateForTimeSeek=kbps;
										if(bpsCalcCnt<10) {
											bpsCalcCnt+=1;
										}
										
										//---- recalc timeseek, using averaged bps
										double sec_to=convByteToTime(low_sv, mediarenderer,0);  //conv. seekByte to seekTime
										sec_to=(sec_to+(resStartTimeBias+resStartTimeBias2)/1000)+timeseek_sv;
										double sec_skip=sec_to-pauseTimeSec;
										if(sec_skip>0 || skipsec_p>-2) {
											if(sec_skip>300) sec_skip=300; //skip max=300secs(5 minutes)
											else if(sec_skip<30) sec_skip=30;
										}
										else {
											if(sec_skip<-300) sec_skip=-300; //max=5 minutes
											else if(sec_skip>-20) sec_skip=-20; 
										}
										params.timeseek=pauseTimeSec+sec_skip;
										vseek=4; //calc after very short play (typeA)
										
										if(PMS.rz_debug>1) {
											/* heavy load?
											String s_Mbps=String.format("%.2f",kbps1/1000.0);
											String s_pauseTimeSec=String.format("%.2f",pauseTimeSec);
											String s_velapsed=String.format("%.2f",velapsed);
											String s_velapsed_bias=String.format("%.2f",velapsed_bias);
											String s_skipsec_r=String.format("%.2f",skipsec_r);
											String s_calib_mintime=String.format("%.2f",calib_mintime);
											String s_calib_mintime2=String.format("%.2f",calib_mintime2);
											*/
											
											vseek_msg="calc after very short play (typeA)";
											PMS.dbg("BitrateCalc TypeA-"+vseek
												+": Mbps="+(kbps1/1000.0)
												+" (Total PlayTime="+pauseTimeSec
												+", Last PlayTime="+velapsed
												+", ResumeTime="+velapsed_bias
												+", Skip="+skipsec_r
												+", calib_mintime="+calib_mintime
												+", calib_mintime2="+calib_mintime2+")"
											);
										}
									}
								}
								else {
									vseek=100;
									if(PMS.rz_debug>1) {
										vseek_msg="BeteSeek Type-1-2-1-2: Noop -- SeverSideTimeseekMode is OFF";
									}
								}
							}
							else {
								vseek=101;
								if(PMS.rz_debug>1) {
									vseek_msg="BeteSeek Type-1-2-2: Noop (too short Byteseek<5000000 or Timeseek<0 :"
										+" Byteseek="+low_sv
										+", Timeseek="+params.timeseek;
								}
							}
							if(getMedia()!=null && bitrateForTimeSeek>0) {
								//getMedia().setBitrate((int)bitrateForTimeSeek*1000);
								//when target is audio(as video), this overwrite bps as audio
							}
							if(PMS.rz_debug>2) {
								PMS.dbg("VirualTimeSeek hack="+hack+", low_sv="+low_sv
									+", Assumed kbps="+bitrateForTimeSeek
									+"(min="+min_bps+", max="+max_bps+", def="+def_bps+")");
								PMS.dbg("Converted Timeseek="+params.timeseek+", skipsec_p="+skipsec_p);
							}
						}
						else {
							vseek=103;
							if(PMS.rz_debug>1) {
								vseek_msg="VirualTimeSeek Type-1-3: Noop (VseekHack(BSH_CALIB) is OFF: hack="+hack+")";
							}
						}
					}
				}
				else {
					//----------------------------------------------------------------
					// Stop - Restart in long time will be pause-restart.
					//----------------------------------------------------------------
					if(PMS.rz_debug>1) {
						PMS.dbg("VirualTimeSeek Type-3: LONG_TIME Stop and Restart");
						PMS.dbg("  pause(msec)="+pause_msec
							+", pauseTimeSec="+pauseTimeSec
							+", params.timeseek="+params.timeseek
							+", duration="+duration
							+", durationDelta="+durationDelta
						);
					}
					if((hack&BSH_REGZA_PAUSE_HACK)!=0
					 	&& params.timeseek<pauseTimeSec && resStat==RES_STOP && pauseTimeSec>1.0
						&& pauseTimeSec<duration-durationDelta
						) {
						vseek=5;  //Restart after pause
						if(PMS.rz_debug>1) {
					 		vseek_msg="Restart after pause";
							PMS.dbg("VirualTimeSeek Type-3-1: Exec Restart after pause");
							PMS.dbg("VirualTimeSeek: seems to be restart after pause, set PauseTime to timeseek="
								+pauseTimeSec+" secs");
						}
						long pauseRewindTime=(long)(mediarenderer.getRZ_ByteSeekPauseRewind()*1000);
						//resStartTime=pauseTime+resStartTimeBias2 - pauseRewindTime;
						resStartTime=pauseTime - pauseRewindTime;
						resStartTimeBias=(long)(resStartTime-params.timeseek*1000);
						params.timeseek=resStartTime/1000.0;
					}
					else {
						vseek=105;
						if(PMS.rz_debug>1) {
							vseek_msg="BeteSeek Type-3-2: Noop: too short pause/restart or near end";
							PMS.dbg("VirualTimeSeek "+vseek_msg);
						}
					}
				}
				if(params.timeseek>duration) {
					if(PMS.rz_debug>1) PMS.dbg("timeseek="+params.timeseek+" is Over duration="+duration+" --> cut-off to the duration");
					//params.timeseek=duration-5;  //some wrong
				}
				
				//--------------------------------------------------------------------
				// vseek results dump
				//--------------------------------------------------------------------
				if(PMS.rz_debug>1) {
					if(vseek>=100) {  //Noop: seek ignored
						PMS.dbg("VirualTimeSeek Type"+vseek+":Noop, seek ignored. "+vseek_msg==null?"":vseek_msg);
					}
					PMS.dbg("==== VirualTimeSeek End:"
						+"  Timeseek="+params.timeseek
						+", Skip="+(params.timeseek-pauseTimeSec)
						+", Mbps="+bitrateForTimeSeek/1000.0
					);
					if(PMS.rz_debug>1) {
						PMS.dbg(" "
							+"  given ByteSeek="+low_sv
							+", given params.timeseek="+timeseek_sv
							+", prevTimeseek="+pauseTimeSec
							+", resStartTime="+resStartTime
							+", resumeTime="+resumeTime
							+", resStartTimeBias="+resStartTimeBias
							+", resStartTimeBias2="+resStartTimeBias2
							+", sess.CurrentSeekMode="+sess.CurrentSeekMode
							+", resStat="+resStat
							+", seekmode="+seekmode
						);
					}
				}
			}
			else {
				if(PMS.rz_debug>2) PMS.dbg("VirualTimeSeek: CheckResult=False2, Don't exec VirualTimeSeek");
			}
			resStat=RES_START;
			
			//-----------------------------------------------------------------------------------
			// now, tuning of given parameters fixed
			//-----------------------------------------------------------------------------------
			if(sess.channel_stat<0) {
				//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.getInputStream: channel_stat="+sess.channel_stat+", let's return");
				return null;
			}
			if(sess.CurrentDLNA!=this) {
				//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.getInputStream: target dlna changed-1, let's return");
				return null;
			}
			if (this instanceof IPushOutput) {
				params.stdin = (IPushOutput) this;
			}
			if (externalProcess == null || externalProcess.isDestroyed()) {
				//if(PMS.rz_debug>1) PMS.dbg("getInputStream: type2-1 () timeseek="+params.timeseek);	//regzamod
				if(PMS.rz_debug>1) {
					PMS.dbg("getInputStream: type2-1 (no prev_proc & getPlayer() != null),time_seek="+params.timeseek);	//regzamod
					PMS.dbg("Starting transcode/remux of " + getName());
					//PMS.dbg("  "+dlnaspec+", "+flags);	//regzamod
				}
				RequestV2.playstartf=1;	//regzamod
				if(this instanceof rz_VirtualVideoActionF || this instanceof VirtualVideoAction) {
					//PMS.dbg("DLNAResource.getInputSTream: exec rz_VirtualVideoActionF.enable()");
					((rz_VirtualVideoActionF)this).getInputStream();  //toggle enable, b/c enable() doesn't called except DIRECT play
				}
				externalProcess = getPlayer().launchTranscode(
					getSystemName(),
					this,
					getMedia(),
					params);
				
				if(PMS.rz_debug>1) {
					PMS.dbg("DLNAResourec.getInputStream: proc params:"
						+" WaitBeforeStart (msec)="  + params.waitbeforestart
						+", minBufferSize1 (Mbytes)=" 	 + params.minBufferSize
						+", minBufferSize2 (bytes)="     + params.secondread_minsize
					);
				}
				if (externalProcess!=null && params.waitbeforestart > 0) {
					try {
						Thread.sleep(params.waitbeforestart);
					} catch (InterruptedException e) {
						logger.error(null, e);
					}
					logger.trace("Finished sleeping for " + params.waitbeforestart + " milliseconds");
				}
			//} else if (params.timeseek > 0 && getMedia() != null && getMedia().isMediaparsed()) {
			} else if ( (bias==null && params.timeseek >0) || (bias!=null && bias!=params.timeseek) ) {  //regzamod, test
				if(PMS.rz_debug>1) PMS.dbg("getInputStream: type2-2 (prev_proc exist && timeseek>0) timeseek="+params.timeseek);	//regzamod
				//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.getInputStream: Channel.isWritable 2b="+sess.ctx.getChannel().isWritable());
				if(PMS.rz_debug>2) {
					if(getMedia()!=null) {
						PMS.dbg("getInputStream: type2-2, params.timeseek >0 ="+params.timeseek
							+", isMediaparsed()="+getMedia().isMediaparsed()
							+", DurationInSeconds="+getMedia().getDurationInSeconds());
					}
					else {
						PMS.dbg("getInputStream: type2-2, params.timeseek >0 ="+params.timeseek+", getMedia()=null");
					}
				}
				
				//check if seekable in buffer
				int buffer_seek=PMS.getConfiguration().getRZ_buffer_seek();
				//long bmin=((ProcessWrapperImpl)externalProcess).getReadCount();
				//long bmax=((ProcessWrapperImpl)externalProcess).getWriteCount();
				long mmx[]=((ProcessWrapperImpl)externalProcess).getBufferedRangeByte();
				if(PMS.rz_debug>1) PMS.dbg("getInputStream: SeekMode="+seekmode
					+", TimeSeek="+params.timeseek+", ByteSeek="+low_sv
					+", buffer_min="+mmx[0]+", buffer_max="+mmx[1]);
				
				boolean do_buffseek=false;
				if(seekmode==1 && buffer_seek>0) {  //buffer_seek on byte_seek
					do_buffseek=true;
				}
				else if(seekmode==2 && buffer_seek>1) {//buffer_seek on time_seek
					//currently, not accurate, to be refined
					do_buffseek=true;
					if(low_sv<=0) {
						long br=(getMedia()==null?24000000:getMedia().getRealVideoBitrate());
						low_sv=(long)(params.timeseek*(br/8));  //roufgh assuming 24Mbps !! to be refined
						//low_sv=low_sv/192*192;  //for m2ts, what do in case mpegts/mpegps?
					}
				}
				if(do_buffseek && low_sv>mmx[0] && low_sv<mmx[1]) {
					if(PMS.rz_debug>1) PMS.dbg("getInputStream: Seek seems be within buffer -> Exec seek in buffer");
					
					low=low_sv;
					int smod=192;
					if(isTS()) smod=188;  //mpegts
					low=low/smod*smod;   //segment align
				}
				else {	// seek pos is out of buffer, seek by transcoder
					if(PMS.rz_debug>1) PMS.dbg("getInputStream: reset buffer and kick new transcoder");
					//if (getMedia().getDurationInSeconds() > 0) {
					if (true || getMedia().getDurationInSeconds() > 0) {	//regzamod, test
						params.minBufferSize = 1;
						Runnable r = new Runnable() {
							@Override
							public void run() {
								externalProcess.stopProcess();
							}
						};
						new Thread(r).start();
						ProcessWrapper newExternalProcess = getPlayer().launchTranscode(
							getSystemName(),
							this,
							getMedia(),
							params);
						try {
							//Thread.sleep(1000);
							Thread.sleep(500);
						} catch (InterruptedException e) {
							logger.error(null, e);
						}
						if (newExternalProcess == null) {
							logger.trace("External process instance is null... sounds not good");
						}
						externalProcess = newExternalProcess;

					}
					else {
						if(PMS.rz_debug>1) PMS.dbg("getInputStream: type2-3, getMedia().getDurationInSeconds() <= 0");	//regzamod
					}
				}
			}
			else {
				InputStream is = externalProcess.getInputStream(low);
				boolean reuse_buffer=false;
				if(PMS.rz_debug>1) PMS.dbg("getInputStream: type2-3 (prev_proc exist && timeseek<=0) timeseek="
					+params.timeseek+", timeseek_prev="+timeseek_prev+", is="+is);
				/*
				if(is!=null && timeseek_prev==0) {
					if(PMS.rz_debug>1) PMS.dbg("getInputStream: Reuse buffer, setCanStop(false)");
					int limit=mediarenderer.getRZ_QuickStartHackTime();
					if(limit>0) {
						reuse_buffer=true;
						((WaitBufferedInputStream)is).getOutputStream().setCanStop(false,limit);
					}
				}
				if(false && reuse_buffer==false) {
					((WaitBufferedInputStream)is).getOutputStream().setCanStop(true,0);
				}
				*/
			}
			timeseek_prev=params.timeseek;
			if (externalProcess == null) {
				return null;
			}
			
			//---- wait_until_stream_ready  
			//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.getInputStream: Channel.isWritable 3="+sess.ctx.getChannel().isWritable());
			InputStream is = null;
			InputStream is_prev = null;
			int elapsed=0;
			int waitmax=PMS.getConfiguration().getRZ_wait_stream_max_time();
			int waitintvl=500; //msec
			int minbuf=PMS.getConfiguration().getRZ_wait_stream_min_buff();
			int avail_prev=0;
			int	avail=0;
			int cnt=0;
			/*
			if(low==0 && high>0 && high <=2000000) {  
				//test: typically low=1443839: not full_data request, may be preview start
				//needn't wait untill datat ready, let's quick return --> bad !!
				if(PMS.rz_debug>1) PMS.dbg("getInputStream :not full_data request -> don't wait until data ready, low="+low+", high="+high);
				minbuf=-1;
			}
			*/
			
			//if(PMS.rz_debug>1) PMS.dbg("getInputStream : wait_until_stream_ready start, wait_max="+waitmax+" msec, Start_ByteSeek="+low);
			//while (is == null && timer < 10) {	//origin
			while (elapsed < waitmax) {	//regzamod
				
				if(PMS.rz_debug>2) {
					PMS.dbg("DLNAResource.getInputStream: in Waiting loop for Stream Ready, wait_intvl(msec)="+waitintvl
					+", wait_max(msec)="+waitmax);
				}
				
				if(externalProcess == null || ((ProcessWrapperImpl)externalProcess).isStopping()) {
					//may be error occured (externalProcess died)
					if(PMS.rz_debug>2) PMS.dbg("getInputStream: waiting, externalProcess seems died! --> return null");
					return null;
				}
				is = externalProcess.getInputStream(low);
				
				if (is != null) {
					if(minbuf<0) {
						break;
					}
					avail=is.available();
					if(avail>minbuf) {
						break;
					}
					else if(avail_prev>0 && avail_prev>=avail) {
						//buffer not growing --> stop waiting
						break;
					}
				}
				else {
					if(is_prev!=null && is==null) {
						break;
					}
				}
				if(!((ProcessWrapperImpl)externalProcess).isRunning()) {
					if(PMS.rz_debug>2) PMS.dbg("DLNAResource.getInputStream: externalProcess seems died");
					last_errtim=System.currentTimeMillis();
					if(is!=null) {
						((WaitBufferedInputStream)is).getOutputStream().setCanStop(true,0);
						is.close();
						is=null;
					}
					//PMS.dbg("DLNAResource.getInputStream: externalProcess seems died --> TEST set is=Null");
					break;
				}
				if(sess.channel_stat<0) {
					//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.getInputStream: channel_stat="+sess.channel_stat+", let's return");
					break;
				}
				if(sess.CurrentDLNA!=this) {
					//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.getInputStream: target dlna changed-2, let's return");
					break;
				}

				if(PMS.getConfiguration().getRZ_reply_processing()>1) {
					//test for quick response when playtarget changed
					//seems have no good effects
					//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.getnputStream: in waitingloop, reply HttpResponseStatus.PROCESSING, test for quick response");
					HttpResponse rep= new DefaultHttpResponse(HttpVersion.HTTP_1_1, 
						HttpResponseStatus.PROCESSING);
					rep.setHeader("Server", PMS.get().getServerName());
					rep.setHeader(HttpHeaders.Names.CONNECTION, "keep-alive");
					ChannelFuture futurewk = sess.ctx.getChannel().write(rep);
				}

				//if(PMS.rz_debug>1) PMS.dbg("getInputStream: waiting, is="+is+", avail="+avail);
				is_prev=is;
				avail_prev=avail;
				try {
					Thread.sleep(waitintvl);
				} catch (InterruptedException e) {
				}
				elapsed +=waitintvl;
				cnt++;
				//if(PMS.rz_debug>1) PMS.dbg("DLNAResource.getInputStream: Channel.isWritable 4="+sess.ctx.getChannel().isWritable());
			}
			if(PMS.rz_debug>2) {
				PMS.dbg("getInputStream : wait_until_stream_ready ended, elapsed="+elapsed
					+" msec, avail_len="+avail);
				if(sess.ctx!=null) {
					PMS.dbg("getInputStream : Channel status" 
						+", isOpen="+sess.ctx.getChannel().isOpen() 
						+", isConnected="+sess.ctx.getChannel().isConnected() 
						+", isReadable="+sess.ctx.getChannel().isReadable() 
						+", isWritable="+sess.ctx.getChannel().isWritable()
					);
				}
			}

			// fail fast: don't leave a process running indefinitely if it's
			// not producing output after params.waitbeforestart milliseconds + 5 seconds
			// this cleans up lingering MEncoder web video transcode processes that hang
			// instead of exiting
			if (is == null && externalProcess != null && !externalProcess.isDestroyed()) {
				Runnable r = new Runnable() {
					@Override
					public void run() {
						logger.trace("External input stream instance is null... stopping process");
						externalProcess.stopProcess();
					}
				};
				new Thread(r).start();
			}
			if(is!=null) {
				PMS.rz_resume.playStarted(this,params.timeseek);	//regzamod
			}
			return is;
		}
	}

	private static InputStream wrap(InputStream input, long high, long low) {
		if (input != null && high > low) {
			long bytes = (high - (low < 0 ? 0 : low)) + 1;
			logger.trace("Using size-limiting stream (" + bytes + " bytes)");
			return new SizeLimitInputStream(input, bytes);
		}
		return input;
	}

	public String mimeType() {
		String s=mimeType_in();
		//PMS.dbg("DLNAResource.mimeType: name="+getName()+", mime="+s+", getPlayer="+getPlayer());
		return s;
	}
			
	public String mimeType_in() {
		//if(getForcedMimeType()!=null) {	//regzamod
		//	return getForcedMimeType();
		//}
		if (getPlayer() != null) {
			return getPlayer().mimeType();
		} else if (getMedia() != null && getMedia().isMediaparsed()) {
			return getMedia().getMimeType();
		} else if (getExt() != null) {
			return getExt().mimeType();
		} else {
			return getDefaultMimeType(getSpecificType());
		}
	}

	/**
	 * Prototype function. Original comment: need to override if some thumbnail works are to be done when mediaparserv2 enabled
	 */
	public void checkThumbnail() {
		// need to override if some thumbnail works are to be done when mediaparserv2 enabled
	}

	/**Checks if a thumbnail exists, and if possible, generates one.
	 * @param input InputFile to check or generate the thumbnail that is being asked for.
	 */
	protected void checkThumbnail(InputFile input) {
		if(PMS.rz_debug>2) PMS.dbg("DLNAResource.checkThumbnail: called");
		if (getMedia() != null && (!getMedia().isThumbready() || getMedia().getThumb()==null) 
			&& PMS.getConfiguration().getThumbnailsEnabled()) {
				
			if(PMS.rz_debug>2) PMS.dbg("DLNAResource.checkThumbnail: seems Thumbnail not ready ---> generate Thumbnail fname="+getName());
			getMedia().setThumbready(true);
			getMedia().generateThumbnail(input, getExt(), getType(),this);
			if (getMedia().getThumb() != null && PMS.getConfiguration().getUseCache() && input.getFile() != null) {
				PMS.get().getDatabase().updateThumbnail(input.getFile().getAbsolutePath(), input.getFile().lastModified(), getType(), getMedia());
			}
		}
		else { 
			//PMS.dbg("DLNAResource.checkThumbnail: seems Thumbnail ready ---> Noop fname="+getName()
			//	+", isThumbready="+(getMedia()==null?"null":getMedia().isThumbready()));
		}
	}

	/**TODO: (botijo) Prototype function?
	 * @return The inputstream
	 * @throws IOException
	 */
	public InputStream getThumbnailInputStream() throws IOException {
		/*if (specificType == 0)
		return getResourceInputStream("images/clapperboard-256x256.png");*/
		return getResourceInputStream("images/thumbnail-256.png");
	}

	public String getThumbnailContentType() {
		return HTTPResource.JPEG_TYPEMIME;
	}

	public int getType() {
		if (getExt() != null) {
			return getExt().getType();
		} else {
			return Format.UNKNOWN;
		}
	}

	/**Prototype function.
	 * @return true if child can be added to other folder.
	 * @see #addChild(DLNAResource)
	 */
	public abstract boolean isValid();

	public boolean allowScan() {
		return false;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString () {
		return this.getClass().getSimpleName() + " [id=" + getId() + ", name=" + getName() + ", full path=" + getResourceId() + ", ext=" + getExt() + ", discovered=" + isDiscovered() + "]";
	}

	/**
	 * Returns the specific type of resource. Valid types are defined in {@link Format}.
	 * 
	 * @return The specific type
	 */
	protected int getSpecificType() {
		return specificType;
	}

	/**
	 * Set the specific type of this resource. Valid types are defined in {@link Format}.
	 * @param specificType The specific type to set.
	 */
	protected void setSpecificType(int specificType) {
		this.specificType = specificType;
	}

	/**
	 * Returns the {@link Format} of this resource, which defines its capabilities.
	 * 
	 * @return The format of this resource.
	 */
	/*
	public Format getExt() {	//original
		return ext;
	}
	*/
	
	public boolean needRealFileMatching() {
		return false;
	}
	
	public boolean is_aav () {	//regzamod
		if(isFolder()) return false;
		boolean is_aav=(getSession()==null?false:getSession().is_aav);  //check if aav is enabled
		if(is_aav) { //aav is enabled
		 	//if(ext!=null && ext.isAudio()) {
		 	if(getType()==Format.AUDIO) {
		 		//PMS.dbg("is_aav: ext!=null && ext.isAudio() --> return true(1)");
				return true;
			}
			else if(getSpecificType()== Format.AUDIO) {
		 		//PMS.dbg("is_aav: getSpecificType()== Format.AUDIO --> return true(2)");
				return true;
			}
			else {
		 		//PMS.dbg("is_aav: --> return false(1)");
				return false;
			}
		}
		else {
		 	//PMS.dbg("is_aav: --> return false(2)");
			return false;
		}
	}
	public boolean is_iav () {	//regzamod
		if(isFolder()) return false;
		boolean is_iav=(getSession()==null?false:getSession().is_iav); //check if iav is enabled
		if(is_iav) { //iav is enabled
		 	if(ext!=null && ext.isImage()) {
				return true;
			}
			else if(getSpecificType()== Format.IMAGE) {
				return true;
			}
			else {
				return false;
			}
		}
		else {
			return false;
		}
	}
	
	public Format getExt() {	//regzamod
		return ext;
	}
	public Format getFormat() { //new func in ums?
		return ext;
	}

	/**
	 * Sets the {@link Format} of this resource, thereby defining its capabilities.
	 * 
	 * @param ext The format to set.
	 */
	protected void setExt(Format ext) {
		this.ext = ext;
	}
	
	//regzamod add
	public boolean isM2TS() {
		return this.ext!=null && this.ext.getMatchedId().equals("m2ts");
	}
	public boolean isTS() {
		return this.ext!=null && this.ext.getMatchedId().equals("ts");
	}
	
	//regzamod add
	public boolean needSort() {
		return sort_enabled;
	}

	/**
	 * Returns the {@link DLNAMediaInfo} object for this resource, containing the
	 * specifics of this resource, e.g. the duration.
	 * 
	 * @return The object containing detailed information.
	 */
	/*
	public DLNAMediaInfo getMedia() {	//original
		return media;
	}
	*/
	public DLNAMediaInfo getMedia() {	//regzamod
		return media;
	}

	/**
	 * Sets the the {@link DLNAMediaInfo} object that contains all specifics for
	 * this resource.
	 * 
	 * @param media The object containing detailed information.
	 * @since 1.50
	 */
	protected void setMedia(DLNAMediaInfo media) {
		this.media = media;
	}

	/**
	 * Returns the {@link DLNAMediaAudio} object for this resource that contains
	 * the audio specifics.
	 * 
	 * @return The audio object containing detailed information.
	 * @since 1.50
	 */
	//protected DLNAMediaAudio getMediaAudio() {
	public DLNAMediaAudio getMediaAudio() {
		return media_audio;
	}

	/**
	 * Sets the {@link DLNAMediaAudio} object for this resource that contains
	 * the audio specifics.
	 * 
	 * @param mediaAudio The audio object containing detailed information.
	 * @since 1.50
	 */
	protected void setMediaAudio(DLNAMediaAudio mediaAudio) {
		this.media_audio = mediaAudio;
	}

	/**
	 * Returns the {@link DLNAMediaSubtitle} object for this resource that
	 * contains the specifics for the subtitles.
	 * 
	 * @return The subtitle object containing detailed information.
	 * @since 1.50
	 */
	//protected DLNAMediaSubtitle getMediaSubtitle() {
	public DLNAMediaSubtitle getMediaSubtitle() {
		return media_subtitle;
	}

	/**
	 * Sets the {@link DLNAMediaSubtitle} object for this resource that
	 * contains the specifics for the subtitles.
	 * 
	 * @param mediaSubtitle The subtitle object containing detailed information.
	 * @since 1.50
	 */
	protected void setMediaSubtitle(DLNAMediaSubtitle mediaSubtitle) {
		this.media_subtitle = mediaSubtitle;
	}

	/**
	 * Returns the timestamp at which this resource was last modified.
	 * 
	 * @return The timestamp.
	 */
	public long getLastmodified() {
		return lastmodified;
	}

	/**
	 * Sets the timestamp at which this resource was last modified.
	 * 
	 * @param lastmodified The timestamp to set.
	 * @since 1.50
	 */
	protected void setLastmodified(long lastmodified) {
		this.lastmodified = lastmodified;
	}

	/**
	 * Returns the {@link Player} object that is used to encode this resource
	 * for the renderer. Can be null.
	 * 
	 * @return The player object.
	 */
	public Player getPlayer() {
		return player;
	}

	/**
	 * Sets the {@link Player} object that is to be used to encode this
	 * resource for the renderer. The player object can be null.
	 * 
	 * @param player The player object to set.
	 * @since 1.50
	 */
	protected void setPlayer(Player player) {
		if((mflags&MF_UNDER_TRASCO_FOLDER)==0) {  //not under trascoFolder
			this.player = player;
		}
	}
	protected void setPlayer_f(Player player) {  //force
		this.player = player;
	}

	/**
	 * Returns true when the details of this resource have already been
	 * investigated. This helps is not doing the same work twice.
	 * 
	 * @return True if discovered, false otherwise.
	 */
	public boolean isDiscovered() {
		return discovered;
	}

	/**
	 * Set to true when the details of this resource have already been
	 * investigated. This helps is not doing the same work twice.
	 * 
	 * @param discovered Set to true if this resource is discovered,
	 * 			false otherwise.
	 * @since 1.50
	 */
	protected void setDiscovered(boolean discovered) {
		this.discovered = discovered;
	}

	/**
	 * Returns true if this resource has subtitles in a file.
	 * 
	 * @return the srtFile
	 * @since 1.50
	 */
	protected boolean isSrtFile() {
		return srtFile;
	}

	/**
	 * Set to true if this resource has subtitles in a file.
	 * 
	 * @param srtFile the srtFile to set
	 * @since 1.50
	 */
	protected void setSrtFile(boolean srtFile) {
		this.srtFile = srtFile;
	}

	/**
	 * Returns the update counter for this resource. When the resource needs
	 * to be refreshed, its counter is updated.
	 * 
	 * @return The update counter.
	 * @see #notifyRefresh()
	 */
	public int getUpdateId() {
		return updateId;
	}

	/**
	 * Sets the update counter for this resource. When the resource needs
	 * to be refreshed, its counter should be updated.
	 * 
	 * @param updateId The counter value to set.
	 * @since 1.50
	 */
	protected void setUpdateId(int updateId) {
		this.updateId = updateId;
	}

	/**
	 * Returns the update counter for all resources. When all resources need
	 * to be refreshed, this counter is updated.
	 * 
	 * @return The system update counter.
	 * @since 1.50
	 */
	public static int getSystemUpdateId() {
		return systemUpdateId;
	}

	/**
	 * Sets the update counter for all resources. When all resources need
	 * to be refreshed, this counter should be updated.
	 * 
	 * @param systemUpdateId The system update counter to set.
	 * @since 1.50
	 */
	public static void setSystemUpdateId(int systemUpdateId) {
		DLNAResource.systemUpdateId = systemUpdateId;
	}

	/**
	 * Returns whether or not this is a nameless resource.
	 * 
	 * @return True if the resource is nameless.
	 */
	public boolean isNoName() {
		return noName;
	}

	/**
	 * Sets whether or not this is a nameless resource.
	 * 
	 * @param noName Set to true if the resource is nameless.
	 * @since 1.50
	 */
	protected void setNoName(boolean noName) {
		this.noName = noName;
	}

	/**
	 * Returns the from - to time range for this resource.
	 * 
	 * @return The time range.
	 */
	public Range.Time getSplitRange() {
		return splitRange;
	}

	/**
	 * Sets the from - to time range for this resource.
	 * 
	 * @param splitRange The time range to set.
	 * @since 1.50
	 */
	protected void setSplitRange(Range.Time splitRange) {
		this.splitRange = splitRange;
	}

	/**
	 * Returns the number of the track to split from this resource.
	 *  
	 * @return the splitTrack
	 * @since 1.50
	 */
	protected int getSplitTrack() {
		return splitTrack;
	}

	/**
	 * Sets the number of the track from this resource to split.
	 * 
	 * @param splitTrack The track number.
	 * @since 1.50
	 */
	protected void setSplitTrack(int splitTrack) {
		this.splitTrack = splitTrack;
	}

	/**
	 * Returns the default renderer configuration for this resource.
	 * 
	 * @return The default renderer configuration.
	 * @since 1.50
	 */
	//protected RendererConfiguration getDefaultRenderer() {
	public RendererConfiguration getDefaultRenderer () {
		return defaultRenderer;
	}
	public rz_SessionInfo getCurrentSession () { //regzamod
		return currentSession;
	}
	public rz_SessionInfo getSession () { //regzamod
		return (defaultRenderer==null?null:defaultRenderer.sess);
	}
	public RootFolder getRootFolder () { //regzamod
		rz_SessionInfo sess=getSession();
		return (sess==null?null:sess.getRoot());
	}
	
	private int isOnSlowMedia= -1;  //not checked yet
	public boolean isOnSlowMedia () {  //dlna target is under slow removable media(CD/DVD/BD)
		if(!isRealFile()) return false;
		if(isOnSlowMedia>=0) return (isOnSlowMedia==0?false:true);
		RootFolder root=getRootFolder();
		if(root==null) isOnSlowMedia=0;
		else if(root.isOnSlowMedia(this)) isOnSlowMedia=1;
		else isOnSlowMedia=0;
		return (isOnSlowMedia==0?false:true);
	}

	/**
	 * Sets the default renderer configuration for this resource.
	 * 
	 * @param defaultRenderer The default renderer configuration to set.
	 * @since 1.50
	 */
	protected void setDefaultRenderer(RendererConfiguration defaultRenderer) {
		this.defaultRenderer = defaultRenderer;
	}
	protected void setCurrentSession(rz_SessionInfo sess) {
		this.currentSession = sess;
	}

	/**
	 * Returns whether or not this resource is handled by Avisynth.
	 * 
	 * @return True if handled by Avisynth, otherwise false.
	 * @since 1.50
	 */
	protected boolean isAvisynth() {
		return avisynth;
	}

	/**
	 * Sets whether or not this resource is handled by Avisynth.
	 * 
	 * @param avisynth Set to true if handled by Avisyth, otherwise false.
	 * @since 1.50
	 */
	protected void setAvisynth(boolean avisynth) {
		this.avisynth = avisynth;
	}

	/**
	 * Returns true if transcoding should be skipped for this resource.
	 * 
	 * @return True if transcoding should be skipped, false otherwise.
	 * @since 1.50
	 */
	protected boolean isSkipTranscode() {
		return skipTranscode;
	}

	/**
	 * Set to true if transcoding should be skipped for this resource.
	 * 
	 * @param skipTranscode Set to true if trancoding should be skipped, false
	 * 			otherwise.
	 * @since 1.50
	 */
	protected void setSkipTranscode(boolean skipTranscode) {
		this.skipTranscode = skipTranscode;
	}

	/**
	 * Returns the list of children for this resource.
	 * 
	 * @return List of children objects.
	 */
	public List<DLNAResource> getChildren () {
		//if(false && sotype==SO_SORTMENU) {
		if(false && (mflags&MF_SORTMENU)!=0) {
			DLNAResource pa=getParent();
			pa=pa.getParent();
			if(pa!=null) {
				if(pa.rz_MetaSortType!=rz_MetaSortType) {
					//PMS.dbg("getChildren: mflags==MF_SORTMENU --> set gran_pa's sort type="
					//	+pa.rz_MetaSortType+" to"+rz_MetaSortType);
					pa.rz_MetaSortType=rz_MetaSortType;
					//PMS.dbg("getChildren: name="+getName()+", exec link Sort");
					pa.sort_enabled=true;
					MapFile.dlnaListSort(pa,pa.getChildren(),pa.getSortType());
					//pa.doSort=true;
					//pa.resolved=false;
				}
				return pa.getChildren();
			}
			return null;
		}
		return children;
	}
	
	public void ClearChildren() {  //regzam
		if(getChildren()!=null) {
			getChildren().clear();
		}
		initLastChildrenId();  //reset id to avoid eternal-increase
	}

	/**
	 * Sets the list of children for this resource.
	 * 
	 * @param children The list of children to set.
	 * @since 1.50
	 */
	protected void setChildren(List<DLNAResource> children) {
		this.children = children;
	}

	/**
	 * Returns the id of the last child added.
	 * 
	 * @return The id.
	 * @since 1.50
	 */
	protected int getLastChildrenId() {
		return lastChildrenId;
	}

	/**
	 * Sets the id of the last child added.
	 * 
	 * @param lastChildrenId The id to set.
	 * @since 1.50
	 */
	protected void setLastChildrenId(int lastChildrenId) {
		this.lastChildrenId = lastChildrenId;
	}
	public void initLastChildrenId() {
		this.lastChildrenId = 100;
	}

	/**
	 * Returns the timestamp when this resource was last refreshed.
	 * 
	 * @return The timestamp.
	 */
	public long getLastRefreshTime() {
		return lastRefreshTime;
	}

	/**
	 * Sets the timestamp when this resource was last refreshed.
	 * 
	 * @param lastRefreshTime The timestamp to set.
	 * @since 1.50
	 */
	public void setLastRefreshTime(long lastRefreshTime) {
		this.lastRefreshTime = lastRefreshTime;
	}
	
	//----- add, regzamod ---------------------
	//Original: according to judging remuxable, resembled codings used individualy in many places.
	// --> should use this common func.
	public int getRemuxable() {
		//for already judged results
		if(toString_sb==null)
			return -1;  //unknown
		else return mpegTsMux==true?1:0;
	}
	
	private boolean isRemuxable(RendererConfiguration mediaRenderer) {
		boolean muxable=false;
		DLNAMediaInfo med=getMedia();
		
		if(med==null) return false;
		
		//--- basic check
		muxable=med.isMuxable(mediaRenderer) 
			&& getMediaSubtitle() == null;
		
		//--- player check
		if(muxable && getPlayer()!=null && MEncoderVideo.ID.equals(getPlayer().id())) {
			//attatched player is Mencoder
			if(!PMS.getConfiguration().isMencoderMuxWhenCompatible()) {
				muxable=false;
			}
		}
		
		//--- extra check: size,aspects etc
		if(muxable && mediaRenderer.isRZ_RemuxJudgeForRegza()) {	//regzamod
			muxable=mediaRenderer.isRZ_NonTranscodeEx(
					med.getWidth(),med.getHeight(),med.getAspect_f(),
					getExt()==null?null:getExt().getMatchedId()
				);
		}
		return muxable;

		/* original
		boolean muxable=false;
		boolean h264mux=false;
		if(getPlayer() != null && getMedia()!=null) {
			h264mux=getMedia().isH264() && !VideoLanVideoStreaming.ID.equals(getPlayer().id()) 
						&& getMedia().isMuxable(mediaRenderer);
			muxable = TSMuxerVideo.ID.equals(getPlayer().id())
				 || VideoLanVideoStreaming.ID.equals(getPlayer().id());
				 
			if(!muxable) { // maybe, like the ps3, mencoder can launch tsmuxer if this a compatible H264 video
				muxable = MEncoderVideo.ID.equals(getPlayer().id()) 
					&& ((getMediaSubtitle() == null && getMedia() != null 
					&& getMedia().getDvdtrack() == 0 
					&& getMedia().isMuxable(mediaRenderer)
					&& PMS.getConfiguration().isMencoderMuxWhenCompatible() 
					&& mediaRenderer.isMuxH264MpegTS()));
					//|| mediaRenderer.isTranscodeToMPEGTSAC3());
				
				if(muxable && mediaRenderer.isRZ_RemuxJudgeForRegza()) {	//regzamod
					muxable=mediaRenderer.isRZ_NonTranscodeEx(getMedia().getWidth(),
						getMedia().getHeight(),getMedia().getAspect_f(),
						getExt()==null?null:getExt().getMatchedId());
				}
			}
		}
		return muxable;
		*/
	}
	
	//public String getForcedFFmpegPath() {
	//	if(rz_MetaFFmpegPath[0]!=null) return rz_MetaFFmpegPath[0];
	//	if(rz_MetaFFmpegPath[1]!=null) return rz_MetaFFmpegPath[1];
	//	return null;
	//}
	//public boolean is_DirectSeek() {
	//	if((getMetaFlags()&META_DIRECT_SEEK)!=0) return true;
	//	return PMS.rz_direct_seek;
	//}
	//public int getMetaFlags() {
	//	if(rz_MetaFlags[0]!=0) return rz_MetaFlags[0];
	//	if(rz_MetaFlags[1]!=0) return rz_MetaFlags[1];
	//	return 0;
	//}
		
	public String getForcedMencoderPath() {
		if(rz_MetaMencoderPath[0]!=null) return rz_MetaMencoderPath[0];
		if(rz_MetaMencoderPath[1]!=null) return rz_MetaMencoderPath[1];
		return null;
	}
	public String getForcedTitle() {
		//User shouldn't change system button name, easily without heavy thinks.
		//At least, like MF_BUTTON_ELEMENT that name is frequentry changed by sys
		//ToDO: also, shoud treat same for other sys objecs
		if((mflags & MF_BUTTON_ELEMENT)!=0) return null; //shoudn't be changed name by user
		
		if(rz_MetaTitle[0]!=null) return rz_MetaTitle[0];
		if(rz_MetaTitle[1]!=null) return rz_MetaTitle[1];
		return null;
	}
	public String getForcedMimeType() {
		if(rz_MetaMimeType[0]!=null) return rz_MetaMimeType[0];
		if(rz_MetaMimeType[1]!=null) return rz_MetaMimeType[1];
		return null;
	}
	public String getForcedDlnaProf() {
		if(rz_MetaDlnaProf[0]!=null) return rz_MetaDlnaProf[0];
		if(rz_MetaDlnaProf[1]!=null) return rz_MetaDlnaProf[1];
		return null;
	}
	public String getForcedDlnaOp() {
		if(rz_MetaDlnaOp[0]!=null) return rz_MetaDlnaOp[0];
		if(rz_MetaDlnaOp[1]!=null) return rz_MetaDlnaOp[1];
		return null;
	}
	public double getForcedZoom() {
		if(rz_MetaZoom[0]>0) return rz_MetaZoom[0];
		if(rz_MetaZoom[1]>0) return rz_MetaZoom[1];
		return -1;
	}
	public int getForcedTransType() {
		if((mflags&MF_UNDER_TRASCO_FOLDER)!=0) {
			//obj is under #- TRANSCODE -# folder, so shouldn't force trans_type
			//but trascoFoder its self forcing!!
			//return -1;
		}
		if(rz_MetaTransType[0]>=0) return rz_MetaTransType[0];
		if(rz_MetaTransType[1]>=0) return rz_MetaTransType[1];
		return -1;
	}
	public int getForcedTransFormat() {
		if(rz_MetaTransFormat[0]>=0) return rz_MetaTransFormat[0];
		if(rz_MetaTransFormat[1]>=0) return rz_MetaTransFormat[1];
		return -1;
	}
	public int getForcedTransFormatWeb() {
		if(rz_MetaTransFormatWeb[0]>=0) return rz_MetaTransFormatWeb[0];
		if(rz_MetaTransFormatWeb[1]>=0) return rz_MetaTransFormatWeb[1];
		return -1;
	}
	public int getForcedRemuxFormat() {
		if(rz_MetaRemuxFormat[0]>=0) return rz_MetaRemuxFormat[0];
		if(rz_MetaRemuxFormat[1]>=0) return rz_MetaRemuxFormat[1];
		return -1;
	}
	public int getForcedTransProc() {
		if(rz_MetaTransProc[0]>=0) return rz_MetaTransProc[0];
		if(rz_MetaTransProc[1]>=0) return rz_MetaTransProc[1];
		return -1;
	}
	public int getForcedRemuxProc() {
		if(rz_MetaRemuxProc[0]>=0) return rz_MetaRemuxProc[0];
		if(rz_MetaRemuxProc[1]>=0) return rz_MetaRemuxProc[1];
		return -1;
	}
	public String getForcedTransProg() {
		//PMS.dbg("getForcedTransProg: rz_MetaTransProg[0]="+rz_MetaTransProg[0]+", [1]="+rz_MetaTransProg[1]);
		if(rz_MetaTransProg[0]!=null) return rz_MetaTransProg[0];
		if(rz_MetaTransProg[1]!=null) return rz_MetaTransProg[1];
		return null;
	}
	public String getForcedRemuxProg() {
		if(rz_MetaRemuxProg[0]!=null) return rz_MetaRemuxProg[0];
		if(rz_MetaRemuxProg[1]!=null) return rz_MetaRemuxProg[1];
		return null;
	}
	public double getForcedSoundDelay() {
		double delay=0;
		if(rz_SoundDelay[0]!=0) delay+=rz_SoundDelay[0];
		if(rz_SoundDelay[1]!=0) delay+=rz_SoundDelay[1];
		return delay;
	}
	
	public String getTsmuxerOptVReprace(String fmt, String vcodec, String file, String fps, String opt, String track) {
		String str=null;
		if(fmt!=null && fmt.length()>0) {
			str=fmt.replace("%acodec%",vcodec);
			str=str.replace("%file%","\""+file+"\"");
			str=str.replace("%fps%",""+fps);
			str=str.replace("%opts%",""+opt);
			str=str.replace("%track_v%",track);
		}
		return str;
	}
	public String getTsmuxerOptAReprace(String fmt, String acodec, String file, String timeshift, String track) {
		String str=null;
		if(fmt!=null && fmt.length()>0) {
			str=fmt.replace("%acodec%",acodec);
			str=str.replace("%file%","\""+file+"\"");
			str=str.replace("%timeshift%",""+timeshift);
			str=str.replace("%track_a%",track);
		}
		return str;
	}
	
	public void resetMetaOpts(DLNAResource dlnp, int pos) {
		dlnp.rz_SoundDelay[pos]=0;
		dlnp.rz_MetaZoom[pos]=-1;
		
		if((dlnp.mflags&MF_UNDER_TRASCO_FOLDER)==0) {
			dlnp.rz_MetaTransType[pos]= -1;
		}
		else {
			//under trascoFolder: shoudn't be changed
		}
		dlnp.rz_MetaTransFormat[pos]= -1;
		dlnp.rz_MetaTransFormatWeb[pos]= -1;
		dlnp.rz_MetaRemuxFormat[pos]= -1;
		dlnp.rz_MetaRemuxProc[pos]= -1; 
		dlnp.rz_MetaRemuxProg[pos]= null; 
		dlnp.rz_MetaTransProc[pos]= -1; 
		dlnp.rz_MetaTransProg[pos]= null; 
		dlnp.rz_MetaDlnaProf[pos]=null;
		dlnp.rz_MetaDlnaOp[pos]=null;
		dlnp.rz_MetaMimeType[pos]=null;
		dlnp.rz_MetaTitle[pos]=null;
		dlnp.rz_MetaMencoderOpt[pos]=null;
		dlnp.rz_MetaMencoderOptA[pos]=null;
		//dlnp.rz_WebOpt[pos]=null;
		dlnp.rz_MetaFFmpegOpt[pos]=null;
		dlnp.rz_MetaTsmuxerOptA[pos]=null;
		dlnp.rz_MetaTsmuxerOptV[pos]=null;
		//dlnp.rz_MetaFFmpegPath[pos]=null;
		dlnp.rz_MetaMencoderPath[pos]=null;
		dlnp.player3=null;
		//dlnp.rz_MetaFlags[pos]=0;
	}
	
	public static void initMetaParams(DLNAResource dlnp) {
		/*public File*/  	dlnp.rz_Metafile=null;		// PlayMetafile
		/*public File*/  	dlnp.rz_Metafile2=null;		// linked-meta file
		/*public File*/  	dlnp.rz_Profile=null;		// PlayProfile
		/*public File*/  	dlnp.rz_Bodyfile=null;		// body file
		/*public String*/ 	dlnp.rz_Bodypath=null;		// path of body file
		/*public boolean*/ 	dlnp.rz_isPartPlay=false;		// partial_play (start/stop time) disignated
		/*public double*/ 	dlnp.rz_PartPlayDuration=0;	// partial_play duration (sec) 
		/*public double*/ 	dlnp.rz_PartPlaySttp=0;		// partial_play start time (sec)
		/*public double*/ 	dlnp.rz_PartPlayEndp=0;		// partial_play stop time (sec)
		/*public double*/ 	dlnp.rz_SeekAudioDelay=0;	// timeseek audio delay (sec)
		/*public double*/ 	dlnp.rz_SeekAudioMagnify=0;	// timeseek audio magnifying ratio
		/*public String*/ 	dlnp.rz_MetaAspect=null;	// force aspect_ratio
		/*public double[]*/ dlnp.rz_MetaShift[0]=0;		// force shift
		/*public double[]*/ dlnp.rz_MetaShift[1]=0;		// force shift
		/*public int*/ 		dlnp.rz_MetaSoundVolume= -1;// force volume (normal=256) 128=-6dBA512=+6dB,  -1:not setted
		/*public int*/ 		dlnp.rz_MetaSoundSync= -1;	// ffmpeg's -async value
		
		//---- pair values [0]: for pxm,[1]: for script
		dlnp.rz_MetaZoom[0]= -1;	// force zoom
		dlnp.rz_MetaZoom[1]= -1;	// force zoom
		dlnp.rz_SoundDelay[0]=0;		// Sound Delay (sec) 
		dlnp.rz_SoundDelay[1]=0;		// Sound Delay (sec) 
		dlnp.rz_MetaTransType[0]= -1;	// {ENC_DIRECT|ENC_REMUX|ENC_TRANSCODE|-1}, -1:not setted 
		dlnp.rz_MetaTransType[1]= -1;	// {ENC_DIRECT|ENC_REMUX|ENC_TRANSCODE|-1}, -1:not 		
		dlnp.rz_MetaTransFormat[0]= -1;	// {TRS_MPEGPS|TRS_MPEGTS|TRS_M2TS}, -1:not setted 
		dlnp.rz_MetaTransFormat[1]= -1;	// {TRS_MPEGPS|TRS_MPEGTS|TRS_M2TS}, -1:not setted 
		dlnp.rz_MetaRemuxFormat[0]= -1;	// {TRS_MPEGPS|TRS_MPEGTS|TRS_M2TS}, -1:not setted 
		dlnp.rz_MetaRemuxFormat[1]= -1;	// {TRS_MPEGPS|TRS_MPEGTS|TRS_M2TS}, -1:not setted 
		dlnp.rz_MetaRemuxProc[0]= -1;	// {213|222|333|-1},1:mencoder,2:ffmpeg,3:tsmuxer, -1:not setted 
		dlnp.rz_MetaRemuxProc[1]= -1;	// {213|222|333|-1},1:mencoder,2:ffmpeg,3:tsmuxer, -1:not setted 
		dlnp.rz_MetaRemuxProg[0]= null;	//  
		dlnp.rz_MetaRemuxProg[1]= null;	// 
		dlnp.rz_MetaTransProc[0]= -1;	// {213|222|333|-1},1:mencoder,2:ffmpeg,3:tsmuxer, -1:not setted 
		dlnp.rz_MetaTransProc[1]= -1;	// {213|222|333|-1},1:mencoder,2:ffmpeg,3:tsmuxer, -1:not setted 
		dlnp.rz_MetaTransProg[0]= null;	// 
		dlnp.rz_MetaTransProg[1]= null;	// 
		dlnp.rz_MetaDlnaProf[0]=null;
		dlnp.rz_MetaDlnaProf[1]=null;
		dlnp.rz_MetaDlnaOp[0]=null;
		dlnp.rz_MetaDlnaOp[1]=null;
		dlnp.rz_MetaMimeType[0]=null;
		dlnp.rz_MetaMimeType[1]=null;
		dlnp.rz_MetaTitle[0]=null;
		dlnp.rz_MetaTitle[1]=null;
		dlnp.rz_MetaMencoderOpt[0]=null;	// mencoder options for trasco
		dlnp.rz_MetaMencoderOpt[1]=null;	// mencoder options for trasco
		dlnp.rz_MetaMencoderOptA[0]=null;	// mencoder options for trasco (audio only)
		dlnp.rz_MetaMencoderOptA[1]=null;	// mencoder options for trasco (audio only)
		//dlnp.rz_WebOpt[0]=null;
		//dlnp.rz_WebOpt[1]=null;
		dlnp.rz_MetaFFmpegOpt[0]=null;		// ffmpeg options for remux
		dlnp.rz_MetaFFmpegOpt[1]=null;		// ffmpeg options for remux
		dlnp.rz_MetaTsmuxerOptA[0]=null;		// tsmuxer
		dlnp.rz_MetaTsmuxerOptA[1]=null;		// tsmuxer
		dlnp.rz_MetaTsmuxerOptV[0]=null;		// tsmuxer
		dlnp.rz_MetaTsmuxerOptV[1]=null;		// tsmuxer
		//dlnp.rz_MetaFFmpegPath[0]=null;		// ffmpeg path
		//dlnp.rz_MetaFFmpegPath[1]=null;		// ffmpeg path
		dlnp.rz_MetaMencoderPath[0]=null;	// Mencoder path
		dlnp.rz_MetaMencoderPath[1]=null;	// Mencoder path
		
		dlnp.rz_MetaChannel_v= -1;	// Video Channel No. -1:not setted
		dlnp.rz_MetaChannel_a= -1;	// Audio Channel No. -1:not setted
		dlnp.rz_MetaChannel_a_ffmpeg= -1;	// Audio Channel No. for ffmpeg when called from #TRANSCODE# virtual file
		dlnp.rz_MetaDispDate=0;
		dlnp.rz_MetaSortType =-1;
		dlnp.rz_MetaSortRecursive=false;
		dlnp.rz_MetaDVDchapter=null;
		dlnp.rz_MetaBodyExists=true;
		dlnp.rz_MetaNotExistSuffix=true;  //add (?) suffix to filename when file's body not found
		dlnp.pxf_resolved=false;
		dlnp.pxf_lastmodified=0;
		//judged_TPR=false;
		
		//---- from pxm , ugly params tobe refined
		dlnp.isVfolder=false;
		dlnp.isDVDISO=false;
		dlnp.dvd_title_num=0;	//dvd title number(valid >=1)
		dlnp.dvd_chapter_str=null;	//dvd chapter number string e.x. 1,2,1-5 
	}
	
	//---- wait_until_stream_ready  
	void waitStreamReady(InputStream is) {
		InputStream is_prev = null;
		int elapsed=0;
		int waitmax=PMS.getConfiguration().getRZ_wait_stream_max_time();
		int waitintvl=500; //msec
		int minbuf=PMS.getConfiguration().getRZ_wait_stream_min_buff();
		int avail_prev=0;
		int	avail=0,cnt=0;
		
		if(PMS.rz_debug>2) PMS.dbg("waitStreamReady : Begin , wait_max(msec)="+waitmax
			+", minbuf="+minbuf
			+", InputStream="+is);
		while (elapsed < waitmax) {	//regzamod
			if (is != null) {
				if(minbuf<0) {
					break;
				}
				try {
					avail=is.available();
				} catch (IOException e) {
				}
				if(avail>minbuf) {
					break;
				}
				else if(false && avail_prev>0 && avail_prev>=avail) {
					//buffer not growing --> stop waiting
					break;
				}
			}
			else {
				if(is_prev!=null && is==null) {
					break;
				}
			}
			is_prev=is;
			avail_prev=avail;
			try {
				Thread.sleep(waitintvl);
			} catch (InterruptedException e) {
			}
			elapsed +=waitintvl;
			cnt++;
		}
		if(PMS.rz_debug>2) PMS.dbg("waitStreamReady : End, InputStream="+is
			+", avail="+avail);
	}
}
