1122 |
1123 |
1124 |
1125 |
1126 |
1127 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | VideoExport
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Notes:
2 |
3 | ### 1. I'm looking for someone to **maintain** this library. Get in contact if you are using it and would like to continue its development. I can help by answering any questions you may have.
4 |
5 | ### 2. There's a newer **rewrite** of the library using Kotlin and Gradle. Download it from https://github.com/hamoid/video_export_processing/tree/kotlinGradle
6 |
7 | # Video Export for Processing
8 |
9 | This library interfaces with FFmpeg and makes it easy to export video files out
10 | of Processing.
11 |
12 | ## Example
13 |
14 | ```java
15 | import com.hamoid.*;
16 |
17 | VideoExport videoExport;
18 |
19 | void setup() {
20 | size(600, 600);
21 | videoExport = new VideoExport(this, "hello.mp4");
22 | videoExport.startMovie();
23 | }
24 | void draw() {
25 | background(#224488);
26 | rect(frameCount * frameCount % width, 0, 40, height);
27 | videoExport.saveFrame();
28 | }
29 | void keyPressed() {
30 | if (key == 'q') {
31 | videoExport.endMovie();
32 | exit();
33 | }
34 | }
35 | ```
36 |
37 | ## FFmpeg
38 |
39 | You need to download and install [FFmpeg](http://ffmpeg.org/) on your system before you can use this library.
40 | Note that you might already have it installed! You can find out by typing ffmpeg or ffmpeg.exe
41 | in the terminal. If the program is not found:
42 |
43 | * GNU/Linux systems: use your favorite package manager.
44 | * Windows: get a [static 32bit or 64bit binary](http://ffmpeg.zeranoe.com/builds/)
45 | * Mac: get a [static 64bit binary](http://evermeet.cx/ffmpeg/)
46 |
47 | For more details and download links, check the official FFmpeg website: [http://ffmpeg.org](http://ffmpeg.org/)
48 |
49 | When you start a Processing sketch that uses this library you may be asked to indicate the location
50 | of your FFmpeg executable.
51 |
52 | ## Frequent issues and questions
53 |
54 | ### What if I change the location of FFmpeg?
55 |
56 | The library should notice and ask you for its location again. The location
57 | information is saved in a json file which you can find in the library location.
58 | If you delete this json file the library will ask you for the location of FFmpeg
59 | and create the file again with the updated information.
60 |
61 | ### Sometimes the resulting mp4 video files are not working. Why?
62 |
63 | mp4 files contain essential metadata at the end of the file.
64 | The video export library saves this metadata when you call the
65 | `endMovie()` method. If you don't call it, the movie may be
66 | incomplete. The endMovie() method was added in version 0.1.5.
67 |
68 | ### I see an FFmpeg error related to "crf". Why?
69 |
70 | This happens if your copy of FFmpeg does not include the h264 encoder.
71 | Not all FFmpeg binaries are equal, some include more features than others.
72 | Try downloading a different or more recent binary. Let me know if that
73 | doesn't work.
74 |
75 | ### Odd widths and heights not allowed
76 |
77 | The exported video is compressed using the h264 codec. This codec does not allow odd image sizes like 111 x 113. Some computer screens actually have odd sizes like 1920 x 1059. If you use fullScreen() with such a screen, exporting will fail with an error like `height not divisible by 2 (1920x1059)`. This will be fixed in future versions by auto correcting the requested size to an even number and notifying the user.
78 |
79 | ### How can I tweak the FFmpeg command the library runs?
80 |
81 | The first time the library runs it will produce a settings.json file, which will
82 | be placed in the library folder. This file can be carefully edited to adjust the
83 | FFmpeg parameters used during the video creation. Why would you do this? FFmpeg
84 | accepts hundreds of parametrs to define which codec to use, how the codec should
85 | behave, etc. FFmpeg also includes hundreds of audio and video filters that can
86 | be used and configured. It would not make sense for me to create hundreds of
87 | methods in the video export library to let you access all those features.
88 | If you are and advanced user, you can tweak those settings yourself by editing
89 | settings.json to change the codec settings or apply special effects to the
90 | resulting video files. See [this example](https://forum.processing.org/two/discussion/comment/95710/#Comment_95710).
91 |
92 | ### NoSuchMethodError when calling endMovie()
93 |
94 | There seems to be a conflict between the Video Export library and the Video library. Both use different versions of the JNA library. See this discussion: https://github.com/hamoid/video_export_processing/issues/38
95 |
96 | ## change log
97 |
98 | * 0.2.3 - February 28th, 2018
99 | * Allow spaces in the FFmpeg command (#37)
100 | * Allow setting the FFmpeg command settings from code, not just in the json
101 | file (#36)
102 | * 0.2.2 - February 11th, 2018
103 | * Re-add jna.jar and jna-platform.jar, fixing broken exported applications
104 | (issue [47](https://github.com/hamoid/video_export_processing/issues/47) )
105 | and the Windows version.
106 | * 0.2.1 - November 8th, 2017
107 | * Add .setFfmpegPath() and fix [issue 41](https://github.com/hamoid/video_export_processing/issues/41)
108 | * 0.2.0 - October 22nd, 2017
109 | * Minor change: when calling nf(), replace ',' with '.'. Some systems may use
110 | ',' due to localization, which fails later when parsing the csv file.
111 | * 0.1.9 - April 22nd, 2017
112 | * https URL is now known by the IDE, so it's possible to install again without
113 | leaving the IDE.
114 | * Solve issue when attaching sound. In Ubuntu, the AAC codec inside FFmpeg is
115 | experimental, and had to be enabled in the command line.
116 | * Allow user customization of the FFmpeg commands that the library runs by including
117 | them inside settings.json, found in the library folder. This enables the user
118 | to tweak the commands to enable filters like blur, vignette, add noise,
119 | crop, etc. In future versions space characters should be allowed in those
120 | filter arguments.
121 | * 0.1.8 - April 17th, 2017
122 | * Switch server url to https (attempt to solve broken installation inside the IDE)
123 | * 0.1.7 - April 1st, 2017
124 | * Setting are now saved in the libray folder. Using the Java Preferences was
125 | giving errors on some Windows versions.
126 | * New example added to produce a video based on FFT data and making sure audio
127 | and video stay in sync.
128 | * In Windows, when ending a video, CTRL+C is sent to FFmpeg to terminate
129 | properly. This seems to fix corrupted videos on Windows.
130 | * The library now notices if FFmpeg is not found (maybe because it was moved)
131 | and asks again for its location.
132 | * 0.1.6 - December 8th, 2016
133 | * Fix for high dpi screens (Thanks to @LodenRietveld)
134 | * 0.1.5 - December 2nd, 2016
135 | * Refactoring. Clean up code.
136 | * Add .startMovie() and .endMovie() to prevent possible "missing-end-of-movie corruption".
137 | * Allow attaching a sound file to the produced movie.
138 | * Allow exporting multiple video files using the same videoExport object.
139 | * 0.1.4 - August 4th, 2016
140 | * Attempt to fix randomly corrupted videos on Windows
141 | * 0.1.3 - July 24th, 2016
142 | * Add webcam saving example.
143 | * Add getFfmpegPath() public method (requested by [@ffd8](https://github.com/ffd8)).
144 | * Replace PGraphics with PImage, enables webcam/movie saving (requested by [@transfluxus](https://github.com/transfluxus)).
145 | * 0.1.1 - June 15th, 2016
146 | * Use .waitFor() to reduce chances of video corruption.
147 | * ...
148 | * 0.0.1 - January 25th, 2015
149 |
150 | ## Download
151 |
152 | http://funprogramming.org/VideoExport-for-Processing/
153 |
--------------------------------------------------------------------------------
/VideoExport.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/data/README:
--------------------------------------------------------------------------------
1 | the data folder:
2 | If your Library is using files like images, sound files,
3 | any data file, etc., put them into the data folder.
4 | When coding your Library you can use processing's internal loading
5 | functions like loadImage(), loadStrings(), etc. to load files
6 | located inside the data folder into your Library.
7 |
8 |
--------------------------------------------------------------------------------
/examples/basic/basic.pde:
--------------------------------------------------------------------------------
1 | import com.hamoid.*;
2 |
3 | VideoExport videoExport;
4 |
5 | // Press 'q' to finish saving the movie and exit.
6 |
7 | // In some systems, if you close your sketch by pressing ESC,
8 | // by closing the window, or by pressing STOP, the resulting
9 | // movie might be corrupted. If that happens to you, use
10 | // videoExport.endMovie() like you see in this example.
11 |
12 | // In other systems pressing ESC produces correct movies
13 | // and .endMovie() is not necessary.
14 |
15 | void setup() {
16 | size(600, 600);
17 |
18 | videoExport = new VideoExport(this);
19 | videoExport.startMovie();
20 | }
21 | void draw() {
22 | background(#224488);
23 | rect(frameCount * frameCount % width, 0, 40, height);
24 |
25 | videoExport.saveFrame();
26 | }
27 | void keyPressed() {
28 | if (key == 'q') {
29 | videoExport.endMovie();
30 | exit();
31 | }
32 | }
--------------------------------------------------------------------------------
/examples/interactive/interactive.pde:
--------------------------------------------------------------------------------
1 | import com.hamoid.*;
2 |
3 | /*
4 | This sketch shows how you can record different takes.
5 |
6 | This can be useful when you have randomized generative
7 | graphics and you want to save only the "good parts".
8 |
9 | For instance, try to record the ball only moving down.
10 | (press R to start when the ball is high, press R again
11 | to stop when the ball is low).
12 |
13 | Do this multiple times to get a video in which the
14 | ball only goes down.
15 | */
16 |
17 | VideoExport videoExport;
18 | boolean recording = false;
19 |
20 | void setup() {
21 | size(600, 600);
22 | noStroke();
23 | frameRate(30);
24 |
25 | println("Press R to toggle recording");
26 |
27 | videoExport = new VideoExport(this, "interactive.mp4");
28 | videoExport.startMovie();
29 | }
30 | void draw() {
31 | background(0);
32 | float t = frameCount * 0.03;
33 | float sz = 100 + 50 * cos(t*1.33) * cos(t*1.84);
34 | ellipse(300 + 200 * cos(t*1.13) * cos(t*0.21),
35 | 300 + 200 * cos(t*1.71) * cos(t*0.47),
36 | sz, sz);
37 |
38 | if(recording) {
39 | videoExport.saveFrame();
40 | }
41 | }
42 |
43 | void keyPressed() {
44 | if(key == 'r' || key == 'R') {
45 | recording = !recording;
46 | println("Recording is " + (recording ? "ON" : "OFF"));
47 | }
48 | if (key == 'q') {
49 | videoExport.endMovie();
50 | exit();
51 | }
52 | }
--------------------------------------------------------------------------------
/examples/internetVideo/internetVideo.pde:
--------------------------------------------------------------------------------
1 | import com.hamoid.*;
2 |
3 | VideoExport videoExport;
4 |
5 | void setup() {
6 | size(600, 600);
7 | noStroke();
8 |
9 | videoExport = new VideoExport(this, "internetVideo.mp4");
10 |
11 | // The standard frame rate for internet videos is about
12 | // 30 frames per second
13 | videoExport.setFrameRate(30);
14 | videoExport.startMovie();
15 |
16 | // By default Processing tries to play at 60 frames
17 | // per second. That means that your exported videos
18 | // may feel slow, as they are played at half the
19 | // speed of the original. You may want to play your
20 | // sketch at 30 fps too, so the sketch and the video
21 | // run at similar frame rates. To compensate for the
22 | // lower frame rate you may have to adjust your sketch
23 | // to make your objects move faster.
24 | frameRate(30);
25 | }
26 | void draw() {
27 | background(33);
28 | textSize(150);
29 | text(frameCount, 50, 300);
30 | videoExport.saveFrame();
31 | }
32 | void keyPressed() {
33 | if (key == 'q') {
34 | videoExport.endMovie();
35 | exit();
36 | }
37 | }
--------------------------------------------------------------------------------
/examples/multipleMovies/multipleMovies.pde:
--------------------------------------------------------------------------------
1 | import com.hamoid.*;
2 |
3 | /*
4 | This sketch shows how you can output multiple movies.
5 | Hold down the mouse button for a few seconds to export a movie.
6 | Each time you do this it will export a new movie.
7 | */
8 |
9 | VideoExport videoExport;
10 | boolean recording = false;
11 | color bgcolor = #FFAA22;
12 |
13 | void setup() {
14 | size(600, 600, P2D);
15 | noStroke();
16 | frameRate(30);
17 |
18 | videoExport = new VideoExport(this);
19 | videoExport.setDebugging(false);
20 | }
21 | void draw() {
22 | background(bgcolor);
23 |
24 | // draw something
25 | float t = frameCount * 0.01;
26 | for(float a=0; a round(movieFPS * soundDuration)) {
28 | videoExport.endMovie();
29 | exit();
30 | }
31 | }
32 |
33 | /*
34 | Note: if you want to visualize sound and want to
35 | match the sound precisely see the "withAudioViz"
36 | example.
37 | */
38 |
--------------------------------------------------------------------------------
/examples/withAudioUrl/data/withAudioUrl.pde:
--------------------------------------------------------------------------------
1 | import com.hamoid.*;
2 |
3 | VideoExport videoExport;
4 |
5 | // This sketch creates a movie with audio from a streaming internet
6 | // radio station url. It will run until the 'q' or 'x' keys are pressed.
7 | // To find the url for a given station, look at the .m3u or .pls files
8 | // on the site.
9 |
10 | float movieFPS = 30;
11 | boolean copyMode = false;
12 | VideoExport videoExport = null;
13 |
14 |
15 | void setup() {
16 | background(0);
17 | fill(255);
18 | frameRate(movieFPS);
19 | // video related setup
20 | videoExport = new VideoExport(this);
21 | // video and processing framerates do not have to exactly match,
22 | // since the audio seems to drive the frame creation -
23 | // but looks choppy if they are too different.
24 | videoExport.setFrameRate(movieFPS);
25 | videoExport.setQuality(70, 128);
26 | videoExport.setAudioUrl("http://ice.somafm.com/spacestation");
27 | videoExport.startMovie();
28 | }
29 |
30 | void draw() {
31 | // demo drawing
32 | spaceTravel();
33 | // save video frame
34 | videoExport.saveFrame();
35 | }
36 |
37 |
38 | void keyPressed() {
39 | if (key == 'q' || key == 'x') {
40 | noLoop();
41 | videoExport.endMovie();
42 | exit();
43 | }
44 | }
45 |
46 | void spaceTravel() {
47 | stroke(random(127), random(159), random(191), random(127));
48 | strokeWeight(random(2,10));
49 | ellipse(random(width), random(height), random(1,3),random(1,6));
50 | // used to use copy(1, 1, width-2, height-2, 0, 0, width, height);
51 | // processing 3 seemed to introduce artifacts, so this looks better
52 | if (copyMode) {
53 | copy(0, 1, width, height-2, 0, 0, width, height);
54 | }
55 | else {
56 | copy(1, 0, width-2, height, 0, 0, width, height);
57 | }
58 | copyMode = !copyMode;
59 | }
60 |
--------------------------------------------------------------------------------
/examples/withAudioViz/data/jingle.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hamoid/video_export_processing/cf51bb6f949f1fd1713284972a302df7e928a7d1/examples/withAudioViz/data/jingle.mp3
--------------------------------------------------------------------------------
/examples/withAudioViz/withAudioViz.pde:
--------------------------------------------------------------------------------
1 | import com.hamoid.*;
2 | import ddf.minim.*;
3 | import ddf.minim.analysis.*;
4 | import ddf.minim.spi.*;
5 |
6 | VideoExport videoExport;
7 |
8 | String audioFilePath = "jingle.mp3";
9 |
10 | String SEP = "|";
11 | float movieFPS = 30;
12 | float frameDuration = 1 / movieFPS;
13 | BufferedReader reader;
14 |
15 | /*
16 | Example to visualize sound frequencies from
17 | an audio file.
18 |
19 | Producing a file with audio and video in sync
20 | is tricky. It gets easily out of sync.
21 |
22 | One approach, used in this example, is:
23 |
24 | Pass 1. Analyze the sound in a Processing sketch
25 | and output a text file including the FFT
26 | analysis data.
27 | Pass 2. Load the data from pass 1 and use it to
28 | output frames for a video file, including
29 | the right frames to match the sound
30 | precisely at any given time.
31 |
32 | Using this technique it does not matter how fast
33 | or slow your second program is, and you know that
34 | no frames will be dropped (as may happen when
35 | recording live).
36 |
37 | The difficulty of recording live graphics with
38 | sound is that the frame rate is not always stable.
39 | We may request 60 frames per second, but once in
40 | a while a frame is not ready on time. So the
41 | "speed of frames" (the frameRate) is not constant
42 | while frames are produced, but they are probably
43 | constant when played back. The "speed of audio",
44 | on the other hand, is often constant. If audio
45 | is constant but video is not, they get out of
46 | sync.
47 | */
48 |
49 | void setup() {
50 | size(600, 600);
51 |
52 | // Produce the video as fast as possible
53 | frameRate(1000);
54 |
55 | // Read a sound file and output a txt file
56 | // with the FFT analysis.
57 | // It uses Minim, because the standard
58 | // Sound library did not work in my system.
59 |
60 | // You could comment out the next line once you
61 | // have produced the txt file to speed up
62 | // experimentation. Otherwise every time you
63 | // run this program it re-generates the FFT
64 | // analysis.
65 | audioToTextFile(audioFilePath);
66 |
67 | // Now open the text file we just created for reading
68 | reader = createReader(audioFilePath + ".txt");
69 |
70 | // Set up the video exporting
71 | videoExport = new VideoExport(this);
72 | videoExport.setFrameRate(movieFPS);
73 | videoExport.setAudioFileName(audioFilePath);
74 | videoExport.startMovie();
75 | }
76 | void draw() {
77 | String line;
78 | try {
79 | line = reader.readLine();
80 | }
81 | catch (IOException e) {
82 | e.printStackTrace();
83 | line = null;
84 | }
85 | if (line == null) {
86 | // Done reading the file.
87 | // Close the video file.
88 | videoExport.endMovie();
89 | exit();
90 | } else {
91 | String[] p = split(line, SEP);
92 | // The first column indicates
93 | // the sound time in seconds.
94 | float soundTime = float(p[0]);
95 |
96 | // Our movie will have 30 frames per second.
97 | // Our FFT analysis probably produces
98 | // 43 rows per second (44100 / fftSize) or
99 | // 46.875 rows per second (48000 / fftSize).
100 | // We have two different data rates: 30fps vs 43rps.
101 | // How to deal with that? We render frames as
102 | // long as the movie time is less than the latest
103 | // data (sound) time.
104 | // I added an offset of half frame duration,
105 | // but I'm not sure if it's useful nor what
106 | // would be the ideal value. Please experiment :)
107 | while (videoExport.getCurrentTime() < soundTime + frameDuration * 0.5) {
108 | background(0);
109 | noStroke();
110 | // Iterate over all our data points (different
111 | // audio frequencies. First bass, then hihats)
112 | for (int i=1; i
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
341 |
--------------------------------------------------------------------------------
/resources/README.md:
--------------------------------------------------------------------------------
1 | ## How to install ##library.name##
2 |
3 | ### Install with the Contribution Manager
4 |
5 | Add contributed Libraries by selecting the menu item _Sketch_ → _Import Library..._ → _Add Library..._ This will open the Contribution Manager, where you can browse for ##library.name##, or any other Library you want to install.
6 |
7 | Not all available Libraries have been converted to show up in this menu. If a Library isn't there, it will need to be installed manually by following the instructions below.
8 |
9 | ### Manual Install
10 |
11 | Contributed Libraries may be downloaded separately and manually placed within the `libraries` folder of your Processing sketchbook. To find (and change) the Processing sketchbook location on your computer, open the Preferences window from the Processing application (PDE) and look for the "Sketchbook location" item at the top.
12 |
13 | By default the following locations are used for your sketchbook folder:
14 | * For Mac users, the sketchbook folder is located inside `~/Documents/Processing`
15 | * For Windows users, the sketchbook folder is located inside `My Documents/Processing`
16 |
17 | Download ##library.name## from ##library.url##
18 |
19 | Unzip and copy the contributed Library's folder into the `libraries` folder in the Processing sketchbook. You will need to create this `libraries` folder if it does not exist.
20 |
21 | The folder structure for Library ##library.name## should be as follows:
22 |
23 | ```
24 | Processing
25 | libraries
26 | ##library.name##
27 | examples
28 | library
29 | ##library.name##.jar
30 | reference
31 | src
32 | ```
33 |
34 | Some folders like `examples` or `src` might be missing. After Library ##library.name## has been successfully installed, restart the Processing application.
35 |
36 | ### Troubleshooting
37 |
38 | If you're having trouble, have a look at the [Processing Wiki](https://github.com/processing/processing/wiki/How-to-Install-a-Contributed-Library) for more information, or contact the author [##author.name##](##author.url##).
39 |
--------------------------------------------------------------------------------
/resources/build.properties:
--------------------------------------------------------------------------------
1 | # Create a Library for the Processing open source programming language and
2 | # environment (http://processing.org/)
3 | #
4 | # Customize the build properties to make the ant-build-process work for your
5 | # environment. How? Please read the comments below.
6 | #
7 | # The default properties are set for OS X. Please refer to comments for Windows
8 | # settings.
9 |
10 |
11 | # Where is your Processing sketchbook located?
12 | # If you are not sure, check the sketchbook location in your Processing
13 | # application preferences.
14 | # ${user.home} points the compiler to your home directory.
15 | # For windows the default path to your sketchbook would be
16 | # ${user.home}/My Documents/Processing (make adjustments below)
17 |
18 | #sketchbook.location=${user.home}/My Documents/Processing
19 | sketchbook.location=${user.home}/Desktop/edu/src/processing
20 |
21 |
22 | # Where are the jar files located that are required for compiling your Library
23 | # such as e.g. core.jar?
24 | # By default the local classpath location points to folder libs inside Eclipse's
25 | # workspace (by default found in your home directory).
26 | # For Windows, the default path would be
27 | # ${user.home}/Documents/workspace/libs (make adjustments below)
28 | # For OS X,the following path will direct you into Processing's application
29 | # package, in case you put Processing inside your Applications folder.
30 |
31 | #classpath.local.location=${user.home}/Documents/workspace/libs
32 | classpath.local.location=/usr/share/processing/core/library
33 |
34 |
35 | # Add all jar files that are required for compiling your project to the local
36 | # and project classpath. Use a comma as delimiter. These jar files must be
37 | # inside your classpath.local.location folder.
38 |
39 | classpath.local.include=core.jar
40 |
41 |
42 | # Add Processing's libraries folder to the classpath.
43 | # If you don't need to include the libraries folder to your classpath, comment
44 | # out the following line.
45 |
46 | classpath.libraries.location=${sketchbook.location}/libraries
47 |
48 |
49 | # Set the java version that should be used to compile your Library.
50 |
51 | java.target.version=1.8
52 |
53 |
54 | # Set the description of the Ant build.xml file.
55 |
56 | ant.description=Processing Library Ant build file.
57 |
58 |
59 | # Give your Library a name. The name must not contain spaces or special
60 | # characters.
61 |
62 | project.name=VideoExport
63 |
64 |
65 | # The name as the user will see it. This can contain spaces and special
66 | # characters.
67 |
68 | project.prettyName=Video Export
69 |
70 |
71 | # Use 'normal' or 'fast' as value for project.compile.
72 | # 'fast' will only compile the project into your sketchbook.
73 | # 'normal' will compile the distribution including the javadoc-reference and all
74 | # web-files (the compile process here takes longer).
75 | # All files compiled with project.compile=normal are stored in the distribution
76 | # folder.
77 |
78 | project.compile=normal
79 |
80 |
81 | # Set your name and URL, used for the web page and properties file.
82 |
83 | author.name=Abe Pazos
84 | author.url=http://hamoid.com
85 |
86 |
87 | # Set the web page for your Library.
88 | # This is NOT a direct link to where to download it.
89 |
90 | library.url=https://funprogramming.org/VideoExport-for-Processing
91 |
92 |
93 | # Set the category (or categories) of your Library from the following list:
94 | # "3D" "Animation" "Compilations" "Data"
95 | # "Fabrication" "Geometry" "GUI" "Hardware"
96 | # "I/O" "Language" "Math" "Simulation"
97 | # "Sound" "Utilities" "Typography" "Video & Vision"
98 | #
99 | # If a value other than those listed is used, your library will listed as
100 | # "Other". Many categories must be comma-separated.
101 |
102 | library.categories=I/O,Video & Vision
103 |
104 |
105 | # A short sentence (or fragment) to summarize the Library's function. This will
106 | # be shown from inside the PDE when the Library is being installed. Avoid
107 | # repeating the name of your Library here. Also, avoid saying anything redundant
108 | # like mentioning that it's a Library. This should start with a capitalized
109 | # letter, and end with a period.
110 |
111 | library.sentence=Simple video file exporter.
112 |
113 |
114 | # Additional information suitable for the Processing website. The value of
115 | # 'sentence' always will be prepended, so you should start by writing the
116 | # second sentence here. If your Library only works on certain operating systems,
117 | # mention it here.
118 |
119 | library.paragraph=Create movies you can share online.
120 |
121 |
122 | # Set the source code repository for your project.
123 | # We recommend Bitbucket (https://bitbucket.org) or GitHub (https://github.com).
124 |
125 | source.host=GitHub
126 | source.url=https://github.com/hamoid/VideoExport-for-Processing
127 | source.repository=https://github.com/hamoid/VideoExport-for-Processing.git
128 |
129 |
130 | # The current version of your Library.
131 | # This number must be parsable as an int. It increments once with each release.
132 | # This is used to compare different versions of the same Library, and check if
133 | # an update is available.
134 |
135 | library.version=23
136 |
137 |
138 | # The version as the user will see it.
139 |
140 | library.prettyVersion=0.2.3
141 |
142 |
143 | # The min and max revision of Processing compatible with your Library.
144 | # Note that these fields use the revision and not the version of Processing,
145 | # parsable as an int. For example, the revision number for 2.2.1 is 227.
146 | # You can find the revision numbers in the change log: https://raw.githubusercontent.com/processing/processing/master/build/shared/revisions.txt
147 | # Only use maxRevision (or minRevision), when your library is known to
148 | # break in a later (or earlier) release. Otherwise, use the default value 0.
149 |
150 | compatible.minRevision=233
151 | compatible.maxRevision=0
152 |
153 |
154 | # The platforms and Processing version that the Library has been tested
155 | # against. This information is only used in the generated webpage.
156 |
157 | tested.platform=osx,linux,windows
158 | tested.processingVersion=3.3.6
159 |
160 |
161 | # Additional information for the generated webpage.
162 |
163 | library.copyright=(c) 2017
164 | library.dependencies=ffmpeg
165 | library.keywords=video,export,mp4
166 |
167 |
168 | # Include javadoc references into your project's javadocs.
169 |
170 | javadoc.java.href=http://docs.oracle.com/javase/8/docs/api/
171 | javadoc.processing.href=http://processing.github.io/processing-javadocs/core/
172 |
--------------------------------------------------------------------------------
/resources/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | ${ant.description}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | ${line}
82 | Building the Processing Library ${project.name} ${library.version}
83 | ${line}
84 | src path ${project.src}
85 | bin path ${project.bin}
86 | classpath.local ${classpath.local.location}
87 | sketchbook ${sketchbook.location}
88 | java version ${java.target.version}
89 | ${line}
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 | ${exampleDir}
343 |
349 |
350 |
356 |
357 |
358 |
359 |
360 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 | ${line}
379 | Name ${project.name}
380 | Version ${library.prettyVersion} (${library.version})
381 | Compiled ${project.compile}
382 | Sketchbook ${sketchbook.location}
383 | ${line}
384 | done, finished.
385 | ${line}
386 |
387 |
388 |
389 |
390 |
391 |
--------------------------------------------------------------------------------
/resources/code/ExampleTaglet.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hamoid/video_export_processing/cf51bb6f949f1fd1713284972a302df7e928a7d1/resources/code/ExampleTaglet.class
--------------------------------------------------------------------------------
/resources/code/ExampleTaglet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved.
3 | *
4 | * Redistribution and use in source and binary forms, with or
5 | * without modification, are permitted provided that the following
6 | * conditions are met:
7 | *
8 | * -Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | *
11 | * -Redistribution in binary form must reproduce the above copyright
12 | * notice, this list of conditions and the following disclaimer in
13 | * the documentation and/or other materials provided with the
14 | * distribution.
15 | *
16 | * Neither the name of Sun Microsystems, Inc. or the names of
17 | * contributors may be used to endorse or promote products derived
18 | * from this software without specific prior written permission.
19 | *
20 | * This software is provided "AS IS," without a warranty of any
21 | * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
22 | * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
23 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
24 | * EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY
25 | * DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR
26 | * RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR
27 | * ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE
28 | * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT,
29 | * SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
30 | * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF
31 | * THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN
32 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
33 | *
34 | * You acknowledge that Software is not designed, licensed or
35 | * intended for use in the design, construction, operation or
36 | * maintenance of any nuclear facility.
37 | */
38 |
39 | import com.sun.tools.doclets.Taglet;
40 | import com.sun.javadoc.*;
41 | import java.util.Map;
42 | import java.io.*;
43 | /**
44 | * A sample Taglet representing @example. This tag can be used in any kind of
45 | * {@link com.sun.javadoc.Doc}. It is not an inline tag. The text is displayed
46 | * in yellow to remind the developer to perform a task. For
47 | * example, "@example Hello" would be shown as:
48 | *
49 | *
50 | * To Do:
51 | *
Fix this!
52 | *
53 | *
54 | *
55 | * @author Jamie Ho
56 | * @since 1.4
57 | */
58 |
59 | public class ExampleTaglet implements Taglet {
60 |
61 | private static final String NAME = "example";
62 | private static final String HEADER = "example To Do:";
63 |
64 | /**
65 | * Return the name of this custom tag.
66 | */
67 | public String getName() {
68 | return NAME;
69 | }
70 |
71 | /**
72 | * Will return true since @example
73 | * can be used in field documentation.
74 | * @return true since @example
75 | * can be used in field documentation and false
76 | * otherwise.
77 | */
78 | public boolean inField() {
79 | return true;
80 | }
81 |
82 | /**
83 | * Will return true since @example
84 | * can be used in constructor documentation.
85 | * @return true since @example
86 | * can be used in constructor documentation and false
87 | * otherwise.
88 | */
89 | public boolean inConstructor() {
90 | return true;
91 | }
92 |
93 | /**
94 | * Will return true since @example
95 | * can be used in method documentation.
96 | * @return true since @example
97 | * can be used in method documentation and false
98 | * otherwise.
99 | */
100 | public boolean inMethod() {
101 | return true;
102 | }
103 |
104 | /**
105 | * Will return true since @example
106 | * can be used in method documentation.
107 | * @return true since @example
108 | * can be used in overview documentation and false
109 | * otherwise.
110 | */
111 | public boolean inOverview() {
112 | return true;
113 | }
114 |
115 | /**
116 | * Will return true since @example
117 | * can be used in package documentation.
118 | * @return true since @example
119 | * can be used in package documentation and false
120 | * otherwise.
121 | */
122 | public boolean inPackage() {
123 | return true;
124 | }
125 |
126 | /**
127 | * Will return true since @example
128 | * can be used in type documentation (classes or interfaces).
129 | * @return true since @example
130 | * can be used in type documentation and false
131 | * otherwise.
132 | */
133 | public boolean inType() {
134 | return true;
135 | }
136 |
137 | /**
138 | * Will return false since @example
139 | * is not an inline tag.
140 | * @return false since @example
141 | * is not an inline tag.
142 | */
143 |
144 | public boolean isInlineTag() {
145 | return false;
146 | }
147 |
148 | /**
149 | * Register this Taglet.
150 | * @param tagletMap the map to register this tag to.
151 | */
152 | public static void register(Map tagletMap) {
153 | ExampleTaglet tag = new ExampleTaglet();
154 | Taglet t = (Taglet) tagletMap.get(tag.getName());
155 | if (t != null) {
156 | tagletMap.remove(tag.getName());
157 | }
158 | tagletMap.put(tag.getName(), tag);
159 | }
160 |
161 | /**
162 | * Given the Tag representation of this custom
163 | * tag, return its string representation.
164 | * @param tag the Tag representation of this custom tag.
165 | */
166 | public String toString(Tag tag) {
167 | return createHTML(readFile(tag.text()));
168 | }
169 |
170 |
171 | /**
172 | * Given an array of Tags representing this custom
173 | * tag, return its string representation.
174 | * @param tags the array of Tags representing of this custom tag.
175 | */
176 | public String toString(Tag[] tags) {
177 | if (tags.length == 0) {
178 | return null;
179 | }
180 | return createHTML(readFile(tags[0].text()));
181 | }
182 |
183 |
184 |
185 | String createHTML(String theString) {
186 | if(theString!=null) {
187 | String dd = "";
193 |
194 | return dd+"\n
" +
195 | "
+Example
" +
196 | "
"+theString+"
" +
197 | "
";
198 | }
199 | return "";
200 | }
201 |
202 |
203 | /**
204 | * check if the examples directory exists and return the example as given in the tag.
205 | * @param theExample the name of the example
206 | */
207 | String readFile(String theExample) {
208 | String record = "";
209 | String myResult = "";
210 | int recCount = 0;
211 | String myDir = "../examples";
212 | File file=new File(myDir);
213 | if(file.exists()==false) {
214 | myDir = "./examples";
215 | }
216 | try {
217 | FileReader fr = new FileReader(myDir+"/"+theExample+"/"+theExample+".pde");
218 | BufferedReader br = new BufferedReader(fr);
219 | record = new String();
220 | while ((record = br.readLine()) != null) {
221 | myResult += record+"\n";
222 | }
223 | } catch (IOException e) {
224 | System.out.println(e);
225 | return null;
226 | }
227 | return myResult;
228 | }
229 | }
230 |
231 |
232 |
--------------------------------------------------------------------------------
/resources/code/ant-contrib-1.0b3.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hamoid/video_export_processing/cf51bb6f949f1fd1713284972a302df7e928a7d1/resources/code/ant-contrib-1.0b3.jar
--------------------------------------------------------------------------------
/resources/code/doc.sh:
--------------------------------------------------------------------------------
1 | # a shell script to create a java documentation
2 | # for a processing Library.
3 | #
4 | # make changes to the variables below so they
5 | # fit the structure of your Library
6 |
7 | # the package name of your Library
8 | package=template;
9 |
10 | # source folder location
11 | src=../src;
12 |
13 | # the destination folder of your documentation
14 | dest=../documentation;
15 |
16 |
17 | # compile the java documentation
18 | javadoc -d $dest -stylesheetfile ./stylesheet.css -sourcepath ${src} ${package}
19 |
--------------------------------------------------------------------------------
/resources/library.properties:
--------------------------------------------------------------------------------
1 | # More on this file here: https://github.com/processing/processing/wiki/Library-Basics
2 | # UTF-8 supported.
3 |
4 | # The name of your Library as you want it formatted.
5 | name = ##library.name##
6 |
7 | # List of authors. Links can be provided using the syntax [author name](url).
8 | authors = [##author.name##](##author.url##)
9 |
10 | # A web page for your Library, NOT a direct link to where to download it.
11 | url = ##library.url##
12 |
13 | # The category (or categories) of your Library, must be from the following list:
14 | # "3D" "Animation" "Compilations" "Data"
15 | # "Fabrication" "Geometry" "GUI" "Hardware"
16 | # "I/O" "Language" "Math" "Simulation"
17 | # "Sound" "Utilities" "Typography" "Video & Vision"
18 | #
19 | # If a value other than those listed is used, your library will listed as
20 | # "Other". Many categories must be comma-separated.
21 | categories = ##library.categories##
22 |
23 | # A short sentence (or fragment) to summarize the Library's function. This will
24 | # be shown from inside the PDE when the Library is being installed. Avoid
25 | # repeating the name of your Library here. Also, avoid saying anything redundant
26 | # like mentioning that it's a Library. This should start with a capitalized
27 | # letter, and end with a period.
28 | sentence = ##library.sentence##
29 |
30 | # Additional information suitable for the Processing website. The value of
31 | # 'sentence' always will be prepended, so you should start by writing the
32 | # second sentence here. If your Library only works on certain operating systems,
33 | # mention it here.
34 | paragraph = ##library.paragraph##
35 |
36 | # Links in the 'sentence' and 'paragraph' attributes can be inserted using the
37 | # same syntax as for authors.
38 | # That is, [here is a link to Processing](http://processing.org/)
39 |
40 | # A version number that increments once with each release. This is used to
41 | # compare different versions of the same Library, and check if an update is
42 | # available. You should think of it as a counter, counting the total number of
43 | # releases you've had.
44 | version = ##library.version## # This must be parsable as an int
45 |
46 | # The version as the user will see it. If blank, the version attribute will be
47 | # used here. This should be a single word, with no spaces.
48 | prettyVersion = ##library.prettyVersion## # This is treated as a String
49 |
50 | # The min and max revision of Processing compatible with your Library.
51 | # Note that these fields use the revision and not the version of Processing,
52 | # parsable as an int. For example, the revision number for 2.2.1 is 227.
53 | # You can find the revision numbers in the change log: https://raw.githubusercontent.com/processing/processing/master/build/shared/revisions.txt
54 | # Only use maxRevision (or minRevision), when your library is known to
55 | # break in a later (or earlier) release. Otherwise, use the default value 0.
56 | minRevision = ##compatible.minRevision##
57 | maxRevision = ##compatible.maxRevision##
58 |
--------------------------------------------------------------------------------
/resources/stylesheet.css:
--------------------------------------------------------------------------------
1 | /* Javadoc style sheet */
2 | /*
3 | Overall document style
4 | */
5 | body {
6 | background-color:#ffffff;
7 | color:#353833;
8 | font-family:Arial, Helvetica, sans-serif;
9 | font-size:76%;
10 | margin:0;
11 | }
12 | a:link, a:visited {
13 | text-decoration:none;
14 | color:#4c6b87;
15 | }
16 | a:hover, a:focus {
17 | text-decoration:none;
18 | color:#bb7a2a;
19 | }
20 | a:active {
21 | text-decoration:none;
22 | color:#4c6b87;
23 | }
24 | a[name] {
25 | color:#353833;
26 | }
27 | a[name]:hover {
28 | text-decoration:none;
29 | color:#353833;
30 | }
31 | pre {
32 | font-size:1.3em;
33 | }
34 | h1 {
35 | font-size:1.8em;
36 | }
37 | h2 {
38 | font-size:1.5em;
39 | }
40 | h3 {
41 | font-size:1.4em;
42 | }
43 | h4 {
44 | font-size:1.3em;
45 | }
46 | h5 {
47 | font-size:1.2em;
48 | }
49 | h6 {
50 | font-size:1.1em;
51 | }
52 | ul {
53 | list-style-type:disc;
54 | }
55 | code, tt {
56 | font-size:1.2em;
57 | }
58 | dt code {
59 | font-size:1.2em;
60 | }
61 | table tr td dt code {
62 | font-size:1.2em;
63 | vertical-align:top;
64 | }
65 | sup {
66 | font-size:.6em;
67 | }
68 | /*
69 | Document title and Copyright styles
70 | */
71 | .clear {
72 | clear:both;
73 | height:0px;
74 | overflow:hidden;
75 | }
76 | .aboutLanguage {
77 | float:right;
78 | padding:0px 21px;
79 | font-size:.8em;
80 | z-index:200;
81 | margin-top:-7px;
82 | }
83 | .legalCopy {
84 | margin-left:.5em;
85 | }
86 | .bar a, .bar a:link, .bar a:visited, .bar a:active {
87 | color:#FFFFFF;
88 | text-decoration:none;
89 | }
90 | .bar a:hover, .bar a:focus {
91 | color:#bb7a2a;
92 | }
93 | .tab {
94 | background-color:#0066FF;
95 | background-image:url(resources/titlebar.gif);
96 | background-position:left top;
97 | background-repeat:no-repeat;
98 | color:#ffffff;
99 | padding:8px;
100 | width:5em;
101 | font-weight:bold;
102 | }
103 | /*
104 | Navigation bar styles
105 | */
106 | .bar {
107 | background-image:url(resources/background.gif);
108 | background-repeat:repeat-x;
109 | color:#FFFFFF;
110 | padding:.8em .5em .4em .8em;
111 | height:auto;/*height:1.8em;*/
112 | font-size:1em;
113 | margin:0;
114 | }
115 | .topNav {
116 | background-image:url(resources/background.gif);
117 | background-repeat:repeat-x;
118 | color:#FFFFFF;
119 | float:left;
120 | padding:0;
121 | width:100%;
122 | clear:right;
123 | height:2.8em;
124 | padding-top:10px;
125 | overflow:hidden;
126 | }
127 | .bottomNav {
128 | margin-top:10px;
129 | background-image:url(resources/background.gif);
130 | background-repeat:repeat-x;
131 | color:#FFFFFF;
132 | float:left;
133 | padding:0;
134 | width:100%;
135 | clear:right;
136 | height:2.8em;
137 | padding-top:10px;
138 | overflow:hidden;
139 | }
140 | .subNav {
141 | background-color:#dee3e9;
142 | border-bottom:1px solid #9eadc0;
143 | float:left;
144 | width:100%;
145 | overflow:hidden;
146 | }
147 | .subNav div {
148 | clear:left;
149 | float:left;
150 | padding:0 0 5px 6px;
151 | }
152 | ul.navList, ul.subNavList {
153 | float:left;
154 | margin:0 25px 0 0;
155 | padding:0;
156 | }
157 | ul.navList li{
158 | list-style:none;
159 | float:left;
160 | padding:3px 6px;
161 | }
162 | ul.subNavList li{
163 | list-style:none;
164 | float:left;
165 | font-size:90%;
166 | }
167 | .topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited {
168 | color:#FFFFFF;
169 | text-decoration:none;
170 | }
171 | .topNav a:hover, .bottomNav a:hover {
172 | text-decoration:none;
173 | color:#bb7a2a;
174 | }
175 | .navBarCell1Rev {
176 | background-image:url(resources/tab.gif);
177 | background-color:#a88834;
178 | color:#FFFFFF;
179 | margin: auto 5px;
180 | border:1px solid #c9aa44;
181 | }
182 | /*
183 | Page header and footer styles
184 | */
185 | .header, .footer {
186 | clear:both;
187 | margin:0 20px;
188 | padding:5px 0 0 0;
189 | }
190 | .indexHeader {
191 | margin:10px;
192 | position:relative;
193 | }
194 | .indexHeader h1 {
195 | font-size:1.3em;
196 | }
197 | .title {
198 | color:#2c4557;
199 | margin:10px 0;
200 | }
201 | .subTitle {
202 | margin:5px 0 0 0;
203 | }
204 | .header ul {
205 | margin:0 0 25px 0;
206 | padding:0;
207 | }
208 | .footer ul {
209 | margin:20px 0 5px 0;
210 | }
211 | .header ul li, .footer ul li {
212 | list-style:none;
213 | font-size:1.2em;
214 | }
215 | /*
216 | Heading styles
217 | */
218 | div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 {
219 | background-color:#dee3e9;
220 | border-top:1px solid #9eadc0;
221 | border-bottom:1px solid #9eadc0;
222 | margin:0 0 6px -8px;
223 | padding:2px 5px;
224 | }
225 | ul.blockList ul.blockList ul.blockList li.blockList h3 {
226 | background-color:#dee3e9;
227 | border-top:1px solid #9eadc0;
228 | border-bottom:1px solid #9eadc0;
229 | margin:0 0 6px -8px;
230 | padding:2px 5px;
231 | }
232 | ul.blockList ul.blockList li.blockList h3 {
233 | padding:0;
234 | margin:15px 0;
235 | }
236 | ul.blockList li.blockList h2 {
237 | padding:0px 0 20px 0;
238 | }
239 | /*
240 | Page layout container styles
241 | */
242 | .contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer {
243 | clear:both;
244 | padding:10px 20px;
245 | position:relative;
246 | }
247 | .indexContainer {
248 | margin:10px;
249 | position:relative;
250 | font-size:1.0em;
251 | }
252 | .indexContainer h2 {
253 | font-size:1.1em;
254 | padding:0 0 3px 0;
255 | }
256 | .indexContainer ul {
257 | margin:0;
258 | padding:0;
259 | }
260 | .indexContainer ul li {
261 | list-style:none;
262 | }
263 | .contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt {
264 | font-size:1.1em;
265 | font-weight:bold;
266 | margin:10px 0 0 0;
267 | color:#4E4E4E;
268 | }
269 | .contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd {
270 | margin:10px 0 10px 20px;
271 | }
272 | .serializedFormContainer dl.nameValue dt {
273 | margin-left:1px;
274 | font-size:1.1em;
275 | display:inline;
276 | font-weight:bold;
277 | }
278 | .serializedFormContainer dl.nameValue dd {
279 | margin:0 0 0 1px;
280 | font-size:1.1em;
281 | display:inline;
282 | }
283 | /*
284 | List styles
285 | */
286 | ul.horizontal li {
287 | display:inline;
288 | font-size:0.9em;
289 | }
290 | ul.inheritance {
291 | margin:0;
292 | padding:0;
293 | }
294 | ul.inheritance li {
295 | display:inline;
296 | list-style:none;
297 | }
298 | ul.inheritance li ul.inheritance {
299 | margin-left:15px;
300 | padding-left:15px;
301 | padding-top:1px;
302 | }
303 | ul.blockList, ul.blockListLast {
304 | margin:10px 0 10px 0;
305 | padding:0;
306 | }
307 | ul.blockList li.blockList, ul.blockListLast li.blockList {
308 | list-style:none;
309 | margin-bottom:25px;
310 | }
311 | ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList {
312 | padding:0px 20px 5px 10px;
313 | border:1px solid #9eadc0;
314 | background-color:#f9f9f9;
315 | }
316 | ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList {
317 | padding:0 0 5px 8px;
318 | background-color:#ffffff;
319 | border:1px solid #9eadc0;
320 | border-top:none;
321 | }
322 | ul.blockList ul.blockList ul.blockList ul.blockList li.blockList {
323 | margin-left:0;
324 | padding-left:0;
325 | padding-bottom:15px;
326 | border:none;
327 | border-bottom:1px solid #9eadc0;
328 | }
329 | ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast {
330 | list-style:none;
331 | border-bottom:none;
332 | padding-bottom:0;
333 | }
334 | table tr td dl, table tr td dl dt, table tr td dl dd {
335 | margin-top:0;
336 | margin-bottom:1px;
337 | }
338 | /*
339 | Table styles
340 | */
341 | .contentContainer table, .classUseContainer table, .constantValuesContainer table {
342 | border-bottom:1px solid #9eadc0;
343 | width:100%;
344 | }
345 | .contentContainer ul li table, .classUseContainer ul li table, .constantValuesContainer ul li table {
346 | width:100%;
347 | }
348 | .contentContainer .description table, .contentContainer .details table {
349 | border-bottom:none;
350 | }
351 | .contentContainer ul li table th.colOne, .contentContainer ul li table th.colFirst, .contentContainer ul li table th.colLast, .classUseContainer ul li table th, .constantValuesContainer ul li table th, .contentContainer ul li table td.colOne, .contentContainer ul li table td.colFirst, .contentContainer ul li table td.colLast, .classUseContainer ul li table td, .constantValuesContainer ul li table td{
352 | vertical-align:top;
353 | padding-right:20px;
354 | }
355 | .contentContainer ul li table th.colLast, .classUseContainer ul li table th.colLast,.constantValuesContainer ul li table th.colLast,
356 | .contentContainer ul li table td.colLast, .classUseContainer ul li table td.colLast,.constantValuesContainer ul li table td.colLast,
357 | .contentContainer ul li table th.colOne, .classUseContainer ul li table th.colOne,
358 | .contentContainer ul li table td.colOne, .classUseContainer ul li table td.colOne {
359 | padding-right:3px;
360 | }
361 | .overviewSummary caption, .packageSummary caption, .contentContainer ul.blockList li.blockList caption, .summary caption, .classUseContainer caption, .constantValuesContainer caption {
362 | position:relative;
363 | text-align:left;
364 | background-repeat:no-repeat;
365 | color:#FFFFFF;
366 | font-weight:bold;
367 | clear:none;
368 | overflow:hidden;
369 | padding:0px;
370 | margin:0px;
371 | }
372 | caption a:link, caption a:hover, caption a:active, caption a:visited {
373 | color:#FFFFFF;
374 | }
375 | .overviewSummary caption span, .packageSummary caption span, .contentContainer ul.blockList li.blockList caption span, .summary caption span, .classUseContainer caption span, .constantValuesContainer caption span {
376 | white-space:nowrap;
377 | padding-top:8px;
378 | padding-left:8px;
379 | display:block;
380 | float:left;
381 | background-image:url(resources/titlebar.gif);
382 | height:18px;
383 | }
384 | .overviewSummary .tabEnd, .packageSummary .tabEnd, .contentContainer ul.blockList li.blockList .tabEnd, .summary .tabEnd, .classUseContainer .tabEnd, .constantValuesContainer .tabEnd {
385 | width:10px;
386 | background-image:url(resources/titlebar_end.gif);
387 | background-repeat:no-repeat;
388 | background-position:top right;
389 | position:relative;
390 | float:left;
391 | }
392 | ul.blockList ul.blockList li.blockList table {
393 | margin:0 0 12px 0px;
394 | width:100%;
395 | }
396 | .tableSubHeadingColor {
397 | background-color: #EEEEFF;
398 | }
399 | .altColor {
400 | background-color:#eeeeef;
401 | }
402 | .rowColor {
403 | background-color:#ffffff;
404 | }
405 | .overviewSummary td, .packageSummary td, .contentContainer ul.blockList li.blockList td, .summary td, .classUseContainer td, .constantValuesContainer td {
406 | text-align:left;
407 | padding:3px 3px 3px 7px;
408 | }
409 | th.colFirst, th.colLast, th.colOne, .constantValuesContainer th {
410 | background:#dee3e9;
411 | border-top:1px solid #9eadc0;
412 | border-bottom:1px solid #9eadc0;
413 | text-align:left;
414 | padding:3px 3px 3px 7px;
415 | }
416 | td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover {
417 | font-weight:bold;
418 | }
419 | td.colFirst, th.colFirst {
420 | border-left:1px solid #9eadc0;
421 | white-space:nowrap;
422 | }
423 | td.colLast, th.colLast {
424 | border-right:1px solid #9eadc0;
425 | }
426 | td.colOne, th.colOne {
427 | border-right:1px solid #9eadc0;
428 | border-left:1px solid #9eadc0;
429 | }
430 | table.overviewSummary {
431 | padding:0px;
432 | margin-left:0px;
433 | }
434 | table.overviewSummary td.colFirst, table.overviewSummary th.colFirst,
435 | table.overviewSummary td.colOne, table.overviewSummary th.colOne {
436 | width:25%;
437 | vertical-align:middle;
438 | }
439 | table.packageSummary td.colFirst, table.overviewSummary th.colFirst {
440 | width:25%;
441 | vertical-align:middle;
442 | }
443 | /*
444 | Content styles
445 | */
446 | .description pre {
447 | margin-top:0;
448 | }
449 | .deprecatedContent {
450 | margin:0;
451 | padding:10px 0;
452 | }
453 | .docSummary {
454 | padding:0;
455 | }
456 | /*
457 | Formatting effect styles
458 | */
459 | .sourceLineNo {
460 | color:green;
461 | padding:0 30px 0 0;
462 | }
463 | h1.hidden {
464 | visibility:hidden;
465 | overflow:hidden;
466 | font-size:.9em;
467 | }
468 | .block {
469 | display:block;
470 | margin:3px 0 0 0;
471 | }
472 | .strong {
473 | font-weight:bold;
474 | }
475 |
--------------------------------------------------------------------------------
/src/com/hamoid/VideoExport.java:
--------------------------------------------------------------------------------
1 | /**
2 | * ##library.name##
3 | * ##library.sentence##
4 | * ##library.url##
5 | *
6 | * Copyright ##copyright## ##author##
7 | *
8 | * This library is free software; you can redistribute it and/or
9 | * modify it under the terms of the GNU Lesser General Public
10 | * License as published by the Free Software Foundation; either
11 | * version 2.1 of the License, or (at your option) any later version.
12 | *
13 | * This library is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 | * Lesser General Public License for more details.
17 | *
18 | * You should have received a copy of the GNU Lesser General
19 | * Public License along with this library; if not, write to the
20 | * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
21 | * Boston, MA 02111-1307 USA
22 | *
23 | * @author ##author##
24 | * @modified ##date##
25 | * @version ##library.prettyVersion## (##library.version##)
26 | */
27 |
28 | package com.hamoid;
29 |
30 | import java.io.BufferedReader;
31 | import java.io.File;
32 | import java.io.IOException;
33 | import java.io.InputStreamReader;
34 | import java.io.OutputStream;
35 | import java.net.URISyntaxException;
36 | import java.util.regex.Matcher;
37 | import java.util.regex.Pattern;
38 |
39 | import javax.swing.JOptionPane;
40 |
41 | import com.sun.jna.platform.win32.Kernel32;
42 | import com.sun.jna.platform.win32.Wincon;
43 |
44 | import processing.core.PApplet;
45 | import processing.core.PConstants;
46 | import processing.core.PImage;
47 | import processing.data.JSONArray;
48 | import processing.data.JSONObject;
49 | import processing.data.StringList;
50 |
51 | /**
52 | * @example basic
53 | */
54 |
55 | public class VideoExport {
56 |
57 | public final static String VERSION = "##library.prettyVersion##";
58 | protected final static String SETTINGS_FFMPEG_PATH = "ffmpeg_path";
59 | protected final static String SETTINGS_CMD_ENCODE_VIDEO = "encode_video";
60 | protected final static String SETTINGS_CMD_ENCODE_AUDIO = "encode_audio";
61 | protected final static String SETTINGS_CMD_ENCODE_VIDEO_AUDIO_URL = "encode_video_audio_url";
62 | protected final static String FFMPEG_PATH_UNSET = "ffmpeg_path_unset";
63 | protected static JSONArray CMD_ENCODE_AUDIO_DEFAULT;
64 | protected static JSONArray CMD_ENCODE_VIDEO_DEFAULT;
65 | protected static JSONArray CMD_ENCODE_VIDEO_AUDIO_URL_DEFAULT;
66 | protected final String ffmpegMetadataComment = "Made with " +
67 | "Video Export for Processing - https://git.io/vAXLk";
68 | protected ProcessBuilder processBuilder;
69 | protected Process process;
70 | protected byte[] pixelsByte = null;
71 | protected int frameCount;
72 | protected boolean loadPixelsEnabled = true;
73 | protected boolean saveDebugInfo = true;
74 | protected String outputFilePath;
75 | protected String audioFilePath;
76 | protected String audioUrl = null;
77 | protected PImage img;
78 | protected PApplet parent;
79 | protected int ffmpegCrfQuality;
80 | protected int ffmpegAudioBitRate;
81 | protected float ffmpegFrameRate;
82 | protected boolean ffmpegFound = false;
83 | protected File ffmpegOutputLog;
84 | protected OutputStream ffmpeg;
85 | protected JSONObject settings;
86 | protected String settingsPath;
87 |
88 | /**
89 | * Constructor, usually called in the setup() method in your sketch to
90 | * initialize and start the library.
91 | *
92 | * Using a default movie file name (processing-movie.mp4).
93 | *
94 | * @param parent
95 | * Pass "this" when constructing a VideoExport instance
96 | */
97 | public VideoExport(PApplet parent) {
98 | this(parent, "processing-movie.mp4", parent.g);
99 | }
100 |
101 | /**
102 | * Constructor that allows specifying a movie file name.
103 | *
104 | * @param parent
105 | * Parent PApplet, normally "this" when called from setup()
106 | * @param outputFileName
107 | * The name of the video file to produce, for instance
108 | * "beauty.mp4"
109 | * @example basic
110 | */
111 | public VideoExport(PApplet parent, String outputFileName) {
112 | this(parent, outputFileName, parent.g);
113 | }
114 |
115 | /**
116 | * Constructor that allows to set a PImage to export as video (advanced)
117 | *
118 | * @param parent
119 | * Parent PApplet, normally "this" when called from setup()
120 | * @param outputFileName
121 | * The name of the video file to produce, for instance
122 | * "beauty.mp4"
123 | * @param img
124 | * PImage object to export as video (can be a PGraphics, Movie,
125 | * Capture...)
126 | * @example usingPGraphics
127 | */
128 | public VideoExport(PApplet parent, final String outputFileName,
129 | PImage img) {
130 |
131 | parent.registerMethod("dispose", this);
132 |
133 | // // Disabling this, since it doesn't seem to help dispose()
134 | // // being called by Processing on shut down.
135 |
136 | // // dispose() workaround suggested at
137 | // // https://github.com/processing/processing/issues/4381
138 |
139 | // Runtime.getRuntime().addShutdownHook(new Thread() {
140 | // @Override public void run() { dispose(); }
141 | // });
142 |
143 | this.parent = parent;
144 | this.img = img;
145 |
146 | CMD_ENCODE_VIDEO_DEFAULT = new JSONArray(new StringList(
147 | new String[]{
148 | "[ffmpeg]", // ffmpeg executable
149 | "-y", // overwrite old file
150 | "-f", "rawvideo", // format rgb raw
151 | "-vcodec", "rawvideo", // in codec rgb raw
152 | "-s", "[width]x[height]", // size
153 | "-pix_fmt", "rgb24", // pix format rgb24
154 | "-r", "[fps]", // frame rate
155 | "-i", "-", // pipe input
156 | "-an", // no audio
157 | "-vcodec", "h264", // out codec h264
158 | "-pix_fmt", "yuv420p", // color space yuv420p
159 | "-crf", "[crf]", // quality
160 | "-metadata", "comment=[comment]", // comment
161 | "[output]" // output file
162 | }));
163 |
164 | CMD_ENCODE_VIDEO_AUDIO_URL_DEFAULT = new JSONArray(new StringList(
165 | new String[]{
166 | "[ffmpeg]", // ffmpeg executable
167 | "-y", // overwrite old file
168 | "-f", "rawvideo", // format rgb raw
169 | "-vcodec", "rawvideo", // in codec rgb raw
170 | "-s", "[width]x[height]", // size
171 | "-pix_fmt", "rgb24", // pix format rgb24
172 | "-r", "[fps]", // frame rate
173 | "-i", "-", // pipe input
174 | "-i", "[url]", // to get audio URL
175 | "-acodec", "aac", // audio codec
176 | "-vcodec", "h264", // out codec h264
177 | "-pix_fmt", "yuv420p", // color space yuv420p
178 | "-crf", "[crf]", // quality
179 | "-strict", "experimental", // audio url won't work without this
180 | "-metadata", "comment=[comment]", // comment
181 | "[output]" // output file
182 | }));
183 |
184 | CMD_ENCODE_AUDIO_DEFAULT = new JSONArray(new StringList(
185 | new String[]{
186 | "[ffmpeg]", // ffmpeg executable
187 | "-y", // overwrite old file
188 | "-i", "[inputvideo]", // video file path
189 | "-i", "[inputaudio]", // audio file path
190 | "-filter_complex", "[1:0]apad", // pad with silence
191 | "-shortest", // match shortest file
192 | "-vcodec", "copy", // don't reencode vid
193 | "-acodec", "aac", // aac audio encoding
194 | "-b:a", "[bitrate]k", // bit rate (quality)
195 | "-metadata", "comment=[comment]", // comment
196 | // https://stackoverflow.com/questions/28586397/ffmpeg-error-while-re-encoding-video#28587897
197 | "-strict", "-2", // enable aac
198 | "[output]" // output file
199 | }));
200 |
201 | // settings = Preferences.userNodeForPackage(this.getClass());
202 | // The Preferences object does not work on Windows 10:
203 | // it requires fiddling with the registry to add a missing key.
204 | // Therefore I decided to find the location of VideoExport.jar in the
205 | // disk, and create a settings file two folders above (which is the root
206 | // folder of this library)
207 | try {
208 | File thisJar = new File(VideoExport.class.getProtectionDomain()
209 | .getCodeSource().getLocation().toURI().getPath())
210 | .getParentFile().getParentFile();
211 | settingsPath = thisJar.getAbsolutePath() + File.separator
212 | + "settings.json";
213 | File settingsFile = new File(settingsPath);
214 | if (settingsFile.isFile()) {
215 | settings = parent.loadJSONObject(settingsPath);
216 |
217 | // Update config files from v0.2.2
218 | Object o;
219 |
220 | // If String, make it JSONArray
221 | o = settings.get(SETTINGS_CMD_ENCODE_VIDEO);
222 | if(o instanceof String) {
223 | settings.setJSONArray(SETTINGS_CMD_ENCODE_VIDEO,
224 | toJSONArray((String) o));
225 | }
226 | // If String, make it JSONArray
227 | o = settings.get(SETTINGS_CMD_ENCODE_AUDIO);
228 | if(o instanceof String) {
229 | settings.setJSONArray(SETTINGS_CMD_ENCODE_AUDIO,
230 | toJSONArray((String) o));
231 | }
232 | // If String, make it JSONArray
233 | o = settings.get(SETTINGS_CMD_ENCODE_VIDEO_AUDIO_URL);
234 | if(o instanceof String) {
235 | settings.setJSONArray(SETTINGS_CMD_ENCODE_VIDEO_AUDIO_URL,
236 | toJSONArray((String) o));
237 | }
238 | } else {
239 | settings = new JSONObject();
240 | settings.setJSONArray(SETTINGS_CMD_ENCODE_VIDEO,
241 | CMD_ENCODE_VIDEO_DEFAULT);
242 | settings.setJSONArray(SETTINGS_CMD_ENCODE_AUDIO,
243 | CMD_ENCODE_AUDIO_DEFAULT);
244 | settings.setJSONArray(SETTINGS_CMD_ENCODE_VIDEO_AUDIO_URL,
245 | CMD_ENCODE_VIDEO_AUDIO_URL_DEFAULT);
246 | }
247 | } catch (URISyntaxException e) {
248 | e.printStackTrace();
249 | System.err.println("Error loading settings.json");
250 | }
251 |
252 | outputFilePath = parent.sketchPath(outputFileName);
253 | ffmpegFrameRate = 30f;
254 | ffmpegCrfQuality = 15;
255 | ffmpegAudioBitRate = 128;
256 | }
257 |
258 | /**
259 | * Return the version of the library.
260 | *
261 | * @return String
262 | */
263 | public static String version() {
264 | return VERSION;
265 | }
266 |
267 | /**
268 | * Allow setting a new movie name, in case we want to export several movies,
269 | * one after the other.
270 | *
271 | * @param newMovieFileName
272 | * String with file name of the new movie to create
273 | */
274 | public void setMovieFileName(final String newMovieFileName) {
275 | outputFilePath = parent.sketchPath(newMovieFileName);
276 | }
277 |
278 | /**
279 | * Allow setting a url for audio file name to be used as input.
280 | *
281 | * @param audioFileName
282 | * String file name to be used as audio track for the movie.
283 | */
284 | public void setAudioFileName(final String audioFileName) {
285 | if (audioUrl != null) {
286 | System.err.println("Can't setAudioFileName() after setAudioUrl()!");
287 | return;
288 | }
289 | audioFilePath = parent.dataPath(audioFileName);
290 | }
291 |
292 | /**
293 | * Allow setting a url for audio input.
294 | *
295 | * @param audioUrl
296 | * String with url to be used as audio track for the movie.
297 | */
298 | public void setAudioUrl(final String audioUrl) {
299 | if (audioFilePath != null) {
300 | System.err.println("Can't setAudioUrl() after setAudioFileName()!");
301 | return;
302 | }
303 | this.audioUrl = audioUrl;
304 | }
305 |
306 | /**
307 | * Set the PImage element. Advanced use only. Optional.
308 | *
309 | * @param img
310 | * A PImage object. Probably used for off-screen exporting..
311 | */
312 | public void setGraphics(PImage img) {
313 | this.img = img;
314 | }
315 |
316 | /**
317 | * Set the quality of the produced video file. Optional.
318 | *
319 | * @param crf
320 | * Video quality. A value between 0 (high compression) and 100
321 | * (high quality, lossless). Default is 70.
322 | * @param audioBitRate
323 | * Audio quality (bit rate in kbps).
324 | * 128 is the default. 192 is very good.
325 | * More than 256 does not make sense.
326 | * Higher numbers produce heavier files.
327 | */
328 | public void setQuality(int crf, int audioBitRate) {
329 | if (ffmpeg != null) {
330 | System.err.println("Can't setQuality() after saveFrame()!");
331 | return;
332 | }
333 | if (crf > 100) {
334 | crf = 100;
335 | } else if (crf < 0) {
336 | crf = 0;
337 | }
338 | ffmpegCrfQuality = (100 - crf) / 2;
339 | ffmpegAudioBitRate = audioBitRate;
340 | }
341 |
342 | /**
343 | * Set the frame rate of the produced video file. Optional.
344 | *
345 | * @param frameRate
346 | * The frame rate at which the resulting video file should be
347 | * played. The default is 30, which is the recommended for online
348 | * video.
349 | */
350 | public void setFrameRate(float frameRate) {
351 | if (ffmpeg != null) {
352 | System.err.println("Can't setFrameRate() after saveFrame()!");
353 | return;
354 | }
355 | ffmpegFrameRate = frameRate;
356 | }
357 |
358 | /**
359 | * You can tell VideoExport not to call loadPixels() internally.
360 | * Use it only if you already call loadPixels() in your program.
361 | * Useful to avoid calling it twice, which might hurt the
362 | * performance a bit. Optional.
363 | *
364 | * @param doLoadPixels
365 | * Set to false to disable the internal loadPixels() call.
366 | */
367 | public void setLoadPixels(boolean doLoadPixels) {
368 | loadPixelsEnabled = doLoadPixels;
369 | }
370 |
371 | /**
372 | * Call this method to specify if you want a debug text file saved
373 | * together with the video file. The text file normally contains the
374 | * output messages from ffmpeg, which may be useful for diagnosing
375 | * problems. If video is being exported correctly you may want to
376 | * call videoExport.setDebugging(false) to avoid creating unnecessary
377 | * files. Optional.
378 | *
379 | * @param saveDebugFile
380 | * Set to false to disable saving the ffmpeg output in a text
381 | * file
382 | */
383 | public void setDebugging(boolean saveDebugFile) {
384 | saveDebugInfo = saveDebugFile;
385 | }
386 |
387 | /**
388 | * Advanced. You can use this if you want to change how ffmpeg behaves,
389 | * for example to add filters, custom encodings, or anything else you
390 | * could do with ffmpeg in the command line. See
391 | * https://forum.processing.org/two/discussion/22139/video-export-library-0-1-9
392 | * To find the default values find CMD_ENCODE_VIDEO_DEFAULT in
393 | * https://github.com/hamoid/video_export_processing/blob/master/src/com/hamoid/VideoExport.java
394 | *
395 | * @param newSettings An array with all the command line
396 | * arguments to call to produce a video.
397 | */
398 | public void setFfmpegVideoSettings(String[] newSettings) {
399 | settings.setJSONArray(SETTINGS_CMD_ENCODE_VIDEO,
400 | new JSONArray(new StringList(newSettings)));
401 | }
402 |
403 | /**
404 | * Advanced. You can use this if you want to change how ffmpeg behaves,
405 | * for example to add filters, custom encodings, or anything else you
406 | * could do with ffmpeg in the command line. See
407 | * https://forum.processing.org/two/discussion/22139/video-export-library-0-1-9
408 | * To find the default values find CMD_ENCODE_AUDIO_DEFAULT in
409 | * https://github.com/hamoid/video_export_processing/blob/master/src/com/hamoid/VideoExport.java
410 | *
411 | * @param newSettings An array with all the command line
412 | * arguments to call to produce a video.
413 | */
414 | public void setFfmpegAudioSettings(String[] newSettings) {
415 | settings.setJSONArray(SETTINGS_CMD_ENCODE_AUDIO,
416 | new JSONArray(new StringList(newSettings)));
417 | }
418 |
419 | /**
420 | * Adds one frame to the video file. The frame will be the content of the
421 | * display, or the content of a PImage if you specified one in the
422 | * constructor.
423 | */
424 | public void saveFrame() {
425 | if (img != null && img.width > 0) {
426 | if (!ffmpegFound) {
427 | return;
428 | }
429 | if (pixelsByte == null) {
430 | pixelsByte = new byte[img.pixelWidth * img.pixelHeight * 3];
431 | }
432 | if (loadPixelsEnabled) {
433 | img.loadPixels();
434 | }
435 |
436 | int byteNum = 0;
437 | for (final int px : img.pixels) {
438 | pixelsByte[byteNum++] = (byte) (px >> 16);
439 | pixelsByte[byteNum++] = (byte) (px >> 8);
440 | pixelsByte[byteNum++] = (byte) (px);
441 | }
442 |
443 | try {
444 | ffmpeg.write(pixelsByte);
445 | frameCount++;
446 | } catch (Exception e) {
447 | e.printStackTrace();
448 | err(ffmpegOutputLog);
449 | }
450 | }
451 | }
452 |
453 | /**
454 | * Make sure ffmpeg is found, then create a process
455 | * to run it.
456 | */
457 | protected void initialize() {
458 | // Get the saved ffmpeg path from the settings file
459 | // which maybe does not exist.
460 | String ffmpeg_path = getFfmpegPath();
461 | // If it did not exist, try to guess where it is
462 | if (ffmpeg_path.equals(FFMPEG_PATH_UNSET)) {
463 | String[] guess_paths = { "/usr/local/bin/ffmpeg",
464 | "/usr/bin/ffmpeg" };
465 | for (String guess_path : guess_paths) {
466 | if ((new File(guess_path)).isFile()) {
467 | ffmpeg_path = guess_path;
468 | settings.setString(SETTINGS_FFMPEG_PATH, ffmpeg_path);
469 | parent.saveJSONObject(settings, settingsPath);
470 | break;
471 | }
472 | }
473 | } else {
474 | // If it did exist in the settings file,
475 | // check if the path is still valid
476 | // (maybe the user moved ffmpeg to a different folder)
477 | File ffmpegFile = new File(ffmpeg_path);
478 | if (!ffmpegFile.isFile()) {
479 | ffmpeg_path = FFMPEG_PATH_UNSET;
480 | }
481 | }
482 | // If it was not set, or if it was moved, ask the user where
483 | // to find ffmpeg. We will try to start after the user makes
484 | // a decision and onFfmpegSelected() is called.
485 | if (ffmpeg_path.equals(FFMPEG_PATH_UNSET)) {
486 | JOptionPane.showMessageDialog(parent.frame,
487 | "The VideoExport library requires ffmpeg,\n"
488 | + "a free command line tool.\n\n"
489 | + "If you don't have ffmpeg yet:\n\n"
490 | + "-- Windows / Mac --\n"
491 | + "1. Download a static build from http://ffmpeg.org\n"
492 | + "2. Unzip it\n\n" + "-- Linux --\n"
493 | + "1. Install ffmpeg using your package manager\n\n"
494 | + "-- When you already have ffmpeg --\n"
495 | + "Click OK and select the ffmpeg or ffmpeg.exe program");
496 |
497 | // Show "select file" dialog
498 | parent.selectInput(
499 | "Please select the previously downloaded ffmpeg or ffmpeg.exe executable",
500 | "onFfmpegSelected", new File("/"), this);
501 | } else {
502 | // If it was found, all good. Start.
503 | startFfmpeg(ffmpeg_path);
504 | }
505 | }
506 |
507 | /**
508 | * Call this function to figure out how many frames your movie has so far.
509 | *
510 | * @return the number of frames added to the movie so far
511 | */
512 | public int getCurrentFrame() {
513 | return frameCount;
514 | }
515 |
516 | /**
517 | * You could use the returned value to display a time counter, a progress
518 | * bar or to create periodic motion, for instance by feeding
519 | * the returned value into the sin() function, and using the result to drive
520 | * the position of an object.
521 | *
522 | * @return the duration of the movie (so far) in seconds
523 | */
524 | public float getCurrentTime() {
525 | return frameCount / ffmpegFrameRate;
526 | }
527 |
528 | /**
529 | * Call this if you need to figure out the path to the ffmpeg program
530 | * (advanced).
531 | *
532 | * @return the path to the ffmpeg program as a String
533 | */
534 | public String getFfmpegPath() {
535 | return settings.getString(SETTINGS_FFMPEG_PATH, FFMPEG_PATH_UNSET);
536 | }
537 |
538 | /**
539 | * Call this method if you know the path to ffmpeg on your computer
540 | * (advanced).
541 | * @param path
542 | */
543 | public void setFfmpegPath(String path) {
544 | settings.setString(SETTINGS_FFMPEG_PATH, path);
545 | parent.saveJSONObject(settings, settingsPath);
546 | }
547 |
548 | /**
549 | * Makes the library forget about where the ffmpeg binary was located.
550 | * Useful if you moved ffmpeg to a different location. After calling this
551 | * function the library will ask you again for the location of ffmpeg.
552 | * Optional.
553 | */
554 | public void forgetFfmpegPath() {
555 | settings.setString(SETTINGS_FFMPEG_PATH, FFMPEG_PATH_UNSET);
556 | parent.saveJSONObject(settings, settingsPath);
557 | }
558 |
559 | /**
560 | * Called internally by the file selector when the user chooses
561 | * the location of ffmpeg on the disk.
562 | *
563 | * @param selection
564 | * (internal)
565 | */
566 | public void onFfmpegSelected(File selection) {
567 | if (selection == null) {
568 | System.err.println(
569 | "The VideoExport library requires ffmpeg but it was not found. "
570 | + "Please try again or read the library documentation.");
571 | } else {
572 | String ffmpeg_path = selection.getAbsolutePath();
573 | System.out.println("ffmpeg selected at " + ffmpeg_path);
574 | settings.setString(SETTINGS_FFMPEG_PATH, ffmpeg_path);
575 | parent.saveJSONObject(settings, settingsPath);
576 | startFfmpeg(ffmpeg_path);
577 | }
578 | }
579 |
580 | // ffmpeg -i input -c:v libx264 -crf 20 -maxrate 400k -bufsize 1835k
581 | // output.mp4 -profile:v baseline -level 3.0
582 | // https://trac.ffmpeg.org/wiki/Encode/H.264#Compatibility
583 | protected void startFfmpeg(String executable) {
584 | // -y = overwrite, otherwise it fails the second time you run
585 | // -an = no audio
586 | // "-b:v", "3000k" = video bit rate
587 | // "-i", "-" = pipe:0
588 |
589 | if (img.pixelWidth == 0 || img.pixelHeight == 0) {
590 | err("The export image size is 0!");
591 | }
592 |
593 | if (img.pixelWidth % 2 == 1 || img.pixelHeight % 2 == 1) {
594 | err("Width and height can only be even numbers when using the h264 encoder\n"
595 | + "but the requested image size is " + img.pixelWidth + "x"
596 | + img.pixelHeight);
597 | }
598 |
599 | // Get command as JSONArray
600 | JSONArray cmd;
601 | // use video cmd settings based on whether an input audio url is used, or
602 | // if audio we be added possibly from a file in attachSound()
603 | String cmdSelection = SETTINGS_CMD_ENCODE_VIDEO;
604 | if (audioUrl != null) {
605 | cmdSelection = SETTINGS_CMD_ENCODE_VIDEO_AUDIO_URL;
606 | }
607 | try {
608 | cmd = settings.getJSONArray(cmdSelection);
609 | } catch (RuntimeException e) {
610 | cmd = CMD_ENCODE_VIDEO_DEFAULT;
611 | }
612 | // JSONArray -> String[]
613 | String[] cmdArgs = cmd.getStringArray();
614 | // Replace variables. I first split, then replace (instead of
615 | // replace, then split) because the replacement may contain spaces.
616 | // For instance the comment would be split into parts and
617 | // break the command.
618 | for (int i = 0; i < cmdArgs.length; i++) {
619 | if (cmdArgs[i].contains("[")) {
620 | cmdArgs[i] = cmdArgs[i]
621 | .replace("[ffmpeg]", executable)
622 | .replace("[width]", "" + img.pixelWidth)
623 | .replace("[height]", "" + img.pixelHeight)
624 | .replace("[fps]", "" + ffmpegFrameRate)
625 | .replace("[url]", "" + audioUrl)
626 | .replace("[crf]", "" + ffmpegCrfQuality)
627 | .replace("[comment]", ffmpegMetadataComment)
628 | .replace("[output]", outputFilePath);
629 | }
630 |
631 | }
632 | processBuilder = new ProcessBuilder(cmdArgs);
633 | processBuilder.redirectErrorStream(true);
634 | ffmpegOutputLog = new File(parent.sketchPath("ffmpeg.txt"));
635 | processBuilder.redirectOutput(ffmpegOutputLog);
636 | processBuilder.redirectInput(ProcessBuilder.Redirect.PIPE);
637 | try {
638 | process = processBuilder.start();
639 | } catch (Exception e) {
640 | e.printStackTrace();
641 | err(ffmpegOutputLog);
642 | }
643 |
644 | ffmpeg = process.getOutputStream();
645 | ffmpegFound = true;
646 | frameCount = 0;
647 | }
648 |
649 | public void startMovie() {
650 | initialize();
651 | }
652 |
653 | /**
654 | * Called to end exporting a movie before exiting our program, or before
655 | * exporting a new movie.
656 | */
657 | public void endMovie() {
658 | if (ffmpeg != null) {
659 | try {
660 | ffmpeg.flush();
661 | ffmpeg.close();
662 | } catch (Exception e) {
663 | e.printStackTrace();
664 | }
665 | ffmpeg = null;
666 | }
667 | if (process != null) {
668 | try {
669 | // Delay to avoid creating corrupted video files.
670 | // Probably not useful: Thread.sleep(500);
671 | // Using a different approach now, by sending the
672 | // CTRL+C keys to ffmpeg if using Windows.
673 |
674 | if (PApplet.platform == PConstants.WINDOWS) {
675 | // Launch tasklist
676 | ProcessBuilder ps = new ProcessBuilder("tasklist");
677 | Process pr = ps.start();
678 |
679 | // Get all processes from tasklist
680 | BufferedReader allProcesses = new BufferedReader(
681 | new InputStreamReader(pr.getInputStream()));
682 | // Regex to find the word "ffmpeg.exe"
683 | Pattern isFfmpeg = Pattern
684 | .compile("ffmpeg\\.exe.*?([0-9]+)");
685 | String processDetails;
686 | // Iterate over all processes
687 | while ((processDetails = allProcesses.readLine()) != null) {
688 | Matcher m = isFfmpeg.matcher(processDetails);
689 | // Check if this process is ffmpeg.exe
690 | if (m.find()) {
691 | // If it is, send it CTRL+C to stop it
692 | Wincon wincon = Kernel32.INSTANCE;
693 | wincon.GenerateConsoleCtrlEvent(Wincon.CTRL_C_EVENT,
694 | Integer.parseInt(m.group(1)));
695 | break;
696 | }
697 | }
698 | } else {
699 | // In Linux and Mac tell the process to end
700 | process.destroy();
701 | }
702 |
703 | process.waitFor();
704 |
705 | if (audioFilePath != null && !audioFilePath.isEmpty()) {
706 | attachSound();
707 | audioFilePath = null;
708 | }
709 |
710 | if (!saveDebugInfo && ffmpegOutputLog.isFile()) {
711 | ffmpegOutputLog.delete();
712 | ffmpegOutputLog = null;
713 | }
714 |
715 | PApplet.println(outputFilePath, "saved.");
716 | } catch (InterruptedException e) {
717 | PApplet.println("Waiting for ffmpeg timed out!");
718 | e.printStackTrace();
719 | } catch (IOException e) {
720 | e.printStackTrace();
721 | }
722 | processBuilder = null;
723 | process = null;
724 | }
725 | }
726 |
727 | protected void attachSound() {
728 | // Add sound to outputFilePath (crop at shortest)
729 | // ffmpeg -i video.avi -i audio.mp3 -codec copy -shortest output.avi
730 |
731 | // if sound is shorter, pad with silence
732 | // ffmpeg -i videofile.mp4 -i audiofile.wav -filter_complex " [1:0] apad
733 | // " -shortest output.mp4
734 |
735 | // Fade in/out. Works with ffmpeg 2.5.2.
736 | // Fade in and fade out both for the duration of 3 seconds.
737 | // ffmpeg -i audio.mp3 -af 'afade=t=in:ss=0:d=3,afade=t=out:st=27:d=3'
738 | // out.mp3
739 |
740 | // Specify length in frames (-t for seconds)
741 | // If source audio is mp3 or aac, use acodec copy
742 | // ffmpeg -i videofile.mp4 -i audiofile.wav -vframes 260 -vcodec copy
743 | // -acodec copy output.mp4
744 |
745 | // Check if the sound file exists and is a regular file (not a
746 | // directory)
747 | File audioPath = new File(audioFilePath);
748 | if (!audioPath.exists() || !audioPath.isFile()) {
749 | System.err.println("The file " + audioFilePath
750 | + " was not found or is not a regular file.");
751 | return;
752 | }
753 |
754 | // Get command as JSONArray
755 | JSONArray cmd;
756 | try {
757 | cmd = settings.getJSONArray(SETTINGS_CMD_ENCODE_AUDIO);
758 | } catch (RuntimeException e) {
759 | cmd = CMD_ENCODE_AUDIO_DEFAULT;
760 | }
761 | // JSONArray -> String[]
762 | String[] cmdArgs = cmd.getStringArray();
763 |
764 | String tmpAudioFile = "temp-with-audio.mp4";
765 |
766 | // Replace variables. I first split, then replace (instead of
767 | // replace, then split) because the replacement may contain spaces.
768 | // For instance the comment would be split into parts and
769 | // break the command.
770 | for (int i = 0; i < cmdArgs.length; i++) {
771 | if (cmdArgs[i].contains("[")) {
772 | cmdArgs[i] = cmdArgs[i]
773 | .replace("[ffmpeg]", getFfmpegPath())
774 | .replace("[inputvideo]", outputFilePath)
775 | .replace("[inputaudio]", audioFilePath)
776 | .replace("[bitrate]", "" + ffmpegAudioBitRate)
777 | .replace("[comment]", ffmpegMetadataComment)
778 | .replace("[output]",
779 | parent.sketchFile(tmpAudioFile)
780 | .getAbsolutePath());
781 | }
782 | }
783 |
784 | processBuilder = new ProcessBuilder(cmdArgs);
785 | processBuilder.redirectErrorStream(true);
786 | File ffmpegOutputLogAudio = new File(
787 | parent.sketchPath("ffmpeg-audio.txt"));
788 | processBuilder.redirectOutput(ffmpegOutputLogAudio);
789 |
790 | try {
791 | process = processBuilder.start();
792 | } catch (IOException e) {
793 | e.printStackTrace();
794 | err(ffmpegOutputLogAudio);
795 | }
796 |
797 | if (process != null) {
798 | try {
799 | // wait until done
800 | process.waitFor();
801 | new File(outputFilePath).delete();
802 | parent.sketchFile(tmpAudioFile)
803 | .renameTo(new File(outputFilePath));
804 | } catch (InterruptedException e) {
805 | PApplet.println(
806 | "Waiting for ffmpeg while adding audio timed out!");
807 | e.printStackTrace();
808 | }
809 | }
810 | if (!saveDebugInfo && ffmpegOutputLogAudio.isFile()) {
811 | ffmpegOutputLogAudio.delete();
812 | }
813 | processBuilder = null;
814 | process = null;
815 |
816 | }
817 |
818 | /**
819 | * Called automatically by Processing to clean up before shut down
820 | */
821 | public void dispose() {
822 | endMovie();
823 | }
824 |
825 | private static JSONArray toJSONArray(String s) {
826 | return new JSONArray(new StringList(s.split(" ")));
827 | }
828 |
829 | protected void err(String msg) {
830 | System.err.println("\nVideoExport error: " + msg + "\n");
831 | System.exit(1);
832 | }
833 |
834 | protected void err(File f) {
835 | err("Ffmpeg failed. Study " + f + " for more details.");
836 | }
837 |
838 | }
839 |
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
Video Export allows you to easily create video files directly from Processing.
55 |
56 |
The video playback frame rate does not depend on the rendering frame rate. That means that even if your sketch runs slowly
57 | because of heavy computation, the resulting video will play back smoothly. It also means that you don't need to export to the video
58 | file every single frame you see on the display: you can toggle video exporting on and off as you wish, while the sketch runs.
59 | This allows you to export only the "good parts" of what you see (by pressing a key, for instance).
60 |
61 |
ffmpeg
62 |
63 |
You need to download and install ffmpeg on your system before you can use this library. Note that you might already have it installed! You can find out by typing ffmpeg or ffmpeg.exe in the terminal. If the program is not found:
64 |
65 |
GNU/Linux systems: use your favorite package manager.
When you start a Processing sketch that uses this library you may be asked to indicate the location of your ffmpeg executable. This may happen once per sketch.
73 |
74 |
75 | Contributed libraries are developed, documented, and maintained by members of the Processing community. Further directions are included with each library. For feedback and support, please post to the Discourse. We strongly encourage all libraries to be open source, but not all of them are.
76 |
77 |
78 |
79 |
80 |
81 |
82 |
Download
83 |
84 | Download ##library.name## version ##library.prettyVersion## (##library.version##) in
85 | .zip format.
86 |
87 |
Installation
88 |
89 | Unzip and put the extracted ##project.name## folder into the libraries folder of your Processing sketches. Reference and examples are included in the ##project.name## folder.
90 |
91 |
92 |
93 |
94 |
95 |
Keywords. ##library.keywords##
96 |
Reference. Have a look at the javadoc reference here. A copy of the reference is included in the .zip as well.
97 |
Source. The source code of ##library.name## is available at ##source.host##, and its repository can be browsed here.