Make sure that BaseWorker deletes the thread when is finished.

Implemented ClientManager so it can manage the usb and wireless threads.
Impelmented better mutex logic on CmaClient.
Execute the cma events in a different thread so the event listener is
available.
Code refactoring.
Fix memory leaks in threads.
Updated readme.
This commit is contained in:
codestation
2013-08-25 01:40:14 -04:30
parent dfdd79e850
commit 92f4572814
29 changed files with 523 additions and 411 deletions

View File

@@ -16,14 +16,12 @@ official CMA and also offer some features missing in the original one.
* Basic metadata for videos (duration, dimensions, thumbnail). * Basic metadata for videos (duration, dimensions, thumbnail).
* Basic metadata for photos (dimensions, thumbnails). * Basic metadata for photos (dimensions, thumbnails).
* Easy wireless pairing (show PIN to the user when a Vita is detected). * Easy wireless pairing (show PIN to the user when a Vita is detected).
* Ability to restart the connection if the Vita is reconnected (on test). * Ability to restart the connection if the Vita is reconnected.
#### TODO: #### TODO:
* Fix remaining bugs with thread synchronizations. * Complete categories for music.
* Remove wireless/usb switch and allow both to run.
* Folder categories for music/videos.
* SQLite backend for database. * SQLite backend for database.
* Fix wireless streaming for music/videos. * Fix streaming for music.
## Planned features ## Planned features
* **Backup browser**: provide a human readable listing of the games saved on the * **Backup browser**: provide a human readable listing of the games saved on the
@@ -37,7 +35,8 @@ using the wireless streaming feature.
#### Dependencies #### Dependencies
* [Qt 4.x or 5.x](http://qt-project.org/) * [Qt 4.x or 5.x](http://qt-project.org/)
* [VitaMTP](https://github.com/yifanlu/VitaMTP) * [VitaMTP](https://github.com/yifanlu/VitaMTP). Use my
[fork](https://github.com/codestation/VitaMTP) until the patches are merged upstream.
* [Libav](http://www.libav.org/) * [Libav](http://www.libav.org/)

View File

@@ -209,17 +209,17 @@ QByteArray AVDecoder::getVideoThumbnail(int width, int height)
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
SwsContext *sws_ctx = sws_getContext( SwsContext *sws_ctx = sws_getContext(
pCodecCtx->width, pCodecCtx->width,
pCodecCtx->height, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->width,
pCodecCtx->height, pCodecCtx->height,
PIX_FMT_RGB24, PIX_FMT_RGB24,
SWS_BICUBIC, SWS_BICUBIC,
NULL, NULL,
NULL, NULL,
NULL NULL
); );
if(!sws_ctx) { if(!sws_ctx) {
avcodec_close(pCodecCtx); avcodec_close(pCodecCtx);
@@ -238,8 +238,8 @@ QByteArray AVDecoder::getVideoThumbnail(int width, int height)
QImage image(pCodecCtx->width, pCodecCtx->height, QImage::Format_RGB888); QImage image(pCodecCtx->width, pCodecCtx->height, QImage::Format_RGB888);
for(int y = 0, height = pCodecCtx->height, width = pCodecCtx->width; y < height; y++){ for(int y = 0, h = pCodecCtx->height, w = pCodecCtx->width; y < h; y++) {
memcpy(image.scanLine(y), pFrameRGB->data[0] + y * pFrameRGB->linesize[0], width * 3); memcpy(image.scanLine(y), pFrameRGB->data[0] + y * pFrameRGB->linesize[0], w * 3);
} }
QBuffer imgbuffer(&data); QBuffer imgbuffer(&data);

View File

@@ -27,9 +27,10 @@ extern "C" {
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libswscale/swscale.h> #include <libswscale/swscale.h>
#include <vitamtp.h>
} }
#include <vitamtp.h>
class AVDecoder class AVDecoder
{ {
public: public:
@@ -46,10 +47,10 @@ public:
class AvInit class AvInit
{ {
public: public:
AvInit() AvInit()
{ {
av_register_all(); av_register_all();
} }
}; };
static AvInit init; static AvInit init;

View File

@@ -25,10 +25,6 @@ BaseWorker::BaseWorker(QObject *parent) :
thread = NULL; thread = NULL;
} }
void BaseWorker::onFinished()
{
}
void BaseWorker::start(const char *thread_name) void BaseWorker::start(const char *thread_name)
{ {
thread = new QThread(); thread = new QThread();
@@ -37,25 +33,21 @@ void BaseWorker::start(const char *thread_name)
thread->setObjectName(thread_name); thread->setObjectName(thread_name);
} }
// Move this service to a new thread
this->moveToThread(thread); this->moveToThread(thread);
// The main loop will be executed when the thread
// signals that it has started.
connect(thread, SIGNAL(started()), this, SLOT(process())); connect(thread, SIGNAL(started()), this, SLOT(process()));
connect(this, SIGNAL(finished()), this, SLOT(onFinished()));
// Make sure that we notify ourselves when the thread connect(this, SIGNAL(finished()), thread, SLOT(quit()), Qt::DirectConnection);
// is finished in order to correctly clean-up the thread.
connect(thread, SIGNAL(finished()), this, SLOT(onFinished()));
// The thread will quit when the sercives
// signals that it's finished.
connect(this, SIGNAL(finished()), thread, SLOT(quit()));
// The thread will be scheduled for deletion when the
// service signals that it's finished
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
// Start the thread
thread->start(); thread->start();
} }
bool BaseWorker::wait()
{
return thread->wait();
}
void BaseWorker::onFinished()
{
}

View File

@@ -31,6 +31,7 @@ public:
explicit BaseWorker(QObject *parent = 0); explicit BaseWorker(QObject *parent = 0);
void start(const char *thread_name = NULL); void start(const char *thread_name = NULL);
bool wait();
private: private:
QThread *thread; QThread *thread;
@@ -38,11 +39,11 @@ private:
signals: signals:
void finished(); void finished();
public slots:
virtual void onFinished();
protected slots: protected slots:
virtual void process() = 0; virtual void process() = 0;
private slots:
virtual void onFinished();
}; };
#endif // BASEWORKER_H #endif // BASEWORKER_H

View File

@@ -31,7 +31,7 @@ bool DeviceCapability::exchangeInfo(vita_device_t *device)
if(vita_info.protocolVersion > VITAMTP_PROTOCOL_MAX_VERSION) { if(vita_info.protocolVersion > VITAMTP_PROTOCOL_MAX_VERSION) {
qWarning("Vita wants protocol version %08d while we only support %08d. Attempting to continue.", qWarning("Vita wants protocol version %08d while we only support %08d. Attempting to continue.",
vita_info.protocolVersion, VITAMTP_PROTOCOL_MAX_VERSION); vita_info.protocolVersion, VITAMTP_PROTOCOL_MAX_VERSION);
} }
QString hostname = QHostInfo::localHostName(); QString hostname = QHostInfo::localHostName();

View File

@@ -20,9 +20,7 @@
#ifndef CAPABILITY_H #ifndef CAPABILITY_H
#define CAPABILITY_H #define CAPABILITY_H
extern "C" {
#include <vitamtp.h> #include <vitamtp.h>
}
class DeviceCapability class DeviceCapability
{ {
@@ -31,10 +29,18 @@ public:
bool exchangeInfo(vita_device_t *device); bool exchangeInfo(vita_device_t *device);
//TODO: vita_info_t doesn't retrieve this info, update vitamtp to get it //TODO: vita_info_t doesn't retrieve this info, update vitamtp to get it
const char *getVersion() { return ""; } const char *getVersion() {
const char *getProtocol() { return ""; } return "";
const char *getOnlineId() { return "PS Vita"; } }
const char *getModelInfo() { return ""; } const char *getProtocol() {
return "";
}
const char *getOnlineId() {
return "PS Vita";
}
const char *getModelInfo() {
return "";
}
private: private:
capability_info_t *generate_pc_capability_info(); capability_info_t *generate_pc_capability_info();

75
clientmanager.cpp Normal file
View File

@@ -0,0 +1,75 @@
#include "clientmanager.h"
#include "cmaclient.h"
ClientManager::ClientManager(QObject *parent) :
QObject(parent)
{
}
void ClientManager::start()
{
// initializing database for the first use
refreshDatabase();
CmaEvent::db = &db;
thread_count = 2;
qDebug("Starting cma threads");
CmaClient *client;
usb_thread = new QThread();
client = new CmaClient();
usb_thread->setObjectName("usb_thread");
connect(usb_thread, SIGNAL(started()), client, SLOT(connectUsb()));
connect(client, SIGNAL(receivedPin(int)), this, SIGNAL(receivedPin(int)));
connect(client, SIGNAL(finished()), usb_thread, SLOT(quit()), Qt::DirectConnection);
connect(usb_thread, SIGNAL(finished()), usb_thread, SLOT(deleteLater()));
connect(usb_thread, SIGNAL(finished()), this, SLOT(threadStopped()));
connect(usb_thread, SIGNAL(finished()), client, SLOT(deleteLater()));
connect(client, SIGNAL(deviceConnected(QString)), this, SIGNAL(deviceConnected(QString)));
connect(client, SIGNAL(deviceDisconnected()), this, SIGNAL(deviceDisconnected()));
connect(client, SIGNAL(refreshDatabase()), this, SLOT(refreshDatabase()));
client->moveToThread(usb_thread);
usb_thread->start();
wireless_thread = new QThread();
client = new CmaClient();
wireless_thread->setObjectName("wireless_thread");
connect(wireless_thread, SIGNAL(started()), client, SLOT(connectWireless()));
connect(client, SIGNAL(finished()), wireless_thread, SLOT(quit()), Qt::DirectConnection);
connect(wireless_thread, SIGNAL(finished()), wireless_thread, SLOT(deleteLater()));
connect(wireless_thread, SIGNAL(finished()), this, SLOT(threadStopped()));
connect(wireless_thread, SIGNAL(finished()), client, SLOT(deleteLater()));
connect(client, SIGNAL(deviceConnected(QString)), this, SIGNAL(deviceConnected(QString)));
connect(client, SIGNAL(deviceDisconnected()), this, SIGNAL(deviceDisconnected()));
connect(client, SIGNAL(refreshDatabase()), this, SLOT(refreshDatabase()));
client->moveToThread(wireless_thread);
wireless_thread->start();
}
void ClientManager::refreshDatabase()
{
QMutexLocker locker(&db.mutex);
db.destroy();
int count = db.create();
qDebug("Added %i entries to the database", count);
}
void ClientManager::stop()
{
CmaClient::stop();
}
void ClientManager::threadStopped()
{
mutex.lock();
if(--thread_count == 0) {
emit stopped();
}
mutex.unlock();
}

37
clientmanager.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef CLIENTMANAGER_H
#define CLIENTMANAGER_H
#include "database.h"
#include <QObject>
#include <QThread>
class ClientManager : public QObject
{
Q_OBJECT
public:
explicit ClientManager(QObject *parent = 0);
void start();
void stop();
private:
int thread_count;
QMutex mutex;
Database db;
QThread *usb_thread;
QThread *wireless_thread;
signals:
void deviceConnected(QString);
void deviceDisconnected();
void receivedPin(int);
void stopped();
private slots:
void refreshDatabase();
void threadStopped();
};
#endif // CLIENTMANAGER_H

View File

@@ -28,13 +28,13 @@
#include <vitamtp.h> #include <vitamtp.h>
const QString CmaBroadcast::broadcast_reply = const QString CmaBroadcast::broadcast_reply =
"%1\r\n" "%1\r\n"
"host-id:%2\r\n" "host-id:%2\r\n"
"host-type:%3\r\n" "host-type:%3\r\n"
"host-name:%4\r\n" "host-name:%4\r\n"
"host-mtp-protocol-version:%5\r\n" "host-mtp-protocol-version:%5\r\n"
"host-request-port:%6\r\n" "host-request-port:%6\r\n"
"host-wireless-protocol-version:%7\r\n"; "host-wireless-protocol-version:%7\r\n";
const char *CmaBroadcast::broadcast_query = "SRCH * HTTP/1.1\r\n"; const char *CmaBroadcast::broadcast_query = "SRCH * HTTP/1.1\r\n";
@@ -85,10 +85,10 @@ void CmaBroadcast::setAvailable()
QMutexLocker locker(&mutex); QMutexLocker locker(&mutex);
reply.clear(); reply.clear();
reply.insert(0, broadcast_reply reply.insert(0, broadcast_reply
.arg(broadcast_ok, uuid, "win", hostname) .arg(broadcast_ok, uuid, "win", hostname)
.arg(VITAMTP_PROTOCOL_MAX_VERSION, 8, 10, QChar('0')) .arg(VITAMTP_PROTOCOL_MAX_VERSION, 8, 10, QChar('0'))
.arg(QCMA_REQUEST_PORT) .arg(QCMA_REQUEST_PORT)
.arg(VITAMTP_WIRELESS_MAX_VERSION, 8, 10, QChar('0'))); .arg(VITAMTP_WIRELESS_MAX_VERSION, 8, 10, QChar('0')));
reply.append('\0'); reply.append('\0');
} }
@@ -97,9 +97,9 @@ void CmaBroadcast::setUnavailable()
QMutexLocker locker(&mutex); QMutexLocker locker(&mutex);
reply.clear(); reply.clear();
reply.insert(0, broadcast_reply reply.insert(0, broadcast_reply
.arg(broadcast_unavailable, uuid, "win", hostname) .arg(broadcast_unavailable, uuid, "win", hostname)
.arg(VITAMTP_PROTOCOL_MAX_VERSION, 8, 10, QChar('0')) .arg(VITAMTP_PROTOCOL_MAX_VERSION, 8, 10, QChar('0'))
.arg(QCMA_REQUEST_PORT) .arg(QCMA_REQUEST_PORT)
.arg(VITAMTP_WIRELESS_MAX_VERSION, 8, 10, QChar('0'))); .arg(VITAMTP_WIRELESS_MAX_VERSION, 8, 10, QChar('0')));
reply.append('\0'); reply.append('\0');
} }

