/*
 * 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.encoders;

import java.io.IOException;

//---- regzamod add start
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.util.List;
import java.util.ArrayList;
//---- regzamod add end

import javax.swing.JComponent;

import net.pms.configuration.PmsConfiguration;
import net.pms.dlna.DLNAMediaInfo;
import net.pms.dlna.DLNAResource;
import net.pms.dlna.WebStream;
import net.pms.dlna.WebVideoStream;
import net.pms.dlna.rz_ScriptExecutor;
import net.pms.formats.Format;
import net.pms.io.OutputParams;
import net.pms.io.PipeProcess;
import net.pms.io.ProcessWrapper;
import net.pms.io.ProcessWrapperImpl;
import net.pms.PMS;
import net.pms.util.ProcessUtil;

import org.slf4j.Logger;		//regzamod
import org.slf4j.LoggerFactory;	//regzamod

public class MEncoderWebVideo extends Player {
	private static final Logger logger = LoggerFactory.getLogger(MEncoderVideo.class);
	public static final String ID = "mencoderwebvideo";
	private final PmsConfiguration configuration;
	private static String[] prev_ret=new String[20];
	private static int ret_max=prev_ret.length;
	private static String prev_cvfile_in;
	private static int prev_rc;
	//private static Process p_minfo;  //for getMediaInfo process


	@Override
	public JComponent config() {
		return null;
	}

	@Override
	public String id() {
		return ID;
	}

	@Override
	public int purpose() {
		return VIDEO_WEBSTREAM_PLAYER;
	}

	@Override
	public boolean isTimeSeekable() {
		return false;
	}

	@Override
	public String mimeType() {
		return "video/mpeg";
	}

	protected String[] getDefaultArgs_ORG() {
		int nThreads = configuration.getMencoderMaxThreads();
		String acodec = configuration.isMencoderAc3Fixed() ? "ac3_fixed" : "ac3";
		return new String[]{
				"-msglevel", "all=2",
				"-quiet",
				"-prefer-ipv4",
				"-cache", "16384",
				"-oac", "lavc",
				"-of", "lavf",
				"-lavfopts", "format=dvd",
				"-ovc", "lavc",
				"-lavcopts", "vcodec=mpeg2video:vbitrate=4096:threads=" 
					+ nThreads + ":acodec=" + acodec + ":abitrate=128",
				"-vf", "harddup",
				"-ofps", "25"
			};
	}
	
	protected String[] getDefaultArgs() {	//regzamod, OK but poor quality & performance 
		int nThreads = configuration.getMencoderMaxThreads();
		String acodec = configuration.isMencoderAc3Fixed() ? "ac3_fixed" : "ac3";
		return new String[]{
				"-msglevel", "all=2",
				"-quiet",
				"-prefer-ipv4",
				"-cache", "16384",	//KByte (16384KB=16MB, max=1048576KB=1GB)),
				"-of", "lavf",
				"-lavfopts", "format=dvd",
				//"-lavfopts", "format=mpg",  //bad??
				"-ovc", "lavc",
				"-oac", "lavc",
				"-lavcopts", "vcodec=mpeg2video:vbitrate=4096:aspect=16/9"
					+ ":threads=" + nThreads 
					+ ":acodec=" + acodec + ":abitrate=128",
				//"-of", "mpeg",
				//"-mpegopts", "format=mpeg2:muxrate=500000:vbuf_size=1194:abuf_size=64",
				"-vf", "harddup",
				"-ofps", "25"
			};
	}
	//caution!! vbitrate= 4-16000(in kbit),16001-24000000(in bit)

	public MEncoderWebVideo(PmsConfiguration configuration) {
		this.configuration = configuration;
	}

	@Override
	public ProcessWrapper launchTranscode(
		String arg_cvfile_in,
		DLNAResource dlna,
		DLNAMediaInfo media,
		OutputParams params) throws IOException {
		
		//---- tuning params for start playing 
		//params.manageFastStart(); 
		params.manageStart(params.mediaRenderer.getRZ_WebStartWait()); 
		
		boolean onlyGetInfo=(params.mflags&OutputParams.F_GETINFO)!=0?true:false;
		
		if(PMS.rz_debug>1) {
			PMS.dbg("==== MencoderWeb.launchTranscode: Start" 
				+", onlyGetInfo="+onlyGetInfo
				+", dlna="+dlna.getName());
		}
		
		long tim1 = System.currentTimeMillis();
			
		//---- option params from pxf/pxm/script
		if(PMS.rz_is_rend_script_enabled && params.sess.scex!=null) {
			rz_ScriptExecutor.medAddOpts spec_addopts=new rz_ScriptExecutor.medAddOpts();
			spec_addopts.player=this.toString();
			//spec_addopts.trans_type=rz_ScriptExecutor.trans_type_int2string(dlna.getEncMode());
			//spec_addopts.trans_format=rz_ScriptExecutor.trans_format_int2string(trans_format);

			//execScripts(type,when,taget,dlna,..)
			// int type;	//=1:load,=2:exec
			// int when;	//=1:list,=2:play,  =3:list|play,  =4:oneshot
			// int target;	//=1:file,=2:folder,=3:file|folder,=4:other
			params.sess.scex.execScripts(2,2,1,dlna,dlna.getSrcPath(),params,spec_addopts);
		}

		//---- args for getRzWebMediaInfo
		String opt_str="";
		String [] uopts=null;
		DLNAResource pa=dlna.getParent();
		if(pa!=null) {
			uopts=pa.getUrlParams();
			if(uopts!=null) {
				for(int i=0;i<uopts.length;i++) {
					if(uopts[i]!=null && uopts[i].length()>0) {
						opt_str += uopts[i];
						opt_str += ",";
						if(PMS.rz_debug>1) {
							if(PMS.rz_debug>1) PMS.dbg("MencoderWeb: parent uopts["+i+"]="+uopts[i]);
						}
					}
				}
			}
		}
		uopts=dlna.getUrlParams();
		if(uopts!=null) {
			//[0]:name,[1]:url,[2]:thumb,[3]:opts,...
			for(int i=0;i<uopts.length;i++) {
				if(uopts[i]!=null && uopts[i].length()>0) {
					opt_str += uopts[i];
					opt_str += ",";
					if(PMS.rz_debug>1) {
						PMS.dbg("MencoderWeb: dlna uopts["+i+"]="+uopts[i]);
					}
				}
			}
		}
		
		int w=configuration.getRZ_web_resize_w();
		int h=configuration.getRZ_web_resize_h();
		if(params.mediaRenderer.getMaxVideoWidth()>0) {
			w=params.mediaRenderer.getMaxVideoWidth();
		}
		if(params.mediaRenderer.getMaxVideoHeight()>0) {
			h=params.mediaRenderer.getMaxVideoHeight();
		}
		
		opt_str += "qlev="+configuration.getRZ_web_quality();
		opt_str += ",resize="+(configuration.isRZ_web_force_resize()?1:0);
		opt_str += ",resize_w="+w;
		opt_str += ",resize_h="+h;
		opt_str += ",asp_hack="+params.mediaRenderer.getRZ_AspectHack();
		opt_str += ",sd_zoom="+params.mediaRenderer.getRZ_SDsizeZoom();
		opt_str += ",renderer="+params.mediaRenderer.toString();
			
		int remux=0;
		if(configuration.isRZ_web_remux() && params.mediaRenderer.isMuxH264MpegTS()) {
			remux=1;
		}
		if(dlna.getForcedTransType()==PMS.ENC_TRANSCODE) {
			remux=0;
		}
		else if(dlna.getForcedTransType()==PMS.ENC_REMUX) {
			remux=2;
		}
		opt_str += ",remux="+remux;	//remuxable or not
			
		int wfmt=params.mediaRenderer.getRZ_TransVFormatWeb();
		//PMS.dbg("MencoderWebVideo: PMS.rz_SysMetaTransFormatWeb="+PMS.rz_SysMetaTransFormatWeb);
		if(dlna.getForcedTransFormatWeb()>0) {  //forced
			wfmt=dlna.getForcedTransFormatWeb();
		}
		/*
		String out_suffix=".mpg";
		if(wfmt==PMS.TRS_M2TS) {
			out_suffix=".m2ts";
			opt_str += ",m2ts=1";	//format=m2ts
		} else {
			opt_str += ",m2ts=0";	//format=mpegps/mpegts
		}
		*/
		String out_suffix=".mpg";
		if(wfmt==PMS.TRS_M2TS) {
			out_suffix=".m2ts";
			opt_str += ",trans_format=m2ts";	//format=m2ts
		} 
		else if(wfmt==PMS.TRS_MPEGTS){
			out_suffix=".ts";
			opt_str += ",trans_format=mpegts";	//format=mpegts
		}
		else if(wfmt==PMS.TRS_MPEGPS){
			opt_str += ",trans_format=mpegps";	//format=mpegps
		}
		else {
			opt_str += ",trans_format=any";	//format=any:not forced
		}
		if(params.mediaRenderer.getRZ_RemuxStartSkip()!=0) {
			//timeseek+=params.mediaRenderer.getRZ_RemuxStartSkip();
			opt_str += ",remux_start_skip="+params.mediaRenderer.getRZ_RemuxStartSkip();
		}
			
		//PMS.dbg("MencoderWebVideo: PMS.rz_SysMetaWebOpt="+PMS.rz_SysMetaWebOpt);
		if(params.sess.rz_SysMetaWebOpt!=null && params.sess.rz_SysMetaWebOpt.length()>0) {		//from scripts
			opt_str += ","+params.sess.rz_SysMetaWebOpt;
		}
		
		if(PMS.rz_debug>1) {
			PMS.dbg("MencoderWebVideo: opt_str="+opt_str);
		}
		
		//---- create output pipe 
		PipeProcess pipe = new PipeProcess("mencoder" + System.currentTimeMillis()+out_suffix);
		params.input_pipes[0] = pipe;
		String arg_cvfile_out=pipe.getInputPipe();
		
		//--------------------------------------------------------------------
		// get Mediainfo
		//--------------------------------------------------------------------
		String prog_path=executable();	//defaults
		long time_mchg=0;
		long time_cash=((WebStream)dlna).param_cache_date;
			
		if(params.sess.scex!=null) {
			time_mchg=params.sess.scex.getRz_ScriptLastModified();  //params may be changed by script execution
		}
		
		int prog_rc=0;
		String[] ret=null;
		
		WebVideoStream wdlna=(WebVideoStream)dlna;
		
		//--- check if resolve already running & wait finish
		if(wdlna.resolve_running) {
			long max_wait=20000; //msec
			long elapsed=0;
			if(PMS.rz_debug>1) PMS.dbg("MencoderWebVideo: media parser running, wait for end");
			while(true) {
				try {
					Thread.sleep(1000);  //msec
				} catch (InterruptedException e) {
				}
				elapsed+=1000;
				if(!wdlna.resolve_running) {
					break;
				}
				else if(elapsed>max_wait) {
					if(PMS.rz_debug>1) PMS.dbg("MencoderWebVideo: wait time over, break");
					break;
				}
			}
			wdlna.resolve_running=false;
			if(PMS.rz_debug>1) PMS.dbg("MencoderWebVideo: wait end"
				+", elapsed(msec)="+elapsed);
		}
		
		//----------------------------------------------------------------------------------------------
		// Resolve media info, Start
		// must be synchronized(singular) within same dlna resolve procs for current and next(pre_parse) 
		//----------------------------------------------------------------------------------------------
		synchronized(dlna) {  
			if(!needGetMedinfo(dlna,time_mchg)) {
				//use previous succeeded cache
				if(PMS.rz_debug>1) PMS.dbg("MEncoderWebVideo: use param_cache");
				ret=((WebStream)dlna).param_cache;
				ret[3]=null;  //logmsg --> should clear to avoid redundant message
			}
			else {
				if(PMS.rz_debug>1) PMS.dbg("MEncoderWebVideo: no valid param_cache, exec getRzWebMediaInfo");
				
				wdlna.resolve_running=true;
				//---- exec getRzWebMediaInfo
				ret=getRzWebMediaInfo("MencoderWeb",arg_cvfile_in, arg_cvfile_out,params.timeseek,opt_str,params);	//regzamod
				try {
					prog_rc=Integer.parseInt(ret[5]);		// web_exit=value
				} catch (Exception e) {
					prog_rc=-1;
				}
				if(prog_rc==0) {
					wdlna.param_cache=ret;
					wdlna.param_cache_date=System.currentTimeMillis();
					wdlna.setResolved(true);
				}
				else {
					wdlna.param_cache=null;
					//dlna.setResolved(false);  //don't reset: to avoid exec simultanious resolve for same bad sites
				}
				wdlna.resolve_running=false;
			}
		}
		//----------------------------------------------------------------------------------------------
		// Now, Resolve media info Ended
		//----------------------------------------------------------------------------------------------
			
		if(PMS.rz_debug>1) {
			PMS.dbg("MencoderWebVideo: getMediainfo"
				+", ret="+(ret==null?"Failed":"Succeeded")
				+", prog_rc="+prog_rc
				+", dlna="+dlna.getName());
		}
		if(ret==null) {
			return null;
		}
		if(onlyGetInfo==true) {
			return null;
		}
		//--------------------------------------------------------------------
		// Now, get Mediainfo End
		//--------------------------------------------------------------------
		//--------------------------------------------------------------------
		// Kick player
		//--------------------------------------------------------------------
		//---- save return values
		String cvfile_in=ret[0];	// valid URL of the contents (optional)
		String cvparams=ret[1];		// transcoding params (optional)
		String cvfile_out=ret[2];	// another output (optional)
		String logmsg=ret[3];		// log message (optional)
		String prog_ex=ret[4];		// another transcoder (optional)
		String cvfile_in2=ret[6];	// valid URL of the contents (optional)
		
		//mediainfos
		int pos=7;
		String med_fmt=ret[pos++];		// width
		String med_w=ret[pos++];		// width
		String med_h=ret[pos++];		// height
		String med_asp=ret[pos++];		// aspect
		String med_fps=ret[pos++];		// fps
		String med_vc=ret[pos++];		// video codec
		String med_ac=ret[pos++];		// audio codec
		String med_remux=ret[pos++];	// remux condition
		if(PMS.rz_debug>1) {
			for(int i=0;i<pos;i++) {
				PMS.dbg2("MencoderWebVideo mediainfos");
				PMS.dbg2("med["+i+"]="+ret[i]);
			}
		}
		//---- check logmsg
		if(logmsg!=null && logmsg.length()>0) {
			logger.info(logmsg);
		}
		
		if(PMS.rz_debug>1) PMS.dbg("MencoderWebVideo: getRzWebMediaInfo rc="+prog_rc);
		if(prog_rc!=0) {
			if(prog_rc==123) {
				//you got kick_params for disp error_msg on renderer_screen
			}
			else {
				long tim2 = System.currentTimeMillis();

				//PMS.dbg("MencoderWebVideo: getRzWebMediaInfo failed rc="+prog_rc);
				logger.warn("MencoderWebVideo: getRzWebMediaInfo failed rc="+prog_rc+", stream="+dlna.getName());
				if(tim2-tim1<5000) {
					if(pipe!=null) pipe.close(); //danger, may cause some Terminal(like REGZA) freeze
					//try {
					//	Thread.sleep(50);
					//} catch (InterruptedException e) {
					//}
					return null;
				}
				prog_path=configuration.getFfmpegPath();  //force fail
			}
		}
				
		//---- check return values of getRzWebMediaInfo ----------------------
		if(prog_rc==0) {
			//---- check mediainfo
			//should use setMedia ??
			dlna.wmed_valid=true;
			dlna.wmed_fmt=med_fmt;
			dlna.wmed_w=med_w;
			dlna.wmed_h=med_h;
			dlna.wmed_asp=med_asp;
			dlna.wmed_fps=med_fps;
			dlna.wmed_vc=med_vc;
			dlna.wmed_ac=med_ac;
		}
		if(med_remux!=null && med_remux.equals("1")) {
			dlna.setEncMode(PMS.ENC_REMUX);
		}
		else {
			dlna.setEncMode(PMS.ENC_TRANSCODE);
		}
		
		//---- check cvfile_in
		if(cvfile_in==null) {
			logger.warn("URL analisys seems failed, original URL as is");
			cvfile_in=arg_cvfile_in;
		}
		else if(cvfile_in.startsWith("ERROR:")) {
			logger.warn("URL analisys error="+cvfile_in+" --> use original URL as is");
			cvfile_in=arg_cvfile_in;
		}
		//---- check cvparams
		if(cvparams==null || cvparams.length()==0) {
			//set defaults
			cvparams="-ss $time_start $input -quiet -prefer-ipv4 -cache 16384 -of mpeg -oac lavc -ovc lavc -mpegopts format=mpeg2:muxrate=500000:vbuf_size=1194:abuf_size=64 -lavcopts aspect=16/9:vcodec=mpeg2video:acodec=ac3:abitrate=128:keyint=5:vqscale=1:vqmin=2 -ofps 25 -vf scale=684:456,expand=720:480 -mc 0.1 -af lavcresample=48000 -srate 48000 -o $output";
		}
		
		//---- check cvfile_out
		if(cvfile_out!=null) {
			logger.warn("MencoderWeb: ==> TEST Mode, outoput to "+cvfile_out);
		}
		else {
			cvfile_out=pipe.getInputPipe();
		}
		
		//---- check logmsg
		/*
		if(logmsg!=null && logmsg.length()>0) {
			logger.info(logmsg);
		}
		*/
		
		//---- check prog_ex
		if(prog_ex!=null) {
			// exec another transcoder, path must be prog_ex
			if(PMS.rz_debug>1) PMS.dbg("RzWebMediaInfo : exec web_prog_ex="+prog_ex+", Good luck!");
			prog_path=prog_ex;
		}
		
		
		if(PMS.rz_debug>1) {	//regzamod
			PMS.dbg("MencoderWeb: minBufferSize="+params.minBufferSize);
			PMS.dbg("MencoderWeb: arg_cvfile_in="+arg_cvfile_in);
			PMS.dbg("MencoderWeb: cvfile_in="+cvfile_in);	
			PMS.dbg("MencoderWeb: cvparams="+cvparams);	
			PMS.dbg("MencoderWeb: cvfile_out="+cvfile_out);	
			PMS.dbg("MencoderWeb: prog_ex="+prog_ex);	
			PMS.dbg("MencoderWeb: prog_rc="+prog_rc);	
		}

		//---- split cvparams by spaces
		String[] margs=cvparams.split("\\s");
		//String cmdArray[] = new String[margs.length + 1];
		String cmdArray[] = new String[margs.length + 1];
		
		//---- setup exec paramaters for transcoder
		cmdArray[0] = prog_path;
		/*
		cmdArray[1] = "-sb";	//test ByteSeek ---> don't work on net seek
		cmdArray[1] = "-ss";
		cmdArray[2] = "0";
		cmdArray[3] = "";
		cmdArray[4] = "";
		if(params.timeseek>0) {
			PMS.dbg("params.timeseek="+params.timeseek);
			//cmdArray[2] = ""+(int)(params.timeseek*500000);
			cmdArray[2] = ""+params.timeseek;
		}
		if(params.timeend>0) {
			PMS.dbg("params.timeend="+params.timeend);
			cmdArray[2] = ""+params.timeend;
		}
		*/
		
		for (int i = 0; i < margs.length; i++) {
			if(margs[i]==null) {
				cmdArray[i + 1] = "";	//fail_safe
			}
			else {
				cmdArray[i + 1] = margs[i];
			}
		}
		//cmdArray[cmdArray.length - 2] = "-o";
		//cmdArray[cmdArray.length - 1] = cvfile_out;	//regzamod2 for test
		
		//substitute spacial keywards to real value
		
		double timeseek=params.timeseek;
		if(timeseek<3.0){
			//start flattering on slow contents cause little timeskip : should be ignored
			timeseek=0.0;
		}
		//if(dlna.getEncMode()==PMS.ENC_REMUX) {
		//	timeseek+=params.mediaRenderer.getRZ_RemuxStartSkip();
		//}
		
		for (int i = 1; i < cmdArray.length; i++) {
			if(cmdArray[i].equals("$input")) {
				cmdArray[i]=cvfile_in;
			}
			if(cmdArray[i].equals("$input2")) {
				cmdArray[i]=cvfile_in2;
			}
			else if(cmdArray[i].equals("$output")) {
				cmdArray[i]=cvfile_out;
			}
			else if(cmdArray[i].equals("$time_start")) {
				//cmdArray[i]=""+(params.timeseek>0?params.timeseek:0);
				cmdArray[i]=""+timeseek;
			}
			else if(cmdArray[i].equals("$time_end")) {
				cmdArray[i]=""+(params.timeend>0?params.timeend:0);
			}
			else if(cmdArray[i].equals("$clippath_a")) {  //audio clippath
				//String clippath=params.sess.getRoot().audio_clip_path;
				String clippath=params.sess.getRoot().getCurrentClipPath_a();
				if(clippath==null) {
					clippath="./bgm";  //dummy
				}
				else {
					clippath=ProcessUtil.getShortFileNameIfWideChars(clippath);
				}
				cmdArray[i]="\""+clippath+"\"";
			}
		}

		//---- create output pipe
		//PipeProcess pipe = new PipeProcess("mencoder" + System.currentTimeMillis());
		//params.input_pipes[0] = pipe;
		
		//---- setup pipe process
		ProcessWrapper mkfifo_process = pipe.getPipeProcess();

		//---- setup transcoder process
		cmdArray = finalizeTranscoderArgs(
			this,
			cvfile_in,
			dlna,
			media,
			params,
			cmdArray);

		ProcessWrapperImpl pw = new ProcessWrapperImpl(cmdArray, params);
		pw.attachProcess(mkfifo_process);
		
		//---- run pipe process
		mkfifo_process.runInNewThread();
		try {
			Thread.sleep(50);
		} catch (Exception e) {
		}
		pipe.deleteLater();

		//---- run transcoder process
		pw.runInNewThread();
		try {
			Thread.sleep(50);
		} catch (InterruptedException e) {
		}
		
		//---- kick resolve next obj, for quick start next
		int min_intvl=configuration.getRZ_rz_web_resolve_intvl();
		if(min_intvl>0) {
			long elapsed=System.currentTimeMillis() - dlna.resStartRealTime;
			DLNAResource ch,tg=null;
			int isv= -1;
				
			if(PMS.rz_debug>1) PMS.dbg("MencoderWebVideo: kick next resolve JUDGE START, Interval(msec)="+elapsed);
			
			if(elapsed>min_intvl && pa!=null) {
				int i=0,kick=0,cnt=0,cnt2=0;
				int size=pa.getChildren().size();
				int size2=size*2;
				while(true) {
					ch=pa.getChildren().get(i);
					if(ch!=null && ch.sotype==DLNAResource.SO_VIDEO_STREAM) {
						if(tg==null && needGetMedinfo(ch,time_mchg)) {
							tg=ch;  //valid top: for nobody found after me
							isv=i;
						}
						if(ch==dlna) {  //me
							kick=1;
						}
						else if(kick==1) {  //after me
							if(needGetMedinfo(ch,time_mchg)) {  //not resolved or too old
								tg=ch;
								isv=i;
								break;
							}
						}
					}
					if(kick==1) cnt++;
					cnt2++;
					if(cnt>size || cnt2>size2) break;
					i++;
					if(i>=size) i=0;
				}
			}
			if(PMS.rz_debug>1) {
				PMS.dbg("MencoderWebVideo: kick next resolve JUDGE END, No.="+isv+", Target="+(tg==null?"null":tg.getName()));
			}

			if(tg!=null) {
				//PMS.dbg("MencoderWebVideo: kick next resolve target found ="+tg.getName());
				((WebVideoStream)tg).resolve_p(pw);
				//PMS.dbg("MencoderWebVideo: kick next resolve kick end");
			}
			else {
				//PMS.dbg("MencoderWebVideo: kick next resolve target not found");
			}
		}
		return pw;
	}
	
	private boolean needGetMedinfo(DLNAResource d, long time_mchg) {
		if(d instanceof WebStream) {
			if(!d.resolved 
				|| ((WebStream)d).param_cache==null
				|| ((WebStream)d).param_cache_date<time_mchg) {
				((WebStream)d).param_cache=null; //assert not cached
				return true;
			}
		}
		return false;
	}

	@Override
	public boolean avisynth() {
		return false;
	}

	@Override
	public String name() {
		return "MEncoder Web";
	}

	@Override
	public String[] args() {
		return getDefaultArgs();
	}

	@Override
	public String executable() {
		return configuration.getMencoderPath();
	}

	@Override
	public int type() {
		return Format.VIDEO;
	}
	
	//---- regzamod add start
	public String[] getRzWebMediaInfo(String transcoder, String cvfile_in, String cvfile_out,
		double timeseek, String opt_str, OutputParams params) {
		
		// single String param can't be return value
		//String path=configuration.getRzWebMediaInfo_Path();  	//get platform specific path using configurationProgramPath.java
		String path=configuration.getRZ_web_mediainfo_path();	//easy way 
		
		String[] ret=new String[ret_max];
		
		if(false && prev_cvfile_in!=null && prev_cvfile_in.equals(cvfile_in) && prev_rc==0) {
			if(PMS.rz_debug>1) PMS.dbg("getRzWebMediaInfo: cvfile_in same as prev, let's return cache");
			return prev_ret;
		}
		//prev_rc= -1;
		//prev_cvfile_in=cvfile_in;
			
		if(PMS.rz_debug>1) {
			PMS.dbg("getRzWebMediaInfo: cvfile_in="+cvfile_in);
			PMS.dbg("getRzWebMediaInfo: getRzWebMediaInfo path="+path);
		}
		//---- setup params for the process
		List<String> list = new ArrayList<String>();
		//list.add("cmd");	//cmd can't handle spaces in path
		//list.add("/c");
		//list.add("\""+path+"\"");
			
		String[] paths=path.split(",");
		if(paths.length>1) {
			for (int i=0;i<paths.length;i++) {
				if(i==0 && paths[i].trim().equals("$python_path$")) {
					paths[i]=configuration.getRZ_python_path();
					//PMS.dbg("MencoderWebVideo: got python_path="+paths[i]);
				}
				if(paths[i].length()>0) list.add(paths[i].trim());
			}
		}
		else {
			list.add(path);
		}
		
		try {
			list.add("\""+configuration.getTempFolder().getAbsolutePath()+"\"");
		} catch (IOException e)  {
			logger.error("getRzWebMediaInfo: IOException", e);
		}
		list.add(""+params.sess.id);
			
		list.add(transcoder);
		list.add("\""+cvfile_in+"\"");
		list.add("\""+cvfile_out+"\"");
		list.add(""+timeseek);
		if(opt_str!=null) {
			list.add("\""+opt_str+"\"");
		}
		if(PMS.rz_debug>1) PMS.dbg("MencoderWebVideo: get_url exec arg="+list);

		//---- kick process
		ProcessBuilder pb = new ProcessBuilder(list);
		//pb.redirectErrorStream(true);	// redirect stderror to stdout, otherwise you need onother read proc for stderr
		
		//private static Process p;
		Process p_minfo=null;
		try {
			p_minfo = pb.start();
			if(p_minfo==null) {
				logger.error("MencoderWebVideo.getRzWebMediaInfo: getinfo proc kick failed");
				return null;
			}
		
			//---- read stderr of proc
			Runnable read_stderr = new Runnable() {
				Process p_minfo_in;
				public Runnable setParam(Process p_minfo) {
					//To give "non static value in main-thread" to child-thread:
					//memory "non static value in main-thread" to "private value in child-thread"
					p_minfo_in=p_minfo;
					return this;
				}
				@Override
				public void run() {
					InputStream ie = p_minfo_in.getErrorStream();
					BufferedReader br2 = new BufferedReader(new InputStreamReader(ie));
					String line;
					try {
						while(true) {
							line= br2.readLine();
							if(line == null) break;
							if(PMS.rz_debug>0) PMS.dbg(line);
						}
						br2.close();
					} catch (Exception e) {
						logger.error("getRzWebMediaInfo: readLine block: Exception!!");
					} 
				}
			}.setParam(p_minfo);
			new Thread(read_stderr).start();
		} catch (Exception e) {
			logger.error("getRzWebMediaInfo: pb.start(): Exception!!",e);
		}

		//---- read stdout proc
		InputStream is = p_minfo.getInputStream();
		BufferedReader br = new BufferedReader(new InputStreamReader(is));
		
		//---- read output(stdout & stderr) of the process
		String line=null;
		
		//must be revised with more smart way?
		ret[0]=cvfile_in;	//web_cvfile_in
		
		try {
			for (int i=0;;i++) {
				line = br.readLine();
				if(line == null) break;
				if(PMS.rz_debug>1) PMS.dbg("MEncoderWebVideo: web_medinfo["+i+"] = "+line);

				if(line.startsWith("http://")) {	// contents url 
					ret[0]=line;
				}
				else if(line.startsWith("web_cvfile_in=")) {	// contents url 
					String s=new String("web_cvfile_in=");
					ret[0]=line.substring(s.length()).trim();
				}
				else if(line.startsWith("web_cvparams")) {	
					String s=new String("web_cvparams=");
					ret[1]=ret[1]==null?line.substring(s.length()):ret[1]+" "+line.substring(s.length()).trim();
				}
				else if(line.startsWith("web_cvfile_out")) {
					String s=new String("web_cvfile_out=");
					ret[2]=line.substring(s.length()).trim();
				}
				else if(line.startsWith("web_logmsg")) {
					String s=new String("web_logmsg=");
					ret[3]=ret[3]==null?line.substring(s.length()):ret[3]+"\n"+line.substring(s.length()).trim();
				}
				else if(line.startsWith("web_prog_ex")) {		//exec another prog, insterd of MencoderWeb
					String s=new String("web_prog_ex=");
					ret[4]=line.substring(s.length()).trim();
				}
				else if(line.startsWith("web_exit")) {		//exec another prog, insterd of MencoderWeb
					String s=new String("web_exit=");
					ret[5]=line.substring(s.length()).trim();
				}
				else if(line.startsWith("web_cvfile_in2=")) {	// contents url 
					String s=new String("web_cvfile_in2=");
					ret[6]=line.substring(s.length()).trim();
				}
				else if(line.startsWith("web_medinfo")) {		//media infos
					//web_medinfo=fmt=%s,w=%d,h=%d,asp=%f,fps=%f,vc=%s,remux=%d
					String s=new String("web_medinfo=");
					line=line.substring(s.length()).trim();
					String med[]=line.split(",");
					int	mtop=7;
					for(int j=0;j<med.length;j++) {
						if(med[j].startsWith("fmt=")) 		 ret[mtop+0] =med[j].substring(4).trim();
						else if(med[j].startsWith("w=")) 	 ret[mtop+1] =med[j].substring(2).trim();
						else if(med[j].startsWith("h=")) 	 ret[mtop+2] =med[j].substring(2).trim();
						else if(med[j].startsWith("asp=")) 	 ret[mtop+3] =med[j].substring(4).trim();
						else if(med[j].startsWith("fps=")) 	 ret[mtop+4] =med[j].substring(4).trim();
						else if(med[j].startsWith("vc=")) 	 ret[mtop+5] =med[j].substring(3).trim();
						else if(med[j].startsWith("ac=")) 	 ret[mtop+6] =med[j].substring(3).trim();
						else if(med[j].startsWith("remux=")) ret[mtop+7] =med[j].substring(6).trim();
					}
				}
				else {
					if(PMS.rz_debug>1) PMS.dbg(line);
				}
			}
			br.close();
		} catch (Exception e) {
			logger.error("getRzWebMediaInfo: readLine block: Exception!!");
		} 

		if(PMS.rz_debug>1) {
			PMS.dbg("getRzWebMediaInfo: web_cvfile_in="+ret[0]);
			PMS.dbg("getRzWebMediaInfo: web_cvparams="+ret[1]);
			PMS.dbg("getRzWebMediaInfo: web_cvfile_out="+ret[2]);
			PMS.dbg("getRzWebMediaInfo: web_logmsg="+ret[3]);
			PMS.dbg("getRzWebMediaInfo: web_prog_ex="+ret[4]);
			PMS.dbg("getRzWebMediaInfo: web_exit="+ret[5]);
			PMS.dbg("getRzWebMediaInfo: web_cvfile_in2="+ret[6]);
		}
		
		//---- ending
		//p.waitFor();	// needn't wait
		//PMS.dbg("getBodyURL.bat exit rc="+ p.exitValue());
		//if(!found || p.exitValue()!=0) {
		/*
		for(int i=0;i<ret_max;i++) prev_ret[i]=ret[i];
		if(ret[5]!=null) {
			try {
				prev_rc=Integer.parseInt(ret[5]);
			} catch (Exception e) {
				prev_rc=-1;
			}
		} else {
			prev_rc=-1;
		}
		*/
		return ret;
	}
	//---- regzamod add end
}
