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 }