mirror of
https://github.com/moonlight-stream/moonlight-qt.git
synced 2026-10-22 08:07:22 +00:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b23a36908c | ||
|
|
e3a7b54f90 | ||
|
|
81d5e7f014 | ||
|
|
9790c218dc | ||
|
|
6d3d51553b | ||
|
|
474591c6a5 | ||
|
|
5de0dc2713 | ||
|
|
57a34a7abb | ||
|
|
54517bda1b | ||
|
|
4291e15b60 | ||
|
|
940ae6406b | ||
|
|
6a0cf4bfd3 | ||
|
|
fee6f75223 | ||
|
|
8c93b2d54c |
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
# Moonlight PC
|
# Moonlight PC
|
||||||
|
|
||||||
[Moonlight PC](https://moonlight-stream.org) is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield, but built to run on Windows, Mac, and Linux. This client is the successor to [Moonlight Chrome](https://github.com/moonlight-stream/moonlight-chrome) for streaming on PC.
|
[Moonlight PC](https://moonlight-stream.org) is an open source PC client for NVIDIA GameStream, as used by the NVIDIA Shield.
|
||||||
|
|
||||||
Moonlight also has mobile versions for [Android](https://github.com/moonlight-stream/moonlight-android) and [iOS](https://github.com/moonlight-stream/moonlight-ios).
|
Moonlight also has mobile versions for [Android](https://github.com/moonlight-stream/moonlight-android) and [iOS](https://github.com/moonlight-stream/moonlight-ios).
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ You can follow development on our [Discord server](https://moonlight-stream.org/
|
|||||||
- Hardware accelerated video decoding on Windows, Mac, and Linux
|
- Hardware accelerated video decoding on Windows, Mac, and Linux
|
||||||
- Supports streaming at up to 120 FPS (high refresh rate monitor recommended)
|
- Supports streaming at up to 120 FPS (high refresh rate monitor recommended)
|
||||||
- Supports streaming at 720p, 1080p, 1440p, 4K, and the client PC's native screen resolution
|
- Supports streaming at 720p, 1080p, 1440p, 4K, and the client PC's native screen resolution
|
||||||
|
- HDR streaming support
|
||||||
- 7.1 surround sound audio support
|
- 7.1 surround sound audio support
|
||||||
- Support for both pointer capture (for games) and direct mouse control (for remote desktop)
|
- Support for both pointer capture (for games) and direct mouse control (for remote desktop)
|
||||||
- Support for passing system-wide keyboard shortcuts like Alt+Tab to the host
|
- Support for passing system-wide keyboard shortcuts like Alt+Tab to the host
|
||||||
|
|||||||
@@ -1288,6 +1288,7 @@ Flickable {
|
|||||||
|
|
||||||
id: decoderComboBox
|
id: decoderComboBox
|
||||||
textRole: "text"
|
textRole: "text"
|
||||||
|
enabled: !enableHdr.checked
|
||||||
model: ListModel {
|
model: ListModel {
|
||||||
id: decoderListModel
|
id: decoderListModel
|
||||||
ListElement {
|
ListElement {
|
||||||
@@ -1304,9 +1305,26 @@ Flickable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ::onActivated must be used, as it only listens for when the index is changed by a human
|
// ::onActivated must be used, as it only listens for when the index is changed by a human
|
||||||
onActivated : {
|
onActivated: {
|
||||||
StreamingPreferences.videoDecoderSelection = decoderListModel.get(currentIndex).val
|
if (enabled) {
|
||||||
|
StreamingPreferences.videoDecoderSelection = decoderListModel.get(currentIndex).val
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This handles the state of the enableHdr checkbox changing
|
||||||
|
onEnabledChanged: {
|
||||||
|
if (enabled) {
|
||||||
|
StreamingPreferences.videoDecoderSelection = decoderListModel.get(currentIndex).val
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
StreamingPreferences.videoDecoderSelection = StreamingPreferences.VDS_AUTO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolTip.delay: 1000
|
||||||
|
ToolTip.timeout: 5000
|
||||||
|
ToolTip.visible: hovered && !enabled
|
||||||
|
ToolTip.text: qsTr("Enabling HDR overrides manual decoder selections.")
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
|||||||
@@ -32,6 +32,22 @@ void StreamUtils::scaleSourceToDestinationSurface(SDL_Rect* src, SDL_Rect* dst)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StreamUtils::screenSpaceToNormalizedDeviceCoords(SDL_FRect* rect, int viewportWidth, int viewportHeight)
|
||||||
|
{
|
||||||
|
rect->x = (rect->x / (viewportWidth / 2.0f)) - 1.0f;
|
||||||
|
rect->y = (rect->y / (viewportHeight / 2.0f)) - 1.0f;
|
||||||
|
rect->w = rect->w / (viewportWidth / 2.0f);
|
||||||
|
rect->h = rect->h / (viewportHeight / 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamUtils::screenSpaceToNormalizedDeviceCoords(SDL_Rect* src, SDL_FRect* dst, int viewportWidth, int viewportHeight)
|
||||||
|
{
|
||||||
|
dst->x = ((float)src->x / (viewportWidth / 2.0f)) - 1.0f;
|
||||||
|
dst->y = ((float)src->y / (viewportHeight / 2.0f)) - 1.0f;
|
||||||
|
dst->w = (float)src->w / (viewportWidth / 2.0f);
|
||||||
|
dst->h = (float)src->h / (viewportHeight / 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
int StreamUtils::getDisplayRefreshRate(SDL_Window* window)
|
int StreamUtils::getDisplayRefreshRate(SDL_Window* window)
|
||||||
{
|
{
|
||||||
int displayIndex = SDL_GetWindowDisplayIndex(window);
|
int displayIndex = SDL_GetWindowDisplayIndex(window);
|
||||||
|
|||||||
@@ -2,6 +2,17 @@
|
|||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
|
// SDL_FRect wasn't added until 2.0.10
|
||||||
|
#if !SDL_VERSION_ATLEAST(2, 0, 10)
|
||||||
|
typedef struct SDL_FRect
|
||||||
|
{
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float w;
|
||||||
|
float h;
|
||||||
|
} SDL_FRect;
|
||||||
|
#endif
|
||||||
|
|
||||||
class StreamUtils
|
class StreamUtils
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -11,6 +22,12 @@ public:
|
|||||||
static
|
static
|
||||||
void scaleSourceToDestinationSurface(SDL_Rect* src, SDL_Rect* dst);
|
void scaleSourceToDestinationSurface(SDL_Rect* src, SDL_Rect* dst);
|
||||||
|
|
||||||
|
static
|
||||||
|
void screenSpaceToNormalizedDeviceCoords(SDL_FRect* rect, int viewportWidth, int viewportHeight);
|
||||||
|
|
||||||
|
static
|
||||||
|
void screenSpaceToNormalizedDeviceCoords(SDL_Rect* src, SDL_FRect* dst, int viewportWidth, int viewportHeight);
|
||||||
|
|
||||||
static
|
static
|
||||||
bool getRealDesktopMode(int displayIndex, SDL_DisplayMode* mode);
|
bool getRealDesktopMode(int displayIndex, SDL_DisplayMode* mode);
|
||||||
|
|
||||||
|
|||||||
@@ -464,10 +464,6 @@ bool D3D11VARenderer::initialize(PDECODER_PARAMETERS params)
|
|||||||
|
|
||||||
m_FrameWaitableObject = m_SwapChain->GetFrameLatencyWaitableObject();
|
m_FrameWaitableObject = m_SwapChain->GetFrameLatencyWaitableObject();
|
||||||
SDL_assert(m_FrameWaitableObject != nullptr);
|
SDL_assert(m_FrameWaitableObject != nullptr);
|
||||||
|
|
||||||
// Wait for the swap chain to be ready. This is required because we don't
|
|
||||||
// we're waiting after presenting in the general case, not before.
|
|
||||||
WaitForSingleObjectEx(m_FrameWaitableObject, 1000, FALSE);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
IDXGIDevice1* dxgiDevice;
|
IDXGIDevice1* dxgiDevice;
|
||||||
@@ -582,13 +578,24 @@ void D3D11VARenderer::setHdrMode(bool enabled)
|
|||||||
unlockContext(this);
|
unlockContext(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void D3D11VARenderer::waitToRender()
|
||||||
|
{
|
||||||
|
if (m_FrameWaitableObject != nullptr) {
|
||||||
|
SDL_assert(m_Windowed);
|
||||||
|
SDL_assert(m_DecoderParams.enableVsync);
|
||||||
|
|
||||||
|
// Wait for the pipeline to be ready for the next frame in V-Sync mode.
|
||||||
|
//
|
||||||
|
// This callback happens before selecting the next frame to render, so
|
||||||
|
// we can wait for the previous frame to finish prior to picking the
|
||||||
|
// next one to display. This reduces the effective display latency
|
||||||
|
// by ensuring we always render the most recent frame immediately.
|
||||||
|
WaitForSingleObjectEx(m_FrameWaitableObject, 500, FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void D3D11VARenderer::renderFrame(AVFrame* frame)
|
void D3D11VARenderer::renderFrame(AVFrame* frame)
|
||||||
{
|
{
|
||||||
if (frame == nullptr) {
|
|
||||||
// End of stream - nothing to do for us
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Acquire the context lock for rendering to prevent concurrent
|
// Acquire the context lock for rendering to prevent concurrent
|
||||||
// access from inside FFmpeg's decoding code
|
// access from inside FFmpeg's decoding code
|
||||||
lockContext(this);
|
lockContext(this);
|
||||||
@@ -674,22 +681,6 @@ void D3D11VARenderer::renderFrame(AVFrame* frame)
|
|||||||
SDL_PushEvent(&event);
|
SDL_PushEvent(&event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_FrameWaitableObject != nullptr) {
|
|
||||||
SDL_assert(m_Windowed);
|
|
||||||
SDL_assert(m_DecoderParams.enableVsync);
|
|
||||||
|
|
||||||
// Wait for the pipeline to be ready for the next frame in V-Sync mode.
|
|
||||||
//
|
|
||||||
// MSDN advises us to wait *before* doing any rendering operations,
|
|
||||||
// however that assumes the a typical game which will latch inputs,
|
|
||||||
// run the engine, draw, etc. after WaitForSingleObjectEx(). In our case,
|
|
||||||
// we actually want wait *after* our rendering operations, because our AVFrame
|
|
||||||
// is already set in stone by the time we enter this function. Waiting after
|
|
||||||
// presenting allows a more recent frame to be received before renderFrame()
|
|
||||||
// is called again.
|
|
||||||
WaitForSingleObjectEx(m_FrameWaitableObject, 1000, FALSE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void D3D11VARenderer::renderOverlay(Overlay::OverlayType type)
|
void D3D11VARenderer::renderOverlay(Overlay::OverlayType type)
|
||||||
@@ -945,12 +936,7 @@ void D3D11VARenderer::notifyOverlayUpdated(Overlay::OverlayType type)
|
|||||||
renderRect.h = newSurface->h;
|
renderRect.h = newSurface->h;
|
||||||
|
|
||||||
// Convert screen space to normalized device coordinates
|
// Convert screen space to normalized device coordinates
|
||||||
renderRect.x /= m_DisplayWidth / 2;
|
StreamUtils::screenSpaceToNormalizedDeviceCoords(&renderRect, m_DisplayWidth, m_DisplayHeight);
|
||||||
renderRect.w /= m_DisplayWidth / 2;
|
|
||||||
renderRect.y /= m_DisplayHeight / 2;
|
|
||||||
renderRect.h /= m_DisplayHeight / 2;
|
|
||||||
renderRect.x -= 1.0f;
|
|
||||||
renderRect.y -= 1.0f;
|
|
||||||
|
|
||||||
// The surface is no longer required
|
// The surface is no longer required
|
||||||
SDL_FreeSurface(newSurface);
|
SDL_FreeSurface(newSurface);
|
||||||
@@ -1290,10 +1276,7 @@ bool D3D11VARenderer::setupRenderingResources()
|
|||||||
|
|
||||||
// Convert screen space to normalized device coordinates
|
// Convert screen space to normalized device coordinates
|
||||||
SDL_FRect renderRect;
|
SDL_FRect renderRect;
|
||||||
renderRect.x = ((float)dst.x / (m_DisplayWidth / 2)) - 1.0f;
|
StreamUtils::screenSpaceToNormalizedDeviceCoords(&dst, &renderRect, m_DisplayWidth, m_DisplayHeight);
|
||||||
renderRect.y = ((float)dst.y / (m_DisplayHeight / 2)) - 1.0f;
|
|
||||||
renderRect.w = (float)dst.w / (m_DisplayWidth / 2);
|
|
||||||
renderRect.h = (float)dst.h / (m_DisplayHeight / 2);
|
|
||||||
|
|
||||||
// Don't sample from the alignment padding area since that's not part of the video
|
// Don't sample from the alignment padding area since that's not part of the video
|
||||||
SDL_assert(m_TextureAlignment != 0);
|
SDL_assert(m_TextureAlignment != 0);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public:
|
|||||||
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary**) override;
|
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary**) override;
|
||||||
virtual bool prepareDecoderContextInGetFormat(AVCodecContext* context, AVPixelFormat pixelFormat) override;
|
virtual bool prepareDecoderContextInGetFormat(AVCodecContext* context, AVPixelFormat pixelFormat) override;
|
||||||
virtual void renderFrame(AVFrame* frame) override;
|
virtual void renderFrame(AVFrame* frame) override;
|
||||||
|
virtual void waitToRender() override;
|
||||||
virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
|
virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
|
||||||
virtual void setHdrMode(bool enabled) override;
|
virtual void setHdrMode(bool enabled) override;
|
||||||
virtual int getRendererAttributes() override;
|
virtual int getRendererAttributes() override;
|
||||||
|
|||||||
@@ -475,11 +475,6 @@ void DrmRenderer::renderFrame(AVFrame* frame)
|
|||||||
AVDRMFrameDescriptor mappedFrame;
|
AVDRMFrameDescriptor mappedFrame;
|
||||||
AVDRMFrameDescriptor* drmFrame;
|
AVDRMFrameDescriptor* drmFrame;
|
||||||
|
|
||||||
if (frame == nullptr) {
|
|
||||||
// End of stream - nothing to do for us
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are acting as the frontend renderer, we'll need to have the backend
|
// If we are acting as the frontend renderer, we'll need to have the backend
|
||||||
// map this frame into a DRM PRIME descriptor that we can render.
|
// map this frame into a DRM PRIME descriptor that we can render.
|
||||||
if (m_BackendRenderer != nullptr) {
|
if (m_BackendRenderer != nullptr) {
|
||||||
|
|||||||
@@ -978,11 +978,6 @@ int DXVA2Renderer::getDecoderColorspace()
|
|||||||
|
|
||||||
void DXVA2Renderer::renderFrame(AVFrame *frame)
|
void DXVA2Renderer::renderFrame(AVFrame *frame)
|
||||||
{
|
{
|
||||||
if (frame == nullptr) {
|
|
||||||
// End of stream - nothing to do for us
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IDirect3DSurface9* surface = reinterpret_cast<IDirect3DSurface9*>(frame->data[3]);
|
IDirect3DSurface9* surface = reinterpret_cast<IDirect3DSurface9*>(frame->data[3]);
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
|
|||||||
@@ -76,11 +76,16 @@ EGLRenderer::EGLRenderer(IFFmpegRenderer *backendRenderer)
|
|||||||
m_Backend(backendRenderer),
|
m_Backend(backendRenderer),
|
||||||
m_VAO(0),
|
m_VAO(0),
|
||||||
m_BlockingSwapBuffers(false),
|
m_BlockingSwapBuffers(false),
|
||||||
|
m_LastRenderSync(EGL_NO_SYNC),
|
||||||
m_LastFrame(av_frame_alloc()),
|
m_LastFrame(av_frame_alloc()),
|
||||||
m_glEGLImageTargetTexture2DOES(nullptr),
|
m_glEGLImageTargetTexture2DOES(nullptr),
|
||||||
m_glGenVertexArraysOES(nullptr),
|
m_glGenVertexArraysOES(nullptr),
|
||||||
m_glBindVertexArrayOES(nullptr),
|
m_glBindVertexArrayOES(nullptr),
|
||||||
m_glDeleteVertexArraysOES(nullptr),
|
m_glDeleteVertexArraysOES(nullptr),
|
||||||
|
m_eglCreateSync(nullptr),
|
||||||
|
m_eglCreateSyncKHR(nullptr),
|
||||||
|
m_eglDestroySync(nullptr),
|
||||||
|
m_eglClientWaitSync(nullptr),
|
||||||
m_GlesMajorVersion(0),
|
m_GlesMajorVersion(0),
|
||||||
m_GlesMinorVersion(0),
|
m_GlesMinorVersion(0),
|
||||||
m_HasExtUnpackSubimage(false),
|
m_HasExtUnpackSubimage(false),
|
||||||
@@ -100,6 +105,10 @@ EGLRenderer::~EGLRenderer()
|
|||||||
if (m_Context) {
|
if (m_Context) {
|
||||||
// Reattach the GL context to the main thread for destruction
|
// Reattach the GL context to the main thread for destruction
|
||||||
SDL_GL_MakeCurrent(m_Window, m_Context);
|
SDL_GL_MakeCurrent(m_Window, m_Context);
|
||||||
|
if (m_LastRenderSync != EGL_NO_SYNC) {
|
||||||
|
SDL_assert(m_eglDestroySync != nullptr);
|
||||||
|
m_eglDestroySync(m_EGLDisplay, m_LastRenderSync);
|
||||||
|
}
|
||||||
if (m_ShaderProgram) {
|
if (m_ShaderProgram) {
|
||||||
glDeleteProgram(m_ShaderProgram);
|
glDeleteProgram(m_ShaderProgram);
|
||||||
}
|
}
|
||||||
@@ -214,13 +223,7 @@ void EGLRenderer::renderOverlay(Overlay::OverlayType type)
|
|||||||
free(packedPixelData);
|
free(packedPixelData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SDL_FRect wasn't added until 2.0.10
|
SDL_FRect overlayRect;
|
||||||
struct {
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
float w;
|
|
||||||
float h;
|
|
||||||
} overlayRect = {};
|
|
||||||
|
|
||||||
// These overlay positions differ from the other renderers because OpenGL
|
// These overlay positions differ from the other renderers because OpenGL
|
||||||
// places the origin in the lower-left corner instead of the upper-left.
|
// places the origin in the lower-left corner instead of the upper-left.
|
||||||
@@ -243,12 +246,7 @@ void EGLRenderer::renderOverlay(Overlay::OverlayType type)
|
|||||||
SDL_FreeSurface(newSurface);
|
SDL_FreeSurface(newSurface);
|
||||||
|
|
||||||
// Convert screen space to normalized device coordinates
|
// Convert screen space to normalized device coordinates
|
||||||
overlayRect.x /= m_ViewportWidth / 2;
|
StreamUtils::screenSpaceToNormalizedDeviceCoords(&overlayRect, m_ViewportWidth, m_ViewportHeight);
|
||||||
overlayRect.w /= m_ViewportWidth / 2;
|
|
||||||
overlayRect.y /= m_ViewportHeight / 2;
|
|
||||||
overlayRect.h /= m_ViewportHeight / 2;
|
|
||||||
overlayRect.x -= 1.0f;
|
|
||||||
overlayRect.y -= 1.0f;
|
|
||||||
|
|
||||||
OVERLAY_VERTEX verts[] =
|
OVERLAY_VERTEX verts[] =
|
||||||
{
|
{
|
||||||
@@ -626,6 +624,30 @@ bool EGLRenderer::initialize(PDECODER_PARAMETERS params)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EGL_KHR_fence_sync is an extension for EGL 1.1+
|
||||||
|
if (eglExtensions.isSupported("EGL_KHR_fence_sync")) {
|
||||||
|
// eglCreateSyncKHR() has a slightly different prototype to eglCreateSync()
|
||||||
|
m_eglCreateSyncKHR = (typeof(m_eglCreateSyncKHR))eglGetProcAddress("eglCreateSyncKHR");
|
||||||
|
m_eglDestroySync = (typeof(m_eglDestroySync))eglGetProcAddress("eglDestroySyncKHR");
|
||||||
|
m_eglClientWaitSync = (typeof(m_eglClientWaitSync))eglGetProcAddress("eglClientWaitSyncKHR");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// EGL 1.5 introduced sync support to the core specification
|
||||||
|
m_eglCreateSync = (typeof(m_eglCreateSync))eglGetProcAddress("eglCreateSync");
|
||||||
|
m_eglDestroySync = (typeof(m_eglDestroySync))eglGetProcAddress("eglDestroySync");
|
||||||
|
m_eglClientWaitSync = (typeof(m_eglClientWaitSync))eglGetProcAddress("eglClientWaitSync");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(m_eglCreateSync || m_eglCreateSyncKHR) || !m_eglDestroySync || !m_eglClientWaitSync) {
|
||||||
|
EGL_LOG(Warn, "Failed to find sync functions");
|
||||||
|
|
||||||
|
// Sub-optimal, but not fatal
|
||||||
|
m_eglCreateSync = nullptr;
|
||||||
|
m_eglCreateSyncKHR = nullptr;
|
||||||
|
m_eglDestroySync = nullptr;
|
||||||
|
m_eglClientWaitSync = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
/* Compute the video region size in order to keep the aspect ratio of the
|
/* Compute the video region size in order to keep the aspect ratio of the
|
||||||
* video stream.
|
* video stream.
|
||||||
*/
|
*/
|
||||||
@@ -825,16 +847,28 @@ bool EGLRenderer::specialize() {
|
|||||||
return err == GL_NO_ERROR;
|
return err == GL_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EGLRenderer::cleanupRenderContext()
|
||||||
|
{
|
||||||
|
// Detach the context from the render thread so the destructor can attach it
|
||||||
|
SDL_GL_MakeCurrent(m_Window, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EGLRenderer::waitToRender()
|
||||||
|
{
|
||||||
|
// Ensure our GL context is active on this thread
|
||||||
|
// See comment in renderFrame() for more details.
|
||||||
|
SDL_GL_MakeCurrent(m_Window, m_Context);
|
||||||
|
|
||||||
|
if (m_LastRenderSync != 0) {
|
||||||
|
SDL_assert(m_eglClientWaitSync != nullptr);
|
||||||
|
m_eglClientWaitSync(m_EGLDisplay, m_LastRenderSync, EGL_SYNC_FLUSH_COMMANDS_BIT, EGL_FOREVER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EGLRenderer::renderFrame(AVFrame* frame)
|
void EGLRenderer::renderFrame(AVFrame* frame)
|
||||||
{
|
{
|
||||||
EGLImage imgs[EGL_MAX_PLANES];
|
EGLImage imgs[EGL_MAX_PLANES];
|
||||||
|
|
||||||
if (frame == nullptr) {
|
|
||||||
// End of stream - unbind the GL context
|
|
||||||
SDL_GL_MakeCurrent(m_Window, nullptr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attach our GL context to the render thread
|
// Attach our GL context to the render thread
|
||||||
// NB: It should already be current, unless the SDL render event watcher
|
// NB: It should already be current, unless the SDL render event watcher
|
||||||
// performs a rendering operation (like a viewport update on resize) on
|
// performs a rendering operation (like a viewport update on resize) on
|
||||||
@@ -889,15 +923,34 @@ void EGLRenderer::renderFrame(AVFrame* frame)
|
|||||||
SDL_GL_SwapWindow(m_Window);
|
SDL_GL_SwapWindow(m_Window);
|
||||||
|
|
||||||
if (m_BlockingSwapBuffers) {
|
if (m_BlockingSwapBuffers) {
|
||||||
// This glClear() forces us to block until the buffer swap is
|
// If we this EGL implementation supports fences, use those to delay
|
||||||
// complete to continue rendering. Mesa won't actually wait
|
// rendering the next frame until this one is completed.
|
||||||
// for the swap with just glFinish() alone. Waiting here keeps us
|
if (m_eglClientWaitSync != nullptr) {
|
||||||
// in lock step with the display refresh rate. If we don't wait
|
// Delete the sync object from last render
|
||||||
// here, we'll stall on the first GL call next frame. Doing the
|
if (m_LastRenderSync != EGL_NO_SYNC) {
|
||||||
// wait here instead allows more time for a newer frame to arrive
|
m_eglDestroySync(m_EGLDisplay, m_LastRenderSync);
|
||||||
// for next renderFrame() call.
|
}
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
glFinish();
|
// Create a new sync object that will be signalled when the buffer swap is completed
|
||||||
|
if (m_eglCreateSync != nullptr) {
|
||||||
|
m_LastRenderSync = m_eglCreateSync(m_EGLDisplay, EGL_SYNC_FENCE, nullptr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SDL_assert(m_eglCreateSyncKHR != nullptr);
|
||||||
|
m_LastRenderSync = m_eglCreateSyncKHR(m_EGLDisplay, EGL_SYNC_FENCE, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// This glClear() forces us to block until the buffer swap is
|
||||||
|
// complete to continue rendering. Mesa won't actually wait
|
||||||
|
// for the swap with just glFinish() alone. Waiting here keeps us
|
||||||
|
// in lock step with the display refresh rate. If we don't wait
|
||||||
|
// here, we'll stall on the first GL call next frame. Doing the
|
||||||
|
// wait here instead allows more time for a newer frame to arrive
|
||||||
|
// for next renderFrame() call.
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glFinish();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_Backend->freeEGLImages(m_EGLDisplay, imgs);
|
m_Backend->freeEGLImages(m_EGLDisplay, imgs);
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ public:
|
|||||||
virtual ~EGLRenderer() override;
|
virtual ~EGLRenderer() override;
|
||||||
virtual bool initialize(PDECODER_PARAMETERS params) override;
|
virtual bool initialize(PDECODER_PARAMETERS params) override;
|
||||||
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
|
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
|
||||||
|
virtual void cleanupRenderContext() override;
|
||||||
|
virtual void waitToRender() override;
|
||||||
virtual void renderFrame(AVFrame* frame) override;
|
virtual void renderFrame(AVFrame* frame) override;
|
||||||
virtual bool testRenderFrame(AVFrame* frame) override;
|
virtual bool testRenderFrame(AVFrame* frame) override;
|
||||||
virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
|
virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
|
||||||
@@ -44,11 +46,16 @@ private:
|
|||||||
IFFmpegRenderer *m_Backend;
|
IFFmpegRenderer *m_Backend;
|
||||||
unsigned int m_VAO;
|
unsigned int m_VAO;
|
||||||
bool m_BlockingSwapBuffers;
|
bool m_BlockingSwapBuffers;
|
||||||
|
EGLSync m_LastRenderSync;
|
||||||
AVFrame* m_LastFrame;
|
AVFrame* m_LastFrame;
|
||||||
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_glEGLImageTargetTexture2DOES;
|
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_glEGLImageTargetTexture2DOES;
|
||||||
PFNGLGENVERTEXARRAYSOESPROC m_glGenVertexArraysOES;
|
PFNGLGENVERTEXARRAYSOESPROC m_glGenVertexArraysOES;
|
||||||
PFNGLBINDVERTEXARRAYOESPROC m_glBindVertexArrayOES;
|
PFNGLBINDVERTEXARRAYOESPROC m_glBindVertexArrayOES;
|
||||||
PFNGLDELETEVERTEXARRAYSOESPROC m_glDeleteVertexArraysOES;
|
PFNGLDELETEVERTEXARRAYSOESPROC m_glDeleteVertexArraysOES;
|
||||||
|
PFNEGLCREATESYNCPROC m_eglCreateSync;
|
||||||
|
PFNEGLCREATESYNCKHRPROC m_eglCreateSyncKHR;
|
||||||
|
PFNEGLDESTROYSYNCPROC m_eglDestroySync;
|
||||||
|
PFNEGLCLIENTWAITSYNCPROC m_eglClientWaitSync;
|
||||||
int m_GlesMajorVersion;
|
int m_GlesMajorVersion;
|
||||||
int m_GlesMinorVersion;
|
int m_GlesMinorVersion;
|
||||||
bool m_HasExtUnpackSubimage;
|
bool m_HasExtUnpackSubimage;
|
||||||
|
|||||||
@@ -345,11 +345,6 @@ bool MmalRenderer::needsTestFrame()
|
|||||||
|
|
||||||
void MmalRenderer::renderFrame(AVFrame* frame)
|
void MmalRenderer::renderFrame(AVFrame* frame)
|
||||||
{
|
{
|
||||||
if (frame == nullptr) {
|
|
||||||
// End of stream - nothing to do for us
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MMAL_BUFFER_HEADER_T* buffer = (MMAL_BUFFER_HEADER_T*)frame->data[3];
|
MMAL_BUFFER_HEADER_T* buffer = (MMAL_BUFFER_HEADER_T*)frame->data[3];
|
||||||
MMAL_STATUS_T status;
|
MMAL_STATUS_T status;
|
||||||
|
|
||||||
|
|||||||
@@ -46,8 +46,9 @@ Pacer::~Pacer()
|
|||||||
SDL_WaitThread(m_RenderThread, nullptr);
|
SDL_WaitThread(m_RenderThread, nullptr);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Send a null AVFrame to indicate end of stream on the main thread
|
// Notify the renderer that it is being destroyed soon
|
||||||
m_VsyncRenderer->renderFrame(nullptr);
|
// NB: This must happen on the same thread that calls renderFrame().
|
||||||
|
m_VsyncRenderer->cleanupRenderContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete any remaining unconsumed frames
|
// Delete any remaining unconsumed frames
|
||||||
@@ -90,6 +91,9 @@ int Pacer::renderThread(void* context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (!me->m_Stopping) {
|
while (!me->m_Stopping) {
|
||||||
|
// Wait for the renderer to be ready for the next frame
|
||||||
|
me->m_VsyncRenderer->waitToRender();
|
||||||
|
|
||||||
// Acquire the frame queue lock to protect the queue and
|
// Acquire the frame queue lock to protect the queue and
|
||||||
// the not empty condition
|
// the not empty condition
|
||||||
me->m_FrameQueueLock.lock();
|
me->m_FrameQueueLock.lock();
|
||||||
@@ -110,8 +114,9 @@ int Pacer::renderThread(void* context)
|
|||||||
me->renderLastFrameAndUnlock();
|
me->renderLastFrameAndUnlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a null AVFrame to indicate end of stream on the render thread
|
// Notify the renderer that it is being destroyed soon
|
||||||
me->m_VsyncRenderer->renderFrame(nullptr);
|
// NB: This must happen on the same thread that calls renderFrame().
|
||||||
|
me->m_VsyncRenderer->cleanupRenderContext();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,19 @@ extern "C" {
|
|||||||
#ifndef EGL_VERSION_1_5
|
#ifndef EGL_VERSION_1_5
|
||||||
typedef intptr_t EGLAttrib;
|
typedef intptr_t EGLAttrib;
|
||||||
typedef void *EGLImage;
|
typedef void *EGLImage;
|
||||||
|
|
||||||
|
typedef void *EGLSync;
|
||||||
|
#define EGL_NO_SYNC ((EGLSync)0)
|
||||||
|
#define EGL_SYNC_FENCE 0x30F9
|
||||||
|
#define EGL_FOREVER 0xFFFFFFFFFFFFFFFFull
|
||||||
|
#define EGL_SYNC_FLUSH_COMMANDS_BIT 0x0001
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(EGL_VERSION_1_5) || !defined(EGL_EGL_PROTOTYPES)
|
#if !defined(EGL_VERSION_1_5) || !defined(EGL_EGL_PROTOTYPES)
|
||||||
|
typedef EGLSync (EGLAPIENTRYP PFNEGLCREATESYNCPROC) (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
|
||||||
|
typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSYNCPROC) (EGLDisplay dpy, EGLSync sync);
|
||||||
|
typedef EGLint (EGLAPIENTRYP PFNEGLCLIENTWAITSYNCPROC) (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
|
||||||
|
|
||||||
typedef EGLImage (EGLAPIENTRYP PFNEGLCREATEIMAGEPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list);
|
typedef EGLImage (EGLAPIENTRYP PFNEGLCREATEIMAGEPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list);
|
||||||
typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEPROC) (EGLDisplay dpy, EGLImage image);
|
typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEPROC) (EGLDisplay dpy, EGLImage image);
|
||||||
typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYPROC) (EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
|
typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYPROC) (EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
|
||||||
@@ -40,6 +50,10 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGL
|
|||||||
typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
|
typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(EGL_KHR_fence_sync) || !defined(EGL_EGLEXT_PROTOTYPES)
|
||||||
|
typedef EGLSyncKHR (EGLAPIENTRYP PFNEGLCREATESYNCKHRPROC) (EGLDisplay dpy, EGLenum type, const EGLint *attrib_list);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef EGL_EXT_image_dma_buf_import
|
#ifndef EGL_EXT_image_dma_buf_import
|
||||||
#define EGL_LINUX_DMA_BUF_EXT 0x3270
|
#define EGL_LINUX_DMA_BUF_EXT 0x3270
|
||||||
#define EGL_LINUX_DRM_FOURCC_EXT 0x3271
|
#define EGL_LINUX_DRM_FOURCC_EXT 0x3271
|
||||||
@@ -102,6 +116,18 @@ public:
|
|||||||
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) = 0;
|
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) = 0;
|
||||||
virtual void renderFrame(AVFrame* frame) = 0;
|
virtual void renderFrame(AVFrame* frame) = 0;
|
||||||
|
|
||||||
|
// Called for threaded renderers to allow them to wait prior to us latching
|
||||||
|
// the next frame for rendering (as opposed to waiting on buffer swap with
|
||||||
|
// an older frame already queued for display).
|
||||||
|
virtual void waitToRender() {
|
||||||
|
// Don't wait by default
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called on the same thread as renderFrame() during destruction of the renderer
|
||||||
|
virtual void cleanupRenderContext() {
|
||||||
|
// Nothing
|
||||||
|
}
|
||||||
|
|
||||||
virtual bool testRenderFrame(AVFrame*) {
|
virtual bool testRenderFrame(AVFrame*) {
|
||||||
// If the renderer doesn't provide an explicit test routine,
|
// If the renderer doesn't provide an explicit test routine,
|
||||||
// we will always assume that any returned AVFrame can be
|
// we will always assume that any returned AVFrame can be
|
||||||
@@ -194,7 +220,7 @@ public:
|
|||||||
virtual void freeEGLImages(EGLDisplay, EGLImage[EGL_MAX_PLANES]) {}
|
virtual void freeEGLImages(EGLDisplay, EGLImage[EGL_MAX_PLANES]) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HAVE_DRM
|
#ifdef HAVE_DRM
|
||||||
// By default we can't do DRM PRIME export
|
// By default we can't do DRM PRIME export
|
||||||
virtual bool canExportDrmPrime() {
|
virtual bool canExportDrmPrime() {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -344,11 +344,6 @@ void SdlRenderer::renderFrame(AVFrame* frame)
|
|||||||
int err;
|
int err;
|
||||||
AVFrame* swFrame = nullptr;
|
AVFrame* swFrame = nullptr;
|
||||||
|
|
||||||
if (frame == nullptr) {
|
|
||||||
// End of stream - nothing to do for us
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame->hw_frames_ctx != nullptr && frame->format != AV_PIX_FMT_CUDA) {
|
if (frame->hw_frames_ctx != nullptr && frame->format != AV_PIX_FMT_CUDA) {
|
||||||
#ifdef HAVE_CUDA
|
#ifdef HAVE_CUDA
|
||||||
ReadbackRetry:
|
ReadbackRetry:
|
||||||
|
|||||||
@@ -351,11 +351,6 @@ int VAAPIRenderer::getDecoderColorspace()
|
|||||||
void
|
void
|
||||||
VAAPIRenderer::renderFrame(AVFrame* frame)
|
VAAPIRenderer::renderFrame(AVFrame* frame)
|
||||||
{
|
{
|
||||||
if (frame == nullptr) {
|
|
||||||
// End of stream - nothing to do for us
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VASurfaceID surface = (VASurfaceID)(uintptr_t)frame->data[3];
|
VASurfaceID surface = (VASurfaceID)(uintptr_t)frame->data[3];
|
||||||
AVHWDeviceContext* deviceContext = (AVHWDeviceContext*)m_HwContext->data;
|
AVHWDeviceContext* deviceContext = (AVHWDeviceContext*)m_HwContext->data;
|
||||||
AVVAAPIDeviceContext* vaDeviceContext = (AVVAAPIDeviceContext*)deviceContext->hwctx;
|
AVVAAPIDeviceContext* vaDeviceContext = (AVVAAPIDeviceContext*)deviceContext->hwctx;
|
||||||
@@ -415,6 +410,8 @@ VAAPIRenderer::renderFrame(AVFrame* frame)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_EGL) || defined(HAVE_DRM)
|
||||||
|
|
||||||
// Ensure that vaExportSurfaceHandle() is supported by the VA-API driver
|
// Ensure that vaExportSurfaceHandle() is supported by the VA-API driver
|
||||||
bool
|
bool
|
||||||
VAAPIRenderer::canExportSurfaceHandle(int layerTypeFlag) {
|
VAAPIRenderer::canExportSurfaceHandle(int layerTypeFlag) {
|
||||||
@@ -490,6 +487,8 @@ VAAPIRenderer::canExportSurfaceHandle(int layerTypeFlag) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_EGL
|
#ifdef HAVE_EGL
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ extern "C" {
|
|||||||
#include <va/va_drm.h>
|
#include <va/va_drm.h>
|
||||||
#endif
|
#endif
|
||||||
#include <libavutil/hwcontext_vaapi.h>
|
#include <libavutil/hwcontext_vaapi.h>
|
||||||
#ifdef HAVE_EGL
|
#if defined(HAVE_EGL) || defined(HAVE_DRM)
|
||||||
#include <va/va_drmcommon.h>
|
#include <va/va_drmcommon.h>
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,7 @@ public:
|
|||||||
virtual void freeEGLImages(EGLDisplay dpy, EGLImage[EGL_MAX_PLANES]) override;
|
virtual void freeEGLImages(EGLDisplay dpy, EGLImage[EGL_MAX_PLANES]) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HAVE_DRM
|
#ifdef HAVE_DRM
|
||||||
virtual bool canExportDrmPrime() override;
|
virtual bool canExportDrmPrime() override;
|
||||||
virtual bool mapDrmPrimeFrame(AVFrame* frame, AVDRMFrameDescriptor* drmDescriptor) override;
|
virtual bool mapDrmPrimeFrame(AVFrame* frame, AVDRMFrameDescriptor* drmDescriptor) override;
|
||||||
virtual void unmapDrmPrimeFrame(AVDRMFrameDescriptor* drmDescriptor) override;
|
virtual void unmapDrmPrimeFrame(AVDRMFrameDescriptor* drmDescriptor) override;
|
||||||
@@ -58,7 +58,10 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
VADisplay openDisplay(SDL_Window* window);
|
VADisplay openDisplay(SDL_Window* window);
|
||||||
|
|
||||||
|
#if defined(HAVE_EGL) || defined(HAVE_DRM)
|
||||||
bool canExportSurfaceHandle(int layerTypeFlag);
|
bool canExportSurfaceHandle(int layerTypeFlag);
|
||||||
|
#endif
|
||||||
|
|
||||||
int m_WindowSystem;
|
int m_WindowSystem;
|
||||||
AVBufferRef* m_HwContext;
|
AVBufferRef* m_HwContext;
|
||||||
|
|||||||
@@ -474,13 +474,17 @@ void VDPAURenderer::renderOverlay(VdpOutputSurface destination, Overlay::Overlay
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VDPAURenderer::waitToRender()
|
||||||
|
{
|
||||||
|
VdpOutputSurface chosenSurface = m_OutputSurface[m_NextSurfaceIndex];
|
||||||
|
|
||||||
|
// Wait for the next render target surface to be idle before proceeding
|
||||||
|
VdpTime pts;
|
||||||
|
m_VdpPresentationQueueBlockUntilSurfaceIdle(m_PresentationQueue, chosenSurface, &pts);
|
||||||
|
}
|
||||||
|
|
||||||
void VDPAURenderer::renderFrame(AVFrame* frame)
|
void VDPAURenderer::renderFrame(AVFrame* frame)
|
||||||
{
|
{
|
||||||
if (frame == nullptr) {
|
|
||||||
// End of stream - nothing to do for us
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VdpStatus status;
|
VdpStatus status;
|
||||||
VdpVideoSurface videoSurface = (VdpVideoSurface)(uintptr_t)frame->data[3];
|
VdpVideoSurface videoSurface = (VdpVideoSurface)(uintptr_t)frame->data[3];
|
||||||
|
|
||||||
@@ -529,7 +533,9 @@ void VDPAURenderer::renderFrame(AVFrame* frame)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for this frame to be off the screen
|
// Wait for this frame to be off the screen. This will usually be a no-op
|
||||||
|
// since it already happened in waitToRender(). However, that won't be the
|
||||||
|
// case is when frame pacing is enabled.
|
||||||
VdpTime pts;
|
VdpTime pts;
|
||||||
m_VdpPresentationQueueBlockUntilSurfaceIdle(m_PresentationQueue, chosenSurface, &pts);
|
m_VdpPresentationQueueBlockUntilSurfaceIdle(m_PresentationQueue, chosenSurface, &pts);
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public:
|
|||||||
virtual bool initialize(PDECODER_PARAMETERS params) override;
|
virtual bool initialize(PDECODER_PARAMETERS params) override;
|
||||||
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
|
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
|
||||||
virtual void notifyOverlayUpdated(Overlay::OverlayType type) override;
|
virtual void notifyOverlayUpdated(Overlay::OverlayType type) override;
|
||||||
|
virtual void waitToRender() override;
|
||||||
virtual void renderFrame(AVFrame* frame) override;
|
virtual void renderFrame(AVFrame* frame) override;
|
||||||
virtual bool needsTestFrame() override;
|
virtual bool needsTestFrame() override;
|
||||||
virtual int getDecoderColorspace() override;
|
virtual int getDecoderColorspace() override;
|
||||||
|
|||||||
@@ -158,14 +158,22 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void waitToRender() override
|
||||||
|
{
|
||||||
|
if (m_DisplayLink != nullptr) {
|
||||||
|
// Vsync is enabled, so wait for a swap before returning
|
||||||
|
SDL_LockMutex(m_VsyncMutex);
|
||||||
|
if (SDL_CondWaitTimeout(m_VsyncPassed, m_VsyncMutex, 100) == SDL_MUTEX_TIMEDOUT) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"V-sync wait timed out after 100 ms");
|
||||||
|
}
|
||||||
|
SDL_UnlockMutex(m_VsyncMutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Caller frees frame after we return
|
// Caller frees frame after we return
|
||||||
virtual void renderFrame(AVFrame* frame) override
|
virtual void renderFrame(AVFrame* frame) override
|
||||||
{
|
{
|
||||||
if (frame == nullptr) {
|
|
||||||
// End of stream - nothing to do for us
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
OSStatus status;
|
OSStatus status;
|
||||||
CVPixelBufferRef pixBuf = reinterpret_cast<CVPixelBufferRef>(frame->data[3]);
|
CVPixelBufferRef pixBuf = reinterpret_cast<CVPixelBufferRef>(frame->data[3]);
|
||||||
|
|
||||||
@@ -235,16 +243,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_DisplayLink != nullptr) {
|
|
||||||
// Vsync is enabled, so wait for a swap before returning
|
|
||||||
SDL_LockMutex(m_VsyncMutex);
|
|
||||||
if (SDL_CondWaitTimeout(m_VsyncPassed, m_VsyncMutex, 100) == SDL_MUTEX_TIMEDOUT) {
|
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
|
||||||
"V-sync wait timed out after 100 ms");
|
|
||||||
}
|
|
||||||
SDL_UnlockMutex(m_VsyncMutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Queue this sample for the next v-sync
|
// Queue this sample for the next v-sync
|
||||||
CMSampleTimingInfo timingInfo = {
|
CMSampleTimingInfo timingInfo = {
|
||||||
.duration = kCMTimeInvalid,
|
.duration = kCMTimeInvalid,
|
||||||
|
|||||||
+4
-9
@@ -4,12 +4,6 @@ clone_depth: 1
|
|||||||
|
|
||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
|
||||||
QTDIR: C:\Qt\5.15
|
|
||||||
- APPVEYOR_BUILD_WORKER_IMAGE: macOS-BigSur
|
|
||||||
BUILD_TARGET: macos
|
|
||||||
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu1604
|
|
||||||
BUILD_TARGET: steamlink
|
|
||||||
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu
|
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu
|
||||||
BUILD_TARGET: linux
|
BUILD_TARGET: linux
|
||||||
FFMPEG_CONFIGURE_ARGS: --enable-pic --disable-static --enable-shared --disable-all --enable-avcodec --enable-decoder=h264 --enable-decoder=hevc --enable-nvdec --enable-hwaccel=h264_nvdec --enable-hwaccel=hevc_nvdec --enable-hwaccel=h264_vaapi --enable-hwaccel=hevc_vaapi --enable-hwaccel=h264_vdpau --enable-hwaccel=hevc_vdpau
|
FFMPEG_CONFIGURE_ARGS: --enable-pic --disable-static --enable-shared --disable-all --enable-avcodec --enable-decoder=h264 --enable-decoder=hevc --enable-nvdec --enable-hwaccel=h264_nvdec --enable-hwaccel=hevc_nvdec --enable-hwaccel=h264_vaapi --enable-hwaccel=hevc_vaapi --enable-hwaccel=h264_vdpau --enable-hwaccel=hevc_vdpau
|
||||||
@@ -19,10 +13,11 @@ install:
|
|||||||
- sh: '[ "$BUILD_TARGET" != macos ] || npm install --global create-dmg'
|
- sh: '[ "$BUILD_TARGET" != macos ] || npm install --global create-dmg'
|
||||||
- sh: '[ "$BUILD_TARGET" != steamlink ] || sudo apt install -y libc6:i386 libstdc++6:i386'
|
- sh: '[ "$BUILD_TARGET" != steamlink ] || sudo apt install -y libc6:i386 libstdc++6:i386'
|
||||||
- sh: '[ "$BUILD_TARGET" != steamlink ] || git clone --depth=1 https://github.com/ValveSoftware/steamlink-sdk.git $HOME/steamlink-sdk'
|
- sh: '[ "$BUILD_TARGET" != steamlink ] || git clone --depth=1 https://github.com/ValveSoftware/steamlink-sdk.git $HOME/steamlink-sdk'
|
||||||
- sh: '[ "$BUILD_TARGET" != linux ] || sudo add-apt-repository ppa:beineri/opt-qt-5.15.2-bionic'
|
- sh: '[ "$BUILD_TARGET" != linux ] || sudo add-apt-repository ppa:cgutman/opt-qt-5.15.2-eglfsgbm-bionic'
|
||||||
|
- sh: '[ "$BUILD_TARGET" != linux ] || sudo add-apt-repository ppa:savoury1/display'
|
||||||
- sh: '[ "$BUILD_TARGET" != linux ] || sudo apt update || true'
|
- sh: '[ "$BUILD_TARGET" != linux ] || sudo apt update || true'
|
||||||
- sh: '[ "$BUILD_TARGET" != linux ] || sudo apt install -y qt515base qt515quickcontrols2 qt515svg qt515wayland nasm libgbm-dev libdrm-dev libfreetype6-dev libasound2-dev libdbus-1-dev libegl1-mesa-dev libgl1-mesa-dev libgles2-mesa-dev libglu1-mesa-dev libibus-1.0-dev libpulse-dev libudev-dev libx11-dev libxcursor-dev libxext-dev libxi-dev libxinerama-dev libxkbcommon-dev libxrandr-dev libxss-dev libxt-dev libxv-dev libxxf86vm-dev wayland-protocols libopus-dev libvdpau-dev'
|
- sh: '[ "$BUILD_TARGET" != linux ] || sudo apt install -y qt515base qt515quickcontrols2 qt515svg qt515wayland nasm libgbm-dev libdrm-dev libfreetype6-dev libasound2-dev libdbus-1-dev libegl1-mesa-dev libgl1-mesa-dev libgles2-mesa-dev libglu1-mesa-dev libibus-1.0-dev libpulse-dev libudev-dev libx11-dev libxcursor-dev libxext-dev libxi-dev libxinerama-dev libxkbcommon-dev libxrandr-dev libxss-dev libxt-dev libxv-dev libxxf86vm-dev wayland-protocols libopus-dev libvdpau-dev libdecor-0-dev'
|
||||||
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export SDL2_REV=5a5ba34b0ec55b5563198eff5eb475eb0b16f092 && git clone https://github.com/cgutman/SDL.git SDL2 && cd SDL2 && git checkout $SDL2_REV && ./configure --enable-video-kmsdrm && make -j$(nproc) && sudo make install && cd ..; fi'
|
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export SDL2_REV=45372b1c276a748e71c373880fbb14d0a92ef6c1 && git clone https://github.com/libsdl-org/SDL.git SDL2 && cd SDL2 && git checkout $SDL2_REV && ./configure --enable-video-kmsdrm && make -j$(nproc) && sudo make install && cd ..; fi'
|
||||||
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export SDL2_TTF_VER=2.0.18 && wget https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-$SDL2_TTF_VER.tar.gz && tar -xvf SDL2_ttf-$SDL2_TTF_VER.tar.gz && cd SDL2_ttf-$SDL2_TTF_VER && ./configure && make -j$(nproc) && sudo make install && cd ..; fi'
|
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export SDL2_TTF_VER=2.0.18 && wget https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-$SDL2_TTF_VER.tar.gz && tar -xvf SDL2_ttf-$SDL2_TTF_VER.tar.gz && cd SDL2_ttf-$SDL2_TTF_VER && ./configure && make -j$(nproc) && sudo make install && cd ..; fi'
|
||||||
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export LIBVA_VER=2.14.0 && git clone https://github.com/intel/libva.git && cd libva && git checkout $LIBVA_VER && ./autogen.sh && ./configure && make -j$(nproc) && sudo make install && cd ..; fi'
|
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export LIBVA_VER=2.14.0 && git clone https://github.com/intel/libva.git && cd libva && git checkout $LIBVA_VER && ./autogen.sh && ./configure && make -j$(nproc) && sudo make install && cd ..; fi'
|
||||||
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export NVHDR_VER=8.1.24.13 && wget https://github.com/FFmpeg/nv-codec-headers/releases/download/n$NVHDR_VER/nv-codec-headers-$NVHDR_VER.tar.gz && tar -xvf nv-codec-headers-$NVHDR_VER.tar.gz && cd nv-codec-headers-$NVHDR_VER && sudo make install && cd ..; fi'
|
- sh: 'if [[ "$BUILD_TARGET" = linux ]]; then export NVHDR_VER=8.1.24.13 && wget https://github.com/FFmpeg/nv-codec-headers/releases/download/n$NVHDR_VER/nv-codec-headers-$NVHDR_VER.tar.gz && tar -xvf nv-codec-headers-$NVHDR_VER.tar.gz && cd nv-codec-headers-$NVHDR_VER && sudo make install && cd ..; fi'
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ popd
|
|||||||
|
|
||||||
echo Creating AppImage
|
echo Creating AppImage
|
||||||
pushd $INSTALLER_FOLDER
|
pushd $INSTALLER_FOLDER
|
||||||
VERSION=$VERSION linuxdeployqt $DEPLOY_FOLDER/usr/share/applications/com.moonlight_stream.Moonlight.desktop -qmldir=$SOURCE_ROOT/app/gui -appimage || fail "linuxdeployqt failed!"
|
VERSION=$VERSION linuxdeployqt $DEPLOY_FOLDER/usr/share/applications/com.moonlight_stream.Moonlight.desktop -qmldir=$SOURCE_ROOT/app/gui -extra-plugins=platforms/libqeglfs.so,platforms/libqwayland-egl.so,platforms/libqwayland-generic.so,egldeviceintegrations,platformthemes,wayland-decoration-client,wayland-shell-integration,wayland-graphics-integration-client -appimage || fail "linuxdeployqt failed!"
|
||||||
popd
|
popd
|
||||||
|
|
||||||
echo Build successful
|
echo Build successful
|
||||||
Reference in new issue
Block a user