xref: /haiku/src/servers/media/media_server.cpp (revision 16c83730262f1e4f0fc69d80744bb36dcfbbe3af)
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 
31 /* to comply with the license above, do not remove the following line */
32 char __dont_remove_copyright_from_binary[] = "Copyright (c) 2002, 2003 "
33 	"Marcus Overhagen <Marcus@Overhagen.de>";
34 
35 
36 #include <stdio.h>
37 #include <string.h>
38 
39 #include <Alert.h>
40 #include <Application.h>
41 #include <Autolock.h>
42 #include <Directory.h>
43 #include <Roster.h>
44 #include <MediaDefs.h>
45 #include <MediaFormats.h>
46 #include <Messenger.h>
47 
48 #include <syscalls.h>
49 
50 #include "AddOnManager.h"
51 #include "AppManager.h"
52 #include "BufferManager.h"
53 #include "DataExchange.h"
54 #include "FormatManager.h"
55 #include "MediaMisc.h"
56 #include "MediaFilesManager.h"
57 #include "NodeManager.h"
58 #include "NotificationManager.h"
59 #include "ServerInterface.h"
60 #include "debug.h"
61 #include "media_server.h"
62 
63 
64 AddOnManager* gAddOnManager;
65 AppManager* gAppManager;
66 BufferManager* gBufferManager;
67 FormatManager* gFormatManager;
68 MediaFilesManager* gMediaFilesManager;
69 NodeManager* gNodeManager;
70 NotificationManager* gNotificationManager;
71 
72 
73 #define REPLY_TIMEOUT ((bigtime_t)500000)
74 
75 
76 class ServerApp : BApplication {
77 public:
78 	ServerApp();
79 	~ServerApp();
80 
81 protected:
82 	virtual void				ArgvReceived(int32 argc, char** argv);
83 	virtual void				ReadyToRun();
84 	virtual bool				QuitRequested();
85 	virtual void				MessageReceived(BMessage* message);
86 
87 private:
88 			void				_HandleMessage(int32 code, const void* data,
89 									size_t size);
90 			void				_LaunchAddOnServer();
91 			void				_QuitAddOnServer();
92 
93 private:
94 			port_id				_ControlPort() const { return fControlPort; }
95 
96 	static	int32				_ControlThread(void* arg);
97 
98 			BLocker				fLocker;
99 			port_id				fControlPort;
100 			thread_id			fControlThread;
101 };
102 
103 
104 ServerApp::ServerApp()
105  	:
106  	BApplication(B_MEDIA_SERVER_SIGNATURE),
107 	fLocker("media server locker")
108 {
109  	gNotificationManager = new NotificationManager;
110  	gBufferManager = new BufferManager;
111 	gAppManager = new AppManager;
112 	gNodeManager = new NodeManager;
113 	gMediaFilesManager = new MediaFilesManager;
114 	gFormatManager = new FormatManager;
115 	gAddOnManager = new AddOnManager;
116 
117 	fControlPort = create_port(64, MEDIA_SERVER_PORT_NAME);
118 	fControlThread = spawn_thread(_ControlThread, "media_server control", 105,
119 		this);
120 	resume_thread(fControlThread);
121 }
122 
123 
124 ServerApp::~ServerApp()
125 {
126 	TRACE("ServerApp::~ServerApp()\n");
127 
128 	delete_port(fControlPort);
129 	wait_for_thread(fControlThread, NULL);
130 
131 	delete gAddOnManager;
132 	delete gNotificationManager;
133 	delete gBufferManager;
134 	delete gAppManager;
135 	delete gNodeManager;
136 	delete gMediaFilesManager;
137 	delete gFormatManager;
138 }
139 
140 
141 void
142 ServerApp::ReadyToRun()
143 {
144 	gNodeManager->LoadState();
145 	gFormatManager->LoadState();
146 
147 	// make sure any previous media_addon_server is gone
148 	_QuitAddOnServer();
149 	// and start a new one
150 	_LaunchAddOnServer();
151 
152 	gAddOnManager->LoadState();
153 }
154 
155 
156 bool
157 ServerApp::QuitRequested()
158 {
159 	TRACE("ServerApp::QuitRequested()\n");
160 	gMediaFilesManager->SaveState();
161 	gNodeManager->SaveState();
162 	gFormatManager->SaveState();
163 	gAddOnManager->SaveState();
164 
165 	_QuitAddOnServer();
166 
167 	return true;
168 }
169 
170 
171 void
172 ServerApp::ArgvReceived(int32 argc, char **argv)
173 {
174 	for (int arg = 1; arg < argc; arg++) {
175 		if (strstr(argv[arg], "dump") != NULL) {
176 			gAppManager->Dump();
177 			gNodeManager->Dump();
178 			gBufferManager->Dump();
179 			gNotificationManager->Dump();
180 			gMediaFilesManager->Dump();
181 		}
182 		if (strstr(argv[arg], "buffer") != NULL)
183 			gBufferManager->Dump();
184 		if (strstr(argv[arg], "node") != NULL)
185 			gNodeManager->Dump();
186 		if (strstr(argv[arg], "files") != NULL)
187 			gMediaFilesManager->Dump();
188 		if (strstr(argv[arg], "quit") != NULL)
189 			PostMessage(B_QUIT_REQUESTED);
190 	}
191 }
192 
193 
194 void
195 ServerApp::_LaunchAddOnServer()
196 {
197 	// Try to launch media_addon_server by mime signature.
198 	// If it fails (for example on the Live CD, where the executable
199 	// hasn't yet been mimesetted), try from this application's
200 	// directory
201 	status_t err = be_roster->Launch(B_MEDIA_ADDON_SERVER_SIGNATURE);
202 	if (err == B_OK)
203 		return;
204 
205 	app_info info;
206 	BEntry entry;
207 	BDirectory dir;
208 	entry_ref ref;
209 
210 	err = GetAppInfo(&info);
211 	err |= entry.SetTo(&info.ref);
212 	err |= entry.GetParent(&entry);
213 	err |= dir.SetTo(&entry);
214 	err |= entry.SetTo(&dir, "media_addon_server");
215 	err |= entry.GetRef(&ref);
216 
217 	if (err == B_OK)
218 		be_roster->Launch(&ref);
219 	if (err == B_OK)
220 		return;
221 
222 	BAlert* alert = new BAlert("media_server", "Launching media_addon_server "
223 		"failed.\n\nmedia_server will terminate", "OK");
224 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
225 		alert->Go();
226 	fprintf(stderr, "Launching media_addon_server (%s) failed: %s\n",
227 		B_MEDIA_ADDON_SERVER_SIGNATURE, strerror(err));
228 	exit(1);
229 }
230 
231 
232 void
233 ServerApp::_QuitAddOnServer()
234 {
235 	// nothing to do if it's already terminated
236 	if (!be_roster->IsRunning(B_MEDIA_ADDON_SERVER_SIGNATURE))
237 		return;
238 
239 	// send a quit request to the media_addon_server
240 	BMessenger msger(B_MEDIA_ADDON_SERVER_SIGNATURE);
241 	if (!msger.IsValid()) {
242 		ERROR("Trouble terminating media_addon_server. Messenger invalid\n");
243 	} else {
244 		BMessage msg(B_QUIT_REQUESTED);
245 		status_t err = msger.SendMessage(&msg, (BHandler *)NULL, 2000000);
246 			// 2 sec timeout
247 		if (err != B_OK) {
248 			ERROR("Trouble terminating media_addon_server (2): %s\n",
249 				strerror(err));
250 		}
251 	}
252 
253 	// wait 5 seconds for it to terminate
254 	for (int i = 0; i < 50; i++) {
255 		if (!be_roster->IsRunning(B_MEDIA_ADDON_SERVER_SIGNATURE))
256 			return;
257 		snooze(100000); // 100 ms
258 	}
259 
260 	// try to kill it (or many of them), up to 10 seconds
261 	for (int i = 0; i < 50; i++) {
262 		team_id id = be_roster->TeamFor(B_MEDIA_ADDON_SERVER_SIGNATURE);
263 		if (id < 0)
264 			break;
265 		kill_team(id);
266 		snooze(200000); // 200 ms
267 	}
268 
269 	if (be_roster->IsRunning(B_MEDIA_ADDON_SERVER_SIGNATURE)) {
270 		ERROR("Trouble terminating media_addon_server, it's still running\n");
271 	}
272 }
273 
274 
275 void
276 ServerApp::_HandleMessage(int32 code, const void* data, size_t size)
277 {
278 	TRACE("ServerApp::HandleMessage %#" B_PRIx32 " enter\n", code);
279 	switch (code) {
280 		case SERVER_CHANGE_FLAVOR_INSTANCES_COUNT:
281 		{
282 			const server_change_flavor_instances_count_request& request
283 				= *static_cast<
284 					const server_change_flavor_instances_count_request*>(data);
285 			server_change_flavor_instances_count_reply reply;
286 			status_t status = B_BAD_VALUE;
287 
288 			if (request.delta == 1) {
289 				status = gNodeManager->IncrementFlavorInstancesCount(
290 					request.add_on_id, request.flavor_id, request.team);
291 			} else if (request.delta == -1) {
292 				status = gNodeManager->DecrementFlavorInstancesCount(
293 					request.add_on_id, request.flavor_id, request.team);
294 			}
295 			request.SendReply(status, &reply, sizeof(reply));
296 			break;
297 		}
298 
299 		case SERVER_RESCAN_DEFAULTS:
300 		{
301 			gNodeManager->RescanDefaultNodes();
302 			break;
303 		}
304 
305 		case SERVER_REGISTER_APP:
306 		{
307 			const server_register_app_request& request = *static_cast<
308 				const server_register_app_request*>(data);
309 			server_register_app_reply reply;
310 
311 			status_t status = gAppManager->RegisterTeam(request.team,
312 				request.messenger);
313 			request.SendReply(status, &reply, sizeof(reply));
314 			break;
315 		}
316 
317 		case SERVER_UNREGISTER_APP:
318 		{
319 			const server_unregister_app_request& request = *static_cast<
320 				const server_unregister_app_request*>(data);
321 			server_unregister_app_reply reply;
322 
323 			status_t status = gAppManager->UnregisterTeam(request.team);
324 			request.SendReply(status, &reply, sizeof(reply));
325 			break;
326 		}
327 
328 		case SERVER_GET_ADD_ON_REF:
329 		{
330 			const server_get_add_on_ref_request& request = *static_cast<
331 				const server_get_add_on_ref_request*>(data);
332 			server_get_add_on_ref_reply reply;
333 
334 			entry_ref ref;
335 			reply.result = gNodeManager->GetAddOnRef(request.add_on_id, &ref);
336 			reply.ref = ref;
337 
338 			request.SendReply(reply.result, &reply, sizeof(reply));
339 			break;
340 		}
341 
342 		case SERVER_NODE_ID_FOR:
343 		{
344 			const server_node_id_for_request& request
345 				= *static_cast<const server_node_id_for_request*>(data);
346 			server_node_id_for_reply reply;
347 
348 			status_t status = gNodeManager->FindNodeID(request.port,
349 				&reply.node_id);
350 			request.SendReply(status, &reply, sizeof(reply));
351 			break;
352 		}
353 
354 		case SERVER_GET_LIVE_NODE_INFO:
355 		{
356 			const server_get_live_node_info_request& request = *static_cast<
357 				const server_get_live_node_info_request*>(data);
358 			server_get_live_node_info_reply reply;
359 
360 			status_t status = gNodeManager->GetLiveNodeInfo(request.node,
361 				&reply.live_info);
362 			request.SendReply(status, &reply, sizeof(reply));
363 			break;
364 		}
365 
366 		case SERVER_GET_LIVE_NODES:
367 		{
368 			const server_get_live_nodes_request& request
369 				= *static_cast<const server_get_live_nodes_request*>(data);
370 			server_get_live_nodes_reply reply;
371 			LiveNodeList nodes;
372 
373 			status_t status = gNodeManager->GetLiveNodes(nodes,
374 				request.max_count,
375 				request.has_input ? &request.input_format : NULL,
376 				request.has_output ? &request.output_format : NULL,
377 				request.has_name ? request.name : NULL, request.require_kinds);
378 
379 			reply.count = nodes.size();
380 			reply.area = -1;
381 
382 			live_node_info* infos = reply.live_info;
383 			area_id area = -1;
384 
385 			if (reply.count > MAX_LIVE_INFO) {
386 				// We create an area here, and transfer it to the client
387 				size_t size = (reply.count * sizeof(live_node_info)
388 					+ B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
389 
390 				area = create_area("get live nodes", (void**)&infos,
391 					B_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
392 				if (area < 0) {
393 					reply.area = area;
394 					reply.count = 0;
395 				}
396 			}
397 
398 			for (int32 index = 0; index < reply.count; index++)
399 				infos[index] = nodes[index];
400 
401 			if (area >= 0) {
402 				// transfer the area to the target team
403 				reply.area = _kern_transfer_area(area, &reply.address,
404 					B_ANY_ADDRESS, request.team);
405 				if (reply.area < 0) {
406 					delete_area(area);
407 					reply.count = 0;
408 				}
409 			}
410 
411 			status = request.SendReply(status, &reply, sizeof(reply));
412 			if (status != B_OK && reply.area >= 0) {
413 				// if we couldn't send the message, delete the area
414 				delete_area(reply.area);
415 			}
416 			break;
417 		}
418 
419 		case SERVER_GET_NODE_FOR:
420 		{
421 			const server_get_node_for_request& request
422 				= *static_cast<const server_get_node_for_request*>(data);
423 			server_get_node_for_reply reply;
424 
425 			status_t status = gNodeManager->GetCloneForID(request.node_id,
426 				request.team, &reply.clone);
427 			request.SendReply(status, &reply, sizeof(reply));
428 			break;
429 		}
430 
431 		case SERVER_RELEASE_NODE:
432 		{
433 			const server_release_node_request& request
434 				= *static_cast<const server_release_node_request*>(data);
435 			server_release_node_reply reply;
436 
437 			status_t status = gNodeManager->ReleaseNode(request.node,
438 				request.team);
439 			request.SendReply(status, &reply, sizeof(reply));
440 			break;
441 		}
442 
443 		case SERVER_RELEASE_NODE_ALL:
444 		{
445 			const server_release_node_request& request
446 				= *static_cast<const server_release_node_request*>(data);
447 			server_release_node_reply reply;
448 
449 			status_t status = gNodeManager->ReleaseNodeAll(request.node.node);
450 			request.SendReply(status, &reply, sizeof(reply));
451 			break;
452 		}
453 
454 		case SERVER_REGISTER_NODE:
455 		{
456 			const server_register_node_request& request
457 				= *static_cast<const server_register_node_request*>(data);
458 			server_register_node_reply reply;
459 
460 			status_t status = gNodeManager->RegisterNode(request.add_on_id,
461 				request.flavor_id, request.name, request.kinds, request.port,
462 				request.team, &reply.node_id);
463 			request.SendReply(status, &reply, sizeof(reply));
464 			break;
465 		}
466 
467 		case SERVER_UNREGISTER_NODE:
468 		{
469 			const server_unregister_node_request& request
470 				= *static_cast<const server_unregister_node_request*>(data);
471 			server_unregister_node_reply reply;
472 
473 			status_t status = gNodeManager->UnregisterNode(request.node_id,
474 				request.team, &reply.add_on_id, &reply.flavor_id);
475 			request.SendReply(status, &reply, sizeof(reply));
476 			break;
477 		}
478 
479 		case SERVER_PUBLISH_INPUTS:
480 		{
481 			const server_publish_inputs_request& request
482 				= *static_cast<const server_publish_inputs_request*>(data);
483 			server_publish_inputs_reply reply;
484 			status_t status;
485 
486 			if (request.count <= MAX_INPUTS) {
487 				status = gNodeManager->PublishInputs(request.node,
488 					request.inputs, request.count);
489 			} else {
490 				media_input* inputs;
491 				area_id clone;
492 				clone = clone_area("media_inputs clone", (void**)&inputs,
493 					B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, request.area);
494 				if (clone < B_OK) {
495 					ERROR("SERVER_PUBLISH_INPUTS: failed to clone area, "
496 						"error %#" B_PRIx32 "\n", clone);
497 					status = clone;
498 				} else {
499 					status = gNodeManager->PublishInputs(request.node, inputs,
500 						request.count);
501 					delete_area(clone);
502 				}
503 			}
504 			request.SendReply(status, &reply, sizeof(reply));
505 			break;
506 		}
507 
508 		case SERVER_PUBLISH_OUTPUTS:
509 		{
510 			const server_publish_outputs_request& request
511 				= *static_cast<const server_publish_outputs_request*>(data);
512 			server_publish_outputs_reply reply;
513 			status_t status;
514 
515 			if (request.count <= MAX_OUTPUTS) {
516 				status = gNodeManager->PublishOutputs(request.node,
517 					request.outputs, request.count);
518 			} else {
519 				media_output* outputs;
520 				area_id clone;
521 				clone = clone_area("media_outputs clone", (void**)&outputs,
522 					B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, request.area);
523 				if (clone < B_OK) {
524 					ERROR("SERVER_PUBLISH_OUTPUTS: failed to clone area, "
525 						"error %#" B_PRIx32 "\n", clone);
526 					status = clone;
527 				} else {
528 					status = gNodeManager->PublishOutputs(request.node, outputs,
529 						request.count);
530 					delete_area(clone);
531 				}
532 			}
533 			request.SendReply(status, &reply, sizeof(reply));
534 			break;
535 		}
536 
537 		case SERVER_GET_NODE:
538 		{
539 			const server_get_node_request& request
540 				= *static_cast<const server_get_node_request*>(data);
541 			server_get_node_reply reply;
542 
543 			status_t status = gNodeManager->GetClone(request.type, request.team,
544 				&reply.node, reply.input_name, &reply.input_id);
545 			request.SendReply(status, &reply, sizeof(reply));
546 			break;
547 		}
548 
549 		case SERVER_SET_NODE:
550 		{
551 			const server_set_node_request& request
552 				= *static_cast<const server_set_node_request*>(data);
553 			server_set_node_reply reply;
554 
555 			status_t status = gNodeManager->SetDefaultNode(request.type,
556 				request.use_node ? &request.node : NULL,
557 				request.use_dni ? &request.dni : NULL,
558 				request.use_input ?  &request.input : NULL);
559 			request.SendReply(status, &reply, sizeof(reply));
560 			break;
561 		}
562 
563 		case SERVER_GET_DORMANT_NODE_FOR:
564 		{
565 			const server_get_dormant_node_for_request& request
566 				= *static_cast<const server_get_dormant_node_for_request*>(
567 					data);
568 			server_get_dormant_node_for_reply reply;
569 
570 			status_t status = gNodeManager->GetDormantNodeInfo(request.node,
571 				&reply.node_info);
572 			request.SendReply(status, &reply, sizeof(reply));
573 			break;
574 		}
575 
576 		case SERVER_GET_INSTANCES_FOR:
577 		{
578 			const server_get_instances_for_request& request
579 				= *static_cast<const server_get_instances_for_request*>(data);
580 			server_get_instances_for_reply reply;
581 
582 			status_t status = gNodeManager->GetInstances(request.add_on_id,
583 				request.flavor_id, reply.node_id, &reply.count,
584 				min_c(request.max_count, MAX_NODE_ID));
585 			if (reply.count == MAX_NODE_ID
586 				&& request.max_count > MAX_NODE_ID) {
587 				// TODO: might be fixed by using an area
588 				PRINT(1, "Warning: SERVER_GET_INSTANCES_FOR: returning "
589 					"possibly truncated list of node id's\n");
590 			}
591 			request.SendReply(status, &reply, sizeof(reply));
592 			break;
593 		}
594 
595 		case SERVER_REGISTER_ADD_ON:
596 		{
597 			const server_register_add_on_request& request = *static_cast<
598 				const server_register_add_on_request*>(data);
599 			server_register_add_on_reply reply;
600 
601 			gNodeManager->RegisterAddOn(request.ref, &reply.add_on_id);
602 			request.SendReply(B_OK, &reply, sizeof(reply));
603 			break;
604 		}
605 
606 		case SERVER_UNREGISTER_ADD_ON:
607 		{
608 			const server_unregister_add_on_command& request = *static_cast<
609 				const server_unregister_add_on_command*>(data);
610 			gNodeManager->UnregisterAddOn(request.add_on_id);
611 			break;
612 		}
613 
614 		case SERVER_REGISTER_DORMANT_NODE:
615 		{
616 			const server_register_dormant_node_command& command
617 				= *static_cast<const server_register_dormant_node_command*>(
618 					data);
619 			if (command.purge_id > 0)
620 				gNodeManager->InvalidateDormantFlavorInfo(command.purge_id);
621 
622 			dormant_flavor_info dormantFlavorInfo;
623 			status_t status = dormantFlavorInfo.Unflatten(command.type,
624 				command.flattened_data, command.flattened_size);
625 			if (status == B_OK)
626 				gNodeManager->AddDormantFlavorInfo(dormantFlavorInfo);
627 			break;
628 		}
629 
630 		case SERVER_GET_DORMANT_NODES:
631 		{
632 			const server_get_dormant_nodes_request& request
633 				= *static_cast<const server_get_dormant_nodes_request*>(data);
634 
635 			server_get_dormant_nodes_reply reply;
636 			reply.count = request.max_count;
637 
638 			dormant_node_info* infos
639 				= new(std::nothrow) dormant_node_info[reply.count];
640 			if (infos != NULL) {
641 				reply.result = gNodeManager->GetDormantNodes(infos,
642 					&reply.count,
643 					request.has_input ? &request.input_format : NULL,
644 					request.has_output ? &request.output_format : NULL,
645 					request.has_name ? request.name : NULL,
646 					request.require_kinds, request.deny_kinds);
647 			} else
648 				reply.result = B_NO_MEMORY;
649 
650 			if (reply.result != B_OK)
651 				reply.count = 0;
652 
653 			request.SendReply(reply.result, &reply, sizeof(reply));
654 			if (reply.count > 0) {
655 				write_port(request.reply_port, 0, infos,
656 					reply.count * sizeof(dormant_node_info));
657 			}
658 			delete[] infos;
659 			break;
660 		}
661 
662 		case SERVER_GET_DORMANT_FLAVOR_INFO:
663 		{
664 			const server_get_dormant_flavor_info_request& request
665 				= *static_cast<const server_get_dormant_flavor_info_request*>(
666 					data);
667 			dormant_flavor_info dormantFlavorInfo;
668 
669 			status_t status = gNodeManager->GetDormantFlavorInfoFor(
670 				request.add_on_id, request.flavor_id, &dormantFlavorInfo);
671 			if (status != B_OK) {
672 				server_get_dormant_flavor_info_reply reply;
673 				reply.result = status;
674 				request.SendReply(reply.result, &reply, sizeof(reply));
675 			} else {
676 				size_t replySize
677 					= sizeof(server_get_dormant_flavor_info_reply)
678 						+ dormantFlavorInfo.FlattenedSize();
679 				server_get_dormant_flavor_info_reply* reply
680 					= (server_get_dormant_flavor_info_reply*)malloc(
681 						replySize);
682 				if (reply != NULL) {
683 					reply->type = dormantFlavorInfo.TypeCode();
684 					reply->flattened_size = dormantFlavorInfo.FlattenedSize();
685 					reply->result = dormantFlavorInfo.Flatten(
686 						reply->flattened_data, reply->flattened_size);
687 
688 					request.SendReply(reply->result, reply, replySize);
689 					free(reply);
690 				} else {
691 					server_get_dormant_flavor_info_reply reply;
692 					reply.result = B_NO_MEMORY;
693 					request.SendReply(reply.result, &reply, sizeof(reply));
694 				}
695 			}
696 			break;
697 		}
698 
699 		case SERVER_SET_NODE_CREATOR:
700 		{
701 			const server_set_node_creator_request& request
702 				= *static_cast<const server_set_node_creator_request*>(data);
703 			server_set_node_creator_reply reply;
704 			status_t status = gNodeManager->SetNodeCreator(request.node,
705 				request.creator);
706 			request.SendReply(status, &reply, sizeof(reply));
707 			break;
708 		}
709 
710 		case SERVER_GET_SHARED_BUFFER_AREA:
711 		{
712 			const server_get_shared_buffer_area_request& request
713 				= *static_cast<const server_get_shared_buffer_area_request*>(
714 					data);
715 			server_get_shared_buffer_area_reply reply;
716 
717 			reply.area = gBufferManager->SharedBufferListArea();
718 			request.SendReply(reply.area >= 0 ? B_OK : reply.area, &reply,
719 				sizeof(reply));
720 			break;
721 		}
722 
723 		case SERVER_REGISTER_BUFFER:
724 		{
725 			const server_register_buffer_request& request
726 				= *static_cast<const server_register_buffer_request*>(data);
727 			server_register_buffer_reply reply;
728 			status_t status;
729 
730 			if (request.info.buffer == 0) {
731 				reply.info = request.info;
732 				// size, offset, flags, area is kept
733 				// get a new beuffer id into reply.info.buffer
734 				status = gBufferManager->RegisterBuffer(request.team,
735 					request.info.size, request.info.flags,
736 					request.info.offset, request.info.area,
737 					&reply.info.buffer);
738 			} else {
739 				reply.info = request.info; // buffer id is kept
740 				status = gBufferManager->RegisterBuffer(request.team,
741 					request.info.buffer, &reply.info.size, &reply.info.flags,
742 					&reply.info.offset, &reply.info.area);
743 			}
744 			request.SendReply(status, &reply, sizeof(reply));
745 			break;
746 		}
747 
748 		case SERVER_UNREGISTER_BUFFER:
749 		{
750 			const server_unregister_buffer_command& request = *static_cast<
751 				const server_unregister_buffer_command*>(data);
752 
753 			gBufferManager->UnregisterBuffer(request.team, request.buffer_id);
754 			break;
755 		}
756 
757 		case SERVER_GET_MEDIA_FILE_TYPES:
758 		{
759 			const server_get_media_types_request& request
760 				= *static_cast<const server_get_media_types_request*>(data);
761 
762 			server_get_media_types_reply reply;
763 			area_id area = gMediaFilesManager->GetTypesArea(reply.count);
764 			if (area >= 0) {
765 				// transfer the area to the target team
766 				reply.area = _kern_transfer_area(area, &reply.address,
767 					B_ANY_ADDRESS, request.team);
768 				if (reply.area < 0) {
769 					delete_area(area);
770 					reply.area = B_ERROR;
771 					reply.count = 0;
772 				}
773 			}
774 
775 			status_t status = request.SendReply(
776 				reply.area < 0 ? reply.area : B_OK, &reply, sizeof(reply));
777 			if (status != B_OK) {
778 				// if we couldn't send the message, delete the area
779 				delete_area(reply.area);
780 			}
781 			break;
782 		}
783 
784 		case SERVER_GET_MEDIA_FILE_ITEMS:
785 		{
786 			const server_get_media_items_request& request
787 				= *static_cast<const server_get_media_items_request*>(data);
788 
789 			server_get_media_items_reply reply;
790 			area_id area = gMediaFilesManager->GetItemsArea(request.type,
791 				reply.count);
792 			if (area >= 0) {
793 				// transfer the area to the target team
794 				reply.area = _kern_transfer_area(area, &reply.address,
795 					B_ANY_ADDRESS, request.team);
796 				if (reply.area < 0) {
797 					delete_area(area);
798 					reply.area = B_ERROR;
799 					reply.count = 0;
800 				}
801 			} else
802 				reply.area = area;
803 
804 			status_t status = request.SendReply(
805 				reply.area < 0 ? reply.area : B_OK, &reply, sizeof(reply));
806 			if (status != B_OK) {
807 				// if we couldn't send the message, delete the area
808 				delete_area(reply.area);
809 			}
810 			break;
811 		}
812 
813 		case SERVER_GET_REF_FOR:
814 		{
815 			const server_get_ref_for_request& request
816 				= *static_cast<const server_get_ref_for_request*>(data);
817 			server_get_ref_for_reply reply;
818 			entry_ref* ref;
819 
820 			status_t status = gMediaFilesManager->GetRefFor(request.type,
821 				request.item, &ref);
822 			if (status == B_OK)
823 				reply.ref = *ref;
824 
825 			request.SendReply(status, &reply, sizeof(reply));
826 			break;
827 		}
828 
829 		case SERVER_SET_REF_FOR:
830 		{
831 			const server_set_ref_for_request& request
832 				= *static_cast<const server_set_ref_for_request*>(data);
833 			server_set_ref_for_reply reply;
834 			entry_ref ref = request.ref;
835 
836 			status_t status = gMediaFilesManager->SetRefFor(request.type,
837 				request.item, ref);
838 			request.SendReply(status, &reply, sizeof(reply));
839 			break;
840 		}
841 
842 		case SERVER_INVALIDATE_MEDIA_ITEM:
843 		{
844 			const server_invalidate_item_request& request
845 				= *static_cast<const server_invalidate_item_request*>(data);
846 			server_invalidate_item_reply reply;
847 
848 			status_t status = gMediaFilesManager->InvalidateItem(request.type,
849 				request.item);
850 			request.SendReply(status, &reply, sizeof(reply));
851 			break;
852 		}
853 
854 		case SERVER_REMOVE_MEDIA_ITEM:
855 		{
856 			const server_remove_media_item_request& request
857 				= *static_cast<const server_remove_media_item_request*>(data);
858 			server_remove_media_item_reply reply;
859 
860 			status_t status = gMediaFilesManager->RemoveItem(request.type,
861 				request.item);
862 			request.SendReply(status, &reply, sizeof(reply));
863 			break;
864 		}
865 
866 		case SERVER_GET_ITEM_AUDIO_GAIN:
867 		{
868 			const server_get_item_audio_gain_request& request
869 				= *static_cast<const server_get_item_audio_gain_request*>(data);
870 			server_get_item_audio_gain_reply reply;
871 
872 			status_t status = gMediaFilesManager->GetAudioGainFor(request.type,
873 				request.item, &reply.gain);
874 			request.SendReply(status, &reply, sizeof(reply));
875 			break;
876 		}
877 
878 		case SERVER_SET_ITEM_AUDIO_GAIN:
879 		{
880 			const server_set_item_audio_gain_request& request
881 				= *static_cast<const server_set_item_audio_gain_request*>(data);
882 			server_set_ref_for_reply reply;
883 
884 			status_t status = gMediaFilesManager->SetAudioGainFor(request.type,
885 				request.item, request.gain);
886 			request.SendReply(status, &reply, sizeof(reply));
887 			break;
888 		}
889 
890 		case SERVER_GET_READERS:
891 		{
892 			const server_get_readers_request& request
893 				= *static_cast<const server_get_readers_request*>(data);
894 			server_get_readers_reply reply;
895 
896 			status_t status = gAddOnManager->GetReaders(reply.ref, &reply.count,
897 				MAX_READERS);
898 			request.SendReply(status, &reply, sizeof(reply));
899 			break;
900 		}
901 
902 		case SERVER_GET_DECODER_FOR_FORMAT:
903 		{
904 			const server_get_decoder_for_format_request& request
905 				= *static_cast<
906 					const server_get_decoder_for_format_request*>(data);
907 			server_get_decoder_for_format_reply reply;
908 
909 			status_t status = gAddOnManager->GetDecoderForFormat(&reply.ref,
910 				request.format);
911 			request.SendReply(status, &reply, sizeof(reply));
912 			break;
913 		}
914 
915 		case SERVER_GET_WRITER_FOR_FORMAT_FAMILY:
916 		{
917 			const server_get_writer_request& request
918 				= *static_cast<const server_get_writer_request*>(data);
919 			server_get_writer_reply reply;
920 
921 			status_t status = gAddOnManager->GetWriter(&reply.ref,
922 				request.internal_id);
923 			request.SendReply(status, &reply, sizeof(reply));
924 			break;
925 		}
926 
927 		case SERVER_GET_FILE_FORMAT_FOR_COOKIE:
928 		{
929 			const server_get_file_format_request& request
930 				= *static_cast<const server_get_file_format_request*>(data);
931 			server_get_file_format_reply reply;
932 
933 			status_t status = gAddOnManager->GetFileFormat(&reply.file_format,
934 				request.cookie);
935 			request.SendReply(status, &reply, sizeof(reply));
936 			break;
937 		}
938 
939 		case SERVER_GET_CODEC_INFO_FOR_COOKIE:
940 		{
941 			const server_get_codec_info_request& request
942 				= *static_cast<const server_get_codec_info_request*>(data);
943 			server_get_codec_info_reply reply;
944 
945 			status_t status = gAddOnManager->GetCodecInfo(&reply.codec_info,
946 				&reply.format_family, &reply.input_format,
947 				&reply.output_format, request.cookie);
948 			request.SendReply(status, &reply, sizeof(reply));
949 			break;
950 		}
951 
952 		case SERVER_GET_ENCODER_FOR_CODEC_INFO:
953 		{
954 			const server_get_encoder_for_codec_info_request& request
955 				= *static_cast<
956 					const server_get_encoder_for_codec_info_request*>(data);
957 			server_get_encoder_for_codec_info_reply reply;
958 			status_t status = gAddOnManager->GetEncoder(&reply.ref, request.id);
959 			request.SendReply(status, &reply, sizeof(reply));
960 			break;
961 		}
962 
963 		default:
964 			printf("media_server: received unknown message code %#08" B_PRIx32
965 				"\n", code);
966 	}
967 	TRACE("ServerApp::HandleMessage %#" B_PRIx32 " leave\n", code);
968 }
969 
970 
971 status_t
972 ServerApp::_ControlThread(void* _server)
973 {
974 	ServerApp* server = (ServerApp*)_server;
975 
976 	char data[B_MEDIA_MESSAGE_SIZE];
977 	ssize_t size;
978 	int32 code;
979 	while ((size = read_port_etc(server->_ControlPort(), &code, data,
980 			sizeof(data), 0, 0)) > 0) {
981 		server->_HandleMessage(code, data, size);
982 	}
983 
984 	return B_OK;
985 }
986 
987 
988 void
989 ServerApp::MessageReceived(BMessage* msg)
990 {
991 	TRACE("ServerApp::MessageReceived %lx enter\n", msg->what);
992 	switch (msg->what) {
993 		case MEDIA_SERVER_REQUEST_NOTIFICATIONS:
994 		case MEDIA_SERVER_CANCEL_NOTIFICATIONS:
995 		case MEDIA_SERVER_SEND_NOTIFICATIONS:
996 			gNotificationManager->EnqueueMessage(msg);
997 			break;
998 
999 		case MEDIA_FILES_MANAGER_SAVE_TIMER:
1000 			gMediaFilesManager->TimerMessage();
1001 			break;
1002 
1003 		case MEDIA_SERVER_GET_FORMATS:
1004 			gFormatManager->GetFormats(*msg);
1005 			break;
1006 
1007 		case MEDIA_SERVER_MAKE_FORMAT_FOR:
1008 			gFormatManager->MakeFormatFor(*msg);
1009 			break;
1010 
1011 		case MEDIA_SERVER_ADD_SYSTEM_BEEP_EVENT:
1012 			gMediaFilesManager->HandleAddSystemBeepEvent(msg);
1013 			break;
1014 		default:
1015 			BApplication::MessageReceived(msg);
1016 			printf("\nmedia_server: unknown message received:\n");
1017 			msg->PrintToStream();
1018 			break;
1019 	}
1020 	TRACE("ServerApp::MessageReceived %lx leave\n", msg->what);
1021 }
1022 
1023 
1024 //	#pragma mark -
1025 
1026 
1027 int
1028 main()
1029 {
1030 	new ServerApp;
1031 	be_app->Run();
1032 	delete be_app;
1033 	return 0;
1034 }
1035