Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e8fac8c
Apply PR#18 test sync from pexip/media
Copilot Jun 21, 2026
990322f
WIP: sync PR#18 tests; pivot to washing pex elements from test-suite
Copilot Jun 21, 2026
fa8a567
Build upstream-backed test elements; meson test plugin + CI
Copilot Jun 21, 2026
9ccf448
Fix wrapper element GType selection and rtmpserversink harness sync
Copilot Jun 21, 2026
a825651
Add explicit GITHUB_TOKEN permissions to CI workflow
Copilot Jun 21, 2026
52a0322
tests: lazily init debug category in tcp_get_listen_port
Copilot Jun 21, 2026
28cbfc5
tests: detect speex and skip codec header buffers in harness
Copilot Jun 21, 2026
fe244d6
Fix video keyframe replay and luma-range id decode for RTMP tests
Copilot Jun 23, 2026
86aa927
tests: detect speex setup headers by duration, robust to pexip patch
Copilot Jun 23, 2026
ea1ad05
gstlog
havardgraff Jun 23, 2026
76a0ee0
tests: fix speex dialin/dialout hang (PTS normalization + FFT window)…
Copilot Jun 23, 2026
8a823c0
tests: fix audio+video mux deadlock (zero-base PTS + system clock on …
Copilot Jun 23, 2026
b474951
Regenerate test TLS certs with SHA-256 to fix SSL test failures
Copilot Jun 23, 2026
cde400b
tests: fix AAC encoder latency, direct-subscriber teardown, extended-…
Copilot Jun 23, 2026
739e1e6
tests: force 48kHz in pexaacenc shim so rtmp_flv_aac ASC matches
Copilot Jun 23, 2026
0919bb5
tests: gate speex-lookahead timestamping tests and slow issue_14907 f…
Copilot Jun 23, 2026
094c36b
ci: add concurrency control and remove obsolete Travis config
Copilot Jun 24, 2026
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
56 changes: 56 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: CI

on:
push:
branches: [ master ]
pull_request:
workflow_dispatch:

permissions:
contents: read

# Cancel superseded runs on the same branch/PR to save CI minutes.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-and-test:
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- uses: actions/checkout@v4

- name: Install build & test dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential meson ninja-build pkg-config \
libssl-dev \
libglib2.0-dev \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev \
gstreamer1.0-plugins-base \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly \
gstreamer1.0-libav \
gstreamer1.0-tools

- name: Configure
run: meson setup build -Dtests=enabled

- name: Build
run: meson compile -C build

- name: Run test-suite
run: meson test -C build --print-errorlogs

- name: Upload test logs
if: always()
uses: actions/upload-artifact@v4
with:
name: meson-testlog
path: build/meson-logs/
if-no-files-found: ignore
30 changes: 0 additions & 30 deletions .travis.yml

This file was deleted.

13 changes: 13 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,16 @@ subdir('pkgconfig')
subdir('utils')
subdir('src')
subdir('plugins')

# Test-suite. The sources are kept in sync with the copy in pexip/media but are
# built here against a plain upstream GStreamer using the compatibility shims in
# tests/shims (see tests/meson.build).
gstcheck_dep = dependency('gstreamer-check-1.0', version : gst_req,
required : get_option('tests'))
gstvideo_dep = dependency('gstreamer-video-1.0', version : gst_req,
required : get_option('tests'))
build_tests = gstcheck_dep.found() and gstvideo_dep.found() and \
openssl_dep.found() and not static_build
if build_tests
subdir('tests')
endif
2 changes: 2 additions & 0 deletions meson_options.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
option('tests', type : 'feature', value : 'auto',
description : 'Build the test-suite (requires gstreamer-check-1.0)')
30 changes: 22 additions & 8 deletions src/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,16 @@ client_handle_message (Client * client, RTMPMessage * msg)
return PEX_RTMP_SERVER_STATUS_INVALID_MSG;
}
guint8 flags = msg->buf->data[0];
const gint video_codec_tag = flags & 0x0f;
/* An H.264/AVC "sequence header" is a codec-data packet (AVCPacketType
* == 0), not an actual coded frame. FLV nevertheless flags it as a
* key-frame (frame-type 1), so it must not be mistaken for a real
* key-frame: doing so would mark a fresh subscriber as already having a
* key-frame and suppress the replay of the first real one. This mirrors
* the AAC codec-data handling in the MSG_AUDIO branch above. */
const gboolean is_avc_codec_data =
(video_codec_tag == 7) && (msg->buf->len > 1)
&& (msg->buf->data[1] == 0);
GSList *subscribers =
connections_get_subscribers (client->connections, client->path);
gboolean stored_packet = FALSE;
Expand All @@ -1266,19 +1276,23 @@ client_handle_message (Client * client, RTMPMessage * msg)
subscriber->msg_stream_id, client->video_codec_data,
msg->abs_timestamp, CHUNK_STREAM_ID_STREAM);
}
if (flags >> 4 == FLV_KEY_FRAME && !subscriber->has_key_frame) {
subscriber->has_key_frame = TRUE;
if (!stored_packet) {
/* Codec-data packets are delivered out-of-band above; never treat them
* as a key-frame nor forward them again as a media frame. */
if (!is_avc_codec_data) {
if (flags >> 4 == FLV_KEY_FRAME && !subscriber->has_key_frame) {
subscriber->has_key_frame = TRUE;
if (!stored_packet) {
ret = client_rtmp_send (subscriber, MSG_VIDEO,
subscriber->msg_stream_id, msg->buf,
msg->abs_timestamp, CHUNK_STREAM_ID_STREAM);
}
}
if (subscriber->has_key_frame) {
ret = client_rtmp_send (subscriber, MSG_VIDEO,
subscriber->msg_stream_id, msg->buf,
msg->abs_timestamp, CHUNK_STREAM_ID_STREAM);
}
}
if (subscriber->has_key_frame) {
ret = client_rtmp_send (subscriber, MSG_VIDEO,
subscriber->msg_stream_id, msg->buf,
msg->abs_timestamp, CHUNK_STREAM_ID_STREAM);
}
}
client->new_metadata = FALSE;
break;
Expand Down
9 changes: 9 additions & 0 deletions src/pexrtmpserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,15 @@ rtmp_server_remove_client (PexRtmpServer * srv,
GSList *subscribers = connections_get_subscribers (srv->connections, path);
for (GSList * walk = subscribers; walk; walk = g_slist_next (walk)) {
Client *subscriber = (Client *) walk->data;
/* Direct (in-process) subscribers are driven by the lifecycle of the
* GStreamer element that owns them, not by publisher presence. Tearing
* one down here would wedge its pexrtmpsrc in a permanent EOS state, so
* the data from a re-connecting publisher on the same path would never
* reach it. Leave direct subscribers in place; they keep blocking on an
* empty queue until the next publisher starts streaming. Only network
* subscribers get disconnected when their publisher goes away. */
if (subscriber->direct)
continue;
GST_DEBUG_OBJECT (srv,
"removing subscriber %p (fd: %d) as its publisher was removed",
subscriber, subscriber->fd);
Expand Down
103 changes: 52 additions & 51 deletions tests/certs/ca.key
Original file line number Diff line number Diff line change
@@ -1,51 +1,52 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----
-----BEGIN PRIVATE KEY-----
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
7uUyGCNGJXafC6cclM17yMp+TpHItY/D
-----END PRIVATE KEY-----
56 changes: 28 additions & 28 deletions tests/certs/ca.pem
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFEzCCAvugAwIBAgIJAPYRqUAh/3uGMA0GCSqGSIb3DQEBBQUAMCAxDzANBgNV
BAoMBlRlc3RDQTENMAsGA1UEAwwEVGVzdDAeFw0xNTAyMjYxNjQ1NDZaFw0yNTAy
MjMxNjQ1NDZaMCAxDzANBgNVBAoMBlRlc3RDQTENMAsGA1UEAwwEVGVzdDCCAiIw
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJrKqoVJZbXPsxWfO2mSOXma1fUc
uZi2e19hb4+6LJ5KWXue/Lb32WSc6l4qO1lA8yspmhvXTXjJhoLfLGkRfmBWdmSp
z0tXBHq2hoVIc70SwF5q7h1IRYpN5dJM5IE7yIzDyXfNWDLmzuUoBXlC0AoN7Q/r
waXZPdIEnsxRW2fK4F2o2lly5oCq0UJiOMkvFzrCqrKTvsP8Ar52ma3YZCu2P3OB
A9wpSonzcHrfQhfEsJJGlBtIT4DXzHZQdUWj8te3PEKfL1QuJq8zPhro1NYiK1IX
q8PSmW6oIOUXh0Tr3RVlyuq6fBUXH7jQJAzLyQZ7Savg7yF4+GBsIibMlP8iskDI
m80wNk9BP75dTEPkZDgspqicdPzGzAVlnfIJ0zYqd3tD6camz0Hom9+eOzoCH3+p
wdelifrYMzXgD3A2KptkgYRH5T9SDvoSEBT6QnIT+x4R4uY9ta+j6ISYBw5yuc2F
vCjZi+lAHw5NIMuZLtysa8ZHKop2SSjP9e9Nhyls4LFKKnG2ghwZ4GmvvsOXpmXV
yN9yq5y/4rZg0w9gOXqWrW5kwS3mCIyvNFrk5PlGACjcFpVCOrvDT2iiYgBTGuPk
CiFHqlNQy+9IbEwU6fwLgzWktUVi9K23+kraI99efCSICTP48kRNZKkq4J+o6Tg+
ABtf93SwU4Jo9nORAgMBAAGjUDBOMB0GA1UdDgQWBBTyjIAzPsCE/qPnT92CHdmG
nDH55TAfBgNVHSMEGDAWgBTyjIAzPsCE/qPnT92CHdmGnDH55TAMBgNVHRMEBTAD
AQH/MA0GCSqGSIb3DQEBBQUAA4ICAQBEFAdAhWzVygqfRi9dSZaZdzCYt/JCcduW
xkkv1faQ7tFkZkLvqpPSCMpqA9vw6bQtZ6/H98Seg+9nGSmCpco8D5qvH9S7fkxI
t6QLBB/teN5wpneqosIN+FcKe76xB6oJvQuOMunCMWepU4zW80jgkhJG0k5zYIFR
Iz3gKSnVJeJdjQ11wZr8n0c50+cs65bNI8Hvd4/aGgPZgesNPz0987w3XUNsqqJd
mICXiygqNFu3ha2DNxp6lx7xgDaQ9UeIoAUzblu0XhZQ6/i4Lf3H3XVPUhTWfm6l
LqICLhbekpma9H1Fjlxb1fTQwaWCTPdY/ZHGvGu8qr+xrN5ge6yWaWI9qu+s4XtL
LmPk+bAfd2HQpxLUK5zwjCSnOImFvR13gaKi+bB2KJLlLAUlcozNpxXPsP+V0Ucm
kgtnS6sQ6OOMzjZfM1CNdD4TLOyq/KFG4ca7yoSSe4vxEA02Ula1mHbHHYk+ooEg
LJeSaiotC2LCRnAwYdWrQGAXf5IUjmqrgM6HlgoHO1jRCg3R5ZcVtcPE8Don+r8t
BP29t8tBZToOC2hI+6Pf+Nq0Dua/fnBGYxtQ3FC+uxIwlNzjx25PE9OK+uxbvLFY
TY9VbRzEa6vCZDuxTUASSTUYDJ4UVe7cXClp4Bge5jS5poBdg7mbWytRacaVUcrM
zFRZOFEGkQ==
MIIFITCCAwmgAwIBAgIUUxsDf7YUP2cmsmHizM9oh8Queo4wDQYJKoZIhvcNAQEL
BQAwIDEPMA0GA1UECgwGVGVzdENBMQ0wCwYDVQQDDARUZXN0MB4XDTI2MDYyMzEx
MzQ1OFoXDTM2MDYyMDExMzQ1OFowIDEPMA0GA1UECgwGVGVzdENBMQ0wCwYDVQQD
DARUZXN0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAy3M7pSeeQQii
2IZF4beulCAX/2asDjZAzU4mEbnDI8/Qwp4meMRXlOoTFRT0W5bhs4vxF00vh8XB
vleJy0sKYWyZtXJPjap/jNCagOPqCQw1avoBwDETka8q6+Ff+jUzUtUpUjqJYu6z
T0l4JV0Ee8T4QM5BJvYHJpkhk1oc8Agv2zyw7/0LbEneaDj46VeNr0bx+DJh356a
zEraQ4adbOBaHBrenthPzsSYYPKaDaVBTweZt3ow7mJQMsya02EjF9XKfS93xpZ4
7BgXubp4WqnG9E6cbT1n2jtiOKVsCHDvXXi/AY52ZJvjWvMpXWGpS3ohR9GVy7OZ
abBegihpt5dqyz3irnAB7MGHrSre45y2kou5SVkyJRmqac5eMDwPHp4IwLOxd2Mt
igCJu0/QN5EmpJdVwB0l7li+uHppcC/fvbUHB1k0VAfy5paQOyVPco3BUaoJcgIQ
Vkx/7RZGxUsPVp09mqpFV/dbsEHakLb0P37aiR4AKw8d6ngr9Itn+v5edViZYzJZ
ZQKjJ2xDXiASdIf69u4RZx7y2O9OheBze9I6AdH25jT1i7LaNcjm8aVnomBazuRF
uM47smKvenSl3xTcDdToKou/X5JbnvO+vi8CiqqhAGv3bV3KhieGbGywEz7QJikt
4l+YJyjte/jxndPXIWjJQ2o+uYHvnusCAwEAAaNTMFEwHQYDVR0OBBYEFDqomKOi
bA5fYzHZuvIFMIcnlSWoMB8GA1UdIwQYMBaAFDqomKOibA5fYzHZuvIFMIcnlSWo
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAH6x9/i4GQkvgeeh
SgJRebDzmJ4DINzu78+YjioD1j3kUQNoubPnb1JrZpKmRUEEou2pV/0WBtfBmmzh
V+pkC1BVQ/DiibEWY+Nktyj/kyooFeW0MfAqneUAmY+sVRokXWioo+tiMwFyvWO6
fVR9OKQ1kNmiKY6o2TzF/XeQYBbNVd6YiUzyBY0vyyNrP8dVuIPQTdcwi4z/6kbG
rwOM4heMGYR/1+FOQUdGjT8aayN3Q6IpBWV4osVGsVOfo8qR1OWyVQxzIcWSjWxu
38LnPTDFEfdviVElHjNbBiLIuapSU4wlLYfem6RrVLXXk9ia5WsRQyyWV5gzF2FT
HngpxFvX9/EOnnPzaVBnvcB9/YxQoGvY8HwdhZCZI6JyllcEpGwu9JUMSM/WmsEO
/WB99+GePjqyi9JQSCEWtATM87rxfg+48mYPJQaFlzartkbzBtjKrTGBkjmg4B9N
PVSvvNQTFUxBDzi01Dc6MPzYlHm1bqne6tj6XIr7o+AvAAnyqbFyKMgk75w43z9l
6IwMuHsmhwq+79EnQ/k135ZFmo1B8Qabihpif0h4bo1vDHzeQ/o8I0OdZaBM9tAv
t9YfmYDh9bQOfyjajuJAfFHHQkaE/Jz9qJdFO471PhaYld9s7lRZ5j3lngj3Skyr
1mLHujsMMgGwLNlyw+1nWDRCS8DS
-----END CERTIFICATE-----
Loading