Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2252,6 +2252,103 @@ consent prompt for directory ACLs, so the Web UI shows setup steps for correctin
</tr>
</table>

### mpeg2_mode

<table>
<tr>
<td>Description</td>
<td colspan="2">
Allows any client implementing the Sunshine codec extension to request MPEG-2/H.262 video streams.
Support is unadvertised by default and must be enabled here. Sunshine probes MPEG-2 independently of
H.264, preferring the selected backend before trying other available backends. Enabling H.263+ does not
displace a working hardware MPEG-2 encoder. Streams using different encoder backends use separate
capture workers.
@warning{MPEG-2 may be more CPU-intensive to encode than modern hardware-accelerated codecs when hardware
encoding is unavailable.}
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}
0
@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
mpeg2_mode = 2
@endcode</td>
</tr>
<tr>
<td rowspan="3">Choices</td>
<td>0</td>
<td>preserve modern codec automatic selection and keep MPEG-2 unadvertised</td>
</tr>
<tr>
<td>1</td>
<td>do not advertise support for MPEG-2</td>
</tr>
<tr>
<td>2</td>
<td>advertise support for MPEG-2/H.262 video</td>
</tr>
</table>

### h263p_mode

<table>
<tr>
<td>Description</td>
<td colspan="2">
Allows any client implementing the Sunshine codec extension to request H.263+ video streams.
Support is unadvertised by default and must be enabled here. Sunshine selects and probes its H.263+
backend independently of MPEG-2 and modern codecs; H.264 support is not a prerequisite.
@warning{H.263+ uses software encoding in Sunshine and may reduce host performance.}
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}
0
@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
h263p_mode = 2
@endcode</td>
</tr>
<tr>
<td rowspan="3">Choices</td>
<td>0</td>
<td>preserve modern codec automatic selection and keep H.263+ unadvertised</td>
</tr>
<tr>
<td>1</td>
<td>do not advertise support for H.263+</td>
</tr>
<tr>
<td>2</td>
<td>advertise support for H.263+ video</td>
</tr>
</table>

### Legacy codec protocol

MPEG-2 and H.263+ are opt-in Sunshine protocol extensions available to any implementing client. Both require
8-bit 4:2:0 video. Sunshine advertises a codec only after its backend passes a probe; enabling an option does
not guarantee that the codec is available on the host. Client automatic selection prefers AV1, HEVC, then H.264
before the legacy codecs. A client requesting only one codec fails negotiation if that codec is unavailable.

| Codec | `ServerCodecModeSupport` flag | RTSP DESCRIBE marker | `x-nv-vqos[0].bitStreamFormat` |
|--------------|-------------------------------|----------------------|--------------------------------|
| MPEG-2/H.262 | `0x00800000` | `MPV/90000` | `3` |
| H.263+ | `0x02000000` | `H263-1998/90000` | `4` |

The streams retain GameStream video framing, frame headers, recovery requests, and FEC. The SDP codec names do
not imply RFC 2250 or RFC 4629 RTP payload packetization. Clients must implement the extension's negotiation,
GameStream depacketization, and matching video decoder; changing a client name does not enable codec support.

### capture

<table>
Expand Down
4 changes: 4 additions & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,8 @@ namespace config {

0, // hevc_mode
0, // av1_mode
0, // mpeg2_mode
0, // h263p_mode

2, // min_threads
{
Expand Down Expand Up @@ -1600,6 +1602,8 @@ namespace config {
int_f(vars, "qp", video.qp);
int_between_f(vars, "hevc_mode", video.hevc_mode, {0, 3});
int_between_f(vars, "av1_mode", video.av1_mode, {0, 3});
int_between_f(vars, "mpeg2_mode", video.mpeg2_mode, {0, 2});
int_between_f(vars, "h263p_mode", video.h263p_mode, {0, 2});
int_f(vars, "min_threads", video.min_threads);
string_f(vars, "sw_preset", video.sw.sw_preset);
if (!video.sw.sw_preset.empty()) {
Expand Down
2 changes: 2 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ namespace config {

int hevc_mode; ///< HEVC support mode advertised to clients.
int av1_mode; ///< AV1 support mode advertised to clients.
int mpeg2_mode; ///< MPEG-2 codec advertisement mode.
int h263p_mode; ///< H.263+ codec advertisement mode.

int min_threads; ///< Minimum number of threads or slices for CPU encoding.

Expand Down
10 changes: 8 additions & 2 deletions src/nvhttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1201,8 +1201,8 @@ namespace nvhttp {
}

uint32_t get_codec_mode_flags() {
uint32_t codec_mode_flags = SCM_H264;
if (video::last_encoder_probe_supported_yuv444_for_codec[0]) {
uint32_t codec_mode_flags = video::last_encoder_probe_supported_h264 ? SCM_H264 : 0;
if (video::last_encoder_probe_supported_h264 && video::last_encoder_probe_supported_yuv444_for_codec[0]) {
codec_mode_flags |= SCM_H264_HIGH8_444;
}
if (video::active_hevc_mode >= 2) {
Expand Down Expand Up @@ -1230,6 +1230,12 @@ namespace nvhttp {
if ((video::active_av1_mode == 4 || video::active_av1_mode == 5) && video::last_encoder_probe_supported_dynamic_range_yuv444_for_codec[2]) {
codec_mode_flags |= SCM_AV1_HIGH10_444;
}
if (video::active_mpeg2_mode >= 2) {
codec_mode_flags |= SCM_MPEG2;
}
if (video::active_h263p_mode == 2) {
codec_mode_flags |= SCM_H263P;
}
return codec_mode_flags;
}

Expand Down
44 changes: 35 additions & 9 deletions src/rtsp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ extern "C" {
#include "input.h"
#include "logging.h"
#include "network.h"
#include "nvhttp.h"
#include "rtsp.h"
#include "stream.h"
#include "sync.h"
Expand All @@ -41,6 +42,23 @@ using asio::ip::udp;
using namespace std::literals;

namespace rtsp_stream {
std::string describe_video_codecs(uint32_t codec_modes) {
std::string result;
if (codec_modes & SCM_MASK_HEVC) {
result += "sprop-parameter-sets=AAAAAU\r\n";
}
if (codec_modes & SCM_MASK_AV1) {
result += "a=rtpmap:98 AV1/90000\r\n";
}
if (codec_modes & SCM_MPEG2) {
result += "a=rtpmap:98 MPV/90000\r\n";
}
if (codec_modes & SCM_H263P) {
result += "a=rtpmap:98 H263-1998/90000\r\n";
}
return result;
}

/**
* @brief Release msg resources.
*
Expand Down Expand Up @@ -902,13 +920,7 @@ namespace rtsp_stream {
ss << "a=x-nv-video[0].refPicInvalidation:1"sv << std::endl;
}

if (video::active_hevc_mode != 1) {
ss << "sprop-parameter-sets=AAAAAU"sv << std::endl;
}

if (video::active_av1_mode != 1) {
ss << "a=rtpmap:98 AV1/90000"sv << std::endl;
}
ss << describe_video_codecs(nvhttp::get_codec_mode_flags());

if (!session.surround_params.empty()) {
// If we have our own surround parameters, advertise them twice first
Expand Down Expand Up @@ -1226,20 +1238,34 @@ namespace rtsp_stream {
config.monitor.bitrate = (int) configuredBitrateKbps;
}

if (config.monitor.videoFormat == 1 && video::active_hevc_mode == 1) {
if (config.monitor.videoFormat == video::format_hevc && video::active_hevc_mode == 1) {
BOOST_LOG(warning) << "HEVC is disabled, yet the client requested HEVC"sv;

respond(sock, session, &option, 400, "BAD REQUEST", req->sequenceNumber, {});
return;
}

if (config.monitor.videoFormat == 2 && video::active_av1_mode == 1) {
if (config.monitor.videoFormat == video::format_av1 && video::active_av1_mode == 1) {
BOOST_LOG(warning) << "AV1 is disabled, yet the client requested AV1"sv;

respond(sock, session, &option, 400, "BAD REQUEST", req->sequenceNumber, {});
return;
}

if (config.monitor.videoFormat == video::format_mpeg2 && video::active_mpeg2_mode < 2) {
BOOST_LOG(warning) << "MPEG-2 is disabled, yet the client requested MPEG-2"sv;

respond(sock, session, &option, 400, "BAD REQUEST", req->sequenceNumber, {});
return;
}

if (config.monitor.videoFormat == video::format_h263p && video::active_h263p_mode != 2) {
BOOST_LOG(warning) << "H.263+ is disabled, yet the client requested H.263+"sv;

respond(sock, session, &option, 400, "BAD REQUEST", req->sequenceNumber, {});
return;
}

// Check that any required encryption is enabled
auto encryption_mode = net::encryption_mode_for_address(sock.remote_endpoint().address());
if (encryption_mode == config::ENCRYPTION_MODE_MANDATORY && (config.encryptionFlagsEnabled & (SS_ENC_VIDEO | SS_ENC_AUDIO)) != (SS_ENC_VIDEO | SS_ENC_AUDIO)) {
Expand Down
9 changes: 9 additions & 0 deletions src/rtsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,21 @@
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <string>

// local includes
#include "crypto.h"
#include "thread_safe.h"

namespace rtsp_stream {
/**
* @brief Describe the codecs advertised by a host, independently of client identity.
*
* @param codec_modes Validated ServerCodecModeSupport capability bits.
* @return GameStream DESCRIBE codec attributes; these do not change RTP packetization.
*/
std::string describe_video_codecs(uint32_t codec_modes);

constexpr auto RTSP_SETUP_PORT = 21; ///< GameStream base-port offset used for the RTSP setup listener.

#pragma pack(push, 1)
Expand Down
3 changes: 2 additions & 1 deletion src/thread_safe.h
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,7 @@ namespace safe {
if (!_count) {
new (_object_buf.data()) element_type;
if (_construct(*reinterpret_cast<element_type *>(_object_buf.data())) != 0) {
reinterpret_cast<element_type *>(_object_buf.data())->~element_type();
return ptr_t {nullptr};
}
}
Expand All @@ -722,7 +723,7 @@ namespace safe {

std::array<std::uint8_t, sizeof(element_type)> _object_buf;

std::uint32_t _count;
std::uint32_t _count = 0; ///< Number of active references, including dynamically allocated storage.
std::mutex _lock;
};

Expand Down
Loading
Loading