# MobileVLCKit: Popvue's own VLCKit build

Popvue plays video with VideoLAN's [VLCKit](https://code.videolan.org/videolan/VLCKit) 3.6.0,
built on libVLC 3.0.21. This folder is a local Swift package that hands it to the app:

- `MobileVLCKit.xcframework`: the framework itself, with a device slice (`ios-arm64`) and a
  simulator slice (`ios-arm64_x86_64-simulator`). It is about 540 MB, so it is not kept in git
  (see `.gitignore`); `Scripts/vlckit/build-vlckit.sh` builds it from VideoLAN's source.
- `Package.swift`: the binary target, and the `PopvueVLCKit` product the app links. That product
  re-exports `MobileVLCKit` (`Sources/PopvueVLCKit`), so the app's code says `import MobileVLCKit`
  and links the same system frameworks and libraries VideoLAN's prebuilt package did.

Xcode embeds and signs `MobileVLCKit.framework` in the app. The app's "Check VLCKit licences"
build phase then runs `Scripts/vlckit/check-vlckit-licences.sh` on the embedded binary and stops
the build if zvbi or any of libVLC's GPL code is ever in it (below).

## A fresh clone

The xcframework is not in git, so on a new Mac, or after a fresh clone, Xcode cannot resolve the
packages and says:

```
local binary target 'MobileVLCKit' at '…/Vendor/MobileVLCKit/MobileVLCKit.xcframework' does not contain a binary artifact.
```

Build it once, then build the app again (in Xcode, File > Packages > Resolve Package Versions
if the error stays):

```sh
Scripts/vlckit/build-vlckit.sh            # work directory /Users/Shared/VLCKit-Popvue
Scripts/vlckit/build-vlckit.sh /some/dir  # or anywhere with room
```

libVLC's build writes the work directory's path into the binary -- its configure line, and the
names of its source files -- so build the one the app ships outside your home folder, as the
default is: under it, every copy of the app would carry your user name.

## Why build it ourselves

VideoLAN's prebuilt MobileVLCKit 3.6.0 (which the app used to take from the `vlckit-spm` Swift
package) has GPL code in it, which has no place in an App Store app:

- zvbi, for teletext. zvbi is mostly LGPL, but its teletext packet 8/30 decoder
  (`src/packet-830.c`) is under the GPL version 2 only.
- Seven of libVLC's modules whose sources are under the GPL 2 or later, with no LGPL wording:
  the Dolby Surround decoder (`audio_filter/channel_mixer/dolby.c`), the mono downmixer
  (`mono.c`), the dummy interface (`control/dummy.c`), rotate (`video_filter/rotate.c`, with
  the motion library `control/motionlib.c` it links), RTP stream output (`stream_out/rtp.c`),
  the syslog logger (`logger/syslog.c`) and VLM's RTSP video-on-demand server (`misc/rtsp.c`).
  VideoLAN's build blacklists libVLC's twenty other GPL modules (hotkeys, hqdn3d, motion,
  remoteosd and the rest) but not these.
- yadif, two of the deinterlace module's modes: `deinterlace/yadif.h`, which `algo_yadif.c`
  includes, comes from MPlayer under the GPL 2 or later. VideoLAN relicensed its playback modules
  to the LGPL in 2012 (vlc commit `fef270581f`) with their contributors' consent, and its message names
  yadif, hqdn3d, mono, dolby, rotate and remoteosd among the code it could not.

Popvue's build leaves all of it out, and nothing the app does goes with it: deinterlacing keeps
its default, auto (the x method), and its other modes; the Dolby Surround decoder only works for
Dolby Surround stereo played to more than two speakers; mono and rotate are filters loaded only
when asked for by name, which the app never does; and the rest is streaming out, a logger that
needs `--syslog`, and VLC's own interfaces, none of which VLCKit's player uses.

