An open API service indexing awesome lists of open source software.

https://github.com/fstark/macflim

MacFlim flim player source code and utilities
https://github.com/fstark/macflim

Last synced: 5 months ago
JSON representation

MacFlim flim player source code and utilities

Awesome Lists containing this project

README

          

# MacFlim, the true Mac video player

MacFlim is a video encoder and player for black and white vintage Macintoshes, now in its second incarnation.

![MacFlim](./assets/macflim.gif)

_The iPod introduction should have waited for MacFlim to be available_

MacFlim brings movie playing abilities to the most popular members of the Macintosh family, namely the Macintosh XL, Macintosh 128K, Macintosh 512K, Mac Plus, Mac SE, Mac SE/30 and the Macintosh Portable, on their beautiful internal black and white 512x342 display (or even larger, for the Mac XL and the Portable!).

# WHAT'S NEW?

The windows version of ``flimmaker`` is available in the [releases!](https://github.com/fstark/macflim/releases), both for x86_64 and arm64! (I don't have feedback confirming that the ARM version works, so feel free to drop a line in the discord if it does)

``flimmaker`` can now be installed on OSX with a simple ``brew install fstark/macflim/flimmaker``, both on x86_64 and arm64!

The Mac application is also available as a ``.sit`` file, if that's easier for you!

And you can join the [Discord server](https://discord.gg/9rVDSw2D4N) if you need support, wants to discuss MacFlim, or just want to say how great it is :-)

New features are:

* A new 'blue' dithering that is as efficient to encode as the 'ordered' dithering, but looks much closer to the 'error' dithering. It will allow nicer flims on lower end macs (like the plus), or encoding flims with more changes on higher end machines (like the se30)

* A new playback method that bypasses the sound driver that is selected automatically for flims non "grouped" on early macs (ie: up to the Macintosh SE). This allow sound to be played on Mac with a [MicroMac Performer upgrade](https://www.micromac.com/products/performer.html).

* A new 'performer' profile that generates flims playable on a Mac with a Performer upgrade.

## What is in the repository?

* The source code and binaries for "MacFlim", the popular video player for the vintage Mac.

* The source code and binaries for "Mini MacFlim", a low-memory version of the popular video player for the vintage Mac.

* The source code and binary for the XCMD to play a flim from HyperCard.

* The source code of flimmaker, the command line encoder. It runs on Mac and Linux (and could probably be compiled on Windows with a C++20 compiler).

With this code, you should be able to encode and play a video sequence on your vintage Mac.

## I have no Mac and I must stream!

Fear not, if you have no access to a vintage Mac, or if you want to look at the results without having to transfer cumbersome files from your desktop to your Mac, you can generate pixel-exact movies that will let you know how the playback would look on the targeted hardware. See the ``--mp4`` option.

![Bullet demo creation](./assets/bullet-demo.gif)

_Create flims and immediately admire them. Note the gif [has reduced frame rate](https://www.biphelps.com/blog/The-Fastest-GIF-Does-Not-Exist); reality is twice as smooth. And has sound._

## What is new since MacFlim 1.0?

Well, the main change is that flims now have sound. This needed a complete rewrite of both the player and the encoder. All your old flims are now obsolete, sorry.

Note that the new player app is currently less fancy than MacFlim 1.0. It only lets you play full screen flims, but with glorious 8-bit 22KHz mono sound. Old features of the player may be added back. There is also a "playflim" HyperCard XCMD to play a flim from HyperCard.

Encoding input is greatly simplified: you don't have to resize the input to 512x342 any more. You don't need to have it in grayscale. You don't have to have it in pgm format. You can directly feed mp4 movies, or even YouTube or Vimeo URLs to the encoder.

Output is different too: flims are encoded relative to a target "profile", and the flim will only play correctly on hardware that would support this profile.

To ease with testing, an optional mp4 of the flim can be generated. This will let you iterate and tweak the encoding parameters without the need to transfer to your vintage hardware or, god forbid, emulator.

## How do I play flims on my Mac?

[The releases are accessible on GitHub, as a dsk file](https://github.com/fstark/macflim/releases).
Head to [the MacFlim website](http://www.macflim.com/macflim2) for sample flims.

Alternatively, you can build it from the source code, by downloading the 'MacFlim Source Code.dsk' file which is a disk image, containing a working System 6.0.7, the MacFlim source code and binaries, and a THINK C 5 development environment. This is what is used to develop MacFlim and Mini MacFlim.

## How do I get the flim encoder, flimmaker

On OSX (ARM or x86):

* Open a Terminal
* ``brew install fstark/macflim/flimmaker``

This will compile and install the latest version of flimmaker for OSX

If ``brew`` is not installed, [install it from the distribution](https://brew.sh/)

On Windows (>=10)

* Go to [the latest release](https://github.com/fstark/macflim/releases)
* Download the zip file for Windows 10.
* Unzip the file locally and move the directory to where you want to install flimmaker
* Use the command line to launch the ``flimmaker.exe`` utility

Note: the full ffmpeg runtime is included. However, if you want to automatically download videos from YouTube by specifity an https:// input, you need to install yt-dlp in a place where it can be found by ``flimmaker``.

## Ok, but if I want to compile the encoder myself? (or if I am on linux)

The pre-requisites are FFmpeg version 5 (this has changed from previous releases), youtube-dl or yt-dlp (optional) and ImageMagick (optional)

* ``ffmpeg`` and associated libraries are required for compilation. (Version 7 or higher is recommended, but version 5 or higher should work.)
* ``yt-dlp`` or ``youtube-dl`` is used if you want to directly encode movies from YouTube or Vimeo (or others).
* ``ImageMagick`` is used if you want to generate ``gif`` files, like the ones on macflim.com.

On a Mac:

brew install ffmpeg
brew install yt-dlp
brew install ImageMagick

On Linux (Ubuntu 24.04 includes ffmpeg>=5):

# Note: make sure you have a recent C++20 compiler
sudo apt-get update
sudo apt-get install git
sudo apt-get install build-essential
sudo apt-get install g++-11

# Make sure you get the dependencies
sudo apt-get install libavformat-dev libavcodec-dev libavutil-dev
sudo apt-get install yt-dlp
sudo apt-get install imagemagick

Note, ``yt-dlp`` is not easy to keep up to date from apt. See https://github.com/yt-dlp/yt-dlp#installation for a better way to install it.

You can get the source code using:

git clone https://github.com/fstark/macflim.git

(or your regional equivalent)

Compiling is as simple as opening a terminal and typing ``make`` (see notes for Apple silicon below). There may be some warnings of obsolete functions used with FFmpeg, but it is already a miracle that it works. If anyone has a pull request to fix this, let me know.

After compilation, you can generate a sample flim using:

./flimmaker 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' --mp4 out.mp4

This will download the video and encode it for se30 playback (the default), as 'out.flim'. It will take a couple of minutes. You can then immediately play the ``out.mp4`` file, which is identical to the se30 playback, including a mono output. Enjoy!

## Apple silicon notes

If you are using Homebrew on a modern Mac to install ``ffmpeg``, chances are that you'll need to tell the compiler explicitly where to look for the headers and libraries:

CFLAGS=-I/opt/homebrew/include/ LDLIBS=-L/opt/homebrew/lib/ make

## General flim creation options

There are quite a few options that control the flim generation.

The general format is:

flimmaker [input-file-name] [--option-name value]

``input-file-name`` can be either:

* A local mp4 file. It will be opened using the installed FFmpeg library, and the "best" video and audio channels will be read and converted.

* A URL supported by ``youtube-dl`` (or ``yy-dlp``). If the ``input-file-name`` starts with ``https://``, flimmaker will try to use ``youtube-dl`` to download the specified file and encode it. See the ``--cache`` option to avoid downloading the same source multiple times.

* A set of local 512x342 8-bit pgm files. If the ``input-file-name`` ends with ``.pgm``, it will be considered as a ``printf`` pattern and used to read local images (starting at index 1? #### CHECK ME). For instance, ``movie-%06d.pgm`` will read all files named ``movie-000001.pgm``, ``movie-000002.pgm``, etc... [Yes, if one uses '%s', the app will crash](https://github.com/fstark/macflim/issues/4). See the ``--fps`` and ``--audio`` option to specify the audio for pgm files.

All other arguments to ``flimmaker`` go in pairs.

### --flim **flimname**

Specifies the name of the generated flim file. If there is no ``--flim`` option specified, ``flimmaker`` uses *out.flim*.

### --mp4 **file**

Creates a 512x342 60fps mp4 file that renders the flim exactly, with its associated sound. This can be used to view the flim without having to load the ``.flim`` file onto a vintage Macintosh, or uploaded to the web. While the sound channel is 44KHz 16 bits, it really contains the 22KHz 8-bit Macintosh sound. This is by far the easiest way to iterate with the encoder parameters. Note that mp4 is not designed to encode such movies, and the resulting files are *huge* because encoding high-frequency black and white images at 60fps efficiently was definitely not a goal for H.264. Mp4 files are often around 25Mbits/second. A 90 minute movie would be around 16GB...

### --pgm **pattern**

Write every generated frame as a pgm file. This is useful to embed a specific frame in a web site, or to look at the detail of the generation of different sets of parameters. The pattern should contain a single '%d', which will be replaced by the frame number. Existing files with this pattern will be removed. Again, if one uses '%s', the app will crash. Example: ``--pgm out-%06d.pgm``. Beware: it is an extremely efficient way to generate tens of thousands of files. Note that the files matching the pattern are removed before generation.

### --gif **file**

Creates an animated gif file version of the flim. The animated gif is at 20 frames per second. Using a gif makes it easier to embed in a web page. Be careful: gif files can get very large, so limit the duration of the output.

### --cache **flimname**

To avoid downloading the same file multiple times, the ``--cache`` argument can be used to specify a destination to download the file to if it doesn't already exist. ``flimmaker`` will use this file if it exists, or download it there otherwise. This is only used with URL specifiers.

### --profile **plus**|**se**|**se30**|**perfect**|**portable**|**xl**|**512k**|**128k**

Specifies the encoding/playback profile you want to use.

* plus: The plus profile aims at playing the resulting file on a Macintosh Plus, limiting the decoding processing power as much as possible by keeping the data small. For this, it skips half of the frames, uses ordered dithering, blurs the image and adds a small border to the generated flim. It allows for a lot of "leakage" from one frame to the next. The compression parameters are also very lossy. The result will only be "good" if the input movie is very static.

* se: As the Macintosh SE has slightly more processing power than the Macintosh Plus, it can manage files will less compression. It still skips half of the frames, but uses the nicer floyd error dithering.

* se30: Targets the SE/30, the most powerful Macintosh. Encoding can use 4 times more space, doesn't skip frames, and limits leakage from one frame to the next. se30 movies are in general correct, in the sense that mostly anything can be faithfully encoded, with a few artifacts.

* perfect: This profile aims at a "perfect" playback. The resulting files can be played on an upgraded computer. For instance, playing from an SE/30 with a RAM disk allows those "perfect" flims to be played.

* portable: Targets the original Macintosh Portable, _full screen_. (If you want 512x342 flims, just use the *se* profile.) Encoding is the same as the SE; however, as there are more pixels to draw, the result has more artifacts.

* xl: The xl profile generates flims that can be played _full screen_ on a 5MHz Lisa 2/10, running MacWorks XL 3.0, from the internal Widget. Flims have no sound, and are encoded at a very low framerate (divided by 4) and low byterate (580 bytes per ticks). The default resolution is 704x364. Use ``--profile xl --width 608 --height 432`` if you have the screen mod installed (and report here to confirm it worked).

* 512k: This profile generates a flim that can be played on a Macintosh 512K, from the slow floppy-based HD20 hard drive. The framerate is divided by 4, the byterate is 480 bytes per ticks, and there is no sound.

* 128k: The profile for playing on a Macintosh 128K, from the floppy disk. Identical to the 512K, but with a byterate of 380 bytes. Make sure flim files are less than 400KB (in general around 20 seconds)!

* performer: *Experimental* profile to generates flim playable on a mac plus with a [MicroMac Performer upgrade](https://www.micromac.com/products/performer.html).

Examples:

# Sweet dreams, from Eurythmics is a good flim for a Plus, as there is almost no camera movement, and very slow scenes changes
./flimmaker 'https://www.youtube.com/watch?v=qeMFqkcPYcg' --profile plus --flim sweet-dreams-plus.flim --mp4 sweet-dreams-plus.mp4

# Gangnam Style has quite a lot scene changes and movements, but works correctly on the SE/30
./flimmaker 'https://www.youtube.com/watch?v=9bZkp7q19f0' --profile se30 --flim gangnam-style-se30.flim --mp4 gangnam-style-se30.mp4

Run ``./flimmaker`` without arguments will display help, and list the detailed options of each of the profiles.

### --from **time**

Starts encoding at that specific time. Time format is ``[[:]:].``, so ``--from 30`` means *30 seconds* from start, ``--from 120`` means *120 seconds* from start, ``--from 1:`` means *1 minute* from start, and ``--from 1:13:12`` means *1 hour, 13 minutes and 12 seconds* from start. ``--from 69.420`` means *1 minute, 9 seconds and 420 milliseconds* from start.

### --duration **time**

Specify the duration of the flim. See ``--from`` for time format. The default duration is 5 minutes. [Due to incompetent coding](https://github.com/fstark/macflim/issues/5), encoding movies that last longer than 10-15m is in general a bad idea.

### --poster **time**

Specifies the timestamp from which to generate the 128x86 poster for display in the library. By default, the image from a third of the active duration will be extracted. (Note: this can produce black images if the duration is longer than 3 times that of the source material.)

### --srt **subtitles file**

Burns the subtitle file into the flim. Note that there are currently limitations (multi-lines are not supported well).

### --bars **boolean**

The Mac screen ratio is 3/2, but most movies out there are 4/3, 16/9 or something else. By default, flimmaker adds black borders around the border of the flim (because it keeps more of the original image and the black bars are less data to encode). Using ``--bars false`` instead crops the image for a nicer "full screen" effect. Note that, if there are already black bars in the input video, using the 'Z' filter (Zoom) described later can help.

### --watermark **string**

Adds the argument string to the top of every frame of the video. This is useful if you generate several similar videos with different parameters and want to keep track of those. Use ``auto`` as the string to have the encoding parameters placed in the video. Please do not use the watermark option when releasing your video or your flim to the world!

There are two additional options, specifically used for dealing with pgm input:

### --fps **frame-rate**

When specifying a set of pgm files as input, one can use ``--fps`` to specify the timing of the source video. The default is 24 fps.

### --audio **raw-audio-filename**

When specifying a set of pgm files as input, the audio must be provided using a raw ``wav`` file of 22200Hz, unsigned 8-bit samples. Such a file can be created using Audacity, or the ``ffmpeg`` and ``sox`` unix command line programs. (Use ``apt-get install sox`` or ``brew install sox`` to install the sox tool.)

ffmpeg -i movie.mp4 audio.wav
sox -V2 audio.wav -r 22200 -e unsigned-integer -b 8 audio.raw remix 1 norm

### --silent **boolean**

If true, the generated flim will not contain any audio, and will be smaller by 22KB/s. This is useful for encoding silent movies, or if you don't need sound and want to get the best image possible for a background display.

### --width **pixels**

The width of the generated flim, in pixels. Width must be a multiple of 32, and width * height must be less than 65536 (#### or 32768 -- check). Default width is 512.

### --height **pixels**

The height of the generated flim, in pixels. Width * height must be less than 65536 (#### or 32768 -- check). Default height is 342.

### --anchor-x **percent** and --anchor-y **percent**

When resizing to the target width and height, a part of the image is cropped. ``--anchor-x`` and ``--anchor-y`` specify where this crop occurs. Imagine your source material is 512x256 and you have a ``--width 256 --height 256``. If anchor-x is 0, then the left column of the output will correspond to the left column of the input. If anchor-x is 1, then the right column of the output will be the right column of the input. If anchor-x is 0.5, the center of the image will match. You can use any value from 0 to 1 for ``anchor-x`` and ``anchor-y``.

## Moar options!

Digging into the dirty details, here are the options that control the encoding itself (i.e., the options driven by the profile).

You can specify ``--profile`` to set up basic options, then override the defaults with more specific choices.

You need to specify those options *after* the ``--profile``.

This is an advanced section for fine-tuning the encodings. You are not expected to understand it, and the details may change without warning.

### --byterate **byterate**

The byterate is the number of bytes per ticks (a tick is 1/60th of a second) that are available to encode the video stream. 370 additional bytes are used for the sound, plus a handful of bytes overhead. When encoding the changes between two frames, flimmaker will use up to byterate bytes (not strictly true, but a good enough approximation of the process), and, if there is not enough bandwidth, will let part the previous frame leak into the next one.

Fundamentally the byterate is what makes a flim playable on specific machines. The rest of the options are there to control what information to throw away to meet this byterate with an acceptable image.

The Mac Plus is able to read and decode around 1500 bytes per tick, the Mac SE around 2500, and the Mac SE/30 6000.

You can play with this parameter if your Mac has a faster/slower drive (example: SE/30 from RAM disk). If the byterate is too high, you will suffer sound and video skips at playback, as your Mac will not able to fetch and decompress the data fast enough.

NOTE: as of today, the byterate is only the *video* byterate. 376 extra bytes are added for audio. This WILL PROBABLY change in the future.

### --fps-ratio **integer**

``--fps-ratio 2`` will effectively halve the framerate of the input, resulting in a worse looking, but smaller flim. If MacFlim has trouble displaying your flim, using ``--fps-ratio 2`` or higher can vastly improve the visual result.

The Mac Plus and Mac SE profiles are ``--fps-ratio 2`` by default, while the SE/30 displays all the frames. The 512K and XL profiles use ``--fps-ratio 4`` to compensate for the extremely low byterate due to the slow drives.

### --group **boolean**

Using ``--group false`` will have the player display partially constructed frames every 60th of a second. Due to limitations in the hardware/the way MacFlim works, the Mac Plus and the Mac SE cannot group the frames, and you can see the construction on screen. The SE/30 doesn't have to display partial results, which results more stable display. However, one can use ``--group false`` for the SE/30 to get some interesting low-fidelity effects.

The Mac Plus and the Mac SE profiles are not grouped, while the SE/30 is.

### --dither **ordered**|**blue**|**error**

The conversion of the image to black and white can be done using ``ordered`` dithering, ``blue`` noise dithering, or ``error`` diffusion. In general, the ``error`` ordering will give the typical original *MacFlim* look. However, the number of pixels changing from one frame to another is higher, so it uses more bandwidth for encoding. The ``blue`` noise option provides a good tradeoff between the regularity of ``ordered`` and the crispness of ``error``, using a 256×256 texture for high-quality results. For flims composed of only large flat regular zones, ``ordered`` encoding may give the nicest results.

The Mac Plus uses ``ordered`` encoding by default while the Mac SE and SE/30 use the ``error`` encoding (floyd algorithm).

### --error-stability **double**

When using the ``error`` dithering, a small change in some part of consecutive frames can lead to very different dithering patterns. This is visually distracting and also consumes bandwidth.

The stability parameter makes the dithered pattern match the preceding frame more closely. A small stability will make images change a lot between frames, and a high stability will cause artifacts.

While the Mac Plus does not use ``error`` dithering, its default ``stability`` is 0.5. The Mac SE also uses a ``stability`` of 0.5. The Mac SE/30 uses a ``stability`` of 0.3.

The minimum stability is 0.

### --error-algorithm **ALGORITHM**

When using the ``error`` dithering, there are a small set of error dithering algorithms that can be used. 'floyd' is the most common, but there are other choices. Launch ``flimmaker`` with no arguments for a list.

### --error-bleed **percent** (from 0 to 1)

The error propagation of the ``error`` diffusion algorithm can be toned down by the bleed, which specifies how much of the error should be propagated to neighboring pixels. Lower values of bleed make changes more local, but impacts image quality. Bleed is 0.98 for the Plus, 0.99 for the SE and SE/30. You can use 1 for full bleed, the original MacFlim look (see ``--error-bidi`` too)

### --error-bidi **boolean**

If ``--error-bidi`` is true, then all the ``error`` dithering algorithms will diffuse the error in alternate directions for each scanline, greatly reducing the impression of having "crawling pixels from the bottom right". ``--error-bidi`` is true by default, but is kept to be able to recover the original pixel crawling MacFlim 1.0 look.

### --filters **gibberish**

After converting the input image to 512x342 grayscale, the encoder applies a series of filters, before dithering the image to pure black and white.

Each filter is a single letter, with an optional numeric parameter. A filter can be specified twice, in which case it will be applied twice. There are no spaces. As an example, ``--filters g1.8b5scz`` means gamma 1.8, blur 5x5, sharpen, add corners to the frame, and reduce it slightly (i.e., add a black border).

Specifying a ``--filters`` argument completely replaces the default filters from the current profile, so run ``flimmaker`` without arguments to know what the defaults are, or look at the first lines of the log of ``flimmaker``.

Filter list:

* Blur 'b' (size): Blurs the image by averaging the neighboring pixels. The argument is the size of the filter, '3' for a 3x3 grid (default) or '5' for a 5x5 grid. No other sizes are supported. As the spatial resolution of the image is used to encode the dithering, blurring the image often doesn't lead to a visible loss of quality and generally enables a better encoding by smoothing out details.

* Sharpen 's': Sharpens the image. It is a good idea to sharpen the image after blurring. This helps to have more defined zones, which, again, helps the encoding.

* Gamma 'g' (value): Applies a gamma transformation to the image. The higher the gamma, the darker the resulting image. Default gamma is 1.6.

* Round Corners 'c': Removes the corners, and produce a lovely period-accurate, rounded-cornered image. Please add it at the end of your filters, so we respect the screen corners!

* Zoom smaller 'z' (size): The image is zoomed out so that there are 'size' black pixels on each side (2*'size' pixels in total horizontally). As a result, the encoding is slightly more efficient. Use several 'z' to get an even smaller image. Default 'size' is 32.

* Zoom larger 'Z' (size): The 'size' leftmost and rightmost pixels of the images are dropped, and the resulting image is zoomed in. One can use 'Zz' (or better, 'Zcz') to punch a 32-pixel-wide black frame around the video. Default 'size' is 32.

* Invert 'i': Inverts the image; the black pixels become white and the white become black. 'Zizi' adds a white border to the image, much to the hilarity of the most immature members of the French-speaking crowd.

* Flip 'f': Horizontally flips the image. This can be useful when creating flims that can appear in the background of YouTube videos, as it creates fewer spurious copyright strikes from the YouTube IA.

* Quantize 'q' (steps): Quantizes the colors so there are only 17 of them. This can help when encoding images of flat colors to avoid spurious gradients. It is particularly useful when using ordered dithering, as there are only 17 different dithering patterns. Using a lower number can create interesting effects: for instance, q5 will generate images with only black, dark gray, pure gray, light gray and white colors. q2 will posterize the image into black and white, rendering dithering inoperative (useful for pure 2-color black and white sources).

* Black 'k' (percent): Remove the darkest part of the image. Often movies have black backgrounds that are not completely black. The dithering algorithm represents this by having a few white pixels in large black areas, which is visually distracting (and eats encoding bandwidth). The black filters collapses the darkest pixels into pure black. The rest of the image color is scaled to the remaining color range. 'percent' should be between 0 and 100. By default the black filter removes the darkest 6.25% of pixels.

* White 'w' (percent): Same as the ``black`` filter, but for white pixels. This is a slightly less frequent issue, as large pure white areas are rarer in movies. 'percent' should be between 0 and 100. By default the white filters removes the lightest 6.25% of pixels.