-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Create C++/OpenGL-based Video Pipeline for more efficient Recording and Frame Processing #1721
feat: Create C++/OpenGL-based Video Pipeline for more efficient Recording and Frame Processing #1721
Changes from 9 commits
499d913
34f139e
c1cd8f6
7b5a0b4
a2a73b8
642ada5
febb735
d5b007c
601492a
3e2dc5e
b574b4a
5294726
6e38032
1d064ee
7512892
6316b11
0475424
3cb2175
3dff39e
85d09a5
29e51d9
7223e1c
f5a9f54
eb2e779
c1d2041
47b5d75
b9407de
771ac77
30332f5
19f7999
6f10e10
cc83e9e
9174bd5
e0935e0
de1a2af
5ad75a1
8e138fe
859a86b
b417ee0
3d08576
885b7b8
0f18bb2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
// | ||
// Created by Marc Rousavy on 25.08.23. | ||
// | ||
|
||
#include "VideoPipeline.h" | ||
#include "OpenGLError.h" | ||
|
||
namespace vision { | ||
|
||
jni::local_ref<VideoPipeline::jhybriddata> VideoPipeline::initHybrid(jni::alias_ref<jhybridobject> jThis) { | ||
return makeCxxInstance(jThis); | ||
} | ||
|
||
VideoPipeline::VideoPipeline(jni::alias_ref<jhybridobject> jThis): _javaPart(jni::make_global(jThis)) { } | ||
|
||
VideoPipeline::~VideoPipeline() { | ||
if (_context.display != EGL_NO_DISPLAY) { | ||
eglMakeCurrent(_context.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); | ||
if (_context.surface != EGL_NO_SURFACE) { | ||
__android_log_print(ANDROID_LOG_INFO, TAG, "Destroying OpenGL Surface..."); | ||
eglDestroySurface(_context.display, _context.surface); | ||
_context.surface = EGL_NO_SURFACE; | ||
} | ||
if (_context.context != EGL_NO_CONTEXT) { | ||
__android_log_print(ANDROID_LOG_INFO, TAG, "Destroying OpenGL Context..."); | ||
eglDestroyContext(_context.display, _context.context); | ||
_context.context = EGL_NO_CONTEXT; | ||
} | ||
__android_log_print(ANDROID_LOG_INFO, TAG, "Destroying OpenGL Display..."); | ||
eglTerminate(_context.display); | ||
_context.display = EGL_NO_DISPLAY; | ||
} | ||
} | ||
|
||
void VideoPipeline::setSize(int width, int height) { | ||
_width = width; | ||
_height = height; | ||
} | ||
|
||
GLContext& VideoPipeline::getGLContext() { | ||
bool successful; | ||
// EGLDisplay | ||
if (_context.display == EGL_NO_DISPLAY) { | ||
__android_log_print(ANDROID_LOG_INFO, TAG, "Initializing EGLDisplay.."); | ||
_context.display = eglGetDisplay(EGL_DEFAULT_DISPLAY); | ||
if (_context.display == EGL_NO_DISPLAY) throw OpenGLError("Failed to get default OpenGL Display!"); | ||
|
||
EGLint major; | ||
EGLint minor; | ||
successful = eglInitialize(_context.display, &major, &minor); | ||
if (!successful) throw OpenGLError("Failed to initialize OpenGL!"); | ||
} | ||
|
||
// EGLConfig | ||
if (_context.config == nullptr) { | ||
__android_log_print(ANDROID_LOG_INFO, TAG, "Initializing EGLConfig.."); | ||
EGLint attributes[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, | ||
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, | ||
EGL_ALPHA_SIZE, 8, | ||
EGL_BLUE_SIZE, 8, | ||
EGL_GREEN_SIZE, 8, | ||
EGL_RED_SIZE, 8, | ||
EGL_DEPTH_SIZE, 0, | ||
EGL_STENCIL_SIZE, 0, | ||
EGL_NONE}; | ||
EGLint numConfigs; | ||
successful = eglChooseConfig(_context.display, attributes, &_context.config, 1, &numConfigs); | ||
if (!successful || numConfigs == 0) throw OpenGLError("Failed to choose OpenGL config!"); | ||
} | ||
|
||
// EGLContext | ||
if (_context.context == EGL_NO_CONTEXT) { | ||
__android_log_print(ANDROID_LOG_INFO, TAG, "Initializing EGLContext.."); | ||
EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; | ||
_context.context = eglCreateContext(_context.display, _context.config, nullptr, contextAttributes); | ||
if (_context.context == EGL_NO_CONTEXT) throw OpenGLError("Failed to create OpenGL context!"); | ||
} | ||
|
||
// EGLSurface | ||
if (_context.surface == EGL_NO_SURFACE) { | ||
// If we don't have a surface at all | ||
__android_log_print(ANDROID_LOG_INFO, TAG, "Initializing EGLSurface.."); | ||
EGLint attributes[] = {EGL_WIDTH, _width, | ||
EGL_HEIGHT, _height, | ||
EGL_NONE}; | ||
_context.surface = eglCreatePbufferSurface(_context.display, _context.config, attributes); | ||
} else { | ||
// If the surface size has changed we need to resize the surface | ||
int currentWidth = 0, currentHeight = 0; | ||
eglQuerySurface(_context.display, _context.surface, EGL_WIDTH, ¤tWidth); | ||
eglQuerySurface(_context.display, _context.surface, EGL_HEIGHT, ¤tHeight); | ||
if (currentWidth != _width || currentHeight != _height) { | ||
__android_log_print(ANDROID_LOG_INFO, TAG, "Resizing EGLSurface from (%i x %i) to (%i x %i)..", | ||
currentWidth, currentHeight, _width, _height); | ||
// Destroy current surface | ||
eglDestroySurface(_context.display, _context.surface); | ||
_context.surface = EGL_NO_SURFACE; | ||
// Create new one with new dimensions | ||
EGLint attributes[] = {EGL_WIDTH, _width, | ||
EGL_HEIGHT, _height, | ||
EGL_NONE}; | ||
_context.surface = eglCreatePbufferSurface(_context.display, _context.config, attributes); | ||
} | ||
} | ||
if (_context.surface == EGL_NO_SURFACE) { | ||
throw OpenGLError("Failed to create OpenGL Surface!"); | ||
} | ||
|
||
successful = eglMakeCurrent(_context.display, _context.surface, _context.surface, _context.context); | ||
if (!successful || eglGetError() != EGL_SUCCESS) throw OpenGLError("Failed to use current OpenGL context!"); | ||
|
||
return _context; | ||
} | ||
|
||
void VideoPipeline::setFrameProcessorOutputSurface(jobject surface) { | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [cpplint] reported by reviewdog 🐶 |
||
} | ||
|
||
void VideoPipeline::setRecordingSessionOutputSurface(jobject surface, jint width, jint height) { | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [cpplint] reported by reviewdog 🐶 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [cpplint] reported by reviewdog 🐶 |
||
} | ||
|
||
void VideoPipeline::registerNatives() { | ||
registerHybrid({ | ||
makeNativeMethod("initHybrid", VideoPipeline::initHybrid), | ||
makeNativeMethod("setFrameProcessorOutputSurface", VideoPipeline::setFrameProcessorOutputSurface), | ||
makeNativeMethod("setRecordingSessionOutputSurface", VideoPipeline::setRecordingSessionOutputSurface), | ||
}); | ||
} | ||
|
||
} // vision | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [cpplint] reported by reviewdog 🐶 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [cpplint] reported by reviewdog 🐶 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// | ||
// Created by Marc Rousavy on 25.08.23. | ||
// | ||
|
||
#pragma once | ||
|
||
#include <jni.h> | ||
#include <fbjni/fbjni.h> | ||
#include <EGL/egl.h> | ||
|
||
namespace vision { | ||
|
||
using namespace facebook; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [cpplint] reported by reviewdog 🐶 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [cpplint] reported by reviewdog 🐶 |
||
struct GLContext { | ||
EGLDisplay display = EGL_NO_DISPLAY; | ||
EGLSurface surface = EGL_NO_SURFACE; | ||
EGLContext context = EGL_NO_CONTEXT; | ||
EGLConfig config = nullptr; | ||
}; | ||
|
||
class VideoPipeline: public jni::HybridClass<VideoPipeline> { | ||
public: | ||
static auto constexpr kJavaDescriptor = "Lcom/mrousavy/camera/utils/VideoPipeline;"; | ||
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis); | ||
static void registerNatives(); | ||
|
||
public: | ||
~VideoPipeline(); | ||
void setFrameProcessorOutputSurface(jobject surface); | ||
void setRecordingSessionOutputSurface(jobject surface, jint width, jint height); | ||
|
||
private: | ||
// Private constructor. Use `create(..)` to create new instances. | ||
explicit VideoPipeline(jni::alias_ref<jhybridobject> jThis); | ||
|
||
private: | ||
GLContext& getGLContext(); | ||
void setSize(int width, int height); | ||
|
||
private: | ||
GLContext _context; | ||
int _width = 0; | ||
int _height = 0; | ||
|
||
private: | ||
friend HybridBase; | ||
jni::global_ref<javaobject> _javaPart; | ||
static constexpr auto TAG = "VideoPipeline"; | ||
}; | ||
|
||
} // vision | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [cpplint] reported by reviewdog 🐶 |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,6 @@ | |
|
||
#include "FrameHostObject.h" | ||
|
||
#include <android/log.h> | ||
#include <fbjni/fbjni.h> | ||
#include <jni.h> | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[cpplint] reported by reviewdog 🐶
Redundant blank line at the start of a code block should be deleted. [whitespace/blank_line] [2]