FreeRDP
tsmf_oss.c
1 
20 #include <freerdp/config.h>
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include <winpr/crt.h>
28 
29 #include <err.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <libgen.h>
33 #include <limits.h>
34 #include <unistd.h>
35 #if defined(__OpenBSD__)
36 #include <soundcard.h>
37 #else
38 #include <sys/soundcard.h>
39 #endif
40 #include <sys/ioctl.h>
41 
42 #include <freerdp/types.h>
43 #include <freerdp/codec/dsp.h>
44 
45 #include "tsmf_audio.h"
46 
47 typedef struct
48 {
49  ITSMFAudioDevice iface;
50 
51  char dev_name[PATH_MAX];
52  int pcm_handle;
53 
54  UINT32 sample_rate;
55  UINT32 channels;
56  UINT32 bits_per_sample;
57 
58  UINT32 data_size_last;
59 } TSMFOssAudioDevice;
60 
61 #define OSS_LOG_ERR(_text, _error) \
62  do \
63  { \
64  if ((_error) != 0) \
65  { \
66  char ebuffer[256] = { 0 }; \
67  WLog_ERR(TAG, "%s: %i - %s", (_text), (_error), \
68  winpr_strerror((_error), ebuffer, sizeof(ebuffer))); \
69  } \
70  } while (0)
71 
72 static BOOL tsmf_oss_open(ITSMFAudioDevice* audio, const char* device)
73 {
74  int tmp = 0;
75  TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
76 
77  if (oss == NULL || oss->pcm_handle != -1)
78  return FALSE;
79 
80  if (device == NULL) /* Default device. */
81  {
82  strncpy(oss->dev_name, "/dev/dsp", sizeof(oss->dev_name));
83  }
84  else
85  {
86  strncpy(oss->dev_name, device, sizeof(oss->dev_name) - 1);
87  }
88 
89  if ((oss->pcm_handle = open(oss->dev_name, O_WRONLY)) < 0)
90  {
91  OSS_LOG_ERR("sound dev open failed", errno);
92  oss->pcm_handle = -1;
93  return FALSE;
94  }
95 
96 #if 0 /* FreeBSD OSS implementation at this moment (2015.03) does not set PCM_CAP_OUTPUT flag. */
97  if (ioctl(oss->pcm_handle, SNDCTL_DSP_GETCAPS, &mask) == -1)
98  {
99  OSS_LOG_ERR("SNDCTL_DSP_GETCAPS failed, try ignory", errno);
100  }
101  else if ((mask & PCM_CAP_OUTPUT) == 0)
102  {
103  OSS_LOG_ERR("Device does not supports playback", EOPNOTSUPP);
104  close(oss->pcm_handle);
105  oss->pcm_handle = -1;
106  return FALSE;
107  }
108 
109 #endif
110  const int rc = ioctl(oss->pcm_handle, SNDCTL_DSP_GETFMTS, &tmp);
111  if (rc == -1)
112  {
113  OSS_LOG_ERR("SNDCTL_DSP_GETFMTS failed", errno);
114  close(oss->pcm_handle);
115  oss->pcm_handle = -1;
116  return FALSE;
117  }
118 
119  if ((AFMT_S16_LE & tmp) == 0)
120  {
121  OSS_LOG_ERR("SNDCTL_DSP_GETFMTS - AFMT_S16_LE", EOPNOTSUPP);
122  close(oss->pcm_handle);
123  oss->pcm_handle = -1;
124  return FALSE;
125  }
126 
127  WLog_INFO(TAG, "open: %s", oss->dev_name);
128  return TRUE;
129 }
130 
131 static BOOL tsmf_oss_set_format(ITSMFAudioDevice* audio, UINT32 sample_rate, UINT32 channels,
132  UINT32 bits_per_sample)
133 {
134  int tmp = 0;
135  TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
136 
137  if (oss == NULL || oss->pcm_handle == -1)
138  return FALSE;
139 
140  oss->sample_rate = sample_rate;
141  oss->channels = channels;
142  oss->bits_per_sample = bits_per_sample;
143  tmp = AFMT_S16_LE;
144 
145  if (ioctl(oss->pcm_handle, SNDCTL_DSP_SETFMT, &tmp) == -1)
146  OSS_LOG_ERR("SNDCTL_DSP_SETFMT failed", errno);
147 
148  tmp = channels;
149 
150  if (ioctl(oss->pcm_handle, SNDCTL_DSP_CHANNELS, &tmp) == -1)
151  OSS_LOG_ERR("SNDCTL_DSP_CHANNELS failed", errno);
152 
153  tmp = sample_rate;
154 
155  if (ioctl(oss->pcm_handle, SNDCTL_DSP_SPEED, &tmp) == -1)
156  OSS_LOG_ERR("SNDCTL_DSP_SPEED failed", errno);
157 
158  tmp = ((bits_per_sample / 8) * channels * sample_rate);
159 
160  if (ioctl(oss->pcm_handle, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
161  OSS_LOG_ERR("SNDCTL_DSP_SETFRAGMENT failed", errno);
162 
163  DEBUG_TSMF("sample_rate %" PRIu32 " channels %" PRIu32 " bits_per_sample %" PRIu32 "",
164  sample_rate, channels, bits_per_sample);
165  return TRUE;
166 }
167 
168 static BOOL tsmf_oss_play(ITSMFAudioDevice* audio, const BYTE* data, UINT32 data_size)
169 {
170  UINT32 offset = 0;
171  TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
172  DEBUG_TSMF("tsmf_oss_play: data_size %" PRIu32 "", data_size);
173 
174  if (oss == NULL || oss->pcm_handle == -1)
175  return FALSE;
176 
177  if (data == NULL || data_size == 0)
178  return TRUE;
179 
180  offset = 0;
181  oss->data_size_last = data_size;
182 
183  while (offset < data_size)
184  {
185  const ssize_t status = write(oss->pcm_handle, &data[offset], (data_size - offset));
186 
187  if (status < 0)
188  {
189  OSS_LOG_ERR("write fail", errno);
190  return FALSE;
191  }
192 
193  offset += status;
194  }
195 
196  return TRUE;
197 }
198 
199 static UINT64 tsmf_oss_get_latency(ITSMFAudioDevice* audio)
200 {
201  UINT64 latency = 0;
202  TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
203 
204  if (oss == NULL)
205  return 0;
206 
207  // latency = ((oss->data_size_last / (oss->bits_per_sample / 8)) * oss->sample_rate);
208  // WLog_INFO(TAG, "latency: %zu", latency);
209  return latency;
210 }
211 
212 static BOOL tsmf_oss_flush(ITSMFAudioDevice* audio)
213 {
214  return TRUE;
215 }
216 
217 static void tsmf_oss_free(ITSMFAudioDevice* audio)
218 {
219  TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
220 
221  if (oss == NULL)
222  return;
223 
224  if (oss->pcm_handle != -1)
225  {
226  WLog_INFO(TAG, "close: %s", oss->dev_name);
227  close(oss->pcm_handle);
228  }
229 
230  free(oss);
231 }
232 
233 FREERDP_ENTRY_POINT(UINT VCAPITYPE oss_freerdp_tsmf_client_audio_subsystem_entry(void* ptr))
234 {
235  ITSMFAudioDevice** sptr = (ITSMFAudioDevice**)ptr;
236  WINPR_ASSERT(sptr);
237  *sptr = NULL;
238 
239  TSMFOssAudioDevice* oss = calloc(1, sizeof(TSMFOssAudioDevice));
240  if (!oss)
241  return ERROR_OUTOFMEMORY;
242 
243  oss->iface.Open = tsmf_oss_open;
244  oss->iface.SetFormat = tsmf_oss_set_format;
245  oss->iface.Play = tsmf_oss_play;
246  oss->iface.GetLatency = tsmf_oss_get_latency;
247  oss->iface.Flush = tsmf_oss_flush;
248  oss->iface.Free = tsmf_oss_free;
249  oss->pcm_handle = -1;
250  *sptr = &oss->iface;
251  return CHANNEL_RC_OK;
252 }