FreeRDP
mf_rdpsnd.c
1 
22 #include <freerdp/config.h>
23 
24 #include <freerdp/server/rdpsnd.h>
25 
26 #include "mf_info.h"
27 #include "mf_rdpsnd.h"
28 #include "mf_interface.h"
29 
30 #include <winpr/sysinfo.h>
31 #include <freerdp/server/server-common.h>
32 #include <freerdp/log.h>
33 #define TAG SERVER_TAG("mac")
34 
35 AQRecorderState recorderState;
36 
37 static void mf_peer_rdpsnd_activated(RdpsndServerContext* context)
38 {
39  OSStatus status;
40  BOOL formatAgreed = FALSE;
41  AUDIO_FORMAT* agreedFormat = NULL;
42  // we should actually loop through the list of client formats here
43  // and see if we can send the client something that it supports...
44  WLog_DBG(TAG, "Client supports the following %d formats: ", context->num_client_formats);
45 
46  int i = 0;
47  for (; i < context->num_client_formats; i++)
48  {
49  /* TODO: improve the way we agree on a format */
50  for (int j = 0; j < context->num_server_formats; j++)
51  {
52  if ((context->client_formats[i].wFormatTag == context->server_formats[j].wFormatTag) &&
53  (context->client_formats[i].nChannels == context->server_formats[j].nChannels) &&
54  (context->client_formats[i].nSamplesPerSec ==
55  context->server_formats[j].nSamplesPerSec))
56  {
57  WLog_DBG(TAG, "agreed on format!");
58  formatAgreed = TRUE;
59  agreedFormat = (AUDIO_FORMAT*)&context->server_formats[j];
60  break;
61  }
62  }
63 
64  if (formatAgreed == TRUE)
65  break;
66  }
67 
68  if (formatAgreed == FALSE)
69  {
70  WLog_DBG(TAG, "Could not agree on a audio format with the server");
71  return;
72  }
73 
74  context->SelectFormat(context, i);
75  context->SetVolume(context, 0x7FFF, 0x7FFF);
76 
77  switch (agreedFormat->wFormatTag)
78  {
79  case WAVE_FORMAT_ALAW:
80  recorderState.dataFormat.mFormatID = kAudioFormatDVIIntelIMA;
81  break;
82 
83  case WAVE_FORMAT_PCM:
84  recorderState.dataFormat.mFormatID = kAudioFormatLinearPCM;
85  break;
86 
87  default:
88  recorderState.dataFormat.mFormatID = kAudioFormatLinearPCM;
89  break;
90  }
91 
92  recorderState.dataFormat.mSampleRate = agreedFormat->nSamplesPerSec;
93  recorderState.dataFormat.mFormatFlags =
94  kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
95  recorderState.dataFormat.mBytesPerPacket = 4;
96  recorderState.dataFormat.mFramesPerPacket = 1;
97  recorderState.dataFormat.mBytesPerFrame = 4;
98  recorderState.dataFormat.mChannelsPerFrame = agreedFormat->nChannels;
99  recorderState.dataFormat.mBitsPerChannel = agreedFormat->wBitsPerSample;
100  recorderState.snd_context = context;
101  status =
102  AudioQueueNewInput(&recorderState.dataFormat, mf_peer_rdpsnd_input_callback, &recorderState,
103  NULL, kCFRunLoopCommonModes, 0, &recorderState.queue);
104 
105  if (status != noErr)
106  {
107  WLog_DBG(TAG, "Failed to create a new Audio Queue. Status code: %" PRId32 "", status);
108  }
109 
110  UInt32 dataFormatSize = sizeof(recorderState.dataFormat);
111  AudioQueueGetProperty(recorderState.queue, kAudioConverterCurrentInputStreamDescription,
112  &recorderState.dataFormat, &dataFormatSize);
113  mf_rdpsnd_derive_buffer_size(recorderState.queue, &recorderState.dataFormat, 0.05,
114  &recorderState.bufferByteSize);
115 
116  for (size_t x = 0; x < SND_NUMBUFFERS; ++x)
117  {
118  AudioQueueAllocateBuffer(recorderState.queue, recorderState.bufferByteSize,
119  &recorderState.buffers[x]);
120  AudioQueueEnqueueBuffer(recorderState.queue, recorderState.buffers[x], 0, NULL);
121  }
122 
123  recorderState.currentPacket = 0;
124  recorderState.isRunning = true;
125  AudioQueueStart(recorderState.queue, NULL);
126 }
127 
128 BOOL mf_peer_rdpsnd_init(mfPeerContext* context)
129 {
130  context->rdpsnd = rdpsnd_server_context_new(context->vcm);
131  context->rdpsnd->rdpcontext = &context->_p;
132  context->rdpsnd->data = context;
133  context->rdpsnd->num_server_formats =
134  server_rdpsnd_get_formats(&context->rdpsnd->server_formats);
135 
136  if (context->rdpsnd->num_server_formats > 0)
137  context->rdpsnd->src_format = &context->rdpsnd->server_formats[0];
138 
139  context->rdpsnd->Activated = mf_peer_rdpsnd_activated;
140  context->rdpsnd->Initialize(context->rdpsnd, TRUE);
141  return TRUE;
142 }
143 
144 BOOL mf_peer_rdpsnd_stop(void)
145 {
146  recorderState.isRunning = false;
147  AudioQueueStop(recorderState.queue, true);
148  return TRUE;
149 }
150 
151 void mf_peer_rdpsnd_input_callback(void* inUserData, AudioQueueRef inAQ,
152  AudioQueueBufferRef inBuffer, const AudioTimeStamp* inStartTime,
153  UInt32 inNumberPacketDescriptions,
154  const AudioStreamPacketDescription* inPacketDescs)
155 {
156  OSStatus status;
157  AQRecorderState* rState;
158  rState = inUserData;
159 
160  if (inNumberPacketDescriptions == 0 && rState->dataFormat.mBytesPerPacket != 0)
161  {
162  inNumberPacketDescriptions =
163  inBuffer->mAudioDataByteSize / rState->dataFormat.mBytesPerPacket;
164  }
165 
166  if (rState->isRunning == 0)
167  {
168  return;
169  }
170 
171  rState->snd_context->SendSamples(rState->snd_context, inBuffer->mAudioData,
172  inBuffer->mAudioDataByteSize / 4,
173  (UINT16)(GetTickCount() & 0xffff));
174  status = AudioQueueEnqueueBuffer(rState->queue, inBuffer, 0, NULL);
175 
176  if (status != noErr)
177  {
178  WLog_DBG(TAG, "AudioQueueEnqueueBuffer() returned status = %" PRId32 "", status);
179  }
180 }
181 
182 void mf_rdpsnd_derive_buffer_size(AudioQueueRef audioQueue,
183  AudioStreamBasicDescription* ASBDescription, Float64 seconds,
184  UInt32* outBufferSize)
185 {
186  static const int maxBufferSize = 0x50000;
187  int maxPacketSize = ASBDescription->mBytesPerPacket;
188 
189  if (maxPacketSize == 0)
190  {
191  UInt32 maxVBRPacketSize = sizeof(maxPacketSize);
192  AudioQueueGetProperty(audioQueue, kAudioQueueProperty_MaximumOutputPacketSize,
193  // in Mac OS X v10.5, instead use
194  // kAudioConverterPropertyMaximumOutputPacketSize
195  &maxPacketSize, &maxVBRPacketSize);
196  }
197 
198  Float64 numBytesForTime = ASBDescription->mSampleRate * maxPacketSize * seconds;
199  *outBufferSize = (UInt32)(numBytesForTime < maxBufferSize ? numBytesForTime : maxBufferSize);
200 }