Skip to content

Commit 3b72fe4

Browse files
committed
Implement FrameCGImage/FrameIOSurface/FrameProvider/ScopedCFTypeRef for macos (based on WebRTC source code).
1 parent 246f5ec commit 3b72fe4

File tree

8 files changed

+587
-1
lines changed

8 files changed

+587
-1
lines changed

source/base/CMakeLists.txt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,16 @@ list(APPEND SOURCE_BASE_DESKTOP_TESTS
369369
desktop/geometry_unittest.cc
370370
desktop/region_unittest.cc)
371371

372+
if (APPLE)
373+
list(APPEND SOURCE_BASE_DESKTOP_MAC
374+
desktop/mac/frame_cgimage.mm
375+
desktop/mac/frame_cgimage.h
376+
desktop/mac/frame_iosurface.mm
377+
desktop/mac/frame_iosurface.h
378+
desktop/mac/frame_provider.mm
379+
desktop/mac/frame_provider.h)
380+
endif()
381+
372382
if (WIN32)
373383
list(APPEND SOURCE_BASE_DESKTOP_WIN
374384
desktop/win/bitmap_info.h
@@ -476,7 +486,8 @@ if (APPLE)
476486
mac/app_nap_blocker.mm
477487
mac/app_nap_blocker.h
478488
mac/nsstring_conversions.mm
479-
mac/nsstring_conversions.h)
489+
mac/nsstring_conversions.h
490+
mac/scoped_cftyperef.h)
480491
endif()
481492

