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