001/******************************************************************************* 002 * Copyright (c) 2024, 2026, Olivier Ayache. All rights reserved. 003 * 004 * This file is part of AVPKit. 005 * 006 * AVPKit is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU Lesser General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * AVPKit is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with AVPKit. If not, see <http://www.gnu.org/licenses/>. 018 *******************************************************************************/ 019package com.avpkit.core; 020 021import java.io.FileInputStream; 022import java.io.FileNotFoundException; 023import java.io.IOException; 024import java.util.Collection; 025import java.util.Enumeration; 026import java.util.Properties; 027 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031/** 032 * A global configuration class for AVPKit. 033 * <p> 034 * This object can help print setting options 035 * on {@link IConfigurable} classes, and also 036 * provides a convenient method {@link #configure(Properties, IConfigurable)} 037 * that lets you configure {@link IConfigurable} objects from 038 * a Java properties or FFmpeg preset file. 039 * </p> 040 * @see #configure(Properties, IConfigurable) 041 * @see IConfigurable 042 * 043 * @author aclarke 044 * 045 */ 046public class Configuration 047{ 048 static private final Logger log = LoggerFactory.getLogger(Configuration.class); 049 050 /** don't allow construction */ 051 private Configuration() {} 052 053 /** 054 * A main program that just prints information on all avaiable 055 * AVPKit options. 056 * @param args ignored 057 */ 058 public static void main(String[] args) 059 { 060 printHelp(System.out); 061 } 062 063 064 /** 065 * Print out help about this AVPKit installation. 066 * 067 * Specifically all installed and supported container formats, installed 068 * and supported codecs, all {@link IContainer} options, 069 * all {@link IStreamCoder} options and all {@link IVideoResampler} 070 * options. 071 * @param stream stream to print to 072 */ 073 public static void printHelp(java.io.PrintStream stream) 074 { 075 printSupportedContainerFormats(stream); 076 printSupportedCodecs(stream); 077 printSupportedContainerProperties(stream); 078 printSupportedStreamCoderProperties(stream); 079 printSupportedVideoResamplerProperties(stream); 080 } 081 082 /** 083 * Print out all installed and supported container formats on this system. 084 * @param stream stream to print to 085 */ 086 public static void printSupportedContainerFormats(java.io.PrintStream stream) 087 { 088 stream.println("======================================="); 089 stream.println(" Demuxable Formats"); 090 stream.println("======================================="); 091 092 Collection<IContainerFormat> inputFormats = IContainerFormat.getInstalledInputFormats(); 093 for(IContainerFormat inputFormat : inputFormats) 094 { 095 stream.printf(" \"%s\": %s\n", inputFormat.getInputFormatShortName(), 096 inputFormat.getInputFormatLongName()); 097 } 098 stream.println("======================================="); 099 stream.println(" Muxable Formats"); 100 stream.println("======================================="); 101 102 Collection<IContainerFormat> outputFormats = IContainerFormat.getInstalledOutputFormats(); 103 for(IContainerFormat outputFormat : outputFormats) 104 { 105 stream.printf(" \"%s\": %s\n", outputFormat.getOutputFormatShortName(), 106 outputFormat.getOutputFormatLongName()); 107 } 108 } 109 110 /** 111 * Print out all installed and supported codecs on this system. 112 * @param stream stream to print to 113 */ 114 public static void printSupportedCodecs(java.io.PrintStream stream) 115 { 116 Collection<ICodec> codecs = ICodec.getInstalledCodecs(); 117 118 stream.println("======================================="); 119 stream.println(" Decodeable Codecs"); 120 stream.println("======================================="); 121 122 for(ICodec codec : codecs) 123 { 124 if (codec.canDecode()) 125 { 126 stream.printf("%s %s (%s): %s\n", 127 codec.getType(), 128 codec.getID(), 129 codec.getName(), 130 codec.getLongName()); 131 } 132 } 133 stream.println("======================================="); 134 stream.println(" Encodeable Codecs"); 135 stream.println("======================================="); 136 for(ICodec codec : codecs) 137 { 138 if (codec.canEncode()) 139 { 140 stream.printf("%s %s (%s): %s\n", 141 codec.getType(), 142 codec.getID(), 143 codec.getName(), 144 codec.getLongName()); 145 } 146 } 147 } 148 149 /** 150 * Print out all {@link IVideoResampler} supported properties, if 151 * that option is supported in this version of AVPKit. 152 * 153 * @param stream stream to print to. 154 */ 155 public static void printSupportedVideoResamplerProperties(java.io.PrintStream stream) 156 { 157 if (IVideoResampler.isSupported(IVideoResampler.Feature.FEATURE_COLORSPACECONVERSION)) 158 printConfigurable(stream, IVideoResampler.make(100, 159 100, 160 IPixelFormat.Type.YUV420P, 161 200, 162 200, 163 IPixelFormat.Type.RGB24)); 164 } 165 166 /** 167 * Print out all {@link IStreamCoder} supported properties. 168 * 169 * @param stream stream to print to. 170 */ 171 public static void printSupportedStreamCoderProperties(java.io.PrintStream stream) 172 { 173 printConfigurable(stream, IStreamCoder.make(IStreamCoder.Direction.ENCODING, (ICodec)null)); 174 } 175 176 /** 177 * Print out all {@link IContainer} supported properties. 178 * 179 * @param stream stream to print to. 180 */ 181 public static void printSupportedContainerProperties(java.io.PrintStream stream) 182 { 183 printConfigurable(stream, IContainer.make()); 184 } 185 186 /** 187 * Print out all configurable options on the {@link IConfigurable} object. 188 * 189 * @param stream stream to print to 190 * @param configObj {@link IConfigurable} object. 191 */ 192 public static void printConfigurable(java.io.PrintStream stream, 193 IConfigurable configObj) 194 { 195 stream.println("======================================="); 196 stream.println(" " + configObj.getClass().getName() + " Properties"); 197 stream.println("======================================="); 198 int numOptions = configObj.getNumProperties(); 199 200 for(int i = 0; i < numOptions; i++) 201 { 202 IProperty prop = configObj.getPropertyMetaData(i); 203 printOption(stream, configObj, prop); 204 } 205 206 } 207 208 /** 209 * Print information about the property on the configurable object. 210 * 211 * @param stream stream to print to 212 * @param configObj configurable object 213 * @param prop property on object 214 */ 215 public static void printOption(java.io.PrintStream stream, 216 IConfigurable configObj, IProperty prop) 217 { 218 if (prop.getType() != IProperty.Type.PROPERTY_FLAGS) 219 { 220 stream.printf(" %s; default= %s; type=%s;\n", 221 prop.getName(), 222 configObj.getPropertyAsString(prop.getName()), 223 prop.getType()); 224 } else { 225 // it's a flag 226 stream.printf(" %s; default= %d; valid values=(", 227 prop.getName(), 228 configObj.getPropertyAsLong(prop.getName())); 229 int numSettings = prop.getNumFlagSettings(); 230 long value = configObj.getPropertyAsLong(prop.getName()); 231 for(int i = 0; i < numSettings; i++) 232 { 233 IProperty fprop = prop.getFlagConstant(i); 234 long flagMask = fprop.getDefault(); 235 boolean isSet = (value & flagMask)>0; 236 stream.printf("%s%s; ", 237 isSet ? "+" : "-", 238 fprop.getName()); 239 } 240 stream.printf("); type=%s;\n", prop.getType()); 241 } 242 stream.printf(" help for %s: %s\n", 243 prop.getName(), 244 prop.getHelp() == null ? "no help available" : prop.getHelp()); 245 } 246 247 /** 248 * Configures an {@link IConfigurable} from a set of Java {@link Properties}. 249 * <p> 250 * Here's some sample code that shows configuring a IStreamCoder 251 * from a FFmpeg preset file: 252 * </p> 253 * <pre> 254 * Properties props = new Properties(); 255 * props.load(new FileInputStream(file)); 256 * IStreamCoder coder = IStreamCoder.make(Direction.ENCODING); 257 * int retval = Configuration.configure(props, coder); 258 * if (retval < 0) 259 * throw new RuntimeException("Could not configure object"); 260 * </pre> 261 * @param properties The properties to use. 262 * @param config The item to configure. 263 * @return <0 on error; >= 0 on success. 264 * @since 3.4 265 */ 266 @SuppressWarnings("unchecked") 267 public static int configure(final Properties properties, final IConfigurable config) 268 { 269 for ( 270 final Enumeration<String> names = (Enumeration<String>) properties.propertyNames(); 271 names.hasMoreElements(); 272 ) 273 { 274 final String name = names.nextElement(); 275 final String value = properties.getProperty(name); 276 if (value != null) { 277 final int retval = config.setProperty(name, value); 278 if (retval < 0) { 279 log.warn("Invalid property on object {}; name=\"{}\"; value=\"{}\"", 280 new Object[]{ 281 config, 282 name, 283 value 284 }); 285 return retval; 286 } 287 } 288 } 289 return 0; 290 } 291 /** 292 * Configures an {@link IConfigurable} from a file. 293 * <p> 294 * This is a handy way to configure a AVPKit {@link IConfigurable} 295 * object, and will also work with FFmpeg preset files. 296 * </p> 297 * @param file A filename for the properties file. 298 * @param config The item to configure. 299 * @return <0 on error; >= 0 on success. 300 * @since 3.4 301 */ 302 @SuppressWarnings("unchecked") 303 public static int configure(final String file, final IConfigurable config) 304 { 305 Properties props = new Properties(); 306 try 307 { 308 props.load(new FileInputStream(file)); 309 } 310 catch (FileNotFoundException e) 311 { 312 e.printStackTrace(); 313 return -1; 314 } 315 catch (IOException e) 316 { 317 e.printStackTrace(); 318 return -1; 319 } 320 return Configuration.configure(props, config); 321 } 322 323}