ADTF  3.18.2
Source Code for SDL Application Service Plugin
Location
./src/examples/src/adtf/system_services/gui/demo_sdl_application_service/
Namespace for entire ADTF SDK.
Build Environment
To see how to set up the build environment have a look at ADTF CMake Environment
this implementation shows:
  • how to implement a Streaming Sink
  • how to create a simple graphics application based on the SDL library (Simple DirectMedia Layer, see Example Build Dependencies)
  • how to initialize and use the adtf/ucom runtime environment
  • how to implement a local service within a standalone application
  • how to register and access a service and service instances
  • how to implement a basic processing loop using the adtf::services::ant::IApplication interface
Remarks
  • Linux:
    1. set SDLDIR as an environment variable. This variable must point to your sdl directory
  • Windows:
    1. add the path of your SDL include directory to the include paths of your Visual Studio (project)
    2. add the path of your SDL lib directory to the lib paths of your Visual Studio (project)
    3. to run this app you have to make sure that the dll can be found from this app. Extend your Path variable or use the CMake Variable SDL_SHARED_LIB to add the SDL.dll as install-target.
  • Call Sequence:
    1. For better understanding the sequence of calls while using base device classes of ADTF SDK the device implementations are added to the installation as cpp-file.
    2. So you can debug through sequence.
