PipeWire capturer: add initial test for SharedScreenCastStream
This test created another PipeWire stream we can connect to with SharedScreenCastStream and recieve frames from there. This is an initial version, where I test whether we can successfuly connect and disconnect, receive frames and it also tests DesktopFrameQueue. In the future I will add tests to test mouse cursor and try to come up with some corner cases and possible scenarios. Bug: webrtc:13429 Change-Id: Ib2a749207085c6324ffe3d5cc8f2f9c631fa6459 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/256267 Reviewed-by: Christoffer Jansson <jansson@webrtc.org> Reviewed-by: Mark Foltz <mfoltz@chromium.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Jan Grulich <grulja@gmail.com> Reviewed-by: Jeremy Leconte <jleconte@webrtc.org> Cr-Commit-Position: refs/heads/main@{#38431}
This commit is contained in:
parent
aafcc43440
commit
1264dc165b
3
BUILD.gn
3
BUILD.gn
@ -106,6 +106,9 @@ if (!build_with_chromium) {
|
|||||||
"tools_webrtc/perf:webrtc_dashboard_upload",
|
"tools_webrtc/perf:webrtc_dashboard_upload",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
if ((is_linux || is_chromeos) && rtc_use_pipewire) {
|
||||||
|
deps += [ "modules/desktop_capture:shared_screencast_stream_test" ]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (target_os == "android") {
|
if (target_os == "android") {
|
||||||
deps += [ "tools_webrtc:binary_version_check" ]
|
deps += [ "tools_webrtc:binary_version_check" ]
|
||||||
|
|||||||
15
DEPS
15
DEPS
@ -490,6 +490,21 @@ deps = {
|
|||||||
],
|
],
|
||||||
'dep_type': 'cipd',
|
'dep_type': 'cipd',
|
||||||
},
|
},
|
||||||
|
'src/third_party/pipewire/linux-amd64': {
|
||||||
|
'packages': [
|
||||||
|
{
|
||||||
|
'package': 'chromium/third_party/pipewire/linux-amd64',
|
||||||
|
'version': 'BaVKmAmwpjdS6O0pnjSaMNSKhO1nmk5mRnyPVAJ2-HEC',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'package': 'chromium/third_party/pipewire-media-session/linux-amd64',
|
||||||
|
'version': 'Y6wUeITvAA0QD1vt8_a7eQdzbp0gkI1B02qfZUMJdowC',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
'condition': 'checkout_linux',
|
||||||
|
'dep_type': 'cipd',
|
||||||
|
},
|
||||||
|
|
||||||
# Everything coming after this is automatically updated by the auto-roller.
|
# Everything coming after this is automatically updated by the auto-roller.
|
||||||
# === ANDROID_DEPS Generated Code Start ===
|
# === ANDROID_DEPS Generated Code Start ===
|
||||||
|
|||||||
@ -3324,6 +3324,32 @@
|
|||||||
},
|
},
|
||||||
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
".",
|
||||||
|
"--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json"
|
||||||
|
],
|
||||||
|
"isolate_name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"merge": {
|
||||||
|
"args": [],
|
||||||
|
"script": "//testing/merge_scripts/standard_isolated_script_merge.py"
|
||||||
|
},
|
||||||
|
"name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"resultdb": {
|
||||||
|
"result_file": "${ISOLATED_OUTDIR}/gtest_output.json",
|
||||||
|
"result_format": "gtest_json"
|
||||||
|
},
|
||||||
|
"swarming": {
|
||||||
|
"can_use_on_swarming_builders": true,
|
||||||
|
"dimension_sets": [
|
||||||
|
{
|
||||||
|
"cpu": "x86-64",
|
||||||
|
"os": "Ubuntu-18.04"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test_id_prefix": "ninja://modules/desktop_capture:pipewire_shared_screencast_stream_test/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"isolate_name": "rtc_media_unittests",
|
"isolate_name": "rtc_media_unittests",
|
||||||
"merge": {
|
"merge": {
|
||||||
@ -4623,6 +4649,32 @@
|
|||||||
},
|
},
|
||||||
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
".",
|
||||||
|
"--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json"
|
||||||
|
],
|
||||||
|
"isolate_name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"merge": {
|
||||||
|
"args": [],
|
||||||
|
"script": "//testing/merge_scripts/standard_isolated_script_merge.py"
|
||||||
|
},
|
||||||
|
"name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"resultdb": {
|
||||||
|
"result_file": "${ISOLATED_OUTDIR}/gtest_output.json",
|
||||||
|
"result_format": "gtest_json"
|
||||||
|
},
|
||||||
|
"swarming": {
|
||||||
|
"can_use_on_swarming_builders": true,
|
||||||
|
"dimension_sets": [
|
||||||
|
{
|
||||||
|
"cpu": "x86-64",
|
||||||
|
"os": "Ubuntu-18.04"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test_id_prefix": "ninja://modules/desktop_capture:pipewire_shared_screencast_stream_test/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"isolate_name": "rtc_media_unittests",
|
"isolate_name": "rtc_media_unittests",
|
||||||
"merge": {
|
"merge": {
|
||||||
@ -5056,6 +5108,32 @@
|
|||||||
},
|
},
|
||||||
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
".",
|
||||||
|
"--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json"
|
||||||
|
],
|
||||||
|
"isolate_name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"merge": {
|
||||||
|
"args": [],
|
||||||
|
"script": "//testing/merge_scripts/standard_isolated_script_merge.py"
|
||||||
|
},
|
||||||
|
"name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"resultdb": {
|
||||||
|
"result_file": "${ISOLATED_OUTDIR}/gtest_output.json",
|
||||||
|
"result_format": "gtest_json"
|
||||||
|
},
|
||||||
|
"swarming": {
|
||||||
|
"can_use_on_swarming_builders": true,
|
||||||
|
"dimension_sets": [
|
||||||
|
{
|
||||||
|
"cpu": "x86-64",
|
||||||
|
"os": "Ubuntu-18.04"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test_id_prefix": "ninja://modules/desktop_capture:pipewire_shared_screencast_stream_test/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"isolate_name": "rtc_media_unittests",
|
"isolate_name": "rtc_media_unittests",
|
||||||
"merge": {
|
"merge": {
|
||||||
@ -6358,6 +6436,32 @@
|
|||||||
},
|
},
|
||||||
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
".",
|
||||||
|
"--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json"
|
||||||
|
],
|
||||||
|
"isolate_name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"merge": {
|
||||||
|
"args": [],
|
||||||
|
"script": "//testing/merge_scripts/standard_isolated_script_merge.py"
|
||||||
|
},
|
||||||
|
"name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"resultdb": {
|
||||||
|
"result_file": "${ISOLATED_OUTDIR}/gtest_output.json",
|
||||||
|
"result_format": "gtest_json"
|
||||||
|
},
|
||||||
|
"swarming": {
|
||||||
|
"can_use_on_swarming_builders": true,
|
||||||
|
"dimension_sets": [
|
||||||
|
{
|
||||||
|
"cpu": "x86-64",
|
||||||
|
"os": "Ubuntu-18.04"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test_id_prefix": "ninja://modules/desktop_capture:pipewire_shared_screencast_stream_test/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"isolate_name": "rtc_media_unittests",
|
"isolate_name": "rtc_media_unittests",
|
||||||
"merge": {
|
"merge": {
|
||||||
@ -6792,6 +6896,32 @@
|
|||||||
},
|
},
|
||||||
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
".",
|
||||||
|
"--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json"
|
||||||
|
],
|
||||||
|
"isolate_name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"merge": {
|
||||||
|
"args": [],
|
||||||
|
"script": "//testing/merge_scripts/standard_isolated_script_merge.py"
|
||||||
|
},
|
||||||
|
"name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"resultdb": {
|
||||||
|
"result_file": "${ISOLATED_OUTDIR}/gtest_output.json",
|
||||||
|
"result_format": "gtest_json"
|
||||||
|
},
|
||||||
|
"swarming": {
|
||||||
|
"can_use_on_swarming_builders": true,
|
||||||
|
"dimension_sets": [
|
||||||
|
{
|
||||||
|
"cpu": "x86-64",
|
||||||
|
"os": "Ubuntu-18.04"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test_id_prefix": "ninja://modules/desktop_capture:pipewire_shared_screencast_stream_test/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"isolate_name": "rtc_media_unittests",
|
"isolate_name": "rtc_media_unittests",
|
||||||
"merge": {
|
"merge": {
|
||||||
|
|||||||
@ -80,6 +80,14 @@
|
|||||||
"label": "//pc:peerconnection_unittests",
|
"label": "//pc:peerconnection_unittests",
|
||||||
"type": "console_test_launcher",
|
"type": "console_test_launcher",
|
||||||
},
|
},
|
||||||
|
"pipewire_shared_screencast_stream_test": {
|
||||||
|
"label":
|
||||||
|
"//modules/desktop_capture:pipewire_shared_screencast_stream_test",
|
||||||
|
"type":
|
||||||
|
"script",
|
||||||
|
"script":
|
||||||
|
"//modules/desktop_capture/linux/wayland/test/shared_screencast_stream_test.py",
|
||||||
|
},
|
||||||
"rtc_media_unittests": {
|
"rtc_media_unittests": {
|
||||||
"label": "//media:rtc_media_unittests",
|
"label": "//media:rtc_media_unittests",
|
||||||
"type": "console_test_launcher",
|
"type": "console_test_launcher",
|
||||||
|
|||||||
@ -183,6 +183,12 @@
|
|||||||
'voip_unittests': {},
|
'voip_unittests': {},
|
||||||
'webrtc_nonparallel_tests': {},
|
'webrtc_nonparallel_tests': {},
|
||||||
},
|
},
|
||||||
|
'linux_desktop_specific_tests': {
|
||||||
|
'pipewire_shared_screencast_stream_test': {
|
||||||
|
'args': ['.'],
|
||||||
|
'mixins': ['resultdb-gtest-json-format'],
|
||||||
|
},
|
||||||
|
},
|
||||||
'more_configs_tests': {
|
'more_configs_tests': {
|
||||||
'peerconnection_unittests': {
|
'peerconnection_unittests': {
|
||||||
'swarming': {
|
'swarming': {
|
||||||
@ -225,5 +231,20 @@
|
|||||||
'desktop_tests',
|
'desktop_tests',
|
||||||
'video_capture_tests',
|
'video_capture_tests',
|
||||||
],
|
],
|
||||||
|
'linux_desktop_tests_tryserver': [
|
||||||
|
'desktop_tests',
|
||||||
|
'linux_desktop_specific_tests',
|
||||||
|
'video_capture_tests_tryserver',
|
||||||
|
'webrtc_perf_tests_tryserver',
|
||||||
|
],
|
||||||
|
'linux_desktop_tests_with_video_capture': [
|
||||||
|
'desktop_tests',
|
||||||
|
'linux_desktop_specific_tests',
|
||||||
|
'video_capture_tests',
|
||||||
|
],
|
||||||
|
'linux_tests': [
|
||||||
|
'desktop_tests',
|
||||||
|
'linux_desktop_specific_tests',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5941,6 +5941,32 @@
|
|||||||
},
|
},
|
||||||
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
".",
|
||||||
|
"--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json"
|
||||||
|
],
|
||||||
|
"isolate_name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"merge": {
|
||||||
|
"args": [],
|
||||||
|
"script": "//testing/merge_scripts/standard_isolated_script_merge.py"
|
||||||
|
},
|
||||||
|
"name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"resultdb": {
|
||||||
|
"result_file": "${ISOLATED_OUTDIR}/gtest_output.json",
|
||||||
|
"result_format": "gtest_json"
|
||||||
|
},
|
||||||
|
"swarming": {
|
||||||
|
"can_use_on_swarming_builders": true,
|
||||||
|
"dimension_sets": [
|
||||||
|
{
|
||||||
|
"cpu": "x86-64",
|
||||||
|
"os": "Ubuntu-18.04"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test_id_prefix": "ninja://modules/desktop_capture:pipewire_shared_screencast_stream_test/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"isolate_name": "rtc_media_unittests",
|
"isolate_name": "rtc_media_unittests",
|
||||||
"merge": {
|
"merge": {
|
||||||
@ -6380,6 +6406,32 @@
|
|||||||
},
|
},
|
||||||
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
".",
|
||||||
|
"--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json"
|
||||||
|
],
|
||||||
|
"isolate_name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"merge": {
|
||||||
|
"args": [],
|
||||||
|
"script": "//testing/merge_scripts/standard_isolated_script_merge.py"
|
||||||
|
},
|
||||||
|
"name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"resultdb": {
|
||||||
|
"result_file": "${ISOLATED_OUTDIR}/gtest_output.json",
|
||||||
|
"result_format": "gtest_json"
|
||||||
|
},
|
||||||
|
"swarming": {
|
||||||
|
"can_use_on_swarming_builders": true,
|
||||||
|
"dimension_sets": [
|
||||||
|
{
|
||||||
|
"cpu": "x86-64",
|
||||||
|
"os": "Ubuntu-18.04"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test_id_prefix": "ninja://modules/desktop_capture:pipewire_shared_screencast_stream_test/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"isolate_name": "rtc_media_unittests",
|
"isolate_name": "rtc_media_unittests",
|
||||||
"merge": {
|
"merge": {
|
||||||
@ -6814,6 +6866,32 @@
|
|||||||
},
|
},
|
||||||
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
".",
|
||||||
|
"--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json"
|
||||||
|
],
|
||||||
|
"isolate_name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"merge": {
|
||||||
|
"args": [],
|
||||||
|
"script": "//testing/merge_scripts/standard_isolated_script_merge.py"
|
||||||
|
},
|
||||||
|
"name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"resultdb": {
|
||||||
|
"result_file": "${ISOLATED_OUTDIR}/gtest_output.json",
|
||||||
|
"result_format": "gtest_json"
|
||||||
|
},
|
||||||
|
"swarming": {
|
||||||
|
"can_use_on_swarming_builders": true,
|
||||||
|
"dimension_sets": [
|
||||||
|
{
|
||||||
|
"cpu": "x86-64",
|
||||||
|
"os": "Ubuntu-18.04"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test_id_prefix": "ninja://modules/desktop_capture:pipewire_shared_screencast_stream_test/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"isolate_name": "rtc_media_unittests",
|
"isolate_name": "rtc_media_unittests",
|
||||||
"merge": {
|
"merge": {
|
||||||
@ -7706,6 +7784,32 @@
|
|||||||
},
|
},
|
||||||
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
".",
|
||||||
|
"--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json"
|
||||||
|
],
|
||||||
|
"isolate_name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"merge": {
|
||||||
|
"args": [],
|
||||||
|
"script": "//testing/merge_scripts/standard_isolated_script_merge.py"
|
||||||
|
},
|
||||||
|
"name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"resultdb": {
|
||||||
|
"result_file": "${ISOLATED_OUTDIR}/gtest_output.json",
|
||||||
|
"result_format": "gtest_json"
|
||||||
|
},
|
||||||
|
"swarming": {
|
||||||
|
"can_use_on_swarming_builders": true,
|
||||||
|
"dimension_sets": [
|
||||||
|
{
|
||||||
|
"cpu": "x86-64",
|
||||||
|
"os": "Ubuntu-18.04"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test_id_prefix": "ninja://modules/desktop_capture:pipewire_shared_screencast_stream_test/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"isolate_name": "rtc_media_unittests",
|
"isolate_name": "rtc_media_unittests",
|
||||||
"merge": {
|
"merge": {
|
||||||
@ -8621,6 +8725,32 @@
|
|||||||
},
|
},
|
||||||
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
".",
|
||||||
|
"--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json"
|
||||||
|
],
|
||||||
|
"isolate_name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"merge": {
|
||||||
|
"args": [],
|
||||||
|
"script": "//testing/merge_scripts/standard_isolated_script_merge.py"
|
||||||
|
},
|
||||||
|
"name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"resultdb": {
|
||||||
|
"result_file": "${ISOLATED_OUTDIR}/gtest_output.json",
|
||||||
|
"result_format": "gtest_json"
|
||||||
|
},
|
||||||
|
"swarming": {
|
||||||
|
"can_use_on_swarming_builders": true,
|
||||||
|
"dimension_sets": [
|
||||||
|
{
|
||||||
|
"cpu": "x86-64",
|
||||||
|
"os": "Ubuntu-18.04"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test_id_prefix": "ninja://modules/desktop_capture:pipewire_shared_screencast_stream_test/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"isolate_name": "rtc_media_unittests",
|
"isolate_name": "rtc_media_unittests",
|
||||||
"merge": {
|
"merge": {
|
||||||
@ -9054,6 +9184,32 @@
|
|||||||
},
|
},
|
||||||
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
"test_id_prefix": "ninja://pc:peerconnection_unittests/"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
".",
|
||||||
|
"--gtest_output=json:${ISOLATED_OUTDIR}/gtest_output.json"
|
||||||
|
],
|
||||||
|
"isolate_name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"merge": {
|
||||||
|
"args": [],
|
||||||
|
"script": "//testing/merge_scripts/standard_isolated_script_merge.py"
|
||||||
|
},
|
||||||
|
"name": "pipewire_shared_screencast_stream_test",
|
||||||
|
"resultdb": {
|
||||||
|
"result_file": "${ISOLATED_OUTDIR}/gtest_output.json",
|
||||||
|
"result_format": "gtest_json"
|
||||||
|
},
|
||||||
|
"swarming": {
|
||||||
|
"can_use_on_swarming_builders": true,
|
||||||
|
"dimension_sets": [
|
||||||
|
{
|
||||||
|
"cpu": "x86-64",
|
||||||
|
"os": "Ubuntu-18.04"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test_id_prefix": "ninja://modules/desktop_capture:pipewire_shared_screencast_stream_test/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"isolate_name": "rtc_media_unittests",
|
"isolate_name": "rtc_media_unittests",
|
||||||
"merge": {
|
"merge": {
|
||||||
|
|||||||
@ -83,7 +83,7 @@
|
|||||||
'os_type': 'linux',
|
'os_type': 'linux',
|
||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests',
|
'isolated_scripts': 'linux_tests',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'Linux MSan': {
|
'Linux MSan': {
|
||||||
@ -91,6 +91,9 @@
|
|||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests',
|
'isolated_scripts': 'desktop_tests',
|
||||||
|
# TODO(crbug.com/webrtc/14568): use 'linux_tests'
|
||||||
|
# Fails on "MemorySanitizer: use-of-uninitialized-value in
|
||||||
|
# libpipewire-0.3.so"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'Linux Tsan v2': {
|
'Linux Tsan v2': {
|
||||||
@ -98,20 +101,23 @@
|
|||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests',
|
'isolated_scripts': 'desktop_tests',
|
||||||
|
# TODO(crbug.com/webrtc/14568): use 'linux_tests'
|
||||||
|
# Fails on "ThreadSanitizer: data race on vptr (ctor/dtor vs
|
||||||
|
# virtual call) in shared_screencast_stream_test"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'Linux UBSan': {
|
'Linux UBSan': {
|
||||||
'os_type': 'linux',
|
'os_type': 'linux',
|
||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests',
|
'isolated_scripts': 'linux_tests',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'Linux UBSan vptr': {
|
'Linux UBSan vptr': {
|
||||||
'os_type': 'linux',
|
'os_type': 'linux',
|
||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests',
|
'isolated_scripts': 'linux_tests',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'Linux32 Debug': {
|
'Linux32 Debug': {
|
||||||
@ -135,7 +141,7 @@
|
|||||||
'os_type': 'linux',
|
'os_type': 'linux',
|
||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests',
|
'isolated_scripts': 'linux_tests',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'Linux64 Debug (ARM)': {},
|
'Linux64 Debug (ARM)': {},
|
||||||
@ -143,7 +149,7 @@
|
|||||||
'os_type': 'linux',
|
'os_type': 'linux',
|
||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests_with_video_capture',
|
'isolated_scripts': 'linux_desktop_tests_with_video_capture',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'Linux64 Release (ARM)': {},
|
'Linux64 Release (ARM)': {},
|
||||||
@ -466,7 +472,7 @@
|
|||||||
'os_type': 'linux',
|
'os_type': 'linux',
|
||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests',
|
'isolated_scripts': 'linux_tests',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'linux_compile_arm64_dbg': {},
|
'linux_compile_arm64_dbg': {},
|
||||||
@ -479,7 +485,7 @@
|
|||||||
'os_type': 'linux',
|
'os_type': 'linux',
|
||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests',
|
'isolated_scripts': 'linux_tests',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'linux_libfuzzer_rel': {},
|
'linux_libfuzzer_rel': {},
|
||||||
@ -487,7 +493,7 @@
|
|||||||
'os_type': 'linux',
|
'os_type': 'linux',
|
||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests',
|
'isolated_scripts': 'linux_tests',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'linux_more_configs': {
|
'linux_more_configs': {
|
||||||
@ -502,13 +508,16 @@
|
|||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests',
|
'isolated_scripts': 'desktop_tests',
|
||||||
|
# TODO(crbug.com/webrtc/14568): use 'linux_tests'
|
||||||
|
# Fails on "MemorySanitizer: use-of-uninitialized-value in
|
||||||
|
# libpipewire-0.3.so"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'linux_rel': {
|
'linux_rel': {
|
||||||
'os_type': 'linux',
|
'os_type': 'linux',
|
||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests_tryserver',
|
'isolated_scripts': 'linux_desktop_tests_tryserver',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'linux_tsan2': {
|
'linux_tsan2': {
|
||||||
@ -516,20 +525,23 @@
|
|||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests',
|
'isolated_scripts': 'desktop_tests',
|
||||||
|
# TODO(crbug.com/webrtc/14568): use 'linux_tests'
|
||||||
|
# Fails on "ThreadSanitizer: data race on vptr (ctor/dtor vs
|
||||||
|
# virtual call) in shared_screencast_stream_test"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'linux_ubsan': {
|
'linux_ubsan': {
|
||||||
'os_type': 'linux',
|
'os_type': 'linux',
|
||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests',
|
'isolated_scripts': 'linux_tests',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'linux_ubsan_vptr': {
|
'linux_ubsan_vptr': {
|
||||||
'os_type': 'linux',
|
'os_type': 'linux',
|
||||||
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
'mixins': ['linux-bionic', 'x86-64', 'resultdb-json-format'],
|
||||||
'test_suites': {
|
'test_suites': {
|
||||||
'isolated_scripts': 'desktop_tests',
|
'isolated_scripts': 'linux_tests',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'linux_x86_dbg': {
|
'linux_x86_dbg': {
|
||||||
|
|||||||
@ -95,6 +95,66 @@ if (rtc_include_tests) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((is_linux || is_chromeos) && rtc_use_pipewire) {
|
||||||
|
rtc_test("shared_screencast_stream_test") {
|
||||||
|
testonly = true
|
||||||
|
|
||||||
|
sources = [
|
||||||
|
"linux/wayland/shared_screencast_stream_unittest.cc",
|
||||||
|
"linux/wayland/test/fake_screencast_stream.cc",
|
||||||
|
"linux/wayland/test/fake_screencast_stream.h",
|
||||||
|
]
|
||||||
|
|
||||||
|
configs += [
|
||||||
|
":gio",
|
||||||
|
":pipewire",
|
||||||
|
":gbm",
|
||||||
|
":egl",
|
||||||
|
":epoxy",
|
||||||
|
":libdrm",
|
||||||
|
]
|
||||||
|
|
||||||
|
deps = [
|
||||||
|
":desktop_capture",
|
||||||
|
":desktop_capture_mock",
|
||||||
|
":primitives",
|
||||||
|
"../../rtc_base:checks",
|
||||||
|
"../../rtc_base:logging",
|
||||||
|
"../../rtc_base:random",
|
||||||
|
"../../rtc_base:timeutils",
|
||||||
|
|
||||||
|
# TODO(bugs.webrtc.org/9987): Remove this dep on rtc_base:rtc_base once
|
||||||
|
# rtc_base:threading is fully defined.
|
||||||
|
"../../rtc_base:rtc_base",
|
||||||
|
"../../rtc_base:task_queue_for_test",
|
||||||
|
"../../rtc_base:threading",
|
||||||
|
"../../system_wrappers",
|
||||||
|
"../../test:test_main",
|
||||||
|
"../../test:test_support",
|
||||||
|
"//api/units:time_delta",
|
||||||
|
"//rtc_base:rtc_event",
|
||||||
|
]
|
||||||
|
|
||||||
|
if (!rtc_link_pipewire) {
|
||||||
|
deps += [ ":pipewire_stubs" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
public_configs = [ ":pipewire_config" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
group("pipewire_shared_screencast_stream_test") {
|
||||||
|
testonly = true
|
||||||
|
|
||||||
|
deps = [ ":shared_screencast_stream_test" ]
|
||||||
|
|
||||||
|
data = [
|
||||||
|
"../../third_party/pipewire",
|
||||||
|
"linux/wayland/test/shared_screencast_stream_test.py",
|
||||||
|
"${root_out_dir}/shared_screencast_stream_test",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rtc_library("desktop_capture_unittests") {
|
rtc_library("desktop_capture_unittests") {
|
||||||
testonly = true
|
testonly = true
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,8 @@ pw_stream * pw_stream_new(pw_core *core, const char *name, pw_properties *props)
|
|||||||
int pw_stream_queue_buffer(pw_stream *stream, pw_buffer *buffer);
|
int pw_stream_queue_buffer(pw_stream *stream, pw_buffer *buffer);
|
||||||
int pw_stream_set_active(pw_stream *stream, bool active);
|
int pw_stream_set_active(pw_stream *stream, bool active);
|
||||||
int pw_stream_update_params(pw_stream *stream, const spa_pod **params, uint32_t n_params);
|
int pw_stream_update_params(pw_stream *stream, const spa_pod **params, uint32_t n_params);
|
||||||
|
uint32_t pw_stream_get_node_id(pw_stream *stream);
|
||||||
|
pw_stream_state pw_stream_get_state(pw_stream *stream, const char **error);
|
||||||
|
|
||||||
// thread-loop.h
|
// thread-loop.h
|
||||||
void pw_thread_loop_destroy(pw_thread_loop *loop);
|
void pw_thread_loop_destroy(pw_thread_loop *loop);
|
||||||
|
|||||||
@ -21,8 +21,6 @@
|
|||||||
#include "absl/memory/memory.h"
|
#include "absl/memory/memory.h"
|
||||||
#include "modules/desktop_capture/linux/wayland/egl_dmabuf.h"
|
#include "modules/desktop_capture/linux/wayland/egl_dmabuf.h"
|
||||||
#include "modules/desktop_capture/linux/wayland/screencast_stream_utils.h"
|
#include "modules/desktop_capture/linux/wayland/screencast_stream_utils.h"
|
||||||
#include "modules/desktop_capture/screen_capture_frame_queue.h"
|
|
||||||
#include "modules/desktop_capture/shared_desktop_frame.h"
|
|
||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
#include "rtc_base/logging.h"
|
#include "rtc_base/logging.h"
|
||||||
#include "rtc_base/sanitizer.h"
|
#include "rtc_base/sanitizer.h"
|
||||||
@ -90,8 +88,11 @@ class SharedScreenCastStreamPrivate {
|
|||||||
uint32_t width = 0,
|
uint32_t width = 0,
|
||||||
uint32_t height = 0);
|
uint32_t height = 0);
|
||||||
void UpdateScreenCastStreamResolution(uint32_t width, uint32_t height);
|
void UpdateScreenCastStreamResolution(uint32_t width, uint32_t height);
|
||||||
|
void SetObserver(SharedScreenCastStream::Observer* observer) {
|
||||||
|
observer_ = observer;
|
||||||
|
}
|
||||||
void StopScreenCastStream();
|
void StopScreenCastStream();
|
||||||
std::unique_ptr<DesktopFrame> CaptureFrame();
|
std::unique_ptr<SharedDesktopFrame> CaptureFrame();
|
||||||
std::unique_ptr<MouseCursor> CaptureCursor();
|
std::unique_ptr<MouseCursor> CaptureCursor();
|
||||||
DesktopVector CaptureCursorPosition();
|
DesktopVector CaptureCursorPosition();
|
||||||
|
|
||||||
@ -99,6 +100,7 @@ class SharedScreenCastStreamPrivate {
|
|||||||
// Stops the streams and cleans up any in-use elements.
|
// Stops the streams and cleans up any in-use elements.
|
||||||
void StopAndCleanupStream();
|
void StopAndCleanupStream();
|
||||||
|
|
||||||
|
SharedScreenCastStream::Observer* observer_ = nullptr;
|
||||||
uint32_t pw_stream_node_id_ = 0;
|
uint32_t pw_stream_node_id_ = 0;
|
||||||
|
|
||||||
DesktopSize stream_size_ = {};
|
DesktopSize stream_size_ = {};
|
||||||
@ -575,15 +577,15 @@ void SharedScreenCastStreamPrivate::StopAndCleanupStream() {
|
|||||||
pw_main_loop_ = nullptr;
|
pw_main_loop_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<DesktopFrame> SharedScreenCastStreamPrivate::CaptureFrame() {
|
std::unique_ptr<SharedDesktopFrame>
|
||||||
|
SharedScreenCastStreamPrivate::CaptureFrame() {
|
||||||
webrtc::MutexLock lock(&queue_lock_);
|
webrtc::MutexLock lock(&queue_lock_);
|
||||||
|
|
||||||
if (!pw_stream_ || !queue_.current_frame()) {
|
if (!pw_stream_ || !queue_.current_frame()) {
|
||||||
return std::unique_ptr<DesktopFrame>{};
|
return std::unique_ptr<SharedDesktopFrame>{};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<SharedDesktopFrame> frame = queue_.current_frame()->Share();
|
return queue_.current_frame()->Share();
|
||||||
return std::move(frame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<MouseCursor> SharedScreenCastStreamPrivate::CaptureCursor() {
|
std::unique_ptr<MouseCursor> SharedScreenCastStreamPrivate::CaptureCursor() {
|
||||||
@ -628,8 +630,18 @@ void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) {
|
|||||||
DesktopRect::MakeWH(bitmap->size.width, bitmap->size.height));
|
DesktopRect::MakeWH(bitmap->size.width, bitmap->size.height));
|
||||||
mouse_cursor_ = std::make_unique<MouseCursor>(
|
mouse_cursor_ = std::make_unique<MouseCursor>(
|
||||||
mouse_frame, DesktopVector(cursor->hotspot.x, cursor->hotspot.y));
|
mouse_frame, DesktopVector(cursor->hotspot.x, cursor->hotspot.y));
|
||||||
|
|
||||||
|
// For testing purpose
|
||||||
|
if (observer_) {
|
||||||
|
observer_->OnCursorShapeChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mouse_cursor_position_.set(cursor->position.x, cursor->position.y);
|
mouse_cursor_position_.set(cursor->position.x, cursor->position.y);
|
||||||
|
|
||||||
|
// For testing purpose
|
||||||
|
if (observer_) {
|
||||||
|
observer_->OnCursorPositionChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -704,6 +716,10 @@ void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!src) {
|
if (!src) {
|
||||||
|
// For testing purpose
|
||||||
|
if (observer_) {
|
||||||
|
observer_->OnFailedToProcessBuffer();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -730,6 +746,11 @@ void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) {
|
|||||||
videocrop_metadata->region.size.height >
|
videocrop_metadata->region.size.height >
|
||||||
static_cast<uint32_t>(stream_size_.height()))) {
|
static_cast<uint32_t>(stream_size_.height()))) {
|
||||||
RTC_LOG(LS_ERROR) << "Stream metadata sizes are wrong!";
|
RTC_LOG(LS_ERROR) << "Stream metadata sizes are wrong!";
|
||||||
|
|
||||||
|
if (observer_) {
|
||||||
|
observer_->OnFailedToProcessBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -795,6 +816,10 @@ void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) {
|
|||||||
queue_.MoveToNextFrame();
|
queue_.MoveToNextFrame();
|
||||||
if (queue_.current_frame() && queue_.current_frame()->IsShared()) {
|
if (queue_.current_frame() && queue_.current_frame()->IsShared()) {
|
||||||
RTC_DLOG(LS_WARNING) << "Overwriting frame that is still shared";
|
RTC_DLOG(LS_WARNING) << "Overwriting frame that is still shared";
|
||||||
|
|
||||||
|
if (observer_) {
|
||||||
|
observer_->OnFailedToProcessBuffer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!queue_.current_frame() ||
|
if (!queue_.current_frame() ||
|
||||||
@ -821,6 +846,11 @@ void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) {
|
|||||||
|
|
||||||
queue_.current_frame()->mutable_updated_region()->SetRect(
|
queue_.current_frame()->mutable_updated_region()->SetRect(
|
||||||
DesktopRect::MakeSize(queue_.current_frame()->size()));
|
DesktopRect::MakeSize(queue_.current_frame()->size()));
|
||||||
|
|
||||||
|
// For testing purpose
|
||||||
|
if (observer_) {
|
||||||
|
observer_->OnDesktopFrameChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedScreenCastStreamPrivate::ConvertRGBxToBGRx(uint8_t* frame,
|
void SharedScreenCastStreamPrivate::ConvertRGBxToBGRx(uint8_t* frame,
|
||||||
@ -861,11 +891,16 @@ void SharedScreenCastStream::UpdateScreenCastStreamResolution(uint32_t width,
|
|||||||
private_->UpdateScreenCastStreamResolution(width, height);
|
private_->UpdateScreenCastStreamResolution(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SharedScreenCastStream::SetObserver(
|
||||||
|
SharedScreenCastStream::Observer* observer) {
|
||||||
|
private_->SetObserver(observer);
|
||||||
|
}
|
||||||
|
|
||||||
void SharedScreenCastStream::StopScreenCastStream() {
|
void SharedScreenCastStream::StopScreenCastStream() {
|
||||||
private_->StopScreenCastStream();
|
private_->StopScreenCastStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<DesktopFrame> SharedScreenCastStream::CaptureFrame() {
|
std::unique_ptr<SharedDesktopFrame> SharedScreenCastStream::CaptureFrame() {
|
||||||
return private_->CaptureFrame();
|
return private_->CaptureFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,8 +16,9 @@
|
|||||||
#include "absl/types/optional.h"
|
#include "absl/types/optional.h"
|
||||||
#include "api/ref_counted_base.h"
|
#include "api/ref_counted_base.h"
|
||||||
#include "api/scoped_refptr.h"
|
#include "api/scoped_refptr.h"
|
||||||
#include "modules/desktop_capture/desktop_frame.h"
|
|
||||||
#include "modules/desktop_capture/mouse_cursor.h"
|
#include "modules/desktop_capture/mouse_cursor.h"
|
||||||
|
#include "modules/desktop_capture/screen_capture_frame_queue.h"
|
||||||
|
#include "modules/desktop_capture/shared_desktop_frame.h"
|
||||||
#include "rtc_base/system/rtc_export.h"
|
#include "rtc_base/system/rtc_export.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
@ -27,6 +28,18 @@ class SharedScreenCastStreamPrivate;
|
|||||||
class RTC_EXPORT SharedScreenCastStream
|
class RTC_EXPORT SharedScreenCastStream
|
||||||
: public rtc::RefCountedNonVirtual<SharedScreenCastStream> {
|
: public rtc::RefCountedNonVirtual<SharedScreenCastStream> {
|
||||||
public:
|
public:
|
||||||
|
class Observer {
|
||||||
|
public:
|
||||||
|
virtual void OnCursorPositionChanged() = 0;
|
||||||
|
virtual void OnCursorShapeChanged() = 0;
|
||||||
|
virtual void OnDesktopFrameChanged() = 0;
|
||||||
|
virtual void OnFailedToProcessBuffer() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Observer() = default;
|
||||||
|
virtual ~Observer() = default;
|
||||||
|
};
|
||||||
|
|
||||||
static rtc::scoped_refptr<SharedScreenCastStream> CreateDefault();
|
static rtc::scoped_refptr<SharedScreenCastStream> CreateDefault();
|
||||||
|
|
||||||
bool StartScreenCastStream(uint32_t stream_node_id);
|
bool StartScreenCastStream(uint32_t stream_node_id);
|
||||||
@ -35,6 +48,7 @@ class RTC_EXPORT SharedScreenCastStream
|
|||||||
uint32_t width = 0,
|
uint32_t width = 0,
|
||||||
uint32_t height = 0);
|
uint32_t height = 0);
|
||||||
void UpdateScreenCastStreamResolution(uint32_t width, uint32_t height);
|
void UpdateScreenCastStreamResolution(uint32_t width, uint32_t height);
|
||||||
|
void SetObserver(SharedScreenCastStream::Observer* observer);
|
||||||
void StopScreenCastStream();
|
void StopScreenCastStream();
|
||||||
|
|
||||||
// Below functions return the most recent information we get from a
|
// Below functions return the most recent information we get from a
|
||||||
@ -47,7 +61,7 @@ class RTC_EXPORT SharedScreenCastStream
|
|||||||
// Returns the most recent screen/window frame we obtained from PipeWire
|
// Returns the most recent screen/window frame we obtained from PipeWire
|
||||||
// buffer. Will return an empty frame in case we didn't manage to get a frame
|
// buffer. Will return an empty frame in case we didn't manage to get a frame
|
||||||
// from PipeWire buffer.
|
// from PipeWire buffer.
|
||||||
std::unique_ptr<DesktopFrame> CaptureFrame();
|
std::unique_ptr<SharedDesktopFrame> CaptureFrame();
|
||||||
|
|
||||||
// Returns the most recent mouse cursor image. Will return an nullptr cursor
|
// Returns the most recent mouse cursor image. Will return an nullptr cursor
|
||||||
// in case we didn't manage to get a cursor from PipeWire buffer. NOTE: the
|
// in case we didn't manage to get a cursor from PipeWire buffer. NOTE: the
|
||||||
@ -65,6 +79,13 @@ class RTC_EXPORT SharedScreenCastStream
|
|||||||
SharedScreenCastStream();
|
SharedScreenCastStream();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class SharedScreenCastStreamPrivate;
|
||||||
|
// Allows test cases to use private functionality
|
||||||
|
friend class PipeWireStreamTest;
|
||||||
|
|
||||||
|
// FIXME: is this a useful thing to be public?
|
||||||
|
explicit SharedScreenCastStream(Observer* notifier);
|
||||||
|
|
||||||
SharedScreenCastStream(const SharedScreenCastStream&) = delete;
|
SharedScreenCastStream(const SharedScreenCastStream&) = delete;
|
||||||
SharedScreenCastStream& operator=(const SharedScreenCastStream&) = delete;
|
SharedScreenCastStream& operator=(const SharedScreenCastStream&) = delete;
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "api/units/time_delta.h"
|
||||||
|
#include "modules/desktop_capture/desktop_capturer.h"
|
||||||
|
#include "modules/desktop_capture/desktop_frame.h"
|
||||||
|
#include "modules/desktop_capture/linux/wayland/test/fake_screencast_stream.h"
|
||||||
|
#include "modules/desktop_capture/rgba_color.h"
|
||||||
|
#include "rtc_base/event.h"
|
||||||
|
#include "test/gmock.h"
|
||||||
|
#include "test/gtest.h"
|
||||||
|
|
||||||
|
using ::testing::_;
|
||||||
|
using ::testing::Ge;
|
||||||
|
using ::testing::Invoke;
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
constexpr TimeDelta kShortWait = TimeDelta::Seconds(2);
|
||||||
|
constexpr TimeDelta kLongWait = TimeDelta::Seconds(10);
|
||||||
|
|
||||||
|
constexpr int kBytesPerPixel = 4;
|
||||||
|
constexpr int32_t kWidth = 800;
|
||||||
|
constexpr int32_t kHeight = 640;
|
||||||
|
|
||||||
|
class PipeWireStreamTest : public ::testing::Test,
|
||||||
|
public FakeScreenCastStream::Observer,
|
||||||
|
public SharedScreenCastStream::Observer {
|
||||||
|
public:
|
||||||
|
PipeWireStreamTest()
|
||||||
|
: fake_screencast_stream_(
|
||||||
|
std::make_unique<FakeScreenCastStream>(this, kWidth, kHeight)),
|
||||||
|
shared_screencast_stream_(new SharedScreenCastStream()) {
|
||||||
|
shared_screencast_stream_->SetObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~PipeWireStreamTest() override {}
|
||||||
|
|
||||||
|
// FakeScreenCastPortal::Observer
|
||||||
|
MOCK_METHOD(void, OnFrameRecorded, (), (override));
|
||||||
|
MOCK_METHOD(void, OnStreamReady, (uint32_t stream_node_id), (override));
|
||||||
|
MOCK_METHOD(void, OnStartStreaming, (), (override));
|
||||||
|
MOCK_METHOD(void, OnStopStreaming, (), (override));
|
||||||
|
|
||||||
|
// SharedScreenCastStream::Observer
|
||||||
|
MOCK_METHOD(void, OnCursorPositionChanged, (), (override));
|
||||||
|
MOCK_METHOD(void, OnCursorShapeChanged, (), (override));
|
||||||
|
MOCK_METHOD(void, OnDesktopFrameChanged, (), (override));
|
||||||
|
MOCK_METHOD(void, OnFailedToProcessBuffer, (), (override));
|
||||||
|
|
||||||
|
void StartScreenCastStream(uint32_t stream_node_id) {
|
||||||
|
shared_screencast_stream_->StartScreenCastStream(stream_node_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint recorded_frames_ = 0;
|
||||||
|
bool streaming_ = false;
|
||||||
|
std::unique_ptr<FakeScreenCastStream> fake_screencast_stream_;
|
||||||
|
rtc::scoped_refptr<SharedScreenCastStream> shared_screencast_stream_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(PipeWireStreamTest, TestPipeWire) {
|
||||||
|
// Set expectations for PipeWire to successfully connect both streams
|
||||||
|
rtc::Event waitConnectEvent;
|
||||||
|
EXPECT_CALL(*this, OnStreamReady(_))
|
||||||
|
.WillOnce(Invoke(this, &PipeWireStreamTest::StartScreenCastStream));
|
||||||
|
EXPECT_CALL(*this, OnStartStreaming).WillOnce([&waitConnectEvent] {
|
||||||
|
waitConnectEvent.Set();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Give it some time to connect, the order between these shouldn't matter, but
|
||||||
|
// we need to be sure we are connected before we proceed to work with frames.
|
||||||
|
waitConnectEvent.Wait(kLongWait);
|
||||||
|
|
||||||
|
rtc::Event frameRetrievedEvent;
|
||||||
|
EXPECT_CALL(*this, OnFrameRecorded).Times(3);
|
||||||
|
EXPECT_CALL(*this, OnDesktopFrameChanged)
|
||||||
|
.WillRepeatedly([&frameRetrievedEvent] { frameRetrievedEvent.Set(); });
|
||||||
|
|
||||||
|
// Record a frame in FakePipeWireStream
|
||||||
|
RgbaColor red_color(255, 0, 0);
|
||||||
|
fake_screencast_stream_->RecordFrame(red_color);
|
||||||
|
frameRetrievedEvent.Wait(kShortWait);
|
||||||
|
|
||||||
|
// Retrieve a frame from SharedScreenCastStream
|
||||||
|
frameRetrievedEvent.Wait(kShortWait);
|
||||||
|
std::unique_ptr<SharedDesktopFrame> frame =
|
||||||
|
shared_screencast_stream_->CaptureFrame();
|
||||||
|
|
||||||
|
// Check frame parameters
|
||||||
|
ASSERT_NE(frame, nullptr);
|
||||||
|
ASSERT_NE(frame->data(), nullptr);
|
||||||
|
EXPECT_EQ(frame->rect().width(), kWidth);
|
||||||
|
EXPECT_EQ(frame->rect().height(), kHeight);
|
||||||
|
EXPECT_EQ(frame->stride(), frame->rect().width() * kBytesPerPixel);
|
||||||
|
EXPECT_EQ(frame->data()[0], static_cast<uint8_t>(red_color.ToUInt32()));
|
||||||
|
|
||||||
|
// Test DesktopFrameQueue
|
||||||
|
RgbaColor green_color(0, 255, 0);
|
||||||
|
fake_screencast_stream_->RecordFrame(green_color);
|
||||||
|
frameRetrievedEvent.Wait(kShortWait);
|
||||||
|
std::unique_ptr<SharedDesktopFrame> frame2 =
|
||||||
|
shared_screencast_stream_->CaptureFrame();
|
||||||
|
ASSERT_NE(frame2, nullptr);
|
||||||
|
ASSERT_NE(frame2->data(), nullptr);
|
||||||
|
EXPECT_EQ(frame2->rect().width(), kWidth);
|
||||||
|
EXPECT_EQ(frame2->rect().height(), kHeight);
|
||||||
|
EXPECT_EQ(frame2->stride(), frame->rect().width() * kBytesPerPixel);
|
||||||
|
EXPECT_EQ(frame2->data()[0], static_cast<uint8_t>(green_color.ToUInt32()));
|
||||||
|
|
||||||
|
// Thanks to DesktopFrameQueue we should be able to have two frames shared
|
||||||
|
EXPECT_EQ(frame->IsShared(), true);
|
||||||
|
EXPECT_EQ(frame2->IsShared(), true);
|
||||||
|
EXPECT_NE(frame->data(), frame2->data());
|
||||||
|
|
||||||
|
// This should result into overwriting a frame in use
|
||||||
|
rtc::Event frameRecordedEvent;
|
||||||
|
RgbaColor blue_color(0, 0, 255);
|
||||||
|
EXPECT_CALL(*this, OnFailedToProcessBuffer).WillOnce([&frameRecordedEvent] {
|
||||||
|
frameRecordedEvent.Set();
|
||||||
|
});
|
||||||
|
|
||||||
|
fake_screencast_stream_->RecordFrame(blue_color);
|
||||||
|
frameRecordedEvent.Wait(kShortWait);
|
||||||
|
|
||||||
|
// Test disconnection from stream
|
||||||
|
EXPECT_CALL(*this, OnStopStreaming);
|
||||||
|
shared_screencast_stream_->StopScreenCastStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
@ -0,0 +1,370 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2022 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "modules/desktop_capture/linux/wayland/test/fake_screencast_stream.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "rtc_base/logging.h"
|
||||||
|
|
||||||
|
#if defined(WEBRTC_DLOPEN_PIPEWIRE)
|
||||||
|
#include "modules/desktop_capture/linux/wayland/pipewire_stubs.h"
|
||||||
|
using modules_desktop_capture_linux_wayland::InitializeStubs;
|
||||||
|
using modules_desktop_capture_linux_wayland::kModulePipewire;
|
||||||
|
using modules_desktop_capture_linux_wayland::StubPathMap;
|
||||||
|
#endif // defined(WEBRTC_DLOPEN_PIPEWIRE)
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
#if defined(WEBRTC_DLOPEN_PIPEWIRE)
|
||||||
|
const char kPipeWireLib[] = "libpipewire-0.3.so.0";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
constexpr int kBytesPerPixel = 4;
|
||||||
|
|
||||||
|
FakeScreenCastStream::FakeScreenCastStream(Observer* observer,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height)
|
||||||
|
: observer_(observer), width_(width), height_(height) {
|
||||||
|
#if defined(WEBRTC_DLOPEN_PIPEWIRE)
|
||||||
|
StubPathMap paths;
|
||||||
|
|
||||||
|
// Check if the PipeWire library is available.
|
||||||
|
paths[kModulePipewire].push_back(kPipeWireLib);
|
||||||
|
|
||||||
|
if (!InitializeStubs(paths)) {
|
||||||
|
RTC_LOG(LS_ERROR)
|
||||||
|
<< "One of following libraries is missing on your system:\n"
|
||||||
|
<< " - PipeWire (" << kPipeWireLib << ")\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif // defined(WEBRTC_DLOPEN_PIPEWIRE)
|
||||||
|
|
||||||
|
pw_init(/*argc=*/nullptr, /*argc=*/nullptr);
|
||||||
|
|
||||||
|
pw_main_loop_ = pw_thread_loop_new("pipewire-test-main-loop", nullptr);
|
||||||
|
|
||||||
|
pw_context_ =
|
||||||
|
pw_context_new(pw_thread_loop_get_loop(pw_main_loop_), nullptr, 0);
|
||||||
|
if (!pw_context_) {
|
||||||
|
RTC_LOG(LS_ERROR) << "PipeWire test: Failed to create PipeWire context";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pw_thread_loop_start(pw_main_loop_) < 0) {
|
||||||
|
RTC_LOG(LS_ERROR) << "PipeWire test: Failed to start main PipeWire loop";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize event handlers, remote end and stream-related.
|
||||||
|
pw_core_events_.version = PW_VERSION_CORE_EVENTS;
|
||||||
|
pw_core_events_.error = &OnCoreError;
|
||||||
|
|
||||||
|
pw_stream_events_.version = PW_VERSION_STREAM_EVENTS;
|
||||||
|
pw_stream_events_.add_buffer = &OnStreamAddBuffer;
|
||||||
|
pw_stream_events_.remove_buffer = &OnStreamRemoveBuffer;
|
||||||
|
pw_stream_events_.state_changed = &OnStreamStateChanged;
|
||||||
|
pw_stream_events_.param_changed = &OnStreamParamChanged;
|
||||||
|
|
||||||
|
{
|
||||||
|
PipeWireThreadLoopLock thread_loop_lock(pw_main_loop_);
|
||||||
|
|
||||||
|
pw_core_ = pw_context_connect(pw_context_, nullptr, 0);
|
||||||
|
if (!pw_core_) {
|
||||||
|
RTC_LOG(LS_ERROR) << "PipeWire test: Failed to connect PipeWire context";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pw_core_add_listener(pw_core_, &spa_core_listener_, &pw_core_events_, this);
|
||||||
|
|
||||||
|
pw_stream_ = pw_stream_new(pw_core_, "webrtc-test-stream", nullptr);
|
||||||
|
|
||||||
|
if (!pw_stream_) {
|
||||||
|
RTC_LOG(LS_ERROR) << "PipeWire test: Failed to create PipeWire stream";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pw_stream_add_listener(pw_stream_, &spa_stream_listener_,
|
||||||
|
&pw_stream_events_, this);
|
||||||
|
uint8_t buffer[2048] = {};
|
||||||
|
|
||||||
|
spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)};
|
||||||
|
|
||||||
|
std::vector<const spa_pod*> params;
|
||||||
|
|
||||||
|
spa_rectangle resolution =
|
||||||
|
SPA_RECTANGLE(uint32_t(width_), uint32_t(height_));
|
||||||
|
params.push_back(BuildFormat(&builder, SPA_VIDEO_FORMAT_BGRx,
|
||||||
|
/*modifiers=*/{}, &resolution));
|
||||||
|
|
||||||
|
auto flags =
|
||||||
|
pw_stream_flags(PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_ALLOC_BUFFERS);
|
||||||
|
if (pw_stream_connect(pw_stream_, PW_DIRECTION_OUTPUT, SPA_ID_INVALID,
|
||||||
|
flags, params.data(), params.size()) != 0) {
|
||||||
|
RTC_LOG(LS_ERROR) << "PipeWire test: Could not connect receiving stream.";
|
||||||
|
pw_stream_destroy(pw_stream_);
|
||||||
|
pw_stream_ = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FakeScreenCastStream::~FakeScreenCastStream() {
|
||||||
|
if (pw_main_loop_) {
|
||||||
|
pw_thread_loop_stop(pw_main_loop_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pw_stream_) {
|
||||||
|
pw_stream_destroy(pw_stream_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pw_core_) {
|
||||||
|
pw_core_disconnect(pw_core_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pw_context_) {
|
||||||
|
pw_context_destroy(pw_context_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pw_main_loop_) {
|
||||||
|
pw_thread_loop_destroy(pw_main_loop_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FakeScreenCastStream::RecordFrame(RgbaColor rgba_color) {
|
||||||
|
const char* error;
|
||||||
|
if (pw_stream_get_state(pw_stream_, &error) != PW_STREAM_STATE_STREAMING) {
|
||||||
|
if (error) {
|
||||||
|
RTC_LOG(LS_ERROR)
|
||||||
|
<< "PipeWire test: Failed to record frame: stream is not active: "
|
||||||
|
<< error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pw_buffer* buffer = pw_stream_dequeue_buffer(pw_stream_);
|
||||||
|
if (!buffer) {
|
||||||
|
RTC_LOG(LS_ERROR) << "PipeWire test: No available buffer";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct spa_buffer* spa_buffer = buffer->buffer;
|
||||||
|
struct spa_data* spa_data = spa_buffer->datas;
|
||||||
|
uint8_t* data = static_cast<uint8_t*>(spa_data->data);
|
||||||
|
if (!data) {
|
||||||
|
RTC_LOG(LS_ERROR)
|
||||||
|
<< "PipeWire test: Failed to record frame: invalid buffer data";
|
||||||
|
pw_stream_queue_buffer(pw_stream_, buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int stride = SPA_ROUND_UP_N(width_ * kBytesPerPixel, 4);
|
||||||
|
|
||||||
|
spa_data->chunk->offset = 0;
|
||||||
|
spa_data->chunk->size = height_ * stride;
|
||||||
|
spa_data->chunk->stride = stride;
|
||||||
|
|
||||||
|
uint32_t color = rgba_color.ToUInt32();
|
||||||
|
for (uint32_t i = 0; i < height_; i++) {
|
||||||
|
uint32_t* column = reinterpret_cast<uint32_t*>(data);
|
||||||
|
for (uint32_t j = 0; j < width_; j++) {
|
||||||
|
column[j] = color;
|
||||||
|
}
|
||||||
|
data += stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
pw_stream_queue_buffer(pw_stream_, buffer);
|
||||||
|
if (observer_) {
|
||||||
|
observer_->OnFrameRecorded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FakeScreenCastStream::StartStreaming() {
|
||||||
|
if (pw_stream_ && pw_node_id_ != 0) {
|
||||||
|
pw_stream_set_active(pw_stream_, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FakeScreenCastStream::StopStreaming() {
|
||||||
|
if (pw_stream_ && pw_node_id_ != 0) {
|
||||||
|
pw_stream_set_active(pw_stream_, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void FakeScreenCastStream::OnCoreError(void* data,
|
||||||
|
uint32_t id,
|
||||||
|
int seq,
|
||||||
|
int res,
|
||||||
|
const char* message) {
|
||||||
|
FakeScreenCastStream* that = static_cast<FakeScreenCastStream*>(data);
|
||||||
|
RTC_DCHECK(that);
|
||||||
|
|
||||||
|
RTC_LOG(LS_ERROR) << "PipeWire test: PipeWire remote error: " << message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void FakeScreenCastStream::OnStreamStateChanged(void* data,
|
||||||
|
pw_stream_state old_state,
|
||||||
|
pw_stream_state state,
|
||||||
|
const char* error_message) {
|
||||||
|
FakeScreenCastStream* that = static_cast<FakeScreenCastStream*>(data);
|
||||||
|
RTC_DCHECK(that);
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case PW_STREAM_STATE_ERROR:
|
||||||
|
RTC_LOG(LS_ERROR) << "PipeWire test: PipeWire stream state error: "
|
||||||
|
<< error_message;
|
||||||
|
break;
|
||||||
|
case PW_STREAM_STATE_PAUSED:
|
||||||
|
if (that->pw_node_id_ == 0 && that->pw_stream_) {
|
||||||
|
that->pw_node_id_ = pw_stream_get_node_id(that->pw_stream_);
|
||||||
|
that->observer_->OnStreamReady(that->pw_node_id_);
|
||||||
|
} else {
|
||||||
|
// Stop streaming
|
||||||
|
that->is_streaming_ = false;
|
||||||
|
that->observer_->OnStopStreaming();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PW_STREAM_STATE_STREAMING:
|
||||||
|
// Start streaming
|
||||||
|
that->is_streaming_ = true;
|
||||||
|
that->observer_->OnStartStreaming();
|
||||||
|
break;
|
||||||
|
case PW_STREAM_STATE_CONNECTING:
|
||||||
|
break;
|
||||||
|
case PW_STREAM_STATE_UNCONNECTED:
|
||||||
|
if (that->is_streaming_) {
|
||||||
|
// Stop streaming
|
||||||
|
that->is_streaming_ = false;
|
||||||
|
that->observer_->OnStopStreaming();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void FakeScreenCastStream::OnStreamParamChanged(void* data,
|
||||||
|
uint32_t id,
|
||||||
|
const struct spa_pod* format) {
|
||||||
|
FakeScreenCastStream* that = static_cast<FakeScreenCastStream*>(data);
|
||||||
|
RTC_DCHECK(that);
|
||||||
|
|
||||||
|
RTC_LOG(LS_INFO) << "PipeWire test: PipeWire stream format changed.";
|
||||||
|
if (!format || id != SPA_PARAM_Format) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spa_format_video_raw_parse(format, &that->spa_video_format_);
|
||||||
|
|
||||||
|
auto stride = SPA_ROUND_UP_N(that->width_ * kBytesPerPixel, 4);
|
||||||
|
|
||||||
|
uint8_t buffer[1024] = {};
|
||||||
|
auto builder = spa_pod_builder{buffer, sizeof(buffer)};
|
||||||
|
|
||||||
|
// Setup buffers and meta header for new format.
|
||||||
|
|
||||||
|
std::vector<const spa_pod*> params;
|
||||||
|
const int buffer_types = (1 << SPA_DATA_MemFd);
|
||||||
|
spa_rectangle resolution = SPA_RECTANGLE(that->width_, that->height_);
|
||||||
|
|
||||||
|
params.push_back(reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(
|
||||||
|
&builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
|
||||||
|
SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&resolution),
|
||||||
|
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(16, 2, 16),
|
||||||
|
SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), SPA_PARAM_BUFFERS_stride,
|
||||||
|
SPA_POD_Int(stride), SPA_PARAM_BUFFERS_size,
|
||||||
|
SPA_POD_Int(stride * that->height_), SPA_PARAM_BUFFERS_align,
|
||||||
|
SPA_POD_Int(16), SPA_PARAM_BUFFERS_dataType,
|
||||||
|
SPA_POD_CHOICE_FLAGS_Int(buffer_types))));
|
||||||
|
params.push_back(reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(
|
||||||
|
&builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type,
|
||||||
|
SPA_POD_Id(SPA_META_Header), SPA_PARAM_META_size,
|
||||||
|
SPA_POD_Int(sizeof(struct spa_meta_header)))));
|
||||||
|
|
||||||
|
pw_stream_update_params(that->pw_stream_, params.data(), params.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void FakeScreenCastStream::OnStreamAddBuffer(void* data, pw_buffer* buffer) {
|
||||||
|
FakeScreenCastStream* that = static_cast<FakeScreenCastStream*>(data);
|
||||||
|
RTC_DCHECK(that);
|
||||||
|
|
||||||
|
struct spa_data* spa_data = buffer->buffer->datas;
|
||||||
|
|
||||||
|
spa_data->mapoffset = 0;
|
||||||
|
spa_data->flags = SPA_DATA_FLAG_READWRITE;
|
||||||
|
|
||||||
|
if (!(spa_data[0].type & (1 << SPA_DATA_MemFd))) {
|
||||||
|
RTC_LOG(LS_ERROR)
|
||||||
|
<< "PipeWire test: Client doesn't support memfd buffer data type";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int stride = SPA_ROUND_UP_N(that->width_ * kBytesPerPixel, 4);
|
||||||
|
spa_data->maxsize = stride * that->height_;
|
||||||
|
spa_data->type = SPA_DATA_MemFd;
|
||||||
|
spa_data->fd =
|
||||||
|
memfd_create("pipewire-test-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
||||||
|
if (spa_data->fd == -1) {
|
||||||
|
RTC_LOG(LS_ERROR) << "PipeWire test: Can't create memfd";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spa_data->mapoffset = 0;
|
||||||
|
|
||||||
|
if (ftruncate(spa_data->fd, spa_data->maxsize) < 0) {
|
||||||
|
RTC_LOG(LS_ERROR) << "PipeWire test: Can't truncate to"
|
||||||
|
<< spa_data->maxsize;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL;
|
||||||
|
if (fcntl(spa_data->fd, F_ADD_SEALS, seals) == -1) {
|
||||||
|
RTC_LOG(LS_ERROR) << "PipeWire test: Failed to add seals";
|
||||||
|
}
|
||||||
|
|
||||||
|
spa_data->data = mmap(nullptr, spa_data->maxsize, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED, spa_data->fd, spa_data->mapoffset);
|
||||||
|
if (spa_data->data == MAP_FAILED) {
|
||||||
|
RTC_LOG(LS_ERROR) << "PipeWire test: Failed to mmap memory";
|
||||||
|
} else {
|
||||||
|
RTC_LOG(LS_INFO) << "PipeWire test: Memfd created successfully: "
|
||||||
|
<< spa_data->data << spa_data->maxsize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void FakeScreenCastStream::OnStreamRemoveBuffer(void* data, pw_buffer* buffer) {
|
||||||
|
FakeScreenCastStream* that = static_cast<FakeScreenCastStream*>(data);
|
||||||
|
RTC_DCHECK(that);
|
||||||
|
|
||||||
|
struct spa_buffer* spa_buffer = buffer->buffer;
|
||||||
|
struct spa_data* spa_data = spa_buffer->datas;
|
||||||
|
if (spa_data && spa_data->type == SPA_DATA_MemFd) {
|
||||||
|
munmap(spa_data->data, spa_data->maxsize);
|
||||||
|
close(spa_data->fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t FakeScreenCastStream::PipeWireNodeId() {
|
||||||
|
return pw_node_id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_TEST_FAKE_SCREENCAST_STREAM_H_
|
||||||
|
#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_TEST_FAKE_SCREENCAST_STREAM_H_
|
||||||
|
|
||||||
|
#include <pipewire/pipewire.h>
|
||||||
|
#include <spa/param/video/format-utils.h>
|
||||||
|
|
||||||
|
#include "modules/desktop_capture/linux/wayland/screencast_stream_utils.h"
|
||||||
|
#include "modules/desktop_capture/rgba_color.h"
|
||||||
|
#include "rtc_base/random.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
class FakeScreenCastStream {
|
||||||
|
public:
|
||||||
|
class Observer {
|
||||||
|
public:
|
||||||
|
virtual void OnFrameRecorded() = 0;
|
||||||
|
virtual void OnStreamReady(uint32_t stream_node_id) = 0;
|
||||||
|
virtual void OnStartStreaming() = 0;
|
||||||
|
virtual void OnStopStreaming() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Observer() = default;
|
||||||
|
virtual ~Observer() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit FakeScreenCastStream(Observer* observer,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height);
|
||||||
|
~FakeScreenCastStream();
|
||||||
|
|
||||||
|
uint32_t PipeWireNodeId();
|
||||||
|
|
||||||
|
void RecordFrame(RgbaColor rgba_color);
|
||||||
|
void StartStreaming();
|
||||||
|
void StopStreaming();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Observer* observer_;
|
||||||
|
|
||||||
|
// Resolution parameters.
|
||||||
|
uint32_t width_ = 0;
|
||||||
|
uint32_t height_ = 0;
|
||||||
|
|
||||||
|
bool is_streaming_ = false;
|
||||||
|
uint32_t pw_node_id_ = 0;
|
||||||
|
|
||||||
|
// PipeWire types
|
||||||
|
struct pw_context* pw_context_ = nullptr;
|
||||||
|
struct pw_core* pw_core_ = nullptr;
|
||||||
|
struct pw_stream* pw_stream_ = nullptr;
|
||||||
|
struct pw_thread_loop* pw_main_loop_ = nullptr;
|
||||||
|
|
||||||
|
spa_hook spa_core_listener_;
|
||||||
|
spa_hook spa_stream_listener_;
|
||||||
|
|
||||||
|
// event handlers
|
||||||
|
pw_core_events pw_core_events_ = {};
|
||||||
|
pw_stream_events pw_stream_events_ = {};
|
||||||
|
|
||||||
|
struct spa_video_info_raw spa_video_format_;
|
||||||
|
|
||||||
|
// PipeWire callbacks
|
||||||
|
static void OnCoreError(void* data,
|
||||||
|
uint32_t id,
|
||||||
|
int seq,
|
||||||
|
int res,
|
||||||
|
const char* message);
|
||||||
|
static void OnStreamAddBuffer(void* data, pw_buffer* buffer);
|
||||||
|
static void OnStreamRemoveBuffer(void* data, pw_buffer* buffer);
|
||||||
|
static void OnStreamParamChanged(void* data,
|
||||||
|
uint32_t id,
|
||||||
|
const struct spa_pod* format);
|
||||||
|
static void OnStreamStateChanged(void* data,
|
||||||
|
pw_stream_state old_state,
|
||||||
|
pw_stream_state state,
|
||||||
|
const char* error_message);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_TEST_FAKE_SCREENCAST_STREAM_H_
|
||||||
@ -0,0 +1,106 @@
|
|||||||
|
#!/usr/bin/env vpython3
|
||||||
|
# Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Use of this source code is governed by a BSD-style license
|
||||||
|
# that can be found in the LICENSE file in the root of the source
|
||||||
|
# tree. An additional intellectual property rights grant can be found
|
||||||
|
# in the file PATENTS. All contributing project authors may
|
||||||
|
# be found in the AUTHORS file in the root of the source tree.
|
||||||
|
"""
|
||||||
|
This script is the wrapper that runs the "shared_screencast_screen" test.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
# Get rid of "modules/desktop_capture/linux/wayland/test"
|
||||||
|
ROOT_DIR = os.path.normpath(
|
||||||
|
os.path.join(SCRIPT_DIR, os.pardir, os.pardir, os.pardir, os.pardir,
|
||||||
|
os.pardir))
|
||||||
|
|
||||||
|
|
||||||
|
def _ParseArgs():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Run shared_screencast_screen test.')
|
||||||
|
parser.add_argument('build_dir',
|
||||||
|
help='Path to the build directory (e.g. out/Release).')
|
||||||
|
# Unused args
|
||||||
|
# We just need to avoid passing these to the test
|
||||||
|
parser.add_argument(
|
||||||
|
'--isolated-script-test-perf-output',
|
||||||
|
default=None,
|
||||||
|
help='Path to store perf results in histogram proto format.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--isolated-script-test-output',
|
||||||
|
default=None,
|
||||||
|
help='Path to output JSON file which Chromium infra requires.')
|
||||||
|
|
||||||
|
return parser.parse_known_args()
|
||||||
|
|
||||||
|
|
||||||
|
def _GetPipeWireDir():
|
||||||
|
pipewire_dir = os.path.join(ROOT_DIR, 'third_party', 'pipewire',
|
||||||
|
'linux-amd64')
|
||||||
|
|
||||||
|
if not os.path.isdir(pipewire_dir):
|
||||||
|
pipewire_dir = None
|
||||||
|
|
||||||
|
return pipewire_dir
|
||||||
|
|
||||||
|
|
||||||
|
def _ConfigurePipeWirePaths(path):
|
||||||
|
library_dir = os.path.join(path, 'lib64')
|
||||||
|
pipewire_binary_dir = os.path.join(path, 'bin')
|
||||||
|
pipewire_config_prefix = os.path.join(path, 'share', 'pipewire')
|
||||||
|
pipewire_module_dir = os.path.join(library_dir, 'pipewire-0.3')
|
||||||
|
spa_plugin_dir = os.path.join(library_dir, 'spa-0.2')
|
||||||
|
media_session_config_dir = os.path.join(pipewire_config_prefix,
|
||||||
|
'media-session.d')
|
||||||
|
|
||||||
|
env_vars = os.environ
|
||||||
|
env_vars['LD_LIBRARY_PATH'] = library_dir
|
||||||
|
env_vars['PIPEWIRE_CONFIG_PREFIX'] = pipewire_config_prefix
|
||||||
|
env_vars['PIPEWIRE_MODULE_DIR'] = pipewire_module_dir
|
||||||
|
env_vars['SPA_PLUGIN_DIR'] = spa_plugin_dir
|
||||||
|
env_vars['MEDIA_SESSION_CONFIG_DIR'] = media_session_config_dir
|
||||||
|
env_vars['PIPEWIRE_RUNTIME_DIR'] = '/tmp'
|
||||||
|
env_vars['PATH'] = env_vars['PATH'] + ':' + pipewire_binary_dir
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args, extra_args = _ParseArgs()
|
||||||
|
|
||||||
|
pipewire_dir = _GetPipeWireDir()
|
||||||
|
|
||||||
|
if pipewire_dir is None:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
_ConfigurePipeWirePaths(pipewire_dir)
|
||||||
|
|
||||||
|
pipewire_process = subprocess.Popen(["pipewire"], stdout=None)
|
||||||
|
pipewire_media_session_process = subprocess.Popen(["pipewire-media-session"],
|
||||||
|
stdout=None)
|
||||||
|
|
||||||
|
test_command = os.path.join(args.build_dir, 'shared_screencast_stream_test')
|
||||||
|
pipewire_test_process = subprocess.run([test_command] + extra_args,
|
||||||
|
stdout=True,
|
||||||
|
check=False)
|
||||||
|
|
||||||
|
return_value = pipewire_test_process.returncode
|
||||||
|
|
||||||
|
pipewire_media_session_process.terminate()
|
||||||
|
pipewire_process.terminate()
|
||||||
|
|
||||||
|
if args.isolated_script_test_output:
|
||||||
|
with open(args.isolated_script_test_output, 'w') as f:
|
||||||
|
json.dump({"version": 3}, f)
|
||||||
|
|
||||||
|
return return_value
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
Loading…
x
Reference in New Issue
Block a user