OpenShot Library | libopenshot  0.7.0
Frame.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 <thread> // for std::this_thread::sleep_for
14 #include <chrono> // for std::chrono::milliseconds
15 #include <iomanip>
16 #include <limits>
17 
18 #include "Frame.h"
19 #include "AudioBufferSource.h"
20 #include "AudioResampler.h"
21 #include "QtUtilities.h"
22 
23 #include <AppConfig.h>
24 #include <juce_audio_basics/juce_audio_basics.h>
25 #include <juce_audio_devices/juce_audio_devices.h>
26 
27 #include <QApplication>
28 #include <QImage>
29 #include <QPixmap>
30 #include <QBitmap>
31 #include <QColor>
32 #include <QString>
33 #include <QVector>
34 #include <QPainter>
35 #include <QHBoxLayout>
36 #include <QWidget>
37 #include <QLabel>
38 #include <QPointF>
39 #include <QWidget>
40 
41 using namespace std;
42 using namespace openshot;
43 
44 // Constructor - image & audio
45 Frame::Frame(int64_t number, int width, int height, std::string color, int samples, int channels)
46  : audio(std::make_shared<juce::AudioBuffer<float>>(channels, samples)),
47  number(number), capture_timestamp(std::numeric_limits<double>::quiet_NaN()), width(width), height(height),
48  pixel_ratio(1,1), color(color),
49  channels(channels), channel_layout(LAYOUT_STEREO),
50  sample_rate(44100),
51  has_audio_data(false), has_image_data(false),
52  max_audio_sample(0), audio_is_increasing(true)
53 {
54  // zero (fill with silence) the audio buffer
55  audio->clear();
56 }
57 
58 // Delegating Constructor - blank frame
59 Frame::Frame() : Frame::Frame(1, 1, 1, "#000000", 0, 2) {}
60 
61 // Delegating Constructor - image only
62 Frame::Frame(int64_t number, int width, int height, std::string color)
63  : Frame::Frame(number, width, height, color, 0, 2) {}
64 
65 // Delegating Constructor - audio only
66 Frame::Frame(int64_t number, int samples, int channels)
67  : Frame::Frame(number, 1, 1, "#000000", samples, channels) {}
68 
69 
70 // Copy constructor
71 Frame::Frame ( const Frame &other )
72 {
73  // copy pointers and data
74  DeepCopy(other);
75 }
76 
77 // Assignment operator
79 {
80  // copy pointers and data
81  DeepCopy(other);
82 
83  return *this;
84 }
85 
86 // Copy data and pointers from another Frame instance
87 void Frame::DeepCopy(const Frame& other)
88 {
89  number = other.number;
91  channels = other.channels;
92  width = other.width;
93  height = other.height;
94  channel_layout = other.channel_layout;
97  sample_rate = other.sample_rate;
98  pixel_ratio = Fraction(other.pixel_ratio.num, other.pixel_ratio.den);
99  color = other.color;
100  max_audio_sample = other.max_audio_sample;
101  audio_is_increasing = other.audio_is_increasing;
102 
103  if (other.image)
104  image = std::make_shared<QImage>(*(other.image));
105  if (other.audio)
106  audio = std::make_shared<juce::AudioBuffer<float>>(*(other.audio));
107  if (other.wave_image)
108  wave_image = std::make_shared<QImage>(*(other.wave_image));
109 }
110 
111 // Destructor
113  // Clear all pointers
114  image.reset();
115  audio.reset();
116  #ifdef USE_OPENCV
117  imagecv.release();
118  #endif
119 }
120 
121 // Display the frame image to the screen (primarily used for debugging reasons)
123 {
124  if (!QApplication::instance()) {
125  // Only create the QApplication once
126  static int argc = 1;
127  static char* argv[1] = {NULL};
128  previewApp = std::make_shared<QApplication>(argc, argv);
129  }
130 
131  // Get preview image
132  std::shared_ptr<QImage> previewImage = GetImage();
133 
134  // Update the image to reflect the correct pixel aspect ration (i.e. to fix non-square pixels)
135  if (pixel_ratio.num != 1 || pixel_ratio.den != 1)
136  {
137  // Resize to fix DAR
138  previewImage = std::make_shared<QImage>(previewImage->scaled(
139  previewImage->size().width(), previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble(),
140  Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
141  }
142 
143  // Create window
144  QWidget previewWindow;
145  previewWindow.setStyleSheet("background-color: #000000;");
146  QHBoxLayout layout;
147 
148  // Create label with current frame's image
149  QLabel previewLabel;
150  previewLabel.setPixmap(QPixmap::fromImage(*previewImage));
151  previewLabel.setMask(QPixmap::fromImage(*previewImage).mask());
152  layout.addWidget(&previewLabel);
153 
154  // Show the window
155  previewWindow.setLayout(&layout);
156  previewWindow.show();
157  previewApp->exec();
158 }
159 
160 // Get an audio waveform image
161 std::shared_ptr<QImage> Frame::GetWaveform(int width, int height, int Red, int Green, int Blue, int Alpha)
162 {
163  // Clear any existing waveform image
164  ClearWaveform();
165 
166  // Init a list of lines
167  QVector<QPointF> lines;
168  QVector<QPointF> labels;
169 
170  // Calculate width of an image based on the # of samples
171  int total_samples = GetAudioSamplesCount();
172  if (total_samples > 0)
173  {
174  // If samples are present...
175  int new_height = 200 * audio->getNumChannels();
176  int height_padding = 20 * (audio->getNumChannels() - 1);
177  int total_height = new_height + height_padding;
178  int total_width = 0;
179  float zero_height = 1.0; // Used to clamp near-zero vales to this value to prevent gaps
180 
181  // Loop through each audio channel
182  float Y = 100.0;
183  for (int channel = 0; channel < audio->getNumChannels(); channel++)
184  {
185  float X = 0.0;
186 
187  // Get audio for this channel
188  const float *samples = audio->getReadPointer(channel);
189 
190  for (int sample = 0; sample < GetAudioSamplesCount(); sample++, X++)
191  {
192  // Sample value (scaled to -100 to 100)
193  float value = samples[sample] * 100.0;
194 
195  // Set threshold near zero (so we don't allow near-zero values)
196  // This prevents empty gaps from appearing in the waveform
197  if (value > -zero_height && value < 0.0) {
198  value = -zero_height;
199  } else if (value > 0.0 && value < zero_height) {
200  value = zero_height;
201  }
202 
203  // Append a line segment for each sample
204  lines.push_back(QPointF(X, Y));
205  lines.push_back(QPointF(X, Y - value));
206  }
207 
208  // Add Channel Label Coordinate
209  labels.push_back(QPointF(5.0, Y - 5.0));
210 
211  // Increment Y
212  Y += (200 + height_padding);
213  total_width = X;
214  }
215 
216  // Create blank image
217  wave_image = std::make_shared<QImage>(
218  total_width, total_height, QImage::Format_RGBA8888_Premultiplied);
219  wave_image->fill(QColor(0,0,0,0));
220 
221  // Load QPainter with wave_image device
222  QPainter painter(wave_image.get());
223 
224  // Set pen color
225  QPen pen;
226  pen.setColor(QColor(Red, Green, Blue, Alpha));
227  pen.setWidthF(1.0);
228  pen.setStyle(Qt::SolidLine);
229  painter.setPen(pen);
230 
231  // Draw the waveform
232  painter.drawLines(lines);
233  painter.end();
234  }
235  else
236  {
237  // No audio samples present
238  wave_image = std::make_shared<QImage>(width, height, QImage::Format_RGBA8888_Premultiplied);
239  wave_image->fill(QColor(QString::fromStdString("#000000")));
240  }
241 
242  // Resize Image (if needed)
243  if (wave_image->width() != width || wave_image->height() != height) {
244  QImage scaled_wave_image = wave_image->scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
245  wave_image = std::make_shared<QImage>(scaled_wave_image);
246  }
247 
248  // Return new image
249  return wave_image;
250 }
251 
252 // Clear the waveform image (and deallocate its memory)
254 {
255  if (wave_image)
256  wave_image.reset();
257 }
258 
259 // Get an audio waveform image pixels
260 const unsigned char* Frame::GetWaveformPixels(int width, int height, int Red, int Green, int Blue, int Alpha)
261 {
262  // Get audio wave form image
263  wave_image = GetWaveform(width, height, Red, Green, Blue, Alpha);
264 
265  // Return array of pixel packets
266  return wave_image->constBits();
267 }
268 
269 // Display the wave form
271 {
272  // Get audio wave form image
273  GetWaveform(720, 480, 0, 123, 255, 255);
274 
275  if (!QApplication::instance()) {
276  // Only create the QApplication once
277  static int argc = 1;
278  static char* argv[1] = {NULL};
279  previewApp = std::make_shared<QApplication>(argc, argv);
280  }
281 
282  // Create window
283  QWidget previewWindow;
284  previewWindow.setStyleSheet("background-color: #000000;");
285  QHBoxLayout layout;
286 
287  // Create label with current frame's waveform image
288  QLabel previewLabel;
289  previewLabel.setPixmap(QPixmap::fromImage(*wave_image));
290  previewLabel.setMask(QPixmap::fromImage(*wave_image).mask());
291  layout.addWidget(&previewLabel);
292 
293  // Show the window
294  previewWindow.setLayout(&layout);
295  previewWindow.show();
296  previewApp->exec();
297 
298  // Deallocate waveform image
299  ClearWaveform();
300 }
301 
302 // Get magnitude of range of samples (if channel is -1, return average of all channels for that sample)
303 float Frame::GetAudioSample(int channel, int sample, int magnitude_range)
304 {
305  if (channel > 0) {
306  // return average magnitude for a specific channel/sample range
307  return audio->getMagnitude(channel, sample, magnitude_range);
308 
309  } else {
310  // Return average magnitude for all channels
311  return audio->getMagnitude(sample, magnitude_range);
312  }
313 }
314 
315 // Get an array of sample data (and optional reverse the sample values)
316 float* Frame::GetAudioSamples(int channel) {
317 
318  // Copy audio data
319  juce::AudioBuffer<float> *buffer(audio.get());
320 
321  // return JUCE audio data for this channel
322  return buffer->getWritePointer(channel);
323 }
324 
325 // Get an array of sample data (all channels interleaved together), using any sample rate
326 float* Frame::GetInterleavedAudioSamples(int* sample_count)
327 {
328  // Copy audio data
329  juce::AudioBuffer<float> *buffer(audio.get());
330 
331  float *output = NULL;
332  int num_of_channels = audio->getNumChannels();
333  int num_of_samples = GetAudioSamplesCount();
334 
335  // INTERLEAVE all samples together (channel 1 + channel 2 + channel 1 + channel 2, etc...)
336  output = new float[num_of_channels * num_of_samples];
337  int position = 0;
338 
339  // Loop through samples in each channel (combining them)
340  for (int sample = 0; sample < num_of_samples; sample++)
341  {
342  for (int channel = 0; channel < num_of_channels; channel++)
343  {
344  // Add sample to output array
345  output[position] = buffer->getReadPointer(channel)[sample];
346 
347  // increment position
348  position++;
349  }
350  }
351 
352  // Update sample count (since it might have changed due to resampling)
353  *sample_count = num_of_samples;
354 
355  // return combined array
356  return output;
357 }
358 
359 // Get number of audio channels
361 {
362  const std::lock_guard<std::recursive_mutex> lock(addingAudioMutex);
363  if (audio)
364  return audio->getNumChannels();
365  else
366  return 0;
367 }
368 
369 // Get number of audio samples
371 {
372  const std::lock_guard<std::recursive_mutex> lock(addingAudioMutex);
373  return max_audio_sample;
374 }
375 
377 {
378  return audio.get();
379 }
380 
381 // Get the size in bytes of this frame (rough estimate)
383 {
384  int64_t total_bytes = 0;
385  if (image) {
386  total_bytes += static_cast<int64_t>(
387  width * height * sizeof(char) * 4);
388  }
389  if (audio) {
390  // approximate audio size (sample rate / 24 fps)
391  total_bytes += (sample_rate / 24.0) * sizeof(float);
392  }
393 
394  // return size of this frame
395  return total_bytes;
396 }
397 
398 // Get pixel data (as packets)
399 const unsigned char* Frame::GetPixels()
400 {
401  // Check for blank image
402  if (!image)
403  // Fill with black
404  AddColor(width, height, color);
405 
406  // Return array of pixel packets
407  return image->constBits();
408 }
409 
410 // Get pixel data (for only a single scan-line)
411 const unsigned char* Frame::GetPixels(int row)
412 {
413  // Check for blank image
414  if (!image)
415  // Fill with black
416  AddColor(width, height, color);
417 
418  // Return array of pixel packets
419  return image->constScanLine(row);
420 }
421 
422 // Check a specific pixel color value (returns True/False)
423 bool Frame::CheckPixel(int row, int col, int red, int green, int blue, int alpha, int threshold) {
424  int col_pos = col * 4; // Find column array position
425  if (!image || row < 0 || row >= (height - 1) ||
426  col_pos < 0 || col_pos >= (width - 1) ) {
427  // invalid row / col
428  return false;
429  }
430  // Check pixel color
431  const unsigned char* pixels = GetPixels(row);
432  if (pixels[col_pos + 0] >= (red - threshold) && pixels[col_pos + 0] <= (red + threshold) &&
433  pixels[col_pos + 1] >= (green - threshold) && pixels[col_pos + 1] <= (green + threshold) &&
434  pixels[col_pos + 2] >= (blue - threshold) && pixels[col_pos + 2] <= (blue + threshold) &&
435  pixels[col_pos + 3] >= (alpha - threshold) && pixels[col_pos + 3] <= (alpha + threshold)) {
436  // Pixel color matches successfully
437  return true;
438  } else {
439  // Pixel color does not match
440  return false;
441  }
442 }
443 
444 // Set Pixel Aspect Ratio
445 void Frame::SetPixelRatio(int num, int den)
446 {
447  pixel_ratio.num = num;
448  pixel_ratio.den = den;
449 }
450 
451 // Set frame number
452 void Frame::SetFrameNumber(int64_t new_number)
453 {
454  number = new_number;
455 }
456 
457 // Calculate the # of samples per video frame (for a specific frame number and frame rate)
458 int Frame::GetSamplesPerFrame(int64_t number, Fraction fps, int sample_rate, int channels)
459 {
460  // Directly return 0 for invalid audio/frame-rate parameters
461  // so that we do not need to deal with NaNs later
462  if (channels <= 0 || sample_rate <= 0 || fps.num <= 0 || fps.den <= 0) return 0;
463 
464  // Get the total # of samples for the previous frame, and the current frame (rounded)
465  double fps_rate = fps.Reciprocal().ToDouble();
466 
467  // Determine previous samples total, and make sure it's evenly divisible by the # of channels
468  double previous_samples = (sample_rate * fps_rate) * (number - 1);
469  double previous_samples_remainder = fmod(previous_samples, (double)channels); // subtract the remainder to the total (to make it evenly divisible)
470  previous_samples -= previous_samples_remainder;
471 
472  // Determine the current samples total, and make sure it's evenly divisible by the # of channels
473  double total_samples = (sample_rate * fps_rate) * number;
474  double total_samples_remainder = fmod(total_samples, (double)channels); // subtract the remainder to the total (to make it evenly divisible)
475  total_samples -= total_samples_remainder;
476 
477  // Subtract the previous frame's total samples with this frame's total samples. Not all sample rates can
478  // be evenly divided into frames, so each frame can have have different # of samples.
479  int samples_per_frame = round(total_samples - previous_samples);
480  if (samples_per_frame < 0)
481  samples_per_frame = 0;
482  return samples_per_frame;
483 }
484 
485 // Calculate the # of samples per video frame (for the current frame number)
486 int Frame::GetSamplesPerFrame(Fraction fps, int sample_rate, int channels)
487 {
488  return GetSamplesPerFrame(number, fps, sample_rate, channels);
489 }
490 
491 // Get height of image
493 {
494  return height;
495 }
496 
497 // Get height of image
499 {
500  return width;
501 }
502 
503 // Get the original sample rate of this frame's audio data
505 {
506  return sample_rate;
507 }
508 
509 // Get the original sample rate of this frame's audio data
511 {
512  return channel_layout;
513 }
514 
515 
516 // Save the frame image to the specified path. The image format is determined from the extension (i.e. image.PNG, image.JPEG)
517 void Frame::Save(std::string path, float scale, std::string format, int quality)
518 {
519  // Get preview image
520  std::shared_ptr<QImage> previewImage = GetImage();
521 
522  // Update the image to reflect the correct pixel aspect ration (i.e. to fix non-square pixels)
523  if (pixel_ratio.num != 1 || pixel_ratio.den != 1)
524  {
525  // Resize to fix DAR
526  previewImage = std::make_shared<QImage>(previewImage->scaled(
527  previewImage->size().width(), previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble(),
528  Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
529  }
530 
531  // scale image if needed
532  if (fabs(scale) > 1.001 || fabs(scale) < 0.999)
533  {
534  // Resize image
535  previewImage = std::make_shared<QImage>(previewImage->scaled(
536  previewImage->size().width() * scale, previewImage->size().height() * scale,
537  Qt::KeepAspectRatio, Qt::SmoothTransformation));
538  }
539 
540  // Save image
541  previewImage->save(QString::fromStdString(path), format.c_str(), quality);
542 }
543 
544 // Thumbnail the frame image to the specified path. The image format is determined from the extension (i.e. image.PNG, image.JPEG)
545 void Frame::Thumbnail(std::string path, int new_width, int new_height, std::string mask_path, std::string overlay_path,
546  std::string background_color, bool ignore_aspect, std::string format, int quality, float rotate, ScaleType scale_mode) {
547 
548  // Create blank thumbnail image & fill background color
549  auto thumbnail = std::make_shared<QImage>(
550  new_width, new_height, QImage::Format_RGBA8888_Premultiplied);
551  thumbnail->fill(QColor(QString::fromStdString(background_color)));
552 
553  // Create painter
554  QPainter painter(thumbnail.get());
555  painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing, true);
556 
557  // Get preview image
558  std::shared_ptr<QImage> previewImage = GetImage();
559 
560  // Update the image to reflect the correct pixel aspect ration (i.e. to fix non-squar pixels)
561  if (pixel_ratio.num != 1 || pixel_ratio.den != 1)
562  {
563  // Calculate correct DAR (display aspect ratio)
564  int aspect_width = previewImage->size().width();
565  int aspect_height = previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble();
566 
567  // Resize to fix DAR
568  previewImage = std::make_shared<QImage>(previewImage->scaled(
569  aspect_width, aspect_height,
570  Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
571  }
572 
573  // Resize frame image
574  Qt::AspectRatioMode aspect_ratio_mode = Qt::KeepAspectRatio;
575  if (ignore_aspect) {
576  aspect_ratio_mode = Qt::IgnoreAspectRatio;
577  } else {
578  switch (scale_mode) {
579  case SCALE_CROP:
580  aspect_ratio_mode = Qt::KeepAspectRatioByExpanding;
581  break;
582  case SCALE_STRETCH:
583  aspect_ratio_mode = Qt::IgnoreAspectRatio;
584  break;
585  case SCALE_FIT:
586  case SCALE_NONE:
587  default:
588  aspect_ratio_mode = Qt::KeepAspectRatio;
589  break;
590  }
591  }
592 
593  previewImage = std::make_shared<QImage>(previewImage->scaled(
594  new_width, new_height,
595  aspect_ratio_mode, Qt::SmoothTransformation));
596 
597  // Composite frame image onto background (centered)
598  int x = (new_width - previewImage->size().width()) / 2.0; // center
599  int y = (new_height - previewImage->size().height()) / 2.0; // center
600  painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
601 
602 
603  // Create transform and rotate (if needed)
604  QTransform transform;
605  float origin_x = previewImage->width() / 2.0;
606  float origin_y = previewImage->height() / 2.0;
607  transform.translate(origin_x, origin_y);
608  transform.rotate(rotate);
609  transform.translate(-origin_x,-origin_y);
610  painter.setTransform(transform);
611 
612  // Draw image onto QImage
613  painter.drawImage(x, y, *previewImage);
614 
615 
616  // Overlay Image (if any)
617  if (overlay_path != "") {
618  // Open overlay
619  auto overlay = std::make_shared<QImage>();
620  overlay->load(QString::fromStdString(overlay_path));
621 
622  // Set pixel format
623  overlay = std::make_shared<QImage>(
624  overlay->convertToFormat(QImage::Format_RGBA8888_Premultiplied));
625 
626  // Resize to fit
627  overlay = std::make_shared<QImage>(overlay->scaled(
628  new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
629 
630  // Composite onto thumbnail
631  painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
632  painter.drawImage(0, 0, *overlay);
633  }
634 
635 
636  // Mask Image (if any)
637  if (mask_path != "") {
638  // Open mask
639  auto mask = std::make_shared<QImage>();
640  mask->load(QString::fromStdString(mask_path));
641 
642  // Set pixel format
643  mask = std::make_shared<QImage>(
644  mask->convertToFormat(QImage::Format_RGBA8888_Premultiplied));
645 
646  // Resize to fit
647  mask = std::make_shared<QImage>(mask->scaled(
648  new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
649 
650  // Negate mask
651  mask->invertPixels();
652 
653  // Get pixels
654  unsigned char *pixels = static_cast<unsigned char *>(thumbnail->bits());
655  const unsigned char *mask_pixels = static_cast<const unsigned char *>(mask->constBits());
656 
657  // Convert the mask image to grayscale
658  // Loop through pixels
659  for (int pixel = 0, byte_index=0; pixel < new_width * new_height; pixel++, byte_index+=4)
660  {
661  // Get the RGB values from the pixel
662  int gray_value = qGray(mask_pixels[byte_index], mask_pixels[byte_index] + 1, mask_pixels[byte_index] + 2);
663  int Frame_Alpha = pixels[byte_index + 3];
664  int Mask_Value = constrain(Frame_Alpha - gray_value);
665 
666  // Set all alpha pixels to gray value
667  pixels[byte_index + 3] = Mask_Value;
668  }
669  }
670 
671 
672  // End painter
673  painter.end();
674 
675  // Save image
676  thumbnail->save(QString::fromStdString(path), format.c_str(), quality);
677 }
678 
679 // Constrain a color value from 0 to 255
680 int Frame::constrain(int color_value)
681 {
682  // Constrain new color from 0 to 255
683  if (color_value < 0)
684  color_value = 0;
685  else if (color_value > 255)
686  color_value = 255;
687 
688  return color_value;
689 }
690 
691 void Frame::AddColor(int new_width, int new_height, std::string new_color)
692 {
693  const std::lock_guard<std::recursive_mutex> lock(addingImageMutex);
694  // Update parameters
695  width = new_width;
696  height = new_height;
697  color = new_color;
698  AddColor(QColor(QString::fromStdString(new_color)));
699 }
700 
701 // Add (or replace) pixel data to the frame (based on a solid color)
702 void Frame::AddColor(const QColor& new_color)
703 {
704  // Create new image object, and fill with pixel data
705  const std::lock_guard<std::recursive_mutex> lock(addingImageMutex);
706  image = std::make_shared<QImage>(width, height, QImage::Format_RGBA8888_Premultiplied);
707 
708  // Fill with solid color
709  image->fill(new_color);
710  has_image_data = true;
711 }
712 
713 // Add (or replace) pixel data to the frame
715  int new_width, int new_height, int bytes_per_pixel,
716  QImage::Format type, const unsigned char *pixels_)
717 {
718  if (has_image_data) {
719  // Delete the previous QImage
720  image.reset();
721  }
722 
723  // Create new image object from pixel data
724  auto new_image = std::make_shared<QImage>(
725  pixels_,
726  new_width, new_height,
727  new_width * bytes_per_pixel,
728  type,
729  (QImageCleanupFunction) &openshot::cleanUpBuffer,
730  (void*) pixels_
731  );
732  AddImage(new_image);
733 }
734 
735 // Add (or replace) pixel data to the frame
736 void Frame::AddImage(std::shared_ptr<QImage> new_image)
737 {
738  // Ignore blank images
739  if (!new_image)
740  return;
741 
742  // assign image data
743  const std::lock_guard<std::recursive_mutex> lock(addingImageMutex);
744  image = new_image;
745 
746  // Always convert to Format_RGBA8888_Premultiplied (if different)
747  if (image->format() != QImage::Format_RGBA8888_Premultiplied)
748  *image = image->convertToFormat(QImage::Format_RGBA8888_Premultiplied);
749 
750  // Update height and width
751  width = image->width();
752  height = image->height();
753  has_image_data = true;
754 }
755 
756 // Add (or replace) pixel data to the frame (for only the odd or even lines)
757 void Frame::AddImage(std::shared_ptr<QImage> new_image, bool only_odd_lines)
758 {
759  // Ignore blank new_image
760  if (!new_image)
761  return;
762 
763  // Check for blank source image
764  if (!image) {
765  // Replace the blank source image
766  AddImage(new_image);
767 
768  } else {
769  // Ignore image of different sizes or formats
770  bool ret=false;
771  if (image == new_image || image->size() != new_image->size()) {
772  ret = true;
773  }
774  else if (new_image->format() != QImage::Format_RGBA8888_Premultiplied) {
775  new_image = std::make_shared<QImage>(
776  new_image->convertToFormat(QImage::Format_RGBA8888_Premultiplied));
777  }
778  if (ret) {
779  return;
780  }
781 
782  // Get the frame's image
783  const std::lock_guard<std::recursive_mutex> lock(addingImageMutex);
784  unsigned char *pixels = image->bits();
785  const unsigned char *new_pixels = new_image->constBits();
786 
787  // Loop through the scanlines of the image (even or odd)
788  int start = 0;
789  if (only_odd_lines)
790  start = 1;
791 
792  for (int row = start; row < image->height(); row += 2) {
793  int offset = row * image->bytesPerLine();
794  memcpy(pixels + offset, new_pixels + offset, image->bytesPerLine());
795  }
796 
797  // Update height and width
798  height = image->height();
799  width = image->width();
800  has_image_data = true;
801  }
802 }
803 
804 
805 // Resize audio container to hold more (or less) samples and channels
806 void Frame::ResizeAudio(int channels, int length, int rate, ChannelLayout layout)
807 {
808  const std::lock_guard<std::recursive_mutex> lock(addingAudioMutex);
809 
810  // Resize JUCE audio buffer
811  audio->setSize(channels, length, true, true, false);
812  channel_layout = layout;
813  sample_rate = rate;
814 
815  // Calculate max audio sample added
816  max_audio_sample = length;
817 }
818 
820 void Frame::SetAudioDirection(bool is_increasing) {
821  if (audio && !audio_is_increasing && is_increasing) {
822  // Forward audio buffer
823  audio->reverse(0, audio->getNumSamples());
824  } else if (audio && audio_is_increasing && !is_increasing) {
825  // Reverse audio buffer
826  audio->reverse(0, audio->getNumSamples());
827  }
828  audio_is_increasing = is_increasing;
829 }
830 
831 // Add audio samples to a specific channel
832 void Frame::AddAudio(bool replaceSamples, int destChannel, int destStartSample, const float* source, int numSamples, float gainToApplyToSource = 1.0f) {
833  const std::lock_guard<std::recursive_mutex> lock(addingAudioMutex);
834 
835  // Clamp starting sample to 0
836  int destStartSampleAdjusted = max(destStartSample, 0);
837 
838  // Extend audio container to hold more (or less) samples and channels.. if needed
839  int new_length = destStartSampleAdjusted + numSamples;
840  int new_channel_length = audio->getNumChannels();
841  if (destChannel >= new_channel_length)
842  new_channel_length = destChannel + 1;
843  if (new_length > audio->getNumSamples() || new_channel_length > audio->getNumChannels())
844  audio->setSize(new_channel_length, new_length, true, true, false);
845 
846  // Clear the range of samples first (if needed)
847  if (replaceSamples)
848  audio->clear(destChannel, destStartSampleAdjusted, numSamples);
849 
850  // Add samples to frame's audio buffer
851  audio->addFrom(destChannel, destStartSampleAdjusted, source, numSamples, gainToApplyToSource);
852  has_audio_data = true;
853 
854  // Calculate max audio sample added
855  if (new_length > max_audio_sample)
856  max_audio_sample = new_length;
857 
858  // Reset audio direction
859  audio_is_increasing = true;
860 }
861 
862 // Apply gain ramp (i.e. fading volume)
863 void Frame::ApplyGainRamp(int destChannel, int destStartSample, int numSamples, float initial_gain = 0.0f, float final_gain = 1.0f)
864 {
865  const std::lock_guard<std::recursive_mutex> lock(addingAudioMutex);
866 
867  // Apply gain ramp
868  audio->applyGainRamp(destChannel, destStartSample, numSamples, initial_gain, final_gain);
869 }
870 
871 // Get pointer to Magick++ image object
872 std::shared_ptr<QImage> Frame::GetImage()
873 {
874  // Check for blank image
875  if (!image)
876  // Fill with black
877  AddColor(width, height, color);
878 
879  return image;
880 }
881 
882 #ifdef USE_OPENCV
883 
884 // Convert Qimage to Mat
885 cv::Mat Frame::Qimage2mat( std::shared_ptr<QImage>& qimage) {
886 
887  cv::Mat mat = cv::Mat(qimage->height(), qimage->width(), CV_8UC4, (uchar*)qimage->constBits(), qimage->bytesPerLine()).clone();
888  cv::Mat mat2 = cv::Mat(mat.rows, mat.cols, CV_8UC3 );
889  int from_to[] = { 0,0, 1,1, 2,2 };
890  cv::mixChannels( &mat, 1, &mat2, 1, from_to, 3 );
891  cv::cvtColor(mat2, mat2, cv::COLOR_RGB2BGR);
892  return mat2;
893 }
894 
895 // Get pointer to OpenCV image object
897 {
898  // Check for blank image
899  if (!image)
900  // Fill with black
901  AddColor(width, height, color);
902 
903  // if (imagecv.empty())
904  // Convert Qimage to Mat
905  imagecv = Qimage2mat(image);
906 
907  return imagecv;
908 }
909 
910 std::shared_ptr<QImage> Frame::Mat2Qimage(cv::Mat img){
911  cv::cvtColor(img, img, cv::COLOR_BGR2RGB);
912  QImage qimg((uchar*) img.data, img.cols, img.rows, img.step, QImage::Format_RGB888);
913 
914  std::shared_ptr<QImage> imgIn = std::make_shared<QImage>(qimg.copy());
915 
916  // Always convert to RGBA8888 (if different)
917  if (imgIn->format() != QImage::Format_RGBA8888_Premultiplied)
918  *imgIn = imgIn->convertToFormat(QImage::Format_RGBA8888_Premultiplied);
919 
920  return imgIn;
921 }
922 
923 // Set pointer to OpenCV image object
924 void Frame::SetImageCV(cv::Mat _image)
925 {
926  imagecv = _image;
927  image = Mat2Qimage(_image);
928 }
929 #endif
930 
931 // Play audio samples for this frame
933 {
934  // Check if samples are present
935  if (!GetAudioSamplesCount())
936  return;
937 
938  juce::AudioDeviceManager deviceManager;
939  juce::String error = deviceManager.initialise (
940  0, /* number of input channels */
941  2, /* number of output channels */
942  0, /* no XML settings.. */
943  true /* select default device on failure */);
944 
945  // Output error (if any)
946  if (error.isNotEmpty()) {
947  cout << "Error on initialise(): " << error << endl;
948  }
949 
950  juce::AudioSourcePlayer audioSourcePlayer;
951  deviceManager.addAudioCallback (&audioSourcePlayer);
952 
953  std::unique_ptr<AudioBufferSource> my_source;
954  my_source.reset (new AudioBufferSource (audio.get()));
955 
956  // Create TimeSliceThread for audio buffering
957  juce::TimeSliceThread my_thread("Audio buffer thread");
958 
959  // Start thread
960  my_thread.startThread();
961 
962  juce::AudioTransportSource transport1;
963  transport1.setSource (my_source.get(),
964  5000, // tells it to buffer this many samples ahead
965  &my_thread,
966  (double) sample_rate,
967  audio->getNumChannels()); // sample rate of source
968  transport1.setPosition (0);
969  transport1.setGain(1.0);
970 
971 
972  // Create MIXER
973  juce::MixerAudioSource mixer;
974  mixer.addInputSource(&transport1, false);
975  audioSourcePlayer.setSource (&mixer);
976 
977  // Start transports
978  transport1.start();
979 
980  while (transport1.isPlaying())
981  {
982  cout << "playing" << endl;
983  std::this_thread::sleep_for(std::chrono::seconds(1));
984  }
985 
986  cout << "DONE!!!" << endl;
987 
988  transport1.stop();
989  transport1.setSource (0);
990  audioSourcePlayer.setSource (0);
991  my_thread.stopThread(500);
992  deviceManager.removeAudioCallback (&audioSourcePlayer);
993  deviceManager.closeAudioDevice();
994  deviceManager.removeAllChangeListeners();
995  deviceManager.dispatchPendingMessages();
996 
997  cout << "End of Play()" << endl;
998 
999 
1000 }
1001 
1002 // Add audio silence
1003 void Frame::AddAudioSilence(int numSamples)
1004 {
1005  const std::lock_guard<std::recursive_mutex> lock(addingAudioMutex);
1006 
1007  // Resize audio container
1008  audio->setSize(channels, numSamples, false, true, false);
1009  audio->clear();
1010  has_audio_data = true;
1011 
1012  // Calculate max audio sample added
1013  max_audio_sample = numSamples;
1014 
1015  // Reset audio direction
1016  audio_is_increasing = true;
1017 }
openshot::Frame::GetWaveformPixels
const unsigned char * GetWaveformPixels(int width, int height, int Red, int Green, int Blue, int Alpha)
Get an audio waveform image pixels.
Definition: Frame.cpp:260
openshot::Frame::SampleRate
int SampleRate()
Get the original sample rate of this frame's audio data.
Definition: Frame.cpp:504
openshot::Frame::operator=
Frame & operator=(const Frame &other)
Assignment operator.
Definition: Frame.cpp:78
openshot::Frame::capture_timestamp
double capture_timestamp
Optional source capture timestamp in seconds for live capture frames.
Definition: Frame.h:118
openshot::Frame::GetAudioSamples
float * GetAudioSamples(int channel)
Get an array of sample data (and optional reverse the sample values)
Definition: Frame.cpp:316
openshot::Frame::SetFrameNumber
void SetFrameNumber(int64_t number)
Set frame number.
Definition: Frame.cpp:452
openshot::Frame::has_audio_data
bool has_audio_data
This frame has been loaded with audio data.
Definition: Frame.h:119
openshot
This namespace is the default namespace for all code in the openshot library.
Definition: AnimatedCurve.h:24
juce::AudioBuffer< float >
openshot::Fraction
This class represents a fraction.
Definition: Fraction.h:30
AudioBufferSource.h
Header file for AudioBufferSource class.
AudioResampler.h
Header file for AudioResampler class.
openshot::Frame
This class represents a single frame of video (i.e. image & audio data)
Definition: Frame.h:89
QtUtilities.h
Header file for QtUtilities (compatibiity overlay)
openshot::Frame::has_image_data
bool has_image_data
This frame has been loaded with pixel data.
Definition: Frame.h:120
openshot::LAYOUT_STEREO
@ LAYOUT_STEREO
Definition: ChannelLayouts.h:31
openshot::Frame::AddAudioSilence
void AddAudioSilence(int numSamples)
Add audio silence.
Definition: Frame.cpp:1003
openshot::Fraction::ToDouble
double ToDouble() const
Return this fraction as a double (i.e. 1/2 = 0.5)
Definition: Fraction.cpp:40
openshot::Frame::GetBytes
int64_t GetBytes()
Get the size in bytes of this frame (rough estimate)
Definition: Frame.cpp:382
openshot::Frame::ResizeAudio
void ResizeAudio(int channels, int length, int sample_rate, openshot::ChannelLayout channel_layout)
Resize audio container to hold more (or less) samples and channels.
Definition: Frame.cpp:806
openshot::Fraction::num
int num
Numerator for the fraction.
Definition: Fraction.h:32
openshot::Frame::GetPixels
const unsigned char * GetPixels()
Get pixel data (as packets)
Definition: Frame.cpp:399
openshot::Fraction::den
int den
Denominator for the fraction.
Definition: Fraction.h:33
openshot::Fraction::Reciprocal
Fraction Reciprocal() const
Return the reciprocal as a Fraction.
Definition: Fraction.cpp:78
openshot::Frame::AddImage
void AddImage(int new_width, int new_height, int bytes_per_pixel, QImage::Format type, const unsigned char *pixels_)
Add (or replace) pixel data to the frame.
Definition: Frame.cpp:714
openshot::AudioBufferSource
This class is used to expose an AudioBuffer<float> as an AudioSource in JUCE.
Definition: AudioBufferSource.h:27
openshot::SCALE_CROP
@ SCALE_CROP
Scale the clip until both height and width fill the canvas (cropping the overlap)
Definition: Enums.h:37
juce
Definition: Robotization.h:29
openshot::Frame::ApplyGainRamp
void ApplyGainRamp(int destChannel, int destStartSample, int numSamples, float initial_gain, float final_gain)
Apply gain ramp (i.e. fading volume)
Definition: Frame.cpp:863
openshot::Frame::GetAudioChannelsCount
int GetAudioChannelsCount()
Get number of audio channels.
Definition: Frame.cpp:360
openshot::Frame::GetHeight
int GetHeight()
Get height of image.
Definition: Frame.cpp:492
path
path
Definition: FFmpegWriter.cpp:1578
Frame.h
Header file for Frame class.
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::Frame::GetInterleavedAudioSamples
float * GetInterleavedAudioSamples(int *sample_count)
Get an array of sample data (all channels interleaved together), using any sample rate.
Definition: Frame.cpp:326
openshot::SCALE_FIT
@ SCALE_FIT
Scale the clip until either height or width fills the canvas (with no cropping)
Definition: Enums.h:38
openshot::Frame::CheckPixel
bool CheckPixel(int row, int col, int red, int green, int blue, int alpha, int threshold)
Check a specific pixel color value (returns True/False)
Definition: Frame.cpp:423
openshot::Frame::GetWaveform
std::shared_ptr< QImage > GetWaveform(int width, int height, int Red, int Green, int Blue, int Alpha)
Get an audio waveform image.
Definition: Frame.cpp:161
openshot::Frame::AddAudio
void AddAudio(bool replaceSamples, int destChannel, int destStartSample, const float *source, int numSamples, float gainToApplyToSource)
Add audio samples to a specific channel.
Definition: Frame.cpp:832
openshot::Frame::Thumbnail
void Thumbnail(std::string path, int new_width, int new_height, std::string mask_path, std::string overlay_path, std::string background_color, bool ignore_aspect, std::string format="png", int quality=100, float rotate=0.0, ScaleType scale_mode=SCALE_FIT)
Definition: Frame.cpp:545
openshot::Frame::GetWidth
int GetWidth()
Get height of image.
Definition: Frame.cpp:498
openshot::Frame::audio
std::shared_ptr< juce::AudioBuffer< float > > audio
Definition: Frame.h:116
openshot::Frame::ClearWaveform
void ClearWaveform()
Clear the waveform image (and deallocate its memory)
Definition: Frame.cpp:253
openshot::Frame::SetPixelRatio
void SetPixelRatio(int num, int den)
Set Pixel Aspect Ratio.
Definition: Frame.cpp:445
openshot::Frame::Save
void Save(std::string path, float scale, std::string format="PNG", int quality=100)
Save the frame image to the specified path. The image format can be BMP, JPG, JPEG,...
Definition: Frame.cpp:517
openshot::Frame::GetImage
std::shared_ptr< QImage > GetImage()
Get pointer to Qt QImage image object.
Definition: Frame.cpp:872
openshot::Frame::SetAudioDirection
void SetAudioDirection(bool is_increasing)
Set the direction of the audio buffer of this frame.
Definition: Frame.cpp:820
openshot::Frame::GetImageCV
cv::Mat GetImageCV()
Get pointer to OpenCV Mat image object.
Definition: Frame.cpp:896
openshot::Frame::ChannelsLayout
openshot::ChannelLayout ChannelsLayout()
Definition: Frame.cpp:510
openshot::Frame::GetAudioSampleBuffer
juce::AudioBuffer< float > * GetAudioSampleBuffer()
Definition: Frame.cpp:376
openshot::ScaleType
ScaleType
This enumeration determines how clips are scaled to fit their parent container.
Definition: Enums.h:35
openshot::ChannelLayout
ChannelLayout
This enumeration determines the audio channel layout (such as stereo, mono, 5 point surround,...
Definition: ChannelLayouts.h:28
openshot::Frame::Play
void Play()
Play audio samples for this frame.
Definition: Frame.cpp:932
openshot::SCALE_NONE
@ SCALE_NONE
Do not scale the clip.
Definition: Enums.h:40
openshot::Frame::DeepCopy
void DeepCopy(const Frame &other)
Copy data and pointers from another Frame instance.
Definition: Frame.cpp:87
openshot::Frame::GetAudioSample
float GetAudioSample(int channel, int sample, int magnitude_range)
Get magnitude of range of samples (if channel is -1, return average of all channels for that sample)
Definition: Frame.cpp:303
openshot::Frame::number
int64_t number
This is the frame number (starting at 1)
Definition: Frame.h:117
openshot::SCALE_STRETCH
@ SCALE_STRETCH
Scale the clip until both height and width fill the canvas (distort to fit)
Definition: Enums.h:39
openshot::Frame::Mat2Qimage
std::shared_ptr< QImage > Mat2Qimage(cv::Mat img)
Convert OpenCV Mat to QImage.
Definition: Frame.cpp:910
openshot::Frame::SetImageCV
void SetImageCV(cv::Mat _image)
Set pointer to OpenCV image object.
Definition: Frame.cpp:924
openshot::Frame::Display
void Display()
Display the frame image to the screen (primarily used for debugging reasons)
Definition: Frame.cpp:122
openshot::Frame::~Frame
virtual ~Frame()
Destructor.
Definition: Frame.cpp:112
openshot::Frame::Qimage2mat
cv::Mat Qimage2mat(std::shared_ptr< QImage > &qimage)
Convert Qimage to Mat.
Definition: Frame.cpp:885
openshot::Frame::Frame
Frame()
Constructor - blank frame.
Definition: Frame.cpp:59
openshot::Frame::GetAudioSamplesCount
int GetAudioSamplesCount()
Get number of audio samples.
Definition: Frame.cpp:370
openshot::Frame::AddColor
void AddColor(int new_width, int new_height, std::string new_color)
Add (or replace) pixel data to the frame (based on a solid color)
Definition: Frame.cpp:691
openshot::Frame::DisplayWaveform
void DisplayWaveform()
Display the wave form.
Definition: Frame.cpp:270