Does intel J1900 have vsync? - intel

i am trying to run Qt5 with eglfs on an intel j1900 cpu (HD graphics gen7). when running the app i and getting the error
Could not queue DRM page flip on screen DP1 (Invalid argument)
looking at the source for eglfs I see on line 294
FrameBuffer *fb = framebufferForBufferObject(m_gbm_bo_next);
ensureModeSet(fb->fb);
QKmsOutput &op(output());
const int fd = device()->fd();
m_flipPending = true;
int ret = drmModePageFlip(fd,
op.crtc_id,
fb->fb,
DRM_MODE_PAGE_FLIP_EVENT,
this);
if (ret) {
qErrnoWarning("Could not queue DRM page flip on screen %s", qPrintable(name()));
m_flipPending = false;
gbm_surface_release_buffer(m_gbm_surface, m_gbm_bo_next);
m_gbm_bo_next = nullptr;
return;
}
for (CloneDestination &d : m_cloneDests) {
if (d.screen != this) {
d.screen->ensureModeSet(fb->fb);
d.cloneFlipPending = true;
int ret = drmModePageFlip(fd,
d.screen->output().crtc_id,
fb->fb,
DRM_MODE_PAGE_FLIP_EVENT,
d.screen);
if (ret) {
qErrnoWarning("Could not queue DRM page flip for clone screen %s", qPrintable(name()));
d.cloneFlipPending = false;
}
}
}
http://code.qt.io/cgit/qt/qtbase.git/tree/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp?h=5.11#n294
I can't seem to find a way for eglfs to not use vsync, and using the env varibles below dont make a difference
QT_QPA_EGLFS_SWAPINTERVAL
QT_QPA_EGLFS_FORCEVSYNC
FB_MULTI_BUFFER
QT_QPA_EGLFS_FORCE888

Related

decrypt function at run time and use it QT c++

