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