/src/mpv/audio/out/ao_null.c
Line | Count | Source |
1 | | /* |
2 | | * null audio output driver |
3 | | * |
4 | | * This file is part of mpv. |
5 | | * |
6 | | * mpv is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * mpv is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with mpv. If not, see <http://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | /* |
21 | | * Note: this does much more than just ignoring audio output. It simulates |
22 | | * (to some degree) an ideal AO. |
23 | | */ |
24 | | |
25 | | #include <stdio.h> |
26 | | #include <stdlib.h> |
27 | | #include <math.h> |
28 | | |
29 | | #include "mpv_talloc.h" |
30 | | |
31 | | #include "osdep/timer.h" |
32 | | #include "options/m_option.h" |
33 | | #include "common/common.h" |
34 | | #include "common/msg.h" |
35 | | #include "audio/format.h" |
36 | | #include "ao.h" |
37 | | #include "internal.h" |
38 | | |
39 | | struct priv { |
40 | | bool paused; |
41 | | double last_time; |
42 | | float buffered; // samples |
43 | | int buffersize; // samples |
44 | | bool playing; |
45 | | |
46 | | bool untimed; |
47 | | float bufferlen; // seconds |
48 | | float speed; // multiplier |
49 | | float latency_sec; // seconds |
50 | | float latency; // samples |
51 | | bool broken_eof; |
52 | | bool broken_delay; |
53 | | |
54 | | // Minimal unit of audio samples that can be written at once. If play() is |
55 | | // called with sizes not aligned to this, a rounded size will be returned. |
56 | | // (This is not needed by the AO API, but many AOs behave this way.) |
57 | | int outburst; // samples |
58 | | |
59 | | struct m_channels channel_layouts; |
60 | | int format; |
61 | | }; |
62 | | |
63 | | static void drain(struct ao *ao) |
64 | 10.9M | { |
65 | 10.9M | struct priv *priv = ao->priv; |
66 | | |
67 | 10.9M | if (ao->untimed) { |
68 | 10.9M | priv->buffered = 0; |
69 | 10.9M | return; |
70 | 10.9M | } |
71 | | |
72 | 0 | if (priv->paused) |
73 | 0 | return; |
74 | | |
75 | 0 | double now = mp_time_sec(); |
76 | 0 | if (priv->buffered > 0) { |
77 | 0 | priv->buffered -= (now - priv->last_time) * ao->samplerate * priv->speed; |
78 | 0 | if (priv->buffered < 0) |
79 | 0 | priv->buffered = 0; |
80 | 0 | } |
81 | 0 | priv->last_time = now; |
82 | 0 | } |
83 | | |
84 | | static int init(struct ao *ao) |
85 | 30.4k | { |
86 | 30.4k | struct priv *priv = ao->priv; |
87 | | |
88 | 30.4k | if (priv->format) |
89 | 0 | ao->format = priv->format; |
90 | | |
91 | 30.4k | ao->untimed = priv->untimed; |
92 | | |
93 | 30.4k | struct mp_chmap_sel sel = {.tmp = ao}; |
94 | 30.4k | if (priv->channel_layouts.num_chmaps) { |
95 | 0 | for (int n = 0; n < priv->channel_layouts.num_chmaps; n++) |
96 | 0 | mp_chmap_sel_add_map(&sel, &priv->channel_layouts.chmaps[n]); |
97 | 30.4k | } else { |
98 | 30.4k | mp_chmap_sel_add_any(&sel); |
99 | 30.4k | } |
100 | 30.4k | if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) |
101 | 0 | mp_chmap_from_channels(&ao->channels, 2); |
102 | | |
103 | 30.4k | priv->latency = priv->latency_sec * ao->samplerate; |
104 | | |
105 | | // A "buffer" for this many seconds of audio |
106 | 30.4k | int bursts = (int)(ao->samplerate * priv->bufferlen + 1) / priv->outburst; |
107 | 30.4k | ao->device_buffer = priv->outburst * bursts + priv->latency; |
108 | | |
109 | 30.4k | priv->last_time = mp_time_sec(); |
110 | | |
111 | 30.4k | return 0; |
112 | 30.4k | } |
113 | | |
114 | | // close audio device |
115 | | static void uninit(struct ao *ao) |
116 | 30.4k | { |
117 | 30.4k | } |
118 | | |
119 | | // stop playing and empty buffers (for seeking) |
120 | | static void reset(struct ao *ao) |
121 | 17.5k | { |
122 | 17.5k | struct priv *priv = ao->priv; |
123 | 17.5k | priv->paused = false; |
124 | 17.5k | priv->buffered = 0; |
125 | 17.5k | priv->playing = false; |
126 | 17.5k | } |
127 | | |
128 | | static void start(struct ao *ao) |
129 | 29.5k | { |
130 | 29.5k | struct priv *priv = ao->priv; |
131 | | |
132 | 29.5k | if (priv->paused) |
133 | 29.5k | MP_ERR(ao, "illegal state: start() while paused\n"); |
134 | | |
135 | 29.5k | drain(ao); |
136 | 29.5k | priv->paused = false; |
137 | 29.5k | priv->last_time = mp_time_sec(); |
138 | 29.5k | priv->playing = true; |
139 | 29.5k | } |
140 | | |
141 | | static bool set_pause(struct ao *ao, bool paused) |
142 | 2 | { |
143 | 2 | struct priv *priv = ao->priv; |
144 | | |
145 | 2 | if (!priv->playing) |
146 | 2 | MP_ERR(ao, "illegal state: set_pause() while not playing\n"); |
147 | | |
148 | 2 | if (priv->paused != paused) { |
149 | | |
150 | 2 | drain(ao); |
151 | 2 | priv->paused = paused; |
152 | 2 | if (!priv->paused) |
153 | 1 | priv->last_time = mp_time_sec(); |
154 | 2 | } |
155 | | |
156 | 2 | return true; |
157 | 2 | } |
158 | | |
159 | | static bool audio_write(struct ao *ao, void **data, int samples) |
160 | 7.51M | { |
161 | 7.51M | struct priv *priv = ao->priv; |
162 | | |
163 | 7.51M | if (priv->buffered <= 0) |
164 | 7.51M | priv->buffered = priv->latency; // emulate fixed latency |
165 | | |
166 | 7.51M | priv->buffered += samples; |
167 | 7.51M | return true; |
168 | 7.51M | } |
169 | | |
170 | | static void get_state(struct ao *ao, struct mp_pcm_state *state) |
171 | 10.9M | { |
172 | 10.9M | struct priv *priv = ao->priv; |
173 | | |
174 | 10.9M | drain(ao); |
175 | | |
176 | 10.9M | state->free_samples = ao->device_buffer - priv->latency - priv->buffered; |
177 | 10.9M | state->free_samples = state->free_samples / priv->outburst * priv->outburst; |
178 | 10.9M | state->queued_samples = priv->buffered; |
179 | | |
180 | | // Note how get_state returns the delay in audio device time (instead of |
181 | | // adjusting for speed), since most AOs seem to also do that. |
182 | 10.9M | state->delay = priv->buffered; |
183 | | |
184 | | // Drivers with broken EOF handling usually always report the same device- |
185 | | // level delay that is additional to the buffer time. |
186 | 10.9M | if (priv->broken_eof && priv->buffered < priv->latency) |
187 | 0 | state->delay = priv->latency; |
188 | | |
189 | 10.9M | state->delay /= ao->samplerate; |
190 | | |
191 | 10.9M | if (priv->broken_delay) { // Report only multiples of outburst |
192 | 0 | double q = priv->outburst / (double)ao->samplerate; |
193 | 0 | if (state->delay > 0) |
194 | 0 | state->delay = (int)(state->delay / q) * q; |
195 | 0 | } |
196 | | |
197 | 10.9M | state->playing = priv->playing && priv->buffered > 0; |
198 | 10.9M | } |
199 | | |
200 | | #define OPT_BASE_STRUCT struct priv |
201 | | |
202 | | const struct ao_driver audio_out_null = { |
203 | | .description = "Null audio output", |
204 | | .name = "null", |
205 | | .init = init, |
206 | | .uninit = uninit, |
207 | | .reset = reset, |
208 | | .get_state = get_state, |
209 | | .set_pause = set_pause, |
210 | | .write = audio_write, |
211 | | .start = start, |
212 | | .priv_size = sizeof(struct priv), |
213 | | .priv_defaults = &(const struct priv) { |
214 | | .bufferlen = 0.2, |
215 | | .outburst = 256, |
216 | | .speed = 1, |
217 | | }, |
218 | | .options = (const struct m_option[]) { |
219 | | {"untimed", OPT_BOOL(untimed)}, |
220 | | {"buffer", OPT_FLOAT(bufferlen), M_RANGE(0, 100)}, |
221 | | {"outburst", OPT_INT(outburst), M_RANGE(1, 100000)}, |
222 | | {"speed", OPT_FLOAT(speed), M_RANGE(0, 10000)}, |
223 | | {"latency", OPT_FLOAT(latency_sec), M_RANGE(0, 100)}, |
224 | | {"broken-eof", OPT_BOOL(broken_eof)}, |
225 | | {"broken-delay", OPT_BOOL(broken_delay)}, |
226 | | {"channel-layouts", OPT_CHANNELS(channel_layouts)}, |
227 | | {"format", OPT_AUDIOFORMAT(format)}, |
228 | | {0} |
229 | | }, |
230 | | .options_prefix = "ao-null", |
231 | | }; |