I'm new to QT and I'm trying to create an encrypted function.
Overall what you do in C / C ++ is:
Take pointer to function
make the function page rwx
Encrypt it (for the example I encrypt and decrypt in the same program)
Decrypt it and run it
A simple code in C will happen roughly like this:
void TestFunction()
{
printf("\nmsgbox test encrypted func\n");
}
// use this as a end label
void FunctionStub() { return; }
void XorBlock(DWORD dwStartAddress, DWORD dwSize)
{
char * addr = (char *)dwStartAddress;
for (int i = 0; i< dwSize; i++)
{
addr[i] ^= 0xff;
}
}
DWORD GetFuncSize(DWORD* Function, DWORD* StubFunction)
{
DWORD dwFunctionSize = 0, dwOldProtect;
DWORD *fnA = NULL, *fnB = NULL;
fnA = (DWORD *)Function;
fnB = (DWORD *)StubFunction;
dwFunctionSize = (fnB - fnA);
VirtualProtect(fnA, dwFunctionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect); // make function page read write execute permission
return dwFunctionSize;
}
int main()
{
DWORD dwFuncSize = GetFuncSize((DWORD*)&TestFunction, (DWORD*)&FunctionStub);
printf("use func");
TestFunction();
XorBlock((DWORD)&TestFunction, dwFuncSize); // XOR encrypt the function
printf("after enc");
//TestFunction(); // If you try to run the encrypted function you will get Access Violation Exception.
XorBlock((DWORD)&TestFunction, dwFuncSize); // XOR decrypt the function
printf("after\n");
TestFunction(); // Fine here
getchar();
}
When I try to run such an example in QT I get a run time error.
Here is the code in QT:
void TestFunction()
{
QMessageBox::information(0, "Test", "msgbox test encrypted func");
}
void FunctionStub() { return; }
void XorBlock(DWORD dwStartAddress, DWORD dwSize)
{
char * addr = (char *)dwStartAddress;
for (int i = 0; i< dwSize; i++)
{
addr[i] ^= 0xff; // here i get seg. fault
}
}
DWORD GetFuncSize(DWORD* Function, DWORD* StubFunction)
{
DWORD dwFunctionSize = 0, dwOldProtect;
DWORD *fnA = NULL, *fnB = NULL;
fnA = (DWORD *)Function;
fnB = (DWORD *)StubFunction;
dwFunctionSize = (fnB - fnA);
VirtualProtect(fnA, dwFunctionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect); // Need to modify our privileges to the memory
QMessageBox::information(0, "Test", "change func to read write execute ");
return dwFunctionSize;
}
void check_enc_function()
{
DWORD dwFuncSize = GetFuncSize((DWORD*)&TestFunction, (DWORD*)&FunctionStub);
QMessageBox::information(0, "Test", "use func");
TestFunction();
XorBlock((DWORD)&TestFunction, dwFuncSize); // XOR encrypt the function -> ### i get seg fault in here ###
QMessageBox::information(0, "Test", "after enc");
TestFunction(); // If you try to run the encrypted function you will get Access Violation Exception.
XorBlock((DWORD)&TestFunction, dwFuncSize); // XOR decrypt the function
QMessageBox::information(0, "Test", "after dec");
TestFunction(); // Fine here
getchar();
}
Why should this happen?
QT is supposed to behave like precision as standard C ++ ...
post Scriptum.
Interestingly in the same matter, what is the most legitimate way to keep an important function encrypted (the reason it is encrypted is DRM)?
Legitimately I mean that anti-viruses will not mistakenly mark me as a virus because I defend myself.
PS2
If I pass an encrypted function over the network (say, I will build a server client schema that the client asks for the function it needs to run from the server and the server sends it to it if it is approved) How can I arrange the symbols so that the function does not collapse?
PS3
How in QT can I turn off the DEP and ASLR defenses? (In my opinion so that I can execute PS 2. I have to cancel them)
Thanks
yoko
The example is undefined behaviour on my system.
The first and main issue in your code is:
void TestFunction() { /* ... */ }
void FunctionStub() { return; }
You assume that the compiler will put FunctionStub after TestFunction without any padding. I compiled your example and FunctionStub in my case was above TestFunction which resulted in a negative dwFunctionSize.
dwFunctionSize = (fnB - fnA);
TestFunction located at # 0xa11d90
FunctionStub located at # 0xa11b50
dwFunctionSize = -0x240
Also in XorBlock
addr[i] ^= 0xff;
Is doing nothing.
I assume you want to write in XorBlock to the memory location to XOR the entire TestFunction.
You could do something like this:
void XorBlock(DWORD dwStartAddress, DWORD dwSize)
{
DWORD dwEndAddress = dwStartAddress + dwSize;
for(DWORD i = dwStartAddress; i < dwEndAddress; i++) {
// ...
}
}
I can't see any Qt-specific in your example. Even if it's Qt function call it's just a call. So I guess you have undefined behaviour in both examples but only second one crashes.
I can't see any reason for compiler and linker to keep function order. For example GCC let you specify the code section for each function. So you can reorder it in executable without reordering in cpp.
I think you need some compiler specific things to make it work.

Qt+GStreamer: How to take a snapshot while playing live video stream

