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("input.mpg"); 043 * reader.addListener(ToolFactory.makeViewer(true)); 044 * reader.addListener(ToolFactory.makeWriter("output.flv", 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 "input.flv", while 290 * transcoding "input.flv" into "output1.mov" 291 * and "output2.mp4" 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}