xref: /haiku/src/servers/media/media_server.cpp (revision 51978af14a173e7fae0563b562be5603bc652aeb)
1 /*
2  * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files or portions
6  * thereof (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so, subject
10  * to the following conditions:
11  *
12  *  * Redistributions of source code must retain the above copyright notice,
13  *    this list of conditions and the following disclaimer.
14  *
15  *  * Redistributions in binary form must reproduce the above copyright notice
16  *    in the  binary, as well as this list of conditions and the following
17  *    disclaimer in the documentation and/or other materials provided with
18  *    the distribution.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  *
28  */
29 
30 /* to comply with the license above, do not remove the following line */
31 char __dont_remove_copyright_from_binary[] = "Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>";
32 
33 #include <Application.h>
34 #include <stdio.h>
35 #include <Messenger.h>
36 #include <MediaDefs.h>
37 #include <MediaFormats.h>
38 #include <Autolock.h>
39 #include <string.h>
40 #include "NotificationManager.h"
41 #include "ServerInterface.h"
42 #include "DataExchange.h"
43 #include "BufferManager.h"
44 #include "NodeManager.h"
45 #include "AppManager.h"
46 #include "MediaMisc.h"
47 #include "media_server.h"
48 #include "debug.h"
49 
50 /*
51  *
52  * An implementation of a new media_server for the OpenBeOS MediaKit
53  * Started by Marcus Overhagen <marcus@overhagen.de> on 2001-10-25
54  *
55  */
56 
57 NotificationManager *gNotificationManager;
58 BufferManager *gBufferManager;
59 AppManager *gAppManager;
60 NodeManager *gNodeManager;
61 
62 namespace BPrivate { namespace media {
63 	extern team_id team;
64 } } // BPrivate::media
65 
66 
67 #define REPLY_TIMEOUT ((bigtime_t)500000)
68 
69 class ServerApp : BApplication
70 {
71 public:
72 	ServerApp();
73 	~ServerApp();
74 
75 	bool QuitRequested();
76 	void HandleMessage(int32 code, void *data, size_t size);
77 	void ArgvReceived(int32 argc, char **argv);
78 	static int32 controlthread(void *arg);
79 //	void StartSystemTimeSource();
80 
81 /* functionality not yet implemented
82 00014a00 T _ServerApp::_ServerApp(void)
83 00014e1c T _ServerApp::~_ServerApp(void)
84 00014ff4 T _ServerApp::MessageReceived(BMessage *);
85 00015840 T _ServerApp::QuitRequested(void)
86 00015b50 T _ServerApp::_DoNotify(command_data *)
87 00015d18 T _ServerApp::_UnregisterApp(long, bool)
88 00018e90 T _ServerApp::AddOnHost(void)
89 00019530 T _ServerApp::AboutRequested(void)
90 00019d04 T _ServerApp::AddPurgableBufferGroup(long, long, long, void *)
91 00019db8 T _ServerApp::CancelPurgableBufferGroupCleanup(long)
92 00019e50 T _ServerApp::DirtyWork(void)
93 0001a4bc T _ServerApp::ArgvReceived(long, char **)
94 0001a508 T _ServerApp::CleanupPurgedBufferGroup(_ServerApp::purgable_buffer_group const &, bool)
95 0001a5dc T _ServerApp::DirtyWorkLaunch(void *)
96 0001a634 T _ServerApp::SetQuitMode(bool)
97 0001a648 T _ServerApp::IsQuitMode(void) const
98 0001a658 T _ServerApp::BroadcastCurrentStateTo(BMessenger &)
99 0001adcc T _ServerApp::ReadyToRun(void)
100 */
101 
102 private:
103 	port_id		control_port;
104 	thread_id	control_thread;
105 
106 	BLocker *fLocker;
107 
108 	void MessageReceived(BMessage *msg);
109 	typedef BApplication inherited;
110 };
111 
112 ServerApp::ServerApp()
113  	: BApplication(NEW_MEDIA_SERVER_SIGNATURE),
114 	fLocker(new BLocker("media server locker"))
115 {
116 
117  	gNotificationManager = new NotificationManager;
118  	gBufferManager = new BufferManager;
119 	gAppManager = new AppManager;
120 	gNodeManager = new NodeManager;
121 
122 	control_port = create_port(64, MEDIA_SERVER_PORT_NAME);
123 	control_thread = spawn_thread(controlthread, "media_server control", 105, this);
124 	resume_thread(control_thread);
125 
126 //	StartSystemTimeSource();
127 	gNodeManager->LoadState();
128 	gAppManager->StartAddonServer();
129 }
130 
131 ServerApp::~ServerApp()
132 {
133 	TRACE("ServerApp::~ServerApp()\n");
134 	delete gNotificationManager;
135 	delete gBufferManager;
136 	delete gAppManager;
137 	delete gNodeManager;
138 	delete fLocker;
139 	delete_port(control_port);
140 	status_t err;
141 	wait_for_thread(control_thread,&err);
142 }
143 
144 void ServerApp::ArgvReceived(int32 argc, char **argv)
145 {
146 	for (int arg = 1; arg < argc; arg++) {
147 		if (strstr(argv[arg], "dump")) {
148 			gAppManager->Dump();
149 			gNodeManager->Dump();
150 			gBufferManager->Dump();
151 			gNotificationManager->Dump();
152 		}
153 		if (strstr(argv[arg], "buffer")) {
154 			gBufferManager->Dump();
155 		}
156 		if (strstr(argv[arg], "node")) {
157 			gNodeManager->Dump();
158 		}
159 		if (strstr(argv[arg], "quit")) {
160 			PostMessage(B_QUIT_REQUESTED);
161 		}
162 	}
163 }
164 
165 bool
166 ServerApp::QuitRequested()
167 {
168 	TRACE("ServerApp::QuitRequested()\n");
169 	gNodeManager->SaveState();
170 	gAppManager->TerminateAddonServer();
171 	return true;
172 }
173 
174 /*
175 void
176 ServerApp::StartSystemTimeSource()
177 {
178 	TRACE("StartSystemTimeSource enter\n");
179 	status_t rv;
180 
181 	TRACE("StartSystemTimeSource creating object\n");
182 
183 	// register a dummy node
184 	media_node node;
185 	rv = gNodeManager->RegisterNode(&node.node, -1, 0, "System Clock", B_TIME_SOURCE, SYSTEM_TIMESOURCE_CONTROL_PORT, BPrivate::media::team);
186 	ASSERT(rv == B_OK);
187 
188 	ASSERT(node.node == NODE_SYSTEM_TIMESOURCE_ID);
189 
190 	TRACE("StartSystemTimeSource setting as default\n");
191 
192 	rv = gNodeManager->SetDefaultNode(SYSTEM_TIME_SOURCE, &node, NULL, NULL);
193 	ASSERT(rv == B_OK);
194 
195 	TRACE("StartSystemTimeSource leave\n");
196 }
197 */
198 
199 void
200 ServerApp::HandleMessage(int32 code, void *data, size_t size)
201 {
202 	status_t rv;
203 	TRACE("ServerApp::HandleMessage %#lx enter\n", code);
204 	switch (code) {
205 		case SERVER_CHANGE_ADDON_FLAVOR_INSTANCES_COUNT:
206 		{
207 			const server_change_addon_flavor_instances_count_request *request = reinterpret_cast<const server_change_addon_flavor_instances_count_request *>(data);
208 			server_change_addon_flavor_instances_count_reply reply;
209 			ASSERT(request->delta == 1 || request->delta == -1);
210 			if (request->delta == 1)
211 				rv = gNodeManager->IncrementAddonFlavorInstancesCount(request->addonid,	request->flavorid, request->team);
212 			else
213 				rv = gNodeManager->DecrementAddonFlavorInstancesCount(request->addonid,	request->flavorid, request->team);
214 			request->SendReply(rv, &reply, sizeof(reply));
215 			break;
216 		}
217 
218 		case SERVER_RESCAN_DEFAULTS:
219 		{
220 			gNodeManager->RescanDefaultNodes();
221 			break;
222 		}
223 
224 		case SERVER_REGISTER_ADDONSERVER:
225 		{
226 			const server_register_addonserver_request *request = reinterpret_cast<const server_register_addonserver_request *>(data);
227 			server_register_addonserver_reply reply;
228 			rv = gAppManager->RegisterAddonServer(request->team);
229 			request->SendReply(rv, &reply, sizeof(reply));
230 			break;
231 		}
232 
233 		case SERVER_REGISTER_APP:
234 		{
235 			const server_register_app_request *request = reinterpret_cast<const server_register_app_request *>(data);
236 			server_register_app_reply reply;
237 			rv = gAppManager->RegisterTeam(request->team, request->messenger);
238 			request->SendReply(rv, &reply, sizeof(reply));
239 			break;
240 		}
241 
242 		case SERVER_UNREGISTER_APP:
243 		{
244 			const server_unregister_app_request *request = reinterpret_cast<const server_unregister_app_request *>(data);
245 			server_unregister_app_reply reply;
246 			rv = gAppManager->UnregisterTeam(request->team);
247 			request->SendReply(rv, &reply, sizeof(reply));
248 			break;
249 		}
250 
251 		case SERVER_GET_MEDIAADDON_REF:
252 		{
253 			server_get_mediaaddon_ref_request *msg = (server_get_mediaaddon_ref_request *)data;
254 			server_get_mediaaddon_ref_reply reply;
255 			entry_ref tempref;
256 			reply.result = gNodeManager->GetAddonRef(&tempref, msg->addonid);
257 			reply.ref = tempref;
258 			write_port(msg->reply_port, 0, &reply, sizeof(reply));
259 			break;
260 		}
261 
262 		case SERVER_NODE_ID_FOR:
263 		{
264 			const server_node_id_for_request *request = reinterpret_cast<const server_node_id_for_request *>(data);
265 			server_node_id_for_reply reply;
266 			rv = gNodeManager->FindNodeId(&reply.nodeid, request->port);
267 			request->SendReply(rv, &reply, sizeof(reply));
268 			break;
269 		}
270 
271 		case SERVER_GET_LIVE_NODE_INFO:
272 		{
273 			const server_get_live_node_info_request *request = reinterpret_cast<const server_get_live_node_info_request *>(data);
274 			server_get_live_node_info_reply reply;
275 			rv = gNodeManager->GetLiveNodeInfo(&reply.live_info, request->node);
276 			request->SendReply(rv, &reply, sizeof(reply));
277 			break;
278 		}
279 
280 		case SERVER_GET_LIVE_NODES:
281 		{
282 			const server_get_live_nodes_request *request = reinterpret_cast<const server_get_live_nodes_request *>(data);
283 			server_get_live_nodes_reply reply;
284 			Stack<live_node_info> livenodes;
285 			rv = gNodeManager->GetLiveNodes(
286 					&livenodes,
287 					request->maxcount,
288 					request->has_input ? &request->inputformat : NULL,
289 					request->has_output ? &request->outputformat : NULL,
290 					request->has_name ? request->name : NULL,
291 					request->require_kinds);
292 			reply.count = livenodes.CountItems();
293 			if (reply.count <= MAX_LIVE_INFO) {
294 				for (int32 index = 0; index < reply.count; index++)
295 					livenodes.Pop(&reply.live_info[index]);
296 				reply.area = -1;
297 			} else {
298 				// we create an area here, and pass it to the library, where it will be deleted.
299 				live_node_info *start_addr;
300 				size_t size;
301 				size = ((reply.count * sizeof(live_node_info)) + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
302 				reply.area = create_area("get live nodes", reinterpret_cast<void **>(&start_addr), B_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
303 				if (reply.area < B_OK) {
304 					ERROR("SERVER_GET_LIVE_NODES: failed to create area, error %#lx\n", reply.area);
305 					reply.count = 0;
306 					rv = B_ERROR;
307 				} else {
308 					for (int32 index = 0; index < reply.count; index++)
309 						livenodes.Pop(&start_addr[index]);
310 				}
311 			}
312 			rv = request->SendReply(rv, &reply, sizeof(reply));
313 			if (rv != B_OK)
314 				delete_area(reply.area); // if we couldn't send the message, delete the area
315 			break;
316 		}
317 
318 		case SERVER_GET_NODE_FOR:
319 		{
320 			const server_get_node_for_request *request = reinterpret_cast<const server_get_node_for_request *>(data);
321 			server_get_node_for_reply reply;
322 			rv = gNodeManager->GetCloneForId(&reply.clone, request->nodeid, request->team);
323 			request->SendReply(rv, &reply, sizeof(reply));
324 			break;
325 		}
326 
327 		case SERVER_RELEASE_NODE:
328 		{
329 			const server_release_node_request *request = reinterpret_cast<const server_release_node_request *>(data);
330 			server_release_node_reply reply;
331 			rv = gNodeManager->ReleaseNode(request->node, request->team);
332 			request->SendReply(rv, &reply, sizeof(reply));
333 			break;
334 		}
335 
336 		case SERVER_REGISTER_NODE:
337 		{
338 			const server_register_node_request *request = reinterpret_cast<const server_register_node_request *>(data);
339 			server_register_node_reply reply;
340 			rv = gNodeManager->RegisterNode(&reply.nodeid, request->addon_id, request->addon_flavor_id, request->name, request->kinds, request->port, request->team);
341 			request->SendReply(rv, &reply, sizeof(reply));
342 			break;
343 		}
344 
345 		case SERVER_UNREGISTER_NODE:
346 		{
347 			const server_unregister_node_request *request = reinterpret_cast<const server_unregister_node_request *>(data);
348 			server_unregister_node_reply reply;
349 			rv = gNodeManager->UnregisterNode(&reply.addonid, &reply.flavorid, request->nodeid, request->team);
350 			request->SendReply(rv, &reply, sizeof(reply));
351 			break;
352 		}
353 
354 		case SERVER_PUBLISH_INPUTS:
355 		{
356 			const server_publish_inputs_request *request = reinterpret_cast<const server_publish_inputs_request *>(data);
357 			server_publish_inputs_reply reply;
358 			if (request->count <= MAX_INPUTS) {
359 				rv = gNodeManager->PublishInputs(request->node, request->inputs, request->count);
360 			} else {
361 				media_input *inputs;
362 				area_id clone;
363 				clone = clone_area("media_inputs clone", reinterpret_cast<void **>(&inputs), B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, request->area);
364 				if (clone < B_OK) {
365 					ERROR("SERVER_PUBLISH_INPUTS: failed to clone area, error %#lx\n", clone);
366 					rv = B_ERROR;
367 				} else {
368 					rv = gNodeManager->PublishInputs(request->node, inputs, request->count);
369 					delete_area(clone);
370 				}
371 			}
372 			request->SendReply(rv, &reply, sizeof(reply));
373 			break;
374 		}
375 
376 		case SERVER_PUBLISH_OUTPUTS:
377 		{
378 			const server_publish_outputs_request *request = reinterpret_cast<const server_publish_outputs_request *>(data);
379 			server_publish_outputs_reply reply;
380 			if (request->count <= MAX_OUTPUTS) {
381 				rv = gNodeManager->PublishOutputs(request->node, request->outputs, request->count);
382 			} else {
383 				media_output *outputs;
384 				area_id clone;
385 				clone = clone_area("media_outputs clone", reinterpret_cast<void **>(&outputs), B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, request->area);
386 				if (clone < B_OK) {
387 					ERROR("SERVER_PUBLISH_OUTPUTS: failed to clone area, error %#lx\n", clone);
388 					rv = B_ERROR;
389 				} else {
390 					rv = gNodeManager->PublishOutputs(request->node, outputs, request->count);
391 					delete_area(clone);
392 				}
393 			}
394 			request->SendReply(rv, &reply, sizeof(reply));
395 			break;
396 		}
397 
398 		case SERVER_GET_NODE:
399 		{
400 			const server_get_node_request *request = reinterpret_cast<const server_get_node_request *>(data);
401 			server_get_node_reply reply;
402 			rv = gNodeManager->GetClone(&reply.node, reply.input_name, &reply.input_id, request->type, request->team);
403 			request->SendReply(rv, &reply, sizeof(reply));
404 			break;
405 		}
406 
407 		case SERVER_SET_NODE:
408 		{
409 			const server_set_node_request *request = reinterpret_cast<const server_set_node_request *>(data);
410 			server_set_node_reply reply;
411 			rv = gNodeManager->SetDefaultNode(request->type, request->use_node ? &request->node : NULL, request->use_dni ? &request->dni : NULL, request->use_input ?  &request->input : NULL);
412 			request->SendReply(rv, &reply, sizeof(reply));
413 			break;
414 		}
415 
416 		case SERVER_GET_DORMANT_NODE_FOR:
417 		{
418 			const server_get_dormant_node_for_request *request = reinterpret_cast<const server_get_dormant_node_for_request *>(data);
419 			server_get_dormant_node_for_reply reply;
420 			rv = gNodeManager->GetDormantNodeInfo(&reply.node_info, request->node);
421 			request->SendReply(rv, &reply, sizeof(reply));
422 			break;
423 		}
424 
425 		case SERVER_GET_INSTANCES_FOR:
426 		{
427 			const server_get_instances_for_request *request = reinterpret_cast<const server_get_instances_for_request *>(data);
428 			server_get_instances_for_reply reply;
429 			rv = gNodeManager->GetInstances(reply.node_id, &reply.count, min_c(request->maxcount, MAX_NODE_ID), request->addon_id, request->addon_flavor_id);
430 			if (reply.count == MAX_NODE_ID && request->maxcount > MAX_NODE_ID) { // XXX might be fixed by using an area
431 				PRINT(1, "Warning: SERVER_GET_INSTANCES_FOR: returning possibly truncated list of node id's\n");
432 			}
433 			request->SendReply(rv, &reply, sizeof(reply));
434 			break;
435 		}
436 
437 		case SERVER_REGISTER_MEDIAADDON:
438 		{
439 			server_register_mediaaddon_request *msg = (server_register_mediaaddon_request *)data;
440 			server_register_mediaaddon_reply reply;
441 			gNodeManager->RegisterAddon(msg->ref, &reply.addonid);
442 			write_port(msg->reply_port, 0, &reply, sizeof(reply));
443 			break;
444 		}
445 
446 		case SERVER_UNREGISTER_MEDIAADDON:
447 		{
448 			server_unregister_mediaaddon_command *msg = (server_unregister_mediaaddon_command *)data;
449 			gNodeManager->UnregisterAddon(msg->addonid);
450 			break;
451 		}
452 
453 		case SERVER_REGISTER_DORMANT_NODE:
454 		{
455 			xfer_server_register_dormant_node *msg = (xfer_server_register_dormant_node *)data;
456 			dormant_flavor_info dfi;
457 			if (msg->purge_id > 0)
458 				gNodeManager->InvalidateDormantFlavorInfo(msg->purge_id);
459 			rv = dfi.Unflatten(msg->dfi_type, &(msg->dfi), msg->dfi_size);
460 			ASSERT(rv == B_OK);
461 			gNodeManager->AddDormantFlavorInfo(dfi);
462 			break;
463 		}
464 
465 		case SERVER_GET_DORMANT_NODES:
466 		{
467 			xfer_server_get_dormant_nodes *msg = (xfer_server_get_dormant_nodes *)data;
468 			xfer_server_get_dormant_nodes_reply reply;
469 			dormant_node_info * infos = new dormant_node_info[msg->maxcount];
470 			reply.count = msg->maxcount;
471 			reply.result = gNodeManager->GetDormantNodes(
472 				infos,
473 				&reply.count,
474 				msg->has_input ? &msg->inputformat : NULL,
475 				msg->has_output ? &msg->outputformat : NULL,
476 				msg->has_name ? msg->name : NULL,
477 				msg->require_kinds,
478 				msg->deny_kinds);
479 			if (reply.result != B_OK)
480 				reply.count = 0;
481 			write_port(msg->reply_port, 0, &reply, sizeof(reply));
482 			if (reply.count > 0)
483 				write_port(msg->reply_port, 0, infos, reply.count * sizeof(dormant_node_info));
484 			delete [] infos;
485 			break;
486 		}
487 
488 		case SERVER_GET_DORMANT_FLAVOR_INFO:
489 		{
490 			xfer_server_get_dormant_flavor_info *msg = (xfer_server_get_dormant_flavor_info *)data;
491 			dormant_flavor_info dfi;
492 			status_t rv;
493 
494 			rv = gNodeManager->GetDormantFlavorInfoFor(msg->addon, msg->flavor_id, &dfi);
495 			if (rv != B_OK) {
496 				xfer_server_get_dormant_flavor_info_reply reply;
497 				reply.result = rv;
498 				write_port(msg->reply_port, 0, &reply, sizeof(reply));
499 			} else {
500 				xfer_server_get_dormant_flavor_info_reply *reply;
501 				int replysize;
502 				replysize = sizeof(xfer_server_get_dormant_flavor_info_reply) + dfi.FlattenedSize();
503 				reply = (xfer_server_get_dormant_flavor_info_reply *)malloc(replysize);
504 
505 				reply->dfi_size = dfi.FlattenedSize();
506 				reply->dfi_type = dfi.TypeCode();
507 				reply->result = dfi.Flatten(reply->dfi, reply->dfi_size);
508 				write_port(msg->reply_port, 0, reply, replysize);
509 				free(reply);
510 			}
511 			break;
512 		}
513 
514 		case SERVER_SET_NODE_CREATOR:
515 		{
516 			const server_set_node_creator_request *request = reinterpret_cast<const server_set_node_creator_request *>(data);
517 			server_set_node_creator_reply reply;
518 			rv = gNodeManager->SetNodeCreator(request->node, request->creator);
519 			request->SendReply(rv, &reply, sizeof(reply));
520 			break;
521 		}
522 
523 		case SERVER_GET_SHARED_BUFFER_AREA:
524 		{
525 			const server_get_shared_buffer_area_request *request = reinterpret_cast<const server_get_shared_buffer_area_request *>(data);
526 			server_get_shared_buffer_area_reply reply;
527 
528 			reply.area = gBufferManager->SharedBufferListID();
529 			request->SendReply(B_OK, &reply, sizeof(reply));
530 			break;
531 		}
532 
533 		case SERVER_REGISTER_BUFFER:
534 		{
535 			const server_register_buffer_request *request = reinterpret_cast<const server_register_buffer_request *>(data);
536 			server_register_buffer_reply reply;
537 			status_t status;
538 			if (request->info.buffer == 0) {
539 				reply.info = request->info; //size, offset, flags, area is kept
540 				// get a new beuffer id into reply.info.buffer
541 				status = gBufferManager->RegisterBuffer(request->team, request->info.size, request->info.flags, request->info.offset, request->info.area, &reply.info.buffer);
542 			} else {
543 				reply.info = request->info; //buffer id is kept
544 				status = gBufferManager->RegisterBuffer(request->team, request->info.buffer, &reply.info.size, &reply.info.flags, &reply.info.offset, &reply.info.area);
545 			}
546 			request->SendReply(status, &reply, sizeof(reply));
547 			break;
548 		}
549 
550 		case SERVER_UNREGISTER_BUFFER:
551 		{
552 			const server_unregister_buffer_command *cmd = reinterpret_cast<const server_unregister_buffer_command *>(data);
553 
554 			gBufferManager->UnregisterBuffer(cmd->team, cmd->bufferid);
555 			break;
556 		}
557 
558 		default:
559 			printf("media_server: received unknown message code %#08lx\n",code);
560 	}
561 	TRACE("ServerApp::HandleMessage %#lx leave\n", code);
562 }
563 
564 int32
565 ServerApp::controlthread(void *arg)
566 {
567 	char data[B_MEDIA_MESSAGE_SIZE];
568 	ServerApp *app;
569 	ssize_t size;
570 	int32 code;
571 
572 	app = (ServerApp *)arg;
573 	while ((size = read_port_etc(app->control_port, &code, data, sizeof(data), 0, 0)) > 0)
574 		app->HandleMessage(code, data, size);
575 
576 	return 0;
577 }
578 
579 void ServerApp::MessageReceived(BMessage *msg)
580 {
581 	TRACE("ServerApp::MessageReceived %lx enter\n", msg->what);
582 	switch (msg->what) {
583 		case MEDIA_SERVER_REQUEST_NOTIFICATIONS: gNotificationManager->EnqueueMessage(msg); break;
584 		case MEDIA_SERVER_CANCEL_NOTIFICATIONS: gNotificationManager->EnqueueMessage(msg); break;
585 		case MEDIA_SERVER_SEND_NOTIFICATIONS: gNotificationManager->EnqueueMessage(msg); break;
586 		default:
587 			printf("\nnew media server: unknown message received\n");
588 			msg->PrintToStream();
589 	}
590 	TRACE("ServerApp::MessageReceived %lx leave\n", msg->what);
591 }
592 
593 int main()
594 {
595 	new ServerApp;
596 	be_app->Run();
597 	delete be_app;
598 	return 0;
599 }
600