I've developed a video player based on Qt and QtGstreamer. It is used to play live streams (RTSP). I have to add the possibility for the user to take snapshots while he is playing a live stream without perturbing the video playback.
Here the graph of the pipeline I've made:
-->queue-->autovideosink
uridecodebin-->videoflip-->tee--|
| -->queue->videoconvert-->pngenc-->filesink
|
|->audioconvert-->autoaudiosink
I use the pad-added signal from uridecodebin to add and link dynamically my elements to the pipeline, function of the received caps.
void Player::onPadAdded(const QGst::PadPtr &pad)
{
QGst::CapsPtr caps = pad->currentCaps();
if (caps->toString().startsWith("video/x-raw")) {
qDebug("Received 'video/x-raw' caps");
handleNewVideoPad(pad);
}
else if (caps->toString().startsWith("audio/x-raw")) {
qDebug("Received 'audio/x-raw' caps");
if (!m_audioEnabled) {
qDebug("Audio is disabled in the player. Ignoring...");
return;
}
handleNewAudioPad(pad);
}
else {
qWarning("Unsuported caps, arborting ...!");
return;
}
}
[...]
void Player::handleNewVideoPad(QGst::PadPtr pad)
{
m_player->videoTeeVideoSrcPad = m_player->videoTee->getRequestPad("src_%u");
// Add video elements
m_player->pipeline->add(m_player->videoFlip);
m_player->pipeline->add(m_player->videoTee);
m_player->pipeline->add(m_player->videoQueue);
m_player->pipeline->add(m_player->videoSink);
// Add snap elements
m_player->pipeline->add(m_player->snapQueue);
m_player->pipeline->add(m_player->snapConverter);
m_player->pipeline->add(m_player->snapEncoder);
m_player->pipeline->add(m_player->snapSink);
// Link video elements
m_player->videoFlip->link(m_player->videoTee);
m_player->videoQueue->link(m_player->videoSink);
// Link snap elements
m_player->snapQueue->link(m_player->snapConverter);
m_player->snapConverter->link(m_player->snapEncoder);
m_player->snapEncoder->link(m_player->snapSink);
// Lock snap elements
m_player->snapQueue->setStateLocked(true);
m_player->snapConverter->setStateLocked(true);
m_player->snapEncoder->setStateLocked(true);
m_player->snapSink->setStateLocked(true);
m_player->videoFlip->setState(QGst::StatePlaying);
m_player->videoTee->setState(QGst::StatePlaying);
m_player->videoQueue->setState(QGst::StatePlaying);
m_player->videoSink->setState(QGst::StatePlaying);
// Link pads
m_player->videoTeeVideoSrcPad->link(m_player->videoQueue->getStaticPad("sink"));
pad->link(m_player->videoSinkPad);
m_player->videoLinked = true;
}
The method to take a snapshot:
void Player::takeSnapshot()
{
QDateTime dateTime = QDateTime::currentDateTime();
QString snapLocation = QString("/%1/snap_%2.png").arg(m_snapDir).arg(dateTime.toString(Qt::ISODate));
m_player->inSnapshotCaputre = true;
if (m_player->videoTeeSnapSrcPad) {
m_player->videoTee->releaseRequestPad(m_player->videoTeeSnapSrcPad);
m_player->videoTeeSnapSrcPad.clear();
}
m_player->videoTeeSnapSrcPad = m_player->videoTee->getRequestPad("src_%u");
// Stop the snapshot branch
m_player->snapQueue->setState(QGst::StateNull);
m_player->snapConverter->setState(QGst::StateNull);
m_player->snapEncoder->setState(QGst::StateNull);
m_player->snapSink->setState(QGst::StateNull);
// Link Tee src pad to snap queue sink pad
m_player->videoTeeSnapSrcPad->link(m_player->snapQueue->getStaticPad("sink"));
// Set the snapshot location property
m_player->snapSink->setProperty("location", snapLocation);
// Unlock snapshot branch
m_player->snapQueue->setStateLocked(false);
m_player->snapConverter->setStateLocked(false);
m_player->snapEncoder->setStateLocked(false);
m_player->snapSink->setStateLocked(false);
m_player->videoTeeSnapSrcPad->setActive(true);
// Synch snapshot branch state with parent
m_player->snapQueue->syncStateWithParent();
m_player->snapConverter->syncStateWithParent();
m_player->snapEncoder->syncStateWithParent();
m_player->snapSink->syncStateWithParent();
}
The bus message callback:
void Player::onBusMessage(const QGst::MessagePtr & message)
{
QGst::ElementPtr source = message->source().staticCast<QGst::Element>();
switch (message->type()) {
case QGst::MessageEos: { //End of stream. We reached the end of the file.
qDebug("Message End Off Stream");
if (m_player->inSnapshotCaputre) {
blockSignals(true);
pause();
play();
blockSignals(false);
m_player->inSnapshotCaputre = false;
}
else {
m_eos = true;
stop();
}
break;
}
[...]
}
The problem is:
When I set the snapshot property to true of the pngenc element, I receive the EOS event which stop my pipeline, so I need to restart it, which freeze the video playback for about half a second, which in not acceptable in my case.
When I set the snapshot property to false of the pngenc element, I have no pipeline perturbations, but my png file keeps growing until I call again the Player::takeSnapshot() method.
Where am I wrong ? Is there a better way to do it ?
I've tried unsuccessfully creating a QGst::Bin element for my snapshot branch. What about pad probe ?
Thanks by advance
You can take the last-sample property on any sink, e.g. your video sink. This contains a GstSample, which has a buffer with the very latest video frame in it. You can take that as a snapshot, and e.g. with gst_video_convert_sample() or the async variant of it, convert it to a PNG/JPG/whatever.
See https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer-libs/html/GstBaseSink.html#GstBaseSink--last-sample and https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-base-libs/html/gst-plugins-base-libs-gstvideo.html#gst-video-convert-sample
Alternatively, you would have to shut down the filesink snapshot pipeline after the first frame. For example by having a pad probe to know when the first frame happened, and then injecting an EOS event to prevent further PNG frames to be appended to the same file.
Thanks to #sebastian-droge answer, I found the solution, using gst_video_convert_sample and the last-sample property of my video sink.
The solution I've implemented is:
void Player::takeSnapshot()
{
QDateTime currentDate = QDateTime::currentDateTime();
QString location = QString("%1/snap_%2.png").arg(QDir::homePath()).arg(currentDate.toString(Qt::ISODate));
QImage snapShot;
QImage::Format snapFormat;
QGlib::Value val = m_videoSink->property("last-sample");
GstSample *videoSample = (GstSample *)g_value_get_boxed(val);
QGst::SamplePtr sample = QGst::SamplePtr::wrap(videoSample);
QGst::SamplePtr convertedSample;
QGst::BufferPtr buffer;
QGst::CapsPtr caps = sample->caps();
QGst::MapInfo mapInfo;
GError *err = NULL;
GstCaps * capsTo = NULL;
const QGst::StructurePtr structure = caps->internalStructure(0);
int width, height;
width = structure.data()->value("width").get<int>();
height = structure.data()->value("height").get<int>();
qDebug() << "Sample caps:" << structure.data()->toString();
/*
* { QImage::Format_RGBX8888, GST_VIDEO_FORMAT_RGBx },
* { QImage::Format_RGBA8888, GST_VIDEO_FORMAT_RGBA },
* { QImage::Format_RGB888 , GST_VIDEO_FORMAT_RGB },
* { QImage::Format_RGB16 , GST_VIDEO_FORMAT_RGB16 }
*/
snapFormat = QImage::Format_RGB888;
capsTo = gst_caps_new_simple("video/x-raw",
"format", G_TYPE_STRING, "RGB",
"width", G_TYPE_INT, width,
"height", G_TYPE_INT, height,
NULL);
convertedSample = QGst::SamplePtr::wrap(gst_video_convert_sample(videoSample, capsTo, GST_SECOND, &err));
if (convertedSample.isNull()) {
qWarning() << "gst_video_convert_sample Failed:" << err->message;
}
else {
qDebug() << "Converted sample caps:" << convertedSample->caps()->toString();
buffer = convertedSample->buffer();
buffer->map(mapInfo, QGst::MapRead);
snapShot = QImage((const uchar *)mapInfo.data(),
width,
height,
snapFormat);
qDebug() << "Saving snap to" << location;
snapShot.save(location);
buffer->unmap(mapInfo);
}
val.clear();
sample.clear();
convertedSample.clear();
buffer.clear();
caps.clear();
g_clear_error(&err);
if (capsTo)
gst_caps_unref(capsTo);
}
I've create a simple test application, which implement this solution. The code is available on my Github

use phonon crash in phonon_ds9d4.dll

use code :
Phonon::VideoPlayer *player = new Phonon::VideoPlayer(Phonon::MusicCategory, NULL);
QObject::connect(player, SIGNAL(finished()), player, SLOT(deleteLater()));
player->play(Phonon::MediaSource("c:/yaodong.mkv"));
but crash.
env is win7 and vs2008, qt 4.7.1
windows sdk is 7.1
dx sdk is Microsoft DirectX SDK (March 2009)
and i find the stack is
mediaobject.cpp (line 233) graph is null
} else if (!m_currentWork.url.isEmpty()) {
//let's render a url (blocking call)
hr = m_currentWork.graph->RenderFile(reinterpret_cast<const wchar_t *>(m_currentWork.url.utf16()), 0);
}
then i find run step to the function:
quint16 WorkerThread::addUrlToRender(const QString &url)
{
QMutexLocker locker(&m_mutex);
Work w;
w.task = Render;
//we create a new graph
w.graph = Graph(CLSID_FilterGraph, IID_IGraphBuilder);
w.url = url;
w.url.detach();
w.id = m_currentWorkId++;
m_queue.enqueue(w);
m_waitCondition.set();
return w.id;
}
the line : w.graph = Graph(CLSID_FilterGraph, IID_IGraphBuilder);
w.graph is null.
but i wrote the code outside :
IGraphBuilder *pGraph = NULL;
::CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER, IID_IGraphBuilder,
reinterpret_cast<void**>(&pGraph));
the pGraph is not null.
help. thanks!

