From da0244c5387cab3ccd682d8de24085720d6f6501 Mon Sep 17 00:00:00 2001 From: ns6089 <61738816+ns6089@users.noreply.github.com> Date: Wed, 15 May 2024 16:22:42 +0300 Subject: [PATCH] Support YUV 4:4:4 formats --- app/cli/commandlineparser.cpp | 6 +- app/gui/SettingsView.qml | 31 ++++- app/settings/streamingpreferences.cpp | 12 +- app/settings/streamingpreferences.h | 5 +- app/streaming/session.cpp | 55 +++++++- app/streaming/session.h | 51 ++++--- app/streaming/video/ffmpeg-renderers/plvk.cpp | 18 ++- app/streaming/video/ffmpeg-renderers/plvk.h | 1 + app/streaming/video/ffmpeg.cpp | 60 ++++++++- app/streaming/video/ffmpeg.h | 6 + app/streaming/video/ffmpeg_videosamples.cpp | 127 ++++++++++++++++++ moonlight-common-c/moonlight-common-c | 2 +- 12 files changed, 332 insertions(+), 42 deletions(-) diff --git a/app/cli/commandlineparser.cpp b/app/cli/commandlineparser.cpp index 37a98321..f932e5c2 100644 --- a/app/cli/commandlineparser.cpp +++ b/app/cli/commandlineparser.cpp @@ -374,6 +374,7 @@ void StreamCommandLineParser::parse(const QStringList &args, StreamingPreference parser.addToggleOption("keep-awake", "prevent display sleep while streaming"); parser.addToggleOption("performance-overlay", "show performance overlay"); parser.addToggleOption("hdr", "HDR streaming"); + parser.addToggleOption("yuv444", "YUV 4:4:4 sampling, if supported"); parser.addChoiceOption("capture-system-keys", "capture system key combos", m_CaptureSysKeysModeMap.keys()); parser.addChoiceOption("video-codec", "video codec", m_VideoCodecMap.keys()); parser.addChoiceOption("video-decoder", "video decoder", m_VideoDecoderMap.keys()); @@ -425,7 +426,7 @@ void StreamCommandLineParser::parse(const QStringList &args, StreamingPreference } } else if (displaySet || parser.isSet("fps")) { preferences->bitrateKbps = preferences->getDefaultBitrate( - preferences->width, preferences->height, preferences->fps); + preferences->width, preferences->height, preferences->fps, preferences->enableYUV444); } // Resolve --packet-size option @@ -493,6 +494,9 @@ void StreamCommandLineParser::parse(const QStringList &args, StreamingPreference // Resolve --hdr and --no-hdr options preferences->enableHdr = parser.getToggleOptionValue("hdr", preferences->enableHdr); + + // Resolve --yuv444 and --no-yuv444 options + preferences->enableYUV444 = parser.getToggleOptionValue("yuv444", preferences->enableYUV444); // Resolve --capture-system-keys option if (parser.isSet("capture-system-keys")) { diff --git a/app/gui/SettingsView.qml b/app/gui/SettingsView.qml index 6f5ddd98..3806b45f 100644 --- a/app/gui/SettingsView.qml +++ b/app/gui/SettingsView.qml @@ -283,7 +283,8 @@ Flickable { StreamingPreferences.bitrateKbps = StreamingPreferences.getDefaultBitrate(StreamingPreferences.width, StreamingPreferences.height, - StreamingPreferences.fps); + StreamingPreferences.fps, + StreamingPreferences.enableYUV444); slider.value = StreamingPreferences.bitrateKbps } @@ -448,7 +449,8 @@ Flickable { StreamingPreferences.bitrateKbps = StreamingPreferences.getDefaultBitrate(StreamingPreferences.width, StreamingPreferences.height, - StreamingPreferences.fps); + StreamingPreferences.fps, + StreamingPreferences.enableYUV444); slider.value = StreamingPreferences.bitrateKbps } @@ -1615,6 +1617,31 @@ Flickable { qsTr("HDR streaming is not supported on this PC.") } + CheckBox { + id: enableYUV444 + width: parent.width + text: qsTr("Enable YUV 4:4:4 (Experimental)") + font.pointSize: 12 + + checked: StreamingPreferences.enableYUV444 + onCheckedChanged: { + StreamingPreferences.enableYUV444 = checked + StreamingPreferences.bitrateKbps = StreamingPreferences.getDefaultBitrate(StreamingPreferences.width, + StreamingPreferences.height, + StreamingPreferences.fps, + StreamingPreferences.enableYUV444); + slider.value = StreamingPreferences.bitrateKbps + } + + ToolTip.delay: 1000 + ToolTip.timeout: 5000 + ToolTip.visible: hovered + ToolTip.text: enabled ? + qsTr("Good for streaming desktop and text-heavy games, not very good for fast-paced games.") + : + qsTr("YUV 4:4:4 is not supported on this PC.") + } + CheckBox { id: enableMdns width: parent.width diff --git a/app/settings/streamingpreferences.cpp b/app/settings/streamingpreferences.cpp index df654e42..ba3d8002 100644 --- a/app/settings/streamingpreferences.cpp +++ b/app/settings/streamingpreferences.cpp @@ -23,6 +23,7 @@ #define SER_AUDIOCFG "audiocfg" #define SER_VIDEOCFG "videocfg" #define SER_HDR "hdr" +#define SER_YUV444 "yuv444" #define SER_VIDEODEC "videodec" #define SER_WINDOWMODE "windowmode" #define SER_MDNS "mdns" @@ -117,7 +118,8 @@ void StreamingPreferences::reload() width = settings.value(SER_WIDTH, 1280).toInt(); height = settings.value(SER_HEIGHT, 720).toInt(); fps = settings.value(SER_FPS, 60).toInt(); - bitrateKbps = settings.value(SER_BITRATE, getDefaultBitrate(width, height, fps)).toInt(); + enableYUV444 = settings.value(SER_YUV444, false).toBool(); + bitrateKbps = settings.value(SER_BITRATE, getDefaultBitrate(width, height, fps, enableYUV444)).toInt(); enableVsync = settings.value(SER_VSYNC, true).toBool(); gameOptimizations = settings.value(SER_GAMEOPTS, true).toBool(); playAudioOnHost = settings.value(SER_HOSTAUDIO, false).toBool(); @@ -322,6 +324,7 @@ void StreamingPreferences::save() settings.setValue(SER_SHOWPERFOVERLAY, showPerformanceOverlay); settings.setValue(SER_AUDIOCFG, static_cast(audioConfig)); settings.setValue(SER_HDR, enableHdr); + settings.setValue(SER_YUV444, enableYUV444); settings.setValue(SER_VIDEOCFG, static_cast(videoCodecConfig)); settings.setValue(SER_VIDEODEC, static_cast(videoDecoderSelection)); settings.setValue(SER_WINDOWMODE, static_cast(windowMode)); @@ -337,7 +340,7 @@ void StreamingPreferences::save() settings.setValue(SER_KEEPAWAKE, keepAwake); } -int StreamingPreferences::getDefaultBitrate(int width, int height, int fps) +int StreamingPreferences::getDefaultBitrate(int width, int height, int fps, bool yuv444) { // Don't scale bitrate linearly beyond 60 FPS. It's definitely not a linear // bitrate increase for frame rate once we get to values that high. @@ -385,5 +388,10 @@ int StreamingPreferences::getDefaultBitrate(int width, int height, int fps) } } + if (yuv444) { + // This is rough estimation based on the fact that 4:4:4 doubles the amount of raw YUV data compared to 4:2:0 + resolutionFactor *= 2; + } + return qRound(resolutionFactor * frameRateFactor) * 1000; } diff --git a/app/settings/streamingpreferences.h b/app/settings/streamingpreferences.h index 6a99833e..14aa74a0 100644 --- a/app/settings/streamingpreferences.h +++ b/app/settings/streamingpreferences.h @@ -12,7 +12,7 @@ public: static StreamingPreferences* get(QQmlEngine *qmlEngine = nullptr); Q_INVOKABLE static int - getDefaultBitrate(int width, int height, int fps); + getDefaultBitrate(int width, int height, int fps, bool yuv444); Q_INVOKABLE void save(); @@ -125,6 +125,7 @@ public: Q_PROPERTY(AudioConfig audioConfig MEMBER audioConfig NOTIFY audioConfigChanged) Q_PROPERTY(VideoCodecConfig videoCodecConfig MEMBER videoCodecConfig NOTIFY videoCodecConfigChanged) Q_PROPERTY(bool enableHdr MEMBER enableHdr NOTIFY enableHdrChanged) + Q_PROPERTY(bool enableYUV444 MEMBER enableYUV444 NOTIFY enableYUV444Changed) Q_PROPERTY(VideoDecoderSelection videoDecoderSelection MEMBER videoDecoderSelection NOTIFY videoDecoderSelectionChanged) Q_PROPERTY(WindowMode windowMode MEMBER windowMode NOTIFY windowModeChanged) Q_PROPERTY(WindowMode recommendedFullScreenMode MEMBER recommendedFullScreenMode CONSTANT) @@ -169,6 +170,7 @@ public: AudioConfig audioConfig; VideoCodecConfig videoCodecConfig; bool enableHdr; + bool enableYUV444; VideoDecoderSelection videoDecoderSelection; WindowMode windowMode; WindowMode recommendedFullScreenMode; @@ -191,6 +193,7 @@ signals: void audioConfigChanged(); void videoCodecConfigChanged(); void enableHdrChanged(); + void enableYUV444Changed(); void videoDecoderSelectionChanged(); void uiDisplayModeChanged(); void windowModeChanged(); diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index d3795fee..c833134e 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -597,15 +597,22 @@ bool Session::initialize() return false; } + LiInitializeStreamConfiguration(&m_StreamConfig); + m_StreamConfig.width = m_Preferences->width; + m_StreamConfig.height = m_Preferences->height; + + int x, y, width, height; + getWindowDimensions(x, y, width, height); + // Create a hidden window to use for decoder initialization tests - SDL_Window* testWindow = SDL_CreateWindow("", 0, 0, 1280, 720, + SDL_Window* testWindow = SDL_CreateWindow("", x, y, width, height, SDL_WINDOW_HIDDEN | StreamUtils::getPlatformWindowFlags()); if (!testWindow) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to create test window with platform flags: %s", SDL_GetError()); - testWindow = SDL_CreateWindow("", 0, 0, 1280, 720, SDL_WINDOW_HIDDEN); + testWindow = SDL_CreateWindow("", x, y, width, height, SDL_WINDOW_HIDDEN); if (!testWindow) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create window for hardware decode test: %s", @@ -621,9 +628,6 @@ bool Session::initialize() LiInitializeVideoCallbacks(&m_VideoCallbacks); m_VideoCallbacks.setup = drSetup; - LiInitializeStreamConfiguration(&m_StreamConfig); - m_StreamConfig.width = m_Preferences->width; - m_StreamConfig.height = m_Preferences->height; m_StreamConfig.fps = m_Preferences->fps; m_StreamConfig.bitrate = m_Preferences->bitrateKbps; @@ -677,10 +681,15 @@ bool Session::initialize() CHANNEL_MASK_FROM_AUDIO_CONFIGURATION(m_StreamConfig.audioConfiguration)); // Start with all codecs and profiles in priority order + m_SupportedVideoFormats.append(VIDEO_FORMAT_AV1_HIGH10_444); m_SupportedVideoFormats.append(VIDEO_FORMAT_AV1_MAIN10); + m_SupportedVideoFormats.append(VIDEO_FORMAT_H265_REXT10_444); m_SupportedVideoFormats.append(VIDEO_FORMAT_H265_MAIN10); + m_SupportedVideoFormats.append(VIDEO_FORMAT_AV1_HIGH8_444); m_SupportedVideoFormats.append(VIDEO_FORMAT_AV1_MAIN8); + m_SupportedVideoFormats.append(VIDEO_FORMAT_H265_REXT8_444); m_SupportedVideoFormats.append(VIDEO_FORMAT_H265); + m_SupportedVideoFormats.append(VIDEO_FORMAT_H264_HIGH8_444); m_SupportedVideoFormats.append(VIDEO_FORMAT_H264); // Mask off 10-bit codecs right away if HDR is not enabled @@ -688,6 +697,11 @@ bool Session::initialize() m_SupportedVideoFormats.removeByMask(VIDEO_FORMAT_MASK_10BIT); } + // Mask off YUV 4:4:4 codecs right away if the option is not enabled + if (!m_Preferences->enableYUV444) { + m_SupportedVideoFormats.removeByMask(VIDEO_FORMAT_MASK_YUV444); + } + switch (m_Preferences->videoCodecConfig) { case StreamingPreferences::VCC_AUTO: @@ -969,6 +983,37 @@ bool Session::validateLaunch(SDL_Window* testWindow) } } + if (m_Preferences->enableYUV444) { + if (!(m_Computer->serverCodecModeSupport & SCM_MASK_YUV444)) { + emitLaunchWarning(tr("Your host PC doesn't support YUV 4:4:4 streaming.")); + m_SupportedVideoFormats.removeByMask(VIDEO_FORMAT_MASK_YUV444); + } + else if (m_Preferences->videoDecoderSelection != StreamingPreferences::VDS_FORCE_SOFTWARE) { + m_SupportedVideoFormats.removeByMask(~m_SupportedVideoFormats.maskByServerCodecModes(m_Computer->serverCodecModeSupport)); + + if (!m_SupportedVideoFormats.isEmpty() && + !(m_SupportedVideoFormats.front() & VIDEO_FORMAT_MASK_YUV444)) { + emitLaunchWarning(tr("Your host PC doesn't support YUV 4:4:4 streaming for selected video codec.")); + } + else { + while (!m_SupportedVideoFormats.isEmpty() && + (m_SupportedVideoFormats.front() & VIDEO_FORMAT_MASK_YUV444) && + !isHardwareDecodeAvailable(testWindow, + m_Preferences->videoDecoderSelection, + m_SupportedVideoFormats.front(), + m_StreamConfig.width, + m_StreamConfig.height, + m_StreamConfig.fps)) { + m_SupportedVideoFormats.removeFirst(); + } + if (!m_SupportedVideoFormats.isEmpty() && + !(m_SupportedVideoFormats.front() & VIDEO_FORMAT_MASK_YUV444)) { + emitLaunchWarning(tr("This PC's GPU doesn't support YUV 4:4:4 decoding for selected video codec.")); + } + } + } + } + if (m_StreamConfig.width >= 3840) { // Only allow 4K on GFE 3.x+ if (m_Computer->gfeVersion.isEmpty() || m_Computer->gfeVersion.startsWith("2.")) { diff --git a/app/streaming/session.h b/app/streaming/session.h index 0abe2a8f..cdba28ca 100644 --- a/app/streaming/session.h +++ b/app/streaming/session.h @@ -41,36 +41,33 @@ public: int maskByServerCodecModes(int serverCodecModes) { - int val = *this; + int mask = 0; + + const QMap mapping = { + {SCM_H264, VIDEO_FORMAT_H264}, + {SCM_H264_HIGH8_444, VIDEO_FORMAT_H264_HIGH8_444}, + {SCM_HEVC, VIDEO_FORMAT_H265}, + {SCM_HEVC_MAIN10, VIDEO_FORMAT_H265_MAIN10}, + {SCM_HEVC_REXT8_444, VIDEO_FORMAT_H265_REXT8_444}, + {SCM_HEVC_REXT10_444, VIDEO_FORMAT_H265_REXT10_444}, + {SCM_AV1_MAIN8, VIDEO_FORMAT_AV1_MAIN8}, + {SCM_AV1_MAIN10, VIDEO_FORMAT_AV1_MAIN10}, + {SCM_AV1_HIGH8_444, VIDEO_FORMAT_AV1_HIGH8_444}, + {SCM_AV1_HIGH10_444, VIDEO_FORMAT_AV1_HIGH10_444}, + }; + + for (QMap::const_iterator it = mapping.cbegin(); it != mapping.cend(); ++it) { + if (serverCodecModes & it.key()) { + mask |= it.value(); + serverCodecModes &= ~it.key(); + } + } // Make sure nobody forgets to update this for new SCM values - SDL_assert((serverCodecModes & ~(SCM_MASK_H264 | SCM_MASK_HEVC | SCM_MASK_AV1)) == 0); + SDL_assert(serverCodecModes == 0); - // H.264 SCM masks - if (!(serverCodecModes & SCM_H264)) { - val &= ~VIDEO_FORMAT_H264; - } - SDL_assert((serverCodecModes & SCM_MASK_H264 & ~SCM_H264) == 0); - - // HEVC SCM masks - if (!(serverCodecModes & SCM_HEVC)) { - val &= ~VIDEO_FORMAT_H265; - } - if (!(serverCodecModes & SCM_HEVC_MAIN10)) { - val &= ~VIDEO_FORMAT_H265_MAIN10; - } - SDL_assert((serverCodecModes & SCM_MASK_HEVC & ~(SCM_HEVC | SCM_HEVC_MAIN10)) == 0); - - // AV1 SCM masks - if (!(serverCodecModes & SCM_AV1_MAIN8)) { - val &= ~VIDEO_FORMAT_AV1_MAIN8; - } - if (!(serverCodecModes & SCM_AV1_MAIN10)) { - val &= ~VIDEO_FORMAT_AV1_MAIN10; - } - SDL_assert((serverCodecModes & SCM_MASK_AV1 & ~(SCM_AV1_MAIN8 | SCM_AV1_MAIN10)) == 0); - - return val; + int val = *this; + return val & mask; } }; diff --git a/app/streaming/video/ffmpeg-renderers/plvk.cpp b/app/streaming/video/ffmpeg-renderers/plvk.cpp index 0e245d44..479d5eba 100644 --- a/app/streaming/video/ffmpeg-renderers/plvk.cpp +++ b/app/streaming/video/ffmpeg-renderers/plvk.cpp @@ -917,9 +917,16 @@ int PlVkRenderer::getRendererAttributes() return RENDERER_ATTRIBUTE_HDR_SUPPORT; } +int PlVkRenderer::getDecoderColorspace() +{ + // We rely on libplacebo for color conversion, pick colorspace with the same primaries as sRGB + return COLORSPACE_REC_709; +} + int PlVkRenderer::getDecoderColorRange() { - // Explicitly set the color range to full to fix raised black levels on OLED displays + // Explicitly set the color range to full to fix raised black levels on OLED displays, + // should also reduce banding artifacts in all situations return COLOR_RANGE_FULL; } @@ -948,6 +955,15 @@ bool PlVkRenderer::isPixelFormatSupported(int videoFormat, AVPixelFormat pixelFo // Vulkan frames are always supported return true; } + else if (videoFormat & VIDEO_FORMAT_MASK_YUV444) { + if (videoFormat & VIDEO_FORMAT_MASK_10BIT) { + return pixelFormat == AV_PIX_FMT_YUV444P10; + } + else { + return pixelFormat == AV_PIX_FMT_YUV444P || + pixelFormat == AV_PIX_FMT_YUVJ444P; + } + } else if (videoFormat & VIDEO_FORMAT_MASK_10BIT) { switch (pixelFormat) { case AV_PIX_FMT_P010: diff --git a/app/streaming/video/ffmpeg-renderers/plvk.h b/app/streaming/video/ffmpeg-renderers/plvk.h index cc2788d7..00c67bfa 100644 --- a/app/streaming/video/ffmpeg-renderers/plvk.h +++ b/app/streaming/video/ffmpeg-renderers/plvk.h @@ -23,6 +23,7 @@ public: virtual void notifyOverlayUpdated(Overlay::OverlayType) override; virtual bool notifyWindowChanged(PWINDOW_STATE_CHANGE_INFO) override; virtual int getRendererAttributes() override; + virtual int getDecoderColorspace() override; virtual int getDecoderColorRange() override; virtual int getDecoderCapabilities() override; virtual bool needsTestFrame() override; diff --git a/app/streaming/video/ffmpeg.cpp b/app/streaming/video/ffmpeg.cpp index 751d06d4..8f5ff00a 100644 --- a/app/streaming/video/ffmpeg.cpp +++ b/app/streaming/video/ffmpeg.cpp @@ -537,6 +537,26 @@ bool FFmpegVideoDecoder::completeInitialization(const AVCodec* decoder, enum AVP m_Pkt->data = (uint8_t*)k_AV1Main10TestFrame; m_Pkt->size = sizeof(k_AV1Main10TestFrame); break; + case VIDEO_FORMAT_H264_HIGH8_444: + m_Pkt->data = (uint8_t*)k_h264High_444TestFrame; + m_Pkt->size = sizeof(k_h264High_444TestFrame); + break; + case VIDEO_FORMAT_H265_REXT8_444: + m_Pkt->data = (uint8_t*)k_HEVCRExt8_444TestFrame; + m_Pkt->size = sizeof(k_HEVCRExt8_444TestFrame); + break; + case VIDEO_FORMAT_H265_REXT10_444: + m_Pkt->data = (uint8_t*)k_HEVCRExt10_444TestFrame; + m_Pkt->size = sizeof(k_HEVCRExt10_444TestFrame); + break; + case VIDEO_FORMAT_AV1_HIGH8_444: + m_Pkt->data = (uint8_t*)k_AV1High8_444TestFrame; + m_Pkt->size = sizeof(k_AV1High8_444TestFrame); + break; + case VIDEO_FORMAT_AV1_HIGH10_444: + m_Pkt->data = (uint8_t*)k_AV1High10_444TestFrame; + m_Pkt->size = sizeof(k_AV1High10_444TestFrame); + break; default: SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No test frame for format: %x", @@ -692,16 +712,33 @@ void FFmpegVideoDecoder::stringifyVideoStats(VIDEO_STATS& stats, char* output, i codecString = "H.264"; break; + case VIDEO_FORMAT_H264_HIGH8_444: + codecString = "H.264 4:4:4"; + break; + case VIDEO_FORMAT_H265: codecString = "HEVC"; break; + case VIDEO_FORMAT_H265_REXT8_444: + codecString = "HEVC 4:4:4"; + break; + case VIDEO_FORMAT_H265_MAIN10: if (LiGetCurrentHostDisplayHdrMode()) { - codecString = "HEVC Main 10 HDR"; + codecString = "HEVC 10-bit HDR"; } else { - codecString = "HEVC Main 10 SDR"; + codecString = "HEVC 10-bit SDR"; + } + break; + + case VIDEO_FORMAT_H265_REXT10_444: + if (LiGetCurrentHostDisplayHdrMode()) { + codecString = "HEVC 10-bit HDR 4:4:4"; + } + else { + codecString = "HEVC 10-bit SDR 4:4:4"; } break; @@ -709,6 +746,10 @@ void FFmpegVideoDecoder::stringifyVideoStats(VIDEO_STATS& stats, char* output, i codecString = "AV1"; break; + case VIDEO_FORMAT_AV1_HIGH8_444: + codecString = "AV1 4:4:4"; + break; + case VIDEO_FORMAT_AV1_MAIN10: if (LiGetCurrentHostDisplayHdrMode()) { codecString = "AV1 10-bit HDR"; @@ -718,6 +759,15 @@ void FFmpegVideoDecoder::stringifyVideoStats(VIDEO_STATS& stats, char* output, i } break; + case VIDEO_FORMAT_AV1_HIGH10_444: + if (LiGetCurrentHostDisplayHdrMode()) { + codecString = "AV1 10-bit HDR 4:4:4"; + } + else { + codecString = "AV1 10-bit SDR 4:4:4"; + } + break; + default: SDL_assert(false); codecString = "UNKNOWN"; @@ -1254,6 +1304,12 @@ bool FFmpegVideoDecoder::tryInitializeHwAccelDecoder(PDECODER_PARAMETERS params, break; } + // TODO: reexamine this + if ((params->videoFormat & VIDEO_FORMAT_MASK_YUV444) && config->device_type != AV_HWDEVICE_TYPE_VULKAN) { + // We only support YUV 4:4:4 decoding on Vulkan through libplacebo + continue; + } + // Initialize the hardware codec and submit a test frame if the renderer needs it IFFmpegRenderer::InitFailureReason failureReason; if (tryInitializeRenderer(decoder, AV_PIX_FMT_NONE, params, config, &failureReason, diff --git a/app/streaming/video/ffmpeg.h b/app/streaming/video/ffmpeg.h index 2dd387fa..7dbcd503 100644 --- a/app/streaming/video/ffmpeg.h +++ b/app/streaming/video/ffmpeg.h @@ -119,4 +119,10 @@ private: static const uint8_t k_HEVCMain10TestFrame[]; static const uint8_t k_AV1Main8TestFrame[]; static const uint8_t k_AV1Main10TestFrame[]; + static const uint8_t k_h264High_444TestFrame[]; + static const uint8_t k_HEVCRExt8_444TestFrame[]; + static const uint8_t k_HEVCRExt10_444TestFrame[]; + static const uint8_t k_AV1High8_444TestFrame[]; + static const uint8_t k_AV1High10_444TestFrame[]; + }; diff --git a/app/streaming/video/ffmpeg_videosamples.cpp b/app/streaming/video/ffmpeg_videosamples.cpp index 66f0f8c1..d2552396 100644 --- a/app/streaming/video/ffmpeg_videosamples.cpp +++ b/app/streaming/video/ffmpeg_videosamples.cpp @@ -41,3 +41,130 @@ const uint8_t FFmpegVideoDecoder::k_AV1Main8TestFrame[] = { const uint8_t FFmpegVideoDecoder::k_AV1Main10TestFrame[] = { 0x12, 0x00, 0x0A, 0x10, 0x00, 0x00, 0x00, 0x43, 0xFC, 0x13, 0xFC, 0x0B, 0x3C, 0x02, 0x4E, 0xA8, 0x30, 0x30, 0x30, 0xA0, 0x32, 0x8B, 0x81, 0x80, 0x00, 0x10, 0x00, 0x4B, 0x00, 0x10, 0xC2, 0xFB, 0xE1, 0x52, 0x49, 0xFF, 0xE0, 0x00, 0x28, 0xB8, 0xB4, 0x5D, 0x8F, 0xF0, 0x01, 0x33, 0xAC, 0x5D, 0xD3, 0xCB, 0xF0, 0xFB, 0x2F, 0x59, 0xE9, 0xBF, 0x5B, 0x6E, 0x2C, 0x7A, 0x2B, 0xD1, 0xC5, 0x45, 0x24, 0x93, 0xC7, 0x92, 0x89, 0x89, 0x4E, 0x6F, 0x04, 0x58, 0x09, 0xD1, 0x70, 0x67, 0xE9, 0xB6, 0x09, 0x1A, 0x35, 0xB6, 0x37, 0x4E, 0x7D, 0x61, 0xC3, 0xF5, 0xBD, 0x48, 0xA4, 0x23, 0x60, 0x59, 0xA2, 0xA0, 0x21, 0xDA, 0x20, 0x92, 0xDB, 0xB9, 0x31, 0x43, 0xFB, 0x85, 0xD9, 0x3B, 0x4F, 0xF3, 0x96, 0x05, 0x0A, 0x98, 0x6E, 0xCE, 0xC5, 0x3F, 0xCD, 0x18, 0x6D, 0x2C, 0x17, 0x49, 0x68, 0xC3, 0x4A, 0xC0, 0xF5, 0x58, 0x0C, 0xA4, 0xDE, 0xF6, 0x4A, 0x11, 0x24, 0xC2, 0xE0, 0x24, 0xB7, 0x00, 0xCB, 0x71, 0x96, 0xDA, 0x35, 0xFD, 0xBA, 0x3F, 0xD9, 0xB7, 0x58, 0xF2, 0x28, 0x14, 0x2D, 0xB0, 0xE9, 0xE3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +// 720p H.264 High 4:4:4 +// ffmpeg -i green128.png -pix_fmt yuv444p -colorspace smpte170m -color_trc smpte170m -color_primaries smpte170m -c:v libx264 -bsf:v filter_units=remove_types=6 test8_444.h264 +// xxd -i test8_444.h264 +const uint8_t FFmpegVideoDecoder::k_h264High_444TestFrame[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0xf4, 0x00, 0x1f, 0x91, 0x9b, 0x28, 0x0a, + 0x00, 0xb7, 0x60, 0x2d, 0x41, 0x81, 0x81, 0x90, 0x00, 0x00, 0x03, 0x00, + 0x10, 0x00, 0x00, 0x03, 0x03, 0x20, 0xf1, 0x83, 0x19, 0x60, 0x00, 0x00, + 0x00, 0x01, 0x68, 0xeb, 0xe3, 0xc4, 0x48, 0x44, 0x00, 0x00, 0x01, 0x65, + 0x88, 0x84, 0x00, 0x2b, 0xff, 0xfe, 0xf5, 0xdb, 0xf3, 0x2c, 0x93, 0x97, + 0x37, 0xc0, 0xa5, 0x7d, 0x51, 0xf0, 0x29, 0x74, 0xec, 0x3f, 0xff, 0x07, + 0xb6, 0x53, 0xd1, 0x9d, 0x16, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x59, 0x20, 0x00, 0x00, 0x03, 0x00, 0x69, 0xa0, 0x00, + 0x09, 0x84, 0x00, 0x01, 0x59, 0x80, 0x00, 0x44, 0xe0, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x0b, 0x29, +}; + +// 720p HEVC RExt8 4:4:4 +// ffmpeg -i green128.png -pix_fmt yuv444p -colorspace smpte170m -color_trc smpte170m -color_primaries smpte170m -c:v libx265 -x265-params info=0 test8_444.hevc +// xxd -i test8_444.hevc +const uint8_t FFmpegVideoDecoder::k_HEVCRExt8_444TestFrame[] = { + 0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x04, 0x08, + 0x00, 0x00, 0x03, 0x00, 0x9e, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x5d, + 0x95, 0x98, 0x09, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x01, 0x04, 0x08, + 0x00, 0x00, 0x03, 0x00, 0x9e, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x5d, + 0x90, 0x00, 0x50, 0x10, 0x05, 0xa2, 0xcb, 0x2b, 0x34, 0x92, 0x65, 0x78, + 0x0b, 0x50, 0x60, 0x60, 0x60, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, + 0x00, 0x06, 0x42, 0x00, 0x00, 0x00, 0x01, 0x44, 0x01, 0xc1, 0x72, 0x86, + 0x0c, 0x46, 0x24, 0x00, 0x00, 0x01, 0x28, 0x01, 0xaf, 0x1d, 0x18, 0x69, + 0x57, 0x59, 0x55, 0x54, 0x51, 0x34, 0xd2, 0x4a, 0xf7, 0xcf, 0x80, 0xff, + 0xf1, 0xcc, 0x1f, 0xc9, 0x84, 0x7d, 0xf8, 0xb6, 0xba, 0xfa, 0xcd, 0x61, + 0xb5, 0xe3, 0xc1, 0x02, 0x19, 0x26, 0x30, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x04, 0xf4, 0xa8, 0x17, + 0x96, 0x03, 0x4c, 0x4e, 0x1a, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x09, 0xf8, 0x93, 0x0b, + 0x93, 0x40, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x48, 0xc0, 0x87, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x01, 0xa3, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0xb5, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x0b, 0xd8, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x51, 0xc0, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x01, 0x39, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x02, 0xca, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x74, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x07, 0x6c, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x27, 0xa0, +}; + +// 720p HEVC RExt10 4:4:4 +// ffmpeg -i green128.png -pix_fmt yuv444p10 -colorspace bt2020nc -color_trc smpte2084 -color_primaries bt2020 -c:v libx265 -x265-params info=0 test10_444.hevc +// xxd -i test10_444.hevc +const uint8_t FFmpegVideoDecoder::k_HEVCRExt10_444TestFrame[] = { + 0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x04, 0x08, + 0x00, 0x00, 0x03, 0x00, 0x9c, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x5d, + 0x95, 0x98, 0x09, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x01, 0x04, 0x08, + 0x00, 0x00, 0x03, 0x00, 0x9c, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x5d, + 0x90, 0x00, 0x50, 0x10, 0x05, 0xa2, 0x6c, 0xb2, 0xb3, 0x49, 0x26, 0x57, + 0x80, 0xb5, 0x09, 0x10, 0x09, 0x04, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, + 0x00, 0x03, 0x00, 0x64, 0x20, 0x00, 0x00, 0x00, 0x01, 0x44, 0x01, 0xc1, + 0x72, 0x86, 0x0c, 0x46, 0x24, 0x00, 0x00, 0x01, 0x28, 0x01, 0xaf, 0x1d, + 0x18, 0x69, 0x57, 0x59, 0x55, 0x54, 0x51, 0x34, 0xd2, 0x4a, 0xf7, 0xcf, + 0x80, 0xff, 0xf1, 0xef, 0x9f, 0xc9, 0x84, 0x7d, 0xf8, 0xb6, 0xba, 0xfa, + 0xcd, 0x61, 0xb5, 0xe3, 0xc1, 0x02, 0x19, 0x26, 0x30, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x04, 0xf4, + 0xa8, 0x17, 0x96, 0x03, 0x4c, 0x4e, 0x1a, 0x80, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x09, 0xf8, + 0x93, 0x0b, 0x93, 0x40, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x48, 0xc0, 0x87, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x01, 0xa3, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0xb5, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x0b, 0xd8, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x51, + 0xc0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x01, 0x39, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x02, 0xca, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x74, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x6c, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x27, 0xa0, +}; + +// 720p AV1 High8 4:4:4 +// ffmpeg -i green128.png -pix_fmt yuv444p -colorspace smpte170m -color_trc smpte170m -color_primaries smpte170m -c:v libaom-av1 test8_444.obu +// xxd -i test8_444.obu +const uint8_t FFmpegVideoDecoder::k_AV1High8_444TestFrame[] = { + 0x12, 0x00, 0x0a, 0x0e, 0x20, 0x00, 0x00, 0x2d, 0x4c, 0xff, 0xb3, 0xdf, + 0xff, 0x9a, 0x0c, 0x0c, 0x0c, 0x20, 0x32, 0x25, 0x10, 0x00, 0x90, 0x00, + 0x00, 0x00, 0xa0, 0x00, 0x00, 0x80, 0x0c, 0x80, 0xad, 0xfd, 0xdb, 0x2e, + 0x81, 0x37, 0x40, 0xfd, 0x19, 0x97, 0xdc, 0x15, 0x72, 0x1d, 0xe0, 0xff, + 0x00, 0x35, 0x9d, 0x2c, 0x10, 0x7d, 0x17, 0x58, 0x60, +}; + +// 720p AV1 High10 4:4:4 +// ffmpeg -i green128.png -pix_fmt yuv444p10 -colorspace bt2020nc -color_trc smpte2084 -color_primaries bt2020 -c:v libaom-av1 test10_444.obu +// xxd -i test10_444.obu +const uint8_t FFmpegVideoDecoder::k_AV1High10_444TestFrame[] = { + 0x12, 0x00, 0x0a, 0x0e, 0x20, 0x00, 0x00, 0x2d, 0x4c, 0xff, 0xb3, 0xdf, + 0xff, 0x9e, 0x12, 0x20, 0x12, 0x20, 0x32, 0x25, 0x10, 0x00, 0x90, 0x00, + 0x00, 0x00, 0xa0, 0x00, 0x00, 0x80, 0x0c, 0x80, 0xad, 0xfe, 0x04, 0x70, + 0x81, 0x37, 0x40, 0xfd, 0x19, 0x97, 0xdc, 0x15, 0x72, 0x1d, 0xe0, 0xff, + 0x00, 0x35, 0x9d, 0x2c, 0x10, 0x7d, 0x17, 0x58, 0x60, +}; diff --git a/moonlight-common-c/moonlight-common-c b/moonlight-common-c/moonlight-common-c index c245fe59..8599b604 160000 --- a/moonlight-common-c/moonlight-common-c +++ b/moonlight-common-c/moonlight-common-c @@ -1 +1 @@ -Subproject commit c245fe599d932943a50125b903ee325aac2d0d8a +Subproject commit 8599b6042a4ba27749b0f94134dd614b4328a9bc