482493
list(APPEND SOURCE_BASE_MEMORY
@@ -758,6 +769,7 @@ endif()
758769

759770
if (APPLE)
760771
source_group(mac FILES ${SOURCE_BASE_MAC})
772+
source_group(desktop\\mac FILES ${SOURCE_BASE_DESKTOP_MAC})
761773
endif()
762774

763775
add_library(aspia_base STATIC
@@ -768,6 +780,7 @@ add_library(aspia_base STATIC
768780
${SOURCE_BASE_CODEC}
769781
${SOURCE_BASE_CRYPTO}
770782
${SOURCE_BASE_DESKTOP}
783+
${SOURCE_BASE_DESKTOP_MAC}
771784
${SOURCE_BASE_DESKTOP_WIN}
772785
${SOURCE_BASE_DESKTOP_X11}
773786
${SOURCE_BASE_FILES}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//
2+
// Aspia Project
3+
// Copyright (C) 2016-2023 Dmitry Chapyshev <[email protected]>
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
//
18+
19+
#ifndef BASE_DESKTOP_MAC_FRAME_CGIMAGE_H
20+
#define BASE_DESKTOP_MAC_FRAME_CGIMAGE_H
21+
22+
#include "base/macros_magic.h"
23+
#include "base/desktop/frame.h"
24+
#include "base/mac/scoped_cftyperef.h"
25+
26+
#include <memory>
27+
28+
#include <CoreGraphics/CoreGraphics.h>
29+
30+
namespace base {
31+
32+
class FrameCGImage final : public Frame
33+
{
34+
public:
35+
// Create an image containing a snapshot of the display at the time this is being called.
36+
static std::unique_ptr<FrameCGImage> createForDisplay(CGDirectDisplayID display_id);
37+
38+
static std::unique_ptr<FrameCGImage> createFromCGImage(ScopedCFTypeRef<CGImageRef> cg_image);
39+
40+
~FrameCGImage() override;
41+
42+
private:
43+
// This constructor expects `cg_image` to hold a non-null CGImageRef.
44+
FrameCGImage(Size size,
45+
int stride,
46+
uint8_t* data,
47+
ScopedCFTypeRef<CGImageRef> cg_image,
48+
ScopedCFTypeRef<CFDataRef> cg_data);
49+
50+
const ScopedCFTypeRef<CGImageRef> cg_image_;
51+
const ScopedCFTypeRef<CFDataRef> cg_data_;
52+
53+
DISALLOW_COPY_AND_ASSIGN(FrameCGImage);
54+
};
55+
56+
} // namespace base
57+
58+
#endif // BASE_DESKTOP_MAC_FRAME_CGIMAGE_H
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//
2+
// Aspia Project
3+
// Copyright (C) 2016-2023 Dmitry Chapyshev <[email protected]>
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
//
18+
19+
#include "base/desktop/mac/frame_cgimage.h"
20+
21+
#include <AvailabilityMacros.h>
22+
23+
namespace base {
24+
25+
namespace {
26+
27+
const int kBytesPerPixel = 4;
28+
29+
} // namespace
30+
31+
//--------------------------------------------------------------------------------------------------
32+
// static
33+
std::unique_ptr<FrameCGImage> FrameCGImage::createForDisplay(CGDirectDisplayID display_id)
34+
{
35+
// Create an image containing a snapshot of the display.
36+
ScopedCFTypeRef<CGImageRef> cg_image(CGDisplayCreateImage(display_id));
37+
if (!cg_image)
38+
return nullptr;
39+
40+
return FrameCGImage::createFromCGImage(cg_image);
41+
}
42+
43+
//--------------------------------------------------------------------------------------------------
44+
// static
45+
std::unique_ptr<FrameCGImage> FrameCGImage::createFromCGImage(ScopedCFTypeRef<CGImageRef> cg_image)
46+
{
47+
// Verify that the image has 32-bit depth.
48+
int bits_per_pixel = CGImageGetBitsPerPixel(cg_image.get());
49+
if (bits_per_pixel / 8 != kBytesPerPixel)
50+
{
51+
LOG(LS_ERROR) << "CGDisplayCreateImage() returned imaged with " << bits_per_pixel
52+
<< " bits per pixel. Only 32-bit depth is supported.";
53+
return nullptr;
54+
}
55+
56+
// Request access to the raw pixel data via the image's DataProvider.
57+
CGDataProviderRef cg_provider = CGImageGetDataProvider(cg_image.get());
58+
DCHECK(cg_provider);
59+
60+
// CGDataProviderCopyData returns a new data object containing a copy of the provider’s data.
61+
ScopedCFTypeRef<CFDataRef> cg_data(CGDataProviderCopyData(cg_provider));
62+
DCHECK(cg_data);
63+
64+
// CFDataGetBytePtr returns a read-only pointer to the bytes of a CFData object.
65+
uint8_t* data = const_cast<uint8_t*>(CFDataGetBytePtr(cg_data.get()));
66+
DCHECK(data);
67+
68+
Size size(CGImageGetWidth(cg_image.get()), CGImageGetHeight(cg_image.get()));
69+
int stride = CGImageGetBytesPerRow(cg_image.get());
70+
71+
std::unique_ptr<FrameCGImage> frame(new FrameCGImage(size, stride, data, cg_image, cg_data));
72+
return frame;
73+
}
74+
75+
//--------------------------------------------------------------------------------------------------
76+
FrameCGImage::FrameCGImage(Size size,
77+
int stride,
78+
uint8_t* data,
79+
ScopedCFTypeRef<CGImageRef> cg_image,
80+
ScopedCFTypeRef<CFDataRef> cg_data)
81+
: Frame(size, PixelFormat::ARGB(), stride, data, nullptr),
82+
cg_image_(cg_image),
83+
cg_data_(cg_data)
84+
{
85+
DCHECK(cg_image_);
86+
DCHECK(cg_data_);
87+
}
88+
89+
//--------------------------------------------------------------------------------------------------
90+
FrameCGImage::~FrameCGImage() = default;
91+
92+
} // namespace webrtc
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// Aspia Project
3+
// Copyright (C) 2016-2023 Dmitry Chapyshev <[email protected]>
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
//
18+
19+
#ifndef BASE_DESKTOP_MAC_FRAME_IOSURFACE_H
20+
#define BASE_DESKTOP_MAC_FRAME_IOSURFACE_H
21+
22+
#include "base/desktop/frame.h"
23+
#include "base/mac/scoped_cftyperef.h"
24+
25+
#include <memory>
26+
27+
#include <CoreGraphics/CoreGraphics.h>
28+
#include <IOSurface/IOSurface.h>
29+
30+
namespace base {
31+
32+
class FrameIOSurface final : public Frame
33+
{
34+
public:
35+
// Lock an IOSurfaceRef containing a snapshot of a display. Return NULL if failed to lock.
36+
static std::unique_ptr<FrameIOSurface> wrap(ScopedCFTypeRef<IOSurfaceRef> io_surface);
37+
38+
~FrameIOSurface() override;
39+
40+
private:
41+
// This constructor expects `io_surface` to hold a non-null IOSurfaceRef.
42+
explicit FrameIOSurface(ScopedCFTypeRef<IOSurfaceRef> io_surface);
43+
44+
const ScopedCFTypeRef<IOSurfaceRef> io_surface_;
45+
};
46+
47+
} // namespace base
48+
49+
#endif // BASE_DESKTOP_MAC_FRAME_IOSURFACE_H
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//
2+
// Aspia Project
3+
// Copyright (C) 2016-2023 Dmitry Chapyshev <[email protected]>
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
//
18+
19+
#include "base/desktop/mac/frame_iosurface.h"
20+
21+
namespace base {
22+
23+
namespace {
24+
25+
const int kBytesPerPixel = 4;
26+
27+
} // namespace
28+
29+
//--------------------------------------------------------------------------------------------------
30+
// static
31+
std::unique_ptr<FrameIOSurface> FrameIOSurface::wrap(ScopedCFTypeRef<IOSurfaceRef> io_surface)
32+
{
33+
if (!io_surface)
34+
return nullptr;
35+
36+
IOSurfaceIncrementUseCount(io_surface.get());
37+
IOReturn status = IOSurfaceLock(io_surface.get(), kIOSurfaceLockReadOnly, nullptr);
38+
if (status != kIOReturnSuccess)
39+
{
40+
LOG(LS_ERROR) << "Failed to lock the IOSurface with status " << status;
41+
IOSurfaceDecrementUseCount(io_surface.get());
42+
return nullptr;
43+
}
44+
45+
// Verify that the image has 32-bit depth.
46+
int bytes_per_pixel = IOSurfaceGetBytesPerElement(io_surface.get());
47+
if (bytes_per_pixel != kBytesPerPixel)
48+
{
49+
LOG(LS_ERROR) << "CGDisplayStream handler returned IOSurface with " << (8 * bytes_per_pixel)
50+
<< " bits per pixel. Only 32-bit depth is supported.";
51+
IOSurfaceUnlock(io_surface.get(), kIOSurfaceLockReadOnly, nullptr);
52+
IOSurfaceDecrementUseCount(io_surface.get());
53+
return nullptr;
54+
}
55+
56+
return std::unique_ptr<FrameIOSurface>(new FrameIOSurface(io_surface));
57+
}
58+
59+
//--------------------------------------------------------------------------------------------------
60+
FrameIOSurface::FrameIOSurface(ScopedCFTypeRef<IOSurfaceRef> io_surface)
61+
: Frame(Size(IOSurfaceGetWidth(io_surface.get()), IOSurfaceGetHeight(io_surface.get())),
62+
PixelFormat::ARGB(),
63+
IOSurfaceGetBytesPerRow(io_surface.get()),
64+
static_cast<uint8_t*>(IOSurfaceGetBaseAddress(io_surface.get())),
65+
nullptr),
66+
io_surface_(io_surface)
67+
{
68+
DCHECK(io_surface_);
69+
}
70+
71+
//--------------------------------------------------------------------------------------------------
72+
FrameIOSurface::~FrameIOSurface()
73+
{
74+
IOSurfaceUnlock(io_surface_.get(), kIOSurfaceLockReadOnly, nullptr);
75+
IOSurfaceDecrementUseCount(io_surface_.get());
76+
}
77+
78+
} // namespace base
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//
2+
// Aspia Project
3+
// Copyright (C) 2016-2023 Dmitry Chapyshev <[email protected]>
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
//
18+
19+
#ifndef BASE_DESKTOP_MAC_FRAME_PROVIDER_H
20+
#define BASE_DESKTOP_MAC_FRAME_PROVIDER_H
21+
22+
#include "base/macros_magic.h"
23+
#include "base/desktop/shared_frame.h"
24+
#include "base/mac/scoped_cftyperef.h"
25+
#include "base/threading/thread_checker.h"
26+
27+
#include <map>
28+
#include <memory>
29+
30+
#include <CoreGraphics/CoreGraphics.h>
31+
#include <IOSurface/IOSurface.h>
32+
33+
namespace base {
34+
35+
class FrameProvider
36+
{
37+
public:
38+
explicit FrameProvider(bool allow_iosurface);
39+
~FrameProvider();
40+
41+
// The caller takes ownership of the returned desktop frame. Otherwise returns null if
42+
// `display_id` is invalid or not ready. Note that this function does not remove the frame from
43+
// the internal container. Caller has to call the Release function.
44+
std::unique_ptr<Frame> takeLatestFrameForDisplay(CGDirectDisplayID display_id);
45+
46+
// OS sends the latest IOSurfaceRef through CGDisplayStreamFrameAvailableHandler callback; we
47+
// store it here.
48+
void invalidateIOSurface(CGDirectDisplayID display_id, ScopedCFTypeRef<IOSurfaceRef> io_surface);
49+
50+
// Expected to be called before stopping the CGDisplayStreamRef streams.
51+
void release();
52+
53+
private:
54+
THREAD_CHECKER(thread_checker_);
55+
const bool allow_iosurface_;
56+
57+
// Most recent IOSurface that contains a capture of matching display.
58+
std::map<CGDirectDisplayID, std::unique_ptr<SharedFrame>> io_surfaces_;
59+
60+
DISALLOW_COPY_AND_ASSIGN(FrameProvider);
61+
};
62+
63+
} // namespace base
64+
65+
#endif // BASE_DESKTOP_MAC_FRAME_PROVIDER_H

0 commit comments

Comments
 (0)