xref: /haiku/src/apps/cortex/addons/LoggingConsumer/LoggingConsumer.cpp (revision e81a954787e50e56a7f06f72705b7859b6ab06d1)
1 /*
2  * Copyright 1991-1999, Be Incorporated.
3  * Copyright (c) 1999-2000, Eric Moon.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions, and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions, and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
28  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 
33 // LoggingConsumer.cpp
34 
35 #include "LoggingConsumer.h"
36 #include "LogWriter.h"
37 #include <media/MediaRoster.h>
38 #include <media/TimeSource.h>
39 #include <media/ParameterWeb.h>
40 #include <media/Buffer.h>
41 #include <kernel/OS.h>
42 #include <stdio.h>
43 #include <string.h>
44 
45 // e.moon [11jun99]
46 #include <Debug.h>
47 
48 // id's of the node's BParameters
49 const int32 INPUT_NULL_PARAM = 1;
50 const int32 LATENCY_PARAM = 2;
51 const int32 OUTPUT_NULL_PARAM = 3;
52 const int32 CPU_NULL_PARAM = 11;
53 const int32 CPU_SPIN_PARAM = 12;
54 const int32 PRIO_NULL_PARAM = 21;
55 const int32 PRIORITY_PARAM = 22;
56 
57 // build the LoggingConsumer's BParameterWeb
58 static BParameterWeb* build_parameter_web()
59 {
60 	BParameterWeb* web = new BParameterWeb;
61 
62 	BParameterGroup* mainGroup = web->MakeGroup("LoggingConsumer Parameters");
63 	BParameterGroup* group = mainGroup->MakeGroup("Latency control");
64 	BParameter* nullParam = group->MakeNullParameter(INPUT_NULL_PARAM, B_MEDIA_NO_TYPE, "Latency", B_GENERIC);
65 	BParameter* latencyParam = group->MakeContinuousParameter(LATENCY_PARAM, B_MEDIA_NO_TYPE, "",
66 		B_GAIN, "ms", 5, 100, 5);
67 	nullParam->AddOutput(latencyParam);
68 	latencyParam->AddInput(nullParam);
69 
70 	group = mainGroup->MakeGroup("CPU percentage");
71 	nullParam = group->MakeNullParameter(CPU_NULL_PARAM, B_MEDIA_NO_TYPE, "CPU spin percentage", B_GENERIC);
72 	BContinuousParameter* cpuParam = group->MakeContinuousParameter(CPU_SPIN_PARAM, B_MEDIA_NO_TYPE, "",
73 		B_GAIN, "percent", 5, 80, 5);
74 	nullParam->AddOutput(cpuParam);
75 	cpuParam->AddInput(nullParam);
76 
77 	group = mainGroup->MakeGroup("Priority");
78 	nullParam = group->MakeNullParameter(PRIO_NULL_PARAM, B_MEDIA_NO_TYPE, "Thread priority", B_GENERIC);
79 	BDiscreteParameter* prioParam = group->MakeDiscreteParameter(PRIORITY_PARAM, B_MEDIA_NO_TYPE, "", B_GENERIC);
80 	prioParam->AddItem(5, "B_LOW_PRIORITY");
81 	prioParam->AddItem(10, "B_NORMAL_PRIORITY");
82 	prioParam->AddItem(15, "B_DISPLAY_PRIORITY");
83 	prioParam->AddItem(20, "B_URGENT_DISPLAY_PRIORITY");
84 	prioParam->AddItem(100, "B_REAL_TIME_DISPLAY_PRIORITY");
85 	prioParam->AddItem(110, "B_URGENT_PRIORITY");
86 	prioParam->AddItem(120, "B_REAL_TIME_PRIORITY");
87 
88 	return web;
89 }
90 
91 // --------------------
92 // LoggingConsumer class implementation
93 LoggingConsumer::LoggingConsumer(
94 	const entry_ref& logFile,
95 	BMediaAddOn* pAddOn)
96 
97 	:	BMediaNode("LoggingConsumer"),
98 		BBufferConsumer(B_MEDIA_UNKNOWN_TYPE),
99 		BControllable(),
100 		BMediaEventLooper(),
101 		mLogRef(logFile),
102 		mWeb(NULL),
103 		mLateBuffers(0),
104 		mLatency(50 * 1000),		// default to 50 milliseconds
105 		mSpinPercentage(0.10),		// default to spinning 10% of total latency
106 		mPriority(B_URGENT_DISPLAY_PRIORITY),		// !!! testing; will be B_REAL_TIME_PRIORITY for release
107 		mLastLatencyChange(0),
108 		mLastSpinChange(0),
109 		mLastPrioChange(0),
110 		m_pAddOn(pAddOn)
111 {
112 	// spin off the logging thread
113 	mLogger = new LogWriter(logFile);
114 
115 	// parameter-web init moved to NodeRegistered()
116 	// e.moon [11jun99]
117 }
118 
119 LoggingConsumer::~LoggingConsumer()
120 {
121 	PRINT(("~LoggingConsumer()\n"));
122 	BMediaEventLooper::Quit();
123 // ahem:
124 // "Once you've called BControllable::SetParameterWeb(), the node takes
125 //  responsibility for the parameter web object and you shouldn't delete it. "
126 //	SetParameterWeb(NULL);
127 //	delete mWeb;
128 
129 	// delete the logging thread only after the looper thread has quit, otherwise there's
130 	// a potential race condition with the looper thread trying to write to the now-
131 	// deleted log
132 	delete mLogger;
133 }
134 
135 //
136 // Log message filtering control
137 //
138 
139 void
140 LoggingConsumer::SetEnabled(log_what what, bool enable)
141 {
142 	mLogger->SetEnabled(what, enable);
143 }
144 
145 void
146 LoggingConsumer::EnableAllMessages()
147 {
148 	mLogger->EnableAllMessages();
149 }
150 
151 void
152 LoggingConsumer::DisableAllMessages()
153 {
154 	mLogger->DisableAllMessages();
155 }
156 
157 //
158 // BMediaNode methods
159 //
160 
161 
162 BMediaAddOn*
163 LoggingConsumer::AddOn(int32 *internal_id) const
164 {
165 	PRINT(("~LoggingConsumer::AddOn()\n"));
166 	// e.moon [11jun99]
167 	if(m_pAddOn) {
168 		*internal_id = 0;
169 		return m_pAddOn;
170 	} else
171 		return NULL;
172 }
173 
174 void
175 LoggingConsumer::SetRunMode(run_mode mode)
176 {
177 	// !!! Need to handle offline mode etc. properly!
178 	log_message logMsg;
179 	logMsg.now = TimeSource()->Now();
180 	mLogger->Log(LOG_SET_RUN_MODE, logMsg);
181 
182 	BMediaEventLooper::SetRunMode(mode);
183 }
184 
185 void
186 LoggingConsumer::Preroll()
187 {
188 	log_message logMsg;
189 	logMsg.now = TimeSource()->Now();
190 	mLogger->Log(LOG_PREROLL, logMsg);
191 
192 	BMediaEventLooper::Preroll();
193 }
194 
195 void
196 LoggingConsumer::SetTimeSource(BTimeSource* time_source)
197 {
198 	log_message logMsg;
199 	logMsg.now = TimeSource()->Now();
200 	mLogger->Log(LOG_SET_TIME_SOURCE, logMsg);
201 
202 	BMediaNode::SetTimeSource(time_source);
203 }
204 
205 status_t
206 LoggingConsumer::RequestCompleted(const media_request_info &info)
207 {
208 	log_message logMsg;
209 	logMsg.now = TimeSource()->Now();
210 	mLogger->Log(LOG_REQUEST_COMPLETED, logMsg);
211 
212 	return BMediaNode::RequestCompleted(info);
213 }
214 
215 // e.moon [11jun99; testing add-on]
216 status_t
217 LoggingConsumer::DeleteHook(BMediaNode* pNode) {
218 	PRINT(("LoggingConsumer::DeleteHook(%p)\n", pNode));
219 	return BBufferConsumer::DeleteHook(pNode);
220 //	ASSERT(pNode == this);
221 //	delete this;
222 //	return B_OK;
223 }
224 
225 //
226 // BControllable methods
227 //
228 
229 status_t
230 LoggingConsumer::GetParameterValue(int32 id, bigtime_t* last_change, void* value, size_t* ioSize)
231 {
232 	log_message logMsg;
233 	logMsg.now = TimeSource()->Now();
234 	logMsg.param.id = id;
235 	mLogger->Log(LOG_GET_PARAM_VALUE, logMsg);
236 
237 	// return an error if the caller hasn't reserved enough space for the parameter data.
238 	// we know that all of our parameters fit in a float or int32 (4 bytes), so we can just
239 	// check for it once here, instead of on a per-parameter basis
240 	if (*ioSize < sizeof(float)) return B_ERROR;
241 
242 	// write out the designated parameter data
243 	switch (id)
244 	{
245 	case LATENCY_PARAM:
246 		*last_change = mLastLatencyChange;
247 		*((float*) value) = mLatency / 1000;		// the BParameter reads milliseconds, not microseconds
248 		*ioSize = sizeof(float);
249 		break;
250 
251 	case CPU_SPIN_PARAM:
252 		*last_change = mLastSpinChange;
253 		*((float*) value) = mSpinPercentage;
254 		*ioSize = sizeof(float);
255 		break;
256 
257 	case PRIORITY_PARAM:
258 		*last_change = mLastPrioChange;
259 		*((int32*) value) = mPriority;
260 		*ioSize = sizeof(int32);
261 		break;
262 
263 	default:
264 		return B_ERROR;
265 	}
266 
267 	return B_OK;
268 }
269 
270 void
271 LoggingConsumer::SetParameterValue(int32 id, bigtime_t performance_time, const void* value, size_t size)
272 {
273 	log_message logMsg;
274 	logMsg.now = TimeSource()->Now();
275 	logMsg.param.id = id;
276 	mLogger->Log(LOG_SET_PARAM_VALUE, logMsg);
277 
278 	// if it's one of our parameters, enqueue a "set parameter" event for handling at the appropriate time
279 	switch (id)
280 	{
281 	case LATENCY_PARAM:
282 	case CPU_SPIN_PARAM:
283 	case PRIORITY_PARAM:
284 		{
285 			// !!! Change from B_USER_EVENT to B_SET_PARAMETER once it's defined
286 			media_timed_event event(performance_time, BTimedEventQueue::B_USER_EVENT,
287 				(void*) value, BTimedEventQueue::B_NO_CLEANUP, size, id, NULL);
288 			EventQueue()->AddEvent(event);
289 		}
290 		break;
291 
292 	default:		// do nothing for other parameter IDs
293 		break;
294 	}
295 	return;
296 }
297 
298 //
299 // BBufferConsumer methods
300 //
301 
302 status_t
303 LoggingConsumer::HandleMessage(int32 message, const void *data, size_t size)
304 {
305 	log_message logMsg;
306 	logMsg.now = TimeSource()->Now();
307 	mLogger->Log(LOG_HANDLE_MESSAGE, logMsg);
308 
309 	// try each of our superclasses to handle the message
310 	status_t err;
311 	err = BControllable::HandleMessage(message, data, size);
312 	if (err) err = BBufferConsumer::HandleMessage(message, data, size);
313 	if (err) err = BMediaNode::HandleMessage(message, data, size);
314 	return err;
315 }
316 
317 // all of these next methods are pure virtual in BBufferConsumer
318 
319 status_t
320 LoggingConsumer::AcceptFormat(const media_destination& dest, media_format* format)
321 {
322 	char formatStr[256];
323 	string_for_format(*format, formatStr, 255);
324 	PRINT(("LoggingConsumer::AcceptFormat:\n\tformat %s\n", formatStr));
325 
326 	log_message logMsg;
327 	logMsg.now = TimeSource()->Now();
328 	mLogger->Log(LOG_ACCEPT_FORMAT, logMsg);
329 
330 	// return an error if this isn't really our one input's destination
331 	if (dest != mInput.destination) return B_MEDIA_BAD_DESTINATION;
332 
333 	// the destination given really is our input, and we accept any kind of media data,
334 	// so now we just confirm that we can handle whatever the producer asked for.
335 	return B_OK;
336 }
337 
338 status_t
339 LoggingConsumer::GetNextInput(int32* cookie, media_input* out_input)
340 {
341 	// we have a single hardcoded input that can accept any kind of media data
342 	if (0 == *cookie)
343 	{
344 		mInput.format.type = B_MEDIA_UNKNOWN_TYPE;		// accept any format
345 
346 		*out_input = mInput;
347 		*cookie = 1;
348 		return B_OK;
349 	}
350 	else return B_BAD_INDEX;
351 }
352 
353 void
354 LoggingConsumer::DisposeInputCookie(int32 /*cookie*/ )
355 {
356 	// we don't use any kind of state or extra storage for iterating over our
357 	// inputs, so we don't have to do any special disposal of input cookies.
358 }
359 
360 void
361 LoggingConsumer::BufferReceived(BBuffer* buffer)
362 {
363 	bigtime_t bufferStart = buffer->Header()->start_time;
364 	bigtime_t now = TimeSource()->Now();
365 	bigtime_t how_early = bufferStart - EventLatency() - SchedulingLatency() - now;
366 
367 	log_message logMsg;
368 	logMsg.now = now;
369 	logMsg.buffer_data.start_time = bufferStart;
370 	logMsg.buffer_data.offset = how_early;
371 	mLogger->Log(LOG_BUFFER_RECEIVED, logMsg);
372 
373 	// There's a special case here with handling B_MEDIA_PARAMETERS buffers.
374 	// These contain sets of parameter value changes, with their own performance
375 	// times embedded in the buffers.  So, we want to dispatch those parameter
376 	// changes as their own events rather than pushing this buffer on the queue to
377 	// be handled later.
378 	if (B_MEDIA_PARAMETERS == buffer->Header()->type)
379 	{
380 		ApplyParameterData(buffer->Data(), buffer->SizeUsed());
381 		buffer->Recycle();
382 	}
383 	else		// ahh, it's a regular media buffer, so push it on the event queue
384 	{
385 		status_t err;
386 		media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER,
387 			buffer, BTimedEventQueue::B_RECYCLE_BUFFER);
388 		err = EventQueue()->AddEvent(event);
389 
390 		// HandleEvent() will recycle the buffer.  However, if we incurred an error trying to
391 		// put the event into the queue, we have to recycle it ourselves, since HandleEvent()
392 		// will never see the buffer in that case.
393 		if (err) buffer->Recycle();
394 	}
395 }
396 
397 void
398 LoggingConsumer::ProducerDataStatus(const media_destination& for_whom, int32 status, bigtime_t at_performance_time)
399 {
400 	log_message logMsg;
401 	logMsg.now = TimeSource()->Now();
402 	logMsg.data_status.status = status;
403 	mLogger->Log(LOG_PRODUCER_DATA_STATUS, logMsg);
404 
405 	if (for_whom == mInput.destination)
406 	{
407 		media_timed_event event(at_performance_time, BTimedEventQueue::B_DATA_STATUS,
408 			&mInput, BTimedEventQueue::B_NO_CLEANUP, status, 0, NULL);
409 		EventQueue()->AddEvent(event);
410 	}
411 }
412 
413 status_t
414 LoggingConsumer::GetLatencyFor(const media_destination& for_whom, bigtime_t* out_latency, media_node_id* out_timesource)
415 {
416 	// make sure this is one of my valid inputs
417 	if (for_whom != mInput.destination) return B_MEDIA_BAD_DESTINATION;
418 
419 	// report internal latency + downstream latency here, NOT including scheduling latency.
420 	// we're a final consumer (no outputs), so we have no downstream latency.
421 	*out_latency = mLatency;
422 	*out_timesource = TimeSource()->ID();
423 	return B_OK;
424 }
425 
426 status_t
427 LoggingConsumer::Connected(
428 	const media_source& producer,
429 	const media_destination& where,
430 	const media_format& with_format,
431 	media_input* out_input)
432 {
433 
434 	char formatStr[256];
435 	string_for_format(with_format, formatStr, 255);
436 	PRINT(("LoggingConsumer::Connected:\n\tformat %s\n", formatStr));
437 	string_for_format(mInput.format, formatStr, 255);
438 	PRINT(("\tinput format %s\n", formatStr));
439 
440 	log_message logMsg;
441 	logMsg.now = TimeSource()->Now();
442 	mLogger->Log(LOG_CONNECTED, logMsg);
443 
444 	if (where != mInput.destination) return B_MEDIA_BAD_DESTINATION;
445 
446 	// calculate my latency here, because it may depend on buffer sizes/durations, then
447 	// tell the BMediaEventLooper how early we need to get the buffers
448 	SetEventLatency(mLatency);
449 
450 	// record useful information about the connection, and return success
451 	// * e.moon [14jun99]: stores format
452 	mInput.format = with_format;
453 	mInput.source = producer;
454 	*out_input = mInput;
455 	return B_OK;
456 }
457 
458 void
459 LoggingConsumer::Disconnected(
460 	const media_source& producer,
461 	const media_destination& where)
462 {
463 	log_message logMsg;
464 	logMsg.now = TimeSource()->Now();
465 	mLogger->Log(LOG_DISCONNECTED, logMsg);
466 
467 	// wipe out our input record
468 	memset(&mInput, 0, sizeof(mInput));
469 }
470 
471 status_t
472 LoggingConsumer::FormatChanged(
473 	const media_source& producer,
474 	const media_destination& consumer,
475 	int32 change_tag,
476 	const media_format& format)
477 {
478 	log_message logMsg;
479 	logMsg.now = TimeSource()->Now();
480 	mLogger->Log(LOG_FORMAT_CHANGED, logMsg);
481 
482 	return B_OK;
483 }
484 
485 status_t
486 LoggingConsumer::SeekTagRequested(
487 	const media_destination& destination,
488 	bigtime_t in_target_time,
489 	uint32 in_flags,
490 	media_seek_tag* out_seek_tag,
491 	bigtime_t* out_tagged_time,
492 	uint32* out_flags)
493 {
494 	log_message logMsg;
495 	logMsg.now = TimeSource()->Now();
496 	mLogger->Log(LOG_SEEK_TAG, logMsg);
497 
498 	return B_OK;
499 }
500 
501 //
502 // BMediaEventLooper virtual methods
503 //
504 
505 void
506 LoggingConsumer::NodeRegistered()
507 {
508 	log_message logMsg;
509 	logMsg.now = TimeSource()->Now();
510 	mLogger->Log(LOG_REGISTERED, logMsg);
511 
512 	// publish our parameter web
513 	mWeb = build_parameter_web();
514 	SetParameterWeb(mWeb);
515 
516 	// Set our priority and start the BMediaEventLooper's thread
517 	SetPriority(mPriority);
518 	Run();
519 
520 	// Initialize as much of our input as we can, now that the Media Kit really "knows" about us
521 	mInput.destination.port = ControlPort();
522 	mInput.destination.id = 0;
523 	mInput.node = Node();
524 	strcpy(mInput.name, "Logged input");
525 }
526 
527 void
528 LoggingConsumer::Start(bigtime_t performance_time)
529 {
530 	PRINT(("LoggingConsumer::Start(%" B_PRIdBIGTIME "): now %" B_PRIdBIGTIME "\n",
531 		performance_time, TimeSource()->Now()));
532 
533 	log_message logMsg;
534 	logMsg.now = TimeSource()->Now();
535 	mLogger->Log(LOG_START, logMsg);
536 
537 	BMediaEventLooper::Start(performance_time);
538 }
539 
540 void
541 LoggingConsumer::Stop(bigtime_t performance_time, bool immediate)
542 {
543 	log_message logMsg;
544 	logMsg.now = TimeSource()->Now();
545 	mLogger->Log(LOG_STOP, logMsg);
546 
547 	BMediaEventLooper::Stop(performance_time, immediate);
548 }
549 
550 void
551 LoggingConsumer::Seek(bigtime_t media_time, bigtime_t performance_time)
552 {
553 	log_message logMsg;
554 	logMsg.now = TimeSource()->Now();
555 	mLogger->Log(LOG_SEEK, logMsg);
556 
557 	BMediaEventLooper::Seek(media_time, performance_time);
558 }
559 
560 void
561 LoggingConsumer::TimeWarp(bigtime_t at_real_time, bigtime_t to_performance_time)
562 {
563 	log_message logMsg;
564 	logMsg.now = TimeSource()->Now();
565 	mLogger->Log(LOG_TIMEWARP, logMsg);
566 
567 	BMediaEventLooper::TimeWarp(at_real_time, to_performance_time);
568 }
569 
570 void
571 LoggingConsumer::HandleEvent(const media_timed_event *event, bigtime_t /* lateness */, bool /* realTimeEvent */)
572 {
573 	log_message logMsg;
574 	logMsg.now = TimeSource()->Now();
575 	mLogger->Log(LOG_HANDLE_EVENT, logMsg);
576 
577 	switch (event->type)
578 	{
579 	case BTimedEventQueue::B_HANDLE_BUFFER:
580 		{
581 			BBuffer* buffer = const_cast<BBuffer*>((BBuffer*) event->pointer);
582 			if (buffer)
583 			{
584 				media_header* hdr = buffer->Header();
585 				if (hdr->destination == mInput.destination.id)
586 				{
587 					bigtime_t now = TimeSource()->Now();
588 					bigtime_t perf_time = hdr->start_time;
589 
590 					// the how_early calculated here doesn't include scheduling latency because
591 					// we've already been scheduled to handle the buffer
592 					bigtime_t how_early = perf_time - mLatency - now;
593 
594 					// logMsg.now is already set
595 					logMsg.buffer_data.start_time = perf_time;
596 					logMsg.buffer_data.offset = how_early;
597 					mLogger->Log(LOG_BUFFER_HANDLED, logMsg);
598 
599 					// if the buffer is late, we ignore it and report the fact to the producer
600 					// who sent it to us
601 					if (how_early < 0)
602 					{
603 						mLateBuffers++;
604 						NotifyLateProducer(mInput.source, -how_early, perf_time);
605 					}
606 					else
607 					{
608 						// burn some percentage of our stated latency in CPU time (controlled by
609 						// a BParameter).  this simulates a user-configurable amount of CPU cost
610 						// associated with the consumer.
611 						bigtime_t spin_start = ::system_time();
612 						bigtime_t spin_now = spin_start;
613 						bigtime_t usecToSpin = bigtime_t(mSpinPercentage / 100.0 * mLatency);
614 						while (spin_now - spin_start < usecToSpin)
615 						{
616 							for (long k = 0; k < 1000000; k++) { /* intentionally blank */ }
617 							spin_now = ::system_time();
618 						}
619 					}
620 
621 					// we're done "processing the buffer;" now we recycle it and return to the loop
622 					buffer->Recycle();
623 				}
624 				else
625 				{
626 					//fprintf(stderr, "* Woah!  Got a buffer for a different destination!\n");
627 				}
628 			}
629 		}
630 		break;
631 
632 	// !!! change to B_PARAMETER as soon as it's available
633 
634 	// +++++ e.moon [16jun99]
635 	// !!! this can't be right: the parameter value is accessed by the pointer
636 	//     originally passed to SetParameterValue().  there's no guarantee that
637 	//     value's still valid, is there?
638 
639 	case BTimedEventQueue::B_USER_EVENT:
640 		{
641 			size_t dataSize = size_t(event->data);
642 			int32 param = int32(event->bigdata);
643 			logMsg.param.id = param;
644 
645 			// handle the message if there's sufficient data provided.  we only check against
646 			// sizeof(float) because all of our parameters happen to be 4 bytes.  if various
647 			// parameters took different amounts of data, we'd check the size on a per-parameter
648 			// basis.
649 			if (dataSize >= sizeof(float)) switch (param)
650 			{
651 			case LATENCY_PARAM:
652 				{
653 					float value = *((float*) event->pointer);
654 					mLatency = bigtime_t(value* 1000);
655 					mLastLatencyChange = logMsg.now;
656 
657 					// my latency just changed, so reconfigure the BMediaEventLooper
658 					// to give me my events at the proper time
659 					SetEventLatency(mLatency);
660 
661 					// tell the producer that my latency changed, and broadcast a message
662 					// about the parameter change to any applications that may be looking
663 					// for it through the BMediaRoster::StartWatching() mechanism.
664 					//
665 					// if we had more than one input, we'd need to tell *all* producers about
666 					// the change in our latency.
667 					SendLatencyChange(mInput.source, mInput.destination, EventLatency() + SchedulingLatency());
668 					BroadcastNewParameterValue(logMsg.now, param, &value, sizeof(value));
669 
670 					// log the new latency value, for recordkeeping
671 					logMsg.param.value = value;
672 					mLogger->Log(LOG_SET_PARAM_HANDLED, logMsg);
673 				}
674 				break;
675 
676 			case CPU_SPIN_PARAM:
677 				{
678 					float value = *((float*) event->pointer);
679 					mSpinPercentage = value;
680 					mLastSpinChange = logMsg.now;
681 					BroadcastNewParameterValue(logMsg.now, param, &value, sizeof(value));
682 					logMsg.param.value = value;
683 					mLogger->Log(LOG_SET_PARAM_HANDLED, logMsg);
684 				}
685 				break;
686 
687 			case PRIORITY_PARAM:
688 				{
689 					mPriority = *((int32*) event->pointer);
690 					// DO NOT use ::set_thead_priority() to directly alter the node's control
691 					// thread priority.  BMediaEventLooper tracks the priority itself and recalculates
692 					// the node's scheduling latency whenever SetPriority() is called.  This is VERY
693 					// important for correct functioning of a node chain.  You should *only* alter a
694 					// BMediaEventLooper's priority by calling its SetPriority() method.
695 					SetPriority(mPriority);
696 
697 					mLastPrioChange = logMsg.now;
698 					BroadcastNewParameterValue(logMsg.now, param, &mPriority, sizeof(mPriority));
699 					logMsg.param.value = (float) mPriority;
700 					mLogger->Log(LOG_SET_PARAM_HANDLED, logMsg);
701 				}
702 				break;
703 
704 			// log the fact that we "handled" a "set parameter" event for a
705 			// nonexistent parameter
706 			default:
707 				mLogger->Log(LOG_INVALID_PARAM_HANDLED, logMsg);
708 				break;
709 			}
710 		}
711 		break;
712 
713 	case BTimedEventQueue::B_START:
714 		// okay, let's go!
715 		mLogger->Log(LOG_START_HANDLED, logMsg);
716 		break;
717 
718 	case BTimedEventQueue::B_STOP:
719 		mLogger->Log(LOG_STOP_HANDLED, logMsg);
720 		// stopping implies not handling any more buffers.  So, we flush all pending
721 		// buffers out of the event queue before returning to the event loop.
722 		EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER);
723 		break;
724 
725 	case BTimedEventQueue::B_SEEK:
726 		// seeking the log doesn't make any sense, so we just log that we handled the seek
727 		// and return without doing anything else
728 		mLogger->Log(LOG_SEEK_HANDLED, logMsg);
729 		break;
730 
731 	case BTimedEventQueue::B_WARP:
732 		// similarly, time warps aren't meaningful to the logger, so just record it and return
733 		mLogger->Log(LOG_WARP_HANDLED, logMsg);
734 		break;
735 
736 	case BTimedEventQueue::B_DATA_STATUS:
737 		// we really don't care about the producer's data status, but this is where
738 		// we'd do something about it if we did.
739 		logMsg.data_status.status = event->data;
740 		mLogger->Log(LOG_DATA_STATUS_HANDLED, logMsg);
741 		break;
742 
743 	default:
744 		// hmm, someone enqueued a message that we don't understand.  log and ignore it.
745 		logMsg.unknown.what = event->type;
746 		mLogger->Log(LOG_HANDLE_UNKNOWN, logMsg);
747 		break;
748 	}
749 }
750