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}