OpenShot Library | libopenshot  0.5.0
FrameMapper.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 <cmath>
14 #include <algorithm>
15 #include <iostream>
16 #include <iomanip>
17 
18 #include "FrameMapper.h"
19 #include "Exceptions.h"
20 #include "Clip.h"
21 #include "MemoryTrim.h"
22 #include "ZmqLogger.h"
23 
24 using namespace std;
25 using namespace openshot;
26 
27 FrameMapper::FrameMapper(ReaderBase *reader, Fraction target, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout) :
28  reader(reader), target(target), pulldown(target_pulldown), is_dirty(true), avr(NULL), parent_position(0.0), parent_start(0.0), previous_frame(0)
29 {
30  // Set the original frame rate from the reader
31  original = Fraction(reader->info.fps.num, reader->info.fps.den);
32 
33  // Set all info struct members equal to the internal reader
34  info = reader->info;
35  info.fps.num = target.num;
36  info.fps.den = target.den;
37  info.video_timebase.num = target.den;
38  info.video_timebase.den = target.num;
40  info.sample_rate = target_sample_rate;
41  info.channels = target_channels;
42  info.channel_layout = target_channel_layout;
43  info.width = reader->info.width;
44  info.height = reader->info.height;
45 
46  // Enable/Disable audio (based on settings)
48 
49  // Used to toggle odd / even fields
50  field_toggle = true;
51 
52  // Adjust cache size based on size of frame and audio
53  const int initial_cache_frames = std::max(Settings::Instance()->CACHE_MIN_FRAMES, OPEN_MP_NUM_PROCESSORS);
54  final_cache.SetMaxBytesFromInfo(initial_cache_frames, info.width, info.height, info.sample_rate, info.channels);
55 }
56 
57 // Destructor
59 
60  // Auto Close if not already
61  Close();
62 
63  reader = NULL;
64 }
65 
68 {
69  if (reader)
70  return reader;
71  else
72  // Throw error if reader not initialized
73  throw ReaderClosed("No Reader has been initialized for FrameMapper. Call Reader(*reader) before calling this method.");
74 }
75 
76 void FrameMapper::AddField(int64_t frame)
77 {
78  // Add a field, and toggle the odd / even field
79  Field f = { frame, bool(field_toggle) };
80  AddField(f);
81 }
82 
83 void FrameMapper::AddField(int64_t frame, bool isOdd)
84 {
85  // Add a field, and toggle the odd / even field
86  Field f = { frame, isOdd };
87  AddField(f);
88 }
89 
90 void FrameMapper::AddField(Field field)
91 {
92  // Add a field to the end of the field list
93  fields.push_back(field);
94 
95  // toggle the odd / even flag
96  field_toggle = (field_toggle ? false : true);
97 }
98 
99 // Clear both the fields & frames lists
100 void FrameMapper::Clear() {
101  // Prevent async calls to the following code
102  const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
103 
104  // Clear the fields & frames lists
105  fields.clear();
106  fields.shrink_to_fit();
107  frames.clear();
108  frames.shrink_to_fit();
109 }
110 
111 // Use the original and target frame rates and a pull-down technique to create
112 // a mapping between the original fields and frames or a video to a new frame rate.
113 // This might repeat or skip fields and frames of the original video, depending on
114 // whether the frame rate is increasing or decreasing.
115 void FrameMapper::Init()
116 {
117  ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::Init (Calculate frame mappings)");
118 
119  // Do not initialize anything if just a picture with no audio
121  // Skip initialization
122  return;
123 
124  // Prevent async calls to the following code
125  const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
126 
127  // Clear the fields & frames lists
128  Clear();
129 
130  // Find parent position (if any)
131  Clip *parent = static_cast<Clip *>(ParentClip());
132  if (parent) {
133  parent_position = parent->Position();
134  parent_start = parent->Start();
135  } else {
136  parent_position = 0.0;
137  parent_start = 0.0;
138  }
139 
140  // Mark as not dirty
141  is_dirty = false;
142 
143  // Clear cache
144  final_cache.Clear();
145 
146  // Some framerates are handled special, and some use a generic Keyframe curve to
147  // map the framerates. These are the special framerates:
148  if ((fabs(original.ToFloat() - 24.0) < 1e-7 || fabs(original.ToFloat() - 25.0) < 1e-7 || fabs(original.ToFloat() - 30.0) < 1e-7) &&
149  (fabs(target.ToFloat() - 24.0) < 1e-7 || fabs(target.ToFloat() - 25.0) < 1e-7 || fabs(target.ToFloat() - 30.0) < 1e-7)) {
150 
151  // Get the difference (in frames) between the original and target frame rates
152  float difference = target.ToInt() - original.ToInt();
153 
154  // Find the number (i.e. interval) of fields that need to be skipped or repeated
155  int field_interval = 0;
156  int frame_interval = 0;
157 
158  if (difference != 0)
159  {
160  field_interval = round(fabs(original.ToInt() / difference));
161 
162  // Get frame interval (2 fields per frame)
163  frame_interval = field_interval * 2.0f;
164  }
165 
166 
167  // Calculate # of fields to map
168  int64_t frame = 1;
169  int64_t number_of_fields = reader->info.video_length * 2;
170 
171  // Loop through all fields in the original video file
172  for (int64_t field = 1; field <= number_of_fields; field++)
173  {
174 
175  if (difference == 0) // Same frame rate, NO pull-down or special techniques required
176  {
177  // Add fields
178  AddField(frame);
179  }
180  else if (difference > 0) // Need to ADD fake fields & frames, because original video has too few frames
181  {
182  // Add current field
183  AddField(frame);
184 
185  if (pulldown == PULLDOWN_CLASSIC && field % field_interval == 0)
186  {
187  // Add extra field for each 'field interval
188  AddField(frame);
189  }
190  else if (pulldown == PULLDOWN_ADVANCED && field % field_interval == 0 && field % frame_interval != 0)
191  {
192  // Add both extra fields in the middle 'together' (i.e. 2:3:3:2 technique)
193  AddField(frame); // add field for current frame
194 
195  if (frame + 1 <= info.video_length)
196  // add field for next frame (if the next frame exists)
197  AddField(frame + 1, field_toggle);
198  }
199  else if (pulldown == PULLDOWN_NONE && field % frame_interval == 0)
200  {
201  // No pull-down technique needed, just repeat this frame
202  AddField(frame);
203  AddField(frame);
204  }
205  }
206  else if (difference < 0) // Need to SKIP fake fields & frames, because we want to return to the original film frame rate
207  {
208 
209  if (pulldown == PULLDOWN_CLASSIC && field % field_interval == 0)
210  {
211  // skip current field and toggle the odd/even flag
212  field_toggle = (field_toggle ? false : true);
213  }
214  else if (pulldown == PULLDOWN_ADVANCED && field % field_interval == 0 && field % frame_interval != 0)
215  {
216  // skip this field, plus the next field
217  field++;
218  }
219  else if (pulldown == PULLDOWN_NONE && frame % field_interval == 0)
220  {
221  // skip this field, plus the next one
222  field++;
223  }
224  else
225  {
226  // No skipping needed, so add the field
227  AddField(frame);
228  }
229  }
230 
231  // increment frame number (if field is divisible by 2)
232  if (field % 2 == 0 && field > 0)
233  frame++;
234  }
235 
236  } else {
237  // Map the remaining framerates using a linear algorithm
238  double rate_diff = target.ToDouble() / original.ToDouble();
239  int64_t new_length = reader->info.video_length * rate_diff;
240 
241  // Calculate the value difference
242  double value_increment = reader->info.video_length / (double) (new_length);
243 
244  // Loop through curve, and build list of frames
245  double original_frame_num = 1.0f;
246  for (int64_t frame_num = 1; frame_num <= new_length; frame_num++)
247  {
248  // Add 2 fields per frame
249  AddField(round(original_frame_num));
250  AddField(round(original_frame_num));
251 
252  // Increment original frame number
253  original_frame_num += value_increment;
254  }
255  }
256 
257  // Loop through the target frames again (combining fields into frames)
258  Field Odd = {0, true}; // temp field used to track the ODD field
259  Field Even = {0, true}; // temp field used to track the EVEN field
260 
261  // Variables used to remap audio samples
262  int64_t start_samples_frame = 1;
263  int start_samples_position = 0;
264 
265  for (std::vector<Field>::size_type field = 1; field <= fields.size(); field++)
266  {
267  // Get the current field
268  Field f = fields[field - 1];
269 
270  // Is field divisible by 2?
271  if (field % 2 == 0 && field > 0)
272  {
273  // New frame number
274  int64_t frame_number = field / 2;
275 
276  // Set the bottom frame
277  if (f.isOdd)
278  Odd = f;
279  else
280  Even = f;
281 
282  // Determine the range of samples (from the original rate). Resampling happens in real-time when
283  // calling the GetFrame() method. So this method only needs to redistribute the original samples with
284  // the original sample rate.
285  int64_t end_samples_frame = start_samples_frame;
286  int end_samples_position = start_samples_position;
287  int remaining_samples = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number), target, reader->info.sample_rate, reader->info.channels);
288 
289  while (remaining_samples > 0)
290  {
291  // Get original samples (with NO framerate adjustments)
292  // This is the original reader's frame numbers
293  int original_samples = Frame::GetSamplesPerFrame(end_samples_frame, original, reader->info.sample_rate, reader->info.channels) - end_samples_position;
294 
295  // Enough samples
296  if (original_samples >= remaining_samples)
297  {
298  // Take all that we need, and break loop
299  end_samples_position += remaining_samples - 1;
300  remaining_samples = 0;
301  } else
302  {
303  // Not enough samples (take them all, and keep looping)
304  end_samples_frame += 1; // next frame
305  end_samples_position = 0; // next frame, starting on 1st sample
306  remaining_samples -= original_samples; // reduce the remaining amount
307  }
308  }
309 
310 
311 
312  // Create the sample mapping struct
313  SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position, Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number), target, reader->info.sample_rate, reader->info.channels)};
314 
315  // Reset the audio variables
316  start_samples_frame = end_samples_frame;
317  start_samples_position = end_samples_position + 1;
318  if (start_samples_position >= Frame::GetSamplesPerFrame(AdjustFrameNumber(start_samples_frame), original, reader->info.sample_rate, reader->info.channels))
319  {
320  start_samples_frame += 1; // increment the frame (since we need to wrap onto the next one)
321  start_samples_position = 0; // reset to 0, since we wrapped
322  }
323 
324  // Create a frame and ADD it to the frames collection
325  MappedFrame frame = {Odd, Even, Samples};
326  frames.push_back(frame);
327  }
328  else
329  {
330  // Set the top field
331  if (f.isOdd)
332  Odd = f;
333  else
334  Even = f;
335  }
336  }
337 
338  // Clear the internal fields list (no longer needed)
339  fields.clear();
340  fields.shrink_to_fit();
341 
342  if (avr) {
343  // Delete resampler (if exists)
344  SWR_CLOSE(avr);
345  SWR_FREE(&avr);
346  avr = NULL;
347  }
348 }
349 
350 MappedFrame FrameMapper::GetMappedFrame(int64_t TargetFrameNumber)
351 {
352  // Check if mappings are dirty (and need to be recalculated)
353  if (is_dirty)
354  // Recalculate mappings
355  Init();
356 
357  // Ignore mapping on single image readers
359  // Return the same number
360  MappedFrame frame;
361  frame.Even.Frame = TargetFrameNumber;
362  frame.Odd.Frame = TargetFrameNumber;
363  frame.Samples.frame_start = 0;
364  frame.Samples.frame_end = 0;
365  frame.Samples.sample_start = 0;
366  frame.Samples.sample_end = 0;
367  frame.Samples.total = 0;
368  return frame;
369  }
370 
371  // Check if frame number is valid
372  if(TargetFrameNumber < 1 || frames.size() == 0)
373  // frame too small, return error
374  throw OutOfBoundsFrame("An invalid frame was requested.", TargetFrameNumber, frames.size());
375 
376  else if (TargetFrameNumber > (int64_t)frames.size())
377  // frame too large, set to end frame
378  TargetFrameNumber = frames.size();
379 
380  // Debug output
382  "FrameMapper::GetMappedFrame",
383  "TargetFrameNumber", TargetFrameNumber,
384  "frames.size()", frames.size(),
385  "frames[...].Odd", frames[TargetFrameNumber - 1].Odd.Frame,
386  "frames[...].Even", frames[TargetFrameNumber - 1].Even.Frame);
387 
388  // Return frame
389  return frames[TargetFrameNumber - 1];
390 }
391 
392 // Get or generate a blank frame
393 std::shared_ptr<Frame> FrameMapper::GetOrCreateFrame(int64_t number)
394 {
395  std::shared_ptr<Frame> new_frame;
396 
397  // Init some basic properties about this frame (keep sample rate and # channels the same as the original reader for now)
398  int samples_in_frame = Frame::GetSamplesPerFrame(AdjustFrameNumber(number), target, reader->info.sample_rate, reader->info.channels);
399 
400  try {
401  // Debug output
403  "FrameMapper::GetOrCreateFrame (from reader)",
404  "number", number,
405  "samples_in_frame", samples_in_frame);
406 
407  // Attempt to get a frame (but this could fail if a reader has just been closed)
408  new_frame = reader->GetFrame(number);
409 
410  // Return real frame
411  return new_frame;
412 
413  } catch (const ReaderClosed & e) {
414  // ...
415  } catch (const OutOfBoundsFrame & e) {
416  // ...
417  }
418 
419  // Debug output
421  "FrameMapper::GetOrCreateFrame (create blank)",
422  "number", number,
423  "samples_in_frame", samples_in_frame);
424 
425  // Create blank frame
426  new_frame = std::make_shared<Frame>(number, info.width, info.height, "#000000", samples_in_frame, reader->info.channels);
427  new_frame->SampleRate(reader->info.sample_rate);
428  new_frame->ChannelsLayout(info.channel_layout);
429  new_frame->AddAudioSilence(samples_in_frame);
430  return new_frame;
431 }
432 
433 // Get an openshot::Frame object for a specific frame number of this reader.
434 std::shared_ptr<Frame> FrameMapper::GetFrame(int64_t requested_frame)
435 {
436  // Check final cache, and just return the frame (if it's available)
437  std::shared_ptr<Frame> final_frame = final_cache.GetFrame(requested_frame);
438  if (final_frame) return final_frame;
439 
440  // Create a scoped lock, allowing only a single thread to run the following code at one time
441  const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
442 
443  // Find parent properties (if any)
444  Clip *parent = static_cast<Clip *>(ParentClip());
445  bool is_increasing = true;
446  bool direction_flipped = false;
447 
448  {
449  const std::lock_guard<std::recursive_mutex> lock(directionMutex);
450 
451  // One-shot: if a hint exists, consume it for THIS call, regardless of frame number.
452  if (have_hint) {
453  is_increasing = hint_increasing;
454  have_hint = false;
455  } else if (previous_frame > 0 && std::llabs(requested_frame - previous_frame) == 1) {
456  // Infer from request order when adjacent
457  is_increasing = (requested_frame > previous_frame);
458  } else if (last_dir_initialized) {
459  // Reuse last known direction if non-adjacent and no hint
460  is_increasing = last_is_increasing;
461  } else {
462  is_increasing = true; // default on first call
463  }
464 
465  // Detect flips so we can reset SR context
466  if (!last_dir_initialized) {
467  last_is_increasing = is_increasing;
468  last_dir_initialized = true;
469  } else if (last_is_increasing != is_increasing) {
470  direction_flipped = true;
471  last_is_increasing = is_increasing;
472  }
473  }
474  if (parent) {
475  float position = parent->Position();
476  float start = parent->Start();
477  if (parent_position != position || parent_start != start) {
478  // Force dirty if parent clip has moved or been trimmed
479  // since this heavily affects frame #s and audio mappings
480  is_dirty = true;
481  }
482  }
483 
484  // Check if mappings are dirty (and need to be recalculated)
485  if (is_dirty)
486  Init();
487 
488  // Check final cache a 2nd time (due to potential lock already generating this frame)
489  final_frame = final_cache.GetFrame(requested_frame);
490  if (final_frame) return final_frame;
491 
492  // Minimum number of frames to process (for performance reasons)
493  // Dialing this down to 1 for now, as it seems to improve performance, and reduce export crashes
494  int minimum_frames = 1;
495 
496  // Debug output
498  "FrameMapper::GetFrame (Loop through frames)",
499  "requested_frame", requested_frame,
500  "minimum_frames", minimum_frames);
501 
502  // Loop through all requested frames
503  for (int64_t frame_number = requested_frame; frame_number < requested_frame + minimum_frames; frame_number++)
504  {
505  // Debug output
507  "FrameMapper::GetFrame (inside omp for loop)",
508  "frame_number", frame_number,
509  "minimum_frames", minimum_frames,
510  "requested_frame", requested_frame);
511 
512  // Get the mapped frame
513  MappedFrame mapped = GetMappedFrame(frame_number);
514  std::shared_ptr<Frame> mapped_frame = GetOrCreateFrame(mapped.Odd.Frame);
515 
516  // Get # of channels in the actual frame
517  int channels_in_frame = mapped_frame->GetAudioChannelsCount();
518  int samples_in_frame = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame_number), target, mapped_frame->SampleRate(), channels_in_frame);
519 
520  // Determine if mapped frame is identical to source frame
521  // including audio sample distribution according to mapped.Samples,
522  // and frame_number. In some cases such as end of stream, the reader
523  // will return a frame with a different frame number. In these cases,
524  // we cannot use the frame as is, nor can we modify the frame number,
525  // otherwise the reader's cache object internals become invalid.
526  if (info.sample_rate == mapped_frame->SampleRate() &&
527  info.channels == mapped_frame->GetAudioChannelsCount() &&
528  info.channel_layout == mapped_frame->ChannelsLayout() &&
529  mapped.Samples.total == mapped_frame->GetAudioSamplesCount() &&
530  mapped.Samples.total == samples_in_frame && is_increasing &&
531  mapped.Samples.frame_start == mapped.Odd.Frame &&
532  mapped.Samples.sample_start == 0 &&
533  mapped_frame->number == frame_number &&// in some conditions (e.g. end of stream)
534  info.fps.num == reader->info.fps.num &&
535  info.fps.den == reader->info.fps.den) {
536  // Add original frame to cache, and skip the rest (for performance reasons)
537  final_cache.Add(mapped_frame);
538  continue;
539  }
540 
541  // Create a new frame
542  auto frame = std::make_shared<Frame>(
543  frame_number, 1, 1, "#000000", samples_in_frame, channels_in_frame);
544  frame->SampleRate(mapped_frame->SampleRate());
545  frame->ChannelsLayout(mapped_frame->ChannelsLayout());
546 
547 
548  // Copy the image from the odd field
549  std::shared_ptr<Frame> odd_frame = mapped_frame;
550 
551  if (odd_frame && odd_frame->has_image_data)
552  frame->AddImage(std::make_shared<QImage>(*odd_frame->GetImage()), true);
553  if (mapped.Odd.Frame != mapped.Even.Frame) {
554  // Add even lines (if different than the previous image)
555  std::shared_ptr<Frame> even_frame;
556  even_frame = GetOrCreateFrame(mapped.Even.Frame);
557  if (even_frame && even_frame->has_image_data)
558  frame->AddImage(std::make_shared<QImage>(*even_frame->GetImage()), false);
559  }
560 
561  // Only treat the reader as audio-capable when the wrapped reader reports
562  // audio. Individual source frames can still be empty near boundaries, but
563  // those cases should pad with silence instead of dropping this mapped frame.
564  const bool reader_has_audio = info.has_audio &&
565  mapped_frame->SampleRate() > 0 &&
566  mapped_frame->GetAudioChannelsCount() > 0;
567 
568  // Resample audio on frame (if needed)
569  bool need_resampling = false;
570  if ((info.has_audio && reader_has_audio) &&
571  (info.sample_rate != frame->SampleRate() ||
572  info.channels != frame->GetAudioChannelsCount() ||
573  info.channel_layout != frame->ChannelsLayout()))
574  // Resample audio and correct # of channels if needed
575  need_resampling = true;
576 
577  // create a copy of mapped.Samples that will be used by copy loop
578  SampleRange copy_samples = mapped.Samples;
579 
580  if (need_resampling)
581  {
582  // Reset resampler when non-adjacent request OR playback direction flips
583  if (direction_flipped || (previous_frame > 0 && std::llabs(requested_frame - previous_frame) > 1)) {
584  if (avr) {
585  // Delete resampler (if exists)
586  SWR_CLOSE(avr);
587  SWR_FREE(&avr);
588  avr = nullptr;
589  }
590  }
591 
592  // Resampling needed, modify copy of SampleRange object that includes some additional input samples on
593  // first iteration, and continues the offset to ensure that the resampler is not input limited.
594  const int EXTRA_INPUT_SAMPLES = 64;
595 
596  if (!avr) {
597  // This is the first iteration, and we need to extend # of samples for this frame
598  // Extend sample count range by an additional EXTRA_INPUT_SAMPLES
599  copy_samples.Extend(EXTRA_INPUT_SAMPLES, original, reader->info.sample_rate, reader->info.channels, is_increasing);
600  } else {
601  // Sample rate conversion has already been allocated on this clip, so
602  // this is not the first iteration. Shift position by EXTRA_INPUT_SAMPLES to correctly align samples
603  copy_samples.Shift(EXTRA_INPUT_SAMPLES, original, reader->info.sample_rate, reader->info.channels, is_increasing);
604  }
605  }
606 
607  // Preserve the target frame duration even when a source frame has no samples.
608  if (reader_has_audio && samples_in_frame > 0)
609  frame->AddAudioSilence(samples_in_frame);
610 
611  // Copy the samples
612  int samples_copied = 0;
613  int64_t starting_frame = copy_samples.frame_start;
614  while (reader_has_audio && samples_copied < copy_samples.total)
615  {
616  // Init number of samples to copy this iteration
617  int remaining_samples = copy_samples.total - samples_copied;
618  int number_to_copy = 0;
619 
620  // number of original samples on this frame
621  std::shared_ptr<Frame> original_frame = mapped_frame;
622  if (starting_frame != original_frame->number) {
623  original_frame = GetOrCreateFrame(starting_frame);
624  }
625 
626  int original_samples = original_frame->GetAudioSamplesCount();
627  if (original_samples <= 0) {
628  if (starting_frame >= copy_samples.frame_end)
629  break;
630  starting_frame++;
631  continue;
632  }
633 
634  if (starting_frame == copy_samples.frame_start)
635  number_to_copy = original_samples - copy_samples.sample_start;
636  else if (starting_frame > copy_samples.frame_start && starting_frame < copy_samples.frame_end)
637  number_to_copy = original_samples;
638  else
639  number_to_copy = copy_samples.sample_end + 1;
640 
641  if (number_to_copy <= 0) {
642  if (starting_frame >= copy_samples.frame_end)
643  break;
644  starting_frame++;
645  continue;
646  }
647  if (number_to_copy > remaining_samples)
648  number_to_copy = remaining_samples;
649 
650  // Loop through each channel
651  for (int channel = 0; channel < channels_in_frame; channel++)
652  {
653  if (starting_frame == copy_samples.frame_start)
654  {
655  // Starting frame (take the ending samples)
656  frame->AddAudio(true, channel, samples_copied, original_frame->GetAudioSamples(channel) + copy_samples.sample_start, number_to_copy, 1.0);
657  }
658  else if (starting_frame > copy_samples.frame_start && starting_frame < copy_samples.frame_end)
659  {
660  // Middle frame (take all samples)
661  frame->AddAudio(true, channel, samples_copied, original_frame->GetAudioSamples(channel), number_to_copy, 1.0);
662  }
663  else
664  {
665  // Ending frame (take the beginning samples)
666  frame->AddAudio(false, channel, samples_copied, original_frame->GetAudioSamples(channel), number_to_copy, 1.0);
667  }
668  }
669 
670  // increment frame
671  samples_copied += number_to_copy;
672  starting_frame++;
673  }
674 
675  // Set audio direction
676  frame->SetAudioDirection(is_increasing);
677 
678  // Resample audio on frame (if needed)
679  if (need_resampling)
680  // Resample audio and correct # of channels if needed
681  ResampleMappedAudio(frame, mapped.Odd.Frame);
682 
683  // Add frame to final cache
684  final_cache.Add(frame);
685 
686  } // for loop
687 
688  // Return processed openshot::Frame
689  return final_cache.GetFrame(requested_frame);
690 }
691 
692 void FrameMapper::PrintMapping(std::ostream* out)
693 {
694  // Check if mappings are dirty (and need to be recalculated)
695  if (is_dirty)
696  // Recalculate mappings
697  Init();
698 
699  // Loop through frame mappings
700  for (float map = 1; map <= frames.size(); map++)
701  {
702  MappedFrame frame = frames[map - 1];
703  *out << "Target frame #: " << map
704  << " mapped to original frame #:\t("
705  << frame.Odd.Frame << " odd, "
706  << frame.Even.Frame << " even)" << std::endl;
707 
708  *out << " - Audio samples mapped to frame "
709  << frame.Samples.frame_start << ":"
710  << frame.Samples.sample_start << " to frame "
711  << frame.Samples.frame_end << ":"
712  << frame.Samples.sample_end << endl;
713  }
714 
715 }
716 
717 // Determine if reader is open or closed
719  if (reader)
720  return reader->IsOpen();
721  else
722  return false;
723 }
724 
725 // Open the internal reader
727 {
728  if (reader)
729  {
730  ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::Open");
731 
732  // Open the reader
733  reader->Open();
734  }
735 }
736 
737 // Close the internal reader
739 {
740  if (reader)
741  {
742  // Create a scoped lock, allowing only a single thread to run the following code at one time
743  const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
744 
745  ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::Close");
746 
747  // Close internal reader
748  reader->Close();
749  }
750 
751  // Clear the fields & frames lists
752  Clear();
753 
754  // Mark as dirty
755  is_dirty = true;
756 
757  // Clear cache
758  final_cache.Clear();
759 
760  // Deallocate resample buffer
761  if (avr) {
762  SWR_CLOSE(avr);
763  SWR_FREE(&avr);
764  avr = NULL;
765  }
766 }
767 
768 
769 // Generate JSON string of this object
770 std::string FrameMapper::Json() const {
771 
772  // Return formatted string
773  return JsonValue().toStyledString();
774 }
775 
776 // Generate Json::Value for this object
777 Json::Value FrameMapper::JsonValue() const {
778 
779  // Create root json object
780  Json::Value root = ReaderBase::JsonValue(); // get parent properties
781  root["type"] = "FrameMapper";
782  if (reader) {
783  root["reader"] = reader->JsonValue();
784  }
785 
786  // return JsonValue
787  return root;
788 }
789 
790 // Load JSON string into this object
791 void FrameMapper::SetJson(const std::string value) {
792 
793  // Parse JSON string into JSON objects
794  try
795  {
796  const Json::Value root = openshot::stringToJson(value);
797  // Set all values that match
798  SetJsonValue(root);
799 
800  if (!root["reader"].isNull()) // does Json contain a reader?
801  {
802  // Placeholder to load reader
803  // TODO: need a createReader method for this and Clip JSON methods
804  }
805  }
806  catch (const std::exception& e)
807  {
808  // Error parsing JSON (or missing keys)
809  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
810  }
811 }
812 
813 // Load Json::Value into this object
814 void FrameMapper::SetJsonValue(const Json::Value root) {
815 
816  // Set parent data
818 }
819 
820 // Change frame rate or audio mapping details
821 void FrameMapper::ChangeMapping(Fraction target_fps, PulldownType target_pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout)
822 {
824  "FrameMapper::ChangeMapping",
825  "target_fps.num", target_fps.num,
826  "target_fps.den", target_fps.den,
827  "target_pulldown", target_pulldown,
828  "target_sample_rate", target_sample_rate,
829  "target_channels", target_channels,
830  "target_channel_layout", target_channel_layout);
831 
832  // Mark as dirty
833  is_dirty = true;
834 
835  // Update mapping details
836  target.num = target_fps.num;
837  target.den = target_fps.den;
838  info.fps.num = target_fps.num;
839  info.fps.den = target_fps.den;
840  info.video_timebase.num = target_fps.den;
841  info.video_timebase.den = target_fps.num;
843  pulldown = target_pulldown;
844  info.sample_rate = target_sample_rate;
845  info.channels = target_channels;
846  info.channel_layout = target_channel_layout;
847 
848  // Enable/Disable audio (based on settings)
850 
851  // Clear cache
852  final_cache.Clear();
853 
854  // Adjust cache size based on size of frame and audio
855  const int reset_cache_frames = std::max(Settings::Instance()->CACHE_MIN_FRAMES, OPEN_MP_NUM_PROCESSORS * 4);
856  final_cache.SetMaxBytesFromInfo(reset_cache_frames, info.width, info.height, info.sample_rate, info.channels);
857 
858  // Deallocate resample buffer
859  if (avr) {
860  SWR_CLOSE(avr);
861  SWR_FREE(&avr);
862  avr = NULL;
863  }
864 }
865 
866 // Resample audio and map channels (if needed)
867 void FrameMapper::ResampleMappedAudio(std::shared_ptr<Frame> frame, int64_t original_frame_number)
868 {
869  // Check if mappings are dirty (and need to be recalculated)
870  if (is_dirty)
871  // Recalculate mappings
872  Init();
873 
874  // Init audio buffers / variables
875  int total_frame_samples = 0;
876  int channels_in_frame = frame->GetAudioChannelsCount();
877  int sample_rate_in_frame = frame->SampleRate();
878  int samples_in_frame = frame->GetAudioSamplesCount();
879  ChannelLayout channel_layout_in_frame = frame->ChannelsLayout();
880 
882  "FrameMapper::ResampleMappedAudio",
883  "frame->number", frame->number,
884  "original_frame_number", original_frame_number,
885  "channels_in_frame", channels_in_frame,
886  "samples_in_frame", samples_in_frame,
887  "sample_rate_in_frame", sample_rate_in_frame);
888 
889  // Get audio sample array
890  float* frame_samples_float = NULL;
891  // Get samples interleaved together (c1 c2 c1 c2 c1 c2)
892  frame_samples_float = frame->GetInterleavedAudioSamples(&samples_in_frame);
893 
894  // Calculate total samples
895  total_frame_samples = samples_in_frame * channels_in_frame;
896 
897  // Create a new array (to hold all S16 audio samples for the current queued frames)
898  int16_t* frame_samples = (int16_t*) av_malloc(sizeof(int16_t)*total_frame_samples);
899 
900  // Translate audio sample values back to 16 bit integers with saturation
901  float valF;
902  int16_t conv;
903  const int16_t max16 = 32767;
904  const int16_t min16 = -32768;
905  for (int s = 0; s < total_frame_samples; s++) {
906  valF = frame_samples_float[s] * (1 << 15);
907  if (valF > max16)
908  conv = max16;
909  else if (valF < min16)
910  conv = min16;
911  else
912  conv = int(valF + 32768.5) - 32768; // +0.5 is for rounding
913 
914  // Copy into buffer
915  frame_samples[s] = conv;
916  }
917 
918  // Deallocate float array
919  delete[] frame_samples_float;
920  frame_samples_float = NULL;
921 
922  // Create input frame (and allocate arrays)
923  AVFrame *audio_frame = AV_ALLOCATE_FRAME();
924  AV_RESET_FRAME(audio_frame);
925  audio_frame->nb_samples = total_frame_samples / channels_in_frame;
926 
927  int buf_size = audio_frame->nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * channels_in_frame;
928  int error_code = avcodec_fill_audio_frame(
929  audio_frame, channels_in_frame, AV_SAMPLE_FMT_S16,
930  (uint8_t *) frame_samples, buf_size, 1);
931 
932  if (error_code < 0)
933  {
935  "FrameMapper::ResampleMappedAudio ERROR [" + av_err2string(error_code) + "]",
936  "error_code", error_code);
937  throw ErrorEncodingVideo("Error while resampling audio in frame mapper", frame->number);
938  }
939 
940  // Update total samples & input frame size (due to bigger or smaller data types)
941  total_frame_samples = Frame::GetSamplesPerFrame(AdjustFrameNumber(frame->number), target, info.sample_rate, info.channels);
942 
943  // Create output frame (and allocate arrays)
944  AVFrame *audio_converted = AV_ALLOCATE_FRAME();
945  AV_RESET_FRAME(audio_converted);
946  audio_converted->nb_samples = total_frame_samples;
947  av_samples_alloc(audio_converted->data, audio_converted->linesize, info.channels, total_frame_samples, AV_SAMPLE_FMT_S16, 0);
948 
949  int nb_samples = 0;
950 
951  // setup resample context
952  if (!avr) {
953  avr = SWR_ALLOC();
954  av_opt_set_int(avr, "in_channel_layout", channel_layout_in_frame, 0);
955  av_opt_set_int(avr, "out_channel_layout", info.channel_layout, 0);
956  av_opt_set_int(avr, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
957  av_opt_set_int(avr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
958  av_opt_set_int(avr, "in_sample_rate", sample_rate_in_frame, 0);
959  av_opt_set_int(avr, "out_sample_rate", info.sample_rate, 0);
960  av_opt_set_int(avr, "in_channels", channels_in_frame, 0);
961  av_opt_set_int(avr, "out_channels", info.channels, 0);
962  SWR_INIT(avr);
963  }
964 
965  // Convert audio samples
966  nb_samples = SWR_CONVERT(avr, // audio resample context
967  audio_converted->data, // output data pointers
968  audio_converted->linesize[0], // output plane size, in bytes. (0 if unknown)
969  audio_converted->nb_samples, // maximum number of samples that the output buffer can hold
970  audio_frame->data, // input data pointers
971  audio_frame->linesize[0], // input plane size, in bytes (0 if unknown)
972  audio_frame->nb_samples); // number of input samples to convert
973 
974  // Create a new array (to hold all resampled S16 audio samples)
975  int16_t* resampled_samples = new int16_t[(nb_samples * info.channels)];
976 
977  // Copy audio samples over original samples
978  memcpy(resampled_samples, audio_converted->data[0], (nb_samples * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16) * info.channels));
979 
980  // Free frames
981  av_freep(&audio_frame->data[0]);
982  AV_FREE_FRAME(&audio_frame);
983  av_freep(&audio_converted->data[0]);
984  AV_FREE_FRAME(&audio_converted);
985  frame_samples = NULL;
986 
987  // Resize the frame to hold the right # of channels and samples
988  int channel_buffer_size = nb_samples;
989  frame->ResizeAudio(info.channels, channel_buffer_size, info.sample_rate, info.channel_layout);
990 
992  "FrameMapper::ResampleMappedAudio (Audio successfully resampled)",
993  "nb_samples", nb_samples,
994  "total_frame_samples", total_frame_samples,
995  "info.sample_rate", info.sample_rate,
996  "channels_in_frame", channels_in_frame,
997  "info.channels", info.channels,
998  "info.channel_layout", info.channel_layout);
999 
1000  // Array of floats (to hold samples for each channel)
1001  float *channel_buffer = new float[channel_buffer_size];
1002 
1003  // Divide audio into channels. Loop through each channel
1004  for (int channel_filter = 0; channel_filter < info.channels; channel_filter++)
1005  {
1006  // Init array
1007  for (int z = 0; z < channel_buffer_size; z++)
1008  channel_buffer[z] = 0.0f;
1009 
1010  // Loop through all samples and add them to our Frame based on channel.
1011  // Toggle through each channel number, since channel data is stored like (left right left right)
1012  int channel = 0;
1013  int position = 0;
1014  for (int sample = 0; sample < (nb_samples * info.channels); sample++)
1015  {
1016  // Only add samples for current channel
1017  if (channel_filter == channel)
1018  {
1019  // Add sample (convert from (-32768 to 32768) to (-1.0 to 1.0))
1020  channel_buffer[position] = resampled_samples[sample] * (1.0f / (1 << 15));
1021 
1022  // Increment audio position
1023  position++;
1024  }
1025 
1026  // increment channel (if needed)
1027  if ((channel + 1) < info.channels)
1028  // move to next channel
1029  channel ++;
1030  else
1031  // reset channel
1032  channel = 0;
1033  }
1034 
1035  // Add samples to frame for this channel
1036  frame->AddAudio(true, channel_filter, 0, channel_buffer, position, 1.0f);
1037  }
1038 
1039  // Update frame's audio meta data
1040  frame->SampleRate(info.sample_rate);
1041  frame->ChannelsLayout(info.channel_layout);
1042 
1043  // clear channel buffer
1044  delete[] channel_buffer;
1045  channel_buffer = NULL;
1046 
1047  // Delete arrays
1048  delete[] resampled_samples;
1049  resampled_samples = NULL;
1050 
1051  // Keep track of last resampled frame
1052  previous_frame = frame->number;
1053 }
1054 
1055 // Adjust frame number for Clip position and start (which can result in a different number)
1056 int64_t FrameMapper::AdjustFrameNumber(int64_t clip_frame_number) {
1057 
1058  // Get clip position from parent clip (if any)
1059  float position = 0.0;
1060  float start = 0.0;
1061  Clip *parent = static_cast<Clip *>(ParentClip());
1062  if (parent) {
1063  position = parent->Position();
1064  start = parent->Start();
1065  }
1066 
1067  // Adjust start frame and position based on parent clip.
1068  // This ensures the same frame # is used by mapped readers and clips,
1069  // when calculating samples per frame.
1070  // Thus, this prevents gaps and mismatches in # of samples.
1071  int64_t clip_start_frame = (start * info.fps.ToDouble()) + 1;
1072  int64_t clip_start_position = round(position * info.fps.ToDouble()) + 1;
1073  int64_t frame_number = clip_frame_number + clip_start_position - clip_start_frame;
1074 
1075  return frame_number;
1076 }
1077 
1078 // Set direction hint for the next call to GetFrame
1079 void FrameMapper::SetDirectionHint(const bool increasing)
1080 {
1081  const std::lock_guard<std::recursive_mutex> lock(directionMutex);
1082  hint_increasing = increasing;
1083  have_hint = true;
1084 }
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::FrameMapper::ChangeMapping
void ChangeMapping(Fraction target_fps, PulldownType pulldown, int target_sample_rate, int target_channels, ChannelLayout target_channel_layout)
Change frame rate or audio mapping details.
Definition: FrameMapper.cpp:821
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::FrameMapper::fields
std::vector< Field > fields
Definition: FrameMapper.h:239
openshot::Fraction::ToFloat
float ToFloat()
Return this fraction as a float (i.e. 1/2 = 0.5)
Definition: Fraction.cpp:35
openshot::ReaderBase::JsonValue
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
Definition: ReaderBase.cpp:106
openshot::FrameMapper::ResampleMappedAudio
void ResampleMappedAudio(std::shared_ptr< Frame > frame, int64_t original_frame_number)
Resample audio and map channels (if needed)
Definition: FrameMapper.cpp:867
Clip.h
Header file for Clip class.
openshot::CacheMemory::GetFrame
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number)
Get a frame from the cache.
Definition: CacheMemory.cpp:84
openshot::Fraction::ToInt
int ToInt()
Return a rounded integer of the fraction (for example 30000/1001 returns 30)
Definition: Fraction.cpp:45
openshot::CacheMemory::Add
void Add(std::shared_ptr< openshot::Frame > frame)
Add a Frame to the cache.
Definition: CacheMemory.cpp:47
openshot::Field::isOdd
bool isOdd
Definition: FrameMapper.h:58
openshot::ReaderBase::GetFrame
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
AV_ALLOCATE_FRAME
#define AV_ALLOCATE_FRAME()
Definition: FFmpegUtilities.h:203
openshot::FrameMapper::PrintMapping
void PrintMapping(std::ostream *out=&std::cout)
Print all of the original frames and which new frames they map to.
Definition: FrameMapper.cpp:692
openshot::ReaderBase::SetJsonValue
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
Definition: ReaderBase.cpp:157
SWR_CONVERT
#define SWR_CONVERT(ctx, out, linesize, out_count, in, linesize2, in_count)
Definition: FFmpegUtilities.h:149
openshot::SampleRange::frame_end
int64_t frame_end
Definition: FrameMapper.h:72
openshot
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:28
openshot::Clip
This class represents a clip (used to arrange readers on the timeline)
Definition: Clip.h:89
openshot::Fraction
This class represents a fraction.
Definition: Fraction.h:30
AV_FREE_FRAME
#define AV_FREE_FRAME(av_frame)
Definition: FFmpegUtilities.h:207
openshot::Field
This struct holds a single field (half a frame).
Definition: FrameMapper.h:55
MemoryTrim.h
Cross-platform helper to encourage returning freed memory to the OS.
openshot::ReaderBase::info
openshot::ReaderInfo info
Information about the current media file.
Definition: ReaderBase.h:88
openshot::ReaderInfo::duration
float duration
Length of time (in seconds)
Definition: ReaderBase.h:43
openshot::FrameMapper::Json
std::string Json() const override
Generate JSON string of this object.
Definition: FrameMapper.cpp:770
openshot::ReaderInfo::has_video
bool has_video
Determines if this file has a video stream.
Definition: ReaderBase.h:40
openshot::ReaderInfo::width
int width
The width of the video (in pixesl)
Definition: ReaderBase.h:46
openshot::ClipBase::Position
void Position(float value)
Set the Id of this clip object
Definition: ClipBase.cpp:19
openshot::SampleRange::sample_start
int sample_start
Definition: FrameMapper.h:70
openshot::MappedFrame::Even
Field Even
Definition: FrameMapper.h:167
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
FrameMapper.h
Header file for the FrameMapper class.
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::FrameMapper::SetJsonValue
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Definition: FrameMapper.cpp:814
openshot::ReaderInfo::video_length
int64_t video_length
The number of frames in the video stream.
Definition: ReaderBase.h:53
openshot::ReaderInfo::height
int height
The height of the video (in pixels)
Definition: ReaderBase.h:45
openshot::Fraction::num
int num
Numerator for the fraction.
Definition: Fraction.h:32
openshot::FrameMapper::Open
void Open() override
Open the internal reader.
Definition: FrameMapper.cpp:726
ZmqLogger.h
Header file for ZeroMQ-based Logger class.
openshot::ErrorEncodingVideo
Exception when encoding audio packet.
Definition: Exceptions.h:148
openshot::Fraction::den
int den
Denominator for the fraction.
Definition: Fraction.h:33
OPEN_MP_NUM_PROCESSORS
#define OPEN_MP_NUM_PROCESSORS
Definition: OpenMPUtilities.h:23
openshot::SampleRange::frame_start
int64_t frame_start
Definition: FrameMapper.h:69
AV_RESET_FRAME
#define AV_RESET_FRAME(av_frame)
Definition: FFmpegUtilities.h:206
openshot::FrameMapper::~FrameMapper
virtual ~FrameMapper()
Destructor.
Definition: FrameMapper.cpp:58
openshot::FrameMapper::GetFrame
std::shared_ptr< Frame > GetFrame(int64_t requested_frame) override
This method is required for all derived classes of ReaderBase, and return the openshot::Frame object,...
Definition: FrameMapper.cpp:434
SWR_CLOSE
#define SWR_CLOSE(ctx)
Definition: FFmpegUtilities.h:152
openshot::ReaderBase::Open
virtual void Open()=0
Open the reader (and start consuming resources, such as images or video files)
openshot::ReaderInfo::has_audio
bool has_audio
Determines if this file has an audio stream.
Definition: ReaderBase.h:41
openshot::ReaderBase::IsOpen
virtual bool IsOpen()=0
Determine if reader is open or closed.
openshot::FrameMapper::frames
std::vector< MappedFrame > frames
Definition: FrameMapper.h:240
openshot::FrameMapper::Close
void Close() override
Close the openshot::FrameMapper and internal reader.
Definition: FrameMapper.cpp:738
openshot::InvalidJSON
Exception for invalid JSON.
Definition: Exceptions.h:223
openshot::MappedFrame::Odd
Field Odd
Definition: FrameMapper.h:166
openshot::FrameMapper::IsOpen
bool IsOpen() override
Determine if reader is open or closed.
Definition: FrameMapper.cpp:718
openshot::MappedFrame::Samples
SampleRange Samples
Definition: FrameMapper.h:168
openshot::MappedFrame
This struct holds two fields which together make up a complete video frame.
Definition: FrameMapper.h:164
SWR_INIT
#define SWR_INIT(ctx)
Definition: FFmpegUtilities.h:154
openshot::ReaderInfo::has_single_image
bool has_single_image
Determines if this file only contains a single image.
Definition: ReaderBase.h:42
openshot::SampleRange
This struct holds a the range of samples needed by this frame.
Definition: FrameMapper.h:67
openshot::ReaderInfo::video_timebase
openshot::Fraction video_timebase
The video timebase determines how long each frame stays on the screen.
Definition: ReaderBase.h:55
openshot::Settings::Instance
static Settings * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
Definition: Settings.cpp:23
openshot::ClipBase::Start
void Start(float value)
Set start position (in seconds) of clip (trim start of video)
Definition: ClipBase.cpp:42
openshot::SampleRange::total
int total
Definition: FrameMapper.h:155
openshot::Frame::GetSamplesPerFrame
int GetSamplesPerFrame(openshot::Fraction fps, int sample_rate, int channels)
Calculate the # of samples per video frame (for the current frame number)
Definition: Frame.cpp:484
openshot::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::FrameMapper::JsonValue
Json::Value JsonValue() const override
Generate Json::Value for this object.
Definition: FrameMapper.cpp:777
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::PULLDOWN_ADVANCED
@ PULLDOWN_ADVANCED
Advanced 2:3:3:2 pull-down (minimal dirty frames)
Definition: FrameMapper.h:45
openshot::FrameMapper::GetMappedFrame
MappedFrame GetMappedFrame(int64_t TargetFrameNumber)
Get a frame based on the target frame rate and the new frame number of a frame.
Definition: FrameMapper.cpp:350
openshot::SampleRange::Shift
void Shift(int64_t samples, openshot::Fraction fps, int sample_rate, int channels, bool right_side)
Definition: FrameMapper.h:140
openshot::SampleRange::sample_end
int sample_end
Definition: FrameMapper.h:73
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
SWR_FREE
#define SWR_FREE(ctx)
Definition: FFmpegUtilities.h:153
openshot::ReaderInfo::fps
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition: ReaderBase.h:48
openshot::ReaderBase
This abstract class is the base class, used by all readers in libopenshot.
Definition: ReaderBase.h:75
openshot::PulldownType
PulldownType
This enumeration determines how frame rates are increased or decreased.
Definition: FrameMapper.h:42
openshot::ReaderBase::Close
virtual void Close()=0
Close the reader (and any resources it was consuming)
openshot::ChannelLayout
ChannelLayout
This enumeration determines the audio channel layout (such as stereo, mono, 5 point surround,...
Definition: ChannelLayouts.h:28
SWR_ALLOC
#define SWR_ALLOC()
Definition: FFmpegUtilities.h:151
openshot::FrameMapper::SetDirectionHint
void SetDirectionHint(const bool increasing)
Set time-curve informed direction hint (from Clip class) for the next call to GetFrame.
Definition: FrameMapper.cpp:1079
openshot::FrameMapper::Reader
ReaderBase * Reader()
Get the current reader.
Definition: FrameMapper.cpp:67
openshot::Field::Frame
int64_t Frame
Definition: FrameMapper.h:57
openshot::PULLDOWN_NONE
@ PULLDOWN_NONE
Do not apply pull-down techniques, just repeat or skip entire frames.
Definition: FrameMapper.h:46
openshot::ReaderInfo::channels
int channels
The number of audio channels used in the audio stream.
Definition: ReaderBase.h:61
Exceptions.h
Header file for all Exception classes.
openshot::FrameMapper::SetJson
void SetJson(const std::string value) override
Load JSON string into this object.
Definition: FrameMapper.cpp:791
openshot::SampleRange::Extend
void Extend(int64_t samples, openshot::Fraction fps, int sample_rate, int channels, bool right_side)
Extend SampleRange on either side.
Definition: FrameMapper.h:76
openshot::PULLDOWN_CLASSIC
@ PULLDOWN_CLASSIC
Classic 2:3:2:3 pull-down.
Definition: FrameMapper.h:44
openshot::ReaderBase::getFrameMutex
std::recursive_mutex getFrameMutex
Mutex for multiple threads.
Definition: ReaderBase.h:79
openshot::ReaderBase::ParentClip
openshot::ClipBase * ParentClip()
Parent clip object of this reader (which can be unparented and NULL)
Definition: ReaderBase.cpp:240