logo

overlay

My own overlay for experimentations, use with caution, no support is provided git clone https://hacktivis.me/git/overlay.git

mpv-0.33.1-sndio_pr9298.patch (11999B)


  1. From d41e8d39dd1de81aa5f56bb1aa4d9fffc1a2ef3d Mon Sep 17 00:00:00 2001
  2. From: rim <rozhuk.im@gmail.com>
  3. Date: Mon, 18 Oct 2021 23:55:49 +0200
  4. Subject: [PATCH] ao_sndio: add this audio output again
  5. Changes:
  6. - rewrite to use new internal MPV API;
  7. - code refactoring;
  8. - fix buffers size calculations;
  9. - buffer set to auto;
  10. - reset() - clean/reinit device only after errors;
  11. ---
  12. DOCS/man/ao.rst | 6 +
  13. audio/out/ao.c | 4 +
  14. audio/out/ao_sndio.c | 311 +++++++++++++++++++++++++++++++++++++++++++
  15. wscript | 6 +
  16. wscript_build.py | 1 +
  17. 5 files changed, 328 insertions(+)
  18. create mode 100644 audio/out/ao_sndio.c
  19. diff --git a/DOCS/man/ao.rst b/DOCS/man/ao.rst
  20. index cc42ec23a6..1f9bf915b3 100644
  21. --- a/DOCS/man/ao.rst
  22. +++ b/DOCS/man/ao.rst
  23. @@ -216,5 +216,11 @@ Available audio output drivers are:
  24. ``no-waveheader`` option - with ``waveheader`` it's broken, because
  25. it will write a WAVE header every time the file is opened.
  26. +``sndio``
  27. + Audio output to the OpenBSD sndio sound system
  28. +
  29. + (Note: only supports mono, stereo, 4.0, 5.1 and 7.1 channel
  30. + layouts.)
  31. +
  32. ``wasapi``
  33. Audio output to the Windows Audio Session API.
  34. diff --git a/audio/out/ao.c b/audio/out/ao.c
  35. index 52a38b63be..00893c0eb3 100644
  36. --- a/audio/out/ao.c
  37. +++ b/audio/out/ao.c
  38. @@ -40,6 +40,7 @@ extern const struct ao_driver audio_out_audiounit;
  39. extern const struct ao_driver audio_out_coreaudio;
  40. extern const struct ao_driver audio_out_coreaudio_exclusive;
  41. extern const struct ao_driver audio_out_rsound;
  42. +extern const struct ao_driver audio_out_sndio;
  43. extern const struct ao_driver audio_out_pulse;
  44. extern const struct ao_driver audio_out_jack;
  45. extern const struct ao_driver audio_out_openal;
  46. @@ -83,6 +84,9 @@ static const struct ao_driver * const audio_out_drivers[] = {
  47. #endif
  48. #if HAVE_SDL2_AUDIO
  49. &audio_out_sdl,
  50. +#endif
  51. +#if HAVE_SNDIO
  52. + &audio_out_sndio,
  53. #endif
  54. &audio_out_null,
  55. #if HAVE_COREAUDIO
  56. diff --git a/audio/out/ao_sndio.c b/audio/out/ao_sndio.c
  57. new file mode 100644
  58. index 0000000000..ec2fb3bcb6
  59. --- /dev/null
  60. +++ b/audio/out/ao_sndio.c
  61. @@ -0,0 +1,311 @@
  62. +/*
  63. + * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
  64. + * Copyright (c) 2013 Christian Neukirchen <chneukirchen@gmail.com>
  65. + * Copyright (c) 2020 Rozhuk Ivan <rozhuk.im@gmail.com>
  66. + *
  67. + * Permission to use, copy, modify, and distribute this software for any
  68. + * purpose with or without fee is hereby granted, provided that the above
  69. + * copyright notice and this permission notice appear in all copies.
  70. + *
  71. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  72. + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  73. + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  74. + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  75. + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  76. + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  77. + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  78. + */
  79. +
  80. +#include "config.h"
  81. +
  82. +#include <sys/types.h>
  83. +#include <poll.h>
  84. +#include <errno.h>
  85. +#include <sndio.h>
  86. +
  87. +#include "options/m_option.h"
  88. +#include "common/msg.h"
  89. +
  90. +#include "audio/format.h"
  91. +#include "ao.h"
  92. +#include "internal.h"
  93. +
  94. +struct priv {
  95. + struct sio_hdl *hdl;
  96. + struct sio_par par;
  97. + int delay;
  98. + bool playing;
  99. + int vol;
  100. + int havevol;
  101. + struct pollfd *pfd;
  102. +};
  103. +
  104. +
  105. +static const struct mp_chmap sndio_layouts[] = {
  106. + {0}, /* empty */
  107. + {1, {MP_SPEAKER_ID_FL}}, /* mono */
  108. + MP_CHMAP2(FL, FR), /* stereo */
  109. + {0}, /* 2.1 */
  110. + MP_CHMAP4(FL, FR, BL, BR), /* 4.0 */
  111. + {0}, /* 5.0 */
  112. + MP_CHMAP6(FL, FR, BL, BR, FC, LFE), /* 5.1 */
  113. + {0}, /* 6.1 */
  114. + MP_CHMAP8(FL, FR, BL, BR, FC, LFE, SL, SR), /* 7.1 */
  115. + /* Above is the fixed channel assignment for sndio, since we need to
  116. + * fill all channels and cannot insert silence, not all layouts are
  117. + * supported.
  118. + * NOTE: MP_SPEAKER_ID_NA could be used to add padding channels. */
  119. +};
  120. +
  121. +static void uninit(struct ao *ao);
  122. +
  123. +
  124. +/* Make libsndio call movecb(). */
  125. +static void process_events(struct ao *ao)
  126. +{
  127. + struct priv *p = ao->priv;
  128. +
  129. + int n = sio_pollfd(p->hdl, p->pfd, POLLOUT);
  130. + while (poll(p->pfd, n, 0) < 0 && errno == EINTR);
  131. +
  132. + sio_revents(p->hdl, p->pfd);
  133. +}
  134. +
  135. +/* Call-back invoked to notify of the hardware position. */
  136. +static void movecb(void *addr, int delta)
  137. +{
  138. + struct ao *ao = addr;
  139. + struct priv *p = ao->priv;
  140. +
  141. + p->delay -= delta;
  142. +}
  143. +
  144. +/* Call-back invoked to notify about volume changes. */
  145. +static void volcb(void *addr, unsigned newvol)
  146. +{
  147. + struct ao *ao = addr;
  148. + struct priv *p = ao->priv;
  149. +
  150. + p->vol = newvol;
  151. +}
  152. +
  153. +static int init(struct ao *ao)
  154. +{
  155. + struct priv *p = ao->priv;
  156. + struct mp_chmap_sel sel = {0};
  157. + size_t i;
  158. + struct af_to_par {
  159. + int format, bits, sig;
  160. + };
  161. + static const struct af_to_par af_to_par[] = {
  162. + {AF_FORMAT_U8, 8, 0},
  163. + {AF_FORMAT_S16, 16, 1},
  164. + {AF_FORMAT_S32, 32, 1},
  165. + };
  166. + const struct af_to_par *ap;
  167. + const char *device = ((ao->device) ? ao->device : SIO_DEVANY);
  168. +
  169. + /* Opening device. */
  170. + MP_VERBOSE(ao, "Using '%s' audio device.\n", device);
  171. + p->hdl = sio_open(device, SIO_PLAY, 0);
  172. + if (p->hdl == NULL) {
  173. + MP_ERR(ao, "Can't open audio device %s.\n", device);
  174. + goto err_out;
  175. + }
  176. +
  177. + sio_initpar(&p->par);
  178. +
  179. + /* Selecting sound format. */
  180. + ao->format = af_fmt_from_planar(ao->format);
  181. +
  182. + p->par.bits = 16;
  183. + p->par.sig = 1;
  184. + p->par.le = SIO_LE_NATIVE;
  185. + for (i = 0; i < MP_ARRAY_SIZE(af_to_par); i++) {
  186. + ap = &af_to_par[i];
  187. + if (ap->format == ao->format) {
  188. + p->par.bits = ap->bits;
  189. + p->par.sig = ap->sig;
  190. + break;
  191. + }
  192. + }
  193. +
  194. + p->par.rate = ao->samplerate;
  195. +
  196. + /* Channels count. */
  197. + for (i = 0; i < MP_ARRAY_SIZE(sndio_layouts); i++) {
  198. + mp_chmap_sel_add_map(&sel, &sndio_layouts[i]);
  199. + }
  200. + if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
  201. + goto err_out;
  202. +
  203. + p->par.pchan = ao->channels.num;
  204. +#ifdef __FreeBSD__
  205. + /* OSS wrapper have bad defaults, overwrite it. */
  206. + p->par.appbufsz = ((p->par.rate * 25) / 1000); /* 25 ms. */
  207. +#endif
  208. + if (!sio_setpar(p->hdl, &p->par)) {
  209. + MP_ERR(ao, "couldn't set params\n");
  210. + goto err_out;
  211. + }
  212. +
  213. + /* Get current sound params. */
  214. + if (!sio_getpar(p->hdl, &p->par)) {
  215. + MP_ERR(ao, "couldn't get params\n");
  216. + goto err_out;
  217. + }
  218. + if (p->par.bps > 1 && p->par.le != SIO_LE_NATIVE) {
  219. + MP_ERR(ao, "swapped endian output not supported\n");
  220. + goto err_out;
  221. + }
  222. +
  223. + /* Update sound params. */
  224. + if (p->par.bits == 8 && p->par.bps == 1 && !p->par.sig) {
  225. + ao->format = AF_FORMAT_U8;
  226. + } else if (p->par.bits == 16 && p->par.bps == 2 && p->par.sig) {
  227. + ao->format = AF_FORMAT_S16;
  228. + } else if ((p->par.bits == 32 || p->par.msb) && p->par.bps == 4 && p->par.sig) {
  229. + ao->format = AF_FORMAT_S32;
  230. + } else {
  231. + MP_ERR(ao, "couldn't set format\n");
  232. + goto err_out;
  233. + }
  234. +
  235. + p->havevol = sio_onvol(p->hdl, volcb, ao);
  236. + sio_onmove(p->hdl, movecb, ao);
  237. +
  238. + p->pfd = talloc_array_ptrtype(p, p->pfd, sio_nfds(p->hdl));
  239. + if (!p->pfd)
  240. + goto err_out;
  241. +
  242. + ao->device_buffer = p->par.bufsz;
  243. + MP_VERBOSE(ao, "bufsz = %i, appbufsz = %i, round = %i\n",
  244. + p->par.bufsz, p->par.appbufsz, p->par.round);
  245. +
  246. + p->delay = 0;
  247. + p->playing = false;
  248. + if (!sio_start(p->hdl)) {
  249. + MP_ERR(ao, "start: sio_start() fail.\n");
  250. + goto err_out;
  251. + }
  252. +
  253. + return 0;
  254. +
  255. +err_out:
  256. + uninit(ao);
  257. + return -1;
  258. +}
  259. +
  260. +static void uninit(struct ao *ao)
  261. +{
  262. + struct priv *p = ao->priv;
  263. +
  264. + if (p->hdl) {
  265. + sio_close(p->hdl);
  266. + p->hdl = NULL;
  267. + }
  268. + p->pfd = NULL;
  269. + p->playing = false;
  270. +}
  271. +
  272. +static int control(struct ao *ao, enum aocontrol cmd, void *arg)
  273. +{
  274. + struct priv *p = ao->priv;
  275. + ao_control_vol_t *vol = arg;
  276. +
  277. + switch (cmd) {
  278. + case AOCONTROL_GET_VOLUME:
  279. + if (!p->havevol)
  280. + return CONTROL_FALSE;
  281. + vol->left = vol->right = p->vol * 100 / SIO_MAXVOL;
  282. + break;
  283. + case AOCONTROL_SET_VOLUME:
  284. + if (!p->havevol)
  285. + return CONTROL_FALSE;
  286. + sio_setvol(p->hdl, vol->left * SIO_MAXVOL / 100);
  287. + break;
  288. + default:
  289. + return CONTROL_UNKNOWN;
  290. + }
  291. + return CONTROL_OK;
  292. +}
  293. +
  294. +static void reset(struct ao *ao)
  295. +{
  296. + struct priv *p = ao->priv;
  297. +
  298. + if (p->playing) {
  299. + p->playing = false;
  300. +
  301. + if (!sio_stop(p->hdl)) {
  302. + MP_ERR(ao, "reset: couldn't sio_stop()\n");
  303. + }
  304. + p->delay = 0;
  305. + if (!sio_start(p->hdl)) {
  306. + MP_ERR(ao, "reset: sio_start() fail.\n");
  307. + }
  308. + }
  309. +}
  310. +
  311. +static void start(struct ao *ao)
  312. +{
  313. + struct priv *p = ao->priv;
  314. +
  315. + p->playing = true;
  316. + process_events(ao);
  317. +}
  318. +
  319. +static bool audio_write(struct ao *ao, void **data, int samples)
  320. +{
  321. + struct priv *p = ao->priv;
  322. + const size_t size = (samples * ao->sstride);
  323. + size_t rc;
  324. +
  325. + rc = sio_write(p->hdl, data[0], size);
  326. + if (rc != size) {
  327. + MP_WARN(ao, "audio_write: unexpected partial write: required: %zu, written: %zu.\n",
  328. + size, rc);
  329. + reset(ao);
  330. + p->playing = false;
  331. + return false;
  332. + }
  333. + p->delay += samples;
  334. +
  335. + return true;
  336. +}
  337. +
  338. +static void get_state(struct ao *ao, struct mp_pcm_state *state)
  339. +{
  340. + struct priv *p = ao->priv;
  341. +
  342. + process_events(ao);
  343. +
  344. + /* how many samples we can play without blocking */
  345. + state->free_samples = ao->device_buffer - p->delay;
  346. + /* how many samples are already in the buffer to be played */
  347. + state->queued_samples = p->delay;
  348. + /* delay in seconds between first and last sample in buffer */
  349. + state->delay = p->delay / (double)p->par.rate;
  350. +
  351. + /* reset on unexpected EOF / underrun */
  352. + if (state->queued_samples && state->queued_samples &&
  353. + state->queued_samples < state->free_samples &&
  354. + p->playing)
  355. + {
  356. + reset(ao);
  357. + }
  358. + state->playing = p->playing;
  359. +}
  360. +
  361. +const struct ao_driver audio_out_sndio = {
  362. + .name = "sndio",
  363. + .description = "sndio audio output",
  364. + .init = init,
  365. + .uninit = uninit,
  366. + .control = control,
  367. + .reset = reset,
  368. + .start = start,
  369. + .write = audio_write,
  370. + .get_state = get_state,
  371. + .priv_size = sizeof(struct priv),
  372. +};
  373. diff --git a/wscript b/wscript
  374. index b81f1202ec..e9af544b0c 100644
  375. --- a/wscript
  376. +++ b/wscript
  377. @@ -421,6 +421,12 @@ audio_output_features = [
  378. 'desc': 'SDL2 audio output',
  379. 'deps': 'sdl2',
  380. 'func': check_true,
  381. + }, {
  382. + 'name': '--sndio',
  383. + 'desc': 'sndio audio input/output',
  384. + 'func': check_statement('sndio.h',
  385. + 'struct sio_par par; sio_initpar(&par); const char *s = SIO_DEVANY', lib='sndio'),
  386. + 'default': 'disable'
  387. }, {
  388. 'name': '--pulse',
  389. 'desc': 'PulseAudio audio output',
  390. diff --git a/wscript_build.py b/wscript_build.py
  391. index 14c254e1ec..7943f850b0 100644
  392. --- a/wscript_build.py
  393. +++ b/wscript_build.py
  394. @@ -247,6 +247,7 @@ def build(ctx):
  395. ( "audio/out/ao_pcm.c" ),
  396. ( "audio/out/ao_pulse.c", "pulse" ),
  397. ( "audio/out/ao_sdl.c", "sdl2-audio" ),
  398. + ( "audio/out/ao_sndio.c", "sndio" ),
  399. ( "audio/out/ao_wasapi.c", "wasapi" ),
  400. ( "audio/out/ao_wasapi_changenotify.c", "wasapi" ),
  401. ( "audio/out/ao_wasapi_utils.c", "wasapi" ),
  402. --
  403. 2.32.0