Dll error return pointer from openni

i was trying to write a plugin for return RGB image stream from ASUS Xtion PRO LIVE using openni.
So in my Dll program i implement getdata() function of VideoFrameRef class to return pointer from image data. when i call this function from my test program it throws an exception.
hear is my dll code
void* MyRgbStrem::GetStream()
{
Device device;
VideoStream color;
VideoFrameRef vframeRef;
VideoMode vmode;
Status status = STATUS_OK;
status = openni::OpenNI::initialize ();
status = device.open(openni::ANY_DEVICE);
status = color.create (device, SENSOR_COLOR);
status = color.start();
while (true)
{
if (device.getSensorInfo(SENSOR_COLOR) != NULL)
{
status = color. readFrame(&vframeRef);
if (vframeRef.isValid())
{
return ( uint16_t*) vframeRef.getData();
}
}
}
}
and in the header file i declar class MyRgbStrem
namespace Rgbstream
{
class MyRgbStrem
{
public:
static __declspec(dllexport) void* GetStream();
};
}
in my test program i use opencv for image processing and to get frame width, height and size i declae few more functions in dll program and call them in test application
this is the program i use to call dll
{
Mat rgb,bgr;
int height, width, size;
height = Rgbstream::MyRgbStrem::GetFrameSize_H();
width = Rgbstream::MyRgbStrem::GetFrameSize_W();
size = Rgbstream::MyRgbStrem::getSizeOfData();
bgr.create ( height,width, CV_8UC3);
rgb.create (height,width, CV_8UC3);
while (true)
{
const void* imgbuff = Rgbstream::MyRgbStrem::GetStream();
memcpy (bgr.data, imgbuff, size );
cvtColor(rgb,bgr, CV_RGB2BGR);
namedWindow ("Color Video",CV_WINDOW_AUTOSIZE);
imshow ("Color Video", bgr);
char key = waitKey (10);
if (key == 27) break;
}
return 0;
}
when i run this program it throws an exception like this
Unhandled exception at 0x0F97E89A (msvcr110d.dll) in CallRgbStream.exe: 0xC0000005: Access violation reading location 0x00268000.
The program '[9544] CallRgbStream.exe' has exited with code 0 (0x0).
any help is appreciate. thanks

