Uses 108K of libraries. Click here for Pure Java version (16066L/121K).
!7 module VideoSplicer > DynPrintLog { S inputFile, outputFile; S timestamps; S originalLength, cutLength; S ffmpegOptions = "", videoCodec = ""; int volumeThresholdPercent = 15; S minSilence = "0.2", leadIn = "0.2", leadOut = "0.2"; transient double profileSamplingInterval = 0.05; // 50 ms transient int audioWindowSize = iround(profileSamplingInterval*16000); transient int timeFieldMinWidth = 40; transient ImageSurface isPreview; transient JAmplitudeGraph graph; transient float[] profile; visual jHandleFileDrop(voidfunc(File f) { setField(inputFile := f2s(f)) }, northCenterAndSouthWithMargins( vstackWithSpacing( withLabel("Input video:", filePathInputWithBrowseButton(dm_textField inputFile(), onChoose := rThread loadVideo)), withLabel("Output video:", filePathInputWithBrowseButton(dm_textField outputFile())), centeredLine( jbutton("Load video", rThread loadVideo), jbutton("Find jump cuts", rThread autoJumpCuts), jhspacer(10), jbutton("Make video", rThread makeVideo)), jCenteredSection("Jump Cut Parameters", centeredLineWithSpacing(20, withLabelLeftAndRight("Volume threshold:", dm_spinner volumeThresholdPercent(0, 100), "%"), withLabelLeftAndRight("Shortest silence to cut:", jMinWidth(timeFieldMinWidth, dm_centeredTextField minSilence()), "s"), jline( withLabel("Lead-in/out:", jMinWidth(timeFieldMinWidth, dm_centeredTextField leadIn())), jlabel("/"), withLabelToTheRight(jMinWidth(timeFieldMinWidth, dm_centeredTextField leadOut()), "s")) ))), jhsplit(0.2, centerAndSouthWithMargin( jLiveValueSection(dm_calculatedLiveValue(S, () -> "Timestamps (" + countLines(timestamps) + ")"), dm_textArea("timestamps")), vstack( rightAlignedLine(withLabel("Total in:", dm_label("originalLength"))), rightAlignedLine(withLabel("Total out (est.):", dm_label("cutLength")))) ), northAndCenterWithMargin( jsection("Audio", jMinHeight(50, graph = setForeground(Color.red, swingNu(JAmplitudeGraph)))), hgridWithSpacing(jsection("Preview", jscroll_center(isPreview = jImageSurface())), super))), jvstackWithSpacing( ffmpegVersionPanel(), hgridWithSpacing(dm_textFieldWithLabel ffmpegOptions(), dm_textFieldWithLabel videoCodec())))); void loadVideo enter { temp dm_tempDisableAllButtons(); File f = newFile(inputFile); if (!fileExists(f)) ret with infoBox("File not found: " + f2s(f)); S id = md5(f2s(f)); File previewFile = prepareCacheProgramFile("preview-" + id + ".jpg"); File audioFile = prepareCacheProgramFile("preview-" + id + ".wav"); if (!fileExists(previewFile)) { print("Getting preview image..."); ffmpeg_getSingleFrame(f, previewFile, 0.0); print("Done"); } else print("Have preview image"); isPreview.setImageAndZoomToDisplay(loadImage2(previewFile)); if (!fileExists(audioFile)) { print("Extracting audio..."); ffmpeg_toMonoAudio_16k(f, audioFile); print("Done - " + fileInfo(audioFile)); } else print("Have audio"); print("Getting volume profile..."); profile = decodeWAVToMonoSamples_floatVolumeProfile(audioFile, audioWindowSize); print("Have volume profile (" + nEntries(l(profile)) + ")"); setField(originalLength := formatMinuteAndSeconds(iceil(l(profile)*profileSamplingInterval))); //printStruct(takeFirstOfFloatArray(100, profile)); graph.setValues(profile); } void makeVideo enter { temp dm_tempDisableAllButtons(); if (empty(outputFile)) ret with infoBox("Need output file path"); File in = newFile(inputFile), out = newFile(outputFile); if (empty(timestamps)) autoJumpCuts(); L<DoubleRange> ranges = parseTimestampRanges(timestamps); if (empty(ranges)) ret with infoBox("No timestamps"); print(+ranges); if (fileExists(out) && !confirmOKCancel("Overwrite " + fileName(out) + "?")) ret; temp tempInfoBox_noHide("Splicing video..."); backtickToConsole(ffmpegCmd() + " -y " + ffmpegOptions + " " + ffmpeg_argsForSplice_usingFile(in, out, ranges, beforeOut := empty(videoCodec) ? "" : "-vcodec " + videoCodec)); if (fileExists(out)) infoBox("Done splicing video!" + fileInfo(out)); else infoBox("Something went wrong..."); } void autoJumpCuts enter { if (profile == null) loadVideo(); if (profile == null) ret; L<DoubleRange> ranges = audio_findSpeechPartsFromVolumeProfile(profile, profileSamplingInterval, volumeThreshold := volumeThresholdPercent/100.0, minSilenceDuration := parseDouble(minSilence), pre := parseDouble(leadIn), post := parseDouble(leadOut)); setField(timestamps := formatTimestampRanges(ranges)); infoBox("Created " + nSlices(ranges) + ", ready to make video"); } start { shouldKeepTempFiles = () -> true; dm_watchField timestamps(r { pcall-silent { double len = totalLengthOfDoubleRanges(parseTimestampRanges(timestamps)); setField(cutLength := formatMinuteAndSeconds(iceil(len))); } }); dm_watchField inputFile(r { setField(outputFile := f2s(appendToBaseName(newFile(inputFile), "-cut"))) }); } }
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: | 448 / 2894 |
Version history: | 70 change(s) |
Referenced in: | [show references] |