Build working with mingw. Removed ffmpeg and freeimage since homeservers can now generate thumbnails for us.

This commit is contained in:
rexy712 2019-09-13 19:14:15 -07:00
parent 4abfa99bfe
commit 46d3652fa3
7 changed files with 85 additions and 353 deletions

1
.gitignore vendored
View File

@ -12,3 +12,4 @@ lib*.so
lib*.a lib*.a
matrix-send matrix-send
.scripts .scripts
windows_bs

View File

@ -1,8 +1,6 @@
general/other: general/other:
5:thorough error checking 5:thorough error checking
2:move libav/freeimage calls from matrix to standalone functions or a singleton
1:use libmagic to determine file types for uploading? 1:use libmagic to determine file types for uploading?
1:raii swscontext
matrix: matrix:
session: session:
@ -18,6 +16,7 @@ matrix:
3:set presence 3:set presence
2:query other users' data 2:query other users' data
1:download media 1:download media
1:thumbnail images and videos from server
1:query/set custom user data 1:query/set custom user data
1:query/addto public room directory 1:query/addto public room directory
1:deactivate account 1:deactivate account

View File

@ -1,73 +0,0 @@
/**
This file is a part of rexy's matrix client
Copyright (C) 2019 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MATRIX_IMAGE_THUMBNAIL_MAKER_HPP
#define MATRIX_IMAGE_THUMBNAIL_MAKER_HPP
#include <FreeImagePlus.h>
#include <vector>
#include <cstdlib> //size_t
#include "matrix/upload_info.hpp"
#define THUMBSIZE 500
namespace matrix::internal{
static std::vector<char> _create_image_thumbnail(fipImage& image, FREE_IMAGE_FORMAT type, size_t target_size){
image.makeThumbnail(target_size);
FreeImageIO fileout;
std::vector<char> buffer;
fileout.write_proc = [](void* ptr, unsigned int size, unsigned int nmemb, void* fp) -> unsigned int{
std::vector<char>& buffer = *reinterpret_cast<std::vector<char>*>(fp);
buffer.insert(buffer.end(), (char*)ptr, ((char*)ptr)+size*nmemb);
return size*nmemb;
};
switch(type){
case FIF_JPEG:
return image.saveToHandle(type, &fileout, &buffer, JPEG_QUALITYGOOD | JPEG_SUBSAMPLING_411) ? buffer : std::vector<char>();
case FIF_PNG:
return image.saveToHandle(type, &fileout, &buffer, PNG_Z_BEST_COMPRESSION) ? buffer : std::vector<char>();
default:
return image.saveToHandle(type, &fileout, &buffer) ? buffer : std::vector<char>();
};
}
static std::vector<char> _load_image_thumbnail_info(fipImage& image, image_info& info, FREE_IMAGE_FORMAT type){
//create and upload thumbnail
if(info.width > THUMB_SIZE || info.height > THUMB_SIZE){
std::vector<char> thumb_data = _create_image_thumbnail(image, type, THUMB_SIZE);
if(!thumb_data.size()){
info.thumb_width = info.width;
info.thumb_height = info.height;
info.thumbsize = info.filesize;
}else{
info.thumb_width = image.getWidth();
info.thumb_height = image.getHeight();
info.thumbsize = thumb_data.size();
return thumb_data;
}
}else{
info.thumb_width = info.width;
info.thumb_height = info.height;
info.thumbsize = info.filesize;
}
return {};
}
}
#endif

View File

@ -1,127 +0,0 @@
/**
This file is a part of rexy's matrix client
Copyright (C) 2019 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
# include "libav/frame.hpp"
# include "libav/codec/context.hpp"
# include "libav/fmt/context.hpp"
# include "libav/packet.hpp"
extern "C"{
# include <libswscale/swscale.h> //sws_scale
# include <libavutil/imgutils.h> //av_image_alloc
}
#include <cstdlib> //size_t
namespace matrix::internal{
struct libav_initializer{
libav_initializer(void){
REGISTER_LIBAV();
}
};
static int initlibav(void){
static libav_initializer init;
return 0;
}
static libavcodec::context _get_libav_jpg_context(AVRational src, size_t w, size_t h){
AVCodec* jpg_codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
libavcodec::context jctx(jpg_codec);
jctx->pix_fmt = AV_PIX_FMT_YUVJ420P;
jctx->width = w;
jctx->height = h;
jctx->codec_id = AV_CODEC_ID_MJPEG;
jctx->codec_type = AVMEDIA_TYPE_VIDEO;
if(src.num == 0 || src.den == 0)
jctx->time_base = AVRational{1, 30};
else
jctx->time_base = src;
jctx.open();
return jctx;
}
[[maybe_unused]]
static void _change_color_range(SwsContext* sws_ctx){
int dummy[4];
int src_range, dest_range;
int br, con, sat;
sws_getColorspaceDetails(sws_ctx, (int**)&dummy, &src_range, (int**)&dummy, &dest_range, &br, &con, &sat);
const int* coefs = sws_getCoefficients(SWS_CS_DEFAULT);
src_range = 2;
sws_setColorspaceDetails(sws_ctx, coefs, src_range, coefs, dest_range, br, con, sat);
}
static void _calc_video_thumbnail_dims(size_t target, size_t src_w, size_t src_h, size_t* w, size_t* h){
size_t target_width = src_w;
size_t target_height = src_h;
if(target_width > target_height){
if(target_width > target){
target_height = target_height * ((float)target / target_width);
target_width = target;
}
}else{
if(target_height > target){
target_width = target_width * ((float)target / target_height);
target_height = target;
}
}
*w = target_width;
*h = target_height;
}
static libav::packet _create_video_thumbnail(libavfmt::input_context& src, libavcodec::context& src_ctx, int index, size_t targw, size_t targh){
libavcodec::context jpg_context = _get_libav_jpg_context(src_ctx->time_base, targw, targh);
libav::frame src_frame(src_ctx->width, src_ctx->height, src_ctx->pix_fmt);
libav::frame jpg_frame(src_ctx->width, src_ctx->height, jpg_context->pix_fmt);
libav::packet packet;
SwsContext* sws_ctx = sws_getContext(src_ctx->width, src_ctx->height, src_ctx->pix_fmt,
targw, targh, AV_PIX_FMT_YUV420P,
SWS_BILINEAR, NULL, NULL, NULL);
//_change_color_range(sws_ctx);
av_image_alloc(jpg_frame->data, jpg_frame->linesize, targw, targh, jpg_context->pix_fmt, 32);
jpg_frame.set_freep();
while(av_read_frame(src, packet) >= 0){
if(packet->stream_index == index){
avcodec_send_packet(src_ctx, packet);
if(avcodec_receive_frame(src_ctx, src_frame) >= 0)
break;
}
av_packet_unref(packet);
}
sws_scale(sws_ctx, src_frame->data, src_frame->linesize, 0, src_ctx->height, jpg_frame->data, jpg_frame->linesize);
sws_freeContext(sws_ctx);
if(avcodec_send_frame(jpg_context, jpg_frame) < 0){
return libav::packet(nullptr);
}
if(avcodec_receive_packet(jpg_context, packet) < 0){
return libav::packet(nullptr);
}
return packet;
}
static raii::string get_libav_mimetype(const libav::fmt::input_context& ctx){
const char* first = strstr(ctx->iformat->name, ",");
if(first)
return raii::string("video/" + raii::static_string(ctx->iformat->name, first - ctx->iformat->name));
return raii::string("video/" + raii::static_string(ctx->iformat->name));
}
}
#endif

120
makefile
View File

@ -15,72 +15,110 @@
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
WINDOWS=1 WINDOWS::=1
endif endif
SOURCE_DIRS:=src/raii src/matrix SOURCE_DIRS::=src/raii src/matrix
OBJDIR:=obj OBJDIR::=obj
DEPDIR:=$(OBJDIR)/dep DEPDIR::=$(OBJDIR)/dep
INCLUDE_DIRS:=include LIBDIR::=lib
EXT:=cpp INCLUDE_DIRS::=include
MAIN_LIBRARY:=librmatrix.so CXXFLAGS::=-g -std=c++17 -Wall -pedantic -Wextra
EXT::=cpp
CXXFLAGS:=-g -std=c++17 -Wall -pedantic -Wextra -fPIC MAIN_LIBRARY::=rmatrix
SHARED?=1
ifneq ($(WINDOWS),1) ifneq ($(WINDOWS),1)
CXX:=g++ CXX::=g++
AR:=ar LDLIBS::=-lcurl -lrjp
RANLIB:=ranlib LDFLAGS::=
LDFLAGS:=-shared STRIP::=strip
LDLIBS:=-lcurl -lrjp -lavformat -lavcodec -lavutil -lswresample -lswscale -lfreeimageplus RANLIB::=ranlib
STRIP:=strip AR::=ar
AS::=as
else #windows
MINGW_PREFIX::=x86_64-w64-mingw32-
CXX::=$(MINGW_PREFIX)g++
LDLIBS::=-lcurl -lrjp
LDFLAGS::=-static-libgcc -static-libstdc++
STRIP::=$(MINGW_PREFIX)strip
RANLIB::=$(MINGW_PREFIX)ranlib
AR::=$(MINGW_PREFIX)ar
AS::=$(MINGW_PREFIX)as
DLLOUT::=$(MAIN_LIBRARY).dll
endif #windows
ifeq ($(SHARED),1)
ifeq ($(OS),Windows_NT)
INTERNAL_MAIN_LIBRARY::=lib$(MAIN_LIBRARY).a
else
INTERNAL_MAIN_LIBRARY::=lib$(MAIN_LIBRARY).so
endif
else else
CXX:=x86_64-w64-mingw32-g++ INTERNAL_MAIN_LIBRARY::=lib$(MAIN_LIBRARY).a
AR:=x86_64-w64-mingw32-ar
RANLIB:=x86_64-w64-mingw32-ranlib
LDFLAGS:=
LDLIBS:=-lcurl -lrjp -lavformat -lavcodec -lavutil -lswresample -lswscale -lfreeimageplus
STRIP:=x86_64-w64-mingw32-strip
endif endif
all: CXXFLAGS+=-O0 all: CXXFLAGS+=-O0
release: CXXFLAGS+=-O2 release: CXXFLAGS+=-O2
memchk:LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls memchk: LDFLAGS+=-fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
memchk:CXXFLAGS+=-O0 -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls memchk: CXXFLAGS+=-O0 -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
mkdir=mkdir $(subst /,\,$(1)) > NUL 2>&1 mkdir=mkdir $(subst /,\,$(1)) > NUL 2>&1
rm=del /F $(1) > NUL 2>&1 rm=del /F $(1) > NUL 2>&1
rmdir=rd /s /q $(1) > NUL 2>&1 rmdir=rd /S /Q $(1) > NUL 2>&1
move=move /y $(subst /,\,$(1)) $(subst /,\,$(2)) > NUL 2>&1 move=move /Y $(subst /,\,$(1)) $(subst /,\,$(2)) > NUL 2>&1
MAIN_EXECUTABLE:=$(MAIN_EXECUTABLE).exe copy=copy /Y /B $(subst /,\,$(1)) $(subst /,\,$(2)) > NUL 2>&1
LDLIBS:=-lglfw3 -lSOIL -lgl3w -lm -lopengl32 -lglu32 -lgdi32 -lkernel32
else else
mkdir=mkdir -p $(1) mkdir=mkdir -p $(1)
rm=rm -f $(1) rm=rm -f $(1)
rmdir=rm -rf $(1) rmdir=rm -rf $(1)
move=mv $(1) $(2) move=mv $(1) $(2)
copy=cp $(1) $(2)
endif endif
INTERNAL_CXXFLAGS=-c $(foreach dir,$(INCLUDE_DIRS),-I"$(dir)") -MMD -MP -MF"$(DEPDIR)/$(notdir $(patsubst %.o,%.d,$@))" INTERNAL_CXXFLAGS=-c $(foreach dir,$(INCLUDE_DIRS),-I"$(dir)") -MMD -MP -MF"$(DEPDIR)/$(notdir $(patsubst %.o,%.d,$@))"
SOURCES:=$(foreach source,$(SOURCE_DIRS),$(foreach ext,$(EXT),$(wildcard $(source)/*.$(ext)))) ifeq ($(SHARED),1)
OBJECTS:=$(addprefix $(OBJDIR)/,$(subst \,.,$(subst /,.,$(addsuffix .o,$(SOURCES))))) INTERNAL_CXXFLAGS+=-fPIC
endif
SOURCES::=$(foreach source,$(SOURCE_DIRS),$(foreach ext,$(EXT),$(wildcard $(source)/*.$(ext))))
OBJECTS::=$(addprefix $(OBJDIR)/,$(subst \,.,$(subst /,.,$(addsuffix .o,$(SOURCES)))))
.PHONY: all
ifeq ($(SHARED),1)
ifeq ($(WINDOWS),1)
all: $(DLLOUT)
$(INTERNAL_MAIN_LIBRARY): $(OBJECTS)
$(CXX) -shared -o "$(DLLOUT)" $^ -Wl,--out-implib,"lib$(MAIN_LIBRARY).a" $(LDLIBS) $(LDFLAGS)
$(DLLOUT): $(INTERNAL_MAIN_LIBRARY)
else #windows
all: $(INTERNAL_MAIN_LIBRARY)
$(INTERNAL_MAIN_LIBRARY): $(OBJECTS)
$(CXX) -shared -o "$@" $^ $(CXXFLAGS) $(LDFLAGS) $(LDLIBS)
endif #windows
else #shared
all: $(INTERNAL_MAIN_LIBRARY)
$(INTERNAL_MAIN_LIBRARY): $(OBJECTS)
$(AR) rcs "$@" $^
$(RANLIB) "$@"
endif #shared
all: $(MAIN_LIBRARY)
.PHONY: memchk .PHONY: memchk
memchk: all
.PHONY: release .PHONY: release
memchk: all
release: all release: all
.PHONY: utils .PHONY: utils
utils: matrix-send utils: matrix-send
tester: $(MAIN_LIBRARY) $(OBJDIR)/src.test.cpp.o tester: all $(OBJDIR)/src.test.cpp.o
$(CXX) -L. -o "$@" $(OBJDIR)/src.test.cpp.o -lrmatrix -lpthread $(CXX) -L. -o "$@" $(OBJDIR)/src.test.cpp.o -l$(MAIN_LIBRARY) $(LDLIBS) -lpthread -Iinclude
matrix-send: $(MAIN_LIBRARY) $(OBJDIR)/util.matrix-send.cpp.o matrix-send: all $(OBJDIR)/util.matrix-send.cpp.o
$(CXX) -L. -o "$@" $(OBJDIR)/util.matrix-send.cpp.o -lrmatrix $(CXX) -L. -o "$@" $(OBJDIR)/util.matrix-send.cpp.o -l$(MAIN_LIBRARY) $(LDLIBS) -Iinclude
$(MAIN_LIBRARY): $(OBJECTS)
$(CXX) -o "$@" $^ $(CXXFLAGS) $(LDFLAGS) $(LDLIBS)
define GENERATE_OBJECTS define GENERATE_OBJECTS
@ -103,11 +141,11 @@ $(DEPDIR):
clean: clean:
$(call rmdir,"$(DEPDIR)") $(call rmdir,"$(DEPDIR)")
$(call rmdir,"$(OBJDIR)") $(call rmdir,"$(OBJDIR)")
$(call rm,"$(MAIN_LIBRARY)") $(call rm,"lib$(MAIN_LIBRARY).so")
.PHONY: utilsclean $(call rm,"lib$(MAIN_LIBRARY).a")
fullclean: clean
$(call rm,"matrix-send")
$(call rm,"tester") $(call rm,"tester")
$(call rm,"matrix-send")
$(call rm,"$(DLLOUT)")
-include $(wildcard $(DEPDIR)/*.d) -include $(wildcard $(DEPDIR)/*.d)

View File

@ -23,15 +23,6 @@
#include "raii/rjp_ptr.hpp" #include "raii/rjp_ptr.hpp"
#include "raii/util.hpp" #include "raii/util.hpp"
#ifdef HAS_FREEIMAGE
# include "matrix/image_thumbnail_maker.hpp"
#endif
#ifdef HAS_FFMPEG
# define THUMBSIZE 500
# include "matrix/video_thumbnail_maker.hpp"
#endif
namespace matrix{ namespace matrix{
//Ctor //Ctor
@ -109,117 +100,17 @@ namespace matrix{
image_info client::upload_image(const raii::string_base& filename)const{ image_info client::upload_image(const raii::string_base& filename)const{
return upload_image(filename, raii::static_string()); return upload_image(filename, raii::static_string());
} }
#ifdef HAS_FREEIMAGE
image_info client::upload_image(const raii::string_base& filename, const raii::string_base& alias)const{
image_info ret;
FREE_IMAGE_FORMAT type = fipImage::identifyFIF(filename.get());
ret.mimetype = FreeImage_GetFIFMimeType(type);
raii::curl_llist header(raii::string("Content-Type: " + ret.mimetype));
{
raii::filerd fd(filename, "rb");
if(!fd)
return ret;
ret.filesize = fd.length();
ret.fileurl = _upload_file(fd, header);
}
fipImage image;
image.load(filename.get());
if(!image.isValid())
return {};
ret.width = image.getWidth();
ret.height = image.getHeight();
ret.filename = alias ? alias : filename;
std::vector<char> thumb_data = internal::_load_image_thumbnail_info(image, ret, type);
if(thumb_data.size())
ret.thumburl = _post_and_find(raii::static_string(thumb_data.data(), thumb_data.size()), m_ses->urls.file_upload(), header, "content_uri"_ss);
return ret;
}
#else //HAS_FREEIMAGE
image_info client::upload_image(const raii::string_base& filename, const raii::string_base& alias)const{ image_info client::upload_image(const raii::string_base& filename, const raii::string_base& alias)const{
image_info ret = {}; image_info ret = {};
ret = upload_file(filename, alias); ret = upload_file(filename, alias);
return ret; return ret;
} }
#endif //HAS_FREEIMAGE
video_info client::upload_video(const raii::string_base& filename)const{ video_info client::upload_video(const raii::string_base& filename)const{
return upload_video(filename, raii::static_string()); return upload_video(filename, raii::static_string());
} }
audio_info client::upload_audio(const raii::string_base& filename)const{ audio_info client::upload_audio(const raii::string_base& filename)const{
return upload_audio(filename, raii::static_string()); return upload_audio(filename, raii::static_string());
} }
#ifdef HAS_FFMPEG
video_info client::upload_video(const raii::string_base& filename, const raii::string_base& alias)const{
internal::initlibav();
video_info ret = {};
libav::fmt::input_context in(filename);
if(!in) return {};
int stream_index = -1;
for(size_t i = 0;i < in->nb_streams;++i){
if(in->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
stream_index = i;
break;
}
}
if(stream_index == -1) return {};
libavcodec::context src_ctx(in, stream_index);
if(!src_ctx) return {};
src_ctx.open();
if(!src_ctx.is_open()) return {};
size_t thumbw, thumbh;
internal::_calc_video_thumbnail_dims(THUMB_SIZE, src_ctx->width, src_ctx->height, &thumbw, &thumbh);
libav::packet packet = internal::_create_video_thumbnail(in, src_ctx, stream_index, thumbw, thumbh);
if(packet){
raii::curl_llist header("Content-Type: image/jpeg");
ret.thumb_width = thumbw;
ret.thumb_height = thumbh;
ret.thumbsize = packet->size;
ret.thumburl = _post_and_find(raii::static_string((char*)packet->data, packet->size), m_ses->urls.file_upload(), header, "content_uri"_ss);
packet.reset();
}
ret.width = src_ctx->width;
ret.height = src_ctx->height;
ret.mimetype = internal::get_libav_mimetype(in);
src_ctx.reset();
in.reset();
raii::curl_llist header(raii::string("Content-Type:" + ret.mimetype));
header += "Transfer-Encoding: chunked";
raii::filerd fd(filename);
if(!fd) return {};
ret.filesize = fd.length();
ret.fileurl = _upload_file(fd, header);
ret.filename = alias ? alias : filename;
return ret;
}
audio_info client::upload_audio(const raii::string_base& filename, const raii::string_base& alias)const{
audio_info ret = {};
libav::fmt::input_context in(filename);
ret.mimetype = internal::get_libav_mimetype(in);
in.reset();
raii::curl_llist header(raii::string("Content-Type:" + ret.mimetype));
raii::filerd fd(filename);
if(!fd) return {};
ret.filesize = fd.length();
ret.fileurl = _upload_file(fd, header);
ret.filename = alias ? alias : filename;
return ret;
}
#else //HAS_FFMPEG
video_info client::upload_video(const raii::string_base& filename, const raii::string_base& alias)const{ video_info client::upload_video(const raii::string_base& filename, const raii::string_base& alias)const{
video_info ret = {}; video_info ret = {};
ret = upload_file(filename, alias); ret = upload_file(filename, alias);
@ -230,7 +121,6 @@ namespace matrix{
ret = upload_file(filename, alias); ret = upload_file(filename, alias);
return ret; return ret;
} }
#endif //HAS_FFMPEG
/******************************* /*******************************

View File

@ -135,6 +135,10 @@ namespace matrix{
return raii::rjp_string(res.value); return raii::rjp_string(res.value);
} }
void connection::_set_curl_defaults(const raii::string_base& useragent)const{ void connection::_set_curl_defaults(const raii::string_base& useragent)const{
#ifdef _WIN32
//temporary measure to make ssl connections work on wine
m_curl.setopt(CURLOPT_CAINFO, "./cacert.pem");
#endif
m_curl.setopt(CURLOPT_BUFFERSIZE, 102400L); m_curl.setopt(CURLOPT_BUFFERSIZE, 102400L);
m_curl.setopt(CURLOPT_NOPROGRESS, 1L); m_curl.setopt(CURLOPT_NOPROGRESS, 1L);
m_curl.setuseragent(useragent); m_curl.setuseragent(useragent);