kopia lustrzana https://github.com/jameshball/osci-render
Add pop-up to download ffmpeg if not already downloaded
rodzic
157676439e
commit
7b98b34b04
|
@ -45,6 +45,9 @@ private:
|
||||||
OscirenderAudioProcessor& audioProcessor;
|
OscirenderAudioProcessor& audioProcessor;
|
||||||
|
|
||||||
juce::File applicationFolder = juce::File::getSpecialLocation(juce::File::SpecialLocationType::userApplicationDataDirectory)
|
juce::File applicationFolder = juce::File::getSpecialLocation(juce::File::SpecialLocationType::userApplicationDataDirectory)
|
||||||
|
#if JUCE_MAC
|
||||||
|
.getChildFile("Application Support")
|
||||||
|
#endif
|
||||||
.getChildFile("osci-render");
|
.getChildFile("osci-render");
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,9 @@ private:
|
||||||
SosciAudioProcessor& audioProcessor;
|
SosciAudioProcessor& audioProcessor;
|
||||||
|
|
||||||
juce::File applicationFolder = juce::File::getSpecialLocation(juce::File::SpecialLocationType::userApplicationDataDirectory)
|
juce::File applicationFolder = juce::File::getSpecialLocation(juce::File::SpecialLocationType::userApplicationDataDirectory)
|
||||||
|
#if JUCE_MAC
|
||||||
|
.getChildFile("Application Support")
|
||||||
|
#endif
|
||||||
.getChildFile("osci-render");
|
.getChildFile("osci-render");
|
||||||
public:
|
public:
|
||||||
OscirenderLookAndFeel lookAndFeel;
|
OscirenderLookAndFeel lookAndFeel;
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
#include "DownloaderComponent.h"
|
||||||
|
|
||||||
|
DownloaderComponent::DownloaderComponent(juce::URL url, juce::File file, juce::String title, juce::Component* parent) : juce::ThreadWithProgressWindow(title, true, true, 1000, juce::String(), parent), url(url), file(file) {
|
||||||
|
if (url.toString(false).endsWithIgnoreCase(".gz")) {
|
||||||
|
uncompressOnFinish = true;
|
||||||
|
this->file = file.getSiblingFile(file.getFileName() + ".gz");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloaderComponent::run() {
|
||||||
|
downloader = std::make_unique<DownloaderThread>(url, file, this, taskLock, task);
|
||||||
|
downloader->startThread();
|
||||||
|
while (!threadShouldExit()) {
|
||||||
|
{
|
||||||
|
juce::CriticalSection::ScopedTryLockType lock(taskLock);
|
||||||
|
if (lock.isLocked() && task != nullptr) {
|
||||||
|
if (task->isFinished()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wait(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloaderComponent::threadComplete(bool userPressedCancel) {
|
||||||
|
if (!userPressedCancel && uncompressOnFinish && task->isFinished() && !task->hadError()) {
|
||||||
|
juce::FileInputStream input(file);
|
||||||
|
juce::GZIPDecompressorInputStream decompressedInput(&input, false, juce::GZIPDecompressorInputStream::gzipFormat);
|
||||||
|
juce::File uncompressedFile = file.getSiblingFile(file.getFileNameWithoutExtension());
|
||||||
|
juce::FileOutputStream output(uncompressedFile);
|
||||||
|
if (output.writeFromInputStream(decompressedInput, -1) < 1) {
|
||||||
|
uncompressedFile.deleteFile();
|
||||||
|
} else {
|
||||||
|
uncompressedFile.setExecutePermission(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloaderComponent::finished(juce::URL::DownloadTask* task, bool success) {
|
||||||
|
notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloaderComponent::progress(juce::URL::DownloadTask* task, juce::int64 bytesDownloaded, juce::int64 totalLength) {
|
||||||
|
setProgress((float)bytesDownloaded / (float)totalLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloaderComponent::download() {
|
||||||
|
launchThread();
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <JuceHeader.h>
|
||||||
|
|
||||||
|
class DownloaderThread : public juce::Thread {
|
||||||
|
public:
|
||||||
|
DownloaderThread(juce::URL url, juce::File file, juce::URL::DownloadTaskListener* listener, juce::CriticalSection& taskLock, std::unique_ptr<juce::URL::DownloadTask>& task) : juce::Thread("Downloader Thread"), url(url), file(file), listener(listener), taskLock(taskLock), task(task) {};
|
||||||
|
|
||||||
|
void run() override {
|
||||||
|
juce::CriticalSection::ScopedLockType lock(taskLock);
|
||||||
|
task = url.downloadToFile(file, juce::URL::DownloadTaskOptions().withListener(listener));
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
juce::URL url;
|
||||||
|
juce::File file;
|
||||||
|
juce::CriticalSection& taskLock;
|
||||||
|
juce::URL::DownloadTaskListener* listener;
|
||||||
|
std::unique_ptr<juce::URL::DownloadTask>& task;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DownloaderComponent : public juce::ThreadWithProgressWindow, public juce::URL::DownloadTaskListener {
|
||||||
|
public:
|
||||||
|
DownloaderComponent(juce::URL url, juce::File file, juce::String title, juce::Component* parent);
|
||||||
|
|
||||||
|
void download();
|
||||||
|
void run() override;
|
||||||
|
void threadComplete(bool userPressedCancel) override;
|
||||||
|
void finished(juce::URL::DownloadTask* task, bool success) override;
|
||||||
|
void progress(juce::URL::DownloadTask* task, juce::int64 bytesDownloaded, juce::int64 totalLength) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
juce::URL url;
|
||||||
|
juce::File file;
|
||||||
|
juce::CriticalSection taskLock;
|
||||||
|
std::unique_ptr<juce::URL::DownloadTask> task;
|
||||||
|
std::unique_ptr<DownloaderThread> downloader;
|
||||||
|
|
||||||
|
bool uncompressOnFinish = false;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(DownloaderComponent)
|
||||||
|
};
|
|
@ -11,7 +11,7 @@
|
||||||
#include "TexturedFragmentShader.glsl"
|
#include "TexturedFragmentShader.glsl"
|
||||||
#include "TexturedVertexShader.glsl"
|
#include "TexturedVertexShader.glsl"
|
||||||
|
|
||||||
VisualiserComponent::VisualiserComponent(juce::File ffmpegPath, std::function<void()>& haltRecording, AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, VisualiserComponent* parent, bool visualiserOnly) : ffmpegPath(ffmpegPath), haltRecording(haltRecording), settings(settings), threadManager(threadManager), visualiserOnly(visualiserOnly), AudioBackgroundThread("VisualiserComponent" + juce::String(parent != nullptr ? " Child" : ""), threadManager), parent(parent) {
|
VisualiserComponent::VisualiserComponent(juce::File ffmpegFile, std::function<void()>& haltRecording, AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, VisualiserComponent* parent, bool visualiserOnly) : ffmpegFile(ffmpegFile), haltRecording(haltRecording), settings(settings), threadManager(threadManager), visualiserOnly(visualiserOnly), AudioBackgroundThread("VisualiserComponent" + juce::String(parent != nullptr ? " Child" : ""), threadManager), parent(parent) {
|
||||||
|
|
||||||
haltRecording = [this] {
|
haltRecording = [this] {
|
||||||
setRecording(false);
|
setRecording(false);
|
||||||
|
@ -170,20 +170,25 @@ void VisualiserComponent::setFullScreen(bool fullScreen) {}
|
||||||
|
|
||||||
void VisualiserComponent::setRecording(bool recording) {
|
void VisualiserComponent::setRecording(bool recording) {
|
||||||
if (recording) {
|
if (recording) {
|
||||||
|
if (!ffmpegFile.exists()) {
|
||||||
|
ffmpegDownloader.download();
|
||||||
|
record.setToggleState(false, juce::NotificationType::sendNotification);
|
||||||
|
return;
|
||||||
|
}
|
||||||
juce::TemporaryFile tempFile = juce::TemporaryFile(".mp4");
|
juce::TemporaryFile tempFile = juce::TemporaryFile(".mp4");
|
||||||
juce::String resolution = std::to_string(renderTexture.width) + "x" + std::to_string(renderTexture.height);
|
juce::String resolution = std::to_string(renderTexture.width) + "x" + std::to_string(renderTexture.height);
|
||||||
juce::String cmd = ffmpegPath.getFullPathName() +
|
juce::String cmd = "\"" + ffmpegFile.getFullPathName() +
|
||||||
" -r " + juce::String(FRAME_RATE) + " -f rawvideo -pix_fmt rgba -s " + resolution + " -i - -threads 0 -preset fast -y -pix_fmt yuv420p -crf " + juce::String(21) + " -vf vflip " + tempFile.getFile().getFullPathName();
|
"\" -r " + juce::String(FRAME_RATE) + " -f rawvideo -pix_fmt rgba -s " + resolution + " -i - -threads 0 -preset fast -y -pix_fmt yuv420p -crf " + juce::String(21) + " -vf vflip \"" + tempFile.getFile().getFullPathName() + "\"";
|
||||||
|
|
||||||
// open pipe to ffmpeg's stdin in binary write mode
|
// open pipe to ffmpeg's stdin in binary write mode
|
||||||
#if JUCE_WINDOWS
|
#if JUCE_WINDOWS
|
||||||
ffmpeg = _popen(cmd.toStdString().c_str(), "wb");
|
ffmpeg = _popen(cmd.toStdString().c_str(), "wb");
|
||||||
#else
|
#else
|
||||||
ffmpeg = popen(cmd.toStdString().c_str(), "w");
|
ffmpeg = popen(cmd.toStdString().c_str(), "w");
|
||||||
|
#endif
|
||||||
if (ffmpeg == nullptr) {
|
if (ffmpeg == nullptr) {
|
||||||
DBG("popen failed: " + juce::String(std::strerror(errno)));
|
DBG("popen failed: " + juce::String(std::strerror(errno)));
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
framePixels.resize(renderTexture.width * renderTexture.height * 4);
|
framePixels.resize(renderTexture.width * renderTexture.height * 4);
|
||||||
setPaused(false);
|
setPaused(false);
|
||||||
} else if (ffmpeg != nullptr) {
|
} else if (ffmpeg != nullptr) {
|
||||||
|
@ -223,7 +228,7 @@ void VisualiserComponent::resized() {
|
||||||
void VisualiserComponent::popoutWindow() {
|
void VisualiserComponent::popoutWindow() {
|
||||||
setRecording(false);
|
setRecording(false);
|
||||||
record.setEnabled(false);
|
record.setEnabled(false);
|
||||||
auto visualiser = new VisualiserComponent(ffmpegPath, haltRecording, threadManager, settings, this);
|
auto visualiser = new VisualiserComponent(ffmpegFile, haltRecording, threadManager, settings, this);
|
||||||
visualiser->settings.setLookAndFeel(&getLookAndFeel());
|
visualiser->settings.setLookAndFeel(&getLookAndFeel());
|
||||||
visualiser->openSettings = openSettings;
|
visualiser->openSettings = openSettings;
|
||||||
visualiser->closeSettings = closeSettings;
|
visualiser->closeSettings = closeSettings;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "VisualiserSettings.h"
|
#include "VisualiserSettings.h"
|
||||||
#include "../components/StopwatchComponent.h"
|
#include "../components/StopwatchComponent.h"
|
||||||
#include "../img/qoixx.hpp"
|
#include "../img/qoixx.hpp"
|
||||||
|
#include "../components/DownloaderComponent.h"
|
||||||
|
|
||||||
#define FILE_RENDER_DUMMY 0
|
#define FILE_RENDER_DUMMY 0
|
||||||
#define FILE_RENDER_PNG 1
|
#define FILE_RENDER_PNG 1
|
||||||
|
@ -28,7 +29,7 @@ struct Texture {
|
||||||
class VisualiserWindow;
|
class VisualiserWindow;
|
||||||
class VisualiserComponent : public juce::Component, public AudioBackgroundThread, public juce::MouseListener, public juce::OpenGLRenderer, public juce::AsyncUpdater {
|
class VisualiserComponent : public juce::Component, public AudioBackgroundThread, public juce::MouseListener, public juce::OpenGLRenderer, public juce::AsyncUpdater {
|
||||||
public:
|
public:
|
||||||
VisualiserComponent(juce::File ffmpegPath, std::function<void()>& haltRecording, AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, VisualiserComponent* parent = nullptr, bool visualiserOnly = false);
|
VisualiserComponent(juce::File ffmpegFile, std::function<void()>& haltRecording, AudioBackgroundThreadManager& threadManager, VisualiserSettings& settings, VisualiserComponent* parent = nullptr, bool visualiserOnly = false);
|
||||||
~VisualiserComponent() override;
|
~VisualiserComponent() override;
|
||||||
|
|
||||||
std::function<void()> openSettings;
|
std::function<void()> openSettings;
|
||||||
|
@ -81,7 +82,37 @@ private:
|
||||||
long numFrames = 0;
|
long numFrames = 0;
|
||||||
std::vector<unsigned char> framePixels;
|
std::vector<unsigned char> framePixels;
|
||||||
FILE* ffmpeg = nullptr;
|
FILE* ffmpeg = nullptr;
|
||||||
juce::File ffmpegPath;
|
juce::File ffmpegFile;
|
||||||
|
juce::String ffmpegURL = juce::String("https://github.com/eugeneware/ffmpeg-static/releases/download/b6.0/") +
|
||||||
|
#if JUCE_WINDOWS
|
||||||
|
#if JUCE_64BIT
|
||||||
|
"ffmpeg-win32-x64"
|
||||||
|
#elif JUCE_32BIT
|
||||||
|
"ffmpeg-win32-ia32"
|
||||||
|
#endif
|
||||||
|
#elif JUCE_MAC
|
||||||
|
#if JUCE_ARM
|
||||||
|
"ffmpeg-darwin-arm64"
|
||||||
|
#elif JUCE_INTEL
|
||||||
|
"ffmpeg-darwin-x64"
|
||||||
|
#endif
|
||||||
|
#elif JUCE_LINUX
|
||||||
|
#if JUCE_ARM
|
||||||
|
#if JUCE_64BIT
|
||||||
|
"ffmpeg-linux-arm64"
|
||||||
|
#elif JUCE_32BIT
|
||||||
|
"ffmpeg-linux-arm"
|
||||||
|
#endif
|
||||||
|
#elif JUCE_INTEL
|
||||||
|
#if JUCE_64BIT
|
||||||
|
"ffmpeg-linux-x64"
|
||||||
|
#elif JUCE_32BIT
|
||||||
|
"ffmpeg-linux-ia32"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
+ ".gz";
|
||||||
|
DownloaderComponent ffmpegDownloader{ffmpegURL, ffmpegFile, "Downloading ffmpeg...", this};
|
||||||
|
|
||||||
Semaphore renderingSemaphore{0};
|
Semaphore renderingSemaphore{0};
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
pluginManufacturer="jameshball" aaxIdentifier="sh.ball.oscirender"
|
pluginManufacturer="jameshball" aaxIdentifier="sh.ball.oscirender"
|
||||||
cppLanguageStandard="20" projectLineFeed=" " headerPath="./include"
|
cppLanguageStandard="20" projectLineFeed=" " headerPath="./include"
|
||||||
version="2.3.0" companyName="James H Ball" companyWebsite="https://osci-render.com"
|
version="2.3.0" companyName="James H Ball" companyWebsite="https://osci-render.com"
|
||||||
companyEmail="james@ball.sh" defines="NOMINMAX=1" pluginAUMainType="'aumf'">
|
companyEmail="james@ball.sh" defines="NOMINMAX=1 INTERNET_FLAG_NO_AUTO_REDIRECT=0"
|
||||||
|
pluginAUMainType="'aumf'">
|
||||||
<MAINGROUP id="j5Ge2T" name="osci-render">
|
<MAINGROUP id="j5Ge2T" name="osci-render">
|
||||||
<GROUP id="{5ABCED88-0059-A7AF-9596-DBF91DDB0292}" name="Resources">
|
<GROUP id="{5ABCED88-0059-A7AF-9596-DBF91DDB0292}" name="Resources">
|
||||||
<GROUP id="{8930EC48-30FD-646B-9DC5-0861171F8B2E}" name="fonts">
|
<GROUP id="{8930EC48-30FD-646B-9DC5-0861171F8B2E}" name="fonts">
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
aaxIdentifier="sh.ball.sosci" cppLanguageStandard="20" projectLineFeed=" "
|
aaxIdentifier="sh.ball.sosci" cppLanguageStandard="20" projectLineFeed=" "
|
||||||
headerPath="./include" version="1.0.0" companyName="James H Ball"
|
headerPath="./include" version="1.0.0" companyName="James H Ball"
|
||||||
companyWebsite="https://osci-render.com" companyEmail="james@ball.sh"
|
companyWebsite="https://osci-render.com" companyEmail="james@ball.sh"
|
||||||
defines="NOMINMAX=1" pluginManufacturerCode="Jhba" pluginCode="Sosc"
|
defines="NOMINMAX=1 INTERNET_FLAG_NO_AUTO_REDIRECT=0" pluginManufacturerCode="Jhba"
|
||||||
pluginAUMainType="'aufx'">
|
pluginCode="Sosc" pluginAUMainType="'aufx'">
|
||||||
<MAINGROUP id="j5Ge2T" name="sosci">
|
<MAINGROUP id="j5Ge2T" name="sosci">
|
||||||
<GROUP id="{5ABCED88-0059-A7AF-9596-DBF91DDB0292}" name="Resources">
|
<GROUP id="{5ABCED88-0059-A7AF-9596-DBF91DDB0292}" name="Resources">
|
||||||
<GROUP id="{1C0FC3AA-01F6-8768-381C-200ED18AB5F2}" name="fonts">
|
<GROUP id="{1C0FC3AA-01F6-8768-381C-200ED18AB5F2}" name="fonts">
|
||||||
|
@ -67,6 +67,10 @@
|
||||||
file="Source/components/AboutComponent.cpp"/>
|
file="Source/components/AboutComponent.cpp"/>
|
||||||
<FILE id="FtOv3F" name="AboutComponent.h" compile="0" resource="0"
|
<FILE id="FtOv3F" name="AboutComponent.h" compile="0" resource="0"
|
||||||
file="Source/components/AboutComponent.h"/>
|
file="Source/components/AboutComponent.h"/>
|
||||||
|
<FILE id="aihIvJ" name="DownloaderComponent.cpp" compile="1" resource="0"
|
||||||
|
file="Source/components/DownloaderComponent.cpp"/>
|
||||||
|
<FILE id="wBQEfb" name="DownloaderComponent.h" compile="0" resource="0"
|
||||||
|
file="Source/components/DownloaderComponent.h"/>
|
||||||
<FILE id="xLAEHK" name="EffectComponent.cpp" compile="1" resource="0"
|
<FILE id="xLAEHK" name="EffectComponent.cpp" compile="1" resource="0"
|
||||||
file="Source/components/EffectComponent.cpp"/>
|
file="Source/components/EffectComponent.cpp"/>
|
||||||
<FILE id="u4UCwb" name="EffectComponent.h" compile="0" resource="0"
|
<FILE id="u4UCwb" name="EffectComponent.h" compile="0" resource="0"
|
||||||
|
|
Ładowanie…
Reference in New Issue