1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
|
# ๐๏ธ Multitrack Support in Liquidsoap
Starting in version 2.2.0, Liquidsoap gained support for **multitrack operations** โ a powerful addition that lets you work with individual tracks inside your media files. Whether it's audio, video, or metadata, you can now demux, remux, encode, decode, apply filters, and more โ all at the track level!
This unlocks advanced media workflows, like keeping multiple language tracks, adding a default video stream to audio-only files, or even mixing and matching content across sources.
Letโs walk through what this means, step by step ๐
## ๐ง What is a Track?
A media file can contain multiple โtracks.โ Think of a movie file with:
- ๐ง English and French audio tracks
- ๐ฅ One video track
- ๐ Metadata like title and artist
In Liquidsoap, these tracks are made accessible through operators that let you manipulate them individually. This opens up many possibilities โ but also introduces a few new concepts and conventions to learn.
## ๐ง Requirements: When You Need FFmpeg
Some multitrack features rely on FFmpeg, while others don't:
| Feature | Requires FFmpeg? |
| ----------------------------------------------- | -------------------------------- |
| Track-level encode/decode | โ
Yes |
| Encode or decode multiple audio or video tracks | โ
Yes |
| Demuxing/remuxing tracks | โ No |
| Source track manipulation | โ No (unless decoding/encoding) |
To fully unlock multitrack functionality, make sure your Liquidsoap is compiled with FFmpeg support.
## ๐ฌ Using Multitrack Media
Letโs say you have a media file with multiple tracks, like:
```sh
movie.mkv
โโโ audio (English)
โโโ audio_2 (French)
โโโ video
```
You can create a source with this file like so:
```liquidsoap
s = single("/path/to/movie.mkv")
```
By default, only the first audio and video track will be used:
```liquidsoap
output.file(%ffmpeg(%audio.copy, %video.copy), "/path/to/copy.mkv", s)
```
๐ชต **Logs will confirm the detected tracks:**
```
[output_file:3] Content type is {audio=ffmpeg.copy,video=ffmpeg.copy}.
[decoder.ffmpeg:3] Requested content-type for "/path/to/movie.mkv": {audio=ffmpeg.copy,video=ffmpeg.copy}
[decoder.ffmpeg:3] FFmpeg recognizes "/path/to/movie.mkv" as video: {codec: h264, 1920x1038, yuv420p}, audio: {codec: aac, 48000Hz, 6 channel(s)}, audio_2: {codec: aac, 48000Hz, 6 channel(s)}
[decoder.ffmpeg:3] Decoded content-type for "/path/to/movie.mkv": {audio=ffmpeg.copy(codec="aac",channel_layout="5.1",sample_format=fltp,sample_rate=48000),video=ffmpeg.copy(codec="h264",width=1920,height=1038,aspect_ratio=1/1,pixel_format=yuv420p)}
```
Here, `audio_2` is present but unused. Letโs fix that ๐
## ๐๏ธ Custom Track Handling
What if you want to **keep both audio tracks**, re-encoding the second one to stereo?
```liquidsoap
output.file(
%ffmpeg(
%audio.copy,
%audio_2(channels=2, codec="aac"),
%video.copy
),
"/path/to/copy.mkv",
s
)
```
๐ชต Logs now show `audio_2` being processed:
```
[output_file:3] Content type is {audio=ffmpeg.copy,audio_2=pcm(stereo),video=ffmpeg.copy}.
[decoder.ffmpeg:3] Requested content-type for "/path/to/movie.mkv": {audio=ffmpeg.copy,audio_2=pcm(stereo),video=ffmpeg.copy}
[decoder.ffmpeg:3] FFmpeg recognizes "/path/to/movie.mkv" as video: {codec: h264, 1920x1038, yuv420p}, audio: {codec: aac, 48000Hz, 6 channel(s)}, audio_2: {codec: aac, 48000Hz, 6 channel(s)}
[decoder.ffmpeg:3] Decoded content-type for "/path/to/movie.mkv": {audio=ffmpeg.copy(codec="aac",channel_layout="5.1",sample_format=fltp,sample_rate=48000),audio_2=pcm(5.1),video=ffmpeg.copy(codec="h264",width=1920,height=1038,aspect_ratio=1/1,pixel_format=yuv420p)}
```
You now have a file with two audio tracks (one copied, one re-encoded) and one video track ๐
## โ ๏ธ Playlist Caveats
If your source is a playlist:
```liquidsoap
s = playlist("/path/to/playlist")
```
โฆand you request multiple tracks:
```liquidsoap
output.file(
fallible=true,
%ffmpeg(%audio.copy, %audio_2(...), %video.copy),
"/path/to/copy.mkv",
s
)
```
Then **only files with all requested tracks** (audio + audio_2 + video) will be accepted. Others will be skipped.
## ๐งญ Track Naming Conventions
When decoding, track names follow this pattern:
- `audio`, `audio_2`, `audio_3`, ...
- `video`, `video_2`, `video_3`, ...
These names are fixed by the decoder โ you **canโt** rename them directly when reading media.
For example, this will fail:
```liquidsoap
output.file(%ffmpeg(%audio_fr.copy, %audio_en(...), "/path/to/file.mkv", playlist("...")))
```
Why? Because the decoder doesnโt know what `audio_fr` or `audio_en` means. Track names must match what the decoder emits: `audio`, `audio_2`, etc.
Once you're remuxing tracks, however, you can assign **any name** you want!
## ๐ Demuxing and Remuxing Tracks
To extract and rebuild track sets:
```liquidsoap
s = playlist(...)
let {audio, video, metadata, track_marks} = source.tracks(s)
```
You can then remix these into a new source:
```liquidsoap
s = source({
audio = audio,
video = video,
metadata = metadata,
track_marks = track_marks
})
```
Tracks can also be added or replaced:
```liquidsoap
s = source(source.tracks(s).{video = source.tracks(image).video})
```
One limitation is that it is not currently possible to add default tracks.
The following **won't work**:
```liquidsoap
video = source.tracks(s).video ?? source.tracks(image).video
```
## ๐งน Cleaning Up Tracks
Want to remove `track_marks`?
```liquidsoap
let {track_marks=_, ...tracks} = source.tracks(s)
s = source(tracks)
```
This is equivalent to the older `drop_tracks` operator.
## ๐ Track-Level Operators
Many operators now work directly on tracks. Examples:
```liquidsoap
mono = track.audio.mean(audio_track)
encoded = track.ffmpeg.encode.audio(%ffmpeg(%audio(codec="aac")), audio_track)
```
๐จ But beware: some operators (like inline encoders) put the track on a new **clock**. Youโll need to re-derive metadata and track marks from the new track to avoid clock conflicts:
```liquidsoap
let encoded_audio = track.ffmpeg.encode.audio(..., audio)
s = source({
audio = encoded_audio,
metadata = track.metadata(encoded_audio),
track_marks = track.track_marks(encoded_audio)
})
```
## ๐ท๏ธ How Encoders Detect Track Types
Liquidsoap uses **naming conventions** and **hints** to determine what kind of data each track holds:
Priority order:
1. `%audio.copy` or `%video.copy` โ auto-detect
2. Named content type (`audio_content` or `video_content`)
3. Track name contains โaudioโ or โvideoโ
4. Codec implies type
### โ
Example: Explicit typing
```liquidsoap
output.file(
%ffmpeg(
%en(audio_content, codec=audio_codec),
%fr(codec="aac"),
%director_cut(video_content, codec=video_codec)
),
"/path/to/copy.mkv",
s
)
```
### โ
Or by naming convention
```liquidsoap
%audio_en(codec=...)
%director_cut_video(codec=...)
```
Internally, Liquidsoap maps this to content-type info. Once handed off to FFmpeg, **track names are lost** โ FFmpeg just sees numbered tracks in order.
## ๐ Summary
Multitrack support opens up powerful new workflows:
- ๐ Mix and match audio/video/metadata across sources
- ๐งฑ Build custom media containers
- ๐ฏ Target different formats per track
- ๐งช Combine synchronous and asynchronous operations (with care!)
Weโve only scratched the surface โ go ahead and explore the code, experiment, and let your creativity flow! ๐ก
And if thereโs an operator you wish worked at the track level, donโt hesitate to [open a feature request](https://github.com/savonet/liquidsoap)!
|