001    package railo.runtime.video;
002    
003    import java.io.IOException;
004    import java.io.InputStream;
005    import java.io.OutputStream;
006    import java.util.ArrayList;
007    import java.util.LinkedHashMap;
008    import java.util.Map;
009    import java.util.StringTokenizer;
010    
011    import org.w3c.dom.Document;
012    import org.w3c.dom.Element;
013    import org.w3c.dom.Node;
014    import org.w3c.dom.NodeList;
015    import org.xml.sax.InputSource;
016    import org.xml.sax.SAXException;
017    
018    import railo.commons.io.IOUtil;
019    import railo.commons.io.res.Resource;
020    import railo.loader.util.Util;
021    import railo.runtime.config.Config;
022    import railo.runtime.config.ConfigImpl;
023    import railo.runtime.exp.ApplicationException;
024    import railo.runtime.exp.PageException;
025    import railo.runtime.op.Caster;
026    import railo.runtime.text.xml.XMLUtil;
027    
028    
029    public class ProfileCollection  {
030            
031            private static VideoUtil util=VideoUtilImpl.getInstance();
032            
033            private Map<String,VideoProfile> profiles;
034    
035            public ProfileCollection(Config config) throws ApplicationException {
036                    init(config,true);
037            }
038            
039            private void init(Config config, boolean initProfiles) throws ApplicationException {
040                    // get the video directory
041                    Resource dir = ((ConfigImpl)config).getVideoDirectory();
042                    
043                    // get the video.xml
044                    Resource xml=dir.getRealResource("video.xml");
045                
046                    // create (if not exist) and return video xml as dom
047                    Element video;
048                    try {
049                            video = getVideoXML(xml);
050                    } catch (Exception e) {
051                            throw new ApplicationException("can not load video xml file ["+xml+"]",Caster.toClassName(e)+":"+e.getMessage());
052                    }
053                    
054                    // translate form DOM to a List of VideoProfile
055                    if(initProfiles){
056                            try {
057                                    profiles=translateVideoXML(video);
058                            } catch (PageException e) {
059                                    throw new ApplicationException("can not load profiles from video xml file ["+xml+"] a type is invalid",e.getMessage());
060                            }
061                    }
062            }
063            
064            /**
065             * @return the qualities
066             */
067            public Map<String,VideoProfile> getProfiles() {
068                    return profiles;
069            }
070            
071            
072    
073            /**
074             *  translate form DOM to a List of VideoProfile
075             * @param video
076             * @return
077             * @throws PageException
078             */
079            private static Map<String,VideoProfile> translateVideoXML(Element video) throws PageException {
080                    Map<String,VideoProfile> profiles=new LinkedHashMap<String,VideoProfile>();
081                    // quality
082                Element qd = getChildByName(video, "profiles", false);
083                Element[] items = getChildren(qd, "profile");
084                Element item;
085                VideoProfile vq;
086                String value;
087                for(int i=0;i<items.length;i++) {
088                    item=items[i];
089                    vq=new VideoProfileImpl();
090                    // aspect-ratio
091                    value=item.getAttribute("aspect-ratio");
092                    if(!Util.isEmpty(value))vq.setAspectRatio(value);
093    
094                    // aspect-ratio
095                    value=item.getAttribute("audio-bitrate");
096                    if(!Util.isEmpty(value))vq.setAudioBitrate(util.toBytes(value));
097    
098                    // audio-samplerate
099                    value=item.getAttribute("audio-samplerate");
100                    if(!Util.isEmpty(value))vq.setAudioSamplerate(util.toHerz(value));
101                    
102                    // dimension
103                    String w = item.getAttribute("width");
104                    String h = item.getAttribute("height");
105                    if(!Util.isEmpty(w) && !Util.isEmpty(h))        {
106                            vq.setDimension(Caster.toIntValue(w), Caster.toIntValue(h));
107                    }
108    
109                    // framerate
110                    value=item.getAttribute("framerate");
111                    String value2 = item.getAttribute("fps");
112                    if(!Util.isEmpty(value))vq.setFramerate(Caster.toDoubleValue(value));
113                    else if(!Util.isEmpty(value2))vq.setFramerate(Caster.toDoubleValue(value2));
114                    
115                    // video-bitrate
116                    value=item.getAttribute("video-bitrate");
117                    if(!Util.isEmpty(value))vq.setVideoBitrate(util.toBytes(value));
118    
119                    // video-bitrate-max
120                    value=item.getAttribute("video-bitrate-max");
121                    if(!Util.isEmpty(value))vq.setVideoBitrateMax(util.toBytes(value));
122    
123                    // video-bitrate-min
124                    value=item.getAttribute("video-bitrate-min");
125                    if(!Util.isEmpty(value))vq.setVideoBitrateMin(util.toBytes(value));
126    
127                    // video-bitrate-tolerance
128                    value=item.getAttribute("video-bitrate-tolerance");
129                    if(!Util.isEmpty(value))vq.setVideoBitrateTolerance(util.toBytes(value));
130    
131                    // video-codec
132                    value=item.getAttribute("video-codec");
133                    //print.out("video-codec:"+value);
134                    if(!Util.isEmpty(value))vq.setVideoCodec(value);
135                    
136                    // audio-codec
137                    value=item.getAttribute("audio-codec");
138                    if(!Util.isEmpty(value))vq.setAudioCodec(value);
139                    
140                    // 
141                    value=item.getAttribute("label");
142                    //print.out("label:"+value);
143                    if(!Util.isEmpty(value)) {
144                            String[] arr=toArray(value);
145                            for(int y=0;y<arr.length;y++) {
146                                    profiles.put(arr[y].trim().toLowerCase(), vq);
147                            }
148                    }       
149                }
150                return profiles;
151            }
152            
153            
154            private static Element getChildByName(Node parent, String nodeName,boolean insertBefore) {
155            if(parent==null) return null;
156            NodeList list=parent.getChildNodes();
157            int len=list.getLength();
158            
159            for(int i=0;i<len;i++) {
160                Node node=list.item(i);
161                
162                if(node.getNodeType()==Node.ELEMENT_NODE && node.getNodeName().equalsIgnoreCase(nodeName)) {
163                    return (Element) node;
164                }
165            }
166            Element newEl = parent.getOwnerDocument().createElement(nodeName);
167            if(insertBefore)parent.insertBefore(newEl, parent.getFirstChild());
168            else parent.appendChild(newEl);        
169            return newEl;
170        }
171            
172        private static Element[] getChildren(Node parent, String nodeName) {
173            if(parent==null) return new Element[0];
174            NodeList list=parent.getChildNodes();
175            int len=list.getLength();
176            ArrayList rtn=new ArrayList();
177            
178            for(int i=0;i<len;i++) {
179                Node node=list.item(i);
180                if(node.getNodeType()==Node.ELEMENT_NODE && node.getNodeName().equalsIgnoreCase(nodeName)) {
181                    rtn.add(node);
182                }
183            }
184            return (Element[]) rtn.toArray(new Element[rtn.size()]);
185        }
186        
187        private static String[] toArray(String str) {
188                    StringTokenizer st=new StringTokenizer(str,",");
189                    ArrayList list=new ArrayList();
190                    while(st.hasMoreTokens()) {
191                            list.add(str=st.nextToken());
192                    }
193                    return (String[]) list.toArray(new String[list.size()]);
194            }
195        
196        /**
197             * create (if not exist) and return video xml as dom
198             * @param xml
199             * @return
200             * @throws IOException
201             * @throws SAXException
202             */
203            private static Element getVideoXML(Resource xml) throws IOException, SAXException {
204                    if(!xml.exists()) {
205                    createFileFromResource("/resource/video/video.xml",xml);
206                }
207                Document doc = loadDocument(xml);
208                return doc.getDocumentElement();
209            }
210            
211            public static void createFileFromResource(String path, Resource bin) throws IOException {
212                    InputStream is=null;
213                    OutputStream os=null;
214    
215                    if(bin.exists()) return;
216                    
217                    try {
218                            Util.copy(
219                                is=new VideoInputImpl(null).getClass().getResourceAsStream(path),
220                                os=bin.getOutputStream()
221                    );
222                    } 
223                    finally {
224                            Util.closeEL(is);
225                            Util.closeEL(os);
226                    }
227            }
228            
229            private static Document loadDocument(Resource xmlFile) throws SAXException, IOException {
230            InputStream is=null;
231            try {
232                    return loadDocument(is=xmlFile.getInputStream());
233            }
234            finally {
235                    Util.closeEL(is);
236            }
237        }
238        
239        private static Document loadDocument(InputStream is) throws SAXException, IOException {
240            try{
241                InputSource source = new InputSource(is);
242                    return XMLUtil.parse(source, null, false);
243            }
244            finally {
245                    IOUtil.closeEL(is);
246            }
247        }
248    }