kopia lustrzana https://github.com/jameshball/osci-render
ffmpeg support now fully working on windows
rodzic
af4dc0c97c
commit
e96d885e4c
|
@ -0,0 +1,137 @@
|
|||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#if JUCE_WINDOWS
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
|
||||
class WriteProcess {
|
||||
public:
|
||||
|
||||
WriteProcess() {}
|
||||
|
||||
void start(juce::String cmd) {
|
||||
if (isRunning()) {
|
||||
close();
|
||||
}
|
||||
#if JUCE_WINDOWS
|
||||
cmd = "cmd /c \"" + cmd + "\"";
|
||||
|
||||
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
|
||||
|
||||
if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) {
|
||||
errorExit(TEXT("CreatePipe"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark the write handle as inheritable
|
||||
if (!SetHandleInformation(hWritePipe, HANDLE_FLAG_INHERIT, 0)) {
|
||||
CloseHandle(hReadPipe);
|
||||
CloseHandle(hWritePipe);
|
||||
errorExit(TEXT("SetHandleInformation"));
|
||||
return;
|
||||
}
|
||||
|
||||
STARTUPINFO si = { 0 };
|
||||
|
||||
si.cb = sizeof(STARTUPINFO);
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
si.hStdInput = hReadPipe; // Child process reads from here
|
||||
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); // Forward to parent stdout
|
||||
si.hStdError = GetStdHandle(STD_ERROR_HANDLE); // Forward to parent stderr
|
||||
|
||||
// Create the process
|
||||
if (!CreateProcess(NULL, const_cast<LPSTR>(cmd.toStdString().c_str()), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
|
||||
CloseHandle(hReadPipe);
|
||||
CloseHandle(hWritePipe);
|
||||
errorExit(TEXT("CreateProcess"));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
process = popen(cmd.toStdString().c_str(), "w");
|
||||
if (process == nullptr) {
|
||||
DBG("popen failed: " + juce::String(std::strerror(errno)));
|
||||
jassertfalse;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void write(void* data, size_t size) {
|
||||
#if JUCE_WINDOWS
|
||||
DWORD bytesWritten;
|
||||
if (!WriteFile(hWritePipe, data, size, &bytesWritten, NULL)) {
|
||||
errorExit(TEXT("WriteFile"));
|
||||
}
|
||||
#else
|
||||
fwrite(data, size, 1, process);
|
||||
#endif
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (isRunning()) {
|
||||
#if JUCE_WINDOWS
|
||||
// Close the write handle to signal EOF to the process
|
||||
CloseHandle(hWritePipe);
|
||||
|
||||
// Wait for the process to finish
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
|
||||
// Clean up
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(hReadPipe);
|
||||
|
||||
hWritePipe = INVALID_HANDLE_VALUE;
|
||||
hReadPipe = INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
pclose(process);
|
||||
process = nullptr;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool isRunning() {
|
||||
#if JUCE_WINDOWS
|
||||
return hWritePipe != INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
return process != nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
// from https://learn.microsoft.com/en-us/windows/win32/ProcThread/creating-a-child-process-with-redirected-input-and-output
|
||||
void errorExit(PCTSTR lpszFunction) {
|
||||
LPVOID lpMsgBuf;
|
||||
DWORD dw = GetLastError();
|
||||
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
dw,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR) &lpMsgBuf,
|
||||
0, NULL );
|
||||
|
||||
DBG("Error: " + juce::String((LPCTSTR)lpMsgBuf));
|
||||
|
||||
LocalFree(lpMsgBuf);
|
||||
jassertfalse;
|
||||
}
|
||||
#endif
|
||||
|
||||
~WriteProcess() {
|
||||
close();
|
||||
}
|
||||
|
||||
private:
|
||||
#if JUCE_WINDOWS
|
||||
HANDLE hReadPipe = INVALID_HANDLE_VALUE;
|
||||
HANDLE hWritePipe = INVALID_HANDLE_VALUE;
|
||||
PROCESS_INFORMATION pi;
|
||||
#else
|
||||
FILE* process = nullptr;
|
||||
#endif
|
||||
};
|
|
@ -195,29 +195,13 @@ void VisualiserComponent::setRecording(bool recording) {
|
|||
juce::String resolution = std::to_string(renderTexture.width) + "x" + std::to_string(renderTexture.height);
|
||||
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() + "\"";
|
||||
#if JUCE_WINDOWS
|
||||
cmd = "cmd /c \"" + cmd + "\"";
|
||||
#endif
|
||||
|
||||
// open pipe to ffmpeg's stdin in binary write mode
|
||||
#if JUCE_WINDOWS
|
||||
ffmpeg = _popen(cmd.toStdString().c_str(), "wb");
|
||||
#else
|
||||
ffmpeg = popen(cmd.toStdString().c_str(), "w");
|
||||
#endif
|
||||
if (ffmpeg == nullptr) {
|
||||
DBG("popen failed: " + juce::String(std::strerror(errno)));
|
||||
}
|
||||
ffmpegProcess.start(cmd);
|
||||
framePixels.resize(renderTexture.width * renderTexture.height * 4);
|
||||
setPaused(false);
|
||||
stopwatch.start();
|
||||
} else if (ffmpeg != nullptr) {
|
||||
#if JUCE_WINDOWS
|
||||
_pclose(ffmpeg);
|
||||
#else
|
||||
pclose(ffmpeg);
|
||||
#endif
|
||||
ffmpeg = nullptr;
|
||||
} else if (ffmpegProcess.isRunning()) {
|
||||
ffmpegProcess.close();
|
||||
}
|
||||
setBlockOnAudioThread(recording);
|
||||
numFrames = 0;
|
||||
|
@ -378,7 +362,7 @@ void VisualiserComponent::renderOpenGL() {
|
|||
// draw frame to ffmpeg
|
||||
glBindTexture(GL_TEXTURE_2D, renderTexture.id);
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, framePixels.data());
|
||||
fwrite(framePixels.data(), 4 * renderTexture.width * renderTexture.height, 1, ffmpeg);
|
||||
ffmpegProcess.write(framePixels.data(), 4 * renderTexture.width * renderTexture.height);
|
||||
}
|
||||
|
||||
renderingSemaphore.release();
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "../components/StopwatchComponent.h"
|
||||
#include "../img/qoixx.hpp"
|
||||
#include "../components/DownloaderComponent.h"
|
||||
#include "../concurrency/WriteProcess.h"
|
||||
|
||||
#define FILE_RENDER_DUMMY 0
|
||||
#define FILE_RENDER_PNG 1
|
||||
|
@ -81,7 +82,7 @@ private:
|
|||
SvgButton record{"Record", BinaryData::record_svg, juce::Colours::red, juce::Colours::red.withAlpha(0.01f)};
|
||||
long numFrames = 0;
|
||||
std::vector<unsigned char> framePixels;
|
||||
FILE* ffmpeg = nullptr;
|
||||
WriteProcess ffmpegProcess;
|
||||
juce::File ffmpegFile;
|
||||
juce::String ffmpegURL = juce::String("https://github.com/eugeneware/ffmpeg-static/releases/download/b6.0/") +
|
||||
#if JUCE_WINDOWS
|
||||
|
|
|
@ -196,6 +196,7 @@
|
|||
file="Source/concurrency/BufferConsumer.h"/>
|
||||
<FILE id="L9aCHY" name="readerwritercircularbuffer.h" compile="0" resource="0"
|
||||
file="Source/concurrency/readerwritercircularbuffer.h"/>
|
||||
<FILE id="aat2Je" name="WriteProcess.h" compile="0" resource="0" file="Source/concurrency/WriteProcess.h"/>
|
||||
</GROUP>
|
||||
<GROUP id="{A3E24187-62A5-AB8D-8837-14043B89A640}" name="gpla">
|
||||
<FILE id="KvDV8j" name="LineArtParser.cpp" compile="1" resource="0"
|
||||
|
|
|
@ -101,6 +101,7 @@
|
|||
<FILE id="F5kUMH" name="BlockingQueue.h" compile="0" resource="0" file="Source/concurrency/BlockingQueue.h"/>
|
||||
<FILE id="WQ2W15" name="BufferConsumer.h" compile="0" resource="0"
|
||||
file="Source/concurrency/BufferConsumer.h"/>
|
||||
<FILE id="ALHDU9" name="WriteProcess.h" compile="0" resource="0" file="Source/concurrency/WriteProcess.h"/>
|
||||
</GROUP>
|
||||
<GROUP id="{92CEA658-C82C-9CEB-15EB-945EF6B6B5C8}" name="shape">
|
||||
<FILE id="VdK1eT" name="OsciPoint.cpp" compile="1" resource="0" file="Source/shape/OsciPoint.cpp"/>
|
||||
|
|
Ładowanie…
Reference in New Issue