Verified Commit b72c72a3 authored by Linus Jahn's avatar Linus Jahn Committed by Linus Jahn
Browse files

media-sharing: Generate, send and receive image thumbnails

parent 007ba433
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include "gloox-extensions/reference.h" #include "gloox-extensions/reference.h"
#include "gloox-extensions/sims.h" #include "gloox-extensions/sims.h"
#include "gloox-extensions/jinglefile.h" #include "gloox-extensions/jinglefile.h"
#include "gloox-extensions/bitsofbinarydata.h"
// Kaidan // Kaidan
#include "MessageModel.h" #include "MessageModel.h"
#include "Notifications.h" #include "Notifications.h"
...@@ -124,35 +125,8 @@ void MessageHandler::handleMessage(const gloox::Message &stanza, gloox::MessageS ...@@ -124,35 +125,8 @@ void MessageHandler::handleMessage(const gloox::Message &stanza, gloox::MessageS
msg.message = body; msg.message = body;
msg.type = MessageType::MessageText; // only text, no media msg.type = MessageType::MessageText; // only text, no media
// // handle media sharing (SIMS) content
// Get media sharing (SIMS) information handleMediaSharing(const_cast<gloox::Message*>(message), &msg);
//
const gloox::Reference *ref = message->findExtension
<gloox::Reference>(gloox::EXT_REFERENCES);
if (ref && ref->getEmbeddedSIMS()) {
gloox::SIMS *sims = ref->getEmbeddedSIMS();
gloox::StringList sources = sims->sources();
for (auto &source : sources) {
if (source.rfind("https://", 0) == 0 ||
source.rfind("http://", 0) == 0) {
msg.mediaUrl = QString::fromStdString(source);
break;
}
}
gloox::Jingle::File *file = sims->file();
if (file && file->valid()) {
msg.message = QString::fromStdString(file->desc());
msg.mediaSize = file->size();
msg.mediaContentType = QString::fromStdString(file->mediaType());
msg.mediaLastModified = stringToQDateTime(file->date()).toTime_t();
QMimeType mimeType = QMimeDatabase().mimeTypeForName(msg.mediaContentType);
msg.type = getMessageType(mimeType);
for (gloox::Hash &hash : file->hashes())
msg.mediaHashes.append(QString::fromStdString(hash.tag()->xml()));
}
}
// //
// If it is a delayed delivery (containing a timestamp), use its timestamp // If it is a delayed delivery (containing a timestamp), use its timestamp
...@@ -203,6 +177,59 @@ void MessageHandler::handleMessage(const gloox::Message &stanza, gloox::MessageS ...@@ -203,6 +177,59 @@ void MessageHandler::handleMessage(const gloox::Message &stanza, gloox::MessageS
handleReceiptMessage(message, isCarbonMessage); handleReceiptMessage(message, isCarbonMessage);
} }
void MessageHandler::handleMediaSharing(const gloox::Message *message,
MessageModel::Message *msg)
{
//
// Get media sharing (SIMS) information
//
const gloox::Reference *ref = message->findExtension
<gloox::Reference>(gloox::EXT_REFERENCES);
if (ref && ref->getEmbeddedSIMS()) {
gloox::SIMS *sims = ref->getEmbeddedSIMS();
gloox::StringList sources = sims->sources();
for (auto &source : sources) {
if (source.rfind("https://", 0) == 0 ||
source.rfind("http://", 0) == 0) {
msg->mediaUrl = QString::fromStdString(source);
break;
}
}
gloox::Jingle::File *file = sims->file();
if (file && file->valid()) {
msg->message = QString::fromStdString(file->desc());
msg->mediaSize = file->size();
msg->mediaContentType = QString::fromStdString(file->mediaType());
msg->mediaLastModified = stringToQDateTime(file->date()).toTime_t();
QMimeType mimeType = QMimeDatabase().mimeTypeForName(msg->mediaContentType);
msg->type = getMessageType(mimeType);
for (gloox::Hash &hash : file->hashes())
msg->mediaHashes.append(QString::fromStdString(hash.tag()->xml()));
// extract thumbnail
const gloox::Jingle::Thumb *thumb = file->thumb();
if (thumb && thumb->valid()) {
// check if uri is valid (it is a BoB content id [cid])
std::string uri = thumb->uri();
if (uri.rfind("cid:", 0) == 0) {
const gloox::BitsOfBinaryData *thumbData = message->
findExtension<gloox::BitsOfBinaryData>(gloox::EXT_BITSOFBINARY);
// check if thumbnail uri matches the attached data uri
if (thumbData && thumb->uri() == uri.substr(4, uri.length() - 4)) {
// save media thumbnail
msg->mediaThumb = QByteArray::fromBase64(
QByteArray::fromStdString(thumbData->data())
);
}
}
}
}
}
}
void MessageHandler::handleReceiptMessage(const gloox::Message *message, void MessageHandler::handleReceiptMessage(const gloox::Message *message,
bool isCarbonMessage) bool isCarbonMessage)
{ {
......
...@@ -65,6 +65,12 @@ public: ...@@ -65,6 +65,12 @@ public:
virtual void handleMessage(const gloox::Message &message, gloox::MessageSession *session = 0); virtual void handleMessage(const gloox::Message &message, gloox::MessageSession *session = 0);
/**
* Handles and processes media sharing content of messages
*/
void handleMediaSharing(const gloox::Message *message,
MessageModel::Message *msg);
/** /**
* Handles a message with a possible receipt or receipt request * Handles a message with a possible receipt or receipt request
*/ */
......
...@@ -33,16 +33,21 @@ ...@@ -33,16 +33,21 @@
#include "MessageHandler.h" #include "MessageHandler.h"
// gloox // gloox
#include <gloox/message.h> #include <gloox/message.h>
#include <gloox/base64.h>
#include "gloox-extensions/httpuploadmanager.h" #include "gloox-extensions/httpuploadmanager.h"
#include "gloox-extensions/reference.h" #include "gloox-extensions/reference.h"
#include "gloox-extensions/sims.h" #include "gloox-extensions/sims.h"
#include "gloox-extensions/processinghints.h" #include "gloox-extensions/processinghints.h"
#include "gloox-extensions/jinglefile.h" #include "gloox-extensions/jinglefile.h"
#include "gloox-extensions/hash.h" #include "gloox-extensions/hash.h"
#include "gloox-extensions/thumb.h"
#include "gloox-extensions/bitsofbinarydata.h"
// Qt // Qt
#include <QMimeDatabase> #include <QMimeDatabase>
#include <QMimeType> #include <QMimeType>
#include <QDateTime> #include <QDateTime>
#include <QBuffer>
#include <QImage>
#include <QDebug> #include <QDebug>
UploadHandler::UploadHandler(gloox::Client *client, MessageHandler *msgHandler, UploadHandler::UploadHandler(gloox::Client *client, MessageHandler *msgHandler,
...@@ -61,7 +66,6 @@ void UploadHandler::uploadFile(QString jid, QString filePath, QString message) ...@@ -61,7 +66,6 @@ void UploadHandler::uploadFile(QString jid, QString filePath, QString message)
QMimeDatabase mimeDb; QMimeDatabase mimeDb;
QMimeType mimeType = mimeDb.mimeTypeForFile(filePath); QMimeType mimeType = mimeDb.mimeTypeForFile(filePath);
QString mimeTypeStr = mimeType.name(); QString mimeTypeStr = mimeType.name();
qDebug() << filePath;
int id = manager->uploadFile(filePath.toStdString(), true, int id = manager->uploadFile(filePath.toStdString(), true,
mimeTypeStr.toStdString()); mimeTypeStr.toStdString());
...@@ -75,10 +79,11 @@ void UploadHandler::uploadFile(QString jid, QString filePath, QString message) ...@@ -75,10 +79,11 @@ void UploadHandler::uploadFile(QString jid, QString filePath, QString message)
meta.jid = jid; meta.jid = jid;
meta.msgId = client->getID(); meta.msgId = client->getID();
meta.message = message; meta.message = message;
meta.filePath = filePath;
meta.type = msgHandler->getMessageType(mimeType);
mediaShares[id] = meta; mediaShares[id] = meta;
MessageType type = msgHandler->getMessageType(mimeType);
msgHandler->addMessageToDb( msgHandler->addMessageToDb(
jid, message, QString::fromStdString(meta.msgId), jid, message, QString::fromStdString(meta.msgId),
MessageType::MessageFile, filePath MessageType::MessageFile, filePath
...@@ -91,6 +96,8 @@ void UploadHandler::handleUploadFailed(int id, gloox::HttpUploadError error, ...@@ -91,6 +96,8 @@ void UploadHandler::handleUploadFailed(int id, gloox::HttpUploadError error,
const std::string &stamp) const std::string &stamp)
{ {
qDebug() << "[client] A file upload has failed."; qDebug() << "[client] A file upload has failed.";
// the media meta isn't needed anymore, so delete it
mediaShares.remove(id);
} }
void UploadHandler::handleUploadFinished(int id, std::string &name, void UploadHandler::handleUploadFinished(int id, std::string &name,
...@@ -105,7 +112,6 @@ void UploadHandler::handleUploadFinished(int id, std::string &name, ...@@ -105,7 +112,6 @@ void UploadHandler::handleUploadFinished(int id, std::string &name,
msg.mediaContentType = QString::fromStdString(contentType); msg.mediaContentType = QString::fromStdString(contentType);
msg.mediaSize = size; msg.mediaSize = size;
// TODO: generate thumbnail
// TODO: lastModified / date // TODO: lastModified / date
// Hashes // Hashes
...@@ -120,10 +126,30 @@ void UploadHandler::handleUploadFinished(int id, std::string &name, ...@@ -120,10 +126,30 @@ void UploadHandler::handleUploadFinished(int id, std::string &name,
// last modified date // last modified date
std::string date = ""; std::string date = "";
// thumbnail
QSize thumbSize = generateMediaThumb(mediaShares[id].filePath,
mediaShares[id].type, &msg.mediaThumb);
gloox::Jingle::Thumb *thumb = nullptr;
gloox::BitsOfBinaryData *thumbBob = nullptr;
if (!msg.mediaThumb.isEmpty()) {
// encode to base64
std::string thumbData = gloox::Base64::encode64(
std::string(msg.mediaThumb.constData(), msg.mediaThumb.length())
);
// create BoB content identifier
std::string thumbCid = gloox::BitsOfBinaryData::generateContentId(thumbData);
// create BoB data object
thumbBob = new gloox::BitsOfBinaryData(thumbData, thumbCid, "image/jpeg");
// create thumb object (links to BoB data)
thumb = new gloox::Jingle::Thumb(
"cid:" + thumbCid, thumbSize.width(), thumbSize.height(), "image/jpeg"
);
}
// file meta information // file meta information
gloox::Jingle::File *fileInfo = new gloox::Jingle::File( gloox::Jingle::File *fileInfo = new gloox::Jingle::File(
name, size, hashes, contentType, date, name, size, hashes, contentType, date,
mediaShares[id].message.toStdString() mediaShares[id].message.toStdString(), thumb
); );
// list of sources for the file (TODO: jingle as fallback) // list of sources for the file (TODO: jingle as fallback)
...@@ -144,6 +170,8 @@ void UploadHandler::handleUploadFinished(int id, std::string &name, ...@@ -144,6 +170,8 @@ void UploadHandler::handleUploadFinished(int id, std::string &name,
gloox::Message message(gloox::Message::Chat, to.bareJID(), msgBody); gloox::Message message(gloox::Message::Chat, to.bareJID(), msgBody);
message.setID(mediaShares[id].msgId); message.setID(mediaShares[id].msgId);
message.addExtension((gloox::StanzaExtension*) simsRef); message.addExtension((gloox::StanzaExtension*) simsRef);
if (thumbBob)
message.addExtension(thumbBob); // thumbnail BoB data
client->send(message); client->send(message);
...@@ -173,3 +201,25 @@ void UploadHandler::handleUploadServiceAdded(const gloox::JID &jid, ...@@ -173,3 +201,25 @@ void UploadHandler::handleUploadServiceAdded(const gloox::JID &jid,
void UploadHandler::handleFileSizeLimitChanged(unsigned long maxFileSize) void UploadHandler::handleFileSizeLimitChanged(unsigned long maxFileSize)
{ {
} }
QSize UploadHandler::generateMediaThumb(QString &filePath, MessageType type,
QByteArray *bytes)
{
if (type != MessageType::MessageImage)
return QSize(0, 0);
// results should be about 1-3 kB large
int finalSize = 40;
QImage img(filePath);
// Qt::FastTransformation is used because quality doesn't matter at this
// size; you won't be able to really see a large difference
img = img.scaled(finalSize, finalSize, Qt::KeepAspectRatio, Qt::FastTransformation);
// save image to byte array
QBuffer buffer(bytes);
buffer.open(QIODevice::WriteOnly);
img.save(&buffer, "JPEG", 90);
return img.size();
}
...@@ -37,6 +37,11 @@ ...@@ -37,6 +37,11 @@
// Qt // Qt
#include <QObject> #include <QObject>
#include <QMap> #include <QMap>
#include <QSize>
// Kaidan
#include "Enums.h"
using namespace Enums;
namespace gloox { namespace gloox {
class Client; class Client;
...@@ -133,10 +138,18 @@ protected: ...@@ -133,10 +138,18 @@ protected:
const std::string &stamp = gloox::EmptyString); const std::string &stamp = gloox::EmptyString);
private: private:
/**
* Generates a media thumbnail (currently only image thumbs)
*/
QSize generateMediaThumb(QString &filePath, MessageType type,
QByteArray *bytes);
struct MediaSharingMeta { struct MediaSharingMeta {
QString jid; QString jid;
std::string msgId; std::string msgId;
QString message; QString message;
QString filePath;
MessageType type;
}; };
gloox::Client *client; gloox::Client *client;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment