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 *******************************************************************************/
019
020package com.avpkit.mediatool;
021
022import static java.lang.System.exit;
023import static java.lang.System.out;
024
025import java.io.File;
026
027import com.avpkit.ferry.JNIMemoryManager;
028import com.avpkit.ferry.RefCounted;
029import com.avpkit.core.IContainer;
030import com.avpkit.core.ICodec;
031
032/**
033 * <strong>Start Here --</strong> A Factory for MediaTools, and global
034 * settings for the API.
035 * <p>
036 * Here's an example of a {@link ToolFactory} program that opens up an input media
037 * file, transcodes all the media to a new format, while playing the media on
038 * your machine:
039 * </p>
040 * 
041 * <pre>
042 * IMediaReader reader = ToolFactory.makeReader(&quot;input.mpg&quot;);
043 * reader.addListener(ToolFactory.makeViewer(true));
044 * reader.addListener(ToolFactory.makeWriter(&quot;output.flv&quot;, reader));
045 * while (reader.readPacket() == null)
046 *   ;
047 * </pre>
048 * 
049 * @author trebor
050 * @author aclarke
051 * 
052 */
053public class ToolFactory
054{
055
056  /**
057   * A private constructor to force only static members in this class.
058   */
059  private ToolFactory()
060  {
061
062  }
063
064  /* IMediaReader Factories */
065
066  /**
067   * Create an {@link IMediaReader} to reads and dispatches decoded media from a
068   * media container for a given source URL.
069   * 
070   * @param url the location of the media content, a file name will also work
071   *        here
072   * @return An {@link IMediaReader}
073   */
074
075  public static IMediaReader makeReader(String url)
076  {
077    return new MediaReader(url);
078  }
079
080  /**
081   * Create an {@link IMediaReader} to reads and dispatches decoded media from a
082   * media container for a given source URL.
083   * 
084   * <p>
085   * 
086   * Any AVPKit resourced opened by the {@link IMediaReader} will be closed by
087   * the {@link IMediaReader}, however resources opened outside the
088   * {@link IMediaReader} will not be closed. In short {@link IMediaReader}
089   * closes what it opens.
090   * 
091   * </p>
092   * 
093   * @param container an already created media container to read data from.
094   */
095
096  public static IMediaReader makeReader(IContainer container)
097  {
098    return new MediaReader(container);
099  }
100
101  /* MediaWriter constructors */
102  /**
103   * Use a specified {@link IMediaReader} as a source for media data and meta
104   * data about the container and it's streams. The {@link IMediaReader} must be
105   * configured such that streams will not be dynamically added to the
106   * container, which is the default for {@link IMediaReader}.
107   * 
108   * @param url the url or filename of the media destination
109   * @param reader the media source
110   * 
111   * @throws IllegalArgumentException if the specified {@link IMediaReader} is
112   *         configure to allow dynamic adding of streams.
113   */
114
115  public static IMediaWriter makeWriter(String url, IMediaReader reader)
116  {
117    return new MediaWriter(url, reader);
118  }
119
120  /**
121   * Use a specified {@link IContainer} as a source for and meta data about the
122   * container and it's streams. The {@link IContainer} must be configured such
123   * that streams will not be dynamically added to the container.
124   * 
125   * @param url the url or filename of the media destination
126   * @param inputContainer the source media container which will be queries to
127   *        determine what streams and media should be added when writing data.
128   * 
129   * @throws IllegalArgumentException if the specifed {@link IContainer} is not
130   *         a of type READ or is configure to allow dynamic adding of streams.
131   */
132
133  public static IMediaWriter makeWriter(String url, IContainer inputContainer)
134  {
135    return new MediaWriter(url, inputContainer);
136  }
137
138  /**
139   * Create a MediaWriter which will require subsequent calls to
140   * {@link IMediaWriter#addVideoStream(int, int, ICodec, int, int)} and/or
141   * {@link IMediaWriter#addAudioStream(int, int, ICodec, int, int)} to
142   * configure the writer. Streams may be added or further configured as needed
143   * until the first attempt to write data.
144   * 
145   * @param url the url or filename of the media destination
146   */
147  public static IMediaWriter makeWriter(String url)
148  {
149    return new MediaWriter(url);
150  }
151
152  /** Media Viewer Factories */
153  /**
154   * Construct a default media viewer.
155   */
156
157  public static IMediaViewer makeViewer()
158  {
159    return new MediaViewer();
160  }
161
162  /**
163   * Construct a media viewer which plays in the specified mode.
164   * 
165   * @param mode the play mode of this viewer
166   */
167
168  public static IMediaViewer makeViewer(IMediaViewer.Mode mode)
169  {
170    return new MediaViewer(mode);
171  }
172
173  /**
174   * Construct a media viewer and optionally show media statistics.
175   * 
176   * @param showStats display media statistics
177   */
178
179  public static IMediaViewer makeViewer(boolean showStats)
180  {
181    return new MediaViewer(showStats);
182  }
183
184  /**
185   * Construct a media viewer which plays in the specified mode and optionally
186   * shows media statistics.
187   * 
188   * @param mode the play mode of this viewer
189   * @param showStats display media statistics
190   */
191
192  public static IMediaViewer makeViewer(IMediaViewer.Mode mode,
193      boolean showStats)
194  {
195    return new MediaViewer(mode, showStats);
196  }
197
198  /**
199   * Construct a media viewer, optionally show media statistics and specify the
200   * default frame close behavior.
201   * 
202   * @param showStats display media statistics
203   * @param defaultCloseOperation what should Swing do if the window is closed.
204   *        See the {@link javax.swing.WindowConstants} documentation for valid
205   *        values.
206   */
207
208  public static IMediaViewer makeViewer(boolean showStats,
209      int defaultCloseOperation)
210  {
211    return new MediaViewer(showStats, defaultCloseOperation);
212  }
213
214  /**
215   * Construct a media viewer which plays in the specified mode, optionally
216   * shows media statistics and specifies the default frame close behavior.
217   * 
218   * @param mode the play mode of this viewer
219   * @param showStats display media statistics
220   * @param defaultCloseOperation what should Swing do if the window is closed.
221   *        See the {@link javax.swing.WindowConstants} documentation for valid
222   *        values.
223   */
224
225  public static IMediaViewer makeViewer(IMediaViewer.Mode mode,
226      boolean showStats, int defaultCloseOperation)
227  {
228    return new MediaViewer(mode, showStats, defaultCloseOperation);
229  }
230
231  /** {@link IMediaDebugListener} Factories */
232
233  /**
234   * Construct a debug listener which logs all event types.
235   */
236
237  public static IMediaDebugListener makeDebugListener()
238  {
239    return new MediaDebugListener();
240  }
241
242  /**
243   * Construct a debug listener with custom set of event types to log.
244   * 
245   * @param events the event types which will be logged
246   */
247
248  public static IMediaDebugListener makeDebugListener(
249      IMediaDebugListener.Event... events)
250  {
251    return new MediaDebugListener(events);
252  }
253
254  /**
255   * Construct a debug listener with custom set of event types to log.
256   * 
257   * @param mode log mode, see {@link IMediaDebugListener.Mode}
258   * @param events the event types which will be logged
259   */
260
261  public static IMediaDebugListener makeDebugListener(
262      IMediaDebugListener.Mode mode, IMediaDebugListener.Event... events)
263  {
264    return new MediaDebugListener(mode, events);
265  }
266  
267  /**
268   * Construct a debug listener with custom name and set of event types to
269   * log.
270   * 
271   * @param name symbolic name for this listener
272   * @param mode log mode, see {@link IMediaDebugListener.Mode}
273   * @param events the event types which will be logged
274   */
275
276  public static IMediaDebugListener makeDebugListener(String name,
277    IMediaDebugListener.Mode mode, IMediaDebugListener.Event... events)
278  {
279    return new MediaDebugListener(name, mode, events);
280  }
281
282  /**
283   * A sample program for the {@link ToolFactory}.  If given
284   * one argument on the command line, it will interpret that
285   * as a media file to read and play.  If given more than one,
286   * it will attempt to transcode the first file into formats
287   * guessed for all the other arguments.
288   * <p>
289   * For example, this decodes and plays &quot;input.flv&quot;, while
290   * transcoding &quot;input.flv&quot; into &quot;output1.mov&quot;
291   * and &quot;output2.mp4&quot;
292   * </p>
293   * <pre>
294   * com.avpkit.mediatool.ToolFactory input.flv output1.mov output2.mp4
295   * </pre>
296   * 
297   * @param args input filename and option output filenames
298   */
299
300  public static void main(String[] args)
301  {
302    if (args.length < 1)
303    {
304      System.out.println("Must enter at least one Source File to read.");
305      exit(0);
306    }
307
308    File source = new File(args[0]);
309    if (!source.exists())
310    {
311      out.println("Source file does not exist: " + source);
312      exit(0);
313    }
314
315    IMediaReader reader = ToolFactory.makeReader(args[0]);
316    reader.addListener(ToolFactory.makeViewer(true));
317    for(int i = 1; i< args.length; i++)
318      reader.addListener(ToolFactory.makeWriter(args[i], reader));
319    while (reader.readPacket() == null)
320      do {} while(false);
321
322  }
323
324  /**
325   * Turns on and off Turbo-Charging for the {@link ToolFactory} package.
326   * <p>
327   * Turbo-Charging is off by default.
328   * </p>
329   * <p>
330   * When running Turbo-Charged {@link ToolFactory} will make a variety of tuning
331   * assumptions that can speed up execution of your program, sometimes by
332   * significant amounts.  {@link ToolFactory} was designed from
333   * the ground up to run Turbo-Charged, but it can cause issues
334   * for other com.avpkit.core-based programs running
335   * in the same Java process.
336   * </p>
337   * <p> It is safe to turn on if your program only uses
338   * interfaces in the com.avpkit.mediatool API, and you are not running
339   * in the same Java process as other programs using the
340   * com.avpkit.core API.
341   * </p>
342   * <p>
343   * If you turn on Turbo-Charging and then access any of the
344   * underlying com.avpkit.core interfaces (e.g.
345   * {@link IMediaCoder#getContainer()}) behind ToolFactory,
346   * you must:
347   * <ul>
348   * <li>Call
349   * {@link RefCounted#delete()} on any references returned on the stack
350   * to your methods.</li>
351   * <li>Call {@link RefCounted#copyReference()} on any
352   * com.avpkit.core parameters passed to your methods
353   * that you need to keep a reference to after your method returns.</li>
354   * <li>Ensure you do not call {@link RefCounted#delete()} on
355   * parameters passed <strong>into</strong> your methods.  The calling
356   * method is managing that reference.</li>
357   * </ul>
358   * <p>
359   * Failure to follow these rules could lead to {@link OutOfMemoryError}
360   * errors, or to premature releasing of resources.
361   * </p>
362   * <p>
363   * Turbo-Charging works by changing the global
364   * {@link com.avpkit.ferry.JNIMemoryManager.MemoryModel} that the underlying
365   * com.avpkit.core API in your program is using. If you are using
366   * {@link ToolFactory} in a java program that contains other code using the
367   * com.avpkit.core API, you will force that code to use the new
368   * memory model.
369   * </p>
370   * <p>
371   * 
372   * Turbo-Charging should not be used by pregnant women.
373   * 
374   * </p>
375   * <p>
376   * 
377   * Badgers considering Turbo-Charging should check burrows for
378   * at least three feet of vertical clearance.
379   * 
380   * </p>
381   * <p>
382   * 
383   * Turbo-Charging Java Programs should be given at least 2-weeks of
384   * vacation per year in order to adequately recover from the
385   * Turbo-Charging experience. 
386   * 
387   * </p>
388   * <p>
389   * 
390   * Turbo-Charging is illegal without a license in British Columbia.
391   * Contact the Royal Canadian Mounted Police.  
392   * </p>
393   * <p>
394   * 
395   * Writing documentation about Turbo-Charging may lead to
396   * excessive stupidity.  Limiting documentation to four warnings
397   * or fewer is strongly recommended.
398   * 
399   * </p>
400   * 
401   * 
402   * @param turbo should we turn on turbo mode
403   * 
404   * @see JNIMemoryManager#setMemoryModel(com.avpkit.ferry.JNIMemoryManager.MemoryModel)
405   */
406  public static void setTurboCharged(boolean turbo)
407  {
408    if (turbo)
409      JNIMemoryManager
410          .setMemoryModel(JNIMemoryManager.MemoryModel.NATIVE_BUFFERS);
411    else
412      JNIMemoryManager
413          .setMemoryModel(JNIMemoryManager.MemoryModel.JAVA_STANDARD_HEAP);
414  }
415
416  /**
417   * Is {@link ToolFactory} running in Turbo-Charged mode?
418   * @return true if Turbo-Charged.  false if really just slogging
419   *   along and finding its own way.
420   */
421  public static boolean isTurboCharged()
422  {
423    return !(JNIMemoryManager.getMemoryModel() == JNIMemoryManager.MemoryModel.JAVA_STANDARD_HEAP);
424  }
425}