pulse-input.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /*
  2. Copyright (C) 2014 by Leonhard Oelke <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. #include <util/bmem.h>
  15. #include <obs.h>
  16. #include "pulse-wrapper.h"
  17. #define PULSE_DATA(voidptr) struct pulse_data *data = voidptr;
  18. struct pulse_data {
  19. obs_source_t source;
  20. char *device;
  21. enum speaker_layout speakers;
  22. pa_sample_format_t format;
  23. uint_fast32_t samples_per_sec;
  24. uint_fast8_t channels;
  25. uint_fast32_t bytes_per_frame;
  26. pa_stream *stream;
  27. };
  28. /*
  29. * get obs from pulse audio format
  30. */
  31. static enum audio_format pulse_to_obs_audio_format(
  32. pa_sample_format_t format)
  33. {
  34. switch (format) {
  35. case PA_SAMPLE_U8:
  36. return AUDIO_FORMAT_U8BIT;
  37. case PA_SAMPLE_S16LE:
  38. return AUDIO_FORMAT_16BIT;
  39. case PA_SAMPLE_S24_32LE:
  40. return AUDIO_FORMAT_32BIT;
  41. case PA_SAMPLE_FLOAT32LE:
  42. return AUDIO_FORMAT_FLOAT;
  43. default:
  44. return AUDIO_FORMAT_UNKNOWN;
  45. }
  46. return AUDIO_FORMAT_UNKNOWN;
  47. }
  48. /*
  49. * get the buffer size needed for length msec with current settings
  50. */
  51. static uint_fast32_t get_buffer_size(struct pulse_data *data,
  52. uint_fast32_t length)
  53. {
  54. return (length * data->samples_per_sec * data->bytes_per_frame) / 1000;
  55. }
  56. /*
  57. * Get latency for a pulse audio stream
  58. */
  59. static int pulse_get_stream_latency(pa_stream *stream, int64_t *latency)
  60. {
  61. int ret;
  62. int sign;
  63. pa_usec_t abs;
  64. ret = pa_stream_get_latency(stream, &abs, &sign);
  65. *latency = (sign) ? -(int64_t) abs : (int64_t) abs;
  66. return ret;
  67. }
  68. /*
  69. * Callback for pulse which gets executed when new audio data is available
  70. */
  71. static void pulse_stream_read(pa_stream *p, size_t nbytes, void *userdata)
  72. {
  73. UNUSED_PARAMETER(p);
  74. UNUSED_PARAMETER(nbytes);
  75. PULSE_DATA(userdata);
  76. const void *frames;
  77. size_t bytes;
  78. uint64_t pa_time;
  79. int64_t pa_latency;
  80. pa_stream_peek(data->stream, &frames, &bytes);
  81. // check if we got data
  82. if (!bytes)
  83. goto exit;
  84. if (!frames) {
  85. blog(LOG_DEBUG,
  86. "pulse-input: Got audio hole of %u bytes",
  87. (unsigned int) bytes);
  88. pa_stream_drop(data->stream);
  89. goto exit;
  90. }
  91. if (pa_stream_get_time(data->stream, &pa_time) < 0) {
  92. blog(LOG_ERROR,
  93. "pulse-input: Failed to get timing info !");
  94. pa_stream_drop(data->stream);
  95. goto exit;
  96. }
  97. pulse_get_stream_latency(data->stream, &pa_latency);
  98. struct source_audio out;
  99. out.speakers = data->speakers;
  100. out.samples_per_sec = data->samples_per_sec;
  101. out.format = pulse_to_obs_audio_format(data->format);
  102. out.data[0] = (uint8_t *) frames;
  103. out.frames = bytes / data->bytes_per_frame;
  104. out.timestamp = (pa_time - pa_latency) * 1000;
  105. obs_source_output_audio(data->source, &out);
  106. pa_stream_drop(data->stream);
  107. exit:
  108. pulse_signal(0);
  109. }
  110. /*
  111. * Server info callback
  112. */
  113. static void pulse_server_info(pa_context *c, const pa_server_info *i,
  114. void *userdata)
  115. {
  116. UNUSED_PARAMETER(c);
  117. PULSE_DATA(userdata);
  118. data->format = i->sample_spec.format;
  119. data->samples_per_sec = i->sample_spec.rate;
  120. data->channels = i->sample_spec.channels;
  121. blog(LOG_DEBUG, "pulse-input: Default format: %s, %u Hz, %u channels",
  122. pa_sample_format_to_string(i->sample_spec.format),
  123. i->sample_spec.rate,
  124. i->sample_spec.channels);
  125. pulse_signal(0);
  126. }
  127. /*
  128. * start recording
  129. */
  130. static int_fast32_t pulse_start_recording(struct pulse_data *data)
  131. {
  132. if (pulse_get_server_info(pulse_server_info, (void *) data) < 0) {
  133. blog(LOG_ERROR, "pulse-input: Unable to get server info !");
  134. return -1;
  135. }
  136. pa_sample_spec spec;
  137. spec.format = data->format;
  138. spec.rate = data->samples_per_sec;
  139. spec.channels = data->channels;
  140. if (!pa_sample_spec_valid(&spec)) {
  141. blog(LOG_ERROR, "pulse-input: Sample spec is not valid");
  142. return -1;
  143. }
  144. data->bytes_per_frame = pa_frame_size(&spec);
  145. blog(LOG_DEBUG, "pulse-input: %u bytes per frame",
  146. (unsigned int) data->bytes_per_frame);
  147. data->stream = pulse_stream_new(obs_source_getname(data->source),
  148. &spec, NULL);
  149. if (!data->stream) {
  150. blog(LOG_ERROR, "pulse-input: Unable to create stream");
  151. return -1;
  152. }
  153. pulse_lock();
  154. pa_stream_set_read_callback(data->stream, pulse_stream_read,
  155. (void *) data);
  156. pulse_unlock();
  157. pa_buffer_attr attr;
  158. attr.fragsize = get_buffer_size(data, 250);
  159. attr.maxlength = (uint32_t) -1;
  160. attr.minreq = (uint32_t) -1;
  161. attr.prebuf = (uint32_t) -1;
  162. attr.tlength = (uint32_t) -1;
  163. pa_stream_flags_t flags =
  164. PA_STREAM_INTERPOLATE_TIMING
  165. | PA_STREAM_AUTO_TIMING_UPDATE
  166. | PA_STREAM_ADJUST_LATENCY;
  167. pulse_lock();
  168. int_fast32_t ret = pa_stream_connect_record(data->stream, data->device,
  169. &attr, flags);
  170. pulse_unlock();
  171. if (ret < 0) {
  172. blog(LOG_ERROR, "pulse-input: Unable to connect to stream");
  173. return -1;
  174. }
  175. blog(LOG_DEBUG, "pulse-input: Recording started");
  176. return 0;
  177. }
  178. /*
  179. * stop recording
  180. */
  181. static void pulse_stop_recording(struct pulse_data *data)
  182. {
  183. if (data->stream) {
  184. pulse_lock();
  185. pa_stream_disconnect(data->stream);
  186. pa_stream_unref(data->stream);
  187. pulse_unlock();
  188. }
  189. }
  190. /*
  191. * input info callback
  192. */
  193. static void pulse_input_info(pa_context *c, const pa_source_info *i, int eol,
  194. void *userdata)
  195. {
  196. UNUSED_PARAMETER(c);
  197. if (eol != 0 || i->monitor_of_sink != PA_INVALID_INDEX)
  198. goto skip;
  199. obs_property_list_add_string((obs_property_t) userdata,
  200. i->description, i->name);
  201. skip:
  202. pulse_signal(0);
  203. }
  204. /**
  205. * output info callback
  206. */
  207. static void pulse_output_info(pa_context *c, const pa_source_info *i, int eol,
  208. void *userdata)
  209. {
  210. UNUSED_PARAMETER(c);
  211. if (eol != 0 || i->monitor_of_sink == PA_INVALID_INDEX)
  212. goto skip;
  213. obs_property_list_add_string((obs_property_t) userdata,
  214. i->description, i->name);
  215. skip:
  216. pulse_signal(0);
  217. }
  218. /*
  219. * get plugin properties
  220. */
  221. static obs_properties_t pulse_properties(const char *locale, bool input)
  222. {
  223. obs_properties_t props = obs_properties_create(locale);
  224. obs_property_t devices = obs_properties_add_list(props, "device_id",
  225. "Device", OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
  226. pulse_init();
  227. pa_source_info_cb_t cb = (input) ? pulse_input_info : pulse_output_info;
  228. pulse_get_source_info_list(cb, (void *) devices);
  229. pulse_unref();
  230. return props;
  231. }
  232. static obs_properties_t pulse_input_properties(const char *locale)
  233. {
  234. return pulse_properties(locale, true);
  235. }
  236. static obs_properties_t pulse_output_properties(const char *locale)
  237. {
  238. return pulse_properties(locale, false);
  239. }
  240. /*
  241. * get plugin defaults
  242. */
  243. static void pulse_defaults(obs_data_t settings)
  244. {
  245. obs_data_set_default_string(settings, "device_id", "default");
  246. }
  247. /*
  248. * Returns the name of the plugin
  249. */
  250. static const char *pulse_input_getname(const char *locale)
  251. {
  252. UNUSED_PARAMETER(locale);
  253. return "Pulse Audio Input Capture";
  254. }
  255. static const char *pulse_output_getname(const char *locale)
  256. {
  257. UNUSED_PARAMETER(locale);
  258. return "Pulse Audio Output Capture";
  259. }
  260. /*
  261. * Destroy the plugin object and free all memory
  262. */
  263. static void pulse_destroy(void *vptr)
  264. {
  265. PULSE_DATA(vptr);
  266. if (!data)
  267. return;
  268. pulse_stop_recording(data);
  269. pulse_unref();
  270. if (data->device)
  271. bfree(data->device);
  272. bfree(data);
  273. blog(LOG_DEBUG, "pulse-input: Input destroyed");
  274. }
  275. /*
  276. * Create the plugin object
  277. */
  278. static void *pulse_create(obs_data_t settings, obs_source_t source)
  279. {
  280. struct pulse_data *data = bzalloc(sizeof(struct pulse_data));
  281. data->source = source;
  282. data->speakers = SPEAKERS_STEREO;
  283. data->device = bstrdup(obs_data_getstring(settings, "device_id"));
  284. pulse_init();
  285. if (pulse_start_recording(data) < 0)
  286. goto fail;
  287. return data;
  288. fail:
  289. pulse_destroy(data);
  290. return NULL;
  291. }
  292. static void pulse_update(void *vptr, obs_data_t settings)
  293. {
  294. UNUSED_PARAMETER(vptr);
  295. UNUSED_PARAMETER(settings);
  296. }
  297. struct obs_source_info pulse_input_capture = {
  298. .id = "pulse_input_capture",
  299. .type = OBS_SOURCE_TYPE_INPUT,
  300. .output_flags = OBS_SOURCE_AUDIO,
  301. .getname = pulse_input_getname,
  302. .create = pulse_create,
  303. .destroy = pulse_destroy,
  304. .update = pulse_update,
  305. .defaults = pulse_defaults,
  306. .properties = pulse_input_properties
  307. };
  308. struct obs_source_info pulse_output_capture = {
  309. .id = "pulse_output_capture",
  310. .type = OBS_SOURCE_TYPE_INPUT,
  311. .output_flags = OBS_SOURCE_AUDIO,
  312. .getname = pulse_output_getname,
  313. .create = pulse_create,
  314. .destroy = pulse_destroy,
  315. .update = pulse_update,
  316. .defaults = pulse_defaults,
  317. .properties = pulse_output_properties
  318. };