vfs: check userland buffers before reading them.
[haiku.git] / src / kits / game / GameProducer.cpp
blobc173058e41aa91b00e6942d74cc0cff04299fd3b
1 /*
2 * Copyright 2002-2014 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Christopher ML Zumwalt May (zummy@users.sf.net)
7 */
10 /* A MediaKit producer node which mixes sound from the GameKit
11 and sends them to the audio mixer
15 #include "GameProducer.h"
17 #include <string.h>
18 #include <stdio.h>
20 #include <Buffer.h>
21 #include <BufferGroup.h>
22 #include <ByteOrder.h>
23 #include <List.h>
24 #include <MediaDefs.h>
25 #include <TimeSource.h>
27 #include "GameSoundBuffer.h"
28 #include "GameSoundDevice.h"
29 #include "GSUtility.h"
32 struct _gs_play {
33 gs_id sound;
34 bool* hook;
36 _gs_play* next;
37 _gs_play* previous;
41 GameProducer::GameProducer(GameSoundBuffer* object,
42 const gs_audio_format* format)
44 BMediaNode("GameProducer.h"),
45 BBufferProducer(B_MEDIA_RAW_AUDIO),
46 BMediaEventLooper(),
47 fBufferGroup(NULL),
48 fLatency(0),
49 fInternalLatency(0),
50 fOutputEnabled(true)
52 // initialize our preferred format object
53 fPreferredFormat.type = B_MEDIA_RAW_AUDIO;
54 fPreferredFormat.u.raw_audio.format = format->format;
55 fPreferredFormat.u.raw_audio.channel_count = format->channel_count;
56 fPreferredFormat.u.raw_audio.frame_rate = format->frame_rate; // Hertz
57 fPreferredFormat.u.raw_audio.byte_order = format->byte_order;
58 // fPreferredFormat.u.raw_audio.channel_mask
59 // = B_CHANNEL_LEFT | B_CHANNEL_RIGHT;
60 // fPreferredFormat.u.raw_audio.valid_bits = 32;
61 // fPreferredFormat.u.raw_audio.matrix_mask = B_MATRIX_AMBISONIC_WXYZ;
63 // we'll use the consumer's preferred buffer size, if any
64 fPreferredFormat.u.raw_audio.buffer_size
65 = media_raw_audio_format::wildcard.buffer_size;
67 // we're not connected yet
68 fOutput.destination = media_destination::null;
69 fOutput.format = fPreferredFormat;
71 fFrameSize = get_sample_size(format->format) * format->channel_count;
72 fObject = object;
76 GameProducer::~GameProducer()
78 // Stop the BMediaEventLooper thread
79 Quit();
83 // BMediaNode methods
84 BMediaAddOn*
85 GameProducer::AddOn(int32* internal_id) const
87 return NULL;
91 // BBufferProducer methods
92 status_t
93 GameProducer::GetNextOutput(int32* cookie, media_output* _output)
95 // we currently support only one output
96 if (0 != *cookie)
97 return B_BAD_INDEX;
99 *_output = fOutput;
100 *cookie += 1;
101 return B_OK;
105 status_t
106 GameProducer::DisposeOutputCookie(int32 cookie)
108 // do nothing because our cookie is only an integer
109 return B_OK;
113 void
114 GameProducer::EnableOutput(const media_source& what, bool enabled,
115 int32* _deprecated_)
117 // If I had more than one output, I'd have to walk my list of output records
118 // to see which one matched the given source, and then enable/disable that
119 // one. But this node only has one output, so I just make sure the given
120 // source matches, then set the enable state accordingly.
121 if (what == fOutput.source)
123 fOutputEnabled = enabled;
128 status_t
129 GameProducer::FormatSuggestionRequested(media_type type, int32 /*quality*/,
130 media_format* format)
132 // insure that we received a format
133 if (!format)
134 return B_BAD_VALUE;
136 // returning our preferred format
137 *format = fPreferredFormat;
139 // our format is supported
140 if (type == B_MEDIA_UNKNOWN_TYPE)
141 return B_OK;
143 // we only support raw audo
144 return (type != B_MEDIA_RAW_AUDIO) ? B_MEDIA_BAD_FORMAT : B_OK;
148 status_t
149 GameProducer::FormatProposal(const media_source& output, media_format* format)
151 // doest the proposed output match our output?
152 if (output != fOutput.source)
153 return B_MEDIA_BAD_SOURCE;
155 // return our preferred format
156 *format = fPreferredFormat;
158 // we will reject the proposal if the format is not audio
159 media_type requestedType = format->type;
160 if ((requestedType != B_MEDIA_UNKNOWN_TYPE)
161 && (requestedType != B_MEDIA_RAW_AUDIO)) {
162 return B_MEDIA_BAD_FORMAT;
165 return B_OK; // raw audio or wildcard type, either is okay by us
169 status_t
170 GameProducer::PrepareToConnect(const media_source& what,
171 const media_destination& where, media_format* format,
172 media_source* _source, char* out_name)
174 // The format has been processed by the consumer at this point. We need
175 // to insure the format is still acceptable and any wild care are filled in.
177 // trying to connect something that isn't our source?
178 if (what != fOutput.source)
179 return B_MEDIA_BAD_SOURCE;
181 // are we already connected?
182 if (fOutput.destination != media_destination::null)
183 return B_MEDIA_ALREADY_CONNECTED;
185 // the format may not yet be fully specialized (the consumer might have
186 // passed back some wildcards). Finish specializing it now, and return an
187 // error if we don't support the requested format.
188 if (format->type != B_MEDIA_RAW_AUDIO)
189 return B_MEDIA_BAD_FORMAT;
191 if (format->u.raw_audio.format != fPreferredFormat.u.raw_audio.format)
192 return B_MEDIA_BAD_FORMAT;
194 // check the buffer size, which may still be wildcarded
195 if (format->u.raw_audio.buffer_size
196 == media_raw_audio_format::wildcard.buffer_size) {
197 format->u.raw_audio.buffer_size = 4096;
198 // pick something comfortable to suggest
201 // Now reserve the connection, and return information about it
202 fOutput.destination = where;
203 fOutput.format = *format;
204 *_source = fOutput.source;
205 strlcpy(out_name, fOutput.name, B_MEDIA_NAME_LENGTH);
206 return B_OK;
210 void
211 GameProducer::Connect(status_t error, const media_source& source,
212 const media_destination& destination, const media_format& format,
213 char* ioName)
215 // If something earlier failed, Connect() might still be called, but with a
216 // non-zero error code. When that happens we simply unreserve the
217 // connection and do nothing else.
218 if (error) {
219 fOutput.destination = media_destination::null;
220 fOutput.format = fPreferredFormat;
221 return;
224 // Okay, the connection has been confirmed. Record the destination and
225 // format that we agreed on, and report our connection name again.
226 fOutput.destination = destination;
227 fOutput.format = format;
228 strlcpy(ioName, fOutput.name, B_MEDIA_NAME_LENGTH);
230 // Now that we're connected, we can determine our downstream latency.
231 // Do so, then make sure we get our events early enough.
232 media_node_id id;
233 FindLatencyFor(fOutput.destination, &fLatency, &id);
235 if (!fBufferGroup)
236 fBufferSize = fOutput.format.u.raw_audio.buffer_size;
237 // Have to set it before latency calculating
239 // Use a dry run to see how long it takes me to fill a buffer of data
241 // The first step to setup the buffer
242 bigtime_t start, produceLatency;
243 int32 frames = int32(fBufferSize / fFrameSize);
244 float* data = new float[frames * 2];
246 // Second, fill the buffer
247 start = ::system_time();
248 for (int32 i = 0; i < frames; i++) {
249 data[i * 2] = 0.8 * float(i / frames);
250 data[i * 2 + 1] = 0.8 * float(i / frames);
252 produceLatency = ::system_time();
254 // Third, calculate the latency
255 fInternalLatency = produceLatency - start;
256 SetEventLatency(fLatency + fInternalLatency);
258 // Finaily, clean up
259 delete [] data;
261 // reset our buffer duration, etc. to avoid later calculations
262 bigtime_t duration = bigtime_t(1000000) * frames
263 / bigtime_t(fOutput.format.u.raw_audio.frame_rate);
264 SetBufferDuration(duration);
266 // Set up the buffer group for our connection, as long as nobody handed us a
267 // buffer group (via SetBufferGroup()) prior to this.
268 if (!fBufferGroup) {
269 int32 count = int32(fLatency / BufferDuration() + 2);
270 fBufferGroup = new BBufferGroup(fBufferSize, count);
275 void
276 GameProducer::Disconnect(const media_source& what,
277 const media_destination& where)
279 // Make sure that our connection is the one being disconnected
280 if ((where == fOutput.destination) && (what == fOutput.source)) {
281 fOutput.destination = media_destination::null;
282 fOutput.format = fPreferredFormat;
283 delete fBufferGroup;
284 fBufferGroup = NULL;
289 status_t
290 GameProducer::FormatChangeRequested(const media_source& source,
291 const media_destination& destination, media_format* io_format,
292 int32* _deprecated_)
294 // we don't support any other formats, so we just reject any format changes.
295 return B_ERROR;
299 status_t
300 GameProducer::SetBufferGroup(const media_source& forSource,
301 BBufferGroup* newGroup)
303 // verify that we didn't get bogus arguments before we proceed
304 if (forSource != fOutput.source)
305 return B_MEDIA_BAD_SOURCE;
307 // Are we being passed the buffer group we're already using?
308 if (newGroup == fBufferGroup)
309 return B_OK;
311 // Ahh, someone wants us to use a different buffer group. At this point we
312 // delete the one we are using and use the specified one instead. If the
313 // specified group is NULL, we need to recreate one ourselves, and use
314 // *that*. Note that if we're caching a BBuffer that we requested earlier,
315 // we have to Recycle() that buffer *before* deleting the buffer group,
316 // otherwise we'll deadlock waiting for that buffer to be recycled!
317 delete fBufferGroup; // waits for all buffers to recycle
318 if (newGroup != NULL) {
319 // we were given a valid group; just use that one from now on
320 fBufferGroup = newGroup;
322 // get buffer length from the first buffer
323 BBuffer* buffers[1];
324 if (newGroup->GetBufferList(1, buffers) != B_OK)
325 return B_BAD_VALUE;
326 fBufferSize = buffers[0]->SizeAvailable();
327 } else {
328 // we were passed a NULL group pointer; that means we construct
329 // our own buffer group to use from now on
330 fBufferSize = fOutput.format.u.raw_audio.buffer_size;
331 int32 count = int32(fLatency / BufferDuration() + 2);
332 fBufferGroup = new BBufferGroup(fBufferSize, count);
335 return B_OK;
339 status_t
340 GameProducer::GetLatency(bigtime_t* _latency)
342 // report our *total* latency: internal plus downstream plus scheduling
343 *_latency = EventLatency() + SchedulingLatency();
344 return B_OK;
348 void
349 GameProducer::LateNoticeReceived(const media_source& what, bigtime_t howMuch,
350 bigtime_t performanceDuration)
352 // If we're late, we need to catch up. Respond in a manner appropriate to
353 // our current run mode.
354 if (what == fOutput.source) {
355 if (RunMode() == B_RECORDING) {
356 // A hardware capture node can't adjust; it simply emits buffers at
357 // appropriate points. We (partially) simulate this by not
358 // adjusting our behavior upon receiving late notices -- after all,
359 // the hardware can't choose to capture "sooner"...
360 } else if (RunMode() == B_INCREASE_LATENCY) {
361 // We're late, and our run mode dictates that we try to produce
362 // buffers earlier in order to catch up. This argues that the
363 // downstream nodes are not properly reporting their latency, but
364 // there's not much we can do about that at the moment, so we try
365 // to start producing buffers earlier to compensate.
366 fInternalLatency += howMuch;
367 SetEventLatency(fLatency + fInternalLatency);
368 } else {
369 // The other run modes dictate various strategies for sacrificing
370 // data quality in the interests of timely data delivery. The way we
371 // do this is to skip a buffer, which catches us up in time by one
372 // buffer duration.
373 size_t nSamples = fBufferSize / fFrameSize;
374 fFramesSent += nSamples;
380 void
381 GameProducer::LatencyChanged(const media_source& source,
382 const media_destination& destination, bigtime_t new_latency, uint32 flags)
384 // something downstream changed latency, so we need to start producing
385 // buffers earlier (or later) than we were previously. Make sure that the
386 // connection that changed is ours, and adjust to the new downstream
387 // latency if so.
388 if ((source == fOutput.source) && (destination == fOutput.destination)) {
389 fLatency = new_latency;
390 SetEventLatency(fLatency + fInternalLatency);
395 status_t
396 GameProducer::SetPlayRate(int32 numerator, int32 denominator)
398 // Play rates are weird. We don't support them
399 return B_ERROR;
403 status_t
404 GameProducer::HandleMessage(int32 message, const void* data, size_t size)
406 // We currently do not handle private messages
407 return B_ERROR;
411 void
412 GameProducer::AdditionalBufferRequested(const media_source& source,
413 media_buffer_id prev_buffer, bigtime_t prev_time,
414 const media_seek_tag* prev_tag)
416 // we don't support offline mode (yet...)
417 return;
421 // BMediaEventLooper methods
422 void
423 GameProducer::NodeRegistered()
425 // set up as much information about our output as we can
426 fOutput.source.port = ControlPort();
427 fOutput.source.id = 0;
428 fOutput.node = Node();
429 strlcpy(fOutput.name, "GameProducer Output", B_MEDIA_NAME_LENGTH);
431 // Start the BMediaEventLooper thread
432 SetPriority(B_REAL_TIME_PRIORITY);
433 Run();
437 void
438 GameProducer::SetRunMode(run_mode mode)
440 // We don't support offline run mode, so broadcast an error if we're set to
441 // B_OFFLINE. Unfortunately, we can't actually reject the mode change...
442 if (B_OFFLINE == mode) {
443 ReportError(B_NODE_FAILED_SET_RUN_MODE);
448 void
449 GameProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness,
450 bool realTimeEvent)
452 // FPRINTF(stderr, "ToneProducer::HandleEvent\n");
453 switch (event->type)
455 case BTimedEventQueue::B_START:
456 // don't do anything if we're already running
457 if (RunState() != B_STARTED) {
458 // Going to start sending buffers so setup the needed bookkeeping
459 fFramesSent = 0;
460 fStartTime = event->event_time;
461 media_timed_event firstBufferEvent(fStartTime,
462 BTimedEventQueue::B_HANDLE_BUFFER);
464 // Alternatively, we could call HandleEvent() directly with this
465 // event, to avoid a trip through the event queue like this:
466 // this->HandleEvent(&firstBufferEvent, 0, false);
467 EventQueue()->AddEvent(firstBufferEvent);
469 break;
471 case BTimedEventQueue::B_STOP:
472 // When we handle a stop, we must ensure that downstream consumers don't
473 // get any more buffers from us. This means we have to flush any
474 // pending buffer-producing events from the queue.
475 EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true,
476 BTimedEventQueue::B_HANDLE_BUFFER);
477 break;
479 case BTimedEventQueue::B_HANDLE_BUFFER:
481 // Ensure we're both started and connected before delivering buffer
482 if ((RunState() == BMediaEventLooper::B_STARTED)
483 && (fOutput.destination != media_destination::null)) {
484 // Get the next buffer of data
485 BBuffer* buffer = FillNextBuffer(event->event_time);
486 if (buffer) {
487 // Send the buffer downstream if output is enabled
488 status_t err = B_ERROR;
489 if (fOutputEnabled) {
490 err = SendBuffer(buffer, fOutput.source,
491 fOutput.destination);
493 if (err) {
494 // we need to recycle the buffer ourselves if output is
495 // disabled or if the call to SendBuffer() fails
496 buffer->Recycle();
500 // track how much media we've delivered so far
501 size_t nFrames = fBufferSize / fFrameSize;
502 fFramesSent += nFrames;
504 // The buffer is on its way; now schedule the next one to go
505 bigtime_t nextEvent = fStartTime + bigtime_t(double(fFramesSent)
506 / double(fOutput.format.u.raw_audio.frame_rate)
507 * 1000000.0);
508 media_timed_event nextBufferEvent(nextEvent,
509 BTimedEventQueue::B_HANDLE_BUFFER);
510 EventQueue()->AddEvent(nextBufferEvent);
513 break;
515 default:
516 break;
521 BBuffer*
522 GameProducer::FillNextBuffer(bigtime_t event_time)
524 // get a buffer from our buffer group
525 BBuffer* buf = fBufferGroup->RequestBuffer(fBufferSize, BufferDuration());
527 // if we fail to get a buffer (for example, if the request times out), we
528 // skip this buffer and go on to the next, to avoid locking up the control
529 // thread.
530 if (!buf)
531 return NULL;
533 // we need to discribe the buffer
534 int64 frames = int64(fBufferSize / fFrameSize);
535 memset(buf->Data(), 0, fBufferSize);
537 // now fill the buffer with data, continuing where the last buffer left off
538 fObject->Play(buf->Data(), frames);
540 // fill in the buffer header
541 media_header* hdr = buf->Header();
542 hdr->type = B_MEDIA_RAW_AUDIO;
543 hdr->size_used = fBufferSize;
544 hdr->time_source = TimeSource()->ID();
546 bigtime_t stamp;
547 if (RunMode() == B_RECORDING) {
548 // In B_RECORDING mode, we stamp with the capture time. We're not
549 // really a hardware capture node, but we simulate it by using the
550 // (precalculated) time at which this buffer "should" have been created.
551 stamp = event_time;
552 } else {
553 // okay, we're in one of the "live" performance run modes. in these
554 // modes, we stamp the buffer with the time at which the buffer should
555 // be rendered to the output, not with the capture time. fStartTime is
556 // the cached value of the first buffer's performance time; we calculate
557 // this buffer's performance time as an offset from that time, based on
558 // the amount of media we've created so far.
559 // Recalculating every buffer like this avoids accumulation of error.
560 stamp = fStartTime + bigtime_t(double(fFramesSent)
561 / double(fOutput.format.u.raw_audio.frame_rate) * 1000000.0);
563 hdr->start_time = stamp;
565 return buf;