OpenShot Library | libopenshot 0.6.0
Loading...
Searching...
No Matches
EffectBase.cpp
Go to the documentation of this file.
1
9// Copyright (c) 2008-2019 OpenShot Studios, LLC
10//
11// SPDX-License-Identifier: LGPL-3.0-or-later
12
13#include <iostream>
14#include <iomanip>
15#include <algorithm>
16#include <cmath>
17
18#include "EffectBase.h"
19
20#include "Exceptions.h"
21#include "Clip.h"
22#include "Timeline.h"
23#include "ReaderBase.h"
24#include "ChunkReader.h"
25#include "FFmpegReader.h"
26#include "QtImageReader.h"
27#include "ZmqLogger.h"
28#include <omp.h>
29
30#ifdef USE_IMAGEMAGICK
31 #include "ImageReader.h"
32#endif
33
34using namespace openshot;
35
36// Initialize the values of the EffectInfo struct
38{
39 // Init clip settings
40 Position(0.0);
41 Layer(0);
42 Start(0.0);
43 End(0.0);
44 Order(0);
45 ParentClip(NULL);
46 parentEffect = NULL;
47 mask_invert = false;
48 mask_reader = NULL;
51
52 info.has_video = false;
53 info.has_audio = false;
55 info.name = "";
56 info.description = "";
59}
60
61// Display file information
62void EffectBase::DisplayInfo(std::ostream* out) {
63 *out << std::fixed << std::setprecision(2) << std::boolalpha;
64 *out << "----------------------------" << std::endl;
65 *out << "----- Effect Information -----" << std::endl;
66 *out << "----------------------------" << std::endl;
67 *out << "--> Name: " << info.name << std::endl;
68 *out << "--> Description: " << info.description << std::endl;
69 *out << "--> Has Video: " << info.has_video << std::endl;
70 *out << "--> Has Audio: " << info.has_audio << std::endl;
71 *out << "--> Apply Before Clip Keyframes: " << info.apply_before_clip << std::endl;
72 *out << "--> Order: " << order << std::endl;
73 *out << "----------------------------" << std::endl;
74}
75
76// Constrain a color value from 0 to 255
77int EffectBase::constrain(int color_value)
78{
79 // Constrain new color from 0 to 255
80 if (color_value < 0)
81 color_value = 0;
82 else if (color_value > 255)
83 color_value = 255;
84
85 return color_value;
86}
87
88// Generate JSON string of this object
89std::string EffectBase::Json() const {
90
91 // Return formatted string
92 return JsonValue().toStyledString();
93}
94
95// Generate Json::Value for this object
96Json::Value EffectBase::JsonValue() const {
97
98 // Create root json object
99 Json::Value root = ClipBase::JsonValue(); // get parent properties
100 root["name"] = info.name;
101 root["class_name"] = info.class_name;
102 root["description"] = info.description;
103 root["parent_effect_id"] = info.parent_effect_id;
104 root["has_video"] = info.has_video;
105 root["has_audio"] = info.has_audio;
106 root["has_tracked_object"] = info.has_tracked_object;
107 root["apply_before_clip"] = info.apply_before_clip;
108 root["order"] = Order();
109 root["mask_invert"] = mask_invert;
110 root["mask_time_mode"] = mask_time_mode;
111 root["mask_loop_mode"] = mask_loop_mode;
112 if (mask_reader)
113 root["mask_reader"] = mask_reader->JsonValue();
114 else
115 root["mask_reader"] = Json::objectValue;
116
117 // return JsonValue
118 return root;
119}
120
121// Load JSON string into this object
122void EffectBase::SetJson(const std::string value) {
123
124 // Parse JSON string into JSON objects
125 try
126 {
127 Json::Value root = openshot::stringToJson(value);
128 // Set all values that match
129 SetJsonValue(root);
130 }
131 catch (const std::exception& e)
132 {
133 // Error parsing JSON (or missing keys)
134 throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
135 }
136}
137
138// Load Json::Value into this object
139void EffectBase::SetJsonValue(const Json::Value root) {
140
141 if (ParentTimeline()){
142 // Get parent timeline
143 Timeline* parentTimeline = static_cast<Timeline *>(ParentTimeline());
144
145 // Get the list of effects on the timeline
146 std::list<EffectBase*> effects = parentTimeline->ClipEffects();
147
148 // TODO: Fix recursive call for Object Detection
149
150 // // Loop through the effects and check if we have a child effect linked to this effect
151 for (auto const& effect : effects){
152 // Set the properties of all effects which parentEffect points to this
153 if ((effect->info.parent_effect_id == this->Id()) && (effect->Id() != this->Id()))
154 effect->SetJsonValue(root);
155 }
156 }
157
158 // Set this effect properties with the parent effect properties (except the id and parent_effect_id)
159 Json::Value my_root;
160 if (parentEffect){
161 my_root = parentEffect->JsonValue();
162 my_root["id"] = this->Id();
163 my_root["parent_effect_id"] = this->info.parent_effect_id;
164 } else {
165 my_root = root;
166 }
167
168 // Legacy compatibility: older shared-mask JSON stored source trim
169 // separately from the effect trim. Canonical trim now uses ClipBase.
170 if (my_root["start"].isNull() && !my_root["mask_start"].isNull())
171 my_root["start"] = my_root["mask_start"];
172 if (my_root["end"].isNull() && !my_root["mask_end"].isNull())
173 my_root["end"] = my_root["mask_end"];
174
175 // Set parent data
176 ClipBase::SetJsonValue(my_root);
177
178 // Set data from Json (if key is found)
179 if (!my_root["order"].isNull())
180 Order(my_root["order"].asInt());
181
182 if (!my_root["apply_before_clip"].isNull())
183 info.apply_before_clip = my_root["apply_before_clip"].asBool();
184
185 if (!my_root["mask_invert"].isNull())
186 mask_invert = my_root["mask_invert"].asBool();
187 if (!my_root["mask_time_mode"].isNull()) {
188 const int time_mode = my_root["mask_time_mode"].asInt();
189 mask_time_mode = (time_mode == MASK_TIME_TIMELINE || time_mode == MASK_TIME_SOURCE_FPS)
190 ? time_mode : MASK_TIME_SOURCE_FPS;
191 }
192 if (!my_root["mask_loop_mode"].isNull()) {
193 const int loop_mode = my_root["mask_loop_mode"].asInt();
194 if (loop_mode >= MASK_LOOP_PLAY_ONCE && loop_mode <= MASK_LOOP_PING_PONG)
195 mask_loop_mode = loop_mode;
196 else
198 }
199
200 const Json::Value mask_reader_json =
201 !my_root["mask_reader"].isNull() ? my_root["mask_reader"] : my_root["reader"];
202
203 if (!mask_reader_json.isNull()) {
204 if (!mask_reader_json["type"].isNull()) {
205 MaskReader(CreateReaderFromJson(mask_reader_json));
206 } else if (mask_reader_json.isObject() && mask_reader_json.empty()) {
207 MaskReader(NULL);
208 }
209 }
210
211 if (!my_root["parent_effect_id"].isNull()){
212 info.parent_effect_id = my_root["parent_effect_id"].asString();
213 if (info.parent_effect_id.size() > 0 && info.parent_effect_id != "" && parentEffect == NULL)
215 else
216 parentEffect = NULL;
217 }
218}
219
220// Generate Json::Value for this object
221Json::Value EffectBase::JsonInfo() const {
222
223 // Create root json object
224 Json::Value root;
225 root["name"] = info.name;
226 root["class_name"] = info.class_name;
227 root["description"] = info.description;
228 root["has_video"] = info.has_video;
229 root["has_audio"] = info.has_audio;
230
231 // return JsonValue
232 return root;
233}
234
235// Get all properties for a specific frame
236Json::Value EffectBase::BasePropertiesJSON(int64_t requested_frame) const {
237 // Generate JSON properties list
238 Json::Value root;
239 root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame);
240 root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame);
241 root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame);
242 root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame);
243 root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame);
244 root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 30 * 60 * 60 * 48, true, requested_frame);
245
246 // Add replace_image choices (dropdown style)
247 root["apply_before_clip"] = add_property_json("Apply Before Clip Keyframes", info.apply_before_clip, "int", "", NULL, 0, 1, false, requested_frame);
248 root["apply_before_clip"]["choices"].append(add_property_choice_json("Yes", true, info.apply_before_clip));
249 root["apply_before_clip"]["choices"].append(add_property_choice_json("No", false, info.apply_before_clip));
250
251 // Set the parent effect which properties this effect will inherit
252 root["parent_effect_id"] = add_property_json("Parent", 0.0, "string", info.parent_effect_id, NULL, -1, -1, false, requested_frame);
253
254 if (info.has_video) {
255 root["mask_invert"] = add_property_json("Mask: Invert", mask_invert, "int", "", NULL, 0, 1, false, requested_frame);
256 root["mask_invert"]["choices"].append(add_property_choice_json("Yes", true, mask_invert));
257 root["mask_invert"]["choices"].append(add_property_choice_json("No", false, mask_invert));
258
259 root["mask_time_mode"] = add_property_json("Mask: Time Mode", mask_time_mode, "int", "", NULL, 0, 1, false, requested_frame);
260 root["mask_time_mode"]["choices"].append(add_property_choice_json("Timeline", MASK_TIME_TIMELINE, mask_time_mode));
261 root["mask_time_mode"]["choices"].append(add_property_choice_json("Source FPS", MASK_TIME_SOURCE_FPS, mask_time_mode));
262
263 root["mask_loop_mode"] = add_property_json("Mask: Loop", mask_loop_mode, "int", "", NULL, 0, 2, false, requested_frame);
264 root["mask_loop_mode"]["choices"].append(add_property_choice_json("Play Once", MASK_LOOP_PLAY_ONCE, mask_loop_mode));
265 root["mask_loop_mode"]["choices"].append(add_property_choice_json("Repeat", MASK_LOOP_REPEAT, mask_loop_mode));
266 root["mask_loop_mode"]["choices"].append(add_property_choice_json("Ping-Pong", MASK_LOOP_PING_PONG, mask_loop_mode));
267
268 if (mask_reader)
269 root["mask_reader"] = add_property_json("Mask: Source", 0.0, "reader", mask_reader->Json(), NULL, 0, 1, false, requested_frame);
270 else
271 root["mask_reader"] = add_property_json("Mask: Source", 0.0, "reader", "{}", NULL, 0, 1, false, requested_frame);
272 }
273
274 return root;
275}
276
277ReaderBase* EffectBase::CreateReaderFromJson(const Json::Value& reader_json) const {
278 if (reader_json["type"].isNull())
279 return NULL;
280
281 ReaderBase* reader = NULL;
282 const std::string type = reader_json["type"].asString();
283
284 if (type == "FFmpegReader") {
285 reader = new FFmpegReader(reader_json["path"].asString());
286 reader->SetJsonValue(reader_json);
287 // Mask readers are video-only sources. Disabling audio avoids FFmpeg
288 // A/V readiness fallbacks that can repeat stale video frames.
289 reader->info.has_audio = false;
290 reader->info.audio_stream_index = -1;
291 } else if (type == "QtImageReader") {
292 reader = new QtImageReader(reader_json["path"].asString());
293 reader->SetJsonValue(reader_json);
294 } else if (type == "ChunkReader") {
295 reader = new ChunkReader(reader_json["path"].asString(),
296 static_cast<ChunkVersion>(reader_json["chunk_version"].asInt()));
297 reader->SetJsonValue(reader_json);
298#ifdef USE_IMAGEMAGICK
299 } else if (type == "ImageReader") {
300 reader = new ImageReader(reader_json["path"].asString());
301 reader->SetJsonValue(reader_json);
302#endif
303 }
304
305 return reader;
306}
307
309 if (mask_reader == new_reader)
310 return;
311
312 if (mask_reader) {
313 mask_reader->Close();
314 delete mask_reader;
315 }
316
317 mask_reader = new_reader;
318 cached_single_mask_image.reset();
319 cached_single_mask_width = 0;
320 cached_single_mask_height = 0;
321 if (mask_reader)
322 mask_reader->ParentClip(clip);
323}
324
326 if (clip) {
327 Clip* parent_clip = dynamic_cast<Clip*>(clip);
328 if (parent_clip && parent_clip->info.fps.num > 0 && parent_clip->info.fps.den > 0)
329 return parent_clip->info.fps.ToDouble();
330 }
331
332 Timeline* parent_timeline = dynamic_cast<Timeline*>(ParentTimeline());
333 if (parent_timeline && parent_timeline->info.fps.num > 0 && parent_timeline->info.fps.den > 0)
334 return parent_timeline->info.fps.ToDouble();
335
336 if (mask_reader && mask_reader->info.fps.num > 0 && mask_reader->info.fps.den > 0)
337 return mask_reader->info.fps.ToDouble();
338
339 return 30.0;
340}
341
343 if (!mask_reader)
344 return 0.0;
345
346 if (mask_reader->info.duration > 0.0f)
347 return static_cast<double>(mask_reader->info.duration);
348
349 if (mask_reader->info.video_length > 0 &&
350 mask_reader->info.fps.num > 0 && mask_reader->info.fps.den > 0) {
351 return static_cast<double>(mask_reader->info.video_length) / mask_reader->info.fps.ToDouble();
352 }
353
354 return 0.0;
355}
356
357int64_t EffectBase::MapMaskFrameNumber(int64_t frame_number) {
358 if (!mask_reader)
359 return frame_number;
360
361 int64_t requested_index = std::max(int64_t(0), frame_number - 1);
362 if (!clip && ParentTimeline()) {
363 const double host_fps = ResolveMaskHostFps();
364 if (host_fps > 0.0) {
365 const int64_t start_offset = static_cast<int64_t>(std::llround(std::max(0.0f, Start()) * host_fps));
366 requested_index = std::max(int64_t(0), requested_index - start_offset);
367 }
368 }
369 int64_t mapped_index = requested_index;
370
372 mask_reader->info.fps.num > 0 && mask_reader->info.fps.den > 0) {
373 const double host_fps = ResolveMaskHostFps();
374 const double source_fps = mask_reader->info.fps.ToDouble();
375 if (host_fps > 0.0 && source_fps > 0.0) {
376 const double seconds = static_cast<double>(requested_index) / host_fps;
377 mapped_index = static_cast<int64_t>(std::llround(seconds * source_fps));
378 }
379 }
380
381 const int64_t source_len = mask_reader->info.video_length;
382 const double source_fps = (mask_reader->info.fps.num > 0 && mask_reader->info.fps.den > 0)
383 ? mask_reader->info.fps.ToDouble() : 30.0;
384 const double source_duration = ResolveMaskSourceDuration();
385 const double start_sec = std::min<double>(std::max(0.0f, Start()), source_duration);
386 const double end_sec = std::min<double>(std::max(0.0f, End()), source_duration);
387
388 const int64_t range_start = std::max(int64_t(1), static_cast<int64_t>(std::llround(start_sec * source_fps)) + 1);
389 int64_t range_end = (end_sec > 0.0)
390 ? static_cast<int64_t>(std::llround(end_sec * source_fps)) + 1
391 : source_len;
392 if (source_len > 0)
393 range_end = std::min(range_end, source_len);
394 if (range_end < range_start)
395 range_end = range_start;
396
397 const int64_t range_len = std::max(int64_t(1), range_end - range_start + 1);
398 int64_t range_index = mapped_index;
399
400 switch (mask_loop_mode) {
401 case MASK_LOOP_REPEAT:
402 range_index = mapped_index % range_len;
403 break;
405 if (range_len > 1) {
406 const int64_t cycle_len = (range_len * 2) - 2;
407 int64_t phase = mapped_index % cycle_len;
408 if (phase >= range_len)
409 phase = cycle_len - phase;
410 range_index = phase;
411 } else {
412 range_index = 0;
413 }
414 break;
416 default:
417 if (mapped_index < 0)
418 range_index = 0;
419 else if (mapped_index >= range_len)
420 range_index = range_len - 1;
421 else
422 range_index = mapped_index;
423 break;
424 }
425
426 int64_t mapped_frame = range_start + range_index;
427 if (source_len > 0)
428 mapped_frame = std::min(std::max(int64_t(1), mapped_frame), source_len);
429 return std::max(int64_t(1), mapped_frame);
430}
431
432std::shared_ptr<QImage> EffectBase::GetMaskImage(std::shared_ptr<QImage> target_image, int64_t frame_number) {
433 if (!mask_reader || !target_image || target_image->isNull())
434 return {};
435
436 std::shared_ptr<QImage> source_mask;
437 bool used_cached_scaled = false;
438 #pragma omp critical (open_effect_mask_reader)
439 {
440 try {
441 if (!mask_reader->IsOpen())
442 mask_reader->Open();
443
444 if (mask_reader->info.has_single_image &&
445 cached_single_mask_image &&
446 cached_single_mask_width == target_image->width() &&
447 cached_single_mask_height == target_image->height()) {
448 source_mask = cached_single_mask_image;
449 used_cached_scaled = true;
450 }
451 else {
452 const int64_t mapped_frame = MapMaskFrameNumber(frame_number);
453 auto source_frame = mask_reader->GetFrame(mapped_frame);
454 if (source_frame && source_frame->GetImage() && !source_frame->GetImage()->isNull())
455 source_mask = std::make_shared<QImage>(*source_frame->GetImage());
456 }
457 } catch (const std::exception& e) {
459 std::string("EffectBase::GetMaskImage unable to read mask frame: ") + e.what());
460 source_mask.reset();
461 }
462 }
463
464 if (!source_mask || source_mask->isNull())
465 return {};
466
467 if (used_cached_scaled)
468 return source_mask;
469
470 auto scaled_mask = std::make_shared<QImage>(
471 source_mask->scaled(
472 target_image->width(), target_image->height(),
473 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
474 if (mask_reader->info.has_single_image) {
475 cached_single_mask_image = scaled_mask;
476 cached_single_mask_width = target_image->width();
477 cached_single_mask_height = target_image->height();
478 }
479 return scaled_mask;
480}
481
482void EffectBase::BlendWithMask(std::shared_ptr<QImage> original_image, std::shared_ptr<QImage> effected_image,
483 std::shared_ptr<QImage> mask_image) const {
484 if (!original_image || !effected_image || !mask_image)
485 return;
486 if (original_image->size() != effected_image->size() || effected_image->size() != mask_image->size())
487 return;
488
489 unsigned char* original_pixels = reinterpret_cast<unsigned char*>(original_image->bits());
490 unsigned char* effected_pixels = reinterpret_cast<unsigned char*>(effected_image->bits());
491 unsigned char* mask_pixels = reinterpret_cast<unsigned char*>(mask_image->bits());
492 const int pixel_count = effected_image->width() * effected_image->height();
493
494 #pragma omp parallel for schedule(static)
495 for (int i = 0; i < pixel_count; ++i) {
496 const int idx = i * 4;
497 int gray = qGray(mask_pixels[idx], mask_pixels[idx + 1], mask_pixels[idx + 2]);
498 if (mask_invert)
499 gray = 255 - gray;
500 const float factor = static_cast<float>(gray) / 255.0f;
501 const float inverse = 1.0f - factor;
502
503 effected_pixels[idx] = static_cast<unsigned char>(
504 (original_pixels[idx] * inverse) + (effected_pixels[idx] * factor));
505 effected_pixels[idx + 1] = static_cast<unsigned char>(
506 (original_pixels[idx + 1] * inverse) + (effected_pixels[idx + 1] * factor));
507 effected_pixels[idx + 2] = static_cast<unsigned char>(
508 (original_pixels[idx + 2] * inverse) + (effected_pixels[idx + 2] * factor));
509 effected_pixels[idx + 3] = static_cast<unsigned char>(
510 (original_pixels[idx + 3] * inverse) + (effected_pixels[idx + 3] * factor));
511 }
512}
513
514std::shared_ptr<openshot::Frame> EffectBase::ProcessFrame(std::shared_ptr<openshot::Frame> frame, int64_t frame_number) {
515 // Audio-only effects skip common mask handling.
516 if (!info.has_video || !mask_reader)
517 return GetFrame(frame, frame_number);
518
519 // Effects that already apply masks inside GetFrame() should bypass common blend handling.
521 return GetFrame(frame, frame_number);
522
523 auto pre_image = frame->GetImage();
524 if (!pre_image || pre_image->isNull())
525 return GetFrame(frame, frame_number);
526
527 const auto original_image = std::make_shared<QImage>(pre_image->copy());
528 auto output_frame = GetFrame(frame, frame_number);
529 if (!output_frame)
530 return output_frame;
531 auto effected_image = output_frame->GetImage();
532 if (!effected_image || effected_image->isNull() ||
533 effected_image->size() != original_image->size())
534 return output_frame;
535
536 auto mask_image = GetMaskImage(effected_image, frame_number);
537 if (!mask_image || mask_image->isNull())
538 return output_frame;
539
540 if (UseCustomMaskBlend(frame_number))
541 ApplyCustomMaskBlend(original_image, effected_image, mask_image, frame_number);
542 else
543 BlendWithMask(original_image, effected_image, mask_image);
544
545 return output_frame;
546}
547
552
555 clip = new_clip;
556 if (mask_reader)
557 mask_reader->ParentClip(new_clip);
558}
559
560// Set the parent effect from which this properties will be set to
561void EffectBase::SetParentEffect(std::string parentEffect_id) {
562
563 // Get parent Timeline
564 Timeline* parentTimeline = static_cast<Timeline *>(ParentTimeline());
565
566 if (parentTimeline){
567
568 // Get a pointer to the parentEffect
569 EffectBase* parentEffectPtr = parentTimeline->GetClipEffect(parentEffect_id);
570
571 if (parentEffectPtr){
572 // Set the parent Effect
573 parentEffect = parentEffectPtr;
574
575 // Set the properties of this effect with the parent effect's properties
576 Json::Value EffectJSON = parentEffect->JsonValue();
577 EffectJSON["id"] = this->Id();
578 EffectJSON["parent_effect_id"] = this->info.parent_effect_id;
579 this->SetJsonValue(EffectJSON);
580 }
581 }
582 return;
583}
584
585// Return the ID of this effect's parent clip
586std::string EffectBase::ParentClipId() const{
587 if(clip)
588 return clip->Id();
589 else
590 return "";
591}
592
Header file for ChunkReader class.
Header file for Clip class.
Header file for EffectBase class.
Header file for all Exception classes.
Header file for FFmpegReader class.
Header file for ImageReader class.
Header file for QtImageReader class.
Header file for ReaderBase class.
Header file for Timeline class.
Header file for ZeroMQ-based Logger class.
This class reads a special chunk-formatted file, which can be easily shared in a distributed environm...
Definition ChunkReader.h:79
This abstract class is the base class, used by all clips in libopenshot.
Definition ClipBase.h:32
float Start() const
Get start position (in seconds) of clip (trim start of video)
Definition ClipBase.h:88
float Duration() const
Get the length of this clip (in seconds)
Definition ClipBase.h:90
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number)=0
This method is required for all derived classes of ClipBase, and returns a new openshot::Frame object...
virtual float End() const
Get end position (in seconds) of clip (trim end of video)
Definition ClipBase.h:89
std::string Id() const
Get the Id of this clip object.
Definition ClipBase.h:85
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
Definition ClipBase.cpp:64
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
int Layer() const
Get layer of clip on timeline (lower number is covered by higher numbers)
Definition ClipBase.h:87
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
Definition ClipBase.cpp:80
float Position() const
Get position on timeline (in seconds)
Definition ClipBase.h:86
virtual openshot::TimelineBase * ParentTimeline()
Get the associated Timeline pointer (if any)
Definition ClipBase.h:91
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
This class represents a clip (used to arrange readers on the timeline)
Definition Clip.h:89
This abstract class is the base class, used by all effects in libopenshot.
Definition EffectBase.h:57
ReaderBase * CreateReaderFromJson(const Json::Value &reader_json) const
Create a reader instance from reader JSON.
int mask_loop_mode
Behavior when mask range reaches the end.
Definition EffectBase.h:125
virtual bool UseCustomMaskBlend(int64_t frame_number) const
Optional override for effects that need custom mask behavior.
Definition EffectBase.h:93
virtual void SetJson(const std::string value)
Load JSON string into this object.
ReaderBase * MaskReader()
Get the common mask reader.
Definition EffectBase.h:175
EffectBase * parentEffect
Parent effect (which properties will set this effect properties)
Definition EffectBase.h:104
Json::Value JsonInfo() const
Generate JSON object of meta data / info.
virtual bool HandlesMaskInternally() const
Optional override for effects that apply mask processing inside GetFrame().
Definition EffectBase.h:100
bool mask_invert
Invert grayscale mask values before blending.
Definition EffectBase.h:111
std::shared_ptr< openshot::Frame > ProcessFrame(std::shared_ptr< openshot::Frame > frame, int64_t frame_number)
Apply effect processing with common mask support (if enabled).
std::string ParentClipId() const
Return the ID of this effect's parent clip.
void SetParentEffect(std::string parentEffect_id)
Set the parent effect from which this properties will be set to.
virtual Json::Value JsonValue() const
Generate Json::Value for this object.
openshot::ClipBase * ParentClip()
Parent clip object of this effect (which can be unparented and NULL)
int constrain(int color_value)
Constrain a color value from 0 to 255.
virtual std::string Json() const
Generate JSON string of this object.
double ResolveMaskHostFps()
Determine host FPS used to convert timeline frames to mask source FPS.
Json::Value BasePropertiesJSON(int64_t requested_frame) const
Generate JSON object of base properties (recommended to be used by all effects)
int mask_time_mode
How effect frames map to mask source frames.
Definition EffectBase.h:124
virtual void ApplyCustomMaskBlend(std::shared_ptr< QImage > original_image, std::shared_ptr< QImage > effected_image, std::shared_ptr< QImage > mask_image, int64_t frame_number) const
Optional override for effects with custom mask implementation.
Definition EffectBase.h:96
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
int Order() const
Get the order that this effect should be executed.
Definition EffectBase.h:182
double ResolveMaskSourceDuration() const
Determine mask source duration in seconds.
openshot::ClipBase * clip
Pointer to the parent clip instance (if any)
Definition EffectBase.h:73
EffectInfoStruct info
Information about the current effect.
Definition EffectBase.h:110
int64_t MapMaskFrameNumber(int64_t frame_number)
Convert an effect frame number to a mask source frame number.
void DisplayInfo(std::ostream *out=&std::cout)
Display effect information in the standard output stream (stdout)
This class uses the FFmpeg libraries, to open video files and audio files, and return openshot::Frame...
int num
Numerator for the fraction.
Definition Fraction.h:32
double ToDouble() const
Return this fraction as a double (i.e. 1/2 = 0.5)
Definition Fraction.cpp:40
int den
Denominator for the fraction.
Definition Fraction.h:33
This class uses the ImageMagick++ libraries, to open image files, and return openshot::Frame objects ...
Definition ImageReader.h:56
Exception for invalid JSON.
Definition Exceptions.h:224
This class uses the Qt library, to open image files, and return openshot::Frame objects containing th...
This abstract class is the base class, used by all readers in libopenshot.
Definition ReaderBase.h:76
virtual bool IsOpen()=0
Determine if reader is open or closed.
openshot::ReaderInfo info
Information about the current media file.
Definition ReaderBase.h:88
virtual std::string Json() const =0
Generate JSON string of this object.
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
virtual void Open()=0
Open the reader (and start consuming resources, such as images or video files)
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
openshot::ClipBase * ParentClip()
Parent clip object of this reader (which can be unparented and NULL)
virtual void Close()=0
Close the reader (and any resources it was consuming)
This class represents a timeline.
Definition Timeline.h:153
openshot::EffectBase * GetClipEffect(const std::string &id)
Look up a clip effect by ID.
Definition Timeline.cpp:441
std::list< openshot::EffectBase * > ClipEffects() const
Return the list of effects on all clips.
Definition Timeline.cpp:454
void Log(std::string message)
Log message to all subscribers of this logger (if any)
static ZmqLogger * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
Definition ZmqLogger.cpp:35
This namespace is the default namespace for all code in the openshot library.
Definition Compressor.h:29
ChunkVersion
This enumeration allows the user to choose which version of the chunk they would like (low,...
Definition ChunkReader.h:50
const Json::Value stringToJson(const std::string value)
Definition Json.cpp:16
bool has_video
Determines if this effect manipulates the image of a frame.
Definition EffectBase.h:43
bool apply_before_clip
Apply effect before we evaluate the clip's keyframes.
Definition EffectBase.h:46
std::string parent_effect_id
Id of the parent effect (if there is one)
Definition EffectBase.h:42
bool has_audio
Determines if this effect manipulates the audio of a frame.
Definition EffectBase.h:44
std::string class_name
The class name of the effect.
Definition EffectBase.h:39
std::string name
The name of the effect.
Definition EffectBase.h:40
std::string description
The description of this effect and what it does.
Definition EffectBase.h:41
bool has_tracked_object
Determines if this effect track objects through the clip.
Definition EffectBase.h:45
bool has_single_image
Determines if this file only contains a single image.
Definition ReaderBase.h:42
float duration
Length of time (in seconds)
Definition ReaderBase.h:43
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition ReaderBase.h:48
int64_t video_length
The number of frames in the video stream.
Definition ReaderBase.h:53
bool has_audio
Determines if this file has an audio stream.
Definition ReaderBase.h:41
int audio_stream_index
The index of the audio stream.
Definition ReaderBase.h:63