001    package railo.runtime.tag;
002    
003    
004    import java.io.IOException;
005    import java.io.InputStream;
006    import java.io.OutputStream;
007    import java.lang.reflect.Method;
008    import java.util.Iterator;
009    import java.util.Random;
010    
011    import railo.commons.io.res.Resource;
012    import railo.commons.io.res.util.ResourceUtil;
013    import railo.commons.lang.ClassException;
014    import railo.commons.lang.StringUtil;
015    import railo.loader.util.Util;
016    import railo.runtime.PageContext;
017    import railo.runtime.exp.ApplicationException;
018    import railo.runtime.exp.PageException;
019    import railo.runtime.ext.tag.TagSupport;
020    import railo.runtime.op.Caster;
021    import railo.runtime.type.Collection.Key;
022    import railo.runtime.type.KeyImpl;
023    import railo.runtime.type.Struct;
024    import railo.runtime.type.StructImpl;
025    import railo.runtime.type.util.KeyConstants;
026    import railo.runtime.video.ProfileCollection;
027    import railo.runtime.video.VideoExecuter;
028    import railo.runtime.video.VideoInfo;
029    import railo.runtime.video.VideoInput;
030    import railo.runtime.video.VideoInputImpl;
031    import railo.runtime.video.VideoOutput;
032    import railo.runtime.video.VideoOutputImpl;
033    import railo.runtime.video.VideoProfile;
034    import railo.runtime.video.VideoProfileImpl;
035    import railo.runtime.video.VideoUtilImpl;
036    
037    /**
038     * implementation of the tag Compiler
039     */
040    public class Video extends TagSupport {
041    
042            private static final int ACTION_CONVERT=0;
043            private static final int ACTION_INFO=1;
044            private static final int ACTION_CONCAT=2;
045            private static final int ACTION_CUT_IMAGE=4;
046            private static final int ACTION_INSTALL=8;
047            private static final int ACTION_UNINSTALL=16;
048            
049            public static final int NAMECONFLICT_UNDEFINED=0;
050            public static final int NAMECONFLICT_ERROR=1;
051            public static final int NAMECONFLICT_SKIP=2;
052            public static final int NAMECONFLICT_OVERWRITE=3;
053            public static final int NAMECONFLICT_MAKEUNIQUE=4;
054    
055            public static final int EXECUTION_QUALITY=0;
056            public static final int EXECUTION_PERFORMANCE=1;
057            private static final Key SOURCE = KeyConstants._source;
058            private static final Key SOURCE1 = KeyImpl.intern("source1");
059            private static final Key SOURCE2 = KeyImpl.intern("source2");
060            private static final Key AUDIO = KeyImpl.intern("audio");
061            private static final Key VIDEO = KeyImpl.intern("video");
062            
063            private static VideoUtilImpl util=VideoUtilImpl.getInstance();
064            
065            
066            private String result="cfvideo";
067            private int action;
068            private String strAction;
069            private VideoInput source;
070            private VideoInput source1;
071            private VideoInput source2;
072            private VideoOutput destination;
073            private int nameconflict=NAMECONFLICT_UNDEFINED;
074            private String name;
075            private int width=-1;
076            private int height=-1;
077            private String strWidth;
078            private String strHeight;
079            private Struct data;
080            
081    
082            private long audiosamplerate=0;
083            private long audioBitrate=0;
084            private double startTime=0;
085            private long startFrame=0;
086            private double maxTime=0;
087            private long maxFrame=0;
088            
089            private VideoProfile profile=null;
090            
091            private int aspectRatio;
092            private int framerate;
093            private long videoBitrate;
094            private long videoBitrateMin;
095            private long videoBitrateMax;
096            private long videoBitrateTolerance;
097            private String debug=null;
098            
099            private String author;
100            private String title;
101            private String comment;
102            private String copyright;
103            private String videoCodec;
104            private String audioCodec;
105            private long buffersize;
106            private int execution=EXECUTION_PERFORMANCE;
107            private static ProfileCollection _profileCollection;
108    
109            private static Method toStruct;
110            private static Boolean hasToStruct;
111            
112            public Video(){
113                    
114            }
115            
116            /**
117             * @see javax.servlet.jsp.tagext.Tag#release()
118             */
119            public void release() {
120                    super.release();
121                    result="cfvideo";
122                    execution=EXECUTION_PERFORMANCE;
123                    source=null;
124                    source1=null;
125                    source2=null;
126                    destination=null;
127                    nameconflict=NAMECONFLICT_UNDEFINED;
128                    name=null;
129                    width=-1;
130                    height=-1;
131                    profile=null;
132                    audioBitrate=0;
133                    videoCodec=null;
134                    audioCodec=null;
135                    
136                    audiosamplerate=0;
137                    aspectRatio=0;
138                    framerate=0;
139                    videoBitrate=0;
140                    videoBitrateMin=0;
141                    videoBitrateMax=0;
142                    videoBitrateTolerance=0;
143                    buffersize=0;
144                    author=null;
145                    title=null;
146                    comment=null;
147                    copyright=null;
148                    debug=null;
149    
150                    maxTime=0;
151                    maxFrame=0;
152                    startTime=0;
153                    startFrame=0;
154                    strWidth=null;
155                    strHeight=null;
156                    data=null;
157            }
158            
159    
160    
161            /**
162             * @param action the action to set
163             * @throws PageException 
164             */
165            public void setAction(String action) throws PageException {
166                    strAction=action;
167                    action=action.toLowerCase().trim();
168            if(action.equals("concat"))                     this.action=ACTION_CONCAT; 
169            else if(action.equals("merge"))                 this.action=ACTION_CONCAT; 
170            else if(action.equals("convert"))       this.action=ACTION_CONVERT;
171            //else if(action.equals("cut"))         this.action=ACTION_CUT;
172            else if(action.equals("cut image"))     this.action=ACTION_CUT_IMAGE;
173            else if(action.equals("cutimage"))      this.action=ACTION_CUT_IMAGE;
174            else if(action.equals("cut_image"))     this.action=ACTION_CUT_IMAGE;
175            else if(action.equals("cut-image"))     this.action=ACTION_CUT_IMAGE;
176            else if(action.equals("info"))          this.action=ACTION_INFO;
177            
178            else if(action.equals("install"))               this.action=ACTION_INSTALL;
179            else if(action.equals("uninstall"))             this.action=ACTION_UNINSTALL;
180            
181            else throw doThrow("invalid value for attribute action for tag video ["+action+"], " +
182                            "valid actions are [concat, convert, cutImage, info]");
183                    
184            }
185    
186            /**
187             * @param source the source to set
188             * @throws PageException 
189             */
190            public void setSource(String source) throws PageException {
191                    this.source = new VideoInputImpl(ResourceUtil.toResourceExisting(pageContext, source));
192            }
193            
194            public void setData(Struct data){
195                    this.data=data;
196            }
197    
198            /**
199             * @param destination the destination to set
200             * @throws PageException 
201             */
202            public void setDestination(String destination) {
203                    this.destination = new VideoOutputImpl(ResourceUtil.toResourceNotExisting(pageContext, destination));
204            }
205    
206            /** set the value nameconflict
207            *  Action to take if filename is the same as that of a file in the directory.
208            * @param nameconflict value to set
209             * @throws ApplicationException 
210            **/
211            public void setNameconflict(String nameconflict) throws PageException   {
212                    nameconflict=nameconflict.toLowerCase().trim();
213                    if("error".equals(nameconflict))                        this.nameconflict=NAMECONFLICT_ERROR;
214                    else if("skip".equals(nameconflict))            this.nameconflict=NAMECONFLICT_SKIP;
215                    else if("overwrite".equals(nameconflict))       this.nameconflict=NAMECONFLICT_OVERWRITE;
216                    else if("makeunique".equals(nameconflict))      this.nameconflict=NAMECONFLICT_MAKEUNIQUE;
217                    else throw doThrow("invalid value for attribute nameconflict ["+nameconflict+"]",
218                                    "valid values are [error,skip,overwrite,makeunique]");
219            }
220    
221            public void setProfile(String strProfile) throws PageException {
222                    VideoProfile p = getProfile(strProfile);
223                    if(p!=null)profile=p.duplicate();
224                    else throw doThrow("invalid profile defintion ["+strProfile+"], valid profiles are ["+getProfileKeyList()+"]");
225            }
226    
227            public void setExecution(String execution) throws PageException {
228    
229                    execution=execution.toLowerCase().trim();
230                    if("quality".equals(execution))                         this.execution=EXECUTION_QUALITY;
231                    else if("q".equals(execution))                          this.execution=EXECUTION_QUALITY;
232                    else if("performance".equals(execution))        this.execution=EXECUTION_PERFORMANCE;
233                    else if("p".equals(execution))                          this.execution=EXECUTION_PERFORMANCE;
234                    else throw doThrow("invalid value for attribute execution ["+execution+"]",
235                                    "valid values are [quality,performance]");
236            }
237    
238            public void setQuality(String strQuality) throws PageException {
239                    setProfile(strQuality);
240            }
241                    
242            /**
243             * @see javax.servlet.jsp.tagext.Tag#doStartTag()
244             */
245            public int doStartTag() throws PageException    {
246                    
247                    try {
248                            if(action==ACTION_CONVERT) doActionConvert();
249                            else if(action==ACTION_CONCAT) doActionConcat();
250                            else if(action==ACTION_CUT_IMAGE) doActionCutImage();
251                            else if(action==ACTION_INFO) doActionInfo();
252                            else if(action==ACTION_INSTALL) doActionInstall();
253                            else if(action==ACTION_UNINSTALL) doActionUninstall();
254                    }
255                    catch(Throwable t) {
256                            throw Caster.toPageException(t);
257                    }
258                    return SKIP_BODY;
259            }
260    
261    
262    
263            private void doActionInstall() throws ClassException, IOException {
264                    if(data==null)data=new StructImpl();
265                    getVideoExecuter().install(pageContext.getConfig(),data);  
266            }
267            private void doActionUninstall() throws ClassException, IOException {
268                    getVideoExecuter().uninstall(pageContext.getConfig());  
269            }
270    
271            public Struct doActionInfo() throws PageException, IOException {
272                    return doActionInfo(source);
273                    
274            }
275            
276            private Struct doActionInfo(VideoInput source) throws PageException, IOException {
277    
278                    // precheck settings
279                    checkFile(source,"source", true, true, false);
280                    
281                    //VideoConfig vc = getConfig();
282                    debug(source);
283                    Struct info = toStruct(getVideoExecuter().info(pageContext.getConfig(), source));
284                    pageContext.setVariable(result,info);
285                    return info;
286                    
287            }
288    
289            private VideoExecuter getVideoExecuter() throws ClassException {
290                    return VideoUtilImpl.createVideoExecuter(pageContext.getConfig());
291            }
292    
293    
294    
295            private VideoInfo getInfo(VideoInput source) throws PageException, IOException {
296    
297                    // precheck settings
298                    checkFile(source,"source", true, true, false);
299                    
300                    // execute
301                    return getVideoExecuter().info(pageContext.getConfig(),source);
302                    //print.out(raw);
303    
304                    // write cfvideo                
305                    //return new Info(raw,true,startTime,maxTime);  
306            }
307    
308    
309            private void doActionCutImage() throws PageException, IOException {
310    
311                    // precheck settings
312                    checkFile(source,"source", true, true, false);
313                    if(!checkDestination(source,destination,name,nameconflict)) return;
314                    
315                    // input
316                    if(profile==null)profile=new VideoProfileImpl();
317                    
318                    // settings
319                    settings(destination,profile);
320                    
321                    destination.setMaxFrames(1);
322                    destination.setFormat("image2");
323                    // execute
324                    Struct info=toStruct(getVideoExecuter().convert(pageContext.getConfig(),new VideoInput[]{source},destination,profile));
325                    
326                    // write cfvideo
327                    debug(source);
328                    pageContext.setVariable(result,info);
329                    
330                    //setResult(raw,false);
331                    
332            }
333    
334    
335    
336            private void doActionConcat() throws PageException, IOException {
337                    // precheck settings
338                    checkFile(source1,"source1", true, true, false);
339                    checkFile(source2,"source2", true, true, false);
340                    if(!checkDestination(source1,destination,name,nameconflict)) return;
341    
342                    Pair s1 =toMpeg(source1);
343                    Pair s2 =toMpeg(source2);
344                    source = new VideoInputImpl(pageContext.getConfig().getTempDirectory().getRealResource("tmp-"+new Random().nextInt()+".mpg"));
345                    try {
346                            merge(s1.res,s2.res,source.getResource());
347                            Struct sct = doActionConvert();
348                            sct.setEL(SOURCE1, s1.sct);
349                            sct.setEL(SOURCE2, s2.sct);
350                            sct.removeEL(SOURCE);
351                            
352                    }
353                    finally {
354                            source.getResource().delete();
355                            if(!s1.res.equals(source1.getResource()))s1.res.delete();
356                            if(!s2.res.equals(source2.getResource()))s2.res.delete();
357                    }
358                    
359            }
360    
361    
362    
363            private void merge(Resource in1, Resource in2, Resource out) throws IOException {
364                    InputStream is1=null;
365                    InputStream is2=null;
366                    OutputStream os=null;
367                    try {
368                            is1=in1.getInputStream();
369                            is2=in2.getInputStream();
370                            os=out.getOutputStream();
371                    }
372                    catch(IOException ioe) {
373                            Util.closeEL(is1);
374                            Util.closeEL(is2);
375                            Util.closeEL(os);
376                            throw ioe;
377                    }
378    
379                    try {
380                            copy(is1, os);
381                            copy(is2, os);
382                    } 
383                    finally {
384                            Util.closeEL(is1);
385                            Util.closeEL(is2);
386                            Util.closeEL(os);
387                    }
388            }
389    
390    
391            public final static void copy(InputStream in, OutputStream out) throws IOException {
392            byte[] buffer = new byte[0xffff];
393            int len;
394            while((len = in.read(buffer)) !=-1)
395              out.write(buffer, 0, len);
396        }
397        
398    
399    
400    
401            private Pair toMpeg(VideoInput vi) throws PageException, IOException {
402                    VideoInfo info = getInfo(vi);
403                    //Struct sct = _doActionInfo(vi);
404                    
405                    
406                    if("mpeg1video".equals(info.getVideoCodec()))
407                            return new Pair(vi.getResource(),toStruct(info));
408                    
409                    VideoOutput tmp = new VideoOutputImpl(pageContext.getConfig().getTempDirectory().getRealResource("tmp-"+new Random().nextInt()+".mpg"));
410                    try {
411                            doActionConvert(vi,tmp,null,null,NAMECONFLICT_ERROR);
412                            return new Pair(tmp.getResource(),toStruct(info));
413                    }
414                    catch(PageException pe) {
415                            tmp.getResource().delete();
416                            throw pe;
417                    }
418                    catch(IOException ioe) {
419                            tmp.getResource().delete();
420                            throw ioe;
421                    }
422                    
423            }
424    
425    
426    
427            private Struct doActionConvert() throws PageException, IOException {
428                    return doActionConvert(source,destination,profile,name,nameconflict);
429            }
430            private Struct doActionConvert(VideoInput source,VideoOutput destination, VideoProfile quality,String name, int nameconflict) throws PageException, IOException {
431                                    
432                    // precheck settings
433                    checkFile(source,"source", true, true, false);
434                    if(!checkDestination(source,destination,name,nameconflict)) return new StructImpl();
435                    
436                    // input
437                    if(quality==null)quality=new VideoProfileImpl();
438                    
439                    // settings
440                    settings(destination,quality);
441                    
442                    // execute
443                    Struct info = toStruct(getVideoExecuter().convert(pageContext.getConfig(),new VideoInput[]{source},destination,quality));
444                    
445                    // write cfvideo
446                    debug(source);
447                    
448                    pageContext.setVariable(result,info);
449                    return info;
450                    //return setResult(raw,false);
451            }    
452            
453    
454            private void debug(VideoInput vi) throws PageException {
455                    if(!StringUtil.isEmpty(debug))pageContext.setVariable(debug, vi.getCommandAsString());
456            }
457    
458            private void settings(VideoOutput vo,VideoProfile vq) throws PageException, IOException {
459                    defineSize(vq,new VideoInput[]{source});
460                    if(audioBitrate>0)vq.setAudioBitrate(audioBitrate);
461                    if(aspectRatio>0)vq.setAspectRatio(aspectRatio);
462                    if(framerate>0)vq.setFramerate(framerate);
463                    if(videoBitrate>0)vq.setVideoBitrate(videoBitrate);
464                    if(videoBitrateMin>0)vq.setVideoBitrateMin(videoBitrateMin);
465                    if(videoBitrateMax>0)vq.setVideoBitrateMax(videoBitrateMax);
466                    if(videoBitrateTolerance>0)vq.setVideoBitrateTolerance(videoBitrateTolerance);
467                    if(audiosamplerate>0)vq.setAudioSamplerate(audiosamplerate);
468                    if(buffersize>0)vq.setBufferSize(buffersize);
469                    if(execution==EXECUTION_QUALITY)vq.setPass(2);
470    
471                    if(!StringUtil.isEmpty(title))vo.setTitle(title);
472                    if(!StringUtil.isEmpty(author))vo.setAuthor(author);
473                    if(!StringUtil.isEmpty(comment))vo.setComment(comment);
474                    if(!StringUtil.isEmpty(copyright))vo.setCopyright(copyright);
475                    if(!StringUtil.isEmpty(videoCodec))vq.setVideoCodec(videoCodec);
476                    if(!StringUtil.isEmpty(audioCodec))vq.setAudioCodec(audioCodec);
477                    
478                    
479                    
480                    if(framerate==0 && (startFrame>0 || maxTime>0)) {
481                            framerate=(int)getInfo(source).getFramerate();
482                    }
483                    
484                    // start
485                    if(startFrame>0) {
486                            if(framerate>0) startTime=(startFrame/framerate);
487                    }
488                    if(startTime>0)destination.setOffset(startTime);
489                    
490                    // max
491                    if(maxTime>0) {
492                            if(framerate>0) maxFrame=(long)(maxTime*framerate);
493                    }
494                    if(maxTime>0)vo.setMaxFrames(maxFrame);
495                    
496                    // maxtime (only for cfvideo struct)
497                    if(maxFrame>0 && maxTime==0) {
498                            if(framerate!=-1) maxTime=maxFrame/framerate;
499                    }
500                    destination.setFrameRate(framerate);
501                    
502            }
503    
504            private ProfileCollection getProfileCollection() throws PageException {
505                    return getProfileCollection(pageContext);
506            }
507            
508            public static ProfileCollection getProfileCollection(PageContext pc) throws PageException {
509                    if(_profileCollection==null) {
510                            try {
511                                    _profileCollection=new ProfileCollection(pc.getConfig());
512                            } 
513                            catch (Exception e) {
514                                    throw Caster.toPageException(e);
515                            }
516                    }
517                    return _profileCollection;
518            }
519    
520            private void defineSize(VideoProfile quality, VideoInput[] sources) throws PageException {
521                    if(strWidth!=null || strHeight!=null) {
522                            int[] dim = VideoUtilImpl.getInstance().calculateDimension(pageContext, sources, width, strWidth, height, strHeight);
523                            quality.setDimension(dim[0], dim[1]);
524                    }
525            }
526    
527            private void checkFile(VideoInput vi, String label, boolean exist, boolean canRead, boolean canWrite) throws PageException {
528                    if(vi==null) {
529                            throw doThrow("attribute "+label+" is required for tag video action "+strAction);
530                    }
531                    checkFile(vi.getResource(), label, exist, canRead, canWrite);
532            }
533    
534            private void checkFile(VideoOutput vo, String label, boolean exist, boolean canRead, boolean canWrite) throws PageException {
535                    if(vo==null) {
536                            throw doThrow("attribute "+label+" is required for tag video action "+strAction);
537                    }
538                    checkFile(vo.getResource(), label, exist, canRead, canWrite);
539            }
540            
541            
542            private void checkFile(Resource res, String label, boolean exist, boolean canRead, boolean canWrite) throws PageException {
543                    
544                    if(res==null) {
545                            throw doThrow("attribute "+label+" is required for tag video action "+strAction);
546                    }
547                    
548                    
549                    if(!res.exists()) {
550                            if(exist)throw doThrow(""+label+" file does not exist");
551                    }
552                    else {
553                             if(!res.isFile())
554                                    throw doThrow(label+" ["+res.toString()+"] is not a file");
555                    else if(canRead &&!res.canRead())
556                        throw doThrow("no read access to "+label+" ["+res.toString()+"]");
557                    else if(canWrite && !res.canWrite())
558                        throw doThrow("no write access to "+label+" ["+res.toString()+"]");
559                    }
560            }
561            
562            private boolean checkDestination(VideoInput vi, VideoOutput destination, String name, int nameconflict) throws PageException {
563                    checkFile(destination,"destination", false, false, false);
564                    
565                    // destination
566                    if(!Util.isEmpty(name))destination.setResource(destination.getResource().getRealResource(name));
567                    else if(destination.getResource().isDirectory()) {
568                            destination.setResource(destination.getResource().getRealResource(vi.getResource().getName()));
569                    }
570                    
571                    // escape %d
572                    String _name=destination.getResource().getName();
573                    if(_name.indexOf("%d")!=-1) {
574                            destination.setResource(destination.getResource().getParentResource().getRealResource(_name.replaceAll("%d", "%%d")));
575                    }
576                    
577                    
578                    if(destination.getResource().exists() && nameconflict!=NAMECONFLICT_OVERWRITE) {
579                            // SKIP
580                            if(nameconflict==NAMECONFLICT_SKIP) return false;
581                            // MAKEUNIQUE
582                            else if(nameconflict==NAMECONFLICT_MAKEUNIQUE) destination.setResource(makeUnique(destination.getResource()));
583                            // ERROR
584                            else doThrow("destiniation file ["+destination.toString()+"] already exist");
585                    }
586            return true;
587            }
588            
589            private Resource makeUnique(Resource res) {
590    
591                    String ext=getFileExtension(res);
592                    String name=getFileName(res);
593                    ext=(ext==null)?"":"."+ext;
594                    int count=0;
595                    while(res.exists()) {
596                            res=res.getParentResource().getRealResource(name+(++count)+ext);
597                    }
598                    return res;
599            }
600            
601            /**
602             * get file extension of a file object
603             * @param file file object
604             * @return extnesion
605             */
606            private static String getFileExtension(Resource file) {
607                    String name=file.getName();
608                    int index = name.lastIndexOf('.');
609                    if(index==-1) return null;
610                    return name.substring(index+1).trim();
611            }
612            
613            /**
614             * get file name of a file object without extension
615             * @param file file object
616             * @return name of the file 
617             */
618            private static String getFileName(Resource file) {
619                    String name=file.getName();
620                    int pos=name.lastIndexOf(".");
621                    
622                    if(pos==-1)return name;
623                    return name.substring(0,pos);
624            }
625    
626            private VideoProfile getProfile(String strProfile) throws PageException {
627                    strProfile=strProfile.trim().toLowerCase();
628                    return (VideoProfile) getProfileCollection().getProfiles().get(strProfile);
629            }
630            
631            private String getProfileKeyList() throws PageException {
632                    Iterator it = getProfileCollection().getProfiles().keySet().iterator();
633                    StringBuffer sb=new StringBuffer();
634                    boolean doDel=false;
635                    while(it.hasNext()) {
636                            if(doDel)sb.append(", ");
637                            sb.append(it.next());
638                            doDel=true;
639                    }
640                    
641                    return sb.toString();
642            }
643            
644            private PageException doThrow(String message) {
645                    return doThrow(message,null);
646            }
647            
648            private PageException doThrow(String message, String detail) {
649                    if(detail==null)return new ApplicationException(message);
650                    return new ApplicationException(message,detail);
651            }
652            
653    
654    
655    
656    
657            /**
658             * @param width the width to set
659             */
660            public void setWidth(String strWidth) {
661                    this.width = Caster.toIntValue(strWidth,-1);
662                    this.strWidth = strWidth;
663            }
664    
665            /**
666             * @param height the height to set
667             */
668            public void setHeight(String strHeight) {
669                    this.height = Caster.toIntValue(strHeight,-1);
670                    this.strHeight = strHeight;
671            }
672    
673            /**
674             * @param audioBitrate the audioBitrate to set
675             * @throws PageException 
676             */
677            public void setAudiobitrate(String audioBitrate) throws PageException {
678                    this.audioBitrate = util.toBytes(audioBitrate);
679            }
680    
681            /**
682             * @param aspectRatio the aspectRatio to set
683             * @throws PageException 
684             */
685            public void setAspectratio(String strAspectRatio) throws PageException {
686                    strAspectRatio=strAspectRatio.trim().toLowerCase();
687                    if("16:9".equals(strAspectRatio)) aspectRatio=VideoProfile.ASPECT_RATIO_16_9;
688                    else if("4:3".equals(strAspectRatio)) aspectRatio=VideoProfile.ASPECT_RATIO_4_3;
689                    else throw doThrow("invalid aspect ratio definition ["+strAspectRatio+"], valid values are [16:9, 4:3]");
690            }
691    
692            /**
693             * @param framerate the framerate to set
694             */
695            public void setFramerate(double framerate) {
696                    setFps(framerate);
697            }
698            public void setFps(double framerate) {
699                    this.framerate = (int) framerate;
700            }
701    
702            /**
703             * @param videoBitrate the videoBitrate to set
704             * @throws PageException 
705             */
706            public void setVideobitrate(String videoBitrate) throws PageException {
707                    this.videoBitrate = util.toBytes(videoBitrate);
708            }
709    
710            /**
711             * @param videoBitrateMin the videoBitrateMin to set
712             * @throws PageException 
713             */
714            public void setVideobitratemin(String videoBitrateMin) throws PageException {
715                    this.videoBitrateMin = util.toBytes(videoBitrateMin);
716            }
717    
718            /**
719             * @param videoBitrateMax the videoBitrateMax to set
720             * @throws PageException 
721             */
722            public void setVideobitratemax(String videoBitrateMax) throws PageException {
723                    this.videoBitrateMax = util.toBytes(videoBitrateMax);
724            }
725    
726            /**
727             * @param videoBitrateTolerance the videoBitrateTolerance to set
728             * @throws PageException 
729             */
730            public void setVideobitratetolerance(String videoBitrateTolerance) throws PageException {
731                    this.videoBitrateTolerance = util.toBytes(videoBitrateTolerance);
732            }
733    
734            /**
735             * @param author the author to set
736             */
737            public void setAuthor(String author) {
738                    this.author = author;
739            }
740    
741            /**
742             * @param title the title to set
743             */
744            public void setTitle(String title) {
745                    this.title = title;
746            }
747    
748            /**
749             * @param comment the comment to set
750             */
751            public void setComment(String comment) {
752                    this.comment = comment;
753            }
754    
755            /**
756             * @param copyright the copyright to set
757             */
758            public void setCopyright(String copyright) {
759                    this.copyright = copyright;
760            }
761    
762            /**
763             * @param max the maxFrames to set
764             * @throws PageException 
765             */
766            public void setMax(String strMax) throws PageException {
767                    strMax=strMax.trim().toLowerCase();
768                    if(strMax.endsWith("f"))                        this.maxFrame=Caster.toLongValue(strMax.substring(0,strMax.length()-1));
769                    else if(strMax.endsWith("ms"))  this.maxTime=Caster.toDoubleValue(strMax.substring(0,strMax.length()-2))/1000F;
770                    else if(strMax.endsWith("s"))           this.maxTime=Caster.toDoubleValue(strMax.substring(0,strMax.length()-1));
771                    else setStart(strMax+" ms");
772            }
773    
774            /**
775             * @param result the result to set
776             */
777            public void setResult(String result) {
778                    this.result = result;
779            }
780    
781    
782    
783            /**
784             * @param source1 the source1 to set
785             * @throws PageException 
786             */
787            public void setSource1(String source1) throws PageException {
788                    this.source1 = new VideoInputImpl(ResourceUtil.toResourceExisting(pageContext, source1));
789            }
790    
791    
792    
793            /**
794             * @param source2 the source2 to set
795             * @throws PageException 
796             */
797            public void setSource2(String source2) throws PageException {
798                    this.source2 = new VideoInputImpl(ResourceUtil.toResourceExisting(pageContext, source2));
799            }
800    
801    
802    
803            /**
804             * @param strStart the position to set
805             * @throws PageException 
806             */
807            public void setStart(String strStart) throws PageException {
808                    strStart=strStart.trim().toLowerCase();
809                    if(strStart.endsWith("f"))                      this.startFrame=Caster.toLongValue(strStart.substring(0,strStart.length()-1));
810                    else if(strStart.endsWith("ms"))        this.startTime=Caster.toDoubleValue(strStart.substring(0,strStart.length()-2))/1000F;
811                    else if(strStart.endsWith("s"))         this.startTime=Caster.toDoubleValue(strStart.substring(0,strStart.length()-1));
812                    else setStart(strStart+" ms");
813            }
814    
815            /**
816             * @param videoCodec the videoCodec to set
817             */
818            public void setVideocodec(String videoCodec) {
819                    this.videoCodec = videoCodec;
820            }
821            public void setAudiocodec(String audioCodec) {
822                    this.audioCodec = audioCodec;
823            }
824            
825            class Pair {
826                    Resource res;
827                    Struct sct;
828                    public Pair(Resource res, Struct sct) {
829                            this.res = res;
830                            this.sct = sct;
831                    }
832            }
833    
834            /**
835             * @param audiosamplerate the audiosamplerate to set
836             * @throws PageException 
837             */
838            public void setAudiosamplerate(String sAudiosamplerate) throws PageException {
839                    this.audiosamplerate = util.toHerz(sAudiosamplerate);
840            }
841    
842            public static void checkRestriction() {
843                    
844            }
845            
846            /**
847             * @param debug the debug to set
848             */
849            public void setDebug(String debug) {
850                    this.debug = debug;
851            }
852    
853            /**
854             * @param buffersize the buffersize to set
855             */
856            public void setBuffersize(double buffersize) {
857                    this.buffersize = (long) buffersize;
858            }
859            
860    
861            public Struct toStruct(VideoInfo[] infos) {
862                    Struct sct=new StructImpl();
863                    sct.setEL("source", toStruct(infos[0]));
864                    sct.setEL("destination", toStruct(infos[1]));
865                    return sct;
866            }
867            
868            private Struct toStruct(VideoInfo info) {
869                    
870                    Struct sct=base(info);
871                    
872                    // audio
873                    Struct audio=Caster.toStruct(sct.get(AUDIO,null),null);
874                    if(audio==null) {
875                            audio=new StructImpl();
876                            sct.setEL(AUDIO, audio);
877                    }
878                    
879                    // video
880                    Struct video=Caster.toStruct(sct.get(VIDEO,null),null);
881                    if(video==null) {
882                            video=new StructImpl();
883                            sct.setEL(VIDEO, video);
884                    }
885                    
886                    // Audio
887                    audio.setEL("channels", info.getAudioChannels());
888                    audio.setEL("codec", info.getAudioCodec());
889                    if(info.getAudioBitrate()!=-1)audio.setEL("bitrate", new Double(info.getAudioBitrate()));
890                    if(info.getAudioSamplerate()!=-1)audio.setEL("samplerate", new Double(info.getAudioSamplerate()));
891                    
892                    // Video
893                    video.setEL("codec", info.getVideoCodec());
894                    video.setEL(KeyConstants._format, info.getVideoFormat());
895                    if(info.getVideoBitrate()!=-1)video.setEL("bitrate", new Double(info.getVideoBitrate()));
896                    if(info.getFramerate()!=-1)video.setEL("framerate", new Double(info.getFramerate()));
897                    
898                    // Allgemein
899                    if(info.getDuration()!=-1)sct.setEL("duration", new Double(info.getDuration()));
900                    if(info.getHeight()!=-1)sct.setEL(KeyConstants._height, new Double(info.getHeight()));
901                    if(info.getWidth()!=-1)sct.setEL(KeyConstants._width, new Double(info.getWidth()));
902                    
903                    
904                    
905                    return sct;
906            }
907            
908            /*
909             * genrate the base struct for info
910             * @param info
911             * @return
912             */
913            private static synchronized Struct base(VideoInfo info) {
914                    // FUTURE add toStruct():Struct to the VideoInfo interface and remove reflection here
915                    Struct sct=null;
916                    if(hasToStruct!=Boolean.FALSE){
917                            try {
918                                    if(toStruct==null) {
919                                            toStruct = info.getClass().getMethod("toStruct", new Class[0]);
920                                    }
921                                    sct= Caster.toStruct(toStruct.invoke(info, new Object[0]),null);
922                                    hasToStruct=Boolean.TRUE;
923                            }
924                            catch(Throwable t){
925                                    hasToStruct=Boolean.FALSE;
926                            }
927                    }
928                    
929                    if(sct==null)sct=new StructImpl();
930                    
931                    return sct;
932            }
933    }