File: multitrack.md

package info (click to toggle)
liquidsoap 2.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 12,372 kB
  • sloc: ml: 71,806; javascript: 27,320; ansic: 398; xml: 114; sh: 99; lisp: 96; makefile: 26
file content (245 lines) | stat: -rw-r--r-- 7,888 bytes parent folder | download
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)!