From ad57a55d127abbdda2eaec374d2cf11527430189 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sat, 21 Jul 2018 20:22:00 -0700 Subject: [PATCH] Add (untested) Steam Link video decoding/rendering --- app/app.pro | 9 +++ app/streaming/session.cpp | 19 ++++++ app/streaming/video/sl.cpp | 106 +++++++++++++++++++++++++++++++ app/streaming/video/sl.h | 28 ++++++++ config.tests/SLVideo/SLVideo.pro | 3 + config.tests/SLVideo/main.cpp | 7 ++ moonlight-qt.pro | 4 ++ 7 files changed, 176 insertions(+) create mode 100644 app/streaming/video/sl.cpp create mode 100644 app/streaming/video/sl.h create mode 100644 config.tests/SLVideo/SLVideo.pro create mode 100644 config.tests/SLVideo/main.cpp diff --git a/app/app.pro b/app/app.pro index 3934a483..f09448ff 100644 --- a/app/app.pro +++ b/app/app.pro @@ -101,6 +101,15 @@ libva { DEFINES += HAVE_LIBVA SOURCES += streaming/video/ffmpeg-renderers/vaapi.cpp + HEADERS += streaming/video/ffmpeg-renderers/vaapi.h +} +config_SLVideo { + message(SLVideo decoder/renderer selected) + + DEFINES += HAVE_SLVIDEO + LIBS += -lSLVideo + SOURCES += streaming/video/sl.cpp + HEADERS += streaming/video/sl.h } win32 { message(DXVA2 renderer selected) diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index eb2ad656..ae54dd43 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -9,6 +9,10 @@ #include "video/ffmpeg.h" #endif +#ifdef HAVE_SLVIDEO +#include "video/sl.h" +#endif + #include #include @@ -85,6 +89,21 @@ bool Session::chooseDecoder(StreamingPreferences::VideoDecoderSelection vds, SDL_Window* window, int videoFormat, int width, int height, int frameRate, IVideoDecoder*& chosenDecoder) { +#ifdef HAVE_SLVIDEO + chosenDecoder = new SLVideoDecoder(); + if (chosenDecoder->initialize(vds, window, videoFormat, width, height, frameRate)) { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "SLVideo video decoder chosen"); + return true; + } + else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "Unable to load SLVideo decoder"); + delete chosenDecoder; + chosenDecoder = nullptr; + } +#endif + #ifdef HAVE_FFMPEG chosenDecoder = new FFmpegVideoDecoder(); if (chosenDecoder->initialize(vds, window, videoFormat, width, height, frameRate)) { diff --git a/app/streaming/video/sl.cpp b/app/streaming/video/sl.cpp new file mode 100644 index 00000000..c187e9dd --- /dev/null +++ b/app/streaming/video/sl.cpp @@ -0,0 +1,106 @@ +#include "sl.h" + +SLVideoDecoder::SLVideoDecoder() + : m_VideoContext(nullptr), + m_VideoStream(nullptr) +{ + +} + +SLVideoDecoder::~SLVideoDecoder() +{ + if (m_VideoStream != nullptr) { + SLVideo_FreeStream(m_VideoStream); + } + + if (m_VideoContext != nullptr) { + SLVideo_FreeContext(m_VideoContext); + } +} + +bool +SLVideoDecoder::isHardwareAccelerated() +{ + // SLVideo is always hardware accelerated + return true; +} + +bool +SLVideoDecoder::initialize(StreamingPreferences::VideoDecoderSelection vds, + SDL_Window*, + int videoFormat, int, int, int frameRate) +{ + // SLVideo only supports hardware decoding + if (vds == StreamingPreferences::VDS_FORCE_SOFTWARE) { + return false; + } + + // SLVideo only supports H.264 + if (videoFormat != VIDEO_FORMAT_H264) { + return false; + } + + m_VideoContext = SLVideo_CreateContext(); + if (!m_VideoContext) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "SLVideo_CreateContext() failed"); + return false; + } + + // Create a low latency H.264 stream + m_VideoStream = SLVideo_CreateStream(m_VideoContext, k_ESLVideoFormatH264, 1); + if (!m_VideoStream) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "SLVideo_CreateStream() failed"); + return false; + } + + SLVideo_SetStreamTargetFramerate(m_VideoStream, frameRate, 1); + + return true; +} + +int +SLVideoDecoder::submitDecodeUnit(PDECODE_UNIT du) +{ + int err; + + err = SLVideo_BeginFrame(m_VideoStream, du->fullLength); + if (err < 0) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "SLVideo_BeginFrame() failed: %d", + err); + + // Need an IDR frame to resync + return DR_NEED_IDR; + } + + PLENTRY entry = du->bufferList; + while (entry != nullptr) { + err = SLVideo_WriteFrameData(m_VideoStream, + entry->data, + entry->length); + if (err < 0) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "SLVideo_WriteFrameData() failed: %d", + err); + + // Need an IDR frame to resync + return DR_NEED_IDR; + } + + entry = entry->next; + } + + err = SLVideo_SubmitFrame(m_VideoStream); + if (err < 0) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "SLVideo_SubmitFrame() failed: %d", + err); + + // Need an IDR frame to resync + return DR_NEED_IDR; + } + + return DR_OK; +} diff --git a/app/streaming/video/sl.h b/app/streaming/video/sl.h new file mode 100644 index 00000000..a1cc1af4 --- /dev/null +++ b/app/streaming/video/sl.h @@ -0,0 +1,28 @@ +#pragma once + +#include "decoder.h" + +#include + +class SLVideoDecoder : public IVideoDecoder +{ +public: + SLVideoDecoder(); + virtual ~SLVideoDecoder(); + virtual bool initialize(StreamingPreferences::VideoDecoderSelection vds, + SDL_Window* window, + int videoFormat, + int width, + int height, + int frameRate); + virtual bool isHardwareAccelerated(); + virtual int submitDecodeUnit(PDECODE_UNIT du); + + // Unused since rendering is done directly from the decode thread + virtual void renderFrame(SDL_UserEvent*) {} + virtual void dropFrame(SDL_UserEvent*) {} + +private: + CSLVideoContext* m_VideoContext; + CSLVideoStream* m_VideoStream; +}; diff --git a/config.tests/SLVideo/SLVideo.pro b/config.tests/SLVideo/SLVideo.pro new file mode 100644 index 00000000..a2851c4c --- /dev/null +++ b/config.tests/SLVideo/SLVideo.pro @@ -0,0 +1,3 @@ +SOURCES = main.cpp +LIBS += -lSLVideo + diff --git a/config.tests/SLVideo/main.cpp b/config.tests/SLVideo/main.cpp new file mode 100644 index 00000000..0387b337 --- /dev/null +++ b/config.tests/SLVideo/main.cpp @@ -0,0 +1,7 @@ +#include + +int main() +{ + SLVideo_CreateContext(); + return 0; +} diff --git a/moonlight-qt.pro b/moonlight-qt.pro index 2e520723..96fdd92e 100644 --- a/moonlight-qt.pro +++ b/moonlight-qt.pro @@ -6,3 +6,7 @@ SUBDIRS = \ app CONFIG += ordered + +# Run our compile tests +load(configure) +qtCompileTest(SLVideo)