View File

@@ -23,7 +23,7 @@
#include "cmaevent.h" #include "cmaevent.h"
#include "utils.h" #include "utils.h"
#include "QApplication" #include <QApplication>
#include <QDateTime> #include <QDateTime>
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
@@ -33,9 +33,12 @@
QMutex CmaClient::mutex; QMutex CmaClient::mutex;
QMutex CmaClient::runner; QMutex CmaClient::runner;
QMutex CmaClient::eloop; QMutex CmaClient::cancel;
bool CmaClient::is_running = true; QSemaphore CmaClient::sema;
bool CmaClient::event_loop_enabled = true;
bool CmaClient::is_active = true;
bool CmaClient::in_progress = false;
int CmaClient::is_cancelled = false;
CmaClient *CmaClient::this_object = NULL; CmaClient *CmaClient::this_object = NULL;
@@ -43,43 +46,28 @@ CmaClient::CmaClient(QObject *parent) :
QObject(parent) QObject(parent)
{ {
this_object = this; this_object = this;
device = NULL;
}
bool CmaClient::isRunning()
{
QMutexLocker locker(&runner);
return is_running;
}
void CmaClient::setRunning(bool state)
{
QMutexLocker locker(&runner);
is_running = state;
} }
void CmaClient::connectUsb() void CmaClient::connectUsb()
{ {
vita_device_t *vita; vita_device_t *vita;
//int num_tries = 0;
qDebug() << "Starting usb_thread:" << QThread::currentThreadId(); qDebug() << "Starting usb_thread:" << QThread::currentThreadId();
while(isRunning()) { do {
if((vita = VitaMTP_Get_First_USB_Vita()) !=NULL) { if((vita = VitaMTP_Get_First_USB_Vita()) !=NULL) {
cancel_wireless = 1;
processNewConnection(vita); processNewConnection(vita);
VitaMTP_Close_USB_Vita();
} else { } else {
//qDebug("No Vita detected via USB, attempt %i", +num_tries++); //TODO: replace this with an event-driven setup
if(mutex.tryLock()) { Sleeper::msleep(2000);
mutex.unlock(); mutex.lock();
Sleeper::msleep(2000); if(in_progress) {
} else { sema.acquire();
mutex.lock();
mutex.unlock();
} }
mutex.unlock();
} }
} } while(isActive());
qDebug("Finishing usb_thread"); qDebug("Finishing usb_thread");
emit finished(); emit finished();
@@ -88,24 +76,24 @@ void CmaClient::connectUsb()
void CmaClient::connectWireless() void CmaClient::connectWireless()
{ {
vita_device_t *vita; vita_device_t *vita;
wireless_host_info_t host; wireless_host_info_t host = {NULL, NULL, NULL, QCMA_REQUEST_PORT};
host.port = QCMA_REQUEST_PORT;
typedef CmaClient CC; typedef CmaClient CC;
cancel_wireless = 0;
qDebug() << "Starting wireless_thread:" << QThread::currentThreadId(); qDebug() << "Starting wireless_thread:" << QThread::currentThreadId();
while(isRunning()) { do {
if((vita = VitaMTP_Get_First_Wireless_Vita(&host, 0, &cancel_wireless, CC::deviceRegistered, CC::generatePin)) != NULL) { if((vita = VitaMTP_Get_First_Wireless_Vita(&host, 0, CC::cancelCallback, CC::deviceRegistered, CC::generatePin)) != NULL) {
processNewConnection(vita); processNewConnection(vita);
} else { } else {
qDebug("Wireless listener was cancelled"); Sleeper::msleep(2000);
// wait until the event loop of the usb thread is finished
mutex.lock(); mutex.lock();
cancel_wireless = 0; if(in_progress) {
mutex.unlock(); sema.acquire();
}
mutex.unlock();;
} }
} } while(isActive());
qDebug("Finishing wireless_thread"); qDebug("Finishing wireless_thread");
emit finished(); emit finished();
} }
@@ -113,9 +101,9 @@ void CmaClient::connectWireless()
void CmaClient::processNewConnection(vita_device_t *device) void CmaClient::processNewConnection(vita_device_t *device)
{ {
QMutexLocker locker(&mutex); QMutexLocker locker(&mutex);
in_progress = true;
broadcast.setUnavailable(); broadcast.setUnavailable();
this->device = device;
qDebug("Vita connected: id %s", VitaMTP_Get_Identification(device)); qDebug("Vita connected: id %s", VitaMTP_Get_Identification(device));
DeviceCapability *vita_info = new DeviceCapability(); DeviceCapability *vita_info = new DeviceCapability();
@@ -124,10 +112,17 @@ void CmaClient::processNewConnection(vita_device_t *device)
} else { } else {
// Conection successful, inform the user // Conection successful, inform the user
emit deviceConnected(QString(tr("Connected to ")) + vita_info->getOnlineId()); emit deviceConnected(QString(tr("Connected to ")) + vita_info->getOnlineId());
enterEventLoop(); enterEventLoop(device);
} }
VitaMTP_SendHostStatus(device, VITA_HOST_STATUS_EndConnection);
VitaMTP_Release_Device(device);
emit deviceDisconnected();
broadcast.setAvailable(); broadcast.setAvailable();
in_progress = false;
sema.release();
} }
int CmaClient::deviceRegistered(const char *deviceid) int CmaClient::deviceRegistered(const char *deviceid)
@@ -146,54 +141,67 @@ int CmaClient::generatePin(wireless_vita_info_t *info, int *p_err)
return pin; return pin;
} }
void CmaClient::enterEventLoop() int CmaClient::cancelCallback()
{
QMutexLocker locker(&cancel);
return is_cancelled;
}
void CmaClient::enterEventLoop(vita_device_t *device)
{ {
vita_event_t event; vita_event_t event;
qDebug("Starting event loop"); qDebug("Starting event loop");
event_loop_enabled = true; CmaEvent eventLoop(device);
eventLoop.start("event_thread");
while(isEventLoopEnabled()) { while(isActive()) {
if(VitaMTP_Read_Event(device, &event) < 0) { if(VitaMTP_Read_Event(device, &event) < 0) {
qWarning("Error reading event from Vita."); qWarning("Error reading event from Vita.");
break; break;
} }
CmaEvent *vita_event = new CmaEvent(device, event);
connect(vita_event, SIGNAL(finishedEventLoop()), this, SLOT(finishEventLoop())); // do not create a event for this since there aren't more events to read
connect(vita_event, SIGNAL(refreshDatabase()), this, SIGNAL(refreshDatabase())); if(event.Code == PTP_EC_VITA_RequestTerminate) {
vita_event->start("cma_event"); qDebug("Terminating event thread");
break;
// this one shuold be processed inmediately
} else if(event.Code == PTP_EC_VITA_RequestCancelTask) {
quint32 eventIdToCancel = event.Param2;
qDebug("Cancelling event %d", eventIdToCancel);
VitaMTP_CancelTask(device, eventIdToCancel);
continue;
}
// the events are processed synchronously except for cancel/terminate
qDebug("Sending new event");
eventLoop.setEvent(event);
} }
}
bool CmaClient::isEventLoopEnabled() eventLoop.stop();
{ eventLoop.wait();
QMutexLocker locker(&eloop); qDebug("Finishing event loop");
return event_loop_enabled;
}
void CmaClient::finishEventLoop()
{
QMutexLocker locker(&eloop);
event_loop_enabled = false;
}
void CmaClient::close()
{
if(device) {
VitaMTP_SendHostStatus(device, VITA_HOST_STATUS_EndConnection);
VitaMTP_Release_Device(device);
device = NULL;
}
} }
void CmaClient::stop() void CmaClient::stop()
{ {
CmaClient::setRunning(false); CmaClient::setActive(false);
CmaClient::finishEventLoop(); cancel.lock();
is_cancelled = true;
cancel.unlock();
} }
CmaClient::~CmaClient() bool CmaClient::isActive()
{ {
close(); QMutexLocker locker(&runner);
return is_active;
} }
void CmaClient::setActive(bool state)
{
QMutexLocker locker(&runner);
is_active = state;
}

