| 1901 | } |
| 1902 | |
| 1903 | void encode_run( |
| 1904 | int &frame_nr, // Store progress of the frame number |
| 1905 | safe::mail_t mail, |
| 1906 | img_event_t images, |
| 1907 | config_t config, |
| 1908 | std::shared_ptr<platf::display_t> disp, |
| 1909 | std::unique_ptr<platf::encode_device_t> encode_device, |
| 1910 | safe::signal_t &reinit_event, |
| 1911 | const encoder_t &encoder, |
| 1912 | void *channel_data |
| 1913 | ) { |
| 1914 | auto session = make_encode_session(disp.get(), encoder, config, disp->width, disp->height, std::move(encode_device)); |
| 1915 | if (!session) { |
| 1916 | return; |
| 1917 | } |
| 1918 | |
| 1919 | // As a workaround for NVENC hangs and to generally speed up encoder reinit, |
| 1920 | // we will complete the encoder teardown in a separate thread if supported. |
| 1921 | // This will move expensive processing off the encoder thread to allow us |
| 1922 | // to restart encoding as soon as possible. For cases where the NVENC driver |
| 1923 | // hang occurs, this thread may probably never exit, but it will allow |
| 1924 | // streaming to continue without requiring a full restart of Sunshine. |
| 1925 | auto fail_guard = util::fail_guard([&encoder, &session] { |
| 1926 | if (encoder.flags & ASYNC_TEARDOWN) { |
| 1927 | std::thread encoder_teardown_thread {[session = std::move(session)]() mutable { |
| 1928 | BOOST_LOG(info) << "Starting async encoder teardown"; |
| 1929 | session.reset(); |
| 1930 | BOOST_LOG(info) << "Async encoder teardown complete"; |
| 1931 | }}; |
| 1932 | encoder_teardown_thread.detach(); |
| 1933 | } |
| 1934 | }); |
| 1935 | |
| 1936 | // set max frame time based on client-requested target framerate. |
| 1937 | double minimum_fps_target = (config::video.minimum_fps_target > 0.0) ? config::video.minimum_fps_target * 1000 : std::max(config.encodingFramerate / 5, 10000); |
| 1938 | auto max_frametime = std::chrono::nanoseconds(1000ms) * 1000 / minimum_fps_target; |
| 1939 | auto encode_frame_threshold = std::chrono::nanoseconds(1000ms) * 1000 / config.encodingFramerate; |
| 1940 | auto frame_variation_threshold = encode_frame_threshold / 4; |
| 1941 | auto min_frame_diff = encode_frame_threshold - frame_variation_threshold; |
| 1942 | BOOST_LOG(info) << "Minimum FPS target set to ~"sv << (minimum_fps_target / 2000) << "fps ("sv << max_frametime * 2 << ")"sv; |
| 1943 | BOOST_LOG(info) << "Encoding Frame threshold: "sv << encode_frame_threshold; |
| 1944 | |
| 1945 | auto shutdown_event = mail->event<bool>(mail::shutdown); |
| 1946 | auto packets = mail::man->queue<packet_t>(mail::video_packets); |
| 1947 | auto idr_events = mail->event<bool>(mail::idr); |
| 1948 | auto invalidate_ref_frames_events = mail->event<std::pair<int64_t, int64_t>>(mail::invalidate_ref_frames); |
| 1949 | |
| 1950 | { |
| 1951 | // Load a dummy image into the AVFrame to ensure we have something to encode |
| 1952 | // even if we timeout waiting on the first frame. This is a relatively large |
| 1953 | // allocation which can be freed immediately after convert(), so we do this |
| 1954 | // in a separate scope. |
| 1955 | auto dummy_img = disp->alloc_img(); |
| 1956 | if (!dummy_img || disp->dummy_img(dummy_img.get()) || session->convert(*dummy_img)) { |
| 1957 | return; |
| 1958 | } |
| 1959 | } |
| 1960 |
no test coverage detected