public:ffmpeg-based_interval_timer_for_sport_and_exercise
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revisionNext revisionBoth sides next revision | ||
public:ffmpeg-based_interval_timer_for_sport_and_exercise [2018/02/07 01:47] – created fangfufu | public:ffmpeg-based_interval_timer_for_sport_and_exercise [2018/02/09 18:19] – fangfufu | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== FFmpeg-based Interval Timer for Sport and Exercise ====== | ====== FFmpeg-based Interval Timer for Sport and Exercise ====== | ||
- | I unfortunately | + | I am the one of the cycling captain of UEA Triathlon Club for the academic year of 2017-2018. |
- | After digging through FFmpeg documentation [(https:// | + | I used to use a Matlab-based |
- | Here I present | + | After digging through FFmpeg documentation [(https:// |
+ | Here I present the program listing, and an example output. The script works fine on Debian Stretch. | ||
+ | |||
+ | I have to say FFmpeg is one of my most abused software. Last time I used FFmpeg to upload arbitrary file to Youtube [(https:// | ||
===== Program listing ===== | ===== Program listing ===== | ||
<code bash> | <code bash> | ||
#!/bin/bash | #!/bin/bash | ||
+ | # Copyright < | ||
+ | # | ||
+ | # Permission is hereby granted, free of charge, to any person obtaining a copy | ||
+ | # of this software and associated documentation files (the " | ||
+ | # in the Software without restriction, | ||
+ | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
+ | # copies of the Software, and to permit persons to whom the Software is | ||
+ | # furnished to do so, subject to the following conditions: | ||
+ | # | ||
+ | # The above copyright notice and this permission notice shall be included in all | ||
+ | # copies or substantial portions of the Software. | ||
+ | # | ||
+ | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
+ | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
+ | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
+ | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
+ | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
+ | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
+ | # SOFTWARE. | ||
+ | |||
+ | # The content of the set | ||
+ | # Format: | ||
+ | # min:sec text | ||
+ | turbo=( | ||
+ | #Warm up | ||
+ | '05:00 90 RPM Smooth pedalling' | ||
+ | '02:00 95 RPM Smooth pedalling' | ||
+ | '02:00 100 RPM Smooth pedalling' | ||
+ | '02:00 105 RPM Smooth pedalling' | ||
+ | '01:30 110 RPM Smooth pedalling' | ||
+ | '00:30 120-130 RPM Maintain form' | ||
+ | '02:00 90 RPM Relax and recover' | ||
+ | '00:06 MAX RPM Rev out' | ||
+ | '01:00 90 RPM Smooth pedalling' | ||
+ | '00:06 MAX RPM Rev out' | ||
+ | '01:00 90 RPM Smooth pedalling' | ||
+ | '00:06 MAX RPM Rev out' | ||
+ | '02:45 90 RPM Relax and recover' | ||
+ | #Russian Steps | ||
+ | '00:15 Max effort' | ||
+ | '00:45 Recovery' | ||
+ | '00:30 Max effort' | ||
+ | '00:30 Recovery' | ||
+ | '00:45 Max effort' | ||
+ | '00:15 Recovery' | ||
+ | '01:00 Max effort' | ||
+ | '01:00 Recovery' | ||
+ | '00:45 Max effort' | ||
+ | '00:15 Recovery' | ||
+ | '00:30 Max effort' | ||
+ | '00:30 Recovery' | ||
+ | '00:15 Max effort' | ||
+ | '05:45 Recovery' | ||
+ | '00:15 Max effort' | ||
+ | '00:45 Recovery' | ||
+ | '00:30 Max effort' | ||
+ | '00:30 Recovery' | ||
+ | '00:45 Max effort' | ||
+ | '00:15 Recovery' | ||
+ | '01:00 Max effort' | ||
+ | '01:00 Recovery' | ||
+ | '00:45 Max effort' | ||
+ | '00:15 Recovery' | ||
+ | '00:30 Max effort' | ||
+ | '00:30 Recovery' | ||
+ | '00:15 Max effort' | ||
+ | '05:45 Recovery' | ||
+ | '00:15 Max effort' | ||
+ | '00:45 Recovery' | ||
+ | '00:30 Max effort' | ||
+ | '00:30 Recovery' | ||
+ | '00:45 Max effort' | ||
+ | '00:15 Recovery' | ||
+ | '01:00 Max effort' | ||
+ | '01:00 Recovery' | ||
+ | '00:45 Max effort' | ||
+ | '00:15 Recovery' | ||
+ | '00:30 Max effort' | ||
+ | '00:30 Recovery' | ||
+ | '00:15 Max effort' | ||
+ | '10:00 Cool Down' | ||
+ | ) | ||
+ | ################################################################################ | ||
+ | resolution=1280x720 | ||
+ | framerate=30 | ||
# The file number counter | # The file number counter | ||
let counter=0 | let counter=0 | ||
+ | |||
+ | # The length of the set | ||
+ | let setLen=${# | ||
function cleanup { | function cleanup { | ||
- | rm seg-*.mp4 filelist.txt | + | rm -f seg-*.mp4 filelist.txt intro.txt |
} | } | ||
- | # calling | + | # Calling |
- | function | + | function |
- | let min=$1/60 | + | ffmpeg -f lavfi -i color=c=white: |
- | | + | -vf " |
- | ffmpeg -f lavfi -i color=c=white: | + | r=${framerate}: |
- | -vf " | + | -t ${1} -preset ultrafast -tune stillimage -y -loglevel panic -stats \ |
- | r=30: x=(w-tw)/2: y=h-h*0.75: fontcolor=black:,\ | + | seg-${counter}.mp4 |
+ | echo "file seg-${counter}.mp4" | ||
+ | let counter++ | ||
+ | } | ||
+ | |||
+ | # Calling convention: | ||
+ | function videoSegment { | ||
+ | let duration=${1}*60+${2} | ||
+ | | ||
+ | sec=${2} | ||
+ | textNow=${3} | ||
+ | textNext=$(echo " | ||
+ | ffmpeg -f lavfi -i color=c=white: | ||
+ | -vf "\ | ||
+ | | ||
+ | r=${framerate}: x=(w-tw)*0.5: y=h-h*0.80: fontcolor=Black:,\ | ||
+ | | ||
drawtext=text=' | drawtext=text=' | ||
- | r=30: x=(w-tw)/2: y=h-h*0.5: fontcolor=black:, | + | r=${framerate}: x=(w-tw)*0.5: y=h-h*0.60: fontcolor=Black:, |
+ | \ | ||
drawtext=timecode=' | drawtext=timecode=' | ||
- | r=30: x=(w-tw)/2: y=h-h*0.25: fontcolor=black:" \ | + | r=${framerate}: x=(w-tw)*0.5: y=h-h*0.40: fontcolor=Black:, \ |
- | -t ${1} -preset | + | \ |
+ | drawtext=text=' | ||
+ | r=${framerate}: | ||
+ | | ||
+ | -t ${duration} -preset | ||
seg-${counter}.mp4 | seg-${counter}.mp4 | ||
echo "file seg-${counter}.mp4" | echo "file seg-${counter}.mp4" | ||
Line 35: | Line 147: | ||
function output { | function output { | ||
- | ffmpeg -f concat -i filelist.txt -c copy ${1} | + | ffmpeg -f concat -i filelist.txt -c copy -y ${1} |
cleanup | cleanup | ||
} | } | ||
+ | # Calling convention: segment_id | ||
+ | function turboSegment { | ||
+ | thisLine=$(echo ${turbo[${1}]}) | ||
+ | timing=$(echo $thisLine|cut -d ' ' -f 1) | ||
+ | min=$(echo $timing|cut -d ':' | ||
+ | sec=$(echo $timing|cut -d ':' | ||
+ | textNow=$(echo $thisLine|cut -d ' ' -f 2-) | ||
+ | |||
+ | let nextSeg=${1}+1 | ||
+ | nextLine=$(echo ${turbo[${nextSeg}]}|tr ':' | ||
+ | videoSegment $min $sec " | ||
+ | } | ||
+ | ################################################################################ | ||
cleanup | cleanup | ||
- | segment 300 "Easy, gradually build up" | + | # Create the introduction text variable |
- | segment 600 "10 mile TT effort" | + | printf |
- | segment 15 | + | |
- | segment 600 " | + | # introduction |
- | segment 600 "10 mile TT effort" | + | |
- | segment 20 " | + | # read -p "Press any key to continue, or Ctrl+C to stop..." |
- | segment 600 "Easy" | + | |
- | segment 600 "10 mile TT effort" | + | |
- | segment 25 " | + | |
- | segment 300 "Cool down" | + | |
- | output UEA-Triathlon-2018-02-07.mp4 | + | for i in $(seq 0 $setLen); do |
+ | turboSegment ${i} | ||
+ | done | ||
+ | output UEA-Triathlon-$(date --rfc-3339=date).mp4 | ||
</ | </ | ||
===== Example output ===== | ===== Example output ===== | ||
- | [[https:// | + | [[https:// |
public/ffmpeg-based_interval_timer_for_sport_and_exercise.txt · Last modified: 2018/03/31 00:38 by 127.0.0.1