Uses 108K of libraries. Click here for Pure Java version (16066L/121K).
1 | !7 |
2 | |
3 | module VideoSplicer > DynPrintLog { |
4 | S inputFile, outputFile; |
5 | S timestamps; |
6 | S originalLength, cutLength; |
7 | S ffmpegOptions = "", videoCodec = ""; |
8 | |
9 | int volumeThresholdPercent = 15; |
10 | S minSilence = "0.2", leadIn = "0.2", leadOut = "0.2"; |
11 | |
12 | transient double profileSamplingInterval = 0.05; // 50 ms |
13 | transient int audioWindowSize = iround(profileSamplingInterval*16000); |
14 | |
15 | transient int timeFieldMinWidth = 40; |
16 | transient ImageSurface isPreview; |
17 | transient JAmplitudeGraph graph; |
18 | transient float[] profile; |
19 | |
20 | visual jHandleFileDrop(voidfunc(File f) { setField(inputFile := f2s(f)) }, |
21 | northCenterAndSouthWithMargins( |
22 | vstackWithSpacing( |
23 | withLabel("Input video:", filePathInputWithBrowseButton(dm_textField inputFile(), onChoose := rThread loadVideo)), |
24 | withLabel("Output video:", filePathInputWithBrowseButton(dm_textField outputFile())), |
25 | centeredLine( |
26 | jbutton("Load video", rThread loadVideo), |
27 | jbutton("Find jump cuts", rThread autoJumpCuts), |
28 | jhspacer(10), |
29 | jbutton("Make video", rThread makeVideo)), |
30 | jCenteredSection("Jump Cut Parameters", centeredLineWithSpacing(20, |
31 | withLabelLeftAndRight("Volume threshold:", dm_spinner volumeThresholdPercent(0, 100), "%"), |
32 | withLabelLeftAndRight("Shortest silence to cut:", jMinWidth(timeFieldMinWidth, dm_centeredTextField minSilence()), "s"), |
33 | jline( |
34 | withLabel("Lead-in/out:", jMinWidth(timeFieldMinWidth, dm_centeredTextField leadIn())), |
35 | jlabel("/"), |
36 | withLabelToTheRight(jMinWidth(timeFieldMinWidth, dm_centeredTextField leadOut()), "s")) |
37 | ))), |
38 | jhsplit(0.2, |
39 | centerAndSouthWithMargin( |
40 | jLiveValueSection(dm_calculatedLiveValue(S, () -> "Timestamps (" + countLines(timestamps) + ")"), dm_textArea("timestamps")), |
41 | vstack( |
42 | rightAlignedLine(withLabel("Total in:", dm_label("originalLength"))), |
43 | rightAlignedLine(withLabel("Total out (est.):", dm_label("cutLength")))) |
44 | ), |
45 | northAndCenterWithMargin( |
46 | jsection("Audio", jMinHeight(50, graph = |
47 | setForeground(Color.red, swingNu(JAmplitudeGraph)))), |
48 | hgridWithSpacing(jsection("Preview", jscroll_center(isPreview = jImageSurface())), |
49 | super))), |
50 | jvstackWithSpacing( |
51 | ffmpegVersionPanel(), |
52 | hgridWithSpacing(dm_textFieldWithLabel ffmpegOptions(), dm_textFieldWithLabel videoCodec())))); |
53 | |
54 | void loadVideo enter { |
55 | temp dm_tempDisableAllButtons(); |
56 | File f = newFile(inputFile); |
57 | if (!fileExists(f)) ret with infoBox("File not found: " + f2s(f)); |
58 | S id = md5(f2s(f)); |
59 | |
60 | File previewFile = prepareCacheProgramFile("preview-" + id + ".jpg"); |
61 | File audioFile = prepareCacheProgramFile("preview-" + id + ".wav"); |
62 | if (!fileExists(previewFile)) { |
63 | print("Getting preview image..."); |
64 | ffmpeg_getSingleFrame(f, previewFile, 0.0); |
65 | print("Done"); |
66 | } else print("Have preview image"); |
67 | isPreview.setImageAndZoomToDisplay(loadImage2(previewFile)); |
68 | |
69 | if (!fileExists(audioFile)) { |
70 | print("Extracting audio..."); |
71 | ffmpeg_toMonoAudio_16k(f, audioFile); |
72 | print("Done - " + fileInfo(audioFile)); |
73 | } else print("Have audio"); |
74 | |
75 | print("Getting volume profile..."); |
76 | profile = decodeWAVToMonoSamples_floatVolumeProfile(audioFile, audioWindowSize); |
77 | print("Have volume profile (" + nEntries(l(profile)) + ")"); |
78 | setField(originalLength := formatMinuteAndSeconds(iceil(l(profile)*profileSamplingInterval))); |
79 | //printStruct(takeFirstOfFloatArray(100, profile)); |
80 | |
81 | graph.setValues(profile); |
82 | } |
83 | |
84 | void makeVideo enter { |
85 | temp dm_tempDisableAllButtons(); |
86 | if (empty(outputFile)) ret with infoBox("Need output file path"); |
87 | File in = newFile(inputFile), out = newFile(outputFile); |
88 | if (empty(timestamps)) autoJumpCuts(); |
89 | L<DoubleRange> ranges = parseTimestampRanges(timestamps); |
90 | if (empty(ranges)) ret with infoBox("No timestamps"); |
91 | print(+ranges); |
92 | if (fileExists(out) && !confirmOKCancel("Overwrite " + fileName(out) + "?")) ret; |
93 | temp tempInfoBox_noHide("Splicing video..."); |
94 | backtickToConsole(ffmpegCmd() + " -y " + ffmpegOptions + " " + ffmpeg_argsForSplice_usingFile(in, out, ranges, |
95 | beforeOut := empty(videoCodec) ? "" : "-vcodec " + videoCodec)); |
96 | if (fileExists(out)) |
97 | infoBox("Done splicing video!" + fileInfo(out)); |
98 | else |
99 | infoBox("Something went wrong..."); |
100 | } |
101 | |
102 | void autoJumpCuts enter { |
103 | if (profile == null) loadVideo(); |
104 | if (profile == null) ret; |
105 | L<DoubleRange> ranges = audio_findSpeechPartsFromVolumeProfile(profile, profileSamplingInterval, |
106 | volumeThreshold := volumeThresholdPercent/100.0, |
107 | minSilenceDuration := parseDouble(minSilence), |
108 | pre := parseDouble(leadIn), |
109 | post := parseDouble(leadOut)); |
110 | setField(timestamps := formatTimestampRanges(ranges)); |
111 | infoBox("Created " + nSlices(ranges) + ", ready to make video"); |
112 | } |
113 | |
114 | start { |
115 | shouldKeepTempFiles = () -> true; |
116 | dm_watchField timestamps(r { |
117 | pcall-silent { |
118 | double len = totalLengthOfDoubleRanges(parseTimestampRanges(timestamps)); |
119 | setField(cutLength := formatMinuteAndSeconds(iceil(len))); |
120 | } |
121 | }); |
122 | |
123 | dm_watchField inputFile(r { |
124 | setField(outputFile := f2s(appendToBaseName(newFile(inputFile), "-cut"))) |
125 | }); |
126 | } |
127 | } |
download show line numbers debug dex old transpilations
Travelled to 7 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv
No comments. add comment
Snippet ID: | #1024578 |
Snippet name: | AutoJumpCut (Single Video) |
Eternal ID of this version: | #1024578/71 |
Text MD5: | 386d898ea956c378a1fe24a4ddb9d49f |
Transpilation MD5: | ecea8d0bc520a9321fcc6a355e00d6ed |
Author: | stefan |
Category: | javax |
Type: | JavaX source code (Dynamic Module) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2020-02-25 02:47:40 |
Source code size: | 5523 bytes / 127 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 449 / 2895 |
Version history: | 70 change(s) |
Referenced in: | [show references] |