View File

@@ -24,47 +24,47 @@
#include "cmabroadcast.h" #include "cmabroadcast.h"
#include "cmaobject.h" #include "cmaobject.h"
#include "database.h" #include "database.h"
#include "cmaevent.h"
#include <QObject> #include <QObject>
#include <QSemaphore>
#include <QString> #include <QString>
#include <QWaitCondition>
extern "C" {
#include <vitamtp.h> #include <vitamtp.h>
}
class CmaClient : public QObject class CmaClient : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit CmaClient(QObject *parent = 0); explicit CmaClient(QObject *parent = 0);
~CmaClient();
void launch(); void launch();
private: private:
static bool isRunning(); static bool isActive();
static void setRunning(bool state); static void setActive(bool state);
static bool isEventLoopEnabled(); static bool isEventLoopEnabled();
static void setEventLoop(bool state); static void setEventLoop(bool state);
void enterEventLoop(); void enterEventLoop(vita_device_t *device);
void processNewConnection(vita_device_t *device); void processNewConnection(vita_device_t *device);
static int deviceRegistered(const char *deviceid); static int deviceRegistered(const char *deviceid);
static int generatePin(wireless_vita_info_t *info, int *p_err); static int generatePin(wireless_vita_info_t *info, int *p_err);
static int cancelCallback();
int cancel_wireless;
CmaBroadcast broadcast; CmaBroadcast broadcast;
vita_device_t *device; static bool is_active;
static bool event_loop_enabled; static bool in_progress;
static bool is_running; static int is_cancelled;
static CmaClient *this_object; static CmaClient *this_object;
static QMutex mutex; static QMutex mutex;
static QMutex runner; static QMutex runner;
static QMutex eloop; static QMutex cancel;
static QSemaphore sema;
signals: signals:
void newEvent(vita_event_t event);
void receivedPin(int); void receivedPin(int);
void deviceDetected(); void deviceDetected();
void deviceConnected(QString); void deviceConnected(QString);
@@ -73,13 +73,11 @@ signals:
void finished(); void finished();
public slots: public slots:
void close();
static void stop(); static void stop();
private slots: private slots:
void connectUsb(); void connectUsb();
void connectWireless(); void connectWireless();
static void finishEventLoop();
}; };
#endif // CMACLIENT_H #endif // CMACLIENT_H

View File

@@ -7,16 +7,59 @@
#include <QSettings> #include <QSettings>
#include <QUrl> #include <QUrl>
Database CmaEvent::db; Database *CmaEvent::db = NULL;
metadata_t CmaEvent::g_thumbmeta = {0, 0, 0, NULL, NULL, 0, 0, 0, Thumbnail, {{18, 144, 80, 0, 1, 1.0f, 2}}, NULL}; metadata_t CmaEvent::g_thumbmeta = {0, 0, 0, NULL, NULL, 0, 0, 0, Thumbnail, {{18, 144, 80, 0, 1, 1.0f, 2}}, NULL};
CmaEvent::CmaEvent(vita_device_t *s_device, vita_event_t s_event, QObject *parent) : CmaEvent::CmaEvent(vita_device_t *s_device, QObject *parent) :
BaseWorker(parent), device(s_device), t_event(s_event) BaseWorker(parent), device(s_device), is_active(true)
{ {
} }
void CmaEvent::process() void CmaEvent::process()
{
qDebug() << "Starting event_thread:" << QThread::currentThreadId();
while(true) {
sema.acquire();
if(!isActive()) {
break;
}
mutex.lock();
processEvent();
mutex.unlock();
}
qDebug("Finishing event_thread");
emit finished();
}
bool CmaEvent::isActive()
{
QMutexLocker locker(&active);
return is_active;
}
void CmaEvent::stop()
{
QMutexLocker locker(&active);
is_active = false;
sema.release();
}
void CmaEvent::setDevice(vita_device_t *device)
{
QMutexLocker locker(&mutex);
this->device = device;
}
void CmaEvent::setEvent(vita_event_t event)
{
mutex.lock();
this->t_event = event;
mutex.unlock();
sema.release();
}
void CmaEvent::processEvent()
{ {
qDebug() << "Starting event_thread:" << QThread::currentThreadId(); qDebug() << "Starting event_thread:" << QThread::currentThreadId();
switch(t_event.Code) { switch(t_event.Code) {
@@ -29,9 +72,6 @@ void CmaEvent::process()
case PTP_EC_VITA_RequestSendObject: case PTP_EC_VITA_RequestSendObject:
vitaEventSendObject(&t_event, t_event.Param1); vitaEventSendObject(&t_event, t_event.Param1);
break; break;
case PTP_EC_VITA_RequestCancelTask: // unimplemented
vitaEventCancelTask(&t_event, t_event.Param1);
break;
case PTP_EC_VITA_RequestSendHttpObjectFromURL: case PTP_EC_VITA_RequestSendHttpObjectFromURL:
vitaEventSendHttpObjectFromURL(&t_event, t_event.Param1); vitaEventSendHttpObjectFromURL(&t_event, t_event.Param1);
break; break;
@@ -80,15 +120,10 @@ void CmaEvent::process()
case PTP_EC_VITA_RequestSendNPAccountInfo: case PTP_EC_VITA_RequestSendNPAccountInfo:
vitaEventSendNPAccountInfo(&t_event, t_event.Param1); vitaEventSendNPAccountInfo(&t_event, t_event.Param1);
break; break;
case PTP_EC_VITA_RequestTerminate:
vitaEventRequestTerminate(&t_event, t_event.Param1);
break;
default: default:
vitaEventUnimplementated(&t_event, t_event.Param1); vitaEventUnimplementated(&t_event, t_event.Param1);
} }
qDebug("Ended event, code: 0x%x, id: %d", t_event.Code, t_event.Param1); qDebug("Ended event, code: 0x%x, id: %d", t_event.Code, t_event.Param1);
emit finished();
} }
quint16 CmaEvent::processAllObjects(CMAObject *parent, quint32 handle) quint16 CmaEvent::processAllObjects(CMAObject *parent, quint32 handle)
@@ -106,12 +141,12 @@ quint16 CmaEvent::processAllObjects(CMAObject *parent, quint32 handle)
return PTP_RC_VITA_Invalid_Data; return PTP_RC_VITA_Invalid_Data;
} }
CMAObject *object = db.pathToObject(remote_meta.name, parent->metadata.ohfi); CMAObject *object = db->pathToObject(remote_meta.name, parent->metadata.ohfi);
if(object) { if(object) {
qDebug("Deleting %s", object->path.toStdString().c_str()); qDebug("Deleting %s", object->path.toStdString().c_str());
removeRecursively(object->path); removeRecursively(object->path);
db.remove(object); db->remove(object);
} }
QDir dir(parent->path); QDir dir(parent->path);
@@ -140,7 +175,7 @@ quint16 CmaEvent::processAllObjects(CMAObject *parent, quint32 handle)
object = new CMAObject(parent); object = new CMAObject(parent);
object->initObject(info); object->initObject(info);
object->metadata.handle = remote_meta.handle; object->metadata.handle = remote_meta.handle;
db.append(parent->metadata.ohfi, object); db->append(parent->metadata.ohfi, object);
free(remote_meta.name); free(remote_meta.name);
qDebug("Added object %s with OHFI %i to database", object->metadata.path, object->metadata.ohfi); qDebug("Added object %s with OHFI %i to database", object->metadata.path, object->metadata.ohfi);
@@ -151,7 +186,7 @@ quint16 CmaEvent::processAllObjects(CMAObject *parent, quint32 handle)
if(ret != PTP_RC_OK) { if(ret != PTP_RC_OK) {
qDebug("Deleteting object with OHFI %d", object->metadata.ohfi); qDebug("Deleteting object with OHFI %d", object->metadata.ohfi);
db.remove(object); db->remove(object);
free(data.fileData); free(data.fileData);
return ret; return ret;
} }
@@ -173,9 +208,9 @@ void CmaEvent::vitaEventGetTreatObject(vita_event_t *event, int eventId)
return; return;
} }
QMutexLocker locker(&db.mutex); QMutexLocker locker(&db->mutex);
CMAObject *parent = db.ohfiToObject(treatObject.ohfiParent); CMAObject *parent = db->ohfiToObject(treatObject.ohfiParent);
if(parent == NULL) { if(parent == NULL) {
qWarning("Cannot find parent OHFI %d", treatObject.ohfiParent); qWarning("Cannot find parent OHFI %d", treatObject.ohfiParent);
@@ -196,14 +231,14 @@ void CmaEvent::vitaEventSendCopyConfirmationInfo(vita_event_t *event, int eventI
return; return;
} }
QMutexLocker locker(&db.mutex); QMutexLocker locker(&db->mutex);
quint64 size = 0; quint64 size = 0;
for(quint32 i = 0; i < info->count; i++) { for(quint32 i = 0; i < info->count; i++) {
CMAObject *object; CMAObject *object;
if((object = db.ohfiToObject(info->ohfi[i])) == NULL) { if((object = db->ohfiToObject(info->ohfi[i])) == NULL) {
qWarning("Cannot find OHFI %d", info->ohfi[i]); qWarning("Cannot find OHFI %d", info->ohfi[i]);
free(info); free(info);
return; return;
@@ -231,9 +266,9 @@ void CmaEvent::vitaEventSendObjectMetadataItems(vita_event_t *event, int eventId
return; return;
} }
QMutexLocker locker(&db.mutex); QMutexLocker locker(&db->mutex);
CMAObject *object = db.ohfiToObject(ohfi); CMAObject *object = db->ohfiToObject(ohfi);
if(object == NULL) { if(object == NULL) {
qWarning("Cannot find OHFI %d in database", ohfi); qWarning("Cannot find OHFI %d in database", ohfi);
@@ -259,13 +294,6 @@ void CmaEvent::vitaEventSendNPAccountInfo(vita_event_t *event, int eventId)
qWarning("Event 0x%x unimplemented!", event->Code); qWarning("Event 0x%x unimplemented!", event->Code);
} }
void CmaEvent::vitaEventRequestTerminate(vita_event_t *event, int eventId)
{
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
//qWarning("Event 0x%x unimplemented!", event->Code);
emit finishedEventLoop();
}
void CmaEvent::vitaEventUnimplementated(vita_event_t *event, int eventId) void CmaEvent::vitaEventUnimplementated(vita_event_t *event, int eventId)
{ {
qWarning("Unknown event not handled, code: 0x%x, id: %d", event->Code, eventId); qWarning("Unknown event not handled, code: 0x%x, id: %d", event->Code, eventId);
@@ -276,10 +304,10 @@ void CmaEvent::vitaEventSendNumOfObject(vita_event_t *event, int eventId)
{ {
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
QMutexLocker locker(&db.mutex); QMutexLocker locker(&db->mutex);
uint ohfi = event->Param2; uint ohfi = event->Param2;
int items = db.filterObjects(ohfi, NULL); int items = db->filterObjects(ohfi, NULL);
if(VitaMTP_SendNumOfObject(device, eventId, items) != PTP_RC_OK) { if(VitaMTP_SendNumOfObject(device, eventId, items) != PTP_RC_OK) {
qWarning("Error occured receiving object count for OHFI parent %d", ohfi); qWarning("Error occured receiving object count for OHFI parent %d", ohfi);
@@ -299,10 +327,10 @@ void CmaEvent::vitaEventSendObjectMetadata(vita_event_t *event, int eventId)
qWarning("GetBrowseInfo failed"); qWarning("GetBrowseInfo failed");
return; return;
} }
QMutexLocker locker(&db.mutex); QMutexLocker locker(&db->mutex);
metadata_t *meta; metadata_t *meta;
int count = db.filterObjects(browse.ohfiParent, &meta); // if meta is null, will return empty XML int count = db->filterObjects(browse.ohfiParent, &meta); // if meta is null, will return empty XML
qDebug("Sending %i metadata filtered objects for OHFI %d", count, browse.ohfiParent); qDebug("Sending %i metadata filtered objects for OHFI %d", count, browse.ohfiParent);
if(VitaMTP_SendObjectMetadata(device, eventId, meta) != PTP_RC_OK) { // send all objects with OHFI parent if(VitaMTP_SendObjectMetadata(device, eventId, meta) != PTP_RC_OK) { // send all objects with OHFI parent
@@ -318,12 +346,12 @@ void CmaEvent::vitaEventSendObject(vita_event_t *event, int eventId)
int ohfi = event->Param2; int ohfi = event->Param2;
QMutexLocker locker(&db.mutex); QMutexLocker locker(&db->mutex);
qDebug("Searching object with OHFI %d", ohfi); qDebug("Searching object with OHFI %d", ohfi);
Database::find_data iters; Database::find_data iters;
if(!db.find(ohfi, iters)) { if(!db->find(ohfi, iters)) {
qWarning("Failed to find OHFI %d", ohfi); qWarning("Failed to find OHFI %d", ohfi);
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI); VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_OHFI);
return; return;
@@ -363,7 +391,7 @@ void CmaEvent::vitaEventSendObject(vita_event_t *event, int eventId)
qDebug("Sending %s of %lu bytes to device", object->metadata.name, len); qDebug("Sending %s of %lu bytes to device", object->metadata.name, len);
qDebug("OHFI %d with handle 0x%08X", ohfi, parentHandle); qDebug("OHFI %d with handle 0x%08X", ohfi, parentHandle);
if(VitaMTP_SendObject(device, &parentHandle, &handle, &object->metadata, data) != PTP_RC_OK) { if(VitaMTP_SendObject(device, &parentHandle, &handle, &object->metadata, data, NULL) != PTP_RC_OK) {
qWarning("Sending of %s failed.", object->metadata.name); qWarning("Sending of %s failed.", object->metadata.name);
file.unmap(data); file.unmap(data);
return; return;
@@ -389,14 +417,6 @@ void CmaEvent::vitaEventSendObject(vita_event_t *event, int eventId)
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Data); // TODO: Send thumbnail VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Invalid_Data); // TODO: Send thumbnail
} }
void CmaEvent::vitaEventCancelTask(vita_event_t *event, int eventId)
{
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
int eventIdToCancel = event->Param2;
VitaMTP_CancelTask(device, eventIdToCancel);
}
void CmaEvent::vitaEventSendHttpObjectFromURL(vita_event_t *event, int eventId) void CmaEvent::vitaEventSendHttpObjectFromURL(vita_event_t *event, int eventId)
{ {
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
@@ -451,9 +471,9 @@ void CmaEvent::vitaEventSendObjectStatus(vita_event_t *event, int eventId)
return; return;
} }
QMutexLocker locker(&db.mutex); QMutexLocker locker(&db->mutex);
CMAObject *object = db.pathToObject(objectstatus.title, objectstatus.ohfiRoot); CMAObject *object = db->pathToObject(objectstatus.title, objectstatus.ohfiRoot);
if(object == NULL) { // not in database, don't return metadata if(object == NULL) { // not in database, don't return metadata
qDebug("Object %s not in database (OHFI: %i). Sending OK response for non-existence", objectstatus.title, objectstatus.ohfiRoot); qDebug("Object %s not in database (OHFI: %i). Sending OK response for non-existence", objectstatus.title, objectstatus.ohfiRoot);
@@ -477,10 +497,10 @@ void CmaEvent::vitaEventSendObjectThumb(vita_event_t *event, int eventId)
{ {
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
QMutexLocker locker(&db.mutex); QMutexLocker locker(&db->mutex);
int ohfi = event->Param2; int ohfi = event->Param2;
CMAObject *object = db.ohfiToObject(ohfi); CMAObject *object = db->ohfiToObject(ohfi);
if(object == NULL) { if(object == NULL) {
qWarning("Cannot find OHFI %d in database.", ohfi); qWarning("Cannot find OHFI %d in database.", ohfi);
@@ -515,10 +535,10 @@ void CmaEvent::vitaEventDeleteObject(vita_event_t *event, int eventId)
{ {
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
QMutexLocker locker(&db.mutex); QMutexLocker locker(&db->mutex);
int ohfi = event->Param2; int ohfi = event->Param2;
CMAObject *object = db.ohfiToObject(ohfi); CMAObject *object = db->ohfiToObject(ohfi);
if(object == NULL) { if(object == NULL) {
qWarning("OHFI %d not found", ohfi); qWarning("OHFI %d not found", ohfi);
@@ -528,7 +548,7 @@ void CmaEvent::vitaEventDeleteObject(vita_event_t *event, int eventId)
qDebug("Deleting %s, OHFI: %i", object->metadata.path, object->metadata.ohfi); qDebug("Deleting %s, OHFI: %i", object->metadata.path, object->metadata.ohfi);
removeRecursively(object->path); removeRecursively(object->path);
db.remove(object); db->remove(object);
VitaMTP_ReportResult(device, eventId, PTP_RC_OK); VitaMTP_ReportResult(device, eventId, PTP_RC_OK);
} }
@@ -547,7 +567,7 @@ void CmaEvent::vitaEventGetSettingInfo(vita_event_t *event, int eventId)
if(QSettings().value("lastAccountId").toString() != settingsinfo->current_account.accountId) { if(QSettings().value("lastAccountId").toString() != settingsinfo->current_account.accountId) {
db.setUUID(settingsinfo->current_account.accountId); db->setUUID(settingsinfo->current_account.accountId);
// set the database to be updated ASAP // set the database to be updated ASAP
emit refreshDatabase(); emit refreshDatabase();
} }
@@ -604,9 +624,9 @@ void CmaEvent::vitaEventSendPartOfObject(vita_event_t *event, int eventId)
return; return;
} }
QMutexLocker locker(&db.mutex); QMutexLocker locker(&db->mutex);
CMAObject *object = db.ohfiToObject(part_init.ohfi); CMAObject *object = db->ohfiToObject(part_init.ohfi);
if(object == NULL) { if(object == NULL) {
qWarning("Cannot find object for OHFI %d", part_init.ohfi); qWarning("Cannot find object for OHFI %d", part_init.ohfi);
@@ -644,9 +664,9 @@ void CmaEvent::vitaEventOperateObject(vita_event_t *event, int eventId)
return; return;
} }
QMutexLocker locker(&db.mutex); QMutexLocker locker(&db->mutex);
CMAObject *root = db.ohfiToObject(operateobject.ohfi); CMAObject *root = db->ohfiToObject(operateobject.ohfi);
// end for renaming only // end for renaming only
if(root == NULL) { if(root == NULL) {
@@ -665,7 +685,7 @@ void CmaEvent::vitaEventOperateObject(vita_event_t *event, int eventId)
} else { } else {
CMAObject *newobj = new CMAObject(root); CMAObject *newobj = new CMAObject(root);
newobj->initObject(QFileInfo(dir, operateobject.title)); newobj->initObject(QFileInfo(dir, operateobject.title));
db.append(operateobject.ohfi, newobj); db->append(operateobject.ohfi, newobj);
qDebug("Created folder %s with OHFI %d under parent %s", newobj->metadata.path, newobj->metadata.ohfi, root->metadata.path); qDebug("Created folder %s with OHFI %d under parent %s", newobj->metadata.path, newobj->metadata.ohfi, root->metadata.path);
VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, newobj->metadata.ohfi); VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, newobj->metadata.ohfi);
} }
@@ -681,7 +701,7 @@ void CmaEvent::vitaEventOperateObject(vita_event_t *event, int eventId)
} else { } else {
CMAObject *newobj = new CMAObject(root); CMAObject *newobj = new CMAObject(root);
newobj->initObject(file); newobj->initObject(file);
db.append(root->metadata.ohfi, newobj); db->append(root->metadata.ohfi, newobj);
qDebug("Created file %s with OHFI %d under parent %s", newobj->metadata.path, newobj->metadata.ohfi, root->metadata.path); qDebug("Created file %s with OHFI %d under parent %s", newobj->metadata.path, newobj->metadata.ohfi, root->metadata.path);
VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, newobj->metadata.ohfi); VitaMTP_ReportResultWithParam(device, eventId, PTP_RC_OK, newobj->metadata.ohfi);
} }
@@ -696,7 +716,7 @@ void CmaEvent::vitaEventOperateObject(vita_event_t *event, int eventId)
//rename the current object //rename the current object
root->rename(operateobject.title); root->rename(operateobject.title);
Database::find_data iters; Database::find_data iters;
db.find(root->metadata.ohfi, iters); db->find(root->metadata.ohfi, iters);
// rename the rest of the list only if has the renamed parent in some part of the chain // rename the rest of the list only if has the renamed parent in some part of the chain
while(iters.it != iters.end) { while(iters.it != iters.end) {
@@ -740,8 +760,8 @@ void CmaEvent::vitaEventGetPartOfObject(vita_event_t *event, int eventId)
return; return;
} }
QMutexLocker locker(&db.mutex); QMutexLocker locker(&db->mutex);
CMAObject *object = db.ohfiToObject(part_init.ohfi); CMAObject *object = db->ohfiToObject(part_init.ohfi);
if(object == NULL) { if(object == NULL) {
qWarning("Cannot find OHFI %d", part_init.ohfi); qWarning("Cannot find OHFI %d", part_init.ohfi);
@@ -771,10 +791,10 @@ void CmaEvent::vitaEventSendStorageSize(vita_event_t *event, int eventId)
{ {
qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId); qDebug("Event recieved in %s, code: 0x%x, id: %d", Q_FUNC_INFO, event->Code, eventId);
QMutexLocker locker(&db.mutex); QMutexLocker locker(&db->mutex);
int ohfi = event->Param2; int ohfi = event->Param2;
CMAObject *object = db.ohfiToObject(ohfi); CMAObject *object = db->ohfiToObject(ohfi);
if(object == NULL) { if(object == NULL) {
qWarning("Error: Cannot find OHFI %d", ohfi); qWarning("Error: Cannot find OHFI %d", ohfi);
@@ -825,9 +845,9 @@ void CmaEvent::vitaEventCheckExistance(vita_event_t *event, int eventId)
return; return;
} }
QMutexLocker locker(&db.mutex); QMutexLocker locker(&db->mutex);
CMAObject *object = db.pathToObject(existance.name, 0); CMAObject *object = db->pathToObject(existance.name, 0);
if(object == NULL) { if(object == NULL) {
VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Different_Object); VitaMTP_ReportResult(device, eventId, PTP_RC_VITA_Different_Object);

View File

@@ -6,6 +6,7 @@
#include "baseworker.h" #include "baseworker.h"
#include <QObject> #include <QObject>
#include <QSemaphore>
#include <vitamtp.h> #include <vitamtp.h>
@@ -13,16 +14,15 @@ class CmaEvent : public BaseWorker
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit CmaEvent(vita_device_t *s_device, vita_event_t s_event, QObject *parent = 0); explicit CmaEvent(vita_device_t *s_device, QObject *parent = 0);
static Database db; static Database *db;
private: private:
uint16_t processAllObjects(CMAObject *parent, uint32_t handle); uint16_t processAllObjects(CMAObject *parent, uint32_t handle);
void vitaEventSendObject(vita_event_t *event, int eventId); void vitaEventSendObject(vita_event_t *event, int eventId);
void vitaEventSendObjectMetadata(vita_event_t *event, int eventId); void vitaEventSendObjectMetadata(vita_event_t *event, int eventId);
void vitaEventSendNumOfObject(vita_event_t *event, int eventId); void vitaEventSendNumOfObject(vita_event_t *event, int eventId);
void vitaEventCancelTask(vita_event_t *event, int eventId);
void vitaEventSendHttpObjectFromURL(vita_event_t *event, int eventId); void vitaEventSendHttpObjectFromURL(vita_event_t *event, int eventId);
void vitaEventUnimplementated(vita_event_t *event, int eventId); void vitaEventUnimplementated(vita_event_t *event, int eventId);
void vitaEventSendObjectStatus(vita_event_t *event, int eventId); void vitaEventSendObjectStatus(vita_event_t *event, int eventId);
@@ -39,10 +39,17 @@ private:
void vitaEventSendCopyConfirmationInfo(vita_event_t *event, int eventId); void vitaEventSendCopyConfirmationInfo(vita_event_t *event, int eventId);
void vitaEventSendObjectMetadataItems(vita_event_t *event, int eventId); void vitaEventSendObjectMetadataItems(vita_event_t *event, int eventId);
void vitaEventSendNPAccountInfo(vita_event_t *event, int eventId); void vitaEventSendNPAccountInfo(vita_event_t *event, int eventId);
void vitaEventRequestTerminate(vita_event_t *event, int eventId);
void processEvent();
bool isActive();
void setDevice(vita_device_t *device);
vita_device_t *device; vita_device_t *device;
vita_event_t t_event; vita_event_t t_event;
bool is_active;
QMutex mutex;
QMutex active;
QSemaphore sema;
static metadata_t g_thumbmeta; static metadata_t g_thumbmeta;
@@ -53,7 +60,8 @@ signals:
public slots: public slots:
void process(); void process();
void setEvent(vita_event_t event);
void stop();
}; };
#endif // CMAEVENT_H #endif // CMAEVENT_H

View File

@@ -23,9 +23,7 @@
#include <QFileInfo> #include <QFileInfo>
#include <QString> #include <QString>
extern "C" {
#include <vitamtp.h> #include <vitamtp.h>
}
#define OHFI_OFFSET 1000 #define OHFI_OFFSET 1000

View File

@@ -34,70 +34,70 @@ void CMARootObject::initObject(const QString &path)
metadata.type = VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_REGULAR; metadata.type = VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_REGULAR;
switch(root_ohfi) { switch(root_ohfi) {
case VITA_OHFI_MUSIC: case VITA_OHFI_MUSIC:
metadata.dataType = Music; metadata.dataType = Music;
this->path = path; this->path = path;
num_filters = 2; num_filters = 2;
filters = new metadata_t[2]; filters = new metadata_t[2];
//createFilter(&filters[0], "Artists", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ARTISTS); //createFilter(&filters[0], "Artists", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ARTISTS);
createFilter(&filters[0], "Albums", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ALBUMS); createFilter(&filters[0], "Albums", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ALBUMS);
createFilter(&filters[1], "All", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_SONGS); createFilter(&filters[1], "All", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_SONGS);
//createFilter(&filters[3], "Genres", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_GENRES); //createFilter(&filters[3], "Genres", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_GENRES);
//createFilter(&filters[4], "Playlists", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_PLAYLISTS); //createFilter(&filters[4], "Playlists", VITA_DIR_TYPE_MASK_MUSIC | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_PLAYLISTS);
break; break;
case VITA_OHFI_PHOTO: case VITA_OHFI_PHOTO:
metadata.dataType = Photo; metadata.dataType = Photo;
this->path = path; this->path = path;
num_filters = 2; num_filters = 2;
filters = new metadata_t[2]; filters = new metadata_t[2];
createFilter(&filters[0], "Folders", VITA_DIR_TYPE_MASK_PHOTO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_REGULAR); createFilter(&filters[0], "Folders", VITA_DIR_TYPE_MASK_PHOTO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_REGULAR);
createFilter(&filters[1], "All", VITA_DIR_TYPE_MASK_PHOTO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ALL); createFilter(&filters[1], "All", VITA_DIR_TYPE_MASK_PHOTO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ALL);
break; break;
case VITA_OHFI_VIDEO: case VITA_OHFI_VIDEO:
metadata.dataType = Video; metadata.dataType = Video;
this->path = path; this->path = path;
num_filters = 2; num_filters = 2;
filters = new metadata_t[2]; filters = new metadata_t[2];
createFilter(&filters[0], "Folders", VITA_DIR_TYPE_MASK_VIDEO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_REGULAR); createFilter(&filters[0], "Folders", VITA_DIR_TYPE_MASK_VIDEO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_REGULAR);
createFilter(&filters[1], "All", VITA_DIR_TYPE_MASK_VIDEO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ALL); createFilter(&filters[1], "All", VITA_DIR_TYPE_MASK_VIDEO | VITA_DIR_TYPE_MASK_ROOT | VITA_DIR_TYPE_MASK_ALL);
break; break;
case VITA_OHFI_VITAAPP: case VITA_OHFI_VITAAPP:
metadata.dataType = App; metadata.dataType = App;
this->path = QDir(QDir(path).absoluteFilePath("APP")).absoluteFilePath(uuid); this->path = QDir(QDir(path).absoluteFilePath("APP")).absoluteFilePath(uuid);
num_filters = 0; num_filters = 0;
break; break;
case VITA_OHFI_PSPAPP: case VITA_OHFI_PSPAPP:
metadata.dataType = App; metadata.dataType = App;
this->path = QDir(QDir(path).absoluteFilePath("PGAME")).absoluteFilePath(uuid); this->path = QDir(QDir(path).absoluteFilePath("PGAME")).absoluteFilePath(uuid);
num_filters = 0; num_filters = 0;
break; break;
case VITA_OHFI_PSPSAVE: case VITA_OHFI_PSPSAVE:
metadata.dataType = SaveData; metadata.dataType = SaveData;
this->path = QDir(QDir(path).absoluteFilePath("PSAVEDATA")).absoluteFilePath(uuid); this->path = QDir(QDir(path).absoluteFilePath("PSAVEDATA")).absoluteFilePath(uuid);
num_filters = 0; num_filters = 0;
break; break;
case VITA_OHFI_PSXAPP: case VITA_OHFI_PSXAPP:
metadata.dataType = App; metadata.dataType = App;
this->path = QDir(QDir(path).absoluteFilePath("PSGAME")).absoluteFilePath(uuid); this->path = QDir(QDir(path).absoluteFilePath("PSGAME")).absoluteFilePath(uuid);
num_filters = 0; num_filters = 0;
break; break;
case VITA_OHFI_PSMAPP: case VITA_OHFI_PSMAPP:
metadata.dataType = App; metadata.dataType = App;
this->path = QDir(QDir(path).absoluteFilePath("PSM")).absoluteFilePath(uuid); this->path = QDir(QDir(path).absoluteFilePath("PSM")).absoluteFilePath(uuid);
num_filters = 0; num_filters = 0;
break; break;
case VITA_OHFI_BACKUP: case VITA_OHFI_BACKUP:
metadata.dataType = App; metadata.dataType = App;
this->path = QDir(QDir(path).absoluteFilePath("SYSTEM")).absoluteFilePath(uuid); this->path = QDir(QDir(path).absoluteFilePath("SYSTEM")).absoluteFilePath(uuid);
num_filters = 0; num_filters = 0;
} }
} }

View File

@@ -24,9 +24,7 @@
#include <QList> #include <QList>
extern "C" {
#include <vitamtp.h> #include <vitamtp.h>
}
class CMARootObject : public CMAObject class CMARootObject : public CMAObject
{ {

View File

@@ -91,33 +91,33 @@ void ConfigWidget::browseBtnPressed(int btn)
QLineEdit *lineedit; QLineEdit *lineedit;
switch(btn) { switch(btn) {
case BTN_PHOTO: case BTN_PHOTO:
lineedit = ui->photoPath; lineedit = ui->photoPath;
msg = tr("Select the folder to be used as a photo source"); msg = tr("Select the folder to be used as a photo source");
break; break;
case BTN_MUSIC: case BTN_MUSIC:
lineedit = ui->musicPath; lineedit = ui->musicPath;
msg = tr("Select the folder to be used as a music source"); msg = tr("Select the folder to be used as a music source");
break; break;
case BTN_VIDEO: case BTN_VIDEO:
lineedit = ui->videoPath; lineedit = ui->videoPath;
msg = tr("Select the folder to be used as a video source"); msg = tr("Select the folder to be used as a video source");
break; break;
case BTN_APPS: case BTN_APPS:
lineedit = ui->appPath; lineedit = ui->appPath;
msg = tr("Select the folder to be used to save PS Vita games and backups"); msg = tr("Select the folder to be used to save PS Vita games and backups");
break; break;
case BTN_URL: case BTN_URL:
lineedit = ui->urlPath; lineedit = ui->urlPath;
msg = tr("Select the folder to be used to fetch software updates"); msg = tr("Select the folder to be used to fetch software updates");
break; break;
default: default:
return; return;
} }
QFileDialog dialog; QFileDialog dialog;

View File

@@ -34,7 +34,8 @@ const QStringList Database::video_types = QStringList() << "mp4";
Database::Database(QObject *parent) : Database::Database(QObject *parent) :
QObject(parent), mutex(QMutex::Recursive) QObject(parent), mutex(QMutex::Recursive)
{ {
CMARootObject::uuid = QSettings().value("lastAccountId", "ffffffffffffffff").toString(); QString uuid = QSettings().value("lastAccountId", "ffffffffffffffff").toString();
CMARootObject::uuid = uuid;
} }
Database::~Database() Database::~Database()
@@ -63,26 +64,26 @@ int Database::create()
CMARootObject *obj = new CMARootObject(ohfi_array[i]); CMARootObject *obj = new CMARootObject(ohfi_array[i]);
switch(ohfi_array[i]) { switch(ohfi_array[i]) {
case VITA_OHFI_MUSIC: case VITA_OHFI_MUSIC:
obj->initObject(settings.value("musicPath").toString()); obj->initObject(settings.value("musicPath").toString());
break; break;
case VITA_OHFI_PHOTO: case VITA_OHFI_PHOTO:
obj->initObject(settings.value("photoPath").toString()); obj->initObject(settings.value("photoPath").toString());
break; break;
case VITA_OHFI_VIDEO: case VITA_OHFI_VIDEO:
obj->initObject(settings.value("videoPath").toString()); obj->initObject(settings.value("videoPath").toString());
break; break;
case VITA_OHFI_BACKUP: case VITA_OHFI_BACKUP:
case VITA_OHFI_VITAAPP: case VITA_OHFI_VITAAPP:
case VITA_OHFI_PSPAPP: case VITA_OHFI_PSPAPP:
case VITA_OHFI_PSPSAVE: case VITA_OHFI_PSPSAVE:
case VITA_OHFI_PSXAPP: case VITA_OHFI_PSXAPP:
case VITA_OHFI_PSMAPP: case VITA_OHFI_PSMAPP:
obj->initObject(settings.value("appsPath").toString()); obj->initObject(settings.value("appsPath").toString());
} }
root_list list; root_list list;
@@ -382,9 +383,6 @@ int Database::filterObjects(int ohfiParent, metadata_t **p_head)
*p_head = temp.next_metadata; *p_head = temp.next_metadata;
} }
if(p_head) {
dumpMetadataList(*p_head);
}
return numObjects; return numObjects;
} }

View File

@@ -27,9 +27,7 @@
#include <QMutex> #include <QMutex>
#include <QObject> #include <QObject>
extern "C" {
#include <vitamtp.h> #include <vitamtp.h>
}
class Database : public QObject class Database : public QObject
{ {

View File

@@ -17,6 +17,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef Q_OS_WIN32
#include <signal.h>
#endif
#include <QDebug> #include <QDebug>
#include <QThread> #include <QThread>
@@ -31,8 +35,8 @@ void noMessageOutput(QtMsgType type, const QMessageLogContext &, const QString &
void noMessageOutput(QtMsgType type, const char *msg) void noMessageOutput(QtMsgType type, const char *msg)
{ {
#endif #endif
Q_UNUSED(type); Q_UNUSED(type);
Q_UNUSED(msg); Q_UNUSED(msg);
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@@ -43,6 +47,13 @@ int main(int argc, char *argv[])
SingleApplication app(argc, argv); SingleApplication app(argc, argv);
#ifndef Q_OS_WIN32
// FIXME: libmtp sends SIGPIPE if a socket write fails crashing the whole app
// the proper fix is to libmtp to handle the cancel properly or ignoring
// SIGPIPE on the socket
signal(SIGPIPE, SIG_IGN);
#endif
if(!app.arguments().contains("--with-debug")) { if(!app.arguments().contains("--with-debug")) {
VitaMTP_Set_Logging(VitaMTP_NONE); VitaMTP_Set_Logging(VitaMTP_NONE);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)

View File

@@ -49,7 +49,7 @@ void MainWidget::checkSettings()
return; return;
} }
} }
startServer(); manager.start();
} }
void MainWidget::dialogResult(int result) void MainWidget::dialogResult(int result)
@@ -57,55 +57,18 @@ void MainWidget::dialogResult(int result)
if(result == QDialog::Accepted) { if(result == QDialog::Accepted) {
if(first_run) { if(first_run) {
first_run = false; first_run = false;
startServer(); manager.start();
} }
} else if(first_run) { } else if(first_run) {
qApp->quit(); qApp->quit();
} }
} }
void MainWidget::startServer()
{
qDebug("Starting cma threads");
QThread *thread;
CmaClient *client;
thread = new QThread();
client = new CmaClient();
thread->setObjectName("usb_thread");
connect(thread, SIGNAL(started()), client, SLOT(connectUsb()));
connect(client, SIGNAL(receivedPin(int)), this, SLOT(showPin(int)));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), client, SLOT(deleteLater()));
connectClientSignals(client);
client->moveToThread(thread);
thread->start();
thread = new QThread();
client = new CmaClient();
thread->setObjectName("wireless_thread");
connect(thread, SIGNAL(started()), client, SLOT(connectWireless()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), client, SLOT(deleteLater()));
connectClientSignals(client);
client->moveToThread(thread);
thread->start();
}
void MainWidget::stopServer() void MainWidget::stopServer()
{ {
CmaClient::stop(); setTrayTooltip(tr("Shutting down..."));
qApp->quit(); receiveMessage(tr("Stopping QCMA..."));
} manager.stop();
void MainWidget::refreshDatabase()
{
QMutexLocker locker(&CmaEvent::db.mutex);
CmaEvent::db.destroy();
int count = CmaEvent::db.create();
qDebug("Added %i entries to the database", count);
} }
void MainWidget::deviceDisconnect() void MainWidget::deviceDisconnect()
@@ -114,14 +77,6 @@ void MainWidget::deviceDisconnect()
receiveMessage(tr("The device has been disconnected")); receiveMessage(tr("The device has been disconnected"));
} }
void MainWidget::connectClientSignals(CmaClient *client)
{
connect(client, SIGNAL(deviceConnected(QString)), this, SLOT(receiveMessage(QString)));
connect(client, SIGNAL(deviceConnected(QString)), this, SLOT(setTrayTooltip(QString)));
connect(client, SIGNAL(deviceDisconnected()), this, SLOT(deviceDisconnect()));
connect(client, SIGNAL(refreshDatabase()), this, SLOT(refreshDatabase()));
}
void MainWidget::showPin(int pin) void MainWidget::showPin(int pin)
{ {
receiveMessage(QString(tr("Received PIN: %1").arg(QString::number(pin), 8, QChar('0')))); receiveMessage(QString(tr("Received PIN: %1").arg(QString::number(pin), 8, QChar('0'))));
@@ -129,10 +84,19 @@ void MainWidget::showPin(int pin)
void MainWidget::prepareApplication() void MainWidget::prepareApplication()
{ {
connect(&dialog, SIGNAL(finished(int)), this, SLOT(dialogResult(int))); connectSignals();
createTrayIcon(); createTrayIcon();
checkSettings(); checkSettings();
refreshDatabase(); }
void MainWidget::connectSignals()
{
connect(&dialog, SIGNAL(finished(int)), this, SLOT(dialogResult(int)));
connect(&manager, SIGNAL(stopped()), qApp, SLOT(quit()));
connect(&manager, SIGNAL(receivedPin(int)), this, SLOT(showPin(int)));
connect(&manager, SIGNAL(deviceConnected(QString)), this, SLOT(receiveMessage(QString)));
connect(&manager, SIGNAL(deviceConnected(QString)), this, SLOT(setTrayTooltip(QString)));
connect(&manager, SIGNAL(deviceDisconnected()), this, SLOT(deviceDisconnect()));
} }
void MainWidget::setTrayTooltip(QString message) void MainWidget::setTrayTooltip(QString message)

View File

@@ -21,18 +21,14 @@
#define MAINWIDGET_H #define MAINWIDGET_H
#include "configwidget.h" #include "configwidget.h"
#include "clientmanager.h"
#include "cmaclient.h" #include "cmaclient.h"
#include <QAction> #include <QAction>
#include <QWidget> #include <QWidget>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
extern "C" {
#include <vitamtp.h> #include <vitamtp.h>
}
class QMenu;
class MainWidget : public QWidget class MainWidget : public QWidget
{ {
@@ -44,9 +40,9 @@ public:
void prepareApplication(); void prepareApplication();
private: private:
void connectSignals();
void createTrayIcon(); void createTrayIcon();
void checkSettings(); void checkSettings();
void connectClientSignals(CmaClient *client);
bool first_run; bool first_run;
ConfigWidget dialog; ConfigWidget dialog;
@@ -54,7 +50,7 @@ private:
QAction *quit; QAction *quit;
QAction *reload; QAction *reload;
QAction *options; QAction *options;
CmaClient clientLoop; ClientManager manager;
const static QStringList path_list; const static QStringList path_list;
private slots: private slots:
@@ -63,8 +59,6 @@ private slots:
void receiveMessage(QString message); void receiveMessage(QString message);
void setTrayTooltip(QString message); void setTrayTooltip(QString message);
void showPin(int pin); void showPin(int pin);
void refreshDatabase();
void startServer();
void stopServer(); void stopServer();
}; };

View File

@@ -27,7 +27,8 @@ SOURCES += main.cpp \
cmaclient.cpp \ cmaclient.cpp \
cmabroadcast.cpp \ cmabroadcast.cpp \
avdecoder.cpp \ avdecoder.cpp \
cmaevent.cpp cmaevent.cpp \
clientmanager.cpp
HEADERS += \ HEADERS += \
capability.h \ capability.h \
@@ -43,7 +44,8 @@ HEADERS += \
cmaclient.h \ cmaclient.h \
cmabroadcast.h \ cmabroadcast.h \
avdecoder.h \ avdecoder.h \
cmaevent.h cmaevent.h \
clientmanager.h
CONFIG += link_pkgconfig CONFIG += link_pkgconfig
PKGCONFIG += libvitamtp libavformat libavcodec libavutil libswscale PKGCONFIG += libvitamtp libavformat libavcodec libavutil libswscale

View File

@@ -42,7 +42,7 @@ const char *SfoReader::value(const char *key, const char *defaultValue) {
for(uint i = 0; i < header->pair_count; i++) { for(uint i = 0; i < header->pair_count; i++) {
const char *curr_key = base_key + index[i].key_offset; const char *curr_key = base_key + index[i].key_offset;
if(strcmp(key, curr_key) == 0) { if(strcmp(key, curr_key) == 0) {
return key_offset + header->value_offset + index[i].data_offset; return key_offset + header->value_offset + index[i].data_offset;
} }
} }
return defaultValue; return defaultValue;

View File

@@ -37,7 +37,7 @@ private:
quint32 value_size; quint32 value_size;
quint32 value_size_with_padding; quint32 value_size_with_padding;
quint32 data_offset; quint32 data_offset;
}__attribute__((packed)) sfo_index; } __attribute__((packed)) sfo_index;
typedef struct { typedef struct {
char id[4]; char id[4];
@@ -45,7 +45,7 @@ private:
quint32 key_offset; quint32 key_offset;
quint32 value_offset; quint32 value_offset;
quint32 pair_count; quint32 pair_count;
}__attribute__((packed)) sfo_header; } __attribute__((packed)) sfo_header;
QByteArray data; QByteArray data;
const char *key_offset; const char *key_offset;

24
utils.h
View File

@@ -24,21 +24,25 @@
#include <QString> #include <QString>
#include <QThread> #include <QThread>
extern "C" {
#include <vitamtp.h> #include <vitamtp.h>
}
// Qt4 doesn't have public methods for Thread::*sleep // Qt4 doesn't have public methods for Thread::*sleep
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
typedef QThread Sleeper; typedef QThread Sleeper;
#else #else
class Sleeper : QThread class Sleeper : QThread
{ {
public: public:
static void sleep(unsigned long secs) { QThread::sleep(secs); } static void sleep(unsigned long secs) {
static void msleep(unsigned long msecs) { QThread::msleep(msecs); } QThread::sleep(secs);
static void usleep(unsigned long usecs) { QThread::usleep(usecs); } }
}; static void msleep(unsigned long msecs) {
QThread::msleep(msecs);
}
static void usleep(unsigned long usecs) {
QThread::usleep(usecs);
}
};
#endif #endif
bool removeRecursively(const QString &dirName); bool removeRecursively(const QString &dirName);