Skip to content

Commit

Permalink
Linux/tv-casting-app: simplified APIs for commands
Browse files Browse the repository at this point in the history
  • Loading branch information
sharadb-amazon committed Dec 16, 2023
1 parent a77fe11 commit 050f35b
Show file tree
Hide file tree
Showing 13 changed files with 325 additions and 45 deletions.
46 changes: 45 additions & 1 deletion examples/tv-casting-app/linux/simple-app-helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,53 @@ void DiscoveryDelegateImpl::HandleOnUpdated(matter::casting::memory::Strong<matt
ChipLogProgress(AppServer, "Updated CastingPlayer with ID: %s", player->GetId());
}

void OnLaunchURLSuccess(void * context,
const chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type::ResponseType & response)
{
ChipLogProgress(AppServer, "OnLaunchURLSuccess with response: %.*s", static_cast<int>(response.data.Value().size()),
response.data.Value().data());
}

void OnLaunchURLFailure(void * context, CHIP_ERROR error)
{
ChipLogError(AppServer, "OnLaunchURLFailure with err %" CHIP_ERROR_FORMAT, error.Format());
}

void ConnectionHandler(CHIP_ERROR err, matter::casting::core::CastingPlayer * castingPlayer)
{
ChipLogProgress(AppServer, "ConnectionHandler called with %" CHIP_ERROR_FORMAT, err.Format());
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogProgress(AppServer,
"ConnectionHandler: Failed to connect to CastingPlayer(ID: %s) with err %" CHIP_ERROR_FORMAT,
castingPlayer->GetId(), err.Format()));

ChipLogProgress(AppServer, "ConnectionHandler: Successfully connected to CastingPlayer(ID: %s)", castingPlayer->GetId());
std::vector<matter::casting::memory::Strong<matter::casting::core::Endpoint>> endpoints = castingPlayer->GetEndpoints();

// Find the desired Endpoint and auto-trigger some Matter Casting demo interactions
auto it = std::find_if(endpoints.begin(), endpoints.end(),
[](const matter::casting::memory::Strong<matter::casting::core::Endpoint> & endpoint) {
return endpoint->GetVendorId() == 65521;
});
if (it != endpoints.end())
{
unsigned index = (unsigned int) std::distance(endpoints.begin(), it);
matter::casting::memory::Strong<matter::casting::clusters::content_launcher::ContentLauncherCluster> cluster =
endpoints[index]->GetCluster<matter::casting::clusters::content_launcher::ContentLauncherCluster>();
if (cluster != nullptr)
{
chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type request;
request.contentURL = chip::CharSpan::fromCharString("https://www.test.com/videoid");
request.displayString = chip::Optional<chip::CharSpan>(chip::CharSpan::fromCharString("Test video"));
request.brandingInformation =
chip::MakeOptional(chip::app::Clusters::ContentLauncher::Structs::BrandingInformationStruct::Type());
cluster->LaunchURL(request, nullptr, OnLaunchURLSuccess, OnLaunchURLFailure,
chip::MakeOptional(static_cast<unsigned short>(5 * 1000)));
}
}
else
{
ChipLogError(AppServer, "Desired Endpoint not found on the CastingPlayer(ID: %s)", castingPlayer->GetId());
}
}

#if defined(ENABLE_CHIP_SHELL)
Expand Down
11 changes: 11 additions & 0 deletions examples/tv-casting-app/linux/simple-app-helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ class DiscoveryDelegateImpl : public matter::casting::core::DiscoveryDelegate
void HandleOnUpdated(matter::casting::memory::Strong<matter::casting::core::CastingPlayer> player) override;
};

/**
* @brief Success handler for the ContentLauncher.LaunchURL command's response
*/
void OnLaunchURLSuccess(void * context,
const chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type::ResponseType & response);

/**
* @brief Failure handler for the ContentLauncher.LaunchURL command's response
*/
void OnLaunchURLFailure(void * context, CHIP_ERROR error);

/**
* @brief Linux tv-casting-app's onCompleted handler for CastingPlayer.VerifyOrEstablishConnection API
*/
Expand Down
2 changes: 2 additions & 0 deletions examples/tv-casting-app/tv-casting-common/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ chip_data_model("tv-casting-common") {

# Add simplified casting API files here
sources += [
"clusters/ClusterTemplates.h",
"clusters/ContentLauncherCluster.cpp",
"clusters/ContentLauncherCluster.h",
"clusters/MediaPlaybackCluster.h",
"clusters/TargetNavigatorCluster.h",
Expand Down
138 changes: 138 additions & 0 deletions examples/tv-casting-app/tv-casting-common/clusters/ClusterTemplates.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

#include "core/Endpoint.h"
#include "core/Types.h"

#include "lib/support/logging/CHIPLogging.h"

namespace matter {
namespace casting {
namespace clusters {

template <typename ResponseType>
using SuccessCallbackType = std::function<void(void * context, const ResponseType & responseObject)>;
using FailureCallbackType = std::function<void(void * context, CHIP_ERROR err)>;

template <typename T>
struct CommandContext
{
CommandContext(memory::Strong<core::Endpoint> endpoint, T request, void * context,
SuccessCallbackType<typename T::ResponseType> successCb, FailureCallbackType failureCb,
const chip::Optional<uint16_t> & timedInvokeTimeoutMs) :
mSuccessCb(successCb),
mFailureCb(failureCb)
{
mEndpoint = endpoint;
mRequest = request;
mClientContext = context;
mTimedInvokeTimeoutMs = timedInvokeTimeoutMs;
}

memory::Strong<core::Endpoint> mEndpoint;
T mRequest;
void * mClientContext;
SuccessCallbackType<typename T::ResponseType> mSuccessCb;
FailureCallbackType mFailureCb;
chip::Optional<uint16_t> mTimedInvokeTimeoutMs;
};

template <typename T, typename C>
class Command
{
public:
Command(memory::Weak<core::Endpoint> endpoint) { this->mEndpoint = endpoint; }

void Invoke(T request, void * context, SuccessCallbackType<typename T::ResponseType> successCb, FailureCallbackType failureCb,
const chip::Optional<uint16_t> & timedInvokeTimeoutMs)
{
memory::Strong<core::Endpoint> endpoint = this->GetEndpoint().lock();
if (endpoint)
{
C * commandContext = new C(endpoint, request, context, successCb, failureCb, timedInvokeTimeoutMs);

endpoint->GetCastingPlayer()->FindOrEstablishSession(
commandContext,
// FindOrEstablishSession success handler
[](void * context, chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle) {
C * _commandContext = static_cast<C *>(context);
ChipLogProgress(AppServer, "<Command>::Invoke() Found or established session");

// Invoke command
support::MediaClusterBase cluster(exchangeMgr, sessionHandle, _commandContext->mEndpoint->GetId());
CHIP_ERROR err = cluster.template InvokeCommand(
_commandContext->mRequest, _commandContext,
// Command success handler
[](void * context,
const chip::app::Clusters::ContentLauncher::Commands::LauncherResponse::DecodableType & response) {
C * _commandContext = static_cast<C *>(context);
ChipLogProgress(AppServer, "<Command>::Invoke() response success");
_commandContext->mSuccessCb(_commandContext->mClientContext, response);
delete _commandContext;
},
// Command failure handler
[](void * context, CHIP_ERROR error) {
C * _commandContext = static_cast<C *>(context);
ChipLogError(AppServer,
"<Command>::Invoke() failure response on EndpointId: %d with error: "
"%" CHIP_ERROR_FORMAT,
_commandContext->mEndpoint->GetId(), error.Format());
_commandContext->mFailureCb(_commandContext->mClientContext, error);
delete _commandContext;
},
_commandContext->mTimedInvokeTimeoutMs);

// error in invoking the command
if (err != CHIP_NO_ERROR)
{
ChipLogError(AppServer,
"<Command>::Invoke() failure in invoking command on EndpointId: %d with error: "
"%" CHIP_ERROR_FORMAT,
_commandContext->mEndpoint->GetId(), err.Format());
_commandContext->mFailureCb(_commandContext->mClientContext, err);
delete _commandContext;
}
},
// FindOrEstablishSession failure handler
[](void * context, const chip::ScopedNodeId & peerId, CHIP_ERROR error) {
C * _commandContext = static_cast<C *>(context);
ChipLogError(AppServer,
"<Command>::Invoke() failure in retrieving session info for peerId.nodeId: "
"0x" ChipLogFormatX64 ", peer.fabricIndex: %d with error: %" CHIP_ERROR_FORMAT,
ChipLogValueX64(peerId.GetNodeId()), peerId.GetFabricIndex(), error.Format());
_commandContext->mFailureCb(_commandContext->mClientContext, error);
delete _commandContext;
});
}
else
{
ChipLogError(AppServer, "<Command>::Invoke() failure in retrieving Endpoint");
failureCb(context, CHIP_ERROR_INCORRECT_STATE);
}
}

protected:
memory::Weak<core::Endpoint> GetEndpoint() const { return mEndpoint.lock(); }
memory::Weak<core::Endpoint> mEndpoint;
};

}; // namespace clusters
}; // namespace casting
}; // namespace matter
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "ContentLauncherCluster.h"

namespace matter {
namespace casting {
namespace clusters {
namespace content_launcher {

void ContentLauncherCluster::LaunchURL(
chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type request, void * context,
SuccessCallbackType<chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type::ResponseType> successCb,
FailureCallbackType failureCb, const chip::Optional<uint16_t> & timedInvokeTimeoutMs)
{
LaunchURLCommand command(this->GetEndpoint());
command.Invoke(request, context, successCb, failureCb, timedInvokeTimeoutMs);
}

}; // namespace content_launcher
}; // namespace clusters
}; // namespace casting
}; // namespace matter
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#pragma once

#include "clusters/ClusterTemplates.h"
#include "core/Endpoint.h"
#include "core/Types.h"

Expand All @@ -26,19 +27,35 @@
namespace matter {
namespace casting {
namespace clusters {
namespace content_launcher {

class ContentLauncherCluster : public core::BaseCluster
{
private:
protected:
public:
ContentLauncherCluster(memory::Weak<core::Endpoint> endpoint) : core::BaseCluster(endpoint) {}

// TODO:
// LaunchURL(const char * contentUrl, const char * contentDisplayStr,
// chip::Optional<chip::app::Clusters::ContentLauncher::Structs::BrandingInformationStruct::Type> brandingInformation);
void LaunchURL(chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type request, void * context,
SuccessCallbackType<chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type::ResponseType> successCb,
FailureCallbackType failureCb, const chip::Optional<uint16_t> & timedInvokeTimeoutMs);
};

struct LaunchURLContext : public CommandContext<chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type>
{
LaunchURLContext(memory::Strong<core::Endpoint> endpoint,
chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type request, void * context,
SuccessCallbackType<chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type::ResponseType> successCb,
FailureCallbackType failureCb, const chip::Optional<uint16_t> & timedInvokeTimeoutMs) :
CommandContext(endpoint, request, context, successCb, failureCb, timedInvokeTimeoutMs)
{}
};

class LaunchURLCommand : public Command<chip::app::Clusters::ContentLauncher::Commands::LaunchURL::Type, LaunchURLContext>
{
public:
LaunchURLCommand(memory::Weak<core::Endpoint> endpoint) : Command(endpoint) {}
};

}; // namespace content_launcher
}; // namespace clusters
}; // namespace casting
}; // namespace matter
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ ConnectionContext::ConnectionContext(void * clientContext, core::CastingPlayer *
ChipLogError(AppServer, "Invalid ConnectionContext received in DeviceConnection success callback"));

connectionContext->mTargetCastingPlayer->mConnectionState = core::CASTING_PLAYER_CONNECTED;
connectionContext->mOnDeviceConnectedFn(context, exchangeMgr, sessionHandle);
connectionContext->mOnDeviceConnectedFn(connectionContext->mClientContext, exchangeMgr, sessionHandle);
delete connectionContext;
},
this);
Expand Down
34 changes: 17 additions & 17 deletions examples/tv-casting-app/tv-casting-common/core/CastingPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,23 @@ class CastingPlayer : public std::enable_shared_from_this<CastingPlayer>
unsigned long long int commissioningWindowTimeoutSec = kCommissioningWindowTimeoutSec,
EndpointFilter desiredEndpointFilter = EndpointFilter());

/**
* @brief Find an existing session for this CastingPlayer, or trigger a new session
* request.
*
* The caller can optionally provide `onDeviceConnected` and `onDeviceConnectionFailure` callback
* objects. If provided, these will be used to inform the caller about
* successful or failed connection establishment.
*
* If the connection is already established, the `onDeviceConnected` callback
* will be immediately called, before FindOrEstablishSession returns.
*
* The `onDeviceConnectionFailure` callback may be called before the FindOrEstablishSession
* call returns, for error cases that are detected synchronously.
*/
void FindOrEstablishSession(void * clientContext, chip::OnDeviceConnected onDeviceConnected,
chip::OnDeviceConnectionFailure onDeviceConnectionFailure);

/**
* @brief Register an endpoint on this CastingPlayer. If the provided endpoint was already registered, its information will be
* updated in the registry.
Expand Down Expand Up @@ -191,23 +208,6 @@ class CastingPlayer : public std::enable_shared_from_this<CastingPlayer>
chip::Inet::IPAddress * GetIpAddressForUDCRequest();
#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT

/**
* @brief Find an existing session for this CastingPlayer, or trigger a new session
* request.
*
* The caller can optionally provide `onDeviceConnected` and `onDeviceConnectionFailure` callback
* objects. If provided, these will be used to inform the caller about
* successful or failed connection establishment.
*
* If the connection is already established, the `onDeviceConnected` callback
* will be immediately called, before FindOrEstablishSession returns.
*
* The `onDeviceConnectionFailure` callback may be called before the FindOrEstablishSession
* call returns, for error cases that are detected synchronously.
*/
void FindOrEstablishSession(void * clientContext, chip::OnDeviceConnected onDeviceConnected,
chip::OnDeviceConnectionFailure onDeviceConnectionFailure);

/**
* @brief Checks if the cachedCastingPlayer contains an Endpoint that matches the description of the desiredEndpointFilter
*
Expand Down
5 changes: 1 addition & 4 deletions examples/tv-casting-app/tv-casting-common/core/Cluster.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ class Endpoint;
// Base cluster class
class BaseCluster
{
private:
protected:
memory::Weak<Endpoint> mEndpoint;

public:
BaseCluster(memory::Weak<Endpoint> endpoint) { this->mEndpoint = endpoint; }

Expand All @@ -47,6 +43,7 @@ class BaseCluster

protected:
memory::Weak<Endpoint> GetEndpoint() const { return mEndpoint.lock(); }
memory::Weak<Endpoint> mEndpoint;
};

}; // namespace core
Expand Down
Loading

0 comments on commit 050f35b

Please sign in to comment.