28 #include "../include/Frame.h"
34 Frame::Frame() : number(1), pixel_ratio(1,1), channels(2), width(1), height(1), color(
"#000000"),
35 channel_layout(
LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false),
39 audio = std::shared_ptr<juce::AudioSampleBuffer>(
new juce::AudioSampleBuffer(channels, 0));
47 : number(number), pixel_ratio(1,1), channels(2), width(width), height(height), color(color),
48 channel_layout(
LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false),
52 audio = std::shared_ptr<juce::AudioSampleBuffer>(
new juce::AudioSampleBuffer(channels, 0));
60 number(number), pixel_ratio(1,1), channels(channels), width(1), height(1), color(
"#000000"),
61 channel_layout(
LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false),
65 audio = std::shared_ptr<juce::AudioSampleBuffer>(
new juce::AudioSampleBuffer(channels, samples));
72 Frame::Frame(int64_t number,
int width,
int height,
string color,
int samples,
int channels)
73 : number(number), pixel_ratio(1,1), channels(channels), width(width), height(height), color(color),
74 channel_layout(
LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false),
78 audio = std::shared_ptr<juce::AudioSampleBuffer>(
new juce::AudioSampleBuffer(channels, samples));
105 channels = other.channels;
107 height = other.height;
108 channel_layout = other.channel_layout;
111 sample_rate = other.sample_rate;
112 pixel_ratio =
Fraction(other.pixel_ratio.
num, other.pixel_ratio.
den);
116 image = std::shared_ptr<QImage>(
new QImage(*(other.image)));
118 audio = std::shared_ptr<juce::AudioSampleBuffer>(
new juce::AudioSampleBuffer(*(other.audio)));
119 if (other.wave_image)
120 wave_image = std::shared_ptr<QImage>(
new QImage(*(other.wave_image)));
133 if (!QApplication::instance()) {
136 static char* argv[1] = {NULL};
137 previewApp = std::shared_ptr<QApplication>(
new QApplication(argc, argv));
141 std::shared_ptr<QImage> previewImage =
GetImage();
144 if (pixel_ratio.
num != 1 || pixel_ratio.
den != 1)
147 int new_width = previewImage->size().width();
151 previewImage = std::shared_ptr<QImage>(
new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
155 QWidget previewWindow;
156 previewWindow.setStyleSheet(
"background-color: #000000;");
161 previewLabel.setPixmap(QPixmap::fromImage(*previewImage));
162 previewLabel.setMask(QPixmap::fromImage(*previewImage).mask());
163 layout.addWidget(&previewLabel);
166 previewWindow.setLayout(&layout);
167 previewWindow.show();
172 std::shared_ptr<QImage>
Frame::GetWaveform(
int width,
int height,
int Red,
int Green,
int Blue,
int Alpha)
178 QVector<QPointF> lines;
179 QVector<QPointF> labels;
183 if (total_samples > 0)
186 int new_height = 200 * audio->getNumChannels();
187 int height_padding = 20 * (audio->getNumChannels() - 1);
188 int total_height = new_height + height_padding;
193 for (
int channel = 0; channel < audio->getNumChannels(); channel++)
198 const float *samples = audio->getReadPointer(channel);
203 float value = samples[sample] * 100;
208 lines.push_back(QPointF(X,Y));
209 lines.push_back(QPointF(X,Y-value));
213 lines.push_back(QPointF(X,Y));
214 lines.push_back(QPointF(X,Y));
219 labels.push_back(QPointF(5, Y - 5));
222 Y += (200 + height_padding);
227 wave_image = std::shared_ptr<QImage>(
new QImage(total_width, total_height, QImage::Format_RGBA8888));
228 wave_image->fill(QColor(0,0,0,0));
231 QPainter painter(wave_image.get());
234 painter.setPen(QColor(Red, Green, Blue, Alpha));
237 painter.drawLines(lines);
250 if (width != total_width || height != total_height) {
251 QImage scaled_wave_image = wave_image->scaled(width, height, Qt::IgnoreAspectRatio, Qt::FastTransformation);
252 wave_image = std::shared_ptr<QImage>(
new QImage(scaled_wave_image));
258 wave_image = std::shared_ptr<QImage>(
new QImage(width, height, QImage::Format_RGBA8888));
259 wave_image->fill(QColor(QString::fromStdString(
"#000000")));
277 wave_image =
GetWaveform(width, height, Red, Green, Blue, Alpha);
280 return wave_image->bits();
289 if (!QApplication::instance()) {
292 static char* argv[1] = {NULL};
293 previewApp = std::shared_ptr<QApplication>(
new QApplication(argc, argv));
297 QWidget previewWindow;
298 previewWindow.setStyleSheet(
"background-color: #000000;");
303 previewLabel.setPixmap(QPixmap::fromImage(*wave_image));
304 previewLabel.setMask(QPixmap::fromImage(*wave_image).mask());
305 layout.addWidget(&previewLabel);
308 previewWindow.setLayout(&layout);
309 previewWindow.show();
321 return audio->getMagnitude(channel, sample, magnitude_range);
325 return audio->getMagnitude(sample, magnitude_range);
333 return audio->getWritePointer(channel);
339 float *output = NULL;
340 AudioSampleBuffer *buffer(audio.get());
341 int num_of_channels = audio->getNumChannels();
345 if (new_sample_rate != sample_rate)
348 resampler->
SetBuffer(audio.get(), sample_rate, new_sample_rate);
354 num_of_samples = buffer->getNumSamples();
358 output =
new float[num_of_channels * num_of_samples];
362 for (
int channel = 0; channel < num_of_channels; channel++)
364 for (
int sample = 0; sample < num_of_samples; sample++)
367 output[position] = buffer->getReadPointer(channel)[sample];
375 *sample_count = num_of_samples;
385 float *output = NULL;
386 AudioSampleBuffer *buffer(audio.get());
387 int num_of_channels = audio->getNumChannels();
391 if (new_sample_rate != sample_rate && resampler)
394 resampler->
SetBuffer(audio.get(), sample_rate, new_sample_rate);
400 num_of_samples = buffer->getNumSamples();
404 output =
new float[num_of_channels * num_of_samples];
408 for (
int sample = 0; sample < num_of_samples; sample++)
410 for (
int channel = 0; channel < num_of_channels; channel++)
413 output[position] = buffer->getReadPointer(channel)[sample];
421 *sample_count = num_of_samples;
430 const GenericScopedLock<CriticalSection> lock(addingAudioSection);
432 return audio->getNumChannels();
440 const GenericScopedLock<CriticalSection> lock(addingAudioSection);
441 return max_audio_sample;
452 int64_t total_bytes = 0;
454 total_bytes += (width * height *
sizeof(char) * 4);
457 total_bytes += (sample_rate / 24.0) *
sizeof(float);
473 return image->bits();
480 return image->scanLine(row);
486 pixel_ratio.
num = num;
487 pixel_ratio.
den = den;
503 double previous_samples = (sample_rate * fps_rate) * (number - 1);
504 double previous_samples_remainder = fmod(previous_samples, (
double)channels);
505 previous_samples -= previous_samples_remainder;
508 double total_samples = (sample_rate * fps_rate) * number;
509 double total_samples_remainder = fmod(total_samples, (
double)channels);
510 total_samples -= total_samples_remainder;
514 int samples_per_frame = round(total_samples - previous_samples);
515 return samples_per_frame;
545 return channel_layout;
550 void Frame::Save(
string path,
float scale,
string format,
int quality)
553 std::shared_ptr<QImage> previewImage =
GetImage();
556 if (abs(scale) > 1.001 || abs(scale) < 0.999)
558 int new_width = width;
559 int new_height = height;
562 if (pixel_ratio.
num != 1 || pixel_ratio.
den != 1)
565 int new_width = previewImage->size().width();
569 previewImage = std::shared_ptr<QImage>(
new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
573 previewImage = std::shared_ptr<QImage>(
new QImage(previewImage->scaled(new_width * scale, new_height * scale, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
577 previewImage->save(QString::fromStdString(path), format.c_str(), quality);
581 void Frame::Thumbnail(
string path,
int new_width,
int new_height,
string mask_path,
string overlay_path,
582 string background_color,
bool ignore_aspect,
string format,
int quality,
float rotate) {
585 std::shared_ptr<QImage> thumbnail = std::shared_ptr<QImage>(
new QImage(new_width, new_height, QImage::Format_RGBA8888));
586 thumbnail->fill(QColor(QString::fromStdString(background_color)));
589 QPainter painter(thumbnail.get());
590 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing,
true);
593 std::shared_ptr<QImage> previewImage =
GetImage();
596 if (pixel_ratio.
num != 1 || pixel_ratio.
den != 1)
599 int aspect_width = previewImage->size().width();
600 int aspect_height = previewImage->size().height() * pixel_ratio.
Reciprocal().
ToDouble();
603 previewImage = std::shared_ptr<QImage>(
new QImage(previewImage->scaled(aspect_width, aspect_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
609 previewImage = std::shared_ptr<QImage>(
new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
612 previewImage = std::shared_ptr<QImage>(
new QImage(previewImage->scaled(new_width, new_height, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
615 int x = (new_width - previewImage->size().width()) / 2.0;
616 int y = (new_height - previewImage->size().height()) / 2.0;
617 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
621 QTransform transform;
622 float origin_x = previewImage->width() / 2.0;
623 float origin_y = previewImage->height() / 2.0;
624 transform.translate(origin_x, origin_y);
625 transform.rotate(rotate);
626 transform.translate(-origin_x,-origin_y);
627 painter.setTransform(transform);
630 painter.drawImage(x, y, *previewImage);
634 if (overlay_path !=
"") {
636 std::shared_ptr<QImage> overlay = std::shared_ptr<QImage>(
new QImage());
637 overlay->load(QString::fromStdString(overlay_path));
640 overlay = std::shared_ptr<QImage>(
new QImage(overlay->convertToFormat(QImage::Format_RGBA8888)));
643 overlay = std::shared_ptr<QImage>(
new QImage(overlay->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
646 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
647 painter.drawImage(0, 0, *overlay);
652 if (mask_path !=
"") {
654 std::shared_ptr<QImage> mask = std::shared_ptr<QImage>(
new QImage());
655 mask->load(QString::fromStdString(mask_path));
658 mask = std::shared_ptr<QImage>(
new QImage(mask->convertToFormat(QImage::Format_RGBA8888)));
661 mask = std::shared_ptr<QImage>(
new QImage(mask->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
664 mask->invertPixels();
667 unsigned char *pixels = (
unsigned char *) thumbnail->bits();
668 unsigned char *mask_pixels = (
unsigned char *) mask->bits();
672 for (
int pixel = 0, byte_index=0; pixel < new_width * new_height; pixel++, byte_index+=4)
675 int gray_value = qGray(mask_pixels[byte_index], mask_pixels[byte_index] + 1, mask_pixels[byte_index] + 2);
676 int Frame_Alpha = pixels[byte_index + 3];
677 int Mask_Value = constrain(Frame_Alpha - gray_value);
680 pixels[byte_index + 3] = Mask_Value;
689 thumbnail->save(QString::fromStdString(path), format.c_str(), quality);
693 int Frame::constrain(
int color_value)
698 else if (color_value > 255)
711 const GenericScopedLock<CriticalSection> lock(addingImageSection);
712 #pragma omp critical (AddImage)
714 image = std::shared_ptr<QImage>(
new QImage(new_width, new_height, QImage::Format_RGBA8888));
717 image->fill(QColor(QString::fromStdString(color)));
720 width = image->width();
721 height = image->height();
726 void Frame::AddImage(
int new_width,
int new_height,
int bytes_per_pixel, QImage::Format type,
const unsigned char *pixels_)
729 const GenericScopedLock<CriticalSection> lock(addingImageSection);
730 int buffer_size = new_width * new_height * bytes_per_pixel;
731 qbuffer =
new unsigned char[buffer_size]();
734 memcpy((
unsigned char*)qbuffer, pixels_, buffer_size);
737 #pragma omp critical (AddImage)
739 image = std::shared_ptr<QImage>(
new QImage(qbuffer, new_width, new_height, new_width * bytes_per_pixel, type, (QImageCleanupFunction) &
openshot::Frame::cleanUpBuffer, (
void*) qbuffer));
742 if (image->format() != QImage::Format_RGBA8888)
743 *image = image->convertToFormat(QImage::Format_RGBA8888);
746 width = image->width();
747 height = image->height();
760 const GenericScopedLock<CriticalSection> lock(addingImageSection);
761 #pragma omp critical (AddImage)
766 if (image->format() != QImage::Format_RGBA8888)
767 *image = image->convertToFormat(QImage::Format_RGBA8888);
770 width = image->width();
771 height = image->height();
792 #pragma omp critical (AddImage)
793 if (image == new_image || image->size() != image->size() || image->format() != image->format())
799 const GenericScopedLock<CriticalSection> lock(addingImageSection);
800 #pragma omp critical (AddImage)
802 const unsigned char *pixels = image->bits();
803 const unsigned char *new_pixels = new_image->bits();
809 for (
int row = start; row < image->height(); row += 2) {
810 memcpy((
unsigned char *) pixels, new_pixels + (row * image->bytesPerLine()), image->bytesPerLine());
811 new_pixels += image->bytesPerLine();
815 width = image->width();
816 height = image->height();
826 const GenericScopedLock<CriticalSection> lock(addingAudioSection);
829 audio->setSize(channels, length,
true,
true,
false);
830 channel_layout = layout;
834 max_audio_sample = length;
838 void Frame::AddAudio(
bool replaceSamples,
int destChannel,
int destStartSample,
const float* source,
int numSamples,
float gainToApplyToSource = 1.0f) {
839 const GenericScopedLock<CriticalSection> lock(addingAudioSection);
840 #pragma omp critical (adding_audio)
843 int destStartSampleAdjusted = max(destStartSample, 0);
846 int new_length = destStartSampleAdjusted + numSamples;
847 int new_channel_length = audio->getNumChannels();
848 if (destChannel >= new_channel_length)
849 new_channel_length = destChannel + 1;
850 if (new_length > audio->getNumSamples() || new_channel_length > audio->getNumChannels())
851 audio->setSize(new_channel_length, new_length,
true,
true,
false);
855 audio->clear(destChannel, destStartSampleAdjusted, numSamples);
858 audio->addFrom(destChannel, destStartSampleAdjusted, source, numSamples, gainToApplyToSource);
862 if (new_length > max_audio_sample)
863 max_audio_sample = new_length;
868 void Frame::ApplyGainRamp(
int destChannel,
int destStartSample,
int numSamples,
float initial_gain = 0.0f,
float final_gain = 1.0f)
870 const GenericScopedLock<CriticalSection> lock(addingAudioSection);
873 audio->applyGainRamp(destChannel, destStartSample, numSamples, initial_gain, final_gain);
887 #ifdef USE_IMAGEMAGICK
889 std::shared_ptr<Magick::Image> Frame::GetMagickImage()
897 QRgb
const *tmpBits = (
const QRgb*)image->bits();
900 std::shared_ptr<Magick::Image> magick_image = std::shared_ptr<Magick::Image>(
new Magick::Image(image->width(), image->height(),
"RGBA", Magick::CharPixel, tmpBits));
903 magick_image->backgroundColor(Magick::Color(
"none"));
904 magick_image->virtualPixelMethod(Magick::TransparentVirtualPixelMethod);
905 magick_image->matte(
true);
911 #ifdef USE_IMAGEMAGICK
913 void Frame::AddMagickImage(std::shared_ptr<Magick::Image> new_image)
916 const std::size_t bufferSize = new_image->columns() * new_image->rows() * BPP;
921 qbuffer =
new unsigned char[bufferSize]();
922 unsigned char *buffer = (
unsigned char*)qbuffer;
927 Magick::PixelPacket *pixels = new_image->getPixels(0,0, new_image->columns(), new_image->rows());
928 for (
int n = 0, i = 0; n < new_image->columns() * new_image->rows(); n += 1, i += 4) {
929 buffer[i+0] = MagickCore::ScaleQuantumToChar((Magick::Quantum) pixels[n].red);
930 buffer[i+1] = MagickCore::ScaleQuantumToChar((Magick::Quantum) pixels[n].green);
931 buffer[i+2] = MagickCore::ScaleQuantumToChar((Magick::Quantum) pixels[n].blue);
932 buffer[i+3] = 255 - MagickCore::ScaleQuantumToChar((Magick::Quantum) pixels[n].opacity);
937 image = std::shared_ptr<QImage>(
new QImage(qbuffer, width, height, width * BPP, QImage::Format_RGBA8888, (QImageCleanupFunction) &
cleanUpBuffer, (
void*) qbuffer));
940 width = image->width();
941 height = image->height();
953 AudioDeviceManager deviceManager;
954 deviceManager.initialise (0,
960 AudioSourcePlayer audioSourcePlayer;
961 deviceManager.addAudioCallback (&audioSourcePlayer);
963 ScopedPointer<AudioBufferSource> my_source;
967 TimeSliceThread my_thread(
"Audio buffer thread");
970 my_thread.startThread();
972 AudioTransportSource transport1;
973 transport1.setSource (my_source,
976 (
double) sample_rate,
977 audio->getNumChannels());
978 transport1.setPosition (0);
979 transport1.setGain(1.0);
983 MixerAudioSource mixer;
984 mixer.addInputSource(&transport1,
false);
985 audioSourcePlayer.setSource (&mixer);
990 while (transport1.isPlaying())
992 cout <<
"playing" << endl;
996 cout <<
"DONE!!!" << endl;
999 transport1.setSource (0);
1000 audioSourcePlayer.setSource (0);
1001 my_thread.stopThread(500);
1002 deviceManager.removeAudioCallback (&audioSourcePlayer);
1003 deviceManager.closeAudioDevice();
1004 deviceManager.removeAllChangeListeners();
1005 deviceManager.dispatchPendingMessages();
1007 cout <<
"End of Play()" << endl;
1018 unsigned char* ptr_to_qbuffer = (
unsigned char*) info;
1019 delete[] ptr_to_qbuffer;
1026 const GenericScopedLock<CriticalSection> lock(addingAudioSection);
1029 audio->setSize(channels, numSamples,
false,
true,
false);
1034 if (numSamples > max_audio_sample)
1035 max_audio_sample = numSamples;
void SetBuffer(AudioSampleBuffer *new_buffer, double sample_rate, double new_sample_rate)
Sets the audio buffer and key settings.
int GetWidth()
Get height of image.
int num
Numerator for the fraction.
int GetAudioSamplesCount()
Get number of audio samples.
float * GetInterleavedAudioSamples(int new_sample_rate, AudioResampler *resampler, int *sample_count)
Get an array of sample data (all channels interleaved together), using any sample rate...
This class represents a single frame of video (i.e. image & audio data)
const unsigned char * GetWaveformPixels(int width, int height, int Red, int Green, int Blue, int Alpha)
Get an audio waveform image pixels.
juce::AudioSampleBuffer * GetAudioSampleBuffer()
const unsigned char * GetPixels()
Get pixel data (as packets)
void Play()
Play audio samples for this frame.
void Save(string path, float scale, string format="PNG", int quality=100)
Save the frame image to the specified path. The image format can be BMP, JPG, JPEG, PNG, PPM, XBM, XPM.
void DeepCopy(const Frame &other)
Copy data and pointers from another Frame instance.
void AddAudio(bool replaceSamples, int destChannel, int destStartSample, const float *source, int numSamples, float gainToApplyToSource)
Add audio samples to a specific channel.
void Display()
Display the frame image to the screen (primarily used for debugging reasons)
Fraction Reciprocal()
Return the reciprocal as a Fraction.
int64_t number
This is the frame number (starting at 1)
This class is used to expose an AudioSampleBuffer as an AudioSource in JUCE.
Frame & operator=(const Frame &other)
Assignment operator.
void ClearWaveform()
Clear the waveform image (and deallocate it's memory)
void AddColor(int new_width, int new_height, string new_color)
Add (or replace) pixel data to the frame (based on a solid color)
float * GetAudioSamples(int channel)
Get an array of sample data.
void SetFrameNumber(int64_t number)
Set frame number.
void AddAudioSilence(int numSamples)
Add audio silence.
bool has_audio_data
This frame has been loaded with audio data.
This class represents a fraction.
void Thumbnail(string path, int new_width, int new_height, string mask_path, string overlay_path, string background_color, bool ignore_aspect, string format="png", int quality=100, float rotate=0.0)
static void cleanUpBuffer(void *info)
Clean up buffer after QImage is deleted.
ChannelLayout
This enumeration determines the audio channel layout (such as stereo, mono, 5 point surround...
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.
Frame()
Constructor - blank frame (300x200 blank image, 48kHz audio silence)
void ApplyGainRamp(int destChannel, int destStartSample, int numSamples, float initial_gain, float final_gain)
Apply gain ramp (i.e. fading volume)
void DisplayWaveform()
Display the wave form.
void SetPixelRatio(int num, int den)
Set Pixel Aspect Ratio.
int GetAudioChannelsCount()
Get number of audio channels.
This namespace is the default namespace for all code in the openshot library.
ChannelLayout ChannelsLayout()
std::shared_ptr< QImage > GetImage()
Get pointer to Qt QImage image object.
std::shared_ptr< QImage > GetWaveform(int width, int height, int Red, int Green, int Blue, int Alpha)
Get an audio waveform image.
int64_t GetBytes()
Get the size in bytes of this frame (rough estimate)
AudioSampleBuffer * GetResampledBuffer()
Get the resampled audio buffer.
float * GetPlanarAudioSamples(int new_sample_rate, AudioResampler *resampler, int *sample_count)
int den
Denominator for the fraction.
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) ...
int GetSamplesPerFrame(Fraction fps, int sample_rate, int channels)
Calculate the # of samples per video frame (for the current frame number)
bool has_image_data
This frame has been loaded with pixel data.
void ResizeAudio(int channels, int length, int sample_rate, ChannelLayout channel_layout)
Resize audio container to hold more (or less) samples and channels.
int GetHeight()
Get height of image.
double ToDouble()
Return this fraction as a double (i.e. 1/2 = 0.5)
int SampleRate()
Get the original sample rate of this frame's audio data.
This class is used to resample audio data for many sequential frames.