Teletext subtitles are read by telx, VLC's own teletext decoder, in zvbi's place. Its source,
`modules/codec/telx.c`, is under the LGPL 2.1 or later: the 2012 relicensing moved it from the
GPL with its contributors' consent. Its header also says some of its code was converted from
ProjectX, a Java DVB tool under the GPL, whose author is dvb.matt; the relicensing does not say
whether he agreed, nor which code that is. If that is a doubt worth removing, `--disable-telx` in place of `--enable-telx` in patch 0001 leaves telx out too, and
teletext subtitles with it (DVB subtitles stay); or ask VideoLAN.

Everything else in the binary is VideoLAN's build: VLCKit and libVLC under the LGPL 2.1, and the
libraries libVLC's contrib builds in, each LGPL or permissively licensed. Their licences are on
the app's Acknowledgements page (`Scripts/generate_acknowledgements.py`, with the texts and the
list of libraries the binary links in `Scripts/vlckit-licences`).

The public API is VideoLAN's: the exported libVLC and VLCKit symbols of this build and of
VideoLAN's prebuilt 3.6.0 are the same list.

## What the build does

`build-vlckit.sh` clones VLCKit into its work directory, checks out tag `3.6.0` (commit
`c73b779f`), applies the eight patches in `Scripts/vlckit` with `git am` on a branch
`popvue-lgpl`, and runs VideoLAN's own `buildMobileVLCKit.sh -f` (device and simulator, a
dynamic xcframework). VLCKit's build fetches libVLC at the revision it pins (`TESTEDHASH`,
`dd8bfdba`, vlc's 3.0.21 tag), applies VLCKit's own libVLC patches, then downloads and compiles
every contrib library from source. Finally the licence checks run -- `find-gpl-sources.py` on the
build, which reads every libVLC source and header compiled into what it links and stops at any
under the GPL, then `check-vlckit-licences.sh` on the binary, which looks for the GPL code known
by name -- and the xcframework is copied here.

| Patch | What it changes |
| --- | --- |
| `0001` build without zvbi | `--disable-zvbi` (contribs and libVLC) and `--enable-telx`; keeps four of newer clang's default errors (implicit declarations, int and pointer conversions) as warnings, as the older contrib code needs |
| `0002` iOS 12 minimum | Xcode 16 and later cannot target iOS 9, VLCKit's minimum; the app needs iOS 15 |
| `0003` sha512sum | macOS 15's BSD `sha512sum` cannot check a list read from stdin, which VLC's tools and contrib builds rely on; a GNU-style one in front of it runs `shasum -a 512` |
| `0004` zlib | a libVLC contrib patch: zlib 1.2.13 defines `fdopen` away whenever `TARGET_OS_MAC` is set, which breaks with the iOS 18 and later SDKs (zlib 1.3 dropped the test). It changes a line of zlib's `zutil.h`, so the Acknowledgements mark zlib as altered, as its licence asks |
| `0005` libpng | a libVLC contrib patch: libpng 1.6.37 includes classic Mac OS's `<fp.h>` whenever `TARGET_OS_MAC` is set (libpng 1.6.44 dropped the test). It changes a line of libpng's `pngpriv.h`, so libpng is marked altered too |
| `0006` libgcrypt | a libVLC contrib patch: on the x86_64 simulator newer clang passes libgcrypt's Windows x64 assembler test, so its AES code takes the Win64 path and fails to assemble |
| `0007` GPL modules | adds libVLC's seven GPL modules above to the blacklist `buildMobileVLCKit.sh` removes plugins by, each by its full file name, as the list takes every file whose name holds an entry (`dummy` alone would take nine LGPL dummy modules too) |
| `0008` yadif | a libVLC patch: the deinterlace module without `algo_yadif.c` and the yadif headers, and libVLC's lists of deinterlace modes without yadif and yadif2x; a yadif mode asked for anyway is refused by `libvlc_video_set_deinterlace` and the video output, and the filter takes auto's x |

Patches 0004 to 0006 and 0008 add files to VLCKit's `libvlc/patches`, which VLCKit's build
applies to libVLC on top of its own. The libVLC this build embeds therefore reports itself as
`3.0.21-53-g…`: vlc 3.0.21, then VLCKit's 49 patches and Popvue's 4 (the hash differs from one
build to the next, as `git am` dates the commits it makes). VideoLAN's prebuilt one is
`3.0.21-49-gd1840ca85f`.

