OpenShot Library | libopenshot  0.6.0
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 
34 using 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;
54  info.has_tracked_object = false;
55  info.name = "";
56  info.description = "";
58  info.apply_before_clip = true;
59 }
60 
61 // Display file information
62 void 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
77 int 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
89 std::string EffectBase::Json() const {
90 
91  // Return formatted string
92  return JsonValue().toStyledString();
93 }
94 
95 // Generate Json::Value for this object
96 Json::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
122 void 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
139 void 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
221 Json::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
236 Json::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 
277 ReaderBase* 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 
357 int64_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;
404  case MASK_LOOP_PING_PONG:
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;
415  case MASK_LOOP_PLAY_ONCE:
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 
432 std::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 
482 void 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 
514 std::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.
520  if (HandlesMaskInternally())
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 
550  return clip;
551 }
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
561 void 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
586 std::string EffectBase::ParentClipId() const{
587  if(clip)
588  return clip->Id();
589  else
590  return "";
591 }
592 
594  MaskReader(NULL);
595 }
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 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
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...
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.
Definition: EffectBase.cpp:277
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.
Definition: EffectBase.cpp:122
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.
Definition: EffectBase.cpp:221
virtual bool HandlesMaskInternally() const
Optional override for effects that apply mask processing inside GetFrame().
Definition: EffectBase.h:100
ReaderBase * MaskReader()
Get the common mask reader.
Definition: EffectBase.h:175
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).
Definition: EffectBase.cpp:514
std::string ParentClipId() const
Return the ID of this effect's parent clip.
Definition: EffectBase.cpp:586
void SetParentEffect(std::string parentEffect_id)
Set the parent effect from which this properties will be set to.
Definition: EffectBase.cpp:561
virtual Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: EffectBase.cpp:96
openshot::ClipBase * ParentClip()
Parent clip object of this effect (which can be unparented and NULL)
Definition: EffectBase.cpp:549
int constrain(int color_value)
Constrain a color value from 0 to 255.
Definition: EffectBase.cpp:77
virtual std::string Json() const
Generate JSON string of this object.
Definition: EffectBase.cpp:89
double ResolveMaskHostFps()
Determine host FPS used to convert timeline frames to mask source FPS.
Definition: EffectBase.cpp:325
Json::Value BasePropertiesJSON(int64_t requested_frame) const
Generate JSON object of base properties (recommended to be used by all effects)
Definition: EffectBase.cpp:236
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.
Definition: EffectBase.cpp:139
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.
Definition: EffectBase.cpp:342
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.
Definition: EffectBase.cpp:357
void DisplayInfo(std::ostream *out=&std::cout)
Display effect information in the standard output stream (stdout)
Definition: EffectBase.cpp:62
This class uses the FFmpeg libraries, to open video files and audio files, and return openshot::Frame...
Definition: FFmpegReader.h:103
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...
Definition: QtImageReader.h:75
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.
Definition: ReaderBase.cpp:157
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
Definition: ReaderBase.cpp:106
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
virtual void Open()=0
Open the reader (and start consuming resources, such as images or video files)
openshot::ClipBase * ParentClip()
Parent clip object of this reader (which can be unparented and NULL)
Definition: ReaderBase.cpp:240
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)
Definition: ZmqLogger.cpp:103
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