Compare commits
	
		
			3 Commits
		
	
	
		
			master
			...
			setup-arek
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 5e15153eef | |||
| 58dd1d5beb | |||
| 068ab054bb | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -2,3 +2,5 @@ | ||||
| *.mp4 | ||||
| out/* | ||||
| !out/keepme | ||||
| *.wav | ||||
| .DS_Store | ||||
							
								
								
									
										1
									
								
								add-glitch.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @ -0,0 +1 @@ | ||||
| ffmpeg -hwaccel videotoolbox -i glitch.mp4 -i video-with-new-audio.mp4 -i glitch.mp4 -filter_complex "[0:v:0][0:a:0][1:v:0][1:a:0][2:v:0][2:a:0]concat=n=3:v=1:a=1[vv][aa]" -map "[vv]" -map "[aa]" -c:v libx264 -r 50 -c:a aac -strict experimental -b:a 320k output.mp4 | ||||
							
								
								
									
										9
									
								
								demux.sh
									
									
									
									
									
								
							
							
						
						| @ -1,9 +0,0 @@ | ||||
| echo "generating the demuxers..." | ||||
| 
 | ||||
| # generuje ścieżki do złożenia przez ffmpega: | ||||
| ts-node generate-demuxer.ts >out/demuxer.txt | ||||
| 
 | ||||
| # używa demuxer.txt żeby skleić końcowe video z dźwiękiem: | ||||
| echo generowanie całości | ||||
| ffmpeg -y -f concat -safe 0 -i out/demuxer.txt -r $framerate -video_track_timescale $timescale -tune stillimage -pix_fmt yuv420p out/video.mp4 | ||||
| #                   ^ daję safe 0 aby przyjmowało bezwzględne ścieżki | ||||
							
								
								
									
										0
									
								
								find-loudness.ts
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								fix-audio.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @ -0,0 +1 @@ | ||||
| ffmpeg -hwaccel videotoolbox -i out/video.mp4 -i icdw-11-final.wav -c:v copy -c:a aac -b:a 320k -strict experimental video-with-new-audio.mp4 | ||||
							
								
								
									
										116
									
								
								generate-demuxer.ts
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						| @ -2,9 +2,7 @@ import findLoudness, { SwapPoint } from "./find-loudness"; | ||||
| 
 | ||||
| const graph_density = 8000; | ||||
| 
 | ||||
| const threshold_at_point = parseInt( | ||||
|   process.env.demuxer_volume_threshold || "1" | ||||
| ); | ||||
| const threshold_at_point = 2; | ||||
| 
 | ||||
| const inertia_s = 0.3; | ||||
| const inertia_samples = inertia_s * graph_density; | ||||
| @ -16,65 +14,75 @@ const minutes = (units: number) => Math.floor(s(units) / 60); | ||||
| const hours = (units: number) => Math.floor(units / graph_density / 60 / 60); | ||||
| 
 | ||||
| const formatTime = (units: number) => | ||||
|   `${hours(units)}:${minutes(units)}:${Math.floor(s(units) % 60)}`; | ||||
| 	`${hours(units)}:${minutes(units)}:${Math.floor(s(units) % 60)}`; | ||||
| 
 | ||||
| type Mode = { left: boolean; right: boolean }; | ||||
| 
 | ||||
| async function run() { | ||||
|   const [left_breaks, right_breaks] = await Promise.all([ | ||||
|     findLoudness("/tmp/leftraw", threshold_at_point, inertia_samples, "left"), | ||||
|     findLoudness("/tmp/rightraw", threshold_at_point, inertia_samples, "right"), | ||||
|   ]); | ||||
| 	const [left_breaks, right_breaks] = await Promise.all([ | ||||
| 		findLoudness( | ||||
| 			"/tmp/leftraw", | ||||
| 			threshold_at_point, | ||||
| 			inertia_samples, | ||||
| 			"left" | ||||
| 		), | ||||
| 		findLoudness( | ||||
| 			"/tmp/rightraw", | ||||
| 			threshold_at_point, | ||||
| 			inertia_samples, | ||||
| 			"right" | ||||
| 		), | ||||
| 	]); | ||||
| 
 | ||||
|   const merged = [...left_breaks, ...right_breaks].sort((a, b) => | ||||
|     a.position_start < b.position_start | ||||
|       ? -1 | ||||
|       : a.position_start > b.position_start | ||||
|       ? 1 | ||||
|       : 0 | ||||
|   ); | ||||
| 	const merged = [...left_breaks, ...right_breaks].sort((a, b) => | ||||
| 		a.position_start < b.position_start | ||||
| 			? -1 | ||||
| 			: a.position_start > b.position_start | ||||
| 				? 1 | ||||
| 				: 0 | ||||
| 	); | ||||
| 
 | ||||
|   // console.log("left breaks:", left_breaks);
 | ||||
|   // console.log(`right_breaks`, right_breaks);
 | ||||
|   // console.log(`merged`, merged);
 | ||||
| 	// console.log("left breaks:", left_breaks);
 | ||||
| 	// console.log(`right_breaks`, right_breaks);
 | ||||
| 	// console.log(`merged`, merged);
 | ||||
| 
 | ||||
|   function new_mode(m: Mode, s: SwapPoint): Mode { | ||||
|     return { ...m, [s.label]: s.loud }; | ||||
|   } | ||||
| 	function new_mode(m: Mode, s: SwapPoint): Mode { | ||||
| 		return { ...m, [s.label]: s.loud }; | ||||
| 	} | ||||
| 
 | ||||
|   function mode_to_string(mode: Mode) { | ||||
|     if (mode.left && mode.right) { | ||||
|       return "both"; | ||||
|     } | ||||
|     for (const side of ["left", "right"]) { | ||||
|       if (mode[side as keyof Mode]) { | ||||
|         return side; | ||||
|       } | ||||
|     } | ||||
|     return "none"; | ||||
|   } | ||||
| 	function mode_to_string(mode: Mode) { | ||||
| 		if (mode.left && mode.right) { | ||||
| 			return "both"; | ||||
| 		} | ||||
| 		for (const side of ["left", "right"]) { | ||||
| 			if (mode[side as keyof Mode]) { | ||||
| 				return side; | ||||
| 			} | ||||
| 		} | ||||
| 		return "none"; | ||||
| 	} | ||||
| 
 | ||||
|   console.log("file", `${process.cwd()}/pics/none.png`); | ||||
|   let last_point = 0; | ||||
|   let mode: Mode = { left: false, right: false }; | ||||
|   let last_file; | ||||
|   let total = 0; | ||||
|   for (let i = 2; i < merged.length; i++) { | ||||
|     const point = merged[i]; | ||||
|     mode = new_mode(mode, point); | ||||
|     const file = `${process.cwd()}/pics/${mode_to_string(mode)}.png`; | ||||
|     const duration = (point.position_start - last_point) / graph_density; | ||||
|     console.log( | ||||
|       "duration", | ||||
|       (point.position_start - last_point) / graph_density | ||||
|     ); | ||||
|     console.log("file", file); | ||||
|     last_point = point.position_start; | ||||
|     last_file = file; | ||||
|     total += duration * graph_density; | ||||
|   } | ||||
|   console.log("duration", merged[merged.length - 1].duration / graph_density); | ||||
|   console.log("file", last_file); | ||||
|   console.error(total, formatTime(total)); | ||||
| 	console.log("file", `${process.cwd()}/pics/none.png`); | ||||
| 	let last_point = 0; | ||||
| 	let mode: Mode = { left: false, right: false }; | ||||
| 	let last_file; | ||||
| 	let total = 0; | ||||
| 	for (let i = 2; i < merged.length; i++) { | ||||
| 		const point = merged[i]; | ||||
| 		mode = new_mode(mode, point); | ||||
| 		const file = `${process.cwd()}/pics/${mode_to_string(mode)}.png`; | ||||
| 		const duration = (point.position_start - last_point) / graph_density; | ||||
| 		console.log( | ||||
| 			"duration", | ||||
| 			(point.position_start - last_point) / graph_density | ||||
| 		); | ||||
| 		console.log("file", file); | ||||
| 		last_point = point.position_start; | ||||
| 		last_file = file; | ||||
| 		total += duration * graph_density; | ||||
| 	} | ||||
| 	console.log("duration", merged[merged.length - 1].duration / graph_density); | ||||
| 	console.log("file", last_file); | ||||
| 	console.error(total, formatTime(total)); | ||||
| } | ||||
| run(); | ||||
|  | ||||
							
								
								
									
										49
									
								
								generate.sh
									
									
									
									
									
								
							
							
						
						| @ -6,18 +6,43 @@ | ||||
| #  W katalogu z tym skryptem musisz mieć katalog "pics", w którym są pliki "left.png", "right.png", "none.png" i "both.png" | ||||
| # | ||||
| 
 | ||||
| export input=~/Downloads/icdw5/icdw5-stereo.wav                  # tutaj dajemy ścieżkę do pliku mp3 z Arkiem w jednym kanale i Kubą w drugim | ||||
| export input_mono=~/Downloads/icdw5/icdw5-stereo.wav             # tutaj dajemy ścieżkę do pliku mp3 z Arkiem w jednym kanale i Kubą w drugim | ||||
| export intro=~/projects/midline/podcast-visualizer/out/intro.mp4 # glitch | ||||
| export outro=~/projects/midline/podcast-visualizer/out/intro.mp4 # to samo na końcu, co na początku | ||||
| export final_output=~/Downloads/icdw5-viz.mp4 | ||||
| export framerate=25 | ||||
| export timescale=25000 | ||||
| export demuxer_volume_threshold=15 #od 0 do 128 | ||||
| framerate=60 | ||||
| timescale=25000 | ||||
| 
 | ||||
| aresample=8000   # to bez zmian | ||||
| 
 | ||||
| # echo dzielimy mp3 na dwa osobne wav | ||||
| # ffmpeg -i $input -map_channel 0.0.0 /tmp/left.wav -map_channel 0.0.1 /tmp/right.wav | ||||
| 
 | ||||
| 
 | ||||
| ffmpeg -i ./left.wav -ac 1 -filter:a aresample=$aresample -map 0:a -c:a pcm_u8 -f data -  > /tmp/leftraw & | ||||
| ffmpeg -i ./right.wav -ac 1 -filter:a aresample=$aresample -map 0:a -c:a pcm_u8 -f data -  > /tmp/rightraw & | ||||
| 
 | ||||
| # czekamy aż obydwa wątki się zakończą | ||||
| wait; | ||||
| 
 | ||||
| echo "generating the demuxers..."; | ||||
| 
 | ||||
| # generuje ścieżki do złożenia przez ffmpega: | ||||
| 
 | ||||
| ts-node generate-demuxer.ts > out/demuxer.txt | ||||
| 
 | ||||
| aresample=8000 # to bez zmian | ||||
| mkdir -p out | ||||
| 
 | ||||
| source ./split.sh | ||||
| source ./demux.sh | ||||
| source ./merge.sh | ||||
| # używa demuxer.txt żeby skleić końcowe video z dźwiękiem: | ||||
| echo generowanie całości | ||||
| # ffmpeg -y -f concat -safe 0 -i out/demuxer.txt -r $framerate -video_track_timescale $timescale -tune stillimage -fps_mode vfr -pix_fmt yuv420p out/video.mp4 | ||||
| 
 | ||||
| # linux | ||||
| # ffmpeg -y -f concat -safe 0 -hwaccel vaapi -hwaccel_output_format vaapi -vaapi_device /dev/dri/renderD128 -i out/demuxer.txt -r $framerate -video_track_timescale $timescale -tune stillimage -pix_fmt yuv420p out/video.mp4 | ||||
| 
 | ||||
| # macos mod | ||||
| ffmpeg -y -f concat -safe 0 -hwaccel videotoolbox -i out/demuxer.txt -r $framerate -video_track_timescale $timescale -tune stillimage -pix_fmt yuv420p out/video.mp4 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #                   ^ daję safe 0 aby przyjmowało bezwzględne ścieżki | ||||
| 
 | ||||
| 
 | ||||
| # echo łączenie video z dźwiękiem: | ||||
| # ffmpeg  -i out/video.mp4 -i $input -ac 1 -video_track_timescale $timescale -tune stillimage out/video-and-audio.mp4 | ||||
|  | ||||
							
								
								
									
										44
									
								
								merge.sh
									
									
									
									
									
								
							
							
						
						| @ -1,44 +0,0 @@ | ||||
| processed_intro_path=$PWD/out/intro-processed.mp4 | ||||
| processed_outro_path=$PWD/out/outro-processed.mp4 | ||||
| 
 | ||||
| echo łączenie video z dźwiękiem: | ||||
| ffmpeg -y -vaapi_device /dev/dri/renderD128 \ | ||||
| 	-i out/video.mp4 -i $input_mono \ | ||||
| 	-video_track_timescale $timescale \ | ||||
| 	-tune stillimage \ | ||||
| 	-c:a aac \ | ||||
| 	-ac 1 \ | ||||
| 	-strict experimental \ | ||||
| 	-vf 'format=nv12,hwupload' \ | ||||
| 	-c:v h264_vaapi \ | ||||
| 	out/video-and-audio.mp4 | ||||
| 
 | ||||
| echo reencoding intro to enable fast concat... | ||||
| 
 | ||||
| function reencode_for_demux_compatibility() { | ||||
| 	ffmpeg -y -i "$1" \ | ||||
| 		-pix_fmt yuv420p \ | ||||
| 		-r $framerate \ | ||||
| 		-video_track_timescale $timescale \ | ||||
| 		-ac 1 \ | ||||
| 		-b:a 320k \ | ||||
| 		"$2" | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| reencode_for_demux_compatibility "$intro" "$processed_intro_path" | ||||
| 
 | ||||
| if [ "$intro" = "$outro" ]; then | ||||
| 	processed_outro_path="$processed_intro_path" | ||||
| else | ||||
| 	reencode_for_demux_compatibility "$outro" "$processed_outro_path" | ||||
| fi | ||||
| 
 | ||||
| echo "" >out/demuxer-branding.txt | ||||
| echo "file $processed_intro_path" >>out/demuxer-branding.txt | ||||
| echo "file video-and-audio.mp4" >>out/demuxer-branding.txt | ||||
| echo "file $processed_outro_path" >>out/demuxer-branding.txt | ||||
| 
 | ||||
| cat out/demuxer-branding.txt | ||||
| 
 | ||||
| ffmpeg -y -f concat -safe 0 -i out/demuxer-branding.txt -c:v copy -b:a 320k "$final_output" | ||||
							
								
								
									
										0
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
							
								
								
									
										0
									
								
								package.json
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								pics/both.png
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						| Before Width: | Height: | Size: 329 KiB After Width: | Height: | Size: 322 KiB | 
							
								
								
									
										
											BIN
										
									
								
								pics/left.png
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						| Before Width: | Height: | Size: 311 KiB After Width: | Height: | Size: 313 KiB | 
							
								
								
									
										
											BIN
										
									
								
								pics/none.png
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						| Before Width: | Height: | Size: 287 KiB After Width: | Height: | Size: 301 KiB | 
							
								
								
									
										
											BIN
										
									
								
								pics/right.png
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						| Before Width: | Height: | Size: 306 KiB After Width: | Height: | Size: 313 KiB | 
							
								
								
									
										9
									
								
								split.sh
									
									
									
									
									
								
							
							
						
						| @ -1,9 +0,0 @@ | ||||
| echo dzielimy mp3 na dwa osobne wav | ||||
| ffmpeg -y -i $input -map_channel 0.0.0 /tmp/left.wav -map_channel 0.0.1 /tmp/right.wav | ||||
| 
 | ||||
| echo na dwóch wątkach generujemy surowe pliki | ||||
| ffmpeg -y -i /tmp/left.wav -ac 1 -filter:a aresample=$aresample -map 0:a -c:a pcm_u8 -f data - >/tmp/leftraw & | ||||
| ffmpeg -y -i /tmp/right.wav -ac 1 -filter:a aresample=$aresample -map 0:a -c:a pcm_u8 -f data - >/tmp/rightraw & | ||||
| 
 | ||||
| # czekamy aż obydwa wątki się zakończą | ||||
| wait | ||||