Qt 4.8. Tablet Events on Mac OS 10.6.8

TabletEvents comes as mouse events.
Actual for MAC OS Qt 4.8.0 - 4.8.5.
Works fine in Qt 4.7.3 on any OS and Qt 4.8.0 on Windows and Linux.
I have two instances of QGraphcisScene and two instances of QGraphicsView.
The same types, but one view have a parent, and the another - doesn't (also it's transparent, used for drawing something over desktop).
I'm using tablet (wacom pen and touch) for painting. I handle QTabletEvents and it works only for QGrahicsView instance that doesn't have parent (means parent==0).
On the view with parent (
QMainWindow->centralWidget->ControlContainerWidget->QStackedLayout->QGraphicsView
) tablet events doesn't comes. They comes to QApplication::eventFilter fine, but doesn't comes to view. They comes to QMainWindow as mouseEvents.
If i set parent to 0, tablet events delivers fine.
The 1st receiver of tablet event is QMainWindow.
I see that inside qt_mac_handleTabletEvent:
QWidget *qwidget = [theView qt_qwidget];
QWidget *widgetToGetMouse = qwidget;
And then:
`qt_sendSpontaneousEvent(widgetToGetMouse, &qtabletEvent);`
qtabletEvent - is not accepted event created just before calling sendSpontaneousEvent.
Then inside QApplication::notify():
QWidget *w = static_cast<QWidget *>(receiver);
QTabletEvent *tablet = static_cast<QTabletEvent*>(e);
QPoint relpos = tablet->pos();
bool eventAccepted = tablet->isAccepted();
while (w) {
QTabletEvent te(tablet->type(), relpos, tablet->globalPos(),
tablet->hiResGlobalPos(), tablet->device(), tablet->pointerType(),
tablet->pressure(), tablet->xTilt(), tablet->yTilt(),
tablet->tangentialPressure(), tablet->rotation(), tablet->z(),
tablet->modifiers(), tablet->uniqueId());
te.spont = e->spontaneous();
res = d->notify_helper(w, w == receiver ? tablet : &te);
eventAccepted = ((w == receiver) ? tablet : &te)->isAccepted();
e->spont = false;
if ((res && eventAccepted)
|| w->isWindow()
|| w->testAttribute(Qt::WA_NoMousePropagation))
break;
relpos += w->pos();
w = w->parentWidget();
}
tablet->setAccepted(eventAccepted);
As we can see:
res = d->notify_helper(w, w == receiver ? tablet : &te);
It calls event processing by filters, layouts and then - QMainWindow::tabletEvent. Default implementation is event->ignore().
Since QMainWindow have no Parent, it is all.
So tablet event doesn't comes to QMainWindow childs.
Then seems it is QWidget *qwidget = [theView qt_qwidget]; works wrong.
Unfortunately, i can't debug it...
Please give me some hints... i'm stucked...
I spent more time on comparison Qt 4.8.0 and 4.7.3 and now i see that it is the problem in internal qt event dispatcher. It sends event to NSWindow (QMainWindow) instead of NSView (QGraphicsView).
I didn't found where is the problem, but i found that QMainWindow returns false from ::event() method.
So i reimplemented that method and parsed tablet event there:
bool UBMainWindow::event(QEvent *event)
{
bool bRes = QMainWindow::event(event);
if (NULL != UBApplication::boardController)
{
UBBoardView *controlV = UBApplication::boardController->controlView();
if (controlV && controlV->isVisible())
{
switch (event->type())
{
case QEvent::TabletEnterProximity:
case QEvent::TabletLeaveProximity:
case QEvent::TabletMove:
case QEvent::TabletPress:
case QEvent::TabletRelease:
{
return controlV->directTabletEvent(event);
}
}
}
}
return bRes;
}
The problem is: i need to use tablet for any controls in application, so i need to determine when QGraphicsView is under mouse cursor:
bool UBBoardView::directTabletEvent(QEvent *event)
{
QTabletEvent *tEvent = static_cast<QTabletEvent *>(event);
tEvent = new QTabletEvent(tEvent->type()
, mapFromGlobal(tEvent->pos())
, tEvent->globalPos()
, tEvent->hiResGlobalPos()
, tEvent->device()
, tEvent->pointerType()
, tEvent->pressure()
, tEvent->xTilt()
, tEvent->yTilt()
, tEvent->tangentialPressure()
, tEvent->rotation()
, tEvent->z()
, tEvent->modifiers()
, tEvent->uniqueId());
if (geometry().contains(tEvent->pos()))
{
if (NULL == widgetForTabletEvent(this->parentWidget(), tEvent->pos()))
{
tabletEvent(tEvent);
return true;
}
}
return false;
}
Also i need to stop handle tablet events for QGraphicsView childs.
QWidget *UBBoardView::widgetForTabletEvent(QWidget *w, const QPoint &pos)
{
Q_ASSERT(w);
UBBoardView *board = qobject_cast<UBBoardView *>(w);
QWidget *childAtPos = NULL;
QList<QObject *> childs = w->children();
foreach(QObject *child, childs)
{
QWidget *childWidget = qobject_cast<QWidget *>(child);
if (childWidget)
{
if (childWidget->isVisible() && childWidget->geometry().contains(pos))
{
QWidget *lastChild = widgetForTabletEvent(childWidget, pos);
if (board && board->viewport() == lastChild)
continue;
if (NULL != lastChild)
childAtPos = lastChild;
else
childAtPos = childWidget;
break;
}
else
childAtPos = NULL;
}
}
return childAtPos;
}
Maybe when i will have more time - i will investigate qt more deeply and fix that problem at all.

Resources