Skip to content
Open
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
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ For more information, please visit <http://www.openshot.org/>.
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules")

################ PROJECT VERSION ####################
set(PROJECT_VERSION_FULL "0.7.0")
set(PROJECT_SO_VERSION 30)
set(PROJECT_VERSION_FULL "1.0.0")
set(PROJECT_SO_VERSION 31)

# Remove the dash and anything following, to get the #.#.# version for project()
STRING(REGEX REPLACE "\-.*$" "" VERSION_NUM "${PROJECT_VERSION_FULL}")
Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ add_feature_info("Wayland screen capture" OPENSHOT_WAYLAND_CAPTURE "Use xdg-desk
# Find JUCE-based openshot Audio libraries
if(NOT TARGET OpenShot::Audio)
# Only load if necessary (not for integrated builds)
find_package(OpenShotAudio 0.6.0 REQUIRED)
find_package(OpenShotAudio 1.0.0 REQUIRED)
endif()
target_link_libraries(openshot PUBLIC OpenShot::Audio)

Expand Down
17 changes: 12 additions & 5 deletions src/ScreenCaptureReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,7 @@ ScreenCaptureReader::~ScreenCaptureReader()

bool ScreenCaptureReader::IsOpen()
{
const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
return backend_reader ? backend_reader->IsOpen() : is_open;
}

Expand Down Expand Up @@ -947,11 +948,11 @@ void ScreenCaptureReader::OpenDecoder()

std::shared_ptr<Frame> ScreenCaptureReader::GetFrame(int64_t number)
{
const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
if (backend_reader) {
if (!backend_reader->IsOpen()) {
throw ReaderClosed("The ScreenCaptureReader is closed. Call Open() before GetFrame().");
}
const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
auto frame = backend_reader->GetFrame(number);
if (system_audio && !manual_system_audio) system_audio->AddFrameAudio(frame, number, info.fps);
return frame;
Expand All @@ -960,7 +961,6 @@ std::shared_ptr<Frame> ScreenCaptureReader::GetFrame(int64_t number)
throw ReaderClosed("The ScreenCaptureReader is closed. Call Open() before GetFrame().");
}

const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
auto frame = DecodeNextFrame(number);
if (system_audio && !manual_system_audio) system_audio->AddFrameAudio(frame, number, info.fps);
return frame;
Expand Down Expand Up @@ -1101,13 +1101,20 @@ std::shared_ptr<Frame> ScreenCaptureReader::DecodeNextFrame(int64_t number)

void ScreenCaptureReader::Close()
{
// Signal blocking native reads before waiting for GetFrame(). The FFmpeg
// interrupt callback observes close_requested, while backend readers use
// Close() to wake their own blocking waits.
close_requested = true;
if (system_audio) {
system_audio->Close();
}
if (backend_reader) {
backend_reader->Close();
}

// GetFrame() owns all decoder and system-audio use under this mutex. Do not
// release those resources until an interrupted read has completely exited.
const std::lock_guard<std::recursive_mutex> lock(getFrameMutex);
if (system_audio) {
system_audio->Close();
}
if (packet) {
av_packet_free(&packet);
}
Expand Down
133 changes: 133 additions & 0 deletions src/WaylandBufferUtilities.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/**
* @file
* @brief Bounds-safe helpers for mapped PipeWire video buffers
*/

// Copyright (c) 2008-2026 OpenShot Studios, LLC
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#ifndef OPENSHOT_WAYLAND_BUFFER_UTILITIES_H
#define OPENSHOT_WAYLAND_BUFFER_UTILITIES_H

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>

namespace openshot::wayland
{
struct PackedVideoLayout
{
size_t offset = 0;
size_t valid_size = 0;
int stride = 0;
int crop_x = 0;
int crop_y = 0;
int width = 0;
int height = 0;
bool valid = false;
};

inline PackedVideoLayout ResolvePackedVideoLayout(
size_t max_size,
size_t chunk_offset,
size_t chunk_size,
int chunk_stride,
int stream_width,
int stream_height,
int crop_x,
int crop_y,
int crop_width,
int crop_height)
{
PackedVideoLayout layout;
if (max_size == 0 || stream_width <= 0 || stream_height <= 0
|| stream_width > std::numeric_limits<int>::max() / 4
|| chunk_stride < 0) {
return layout;
}

layout.offset = chunk_offset % max_size;
layout.valid_size = std::min(chunk_size, max_size);
layout.stride = chunk_stride > 0 ? chunk_stride : stream_width * 4;
if (layout.stride < 4 || layout.valid_size < static_cast<size_t>(layout.stride)) {
return layout;
}

const int readable_width = std::min(
stream_width,
layout.stride / 4);
const int readable_height = std::min(
stream_height,
static_cast<int>(layout.valid_size / static_cast<size_t>(layout.stride)));

layout.crop_x = std::max(0, crop_x);
layout.crop_y = std::max(0, crop_y);
if (layout.crop_x >= readable_width || layout.crop_y >= readable_height) {
return layout;
}

const int requested_width = crop_width > 0
? crop_width
: stream_width - layout.crop_x;
const int requested_height = crop_height > 0
? crop_height
: stream_height - layout.crop_y;
layout.width = std::min(requested_width, readable_width - layout.crop_x);
layout.height = std::min(requested_height, readable_height - layout.crop_y);

// H.264 requires even dimensions, and capture callers already expect them.
layout.width -= layout.width % 2;
layout.height -= layout.height % 2;
if (layout.width <= 0 || layout.height <= 0) {
return layout;
}

const size_t final_row_end =
static_cast<size_t>(layout.crop_y + layout.height - 1) * layout.stride
+ static_cast<size_t>(layout.crop_x + layout.width) * 4;
if (final_row_end > layout.valid_size) {
return layout;
}

layout.valid = true;
return layout;
}

inline int DamageFrameWaitMilliseconds(int fps_num, int fps_den, bool have_last_frame)
{
if (!have_last_frame) {
return 5000;
}
const double fps = fps_num > 0 && fps_den > 0
? static_cast<double>(fps_num) / fps_den
: 30.0;
return std::max(1, static_cast<int>(1000.0 / std::max(1.0, fps)));
}

inline bool CopyWrappedBytes(
const uint8_t* source,
size_t max_size,
size_t chunk_offset,
size_t logical_offset,
uint8_t* destination,
size_t byte_count)
{
if (!source || !destination || max_size == 0 || byte_count > max_size) {
return false;
}
const size_t physical_offset = (
(chunk_offset % max_size) + (logical_offset % max_size)
) % max_size;
const size_t first_size = std::min(byte_count, max_size - physical_offset);
std::memcpy(destination, source + physical_offset, first_size);
if (first_size < byte_count) {
std::memcpy(destination + first_size, source, byte_count - first_size);
}
return true;
}
}

#endif
Loading
Loading