Skip to content

Instantly share code, notes, and snippets.

@coderofsalvation
Last active December 17, 2023 06:45
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save coderofsalvation/8dd3cafd0d21d1fa6dd9cb0de8e58628 to your computer and use it in GitHub Desktop.
Save coderofsalvation/8dd3cafd0d21d1fa6dd9cb0de8e58628 to your computer and use it in GitHub Desktop.
ffmpeg commandline crossfade-looped video
#!/bin/bash
[[ ! -n $3 ]] && { echo "Usage: crossfadevideo <input.mp4> <fade in seconds> <output.mp4> [looptimes]"; exit; }
input="$1"
fade="$2"
duration="$(ffprobe -v error -select_streams v:0 -show_entries stream=duration -of default=noprint_wrappers=1:nokey=1 "$input")"
duration=$(echo "$duration-($fade)" | bc | sed 's/\..*//g')
[[ ${duration:0:1} == "." ]] && duration="0$duration"
output="$3"
[[ -n $4 ]] && loop=$4 && output="${output}.mkv"
set -x
ffmpeg -y -i "$input" -filter_complex '
[0]split[body][pre];
[pre]trim=duration='$fade',format=yuva420p,fade=d='$fade':alpha=1,setpts=PTS+('$(($duration-$fade))'/TB)[jt];
[body]trim='$fade',setpts=PTS-STARTPTS[main];
[main][jt]overlay' "$output"
# we use mkv for looping since ffmpeg loops mp4 badly
[[ -n $loop ]] && ffmpeg -y -stream_loop $loop -i $output -c copy ${output/\.mkv/}
@raphaelmsr
Copy link

raphaelmsr commented Nov 23, 2020

Hey! Looks like a nice script but I am getting unexpected EOF while looking for matching `)' on line 5
What the frack ? Thanks !

@coderofsalvation
Copy link
Author

coderofsalvation commented Nov 23, 2020

a paste gone wrong :)
For some reason on line 5 a bracket and quote as missing 🗡️
Updated it..can you check it out?

@raphaelmsr
Copy link

Awesome ! Thank you for your reply

@victoriagrace
Copy link

Fixed a typo in my fork. Should work now.

@mexomagno
Copy link

mexomagno commented Aug 1, 2021

My man, this is absolutely beautiful. Thanks for taking the time to take the oneliner into this very usable script.

Duration always returned empty for me so (thanks to this post) I replaced line 5 with

duration=$(ffmpeg -i cut.mp4 2>&1 | grep "Duration"| cut -d ' ' -f 4 | sed s/,// | sed 's@\..*@@g' | awk '{ split($1, A, ":"); split(A[3], B, "."); print 3600*A[1] + 60*A[2] + B[1] }')

Also final loop length was fade seconds longer than needed, so my setpts bit looks like setpts=PTS+('$(($duration-$fade))'/TB)[jt]

With that, everything works perfect for me

@coderofsalvation
Copy link
Author

coderofsalvation commented Oct 18, 2021

@mexomagno thank you for the fix, I've integrated it (and added a looptimes param too)

@api-haus
Copy link

Version of this script that supports transparent videos: https://gist.github.com/api-haus/e4845156845ce07de1744a5fa45db5be

@ozeidan
Copy link

ozeidan commented Dec 17, 2022

The last command of the last line deleted all the mkv files in my folder. Luckily I didn't need any of them. But maybe it would be better to fix this

@weweb1
Copy link

weweb1 commented Apr 15, 2023

Hi. Can you be so kind to explain me what variables should be changed in the code to make it work for me? Which parts?
When I try to execute it, returns only echo and nothing happens

@coderofsalvation
Copy link
Author

@weweb1 sure! Please copy/paste the command you typed in your terminal.

@weweb1
Copy link

weweb1 commented Apr 15, 2023

Thanks a lot! @coderofsalvation

I directly copied your code from above, making sure that I run it in the folder with the video, and with the video name called input.mp4:

@pigubaoza
Copy link

pigubaoza commented Dec 17, 2023

im getting a freeze for fade seconds at the end of the video (if i do not provide looptimes), and also between every loop when i do provide looptimes. any idea how i can fix this?

this is my ffmpeg version

ffmpeg version 6.0 Copyright (c) 2000-2023 the FFmpeg developers
built with Apple clang version 14.0.3 (clang-1403.0.22.14.1)
configuration: --prefix=/opt/homebrew/Cellar/ffmpeg/6.0_1 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libaribb24 --enable-libbluray --enable-libdav1d --enable-libjxl --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librist --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libsvtav1 --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvmaf --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-videotoolbox --enable-audiotoolbox --enable-neon
libavutil      58.  2.100 / 58.  2.100
libavcodec     60.  3.100 / 60.  3.100
libavformat    60.  3.100 / 60.  3.100
libavdevice    60.  1.100 / 60.  1.100
libavfilter     9.  3.100 /  9.  3.100
libswscale      7.  1.100 /  7.  1.100
libswresample   4. 10.100 /  4. 10.100
libpostproc    57.  1.100 / 57.  1.100

EDIT: i think this is because im using a file with two streams. removing the audio stream fixes the problem. would be great if we can detect and handle such files in the script

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'input.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: mp42mp41
    creation_time   : 2021-12-11T19:25:10.000000Z
  Duration: 00:00:10.00, start: 0.000000, bitrate: 100323 kb/s
  Stream #0:0[0x1](eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 3840x2160, 99986 kb/s, 30 fps, 30 tbr, 30k tbn (default)
    Metadata:
      creation_time   : 2021-12-11T19:25:10.000000Z
      handler_name    : ?Mainconcept Video Media Handler
      vendor_id       : [0][0][0][0]
      encoder         : AVC Coding
  Stream #0:1[0x2](eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 316 kb/s (default)
    Metadata:
      creation_time   : 2021-12-11T19:25:10.000000Z
      handler_name    : #Mainconcept MP4 Sound Media Handler
      vendor_id       : [0][0][0][0]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment