/* * Copyright (c) 2002-2006 Marcus Overhagen * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files or portions * thereof (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject * to the following conditions: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice * in the binary, as well as this list of conditions and the following * disclaimer in the documentation and/or other materials provided with * the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ /* to comply with the license above, do not remove the following line */ char __dont_remove_copyright_from_binary[] = "Copyright (c) 2002-2006 Marcus Overhagen "; #include #include #include #include #include #include #include #include #include #include #include #include "debug.h" #include "MediaRosterEx.h" #include "MediaMisc.h" #include "PortPool.h" #include "ServerInterface.h" #include "DataExchange.h" #include "DormantNodeManager.h" #include "Notifications.h" #include "TimeSourceObjectManager.h" namespace BPrivate { namespace media { // the BMediaRoster destructor is private, // but _DefaultDeleter is a friend class of // the BMediaRoster an thus can delete it class DefaultDeleter { public: ~DefaultDeleter() { if (BMediaRoster::_sDefault) { BMediaRoster::_sDefault->Lock(); BMediaRoster::_sDefault->Quit(); } } }; } } // BPrivate::media using namespace BPrivate::media; // DefaultDeleter will delete the BMediaRoster object in it's destructor. DefaultDeleter _deleter; BMediaRosterEx::BMediaRosterEx(status_t * out_error) : BMediaRoster() { status_t rv; // register this application with the media server server_register_app_request request; server_register_app_reply reply; request.team = team; request.messenger = BMessenger(NULL, this); rv = QueryServer(SERVER_REGISTER_APP, &request, sizeof(request), &reply, sizeof(reply)); *out_error = (rv != B_OK) ? B_MEDIA_SYSTEM_FAILURE : B_OK; } status_t BMediaRosterEx::SaveNodeConfiguration(BMediaNode *node) { BMediaAddOn *addon; media_addon_id addonid; int32 flavorid; addon = node->AddOn(&flavorid); if (!addon) { // XXX this check incorrectly triggers on BeOS R5 BT848 node ERROR("BMediaRosterEx::SaveNodeConfiguration node %ld not instantiated from BMediaAddOn!\n", node->ID()); return B_ERROR; } addonid = addon->AddonID(); // XXX fix this printf("### BMediaRosterEx::SaveNodeConfiguration should save addon-id %ld, flavor-id %ld config NOW!\n", addonid, flavorid); return B_OK; } status_t BMediaRosterEx::LoadNodeConfiguration(media_addon_id addonid, int32 flavorid, BMessage *out_msg) { // XXX fix this out_msg->MakeEmpty(); // to be fully R5 compliant printf("### BMediaRosterEx::LoadNodeConfiguration should load addon-id %ld, flavor-id %ld config NOW!\n", addonid, flavorid); return B_OK; } status_t BMediaRosterEx::IncrementAddonFlavorInstancesCount(media_addon_id addonid, int32 flavorid) { server_change_addon_flavor_instances_count_request request; server_change_addon_flavor_instances_count_reply reply; request.addonid = addonid; request.flavorid = flavorid; request.delta = 1; request.team = team; return QueryServer(SERVER_CHANGE_ADDON_FLAVOR_INSTANCES_COUNT, &request, sizeof(request), &reply, sizeof(reply)); } status_t BMediaRosterEx::DecrementAddonFlavorInstancesCount(media_addon_id addonid, int32 flavorid) { server_change_addon_flavor_instances_count_request request; server_change_addon_flavor_instances_count_reply reply; request.addonid = addonid; request.flavorid = flavorid; request.delta = -1; request.team = team; return QueryServer(SERVER_CHANGE_ADDON_FLAVOR_INSTANCES_COUNT, &request, sizeof(request), &reply, sizeof(reply)); } status_t BMediaRosterEx::SetNodeCreator(media_node_id node, team_id creator) { server_set_node_creator_request request; server_set_node_creator_reply reply; request.node = node; request.creator = creator; return QueryServer(SERVER_SET_NODE_CREATOR, &request, sizeof(request), &reply, sizeof(reply)); } status_t BMediaRosterEx::GetNode(node_type type, media_node * out_node, int32 * out_input_id, BString * out_input_name) { if (out_node == NULL) return B_BAD_VALUE; server_get_node_request request; server_get_node_reply reply; status_t rv; request.type = type; request.team = team; rv = QueryServer(SERVER_GET_NODE, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) return rv; *out_node = reply.node; if (out_input_id) *out_input_id = reply.input_id; if (out_input_name) *out_input_name = reply.input_name; return rv; } status_t BMediaRosterEx::SetNode(node_type type, const media_node *node, const dormant_node_info *info, const media_input *input) { server_set_node_request request; server_set_node_reply reply; request.type = type; request.use_node = node ? true : false; if (node) request.node = *node; request.use_dni = info ? true : false; if (info) request.dni = *info; request.use_input = input ? true : false; if (input) request.input = *input; return QueryServer(SERVER_SET_NODE, &request, sizeof(request), &reply, sizeof(reply)); } status_t BMediaRosterEx::GetAllOutputs(const media_node & node, List *list) { int32 cookie; status_t rv; status_t result; PRINT(4, "BMediaRosterEx::GetAllOutputs() node %ld, port %ld\n", node.node, node.port); if (!(node.kind & B_BUFFER_PRODUCER)) { ERROR("BMediaRosterEx::GetAllOutputs: node %ld is not a B_BUFFER_PRODUCER\n", node.node); return B_MEDIA_BAD_NODE; } result = B_OK; cookie = 0; list->MakeEmpty(); for (;;) { producer_get_next_output_request request; producer_get_next_output_reply reply; request.cookie = cookie; rv = QueryPort(node.port, PRODUCER_GET_NEXT_OUTPUT, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) break; cookie = reply.cookie; if (!list->Insert(reply.output)) { ERROR("GetAllOutputs: list->Insert failed\n"); result = B_ERROR; } #if DEBUG >= 3 PRINT(3," next cookie %ld, ", cookie); PRINT_OUTPUT("output ", reply.output); #endif } producer_dispose_output_cookie_request request; producer_dispose_output_cookie_reply reply; QueryPort(node.port, PRODUCER_DISPOSE_OUTPUT_COOKIE, &request, sizeof(request), &reply, sizeof(reply)); return result; } status_t BMediaRosterEx::GetAllOutputs(BBufferProducer *node, List *list) { int32 cookie; status_t result; PRINT(4, "BMediaRosterEx::GetAllOutputs() (by pointer) node %ld, port %ld\n", node->ID(), node->ControlPort()); result = B_OK; cookie = 0; list->MakeEmpty(); for (;;) { media_output output; if (B_OK != node->GetNextOutput(&cookie, &output)) break; if (!list->Insert(output)) { ERROR("GetAllOutputs: list->Insert failed\n"); result = B_ERROR; } #if DEBUG >= 3 PRINT(3," next cookie %ld, ", cookie); PRINT_OUTPUT("output ", output); #endif } node->DisposeOutputCookie(cookie); return result; } status_t BMediaRosterEx::GetAllInputs(const media_node & node, List *list) { int32 cookie; status_t rv; status_t result; PRINT(4, "BMediaRosterEx::GetAllInputs() node %ld, port %ld\n", node.node, node.port); if (!(node.kind & B_BUFFER_CONSUMER)) { ERROR("BMediaRosterEx::GetAllInputs: node %ld is not a B_BUFFER_CONSUMER\n", node.node); return B_MEDIA_BAD_NODE; } result = B_OK; cookie = 0; list->MakeEmpty(); for (;;) { consumer_get_next_input_request request; consumer_get_next_input_reply reply; request.cookie = cookie; rv = QueryPort(node.port, CONSUMER_GET_NEXT_INPUT, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) break; cookie = reply.cookie; if (!list->Insert(reply.input)) { ERROR("GetAllInputs: list->Insert failed\n"); result = B_ERROR; } #if DEBUG >= 3 PRINT(3," next cookie %ld, ", cookie); PRINT_OUTPUT("input ", reply.input); #endif } consumer_dispose_input_cookie_request request; consumer_dispose_input_cookie_reply reply; QueryPort(node.port, CONSUMER_DISPOSE_INPUT_COOKIE, &request, sizeof(request), &reply, sizeof(reply)); return result; } status_t BMediaRosterEx::GetAllInputs(BBufferConsumer *node, List *list) { int32 cookie; status_t result; PRINT(4, "BMediaRosterEx::GetAllInputs() (by pointer) node %ld, port %ld\n", node->ID(), node->ControlPort()); result = B_OK; cookie = 0; list->MakeEmpty(); for (;;) { media_input input; if (B_OK != node->GetNextInput(&cookie, &input)) break; if (!list->Insert(input)) { ERROR("GetAllInputs: list->Insert failed\n"); result = B_ERROR; } #if DEBUG >= 3 PRINT(3," next cookie %ld, ", cookie); PRINT_INPUT("input ", input); #endif } node->DisposeInputCookie(cookie); return result; } status_t BMediaRosterEx::PublishOutputs(const media_node & node, List *list) { server_publish_outputs_request request; server_publish_outputs_reply reply; media_output *output; media_output *outputs; int32 count; status_t rv; count = list->CountItems(); TRACE("PublishOutputs: publishing %ld\n", count); request.node = node; request.count = count; if (count > MAX_OUTPUTS) { void *start_addr; size_t size; size = ROUND_UP_TO_PAGE(count * sizeof(media_output)); request.area = create_area("publish outputs", &start_addr, B_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); if (request.area < B_OK) { ERROR("PublishOutputs: failed to create area, %#lx\n", request.area); return (status_t)request.area; } outputs = static_cast(start_addr); } else { request.area = -1; outputs = request.outputs; } TRACE("PublishOutputs: area %ld\n", request.area); int i; for (i = 0, list->Rewind(); list->GetNext(&output); i++) { ASSERT(i < count); outputs[i] = *output; } rv = QueryServer(SERVER_PUBLISH_OUTPUTS, &request, sizeof(request), &reply, sizeof(reply)); if (request.area != -1) delete_area(request.area); return rv; } status_t BMediaRosterEx::PublishInputs(const media_node & node, List *list) { server_publish_inputs_request request; server_publish_inputs_reply reply; media_input *input; media_input *inputs; int32 count; status_t rv; count = list->CountItems(); TRACE("PublishInputs: publishing %ld\n", count); request.node = node; request.count = count; if (count > MAX_INPUTS) { void *start_addr; size_t size; size = ROUND_UP_TO_PAGE(count * sizeof(media_input)); request.area = create_area("publish inputs", &start_addr, B_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); if (request.area < B_OK) { ERROR("PublishInputs: failed to create area, %#lx\n", request.area); return (status_t)request.area; } inputs = static_cast(start_addr); } else { request.area = -1; inputs = request.inputs; } TRACE("PublishInputs: area %ld\n", request.area); int i; for (i = 0, list->Rewind(); list->GetNext(&input); i++) { ASSERT(i < count); inputs[i] = *input; } rv = QueryServer(SERVER_PUBLISH_INPUTS, &request, sizeof(request), &reply, sizeof(reply)); if (request.area != -1) delete_area(request.area); return rv; } /************************************************************* * public BMediaRoster *************************************************************/ status_t BMediaRoster::GetVideoInput(media_node * out_node) { CALLED(); return MediaRosterEx(this)->GetNode(VIDEO_INPUT, out_node); } status_t BMediaRoster::GetAudioInput(media_node * out_node) { CALLED(); return MediaRosterEx(this)->GetNode(AUDIO_INPUT, out_node); } status_t BMediaRoster::GetVideoOutput(media_node * out_node) { CALLED(); return MediaRosterEx(this)->GetNode(VIDEO_OUTPUT, out_node); } status_t BMediaRoster::GetAudioMixer(media_node * out_node) { CALLED(); return MediaRosterEx(this)->GetNode(AUDIO_MIXER, out_node); } status_t BMediaRoster::GetAudioOutput(media_node * out_node) { CALLED(); return MediaRosterEx(this)->GetNode(AUDIO_OUTPUT, out_node); } status_t BMediaRoster::GetAudioOutput(media_node * out_node, int32 * out_input_id, BString * out_input_name) { CALLED(); return MediaRosterEx(this)->GetNode(AUDIO_OUTPUT_EX, out_node, out_input_id, out_input_name); } status_t BMediaRoster::GetTimeSource(media_node * out_node) { CALLED(); status_t rv; // XXX need to do this in a nicer way. rv = MediaRosterEx(this)->GetNode(TIME_SOURCE, out_node); if (rv != B_OK) return rv; // We don't do reference counting for timesources, that's why we // release the node immediately. ReleaseNode(*out_node); // we need to remember to not use this node with server side reference counting out_node->kind |= NODE_KIND_NO_REFCOUNTING; return B_OK; } status_t BMediaRoster::SetVideoInput(const media_node & producer) { CALLED(); return MediaRosterEx(this)->SetNode(VIDEO_INPUT, &producer); } status_t BMediaRoster::SetVideoInput(const dormant_node_info & producer) { CALLED(); return MediaRosterEx(this)->SetNode(VIDEO_INPUT, NULL, &producer); } status_t BMediaRoster::SetAudioInput(const media_node & producer) { CALLED(); return MediaRosterEx(this)->SetNode(AUDIO_INPUT, &producer); } status_t BMediaRoster::SetAudioInput(const dormant_node_info & producer) { CALLED(); return MediaRosterEx(this)->SetNode(AUDIO_INPUT, NULL, &producer); } status_t BMediaRoster::SetVideoOutput(const media_node & consumer) { CALLED(); return MediaRosterEx(this)->SetNode(VIDEO_OUTPUT, &consumer); } status_t BMediaRoster::SetVideoOutput(const dormant_node_info & consumer) { CALLED(); return MediaRosterEx(this)->SetNode(VIDEO_OUTPUT, NULL, &consumer); } status_t BMediaRoster::SetAudioOutput(const media_node & consumer) { CALLED(); return MediaRosterEx(this)->SetNode(AUDIO_OUTPUT, &consumer); } status_t BMediaRoster::SetAudioOutput(const media_input & input_to_output) { CALLED(); return MediaRosterEx(this)->SetNode(AUDIO_OUTPUT, NULL, NULL, &input_to_output); } status_t BMediaRoster::SetAudioOutput(const dormant_node_info & consumer) { CALLED(); return MediaRosterEx(this)->SetNode(AUDIO_OUTPUT, NULL, &consumer); } status_t BMediaRoster::GetNodeFor(media_node_id node, media_node * clone) { CALLED(); if (clone == NULL) return B_BAD_VALUE; if (IS_INVALID_NODEID(node)) return B_MEDIA_BAD_NODE; server_get_node_for_request request; server_get_node_for_reply reply; status_t rv; request.nodeid = node; request.team = team; rv = QueryServer(SERVER_GET_NODE_FOR, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) return rv; *clone = reply.clone; return B_OK; } status_t BMediaRoster::GetSystemTimeSource(media_node * clone) { CALLED(); status_t rv; // XXX need to do this in a nicer way. rv = MediaRosterEx(this)->GetNode(SYSTEM_TIME_SOURCE, clone); if (rv != B_OK) return rv; // We don't do reference counting for timesources, that's why we // release the node immediately. ReleaseNode(*clone); // we need to remember to not use this node with server side reference counting clone->kind |= NODE_KIND_NO_REFCOUNTING; return B_OK; } status_t BMediaRoster::ReleaseNode(const media_node & node) { CALLED(); if (IS_INVALID_NODE(node)) return B_MEDIA_BAD_NODE; if (node.kind & NODE_KIND_NO_REFCOUNTING) { printf("BMediaRoster::ReleaseNode, trying to release reference counting disabled timesource, node %ld, port %ld, team %ld\n", node.node, node.port, team); return B_OK; } server_release_node_request request; server_release_node_reply reply; status_t rv; request.node = node; request.team = team; TRACE("BMediaRoster::ReleaseNode, node %ld, port %ld, team %ld\n", node.node, node.port, team); rv = QueryServer(SERVER_RELEASE_NODE, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) { ERROR("BMediaRoster::ReleaseNode FAILED, node %ld, port %ld, team %ld!\n", node.node, node.port, team); } return rv; } BTimeSource * BMediaRoster::MakeTimeSourceFor(const media_node & for_node) { // MakeTimeSourceFor() returns a BTimeSource object // corresponding to the specified node's time source. CALLED(); if (IS_SYSTEM_TIMESOURCE(for_node)) { // special handling for the system time source TRACE("BMediaRoster::MakeTimeSourceFor, asked for system time source\n"); return MediaRosterEx(this)->MakeTimeSourceObject(NODE_SYSTEM_TIMESOURCE_ID); } if (IS_INVALID_NODE(for_node)) { ERROR("BMediaRoster::MakeTimeSourceFor: for_node invalid, node %ld, port %ld, kinds 0x%lx\n", for_node.node, for_node.port, for_node.kind); return NULL; } TRACE("BMediaRoster::MakeTimeSourceFor: node %ld enter\n", for_node.node); node_get_timesource_request request; node_get_timesource_reply reply; BTimeSource *source; status_t rv; // ask the node to get it's current timesource id rv = QueryPort(for_node.port, NODE_GET_TIMESOURCE, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) { ERROR("BMediaRoster::MakeTimeSourceFor: request failed\n"); return NULL; } source = MediaRosterEx(this)->MakeTimeSourceObject(reply.timesource_id); TRACE("BMediaRoster::MakeTimeSourceFor: node %ld leave\n", for_node.node); return source; } BTimeSource * BMediaRosterEx::MakeTimeSourceObject(media_node_id timesource_id) { BTimeSource *source; media_node clone; status_t rv; rv = GetNodeFor(timesource_id, &clone); if (rv != B_OK) { ERROR("BMediaRosterEx::MakeTimeSourceObject: GetNodeFor failed\n"); return NULL; } source = _TimeSourceObjectManager->GetTimeSource(clone); if (source == NULL) { ERROR("BMediaRosterEx::MakeTimeSourceObject: GetTimeSource failed\n"); return NULL; } // XXX release? ReleaseNode(clone); return source; } status_t BMediaRoster::Connect(const media_source & from, const media_destination & to, media_format * io_format, media_output * out_output, media_input * out_input) { return BMediaRoster::Connect(from, to, io_format, out_output, out_input, 0); } status_t BMediaRoster::Connect(const media_source & from, const media_destination & to, media_format * io_format, media_output * out_output, media_input * out_input, uint32 in_flags, void * _reserved) { CALLED(); if (io_format == NULL || out_output == NULL || out_input == NULL) return B_BAD_VALUE; if (IS_INVALID_SOURCE(from)) { ERROR("BMediaRoster::Connect: media_source invalid\n"); return B_MEDIA_BAD_SOURCE; } if (IS_INVALID_DESTINATION(to)) { ERROR("BMediaRoster::Connect: media_destination invalid\n"); return B_MEDIA_BAD_DESTINATION; } status_t rv; // find the output and input nodes // XXX isn't there a easier way? media_node sourcenode; media_node destnode; rv = GetNodeFor(NodeIDFor(from.port), &sourcenode); if (rv != B_OK) { ERROR("BMediaRoster::Connect: Can't find source node for port %ld\n", from.port); return B_MEDIA_BAD_SOURCE; } ReleaseNode(sourcenode); rv = GetNodeFor(NodeIDFor(to.port), &destnode); if (rv != B_OK) { ERROR("BMediaRoster::Connect: Can't find destination node for port %ld\n", to.port); return B_MEDIA_BAD_DESTINATION; } ReleaseNode(destnode); if (!(sourcenode.kind & B_BUFFER_PRODUCER)) { ERROR("BMediaRoster::Connect: source node %ld is not a B_BUFFER_PRODUCER\n", sourcenode.node); return B_MEDIA_BAD_SOURCE; } if (!(destnode.kind & B_BUFFER_CONSUMER)) { ERROR("BMediaRoster::Connect: destination node %ld is not a B_BUFFER_CONSUMER\n", destnode.node); return B_MEDIA_BAD_DESTINATION; } producer_format_proposal_request request1; producer_format_proposal_reply reply1; PRINT_FORMAT("BMediaRoster::Connect calling BBufferProducer::FormatProposal with format ", *io_format); // BBufferProducer::FormatProposal request1.output = from; request1.format = *io_format; rv = QueryPort(from.port, PRODUCER_FORMAT_PROPOSAL, &request1, sizeof(request1), &reply1, sizeof(reply1)); if (rv != B_OK) { ERROR("BMediaRoster::Connect: aborted after BBufferProducer::FormatProposal, status = %#lx\n",rv); return rv; } // reply1.format now contains the format proposed by the producer consumer_accept_format_request request2; consumer_accept_format_reply reply2; PRINT_FORMAT("BMediaRoster::Connect calling BBufferConsumer::AcceptFormat with format ", reply1.format); // BBufferConsumer::AcceptFormat request2.dest = to; request2.format = reply1.format; rv = QueryPort(to.port, CONSUMER_ACCEPT_FORMAT, &request2, sizeof(request2), &reply2, sizeof(reply2)); if (rv != B_OK) { ERROR("BMediaRoster::Connect: aborted after BBufferConsumer::AcceptFormat, status = %#lx\n",rv); return rv; } // reply2.format now contains the format accepted by the consumer // BBufferProducer::PrepareToConnect producer_prepare_to_connect_request request3; producer_prepare_to_connect_reply reply3; PRINT_FORMAT("BMediaRoster::Connect calling BBufferProducer::PrepareToConnect with format", reply2.format); request3.source = from; request3.destination = to; request3.format = reply2.format; strcpy(request3.name, "XXX some default name"); // XXX fix this rv = QueryPort(from.port, PRODUCER_PREPARE_TO_CONNECT, &request3, sizeof(request3), &reply3, sizeof(reply3)); if (rv != B_OK) { ERROR("BMediaRoster::Connect: aborted after BBufferProducer::PrepareToConnect, status = %#lx\n",rv); return rv; } // reply3.format is still our pretty media format // reply3.out_source the real source to be used for the connection // reply3.name the name BBufferConsumer::Connected will see in the outInput->name argument // BBufferConsumer::Connected consumer_connected_request request4; consumer_connected_reply reply4; status_t con_status; PRINT_FORMAT("BMediaRoster::Connect calling BBufferConsumer::Connected with format ", reply3.format); request4.input.node = destnode; request4.input.source = reply3.out_source; request4.input.destination = to; request4.input.format = reply3.format; strcpy(request4.input.name, reply3.name); con_status = QueryPort(to.port, CONSUMER_CONNECTED, &request4, sizeof(request4), &reply4, sizeof(reply4)); if (con_status != B_OK) { ERROR("BMediaRoster::Connect: aborting after BBufferConsumer::Connected, status = %#lx\n",con_status); // we do NOT return here! } // con_status contains the status code to be supplied to BBufferProducer::Connect's status argument // reply4.input contains the media_input that describes the connection from the consumer point of view // BBufferProducer::Connect producer_connect_request request5; producer_connect_reply reply5; PRINT_FORMAT("BMediaRoster::Connect calling BBufferProducer::Connect with format ", reply4.input.format); request5.error = con_status; request5.source = reply3.out_source; request5.destination = reply4.input.destination; request5.format = reply4.input.format; strcpy(request5.name, reply4.input.name); rv = QueryPort(reply4.input.source.port, PRODUCER_CONNECT, &request5, sizeof(request5), &reply5, sizeof(reply5)); if (con_status != B_OK) { ERROR("BMediaRoster::Connect: aborted\n"); return con_status; } if (rv != B_OK) { ERROR("BMediaRoster::Connect: aborted after BBufferProducer::Connect, status = %#lx\n",rv); return rv; } // reply5.name contains the name assigned to the connection by the producer // initilize connection info *io_format = reply4.input.format; *out_input = reply4.input; out_output->node = sourcenode; out_output->source = reply4.input.source; out_output->destination = reply4.input.destination; out_output->format = reply4.input.format; strcpy(out_output->name, reply5.name); // the connection is now made printf("BMediaRoster::Connect connection established!\n"); PRINT_FORMAT(" format", *io_format); PRINT_INPUT(" input", *out_input); PRINT_OUTPUT(" output", *out_output); // XXX register connection with server // XXX we should just send a notification, instead of republishing all endpoints List outlist; List inlist; if (B_OK == MediaRosterEx(this)->GetAllOutputs(out_output->node , &outlist)) MediaRosterEx(this)->PublishOutputs(out_output->node , &outlist); if (B_OK == MediaRosterEx(this)->GetAllInputs(out_input->node , &inlist)) MediaRosterEx(this)->PublishInputs(out_input->node, &inlist); // XXX if (mute) BBufferProducer::EnableOutput(false) if (in_flags & B_CONNECT_MUTED) { } // send a notification BPrivate::media::notifications::ConnectionMade(*out_input, *out_output, *io_format); return B_OK; }; status_t BMediaRoster::Disconnect(media_node_id source_nodeid, const media_source & source, media_node_id destination_nodeid, const media_destination & destination) { CALLED(); if (IS_INVALID_NODEID(source_nodeid)) { ERROR("BMediaRoster::Disconnect: source media_node_id invalid\n"); return B_MEDIA_BAD_SOURCE; } if (IS_INVALID_NODEID(destination_nodeid)) { ERROR("BMediaRoster::Disconnect: destination media_node_id invalid\n"); return B_MEDIA_BAD_DESTINATION; } if (IS_INVALID_SOURCE(source)) { ERROR("BMediaRoster::Disconnect: media_source invalid\n"); return B_MEDIA_BAD_SOURCE; } if (IS_INVALID_DESTINATION(destination)) { ERROR("BMediaRoster::Disconnect: media_destination invalid\n"); return B_MEDIA_BAD_DESTINATION; } producer_disconnect_request request2; producer_disconnect_reply reply2; consumer_disconnected_request request1; consumer_disconnected_reply reply1; status_t rv1, rv2; // XXX we should ask the server if this connection really exists request1.source = source; request1.destination = destination; request2.source = source; request2.destination = destination; rv1 = QueryPort(source.port, PRODUCER_DISCONNECT, &request1, sizeof(request1), &reply1, sizeof(reply1)); rv2 = QueryPort(destination.port, CONSUMER_DISCONNECTED, &request2, sizeof(request2), &reply2, sizeof(reply2)); // XXX unregister connection with server // XXX we should just send a notification, instead of republishing all endpoints List outlist; List inlist; media_node sourcenode; media_node destnode; if (B_OK == GetNodeFor(source_nodeid, &sourcenode)) { if (!(sourcenode.kind & B_BUFFER_PRODUCER)) { ERROR("BMediaRoster::Disconnect: source_nodeid %ld is not a B_BUFFER_PRODUCER\n", source_nodeid); } if (B_OK == MediaRosterEx(this)->GetAllOutputs(sourcenode , &outlist)) MediaRosterEx(this)->PublishOutputs(sourcenode , &outlist); ReleaseNode(sourcenode); } else { ERROR("BMediaRoster::Disconnect: GetNodeFor source_nodeid %ld failed\n", source_nodeid); } if (B_OK == GetNodeFor(destination_nodeid, &destnode)) { if (!(destnode.kind & B_BUFFER_CONSUMER)) { ERROR("BMediaRoster::Disconnect: destination_nodeid %ld is not a B_BUFFER_CONSUMER\n", destination_nodeid); } if (B_OK == MediaRosterEx(this)->GetAllInputs(destnode , &inlist)) MediaRosterEx(this)->PublishInputs(destnode, &inlist); ReleaseNode(destnode); } else { ERROR("BMediaRoster::Disconnect: GetNodeFor destination_nodeid %ld failed\n", destination_nodeid); } // send a notification BPrivate::media::notifications::ConnectionBroken(source, destination); return (rv1 != B_OK || rv2 != B_OK) ? B_ERROR : B_OK; } status_t BMediaRoster::Disconnect(const media_output &output, const media_input &input) { if (IS_INVALID_NODEID(output.node.node)) { printf("BMediaRoster::Disconnect: output.node.node %ld invalid\n", output.node.node); return B_MEDIA_BAD_SOURCE; } if (IS_INVALID_NODEID(input.node.node)) { printf("BMediaRoster::Disconnect: input.node.node %ld invalid\n", input.node.node); return B_MEDIA_BAD_DESTINATION; } if (!(output.node.kind & B_BUFFER_PRODUCER)) { printf("BMediaRoster::Disconnect: output.node.kind 0x%lx is no B_BUFFER_PRODUCER\n", output.node.kind); return B_MEDIA_BAD_SOURCE; } if (!(input.node.kind & B_BUFFER_CONSUMER)) { printf("BMediaRoster::Disconnect: input.node.kind 0x%lx is no B_BUFFER_PRODUCER\n", input.node.kind); return B_MEDIA_BAD_DESTINATION; } if (input.source.port != output.source.port) { printf("BMediaRoster::Disconnect: input.source.port %ld doesn't match output.source.port %ld\n", input.source.port, output.source.port); return B_MEDIA_BAD_SOURCE; } if (input.source.id != output.source.id) { printf("BMediaRoster::Disconnect: input.source.id %ld doesn't match output.source.id %ld\n", input.source.id, output.source.id); return B_MEDIA_BAD_SOURCE; } if (input.destination.port != output.destination.port) { printf("BMediaRoster::Disconnect: input.destination.port %ld doesn't match output.destination.port %ld\n", input.destination.port, output.destination.port); return B_MEDIA_BAD_DESTINATION; } if (input.destination.id != output.destination.id) { printf("BMediaRoster::Disconnect: input.destination.id %ld doesn't match output.destination.id %ld\n", input.destination.id, output.destination.id); return B_MEDIA_BAD_DESTINATION; } return Disconnect(output.node.node, output.source, input.node.node, input.destination); } status_t BMediaRoster::StartNode(const media_node & node, bigtime_t at_performance_time) { CALLED(); if (node.node <= 0) return B_MEDIA_BAD_NODE; TRACE("BMediaRoster::StartNode, node %ld, at perf %Ld\n", node.node, at_performance_time); node_start_command command; command.performance_time = at_performance_time; return SendToPort(node.port, NODE_START, &command, sizeof(command)); } status_t BMediaRoster::StopNode(const media_node & node, bigtime_t at_performance_time, bool immediate) { CALLED(); if (IS_INVALID_NODE(node)) return B_MEDIA_BAD_NODE; TRACE("BMediaRoster::StopNode, node %ld, at perf %Ld %s\n", node.node, at_performance_time, immediate ? "NOW" : ""); node_stop_command command; command.performance_time = at_performance_time; command.immediate = immediate; return SendToPort(node.port, NODE_STOP, &command, sizeof(command)); } status_t BMediaRoster::SeekNode(const media_node & node, bigtime_t to_media_time, bigtime_t at_performance_time) { CALLED(); if (IS_INVALID_NODE(node)) return B_MEDIA_BAD_NODE; TRACE("BMediaRoster::SeekNode, node %ld, at perf %Ld, to perf %Ld\n", node.node, at_performance_time, to_media_time); node_seek_command command; command.media_time = to_media_time; command.performance_time = at_performance_time; return SendToPort(node.port, NODE_SEEK, &command, sizeof(command)); } status_t BMediaRoster::StartTimeSource(const media_node & node, bigtime_t at_real_time) { CALLED(); if (IS_SYSTEM_TIMESOURCE(node)) { // XXX debug this //ERROR("BMediaRoster::StartTimeSource node %ld is system timesource\n", node.node); return B_OK; } // if (IS_SHADOW_TIMESOURCE(node)) { // // XXX debug this // ERROR("BMediaRoster::StartTimeSource node %ld is shadow timesource\n", node.node); // return B_OK; // } if (IS_INVALID_NODE(node)) { ERROR("BMediaRoster::StartTimeSource node %ld invalid\n", node.node); return B_MEDIA_BAD_NODE; } if ((node.kind & B_TIME_SOURCE) == 0) { ERROR("BMediaRoster::StartTimeSource node %ld is no timesource\n", node.node); return B_MEDIA_BAD_NODE; } TRACE("BMediaRoster::StartTimeSource, node %ld, at real %Ld\n", node.node, at_real_time); BTimeSource::time_source_op_info msg; msg.op = BTimeSource::B_TIMESOURCE_START; msg.real_time = at_real_time; return write_port(node.port, TIMESOURCE_OP, &msg, sizeof(msg)); } status_t BMediaRoster::StopTimeSource(const media_node & node, bigtime_t at_real_time, bool immediate) { CALLED(); if (IS_SYSTEM_TIMESOURCE(node)) { // XXX debug this //ERROR("BMediaRoster::StopTimeSource node %ld is system timesource\n", node.node); return B_OK; } // if (IS_SHADOW_TIMESOURCE(node)) { // // XXX debug this // ERROR("BMediaRoster::StopTimeSource node %ld is shadow timesource\n", node.node); // return B_OK; // } if (IS_INVALID_NODE(node)) { ERROR("BMediaRoster::StopTimeSource node %ld invalid\n", node.node); return B_MEDIA_BAD_NODE; } if ((node.kind & B_TIME_SOURCE) == 0) { ERROR("BMediaRoster::StopTimeSource node %ld is no timesource\n", node.node); return B_MEDIA_BAD_NODE; } TRACE("BMediaRoster::StopTimeSource, node %ld, at real %Ld %s\n", node.node, at_real_time, immediate ? "NOW" : ""); BTimeSource::time_source_op_info msg; msg.op = immediate ? BTimeSource::B_TIMESOURCE_STOP_IMMEDIATELY : BTimeSource::B_TIMESOURCE_STOP; msg.real_time = at_real_time; return write_port(node.port, TIMESOURCE_OP, &msg, sizeof(msg)); } status_t BMediaRoster::SeekTimeSource(const media_node & node, bigtime_t to_performance_time, bigtime_t at_real_time) { CALLED(); if (IS_SYSTEM_TIMESOURCE(node)) { // XXX debug this // ERROR("BMediaRoster::SeekTimeSource node %ld is system timesource\n", node.node); // you can't seek the system time source, but // returning B_ERROR would break StampTV return B_OK; } // if (IS_SHADOW_TIMESOURCE(node)) { // // XXX debug this // ERROR("BMediaRoster::SeekTimeSource node %ld is shadow timesource\n", node.node); // return B_OK; // } if (IS_INVALID_NODE(node)) { ERROR("BMediaRoster::SeekTimeSource node %ld invalid\n", node.node); return B_MEDIA_BAD_NODE; } if ((node.kind & B_TIME_SOURCE) == 0) { ERROR("BMediaRoster::SeekTimeSource node %ld is no timesource\n", node.node); return B_MEDIA_BAD_NODE; } TRACE("BMediaRoster::SeekTimeSource, node %ld, at real %Ld, to perf %Ld\n", node.node, at_real_time, to_performance_time); BTimeSource::time_source_op_info msg; msg.op = BTimeSource::B_TIMESOURCE_SEEK; msg.real_time = at_real_time; msg.performance_time = to_performance_time; return write_port(node.port, TIMESOURCE_OP, &msg, sizeof(msg)); } status_t BMediaRoster::SyncToNode(const media_node & node, bigtime_t at_time, bigtime_t timeout) { UNIMPLEMENTED(); return B_OK; } status_t BMediaRoster::SetRunModeNode(const media_node & node, BMediaNode::run_mode mode) { CALLED(); if (IS_INVALID_NODE(node)) return B_MEDIA_BAD_NODE; node_set_run_mode_command msg; msg.mode = mode; return write_port(node.port, NODE_SET_RUN_MODE, &msg, sizeof(msg)); } status_t BMediaRoster::PrerollNode(const media_node & node) { CALLED(); if (IS_INVALID_NODE(node)) return B_MEDIA_BAD_NODE; char dummy; return write_port(node.port, NODE_PREROLL, &dummy, sizeof(dummy)); } status_t BMediaRoster::RollNode(const media_node & node, bigtime_t startPerformance, bigtime_t stopPerformance, bigtime_t atMediaTime) { UNIMPLEMENTED(); return B_ERROR; } status_t BMediaRoster::SetProducerRunModeDelay(const media_node & node, bigtime_t delay, BMediaNode::run_mode mode) { CALLED(); if (IS_INVALID_NODE(node)) return B_MEDIA_BAD_NODE; if ((node.kind & B_BUFFER_PRODUCER) == 0) return B_MEDIA_BAD_NODE; producer_set_run_mode_delay_command command; command.mode = mode; command.delay = delay; return SendToPort(node.port, PRODUCER_SET_RUN_MODE_DELAY, &command, sizeof(command)); } status_t BMediaRoster::SetProducerRate(const media_node & producer, int32 numer, int32 denom) { CALLED(); if (IS_INVALID_NODE(producer)) return B_MEDIA_BAD_NODE; if ((producer.kind & B_BUFFER_PRODUCER) == 0) return B_MEDIA_BAD_NODE; producer_set_play_rate_request msg; producer_set_play_rate_reply reply; status_t rv; int32 code; msg.numer = numer; msg.denom = denom; msg.reply_port = _PortPool->GetPort(); rv = write_port(producer.node, PRODUCER_SET_PLAY_RATE, &msg, sizeof(msg)); if (rv != B_OK) { _PortPool->PutPort(msg.reply_port); return rv; } rv = read_port(msg.reply_port, &code, &reply, sizeof(reply)); _PortPool->PutPort(msg.reply_port); return (rv < B_OK) ? rv : reply.result; } /* Nodes will have available inputs/outputs as long as they are capable */ /* of accepting more connections. The node may create an additional */ /* output or input as the currently available is taken into usage. */ status_t BMediaRoster::GetLiveNodeInfo(const media_node & node, live_node_info * out_live_info) { CALLED(); if (out_live_info == NULL) return B_BAD_VALUE; if (IS_INVALID_NODE(node)) return B_MEDIA_BAD_NODE; server_get_live_node_info_request request; server_get_live_node_info_reply reply; status_t rv; request.node = node; rv = QueryServer(SERVER_GET_LIVE_NODE_INFO, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) return rv; *out_live_info = reply.live_info; return B_OK; } status_t BMediaRoster::GetLiveNodes(live_node_info * out_live_nodes, int32 * io_total_count, const media_format * has_input, const media_format * has_output, const char * name, uint64 node_kinds) { CALLED(); if (out_live_nodes == NULL || io_total_count == NULL) return B_BAD_VALUE; if (*io_total_count <= 0) return B_BAD_VALUE; // XXX we also support the wildcard search as GetDormantNodes does. This needs to be documented server_get_live_nodes_request request; server_get_live_nodes_reply reply; status_t rv; request.maxcount = *io_total_count; request.has_input = (bool) has_input; if (has_input) request.inputformat = *has_input; // XXX we should not make a flat copy of media_format request.has_output = (bool) has_output; if (has_output) request.outputformat = *has_output; // XXX we should not make a flat copy of media_format request.has_name = (bool) name; if (name) { int len = strlen(name); len = min_c(len, (int)sizeof(request.name) - 1); memcpy(request.name, name, len); request.name[len] = 0; } request.require_kinds = node_kinds; rv = QueryServer(SERVER_GET_LIVE_NODES, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) { ERROR("BMediaRoster::GetLiveNodes failed querying server\n"); *io_total_count = 0; return rv; } if (reply.count > MAX_LIVE_INFO) { live_node_info *live_info; area_id clone; clone = clone_area("live_node_info clone", reinterpret_cast(&live_info), B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, reply.area); if (clone < B_OK) { ERROR("BMediaRoster::GetLiveNodes failed to clone area, %#lx\n", clone); delete_area(reply.area); *io_total_count = 0; return B_ERROR; } for (int32 i = 0; i < reply.count; i++) { out_live_nodes[i] = live_info[i]; } delete_area(clone); delete_area(reply.area); } else { for (int32 i = 0; i < reply.count; i++) { out_live_nodes[i] = reply.live_info[i]; } } *io_total_count = reply.count; return B_OK; } status_t BMediaRoster::GetFreeInputsFor(const media_node & node, media_input * out_free_inputs, int32 buf_num_inputs, int32 * out_total_count, media_type filter_type) { CALLED(); if (IS_INVALID_NODE(node)) { ERROR("BMediaRoster::GetFreeInputsFor: node %ld, port %ld invalid\n", node.node, node.port); return B_MEDIA_BAD_NODE; } if ((node.kind & B_BUFFER_CONSUMER) == 0) { ERROR("BMediaRoster::GetFreeInputsFor: node %ld, port %ld is not a consumer\n", node.node, node.port); return B_MEDIA_BAD_NODE; } if (out_free_inputs == NULL || out_total_count == NULL) return B_BAD_VALUE; List list; media_input *input; status_t rv; *out_total_count = 0; rv = MediaRosterEx(this)->GetAllInputs(node, &list); if (B_OK != rv) return rv; PRINT(4, "BMediaRoster::GetFreeInputsFor node %ld, max %ld, filter-type %ld\n", node.node, buf_num_inputs, filter_type); int32 i; for (i = 0, list.Rewind(); list.GetNext(&input);) { if (filter_type != B_MEDIA_UNKNOWN_TYPE && filter_type != input->format.type) continue; // media_type used, but doesn't match if (input->source != media_source::null) continue; // consumer source already connected out_free_inputs[i] = *input; *out_total_count += 1; buf_num_inputs -= 1; #if DEBUG >= 3 PRINT_OUTPUT(" input", out_free_inputs[i]); #endif if (buf_num_inputs == 0) break; i++; } MediaRosterEx(this)->PublishInputs(node, &list); return B_OK; } status_t BMediaRoster::GetConnectedInputsFor(const media_node & node, media_input * out_active_inputs, int32 buf_num_inputs, int32 * out_total_count) { CALLED(); if (IS_INVALID_NODE(node) || (node.kind & B_BUFFER_CONSUMER) == 0) return B_MEDIA_BAD_NODE; if (out_active_inputs == NULL || out_total_count == NULL) return B_BAD_VALUE; List list; media_input *input; status_t rv; *out_total_count = 0; rv = MediaRosterEx(this)->GetAllInputs(node, &list); if (B_OK != rv) return rv; PRINT(4, "BMediaRoster::GetConnectedInputsFor node %ld, max %ld\n", node.node, buf_num_inputs); int32 i; for (i = 0, list.Rewind(); list.GetNext(&input);) { if (input->source == media_source::null) continue; // consumer source not connected out_active_inputs[i] = *input; *out_total_count += 1; buf_num_inputs -= 1; #if DEBUG >= 3 PRINT_OUTPUT(" input ", out_active_inputs[i]); #endif if (buf_num_inputs == 0) break; i++; } MediaRosterEx(this)->PublishInputs(node, &list); return B_OK; } status_t BMediaRoster::GetAllInputsFor(const media_node & node, media_input * out_inputs, int32 buf_num_inputs, int32 * out_total_count) { CALLED(); if (IS_INVALID_NODE(node) || (node.kind & B_BUFFER_CONSUMER) == 0) return B_MEDIA_BAD_NODE; if (out_inputs == NULL || out_total_count == NULL) return B_BAD_VALUE; List list; media_input *input; status_t rv; *out_total_count = 0; rv = MediaRosterEx(this)->GetAllInputs(node, &list); if (B_OK != rv) return rv; PRINT(4, "BMediaRoster::GetAllInputsFor node %ld, max %ld\n", node.node, buf_num_inputs); int32 i; for (i = 0, list.Rewind(); list.GetNext(&input); i++) { out_inputs[i] = *input; *out_total_count += 1; buf_num_inputs -= 1; #if DEBUG >= 3 PRINT_OUTPUT(" input ", out_inputs[i]); #endif if (buf_num_inputs == 0) break; } MediaRosterEx(this)->PublishInputs(node, &list); return B_OK; } status_t BMediaRoster::GetFreeOutputsFor(const media_node & node, media_output * out_free_outputs, int32 buf_num_outputs, int32 * out_total_count, media_type filter_type) { CALLED(); if (IS_INVALID_NODE(node) || (node.kind & B_BUFFER_PRODUCER) == 0) return B_MEDIA_BAD_NODE; if (out_free_outputs == NULL || out_total_count == NULL) return B_BAD_VALUE; List list; media_output *output; status_t rv; *out_total_count = 0; rv = MediaRosterEx(this)->GetAllOutputs(node, &list); if (B_OK != rv) return rv; PRINT(4, "BMediaRoster::GetFreeOutputsFor node %ld, max %ld, filter-type %ld\n", node.node, buf_num_outputs, filter_type); int32 i; for (i = 0, list.Rewind(); list.GetNext(&output);) { if (filter_type != B_MEDIA_UNKNOWN_TYPE && filter_type != output->format.type) continue; // media_type used, but doesn't match if (output->destination != media_destination::null) continue; // producer destination already connected out_free_outputs[i] = *output; *out_total_count += 1; buf_num_outputs -= 1; #if DEBUG >= 3 PRINT_OUTPUT(" output ", out_free_outputs[i]); #endif if (buf_num_outputs == 0) break; i++; } MediaRosterEx(this)->PublishOutputs(node, &list); return B_OK; } status_t BMediaRoster::GetConnectedOutputsFor(const media_node & node, media_output * out_active_outputs, int32 buf_num_outputs, int32 * out_total_count) { CALLED(); if (IS_INVALID_NODE(node) || (node.kind & B_BUFFER_PRODUCER) == 0) return B_MEDIA_BAD_NODE; if (out_active_outputs == NULL || out_total_count == NULL) return B_BAD_VALUE; List list; media_output *output; status_t rv; *out_total_count = 0; rv = MediaRosterEx(this)->GetAllOutputs(node, &list); if (B_OK != rv) return rv; PRINT(4, "BMediaRoster::GetConnectedOutputsFor node %ld, max %ld\n", node.node, buf_num_outputs); int32 i; for (i = 0, list.Rewind(); list.GetNext(&output);) { if (output->destination == media_destination::null) continue; // producer destination not connected out_active_outputs[i] = *output; *out_total_count += 1; buf_num_outputs -= 1; #if DEBUG >= 3 PRINT_OUTPUT(" output ", out_active_outputs[i]); #endif if (buf_num_outputs == 0) break; i++; } MediaRosterEx(this)->PublishOutputs(node, &list); return B_OK; } status_t BMediaRoster::GetAllOutputsFor(const media_node & node, media_output * out_outputs, int32 buf_num_outputs, int32 * out_total_count) { CALLED(); if (IS_INVALID_NODE(node) || (node.kind & B_BUFFER_PRODUCER) == 0) return B_MEDIA_BAD_NODE; if (out_outputs == NULL || out_total_count == NULL) return B_BAD_VALUE; List list; media_output *output; status_t rv; *out_total_count = 0; rv = MediaRosterEx(this)->GetAllOutputs(node, &list); if (B_OK != rv) return rv; PRINT(4, "BMediaRoster::GetAllOutputsFor node %ld, max %ld\n", node.node, buf_num_outputs); int32 i; for (i = 0, list.Rewind(); list.GetNext(&output); i++) { out_outputs[i] = *output; *out_total_count += 1; buf_num_outputs -= 1; #if DEBUG >= 3 PRINT_OUTPUT(" output ", out_outputs[i]); #endif if (buf_num_outputs == 0) break; } MediaRosterEx(this)->PublishOutputs(node, &list); return B_OK; } status_t BMediaRoster::StartWatching(const BMessenger & where) { CALLED(); if (!where.IsValid()) { ERROR("BMediaRoster::StartWatching: messenger invalid!\n"); return B_BAD_VALUE; } return BPrivate::media::notifications::Register(where, media_node::null, B_MEDIA_WILDCARD); } status_t BMediaRoster::StartWatching(const BMessenger & where, int32 notificationType) { CALLED(); if (!where.IsValid()) { ERROR("BMediaRoster::StartWatching: messenger invalid!\n"); return B_BAD_VALUE; } if (false == BPrivate::media::notifications::IsValidNotificationRequest(false, notificationType)) { ERROR("BMediaRoster::StartWatching: notificationType invalid!\n"); return B_BAD_VALUE; } return BPrivate::media::notifications::Register(where, media_node::null, notificationType); } status_t BMediaRoster::StartWatching(const BMessenger & where, const media_node & node, int32 notificationType) { CALLED(); if (!where.IsValid()) { ERROR("BMediaRoster::StartWatching: messenger invalid!\n"); return B_BAD_VALUE; } if (IS_INVALID_NODE(node)) { ERROR("BMediaRoster::StartWatching: node invalid!\n"); return B_MEDIA_BAD_NODE; } if (false == BPrivate::media::notifications::IsValidNotificationRequest(true, notificationType)) { ERROR("BMediaRoster::StartWatching: notificationType invalid!\n"); return B_BAD_VALUE; } return BPrivate::media::notifications::Register(where, node, notificationType); } status_t BMediaRoster::StopWatching(const BMessenger & where) { CALLED(); // messenger may already be invalid, so we don't check this return BPrivate::media::notifications::Unregister(where, media_node::null, B_MEDIA_WILDCARD); } status_t BMediaRoster::StopWatching(const BMessenger & where, int32 notificationType) { CALLED(); // messenger may already be invalid, so we don't check this if (false == BPrivate::media::notifications::IsValidNotificationRequest(false, notificationType)) { ERROR("BMediaRoster::StopWatching: notificationType invalid!\n"); return B_BAD_VALUE; } return BPrivate::media::notifications::Unregister(where, media_node::null, notificationType); } status_t BMediaRoster::StopWatching(const BMessenger & where, const media_node & node, int32 notificationType) { CALLED(); // messenger may already be invalid, so we don't check this if (IS_INVALID_NODE(node)) { ERROR("BMediaRoster::StopWatching: node invalid!\n"); return B_MEDIA_BAD_NODE; } if (false == BPrivate::media::notifications::IsValidNotificationRequest(true, notificationType)) { ERROR("BMediaRoster::StopWatching: notificationType invalid!\n"); return B_BAD_VALUE; } return BPrivate::media::notifications::Unregister(where, node, notificationType); } status_t BMediaRoster::RegisterNode(BMediaNode * node) { CALLED(); // addon-id = -1 (unused), addon-flavor-id = 0 (unused, too) return MediaRosterEx(this)->RegisterNode(node, -1, 0); } status_t BMediaRosterEx::RegisterNode(BMediaNode * node, media_addon_id addonid, int32 flavorid) { CALLED(); if (node == NULL) return B_BAD_VALUE; // some sanity check // I'm not sure if the media kit warrants to call BMediaNode::AddOn() here. // Perhaps we don't need it. { BMediaAddOn *addon; int32 addon_flavor_id; media_addon_id addon_id; addon_flavor_id = 0; addon = node->AddOn(&addon_flavor_id); addon_id = addon ? addon->AddonID() : -1; ASSERT(addonid == addon_id); ASSERT(flavorid == addon_flavor_id); } status_t rv; server_register_node_request request; server_register_node_reply reply; request.addon_id = addonid; request.addon_flavor_id = flavorid; strcpy(request.name, node->Name()); request.kinds = node->Kinds(); request.port = node->ControlPort(); request.team = team; TRACE("BMediaRoster::RegisterNode: sending SERVER_REGISTER_NODE: port %ld, kinds 0x%Lx, team %ld, name '%s'\n", request.port, request.kinds, request.team, request.name); rv = QueryServer(SERVER_REGISTER_NODE, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) { ERROR("BMediaRoster::RegisterNode: failed to register node %s (error %#lx)\n", node->Name(), rv); return rv; } TRACE("BMediaRoster::RegisterNode: QueryServer SERVER_REGISTER_NODE finished\n"); // we are a friend class of BMediaNode and initialize this member variable node->fNodeID = reply.nodeid; ASSERT(reply.nodeid == node->Node().node); ASSERT(reply.nodeid == node->ID()); // call the callback node->NodeRegistered(); TRACE("BMediaRoster::RegisterNode: NodeRegistered callback finished\n"); // if the BMediaNode also inherits from BTimeSource, we need to call BTimeSource::FinishCreate() if (node->Kinds() & B_TIME_SOURCE) { BTimeSource *ts; ts = dynamic_cast(node); if (ts) ts->FinishCreate(); } TRACE("BMediaRoster::RegisterNode: publishing inputs/outputs\n"); // register existing inputs and outputs with the // media_server, this allows GetLiveNodes() to work // with created, but unconnected nodes. // The node control loop might not be running, or might deadlock // if we send a message and wait for a reply here. // We have a pointer to the node, and thus call the functions directly if (node->Kinds() & B_BUFFER_PRODUCER) { BBufferProducer *bp; bp = dynamic_cast(node); if (bp) { List list; if (B_OK == GetAllOutputs(bp, &list)) PublishOutputs(node->Node(), &list); } } if (node->Kinds() & B_BUFFER_CONSUMER) { BBufferConsumer *bc; bc = dynamic_cast(node); if (bc) { List list; if (B_OK == GetAllInputs(bc, &list)) PublishInputs(node->Node(), &list); } } TRACE("BMediaRoster::RegisterNode: sending NodesCreated\n"); BPrivate::media::notifications::NodesCreated(&reply.nodeid, 1); TRACE("BMediaRoster::RegisterNode: finished\n"); /* TRACE("BMediaRoster::RegisterNode: registered node name '%s', id %ld, addon %ld, flavor %ld\n", node->Name(), node->ID(), addon_id, addon_flavor_id); TRACE("BMediaRoster::RegisterNode: node this %p\n", node); TRACE("BMediaRoster::RegisterNode: node fConsumerThis %p\n", node->fConsumerThis); TRACE("BMediaRoster::RegisterNode: node fProducerThis %p\n", node->fProducerThis); TRACE("BMediaRoster::RegisterNode: node fFileInterfaceThis %p\n", node->fFileInterfaceThis); TRACE("BMediaRoster::RegisterNode: node fControllableThis %p\n", node->fControllableThis); TRACE("BMediaRoster::RegisterNode: node fTimeSourceThis %p\n", node->fTimeSourceThis); */ return B_OK; } status_t BMediaRoster::UnregisterNode(BMediaNode * node) { CALLED(); if (node == NULL) return B_BAD_VALUE; TRACE("BMediaRoster::UnregisterNode %ld (%p)\n", node->ID(), node); if (node->fKinds & NODE_KIND_NO_REFCOUNTING) { printf("BMediaRoster::UnregisterNode, trying to unregister reference counting disabled timesource, node %ld, port %ld, team %ld\n", node->ID(), node->ControlPort(), team); return B_OK; } if (node->ID() == NODE_UNREGISTERED_ID) { PRINT(1, "Warning: BMediaRoster::UnregisterNode: node id %ld, name '%s' already unregistered\n", node->ID(), node->Name()); return B_OK; } if (node->fRefCount != 0) { PRINT(1, "Warning: BMediaRoster::UnregisterNode: node id %ld, name '%s' has local reference count of %ld\n", node->ID(), node->Name(), node->fRefCount); // no return here, we continue and unregister! } // Calling BMediaAddOn::GetConfigurationFor(BMediaNode *node, BMessage *config) // if this node was instanciated by an add-on needs to be done *somewhere* // We can't do it here because it is already to late (destructor of the node // might have been called). server_unregister_node_request request; server_unregister_node_reply reply; status_t rv; request.nodeid = node->ID(); request.team = team; // send a notification BPrivate::media::notifications::NodesDeleted(&request.nodeid, 1); rv = QueryServer(SERVER_UNREGISTER_NODE, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) { ERROR("BMediaRoster::UnregisterNode: failed to unregister node id %ld, name '%s' (error %#lx)\n", node->ID(), node->Name(), rv); return rv; } if (reply.addonid != -1) { // Small problem here, we can't use DormantNodeManager::PutAddon(), as // UnregisterNode() is called by a dormant node itself (by the destructor). // The add-on that contains the node needs to remain in memory until the // destructor execution is finished. // DormantNodeManager::PutAddonDelayed() will delay unloading. _DormantNodeManager->PutAddonDelayed(reply.addonid); rv = MediaRosterEx(this)->DecrementAddonFlavorInstancesCount(reply.addonid, reply.flavorid); if (rv != B_OK) { ERROR("BMediaRoster::UnregisterNode: DecrementAddonFlavorInstancesCount failed\n"); // this is really a problem, but we can't fail now } } // we are a friend class of BMediaNode and invalidate this member variable node->fNodeID = NODE_UNREGISTERED_ID; return B_OK; } // thread safe for multiple calls to Roster() /* static */ BMediaRoster * BMediaRoster::Roster(status_t *out_error) { static BLocker locker("BMediaRoster::Roster locker"); locker.Lock(); if (out_error) *out_error = B_OK; if (_sDefault == NULL) { status_t err; _sDefault = new BMediaRosterEx(&err); if (err != B_OK) { if (_sDefault) { _sDefault->Lock(); _sDefault->Quit(); _sDefault = NULL; } if (out_error) *out_error = err; } } locker.Unlock(); return _sDefault; } // won't create it if there isn't one // not thread safe if you call Roster() at the same time /* static */ BMediaRoster * BMediaRoster::CurrentRoster() { return _sDefault; } status_t BMediaRoster::SetTimeSourceFor(media_node_id node, media_node_id time_source) { CALLED(); if (IS_INVALID_NODEID(node) || IS_INVALID_NODEID(time_source)) return B_BAD_VALUE; media_node clone; status_t rv, result; TRACE("BMediaRoster::SetTimeSourceFor: node %ld will be assigned time source %ld\n", node, time_source); printf("BMediaRoster::SetTimeSourceFor: node %ld time source %ld enter\n", node, time_source); // we need to get a clone of the node to have a port id rv = GetNodeFor(node, &clone); if (rv != B_OK) { ERROR("BMediaRoster::SetTimeSourceFor, GetNodeFor failed, node id %ld\n", node); return B_ERROR; } // we just send the request to set time_source-id as timesource to the node, // the NODE_SET_TIMESOURCE handler code will do the real assignment result = B_OK; node_set_timesource_command cmd; cmd.timesource_id = time_source; rv = SendToPort(clone.port, NODE_SET_TIMESOURCE, &cmd, sizeof(cmd)); if (rv != B_OK) { ERROR("BMediaRoster::SetTimeSourceFor, sending NODE_SET_TIMESOURCE failed, node id %ld\n", node); result = B_ERROR; } // we release the clone rv = ReleaseNode(clone); if (rv != B_OK) { ERROR("BMediaRoster::SetTimeSourceFor, ReleaseNode failed, node id %ld\n", node); result = B_ERROR; } printf("BMediaRoster::SetTimeSourceFor: node %ld time source %ld leave\n", node, time_source); return result; } status_t BMediaRoster::GetParameterWebFor(const media_node & node, BParameterWeb ** out_web) { CALLED(); if (out_web == NULL) return B_BAD_VALUE; if (IS_INVALID_NODE(node)) return B_MEDIA_BAD_NODE; if ((node.kind & B_CONTROLLABLE) == 0) return B_MEDIA_BAD_NODE; controllable_get_parameter_web_request request; controllable_get_parameter_web_reply reply; int32 requestsize[] = {B_PAGE_SIZE, 4*B_PAGE_SIZE, 16*B_PAGE_SIZE, 64*B_PAGE_SIZE, 128*B_PAGE_SIZE, 256*B_PAGE_SIZE, 0}; int32 size; // XXX it might be better to query the node for the (current) parameter size first for (int i = 0; (size = requestsize[i]) != 0; i++) { status_t rv; area_id area; void *data; area = create_area("parameter web data", &data, B_ANY_ADDRESS, size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); if (area < B_OK) { ERROR("BMediaRoster::GetParameterWebFor couldn't create area of size %ld\n", size); return B_ERROR; } request.maxsize = size; request.area = area; rv = QueryPort(node.port, CONTROLLABLE_GET_PARAMETER_WEB, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) { ERROR("BMediaRoster::GetParameterWebFor CONTROLLABLE_GET_PARAMETER_WEB failed\n"); delete_area(area); return B_ERROR; } if (reply.size == 0) { // no parameter web available // XXX should we return an error? ERROR("BMediaRoster::GetParameterWebFor node %ld has no parameter web\n", node.node); *out_web = new BParameterWeb(); delete_area(area); return B_OK; } if (reply.size > 0) { // we got a flattened parameter web! *out_web = new BParameterWeb(); printf("BMediaRoster::GetParameterWebFor Unflattening %ld bytes, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx\n", reply.size, ((uint32*)data)[0], ((uint32*)data)[1], ((uint32*)data)[2], ((uint32*)data)[3]); rv = (*out_web)->Unflatten(reply.code, data, reply.size); if (rv != B_OK) { ERROR("BMediaRoster::GetParameterWebFor Unflatten failed, %s\n", strerror(rv)); delete_area(area); delete *out_web; return B_ERROR; } delete_area(area); return B_OK; } delete_area(area); ASSERT(reply.size == -1); // parameter web data was too large // loop and try a larger size } ERROR("BMediaRoster::GetParameterWebFor node %ld has no parameter web larger than %ld\n", node.node, size); return B_ERROR; } status_t BMediaRoster::StartControlPanel(const media_node & node, BMessenger * out_messenger) { UNIMPLEMENTED(); return B_ERROR; } status_t BMediaRoster::GetDormantNodes(dormant_node_info * out_info, int32 * io_count, const media_format * has_input /* = NULL */, const media_format * has_output /* = NULL */, const char * name /* = NULL */, uint64 require_kinds /* = NULL */, uint64 deny_kinds /* = NULL */) { CALLED(); if (out_info == NULL) return B_BAD_VALUE; if (io_count == NULL) return B_BAD_VALUE; if (*io_count <= 0) return B_BAD_VALUE; xfer_server_get_dormant_nodes msg; port_id port; status_t rv; port = find_port(MEDIA_SERVER_PORT_NAME); if (port <= B_OK) return B_ERROR; msg.maxcount = *io_count; msg.has_input = (bool) has_input; if (has_input) msg.inputformat = *has_input; // XXX we should not make a flat copy of media_format msg.has_output = (bool) has_output; if (has_output) msg.outputformat = *has_output;; // XXX we should not make a flat copy of media_format msg.has_name = (bool) name; if (name) { int len = strlen(name); len = min_c(len, (int)sizeof(msg.name) - 1); memcpy(msg.name, name, len); msg.name[len] = 0; } msg.require_kinds = require_kinds; msg.deny_kinds = deny_kinds; msg.reply_port = _PortPool->GetPort(); rv = write_port(port, SERVER_GET_DORMANT_NODES, &msg, sizeof(msg)); if (rv != B_OK) { _PortPool->PutPort(msg.reply_port); return rv; } xfer_server_get_dormant_nodes_reply reply; int32 code; rv = read_port(msg.reply_port, &code, &reply, sizeof(reply)); if (rv < B_OK) { _PortPool->PutPort(msg.reply_port); return rv; } *io_count = reply.count; if (*io_count > 0) { rv = read_port(msg.reply_port, &code, out_info, *io_count * sizeof(dormant_node_info)); if (rv < B_OK) reply.result = rv; } _PortPool->PutPort(msg.reply_port); return reply.result; } /* This function is used to do the real work of instantiating a dormant node. It is either * called by the media_addon_server to instantiate a global node, or it gets called from * BMediaRoster::InstantiateDormantNode() to create a local one. * * Checks concerning global/local are not done here. */ status_t BMediaRosterEx::InstantiateDormantNode(media_addon_id addonid, int32 flavorid, team_id creator, media_node *out_node) { // This function is always called from the correct context, if the node // is supposed to be global, it is called from the media_addon_server. // if B_FLAVOR_IS_GLOBAL, we need to use the BMediaAddOn object that // resides in the media_addon_server // RegisterNode() must be called for nodes instantiated from add-ons, // since the media kit warrants that it's done automatically. // addonid Indicates the ID number of the media add-on in which the node resides. // flavorid Indicates the internal ID number that the add-on uses to identify the flavor, // this is the number that was published by BMediaAddOn::GetFlavorAt() in the // flavor_info::internal_id field. // creator The creator team is -1 if nodes are created locally. If created globally, // it will contain (while called in media_addon_server context) the team-id of // the team that requested the instantiation. TRACE("BMediaRosterEx::InstantiateDormantNode: addon-id %ld, flavor_id %ld\n", addonid, flavorid); // Get flavor_info from the server dormant_flavor_info node_info; status_t rv; rv = GetDormantFlavorInfo(addonid, flavorid, &node_info); if (rv != B_OK) { ERROR("BMediaRosterEx::InstantiateDormantNode error: failed to get dormant_flavor_info for addon-id %ld, flavor-id %ld\n", addonid, flavorid); return B_ERROR; } //ASSERT(node_info.internal_id == flavorid); if (node_info.internal_id != flavorid) { ERROR("############# BMediaRosterEx::InstantiateDormantNode failed: ID mismatch for addon-id %ld, flavor-id %ld, node_info.internal_id %ld, node_info.name %s\n", addonid, flavorid, node_info.internal_id, node_info.name); return B_ERROR; } // load the BMediaAddOn object BMediaAddOn *addon; addon = _DormantNodeManager->GetAddon(addonid); if (!addon) { ERROR("BMediaRosterEx::InstantiateDormantNode: GetAddon failed\n"); return B_ERROR; } // Now we need to try to increment the use count of this addon flavor // in the server. This can fail if the total number instances of this // flavor is limited. rv = IncrementAddonFlavorInstancesCount(addonid, flavorid); if (rv != B_OK) { ERROR("BMediaRosterEx::InstantiateDormantNode error: can't create more nodes for addon-id %ld, flavor-id %ld\n", addonid, flavorid); // Put the addon back into the pool _DormantNodeManager->PutAddon(addonid); return B_ERROR; } BMessage config; rv = LoadNodeConfiguration(addonid, flavorid, &config); if (rv != B_OK) { ERROR("BMediaRosterEx::InstantiateDormantNode: couldn't load configuration for addon-id %ld, flavor-id %ld\n", addonid, flavorid); // do not return, this is a minor problem, not a reason to fail } BMediaNode *node; status_t out_error; out_error = B_OK; node = addon->InstantiateNodeFor(&node_info, &config, &out_error); if (!node) { ERROR("BMediaRosterEx::InstantiateDormantNode: InstantiateNodeFor failed\n"); // Put the addon back into the pool _DormantNodeManager->PutAddon(addonid); // We must decrement the use count of this addon flavor in the // server to compensate the increment done in the beginning. rv = DecrementAddonFlavorInstancesCount(addonid, flavorid); if (rv != B_OK) { ERROR("BMediaRosterEx::InstantiateDormantNode: DecrementAddonFlavorInstancesCount failed\n"); } return (out_error != B_OK) ? out_error : B_ERROR; } rv = RegisterNode(node, addonid, flavorid); if (rv != B_OK) { ERROR("BMediaRosterEx::InstantiateDormantNode: RegisterNode failed\n"); delete node; // Put the addon back into the pool _DormantNodeManager->PutAddon(addonid); // We must decrement the use count of this addon flavor in the // server to compensate the increment done in the beginning. rv = DecrementAddonFlavorInstancesCount(addonid, flavorid); if (rv != B_OK) { ERROR("BMediaRosterEx::InstantiateDormantNode: DecrementAddonFlavorInstancesCount failed\n"); } return B_ERROR; } if (creator != -1) { // send a message to the server to assign team "creator" as creator of node "node->ID()" printf("!!! BMediaRosterEx::InstantiateDormantNode assigning team %ld as creator of node %ld\n", creator, node->ID()); rv = MediaRosterEx(this)->SetNodeCreator(node->ID(), creator); if (rv != B_OK) { ERROR("BMediaRosterEx::InstantiateDormantNode failed to assign team %ld as creator of node %ld\n", creator, node->ID()); // do not return, this is a minor problem, not a reason to fail } } // RegisterNode() does remember the add-on id in the server // and UnregisterNode() will call DormantNodeManager::PutAddon() // when the node is unregistered. *out_node = node->Node(); TRACE("BMediaRosterEx::InstantiateDormantNode: addon-id %ld, flavor_id %ld instanciated as node %ld, port %ld in team %ld\n", addonid, flavorid, out_node->node, out_node->port, team); return B_OK; } status_t BMediaRoster::InstantiateDormantNode(const dormant_node_info & in_info, media_node * out_node, uint32 flags /* currently 0 or B_FLAVOR_IS_GLOBAL or B_FLAVOR_IS_LOCAL */ ) { CALLED(); if (out_node == 0) return B_BAD_VALUE; if (in_info.addon <= 0) { ERROR("BMediaRoster::InstantiateDormantNode error: addon-id %ld invalid.\n", in_info.addon); return B_BAD_VALUE; } printf("BMediaRoster::InstantiateDormantNode: addon-id %ld, flavor_id %ld, flags 0x%lX\n", in_info.addon, in_info.flavor_id, flags); // Get flavor_info from the server // XXX this is a little overhead, as we get the full blown dormant_flavor_info, // XXX but only need the flags. dormant_flavor_info node_info; status_t rv; rv = MediaRosterEx(this)->GetDormantFlavorInfo(in_info.addon, in_info.flavor_id, &node_info); if (rv != B_OK) { ERROR("BMediaRoster::InstantiateDormantNode: failed to get dormant_flavor_info for addon-id %ld, flavor-id %ld\n", in_info.addon, in_info.flavor_id); return B_NAME_NOT_FOUND; } ASSERT(node_info.internal_id == in_info.flavor_id); printf("BMediaRoster::InstantiateDormantNode: name \"%s\", info \"%s\", flavor_flags 0x%lX, internal_id %ld, possible_count %ld\n", node_info.name, node_info.info, node_info.flavor_flags, node_info.internal_id, node_info.possible_count); #if DEBUG if (flags & B_FLAVOR_IS_LOCAL) printf("BMediaRoster::InstantiateDormantNode: caller requested B_FLAVOR_IS_LOCAL\n"); if (flags & B_FLAVOR_IS_GLOBAL) printf("BMediaRoster::InstantiateDormantNode: caller requested B_FLAVOR_IS_GLOBAL\n"); if (node_info.flavor_flags & B_FLAVOR_IS_LOCAL) printf("BMediaRoster::InstantiateDormantNode: node requires B_FLAVOR_IS_LOCAL\n"); if (node_info.flavor_flags & B_FLAVOR_IS_GLOBAL) printf("BMediaRoster::InstantiateDormantNode: node requires B_FLAVOR_IS_GLOBAL\n"); #endif // Make sure that flags demanded by the dormant node and those requested // by the caller are not incompatible. if ((node_info.flavor_flags & B_FLAVOR_IS_GLOBAL) && (flags & B_FLAVOR_IS_LOCAL)) { ERROR("BMediaRoster::InstantiateDormantNode: requested B_FLAVOR_IS_LOCAL, but dormant node has B_FLAVOR_IS_GLOBAL\n"); return B_NAME_NOT_FOUND; } if ((node_info.flavor_flags & B_FLAVOR_IS_LOCAL) && (flags & B_FLAVOR_IS_GLOBAL)) { ERROR("BMediaRoster::InstantiateDormantNode: requested B_FLAVOR_IS_GLOBAL, but dormant node has B_FLAVOR_IS_LOCAL\n"); return B_NAME_NOT_FOUND; } // If either the node, or the caller requested to make the instance global // we will do it by forwarding this request into the media_addon_server, which // in turn will call BMediaRosterEx::InstantiateDormantNode to create the node // there and make it globally available. if ((node_info.flavor_flags & B_FLAVOR_IS_GLOBAL) || (flags & B_FLAVOR_IS_GLOBAL)) { printf("BMediaRoster::InstantiateDormantNode: creating global object in media_addon_server\n"); addonserver_instantiate_dormant_node_request request; addonserver_instantiate_dormant_node_reply reply; request.addonid = in_info.addon; request.flavorid = in_info.flavor_id; request.creator_team = team; // creator team is allowed to also release global nodes rv = QueryAddonServer(ADDONSERVER_INSTANTIATE_DORMANT_NODE, &request, sizeof(request), &reply, sizeof(reply)); if (rv == B_OK) { *out_node = reply.node; } } else { // creator team = -1, as this is a local node rv = MediaRosterEx(this)->InstantiateDormantNode(in_info.addon, in_info.flavor_id, -1, out_node); } if (rv != B_OK) { *out_node = media_node::null; return B_NAME_NOT_FOUND; } return B_OK; } status_t BMediaRoster::InstantiateDormantNode(const dormant_node_info & in_info, media_node * out_node) { return InstantiateDormantNode(in_info, out_node, 0); } status_t BMediaRoster::GetDormantNodeFor(const media_node & node, dormant_node_info * out_info) { CALLED(); if (out_info == NULL) return B_BAD_VALUE; if (IS_INVALID_NODE(node)) return B_MEDIA_BAD_NODE; server_get_dormant_node_for_request request; server_get_dormant_node_for_reply reply; status_t rv; request.node = node; rv = QueryServer(SERVER_GET_DORMANT_NODE_FOR, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) return rv; *out_info = reply.node_info; return B_OK; } status_t BMediaRosterEx::GetDormantFlavorInfo(media_addon_id addonid, int32 flavorid, dormant_flavor_info * out_flavor) { CALLED(); if (out_flavor == NULL) return B_BAD_VALUE; xfer_server_get_dormant_flavor_info msg; xfer_server_get_dormant_flavor_info_reply *reply; port_id port; status_t rv; int32 code; port = find_port(MEDIA_SERVER_PORT_NAME); if (port <= B_OK) return B_ERROR; reply = (xfer_server_get_dormant_flavor_info_reply *) malloc(16000); if (reply == 0) return B_ERROR; msg.addon = addonid; msg.flavor_id = flavorid; msg.reply_port = _PortPool->GetPort(); rv = write_port(port, SERVER_GET_DORMANT_FLAVOR_INFO, &msg, sizeof(msg)); if (rv != B_OK) { free(reply); _PortPool->PutPort(msg.reply_port); return rv; } rv = read_port(msg.reply_port, &code, reply, 16000); _PortPool->PutPort(msg.reply_port); if (rv < B_OK) { free(reply); return rv; } if (reply->result == B_OK) rv = out_flavor->Unflatten(reply->dfi_type, &reply->dfi, reply->dfi_size); else rv = reply->result; free(reply); return rv; } status_t BMediaRoster::GetDormantFlavorInfoFor(const dormant_node_info & in_dormant, dormant_flavor_info * out_flavor) { return MediaRosterEx(this)->GetDormantFlavorInfo(in_dormant.addon, in_dormant.flavor_id, out_flavor); } // Reports in outLatency the maximum latency found downstream from // the specified BBufferProducer, producer, given the current connections. status_t BMediaRoster::GetLatencyFor(const media_node & producer, bigtime_t * out_latency) { CALLED(); if (out_latency == NULL) return B_BAD_VALUE; if (IS_INVALID_NODE(producer)) return B_MEDIA_BAD_NODE; if ((producer.kind & B_BUFFER_PRODUCER) == 0) return B_MEDIA_BAD_NODE; producer_get_latency_request request; producer_get_latency_reply reply; status_t rv; rv = QueryPort(producer.port, PRODUCER_GET_LATENCY, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) return rv; *out_latency = reply.latency; // printf("BMediaRoster::GetLatencyFor producer %ld has maximum latency %Ld\n", producer.node, *out_latency); return B_OK; } status_t BMediaRoster::GetInitialLatencyFor(const media_node & producer, bigtime_t * out_latency, uint32 * out_flags /* = NULL */) { CALLED(); if (out_latency == NULL) return B_BAD_VALUE; if (IS_INVALID_NODE(producer)) return B_MEDIA_BAD_NODE; if ((producer.kind & B_BUFFER_PRODUCER) == 0) return B_MEDIA_BAD_NODE; producer_get_initial_latency_request request; producer_get_initial_latency_reply reply; status_t rv; rv = QueryPort(producer.port, PRODUCER_GET_INITIAL_LATENCY, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) return rv; *out_latency = reply.initial_latency; if (out_flags) *out_flags = reply.flags; printf("BMediaRoster::GetInitialLatencyFor producer %ld has maximum initial latency %Ld\n", producer.node, *out_latency); return B_OK; } status_t BMediaRoster::GetStartLatencyFor(const media_node & time_source, bigtime_t * out_latency) { CALLED(); if (out_latency == NULL) return B_BAD_VALUE; if (IS_INVALID_NODE(time_source)) return B_MEDIA_BAD_NODE; if ((time_source.kind & B_TIME_SOURCE) == 0) return B_MEDIA_BAD_NODE; timesource_get_start_latency_request request; timesource_get_start_latency_reply reply; status_t rv; rv = QueryPort(time_source.port, TIMESOURCE_GET_START_LATENCY, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) return rv; *out_latency = reply.start_latency; printf("BMediaRoster::GetStartLatencyFor timesource %ld has maximum initial latency %Ld\n", time_source.node, *out_latency); return B_OK; } status_t BMediaRoster::GetFileFormatsFor(const media_node & file_interface, media_file_format * out_formats, int32 * io_num_infos) { UNIMPLEMENTED(); return B_ERROR; } status_t BMediaRoster::SetRefFor(const media_node & file_interface, const entry_ref & file, bool create_and_truncate, bigtime_t * out_length) /* if create is false */ { UNIMPLEMENTED(); return B_ERROR; } status_t BMediaRoster::GetRefFor(const media_node & node, entry_ref * out_file, BMimeType * mime_type) { UNIMPLEMENTED(); return B_ERROR; } status_t BMediaRoster::SniffRefFor(const media_node & file_interface, const entry_ref & file, BMimeType * mime_type, float * out_capability) { UNIMPLEMENTED(); return B_ERROR; } /* This is the generic "here's a file, now can someone please play it" interface */ status_t BMediaRoster::SniffRef(const entry_ref & file, uint64 require_node_kinds, /* if you need an EntityInterface or BufferConsumer or something */ dormant_node_info * out_node, BMimeType * mime_type) { UNIMPLEMENTED(); return B_ERROR; } status_t BMediaRoster::GetDormantNodeForType(const BMimeType & type, uint64 require_node_kinds, dormant_node_info * out_node) { UNIMPLEMENTED(); return B_ERROR; } status_t BMediaRoster::GetReadFileFormatsFor(const dormant_node_info & in_node, media_file_format * out_read_formats, int32 in_read_count, int32 * out_read_count) { UNIMPLEMENTED(); return B_ERROR; } status_t BMediaRoster::GetWriteFileFormatsFor(const dormant_node_info & in_node, media_file_format * out_write_formats, int32 in_write_count, int32 * out_write_count) { UNIMPLEMENTED(); return B_ERROR; } status_t BMediaRoster::GetFormatFor(const media_output & output, media_format * io_format, uint32 flags) { CALLED(); if (io_format == NULL) return B_BAD_VALUE; if ((output.node.kind & B_BUFFER_PRODUCER) == 0) return B_MEDIA_BAD_NODE; if (IS_INVALID_SOURCE(output.source)) return B_MEDIA_BAD_SOURCE; producer_format_suggestion_requested_request request; producer_format_suggestion_requested_reply reply; status_t rv; request.type = B_MEDIA_UNKNOWN_TYPE; request.quality = 0; // XXX what should this be? rv = QueryPort(output.source.port, PRODUCER_FORMAT_SUGGESTION_REQUESTED, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) return rv; *io_format = reply.format; return B_OK; } status_t BMediaRoster::GetFormatFor(const media_input & input, media_format * io_format, uint32 flags) { CALLED(); if (io_format == NULL) return B_BAD_VALUE; if ((input.node.kind & B_BUFFER_CONSUMER) == 0) return B_MEDIA_BAD_NODE; if (IS_INVALID_DESTINATION(input.destination)) return B_MEDIA_BAD_DESTINATION; consumer_accept_format_request request; consumer_accept_format_reply reply; status_t rv; request.dest = input.destination; memset(&request.format, 0, sizeof(request.format)); // wildcard rv = QueryPort(input.destination.port, CONSUMER_ACCEPT_FORMAT, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) return rv; *io_format = reply.format; return B_OK; } status_t BMediaRoster::GetFormatFor(const media_node & node, media_format * io_format, float quality) { UNIMPLEMENTED(); if (io_format == NULL) return B_BAD_VALUE; if (IS_INVALID_NODE(node)) return B_MEDIA_BAD_NODE; if ((node.kind & (B_BUFFER_CONSUMER | B_BUFFER_PRODUCER)) == 0) return B_MEDIA_BAD_NODE; return B_ERROR; } ssize_t BMediaRoster::GetNodeAttributesFor(const media_node & node, media_node_attribute * outArray, size_t inMaxCount) { UNIMPLEMENTED(); return B_ERROR; } media_node_id BMediaRoster::NodeIDFor(port_id source_or_destination_port) { CALLED(); server_node_id_for_request request; server_node_id_for_reply reply; status_t rv; request.port = source_or_destination_port; rv = QueryServer(SERVER_NODE_ID_FOR, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) { ERROR("BMediaRoster::NodeIDFor: failed (error %#lx)\n", rv); return -1; } return reply.nodeid; } status_t BMediaRoster::GetInstancesFor(media_addon_id addon, int32 flavor, media_node_id * out_id, int32 * io_count) { CALLED(); if (out_id == NULL) return B_BAD_VALUE; if (io_count && *io_count <= 0) return B_BAD_VALUE; server_get_instances_for_request request; server_get_instances_for_reply reply; status_t rv; request.maxcount = (io_count ? *io_count : 1); request.addon_id = addon; request.addon_flavor_id = flavor; rv = QueryServer(SERVER_GET_INSTANCES_FOR, &request, sizeof(request), &reply, sizeof(reply)); if (rv != B_OK) { ERROR("BMediaRoster::GetLiveNodes failed\n"); return rv; } if(io_count) *io_count = reply.count; if (reply.count > 0) memcpy(out_id, reply.node_id, sizeof(media_node_id) * reply.count); return B_OK; } status_t BMediaRoster::SetRealtimeFlags(uint32 in_enabled) { UNIMPLEMENTED(); return B_ERROR; } status_t BMediaRoster::GetRealtimeFlags(uint32 * out_enabled) { UNIMPLEMENTED(); return B_ERROR; } ssize_t BMediaRoster::AudioBufferSizeFor(int32 channel_count, uint32 sample_format, float frame_rate, bus_type bus_kind) { bigtime_t buffer_duration; ssize_t buffer_size; system_info info; get_system_info(&info); if (info.cpu_clock_speed > 2000000000) buffer_duration = 2500; else if (info.cpu_clock_speed > 1000000000) buffer_duration = 5000; else if (info.cpu_clock_speed > 600000000) buffer_duration = 10000; else if (info.cpu_clock_speed > 200000000) buffer_duration = 20000; else if (info.cpu_clock_speed > 100000000) buffer_duration = 30000; else buffer_duration = 50000; if ((bus_kind == B_ISA_BUS || bus_kind == B_PCMCIA_BUS) && buffer_duration < 25000) buffer_duration = 25000; buffer_size = (sample_format & 0xf) * channel_count * (ssize_t)((frame_rate * buffer_duration) / 1000000.0); printf("Suggested buffer duration %Ld, size %ld\n", buffer_duration, buffer_size); return buffer_size; } /* Use MediaFlags to inquire about specific features of the Media Kit. */ /* Returns < 0 for "not present", positive size for output data size. */ /* 0 means that the capability is present, but no data about it. */ /* static */ ssize_t BMediaRoster::MediaFlags(media_flags cap, void * buf, size_t maxSize) { UNIMPLEMENTED(); return 0; } /* BLooper overrides */ /* virtual */ void BMediaRoster::MessageReceived(BMessage * message) { switch (message->what) { case 'PING': { // media_server plays ping-pong with the BMediaRosters // to detect dead teams. Normal communication uses ports. static BMessage pong('PONG'); message->SendReply(&pong, static_cast(NULL), 2000000); return; } case NODE_FINAL_RELEASE: { // this function is called by a BMediaNode to delete // itself, as this needs to be done from another thread // context, it is done here. // XXX If a node is released using BMediaRoster::ReleaseNode() // XXX instead of using BMediaNode::Release() / BMediaNode::Acquire() // XXX fRefCount of the BMediaNode will not be correct. BMediaNode *node; message->FindPointer("node", reinterpret_cast(&node)); TRACE("BMediaRoster::MessageReceived NODE_FINAL_RELEASE saving node %ld configuration\n", node->ID()); MediaRosterEx(BMediaRoster::Roster())->SaveNodeConfiguration(node); TRACE("BMediaRoster::MessageReceived NODE_FINAL_RELEASE releasing node %ld\n", node->ID()); node->DeleteHook(node); // we don't call Release(), see above! return; } } printf("BMediaRoster::MessageReceived: unknown message!\n"); message->PrintToStream(); } /* virtual */ bool BMediaRoster::QuitRequested() { UNIMPLEMENTED(); return true; } /* virtual */ BHandler * BMediaRoster::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier, int32 form, const char *property) { return BLooper::ResolveSpecifier(msg, index, specifier, form, property); } /* virtual */ status_t BMediaRoster::GetSupportedSuites(BMessage *data) { return BLooper::GetSupportedSuites(data); } BMediaRoster::~BMediaRoster() { CALLED(); // unregister this application with the media server server_unregister_app_request request; server_unregister_app_reply reply; request.team = team; QueryServer(SERVER_UNREGISTER_APP, &request, sizeof(request), &reply, sizeof(reply)); } /************************************************************* * private BMediaRoster *************************************************************/ // deprecated call status_t BMediaRoster::SetOutputBuffersFor(const media_source & output, BBufferGroup * group, bool will_reclaim ) { UNIMPLEMENTED(); debugger("BMediaRoster::SetOutputBuffersFor missing\n"); return B_ERROR; } /* FBC stuffing (Mmmh, Stuffing!) */ status_t BMediaRoster::_Reserved_MediaRoster_0(void *) { return B_ERROR; } status_t BMediaRoster::_Reserved_MediaRoster_1(void *) { return B_ERROR; } status_t BMediaRoster::_Reserved_MediaRoster_2(void *) { return B_ERROR; } status_t BMediaRoster::_Reserved_MediaRoster_3(void *) { return B_ERROR; } status_t BMediaRoster::_Reserved_MediaRoster_4(void *) { return B_ERROR; } status_t BMediaRoster::_Reserved_MediaRoster_5(void *) { return B_ERROR; } status_t BMediaRoster::_Reserved_MediaRoster_6(void *) { return B_ERROR; } status_t BMediaRoster::_Reserved_MediaRoster_7(void *) { return B_ERROR; } BMediaRoster::BMediaRoster() : BLooper("_BMediaRoster_", B_URGENT_DISPLAY_PRIORITY, B_LOOPER_PORT_DEFAULT_CAPACITY) { CALLED(); // start the looper Run(); } /* static */ status_t BMediaRoster::ParseCommand(BMessage & reply) { UNIMPLEMENTED(); return B_ERROR; } status_t BMediaRoster::GetDefaultInfo(media_node_id for_default, BMessage & out_config) { UNIMPLEMENTED(); return B_ERROR; } status_t BMediaRoster::SetRunningDefault(media_node_id for_default, const media_node & node) { UNIMPLEMENTED(); return B_ERROR; } /************************************************************* * static BMediaRoster variables *************************************************************/ bool BMediaRoster::_isMediaServer; port_id BMediaRoster::_mReplyPort; int32 BMediaRoster::_mReplyPortRes; int32 BMediaRoster::_mReplyPortUnavailCount; BMediaRoster * BMediaRoster::_sDefault = NULL;