It takes about an hour on an Apple M4 (10 cores), most of it compiling the contribs; the work
directory ends up at about 12 GB (5.5 GB of it contribs for the three slices). It needs Xcode
with the iOS and simulator SDKs, git, and network access to code.videolan.org, videolan.org's
download mirrors and the hosts of the contrib libraries.

## The LGPL

VLCKit and libVLC are used under the GNU Lesser General Public License 2.1, and FFmpeg, FriBidi
and the other LGPL libraries inside them too. Shipping a modified build means its complete
source must be available to anyone who has the app (or a written offer of it, good for three
years), and VideoLAN's repositories do not hold Popvue's changes. Before the app ships, publish
on Popvue's website:

- VLCKit at tag `3.6.0`, with the eight patches here, `build-vlckit.sh` and this README;
- libVLC at vlc's `3.0.21` tag (`dd8bfdba`), with VLCKit's 49 patches (in its `libvlc/patches`)
  and Popvue's four (inside patches 0004 to 0006 and 0008);
- the contrib tarballs the build downloads (`libvlc/vlc/contrib/tarballs` in the work
  directory), as their own hosts may not keep them.

Then set the page's address in `SupportLinks.vlcKitBuildSource`: the Acknowledgements page,
which says Popvue uses its own build and what it changes and names VideoLAN's sources, gives the
address too, with a row opening it. Until then every Release build warns that it is nil.

## Updating VLCKit later

1. Set `VLCKIT_TAG` and `VLCKIT_COMMIT` in `build-vlckit.sh` to the new release.
2. Re-derive the patches: in the work directory's `vlckit`, check out the new tag on
   `popvue-lgpl` and `git am` the old patches one by one. Keep 0001 (the zvbi switches may have
   moved in `buildMobileVLCKit.sh`), 0007 (unless VideoLAN's own blacklist now has its modules)
   and 0008; drop any build fix the new release no longer needs (a newer zlib, libpng or
   libgcrypt in contrib, a newer minimum iOS) and fix the rest. Then replace the files here with
   `git format-patch <tag>..popvue-lgpl -o Scripts/vlckit`.
3. Run `build-vlckit.sh`. It stops if `find-gpl-sources.py` finds a libVLC source or header
   under the GPL in what the build links, or `check-vlckit-licences.sh` finds zvbi or GPL code
   it knows in the binary. A module the first names that is new to the list goes on 0007's
   blacklist, and its entry point (`nm -U` on the binary lists them, `_vlc_entry__…`) on
   `check-vlckit-licences.sh`'s.
4. Compare the API with VideoLAN's prebuilt binary of the same release, so the app's code keeps
   working: `nm -gU <binary> | grep -E ' _(libvlc_|VLC|kVLC|OBJC_CLASS_\$_VLC)'` on both should
   list the same symbols.
5. Refresh the acknowledgements: in `Scripts/vlckit-licences/manifest.json`, list the libraries
   the new binary links (each contrib library's own symbols found in the binary, and the
   configure lines it embeds) at the versions the new libVLC's `contrib/src/*/rules.mak` give,
   take their licence texts again from the contrib tarballs (and `vlckit.txt` from VLCKit's
   `COPYING`), and set `vlckit`, `libvlc` and `libvlcBuild` to the new tag and to what the
   binary reports (`strings` on it shows `3.0.21-53-g…`; the pin leaves out the hash). Mark a
   library a patch alters (`"altered"`, as zlib and libpng are) and unmark one whose patch is
   dropped. The page links libVLC's source at its release tag, which VLCKit 3.6.0's
   `TESTEDHASH` is; should a later VLCKit pin a revision between tags, link that revision in the
   generator instead. Then run `python3 Scripts/generate_acknowledgements.py <SourcePackages>`, which stops
   while any of them disagrees with the binary.
6. Build the app: the "Check VLCKit licences" phase must print
   `check-vlckit-licences: no zvbi and no GPL libVLC code in MobileVLCKit`.
7. Publish the new source and build recipe at Popvue's address for it before shipping the build.
