OpenShot Library | libopenshot  0.7.0
Clip.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 "Clip.h"
14 
15 #include "AudioResampler.h"
16 #include "Exceptions.h"
17 #include "FFmpegReader.h"
18 #include "FrameMapper.h"
19 #include "QtImageReader.h"
20 #include "ChunkReader.h"
21 #include "DummyReader.h"
22 #include "Timeline.h"
23 #include "ZmqLogger.h"
25 
26 #include <algorithm>
27 #include <cmath>
28 #include <sstream>
29 #include <QPainter>
30 #include <QPainterPath>
31 
32 #ifdef USE_IMAGEMAGICK
33  #include "MagickUtilities.h"
34  #include "ImageReader.h"
35  #include "TextReader.h"
36 #endif
37 
38 #include <Qt>
39 
40 using namespace openshot;
41 
42 namespace {
43  struct CompositeChoice { const char* name; CompositeType value; };
44  const CompositeChoice composite_choices[] = {
45  {"Normal", COMPOSITE_SOURCE_OVER},
46 
47  // Darken group
48  {"Darken", COMPOSITE_DARKEN},
49  {"Multiply", COMPOSITE_MULTIPLY},
50  {"Color Burn", COMPOSITE_COLOR_BURN},
51 
52  // Lighten group
53  {"Lighten", COMPOSITE_LIGHTEN},
54  {"Screen", COMPOSITE_SCREEN},
55  {"Color Dodge", COMPOSITE_COLOR_DODGE},
56  {"Add", COMPOSITE_PLUS},
57 
58  // Contrast group
59  {"Overlay", COMPOSITE_OVERLAY},
60  {"Soft Light", COMPOSITE_SOFT_LIGHT},
61  {"Hard Light", COMPOSITE_HARD_LIGHT},
62 
63  // Compare
64  {"Difference", COMPOSITE_DIFFERENCE},
65  {"Exclusion", COMPOSITE_EXCLUSION},
66  };
67  const int composite_choices_count = sizeof(composite_choices)/sizeof(CompositeChoice);
68 }
69 
70 // Init default settings for a clip
72 {
73  // Init clip settings
74  Position(0.0);
75  Layer(0);
76  Start(0.0);
77  ClipBase::End(0.0);
79  scale = SCALE_FIT;
84  waveform = false;
86  reader_orientation_mode = ReaderOrientationMode::Reader;
88  parentObjectId = "";
89 
90  // Init scale curves
91  scale_x = Keyframe(1.0);
92  scale_y = Keyframe(1.0);
93 
94  // Init location curves
95  location_x = Keyframe(0.0);
96  location_y = Keyframe(0.0);
97 
98  // Init alpha
99  alpha = Keyframe(1.0);
100  margin = Keyframe(0.0);
101  corner_radius = Keyframe(0.0);
102 
103  // Init time & volume
104  time = Keyframe(1.0);
105  volume = Keyframe(1.0);
106 
107  // Init audio waveform color
108  wave_color = Color((unsigned char)0, (unsigned char)123, (unsigned char)255, (unsigned char)255);
109 
110  // Init shear and perspective curves
111  shear_x = Keyframe(0.0);
112  shear_y = Keyframe(0.0);
113  origin_x = Keyframe(0.5);
114  origin_y = Keyframe(0.5);
115  perspective_c1_x = Keyframe(-1.0);
116  perspective_c1_y = Keyframe(-1.0);
117  perspective_c2_x = Keyframe(-1.0);
118  perspective_c2_y = Keyframe(-1.0);
119  perspective_c3_x = Keyframe(-1.0);
120  perspective_c3_y = Keyframe(-1.0);
121  perspective_c4_x = Keyframe(-1.0);
122  perspective_c4_y = Keyframe(-1.0);
123 
124  // Init audio channel filter and mappings
125  channel_filter = Keyframe(-1.0);
126  channel_mapping = Keyframe(-1.0);
127 
128  // Init audio and video overrides
129  has_audio = Keyframe(-1.0);
130  has_video = Keyframe(-1.0);
131 
132  // Initialize the attached object and attached clip as null pointers
133  parentTrackedObject = nullptr;
134  parentClipObject = NULL;
135 
136  // Init reader info struct
138 }
139 
140 // Init reader info details
142  if (reader) {
143  // Init rotation (if any)
145 
146  // Initialize info struct
147  info = reader->info;
148 
149  // Init cache
151  }
152 }
153 
155  // Only apply metadata rotation if clip rotation has not been explicitly set.
156  if (rotation.GetCount() > 0 || !reader)
157  return;
158 
159  if (reader->ApplyOrientationMetadata()) {
160  rotation = Keyframe(0.0f);
161  return;
162  }
163 
164  const auto rotate_meta = reader->info.metadata.find("rotate");
165  if (rotate_meta == reader->info.metadata.end()) {
166  // Ensure rotation keyframes always start with a default 0° point.
167  rotation = Keyframe(0.0f);
168  return;
169  }
170 
171  float rotate_angle = 0.0f;
172  try {
173  rotate_angle = strtof(rotate_meta->second.c_str(), nullptr);
174  } catch (const std::exception& e) {
175  return; // ignore invalid metadata
176  }
177 
178  rotation = Keyframe(rotate_angle);
179 
180  // Do not overwrite user-authored scale curves.
181  auto has_default_scale = [](const Keyframe& kf) {
182  return kf.GetCount() == 1 && fabs(kf.GetPoint(0).co.Y - 1.0) < 0.00001;
183  };
184  if (!has_default_scale(scale_x) || !has_default_scale(scale_y))
185  return;
186 
187  // No need to adjust scaling when the metadata rotation is effectively zero.
188  if (fabs(rotate_angle) < 0.0001f)
189  return;
190 
191  float w = static_cast<float>(reader->info.width);
192  float h = static_cast<float>(reader->info.height);
193  if (w <= 0.0f || h <= 0.0f)
194  return;
195 
196  float rad = rotate_angle * static_cast<float>(M_PI) / 180.0f;
197 
198  float new_width = fabs(w * cos(rad)) + fabs(h * sin(rad));
199  float new_height = fabs(w * sin(rad)) + fabs(h * cos(rad));
200  if (new_width <= 0.0f || new_height <= 0.0f)
201  return;
202 
203  float uniform_scale = std::min(w / new_width, h / new_height);
204 
205  scale_x = Keyframe(uniform_scale);
206  scale_y = Keyframe(uniform_scale);
207 }
208 
209 // Default Constructor for a clip
210 Clip::Clip() : resampler(NULL), reader(NULL), allocated_reader(NULL), is_open(false)
211 {
212  // Init all default settings
213  init_settings();
214 }
215 
216 // Constructor with reader
217 Clip::Clip(ReaderBase* new_reader) : resampler(NULL), reader(new_reader), allocated_reader(NULL), is_open(false)
218 {
219  // Init all default settings
220  init_settings();
221 
222  // Open and Close the reader (to set the duration of the clip)
223  Open();
224  Close();
225 
226  // Update duration and set parent
227  if (reader) {
228  ClipBase::End(reader->info.duration);
229  reader->ParentClip(this);
230  // Init reader info struct
232  }
233 }
234 
235 // Constructor with filepath
236 Clip::Clip(std::string path) : resampler(NULL), reader(NULL), allocated_reader(NULL), is_open(false)
237 {
238  // Init all default settings
239  init_settings();
240  reader = CreateReader(path);
241 
242  // Update duration and set parent
243  if (reader) {
244  ClipBase::End(reader->info.duration);
245  reader->ParentClip(this);
246  allocated_reader = reader;
247  // Init reader info struct
249  }
250 }
251 
252 ReaderBase* Clip::CreateReader(std::string path, bool inspect_reader)
253 {
254  // Get file extension (and convert to lower case)
255  std::string ext = get_file_extension(path);
256  std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
257 
258  // Determine if common video formats (or image sequences)
259  if (ext=="avi" || ext=="flac" || ext=="mov" || ext=="mkv" || ext=="mpg" || ext=="mpeg" || ext=="mp3" || ext=="mp4" || ext=="mts" ||
260  ext=="ogg" || ext=="wav" || ext=="wmv" || ext=="webm" || ext=="vob" || ext=="gif" || path.find("%") != std::string::npos)
261  {
262  try
263  {
264  return new openshot::FFmpegReader(path, inspect_reader);
265  } catch(...) { }
266  }
267  if (ext=="osp")
268  {
269  try
270  {
271  return new openshot::Timeline(path, true);
272  } catch(...) { }
273  }
274 
275  // If no video found, try each reader
276  try
277  {
278  return new openshot::QtImageReader(path, inspect_reader);
279  } catch(...) {
280  try
281  {
282  return new openshot::FFmpegReader(path, inspect_reader);
283  } catch(...) { }
284  }
285 
286  return NULL;
287 }
288 
289 // Destructor
291 {
292  // Delete the reader if clip created it
293  if (allocated_reader) {
294  delete allocated_reader;
295  allocated_reader = NULL;
296  reader = NULL;
297  }
298 
299  // Close the resampler
300  if (resampler) {
301  delete resampler;
302  resampler = NULL;
303  }
304 
305  // Close clip
306  Close();
307 }
308 
309 // Attach clip to bounding box
310 void Clip::AttachToObject(std::string object_id)
311 {
312  // Search for the tracked object on the timeline
313  Timeline* parentTimeline = static_cast<Timeline *>(ParentTimeline());
314 
315  if (parentTimeline) {
316  // Create a smart pointer to the tracked object from the timeline
317  std::shared_ptr<openshot::TrackedObjectBase> trackedObject = parentTimeline->GetTrackedObject(object_id);
318  Clip* clipObject = parentTimeline->GetClip(object_id);
319 
320  // Check for valid tracked object
321  if (trackedObject){
322  SetAttachedObject(trackedObject);
323  parentClipObject = NULL;
324  }
325  else if (clipObject) {
326  SetAttachedClip(clipObject);
327  parentTrackedObject = nullptr;
328  }
329  }
330 }
331 
332 // Set the pointer to the trackedObject this clip is attached to
333 void Clip::SetAttachedObject(std::shared_ptr<openshot::TrackedObjectBase> trackedObject){
334  parentTrackedObject = trackedObject;
335 }
336 
337 // Set the pointer to the clip this clip is attached to
338 void Clip::SetAttachedClip(Clip* clipObject){
339  parentClipObject = clipObject;
340 }
341 
343 void Clip::Reader(ReaderBase* new_reader)
344 {
345  // Delete previously allocated reader (if not related to new reader)
346  // FrameMappers that point to the same allocated reader are ignored
347  bool is_same_reader = false;
348  if (new_reader && allocated_reader) {
349  if (new_reader->Name() == "FrameMapper") {
350  // Determine if FrameMapper is pointing at the same allocated ready
351  FrameMapper* clip_mapped_reader = static_cast<FrameMapper*>(new_reader);
352  if (allocated_reader == clip_mapped_reader->Reader()) {
353  is_same_reader = true;
354  }
355  }
356  }
357  // Clear existing allocated reader (if different)
358  if (allocated_reader && !is_same_reader) {
359  reader->Close();
360  allocated_reader->Close();
361  delete allocated_reader;
362  reader = NULL;
363  allocated_reader = NULL;
364  }
365 
366  // set reader pointer
367  reader = new_reader;
368 
369  // set parent
370  if (reader) {
371  reader->ApplyOrientationMetadata(reader_orientation_mode == ReaderOrientationMode::Reader);
372  reader->ParentClip(this);
373 
374  // Init reader info struct
376  }
377 }
378 
381 {
382  if (reader)
383  return reader;
384  else
385  // Throw error if reader not initialized
386  throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
387 }
388 
389 // Open the internal reader
391 {
392  if (reader)
393  {
394  // Open the reader
395  reader->Open();
396  is_open = true;
397 
398  // Copy Reader info to Clip
399  info = reader->info;
400 
401  // Set some clip properties from the file reader
402  if (end == 0.0)
403  ClipBase::End(reader->info.duration);
404  }
405  else
406  // Throw error if reader not initialized
407  throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
408 }
409 
410 // Close the internal reader
412 {
413  if (is_open && reader) {
414  ZmqLogger::Instance()->AppendDebugMethod("Clip::Close");
415 
416  // Close the reader
417  reader->Close();
418  }
419 
420  // Clear cache
421  final_cache.Clear();
422  is_open = false;
423 }
424 
425 // Get end position of clip (trim end of video), which can be affected by the time curve.
426 float Clip::End() const
427 {
428  // if a time curve is present, use its length
429  if (time.GetCount() > 1)
430  {
431  // Determine the FPS fo this clip
432  float fps = 24.0;
433  if (reader)
434  // file reader
435  fps = reader->info.fps.ToFloat();
436  else
437  // Throw error if reader not initialized
438  throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
439 
440  return float(time.GetLength()) / fps;
441  }
442  else
443  // just use the duration (as detected by the reader)
444  return end;
445 }
446 
447 // Override End() position
448 void Clip::End(float value) {
449  ClipBase::End(value);
450 }
451 
452 // Set associated Timeline pointer
454  timeline = new_timeline;
455 
456  // Clear cache (it might have changed)
457  final_cache.Clear();
458 }
459 
460 // Create an openshot::Frame object for a specific frame number of this reader.
461 std::shared_ptr<Frame> Clip::GetFrame(int64_t clip_frame_number)
462 {
463  // Call override of GetFrame
464  return GetFrame(NULL, clip_frame_number, NULL);
465 }
466 
467 // Create an openshot::Frame object for a specific frame number of this reader.
468 // NOTE: background_frame is ignored in this method (this method is only used by Effect classes)
469 std::shared_ptr<Frame> Clip::GetFrame(std::shared_ptr<openshot::Frame> background_frame, int64_t clip_frame_number)
470 {
471  // Call override of GetFrame
472  return GetFrame(background_frame, clip_frame_number, NULL);
473 }
474 
475 // Use an existing openshot::Frame object and draw this Clip's frame onto it
476 std::shared_ptr<Frame> Clip::GetFrame(std::shared_ptr<openshot::Frame> background_frame, int64_t clip_frame_number, openshot::TimelineInfoStruct* options)
477 {
478  // Check for open reader (or throw exception)
479  if (!is_open)
480  throw ReaderClosed("The Clip is closed. Call Open() before calling this method.");
481 
482  if (reader)
483  {
484  // Get frame object
485  std::shared_ptr<Frame> frame = NULL;
486 
487  // Generate clip frame
488  frame = GetOrCreateFrame(clip_frame_number);
489 
490  // Get frame size and frame #
491  int64_t timeline_frame_number = clip_frame_number;
492  QSize timeline_size(frame->GetWidth(), frame->GetHeight());
493  if (background_frame) {
494  // If a background frame is provided, use it instead
495  timeline_frame_number = background_frame->number;
496  timeline_size.setWidth(background_frame->GetWidth());
497  timeline_size.setHeight(background_frame->GetHeight());
498  }
499 
500  // Get time mapped frame object (used to increase speed, change direction, etc...)
501  apply_timemapping(frame);
502 
503  // Apply waveform image (if any)
504  apply_waveform(frame, timeline_size);
505 
506  // Apply effects BEFORE applying keyframes (if any local or global effects are used)
507  apply_effects(frame, timeline_frame_number, options, true);
508 
509  // Apply keyframe / transforms to current clip image
510  apply_keyframes(frame, timeline_size);
511 
512  // Apply effects AFTER applying keyframes (if any local or global effects are used)
513  apply_effects(frame, timeline_frame_number, options, false);
514 
515  // Timeline composition can paint directly into the timeline-owned background
516  // without mutating the cached clip frame.
517  if (options) {
518  if (!background_frame) {
519  background_frame = std::make_shared<Frame>(frame->number, frame->GetWidth(), frame->GetHeight(),
520  "#00000000", frame->GetAudioSamplesCount(),
521  frame->GetAudioChannelsCount());
522  }
523  apply_background(frame, background_frame, false);
524  return frame;
525  }
526 
527  // No background: return the frame directly.
528  if (!background_frame) {
529  return frame;
530  }
531 
532  // Always composite on a copy so cached frame pixels remain immutable.
533  auto output = std::make_shared<Frame>(*frame.get());
534  apply_background(output, background_frame, true);
535  return output;
536  }
537  else
538  // Throw error if reader not initialized
539  throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
540 }
541 
542 // Look up an effect by ID
543 openshot::EffectBase* Clip::GetEffect(const std::string& id)
544 {
545  // Find the matching effect (if any)
546  for (const auto& effect : effects) {
547  if (effect->Id() == id) {
548  return effect;
549  }
550  }
551  return nullptr;
552 }
553 
554 // Return the associated ParentClip (if any)
556  if (!parentObjectId.empty() && (!parentClipObject && !parentTrackedObject)) {
557  // Attach parent clip OR object to this clip
558  AttachToObject(parentObjectId);
559  }
560  return parentClipObject;
561 }
562 
563 // Return the associated Parent Tracked Object (if any)
564 std::shared_ptr<openshot::TrackedObjectBase> Clip::GetParentTrackedObject() {
565  if (!parentObjectId.empty() && (!parentClipObject && !parentTrackedObject)) {
566  // Attach parent clip OR object to this clip
567  AttachToObject(parentObjectId);
568  }
569  return parentTrackedObject;
570 }
571 
572 // Get file extension
573 std::string Clip::get_file_extension(std::string path)
574 {
575  // Return last part of path safely (handle filenames without a dot)
576  const auto dot_pos = path.find_last_of('.');
577  if (dot_pos == std::string::npos || dot_pos + 1 >= path.size()) {
578  return std::string();
579  }
580 
581  return path.substr(dot_pos + 1);
582 }
583 
584 // Adjust the audio and image of a time mapped frame
585 void Clip::apply_timemapping(std::shared_ptr<Frame> frame)
586 {
587  // Check for valid reader
588  if (!reader)
589  // Throw error if reader not initialized
590  throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
591 
592  // Check for a valid time map curve
593  if (time.GetLength() > 1)
594  {
595  const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
596 
597  int64_t clip_frame_number = frame->number;
598  int64_t new_frame_number = adjust_frame_number_minimum(time.GetLong(clip_frame_number));
599 
600  // create buffer
601  juce::AudioBuffer<float> *source_samples = nullptr;
602 
603  // Get delta (difference from this frame to the next time mapped frame: Y value)
604  double delta = time.GetDelta(clip_frame_number + 1);
605  const bool prev_is_increasing = time.IsIncreasing(clip_frame_number);
606  const bool is_increasing = time.IsIncreasing(clip_frame_number + 1);
607 
608  // Determine length of source audio (in samples)
609  // A delta of 1.0 == normal expected samples
610  // A delta of 0.5 == 50% of normal expected samples
611  // A delta of 2.0 == 200% of normal expected samples
612  int target_sample_count = Frame::GetSamplesPerFrame(adjust_timeline_framenumber(clip_frame_number), Reader()->info.fps,
614  Reader()->info.channels);
615  int source_sample_count = round(target_sample_count * fabs(delta));
616 
617  // Determine starting audio location
618  AudioLocation location;
619  if (previous_location.frame == 0 || abs(new_frame_number - previous_location.frame) > 2 || prev_is_increasing != is_increasing) {
620  // No previous location OR gap detected
621  location.frame = new_frame_number;
622  location.sample_start = 0;
623 
624  // Create / Reset resampler
625  // We don't want to interpolate between unrelated audio data
626  if (resampler) {
627  delete resampler;
628  resampler = nullptr;
629  }
630  // Init resampler with # channels from Reader (should match the timeline)
631  resampler = new AudioResampler(Reader()->info.channels);
632 
633  // Allocate buffer of silence to initialize some data inside the resampler
634  // To prevent it from becoming input limited
635  juce::AudioBuffer<float> init_samples(Reader()->info.channels, 64);
636  init_samples.clear();
637  resampler->SetBuffer(&init_samples, 1.0);
638  resampler->GetResampledBuffer();
639 
640  } else {
641  // Use previous location
642  location = previous_location;
643  }
644 
645  if (source_sample_count <= 0) {
646  // Add silence and bail (we don't need any samples)
647  frame->AddAudioSilence(target_sample_count);
648  return;
649  }
650 
651  // Allocate a new sample buffer for these delta frames
652  source_samples = new juce::AudioBuffer<float>(Reader()->info.channels, source_sample_count);
653  source_samples->clear();
654 
655  // Determine ending audio location
656  int remaining_samples = source_sample_count;
657  int source_pos = 0;
658  while (remaining_samples > 0) {
659  std::shared_ptr<Frame> source_frame = GetOrCreateFrame(location.frame, false);
660  int frame_sample_count = source_frame->GetAudioSamplesCount() - location.sample_start;
661 
662  // Inform FrameMapper of the direction for THIS mapper frame
663  if (auto *fm = dynamic_cast<FrameMapper*>(reader)) {
664  fm->SetDirectionHint(is_increasing);
665  }
666  source_frame->SetAudioDirection(is_increasing);
667 
668  if (frame_sample_count == 0) {
669  // No samples found in source frame (fill with silence)
670  if (is_increasing) {
671  location.frame++;
672  } else {
673  location.frame--;
674  }
675  location.sample_start = 0;
676  break;
677  }
678  if (remaining_samples - frame_sample_count >= 0) {
679  // Use all frame samples & increment location
680  for (int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++) {
681  source_samples->addFrom(channel, source_pos, source_frame->GetAudioSamples(channel) + location.sample_start, frame_sample_count, 1.0f);
682  }
683  if (is_increasing) {
684  location.frame++;
685  } else {
686  location.frame--;
687  }
688  location.sample_start = 0;
689  remaining_samples -= frame_sample_count;
690  source_pos += frame_sample_count;
691 
692  } else {
693  // Use just what is needed (and reverse samples)
694  for (int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++) {
695  source_samples->addFrom(channel, source_pos, source_frame->GetAudioSamples(channel) + location.sample_start, remaining_samples, 1.0f);
696  }
697  location.sample_start += remaining_samples;
698  remaining_samples = 0;
699  source_pos += remaining_samples;
700  }
701 
702  }
703 
704  // Resize audio for current frame object + fill with silence
705  // We are fixing to clobber this with actual audio data (possibly resampled)
706  frame->AddAudioSilence(target_sample_count);
707 
708  if (source_sample_count != target_sample_count) {
709  // Resample audio (if needed)
710  double resample_ratio = double(source_sample_count) / double(target_sample_count);
711  resampler->SetBuffer(source_samples, resample_ratio);
712 
713  // Resample the data
714  juce::AudioBuffer<float> *resampled_buffer = resampler->GetResampledBuffer();
715 
716  // Fill the frame with resampled data
717  for (int channel = 0; channel < Reader()->info.channels; channel++) {
718  // Add new (slower) samples, to the frame object
719  frame->AddAudio(true, channel, 0, resampled_buffer->getReadPointer(channel, 0), std::min(resampled_buffer->getNumSamples(), target_sample_count), 1.0f);
720  }
721  } else {
722  // Fill the frame
723  for (int channel = 0; channel < Reader()->info.channels; channel++) {
724  // Add new (slower) samples, to the frame object
725  frame->AddAudio(true, channel, 0, source_samples->getReadPointer(channel, 0), target_sample_count, 1.0f);
726  }
727  }
728 
729  // Clean up
730  delete source_samples;
731 
732  // Set previous location
733  previous_location = location;
734  }
735 }
736 
737 // Adjust frame number minimum value
738 int64_t Clip::adjust_frame_number_minimum(int64_t frame_number)
739 {
740  // Never return a frame number 0 or below
741  if (frame_number < 1)
742  return 1;
743  else
744  return frame_number;
745 
746 }
747 
748 // Get or generate a blank frame
749 std::shared_ptr<Frame> Clip::GetOrCreateFrame(int64_t number, bool enable_time)
750 {
751  try {
752  // Init to requested frame
753  int64_t clip_frame_number = adjust_frame_number_minimum(number);
754  bool is_increasing = true;
755 
756  // Adjust for time-mapping (if any)
757  if (enable_time && time.GetLength() > 1) {
758  is_increasing = time.IsIncreasing(clip_frame_number + 1);
759  const int64_t time_frame_number = adjust_frame_number_minimum(time.GetLong(clip_frame_number));
760  if (auto *fm = dynamic_cast<FrameMapper*>(reader)) {
761  // Inform FrameMapper which direction this mapper frame is being requested
762  fm->SetDirectionHint(is_increasing);
763  }
764  clip_frame_number = time_frame_number;
765  }
766 
767  // Debug output
769  "Clip::GetOrCreateFrame (from reader)",
770  "number", number, "clip_frame_number", clip_frame_number);
771 
772  // Attempt to get a frame (but this could fail if a reader has just been closed)
773  auto reader_frame = reader->GetFrame(clip_frame_number);
774  if (reader_frame) {
775  // Override frame # (due to time-mapping might change it)
776  reader_frame->number = number;
777  reader_frame->SetAudioDirection(is_increasing);
778 
779  // Return real frame
780  // Create a new copy of reader frame
781  // This allows a clip to modify the pixels and audio of this frame without
782  // changing the underlying reader's frame data
783  auto reader_copy = std::make_shared<Frame>(*reader_frame.get());
784  if (has_video.GetInt(number) == 0) {
785  // No video, so add transparent pixels
786  reader_copy->AddColor(QColor(Qt::transparent));
787  }
788  if (has_audio.GetInt(number) == 0 || number > reader->info.video_length) {
789  // No audio, so include silence (also, mute audio if past end of reader)
790  reader_copy->AddAudioSilence(reader_copy->GetAudioSamplesCount());
791  }
792  return reader_copy;
793  }
794 
795  } catch (const ReaderClosed & e) {
796  // ...
797  } catch (const OutOfBoundsFrame & e) {
798  // ...
799  }
800 
801  // Estimate # of samples needed for this frame
802  int estimated_samples_in_frame = Frame::GetSamplesPerFrame(number, reader->info.fps, reader->info.sample_rate, reader->info.channels);
803 
804  // Debug output
806  "Clip::GetOrCreateFrame (create blank)",
807  "number", number,
808  "estimated_samples_in_frame", estimated_samples_in_frame);
809 
810  // Create blank frame
811  auto new_frame = std::make_shared<Frame>(
812  number, reader->info.width, reader->info.height,
813  "#000000", estimated_samples_in_frame, reader->info.channels);
814  new_frame->SampleRate(reader->info.sample_rate);
815  new_frame->ChannelsLayout(reader->info.channel_layout);
816  new_frame->AddAudioSilence(estimated_samples_in_frame);
817  return new_frame;
818 }
819 
820 // Generate JSON string of this object
821 std::string Clip::Json() const {
822 
823  // Return formatted string
824  return JsonValue().toStyledString();
825 }
826 
827 // Get all properties for a specific frame
828 std::string Clip::PropertiesJSON(int64_t requested_frame) const {
829 
830  // Generate JSON properties list
831  Json::Value root;
832  root["id"] = add_property_json("ID", 0.0, "string", Id(), NULL, -1, -1, true, requested_frame);
833  root["position"] = add_property_json("Position", Position(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame);
834  root["layer"] = add_property_json("Track", Layer(), "int", "", NULL, 0, 20, false, requested_frame);
835  root["start"] = add_property_json("Start", Start(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame);
836  root["end"] = add_property_json("End", End(), "float", "", NULL, 0, 30 * 60 * 60 * 48, false, requested_frame);
837  root["duration"] = add_property_json("Duration", Duration(), "float", "", NULL, 0, 30 * 60 * 60 * 48, true, requested_frame);
838  root["gravity"] = add_property_json("Gravity", gravity, "int", "", NULL, 0, 8, false, requested_frame);
839  root["scale"] = add_property_json("Scale", scale, "int", "", NULL, 0, 3, false, requested_frame);
840  root["display"] = add_property_json("Frame Number", display, "int", "", NULL, 0, 3, false, requested_frame);
841  root["mixing"] = add_property_json("Volume Mixing", mixing, "int", "", NULL, 0, 2, false, requested_frame);
842  root["composite"] = add_property_json("Composite", composite, "int", "", NULL, 0, composite_choices_count - 1, false, requested_frame);
843  root["waveform"] = add_property_json("Waveform", waveform, "int", "", NULL, 0, 1, false, requested_frame);
844  root["waveform_mode"] = add_property_json("Waveform Mode", waveform_mode, "int", "", NULL, 0, AUDIO_VISUALIZATION_RADIAL_BARS, false, requested_frame);
845  root["parentObjectId"] = add_property_json("Parent", 0.0, "string", parentObjectId, NULL, -1, -1, false, requested_frame);
846 
847  // Add gravity choices (dropdown style)
848  root["gravity"]["choices"].append(add_property_choice_json("Top Left", GRAVITY_TOP_LEFT, gravity));
849  root["gravity"]["choices"].append(add_property_choice_json("Top Center", GRAVITY_TOP, gravity));
850  root["gravity"]["choices"].append(add_property_choice_json("Top Right", GRAVITY_TOP_RIGHT, gravity));
851  root["gravity"]["choices"].append(add_property_choice_json("Left", GRAVITY_LEFT, gravity));
852  root["gravity"]["choices"].append(add_property_choice_json("Center", GRAVITY_CENTER, gravity));
853  root["gravity"]["choices"].append(add_property_choice_json("Right", GRAVITY_RIGHT, gravity));
854  root["gravity"]["choices"].append(add_property_choice_json("Bottom Left", GRAVITY_BOTTOM_LEFT, gravity));
855  root["gravity"]["choices"].append(add_property_choice_json("Bottom Center", GRAVITY_BOTTOM, gravity));
856  root["gravity"]["choices"].append(add_property_choice_json("Bottom Right", GRAVITY_BOTTOM_RIGHT, gravity));
857 
858  // Add scale choices (dropdown style)
859  root["scale"]["choices"].append(add_property_choice_json("Crop", SCALE_CROP, scale));
860  root["scale"]["choices"].append(add_property_choice_json("Best Fit", SCALE_FIT, scale));
861  root["scale"]["choices"].append(add_property_choice_json("Stretch", SCALE_STRETCH, scale));
862  root["scale"]["choices"].append(add_property_choice_json("None", SCALE_NONE, scale));
863 
864  // Add frame number display choices (dropdown style)
865  root["display"]["choices"].append(add_property_choice_json("None", FRAME_DISPLAY_NONE, display));
866  root["display"]["choices"].append(add_property_choice_json("Clip", FRAME_DISPLAY_CLIP, display));
867  root["display"]["choices"].append(add_property_choice_json("Timeline", FRAME_DISPLAY_TIMELINE, display));
868  root["display"]["choices"].append(add_property_choice_json("Both", FRAME_DISPLAY_BOTH, display));
869 
870  // Add volume mixing choices (dropdown style)
871  root["mixing"]["choices"].append(add_property_choice_json("None", VOLUME_MIX_NONE, mixing));
872  root["mixing"]["choices"].append(add_property_choice_json("Average", VOLUME_MIX_AVERAGE, mixing));
873  root["mixing"]["choices"].append(add_property_choice_json("Reduce", VOLUME_MIX_REDUCE, mixing));
874 
875  // Add composite choices (dropdown style)
876  for (int i = 0; i < composite_choices_count; ++i)
877  root["composite"]["choices"].append(add_property_choice_json(composite_choices[i].name, composite_choices[i].value, composite));
878 
879  // Add waveform choices (dropdown style)
880  root["waveform"]["choices"].append(add_property_choice_json("Yes", true, waveform));
881  root["waveform"]["choices"].append(add_property_choice_json("No", false, waveform));
882 
883  // Add waveform mode choices (dropdown style)
884  root["waveform_mode"]["choices"].append(add_property_choice_json("Waveform", AUDIO_VISUALIZATION_WAVEFORM, waveform_mode));
885  root["waveform_mode"]["choices"].append(add_property_choice_json("Filled Waveform", AUDIO_VISUALIZATION_FILLED_WAVEFORM, waveform_mode));
886  root["waveform_mode"]["choices"].append(add_property_choice_json("Bars", AUDIO_VISUALIZATION_BARS, waveform_mode));
887  root["waveform_mode"]["choices"].append(add_property_choice_json("Radial", AUDIO_VISUALIZATION_RADIAL, waveform_mode));
888  root["waveform_mode"]["choices"].append(add_property_choice_json("Radial Bars", AUDIO_VISUALIZATION_RADIAL_BARS, waveform_mode));
889  root["waveform_mode"]["choices"].append(add_property_choice_json("Spectrum", AUDIO_VISUALIZATION_SPECTRUM, waveform_mode));
890  root["waveform_mode"]["choices"].append(add_property_choice_json("Phase Scope", AUDIO_VISUALIZATION_PHASE_SCOPE, waveform_mode));
891  root["waveform_mode"]["choices"].append(add_property_choice_json("Particles", AUDIO_VISUALIZATION_PARTICLES, waveform_mode));
892  root["waveform_mode"]["choices"].append(add_property_choice_json("VU Meter", AUDIO_VISUALIZATION_VU_METER, waveform_mode));
893 
894  // Add the parentClipObject's properties
895  if (parentClipObject)
896  {
897  // Convert Clip's frame position to Timeline's frame position
898  long clip_start_position = round(Position() * info.fps.ToDouble()) + 1;
899  long clip_start_frame = (Start() * info.fps.ToDouble()) + 1;
900  double timeline_frame_number = requested_frame + clip_start_position - clip_start_frame;
901 
902  // Correct the parent Clip Object properties by the clip's reference system
903  float parentObject_location_x = parentClipObject->location_x.GetValue(timeline_frame_number);
904  float parentObject_location_y = parentClipObject->location_y.GetValue(timeline_frame_number);
905  float parentObject_scale_x = parentClipObject->scale_x.GetValue(timeline_frame_number);
906  float parentObject_scale_y = parentClipObject->scale_y.GetValue(timeline_frame_number);
907  float parentObject_shear_x = parentClipObject->shear_x.GetValue(timeline_frame_number);
908  float parentObject_shear_y = parentClipObject->shear_y.GetValue(timeline_frame_number);
909  float parentObject_rotation = parentClipObject->rotation.GetValue(timeline_frame_number);
910 
911  // Add the parent Clip Object properties to JSON
912  root["location_x"] = add_property_json("Location X", parentObject_location_x, "float", "", &location_x, -1.0, 1.0, false, requested_frame);
913  root["location_y"] = add_property_json("Location Y", parentObject_location_y, "float", "", &location_y, -1.0, 1.0, false, requested_frame);
914  root["scale_x"] = add_property_json("Scale X", parentObject_scale_x, "float", "", &scale_x, 0.0, 1.0, false, requested_frame);
915  root["scale_y"] = add_property_json("Scale Y", parentObject_scale_y, "float", "", &scale_y, 0.0, 1.0, false, requested_frame);
916  root["rotation"] = add_property_json("Rotation", parentObject_rotation, "float", "", &rotation, -360, 360, false, requested_frame);
917  root["shear_x"] = add_property_json("Shear X", parentObject_shear_x, "float", "", &shear_x, -1.0, 1.0, false, requested_frame);
918  root["shear_y"] = add_property_json("Shear Y", parentObject_shear_y, "float", "", &shear_y, -1.0, 1.0, false, requested_frame);
919  }
920  else
921  {
922  // Add this own clip's properties to JSON
923  root["location_x"] = add_property_json("Location X", location_x.GetValue(requested_frame), "float", "", &location_x, -1.0, 1.0, false, requested_frame);
924  root["location_y"] = add_property_json("Location Y", location_y.GetValue(requested_frame), "float", "", &location_y, -1.0, 1.0, false, requested_frame);
925  root["scale_x"] = add_property_json("Scale X", scale_x.GetValue(requested_frame), "float", "", &scale_x, 0.0, 1.0, false, requested_frame);
926  root["scale_y"] = add_property_json("Scale Y", scale_y.GetValue(requested_frame), "float", "", &scale_y, 0.0, 1.0, false, requested_frame);
927  root["rotation"] = add_property_json("Rotation", rotation.GetValue(requested_frame), "float", "", &rotation, -360, 360, false, requested_frame);
928  root["shear_x"] = add_property_json("Shear X", shear_x.GetValue(requested_frame), "float", "", &shear_x, -1.0, 1.0, false, requested_frame);
929  root["shear_y"] = add_property_json("Shear Y", shear_y.GetValue(requested_frame), "float", "", &shear_y, -1.0, 1.0, false, requested_frame);
930  }
931 
932  // Keyframes
933  root["alpha"] = add_property_json("Alpha", alpha.GetValue(requested_frame), "float", "", &alpha, 0.0, 1.0, false, requested_frame);
934  root["corner_radius"] = add_property_json("Corner Radius", corner_radius.GetValue(requested_frame), "float", "", &corner_radius, 0.0, 0.5, false, requested_frame);
935  root["margin"] = add_property_json("Margin", margin.GetValue(requested_frame), "float", "", &margin, 0.0, 0.5, false, requested_frame);
936  root["origin_x"] = add_property_json("Origin X", origin_x.GetValue(requested_frame), "float", "", &origin_x, 0.0, 1.0, false, requested_frame);
937  root["origin_y"] = add_property_json("Origin Y", origin_y.GetValue(requested_frame), "float", "", &origin_y, 0.0, 1.0, false, requested_frame);
938  root["volume"] = add_property_json("Volume", volume.GetValue(requested_frame), "float", "", &volume, 0.0, 1.0, false, requested_frame);
939  root["time"] = add_property_json("Time", time.GetValue(requested_frame), "float", "", &time, 0.0, 30 * 60 * 60 * 48, false, requested_frame);
940  root["channel_filter"] = add_property_json("Channel Filter", channel_filter.GetValue(requested_frame), "int", "", &channel_filter, -1, 10, false, requested_frame);
941  root["channel_mapping"] = add_property_json("Channel Mapping", channel_mapping.GetValue(requested_frame), "int", "", &channel_mapping, -1, 10, false, requested_frame);
942  root["has_audio"] = add_property_json("Enable Audio", has_audio.GetValue(requested_frame), "int", "", &has_audio, -1, 1.0, false, requested_frame);
943  root["has_video"] = add_property_json("Enable Video", has_video.GetValue(requested_frame), "int", "", &has_video, -1, 1.0, false, requested_frame);
944 
945  // Add enable audio/video choices (dropdown style)
946  root["has_audio"]["choices"].append(add_property_choice_json("Auto", -1, has_audio.GetValue(requested_frame)));
947  root["has_audio"]["choices"].append(add_property_choice_json("Off", 0, has_audio.GetValue(requested_frame)));
948  root["has_audio"]["choices"].append(add_property_choice_json("On", 1, has_audio.GetValue(requested_frame)));
949  root["has_video"]["choices"].append(add_property_choice_json("Auto", -1, has_video.GetValue(requested_frame)));
950  root["has_video"]["choices"].append(add_property_choice_json("Off", 0, has_video.GetValue(requested_frame)));
951  root["has_video"]["choices"].append(add_property_choice_json("On", 1, has_video.GetValue(requested_frame)));
952 
953  root["wave_color"] = add_property_json("Wave Color", 0.0, "color", "", &wave_color.red, 0, 255, false, requested_frame);
954  root["wave_color"]["red"] = add_property_json("Red", wave_color.red.GetValue(requested_frame), "float", "", &wave_color.red, 0, 255, false, requested_frame);
955  root["wave_color"]["blue"] = add_property_json("Blue", wave_color.blue.GetValue(requested_frame), "float", "", &wave_color.blue, 0, 255, false, requested_frame);
956  root["wave_color"]["green"] = add_property_json("Green", wave_color.green.GetValue(requested_frame), "float", "", &wave_color.green, 0, 255, false, requested_frame);
957  root["wave_color"]["alpha"] = add_property_json("Alpha", wave_color.alpha.GetValue(requested_frame), "float", "", &wave_color.alpha, 0, 255, false, requested_frame);
958 
959  // Return formatted string
960  return root.toStyledString();
961 }
962 
963 // Generate Json::Value for this object
964 Json::Value Clip::JsonValue() const {
965 
966  // Create root json object
967  Json::Value root = ClipBase::JsonValue(); // get parent properties
968  root["parentObjectId"] = parentObjectId;
969  root["gravity"] = gravity;
970  root["scale"] = scale;
971  root["anchor"] = anchor;
972  root["display"] = display;
973  root["mixing"] = mixing;
974  root["composite"] = composite;
975  root["waveform"] = waveform;
976  root["waveform_mode"] = waveform_mode;
977  switch (reader_orientation_mode) {
978  case ReaderOrientationMode::LegacyClipTransform:
979  root["reader_orientation_mode"] = "legacy_clip_transform";
980  break;
981  case ReaderOrientationMode::Reader:
982  default:
983  root["reader_orientation_mode"] = "reader";
984  break;
985  }
986  root["scale_x"] = scale_x.JsonValue();
987  root["scale_y"] = scale_y.JsonValue();
988  root["location_x"] = location_x.JsonValue();
989  root["location_y"] = location_y.JsonValue();
990  root["alpha"] = alpha.JsonValue();
991  root["corner_radius"] = corner_radius.JsonValue();
992  root["margin"] = margin.JsonValue();
993  root["rotation"] = rotation.JsonValue();
994  root["time"] = time.JsonValue();
995  root["volume"] = volume.JsonValue();
996  root["wave_color"] = wave_color.JsonValue();
997  root["shear_x"] = shear_x.JsonValue();
998  root["shear_y"] = shear_y.JsonValue();
999  root["origin_x"] = origin_x.JsonValue();
1000  root["origin_y"] = origin_y.JsonValue();
1001  root["channel_filter"] = channel_filter.JsonValue();
1002  root["channel_mapping"] = channel_mapping.JsonValue();
1003  root["has_audio"] = has_audio.JsonValue();
1004  root["has_video"] = has_video.JsonValue();
1005  root["perspective_c1_x"] = perspective_c1_x.JsonValue();
1006  root["perspective_c1_y"] = perspective_c1_y.JsonValue();
1007  root["perspective_c2_x"] = perspective_c2_x.JsonValue();
1008  root["perspective_c2_y"] = perspective_c2_y.JsonValue();
1009  root["perspective_c3_x"] = perspective_c3_x.JsonValue();
1010  root["perspective_c3_y"] = perspective_c3_y.JsonValue();
1011  root["perspective_c4_x"] = perspective_c4_x.JsonValue();
1012  root["perspective_c4_y"] = perspective_c4_y.JsonValue();
1013 
1014  // Add array of effects
1015  root["effects"] = Json::Value(Json::arrayValue);
1016 
1017  // loop through effects
1018  for (auto existing_effect : effects)
1019  {
1020  root["effects"].append(existing_effect->JsonValue());
1021  }
1022 
1023  if (reader)
1024  root["reader"] = reader->JsonValue();
1025  else
1026  root["reader"] = Json::Value(Json::objectValue);
1027 
1028  // return JsonValue
1029  return root;
1030 }
1031 
1032 // Load JSON string into this object
1033 void Clip::SetJson(const std::string value) {
1034 
1035  // Parse JSON string into JSON objects
1036  try
1037  {
1038  const Json::Value root = openshot::stringToJson(value);
1039  // Set all values that match
1040  SetJsonValue(root);
1041  }
1042  catch (const std::exception& e)
1043  {
1044  // Error parsing JSON (or missing keys)
1045  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
1046  }
1047 }
1048 
1049 // Load Json::Value into this object
1050 void Clip::SetJsonValue(const Json::Value root) {
1051  auto ensure_default_keyframe = [](Keyframe& kf, double default_value) {
1052  if (kf.GetCount() == 0) {
1053  kf = Keyframe(default_value);
1054  }
1055  };
1056 
1057  // Set parent data
1058  ClipBase::SetJsonValue(root);
1059 
1060  // Older project files predate reader-applied orientation metadata and stored
1061  // phone/camera rotation as ordinary clip rotation/scale keyframes.
1062  if (root["reader_orientation_mode"].isNull()) {
1063  reader_orientation_mode = ReaderOrientationMode::LegacyClipTransform;
1064  } else {
1065  const std::string mode = root["reader_orientation_mode"].asString();
1066  if (mode == "legacy_clip_transform") {
1067  reader_orientation_mode = ReaderOrientationMode::LegacyClipTransform;
1068  } else {
1069  reader_orientation_mode = ReaderOrientationMode::Reader;
1070  }
1071  }
1072 
1073  // Set data from Json (if key is found)
1074  if (!root["parentObjectId"].isNull()){
1075  parentObjectId = root["parentObjectId"].asString();
1076  if (parentObjectId.size() > 0 && parentObjectId != ""){
1077  AttachToObject(parentObjectId);
1078  } else{
1079  parentTrackedObject = nullptr;
1080  parentClipObject = NULL;
1081  }
1082  }
1083  if (!root["gravity"].isNull())
1084  gravity = (GravityType) root["gravity"].asInt();
1085  if (!root["scale"].isNull())
1086  scale = (ScaleType) root["scale"].asInt();
1087  if (!root["anchor"].isNull())
1088  anchor = (AnchorType) root["anchor"].asInt();
1089  if (!root["display"].isNull())
1090  display = (FrameDisplayType) root["display"].asInt();
1091  if (!root["mixing"].isNull())
1092  mixing = (VolumeMixType) root["mixing"].asInt();
1093  if (!root["composite"].isNull())
1094  composite = (CompositeType) root["composite"].asInt();
1095  if (!root["waveform"].isNull())
1096  waveform = root["waveform"].asBool();
1097  if (!root["waveform_mode"].isNull())
1098  waveform_mode = root["waveform_mode"].asInt();
1099  if (!root["scale_x"].isNull())
1100  scale_x.SetJsonValue(root["scale_x"]);
1101  if (!root["scale_y"].isNull())
1102  scale_y.SetJsonValue(root["scale_y"]);
1103  if (!root["location_x"].isNull())
1104  location_x.SetJsonValue(root["location_x"]);
1105  if (!root["location_y"].isNull())
1106  location_y.SetJsonValue(root["location_y"]);
1107  if (!root["alpha"].isNull())
1108  alpha.SetJsonValue(root["alpha"]);
1109  if (!root["corner_radius"].isNull())
1110  corner_radius.SetJsonValue(root["corner_radius"]);
1111  if (!root["margin"].isNull())
1112  margin.SetJsonValue(root["margin"]);
1113  if (!root["rotation"].isNull())
1114  rotation.SetJsonValue(root["rotation"]);
1115  if (!root["time"].isNull())
1116  time.SetJsonValue(root["time"]);
1117  if (!root["volume"].isNull())
1118  volume.SetJsonValue(root["volume"]);
1119  if (!root["wave_color"].isNull())
1120  wave_color.SetJsonValue(root["wave_color"]);
1121  if (!root["shear_x"].isNull())
1122  shear_x.SetJsonValue(root["shear_x"]);
1123  if (!root["shear_y"].isNull())
1124  shear_y.SetJsonValue(root["shear_y"]);
1125  if (!root["origin_x"].isNull())
1126  origin_x.SetJsonValue(root["origin_x"]);
1127  if (!root["origin_y"].isNull())
1128  origin_y.SetJsonValue(root["origin_y"]);
1129  if (!root["channel_filter"].isNull())
1130  channel_filter.SetJsonValue(root["channel_filter"]);
1131  if (!root["channel_mapping"].isNull())
1132  channel_mapping.SetJsonValue(root["channel_mapping"]);
1133  if (!root["has_audio"].isNull())
1134  has_audio.SetJsonValue(root["has_audio"]);
1135  if (!root["has_video"].isNull())
1136  has_video.SetJsonValue(root["has_video"]);
1137  if (!root["perspective_c1_x"].isNull())
1138  perspective_c1_x.SetJsonValue(root["perspective_c1_x"]);
1139  if (!root["perspective_c1_y"].isNull())
1140  perspective_c1_y.SetJsonValue(root["perspective_c1_y"]);
1141  if (!root["perspective_c2_x"].isNull())
1142  perspective_c2_x.SetJsonValue(root["perspective_c2_x"]);
1143  if (!root["perspective_c2_y"].isNull())
1144  perspective_c2_y.SetJsonValue(root["perspective_c2_y"]);
1145  if (!root["perspective_c3_x"].isNull())
1146  perspective_c3_x.SetJsonValue(root["perspective_c3_x"]);
1147  if (!root["perspective_c3_y"].isNull())
1148  perspective_c3_y.SetJsonValue(root["perspective_c3_y"]);
1149  if (!root["perspective_c4_x"].isNull())
1150  perspective_c4_x.SetJsonValue(root["perspective_c4_x"]);
1151  if (!root["perspective_c4_y"].isNull())
1152  perspective_c4_y.SetJsonValue(root["perspective_c4_y"]);
1153 
1154  // Core clip transforms should never remain empty after load. Empty JSON
1155  // point arrays can be produced by editing flows that remove every keyframe.
1156  ensure_default_keyframe(scale_x, 1.0);
1157  ensure_default_keyframe(scale_y, 1.0);
1158  ensure_default_keyframe(location_x, 0.0);
1159  ensure_default_keyframe(location_y, 0.0);
1160  ensure_default_keyframe(origin_x, 0.5);
1161  ensure_default_keyframe(origin_y, 0.5);
1162  ensure_default_keyframe(rotation, 0.0);
1163  ensure_default_keyframe(corner_radius, 0.0);
1164  ensure_default_keyframe(margin, 0.0);
1165  if (!root["effects"].isNull()) {
1166 
1167  // Clear existing effects
1168  effects.clear();
1169 
1170  // loop through effects
1171  for (const auto existing_effect : root["effects"]) {
1172  // Skip NULL nodes
1173  if (existing_effect.isNull()) {
1174  continue;
1175  }
1176 
1177  // Create Effect
1178  EffectBase *e = NULL;
1179  if (!existing_effect["type"].isNull()) {
1180 
1181  // Create instance of effect
1182  if ( (e = EffectInfo().CreateEffect(existing_effect["type"].asString()))) {
1183 
1184  // Load Json into Effect
1185  e->SetJsonValue(existing_effect);
1186 
1187  // Add Effect to Timeline
1188  AddEffect(e);
1189  }
1190  }
1191  }
1192  }
1193  if (!root["reader"].isNull()) // does Json contain a reader?
1194  {
1195  if (!root["reader"]["type"].isNull()) // does the reader Json contain a 'type'?
1196  {
1197  // Close previous reader (if any)
1198  bool already_open = false;
1199  if (reader)
1200  {
1201  // Track if reader was open
1202  already_open = reader->IsOpen();
1203 
1204  // Close and delete existing allocated reader (if any)
1205  Reader(NULL);
1206  }
1207 
1208  // Create new reader (and load properties)
1209  std::string type = root["reader"]["type"].asString();
1210 
1211  if (type == "FFmpegReader") {
1212 
1213  // Create new reader
1214  reader = new openshot::FFmpegReader(root["reader"]["path"].asString(), false);
1215  reader->SetJsonValue(root["reader"]);
1216 
1217  } else if (type == "QtImageReader") {
1218 
1219  // Create new reader
1220  reader = new openshot::QtImageReader(root["reader"]["path"].asString(), false);
1221  reader->SetJsonValue(root["reader"]);
1222 
1223 #ifdef USE_IMAGEMAGICK
1224  } else if (type == "ImageReader") {
1225 
1226  // Create new reader
1227  reader = new ImageReader(root["reader"]["path"].asString(), false);
1228  reader->SetJsonValue(root["reader"]);
1229 
1230  } else if (type == "TextReader") {
1231 
1232  // Create new reader
1233  reader = new TextReader();
1234  reader->SetJsonValue(root["reader"]);
1235 #endif
1236 
1237  } else if (type == "ChunkReader") {
1238 
1239  // Create new reader
1240  reader = new openshot::ChunkReader(root["reader"]["path"].asString(), (ChunkVersion) root["reader"]["chunk_version"].asInt());
1241  reader->SetJsonValue(root["reader"]);
1242 
1243  } else if (type == "DummyReader") {
1244 
1245  // Create new reader
1246  reader = new openshot::DummyReader();
1247  reader->SetJsonValue(root["reader"]);
1248 
1249  } else if (type == "Timeline") {
1250 
1251  // Create new reader (always load from file again)
1252  // This prevents FrameMappers from being loaded on accident
1253  reader = new openshot::Timeline(root["reader"]["path"].asString(), true);
1254  }
1255 
1256  // mark as managed reader and set parent
1257  if (reader) {
1258  reader->ApplyOrientationMetadata(reader_orientation_mode == ReaderOrientationMode::Reader);
1259  reader->ParentClip(this);
1260  allocated_reader = reader;
1261  }
1262 
1263  // Re-Open reader (if needed)
1264  if (already_open) {
1265  reader->Open();
1266  }
1267  }
1268  }
1269 
1270  // Clear cache (it might have changed)
1271  final_cache.Clear();
1272 }
1273 
1274 // Sort effects by order
1275 void Clip::sort_effects()
1276 {
1277  // sort clips
1278  effects.sort(CompareClipEffects());
1279 }
1280 
1281 // Add an effect to the clip
1283 {
1284  // Set parent clip pointer
1285  effect->ParentClip(this);
1286 
1287  // Add effect to list
1288  effects.push_back(effect);
1289 
1290  // Sort effects
1291  sort_effects();
1292 
1293  // Get the parent timeline of this clip
1294  Timeline* parentTimeline = static_cast<Timeline *>(ParentTimeline());
1295 
1296  if (parentTimeline)
1297  effect->ParentTimeline(parentTimeline);
1298 
1299  #ifdef USE_OPENCV
1300  // Add Tracked Object to Timeline
1301  if (effect->info.has_tracked_object){
1302 
1303  // Check if this clip has a parent timeline
1304  if (parentTimeline){
1305 
1306  effect->ParentTimeline(parentTimeline);
1307 
1308  // Iterate through effect's vector of Tracked Objects
1309  for (auto const& trackedObject : effect->trackedObjects){
1310 
1311  // Cast the Tracked Object as TrackedObjectBBox
1312  std::shared_ptr<TrackedObjectBBox> trackedObjectBBox = std::static_pointer_cast<TrackedObjectBBox>(trackedObject.second);
1313 
1314  // Set the Tracked Object's parent clip to this
1315  trackedObjectBBox->ParentClip(this);
1316 
1317  // Add the Tracked Object to the timeline
1318  parentTimeline->AddTrackedObject(trackedObjectBBox);
1319  }
1320  }
1321  }
1322  #endif
1323 
1324  // Clear cache (it might have changed)
1325  final_cache.Clear();
1326 }
1327 
1328 // Remove an effect from the clip
1330 {
1331  effects.remove(effect);
1332 
1333  // Clear cache (it might have changed)
1334  final_cache.Clear();
1335 }
1336 
1337 // Apply background image to the current clip image (i.e. flatten this image onto previous layer)
1338 void Clip::apply_background(std::shared_ptr<openshot::Frame> frame,
1339  std::shared_ptr<openshot::Frame> background_frame,
1340  bool update_frame_image) {
1341  // Add background canvas
1342  std::shared_ptr<QImage> background_canvas = background_frame->GetImage();
1343  QPainter painter(background_canvas.get());
1344 
1345  // Composite a new layer onto the image
1346  painter.setCompositionMode(static_cast<QPainter::CompositionMode>(composite));
1347  painter.drawImage(0, 0, *frame->GetImage());
1348  painter.end();
1349 
1350  // Standalone clip requests update frame->image, but timeline composition
1351  // draws onto the timeline-owned background frame only.
1352  if (update_frame_image)
1353  frame->AddImage(background_canvas);
1354 }
1355 
1356 // Apply effects to the source frame (if any)
1357 void Clip::apply_effects(std::shared_ptr<Frame> frame, int64_t timeline_frame_number, TimelineInfoStruct* options, bool before_keyframes)
1358 {
1359  for (auto effect : effects)
1360  {
1361  // Apply the effect to this frame
1362  if (effect->info.apply_before_clip && before_keyframes) {
1363  effect->ProcessFrame(frame, frame->number);
1364  } else if (!effect->info.apply_before_clip && !before_keyframes) {
1365  effect->ProcessFrame(frame, frame->number);
1366  }
1367  }
1368 
1369  if (timeline != NULL && options != NULL) {
1370  // Apply global timeline effects (i.e. transitions & masks... if any)
1371  Timeline* timeline_instance = static_cast<Timeline*>(timeline);
1372  options->is_before_clip_keyframes = before_keyframes;
1373  timeline_instance->apply_effects(frame, timeline_frame_number, Layer(), options);
1374  }
1375 }
1376 
1377 // Compare 2 floating point numbers for equality
1378 bool Clip::isNear(double a, double b)
1379 {
1380  return fabs(a - b) < 0.000001;
1381 }
1382 
1383 // Apply keyframes to the source frame (if any)
1384 void Clip::apply_keyframes(std::shared_ptr<Frame> frame, QSize timeline_size) {
1385  // Skip out if video was disabled or only an audio frame (no visualisation in use)
1386  if (!frame->has_image_data) {
1387  // Skip the rest of the image processing for performance reasons
1388  return;
1389  }
1390 
1391  // Get image from clip, and create transparent background image
1392  std::shared_ptr<QImage> source_image = frame->GetImage();
1393  std::shared_ptr<QImage> background_canvas = std::make_shared<QImage>(timeline_size.width(),
1394  timeline_size.height(),
1395  QImage::Format_RGBA8888_Premultiplied);
1396  background_canvas->fill(QColor(Qt::transparent));
1397 
1398  // Get transform from clip's keyframes
1399  QTransform transform = get_transform(frame, background_canvas->width(), background_canvas->height());
1400 
1401  // Load timeline's new frame image into a QPainter
1402  QPainter painter(background_canvas.get());
1403  painter.setRenderHint(QPainter::TextAntialiasing, true);
1404  if (!transform.isIdentity()) {
1405  painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
1406  }
1407  // Apply transform (translate, rotate, scale)
1408  painter.setTransform(transform);
1409 
1410  // Composite a new layer onto the image
1411  painter.setCompositionMode(static_cast<QPainter::CompositionMode>(composite));
1412 
1413  // Apply opacity via painter instead of per-pixel alpha manipulation
1414  const float alpha_value = alpha.GetValue(frame->number);
1415  const double corner_radius_value = std::max(0.0, std::min(0.5, corner_radius.GetValue(frame->number)));
1416  if (corner_radius_value > 0.0f) {
1417  painter.setRenderHint(QPainter::Antialiasing, true);
1418  const double radius_pixels = corner_radius_value
1419  * std::min(source_image->width(), source_image->height());
1420  QPainterPath clip_path;
1421  clip_path.addRoundedRect(
1422  QRectF(0, 0, source_image->width(), source_image->height()),
1423  radius_pixels,
1424  radius_pixels);
1425  painter.setClipPath(clip_path);
1426  }
1427  if (alpha_value != 1.0f) {
1428  painter.setOpacity(alpha_value);
1429  painter.drawImage(0, 0, *source_image);
1430  // Reset so any subsequent drawing (e.g., overlays) isn’t faded
1431  painter.setOpacity(1.0);
1432  } else {
1433  painter.drawImage(0, 0, *source_image);
1434  }
1435 
1436  if (timeline) {
1437  Timeline *t = static_cast<Timeline *>(timeline);
1438 
1439  // Draw frame #'s on top of image (if needed)
1440  if (display != FRAME_DISPLAY_NONE) {
1441  std::stringstream frame_number_str;
1442  switch (display) {
1443  case (FRAME_DISPLAY_NONE):
1444  // This is only here to prevent unused-enum warnings
1445  break;
1446 
1447  case (FRAME_DISPLAY_CLIP):
1448  frame_number_str << frame->number;
1449  break;
1450 
1451  case (FRAME_DISPLAY_TIMELINE):
1452  frame_number_str << round((Position() - Start()) * t->info.fps.ToFloat()) + frame->number;
1453  break;
1454 
1455  case (FRAME_DISPLAY_BOTH):
1456  frame_number_str << round((Position() - Start()) * t->info.fps.ToFloat()) + frame->number << " (" << frame->number << ")";
1457  break;
1458  }
1459 
1460  // Draw frame number on top of image
1461  painter.setPen(QColor("#ffffff"));
1462  painter.drawText(20, 20, QString(frame_number_str.str().c_str()));
1463  }
1464  }
1465  painter.end();
1466 
1467  // Add new QImage to frame
1468  frame->AddImage(background_canvas);
1469 }
1470 
1471 // Apply apply_waveform image to the source frame (if any)
1472 void Clip::apply_waveform(std::shared_ptr<Frame> frame, QSize timeline_size) {
1473 
1474  if (!Waveform()) {
1475  // Exit if no waveform is needed
1476  return;
1477  }
1478 
1479  // Debug output
1480  ZmqLogger::Instance()->AppendDebugMethod("Clip::apply_waveform (Generate Waveform Image)",
1481  "frame->number", frame->number,
1482  "Waveform()", Waveform(),
1483  "width", timeline_size.width(),
1484  "height", timeline_size.height());
1485 
1486  // Get the color of the waveform
1487  int red = wave_color.red.GetInt(frame->number);
1488  int green = wave_color.green.GetInt(frame->number);
1489  int blue = wave_color.blue.GetInt(frame->number);
1490  int alpha = wave_color.alpha.GetInt(frame->number);
1491 
1492  // Render the waveform through the audio visualization effect so clip shortcuts
1493  // and explicit effects share the same rendering path.
1494  auto visual_frame = std::make_shared<Frame>(*frame.get());
1495  visual_frame->AddImage(std::make_shared<QImage>(
1496  timeline_size.width(), timeline_size.height(), QImage::Format_RGBA8888_Premultiplied));
1497  visual_frame->GetImage()->fill(Qt::transparent);
1498 
1499  AudioVisualization visualization;
1500  visualization.visualization_type = waveform_mode;
1501  visualization.style = AUDIO_VISUALIZATION_STYLE_MINIMAL;
1502  visualization.color = Color(
1503  static_cast<unsigned char>(red),
1504  static_cast<unsigned char>(green),
1505  static_cast<unsigned char>(blue),
1506  static_cast<unsigned char>(alpha));
1507  visualization.intensity = Keyframe(1.0);
1508  visualization.smoothing = Keyframe(0.25);
1509  visualization.detail = Keyframe(0.75);
1510  visualization.glow = Keyframe(0.0);
1511  visualization.color_spread = Keyframe(0.0);
1514  visualization.frequency_low = Keyframe(0.0);
1515  visualization.frequency_high = Keyframe(1.0);
1517  visualization.GetFrame(visual_frame, frame->number);
1518 
1519  frame->AddImage(visual_frame->GetImage());
1520 }
1521 
1522 // Scale a source size to a target size (given a specific scale-type)
1523 QSize Clip::scale_size(QSize source_size, ScaleType source_scale, int target_width, int target_height) {
1524  switch (source_scale)
1525  {
1526  case (SCALE_FIT): {
1527  source_size.scale(target_width, target_height, Qt::KeepAspectRatio);
1528  break;
1529  }
1530  case (SCALE_STRETCH): {
1531  source_size.scale(target_width, target_height, Qt::IgnoreAspectRatio);
1532  break;
1533  }
1534  case (SCALE_CROP): {
1535  source_size.scale(target_width, target_height, Qt::KeepAspectRatioByExpanding);;
1536  break;
1537  }
1538  }
1539 
1540  return source_size;
1541 }
1542 
1543 // Get QTransform from keyframes
1544 QTransform Clip::get_transform(std::shared_ptr<Frame> frame, int width, int height)
1545 {
1546  // Get image from clip
1547  std::shared_ptr<QImage> source_image = frame->GetImage();
1548 
1549  const double margin_value = std::max(0.0, std::min(0.5, margin.GetValue(frame->number)));
1550  const double margin_pixels = margin_value * std::min(width, height);
1551  const double layout_x = margin_pixels;
1552  const double layout_y = margin_pixels;
1553  const double layout_width = std::max(1.0, width - (margin_pixels * 2.0));
1554  const double layout_height = std::max(1.0, height - (margin_pixels * 2.0));
1555 
1556  /* RESIZE SOURCE IMAGE - based on scale type */
1557  QSize source_size = scale_size(source_image->size(), scale, layout_width, layout_height);
1558 
1559  // Initialize parent object's properties (Clip or Tracked Object)
1560  float parentObject_location_x = 0.0;
1561  float parentObject_location_y = 0.0;
1562  float parentObject_scale_x = 1.0;
1563  float parentObject_scale_y = 1.0;
1564  float parentObject_shear_x = 0.0;
1565  float parentObject_shear_y = 0.0;
1566  float parentObject_rotation = 0.0;
1567 
1568  // Get the parentClipObject properties
1569  if (GetParentClip()){
1570  // Get the start trim position of the parent clip
1571  long parent_start_offset = parentClipObject->Start() * info.fps.ToDouble();
1572  long parent_frame_number = frame->number + parent_start_offset;
1573 
1574  // Get parent object's properties (Clip)
1575  parentObject_location_x = parentClipObject->location_x.GetValue(parent_frame_number);
1576  parentObject_location_y = parentClipObject->location_y.GetValue(parent_frame_number);
1577  parentObject_scale_x = parentClipObject->scale_x.GetValue(parent_frame_number);
1578  parentObject_scale_y = parentClipObject->scale_y.GetValue(parent_frame_number);
1579  parentObject_shear_x = parentClipObject->shear_x.GetValue(parent_frame_number);
1580  parentObject_shear_y = parentClipObject->shear_y.GetValue(parent_frame_number);
1581  parentObject_rotation = parentClipObject->rotation.GetValue(parent_frame_number);
1582  }
1583 
1584  // Get the parentTrackedObject properties
1585  if (GetParentTrackedObject()){
1586  // Get the attached object's parent clip's properties
1587  Clip* parentClip = (Clip*) parentTrackedObject->ParentClip();
1588  if (parentClip)
1589  {
1590  // Get the start trim position of the parent clip
1591  long parent_start_offset = parentClip->Start() * info.fps.ToDouble();
1592  long parent_frame_number = frame->number + parent_start_offset;
1593 
1594  // Access the parentTrackedObject's properties
1595  std::map<std::string, float> trackedObjectProperties = parentTrackedObject->GetBoxValues(parent_frame_number);
1596 
1597  // Get actual scaled parent size
1598  QSize parent_size = scale_size(QSize(parentClip->info.width, parentClip->info.height),
1599  parentClip->scale, width, height);
1600 
1601  // Get actual scaled tracked object size
1602  int trackedWidth = trackedObjectProperties["w"] * trackedObjectProperties["sx"] * parent_size.width() *
1603  parentClip->scale_x.GetValue(parent_frame_number);
1604  int trackedHeight = trackedObjectProperties["h"] * trackedObjectProperties["sy"] * parent_size.height() *
1605  parentClip->scale_y.GetValue(parent_frame_number);
1606 
1607  // Scale the clip source_size based on the actual tracked object size
1608  source_size = scale_size(source_size, scale, trackedWidth, trackedHeight);
1609 
1610  // Update parentObject's properties based on the tracked object's properties and parent clip's scale
1611  parentObject_location_x = parentClip->location_x.GetValue(parent_frame_number) + ((trackedObjectProperties["cx"] - 0.5) * parentClip->scale_x.GetValue(parent_frame_number));
1612  parentObject_location_y = parentClip->location_y.GetValue(parent_frame_number) + ((trackedObjectProperties["cy"] - 0.5) * parentClip->scale_y.GetValue(parent_frame_number));
1613  parentObject_rotation = trackedObjectProperties["r"] + parentClip->rotation.GetValue(parent_frame_number);
1614  }
1615  }
1616 
1617  /* GRAVITY LOCATION - Initialize X & Y to the correct values (before applying location curves) */
1618  float x = 0.0; // left
1619  float y = 0.0; // top
1620 
1621  // Adjust size for scale x and scale y
1622  float sx = scale_x.GetValue(frame->number); // percentage X scale
1623  float sy = scale_y.GetValue(frame->number); // percentage Y scale
1624 
1625  // Change clip's scale to parentObject's scale
1626  if(parentObject_scale_x != 0.0 && parentObject_scale_y != 0.0){
1627  sx*= parentObject_scale_x;
1628  sy*= parentObject_scale_y;
1629  }
1630 
1631  float scaled_source_width = source_size.width() * sx;
1632  float scaled_source_height = source_size.height() * sy;
1633 
1634  switch (gravity)
1635  {
1636  case (GRAVITY_TOP_LEFT):
1637  x = layout_x;
1638  y = layout_y;
1639  // This is only here to prevent unused-enum warnings
1640  break;
1641  case (GRAVITY_TOP):
1642  x = layout_x + ((layout_width - scaled_source_width) / 2.0); // center
1643  y = layout_y;
1644  break;
1645  case (GRAVITY_TOP_RIGHT):
1646  x = layout_x + layout_width - scaled_source_width; // right
1647  y = layout_y;
1648  break;
1649  case (GRAVITY_LEFT):
1650  x = layout_x;
1651  y = layout_y + ((layout_height - scaled_source_height) / 2.0); // center
1652  break;
1653  case (GRAVITY_CENTER):
1654  x = layout_x + ((layout_width - scaled_source_width) / 2.0); // center
1655  y = layout_y + ((layout_height - scaled_source_height) / 2.0); // center
1656  break;
1657  case (GRAVITY_RIGHT):
1658  x = layout_x + layout_width - scaled_source_width; // right
1659  y = layout_y + ((layout_height - scaled_source_height) / 2.0); // center
1660  break;
1661  case (GRAVITY_BOTTOM_LEFT):
1662  x = layout_x;
1663  y = layout_y + (layout_height - scaled_source_height); // bottom
1664  break;
1665  case (GRAVITY_BOTTOM):
1666  x = layout_x + ((layout_width - scaled_source_width) / 2.0); // center
1667  y = layout_y + (layout_height - scaled_source_height); // bottom
1668  break;
1669  case (GRAVITY_BOTTOM_RIGHT):
1670  x = layout_x + layout_width - scaled_source_width; // right
1671  y = layout_y + (layout_height - scaled_source_height); // bottom
1672  break;
1673  }
1674 
1675  // Debug output
1677  "Clip::get_transform (Gravity)",
1678  "frame->number", frame->number,
1679  "source_clip->gravity", gravity,
1680  "scaled_source_width", scaled_source_width,
1681  "scaled_source_height", scaled_source_height);
1682 
1683  QTransform transform;
1684 
1685  /* LOCATION, ROTATION, AND SCALE */
1686  float r = rotation.GetValue(frame->number) + parentObject_rotation; // rotate in degrees
1687  float location_x_value = location_x.GetValue(frame->number) + parentObject_location_x;
1688  float location_y_value = location_y.GetValue(frame->number) + parentObject_location_y;
1689  auto location_offset = [](float location, float anchored_position, float canvas_size, float clip_size) {
1690  if (location < 0.0f) {
1691  return location * (anchored_position + clip_size);
1692  }
1693  return location * (canvas_size - anchored_position);
1694  };
1695  x += location_offset(location_x_value, x - layout_x, layout_width, scaled_source_width);
1696  y += location_offset(location_y_value, y - layout_y, layout_height, scaled_source_height);
1697  float shear_x_value = shear_x.GetValue(frame->number) + parentObject_shear_x;
1698  float shear_y_value = shear_y.GetValue(frame->number) + parentObject_shear_y;
1699  float origin_x_value = origin_x.GetValue(frame->number);
1700  float origin_y_value = origin_y.GetValue(frame->number);
1701 
1702  // Transform source image (if needed)
1704  "Clip::get_transform (Build QTransform - if needed)",
1705  "frame->number", frame->number,
1706  "x", x, "y", y,
1707  "r", r,
1708  "sx", sx, "sy", sy);
1709 
1710  if (!isNear(x, 0) || !isNear(y, 0)) {
1711  // TRANSLATE/MOVE CLIP
1712  transform.translate(x, y);
1713  }
1714  if (!isNear(r, 0) || !isNear(shear_x_value, 0) || !isNear(shear_y_value, 0)) {
1715  // ROTATE CLIP (around origin_x, origin_y)
1716  float origin_x_offset = (scaled_source_width * origin_x_value);
1717  float origin_y_offset = (scaled_source_height * origin_y_value);
1718  transform.translate(origin_x_offset, origin_y_offset);
1719  transform.rotate(r);
1720  transform.shear(shear_x_value, shear_y_value);
1721  transform.translate(-origin_x_offset,-origin_y_offset);
1722  }
1723  // SCALE CLIP (if needed)
1724  float source_width_scale = (float(source_size.width()) / float(source_image->width())) * sx;
1725  float source_height_scale = (float(source_size.height()) / float(source_image->height())) * sy;
1726  if (!isNear(source_width_scale, 1.0) || !isNear(source_height_scale, 1.0)) {
1727  transform.scale(source_width_scale, source_height_scale);
1728  }
1729 
1730  return transform;
1731 }
1732 
1733 // Adjust frame number for Clip position and start (which can result in a different number)
1734 int64_t Clip::adjust_timeline_framenumber(int64_t clip_frame_number) {
1735 
1736  // Get clip position from parent clip (if any)
1737  float position = 0.0;
1738  float start = 0.0;
1739  Clip *parent = static_cast<Clip *>(ParentClip());
1740  if (parent) {
1741  position = parent->Position();
1742  start = parent->Start();
1743  }
1744 
1745  // Adjust start frame and position based on parent clip.
1746  // This ensures the same frame # is used by mapped readers and clips,
1747  // when calculating samples per frame.
1748  // Thus, this prevents gaps and mismatches in # of samples.
1749  int64_t clip_start_frame = (start * info.fps.ToDouble()) + 1;
1750  int64_t clip_start_position = round(position * info.fps.ToDouble()) + 1;
1751  int64_t frame_number = clip_frame_number + clip_start_position - clip_start_frame;
1752 
1753  return frame_number;
1754 }
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::stringToJson
const Json::Value stringToJson(const std::string value)
Definition: Json.cpp:16
openshot::CacheMemory::Clear
void Clear()
Clear the cache of all frames.
Definition: CacheMemory.cpp:224
openshot::Clip::Open
void Open() override
Open the internal reader.
Definition: Clip.cpp:390
openshot::Keyframe::IsIncreasing
bool IsIncreasing(int index) const
Get the direction of the curve at a specific index (increasing or decreasing)
Definition: KeyFrame.cpp:292
openshot::AudioVisualization::background
int background
Definition: AudioVisualization.h:84
openshot::ReaderInfo::sample_rate
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition: ReaderBase.h:60
openshot::ClipBase::timeline
openshot::TimelineBase * timeline
Pointer to the parent timeline instance (if any)
Definition: ClipBase.h:40
openshot::AUDIO_VISUALIZATION_STYLE_MINIMAL
@ AUDIO_VISUALIZATION_STYLE_MINIMAL
Definition: AudioVisualization.h:43
openshot::EffectInfo
This class returns a listing of all effects supported by libopenshot.
Definition: EffectInfo.h:28
openshot::FRAME_DISPLAY_BOTH
@ FRAME_DISPLAY_BOTH
Display both the clip's and timeline's frame number.
Definition: Enums.h:56
openshot::Fraction::ToFloat
float ToFloat()
Return this fraction as a float (i.e. 1/2 = 0.5)
Definition: Fraction.cpp:35
openshot::AudioVisualization::visualization_type
int visualization_type
Definition: AudioVisualization.h:72
openshot::EffectBase
This abstract class is the base class, used by all effects in libopenshot.
Definition: EffectBase.h:56
openshot::EffectBase::info
EffectInfoStruct info
Information about the current effect.
Definition: EffectBase.h:114
openshot::ReaderBase::JsonValue
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
Definition: ReaderBase.cpp:110
openshot::AUDIO_VISUALIZATION_RADIAL
@ AUDIO_VISUALIZATION_RADIAL
Definition: AudioVisualization.h:31
openshot::COMPOSITE_MULTIPLY
@ COMPOSITE_MULTIPLY
Definition: Enums.h:91
openshot::Clip::anchor
openshot::AnchorType anchor
The anchor determines what parent a clip should snap to.
Definition: Clip.h:187
Clip.h
Header file for Clip class.
openshot::Keyframe::GetLong
int64_t GetLong(int64_t index) const
Get the rounded LONG value at a specific index.
Definition: KeyFrame.cpp:287
openshot::Clip::CreateReader
static openshot::ReaderBase * CreateReader(std::string path, bool inspect_reader=true)
Definition: Clip.cpp:252
openshot::ChunkReader
This class reads a special chunk-formatted file, which can be easily shared in a distributed environm...
Definition: ChunkReader.h:78
openshot::ReaderBase::GetFrame
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
openshot::Clip::previous_location
AudioLocation previous_location
Previous time-mapped audio location.
Definition: Clip.h:95
openshot::AudioVisualization::channel_layout
int channel_layout
Definition: AudioVisualization.h:81
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::ReaderBase::SetJsonValue
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
Definition: ReaderBase.cpp:161
openshot::Clip::GetEffect
openshot::EffectBase * GetEffect(const std::string &id)
Look up an effect by ID.
Definition: Clip.cpp:543
openshot::ReaderBase::ApplyOrientationMetadata
void ApplyOrientationMetadata(bool value)
Set whether readers should apply source orientation metadata to returned frames.
Definition: ReaderBase.cpp:270
openshot::ClipBase::End
virtual void End(float value)
Set end position (in seconds) of clip (trim end of video)
Definition: ClipBase.cpp:53
openshot
This namespace is the default namespace for all code in the openshot library.
Definition: AnimatedCurve.h:24
openshot::Clip::scale_y
openshot::Keyframe scale_y
Curve representing the vertical scaling in percent (0 to 1)
Definition: Clip.h:332
openshot::Clip::PropertiesJSON
std::string PropertiesJSON(int64_t requested_frame) const override
Definition: Clip.cpp:828
openshot::EffectBase::ParentClip
openshot::ClipBase * ParentClip()
Parent clip object of this effect (which can be unparented and NULL)
Definition: EffectBase.cpp:676
openshot::AudioLocation
This struct holds the associated video frame and starting sample # for an audio packet.
Definition: AudioLocation.h:25
openshot::Keyframe::GetDelta
double GetDelta(int64_t index) const
Get the change in Y value (from the previous Y value)
Definition: KeyFrame.cpp:399
TextReader.h
Header file for TextReader class.
openshot::AUDIO_VISUALIZATION_SPECTRUM
@ AUDIO_VISUALIZATION_SPECTRUM
Definition: AudioVisualization.h:32
openshot::Clip::time
openshot::Keyframe time
Curve representing the frames over time to play (used for speed and direction of video)
Definition: Clip.h:347
openshot::CompositeType
CompositeType
This enumeration determines how clips are composited onto lower layers.
Definition: Enums.h:75
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::AudioLocation::frame
int64_t frame
Definition: AudioLocation.h:26
openshot::Clip
This class represents a clip (used to arrange readers on the timeline)
Definition: Clip.h:89
juce::AudioBuffer< float >
openshot::AudioLocation::sample_start
int sample_start
Definition: AudioLocation.h:27
openshot::Clip::alpha
openshot::Keyframe alpha
Curve representing the alpha (1 to 0)
Definition: Clip.h:335
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:426
openshot::AudioVisualization::GetFrame
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number) override
This method is required for all derived classes of ClipBase, and returns a new openshot::Frame object...
Definition: AudioVisualization.h:89
openshot::ReaderBase::info
openshot::ReaderInfo info
Information about the current media file.
Definition: ReaderBase.h:91
openshot::COMPOSITE_SCREEN
@ COMPOSITE_SCREEN
Definition: Enums.h:92
openshot::GRAVITY_TOP_LEFT
@ GRAVITY_TOP_LEFT
Align clip to the top left of its parent.
Definition: Enums.h:23
Timeline.h
Header file for Timeline class.
openshot::Clip::origin_x
openshot::Keyframe origin_x
Curve representing X origin point (0.0=0% (left), 1.0=100% (right))
Definition: Clip.h:343
openshot::Clip::ParentTimeline
void ParentTimeline(openshot::TimelineBase *new_timeline) override
Set associated Timeline pointer.
Definition: Clip.cpp:453
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:461
openshot::Clip::Close
void Close() override
Close the internal reader.
Definition: Clip.cpp:411
AudioResampler.h
Header file for AudioResampler class.
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:334
openshot::DummyReader
This class is used as a simple, dummy reader, which can be very useful when writing unit tests....
Definition: DummyReader.h:85
openshot::GRAVITY_TOP_RIGHT
@ GRAVITY_TOP_RIGHT
Align clip to the top right of its parent.
Definition: Enums.h:25
openshot::Keyframe::SetJsonValue
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: KeyFrame.cpp:372
openshot::GravityType
GravityType
This enumeration determines how clips are aligned to their parent container.
Definition: Enums.h:21
openshot::Clip::origin_y
openshot::Keyframe origin_y
Curve representing Y origin point (0.0=0% (top), 1.0=100% (bottom))
Definition: Clip.h:344
openshot::Clip::GetParentTrackedObject
std::shared_ptr< openshot::TrackedObjectBase > GetParentTrackedObject()
Return the associated Parent Tracked Object (if any)
Definition: Clip.cpp:564
openshot::ReaderInfo::duration
float duration
Length of time (in seconds)
Definition: ReaderBase.h:43
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:111
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:365
openshot::Clip::AddEffect
void AddEffect(openshot::EffectBase *effect)
Add an effect to the clip.
Definition: Clip.cpp:1282
openshot::ReaderBase::Name
virtual std::string Name()=0
Return the type name of the class.
openshot::Clip::~Clip
virtual ~Clip()
Destructor.
Definition: Clip.cpp:290
openshot::ReaderInfo::width
int width
The width of the video (in pixesl)
Definition: ReaderBase.h:46
openshot::Clip::Json
std::string Json() const override
Generate JSON string of this object.
Definition: Clip.cpp:821
openshot::ClipBase::Position
void Position(float value)
Set the Id of this clip object
Definition: ClipBase.cpp:19
openshot::AUDIO_VISUALIZATION_BARS
@ AUDIO_VISUALIZATION_BARS
Definition: AudioVisualization.h:30
openshot::GRAVITY_RIGHT
@ GRAVITY_RIGHT
Align clip to the right of its parent (middle aligned)
Definition: Enums.h:28
openshot::FRAME_DISPLAY_NONE
@ FRAME_DISPLAY_NONE
Do not display the frame number.
Definition: Enums.h:53
openshot::COMPOSITE_SOFT_LIGHT
@ COMPOSITE_SOFT_LIGHT
Definition: Enums.h:99
openshot::CompareClipEffects
Definition: Clip.h:48
openshot::Clip::SetJsonValue
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Definition: Clip.cpp:1050
openshot::AudioVisualization::color_spread
Keyframe color_spread
Definition: AudioVisualization.h:79
openshot::AudioVisualization::smoothing
Keyframe smoothing
Definition: AudioVisualization.h:76
openshot::OutOfBoundsFrame
Exception for frames that are out of bounds.
Definition: Exceptions.h:306
openshot::Fraction::ToDouble
double ToDouble() const
Return this fraction as a double (i.e. 1/2 = 0.5)
Definition: Fraction.cpp:40
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:561
openshot::Keyframe::JsonValue
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: KeyFrame.cpp:339
openshot::AUDIO_VISUALIZATION_PARTICLES
@ AUDIO_VISUALIZATION_PARTICLES
Definition: AudioVisualization.h:34
openshot::COMPOSITE_LIGHTEN
@ COMPOSITE_LIGHTEN
Definition: Enums.h:95
FrameMapper.h
Header file for the FrameMapper class.
openshot::GRAVITY_TOP
@ GRAVITY_TOP
Align clip to the top center of its parent.
Definition: Enums.h:24
openshot::COMPOSITE_OVERLAY
@ COMPOSITE_OVERLAY
Definition: Enums.h:93
openshot::AUDIO_VISUALIZATION_PHASE_SCOPE
@ AUDIO_VISUALIZATION_PHASE_SCOPE
Definition: AudioVisualization.h:33
openshot::CacheBase::SetMaxBytesFromInfo
void SetMaxBytesFromInfo(int64_t number_of_frames, int width, int height, int sample_rate, int channels)
Set maximum bytes to a different amount based on a ReaderInfo struct.
Definition: CacheBase.cpp:28
openshot::Color
This class represents a color (used on the timeline and clips)
Definition: Color.h:27
openshot::Clip::display
openshot::FrameDisplayType display
The format to display the frame number (if any)
Definition: Clip.h:188
openshot::AUDIO_VISUALIZATION_BACKGROUND_TRANSPARENT
@ AUDIO_VISUALIZATION_BACKGROUND_TRANSPARENT
Definition: AudioVisualization.h:54
openshot::ClipBase::SetJsonValue
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
Definition: ClipBase.cpp:80
openshot::Clip::perspective_c2_y
openshot::Keyframe perspective_c2_y
Curves representing Y for coordinate 2.
Definition: Clip.h:357
openshot::Clip::scale_x
openshot::Keyframe scale_x
Curve representing the horizontal scaling in percent (0 to 1)
Definition: Clip.h:331
openshot::QtImageReader
This class uses the Qt library, to open image files, and return openshot::Frame objects containing th...
Definition: QtImageReader.h:74
openshot::ClipBase::JsonValue
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
Definition: ClipBase.cpp:64
openshot::ReaderInfo::video_length
int64_t video_length
The number of frames in the video stream.
Definition: ReaderBase.h:53
openshot::AudioResampler
This class is used to resample audio data for many sequential frames.
Definition: AudioResampler.h:30
openshot::AudioResampler::SetBuffer
void SetBuffer(juce::AudioBuffer< float > *new_buffer, double sample_rate, double new_sample_rate)
Sets the audio buffer and key settings.
Definition: AudioResampler.cpp:60
openshot::ReaderInfo::height
int height
The height of the video (in pixels)
Definition: ReaderBase.h:45
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::Clip::corner_radius
openshot::Keyframe corner_radius
Curve representing corner radius as a percent of the clip's shortest side.
Definition: Clip.h:337
openshot::ClipBase::position
float position
The position on the timeline where this clip should start playing.
Definition: ClipBase.h:35
openshot::Timeline::GetClip
openshot::Clip * GetClip(const std::string &id)
Look up a single clip by ID.
Definition: Timeline.cpp:420
openshot::Clip::perspective_c3_y
openshot::Keyframe perspective_c3_y
Curves representing Y for coordinate 3.
Definition: Clip.h:359
openshot::Clip::perspective_c4_y
openshot::Keyframe perspective_c4_y
Curves representing Y for coordinate 4.
Definition: Clip.h:361
ZmqLogger.h
Header file for ZeroMQ-based Logger class.
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:369
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
AudioVisualization.h
Header file for AudioVisualization effect class.
openshot::Clip::gravity
openshot::GravityType gravity
The gravity of a clip determines where it snaps to its parent.
Definition: Clip.h:185
openshot::Color::SetJsonValue
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: Color.cpp:117
openshot::TimelineInfoStruct::is_before_clip_keyframes
bool is_before_clip_keyframes
Is this before clip keyframes are applied.
Definition: TimelineBase.h:35
openshot::ReaderBase::Open
virtual void Open()=0
Open the reader (and start consuming resources, such as images or video files)
openshot::GRAVITY_BOTTOM
@ GRAVITY_BOTTOM
Align clip to the bottom center of its parent.
Definition: Enums.h:30
openshot::ReaderBase::IsOpen
virtual bool IsOpen()=0
Determine if reader is open or closed.
openshot::AUDIO_VISUALIZATION_VU_METER
@ AUDIO_VISUALIZATION_VU_METER
Definition: AudioVisualization.h:35
openshot::InvalidJSON
Exception for invalid JSON.
Definition: Exceptions.h:223
openshot::AudioVisualization::frequency_low
Keyframe frequency_low
Definition: AudioVisualization.h:82
openshot::Timeline
This class represents a timeline.
Definition: Timeline.h:153
openshot::ImageReader
This class uses the ImageMagick++ libraries, to open image files, and return openshot::Frame objects ...
Definition: ImageReader.h:55
openshot::Clip::composite
openshot::CompositeType composite
How this clip is composited onto lower layers.
Definition: Clip.h:190
openshot::Clip::perspective_c1_x
openshot::Keyframe perspective_c1_x
Curves representing X for coordinate 1.
Definition: Clip.h:354
openshot::SCALE_CROP
@ SCALE_CROP
Scale the clip until both height and width fill the canvas (cropping the overlap)
Definition: Enums.h:37
openshot::Color::green
openshot::Keyframe green
Curve representing the green value (0 - 255)
Definition: Color.h:31
openshot::COMPOSITE_HARD_LIGHT
@ COMPOSITE_HARD_LIGHT
Definition: Enums.h:98
openshot::Clip::init_settings
void init_settings()
Init default settings for a clip.
Definition: Clip.cpp:71
openshot::TimelineInfoStruct
This struct contains info about the current Timeline clip instance.
Definition: TimelineBase.h:32
openshot::AudioVisualization::intensity
Keyframe intensity
Definition: AudioVisualization.h:75
openshot::EffectInfoStruct::has_tracked_object
bool has_tracked_object
Determines if this effect track objects through the clip.
Definition: EffectBase.h:45
openshot::ReaderInfo::metadata
std::map< std::string, std::string > metadata
An optional map/dictionary of metadata for this reader.
Definition: ReaderBase.h:65
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::Start
void Start(float value)
Set start position (in seconds) of clip (trim start of video)
Definition: ClipBase.cpp:42
openshot::FFmpegReader
This class uses the FFmpeg libraries, to open video files and audio files, and return openshot::Frame...
Definition: FFmpegReader.h:103
path
path
Definition: FFmpegWriter.cpp:1578
openshot::COMPOSITE_PLUS
@ COMPOSITE_PLUS
Definition: Enums.h:90
openshot::FrameMapper
This class creates a mapping between 2 different frame rates, applying a specific pull-down technique...
Definition: FrameMapper.h:193
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:486
openshot::AudioVisualization::detail
Keyframe detail
Definition: AudioVisualization.h:77
ChunkReader.h
Header file for ChunkReader class.
openshot::COMPOSITE_DIFFERENCE
@ COMPOSITE_DIFFERENCE
Definition: Enums.h:100
openshot::AudioVisualization::style
int style
Definition: AudioVisualization.h:73
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::ClipBase::start
float start
The position in seconds to start playing (used to trim the beginning of a clip)
Definition: ClipBase.h:37
openshot::Clip::Reader
openshot::ReaderBase * Reader()
Get the current reader.
Definition: Clip.cpp:380
openshot::SCALE_FIT
@ SCALE_FIT
Scale the clip until either height or width fills the canvas (with no cropping)
Definition: Enums.h:38
openshot::GRAVITY_BOTTOM_LEFT
@ GRAVITY_BOTTOM_LEFT
Align clip to the bottom left of its parent.
Definition: Enums.h:29
openshot::Clip::perspective_c2_x
openshot::Keyframe perspective_c2_x
Curves representing X for coordinate 2.
Definition: Clip.h:356
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::Clip::volume
openshot::Keyframe volume
Curve representing the volume (0 to 1)
Definition: Clip.h:348
openshot::COMPOSITE_DARKEN
@ COMPOSITE_DARKEN
Definition: Enums.h:94
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:229
openshot::Clip::SetJson
void SetJson(const std::string value) override
Load JSON string into this object.
Definition: Clip.cpp:1033
openshot::GRAVITY_BOTTOM_RIGHT
@ GRAVITY_BOTTOM_RIGHT
Align clip to the bottom right of its parent.
Definition: Enums.h:31
openshot::Color::JsonValue
Json::Value JsonValue() const
Generate Json::Value for this object.
Definition: Color.cpp:86
openshot::Keyframe::GetLength
int64_t GetLength() const
Definition: KeyFrame.cpp:417
openshot::Keyframe::GetInt
int GetInt(int64_t index) const
Get the rounded INT value at a specific index.
Definition: KeyFrame.cpp:282
openshot::AudioVisualization::color_mode
int color_mode
Definition: AudioVisualization.h:80
openshot::ANCHOR_CANVAS
@ ANCHOR_CANVAS
Anchor the clip to the canvas.
Definition: Enums.h:46
openshot::Clip::SetAttachedClip
void SetAttachedClip(Clip *clipObject)
Set the pointer to the clip this clip is attached to.
Definition: Clip.cpp:338
openshot::AUDIO_VISUALIZATION_RADIAL_BARS
@ AUDIO_VISUALIZATION_RADIAL_BARS
Definition: AudioVisualization.h:36
openshot::Clip::perspective_c4_x
openshot::Keyframe perspective_c4_x
Curves representing X for coordinate 4.
Definition: Clip.h:360
openshot::ReaderClosed
Exception when a reader is closed, and a frame is requested.
Definition: Exceptions.h:369
openshot::ReaderInfo::channel_layout
openshot::ChannelLayout channel_layout
The channel layout (mono, stereo, 5 point surround, etc...)
Definition: ReaderBase.h:62
openshot::Clip::perspective_c1_y
openshot::Keyframe perspective_c1_y
Curves representing Y for coordinate 1.
Definition: Clip.h:355
openshot::COMPOSITE_EXCLUSION
@ COMPOSITE_EXCLUSION
Definition: Enums.h:101
openshot::AUDIO_VISUALIZATION_COLOR_SEED
@ AUDIO_VISUALIZATION_COLOR_SEED
Definition: AudioVisualization.h:62
openshot::Clip::channel_filter
openshot::Keyframe channel_filter
A number representing an audio channel to filter (clears all other channels)
Definition: Clip.h:364
openshot::Clip::init_reader_rotation
void init_reader_rotation()
Update default rotation from reader.
Definition: Clip.cpp:154
openshot::ClipBase::Id
void Id(std::string value)
Definition: ClipBase.h:94
openshot::Clip::init_reader_settings
void init_reader_settings()
Init reader info details.
Definition: Clip.cpp:141
openshot::TimelineBase
This class represents a timeline (used for building generic timeline implementations)
Definition: TimelineBase.h:41
MagickUtilities.h
Header file for MagickUtilities (IM6/IM7 compatibility overlay)
openshot::GRAVITY_LEFT
@ GRAVITY_LEFT
Align clip to the left of its parent (middle aligned)
Definition: Enums.h:26
openshot::Keyframe::GetCount
int64_t GetCount() const
Get the number of points (i.e. # of points)
Definition: KeyFrame.cpp:424
openshot::ReaderInfo::fps
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition: ReaderBase.h:48
openshot::Clip::Clip
Clip()
Default Constructor.
Definition: Clip.cpp:210
openshot::ReaderBase
This abstract class is the base class, used by all readers in libopenshot.
Definition: ReaderBase.h:75
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:333
openshot::AUDIO_VISUALIZATION_CHANNEL_AUTO
@ AUDIO_VISUALIZATION_CHANNEL_AUTO
Definition: AudioVisualization.h:47
openshot::ClipBase::previous_properties
std::string previous_properties
This string contains the previous JSON properties.
Definition: ClipBase.h:39
openshot::Clip::scale
openshot::ScaleType scale
The scale determines how a clip should be resized to fit its parent.
Definition: Clip.h:186
openshot::COMPOSITE_SOURCE_OVER
@ COMPOSITE_SOURCE_OVER
Definition: Enums.h:76
openshot::AudioResampler::GetResampledBuffer
juce::AudioBuffer< float > * GetResampledBuffer()
Get the resampled audio buffer.
Definition: AudioResampler.cpp:106
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::ReaderBase::Close
virtual void Close()=0
Close the reader (and any resources it was consuming)
openshot::AudioVisualization
Definition: AudioVisualization.h:66
openshot::AnchorType
AnchorType
This enumeration determines what parent a clip should be aligned to.
Definition: Enums.h:44
openshot::ScaleType
ScaleType
This enumeration determines how clips are scaled to fit their parent container.
Definition: Enums.h:35
openshot::Clip::AttachToObject
void AttachToObject(std::string object_id)
Attach clip to Tracked Object or to another Clip.
Definition: Clip.cpp:310
openshot::AudioVisualization::glow
Keyframe glow
Definition: AudioVisualization.h:78
openshot::Color::alpha
openshot::Keyframe alpha
Curve representing the alpha value (0 - 255)
Definition: Color.h:33
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:368
openshot::Clip::GetParentClip
openshot::Clip * GetParentClip()
Return the associated ParentClip (if any)
Definition: Clip.cpp:555
openshot::Clip::rotation
openshot::Keyframe rotation
Curve representing the rotation (0 to 360)
Definition: Clip.h:340
openshot::SCALE_NONE
@ SCALE_NONE
Do not scale the clip.
Definition: Enums.h:40
openshot::TextReader
This class uses the ImageMagick++ libraries, to create frames with "Text", and return openshot::Frame...
Definition: TextReader.h:62
QtImageReader.h
Header file for QtImageReader class.
openshot::GRAVITY_CENTER
@ GRAVITY_CENTER
Align clip to the center of its parent (middle aligned)
Definition: Enums.h:27
openshot::AUDIO_VISUALIZATION_WAVEFORM
@ AUDIO_VISUALIZATION_WAVEFORM
Definition: AudioVisualization.h:28
openshot::Clip::JsonValue
Json::Value JsonValue() const override
Generate Json::Value for this object.
Definition: Clip.cpp:964
openshot::Color::red
openshot::Keyframe red
Curve representing the red value (0 - 255)
Definition: Color.h:30
openshot::SCALE_STRETCH
@ SCALE_STRETCH
Scale the clip until both height and width fill the canvas (distort to fit)
Definition: Enums.h:39
ImageReader.h
Header file for ImageReader class.
openshot::FrameMapper::Reader
ReaderBase * Reader()
Get the current reader.
Definition: FrameMapper.cpp:67
openshot::AudioVisualization::frequency_high
Keyframe frequency_high
Definition: AudioVisualization.h:83
openshot::Clip::perspective_c3_x
openshot::Keyframe perspective_c3_x
Curves representing X for coordinate 3.
Definition: Clip.h:358
openshot::COMPOSITE_COLOR_BURN
@ COMPOSITE_COLOR_BURN
Definition: Enums.h:97
openshot::VOLUME_MIX_NONE
@ VOLUME_MIX_NONE
Do not apply any volume mixing adjustments. Just add the samples together.
Definition: Enums.h:69
openshot::AudioVisualization::color
Color color
Definition: AudioVisualization.h:74
openshot::ChunkVersion
ChunkVersion
This enumeration allows the user to choose which version of the chunk they would like (low,...
Definition: ChunkReader.h:49
openshot::ClipBase::Layer
void Layer(int value)
Set layer of clip on timeline (lower number is covered by higher numbers)
Definition: ClipBase.cpp:31
openshot::ReaderInfo::channels
int channels
The number of audio channels used in the audio stream.
Definition: ReaderBase.h:61
openshot::VolumeMixType
VolumeMixType
This enumeration determines the strategy when mixing audio with other clips.
Definition: Enums.h:67
openshot::Clip::wave_color
openshot::Color wave_color
Curve representing the color of the audio wave form.
Definition: Clip.h:351
openshot::Clip::shear_y
openshot::Keyframe shear_y
Curve representing Y shear angle in degrees (-45.0=down, 45.0=up)
Definition: Clip.h:342
openshot::Clip::RemoveEffect
void RemoveEffect(openshot::EffectBase *effect)
Remove an effect from the clip.
Definition: Clip.cpp:1329
DummyReader.h
Header file for DummyReader class.
openshot::AUDIO_VISUALIZATION_FILLED_WAVEFORM
@ AUDIO_VISUALIZATION_FILLED_WAVEFORM
Definition: AudioVisualization.h:29
openshot::Color::blue
openshot::Keyframe blue
Curve representing the red value (0 - 255)
Definition: Color.h:32
openshot::Timeline::GetTrackedObject
std::shared_ptr< openshot::TrackedObjectBase > GetTrackedObject(std::string id) const
Return tracked object pointer by it's id.
Definition: Timeline.cpp:247
Exceptions.h
Header file for all Exception classes.
openshot::Clip::mixing
openshot::VolumeMixType mixing
What strategy should be followed when mixing audio with other clips.
Definition: Clip.h:189
FFmpegReader.h
Header file for FFmpegReader class.
openshot::COMPOSITE_COLOR_DODGE
@ COMPOSITE_COLOR_DODGE
Definition: Enums.h:96
openshot::Clip::shear_x
openshot::Keyframe shear_x
Curve representing X shear angle in degrees (-45.0=left, 45.0=right)
Definition: Clip.h:341
openshot::EffectBase::SetJsonValue
virtual void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: EffectBase.cpp:146
openshot::Keyframe::GetValue
double GetValue(int64_t index) const
Get the value at a specific index.
Definition: KeyFrame.cpp:258
openshot::Clip::margin
openshot::Keyframe margin
Curve representing edge margin as a percent of the canvas' shortest side.
Definition: Clip.h:336
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:333
openshot::Clip::getFrameMutex
std::recursive_mutex getFrameMutex
Mutex for multiple threads.
Definition: Clip.h:92
openshot::FrameDisplayType
FrameDisplayType
This enumeration determines the display format of the clip's frame number (if any)....
Definition: Enums.h:51
openshot::ReaderBase::ParentClip
openshot::ClipBase * ParentClip()
Parent clip object of this reader (which can be unparented and NULL)
Definition: ReaderBase.cpp:244