Loading...
Searching...
No Matches
Clip.cpp
Go to the documentation of this file.
// Copyright (c) 2008-2019 OpenShot Studios, LLC
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "Clip.h"
#include "AudioResampler.h"
#include "Exceptions.h"
#include "FFmpegReader.h"
#include "FrameMapper.h"
#include "QtImageReader.h"
#include "ChunkReader.h"
#include "DummyReader.h"
#include "Timeline.h"
#include "ZmqLogger.h"
#include <algorithm>
#include <cmath>
#ifdef USE_IMAGEMAGICK
#include "MagickUtilities.h"
#include "ImageReader.h"
#include "TextReader.h"
#endif
#include <Qt>
using namespace openshot;
namespace {
struct CompositeChoice { const char* name; CompositeType value; };
const CompositeChoice composite_choices[] = {
{"Normal", COMPOSITE_SOURCE_OVER},
// Darken group
{"Darken", COMPOSITE_DARKEN},
{"Multiply", COMPOSITE_MULTIPLY},
{"Color Burn", COMPOSITE_COLOR_BURN},
// Lighten group
{"Lighten", COMPOSITE_LIGHTEN},
{"Screen", COMPOSITE_SCREEN},
{"Color Dodge", COMPOSITE_COLOR_DODGE},
{"Add", COMPOSITE_PLUS},
// Contrast group
{"Overlay", COMPOSITE_OVERLAY},
{"Soft Light", COMPOSITE_SOFT_LIGHT},
{"Hard Light", COMPOSITE_HARD_LIGHT},
// Compare
{"Difference", COMPOSITE_DIFFERENCE},
{"Exclusion", COMPOSITE_EXCLUSION},
};
const int composite_choices_count = sizeof(composite_choices)/sizeof(CompositeChoice);
}
// Init default settings for a clip
void Clip::init_settings()
{
// Init clip settings
Position(0.0);
Layer(0);
Start(0.0);
ClipBase::End(0.0);
gravity = GRAVITY_CENTER;
scale = SCALE_FIT;
anchor = ANCHOR_CANVAS;
display = FRAME_DISPLAY_NONE;
mixing = VOLUME_MIX_NONE;
composite = COMPOSITE_SOURCE_OVER;
waveform = false;
previous_properties = "";
parentObjectId = "";
// Init scale curves
scale_x = Keyframe(1.0);
scale_y = Keyframe(1.0);
// Init location curves
location_x = Keyframe(0.0);
location_y = Keyframe(0.0);
// Init alpha
alpha = Keyframe(1.0);
// Init time & volume
time = Keyframe(1.0);
volume = Keyframe(1.0);
// Init audio waveform color
wave_color = Color((unsigned char)0, (unsigned char)123, (unsigned char)255, (unsigned char)255);
// Init shear and perspective curves
shear_x = Keyframe(0.0);
shear_y = Keyframe(0.0);
origin_x = Keyframe(0.5);
origin_y = Keyframe(0.5);
perspective_c1_x = Keyframe(-1.0);
perspective_c1_y = Keyframe(-1.0);
perspective_c2_x = Keyframe(-1.0);
perspective_c2_y = Keyframe(-1.0);
perspective_c3_x = Keyframe(-1.0);
perspective_c3_y = Keyframe(-1.0);
perspective_c4_x = Keyframe(-1.0);
perspective_c4_y = Keyframe(-1.0);
// Init audio channel filter and mappings
channel_filter = Keyframe(-1.0);
channel_mapping = Keyframe(-1.0);
// Init audio and video overrides
has_audio = Keyframe(-1.0);
has_video = Keyframe(-1.0);
// Initialize the attached object and attached clip as null pointers
parentTrackedObject = nullptr;
parentClipObject = NULL;
// Init reader info struct
init_reader_settings();
}
// Init reader info details
void Clip::init_reader_settings() {
if (reader) {
// Init rotation (if any)
init_reader_rotation();
// Initialize info struct
info = reader->info;
// Init cache
final_cache.SetMaxBytesFromInfo(8, info.width, info.height, info.sample_rate, info.channels);
}
}
void Clip::init_reader_rotation() {
// Only apply metadata rotation if clip rotation has not been explicitly set.
if (rotation.GetCount() > 0 || !reader)
return;
const auto rotate_meta = reader->info.metadata.find("rotate");
if (rotate_meta == reader->info.metadata.end()) {
// Ensure rotation keyframes always start with a default 0° point.
rotation = Keyframe(0.0f);
return;
}
float rotate_angle = 0.0f;
try {
rotate_angle = strtof(rotate_meta->second.c_str(), nullptr);
} catch (const std::exception& e) {
return; // ignore invalid metadata
}
rotation = Keyframe(rotate_angle);
// Do not overwrite user-authored scale curves.
auto has_default_scale = [](const Keyframe& kf) {
return kf.GetCount() == 1 && fabs(kf.GetPoint(0).co.Y - 1.0) < 0.00001;
};
if (!has_default_scale(scale_x) || !has_default_scale(scale_y))
return;
// No need to adjust scaling when the metadata rotation is effectively zero.
if (fabs(rotate_angle) < 0.0001f)
return;
float w = static_cast<float>(reader->info.width);
float h = static_cast<float>(reader->info.height);
if (w <= 0.0f || h <= 0.0f)
return;
float rad = rotate_angle * static_cast<float>(M_PI) / 180.0f;
float new_width = fabs(w * cos(rad)) + fabs(h * sin(rad));
float new_height = fabs(w * sin(rad)) + fabs(h * cos(rad));
if (new_width <= 0.0f || new_height <= 0.0f)
return;
float uniform_scale = std::min(w / new_width, h / new_height);
scale_x = Keyframe(uniform_scale);
scale_y = Keyframe(uniform_scale);
}
// Default Constructor for a clip
Clip::Clip() : resampler(NULL), reader(NULL), allocated_reader(NULL), is_open(false)
{
// Init all default settings
init_settings();
}
// Constructor with reader
Clip::Clip(ReaderBase* new_reader) : resampler(NULL), reader(new_reader), allocated_reader(NULL), is_open(false)
{
// Init all default settings
init_settings();
// Open and Close the reader (to set the duration of the clip)
Open();
Close();
// Update duration and set parent
if (reader) {
ClipBase::End(reader->info.duration);
reader->ParentClip(this);
// Init reader info struct
init_reader_settings();
}
}
// Constructor with filepath
Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(NULL), is_open(false)
{
// Init all default settings
init_settings();
// Get file extension (and convert to lower case)
std::string ext = get_file_extension(path);
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
// Determine if common video formats (or image sequences)
if (ext=="avi" || ext=="mov" || ext=="mkv" || ext=="mpg" || ext=="mpeg" || ext=="mp3" || ext=="mp4" || ext=="mts" ||
ext=="ogg" || ext=="wav" || ext=="wmv" || ext=="webm" || ext=="vob" || ext=="gif" || path.find("%") != std::string::npos)
{
try
{
// Open common video format
reader = new openshot::FFmpegReader(path);
} catch(...) { }
}
if (ext=="osp")
{
try
{
// Open common video format
reader = new openshot::Timeline(path, true);
} catch(...) { }
}
// If no video found, try each reader
if (!reader)
{
try
{
// Try an image reader
reader = new openshot::QtImageReader(path);
} catch(...) {
try
{
// Try a video reader
reader = new openshot::FFmpegReader(path);
} catch(...) { }
}
}
// Update duration and set parent
if (reader) {
ClipBase::End(reader->info.duration);
reader->ParentClip(this);
allocated_reader = reader;
// Init reader info struct
init_reader_settings();
}
}
// Destructor
Clip::~Clip()
{
// Delete the reader if clip created it
if (allocated_reader) {
delete allocated_reader;
allocated_reader = NULL;
reader = NULL;
}
// Close the resampler
if (resampler) {
delete resampler;
resampler = NULL;
}
// Close clip
Close();
}
// Attach clip to bounding box
void Clip::AttachToObject(std::string object_id)
{
// Search for the tracked object on the timeline
Timeline* parentTimeline = static_cast<Timeline *>(ParentTimeline());
if (parentTimeline) {
// Create a smart pointer to the tracked object from the timeline
std::shared_ptr<openshot::TrackedObjectBase> trackedObject = parentTimeline->GetTrackedObject(object_id);
Clip* clipObject = parentTimeline->GetClip(object_id);
// Check for valid tracked object
if (trackedObject){
SetAttachedObject(trackedObject);
parentClipObject = NULL;
}
else if (clipObject) {
SetAttachedClip(clipObject);
parentTrackedObject = nullptr;
}
}
}
// Set the pointer to the trackedObject this clip is attached to
void Clip::SetAttachedObject(std::shared_ptr<openshot::TrackedObjectBase> trackedObject){
parentTrackedObject = trackedObject;
}
// Set the pointer to the clip this clip is attached to
void Clip::SetAttachedClip(Clip* clipObject){
parentClipObject = clipObject;
}
void Clip::Reader(ReaderBase* new_reader)
{
// Delete previously allocated reader (if not related to new reader)
// FrameMappers that point to the same allocated reader are ignored
bool is_same_reader = false;
if (new_reader && allocated_reader) {
if (new_reader->Name() == "FrameMapper") {
// Determine if FrameMapper is pointing at the same allocated ready
FrameMapper* clip_mapped_reader = static_cast<FrameMapper*>(new_reader);
if (allocated_reader == clip_mapped_reader->Reader()) {
is_same_reader = true;
}
}
}
// Clear existing allocated reader (if different)
if (allocated_reader && !is_same_reader) {
reader->Close();
allocated_reader->Close();
delete allocated_reader;
reader = NULL;
allocated_reader = NULL;
}
// set reader pointer
reader = new_reader;
// set parent
if (reader) {
reader->ParentClip(this);
// Init reader info struct
init_reader_settings();
}
}
ReaderBase* Clip::Reader()
{
if (reader)
return reader;
else
// Throw error if reader not initialized
throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
}
// Open the internal reader
void Clip::Open()
{
if (reader)
{
// Open the reader
reader->Open();
is_open = true;
// Copy Reader info to Clip
info = reader->info;
// Set some clip properties from the file reader
if (end == 0.0)
ClipBase::End(reader->info.duration);
}
else
// Throw error if reader not initialized
throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
}
// Close the internal reader
void Clip::Close()
{
if (is_open && reader) {
ZmqLogger::Instance()->AppendDebugMethod("Clip::Close");
// Close the reader
reader->Close();
}
// Clear cache
final_cache.Clear();
is_open = false;
}
// Get end position of clip (trim end of video), which can be affected by the time curve.
float Clip::End() const
{
// if a time curve is present, use its length
if (time.GetCount() > 1)
{
// Determine the FPS fo this clip
float fps = 24.0;
if (reader)
// file reader
fps = reader->info.fps.ToFloat();
else
// Throw error if reader not initialized
throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
return float(time.GetLength()) / fps;
}
else
// just use the duration (as detected by the reader)
return end;
}
// Override End() position
void Clip::End(float value) {
ClipBase::End(value);
}
// Set associated Timeline pointer
void Clip::ParentTimeline(openshot::TimelineBase* new_timeline) {
timeline = new_timeline;
// Clear cache (it might have changed)
final_cache.Clear();
}
// Create an openshot::Frame object for a specific frame number of this reader.
std::shared_ptr<Frame> Clip::GetFrame(int64_t clip_frame_number)
{
// Call override of GetFrame
return GetFrame(NULL, clip_frame_number, NULL);
}
// Create an openshot::Frame object for a specific frame number of this reader.
// NOTE: background_frame is ignored in this method (this method is only used by Effect classes)
std::shared_ptr<Frame> Clip::GetFrame(std::shared_ptr<openshot::Frame> background_frame, int64_t clip_frame_number)
{
// Call override of GetFrame
return GetFrame(background_frame, clip_frame_number, NULL);
}
// Use an existing openshot::Frame object and draw this Clip's frame onto it
std::shared_ptr<Frame> Clip::GetFrame(std::shared_ptr<openshot::Frame> background_frame, int64_t clip_frame_number, openshot::TimelineInfoStruct* options)
{
// Check for open reader (or throw exception)
if (!is_open)
throw ReaderClosed("The Clip is closed. Call Open() before calling this method.");
if (reader)
{
// Get frame object
std::shared_ptr<Frame> frame = NULL;
// Check cache
frame = final_cache.GetFrame(clip_frame_number);
if (!frame) {
// Generate clip frame
frame = GetOrCreateFrame(clip_frame_number);
// Get frame size and frame #
int64_t timeline_frame_number = clip_frame_number;
QSize timeline_size(frame->GetWidth(), frame->GetHeight());
if (background_frame) {
// If a background frame is provided, use it instead
timeline_frame_number = background_frame->number;
timeline_size.setWidth(background_frame->GetWidth());
timeline_size.setHeight(background_frame->GetHeight());
}
// Get time mapped frame object (used to increase speed, change direction, etc...)
apply_timemapping(frame);
// Apply waveform image (if any)
apply_waveform(frame, timeline_size);
// Apply effects BEFORE applying keyframes (if any local or global effects are used)
apply_effects(frame, timeline_frame_number, options, true);
// Apply keyframe / transforms to current clip image
apply_keyframes(frame, timeline_size);
// Apply effects AFTER applying keyframes (if any local or global effects are used)
apply_effects(frame, timeline_frame_number, options, false);
// Add final frame to cache (before flattening into background_frame)
final_cache.Add(frame);
}
if (!background_frame) {
// Create missing background_frame w/ transparent color (if needed)
background_frame = std::make_shared<Frame>(frame->number, frame->GetWidth(), frame->GetHeight(),
"#00000000", frame->GetAudioSamplesCount(),
frame->GetAudioChannelsCount());
}
// Apply background canvas (i.e. flatten this image onto previous layer image)
apply_background(frame, background_frame);
// Return processed 'frame'
return frame;
}
else
// Throw error if reader not initialized
throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
}
// Look up an effect by ID
openshot::EffectBase* Clip::GetEffect(const std::string& id)
{
// Find the matching effect (if any)
for (const auto& effect : effects) {
if (effect->Id() == id) {
return effect;
}
}
return nullptr;
}
// Return the associated ParentClip (if any)
openshot::Clip* Clip::GetParentClip() {
if (!parentObjectId.empty() && (!parentClipObject && !parentTrackedObject)) {
// Attach parent clip OR object to this clip
AttachToObject(parentObjectId);
}
return parentClipObject;
}
// Return the associated Parent Tracked Object (if any)
std::shared_ptr<openshot::TrackedObjectBase> Clip::GetParentTrackedObject() {
if (!parentObjectId.empty() && (!parentClipObject && !parentTrackedObject)) {
// Attach parent clip OR object to this clip
AttachToObject(parentObjectId);
}
return parentTrackedObject;
}
// Get file extension
std::string Clip::get_file_extension(std::string path)
{
// Return last part of path safely (handle filenames without a dot)
const auto dot_pos = path.find_last_of('.');
if (dot_pos == std::string::npos || dot_pos + 1 >= path.size()) {
return std::string();
}
return path.substr(dot_pos + 1);
}
// Adjust the audio and image of a time mapped frame
void Clip::apply_timemapping(std::shared_ptr<Frame> frame)
{
// Check for valid reader
if (!reader)
// Throw error if reader not initialized
throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
// Check for a valid time map curve
if (time.GetLength() > 1)
{
const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
int64_t clip_frame_number = frame->number;
int64_t new_frame_number = adjust_frame_number_minimum(time.GetLong(clip_frame_number));
// create buffer
juce::AudioBuffer<float> *source_samples = nullptr;
// Get delta (difference from this frame to the next time mapped frame: Y value)
double delta = time.GetDelta(clip_frame_number + 1);
const bool prev_is_increasing = time.IsIncreasing(clip_frame_number);
const bool is_increasing = time.IsIncreasing(clip_frame_number + 1);
// Determine length of source audio (in samples)
// A delta of 1.0 == normal expected samples
// A delta of 0.5 == 50% of normal expected samples
// A delta of 2.0 == 200% of normal expected samples
int target_sample_count = Frame::GetSamplesPerFrame(adjust_timeline_framenumber(clip_frame_number), Reader()->info.fps,
Reader()->info.sample_rate,
Reader()->info.channels);
int source_sample_count = round(target_sample_count * fabs(delta));
// Determine starting audio location
AudioLocation location;
if (previous_location.frame == 0 || abs(new_frame_number - previous_location.frame) > 2 || prev_is_increasing != is_increasing) {
// No previous location OR gap detected
location.frame = new_frame_number;
location.sample_start = 0;
// Create / Reset resampler
// We don't want to interpolate between unrelated audio data
if (resampler) {
delete resampler;
resampler = nullptr;
}
// Init resampler with # channels from Reader (should match the timeline)
resampler = new AudioResampler(Reader()->info.channels);
// Allocate buffer of silence to initialize some data inside the resampler
// To prevent it from becoming input limited
juce::AudioBuffer<float> init_samples(Reader()->info.channels, 64);
init_samples.clear();
resampler->SetBuffer(&init_samples, 1.0);
resampler->GetResampledBuffer();
} else {
// Use previous location
location = previous_location;
}
if (source_sample_count <= 0) {
// Add silence and bail (we don't need any samples)
frame->AddAudioSilence(target_sample_count);
return;
}
// Allocate a new sample buffer for these delta frames
source_samples = new juce::AudioBuffer<float>(Reader()->info.channels, source_sample_count);
source_samples->clear();
// Determine ending audio location
int remaining_samples = source_sample_count;
int source_pos = 0;
while (remaining_samples > 0) {
std::shared_ptr<Frame> source_frame = GetOrCreateFrame(location.frame, false);
int frame_sample_count = source_frame->GetAudioSamplesCount() - location.sample_start;
// Inform FrameMapper of the direction for THIS mapper frame
if (auto *fm = dynamic_cast<FrameMapper*>(reader)) {
fm->SetDirectionHint(is_increasing);
}
source_frame->SetAudioDirection(is_increasing);
if (frame_sample_count == 0) {
// No samples found in source frame (fill with silence)
if (is_increasing) {
location.frame++;
} else {
location.frame--;
}
location.sample_start = 0;
break;
}
if (remaining_samples - frame_sample_count >= 0) {
// Use all frame samples & increment location
for (int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++) {
source_samples->addFrom(channel, source_pos, source_frame->GetAudioSamples(channel) + location.sample_start, frame_sample_count, 1.0f);
}
if (is_increasing) {
location.frame++;
} else {
location.frame--;
}
location.sample_start = 0;
remaining_samples -= frame_sample_count;
source_pos += frame_sample_count;
} else {
// Use just what is needed (and reverse samples)
for (int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++) {
source_samples->addFrom(channel, source_pos, source_frame->GetAudioSamples(channel) + location.sample_start, remaining_samples, 1.0f);
}
location.sample_start += remaining_samples;
remaining_samples = 0;
source_pos += remaining_samples;
}
}
// Resize audio for current frame object + fill with silence
// We are fixing to clobber this with actual audio data (possibly resampled)
frame->AddAudioSilence(target_sample_count);
if (source_sample_count != target_sample_count) {
// Resample audio (if needed)
double resample_ratio = double(source_sample_count) / double(target_sample_count);
resampler->SetBuffer(source_samples, resample_ratio);
// Resample the data
juce::AudioBuffer<float> *resampled_buffer = resampler->GetResampledBuffer();
// Fill the frame with resampled data
for (int channel = 0; channel < Reader()->info.channels; channel++) {
// Add new (slower) samples, to the frame object
frame->AddAudio(true, channel, 0, resampled_buffer->getReadPointer(channel, 0), std::min(resampled_buffer->getNumSamples(), target_sample_count), 1.0f);
}
} else {
// Fill the frame
for (int channel = 0; channel < Reader()->info.channels; channel++) {
// Add new (slower) samples, to the frame object
frame->AddAudio(true, channel, 0, source_samples->getReadPointer(channel, 0), target_sample_count, 1.0f);
}
}
// Clean up
delete source_samples;
// Set previous location
previous_location = location;
}
}
// Adjust frame number minimum value
int64_t Clip::adjust_frame_number_minimum(int64_t frame_number)
{
// Never return a frame number 0 or below
if (frame_number < 1)
return 1;
else
return frame_number;
}
// Get or generate a blank frame
std::shared_ptr<Frame> Clip::GetOrCreateFrame(int64_t number, bool enable_time)
{
try {
// Init to requested frame
int64_t clip_frame_number = adjust_frame_number_minimum(number);
bool is_increasing = true;
// Adjust for time-mapping (if any)
if (enable_time && time.GetLength() > 1) {
is_increasing = time.IsIncreasing(clip_frame_number + 1);
const int64_t time_frame_number = adjust_frame_number_minimum(time.GetLong(clip_frame_number));
if (auto *fm = dynamic_cast<FrameMapper*>(reader)) {
// Inform FrameMapper which direction this mapper frame is being requested
fm->SetDirectionHint(is_increasing);
}
clip_frame_number = time_frame_number;
}
// Debug output
ZmqLogger::Instance()->AppendDebugMethod(
"Clip::GetOrCreateFrame (from reader)",
"number", number, "clip_frame_number", clip_frame_number);
// Attempt to get a frame (but this could fail if a reader has just been closed)
auto reader_frame = reader->GetFrame(clip_frame_number);
if (reader_frame) {
// Override frame # (due to time-mapping might change it)
reader_frame->number = number;
reader_frame->SetAudioDirection(is_increasing);
// Return real frame
// Create a new copy of reader frame
// This allows a clip to modify the pixels and audio of this frame without
// changing the underlying reader's frame data
auto reader_copy = std::make_shared<Frame>(*reader_frame.get());
if (has_video.GetInt(number) == 0) {
// No video, so add transparent pixels
reader_copy->AddColor(QColor(Qt::transparent));
}
if (has_audio.GetInt(number) == 0 || number > reader->info.video_length) {
// No audio, so include silence (also, mute audio if past end of reader)
reader_copy->AddAudioSilence(reader_copy->GetAudioSamplesCount());
}
return reader_copy;
}
} catch (const ReaderClosed & e) {
// ...
} catch (const OutOfBoundsFrame & e) {
// ...
}
// Estimate # of samples needed for this frame
int estimated_samples_in_frame = Frame::GetSamplesPerFrame(number, reader->info.fps, reader->info.sample_rate, reader->info.channels);
// Debug output
ZmqLogger::Instance()->AppendDebugMethod(
"Clip::GetOrCreateFrame (create blank)",
"number", number,
"estimated_samples_in_frame", estimated_samples_in_frame);
// Create blank frame
auto new_frame = std::make_shared<Frame>(
number, reader->info.width, reader->info.height,
"#000000", estimated_samples_in_frame, reader->info.channels);
new_frame->SampleRate(reader->info.sample_rate);
new_frame->ChannelsLayout(reader->info.channel_layout);
new_frame->AddAudioSilence(estimated_samples_in_frame);
return new_frame;
}
// Generate JSON string of this object
std::string Clip::Json() const {
// Return formatted string
return JsonValue().toStyledString();
}
// Get all properties for a specific frame
std::string Clip::PropertiesJSON(int64_t requested_frame) const {
// Generate JSON properties list
Json::Value root;
root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame);
root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame);
root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame);
root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame);
root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame);
root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 30 * 60 * 60 * 48, true, requested_frame);
root["gravity"] = add_property_json("Gravity", gravity, "int", "", NULL, 0, 8, false, requested_frame);
root["scale"] = add_property_json("Scale", scale, "int", "", NULL, 0, 3, false, requested_frame);
root["display"] = add_property_json("Frame Number", display, "int", "", NULL, 0, 3, false, requested_frame);
root["mixing"] = add_property_json("Volume Mixing", mixing, "int", "", NULL, 0, 2, false, requested_frame);
root["composite"] = add_property_json("Composite", composite, "int", "", NULL, 0, composite_choices_count - 1, false, requested_frame);
root["waveform"] = add_property_json("Waveform", waveform, "int", "", NULL, 0, 1, false, requested_frame);
root["parentObjectId"] = add_property_json("Parent", 0.0, "string", parentObjectId, NULL, -1, -1, false, requested_frame);
// Add gravity choices (dropdown style)
root["gravity"]["choices"].append(add_property_choice_json("Top Left", GRAVITY_TOP_LEFT, gravity));
root["gravity"]["choices"].append(add_property_choice_json("Top Center", GRAVITY_TOP, gravity));
root["gravity"]["choices"].append(add_property_choice_json("Top Right", GRAVITY_TOP_RIGHT, gravity));
root["gravity"]["choices"].append(add_property_choice_json("Left", GRAVITY_LEFT, gravity));
root["gravity"]["choices"].append(add_property_choice_json("Center", GRAVITY_CENTER, gravity));
root["gravity"]["choices"].append(add_property_choice_json("Right", GRAVITY_RIGHT, gravity));
root["gravity"]["choices"].append(add_property_choice_json("Bottom Left", GRAVITY_BOTTOM_LEFT, gravity));
root["gravity"]["choices"].append(add_property_choice_json("Bottom Center", GRAVITY_BOTTOM, gravity));
root["gravity"]["choices"].append(add_property_choice_json("Bottom Right", GRAVITY_BOTTOM_RIGHT, gravity));
// Add scale choices (dropdown style)
root["scale"]["choices"].append(add_property_choice_json("Crop", SCALE_CROP, scale));
root["scale"]["choices"].append(add_property_choice_json("Best Fit", SCALE_FIT, scale));
root["scale"]["choices"].append(add_property_choice_json("Stretch", SCALE_STRETCH, scale));
root["scale"]["choices"].append(add_property_choice_json("None", SCALE_NONE, scale));
// Add frame number display choices (dropdown style)
root["display"]["choices"].append(add_property_choice_json("None", FRAME_DISPLAY_NONE, display));
root["display"]["choices"].append(add_property_choice_json("Clip", FRAME_DISPLAY_CLIP, display));
root["display"]["choices"].append(add_property_choice_json("Timeline", FRAME_DISPLAY_TIMELINE, display));
root["display"]["choices"].append(add_property_choice_json("Both", FRAME_DISPLAY_BOTH, display));
// Add volume mixing choices (dropdown style)
root["mixing"]["choices"].append(add_property_choice_json("None", VOLUME_MIX_NONE, mixing));
root["mixing"]["choices"].append(add_property_choice_json("Average", VOLUME_MIX_AVERAGE, mixing));
root["mixing"]["choices"].append(add_property_choice_json("Reduce", VOLUME_MIX_REDUCE, mixing));
// Add composite choices (dropdown style)
for (int i = 0; i < composite_choices_count; ++i)
root["composite"]["choices"].append(add_property_choice_json(composite_choices[i].name, composite_choices[i].value, composite));
// Add waveform choices (dropdown style)
root["waveform"]["choices"].append(add_property_choice_json("Yes", true, waveform));
root["waveform"]["choices"].append(add_property_choice_json("No", false, waveform));
// Add the parentClipObject's properties
if (parentClipObject)
{
// Convert Clip's frame position to Timeline's frame position
long clip_start_position = round(Position() * info.fps.ToDouble()) + 1;
long clip_start_frame = (Start() * info.fps.ToDouble()) + 1;
double timeline_frame_number = requested_frame + clip_start_position - clip_start_frame;
// Correct the parent Clip Object properties by the clip's reference system
float parentObject_location_x = parentClipObject->location_x.GetValue(timeline_frame_number);
float parentObject_location_y = parentClipObject->location_y.GetValue(timeline_frame_number);
float parentObject_scale_x = parentClipObject->scale_x.GetValue(timeline_frame_number);
float parentObject_scale_y = parentClipObject->scale_y.GetValue(timeline_frame_number);
float parentObject_shear_x = parentClipObject->shear_x.GetValue(timeline_frame_number);
float parentObject_shear_y = parentClipObject->shear_y.GetValue(timeline_frame_number);
float parentObject_rotation = parentClipObject->rotation.GetValue(timeline_frame_number);
// Add the parent Clip Object properties to JSON
root["location_x"] = add_property_json("Location X", parentObject_location_x, "float", "", &location_x, -1.0, 1.0, false, requested_frame);
root["location_y"] = add_property_json("Location Y", parentObject_location_y, "float", "", &location_y, -1.0, 1.0, false, requested_frame);
root["scale_x"] = add_property_json("Scale X", parentObject_scale_x, "float", "", &scale_x, 0.0, 1.0, false, requested_frame);
root["scale_y"] = add_property_json("Scale Y", parentObject_scale_y, "float", "", &scale_y, 0.0, 1.0, false, requested_frame);
root["rotation"] = add_property_json("Rotation", parentObject_rotation, "float", "", &rotation, -360, 360, false, requested_frame);
root["shear_x"] = add_property_json("Shear X", parentObject_shear_x, "float", "", &shear_x, -1.0, 1.0, false, requested_frame);
root["shear_y"] = add_property_json("Shear Y", parentObject_shear_y, "float", "", &shear_y, -1.0, 1.0, false, requested_frame);
}
else
{
// Add this own clip's properties to JSON
root["location_x"] = add_property_json("Location X", location_x.GetValue(requested_frame), "float", "", &location_x, -1.0, 1.0, false, requested_frame);
root["location_y"] = add_property_json("Location Y", location_y.GetValue(requested_frame), "float", "", &location_y, -1.0, 1.0, false, requested_frame);
root["scale_x"] = add_property_json("Scale X", scale_x.GetValue(requested_frame), "float", "", &scale_x, 0.0, 1.0, false, requested_frame);
root["scale_y"] = add_property_json("Scale Y", scale_y.GetValue(requested_frame), "float", "", &scale_y, 0.0, 1.0, false, requested_frame);
root["rotation"] = add_property_json("Rotation", rotation.GetValue(requested_frame), "float", "", &rotation, -360, 360, false, requested_frame);
root["shear_x"] = add_property_json("Shear X", shear_x.GetValue(requested_frame), "float", "", &shear_x, -1.0, 1.0, false, requested_frame);
root["shear_y"] = add_property_json("Shear Y", shear_y.GetValue(requested_frame), "float", "", &shear_y, -1.0, 1.0, false, requested_frame);
}
// Keyframes
root["alpha"] = add_property_json("Alpha", alpha.GetValue(requested_frame), "float", "", &alpha, 0.0, 1.0, false, requested_frame);
root["origin_x"] = add_property_json("Origin X", origin_x.GetValue(requested_frame), "float", "", &origin_x, 0.0, 1.0, false, requested_frame);
root["origin_y"] = add_property_json("Origin Y", origin_y.GetValue(requested_frame), "float", "", &origin_y, 0.0, 1.0, false, requested_frame);
root["volume"] = add_property_json("Volume", volume.GetValue(requested_frame), "float", "", &volume, 0.0, 1.0, false, requested_frame);
root["time"] = add_property_json("Time", time.GetValue(requested_frame), "float", "", &time, 0.0, 30 * 60 * 60 * 48, false, requested_frame);
root["channel_filter"] = add_property_json("Channel Filter", channel_filter.GetValue(requested_frame), "int", "", &channel_filter, -1, 10, false, requested_frame);
root["channel_mapping"] = add_property_json("Channel Mapping", channel_mapping.GetValue(requested_frame), "int", "", &channel_mapping, -1, 10, false, requested_frame);
root["has_audio"] = add_property_json("Enable Audio", has_audio.GetValue(requested_frame), "int", "", &has_audio, -1, 1.0, false, requested_frame);
root["has_video"] = add_property_json("Enable Video", has_video.GetValue(requested_frame), "int", "", &has_video, -1, 1.0, false, requested_frame);
// Add enable audio/video choices (dropdown style)
root["has_audio"]["choices"].append(add_property_choice_json("Auto", -1, has_audio.GetValue(requested_frame)));
root["has_audio"]["choices"].append(add_property_choice_json("Off", 0, has_audio.GetValue(requested_frame)));
root["has_audio"]["choices"].append(add_property_choice_json("On", 1, has_audio.GetValue(requested_frame)));
root["has_video"]["choices"].append(add_property_choice_json("Auto", -1, has_video.GetValue(requested_frame)));
root["has_video"]["choices"].append(add_property_choice_json("Off", 0, has_video.GetValue(requested_frame)));
root["has_video"]["choices"].append(add_property_choice_json("On", 1, has_video.GetValue(requested_frame)));
root["wave_color"] = add_property_json("Wave Color", 0.0, "color", "", &wave_color.red, 0, 255, false, requested_frame);
root["wave_color"]["red"] = add_property_json("Red", wave_color.red.GetValue(requested_frame), "float", "", &wave_color.red, 0, 255, false, requested_frame);
root["wave_color"]["blue"] = add_property_json("Blue", wave_color.blue.GetValue(requested_frame), "float", "", &wave_color.blue, 0, 255, false, requested_frame);
root["wave_color"]["green"] = add_property_json("Green", wave_color.green.GetValue(requested_frame), "float", "", &wave_color.green, 0, 255, false, requested_frame);
root["wave_color"]["alpha"] = add_property_json("Alpha", wave_color.alpha.GetValue(requested_frame), "float", "", &wave_color.alpha, 0, 255, false, requested_frame);
// Return formatted string
return root.toStyledString();
}
// Generate Json::Value for this object
Json::Value Clip::JsonValue() const {
// Create root json object
Json::Value root = ClipBase::JsonValue(); // get parent properties
root["parentObjectId"] = parentObjectId;
root["gravity"] = gravity;
root["scale"] = scale;
root["anchor"] = anchor;
root["display"] = display;
root["mixing"] = mixing;
root["composite"] = composite;
root["waveform"] = waveform;
root["scale_x"] = scale_x.JsonValue();
root["scale_y"] = scale_y.JsonValue();
root["location_x"] = location_x.JsonValue();
root["location_y"] = location_y.JsonValue();
root["alpha"] = alpha.JsonValue();
root["rotation"] = rotation.JsonValue();
root["time"] = time.JsonValue();
root["volume"] = volume.JsonValue();
root["wave_color"] = wave_color.JsonValue();
root["shear_x"] = shear_x.JsonValue();
root["shear_y"] = shear_y.JsonValue();
root["origin_x"] = origin_x.JsonValue();
root["origin_y"] = origin_y.JsonValue();
root["channel_filter"] = channel_filter.JsonValue();
root["channel_mapping"] = channel_mapping.JsonValue();
root["has_audio"] = has_audio.JsonValue();
root["has_video"] = has_video.JsonValue();
root["perspective_c1_x"] = perspective_c1_x.JsonValue();
root["perspective_c1_y"] = perspective_c1_y.JsonValue();
root["perspective_c2_x"] = perspective_c2_x.JsonValue();
root["perspective_c2_y"] = perspective_c2_y.JsonValue();
root["perspective_c3_x"] = perspective_c3_x.JsonValue();
root["perspective_c3_y"] = perspective_c3_y.JsonValue();
root["perspective_c4_x"] = perspective_c4_x.JsonValue();
root["perspective_c4_y"] = perspective_c4_y.JsonValue();
// Add array of effects
root["effects"] = Json::Value(Json::arrayValue);
// loop through effects
for (auto existing_effect : effects)
{
root["effects"].append(existing_effect->JsonValue());
}
if (reader)
root["reader"] = reader->JsonValue();
else
root["reader"] = Json::Value(Json::objectValue);
// return JsonValue
return root;
}
// Load JSON string into this object
void Clip::SetJson(const std::string value) {
// Parse JSON string into JSON objects
try
{
const Json::Value root = openshot::stringToJson(value);
// Set all values that match
SetJsonValue(root);
}
catch (const std::exception& e)
{
// Error parsing JSON (or missing keys)
throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
}
}
// Load Json::Value into this object
void Clip::SetJsonValue(const Json::Value root) {
// Set parent data
ClipBase::SetJsonValue(root);
// Set data from Json (if key is found)
if (!root["parentObjectId"].isNull()){
parentObjectId = root["parentObjectId"].asString();
if (parentObjectId.size() > 0 && parentObjectId != ""){
AttachToObject(parentObjectId);
} else{
parentTrackedObject = nullptr;
parentClipObject = NULL;
}
}
if (!root["gravity"].isNull())
gravity = (GravityType) root["gravity"].asInt();
if (!root["scale"].isNull())
scale = (ScaleType) root["scale"].asInt();
if (!root["anchor"].isNull())
anchor = (AnchorType) root["anchor"].asInt();
if (!root["display"].isNull())
display = (FrameDisplayType) root["display"].asInt();
if (!root["mixing"].isNull())
mixing = (VolumeMixType) root["mixing"].asInt();
if (!root["composite"].isNull())
composite = (CompositeType) root["composite"].asInt();
if (!root["waveform"].isNull())
waveform = root["waveform"].asBool();
if (!root["scale_x"].isNull())
scale_x.SetJsonValue(root["scale_x"]);
if (!root["scale_y"].isNull())
scale_y.SetJsonValue(root["scale_y"]);
if (!root["location_x"].isNull())
location_x.SetJsonValue(root["location_x"]);
if (!root["location_y"].isNull())
location_y.SetJsonValue(root["location_y"]);
if (!root["alpha"].isNull())
alpha.SetJsonValue(root["alpha"]);
if (!root["rotation"].isNull())
rotation.SetJsonValue(root["rotation"]);
if (!root["time"].isNull())
time.SetJsonValue(root["time"]);
if (!root["volume"].isNull())
volume.SetJsonValue(root["volume"]);
if (!root["wave_color"].isNull())
wave_color.SetJsonValue(root["wave_color"]);
if (!root["shear_x"].isNull())
shear_x.SetJsonValue(root["shear_x"]);
if (!root["shear_y"].isNull())
shear_y.SetJsonValue(root["shear_y"]);
if (!root["origin_x"].isNull())
origin_x.SetJsonValue(root["origin_x"]);
if (!root["origin_y"].isNull())
origin_y.SetJsonValue(root["origin_y"]);
if (!root["channel_filter"].isNull())
channel_filter.SetJsonValue(root["channel_filter"]);
if (!root["channel_mapping"].isNull())
channel_mapping.SetJsonValue(root["channel_mapping"]);
if (!root["has_audio"].isNull())
has_audio.SetJsonValue(root["has_audio"]);
if (!root["has_video"].isNull())
has_video.SetJsonValue(root["has_video"]);
if (!root["perspective_c1_x"].isNull())
perspective_c1_x.SetJsonValue(root["perspective_c1_x"]);
if (!root["perspective_c1_y"].isNull())
perspective_c1_y.SetJsonValue(root["perspective_c1_y"]);
if (!root["perspective_c2_x"].isNull())
perspective_c2_x.SetJsonValue(root["perspective_c2_x"]);
if (!root["perspective_c2_y"].isNull())
perspective_c2_y.SetJsonValue(root["perspective_c2_y"]);
if (!root["perspective_c3_x"].isNull())
perspective_c3_x.SetJsonValue(root["perspective_c3_x"]);
if (!root["perspective_c3_y"].isNull())
perspective_c3_y.SetJsonValue(root["perspective_c3_y"]);
if (!root["perspective_c4_x"].isNull())
perspective_c4_x.SetJsonValue(root["perspective_c4_x"]);
if (!root["perspective_c4_y"].isNull())
perspective_c4_y.SetJsonValue(root["perspective_c4_y"]);
if (!root["effects"].isNull()) {
// Clear existing effects
effects.clear();
// loop through effects
for (const auto existing_effect : root["effects"]) {
// Skip NULL nodes
if (existing_effect.isNull()) {
continue;
}
// Create Effect
EffectBase *e = NULL;
if (!existing_effect["type"].isNull()) {
// Create instance of effect
if ( (e = EffectInfo().CreateEffect(existing_effect["type"].asString()))) {
// Load Json into Effect
e->SetJsonValue(existing_effect);
// Add Effect to Timeline
AddEffect(e);
}
}
}
}
if (!root["reader"].isNull()) // does Json contain a reader?
{
if (!root["reader"]["type"].isNull()) // does the reader Json contain a 'type'?
{
// Close previous reader (if any)
bool already_open = false;
if (reader)
{
// Track if reader was open
already_open = reader->IsOpen();
// Close and delete existing allocated reader (if any)
Reader(NULL);
}
// Create new reader (and load properties)
std::string type = root["reader"]["type"].asString();
if (type == "FFmpegReader") {
// Create new reader
reader = new openshot::FFmpegReader(root["reader"]["path"].asString(), false);
reader->SetJsonValue(root["reader"]);
} else if (type == "QtImageReader") {
// Create new reader
reader = new openshot::QtImageReader(root["reader"]["path"].asString(), false);
reader->SetJsonValue(root["reader"]);
#ifdef USE_IMAGEMAGICK
} else if (type == "ImageReader") {
// Create new reader
reader = new ImageReader(root["reader"]["path"].asString(), false);
reader->SetJsonValue(root["reader"]);
} else if (type == "TextReader") {
// Create new reader
reader = new TextReader();
reader->SetJsonValue(root["reader"]);
#endif
} else if (type == "ChunkReader") {
// Create new reader
reader = new openshot::ChunkReader(root["reader"]["path"].asString(), (ChunkVersion) root["reader"]["chunk_version"].asInt());
reader->SetJsonValue(root["reader"]);
} else if (type == "DummyReader") {
// Create new reader
reader = new openshot::DummyReader();
reader->SetJsonValue(root["reader"]);
} else if (type == "Timeline") {
// Create new reader (always load from file again)
// This prevents FrameMappers from being loaded on accident
reader = new openshot::Timeline(root["reader"]["path"].asString(), true);
}
// mark as managed reader and set parent
if (reader) {
reader->ParentClip(this);
allocated_reader = reader;
}
// Re-Open reader (if needed)
if (already_open) {
reader->Open();
}
}
}
// Clear cache (it might have changed)
final_cache.Clear();
}
// Sort effects by order
void Clip::sort_effects()
{
// sort clips
effects.sort(CompareClipEffects());
}
// Add an effect to the clip
void Clip::AddEffect(EffectBase* effect)
{
// Set parent clip pointer
effect->ParentClip(this);
// Add effect to list
effects.push_back(effect);
// Sort effects
sort_effects();
// Get the parent timeline of this clip
Timeline* parentTimeline = static_cast<Timeline *>(ParentTimeline());
if (parentTimeline)
effect->ParentTimeline(parentTimeline);
#ifdef USE_OPENCV
// Add Tracked Object to Timeline
if (effect->info.has_tracked_object){
// Check if this clip has a parent timeline
if (parentTimeline){
effect->ParentTimeline(parentTimeline);
// Iterate through effect's vector of Tracked Objects
for (auto const& trackedObject : effect->trackedObjects){
// Cast the Tracked Object as TrackedObjectBBox
std::shared_ptr<TrackedObjectBBox> trackedObjectBBox = std::static_pointer_cast<TrackedObjectBBox>(trackedObject.second);
// Set the Tracked Object's parent clip to this
trackedObjectBBox->ParentClip(this);
// Add the Tracked Object to the timeline
parentTimeline->AddTrackedObject(trackedObjectBBox);
}
}
}
#endif
// Clear cache (it might have changed)
final_cache.Clear();
}
// Remove an effect from the clip
void Clip::RemoveEffect(EffectBase* effect)
{
effects.remove(effect);
// Clear cache (it might have changed)
final_cache.Clear();
}
// Apply background image to the current clip image (i.e. flatten this image onto previous layer)
void Clip::apply_background(std::shared_ptr<openshot::Frame> frame, std::shared_ptr<openshot::Frame> background_frame) {
// Add background canvas
std::shared_ptr<QImage> background_canvas = background_frame->GetImage();
QPainter painter(background_canvas.get());
// Composite a new layer onto the image
painter.setCompositionMode(static_cast<QPainter::CompositionMode>(composite));
painter.drawImage(0, 0, *frame->GetImage());
painter.end();
// Add new QImage to frame
frame->AddImage(background_canvas);
}
// Apply effects to the source frame (if any)
void Clip::apply_effects(std::shared_ptr<Frame> frame, int64_t timeline_frame_number, TimelineInfoStruct* options, bool before_keyframes)
{
for (auto effect : effects)
{
// Apply the effect to this frame
if (effect->info.apply_before_clip && before_keyframes) {
effect->GetFrame(frame, frame->number);
} else if (!effect->info.apply_before_clip && !before_keyframes) {
effect->GetFrame(frame, frame->number);
}
}
if (timeline != NULL && options != NULL) {
// Apply global timeline effects (i.e. transitions & masks... if any)
Timeline* timeline_instance = static_cast<Timeline*>(timeline);
options->is_before_clip_keyframes = before_keyframes;
timeline_instance->apply_effects(frame, timeline_frame_number, Layer(), options);
}
}
// Compare 2 floating point numbers for equality
bool Clip::isNear(double a, double b)
{
return fabs(a - b) < 0.000001;
}
// Apply keyframes to the source frame (if any)
void Clip::apply_keyframes(std::shared_ptr<Frame> frame, QSize timeline_size) {
// Skip out if video was disabled or only an audio frame (no visualisation in use)
if (!frame->has_image_data) {
// Skip the rest of the image processing for performance reasons
return;
}
// Get image from clip, and create transparent background image
std::shared_ptr<QImage> source_image = frame->GetImage();
std::shared_ptr<QImage> background_canvas = std::make_shared<QImage>(timeline_size.width(),
timeline_size.height(),
QImage::Format_RGBA8888_Premultiplied);
background_canvas->fill(QColor(Qt::transparent));
// Get transform from clip's keyframes
QTransform transform = get_transform(frame, background_canvas->width(), background_canvas->height());
// Load timeline's new frame image into a QPainter
QPainter painter(background_canvas.get());
painter.setRenderHint(QPainter::TextAntialiasing, true);
if (!transform.isIdentity()) {
painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
}
// Apply transform (translate, rotate, scale)
painter.setTransform(transform);
// Composite a new layer onto the image
painter.setCompositionMode(static_cast<QPainter::CompositionMode>(composite));
// Apply opacity via painter instead of per-pixel alpha manipulation
const float alpha_value = alpha.GetValue(frame->number);
if (alpha_value != 1.0f) {
painter.setOpacity(alpha_value);
painter.drawImage(0, 0, *source_image);
// Reset so any subsequent drawing (e.g., overlays) isn’t faded
painter.setOpacity(1.0);
} else {
painter.drawImage(0, 0, *source_image);
}
if (timeline) {
Timeline *t = static_cast<Timeline *>(timeline);
// Draw frame #'s on top of image (if needed)
if (display != FRAME_DISPLAY_NONE) {
std::stringstream frame_number_str;
switch (display) {
case (FRAME_DISPLAY_NONE):
// This is only here to prevent unused-enum warnings
break;
case (FRAME_DISPLAY_CLIP):
frame_number_str << frame->number;
break;
case (FRAME_DISPLAY_TIMELINE):
frame_number_str << round((Position() - Start()) * t->info.fps.ToFloat()) + frame->number;
break;
case (FRAME_DISPLAY_BOTH):
frame_number_str << round((Position() - Start()) * t->info.fps.ToFloat()) + frame->number << " (" << frame->number << ")";
break;
}
// Draw frame number on top of image
painter.setPen(QColor("#ffffff"));
painter.drawText(20, 20, QString(frame_number_str.str().c_str()));
}
}
painter.end();
// Add new QImage to frame
frame->AddImage(background_canvas);
}
// Apply apply_waveform image to the source frame (if any)
void Clip::apply_waveform(std::shared_ptr<Frame> frame, QSize timeline_size) {
if (!Waveform()) {
// Exit if no waveform is needed
return;
}
// Get image from clip
std::shared_ptr<QImage> source_image = frame->GetImage();
// Debug output
ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_waveform (Generate Waveform Image)",
"frame->number", frame->number,
"Waveform()", Waveform(),
"width", timeline_size.width(),
"height", timeline_size.height());
// Get the color of the waveform
int red = wave_color.red.GetInt(frame->number);
int green = wave_color.green.GetInt(frame->number);
int blue = wave_color.blue.GetInt(frame->number);
int alpha = wave_color.alpha.GetInt(frame->number);
// Generate Waveform Dynamically (the size of the timeline)
source_image = frame->GetWaveform(timeline_size.width(), timeline_size.height(), red, green, blue, alpha);
frame->AddImage(source_image);
}
// Scale a source size to a target size (given a specific scale-type)
QSize Clip::scale_size(QSize source_size, ScaleType source_scale, int target_width, int target_height) {
switch (source_scale)
{
case (SCALE_FIT): {
source_size.scale(target_width, target_height, Qt::KeepAspectRatio);
break;
}
case (SCALE_STRETCH): {
source_size.scale(target_width, target_height, Qt::IgnoreAspectRatio);
break;
}
case (SCALE_CROP): {
source_size.scale(target_width, target_height, Qt::KeepAspectRatioByExpanding);;
break;
}
}
return source_size;
}
// Get QTransform from keyframes
QTransform Clip::get_transform(std::shared_ptr<Frame> frame, int width, int height)
{
// Get image from clip
std::shared_ptr<QImage> source_image = frame->GetImage();
/* RESIZE SOURCE IMAGE - based on scale type */
QSize source_size = scale_size(source_image->size(), scale, width, height);
// Initialize parent object's properties (Clip or Tracked Object)
float parentObject_location_x = 0.0;
float parentObject_location_y = 0.0;
float parentObject_scale_x = 1.0;
float parentObject_scale_y = 1.0;
float parentObject_shear_x = 0.0;
float parentObject_shear_y = 0.0;
float parentObject_rotation = 0.0;
// Get the parentClipObject properties
if (GetParentClip()){
// Get the start trim position of the parent clip
long parent_start_offset = parentClipObject->Start() * info.fps.ToDouble();
long parent_frame_number = frame->number + parent_start_offset;
// Get parent object's properties (Clip)
parentObject_location_x = parentClipObject->location_x.GetValue(parent_frame_number);
parentObject_location_y = parentClipObject->location_y.GetValue(parent_frame_number);
parentObject_scale_x = parentClipObject->scale_x.GetValue(parent_frame_number);
parentObject_scale_y = parentClipObject->scale_y.GetValue(parent_frame_number);
parentObject_shear_x = parentClipObject->shear_x.GetValue(parent_frame_number);
parentObject_shear_y = parentClipObject->shear_y.GetValue(parent_frame_number);
parentObject_rotation = parentClipObject->rotation.GetValue(parent_frame_number);
}
// Get the parentTrackedObject properties
if (GetParentTrackedObject()){
// Get the attached object's parent clip's properties
Clip* parentClip = (Clip*) parentTrackedObject->ParentClip();
if (parentClip)
{
// Get the start trim position of the parent clip
long parent_start_offset = parentClip->Start() * info.fps.ToDouble();
long parent_frame_number = frame->number + parent_start_offset;
// Access the parentTrackedObject's properties
std::map<std::string, float> trackedObjectProperties = parentTrackedObject->GetBoxValues(parent_frame_number);
// Get actual scaled parent size
QSize parent_size = scale_size(QSize(parentClip->info.width, parentClip->info.height),
parentClip->scale, width, height);
// Get actual scaled tracked object size
int trackedWidth = trackedObjectProperties["w"] * trackedObjectProperties["sx"] * parent_size.width() *
parentClip->scale_x.GetValue(parent_frame_number);
int trackedHeight = trackedObjectProperties["h"] * trackedObjectProperties["sy"] * parent_size.height() *
parentClip->scale_y.GetValue(parent_frame_number);
// Scale the clip source_size based on the actual tracked object size
source_size = scale_size(source_size, scale, trackedWidth, trackedHeight);
// Update parentObject's properties based on the tracked object's properties and parent clip's scale
parentObject_location_x = parentClip->location_x.GetValue(parent_frame_number) + ((trackedObjectProperties["cx"] - 0.5) * parentClip->scale_x.GetValue(parent_frame_number));
parentObject_location_y = parentClip->location_y.GetValue(parent_frame_number) + ((trackedObjectProperties["cy"] - 0.5) * parentClip->scale_y.GetValue(parent_frame_number));
parentObject_rotation = trackedObjectProperties["r"] + parentClip->rotation.GetValue(parent_frame_number);
}
}
/* GRAVITY LOCATION - Initialize X & Y to the correct values (before applying location curves) */
float x = 0.0; // left
float y = 0.0; // top
// Adjust size for scale x and scale y
float sx = scale_x.GetValue(frame->number); // percentage X scale
float sy = scale_y.GetValue(frame->number); // percentage Y scale
// Change clip's scale to parentObject's scale
if(parentObject_scale_x != 0.0 && parentObject_scale_y != 0.0){
sx*= parentObject_scale_x;
sy*= parentObject_scale_y;
}
float scaled_source_width = source_size.width() * sx;
float scaled_source_height = source_size.height() * sy;
switch (gravity)
{
case (GRAVITY_TOP_LEFT):
// This is only here to prevent unused-enum warnings
break;
case (GRAVITY_TOP):
x = (width - scaled_source_width) / 2.0; // center
break;
case (GRAVITY_TOP_RIGHT):
x = width - scaled_source_width; // right
break;
case (GRAVITY_LEFT):
y = (height - scaled_source_height) / 2.0; // center
break;
case (GRAVITY_CENTER):
x = (width - scaled_source_width) / 2.0; // center
y = (height - scaled_source_height) / 2.0; // center
break;
case (GRAVITY_RIGHT):
x = width - scaled_source_width; // right
y = (height - scaled_source_height) / 2.0; // center
break;
case (GRAVITY_BOTTOM_LEFT):
y = (height - scaled_source_height); // bottom
break;
case (GRAVITY_BOTTOM):
x = (width - scaled_source_width) / 2.0; // center
y = (height - scaled_source_height); // bottom
break;
case (GRAVITY_BOTTOM_RIGHT):
x = width - scaled_source_width; // right
y = (height - scaled_source_height); // bottom
break;
}
// Debug output
ZmqLogger::Instance()->AppendDebugMethod(
"Clip::get_transform (Gravity)",
"frame->number", frame->number,
"source_clip->gravity", gravity,
"scaled_source_width", scaled_source_width,
"scaled_source_height", scaled_source_height);
QTransform transform;
/* LOCATION, ROTATION, AND SCALE */
float r = rotation.GetValue(frame->number) + parentObject_rotation; // rotate in degrees
x += width * (location_x.GetValue(frame->number) + parentObject_location_x); // move in percentage of final width
y += height * (location_y.GetValue(frame->number) + parentObject_location_y); // move in percentage of final height
float shear_x_value = shear_x.GetValue(frame->number) + parentObject_shear_x;
float shear_y_value = shear_y.GetValue(frame->number) + parentObject_shear_y;
float origin_x_value = origin_x.GetValue(frame->number);
float origin_y_value = origin_y.GetValue(frame->number);
// Transform source image (if needed)
ZmqLogger::Instance()->AppendDebugMethod(
"Clip::get_transform (Build QTransform - if needed)",
"frame->number", frame->number,
"x", x, "y", y,
"r", r,
"sx", sx, "sy", sy);
if (!isNear(x, 0) || !isNear(y, 0)) {
// TRANSLATE/MOVE CLIP
transform.translate(x, y);
}
if (!isNear(r, 0) || !isNear(shear_x_value, 0) || !isNear(shear_y_value, 0)) {
// ROTATE CLIP (around origin_x, origin_y)
float origin_x_offset = (scaled_source_width * origin_x_value);
float origin_y_offset = (scaled_source_height * origin_y_value);
transform.translate(origin_x_offset, origin_y_offset);
transform.rotate(r);
transform.shear(shear_x_value, shear_y_value);
transform.translate(-origin_x_offset,-origin_y_offset);
}
// SCALE CLIP (if needed)
float source_width_scale = (float(source_size.width()) / float(source_image->width())) * sx;
float source_height_scale = (float(source_size.height()) / float(source_image->height())) * sy;
if (!isNear(source_width_scale, 1.0) || !isNear(source_height_scale, 1.0)) {
transform.scale(source_width_scale, source_height_scale);
}
return transform;
}
// Adjust frame number for Clip position and start (which can result in a different number)
int64_t Clip::adjust_timeline_framenumber(int64_t clip_frame_number) {
// Get clip position from parent clip (if any)
float position = 0.0;
float start = 0.0;
Clip *parent = static_cast<Clip *>(ParentClip());
if (parent) {
position = parent->Position();
start = parent->Start();
}
// Adjust start frame and position based on parent clip.
// This ensures the same frame # is used by mapped readers and clips,
// when calculating samples per frame.
// Thus, this prevents gaps and mismatches in # of samples.
int64_t clip_start_frame = (start * info.fps.ToDouble()) + 1;
int64_t clip_start_position = round(position * info.fps.ToDouble()) + 1;
int64_t frame_number = clip_frame_number + clip_start_position - clip_start_frame;
return frame_number;
}
AudioResampler.h
Header file for AudioResampler class.
ChunkReader.h
Header file for ChunkReader class.
Clip.h
Header file for Clip class.
DummyReader.h
Header file for DummyReader class.
Exceptions.h
Header file for all Exception classes.
FFmpegReader.h
Header file for FFmpegReader class.
path
path
Definition FFmpegWriter.cpp:1468
FrameMapper.h
Header file for the FrameMapper class.
ImageReader.h
Header file for ImageReader class.
MagickUtilities.h
Header file for MagickUtilities (IM6/IM7 compatibility overlay).
QtImageReader.h
Header file for QtImageReader class.
TextReader.h
Header file for TextReader class.
Timeline.h
Header file for Timeline class.
ZmqLogger.h
Header file for ZeroMQ-based Logger class.
openshot::ChunkReader
This class reads a special chunk-formatted file, which can be easily shared in a distributed environm...
Definition ChunkReader.h:79
openshot::ClipBase::Start
float Start() const
Get start position (in seconds) of clip (trim start of video).
Definition ClipBase.h:88
openshot::ClipBase::start
float start
The position in seconds to start playing (used to trim the beginning of a clip).
Definition ClipBase.h:37
openshot::ClipBase::Duration
float Duration() const
Get the length of this clip (in seconds).
Definition ClipBase.h:90
openshot::ClipBase::End
virtual float End() const
Get end position (in seconds) of clip (trim end of video).
Definition ClipBase.h:89
openshot::ClipBase::Id
std::string Id() const
Get the Id of this clip object.
Definition ClipBase.h:85
openshot::ClipBase::JsonValue
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
Definition ClipBase.cpp:64
openshot::ClipBase::add_property_choice_json
Json::Value add_property_choice_json(std::string name, int value, int selected_value) const
Generate JSON choice for a property (dropdown properties).
Definition ClipBase.cpp:132
openshot::ClipBase::Layer
int Layer() const
Get layer of clip on timeline (lower number is covered by higher numbers).
Definition ClipBase.h:87
openshot::ClipBase::SetJsonValue
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
Definition ClipBase.cpp:80
openshot::ClipBase::timeline
openshot::TimelineBase * timeline
Pointer to the parent timeline instance (if any).
Definition ClipBase.h:40
openshot::ClipBase::Position
float Position() const
Get position on timeline (in seconds).
Definition ClipBase.h:86
openshot::ClipBase::ParentTimeline
virtual openshot::TimelineBase * ParentTimeline()
Get the associated Timeline pointer (if any).
Definition ClipBase.h:91
openshot::ClipBase::position
float position
The position on the timeline where this clip should start playing.
Definition ClipBase.h:35
openshot::ClipBase::end
float end
The position in seconds to end playing (used to trim the ending of a clip).
Definition ClipBase.h:38
openshot::ClipBase::previous_properties
std::string previous_properties
This string contains the previous JSON properties.
Definition ClipBase.h:39
openshot::ClipBase::add_property_json
Json::Value add_property_json(std::string name, float value, std::string type, std::string memo, const Keyframe *keyframe, float min_value, float max_value, bool readonly, int64_t requested_frame) const
Generate JSON for a property.
Definition ClipBase.cpp:96
openshot::Clip
This class represents a clip (used to arrange readers on the timeline).
Definition Clip.h:89
openshot::Clip::SetAttachedObject
void SetAttachedObject(std::shared_ptr< openshot::TrackedObjectBase > trackedObject)
Set the pointer to the trackedObject this clip is attached to.
Definition Clip.cpp:326
openshot::Clip::scale_x
openshot::Keyframe scale_x
Curve representing the horizontal scaling in percent (0 to 1).
Definition Clip.h:316
openshot::Clip::location_y
openshot::Keyframe location_y
Curve representing the relative Y position in percent based on the gravity (-1 to 1).
Definition Clip.h:319
openshot::Clip::shear_x
openshot::Keyframe shear_x
Curve representing X shear angle in degrees (-45.0=left, 45.0=right).
Definition Clip.h:324
openshot::Clip::perspective_c4_x
openshot::Keyframe perspective_c4_x
Curves representing X for coordinate 4.
Definition Clip.h:343
openshot::Clip::anchor
openshot::AnchorType anchor
The anchor determines what parent a clip should snap to.
Definition Clip.h:178
openshot::Clip::mixing
openshot::VolumeMixType mixing
What strategy should be followed when mixing audio with other clips.
Definition Clip.h:180
openshot::Clip::Open
void Open() override
Open the internal reader.
Definition Clip.cpp:382
openshot::Clip::rotation
openshot::Keyframe rotation
Curve representing the rotation (0 to 360).
Definition Clip.h:323
openshot::Clip::channel_filter
openshot::Keyframe channel_filter
A number representing an audio channel to filter (clears all other channels).
Definition Clip.h:347
openshot::Clip::display
openshot::FrameDisplayType display
The format to display the frame number (if any).
Definition Clip.h:179
openshot::Clip::init_reader_rotation
void init_reader_rotation()
Update default rotation from reader.
Definition Clip.cpp:146
openshot::Clip::Clip
Clip()
Default Constructor.
Definition Clip.cpp:197
openshot::Clip::perspective_c1_x
openshot::Keyframe perspective_c1_x
Curves representing X for coordinate 1.
Definition Clip.h:337
openshot::Clip::AttachToObject
void AttachToObject(std::string object_id)
Attach clip to Tracked Object or to another Clip.
Definition Clip.cpp:303
openshot::Clip::Json
std::string Json() const override
Generate JSON string of this object.
Definition Clip.cpp:811
openshot::Clip::GetEffect
openshot::EffectBase * GetEffect(const std::string &id)
Look up an effect by ID.
Definition Clip.cpp:533
openshot::Clip::SetJsonValue
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Definition Clip.cpp:1014
openshot::Clip::alpha
openshot::Keyframe alpha
Curve representing the alpha (1 to 0).
Definition Clip.h:320
openshot::Clip::has_audio
openshot::Keyframe has_audio
An optional override to determine if this clip has audio (-1=undefined, 0=no, 1=yes).
Definition Clip.h:351
openshot::Clip::perspective_c3_x
openshot::Keyframe perspective_c3_x
Curves representing X for coordinate 3.
Definition Clip.h:341
openshot::Clip::init_reader_settings
void init_reader_settings()
Init reader info details.
Definition Clip.cpp:133
openshot::Clip::perspective_c1_y
openshot::Keyframe perspective_c1_y
Curves representing Y for coordinate 1.
Definition Clip.h:338
openshot::Clip::JsonValue
Json::Value JsonValue() const override
Generate Json::Value for this object.
Definition Clip.cpp:940
openshot::Clip::SetAttachedClip
void SetAttachedClip(Clip *clipObject)
Set the pointer to the clip this clip is attached to.
Definition Clip.cpp:331
openshot::Clip::ParentTimeline
openshot::TimelineBase * ParentTimeline() override
Get the associated Timeline pointer (if any).
Definition Clip.h:294
openshot::Clip::perspective_c4_y
openshot::Keyframe perspective_c4_y
Curves representing Y for coordinate 4.
Definition Clip.h:344
openshot::Clip::time
openshot::Keyframe time
Curve representing the frames over time to play (used for speed and direction of video).
Definition Clip.h:330
openshot::Clip::GetParentClip
openshot::Clip * GetParentClip()
Return the associated ParentClip (if any).
Definition Clip.cpp:545
openshot::Clip::Waveform
bool Waveform()
Get the waveform property of this clip.
Definition Clip.h:312
openshot::Clip::composite
openshot::CompositeType composite
How this clip is composited onto lower layers.
Definition Clip.h:181
openshot::Clip::gravity
openshot::GravityType gravity
The gravity of a clip determines where it snaps to its parent.
Definition Clip.h:176
openshot::Clip::previous_location
AudioLocation previous_location
Previous time-mapped audio location.
Definition Clip.h:95
openshot::Clip::perspective_c3_y
openshot::Keyframe perspective_c3_y
Curves representing Y for coordinate 3.
Definition Clip.h:342
openshot::Clip::GetParentTrackedObject
std::shared_ptr< openshot::TrackedObjectBase > GetParentTrackedObject()
Return the associated Parent Tracked Object (if any).
Definition Clip.cpp:554
openshot::Clip::AddEffect
void AddEffect(openshot::EffectBase *effect)
Add an effect to the clip.
Definition Clip.cpp:1209
openshot::Clip::Close
void Close() override
Close the internal reader.
Definition Clip.cpp:403
openshot::Clip::~Clip
virtual ~Clip()
Destructor.
Definition Clip.cpp:283
openshot::Clip::perspective_c2_y
openshot::Keyframe perspective_c2_y
Curves representing Y for coordinate 2.
Definition Clip.h:340
openshot::Clip::volume
openshot::Keyframe volume
Curve representing the volume (0 to 1).
Definition Clip.h:331
openshot::Clip::shear_y
openshot::Keyframe shear_y
Curve representing Y shear angle in degrees (-45.0=down, 45.0=up).
Definition Clip.h:325
openshot::Clip::scale_y
openshot::Keyframe scale_y
Curve representing the vertical scaling in percent (0 to 1).
Definition Clip.h:317
openshot::Clip::End
float End() const override
Get end position (in seconds) of clip (trim end of video), which can be affected by the time curve.
Definition Clip.cpp:418
openshot::Clip::GetFrame
std::shared_ptr< openshot::Frame > GetFrame(int64_t clip_frame_number) override
Get an openshot::Frame object for a specific frame number of this clip. The image size and number of ...
Definition Clip.cpp:453
openshot::Clip::Reader
openshot::ReaderBase * Reader()
Get the current reader.
Definition Clip.cpp:372
openshot::Clip::RemoveEffect
void RemoveEffect(openshot::EffectBase *effect)
Remove an effect from the clip.
Definition Clip.cpp:1256
openshot::Clip::channel_mapping
openshot::Keyframe channel_mapping
A number representing an audio channel to output (only works when filtering a channel).
Definition Clip.h:348
openshot::Clip::has_video
openshot::Keyframe has_video
An optional override to determine if this clip has video (-1=undefined, 0=no, 1=yes).
Definition Clip.h:352
openshot::Clip::PropertiesJSON
std::string PropertiesJSON(int64_t requested_frame) const override
Definition Clip.cpp:818
openshot::Clip::Reader
void Reader(openshot::ReaderBase *new_reader)
Set the current reader.
Definition Clip.cpp:336
openshot::Clip::wave_color
openshot::Color wave_color
Curve representing the color of the audio wave form.
Definition Clip.h:334
openshot::Clip::init_settings
void init_settings()
Init default settings for a clip.
Definition Clip.cpp:67
openshot::Clip::perspective_c2_x
openshot::Keyframe perspective_c2_x
Curves representing X for coordinate 2.
Definition Clip.h:339
openshot::Clip::scale
openshot::ScaleType scale
The scale determines how a clip should be resized to fit its parent.
Definition Clip.h:177
openshot::Clip::location_x
openshot::Keyframe location_x
Curve representing the relative X position in percent based on the gravity (-1 to 1).
Definition Clip.h:318
openshot::Clip::origin_x
openshot::Keyframe origin_x
Curve representing X origin point (0.0=0% (left), 1.0=100% (right)).
Definition Clip.h:326
openshot::Clip::getFrameMutex
std::recursive_mutex getFrameMutex
Mutex for multiple threads.
Definition Clip.h:92
openshot::Clip::SetJson
void SetJson(const std::string value) override
Load JSON string into this object.
Definition Clip.cpp:997
openshot::Clip::origin_y
openshot::Keyframe origin_y
Curve representing Y origin point (0.0=0% (top), 1.0=100% (bottom)).
Definition Clip.h:327
openshot::Color
This class represents a color (used on the timeline and clips).
Definition Color.h:27
openshot::DummyReader
This class is used as a simple, dummy reader, which can be very useful when writing unit tests....
Definition DummyReader.h:86
openshot::EffectBase
This abstract class is the base class, used by all effects in libopenshot.
Definition EffectBase.h:54
openshot::EffectBase::ParentClip
openshot::ClipBase * ParentClip()
Parent clip object of this effect (which can be unparented and NULL).
Definition EffectBase.cpp:201
openshot::EffectBase::SetJsonValue
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition EffectBase.cpp:115
openshot::EffectBase::info
EffectInfoStruct info
Information about the current effect.
Definition EffectBase.h:69
openshot::EffectBase::trackedObjects
std::map< int, std::shared_ptr< openshot::TrackedObjectBase > > trackedObjects
Map of Tracked Object's by their indices (used by Effects that track objects on clips).
Definition EffectBase.h:66
openshot::EffectInfo
This class returns a listing of all effects supported by libopenshot.
Definition EffectInfo.h:29
openshot::FFmpegReader
This class uses the FFmpeg libraries, to open video files and audio files, and return openshot::Frame...
Definition FFmpegReader.h:103
openshot::Fraction::ToFloat
float ToFloat()
Return this fraction as a float (i.e. 1/2 = 0.5).
Definition Fraction.cpp:35
openshot::FrameMapper
This class creates a mapping between 2 different frame rates, applying a specific pull-down technique...
Definition FrameMapper.h:193
openshot::FrameMapper::Reader
ReaderBase * Reader()
Get the current reader.
Definition FrameMapper.cpp:65
openshot::Frame::GetSamplesPerFrame
int GetSamplesPerFrame(openshot::Fraction fps, int sample_rate, int channels)
Calculate the # of samples per video frame (for the current frame number).
Definition Frame.cpp:484
openshot::ImageReader
This class uses the ImageMagick++ libraries, to open image files, and return openshot::Frame objects ...
Definition ImageReader.h:56
openshot::InvalidJSON
Exception for invalid JSON.
Definition Exceptions.h:218
openshot::Keyframe
A Keyframe is a collection of Point instances, which is used to vary a number or property over time.
Definition KeyFrame.h:53
openshot::Keyframe::GetValue
double GetValue(int64_t index) const
Get the value at a specific index.
Definition KeyFrame.cpp:258
openshot::QtImageReader
This class uses the Qt library, to open image files, and return openshot::Frame objects containing th...
Definition QtImageReader.h:75
openshot::ReaderBase::Name
virtual std::string Name()=0
Return the type name of the class.
openshot::ReaderBase::info
openshot::ReaderInfo info
Information about the current media file.
Definition ReaderBase.h:88
openshot::ReaderBase::ParentClip
openshot::ClipBase * ParentClip()
Parent clip object of this reader (which can be unparented and NULL).
Definition ReaderBase.cpp:240
openshot::ReaderBase::ReaderBase
ReaderBase()
Constructor for the base reader, where many things are initialized.
Definition ReaderBase.cpp:26
openshot::ReaderClosed
Exception when a reader is closed, and a frame is requested.
Definition Exceptions.h:364
openshot::TextReader
This class uses the ImageMagick++ libraries, to create frames with "Text", and return openshot::Frame...
Definition TextReader.h:63
openshot::TimelineBase
This class represents a timeline (used for building generic timeline implementations).
Definition TimelineBase.h:41
openshot::Timeline
This class represents a timeline.
Definition Timeline.h:154
openshot::Timeline::AddTrackedObject
void AddTrackedObject(std::shared_ptr< openshot::TrackedObjectBase > trackedObject)
Add to the tracked_objects map a pointer to a tracked object (TrackedObjectBBox).
Definition Timeline.cpp:224
openshot::Timeline::GetTrackedObject
std::shared_ptr< openshot::TrackedObjectBase > GetTrackedObject(std::string id) const
Return tracked object pointer by it's id.
Definition Timeline.cpp:242
openshot::Timeline::GetClip
openshot::Clip * GetClip(const std::string &id)
Look up a single clip by ID.
Definition Timeline.cpp:413
openshot::Timeline::apply_effects
std::shared_ptr< openshot::Frame > apply_effects(std::shared_ptr< openshot::Frame > frame, int64_t timeline_frame_number, int layer, TimelineInfoStruct *options)
Apply global/timeline effects to the source frame (if any).
Definition Timeline.cpp:546
openshot::ZmqLogger::AppendDebugMethod
void AppendDebugMethod(std::string method_name, std::string arg1_name="", float arg1_value=-1.0, std::string arg2_name="", float arg2_value=-1.0, std::string arg3_name="", float arg3_value=-1.0, std::string arg4_name="", float arg4_value=-1.0, std::string arg5_name="", float arg5_value=-1.0, std::string arg6_name="", float arg6_value=-1.0)
Append debug information.
Definition ZmqLogger.cpp:178
openshot::ZmqLogger::Instance
static ZmqLogger * Instance()
Create or get an instance of this logger singleton (invoke the class with this method).
Definition ZmqLogger.cpp:35
openshot
This namespace is the default namespace for all code in the openshot library.
Definition Compressor.h:29
openshot::AnchorType
AnchorType
This enumeration determines what parent a clip should be aligned to.
Definition Enums.h:45
openshot::ANCHOR_CANVAS
@ ANCHOR_CANVAS
Anchor the clip to the canvas.
Definition Enums.h:46
openshot::ChunkVersion
ChunkVersion
This enumeration allows the user to choose which version of the chunk they would like (low,...
Definition ChunkReader.h:50
openshot::GravityType
GravityType
This enumeration determines how clips are aligned to their parent container.
Definition Enums.h:22
openshot::GRAVITY_TOP_LEFT
@ GRAVITY_TOP_LEFT
Align clip to the top left of its parent.
Definition Enums.h:23
openshot::GRAVITY_LEFT
@ GRAVITY_LEFT
Align clip to the left of its parent (middle aligned).
Definition Enums.h:26
openshot::GRAVITY_TOP_RIGHT
@ GRAVITY_TOP_RIGHT
Align clip to the top right of its parent.
Definition Enums.h:25
openshot::GRAVITY_RIGHT
@ GRAVITY_RIGHT
Align clip to the right of its parent (middle aligned).
Definition Enums.h:28
openshot::GRAVITY_BOTTOM_LEFT
@ GRAVITY_BOTTOM_LEFT
Align clip to the bottom left of its parent.
Definition Enums.h:29
openshot::GRAVITY_BOTTOM
@ GRAVITY_BOTTOM
Align clip to the bottom center of its parent.
Definition Enums.h:30
openshot::GRAVITY_TOP
@ GRAVITY_TOP
Align clip to the top center of its parent.
Definition Enums.h:24
openshot::GRAVITY_BOTTOM_RIGHT
@ GRAVITY_BOTTOM_RIGHT
Align clip to the bottom right of its parent.
Definition Enums.h:31
openshot::GRAVITY_CENTER
@ GRAVITY_CENTER
Align clip to the center of its parent (middle aligned).
Definition Enums.h:27
openshot::ScaleType
ScaleType
This enumeration determines how clips are scaled to fit their parent container.
Definition Enums.h:36
openshot::SCALE_FIT
@ SCALE_FIT
Scale the clip until either height or width fills the canvas (with no cropping).
Definition Enums.h:38
openshot::SCALE_STRETCH
@ SCALE_STRETCH
Scale the clip until both height and width fill the canvas (distort to fit).
Definition Enums.h:39
openshot::SCALE_CROP
@ SCALE_CROP
Scale the clip until both height and width fill the canvas (cropping the overlap).
Definition Enums.h:37
openshot::SCALE_NONE
@ SCALE_NONE
Do not scale the clip.
Definition Enums.h:40
openshot::VolumeMixType
VolumeMixType
This enumeration determines the strategy when mixing audio with other clips.
Definition Enums.h:68
openshot::VOLUME_MIX_AVERAGE
@ VOLUME_MIX_AVERAGE
Evenly divide the overlapping clips volume keyframes, so that the sum does not exceed 100%.
Definition Enums.h:70
openshot::VOLUME_MIX_NONE
@ VOLUME_MIX_NONE
Do not apply any volume mixing adjustments. Just add the samples together.
Definition Enums.h:69
openshot::VOLUME_MIX_REDUCE
@ VOLUME_MIX_REDUCE
Reduce volume by about %25, and then mix (louder, but could cause pops if the sum exceeds 100%).
Definition Enums.h:71
openshot::FrameDisplayType
FrameDisplayType
This enumeration determines the display format of the clip's frame number (if any)....
Definition Enums.h:52
openshot::FRAME_DISPLAY_CLIP
@ FRAME_DISPLAY_CLIP
Display the clip's internal frame number.
Definition Enums.h:54
openshot::FRAME_DISPLAY_TIMELINE
@ FRAME_DISPLAY_TIMELINE
Display the timeline's frame number.
Definition Enums.h:55
openshot::FRAME_DISPLAY_BOTH
@ FRAME_DISPLAY_BOTH
Display both the clip's and timeline's frame number.
Definition Enums.h:56
openshot::FRAME_DISPLAY_NONE
@ FRAME_DISPLAY_NONE
Do not display the frame number.
Definition Enums.h:53
openshot::stringToJson
const Json::Value stringToJson(const std::string value)
Definition Json.cpp:16
openshot::CompositeType
CompositeType
This enumeration determines how clips are composited onto lower layers.
Definition Enums.h:75
openshot::COMPOSITE_LIGHTEN
@ COMPOSITE_LIGHTEN
Definition Enums.h:95
openshot::COMPOSITE_MULTIPLY
@ COMPOSITE_MULTIPLY
Definition Enums.h:91
openshot::COMPOSITE_EXCLUSION
@ COMPOSITE_EXCLUSION
Definition Enums.h:101
openshot::COMPOSITE_PLUS
@ COMPOSITE_PLUS
Definition Enums.h:90
openshot::COMPOSITE_SOURCE_OVER
@ COMPOSITE_SOURCE_OVER
Definition Enums.h:76
openshot::COMPOSITE_DARKEN
@ COMPOSITE_DARKEN
Definition Enums.h:94
openshot::COMPOSITE_COLOR_DODGE
@ COMPOSITE_COLOR_DODGE
Definition Enums.h:96
openshot::COMPOSITE_SOFT_LIGHT
@ COMPOSITE_SOFT_LIGHT
Definition Enums.h:99
openshot::COMPOSITE_DIFFERENCE
@ COMPOSITE_DIFFERENCE
Definition Enums.h:100
openshot::COMPOSITE_HARD_LIGHT
@ COMPOSITE_HARD_LIGHT
Definition Enums.h:98
openshot::COMPOSITE_OVERLAY
@ COMPOSITE_OVERLAY
Definition Enums.h:93
openshot::COMPOSITE_COLOR_BURN
@ COMPOSITE_COLOR_BURN
Definition Enums.h:97
openshot::COMPOSITE_SCREEN
@ COMPOSITE_SCREEN
Definition Enums.h:92
openshot::AudioLocation::frame
int64_t frame
Definition AudioLocation.h:26
openshot::AudioLocation::sample_start
int sample_start
Definition AudioLocation.h:27
openshot::CompareClipEffects
Definition Clip.h:48
openshot::EffectInfoStruct::has_tracked_object
bool has_tracked_object
Determines if this effect track objects through the clip.
Definition EffectBase.h:42
openshot::ReaderInfo::width
int width
The width of the video (in pixesl).
Definition ReaderBase.h:46
openshot::ReaderInfo::channels
int channels
The number of audio channels used in the audio stream.
Definition ReaderBase.h:61
openshot::ReaderInfo::fps
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps).
Definition ReaderBase.h:48
openshot::ReaderInfo::height
int height
The height of the video (in pixels).
Definition ReaderBase.h:45
openshot::TimelineInfoStruct
This struct contains info about the current Timeline clip instance.
Definition TimelineBase.h:33
openshot::TimelineInfoStruct::is_before_clip_keyframes
bool is_before_clip_keyframes
Is this before clip keyframes are applied.
Definition TimelineBase.h:35