Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upstreaming Krita Animation Plugin #961

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ option(MOD_GLAXNIMATE "Enable Glaxnimate module (Qt5)" OFF)
option(MOD_GLAXNIMATE_QT6 "Enable Glaxnimate module (Qt6)" OFF)
option(MOD_JACKRACK "Enable JACK Rack module" ON)
option(MOD_KDENLIVE "Enable Kdenlive module" ON)
option(MOD_KRITA "Enable Krita module" ON)
option(MOD_NDI "Enable NDI module" OFF)
option(MOD_NORMALIZE "Enable Normalize module (GPL)" ON)
option(MOD_OLDFILM "Enable Oldfilm module" ON)
Expand Down Expand Up @@ -590,6 +591,7 @@ add_feature_info("Module: Glaxnimate (Qt5)" MOD_GLAXNIMATE "")
add_feature_info("Module: Glaxnimate (Qt6)" MOD_GLAXNIMATE_QT6 "")
add_feature_info("Module: JACKRack" MOD_JACKRACK "")
add_feature_info("Module: Kdenlive" MOD_KDENLIVE "")
add_feature_info("Module: Krita" MOD_KRITA "")
add_feature_info("Module: NDI" MOD_NDI "")
add_feature_info("Module: Normalize" MOD_NORMALIZE "")
add_feature_info("Module: Oldfilm" MOD_OLDFILM "")
Expand Down
8 changes: 2 additions & 6 deletions src/framework/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,8 @@ set_target_properties(mlt PROPERTIES

if(WIN32)
if(MINGW)
install(FILES "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/libmlt-${MLT_VERSION_MAJOR}.dll"
DESTINATION ${CMAKE_INSTALL_LIBDIR}
RENAME libmlt.dll
)
target_link_options(mlt PRIVATE -Wl,--output-def,libmlt.def)
install(FILES "${CMAKE_BINARY_DIR}/libmlt.def" DESTINATION ${CMAKE_INSTALL_LIBDIR})
target_link_options(mlt PRIVATE -Wl,--output-def,${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/libmlt-${MLT_VERSION_MAJOR}.def)
install(FILES "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/libmlt-${MLT_VERSION_MAJOR}.def" DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
target_sources(mlt PRIVATE ../win32/win32.c ../win32/strptime.c)
target_link_libraries(mlt PRIVATE Iconv::Iconv)
Expand Down
2 changes: 2 additions & 0 deletions src/framework/mlt_property.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ typedef locale_t mlt_locale_t;
#elif defined(__OpenBSD__)
/* XXX matches __nop_locale glue in libc++ */
typedef void *mlt_locale_t;
#elif (defined _WIN32 && defined _LIBCPP_VERSION)
struct mlt_locale_t;
#else
typedef char *mlt_locale_t;
#endif
Expand Down
13 changes: 7 additions & 6 deletions src/framework/mlt_repository.c
Original file line number Diff line number Diff line change
Expand Up @@ -336,12 +336,13 @@ void *mlt_repository_create(mlt_repository self,

void mlt_repository_close(mlt_repository self)
{
mlt_properties_close(self->consumers);
mlt_properties_close(self->filters);
mlt_properties_close(self->producers);
mlt_properties_close(self->transitions);
mlt_properties_close(&self->parent);
free(self);
mlt_properties_close( self->consumers );
mlt_properties_close( self->filters );
mlt_properties_close( self->links );
mlt_properties_close( self->producers );
mlt_properties_close( self->transitions );
mlt_properties_close( &self->parent );
free( self );
Comment on lines +339 to +345
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation of this is still wrong; it uses 4 spaces now instead of tabs. Our .clang-format file covers that.

}

/** Get the list of registered consumers.
Expand Down
8 changes: 2 additions & 6 deletions src/mlt++/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,8 @@ set_target_properties(mlt++ PROPERTIES

if(WIN32)
if(MINGW)
install(FILES "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/libmlt++-${MLT_VERSION_MAJOR}.dll"
DESTINATION ${CMAKE_INSTALL_LIBDIR}
RENAME libmlt++.dll
)
target_link_options(mlt++ PRIVATE -Wl,--output-def,libmlt++.def)
install(FILES "${CMAKE_BINARY_DIR}/libmlt++.def" DESTINATION ${CMAKE_INSTALL_LIBDIR})
target_link_options(mlt++ PRIVATE -Wl,--output-def,${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/libmlt++-${MLT_VERSION_MAJOR}.def)
install(FILES "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/libmlt++-${MLT_VERSION_MAJOR}.def" DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
target_compile_definitions(mlt++ PRIVATE MLTPP_EXPORTS)
endif()
Expand Down
4 changes: 3 additions & 1 deletion src/mlt++/MltPushConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ PushConsumer::PushConsumer(Profile &profile, const char *id, const char *service
}
}

PushConsumer::~PushConsumer() {}
PushConsumer::~PushConsumer() {
delete m_private;
}

void PushConsumer::set_render(int width, int height, double aspect_ratio)
{
Expand Down
4 changes: 4 additions & 0 deletions src/modules/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ if(MOD_KDENLIVE)
add_subdirectory(kdenlive)
endif()

if(MOD_KRITA)
add_subdirectory(krita)
endif()

if(MOD_NDI)
add_subdirectory(ndi)
endif()
Expand Down
18 changes: 18 additions & 0 deletions src/modules/krita/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
add_library(mltkrita MODULE
factory.c
producer_krita.c
)

target_compile_options(mltkrita PRIVATE ${MLT_COMPILE_OPTIONS})

target_link_libraries(mltkrita PRIVATE mlt m)

set_target_properties(mltkrita PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}")

install(TARGETS mltkrita LIBRARY DESTINATION ${MLT_INSTALL_MODULE_DIR})

install(FILES
producer_krita.yml
DESTINATION ${MLT_INSTALL_DATA_DIR}/krita
)

41 changes: 41 additions & 0 deletions src/modules/krita/factory.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* factory.c -- the factory method interfaces
* Copyright (C) 2022 Eoin O'Neill <eoinoneill1991@gmail.com>
* Copyright (C) 2022 Emmet O'Neill <emmetoneill.pdx@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include <framework/mlt.h>
#include <limits.h>
#include <string.h>

extern mlt_producer producer_krita_init(mlt_profile profile,
mlt_service_type type,
const char *id,
char *arg);

static mlt_properties metadata(mlt_service_type type, const char *id, void *data)
{
char file[PATH_MAX];
snprintf(file, PATH_MAX, "%s/krita/%s", mlt_environment("MLT_DATA"), (char *) data);
return mlt_properties_parse_yaml(file);
}

MLT_REPOSITORY
{
MLT_REGISTER(mlt_service_producer_type, "krita", producer_krita_init);
MLT_REGISTER_METADATA(mlt_service_producer_type, "krita", metadata, "producer_krita.yml");
}
189 changes: 189 additions & 0 deletions src/modules/krita/producer_krita.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* producer_krita.c -- Produces variable-speed audio within a restricted range of frames. Used internally by Krita to drive audio-synced animation playback.
* Copyright (C) 2022 Eoin O'Neill <eoinoneill1991@gmail.com>
* Copyright (C) 2022 Emmet O'Neill <emmetoneill.pdx@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include <framework/mlt.h>
#include <limits.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <framework/mlt_factory.h>
#include <framework/mlt_frame.h>
#include <framework/mlt_producer.h>
#include <framework/mlt_property.h>
#include <framework/mlt_service.h>

typedef struct
{
mlt_producer producer_internal;
} private_data;

/** Restricts frame index to within range by modulus wrapping (not clamping).
*/
static int restrict_range(int index, int min, int max)
{
const int span = max - min;
return (MAX(index - min, 0) % (span + 1)) + min;
}

static int is_valid_range(const int frame_start, const int frame_end)
{
const bool NON_NEGATIVE = frame_start >= 0 && frame_end >= 0;
const bool NON_INVERTED = frame_end > frame_start;

return NON_NEGATIVE && NON_INVERTED;
}

static int producer_get_audio(mlt_frame frame,
void **buffer,
mlt_audio_format *format,
int *frequency,
int *channels,
int *samples)
{
mlt_producer producer = mlt_frame_pop_audio(frame);

struct mlt_audio_s audio;

mlt_audio_set_values(&audio, *buffer, *frequency, *format, *samples, *channels);

int error = mlt_frame_get_audio(frame,
&audio.data,
&audio.format,
&audio.frequency,
&audio.channels,
&audio.samples);

mlt_properties props = MLT_PRODUCER_PROPERTIES(producer);

// Scale the frequency to account for the dynamic speed (normalized).
const double SPEED = mlt_properties_get_double(props, "speed");

audio.frequency = (double) audio.frequency * fabs(SPEED);
if (SPEED < 0.0) {
mlt_audio_reverse(&audio);
}

mlt_audio_get_values(&audio, buffer, frequency, format, samples, channels);

return error;
}

static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index)
{
mlt_properties props = MLT_PRODUCER_PROPERTIES(producer);
const int FRAME_START = mlt_properties_get_int(props, "start_frame");
const int FRAME_END = mlt_properties_get_int(props, "end_frame");
const bool IS_RANGE_LIMITED = mlt_properties_get_int(props, "limit_enabled");

private_data *pdata = (private_data *) producer->child;
const int POSITION = mlt_producer_position(pdata->producer_internal);

if (IS_RANGE_LIMITED && is_valid_range(FRAME_START, FRAME_END)) {
mlt_properties_set_position(MLT_PRODUCER_PROPERTIES(pdata->producer_internal),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does mlt_producer_seek() work here instead of modifying a private property directly?

"_position",
restrict_range(POSITION, FRAME_START, FRAME_END));
}

int retval = mlt_service_get_frame((mlt_service) pdata->producer_internal, frame, index);

if (!mlt_frame_is_test_audio(*frame)) {
mlt_frame_push_audio(*frame, producer);
mlt_frame_push_audio(*frame, producer_get_audio);
}

return retval;
}

static int producer_seek(mlt_producer producer, mlt_position position)
{
private_data *pdata = (private_data *) producer->child;

int retval = mlt_producer_seek(pdata->producer_internal, position);

return retval;
}

static void producer_close(mlt_producer producer)
{
private_data *pdata = (private_data *) producer->child;

if (pdata) {
mlt_producer_close(pdata->producer_internal);
free(pdata);
}

producer->close = NULL;
mlt_producer_close(producer);
free(producer);
}

/** Constructor for the producer.
*/
mlt_producer producer_krita_init(mlt_profile profile,
mlt_service_type type,
const char *id,
char *arg)
{
// Create a new producer object
mlt_producer producer = mlt_producer_new(profile);
private_data *pdata = (private_data *) calloc(1, sizeof(private_data));

if (arg && producer && pdata) {
mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer);

// Initialize the producer
mlt_properties_set(producer_properties, "resource", arg);
producer->child = pdata;
producer->get_frame = producer_get_frame;
producer->seek = producer_seek;
producer->close = (mlt_destructor) producer_close;

// Get the resource to be passed to the clip producer
char *resource = arg;

// Create internal producer
pdata->producer_internal = mlt_factory_producer(profile, "abnormal", resource);

if (pdata->producer_internal) {
mlt_producer_set_speed(pdata->producer_internal, 1.0);
}
}

const bool INVALID_CONTEXT = !producer || !pdata || !pdata->producer_internal;
if (INVALID_CONTEXT) { // Clean up early...
if (pdata) {
mlt_producer_close(pdata->producer_internal);
free(pdata);
}

if (producer) {
producer->child = NULL;
producer->close = NULL;
mlt_producer_close(producer);
free(producer);
producer = NULL;
}
}

return producer;
}
Loading