Header
#ifndef _DEMO_SDL_APP_SERVICE_HEADER_
#define _DEMO_SDL_APP_SERVICE_HEADER_
#define CID_ADTF_SDL_DEMO_APP "demo_sdl_application.ui_service.adtf.cid"
class cSDLAppService : public adtf::ucom::object<cADTFService, adtf::services::ant::IApplication, ISDLApplication>
{
A_UTILS_D(cSDLAppService);
public:
ADTF_CLASS_ID_NAME(cSDLAppService, CID_ADTF_SDL_DEMO_APP, "SDL Application Service");
PROVIDE_INTERFACE(ISDLApplication));
public:
cSDLAppService();
public: // overrides cService
tResult ServiceInit() override;
tResult ServiceShutdown() override;
public: // implements IApplication
tResult Exec() override;
tResult Exit(tResult resExit=ERR_NOERROR) override;
tResult EnqueueJob(const adtf::ucom::ant::iobject_ptr<IJob>& oJob, bool bWait) override;
public: // implements ISDLApplication
tResult RegisterSDLDrawer(ISDLDrawer& oDrawer) override;
tResult UnregisterSDLDrawer(ISDLDrawer& oDrawer) override;
protected:
tResult Paint();
private:
};
#endif // _DEMO_SERVICE_HEADER_
#define REQUIRE_INTERFACE(_interface)
Macro usable with ADTF_CLASS_DEPENDENCIES() to require mandatory interfaces.
#define PROVIDE_INTERFACE(_interface)
Macro usable with ADTF_CLASS_DEPENDENCIES() to provide interfaces by defining class.
#define ADTF_CLASS_DEPENDENCIES(...)
Add interface ids (string literals,.
#define ADTF_CLASS_ID_NAME(_class, _strcid, _strclabel)
Common macro to enable correct treatment of class identifier AND Class Name by IClassInfo.
Definition: class_id.h:33
Property Variable template for the given T.
The IApplication interface wraps a generic processing loop.
The IReferenceClock interface provides the reference time source for the filter graph.
Base object pointer to realize binary compatible reference counting in interface methods.
Use this template if you want to implement an ucom::ant::IObject based Interface and/or subclass an e...
Definition: object.h:379
#define A_UTILS_D(__pclassname_)
Helper macro for d-pattern definitions.
Definition: d_ptr.h:270
Implementation
#include "stdafx.h"
#include "sdl_application_intf.h"
#include "sdl_application.h"
#include <future>
ADTF_PLUGIN("SDL Application Service",
cSDLAppService);
#define LIMIT_FRAMERATE true
#define RECTS_PER_SECOND 5000
struct tInternalFormat : public adtf::streaming::tStreamImageFormat
{
tInternalFormat& operator=(const adtf::streaming::tStreamImageFormat& oCopyFrom)
{
adtf::streaming::tStreamImageFormat::operator=(oCopyFrom);
return *this;
}
uint32_t Rmask = 0;
uint32_t Gmask = 0;
uint32_t Bmask = 0;
uint32_t Amask = 0;
unsigned int nBitsPerPixel = 0;
};
tResult get_mask_and_bit_count(const adtf::streaming::tStreamImageFormat& sFormat,
tInternalFormat& sFormatToSet)
{
using namespace adtf::streaming;
//will reset
sFormatToSet = sFormat;
//gerneric RGB definition ... see stream_image_format_is_generic_rgb documentation
{
if (!stream_image_format_is_generic_greyscale(sFormat))
{
RETURN_ERROR(ERR_NOT_SUPPORTED);
}
{
RETURN_ERROR(ERR_NOT_SUPPORTED);
}
sFormatToSet.Rmask = 0xFF;
sFormatToSet.Gmask = 0xFF;
sFormatToSet.Bmask = 0xFF;
sFormatToSet.Amask = 0xFF;
sFormatToSet.nBitsPerPixel = 8;
}
sFormatToSet.Rmask = stream_image_format_get_generic_mask("R", sFormat);
sFormatToSet.Gmask = stream_image_format_get_generic_mask("G", sFormat);
sFormatToSet.Bmask = stream_image_format_get_generic_mask("B", sFormat);
sFormatToSet.Amask = stream_image_format_get_generic_mask("A", sFormat);
sFormatToSet.nBitsPerPixel = stream_image_format_get_generic_pixel_size(sFormat);
}
class cSDLDrawRect : public ISDLApplication::ISDLCanvas
{
SDL_Surface* m_pGlobalScreen;
ISDLApplication::ISDLDrawer* m_pDrawerRef;
tInternalFormat m_sCurrentFormat;
protected:
cSDLDrawRect() = default;
public:
cSDLDrawRect(SDL_Surface* pSurface, ISDLApplication::ISDLDrawer& oDrawer)
{
m_pGlobalScreen = pSurface;
m_pDrawerRef = &oDrawer;
}
bool Check(ISDLApplication::ISDLDrawer& oDrawer)
{
return (m_pDrawerRef == &oDrawer);
}
tResult DrawImage(const adtf::streaming::tStreamImageFormat& sFormat,
const void* pData, size_t /*szDataSize*/) override
{
if (m_sCurrentFormat != sFormat)
{
RETURN_IF_FAILED(get_mask_and_bit_count(sFormat, m_sCurrentFormat));
LOG_INFO("Format changed");
}
SDL_Surface* pSurface = NULL;
//SDL_CreateRGBSurfaceFrom will copy?
void* pDataNonConst = const_cast<void*>(pData);
pSurface = SDL_CreateRGBSurfaceFrom(
pDataNonConst,
m_sCurrentFormat.m_ui32Width,
m_sCurrentFormat.m_ui32Height,
m_sCurrentFormat.nBitsPerPixel,
static_cast<int>(m_sCurrentFormat.m_szMaxByteSize) / m_sCurrentFormat.m_ui32Height,
m_sCurrentFormat.Rmask, m_sCurrentFormat.Gmask, m_sCurrentFormat.Bmask, m_sCurrentFormat.Amask);
if (nullptr == pSurface)
{
RETURN_ERROR(ERR_FAILED);
}
SDL_Rect rectDest;
rectDest.x = rectDest.y = 0;
// super-verbose guards for the upcoming static_cast
constexpr auto dest_type_min = std::numeric_limits<decltype(rectDest.w)>::min();
constexpr auto dest_type_max = std::numeric_limits<decltype(rectDest.w)>::max();
// prevent 'unused variable' warning in release builds
(void)dest_type_min;
(void)dest_type_max;
assert(m_pGlobalScreen->w >= dest_type_min && m_pGlobalScreen->w <= dest_type_max);
assert(m_pGlobalScreen->h >= dest_type_min && m_pGlobalScreen->h <= dest_type_max);
rectDest.w = static_cast<Uint16>(m_pGlobalScreen->w);
rectDest.h = static_cast<Uint16>(m_pGlobalScreen->h);
SDL_BlitSurface(pSurface, &rectDest, m_pGlobalScreen, &m_pGlobalScreen->clip_rect);
SDL_UpdateRects(m_pGlobalScreen, 1, &rectDest);
SDL_FreeSurface(pSurface);
}
tResult Paint()
{
RETURN_IF_FAILED(m_pDrawerRef->OnPaint(*this));
}
};
class cSDLAppService::cSDLAppServicePrivate : public adtf_util::d_ptr_impl<cSDLAppService, cSDLAppService::cSDLAppServicePrivate>
{
friend class cSDLAppService;
protected:
std::vector<cSDLDrawRect*> m_lstDrawRects;
SDL_Surface* m_pScreen = nullptr;
std::atomic_bool m_bExit;
std::recursive_mutex m_oListSynch;
};
cSDLAppService::cSDLAppService()
{
A_UTILS_D_CREATE(cSDLAppService);
_d->m_bExit = false;
m_ui32SetWidth.SetDescription("Specified width for Sample dimension.");
RegisterPropertyVariable("width", m_ui32SetWidth);
m_ui32SetHeight.SetDescription("Specified height for Sample dimension.");
RegisterPropertyVariable("height", m_ui32SetHeight);
m_ui32PixelDepth.SetDescription("The bit depth in pixel.");
RegisterPropertyVariable("pixel", m_ui32PixelDepth);
// sets a short description for the component
SetDescription("Use this System Service to provide a simple graphics application based on SDL.");
// set help link to jump to documentation from ADTF Configuration Editor
SetHelpLink("$(ADTF_DIR)/doc/adtf_html/page_demo_sdl_application.html");
}
tResult cSDLAppService::ServiceInit()
{
srand( (unsigned)time( NULL ) );
if (0 != SDL_Init(SDL_INIT_VIDEO))
{
RETURN_ERROR(ERR_FAILED);
}
_d->m_pScreen = SDL_SetVideoMode(m_ui32SetWidth, m_ui32SetHeight, m_ui32PixelDepth, 0);
if (nullptr == _d->m_pScreen)
{
RETURN_ERROR_DESC(ERR_FAILED, "failed to set video mode: %s", SDL_GetError());
}
SDL_WM_SetCaption("ADTF SDL Demo Application", NULL);
}
tResult cSDLAppService::ServiceShutdown()
{
SDL_Quit();
}
tResult cSDLAppService::Exit(tResult resExit)
{
_d->m_bExit = true;
}
{
if (pClock)
{
return pClock->GetTime();
}
else
{
return adtf_util::cSystem::GetTime();
}
}
struct tJobAndPromise
{
std::unique_ptr<std::promise<void>> pFinished;
};
tResult cSDLAppService::Exec()
{
if (nullptr == _d->m_pScreen)
{
RETURN_ERROR(ERR_INVALID_STATE);
}
object_ptr<adtf::services::IReferenceClock> pClock;
_runtime->GetObject(pClock);
// handle events
SDL_Event sEvent;
tTimeStamp tmLastUpdate = get_current_time(pClock.Get());
tTimeStamp tmCurrent;
tTimeStamp tmElapsed;
tTimeStamp tmFrameCount = get_current_time(pClock.Get());
int nFrameCount = 0;
_d->m_bExit = false;
while (!_d->m_bExit)
{
while (0 != SDL_PollEvent(&sEvent) && !_d->m_bExit)
{
switch (sEvent.type)
{
case SDL_MOUSEBUTTONDOWN:
{
LOG_INFO(adtf_util::cString::Format("mouse down' ").GetPtr());
break;
}
case SDL_KEYDOWN:
{
LOG_INFO(adtf_util::cString::Format("pressed key '%s' ", SDL_GetKeyName(sEvent.key.keysym.sym)).GetPtr());
break;
}
case SDL_QUIT:
{
LOG_INFO("quit application");
_d->m_bExit = true;
break;
}
case SDL_USEREVENT:
{
if (sEvent.user.code == 1)
{
auto pJobAndFuture = static_cast<tJobAndPromise*>(sEvent.user.data1);
pJobAndFuture->pJob->Run(get_current_time(pClock.Get()), IRunnable::RUN_JOB, nullptr, 0);
sEvent.user.data1 = nullptr;
if (pJobAndFuture->pFinished)
{
pJobAndFuture->pFinished->set_value();
}
else
{
delete pJobAndFuture;
}
}
break;
}
default:
break;
}
}
if (!_d->m_bExit)
{
// update graphics
tmCurrent = get_current_time(pClock.Get());
tmElapsed = tmCurrent-tmLastUpdate;
Paint();
SDL_Flip(_d->m_pScreen);
tmLastUpdate = tmCurrent;
// limit frame rate
if (LIMIT_FRAMERATE && tmElapsed < 10000)
{
adtf_util::cSystem::Sleep(10000 - tmElapsed);
}
// update frame counter
nFrameCount++;
if (tmCurrent - tmFrameCount >= 1000000)
{
//LOG_INFO(adtf_util::cString::Format("%d frames per second\n", nFrameCount));
nFrameCount = 0;
tmFrameCount = tmCurrent;
}
}
}
}
tResult cSDLAppService::EnqueueJob(const adtf::ucom::ant::iobject_ptr<IJob>& pJob, bool bWait)
{
SDL_Event user_event;
auto pJobAndFuture = new tJobAndPromise;
pJobAndFuture->pJob = pJob;
if (bWait)
{
pJobAndFuture->pFinished.reset(new std::promise<void>());
}
user_event.type = SDL_USEREVENT;
user_event.user.code = 1;
user_event.user.data1 = pJobAndFuture;
user_event.user.data2 = nullptr;
int nRes = SDL_PushEvent(&user_event);
if (nRes != 0)
{
delete pJobAndFuture;
RETURN_ERROR(ERR_DEVICE_IN_USE);
}
if (bWait)
{
pJobAndFuture->pFinished->get_future().wait();
delete pJobAndFuture;
}
}
tResult cSDLAppService::RegisterSDLDrawer(ISDLDrawer& oDrawer)
{
_d->m_oListSynch.lock();
if (_d->m_lstDrawRects.size() > 0)
{
RETURN_ERROR_DESC(ERR_DEVICE_IN_USE, "Can only handle one display at the Moment");
}
cSDLDrawRect* pDrawRect = new cSDLDrawRect(_d->m_pScreen, oDrawer);
_d->m_lstDrawRects.push_back(pDrawRect);
_d->m_oListSynch.unlock();
}
tResult cSDLAppService::UnregisterSDLDrawer(ISDLDrawer& oDrawer)
{
_d->m_oListSynch.lock();
for (std::vector<cSDLDrawRect*>::iterator it = _d->m_lstDrawRects.begin();
it != _d->m_lstDrawRects.end();
it++)
{
if ((*it)->Check(oDrawer))
{
_d->m_lstDrawRects.erase(it);
_d->m_oListSynch.unlock();
}
}
_d->m_oListSynch.unlock();
RETURN_ERROR(ERR_NOT_FOUND);
}
tResult cSDLAppService::Paint()
{
std::lock_guard<std::recursive_mutex> oG(_d->m_oListSynch);
for (auto it : _d->m_lstDrawRects)
{
(*it).Paint();
}
}
#define ADTF_PLUGIN(__plugin_identifier,...)
The ADTF Plugin Macro will add the code of a adtf::ucom::ant::IPlugin implementation.
Definition: adtf_plugin.h:22
#define RETURN_ERROR_DESC(_code,...)
Same as RETURN_ERROR(_error) using a printf like parameter list for detailed error description.
#define RETURN_IF_FAILED(s)
Return if expression is failed, which requires the calling function's return type to be tResult.
#define RETURN_NOERROR
Return status ERR_NOERROR, which requires the calling function's return type to be tResult.
#define RETURN_ERROR(code)
Return specific error code, which requires the calling function's return type to be tResult.
virtual tTimeStamp GetTime() const =0
Retrieves the current reference time.
virtual tResult GetObject(iobject_ptr< IObject > &pObject, const char *strNameOID) const =0
Get registered object from object registry.
Object pointer implementation used for reference counting on objects of type IObject.
Definition: object_ptr.h:163
#define A_UTILS_D_CREATE(__classname_)
helper macro for d-pattern usage.
Definition: d_ptr.h:286
uint32_t stream_image_format_get_generic_mask(const char *strChannel, const tStreamImageFormat &strFormat)
Retrieves the pixelsize by a Generic Pixel Format.
bool stream_image_format_is_generic_rgb(const tStreamImageFormat &strFormat)
Check if R, G and B components are available in Generic Pixel Format.
unsigned int stream_image_format_get_generic_pixel_size(const tStreamImageFormat &strFormat)
Retrieves the pixelsize by a Generic Pixel Format.
Namespace for the ADTF Streaming SDK.
adtf::ucom::IRuntime * _runtime
Global Runtime Pointer to reference to the current runtime.
Simple Stream Type and Stream Meta Type definition for video frames or an image sequence as a conveni...