28 #include "../include/Frame.h"
31 using namespace openshot;
34 Frame::Frame() : number(1), pixel_ratio(1,1), channels(2), width(1), height(1),
35 channel_layout(
LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false)
38 audio = tr1::shared_ptr<juce::AudioSampleBuffer>(
new juce::AudioSampleBuffer(channels, 0));
46 : number(number), pixel_ratio(1,1), channels(2), width(width), height(height),
47 channel_layout(
LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false)
50 audio = tr1::shared_ptr<juce::AudioSampleBuffer>(
new juce::AudioSampleBuffer(channels, 0));
58 number(number), pixel_ratio(1,1), channels(channels), width(1), height(1),
59 channel_layout(
LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false)
62 audio = tr1::shared_ptr<juce::AudioSampleBuffer>(
new juce::AudioSampleBuffer(channels, samples));
69 Frame::Frame(
long int number,
int width,
int height,
string color,
int samples,
int channels)
70 : number(number), pixel_ratio(1,1), channels(channels), width(width), height(height),
71 channel_layout(
LAYOUT_STEREO), sample_rate(44100), qbuffer(NULL), has_audio_data(false), has_image_data(false)
74 audio = tr1::shared_ptr<juce::AudioSampleBuffer>(
new juce::AudioSampleBuffer(channels, samples));
92 image = tr1::shared_ptr<QImage>(
new QImage(*(other.image)));
93 audio = tr1::shared_ptr<juce::AudioSampleBuffer>(
new juce::AudioSampleBuffer(*(other.audio)));
94 pixel_ratio =
Fraction(other.pixel_ratio.
num, other.pixel_ratio.
den);
95 channels = other.channels;
96 channel_layout = other.channel_layout;
99 sample_rate = other.sample_rate;
102 if (other.wave_image)
103 wave_image = tr1::shared_ptr<QImage>(
new QImage(*(other.wave_image)));
116 if (!QApplication::instance()) {
119 static char* argv[1] = {NULL};
120 previewApp = tr1::shared_ptr<QApplication>(
new QApplication(argc, argv));
124 tr1::shared_ptr<QImage> previewImage =
GetImage();
127 if (pixel_ratio.
num != 1 || pixel_ratio.
den != 1)
130 int new_width = previewImage->size().width();
134 previewImage = tr1::shared_ptr<QImage>(
new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
138 QWidget previewWindow;
139 previewWindow.setStyleSheet(
"background-color: #000000;");
144 previewLabel.setPixmap(QPixmap::fromImage(*previewImage));
145 previewLabel.setMask(QPixmap::fromImage(*previewImage).mask());
146 layout.addWidget(&previewLabel);
149 previewWindow.setLayout(&layout);
150 previewWindow.show();
155 tr1::shared_ptr<QImage>
Frame::GetWaveform(
int width,
int height,
int Red,
int Green,
int Blue,
int Alpha)
161 QVector<QPointF> lines;
162 QVector<QPointF> labels;
165 int total_samples = audio->getNumSamples();
166 if (total_samples > 0)
169 int new_height = 200 * audio->getNumChannels();
170 int height_padding = 20 * (audio->getNumChannels() - 1);
171 int total_height = new_height + height_padding;
176 for (
int channel = 0; channel < audio->getNumChannels(); channel++)
181 const float *samples = audio->getReadPointer(channel);
183 for (
int sample = 0; sample < audio->getNumSamples(); sample++, X++)
186 float value = samples[sample] * 100;
191 lines.push_back(QPointF(X,Y));
192 lines.push_back(QPointF(X,Y-value));
196 lines.push_back(QPointF(X,Y));
197 lines.push_back(QPointF(X,Y));
202 labels.push_back(QPointF(5, Y - 5));
205 Y += (200 + height_padding);
210 wave_image = tr1::shared_ptr<QImage>(
new QImage(total_width, total_height, QImage::Format_RGBA8888));
211 wave_image->fill(QColor(0,0,0,0));
214 QPainter painter(wave_image.get());
217 painter.setPen(QColor(Red, Green, Blue, Alpha));
220 painter.drawLines(lines);
233 if (width != total_width || height != total_height) {
234 QImage scaled_wave_image = wave_image->scaled(width, height, Qt::IgnoreAspectRatio, Qt::FastTransformation);
235 wave_image = tr1::shared_ptr<QImage>(
new QImage(scaled_wave_image));
241 wave_image = tr1::shared_ptr<QImage>(
new QImage(width, height, QImage::Format_RGBA8888));
242 wave_image->fill(QColor(QString::fromStdString(
"#000000")));
260 wave_image =
GetWaveform(width, height, Red, Green, Blue, Alpha);
263 return wave_image->bits();
272 if (!QApplication::instance()) {
275 static char* argv[1] = {NULL};
276 previewApp = tr1::shared_ptr<QApplication>(
new QApplication(argc, argv));
280 QWidget previewWindow;
281 previewWindow.setStyleSheet(
"background-color: #000000;");
286 previewLabel.setPixmap(QPixmap::fromImage(*wave_image));
287 previewLabel.setMask(QPixmap::fromImage(*wave_image).mask());
288 layout.addWidget(&previewLabel);
291 previewWindow.setLayout(&layout);
292 previewWindow.show();
303 return audio->getWritePointer(channel);
309 float *output = NULL;
310 AudioSampleBuffer *buffer(audio.get());
311 int num_of_channels = audio->getNumChannels();
312 int num_of_samples = audio->getNumSamples();
315 if (new_sample_rate != sample_rate)
318 resampler->
SetBuffer(audio.get(), sample_rate, new_sample_rate);
324 num_of_samples = buffer->getNumSamples();
328 output =
new float[num_of_channels * num_of_samples];
332 for (
int channel = 0; channel < num_of_channels; channel++)
334 for (
int sample = 0; sample < num_of_samples; sample++)
337 output[position] = buffer->getReadPointer(channel)[sample];
345 *sample_count = num_of_samples;
355 float *output = NULL;
356 AudioSampleBuffer *buffer(audio.get());
357 int num_of_channels = audio->getNumChannels();
358 int num_of_samples = audio->getNumSamples();
361 if (new_sample_rate != sample_rate && resampler)
364 resampler->
SetBuffer(audio.get(), sample_rate, new_sample_rate);
370 num_of_samples = buffer->getNumSamples();
374 output =
new float[num_of_channels * num_of_samples];
378 for (
int sample = 0; sample < num_of_samples; sample++)
380 for (
int channel = 0; channel < num_of_channels; channel++)
383 output[position] = buffer->getReadPointer(channel)[sample];
391 *sample_count = num_of_samples;
400 return audio->getNumChannels();
406 return audio->getNumSamples();
417 int64 total_bytes = 0;
419 total_bytes += (width * height *
sizeof(char) * 4);
421 total_bytes += (audio->getNumSamples() * audio->getNumChannels() *
sizeof(float));
436 return image->bits();
443 return image->scanLine(row);
449 pixel_ratio.
num = num;
450 pixel_ratio.
den = den;
466 double previous_samples = (sample_rate * fps_rate) * (number - 1);
467 double previous_samples_remainder = fmod(previous_samples, (
double)channels);
468 previous_samples -= previous_samples_remainder;
471 double total_samples = (sample_rate * fps_rate) * number;
472 double total_samples_remainder = fmod(total_samples, (
double)channels);
473 total_samples -= total_samples_remainder;
477 int samples_per_frame = round(total_samples - previous_samples);
478 return samples_per_frame;
511 return channel_layout;
516 void Frame::Save(
string path,
float scale,
string format,
int quality)
519 tr1::shared_ptr<QImage> previewImage =
GetImage();
522 if (abs(scale) > 1.001 || abs(scale) < 0.999)
524 int new_width = width;
525 int new_height = height;
528 if (pixel_ratio.
num != 1 || pixel_ratio.
den != 1)
531 int new_width = previewImage->size().width();
535 previewImage = tr1::shared_ptr<QImage>(
new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
539 previewImage = tr1::shared_ptr<QImage>(
new QImage(previewImage->scaled(new_width * scale, new_height * scale, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
543 previewImage->save(QString::fromStdString(path), format.c_str(), quality);
547 void Frame::Thumbnail(
string path,
int new_width,
int new_height,
string mask_path,
string overlay_path,
548 string background_color,
bool ignore_aspect,
string format,
int quality)
throw(
InvalidFile) {
551 tr1::shared_ptr<QImage> thumbnail = tr1::shared_ptr<QImage>(
new QImage(new_width, new_height, QImage::Format_RGBA8888));
552 thumbnail->fill(QColor(QString::fromStdString(background_color)));
555 QTransform transform;
556 QPainter painter(thumbnail.get());
557 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing,
true);
561 tr1::shared_ptr<QImage> previewImage = GetImage();
564 if (pixel_ratio.num != 1 || pixel_ratio.den != 1)
567 int aspect_width = previewImage->size().width();
568 int aspect_height = previewImage->size().height() * pixel_ratio.Reciprocal().ToDouble();
571 previewImage = tr1::shared_ptr<QImage>(
new QImage(previewImage->scaled(aspect_width, aspect_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
577 previewImage = tr1::shared_ptr<QImage>(
new QImage(previewImage->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
580 previewImage = tr1::shared_ptr<QImage>(
new QImage(previewImage->scaled(new_width, new_height, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
583 int x = (new_width - previewImage->size().width()) / 2.0;
584 int y = (new_height - previewImage->size().height()) / 2.0;
585 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
586 painter.drawImage(x, y, *previewImage);
590 if (overlay_path !=
"") {
592 tr1::shared_ptr<QImage> overlay = tr1::shared_ptr<QImage>(
new QImage());
593 overlay->load(QString::fromStdString(overlay_path));
596 overlay = tr1::shared_ptr<QImage>(
new QImage(overlay->convertToFormat(QImage::Format_RGBA8888)));
599 overlay = tr1::shared_ptr<QImage>(
new QImage(overlay->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
602 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
603 painter.drawImage(0, 0, *overlay);
608 if (mask_path !=
"") {
610 tr1::shared_ptr<QImage> mask = tr1::shared_ptr<QImage>(
new QImage());
611 mask->load(QString::fromStdString(mask_path));
614 mask = tr1::shared_ptr<QImage>(
new QImage(mask->convertToFormat(QImage::Format_RGBA8888)));
617 mask = tr1::shared_ptr<QImage>(
new QImage(mask->scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)));
620 mask->invertPixels();
623 unsigned char *pixels = (
unsigned char *) thumbnail->bits();
624 unsigned char *mask_pixels = (
unsigned char *) mask->bits();
628 for (
int pixel = 0, byte_index=0; pixel < new_width * new_height; pixel++, byte_index+=4)
631 int gray_value = qGray(mask_pixels[byte_index], mask_pixels[byte_index] + 1, mask_pixels[byte_index] + 2);
632 int Frame_Alpha = pixels[byte_index + 3];
633 int Mask_Value = constrain(Frame_Alpha - gray_value);
636 pixels[byte_index + 3] = Mask_Value;
645 thumbnail->save(QString::fromStdString(path), format.c_str(), quality);
649 int Frame::constrain(
int color_value)
654 else if (color_value > 255)
664 image = tr1::shared_ptr<QImage>(
new QImage(width, height, QImage::Format_RGBA8888));
667 image->fill(QColor(QString::fromStdString(color)));
670 width = image->width();
671 height = image->height();
676 void Frame::AddImage(
int width,
int height,
int bytes_per_pixel, QImage::Format type,
const unsigned char *pixels_)
679 const GenericScopedLock<CriticalSection> lock(addingImageSection);
680 int buffer_size = width * height * bytes_per_pixel;
681 qbuffer =
new unsigned char[buffer_size]();
684 memcpy((
unsigned char*)qbuffer, pixels_, buffer_size);
687 image = tr1::shared_ptr<QImage>(
new QImage(qbuffer, width, height, width * bytes_per_pixel, type, (QImageCleanupFunction) &
openshot::Frame::cleanUpBuffer, (
void*) qbuffer));
690 if (image->format() != QImage::Format_RGBA8888)
691 image->convertToFormat(QImage::Format_RGBA8888);
694 width = image->width();
695 height = image->height();
710 if (image->format() != QImage::Format_RGBA8888)
711 image->convertToFormat(QImage::Format_RGBA8888);
714 width = image->width();
715 height = image->height();
734 if (image == new_image || image->size() != image->size() || image->format() != image->format())
738 const unsigned char *pixels = image->bits();
739 const unsigned char *new_pixels = new_image->bits();
745 for (
int row = start; row < image->height(); row += 2) {
746 memcpy((
unsigned char *) pixels, new_pixels + (row * image->bytesPerLine()), image->bytesPerLine());
747 new_pixels += image->bytesPerLine();
751 width = image->width();
752 height = image->height();
762 audio->setSize(channels, length,
true,
true,
false);
763 channel_layout = layout;
768 void Frame::AddAudio(
bool replaceSamples,
int destChannel,
int destStartSample,
const float* source,
int numSamples,
float gainToApplyToSource = 1.0f)
771 int new_length = destStartSample + numSamples;
772 int new_channel_length = audio->getNumChannels();
773 if (destChannel >= new_channel_length)
774 new_channel_length = destChannel + 1;
775 if (new_length > audio->getNumSamples() || new_channel_length > audio->getNumChannels())
776 audio->setSize(new_channel_length, new_length,
true,
true,
false);
780 audio->clear(destChannel, destStartSample, numSamples);
783 audio->addFrom(destChannel, destStartSample, source, numSamples, gainToApplyToSource);
788 void Frame::ApplyGainRamp(
int destChannel,
int destStartSample,
int numSamples,
float initial_gain = 0.0f,
float final_gain = 1.0f)
791 audio->applyGainRamp(destChannel, destStartSample, numSamples, initial_gain, final_gain);
805 #ifdef USE_IMAGEMAGICK
807 tr1::shared_ptr<Magick::Image> Frame::GetMagickImage()
815 QRgb
const *tmpBits = (
const QRgb*)image->bits();
818 tr1::shared_ptr<Magick::Image> magick_image = tr1::shared_ptr<Magick::Image>(
new Magick::Image(image->width(), image->height(),
"RGBA", Magick::CharPixel, tmpBits));
821 magick_image->backgroundColor(Magick::Color(
"none"));
822 magick_image->virtualPixelMethod(Magick::TransparentVirtualPixelMethod);
823 magick_image->matte(
true);
829 #ifdef USE_IMAGEMAGICK
831 void Frame::AddMagickImage(tr1::shared_ptr<Magick::Image> new_image)
834 const std::size_t bufferSize = new_image->columns() * new_image->rows() * BPP;
839 qbuffer =
new unsigned char[bufferSize]();
840 unsigned char *buffer = (
unsigned char*)qbuffer;
845 Magick::PixelPacket *pixels = new_image->getPixels(0,0, new_image->columns(), new_image->rows());
846 for (
int n = 0, i = 0; n < new_image->columns() * new_image->rows(); n += 1, i += 4) {
847 buffer[i+0] = MagickCore::ScaleQuantumToChar((Magick::Quantum) pixels[n].red);
848 buffer[i+1] = MagickCore::ScaleQuantumToChar((Magick::Quantum) pixels[n].green);
849 buffer[i+2] = MagickCore::ScaleQuantumToChar((Magick::Quantum) pixels[n].blue);
850 buffer[i+3] = 255 - MagickCore::ScaleQuantumToChar((Magick::Quantum) pixels[n].opacity);
855 image = tr1::shared_ptr<QImage>(
new QImage(qbuffer, width, height, width * BPP, QImage::Format_RGBA8888, (QImageCleanupFunction) &
cleanUpBuffer, (
void*) qbuffer));
858 width = image->width();
859 height = image->height();
868 if (!audio->getNumSamples())
871 AudioDeviceManager deviceManager;
872 deviceManager.initialise (0,
878 AudioSourcePlayer audioSourcePlayer;
879 deviceManager.addAudioCallback (&audioSourcePlayer);
881 ScopedPointer<AudioBufferSource> my_source;
885 TimeSliceThread my_thread(
"Audio buffer thread");
888 my_thread.startThread();
890 AudioTransportSource transport1;
891 transport1.setSource (my_source,
894 (
double) sample_rate,
895 audio->getNumChannels());
896 transport1.setPosition (0);
897 transport1.setGain(1.0);
901 MixerAudioSource mixer;
902 mixer.addInputSource(&transport1,
false);
903 audioSourcePlayer.setSource (&mixer);
908 while (transport1.isPlaying())
910 cout <<
"playing" << endl;
914 cout <<
"DONE!!!" << endl;
917 transport1.setSource (0);
918 audioSourcePlayer.setSource (0);
919 my_thread.stopThread(500);
920 deviceManager.removeAudioCallback (&audioSourcePlayer);
921 deviceManager.closeAudioDevice();
922 deviceManager.removeAllChangeListeners();
923 deviceManager.dispatchPendingMessages();
925 cout <<
"End of Play()" << endl;
937 unsigned char* ptr_to_qbuffer = (
unsigned char*) info;
938 delete ptr_to_qbuffer;
946 audio->setSize(channels, numSamples,
false,
true,
false);
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)
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 channels.
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...
tr1::shared_ptr< QImage > GetImage()
Get pointer to Qt QImage image object.
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.
long int number
This is the frame number (starting at 1)
void AddImage(int width, int height, int bytes_per_pixel, QImage::Format type, const unsigned char *pixels_)
Add (or replace) pixel data to the frame.
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.
void AddColor(int width, int height, string color)
Add (or replace) pixel data to the frame (based on a solid color)
This class is used to expose an AudioSampleBuffer as an AudioSource in JUCE.
void ClearWaveform()
Clear the waveform image (and deallocate it's memory)
float * GetAudioSamples(int channel)
Get an array of sample data.
Exception for files that can not be found or opened.
void AddAudioSilence(int numSamples)
Add audio silence.
bool has_audio_data
This frame has been loaded with audio data.
This class represents a fraction.
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...
tr1::shared_ptr< QImage > GetWaveform(int width, int height, int Red, int Green, int Blue, int Alpha)
Get an audio waveform image.
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.
ChannelLayout ChannelsLayout()
~Frame()
Assignment operator.
int64 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.
void SetFrameNumber(int number)
Set frame number.
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.