Creating a Ghost in the Shell screensaver for macOS
Ghost in the Shell (1998) has a brief interlude containing gorgeously drawn scenes of a dystopian city modelled after Hong Kong.
I've wanted to create a screen saver that loops this scene over and over for a while, and I finally got around to doing it.
My first idea was to fork JohnCoates/Aerial and repurpose it for this. But I stumbled on SaveHollywood, which is a macOS screensaver that allows you to supply any custom video(s) to play. So that was a significant chunk of work done.
Producing the video turned out to be tricker than I initially thought. I first scrubbed my copy of GitS to find the exact timestamps for the scenes that I want to export:
33:04 to 33:53 34:04 to 36:20
I wrote a quick
ffmpeg invocation to slice it:
ffmpeg -i ghost-full.mp4 -ss "00:33:04" -to "00:33:53" \ -c copy ghost-1.mp4
But the resulting video didn't work correctly. It had a few seconds of no output in the beginning. I found out that this was because of a concept called "index keyframes", or
i-frames, which for this task were the only frames that I could crop from without re-encoding and without exporting gaps. Re-encoding has the potential to introduce a loss of quality which is something I wanted to avoid.
i-frames can be done with a tool that is part of the
ffprobe -select_streams v -show_frames \ -show_entries frame=pict_type \ -of csv ghost-full.mp4 > ghost-frames.csv
This will output the full list of frames to a
ghost-frames.csv file. To get the
cat ghost-frames.csv | grep -n I | cut -d ':' -f 1 > ghost-i-frames.csv
Then you can peek around
vim i-frames.csv to see the exact frame numbers. However, what I knew was the timestamp,
00:33:04, not the frame number.
The timestamp in seconds is:
33 * 60 + 4 = 1984
To get the frame, you multiply by the framerate. To get the framerate:
ffprobe -v 0 -of compact=p=0 -select_streams 0 \ -show_entries stream=r_frame_rate ghost-full.mp4
24000/1001, which is
23.976023976. The former is better for arithmetic, so:
1984 * (24000 / 1001) = 47568.431568432
So I needed an
i-frame somewhere around frame
i-frames.csv I settled on
47556, but after some trial and error I found the right one to be at the
Apply the same process to the end section for my first clip and to both start and end of my second clip, and I got:
ffmpeg -i ghost-full.mp4 -ss "00:33:02.898" -to "00:33:52.905" -c copy ghost-1.mp4 ffmpeg -i ghost-full.mp4 -ss "00:34:03.500" -to "00:36:19.969" -c copy ghost-2.mp4
Plugging these into
SaveHollywood produced great results, but the transition between the two videos did include a half-second of a black screen, which was slightly jarring. So I concatenated them:
$ cat filelist.txt file 'ghost-1.mp4' file 'ghost-2.mp4' $ ffmpeg -f concat -safe 0 -i filelist.txt -c copy ghost-concat.mp4
And now I had what I set out to do, but I felt that it could also do with some different music. I downloaded the OST from YouTube using
youtube-dl, plugged it into Audacity, and exported a crop of the soundtrack that I wanted to use. Then finally:
ffmpeg -i ghost-concat.mp4 -i ghost-audio.mp3 -codec copy -shortest ghost-saver.mov
I had to export the final version as
.mov to get the sound to play in QuickLook/QuickTime/SaveHollywood.
And that's it.