diff --git a/headers/os/media/MediaDefs.h b/headers/os/media/MediaDefs.h index 8cbce1e270..a7fa6d8db6 100644 --- a/headers/os/media/MediaDefs.h +++ b/headers/os/media/MediaDefs.h @@ -47,8 +47,10 @@ enum { B_MEDIA_NEW_PARAMETER_VALUE, /* N "node", "parameter", "when", */ /* "value" */ B_MEDIA_NODE_STOPPED, /* N "node", "when" */ - B_MEDIA_FLAVORS_CHANGED /* "be:addon_id", "be:new_count", */ + B_MEDIA_FLAVORS_CHANGED, /* "be:addon_id", "be:new_count", */ /* "be:gone_count" */ + B_MEDIA_SERVER_STARTED, + B_MEDIA_SERVER_QUIT }; diff --git a/headers/private/media/DataExchange.h b/headers/private/media/DataExchange.h index 0b5b21d663..85c50ceb5e 100644 --- a/headers/private/media/DataExchange.h +++ b/headers/private/media/DataExchange.h @@ -16,7 +16,11 @@ namespace media { namespace dataexchange { -void InitDataExchange(); +void InitServerDataExchange(); +void InitRosterDataExchange(const BMessenger& rosterMessenger); + +// BMessage based data exchange with the current BMediaRoster +status_t SendToRoster(BMessage* msg); // BMessage based data exchange with the media_server status_t SendToServer(BMessage* msg); diff --git a/headers/private/media/MediaRosterEx.h b/headers/private/media/MediaRosterEx.h index cb4f83730b..ab8ba22b25 100644 --- a/headers/private/media/MediaRosterEx.h +++ b/headers/private/media/MediaRosterEx.h @@ -88,6 +88,8 @@ public: BTimeSource* MakeTimeSourceObject(media_node_id timesource_id); + status_t BuildConnections(); + private: friend class BMediaRoster; }; diff --git a/headers/private/media/ServerInterface.h b/headers/private/media/ServerInterface.h index 116de33d82..59991e617d 100644 --- a/headers/private/media/ServerInterface.h +++ b/headers/private/media/ServerInterface.h @@ -32,6 +32,12 @@ enum { MEDIA_ADD_ON_SERVER_PLAY_MEDIA = '_TRU' }; +enum { + // local media services status notification service + MEDIA_ROSTER_REQUEST_NOTIFICATIONS = 2000, + MEDIA_ROSTER_CANCEL_NOTIFICATIONS +}; + // Raw port based communication enum { GENERAL_PURPOSE_WAKEUP = 0, // when no action but wait termination needed diff --git a/src/kits/media/DataExchange.cpp b/src/kits/media/DataExchange.cpp index f340ea73a2..8c33d14ee6 100644 --- a/src/kits/media/DataExchange.cpp +++ b/src/kits/media/DataExchange.cpp @@ -25,6 +25,7 @@ namespace dataexchange { static BMessenger sMediaServerMessenger; +static BMessenger sMediaRosterMessenger; static port_id sMediaServerPort; static port_id sMediaAddonServerPort; @@ -58,7 +59,7 @@ find_media_addon_server_port() void -InitDataExchange() +InitServerDataExchange() { sMediaServerMessenger = BMessenger(B_MEDIA_SERVER_SIGNATURE); find_media_server_port(); @@ -66,6 +67,27 @@ InitDataExchange() } +void +InitRosterDataExchange(const BMessenger& rosterMessenger) +{ + sMediaRosterMessenger = rosterMessenger; +} + + +//! BMessage based data exchange with the local BMediaRoster +status_t +SendToRoster(BMessage* msg) +{ + status_t status = sMediaRosterMessenger.SendMessage(msg, + static_cast(NULL), TIMEOUT); + if (status != B_OK) { + ERROR("SendToRoster: SendMessage failed: %s\n", strerror(status)); + DEBUG_ONLY(msg->PrintToStream()); + } + return status; +} + + //! BMessage based data exchange with the media_server status_t SendToServer(BMessage* msg) diff --git a/src/kits/media/MediaRoster.cpp b/src/kits/media/MediaRoster.cpp index fce1bcd7eb..42c3a32a33 100644 --- a/src/kits/media/MediaRoster.cpp +++ b/src/kits/media/MediaRoster.cpp @@ -59,14 +59,15 @@ char __dont_remove_copyright_from_binary[] = "Copyright (c) 2002-2006 Marcus " #include -#include #include +#include #include -#include #include +#include #include #include #include +#include #include "TimeSourceObjectManager.h" @@ -75,6 +76,14 @@ namespace BPrivate { namespace media { +struct RosterNotification { + BMessenger messenger; + int32 what; +}; + +static bool sServerIsUp = false; +static List sNotificationList; + // This class is provided to ensure the BMediaRoster is quit. class MediaRosterUndertaker { public: @@ -101,11 +110,25 @@ BMediaRosterEx::BMediaRosterEx(status_t* _error) : BMediaRoster() { - InitDataExchange(); - gDormantNodeManager = new DormantNodeManager; gTimeSourceObjectManager = new TimeSourceObjectManager; + *_error = BuildConnections(); + + InitRosterDataExchange(BMessenger(this, this)); + + if (be_roster->StartWatching(BMessenger(this, this), + B_REQUEST_LAUNCHED | B_REQUEST_QUIT) != B_OK) + *_error = B_ERROR; + + sServerIsUp = BMediaRoster::IsRunning(); +} + + +status_t +BMediaRosterEx::BuildConnections() +{ + InitServerDataExchange(); // register this application with the media server server_register_app_request request; server_register_app_reply reply; @@ -115,9 +138,9 @@ BMediaRosterEx::BMediaRosterEx(status_t* _error) status_t status = QueryServer(SERVER_REGISTER_APP, &request, sizeof(request), &reply, sizeof(reply)); if (status != B_OK) - *_error = B_MEDIA_SYSTEM_FAILURE; - else - *_error = B_OK; + return B_MEDIA_SYSTEM_FAILURE; + + return B_OK; } @@ -1902,6 +1925,10 @@ BMediaRoster::StartWatching(const BMessenger & where, int32 notificationType) ERROR("BMediaRoster::StartWatching: notificationType invalid!\n"); return B_BAD_VALUE; } + + // NOTE: we support only explicitly B_MEDIA_SERVER_STARTED/QUIT + // notifications. This should be cleared in documentation. + return BPrivate::media::notifications::Register(where, media_node::null, notificationType); } @@ -3308,6 +3335,119 @@ BMediaRoster::MessageReceived(BMessage* message) return; } + case MEDIA_ROSTER_REQUEST_NOTIFICATIONS: + { + RosterNotification notification; + if (message->FindInt32(NOTIFICATION_PARAM_WHAT, ¬ification.what) + != B_OK) { + TRACE("BMediaRoster MEDIA_ROSTER_REQUEST_NOTIFICATIONS can't" + "find what parameter"); + return; + } + if (message->FindMessenger(NOTIFICATION_PARAM_MESSENGER, + ¬ification.messenger) != B_OK) { + TRACE("BMediaRoster MEDIA_ROSTER_REQUEST_NOTIFICATIONS can't" + "find messenger"); + return; + } + sNotificationList.Insert(notification); + return; + } + + case MEDIA_ROSTER_CANCEL_NOTIFICATIONS: + { + RosterNotification notification; + if (message->FindInt32(NOTIFICATION_PARAM_WHAT, ¬ification.what) + != B_OK) { + TRACE("BMediaRoster MEDIA_ROSTER_CANCEL_NOTIFICATIONS can't" + "find what parameter"); + return; + } + if (message->FindMessenger(NOTIFICATION_PARAM_MESSENGER, + ¬ification.messenger) != B_OK) { + TRACE("BMediaRoster MEDIA_ROSTER_CANCEL_NOTIFICATIONS can't" + "find messenger"); + return; + } + for (int32 i = 0; i < sNotificationList.CountItems(); i++) { + RosterNotification* current; + if (sNotificationList.Get(i, ¤t) != true) + return; + if (current->what == notification.what + && current->messenger == notification.messenger) { + sNotificationList.Remove(i); + return; + } + } + return; + } + + case B_SOME_APP_LAUNCHED: + { + BString mimeSig; + if (message->FindString("be:signature", &mimeSig) != B_OK) + return; + if (mimeSig != B_MEDIA_ADDON_SERVER_SIGNATURE + && mimeSig != B_MEDIA_SERVER_SIGNATURE) + return; + + TRACE("BMediaRoster::MessageReceived media services are going up."); + + // Send the notification to our subscribers + if (BMediaRoster::IsRunning()) { + sServerIsUp = true; + // Restore our friendship with the media servers + if (MediaRosterEx(this)->BuildConnections() != B_OK) { + TRACE("BMediaRoster::MessageReceived can't reconnect" + "to media_server."); + } + + for (int32 i = 0; i < sNotificationList.CountItems(); i++) { + RosterNotification* current; + if (sNotificationList.Get(i, ¤t) != true) + return; + if (current->what == B_MEDIA_SERVER_STARTED) { + if (current->messenger.SendMessage( + B_MEDIA_SERVER_STARTED) != B_OK) { + if(!current->messenger.IsValid()) + sNotificationList.Remove(i); + } + } + } + } + return; + } + + case B_SOME_APP_QUIT: + { + BString mimeSig; + if (message->FindString("be:signature", &mimeSig) != B_OK) + return; + if (mimeSig != B_MEDIA_ADDON_SERVER_SIGNATURE + && mimeSig != B_MEDIA_SERVER_SIGNATURE) + return; + + TRACE("BMediaRoster::MessageReceived media services are down."); + + // Send the notification to our subscribers + if (!BMediaRoster::IsRunning() && sServerIsUp == true) { + sServerIsUp = false; + for (int32 i = 0; i < sNotificationList.CountItems(); i++) { + RosterNotification* current; + if (sNotificationList.Get(i, ¤t) != true) + return; + if (current->what == B_MEDIA_SERVER_QUIT) { + if (current->messenger.SendMessage( + B_MEDIA_SERVER_QUIT) != B_OK) { + if(!current->messenger.IsValid()) + sNotificationList.Remove(i); + } + } + } + } + return; + } + case NODE_FINAL_RELEASE: { // this function is called by a BMediaNode to delete @@ -3338,7 +3478,14 @@ BMediaRoster::MessageReceived(BMessage* message) bool BMediaRoster::QuitRequested() { - UNIMPLEMENTED(); + CALLED(); + + if (be_roster->StopWatching(BMessenger(this, this)) != B_OK) + TRACE("Can't unregister roster notifications"); + + if (sNotificationList.CountItems() != 0) + sNotificationList.MakeEmpty(); + return true; } diff --git a/src/kits/media/Notifications.cpp b/src/kits/media/Notifications.cpp index 1ab1bfaa6e..4fb66bed71 100644 --- a/src/kits/media/Notifications.cpp +++ b/src/kits/media/Notifications.cpp @@ -58,6 +58,15 @@ Register(const BMessenger& notifyHandler, const media_node& node, int32 notification) { CALLED(); + + if (notification == B_MEDIA_SERVER_STARTED + || notification == B_MEDIA_SERVER_QUIT) { + BMessage msg(MEDIA_ROSTER_REQUEST_NOTIFICATIONS); + msg.AddInt32(NOTIFICATION_PARAM_WHAT, notification); + msg.AddMessenger(NOTIFICATION_PARAM_MESSENGER, notifyHandler); + return BPrivate::media::dataexchange::SendToRoster(&msg); + } + BMessage msg(MEDIA_SERVER_REQUEST_NOTIFICATIONS); msg.AddInt32(NOTIFICATION_PARAM_WHAT, notification); msg.AddInt32(NOTIFICATION_PARAM_TEAM, BPrivate::current_team()); @@ -73,6 +82,15 @@ Unregister(const BMessenger& notifyHandler, const media_node& node, int32 notification) { CALLED(); + + if (notification == B_MEDIA_SERVER_STARTED + || notification == B_MEDIA_SERVER_QUIT) { + BMessage msg(MEDIA_ROSTER_CANCEL_NOTIFICATIONS); + msg.AddInt32(NOTIFICATION_PARAM_WHAT, notification); + msg.AddMessenger(NOTIFICATION_PARAM_MESSENGER, notifyHandler); + return BPrivate::media::dataexchange::SendToRoster(&msg); + } + BMessage msg(MEDIA_SERVER_CANCEL_NOTIFICATIONS); msg.AddInt32(NOTIFICATION_PARAM_WHAT, notification); msg.AddInt32(NOTIFICATION_PARAM_TEAM, BPrivate::current_team()); @@ -295,6 +313,11 @@ IsValidNotificationRequest(bool node_specific, int32 notification) case B_MEDIA_FLAVORS_CHANGED: return true; + // invalid if we watch for a specific node + case B_MEDIA_SERVER_STARTED: + case B_MEDIA_SERVER_QUIT: + return !node_specific; + // only valid for node specific watching case B_MEDIA_PARAMETER_CHANGED: case B_MEDIA_FORMAT_CHANGED: