blob: c8182265560738cdb534b8fbeab889d4aa3f88c3 [file] [log] [blame] [edit]
/*
* This file is part of Wireless Display Software for Linux OS
*
* Copyright (C) 2014 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include "libwds/public/source.h"
#include "libwds/source/cap_negotiation_state.h"
#include "libwds/source/init_state.h"
#include "libwds/source/streaming_state.h"
#include "libwds/source/session_state.h"
#include "libwds/common/message_handler.h"
#include "libwds/common/rtsp_input_handler.h"
#include "libwds/public/wds_export.h"
#include "libwds/rtsp/getparameter.h"
#include "libwds/rtsp/setparameter.h"
#include "libwds/rtsp/triggermethod.h"
#include "libwds/public/media_manager.h"
namespace wds {
using rtsp::Message;
using rtsp::Request;
using rtsp::Reply;
namespace {
bool InitializeRequestId(Request* request) {
Request::ID id = Request::UNKNOWN;
switch(request->method()) {
case Request::MethodOptions:
id = Request::M2;
break;
case Request::MethodSetup:
id = Request::M6;
break;
case Request::MethodPlay:
id = Request::M7;
break;
case Request::MethodTeardown:
id = Request::M8;
break;
case Request::MethodPause:
id = Request::M9;
break;
case Request::MethodSetParameter:
if (auto payload = rtsp::ToPropertyMapPayload(request->payload())) {
if (payload->HasProperty(rtsp::RoutePropertyType))
id = Request::M10;
else if (payload->HasProperty(rtsp::ConnectorTypePropertyType))
id = Request::M11;
else if (payload->HasProperty(rtsp::StandbyPropertyType))
id = Request::M12;
else if (payload->HasProperty(rtsp::IDRRequestPropertyType))
id = Request::M13;
else if (payload->HasProperty(rtsp::UIBCCapabilityPropertyType))
id = Request::M14;
else if (payload->HasProperty(rtsp::UIBCSettingPropertyType))
id = Request::M15;
break;
}
default:
WDS_ERROR("Failed to identify the received message");
return false;
}
request->set_id(id);
return true;
}
}
class SourceStateMachine : public MessageSequenceHandler {
public:
SourceStateMachine(const InitParams& init_params, unsigned& timer_id)
: MessageSequenceHandler(init_params) {
MessageHandlerPtr m16_sender = make_ptr(new source::M16Sender(init_params));
AddSequencedHandler(make_ptr(new source::InitState(init_params)));
AddSequencedHandler(make_ptr(new source::CapNegotiationState(init_params)));
AddSequencedHandler(make_ptr(new source::SessionState(init_params, timer_id, m16_sender)));
AddSequencedHandler(make_ptr(new source::StreamingState(init_params, m16_sender)));
}
};
class SourceImpl final : public Source, public RTSPInputHandler, public MessageHandler::Observer {
public:
SourceImpl(Delegate* delegate, SourceMediaManager* mng, Peer::Observer* observer);
private:
// Source implementation.
void Start() override;
void Reset() override;
void RTSPDataReceived(const std::string& message) override;
bool Teardown() override;
bool Play() override;
bool Pause() override;
// public MessageHandler::Observer
void OnCompleted(MessageHandlerPtr handler) override;
void OnError(MessageHandlerPtr handler) override;
void OnTimerEvent(unsigned timer_id) override;
// RTSPInputHandler
void MessageParsed(std::unique_ptr<Message> message) override;
void ParserErrorOccurred(const std::string& invalid_input) override;
// Keep-alive function
void SendKeepAlive();
void ResetAndTeardownMedia();
unsigned keep_alive_timer_;
std::shared_ptr<SourceStateMachine> state_machine_;
Delegate* delegate_;
SourceMediaManager* media_manager_;
Peer::Observer* observer_;
};
SourceImpl::SourceImpl(Delegate* delegate, SourceMediaManager* mng, Peer::Observer* observer)
: keep_alive_timer_(0),
state_machine_(new SourceStateMachine({delegate, mng, this}, keep_alive_timer_)),
delegate_(delegate),
media_manager_(mng),
observer_(observer) {
}
void SourceImpl::Start() {
state_machine_->Start();
}
void SourceImpl::Reset() {
state_machine_->Reset();
delegate_->ReleaseTimer(keep_alive_timer_);
}
void SourceImpl::RTSPDataReceived(const std::string& message) {
AddInput(message);
}
void SourceImpl::OnTimerEvent(unsigned timer_id) {
if (keep_alive_timer_ == timer_id)
SendKeepAlive();
else if (state_machine_->HandleTimeoutEvent(timer_id) && observer_)
observer_->ErrorOccurred(TimeoutError);
}
void SourceImpl::SendKeepAlive() {
delegate_->ReleaseTimer(keep_alive_timer_);
auto get_param = std::unique_ptr<Request>(
new rtsp::GetParameter("rtsp://localhost/wfd1.0"));
get_param->header().set_cseq(delegate_->GetNextCSeq());
get_param->set_id(Request::M16);
assert(state_machine_->CanSend(get_param.get()));
state_machine_->Send(std::move(get_param));
keep_alive_timer_ =
delegate_->CreateTimer(kDefaultKeepAliveTimeout - kDefaultTimeoutValue);
assert(keep_alive_timer_);
}
namespace {
std::unique_ptr<Message> CreateM5(int send_cseq, rtsp::TriggerMethod::Method method) {
auto set_param = std::unique_ptr<Request>(
new rtsp::SetParameter("rtsp://localhost/wfd1.0"));
set_param->header().set_cseq(send_cseq);
auto payload = new rtsp::PropertyMapPayload();
payload->AddProperty(
std::shared_ptr<rtsp::Property>(new rtsp::TriggerMethod(method)));
set_param->set_payload(std::unique_ptr<rtsp::Payload>(payload));
set_param->set_id(Request::M5);
return std::move(set_param);
}
}
bool SourceImpl::Teardown() {
auto m5 = CreateM5(delegate_->GetNextCSeq(),
rtsp::TriggerMethod::TEARDOWN);
if (!state_machine_->CanSend(m5.get()))
return false;
state_machine_->Send(std::move(m5));
return true;
}
bool SourceImpl::Play() {
auto m5 = CreateM5(delegate_->GetNextCSeq(),
rtsp::TriggerMethod::PLAY);
if (!state_machine_->CanSend(m5.get()))
return false;
state_machine_->Send(std::move(m5));
return true;
}
bool SourceImpl::Pause() {
auto m5 = CreateM5(delegate_->GetNextCSeq(),
rtsp::TriggerMethod::PAUSE);
if (!state_machine_->CanSend(m5.get()))
return false;
state_machine_->Send(std::move(m5));
return true;
}
void SourceImpl::OnCompleted(MessageHandlerPtr handler) {
assert(handler == state_machine_);
if (observer_)
observer_->SessionCompleted();
}
void SourceImpl::OnError(MessageHandlerPtr handler) {
assert(handler == state_machine_);
if (observer_)
observer_->ErrorOccurred(UnexpectedMessageError);
}
void SourceImpl::MessageParsed(std::unique_ptr<Message> message) {
if (message->is_request() && !InitializeRequestId(ToRequest(message.get()))) {
WDS_ERROR("Cannot identify the received message");
if (observer_)
observer_->ErrorOccurred(UnexpectedMessageError);
return;
}
if (!state_machine_->CanHandle(message.get())) {
WDS_ERROR("Cannot handle the received message with Id: %d", ToRequest(message.get())->id());
if (observer_)
observer_->ErrorOccurred(UnexpectedMessageError);
return;
}
state_machine_->Handle(std::move(message));
}
void SourceImpl::ParserErrorOccurred(const std::string& invalid_input) {
WDS_ERROR("Failed to parse: %s", invalid_input.c_str());
if (observer_)
observer_->ErrorOccurred(MessageParseError);
}
Source* Source::Create(Delegate* delegate, SourceMediaManager* mng, Peer::Observer* observer) {
return new SourceImpl(delegate, mng, observer);
}
} // namespace wds