1 /*********************************************************************** 2 * AUTHOR: Marcus Overhagen 3 * FILE: MediaRoster.cpp 4 * DESCR: 5 ***********************************************************************/ 6 #include <MediaRoster.h> 7 #include <Locker.h> 8 #include <Message.h> 9 #include <Messenger.h> 10 #include <StopWatch.h> 11 #include <OS.h> 12 #include <String.h> 13 #include <TimeSource.h> 14 #undef DEBUG 15 #define DEBUG 3 16 #include "debug.h" 17 #include "PortPool.h" 18 #include "ServerInterface.h" 19 #include "DormantNodeManager.h" 20 #include "NotificationManager.h" 21 22 static BMessenger *ServerMessenger = 0; 23 static team_id team; 24 25 // the BMediaRoster destructor is private, 26 // but _DefaultDeleter is a friend class of 27 // the BMediaRoster an thus can delete it 28 class _DefaultDeleter 29 { 30 public: 31 void Delete() { delete BMediaRoster::_sDefault; } 32 }; 33 34 35 namespace MediaKitPrivate 36 { 37 38 class RosterSingleton 39 { 40 public: 41 RosterSingleton() 42 { 43 thread_info info; 44 get_thread_info(find_thread(NULL), &info); 45 team = info.team; 46 ServerMessenger = new BMessenger(NEW_MEDIA_SERVER_SIGNATURE); 47 } 48 ~RosterSingleton() 49 { 50 _DefaultDeleter deleter; 51 deleter.Delete(); // deletes BMediaRoster::_sDefault 52 delete ServerMessenger; 53 } 54 }; 55 56 RosterSingleton singleton; 57 58 59 status_t GetNode(node_type type, media_node * out_node, int32 * out_input_id = NULL, BString * out_input_name = NULL); 60 status_t SetNode(node_type type, const media_node *node, const dormant_node_info *info = NULL, const media_input *input = NULL); 61 62 status_t QueryServer(BMessage *query, BMessage *reply) 63 { 64 status_t status; 65 status = ServerMessenger->SendMessage(query,reply); 66 if (status != B_OK || reply->what != B_OK) { 67 TRACE("QueryServer failed! status = 0x%08lx\n",status); 68 TRACE("Query:\n"); 69 query->PrintToStream(); 70 TRACE("Reply:\n"); 71 reply->PrintToStream(); 72 } 73 return status; 74 } 75 76 status_t GetNode(node_type type, media_node * out_node, int32 * out_input_id, BString * out_input_name) 77 { 78 if (out_node == NULL) 79 return B_BAD_VALUE; 80 81 xfer_server_get_node msg; 82 xfer_server_get_node_reply reply; 83 port_id port; 84 status_t rv; 85 int32 code; 86 87 port = find_port("media_server port"); 88 if (port <= B_OK) 89 return B_ERROR; 90 91 msg.type = type; 92 msg.reply_port = _PortPool->GetPort(); 93 rv = write_port(port, SERVER_GET_NODE, &msg, sizeof(msg)); 94 if (rv != B_OK) { 95 _PortPool->PutPort(msg.reply_port); 96 return rv; 97 } 98 rv = read_port(msg.reply_port, &code, &reply, sizeof(reply)); 99 _PortPool->PutPort(msg.reply_port); 100 if (rv < B_OK) 101 return rv; 102 *out_node = reply.node; 103 if (out_input_id) 104 *out_input_id = reply.input_id; 105 if (out_input_name) 106 *out_input_name = reply.input_name; 107 return reply.result; 108 } 109 110 status_t SetNode(node_type type, const media_node *node, const dormant_node_info *info, const media_input *input) 111 { 112 xfer_server_set_node msg; 113 xfer_server_set_node_reply reply; 114 port_id port; 115 status_t rv; 116 int32 code; 117 118 port = find_port("media_server port"); 119 if (port <= B_OK) 120 return B_ERROR; 121 122 msg.type = type; 123 msg.use_node = node ? true : false; 124 if (node) 125 msg.node = *node; 126 msg.use_dni = info ? true : false; 127 if (info) 128 msg.dni = *info; 129 msg.use_input = input ? true : false; 130 if (input) 131 msg.input = *input; 132 msg.reply_port = _PortPool->GetPort(); 133 rv = write_port(port, SERVER_SET_NODE, &msg, sizeof(msg)); 134 if (rv != B_OK) { 135 _PortPool->PutPort(msg.reply_port); 136 return rv; 137 } 138 rv = read_port(msg.reply_port, &code, &reply, sizeof(reply)); 139 _PortPool->PutPort(msg.reply_port); 140 141 return (rv < B_OK) ? rv : reply.result; 142 } 143 144 }; 145 146 /************************************************************* 147 * public BMediaRoster 148 *************************************************************/ 149 150 status_t 151 BMediaRoster::GetVideoInput(media_node * out_node) 152 { 153 CALLED(); 154 return MediaKitPrivate::GetNode(VIDEO_INPUT, out_node); 155 } 156 157 158 status_t 159 BMediaRoster::GetAudioInput(media_node * out_node) 160 { 161 CALLED(); 162 return MediaKitPrivate::GetNode(AUDIO_INPUT, out_node); 163 } 164 165 166 status_t 167 BMediaRoster::GetVideoOutput(media_node * out_node) 168 { 169 CALLED(); 170 return MediaKitPrivate::GetNode(VIDEO_OUTPUT, out_node); 171 } 172 173 174 status_t 175 BMediaRoster::GetAudioMixer(media_node * out_node) 176 { 177 CALLED(); 178 return MediaKitPrivate::GetNode(AUDIO_MIXER, out_node); 179 } 180 181 182 status_t 183 BMediaRoster::GetAudioOutput(media_node * out_node) 184 { 185 CALLED(); 186 return MediaKitPrivate::GetNode(AUDIO_OUTPUT, out_node); 187 } 188 189 190 status_t 191 BMediaRoster::GetAudioOutput(media_node * out_node, 192 int32 * out_input_id, 193 BString * out_input_name) 194 { 195 CALLED(); 196 return MediaKitPrivate::GetNode(AUDIO_OUTPUT_EX, out_node, out_input_id, out_input_name); 197 } 198 199 200 status_t 201 BMediaRoster::GetTimeSource(media_node * out_node) 202 { 203 CALLED(); 204 return MediaKitPrivate::GetNode(TIME_SOURCE, out_node); 205 } 206 207 208 status_t 209 BMediaRoster::SetVideoInput(const media_node & producer) 210 { 211 CALLED(); 212 return MediaKitPrivate::SetNode(VIDEO_INPUT, &producer); 213 } 214 215 216 status_t 217 BMediaRoster::SetVideoInput(const dormant_node_info & producer) 218 { 219 CALLED(); 220 return MediaKitPrivate::SetNode(VIDEO_INPUT, NULL, &producer); 221 } 222 223 224 status_t 225 BMediaRoster::SetAudioInput(const media_node & producer) 226 { 227 CALLED(); 228 return MediaKitPrivate::SetNode(AUDIO_INPUT, &producer); 229 } 230 231 232 status_t 233 BMediaRoster::SetAudioInput(const dormant_node_info & producer) 234 { 235 CALLED(); 236 return MediaKitPrivate::SetNode(AUDIO_INPUT, NULL, &producer); 237 } 238 239 240 status_t 241 BMediaRoster::SetVideoOutput(const media_node & consumer) 242 { 243 CALLED(); 244 return MediaKitPrivate::SetNode(VIDEO_OUTPUT, &consumer); 245 } 246 247 248 status_t 249 BMediaRoster::SetVideoOutput(const dormant_node_info & consumer) 250 { 251 CALLED(); 252 return MediaKitPrivate::SetNode(VIDEO_OUTPUT, NULL, &consumer); 253 } 254 255 256 status_t 257 BMediaRoster::SetAudioOutput(const media_node & consumer) 258 { 259 CALLED(); 260 return MediaKitPrivate::SetNode(AUDIO_OUTPUT, &consumer); 261 } 262 263 264 status_t 265 BMediaRoster::SetAudioOutput(const media_input & input_to_output) 266 { 267 CALLED(); 268 return MediaKitPrivate::SetNode(AUDIO_OUTPUT, NULL, NULL, &input_to_output); 269 } 270 271 272 status_t 273 BMediaRoster::SetAudioOutput(const dormant_node_info & consumer) 274 { 275 CALLED(); 276 return MediaKitPrivate::SetNode(AUDIO_OUTPUT, NULL, &consumer); 277 } 278 279 280 status_t 281 BMediaRoster::GetNodeFor(media_node_id node, 282 media_node * clone) 283 { 284 UNIMPLEMENTED(); 285 return B_ERROR; 286 } 287 288 289 status_t 290 BMediaRoster::GetSystemTimeSource(media_node * clone) 291 { 292 CALLED(); 293 return MediaKitPrivate::GetNode(SYSTEM_TIME_SOURCE, clone); 294 } 295 296 297 status_t 298 BMediaRoster::ReleaseNode(const media_node & node) 299 { 300 UNIMPLEMENTED(); 301 return B_ERROR; 302 } 303 304 305 306 BTimeSource * 307 BMediaRoster::MakeTimeSourceFor(const media_node & for_node) 308 { 309 UNIMPLEMENTED(); 310 return 0; 311 } 312 313 314 status_t 315 BMediaRoster::Connect(const media_source & from, 316 const media_destination & to, 317 media_format * io_format, 318 media_output * out_output, 319 media_input * out_input) 320 { 321 return BMediaRoster::Connect(from, to, io_format, out_output, out_input, 0); 322 } 323 324 325 status_t 326 BMediaRoster::Connect(const media_source & from, 327 const media_destination & to, 328 media_format * io_format, 329 media_output * out_output, 330 media_input * out_input, 331 uint32 in_flags, 332 void * _reserved) 333 { 334 CALLED(); 335 if (io_format == NULL || out_output == NULL || out_input == NULL) 336 return B_BAD_VALUE; 337 if (from == media_source::null) 338 return B_MEDIA_BAD_SOURCE; 339 if (to == media_destination::null) 340 return B_MEDIA_BAD_DESTINATION; 341 342 xfer_producer_format_proposal msg1; 343 xfer_producer_format_proposal_reply reply1; 344 xfer_consumer_accept_format msg2; 345 xfer_consumer_accept_format_reply reply2; 346 xfer_producer_prepare_to_connect msg3; 347 xfer_producer_prepare_to_connect_reply reply3; 348 xfer_consumer_connected msg4; 349 xfer_consumer_connected_reply reply4; 350 xfer_producer_connect msg5; 351 xfer_producer_connect_reply reply5; 352 status_t rv; 353 port_id port; 354 int32 code; 355 356 port = _PortPool->GetPort(); 357 358 // BBufferProducer::FormatProposal 359 msg1.output = from; 360 msg1.format = *io_format; 361 msg1.reply_port = port; 362 rv = write_port(from.port, PRODUCER_FORMAT_PROPOSAL, &msg1, sizeof(msg1)); 363 if (rv != B_OK) 364 goto failed; 365 rv = read_port(port, &code, &reply1, sizeof(reply1)); 366 if (rv < B_OK) 367 goto failed; 368 if (reply1.result != B_OK) { 369 rv = reply1.result; 370 goto failed; 371 } 372 373 // BBufferConsumer::AcceptFormat 374 msg2.dest = to; 375 msg2.format = *io_format; 376 msg2.reply_port = port; 377 rv = write_port(to.port, CONSUMER_ACCEPT_FORMAT, &msg2, sizeof(msg2)); 378 if (rv != B_OK) 379 goto failed; 380 rv = read_port(port, &code, &reply2, sizeof(reply2)); 381 if (rv < B_OK) 382 goto failed; 383 if (reply2.result != B_OK) { 384 rv = reply2.result; 385 goto failed; 386 } 387 *io_format = reply2.format; 388 389 // BBufferProducer::PrepareToConnect 390 msg3.source = from; 391 msg3.destination = to; 392 msg3.format = *io_format; 393 msg3.reply_port = port; 394 rv = write_port(from.port, PRODUCER_PREPARE_TO_CONNECT, &msg3, sizeof(msg3)); 395 if (rv != B_OK) 396 goto failed; 397 rv = read_port(port, &code, &reply3, sizeof(reply3)); 398 if (rv < B_OK) 399 goto failed; 400 if (reply3.result != B_OK) { 401 rv = reply3.result; 402 goto failed; 403 } 404 *io_format = reply3.format; 405 //reply3.out_source; 406 //reply3.name; 407 408 // BBufferConsumer::Connected 409 msg4.producer = reply3.out_source; 410 msg4.where = to; 411 msg4.with_format = *io_format; 412 msg4.reply_port = port; 413 rv = write_port(to.port, CONSUMER_CONNECTED, &msg4, sizeof(msg4)); 414 if (rv != B_OK) 415 goto failed; 416 rv = read_port(port, &code, &reply4, sizeof(reply4)); 417 if (rv < B_OK) 418 goto failed; 419 if (reply4.result != B_OK) { 420 rv = reply4.result; 421 goto failed; 422 } 423 // reply4.input; 424 425 // BBufferProducer::Connect 426 msg5.error = B_OK; 427 msg5.source = reply3.out_source; 428 msg5.destination = to; 429 msg5.format = *io_format; 430 msg5.name[0] = 0; 431 msg5.reply_port = port; 432 433 rv = write_port(from.port, PRODUCER_CONNECT, &msg5, sizeof(msg5)); 434 if (rv != B_OK) 435 goto failed; 436 rv = read_port(port, &code, &reply5, sizeof(reply5)); 437 if (rv < B_OK) 438 goto failed; 439 440 // out_output->node = 441 out_output->source = reply3.out_source; 442 out_output->destination = to;//reply4.input; 443 out_output->format = *io_format; 444 strcpy(out_output->name,reply5.name); 445 446 // out_input->node 447 out_input->source = reply3.out_source; 448 out_input->destination = to;//reply4.input; 449 out_input->format = *io_format; 450 strcpy(out_input->name,reply3.name); 451 452 _PortPool->PutPort(port); 453 return B_OK; 454 455 failed: 456 _PortPool->PutPort(port); 457 return rv; 458 } 459 460 461 status_t 462 BMediaRoster::Disconnect(media_node_id source_node, 463 const media_source & source, 464 media_node_id destination_node, 465 const media_destination & destination) 466 { 467 UNIMPLEMENTED(); 468 return B_ERROR; 469 } 470 471 472 status_t 473 BMediaRoster::StartNode(const media_node & node, 474 bigtime_t at_performance_time) 475 { 476 CALLED(); 477 if (node.node == 0) 478 return B_MEDIA_BAD_NODE; 479 480 xfer_node_start msg; 481 msg.performance_time = at_performance_time; 482 483 return write_port(node.port, NODE_START, &msg, sizeof(msg)); 484 } 485 486 487 status_t 488 BMediaRoster::StopNode(const media_node & node, 489 bigtime_t at_performance_time, 490 bool immediate) 491 { 492 CALLED(); 493 if (node.node == 0) 494 return B_MEDIA_BAD_NODE; 495 496 xfer_node_stop msg; 497 msg.performance_time = at_performance_time; 498 msg.immediate = immediate; 499 500 return write_port(node.port, NODE_STOP, &msg, sizeof(msg)); 501 } 502 503 504 status_t 505 BMediaRoster::SeekNode(const media_node & node, 506 bigtime_t to_media_time, 507 bigtime_t at_performance_time) 508 { 509 CALLED(); 510 if (node.node == 0) 511 return B_MEDIA_BAD_NODE; 512 513 xfer_node_seek msg; 514 msg.media_time = to_media_time; 515 msg.performance_time = at_performance_time; 516 517 return write_port(node.port, NODE_SEEK, &msg, sizeof(msg)); 518 } 519 520 521 status_t 522 BMediaRoster::StartTimeSource(const media_node & node, 523 bigtime_t at_real_time) 524 { 525 CALLED(); 526 if (node.node == 0) 527 return B_MEDIA_BAD_NODE; 528 if ((node.kind & B_TIME_SOURCE) == 0) 529 return B_MEDIA_BAD_NODE; 530 531 BTimeSource::time_source_op_info msg; 532 msg.op = BTimeSource::B_TIMESOURCE_START; 533 msg.real_time = at_real_time; 534 535 return write_port(node.port, TIMESOURCE_OP, &msg, sizeof(msg)); 536 } 537 538 539 status_t 540 BMediaRoster::StopTimeSource(const media_node & node, 541 bigtime_t at_real_time, 542 bool immediate) 543 { 544 CALLED(); 545 if (node.node == 0) 546 return B_MEDIA_BAD_NODE; 547 if ((node.kind & B_TIME_SOURCE) == 0) 548 return B_MEDIA_BAD_NODE; 549 550 BTimeSource::time_source_op_info msg; 551 msg.op = immediate ? BTimeSource::B_TIMESOURCE_STOP_IMMEDIATELY : BTimeSource::B_TIMESOURCE_STOP; 552 msg.real_time = at_real_time; 553 554 return write_port(node.port, TIMESOURCE_OP, &msg, sizeof(msg)); 555 } 556 557 558 status_t 559 BMediaRoster::SeekTimeSource(const media_node & node, 560 bigtime_t to_performance_time, 561 bigtime_t at_real_time) 562 { 563 CALLED(); 564 if (node.node == 0) 565 return B_MEDIA_BAD_NODE; 566 if ((node.kind & B_TIME_SOURCE) == 0) 567 return B_MEDIA_BAD_NODE; 568 569 BTimeSource::time_source_op_info msg; 570 msg.op = BTimeSource::B_TIMESOURCE_SEEK; 571 msg.real_time = at_real_time; 572 msg.performance_time = to_performance_time; 573 574 return write_port(node.port, TIMESOURCE_OP, &msg, sizeof(msg)); 575 } 576 577 578 status_t 579 BMediaRoster::SyncToNode(const media_node & node, 580 bigtime_t at_time, 581 bigtime_t timeout) 582 { 583 UNIMPLEMENTED(); 584 return B_ERROR; 585 } 586 587 588 status_t 589 BMediaRoster::SetRunModeNode(const media_node & node, 590 BMediaNode::run_mode mode) 591 { 592 CALLED(); 593 if (node.node == 0) 594 return B_MEDIA_BAD_NODE; 595 596 xfer_node_set_run_mode msg; 597 msg.mode = mode; 598 599 return write_port(node.port, NODE_SET_RUN_MODE, &msg, sizeof(msg)); 600 } 601 602 603 status_t 604 BMediaRoster::PrerollNode(const media_node & node) 605 { 606 CALLED(); 607 if (node.node == 0) 608 return B_MEDIA_BAD_NODE; 609 610 char dummy; 611 return write_port(node.port, NODE_PREROLL, &dummy, sizeof(dummy)); 612 } 613 614 615 status_t 616 BMediaRoster::RollNode(const media_node & node, 617 bigtime_t startPerformance, 618 bigtime_t stopPerformance, 619 bigtime_t atMediaTime) 620 { 621 UNIMPLEMENTED(); 622 return B_ERROR; 623 } 624 625 626 status_t 627 BMediaRoster::SetProducerRunModeDelay(const media_node & node, 628 bigtime_t delay, 629 BMediaNode::run_mode mode) 630 { 631 UNIMPLEMENTED(); 632 return B_ERROR; 633 } 634 635 636 status_t 637 BMediaRoster::SetProducerRate(const media_node & producer, 638 int32 numer, 639 int32 denom) 640 { 641 CALLED(); 642 if (producer.node == 0) 643 return B_MEDIA_BAD_NODE; 644 if ((producer.kind & B_BUFFER_PRODUCER) == 0) 645 return B_MEDIA_BAD_NODE; 646 647 xfer_producer_set_play_rate msg; 648 xfer_producer_set_play_rate_reply reply; 649 status_t rv; 650 int32 code; 651 652 msg.numer = numer; 653 msg.denom = denom; 654 msg.reply_port = _PortPool->GetPort(); 655 rv = write_port(producer.node, PRODUCER_SET_PLAY_RATE, &msg, sizeof(msg)); 656 if (rv != B_OK) { 657 _PortPool->PutPort(msg.reply_port); 658 return rv; 659 } 660 rv = read_port(msg.reply_port, &code, &reply, sizeof(reply)); 661 _PortPool->PutPort(msg.reply_port); 662 return (rv < B_OK) ? rv : reply.result; 663 } 664 665 666 667 /* Nodes will have available inputs/outputs as long as they are capable */ 668 /* of accepting more connections. The node may create an additional */ 669 /* output or input as the currently available is taken into usage. */ 670 status_t 671 BMediaRoster::GetLiveNodeInfo(const media_node & node, 672 live_node_info * out_live_info) 673 { 674 UNIMPLEMENTED(); 675 return B_ERROR; 676 } 677 678 679 status_t 680 BMediaRoster::GetLiveNodes(live_node_info * out_live_nodes, 681 int32 * io_total_count, 682 const media_format * has_input, 683 const media_format * has_output, 684 const char * name, 685 uint64 node_kinds) 686 { 687 UNIMPLEMENTED(); 688 return B_ERROR; 689 } 690 691 692 status_t 693 BMediaRoster::GetFreeInputsFor(const media_node & node, 694 media_input * out_free_inputs, 695 int32 buf_num_inputs, 696 int32 * out_total_count, 697 media_type filter_type) 698 { 699 UNIMPLEMENTED(); 700 return B_ERROR; 701 } 702 703 704 status_t 705 BMediaRoster::GetConnectedInputsFor(const media_node & node, 706 media_input * out_active_inputs, 707 int32 buf_num_inputs, 708 int32 * out_total_count) 709 { 710 UNIMPLEMENTED(); 711 return B_ERROR; 712 } 713 714 715 status_t 716 BMediaRoster::GetAllInputsFor(const media_node & node, 717 media_input * out_inputs, 718 int32 buf_num_inputs, 719 int32 * out_total_count) 720 { 721 CALLED(); 722 if (node.node == 0 || (node.kind & B_BUFFER_CONSUMER) == 0) 723 return B_MEDIA_BAD_NODE; 724 if (out_inputs == NULL || out_total_count == NULL) 725 return B_BAD_VALUE; 726 727 status_t rv; 728 status_t rv2; 729 port_id port; 730 int32 code; 731 int32 cookie; 732 733 port = _PortPool->GetPort(); 734 *out_total_count = 0; 735 cookie = 0; 736 rv = B_OK; 737 for (int32 i = 0; i < buf_num_inputs; i++) { 738 xfer_consumer_get_next_input msg; 739 xfer_consumer_get_next_input_reply reply; 740 msg.cookie = cookie; 741 msg.reply_port = port; 742 rv = write_port(node.port, CONSUMER_GET_NEXT_INPUT, &msg, sizeof(msg)); 743 if (rv != B_OK) 744 break; 745 rv = read_port(msg.reply_port, &code, &reply, sizeof(reply)); 746 if (rv < B_OK || reply.result != B_OK) 747 break; 748 *out_total_count += 1; 749 out_inputs[i] = reply.input; 750 cookie = reply.cookie; 751 } 752 _PortPool->PutPort(port); 753 754 xfer_consumer_dispose_input_cookie msg2; 755 msg2.cookie = cookie; 756 rv2 = write_port(node.port, CONSUMER_DISPOSE_INPUT_COOKIE, &msg2, sizeof(msg2)); 757 758 return (rv < B_OK) ? rv : rv2; 759 } 760 761 762 status_t 763 BMediaRoster::GetFreeOutputsFor(const media_node & node, 764 media_output * out_free_outputs, 765 int32 buf_num_outputs, 766 int32 * out_total_count, 767 media_type filter_type) 768 { 769 UNIMPLEMENTED(); 770 return B_ERROR; 771 } 772 773 774 status_t 775 BMediaRoster::GetConnectedOutputsFor(const media_node & node, 776 media_output * out_active_outputs, 777 int32 buf_num_outputs, 778 int32 * out_total_count) 779 { 780 UNIMPLEMENTED(); 781 return B_ERROR; 782 } 783 784 785 status_t 786 BMediaRoster::GetAllOutputsFor(const media_node & node, 787 media_output * out_outputs, 788 int32 buf_num_outputs, 789 int32 * out_total_count) 790 { 791 CALLED(); 792 if (node.node == 0 || (node.kind & B_BUFFER_PRODUCER) == 0) 793 return B_MEDIA_BAD_NODE; 794 if (out_outputs == NULL || out_total_count == NULL) 795 return B_BAD_VALUE; 796 797 status_t rv; 798 status_t rv2; 799 port_id port; 800 int32 code; 801 int32 cookie; 802 803 port = _PortPool->GetPort(); 804 *out_total_count = 0; 805 cookie = 0; 806 rv = B_OK; 807 for (int32 i = 0; i < buf_num_outputs; i++) { 808 xfer_producer_get_next_output msg; 809 xfer_producer_get_next_output_reply reply; 810 msg.cookie = cookie; 811 msg.reply_port = port; 812 rv = write_port(node.port, PRODUCER_GET_NEXT_OUTPUT, &msg, sizeof(msg)); 813 if (rv != B_OK) 814 break; 815 rv = read_port(msg.reply_port, &code, &reply, sizeof(reply)); 816 if (rv < B_OK || reply.result != B_OK) 817 break; 818 *out_total_count += 1; 819 out_outputs[i] = reply.output; 820 cookie = reply.cookie; 821 } 822 _PortPool->PutPort(port); 823 824 xfer_producer_dispose_output_cookie msg2; 825 msg2.cookie = cookie; 826 rv2 = write_port(node.port, PRODUCER_DISPOSE_OUTPUT_COOKIE, &msg2, sizeof(msg2)); 827 828 return (rv < B_OK) ? rv : rv2; 829 } 830 831 832 status_t 833 BMediaRoster::StartWatching(const BMessenger & where) 834 { 835 CALLED(); 836 if (!where.IsValid()) { 837 TRACE("BMediaRoster::StartWatching: messenger invalid!\n"); 838 return B_BAD_VALUE; 839 } 840 return _NotificationManager->Register(where, media_node::null, B_MEDIA_WILDCARD); 841 } 842 843 844 status_t 845 BMediaRoster::StartWatching(const BMessenger & where, 846 int32 notificationType) 847 { 848 CALLED(); 849 if (!where.IsValid()) { 850 TRACE("BMediaRoster::StartWatching: messenger invalid!\n"); 851 return B_BAD_VALUE; 852 } 853 if (!BPrivate::media::NotificationManager::IsValidNotificationType(notificationType)) { 854 TRACE("BMediaRoster::StartWatching: notificationType invalid!\n"); 855 return B_BAD_VALUE; 856 } 857 return _NotificationManager->Register(where, media_node::null, notificationType); 858 } 859 860 861 status_t 862 BMediaRoster::StartWatching(const BMessenger & where, 863 const media_node & node, 864 int32 notificationType) 865 { 866 CALLED(); 867 if (!where.IsValid()) { 868 TRACE("BMediaRoster::StartWatching: messenger invalid!\n"); 869 return B_BAD_VALUE; 870 } 871 if (node.node == 0) { 872 TRACE("BMediaRoster::StartWatching: node invalid!\n"); 873 return B_MEDIA_BAD_NODE; 874 } 875 if (!BPrivate::media::NotificationManager::IsValidNotificationType(notificationType)) { 876 TRACE("BMediaRoster::StartWatching: notificationType invalid!\n"); 877 return B_BAD_VALUE; 878 } 879 return _NotificationManager->Register(where, node, notificationType); 880 } 881 882 883 status_t 884 BMediaRoster::StopWatching(const BMessenger & where) 885 { 886 CALLED(); 887 // messenger may already be invalid, so we don't check this 888 return _NotificationManager->Unregister(where, media_node::null, B_MEDIA_WILDCARD); 889 } 890 891 892 status_t 893 BMediaRoster::StopWatching(const BMessenger & where, 894 int32 notificationType) 895 { 896 CALLED(); 897 // messenger may already be invalid, so we don't check this 898 if (!BPrivate::media::NotificationManager::IsValidNotificationType(notificationType)) { 899 TRACE("BMediaRoster::StopWatching: notificationType invalid!\n"); 900 return B_BAD_VALUE; 901 } 902 return _NotificationManager->Unregister(where, media_node::null, notificationType); 903 } 904 905 906 status_t 907 BMediaRoster::StopWatching(const BMessenger & where, 908 const media_node & node, 909 int32 notificationType) 910 { 911 CALLED(); 912 // messenger may already be invalid, so we don't check this 913 if (node.node == 0) { 914 TRACE("BMediaRoster::StopWatching: node invalid!\n"); 915 return B_MEDIA_BAD_NODE; 916 } 917 if (!BPrivate::media::NotificationManager::IsValidNotificationType(notificationType)) { 918 TRACE("BMediaRoster::StopWatching: notificationType invalid!\n"); 919 return B_BAD_VALUE; 920 } 921 return _NotificationManager->Unregister(where, node, notificationType); 922 } 923 924 925 status_t 926 BMediaRoster::RegisterNode(BMediaNode * node) 927 { 928 CALLED(); 929 if (node == NULL) 930 return B_BAD_VALUE; 931 932 xfer_node_registered msg; 933 msg.node_id = 1; 934 935 return node->HandleMessage(NODE_REGISTERED,&msg,sizeof(msg)); 936 } 937 938 939 status_t 940 BMediaRoster::UnregisterNode(BMediaNode * node) 941 { 942 UNIMPLEMENTED(); 943 return B_ERROR; 944 } 945 946 947 // thread safe for multiple calls to Roster() 948 /* static */ BMediaRoster * 949 BMediaRoster::Roster(status_t* out_error) 950 { 951 CALLED(); 952 static BLocker locker("BMediaRoster::Roster locker"); 953 locker.Lock(); 954 if (_sDefault == NULL) { 955 _sDefault = new BMediaRoster(); 956 if (out_error != NULL) 957 *out_error = B_OK; 958 } else { 959 if (out_error != NULL) 960 *out_error = B_OK; 961 } 962 locker.Unlock(); 963 return _sDefault; 964 } 965 966 967 // won't create it if there isn't one 968 // not thread safe if you call Roster() at the same time 969 /* static */ BMediaRoster * 970 BMediaRoster::CurrentRoster() 971 { 972 CALLED(); 973 return _sDefault; 974 } 975 976 977 status_t 978 BMediaRoster::SetTimeSourceFor(media_node_id node, 979 media_node_id time_source) 980 { 981 UNIMPLEMENTED(); 982 return B_ERROR; 983 } 984 985 986 status_t 987 BMediaRoster::GetParameterWebFor(const media_node & node, 988 BParameterWeb ** out_web) 989 { 990 UNIMPLEMENTED(); 991 return B_ERROR; 992 } 993 994 995 status_t 996 BMediaRoster::StartControlPanel(const media_node & node, 997 BMessenger * out_messenger) 998 { 999 UNIMPLEMENTED(); 1000 return B_ERROR; 1001 } 1002 1003 1004 status_t 1005 BMediaRoster::GetDormantNodes(dormant_node_info * out_info, 1006 int32 * io_count, 1007 const media_format * has_input /* = NULL */, 1008 const media_format * has_output /* = NULL */, 1009 const char * name /* = NULL */, 1010 uint64 require_kinds /* = NULL */, 1011 uint64 deny_kinds /* = NULL */) 1012 { 1013 CALLED(); 1014 if (out_info == NULL) 1015 return B_BAD_VALUE; 1016 if (io_count == NULL) 1017 return B_BAD_VALUE; 1018 if (*io_count <= 0) 1019 return B_BAD_VALUE; 1020 1021 xfer_server_get_dormant_nodes msg; 1022 port_id port; 1023 status_t rv; 1024 1025 port = find_port("media_server port"); 1026 if (port <= B_OK) 1027 return B_ERROR; 1028 1029 msg.maxcount = *io_count; 1030 msg.has_input = (bool) has_input; 1031 if (has_input) 1032 msg.inputformat = *has_input; // XXX we should not make a flat copy of media_format 1033 msg.has_output = (bool) has_output; 1034 if (has_output) 1035 msg.outputformat = *has_output;; // XXX we should not make a flat copy of media_format 1036 msg.has_name = (bool) name; 1037 if (name) { 1038 int len = min_c(strlen(name),sizeof(msg.name) - 1); 1039 memcpy(msg.name,name,len); 1040 msg.name[len] = 0; 1041 } 1042 msg.require_kinds = require_kinds; 1043 msg.deny_kinds = deny_kinds; 1044 msg.reply_port = _PortPool->GetPort(); 1045 1046 rv = write_port(port, SERVER_GET_DORMANT_NODES, &msg, sizeof(msg)); 1047 if (rv != B_OK) { 1048 _PortPool->PutPort(msg.reply_port); 1049 return rv; 1050 } 1051 1052 xfer_server_get_dormant_nodes_reply reply; 1053 int32 code; 1054 1055 rv = read_port(msg.reply_port, &code, &reply, sizeof(reply)); 1056 if (rv < B_OK) { 1057 _PortPool->PutPort(msg.reply_port); 1058 return rv; 1059 } 1060 1061 *io_count = reply.count; 1062 1063 if (*io_count > 0) { 1064 rv = read_port(msg.reply_port, &code, out_info, *io_count * sizeof(dormant_node_info)); 1065 if (rv < B_OK) 1066 reply.result = rv; 1067 } 1068 _PortPool->PutPort(msg.reply_port); 1069 1070 return reply.result; 1071 } 1072 1073 1074 status_t 1075 BMediaRoster::InstantiateDormantNode(const dormant_node_info & in_info, 1076 media_node * out_node, 1077 uint32 flags /* currently B_FLAVOR_IS_GLOBAL or B_FLAVOR_IS_LOCAL */ ) 1078 { 1079 CALLED(); 1080 if ((flags & (B_FLAVOR_IS_GLOBAL | B_FLAVOR_IS_LOCAL)) == 0) { 1081 printf("Error: BMediaRoster::InstantiateDormantNode called without flags\n"); 1082 return B_BAD_VALUE; 1083 } 1084 if (out_node == 0) 1085 return B_BAD_VALUE; 1086 1087 // XXX we should not trust the values passed in by the user, 1088 // XXX and ask the server to determine where to insta 1089 1090 1091 // XXX SOMETHING IS VERY WRONG HERE 1092 // if ((in_info.flavor_flags & B_FLAVOR_IS_GLOBAL) == 0 && (flags & B_FLAVOR_IS_LOCAL)) { 1093 if (flags & B_FLAVOR_IS_LOCAL) { 1094 return InstantiateDormantNode(in_info,out_node); 1095 } 1096 1097 // XXX SOMETHING IS VERY WRONG HERE 1098 // if ((in_info.flavor_flags & B_FLAVOR_IS_GLOBAL) || (flags & B_FLAVOR_IS_GLOBAL)) { 1099 if (flags & B_FLAVOR_IS_GLOBAL) { 1100 // forward this request into the media_addon_server, 1101 // which in turn will call InstantiateDormantNode() 1102 // to create it there localy 1103 xfer_addonserver_instantiate_dormant_node msg; 1104 xfer_addonserver_instantiate_dormant_node_reply reply; 1105 port_id port; 1106 status_t rv; 1107 int32 code; 1108 port = find_port("media_addon_server port"); 1109 if (port <= B_OK) 1110 return B_ERROR; 1111 msg.info = in_info; 1112 msg.reply_port = _PortPool->GetPort(); 1113 rv = write_port(port, ADDONSERVER_INSTANTIATE_DORMANT_NODE, &msg, sizeof(msg)); 1114 if (rv != B_OK) { 1115 _PortPool->PutPort(msg.reply_port); 1116 return rv; 1117 } 1118 rv = read_port(msg.reply_port, &code, &reply, sizeof(reply)); 1119 _PortPool->PutPort(msg.reply_port); 1120 if (rv < B_OK) 1121 return rv; 1122 *out_node = reply.node; 1123 return reply.result; 1124 } 1125 1126 // XXX SOMETHING IS VERY WRONG HERE 1127 // printf("Error: BMediaRoster::InstantiateDormantNode in_info.flavor_flags = %#08lx, flags = %#08lx\n", in_info.flavor_flags, flags); 1128 1129 return B_ERROR; 1130 } 1131 1132 1133 status_t 1134 BMediaRoster::InstantiateDormantNode(const dormant_node_info & in_info, 1135 media_node * out_node) 1136 { 1137 CALLED(); 1138 1139 // to instantiate a dormant node in the current address space, we need to 1140 // either load the add-on from file and create a new BMediaAddOn class, or 1141 // reuse the cached BMediaAddOn from a previous call 1142 // call BMediaAddOn::InstantiateNodeFor() 1143 // and cache the BMediaAddOn after that for later reuse. 1144 // BeOS R5 does not seem to delete it when the application quits 1145 // if B_FLAVOR_IS_GLOBAL, we need to use the BMediaAddOn object that 1146 // resides in the media_addon_server 1147 1148 // RegisterNode() is called automatically for nodes instantiated from add-ons 1149 1150 //XXX TEST! 1151 BMediaAddOn *addon; 1152 BMediaNode *node; 1153 BMessage config; 1154 status_t out_error; 1155 addon = _DormantNodeManager->GetAddon(in_info.addon); 1156 if (!addon) { 1157 printf("BMediaRoster::InstantiateDormantNode: GetAddon failed\n"); 1158 return B_ERROR; 1159 } 1160 flavor_info temp; 1161 temp.internal_id = in_info.flavor_id; 1162 node = addon->InstantiateNodeFor(&temp, &config, &out_error); 1163 if (!node) { 1164 printf("BMediaRoster::InstantiateDormantNode: InstantiateNodeFor failed\n"); 1165 _DormantNodeManager->PutAddon(in_info.addon); 1166 return B_ERROR; 1167 } 1168 *out_node = node->Node(); 1169 return B_OK; 1170 } 1171 1172 1173 status_t 1174 BMediaRoster::GetDormantNodeFor(const media_node & node, 1175 dormant_node_info * out_info) 1176 { 1177 UNIMPLEMENTED(); 1178 1179 return B_ERROR; 1180 } 1181 1182 1183 status_t 1184 BMediaRoster::GetDormantFlavorInfoFor(const dormant_node_info & in_dormant, 1185 dormant_flavor_info * out_flavor) 1186 { 1187 CALLED(); 1188 1189 xfer_server_get_dormant_flavor_info msg; 1190 xfer_server_get_dormant_flavor_info_reply *reply; 1191 port_id port; 1192 status_t rv; 1193 int32 code; 1194 1195 port = find_port("media_server port"); 1196 if (port <= B_OK) 1197 return B_ERROR; 1198 1199 reply = (xfer_server_get_dormant_flavor_info_reply *) malloc(16000); 1200 if (reply == 0) 1201 return B_ERROR; 1202 1203 msg.addon = in_dormant.addon; 1204 msg.flavor_id = in_dormant.flavor_id; 1205 msg.reply_port = _PortPool->GetPort(); 1206 rv = write_port(port, SERVER_GET_DORMANT_FLAVOR_INFO, &msg, sizeof(msg)); 1207 if (rv != B_OK) { 1208 free(reply); 1209 _PortPool->PutPort(msg.reply_port); 1210 return rv; 1211 } 1212 rv = read_port(msg.reply_port, &code, reply, 16000); 1213 _PortPool->PutPort(msg.reply_port); 1214 1215 if (rv < B_OK) { 1216 free(reply); 1217 return rv; 1218 } 1219 1220 if (reply->result == B_OK) 1221 rv = out_flavor->Unflatten(reply->dfi_type, &reply->dfi, reply->dfi_size); 1222 else 1223 rv = reply->result; 1224 1225 free(reply); 1226 return rv; 1227 } 1228 1229 1230 status_t 1231 BMediaRoster::GetLatencyFor(const media_node & producer, 1232 bigtime_t * out_latency) 1233 { 1234 UNIMPLEMENTED(); 1235 *out_latency = 0; 1236 return B_ERROR; 1237 } 1238 1239 1240 status_t 1241 BMediaRoster::GetInitialLatencyFor(const media_node & producer, 1242 bigtime_t * out_latency, 1243 uint32 * out_flags) 1244 { 1245 UNIMPLEMENTED(); 1246 *out_latency = 0; 1247 *out_flags = 0; 1248 return B_ERROR; 1249 } 1250 1251 1252 status_t 1253 BMediaRoster::GetStartLatencyFor(const media_node & time_source, 1254 bigtime_t * out_latency) 1255 { 1256 UNIMPLEMENTED(); 1257 *out_latency = 0; 1258 return B_ERROR; 1259 } 1260 1261 1262 status_t 1263 BMediaRoster::GetFileFormatsFor(const media_node & file_interface, 1264 media_file_format * out_formats, 1265 int32 * io_num_infos) 1266 { 1267 UNIMPLEMENTED(); 1268 return B_ERROR; 1269 } 1270 1271 1272 status_t 1273 BMediaRoster::SetRefFor(const media_node & file_interface, 1274 const entry_ref & file, 1275 bool create_and_truncate, 1276 bigtime_t * out_length) /* if create is false */ 1277 { 1278 UNIMPLEMENTED(); 1279 return B_ERROR; 1280 } 1281 1282 1283 status_t 1284 BMediaRoster::GetRefFor(const media_node & node, 1285 entry_ref * out_file, 1286 BMimeType * mime_type) 1287 { 1288 UNIMPLEMENTED(); 1289 return B_ERROR; 1290 } 1291 1292 1293 status_t 1294 BMediaRoster::SniffRefFor(const media_node & file_interface, 1295 const entry_ref & file, 1296 BMimeType * mime_type, 1297 float * out_capability) 1298 { 1299 UNIMPLEMENTED(); 1300 return B_ERROR; 1301 } 1302 1303 1304 /* This is the generic "here's a file, now can someone please play it" interface */ 1305 status_t 1306 BMediaRoster::SniffRef(const entry_ref & file, 1307 uint64 require_node_kinds, /* if you need an EntityInterface or BufferConsumer or something */ 1308 dormant_node_info * out_node, 1309 BMimeType * mime_type) 1310 { 1311 UNIMPLEMENTED(); 1312 return B_ERROR; 1313 } 1314 1315 1316 status_t 1317 BMediaRoster::GetDormantNodeForType(const BMimeType & type, 1318 uint64 require_node_kinds, 1319 dormant_node_info * out_node) 1320 { 1321 UNIMPLEMENTED(); 1322 return B_ERROR; 1323 } 1324 1325 1326 status_t 1327 BMediaRoster::GetReadFileFormatsFor(const dormant_node_info & in_node, 1328 media_file_format * out_read_formats, 1329 int32 in_read_count, 1330 int32 * out_read_count) 1331 { 1332 UNIMPLEMENTED(); 1333 return B_ERROR; 1334 } 1335 1336 1337 status_t 1338 BMediaRoster::GetWriteFileFormatsFor(const dormant_node_info & in_node, 1339 media_file_format * out_write_formats, 1340 int32 in_write_count, 1341 int32 * out_write_count) 1342 { 1343 UNIMPLEMENTED(); 1344 return B_ERROR; 1345 } 1346 1347 1348 status_t 1349 BMediaRoster::GetFormatFor(const media_output & output, 1350 media_format * io_format, 1351 uint32 flags) 1352 { 1353 UNIMPLEMENTED(); 1354 return B_ERROR; 1355 } 1356 1357 1358 status_t 1359 BMediaRoster::GetFormatFor(const media_input & input, 1360 media_format * io_format, 1361 uint32 flags) 1362 { 1363 UNIMPLEMENTED(); 1364 return B_ERROR; 1365 } 1366 1367 1368 status_t 1369 BMediaRoster::GetFormatFor(const media_node & node, 1370 media_format * io_format, 1371 float quality) 1372 { 1373 UNIMPLEMENTED(); 1374 return B_ERROR; 1375 } 1376 1377 1378 ssize_t 1379 BMediaRoster::GetNodeAttributesFor(const media_node & node, 1380 media_node_attribute * outArray, 1381 size_t inMaxCount) 1382 { 1383 UNIMPLEMENTED(); 1384 return B_ERROR; 1385 } 1386 1387 1388 media_node_id 1389 BMediaRoster::NodeIDFor(port_id source_or_destination_port) 1390 { 1391 UNIMPLEMENTED(); 1392 return B_ERROR; 1393 } 1394 1395 1396 status_t 1397 BMediaRoster::GetInstancesFor(media_addon_id addon, 1398 int32 flavor, 1399 media_node_id * out_id, 1400 int32 * io_count) 1401 { 1402 UNIMPLEMENTED(); 1403 return B_ERROR; 1404 } 1405 1406 1407 1408 status_t 1409 BMediaRoster::SetRealtimeFlags(uint32 in_enabled) 1410 { 1411 UNIMPLEMENTED(); 1412 return B_ERROR; 1413 } 1414 1415 1416 status_t 1417 BMediaRoster::GetRealtimeFlags(uint32 * out_enabled) 1418 { 1419 UNIMPLEMENTED(); 1420 return B_ERROR; 1421 } 1422 1423 1424 ssize_t 1425 BMediaRoster::AudioBufferSizeFor(int32 channel_count, 1426 uint32 sample_format, 1427 float frame_rate, 1428 bus_type bus_kind) 1429 { 1430 UNIMPLEMENTED(); 1431 return 4096; 1432 } 1433 1434 1435 /* Use MediaFlags to inquire about specific features of the Media Kit. */ 1436 /* Returns < 0 for "not present", positive size for output data size. */ 1437 /* 0 means that the capability is present, but no data about it. */ 1438 /* static */ ssize_t 1439 BMediaRoster::MediaFlags(media_flags cap, 1440 void * buf, 1441 size_t maxSize) 1442 { 1443 UNIMPLEMENTED(); 1444 return 0; 1445 } 1446 1447 1448 1449 /* BLooper overrides */ 1450 /* virtual */ void 1451 BMediaRoster::MessageReceived(BMessage * message) 1452 { 1453 UNIMPLEMENTED(); 1454 } 1455 1456 /* virtual */ bool 1457 BMediaRoster::QuitRequested() 1458 { 1459 UNIMPLEMENTED(); 1460 return true; 1461 } 1462 1463 /* virtual */ BHandler * 1464 BMediaRoster::ResolveSpecifier(BMessage *msg, 1465 int32 index, 1466 BMessage *specifier, 1467 int32 form, 1468 const char *property) 1469 { 1470 UNIMPLEMENTED(); 1471 return 0; 1472 } 1473 1474 1475 /* virtual */ status_t 1476 BMediaRoster::GetSupportedSuites(BMessage *data) 1477 { 1478 UNIMPLEMENTED(); 1479 return B_ERROR; 1480 } 1481 1482 1483 BMediaRoster::~BMediaRoster() 1484 { 1485 CALLED(); 1486 BMessage msg(MEDIA_SERVER_UNREGISTER_APP); 1487 BMessage reply; 1488 msg.AddInt32("team",team); 1489 ServerMessenger->SendMessage(&msg,&reply); 1490 } 1491 1492 1493 /************************************************************* 1494 * private BMediaRoster 1495 *************************************************************/ 1496 1497 // deprecated call 1498 status_t 1499 BMediaRoster::SetOutputBuffersFor(const media_source & output, 1500 BBufferGroup * group, 1501 bool will_reclaim ) 1502 { 1503 UNIMPLEMENTED(); 1504 return B_ERROR; 1505 } 1506 1507 1508 /* FBC stuffing (Mmmh, Stuffing!) */ 1509 status_t BMediaRoster::_Reserved_MediaRoster_0(void *) { return B_ERROR; } 1510 status_t BMediaRoster::_Reserved_MediaRoster_1(void *) { return B_ERROR; } 1511 status_t BMediaRoster::_Reserved_MediaRoster_2(void *) { return B_ERROR; } 1512 status_t BMediaRoster::_Reserved_MediaRoster_3(void *) { return B_ERROR; } 1513 status_t BMediaRoster::_Reserved_MediaRoster_4(void *) { return B_ERROR; } 1514 status_t BMediaRoster::_Reserved_MediaRoster_5(void *) { return B_ERROR; } 1515 status_t BMediaRoster::_Reserved_MediaRoster_6(void *) { return B_ERROR; } 1516 status_t BMediaRoster::_Reserved_MediaRoster_7(void *) { return B_ERROR; } 1517 1518 1519 BMediaRoster::BMediaRoster() : 1520 BLooper("BMediaRoster looper",B_NORMAL_PRIORITY,B_LOOPER_PORT_DEFAULT_CAPACITY) 1521 { 1522 CALLED(); 1523 BMessage msg(MEDIA_SERVER_REGISTER_APP); 1524 BMessage reply; 1525 msg.AddInt32("team",team); 1526 ServerMessenger->SendMessage(&msg,&reply); 1527 } 1528 1529 /* static */ status_t 1530 BMediaRoster::ParseCommand(BMessage & reply) 1531 { 1532 UNIMPLEMENTED(); 1533 return B_ERROR; 1534 } 1535 1536 1537 1538 status_t 1539 BMediaRoster::GetDefaultInfo(media_node_id for_default, 1540 BMessage & out_config) 1541 { 1542 UNIMPLEMENTED(); 1543 return B_ERROR; 1544 } 1545 1546 1547 1548 status_t 1549 BMediaRoster::SetRunningDefault(media_node_id for_default, 1550 const media_node & node) 1551 { 1552 UNIMPLEMENTED(); 1553 return B_ERROR; 1554 } 1555 1556 1557 /************************************************************* 1558 * static BMediaRoster variables 1559 *************************************************************/ 1560 1561 bool BMediaRoster::_isMediaServer; 1562 port_id BMediaRoster::_mReplyPort; 1563 int32 BMediaRoster::_mReplyPortRes; 1564 int32 BMediaRoster::_mReplyPortUnavailCount; 1565 BMediaRoster * BMediaRoster::_sDefault = NULL; 1566 1567